mirror of https://github.com/apache/superset.git
feat: Adds more customization properties to DropdownContainer (#22031)
This commit is contained in:
parent
9b6322b640
commit
b040211970
|
@ -20,7 +20,8 @@ import React, { useEffect, useState } from 'react';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import { css } from '@superset-ui/core';
|
import { css } from '@superset-ui/core';
|
||||||
import Select from '../Select/Select';
|
import Select from '../Select/Select';
|
||||||
import DropdownContainer, { DropdownContainerProps } from '.';
|
import Button from '../Button';
|
||||||
|
import DropdownContainer, { DropdownContainerProps, Ref } from '.';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'DropdownContainer',
|
title: 'DropdownContainer',
|
||||||
|
@ -31,6 +32,7 @@ const ITEMS_COUNT = 6;
|
||||||
const ITEM_OPTIONS = 10;
|
const ITEM_OPTIONS = 10;
|
||||||
const MIN_WIDTH = 700;
|
const MIN_WIDTH = 700;
|
||||||
const MAX_WIDTH = 1500;
|
const MAX_WIDTH = 1500;
|
||||||
|
const HEIGHT = 400;
|
||||||
|
|
||||||
const itemsOptions = Array.from({ length: ITEM_OPTIONS }).map((_, i) => ({
|
const itemsOptions = Array.from({ length: ITEM_OPTIONS }).map((_, i) => ({
|
||||||
label: `Option ${i}`,
|
label: `Option ${i}`,
|
||||||
|
@ -60,23 +62,25 @@ const generateItems = (overflowingState?: OverflowingState) =>
|
||||||
export const Component = (props: DropdownContainerProps) => {
|
export const Component = (props: DropdownContainerProps) => {
|
||||||
const [items, setItems] = useState<ItemsType>([]);
|
const [items, setItems] = useState<ItemsType>([]);
|
||||||
const [overflowingState, setOverflowingState] = useState<OverflowingState>();
|
const [overflowingState, setOverflowingState] = useState<OverflowingState>();
|
||||||
|
const containerRef = React.useRef<Ref>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setItems(generateItems(overflowingState));
|
setItems(generateItems(overflowingState));
|
||||||
}, [overflowingState]);
|
}, [overflowingState]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div>
|
||||||
<div
|
<div
|
||||||
css={css`
|
css={css`
|
||||||
position: relative;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
min-width: ${MIN_WIDTH}px;
|
min-width: ${MIN_WIDTH}px;
|
||||||
width: ${MIN_WIDTH}px;
|
width: ${MIN_WIDTH}px;
|
||||||
max-width: ${MAX_WIDTH}px;
|
max-width: ${MAX_WIDTH}px;
|
||||||
height: 80vh;
|
height: ${HEIGHT}px;
|
||||||
border: 1px solid lightgray;
|
border: 1px solid lightgray;
|
||||||
resize: horizontal;
|
resize: horizontal;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
|
margin-bottom: 12px;
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<DropdownContainer
|
<DropdownContainer
|
||||||
|
@ -87,12 +91,13 @@ export const Component = (props: DropdownContainerProps) => {
|
||||||
setOverflowingState(value);
|
setOverflowingState(value);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
ref={containerRef}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
<Button onClick={() => containerRef.current?.open()}>Open</Button>
|
||||||
<span
|
<span
|
||||||
css={css`
|
css={css`
|
||||||
position: absolute;
|
margin-left: ${MIN_WIDTH - 340}px;
|
||||||
right: 20px;
|
|
||||||
bottom: 8px;
|
|
||||||
color: gray;
|
color: gray;
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
|
|
|
@ -18,8 +18,11 @@
|
||||||
*/
|
*/
|
||||||
import React, {
|
import React, {
|
||||||
CSSProperties,
|
CSSProperties,
|
||||||
|
forwardRef,
|
||||||
ReactElement,
|
ReactElement,
|
||||||
|
RefObject,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
useLayoutEffect,
|
useLayoutEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
|
@ -68,10 +71,18 @@ export interface DropdownContainerProps {
|
||||||
* Option to customize the content of the popover.
|
* Option to customize the content of the popover.
|
||||||
*/
|
*/
|
||||||
popoverContent?: (overflowedItems: Item[]) => ReactElement;
|
popoverContent?: (overflowedItems: Item[]) => ReactElement;
|
||||||
|
/**
|
||||||
|
* Popover ref.
|
||||||
|
*/
|
||||||
|
popoverRef?: RefObject<HTMLDivElement>;
|
||||||
/**
|
/**
|
||||||
* Popover additional style properties.
|
* Popover additional style properties.
|
||||||
*/
|
*/
|
||||||
popoverStyle?: CSSProperties;
|
popoverStyle?: CSSProperties;
|
||||||
|
/**
|
||||||
|
* Displayed count in the popover trigger.
|
||||||
|
*/
|
||||||
|
popoverTriggerCount?: number;
|
||||||
/**
|
/**
|
||||||
* Icon of the popover trigger.
|
* Icon of the popover trigger.
|
||||||
*/
|
*/
|
||||||
|
@ -86,21 +97,30 @@ export interface DropdownContainerProps {
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DropdownContainer = ({
|
export type Ref = HTMLDivElement & { open: () => void };
|
||||||
|
|
||||||
|
const DropdownContainer = forwardRef(
|
||||||
|
(
|
||||||
|
{
|
||||||
items,
|
items,
|
||||||
onOverflowingStateChange,
|
onOverflowingStateChange,
|
||||||
popoverContent,
|
popoverContent,
|
||||||
|
popoverRef,
|
||||||
popoverStyle = {},
|
popoverStyle = {},
|
||||||
|
popoverTriggerCount,
|
||||||
popoverTriggerIcon,
|
popoverTriggerIcon,
|
||||||
popoverTriggerText = t('More'),
|
popoverTriggerText = t('More'),
|
||||||
style,
|
style,
|
||||||
}: DropdownContainerProps) => {
|
}: DropdownContainerProps,
|
||||||
|
outerRef: RefObject<Ref>,
|
||||||
|
) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { ref, width = 0 } = useResizeDetector<HTMLDivElement>();
|
const { ref, width = 0 } = useResizeDetector<HTMLDivElement>();
|
||||||
const previousWidth = usePrevious(width) || 0;
|
const previousWidth = usePrevious(width) || 0;
|
||||||
const { current } = ref;
|
const { current } = ref;
|
||||||
const [overflowingIndex, setOverflowingIndex] = useState<number>(-1);
|
const [overflowingIndex, setOverflowingIndex] = useState<number>(-1);
|
||||||
const [itemsWidth, setItemsWidth] = useState<number[]>([]);
|
const [itemsWidth, setItemsWidth] = useState<number[]>([]);
|
||||||
|
const [popoverVisible, setPopoverVisible] = useState(false);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const container = current?.children.item(0);
|
const container = current?.children.item(0);
|
||||||
|
@ -194,16 +214,31 @@ const DropdownContainer = ({
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: ${theme.gridUnit * 3}px;
|
gap: ${theme.gridUnit * 3}px;
|
||||||
width: 200px;
|
|
||||||
`}
|
`}
|
||||||
style={popoverStyle}
|
style={popoverStyle}
|
||||||
|
ref={popoverRef}
|
||||||
>
|
>
|
||||||
{popoverContent
|
{popoverContent
|
||||||
? popoverContent(overflowedItems)
|
? popoverContent(overflowedItems)
|
||||||
: overflowedItems.map(item => item.element)}
|
: overflowedItems.map(item => item.element)}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
[overflowedItems, popoverContent, popoverStyle, theme.gridUnit],
|
[
|
||||||
|
overflowedItems,
|
||||||
|
popoverContent,
|
||||||
|
popoverRef,
|
||||||
|
popoverStyle,
|
||||||
|
theme.gridUnit,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
useImperativeHandle(
|
||||||
|
outerRef,
|
||||||
|
() => ({
|
||||||
|
...(ref.current as HTMLDivElement),
|
||||||
|
open: () => setPopoverVisible(true),
|
||||||
|
}),
|
||||||
|
[ref],
|
||||||
);
|
);
|
||||||
|
|
||||||
const overflowingCount =
|
const overflowingCount =
|
||||||
|
@ -233,15 +268,17 @@ const DropdownContainer = ({
|
||||||
<Popover
|
<Popover
|
||||||
content={content}
|
content={content}
|
||||||
trigger="click"
|
trigger="click"
|
||||||
|
visible={popoverVisible}
|
||||||
|
onVisibleChange={visible => setPopoverVisible(visible)}
|
||||||
overlayInnerStyle={{
|
overlayInnerStyle={{
|
||||||
overflow: 'auto',
|
|
||||||
maxHeight: 500,
|
maxHeight: 500,
|
||||||
|
overflowY: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button buttonStyle="secondary">
|
<Button buttonStyle="secondary">
|
||||||
{popoverTriggerIcon}
|
{popoverTriggerIcon}
|
||||||
{popoverTriggerText}
|
{popoverTriggerText}
|
||||||
<Badge count={overflowingCount} />
|
<Badge count={popoverTriggerCount || overflowingCount} />
|
||||||
<Icons.DownOutlined
|
<Icons.DownOutlined
|
||||||
iconSize="m"
|
iconSize="m"
|
||||||
iconColor={theme.colors.grayscale.base}
|
iconColor={theme.colors.grayscale.base}
|
||||||
|
@ -251,6 +288,7 @@ const DropdownContainer = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default DropdownContainer;
|
export default DropdownContainer;
|
||||||
|
|
Loading…
Reference in New Issue