@aloudata/aloudata-design 3.0.0-beta.13 → 3.0.0-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/AProgress/index.d.ts +1 -1
  2. package/dist/AProgress/index.js +19 -5
  3. package/dist/AProgress/index.js.map +1 -1
  4. package/dist/Alert/index.d.ts +1 -1
  5. package/dist/Alert/index.js +22 -8
  6. package/dist/Alert/index.js.map +1 -1
  7. package/dist/Avatar/component/Avatar/index.d.ts +1 -1
  8. package/dist/Avatar/component/Avatar/index.js +4 -4
  9. package/dist/Avatar/component/Avatar/index.js.map +1 -1
  10. package/dist/Avatar/component/Avatar/type.d.ts +1 -1
  11. package/dist/Avatar/component/Avatar/type.js +6 -1
  12. package/dist/Avatar/component/Avatar/type.js.map +1 -1
  13. package/dist/Avatar/index.js +67 -8
  14. package/dist/Avatar/index.js.map +1 -1
  15. package/dist/Badge/index.d.ts +1 -0
  16. package/dist/Badge/index.js +32 -9
  17. package/dist/Badge/index.js.map +1 -1
  18. package/dist/Breadcrumb/index.js +21 -12
  19. package/dist/Breadcrumb/index.js.map +1 -1
  20. package/dist/Button/index.d.ts +2 -0
  21. package/dist/Button/index.js +39 -32
  22. package/dist/Button/index.js.map +1 -1
  23. package/dist/Card/index.js +5 -5
  24. package/dist/Card/index.js.map +1 -1
  25. package/dist/Checkbox/index.js +4 -11
  26. package/dist/Checkbox/index.js.map +1 -1
  27. package/dist/Checkbox/type.d.ts +2 -0
  28. package/dist/Collapse/index.js +38 -22
  29. package/dist/Collapse/index.js.map +1 -1
  30. package/dist/DataPreviewTable/index.js +1 -1
  31. package/dist/DataPreviewTable/index.js.map +1 -1
  32. package/dist/Drawer/index.d.ts +1 -0
  33. package/dist/Drawer/index.js +45 -33
  34. package/dist/Drawer/index.js.map +1 -1
  35. package/dist/Dropdown/index.d.ts +5 -0
  36. package/dist/Dropdown/index.js +151 -34
  37. package/dist/Dropdown/index.js.map +1 -1
  38. package/dist/LogicTree/DisplayLogicTree.d.ts +1 -1
  39. package/dist/LogicTree/DisplayLogicTree.js.map +1 -1
  40. package/dist/LogicTree/components/DisplayLogicItem/index.d.ts +1 -1
  41. package/dist/LogicTree/components/DisplayLogicItem/index.js +1 -1
  42. package/dist/LogicTree/components/DisplayLogicItem/index.js.map +1 -1
  43. package/dist/LogicTree/components/LogicItem/index.js +2 -3
  44. package/dist/LogicTree/components/LogicItem/index.js.map +1 -1
  45. package/dist/LogicTree/index.d.ts +1 -1
  46. package/dist/LogicTree/index.js.map +1 -1
  47. package/dist/MemberPicker/components/NickLabel.js +1 -1
  48. package/dist/MemberPicker/components/NickLabel.js.map +1 -1
  49. package/dist/Menu/index.d.ts +4 -0
  50. package/dist/Menu/index.js +34 -12
  51. package/dist/Menu/index.js.map +1 -1
  52. package/dist/Progress/index.d.ts +0 -3
  53. package/dist/Progress/index.js +0 -3
  54. package/dist/Progress/index.js.map +1 -1
  55. package/dist/Table/components/Footer/index.js +1 -1
  56. package/dist/Table/components/Footer/index.js.map +1 -1
  57. package/dist/Table/hooks/useRowSelection.d.ts +1 -1
  58. package/dist/Table/hooks/useRowSelection.js +7 -9
  59. package/dist/Table/hooks/useRowSelection.js.map +1 -1
  60. package/dist/Table/index.js +1 -1
  61. package/dist/Table/index.js.map +1 -1
  62. package/dist/_utils/storybookArgTypes.d.ts +11 -0
  63. package/dist/_utils/storybookArgTypes.js +2 -0
  64. package/dist/aloudata-design.css +1 -1
  65. package/dist/index.d.ts +3 -2
  66. package/dist/index.js +1 -1
  67. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/Collapse/index.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { ArrowDownLightLine, ArrowRightLightLine } from '../Icon';\nimport { cn } from '../lib/utils';\n\nexport interface CollapsePanelProps {\n key: string;\n header?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n disabled?: boolean;\n extra?: React.ReactNode;\n forceRender?: boolean;\n collapsible?: 'header' | 'icon' | 'disabled';\n showArrow?: boolean;\n ref?: React.Ref<HTMLDivElement>;\n}\n\nexport interface CollapseProps {\n activeKey?: string | string[];\n defaultActiveKey?: string | string[];\n accordion?: boolean;\n onChange?: (key: string | string[]) => void;\n className?: string;\n style?: React.CSSProperties;\n bordered?: boolean;\n expandIconPosition?: 'start' | 'end' | 'left' | 'right';\n expandIcon?: (props: { isActive: boolean }) => React.ReactNode;\n ghost?: boolean;\n size?: 'large' | 'middle' | 'small';\n collapsible?: 'header' | 'icon' | 'disabled';\n children?: React.ReactNode;\n items?: Array<{\n key: string;\n label: React.ReactNode;\n children?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n extra?: React.ReactNode;\n disabled?: boolean;\n collapsible?: 'header' | 'icon' | 'disabled';\n forceRender?: boolean;\n }>;\n}\n\n/** Internal rendering component for a single panel */\nfunction CollapsePanelInternal(props: {\n panelKey: string;\n header: React.ReactNode;\n isActive: boolean;\n onToggle: () => void;\n disabled?: boolean;\n extra?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n forceRender?: boolean;\n expandIcon?: (props: { isActive: boolean }) => React.ReactNode;\n expandIconPosition?: 'start' | 'end';\n}) {\n const {\n header,\n isActive,\n onToggle,\n disabled,\n extra,\n className,\n style,\n children,\n forceRender,\n expandIcon,\n expandIconPosition = 'start',\n } = props;\n\n const iconNode = expandIcon ? (\n expandIcon({ isActive })\n ) : isActive ? (\n <ArrowDownLightLine size={20} color=\"var(--alias-colors-icon-subtle)\" />\n ) : (\n <ArrowRightLightLine size={20} color=\"var(--alias-colors-icon-subtle)\" />\n );\n\n // antd 兼容:保留 ant-collapse-expand-icon class,消费方 CSS 可能依赖该选择器\n const iconElement = (\n <span className=\"ald-collapse-expand-icon ant-collapse-expand-icon tw-flex tw-items-center\">\n {iconNode}\n </span>\n );\n\n return (\n <div\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-collapse-item ant-collapse-item tw-border-b tw-border-solid tw-border-[var(--border-default)]',\n isActive && 'ald-collapse-item-active',\n disabled && 'tw-pointer-events-none tw-opacity-50',\n className,\n )}\n style={style}\n >\n <div\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n className=\"ald-collapse-header ant-collapse-header tw-flex tw-cursor-pointer tw-items-center tw-gap-2 tw-px-4 tw-py-3\"\n onClick={disabled ? undefined : onToggle}\n >\n {expandIconPosition === 'start' && iconElement}\n {/* antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器 */}\n <span className=\"ald-collapse-header-text ant-collapse-header-text tw-flex-1\">\n {header}\n </span>\n {/* antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器 */}\n {extra && (\n <span className=\"ald-collapse-extra ant-collapse-extra\">{extra}</span>\n )}\n {expandIconPosition === 'end' && iconElement}\n </div>\n {(isActive || forceRender) && (\n <div\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n className=\"ald-collapse-content ant-collapse-content tw-px-4 tw-pb-4\"\n style={!isActive && forceRender ? { display: 'none' } : undefined}\n >\n {children}\n </div>\n )}\n </div>\n );\n}\n\n/**\n * Checks whether a React element is a Collapse.Panel.\n * We tag the Panel component with a `__COLLAPSE_PANEL` flag\n * so we can detect it regardless of module wrapper differences.\n */\nfunction isPanelElement(\n child: React.ReactNode,\n): child is React.ReactElement<CollapsePanelProps> {\n if (!React.isValidElement(child)) return false;\n return (child.type as any)?.__COLLAPSE_PANEL === true;\n}\n\n/**\n * Convert Collapse.Panel children into the internal items format.\n */\nfunction panelChildrenToItems(children: React.ReactNode) {\n const items: Array<{\n key: string;\n label: React.ReactNode;\n children?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n extra?: React.ReactNode;\n disabled?: boolean;\n collapsible?: 'header' | 'icon' | 'disabled';\n forceRender?: boolean;\n }> = [];\n\n React.Children.forEach(children, (child) => {\n if (!isPanelElement(child)) return;\n const {\n header,\n children: panelChildren,\n className,\n style,\n extra,\n disabled,\n collapsible,\n forceRender,\n } = child.props;\n\n // The `key` prop is not part of child.props in React — access it from child.key\n const panelKey =\n child.key !== null && child.key !== undefined ? String(child.key) : '';\n\n items.push({\n key: panelKey,\n label: header,\n children: panelChildren,\n className,\n style,\n extra,\n disabled,\n collapsible,\n forceRender,\n });\n });\n\n return items;\n}\n\nconst Collapse = (props: CollapseProps) => {\n const {\n defaultActiveKey = [],\n activeKey: controlledActiveKey,\n accordion,\n onChange,\n className,\n style,\n bordered = true,\n expandIconPosition,\n expandIcon,\n items: itemsProp,\n children,\n } = props;\n\n const resolvedPosition =\n expandIconPosition === 'left'\n ? 'start'\n : expandIconPosition === 'right'\n ? 'end'\n : expandIconPosition ?? 'start';\n\n const normalizeKeys = (keys: string | string[] | undefined): string[] => {\n if (!keys) return [];\n return Array.isArray(keys) ? keys : [keys];\n };\n\n const [internalActiveKey, setInternalActiveKey] = useState<string[]>(\n normalizeKeys(defaultActiveKey),\n );\n\n const activeKeys =\n controlledActiveKey !== undefined\n ? normalizeKeys(controlledActiveKey)\n : internalActiveKey;\n\n const toggleKey = (key: string) => {\n let newKeys: string[];\n if (accordion) {\n newKeys = activeKeys.includes(key) ? [] : [key];\n } else {\n newKeys = activeKeys.includes(key)\n ? activeKeys.filter((k) => k !== key)\n : [...activeKeys, key];\n }\n if (controlledActiveKey === undefined) {\n setInternalActiveKey(newKeys);\n }\n onChange?.(accordion ? newKeys[0] || '' : newKeys);\n };\n\n // Resolve items: from `items` prop, or by converting Panel children\n const items =\n itemsProp || (children ? panelChildrenToItems(children) : undefined);\n\n const renderItems = () => {\n if (items && items.length > 0) {\n return items.map((item) => (\n <CollapsePanelInternal\n key={item.key}\n panelKey={item.key}\n header={item.label}\n isActive={activeKeys.includes(item.key)}\n onToggle={() => toggleKey(item.key)}\n disabled={item.disabled || item.collapsible === 'disabled'}\n extra={item.extra}\n className={item.className}\n style={item.style}\n forceRender={item.forceRender}\n expandIcon={expandIcon}\n expandIconPosition={resolvedPosition}\n >\n {item.children}\n </CollapsePanelInternal>\n ));\n }\n // If children are not Panel elements (rare fallback), render them as-is\n return children;\n };\n\n return (\n <div\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-collapse ant-collapse',\n bordered &&\n 'tw-rounded-[6px] tw-border tw-border-solid tw-border-[var(--alias-colors-border-default)] tw-bg-[var(--alias-colors-bg-skeleton-subtle)]',\n className,\n )}\n style={style}\n >\n {renderItems()}\n </div>\n );\n};\n\n/**\n * Collapse.Panel — legacy sub-component for backwards compatibility.\n * When used as a child of <Collapse>, the parent extracts its props\n * and renders the panels via the internal CollapsePanelInternal component.\n * Panel itself never renders directly; the parent handles rendering.\n */\nconst Panel: React.FC<CollapsePanelProps> = ({ children }) => {\n // This component should never render on its own — Collapse extracts\n // its props. But if used outside of Collapse, render children as fallback.\n return <>{children}</>;\n};\n\n// Tag for detection by isPanelElement\n(Panel as any).__COLLAPSE_PANEL = true;\n\nCollapse.Panel = Panel;\n\nexport default Collapse;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/Collapse/index.tsx"],"sourcesContent":["import React, { useRef, useState } from 'react';\nimport { ArrowRightLightLine } from '../Icon';\nimport { cn } from '../lib/utils';\n\nexport interface CollapsePanelProps {\n key: string;\n header?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n disabled?: boolean;\n extra?: React.ReactNode;\n forceRender?: boolean;\n collapsible?: 'header' | 'icon' | 'disabled';\n showArrow?: boolean;\n ref?: React.Ref<HTMLDivElement>;\n}\n\nexport interface CollapseProps {\n activeKey?: string | string[];\n defaultActiveKey?: string | string[];\n accordion?: boolean;\n onChange?: (key: string | string[]) => void;\n className?: string;\n style?: React.CSSProperties;\n bordered?: boolean;\n expandIconPosition?: 'start' | 'end' | 'left' | 'right';\n expandIcon?: (props: { isActive: boolean }) => React.ReactNode;\n ghost?: boolean;\n size?: 'large' | 'middle' | 'small';\n collapsible?: 'header' | 'icon' | 'disabled';\n children?: React.ReactNode;\n items?: Array<{\n key: string;\n label: React.ReactNode;\n children?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n extra?: React.ReactNode;\n disabled?: boolean;\n collapsible?: 'header' | 'icon' | 'disabled';\n forceRender?: boolean;\n }>;\n}\n\n/** Internal rendering component for a single panel */\nfunction CollapsePanelInternal(props: {\n panelKey: string;\n header: React.ReactNode;\n isActive: boolean;\n onToggle: () => void;\n disabled?: boolean;\n extra?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n forceRender?: boolean;\n expandIcon?: (props: { isActive: boolean }) => React.ReactNode;\n expandIconPosition?: 'start' | 'end';\n bordered?: boolean;\n}) {\n const {\n header,\n isActive,\n onToggle,\n disabled,\n extra,\n className,\n style,\n children,\n forceRender,\n expandIcon,\n expandIconPosition = 'start',\n bordered,\n } = props;\n\n const contentRef = useRef<HTMLDivElement>(null);\n const [rendered, setRendered] = useState(isActive || !!forceRender);\n\n // Ensure content is in DOM before expanding so transition can play\n if (isActive && !rendered) {\n setRendered(true);\n }\n\n const handleTransitionEnd = () => {\n if (!isActive && !forceRender) {\n setRendered(false);\n }\n };\n\n const iconNode = expandIcon ? (\n expandIcon({ isActive })\n ) : (\n <ArrowRightLightLine size={20} color=\"var(--alias-colors-icon-subtle)\" />\n );\n\n // antd 兼容:保留 ant-collapse-expand-icon class,消费方 CSS 可能依赖该选择器\n const iconElement = (\n <span\n className=\"ald-collapse-expand-icon ant-collapse-expand-icon tw-flex tw-items-center\"\n style={{\n transform: isActive ? 'rotate(90deg)' : 'rotate(0deg)',\n transition: 'transform 200ms',\n }}\n >\n {iconNode}\n </span>\n );\n\n return (\n <div\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-collapse-item ant-collapse-item tw-w-full',\n isActive && 'ald-collapse-item-active',\n disabled && 'tw-pointer-events-none tw-opacity-50',\n className,\n )}\n style={style}\n >\n <div\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n className=\"ald-collapse-header ant-collapse-header tw-flex tw-cursor-pointer tw-items-center tw-gap-2 tw-p-3\"\n onClick={disabled ? undefined : onToggle}\n >\n {expandIconPosition === 'start' && iconElement}\n {/* antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器 */}\n <span className=\"ald-collapse-header-text ant-collapse-header-text tw-flex-1 tw-text-sm tw-font-medium tw-leading-5 tw-text-[var(--content-primary)]\">\n {header}\n </span>\n {/* antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器 */}\n {extra && (\n <span className=\"ald-collapse-extra ant-collapse-extra\">{extra}</span>\n )}\n {expandIconPosition === 'end' && iconElement}\n </div>\n <div\n className=\"tw-grid tw-transition-[grid-template-rows] tw-duration-200\"\n style={{ gridTemplateRows: isActive ? '1fr' : '0fr' }}\n onTransitionEnd={handleTransitionEnd}\n >\n <div className=\"tw-overflow-hidden\">\n {rendered && (\n <div\n ref={contentRef}\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n className={cn(\n 'ald-collapse-content ant-collapse-content tw-flex tw-items-start tw-gap-2.5 tw-self-stretch',\n bordered\n ? 'tw-bg-[var(--background-default)] tw-p-4'\n : 'tw-pb-4 tw-pl-10 tw-pr-4 tw-pt-0',\n )}\n >\n {children}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * Checks whether a React element is a Collapse.Panel.\n * We tag the Panel component with a `__COLLAPSE_PANEL` flag\n * so we can detect it regardless of module wrapper differences.\n */\nfunction isPanelElement(\n child: React.ReactNode,\n): child is React.ReactElement<CollapsePanelProps> {\n if (!React.isValidElement(child)) return false;\n return (child.type as any)?.__COLLAPSE_PANEL === true;\n}\n\n/**\n * Convert Collapse.Panel children into the internal items format.\n */\nfunction panelChildrenToItems(children: React.ReactNode) {\n const items: Array<{\n key: string;\n label: React.ReactNode;\n children?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n extra?: React.ReactNode;\n disabled?: boolean;\n collapsible?: 'header' | 'icon' | 'disabled';\n forceRender?: boolean;\n }> = [];\n\n React.Children.forEach(children, (child) => {\n if (!isPanelElement(child)) return;\n const {\n header,\n children: panelChildren,\n className,\n style,\n extra,\n disabled,\n collapsible,\n forceRender,\n } = child.props;\n\n // The `key` prop is not part of child.props in React — access it from child.key\n const panelKey =\n child.key !== null && child.key !== undefined ? String(child.key) : '';\n\n items.push({\n key: panelKey,\n label: header,\n children: panelChildren,\n className,\n style,\n extra,\n disabled,\n collapsible,\n forceRender,\n });\n });\n\n return items;\n}\n\nconst Collapse = (props: CollapseProps) => {\n const {\n defaultActiveKey = [],\n activeKey: controlledActiveKey,\n accordion,\n onChange,\n className,\n style,\n bordered = true,\n ghost,\n expandIconPosition,\n expandIcon,\n items: itemsProp,\n children,\n } = props;\n\n const resolvedPosition =\n expandIconPosition === 'left'\n ? 'start'\n : expandIconPosition === 'right'\n ? 'end'\n : expandIconPosition ?? 'start';\n\n const normalizeKeys = (keys: string | string[] | undefined): string[] => {\n if (!keys) return [];\n return Array.isArray(keys) ? keys : [keys];\n };\n\n const [internalActiveKey, setInternalActiveKey] = useState<string[]>(\n normalizeKeys(defaultActiveKey),\n );\n\n const activeKeys =\n controlledActiveKey !== undefined\n ? normalizeKeys(controlledActiveKey)\n : internalActiveKey;\n\n const toggleKey = (key: string) => {\n let newKeys: string[];\n if (accordion) {\n newKeys = activeKeys.includes(key) ? [] : [key];\n } else {\n newKeys = activeKeys.includes(key)\n ? activeKeys.filter((k) => k !== key)\n : [...activeKeys, key];\n }\n if (controlledActiveKey === undefined) {\n setInternalActiveKey(newKeys);\n }\n onChange?.(accordion ? newKeys[0] || '' : newKeys);\n };\n\n // Resolve items: from `items` prop, or by converting Panel children\n const items =\n itemsProp || (children ? panelChildrenToItems(children) : undefined);\n\n const renderItems = () => {\n if (items && items.length > 0) {\n return items.map((item, index) => (\n <React.Fragment key={item.key}>\n {index > 0 && <div className=\"ald-collapse-divider\" />}\n <CollapsePanelInternal\n panelKey={item.key}\n header={item.label}\n isActive={activeKeys.includes(item.key)}\n onToggle={() => toggleKey(item.key)}\n disabled={item.disabled || item.collapsible === 'disabled'}\n extra={item.extra}\n className={item.className}\n style={item.style}\n forceRender={item.forceRender}\n expandIcon={expandIcon}\n expandIconPosition={resolvedPosition}\n bordered={bordered}\n >\n {item.children}\n </CollapsePanelInternal>\n </React.Fragment>\n ));\n }\n // If children are not Panel elements (rare fallback), render them as-is\n return children;\n };\n\n return (\n <div\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-collapse ant-collapse tw-flex tw-flex-col tw-items-start tw-overflow-hidden tw-rounded-r-75 [&>.ald-collapse-divider]:tw-h-px [&>.ald-collapse-divider]:tw-w-full [&>.ald-collapse-divider]:tw-bg-[var(--border-default)]',\n !bordered && !ghost && 'tw-bg-[var(--background-neutral-subtle)]',\n bordered &&\n 'tw-border tw-border-solid tw-border-[var(--border-default)] tw-bg-[var(--background-neutral-subtle)]',\n className,\n )}\n style={style}\n >\n {renderItems()}\n </div>\n );\n};\n\n/**\n * Collapse.Panel — legacy sub-component for backwards compatibility.\n * When used as a child of <Collapse>, the parent extracts its props\n * and renders the panels via the internal CollapsePanelInternal component.\n * Panel itself never renders directly; the parent handles rendering.\n */\nconst Panel: React.FC<CollapsePanelProps> = ({ children }) => {\n // This component should never render on its own — Collapse extracts\n // its props. But if used outside of Collapse, render children as fallback.\n return <>{children}</>;\n};\n\n// Tag for detection by isPanelElement\n(Panel as any).__COLLAPSE_PANEL = true;\n\nCollapse.Panel = Panel;\n\nexport default Collapse;\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { cn } from "../lib/utils.js";
2
+ import Progress_default from "../Progress/index.js";
2
3
  import { LocaleContext, getTranslator } from "../locale/default.js";
3
4
  import ScrollArea_default from "../ScrollArea/index.js";
4
5
  /* empty css */
5
- import Progress_default from "../Progress/index.js";
6
6
  import CustomSpin from "../Spin/index.js";
7
7
  import Body_default from "./components/Body/index.js";
8
8
  import Error_default from "./components/Body/Error.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/DataPreviewTable/index.tsx"],"sourcesContent":["import { cn } from '../lib/utils';\nimport './data-preview-table.css';\nimport React, {\n forwardRef,\n memo,\n useContext,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react';\nimport Progress, { IProgressRef } from '../Progress';\nimport ScrollArea from '../ScrollArea';\nimport Spin from '../Spin';\nimport { LocaleContext, getTranslator } from '../locale/default';\nimport Body from './components/Body';\nimport Error from './components/Body/Error';\nimport Header from './components/Header';\nimport { CELL_HEIGHT, DEFAULT_HEADER_HEIGHT } from './constant';\nimport useDirection from './hooks/useDirection';\nimport { DataPreviewTableRef, ITableProps } from './interface';\n\nconst DataPreviewTable = forwardRef(function (\n props: ITableProps,\n ref: React.Ref<DataPreviewTableRef>,\n) {\n const { locale } = useContext(LocaleContext);\n const t = getTranslator(locale);\n\n const {\n columns = [],\n datasource = [],\n width,\n height,\n headerHeight = 0,\n loading,\n dataStatus,\n errorMsg = t.DataPreviewTable.dataLoadFailed,\n } = props;\n\n const realHeaderHeight = Math.max(headerHeight, DEFAULT_HEADER_HEIGHT);\n const contentHeight = useMemo(() => {\n return datasource.length * CELL_HEIGHT + realHeaderHeight;\n }, [datasource, realHeaderHeight]);\n const wrapRef = useRef<HTMLDivElement>(null);\n const progressRef = useRef<IProgressRef>(null);\n const {\n columnRange,\n rowRange,\n onScroll,\n columnLayout,\n setColumnsWidth,\n tableColumns,\n contentWidth,\n } = useDirection(columns, wrapRef);\n\n useImperativeHandle(ref, () => ({\n scrollToColumnInView: (columnId: string) => {\n // 实现scrollTo方法\n const columnPos = columnLayout[columnId];\n if (!columnPos) return;\n // 根据容器的宽度,容器的scrollLeft,columnPos,判断当前列是否在可视区域内\n const { scrollLeft } = wrapRef.current as HTMLDivElement;\n const { left, width } = columnPos;\n const containerWidth = wrapRef.current?.clientWidth || 0;\n if (left < scrollLeft) {\n // 左边不可见\n wrapRef.current?.scrollTo({\n left,\n behavior: 'smooth',\n });\n } else if (left + width > scrollLeft + containerWidth) {\n // 右边不可见\n wrapRef.current?.scrollTo({\n left: left + width - containerWidth,\n behavior: 'smooth',\n });\n }\n },\n progressRestart: () => {\n progressRef.current?.restart();\n },\n }));\n\n const skeletonRows = useMemo(() => {\n if (!wrapRef.current) return 0;\n const rows = Math.ceil(\n ((wrapRef.current?.clientHeight || 0) - realHeaderHeight) / CELL_HEIGHT,\n );\n return rows - 1 < 0 ? 0 : rows - 1;\n }, [realHeaderHeight]);\n\n return (\n <ScrollArea\n ref={wrapRef}\n style={{ width, height, willChange: 'transform' }}\n onViewportScroll={() => {\n // const { scrollTop, scrollLeft } = e.target as HTMLDivElement;\n onScroll();\n }}\n className={cn(props.className, 'ald-data-preview-table', {\n 'ald-data-preview-table-show-skeleton': dataStatus === 'pending',\n })}\n >\n <div\n style={{\n height:\n dataStatus === 'pending'\n ? skeletonRows * CELL_HEIGHT + realHeaderHeight\n : contentHeight,\n position: 'relative',\n width: 'fit-content',\n }}\n >\n <div className=\"ald-data-preview-sticky\">\n <Progress\n className=\"ald-data-preview-progress\"\n loading={!!loading}\n ref={progressRef}\n ></Progress>\n <Header\n contentWidth={contentWidth}\n headerHeight={realHeaderHeight}\n columns={tableColumns}\n columnLayout={columnLayout}\n setWidth={setColumnsWidth}\n columnRange={columnRange}\n />\n </div>\n {dataStatus === 'pending' && tableColumns.length > 0 && (\n <div className=\"ald-data-preview-loading\">\n <Spin />\n </div>\n )}\n {dataStatus === 'success' && (\n <Body\n columns={tableColumns}\n columnRange={columnRange}\n columnLayout={columnLayout}\n datasource={datasource}\n rowRange={rowRange}\n headerHeight={realHeaderHeight}\n />\n )}\n {dataStatus === 'error' && (\n <Error\n errorMsg={errorMsg}\n wrapWidth={wrapRef.current?.clientWidth || 0}\n />\n )}\n </div>\n </ScrollArea>\n );\n});\n\nexport default memo(DataPreviewTable);\n"],"mappings":";;;;;;;;;;;;AA0JA,IAAA,2BAAe,KArIU,WAAW,SAClC,OACA,KACA;CACA,MAAM,EAAE,WAAW,WAAW,cAAc;CAC5C,MAAM,IAAI,cAAc,OAAO;CAE/B,MAAM,EACJ,UAAU,EAAE,EACZ,aAAa,EAAE,EACf,OACA,QACA,eAAe,GACf,SACA,YACA,WAAW,EAAE,iBAAiB,mBAC5B;CAEJ,MAAM,mBAAmB,KAAK,IAAI,cAAA,GAAoC;CACtE,MAAM,gBAAgB,cAAc;AAClC,SAAO,WAAW,SAAA,KAAuB;IACxC,CAAC,YAAY,iBAAiB,CAAC;CAClC,MAAM,UAAU,OAAuB,KAAK;CAC5C,MAAM,cAAc,OAAqB,KAAK;CAC9C,MAAM,EACJ,aACA,UACA,UACA,cACA,iBACA,cACA,iBACE,aAAa,SAAS,QAAQ;AAElC,qBAAoB,YAAY;EAC9B,uBAAuB,aAAqB;GAE1C,MAAM,YAAY,aAAa;AAC/B,OAAI,CAAC,UAAW;GAEhB,MAAM,EAAE,eAAe,QAAQ;GAC/B,MAAM,EAAE,MAAM,UAAU;GACxB,MAAM,iBAAiB,QAAQ,SAAS,eAAe;AACvD,OAAI,OAAO,WAET,SAAQ,SAAS,SAAS;IACxB;IACA,UAAU;IACX,CAAC;YACO,OAAO,QAAQ,aAAa,eAErC,SAAQ,SAAS,SAAS;IACxB,MAAM,OAAO,QAAQ;IACrB,UAAU;IACX,CAAC;;EAGN,uBAAuB;AACrB,eAAY,SAAS,SAAS;;EAEjC,EAAE;CAEH,MAAM,eAAe,cAAc;AACjC,MAAI,CAAC,QAAQ,QAAS,QAAO;EAC7B,MAAM,OAAO,KAAK,OACd,QAAQ,SAAS,gBAAgB,KAAK,oBAAA,GACzC;AACD,SAAO,OAAO,IAAI,IAAI,IAAI,OAAO;IAChC,CAAC,iBAAiB,CAAC;AAEtB,QACE,oBAAC,oBAAD;EACE,KAAK;EACL,OAAO;GAAE;GAAO;GAAQ,YAAY;GAAa;EACjD,wBAAwB;AAEtB,aAAU;;EAEZ,WAAW,GAAG,MAAM,WAAW,0BAA0B,EACvD,wCAAwC,eAAe,WACxD,CAAC;YAEF,qBAAC,OAAD;GACE,OAAO;IACL,QACE,eAAe,YACX,eAAA,KAA6B,mBAC7B;IACN,UAAU;IACV,OAAO;IACR;aARH;IAUE,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,kBAAD;MACE,WAAU;MACV,SAAS,CAAC,CAAC;MACX,KAAK;MACK,CAAA,EACZ,oBAAC,QAAD;MACgB;MACd,cAAc;MACd,SAAS;MACK;MACd,UAAU;MACG;MACb,CAAA,CACE;;IACL,eAAe,aAAa,aAAa,SAAS,KACjD,oBAAC,OAAD;KAAK,WAAU;eACb,oBAAC,YAAD,EAAQ,CAAA;KACJ,CAAA;IAEP,eAAe,aACd,oBAAC,cAAD;KACE,SAAS;KACI;KACC;KACF;KACF;KACV,cAAc;KACd,CAAA;IAEH,eAAe,WACd,oBAAC,eAAD;KACY;KACV,WAAW,QAAQ,SAAS,eAAe;KAC3C,CAAA;IAEA;;EACK,CAAA;EAEf,CAEmC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/DataPreviewTable/index.tsx"],"sourcesContent":["import { cn } from '../lib/utils';\nimport './data-preview-table.css';\nimport React, {\n forwardRef,\n memo,\n useContext,\n useImperativeHandle,\n useMemo,\n useRef,\n} from 'react';\nimport Progress, { IProgressRef } from '../Progress';\nimport ScrollArea from '../ScrollArea';\nimport Spin from '../Spin';\nimport { LocaleContext, getTranslator } from '../locale/default';\nimport Body from './components/Body';\nimport Error from './components/Body/Error';\nimport Header from './components/Header';\nimport { CELL_HEIGHT, DEFAULT_HEADER_HEIGHT } from './constant';\nimport useDirection from './hooks/useDirection';\nimport { DataPreviewTableRef, ITableProps } from './interface';\n\nconst DataPreviewTable = forwardRef(function (\n props: ITableProps,\n ref: React.Ref<DataPreviewTableRef>,\n) {\n const { locale } = useContext(LocaleContext);\n const t = getTranslator(locale);\n\n const {\n columns = [],\n datasource = [],\n width,\n height,\n headerHeight = 0,\n loading,\n dataStatus,\n errorMsg = t.DataPreviewTable.dataLoadFailed,\n } = props;\n\n const realHeaderHeight = Math.max(headerHeight, DEFAULT_HEADER_HEIGHT);\n const contentHeight = useMemo(() => {\n return datasource.length * CELL_HEIGHT + realHeaderHeight;\n }, [datasource, realHeaderHeight]);\n const wrapRef = useRef<HTMLDivElement>(null);\n const progressRef = useRef<IProgressRef>(null);\n const {\n columnRange,\n rowRange,\n onScroll,\n columnLayout,\n setColumnsWidth,\n tableColumns,\n contentWidth,\n } = useDirection(columns, wrapRef);\n\n useImperativeHandle(ref, () => ({\n scrollToColumnInView: (columnId: string) => {\n // 实现scrollTo方法\n const columnPos = columnLayout[columnId];\n if (!columnPos) return;\n // 根据容器的宽度,容器的scrollLeft,columnPos,判断当前列是否在可视区域内\n const { scrollLeft } = wrapRef.current as HTMLDivElement;\n const { left, width } = columnPos;\n const containerWidth = wrapRef.current?.clientWidth || 0;\n if (left < scrollLeft) {\n // 左边不可见\n wrapRef.current?.scrollTo({\n left,\n behavior: 'smooth',\n });\n } else if (left + width > scrollLeft + containerWidth) {\n // 右边不可见\n wrapRef.current?.scrollTo({\n left: left + width - containerWidth,\n behavior: 'smooth',\n });\n }\n },\n progressRestart: () => {\n progressRef.current?.restart();\n },\n }));\n\n const skeletonRows = useMemo(() => {\n if (!wrapRef.current) return 0;\n const rows = Math.ceil(\n ((wrapRef.current?.clientHeight || 0) - realHeaderHeight) / CELL_HEIGHT,\n );\n return rows - 1 < 0 ? 0 : rows - 1;\n }, [realHeaderHeight]);\n\n return (\n <ScrollArea\n ref={wrapRef}\n style={{ width, height, willChange: 'transform' }}\n onViewportScroll={() => {\n // const { scrollTop, scrollLeft } = e.target as HTMLDivElement;\n onScroll();\n }}\n className={cn(props.className, 'ald-data-preview-table', {\n 'ald-data-preview-table-show-skeleton': dataStatus === 'pending',\n })}\n >\n <div\n style={{\n height:\n dataStatus === 'pending'\n ? skeletonRows * CELL_HEIGHT + realHeaderHeight\n : contentHeight,\n position: 'relative',\n width: 'fit-content',\n }}\n >\n <div className=\"ald-data-preview-sticky\">\n <Progress\n className=\"ald-data-preview-progress\"\n loading={!!loading}\n ref={progressRef}\n />\n <Header\n contentWidth={contentWidth}\n headerHeight={realHeaderHeight}\n columns={tableColumns}\n columnLayout={columnLayout}\n setWidth={setColumnsWidth}\n columnRange={columnRange}\n />\n </div>\n {dataStatus === 'pending' && tableColumns.length > 0 && (\n <div className=\"ald-data-preview-loading\">\n <Spin />\n </div>\n )}\n {dataStatus === 'success' && (\n <Body\n columns={tableColumns}\n columnRange={columnRange}\n columnLayout={columnLayout}\n datasource={datasource}\n rowRange={rowRange}\n headerHeight={realHeaderHeight}\n />\n )}\n {dataStatus === 'error' && (\n <Error\n errorMsg={errorMsg}\n wrapWidth={wrapRef.current?.clientWidth || 0}\n />\n )}\n </div>\n </ScrollArea>\n );\n});\n\nexport default memo(DataPreviewTable);\n"],"mappings":";;;;;;;;;;;;AA0JA,IAAA,2BAAe,KArIU,WAAW,SAClC,OACA,KACA;CACA,MAAM,EAAE,WAAW,WAAW,cAAc;CAC5C,MAAM,IAAI,cAAc,OAAO;CAE/B,MAAM,EACJ,UAAU,EAAE,EACZ,aAAa,EAAE,EACf,OACA,QACA,eAAe,GACf,SACA,YACA,WAAW,EAAE,iBAAiB,mBAC5B;CAEJ,MAAM,mBAAmB,KAAK,IAAI,cAAA,GAAoC;CACtE,MAAM,gBAAgB,cAAc;AAClC,SAAO,WAAW,SAAA,KAAuB;IACxC,CAAC,YAAY,iBAAiB,CAAC;CAClC,MAAM,UAAU,OAAuB,KAAK;CAC5C,MAAM,cAAc,OAAqB,KAAK;CAC9C,MAAM,EACJ,aACA,UACA,UACA,cACA,iBACA,cACA,iBACE,aAAa,SAAS,QAAQ;AAElC,qBAAoB,YAAY;EAC9B,uBAAuB,aAAqB;GAE1C,MAAM,YAAY,aAAa;AAC/B,OAAI,CAAC,UAAW;GAEhB,MAAM,EAAE,eAAe,QAAQ;GAC/B,MAAM,EAAE,MAAM,UAAU;GACxB,MAAM,iBAAiB,QAAQ,SAAS,eAAe;AACvD,OAAI,OAAO,WAET,SAAQ,SAAS,SAAS;IACxB;IACA,UAAU;IACX,CAAC;YACO,OAAO,QAAQ,aAAa,eAErC,SAAQ,SAAS,SAAS;IACxB,MAAM,OAAO,QAAQ;IACrB,UAAU;IACX,CAAC;;EAGN,uBAAuB;AACrB,eAAY,SAAS,SAAS;;EAEjC,EAAE;CAEH,MAAM,eAAe,cAAc;AACjC,MAAI,CAAC,QAAQ,QAAS,QAAO;EAC7B,MAAM,OAAO,KAAK,OACd,QAAQ,SAAS,gBAAgB,KAAK,oBAAA,GACzC;AACD,SAAO,OAAO,IAAI,IAAI,IAAI,OAAO;IAChC,CAAC,iBAAiB,CAAC;AAEtB,QACE,oBAAC,oBAAD;EACE,KAAK;EACL,OAAO;GAAE;GAAO;GAAQ,YAAY;GAAa;EACjD,wBAAwB;AAEtB,aAAU;;EAEZ,WAAW,GAAG,MAAM,WAAW,0BAA0B,EACvD,wCAAwC,eAAe,WACxD,CAAC;YAEF,qBAAC,OAAD;GACE,OAAO;IACL,QACE,eAAe,YACX,eAAA,KAA6B,mBAC7B;IACN,UAAU;IACV,OAAO;IACR;aARH;IAUE,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,kBAAD;MACE,WAAU;MACV,SAAS,CAAC,CAAC;MACX,KAAK;MACL,CAAA,EACF,oBAAC,QAAD;MACgB;MACd,cAAc;MACd,SAAS;MACK;MACd,UAAU;MACG;MACb,CAAA,CACE;;IACL,eAAe,aAAa,aAAa,SAAS,KACjD,oBAAC,OAAD;KAAK,WAAU;eACb,oBAAC,YAAD,EAAQ,CAAA;KACJ,CAAA;IAEP,eAAe,aACd,oBAAC,cAAD;KACE,SAAS;KACI;KACC;KACF;KACF;KACV,cAAc;KACd,CAAA;IAEH,eAAe,WACd,oBAAC,eAAD;KACY;KACV,WAAW,QAAQ,SAAS,eAAe;KAC3C,CAAA;IAEA;;EACK,CAAA;EAEf,CAEmC"}
