/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* eslint-disable camelcase */ import React, { useCallback, useEffect, useMemo, useRef, useState, } from 'react'; import { AdhocColumn, t, styled, css } from '@superset-ui/core'; import { ColumnMeta, isAdhocColumn, isSavedExpression, } from '@superset-ui/chart-controls'; import Tabs from 'src/components/Tabs'; import Button from 'src/components/Button'; import { Select } from 'src/components'; import { Form, FormItem } from 'src/components/Form'; import { SQLEditor } from 'src/components/AsyncAceEditor'; import { EmptyStateSmall } from 'src/components/EmptyState'; import { StyledColumnOption } from 'src/explore/components/optionRenderers'; import { POPOVER_INITIAL_HEIGHT, UNRESIZABLE_POPOVER_WIDTH, } from 'src/explore/constants'; const StyledSelect = styled(Select)` .metric-option { & > svg { min-width: ${({ theme }) => `${theme.gridUnit * 4}px`}; } & > .option-label { overflow: hidden; text-overflow: ellipsis; } } `; interface ColumnSelectPopoverProps { columns: ColumnMeta[]; editedColumn?: ColumnMeta | AdhocColumn; onChange: (column: ColumnMeta | AdhocColumn) => void; onClose: () => void; setLabel: (title: string) => void; getCurrentTab: (tab: string) => void; label: string; isTemporal?: boolean; } const getInitialColumnValues = ( editedColumn?: ColumnMeta | AdhocColumn, ): [AdhocColumn?, ColumnMeta?, ColumnMeta?] => { if (!editedColumn) { return [undefined, undefined, undefined]; } if (isAdhocColumn(editedColumn)) { return [editedColumn, undefined, undefined]; } if (isSavedExpression(editedColumn)) { return [undefined, editedColumn, undefined]; } return [undefined, undefined, editedColumn]; }; const ColumnSelectPopover = ({ columns, editedColumn, onChange, onClose, setLabel, getCurrentTab, label, isTemporal, }: ColumnSelectPopoverProps) => { const [initialLabel] = useState(label); const [initialAdhocColumn, initialCalculatedColumn, initialSimpleColumn] = getInitialColumnValues(editedColumn); const [adhocColumn, setAdhocColumn] = useState( initialAdhocColumn, ); const [selectedCalculatedColumn, setSelectedCalculatedColumn] = useState< ColumnMeta | undefined >(initialCalculatedColumn); const [selectedSimpleColumn, setSelectedSimpleColumn] = useState< ColumnMeta | undefined >(initialSimpleColumn); const sqlEditorRef = useRef(null); const [calculatedColumns, simpleColumns] = useMemo( () => columns?.reduce( (acc: [ColumnMeta[], ColumnMeta[]], column: ColumnMeta) => { if (column.expression) { acc[0].push(column); } else { acc[1].push(column); } return acc; }, [[], []], ), [columns], ); const onSqlExpressionChange = useCallback( sqlExpression => { setAdhocColumn({ label, sqlExpression } as AdhocColumn); setSelectedSimpleColumn(undefined); setSelectedCalculatedColumn(undefined); }, [label], ); const onCalculatedColumnChange = useCallback( selectedColumnName => { const selectedColumn = calculatedColumns.find( col => col.column_name === selectedColumnName, ); setSelectedCalculatedColumn(selectedColumn); setSelectedSimpleColumn(undefined); setAdhocColumn(undefined); setLabel( selectedColumn?.verbose_name || selectedColumn?.column_name || '', ); }, [calculatedColumns, setLabel], ); const onSimpleColumnChange = useCallback( selectedColumnName => { const selectedColumn = simpleColumns.find( col => col.column_name === selectedColumnName, ); setSelectedCalculatedColumn(undefined); setSelectedSimpleColumn(selectedColumn); setAdhocColumn(undefined); setLabel( selectedColumn?.verbose_name || selectedColumn?.column_name || '', ); }, [setLabel, simpleColumns], ); const defaultActiveTabKey = initialAdhocColumn ? 'sqlExpression' : initialSimpleColumn || calculatedColumns.length === 0 ? 'simple' : 'saved'; useEffect(() => { getCurrentTab(defaultActiveTabKey); }, [defaultActiveTabKey, getCurrentTab]); const onSave = useCallback(() => { if (adhocColumn && adhocColumn.label !== label) { adhocColumn.label = label; } const selectedColumn = adhocColumn || selectedCalculatedColumn || selectedSimpleColumn; if (!selectedColumn) { return; } onChange(selectedColumn); onClose(); }, [ adhocColumn, label, onChange, onClose, selectedCalculatedColumn, selectedSimpleColumn, ]); const onResetStateAndClose = useCallback(() => { setSelectedCalculatedColumn(initialCalculatedColumn); setSelectedSimpleColumn(initialSimpleColumn); setAdhocColumn(initialAdhocColumn); onClose(); }, [ initialAdhocColumn, initialCalculatedColumn, initialSimpleColumn, onClose, ]); const onTabChange = useCallback( tab => { getCurrentTab(tab); // @ts-ignore sqlEditorRef.current?.editor.focus(); }, [getCurrentTab], ); const onSqlEditorFocus = useCallback(() => { // @ts-ignore sqlEditorRef.current?.editor.resize(); }, []); const stateIsValid = adhocColumn || selectedCalculatedColumn || selectedSimpleColumn; const hasUnsavedChanges = initialLabel !== label || selectedCalculatedColumn?.column_name !== initialCalculatedColumn?.column_name || selectedSimpleColumn?.column_name !== initialSimpleColumn?.column_name || adhocColumn?.sqlExpression !== initialAdhocColumn?.sqlExpression; const savedExpressionsLabel = t('Saved expressions'); const simpleColumnsLabel = t('Column'); return (
{calculatedColumns.length > 0 ? ( ({ value: calculatedColumn.column_name, label: calculatedColumn.verbose_name || calculatedColumn.column_name, customLabel: ( ), key: calculatedColumn.column_name, }))} /> ) : ( )} {isTemporal && simpleColumns.length === 0 ? ( ) : (