diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx index dbbc8fe94843..4806e5394a3a 100644 --- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx +++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/ColumnSelectPopover.tsx @@ -46,9 +46,10 @@ import { EmptyStateSmall } from 'src/components/EmptyState'; import { StyledColumnOption } from 'src/explore/components/optionRenderers'; import { POPOVER_INITIAL_HEIGHT, - UNRESIZABLE_POPOVER_WIDTH, + POPOVER_INITIAL_WIDTH, } from 'src/explore/constants'; import { ExplorePageState } from 'src/explore/types'; +import useResizeButton from './useResizeButton'; const StyledSelect = styled(Select)` .metric-option { @@ -117,6 +118,11 @@ const ColumnSelectPopover = ({ ColumnMeta | undefined >(initialSimpleColumn); + const [resizeButton, width, height] = useResizeButton( + POPOVER_INITIAL_WIDTH, + POPOVER_INITIAL_HEIGHT, + ); + const sqlEditorRef = useRef(null); const [calculatedColumns, simpleColumns] = useMemo( @@ -258,8 +264,8 @@ const ColumnSelectPopover = ({ className="adhoc-metric-edit-tabs" allowOverflow css={css` - height: ${POPOVER_INITIAL_HEIGHT}px; - width: ${UNRESIZABLE_POPOVER_WIDTH}px; + height: ${height}px; + width: ${width}px; `} > @@ -393,7 +399,7 @@ const ColumnSelectPopover = ({ showLoadingForImport onChange={onSqlExpressionChange} width="100%" - height={`${POPOVER_INITIAL_HEIGHT - 80}px`} + height={`${height - 80}px`} showGutter={false} editorProps={{ $blockScrolling: true }} enableLiveAutocompletion @@ -417,6 +423,7 @@ const ColumnSelectPopover = ({ > {t('Save')} + {resizeButton} ); diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/useResizeButton.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/useResizeButton.tsx new file mode 100644 index 000000000000..71d5047c6ad4 --- /dev/null +++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/useResizeButton.tsx @@ -0,0 +1,139 @@ +/** + * 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. + */ + +import React, { useCallback, useEffect, useState } from 'react'; +import throttle from 'lodash/throttle'; +import { + POPOVER_INITIAL_HEIGHT, + POPOVER_INITIAL_WIDTH, +} from 'src/explore/constants'; + +const RESIZE_THROTTLE_MS = 50; + +export default function useResizeButton( + minWidth: number, + minHeight: number, +): [JSX.Element, number, number] { + const [width, setWidth] = useState(POPOVER_INITIAL_WIDTH); + const [height, setHeight] = useState(POPOVER_INITIAL_HEIGHT); + const [clientX, setClientX] = useState(0); + const [clientY, setClientY] = useState(0); + const [dragStartX, setDragStartX] = useState(0); + const [dragStartY, setDragStartY] = useState(0); + const [dragStartWidth, setDragStartWidth] = useState(width); + const [dragStartHeight, setDragStartHeight] = useState(height); + const [isDragging, setIsDragging] = useState(false); + + const onMouseMove = useCallback((ev: MouseEvent): void => { + ev.preventDefault(); + setClientX(ev.clientX); + setClientY(ev.clientY); + }, []); + + const onMouseUp = useCallback(() => { + setIsDragging(false); + }, []); + + const onDragDown = useCallback((ev: React.MouseEvent): void => { + setDragStartX(ev.clientX); + setDragStartY(ev.clientY); + setIsDragging(true); + }, []); + + useEffect(() => { + if (isDragging) { + document.addEventListener('mousemove', onMouseMove); + } else { + setDragStartWidth(width); + setDragStartHeight(height); + document.removeEventListener('mousemove', onMouseMove); + } + }, [onMouseMove, isDragging]); + + const handleResize = useCallback( + throttle( + ({ + dragStartX, + dragStartY, + dragStartWidth, + dragStartHeight, + clientX, + clientY, + minWidth, + minHeight, + }: { + dragStartX: number; + dragStartY: number; + dragStartWidth: number; + dragStartHeight: number; + clientX: number; + clientY: number; + minWidth: number; + minHeight: number; + }): void => { + setWidth(Math.max(dragStartWidth + (clientX - dragStartX), minWidth)); + setHeight( + Math.max(dragStartHeight + (clientY - dragStartY), minHeight), + ); + }, + RESIZE_THROTTLE_MS, + ), + [setHeight, setWidth], + ); + + useEffect(() => { + if (isDragging) { + handleResize({ + dragStartX, + dragStartY, + dragStartWidth, + dragStartHeight, + clientX, + clientY, + minWidth, + minHeight, + }); + } + }, [ + isDragging, + clientX, + clientY, + dragStartWidth, + dragStartHeight, + dragStartX, + dragStartY, + ]); + + useEffect(() => { + document.addEventListener('mouseup', onMouseUp); + return () => document.removeEventListener('mouseup', onMouseUp); + }, [onMouseUp]); + + return [ + , + width, + height, + ]; +} diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/index.jsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/index.jsx index 7820ceb9d4e2..34be012f5eca 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/index.jsx +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopover/index.jsx @@ -150,7 +150,7 @@ export default class AdhocFilterEditPopover extends React.Component { POPOVER_INITIAL_WIDTH, ), height: Math.max( - this.dragStartHeight + (e.clientY - this.dragStartY) * 2, + this.dragStartHeight + (e.clientY - this.dragStartY), POPOVER_INITIAL_HEIGHT, ), }); diff --git a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx index 9ed817f28395..d796d6e8a61f 100644 --- a/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx +++ b/superset-frontend/src/explore/components/controls/MetricControl/AdhocMetricEditPopover/index.jsx @@ -248,7 +248,7 @@ export default class AdhocMetricEditPopover extends React.PureComponent { POPOVER_INITIAL_WIDTH, ), height: Math.max( - this.dragStartHeight + (e.clientY - this.dragStartY) * 2, + this.dragStartHeight + (e.clientY - this.dragStartY), POPOVER_INITIAL_HEIGHT, ), }); diff --git a/superset-frontend/src/explore/constants.ts b/superset-frontend/src/explore/constants.ts index 58c90cfba37a..e01dae493588 100644 --- a/superset-frontend/src/explore/constants.ts +++ b/superset-frontend/src/explore/constants.ts @@ -158,6 +158,4 @@ export const TIME_FILTER_MAP = { export const POPOVER_INITIAL_HEIGHT = 240; export const POPOVER_INITIAL_WIDTH = 320; -export const UNRESIZABLE_POPOVER_WIDTH = 296; - export const UNSAVED_CHART_ID = 0;