@zerodep-dev/vue-tsx-pro-ui 0.1.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/code-editor/CodeEditor.d.ts +18 -0
- package/dist/code-editor/CodeEditor.js +57 -0
- package/dist/code-editor/CodeEditor.js.map +1 -0
- package/dist/code-editor/editor.d.ts +52 -0
- package/dist/code-editor/editor.js +225 -0
- package/dist/code-editor/editor.js.map +1 -0
- package/dist/code-editor/index.d.ts +9 -0
- package/dist/code-editor/index.js +3 -0
- 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/lang/en-US.js +21 -3
- package/dist/config/lang/en-US.js.map +1 -1
- package/dist/config/lang/format.d.ts +2 -1
- package/dist/config/lang/format.js +9 -2
- package/dist/config/lang/format.js.map +1 -1
- package/dist/config/lang/lang.d.ts +19 -6
- package/dist/config/lang/lang.js +11 -2
- package/dist/config/lang/lang.js.map +1 -1
- package/dist/config/lang/zh-CN.js +21 -3
- package/dist/config/lang/zh-CN.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/diagram/index.d.ts +2 -1
- package/dist/diagram/index.js +2 -1
- package/dist/diagram/layout.d.ts +57 -0
- package/dist/diagram/layout.js +50 -0
- package/dist/diagram/layout.js.map +1 -0
- 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.d.ts +7 -0
- package/dist/form/Field.js +20 -9
- 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/form/useForm.d.ts +4 -1
- package/dist/form/useForm.js +35 -13
- package/dist/form/useForm.js.map +1 -1
- package/dist/index.d.ts +18 -10
- package/dist/index.js +17 -9
- 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 +5 -5
- 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 +4 -4
- 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 +15 -15
- 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 +5 -5
- 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 +7 -7
- package/dist/input/TreeSelect.js.map +1 -1
- package/dist/input/Upload.js +10 -9
- package/dist/input/Upload.js.map +1 -1
- package/dist/input/condition.d.ts +1 -1
- package/dist/input/condition.js +32 -14
- package/dist/input/condition.js.map +1 -1
- package/dist/input/people.d.ts +15 -14
- package/dist/input/people.js +5 -5
- package/dist/input/people.js.map +1 -1
- package/dist/input/remote-items.d.ts +4 -2
- package/dist/input/remote-items.js +7 -2
- package/dist/input/remote-items.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 +10 -1
- package/dist/patterns/QueryTable.js +128 -68
- 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 +39 -1
- package/dist/patterns/messages.js +40 -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/clone.d.ts +8 -0
- package/dist/utils/data/clone.js +21 -0
- package/dist/utils/data/clone.js.map +1 -0
- package/dist/utils/data/date.d.ts +10 -0
- package/dist/utils/data/date.js +13 -1
- package/dist/utils/data/date.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/data/numeric.d.ts +7 -0
- package/dist/utils/data/numeric.js +46 -1
- package/dist/utils/data/numeric.js.map +1 -1
- package/dist/utils/dom/download.d.ts +8 -1
- package/dist/utils/dom/download.js +19 -2
- package/dist/utils/dom/download.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/error-message.d.ts +17 -0
- package/dist/utils/request/error-message.js +50 -0
- package/dist/utils/request/error-message.js.map +1 -0
- package/dist/utils/request/http.d.ts +80 -4
- package/dist/utils/request/http.js +121 -24
- package/dist/utils/request/http.js.map +1 -1
- package/dist/utils/request/use-autosave.d.ts +35 -0
- package/dist/utils/request/use-autosave.js +69 -0
- package/dist/utils/request/use-autosave.js.map +1 -0
- package/dist/utils/request/use-request.js +4 -2
- package/dist/utils/request/use-request.js.map +1 -1
- package/package.json +57 -4
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsGA,IAAa,kBAAkB,IAAI,OAAO;CAExC,MAAM;EACJ,SAAS;EACT,UAAU;EACV,YAAY;EACZ,WAAW;EACX,QAAQ;CACV;CAEA,OAAO;EACL,SAAS;EACT,YAAY;EACZ,WAAW;EACX,WAAW,EAAE,MAAM;EACnB,YAAY;CACd;CACA,KAAK;EACH,SAAS;EACT,YAAY;EACZ,WAAW;EACX,WAAW,EAAE,MAAM;EACnB,YAAY;EACZ,KAAK;CACP;CACA,QAAQ;EACN,SAAS;EACT,WAAW;EACX,QAAQ;EACR,YAAY;EACZ,UAAU;EACV,UAAU;CACZ;CAEA,MAAM,EAAE,SAAS,WAAW;CAE5B,SAAS;EACP,SAAS;EACT,YAAY;EACZ,KAAK;EACL,YAAY;EACZ,mBAAmB;CACrB;CAEA,gBAAgB,EAAE,QAAQ,SAAS;CAEnC,UAAU;EACR,WAAW;EACX,SAAS;EACT,iBAAiB;EACjB,aAAa;EACb,aAAa;EACb,aAAa;EACb,cAAc;CAChB;CACA,SAAS;EAAE,WAAW;EAAQ,SAAS;EAAQ,YAAY;EAAY,KAAK;EAAO,UAAU;CAAE;AACjG,CAAC;;AAGD,IAAa,wBAAwB,IAAI,OAAO,EAC9C,UAAU,cAAsB;CAC9B,WAAW;CACX,qBAAqB,gCAAgC,SAAQ;AAC/D,GACF,CAAC;;AAGD,IAAa,YAAY,0BACvB,QAAA,SA2BsB;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,WAAAA,aAAA,QAAA,UAAA;CAAA,MAAA,UAAAA,aAAA,QAAA,SAAA;CAAA,MAAA,UAAAA,aAAA,QAAA,WAxBV,KAAK;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,WAAAC,SAAA,QAAA,mBAEiB,OAAK,KAAA,IAAA;CAAA,MAAA,aAAAD,aAAA,QAAA,cACxB,CAAC;CAAA,MAAA,gBAAAA,aAAA,QAAA,iBACE,MAAM;CAAA,MAAA,QAAAA,aAAA,QAAA,OAAA;CAAA,MAAA,MAAAA,aAAA,QAAA,KAAA;CAAA,MAAA,UAAAA,aAAA,QAAA,SAAA;CAAA,MAAA,iBAAAA,aAAA,QAAA,kBAIL,OAAO;CAAA,MAAA,aAAAA,aAAA,QAAA,cACX,MAAM;CAAA,MAAA,aAAAA,aAAA,QAAA,YAAA;CAAA,MAAA,WAAAA,aAAA,QAAA,UAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,eAAAA,aAAA,QAAA,cAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,aAAAA,aAAA,QAAA,YAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,kBAAAA,aAAA,QAAA,iBAAA;CAAA,MAAA,YAAAC,SAAA,QAAA,oBAUiC,MAAI,KAAA,IAAA;CAAA,MAAA,sBAAAD,aAAA,QAAA,qBAAA;CAAA,MAAA,gBAAAA,aAAA,QAAA,eAAA;CAAA,MAAA,WAAAE,aAAA,MAAA;CAAA,MAIrD,OAAIC,UAAA,QAAA,MAAA,KAAA;CAEP,MAAM,WAAW,YAAY,YAAY,gBAAgB;CACzD,MAAM,OAAO,QAAQ;CACrB,MAAM,SAAS,MAAM;CACrB,MAAM,aAAa,MAAM;CACzB,IAAI,eAAYC,IAAU,KAAK;;CAG/B,MAAM,cAAc,SAClB,CAAC,iBAAiB,IAAI,IAClB,IACA,SAAS,OACP,WAAW,KAAK,GAAG,KAClB,SAAS,OAAO,KAAK,MAAM,KAAK,IAAA,CAAK,QACnC,KAAK,UAAU,MAAM,WAAW,KAAK,GACtC,CACF;CACR,0BAA0B;EAAE,gBAAc,eAAA;EAAE,YAAU,WAAA;CAAC,EAAE;CAEzD,SAAS,SAAS,OAAc;EAC9B,MAAM,eAAe;EACrB,SAAA,QAAW;CACb;CAEA,aAAOC,YAAA,QAAAC,cAEC,WAAmB,EAAE,IAAI,gBAAgB,KAAK,GAAG,MAAM;EAAE,MAAM;EAAU;CAAS,CAAC,GAAC,EAAA,iBACzE,KAAA,SAAQ,OAAI,SAAA,QAAc,KAAA,EAAS,CAAA,GAAA;EAEjD,MAAA,SAAS,QAAID,YAAA,OACH,WAAkB,EAAE,IAAI,gBAAgB,MAAM,GAAC,WAAA,KAAY,GAAC,CAAA,MAAA,KAAA,CAAA;EACtEA,YAAA,OAEK,WACF,EAAE,IAAI,CAAC,gBAAgB,QAAM,IAAA,KAAE,sBAAsB,eAAO,CAAA,cAAA,KAAA,GAAA,IAAA,CAAA,EAAiB,GAAC,YAAA,KAEhF,GAAC,CAAA,SAAA,OAGA,KAAA,SAAQ,QAAI,SAAA,SAAYA,YAAA,OACd,WAAkB,EAAE,IAAI,gBAAgB,KAAK,GAAC,UAAA,OAAa,EAAE,IAAI,OAAO,CAAC,GAAC,CAAA,KAAA,KAAA,CAAA,CAGpF,CAAA;EAAAA,YAAA,OAEM,WAAkB,EAAE,IAAI,gBAAgB,QAAQ,GAAC,aAAA,KAAc,GAAC;GACtE,KAAA,SAAQ,QAAIA,YAAA,QAAAC,cAEL,eACF;IACE,SAAS;IACT,MAAM;IACN,SAAS;IACT,cAAc,EACZ,IAAI,CAAC,iBAAiB,MAAM,SAAA,SAAY,gBAAgB,cAAc,EACxE;GACF,GAAC,YAAA,OAED;IACE,MAAM;IACN,iBAAe,SAAA;IACf,iBAAiB,SAAA,QAAW,SAAS,KAAA;IACrC,eAAgB,SAAA,QAAW,CAAA,SAAA;GAC7B,CACF,GAAC,EAAA,OAAA,UAAA,CAAA,GAAA;IAAA,eAAA,CAEA,SAAA,QAAW,SAAS,iBAAiB,SAAS,cAC9C,CAAA,SAAA,SAAa,WAAA,QAAa,KAACD,YAAA,OAAA;KAAA,QAAA;KAAA,UAAA;KAAA,OAAA;IAAA,GAAA;KAAA,eAAA,CAEvB,SAAS,aAAY,WAAA,KAAW,CAAC;KAAA,SAAA;IAAA,CAAA,CAErC;IAAA,SAAA;GAAA,CAAA;GAGJ,gBAAA,SAAAA,YAAA,QAAAC,cAEO,eACF;IAAE,SAAS;IAAS,MAAM;IAAW,MAAM;GAAW,GAAC,oBAAA,OAEvD;IACE,MAAM;IACN,iBAAe,aAAA;IACf,iBAAiB,aAAA,QAAe,aAAa,KAAA;IAC7C,eAAgB,aAAA,QAAe,CAAA,aAAA;GACjC,CACF,GAAC,EAAA,OAAA,UAAA,CAAA,GAAA;IAAA,eAAA,CAEA,SAAS,gBACT,CAAA,aAAA,SAAa,UAAA,SAAaD,YAAA,OAAA;KAAA,QAAA;KAAA,UAAA;KAAA,OAAA;IAAA,GAAA;KAAA,eAAA,CAEtB,SAAS,aAAa,WAAU,UAAA,KAAU,CAAC,CAAC;KAAA,SAAA;IAAA,CAAA,CAEhD;IAAA,SAAA;GAAA,CAAA;GAEJ,QAAA;GAAAA,YAAA,QAAAC,cAGK,eAA4B;IAAE,SAAS;IAAW,MAAM;GAAU,GAAC,WAAA,OAAc;IACnF,MAAM;IACN,eAAe;KACb,IAAA,gBAAA,OAAqB,UAAA,QAAY;KACjC,QAAA,QAAU;IACZ;GACF,CAAC,GAAC,EAAA,OAAA,UAAA,CAAA,GAAA;IAAA,eAAA,CAED,SAAS,WAAW;IAAA,SAAA;GAAA,CAAA;GAAAD,YAAA,QAAAC,cAEX,eAA4B,EAAE,SAAO,QAAA,MAAC,GAAC,YAAA,OAAe,EAAE,MAAM,SAAS,CAAC,GAAC,EAAA,OAAA,UAAA,CAAA,GAAA;IAAA,eAAA,CAClF,SAAS,YAAY;IAAA,SAAA;GAAA,CAAA;EAAA,CAAA;EAGzB,IAAA,SAAO,QAAID,YAAA,OACD,WAAkB,EAAE,IAAI,gBAAgB,IAAI,GAAC,SAAA,KAAU,GAAC,CAAA,IAAA,KAAA,CAAA;EAElE,gBAAA,SAAA,aAAA,SAA+BA,YAAA,kBAAAC,cAExB,eACF;GACE,QAAM,gBAAA;GACN,IAAI,gBAAgB;GACpB,cAAc,SAAS;EACzB,GAAC,cAAA,OAED,EAAE,IAAI,WAAW,CACnB,GAAC;GAAA,UAAA,UAAA;GAAA,mBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,UAAA,QAAA;EAAA,CAAA,GAAA,IAAA;EAIJ,gBAAA,SAAmB,CAAA,aAAA,SAAa,UAAA,SAAaD,YAAA,OACnC,IAAI,MAAM,gBAAgB,OAAO,GAAC,CAAAA,YAAA,OAAA;GAAA,QAAA;GAAA,OAAA;EAAA,GAAA;GAAA,eAAA;IAEtC,SAAS;IAAe;IAAE,kBAAiB,UAAA,OAAA,gBAAA,OAA6B,IAAI;GAAC;GAAA,SAAA;EAAA,CAAA,GAAAA,YAAA,QAAA;GAAA,WAAA;GAAA,QAAA;GAAA,QAAA;GAAA,WAAA,OAAA,OAAA,OAAA,WAEjB,UAAA,QAAY;GAAK,OAAA;EAAA,GAAA;GAAA,eAAA,CAC7E,KAAK,KAAK;GAAA,SAAA;EAAA,CAAA,CAAA,CAAA;CAGhB,CAAA;AAGP,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
|
@@ -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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KanbanBoard.js","names":["_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_vue_shallowRef","_createVNode","_mergeProps"],"sources":["../../src/patterns/KanbanBoard.tsx"],"sourcesContent":["/**\n * 看板:一列一个状态(或阶段、负责人),卡片在列之间移动。任务看板、招聘流程、工单。\n *\n * <KanbanBoard\n * aria-label=\"任务看板\"\n * columns={[{ key: 'designing', label: '设计中' }, { key: 'verifying', label: '测试中' }]}\n * items={tasks}\n * getKey={(task) => task.code}\n * getColumn={(task) => task.status}\n * getLabel={(task) => task.name}\n * canMove={(task, to) => (to === next(task.status) ? null : '只能拖到下一列')}\n * onMove={(task, to) => openProcessDialog(task, to)}\n * >\n * {(task) => <TaskCard task={task} />}\n * </KanbanBoard>\n *\n * 数据由使用方控制:看板不自己挪卡片,只在放下时报告 onMove(使用方处理完、刷新数据,卡片才换列)。\n * canMove 返回不能移动的原因(null 是可以):拖动时经过的列按它高亮(可以放的是强调色,不能放的标红并写出原因),\n * 落点处画一条插入线;放在不能放的列上时卡片弹回、读屏播报原因,并报告 onReject。\n * 给了 onReorder 时列内也能排序(数据立即重排,写回整个数组)。\n *\n * 拖动、键盘、读屏播报、自动滚动建在 useSortable 的分组上(utils/dom/sortable.ts):\n * - 指针:在卡片上按下拖动超过 3px 开始(点在卡片里的链接、按钮上不算),影子卡跟着指针走,原位留着变淡的占位;\n * - 键盘:每张卡片有把手按钮,空格 / Enter 拿起,← / → 换列(从右到左排版下镜像),↑ / ↓ 调整位置,\n * 空格 / Enter 放下,Esc 取消;每一步都有读屏播报(aria-live)。\n * 列多时整块横向滚动(ScrollArea),每列里的卡片多时列自己竖向滚动。\n */\nimport { GripVertical } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { Badge } from '../base/Badge';\nimport { useMessages } from '../config/lang/messages';\nimport { patternsMessages } from './messages';\nimport { ButtonBase } from '../base/ButtonBase';\nimport { Icon } from '../base/Icon';\nimport { VisuallyHidden } from '../base/VisuallyHidden';\nimport { useLang } from '../config/context';\nimport { ScrollArea } from '../layout/ScrollArea';\nimport { useSortable } from '../utils/dom/sortable';\nimport { mergeAttrs, type PartProps } from '../utils/props';\nimport type { StatusTone } from '../utils/style/tone-styles';\nimport { t } from '../config/theme/tokens';\n\n/** 看板的一列 */\nexport interface KanbanColumn {\n /** 列的标识:getColumn 返回它 */\n key: string;\n /** 列名 */\n label: string;\n /** 列名旁计数的色调(超时、异常这类要注意的列) */\n tone?: StatusTone;\n}\n\n/** 卡片的渲染状态 */\nexport interface KanbanCardState {\n /** 正在被拖动或被键盘拿起 */\n dragging: boolean;\n}\n\n/** 未声明的属性落到外层 div 上;aria-label 是看板的名称 */\nexport interface KanbanBoardProps<T> extends PartProps<'div'> {\n /** 各列:从起始边排 */\n columns: readonly KanbanColumn[];\n /** 全部卡片的数据 */\n items: readonly T[];\n /** 卡片的标识 */\n getKey: (item: T) => string;\n /** 卡片在哪一列 */\n getColumn: (item: T) => string;\n /** 卡片的名称:把手的名称、读屏播报;默认是 key */\n getLabel?: (item: T) => string;\n /** 能不能移到 to 列:返回不能的原因,null、undefined 是可以 */\n canMove?: (item: T, to: string) => string | null | undefined;\n /** 放到了可以放的列:使用方处理(打开处理的对话框、调接口),完成后刷新数据;index 是在目标列里的位置 */\n onMove: (item: T, to: string, index: number) => void;\n /** 列内排序:写回新的数组(所有列的卡片);不给时列内不能排序 */\n onReorder?: (items: T[]) => void;\n /** 放到了不能放的列:卡片已经弹回,可以在这里提示原因 */\n onReject?: (item: T, to: string, reason: string) => void;\n /**\n * 每列至少多宽(CSS 长度):放得下时各列平分整行,放不下时横向滚动\n * @default '16rem'\n */\n columnWidth?: string;\n /**\n * 每列的最大高度(CSS 长度):卡片多时列里竖向滚动\n * @default '70vh'\n */\n columnHeight?: string;\n /** 一列没有卡片时显示的内容,默认“没有” */\n empty?: VNodeChild;\n /** 卡片的内容 */\n children: (item: T, state: KanbanCardState) => VNodeChild;\n}\n\n/** KanbanBoard 的样式:board、column、header、list、card、handle… */\nexport const kanbanBoardStyles = css.create({\n board: { display: 'flex', gap: '_md', alignItems: 'flex-start', paddingBlockEnd: '_sm' },\n column: {\n display: 'flex',\n flexDirection: 'column',\n flexShrink: 0,\n borderRadius: '_lg',\n backgroundColor: '_surfaceMuted',\n outlineWidth: '_borderWidths.sm',\n outlineStyle: 'solid',\n outlineColor: 'transparent',\n outlineOffset: '-2px',\n transition: `outline-color ${t.durations.sm} ${t.easings.standard}`,\n },\n allowed: { outlineColor: '_accent' },\n denied: { outlineColor: '_danger' },\n header: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '_sm',\n paddingBlock: '_sm',\n paddingInline: '_md',\n fontWeight: '_semibold',\n fontSize: '_sm',\n },\n reason: { paddingInline: '_md', paddingBlockEnd: '_xs', color: '_dangerText', fontSize: '_xs' },\n list: {\n display: 'flex',\n flexDirection: 'column',\n gap: '_sm',\n margin: 0,\n paddingInline: '_sm',\n paddingBlockEnd: '_sm',\n listStyle: 'none',\n overflowY: 'auto',\n minHeight: '3rem',\n },\n card: {\n position: 'relative',\n display: 'grid',\n gridTemplateColumns: 'minmax(0, 1fr) auto',\n alignItems: 'start',\n gap: '_xs',\n padding: '_sm',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_border',\n borderRadius: '_md',\n backgroundColor: '_surfaceRaised',\n cursor: 'grab',\n touchAction: 'none',\n transition: `translate ${t.durations.md} ${t.easings.standard}`,\n },\n // 指针拖动中:原位的卡片变淡,跟着指针走的是影子卡(画在滚动区域外面,不被列裁掉)\n placeholder: { opacity: 0.4 },\n ghost: {\n position: 'fixed',\n zIndex: 50,\n pointerEvents: 'none',\n cursor: 'grabbing',\n boxShadow: '_lg',\n transition: 'none',\n rotate: '1.5deg',\n },\n // 键盘拿起:高亮边框\n lifted: { borderColor: '_accent', boxShadow: '_md' },\n handle: { color: '_textMuted', borderRadius: '_sm', padding: '2px', cursor: 'grab' },\n empty: { color: '_textMuted', fontSize: '_xs', paddingBlock: '_sm', textAlign: 'center' },\n // 拖到别的列时的落点:卡片之间的一条线\n indicator: {\n height: '_borderWidths.md',\n marginBlock: `calc(-1 * ${t.space.xs})`,\n borderRadius: '_full',\n backgroundColor: '_accent',\n },\n indicatorDenied: { backgroundColor: '_danger' },\n});\n\n/** KanbanBoard 的动态样式:列宽、列高、拖动的位移 */\nexport const kanbanBoardDynamicStyles = css.create({\n // 至少这么宽,富余的空间各列平分(同 Jira 的看板),放不下时外层横向滚动\n width: (width: string) => ({ flexBasis: width, flexGrow: 1, minWidth: width }),\n height: (height: string) => ({ maxHeight: height }),\n ghost: (left: number, top: number, width: number) => ({ left, top, width }),\n});\n\n/** 看板 */\nexport const KanbanBoard = _component(\n <T,>({\n columns,\n items,\n getKey,\n getColumn,\n getLabel,\n canMove,\n onMove,\n onReorder,\n onReject,\n columnWidth = '16rem',\n columnHeight = '70vh',\n empty,\n children,\n ...rest\n }: KanbanBoardProps<T>) => {\n const lang = useLang();\n const messages = useMessages('patterns', patternsMessages);\n let board = _state.raw<HTMLElement | null>(null);\n const labelOf = (item: T) => getLabel?.(item) ?? getKey(item);\n const itemOf = (key: string) => items.find((item) => getKey(item) === key);\n const sortable = useSortable({\n items: () => items,\n getKey: (item) => getKey(item),\n getLabel: labelOf,\n // 用时再读属性:写没写 onReorder 决定了列内能不能排序,父级后来才给也要跟上\n get onChange() {\n return onReorder && ((next: T[]) => onReorder(next));\n },\n container: () => board,\n groups: {\n keys: () => columns.map((column) => column.key),\n of: (item) => getColumn(item),\n label: (key) => columns.find((column) => column.key === key)?.label ?? key,\n canMove: (item, to) => canMove?.(item, to),\n onMove: (item, to, index) => onMove(item, to, index),\n onReject: (item, to, reason) => onReject?.(item, to, reason),\n },\n });\n\n /** 整张卡片都能拖:卡片里的链接、按钮(把手除外)、输入框照常点 */\n function onPointerdown(event: PointerEvent, key: string) {\n const target = event.target as Element;\n if (target.closest('a, input, select, textarea, button:not([data-handle])')) return;\n (sortable.handleProps(key).onPointerdown as (event: PointerEvent) => void)(event);\n }\n\n return () => {\n const dragging = sortable.active;\n const drop = sortable.target;\n const ghost = sortable.ghost;\n const ghostItem = ghost && itemOf(ghost.key);\n const draggedItem = dragging === undefined ? undefined : itemOf(dragging);\n const draggedFrom = draggedItem === undefined ? undefined : getColumn(draggedItem);\n return (\n <div {...mergeAttrs<'div'>({}, rest, { role: 'group' })}>\n <ScrollArea axis=\"x\">\n <div\n ref={(element: unknown) => (board = element instanceof HTMLElement ? element : null)}\n {...css.attrs(kanbanBoardStyles.board)}\n >\n {columns.map((column) => {\n const cards = items.filter((item) => getColumn(item) === column.key);\n // 拖到了别的列:高亮这一列,在落点画线\n const over = drop && draggedFrom !== column.key && drop.group === column.key;\n const reason = over ? drop.reason : null;\n const headingId = `${sortable.instructionsId}-${column.key}`;\n const indicator = (\n <li\n key=\"drop-indicator\"\n aria-hidden=\"true\"\n {...css.attrs(\n kanbanBoardStyles.indicator,\n reason != null && kanbanBoardStyles.indicatorDenied,\n )}\n />\n );\n return (\n <section\n key={column.key}\n aria-labelledby={headingId}\n {...sortable.groupProps(column.key)}\n {...css.attrs(\n kanbanBoardStyles.column,\n kanbanBoardDynamicStyles.width(columnWidth),\n over && (reason ? kanbanBoardStyles.denied : kanbanBoardStyles.allowed),\n )}\n >\n <div {...css.attrs(kanbanBoardStyles.header)}>\n <span id={headingId}>{column.label}</span>\n <Badge\n radius=\"full\"\n tone={cards.length > 0 ? (column.tone ?? 'neutral') : 'neutral'}\n >\n {cards.length}\n </Badge>\n </div>\n {reason && <div {...css.attrs(kanbanBoardStyles.reason)}>{reason}</div>}\n <ul\n {...css.attrs(\n kanbanBoardStyles.list,\n kanbanBoardDynamicStyles.height(columnHeight),\n )}\n >\n {cards.length === 0 && !over && (\n <li {...css.attrs(kanbanBoardStyles.empty)}>\n {empty ?? messages.kanbanEmpty}\n </li>\n )}\n {cards.flatMap((item, index) => {\n const key = getKey(item);\n const active = dragging === key;\n const { onPointerdown: _, ...handle } = sortable.handleProps(key);\n const card = (\n <li\n key={key}\n {...sortable.itemProps(key)}\n {...css.attrs(\n kanbanBoardStyles.card,\n active && ghost && kanbanBoardStyles.placeholder,\n active && !ghost && kanbanBoardStyles.lifted,\n )}\n data-state={active ? 'dragging' : undefined}\n onPointerdown={(event: PointerEvent) => onPointerdown(event, key)}\n >\n <div>{children(item, { dragging: active })}</div>\n <ButtonBase\n {...handle}\n data-handle=\"\"\n sx={kanbanBoardStyles.handle}\n aria-label={lang.sortableHandle(labelOf(item))}\n >\n <Icon icon={GripVertical} />\n </ButtonBase>\n </li>\n );\n return over && drop.index === index ? [indicator, card] : [card];\n })}\n {over && drop.index >= cards.length && indicator}\n </ul>\n </section>\n );\n })}\n </div>\n </ScrollArea>\n {ghost && ghostItem !== undefined && (\n <div\n aria-hidden=\"true\"\n {...css.attrs(\n kanbanBoardStyles.card,\n kanbanBoardStyles.ghost,\n kanbanBoardDynamicStyles.ghost(ghost.left, ghost.top, ghost.width),\n )}\n >\n <div>{children(ghostItem, { dragging: true })}</div>\n </div>\n )}\n <VisuallyHidden id={sortable.instructionsId}>\n {messages.kanbanInstructions}\n </VisuallyHidden>\n <VisuallyHidden aria-live=\"assertive\">{sortable.announcement}</VisuallyHidden>\n </div>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"KanbanBoard.js","names":["_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_vue_shallowRef","_createVNode","_mergeProps"],"sources":["../../src/patterns/KanbanBoard.tsx"],"sourcesContent":["/**\n * 看板:一列一个状态(或阶段、负责人),卡片在列之间移动。任务看板、招聘流程、工单。\n *\n * <KanbanBoard\n * aria-label=\"任务看板\"\n * columns={[{ key: 'designing', label: '设计中' }, { key: 'verifying', label: '测试中' }]}\n * items={tasks}\n * getKey={(task) => task.code}\n * getColumn={(task) => task.status}\n * getLabel={(task) => task.name}\n * canMove={(task, to) => (to === next(task.status) ? null : '只能拖到下一列')}\n * onMove={(task, to) => openProcessDialog(task, to)}\n * >\n * {(task) => <TaskCard task={task} />}\n * </KanbanBoard>\n *\n * 数据由使用方控制:看板不自己挪卡片,只在放下时报告 onMove(使用方处理完、刷新数据,卡片才换列)。\n * canMove 返回不能移动的原因(null 是可以):拖动时经过的列按它高亮(可以放的是强调色,不能放的标红并写出原因),\n * 落点处画一条插入线;放在不能放的列上时卡片弹回、读屏播报原因,并报告 onReject。\n * 给了 onReorder 时列内也能排序(数据立即重排,写回整个数组)。\n *\n * 拖动、键盘、读屏播报、自动滚动建在 useSortable 的分组上(utils/dom/sortable.ts):\n * - 指针:在卡片上按下拖动超过 3px 开始(点在卡片里的链接、按钮上不算),影子卡跟着指针走,原位留着变淡的占位;\n * - 键盘:每张卡片有把手按钮,空格 / Enter 拿起,← / → 换列(从右到左排版下镜像),↑ / ↓ 调整位置,\n * 空格 / Enter 放下,Esc 取消;每一步都有读屏播报(aria-live)。\n * 列多时整块横向滚动(ScrollArea),每列里的卡片多时列自己竖向滚动。\n */\nimport { GripVertical } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { Badge } from '../base/Badge';\nimport { useMessages } from '../config/lang/messages';\nimport { patternsMessages } from './messages';\nimport { ButtonBase } from '../base/ButtonBase';\nimport { Icon } from '../base/Icon';\nimport { VisuallyHidden } from '../base/VisuallyHidden';\nimport { useLang } from '../config/context';\nimport { ScrollArea } from '../layout/ScrollArea';\nimport { useSortable } from '../utils/dom/sortable';\nimport { mergeAttrs, type PartProps } from '../utils/props';\nimport type { StatusTone } from '../utils/style/tone-styles';\nimport { t } from '../config/theme/tokens';\n\n/** 看板的一列 */\nexport interface KanbanColumn {\n /** 列的标识:getColumn 返回它 */\n key: string;\n /** 列名 */\n label: string;\n /** 列名旁计数的色调(超时、异常这类要注意的列) */\n tone?: StatusTone;\n}\n\n/** 卡片的渲染状态 */\nexport interface KanbanCardState {\n /** 正在被拖动或被键盘拿起 */\n dragging: boolean;\n}\n\n/** 未声明的属性落到外层 div 上;aria-label 是看板的名称 */\nexport interface KanbanBoardProps<T> extends PartProps<'div'> {\n /** 各列:从起始边排 */\n columns: readonly KanbanColumn[];\n /** 全部卡片的数据 */\n items: readonly T[];\n /** 卡片的标识 */\n getKey: (item: T) => string;\n /** 卡片在哪一列 */\n getColumn: (item: T) => string;\n /** 卡片的名称:把手的名称、读屏播报;默认是 key */\n getLabel?: (item: T) => string;\n /** 能不能移到 to 列:返回不能的原因,null、undefined 是可以 */\n canMove?: (item: T, to: string) => string | null | undefined;\n /** 放到了可以放的列:使用方处理(打开处理的对话框、调接口),完成后刷新数据;index 是在目标列里的位置 */\n onMove: (item: T, to: string, index: number) => void;\n /** 列内排序:写回新的数组(所有列的卡片);不给时列内不能排序 */\n onReorder?: (items: T[]) => void;\n /** 放到了不能放的列:卡片已经弹回,可以在这里提示原因 */\n onReject?: (item: T, to: string, reason: string) => void;\n /**\n * 每列至少多宽(CSS 长度):放得下时各列平分整行,放不下时横向滚动\n * @default '16rem'\n */\n columnWidth?: string;\n /**\n * 每列的最大高度(CSS 长度):卡片多时列里竖向滚动\n * @default '70vh'\n */\n columnHeight?: string;\n /** 一列没有卡片时显示的内容,默认“没有” */\n empty?: VNodeChild;\n /** 卡片的内容 */\n children: (item: T, state: KanbanCardState) => VNodeChild;\n}\n\n/** KanbanBoard 的样式:board、column、header、list、card、handle… */\nexport const kanbanBoardStyles = css.create({\n board: { display: 'flex', gap: '_md', alignItems: 'flex-start', paddingBlockEnd: '_sm' },\n column: {\n display: 'flex',\n flexDirection: 'column',\n flexShrink: 0,\n borderRadius: '_lg',\n backgroundColor: '_surfaceMuted',\n outlineWidth: '_borderWidths.sm',\n outlineStyle: 'solid',\n outlineColor: 'transparent',\n outlineOffset: '-2px',\n transition: `outline-color ${t.durations.sm} ${t.easings.standard}`,\n },\n allowed: { outlineColor: '_accent' },\n denied: { outlineColor: '_danger' },\n header: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '_sm',\n paddingBlock: '_sm',\n paddingInline: '_md',\n fontWeight: '_semibold',\n fontSize: '_sm',\n },\n reason: { paddingInline: '_md', paddingBlockEnd: '_xs', color: '_dangerText', fontSize: '_xs' },\n list: {\n display: 'flex',\n flexDirection: 'column',\n gap: '_sm',\n margin: 0,\n paddingInline: '_sm',\n paddingBlockEnd: '_sm',\n listStyle: 'none',\n overflowY: 'auto',\n minHeight: '3rem',\n },\n card: {\n position: 'relative',\n display: 'grid',\n gridTemplateColumns: 'minmax(0, 1fr) auto',\n alignItems: 'start',\n gap: '_xs',\n padding: '_sm',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_border',\n borderRadius: '_md',\n backgroundColor: '_surfaceRaised',\n cursor: 'grab',\n touchAction: 'none',\n transition: `translate ${t.durations.md} ${t.easings.standard}`,\n },\n // 指针拖动中:原位的卡片变淡,跟着指针走的是影子卡(画在滚动区域外面,不被列裁掉)\n placeholder: { opacity: 0.4 },\n ghost: {\n position: 'fixed',\n zIndex: 50,\n pointerEvents: 'none',\n cursor: 'grabbing',\n boxShadow: '_lg',\n transition: 'none',\n rotate: '1.5deg',\n },\n // 键盘拿起:高亮边框\n lifted: { borderColor: '_accent', boxShadow: '_md' },\n handle: { color: '_textMuted', borderRadius: '_sm', padding: '2px', cursor: 'grab' },\n empty: { color: '_textMuted', fontSize: '_xs', paddingBlock: '_sm', textAlign: 'center' },\n // 拖到别的列时的落点:卡片之间的一条线\n indicator: {\n height: '_borderWidths.md',\n marginBlock: `calc(-1 * ${t.space.xs})`,\n borderRadius: '_full',\n backgroundColor: '_accent',\n },\n indicatorDenied: { backgroundColor: '_danger' },\n});\n\n/** KanbanBoard 的动态样式:列宽、列高、拖动的位移 */\nexport const kanbanBoardDynamicStyles = css.create({\n // 至少这么宽,富余的空间各列平分(同 Jira 的看板),放不下时外层横向滚动\n width: (width: string) => ({ flexBasis: width, flexGrow: 1, minWidth: width }),\n height: (height: string) => ({ maxHeight: height }),\n ghost: (left: number, top: number, width: number) => ({ left, top, width }),\n});\n\n/** 看板 */\nexport const KanbanBoard = _component(\n <T,>({\n columns,\n items,\n getKey,\n getColumn,\n getLabel,\n canMove,\n onMove,\n onReorder,\n onReject,\n columnWidth = '16rem',\n columnHeight = '70vh',\n empty,\n children,\n ...rest\n }: KanbanBoardProps<T>) => {\n const lang = useLang();\n const messages = useMessages('patterns', patternsMessages);\n let board = _state.raw<HTMLElement | null>(null);\n const labelOf = (item: T) => getLabel?.(item) ?? getKey(item);\n const itemOf = (key: string) => items.find((item) => getKey(item) === key);\n const sortable = useSortable({\n items: () => items,\n getKey: (item) => getKey(item),\n getLabel: labelOf,\n // 用时再读属性:写没写 onReorder 决定了列内能不能排序,父级后来才给也要跟上\n get onChange() {\n return onReorder && ((next: T[]) => onReorder(next));\n },\n container: () => board,\n groups: {\n keys: () => columns.map((column) => column.key),\n of: (item) => getColumn(item),\n label: (key) => columns.find((column) => column.key === key)?.label ?? key,\n canMove: (item, to) => canMove?.(item, to),\n onMove: (item, to, index) => onMove(item, to, index),\n onReject: (item, to, reason) => onReject?.(item, to, reason),\n },\n });\n\n /** 整张卡片都能拖:卡片里的链接、按钮(把手除外)、输入框照常点 */\n function onPointerdown(event: PointerEvent, key: string) {\n const target = event.target as Element;\n if (target.closest('a, input, select, textarea, button:not([data-handle])')) return;\n (sortable.handleProps(key).onPointerdown as (event: PointerEvent) => void)(event);\n }\n\n return () => {\n const dragging = sortable.active;\n const drop = sortable.target;\n const ghost = sortable.ghost;\n const ghostItem = ghost && itemOf(ghost.key);\n const draggedItem = dragging === undefined ? undefined : itemOf(dragging);\n const draggedFrom = draggedItem === undefined ? undefined : getColumn(draggedItem);\n return (\n <div {...mergeAttrs<'div'>({}, rest, { role: 'group' })}>\n <ScrollArea axis=\"x\">\n <div\n ref={(element: unknown) => (board = element instanceof HTMLElement ? element : null)}\n {...css.attrs(kanbanBoardStyles.board)}\n >\n {columns.map((column) => {\n const cards = items.filter((item) => getColumn(item) === column.key);\n // 拖到了别的列:高亮这一列,在落点画线\n const over = drop && draggedFrom !== column.key && drop.group === column.key;\n const reason = over ? drop.reason : null;\n const headingId = `${sortable.instructionsId}-${column.key}`;\n const indicator = (\n <li\n key=\"drop-indicator\"\n aria-hidden=\"true\"\n {...css.attrs(\n kanbanBoardStyles.indicator,\n reason != null && kanbanBoardStyles.indicatorDenied,\n )}\n />\n );\n return (\n <section\n key={column.key}\n aria-labelledby={headingId}\n {...sortable.groupProps(column.key)}\n {...css.attrs(\n kanbanBoardStyles.column,\n kanbanBoardDynamicStyles.width(columnWidth),\n over && (reason ? kanbanBoardStyles.denied : kanbanBoardStyles.allowed),\n )}\n >\n <div {...css.attrs(kanbanBoardStyles.header)}>\n <span id={headingId}>{column.label}</span>\n <Badge\n radius=\"full\"\n tone={cards.length > 0 ? (column.tone ?? 'neutral') : 'neutral'}\n >\n {cards.length}\n </Badge>\n </div>\n {reason && <div {...css.attrs(kanbanBoardStyles.reason)}>{reason}</div>}\n <ul\n {...css.attrs(\n kanbanBoardStyles.list,\n kanbanBoardDynamicStyles.height(columnHeight),\n )}\n >\n {cards.length === 0 && !over && (\n <li {...css.attrs(kanbanBoardStyles.empty)}>\n {empty ?? messages.kanbanEmpty}\n </li>\n )}\n {cards.flatMap((item, index) => {\n const key = getKey(item);\n const active = dragging === key;\n const { onPointerdown: _, ...handle } = sortable.handleProps(key);\n const card = (\n <li\n key={key}\n {...sortable.itemProps(key)}\n {...css.attrs(\n kanbanBoardStyles.card,\n active && ghost && kanbanBoardStyles.placeholder,\n active && !ghost && kanbanBoardStyles.lifted,\n )}\n data-state={active ? 'dragging' : undefined}\n onPointerdown={(event: PointerEvent) => onPointerdown(event, key)}\n >\n <div>{children(item, { dragging: active })}</div>\n <ButtonBase\n {...handle}\n data-handle=\"\"\n sx={kanbanBoardStyles.handle}\n aria-label={lang.sortableHandle(labelOf(item))}\n >\n <Icon icon={GripVertical} />\n </ButtonBase>\n </li>\n );\n return over && drop.index === index ? [indicator, card] : [card];\n })}\n {over && drop.index >= cards.length && indicator}\n </ul>\n </section>\n );\n })}\n </div>\n </ScrollArea>\n {ghost && ghostItem !== undefined && (\n <div\n aria-hidden=\"true\"\n {...css.attrs(\n kanbanBoardStyles.card,\n kanbanBoardStyles.ghost,\n kanbanBoardDynamicStyles.ghost(ghost.left, ghost.top, ghost.width),\n )}\n >\n <div>{children(ghostItem, { dragging: true })}</div>\n </div>\n )}\n <VisuallyHidden id={sortable.instructionsId}>\n {messages.kanbanInstructions}\n </VisuallyHidden>\n <VisuallyHidden aria-live=\"assertive\">{sortable.announcement}</VisuallyHidden>\n </div>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type IconButtonProps } from '../base/IconButton';
|
|
2
|
+
import { type MenuProps } from '../overlay/Menu';
|
|
3
|
+
import { type LayoutPreference } from '../config/layout/preference';
|
|
4
|
+
/** 未声明的属性落到菜单(role="menu" 的 div)上 */
|
|
5
|
+
export interface LayoutSwitcherProps extends Omit<MenuProps, 'trigger' | 'children'> {
|
|
6
|
+
/** createLayoutPreference 的结果 */
|
|
7
|
+
layout: LayoutPreference;
|
|
8
|
+
/** 触发按钮的部件属性 */
|
|
9
|
+
propsTrigger?: Partial<IconButtonProps>;
|
|
10
|
+
}
|
|
11
|
+
/** 布局切换(图标按钮 + 两组单选菜单) */
|
|
12
|
+
export declare const LayoutSwitcher: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<LayoutSwitcherProps>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { mergePartProps } from "../utils/props.js";
|
|
2
|
+
import { IconButton } from "../base/IconButton.js";
|
|
3
|
+
import { Menu as Menu$1, MenuGroup, MenuRadioGroup, MenuRadioItem } from "../overlay/Menu.js";
|
|
4
|
+
import { LAYOUT_MODES, LAYOUT_SIDES } from "../config/layout/preference.js";
|
|
5
|
+
import { useMessages } from "../config/lang/messages.js";
|
|
6
|
+
import { patternsMessages } from "./messages.js";
|
|
7
|
+
import { createVNode, isVNode } from "vue";
|
|
8
|
+
import { _component, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
9
|
+
import { Columns2, PanelBottom, PanelLeft, PanelRight, PanelTop } from "@lucide/icons";
|
|
10
|
+
//#region src/patterns/LayoutSwitcher.tsx
|
|
11
|
+
/**
|
|
12
|
+
* 布局切换:图标按钮弹出菜单,两组单选——侧栏的形式(固定 / 抽屉)和抽屉从哪一边滑出(左 / 右 / 上 / 下),
|
|
13
|
+
* 配合 createLayoutPreference 与 AppShell 的 layout 使用,和 ThemeSwitcher 并排放在顶栏里。
|
|
14
|
+
*
|
|
15
|
+
* const layout = createLayoutPreference({ defaults: { mode: 'overlay' } });
|
|
16
|
+
* <AppShell layout={layout} header={<><LayoutSwitcher layout={layout} /><ThemeSwitcher preference={theme} /></>} …>
|
|
17
|
+
*
|
|
18
|
+
* 选了“固定”时抽屉位置不可选(固定侧栏总是在起始边)。触发按钮的图标表示当前的位置。
|
|
19
|
+
*/
|
|
20
|
+
/** 未声明的属性落到菜单(role="menu" 的 div)上 */
|
|
21
|
+
function _isSlot(s) {
|
|
22
|
+
return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !isVNode(s);
|
|
23
|
+
}
|
|
24
|
+
var SIDE_ICONS = {
|
|
25
|
+
start: PanelLeft,
|
|
26
|
+
end: PanelRight,
|
|
27
|
+
top: PanelTop,
|
|
28
|
+
bottom: PanelBottom
|
|
29
|
+
};
|
|
30
|
+
/** 布局切换(图标按钮 + 两组单选菜单) */
|
|
31
|
+
var LayoutSwitcher = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
32
|
+
const _cache = [];
|
|
33
|
+
const layout = readonlyProp(_props, "layout");
|
|
34
|
+
const propsTrigger = readonlyProp(_props, "propsTrigger");
|
|
35
|
+
const rest = restProps(_props, _ctx, true);
|
|
36
|
+
const messages = useMessages("patterns", patternsMessages);
|
|
37
|
+
const modeLabels = {
|
|
38
|
+
docked: messages.layoutModeDocked,
|
|
39
|
+
overlay: messages.layoutModeOverlay
|
|
40
|
+
};
|
|
41
|
+
const sideLabels = {
|
|
42
|
+
start: messages.layoutSideStart,
|
|
43
|
+
end: messages.layoutSideEnd,
|
|
44
|
+
top: messages.layoutSideTop,
|
|
45
|
+
bottom: messages.layoutSideBottom
|
|
46
|
+
};
|
|
47
|
+
const triggerIcon = () => layout.value.mode === "overlay" ? SIDE_ICONS[layout.value.side] : Columns2;
|
|
48
|
+
return () => {
|
|
49
|
+
let _slot, _slot2;
|
|
50
|
+
return createVNode(Menu$1, mergeJsxProps(rest, { "trigger": _cache[0] || (_cache[0] = (props) => createVNode(IconButton, mergePartProps({
|
|
51
|
+
icon: triggerIcon(),
|
|
52
|
+
"aria-label": messages.layoutLabel
|
|
53
|
+
}, propsTrigger.value, props), null)) }), { default: () => [createVNode(MenuGroup, { "label": messages.layoutMode }, { default: () => [createVNode(MenuRadioGroup, {
|
|
54
|
+
"_value": layout.value.mode,
|
|
55
|
+
"onUpdate:_value": _cache[1] || (_cache[1] = (value) => layout.value.mode = value),
|
|
56
|
+
"aria-label": messages.layoutMode
|
|
57
|
+
}, _isSlot(_slot = LAYOUT_MODES.map((mode) => createVNode(MenuRadioItem, {
|
|
58
|
+
"key": mode,
|
|
59
|
+
"value": mode,
|
|
60
|
+
"icon": mode === "overlay" ? PanelLeft : Columns2
|
|
61
|
+
}, { default: () => [modeLabels[mode]] }))) ? _slot : { default: () => [_slot] })] }), createVNode(MenuGroup, { "label": messages.layoutSide }, { default: () => [createVNode(MenuRadioGroup, {
|
|
62
|
+
"_value": layout.value.side,
|
|
63
|
+
"onUpdate:_value": _cache[2] || (_cache[2] = (value) => layout.value.side = value),
|
|
64
|
+
"aria-label": messages.layoutSide
|
|
65
|
+
}, _isSlot(_slot2 = LAYOUT_SIDES.map((side) => createVNode(MenuRadioItem, {
|
|
66
|
+
"key": side,
|
|
67
|
+
"value": side,
|
|
68
|
+
"icon": SIDE_ICONS[side],
|
|
69
|
+
"disabled": layout.value.mode !== "overlay"
|
|
70
|
+
}, { default: () => [sideLabels[side]] }))) ? _slot2 : { default: () => [_slot2] })] })] });
|
|
71
|
+
};
|
|
72
|
+
}, {
|
|
73
|
+
name: "LayoutSwitcher",
|
|
74
|
+
props: ["layout", "propsTrigger"]
|
|
75
|
+
});
|
|
76
|
+
//#endregion
|
|
77
|
+
export { LayoutSwitcher };
|
|
78
|
+
|
|
79
|
+
//# sourceMappingURL=LayoutSwitcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LayoutSwitcher.js","names":["_isVNode","_vtp_readonlyProp","_vtp_restProps","_createVNode","Menu","_mergeProps"],"sources":["../../src/patterns/LayoutSwitcher.tsx"],"sourcesContent":["/**\n * 布局切换:图标按钮弹出菜单,两组单选——侧栏的形式(固定 / 抽屉)和抽屉从哪一边滑出(左 / 右 / 上 / 下),\n * 配合 createLayoutPreference 与 AppShell 的 layout 使用,和 ThemeSwitcher 并排放在顶栏里。\n *\n * const layout = createLayoutPreference({ defaults: { mode: 'overlay' } });\n * <AppShell layout={layout} header={<><LayoutSwitcher layout={layout} /><ThemeSwitcher preference={theme} /></>} …>\n *\n * 选了“固定”时抽屉位置不可选(固定侧栏总是在起始边)。触发按钮的图标表示当前的位置。\n */\nimport { Columns2, PanelBottom, PanelLeft, PanelRight, PanelTop } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { IconButton, type IconButtonProps } from '../base/IconButton';\nimport { useMessages } from '../config/lang/messages';\nimport { patternsMessages } from './messages';\nimport type { IconData } from '../base/Icon';\nimport { Menu, MenuGroup, MenuRadioGroup, MenuRadioItem, type MenuProps } from '../overlay/Menu';\nimport {\n LAYOUT_MODES,\n LAYOUT_SIDES,\n type LayoutMode,\n type LayoutPreference,\n type LayoutSide,\n} from '../config/layout/preference';\nimport { mergePartProps } from '../utils/props';\n\n/** 未声明的属性落到菜单(role=\"menu\" 的 div)上 */\nexport interface LayoutSwitcherProps extends Omit<MenuProps, 'trigger' | 'children'> {\n /** createLayoutPreference 的结果 */\n layout: LayoutPreference;\n /** 触发按钮的部件属性 */\n propsTrigger?: Partial<IconButtonProps>;\n}\n\nconst SIDE_ICONS: Record<LayoutSide, IconData> = {\n start: PanelLeft,\n end: PanelRight,\n top: PanelTop,\n bottom: PanelBottom,\n};\n\n/** 布局切换(图标按钮 + 两组单选菜单) */\nexport const LayoutSwitcher = _component(\n ({ layout, propsTrigger, ...rest }: LayoutSwitcherProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const modeLabels: Record<LayoutMode, string> = {\n docked: messages.layoutModeDocked,\n overlay: messages.layoutModeOverlay,\n };\n const sideLabels: Record<LayoutSide, string> = {\n start: messages.layoutSideStart,\n end: messages.layoutSideEnd,\n top: messages.layoutSideTop,\n bottom: messages.layoutSideBottom,\n };\n // 触发按钮:抽屉时显示它的方向,固定时显示“两栏”\n const triggerIcon = () => (layout.mode === 'overlay' ? SIDE_ICONS[layout.side] : Columns2);\n\n return () => (\n <Menu\n {...rest}\n trigger={(props) => (\n <IconButton\n {...mergePartProps<IconButtonProps>(\n { icon: triggerIcon(), 'aria-label': messages.layoutLabel },\n propsTrigger,\n props,\n )}\n />\n )}\n >\n <MenuGroup label={messages.layoutMode}>\n <MenuRadioGroup\n _value={layout.mode}\n onUpdate:_value={(value: string) => (layout.mode = value as LayoutMode)}\n aria-label={messages.layoutMode}\n >\n {LAYOUT_MODES.map((mode) => (\n <MenuRadioItem\n key={mode}\n value={mode}\n icon={mode === 'overlay' ? PanelLeft : Columns2}\n >\n {modeLabels[mode]}\n </MenuRadioItem>\n ))}\n </MenuRadioGroup>\n </MenuGroup>\n <MenuGroup label={messages.layoutSide}>\n <MenuRadioGroup\n _value={layout.side}\n onUpdate:_value={(value: string) => (layout.side = value as LayoutSide)}\n aria-label={messages.layoutSide}\n >\n {LAYOUT_SIDES.map((side) => (\n <MenuRadioItem\n key={side}\n value={side}\n icon={SIDE_ICONS[side]}\n disabled={layout.mode !== 'overlay'}\n >\n {sideLabels[side]}\n </MenuRadioItem>\n ))}\n </MenuRadioGroup>\n </MenuGroup>\n </Menu>\n );\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAyBA,SAAA,QAAA,GAAA;CAAA,OAAA,OAAA,MAAA,cAAA,OAAA,UAAA,SAAA,KAAA,CAAA,MAAA,qBAAA,CAAAA,QAAA,CAAA;AAAA;AAQA,IAAM,aAA2C;CAC/C,OAAO;CACP,KAAK;CACL,KAAK;CACL,QAAQ;AACV;;AAGA,IAAa,iBAAiB,0BAC5B,QAAA,SAA4D;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,SAAAC,aAAA,QAAA,QAAA;CAAA,MAAA,eAAAA,aAAA,QAAA,cAAA;CAAA,MAAhC,OAAIC,UAAA,QAAA,MAAA,IAAA;CAC9B,MAAM,WAAW,YAAY,YAAY,gBAAgB;CACzD,MAAM,aAAyC;EAC7C,QAAQ,SAAS;EACjB,SAAS,SAAS;CACpB;CACA,MAAM,aAAyC;EAC7C,OAAO,SAAS;EAChB,KAAK,SAAS;EACd,KAAK,SAAS;EACd,QAAQ,SAAS;CACnB;CAEA,MAAM,oBAAqB,OAAA,MAAO,SAAS,YAAY,WAAW,OAAA,MAAO,QAAQ;CAEjF,aAAO;EAAA,IAAA,OAAA;EAAA,OAAAC,YAAAC,QAAAC,cAEC,MAAI,EAAA,WAAA,OAAA,OAAA,OAAA,MACE,UAAKF,YAAA,YAEP,eACF;GAAE,MAAM,YAAY;GAAG,cAAc,SAAS;EAAY,GAAC,aAAA,OAE3D,KACF,GAAC,IAAA,GAEJ,CAAA,GAAA,EAAA,eAAA,CAAAA,YAAA,WAAA,EAAA,SAEiB,SAAS,WAAU,GAAA,EAAA,eAAA,CAAAA,YAAA,gBAAA;GAAA,UAEzB,OAAA,MAAO;GAAI,mBAAA,OAAA,OAAA,OAAA,MACD,UAAmB,OAAA,MAAO,OAAO;GAAoB,cAC3D,SAAS;EAAU,GAAA,QAAA,QAE9B,aAAa,KAAK,SAAIA,YAAA,eAAA;GAAA,OAEd;GAAI,SACF;GAAI,QACL,SAAS,YAAY,YAAY;EAAQ,GAAA,EAAA,eAAA,CAE9C,WAAW,KAAK,EAAA,CAAA,CAEpB,CAAC,IAAA,QAAA,EAAA,eAAA,CAAA,KAAA,EAAA,CAAA,CAAA,EAAA,CAAA,GAAAA,YAAA,WAAA,EAAA,SAGY,SAAS,WAAU,GAAA,EAAA,eAAA,CAAAA,YAAA,gBAAA;GAAA,UAEzB,OAAA,MAAO;GAAI,mBAAA,OAAA,OAAA,OAAA,MACD,UAAmB,OAAA,MAAO,OAAO;GAAoB,cAC3D,SAAS;EAAU,GAAA,QAAA,SAE9B,aAAa,KAAK,SAAIA,YAAA,eAAA;GAAA,OAEd;GAAI,SACF;GAAI,QACL,WAAW;GAAK,YACZ,OAAA,MAAO,SAAS;EAAS,GAAA,EAAA,eAAA,CAElC,WAAW,KAAK,EAAA,CAAA,CAEpB,CAAC,IAAA,SAAA,EAAA,eAAA,CAAA,MAAA,EAAA,CAAA,CAAA,EAAA,CAAA,CAAA,EAAA,CAAA;CAAA;AAKZ,GAAC;CAAA,MAAA;CAAA,OAAA,CAAA,UAAA,cAAA;AAAA,CACH"}
|