/* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ import React, { useEffect, useMemo, ChangeEventHandler, ChangeEvent } from 'react'; import { EuiFormRow, EuiFieldText, EuiFlexItem, EuiCheckbox, EuiSelect, EuiFlexGroup, EuiFieldNumber, EuiFormLabel, EuiComboBox, } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import { EuiComboBoxOptionOption } from '@opensearch-project/oui/src/eui_components/combo_box/types'; import { DocumentLayerSpecification } from '../../../../model/mapLayerType'; import { ColorPicker } from './color_picker'; import { DOCUMENTS_DEFAULT_LABEL_BORDER_COLOR, DOCUMENTS_DEFAULT_LABEL_COLOR, DOCUMENTS_DEFAULT_LABEL_SIZE, DOCUMENTS_DEFAULT_LABEL_TEXT_TYPE, DOCUMENTS_LABEL_TEXT_TYPE, DOCUMENTS_LARGE_LABEL_BORDER_WIDTH, DOCUMENTS_MAX_LABEL_SIZE, DOCUMENTS_MEDIUM_LABEL_BORDER_WIDTH, DOCUMENTS_MIN_LABEL_SIZE, DOCUMENTS_NONE_LABEL_BORDER_WIDTH, DOCUMENTS_SMALL_LABEL_BORDER_WIDTH, } from '../../../../../common'; import { formatFieldStringToComboBox, getFieldsOptions } from '../../../../utils/fields_options'; import { IndexPattern } from '../../../../../../../src/plugins/data/common'; import './label_config.scss'; interface LabelProps { selectedLayerConfig: DocumentLayerSpecification; setSelectedLayerConfig: Function; setIsUpdateDisabled: Function; indexPattern: IndexPattern | null | undefined; } const labelTextTypeOptions = [ { value: DOCUMENTS_LABEL_TEXT_TYPE.FIXED, text: i18n.translate('maps.documents.labelFixedText', { defaultMessage: 'Fixed' }), }, { value: DOCUMENTS_LABEL_TEXT_TYPE.BY_FIELD, text: i18n.translate('maps.documents.labelByFieldText', { defaultMessage: 'Field value' }), }, ]; const labelBorderWidthOptions = [ { value: DOCUMENTS_NONE_LABEL_BORDER_WIDTH, text: i18n.translate('maps.documents.labelNoneBorderWidth', { defaultMessage: 'None', }), }, { value: DOCUMENTS_SMALL_LABEL_BORDER_WIDTH, text: i18n.translate('maps.documents.labelSmallBorderWidth', { defaultMessage: 'Small', }), }, { value: DOCUMENTS_MEDIUM_LABEL_BORDER_WIDTH, text: i18n.translate('maps.documents.labelMediumBorderWidth', { defaultMessage: 'Medium', }), }, { value: DOCUMENTS_LARGE_LABEL_BORDER_WIDTH, text: i18n.translate('maps.documents.labelLargeBorderWidth', { defaultMessage: 'Large', }), }, ]; export const LabelConfig = ({ selectedLayerConfig, setSelectedLayerConfig, setIsUpdateDisabled, indexPattern, }: LabelProps) => { const invalidLabelSize = useMemo(() => { const { size = DOCUMENTS_DEFAULT_LABEL_SIZE, enabled } = selectedLayerConfig.style?.label || {}; return enabled && (size < DOCUMENTS_MIN_LABEL_SIZE || size > DOCUMENTS_MAX_LABEL_SIZE); }, [selectedLayerConfig]); const invalidLabelText = useMemo(() => { const { enabled, textType = DOCUMENTS_DEFAULT_LABEL_TEXT_TYPE, textByField = '', textByFixed = '', } = selectedLayerConfig.style?.label || {}; return ( enabled && (textType === DOCUMENTS_LABEL_TEXT_TYPE.BY_FIELD ? textByField === '' : textByFixed === '') ); }, [selectedLayerConfig]); useEffect(() => { setIsUpdateDisabled(invalidLabelText || invalidLabelSize); }, [invalidLabelText, invalidLabelSize, setIsUpdateDisabled]); const onChangeLabel = (propName: string, propValue: boolean | number | string) => { setSelectedLayerConfig({ ...selectedLayerConfig, style: { ...selectedLayerConfig.style, label: { ...selectedLayerConfig.style?.label, [propName]: propValue, }, }, }); }; const onChangeShowLabel = (event: ChangeEvent<HTMLInputElement>) => { onChangeLabel('enabled', Boolean(event.target.checked)); }; const onChangeLabelTextType: ChangeEventHandler = (event: ChangeEvent<HTMLInputElement>) => onChangeLabel('textType', event.target.value); const onFixedLabelTextChange = (event: ChangeEvent<HTMLInputElement>) => { onChangeLabel('textByFixed', String(event.target.value)); }; const OnChangeLabelSize = (event: ChangeEvent<HTMLInputElement>) => { onChangeLabel('size', Number(event.target.value)); }; const onChangeLabelBorderWidth: ChangeEventHandler = (event: ChangeEvent<HTMLInputElement>) => { onChangeLabel('borderWidth', Number(event.target.value)); }; const onChangeLabelBorderColor = (color: string) => { onChangeLabel('borderColor', color); }; const onChangeLabelColor = (color: string) => { onChangeLabel('color', color); }; const onChangeLabelFieldText = (options: EuiComboBoxOptionOption[]) => { const newSelectedLayerConfig = { ...selectedLayerConfig, style: { ...selectedLayerConfig.style, label: { ...selectedLayerConfig.style?.label, textByField: options[0]?.label || '', // For backwards compatibility, set textType to BY_FIELD if textByField is set textType: DOCUMENTS_LABEL_TEXT_TYPE.BY_FIELD, }, }, }; setSelectedLayerConfig(newSelectedLayerConfig); }; const label = selectedLayerConfig.style?.label; return ( <> <EuiFormRow> <EuiCheckbox id="show-label" label="Add label" checked={label?.enabled ?? false} onChange={onChangeShowLabel} /> </EuiFormRow> {label?.enabled && ( <> <EuiFormRow label={i18n.translate('maps.documents.labelText', { defaultMessage: 'Label text', })} isInvalid={invalidLabelText} error={i18n.translate('maps.documents.textError', { defaultMessage: 'Label text cannot be empty', })} > <EuiFlexGroup responsive={false} alignItems="center" gutterSize="s"> <EuiFlexItem grow={false}> <EuiSelect options={labelTextTypeOptions} value={label?.textType ?? DOCUMENTS_DEFAULT_LABEL_TEXT_TYPE} onChange={onChangeLabelTextType} /> </EuiFlexItem> <EuiFlexItem className={'documentsLabel__text'}> {label?.textType === DOCUMENTS_LABEL_TEXT_TYPE.FIXED && ( <EuiFieldText placeholder={i18n.translate('maps.documents.labelTextPlaceholder', { defaultMessage: 'Enter label text', })} value={label?.textByFixed ?? ''} onChange={onFixedLabelTextChange} isInvalid={invalidLabelText} /> )} {(!label?.textType || label?.textType === DOCUMENTS_LABEL_TEXT_TYPE.BY_FIELD) && ( <EuiComboBox options={getFieldsOptions(indexPattern)} selectedOptions={formatFieldStringToComboBox(label?.textByField)} singleSelection={{ asPlainText: true }} onChange={onChangeLabelFieldText} sortMatchesBy="startsWith" placeholder={i18n.translate('maps.documents.labelByField', { defaultMessage: 'Select index pattern field', })} fullWidth={true} isClearable={false} isInvalid={invalidLabelText} /> )} </EuiFlexItem> </EuiFlexGroup> </EuiFormRow> <EuiFormRow label={i18n.translate('maps.documents.labelSize', { defaultMessage: 'Label size', })} isInvalid={invalidLabelSize} error={i18n.translate('maps.documents.labelSizeError', { defaultMessage: `Must be between ${DOCUMENTS_MIN_LABEL_SIZE} and ${DOCUMENTS_MAX_LABEL_SIZE}`, })} > <EuiFieldNumber placeholder={i18n.translate('maps.documents.labelSizePlaceholder', { defaultMessage: 'Select size', })} value={label?.size ?? DOCUMENTS_DEFAULT_LABEL_SIZE} onChange={OnChangeLabelSize} append={<EuiFormLabel>px</EuiFormLabel>} fullWidth={true} min={DOCUMENTS_MIN_LABEL_SIZE} max={DOCUMENTS_MAX_LABEL_SIZE} /> </EuiFormRow> <ColorPicker originColor={label?.color ?? DOCUMENTS_DEFAULT_LABEL_COLOR} label={i18n.translate('maps.documents.labelColor', { defaultMessage: 'Label color', })} selectedLayerConfigId={selectedLayerConfig.id} setIsUpdateDisabled={setIsUpdateDisabled} onColorChange={onChangeLabelColor} /> <ColorPicker originColor={label?.borderColor ?? DOCUMENTS_DEFAULT_LABEL_BORDER_COLOR} label={'Label border color'} selectedLayerConfigId={selectedLayerConfig.id} setIsUpdateDisabled={setIsUpdateDisabled} onColorChange={onChangeLabelBorderColor} /> <EuiFormRow label={i18n.translate('maps.documents.labelBorderWidth', { defaultMessage: 'Label border width', })} > <EuiSelect options={labelBorderWidthOptions} value={label?.borderWidth ?? DOCUMENTS_NONE_LABEL_BORDER_WIDTH} onChange={onChangeLabelBorderWidth} fullWidth={true} /> </EuiFormRow> </> )} </> ); };