mirror of
https://github.com/apache/superset.git
synced 2026-04-07 18:35:15 +00:00
* Implement dynamic tooltip * Normalize and consolidate * Clean up * Refactor and clean up * Remove unnecessary var * Fix type import * Update superset-frontend/src/explore/components/controls/OptionControls/index.tsx Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com> * Remove unnecessary styled span * Show full tooltip title * Force show tooltip * Force show tooltip D&D off Co-authored-by: Ville Brofeldt <ville.v.brofeldt@gmail.com> Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>
185 lines
4.8 KiB
TypeScript
185 lines
4.8 KiB
TypeScript
/**
|
|
* 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, { useMemo, useRef } from 'react';
|
|
import {
|
|
useDrag,
|
|
useDrop,
|
|
DropTargetMonitor,
|
|
DragSourceMonitor,
|
|
} from 'react-dnd';
|
|
import { DragContainer } from 'src/explore/components/controls/OptionControls';
|
|
import {
|
|
OptionProps,
|
|
OptionItemInterface,
|
|
} from 'src/explore/components/controls/DndColumnSelectControl/types';
|
|
import { Tooltip } from 'src/components/Tooltip';
|
|
import { StyledColumnOption } from 'src/explore/components/optionRenderers';
|
|
import { styled } from '@superset-ui/core';
|
|
import { ColumnMeta } from '@superset-ui/chart-controls';
|
|
import Option from './Option';
|
|
|
|
export const OptionLabel = styled.div`
|
|
width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
`;
|
|
|
|
export default function OptionWrapper(
|
|
props: OptionProps & {
|
|
type: string;
|
|
onShiftOptions: (dragIndex: number, hoverIndex: number) => void;
|
|
},
|
|
) {
|
|
const {
|
|
index,
|
|
label,
|
|
tooltipTitle,
|
|
column,
|
|
type,
|
|
onShiftOptions,
|
|
clickClose,
|
|
withCaret,
|
|
isExtra,
|
|
canDelete = true,
|
|
...rest
|
|
} = props;
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
const labelRef = useRef<HTMLDivElement>(null);
|
|
|
|
const item: OptionItemInterface = useMemo(
|
|
() => ({
|
|
dragIndex: index,
|
|
type,
|
|
}),
|
|
[index, type],
|
|
);
|
|
const [{ isDragging }, drag] = useDrag({
|
|
item,
|
|
collect: (monitor: DragSourceMonitor) => ({
|
|
isDragging: monitor.isDragging(),
|
|
}),
|
|
});
|
|
|
|
const [, drop] = useDrop({
|
|
accept: type,
|
|
|
|
hover: (item: OptionItemInterface, monitor: DropTargetMonitor) => {
|
|
if (!ref.current) {
|
|
return;
|
|
}
|
|
const { dragIndex } = item;
|
|
const hoverIndex = index;
|
|
|
|
// Don't replace items with themselves
|
|
if (dragIndex === hoverIndex) {
|
|
return;
|
|
}
|
|
// Determine rectangle on screen
|
|
const hoverBoundingRect = ref.current?.getBoundingClientRect();
|
|
// Get vertical middle
|
|
const hoverMiddleY =
|
|
(hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
|
// Determine mouse position
|
|
const clientOffset = monitor.getClientOffset();
|
|
// Get pixels to the top
|
|
const hoverClientY = clientOffset?.y
|
|
? clientOffset?.y - hoverBoundingRect.top
|
|
: 0;
|
|
// Only perform the move when the mouse has crossed half of the items height
|
|
// When dragging downwards, only move when the cursor is below 50%
|
|
// When dragging upwards, only move when the cursor is above 50%
|
|
// Dragging downwards
|
|
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
|
|
return;
|
|
}
|
|
// Dragging upwards
|
|
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
|
|
return;
|
|
}
|
|
|
|
// Time to actually perform the action
|
|
onShiftOptions(dragIndex, hoverIndex);
|
|
// eslint-disable-next-line no-param-reassign
|
|
item.dragIndex = hoverIndex;
|
|
},
|
|
});
|
|
|
|
const shouldShowTooltip =
|
|
(!isDragging && tooltipTitle && label && tooltipTitle !== label) ||
|
|
(!isDragging &&
|
|
labelRef &&
|
|
labelRef.current &&
|
|
labelRef.current.scrollWidth > labelRef.current.clientWidth);
|
|
|
|
const LabelContent = () => {
|
|
if (!shouldShowTooltip) {
|
|
return <span>{label}</span>;
|
|
}
|
|
return (
|
|
<Tooltip title={tooltipTitle || label}>
|
|
<span>{label}</span>
|
|
</Tooltip>
|
|
);
|
|
};
|
|
|
|
const ColumnOption = () => (
|
|
<StyledColumnOption
|
|
column={column as ColumnMeta}
|
|
labelRef={labelRef}
|
|
showTooltip={!!shouldShowTooltip}
|
|
showType
|
|
/>
|
|
);
|
|
|
|
const Label = () => {
|
|
if (label) {
|
|
return (
|
|
<OptionLabel ref={labelRef}>
|
|
<LabelContent />
|
|
</OptionLabel>
|
|
);
|
|
}
|
|
if (column) {
|
|
return (
|
|
<OptionLabel>
|
|
<ColumnOption />
|
|
</OptionLabel>
|
|
);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
drag(drop(ref));
|
|
|
|
return (
|
|
<DragContainer ref={ref} {...rest}>
|
|
<Option
|
|
index={index}
|
|
clickClose={clickClose}
|
|
withCaret={withCaret}
|
|
isExtra={isExtra}
|
|
canDelete={canDelete}
|
|
>
|
|
<Label />
|
|
</Option>
|
|
</DragContainer>
|
|
);
|
|
}
|