@aloudata/aloudata-design 3.0.12 → 3.0.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.
- package/dist/App/index.js +6 -1
- package/dist/App/index.js.map +1 -1
- package/dist/Checkbox/index.js +1 -1
- package/dist/Checkbox/index.js.map +1 -1
- package/dist/DataPreviewTable/index.js +51 -47
- package/dist/DataPreviewTable/index.js.map +1 -1
- package/dist/Dropdown/index.js +97 -14
- package/dist/Dropdown/index.js.map +1 -1
- package/dist/Pagination/PageSizeChanger.js +3 -1
- package/dist/Pagination/PageSizeChanger.js.map +1 -1
- package/dist/Select/BaseSelect.js +39 -2
- package/dist/Select/BaseSelect.js.map +1 -1
- package/dist/Skeleton/index.js +18 -3
- package/dist/Skeleton/index.js.map +1 -1
- package/dist/Tabs/index.js +4 -4
- package/dist/Tabs/index.js.map +1 -1
- package/dist/Tree/Tree2.js +17 -5
- package/dist/Tree/Tree2.js.map +1 -1
- package/dist/aloudata-design.css +1 -1
- package/package.json +1 -1
|
@@ -31,6 +31,7 @@ var BaseSelect = React$1.forwardRef((props, ref) => {
|
|
|
31
31
|
const selectRef = useRef(null);
|
|
32
32
|
const widthStyle = getWidthStyle(width);
|
|
33
33
|
const dropdownRef = useRef(null);
|
|
34
|
+
const [matchedPopupWidth, setMatchedPopupWidth] = useState();
|
|
34
35
|
const isResponsiveMode = isResponsive(mode);
|
|
35
36
|
const isMultipleMode = isMultiple(mode);
|
|
36
37
|
/** Used for component focused management */
|
|
@@ -56,8 +57,15 @@ var BaseSelect = React$1.forwardRef((props, ref) => {
|
|
|
56
57
|
value: open
|
|
57
58
|
});
|
|
58
59
|
const mergedOpen = innerOpen;
|
|
60
|
+
const shouldMatchPopupWidth = !!popupMatchSelectWidth && typeof popupMatchSelectWidth !== "number";
|
|
61
|
+
const updateMatchedPopupWidth = React$1.useCallback(() => {
|
|
62
|
+
if (!shouldMatchPopupWidth) return;
|
|
63
|
+
const nextWidth = selectRef.current?.getBoundingClientRect().width;
|
|
64
|
+
if (nextWidth !== void 0) setMatchedPopupWidth((currentWidth) => currentWidth === nextWidth ? currentWidth : nextWidth);
|
|
65
|
+
}, [shouldMatchPopupWidth]);
|
|
59
66
|
const onToggleOpen = React$1.useCallback((newOpen) => {
|
|
60
67
|
const nextOpen = newOpen !== void 0 ? newOpen : !mergedOpen;
|
|
68
|
+
if (nextOpen) updateMatchedPopupWidth();
|
|
61
69
|
if (!nextOpen) {
|
|
62
70
|
setShowResponsiveSelectedSection(false);
|
|
63
71
|
setFocus(false);
|
|
@@ -70,8 +78,36 @@ var BaseSelect = React$1.forwardRef((props, ref) => {
|
|
|
70
78
|
mergedOpen,
|
|
71
79
|
disabled,
|
|
72
80
|
setInnerOpen,
|
|
73
|
-
onOpenChange
|
|
81
|
+
onOpenChange,
|
|
82
|
+
updateMatchedPopupWidth
|
|
74
83
|
]);
|
|
84
|
+
React$1.useLayoutEffect(() => {
|
|
85
|
+
if (!mergedOpen || !shouldMatchPopupWidth) return;
|
|
86
|
+
const element = selectRef.current;
|
|
87
|
+
if (!element) return;
|
|
88
|
+
const updateMatchedWidth = () => {
|
|
89
|
+
const nextWidth = element.getBoundingClientRect().width;
|
|
90
|
+
setMatchedPopupWidth((currentWidth) => currentWidth === nextWidth ? currentWidth : nextWidth);
|
|
91
|
+
};
|
|
92
|
+
updateMatchedWidth();
|
|
93
|
+
if (typeof ResizeObserver === "undefined") {
|
|
94
|
+
window.addEventListener("resize", updateMatchedWidth);
|
|
95
|
+
return () => {
|
|
96
|
+
window.removeEventListener("resize", updateMatchedWidth);
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
let resizeAnimationFrame;
|
|
100
|
+
const scheduleMatchedWidthUpdate = () => {
|
|
101
|
+
if (resizeAnimationFrame !== void 0) cancelAnimationFrame(resizeAnimationFrame);
|
|
102
|
+
resizeAnimationFrame = requestAnimationFrame(updateMatchedWidth);
|
|
103
|
+
};
|
|
104
|
+
const observer = new ResizeObserver(scheduleMatchedWidthUpdate);
|
|
105
|
+
observer.observe(element);
|
|
106
|
+
return () => {
|
|
107
|
+
if (resizeAnimationFrame !== void 0) cancelAnimationFrame(resizeAnimationFrame);
|
|
108
|
+
observer.disconnect();
|
|
109
|
+
};
|
|
110
|
+
}, [mergedOpen, shouldMatchPopupWidth]);
|
|
75
111
|
const [showResponsiveSelectedSection, setShowResponsiveSelectedSection] = useState(false);
|
|
76
112
|
const [overflowCollapsed, setOverflowCollapsed] = useState(false);
|
|
77
113
|
const onSelectorRemove = (val) => {
|
|
@@ -131,6 +167,7 @@ var BaseSelect = React$1.forwardRef((props, ref) => {
|
|
|
131
167
|
};
|
|
132
168
|
}
|
|
133
169
|
}, []);
|
|
170
|
+
const mergedPopupMatchSelectWidth = shouldMatchPopupWidth && matchedPopupWidth !== void 0 ? matchedPopupWidth : popupMatchSelectWidth;
|
|
134
171
|
return /* @__PURE__ */ jsxs("div", {
|
|
135
172
|
className: mergedClassName,
|
|
136
173
|
style: {
|
|
@@ -152,7 +189,7 @@ var BaseSelect = React$1.forwardRef((props, ref) => {
|
|
|
152
189
|
showSearch,
|
|
153
190
|
searchValue: innerSearchValue,
|
|
154
191
|
setInnerSearchValue,
|
|
155
|
-
popupMatchSelectWidth,
|
|
192
|
+
popupMatchSelectWidth: mergedPopupMatchSelectWidth,
|
|
156
193
|
overlayClassName: popupClassName,
|
|
157
194
|
dropdownRender,
|
|
158
195
|
overlayStyle: overlayStyle || dropdownStyle,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseSelect.js","names":[],"sources":["../../src/Select/BaseSelect.tsx"],"sourcesContent":["import './ald-select.css';\nimport * as React from 'react';\nimport useMergedState from 'rc-util/es/hooks/useMergedState';\nimport { cn } from '../lib/utils';\nimport prefixCls from '../_utils/prefixCls';\n\n/** Align config type (locally defined to avoid rc-trigger dependency) */\nexport interface AlignType {\n points?: string[];\n offset?: (number | string)[];\n targetOffset?: (number | string)[];\n overflow?: { adjustX?: boolean; adjustY?: boolean };\n _experimental?: Record<string, any>;\n}\nimport SelectTrigger, { RefTriggerProps } from './SelectTrigger';\nimport Selector from './Selector';\nimport _ from 'lodash';\nimport { useContext, useRef, useState } from 'react';\nimport { FormItemInputContext } from '../Form/FormItemContext';\nimport getWidthStyle from './utils/getWidthStyle';\nimport Suffix from './components/Suffix';\nimport { PlacementType } from '../Dropdown';\nimport {\n BaseSelectRef,\n DisplayValueType,\n ISelectProps,\n LabelInValueType,\n SelectMenuProps,\n} from './interface';\nimport SizeContext from '../ConfigProvider/sizeContext';\nimport { ConfigContext } from '../ConfigProvider';\nimport { useCompactItemContext } from '../Space/CompactContext';\nimport { MenuInfo } from '../Menu';\n\nexport type RenderNode = React.ReactNode | ((props: any) => React.ReactNode);\n\nexport type RenderDOMFunc = (props: any) => HTMLElement;\n\nexport type Mode =\n | 'multiple'\n | {\n type: 'multiple';\n responsive?: boolean;\n maxRows?: number;\n showSelectedSection?: boolean;\n };\n\nexport type RawValueType = string | number;\n\nexport type CustomTagProps = {\n label: React.ReactNode;\n value: any;\n disabled: boolean;\n onClose: (event?: React.MouseEvent<HTMLElement, MouseEvent>) => void;\n closable: boolean;\n};\nexport interface BaseSelectPrivateProps {\n // >>> MISC\n omitDomProps?: string[];\n\n // >>> Value\n displayValues: LabelInValueType[];\n onDisplayValuesChange: (\n values: LabelInValueType[],\n info: {\n type: 'add' | 'remove' | 'clear';\n values: LabelInValueType[];\n },\n ) => void;\n\n // >>> Active\n /** Current dropdown list active item string value */\n activeValue?: string;\n /** Link search input with target element */\n activeDescendantId?: string;\n onActiveValueChange?: (value: string | null) => void;\n}\n\nexport type BaseSelectPropsWithoutPrivate = Omit<\n BaseSelectProps,\n keyof BaseSelectPrivateProps\n>;\n\nexport interface BaseSelectProps\n extends BaseSelectPrivateProps,\n ISelectProps,\n React.AriaAttributes {\n className?: string;\n style?: React.CSSProperties;\n notFoundContent?: React.ReactNode;\n onClear?: () => void;\n innerSearchValue?: string;\n setInnerSearchValue?: (value: string) => void;\n displayMenu?: SelectMenuProps;\n // >>> Open\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n onOptionSelect?: (val: RawValueType) => void;\n\n // >>> Icons\n allowClear?: boolean;\n showArrow?: boolean;\n suffixIcon?: React.ReactNode;\n\n // >>> Dropdown\n dropdownAlign?: AlignType;\n placement?: PlacementType;\n getPopupContainer?: () => HTMLElement;\n\n // >>> Focus\n onBlur?: React.FocusEventHandler<HTMLElement>;\n onFocus?: React.FocusEventHandler<HTMLElement>;\n\n // >>> Rest Events\n onKeyUp?: React.KeyboardEventHandler<HTMLDivElement>;\n onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;\n onMouseDown?: React.MouseEventHandler<HTMLDivElement>;\n onPopupScroll?: React.UIEventHandler<HTMLDivElement>;\n onInputKeyDown?: React.KeyboardEventHandler<\n HTMLInputElement | HTMLTextAreaElement\n >;\n onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;\n onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;\n onClick?: React.MouseEventHandler<HTMLDivElement>;\n}\n\nexport function isMultiple(mode?: Mode) {\n return !!mode;\n}\nexport function isResponsive(mode?: Mode) {\n return mode === 'multiple' || (typeof mode === 'object' && mode?.responsive);\n}\nexport function isShowSelectedSection(mode?: Mode) {\n if (!mode || mode === 'multiple') {\n return false;\n }\n\n return (\n typeof mode === 'object' && mode.showSelectedSection && mode.responsive\n );\n}\nconst BaseSelect = React.forwardRef(\n (props: BaseSelectProps, ref: React.Ref<BaseSelectRef>) => {\n const {\n id,\n className,\n // Value\n displayValues,\n onDisplayValuesChange,\n displayMenu,\n notFoundContent,\n onClear,\n width,\n mode,\n status: customStatus,\n onOptionSelect,\n onClick,\n size: customSize,\n borderLess = false,\n // Status\n disabled,\n placeholder,\n loading = false,\n\n // Open\n open,\n defaultOpen,\n onOpenChange,\n showSearch,\n innerSearchValue,\n setInnerSearchValue,\n // Icons\n allowClear,\n showArrow = true,\n prefix,\n suffixIcon,\n dropdownRender,\n overlayStyle,\n dropdownStyle,\n popupMatchSelectWidth = true,\n placement,\n popupClassName,\n allowOverlap,\n style,\n 'data-testid': dataTestid,\n 'aria-label': ariaLabel,\n ...restProps\n // Rest Props\n } = props;\n const setClassNames = prefixCls('select');\n const selectRef = useRef<HTMLDivElement>(null);\n const widthStyle = getWidthStyle(width);\n const dropdownRef = useRef<RefTriggerProps>(null);\n const isResponsiveMode = isResponsive(mode);\n const isMultipleMode = isMultiple(mode);\n /** Used for component focused management */\n const contentSize = useContext(SizeContext);\n const { direction } = React.useContext(ConfigContext);\n // ===================== Compact Item =====================\n const { compactSize, compactItemClassnames } = useCompactItemContext(\n 'ald-select',\n direction,\n );\n const size = compactSize || customSize || contentSize || 'middle';\n const {\n status: contextStatus,\n // hasFeedback,\n // isFormItemInput,\n // feedbackIcon,\n } = useContext(FormItemInputContext);\n\n const mergedStatus =\n customStatus === null ? undefined : customStatus || contextStatus;\n\n // =========================== Imperative ===========================\n React.useImperativeHandle(ref, () => ({\n focus: () => setFocus(true),\n blur: () => setFocus(false),\n open: () => onToggleOpen(true),\n close: () => {\n console.log('ref close');\n onToggleOpen(false);\n },\n }));\n const [focus, setFocus] = useState(false);\n const [isHover, setIsHover] = useState(false);\n // ============================== Open ==============================\n const [innerOpen, setInnerOpen] = useMergedState<boolean>(false, {\n defaultValue: defaultOpen,\n value: open,\n });\n const mergedOpen = innerOpen;\n const onToggleOpen = React.useCallback(\n (newOpen?: boolean) => {\n const nextOpen = newOpen !== undefined ? newOpen : !mergedOpen;\n if (!nextOpen) {\n setShowResponsiveSelectedSection(false);\n setFocus(false);\n }\n if (mergedOpen !== nextOpen && !disabled) {\n setInnerOpen(nextOpen);\n onOpenChange?.(nextOpen);\n }\n },\n [mergedOpen, disabled, setInnerOpen, onOpenChange],\n );\n const [showResponsiveSelectedSection, setShowResponsiveSelectedSection] =\n useState(false);\n // 多选 responsive 进入 -1 收纳态(仅\"首 tag + +N\")时为 true,\n // width:auto 下据此给根节点加 overflow-collapsed 钳住 min-width 链,\n // 抵抗父 flex 行因 intrinsic 低估造成的假压缩(详见 ald-select.css 对应段)\n const [overflowCollapsed, setOverflowCollapsed] = useState(false);\n\n // ============================ Selector ============================\n const onSelectorRemove = (val: DisplayValueType) => {\n const newValues = displayValues.filter((i) => i !== val);\n\n onDisplayValuesChange(newValues, {\n type: 'remove',\n values: [\n {\n label: val.label,\n value: val.value as RawValueType,\n },\n ],\n });\n };\n\n const mergedClassName = cn(\n setClassNames('beta-ald-select', size, {\n active: mergedOpen || focus,\n borderless: borderLess,\n disabled: disabled,\n loading: loading,\n [`${mergedStatus}`]: !!mergedStatus,\n 'multiple-responsive': isResponsiveMode,\n 'multiple-default': isMultipleMode && !isResponsiveMode,\n // 仅 width:auto 生效:固定宽度下 -1 收纳态的收缩省略是预期行为,\n // 钳 min-width 反而会把固定宽撑破\n 'overflow-collapsed':\n overflowCollapsed && width === 'auto' && !_.isEmpty(displayValues),\n }),\n className,\n compactItemClassnames,\n );\n const showMenu = React.useMemo(() => {\n if (!displayMenu) {\n return undefined;\n }\n\n return {\n ...displayMenu,\n onClick: (info: MenuInfo) => {\n displayMenu?.onClick?.(info);\n if (isMultipleMode) {\n // 多选默认选中后不收起下拉:Dropdown 在 menu.onClick 之后读\n // info.keepOpen 决定是否 onChangeOpen(false),不设则每次点击都被\n // 当成关闭信号。用 ?? 保留用户在自己的 onClick 里显式设 false 的能力\n info.keepOpen = info.keepOpen ?? true;\n } else if (displayMenu) {\n onToggleOpen(false);\n setFocus(false);\n }\n setIsHover(false);\n onOptionSelect?.(info.key);\n },\n };\n }, [displayMenu, isMultipleMode, onToggleOpen, onOptionSelect]);\n const onSelectClick = React.useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (disabled) {\n return;\n }\n onClick?.(e);\n },\n [disabled, onClick],\n );\n React.useEffect(() => {\n const element = selectRef.current;\n if (element) {\n const handleMouseOver = () => setIsHover(true);\n const handleMouseOut = () => setIsHover(false);\n\n element.addEventListener('mouseover', handleMouseOver);\n element.addEventListener('mouseout', handleMouseOut);\n\n return () => {\n element.removeEventListener('mouseover', handleMouseOver);\n element.removeEventListener('mouseout', handleMouseOut);\n };\n }\n }, []);\n return (\n <div\n className={mergedClassName}\n style={{\n ...widthStyle,\n // width:auto 且无选中值时锁 shrink:Chromium 对该子树给父 flex 行的\n // intrinsic 宽度贡献恒少约 14px,父行按低估值布局后回头压缩根节点,\n // placeholder 右侧会被裁;有选中值时必须保持可压缩,rc-overflow\n // 才能在父行变窄时把放不下的 tag 收纳成 \"+N\"\n ...(width === 'auto' && _.isEmpty(displayValues)\n ? { flexShrink: 0 }\n : {}),\n ...style,\n }}\n ref={selectRef}\n id={id}\n onClick={onSelectClick}\n data-testid={dataTestid}\n aria-label={ariaLabel}\n >\n <SelectTrigger\n {...restProps}\n open={!!mergedOpen}\n notFoundContent={notFoundContent}\n menu={showMenu}\n ref={dropdownRef}\n showSearch={showSearch}\n searchValue={innerSearchValue}\n setInnerSearchValue={setInnerSearchValue}\n popupMatchSelectWidth={popupMatchSelectWidth}\n overlayClassName={popupClassName}\n // @ts-ignore\n dropdownRender={dropdownRender}\n overlayStyle={overlayStyle || dropdownStyle}\n placement={placement}\n allowOverlap={allowOverlap}\n onOpenChange={(open) => {\n if (isResponsiveMode) {\n if (\n !mergedOpen &&\n !showResponsiveSelectedSection &&\n displayValues.length > 0 &&\n isShowSelectedSection(mode)\n ) {\n setShowResponsiveSelectedSection(true);\n } else {\n onToggleOpen(open);\n }\n } else {\n onToggleOpen(open);\n }\n }}\n >\n {/* antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器 */}\n <div className={cn(setClassNames('trigger'), 'ant-select-selector')}>\n {prefix && <div className={setClassNames('prefix')}>{prefix}</div>}\n {_.isEmpty(displayValues) && (\n <div\n className={cn(\n setClassNames('placeholder'),\n 'ant-select-selection-placeholder',\n )}\n >\n {placeholder}\n </div>\n )}\n {!_.isEmpty(displayValues) && (\n <Selector\n {...restProps}\n suffixIcon={suffixIcon}\n size={size}\n disabled={disabled}\n mode={mode}\n dropdownRef={dropdownRef}\n displayValues={displayValues}\n onToggleOpen={onToggleOpen}\n onRemove={onSelectorRemove}\n showResponsiveSelectedSection={showResponsiveSelectedSection}\n onOverflowCollapsedChange={setOverflowCollapsed}\n ></Selector>\n )}\n </div>\n </SelectTrigger>\n <Suffix\n showArrow={showArrow}\n allowClear={allowClear}\n disabled={disabled}\n loading={loading}\n isHover={isHover}\n onClear={() => {\n onToggleOpen(false);\n\n onDisplayValuesChange([], {\n type: 'clear',\n values: displayValues,\n });\n onClear?.();\n }}\n isActive={innerOpen}\n icon={suffixIcon}\n displayValues={displayValues}\n ></Suffix>\n </div>\n );\n },\n);\n\nexport default BaseSelect;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BaseSelect.js","names":[],"sources":["../../src/Select/BaseSelect.tsx"],"sourcesContent":["import './ald-select.css';\nimport * as React from 'react';\nimport useMergedState from 'rc-util/es/hooks/useMergedState';\nimport { cn } from '../lib/utils';\nimport prefixCls from '../_utils/prefixCls';\n\n/** Align config type (locally defined to avoid rc-trigger dependency) */\nexport interface AlignType {\n points?: string[];\n offset?: (number | string)[];\n targetOffset?: (number | string)[];\n overflow?: { adjustX?: boolean; adjustY?: boolean };\n _experimental?: Record<string, any>;\n}\nimport SelectTrigger, { RefTriggerProps } from './SelectTrigger';\nimport Selector from './Selector';\nimport _ from 'lodash';\nimport { useContext, useRef, useState } from 'react';\nimport { FormItemInputContext } from '../Form/FormItemContext';\nimport getWidthStyle from './utils/getWidthStyle';\nimport Suffix from './components/Suffix';\nimport { PlacementType } from '../Dropdown';\nimport {\n BaseSelectRef,\n DisplayValueType,\n ISelectProps,\n LabelInValueType,\n SelectMenuProps,\n} from './interface';\nimport SizeContext from '../ConfigProvider/sizeContext';\nimport { ConfigContext } from '../ConfigProvider';\nimport { useCompactItemContext } from '../Space/CompactContext';\nimport { MenuInfo } from '../Menu';\n\nexport type RenderNode = React.ReactNode | ((props: any) => React.ReactNode);\n\nexport type RenderDOMFunc = (props: any) => HTMLElement;\n\nexport type Mode =\n | 'multiple'\n | {\n type: 'multiple';\n responsive?: boolean;\n maxRows?: number;\n showSelectedSection?: boolean;\n };\n\nexport type RawValueType = string | number;\n\nexport type CustomTagProps = {\n label: React.ReactNode;\n value: any;\n disabled: boolean;\n onClose: (event?: React.MouseEvent<HTMLElement, MouseEvent>) => void;\n closable: boolean;\n};\nexport interface BaseSelectPrivateProps {\n // >>> MISC\n omitDomProps?: string[];\n\n // >>> Value\n displayValues: LabelInValueType[];\n onDisplayValuesChange: (\n values: LabelInValueType[],\n info: {\n type: 'add' | 'remove' | 'clear';\n values: LabelInValueType[];\n },\n ) => void;\n\n // >>> Active\n /** Current dropdown list active item string value */\n activeValue?: string;\n /** Link search input with target element */\n activeDescendantId?: string;\n onActiveValueChange?: (value: string | null) => void;\n}\n\nexport type BaseSelectPropsWithoutPrivate = Omit<\n BaseSelectProps,\n keyof BaseSelectPrivateProps\n>;\n\nexport interface BaseSelectProps\n extends BaseSelectPrivateProps,\n ISelectProps,\n React.AriaAttributes {\n className?: string;\n style?: React.CSSProperties;\n notFoundContent?: React.ReactNode;\n onClear?: () => void;\n innerSearchValue?: string;\n setInnerSearchValue?: (value: string) => void;\n displayMenu?: SelectMenuProps;\n // >>> Open\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n onOptionSelect?: (val: RawValueType) => void;\n\n // >>> Icons\n allowClear?: boolean;\n showArrow?: boolean;\n suffixIcon?: React.ReactNode;\n\n // >>> Dropdown\n dropdownAlign?: AlignType;\n placement?: PlacementType;\n getPopupContainer?: () => HTMLElement;\n\n // >>> Focus\n onBlur?: React.FocusEventHandler<HTMLElement>;\n onFocus?: React.FocusEventHandler<HTMLElement>;\n\n // >>> Rest Events\n onKeyUp?: React.KeyboardEventHandler<HTMLDivElement>;\n onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;\n onMouseDown?: React.MouseEventHandler<HTMLDivElement>;\n onPopupScroll?: React.UIEventHandler<HTMLDivElement>;\n onInputKeyDown?: React.KeyboardEventHandler<\n HTMLInputElement | HTMLTextAreaElement\n >;\n onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;\n onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;\n onClick?: React.MouseEventHandler<HTMLDivElement>;\n}\n\nexport function isMultiple(mode?: Mode) {\n return !!mode;\n}\nexport function isResponsive(mode?: Mode) {\n return mode === 'multiple' || (typeof mode === 'object' && mode?.responsive);\n}\nexport function isShowSelectedSection(mode?: Mode) {\n if (!mode || mode === 'multiple') {\n return false;\n }\n\n return (\n typeof mode === 'object' && mode.showSelectedSection && mode.responsive\n );\n}\nconst BaseSelect = React.forwardRef(\n (props: BaseSelectProps, ref: React.Ref<BaseSelectRef>) => {\n const {\n id,\n className,\n // Value\n displayValues,\n onDisplayValuesChange,\n displayMenu,\n notFoundContent,\n onClear,\n width,\n mode,\n status: customStatus,\n onOptionSelect,\n onClick,\n size: customSize,\n borderLess = false,\n // Status\n disabled,\n placeholder,\n loading = false,\n\n // Open\n open,\n defaultOpen,\n onOpenChange,\n showSearch,\n innerSearchValue,\n setInnerSearchValue,\n // Icons\n allowClear,\n showArrow = true,\n prefix,\n suffixIcon,\n dropdownRender,\n overlayStyle,\n dropdownStyle,\n popupMatchSelectWidth = true,\n placement,\n popupClassName,\n allowOverlap,\n style,\n 'data-testid': dataTestid,\n 'aria-label': ariaLabel,\n ...restProps\n // Rest Props\n } = props;\n const setClassNames = prefixCls('select');\n const selectRef = useRef<HTMLDivElement>(null);\n const widthStyle = getWidthStyle(width);\n const dropdownRef = useRef<RefTriggerProps>(null);\n const [matchedPopupWidth, setMatchedPopupWidth] = useState<\n number | undefined\n >();\n const isResponsiveMode = isResponsive(mode);\n const isMultipleMode = isMultiple(mode);\n /** Used for component focused management */\n const contentSize = useContext(SizeContext);\n const { direction } = React.useContext(ConfigContext);\n // ===================== Compact Item =====================\n const { compactSize, compactItemClassnames } = useCompactItemContext(\n 'ald-select',\n direction,\n );\n const size = compactSize || customSize || contentSize || 'middle';\n const {\n status: contextStatus,\n // hasFeedback,\n // isFormItemInput,\n // feedbackIcon,\n } = useContext(FormItemInputContext);\n\n const mergedStatus =\n customStatus === null ? undefined : customStatus || contextStatus;\n\n // =========================== Imperative ===========================\n React.useImperativeHandle(ref, () => ({\n focus: () => setFocus(true),\n blur: () => setFocus(false),\n open: () => onToggleOpen(true),\n close: () => {\n console.log('ref close');\n onToggleOpen(false);\n },\n }));\n const [focus, setFocus] = useState(false);\n const [isHover, setIsHover] = useState(false);\n // ============================== Open ==============================\n const [innerOpen, setInnerOpen] = useMergedState<boolean>(false, {\n defaultValue: defaultOpen,\n value: open,\n });\n const mergedOpen = innerOpen;\n const shouldMatchPopupWidth =\n !!popupMatchSelectWidth && typeof popupMatchSelectWidth !== 'number';\n const updateMatchedPopupWidth = React.useCallback(() => {\n if (!shouldMatchPopupWidth) {\n return;\n }\n\n const nextWidth = selectRef.current?.getBoundingClientRect().width;\n if (nextWidth !== undefined) {\n setMatchedPopupWidth((currentWidth) =>\n currentWidth === nextWidth ? currentWidth : nextWidth,\n );\n }\n }, [shouldMatchPopupWidth]);\n const onToggleOpen = React.useCallback(\n (newOpen?: boolean) => {\n const nextOpen = newOpen !== undefined ? newOpen : !mergedOpen;\n if (nextOpen) {\n updateMatchedPopupWidth();\n }\n if (!nextOpen) {\n setShowResponsiveSelectedSection(false);\n setFocus(false);\n }\n if (mergedOpen !== nextOpen && !disabled) {\n setInnerOpen(nextOpen);\n onOpenChange?.(nextOpen);\n }\n },\n [\n mergedOpen,\n disabled,\n setInnerOpen,\n onOpenChange,\n updateMatchedPopupWidth,\n ],\n );\n React.useLayoutEffect(() => {\n if (!mergedOpen || !shouldMatchPopupWidth) {\n return;\n }\n\n const element = selectRef.current;\n if (!element) {\n return;\n }\n\n const updateMatchedWidth = () => {\n const nextWidth = element.getBoundingClientRect().width;\n setMatchedPopupWidth((currentWidth) =>\n currentWidth === nextWidth ? currentWidth : nextWidth,\n );\n };\n\n updateMatchedWidth();\n\n if (typeof ResizeObserver === 'undefined') {\n window.addEventListener('resize', updateMatchedWidth);\n return () => {\n window.removeEventListener('resize', updateMatchedWidth);\n };\n }\n\n let resizeAnimationFrame: number | undefined;\n const scheduleMatchedWidthUpdate = () => {\n if (resizeAnimationFrame !== undefined) {\n cancelAnimationFrame(resizeAnimationFrame);\n }\n resizeAnimationFrame = requestAnimationFrame(updateMatchedWidth);\n };\n\n const observer = new ResizeObserver(scheduleMatchedWidthUpdate);\n observer.observe(element);\n\n return () => {\n if (resizeAnimationFrame !== undefined) {\n cancelAnimationFrame(resizeAnimationFrame);\n }\n observer.disconnect();\n };\n }, [mergedOpen, shouldMatchPopupWidth]);\n const [showResponsiveSelectedSection, setShowResponsiveSelectedSection] =\n useState(false);\n // 多选 responsive 进入 -1 收纳态(仅\"首 tag + +N\")时为 true,\n // width:auto 下据此给根节点加 overflow-collapsed 钳住 min-width 链,\n // 抵抗父 flex 行因 intrinsic 低估造成的假压缩(详见 ald-select.css 对应段)\n const [overflowCollapsed, setOverflowCollapsed] = useState(false);\n\n // ============================ Selector ============================\n const onSelectorRemove = (val: DisplayValueType) => {\n const newValues = displayValues.filter((i) => i !== val);\n\n onDisplayValuesChange(newValues, {\n type: 'remove',\n values: [\n {\n label: val.label,\n value: val.value as RawValueType,\n },\n ],\n });\n };\n\n const mergedClassName = cn(\n setClassNames('beta-ald-select', size, {\n active: mergedOpen || focus,\n borderless: borderLess,\n disabled: disabled,\n loading: loading,\n [`${mergedStatus}`]: !!mergedStatus,\n 'multiple-responsive': isResponsiveMode,\n 'multiple-default': isMultipleMode && !isResponsiveMode,\n // 仅 width:auto 生效:固定宽度下 -1 收纳态的收缩省略是预期行为,\n // 钳 min-width 反而会把固定宽撑破\n 'overflow-collapsed':\n overflowCollapsed && width === 'auto' && !_.isEmpty(displayValues),\n }),\n className,\n compactItemClassnames,\n );\n const showMenu = React.useMemo(() => {\n if (!displayMenu) {\n return undefined;\n }\n\n return {\n ...displayMenu,\n onClick: (info: MenuInfo) => {\n displayMenu?.onClick?.(info);\n if (isMultipleMode) {\n // 多选默认选中后不收起下拉:Dropdown 在 menu.onClick 之后读\n // info.keepOpen 决定是否 onChangeOpen(false),不设则每次点击都被\n // 当成关闭信号。用 ?? 保留用户在自己的 onClick 里显式设 false 的能力\n info.keepOpen = info.keepOpen ?? true;\n } else if (displayMenu) {\n onToggleOpen(false);\n setFocus(false);\n }\n setIsHover(false);\n onOptionSelect?.(info.key);\n },\n };\n }, [displayMenu, isMultipleMode, onToggleOpen, onOptionSelect]);\n const onSelectClick = React.useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (disabled) {\n return;\n }\n onClick?.(e);\n },\n [disabled, onClick],\n );\n React.useEffect(() => {\n const element = selectRef.current;\n if (element) {\n const handleMouseOver = () => setIsHover(true);\n const handleMouseOut = () => setIsHover(false);\n\n element.addEventListener('mouseover', handleMouseOver);\n element.addEventListener('mouseout', handleMouseOut);\n\n return () => {\n element.removeEventListener('mouseover', handleMouseOver);\n element.removeEventListener('mouseout', handleMouseOut);\n };\n }\n }, []);\n const mergedPopupMatchSelectWidth =\n shouldMatchPopupWidth && matchedPopupWidth !== undefined\n ? matchedPopupWidth\n : popupMatchSelectWidth;\n return (\n <div\n className={mergedClassName}\n style={{\n ...widthStyle,\n // width:auto 且无选中值时锁 shrink:Chromium 对该子树给父 flex 行的\n // intrinsic 宽度贡献恒少约 14px,父行按低估值布局后回头压缩根节点,\n // placeholder 右侧会被裁;有选中值时必须保持可压缩,rc-overflow\n // 才能在父行变窄时把放不下的 tag 收纳成 \"+N\"\n ...(width === 'auto' && _.isEmpty(displayValues)\n ? { flexShrink: 0 }\n : {}),\n ...style,\n }}\n ref={selectRef}\n id={id}\n onClick={onSelectClick}\n data-testid={dataTestid}\n aria-label={ariaLabel}\n >\n <SelectTrigger\n {...restProps}\n open={!!mergedOpen}\n notFoundContent={notFoundContent}\n menu={showMenu}\n ref={dropdownRef}\n showSearch={showSearch}\n searchValue={innerSearchValue}\n setInnerSearchValue={setInnerSearchValue}\n popupMatchSelectWidth={mergedPopupMatchSelectWidth}\n overlayClassName={popupClassName}\n // @ts-ignore\n dropdownRender={dropdownRender}\n overlayStyle={overlayStyle || dropdownStyle}\n placement={placement}\n allowOverlap={allowOverlap}\n onOpenChange={(open) => {\n if (isResponsiveMode) {\n if (\n !mergedOpen &&\n !showResponsiveSelectedSection &&\n displayValues.length > 0 &&\n isShowSelectedSection(mode)\n ) {\n setShowResponsiveSelectedSection(true);\n } else {\n onToggleOpen(open);\n }\n } else {\n onToggleOpen(open);\n }\n }}\n >\n {/* antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器 */}\n <div className={cn(setClassNames('trigger'), 'ant-select-selector')}>\n {prefix && <div className={setClassNames('prefix')}>{prefix}</div>}\n {_.isEmpty(displayValues) && (\n <div\n className={cn(\n setClassNames('placeholder'),\n 'ant-select-selection-placeholder',\n )}\n >\n {placeholder}\n </div>\n )}\n {!_.isEmpty(displayValues) && (\n <Selector\n {...restProps}\n suffixIcon={suffixIcon}\n size={size}\n disabled={disabled}\n mode={mode}\n dropdownRef={dropdownRef}\n displayValues={displayValues}\n onToggleOpen={onToggleOpen}\n onRemove={onSelectorRemove}\n showResponsiveSelectedSection={showResponsiveSelectedSection}\n onOverflowCollapsedChange={setOverflowCollapsed}\n ></Selector>\n )}\n </div>\n </SelectTrigger>\n <Suffix\n showArrow={showArrow}\n allowClear={allowClear}\n disabled={disabled}\n loading={loading}\n isHover={isHover}\n onClear={() => {\n onToggleOpen(false);\n\n onDisplayValuesChange([], {\n type: 'clear',\n values: displayValues,\n });\n onClear?.();\n }}\n isActive={innerOpen}\n icon={suffixIcon}\n displayValues={displayValues}\n ></Suffix>\n </div>\n );\n },\n);\n\nexport default BaseSelect;\n"],"mappings":"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"}
|
package/dist/Skeleton/index.js
CHANGED
|
@@ -26,6 +26,7 @@ var INPUT_SIZE_MAP = {
|
|
|
26
26
|
default: 32,
|
|
27
27
|
large: 40
|
|
28
28
|
};
|
|
29
|
+
var SKELETON_BACKGROUND_COLOR = "var(--alias-colors-bg-skeleton-subtle, var(--background-neutral-subtle, #e5e7eb))";
|
|
29
30
|
var SkeletonButton = ({ active, size = "default", shape = "square", block, className, style }) => {
|
|
30
31
|
const animClass = active ? "tw-animate-pulse" : "";
|
|
31
32
|
const dims = BUTTON_SIZE_MAP[size];
|
|
@@ -33,6 +34,7 @@ var SkeletonButton = ({ active, size = "default", shape = "square", block, class
|
|
|
33
34
|
return /* @__PURE__ */ jsx("span", {
|
|
34
35
|
className: cn("ald-skeleton-button tw-inline-block tw-bg-[var(--background-neutral-subtle)]", animClass, block && "tw-block tw-w-full", className),
|
|
35
36
|
style: {
|
|
37
|
+
backgroundColor: SKELETON_BACKGROUND_COLOR,
|
|
36
38
|
width: shape === "circle" ? dims.height : block ? void 0 : dims.width,
|
|
37
39
|
height: dims.height,
|
|
38
40
|
borderRadius,
|
|
@@ -46,6 +48,7 @@ var SkeletonAvatar = ({ active, size = "default", shape = "circle", className, s
|
|
|
46
48
|
return /* @__PURE__ */ jsx("span", {
|
|
47
49
|
className: cn("ald-skeleton-avatar tw-inline-block tw-bg-[var(--background-neutral-subtle)]", animClass, className),
|
|
48
50
|
style: {
|
|
51
|
+
backgroundColor: SKELETON_BACKGROUND_COLOR,
|
|
49
52
|
width: dim,
|
|
50
53
|
height: dim,
|
|
51
54
|
borderRadius: shape === "circle" ? "50%" : "4px",
|
|
@@ -59,6 +62,7 @@ var SkeletonInput = ({ active, size = "default", block, className, style }) => {
|
|
|
59
62
|
return /* @__PURE__ */ jsx("span", {
|
|
60
63
|
className: cn("ald-skeleton-input tw-inline-block tw-bg-[var(--background-neutral-subtle)]", animClass, block && "tw-block tw-w-full", className),
|
|
61
64
|
style: {
|
|
65
|
+
backgroundColor: SKELETON_BACKGROUND_COLOR,
|
|
62
66
|
width: block ? void 0 : 200,
|
|
63
67
|
height,
|
|
64
68
|
borderRadius: 4,
|
|
@@ -70,6 +74,7 @@ var SkeletonImage = ({ active, className, style }) => {
|
|
|
70
74
|
return /* @__PURE__ */ jsx("span", {
|
|
71
75
|
className: cn("ald-skeleton-image tw-flex tw-items-center tw-justify-center tw-bg-[var(--background-neutral-subtle)]", active ? "tw-animate-pulse" : "", className),
|
|
72
76
|
style: {
|
|
77
|
+
backgroundColor: SKELETON_BACKGROUND_COLOR,
|
|
73
78
|
width: 96,
|
|
74
79
|
height: 96,
|
|
75
80
|
borderRadius: 4,
|
|
@@ -97,6 +102,7 @@ function Skeleton(props) {
|
|
|
97
102
|
children: [avatar && /* @__PURE__ */ jsx("div", {
|
|
98
103
|
className: cn("ald-skeleton-avatar tw-shrink-0 tw-rounded-full tw-bg-[var(--background-neutral-subtle)]", animClass, typeof avatar === "object" && avatar.shape === "square" ? "tw-rounded-r-50" : "tw-rounded-full"),
|
|
99
104
|
style: {
|
|
105
|
+
backgroundColor: SKELETON_BACKGROUND_COLOR,
|
|
100
106
|
width: 40,
|
|
101
107
|
height: 40
|
|
102
108
|
}
|
|
@@ -104,10 +110,16 @@ function Skeleton(props) {
|
|
|
104
110
|
className: "ald-skeleton-content tw-flex-1",
|
|
105
111
|
children: [title && /* @__PURE__ */ jsx("div", {
|
|
106
112
|
className: cn("ald-skeleton-title tw-mb-4 tw-h-4 tw-bg-[var(--background-neutral-subtle)]", animClass, round ? "tw-rounded-full" : "tw-rounded-r-25"),
|
|
107
|
-
style: {
|
|
113
|
+
style: {
|
|
114
|
+
backgroundColor: SKELETON_BACKGROUND_COLOR,
|
|
115
|
+
width: typeof title === "object" ? title.width : "38%"
|
|
116
|
+
}
|
|
108
117
|
}), paragraph && Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx("div", {
|
|
109
118
|
className: cn("ald-skeleton-paragraph tw-mb-3 tw-h-4 tw-bg-[var(--background-neutral-subtle)]", animClass, round ? "tw-rounded-full" : "tw-rounded-r-25"),
|
|
110
|
-
style: {
|
|
119
|
+
style: {
|
|
120
|
+
backgroundColor: SKELETON_BACKGROUND_COLOR,
|
|
121
|
+
width: i === rows - 1 ? "61%" : "100%"
|
|
122
|
+
}
|
|
111
123
|
}, i))]
|
|
112
124
|
})]
|
|
113
125
|
});
|
|
@@ -115,7 +127,10 @@ function Skeleton(props) {
|
|
|
115
127
|
function SkeletonNode({ active, className, style, children }) {
|
|
116
128
|
return /* @__PURE__ */ jsx("div", {
|
|
117
129
|
className: cn("ald-skeleton-node tw-inline-flex tw-items-center tw-justify-center tw-rounded-r-25 tw-bg-[var(--background-neutral-subtle)]", active && "tw-animate-pulse", className),
|
|
118
|
-
style
|
|
130
|
+
style: {
|
|
131
|
+
backgroundColor: SKELETON_BACKGROUND_COLOR,
|
|
132
|
+
...style
|
|
133
|
+
},
|
|
119
134
|
children
|
|
120
135
|
});
|
|
121
136
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/Skeleton/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../lib/utils';\n\nexport interface SkeletonProps {\n active?: boolean;\n loading?: boolean;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n avatar?:\n | boolean\n | {\n size?: number | 'small' | 'default' | 'large';\n shape?: 'circle' | 'square';\n };\n title?: boolean | { width?: number | string };\n paragraph?:\n | boolean\n | { rows?: number; width?: number | string | (number | string)[] };\n round?: boolean;\n}\n\n// --- Shared types for sub-components ---\n\ntype SkeletonElementSize = 'small' | 'default' | 'large';\n\ninterface SkeletonElementBaseProps {\n active?: boolean;\n size?: SkeletonElementSize;\n className?: string;\n style?: React.CSSProperties;\n}\n\ninterface SkeletonButtonProps extends SkeletonElementBaseProps {\n shape?: 'circle' | 'round' | 'square';\n block?: boolean;\n}\n\ninterface SkeletonAvatarProps extends SkeletonElementBaseProps {\n shape?: 'circle' | 'square';\n}\n\ninterface SkeletonInputProps extends SkeletonElementBaseProps {\n block?: boolean;\n}\n\ninterface SkeletonImageProps {\n active?: boolean;\n className?: string;\n style?: React.CSSProperties;\n}\n\n// --- Size maps ---\n\nconst BUTTON_SIZE_MAP: Record<\n SkeletonElementSize,\n { width: number; height: number }\n> = {\n small: { width: 64, height: 24 },\n default: { width: 80, height: 32 },\n large: { width: 96, height: 40 },\n};\n\nconst AVATAR_SIZE_MAP: Record<SkeletonElementSize, number> = {\n small: 24,\n default: 32,\n large: 40,\n};\n\nconst INPUT_SIZE_MAP: Record<SkeletonElementSize, number> = {\n small: 24,\n default: 32,\n large: 40,\n};\n\n// --- Sub-components ---\n\nconst SkeletonButton: React.FC<SkeletonButtonProps> = ({\n active,\n size = 'default',\n shape = 'square',\n block,\n className,\n style,\n}) => {\n const animClass = active ? 'tw-animate-pulse' : '';\n const dims = BUTTON_SIZE_MAP[size];\n\n const borderRadius =\n shape === 'circle'\n ? '50%'\n : shape === 'round'\n ? `${dims.height / 2}px`\n : '4px';\n\n return (\n <span\n className={cn(\n 'ald-skeleton-button tw-inline-block tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n block && 'tw-block tw-w-full',\n className,\n )}\n style={{\n width:\n shape === 'circle' ? dims.height : block ? undefined : dims.width,\n height: dims.height,\n borderRadius,\n ...style,\n }}\n />\n );\n};\n\nconst SkeletonAvatar: React.FC<SkeletonAvatarProps> = ({\n active,\n size = 'default',\n shape = 'circle',\n className,\n style,\n}) => {\n const animClass = active ? 'tw-animate-pulse' : '';\n const dim = typeof size === 'number' ? size : AVATAR_SIZE_MAP[size];\n\n return (\n <span\n className={cn(\n 'ald-skeleton-avatar tw-inline-block tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n className,\n )}\n style={{\n width: dim,\n height: dim,\n borderRadius: shape === 'circle' ? '50%' : '4px',\n ...style,\n }}\n />\n );\n};\n\nconst SkeletonInput: React.FC<SkeletonInputProps> = ({\n active,\n size = 'default',\n block,\n className,\n style,\n}) => {\n const animClass = active ? 'tw-animate-pulse' : '';\n const height = INPUT_SIZE_MAP[size];\n\n return (\n <span\n className={cn(\n 'ald-skeleton-input tw-inline-block tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n block && 'tw-block tw-w-full',\n className,\n )}\n style={{\n width: block ? undefined : 200,\n height,\n borderRadius: 4,\n ...style,\n }}\n />\n );\n};\n\nconst SkeletonImage: React.FC<SkeletonImageProps> = ({\n active,\n className,\n style,\n}) => {\n const animClass = active ? 'tw-animate-pulse' : '';\n\n return (\n <span\n className={cn(\n 'ald-skeleton-image tw-flex tw-items-center tw-justify-center tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n className,\n )}\n style={{\n width: 96,\n height: 96,\n borderRadius: 4,\n ...style,\n }}\n >\n <svg\n viewBox=\"0 0 1098 1024\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"48\"\n height=\"48\"\n fill=\"currentColor\"\n style={{ opacity: 0.2 }}\n >\n <path d=\"M365.714 329.143q0 45.714-32.036 77.75t-77.75 32.036-77.75-32.036-32.036-77.75 32.036-77.75 77.75-32.036 77.75 32.036 32.036 77.75zM950.857 548.571v256H146.286V694.857l182.857-182.857 91.429 91.429 292.571-292.571zM1005.714 146.286H91.429q-7.46 0-12.873 5.413T73.143 164.571v694.857q0 7.46 5.413 12.873t12.873 5.413h914.286q7.46 0 12.873-5.413t5.413-12.873V164.571q0-7.46-5.413-12.873t-12.873-5.413zm91.429 18.286v694.857q0 37.714-26.857 64.571t-64.571 26.857H91.429q-37.714 0-64.571-26.857T0 859.429V164.571q0-37.714 26.857-64.571T91.429 73.143h914.286q37.714 0 64.571 26.857t26.857 64.571z\" />\n </svg>\n </span>\n );\n};\n\n// --- Main Skeleton component ---\n\nfunction Skeleton(props: SkeletonProps) {\n const {\n active = false,\n loading = true,\n className,\n style,\n children,\n avatar,\n title = true,\n paragraph = true,\n round,\n } = props;\n\n if (!loading) {\n return <>{children}</>;\n }\n\n const animClass = active ? 'tw-animate-pulse' : '';\n const rows =\n typeof paragraph === 'object' ? paragraph.rows || 3 : paragraph ? 3 : 0;\n\n return (\n <div\n className={cn('ald-skeleton tw-flex tw-gap-4', className)}\n style={style}\n >\n {avatar && (\n <div\n className={cn(\n 'ald-skeleton-avatar tw-shrink-0 tw-rounded-full tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n typeof avatar === 'object' && avatar.shape === 'square'\n ? 'tw-rounded-r-50'\n : 'tw-rounded-full',\n )}\n style={{ width: 40, height: 40 }}\n />\n )}\n <div className=\"ald-skeleton-content tw-flex-1\">\n {title && (\n <div\n className={cn(\n 'ald-skeleton-title tw-mb-4 tw-h-4 tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n round ? 'tw-rounded-full' : 'tw-rounded-r-25',\n )}\n style={{\n width: typeof title === 'object' ? title.width : '38%',\n }}\n />\n )}\n {paragraph &&\n Array.from({ length: rows }).map((_, i) => (\n <div\n key={i}\n className={cn(\n 'ald-skeleton-paragraph tw-mb-3 tw-h-4 tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n round ? 'tw-rounded-full' : 'tw-rounded-r-25',\n )}\n style={{\n width: i === rows - 1 ? '61%' : '100%',\n }}\n />\n ))}\n </div>\n </div>\n );\n}\n\nfunction SkeletonNode({\n active,\n className,\n style,\n children,\n}: {\n active?: boolean;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n}) {\n return (\n <div\n className={cn(\n 'ald-skeleton-node tw-inline-flex tw-items-center tw-justify-center tw-rounded-r-25 tw-bg-[var(--background-neutral-subtle)]',\n active && 'tw-animate-pulse',\n className,\n )}\n style={style}\n >\n {children}\n </div>\n );\n}\n\n// Attach sub-components\nSkeleton.Button = SkeletonButton;\nSkeleton.Avatar = SkeletonAvatar;\nSkeleton.Input = SkeletonInput;\nSkeleton.Image = SkeletonImage;\nSkeleton.Node = SkeletonNode;\n\nexport default Skeleton;\n"],"mappings":";;;;AAsDA,IAAM,kBAGF;CACF,OAAO;EAAE,OAAO;EAAI,QAAQ;EAAI;CAChC,SAAS;EAAE,OAAO;EAAI,QAAQ;EAAI;CAClC,OAAO;EAAE,OAAO;EAAI,QAAQ;EAAI;CACjC;AAED,IAAM,kBAAuD;CAC3D,OAAO;CACP,SAAS;CACT,OAAO;CACR;AAED,IAAM,iBAAsD;CAC1D,OAAO;CACP,SAAS;CACT,OAAO;CACR;AAID,IAAM,kBAAiD,EACrD,QACA,OAAO,WACP,QAAQ,UACR,OACA,WACA,YACI;CACJ,MAAM,YAAY,SAAS,qBAAqB;CAChD,MAAM,OAAO,gBAAgB;CAE7B,MAAM,eACJ,UAAU,WACN,QACA,UAAU,UACV,GAAG,KAAK,SAAS,EAAE,MACnB;AAEN,QACE,oBAAC,QAAD;EACE,WAAW,GACT,gFACA,WACA,SAAS,sBACT,UACD;EACD,OAAO;GACL,OACE,UAAU,WAAW,KAAK,SAAS,QAAQ,SAAY,KAAK;GAC9D,QAAQ,KAAK;GACb;GACA,GAAG;GACJ;EACD,CAAA;;AAIN,IAAM,kBAAiD,EACrD,QACA,OAAO,WACP,QAAQ,UACR,WACA,YACI;CACJ,MAAM,YAAY,SAAS,qBAAqB;CAChD,MAAM,MAAM,OAAO,SAAS,WAAW,OAAO,gBAAgB;AAE9D,QACE,oBAAC,QAAD;EACE,WAAW,GACT,gFACA,WACA,UACD;EACD,OAAO;GACL,OAAO;GACP,QAAQ;GACR,cAAc,UAAU,WAAW,QAAQ;GAC3C,GAAG;GACJ;EACD,CAAA;;AAIN,IAAM,iBAA+C,EACnD,QACA,OAAO,WACP,OACA,WACA,YACI;CACJ,MAAM,YAAY,SAAS,qBAAqB;CAChD,MAAM,SAAS,eAAe;AAE9B,QACE,oBAAC,QAAD;EACE,WAAW,GACT,+EACA,WACA,SAAS,sBACT,UACD;EACD,OAAO;GACL,OAAO,QAAQ,SAAY;GAC3B;GACA,cAAc;GACd,GAAG;GACJ;EACD,CAAA;;AAIN,IAAM,iBAA+C,EACnD,QACA,WACA,YACI;AAGJ,QACE,oBAAC,QAAD;EACE,WAAW,GACT,yGALY,SAAS,qBAAqB,IAO1C,UACD;EACD,OAAO;GACL,OAAO;GACP,QAAQ;GACR,cAAc;GACd,GAAG;GACJ;YAED,oBAAC,OAAD;GACE,SAAQ;GACR,OAAM;GACN,OAAM;GACN,QAAO;GACP,MAAK;GACL,OAAO,EAAE,SAAS,IAAK;aAEvB,oBAAC,QAAD,EAAM,GAAE,mlBAAolB,CAAA;GACxlB,CAAA;EACD,CAAA;;AAMX,SAAS,SAAS,OAAsB;CACtC,MAAM,EACJ,SAAS,OACT,UAAU,MACV,WACA,OACA,UACA,QACA,QAAQ,MACR,YAAY,MACZ,UACE;AAEJ,KAAI,CAAC,QACH,QAAO,oBAAA,UAAA,EAAG,UAAY,CAAA;CAGxB,MAAM,YAAY,SAAS,qBAAqB;CAChD,MAAM,OACJ,OAAO,cAAc,WAAW,UAAU,QAAQ,IAAI,YAAY,IAAI;AAExE,QACE,qBAAC,OAAD;EACE,WAAW,GAAG,iCAAiC,UAAU;EAClD;YAFT,CAIG,UACC,oBAAC,OAAD;GACE,WAAW,GACT,4FACA,WACA,OAAO,WAAW,YAAY,OAAO,UAAU,WAC3C,oBACA,kBACL;GACD,OAAO;IAAE,OAAO;IAAI,QAAQ;IAAI;GAChC,CAAA,EAEJ,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,SACC,oBAAC,OAAD;IACE,WAAW,GACT,8EACA,WACA,QAAQ,oBAAoB,kBAC7B;IACD,OAAO,EACL,OAAO,OAAO,UAAU,WAAW,MAAM,QAAQ,OAClD;IACD,CAAA,EAEH,aACC,MAAM,KAAK,EAAE,QAAQ,MAAM,CAAC,CAAC,KAAK,GAAG,MACnC,oBAAC,OAAD;IAEE,WAAW,GACT,kFACA,WACA,QAAQ,oBAAoB,kBAC7B;IACD,OAAO,EACL,OAAO,MAAM,OAAO,IAAI,QAAQ,QACjC;IACD,EATK,EASL,CACF,CACA;KACF;;;AAIV,SAAS,aAAa,EACpB,QACA,WACA,OACA,YAMC;AACD,QACE,oBAAC,OAAD;EACE,WAAW,GACT,+HACA,UAAU,oBACV,UACD;EACM;EAEN;EACG,CAAA;;AAKV,SAAS,SAAS;AAClB,SAAS,SAAS;AAClB,SAAS,QAAQ;AACjB,SAAS,QAAQ;AACjB,SAAS,OAAO"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/Skeleton/index.tsx"],"sourcesContent":["import React from 'react';\nimport { cn } from '../lib/utils';\n\nexport interface SkeletonProps {\n active?: boolean;\n loading?: boolean;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n avatar?:\n | boolean\n | {\n size?: number | 'small' | 'default' | 'large';\n shape?: 'circle' | 'square';\n };\n title?: boolean | { width?: number | string };\n paragraph?:\n | boolean\n | { rows?: number; width?: number | string | (number | string)[] };\n round?: boolean;\n}\n\n// --- Shared types for sub-components ---\n\ntype SkeletonElementSize = 'small' | 'default' | 'large';\n\ninterface SkeletonElementBaseProps {\n active?: boolean;\n size?: SkeletonElementSize;\n className?: string;\n style?: React.CSSProperties;\n}\n\ninterface SkeletonButtonProps extends SkeletonElementBaseProps {\n shape?: 'circle' | 'round' | 'square';\n block?: boolean;\n}\n\ninterface SkeletonAvatarProps extends SkeletonElementBaseProps {\n shape?: 'circle' | 'square';\n}\n\ninterface SkeletonInputProps extends SkeletonElementBaseProps {\n block?: boolean;\n}\n\ninterface SkeletonImageProps {\n active?: boolean;\n className?: string;\n style?: React.CSSProperties;\n}\n\n// --- Size maps ---\n\nconst BUTTON_SIZE_MAP: Record<\n SkeletonElementSize,\n { width: number; height: number }\n> = {\n small: { width: 64, height: 24 },\n default: { width: 80, height: 32 },\n large: { width: 96, height: 40 },\n};\n\nconst AVATAR_SIZE_MAP: Record<SkeletonElementSize, number> = {\n small: 24,\n default: 32,\n large: 40,\n};\n\nconst INPUT_SIZE_MAP: Record<SkeletonElementSize, number> = {\n small: 24,\n default: 32,\n large: 40,\n};\n\nconst SKELETON_BACKGROUND_COLOR =\n 'var(--alias-colors-bg-skeleton-subtle, var(--background-neutral-subtle, #e5e7eb))';\n\n// --- Sub-components ---\n\nconst SkeletonButton: React.FC<SkeletonButtonProps> = ({\n active,\n size = 'default',\n shape = 'square',\n block,\n className,\n style,\n}) => {\n const animClass = active ? 'tw-animate-pulse' : '';\n const dims = BUTTON_SIZE_MAP[size];\n\n const borderRadius =\n shape === 'circle'\n ? '50%'\n : shape === 'round'\n ? `${dims.height / 2}px`\n : '4px';\n\n return (\n <span\n className={cn(\n 'ald-skeleton-button tw-inline-block tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n block && 'tw-block tw-w-full',\n className,\n )}\n style={{\n backgroundColor: SKELETON_BACKGROUND_COLOR,\n width:\n shape === 'circle' ? dims.height : block ? undefined : dims.width,\n height: dims.height,\n borderRadius,\n ...style,\n }}\n />\n );\n};\n\nconst SkeletonAvatar: React.FC<SkeletonAvatarProps> = ({\n active,\n size = 'default',\n shape = 'circle',\n className,\n style,\n}) => {\n const animClass = active ? 'tw-animate-pulse' : '';\n const dim = typeof size === 'number' ? size : AVATAR_SIZE_MAP[size];\n\n return (\n <span\n className={cn(\n 'ald-skeleton-avatar tw-inline-block tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n className,\n )}\n style={{\n backgroundColor: SKELETON_BACKGROUND_COLOR,\n width: dim,\n height: dim,\n borderRadius: shape === 'circle' ? '50%' : '4px',\n ...style,\n }}\n />\n );\n};\n\nconst SkeletonInput: React.FC<SkeletonInputProps> = ({\n active,\n size = 'default',\n block,\n className,\n style,\n}) => {\n const animClass = active ? 'tw-animate-pulse' : '';\n const height = INPUT_SIZE_MAP[size];\n\n return (\n <span\n className={cn(\n 'ald-skeleton-input tw-inline-block tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n block && 'tw-block tw-w-full',\n className,\n )}\n style={{\n backgroundColor: SKELETON_BACKGROUND_COLOR,\n width: block ? undefined : 200,\n height,\n borderRadius: 4,\n ...style,\n }}\n />\n );\n};\n\nconst SkeletonImage: React.FC<SkeletonImageProps> = ({\n active,\n className,\n style,\n}) => {\n const animClass = active ? 'tw-animate-pulse' : '';\n\n return (\n <span\n className={cn(\n 'ald-skeleton-image tw-flex tw-items-center tw-justify-center tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n className,\n )}\n style={{\n backgroundColor: SKELETON_BACKGROUND_COLOR,\n width: 96,\n height: 96,\n borderRadius: 4,\n ...style,\n }}\n >\n <svg\n viewBox=\"0 0 1098 1024\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"48\"\n height=\"48\"\n fill=\"currentColor\"\n style={{ opacity: 0.2 }}\n >\n <path d=\"M365.714 329.143q0 45.714-32.036 77.75t-77.75 32.036-77.75-32.036-32.036-77.75 32.036-77.75 77.75-32.036 77.75 32.036 32.036 77.75zM950.857 548.571v256H146.286V694.857l182.857-182.857 91.429 91.429 292.571-292.571zM1005.714 146.286H91.429q-7.46 0-12.873 5.413T73.143 164.571v694.857q0 7.46 5.413 12.873t12.873 5.413h914.286q7.46 0 12.873-5.413t5.413-12.873V164.571q0-7.46-5.413-12.873t-12.873-5.413zm91.429 18.286v694.857q0 37.714-26.857 64.571t-64.571 26.857H91.429q-37.714 0-64.571-26.857T0 859.429V164.571q0-37.714 26.857-64.571T91.429 73.143h914.286q37.714 0 64.571 26.857t26.857 64.571z\" />\n </svg>\n </span>\n );\n};\n\n// --- Main Skeleton component ---\n\nfunction Skeleton(props: SkeletonProps) {\n const {\n active = false,\n loading = true,\n className,\n style,\n children,\n avatar,\n title = true,\n paragraph = true,\n round,\n } = props;\n\n if (!loading) {\n return <>{children}</>;\n }\n\n const animClass = active ? 'tw-animate-pulse' : '';\n const rows =\n typeof paragraph === 'object' ? paragraph.rows || 3 : paragraph ? 3 : 0;\n\n return (\n <div\n className={cn('ald-skeleton tw-flex tw-gap-4', className)}\n style={style}\n >\n {avatar && (\n <div\n className={cn(\n 'ald-skeleton-avatar tw-shrink-0 tw-rounded-full tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n typeof avatar === 'object' && avatar.shape === 'square'\n ? 'tw-rounded-r-50'\n : 'tw-rounded-full',\n )}\n style={{\n backgroundColor: SKELETON_BACKGROUND_COLOR,\n width: 40,\n height: 40,\n }}\n />\n )}\n <div className=\"ald-skeleton-content tw-flex-1\">\n {title && (\n <div\n className={cn(\n 'ald-skeleton-title tw-mb-4 tw-h-4 tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n round ? 'tw-rounded-full' : 'tw-rounded-r-25',\n )}\n style={{\n backgroundColor: SKELETON_BACKGROUND_COLOR,\n width: typeof title === 'object' ? title.width : '38%',\n }}\n />\n )}\n {paragraph &&\n Array.from({ length: rows }).map((_, i) => (\n <div\n key={i}\n className={cn(\n 'ald-skeleton-paragraph tw-mb-3 tw-h-4 tw-bg-[var(--background-neutral-subtle)]',\n animClass,\n round ? 'tw-rounded-full' : 'tw-rounded-r-25',\n )}\n style={{\n backgroundColor: SKELETON_BACKGROUND_COLOR,\n width: i === rows - 1 ? '61%' : '100%',\n }}\n />\n ))}\n </div>\n </div>\n );\n}\n\nfunction SkeletonNode({\n active,\n className,\n style,\n children,\n}: {\n active?: boolean;\n className?: string;\n style?: React.CSSProperties;\n children?: React.ReactNode;\n}) {\n return (\n <div\n className={cn(\n 'ald-skeleton-node tw-inline-flex tw-items-center tw-justify-center tw-rounded-r-25 tw-bg-[var(--background-neutral-subtle)]',\n active && 'tw-animate-pulse',\n className,\n )}\n style={{\n backgroundColor: SKELETON_BACKGROUND_COLOR,\n ...style,\n }}\n >\n {children}\n </div>\n );\n}\n\n// Attach sub-components\nSkeleton.Button = SkeletonButton;\nSkeleton.Avatar = SkeletonAvatar;\nSkeleton.Input = SkeletonInput;\nSkeleton.Image = SkeletonImage;\nSkeleton.Node = SkeletonNode;\n\nexport default Skeleton;\n"],"mappings":"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"}
|
package/dist/Tabs/index.js
CHANGED
|
@@ -99,7 +99,7 @@ function Tabs(props) {
|
|
|
99
99
|
"--tabs-padding": `${paddingVal}px`
|
|
100
100
|
},
|
|
101
101
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
102
|
-
className: cn("ald-tabs-nav ant-tabs-nav tw-flex tw-items-center", isTop && "tw-mb-5", isBottom && "tw-mt-5", isTop && hasDividing && "tw-border-0 tw-border-b tw-border-solid tw-border-b-[var(--global-cool-grey-100)]", isBottom && hasDividing && "tw-border-0 tw-border-t tw-border-solid tw-border-t-[var(--global-cool-grey-100)]", isTop && compact && "!tw-mb-0", isBottom && compact && "!tw-mt-0", isLeft && "tw-flex-col tw-border-0 tw-border-r tw-border-solid tw-border-r-[var(--global-cool-grey-100)]", isRight && "tw-flex-col tw-border-0 tw-border-l tw-border-solid tw-border-l-[var(--global-cool-grey-100)]"),
|
|
102
|
+
className: cn("ald-tabs-nav ant-tabs-nav tw-flex tw-items-center", isVertical && "tw-shrink-0", isTop && "tw-mb-5", isBottom && "tw-mt-5", isTop && hasDividing && "tw-border-0 tw-border-b tw-border-solid tw-border-b-[var(--global-cool-grey-100)]", isBottom && hasDividing && "tw-border-0 tw-border-t tw-border-solid tw-border-t-[var(--global-cool-grey-100)]", isTop && compact && "!tw-mb-0", isBottom && compact && "!tw-mt-0", isLeft && "tw-flex-col tw-border-0 tw-border-r tw-border-solid tw-border-r-[var(--global-cool-grey-100)]", isRight && "tw-flex-col tw-border-0 tw-border-l tw-border-solid tw-border-l-[var(--global-cool-grey-100)]"),
|
|
103
103
|
style: headerBackgroundColor || tabBarStyle ? {
|
|
104
104
|
...tabBarStyle,
|
|
105
105
|
...headerBackgroundColor ? { backgroundColor: headerBackgroundColor } : {}
|
|
@@ -114,11 +114,11 @@ function Tabs(props) {
|
|
|
114
114
|
children: /* @__PURE__ */ jsx(Memo, { size: 16 })
|
|
115
115
|
}),
|
|
116
116
|
/* @__PURE__ */ jsx("div", {
|
|
117
|
-
className: "ald-tabs-nav-wrap ant-tabs-nav-wrap tw-min-w-0 tw-flex-1",
|
|
117
|
+
className: cn("ald-tabs-nav-wrap ant-tabs-nav-wrap", isVertical ? "tw-w-full tw-shrink-0" : "tw-min-w-0 tw-flex-1"),
|
|
118
118
|
style: paddingVal ? { margin: `0 ${paddingVal}px` } : void 0,
|
|
119
119
|
children: /* @__PURE__ */ jsx("div", {
|
|
120
120
|
ref: navListRef,
|
|
121
|
-
className: cn("ald-tabs-nav-list ant-tabs-nav-list tw-flex tw-overflow-hidden", isVertical && "tw-flex-col", centered && !isVertical && "tw-justify-center", monospace && "[&>*]:tw-flex-1", isCard ? "tw-gap-1" : !monospace && "tw-gap-8"),
|
|
121
|
+
className: cn("ald-tabs-nav-list ant-tabs-nav-list tw-flex tw-overflow-hidden", isVertical && "tw-w-full tw-shrink-0 tw-flex-col", centered && !isVertical && "tw-justify-center", monospace && "[&>*]:tw-flex-1", isCard ? "tw-gap-1" : !monospace && "tw-gap-8"),
|
|
122
122
|
style: !isVertical ? {
|
|
123
123
|
scrollbarWidth: "none",
|
|
124
124
|
msOverflowStyle: "none"
|
|
@@ -127,7 +127,7 @@ function Tabs(props) {
|
|
|
127
127
|
const isActive = activeKey === item.key;
|
|
128
128
|
const showClose = isEditable && item.closable !== false;
|
|
129
129
|
return /* @__PURE__ */ jsxs("div", {
|
|
130
|
-
className: cn("ald-tabs-tab ant-tabs-tab tw-relative tw-flex tw-shrink-0 tw-cursor-pointer tw-items-center tw-gap-2 tw-whitespace-nowrap tw-transition-colors", isCard ? cn("tw-h-10 tw-rounded-t-[6px] tw-border tw-border-b-0 tw-border-solid tw-px-4 tw-text-sm tw-leading-5", "tw-gap-sp-75", isActive ? "tw-border-[var(--alias-colors-border-default)] tw-bg-[var(--background-default)] tw-font-medium tw-text-[var(--alias-colors-text-selected)]" : "tw-border-[var(--alias-colors-border-default)] tw-bg-[var(--alias-colors-bg-skeleton-subtler)] tw-text-[var(--alias-colors-text-subtle)] hover:tw-text-inherit") : cn(size === "small" ? "tw-py-2 tw-text-xs tw-leading-4" : "tw-py-2.5 tw-text-sm tw-leading-5", monospace && "tw-justify-center", isActive ? "tw-font-medium tw-text-[var(--alias-colors-text-selected)]" : "tw-font-medium tw-text-[var(--alias-colors-text-subtle)] hover:tw-text-inherit"), item.disabled && "tw-pointer-events-none tw-cursor-default tw-opacity-50"),
|
|
130
|
+
className: cn("ald-tabs-tab ant-tabs-tab tw-relative tw-flex tw-shrink-0 tw-cursor-pointer tw-items-center tw-gap-2 tw-whitespace-nowrap tw-transition-colors", isVertical && "tw-w-full", isCard ? cn("tw-h-10 tw-rounded-t-[6px] tw-border tw-border-b-0 tw-border-solid tw-px-4 tw-text-sm tw-leading-5", "tw-gap-sp-75", isActive ? "tw-border-[var(--alias-colors-border-default)] tw-bg-[var(--background-default)] tw-font-medium tw-text-[var(--alias-colors-text-selected)]" : "tw-border-[var(--alias-colors-border-default)] tw-bg-[var(--alias-colors-bg-skeleton-subtler)] tw-text-[var(--alias-colors-text-subtle)] hover:tw-text-inherit") : cn(size === "small" ? "tw-py-2 tw-text-xs tw-leading-4" : "tw-py-2.5 tw-text-sm tw-leading-5", monospace && "tw-justify-center", isActive ? "tw-font-medium tw-text-[var(--alias-colors-text-selected)]" : "tw-font-medium tw-text-[var(--alias-colors-text-subtle)] hover:tw-text-inherit"), item.disabled && "tw-pointer-events-none tw-cursor-default tw-opacity-50"),
|
|
131
131
|
onClick: item.disabled ? void 0 : (e) => handleTabClick(item.key, e),
|
|
132
132
|
children: [
|
|
133
133
|
item.icon,
|
package/dist/Tabs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/Tabs/index.tsx"],"sourcesContent":["import React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { cn } from '../lib/utils';\nimport { ChevronLeftLine, ChevronRightLine, CloseLightLine } from '../Icon';\nimport TabPane from './TabPane';\n\nexport type TabsSize = 'default' | 'small';\n\ninterface TabItem {\n key: string;\n label: React.ReactNode;\n children?: React.ReactNode;\n disabled?: boolean;\n closable?: boolean;\n icon?: React.ReactNode;\n forceRender?: boolean;\n className?: string;\n}\n\nexport interface ITabsProps {\n size?: TabsSize;\n activeKey?: string;\n defaultActiveKey?: string;\n onChange?: (activeKey: string) => void;\n onTabClick?: (activeKey: string, e: React.MouseEvent) => void;\n destroyInactiveTabPane?: boolean;\n centered?: boolean;\n adaptHeight?: boolean;\n tabPosition?: 'left' | 'right' | 'top' | 'bottom';\n className?: string;\n children?: React.ReactNode;\n headerBackgroundColor?: string;\n monospace?: boolean;\n padding?: boolean | number;\n compact?: boolean;\n hasDividing?: boolean;\n items?: TabItem[];\n type?: 'line' | 'card' | 'editable-card';\n tabBarExtraContent?:\n | React.ReactNode\n | { left?: React.ReactNode; right?: React.ReactNode };\n onEdit?: (\n targetKey: string | React.MouseEvent | React.KeyboardEvent,\n action: 'add' | 'remove',\n ) => void;\n hideAdd?: boolean;\n style?: React.CSSProperties;\n tabBarStyle?: React.CSSProperties;\n popupClassName?: string;\n tabBarGutter?: number;\n moreIcon?: React.ReactNode;\n /** 透传到 Tabs 根容器的 data-testid,用于自动化测试定位 */\n 'data-testid'?: string;\n /** 透传到 Tabs 根容器的 aria-label,用于可访问性与自动化测试定位 */\n 'aria-label'?: string;\n}\n\nexport default function Tabs(props: ITabsProps) {\n const {\n size,\n className,\n adaptHeight,\n style = {},\n monospace: propsMonospace,\n tabPosition = 'top',\n padding: propsPadding = false,\n compact,\n hasDividing = true,\n items,\n activeKey: controlledActiveKey,\n defaultActiveKey,\n onChange,\n onTabClick,\n destroyInactiveTabPane,\n centered,\n tabBarExtraContent,\n headerBackgroundColor,\n children,\n type,\n onEdit,\n hideAdd,\n tabBarStyle,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n } = props;\n\n const isEditable = type === 'editable-card';\n const isCard = type === 'card' || type === 'editable-card';\n\n // Derive items from children if not provided\n const resolvedItems: TabItem[] = useMemo(() => {\n if (items) return items;\n return React.Children.toArray(children)\n .filter(React.isValidElement)\n .map((child: any) => ({\n key: child.key || child.props.key || '',\n label: child.props.tab,\n children: child.props.children,\n disabled: child.props.disabled,\n closable: child.props.closable,\n forceRender: child.props.forceRender,\n }));\n }, [items, children]);\n\n const firstKey = resolvedItems[0]?.key || '';\n const [innerActiveKey, setInnerActiveKey] = useState(\n defaultActiveKey || firstKey,\n );\n const mergedActiveKey =\n controlledActiveKey !== undefined ? controlledActiveKey : innerActiveKey;\n const activeKey = resolvedItems.some((item) => item.key === mergedActiveKey)\n ? mergedActiveKey\n : firstKey;\n\n const handleTabClick = (key: string, e: React.MouseEvent) => {\n if (controlledActiveKey === undefined) {\n setInnerActiveKey(key);\n }\n onChange?.(key);\n onTabClick?.(key, e);\n };\n\n const handleRemove = (key: string, e: React.MouseEvent) => {\n e.stopPropagation();\n onEdit?.(key, 'remove');\n };\n\n const handleAdd = (e: React.MouseEvent) => {\n onEdit?.(e, 'add');\n };\n\n const monospace = tabPosition !== 'top' ? false : propsMonospace;\n const paddingVal = useMemo(() => {\n if (tabPosition !== 'top') return 0;\n if (typeof propsPadding === 'number') return propsPadding;\n if (typeof propsPadding === 'boolean' && propsPadding) return 20;\n return 0;\n }, [propsPadding, tabPosition]);\n\n const isTop = tabPosition === 'top';\n const isBottom = tabPosition === 'bottom';\n const isLeft = tabPosition === 'left';\n const isRight = tabPosition === 'right';\n const isVertical = tabPosition === 'left' || tabPosition === 'right';\n const rootDirectionClass = isRight\n ? 'tw-flex-row-reverse'\n : isLeft\n ? 'tw-flex-row'\n : isBottom\n ? 'tw-flex-col-reverse'\n : 'tw-flex-col';\n\n const extraLeft =\n tabBarExtraContent &&\n typeof tabBarExtraContent === 'object' &&\n 'left' in tabBarExtraContent\n ? tabBarExtraContent.left\n : null;\n const extraRight = tabBarExtraContent\n ? typeof tabBarExtraContent === 'object' && 'right' in tabBarExtraContent\n ? tabBarExtraContent.right\n : React.isValidElement(tabBarExtraContent)\n ? tabBarExtraContent\n : null\n : null;\n\n // Scroll state for overflow\n const navListRef = useRef<HTMLDivElement>(null);\n const [canScrollLeft, setCanScrollLeft] = useState(false);\n const [canScrollRight, setCanScrollRight] = useState(false);\n\n const checkScroll = useCallback(() => {\n const el = navListRef.current;\n if (!el || isVertical) {\n setCanScrollLeft(false);\n setCanScrollRight(false);\n return;\n }\n const { scrollLeft, scrollWidth, clientWidth } = el;\n setCanScrollLeft(scrollLeft > 1);\n setCanScrollRight(scrollLeft + clientWidth < scrollWidth - 1);\n }, [isVertical]);\n\n useEffect(() => {\n checkScroll();\n const el = navListRef.current;\n if (!el) return;\n // Use ResizeObserver to detect size changes\n let ro: ResizeObserver | undefined;\n if (typeof ResizeObserver !== 'undefined') {\n ro = new ResizeObserver(() => checkScroll());\n ro.observe(el);\n }\n el.addEventListener('scroll', checkScroll, { passive: true });\n return () => {\n el.removeEventListener('scroll', checkScroll);\n ro?.disconnect();\n };\n }, [checkScroll, resolvedItems.length]);\n\n const scrollBy = (delta: number) => {\n const el = navListRef.current;\n if (el) {\n el.scrollBy({ left: delta, behavior: 'smooth' });\n }\n };\n\n const showScrollButtons = canScrollLeft || canScrollRight;\n\n return (\n <div\n data-testid={dataTestId}\n aria-label={ariaLabel}\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-tabs ant-tabs tw-flex',\n rootDirectionClass,\n adaptHeight && 'ald-adapt-height tw-h-full',\n size !== 'small' && 'ald-tabs-default',\n monospace && 'ald-tabs-monospace',\n compact && 'ald-tabs-compact',\n !hasDividing && 'ald-tabs-no-dividing',\n className,\n )}\n style={\n {\n ...style,\n '--header-bg-color': headerBackgroundColor,\n '--tabs-padding': `${paddingVal}px`,\n } as React.CSSProperties\n }\n >\n {/* Tab nav */}\n <div\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-tabs-nav ant-tabs-nav tw-flex tw-items-center',\n isTop && 'tw-mb-5',\n isBottom && 'tw-mt-5',\n isTop &&\n hasDividing &&\n 'tw-border-0 tw-border-b tw-border-solid tw-border-b-[var(--global-cool-grey-100)]',\n isBottom &&\n hasDividing &&\n 'tw-border-0 tw-border-t tw-border-solid tw-border-t-[var(--global-cool-grey-100)]',\n isTop && compact && '!tw-mb-0',\n isBottom && compact && '!tw-mt-0',\n isLeft &&\n 'tw-flex-col tw-border-0 tw-border-r tw-border-solid tw-border-r-[var(--global-cool-grey-100)]',\n isRight &&\n 'tw-flex-col tw-border-0 tw-border-l tw-border-solid tw-border-l-[var(--global-cool-grey-100)]',\n )}\n style={\n headerBackgroundColor || tabBarStyle\n ? {\n ...tabBarStyle,\n ...(headerBackgroundColor\n ? { backgroundColor: headerBackgroundColor }\n : {}),\n }\n : undefined\n }\n >\n {extraLeft}\n\n {/* Scroll left button */}\n {showScrollButtons && !isVertical && (\n <button\n type=\"button\"\n className={cn(\n 'ald-tabs-scroll-btn tw-flex tw-shrink-0 tw-items-center tw-justify-center tw-border-none tw-bg-transparent tw-p-1 tw-text-[var(--content-secondary)] tw-transition-colors hover:tw-text-[var(--content-primary)]',\n !canScrollLeft && 'tw-invisible',\n )}\n onClick={() => scrollBy(-200)}\n aria-label=\"Scroll tabs left\"\n >\n <ChevronLeftLine size={16} />\n </button>\n )}\n\n <div\n className=\"ald-tabs-nav-wrap ant-tabs-nav-wrap tw-min-w-0 tw-flex-1\"\n style={paddingVal ? { margin: `0 ${paddingVal}px` } : undefined}\n >\n <div\n ref={navListRef}\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-tabs-nav-list ant-tabs-nav-list tw-flex tw-overflow-hidden',\n isVertical && 'tw-flex-col',\n centered && !isVertical && 'tw-justify-center',\n monospace && '[&>*]:tw-flex-1',\n isCard ? 'tw-gap-1' : !monospace && 'tw-gap-8',\n )}\n style={\n !isVertical\n ? { scrollbarWidth: 'none', msOverflowStyle: 'none' }\n : undefined\n }\n >\n {resolvedItems.map((item) => {\n const isActive = activeKey === item.key;\n // For editable-card, closable defaults to true unless explicitly set to false\n const showClose = isEditable && item.closable !== false;\n\n return (\n <div\n key={item.key}\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-tabs-tab ant-tabs-tab tw-relative tw-flex tw-shrink-0 tw-cursor-pointer tw-items-center tw-gap-2 tw-whitespace-nowrap tw-transition-colors',\n // Card / editable-card styling\n isCard\n ? cn(\n 'tw-h-10 tw-rounded-t-[6px] tw-border tw-border-b-0 tw-border-solid tw-px-4 tw-text-sm tw-leading-5',\n 'tw-gap-sp-75',\n isActive\n ? 'tw-border-[var(--alias-colors-border-default)] tw-bg-[var(--background-default)] tw-font-medium tw-text-[var(--alias-colors-text-selected)]'\n : 'tw-border-[var(--alias-colors-border-default)] tw-bg-[var(--alias-colors-bg-skeleton-subtler)] tw-text-[var(--alias-colors-text-subtle)] hover:tw-text-inherit',\n )\n : cn(\n size === 'small'\n ? 'tw-py-2 tw-text-xs tw-leading-4'\n : 'tw-py-2.5 tw-text-sm tw-leading-5',\n monospace && 'tw-justify-center',\n isActive\n ? 'tw-font-medium tw-text-[var(--alias-colors-text-selected)]'\n : 'tw-font-medium tw-text-[var(--alias-colors-text-subtle)] hover:tw-text-inherit',\n ),\n item.disabled &&\n 'tw-pointer-events-none tw-cursor-default tw-opacity-50',\n )}\n onClick={\n item.disabled\n ? undefined\n : (e) => handleTabClick(item.key, e)\n }\n >\n {item.icon}\n {/* antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器 */}\n <span className=\"ant-tabs-tab-btn\">{item.label}</span>\n {/* Close button for editable-card */}\n {showClose && (\n <span\n className=\"ald-tabs-tab-remove tw-m-0 tw-grid tw-size-4 tw-cursor-pointer tw-place-items-center tw-p-0 tw-text-[var(--alias-colors-icon-subtle)] tw-transition-colors hover:tw-text-[var(--alias-colors-text-default)]\"\n onClick={(e) => handleRemove(item.key, e)}\n aria-label={`Remove ${item.label}`}\n >\n <CloseLightLine size={12} />\n </span>\n )}\n {/* Active indicator for non-card line tabs */}\n {/* antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器 */}\n {!isCard && isActive && isTop && (\n <div className=\"ant-tabs-ink-bar tw-absolute tw-inset-x-0 tw--bottom-px tw-h-[2px] tw-rounded-[2px] tw-bg-[var(--alias-colors-border-selected)]\" />\n )}\n {!isCard && isActive && isBottom && (\n <div className=\"ant-tabs-ink-bar tw-absolute tw-inset-x-0 tw--top-px tw-h-[2px] tw-rounded-[2px] tw-bg-[var(--alias-colors-border-selected)]\" />\n )}\n {!isCard && isActive && isLeft && (\n <div className=\"tw-absolute tw-inset-y-0 tw--right-px tw-w-[2px] tw-rounded-[2px] tw-bg-[var(--alias-colors-border-selected)]\" />\n )}\n {!isCard && isActive && isRight && (\n <div className=\"tw-absolute tw-inset-y-0 tw--left-px tw-w-[2px] tw-rounded-[2px] tw-bg-[var(--alias-colors-border-selected)]\" />\n )}\n </div>\n );\n })}\n </div>\n </div>\n\n {/* Scroll right button */}\n {showScrollButtons && !isVertical && (\n <button\n type=\"button\"\n className={cn(\n 'ald-tabs-scroll-btn tw-flex tw-shrink-0 tw-items-center tw-justify-center tw-border-none tw-bg-transparent tw-p-1 tw-text-[var(--content-secondary)] tw-transition-colors hover:tw-text-[var(--content-primary)]',\n !canScrollRight && 'tw-invisible',\n )}\n onClick={() => scrollBy(200)}\n aria-label=\"Scroll tabs right\"\n >\n <ChevronRightLine size={16} />\n </button>\n )}\n\n {/* Add button for editable-card */}\n {isEditable && !hideAdd && (\n <button\n type=\"button\"\n className=\"ald-tabs-nav-add tw-ml-1 tw-flex tw-shrink-0 tw-cursor-pointer tw-items-center tw-justify-center tw-rounded tw-border tw-border-solid tw-border-[var(--border-default)] tw-bg-[var(--background-default)] tw-px-2 tw-py-1 tw-text-[var(--content-secondary)] tw-transition-colors hover:tw-text-[var(--content-primary)]\"\n onClick={handleAdd}\n aria-label=\"Add tab\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n </button>\n )}\n\n {extraRight}\n </div>\n\n {/* Tab content */}\n {/* antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器 */}\n <div\n className={cn(\n 'ald-tabs-content ant-tabs-content ant-tabs-content-holder tw-min-h-0 tw-flex-1',\n adaptHeight && 'tw-h-full',\n )}\n style={\n paddingVal\n ? { padding: `0 ${paddingVal}px ${paddingVal}px` }\n : undefined\n }\n >\n {resolvedItems.map((item) => {\n const isActive = item.key === activeKey;\n if (!isActive && destroyInactiveTabPane && !item.forceRender) {\n return null;\n }\n return (\n <div\n key={item.key}\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-tabs-tabpane ant-tabs-tabpane',\n // v2 行为:adaptHeight 时 tabpane 撑满内容区,子元素的百分比高度才能解析\n adaptHeight && 'tw-h-full tw-overflow-y-auto',\n isActive ? 'ant-tabs-tabpane-active tw-block' : 'tw-hidden',\n )}\n role=\"tabpanel\"\n >\n {item.children}\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n\nTabs.TabPane = TabPane;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/Tabs/index.tsx"],"sourcesContent":["import React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { cn } from '../lib/utils';\nimport { ChevronLeftLine, ChevronRightLine, CloseLightLine } from '../Icon';\nimport TabPane from './TabPane';\n\nexport type TabsSize = 'default' | 'small';\n\ninterface TabItem {\n key: string;\n label: React.ReactNode;\n children?: React.ReactNode;\n disabled?: boolean;\n closable?: boolean;\n icon?: React.ReactNode;\n forceRender?: boolean;\n className?: string;\n}\n\nexport interface ITabsProps {\n size?: TabsSize;\n activeKey?: string;\n defaultActiveKey?: string;\n onChange?: (activeKey: string) => void;\n onTabClick?: (activeKey: string, e: React.MouseEvent) => void;\n destroyInactiveTabPane?: boolean;\n centered?: boolean;\n adaptHeight?: boolean;\n tabPosition?: 'left' | 'right' | 'top' | 'bottom';\n className?: string;\n children?: React.ReactNode;\n headerBackgroundColor?: string;\n monospace?: boolean;\n padding?: boolean | number;\n compact?: boolean;\n hasDividing?: boolean;\n items?: TabItem[];\n type?: 'line' | 'card' | 'editable-card';\n tabBarExtraContent?:\n | React.ReactNode\n | { left?: React.ReactNode; right?: React.ReactNode };\n onEdit?: (\n targetKey: string | React.MouseEvent | React.KeyboardEvent,\n action: 'add' | 'remove',\n ) => void;\n hideAdd?: boolean;\n style?: React.CSSProperties;\n tabBarStyle?: React.CSSProperties;\n popupClassName?: string;\n tabBarGutter?: number;\n moreIcon?: React.ReactNode;\n /** 透传到 Tabs 根容器的 data-testid,用于自动化测试定位 */\n 'data-testid'?: string;\n /** 透传到 Tabs 根容器的 aria-label,用于可访问性与自动化测试定位 */\n 'aria-label'?: string;\n}\n\nexport default function Tabs(props: ITabsProps) {\n const {\n size,\n className,\n adaptHeight,\n style = {},\n monospace: propsMonospace,\n tabPosition = 'top',\n padding: propsPadding = false,\n compact,\n hasDividing = true,\n items,\n activeKey: controlledActiveKey,\n defaultActiveKey,\n onChange,\n onTabClick,\n destroyInactiveTabPane,\n centered,\n tabBarExtraContent,\n headerBackgroundColor,\n children,\n type,\n onEdit,\n hideAdd,\n tabBarStyle,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n } = props;\n\n const isEditable = type === 'editable-card';\n const isCard = type === 'card' || type === 'editable-card';\n\n // Derive items from children if not provided\n const resolvedItems: TabItem[] = useMemo(() => {\n if (items) return items;\n return React.Children.toArray(children)\n .filter(React.isValidElement)\n .map((child: any) => ({\n key: child.key || child.props.key || '',\n label: child.props.tab,\n children: child.props.children,\n disabled: child.props.disabled,\n closable: child.props.closable,\n forceRender: child.props.forceRender,\n }));\n }, [items, children]);\n\n const firstKey = resolvedItems[0]?.key || '';\n const [innerActiveKey, setInnerActiveKey] = useState(\n defaultActiveKey || firstKey,\n );\n const mergedActiveKey =\n controlledActiveKey !== undefined ? controlledActiveKey : innerActiveKey;\n const activeKey = resolvedItems.some((item) => item.key === mergedActiveKey)\n ? mergedActiveKey\n : firstKey;\n\n const handleTabClick = (key: string, e: React.MouseEvent) => {\n if (controlledActiveKey === undefined) {\n setInnerActiveKey(key);\n }\n onChange?.(key);\n onTabClick?.(key, e);\n };\n\n const handleRemove = (key: string, e: React.MouseEvent) => {\n e.stopPropagation();\n onEdit?.(key, 'remove');\n };\n\n const handleAdd = (e: React.MouseEvent) => {\n onEdit?.(e, 'add');\n };\n\n const monospace = tabPosition !== 'top' ? false : propsMonospace;\n const paddingVal = useMemo(() => {\n if (tabPosition !== 'top') return 0;\n if (typeof propsPadding === 'number') return propsPadding;\n if (typeof propsPadding === 'boolean' && propsPadding) return 20;\n return 0;\n }, [propsPadding, tabPosition]);\n\n const isTop = tabPosition === 'top';\n const isBottom = tabPosition === 'bottom';\n const isLeft = tabPosition === 'left';\n const isRight = tabPosition === 'right';\n const isVertical = tabPosition === 'left' || tabPosition === 'right';\n const rootDirectionClass = isRight\n ? 'tw-flex-row-reverse'\n : isLeft\n ? 'tw-flex-row'\n : isBottom\n ? 'tw-flex-col-reverse'\n : 'tw-flex-col';\n\n const extraLeft =\n tabBarExtraContent &&\n typeof tabBarExtraContent === 'object' &&\n 'left' in tabBarExtraContent\n ? tabBarExtraContent.left\n : null;\n const extraRight = tabBarExtraContent\n ? typeof tabBarExtraContent === 'object' && 'right' in tabBarExtraContent\n ? tabBarExtraContent.right\n : React.isValidElement(tabBarExtraContent)\n ? tabBarExtraContent\n : null\n : null;\n\n // Scroll state for overflow\n const navListRef = useRef<HTMLDivElement>(null);\n const [canScrollLeft, setCanScrollLeft] = useState(false);\n const [canScrollRight, setCanScrollRight] = useState(false);\n\n const checkScroll = useCallback(() => {\n const el = navListRef.current;\n if (!el || isVertical) {\n setCanScrollLeft(false);\n setCanScrollRight(false);\n return;\n }\n const { scrollLeft, scrollWidth, clientWidth } = el;\n setCanScrollLeft(scrollLeft > 1);\n setCanScrollRight(scrollLeft + clientWidth < scrollWidth - 1);\n }, [isVertical]);\n\n useEffect(() => {\n checkScroll();\n const el = navListRef.current;\n if (!el) return;\n // Use ResizeObserver to detect size changes\n let ro: ResizeObserver | undefined;\n if (typeof ResizeObserver !== 'undefined') {\n ro = new ResizeObserver(() => checkScroll());\n ro.observe(el);\n }\n el.addEventListener('scroll', checkScroll, { passive: true });\n return () => {\n el.removeEventListener('scroll', checkScroll);\n ro?.disconnect();\n };\n }, [checkScroll, resolvedItems.length]);\n\n const scrollBy = (delta: number) => {\n const el = navListRef.current;\n if (el) {\n el.scrollBy({ left: delta, behavior: 'smooth' });\n }\n };\n\n const showScrollButtons = canScrollLeft || canScrollRight;\n\n return (\n <div\n data-testid={dataTestId}\n aria-label={ariaLabel}\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-tabs ant-tabs tw-flex',\n rootDirectionClass,\n adaptHeight && 'ald-adapt-height tw-h-full',\n size !== 'small' && 'ald-tabs-default',\n monospace && 'ald-tabs-monospace',\n compact && 'ald-tabs-compact',\n !hasDividing && 'ald-tabs-no-dividing',\n className,\n )}\n style={\n {\n ...style,\n '--header-bg-color': headerBackgroundColor,\n '--tabs-padding': `${paddingVal}px`,\n } as React.CSSProperties\n }\n >\n {/* Tab nav */}\n <div\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-tabs-nav ant-tabs-nav tw-flex tw-items-center',\n isVertical && 'tw-shrink-0',\n isTop && 'tw-mb-5',\n isBottom && 'tw-mt-5',\n isTop &&\n hasDividing &&\n 'tw-border-0 tw-border-b tw-border-solid tw-border-b-[var(--global-cool-grey-100)]',\n isBottom &&\n hasDividing &&\n 'tw-border-0 tw-border-t tw-border-solid tw-border-t-[var(--global-cool-grey-100)]',\n isTop && compact && '!tw-mb-0',\n isBottom && compact && '!tw-mt-0',\n isLeft &&\n 'tw-flex-col tw-border-0 tw-border-r tw-border-solid tw-border-r-[var(--global-cool-grey-100)]',\n isRight &&\n 'tw-flex-col tw-border-0 tw-border-l tw-border-solid tw-border-l-[var(--global-cool-grey-100)]',\n )}\n style={\n headerBackgroundColor || tabBarStyle\n ? {\n ...tabBarStyle,\n ...(headerBackgroundColor\n ? { backgroundColor: headerBackgroundColor }\n : {}),\n }\n : undefined\n }\n >\n {extraLeft}\n\n {/* Scroll left button */}\n {showScrollButtons && !isVertical && (\n <button\n type=\"button\"\n className={cn(\n 'ald-tabs-scroll-btn tw-flex tw-shrink-0 tw-items-center tw-justify-center tw-border-none tw-bg-transparent tw-p-1 tw-text-[var(--content-secondary)] tw-transition-colors hover:tw-text-[var(--content-primary)]',\n !canScrollLeft && 'tw-invisible',\n )}\n onClick={() => scrollBy(-200)}\n aria-label=\"Scroll tabs left\"\n >\n <ChevronLeftLine size={16} />\n </button>\n )}\n\n <div\n className={cn(\n 'ald-tabs-nav-wrap ant-tabs-nav-wrap',\n isVertical ? 'tw-w-full tw-shrink-0' : 'tw-min-w-0 tw-flex-1',\n )}\n style={paddingVal ? { margin: `0 ${paddingVal}px` } : undefined}\n >\n <div\n ref={navListRef}\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-tabs-nav-list ant-tabs-nav-list tw-flex tw-overflow-hidden',\n isVertical && 'tw-w-full tw-shrink-0 tw-flex-col',\n centered && !isVertical && 'tw-justify-center',\n monospace && '[&>*]:tw-flex-1',\n isCard ? 'tw-gap-1' : !monospace && 'tw-gap-8',\n )}\n style={\n !isVertical\n ? { scrollbarWidth: 'none', msOverflowStyle: 'none' }\n : undefined\n }\n >\n {resolvedItems.map((item) => {\n const isActive = activeKey === item.key;\n // For editable-card, closable defaults to true unless explicitly set to false\n const showClose = isEditable && item.closable !== false;\n\n return (\n <div\n key={item.key}\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-tabs-tab ant-tabs-tab tw-relative tw-flex tw-shrink-0 tw-cursor-pointer tw-items-center tw-gap-2 tw-whitespace-nowrap tw-transition-colors',\n isVertical && 'tw-w-full',\n // Card / editable-card styling\n isCard\n ? cn(\n 'tw-h-10 tw-rounded-t-[6px] tw-border tw-border-b-0 tw-border-solid tw-px-4 tw-text-sm tw-leading-5',\n 'tw-gap-sp-75',\n isActive\n ? 'tw-border-[var(--alias-colors-border-default)] tw-bg-[var(--background-default)] tw-font-medium tw-text-[var(--alias-colors-text-selected)]'\n : 'tw-border-[var(--alias-colors-border-default)] tw-bg-[var(--alias-colors-bg-skeleton-subtler)] tw-text-[var(--alias-colors-text-subtle)] hover:tw-text-inherit',\n )\n : cn(\n size === 'small'\n ? 'tw-py-2 tw-text-xs tw-leading-4'\n : 'tw-py-2.5 tw-text-sm tw-leading-5',\n monospace && 'tw-justify-center',\n isActive\n ? 'tw-font-medium tw-text-[var(--alias-colors-text-selected)]'\n : 'tw-font-medium tw-text-[var(--alias-colors-text-subtle)] hover:tw-text-inherit',\n ),\n item.disabled &&\n 'tw-pointer-events-none tw-cursor-default tw-opacity-50',\n )}\n onClick={\n item.disabled\n ? undefined\n : (e) => handleTabClick(item.key, e)\n }\n >\n {item.icon}\n {/* antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器 */}\n <span className=\"ant-tabs-tab-btn\">{item.label}</span>\n {/* Close button for editable-card */}\n {showClose && (\n <span\n className=\"ald-tabs-tab-remove tw-m-0 tw-grid tw-size-4 tw-cursor-pointer tw-place-items-center tw-p-0 tw-text-[var(--alias-colors-icon-subtle)] tw-transition-colors hover:tw-text-[var(--alias-colors-text-default)]\"\n onClick={(e) => handleRemove(item.key, e)}\n aria-label={`Remove ${item.label}`}\n >\n <CloseLightLine size={12} />\n </span>\n )}\n {/* Active indicator for non-card line tabs */}\n {/* antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器 */}\n {!isCard && isActive && isTop && (\n <div className=\"ant-tabs-ink-bar tw-absolute tw-inset-x-0 tw--bottom-px tw-h-[2px] tw-rounded-[2px] tw-bg-[var(--alias-colors-border-selected)]\" />\n )}\n {!isCard && isActive && isBottom && (\n <div className=\"ant-tabs-ink-bar tw-absolute tw-inset-x-0 tw--top-px tw-h-[2px] tw-rounded-[2px] tw-bg-[var(--alias-colors-border-selected)]\" />\n )}\n {!isCard && isActive && isLeft && (\n <div className=\"tw-absolute tw-inset-y-0 tw--right-px tw-w-[2px] tw-rounded-[2px] tw-bg-[var(--alias-colors-border-selected)]\" />\n )}\n {!isCard && isActive && isRight && (\n <div className=\"tw-absolute tw-inset-y-0 tw--left-px tw-w-[2px] tw-rounded-[2px] tw-bg-[var(--alias-colors-border-selected)]\" />\n )}\n </div>\n );\n })}\n </div>\n </div>\n\n {/* Scroll right button */}\n {showScrollButtons && !isVertical && (\n <button\n type=\"button\"\n className={cn(\n 'ald-tabs-scroll-btn tw-flex tw-shrink-0 tw-items-center tw-justify-center tw-border-none tw-bg-transparent tw-p-1 tw-text-[var(--content-secondary)] tw-transition-colors hover:tw-text-[var(--content-primary)]',\n !canScrollRight && 'tw-invisible',\n )}\n onClick={() => scrollBy(200)}\n aria-label=\"Scroll tabs right\"\n >\n <ChevronRightLine size={16} />\n </button>\n )}\n\n {/* Add button for editable-card */}\n {isEditable && !hideAdd && (\n <button\n type=\"button\"\n className=\"ald-tabs-nav-add tw-ml-1 tw-flex tw-shrink-0 tw-cursor-pointer tw-items-center tw-justify-center tw-rounded tw-border tw-border-solid tw-border-[var(--border-default)] tw-bg-[var(--background-default)] tw-px-2 tw-py-1 tw-text-[var(--content-secondary)] tw-transition-colors hover:tw-text-[var(--content-primary)]\"\n onClick={handleAdd}\n aria-label=\"Add tab\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n </button>\n )}\n\n {extraRight}\n </div>\n\n {/* Tab content */}\n {/* antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器 */}\n <div\n className={cn(\n 'ald-tabs-content ant-tabs-content ant-tabs-content-holder tw-min-h-0 tw-flex-1',\n adaptHeight && 'tw-h-full',\n )}\n style={\n paddingVal\n ? { padding: `0 ${paddingVal}px ${paddingVal}px` }\n : undefined\n }\n >\n {resolvedItems.map((item) => {\n const isActive = item.key === activeKey;\n if (!isActive && destroyInactiveTabPane && !item.forceRender) {\n return null;\n }\n return (\n <div\n key={item.key}\n className={cn(\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ald-tabs-tabpane ant-tabs-tabpane',\n // v2 行为:adaptHeight 时 tabpane 撑满内容区,子元素的百分比高度才能解析\n adaptHeight && 'tw-h-full tw-overflow-y-auto',\n isActive ? 'ant-tabs-tabpane-active tw-block' : 'tw-hidden',\n )}\n role=\"tabpanel\"\n >\n {item.children}\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n\nTabs.TabPane = TabPane;\n"],"mappings":"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"}
|
package/dist/Tree/Tree2.js
CHANGED
|
@@ -28,11 +28,23 @@ var Tree = React$1.forwardRef((props, ref) => {
|
|
|
28
28
|
}, ...rest } = props;
|
|
29
29
|
const prefixCls = getPrefixCls("tree", "ald-tree");
|
|
30
30
|
const customTitleRender = React$1.useCallback((node) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
const renderedTitle = titleRender ? titleRender(node) : typeof node.title === "function" ? node.title(node) : node.title;
|
|
32
|
+
const renderedHandler = handlerRender(node);
|
|
33
|
+
const hasHandler = renderedHandler !== null && renderedHandler !== void 0 && renderedHandler !== false;
|
|
34
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
35
|
+
className: cn("ald-tree-title-layout", hasHandler && "ald-tree-title-layout-with-handler"),
|
|
36
|
+
children: [
|
|
37
|
+
/* @__PURE__ */ jsx("span", {
|
|
38
|
+
className: "ald-tree-title-content",
|
|
39
|
+
children: renderedTitle
|
|
40
|
+
}),
|
|
41
|
+
showTabLeader && hasHandler && /* @__PURE__ */ jsx("div", { className: "ald-tree-tab-leader" }),
|
|
42
|
+
hasHandler && /* @__PURE__ */ jsx("span", {
|
|
43
|
+
className: "ald-tree-title-handler",
|
|
44
|
+
children: renderedHandler
|
|
45
|
+
})
|
|
46
|
+
]
|
|
47
|
+
});
|
|
36
48
|
}, [
|
|
37
49
|
titleRender,
|
|
38
50
|
showTabLeader,
|
package/dist/Tree/Tree2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tree2.js","names":[],"sources":["../../src/Tree/Tree.tsx"],"sourcesContent":["import './tree.css';\nimport { cn } from '../lib/utils';\nimport { produce } from 'immer';\nimport { noop } from 'lodash';\nimport type { BasicDataNode, TreeProps as RcTreeProps } from 'rc-tree';\nimport RcTree from 'rc-tree';\nimport type { DataNode, Key } from 'rc-tree/es/interface';\nimport * as React from 'react';\nimport { ConfigContext } from '../ConfigProvider';\nimport HighlightText from '../HighlightText';\nimport { ArrowRightLightLine, DragLine } from '../Icon';\nimport collapseMotion from '../_utils/motion';\nimport { LocaleContext, getTranslator } from '../locale/default';\nimport { composeAllowDrop } from './utils/composeAllowDrop';\nimport dropIndicatorRender from './utils/dropIndicator';\nimport renderSwitcherIcon from './utils/iconUtil';\nconst TREE_SWITCH_CION_SIZE = 16;\nconst NODE_DISABLE_CLASS_NAME = 'ald-tree-node-disabled';\nexport type SwitcherIcon =\n | React.ReactNode\n | ((props: AldTreeNodeProps) => React.ReactNode);\nexport type TreeLeafIcon =\n | React.ReactNode\n | ((props: AldTreeNodeProps) => React.ReactNode);\n\nexport interface AldTreeNodeAttribute {\n eventKey: string;\n prefixCls: string;\n className: string;\n expanded: boolean;\n selected: boolean;\n checked: boolean;\n halfChecked: boolean;\n children: React.ReactNode;\n title: React.ReactNode;\n pos: string;\n dragOver: boolean;\n dragOverGapTop: boolean;\n dragOverGapBottom: boolean;\n isLeaf: boolean;\n selectable: boolean;\n disabled: boolean;\n disableCheckbox: boolean;\n}\n\nexport interface AldTreeNodeProps {\n className?: string;\n checkable?: boolean;\n disabled?: boolean;\n disableCheckbox?: boolean;\n title?: string | React.ReactNode;\n key?: Key;\n eventKey?: string;\n isLeaf?: boolean;\n checked?: boolean;\n expanded?: boolean;\n loading?: boolean;\n selected?: boolean;\n selectable?: boolean;\n icon?:\n | ((treeNode: AldTreeNodeAttribute) => React.ReactNode)\n | React.ReactNode;\n children?: React.ReactNode;\n [customProp: string]: any;\n}\n\nexport type AldTreeNode = React.Component<AldTreeNodeProps, object>;\n\nexport interface AldTreeNodeBaseEvent {\n node: AldTreeNode;\n nativeEvent: MouseEvent;\n}\n\nexport interface AldTreeNodeCheckedEvent extends AldTreeNodeBaseEvent {\n event: 'check';\n checked?: boolean;\n checkedNodes?: AldTreeNode[];\n}\n\nexport interface AldTreeNodeSelectedEvent extends AldTreeNodeBaseEvent {\n event: 'select';\n selected?: boolean;\n selectedNodes?: DataNode[];\n}\n\nexport interface AldTreeNodeExpandedEvent extends AldTreeNodeBaseEvent {\n expanded?: boolean;\n}\n\nexport interface AldTreeNodeMouseEvent {\n node: AldTreeNode;\n event: React.DragEvent<HTMLElement>;\n}\n\nexport interface AldTreeNodeDragEnterEvent extends AldTreeNodeMouseEvent {\n expandedKeys: Key[];\n}\n\nexport interface AldTreeNodeAttribute {\n node: AldTreeNode;\n dragNode: AldTreeNode;\n dragNodesKeys: Key[];\n dropPosition: number;\n dropToGap?: boolean;\n event: React.MouseEvent<HTMLElement>;\n}\n\n// [Legacy] Compatible for v3\nexport type TreeNodeNormal = DataNode;\n\ntype DraggableFn = (node: DataNode) => boolean;\n\ninterface DropOnlyResolvers {\n nodeDraggable: DraggableFn | undefined;\n nodeDroppable: DraggableFn;\n}\n\ninterface DraggableConfig {\n icon?: React.ReactNode | false;\n /** 节点是否可作为 drag source(拖起来)。返回 false:不显示 drag handle、不能起拖。 */\n nodeDraggable?: DraggableFn;\n /**\n * 节点是否可作为 drop target(接收落入)。返回 true:即使 nodeDraggable=false 仍注册\n * onDragOver/onDrop 等 listener,浏览器才会真正派发 drop 事件。\n *\n * 解决 rc-tree 5.x 把\"可拖\"与\"可放\"绑到同一个 nodeDraggable 的限制——之前要让\"类目可接收\n * 资源拖入但本身不可拖\"只能让 nodeDraggable 全返回 true,再用业务侧 CSS hack 隐藏 drag icon。\n *\n * 默认不传 = 沿用 nodeDraggable 行为(向后兼容)。\n */\n nodeDroppable?: DraggableFn;\n}\n\nexport interface TreeProps<T extends BasicDataNode = DataNode>\n extends Omit<\n RcTreeProps<T>,\n | 'prefixCls'\n | 'showLine'\n | 'direction'\n | 'draggable'\n | 'icon'\n | 'switcherIcon'\n | 'filterTreeNode'\n > {\n showLine?: boolean | { showLeafIcon: boolean | TreeLeafIcon };\n className?: string;\n /** 是否支持多选 */\n multiple?: boolean;\n /** 是否自动展开父节点 */\n autoExpandParent?: boolean;\n /** Checkable状态下节点选择完全受控(父子节点选中状态不再关联) */\n checkStrictly?: boolean;\n /** 是否支持选中 */\n checkable?: boolean;\n /** 是否禁用树 */\n disabled?: boolean;\n /** 默认展开所有树节点 */\n defaultExpandAll?: boolean;\n /** 默认展开对应树节点 */\n defaultExpandParent?: boolean;\n /** 默认展开指定的树节点 */\n defaultExpandedKeys?: Key[];\n /** (受控)展开指定的树节点 */\n expandedKeys?: Key[];\n /** (受控)选中复选框的树节点 */\n checkedKeys?: Key[] | { checked: Key[]; halfChecked: Key[] };\n /** 默认选中复选框的树节点 */\n defaultCheckedKeys?: Key[];\n /** (受控)设置选中的树节点 */\n selectedKeys?: Key[];\n /** 默认选中的树节点 */\n defaultSelectedKeys?: Key[];\n selectable?: boolean;\n /** 点击树节点触发 */\n filterTreeNode?: (node: DataNode) => boolean;\n loadedKeys?: Key[];\n /** 设置节点可拖拽(IE>8) */\n draggable?: DraggableFn | boolean | DraggableConfig;\n style?: React.CSSProperties;\n showIcon?: boolean;\n icon?:\n | ((nodeProps: AldTreeNodeAttribute) => React.ReactNode)\n | React.ReactNode\n | RcTreeProps<T>['icon'];\n switcherIcon?: SwitcherIcon | RcTreeProps<T>['switcherIcon'];\n prefixCls?: string;\n children?: React.ReactNode;\n blockNode?: boolean;\n size?: 'large' | 'small';\n titleRender?: (node: T) => React.ReactNode;\n showTabLeader?: boolean;\n handlerRender?: (node: T) => React.ReactNode;\n highlightKeywords?: string;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst Tree = React.forwardRef<any, TreeProps>((props, ref) => {\n const { locale } = React.useContext(LocaleContext);\n const t = getTranslator(locale);\n\n const { getPrefixCls, direction, virtual } = React.useContext(ConfigContext);\n const size = props.size || 'large';\n const {\n className,\n showIcon = false,\n showLine,\n blockNode = false,\n children,\n checkable = false,\n selectable = true,\n draggable,\n titleRender,\n showTabLeader,\n handlerRender = noop,\n filterTreeNode,\n treeData,\n highlightKeywords,\n motion = { ...collapseMotion, motionAppear: false },\n ...rest\n } = props;\n const prefixCls = getPrefixCls('tree', 'ald-tree');\n\n const customTitleRender = React.useCallback(\n (node: DataNode) => {\n const renderedTitle = titleRender\n ? titleRender(node)\n : typeof node.title === 'function'\n ? node.title(node)\n : node.title;\n return (\n <>\n {renderedTitle}\n {showTabLeader && <div className=\"ald-tree-tab-leader\"></div>}\n {handlerRender && (handlerRender(node) as React.ReactNode)}\n </>\n );\n },\n [titleRender, showTabLeader, handlerRender],\n );\n const newProps = {\n ...rest,\n checkable,\n selectable,\n showIcon,\n motion,\n blockNode,\n showLine: Boolean(showLine),\n dropIndicatorRender,\n titleRender: customTitleRender,\n };\n\n // 一次性吃掉 draggable 的归一化:rc-tree 看到的 mergedDraggable + 我们内部用的\n // dropOnlyResolvers(原样保留用户 nodeDraggable/nodeDroppable)。集中一处避免两处 useMemo\n // 的判断散开导致字段扩展时遗漏。\n const { draggableConfig, dropOnlyResolvers } = React.useMemo(() => {\n if (!draggable) {\n return {\n draggableConfig: false as const,\n dropOnlyResolvers: null as DropOnlyResolvers | null,\n };\n }\n const newIcon = (\n <DragLine\n size={TREE_SWITCH_CION_SIZE}\n color={'var(--alias-colors-icon-subtle)'}\n ></DragLine>\n );\n let mergedDraggable: DraggableConfig = { icon: newIcon };\n switch (typeof draggable) {\n case 'function':\n mergedDraggable.nodeDraggable = draggable;\n break;\n case 'object':\n mergedDraggable = {\n ...draggable,\n icon: draggable.icon === undefined ? newIcon : draggable.icon,\n };\n break;\n default:\n break;\n }\n\n // rc-tree 5.x 把\"可拖(drag source)\"与\"可放(drop target)\"绑到同一个 nodeDraggable:\n // 该返回 false 时 onDragOver/onDrop 也连带不注册,浏览器认为该节点 not droppable\n // → drop 事件根本不派发。为了让\"类目可接收资源拖入但本身不可拖\",把 rc-tree 看到的\n // nodeDraggable 改成 nodeDraggable || nodeDroppable,从而保证 drop listener 仍注册。\n // 真正的 drag-source 阻断由 document 级 dragstart capture handler 负责(见下方 effect)。\n let resolvers: DropOnlyResolvers | null = null;\n if (mergedDraggable.nodeDroppable) {\n const userNodeDraggable = mergedDraggable.nodeDraggable;\n const userNodeDroppable = mergedDraggable.nodeDroppable;\n resolvers = {\n nodeDraggable: userNodeDraggable,\n nodeDroppable: userNodeDroppable,\n };\n mergedDraggable = {\n ...mergedDraggable,\n nodeDraggable: (node: DataNode) =>\n (userNodeDraggable ? userNodeDraggable(node) : true) ||\n userNodeDroppable(node),\n };\n }\n\n return { draggableConfig: mergedDraggable, dropOnlyResolvers: resolvers };\n }, [draggable]);\n\n /**\n * drop-only 节点不能真的起拖。\n *\n * 历史教训:以前在 props.onDragStart 里调 preventDefault。问题是 rc-tree 内部\n * onNodeDragStart(Tree.js:88-99)会在调用 user onDragStart **之前**把\n * draggingNodeKey/dragNodeProps 写进 state;TreeNode(TreeNode.js:112)也已经先\n * setDragNodeHighlight(true)。然后 preventDefault 让浏览器取消拖拽,dragend 不再派发\n * → onWindowDragEnd 永远不触发 → state 卡在\"正在拖类目\",外层 .ald-tree-treenode\n * 卡着 `dragging`、内层 .ald-tree-node-content-wrapper 卡着 `ald-tree-node-selected`\n * 高亮,直到下一次合法 dragstart 才被覆盖。\n *\n * 修复手段:document 级 dragstart capture handler,在 rc-tree 的 React 监听器之前\n * stopPropagation+preventDefault,让 TreeNode 的 onDragStart 整段都不执行,rc-tree\n * 内部 state 彻底不写入。document 是 React root 容器的祖先,capture 阶段先于 React\n * delegation 监听器,因此 stopPropagation 能彻底拦截。\n */\n React.useLayoutEffect(() => {\n if (!dropOnlyResolvers) return;\n const handler = (e: DragEvent) => {\n const target = e.target as HTMLElement | null;\n if (!target?.closest) return;\n if (target.closest('.ald-tree-treenode-drop-only')) {\n e.preventDefault();\n e.stopPropagation();\n }\n };\n document.addEventListener('dragstart', handler, true);\n return () => document.removeEventListener('dragstart', handler, true);\n }, [dropOnlyResolvers]);\n\n /**\n * nodeDroppable 同时收口到 rc-tree 的 allowDrop:避免出现\"nodeDroppable=false 但 rc-tree\n * 默认 allowDrop=()=>true 仍然接收 drop\"的语义裂缝。用户自传的 allowDrop 与 nodeDroppable\n * 取 AND(任一拒绝即拒绝)。\n */\n const composedAllowDrop = React.useMemo(\n () => composeAllowDrop(rest.allowDrop, dropOnlyResolvers?.nodeDroppable),\n [rest.allowDrop, dropOnlyResolvers],\n );\n const renderIndent = (nodeProps: AldTreeNodeProps) => {\n const { pos } = nodeProps as AldTreeNodeProps & { pos?: string };\n // rc-tree `pos` 格式:\"0\" 是虚拟根,\"0-0\" 是首层节点,\"0-0-1\" 是 leaf。\n // 顶层节点 level=0 不缩进;每层 +1 unit(switcher 宽度),与 antd Tree 行为一致。\n // 不再为 leaf 额外补 switcher 宽度——leaf 自身的 switcher-noop 已经占位,\n // 旧公式 `isLeaf + level * (iconSize/2)` 会让 leaf 比同级 group 视觉额外右移半个 unit。\n // `?? 2` 兜底:当 pos 缺失时按\"首层节点 `0-0` 长度 = 2\"处理,结果 level=0 不缩进;\n // 切勿改成 1 或 0,否则缺 pos 的节点会被错误右移。\n const level = Math.max(0, (pos?.split('-').length ?? 2) - 2);\n return (\n <span\n style={{ width: level * TREE_SWITCH_CION_SIZE, flexShrink: 0 }}\n ></span>\n );\n };\n const newTreeData = React.useMemo(() => {\n if (!filterTreeNode || !treeData) return treeData;\n\n const shouldNodeDisabled = (node: DataNode): boolean => {\n // 初始化一个标志来表示是否需要为当前节点添加className\n let shouldAddDisabled = true;\n if (filterTreeNode(node)) {\n shouldAddDisabled = false;\n }\n\n if (node.children) {\n // 遍历当前节点的子节点,只要有一个子节点不disable,则该不需要添加className\n const allChildrenShouldAddDisabled = node.children.reduce(\n (pre, child) => {\n return shouldNodeDisabled(child) && pre;\n },\n shouldAddDisabled,\n );\n shouldAddDisabled = allChildrenShouldAddDisabled;\n }\n\n // 如果shouldAddDisabled为true,就为当前节点添加className\n if (shouldAddDisabled) {\n node.className = node.className?.includes(NODE_DISABLE_CLASS_NAME)\n ? node.className\n : cn(node.className, NODE_DISABLE_CLASS_NAME);\n } else {\n const reg = /ald-tree-node-disabled/g;\n node.className?.replace(reg, ' ');\n }\n if (highlightKeywords && typeof node.title !== 'function') {\n node.title = (\n <HighlightText keyword={highlightKeywords}>\n {node.title}\n </HighlightText>\n );\n }\n return shouldAddDisabled;\n };\n const newTreeData = produce(treeData, (draft) => {\n for (const rootNode of draft) {\n shouldNodeDisabled(rootNode);\n }\n });\n return newTreeData;\n }, [filterTreeNode, treeData, highlightKeywords]);\n\n /** 给 drop-only 节点的 wrapper 加 'ald-tree-treenode-drop-only' className(rc-tree 5.x\n * 会把 DataNode.className 透传到 .ald-tree-treenode 上)。css 据此隐藏 drag handle icon\n * 并提供 not-allowed cursor,区分纯 drop target 与真正可拖节点。 */\n const finalTreeData = React.useMemo(() => {\n if (!dropOnlyResolvers || !newTreeData) return newTreeData;\n const { nodeDraggable, nodeDroppable } = dropOnlyResolvers;\n return produce(newTreeData, (draft) => {\n const visit = (nodes: DataNode[] | undefined) => {\n if (!nodes) return;\n for (const node of nodes) {\n const isDraggable = nodeDraggable ? nodeDraggable(node) : true;\n const isDroppable = nodeDroppable(node);\n if (!isDraggable && isDroppable) {\n const existing = node.className ?? '';\n if (!existing.includes('ald-tree-treenode-drop-only')) {\n node.className = cn(existing, 'ald-tree-treenode-drop-only');\n }\n }\n visit(node.children as DataNode[] | undefined);\n }\n };\n visit(draft as DataNode[]);\n });\n }, [newTreeData, dropOnlyResolvers]);\n\n const noChildren = React.useMemo(() => {\n if (finalTreeData?.length) {\n return finalTreeData.every((node) => {\n return !node.children?.length && node.isLeaf !== false;\n });\n }\n return true;\n }, [finalTreeData]);\n\n return (\n <RcTree\n itemHeight={20}\n ref={ref}\n virtual={virtual}\n {...newProps}\n allowDrop={composedAllowDrop}\n prefixCls={prefixCls}\n treeData={finalTreeData}\n className={cn(\n {\n [`${prefixCls}-icon-hide`]: !showIcon,\n [`${prefixCls}-block-node`]: blockNode,\n [`${prefixCls}-unselectable`]: !selectable,\n [`${prefixCls}-large`]: size === 'large',\n [`${prefixCls}-rtl`]: direction === 'rtl',\n ['ald-draggable-tree']: draggable,\n [`${prefixCls}-no-children`]: noChildren,\n },\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ant-tree',\n className,\n )}\n direction={direction as any}\n checkable={\n checkable ? (\n <span className={`${prefixCls}-checkbox-inner`} />\n ) : (\n checkable\n )\n }\n selectable={selectable}\n // @ts-ignore\n switcherIcon={(nodeProps: AldTreeNodeProps) => {\n return (\n <>\n {renderIndent(nodeProps)}\n {renderSwitcherIcon(\n prefixCls,\n <ArrowRightLightLine\n size={TREE_SWITCH_CION_SIZE}\n color={'var(--alias-colors-icon-subtle)'}\n >\n {t.Tree.expand}\n </ArrowRightLightLine>,\n showLine,\n nodeProps,\n TREE_SWITCH_CION_SIZE,\n )}\n </>\n );\n }}\n draggable={draggableConfig}\n >\n {children}\n </RcTree>\n );\n});\n\nexport default Tree;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Tree2.js","names":[],"sources":["../../src/Tree/Tree.tsx"],"sourcesContent":["import './tree.css';\nimport { cn } from '../lib/utils';\nimport { produce } from 'immer';\nimport { noop } from 'lodash';\nimport type { BasicDataNode, TreeProps as RcTreeProps } from 'rc-tree';\nimport RcTree from 'rc-tree';\nimport type { DataNode, Key } from 'rc-tree/es/interface';\nimport * as React from 'react';\nimport { ConfigContext } from '../ConfigProvider';\nimport HighlightText from '../HighlightText';\nimport { ArrowRightLightLine, DragLine } from '../Icon';\nimport collapseMotion from '../_utils/motion';\nimport { LocaleContext, getTranslator } from '../locale/default';\nimport { composeAllowDrop } from './utils/composeAllowDrop';\nimport dropIndicatorRender from './utils/dropIndicator';\nimport renderSwitcherIcon from './utils/iconUtil';\nconst TREE_SWITCH_CION_SIZE = 16;\nconst NODE_DISABLE_CLASS_NAME = 'ald-tree-node-disabled';\nexport type SwitcherIcon =\n | React.ReactNode\n | ((props: AldTreeNodeProps) => React.ReactNode);\nexport type TreeLeafIcon =\n | React.ReactNode\n | ((props: AldTreeNodeProps) => React.ReactNode);\n\nexport interface AldTreeNodeAttribute {\n eventKey: string;\n prefixCls: string;\n className: string;\n expanded: boolean;\n selected: boolean;\n checked: boolean;\n halfChecked: boolean;\n children: React.ReactNode;\n title: React.ReactNode;\n pos: string;\n dragOver: boolean;\n dragOverGapTop: boolean;\n dragOverGapBottom: boolean;\n isLeaf: boolean;\n selectable: boolean;\n disabled: boolean;\n disableCheckbox: boolean;\n}\n\nexport interface AldTreeNodeProps {\n className?: string;\n checkable?: boolean;\n disabled?: boolean;\n disableCheckbox?: boolean;\n title?: string | React.ReactNode;\n key?: Key;\n eventKey?: string;\n isLeaf?: boolean;\n checked?: boolean;\n expanded?: boolean;\n loading?: boolean;\n selected?: boolean;\n selectable?: boolean;\n icon?:\n | ((treeNode: AldTreeNodeAttribute) => React.ReactNode)\n | React.ReactNode;\n children?: React.ReactNode;\n [customProp: string]: any;\n}\n\nexport type AldTreeNode = React.Component<AldTreeNodeProps, object>;\n\nexport interface AldTreeNodeBaseEvent {\n node: AldTreeNode;\n nativeEvent: MouseEvent;\n}\n\nexport interface AldTreeNodeCheckedEvent extends AldTreeNodeBaseEvent {\n event: 'check';\n checked?: boolean;\n checkedNodes?: AldTreeNode[];\n}\n\nexport interface AldTreeNodeSelectedEvent extends AldTreeNodeBaseEvent {\n event: 'select';\n selected?: boolean;\n selectedNodes?: DataNode[];\n}\n\nexport interface AldTreeNodeExpandedEvent extends AldTreeNodeBaseEvent {\n expanded?: boolean;\n}\n\nexport interface AldTreeNodeMouseEvent {\n node: AldTreeNode;\n event: React.DragEvent<HTMLElement>;\n}\n\nexport interface AldTreeNodeDragEnterEvent extends AldTreeNodeMouseEvent {\n expandedKeys: Key[];\n}\n\nexport interface AldTreeNodeAttribute {\n node: AldTreeNode;\n dragNode: AldTreeNode;\n dragNodesKeys: Key[];\n dropPosition: number;\n dropToGap?: boolean;\n event: React.MouseEvent<HTMLElement>;\n}\n\n// [Legacy] Compatible for v3\nexport type TreeNodeNormal = DataNode;\n\ntype DraggableFn = (node: DataNode) => boolean;\n\ninterface DropOnlyResolvers {\n nodeDraggable: DraggableFn | undefined;\n nodeDroppable: DraggableFn;\n}\n\ninterface DraggableConfig {\n icon?: React.ReactNode | false;\n /** 节点是否可作为 drag source(拖起来)。返回 false:不显示 drag handle、不能起拖。 */\n nodeDraggable?: DraggableFn;\n /**\n * 节点是否可作为 drop target(接收落入)。返回 true:即使 nodeDraggable=false 仍注册\n * onDragOver/onDrop 等 listener,浏览器才会真正派发 drop 事件。\n *\n * 解决 rc-tree 5.x 把\"可拖\"与\"可放\"绑到同一个 nodeDraggable 的限制——之前要让\"类目可接收\n * 资源拖入但本身不可拖\"只能让 nodeDraggable 全返回 true,再用业务侧 CSS hack 隐藏 drag icon。\n *\n * 默认不传 = 沿用 nodeDraggable 行为(向后兼容)。\n */\n nodeDroppable?: DraggableFn;\n}\n\nexport interface TreeProps<T extends BasicDataNode = DataNode>\n extends Omit<\n RcTreeProps<T>,\n | 'prefixCls'\n | 'showLine'\n | 'direction'\n | 'draggable'\n | 'icon'\n | 'switcherIcon'\n | 'filterTreeNode'\n > {\n showLine?: boolean | { showLeafIcon: boolean | TreeLeafIcon };\n className?: string;\n /** 是否支持多选 */\n multiple?: boolean;\n /** 是否自动展开父节点 */\n autoExpandParent?: boolean;\n /** Checkable状态下节点选择完全受控(父子节点选中状态不再关联) */\n checkStrictly?: boolean;\n /** 是否支持选中 */\n checkable?: boolean;\n /** 是否禁用树 */\n disabled?: boolean;\n /** 默认展开所有树节点 */\n defaultExpandAll?: boolean;\n /** 默认展开对应树节点 */\n defaultExpandParent?: boolean;\n /** 默认展开指定的树节点 */\n defaultExpandedKeys?: Key[];\n /** (受控)展开指定的树节点 */\n expandedKeys?: Key[];\n /** (受控)选中复选框的树节点 */\n checkedKeys?: Key[] | { checked: Key[]; halfChecked: Key[] };\n /** 默认选中复选框的树节点 */\n defaultCheckedKeys?: Key[];\n /** (受控)设置选中的树节点 */\n selectedKeys?: Key[];\n /** 默认选中的树节点 */\n defaultSelectedKeys?: Key[];\n selectable?: boolean;\n /** 点击树节点触发 */\n filterTreeNode?: (node: DataNode) => boolean;\n loadedKeys?: Key[];\n /** 设置节点可拖拽(IE>8) */\n draggable?: DraggableFn | boolean | DraggableConfig;\n style?: React.CSSProperties;\n showIcon?: boolean;\n icon?:\n | ((nodeProps: AldTreeNodeAttribute) => React.ReactNode)\n | React.ReactNode\n | RcTreeProps<T>['icon'];\n switcherIcon?: SwitcherIcon | RcTreeProps<T>['switcherIcon'];\n prefixCls?: string;\n children?: React.ReactNode;\n blockNode?: boolean;\n size?: 'large' | 'small';\n titleRender?: (node: T) => React.ReactNode;\n showTabLeader?: boolean;\n handlerRender?: (node: T) => React.ReactNode;\n highlightKeywords?: string;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst Tree = React.forwardRef<any, TreeProps>((props, ref) => {\n const { locale } = React.useContext(LocaleContext);\n const t = getTranslator(locale);\n\n const { getPrefixCls, direction, virtual } = React.useContext(ConfigContext);\n const size = props.size || 'large';\n const {\n className,\n showIcon = false,\n showLine,\n blockNode = false,\n children,\n checkable = false,\n selectable = true,\n draggable,\n titleRender,\n showTabLeader,\n handlerRender = noop,\n filterTreeNode,\n treeData,\n highlightKeywords,\n motion = { ...collapseMotion, motionAppear: false },\n ...rest\n } = props;\n const prefixCls = getPrefixCls('tree', 'ald-tree');\n\n const customTitleRender = React.useCallback(\n (node: DataNode) => {\n const renderedTitle = titleRender\n ? titleRender(node)\n : typeof node.title === 'function'\n ? node.title(node)\n : node.title;\n const renderedHandler = handlerRender(node) as React.ReactNode;\n const hasHandler =\n renderedHandler !== null &&\n renderedHandler !== undefined &&\n renderedHandler !== false;\n return (\n <span\n className={cn(\n 'ald-tree-title-layout',\n hasHandler && 'ald-tree-title-layout-with-handler',\n )}\n >\n <span className=\"ald-tree-title-content\">{renderedTitle}</span>\n {showTabLeader && hasHandler && (\n <div className=\"ald-tree-tab-leader\"></div>\n )}\n {hasHandler && (\n <span className=\"ald-tree-title-handler\">{renderedHandler}</span>\n )}\n </span>\n );\n },\n [titleRender, showTabLeader, handlerRender],\n );\n const newProps = {\n ...rest,\n checkable,\n selectable,\n showIcon,\n motion,\n blockNode,\n showLine: Boolean(showLine),\n dropIndicatorRender,\n titleRender: customTitleRender,\n };\n\n // 一次性吃掉 draggable 的归一化:rc-tree 看到的 mergedDraggable + 我们内部用的\n // dropOnlyResolvers(原样保留用户 nodeDraggable/nodeDroppable)。集中一处避免两处 useMemo\n // 的判断散开导致字段扩展时遗漏。\n const { draggableConfig, dropOnlyResolvers } = React.useMemo(() => {\n if (!draggable) {\n return {\n draggableConfig: false as const,\n dropOnlyResolvers: null as DropOnlyResolvers | null,\n };\n }\n const newIcon = (\n <DragLine\n size={TREE_SWITCH_CION_SIZE}\n color={'var(--alias-colors-icon-subtle)'}\n ></DragLine>\n );\n let mergedDraggable: DraggableConfig = { icon: newIcon };\n switch (typeof draggable) {\n case 'function':\n mergedDraggable.nodeDraggable = draggable;\n break;\n case 'object':\n mergedDraggable = {\n ...draggable,\n icon: draggable.icon === undefined ? newIcon : draggable.icon,\n };\n break;\n default:\n break;\n }\n\n // rc-tree 5.x 把\"可拖(drag source)\"与\"可放(drop target)\"绑到同一个 nodeDraggable:\n // 该返回 false 时 onDragOver/onDrop 也连带不注册,浏览器认为该节点 not droppable\n // → drop 事件根本不派发。为了让\"类目可接收资源拖入但本身不可拖\",把 rc-tree 看到的\n // nodeDraggable 改成 nodeDraggable || nodeDroppable,从而保证 drop listener 仍注册。\n // 真正的 drag-source 阻断由 document 级 dragstart capture handler 负责(见下方 effect)。\n let resolvers: DropOnlyResolvers | null = null;\n if (mergedDraggable.nodeDroppable) {\n const userNodeDraggable = mergedDraggable.nodeDraggable;\n const userNodeDroppable = mergedDraggable.nodeDroppable;\n resolvers = {\n nodeDraggable: userNodeDraggable,\n nodeDroppable: userNodeDroppable,\n };\n mergedDraggable = {\n ...mergedDraggable,\n nodeDraggable: (node: DataNode) =>\n (userNodeDraggable ? userNodeDraggable(node) : true) ||\n userNodeDroppable(node),\n };\n }\n\n return { draggableConfig: mergedDraggable, dropOnlyResolvers: resolvers };\n }, [draggable]);\n\n /**\n * drop-only 节点不能真的起拖。\n *\n * 历史教训:以前在 props.onDragStart 里调 preventDefault。问题是 rc-tree 内部\n * onNodeDragStart(Tree.js:88-99)会在调用 user onDragStart **之前**把\n * draggingNodeKey/dragNodeProps 写进 state;TreeNode(TreeNode.js:112)也已经先\n * setDragNodeHighlight(true)。然后 preventDefault 让浏览器取消拖拽,dragend 不再派发\n * → onWindowDragEnd 永远不触发 → state 卡在\"正在拖类目\",外层 .ald-tree-treenode\n * 卡着 `dragging`、内层 .ald-tree-node-content-wrapper 卡着 `ald-tree-node-selected`\n * 高亮,直到下一次合法 dragstart 才被覆盖。\n *\n * 修复手段:document 级 dragstart capture handler,在 rc-tree 的 React 监听器之前\n * stopPropagation+preventDefault,让 TreeNode 的 onDragStart 整段都不执行,rc-tree\n * 内部 state 彻底不写入。document 是 React root 容器的祖先,capture 阶段先于 React\n * delegation 监听器,因此 stopPropagation 能彻底拦截。\n */\n React.useLayoutEffect(() => {\n if (!dropOnlyResolvers) return;\n const handler = (e: DragEvent) => {\n const target = e.target as HTMLElement | null;\n if (!target?.closest) return;\n if (target.closest('.ald-tree-treenode-drop-only')) {\n e.preventDefault();\n e.stopPropagation();\n }\n };\n document.addEventListener('dragstart', handler, true);\n return () => document.removeEventListener('dragstart', handler, true);\n }, [dropOnlyResolvers]);\n\n /**\n * nodeDroppable 同时收口到 rc-tree 的 allowDrop:避免出现\"nodeDroppable=false 但 rc-tree\n * 默认 allowDrop=()=>true 仍然接收 drop\"的语义裂缝。用户自传的 allowDrop 与 nodeDroppable\n * 取 AND(任一拒绝即拒绝)。\n */\n const composedAllowDrop = React.useMemo(\n () => composeAllowDrop(rest.allowDrop, dropOnlyResolvers?.nodeDroppable),\n [rest.allowDrop, dropOnlyResolvers],\n );\n const renderIndent = (nodeProps: AldTreeNodeProps) => {\n const { pos } = nodeProps as AldTreeNodeProps & { pos?: string };\n // rc-tree `pos` 格式:\"0\" 是虚拟根,\"0-0\" 是首层节点,\"0-0-1\" 是 leaf。\n // 顶层节点 level=0 不缩进;每层 +1 unit(switcher 宽度),与 antd Tree 行为一致。\n // 不再为 leaf 额外补 switcher 宽度——leaf 自身的 switcher-noop 已经占位,\n // 旧公式 `isLeaf + level * (iconSize/2)` 会让 leaf 比同级 group 视觉额外右移半个 unit。\n // `?? 2` 兜底:当 pos 缺失时按\"首层节点 `0-0` 长度 = 2\"处理,结果 level=0 不缩进;\n // 切勿改成 1 或 0,否则缺 pos 的节点会被错误右移。\n const level = Math.max(0, (pos?.split('-').length ?? 2) - 2);\n return (\n <span\n style={{ width: level * TREE_SWITCH_CION_SIZE, flexShrink: 0 }}\n ></span>\n );\n };\n const newTreeData = React.useMemo(() => {\n if (!filterTreeNode || !treeData) return treeData;\n\n const shouldNodeDisabled = (node: DataNode): boolean => {\n // 初始化一个标志来表示是否需要为当前节点添加className\n let shouldAddDisabled = true;\n if (filterTreeNode(node)) {\n shouldAddDisabled = false;\n }\n\n if (node.children) {\n // 遍历当前节点的子节点,只要有一个子节点不disable,则该不需要添加className\n const allChildrenShouldAddDisabled = node.children.reduce(\n (pre, child) => {\n return shouldNodeDisabled(child) && pre;\n },\n shouldAddDisabled,\n );\n shouldAddDisabled = allChildrenShouldAddDisabled;\n }\n\n // 如果shouldAddDisabled为true,就为当前节点添加className\n if (shouldAddDisabled) {\n node.className = node.className?.includes(NODE_DISABLE_CLASS_NAME)\n ? node.className\n : cn(node.className, NODE_DISABLE_CLASS_NAME);\n } else {\n const reg = /ald-tree-node-disabled/g;\n node.className?.replace(reg, ' ');\n }\n if (highlightKeywords && typeof node.title !== 'function') {\n node.title = (\n <HighlightText keyword={highlightKeywords}>\n {node.title}\n </HighlightText>\n );\n }\n return shouldAddDisabled;\n };\n const newTreeData = produce(treeData, (draft) => {\n for (const rootNode of draft) {\n shouldNodeDisabled(rootNode);\n }\n });\n return newTreeData;\n }, [filterTreeNode, treeData, highlightKeywords]);\n\n /** 给 drop-only 节点的 wrapper 加 'ald-tree-treenode-drop-only' className(rc-tree 5.x\n * 会把 DataNode.className 透传到 .ald-tree-treenode 上)。css 据此隐藏 drag handle icon\n * 并提供 not-allowed cursor,区分纯 drop target 与真正可拖节点。 */\n const finalTreeData = React.useMemo(() => {\n if (!dropOnlyResolvers || !newTreeData) return newTreeData;\n const { nodeDraggable, nodeDroppable } = dropOnlyResolvers;\n return produce(newTreeData, (draft) => {\n const visit = (nodes: DataNode[] | undefined) => {\n if (!nodes) return;\n for (const node of nodes) {\n const isDraggable = nodeDraggable ? nodeDraggable(node) : true;\n const isDroppable = nodeDroppable(node);\n if (!isDraggable && isDroppable) {\n const existing = node.className ?? '';\n if (!existing.includes('ald-tree-treenode-drop-only')) {\n node.className = cn(existing, 'ald-tree-treenode-drop-only');\n }\n }\n visit(node.children as DataNode[] | undefined);\n }\n };\n visit(draft as DataNode[]);\n });\n }, [newTreeData, dropOnlyResolvers]);\n\n const noChildren = React.useMemo(() => {\n if (finalTreeData?.length) {\n return finalTreeData.every((node) => {\n return !node.children?.length && node.isLeaf !== false;\n });\n }\n return true;\n }, [finalTreeData]);\n\n return (\n <RcTree\n itemHeight={20}\n ref={ref}\n virtual={virtual}\n {...newProps}\n allowDrop={composedAllowDrop}\n prefixCls={prefixCls}\n treeData={finalTreeData}\n className={cn(\n {\n [`${prefixCls}-icon-hide`]: !showIcon,\n [`${prefixCls}-block-node`]: blockNode,\n [`${prefixCls}-unselectable`]: !selectable,\n [`${prefixCls}-large`]: size === 'large',\n [`${prefixCls}-rtl`]: direction === 'rtl',\n ['ald-draggable-tree']: draggable,\n [`${prefixCls}-no-children`]: noChildren,\n },\n // antd 兼容:保留 ant-* class,消费方 CSS 可能依赖该选择器\n 'ant-tree',\n className,\n )}\n direction={direction as any}\n checkable={\n checkable ? (\n <span className={`${prefixCls}-checkbox-inner`} />\n ) : (\n checkable\n )\n }\n selectable={selectable}\n // @ts-ignore\n switcherIcon={(nodeProps: AldTreeNodeProps) => {\n return (\n <>\n {renderIndent(nodeProps)}\n {renderSwitcherIcon(\n prefixCls,\n <ArrowRightLightLine\n size={TREE_SWITCH_CION_SIZE}\n color={'var(--alias-colors-icon-subtle)'}\n >\n {t.Tree.expand}\n </ArrowRightLightLine>,\n showLine,\n nodeProps,\n TREE_SWITCH_CION_SIZE,\n )}\n </>\n );\n }}\n draggable={draggableConfig}\n >\n {children}\n </RcTree>\n );\n});\n\nexport default Tree;\n"],"mappings":"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"}
|