From 12d4aeff18d0a8984b15625488b6092f4f137016 Mon Sep 17 00:00:00 2001 From: siam <31573022+siam-ese@users.noreply.github.com> Date: Thu, 16 May 2024 16:53:29 +0800 Subject: [PATCH] feat(sheet): add tooltip to FilterPanel (#2234) * feat(sheet): add tooltip to FilterPanel * chore(sheet): remove not needed string * refactor: use ctor instead of hook * refactor: resizeObserverCtor * refactor: lazy init * feat(sheet): use container instead of viewport * style: fix eslint issue --- .../design/src/components/tooltip/Tooltip.tsx | 27 +++- .../design/src/components/tooltip/hooks.ts | 64 +++++++++ .../design/src/components/tooltip/index.ts | 1 + packages/design/src/index.ts | 2 +- .../components/SheetsFilterByValuesPanel.tsx | 7 +- packages/ui/src/components/hooks/layout.ts | 121 ++++++++---------- packages/ui/src/components/menu/Menu.tsx | 20 ++- .../ui/src/services/layout/layout.service.ts | 5 + 8 files changed, 162 insertions(+), 85 deletions(-) create mode 100644 packages/design/src/components/tooltip/hooks.ts diff --git a/packages/design/src/components/tooltip/Tooltip.tsx b/packages/design/src/components/tooltip/Tooltip.tsx index 00bc36d3a4..6434e63fae 100644 --- a/packages/design/src/components/tooltip/Tooltip.tsx +++ b/packages/design/src/components/tooltip/Tooltip.tsx @@ -16,12 +16,12 @@ import type { TooltipRef } from 'rc-tooltip'; import RcTooltip from 'rc-tooltip'; -import type { Ref } from 'react'; -import React, { forwardRef, useContext } from 'react'; +import React, { forwardRef, useContext, useImperativeHandle, useRef } from 'react'; import { ConfigContext } from '../config-provider/ConfigProvider'; import styles from './index.module.less'; import { placements } from './placements'; +import { useIsEllipsis } from './hooks'; export interface ITooltipProps { visible?: boolean; @@ -31,21 +31,36 @@ export interface ITooltipProps { title: (() => React.ReactNode) | React.ReactNode; children: React.ReactElement; + /* Tooltip only show if text is ellipsis */ + showIfEllipsis?: boolean; onVisibleChange?: (visible: boolean) => void; style?: React.CSSProperties; } -export const Tooltip = forwardRef((props: ITooltipProps, ref: Ref) => { - const { children, visible, placement = 'top', title, onVisibleChange, style } = props; +type NullableTooltipRef = TooltipRef | null; + +export const Tooltip = forwardRef((props, ref) => { + const { + children, + visible, + placement = 'top', + title, + onVisibleChange, + style, + showIfEllipsis = false, + } = props; const { mountContainer } = useContext(ConfigContext); + const tooltipRef = useRef(null); + useImperativeHandle(ref, () => tooltipRef.current); + const isEllipsis = useIsEllipsis(showIfEllipsis ? tooltipRef.current?.nativeElement : null); return mountContainer && ( mountContainer} overlay={
{typeof title === 'function' ? title() : title}
} diff --git a/packages/design/src/components/tooltip/hooks.ts b/packages/design/src/components/tooltip/hooks.ts new file mode 100644 index 0000000000..76532bdf63 --- /dev/null +++ b/packages/design/src/components/tooltip/hooks.ts @@ -0,0 +1,64 @@ +/** + * Copyright 2023-present DreamNum Inc. + * + * Licensed 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 { useEffect, useState } from 'react'; +import canUseDom from 'rc-util/lib/Dom/canUseDom'; + +/** + * All elements are observed by a single ResizeObserver is got greater performance than each element observed by separate ResizeObserver + * See issue https://github.com/WICG/resize-observer/issues/59#issuecomment-408098151 + */ +const _resizeObserverCallbacks: Set = new Set(); +let _resizeObserver: ResizeObserver; + +export function resizeObserverCtor(callback: ResizeObserverCallback) { + if (!_resizeObserver) { + _resizeObserver = new ResizeObserver((...args) => { + _resizeObserverCallbacks.forEach((callback) => callback(...args)); + }); + } + return { + observe(target: Element, options?: ResizeObserverOptions | undefined) { + _resizeObserverCallbacks.add(callback); + _resizeObserver.observe(target, options); + }, + unobserve(target: Element) { + _resizeObserverCallbacks.delete(callback); + _resizeObserver.unobserve(target); + }, + }; +} + +export function useIsEllipsis(element: HTMLElement | null | undefined) { + const [isEllipsis, setIsEllipsis] = useState(false); + + useEffect(() => { + if (!canUseDom() || !element) { + return; + } + + const resizeObserver = resizeObserverCtor(() => { + element && setIsEllipsis(element.scrollWidth > element.offsetWidth); + }); + setIsEllipsis(element.scrollWidth > element.offsetWidth); + resizeObserver.observe(element); + return () => { + resizeObserver.unobserve(element); + }; + }, [element]); + + return isEllipsis; +} diff --git a/packages/design/src/components/tooltip/index.ts b/packages/design/src/components/tooltip/index.ts index d55ddcd845..1ce735d2c8 100644 --- a/packages/design/src/components/tooltip/index.ts +++ b/packages/design/src/components/tooltip/index.ts @@ -15,3 +15,4 @@ */ export { type ITooltipProps, Tooltip } from './Tooltip'; +export { resizeObserverCtor } from './hooks'; diff --git a/packages/design/src/index.ts b/packages/design/src/index.ts index 221b15bc5c..f30dd26dc4 100644 --- a/packages/design/src/index.ts +++ b/packages/design/src/index.ts @@ -55,7 +55,7 @@ export { type ISelectProps, Select } from './components/select'; export { type ISelectListProps, SelectList } from './components/select-list'; export { type ISegmentedProps, Segmented } from './components/segmented'; export { type ISliderProps, Slider } from './components/slider'; -export { type ITooltipProps, Tooltip } from './components/tooltip'; +export { type ITooltipProps, Tooltip, resizeObserverCtor } from './components/tooltip'; export { type ITreeNodeProps, type ITreeProps, Tree, TreeSelectionMode } from './components/tree'; export { enUS, zhCN, ruRU } from './locale'; export { type ILocale } from './locale/interface'; diff --git a/packages/sheets-filter-ui/src/views/components/SheetsFilterByValuesPanel.tsx b/packages/sheets-filter-ui/src/views/components/SheetsFilterByValuesPanel.tsx index 1a4ea1eca5..0aef61d613 100644 --- a/packages/sheets-filter-ui/src/views/components/SheetsFilterByValuesPanel.tsx +++ b/packages/sheets-filter-ui/src/views/components/SheetsFilterByValuesPanel.tsx @@ -19,8 +19,7 @@ import { useDependency } from '@wendellhu/redi/react-bindings'; import { LocaleService } from '@univerjs/core'; import { useObservable } from '@univerjs/ui'; import List from 'rc-virtual-list'; -import { Button, Checkbox, Input } from '@univerjs/design'; - +import { Button, Checkbox, Input, Tooltip } from '@univerjs/design'; import type { ByValuesModel, IFilterByValueItem } from '../../services/sheets-filter-panel.service'; import { statisticFilterByValueItems } from '../../models/utils'; import styles from './index.module.less'; @@ -78,7 +77,9 @@ export function FilterByValue(props: { model: ByValuesModel }) {
onFilterCheckToggled(item, !item.checked)}> - {item.value} + + {item.value} + {`(${item.count})`}