@zerodep-dev/vue-tsx-pro-ui 0.2.0 → 0.3.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/base/Badge.js +3 -3
- package/dist/base/Badge.js.map +1 -1
- package/dist/base/Can.d.ts +12 -0
- package/dist/base/Can.js +20 -0
- package/dist/base/Can.js.map +1 -0
- package/dist/base/ClientOnly.d.ts +10 -0
- package/dist/base/ClientOnly.js +18 -0
- package/dist/base/ClientOnly.js.map +1 -0
- package/dist/base/CloseButton.js +3 -3
- package/dist/base/CloseButton.js.map +1 -1
- package/dist/base/CodeBlock.js +5 -5
- package/dist/base/CodeBlock.js.map +1 -1
- package/dist/base/CopyButton.js +3 -3
- package/dist/base/CopyButton.js.map +1 -1
- package/dist/base/Icon.js +3 -3
- package/dist/base/Icon.js.map +1 -1
- package/dist/base/Indicator.js +3 -3
- package/dist/base/Indicator.js.map +1 -1
- package/dist/base/StatusDot.js +3 -3
- package/dist/base/StatusDot.js.map +1 -1
- package/dist/base/Tag.js +5 -5
- package/dist/base/Tag.js.map +1 -1
- package/dist/base/button-core.js +4 -4
- package/dist/base/button-core.js.map +1 -1
- package/dist/charts/AreaChart.js +3 -3
- package/dist/charts/AreaChart.js.map +1 -1
- package/dist/charts/BarChart.js +3 -3
- package/dist/charts/BarChart.js.map +1 -1
- package/dist/charts/ChordChart.js +3 -3
- package/dist/charts/ChordChart.js.map +1 -1
- package/dist/charts/ComboChart.js +3 -3
- package/dist/charts/ComboChart.js.map +1 -1
- package/dist/charts/DonutChart.js +3 -3
- package/dist/charts/DonutChart.js.map +1 -1
- package/dist/charts/FunnelChart.js +5 -5
- package/dist/charts/FunnelChart.js.map +1 -1
- package/dist/charts/GraphChart.js +3 -3
- package/dist/charts/GraphChart.js.map +1 -1
- package/dist/charts/LineChart.js +3 -3
- package/dist/charts/LineChart.js.map +1 -1
- package/dist/charts/MapChart.js +7 -7
- package/dist/charts/MapChart.js.map +1 -1
- package/dist/charts/RadarChart.js +16 -15
- package/dist/charts/RadarChart.js.map +1 -1
- package/dist/charts/SankeyChart.js +3 -3
- package/dist/charts/SankeyChart.js.map +1 -1
- package/dist/charts/ScatterChart.js +3 -3
- package/dist/charts/ScatterChart.js.map +1 -1
- package/dist/charts/TimelineChart.js +6 -5
- package/dist/charts/TimelineChart.js.map +1 -1
- package/dist/charts/TreemapChart.js +3 -3
- package/dist/charts/TreemapChart.js.map +1 -1
- package/dist/charts/WaterfallChart.js +3 -3
- package/dist/charts/WaterfallChart.js.map +1 -1
- package/dist/charts/XYChart.js +9 -7
- package/dist/charts/XYChart.js.map +1 -1
- package/dist/charts/frame.js +10 -8
- package/dist/charts/frame.js.map +1 -1
- package/dist/charts/single.js +4 -3
- package/dist/charts/single.js.map +1 -1
- package/dist/config/Provider.d.ts +3 -1
- package/dist/config/Provider.js +9 -4
- package/dist/config/Provider.js.map +1 -1
- package/dist/config/context.d.ts +18 -0
- package/dist/config/context.js +16 -1
- package/dist/config/context.js.map +1 -1
- package/dist/config/createUi.d.ts +20 -3
- package/dist/config/createUi.js +80 -49
- package/dist/config/createUi.js.map +1 -1
- package/dist/config/layout/preference.d.ts +43 -0
- package/dist/config/layout/preference.js +108 -0
- package/dist/config/layout/preference.js.map +1 -0
- package/dist/date/Calendar.js +3 -3
- package/dist/date/Calendar.js.map +1 -1
- package/dist/date/CalendarBoard.js +9 -9
- package/dist/date/CalendarBoard.js.map +1 -1
- package/dist/date/CalendarView.js +7 -7
- package/dist/date/CalendarView.js.map +1 -1
- package/dist/date/DateField.js +3 -3
- package/dist/date/DateField.js.map +1 -1
- package/dist/date/DatePicker.js +5 -5
- package/dist/date/DatePicker.js.map +1 -1
- package/dist/date/DateRangePicker.js +5 -5
- package/dist/date/DateRangePicker.js.map +1 -1
- package/dist/date/DateSegmentGroup.js +5 -5
- package/dist/date/DateSegmentGroup.js.map +1 -1
- package/dist/date/QuarterCalendar.js +6 -6
- package/dist/date/QuarterCalendar.js.map +1 -1
- package/dist/date/RangeCalendar.js +3 -3
- package/dist/date/RangeCalendar.js.map +1 -1
- package/dist/date/SegmentedField.js +3 -3
- package/dist/date/SegmentedField.js.map +1 -1
- package/dist/date/TimeField.js +3 -3
- package/dist/date/TimeField.js.map +1 -1
- package/dist/date/TimePicker.js +4 -4
- package/dist/date/TimePicker.js.map +1 -1
- package/dist/date/date-segments.js +1 -1
- package/dist/date/date-segments.js.map +1 -1
- package/dist/date/picker-popover.js +3 -2
- package/dist/date/picker-popover.js.map +1 -1
- package/dist/diagram/Diagram.js +12 -12
- package/dist/diagram/Diagram.js.map +1 -1
- package/dist/diagram/DiagramLanes.js +3 -3
- package/dist/diagram/DiagramLanes.js.map +1 -1
- package/dist/diagram/DiagramMinimap.js +4 -4
- package/dist/diagram/DiagramMinimap.js.map +1 -1
- package/dist/display/Accordion.js +6 -6
- package/dist/display/Accordion.js.map +1 -1
- package/dist/display/AnimatedNumber.js +4 -4
- package/dist/display/AnimatedNumber.js.map +1 -1
- package/dist/display/Avatar.js +4 -4
- package/dist/display/Avatar.js.map +1 -1
- package/dist/display/Checklist.js +7 -7
- package/dist/display/Checklist.js.map +1 -1
- package/dist/display/Collapse.js +8 -5
- package/dist/display/Collapse.js.map +1 -1
- package/dist/display/Collapsible.js +3 -3
- package/dist/display/Collapsible.js.map +1 -1
- package/dist/display/Descriptions.js +5 -5
- package/dist/display/Descriptions.js.map +1 -1
- package/dist/display/Ellipsis.js +3 -3
- package/dist/display/Ellipsis.js.map +1 -1
- package/dist/display/FieldCompare.js +4 -4
- package/dist/display/FieldCompare.js.map +1 -1
- package/dist/display/Gauge.js +11 -11
- package/dist/display/Gauge.js.map +1 -1
- package/dist/display/Image.js +4 -4
- package/dist/display/Image.js.map +1 -1
- package/dist/display/Marquee.js +3 -3
- package/dist/display/Marquee.js.map +1 -1
- package/dist/display/ProportionBar.js +4 -4
- package/dist/display/ProportionBar.js.map +1 -1
- package/dist/display/Sortable.js +4 -4
- package/dist/display/Sortable.js.map +1 -1
- package/dist/display/Sparkline.js +6 -6
- package/dist/display/Sparkline.js.map +1 -1
- package/dist/display/Stat.js +3 -3
- package/dist/display/Stat.js.map +1 -1
- package/dist/display/Timeline.js +7 -7
- package/dist/display/Timeline.js.map +1 -1
- package/dist/display/Tree.js +10 -10
- package/dist/display/Tree.js.map +1 -1
- package/dist/display/VirtualList.js +7 -7
- package/dist/display/VirtualList.js.map +1 -1
- package/dist/display/Watermark.js +3 -3
- package/dist/display/Watermark.js.map +1 -1
- package/dist/docs-kit/CodeGroup.js +5 -5
- package/dist/docs-kit/CodeGroup.js.map +1 -1
- package/dist/docs-kit/CodePreview.js +6 -6
- package/dist/docs-kit/CodePreview.js.map +1 -1
- package/dist/docs-kit/PropsTable.js +4 -4
- package/dist/docs-kit/PropsTable.js.map +1 -1
- package/dist/feedback/Alert.js +5 -5
- package/dist/feedback/Alert.js.map +1 -1
- package/dist/feedback/Banner.js +5 -5
- package/dist/feedback/Banner.js.map +1 -1
- package/dist/feedback/Empty.js +4 -4
- package/dist/feedback/Empty.js.map +1 -1
- package/dist/feedback/LoadingBar.js +20 -13
- package/dist/feedback/LoadingBar.js.map +1 -1
- package/dist/feedback/LoadingOverlay.js +3 -3
- package/dist/feedback/LoadingOverlay.js.map +1 -1
- package/dist/feedback/Progress.js +14 -14
- package/dist/feedback/Progress.js.map +1 -1
- package/dist/feedback/Result.js +4 -4
- package/dist/feedback/Result.js.map +1 -1
- package/dist/feedback/SkeletonText.js +3 -3
- package/dist/feedback/SkeletonText.js.map +1 -1
- package/dist/feedback/Toast.js +6 -6
- package/dist/feedback/Toast.js.map +1 -1
- package/dist/form/Field.js +7 -7
- package/dist/form/Field.js.map +1 -1
- package/dist/form/Form.js +3 -3
- package/dist/form/Form.js.map +1 -1
- package/dist/form/FormErrorSummary.js +3 -3
- package/dist/form/FormErrorSummary.js.map +1 -1
- package/dist/form/FormSection.js +6 -6
- package/dist/form/FormSection.js.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.js +10 -5
- package/dist/input/Autocomplete.js +3 -3
- package/dist/input/Autocomplete.js.map +1 -1
- package/dist/input/Cascader.js +7 -7
- package/dist/input/Cascader.js.map +1 -1
- package/dist/input/CharacterCount.js +3 -3
- package/dist/input/CharacterCount.js.map +1 -1
- package/dist/input/Checkbox.js +5 -5
- package/dist/input/Checkbox.js.map +1 -1
- package/dist/input/ColorField.js +4 -4
- package/dist/input/ColorField.js.map +1 -1
- package/dist/input/Combobox.js +4 -4
- package/dist/input/Combobox.js.map +1 -1
- package/dist/input/ConditionBuilder.js +15 -14
- package/dist/input/ConditionBuilder.js.map +1 -1
- package/dist/input/FileDrop.js +3 -3
- package/dist/input/FileDrop.js.map +1 -1
- package/dist/input/Listbox.js +9 -9
- package/dist/input/Listbox.js.map +1 -1
- package/dist/input/Mentions.js +3 -3
- package/dist/input/Mentions.js.map +1 -1
- package/dist/input/NumberField.js +3 -3
- package/dist/input/NumberField.js.map +1 -1
- package/dist/input/NumberRangeField.js +5 -5
- package/dist/input/NumberRangeField.js.map +1 -1
- package/dist/input/OtpField.js +5 -5
- package/dist/input/OtpField.js.map +1 -1
- package/dist/input/PeoplePicker.js +6 -6
- package/dist/input/PeoplePicker.js.map +1 -1
- package/dist/input/PeoplePickerPanel.js +4 -4
- package/dist/input/PeoplePickerPanel.js.map +1 -1
- package/dist/input/Radio.js +4 -4
- package/dist/input/Radio.js.map +1 -1
- package/dist/input/Rating.js +5 -5
- package/dist/input/Rating.js.map +1 -1
- package/dist/input/Select.js +4 -4
- package/dist/input/Select.js.map +1 -1
- package/dist/input/SelectedTags.js +4 -4
- package/dist/input/SelectedTags.js.map +1 -1
- package/dist/input/Slider.js +6 -6
- package/dist/input/Slider.js.map +1 -1
- package/dist/input/Switch.js +3 -3
- package/dist/input/Switch.js.map +1 -1
- package/dist/input/TagsInput.js +5 -5
- package/dist/input/TagsInput.js.map +1 -1
- package/dist/input/TextField.js +5 -5
- package/dist/input/TextField.js.map +1 -1
- package/dist/input/Textarea.js +4 -4
- package/dist/input/Textarea.js.map +1 -1
- package/dist/input/ToggleGroup.js +4 -4
- package/dist/input/ToggleGroup.js.map +1 -1
- package/dist/input/Transfer.js +8 -8
- package/dist/input/Transfer.js.map +1 -1
- package/dist/input/TreeSelect.js +6 -6
- package/dist/input/TreeSelect.js.map +1 -1
- package/dist/input/Upload.js +8 -8
- package/dist/input/Upload.js.map +1 -1
- package/dist/layout/Affix.js +4 -4
- package/dist/layout/Affix.js.map +1 -1
- package/dist/layout/ResizeHandle.d.ts +6 -2
- package/dist/layout/ResizeHandle.js +32 -9
- package/dist/layout/ResizeHandle.js.map +1 -1
- package/dist/layout/ScrollArea.js +3 -3
- package/dist/layout/ScrollArea.js.map +1 -1
- package/dist/layout/Separator.js +3 -3
- package/dist/layout/Separator.js.map +1 -1
- package/dist/layout/Splitter.js +3 -3
- package/dist/layout/Splitter.js.map +1 -1
- package/dist/layout/ViewportFill.d.ts +2 -2
- package/dist/layout/ViewportFill.js +4 -4
- package/dist/layout/ViewportFill.js.map +1 -1
- package/dist/navigation/Breadcrumbs.js +4 -4
- package/dist/navigation/Breadcrumbs.js.map +1 -1
- package/dist/navigation/NavBar.js +6 -6
- package/dist/navigation/NavBar.js.map +1 -1
- package/dist/navigation/NavList.d.ts +1 -0
- package/dist/navigation/NavList.js +20 -12
- package/dist/navigation/NavList.js.map +1 -1
- package/dist/navigation/NavMenu.d.ts +27 -0
- package/dist/navigation/NavMenu.js +54 -0
- package/dist/navigation/NavMenu.js.map +1 -0
- package/dist/navigation/PageJump.js +3 -3
- package/dist/navigation/PageJump.js.map +1 -1
- package/dist/navigation/PageSizeSelect.js +4 -4
- package/dist/navigation/PageSizeSelect.js.map +1 -1
- package/dist/navigation/Pagination.js +6 -6
- package/dist/navigation/Pagination.js.map +1 -1
- package/dist/navigation/PaginationBar.js +5 -5
- package/dist/navigation/PaginationBar.js.map +1 -1
- package/dist/navigation/Steps.js +4 -4
- package/dist/navigation/Steps.js.map +1 -1
- package/dist/navigation/TableOfContents.js +5 -5
- package/dist/navigation/TableOfContents.js.map +1 -1
- package/dist/navigation/Tabs.js +7 -7
- package/dist/navigation/Tabs.js.map +1 -1
- package/dist/navigation/Toolbar.js +3 -3
- package/dist/navigation/Toolbar.js.map +1 -1
- package/dist/overlay/AlertDialog.js +4 -4
- package/dist/overlay/AlertDialog.js.map +1 -1
- package/dist/overlay/CommandPalette.js +4 -4
- package/dist/overlay/CommandPalette.js.map +1 -1
- package/dist/overlay/ContextMenu.js +3 -3
- package/dist/overlay/ContextMenu.js.map +1 -1
- package/dist/overlay/Dialog.js +4 -4
- package/dist/overlay/Dialog.js.map +1 -1
- package/dist/overlay/Drawer.js +5 -5
- package/dist/overlay/Drawer.js.map +1 -1
- package/dist/overlay/HoverCard.js +5 -3
- package/dist/overlay/HoverCard.js.map +1 -1
- package/dist/overlay/Lightbox.js +5 -5
- package/dist/overlay/Lightbox.js.map +1 -1
- package/dist/overlay/Menu.js +7 -7
- package/dist/overlay/Menu.js.map +1 -1
- package/dist/overlay/ModalCore.js +9 -9
- package/dist/overlay/ModalCore.js.map +1 -1
- package/dist/overlay/Popconfirm.js +6 -6
- package/dist/overlay/Popconfirm.js.map +1 -1
- package/dist/overlay/Popover.js +5 -3
- package/dist/overlay/Popover.js.map +1 -1
- package/dist/overlay/Tooltip.js +5 -3
- package/dist/overlay/Tooltip.js.map +1 -1
- package/dist/overlay/Tour.js +8 -8
- package/dist/overlay/Tour.js.map +1 -1
- package/dist/overlay/detached.d.ts +2 -0
- package/dist/overlay/detached.js +73 -42
- package/dist/overlay/detached.js.map +1 -1
- package/dist/patterns/ActionBar.js +3 -3
- package/dist/patterns/ActionBar.js.map +1 -1
- package/dist/patterns/AppShell.d.ts +28 -1
- package/dist/patterns/AppShell.js +158 -52
- package/dist/patterns/AppShell.js.map +1 -1
- package/dist/patterns/AuthLayout.d.ts +28 -0
- package/dist/patterns/AuthLayout.js +77 -0
- package/dist/patterns/AuthLayout.js.map +1 -0
- package/dist/patterns/CommentThread.js +9 -9
- package/dist/patterns/CommentThread.js.map +1 -1
- package/dist/patterns/DataImport.js +4 -4
- package/dist/patterns/DataImport.js.map +1 -1
- package/dist/patterns/DeadlineBadge.js +3 -3
- package/dist/patterns/DeadlineBadge.js.map +1 -1
- package/dist/patterns/FilterBar.d.ts +11 -1
- package/dist/patterns/FilterBar.js +39 -10
- package/dist/patterns/FilterBar.js.map +1 -1
- package/dist/patterns/FormDialog.d.ts +18 -0
- package/dist/patterns/FormDialog.js +46 -0
- package/dist/patterns/FormDialog.js.map +1 -0
- package/dist/patterns/KanbanBoard.js +8 -8
- package/dist/patterns/KanbanBoard.js.map +1 -1
- package/dist/patterns/LayoutSwitcher.d.ts +12 -0
- package/dist/patterns/LayoutSwitcher.js +79 -0
- package/dist/patterns/LayoutSwitcher.js.map +1 -0
- package/dist/patterns/NotificationCenter.js +8 -8
- package/dist/patterns/NotificationCenter.js.map +1 -1
- package/dist/patterns/PageTabs.d.ts +12 -7
- package/dist/patterns/PageTabs.js +41 -29
- package/dist/patterns/PageTabs.js.map +1 -1
- package/dist/patterns/QueryTable.d.ts +7 -1
- package/dist/patterns/QueryTable.js +116 -81
- package/dist/patterns/QueryTable.js.map +1 -1
- package/dist/patterns/SlideCaptcha.d.ts +80 -0
- package/dist/patterns/SlideCaptcha.js +354 -0
- package/dist/patterns/SlideCaptcha.js.map +1 -0
- package/dist/patterns/StepsForm.js +7 -7
- package/dist/patterns/StepsForm.js.map +1 -1
- package/dist/patterns/ThemeSwitcher.js +3 -3
- package/dist/patterns/ThemeSwitcher.js.map +1 -1
- package/dist/patterns/index.d.ts +7 -3
- package/dist/patterns/index.js +5 -1
- package/dist/patterns/messages.d.ts +37 -1
- package/dist/patterns/messages.js +38 -2
- package/dist/patterns/messages.js.map +1 -1
- package/dist/table/ColumnHeaderActions.js +3 -3
- package/dist/table/ColumnHeaderActions.js.map +1 -1
- package/dist/table/ColumnResizer.js +3 -3
- package/dist/table/ColumnResizer.js.map +1 -1
- package/dist/table/ColumnSettings.js +5 -5
- package/dist/table/ColumnSettings.js.map +1 -1
- package/dist/table/ColumnTable.js +8 -8
- package/dist/table/ColumnTable.js.map +1 -1
- package/dist/table/DataTable.js +8 -8
- package/dist/table/DataTable.js.map +1 -1
- package/dist/table/DataTableView.js +21 -21
- package/dist/table/DataTableView.js.map +1 -1
- package/dist/table/PivotTable.js +3 -3
- package/dist/table/PivotTable.js.map +1 -1
- package/dist/table/SelectionBar.js +4 -4
- package/dist/table/SelectionBar.js.map +1 -1
- package/dist/table/TreeDataTable.js +5 -5
- package/dist/table/TreeDataTable.js.map +1 -1
- package/dist/theme-customize/ThemeCustomizer.js +7 -6
- package/dist/theme-customize/ThemeCustomizer.js.map +1 -1
- package/dist/theme-customize/ThemeThumbnail.js +3 -3
- package/dist/theme-customize/ThemeThumbnail.js.map +1 -1
- package/dist/three/background/AuroraBackground.js +3 -3
- package/dist/three/background/AuroraBackground.js.map +1 -1
- package/dist/three/background/MeshGradient.js +3 -3
- package/dist/three/background/MeshGradient.js.map +1 -1
- package/dist/three/background/ShaderBackground.js +3 -3
- package/dist/three/background/ShaderBackground.js.map +1 -1
- package/dist/three/core/EffectSurface.js +3 -3
- package/dist/three/core/EffectSurface.js.map +1 -1
- package/dist/three/core/ThreeCanvas.js +4 -4
- package/dist/three/core/ThreeCanvas.js.map +1 -1
- package/dist/three/effects/Confetti.js +3 -3
- package/dist/three/effects/Confetti.js.map +1 -1
- package/dist/three/effects/ParticleField.js +3 -3
- package/dist/three/effects/ParticleField.js.map +1 -1
- package/dist/three/effects/WaveGrid.js +3 -3
- package/dist/three/effects/WaveGrid.js.map +1 -1
- package/dist/three/globe/Globe.js +3 -3
- package/dist/three/globe/Globe.js.map +1 -1
- package/dist/three/model/ModelViewer.js +3 -3
- package/dist/three/model/ModelViewer.js.map +1 -1
- package/dist/utils/data/number.d.ts +5 -0
- package/dist/utils/data/number.js +8 -1
- package/dist/utils/data/number.js.map +1 -1
- package/dist/utils/dom/element.d.ts +3 -1
- package/dist/utils/dom/element.js +8 -4
- package/dist/utils/dom/element.js.map +1 -1
- package/dist/utils/dom/fullscreen.d.ts +1 -1
- package/dist/utils/dom/fullscreen.js +12 -8
- package/dist/utils/dom/fullscreen.js.map +1 -1
- package/dist/utils/dom/mounted.d.ts +7 -0
- package/dist/utils/dom/mounted.js +16 -0
- package/dist/utils/dom/mounted.js.map +1 -0
- package/dist/utils/dom/overflow.js +2 -1
- package/dist/utils/dom/overflow.js.map +1 -1
- package/dist/utils/dom/pan-zoom.js +2 -1
- package/dist/utils/dom/pan-zoom.js.map +1 -1
- package/dist/utils/props.js +3 -13
- package/dist/utils/props.js.map +1 -1
- package/dist/utils/request/http.d.ts +12 -0
- package/dist/utils/request/http.js +6 -2
- package/dist/utils/request/http.js.map +1 -1
- package/dist/utils/request/use-request.js +4 -2
- package/dist/utils/request/use-request.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataImport.js","names":["_isVNode","_vtp_readonlyProp","_vtp_restProps","_vue_ref","_vue_shallowRef","_createVNode","_vue_unref","_mergeProps"],"sources":["../../src/patterns/DataImport.tsx"],"sourcesContent":["/**\n * 数据导入:上传文件 → 对应列 → 逐行校验与预览(有错的格子就地改)→ 确认导入。批量导入零件、报价、人员这类数据用它。\n *\n * <DataImport\n * fields={[{ key: 'part', label: '零件号', required: true, aliases: ['料号'] }, { key: 'quantity', label: '数量', type: 'number' }]}\n * schema={z.object({ part: z.string().min(1), quantity: z.number().int().positive() })}\n * parse={readXlsx} // 读 Excel:@zerodep-dev/vue-tsx-pro-ui/xlsx;不写时读 CSV\n * accept=\".xlsx,.csv\"\n * onImport={(rows) => api.importParts(rows)}\n * />\n *\n * - 上传:选文件或拖进来,parse 读成表(SheetTable);几张工作表时先选一张。\n * - 对应列:按字段的 label 与 aliases 自动对上文件里的列,可以改;必填的字段没对上时不能往下走。\n * - 校验:空的格子(空字符串、只有空白)是 null,同 useForm 的约定(选填的字段在 schema 里写 .nullable())。\n * 有 schema(zod 对象)时逐行解析,错误标在对应的格子上;没有时按 required、type(数字列要是数字)检查,\n * 也可以写 validate。有错的格子可以直接改(表格就地编辑),改完重新校验;“只看有错的行”筛出要处理的。\n * - 导入:onImport 拿到通过校验的行(schema 解析后的值);返回 Promise 时按钮加载中,完成后显示结果。\n *\n * 部件:Root(div)、Steps、Drop(FileDrop)、Mapping(ColumnTable)、Preview(DataTable)、Actions(Flex)。\n */\nimport { CircleAlert, CircleCheck, Download } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { shallowRef, type VNodeChild } from 'vue';\nimport type { $ZodType } from 'zod/v4/core';\nimport { safeParse } from 'zod/v4/core';\nimport { Badge } from '../base/Badge';\nimport { Button, type ButtonProps } from '../base/Button';\nimport { Icon } from '../base/Icon';\nimport { Small } from '../base/Text';\nimport { useLang } from '../config/context';\nimport { useMessages } from '../config/lang/messages';\nimport { Alert } from '../feedback/Alert';\nimport { FileDrop, type FileDropProps } from '../input/FileDrop';\nimport { Select } from '../input/Select';\nimport { Switch } from '../input/Switch';\nimport { Flex, type FlexProps } from '../layout/Flex';\nimport { Steps, type StepsProps } from '../navigation/Steps';\nimport type { DataTableColumn } from '../table/columns';\nimport { ColumnTable } from '../table/ColumnTable';\nimport { DataTable, type DataTableProps } from '../table/DataTable';\nimport { readCsvFile, type SheetTable, type SheetValue } from '../table/sheet';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { patternsMessages } from './messages';\n\n/** 要导入的一个字段 */\nexport interface ImportField {\n /** 字段名:导入的行里的键 */\n key: string;\n /** 名称:对应列时显示,也用来自动对上文件里的同名列 */\n label: string;\n /** 必填:没对应上不能往下走;没有 schema 时空值算错 */\n required?: boolean;\n /** 文件里这一列可能的其他叫法(“料号”“Part No.”):自动对应时也认 */\n aliases?: readonly string[];\n /** 值的类型(没有 schema 时):number 把文字转成数字,转不了算错;默认 text */\n type?: 'text' | 'number';\n /** 预览表里这一列的宽度(px) */\n width?: number;\n}\n\n/** 预览里的一行 */\ninterface ImportRow {\n /** 第几行数据(从 1 起) */\n line: number;\n /** 对应后的原值(字段名 → 值),就地改的是它 */\n raw: Record<string, unknown>;\n /** 校验通过后的值 */\n value?: unknown;\n /** 字段名 → 原因 */\n errors: Record<string, string>;\n}\n\n/** 未声明的属性落到外层 div 上 */\nexport interface DataImportProps<Row> extends PartProps<'div'> {\n /** 要导入的字段,按显示的顺序 */\n fields: readonly ImportField[];\n /** 一行的 zod 对象 schema:逐行解析,错误标在格子上,onImport 拿到解析后的值 */\n schema?: $ZodType<Row>;\n /** 没有 schema 时的逐行校验:返回字段名 → 原因(在 required、type 的检查之后) */\n validate?: (row: Record<string, unknown>) => Record<string, string> | null | undefined;\n /** 读文件:返回各工作表;默认读 CSV(readCsvFile),读 Excel 传 @zerodep-dev/vue-tsx-pro-ui/xlsx 的 readXlsx */\n parse?: (file: File) => Promise<SheetTable[]>;\n /** 能选的文件类型,默认 .csv */\n accept?: string;\n /** 导入通过校验的行:返回 Promise 时按钮加载中,失败时留在预览(由调用方提示原因) */\n onImport: (rows: Row[]) => unknown;\n /** 下载模板:写了就在上传区旁边放“下载模板” */\n onTemplate?: () => void;\n /** 步骤条的部件属性 */\n propsSteps?: Partial<StepsProps>;\n /** 选文件区域的部件属性 */\n propsDrop?: Partial<FileDropProps>;\n /** 预览表(DataTable)的部件属性 */\n propsPreview?: Partial<DataTableProps<ImportRow>>;\n /** 按钮行的部件属性 */\n propsActions?: Partial<FlexProps>;\n /** 导入按钮的部件属性 */\n propsSubmit?: Partial<ButtonProps>;\n}\n\n/** DataImport 的样式:root、previewTable、validIcon、invalidIcon */\nexport const dataImportStyles = css.create({\n root: { display: 'flex', flexDirection: 'column', gap: '_lg', minWidth: 0 },\n previewTable: { maxHeight: '24rem' },\n validIcon: { color: '_successText' },\n invalidIcon: { color: '_dangerText' },\n});\n\n/** 文字规范化后比较:去掉空白、括号与大小写(“零件号 (必填)”也认作“零件号”) */\nconst normalize = (text: string) =>\n text\n .toLowerCase()\n .replace(/[((][^))]*[))]/g, '')\n .replace(/[\\s_*]/g, '');\n\n/** 按名称与别名对上文件里的列 */\nfunction autoMap(\n fields: readonly ImportField[],\n headers: readonly string[],\n): Record<string, string | null> {\n const taken = new Set<string>();\n return Object.fromEntries(\n fields.map((field) => {\n const names = [field.label, field.key, ...(field.aliases ?? [])].map(normalize);\n const header = headers.find((item) => !taken.has(item) && names.includes(normalize(item)));\n if (header) taken.add(header);\n return [field.key, header ?? null];\n }),\n );\n}\n\n/** 空值:null、空字符串 */\nconst isEmpty = (value: unknown) =>\n value == null || (typeof value === 'string' && value.trim() === '');\n\n/** 数据导入:上传 → 对应列 → 校验与预览 → 导入 */\nexport const DataImport = _component(\n <Row,>({\n fields,\n schema,\n validate,\n parse = readCsvFile,\n accept = '.csv',\n onImport,\n onTemplate,\n propsSteps,\n propsDrop,\n propsPreview,\n propsActions,\n propsSubmit,\n ...rest\n }: DataImportProps<Row>) => {\n const messages = useMessages('patterns', patternsMessages);\n const lang = useLang();\n let step = _state(0);\n let sheets = _state.raw<SheetTable[]>([]);\n let sheetName = _state<string | null>(null);\n let mapping = _state<Record<string, string | null>>({});\n let parseError = _state('');\n let onlyErrors = _state(false);\n let importing = _state(false);\n let imported = _state(0);\n // 预览的行:就地改时整个换掉\n const rows = shallowRef<ImportRow[]>([]);\n\n const sheet = () => sheets.find((item) => item.name === sheetName);\n const unmapped = () =>\n fields.filter((field) => field.required && !mapping[field.key]).map((field) => field.label);\n\n async function onFiles(files: File[]) {\n const file = files[0];\n if (!file) return;\n parseError = '';\n try {\n const read = (await parse(file)).filter((item) => item.rows.length > 0);\n if (read.length === 0) {\n parseError = messages.dataImportEmpty;\n return;\n }\n sheets = read;\n sheetName = read[0]!.name;\n mapping = autoMap(fields, read[0]!.headers);\n step = 1;\n } catch (error) {\n parseError = messages.dataImportParseFailed(\n error instanceof Error ? error.message : String(error),\n );\n }\n }\n\n function changeSheet(name: string | null) {\n sheetName = name;\n const current = sheet();\n if (current) mapping = autoMap(fields, current.headers);\n }\n\n /** 校验一行:schema 优先;否则 required、type 与 validate */\n function check(raw: Record<string, unknown>): Pick<ImportRow, 'value' | 'errors'> {\n if (schema) {\n const result = safeParse(schema, raw);\n if (result.success) return { value: result.data, errors: {} };\n const errors: Record<string, string> = {};\n for (const issue of result.error.issues) {\n const key = String(issue.path[0] ?? fields[0]?.key ?? '');\n errors[key] ??= issue.message;\n }\n return { errors };\n }\n const errors: Record<string, string> = {};\n const value: Record<string, unknown> = {};\n for (const field of fields) {\n const cell = raw[field.key];\n if (isEmpty(cell)) {\n if (field.required) errors[field.key] = lang.formRequired;\n value[field.key] = null;\n } else if (field.type === 'number') {\n const number = typeof cell === 'number' ? cell : Number(String(cell).replace(/,/g, ''));\n if (Number.isNaN(number)) errors[field.key] = messages.dataImportNotNumber;\n value[field.key] = number;\n } else value[field.key] = typeof cell === 'string' ? cell.trim() : cell;\n }\n Object.assign(errors, { ...validate?.(value), ...errors });\n return Object.keys(errors).length ? { errors } : { value, errors };\n }\n\n /** 对应完:按对应关系取出各行并校验,进入预览 */\n function preview() {\n const current = sheet();\n if (!current) return;\n rows.value = current.rows.map((row, index) => {\n const raw = Object.fromEntries(\n fields.map((field) => {\n const header = mapping[field.key];\n const value = header ? (row[header] ?? null) : null;\n return [field.key, isEmpty(value) ? null : value] as [string, SheetValue];\n }),\n );\n return { line: index + 1, raw, ...check(raw) };\n });\n onlyErrors = false;\n step = 2;\n }\n\n /** 就地改了一格:改原值、重新校验这一行 */\n function onCellChange(row: ImportRow, key: string, value: unknown) {\n rows.value = rows.value.map((item) => {\n if (item !== row) return item;\n const raw = { ...item.raw, [key]: isEmpty(value) ? null : value };\n return { ...item, raw, ...check(raw) };\n });\n }\n\n async function submit() {\n const valid = rows.value.filter((row) => Object.keys(row.errors).length === 0);\n importing = true;\n try {\n await onImport(valid.map((row) => row.value as Row));\n imported = valid.length;\n step = 3;\n } catch (error) {\n console.error('DataImport 的 onImport 失败,留在预览', error);\n } finally {\n importing = false;\n }\n }\n\n function restart() {\n sheets = [];\n sheetName = null;\n rows.value = [];\n step = 0;\n }\n\n const counts = () => {\n const invalid = rows.value.filter((row) => Object.keys(row.errors).length > 0).length;\n return { total: rows.value.length, invalid, valid: rows.value.length - invalid };\n };\n\n const previewColumns = (): DataTableColumn<ImportRow>[] => [\n { key: 'line', header: messages.dataImportRow, width: 72, value: (row) => row.line },\n {\n key: 'status',\n header: messages.dataImportStatus,\n width: 200,\n cell: (row) => {\n const first = Object.values(row.errors)[0];\n return first ? (\n <Flex align=\"center\" gap=\"xs\">\n <Icon icon={CircleAlert} sx={dataImportStyles.invalidIcon} />\n <Small tone=\"danger\">{first}</Small>\n </Flex>\n ) : (\n <Flex align=\"center\" gap=\"xs\">\n <Icon icon={CircleCheck} sx={dataImportStyles.validIcon} />\n <Small tone=\"muted\">{messages.dataImportValid}</Small>\n </Flex>\n );\n },\n },\n ...fields.map((field): DataTableColumn<ImportRow> => ({\n key: field.key,\n header: field.label,\n width: field.width,\n minWidth: 120,\n type: field.type === 'number' ? 'number' : 'text',\n value: (row) => row.raw[field.key] as SheetValue,\n editable: true,\n })),\n ];\n\n function renderUpload(): VNodeChild {\n return (\n <Flex direction=\"column\" gap=\"sm\">\n <FileDrop\n {...mergePartProps<FileDropProps>(\n { accept, description: messages.dataImportAccept(accept.split(',').join(', ')) },\n propsDrop,\n { onFiles: (files: File[]) => void onFiles(files) },\n )}\n />\n {parseError && <Alert tone=\"danger\">{parseError}</Alert>}\n {onTemplate && (\n <Flex justify=\"end\">\n <Button size=\"sm\" variant=\"ghost\" icon={Download} onClick={onTemplate}>\n {messages.dataImportTemplate}\n </Button>\n </Flex>\n )}\n </Flex>\n );\n }\n\n function renderMapping(): VNodeChild {\n const current = sheet();\n const headers = current?.headers ?? [];\n const missing = unmapped();\n const sampleOf = (header: string | null) =>\n header\n ? (current?.rows ?? [])\n .slice(0, 2)\n .map((row) => row[header])\n .filter((value) => !isEmpty(value))\n .map(String)\n .join('、')\n : '';\n return (\n <Flex direction=\"column\" gap=\"md\">\n {sheets.length > 1 && (\n <Flex align=\"center\" gap=\"sm\">\n <Small tone=\"muted\">{messages.dataImportSheet}</Small>\n <Select\n items={sheets.map((item) => item.name)}\n _value={sheetName}\n onUpdate:_value={changeSheet}\n aria-label={messages.dataImportSheet}\n size=\"sm\"\n />\n </Flex>\n )}\n <ColumnTable\n rows={fields}\n getKey={(field) => field.key}\n aria-label={messages.dataImportSteps[1]}\n columns={[\n {\n key: 'label',\n header: messages.dataImportField,\n cell: (field) => (\n <Flex align=\"center\" gap=\"xs\">\n {field.label}\n {field.required && <Badge tone=\"danger\">*</Badge>}\n </Flex>\n ),\n },\n {\n key: 'column',\n header: messages.dataImportColumn,\n width: 240,\n cell: (field) => (\n <Select\n items={headers}\n _value={mapping[field.key] ?? null}\n onUpdate:_value={(header: string | null) =>\n (mapping = { ...mapping, [field.key]: header })\n }\n placeholder={messages.dataImportSkip}\n clearable\n size=\"sm\"\n aria-label={`${field.label}:${messages.dataImportColumn}`}\n />\n ),\n },\n {\n key: 'sample',\n header: messages.dataImportSample,\n cell: (field) => <Small tone=\"muted\">{sampleOf(mapping[field.key] ?? null)}</Small>,\n },\n ]}\n />\n {missing.length > 0 && (\n <Alert tone=\"warning\">{messages.dataImportUnmapped(missing)}</Alert>\n )}\n </Flex>\n );\n }\n\n function renderPreview(): VNodeChild {\n const { total, valid, invalid } = counts();\n const shown = onlyErrors\n ? rows.value.filter((row) => Object.keys(row.errors).length > 0)\n : rows.value;\n return (\n <Flex direction=\"column\" gap=\"sm\">\n <Flex align=\"center\" justify=\"between\" gap=\"md\" wrap>\n <Small>{messages.dataImportSummary(total, valid, invalid)}</Small>\n {invalid > 0 && (\n <Switch\n _checked={onlyErrors}\n onUpdate:_checked={(value: boolean) => (onlyErrors = value)}\n >\n {messages.dataImportOnlyErrors}\n </Switch>\n )}\n </Flex>\n <DataTable\n {...mergePartProps<Partial<DataTableProps<ImportRow>>>(\n {\n 'aria-label': messages.dataImportPreview,\n propsRoot: { sx: dataImportStyles.previewTable },\n density: 'compact',\n },\n propsPreview,\n )}\n rows={shown}\n columns={previewColumns()}\n getKey={(row) => String(row.line)}\n rowTone={(row) => (Object.keys(row.errors).length ? 'danger' : undefined)}\n validateRow={(row) => row.errors}\n onCellChange={onCellChange}\n />\n {invalid > 0 && <Small tone=\"muted\">{messages.dataImportEditHint}</Small>}\n </Flex>\n );\n }\n\n return () => {\n const { valid } = counts();\n return (\n <div {...mergeAttrs<'div'>({ sx: dataImportStyles.root }, rest)}>\n <Steps\n {...mergePartProps<StepsProps>(\n { items: messages.dataImportSteps.map((title) => ({ title })) },\n propsSteps,\n { items: messages.dataImportSteps.map((title) => ({ title })) },\n )}\n _current={step}\n />\n {step === 0 && renderUpload()}\n {step === 1 && renderMapping()}\n {step === 2 && renderPreview()}\n {step === 3 && (\n <Alert tone=\"success\" title={messages.dataImportDone(imported)}>\n <Button size=\"sm\" variant=\"soft\" onClick={restart}>\n {messages.dataImportAgain}\n </Button>\n </Alert>\n )}\n {(step === 1 || step === 2) && (\n <Flex {...mergePartProps<FlexProps>({ justify: 'end', gap: 'sm' }, propsActions)}>\n <Button\n variant=\"ghost\"\n tone=\"neutral\"\n onClick={() => (step === 1 ? restart() : (step = 1))}\n >\n {messages.stepsFormPrevious}\n </Button>\n {step === 1 ? (\n <Button disabled={unmapped().length > 0} onClick={preview}>\n {messages.stepsFormNext}\n </Button>\n ) : (\n <Button\n {...mergePartProps<ButtonProps>({}, propsSubmit, {\n disabled: valid === 0,\n loading: importing,\n onClick: () => void submit(),\n })}\n >\n {messages.dataImportSubmit(valid)}\n </Button>\n )}\n </Flex>\n )}\n </div>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DataImport.js","names":["_isVNode","_vtp_readonlyProp","_vtp_restProps","_vue_ref","_vue_shallowRef","_createVNode","_vue_unref","_mergeProps"],"sources":["../../src/patterns/DataImport.tsx"],"sourcesContent":["/**\n * 数据导入:上传文件 → 对应列 → 逐行校验与预览(有错的格子就地改)→ 确认导入。批量导入零件、报价、人员这类数据用它。\n *\n * <DataImport\n * fields={[{ key: 'part', label: '零件号', required: true, aliases: ['料号'] }, { key: 'quantity', label: '数量', type: 'number' }]}\n * schema={z.object({ part: z.string().min(1), quantity: z.number().int().positive() })}\n * parse={readXlsx} // 读 Excel:@zerodep-dev/vue-tsx-pro-ui/xlsx;不写时读 CSV\n * accept=\".xlsx,.csv\"\n * onImport={(rows) => api.importParts(rows)}\n * />\n *\n * - 上传:选文件或拖进来,parse 读成表(SheetTable);几张工作表时先选一张。\n * - 对应列:按字段的 label 与 aliases 自动对上文件里的列,可以改;必填的字段没对上时不能往下走。\n * - 校验:空的格子(空字符串、只有空白)是 null,同 useForm 的约定(选填的字段在 schema 里写 .nullable())。\n * 有 schema(zod 对象)时逐行解析,错误标在对应的格子上;没有时按 required、type(数字列要是数字)检查,\n * 也可以写 validate。有错的格子可以直接改(表格就地编辑),改完重新校验;“只看有错的行”筛出要处理的。\n * - 导入:onImport 拿到通过校验的行(schema 解析后的值);返回 Promise 时按钮加载中,完成后显示结果。\n *\n * 部件:Root(div)、Steps、Drop(FileDrop)、Mapping(ColumnTable)、Preview(DataTable)、Actions(Flex)。\n */\nimport { CircleAlert, CircleCheck, Download } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { shallowRef, type VNodeChild } from 'vue';\nimport type { $ZodType } from 'zod/v4/core';\nimport { safeParse } from 'zod/v4/core';\nimport { Badge } from '../base/Badge';\nimport { Button, type ButtonProps } from '../base/Button';\nimport { Icon } from '../base/Icon';\nimport { Small } from '../base/Text';\nimport { useLang } from '../config/context';\nimport { useMessages } from '../config/lang/messages';\nimport { Alert } from '../feedback/Alert';\nimport { FileDrop, type FileDropProps } from '../input/FileDrop';\nimport { Select } from '../input/Select';\nimport { Switch } from '../input/Switch';\nimport { Flex, type FlexProps } from '../layout/Flex';\nimport { Steps, type StepsProps } from '../navigation/Steps';\nimport type { DataTableColumn } from '../table/columns';\nimport { ColumnTable } from '../table/ColumnTable';\nimport { DataTable, type DataTableProps } from '../table/DataTable';\nimport { readCsvFile, type SheetTable, type SheetValue } from '../table/sheet';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { patternsMessages } from './messages';\n\n/** 要导入的一个字段 */\nexport interface ImportField {\n /** 字段名:导入的行里的键 */\n key: string;\n /** 名称:对应列时显示,也用来自动对上文件里的同名列 */\n label: string;\n /** 必填:没对应上不能往下走;没有 schema 时空值算错 */\n required?: boolean;\n /** 文件里这一列可能的其他叫法(“料号”“Part No.”):自动对应时也认 */\n aliases?: readonly string[];\n /** 值的类型(没有 schema 时):number 把文字转成数字,转不了算错;默认 text */\n type?: 'text' | 'number';\n /** 预览表里这一列的宽度(px) */\n width?: number;\n}\n\n/** 预览里的一行 */\ninterface ImportRow {\n /** 第几行数据(从 1 起) */\n line: number;\n /** 对应后的原值(字段名 → 值),就地改的是它 */\n raw: Record<string, unknown>;\n /** 校验通过后的值 */\n value?: unknown;\n /** 字段名 → 原因 */\n errors: Record<string, string>;\n}\n\n/** 未声明的属性落到外层 div 上 */\nexport interface DataImportProps<Row> extends PartProps<'div'> {\n /** 要导入的字段,按显示的顺序 */\n fields: readonly ImportField[];\n /** 一行的 zod 对象 schema:逐行解析,错误标在格子上,onImport 拿到解析后的值 */\n schema?: $ZodType<Row>;\n /** 没有 schema 时的逐行校验:返回字段名 → 原因(在 required、type 的检查之后) */\n validate?: (row: Record<string, unknown>) => Record<string, string> | null | undefined;\n /** 读文件:返回各工作表;默认读 CSV(readCsvFile),读 Excel 传 @zerodep-dev/vue-tsx-pro-ui/xlsx 的 readXlsx */\n parse?: (file: File) => Promise<SheetTable[]>;\n /** 能选的文件类型,默认 .csv */\n accept?: string;\n /** 导入通过校验的行:返回 Promise 时按钮加载中,失败时留在预览(由调用方提示原因) */\n onImport: (rows: Row[]) => unknown;\n /** 下载模板:写了就在上传区旁边放“下载模板” */\n onTemplate?: () => void;\n /** 步骤条的部件属性 */\n propsSteps?: Partial<StepsProps>;\n /** 选文件区域的部件属性 */\n propsDrop?: Partial<FileDropProps>;\n /** 预览表(DataTable)的部件属性 */\n propsPreview?: Partial<DataTableProps<ImportRow>>;\n /** 按钮行的部件属性 */\n propsActions?: Partial<FlexProps>;\n /** 导入按钮的部件属性 */\n propsSubmit?: Partial<ButtonProps>;\n}\n\n/** DataImport 的样式:root、previewTable、validIcon、invalidIcon */\nexport const dataImportStyles = css.create({\n root: { display: 'flex', flexDirection: 'column', gap: '_lg', minWidth: 0 },\n previewTable: { maxHeight: '24rem' },\n validIcon: { color: '_successText' },\n invalidIcon: { color: '_dangerText' },\n});\n\n/** 文字规范化后比较:去掉空白、括号与大小写(“零件号 (必填)”也认作“零件号”) */\nconst normalize = (text: string) =>\n text\n .toLowerCase()\n .replace(/[((][^))]*[))]/g, '')\n .replace(/[\\s_*]/g, '');\n\n/** 按名称与别名对上文件里的列 */\nfunction autoMap(\n fields: readonly ImportField[],\n headers: readonly string[],\n): Record<string, string | null> {\n const taken = new Set<string>();\n return Object.fromEntries(\n fields.map((field) => {\n const names = [field.label, field.key, ...(field.aliases ?? [])].map(normalize);\n const header = headers.find((item) => !taken.has(item) && names.includes(normalize(item)));\n if (header) taken.add(header);\n return [field.key, header ?? null];\n }),\n );\n}\n\n/** 空值:null、空字符串 */\nconst isEmpty = (value: unknown) =>\n value == null || (typeof value === 'string' && value.trim() === '');\n\n/** 数据导入:上传 → 对应列 → 校验与预览 → 导入 */\nexport const DataImport = _component(\n <Row,>({\n fields,\n schema,\n validate,\n parse = readCsvFile,\n accept = '.csv',\n onImport,\n onTemplate,\n propsSteps,\n propsDrop,\n propsPreview,\n propsActions,\n propsSubmit,\n ...rest\n }: DataImportProps<Row>) => {\n const messages = useMessages('patterns', patternsMessages);\n const lang = useLang();\n let step = _state(0);\n let sheets = _state.raw<SheetTable[]>([]);\n let sheetName = _state<string | null>(null);\n let mapping = _state<Record<string, string | null>>({});\n let parseError = _state('');\n let onlyErrors = _state(false);\n let importing = _state(false);\n let imported = _state(0);\n // 预览的行:就地改时整个换掉\n const rows = shallowRef<ImportRow[]>([]);\n\n const sheet = () => sheets.find((item) => item.name === sheetName);\n const unmapped = () =>\n fields.filter((field) => field.required && !mapping[field.key]).map((field) => field.label);\n\n async function onFiles(files: File[]) {\n const file = files[0];\n if (!file) return;\n parseError = '';\n try {\n const read = (await parse(file)).filter((item) => item.rows.length > 0);\n if (read.length === 0) {\n parseError = messages.dataImportEmpty;\n return;\n }\n sheets = read;\n sheetName = read[0]!.name;\n mapping = autoMap(fields, read[0]!.headers);\n step = 1;\n } catch (error) {\n parseError = messages.dataImportParseFailed(\n error instanceof Error ? error.message : String(error),\n );\n }\n }\n\n function changeSheet(name: string | null) {\n sheetName = name;\n const current = sheet();\n if (current) mapping = autoMap(fields, current.headers);\n }\n\n /** 校验一行:schema 优先;否则 required、type 与 validate */\n function check(raw: Record<string, unknown>): Pick<ImportRow, 'value' | 'errors'> {\n if (schema) {\n const result = safeParse(schema, raw);\n if (result.success) return { value: result.data, errors: {} };\n const errors: Record<string, string> = {};\n for (const issue of result.error.issues) {\n const key = String(issue.path[0] ?? fields[0]?.key ?? '');\n errors[key] ??= issue.message;\n }\n return { errors };\n }\n const errors: Record<string, string> = {};\n const value: Record<string, unknown> = {};\n for (const field of fields) {\n const cell = raw[field.key];\n if (isEmpty(cell)) {\n if (field.required) errors[field.key] = lang.formRequired;\n value[field.key] = null;\n } else if (field.type === 'number') {\n const number = typeof cell === 'number' ? cell : Number(String(cell).replace(/,/g, ''));\n if (Number.isNaN(number)) errors[field.key] = messages.dataImportNotNumber;\n value[field.key] = number;\n } else value[field.key] = typeof cell === 'string' ? cell.trim() : cell;\n }\n Object.assign(errors, { ...validate?.(value), ...errors });\n return Object.keys(errors).length ? { errors } : { value, errors };\n }\n\n /** 对应完:按对应关系取出各行并校验,进入预览 */\n function preview() {\n const current = sheet();\n if (!current) return;\n rows.value = current.rows.map((row, index) => {\n const raw = Object.fromEntries(\n fields.map((field) => {\n const header = mapping[field.key];\n const value = header ? (row[header] ?? null) : null;\n return [field.key, isEmpty(value) ? null : value] as [string, SheetValue];\n }),\n );\n return { line: index + 1, raw, ...check(raw) };\n });\n onlyErrors = false;\n step = 2;\n }\n\n /** 就地改了一格:改原值、重新校验这一行 */\n function onCellChange(row: ImportRow, key: string, value: unknown) {\n rows.value = rows.value.map((item) => {\n if (item !== row) return item;\n const raw = { ...item.raw, [key]: isEmpty(value) ? null : value };\n return { ...item, raw, ...check(raw) };\n });\n }\n\n async function submit() {\n const valid = rows.value.filter((row) => Object.keys(row.errors).length === 0);\n importing = true;\n try {\n await onImport(valid.map((row) => row.value as Row));\n imported = valid.length;\n step = 3;\n } catch (error) {\n console.error('DataImport 的 onImport 失败,留在预览', error);\n } finally {\n importing = false;\n }\n }\n\n function restart() {\n sheets = [];\n sheetName = null;\n rows.value = [];\n step = 0;\n }\n\n const counts = () => {\n const invalid = rows.value.filter((row) => Object.keys(row.errors).length > 0).length;\n return { total: rows.value.length, invalid, valid: rows.value.length - invalid };\n };\n\n const previewColumns = (): DataTableColumn<ImportRow>[] => [\n { key: 'line', header: messages.dataImportRow, width: 72, value: (row) => row.line },\n {\n key: 'status',\n header: messages.dataImportStatus,\n width: 200,\n cell: (row) => {\n const first = Object.values(row.errors)[0];\n return first ? (\n <Flex align=\"center\" gap=\"xs\">\n <Icon icon={CircleAlert} sx={dataImportStyles.invalidIcon} />\n <Small tone=\"danger\">{first}</Small>\n </Flex>\n ) : (\n <Flex align=\"center\" gap=\"xs\">\n <Icon icon={CircleCheck} sx={dataImportStyles.validIcon} />\n <Small tone=\"muted\">{messages.dataImportValid}</Small>\n </Flex>\n );\n },\n },\n ...fields.map((field): DataTableColumn<ImportRow> => ({\n key: field.key,\n header: field.label,\n width: field.width,\n minWidth: 120,\n type: field.type === 'number' ? 'number' : 'text',\n value: (row) => row.raw[field.key] as SheetValue,\n editable: true,\n })),\n ];\n\n function renderUpload(): VNodeChild {\n return (\n <Flex direction=\"column\" gap=\"sm\">\n <FileDrop\n {...mergePartProps<FileDropProps>(\n { accept, description: messages.dataImportAccept(accept.split(',').join(', ')) },\n propsDrop,\n { onFiles: (files: File[]) => void onFiles(files) },\n )}\n />\n {parseError && <Alert tone=\"danger\">{parseError}</Alert>}\n {onTemplate && (\n <Flex justify=\"end\">\n <Button size=\"sm\" variant=\"ghost\" icon={Download} onClick={onTemplate}>\n {messages.dataImportTemplate}\n </Button>\n </Flex>\n )}\n </Flex>\n );\n }\n\n function renderMapping(): VNodeChild {\n const current = sheet();\n const headers = current?.headers ?? [];\n const missing = unmapped();\n const sampleOf = (header: string | null) =>\n header\n ? (current?.rows ?? [])\n .slice(0, 2)\n .map((row) => row[header])\n .filter((value) => !isEmpty(value))\n .map(String)\n .join('、')\n : '';\n return (\n <Flex direction=\"column\" gap=\"md\">\n {sheets.length > 1 && (\n <Flex align=\"center\" gap=\"sm\">\n <Small tone=\"muted\">{messages.dataImportSheet}</Small>\n <Select\n items={sheets.map((item) => item.name)}\n _value={sheetName}\n onUpdate:_value={changeSheet}\n aria-label={messages.dataImportSheet}\n size=\"sm\"\n />\n </Flex>\n )}\n <ColumnTable\n rows={fields}\n getKey={(field) => field.key}\n aria-label={messages.dataImportSteps[1]}\n columns={[\n {\n key: 'label',\n header: messages.dataImportField,\n cell: (field) => (\n <Flex align=\"center\" gap=\"xs\">\n {field.label}\n {field.required && <Badge tone=\"danger\">*</Badge>}\n </Flex>\n ),\n },\n {\n key: 'column',\n header: messages.dataImportColumn,\n width: 240,\n cell: (field) => (\n <Select\n items={headers}\n _value={mapping[field.key] ?? null}\n onUpdate:_value={(header: string | null) =>\n (mapping = { ...mapping, [field.key]: header })\n }\n placeholder={messages.dataImportSkip}\n clearable\n size=\"sm\"\n aria-label={`${field.label}:${messages.dataImportColumn}`}\n />\n ),\n },\n {\n key: 'sample',\n header: messages.dataImportSample,\n cell: (field) => <Small tone=\"muted\">{sampleOf(mapping[field.key] ?? null)}</Small>,\n },\n ]}\n />\n {missing.length > 0 && (\n <Alert tone=\"warning\">{messages.dataImportUnmapped(missing)}</Alert>\n )}\n </Flex>\n );\n }\n\n function renderPreview(): VNodeChild {\n const { total, valid, invalid } = counts();\n const shown = onlyErrors\n ? rows.value.filter((row) => Object.keys(row.errors).length > 0)\n : rows.value;\n return (\n <Flex direction=\"column\" gap=\"sm\">\n <Flex align=\"center\" justify=\"between\" gap=\"md\" wrap>\n <Small>{messages.dataImportSummary(total, valid, invalid)}</Small>\n {invalid > 0 && (\n <Switch\n _checked={onlyErrors}\n onUpdate:_checked={(value: boolean) => (onlyErrors = value)}\n >\n {messages.dataImportOnlyErrors}\n </Switch>\n )}\n </Flex>\n <DataTable\n {...mergePartProps<Partial<DataTableProps<ImportRow>>>(\n {\n 'aria-label': messages.dataImportPreview,\n propsRoot: { sx: dataImportStyles.previewTable },\n density: 'compact',\n },\n propsPreview,\n )}\n rows={shown}\n columns={previewColumns()}\n getKey={(row) => String(row.line)}\n rowTone={(row) => (Object.keys(row.errors).length ? 'danger' : undefined)}\n validateRow={(row) => row.errors}\n onCellChange={onCellChange}\n />\n {invalid > 0 && <Small tone=\"muted\">{messages.dataImportEditHint}</Small>}\n </Flex>\n );\n }\n\n return () => {\n const { valid } = counts();\n return (\n <div {...mergeAttrs<'div'>({ sx: dataImportStyles.root }, rest)}>\n <Steps\n {...mergePartProps<StepsProps>(\n { items: messages.dataImportSteps.map((title) => ({ title })) },\n propsSteps,\n { items: messages.dataImportSteps.map((title) => ({ title })) },\n )}\n _current={step}\n />\n {step === 0 && renderUpload()}\n {step === 1 && renderMapping()}\n {step === 2 && renderPreview()}\n {step === 3 && (\n <Alert tone=\"success\" title={messages.dataImportDone(imported)}>\n <Button size=\"sm\" variant=\"soft\" onClick={restart}>\n {messages.dataImportAgain}\n </Button>\n </Alert>\n )}\n {(step === 1 || step === 2) && (\n <Flex {...mergePartProps<FlexProps>({ justify: 'end', gap: 'sm' }, propsActions)}>\n <Button\n variant=\"ghost\"\n tone=\"neutral\"\n onClick={() => (step === 1 ? restart() : (step = 1))}\n >\n {messages.stepsFormPrevious}\n </Button>\n {step === 1 ? (\n <Button disabled={unmapped().length > 0} onClick={preview}>\n {messages.stepsFormNext}\n </Button>\n ) : (\n <Button\n {...mergePartProps<ButtonProps>({}, propsSubmit, {\n disabled: valid === 0,\n loading: importing,\n onClick: () => void submit(),\n })}\n >\n {messages.dataImportSubmit(valid)}\n </Button>\n )}\n </Flex>\n )}\n </div>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
@@ -3,8 +3,8 @@ import { VisuallyHidden } from "../base/VisuallyHidden.js";
|
|
|
3
3
|
import { useMessages } from "../config/lang/messages.js";
|
|
4
4
|
import { deadlineOf, useWorkCalendar } from "../utils/data/workdays.js";
|
|
5
5
|
import { patternsMessages } from "./messages.js";
|
|
6
|
-
import { createTextVNode, createVNode
|
|
7
|
-
import { _component, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
6
|
+
import { createTextVNode, createVNode } from "vue";
|
|
7
|
+
import { _component, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
8
8
|
//#region src/patterns/DeadlineBadge.tsx
|
|
9
9
|
/** 未声明的属性落到 Badge 上 */
|
|
10
10
|
var TONES = {
|
|
@@ -53,7 +53,7 @@ var DeadlineBadge = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
53
53
|
const current = deadline();
|
|
54
54
|
if (!current) return null;
|
|
55
55
|
const late = current.state === "done" && current.remaining < 0;
|
|
56
|
-
return createVNode(Badge,
|
|
56
|
+
return createVNode(Badge, mergeJsxProps(rest, {
|
|
57
57
|
"tone": late ? "danger" : TONES[current.state],
|
|
58
58
|
"title": messages.deadlineOn(current.due),
|
|
59
59
|
"data-state": current.state
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeadlineBadge.js","names":["_vtp_readonlyProp","_vtp_restProps","_createVNode","_mergeProps","_createTextVNode"],"sources":["../../src/patterns/DeadlineBadge.tsx"],"sourcesContent":["/**\n * 时限标记:按工作日算还剩几天、今天到期、超时几天,颜色随状态(超时 danger、今天到期与快到期 warning、\n * 还早 neutral、完成 success 或超时完成 danger)。用在收件箱、待办、各种有时限的列表里。\n *\n * <DeadlineBadge due=\"2026-10-23\" /> 截止日直接给\n * <DeadlineBadge start={verifiedOn} workdays={10} /> 开始日加几个工作日(验收完成后 10 个工作日内付款)\n * <DeadlineBadge due={item.dueOn} done={item.completedOn} /> 完成了按完成那天算\n *\n * 工作日按 createUi / Provider 的 workCalendar(法定节假日与调休),没给时只按周末。今天默认按浏览器,\n * 业务以服务器的日期为准时传 today。悬停与读屏有完整说明(截止日)。算法见 utils/data/workdays 的 deadlineOf。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { Badge, type BadgeProps } from '../base/Badge';\nimport { useMessages } from '../config/lang/messages';\nimport { patternsMessages } from './messages';\nimport { VisuallyHidden } from '../base/VisuallyHidden';\nimport {\n deadlineOf,\n useWorkCalendar,\n type Deadline,\n type DeadlineStates,\n} from '../utils/data/workdays';\nimport type { StatusTone } from '../utils/style/tone-styles';\n\n/** 未声明的属性落到 Badge 上 */\nexport interface DeadlineBadgeProps extends Omit<BadgeProps, 'tone' | 'children' | 'title'> {\n /** 截止日(ISO 日期);与 start + workdays 二选一 */\n due?: string | null;\n /** 开始日(ISO 日期):截止日是它加 workdays 个工作日 */\n start?: string;\n /** 开始日之后几个工作日截止 */\n workdays?: number;\n /** 完成的日期:完成了按完成那天算,显示按时或超时完成 */\n done?: string | null;\n /** 今天(ISO 日期),默认按浏览器的今天 */\n today?: string;\n /**\n * 剩下几个工作日以内算快到期(warning)\n * @default 2\n */\n soon?: number;\n}\n\nconst TONES: Record<keyof DeadlineStates, StatusTone> = {\n pending: 'neutral',\n soon: 'warning',\n due: 'warning',\n overdue: 'danger',\n done: 'success',\n};\n\n/** 时限标记 */\nexport const DeadlineBadge = _component(\n ({ due, start, workdays, done, today, soon, ...rest }: DeadlineBadgeProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const calendar = useWorkCalendar();\n const deadline = (): Deadline | null => {\n if (due) return deadlineOf({ due, done, today, soon }, calendar);\n if (start && workdays != null)\n return deadlineOf({ start, workdays, done, today, soon }, calendar);\n return null;\n };\n const text = ({ state, remaining }: Deadline) => {\n switch (state) {\n case 'overdue':\n return messages.deadlineOverdue(-remaining);\n case 'due':\n return messages.deadlineDue;\n case 'done':\n return remaining < 0 ? messages.deadlineDoneLate(-remaining) : messages.deadlineDone;\n default:\n return messages.deadlineRemaining(remaining);\n }\n };\n return () => {\n const current = deadline();\n if (!current) return null;\n const late = current.state === 'done' && current.remaining < 0;\n return (\n <Badge\n {...rest}\n tone={late ? 'danger' : TONES[current.state]}\n title={messages.deadlineOn(current.due)}\n data-state={current.state}\n >\n {text(current)}\n {/* title 读屏不一定念:截止日另写一份给读屏 */}\n <VisuallyHidden> {messages.deadlineOn(current.due)}</VisuallyHidden>\n </Badge>\n );\n };\n },\n);\n"],"mappings":";;;;;;;;;AA2CA,IAAM,QAAkD;CACtD,SAAS;CACT,MAAM;CACN,KAAK;CACL,SAAS;CACT,MAAM;AACR;;AAGA,IAAa,gBAAgB,0BAC3B,QAAA,SAA8E;CAAA,MAAA,MAAAA,aAAA,QAAA,KAAA;CAAA,MAAA,QAAAA,aAAA,QAAA,OAAA;CAAA,MAAA,WAAAA,aAAA,QAAA,UAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,QAAAA,aAAA,QAAA,OAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAA/B,OAAIC,UAAA,QAAA,MAAA,IAAA;CACjD,MAAM,WAAW,YAAY,YAAY,gBAAgB;CACzD,MAAM,WAAW,gBAAgB;CACjC,MAAM,iBAAkC;EACtC,IAAA,IAAA,OAAS,OAAO,WAAW;GAAE,KAAG,IAAA;GAAE,MAAI,KAAA;GAAE,OAAK,MAAA;GAAE,MAAI,KAAA;EAAC,GAAG,QAAQ;EAC/D,IAAI,MAAA,SAAS,SAAA,SAAY,MACvB,OAAO,WAAW;GAAE,OAAK,MAAA;GAAE,UAAQ,SAAA;GAAE,MAAI,KAAA;GAAE,OAAK,MAAA;GAAE,MAAI,KAAA;EAAC,GAAG,QAAQ;EACpE,OAAO;CACT;CACA,MAAM,QAAQ,EAAE,OAAO,gBAA0B;EAC/C,QAAQ,OAAR;GACE,KAAK,WACH,OAAO,SAAS,gBAAgB,CAAC,SAAS;GAC5C,KAAK,OACH,OAAO,SAAS;GAClB,KAAK,QACH,OAAO,YAAY,IAAI,SAAS,iBAAiB,CAAC,SAAS,IAAI,SAAS;GAC1E,SACE,OAAO,SAAS,kBAAkB,SAAS;EAC/C;CACF;CACA,aAAa;EACX,MAAM,UAAU,SAAS;EACzB,IAAI,CAAC,SAAS,OAAO;EACrB,MAAM,OAAO,QAAQ,UAAU,UAAU,QAAQ,YAAY;EAC7D,OAAAC,YAAA,OAAAC,
|
|
1
|
+
{"version":3,"file":"DeadlineBadge.js","names":["_vtp_readonlyProp","_vtp_restProps","_createVNode","_mergeProps","_createTextVNode"],"sources":["../../src/patterns/DeadlineBadge.tsx"],"sourcesContent":["/**\n * 时限标记:按工作日算还剩几天、今天到期、超时几天,颜色随状态(超时 danger、今天到期与快到期 warning、\n * 还早 neutral、完成 success 或超时完成 danger)。用在收件箱、待办、各种有时限的列表里。\n *\n * <DeadlineBadge due=\"2026-10-23\" /> 截止日直接给\n * <DeadlineBadge start={verifiedOn} workdays={10} /> 开始日加几个工作日(验收完成后 10 个工作日内付款)\n * <DeadlineBadge due={item.dueOn} done={item.completedOn} /> 完成了按完成那天算\n *\n * 工作日按 createUi / Provider 的 workCalendar(法定节假日与调休),没给时只按周末。今天默认按浏览器,\n * 业务以服务器的日期为准时传 today。悬停与读屏有完整说明(截止日)。算法见 utils/data/workdays 的 deadlineOf。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { Badge, type BadgeProps } from '../base/Badge';\nimport { useMessages } from '../config/lang/messages';\nimport { patternsMessages } from './messages';\nimport { VisuallyHidden } from '../base/VisuallyHidden';\nimport {\n deadlineOf,\n useWorkCalendar,\n type Deadline,\n type DeadlineStates,\n} from '../utils/data/workdays';\nimport type { StatusTone } from '../utils/style/tone-styles';\n\n/** 未声明的属性落到 Badge 上 */\nexport interface DeadlineBadgeProps extends Omit<BadgeProps, 'tone' | 'children' | 'title'> {\n /** 截止日(ISO 日期);与 start + workdays 二选一 */\n due?: string | null;\n /** 开始日(ISO 日期):截止日是它加 workdays 个工作日 */\n start?: string;\n /** 开始日之后几个工作日截止 */\n workdays?: number;\n /** 完成的日期:完成了按完成那天算,显示按时或超时完成 */\n done?: string | null;\n /** 今天(ISO 日期),默认按浏览器的今天 */\n today?: string;\n /**\n * 剩下几个工作日以内算快到期(warning)\n * @default 2\n */\n soon?: number;\n}\n\nconst TONES: Record<keyof DeadlineStates, StatusTone> = {\n pending: 'neutral',\n soon: 'warning',\n due: 'warning',\n overdue: 'danger',\n done: 'success',\n};\n\n/** 时限标记 */\nexport const DeadlineBadge = _component(\n ({ due, start, workdays, done, today, soon, ...rest }: DeadlineBadgeProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const calendar = useWorkCalendar();\n const deadline = (): Deadline | null => {\n if (due) return deadlineOf({ due, done, today, soon }, calendar);\n if (start && workdays != null)\n return deadlineOf({ start, workdays, done, today, soon }, calendar);\n return null;\n };\n const text = ({ state, remaining }: Deadline) => {\n switch (state) {\n case 'overdue':\n return messages.deadlineOverdue(-remaining);\n case 'due':\n return messages.deadlineDue;\n case 'done':\n return remaining < 0 ? messages.deadlineDoneLate(-remaining) : messages.deadlineDone;\n default:\n return messages.deadlineRemaining(remaining);\n }\n };\n return () => {\n const current = deadline();\n if (!current) return null;\n const late = current.state === 'done' && current.remaining < 0;\n return (\n <Badge\n {...rest}\n tone={late ? 'danger' : TONES[current.state]}\n title={messages.deadlineOn(current.due)}\n data-state={current.state}\n >\n {text(current)}\n {/* title 读屏不一定念:截止日另写一份给读屏 */}\n <VisuallyHidden> {messages.deadlineOn(current.due)}</VisuallyHidden>\n </Badge>\n );\n };\n },\n);\n"],"mappings":";;;;;;;;;AA2CA,IAAM,QAAkD;CACtD,SAAS;CACT,MAAM;CACN,KAAK;CACL,SAAS;CACT,MAAM;AACR;;AAGA,IAAa,gBAAgB,0BAC3B,QAAA,SAA8E;CAAA,MAAA,MAAAA,aAAA,QAAA,KAAA;CAAA,MAAA,QAAAA,aAAA,QAAA,OAAA;CAAA,MAAA,WAAAA,aAAA,QAAA,UAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,QAAAA,aAAA,QAAA,OAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAA/B,OAAIC,UAAA,QAAA,MAAA,IAAA;CACjD,MAAM,WAAW,YAAY,YAAY,gBAAgB;CACzD,MAAM,WAAW,gBAAgB;CACjC,MAAM,iBAAkC;EACtC,IAAA,IAAA,OAAS,OAAO,WAAW;GAAE,KAAG,IAAA;GAAE,MAAI,KAAA;GAAE,OAAK,MAAA;GAAE,MAAI,KAAA;EAAC,GAAG,QAAQ;EAC/D,IAAI,MAAA,SAAS,SAAA,SAAY,MACvB,OAAO,WAAW;GAAE,OAAK,MAAA;GAAE,UAAQ,SAAA;GAAE,MAAI,KAAA;GAAE,OAAK,MAAA;GAAE,MAAI,KAAA;EAAC,GAAG,QAAQ;EACpE,OAAO;CACT;CACA,MAAM,QAAQ,EAAE,OAAO,gBAA0B;EAC/C,QAAQ,OAAR;GACE,KAAK,WACH,OAAO,SAAS,gBAAgB,CAAC,SAAS;GAC5C,KAAK,OACH,OAAO,SAAS;GAClB,KAAK,QACH,OAAO,YAAY,IAAI,SAAS,iBAAiB,CAAC,SAAS,IAAI,SAAS;GAC1E,SACE,OAAO,SAAS,kBAAkB,SAAS;EAC/C;CACF;CACA,aAAa;EACX,MAAM,UAAU,SAAS;EACzB,IAAI,CAAC,SAAS,OAAO;EACrB,MAAM,OAAO,QAAQ,UAAU,UAAU,QAAQ,YAAY;EAC7D,OAAAC,YAAA,OAAAC,cAEQ,MAAI;GAAA,QACF,OAAO,WAAW,MAAM,QAAQ;GAAM,SACrC,SAAS,WAAW,QAAQ,GAAG;GAAC,cAC3B,QAAQ;EAAK,CAAA,GAAA,EAAA,eAAA,CAExB,KAAK,OAAO,GAACD,YAAA,gBAAA,MAAA,EAAA,eAAA,CAAAE,gBAAA,GAAA,GAEI,SAAS,WAAW,QAAQ,GAAG,CAAC,EAAA,CAAA,CAAA,EAAA,CAAA;CAGxD;AACF,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
|
@@ -23,8 +23,16 @@ export interface FilterBarProps extends PartProps<'form', FilterBarManaged>, Pic
|
|
|
23
23
|
moreActive?: number;
|
|
24
24
|
/** 每个条件的最小宽度(CSS 长度),默认 `'16em'` */
|
|
25
25
|
minFieldWidth?: string;
|
|
26
|
+
/** 整行起始边的内容:在条件前面(QueryTable 的标题);对齐第一行 */
|
|
27
|
+
start?: VNodeChild;
|
|
28
|
+
/** 整行结束边的内容:在查询、重置后面(QueryTable 的工具栏按钮);对齐第一行,展开更多条件时也不往下掉 */
|
|
29
|
+
end?: VNodeChild;
|
|
26
30
|
/** 结束边额外的按钮:放在“重置”前面(如“导出筛选结果”) */
|
|
27
31
|
actions?: VNodeChild;
|
|
32
|
+
/** start 外层的部件属性 */
|
|
33
|
+
propsStart?: PartProps<'div'>;
|
|
34
|
+
/** end 外层的部件属性 */
|
|
35
|
+
propsEnd?: PartProps<'div'>;
|
|
28
36
|
/** 条件栅格的部件属性 */
|
|
29
37
|
propsFields?: PartProps<'div'>;
|
|
30
38
|
/** 按钮区的部件属性 */
|
|
@@ -48,13 +56,15 @@ export interface FilterBarProps extends PartProps<'form', FilterBarManaged>, Pic
|
|
|
48
56
|
/** 常用的条件:Field */
|
|
49
57
|
children?: VNodeChild;
|
|
50
58
|
}
|
|
51
|
-
/** FilterBar 的样式:root、fields、more、actions、toggleIconOpen */
|
|
59
|
+
/** FilterBar 的样式:root、start、end、fields、more、actions、toggleIconOpen */
|
|
52
60
|
export declare const filterBarStyles: Readonly<{
|
|
53
61
|
actions: css.CompiledStyles;
|
|
54
62
|
advanced: css.CompiledStyles;
|
|
63
|
+
end: css.CompiledStyles;
|
|
55
64
|
fields: css.CompiledStyles;
|
|
56
65
|
more: css.CompiledStyles;
|
|
57
66
|
root: css.CompiledStyles;
|
|
67
|
+
start: css.CompiledStyles;
|
|
58
68
|
summary: css.CompiledStyles;
|
|
59
69
|
toggleIconOpen: css.CompiledStyles;
|
|
60
70
|
}>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t } from "../config/theme/tokens.js";
|
|
1
2
|
import { useLang } from "../config/context.js";
|
|
2
3
|
import { mergeAttrs, mergePartProps } from "../utils/props.js";
|
|
3
4
|
import { Badge } from "../base/Badge.js";
|
|
@@ -9,8 +10,8 @@ import { describeCondition, isConditionGroup } from "../input/condition.js";
|
|
|
9
10
|
import { ConditionBuilder } from "../input/ConditionBuilder.js";
|
|
10
11
|
import { useMessages } from "../config/lang/messages.js";
|
|
11
12
|
import { patternsMessages } from "./messages.js";
|
|
12
|
-
import { createVNode,
|
|
13
|
-
import { _component, bindable, childrenProp, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
13
|
+
import { createVNode, ref, useId } from "vue";
|
|
14
|
+
import { _component, bindable, childrenProp, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
14
15
|
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
15
16
|
import { ChevronDown, ListFilter } from "@lucide/icons";
|
|
16
17
|
//#region src/patterns/FilterBar.tsx
|
|
@@ -33,12 +34,15 @@ import { ChevronDown, ListFilter } from "@lucide/icons";
|
|
|
33
34
|
*
|
|
34
35
|
* <FilterBar onSearch={search} onReset={reset} conditionFields={fields} _condition={condition}>…</FilterBar>
|
|
35
36
|
*
|
|
36
|
-
*
|
|
37
|
+
* start、end 是整行两头的内容(标题在条件前面,工具栏的按钮在查询、重置后面):QueryTable 用它们把标题与工具栏并进查询栏这一行,
|
|
38
|
+
* 压成一行;点“展开”后更多的条件排在这一行下面,start、end 仍在第一行。
|
|
39
|
+
*
|
|
40
|
+
* 部件:Root(form)、Start、Fields(条件的栅格)、Field 们、More(收起的条件,display: contents)、Actions(div)、End、
|
|
37
41
|
* Search、Reset(Button)、Toggle(展开收起的 Button)、AdvancedToggle(高级筛选的 Button)、Advanced(条件构造器那一行)。
|
|
38
42
|
*/
|
|
39
43
|
/** 角色与提交由组件管理 */
|
|
40
44
|
/** 未声明的属性落到 form 上 */
|
|
41
|
-
/** FilterBar 的样式:root、fields、more、actions、toggleIconOpen */
|
|
45
|
+
/** FilterBar 的样式:root、start、end、fields、more、actions、toggleIconOpen */
|
|
42
46
|
var filterBarStyles = css.create({
|
|
43
47
|
root: {
|
|
44
48
|
display: "flex",
|
|
@@ -47,6 +51,21 @@ var filterBarStyles = css.create({
|
|
|
47
51
|
columnGap: "_xl",
|
|
48
52
|
rowGap: "_md"
|
|
49
53
|
},
|
|
54
|
+
start: {
|
|
55
|
+
display: "flex",
|
|
56
|
+
alignItems: "center",
|
|
57
|
+
alignSelf: "flex-start",
|
|
58
|
+
minHeight: t.sizes.control,
|
|
59
|
+
flexShrink: 0
|
|
60
|
+
},
|
|
61
|
+
end: {
|
|
62
|
+
display: "flex",
|
|
63
|
+
alignItems: "center",
|
|
64
|
+
alignSelf: "flex-start",
|
|
65
|
+
minHeight: t.sizes.control,
|
|
66
|
+
flexShrink: 0,
|
|
67
|
+
gap: "_sm"
|
|
68
|
+
},
|
|
50
69
|
fields: {
|
|
51
70
|
display: "grid",
|
|
52
71
|
columnGap: "_xl",
|
|
@@ -96,9 +115,13 @@ var FilterBar = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
96
115
|
const expanded = bindable(_props, "_expanded", () => false, _ctx.emit);
|
|
97
116
|
const moreActive = readonlyProp(_props, "moreActive", 0);
|
|
98
117
|
const minFieldWidth = readonlyProp(_props, "minFieldWidth", "16em");
|
|
118
|
+
const start = readonlyProp(_props, "start");
|
|
119
|
+
const end = readonlyProp(_props, "end");
|
|
99
120
|
const actions = readonlyProp(_props, "actions");
|
|
100
121
|
const labelPlacement = readonlyProp(_props, "labelPlacement", "start");
|
|
101
122
|
const labelWidth = readonlyProp(_props, "labelWidth", "auto");
|
|
123
|
+
const propsStart = readonlyProp(_props, "propsStart");
|
|
124
|
+
const propsEnd = readonlyProp(_props, "propsEnd");
|
|
102
125
|
const propsFields = readonlyProp(_props, "propsFields");
|
|
103
126
|
const propsActions = readonlyProp(_props, "propsActions");
|
|
104
127
|
const propsSearch = readonlyProp(_props, "propsSearch");
|
|
@@ -126,13 +149,14 @@ var FilterBar = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
126
149
|
event.preventDefault();
|
|
127
150
|
onSearch.value?.();
|
|
128
151
|
}
|
|
129
|
-
return () => createVNode("form",
|
|
152
|
+
return () => createVNode("form", mergeJsxProps(mergeAttrs({ sx: filterBarStyles.root }, rest, {
|
|
130
153
|
role: "search",
|
|
131
154
|
onSubmit
|
|
132
155
|
}), { "data-expanded": more.value != null ? expanded.value : void 0 }), [
|
|
156
|
+
start.value != null && createVNode("div", mergeAttrs({ sx: filterBarStyles.start }, propsStart.value), [start.value]),
|
|
133
157
|
createVNode("div", mergeAttrs({ sx: [filterBarStyles.fields, css.bind(filterBarColumnStyles.columns, () => [minFieldWidth.value], true)] }, propsFields.value), [children.value, more.value != null && expanded.value && createVNode("div", mergeAttrs({ sx: filterBarStyles.more }, propsMore.value, { id: moreId }), [more.value])]),
|
|
134
158
|
createVNode("div", mergeAttrs({ sx: filterBarStyles.actions }, propsActions.value), [
|
|
135
|
-
more.value != null && createVNode(Button,
|
|
159
|
+
more.value != null && createVNode(Button, mergeJsxProps(mergePartProps({
|
|
136
160
|
variant: "ghost",
|
|
137
161
|
tone: "neutral",
|
|
138
162
|
iconEnd: ChevronDown,
|
|
@@ -153,7 +177,7 @@ var FilterBar = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
153
177
|
})],
|
|
154
178
|
$stable: true
|
|
155
179
|
}),
|
|
156
|
-
conditionFields.value && createVNode(Button,
|
|
180
|
+
conditionFields.value && createVNode(Button, mergeJsxProps(mergePartProps({
|
|
157
181
|
variant: "ghost",
|
|
158
182
|
tone: "neutral",
|
|
159
183
|
icon: ListFilter
|
|
@@ -174,7 +198,7 @@ var FilterBar = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
174
198
|
$stable: true
|
|
175
199
|
}),
|
|
176
200
|
actions.value,
|
|
177
|
-
createVNode(Button,
|
|
201
|
+
createVNode(Button, mergeJsxProps(mergePartProps({
|
|
178
202
|
variant: "outline",
|
|
179
203
|
tone: "neutral"
|
|
180
204
|
}, propsReset.value, {
|
|
@@ -187,12 +211,13 @@ var FilterBar = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
187
211
|
default: () => [messages.filterReset],
|
|
188
212
|
$stable: true
|
|
189
213
|
}),
|
|
190
|
-
createVNode(Button,
|
|
214
|
+
createVNode(Button, mergeJsxProps(mergePartProps({ loading: loading.value }, propsSearch.value, { type: "submit" }), { "key": "__site5" }), {
|
|
191
215
|
default: () => [messages.filterSearch],
|
|
192
216
|
$stable: true
|
|
193
217
|
})
|
|
194
218
|
]),
|
|
195
|
-
|
|
219
|
+
end.value != null && createVNode("div", mergeAttrs({ sx: filterBarStyles.end }, propsEnd.value), [end.value]),
|
|
220
|
+
conditionFields.value && advancedOpen.value && createVNode(ConditionBuilder, mergeJsxProps(mergePartProps({
|
|
196
221
|
fields: conditionFields.value,
|
|
197
222
|
sx: filterBarStyles.advanced,
|
|
198
223
|
"aria-label": messages.filterAdvanced
|
|
@@ -232,9 +257,13 @@ var FilterBar = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
232
257
|
"onUpdate:_expanded",
|
|
233
258
|
"moreActive",
|
|
234
259
|
"minFieldWidth",
|
|
260
|
+
"start",
|
|
261
|
+
"end",
|
|
235
262
|
"actions",
|
|
236
263
|
"labelPlacement",
|
|
237
264
|
"labelWidth",
|
|
265
|
+
"propsStart",
|
|
266
|
+
"propsEnd",
|
|
238
267
|
"propsFields",
|
|
239
268
|
"propsActions",
|
|
240
269
|
"propsSearch",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterBar.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_childrenProp","_vtp_restProps","_vue_ref","_createVNode","_mergeProps"],"sources":["../../src/patterns/FilterBar.tsx"],"sourcesContent":["/**\n * 查询栏(form role=\"search\"):列表页上方的一排筛选条件,最后是“查询”“重置”;条件多时其余的收起来,点“展开”再显示。\n *\n * <FilterBar onSearch={search} onReset={reset} more={<>…不常用的条件…</>} moreActive={2}>\n * <Field label=\"项目名称\"><TextField _value={query.name} /></Field>\n * <Field label=\"任务类别\"><Select … /></Field>\n * </FilterBar>\n *\n * 条件排成栅格:每格不窄于 minFieldWidth,栅格多宽就放几格;按钮区在栅格的结束边,宽度随按钮、不折行,\n * 和条件的最后一行对齐(按钮再多也不会在一格里挤成两行);容器太窄放不下时,按钮区整体换到下一行、靠结束边。\n * 默认标签在起始边、宽度随文字(labelWidth 为 auto:控件紧跟着标签,不留空白;要几行对齐时写固定宽度)。在输入框里按回车就是查询(原生表单提交)。\n * more 收起时不渲染;moreActive 是收起的条件里已经填了几项,显示在“展开”旁边,免得忘了还有条件生效。\n *\n * 高级筛选:给了 conditionFields 时按钮区多一个“高级筛选”,展开后在查询栏下面用 ConditionBuilder 编辑条件树(_condition),\n * 能表达固定条件表达不了的组合(任一满足、介于、不包含);收起时有条件就在下面写成一句话,带“清空”。\n * “重置”同时把条件清成 null。条件是纯 JSON:交给后端(query 的条件树转 SQL)或前端 matchesCondition 筛。\n *\n * <FilterBar onSearch={search} onReset={reset} conditionFields={fields} _condition={condition}>…</FilterBar>\n *\n * 部件:Root(form)、Fields(条件的栅格)、Field 们、More(收起的条件,display: contents)、Actions(div)、\n * Search、Reset(Button)、Toggle(展开收起的 Button)、AdvancedToggle(高级筛选的 Button)、Advanced(条件构造器那一行)。\n */\nimport { ChevronDown, ListFilter } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { useId, type VNodeChild } from 'vue';\nimport { Badge } from '../base/Badge';\nimport { Small } from '../base/Text';\nimport { useLang } from '../config/context';\nimport { useMessages } from '../config/lang/messages';\nimport {\n describeCondition,\n isConditionGroup,\n type Condition,\n type ConditionField,\n} from '../input/condition';\nimport { ConditionBuilder, type ConditionBuilderProps } from '../input/ConditionBuilder';\nimport { patternsMessages } from './messages';\nimport { Button, type ButtonProps } from '../base/Button';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { disclosureStyles } from '../utils/style/transitions';\nimport { provideFieldLayout, type FieldLayout } from '../form/field-layout';\n\n/** 角色与提交由组件管理 */\nexport type FilterBarManaged = 'role' | 'onSubmit' | 'onReset';\n\n/** 未声明的属性落到 form 上 */\nexport interface FilterBarProps\n extends PartProps<'form', FilterBarManaged>, Pick<FieldLayout, 'labelPlacement' | 'labelWidth'> {\n /** 点“查询”或在输入框里按回车 */\n onSearch?: () => void;\n /** 点“重置”:清空条件由使用方做(通常 Object.assign 回初始值,再查询一次) */\n onReset?: () => void;\n /** 查询中:“查询”按钮显示加载中 */\n loading?: boolean;\n /** 不常用的条件:默认收起,点“展开”才显示 */\n more?: VNodeChild;\n /** 是否展开了 more:父级写 _expanded={x} */\n _expanded?: boolean;\n /** 收起的条件里已经填了几项:大于 0 时显示在“展开”旁边 */\n moreActive?: number;\n /** 每个条件的最小宽度(CSS 长度),默认 `'16em'` */\n minFieldWidth?: string;\n /** 结束边额外的按钮:放在“重置”前面(如“导出筛选结果”) */\n actions?: VNodeChild;\n /** 条件栅格的部件属性 */\n propsFields?: PartProps<'div'>;\n /** 按钮区的部件属性 */\n propsActions?: PartProps<'div'>;\n /** 查询按钮的部件属性 */\n propsSearch?: Partial<ButtonProps>;\n /** 重置按钮的部件属性 */\n propsReset?: Partial<ButtonProps>;\n /** 展开收起按钮的部件属性 */\n propsToggle?: Partial<ButtonProps>;\n /** 收起的条件的外层(display: contents)的部件属性 */\n propsMore?: PartProps<'div'>;\n /** 高级筛选的字段:给了时按钮区多一个“高级筛选” */\n conditionFields?: readonly ConditionField[];\n /** 高级筛选的条件:父级写 _condition={condition};没有时是 null,“重置”时清成 null */\n _condition?: Condition | null;\n /** 高级筛选按钮的部件属性 */\n propsAdvancedToggle?: Partial<ButtonProps>;\n /** 高级筛选那一行(条件构造器)的部件属性 */\n propsAdvanced?: Partial<ConditionBuilderProps>;\n /** 常用的条件:Field */\n children?: VNodeChild;\n}\n\n/** FilterBar 的样式:root、fields、more、actions、toggleIconOpen */\nexport const filterBarStyles = css.create({\n // 条件栅格与按钮区并排;放不下时按钮区换行\n root: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'flex-end',\n columnGap: '_xl',\n rowGap: '_md',\n },\n fields: {\n display: 'grid',\n columnGap: '_xl',\n rowGap: '_md',\n alignItems: 'start',\n flexGrow: 1,\n minWidth: 0,\n },\n // 收起的条件直接是栅格的格子\n more: { display: 'contents' },\n // 宽度随按钮、不折行;换到下一行时也靠结束边\n actions: {\n display: 'flex',\n alignItems: 'center',\n gap: '_sm',\n flexShrink: 0,\n marginInlineStart: 'auto',\n },\n // 展开后箭头朝上\n toggleIconOpen: { rotate: '180deg' },\n // 高级筛选与它的摘要各占一整行,排在条件与按钮下面\n advanced: {\n flexBasis: '100%',\n padding: '_md',\n backgroundColor: '_surfaceMuted',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_borderMuted',\n borderRadius: '_md',\n },\n summary: { flexBasis: '100%', display: 'flex', alignItems: 'baseline', gap: '_sm', minWidth: 0 },\n});\n\n/** FilterBar 的样式:columns(每格的最小宽度;栅格本身至少放得下一格) */\nexport const filterBarColumnStyles = css.create({\n columns: (minWidth: string) => ({\n flexBasis: minWidth,\n gridTemplateColumns: `repeat(auto-fill, minmax(min(${minWidth}, 100%), 1fr))`,\n }),\n});\n\n/** 查询栏:筛选条件的栅格,查询、重置,不常用的条件可以展开收起 */\nexport const FilterBar = _component(\n ({\n onSearch,\n onReset,\n loading = false,\n more,\n _expanded: expanded = _bindable(false),\n moreActive = 0,\n minFieldWidth = '16em',\n actions,\n labelPlacement = 'start',\n labelWidth = 'auto',\n propsFields,\n propsActions,\n propsSearch,\n propsReset,\n propsToggle,\n propsMore,\n conditionFields,\n _condition: condition = _bindable<Condition | null>(null),\n propsAdvancedToggle,\n propsAdvanced,\n children,\n ...rest\n }: FilterBarProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const lang = useLang();\n const moreId = useId();\n const advancedId = useId();\n let advancedOpen = _state(false);\n\n /** 条件里有几条比较(按钮旁的计数) */\n const countRules = (item: Condition): number =>\n !isConditionGroup(item)\n ? 1\n : 'not' in item\n ? countRules(item.not)\n : ('all' in item ? item.all : item.any).reduce(\n (sum, child) => sum + countRules(child),\n 0,\n );\n provideFieldLayout(() => ({ labelPlacement, labelWidth }));\n\n function onSubmit(event: Event) {\n event.preventDefault();\n onSearch?.();\n }\n\n return () => (\n <form\n {...mergeAttrs<'form'>({ sx: filterBarStyles.root }, rest, { role: 'search', onSubmit })}\n data-expanded={more != null ? expanded : undefined}\n >\n <div\n {...mergeAttrs<'div'>(\n { sx: [filterBarStyles.fields, filterBarColumnStyles.columns(minFieldWidth)] },\n propsFields,\n )}\n >\n {children}\n {more != null && expanded && (\n <div {...mergeAttrs<'div'>({ sx: filterBarStyles.more }, propsMore, { id: moreId })}>\n {more}\n </div>\n )}\n </div>\n <div {...mergeAttrs<'div'>({ sx: filterBarStyles.actions }, propsActions)}>\n {more != null && (\n <Button\n {...mergePartProps<ButtonProps>(\n {\n variant: 'ghost',\n tone: 'neutral',\n iconEnd: ChevronDown,\n propsIconEnd: {\n sx: [disclosureStyles.icon, expanded && filterBarStyles.toggleIconOpen],\n },\n },\n propsToggle,\n {\n type: 'button',\n 'aria-expanded': expanded,\n 'aria-controls': expanded ? moreId : undefined,\n onClick: () => (expanded = !expanded),\n },\n )}\n >\n {expanded ? messages.filterCollapse : messages.filterExpand}\n {!expanded && moreActive > 0 && (\n <Badge tone=\"accent\" radius=\"full\">\n {messages.filterActive(moreActive)}\n </Badge>\n )}\n </Button>\n )}\n {conditionFields && (\n <Button\n {...mergePartProps<ButtonProps>(\n { variant: 'ghost', tone: 'neutral', icon: ListFilter },\n propsAdvancedToggle,\n {\n type: 'button',\n 'aria-expanded': advancedOpen,\n 'aria-controls': advancedOpen ? advancedId : undefined,\n onClick: () => (advancedOpen = !advancedOpen),\n },\n )}\n >\n {messages.filterAdvanced}\n {!advancedOpen && condition && (\n <Badge tone=\"accent\" radius=\"full\">\n {messages.filterActive(countRules(condition))}\n </Badge>\n )}\n </Button>\n )}\n {actions}\n <Button\n {...mergePartProps<ButtonProps>({ variant: 'outline', tone: 'neutral' }, propsReset, {\n type: 'button',\n onClick: () => {\n if (conditionFields) condition = null;\n onReset?.();\n },\n })}\n >\n {messages.filterReset}\n </Button>\n <Button {...mergePartProps<ButtonProps>({ loading }, propsSearch, { type: 'submit' })}>\n {messages.filterSearch}\n </Button>\n </div>\n {conditionFields && advancedOpen && (\n <ConditionBuilder\n {...mergePartProps<ConditionBuilderProps>(\n {\n fields: conditionFields,\n sx: filterBarStyles.advanced,\n 'aria-label': messages.filterAdvanced,\n },\n propsAdvanced,\n { id: advancedId },\n )}\n _value={condition}\n />\n )}\n {conditionFields && !advancedOpen && condition && (\n <div {...css.attrs(filterBarStyles.summary)}>\n <Small tone=\"muted\">\n {messages.filterAdvanced}:{describeCondition(condition, conditionFields, lang)}\n </Small>\n <Button variant=\"link\" size=\"sm\" type=\"button\" onClick={() => (condition = null)}>\n {lang.clear}\n </Button>\n </div>\n )}\n </form>\n );\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FilterBar.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_childrenProp","_vtp_restProps","_vue_ref","_createVNode","_mergeProps"],"sources":["../../src/patterns/FilterBar.tsx"],"sourcesContent":["/**\n * 查询栏(form role=\"search\"):列表页上方的一排筛选条件,最后是“查询”“重置”;条件多时其余的收起来,点“展开”再显示。\n *\n * <FilterBar onSearch={search} onReset={reset} more={<>…不常用的条件…</>} moreActive={2}>\n * <Field label=\"项目名称\"><TextField _value={query.name} /></Field>\n * <Field label=\"任务类别\"><Select … /></Field>\n * </FilterBar>\n *\n * 条件排成栅格:每格不窄于 minFieldWidth,栅格多宽就放几格;按钮区在栅格的结束边,宽度随按钮、不折行,\n * 和条件的最后一行对齐(按钮再多也不会在一格里挤成两行);容器太窄放不下时,按钮区整体换到下一行、靠结束边。\n * 默认标签在起始边、宽度随文字(labelWidth 为 auto:控件紧跟着标签,不留空白;要几行对齐时写固定宽度)。在输入框里按回车就是查询(原生表单提交)。\n * more 收起时不渲染;moreActive 是收起的条件里已经填了几项,显示在“展开”旁边,免得忘了还有条件生效。\n *\n * 高级筛选:给了 conditionFields 时按钮区多一个“高级筛选”,展开后在查询栏下面用 ConditionBuilder 编辑条件树(_condition),\n * 能表达固定条件表达不了的组合(任一满足、介于、不包含);收起时有条件就在下面写成一句话,带“清空”。\n * “重置”同时把条件清成 null。条件是纯 JSON:交给后端(query 的条件树转 SQL)或前端 matchesCondition 筛。\n *\n * <FilterBar onSearch={search} onReset={reset} conditionFields={fields} _condition={condition}>…</FilterBar>\n *\n * start、end 是整行两头的内容(标题在条件前面,工具栏的按钮在查询、重置后面):QueryTable 用它们把标题与工具栏并进查询栏这一行,\n * 压成一行;点“展开”后更多的条件排在这一行下面,start、end 仍在第一行。\n *\n * 部件:Root(form)、Start、Fields(条件的栅格)、Field 们、More(收起的条件,display: contents)、Actions(div)、End、\n * Search、Reset(Button)、Toggle(展开收起的 Button)、AdvancedToggle(高级筛选的 Button)、Advanced(条件构造器那一行)。\n */\nimport { ChevronDown, ListFilter } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { useId, type VNodeChild } from 'vue';\nimport { Badge } from '../base/Badge';\nimport { Small } from '../base/Text';\nimport { useLang } from '../config/context';\nimport { useMessages } from '../config/lang/messages';\nimport {\n describeCondition,\n isConditionGroup,\n type Condition,\n type ConditionField,\n} from '../input/condition';\nimport { ConditionBuilder, type ConditionBuilderProps } from '../input/ConditionBuilder';\nimport { patternsMessages } from './messages';\nimport { Button, type ButtonProps } from '../base/Button';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { disclosureStyles } from '../utils/style/transitions';\nimport { t } from '../config/theme/tokens';\nimport { provideFieldLayout, type FieldLayout } from '../form/field-layout';\n\n/** 角色与提交由组件管理 */\nexport type FilterBarManaged = 'role' | 'onSubmit' | 'onReset';\n\n/** 未声明的属性落到 form 上 */\nexport interface FilterBarProps\n extends PartProps<'form', FilterBarManaged>, Pick<FieldLayout, 'labelPlacement' | 'labelWidth'> {\n /** 点“查询”或在输入框里按回车 */\n onSearch?: () => void;\n /** 点“重置”:清空条件由使用方做(通常 Object.assign 回初始值,再查询一次) */\n onReset?: () => void;\n /** 查询中:“查询”按钮显示加载中 */\n loading?: boolean;\n /** 不常用的条件:默认收起,点“展开”才显示 */\n more?: VNodeChild;\n /** 是否展开了 more:父级写 _expanded={x} */\n _expanded?: boolean;\n /** 收起的条件里已经填了几项:大于 0 时显示在“展开”旁边 */\n moreActive?: number;\n /** 每个条件的最小宽度(CSS 长度),默认 `'16em'` */\n minFieldWidth?: string;\n /** 整行起始边的内容:在条件前面(QueryTable 的标题);对齐第一行 */\n start?: VNodeChild;\n /** 整行结束边的内容:在查询、重置后面(QueryTable 的工具栏按钮);对齐第一行,展开更多条件时也不往下掉 */\n end?: VNodeChild;\n /** 结束边额外的按钮:放在“重置”前面(如“导出筛选结果”) */\n actions?: VNodeChild;\n /** start 外层的部件属性 */\n propsStart?: PartProps<'div'>;\n /** end 外层的部件属性 */\n propsEnd?: PartProps<'div'>;\n /** 条件栅格的部件属性 */\n propsFields?: PartProps<'div'>;\n /** 按钮区的部件属性 */\n propsActions?: PartProps<'div'>;\n /** 查询按钮的部件属性 */\n propsSearch?: Partial<ButtonProps>;\n /** 重置按钮的部件属性 */\n propsReset?: Partial<ButtonProps>;\n /** 展开收起按钮的部件属性 */\n propsToggle?: Partial<ButtonProps>;\n /** 收起的条件的外层(display: contents)的部件属性 */\n propsMore?: PartProps<'div'>;\n /** 高级筛选的字段:给了时按钮区多一个“高级筛选” */\n conditionFields?: readonly ConditionField[];\n /** 高级筛选的条件:父级写 _condition={condition};没有时是 null,“重置”时清成 null */\n _condition?: Condition | null;\n /** 高级筛选按钮的部件属性 */\n propsAdvancedToggle?: Partial<ButtonProps>;\n /** 高级筛选那一行(条件构造器)的部件属性 */\n propsAdvanced?: Partial<ConditionBuilderProps>;\n /** 常用的条件:Field */\n children?: VNodeChild;\n}\n\n/** FilterBar 的样式:root、start、end、fields、more、actions、toggleIconOpen */\nexport const filterBarStyles = css.create({\n // 条件栅格与按钮区并排;放不下时按钮区换行\n root: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'flex-end',\n columnGap: '_xl',\n rowGap: '_md',\n },\n // 两头的内容和第一行的控件一样高、对齐第一行:展开更多条件时(按钮区落到最后一行)它们仍在第一行\n start: {\n display: 'flex',\n alignItems: 'center',\n alignSelf: 'flex-start',\n minHeight: t.sizes.control,\n flexShrink: 0,\n },\n end: {\n display: 'flex',\n alignItems: 'center',\n alignSelf: 'flex-start',\n minHeight: t.sizes.control,\n flexShrink: 0,\n gap: '_sm',\n },\n fields: {\n display: 'grid',\n columnGap: '_xl',\n rowGap: '_md',\n alignItems: 'start',\n flexGrow: 1,\n minWidth: 0,\n },\n // 收起的条件直接是栅格的格子\n more: { display: 'contents' },\n // 宽度随按钮、不折行;换到下一行时也靠结束边\n actions: {\n display: 'flex',\n alignItems: 'center',\n gap: '_sm',\n flexShrink: 0,\n marginInlineStart: 'auto',\n },\n // 展开后箭头朝上\n toggleIconOpen: { rotate: '180deg' },\n // 高级筛选与它的摘要各占一整行,排在条件与按钮下面\n advanced: {\n flexBasis: '100%',\n padding: '_md',\n backgroundColor: '_surfaceMuted',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_borderMuted',\n borderRadius: '_md',\n },\n summary: { flexBasis: '100%', display: 'flex', alignItems: 'baseline', gap: '_sm', minWidth: 0 },\n});\n\n/** FilterBar 的样式:columns(每格的最小宽度;栅格本身至少放得下一格) */\nexport const filterBarColumnStyles = css.create({\n columns: (minWidth: string) => ({\n flexBasis: minWidth,\n gridTemplateColumns: `repeat(auto-fill, minmax(min(${minWidth}, 100%), 1fr))`,\n }),\n});\n\n/** 查询栏:筛选条件的栅格,查询、重置,不常用的条件可以展开收起 */\nexport const FilterBar = _component(\n ({\n onSearch,\n onReset,\n loading = false,\n more,\n _expanded: expanded = _bindable(false),\n moreActive = 0,\n minFieldWidth = '16em',\n start,\n end,\n actions,\n labelPlacement = 'start',\n labelWidth = 'auto',\n propsStart,\n propsEnd,\n propsFields,\n propsActions,\n propsSearch,\n propsReset,\n propsToggle,\n propsMore,\n conditionFields,\n _condition: condition = _bindable<Condition | null>(null),\n propsAdvancedToggle,\n propsAdvanced,\n children,\n ...rest\n }: FilterBarProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const lang = useLang();\n const moreId = useId();\n const advancedId = useId();\n let advancedOpen = _state(false);\n\n /** 条件里有几条比较(按钮旁的计数) */\n const countRules = (item: Condition): number =>\n !isConditionGroup(item)\n ? 1\n : 'not' in item\n ? countRules(item.not)\n : ('all' in item ? item.all : item.any).reduce(\n (sum, child) => sum + countRules(child),\n 0,\n );\n provideFieldLayout(() => ({ labelPlacement, labelWidth }));\n\n function onSubmit(event: Event) {\n event.preventDefault();\n onSearch?.();\n }\n\n return () => (\n <form\n {...mergeAttrs<'form'>({ sx: filterBarStyles.root }, rest, { role: 'search', onSubmit })}\n data-expanded={more != null ? expanded : undefined}\n >\n {start != null && (\n <div {...mergeAttrs<'div'>({ sx: filterBarStyles.start }, propsStart)}>{start}</div>\n )}\n <div\n {...mergeAttrs<'div'>(\n { sx: [filterBarStyles.fields, filterBarColumnStyles.columns(minFieldWidth)] },\n propsFields,\n )}\n >\n {children}\n {more != null && expanded && (\n <div {...mergeAttrs<'div'>({ sx: filterBarStyles.more }, propsMore, { id: moreId })}>\n {more}\n </div>\n )}\n </div>\n <div {...mergeAttrs<'div'>({ sx: filterBarStyles.actions }, propsActions)}>\n {more != null && (\n <Button\n {...mergePartProps<ButtonProps>(\n {\n variant: 'ghost',\n tone: 'neutral',\n iconEnd: ChevronDown,\n propsIconEnd: {\n sx: [disclosureStyles.icon, expanded && filterBarStyles.toggleIconOpen],\n },\n },\n propsToggle,\n {\n type: 'button',\n 'aria-expanded': expanded,\n 'aria-controls': expanded ? moreId : undefined,\n onClick: () => (expanded = !expanded),\n },\n )}\n >\n {expanded ? messages.filterCollapse : messages.filterExpand}\n {!expanded && moreActive > 0 && (\n <Badge tone=\"accent\" radius=\"full\">\n {messages.filterActive(moreActive)}\n </Badge>\n )}\n </Button>\n )}\n {conditionFields && (\n <Button\n {...mergePartProps<ButtonProps>(\n { variant: 'ghost', tone: 'neutral', icon: ListFilter },\n propsAdvancedToggle,\n {\n type: 'button',\n 'aria-expanded': advancedOpen,\n 'aria-controls': advancedOpen ? advancedId : undefined,\n onClick: () => (advancedOpen = !advancedOpen),\n },\n )}\n >\n {messages.filterAdvanced}\n {!advancedOpen && condition && (\n <Badge tone=\"accent\" radius=\"full\">\n {messages.filterActive(countRules(condition))}\n </Badge>\n )}\n </Button>\n )}\n {actions}\n <Button\n {...mergePartProps<ButtonProps>({ variant: 'outline', tone: 'neutral' }, propsReset, {\n type: 'button',\n onClick: () => {\n if (conditionFields) condition = null;\n onReset?.();\n },\n })}\n >\n {messages.filterReset}\n </Button>\n <Button {...mergePartProps<ButtonProps>({ loading }, propsSearch, { type: 'submit' })}>\n {messages.filterSearch}\n </Button>\n </div>\n {end != null && (\n <div {...mergeAttrs<'div'>({ sx: filterBarStyles.end }, propsEnd)}>{end}</div>\n )}\n {conditionFields && advancedOpen && (\n <ConditionBuilder\n {...mergePartProps<ConditionBuilderProps>(\n {\n fields: conditionFields,\n sx: filterBarStyles.advanced,\n 'aria-label': messages.filterAdvanced,\n },\n propsAdvanced,\n { id: advancedId },\n )}\n _value={condition}\n />\n )}\n {conditionFields && !advancedOpen && condition && (\n <div {...css.attrs(filterBarStyles.summary)}>\n <Small tone=\"muted\">\n {messages.filterAdvanced}:{describeCondition(condition, conditionFields, lang)}\n </Small>\n <Button variant=\"link\" size=\"sm\" type=\"button\" onClick={() => (condition = null)}>\n {lang.clear}\n </Button>\n </div>\n )}\n </form>\n );\n },\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { VNodeChild } from 'vue';
|
|
2
|
+
import { type FormProps } from '../form/Form';
|
|
3
|
+
import { type FormErrorSummaryProps } from '../form/FormErrorSummary';
|
|
4
|
+
import type { FormControllerBase } from '../form/useForm';
|
|
5
|
+
import { type DialogProps } from '../overlay/Dialog';
|
|
6
|
+
/** 未声明的属性落到 dialog 上(见 Dialog) */
|
|
7
|
+
export interface FormDialogProps extends Omit<DialogProps, 'onConfirm' | 'children'> {
|
|
8
|
+
/** useForm() 创建的控制器;确认按钮调它的 submit */
|
|
9
|
+
controller: FormControllerBase;
|
|
10
|
+
/** 表单(form)的部件属性:标签的布局(labelPlacement、labelWidth)等写在这里 */
|
|
11
|
+
propsForm?: Partial<FormProps>;
|
|
12
|
+
/** 错误汇总的部件属性 */
|
|
13
|
+
propsSummary?: Partial<FormErrorSummaryProps>;
|
|
14
|
+
/** 字段 */
|
|
15
|
+
children?: VNodeChild;
|
|
16
|
+
}
|
|
17
|
+
/** 表单对话框:确认就是提交,没通过校验或提交失败时留着 */
|
|
18
|
+
export declare const FormDialog: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<FormDialogProps>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { mergePartProps } from "../utils/props.js";
|
|
2
|
+
import { Form } from "../form/Form.js";
|
|
3
|
+
import { Alert } from "../feedback/Alert.js";
|
|
4
|
+
import { Dialog } from "../overlay/Dialog.js";
|
|
5
|
+
import { FormErrorSummary } from "../form/FormErrorSummary.js";
|
|
6
|
+
import { createVNode, unref } from "vue";
|
|
7
|
+
import { _component, childrenProp, childrenStable, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
8
|
+
//#region src/patterns/FormDialog.tsx
|
|
9
|
+
/** 未声明的属性落到 dialog 上(见 Dialog) */
|
|
10
|
+
/** 表单对话框:确认就是提交,没通过校验或提交失败时留着 */
|
|
11
|
+
var FormDialog = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
12
|
+
const _cache = [];
|
|
13
|
+
const controller = readonlyProp(_props, "controller");
|
|
14
|
+
const propsForm = readonlyProp(_props, "propsForm");
|
|
15
|
+
const propsSummary = readonlyProp(_props, "propsSummary");
|
|
16
|
+
const children = childrenProp(_props);
|
|
17
|
+
const rest = restProps(_props, _ctx, false);
|
|
18
|
+
return () => createVNode(Dialog, mergeJsxProps(rest, {
|
|
19
|
+
"onConfirm": _cache[0] || (_cache[0] = () => controller.value.submit()),
|
|
20
|
+
"key": "__site1"
|
|
21
|
+
}), {
|
|
22
|
+
default: () => [createVNode(Form, mergeJsxProps(mergePartProps({ controller: controller.value }, propsForm.value), { "key": "__site0" }), {
|
|
23
|
+
default: () => [
|
|
24
|
+
controller.value.formError && createVNode(Alert, {
|
|
25
|
+
"tone": "danger",
|
|
26
|
+
"description": controller.value.formError
|
|
27
|
+
}, null),
|
|
28
|
+
createVNode(FormErrorSummary, propsSummary.value, null),
|
|
29
|
+
unref(children)
|
|
30
|
+
],
|
|
31
|
+
$stable: childrenStable()
|
|
32
|
+
})],
|
|
33
|
+
$stable: childrenStable()
|
|
34
|
+
});
|
|
35
|
+
}, {
|
|
36
|
+
name: "FormDialog",
|
|
37
|
+
props: [
|
|
38
|
+
"controller",
|
|
39
|
+
"propsForm",
|
|
40
|
+
"propsSummary"
|
|
41
|
+
]
|
|
42
|
+
});
|
|
43
|
+
//#endregion
|
|
44
|
+
export { FormDialog };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=FormDialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormDialog.js","names":["_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_createVNode","_mergeProps","_vue_unref","_vtp_childrenStable"],"sources":["../../src/patterns/FormDialog.tsx"],"sourcesContent":["/**\n * 表单对话框:Dialog 加 Form 加错误汇总,确认按钮就是提交——校验没通过时留在对话框里(焦点到第一个出错的字段),\n * 提交(useForm 的 onSubmit)进行时确认按钮显示加载中,成功后关闭,失败(reject)时留着。\n * 字段多、有联动、要自己排版的表单用它;一两个文字字段的小表单用命令式的 useFormDialog。\n *\n * const form = useForm({ schema, initial, onSubmit: async (values) => { await api.save(values); open = false; } });\n * <FormDialog controller={form} _open={open} title=\"新建菜单\" size=\"md\">\n * <form.Field name=\"name\" label=\"名称\"><TextField /></form.Field>\n * </FormDialog>\n *\n * 提交被后端拒绝(带业务错误码的错误,如“名称已存在”)时,说明显示在表单顶上的提示条里,对话框留着。\n *\n * 打开前由调用方 form.reset(初始值):新建是空白的初始值,修改是当前记录。在字段里按 Enter 也是提交,\n * 所以 onSubmit 成功后要自己关(写 open = false);只点确认按钮时对话框会在提交成功后自己关。\n *\n * 部件:Dialog 的全部部件(propsConfirm、propsCancel、propsBody 等),加上 Form(propsForm)与 FormErrorSummary(propsSummary)。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport type { VNodeChild } from 'vue';\nimport { Alert } from '../feedback/Alert';\nimport { Form, type FormProps } from '../form/Form';\nimport { FormErrorSummary, type FormErrorSummaryProps } from '../form/FormErrorSummary';\nimport type { FormControllerBase } from '../form/useForm';\nimport { Dialog, type DialogProps } from '../overlay/Dialog';\nimport { mergePartProps } from '../utils/props';\n\n/** 未声明的属性落到 dialog 上(见 Dialog) */\nexport interface FormDialogProps extends Omit<DialogProps, 'onConfirm' | 'children'> {\n /** useForm() 创建的控制器;确认按钮调它的 submit */\n controller: FormControllerBase;\n /** 表单(form)的部件属性:标签的布局(labelPlacement、labelWidth)等写在这里 */\n propsForm?: Partial<FormProps>;\n /** 错误汇总的部件属性 */\n propsSummary?: Partial<FormErrorSummaryProps>;\n /** 字段 */\n children?: VNodeChild;\n}\n\n/** 表单对话框:确认就是提交,没通过校验或提交失败时留着 */\nexport const FormDialog = _component(\n ({ controller, propsForm, propsSummary, children, ...rest }: FormDialogProps) =>\n () => (\n <Dialog {...rest} onConfirm={() => controller.submit()}>\n <Form {...mergePartProps<FormProps>({ controller }, propsForm)}>\n {controller.formError && <Alert tone=\"danger\" description={controller.formError} />}\n <FormErrorSummary {...propsSummary} />\n {children}\n </Form>\n </Dialog>\n ),\n);\n"],"mappings":";;;;;;;;;;AAuCA,IAAa,aAAa,0BACxB,QAAA,SAAA;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,aAAAA,aAAA,QAAA,YAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,eAAAA,aAAA,QAAA,cAAA;CAAA,MAAA,WAAAC,aAAA,MAAA;CAAA,MAAqD,OAAIC,UAAA,QAAA,MAAA,KAAA;CAAA,aACvDC,YAAA,QAAAC,cACc,MAAI;EAAA,aAAA,OAAA,OAAA,OAAA,WAAmB,WAAA,MAAW,OAAO;EAAC,OAAA;CAAA,CAAA,GAAA;EAAA,eAAA,CAAAD,YAAA,MAAAC,cAC1C,eAA0B,EAAE,YAAU,WAAA,MAAC,GAAC,UAAA,KAAW,GAAC,EAAA,OAAA,UAAA,CAAA,GAAA;GAAA,eAAA;IAC3D,WAAA,MAAW,aAASD,YAAA,OAAA;KAAA,QAAA;KAAA,eAAsC,WAAA,MAAW;IAAS,GAAA,IAAA;IAAIA,YAAA,kBAAA,aAAA,OAAA,IAAA;IAAAE,MAAA,QAAA;GAAA;GAAA,SAAAC,eAAA;EAAA,CAAA,CAAA;EAAA,SAAAA,eAAA;CAAA,CAAA;AAKxF,GAAA;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACL"}
|
|
@@ -9,8 +9,8 @@ import { ScrollArea } from "../layout/ScrollArea.js";
|
|
|
9
9
|
import { useSortable } from "../utils/dom/sortable.js";
|
|
10
10
|
import { useMessages } from "../config/lang/messages.js";
|
|
11
11
|
import { patternsMessages } from "./messages.js";
|
|
12
|
-
import { createVNode,
|
|
13
|
-
import { _component, childrenProp, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
12
|
+
import { createVNode, shallowRef } from "vue";
|
|
13
|
+
import { _component, childrenProp, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
14
14
|
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
15
15
|
import { GripVertical } from "@lucide/icons";
|
|
16
16
|
//#region src/patterns/KanbanBoard.tsx
|
|
@@ -209,16 +209,16 @@ var KanbanBoard = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
209
209
|
const draggedItem = dragging === void 0 ? void 0 : itemOf(dragging);
|
|
210
210
|
const draggedFrom = draggedItem === void 0 ? void 0 : getColumn.value(draggedItem);
|
|
211
211
|
return createVNode("div", mergeAttrs({}, rest, { role: "group" }), [
|
|
212
|
-
createVNode(ScrollArea, { "axis": "x" }, { default: () => [createVNode("div",
|
|
212
|
+
createVNode(ScrollArea, { "axis": "x" }, { default: () => [createVNode("div", mergeJsxProps({ "ref": _cache[0] || (_cache[0] = (element) => board.value = element instanceof HTMLElement ? element : null) }, css.attrs(kanbanBoardStyles.board)), [columns.value.map((column) => {
|
|
213
213
|
const cards = items.value.filter((item) => getColumn.value(item) === column.key);
|
|
214
214
|
const over = drop && draggedFrom !== column.key && drop.group === column.key;
|
|
215
215
|
const reason = over ? drop.reason : null;
|
|
216
216
|
const headingId = `${sortable.instructionsId}-${column.key}`;
|
|
217
|
-
const indicator = createVNode("li",
|
|
217
|
+
const indicator = createVNode("li", mergeJsxProps({
|
|
218
218
|
"key": "drop-indicator",
|
|
219
219
|
"aria-hidden": "true"
|
|
220
220
|
}, css.attrs(kanbanBoardStyles.indicator, reason != null && kanbanBoardStyles.indicatorDenied)), null);
|
|
221
|
-
return createVNode("section",
|
|
221
|
+
return createVNode("section", mergeJsxProps({
|
|
222
222
|
"key": column.key,
|
|
223
223
|
"aria-labelledby": headingId
|
|
224
224
|
}, sortable.groupProps(column.key), css.attrs(kanbanBoardStyles.column, css.bind(kanbanBoardDynamicStyles.width, () => [columnWidth.value], true), over && (reason ? kanbanBoardStyles.denied : kanbanBoardStyles.allowed))), [
|
|
@@ -233,10 +233,10 @@ var KanbanBoard = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
233
233
|
const key = getKey.value(item);
|
|
234
234
|
const active = dragging === key;
|
|
235
235
|
const { onPointerdown: _, ...handle } = sortable.handleProps(key);
|
|
236
|
-
const card = createVNode("li",
|
|
236
|
+
const card = createVNode("li", mergeJsxProps({ "key": key }, sortable.itemProps(key), css.attrs(kanbanBoardStyles.card, active && ghost && kanbanBoardStyles.placeholder, active && !ghost && kanbanBoardStyles.lifted), {
|
|
237
237
|
"data-state": active ? "dragging" : void 0,
|
|
238
238
|
"onPointerdown": (event) => onPointerdown(event, key)
|
|
239
|
-
}), [createVNode("div", null, [children.value(item, { dragging: active })]), createVNode(ButtonBase,
|
|
239
|
+
}), [createVNode("div", null, [children.value(item, { dragging: active })]), createVNode(ButtonBase, mergeJsxProps(handle, {
|
|
240
240
|
"data-handle": "",
|
|
241
241
|
"sx": kanbanBoardStyles.handle,
|
|
242
242
|
"aria-label": lang.sortableHandle(labelOf(item))
|
|
@@ -250,7 +250,7 @@ var KanbanBoard = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
250
250
|
])
|
|
251
251
|
]);
|
|
252
252
|
})])] }),
|
|
253
|
-
ghost && ghostItem !== void 0 && createVNode("div",
|
|
253
|
+
ghost && ghostItem !== void 0 && createVNode("div", mergeJsxProps({ "aria-hidden": "true" }, css.attrs(kanbanBoardStyles.card, kanbanBoardStyles.ghost, css.bind(kanbanBoardDynamicStyles.ghost, () => [
|
|
254
254
|
ghost.left,
|
|
255
255
|
ghost.top,
|
|
256
256
|
ghost.width
|