@@ -3,6 +3,7 @@ export interface DrawerProps {
3
3
  open?: boolean;
4
4
  onClose?: (e?: React.MouseEvent | React.KeyboardEvent) => void;
5
5
  title?: React.ReactNode;
6
+ description?: React.ReactNode;
6
7
  placement?: 'top' | 'right' | 'bottom' | 'left';
7
8
  width?: number | string;
8
9
  height?: number | string;
@@ -1,7 +1,7 @@
1
1
  import { cn } from "../lib/utils.js";
2
2
  import Memo from "../Icon/components/CloseLightLine.js";
3
3
  import { useEffect, useRef } from "react";
4
- import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
5
  import * as DialogPrimitive from "@radix-ui/react-dialog";
6
6
  import { hideOthers } from "aria-hidden";
7
7
  //#region src/Drawer/index.tsx
@@ -16,13 +16,13 @@ var placementStyles = {
16
16
  bottom: "tw-inset-x-0 tw-bottom-0 tw-w-full"
17
17
  };
18
18
  var slideAnimationStyles = {
19
- right: "tw-animate-in tw-slide-in-from-right tw-duration-300",
20
- left: "tw-animate-in tw-slide-in-from-left tw-duration-300",
21
- top: "tw-animate-in tw-slide-in-from-top tw-duration-300",
22
- bottom: "tw-animate-in tw-slide-in-from-bottom tw-duration-300"
19
+ right: "data-[state=open]:tw-animate-drawer-in-right data-[state=closed]:tw-animate-drawer-out-right",
20
+ left: "data-[state=open]:tw-animate-drawer-in-left data-[state=closed]:tw-animate-drawer-out-left",
21
+ top: "data-[state=open]:tw-animate-drawer-in-top data-[state=closed]:tw-animate-drawer-out-top",
22
+ bottom: "data-[state=open]:tw-animate-drawer-in-bottom data-[state=closed]:tw-animate-drawer-out-bottom"
23
23
  };
24
24
  function Drawer(props) {
25
- const { open = false, onClose, title, placement = "right", width, height, size = "default", closable = true, mask = true, maskClosable = true, className, style, contentWrapperStyle, bodyStyle, headerStyle, footer, footerStyle, extra, children, zIndex = 1e3 } = props;
25
+ const { open = false, onClose, title, description, placement = "right", width, height, size = "default", closable = true, mask = true, maskClosable = true, className, style, contentWrapperStyle, bodyStyle, headerStyle, footer, footerStyle, extra, children, zIndex = 1e3 } = props;
26
26
  const contentRef = useRef(null);
27
27
  useEffect(() => {
28
28
  if (!open) return;
@@ -74,7 +74,8 @@ function Drawer(props) {
74
74
  if (!val) onClose?.();
75
75
  },
76
76
  children: /* @__PURE__ */ jsxs(DialogPrimitive.Portal, { children: [mask && /* @__PURE__ */ jsx("div", {
77
- className: "ald-drawer-mask tw-animate-in tw-fade-in-0 tw-fixed tw-inset-0 tw-bg-black/45",
77
+ "data-state": open ? "open" : "closed",
78
+ className: "ald-drawer-mask tw-fixed tw-inset-0 tw-bg-black/45 data-[state=closed]:tw-animate-mask-out data-[state=open]:tw-animate-mask-in",
78
79
  style: { zIndex },
79
80
  onClick: maskClosable ? () => onClose?.() : void 0
80
81
  }), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
@@ -87,40 +88,51 @@ function Drawer(props) {
87
88
  ...style
88
89
  },
89
90
  onEscapeKeyDown: () => onClose?.(),
90
- onInteractOutside: maskClosable ? () => onClose?.() : void 0,
91
+ onInteractOutside: (event) => {
92
+ if (!maskClosable) event.preventDefault();
93
+ },
91
94
  children: [
92
- (title || closable || extra) && /* @__PURE__ */ jsxs("div", {
93
- className: "ald-drawer-header ant-drawer-header tw-flex tw-items-center tw-justify-between tw-border-b tw-border-solid tw-border-[var(--border-default)] tw-px-6 tw-py-4",
95
+ (title || description || closable || extra) && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", {
96
+ className: "ald-drawer-header ant-drawer-header tw-flex tw-flex-col tw-items-start tw-gap-[var(--component-gap-lg)] tw-self-stretch tw-bg-[var(--interaction-background-sidepanel)] tw-px-6 tw-pb-0 tw-pt-4",
94
97
  style: headerStyle,
95
- children: [/* @__PURE__ */ jsx(DialogPrimitive.Title, {
96
- className: "ald-drawer-title ant-drawer-header-title tw-m-0 tw-text-base tw-font-semibold tw-text-[var(--content-primary)]",
97
- children: title
98
- }), /* @__PURE__ */ jsxs("div", {
99
- className: "tw-flex tw-items-center tw-gap-2",
100
- children: [extra, closable && /* @__PURE__ */ jsx(DialogPrimitive.Close, {
101
- asChild: true,
102
- children: /* @__PURE__ */ jsx("button", {
103
- type: "button",
104
- className: "ald-drawer-close tw-flex tw-size-8 tw-cursor-pointer tw-items-center tw-justify-center tw-rounded-r-50 tw-border-0 tw-bg-transparent hover:tw-bg-[var(--action-ghost-hover)]",
105
- onClick: () => onClose?.(),
106
- children: /* @__PURE__ */ jsx(Memo, {
107
- size: 20,
108
- color: "var(--content-secondary)"
109
- })
110
- })
111
- })]
112
- })]
113
- }),
98
+ children: /* @__PURE__ */ jsxs("div", {
99
+ className: "tw-flex tw-w-full tw-flex-col tw-gap-[6px]",
100
+ children: [/* @__PURE__ */ jsxs("div", {
101
+ className: "tw-flex tw-w-full tw-items-start tw-justify-between tw-gap-[var(--component-gap-sm)]",
102
+ children: [/* @__PURE__ */ jsx(DialogPrimitive.Title, {
103
+ className: "ald-drawer-title ant-drawer-header-title tw-[font-feature-settings:\"liga\"_off,\"clig\"_off] tw-m-0 tw-flex-[1_0_0] tw-text-lg tw-font-semibold tw-leading-7 tw-text-[var(--content-primary)]",
104
+ children: title
105
+ }), extra || closable ? /* @__PURE__ */ jsxs("div", {
106
+ className: "ald-drawer-actions tw-flex tw-items-center tw-gap-[var(--component-gap-sm)]",
107
+ children: [extra, closable && /* @__PURE__ */ jsx(DialogPrimitive.Close, {
108
+ asChild: true,
109
+ children: /* @__PURE__ */ jsx("button", {
110
+ type: "button",
111
+ className: "ald-drawer-close tw-flex tw-size-8 tw-cursor-pointer tw-items-center tw-justify-center tw-rounded-r-50 tw-border-0 tw-bg-transparent hover:tw-bg-[var(--action-ghost-hover)]",
112
+ onClick: () => onClose?.(),
113
+ children: /* @__PURE__ */ jsx(Memo, {
114
+ size: 20,
115
+ color: "var(--content-secondary)"
116
+ })
117
+ })
118
+ })]
119
+ }) : null]
120
+ }), description ? /* @__PURE__ */ jsx("div", {
121
+ className: "ald-drawer-description tw-[font-feature-settings:\"liga\"_off,\"clig\"_off] tw-self-stretch tw-text-sm tw-font-normal tw-leading-5 tw-text-[var(--content-secondary)]",
122
+ children: description
123
+ }) : null]
124
+ })
125
+ }) }),
114
126
  /* @__PURE__ */ jsx("div", {
115
- className: "ald-drawer-body ant-drawer-body tw-flex-1 tw-overflow-auto tw-px-6 tw-py-4",
127
+ className: "ald-drawer-body ant-drawer-body tw-flex tw-flex-[1_0_0] tw-flex-col tw-items-start tw-self-stretch tw-overflow-auto tw-p-[var(--component-padding-2xl)]",
116
128
  style: bodyStyle,
117
129
  children
118
130
  }),
119
- footer && /* @__PURE__ */ jsx("div", {
120
- className: "ald-drawer-footer ant-drawer-footer tw-border-t tw-border-solid tw-border-[var(--border-default)] tw-px-6 tw-py-4 tw-text-right",
131
+ footer && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", { className: "ald-drawer-footer-divider tw-h-px tw-bg-[var(--border-default)]" }), /* @__PURE__ */ jsx("div", {
132
+ className: "ald-drawer-footer ant-drawer-footer tw-flex tw-self-stretch tw-items-center tw-justify-end tw-gap-[var(--component-gap-lg)] tw-px-[var(--component-padding-2xl)] tw-py-[var(--component-padding-lg)]",
121
133
  style: footerStyle,
122
134
  children: footer
123
- })
135
+ })] })
124
136
  ]
