diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx index a293af4d11bfe..b81468f97c684 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils.tsx @@ -9,6 +9,7 @@ import React from 'react'; import classNames from 'classnames'; import { EuiIcon, EuiFlexItem, EuiFlexGroup, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { DraggingIdentifier } from '../../../../drag_drop'; import { Datasource, DropType, GetDropProps } from '../../../../types'; function getPropsForDropType(type: 'swap' | 'duplicate' | 'combine') { @@ -130,12 +131,34 @@ export const getAdditionalClassesOnDroppable = (dropType?: string) => { } }; +const isOperationFromTheSameGroup = ( + op1?: DraggingIdentifier, + op2?: { layerId: string; groupId: string; columnId: string } +) => { + return ( + op1 && + op2 && + 'columnId' in op1 && + op1.columnId !== op2.columnId && + 'groupId' in op1 && + op1.groupId === op2.groupId && + 'layerId' in op1 && + op1.layerId === op2.layerId + ); +}; + export const getDropProps = ( layerDatasource: Datasource, - layerDatasourceDropProps: GetDropProps -) => { + dropProps: GetDropProps, + isNew?: boolean +): { dropTypes: DropType[]; nextLabel?: string } | undefined => { if (layerDatasource) { - return layerDatasource.getDropProps(layerDatasourceDropProps); + return layerDatasource.getDropProps(dropProps); + } else { + // todo: allow moving operations between layers for annotations + if (isOperationFromTheSameGroup(dropProps.dragging, dropProps)) { + return { dropTypes: [isNew ? 'duplicate_compatible' : 'reorder'], nextLabel: '' }; + } } return; }; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx index f2118bda216b8..867ce32ea700e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/buttons/empty_dimension_button.tsx @@ -131,14 +131,18 @@ export function EmptyDimensionButton({ setNewColumnId(generateId()); }, [itemIndex]); - const dropProps = getDropProps(layerDatasource, { - ...(layerDatasourceDropProps || {}), - dragging, - columnId: newColumnId, - filterOperations: group.filterOperations, - groupId: group.groupId, - dimensionGroups: groups, - }); + const dropProps = getDropProps( + layerDatasource, + { + ...(layerDatasourceDropProps || {}), + dragging, + columnId: newColumnId, + filterOperations: group.filterOperations, + groupId: group.groupId, + dimensionGroups: groups, + }, + true + ); const dropTypes = dropProps?.dropTypes; const nextLabel = dropProps?.nextLabel; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 366d3f93bf842..e404faacb8f97 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -179,59 +179,69 @@ export function LayerPanel( setNextFocusedButtonId(columnId); } - const group = groups.find(({ groupId: gId }) => gId === groupId); - - const filterOperations = group?.filterOperations || (() => false); + if (layerDatasource) { + const group = groups.find(({ groupId: gId }) => gId === groupId); + const filterOperations = group?.filterOperations || (() => false); + const dropResult = layerDatasourceOnDrop({ + ...layerDatasourceDropProps, + droppedItem, + columnId, + layerId: targetLayerId, + filterOperations, + dimensionGroups: groups, + groupId, + dropType, + }); + if (dropResult) { + let previousColumn = + typeof droppedItem.column === 'string' ? droppedItem.column : undefined; - const dropResult = layerDatasource - ? layerDatasourceOnDrop({ - ...layerDatasourceDropProps, - droppedItem, + // make it inherit only for moving and duplicate + if (!previousColumn) { + // when duplicating check if the previous column is required + if ( + dropType === 'duplicate_compatible' && + typeof droppedItem.columnId === 'string' && + group?.requiresPreviousColumnOnDuplicate + ) { + previousColumn = droppedItem.columnId; + } else { + previousColumn = typeof dropResult === 'object' ? dropResult.deleted : undefined; + } + } + const newVisState = setDimension({ columnId, - layerId: targetLayerId, - filterOperations, - dimensionGroups: groups, groupId, - dropType, - }) - : false; - if (dropResult) { - let previousColumn = - typeof droppedItem.column === 'string' ? droppedItem.column : undefined; - - // make it inherit only for moving and duplicate - if (!previousColumn) { - // when duplicating check if the previous column is required - if ( - dropType === 'duplicate_compatible' && - typeof droppedItem.columnId === 'string' && - group?.requiresPreviousColumnOnDuplicate - ) { - previousColumn = droppedItem.columnId; + layerId: targetLayerId, + prevState: props.visualizationState, + previousColumn, + frame: framePublicAPI, + }); + + if (typeof dropResult === 'object') { + // When a column is moved, we delete the reference to the old + updateVisualization( + removeDimension({ + columnId: dropResult.deleted, + layerId: targetLayerId, + prevState: newVisState, + frame: framePublicAPI, + }) + ); } else { - previousColumn = typeof dropResult === 'object' ? dropResult.deleted : undefined; + updateVisualization(newVisState); } } - const newVisState = setDimension({ - columnId, - groupId, - layerId: targetLayerId, - prevState: props.visualizationState, - previousColumn, - frame: framePublicAPI, - }); - - if (typeof dropResult === 'object') { - // When a column is moved, we delete the reference to the old - updateVisualization( - removeDimension({ - columnId: dropResult.deleted, - layerId: targetLayerId, - prevState: newVisState, - frame: framePublicAPI, - }) - ); - } else { + } else { + if (dropType === 'duplicate_compatible' || dropType === 'reorder') { + const newVisState = setDimension({ + columnId, + groupId, + layerId: targetLayerId, + prevState: props.visualizationState, + previousColumn: droppedItem.id, + frame: framePublicAPI, + }); updateVisualization(newVisState); } } diff --git a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx b/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx index 321090c94241a..2793f6a616f69 100644 --- a/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/annotations/helpers.tsx @@ -114,32 +114,42 @@ export const setAnnotationsDimension: Visualization['setDimension'] = ( if (!foundLayer || !isAnnotationsLayer(foundLayer)) { return prevState; } - const dataLayers = getDataLayers(prevState.layers); - const newLayer = { ...foundLayer } as XYAnnotationLayerConfig; - - const hasConfig = newLayer.annotations?.some(({ id }) => id === columnId); + const inputAnnotations = foundLayer.annotations as XYAnnotationLayerConfig['annotations']; + const currentConfig = inputAnnotations?.find(({ id }) => id === columnId); const previousConfig = previousColumn - ? newLayer.annotations?.find(({ id }) => id === previousColumn) - : false; - if (!hasConfig) { - const newTimestamp = getStaticDate(dataLayers, frame?.activeData); - newLayer.annotations = [ - ...(newLayer.annotations || []), - { - label: defaultAnnotationLabel, - key: { - type: 'point_in_time', - timestamp: newTimestamp, - }, - icon: 'triangle', - ...previousConfig, - id: columnId, + ? inputAnnotations?.find(({ id }) => id === previousColumn) + : undefined; + + let resultAnnotations = [...inputAnnotations] as XYAnnotationLayerConfig['annotations']; + if (!currentConfig) { + resultAnnotations.push({ + label: defaultAnnotationLabel, + key: { + type: 'point_in_time', + timestamp: getStaticDate(getDataLayers(prevState.layers), frame?.activeData), }, - ]; + icon: 'triangle', + ...previousConfig, + id: columnId, + }); + } else if (currentConfig && previousConfig) { + // reorder if both configs exist + resultAnnotations = inputAnnotations.filter((c) => c.id !== previousConfig.id); + const targetPosition = resultAnnotations.findIndex((c) => c.id === currentConfig.id); + const targetIndex = inputAnnotations.indexOf(previousConfig); + const sourceIndex = inputAnnotations.indexOf(currentConfig); + resultAnnotations.splice( + targetIndex < sourceIndex ? targetPosition + 1 : targetPosition, + 0, + previousConfig + ); } + return { ...prevState, - layers: prevState.layers.map((l) => (l.layerId === layerId ? newLayer : l)), + layers: prevState.layers.map((l) => + l.layerId === layerId ? { ...foundLayer, annotations: resultAnnotations } : l + ), }; };