Skip to content

Commit

Permalink
[Mappings editor] Add UI/UX to mappings core (#48876)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebelga authored Oct 23, 2019
1 parent 1054c0e commit 1ffc5d5
Show file tree
Hide file tree
Showing 39 changed files with 1,495 additions and 494 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export function useForm<T extends object = FormData>(

if (!areAllFieldsValidated) {
// If *not* all the fiels have been validated, the validity of the form is unknown, thus still "undefined"
return;
return undefined;
}

const isFormValid = fieldsArray.every(isFieldValid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ export class Subject<T> {
}

next(value: T) {
this.value = value;
this.callbacks.forEach(fn => fn(value));
if (value !== this.value) {
this.value = value;
this.callbacks.forEach(fn => fn(value));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@

/*
[1] When the <CreateField /> component is embedded inside the tree, we need
to add some extra indent to make room for the child "L" bullet on the left.
[2] By default all content have a padding left to leave some room for the "L" bullet
unless "--toggle" is added. In that case we don't need padding as the toggle will add it.
[3] We need to compensate from the -4px margin added by the euiFlexGroup to make sure that the
border-bottom is always visible, even when mouseovering and changing the background color.
*/

.mappings-editor {
&__fields-list-item {
&--dotted-line {
> .mappings-editor__fields-list-item__field {
border-bottom-style: dashed;
}
}

&__field {
border-bottom: $euiBorderThin;
margin-top: 4px; // [3]

&:hover {
background-color: $euiColorLightestShade;
.mappings-editor__fields-list-item__actions,
.mappings-editor__fields-list-item__multi-field-button {
opacity: 1;
}
}

&--selected {
background-color: $euiColorLightestShade;
&:hover {
background-color: $euiColorLightestShade;
}
}

&--dim {
opacity: 0.3;

&:hover {
background-color: initial;
}
}
}

&__wrapper {
padding-left: $euiSizeXS;

&--indent {
padding-left: $euiSize;
}
}

&__content {
height: $euiSizeXL * 2;
position: relative;

&--indent {
padding-left: $euiSizeXL;
}
}

&__toggle {
padding-left: $euiSizeXS;
width: $euiSizeL;
}

&__actions,
&__multi-field-button {
opacity: 0;
padding-right: $euiSizeS;
transition: opacity $euiAnimSpeedNormal $euiAnimSlightResistance;
}
}

&__create-field-wrapper {
background-color: $euiColorLightestShade;
border-right: $euiBorderThin;
border-bottom: $euiBorderThin;
border-left: $euiBorderThin;
padding: $euiSize;
}

&__create-field-content {
position: relative;
}

&__edit-field {
&__section {
border-bottom: $euiBorderThin;
padding: $euiSizeXL 0;

&:first-child {
padding-top: 0;
}
}
}
}

.mappings-editor__fields-list {
.mappings-editor__fields-list .mappings-editor__fields-list-item__content,
.mappings-editor__create-field-content {
&::before {
border-bottom: 1px solid $euiColorMediumShade;
content: '';
left: $euiSize;
position: absolute;
top: 50%;
width: $euiSizeS;
}
&::after {
border-left: 1px solid $euiColorMediumShade;
content: '';
left: $euiSize;
position: absolute;
top: calc(50% - #{$euiSizeS});
height: $euiSizeS;
}
}

.mappings-editor__create-field-content {
padding-left: $euiSizeXXL - $euiSizeXS; // [1]
}

.mappings-editor__create-field-wrapper {
&--multi-field {
.mappings-editor__create-field-content {
padding-left: $euiSize;
}

.mappings-editor__create-field-content {
&::before, &::after {
content: none;
}
}
}

&--toggle {
.mappings-editor__create-field-content {
padding-left: $euiSizeXXL - $euiSizeXS; // [1]
}
}
}

.mappings-editor__fields-list .mappings-editor__fields-list-item__content {
padding-left: $euiSizeXL; // [2]

&--toggle, &--multi-field {
&::before, &::after {
content: none;
}
}

&--toggle {
padding-left: 0;
}

&--multi-field {
padding-left: $euiSizeS;
}
}
}

ul.tree {
padding: 0;
margin: 0;
list-style-type: none;
position: relative;
padding-top: $euiSizeXS;

li.tree-item {
list-style-type: none;
border-left: $euiBorderThin;
margin-left: $euiSizeL;
padding-bottom: $euiSizeS;

div {
padding-left: $euiSizeL;
position: relative;

&::before {
content:'';
position: absolute;
top: 0;
left: -1px;
bottom: 50%;
width: $euiSize;
border: $euiBorderThin;
border-top: none;
border-right: none;
}
}
}

> li.tree-item:first-child {
padding-top: $euiSizeS;
}
> li.tree-item:last-child {
border-left-color: transparent;
padding-bottom: 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const ConfigurationForm = React.memo(({ defaultValue }: Props) => {
}, [form]);

return (
<Form form={form} className="mappings-editor">
<Form form={form} className="mappings-editor__configuration">
<FormRow title="Configuration" description="Global settings for the index mappings">
<UseField
path="dynamic"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,51 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useMemo } from 'react';
import { EuiButton, EuiSpacer } from '@elastic/eui';
import React, { useEffect, useMemo } from 'react';
import { EuiButtonEmpty, EuiSpacer } from '@elastic/eui';

import { useState, useDispatch } from '../../mappings_state';
import { validateUniqueName } from '../../lib';
import { useMappingsState, useDispatch } from '../../mappings_state';
import { FieldsList, CreateField, EditField } from './fields';

export const DocumentFields = () => {
const dispatch = useDispatch();
const {
fields: { byId, rootLevelFields },
documentFields: { status, fieldToAddFieldTo, fieldToEdit },
} = useState();
} = useMappingsState();

const getField = (fieldId: string) => byId[fieldId];
const fields = rootLevelFields.map(getField);

const uniqueNameValidatorCreate = useMemo(() => {
return validateUniqueName({ rootLevelFields, byId });
}, [byId, rootLevelFields]);

const uniqueNameValidatorEdit = useMemo(() => {
if (fieldToEdit === undefined) {
return;
}
return validateUniqueName({ rootLevelFields, byId }, byId[fieldToEdit!].source.name);
}, [byId, rootLevelFields, fieldToEdit]);
const fields = useMemo(() => rootLevelFields.map(getField), [rootLevelFields]);

const addField = () => {
dispatch({ type: 'documentField.createField' });
};

useEffect(() => {
if (status === 'idle' && fields.length === 0) {
addField();
}
}, [fields, status]);

const renderCreateField = () => {
// The "fieldToAddFieldTo" is undefined when adding to the top level "properties" object.
if (status !== 'creatingField' || fieldToAddFieldTo !== undefined) {
const showCreateField = status === 'creatingField' && fieldToAddFieldTo === undefined;

if (!showCreateField) {
return null;
}
return <CreateField uniqueNameValidator={uniqueNameValidatorCreate} />;

return <CreateField isCancelable={fields.length > 0} />;
};

const renderAddFieldButton = () => {
if (status !== 'idle') {
return null;
}
const isDisabled = status !== 'idle';
return (
<>
<EuiSpacer />
<EuiButton onClick={addField}>Add field</EuiButton>
<EuiButtonEmpty disabled={isDisabled} onClick={addField} iconType="plusInCircleFilled">
Add field
</EuiButtonEmpty>
</>
);
};
Expand All @@ -61,7 +58,7 @@ export const DocumentFields = () => {
return null;
}
const field = byId[fieldToEdit!];
return <EditField field={field} uniqueNameValidator={uniqueNameValidatorEdit!} />;
return <EditField field={field} />;
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,25 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { EuiTitle } from '@elastic/eui';
import { EuiTitle, EuiText, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

export const DocumentFieldsHeaders = () => {
return (
<EuiTitle size="s">
<h2>Document fields</h2>
</EuiTitle>
<>
<EuiTitle size="s">
<h2>
{i18n.translate('xpack.idxMgmt.mappingsEditor.documentFieldsTitle', {
defaultMessage: 'Document fields',
})}
</h2>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText size="s" color="subdued">
{i18n.translate('xpack.idxMgmt.mappingsEditor.documentFieldsDescription', {
defaultMessage: 'Define which fields the documents of your index will contain.',
})}
</EuiText>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import React from 'react';
import { EuiButton, EuiText } from '@elastic/eui';

import { useDispatch, useState } from '../../mappings_state';
import { useDispatch, useMappingsState } from '../../mappings_state';
import { FieldsEditor } from '../../types';
import { canUseMappingsEditor, normalize } from '../../lib';

Expand All @@ -18,7 +18,7 @@ interface Props {
/* TODO: Review toggle controls UI */
export const EditorToggleControls = ({ editor }: Props) => {
const dispatch = useDispatch();
const { fieldsJsonEditor } = useState();
const { fieldsJsonEditor } = useMappingsState();

const [showMaxDepthWarning, setShowMaxDepthWarning] = React.useState<boolean>(false);
const [showValidityWarning, setShowValidityWarning] = React.useState<boolean>(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export * from './name_parameter';
Loading

0 comments on commit 1ffc5d5

Please sign in to comment.