125
137
  })] })
126
138
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/Drawer/index.tsx"],"sourcesContent":["import * as DialogPrimitive from '@radix-ui/react-dialog';\nimport { hideOthers } from 'aria-hidden';\nimport React, { useEffect, useRef } from 'react';\nimport { CloseLightLine } from '../Icon';\nimport { cn } from '../lib/utils';\n\nexport interface DrawerProps {\n open?: boolean;\n onClose?: (e?: React.MouseEvent | React.KeyboardEvent) => void;\n title?: React.ReactNode;\n placement?: 'top' | 'right' | 'bottom' | 'left';\n width?: number | string;\n height?: number | string;\n size?: 'default' | 'large';\n closable?: boolean;\n mask?: boolean;\n maskClosable?: boolean;\n destroyOnClose?: boolean;\n className?: string;\n style?: React.CSSProperties;\n contentWrapperStyle?: React.CSSProperties;\n bodyStyle?: React.CSSProperties;\n headerStyle?: React.CSSProperties;\n maskStyle?: React.CSSProperties;\n footer?: React.ReactNode;\n footerStyle?: React.CSSProperties;\n extra?: React.ReactNode;\n children?: React.ReactNode;\n zIndex?: number;\n keyboard?: boolean;\n getContainer?: () => HTMLElement;\n}\n\nconst sizePresets: Record<string, number> = {\n default: 378,\n large: 736,\n};\n\nconst placementStyles: Record<string, string> = {\n right: 'tw-inset-y-0 tw-right-0 tw-h-full',\n left: 'tw-inset-y-0 tw-left-0 tw-h-full',\n top: 'tw-inset-x-0 tw-top-0 tw-w-full',\n bottom: 'tw-inset-x-0 tw-bottom-0 tw-w-full',\n};\n\nconst slideAnimationStyles: Record<string, string> = {\n right: 'tw-animate-in tw-slide-in-from-right tw-duration-300',\n left: 'tw-animate-in tw-slide-in-from-left tw-duration-300',\n top: 'tw-animate-in tw-slide-in-from-top tw-duration-300',\n bottom: 'tw-animate-in tw-slide-in-from-bottom tw-duration-300',\n};\n\nfunction Drawer(props: DrawerProps) {\n const {\n open = false,\n onClose,\n title,\n placement = 'right',\n width,\n height,\n size = 'default',\n closable = true,\n mask = true,\n maskClosable = true,\n className,\n style,\n contentWrapperStyle,\n bodyStyle,\n headerStyle,\n footer,\n footerStyle,\n extra,\n children,\n zIndex = 1000,\n } = props;\n\n const contentRef = useRef<HTMLDivElement>(null);\n\n // ---- modal={false} 补偿:锁定背景滚动 ----\n useEffect(() => {\n if (!open) return;\n const prev = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n return () => {\n document.body.style.overflow = prev;\n };\n }, [open]);\n\n // ---- modal={false} 补偿:aria-hidden(屏幕阅读器只感知抽屉) ----\n useEffect(() => {\n if (!open || !contentRef.current) return;\n return hideOthers(contentRef.current);\n }, [open]);\n\n // ---- modal={false} 补偿:Tab 焦点循环(不使用 MutationObserver,避免 FocusScope 劫持焦点)----\n useEffect(() => {\n if (!open || !contentRef.current) return;\n const container = contentRef.current;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return;\n const focusable = container.querySelectorAll<HTMLElement>(\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"]):not([disabled])',\n );\n if (focusable.length === 0) return;\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (e.shiftKey) {\n if (\n document.activeElement === first ||\n !container.contains(document.activeElement)\n ) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (\n document.activeElement === last ||\n !container.contains(document.activeElement)\n ) {\n e.preventDefault();\n first.focus();\n }\n }\n };\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [open]);\n\n const isHorizontal = placement === 'left' || placement === 'right';\n const preset = sizePresets[size] || sizePresets.default;\n const effectiveWidth = width ?? preset;\n const effectiveHeight = height ?? preset;\n const sizeStyle = isHorizontal\n ? { width: effectiveWidth, maxWidth: '100vw' }\n : { height: effectiveHeight, maxHeight: '100vh' };\n\n // modal={false}:禁用 Radix FocusScope 的 MutationObserver,\n // 避免表单校验触发 DOM 变动时劫持焦点。手动补偿滚动锁定、aria-hidden、Tab 循环。\n return (\n <DialogPrimitive.Root\n open={open}\n modal={false}\n onOpenChange={(val) => {\n if (!val) onClose?.();\n }}\n >\n <DialogPrimitive.Portal>\n {/* modal={false} 时 DialogPrimitive.Overlay 不渲染,用普通 div 替代 */}\n {mask && (\n <div\n className=\"ald-drawer-mask tw-animate-in tw-fade-in-0 tw-fixed tw-inset-0 tw-bg-black/45\"\n style={{ zIndex }}\n onClick={maskClosable ? () => onClose?.() : undefined}\n />\n )}\n <DialogPrimitive.Content\n ref={contentRef}\n className={cn(\n 'ald-drawer tw-fixed tw-flex tw-flex-col tw-bg-[var(--background-default)] tw-shadow-xl',\n placementStyles[placement],\n slideAnimationStyles[placement],\n className,\n )}\n style={{\n zIndex: zIndex + 1,\n ...sizeStyle,\n ...contentWrapperStyle,\n ...style,\n }}\n onEscapeKeyDown={() => onClose?.()}\n onInteractOutside={maskClosable ? () => onClose?.() : undefined}\n >\n {(title || closable || extra) && (\n // antd 兼容:保留 ant-drawer-header / ant-drawer-header-title class,消费方 CSS 可能依赖这些选择器\n <div\n className=\"ald-drawer-header ant-drawer-header tw-flex tw-items-center tw-justify-between tw-border-b tw-border-solid tw-border-[var(--border-default)] tw-px-6 tw-py-4\"\n style={headerStyle}\n >\n <DialogPrimitive.Title className=\"ald-drawer-title ant-drawer-header-title tw-m-0 tw-text-base tw-font-semibold tw-text-[var(--content-primary)]\">\n {title}\n </DialogPrimitive.Title>\n <div className=\"tw-flex tw-items-center tw-gap-2\">\n {extra}\n {closable && (\n <DialogPrimitive.Close asChild>\n <button\n type=\"button\"\n className=\"ald-drawer-close tw-flex tw-size-8 tw-cursor-pointer tw-items-center tw-justify-center tw-rounded-r-50 tw-border-0 tw-bg-transparent hover:tw-bg-[var(--action-ghost-hover)]\"\n onClick={() => onClose?.()}\n >\n <CloseLightLine\n size={20}\n color=\"var(--content-secondary)\"\n />\n </button>\n </DialogPrimitive.Close>\n )}\n </div>\n </div>\n )}\n {/* antd 兼容:保留 ant-drawer-body class */}\n <div\n className=\"ald-drawer-body ant-drawer-body tw-flex-1 tw-overflow-auto tw-px-6 tw-py-4\"\n style={bodyStyle}\n >\n {children}\n </div>\n {footer && (\n // antd 兼容:antd .ant-drawer-footer 使用 text-align:right 让 inline 按钮右对齐,\n // 此处加 tw-text-right 保持消费方传入 width:100% 子容器时 inline 元素仍右对齐。\n <div\n className=\"ald-drawer-footer ant-drawer-footer tw-border-t tw-border-solid tw-border-[var(--border-default)] tw-px-6 tw-py-4 tw-text-right\"\n style={footerStyle}\n >\n {footer}\n </div>\n )}\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n );\n}\n\nexport default Drawer;\n"],"mappings":";;;;;;;AAiCA,IAAM,cAAsC;CAC1C,SAAS;CACT,OAAO;CACR;AAED,IAAM,kBAA0C;CAC9C,OAAO;CACP,MAAM;CACN,KAAK;CACL,QAAQ;CACT;AAED,IAAM,uBAA+C;CACnD,OAAO;CACP,MAAM;CACN,KAAK;CACL,QAAQ;CACT;AAED,SAAS,OAAO,OAAoB;CAClC,MAAM,EACJ,OAAO,OACP,SACA,OACA,YAAY,SACZ,OACA,QACA,OAAO,WACP,WAAW,MACX,OAAO,MACP,eAAe,MACf,WACA,OACA,qBACA,WACA,aACA,QACA,aACA,OACA,UACA,SAAS,QACP;CAEJ,MAAM,aAAa,OAAuB,KAAK;AAG/C,iBAAgB;AACd,MAAI,CAAC,KAAM;EACX,MAAM,OAAO,SAAS,KAAK,MAAM;AACjC,WAAS,KAAK,MAAM,WAAW;AAC/B,eAAa;AACX,YAAS,KAAK,MAAM,WAAW;;IAEhC,CAAC,KAAK,CAAC;AAGV,iBAAgB;AACd,MAAI,CAAC,QAAQ,CAAC,WAAW,QAAS;AAClC,SAAO,WAAW,WAAW,QAAQ;IACpC,CAAC,KAAK,CAAC;AAGV,iBAAgB;AACd,MAAI,CAAC,QAAQ,CAAC,WAAW,QAAS;EAClC,MAAM,YAAY,WAAW;EAC7B,MAAM,iBAAiB,MAAqB;AAC1C,OAAI,EAAE,QAAQ,MAAO;GACrB,MAAM,YAAY,UAAU,iBAC1B,8JACD;AACD,OAAI,UAAU,WAAW,EAAG;GAC5B,MAAM,QAAQ,UAAU;GACxB,MAAM,OAAO,UAAU,UAAU,SAAS;AAC1C,OAAI,EAAE,UACJ;QACE,SAAS,kBAAkB,SAC3B,CAAC,UAAU,SAAS,SAAS,cAAc,EAC3C;AACA,OAAE,gBAAgB;AAClB,UAAK,OAAO;;cAIZ,SAAS,kBAAkB,QAC3B,CAAC,UAAU,SAAS,SAAS,cAAc,EAC3C;AACA,MAAE,gBAAgB;AAClB,UAAM,OAAO;;;AAInB,WAAS,iBAAiB,WAAW,cAAc;AACnD,eAAa,SAAS,oBAAoB,WAAW,cAAc;IAClE,CAAC,KAAK,CAAC;CAEV,MAAM,eAAe,cAAc,UAAU,cAAc;CAC3D,MAAM,SAAS,YAAY,SAAS,YAAY;CAGhD,MAAM,YAAY,eACd;EAAE,OAHiB,SAAS;EAGH,UAAU;EAAS,GAC5C;EAAE,QAHkB,UAAU;EAGH,WAAW;EAAS;AAInD,QACE,oBAAC,gBAAgB,MAAjB;EACQ;EACN,OAAO;EACP,eAAe,QAAQ;AACrB,OAAI,CAAC,IAAK,YAAW;;YAGvB,qBAAC,gBAAgB,QAAjB,EAAA,UAAA,CAEG,QACC,oBAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,QAAQ;GACjB,SAAS,qBAAqB,WAAW,GAAG;GAC5C,CAAA,EAEJ,qBAAC,gBAAgB,SAAjB;GACE,KAAK;GACL,WAAW,GACT,0FACA,gBAAgB,YAChB,qBAAqB,YACrB,UACD;GACD,OAAO;IACL,QAAQ,SAAS;IACjB,GAAG;IACH,GAAG;IACH,GAAG;IACJ;GACD,uBAAuB,WAAW;GAClC,mBAAmB,qBAAqB,WAAW,GAAG;aAfxD;KAiBI,SAAS,YAAY,UAErB,qBAAC,OAAD;KACE,WAAU;KACV,OAAO;eAFT,CAIE,oBAAC,gBAAgB,OAAjB;MAAuB,WAAU;gBAC9B;MACqB,CAAA,EACxB,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,OACA,YACC,oBAAC,gBAAgB,OAAjB;OAAuB,SAAA;iBACrB,oBAAC,UAAD;QACE,MAAK;QACL,WAAU;QACV,eAAe,WAAW;kBAE1B,oBAAC,MAAD;SACE,MAAM;SACN,OAAM;SACN,CAAA;QACK,CAAA;OACa,CAAA,CAEtB;QACF;;IAGR,oBAAC,OAAD;KACE,WAAU;KACV,OAAO;KAEN;KACG,CAAA;IACL,UAGC,oBAAC,OAAD;KACE,WAAU;KACV,OAAO;eAEN;KACG,CAAA;IAEgB;KACH,EAAA,CAAA;EACJ,CAAA"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/Drawer/index.tsx"],"sourcesContent":["import * as DialogPrimitive from '@radix-ui/react-dialog';\nimport { hideOthers } from 'aria-hidden';\nimport React, { useEffect, useRef } from 'react';\nimport { CloseLightLine } from '../Icon';\nimport { cn } from '../lib/utils';\n\nexport interface DrawerProps {\n open?: boolean;\n onClose?: (e?: React.MouseEvent | React.KeyboardEvent) => void;\n title?: React.ReactNode;\n description?: React.ReactNode;\n placement?: 'top' | 'right' | 'bottom' | 'left';\n width?: number | string;\n height?: number | string;\n size?: 'default' | 'large';\n closable?: boolean;\n mask?: boolean;\n maskClosable?: boolean;\n destroyOnClose?: boolean;\n className?: string;\n style?: React.CSSProperties;\n contentWrapperStyle?: React.CSSProperties;\n bodyStyle?: React.CSSProperties;\n headerStyle?: React.CSSProperties;\n maskStyle?: React.CSSProperties;\n footer?: React.ReactNode;\n footerStyle?: React.CSSProperties;\n extra?: React.ReactNode;\n children?: React.ReactNode;\n zIndex?: number;\n keyboard?: boolean;\n getContainer?: () => HTMLElement;\n}\n\nconst sizePresets: Record<string, number> = {\n default: 378,\n large: 736,\n};\n\nconst placementStyles: Record<string, string> = {\n right: 'tw-inset-y-0 tw-right-0 tw-h-full',\n left: 'tw-inset-y-0 tw-left-0 tw-h-full',\n top: 'tw-inset-x-0 tw-top-0 tw-w-full',\n bottom: 'tw-inset-x-0 tw-bottom-0 tw-w-full',\n};\n\nconst slideAnimationStyles: Record<string, string> = {\n right:\n 'data-[state=open]:tw-animate-drawer-in-right data-[state=closed]:tw-animate-drawer-out-right',\n left: 'data-[state=open]:tw-animate-drawer-in-left data-[state=closed]:tw-animate-drawer-out-left',\n top: 'data-[state=open]:tw-animate-drawer-in-top data-[state=closed]:tw-animate-drawer-out-top',\n bottom:\n 'data-[state=open]:tw-animate-drawer-in-bottom data-[state=closed]:tw-animate-drawer-out-bottom',\n};\n\nfunction Drawer(props: DrawerProps) {\n const {\n open = false,\n onClose,\n title,\n description,\n placement = 'right',\n width,\n height,\n size = 'default',\n closable = true,\n mask = true,\n maskClosable = true,\n className,\n style,\n contentWrapperStyle,\n bodyStyle,\n headerStyle,\n footer,\n footerStyle,\n extra,\n children,\n zIndex = 1000,\n } = props;\n\n const contentRef = useRef<HTMLDivElement>(null);\n\n // ---- modal={false} 补偿:锁定背景滚动 ----\n useEffect(() => {\n if (!open) return;\n const prev = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n return () => {\n document.body.style.overflow = prev;\n };\n }, [open]);\n\n // ---- modal={false} 补偿:aria-hidden(屏幕阅读器只感知抽屉) ----\n useEffect(() => {\n if (!open || !contentRef.current) return;\n return hideOthers(contentRef.current);\n }, [open]);\n\n // ---- modal={false} 补偿:Tab 焦点循环(不使用 MutationObserver,避免 FocusScope 劫持焦点)----\n useEffect(() => {\n if (!open || !contentRef.current) return;\n const container = contentRef.current;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return;\n const focusable = container.querySelectorAll<HTMLElement>(\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"]):not([disabled])',\n );\n if (focusable.length === 0) return;\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (e.shiftKey) {\n if (\n document.activeElement === first ||\n !container.contains(document.activeElement)\n ) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (\n document.activeElement === last ||\n !container.contains(document.activeElement)\n ) {\n e.preventDefault();\n first.focus();\n }\n }\n };\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [open]);\n\n const isHorizontal = placement === 'left' || placement === 'right';\n const preset = sizePresets[size] || sizePresets.default;\n const effectiveWidth = width ?? preset;\n const effectiveHeight = height ?? preset;\n const sizeStyle = isHorizontal\n ? { width: effectiveWidth, maxWidth: '100vw' }\n : { height: effectiveHeight, maxHeight: '100vh' };\n\n // modal={false}:禁用 Radix FocusScope 的 MutationObserver,\n // 避免表单校验触发 DOM 变动时劫持焦点。手动补偿滚动锁定、aria-hidden、Tab 循环。\n return (\n <DialogPrimitive.Root\n open={open}\n modal={false}\n onOpenChange={(val) => {\n if (!val) onClose?.();\n }}\n >\n <DialogPrimitive.Portal>\n {/* modal={false} 时 DialogPrimitive.Overlay 不渲染,用普通 div 替代 */}\n {mask && (\n <div\n data-state={open ? 'open' : 'closed'}\n className=\"ald-drawer-mask tw-fixed tw-inset-0 tw-bg-black/45 data-[state=closed]:tw-animate-mask-out data-[state=open]:tw-animate-mask-in\"\n style={{ zIndex }}\n onClick={maskClosable ? () => onClose?.() : undefined}\n />\n )}\n <DialogPrimitive.Content\n ref={contentRef}\n className={cn(\n 'ald-drawer tw-fixed tw-flex tw-flex-col tw-bg-[var(--background-default)] tw-shadow-xl',\n placementStyles[placement],\n slideAnimationStyles[placement],\n className,\n )}\n style={{\n zIndex: zIndex + 1,\n ...sizeStyle,\n ...contentWrapperStyle,\n ...style,\n }}\n onEscapeKeyDown={() => onClose?.()}\n onInteractOutside={(event) => {\n if (!maskClosable) {\n event.preventDefault();\n }\n }}\n >\n {(title || description || closable || extra) && (\n <>\n {/* antd 兼容:保留 ant-drawer-header / ant-drawer-header-title class,消费方 CSS 可能依赖这些选择器 */}\n <div\n className=\"ald-drawer-header ant-drawer-header tw-flex tw-flex-col tw-items-start tw-gap-[var(--component-gap-lg)] tw-self-stretch tw-bg-[var(--interaction-background-sidepanel)] tw-px-6 tw-pb-0 tw-pt-4\"\n style={headerStyle}\n >\n <div className=\"tw-flex tw-w-full tw-flex-col tw-gap-[6px]\">\n <div className=\"tw-flex tw-w-full tw-items-start tw-justify-between tw-gap-[var(--component-gap-sm)]\">\n <DialogPrimitive.Title className='ald-drawer-title ant-drawer-header-title tw-[font-feature-settings:\"liga\"_off,\"clig\"_off] tw-m-0 tw-flex-[1_0_0] tw-text-lg tw-font-semibold tw-leading-7 tw-text-[var(--content-primary)]'>\n {title}\n </DialogPrimitive.Title>\n {extra || closable ? (\n <div className=\"ald-drawer-actions tw-flex tw-items-center tw-gap-[var(--component-gap-sm)]\">\n {extra}\n {closable && (\n <DialogPrimitive.Close asChild>\n <button\n type=\"button\"\n className=\"ald-drawer-close tw-flex tw-size-8 tw-cursor-pointer tw-items-center tw-justify-center tw-rounded-r-50 tw-border-0 tw-bg-transparent hover:tw-bg-[var(--action-ghost-hover)]\"\n onClick={() => onClose?.()}\n >\n <CloseLightLine\n size={20}\n color=\"var(--content-secondary)\"\n />\n </button>\n </DialogPrimitive.Close>\n )}\n </div>\n ) : null}\n </div>\n {description ? (\n <div className='ald-drawer-description tw-[font-feature-settings:\"liga\"_off,\"clig\"_off] tw-self-stretch tw-text-sm tw-font-normal tw-leading-5 tw-text-[var(--content-secondary)]'>\n {description}\n </div>\n ) : null}\n </div>\n </div>\n </>\n )}\n {/* antd 兼容:保留 ant-drawer-body class */}\n <div\n className=\"ald-drawer-body ant-drawer-body tw-flex tw-flex-[1_0_0] tw-flex-col tw-items-start tw-self-stretch tw-overflow-auto tw-p-[var(--component-padding-2xl)]\"\n style={bodyStyle}\n >\n {children}\n </div>\n {footer && (\n <>\n <div className=\"ald-drawer-footer-divider tw-h-px tw-bg-[var(--border-default)]\" />\n {/* antd 兼容:antd .ant-drawer-footer 使用 text-align:right 让 inline 按钮右对齐,\n 此处加 tw-text-right 保持消费方传入 width:100% 子容器时 inline 元素仍右对齐。 */}\n <div\n className=\"ald-drawer-footer ant-drawer-footer tw-flex tw-self-stretch tw-items-center tw-justify-end tw-gap-[var(--component-gap-lg)] tw-px-[var(--component-padding-2xl)] tw-py-[var(--component-padding-lg)]\"\n style={footerStyle}\n >\n {footer}\n </div>\n </>\n )}\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n );\n}\n\nexport default Drawer;\n"],"mappings":"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"}
@@ -85,5 +85,10 @@ export interface IDropdownProps {
85
85
  * @default false
86
86
  */
87
87
  popupMatchTriggerWidth?: boolean | number;
88
+ /**
89
+ * @description 空间不足时自动计算菜单最大高度并启用滚动,启用后 offset 固定为 0
90
+ * @default false
91
+ */
92
+ allowOverlap?: boolean;
88
93
  }
89
94
  export default function Dropdown(props: IDropdownProps): import("react/jsx-runtime").JSX.Element;
@@ -1,39 +1,123 @@
1
1
  import { cn } from "../lib/utils.js";
2
2
  /* empty css */
3
3
  import Menu from "../Menu/index.js";
4
- import { cloneElement, useCallback, useEffect, useMemo, useState } from "react";
4
+ import { cloneElement, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import { FloatingFocusManager, FloatingNode, FloatingTree, autoUpdate, flip, offset, safePolygon, shift, size, useClick, useDismiss, useFloating, useFloatingNodeId, useFloatingParentNodeId, useHover, useId as useId$1, useInteractions, useRole } from "@floating-ui/react";
7
7
  import { useMemoizedFn } from "ahooks";
8
8
  import ReactDOM from "react-dom";
9
9
  //#region src/Dropdown/index.tsx
10
+ var OVERLAY_EXIT_ANIMATION_MS = 200;
11
+ function resolvePlacementSide(currentPlacement) {
12
+ const [side] = currentPlacement.split("-");
13
+ if (side === "top" || side === "bottom" || side === "left" || side === "right") return side;
14
+ if (currentPlacement.startsWith("top")) return "top";
15
+ if (currentPlacement.startsWith("bottom")) return "bottom";
16
+ if (currentPlacement.startsWith("left")) return "left";
17
+ if (currentPlacement.startsWith("right")) return "right";
18
+ return "bottom";
19
+ }
10
20
  function Dropdown(props) {
11
- const { children, destroyPopupOnHide = true, getPopupContainer, menu, overlayClassName, placement = "bottom-start", trigger = "click", open, onOpenChange = () => {}, overlayStyle, dropdownRender, disabled, offset: offsetProps = 4, delay = 0, autoUpdatePos = false, initialFocus = -1, popupMatchTriggerWidth = false } = props;
21
+ const { children, destroyPopupOnHide = true, getPopupContainer, menu, overlayClassName, placement = "bottom-start", trigger = "click", open, onOpenChange = () => {}, overlayStyle, dropdownRender, disabled, offset: offsetProps = 4, delay = 0, autoUpdatePos = false, initialFocus = -1, popupMatchTriggerWidth = false, allowOverlap = false } = props;
12
22
  const [isOpen, setIsOpen] = useState(open || false);
23
+ const [isAnimatingOut, setIsAnimatingOut] = useState(false);
24
+ const closeAnimationTimerRef = useRef(null);
25
+ const lastResolvedFloatingStylesRef = useRef(null);
26
+ const lastResolvedSideRef = useRef(resolvePlacementSide(placement));
27
+ const currentFloatingStylesRef = useRef(null);
28
+ const currentFloatingSideRef = useRef(resolvePlacementSide(placement));
13
29
  const onOpenChangeFn = useMemoizedFn(onOpenChange);
14
30
  const isOpenControlled = open !== void 0;
31
+ const clearCloseAnimationTimer = useCallback(() => {
32
+ if (closeAnimationTimerRef.current) {
33
+ clearTimeout(closeAnimationTimerRef.current);
34
+ closeAnimationTimerRef.current = null;
35
+ }
36
+ }, []);
37
+ const stopCloseAnimation = useCallback(() => {
38
+ clearCloseAnimationTimer();
39
+ document.body.classList.remove("ald-dropdown-root-closing");
40
+ setIsAnimatingOut(false);
41
+ }, [clearCloseAnimationTimer]);
42
+ const markRootClosing = useCallback(() => {
43
+ document.body.classList.add("ald-dropdown-root-closing");
44
+ lastResolvedFloatingStylesRef.current = currentFloatingStylesRef.current ?? lastResolvedFloatingStylesRef.current;
45
+ lastResolvedSideRef.current = currentFloatingSideRef.current ?? lastResolvedSideRef.current;
46
+ }, []);
47
+ const startCloseAnimation = useCallback(() => {
48
+ clearCloseAnimationTimer();
49
+ markRootClosing();
50
+ setIsAnimatingOut(true);
51
+ closeAnimationTimerRef.current = setTimeout(() => {
52
+ document.body.classList.remove("ald-dropdown-root-closing");
53
+ setIsAnimatingOut(false);
54
+ closeAnimationTimerRef.current = null;
55
+ }, OVERLAY_EXIT_ANIMATION_MS);
56
+ }, [clearCloseAnimationTimer, markRootClosing]);
57
+ useLayoutEffect(() => {
58
+ if (!isOpenControlled) return;
59
+ if (open) {
60
+ stopCloseAnimation();
61
+ setIsOpen(true);
62
+ return;
63
+ }
64
+ if (isOpen) startCloseAnimation();
65
+ setIsOpen(false);
66
+ }, [
67
+ isOpen,
68
+ isOpenControlled,
69
+ open,
70
+ startCloseAnimation,
71
+ stopCloseAnimation
72
+ ]);
73
+ useEffect(() => {
74
+ return () => {
75
+ clearCloseAnimationTimer();
76
+ };
77
+ }, [clearCloseAnimationTimer]);
15
78
  useEffect(() => {
16
- if (isOpenControlled) setIsOpen(open);
17
- }, [open, isOpenControlled]);
79
+ return () => {
80
+ document.body.classList.remove("ald-dropdown-root-closing");
81
+ };
82
+ }, []);
18
83
  const onChangeOpen = useCallback((newOpen) => {
84
+ if (newOpen) stopCloseAnimation();
85
+ else startCloseAnimation();
19
86
  if (!isOpenControlled) setIsOpen(newOpen);
20
87
  onOpenChangeFn(newOpen);
21
- }, [isOpenControlled, onOpenChangeFn]);
88
+ }, [
89
+ isOpenControlled,
90
+ onOpenChangeFn,
91
+ startCloseAnimation,
92
+ stopCloseAnimation
93
+ ]);
22
94
  const nodeId = useFloatingNodeId();
