@zerodep-dev/vue-tsx-pro-ui 0.3.2 → 0.4.0
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/charts/AreaChart.d.ts +1 -1
- package/dist/charts/AreaChart.js +6 -48
- package/dist/charts/AreaChart.js.map +1 -1
- package/dist/charts/BarChart.js +6 -49
- package/dist/charts/BarChart.js.map +1 -1
- package/dist/charts/ChordChart.d.ts +7 -2
- package/dist/charts/ChordChart.js +151 -60
- package/dist/charts/ChordChart.js.map +1 -1
- package/dist/charts/ComboChart.d.ts +2 -1
- package/dist/charts/ComboChart.js +17 -93
- package/dist/charts/ComboChart.js.map +1 -1
- package/dist/charts/DonutChart.d.ts +6 -1
- package/dist/charts/DonutChart.js +134 -51
- package/dist/charts/DonutChart.js.map +1 -1
- package/dist/charts/FunnelChart.js.map +1 -1
- package/dist/charts/GraphChart.d.ts +22 -10
- package/dist/charts/GraphChart.js +415 -175
- package/dist/charts/GraphChart.js.map +1 -1
- package/dist/charts/LineChart.d.ts +1 -29
- package/dist/charts/LineChart.js +6 -54
- package/dist/charts/LineChart.js.map +1 -1
- package/dist/charts/MapChart.d.ts +21 -48
- package/dist/charts/MapChart.js +426 -353
- package/dist/charts/MapChart.js.map +1 -1
- package/dist/charts/RadarChart.js.map +1 -1
- package/dist/charts/SankeyChart.d.ts +7 -2
- package/dist/charts/SankeyChart.js +164 -76
- package/dist/charts/SankeyChart.js.map +1 -1
- package/dist/charts/ScatterChart.d.ts +7 -1
- package/dist/charts/ScatterChart.js +45 -30
- package/dist/charts/ScatterChart.js.map +1 -1
- package/dist/charts/TimelineChart.d.ts +8 -1
- package/dist/charts/TimelineChart.js +141 -89
- package/dist/charts/TimelineChart.js.map +1 -1
- package/dist/charts/TreemapChart.d.ts +6 -0
- package/dist/charts/TreemapChart.js +174 -58
- package/dist/charts/TreemapChart.js.map +1 -1
- package/dist/charts/WaterfallChart.d.ts +6 -7
- package/dist/charts/WaterfallChart.js +46 -51
- package/dist/charts/WaterfallChart.js.map +1 -1
- package/dist/charts/XYChart.d.ts +44 -35
- package/dist/charts/XYChart.js +493 -281
- package/dist/charts/XYChart.js.map +1 -1
- package/dist/charts/axis-ticks.d.ts +1 -1
- package/dist/charts/axis-ticks.js +4 -5
- package/dist/charts/axis-ticks.js.map +1 -1
- package/dist/charts/flow.d.ts +4 -11
- package/dist/charts/flow.js +4 -4
- package/dist/charts/flow.js.map +1 -1
- package/dist/charts/frame.d.ts +6 -30
- package/dist/charts/frame.js +6 -99
- package/dist/charts/frame.js.map +1 -1
- package/dist/charts/geo/projection.d.ts +45 -0
- package/dist/charts/geo/projection.js +201 -0
- package/dist/charts/geo/projection.js.map +1 -0
- package/dist/charts/geo/topojson.d.ts +50 -0
- package/dist/charts/geo/topojson.js +119 -0
- package/dist/charts/geo/topojson.js.map +1 -0
- package/dist/charts/index.d.ts +21 -9
- package/dist/charts/index.js +19 -8
- package/dist/charts/label-layout.js.map +1 -1
- package/dist/charts/layout/chord.d.ts +44 -0
- package/dist/charts/layout/chord.js +45 -0
- package/dist/charts/layout/chord.js.map +1 -0
- package/dist/charts/layout/graph.d.ts +49 -0
- package/dist/charts/layout/graph.js +268 -0
- package/dist/charts/layout/graph.js.map +1 -0
- package/dist/charts/layout/sankey.d.ts +79 -0
- package/dist/charts/layout/sankey.js +200 -0
- package/dist/charts/layout/sankey.js.map +1 -0
- package/dist/charts/layout/treemap.d.ts +46 -0
- package/dist/charts/layout/treemap.js +152 -0
- package/dist/charts/layout/treemap.js.map +1 -0
- package/dist/charts/marks.d.ts +79 -0
- package/dist/charts/marks.js +231 -0
- package/dist/charts/marks.js.map +1 -0
- package/dist/charts/plot.d.ts +107 -0
- package/dist/charts/plot.js +231 -0
- package/dist/charts/plot.js.map +1 -0
- package/dist/charts/scale.d.ts +29 -0
- package/dist/charts/scale.js +244 -0
- package/dist/charts/scale.js.map +1 -0
- package/dist/charts/shape.d.ts +46 -0
- package/dist/charts/shape.js +131 -0
- package/dist/charts/shape.js.map +1 -0
- package/dist/charts/stack.d.ts +17 -0
- package/dist/charts/stack.js +35 -0
- package/dist/charts/stack.js.map +1 -0
- package/dist/charts/text-size.d.ts +12 -0
- package/dist/charts/text-size.js +32 -0
- package/dist/charts/text-size.js.map +1 -0
- package/dist/display/VirtualList.d.ts +1 -2
- package/dist/display/VirtualList.js +11 -21
- package/dist/display/VirtualList.js.map +1 -1
- package/dist/input/Combobox.d.ts +1 -1
- package/dist/input/Combobox.js.map +1 -1
- package/dist/input/ListPopover.d.ts +1 -1
- package/dist/input/ListPopover.js.map +1 -1
- package/dist/input/Listbox.d.ts +1 -2
- package/dist/input/Listbox.js +7 -19
- package/dist/input/Listbox.js.map +1 -1
- package/dist/input/Select.d.ts +1 -1
- package/dist/input/Select.js.map +1 -1
- package/dist/maps/china-provinces.js +35680 -0
- package/dist/maps/china-provinces.js.map +1 -0
- package/dist/maps/china.d.ts +13 -0
- package/dist/maps/china.js +8 -0
- package/dist/maps/china.js.map +1 -0
- package/dist/maps/index.d.ts +9 -0
- package/dist/maps/index.js +3 -0
- package/dist/maps/world-countries.js +22273 -0
- package/dist/maps/world-countries.js.map +1 -0
- package/dist/maps/world.d.ts +13 -0
- package/dist/maps/world.js +8 -0
- package/dist/maps/world.js.map +1 -0
- package/dist/overlay/CommandPalette.d.ts +1 -1
- package/dist/overlay/CommandPalette.js.map +1 -1
- package/dist/overlay/detached.js +14 -1
- package/dist/overlay/detached.js.map +1 -1
- package/dist/patterns/QueryTable.js +0 -2
- package/dist/patterns/QueryTable.js.map +1 -1
- package/dist/table/DataTable.js +1 -1
- package/dist/table/DataTable.js.map +1 -1
- package/dist/table/DataTableView.d.ts +0 -3
- package/dist/table/DataTableView.js +32 -26
- package/dist/table/DataTableView.js.map +1 -1
- package/dist/table/table-styles.d.ts +1 -1
- package/dist/table/table-styles.js +0 -13
- package/dist/table/table-styles.js.map +1 -1
- package/package.json +7 -8
- package/dist/charts/single.d.ts +0 -41
- package/dist/charts/single.js +0 -66
- package/dist/charts/single.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandPalette.js","names":["_vtp_bindable","_vtp_readonlyProp","_vtp_restProps","_createVNode","_Fragment","_mergeProps","_withDirectives","_vModelText"],"sources":["../../src/overlay/CommandPalette.tsx"],"sourcesContent":["/**\n * 命令面板:Mod+K(macOS 上 ⌘K,其他系统 Ctrl+K)或点触发按钮打开的搜索对话框,边输入边过滤,回车打开。\n * 文档站的站内搜索、后台的“跳转到…”都用它;数据由使用方给,选中后做什么也由使用方决定。\n *\n * <CommandPalette\n * items={pages}\n * toOption={(page) => ({ value: page.path, label: page.title, description: page.summary, group: page.section })}\n * onAction={(page) => router.push(page.path)}\n * />\n *\n * 结构是 Dialog(模态、焦点循环、Esc 关闭)里放一个 combobox 输入框与 ListboxView:\n * 焦点一直在输入框上,↑↓ / Page Up / Page Down 移动高亮(aria-activedescendant),Enter 选中;\n * 数据层与 Select、Combobox 相同(useListState),选项多时虚拟滚动。\n * 远程搜索与 Combobox 一样:_inputValue={keyword} 拿到输入,filter={false} 不再本地过滤,取数据时给 loading。\n */\nimport { Search } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { useId, watch, type VNodeChild } from 'vue';\nimport { ButtonBase, type ButtonBaseProps } from '../base/ButtonBase';\nimport { Icon } from '../base/Icon';\nimport { Kbd } from '../base/Kbd';\nimport { visuallyHiddenStyles } from '../base/VisuallyHidden';\nimport { useLang, useTextMatcher } from '../config/context';\nimport { controlParts, useControlAppearance, type ControlParts } from '../input/control';\nimport {\n ListboxView,\n activeOptionId,\n type ListboxViewProps,\n type OptionRenderState,\n} from '../input/Listbox';\nimport {\n handleListKeydown,\n useListState,\n type ListboxOption,\n type OptionMapping,\n toListboxOption,\n} from '../input/list-state';\nimport { ariaKeyShortcut, formatHotkey, useHotkeys } from '../utils/dom/hotkeys';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { breakpoints } from '../config/theme/tokens';\nimport { openSetter } from '../utils/dom/open-change';\nimport { Dialog, type DialogProps } from './Dialog';\n\n/** 交给触发器的属性:展开到按钮上 */\nexport interface CommandPaletteTriggerProps {\n /** 打开面板 */\n onClick: (event: MouseEvent) => void;\n /** 告诉读屏这个按钮会打开对话框 */\n 'aria-haspopup': 'dialog';\n /** 面板是否打开 */\n 'aria-expanded': boolean;\n}\n\n/** 与数据类型无关的属性;未声明的属性落到对话框(dialog)上 */\nexport interface CommandPaletteBaseProps<T> extends Omit<\n DialogProps,\n 'open' | 'title' | 'description' | 'footer' | 'children' | 'onSelect'\n> {\n /** 是否打开:父级可以写 _open={x};快捷键、触发按钮、选中、Esc 都会改它 */\n _open?: boolean;\n /** 数据:ListboxOption、字符串,或任意数据加 toOption */\n items: readonly T[];\n /** 执行了一项(回车或点击):面板随即关闭 */\n onAction: (item: T) => void;\n /** 输入框里的文字:父级可以写 _inputValue={x}(远程搜索时据此取数据);每次打开清空 */\n _inputValue?: string;\n /** 过滤:默认按选项文字与说明匹配(忽略大小写与重音);函数自定义;false 不过滤(远程搜索,items 就是结果) */\n filter?: false | ((item: T, query: string) => boolean);\n /** 正在取结果(远程搜索):没有结果时列表里是骨架行,已有结果时顶部显示进度条 */\n loading?: boolean;\n /** 打开面板的快捷键,默认 'Mod+K';false 不注册 */\n hotkey?: string | false;\n /** 输入框的占位文字,默认取语言包(搜索文档) */\n placeholder?: string;\n /** 面板的名称(读屏念的),默认取语言包(搜索) */\n label?: string;\n /** 触发器:`(props) => <IconButton {...props} icon={Search} aria-label=\"搜索\" />`;默认是一个像搜索框的按钮,false 不渲染 */\n trigger?: ((props: CommandPaletteTriggerProps) => VNodeChild) | false;\n /** 自定义选项内容 */\n renderOption?: (item: T, state: OptionRenderState) => VNodeChild;\n /** 没有匹配时显示的内容;默认是 Empty(文案取语言包) */\n empty?: VNodeChild;\n /** 默认触发器的部件属性 */\n propsTrigger?: Partial<ButtonBaseProps>;\n /** 输入框的部件属性 */\n propsInput?: PartProps<'input'>;\n /** 列表的部件属性 */\n propsListbox?: Partial<ListboxViewProps<T>>;\n}\n\n/** 命令面板的属性;items 不是 ListboxOption 或字符串时要给 toOption */\nexport type CommandPaletteProps<T = ListboxOption> = CommandPaletteBaseProps<T> & OptionMapping<T>;\n\n/** CommandPalette 的样式:dialog、search、input、listbox、trigger、triggerText、triggerLabel */\nexport const commandPaletteStyles = css.create({\n // 停在视口上部而不是正中:结果变多变少时输入框不跳\n dialog: { marginBlockStart: '12dvh' },\n search: {\n display: 'flex',\n alignItems: 'center',\n gap: '_sm',\n paddingBlock: '_sm',\n paddingInline: '_md',\n borderBlockEndWidth: '_xs',\n borderBlockEndStyle: 'solid',\n borderBlockEndColor: '_border',\n fontSize: '_lg',\n },\n input: { paddingBlock: '_sm' },\n listbox: { maxHeight: 'min(24rem, 50dvh)' },\n body: { padding: 0 },\n // 窄屏(40rem 以下,sizes.sm)只留放大镜图标,不挤占顶栏\n trigger: {\n gap: '_sm',\n minWidth: { default: null, [breakpoints.sm]: '12rem' },\n color: '_textMuted',\n cursor: 'pointer',\n },\n triggerText: { display: { default: 'none', [breakpoints.sm]: 'initial' } },\n // 按钮的名称来自这段文字(与看得见的一致,WCAG 2.5.3):窄屏只留图标时视觉上隐藏,读屏仍念得到\n triggerLabel: {\n flexGrow: 1,\n textAlign: 'start',\n whiteSpace: 'nowrap',\n position: { default: 'absolute', [breakpoints.sm]: 'static' },\n width: { default: 1, [breakpoints.sm]: 'auto' },\n height: { default: 1, [breakpoints.sm]: 'auto' },\n overflow: { default: 'hidden', [breakpoints.sm]: 'visible' },\n clipPath: { default: 'inset(50%)', [breakpoints.sm]: 'none' },\n },\n});\n\n/** 命令面板:快捷键打开的搜索对话框 */\nexport const CommandPalette = _component(\n <T,>({\n _open: open = _bindable(false),\n onOpenChange,\n _inputValue: inputValue = _bindable(''),\n items,\n toOption,\n onAction,\n filter,\n loading = false,\n hotkey = 'Mod+K',\n placeholder,\n label,\n trigger,\n renderOption,\n empty,\n propsTrigger,\n propsInput,\n propsListbox,\n ...rest\n }: CommandPaletteProps<T>) => {\n const lang = useLang();\n const textMatcher = useTextMatcher();\n const listId = useId();\n const appearance = useControlAppearance(() => ({}));\n const optionOf = (item: T) => toListboxOption(item, toOption);\n const setOpen = openSetter(\n () => open,\n (next) => (open = next),\n () => onOpenChange,\n );\n\n function matches(item: T, text: string) {\n if (filter === false) return true;\n if (filter) return filter(item, text);\n const match = textMatcher();\n const option = optionOf(item);\n return match(option.label, text) || match(option.description ?? '', text);\n }\n\n const list = useListState({\n items: () => (inputValue ? items.filter((item) => matches(item, inputValue)) : items),\n toOption: optionOf,\n // 面板不记选择:选中就是执行\n selected: () => [],\n mode: () => 'single',\n onChange: () => {},\n });\n\n // 每次打开从空白开始;输入变了高亮第一个结果\n watch(\n () => open,\n (isOpen) => {\n if (isOpen) inputValue = '';\n },\n );\n // 远程搜索时结果晚到:结果变了也高亮第一个\n watch([() => inputValue, () => open, () => list.options.length], () => list.move('first'), {\n flush: 'post',\n });\n\n useHotkeys(() => (hotkey ? { [hotkey]: (event) => setOpen(!open, 'imperative', event) } : {}), {\n inInputs: true,\n });\n\n function pick(value: string, event?: Event) {\n const entry = list.options[list.indexOf(value)];\n if (!entry || entry.disabled) return;\n setOpen(false, 'select', event);\n onAction(entry.item);\n }\n\n function onKeydown(event: KeyboardEvent) {\n if (handleListKeydown(list, event, { homeEnd: false }, (value) => pick(value, event)))\n event.preventDefault();\n }\n\n const triggerProps = (): CommandPaletteTriggerProps => ({\n onClick: (event: MouseEvent) => setOpen(true, 'trigger', event),\n 'aria-haspopup': 'dialog',\n 'aria-expanded': open,\n });\n\n /** 默认的触发按钮:画成输入框外壳(只取样式,部件标记归面板自己) */\n function renderTrigger(shell: ControlParts) {\n if (trigger === false) return null;\n if (trigger) return trigger(triggerProps());\n const text = placeholder ?? lang.searchPlaceholder;\n return (\n <ButtonBase\n {...mergePartProps<ButtonBaseProps>(\n {\n sx: [shell.root.sx, commandPaletteStyles.trigger],\n // 快捷键由 aria-keyshortcuts 告诉读屏,显示它的 Kbd 不算进名称\n 'aria-keyshortcuts': hotkey ? ariaKeyShortcut(hotkey) : undefined,\n },\n propsTrigger,\n triggerProps(),\n )}\n >\n <Icon icon={Search} sx={shell.icon.sx} />\n <span {...mergeAttrs<'span'>({ sx: commandPaletteStyles.triggerLabel })}>{text}</span>\n {hotkey && (\n <Kbd\n sx={commandPaletteStyles.triggerText}\n aria-hidden={ariaKeyShortcut(hotkey) ? 'true' : undefined}\n >\n {formatHotkey(hotkey)}\n </Kbd>\n )}\n </ButtonBase>\n );\n }\n\n return () => {\n const shell = controlParts(appearance(), {});\n return (\n <>\n {renderTrigger(shell)}\n <Dialog\n {...mergePartProps<DialogProps>(\n {\n title: label ?? lang.searchLabel,\n sx: commandPaletteStyles.dialog,\n size: 'sm',\n closable: false,\n // 标题只给读屏:面板的名称\n propsHeader: { sx: visuallyHiddenStyles.hidden },\n propsBody: { propsContent: { sx: commandPaletteStyles.body } },\n },\n rest as Partial<DialogProps>,\n )}\n _open={open}\n onOpenChange={onOpenChange}\n >\n <div {...mergeAttrs<'div'>({ sx: commandPaletteStyles.search })}>\n <Icon icon={Search} sx={shell.icon.sx} />\n {/* _bind 编译为 v-model:输入法组合期间不过滤 */}\n <input\n {...mergeAttrs<'input'>(\n { sx: [shell.input.sx, commandPaletteStyles.input] },\n propsInput,\n {\n role: 'combobox',\n type: 'search',\n autofocus: true,\n autocomplete: 'off',\n enterkeyhint: 'go',\n placeholder: placeholder ?? lang.searchPlaceholder,\n 'aria-label': label ?? lang.searchLabel,\n 'aria-autocomplete': 'list',\n 'aria-expanded': true,\n 'aria-controls': listId,\n 'aria-activedescendant': activeOptionId(list, listId),\n onKeydown,\n },\n )}\n _value={inputValue}\n />\n </div>\n <ListboxView\n {...mergePartProps<ListboxViewProps<T>>(\n {\n sx: commandPaletteStyles.listbox,\n list,\n id: listId,\n renderOption,\n empty,\n loading,\n propsEmpty: { description: lang.searchEmpty },\n },\n propsListbox,\n { id: listId, 'aria-label': label ?? lang.searchLabel, onPick: pick },\n )}\n />\n </Dialog>\n </>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CommandPalette.js","names":["_vtp_bindable","_vtp_readonlyProp","_vtp_restProps","_createVNode","_Fragment","_mergeProps","_withDirectives","_vModelText"],"sources":["../../src/overlay/CommandPalette.tsx"],"sourcesContent":["/**\n * 命令面板:Mod+K(macOS 上 ⌘K,其他系统 Ctrl+K)或点触发按钮打开的搜索对话框,边输入边过滤,回车打开。\n * 文档站的站内搜索、后台的“跳转到…”都用它;数据由使用方给,选中后做什么也由使用方决定。\n *\n * <CommandPalette\n * items={pages}\n * toOption={(page) => ({ value: page.path, label: page.title, description: page.summary, group: page.section })}\n * onAction={(page) => router.push(page.path)}\n * />\n *\n * 结构是 Dialog(模态、焦点循环、Esc 关闭)里放一个 combobox 输入框与 ListboxView:\n * 焦点一直在输入框上,↑↓ / Page Up / Page Down 移动高亮(aria-activedescendant),Enter 选中;\n * 数据层与 Select、Combobox 相同(useListState),选项多时虚拟滚动。\n * 远程搜索与 Combobox 一样:_inputValue={keyword} 拿到输入,filter={false} 不再本地过滤,取数据时给 loading。\n */\nimport { Search } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { useId, watch, type VNodeChild } from 'vue';\nimport { ButtonBase, type ButtonBaseProps } from '../base/ButtonBase';\nimport { Icon } from '../base/Icon';\nimport { Kbd } from '../base/Kbd';\nimport { visuallyHiddenStyles } from '../base/VisuallyHidden';\nimport { useLang, useTextMatcher } from '../config/context';\nimport { controlParts, useControlAppearance, type ControlParts } from '../input/control';\nimport {\n ListboxView,\n activeOptionId,\n type ListboxViewProps,\n type OptionRenderState,\n} from '../input/Listbox';\nimport {\n handleListKeydown,\n useListState,\n type ListboxOption,\n type OptionMapping,\n toListboxOption,\n} from '../input/list-state';\nimport { ariaKeyShortcut, formatHotkey, useHotkeys } from '../utils/dom/hotkeys';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { breakpoints } from '../config/theme/tokens';\nimport { openSetter } from '../utils/dom/open-change';\nimport { Dialog, type DialogProps } from './Dialog';\n\n/** 交给触发器的属性:展开到按钮上 */\nexport interface CommandPaletteTriggerProps {\n /** 打开面板 */\n onClick: (event: MouseEvent) => void;\n /** 告诉读屏这个按钮会打开对话框 */\n 'aria-haspopup': 'dialog';\n /** 面板是否打开 */\n 'aria-expanded': boolean;\n}\n\n/** 与数据类型无关的属性;未声明的属性落到对话框(dialog)上 */\nexport interface CommandPaletteBaseProps<T> extends Omit<\n DialogProps,\n 'open' | 'title' | 'description' | 'footer' | 'children' | 'onSelect'\n> {\n /** 是否打开:父级可以写 _open={x};快捷键、触发按钮、选中、Esc 都会改它 */\n _open?: boolean;\n /** 数据:ListboxOption、字符串,或任意数据加 toOption */\n items: readonly T[];\n /** 执行了一项(回车或点击):面板随即关闭 */\n onAction: (item: T) => void;\n /** 输入框里的文字:父级可以写 _inputValue={x}(远程搜索时据此取数据);每次打开清空 */\n _inputValue?: string;\n /** 过滤:默认按选项文字与说明匹配(忽略大小写与重音);函数自定义;false 不过滤(远程搜索,items 就是结果) */\n filter?: false | ((item: T, query: string) => boolean);\n /** 正在取结果(远程搜索):列表里换成骨架行 */\n loading?: boolean;\n /** 打开面板的快捷键,默认 'Mod+K';false 不注册 */\n hotkey?: string | false;\n /** 输入框的占位文字,默认取语言包(搜索文档) */\n placeholder?: string;\n /** 面板的名称(读屏念的),默认取语言包(搜索) */\n label?: string;\n /** 触发器:`(props) => <IconButton {...props} icon={Search} aria-label=\"搜索\" />`;默认是一个像搜索框的按钮,false 不渲染 */\n trigger?: ((props: CommandPaletteTriggerProps) => VNodeChild) | false;\n /** 自定义选项内容 */\n renderOption?: (item: T, state: OptionRenderState) => VNodeChild;\n /** 没有匹配时显示的内容;默认是 Empty(文案取语言包) */\n empty?: VNodeChild;\n /** 默认触发器的部件属性 */\n propsTrigger?: Partial<ButtonBaseProps>;\n /** 输入框的部件属性 */\n propsInput?: PartProps<'input'>;\n /** 列表的部件属性 */\n propsListbox?: Partial<ListboxViewProps<T>>;\n}\n\n/** 命令面板的属性;items 不是 ListboxOption 或字符串时要给 toOption */\nexport type CommandPaletteProps<T = ListboxOption> = CommandPaletteBaseProps<T> & OptionMapping<T>;\n\n/** CommandPalette 的样式:dialog、search、input、listbox、trigger、triggerText、triggerLabel */\nexport const commandPaletteStyles = css.create({\n // 停在视口上部而不是正中:结果变多变少时输入框不跳\n dialog: { marginBlockStart: '12dvh' },\n search: {\n display: 'flex',\n alignItems: 'center',\n gap: '_sm',\n paddingBlock: '_sm',\n paddingInline: '_md',\n borderBlockEndWidth: '_xs',\n borderBlockEndStyle: 'solid',\n borderBlockEndColor: '_border',\n fontSize: '_lg',\n },\n input: { paddingBlock: '_sm' },\n listbox: { maxHeight: 'min(24rem, 50dvh)' },\n body: { padding: 0 },\n // 窄屏(40rem 以下,sizes.sm)只留放大镜图标,不挤占顶栏\n trigger: {\n gap: '_sm',\n minWidth: { default: null, [breakpoints.sm]: '12rem' },\n color: '_textMuted',\n cursor: 'pointer',\n },\n triggerText: { display: { default: 'none', [breakpoints.sm]: 'initial' } },\n // 按钮的名称来自这段文字(与看得见的一致,WCAG 2.5.3):窄屏只留图标时视觉上隐藏,读屏仍念得到\n triggerLabel: {\n flexGrow: 1,\n textAlign: 'start',\n whiteSpace: 'nowrap',\n position: { default: 'absolute', [breakpoints.sm]: 'static' },\n width: { default: 1, [breakpoints.sm]: 'auto' },\n height: { default: 1, [breakpoints.sm]: 'auto' },\n overflow: { default: 'hidden', [breakpoints.sm]: 'visible' },\n clipPath: { default: 'inset(50%)', [breakpoints.sm]: 'none' },\n },\n});\n\n/** 命令面板:快捷键打开的搜索对话框 */\nexport const CommandPalette = _component(\n <T,>({\n _open: open = _bindable(false),\n onOpenChange,\n _inputValue: inputValue = _bindable(''),\n items,\n toOption,\n onAction,\n filter,\n loading = false,\n hotkey = 'Mod+K',\n placeholder,\n label,\n trigger,\n renderOption,\n empty,\n propsTrigger,\n propsInput,\n propsListbox,\n ...rest\n }: CommandPaletteProps<T>) => {\n const lang = useLang();\n const textMatcher = useTextMatcher();\n const listId = useId();\n const appearance = useControlAppearance(() => ({}));\n const optionOf = (item: T) => toListboxOption(item, toOption);\n const setOpen = openSetter(\n () => open,\n (next) => (open = next),\n () => onOpenChange,\n );\n\n function matches(item: T, text: string) {\n if (filter === false) return true;\n if (filter) return filter(item, text);\n const match = textMatcher();\n const option = optionOf(item);\n return match(option.label, text) || match(option.description ?? '', text);\n }\n\n const list = useListState({\n items: () => (inputValue ? items.filter((item) => matches(item, inputValue)) : items),\n toOption: optionOf,\n // 面板不记选择:选中就是执行\n selected: () => [],\n mode: () => 'single',\n onChange: () => {},\n });\n\n // 每次打开从空白开始;输入变了高亮第一个结果\n watch(\n () => open,\n (isOpen) => {\n if (isOpen) inputValue = '';\n },\n );\n // 远程搜索时结果晚到:结果变了也高亮第一个\n watch([() => inputValue, () => open, () => list.options.length], () => list.move('first'), {\n flush: 'post',\n });\n\n useHotkeys(() => (hotkey ? { [hotkey]: (event) => setOpen(!open, 'imperative', event) } : {}), {\n inInputs: true,\n });\n\n function pick(value: string, event?: Event) {\n const entry = list.options[list.indexOf(value)];\n if (!entry || entry.disabled) return;\n setOpen(false, 'select', event);\n onAction(entry.item);\n }\n\n function onKeydown(event: KeyboardEvent) {\n if (handleListKeydown(list, event, { homeEnd: false }, (value) => pick(value, event)))\n event.preventDefault();\n }\n\n const triggerProps = (): CommandPaletteTriggerProps => ({\n onClick: (event: MouseEvent) => setOpen(true, 'trigger', event),\n 'aria-haspopup': 'dialog',\n 'aria-expanded': open,\n });\n\n /** 默认的触发按钮:画成输入框外壳(只取样式,部件标记归面板自己) */\n function renderTrigger(shell: ControlParts) {\n if (trigger === false) return null;\n if (trigger) return trigger(triggerProps());\n const text = placeholder ?? lang.searchPlaceholder;\n return (\n <ButtonBase\n {...mergePartProps<ButtonBaseProps>(\n {\n sx: [shell.root.sx, commandPaletteStyles.trigger],\n // 快捷键由 aria-keyshortcuts 告诉读屏,显示它的 Kbd 不算进名称\n 'aria-keyshortcuts': hotkey ? ariaKeyShortcut(hotkey) : undefined,\n },\n propsTrigger,\n triggerProps(),\n )}\n >\n <Icon icon={Search} sx={shell.icon.sx} />\n <span {...mergeAttrs<'span'>({ sx: commandPaletteStyles.triggerLabel })}>{text}</span>\n {hotkey && (\n <Kbd\n sx={commandPaletteStyles.triggerText}\n aria-hidden={ariaKeyShortcut(hotkey) ? 'true' : undefined}\n >\n {formatHotkey(hotkey)}\n </Kbd>\n )}\n </ButtonBase>\n );\n }\n\n return () => {\n const shell = controlParts(appearance(), {});\n return (\n <>\n {renderTrigger(shell)}\n <Dialog\n {...mergePartProps<DialogProps>(\n {\n title: label ?? lang.searchLabel,\n sx: commandPaletteStyles.dialog,\n size: 'sm',\n closable: false,\n // 标题只给读屏:面板的名称\n propsHeader: { sx: visuallyHiddenStyles.hidden },\n propsBody: { propsContent: { sx: commandPaletteStyles.body } },\n },\n rest as Partial<DialogProps>,\n )}\n _open={open}\n onOpenChange={onOpenChange}\n >\n <div {...mergeAttrs<'div'>({ sx: commandPaletteStyles.search })}>\n <Icon icon={Search} sx={shell.icon.sx} />\n {/* _bind 编译为 v-model:输入法组合期间不过滤 */}\n <input\n {...mergeAttrs<'input'>(\n { sx: [shell.input.sx, commandPaletteStyles.input] },\n propsInput,\n {\n role: 'combobox',\n type: 'search',\n autofocus: true,\n autocomplete: 'off',\n enterkeyhint: 'go',\n placeholder: placeholder ?? lang.searchPlaceholder,\n 'aria-label': label ?? lang.searchLabel,\n 'aria-autocomplete': 'list',\n 'aria-expanded': true,\n 'aria-controls': listId,\n 'aria-activedescendant': activeOptionId(list, listId),\n onKeydown,\n },\n )}\n _value={inputValue}\n />\n </div>\n <ListboxView\n {...mergePartProps<ListboxViewProps<T>>(\n {\n sx: commandPaletteStyles.listbox,\n list,\n id: listId,\n renderOption,\n empty,\n loading,\n propsEmpty: { description: lang.searchEmpty },\n },\n propsListbox,\n { id: listId, 'aria-label': label ?? lang.searchLabel, onPick: pick },\n )}\n />\n </Dialog>\n </>\n );\n };\n },\n);\n"],"mappings":"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"}
|
package/dist/overlay/detached.js
CHANGED
|
@@ -5,10 +5,23 @@ import { createVNode, getCurrentInstance, getCurrentScope, h, onScopeDispose, re
|
|
|
5
5
|
import { _component, readonlyProp } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
6
6
|
//#region src/overlay/detached.tsx
|
|
7
7
|
/** 交给对话框内容的控制:开着没有、关上了 */
|
|
8
|
-
/**
|
|
8
|
+
/** 组件树之外的根数到第几个了:给每个根一个自己的 useId 命名段 */
|
|
9
|
+
var detachedRoots = 0;
|
|
10
|
+
/**
|
|
11
|
+
* 组件树之外的根:提供调用处的配置,内容包在 ConfigScope 里(主题变量、语言)。
|
|
12
|
+
* render() 出来的根,useId 从头计数、前缀又和应用相同(共用 appContext),生成的 id 会和页面上已有的撞上
|
|
13
|
+
* (表单对话框的 label 关联到页面里另一个控件)。照 Vue 处理异步边界的做法(markAsyncBoundary)换一个命名段:
|
|
14
|
+
* 子组件沿用父组件的 ids,所以在根上换一次就够了。
|
|
15
|
+
*/
|
|
9
16
|
var DetachedRoot = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
10
17
|
const config = readonlyProp(_props, "config");
|
|
11
18
|
const content = readonlyProp(_props, "content");
|
|
19
|
+
const instance = getCurrentInstance();
|
|
20
|
+
if (instance?.ids) instance.ids = [
|
|
21
|
+
`d${++detachedRoots}-`,
|
|
22
|
+
0,
|
|
23
|
+
0
|
|
24
|
+
];
|
|
12
25
|
ConfigContext.provide(config.value);
|
|
13
26
|
return () => createVNode(ConfigScope, { "key": "__site0" }, {
|
|
14
27
|
default: () => [content.value()],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detached.js","names":["_vtp_readonlyProp","_createVNode"],"sources":["../../src/overlay/detached.tsx"],"sourcesContent":["/**\n * 渲染到组件树之外:沿用调用处的配置(主题、语言、默认值,经 ConfigScope)与应用上下文(全局组件),\n * 生命周期跟着调用的组件——它卸载时一起收掉,不留孤儿。\n *\n * const renderDetached = useDetachedRender(); // setup
|
|
1
|
+
{"version":3,"file":"detached.js","names":["_vtp_readonlyProp","_createVNode"],"sources":["../../src/overlay/detached.tsx"],"sourcesContent":["/**\n * 渲染到组件树之外:沿用调用处的配置(主题、语言、默认值,经 ConfigScope)与应用上下文(全局组件),\n * 生命周期跟着调用的组件——它卸载时一起收掉,不留孤儿。\n *\n * const renderDetached = useDetachedRender(); // setup 里;第三方库管的元素(如别的图表库的提示框)\n * renderDetached(<Tip />, element); // 再调一次换内容,传 null 卸载;组件卸载时自动卸载\n *\n * const mount = useDetachedDialog(); // setup 里;命令式的对话框(useConfirm、useFormDialog)\n * mount((host) => <Dialog _open={host.open} onClose={host.closed}>…</Dialog>);\n *\n * 对话框渲染到 body 末尾,关上后等离场过渡播完再卸载;调用的组件卸载时(例如切换了路由)把开着的对话框关上,\n * 照常走关闭的流程,等着结果的 `await confirm()` 也拿到结果。\n * useDetachedOverlay 在 useDetachedDialog 上面加一层句柄(close、update、closed),useDialog、useDrawer 用它。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport {\n getCurrentInstance,\n getCurrentScope,\n h,\n onScopeDispose,\n render,\n shallowRef,\n type AppContext,\n type VNodeChild,\n} from 'vue';\nimport { ConfigContext, useConfig, type UiConfig } from '../config/context';\nimport { ConfigScope } from '../config/Provider';\nimport type { OpenChangeProps, OpenChangeReasons } from '../utils/dom/open-change';\nimport { afterTransitions } from '../utils/style/transitions';\n\n/** 交给对话框内容的控制:开着没有、关上了 */\nexport interface DetachedDialogHost {\n /** 对话框开着:对话框写 _open={host.open},改成 false 就关上 */\n open: boolean;\n /** 对话框关上了(原生 close 事件里调用):等离场过渡播完卸载 */\n closed: () => void;\n}\n\n/** 组件树之外的根数到第几个了:给每个根一个自己的 useId 命名段 */\nlet detachedRoots = 0;\n\n/**\n * 组件树之外的根:提供调用处的配置,内容包在 ConfigScope 里(主题变量、语言)。\n * render() 出来的根,useId 从头计数、前缀又和应用相同(共用 appContext),生成的 id 会和页面上已有的撞上\n * (表单对话框的 label 关联到页面里另一个控件)。照 Vue 处理异步边界的做法(markAsyncBoundary)换一个命名段:\n * 子组件沿用父组件的 ids,所以在根上换一次就够了。\n */\nconst DetachedRoot = _component(\n ({ config, content }: { config: UiConfig; content: () => VNodeChild }) => {\n const instance = getCurrentInstance() as { ids?: [string, number, number] } | null;\n if (instance?.ids) instance.ids = [`d${++detachedRoots}-`, 0, 0];\n ConfigContext.provide(config);\n return () => <ConfigScope>{content()}</ConfigScope>;\n },\n);\n\n/** 调用处的配置与应用上下文(在 setup 里取),交给组件树之外的根 */\nfunction captureContext() {\n return { config: useConfig(), appContext: getCurrentInstance()?.appContext ?? null };\n}\n\nfunction renderInto(\n container: Element,\n { config, appContext }: { config: UiConfig; appContext: AppContext | null },\n content: () => VNodeChild,\n) {\n const vnode = h(DetachedRoot, { config, content });\n vnode.appContext = appContext;\n render(vnode, container);\n}\n\n/** 在 setup 里调用,返回 renderDetached(content, container):渲染到组件树之外的元素里,调用的组件卸载时一起卸载 */\nexport function useDetachedRender(): (content: VNodeChild | null, container: Element) => void {\n const context = captureContext();\n const containers = new Set<Element>();\n if (getCurrentScope()) {\n onScopeDispose(() => {\n for (const container of containers) render(null, container);\n containers.clear();\n });\n }\n return (content, container) => {\n if (content == null) {\n containers.delete(container);\n render(null, container);\n return;\n }\n containers.add(container);\n renderInto(container, context, () => content);\n };\n}\n\n/** 在 setup 里调用,返回 mount(content):把对话框渲染到组件树之外 */\nexport function useDetachedDialog(): (content: (host: DetachedDialogHost) => VNodeChild) => void {\n const context = captureContext();\n /** 开着的对话框:调用的组件卸载时关上 */\n const live = new Set<DetachedDialogHost>();\n if (getCurrentScope()) {\n onScopeDispose(() => {\n for (const host of live) host.open = false;\n });\n }\n\n return (content) => {\n const container = document.createElement('div');\n document.body.append(container);\n const open = shallowRef(true);\n const host: DetachedDialogHost = {\n get open() {\n return open.value;\n },\n set open(value) {\n open.value = value;\n },\n closed() {\n live.delete(host);\n const dialog = container.querySelector('dialog');\n void (dialog ? afterTransitions(dialog) : Promise.resolve()).then(() => {\n render(null, container);\n container.remove();\n });\n },\n };\n live.add(host);\n renderInto(container, context, () => content(host));\n };\n}\n\n/** 命令式浮层(useDialog、useDrawer)的选项:浮层的属性(_open 由句柄管理),content 是正文 */\nexport type DetachedOverlayOptions<P> = Omit<P, '_open' | 'children'> & {\n /** 正文;给函数时拿到句柄,正文里的按钮可以自己关上、改标题 */\n content?: VNodeChild | ((handle: DetachedOverlayHandle<P>) => VNodeChild);\n};\n\n/** useDialog().open、useDrawer().open 返回的句柄;方法不依赖 this,可以直接传出去(onSaved={handle.close}) */\nexport interface DetachedOverlayHandle<P> {\n /** 关上(onOpenChange 的原因是 imperative);已经关上时什么也不做 */\n readonly close: () => void;\n /** 改属性(标题、宽度、确认按钮的文字、正文……),没写的保持不变 */\n readonly update: (patch: Partial<DetachedOverlayOptions<P>>) => void;\n /** 关上之后 resolve,得到关上的原因:确认是 confirm,Esc、遮罩、关闭按钮、close() 各是 escape、outside、close、imperative */\n readonly closed: Promise<keyof OpenChangeReasons>;\n}\n\n/** 原生 dialog 的 close 事件 */\ntype DialogCloseEvent = Event & { currentTarget: HTMLDialogElement };\n\n/** 浮层要支持的:onOpenChange 报告原因,onClose 落到原生 dialog 上 */\ninterface DetachedOverlayProps extends OpenChangeProps {\n onClose?: (event: DialogCloseEvent) => void;\n}\n\n/**\n * 命令式浮层:在 setup 里调用,返回 open(options)。renderOverlay 把属性、开关与正文交给具体的浮层(Dialog、Drawer):\n *\n * useDetachedOverlay<DialogProps>((props, host, children) => <Dialog {...props} _open={host.open}>{children}</Dialog>);\n */\nexport function useDetachedOverlay<P extends DetachedOverlayProps>(\n renderOverlay: (\n props: Omit<P, '_open' | 'children'>,\n host: DetachedDialogHost,\n children: VNodeChild,\n ) => VNodeChild,\n): (options: DetachedOverlayOptions<P>) => DetachedOverlayHandle<P> {\n const mount = useDetachedDialog();\n return (options) => {\n const state = shallowRef(options);\n let host: DetachedDialogHost | undefined;\n let reason: keyof OpenChangeReasons = 'imperative';\n let resolveClosed!: (reason: keyof OpenChangeReasons) => void;\n const handle: DetachedOverlayHandle<P> = {\n close: () => {\n if (!host?.open) return;\n reason = 'imperative';\n host.open = false;\n state.value.onOpenChange?.(false, { reason });\n },\n update: (patch) => {\n state.value = { ...state.value, ...patch };\n },\n closed: new Promise((resolve) => (resolveClosed = resolve)),\n };\n mount((current) => {\n host = current;\n const { content, onOpenChange, onClose, ...props } = state.value;\n const overlayProps = {\n ...props,\n onOpenChange: (open, details) => {\n if (!open) reason = details.reason;\n onOpenChange?.(open, details);\n },\n onClose: (event: DialogCloseEvent) => {\n onClose?.(event);\n // 原生 close 事件先到这里、后到浮层自己(它在那里报告原因):等它报告完再 resolve\n queueMicrotask(() => resolveClosed(reason));\n current.closed();\n },\n } as Omit<P, '_open' | 'children'> & DetachedOverlayProps;\n return renderOverlay(\n overlayProps,\n current,\n typeof content === 'function' ? content(handle) : content,\n );\n });\n options.onOpenChange?.(true, { reason: 'imperative' });\n return handle;\n };\n}\n"],"mappings":"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"}
|
|
@@ -161,7 +161,6 @@ var QueryTable = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
161
161
|
createVNode(IconButton, toolButton({
|
|
162
162
|
icon: RefreshCw,
|
|
163
163
|
"aria-label": messages.queryTableRefresh,
|
|
164
|
-
loading: query.value.loading,
|
|
165
164
|
onClick: () => void query.value.refresh()
|
|
166
165
|
}), null),
|
|
167
166
|
createVNode(Menu$1, {
|
|
@@ -221,7 +220,6 @@ var QueryTable = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
221
220
|
return createVNode("div", mergeAttrs({ sx: [queryTableStyles.root, fill.value && queryTableStyles.rootFill] }, rest), [filters.value && createVNode(FilterBar, mergePartProps({ "aria-label": messages.queryTableSearch(title.value) }, propsFilterBar.value, {
|
|
222
221
|
onSearch: search,
|
|
223
222
|
onReset: reset,
|
|
224
|
-
loading: query.value.loading,
|
|
225
223
|
more: moreFilters.value?.(draft),
|
|
226
224
|
start: fullscreen.active ? void 0 : createVNode("div", css.attrs(queryTableStyles.titleInline), [title.value]),
|
|
227
225
|
end: fullscreen.active ? void 0 : createVNode("div", mergeAttrs({ sx: queryTableStyles.toolbarInline }, propsToolbar.value), [toolbar.value, renderTools()])
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryTable.js","names":["_isVNode","_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vtp_untrack","_vue_shallowRef","_createVNode","Menu","_mergeProps"],"sources":["../../src/patterns/QueryTable.tsx"],"sourcesContent":["/**\n * 查询表格(列表页的整块):查询栏(FilterBar)+ 工具栏(标题、自己的按钮、刷新、行高、列设置、全屏)+ 批量操作栏 + 数据表格 + 分页栏。\n * 有查询栏时工具栏并进查询栏那一行,压成一行:标题 条件 查询 重置 展开 … 自己的按钮 刷新 行高 列设置 全屏;\n * 条件多时把不常用的放进 moreFilters,点“展开”后更多条件排在这一行下面。没有查询栏、或全屏(全屏的只有表格那一块)时,\n * 工具栏是表格上方单独的一行。fill 时整块占满到窗口底边(ViewportFill),行多了在表格里滚动,分页留在一屏里。\n * 状态都在 useTableQuery 里(服务端分页、排序、条件)。同 Ant Design ProComponents 的 ProTable。\n *\n * const tasks = useTableQuery(fetchTasks, { filters: { keyword: '', status: '' }, pageSize: 20 });\n * <QueryTable\n * query={tasks} columns={columns} getKey={(task) => task.id} title=\"任务台账\" persistKey=\"tasks\"\n * filters={(draft) => <Field label=\"关键字\"><TextField _value={draft.keyword} /></Field>}\n * toolbar={<Button icon={Plus}>新建</Button>}\n * />\n *\n * - 查询栏的条件改的是草稿(draft),点“查询”(或回车)才交给 query.search;“重置”回到 blankFilters(不写时是一开始的条件)并查询。\n * - 表头排序交给服务端(manualSort,写回 query.sort;multiSort 时按住 Shift 追加一列,写回 query.sorts),\n * 表头筛选也交给服务端(manualFilter,写回 query.columnFilters;列的 filter 要写 options,否则只列出当前这页出现过的值),\n * 分页栏绑定 query.page / pageSize / total。fetcher 用 tableParams(query, columns) 把这些写成接口的参数。\n * - persistKey:列设置(显示、顺序、固定、列宽)与行高记在浏览器里,下次打开还是这样。\n * - selectionMode 时勾选行,表格上方出现批量操作栏,batchActions 拿到已选的 key 给出按钮。\n *\n * 部件:Root(div)、FilterBar、Panel(工具栏与表格所在的区域,全屏的就是它)、Toolbar(div)、Title(div)、\n * Table(DataTable)、SelectionBar、Pagination(PaginationBar)。\n */\nimport { Maximize, Minimize, RefreshCw, Rows3 } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { reactive, shallowRef, type Ref, type VNodeChild } from 'vue';\nimport { Button } from '../base/Button';\nimport { IconButton, type IconButtonProps } from '../base/IconButton';\nimport { useLang } from '../config/context';\nimport { useMessages } from '../config/lang/messages';\nimport { Alert, type AlertProps } from '../feedback/Alert';\nimport { PaginationBar, type PaginationBarProps } from '../navigation/PaginationBar';\nimport { Menu, MenuRadioGroup, MenuRadioItem } from '../overlay/Menu';\nimport { ColumnSettings } from '../table/ColumnSettings';\nimport type { ColumnStickies, DataTableColumn } from '../table/columns';\nimport { ViewportFill } from '../layout/ViewportFill';\nimport { DataTable, type DataTableProps } from '../table/DataTable';\nimport { SelectionBar, type SelectionBarProps } from '../table/SelectionBar';\nimport type { DataTableDensities } from '../table/table-styles';\nimport type { TablePage, TableQueryState } from '../table/use-table-query';\nimport { cloneValue } from '../utils/data/clone';\nimport { useFullscreen } from '../utils/dom/fullscreen';\nimport { usePersistentState } from '../utils/dom/storage';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { errorMessage } from '../utils/request/error-message';\nimport { FilterBar, type FilterBarProps } from './FilterBar';\nimport { patternsMessages } from './messages';\n\n/** 未声明的属性落到外层 div 上 */\nexport interface QueryTableProps<T, Filters extends object> extends PartProps<'div'> {\n /** 列表状态:useTableQuery 的返回值 */\n query: TableQueryState<TablePage<T>, Filters>;\n /** 列定义 */\n columns: readonly DataTableColumn<T>[];\n /** 行的 key */\n getKey: (row: T, index: number) => string;\n /** 表格的名称(工具栏起始边的标题,也是表格的 aria-label) */\n title: string;\n /** 查询栏的条件:拿到草稿,控件绑定它的字段(`_value={draft.keyword}`);不写时没有查询栏 */\n filters?: (draft: Filters) => VNodeChild;\n /** 收起的条件(不常用的),点“展开”才显示 */\n moreFilters?: (draft: Filters) => VNodeChild;\n /** “重置”回到的条件;不写时是 query 一开始的条件 */\n blankFilters?: Filters;\n /** 占满到窗口底边(列表页的整页):行多了在表格里滚动,分页一直在一屏里 */\n fill?: boolean;\n /** 工具栏上自己的按钮(新建、导出),在标题之后、内置按钮之前 */\n toolbar?: VNodeChild;\n /** 列设置与行高记在浏览器里的键(localStorage,自动加前缀);不写时只在这次打开里有效 */\n persistKey?: string;\n /** 多列排序:按住 Shift 点列标题追加一列(同 DataTable 的 multiSort),排序写在 query.sorts */\n multiSort?: boolean;\n /** 勾选行:多选时表格上方出现批量操作栏 */\n selectionMode?: DataTableProps<T>['selectionMode'];\n /** 已选行的 key:父级写 _selected={keys} */\n _selected?: string[];\n /** 批量操作栏里的按钮:拿到已选的 key */\n batchActions?: (keys: readonly string[]) => VNodeChild;\n /** 查询栏(FilterBar)的部件属性 */\n propsFilterBar?: Partial<FilterBarProps>;\n /** 工具栏与表格所在区域的部件属性(全屏的就是它) */\n propsPanel?: PartProps<'div'>;\n /** 工具栏的部件属性 */\n propsToolbar?: PartProps<'div'>;\n /** 表格(DataTable)的部件属性:列宽、详情行、行的色调等直接写在这里 */\n propsTable?: Partial<DataTableProps<T>>;\n /** 批量操作栏的部件属性 */\n propsSelectionBar?: Partial<SelectionBarProps>;\n /** 分页栏的部件属性 */\n propsPagination?: Partial<PaginationBarProps>;\n /** 加载失败时表格上方的提示条(Alert)的部件属性 */\n propsError?: Partial<AlertProps>;\n /** 工具栏内置按钮(刷新、行高、全屏)的部件属性 */\n propsToolbarButton?: Partial<IconButtonProps>;\n}\n\n/** QueryTable 的样式:root、rootFill、panel、panelFullscreen、panelFill、toolbar、title、tools、titleInline、toolbarInline */\nexport const queryTableStyles = css.create({\n root: { display: 'flex', flexDirection: 'column', gap: '_md', minWidth: 0 },\n panel: {\n display: 'flex',\n flexDirection: 'column',\n gap: '_sm',\n minWidth: 0,\n backgroundColor: '_surface',\n },\n // 全屏时铺满屏幕、四周留白,表格占满剩下的高度\n panelFullscreen: { padding: '_lg', overflow: 'auto' },\n // fill 时外面的高度定了,整块与区域可以压矮,压不下的在表格里滚动\n rootFill: { flexGrow: 1, minHeight: 0 },\n panelFill: { minHeight: 0 },\n toolbar: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: '_sm' },\n title: { flexGrow: 1, minWidth: 0, fontWeight: '_semibold', fontSize: '_lg' },\n tools: { display: 'flex', alignItems: 'center', gap: '_xs' },\n // 并进查询栏那一行时:标题不撑开,按钮靠在一起\n titleInline: { flexShrink: 0, fontWeight: '_semibold', fontSize: '_lg' },\n toolbarInline: { display: 'flex', alignItems: 'center', gap: '_sm' },\n});\n\n/** 存在浏览器里(给了 persistKey)或只在这次打开里有效的状态 */\nfunction settingRef<V>(key: string | undefined, name: string, initial: V): Ref<V> {\n return key ? usePersistentState(`${key}:${name}`, initial) : shallowRef(initial);\n}\n\n/** 查询表格:查询栏 + 工具栏 + 表格 + 分页,状态来自 useTableQuery */\nexport const QueryTable = _component(\n <T, Filters extends object>({\n query,\n columns,\n getKey,\n title,\n filters,\n moreFilters,\n blankFilters,\n fill = false,\n toolbar,\n persistKey,\n multiSort = false,\n selectionMode,\n _selected: selected = _bindable<string[]>([]),\n batchActions,\n propsFilterBar,\n propsPanel,\n propsToolbar,\n propsTable,\n propsSelectionBar,\n propsPagination,\n propsError,\n propsToolbarButton,\n ...rest\n }: QueryTableProps<T, Filters>) => {\n const messages = useMessages('patterns', patternsMessages);\n const lang = useLang();\n // 查询栏改的是草稿:点“查询”才交给 query\n // 用 cloneValue 不用 structuredClone:条件里的日期(CalendarDate)、Decimal 要保持原样\n const initial = cloneValue(_untrack(query.filters));\n const draft = reactive({ ...initial }) as Filters;\n const key = _untrack(persistKey);\n let hidden = _fromRef(settingRef<string[]>(key, 'hidden', []));\n let order = _fromRef(settingRef<string[]>(key, 'order', []));\n let pins = _fromRef(settingRef<Record<string, keyof ColumnStickies | null>>(key, 'pins', {}));\n let widths = _fromRef(settingRef<Record<string, number>>(key, 'widths', {}));\n let density = _fromRef(settingRef<keyof DataTableDensities>(key, 'density', 'standard'));\n let panel = _state.raw<HTMLElement | null>(null);\n const fullscreen = useFullscreen(() => panel);\n\n function search() {\n query.search(cloneValue(draft) as Filters);\n }\n\n function reset() {\n Object.assign(draft, cloneValue(blankFilters ?? initial));\n search();\n }\n\n const toolButton = (props: Partial<IconButtonProps>) =>\n mergePartProps<IconButtonProps>(\n { icon: RefreshCw, variant: 'ghost', tone: 'neutral', 'aria-label': '' },\n propsToolbarButton,\n props,\n );\n\n function renderTools() {\n return (\n <div {...css.attrs(queryTableStyles.tools)}>\n <IconButton\n {...toolButton({\n icon: RefreshCw,\n 'aria-label': messages.queryTableRefresh,\n loading: query.loading,\n onClick: () => void query.refresh(),\n })}\n />\n <Menu\n placement=\"bottom-end\"\n trigger={(props) => (\n <IconButton\n {...toolButton({ icon: Rows3, 'aria-label': messages.queryTableDensity })}\n {...props}\n />\n )}\n >\n <MenuRadioGroup\n _value={density}\n onUpdate:_value={(value: string) => (density = value as keyof DataTableDensities)}\n >\n <MenuRadioItem value=\"compact\">{messages.queryTableDensityCompact}</MenuRadioItem>\n <MenuRadioItem value=\"standard\">{messages.queryTableDensityStandard}</MenuRadioItem>\n <MenuRadioItem value=\"comfortable\">\n {messages.queryTableDensityComfortable}\n </MenuRadioItem>\n </MenuRadioGroup>\n </Menu>\n <ColumnSettings\n columns={columns}\n _hidden={hidden}\n _order={order}\n onReset={() => {\n pins = {};\n widths = {};\n }}\n propsTrigger={propsToolbarButton}\n />\n {fullscreen.supported && (\n <IconButton\n {...toolButton({\n icon: fullscreen.active ? Minimize : Maximize,\n 'aria-label': fullscreen.active\n ? messages.queryTableExitFullscreen\n : messages.queryTableFullscreen,\n 'aria-pressed': fullscreen.active,\n onClick: () => void fullscreen.toggle(),\n })}\n />\n )}\n </div>\n );\n }\n\n return () => {\n const root = renderRoot();\n return fill ? <ViewportFill>{root}</ViewportFill> : root;\n };\n\n function renderRoot() {\n return (\n <div\n {...mergeAttrs<'div'>(\n { sx: [queryTableStyles.root, fill && queryTableStyles.rootFill] },\n rest,\n )}\n >\n {filters && (\n <FilterBar\n {...mergePartProps<Partial<FilterBarProps>>(\n // 一页上可能有几个查询栏:各有名称,search 地标才分得清\n { 'aria-label': messages.queryTableSearch(title) },\n propsFilterBar,\n {\n onSearch: search,\n onReset: reset,\n loading: query.loading,\n more: moreFilters?.(draft),\n // 全屏时只有表格那一块(查询栏不在里面),工具栏要留在表格上方,所以不并进来\n start: fullscreen.active ? undefined : (\n <div {...css.attrs(queryTableStyles.titleInline)}>{title}</div>\n ),\n end: fullscreen.active ? undefined : (\n <div\n {...mergeAttrs<'div'>({ sx: queryTableStyles.toolbarInline }, propsToolbar)}\n >\n {toolbar}\n {renderTools()}\n </div>\n ),\n },\n )}\n >\n {filters(draft)}\n </FilterBar>\n )}\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n queryTableStyles.panel,\n fill && queryTableStyles.panelFill,\n fullscreen.active && queryTableStyles.panelFullscreen,\n ],\n },\n propsPanel,\n { ref: _toRef(panel) },\n )}\n >\n {(!filters || fullscreen.active) && (\n <div {...mergeAttrs<'div'>({ sx: queryTableStyles.toolbar }, propsToolbar)}>\n <div {...css.attrs(queryTableStyles.title)}>{title}</div>\n {toolbar}\n {renderTools()}\n </div>\n )}\n {selectionMode === 'multiple' && (\n <SelectionBar\n {...mergePartProps<SelectionBarProps>(\n { count: selected.length },\n propsSelectionBar,\n {\n count: selected.length,\n onClear: () => (selected = []),\n },\n )}\n >\n {batchActions?.(selected)}\n </SelectionBar>\n )}\n {/* 加载失败:按错误码说明原因(网络、权限、条件写错),保留上一次的行,给一个重试 */}\n {query.error !== undefined && (\n <Alert\n {...mergePartProps<Partial<AlertProps>>(\n {\n tone: 'danger',\n live: 'assertive',\n action: (\n <Button\n size=\"sm\"\n variant=\"outline\"\n tone=\"danger\"\n onClick={() => void query.refresh()}\n >\n {messages.queryTableRetry}\n </Button>\n ),\n },\n propsError,\n )}\n >\n {errorMessage(query.error, lang)}\n </Alert>\n )}\n <DataTable\n {...mergePartProps<Partial<DataTableProps<T>>>(\n { 'aria-label': title, flexible: fill || fullscreen.active },\n propsTable,\n )}\n rows={query.rows as T[]}\n columns={columns}\n getKey={getKey}\n manualSort\n multiSort={multiSort}\n _sort={query.sort}\n _sorts={query.sorts}\n manualFilter\n _filters={query.columnFilters}\n loading={query.loading}\n density={density}\n selectionMode={selectionMode}\n _selected={selected}\n _hiddenColumns={hidden}\n _columnOrder={order}\n _columnPins={pins}\n _columnWidths={widths}\n resizable\n columnMenu\n />\n <PaginationBar\n {...mergePartProps<Partial<PaginationBarProps>>(\n { propsPagination: { 'aria-label': messages.queryTablePagination(title) } },\n propsPagination,\n )}\n _page={query.page}\n _pageSize={query.pageSize}\n total={query.total}\n />\n </div>\n </div>\n );\n }\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"QueryTable.js","names":["_isVNode","_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vtp_untrack","_vue_shallowRef","_createVNode","Menu","_mergeProps"],"sources":["../../src/patterns/QueryTable.tsx"],"sourcesContent":["/**\n * 查询表格(列表页的整块):查询栏(FilterBar)+ 工具栏(标题、自己的按钮、刷新、行高、列设置、全屏)+ 批量操作栏 + 数据表格 + 分页栏。\n * 有查询栏时工具栏并进查询栏那一行,压成一行:标题 条件 查询 重置 展开 … 自己的按钮 刷新 行高 列设置 全屏;\n * 条件多时把不常用的放进 moreFilters,点“展开”后更多条件排在这一行下面。没有查询栏、或全屏(全屏的只有表格那一块)时,\n * 工具栏是表格上方单独的一行。fill 时整块占满到窗口底边(ViewportFill),行多了在表格里滚动,分页留在一屏里。\n * 状态都在 useTableQuery 里(服务端分页、排序、条件)。同 Ant Design ProComponents 的 ProTable。\n *\n * const tasks = useTableQuery(fetchTasks, { filters: { keyword: '', status: '' }, pageSize: 20 });\n * <QueryTable\n * query={tasks} columns={columns} getKey={(task) => task.id} title=\"任务台账\" persistKey=\"tasks\"\n * filters={(draft) => <Field label=\"关键字\"><TextField _value={draft.keyword} /></Field>}\n * toolbar={<Button icon={Plus}>新建</Button>}\n * />\n *\n * - 查询栏的条件改的是草稿(draft),点“查询”(或回车)才交给 query.search;“重置”回到 blankFilters(不写时是一开始的条件)并查询。\n * - 表头排序交给服务端(manualSort,写回 query.sort;multiSort 时按住 Shift 追加一列,写回 query.sorts),\n * 表头筛选也交给服务端(manualFilter,写回 query.columnFilters;列的 filter 要写 options,否则只列出当前这页出现过的值),\n * 分页栏绑定 query.page / pageSize / total。fetcher 用 tableParams(query, columns) 把这些写成接口的参数。\n * - persistKey:列设置(显示、顺序、固定、列宽)与行高记在浏览器里,下次打开还是这样。\n * - selectionMode 时勾选行,表格上方出现批量操作栏,batchActions 拿到已选的 key 给出按钮。\n *\n * 部件:Root(div)、FilterBar、Panel(工具栏与表格所在的区域,全屏的就是它)、Toolbar(div)、Title(div)、\n * Table(DataTable)、SelectionBar、Pagination(PaginationBar)。\n */\nimport { Maximize, Minimize, RefreshCw, Rows3 } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { reactive, shallowRef, type Ref, type VNodeChild } from 'vue';\nimport { Button } from '../base/Button';\nimport { IconButton, type IconButtonProps } from '../base/IconButton';\nimport { useLang } from '../config/context';\nimport { useMessages } from '../config/lang/messages';\nimport { Alert, type AlertProps } from '../feedback/Alert';\nimport { PaginationBar, type PaginationBarProps } from '../navigation/PaginationBar';\nimport { Menu, MenuRadioGroup, MenuRadioItem } from '../overlay/Menu';\nimport { ColumnSettings } from '../table/ColumnSettings';\nimport type { ColumnStickies, DataTableColumn } from '../table/columns';\nimport { ViewportFill } from '../layout/ViewportFill';\nimport { DataTable, type DataTableProps } from '../table/DataTable';\nimport { SelectionBar, type SelectionBarProps } from '../table/SelectionBar';\nimport type { DataTableDensities } from '../table/table-styles';\nimport type { TablePage, TableQueryState } from '../table/use-table-query';\nimport { cloneValue } from '../utils/data/clone';\nimport { useFullscreen } from '../utils/dom/fullscreen';\nimport { usePersistentState } from '../utils/dom/storage';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { errorMessage } from '../utils/request/error-message';\nimport { FilterBar, type FilterBarProps } from './FilterBar';\nimport { patternsMessages } from './messages';\n\n/** 未声明的属性落到外层 div 上 */\nexport interface QueryTableProps<T, Filters extends object> extends PartProps<'div'> {\n /** 列表状态:useTableQuery 的返回值 */\n query: TableQueryState<TablePage<T>, Filters>;\n /** 列定义 */\n columns: readonly DataTableColumn<T>[];\n /** 行的 key */\n getKey: (row: T, index: number) => string;\n /** 表格的名称(工具栏起始边的标题,也是表格的 aria-label) */\n title: string;\n /** 查询栏的条件:拿到草稿,控件绑定它的字段(`_value={draft.keyword}`);不写时没有查询栏 */\n filters?: (draft: Filters) => VNodeChild;\n /** 收起的条件(不常用的),点“展开”才显示 */\n moreFilters?: (draft: Filters) => VNodeChild;\n /** “重置”回到的条件;不写时是 query 一开始的条件 */\n blankFilters?: Filters;\n /** 占满到窗口底边(列表页的整页):行多了在表格里滚动,分页一直在一屏里 */\n fill?: boolean;\n /** 工具栏上自己的按钮(新建、导出),在标题之后、内置按钮之前 */\n toolbar?: VNodeChild;\n /** 列设置与行高记在浏览器里的键(localStorage,自动加前缀);不写时只在这次打开里有效 */\n persistKey?: string;\n /** 多列排序:按住 Shift 点列标题追加一列(同 DataTable 的 multiSort),排序写在 query.sorts */\n multiSort?: boolean;\n /** 勾选行:多选时表格上方出现批量操作栏 */\n selectionMode?: DataTableProps<T>['selectionMode'];\n /** 已选行的 key:父级写 _selected={keys} */\n _selected?: string[];\n /** 批量操作栏里的按钮:拿到已选的 key */\n batchActions?: (keys: readonly string[]) => VNodeChild;\n /** 查询栏(FilterBar)的部件属性 */\n propsFilterBar?: Partial<FilterBarProps>;\n /** 工具栏与表格所在区域的部件属性(全屏的就是它) */\n propsPanel?: PartProps<'div'>;\n /** 工具栏的部件属性 */\n propsToolbar?: PartProps<'div'>;\n /** 表格(DataTable)的部件属性:列宽、详情行、行的色调等直接写在这里 */\n propsTable?: Partial<DataTableProps<T>>;\n /** 批量操作栏的部件属性 */\n propsSelectionBar?: Partial<SelectionBarProps>;\n /** 分页栏的部件属性 */\n propsPagination?: Partial<PaginationBarProps>;\n /** 加载失败时表格上方的提示条(Alert)的部件属性 */\n propsError?: Partial<AlertProps>;\n /** 工具栏内置按钮(刷新、行高、全屏)的部件属性 */\n propsToolbarButton?: Partial<IconButtonProps>;\n}\n\n/** QueryTable 的样式:root、rootFill、panel、panelFullscreen、panelFill、toolbar、title、tools、titleInline、toolbarInline */\nexport const queryTableStyles = css.create({\n root: { display: 'flex', flexDirection: 'column', gap: '_md', minWidth: 0 },\n panel: {\n display: 'flex',\n flexDirection: 'column',\n gap: '_sm',\n minWidth: 0,\n backgroundColor: '_surface',\n },\n // 全屏时铺满屏幕、四周留白,表格占满剩下的高度\n panelFullscreen: { padding: '_lg', overflow: 'auto' },\n // fill 时外面的高度定了,整块与区域可以压矮,压不下的在表格里滚动\n rootFill: { flexGrow: 1, minHeight: 0 },\n panelFill: { minHeight: 0 },\n toolbar: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: '_sm' },\n title: { flexGrow: 1, minWidth: 0, fontWeight: '_semibold', fontSize: '_lg' },\n tools: { display: 'flex', alignItems: 'center', gap: '_xs' },\n // 并进查询栏那一行时:标题不撑开,按钮靠在一起\n titleInline: { flexShrink: 0, fontWeight: '_semibold', fontSize: '_lg' },\n toolbarInline: { display: 'flex', alignItems: 'center', gap: '_sm' },\n});\n\n/** 存在浏览器里(给了 persistKey)或只在这次打开里有效的状态 */\nfunction settingRef<V>(key: string | undefined, name: string, initial: V): Ref<V> {\n return key ? usePersistentState(`${key}:${name}`, initial) : shallowRef(initial);\n}\n\n/** 查询表格:查询栏 + 工具栏 + 表格 + 分页,状态来自 useTableQuery */\nexport const QueryTable = _component(\n <T, Filters extends object>({\n query,\n columns,\n getKey,\n title,\n filters,\n moreFilters,\n blankFilters,\n fill = false,\n toolbar,\n persistKey,\n multiSort = false,\n selectionMode,\n _selected: selected = _bindable<string[]>([]),\n batchActions,\n propsFilterBar,\n propsPanel,\n propsToolbar,\n propsTable,\n propsSelectionBar,\n propsPagination,\n propsError,\n propsToolbarButton,\n ...rest\n }: QueryTableProps<T, Filters>) => {\n const messages = useMessages('patterns', patternsMessages);\n const lang = useLang();\n // 查询栏改的是草稿:点“查询”才交给 query\n // 用 cloneValue 不用 structuredClone:条件里的日期(CalendarDate)、Decimal 要保持原样\n const initial = cloneValue(_untrack(query.filters));\n const draft = reactive({ ...initial }) as Filters;\n const key = _untrack(persistKey);\n let hidden = _fromRef(settingRef<string[]>(key, 'hidden', []));\n let order = _fromRef(settingRef<string[]>(key, 'order', []));\n let pins = _fromRef(settingRef<Record<string, keyof ColumnStickies | null>>(key, 'pins', {}));\n let widths = _fromRef(settingRef<Record<string, number>>(key, 'widths', {}));\n let density = _fromRef(settingRef<keyof DataTableDensities>(key, 'density', 'standard'));\n let panel = _state.raw<HTMLElement | null>(null);\n const fullscreen = useFullscreen(() => panel);\n\n function search() {\n query.search(cloneValue(draft) as Filters);\n }\n\n function reset() {\n Object.assign(draft, cloneValue(blankFilters ?? initial));\n search();\n }\n\n const toolButton = (props: Partial<IconButtonProps>) =>\n mergePartProps<IconButtonProps>(\n { icon: RefreshCw, variant: 'ghost', tone: 'neutral', 'aria-label': '' },\n propsToolbarButton,\n props,\n );\n\n function renderTools() {\n return (\n <div {...css.attrs(queryTableStyles.tools)}>\n <IconButton\n {...toolButton({\n icon: RefreshCw,\n 'aria-label': messages.queryTableRefresh,\n onClick: () => void query.refresh(),\n })}\n />\n <Menu\n placement=\"bottom-end\"\n trigger={(props) => (\n <IconButton\n {...toolButton({ icon: Rows3, 'aria-label': messages.queryTableDensity })}\n {...props}\n />\n )}\n >\n <MenuRadioGroup\n _value={density}\n onUpdate:_value={(value: string) => (density = value as keyof DataTableDensities)}\n >\n <MenuRadioItem value=\"compact\">{messages.queryTableDensityCompact}</MenuRadioItem>\n <MenuRadioItem value=\"standard\">{messages.queryTableDensityStandard}</MenuRadioItem>\n <MenuRadioItem value=\"comfortable\">\n {messages.queryTableDensityComfortable}\n </MenuRadioItem>\n </MenuRadioGroup>\n </Menu>\n <ColumnSettings\n columns={columns}\n _hidden={hidden}\n _order={order}\n onReset={() => {\n pins = {};\n widths = {};\n }}\n propsTrigger={propsToolbarButton}\n />\n {fullscreen.supported && (\n <IconButton\n {...toolButton({\n icon: fullscreen.active ? Minimize : Maximize,\n 'aria-label': fullscreen.active\n ? messages.queryTableExitFullscreen\n : messages.queryTableFullscreen,\n 'aria-pressed': fullscreen.active,\n onClick: () => void fullscreen.toggle(),\n })}\n />\n )}\n </div>\n );\n }\n\n return () => {\n const root = renderRoot();\n return fill ? <ViewportFill>{root}</ViewportFill> : root;\n };\n\n function renderRoot() {\n return (\n <div\n {...mergeAttrs<'div'>(\n { sx: [queryTableStyles.root, fill && queryTableStyles.rootFill] },\n rest,\n )}\n >\n {filters && (\n <FilterBar\n {...mergePartProps<Partial<FilterBarProps>>(\n // 一页上可能有几个查询栏:各有名称,search 地标才分得清\n { 'aria-label': messages.queryTableSearch(title) },\n propsFilterBar,\n {\n onSearch: search,\n onReset: reset,\n more: moreFilters?.(draft),\n // 全屏时只有表格那一块(查询栏不在里面),工具栏要留在表格上方,所以不并进来\n start: fullscreen.active ? undefined : (\n <div {...css.attrs(queryTableStyles.titleInline)}>{title}</div>\n ),\n end: fullscreen.active ? undefined : (\n <div\n {...mergeAttrs<'div'>({ sx: queryTableStyles.toolbarInline }, propsToolbar)}\n >\n {toolbar}\n {renderTools()}\n </div>\n ),\n },\n )}\n >\n {filters(draft)}\n </FilterBar>\n )}\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n queryTableStyles.panel,\n fill && queryTableStyles.panelFill,\n fullscreen.active && queryTableStyles.panelFullscreen,\n ],\n },\n propsPanel,\n { ref: _toRef(panel) },\n )}\n >\n {(!filters || fullscreen.active) && (\n <div {...mergeAttrs<'div'>({ sx: queryTableStyles.toolbar }, propsToolbar)}>\n <div {...css.attrs(queryTableStyles.title)}>{title}</div>\n {toolbar}\n {renderTools()}\n </div>\n )}\n {selectionMode === 'multiple' && (\n <SelectionBar\n {...mergePartProps<SelectionBarProps>(\n { count: selected.length },\n propsSelectionBar,\n {\n count: selected.length,\n onClear: () => (selected = []),\n },\n )}\n >\n {batchActions?.(selected)}\n </SelectionBar>\n )}\n {/* 加载失败:按错误码说明原因(网络、权限、条件写错),保留上一次的行,给一个重试 */}\n {query.error !== undefined && (\n <Alert\n {...mergePartProps<Partial<AlertProps>>(\n {\n tone: 'danger',\n live: 'assertive',\n action: (\n <Button\n size=\"sm\"\n variant=\"outline\"\n tone=\"danger\"\n onClick={() => void query.refresh()}\n >\n {messages.queryTableRetry}\n </Button>\n ),\n },\n propsError,\n )}\n >\n {errorMessage(query.error, lang)}\n </Alert>\n )}\n <DataTable\n {...mergePartProps<Partial<DataTableProps<T>>>(\n { 'aria-label': title, flexible: fill || fullscreen.active },\n propsTable,\n )}\n rows={query.rows as T[]}\n columns={columns}\n getKey={getKey}\n manualSort\n multiSort={multiSort}\n _sort={query.sort}\n _sorts={query.sorts}\n manualFilter\n _filters={query.columnFilters}\n // 加载提示只有表格的骨架一处:刷新、查询按钮不再各自转圈\n loading={query.loading}\n density={density}\n selectionMode={selectionMode}\n _selected={selected}\n _hiddenColumns={hidden}\n _columnOrder={order}\n _columnPins={pins}\n _columnWidths={widths}\n resizable\n columnMenu\n />\n <PaginationBar\n {...mergePartProps<Partial<PaginationBarProps>>(\n { propsPagination: { 'aria-label': messages.queryTablePagination(title) } },\n propsPagination,\n )}\n _page={query.page}\n _pageSize={query.pageSize}\n total={query.total}\n />\n </div>\n </div>\n );\n }\n },\n);\n"],"mappings":"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"}
|
package/dist/table/DataTable.js
CHANGED
|
@@ -47,7 +47,7 @@ import { GripVertical } from "@lucide/icons";
|
|
|
47
47
|
* - 选择:selectionMode 为 single / multiple 时多一列勾选框,selected 是已选行的 key。
|
|
48
48
|
* - 列宽:resizable 时可以拖动或用键盘调整,columnWidths 记录调整后的宽度(可以双向绑定,保存到本地设置里)。
|
|
49
49
|
* - 定位:handle 拿到 scrollToRow(key),滚到那一行(停在吸顶表头之下)。
|
|
50
|
-
* -
|
|
50
|
+
* - 加载:只用骨架行:loading 时表体换成骨架;滚动加载(onRangeChange)已有数据时骨架接在末尾。
|
|
51
51
|
* - 详情行:renderDetail 给出一行的详情,多一列展开按钮;expandedRows 是展开了的行(可以双向绑定),hasDetail 决定哪些行有。
|
|
52
52
|
* - 分组表头:columnGroups 写组标题与组里各列的 key,相邻的列上方共用一个标题。
|
|
53
53
|
* - 合计:列写 summary('sum' 精确相加,或函数)时底部多一行吸底的合计;空值显示列的 empty(默认 —)。
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_vue_ref","_createVNode","_mergeProps","_Fragment"],"sources":["../../src/table/DataTable.tsx"],"sourcesContent":["/**\n * 数据表格:按列定义渲染一组行,始终虚拟滚动(十万行也只渲染看得见的几十行)。渲染层见 DataTableView。\n *\n * <DataTable\n * rows={orders}\n * columns={columns}\n * getKey={(order) => order.id}\n * _sort={sort}\n * selectionMode=\"multiple\"\n * _selected={picked}\n * loading={loading}\n * aria-label=\"订单\"\n * propsRoot={{ sx: styles.tableHeight }}\n * />\n *\n * - 高度:外层滚动区域默认最高 32em,用 propsRoot 的 sx 改;表头吸顶、固定列不随横向滚动。\n * - 排序:点可排序的列标题在 升序 → 降序 → 不排 之间切换;默认在本地排(createComparer:数字、日期、按语言的文字),\n * manualSort 时只写回 sort,由调用方向服务端要排好序的数据。multiSort 时排序写在 sorts(几列),按住 Shift 点是追加一列。\n * - 表头筛选:列写 filter 时列标题上有筛选按钮,filters 是各列选中的值(可以双向绑定);默认在本地筛,manualFilter 时只写回。\n * - 编辑:列写 editable 时可以就地编辑(Enter / F2、双击或直接打字开始,Enter 提交、Tab 提交并到下一格、Esc 放弃),\n * 列的 validate 与 validateRow 校验,onCellChange 写回;表格随之变成网格,方向键在格之间走(cellNavigation)。\n * - 行拖动:给了 onReorder 时第一个数据列前面有把手,拖动(或聚焦后空格拿起、↑↓ 移动)改变行的顺序;\n * 排序、筛选时显示的顺序不是数据的顺序,这时不能拖。\n * - 列菜单:columnMenu 时列标题结尾有 ⋮(排序、固定在开头 / 结尾、前后移动、隐藏),\n * 写回 columnPins、columnOrder、hiddenColumns(都可以双向绑定,存起来记住各人的列设置)。\n * - 选择:selectionMode 为 single / multiple 时多一列勾选框,selected 是已选行的 key。\n * - 列宽:resizable 时可以拖动或用键盘调整,columnWidths 记录调整后的宽度(可以双向绑定,保存到本地设置里)。\n * - 定位:handle 拿到 scrollToRow(key),滚到那一行(停在吸顶表头之下)。\n * - 加载:loading 时没有数据显示骨架行,已有数据显示进度条;接近末尾加载更多用 onRangeChange。\n * - 详情行:renderDetail 给出一行的详情,多一列展开按钮;expandedRows 是展开了的行(可以双向绑定),hasDetail 决定哪些行有。\n * - 分组表头:columnGroups 写组标题与组里各列的 key,相邻的列上方共用一个标题。\n * - 合计:列写 summary('sum' 精确相加,或函数)时底部多一行吸底的合计;空值显示列的 empty(默认 —)。\n * - 状态列:列写 statuses(createStatusMap)时显示成 StatusBadge;rowTone 给整行着色(导入校验的错误行)。\n * - 工具栏(查询条件用 FilterBar、批量操作用 SelectionBar、分页用 PaginationBar)组合在外面,不内置。\n */\nimport { GripVertical } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { nextTick, useId, type VNodeChild } from 'vue';\nimport { ButtonBase } from '../base/ButtonBase';\nimport { Icon } from '../base/Icon';\nimport { VisuallyHidden } from '../base/VisuallyHidden';\nimport { NumberField } from '../input/NumberField';\nimport { Select } from '../input/Select';\nimport { TextField } from '../input/TextField';\nimport { plainText } from '../utils/data/text';\nimport { useLang } from '../config/context';\nimport { focusFirst } from '../utils/dom/focus';\nimport { useSortable } from '../utils/dom/sortable';\nimport { useDefaults } from '../config/defaults';\nimport { useRecipe } from '../config/styles';\nimport { mergeAttrs, mergePartProps } from '../utils/props';\nimport { toggleSelection } from '../utils/data/selection';\nimport {\n sortList,\n arrangeColumns,\n columnFilter,\n columnType,\n columnValue,\n isCellEditable,\n filterOptions,\n filterRows,\n nextSort,\n nextSorts,\n sortRows,\n type CellEditorContext,\n type ColumnFilterOption,\n type ColumnStickies,\n type DataTableColumn,\n type SortState,\n} from './columns';\nimport type { ColumnMenuActions } from './ColumnHeaderActions';\nimport {\n DataTableView,\n rowLabel,\n type CellPosition,\n type DataTableRowModel,\n type DataTableViewProps,\n} from './DataTableView';\nimport { dataTableRecipe, dataTableStateStyles } from './table-styles';\n\n/** 未声明的属性落到 table 上;aria-label 或 aria-labelledby 是表格的名称 */\nexport interface DataTableProps<T> extends Omit<\n DataTableViewProps<T>,\n | 'rows'\n | 'sort'\n | 'onSortClick'\n | 'selectedKeys'\n | 'onToggleRow'\n | 'onToggleAll'\n | 'widths'\n | 'onResize'\n | 'expandedRows'\n | 'onToggleDetail'\n | 'filters'\n | 'onFilterChange'\n | 'filterOptionsOf'\n | 'onColumnAction'\n | 'hiddenColumns'\n> {\n /** 数据 */\n rows: readonly T[];\n /** 行的 key:稳定且唯一(选择、测量都按它) */\n getKey: (row: T, index: number) => string;\n /** 当前排序:父级可以写 _sort={x};null 为不排序 */\n _sort?: SortState | null;\n /** 不在本地排序:只写回 sort(sorts),数据由调用方按它取 */\n manualSort?: boolean;\n /** 多列排序:排序写在 sorts,按住 Shift 点列标题是追加一列(再点切换方向、降序时去掉) */\n multiSort?: boolean;\n /** 多列排序时的排序,前面的优先:父级可以写 _sorts={x} */\n _sorts?: SortState[];\n /** 表头筛选:各列选中的值(关键字筛选时是 [关键字]),父级可以写 _filters={x} */\n _filters?: Record<string, string[]>;\n /** 不在本地筛选:只写回 filters,数据由调用方按它取 */\n manualFilter?: boolean;\n /** 隐藏的列的 key:列菜单的“隐藏”写回这里,父级可以写 _hiddenColumns={x} */\n _hiddenColumns?: string[];\n /** 列的顺序(列的 key):列菜单、列设置里移动后写回,没列到的按定义的顺序排在后面 */\n _columnOrder?: string[];\n /** 固定的列:key → start / end(null 取消列定义里的固定),列菜单写回 */\n _columnPins?: Record<string, keyof ColumnStickies | null>;\n /** 行拖动:给了它时每行前面有把手,拖完得到新的顺序(排序、筛选时不能拖) */\n onReorder?: (rows: T[]) => void;\n /**\n * 编辑了一格(列写了 editable):写回数据。返回文字(或 resolve 成文字)是不接受的原因,编辑器留着并显示它;\n * 返回 Promise 时等它完成\n */\n onCellChange?: (row: T, key: string, value: unknown) => unknown;\n /** 行校验:返回各列的错误(列的 key → 原因),标在格上;如“结束日期早于开始日期” */\n validateRow?: (row: T) => Readonly<Record<string, string>> | null | undefined;\n /** 已选行的 key:父级写 _selected={keys} */\n _selected?: string[];\n /** 各列调整后的宽度(px):父级可以写 _columnWidths={x} 保存起来 */\n _columnWidths?: Record<string, number>;\n /** 展开了详情的行的 key(有 renderDetail 时):父级可以写 _expandedRows={x} */\n _expandedRows?: string[];\n}\n\n/** 数据表格:虚拟滚动、排序、勾选、列宽调整、加载与空状态 */\nexport const DataTable = _component(\n <T,>({\n rows,\n getKey,\n columns,\n _sort: sort = _bindable<SortState | null>(null),\n manualSort = false,\n multiSort = false,\n _sorts: sorts = _bindable<SortState[]>([]),\n _filters: filters = _bindable<Record<string, string[]>>({}),\n manualFilter = false,\n _hiddenColumns: hiddenColumns = _bindable<string[]>([]),\n _columnOrder: columnOrder = _bindable<string[]>([]),\n _columnPins: columnPins = _bindable<Record<string, keyof ColumnStickies | null>>({}),\n onReorder,\n onCellChange,\n validateRow,\n cellNavigation,\n leading,\n propsRow,\n selectionMode,\n _selected: selected = _bindable<string[]>([]),\n _columnWidths: columnWidths = _bindable<Record<string, number>>({}),\n _expandedRows: expandedRows = _bindable<string[]>([]),\n isRowSelectable,\n density,\n variant,\n striped,\n ...rest\n }: DataTableProps<T>) => {\n const lang = useLang();\n const appearance = useDefaults('DataTable', () => ({ density, variant, striped }), {\n density: 'standard',\n variant: 'surface',\n striped: false,\n });\n const recipe = useRecipe(dataTableRecipe);\n // 编辑器、拖动把手的部件层:与 DataTableView 的表头、表尾取同一组档位\n const s = _derived.by(() => {\n const { density: shownDensity, variant: shownVariant } = appearance();\n return recipe({ density: shownDensity, variant: shownVariant });\n });\n\n const activeSort = () => (multiSort ? sorts : sort);\n const models = _derived.by<DataTableRowModel<T>[]>(() => {\n const kept = manualFilter ? rows : filterRows(rows, columns, filters, lang.code);\n const ordered = manualSort ? kept : sortRows(kept, columns, activeSort(), lang.code);\n return ordered.map((row, index) => ({ key: getKey(row, index), data: row, depth: 0 }));\n });\n // 筛选的选项要看全部行(不是筛过的):行与列不变时不重算\n const options = _derived.by(\n () =>\n new Map(\n columns\n .filter((column) => columnFilter(column))\n .map((column) => [column.key, filterOptions(column, rows, lang.code)]),\n ),\n );\n const arranged = _derived(arrangeColumns(columns, columnOrder, columnPins));\n\n // 行拖动:排序、筛选时显示的顺序不是数据的顺序,不能拖\n const tableMarker = useId();\n const tableElement = () =>\n document.querySelector<HTMLElement>(`table[data-table=\"${tableMarker}\"]`);\n const reorderable = () =>\n !!onReorder &&\n sortList(activeSort()).length === 0 &&\n !Object.values(filters).some((values) => values.length > 0);\n const sortable = useSortable<DataTableRowModel<T>>({\n items: () => models,\n getKey: (row) => row.key,\n getLabel: (row) => rowLabel(columns, row),\n onChange: (next) => onReorder?.(next.map((row) => row.data)),\n container: () => tableElement(),\n disabled: () => !reorderable(),\n });\n\n // 编辑中的格:草稿、校验没过的原因、正在保存\n let editing = _state<\n (CellPosition & { draft: unknown; error?: string; saving?: boolean }) | null\n >(null);\n const columnOf = (key: string) => columns.find((column) => column.key === key);\n const rowOf = (key: string) => models.find((row) => row.key === key);\n\n const cellElement = (cell: CellPosition) =>\n tableElement()?.querySelector<HTMLElement>(\n `tr[data-key=\"${CSS.escape(cell.row)}\"] td[data-column=\"${CSS.escape(cell.column)}\"]`,\n );\n\n /** 编辑完、放弃后焦点回到格上(网格里键盘接着走) */\n function focusCell(cell: CellPosition) {\n void nextTick(() => cellElement(cell)?.focus());\n }\n\n function startEdit(cell: CellPosition, text?: string) {\n const row = rowOf(cell.row);\n const column = columnOf(cell.column);\n if (!row || !column || !isCellEditable(column, row.data)) return;\n const numeric = ['number', 'currency', 'percent'].includes(columnType(column, rows));\n // 直接打字:用打的字替换原值(数字列只认数字)\n const typed = text === undefined ? undefined : numeric ? Number(text) : text;\n const draft =\n typed === undefined || Number.isNaN(typed) ? columnValue(column, row.data) : typed;\n editing = { ...cell, draft };\n // 编辑器渲染出来后焦点移进去(autofocus 对后插入的元素不可靠,日期这类分段输入也没有 autofocus)\n void nextTick(() => {\n const element = cellElement(cell);\n if (element) focusFirst(element);\n });\n }\n\n function cancelEdit() {\n if (!editing) return;\n const cell = { row: editing.row, column: editing.column };\n editing = null;\n focusCell(cell);\n }\n\n /** 提交:列的 validate、onCellChange 都接受了才收起;step 是提交后去哪一格(Tab 往后、Shift + Tab 往前) */\n async function commitEdit(step: 0 | 1 | -1 = 0) {\n const current = editing;\n const row = current && rowOf(current.row);\n const column = current && columnOf(current.column);\n if (!current || !row || !column || current.saving) return;\n const invalid = column.validate?.(current.draft, row.data);\n if (invalid) {\n editing = { ...current, error: invalid };\n return;\n }\n editing = { ...current, saving: true };\n const result = await onCellChange?.(row.data, column.key, current.draft);\n if (typeof result === 'string' && result) {\n editing = { ...current, error: result, saving: false };\n return;\n }\n editing = null;\n const next = step === 0 ? undefined : neighbourEditable(current, step);\n if (next) startEdit(next);\n else focusCell(current);\n }\n\n /** 这一行里往后(往前)的下一个可编辑的格 */\n function neighbourEditable(cell: CellPosition, step: 1 | -1): CellPosition | undefined {\n const row = rowOf(cell.row);\n if (!row) return undefined;\n const hidden = new Set(hiddenColumns);\n const visible = arranged.filter((column) => !hidden.has(column.key));\n const from = visible.findIndex((column) => column.key === cell.column);\n for (let index = from + step; index >= 0 && index < visible.length; index += step) {\n const column = visible[index]!;\n if (isCellEditable(column, row.data)) return { row: cell.row, column: column.key };\n }\n return undefined;\n }\n\n function onEditKeydown(event: KeyboardEvent) {\n // 编辑器自己处理了的键(下拉框的 Enter 打开列表)不再当作提交\n if (event.defaultPrevented) return;\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n cancelEdit();\n } else if (event.key === 'Enter') {\n event.preventDefault();\n void commitEdit();\n } else if (event.key === 'Tab') {\n event.preventDefault();\n void commitEdit(event.shiftKey ? -1 : 1);\n }\n }\n\n /** 正在编辑的格里的编辑器:列的 editor,或按列选的默认编辑器 */\n function renderEditor(row: T, column: DataTableColumn<T>): VNodeChild {\n const current = editing!;\n const context: CellEditorContext<T> = {\n row,\n value: current.draft,\n onChange: (value) => (editing = { ...current, draft: value, error: undefined }),\n commit: () => void commitEdit(),\n cancel: cancelEdit,\n error: current.error,\n };\n const label = plainText(column.header) ?? column.key;\n const common = {\n size: 'sm',\n invalid: !!current.error,\n 'aria-label': label,\n // 编辑器占满格子\n propsRoot: s.editorControl,\n } as const;\n const editor = column.editor ? (\n column.editor(context)\n ) : column.statuses ? (\n // 状态:选了就提交\n <Select\n {...common}\n items={column.statuses.options()}\n _value={typeof current.draft === 'string' ? current.draft : null}\n onUpdate:_value={(value: string | null) => {\n editing = { ...current, draft: value };\n void commitEdit();\n }}\n />\n ) : ['number', 'currency', 'percent'].includes(columnType(column, rows)) ? (\n <NumberField\n {...common}\n stepper={false}\n _value={typeof current.draft === 'number' ? current.draft : null}\n onUpdate:_value={context.onChange}\n />\n ) : (\n <TextField\n {...common}\n _value={\n typeof current.draft === 'string' || typeof current.draft === 'number'\n ? String(current.draft)\n : ''\n }\n onUpdate:_value={context.onChange}\n />\n );\n return (\n <div\n {...mergeAttrs<'div'>(s.editor)}\n // 焦点离开这一格(点了别处):提交\n onFocusout={(event: FocusEvent) => {\n const cell = (event.currentTarget as HTMLElement).closest('td');\n if (!cell?.contains(event.relatedTarget as Node | null)) void commitEdit();\n }}\n >\n {editor}\n {current.error && (\n <div role=\"alert\" {...mergeAttrs<'div'>(s.editorError)}>\n {current.error}\n </div>\n )}\n </div>\n );\n }\n\n /**\n * 能拖动时行的属性:使用方的 propsRow 加上拖动要的属性。在 setup 里建一次(函数不变,行的 memo 才有效);\n * 里面读的拖动状态由各行的渲染跟踪,被拖的行照样更新\n */\n const rowPropsWithHandle = (row: T, index: number) =>\n mergeAttrs<'tr'>({}, typeof propsRow === 'function' ? propsRow(row, index) : propsRow, {\n ...sortable.itemProps(models[index]!.key),\n sx: sortable.active === models[index]!.key ? dataTableStateStyles.rowDragging : undefined,\n });\n\n /** 第一个数据列前面:拖动的把手,再是使用方的 leading */\n function renderLeading(row: DataTableRowModel<T>, index: number) {\n return (\n <>\n <ButtonBase\n {...s.rowHandle}\n ripple={false}\n aria-label={lang.sortableHandle(rowLabel(columns, row))}\n {...sortable.handleProps(row.key)}\n >\n <Icon icon={GripVertical} />\n </ButtonBase>\n {leading?.(row, index)}\n </>\n );\n }\n\n function onSortClick(key: string, event: MouseEvent) {\n if (multiSort) sorts = nextSorts(sorts, key, event.shiftKey);\n else sort = nextSort(sort, key);\n }\n\n function setSort(key: string, direction: SortState['direction'] | null) {\n if (multiSort) {\n const rest = sorts.filter((item) => item.key !== key);\n sorts = direction ? [{ key, direction }, ...rest] : rest;\n } else sort = direction ? { key, direction } : null;\n }\n\n /** 往前、往后移一列:与相邻的可见列交换位置 */\n function move(key: string, step: 1 | -1) {\n const hidden = new Set(hiddenColumns);\n const keys = arranged.map((column) => column.key);\n const visible = keys.filter((item) => !hidden.has(item));\n const neighbour = visible[visible.indexOf(key) + step];\n if (!neighbour) return;\n const [from, to] = [keys.indexOf(key), keys.indexOf(neighbour)];\n [keys[from], keys[to]] = [neighbour, key];\n columnOrder = keys;\n }\n\n function onColumnAction(key: string, action: keyof ColumnMenuActions, width: number) {\n if (action === 'sortAscending') setSort(key, 'ascending');\n else if (action === 'sortDescending') setSort(key, 'descending');\n else if (action === 'unsort') setSort(key, null);\n else if (action === 'moveBackward') move(key, -1);\n else if (action === 'moveForward') move(key, 1);\n else if (action === 'hide') hiddenColumns = [...hiddenColumns, key];\n else {\n const side = action === 'pinStart' ? 'start' : action === 'pinEnd' ? 'end' : null;\n columnPins = { ...columnPins, [key]: side };\n // 固定列的偏移按列宽累加:没写宽度的列记下它现在的宽度\n const column = columns.find((item) => item.key === key);\n if (side && column?.width == null && columnWidths[key] == null && width > 0)\n columnWidths = { ...columnWidths, [key]: width };\n }\n }\n\n function toggleRow(key: string) {\n selected = [...toggleSelection(selected, key, { mode: selectionMode ?? 'multiple' })];\n }\n\n // 用集合查找:逐个 includes 是 O(n²),十万行全选会卡住页面;不能勾选的行跳过\n function toggleAll() {\n const keys = (\n isRowSelectable ? models.filter((row) => isRowSelectable(row.data)) : models\n ).map((row) => row.key);\n const current = new Set(selected);\n const all = keys.length > 0 && keys.every((key) => current.has(key));\n if (!all) {\n selected = [...new Set([...selected, ...keys])];\n return;\n }\n const visible = new Set(keys);\n selected = selected.filter((key) => !visible.has(key));\n }\n\n function toggleDetail(key: string) {\n expandedRows = [...toggleSelection(expandedRows, key, { mode: 'multiple' })];\n }\n\n return () => (\n <>\n <DataTableView\n {...mergePartProps<DataTableViewProps<T>>(\n {\n rows: models,\n columns: arranged,\n widths: columnWidths,\n ...appearance(),\n },\n rest as Partial<DataTableViewProps<T>>,\n {\n rows: models,\n columns: arranged,\n sort: activeSort(),\n onSortClick,\n filters,\n onFilterChange: (key: string, values: string[]) =>\n (filters = { ...filters, [key]: values }),\n filterOptionsOf: (column) => options.get(column.key) ?? ([] as ColumnFilterOption[]),\n hiddenColumns,\n onColumnAction,\n leading: reorderable() ? renderLeading : leading,\n // 不拖动时原样交给行:每次新建的函数会让各行的 memo 失效,勾选一行全部重渲染\n propsRow: reorderable() ? rowPropsWithHandle : propsRow,\n ...({ 'data-table': tableMarker } as Partial<DataTableViewProps<T>>),\n cellNavigation: cellNavigation ?? columns.some((column) => column.editable),\n editing: editing && { row: editing.row, column: editing.column },\n renderEditor,\n rowErrors: validateRow,\n onEditStart: startEdit,\n onEditKeydown,\n selectionMode,\n isRowSelectable,\n selectedKeys: selected,\n onToggleRow: toggleRow,\n onToggleAll: toggleAll,\n widths: columnWidths,\n onResize: (key: string, width: number) =>\n (columnWidths = { ...columnWidths, [key]: width }),\n expandedRows,\n onToggleDetail: toggleDetail,\n },\n )}\n />\n {onReorder && (\n <>\n <VisuallyHidden id={sortable.instructionsId}>\n {lang.sortableInstructions}\n </VisuallyHidden>\n <VisuallyHidden aria-live=\"assertive\">{sortable.announcement}</VisuallyHidden>\n </>\n )}\n </>\n );\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DataTable.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_vue_ref","_createVNode","_mergeProps","_Fragment"],"sources":["../../src/table/DataTable.tsx"],"sourcesContent":["/**\n * 数据表格:按列定义渲染一组行,始终虚拟滚动(十万行也只渲染看得见的几十行)。渲染层见 DataTableView。\n *\n * <DataTable\n * rows={orders}\n * columns={columns}\n * getKey={(order) => order.id}\n * _sort={sort}\n * selectionMode=\"multiple\"\n * _selected={picked}\n * loading={loading}\n * aria-label=\"订单\"\n * propsRoot={{ sx: styles.tableHeight }}\n * />\n *\n * - 高度:外层滚动区域默认最高 32em,用 propsRoot 的 sx 改;表头吸顶、固定列不随横向滚动。\n * - 排序:点可排序的列标题在 升序 → 降序 → 不排 之间切换;默认在本地排(createComparer:数字、日期、按语言的文字),\n * manualSort 时只写回 sort,由调用方向服务端要排好序的数据。multiSort 时排序写在 sorts(几列),按住 Shift 点是追加一列。\n * - 表头筛选:列写 filter 时列标题上有筛选按钮,filters 是各列选中的值(可以双向绑定);默认在本地筛,manualFilter 时只写回。\n * - 编辑:列写 editable 时可以就地编辑(Enter / F2、双击或直接打字开始,Enter 提交、Tab 提交并到下一格、Esc 放弃),\n * 列的 validate 与 validateRow 校验,onCellChange 写回;表格随之变成网格,方向键在格之间走(cellNavigation)。\n * - 行拖动:给了 onReorder 时第一个数据列前面有把手,拖动(或聚焦后空格拿起、↑↓ 移动)改变行的顺序;\n * 排序、筛选时显示的顺序不是数据的顺序,这时不能拖。\n * - 列菜单:columnMenu 时列标题结尾有 ⋮(排序、固定在开头 / 结尾、前后移动、隐藏),\n * 写回 columnPins、columnOrder、hiddenColumns(都可以双向绑定,存起来记住各人的列设置)。\n * - 选择:selectionMode 为 single / multiple 时多一列勾选框,selected 是已选行的 key。\n * - 列宽:resizable 时可以拖动或用键盘调整,columnWidths 记录调整后的宽度(可以双向绑定,保存到本地设置里)。\n * - 定位:handle 拿到 scrollToRow(key),滚到那一行(停在吸顶表头之下)。\n * - 加载:只用骨架行:loading 时表体换成骨架;滚动加载(onRangeChange)已有数据时骨架接在末尾。\n * - 详情行:renderDetail 给出一行的详情,多一列展开按钮;expandedRows 是展开了的行(可以双向绑定),hasDetail 决定哪些行有。\n * - 分组表头:columnGroups 写组标题与组里各列的 key,相邻的列上方共用一个标题。\n * - 合计:列写 summary('sum' 精确相加,或函数)时底部多一行吸底的合计;空值显示列的 empty(默认 —)。\n * - 状态列:列写 statuses(createStatusMap)时显示成 StatusBadge;rowTone 给整行着色(导入校验的错误行)。\n * - 工具栏(查询条件用 FilterBar、批量操作用 SelectionBar、分页用 PaginationBar)组合在外面,不内置。\n */\nimport { GripVertical } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { nextTick, useId, type VNodeChild } from 'vue';\nimport { ButtonBase } from '../base/ButtonBase';\nimport { Icon } from '../base/Icon';\nimport { VisuallyHidden } from '../base/VisuallyHidden';\nimport { NumberField } from '../input/NumberField';\nimport { Select } from '../input/Select';\nimport { TextField } from '../input/TextField';\nimport { plainText } from '../utils/data/text';\nimport { useLang } from '../config/context';\nimport { focusFirst } from '../utils/dom/focus';\nimport { useSortable } from '../utils/dom/sortable';\nimport { useDefaults } from '../config/defaults';\nimport { useRecipe } from '../config/styles';\nimport { mergeAttrs, mergePartProps } from '../utils/props';\nimport { toggleSelection } from '../utils/data/selection';\nimport {\n sortList,\n arrangeColumns,\n columnFilter,\n columnType,\n columnValue,\n isCellEditable,\n filterOptions,\n filterRows,\n nextSort,\n nextSorts,\n sortRows,\n type CellEditorContext,\n type ColumnFilterOption,\n type ColumnStickies,\n type DataTableColumn,\n type SortState,\n} from './columns';\nimport type { ColumnMenuActions } from './ColumnHeaderActions';\nimport {\n DataTableView,\n rowLabel,\n type CellPosition,\n type DataTableRowModel,\n type DataTableViewProps,\n} from './DataTableView';\nimport { dataTableRecipe, dataTableStateStyles } from './table-styles';\n\n/** 未声明的属性落到 table 上;aria-label 或 aria-labelledby 是表格的名称 */\nexport interface DataTableProps<T> extends Omit<\n DataTableViewProps<T>,\n | 'rows'\n | 'sort'\n | 'onSortClick'\n | 'selectedKeys'\n | 'onToggleRow'\n | 'onToggleAll'\n | 'widths'\n | 'onResize'\n | 'expandedRows'\n | 'onToggleDetail'\n | 'filters'\n | 'onFilterChange'\n | 'filterOptionsOf'\n | 'onColumnAction'\n | 'hiddenColumns'\n> {\n /** 数据 */\n rows: readonly T[];\n /** 行的 key:稳定且唯一(选择、测量都按它) */\n getKey: (row: T, index: number) => string;\n /** 当前排序:父级可以写 _sort={x};null 为不排序 */\n _sort?: SortState | null;\n /** 不在本地排序:只写回 sort(sorts),数据由调用方按它取 */\n manualSort?: boolean;\n /** 多列排序:排序写在 sorts,按住 Shift 点列标题是追加一列(再点切换方向、降序时去掉) */\n multiSort?: boolean;\n /** 多列排序时的排序,前面的优先:父级可以写 _sorts={x} */\n _sorts?: SortState[];\n /** 表头筛选:各列选中的值(关键字筛选时是 [关键字]),父级可以写 _filters={x} */\n _filters?: Record<string, string[]>;\n /** 不在本地筛选:只写回 filters,数据由调用方按它取 */\n manualFilter?: boolean;\n /** 隐藏的列的 key:列菜单的“隐藏”写回这里,父级可以写 _hiddenColumns={x} */\n _hiddenColumns?: string[];\n /** 列的顺序(列的 key):列菜单、列设置里移动后写回,没列到的按定义的顺序排在后面 */\n _columnOrder?: string[];\n /** 固定的列:key → start / end(null 取消列定义里的固定),列菜单写回 */\n _columnPins?: Record<string, keyof ColumnStickies | null>;\n /** 行拖动:给了它时每行前面有把手,拖完得到新的顺序(排序、筛选时不能拖) */\n onReorder?: (rows: T[]) => void;\n /**\n * 编辑了一格(列写了 editable):写回数据。返回文字(或 resolve 成文字)是不接受的原因,编辑器留着并显示它;\n * 返回 Promise 时等它完成\n */\n onCellChange?: (row: T, key: string, value: unknown) => unknown;\n /** 行校验:返回各列的错误(列的 key → 原因),标在格上;如“结束日期早于开始日期” */\n validateRow?: (row: T) => Readonly<Record<string, string>> | null | undefined;\n /** 已选行的 key:父级写 _selected={keys} */\n _selected?: string[];\n /** 各列调整后的宽度(px):父级可以写 _columnWidths={x} 保存起来 */\n _columnWidths?: Record<string, number>;\n /** 展开了详情的行的 key(有 renderDetail 时):父级可以写 _expandedRows={x} */\n _expandedRows?: string[];\n}\n\n/** 数据表格:虚拟滚动、排序、勾选、列宽调整、加载与空状态 */\nexport const DataTable = _component(\n <T,>({\n rows,\n getKey,\n columns,\n _sort: sort = _bindable<SortState | null>(null),\n manualSort = false,\n multiSort = false,\n _sorts: sorts = _bindable<SortState[]>([]),\n _filters: filters = _bindable<Record<string, string[]>>({}),\n manualFilter = false,\n _hiddenColumns: hiddenColumns = _bindable<string[]>([]),\n _columnOrder: columnOrder = _bindable<string[]>([]),\n _columnPins: columnPins = _bindable<Record<string, keyof ColumnStickies | null>>({}),\n onReorder,\n onCellChange,\n validateRow,\n cellNavigation,\n leading,\n propsRow,\n selectionMode,\n _selected: selected = _bindable<string[]>([]),\n _columnWidths: columnWidths = _bindable<Record<string, number>>({}),\n _expandedRows: expandedRows = _bindable<string[]>([]),\n isRowSelectable,\n density,\n variant,\n striped,\n ...rest\n }: DataTableProps<T>) => {\n const lang = useLang();\n const appearance = useDefaults('DataTable', () => ({ density, variant, striped }), {\n density: 'standard',\n variant: 'surface',\n striped: false,\n });\n const recipe = useRecipe(dataTableRecipe);\n // 编辑器、拖动把手的部件层:与 DataTableView 的表头、表尾取同一组档位\n const s = _derived.by(() => {\n const { density: shownDensity, variant: shownVariant } = appearance();\n return recipe({ density: shownDensity, variant: shownVariant });\n });\n\n const activeSort = () => (multiSort ? sorts : sort);\n const models = _derived.by<DataTableRowModel<T>[]>(() => {\n const kept = manualFilter ? rows : filterRows(rows, columns, filters, lang.code);\n const ordered = manualSort ? kept : sortRows(kept, columns, activeSort(), lang.code);\n return ordered.map((row, index) => ({ key: getKey(row, index), data: row, depth: 0 }));\n });\n // 筛选的选项要看全部行(不是筛过的):行与列不变时不重算\n const options = _derived.by(\n () =>\n new Map(\n columns\n .filter((column) => columnFilter(column))\n .map((column) => [column.key, filterOptions(column, rows, lang.code)]),\n ),\n );\n const arranged = _derived(arrangeColumns(columns, columnOrder, columnPins));\n\n // 行拖动:排序、筛选时显示的顺序不是数据的顺序,不能拖\n const tableMarker = useId();\n const tableElement = () =>\n document.querySelector<HTMLElement>(`table[data-table=\"${tableMarker}\"]`);\n const reorderable = () =>\n !!onReorder &&\n sortList(activeSort()).length === 0 &&\n !Object.values(filters).some((values) => values.length > 0);\n const sortable = useSortable<DataTableRowModel<T>>({\n items: () => models,\n getKey: (row) => row.key,\n getLabel: (row) => rowLabel(columns, row),\n onChange: (next) => onReorder?.(next.map((row) => row.data)),\n container: () => tableElement(),\n disabled: () => !reorderable(),\n });\n\n // 编辑中的格:草稿、校验没过的原因、正在保存\n let editing = _state<\n (CellPosition & { draft: unknown; error?: string; saving?: boolean }) | null\n >(null);\n const columnOf = (key: string) => columns.find((column) => column.key === key);\n const rowOf = (key: string) => models.find((row) => row.key === key);\n\n const cellElement = (cell: CellPosition) =>\n tableElement()?.querySelector<HTMLElement>(\n `tr[data-key=\"${CSS.escape(cell.row)}\"] td[data-column=\"${CSS.escape(cell.column)}\"]`,\n );\n\n /** 编辑完、放弃后焦点回到格上(网格里键盘接着走) */\n function focusCell(cell: CellPosition) {\n void nextTick(() => cellElement(cell)?.focus());\n }\n\n function startEdit(cell: CellPosition, text?: string) {\n const row = rowOf(cell.row);\n const column = columnOf(cell.column);\n if (!row || !column || !isCellEditable(column, row.data)) return;\n const numeric = ['number', 'currency', 'percent'].includes(columnType(column, rows));\n // 直接打字:用打的字替换原值(数字列只认数字)\n const typed = text === undefined ? undefined : numeric ? Number(text) : text;\n const draft =\n typed === undefined || Number.isNaN(typed) ? columnValue(column, row.data) : typed;\n editing = { ...cell, draft };\n // 编辑器渲染出来后焦点移进去(autofocus 对后插入的元素不可靠,日期这类分段输入也没有 autofocus)\n void nextTick(() => {\n const element = cellElement(cell);\n if (element) focusFirst(element);\n });\n }\n\n function cancelEdit() {\n if (!editing) return;\n const cell = { row: editing.row, column: editing.column };\n editing = null;\n focusCell(cell);\n }\n\n /** 提交:列的 validate、onCellChange 都接受了才收起;step 是提交后去哪一格(Tab 往后、Shift + Tab 往前) */\n async function commitEdit(step: 0 | 1 | -1 = 0) {\n const current = editing;\n const row = current && rowOf(current.row);\n const column = current && columnOf(current.column);\n if (!current || !row || !column || current.saving) return;\n const invalid = column.validate?.(current.draft, row.data);\n if (invalid) {\n editing = { ...current, error: invalid };\n return;\n }\n editing = { ...current, saving: true };\n const result = await onCellChange?.(row.data, column.key, current.draft);\n if (typeof result === 'string' && result) {\n editing = { ...current, error: result, saving: false };\n return;\n }\n editing = null;\n const next = step === 0 ? undefined : neighbourEditable(current, step);\n if (next) startEdit(next);\n else focusCell(current);\n }\n\n /** 这一行里往后(往前)的下一个可编辑的格 */\n function neighbourEditable(cell: CellPosition, step: 1 | -1): CellPosition | undefined {\n const row = rowOf(cell.row);\n if (!row) return undefined;\n const hidden = new Set(hiddenColumns);\n const visible = arranged.filter((column) => !hidden.has(column.key));\n const from = visible.findIndex((column) => column.key === cell.column);\n for (let index = from + step; index >= 0 && index < visible.length; index += step) {\n const column = visible[index]!;\n if (isCellEditable(column, row.data)) return { row: cell.row, column: column.key };\n }\n return undefined;\n }\n\n function onEditKeydown(event: KeyboardEvent) {\n // 编辑器自己处理了的键(下拉框的 Enter 打开列表)不再当作提交\n if (event.defaultPrevented) return;\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n cancelEdit();\n } else if (event.key === 'Enter') {\n event.preventDefault();\n void commitEdit();\n } else if (event.key === 'Tab') {\n event.preventDefault();\n void commitEdit(event.shiftKey ? -1 : 1);\n }\n }\n\n /** 正在编辑的格里的编辑器:列的 editor,或按列选的默认编辑器 */\n function renderEditor(row: T, column: DataTableColumn<T>): VNodeChild {\n const current = editing!;\n const context: CellEditorContext<T> = {\n row,\n value: current.draft,\n onChange: (value) => (editing = { ...current, draft: value, error: undefined }),\n commit: () => void commitEdit(),\n cancel: cancelEdit,\n error: current.error,\n };\n const label = plainText(column.header) ?? column.key;\n const common = {\n size: 'sm',\n invalid: !!current.error,\n 'aria-label': label,\n // 编辑器占满格子\n propsRoot: s.editorControl,\n } as const;\n const editor = column.editor ? (\n column.editor(context)\n ) : column.statuses ? (\n // 状态:选了就提交\n <Select\n {...common}\n items={column.statuses.options()}\n _value={typeof current.draft === 'string' ? current.draft : null}\n onUpdate:_value={(value: string | null) => {\n editing = { ...current, draft: value };\n void commitEdit();\n }}\n />\n ) : ['number', 'currency', 'percent'].includes(columnType(column, rows)) ? (\n <NumberField\n {...common}\n stepper={false}\n _value={typeof current.draft === 'number' ? current.draft : null}\n onUpdate:_value={context.onChange}\n />\n ) : (\n <TextField\n {...common}\n _value={\n typeof current.draft === 'string' || typeof current.draft === 'number'\n ? String(current.draft)\n : ''\n }\n onUpdate:_value={context.onChange}\n />\n );\n return (\n <div\n {...mergeAttrs<'div'>(s.editor)}\n // 焦点离开这一格(点了别处):提交\n onFocusout={(event: FocusEvent) => {\n const cell = (event.currentTarget as HTMLElement).closest('td');\n if (!cell?.contains(event.relatedTarget as Node | null)) void commitEdit();\n }}\n >\n {editor}\n {current.error && (\n <div role=\"alert\" {...mergeAttrs<'div'>(s.editorError)}>\n {current.error}\n </div>\n )}\n </div>\n );\n }\n\n /**\n * 能拖动时行的属性:使用方的 propsRow 加上拖动要的属性。在 setup 里建一次(函数不变,行的 memo 才有效);\n * 里面读的拖动状态由各行的渲染跟踪,被拖的行照样更新\n */\n const rowPropsWithHandle = (row: T, index: number) =>\n mergeAttrs<'tr'>({}, typeof propsRow === 'function' ? propsRow(row, index) : propsRow, {\n ...sortable.itemProps(models[index]!.key),\n sx: sortable.active === models[index]!.key ? dataTableStateStyles.rowDragging : undefined,\n });\n\n /** 第一个数据列前面:拖动的把手,再是使用方的 leading */\n function renderLeading(row: DataTableRowModel<T>, index: number) {\n return (\n <>\n <ButtonBase\n {...s.rowHandle}\n ripple={false}\n aria-label={lang.sortableHandle(rowLabel(columns, row))}\n {...sortable.handleProps(row.key)}\n >\n <Icon icon={GripVertical} />\n </ButtonBase>\n {leading?.(row, index)}\n </>\n );\n }\n\n function onSortClick(key: string, event: MouseEvent) {\n if (multiSort) sorts = nextSorts(sorts, key, event.shiftKey);\n else sort = nextSort(sort, key);\n }\n\n function setSort(key: string, direction: SortState['direction'] | null) {\n if (multiSort) {\n const rest = sorts.filter((item) => item.key !== key);\n sorts = direction ? [{ key, direction }, ...rest] : rest;\n } else sort = direction ? { key, direction } : null;\n }\n\n /** 往前、往后移一列:与相邻的可见列交换位置 */\n function move(key: string, step: 1 | -1) {\n const hidden = new Set(hiddenColumns);\n const keys = arranged.map((column) => column.key);\n const visible = keys.filter((item) => !hidden.has(item));\n const neighbour = visible[visible.indexOf(key) + step];\n if (!neighbour) return;\n const [from, to] = [keys.indexOf(key), keys.indexOf(neighbour)];\n [keys[from], keys[to]] = [neighbour, key];\n columnOrder = keys;\n }\n\n function onColumnAction(key: string, action: keyof ColumnMenuActions, width: number) {\n if (action === 'sortAscending') setSort(key, 'ascending');\n else if (action === 'sortDescending') setSort(key, 'descending');\n else if (action === 'unsort') setSort(key, null);\n else if (action === 'moveBackward') move(key, -1);\n else if (action === 'moveForward') move(key, 1);\n else if (action === 'hide') hiddenColumns = [...hiddenColumns, key];\n else {\n const side = action === 'pinStart' ? 'start' : action === 'pinEnd' ? 'end' : null;\n columnPins = { ...columnPins, [key]: side };\n // 固定列的偏移按列宽累加:没写宽度的列记下它现在的宽度\n const column = columns.find((item) => item.key === key);\n if (side && column?.width == null && columnWidths[key] == null && width > 0)\n columnWidths = { ...columnWidths, [key]: width };\n }\n }\n\n function toggleRow(key: string) {\n selected = [...toggleSelection(selected, key, { mode: selectionMode ?? 'multiple' })];\n }\n\n // 用集合查找:逐个 includes 是 O(n²),十万行全选会卡住页面;不能勾选的行跳过\n function toggleAll() {\n const keys = (\n isRowSelectable ? models.filter((row) => isRowSelectable(row.data)) : models\n ).map((row) => row.key);\n const current = new Set(selected);\n const all = keys.length > 0 && keys.every((key) => current.has(key));\n if (!all) {\n selected = [...new Set([...selected, ...keys])];\n return;\n }\n const visible = new Set(keys);\n selected = selected.filter((key) => !visible.has(key));\n }\n\n function toggleDetail(key: string) {\n expandedRows = [...toggleSelection(expandedRows, key, { mode: 'multiple' })];\n }\n\n return () => (\n <>\n <DataTableView\n {...mergePartProps<DataTableViewProps<T>>(\n {\n rows: models,\n columns: arranged,\n widths: columnWidths,\n ...appearance(),\n },\n rest as Partial<DataTableViewProps<T>>,\n {\n rows: models,\n columns: arranged,\n sort: activeSort(),\n onSortClick,\n filters,\n onFilterChange: (key: string, values: string[]) =>\n (filters = { ...filters, [key]: values }),\n filterOptionsOf: (column) => options.get(column.key) ?? ([] as ColumnFilterOption[]),\n hiddenColumns,\n onColumnAction,\n leading: reorderable() ? renderLeading : leading,\n // 不拖动时原样交给行:每次新建的函数会让各行的 memo 失效,勾选一行全部重渲染\n propsRow: reorderable() ? rowPropsWithHandle : propsRow,\n ...({ 'data-table': tableMarker } as Partial<DataTableViewProps<T>>),\n cellNavigation: cellNavigation ?? columns.some((column) => column.editable),\n editing: editing && { row: editing.row, column: editing.column },\n renderEditor,\n rowErrors: validateRow,\n onEditStart: startEdit,\n onEditKeydown,\n selectionMode,\n isRowSelectable,\n selectedKeys: selected,\n onToggleRow: toggleRow,\n onToggleAll: toggleAll,\n widths: columnWidths,\n onResize: (key: string, width: number) =>\n (columnWidths = { ...columnWidths, [key]: width }),\n expandedRows,\n onToggleDetail: toggleDetail,\n },\n )}\n />\n {onReorder && (\n <>\n <VisuallyHidden id={sortable.instructionsId}>\n {lang.sortableInstructions}\n </VisuallyHidden>\n <VisuallyHidden aria-live=\"assertive\">{sortable.announcement}</VisuallyHidden>\n </>\n )}\n </>\n );\n },\n);\n"],"mappings":"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"}
|
|
@@ -3,7 +3,6 @@ import { type VNodeChild } from 'vue';
|
|
|
3
3
|
import { type ButtonBaseProps } from '../base/ButtonBase';
|
|
4
4
|
import { type IconButtonProps } from '../base/IconButton';
|
|
5
5
|
import { type EmptyProps } from '../feedback/Empty';
|
|
6
|
-
import { type ProgressProps } from '../feedback/Progress';
|
|
7
6
|
import { type SkeletonShapeProps } from '../feedback/Skeleton';
|
|
8
7
|
import { type CheckboxProps } from '../input/Checkbox';
|
|
9
8
|
import { type RadioProps } from '../input/Radio';
|
|
@@ -155,8 +154,6 @@ export interface DataTableViewProps<T> extends PartProps<'table', 'role'>, DataT
|
|
|
155
154
|
propsLeading?: PartProps<'div'>;
|
|
156
155
|
/** 第一个数据列里内容的包装(有 leading 时才有)的部件属性 */
|
|
157
156
|
propsContent?: PartProps<'div'>;
|
|
158
|
-
/** 刷新中表头下方的进度条的部件属性 */
|
|
159
|
-
propsProgress?: Partial<ProgressProps>;
|
|
160
157
|
/** 空状态所在的单元格(td,跨全部列)的部件属性 */
|
|
161
158
|
propsEmptyCell?: PartProps<'td', 'colspan'>;
|
|
162
159
|
/** 详情列的列标题(th,文字只给读屏)的部件属性 */
|