diff --git a/e2e/visual-comparison/docs/docs-visual-comparison.spec.ts-snapshots/default-doc-ci-chromium-linux.png b/e2e/visual-comparison/docs/docs-visual-comparison.spec.ts-snapshots/default-doc-ci-chromium-linux.png index 70feb59955..56af6fd4ae 100644 Binary files a/e2e/visual-comparison/docs/docs-visual-comparison.spec.ts-snapshots/default-doc-ci-chromium-linux.png and b/e2e/visual-comparison/docs/docs-visual-comparison.spec.ts-snapshots/default-doc-ci-chromium-linux.png differ diff --git a/e2e/visual-comparison/sheets/sheets-gridlines.spec.ts-snapshots/sheets-no-gridlines-ci-chromium-linux.png b/e2e/visual-comparison/sheets/sheets-gridlines.spec.ts-snapshots/sheets-no-gridlines-ci-chromium-linux.png index 5beec9a65b..e09553959b 100644 Binary files a/e2e/visual-comparison/sheets/sheets-gridlines.spec.ts-snapshots/sheets-no-gridlines-ci-chromium-linux.png and b/e2e/visual-comparison/sheets/sheets-gridlines.spec.ts-snapshots/sheets-no-gridlines-ci-chromium-linux.png differ diff --git a/e2e/visual-comparison/sheets/sheets-visual-comparison.spec.ts-snapshots/default-sheet-fullpage-ci-chromium-linux.png b/e2e/visual-comparison/sheets/sheets-visual-comparison.spec.ts-snapshots/default-sheet-fullpage-ci-chromium-linux.png index 6917685851..d16145631e 100644 Binary files a/e2e/visual-comparison/sheets/sheets-visual-comparison.spec.ts-snapshots/default-sheet-fullpage-ci-chromium-linux.png and b/e2e/visual-comparison/sheets/sheets-visual-comparison.spec.ts-snapshots/default-sheet-fullpage-ci-chromium-linux.png differ diff --git a/examples/src/docs-uniscript/main.ts b/examples/src/docs-uniscript/main.ts index 82dae3cd76..e84b323595 100644 --- a/examples/src/docs-uniscript/main.ts +++ b/examples/src/docs-uniscript/main.ts @@ -28,6 +28,8 @@ import { UniverUIPlugin } from '@univerjs/ui'; import { UniverUniscriptPlugin } from '@univerjs/uniscript'; import { enUS, faIR, ruRU, zhCN } from '../locales'; +import '../global.css'; + // univer const univer = new Univer({ theme: defaultTheme, diff --git a/examples/src/docs/main.ts b/examples/src/docs/main.ts index 07d69e555b..ccc4877315 100644 --- a/examples/src/docs/main.ts +++ b/examples/src/docs/main.ts @@ -29,6 +29,8 @@ import { DEFAULT_DOCUMENT_DATA_SIMPLE } from '@univerjs/mockdata'; import { UniverUIPlugin } from '@univerjs/ui'; import { enUS, faIR, ruRU, zhCN } from '../locales'; +import '../global.css'; + /* eslint-disable node/prefer-global/process */ const IS_E2E: boolean = !!process.env.IS_E2E; diff --git a/examples/src/mobile-s/main.ts b/examples/src/mobile-s/main.ts index 803d22b39f..13c00f3d8d 100644 --- a/examples/src/mobile-s/main.ts +++ b/examples/src/mobile-s/main.ts @@ -35,6 +35,8 @@ import { UniverSheetsMobileUIPlugin } from '@univerjs/sheets-ui'; import { UniverMobileUIPlugin } from '@univerjs/ui'; import { enUS, faIR } from '../locales'; +import '../global.css'; + // univer const univer = new Univer({ theme: defaultTheme, diff --git a/examples/src/sheets-multi/index.css b/examples/src/sheets-multi/index.css deleted file mode 100644 index e6297719b6..0000000000 --- a/examples/src/sheets-multi/index.css +++ /dev/null @@ -1,3 +0,0 @@ -#app { - height: 100%; -} diff --git a/examples/src/sheets-multi/main.tsx b/examples/src/sheets-multi/main.tsx index 634ae6771d..02eacc3101 100644 --- a/examples/src/sheets-multi/main.tsx +++ b/examples/src/sheets-multi/main.tsx @@ -34,7 +34,8 @@ import { Mosaic, MosaicWindow } from 'react-mosaic-component'; import { enUS, faIR, frFR, ruRU, zhCN } from '../locales'; import 'react-mosaic-component/react-mosaic-component.css'; -import './index.css'; + +import '../global.css'; function factory(id: string) { return function createUniverOnContainer() { diff --git a/examples/src/sheets-uniscript/main.ts b/examples/src/sheets-uniscript/main.ts index 182c21222e..1b3eb39957 100644 --- a/examples/src/sheets-uniscript/main.ts +++ b/examples/src/sheets-uniscript/main.ts @@ -29,9 +29,10 @@ import { UniverSheetsNumfmtUIPlugin } from '@univerjs/sheets-numfmt-ui'; import { UniverSheetsUIPlugin } from '@univerjs/sheets-ui'; import { UniverUIPlugin } from '@univerjs/ui'; import { UniverUniscriptPlugin } from '@univerjs/uniscript'; - import { enUS, faIR, ruRU, zhCN } from '../locales'; +import '../global.css'; + // univer const univer = new Univer({ theme: defaultTheme, diff --git a/examples/src/slides/main.ts b/examples/src/slides/main.ts index ac26d9e10b..285dd03f14 100644 --- a/examples/src/slides/main.ts +++ b/examples/src/slides/main.ts @@ -28,6 +28,8 @@ import { UniverSlidesUIPlugin } from '@univerjs/slides-ui'; import { UniverUIPlugin } from '@univerjs/ui'; import { enUS, faIR, ruRU, zhCN } from '../locales'; +import '../global.css'; + // univer const univer = new Univer({ theme: greenTheme, diff --git a/examples/src/uni/main.ts b/examples/src/uni/main.ts index a93dc86507..c1d61271be 100644 --- a/examples/src/uni/main.ts +++ b/examples/src/uni/main.ts @@ -46,6 +46,8 @@ import { UniSlidesUIController } from '@univerjs/uni-slides-ui'; import { UniverUniUIPlugin } from '@univerjs/uniui'; import { enUS, faIR } from '../locales'; +import '../global.css'; + const LOAD_LAZY_PLUGINS_TIMEOUT = 4_000; // univer diff --git a/packages-experimental/action-recorder/src/global.css b/packages-experimental/action-recorder/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages-experimental/action-recorder/src/global.css +++ b/packages-experimental/action-recorder/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages-experimental/debugger/src/commands/operations/dark-mode.operation.ts b/packages-experimental/debugger/src/commands/operations/dark-mode.operation.ts new file mode 100644 index 0000000000..342f9f6da9 --- /dev/null +++ b/packages-experimental/debugger/src/commands/operations/dark-mode.operation.ts @@ -0,0 +1,34 @@ +/** + * 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 type { IAccessor, ICommand } from '@univerjs/core'; +import { CommandType, ILocalStorageService } from '@univerjs/core'; + +export interface IDarkModeCommandParams { + value?: 'dark' | 'light' | 'auto'; +} + +export const DarkModeOperation: ICommand = { + id: 'debugger.operation.dark-mode', + type: CommandType.COMMAND, + handler: async (accessor: IAccessor, params: IDarkModeCommandParams) => { + const localStorageService = accessor.get(ILocalStorageService); + localStorageService.setItem('local.darkMode', params.value); + window.location.reload(); + + return true; + }, +}; diff --git a/packages-experimental/debugger/src/controllers/dark-mode.controller.ts b/packages-experimental/debugger/src/controllers/dark-mode.controller.ts new file mode 100644 index 0000000000..d30c2d0665 --- /dev/null +++ b/packages-experimental/debugger/src/controllers/dark-mode.controller.ts @@ -0,0 +1,42 @@ +/** + * 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 { ILocalStorageService, Inject, RxDisposable } from '@univerjs/core'; + +export class DarkModeController extends RxDisposable { + constructor( + @Inject(ILocalStorageService) private _localStorageService: ILocalStorageService + ) { + super(); + + this._localStorageService.getItem('local.darkMode').then((darkMode) => { + if (darkMode === 'dark') { + document.documentElement.classList.add('univer-dark'); + } + + if (darkMode === 'system') { + const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + darkModeMediaQuery.addEventListener('change', (e) => { + if (e.matches) { + document.documentElement.classList.add('univer-dark'); + } else { + document.documentElement.classList.remove('univer-dark'); + } + }); + } + }); + } +} diff --git a/packages-experimental/debugger/src/controllers/debugger.controller.ts b/packages-experimental/debugger/src/controllers/debugger.controller.ts index 646c6de347..da4ca28526 100644 --- a/packages-experimental/debugger/src/controllers/debugger.controller.ts +++ b/packages-experimental/debugger/src/controllers/debugger.controller.ts @@ -21,6 +21,7 @@ import { CreateEmptySheetCommand, DisposeCurrentUnitCommand, DisposeUniverComman import { ShowCellContentOperation } from '../commands/operations/cell.operation'; import { ChangeUserCommand } from '../commands/operations/change-user.operation'; import { ConfirmOperation } from '../commands/operations/confirm.operation'; +import { DarkModeOperation } from '../commands/operations/dark-mode.operation'; import { DialogOperation } from '../commands/operations/dialog.operation'; import { LocaleOperation } from '../commands/operations/locale.operation'; import { MessageOperation } from '../commands/operations/message.operation'; @@ -29,10 +30,10 @@ import { OpenWatermarkPanelOperation } from '../commands/operations/open-waterma import { SaveSnapshotOptions } from '../commands/operations/save-snapshot.operations'; import { SetEditable } from '../commands/operations/set.editable.operation'; import { SidebarOperation } from '../commands/operations/sidebar.operation'; + import { ThemeOperation } from '../commands/operations/theme.operation'; import { ImageDemo } from '../components/Image'; - // @ts-ignore import VueI18nIcon from '../components/VueI18nIcon.vue'; import { TEST_EDITOR_CONTAINER_COMPONENT } from '../views/test-editor/component-name'; @@ -54,6 +55,7 @@ export class DebuggerController extends Disposable { [ LocaleOperation, + DarkModeOperation, ThemeOperation, NotificationOperation, DialogOperation, diff --git a/packages-experimental/debugger/src/controllers/menu.schema.ts b/packages-experimental/debugger/src/controllers/menu.schema.ts index b93e1299dc..c876fe5919 100644 --- a/packages-experimental/debugger/src/controllers/menu.schema.ts +++ b/packages-experimental/debugger/src/controllers/menu.schema.ts @@ -20,6 +20,7 @@ import { CreateEmptySheetCommand, DisposeCurrentUnitCommand, DisposeUniverComman import { ShowCellContentOperation } from '../commands/operations/cell.operation'; import { ChangeUserCommand } from '../commands/operations/change-user.operation'; import { ConfirmOperation } from '../commands/operations/confirm.operation'; +import { DarkModeOperation } from '../commands/operations/dark-mode.operation'; import { DialogOperation } from '../commands/operations/dialog.operation'; import { LocaleOperation } from '../commands/operations/locale.operation'; import { MessageOperation } from '../commands/operations/message.operation'; @@ -34,6 +35,7 @@ import { ConfirmMenuItemFactory, CreateEmptySheetMenuItemFactory, CreateFloatDOMMenuItemFactory, + DarkModeMenuItemFactory, DialogMenuItemFactory, DisposeCurrentUnitMenuItemFactory, DisposeUniverItemFactory, @@ -59,6 +61,10 @@ export const menuSchema: MenuSchemaType = { order: 0, menuItemFactory: LocaleMenuItemFactory, }, + [DarkModeOperation.id]: { + order: 0.9, + menuItemFactory: DarkModeMenuItemFactory, + }, [ThemeOperation.id]: { order: 1, menuItemFactory: ThemeMenuItemFactory, diff --git a/packages-experimental/debugger/src/controllers/menu.ts b/packages-experimental/debugger/src/controllers/menu.ts index 81fd824fc0..debf75b595 100644 --- a/packages-experimental/debugger/src/controllers/menu.ts +++ b/packages-experimental/debugger/src/controllers/menu.ts @@ -25,6 +25,7 @@ import { CreateEmptySheetCommand, DisposeCurrentUnitCommand, DisposeUniverComman import { ShowCellContentOperation } from '../commands/operations/cell.operation'; import { ChangeUserCommand, UnitRole } from '../commands/operations/change-user.operation'; import { ConfirmOperation } from '../commands/operations/confirm.operation'; +import { DarkModeOperation } from '../commands/operations/dark-mode.operation'; import { DialogOperation } from '../commands/operations/dialog.operation'; import { LocaleOperation } from '../commands/operations/locale.operation'; import { MessageOperation } from '../commands/operations/message.operation'; @@ -71,6 +72,29 @@ export function LocaleMenuItemFactory(accessor: IAccessor): IMenuSelectorItem { }; } +export function DarkModeMenuItemFactory(accessor: IAccessor): IMenuSelectorItem { + return { + id: DarkModeOperation.id, + title: '🌓', + tooltip: 'Dark Mode', + type: MenuItemType.SELECTOR, + selections: [ + { + label: 'System', + value: 'auto', + }, + { + label: 'Light', + value: 'light', + }, + { + label: 'Dark', + value: 'dark', + }, + ], + }; +} + export function ThemeMenuItemFactory(accessor: IAccessor): IMenuSelectorItem { return { id: ThemeOperation.id, diff --git a/packages-experimental/debugger/src/debugger-plugin.ts b/packages-experimental/debugger/src/debugger-plugin.ts index 02c2351ad5..ed515692ad 100644 --- a/packages-experimental/debugger/src/debugger-plugin.ts +++ b/packages-experimental/debugger/src/debugger-plugin.ts @@ -18,6 +18,7 @@ import type { Dependency } from '@univerjs/core'; import type { IUniverDebuggerConfig } from './controllers/config.schema'; import { IConfigService, Inject, Injector, merge, Plugin } from '@univerjs/core'; import { DEBUGGER_PLUGIN_CONFIG_KEY, defaultPluginConfig } from './controllers/config.schema'; +import { DarkModeController } from './controllers/dark-mode.controller'; import { DebuggerController } from './controllers/debugger.controller'; import { E2EController } from './controllers/e2e/e2e.controller'; import { PerformanceMonitorController } from './controllers/performance-monitor.controller'; @@ -50,6 +51,7 @@ export class UniverDebuggerPlugin extends Plugin { override onStarting(): void { ([ [PerformanceMonitorController], + [DarkModeController], [DebuggerController], [E2EController], [UniverWatermarkMenuController], @@ -63,6 +65,7 @@ export class UniverDebuggerPlugin extends Plugin { } override onRendered(): void { + this._injector.get(DarkModeController); this._injector.get(PerformanceMonitorController); this._injector.get(UniverWatermarkMenuController); } diff --git a/packages-experimental/debugger/src/global.css b/packages-experimental/debugger/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages-experimental/debugger/src/global.css +++ b/packages-experimental/debugger/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages-experimental/uni-docs-ui/src/global.css b/packages-experimental/uni-docs-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages-experimental/uni-docs-ui/src/global.css +++ b/packages-experimental/uni-docs-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages-experimental/uni-formula-ui/src/global.css b/packages-experimental/uni-formula-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages-experimental/uni-formula-ui/src/global.css +++ b/packages-experimental/uni-formula-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages-experimental/uni-sheets-ui/src/global.css b/packages-experimental/uni-sheets-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages-experimental/uni-sheets-ui/src/global.css +++ b/packages-experimental/uni-sheets-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages-experimental/uni-slides-ui/src/global.css b/packages-experimental/uni-slides-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages-experimental/uni-slides-ui/src/global.css +++ b/packages-experimental/uni-slides-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages-experimental/uniui/src/global.css b/packages-experimental/uniui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages-experimental/uniui/src/global.css +++ b/packages-experimental/uniui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/design/package.json b/packages/design/package.json index e063af6108..0437722048 100644 --- a/packages/design/package.json +++ b/packages/design/package.json @@ -68,6 +68,7 @@ "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0" }, "dependencies": { + "@radix-ui/react-tooltip": "^1.1.6", "@rc-component/color-picker": "^2.0.1", "@rc-component/trigger": "^2.2.5", "@types/react-mentions": "^4.4.0", diff --git a/packages/design/src/components/dropdown/DropdownContext.tsx b/packages/design/src/components/dropdown/DropdownContext.tsx index 56ed578c4f..ec51feeba7 100644 --- a/packages/design/src/components/dropdown/DropdownContext.tsx +++ b/packages/design/src/components/dropdown/DropdownContext.tsx @@ -18,8 +18,9 @@ import type { RefObject } from 'react'; import { createContext, useContext } from 'react'; interface IDropdownContextType { - isOpen: boolean; - setIsOpen: (value: boolean) => void; + show: boolean; + updateShow: (value: boolean) => void; + disabled: boolean; overlayRef: RefObject; triggerRef: RefObject; } diff --git a/packages/design/src/components/dropdown/DropdownOverlay.tsx b/packages/design/src/components/dropdown/DropdownOverlay.tsx index 6ffeaa40a0..8268ae4fe6 100644 --- a/packages/design/src/components/dropdown/DropdownOverlay.tsx +++ b/packages/design/src/components/dropdown/DropdownOverlay.tsx @@ -15,12 +15,12 @@ */ import canUseDom from 'rc-util/lib/Dom/canUseDom'; -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; -import clsx from '../../helper/clsx'; +import { clsx } from '../../helper/clsx'; import { useDropdown } from './DropdownContext'; -interface IDropdownOverlayProps { +export interface IDropdownOverlayProps { children: React.ReactNode; className?: string; offset?: { @@ -34,11 +34,12 @@ export function DropdownOverlay({ children, className, offset }: IDropdownOverla return null; } - const { isOpen, setIsOpen, overlayRef, triggerRef } = useDropdown(); + const { show, updateShow, overlayRef, triggerRef } = useDropdown(); + const [mounted, setMounted] = useState(false); const [position, setPosition] = useState({ top: 0, left: 0 }); useEffect(() => { - if (isOpen && triggerRef.current && overlayRef.current) { + if (show && triggerRef.current && overlayRef.current) { const triggerRect = triggerRef.current.getBoundingClientRect(); const overlayRect = overlayRef.current.getBoundingClientRect(); const viewportWidth = document.documentElement.clientWidth; @@ -62,26 +63,39 @@ export function DropdownOverlay({ children, className, offset }: IDropdownOverla top: top + (offset?.y ?? 0), left: left + (offset?.x ?? 0), }); - } - }, [isOpen, offset?.x, offset?.y, overlayRef, triggerRef]); - if (!isOpen) return null; + requestAnimationFrame(() => { + setMounted(true); + }); + } + }, [show, offset?.x, offset?.y, overlayRef, triggerRef]); + + const hideAfterClick = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + updateShow(false); + }, []); + + if (!show) return null; return createPortal(
setIsOpen(false)} + onClick={hideAfterClick} > {children}
, diff --git a/packages/design/src/components/dropdown/DropdownProvider.tsx b/packages/design/src/components/dropdown/DropdownProvider.tsx index 1e7a8c33b4..7202a46051 100644 --- a/packages/design/src/components/dropdown/DropdownProvider.tsx +++ b/packages/design/src/components/dropdown/DropdownProvider.tsx @@ -19,19 +19,34 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import { DropdownContext } from './DropdownContext'; interface IDropdownProviderProps { + visible?: boolean; children: ReactNode; + disabled?: boolean; + onVisibleChange?: (visible: boolean) => void; } -export function DropdownProvider({ children }: IDropdownProviderProps) { - const [isOpen, setIsOpen] = useState(false); +export function DropdownProvider({ visible, children, disabled = false, onVisibleChange }: IDropdownProviderProps) { const triggerRef = useRef(null); const overlayRef = useRef(null); + const [internalShow, setInternalShow] = useState(false); + const isControlled = visible !== undefined; + const show = isControlled ? visible : internalShow; + + const updateShow = (newShow: boolean) => { + if (disabled) return; + + if (!isControlled) { + setInternalShow(newShow); + } + onVisibleChange?.(newShow); + }; + useEffect(() => { const handleClickOutside = (event: MouseEvent) => { const target = event.target as Node; if (!triggerRef.current?.contains(target) && !overlayRef.current?.contains(target)) { - setIsOpen(false); + updateShow(false); } }; @@ -39,7 +54,7 @@ export function DropdownProvider({ children }: IDropdownProviderProps) { return () => window.removeEventListener('mousedown', handleClickOutside, true); }, []); - const contextValue = useMemo(() => ({ isOpen, setIsOpen, triggerRef, overlayRef }), [isOpen]); + const contextValue = useMemo(() => ({ show, updateShow, disabled, triggerRef, overlayRef }), [show, disabled]); return ( diff --git a/packages/design/src/components/dropdown/DropdownTrigger.tsx b/packages/design/src/components/dropdown/DropdownTrigger.tsx index 41e0bd2c6a..d17f3abcc3 100644 --- a/packages/design/src/components/dropdown/DropdownTrigger.tsx +++ b/packages/design/src/components/dropdown/DropdownTrigger.tsx @@ -23,7 +23,7 @@ interface IDropdownTriggerProps { } export function DropdownTrigger({ children }: IDropdownTriggerProps) { - const { setIsOpen, isOpen, triggerRef } = useDropdown(); + const { show, updateShow, triggerRef } = useDropdown(); if (!isValidElement(children)) { throw new Error('DropdownTrigger children must be a valid React element'); @@ -34,7 +34,7 @@ export function DropdownTrigger({ children }: IDropdownTriggerProps) { ref: triggerRef, onClick: (e: React.MouseEvent) => { e.stopPropagation(); - setIsOpen(!isOpen); + updateShow(!show); // eslint-disable-next-line ts/no-explicit-any (children.props as React.HTMLProps)?.onClick?.(e as any); }, diff --git a/packages/design/src/components/dropdown/index.ts b/packages/design/src/components/dropdown/index.ts index d27cf0c367..7cb264253d 100644 --- a/packages/design/src/components/dropdown/index.ts +++ b/packages/design/src/components/dropdown/index.ts @@ -14,6 +14,6 @@ * limitations under the License. */ -export { DropdownOverlay } from './DropdownOverlay'; +export { DropdownOverlay, type IDropdownOverlayProps } from './DropdownOverlay'; export { DropdownProvider } from './DropdownProvider'; export { DropdownTrigger } from './DropdownTrigger'; diff --git a/packages/design/src/components/message/Message.tsx b/packages/design/src/components/message/Message.tsx index cca546fd0e..0da4384110 100644 --- a/packages/design/src/components/message/Message.tsx +++ b/packages/design/src/components/message/Message.tsx @@ -18,7 +18,7 @@ import { ErrorSingle, InfoSingle, Loading, SuccessSingle, WarningSingle } from ' import canUseDom from 'rc-util/lib/Dom/canUseDom'; import React, { useEffect, useMemo, useState } from 'react'; import { createPortal } from 'react-dom'; -import clsx from '../../helper/clsx'; +import { clsx } from '../../helper/clsx'; export enum MessageType { Success = 'success', @@ -51,9 +51,9 @@ const Message = ({ content, type = MessageType.Info }: IMessageProps) => {
{ contentRef.current?.removeEventListener('scroll', handleScroll); + observer.disconnect(); }; }, []); diff --git a/packages/design/src/components/slider/Slider.tsx b/packages/design/src/components/slider/Slider.tsx index ace726ab94..12683cf0ba 100644 --- a/packages/design/src/components/slider/Slider.tsx +++ b/packages/design/src/components/slider/Slider.tsx @@ -157,8 +157,17 @@ export function Slider(props: ISliderProps) {
- - + +
{ const { container } = render(); - fireEvent.click(container.querySelector('[role=reset button]')!); + fireEvent.click(container.querySelector('[role=button]')!); expect(value).toBe(30); }); diff --git a/packages/design/src/components/tooltip/Tooltip.stories.tsx b/packages/design/src/components/tooltip/Tooltip.stories.tsx index 6da696274d..b8a9e00108 100644 --- a/packages/design/src/components/tooltip/Tooltip.stories.tsx +++ b/packages/design/src/components/tooltip/Tooltip.stories.tsx @@ -35,21 +35,62 @@ export const Playground = { render() { return ( <> +
+ +
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+
- - + + +
+ 这是一段很长的文本内容,超出部分会被截断 +
-
- - - showIfEllipsis: 奇奇怪怪乖乖跟琪琪去上课就开始丹江口市的是卡拉斯科独立思考独立思考 - + +
+ + +
+
diff --git a/packages/design/src/components/tooltip/Tooltip.tsx b/packages/design/src/components/tooltip/Tooltip.tsx index e870c1db58..8949f8c4cb 100644 --- a/packages/design/src/components/tooltip/Tooltip.tsx +++ b/packages/design/src/components/tooltip/Tooltip.tsx @@ -14,75 +14,334 @@ * limitations under the License. */ -import type { TooltipRef } from 'rc-tooltip'; -import RcTooltip from 'rc-tooltip'; -import React, { forwardRef, useCallback, useContext, useImperativeHandle, useRef, useState } from 'react'; - -import { ConfigContext } from '../config-provider/ConfigProvider'; -import styles from './index.module.less'; -import { placements } from './placements'; -import { useIsEllipsis } from './hooks'; +import canUseDom from 'rc-util/lib/Dom/canUseDom'; +import React, { useEffect, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { clsx } from '../../helper/clsx'; export interface ITooltipProps { + /** + * Whether the tooltip is visible + * @description If not set, the tooltip will be controlled by the component itself + */ visible?: boolean; - + /** + * Whether the tooltip is a child of the trigger element + * @default false + */ + asChild?: boolean; + /** + * The content of the tooltip + * @description If not set, the tooltip will not be displayed。 Although it can be set to ReactNode, it is recommended to use string. + */ + title?: React.ReactNode; + /** + * The trigger element + */ + children: React.ReactNode; + /** + * The placement of the tooltip + * @default 'bottom' + */ placement?: 'top' | 'bottom' | 'left' | 'right'; - - title: (() => React.ReactNode) | React.ReactNode; - - children: React.ReactElement; - /* Tooltip only show if text is ellipsis */ + /** + * Whether the tooltip is displayed when the content is ellipsis + * @default false + */ showIfEllipsis?: boolean; - + /** + * Callback when the visibility of the tooltip changes + */ onVisibleChange?: (visible: boolean) => void; - - style?: React.CSSProperties; } -export type NullableTooltipRef = TooltipRef | null; +export function Tooltip({ visible, asChild = false, title, children, placement = 'bottom', showIfEllipsis = false, onVisibleChange }: ITooltipProps) { + if (!canUseDom || title === undefined) { + return null; + } -export const Tooltip = forwardRef((props, ref) => { - const { - children, - visible, - placement = 'top', - title, - onVisibleChange, - style, - showIfEllipsis = false, - } = props; + const [mounted, setMounted] = useState(false); + const [position, setPosition] = useState({ x: 0, y: 0 }); + const [actualPlacement, setActualPlacement] = useState(placement); + const triggerRef = useRef(null); + const tooltipRef = useRef(null); + const [arrowOffset, setArrowOffset] = useState(0); - const { mountContainer } = useContext(ConfigContext); - const [tooltipEl, setTooltipEl] = useState(); - const tooltipRef = useRef(null); + const [internalShow, setInternalShow] = useState(false); + const isControlled = visible !== undefined; + const show = isControlled ? visible : internalShow; - // FIXME: cannot set value in a render method - const refHandler = useCallback((ref: NullableTooltipRef) => { - if (ref?.nativeElement) { - setTooltipEl(ref.nativeElement); + const updateShow = (newShow: boolean) => { + if (!isControlled) { + setInternalShow(newShow); } - tooltipRef.current = ref; - }, []); - useImperativeHandle(ref, () => tooltipRef.current); + onVisibleChange?.(newShow); + }; - const isEllipsis = useIsEllipsis(showIfEllipsis ? tooltipEl : null); + const updatePosition = () => { + if (!triggerRef.current || !tooltipRef.current) return; - return mountContainer && ( - mountContainer} - overlay={
{typeof title === 'function' ? title() : title}
} - builtinPlacements={placements} - placement={placement} - mouseEnterDelay={0.2} - showArrow - destroyTooltipOnHide={false} - onVisibleChange={onVisibleChange} - overlayStyle={style} - > - {children} -
+ const triggerRect = triggerRef.current.getBoundingClientRect(); + const tooltipRect = tooltipRef.current.getBoundingClientRect(); + const scrollX = window.scrollX; + const scrollY = window.scrollY; + + const MARGIN = 8; + const ARROW_PADDING = 12; + + let newPlacement = placement; + let newArrowOffset = 0; + + // Calculate base position + const getBasePosition = () => { + switch (newPlacement) { + case 'top': + return { + x: triggerRect.left + (triggerRect.width - tooltipRect.width) / 2, + y: triggerRect.top - tooltipRect.height - MARGIN, + }; + case 'bottom': + return { + x: triggerRect.left + (triggerRect.width - tooltipRect.width) / 2, + y: triggerRect.bottom + MARGIN, + }; + case 'left': + return { + x: triggerRect.left - tooltipRect.width - MARGIN, + y: triggerRect.top + (triggerRect.height - tooltipRect.height) / 2, + }; + case 'right': + return { + x: triggerRect.right + MARGIN, + y: triggerRect.top + (triggerRect.height - tooltipRect.height) / 2, + }; + } + }; + + // Check if it needs to be flipped + const shouldFlip = (pos: { x: number; y: number }) => { + switch (newPlacement) { + case 'top': + return pos.y < MARGIN; + case 'bottom': + return pos.y + tooltipRect.height > window.innerHeight - MARGIN; + case 'left': + return pos.x < MARGIN; + case 'right': + return pos.x + tooltipRect.width > window.innerWidth - MARGIN; + } + }; + + // Get flipped placement + const getFlippedPlacement = () => { + const map = { + top: 'bottom', + bottom: 'top', + left: 'right', + right: 'left', + } as const; + return map[newPlacement]; + }; + + // Initial position calculation + let pos = getBasePosition(); + + // Check if it needs to be flipped + if (shouldFlip(pos)) { + newPlacement = getFlippedPlacement(); + pos = getBasePosition(); + } else { + // Edge avoidance adjustment + if (['top', 'bottom'].includes(newPlacement)) { + const minX = MARGIN; + const maxX = window.innerWidth - tooltipRect.width - MARGIN; + + if (pos.x < minX) { + newArrowOffset = pos.x - minX; + pos.x = minX; + } else if (pos.x > maxX) { + newArrowOffset = pos.x - maxX; + pos.x = maxX; + } + + // Limit arrow offset + const maxArrowOffset = tooltipRect.width / 2 - ARROW_PADDING; + newArrowOffset = Math.max(Math.min(newArrowOffset, maxArrowOffset), -maxArrowOffset); + } else { + const minY = MARGIN; + const maxY = window.innerHeight - tooltipRect.height - MARGIN; + + if (pos.y < minY) { + newArrowOffset = pos.y - minY; + pos.y = minY; + } else if (pos.y > maxY) { + newArrowOffset = pos.y - maxY; + pos.y = maxY; + } + + // Limit arrow offset + const maxArrowOffset = tooltipRect.height / 2 - ARROW_PADDING; + newArrowOffset = Math.max(Math.min(newArrowOffset, maxArrowOffset), -maxArrowOffset); + } + } + + // Add scroll offset + pos.x += scrollX; + pos.y += scrollY; + + setPosition(pos); + setActualPlacement(newPlacement); + setArrowOffset(newArrowOffset); + setMounted(true); + }; + + useEffect(() => { + if (show) { + setMounted(false); + requestAnimationFrame(() => { + updatePosition(); + }); + + const handleUpdate = () => { + setMounted(false); + requestAnimationFrame(updatePosition); + }; + + window.addEventListener('scroll', handleUpdate); + window.addEventListener('resize', handleUpdate); + + return () => { + window.removeEventListener('scroll', handleUpdate); + window.removeEventListener('resize', handleUpdate); + }; + } + }, [show]); + + useEffect(() => { + const element = triggerRef.current; + if (!element) { + return; + } + + const childEl = asChild ? element : element.firstElementChild as HTMLElement; + + function isContentOverflowing(element: HTMLElement): boolean { + return Math.abs(element.scrollWidth - element.clientWidth) > 1; + } + + function onMouseEnter() { + if (showIfEllipsis && childEl) { + if (!isContentOverflowing(childEl)) return; + } + updateShow(true); + } + + function onMouseLeave() { + updateShow(false); + setMounted(false); + } + + if (isControlled && !onVisibleChange) { + return; + } + + element.addEventListener('mouseenter', onMouseEnter); + element.addEventListener('mouseleave', onMouseLeave); + + return () => { + element.removeEventListener('mouseenter', onMouseEnter); + element.removeEventListener('mouseleave', onMouseLeave); + }; + }, [asChild, showIfEllipsis, isControlled, onVisibleChange]); + + useEffect(() => { + if (isControlled) { + if (visible) { + setMounted(false); + requestAnimationFrame(() => { + updatePosition(); + }); + } else { + setMounted(false); + } + } + }, [visible, isControlled]); + + // eslint-disable-next-line react/no-clone-element + const enhancedChild = React.cloneElement( + // eslint-disable-next-line react/no-children-only + React.Children.only(children) as React.ReactElement, + { + ref: triggerRef, + } ); -}); + + return ( + <> + {asChild + ? enhancedChild + : ( +
+ {children} +
+ )} + + {show && createPortal( +
+
+
+ {title} +
+ {/* Arrow */} +
+
+
, + document.body + )} + + ); +}; diff --git a/packages/design/src/components/tooltip/index.module.less b/packages/design/src/components/tooltip/index.module.less deleted file mode 100644 index 80001f789e..0000000000 --- a/packages/design/src/components/tooltip/index.module.less +++ /dev/null @@ -1,108 +0,0 @@ -@tooltip-color: rgb(var(--color-white)); -@tooltip-bg: rgb(var(--color-black)); -@tooltip-arrow-width: 4px; - -.tooltip { - position: absolute; - z-index: 1080; - pointer-events: none; - - display: block; - - font-size: var(--font-size-xxs); - - visibility: visible; - - &-no-events { - pointer-events: none; - } - - &-hidden { - display: none; - } - - &-placement-top { - padding: @tooltip-arrow-width 0; - } - - &-placement-right { - padding: 0 @tooltip-arrow-width; - } - - &-placement-bottom { - padding: @tooltip-arrow-width; - } - - &-placement-left { - padding: 0 @tooltip-arrow-width; - } - - &-placement-top &-arrow, - &-placement-topLeft &-arrow, - &-placement-topRight &-arrow { - bottom: 0; - margin-left: -@tooltip-arrow-width; - border-width: @tooltip-arrow-width @tooltip-arrow-width 0; - border-top-color: @tooltip-bg; - } - - &-placement-top &-arrow { - left: 50%; - } - - &-placement-right &-arrow { - left: 0; - margin-top: -@tooltip-arrow-width; - border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0; - border-right-color: @tooltip-bg; - } - - &-placement-right &-arrow { - top: 50%; - } - - &-placement-left &-arrow { - right: 0; - margin-top: -@tooltip-arrow-width; - border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width; - border-left-color: @tooltip-bg; - } - - &-placement-left &-arrow { - top: 50%; - } - - &-placement-bottom &-arrow { - top: 0; - margin-left: -@tooltip-arrow-width; - border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; - border-bottom-color: @tooltip-bg; - } - - &-placement-bottom &-arrow { - left: 50%; - } - - // Wrapper for the tooltip content - &-inner { - padding: var(--padding-xs) var(--padding-lg); - - color: @tooltip-color; - text-align: left; - text-decoration: none; - - background-color: @tooltip-bg; - border-radius: var(--border-radius-base); - } - - // Arrows - &-arrow { - position: absolute; - - width: 0; - height: 0; - - border-color: transparent; - border-style: solid; - } -} diff --git a/packages/design/src/components/tooltip/index.ts b/packages/design/src/components/tooltip/index.ts index a0b6d78839..d55ddcd845 100644 --- a/packages/design/src/components/tooltip/index.ts +++ b/packages/design/src/components/tooltip/index.ts @@ -14,5 +14,4 @@ * limitations under the License. */ -export { resizeObserverCtor } from './hooks'; -export { type ITooltipProps, type NullableTooltipRef, Tooltip } from './Tooltip'; +export { type ITooltipProps, Tooltip } from './Tooltip'; diff --git a/packages/design/src/components/tooltip/placements.tsx b/packages/design/src/components/tooltip/placements.tsx deleted file mode 100644 index a24ba6a818..0000000000 --- a/packages/design/src/components/tooltip/placements.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/** - * 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 type { BuildInPlacements } from '@rc-component/trigger'; - -const autoAdjustOverflowTopBottom = { - shiftX: 0, - adjustY: 1, -}; - -const autoAdjustOverflowLeftRight = { adjustX: 1, shiftY: true }; - -const targetOffset = [0, 0]; - -export const placements: BuildInPlacements = { - left: { - points: ['cr', 'cl'], - overflow: autoAdjustOverflowLeftRight, - offset: [-4, 0], - targetOffset, - }, - right: { - points: ['cl', 'cr'], - overflow: autoAdjustOverflowLeftRight, - offset: [4, 0], - targetOffset, - }, - top: { - points: ['bc', 'tc'], - overflow: autoAdjustOverflowTopBottom, - offset: [0, -4], - targetOffset, - }, - bottom: { - points: ['tc', 'bc'], - overflow: autoAdjustOverflowTopBottom, - offset: [0, 4], - targetOffset, - }, -}; - -export default placements; diff --git a/packages/design/src/helper/clsx.tsx b/packages/design/src/helper/clsx.tsx index 6b95932e42..436c3e7f70 100644 --- a/packages/design/src/helper/clsx.tsx +++ b/packages/design/src/helper/clsx.tsx @@ -17,6 +17,6 @@ import { type ClassValue, clsx as cn } from 'clsx'; import { twMerge } from 'tailwind-merge'; -export default function clsx(...inputs: ClassValue[]) { +export function clsx(...inputs: ClassValue[]) { return twMerge(cn(inputs)); } diff --git a/packages/design/src/components/tooltip/hooks.ts b/packages/design/src/helper/resize-observer.ts similarity index 69% rename from packages/design/src/components/tooltip/hooks.ts rename to packages/design/src/helper/resize-observer.ts index 76532bdf63..442cb50860 100644 --- a/packages/design/src/components/tooltip/hooks.ts +++ b/packages/design/src/helper/resize-observer.ts @@ -14,9 +14,6 @@ * 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 @@ -41,24 +38,3 @@ export function resizeObserverCtor(callback: ResizeObserverCallback) { }, }; } - -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/index.ts b/packages/design/src/index.ts index 05dcf36ca5..7ec4f11fda 100644 --- a/packages/design/src/index.ts +++ b/packages/design/src/index.ts @@ -27,7 +27,7 @@ export { Confirm, type IConfirmProps } from './components/confirm'; export { DatePanel, DatePicker, type IDatePanelProps, type IDatePickerProps } from './components/date-picker'; export { Dialog, type IDialogProps } from './components/dialog'; export { DropdownLegacy, type IDropdownLegacyProps } from './components/dropdown-legacy'; -export { DropdownOverlay, DropdownProvider, DropdownTrigger } from './components/dropdown'; +export { DropdownOverlay, DropdownProvider, DropdownTrigger, type IDropdownOverlayProps } from './components/dropdown'; export { FormDualColumnLayout, FormLayout, type IFormDualColumnLayoutProps, type IFormLayoutProps } from './components/form-layout'; export { type IInputProps, type IInputWithSlotProps, Input, InputWithSlot } from './components/input'; export { type IInputNumberProps, InputNumber } from './components/input-number'; @@ -42,7 +42,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, type NullableTooltipRef, resizeObserverCtor, Tooltip } from './components/tooltip'; +export { type ITooltipProps, Tooltip } from './components/tooltip'; export { filterLeafNode, findNodePathFromTree, findSubTreeFromPath, type ITreeNodeProps, type ITreeProps, mergeTreeSelected, Tree, TreeSelectionMode } from './components/tree'; export { Switch } from './components/switch'; export { type ILocale } from './locale/interface'; @@ -50,3 +50,5 @@ export { defaultTheme, greenTheme, themeInstance } from './themes'; export { DraggableList, type IDraggableListProps } from './components/draggable-list'; export { type ITextareaProps, Textarea } from './components/textarea'; export { type IMentionsProps, Mention, type MentionProps, Mentions } from './components/mentions'; +export { clsx } from './helper/clsx'; +export { resizeObserverCtor } from './helper/resize-observer'; diff --git a/packages/docs-drawing-ui/src/global.css b/packages/docs-drawing-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/docs-drawing-ui/src/global.css +++ b/packages/docs-drawing-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/docs-hyper-link-ui/src/global.css b/packages/docs-hyper-link-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/docs-hyper-link-ui/src/global.css +++ b/packages/docs-hyper-link-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/docs-mention-ui/src/global.css b/packages/docs-mention-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/docs-mention-ui/src/global.css +++ b/packages/docs-mention-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/docs-thread-comment-ui/src/global.css b/packages/docs-thread-comment-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/docs-thread-comment-ui/src/global.css +++ b/packages/docs-thread-comment-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/docs-ui/src/global.css b/packages/docs-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/docs-ui/src/global.css +++ b/packages/docs-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/drawing-ui/src/global.css b/packages/drawing-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/drawing-ui/src/global.css +++ b/packages/drawing-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/find-replace/src/global.css b/packages/find-replace/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/find-replace/src/global.css +++ b/packages/find-replace/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/sheets-conditional-formatting-ui/src/global.css b/packages/sheets-conditional-formatting-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/sheets-conditional-formatting-ui/src/global.css +++ b/packages/sheets-conditional-formatting-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/sheets-crosshair-highlight/src/global.css b/packages/sheets-crosshair-highlight/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/sheets-crosshair-highlight/src/global.css +++ b/packages/sheets-crosshair-highlight/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/sheets-data-validation-ui/src/global.css b/packages/sheets-data-validation-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/sheets-data-validation-ui/src/global.css +++ b/packages/sheets-data-validation-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/sheets-drawing-ui/src/global.css b/packages/sheets-drawing-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/sheets-drawing-ui/src/global.css +++ b/packages/sheets-drawing-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/sheets-filter-ui/src/global.css b/packages/sheets-filter-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/sheets-filter-ui/src/global.css +++ b/packages/sheets-filter-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/sheets-formula-ui/src/global.css b/packages/sheets-formula-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/sheets-formula-ui/src/global.css +++ b/packages/sheets-formula-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/sheets-hyper-link-ui/src/global.css b/packages/sheets-hyper-link-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/sheets-hyper-link-ui/src/global.css +++ b/packages/sheets-hyper-link-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/sheets-hyper-link-ui/src/views/CellLinkPopup/index.tsx b/packages/sheets-hyper-link-ui/src/views/CellLinkPopup/index.tsx index d38de612fb..a8de832853 100644 --- a/packages/sheets-hyper-link-ui/src/views/CellLinkPopup/index.tsx +++ b/packages/sheets-hyper-link-ui/src/views/CellLinkPopup/index.tsx @@ -85,7 +85,7 @@ export const CellLinkPopupPure = (props: ICellLinkPopupPureProps) => {
{iconsMap[linkObj.type]}
- + {linkObj.name}
diff --git a/packages/sheets-numfmt-ui/src/global.css b/packages/sheets-numfmt-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/sheets-numfmt-ui/src/global.css +++ b/packages/sheets-numfmt-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/sheets-sort-ui/src/global.css b/packages/sheets-sort-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/sheets-sort-ui/src/global.css +++ b/packages/sheets-sort-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/sheets-thread-comment-ui/src/global.css b/packages/sheets-thread-comment-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/sheets-thread-comment-ui/src/global.css +++ b/packages/sheets-thread-comment-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/sheets-ui/src/global.css b/packages/sheets-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/sheets-ui/src/global.css +++ b/packages/sheets-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/sheets-ui/src/views/defined-name/DefinedNameContainer.tsx b/packages/sheets-ui/src/views/defined-name/DefinedNameContainer.tsx index b3bb63f266..05b12cc028 100644 --- a/packages/sheets-ui/src/views/defined-name/DefinedNameContainer.tsx +++ b/packages/sheets-ui/src/views/defined-name/DefinedNameContainer.tsx @@ -192,12 +192,12 @@ export const DefinedNameContainer = () => {
{definedName.formulaOrRefString}
- +
{ closeInsertOpenKeyEditor(definedName.id); }}>
- +
{ deleteDefinedName(definedName.id); }}>
diff --git a/packages/sheets-ui/src/views/defined-name/DefinedNameOverlay.tsx b/packages/sheets-ui/src/views/defined-name/DefinedNameOverlay.tsx index ea6578afa3..5396dbd404 100644 --- a/packages/sheets-ui/src/views/defined-name/DefinedNameOverlay.tsx +++ b/packages/sheets-ui/src/views/defined-name/DefinedNameOverlay.tsx @@ -96,7 +96,7 @@ export function DefinedNameOverlay(props: IDefinedNameOverlayProps) { }; return ( -
+
    { focusDefinedName(definedName); }} @@ -146,6 +147,7 @@ export function DefinedNameOverlay(props: IDefinedNameOverlayProps) {
    {
    - +
    diff --git a/packages/sheets-zen-editor/src/global.css b/packages/sheets-zen-editor/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/sheets-zen-editor/src/global.css +++ b/packages/sheets-zen-editor/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/slides-ui/src/global.css b/packages/slides-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/slides-ui/src/global.css +++ b/packages/slides-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/thread-comment-ui/src/global.css b/packages/thread-comment-ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/thread-comment-ui/src/global.css +++ b/packages/thread-comment-ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/ui/src/global.css b/packages/ui/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/ui/src/global.css +++ b/packages/ui/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/packages/ui/src/views/components/ribbon/Ribbon.tsx b/packages/ui/src/views/components/ribbon/Ribbon.tsx index 62ea6701ec..edef326d70 100644 --- a/packages/ui/src/views/components/ribbon/Ribbon.tsx +++ b/packages/ui/src/views/components/ribbon/Ribbon.tsx @@ -17,10 +17,10 @@ import type { ComponentType } from 'react'; import type { IMenuSchema } from '../../../services/menu/menu-manager.service'; import { LocaleService, useDependency } from '@univerjs/core'; -import { MoreFunctionSingle } from '@univerjs/icons'; -import clsx from 'clsx'; +import { clsx } from '@univerjs/design'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { MoreFunctionSingle } from '@univerjs/icons'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { IMenuManagerService } from '../../../services/menu/menu-manager.service'; import { MenuManagerPosition, RibbonPosition } from '../../../services/menu/types'; import { ComponentContainer } from '../ComponentContainer'; @@ -40,12 +40,17 @@ export function Ribbon(props: IRibbonProps) { const localeService = useDependency(LocaleService); const toolbarRef = useRef(null); - const toolbarItemRefs = useRef>({}); + const toolbarItemRefs = useRef>({}); const [ribbon, setRibbon] = useState([]); - const [category, setCategory] = useState(RibbonPosition.START); + const [activatedTab, setActivatedTab] = useState(RibbonPosition.START); const [collapsedIds, setCollapsedIds] = useState([]); + const handleSelectTab = useCallback((group: IMenuSchema) => { + toolbarItemRefs.current = {}; + setActivatedTab(group.key); + }, []); + // subscribe to menu changes useEffect(() => { function getRibbon(): void { @@ -64,22 +69,28 @@ export function Ribbon(props: IRibbonProps) { // resize observer useEffect(() => { const observer = new ResizeObserver((entries) => { - const toolbar = entries[0].target; - const toolbarWidth = toolbar.clientWidth; - const toolbarItems = Object.values(toolbarItemRefs.current); - const collapsedIds: string[] = []; - let totalWidth = 90; + requestAnimationFrame(() => { + const toolbar = entries[0].target; + const toolbarWidth = toolbar.clientWidth; + const toolbarItems = Object.values(toolbarItemRefs.current); + const collapsedIds: string[] = []; + let totalWidth = 0; - for (const { el, key } of toolbarItems) { - if (!el) continue; + const allGroups = ribbon.find((group) => group.key === activatedTab)?.children ?? []; + const a = []; - totalWidth += el.clientWidth + 8; - if (totalWidth > toolbarWidth) { - collapsedIds.push(key); + for (const { el, key } of toolbarItems) { + if (!el) continue; + a.push(el); + + totalWidth += el.getBoundingClientRect().width + 8; + if (totalWidth > toolbarWidth - 32 - 8 * (allGroups.length - 1)) { + collapsedIds.push(key); + } } - } - setCollapsedIds(collapsedIds); + setCollapsedIds(collapsedIds); + }); }); if (toolbarRef.current) { @@ -89,13 +100,10 @@ export function Ribbon(props: IRibbonProps) { return () => { observer.disconnect(); }; - }, [ribbon]); - - // Should the header when there is at least one header menu components or menu groups. - const hasHeaderMenu = useMemo(() => (headerMenuComponents && headerMenuComponents.size > 0) || ribbon.length > 1, [headerMenuComponents, ribbon]); + }, [ribbon, activatedTab]); const activeGroup = useMemo(() => { - const allGroups = ribbon.find((group) => group.key === category)?.children ?? []; + const allGroups = ribbon.find((group) => group.key === activatedTab)?.children ?? []; const visibleGroups: IMenuSchema[] = []; const hiddenGroups: IMenuSchema[] = []; @@ -115,7 +123,7 @@ export function Ribbon(props: IRibbonProps) { visibleGroups, hiddenGroups, }; - }, [ribbon, category, collapsedIds]); + }, [ribbon, activatedTab, collapsedIds]); const fakeToolbarContent = useMemo(() => ( activeGroup.allGroups.map((groupItem) => ( @@ -124,13 +132,15 @@ export function Ribbon(props: IRibbonProps) { child.item && ( { - toolbarItemRefs.current[child.key] = { - el: ref?.nativeElement as HTMLDivElement, - key: child.key, - }; - }} {...child.item} + ref={(ref) => { + if (ref?.el) { + toolbarItemRefs.current[child.key] = { + el: ref.el, + key: child.key, + }; + } + }} /> ) ))} @@ -141,30 +151,40 @@ export function Ribbon(props: IRibbonProps) { return ( <> {/* header */} - {hasHeaderMenu && ( -
    -
    - {ribbon.length > 1 && ribbon.map((group) => ( - { - setCategory(group.key); - }} - > - {localeService.t(group.key)} - - ))} -
    +
    +
    1, + })} + > + {ribbon.length > 1 && ribbon.map((group) => ( + handleSelectTab(group)} + > + {localeService.t(group.key)} + + ))} +
    + + {(headerMenuComponents && headerMenuComponents.size > 0) && (
    -
    - )} + )} +
    -
    +
    {activeGroup.visibleGroups.map((groupItem) => (groupItem.children?.length || groupItem.item) && (
    @@ -182,7 +202,6 @@ export function Ribbon(props: IRibbonProps) { {collapsedIds.length > 0 && ( {activeGroup.hiddenGroups.map((groupItem) => ( @@ -210,6 +229,7 @@ export function Ribbon(props: IRibbonProps) { {/* fake toolbar for calculating overflow width */}
    {fakeToolbarContent} diff --git a/packages/ui/src/views/components/ribbon/ToolbarItem.tsx b/packages/ui/src/views/components/ribbon/ToolbarItem.tsx index cdf5089b6e..014710aea3 100644 --- a/packages/ui/src/views/components/ribbon/ToolbarItem.tsx +++ b/packages/ui/src/views/components/ribbon/ToolbarItem.tsx @@ -15,8 +15,8 @@ */ import type { IDropdownLegacyProps } from '@univerjs/design'; -import type { Ref } from 'react'; import type { IDisplayMenuItem, IMenuItem, IMenuSelectorItem, IValueOption } from '../../../services/menu/menu'; +import type { ITooltipWrapperRef } from './TooltipButtonWrapper'; import { ICommandService, LocaleService, useDependency } from '@univerjs/core'; import { MoreDownSingle } from '@univerjs/icons'; import clsx from 'clsx'; @@ -33,9 +33,7 @@ import { useToolbarItemStatus } from './hook'; import styles from './index.module.less'; import { DropdownWrapper, TooltipWrapper } from './TooltipButtonWrapper'; -export const ToolbarItem = forwardRef((props: IDisplayMenuItem & { align?: IDropdownLegacyProps['align'] }, ref: Ref) => { - const { align } = props; - +export const ToolbarItem = forwardRef & { align?: IDropdownLegacyProps['align'] }>((props, ref) => { const localeService = useDependency(LocaleService); const commandService = useDependency(ICommandService); const layoutService = useDependency(ILayoutService); @@ -109,13 +107,23 @@ export const ToolbarItem = forwardRef((props: IDisplayMenuItem & { al if (menuType === MenuItemType.BUTTON_SELECTOR) { return (
    -
    +
    & { al onChange={handleSelectionsValueChange} />
    + & { al )} >
    - +
    @@ -167,7 +179,7 @@ export const ToolbarItem = forwardRef((props: IDisplayMenuItem & { al >
    @@ -180,10 +192,10 @@ export const ToolbarItem = forwardRef((props: IDisplayMenuItem & { al />
    - +
    @@ -228,7 +240,11 @@ export const ToolbarItem = forwardRef((props: IDisplayMenuItem & { al } return !hidden && ( - + {renderItem()} ); diff --git a/packages/ui/src/views/components/ribbon/TooltipButtonWrapper.tsx b/packages/ui/src/views/components/ribbon/TooltipButtonWrapper.tsx index 4e1b9383e8..b6ff8c64ba 100644 --- a/packages/ui/src/views/components/ribbon/TooltipButtonWrapper.tsx +++ b/packages/ui/src/views/components/ribbon/TooltipButtonWrapper.tsx @@ -14,17 +14,22 @@ * limitations under the License. */ -import type { IDropdownLegacyProps, ITooltipProps, NullableTooltipRef } from '@univerjs/design'; -import { DropdownLegacy, Tooltip } from '@univerjs/design'; -import React, { createContext, forwardRef, useContext, useMemo, useState } from 'react'; +import type { IDropdownOverlayProps, ITooltipProps } from '@univerjs/design'; +import { DropdownOverlay, DropdownProvider, DropdownTrigger, Tooltip } from '@univerjs/design'; +import React, { createContext, forwardRef, useContext, useImperativeHandle, useMemo, useRef, useState } from 'react'; const TooltipWrapperContext = createContext({ dropdownVisible: false, setDropdownVisible: (_visible: boolean) => {}, }); -export const TooltipWrapper = forwardRef((props, ref) => { +export interface ITooltipWrapperRef { + el: HTMLSpanElement | null; +} + +export const TooltipWrapper = forwardRef((props, ref) => { const { children, ...tooltipProps } = props; + const spanRef = useRef(null); const [tooltipVisible, setTooltipVisible] = useState(false); const [dropdownVisible, setDropdownVisible] = useState(false); @@ -48,14 +53,17 @@ export const TooltipWrapper = forwardRef((pro setDropdownVisible: handleChangeDropdownVisible, }), [dropdownVisible]); + useImperativeHandle(ref, () => ({ + el: spanRef.current, + })); + return ( - + {children} @@ -64,9 +72,7 @@ export const TooltipWrapper = forwardRef((pro ); }); -export function DropdownWrapper(props: IDropdownLegacyProps) { - const { children, ...dropdownProps } = props; - +export function DropdownWrapper({ children, overlay, disabled, offset }: IDropdownOverlayProps & { overlay: React.ReactNode; disabled?: boolean }) { const { setDropdownVisible } = useContext(TooltipWrapperContext); function handleVisibleChange(visible: boolean) { @@ -74,13 +80,18 @@ export function DropdownWrapper(props: IDropdownLegacyProps) { } return ( - -
    e.stopPropagation()}> - {children} -
    -
    + + +
    e.stopPropagation()}> + {children} +
    +
    + + {/* TODO: When the new Menu Component is ready, plz remove the univer-theme class */} +
    + {overlay} +
    +
    +
    ); } diff --git a/packages/ui/src/views/components/ribbon/index.module.less b/packages/ui/src/views/components/ribbon/index.module.less index f0a73cceff..9753182ab7 100644 --- a/packages/ui/src/views/components/ribbon/index.module.less +++ b/packages/ui/src/views/components/ribbon/index.module.less @@ -1,73 +1,5 @@ @toolbar-btn-size: 24px; -.headerbar { - user-select: none; - position: relative; - - > .header-menu { - position: absolute; - top: 0; - right: 0; - - display: flex; - - box-sizing: border-box; - height: 100%; - - > div { - user-select: none; - - display: inline-flex; - align-items: center; - - height: 28px; - margin-left: 4px; - padding: 2px 4px; - - text-align: center; - - border-radius: 4px; - - &:hover { - background-color: rgb(var(--grey-300)); - } - } - } -} - -.menubar { - display: flex; - gap: var(--margin-xs); - align-items: center; - justify-content: center; - - height: 32px; - - &-item { - cursor: pointer; - - display: block; - - padding: var(--padding-xs) var(--padding-sm); - - font-size: var(--font-size-xs); - font-weight: 400; - - border-radius: var(--border-radius-base); - - transition: all 0.1s ease-in-out; - - &:hover:not(&-active) { - background: rgb(var(--grey-300)); - } - - &-active { - color: rgb(var(--color-white)); - background: rgb(var(--primary-color)); - } - } -} - .toolbar { user-select: none; @@ -162,12 +94,8 @@ overflow: hidden; display: flex; - gap: var(--margin-xs); align-items: center; - height: @toolbar-btn-size; - padding-right: 18px; - font-size: var(--font-size-lg); border-radius: var(--border-radius-base); @@ -192,20 +120,6 @@ background-color: rgb(var(--grey-100)); } - &:hover { - background-color: rgb(var(--grey-100)); - - .toolbar { - &-item { - &-select-button { - &-label { - background-color: rgb(var(--grey-100)); - } - } - } - } - } - &-label { position: relative; z-index: 1; @@ -215,31 +129,6 @@ height: 100%; padding: 0 var(--padding-xs); - - &:hover { - background-color: rgb(var(--grey-300)) !important; - } - } - - &-arrow { - position: absolute; - top: 0; - left: 0; - - display: flex; - align-items: center; - justify-content: flex-end; - - box-sizing: border-box; - width: 100%; - height: 100%; - padding-right: var(--padding-xs); - - color: rgb(var(--grey-400)); - - &:hover { - background-color: rgb(var(--grey-300)); - } } } @@ -262,16 +151,6 @@ &-disabled { cursor: not-allowed; color: rgb(var(--grey-200)); - &.toolbar-item-select-activated { - color: rgba(var(--text-color), 0.25); - &:hover { - background-color: rgb(var(--grey-100)); - } - } - } - - &-activated { - background-color: rgb(var(--grey-100)); } &:hover { @@ -297,10 +176,6 @@ } } } - - &-activated { - background-color: rgb(var(--grey-100)); - } } } } diff --git a/packages/uniscript/src/global.css b/packages/uniscript/src/global.css index b5c61c9567..7c05d4ddc4 100644 --- a/packages/uniscript/src/global.css +++ b/packages/uniscript/src/global.css @@ -1,3 +1,2 @@ -@tailwind base; @tailwind components; @tailwind utilities; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f5b72040e7..1e28eb7e96 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1152,6 +1152,9 @@ importers: packages/design: dependencies: + '@radix-ui/react-tooltip': + specifier: ^1.1.6 + version: 1.1.6(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@rc-component/color-picker': specifier: ^2.0.1 version: 2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -4569,6 +4572,206 @@ packages: '@protobufjs/utf8@1.1.0': resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + '@radix-ui/primitive@1.1.1': + resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} + + '@radix-ui/react-arrow@1.1.1': + resolution: {integrity: sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.1': + resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.1': + resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.3': + resolution: {integrity: sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.0': + resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-popper@1.2.1': + resolution: {integrity: sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.3': + resolution: {integrity: sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.2': + resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.0.1': + resolution: {integrity: sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.1.1': + resolution: {integrity: sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-tooltip@1.1.6': + resolution: {integrity: sha512-TLB5D8QLExS1uDn7+wH/bjEmRurNMTzNrtq7IjaS4kjion9NtzsTGkvR5+i7yc9q01Pi2KMM2cN3f8UG4IvvXA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.0': + resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.1.0': + resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.0': + resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.0': + resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.0': + resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.0': + resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.1.1': + resolution: {integrity: sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/rect@1.1.0': + resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} + '@rc-component/color-picker@2.0.1': resolution: {integrity: sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==} peerDependencies: @@ -11705,6 +11908,174 @@ snapshots: '@protobufjs/utf8@1.1.0': {} + '@radix-ui/primitive@1.1.1': {} + + '@radix-ui/react-arrow@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.12 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-compose-refs@1.1.1(@types/react@18.3.12)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + + '@radix-ui/react-context@1.1.1(@types/react@18.3.12)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + + '@radix-ui/react-dismissable-layer@1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.12)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.12 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-id@1.1.0(@types/react@18.3.12)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + + '@radix-ui/react-popper@1.2.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-arrow': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-use-rect': 1.1.0(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/rect': 1.1.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.12 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-portal@1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.12 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-presence@1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.12 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-primitive@2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.1.1(@types/react@18.3.12)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.12 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-slot@1.1.1(@types/react@18.3.12)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + + '@radix-ui/react-tooltip@1.1.6(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-popper': 1.2.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.1(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.12 + '@types/react-dom': 18.3.1 + + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.12)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.12)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.3.12)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.12)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + + '@radix-ui/react-use-rect@1.1.0(@types/react@18.3.12)(react@18.3.1)': + dependencies: + '@radix-ui/rect': 1.1.0 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + + '@radix-ui/react-use-size@1.1.0(@types/react@18.3.12)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + + '@radix-ui/react-visually-hidden@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.12 + '@types/react-dom': 18.3.1 + + '@radix-ui/rect@1.1.0': {} + '@rc-component/color-picker@2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@ant-design/fast-color': 2.0.6