23
- const { refs, floatingStyles, context } = useFloating({
95
+ const { refs, floatingStyles, context, placement: floatingPlacement, x, y } = useFloating({
24
96
  nodeId,
25
97
  placement,
26
98
  open: isOpen,
27
99
  onOpenChange: onChangeOpen,
28
100
  middleware: [
29
- offset(offsetProps),
30
- flip({ fallbackAxisSideDirection: "end" }),
31
- shift(),
32
- popupMatchTriggerWidth ? size({ apply({ rects, elements }) {
33
- Object.assign(elements.floating.style, { width: typeof popupMatchTriggerWidth === "number" ? `${popupMatchTriggerWidth}px` : `${rects.reference.width}px` });
34
- } }) : size({ apply({ elements }) {
35
- Object.assign(elements.floating.style, { minWidth: "144px" });
36
- } })
101
+ offset(allowOverlap ? 0 : offsetProps),
102
+ flip({
103
+ fallbackAxisSideDirection: "end",
104
+ ...allowOverlap && { fallbackStrategy: "bestFit" }
105
+ }),
106
+ shift(allowOverlap ? { mainAxis: true } : void 0),
107
+ size({
108
+ ...allowOverlap && { padding: 8 },
109
+ apply({ availableHeight, rects, elements }) {
110
+ const widthStyle = popupMatchTriggerWidth ? { width: typeof popupMatchTriggerWidth === "number" ? `${popupMatchTriggerWidth}px` : `${rects.reference.width}px` } : { minWidth: "144px" };
111
+ const heightStyle = allowOverlap ? {
112
+ maxHeight: `${Math.max(100, availableHeight)}px`,
113
+ overflowY: "auto"
114
+ } : {};
115
+ Object.assign(elements.floating.style, {
116
+ ...widthStyle,
117
+ ...heightStyle
118
+ });
119
+ }
120
+ })
37
121
  ],
38
122
  whileElementsMounted: autoUpdatePos ? autoUpdate : void 0
39
123
  });
@@ -78,41 +162,70 @@ function Dropdown(props) {
78
162
  });
79
163
  const onMenuItemClick = useCallback((info) => {
80
164
  if (menu?.onClick) menu.onClick(info);
81
- if (info.keepOpen) return;
82
- if (!isOpenControlled) setIsOpen(false);
83
- }, [isOpenControlled, menu]);
165
+ if (info.keepOpen) {
166
+ document.body.classList.remove("ald-dropdown-root-closing");
167
+ return;
168
+ }
169
+ onChangeOpen(false);
170
+ }, [menu, onChangeOpen]);
84
171
  const menuInstance = useMemo(() => {
85
172
  return /* @__PURE__ */ jsx(Menu, {
86
173
  ...menu,
87
174
  items: menu?.items || [],
88
- onClick: onMenuItemClick
175
+ onBeforeLeafItemClick: markRootClosing,
176
+ rootClosing: isAnimatingOut,
177
+ onClick: onMenuItemClick,
178
+ externalOverflow: allowOverlap
89
179
  });
90
- }, [menu, onMenuItemClick]);
180
+ }, [
181
+ allowOverlap,
182
+ isAnimatingOut,
183
+ markRootClosing,
184
+ menu,
185
+ onMenuItemClick
186
+ ]);
91
187
  const popupElement = useMemo(() => {
92
188
  return typeof dropdownRender === "function" ? dropdownRender(menuInstance) : menuInstance;
93
189
  }, [dropdownRender, menuInstance]);
190
+ const floatingSide = resolvePlacementSide(String(floatingPlacement));
191
+ const shouldKeepMounted = !destroyPopupOnHide || isOpen || isAnimatingOut;
192
+ const isPositionReady = x !== null && y !== null;
193
+ const overlayHidden = !isOpen && !isAnimatingOut || isOpen && !isPositionReady;
194
+ const resolvedFloatingStyles = isAnimatingOut ? lastResolvedFloatingStylesRef.current ?? floatingStyles : floatingStyles;
195
+ const resolvedFloatingSide = isAnimatingOut ? lastResolvedSideRef.current : floatingSide;
196
+ if (isOpen && isPositionReady) {
197
+ currentFloatingStylesRef.current = { ...floatingStyles };
198
+ currentFloatingSideRef.current = floatingSide;
199
+ lastResolvedFloatingStylesRef.current = { ...floatingStyles };
200
+ lastResolvedSideRef.current = floatingSide;
201
+ }
94
202
  const renderFloatingContent = useCallback(() => {
95
- const popupElem = /* @__PURE__ */ jsx(FloatingFocusManager, {
96
- context,
97
- modal: false,
98
- initialFocus,
203
+ const surface = /* @__PURE__ */ jsx("div", {
204
+ className: cn("ald-dropdown-overlay", "tw-pointer-events-auto tw-z-[1001] tw-max-w-none", overlayClassName, { "ald-dropdown-overlay-hidden": overlayHidden }),
205
+ ref: refs.setFloating,
206
+ style: {
207
+ ...resolvedFloatingStyles,
208
+ ...overlayStyle
209
+ },
210
+ "aria-labelledby": headingId,
211
+ ...isAnimatingOut ? {} : getFloatingProps(),
99
212
  children: /* @__PURE__ */ jsx("div", {
100
- className: cn("ald-dropdown-overlay", "tw-flex tw-flex-col tw-items-start tw-self-stretch", "tw-rounded-[var(--alias-radius-75,8px)]", "tw-border tw-border-solid tw-border-[var(--global-cool-gray-alpha-100,rgba(0,0,0,0.06))]", "tw-bg-[var(--interaction-background-modeless,#fff)]", "tw-shadow-[var(--elevation-bottom-bottom-lg,0_8px_24px_rgba(0,0,0,0.12))]", "tw-pointer-events-auto tw-z-[1001] tw-max-w-none tw-px-0 tw-py-1 tw-text-sm", overlayClassName, { "ald-dropdown-overlay-hidden tw-invisible": !isOpen }),
101
- ref: refs.setFloating,
102
- style: {
103
- ...floatingStyles,
104
- ...overlayStyle
105
- },
106
- "aria-labelledby": headingId,
107
- ...getFloatingProps(),
213
+ className: cn("ald-dropdown-surface", "tw-flex tw-flex-col tw-items-start tw-text-sm"),
214
+ "data-state": isOpen ? "open" : "closed",
215
+ "data-side": resolvedFloatingSide,
108
216
  children: popupElement
109
217
  })
110
218
  });
219
+ const popupElem = isAnimatingOut && !isOpen ? surface : /* @__PURE__ */ jsx(FloatingFocusManager, {
220
+ context,
221
+ modal: false,
222
+ initialFocus,
223
+ children: surface
224
+ });
111
225
  const popupContainer = typeof getPopupContainer === "function" ? getPopupContainer() : document.body;
112
226
  return ReactDOM.createPortal(popupElem, popupContainer);
113
227
  }, [
114
228
  context,
115
- floatingStyles,
116
229
  getFloatingProps,
117
230
  getPopupContainer,
118
231
  headingId,
@@ -120,10 +233,14 @@ function Dropdown(props) {
120
233
  refs.setFloating,
121
234
  overlayClassName,
122
235
  overlayStyle,
236
+ overlayHidden,
237
+ isAnimatingOut,
123
238
  isOpen,
124
- initialFocus
239
+ initialFocus,
240
+ resolvedFloatingSide,
241
+ resolvedFloatingStyles
125
242
  ]);
126
- const popup = destroyPopupOnHide ? isOpen && renderFloatingContent() : renderFloatingContent();
243
+ const popup = shouldKeepMounted ? renderFloatingContent() : null;
127
244
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
128
245
  ref: useCallback((node) => {
129
246
  if (node) {