@zerodep-dev/vue-tsx-pro-ui 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base/Badge.js +3 -3
- package/dist/base/Badge.js.map +1 -1
- package/dist/base/Can.d.ts +12 -0
- package/dist/base/Can.js +20 -0
- package/dist/base/Can.js.map +1 -0
- package/dist/base/ClientOnly.d.ts +10 -0
- package/dist/base/ClientOnly.js +18 -0
- package/dist/base/ClientOnly.js.map +1 -0
- package/dist/base/CloseButton.js +3 -3
- package/dist/base/CloseButton.js.map +1 -1
- package/dist/base/CodeBlock.js +5 -5
- package/dist/base/CodeBlock.js.map +1 -1
- package/dist/base/CopyButton.js +3 -3
- package/dist/base/CopyButton.js.map +1 -1
- package/dist/base/Icon.js +3 -3
- package/dist/base/Icon.js.map +1 -1
- package/dist/base/Indicator.js +3 -3
- package/dist/base/Indicator.js.map +1 -1
- package/dist/base/StatusDot.js +3 -3
- package/dist/base/StatusDot.js.map +1 -1
- package/dist/base/Tag.js +5 -5
- package/dist/base/Tag.js.map +1 -1
- package/dist/base/button-core.js +4 -4
- package/dist/base/button-core.js.map +1 -1
- package/dist/charts/AreaChart.js +3 -3
- package/dist/charts/AreaChart.js.map +1 -1
- package/dist/charts/BarChart.js +3 -3
- package/dist/charts/BarChart.js.map +1 -1
- package/dist/charts/ChordChart.js +3 -3
- package/dist/charts/ChordChart.js.map +1 -1
- package/dist/charts/ComboChart.js +3 -3
- package/dist/charts/ComboChart.js.map +1 -1
- package/dist/charts/DonutChart.js +3 -3
- package/dist/charts/DonutChart.js.map +1 -1
- package/dist/charts/FunnelChart.js +5 -5
- package/dist/charts/FunnelChart.js.map +1 -1
- package/dist/charts/GraphChart.js +3 -3
- package/dist/charts/GraphChart.js.map +1 -1
- package/dist/charts/LineChart.js +3 -3
- package/dist/charts/LineChart.js.map +1 -1
- package/dist/charts/MapChart.js +7 -7
- package/dist/charts/MapChart.js.map +1 -1
- package/dist/charts/RadarChart.js +16 -15
- package/dist/charts/RadarChart.js.map +1 -1
- package/dist/charts/SankeyChart.js +3 -3
- package/dist/charts/SankeyChart.js.map +1 -1
- package/dist/charts/ScatterChart.js +3 -3
- package/dist/charts/ScatterChart.js.map +1 -1
- package/dist/charts/TimelineChart.js +6 -5
- package/dist/charts/TimelineChart.js.map +1 -1
- package/dist/charts/TreemapChart.js +3 -3
- package/dist/charts/TreemapChart.js.map +1 -1
- package/dist/charts/WaterfallChart.js +3 -3
- package/dist/charts/WaterfallChart.js.map +1 -1
- package/dist/charts/XYChart.js +9 -7
- package/dist/charts/XYChart.js.map +1 -1
- package/dist/charts/frame.js +10 -8
- package/dist/charts/frame.js.map +1 -1
- package/dist/charts/single.js +4 -3
- package/dist/charts/single.js.map +1 -1
- package/dist/config/Provider.d.ts +3 -1
- package/dist/config/Provider.js +9 -4
- package/dist/config/Provider.js.map +1 -1
- package/dist/config/context.d.ts +18 -0
- package/dist/config/context.js +16 -1
- package/dist/config/context.js.map +1 -1
- package/dist/config/createUi.d.ts +20 -3
- package/dist/config/createUi.js +80 -49
- package/dist/config/createUi.js.map +1 -1
- package/dist/config/layout/preference.d.ts +43 -0
- package/dist/config/layout/preference.js +108 -0
- package/dist/config/layout/preference.js.map +1 -0
- package/dist/date/Calendar.js +3 -3
- package/dist/date/Calendar.js.map +1 -1
- package/dist/date/CalendarBoard.js +9 -9
- package/dist/date/CalendarBoard.js.map +1 -1
- package/dist/date/CalendarView.js +7 -7
- package/dist/date/CalendarView.js.map +1 -1
- package/dist/date/DateField.js +3 -3
- package/dist/date/DateField.js.map +1 -1
- package/dist/date/DatePicker.js +5 -5
- package/dist/date/DatePicker.js.map +1 -1
- package/dist/date/DateRangePicker.js +5 -5
- package/dist/date/DateRangePicker.js.map +1 -1
- package/dist/date/DateSegmentGroup.js +5 -5
- package/dist/date/DateSegmentGroup.js.map +1 -1
- package/dist/date/QuarterCalendar.js +6 -6
- package/dist/date/QuarterCalendar.js.map +1 -1
- package/dist/date/RangeCalendar.js +3 -3
- package/dist/date/RangeCalendar.js.map +1 -1
- package/dist/date/SegmentedField.js +3 -3
- package/dist/date/SegmentedField.js.map +1 -1
- package/dist/date/TimeField.js +3 -3
- package/dist/date/TimeField.js.map +1 -1
- package/dist/date/TimePicker.js +4 -4
- package/dist/date/TimePicker.js.map +1 -1
- package/dist/date/date-segments.js +1 -1
- package/dist/date/date-segments.js.map +1 -1
- package/dist/date/picker-popover.js +3 -2
- package/dist/date/picker-popover.js.map +1 -1
- package/dist/diagram/Diagram.js +12 -12
- package/dist/diagram/Diagram.js.map +1 -1
- package/dist/diagram/DiagramLanes.js +3 -3
- package/dist/diagram/DiagramLanes.js.map +1 -1
- package/dist/diagram/DiagramMinimap.js +4 -4
- package/dist/diagram/DiagramMinimap.js.map +1 -1
- package/dist/display/Accordion.js +6 -6
- package/dist/display/Accordion.js.map +1 -1
- package/dist/display/AnimatedNumber.js +4 -4
- package/dist/display/AnimatedNumber.js.map +1 -1
- package/dist/display/Avatar.js +4 -4
- package/dist/display/Avatar.js.map +1 -1
- package/dist/display/Checklist.js +7 -7
- package/dist/display/Checklist.js.map +1 -1
- package/dist/display/Collapse.js +8 -5
- package/dist/display/Collapse.js.map +1 -1
- package/dist/display/Collapsible.js +3 -3
- package/dist/display/Collapsible.js.map +1 -1
- package/dist/display/Descriptions.js +5 -5
- package/dist/display/Descriptions.js.map +1 -1
- package/dist/display/Ellipsis.js +3 -3
- package/dist/display/Ellipsis.js.map +1 -1
- package/dist/display/FieldCompare.js +4 -4
- package/dist/display/FieldCompare.js.map +1 -1
- package/dist/display/Gauge.js +11 -11
- package/dist/display/Gauge.js.map +1 -1
- package/dist/display/Image.js +4 -4
- package/dist/display/Image.js.map +1 -1
- package/dist/display/Marquee.js +3 -3
- package/dist/display/Marquee.js.map +1 -1
- package/dist/display/ProportionBar.js +4 -4
- package/dist/display/ProportionBar.js.map +1 -1
- package/dist/display/Sortable.js +4 -4
- package/dist/display/Sortable.js.map +1 -1
- package/dist/display/Sparkline.js +6 -6
- package/dist/display/Sparkline.js.map +1 -1
- package/dist/display/Stat.js +3 -3
- package/dist/display/Stat.js.map +1 -1
- package/dist/display/Timeline.js +7 -7
- package/dist/display/Timeline.js.map +1 -1
- package/dist/display/Tree.js +10 -10
- package/dist/display/Tree.js.map +1 -1
- package/dist/display/VirtualList.js +7 -7
- package/dist/display/VirtualList.js.map +1 -1
- package/dist/display/Watermark.js +3 -3
- package/dist/display/Watermark.js.map +1 -1
- package/dist/docs-kit/CodeGroup.js +5 -5
- package/dist/docs-kit/CodeGroup.js.map +1 -1
- package/dist/docs-kit/CodePreview.js +6 -6
- package/dist/docs-kit/CodePreview.js.map +1 -1
- package/dist/docs-kit/PropsTable.js +4 -4
- package/dist/docs-kit/PropsTable.js.map +1 -1
- package/dist/feedback/Alert.js +5 -5
- package/dist/feedback/Alert.js.map +1 -1
- package/dist/feedback/Banner.js +5 -5
- package/dist/feedback/Banner.js.map +1 -1
- package/dist/feedback/Empty.js +4 -4
- package/dist/feedback/Empty.js.map +1 -1
- package/dist/feedback/LoadingBar.js +20 -13
- package/dist/feedback/LoadingBar.js.map +1 -1
- package/dist/feedback/LoadingOverlay.js +3 -3
- package/dist/feedback/LoadingOverlay.js.map +1 -1
- package/dist/feedback/Progress.js +14 -14
- package/dist/feedback/Progress.js.map +1 -1
- package/dist/feedback/Result.js +4 -4
- package/dist/feedback/Result.js.map +1 -1
- package/dist/feedback/SkeletonText.js +3 -3
- package/dist/feedback/SkeletonText.js.map +1 -1
- package/dist/feedback/Toast.js +6 -6
- package/dist/feedback/Toast.js.map +1 -1
- package/dist/form/Field.js +7 -7
- package/dist/form/Field.js.map +1 -1
- package/dist/form/Form.js +3 -3
- package/dist/form/Form.js.map +1 -1
- package/dist/form/FormErrorSummary.js +3 -3
- package/dist/form/FormErrorSummary.js.map +1 -1
- package/dist/form/FormSection.js +6 -6
- package/dist/form/FormSection.js.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.js +10 -5
- package/dist/input/Autocomplete.js +3 -3
- package/dist/input/Autocomplete.js.map +1 -1
- package/dist/input/Cascader.js +7 -7
- package/dist/input/Cascader.js.map +1 -1
- package/dist/input/CharacterCount.js +3 -3
- package/dist/input/CharacterCount.js.map +1 -1
- package/dist/input/Checkbox.js +5 -5
- package/dist/input/Checkbox.js.map +1 -1
- package/dist/input/ColorField.js +4 -4
- package/dist/input/ColorField.js.map +1 -1
- package/dist/input/Combobox.js +4 -4
- package/dist/input/Combobox.js.map +1 -1
- package/dist/input/ConditionBuilder.js +15 -14
- package/dist/input/ConditionBuilder.js.map +1 -1
- package/dist/input/FileDrop.js +3 -3
- package/dist/input/FileDrop.js.map +1 -1
- package/dist/input/Listbox.js +9 -9
- package/dist/input/Listbox.js.map +1 -1
- package/dist/input/Mentions.js +3 -3
- package/dist/input/Mentions.js.map +1 -1
- package/dist/input/NumberField.js +3 -3
- package/dist/input/NumberField.js.map +1 -1
- package/dist/input/NumberRangeField.js +5 -5
- package/dist/input/NumberRangeField.js.map +1 -1
- package/dist/input/OtpField.js +5 -5
- package/dist/input/OtpField.js.map +1 -1
- package/dist/input/PeoplePicker.js +6 -6
- package/dist/input/PeoplePicker.js.map +1 -1
- package/dist/input/PeoplePickerPanel.js +4 -4
- package/dist/input/PeoplePickerPanel.js.map +1 -1
- package/dist/input/Radio.js +4 -4
- package/dist/input/Radio.js.map +1 -1
- package/dist/input/Rating.js +5 -5
- package/dist/input/Rating.js.map +1 -1
- package/dist/input/Select.js +4 -4
- package/dist/input/Select.js.map +1 -1
- package/dist/input/SelectedTags.js +4 -4
- package/dist/input/SelectedTags.js.map +1 -1
- package/dist/input/Slider.js +6 -6
- package/dist/input/Slider.js.map +1 -1
- package/dist/input/Switch.js +3 -3
- package/dist/input/Switch.js.map +1 -1
- package/dist/input/TagsInput.js +5 -5
- package/dist/input/TagsInput.js.map +1 -1
- package/dist/input/TextField.js +5 -5
- package/dist/input/TextField.js.map +1 -1
- package/dist/input/Textarea.js +4 -4
- package/dist/input/Textarea.js.map +1 -1
- package/dist/input/ToggleGroup.js +4 -4
- package/dist/input/ToggleGroup.js.map +1 -1
- package/dist/input/Transfer.js +8 -8
- package/dist/input/Transfer.js.map +1 -1
- package/dist/input/TreeSelect.js +6 -6
- package/dist/input/TreeSelect.js.map +1 -1
- package/dist/input/Upload.js +8 -8
- package/dist/input/Upload.js.map +1 -1
- package/dist/layout/Affix.js +4 -4
- package/dist/layout/Affix.js.map +1 -1
- package/dist/layout/ResizeHandle.d.ts +6 -2
- package/dist/layout/ResizeHandle.js +32 -9
- package/dist/layout/ResizeHandle.js.map +1 -1
- package/dist/layout/ScrollArea.js +3 -3
- package/dist/layout/ScrollArea.js.map +1 -1
- package/dist/layout/Separator.js +3 -3
- package/dist/layout/Separator.js.map +1 -1
- package/dist/layout/Splitter.js +3 -3
- package/dist/layout/Splitter.js.map +1 -1
- package/dist/layout/ViewportFill.d.ts +2 -2
- package/dist/layout/ViewportFill.js +4 -4
- package/dist/layout/ViewportFill.js.map +1 -1
- package/dist/navigation/Breadcrumbs.js +4 -4
- package/dist/navigation/Breadcrumbs.js.map +1 -1
- package/dist/navigation/NavBar.js +6 -6
- package/dist/navigation/NavBar.js.map +1 -1
- package/dist/navigation/NavList.d.ts +1 -0
- package/dist/navigation/NavList.js +20 -12
- package/dist/navigation/NavList.js.map +1 -1
- package/dist/navigation/NavMenu.d.ts +27 -0
- package/dist/navigation/NavMenu.js +54 -0
- package/dist/navigation/NavMenu.js.map +1 -0
- package/dist/navigation/PageJump.js +3 -3
- package/dist/navigation/PageJump.js.map +1 -1
- package/dist/navigation/PageSizeSelect.js +4 -4
- package/dist/navigation/PageSizeSelect.js.map +1 -1
- package/dist/navigation/Pagination.js +6 -6
- package/dist/navigation/Pagination.js.map +1 -1
- package/dist/navigation/PaginationBar.js +5 -5
- package/dist/navigation/PaginationBar.js.map +1 -1
- package/dist/navigation/Steps.js +4 -4
- package/dist/navigation/Steps.js.map +1 -1
- package/dist/navigation/TableOfContents.js +5 -5
- package/dist/navigation/TableOfContents.js.map +1 -1
- package/dist/navigation/Tabs.js +7 -7
- package/dist/navigation/Tabs.js.map +1 -1
- package/dist/navigation/Toolbar.js +3 -3
- package/dist/navigation/Toolbar.js.map +1 -1
- package/dist/overlay/AlertDialog.js +4 -4
- package/dist/overlay/AlertDialog.js.map +1 -1
- package/dist/overlay/CommandPalette.js +4 -4
- package/dist/overlay/CommandPalette.js.map +1 -1
- package/dist/overlay/ContextMenu.js +3 -3
- package/dist/overlay/ContextMenu.js.map +1 -1
- package/dist/overlay/Dialog.js +4 -4
- package/dist/overlay/Dialog.js.map +1 -1
- package/dist/overlay/Drawer.js +5 -5
- package/dist/overlay/Drawer.js.map +1 -1
- package/dist/overlay/HoverCard.js +5 -3
- package/dist/overlay/HoverCard.js.map +1 -1
- package/dist/overlay/Lightbox.js +5 -5
- package/dist/overlay/Lightbox.js.map +1 -1
- package/dist/overlay/Menu.js +7 -7
- package/dist/overlay/Menu.js.map +1 -1
- package/dist/overlay/ModalCore.js +9 -9
- package/dist/overlay/ModalCore.js.map +1 -1
- package/dist/overlay/Popconfirm.js +6 -6
- package/dist/overlay/Popconfirm.js.map +1 -1
- package/dist/overlay/Popover.js +5 -3
- package/dist/overlay/Popover.js.map +1 -1
- package/dist/overlay/Tooltip.js +5 -3
- package/dist/overlay/Tooltip.js.map +1 -1
- package/dist/overlay/Tour.js +8 -8
- package/dist/overlay/Tour.js.map +1 -1
- package/dist/overlay/detached.d.ts +2 -0
- package/dist/overlay/detached.js +73 -42
- package/dist/overlay/detached.js.map +1 -1
- package/dist/patterns/ActionBar.js +3 -3
- package/dist/patterns/ActionBar.js.map +1 -1
- package/dist/patterns/AppShell.d.ts +28 -1
- package/dist/patterns/AppShell.js +158 -52
- package/dist/patterns/AppShell.js.map +1 -1
- package/dist/patterns/AuthLayout.d.ts +28 -0
- package/dist/patterns/AuthLayout.js +77 -0
- package/dist/patterns/AuthLayout.js.map +1 -0
- package/dist/patterns/CommentThread.js +9 -9
- package/dist/patterns/CommentThread.js.map +1 -1
- package/dist/patterns/DataImport.js +4 -4
- package/dist/patterns/DataImport.js.map +1 -1
- package/dist/patterns/DeadlineBadge.js +3 -3
- package/dist/patterns/DeadlineBadge.js.map +1 -1
- package/dist/patterns/FilterBar.d.ts +11 -1
- package/dist/patterns/FilterBar.js +39 -10
- package/dist/patterns/FilterBar.js.map +1 -1
- package/dist/patterns/FormDialog.d.ts +18 -0
- package/dist/patterns/FormDialog.js +46 -0
- package/dist/patterns/FormDialog.js.map +1 -0
- package/dist/patterns/KanbanBoard.js +8 -8
- package/dist/patterns/KanbanBoard.js.map +1 -1
- package/dist/patterns/LayoutSwitcher.d.ts +12 -0
- package/dist/patterns/LayoutSwitcher.js +79 -0
- package/dist/patterns/LayoutSwitcher.js.map +1 -0
- package/dist/patterns/NotificationCenter.js +8 -8
- package/dist/patterns/NotificationCenter.js.map +1 -1
- package/dist/patterns/PageTabs.d.ts +12 -7
- package/dist/patterns/PageTabs.js +41 -29
- package/dist/patterns/PageTabs.js.map +1 -1
- package/dist/patterns/QueryTable.d.ts +7 -1
- package/dist/patterns/QueryTable.js +116 -81
- package/dist/patterns/QueryTable.js.map +1 -1
- package/dist/patterns/SlideCaptcha.d.ts +80 -0
- package/dist/patterns/SlideCaptcha.js +354 -0
- package/dist/patterns/SlideCaptcha.js.map +1 -0
- package/dist/patterns/StepsForm.js +7 -7
- package/dist/patterns/StepsForm.js.map +1 -1
- package/dist/patterns/ThemeSwitcher.js +3 -3
- package/dist/patterns/ThemeSwitcher.js.map +1 -1
- package/dist/patterns/index.d.ts +7 -3
- package/dist/patterns/index.js +5 -1
- package/dist/patterns/messages.d.ts +37 -1
- package/dist/patterns/messages.js +38 -2
- package/dist/patterns/messages.js.map +1 -1
- package/dist/table/ColumnHeaderActions.js +3 -3
- package/dist/table/ColumnHeaderActions.js.map +1 -1
- package/dist/table/ColumnResizer.js +3 -3
- package/dist/table/ColumnResizer.js.map +1 -1
- package/dist/table/ColumnSettings.js +5 -5
- package/dist/table/ColumnSettings.js.map +1 -1
- package/dist/table/ColumnTable.js +8 -8
- package/dist/table/ColumnTable.js.map +1 -1
- package/dist/table/DataTable.js +8 -8
- package/dist/table/DataTable.js.map +1 -1
- package/dist/table/DataTableView.js +21 -21
- package/dist/table/DataTableView.js.map +1 -1
- package/dist/table/PivotTable.js +3 -3
- package/dist/table/PivotTable.js.map +1 -1
- package/dist/table/SelectionBar.js +4 -4
- package/dist/table/SelectionBar.js.map +1 -1
- package/dist/table/TreeDataTable.js +5 -5
- package/dist/table/TreeDataTable.js.map +1 -1
- package/dist/theme-customize/ThemeCustomizer.js +7 -6
- package/dist/theme-customize/ThemeCustomizer.js.map +1 -1
- package/dist/theme-customize/ThemeThumbnail.js +3 -3
- package/dist/theme-customize/ThemeThumbnail.js.map +1 -1
- package/dist/three/background/AuroraBackground.js +3 -3
- package/dist/three/background/AuroraBackground.js.map +1 -1
- package/dist/three/background/MeshGradient.js +3 -3
- package/dist/three/background/MeshGradient.js.map +1 -1
- package/dist/three/background/ShaderBackground.js +3 -3
- package/dist/three/background/ShaderBackground.js.map +1 -1
- package/dist/three/core/EffectSurface.js +3 -3
- package/dist/three/core/EffectSurface.js.map +1 -1
- package/dist/three/core/ThreeCanvas.js +4 -4
- package/dist/three/core/ThreeCanvas.js.map +1 -1
- package/dist/three/effects/Confetti.js +3 -3
- package/dist/three/effects/Confetti.js.map +1 -1
- package/dist/three/effects/ParticleField.js +3 -3
- package/dist/three/effects/ParticleField.js.map +1 -1
- package/dist/three/effects/WaveGrid.js +3 -3
- package/dist/three/effects/WaveGrid.js.map +1 -1
- package/dist/three/globe/Globe.js +3 -3
- package/dist/three/globe/Globe.js.map +1 -1
- package/dist/three/model/ModelViewer.js +3 -3
- package/dist/three/model/ModelViewer.js.map +1 -1
- package/dist/utils/data/number.d.ts +5 -0
- package/dist/utils/data/number.js +8 -1
- package/dist/utils/data/number.js.map +1 -1
- package/dist/utils/dom/element.d.ts +3 -1
- package/dist/utils/dom/element.js +8 -4
- package/dist/utils/dom/element.js.map +1 -1
- package/dist/utils/dom/fullscreen.d.ts +1 -1
- package/dist/utils/dom/fullscreen.js +12 -8
- package/dist/utils/dom/fullscreen.js.map +1 -1
- package/dist/utils/dom/mounted.d.ts +7 -0
- package/dist/utils/dom/mounted.js +16 -0
- package/dist/utils/dom/mounted.js.map +1 -0
- package/dist/utils/dom/overflow.js +2 -1
- package/dist/utils/dom/overflow.js.map +1 -1
- package/dist/utils/dom/pan-zoom.js +2 -1
- package/dist/utils/dom/pan-zoom.js.map +1 -1
- package/dist/utils/props.js +3 -13
- package/dist/utils/props.js.map +1 -1
- package/dist/utils/request/http.d.ts +12 -0
- package/dist/utils/request/http.js +6 -2
- package/dist/utils/request/http.js.map +1 -1
- package/dist/utils/request/use-request.js +4 -2
- package/dist/utils/request/use-request.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { mergeAttrs } from "../utils/props.js";
|
|
2
|
+
import { Icon } from "../base/Icon.js";
|
|
3
|
+
import { VisuallyHidden } from "../base/VisuallyHidden.js";
|
|
4
|
+
import { Spinner } from "../base/Spinner.js";
|
|
5
|
+
import { IconButton } from "../base/IconButton.js";
|
|
6
|
+
import { useMessages } from "../config/lang/messages.js";
|
|
7
|
+
import { patternsMessages } from "./messages.js";
|
|
8
|
+
import { Fragment, createVNode, ref, shallowRef, watch } from "vue";
|
|
9
|
+
import { _component, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
10
|
+
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
11
|
+
import { Check, ChevronsRight, RefreshCw, X } from "@lucide/icons";
|
|
12
|
+
//#region src/patterns/SlideCaptcha.tsx
|
|
13
|
+
/**
|
|
14
|
+
* 拼图滑块验证:画面是服务端生成的背景图(有一个缺口)和一块拼图,拖动下面的滑块让拼图对准缺口,松手后把位置和拖动轨迹
|
|
15
|
+
* 交给服务端判定。组件只负责交互,不判对错:答案在服务端,每次给一张新图。
|
|
16
|
+
*
|
|
17
|
+
* <SlideCaptcha
|
|
18
|
+
* challenge={challenge} // 服务端给的图;还没拿到时为 null
|
|
19
|
+
* status={status} // idle / verifying / success / error,由父级按判定结果设置
|
|
20
|
+
* onRelease={(result) => verify(challenge.id, result)}
|
|
21
|
+
* onRefresh={loadChallenge}
|
|
22
|
+
* />
|
|
23
|
+
*
|
|
24
|
+
* - `challenge`:background、piece 是图片地址(data URL 或普通地址),width、height 是背景图的像素尺寸,pieceWidth 是拼图图片的宽度;
|
|
25
|
+
* 拼图图片和背景一样高,左边缘就是缺口的左边缘,所以位置换算成像素直接就是答案。
|
|
26
|
+
* - `onRelease` 交出 { x, track, duration }:x 是拼图左边缘在背景图里的像素位置,track 是(毫秒,位置)序列,
|
|
27
|
+
* 真人拖动有加减速和抖动,服务端据此区分脚本。
|
|
28
|
+
* - `status` 由父级控制:verifying 时不能拖,success 时锁定,error 时滑块晃一下;父级换了新的 challenge 或设回 idle 时滑块回到起点。
|
|
29
|
+
* - 键盘:聚焦滑块后 ←/→ 微调、Home/End、Enter 或空格松手。这类验证对读屏和运动障碍的用户不友好,应用应当另外提供别的方式。
|
|
30
|
+
*/
|
|
31
|
+
/** 服务端给的一张拼图 */
|
|
32
|
+
/** 松手时交给服务端的内容 */
|
|
33
|
+
/** 验证码的状态:idle 可以拖,verifying 等服务端判定,success 通过,error 没对准 */
|
|
34
|
+
/** 未声明的属性落到外层 div 上 */
|
|
35
|
+
/** 键盘每次移动的距离(px) */
|
|
36
|
+
var KEY_STEP = 2;
|
|
37
|
+
/** 轨迹最多记多少个点,太长的拖动只留一部分 */
|
|
38
|
+
var MAX_POINTS = 240;
|
|
39
|
+
/** 滑块的宽度,也是滑块条的高度 */
|
|
40
|
+
var HANDLE = "2.75rem";
|
|
41
|
+
/** 松手回到起点时的过渡:带一点回弹 */
|
|
42
|
+
var SETTLE = "0.28s cubic-bezier(0.34, 1.4, 0.64, 1)";
|
|
43
|
+
/** SlideCaptcha 的样式:root、picture、image、piece、banner、track、fill、handle 等 */
|
|
44
|
+
var slideCaptchaStyles = css.create({
|
|
45
|
+
root: {
|
|
46
|
+
display: "flex",
|
|
47
|
+
flexDirection: "column",
|
|
48
|
+
gap: "_sm",
|
|
49
|
+
width: "_full",
|
|
50
|
+
userSelect: "none"
|
|
51
|
+
},
|
|
52
|
+
picture: {
|
|
53
|
+
position: "relative",
|
|
54
|
+
boxSizing: "border-box",
|
|
55
|
+
width: "_full",
|
|
56
|
+
overflow: "hidden",
|
|
57
|
+
borderRadius: "_md",
|
|
58
|
+
borderWidth: "_xs",
|
|
59
|
+
borderStyle: "solid",
|
|
60
|
+
borderColor: "_border",
|
|
61
|
+
backgroundColor: "_fill"
|
|
62
|
+
},
|
|
63
|
+
image: {
|
|
64
|
+
display: "block",
|
|
65
|
+
width: "_full",
|
|
66
|
+
height: "_full",
|
|
67
|
+
objectFit: "cover",
|
|
68
|
+
pointerEvents: "none",
|
|
69
|
+
userSelect: "none"
|
|
70
|
+
},
|
|
71
|
+
piece: {
|
|
72
|
+
position: "absolute",
|
|
73
|
+
top: 0,
|
|
74
|
+
height: "_full",
|
|
75
|
+
pointerEvents: "none",
|
|
76
|
+
filter: "drop-shadow(0 2px 6px rgb(0 0 0 / 0.45))"
|
|
77
|
+
},
|
|
78
|
+
pieceSettle: { transition: `left ${SETTLE}` },
|
|
79
|
+
refresh: {
|
|
80
|
+
position: "absolute",
|
|
81
|
+
top: "_xs",
|
|
82
|
+
insetInlineEnd: "_xs"
|
|
83
|
+
},
|
|
84
|
+
banner: {
|
|
85
|
+
position: "absolute",
|
|
86
|
+
insetInline: 0,
|
|
87
|
+
bottom: 0,
|
|
88
|
+
paddingBlock: "_xs",
|
|
89
|
+
paddingInline: "_md",
|
|
90
|
+
fontSize: "_sm",
|
|
91
|
+
textAlign: "center",
|
|
92
|
+
color: "white"
|
|
93
|
+
},
|
|
94
|
+
bannerSuccess: { backgroundColor: "rgb(22 163 74 / 0.88)" },
|
|
95
|
+
bannerError: { backgroundColor: "rgb(220 38 38 / 0.88)" },
|
|
96
|
+
loading: {
|
|
97
|
+
display: "flex",
|
|
98
|
+
alignItems: "center",
|
|
99
|
+
justifyContent: "center",
|
|
100
|
+
gap: "_sm",
|
|
101
|
+
height: "_full",
|
|
102
|
+
color: "_textMuted",
|
|
103
|
+
fontSize: "_sm"
|
|
104
|
+
},
|
|
105
|
+
track: {
|
|
106
|
+
position: "relative",
|
|
107
|
+
boxSizing: "border-box",
|
|
108
|
+
height: HANDLE,
|
|
109
|
+
overflow: "hidden",
|
|
110
|
+
borderRadius: "_md",
|
|
111
|
+
borderWidth: "_xs",
|
|
112
|
+
borderStyle: "solid",
|
|
113
|
+
borderColor: "_border",
|
|
114
|
+
backgroundColor: "_fill"
|
|
115
|
+
},
|
|
116
|
+
fill: {
|
|
117
|
+
position: "absolute",
|
|
118
|
+
insetBlock: 0,
|
|
119
|
+
insetInlineStart: 0,
|
|
120
|
+
backgroundColor: "_accentSoft"
|
|
121
|
+
},
|
|
122
|
+
fillSuccess: { backgroundColor: "_successSoft" },
|
|
123
|
+
fillError: { backgroundColor: "_dangerSoft" },
|
|
124
|
+
hint: {
|
|
125
|
+
position: "absolute",
|
|
126
|
+
inset: 0,
|
|
127
|
+
display: "flex",
|
|
128
|
+
alignItems: "center",
|
|
129
|
+
justifyContent: "center",
|
|
130
|
+
paddingInline: HANDLE,
|
|
131
|
+
color: "_textMuted",
|
|
132
|
+
fontSize: "_sm",
|
|
133
|
+
pointerEvents: "none"
|
|
134
|
+
},
|
|
135
|
+
handle: {
|
|
136
|
+
position: "absolute",
|
|
137
|
+
insetBlock: 0,
|
|
138
|
+
boxSizing: "border-box",
|
|
139
|
+
display: "flex",
|
|
140
|
+
alignItems: "center",
|
|
141
|
+
justifyContent: "center",
|
|
142
|
+
width: HANDLE,
|
|
143
|
+
borderRadius: "_md",
|
|
144
|
+
backgroundColor: "_surface",
|
|
145
|
+
boxShadow: "_sm",
|
|
146
|
+
color: "_accentText",
|
|
147
|
+
cursor: "grab",
|
|
148
|
+
touchAction: "none",
|
|
149
|
+
outlineOffset: "-2px"
|
|
150
|
+
},
|
|
151
|
+
handleActive: {
|
|
152
|
+
cursor: "grabbing",
|
|
153
|
+
boxShadow: "_md"
|
|
154
|
+
},
|
|
155
|
+
handleSuccess: { color: "_successText" },
|
|
156
|
+
handleError: { color: "_dangerText" },
|
|
157
|
+
locked: { cursor: "default" },
|
|
158
|
+
settle: { transition: `left ${SETTLE}, width ${SETTLE}` }
|
|
159
|
+
});
|
|
160
|
+
/** SlideCaptcha 的动态样式:位置用百分比,画面和滑块条按容器宽度缩放 */
|
|
161
|
+
var slideCaptchaDynamicStyles = css.create({
|
|
162
|
+
at: (percent) => ({ left: `${percent}%` }),
|
|
163
|
+
pieceWidth: (percent) => ({ width: `${percent}%` }),
|
|
164
|
+
ratio: (aspect) => ({ aspectRatio: String(aspect) }),
|
|
165
|
+
handleAt: (position) => ({ left: `calc(${position} * (100% - ${HANDLE}))` }),
|
|
166
|
+
fillTo: (position) => ({ width: `calc(${position} * (100% - ${HANDLE}) + ${HANDLE})` })
|
|
167
|
+
});
|
|
168
|
+
var clamp01 = (value) => Math.min(1, Math.max(0, value));
|
|
169
|
+
/** 拼图滑块验证:拖动滑块让拼图对准缺口,松手把位置与轨迹交给服务端判定 */
|
|
170
|
+
var SlideCaptcha = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
171
|
+
const _cache = [];
|
|
172
|
+
const challenge = readonlyProp(_props, "challenge");
|
|
173
|
+
const status = readonlyProp(_props, "status", "idle");
|
|
174
|
+
const hint = readonlyProp(_props, "hint");
|
|
175
|
+
const errorText = readonlyProp(_props, "errorText");
|
|
176
|
+
const onRelease = readonlyProp(_props, "onRelease");
|
|
177
|
+
const onRefresh = readonlyProp(_props, "onRefresh");
|
|
178
|
+
const rest = restProps(_props, _ctx, true);
|
|
179
|
+
const messages = useMessages("patterns", patternsMessages);
|
|
180
|
+
let trackElement = shallowRef(null);
|
|
181
|
+
let handleElement = shallowRef(null);
|
|
182
|
+
let position = ref(0);
|
|
183
|
+
let dragging = ref(false);
|
|
184
|
+
let track = [];
|
|
185
|
+
let startedAt = 0;
|
|
186
|
+
const travel = () => challenge.value ? challenge.value.width - challenge.value.pieceWidth : 0;
|
|
187
|
+
const pixels = () => Math.round(position.value * travel());
|
|
188
|
+
const interactive = () => challenge.value !== null && status.value === "idle";
|
|
189
|
+
function record() {
|
|
190
|
+
if (!track.length) startedAt = performance.now();
|
|
191
|
+
if (track.length < MAX_POINTS) track.push({
|
|
192
|
+
t: Math.round(performance.now() - startedAt),
|
|
193
|
+
x: pixels()
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function release() {
|
|
197
|
+
if (!track.length) return;
|
|
198
|
+
const result = {
|
|
199
|
+
x: pixels(),
|
|
200
|
+
track,
|
|
201
|
+
duration: Math.round(performance.now() - startedAt)
|
|
202
|
+
};
|
|
203
|
+
track = [];
|
|
204
|
+
onRelease.value?.(result);
|
|
205
|
+
}
|
|
206
|
+
function grab(event) {
|
|
207
|
+
if (!interactive() || !trackElement.value || !handleElement.value) return;
|
|
208
|
+
event.preventDefault();
|
|
209
|
+
handleElement.value.focus();
|
|
210
|
+
handleElement.value.setPointerCapture(event.pointerId);
|
|
211
|
+
const room = trackElement.value.clientWidth - handleElement.value.offsetWidth;
|
|
212
|
+
const startX = event.clientX;
|
|
213
|
+
const startPosition = position.value;
|
|
214
|
+
track = [];
|
|
215
|
+
record();
|
|
216
|
+
dragging.value = true;
|
|
217
|
+
const move = (next) => {
|
|
218
|
+
position.value = clamp01(startPosition + (next.clientX - startX) / Math.max(room, 1));
|
|
219
|
+
record();
|
|
220
|
+
};
|
|
221
|
+
const end = (next) => {
|
|
222
|
+
handleElement.value?.removeEventListener("pointermove", move);
|
|
223
|
+
handleElement.value?.removeEventListener("pointerup", end);
|
|
224
|
+
handleElement.value?.removeEventListener("pointercancel", end);
|
|
225
|
+
handleElement.value?.releasePointerCapture(next.pointerId);
|
|
226
|
+
dragging.value = false;
|
|
227
|
+
if (next.type === "pointercancel") {
|
|
228
|
+
position.value = 0;
|
|
229
|
+
track = [];
|
|
230
|
+
} else release();
|
|
231
|
+
};
|
|
232
|
+
handleElement.value.addEventListener("pointermove", move);
|
|
233
|
+
handleElement.value.addEventListener("pointerup", end);
|
|
234
|
+
handleElement.value.addEventListener("pointercancel", end);
|
|
235
|
+
}
|
|
236
|
+
function onKeydown(event) {
|
|
237
|
+
if (!interactive()) return;
|
|
238
|
+
const step = KEY_STEP / Math.max(travel(), 1);
|
|
239
|
+
const isRtl = !!trackElement.value && getComputedStyle(trackElement.value).direction === "rtl";
|
|
240
|
+
const forward = isRtl ? "ArrowLeft" : "ArrowRight";
|
|
241
|
+
const backward = isRtl ? "ArrowRight" : "ArrowLeft";
|
|
242
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
243
|
+
event.preventDefault();
|
|
244
|
+
release();
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const next = event.key === forward ? clamp01(position.value + step) : event.key === backward ? clamp01(position.value - step) : event.key === "Home" ? 0 : event.key === "End" ? 1 : null;
|
|
248
|
+
if (next === null) return;
|
|
249
|
+
event.preventDefault();
|
|
250
|
+
position.value = next;
|
|
251
|
+
record();
|
|
252
|
+
}
|
|
253
|
+
watch(() => challenge.value?.id, () => {
|
|
254
|
+
position.value = 0;
|
|
255
|
+
track = [];
|
|
256
|
+
});
|
|
257
|
+
watch(() => status.value, (next, previous) => {
|
|
258
|
+
if (next === "idle" && previous !== "idle") {
|
|
259
|
+
position.value = 0;
|
|
260
|
+
track = [];
|
|
261
|
+
}
|
|
262
|
+
if (next === "error" && handleElement.value && !matchMedia("(prefers-reduced-motion: reduce)").matches) handleElement.value.animate([
|
|
263
|
+
{ translate: "0" },
|
|
264
|
+
{ translate: "-6px" },
|
|
265
|
+
{ translate: "6px" },
|
|
266
|
+
{ translate: "-4px" },
|
|
267
|
+
{ translate: "4px" },
|
|
268
|
+
{ translate: "0" }
|
|
269
|
+
], { duration: 360 });
|
|
270
|
+
});
|
|
271
|
+
return () => {
|
|
272
|
+
const locked = !interactive();
|
|
273
|
+
const settling = !dragging.value;
|
|
274
|
+
const text = status.value === "verifying" ? messages.slideVerifying : status.value === "success" ? messages.slideSuccess : status.value === "error" ? errorText.value ?? messages.slideError : hint.value ?? messages.slideHint;
|
|
275
|
+
const icon = status.value === "success" ? Check : status.value === "error" ? X : ChevronsRight;
|
|
276
|
+
const piecePercent = challenge.value ? position.value * travel() / challenge.value.width * 100 : 0;
|
|
277
|
+
const pieceWidthPercent = challenge.value ? challenge.value.pieceWidth / challenge.value.width * 100 : 0;
|
|
278
|
+
const aspect = challenge.value ? challenge.value.width / challenge.value.height : 2;
|
|
279
|
+
return createVNode("div", mergeJsxProps(mergeAttrs({ sx: slideCaptchaStyles.root }, rest), { "aria-busy": status.value === "verifying" || void 0 }), [
|
|
280
|
+
createVNode("div", mergeAttrs({ sx: [slideCaptchaStyles.picture, css.bind(slideCaptchaDynamicStyles.ratio, () => [aspect])] }), [
|
|
281
|
+
challenge.value ? createVNode(Fragment, null, [createVNode("img", mergeJsxProps(mergeAttrs({ sx: slideCaptchaStyles.image }), {
|
|
282
|
+
"src": challenge.value.background,
|
|
283
|
+
"alt": "",
|
|
284
|
+
"draggable": false
|
|
285
|
+
}), null), createVNode("img", mergeJsxProps(mergeAttrs({ sx: [
|
|
286
|
+
slideCaptchaStyles.piece,
|
|
287
|
+
css.bind(slideCaptchaDynamicStyles.pieceWidth, () => [pieceWidthPercent]),
|
|
288
|
+
css.bind(slideCaptchaDynamicStyles.at, () => [piecePercent]),
|
|
289
|
+
settling && slideCaptchaStyles.pieceSettle
|
|
290
|
+
] }), {
|
|
291
|
+
"src": challenge.value.piece,
|
|
292
|
+
"alt": "",
|
|
293
|
+
"draggable": false
|
|
294
|
+
}), null)]) : createVNode("div", mergeAttrs({ sx: slideCaptchaStyles.loading }), [createVNode(Spinner, null, null), messages.slideLoading]),
|
|
295
|
+
createVNode(IconButton, {
|
|
296
|
+
"sx": slideCaptchaStyles.refresh,
|
|
297
|
+
"icon": RefreshCw,
|
|
298
|
+
"size": "sm",
|
|
299
|
+
"variant": "ghost",
|
|
300
|
+
"tone": "neutral",
|
|
301
|
+
"aria-label": messages.slideRefresh,
|
|
302
|
+
"disabled": status.value === "verifying",
|
|
303
|
+
"onClick": _cache[0] || (_cache[0] = () => onRefresh.value?.())
|
|
304
|
+
}, null),
|
|
305
|
+
(status.value === "success" || status.value === "error") && createVNode("div", mergeAttrs({ sx: [slideCaptchaStyles.banner, status.value === "success" ? slideCaptchaStyles.bannerSuccess : slideCaptchaStyles.bannerError] }), [text])
|
|
306
|
+
]),
|
|
307
|
+
createVNode("div", mergeAttrs({ sx: slideCaptchaStyles.track }, void 0, { ref: trackElement }), [
|
|
308
|
+
createVNode("div", mergeAttrs({ sx: [
|
|
309
|
+
slideCaptchaStyles.fill,
|
|
310
|
+
status.value === "success" && slideCaptchaStyles.fillSuccess,
|
|
311
|
+
status.value === "error" && slideCaptchaStyles.fillError,
|
|
312
|
+
settling && slideCaptchaStyles.settle,
|
|
313
|
+
css.bind(slideCaptchaDynamicStyles.fillTo, () => [position.value], true)
|
|
314
|
+
] }), null),
|
|
315
|
+
createVNode("span", mergeJsxProps(mergeAttrs({ sx: slideCaptchaStyles.hint }), { "aria-hidden": "true" }), [position.value === 0 || status.value !== "idle" ? text : null]),
|
|
316
|
+
createVNode("div", mergeJsxProps(mergeAttrs({ sx: [
|
|
317
|
+
slideCaptchaStyles.handle,
|
|
318
|
+
dragging.value && slideCaptchaStyles.handleActive,
|
|
319
|
+
status.value === "success" && slideCaptchaStyles.handleSuccess,
|
|
320
|
+
status.value === "error" && slideCaptchaStyles.handleError,
|
|
321
|
+
locked && slideCaptchaStyles.locked,
|
|
322
|
+
settling && slideCaptchaStyles.settle,
|
|
323
|
+
css.bind(slideCaptchaDynamicStyles.handleAt, () => [position.value], true)
|
|
324
|
+
] }, void 0, { ref: handleElement }), {
|
|
325
|
+
"role": "slider",
|
|
326
|
+
"aria-label": messages.slideLabel,
|
|
327
|
+
"aria-valuemin": 0,
|
|
328
|
+
"aria-valuemax": travel(),
|
|
329
|
+
"aria-valuenow": pixels(),
|
|
330
|
+
"aria-valuetext": messages.slidePosition(pixels()),
|
|
331
|
+
"aria-disabled": locked || void 0,
|
|
332
|
+
"tabindex": locked ? -1 : 0,
|
|
333
|
+
"onPointerdown": grab,
|
|
334
|
+
"onKeydown": onKeydown
|
|
335
|
+
}), [createVNode(Icon, { "icon": icon }, null)])
|
|
336
|
+
]),
|
|
337
|
+
createVNode(VisuallyHidden, { "aria-live": "polite" }, { default: () => [status.value === "idle" ? "" : text] })
|
|
338
|
+
]);
|
|
339
|
+
};
|
|
340
|
+
}, {
|
|
341
|
+
name: "SlideCaptcha",
|
|
342
|
+
props: [
|
|
343
|
+
"challenge",
|
|
344
|
+
"status",
|
|
345
|
+
"hint",
|
|
346
|
+
"errorText",
|
|
347
|
+
"onRelease",
|
|
348
|
+
"onRefresh"
|
|
349
|
+
]
|
|
350
|
+
});
|
|
351
|
+
//#endregion
|
|
352
|
+
export { SlideCaptcha, slideCaptchaDynamicStyles, slideCaptchaStyles };
|
|
353
|
+
|
|
354
|
+
//# sourceMappingURL=SlideCaptcha.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlideCaptcha.js","names":["_vtp_readonlyProp","_vtp_restProps","_vue_shallowRef","_vue_ref","_createVNode","_mergeProps","_Fragment"],"sources":["../../src/patterns/SlideCaptcha.tsx"],"sourcesContent":["/**\n * 拼图滑块验证:画面是服务端生成的背景图(有一个缺口)和一块拼图,拖动下面的滑块让拼图对准缺口,松手后把位置和拖动轨迹\n * 交给服务端判定。组件只负责交互,不判对错:答案在服务端,每次给一张新图。\n *\n * <SlideCaptcha\n * challenge={challenge} // 服务端给的图;还没拿到时为 null\n * status={status} // idle / verifying / success / error,由父级按判定结果设置\n * onRelease={(result) => verify(challenge.id, result)}\n * onRefresh={loadChallenge}\n * />\n *\n * - `challenge`:background、piece 是图片地址(data URL 或普通地址),width、height 是背景图的像素尺寸,pieceWidth 是拼图图片的宽度;\n * 拼图图片和背景一样高,左边缘就是缺口的左边缘,所以位置换算成像素直接就是答案。\n * - `onRelease` 交出 { x, track, duration }:x 是拼图左边缘在背景图里的像素位置,track 是(毫秒,位置)序列,\n * 真人拖动有加减速和抖动,服务端据此区分脚本。\n * - `status` 由父级控制:verifying 时不能拖,success 时锁定,error 时滑块晃一下;父级换了新的 challenge 或设回 idle 时滑块回到起点。\n * - 键盘:聚焦滑块后 ←/→ 微调、Home/End、Enter 或空格松手。这类验证对读屏和运动障碍的用户不友好,应用应当另外提供别的方式。\n */\nimport { Check, ChevronsRight, RefreshCw, X } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { watch } from 'vue';\nimport { Icon } from '../base/Icon';\nimport { IconButton } from '../base/IconButton';\nimport { Spinner } from '../base/Spinner';\nimport { VisuallyHidden } from '../base/VisuallyHidden';\nimport { useMessages } from '../config/lang/messages';\nimport { mergeAttrs, type PartProps } from '../utils/props';\nimport { patternsMessages } from './messages';\n\n/** 服务端给的一张拼图 */\nexport interface SlideChallenge {\n /** 这一张的标识,换图时变;松手后连同结果一起交给服务端 */\n id: string;\n /** 背景图(带缺口) */\n background: string;\n /** 拼图图片:和背景一样高,左边缘对着缺口的左边缘 */\n piece: string;\n /** 背景图的宽(px) */\n width: number;\n /** 背景图的高(px) */\n height: number;\n /** 拼图图片的宽度(px) */\n pieceWidth: number;\n}\n\n/** 松手时交给服务端的内容 */\nexport interface SlideResult {\n /** 拼图左边缘在背景图里的位置(px,整数) */\n x: number;\n /** 拖动轨迹:距按下的毫秒数,和当时的位置(px) */\n track: { t: number; x: number }[];\n /** 从按下到松手的毫秒数 */\n duration: number;\n}\n\n/** 验证码的状态:idle 可以拖,verifying 等服务端判定,success 通过,error 没对准 */\nexport type SlideStatus = 'idle' | 'verifying' | 'success' | 'error';\n\n/** 未声明的属性落到外层 div 上 */\nexport interface SlideCaptchaProps extends PartProps<'div'> {\n /** 当前的拼图;还没拿到时为 null,显示加载中 */\n challenge: SlideChallenge | null;\n /** 状态,默认 idle */\n status?: SlideStatus;\n /** 滑块条上的提示,默认“向右拖动滑块,让拼图对准缺口” */\n hint?: string;\n /** 没对准时的提示(图片底部和滑块条上),默认“没对准,请再试一次”;服务端给了具体原因时用它 */\n errorText?: string;\n /** 松手:把结果发给服务端判定 */\n onRelease?: (result: SlideResult) => void;\n /** 点了“换一张” */\n onRefresh?: () => void;\n}\n\n/** 键盘每次移动的距离(px) */\nconst KEY_STEP = 2;\n/** 轨迹最多记多少个点,太长的拖动只留一部分 */\nconst MAX_POINTS = 240;\n/** 滑块的宽度,也是滑块条的高度 */\nconst HANDLE = '2.75rem';\n/** 松手回到起点时的过渡:带一点回弹 */\nconst SETTLE = '0.28s cubic-bezier(0.34, 1.4, 0.64, 1)';\n\n/** SlideCaptcha 的样式:root、picture、image、piece、banner、track、fill、handle 等 */\nexport const slideCaptchaStyles = css.create({\n root: {\n display: 'flex',\n flexDirection: 'column',\n gap: '_sm',\n width: '_full',\n userSelect: 'none',\n },\n picture: {\n position: 'relative',\n boxSizing: 'border-box',\n width: '_full',\n overflow: 'hidden',\n borderRadius: '_md',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_border',\n backgroundColor: '_fill',\n },\n image: {\n display: 'block',\n width: '_full',\n height: '_full',\n objectFit: 'cover',\n pointerEvents: 'none',\n userSelect: 'none',\n },\n piece: {\n position: 'absolute',\n top: 0,\n height: '_full',\n pointerEvents: 'none',\n filter: 'drop-shadow(0 2px 6px rgb(0 0 0 / 0.45))',\n },\n pieceSettle: { transition: `left ${SETTLE}` },\n refresh: { position: 'absolute', top: '_xs', insetInlineEnd: '_xs' },\n // 盖在图上的结果条:底色固定为深色半透明,白字在任何背景图上都读得清\n banner: {\n position: 'absolute',\n insetInline: 0,\n bottom: 0,\n paddingBlock: '_xs',\n paddingInline: '_md',\n fontSize: '_sm',\n textAlign: 'center',\n color: 'white',\n },\n bannerSuccess: { backgroundColor: 'rgb(22 163 74 / 0.88)' },\n bannerError: { backgroundColor: 'rgb(220 38 38 / 0.88)' },\n loading: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '_sm',\n height: '_full',\n color: '_textMuted',\n fontSize: '_sm',\n },\n track: {\n position: 'relative',\n boxSizing: 'border-box',\n height: HANDLE,\n overflow: 'hidden',\n borderRadius: '_md',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_border',\n backgroundColor: '_fill',\n },\n fill: {\n position: 'absolute',\n insetBlock: 0,\n insetInlineStart: 0,\n backgroundColor: '_accentSoft',\n },\n fillSuccess: { backgroundColor: '_successSoft' },\n fillError: { backgroundColor: '_dangerSoft' },\n hint: {\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n paddingInline: HANDLE,\n color: '_textMuted',\n fontSize: '_sm',\n pointerEvents: 'none',\n },\n handle: {\n position: 'absolute',\n insetBlock: 0,\n boxSizing: 'border-box',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: HANDLE,\n borderRadius: '_md',\n backgroundColor: '_surface',\n boxShadow: '_sm',\n color: '_accentText',\n cursor: 'grab',\n touchAction: 'none',\n outlineOffset: '-2px',\n },\n handleActive: { cursor: 'grabbing', boxShadow: '_md' },\n handleSuccess: { color: '_successText' },\n handleError: { color: '_dangerText' },\n locked: { cursor: 'default' },\n // 松手后回到起点、或键盘微调时有过渡;拖动中跟手,不加过渡\n settle: { transition: `left ${SETTLE}, width ${SETTLE}` },\n});\n\n/** SlideCaptcha 的动态样式:位置用百分比,画面和滑块条按容器宽度缩放 */\nexport const slideCaptchaDynamicStyles = css.create({\n at: (percent: number) => ({ left: `${percent}%` }),\n pieceWidth: (percent: number) => ({ width: `${percent}%` }),\n ratio: (aspect: number) => ({ aspectRatio: String(aspect) }),\n // 滑块在条里的位置:条宽减去滑块宽的部分按比例走\n handleAt: (position: number) => ({ left: `calc(${position} * (100% - ${HANDLE}))` }),\n fillTo: (position: number) => ({ width: `calc(${position} * (100% - ${HANDLE}) + ${HANDLE})` }),\n});\n\nconst clamp01 = (value: number) => Math.min(1, Math.max(0, value));\n\n/** 拼图滑块验证:拖动滑块让拼图对准缺口,松手把位置与轨迹交给服务端判定 */\nexport const SlideCaptcha = _component(\n ({\n challenge,\n status = 'idle',\n hint,\n errorText,\n onRelease,\n onRefresh,\n ...rest\n }: SlideCaptchaProps) => {\n const messages = useMessages('patterns', patternsMessages);\n let trackElement = _state.raw<HTMLElement | null>(null);\n let handleElement = _state.raw<HTMLElement | null>(null);\n // 拼图左边缘的位置占“可移动距离”的比例(0 到 1)\n let position = _state(0);\n let dragging = _state(false);\n let track: SlideResult['track'] = [];\n let startedAt = 0;\n\n const travel = () => (challenge ? challenge.width - challenge.pieceWidth : 0);\n const pixels = () => Math.round(position * travel());\n const interactive = () => challenge !== null && status === 'idle';\n\n function record() {\n if (!track.length) startedAt = performance.now();\n if (track.length < MAX_POINTS) {\n track.push({ t: Math.round(performance.now() - startedAt), x: pixels() });\n }\n }\n\n function release() {\n if (!track.length) return;\n const result: SlideResult = {\n x: pixels(),\n track,\n duration: Math.round(performance.now() - startedAt),\n };\n track = [];\n onRelease?.(result);\n }\n\n function grab(event: PointerEvent) {\n if (!interactive() || !trackElement || !handleElement) return;\n event.preventDefault();\n handleElement.focus();\n handleElement.setPointerCapture(event.pointerId);\n const room = trackElement.clientWidth - handleElement.offsetWidth;\n const startX = event.clientX;\n const startPosition = position;\n track = [];\n record();\n dragging = true;\n const move = (next: PointerEvent) => {\n position = clamp01(startPosition + (next.clientX - startX) / Math.max(room, 1));\n record();\n };\n const end = (next: PointerEvent) => {\n handleElement?.removeEventListener('pointermove', move);\n handleElement?.removeEventListener('pointerup', end);\n handleElement?.removeEventListener('pointercancel', end);\n handleElement?.releasePointerCapture(next.pointerId);\n dragging = false;\n if (next.type === 'pointercancel') {\n position = 0;\n track = [];\n } else {\n release();\n }\n };\n handleElement.addEventListener('pointermove', move);\n handleElement.addEventListener('pointerup', end);\n handleElement.addEventListener('pointercancel', end);\n }\n\n function onKeydown(event: KeyboardEvent) {\n if (!interactive()) return;\n const step = KEY_STEP / Math.max(travel(), 1);\n const isRtl = !!trackElement && getComputedStyle(trackElement).direction === 'rtl';\n const forward = isRtl ? 'ArrowLeft' : 'ArrowRight';\n const backward = isRtl ? 'ArrowRight' : 'ArrowLeft';\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n release();\n return;\n }\n const next =\n event.key === forward\n ? clamp01(position + step)\n : event.key === backward\n ? clamp01(position - step)\n : event.key === 'Home'\n ? 0\n : event.key === 'End'\n ? 1\n : null;\n if (next === null) return;\n event.preventDefault();\n position = next;\n record();\n }\n\n // 换了新图、或父级设回 idle(重试):滑块回到起点;没对准时滑块晃一下\n watch(\n () => challenge?.id,\n () => {\n position = 0;\n track = [];\n },\n );\n watch(\n () => status,\n (next, previous) => {\n if (next === 'idle' && previous !== 'idle') {\n position = 0;\n track = [];\n }\n if (\n next === 'error' &&\n handleElement &&\n !matchMedia('(prefers-reduced-motion: reduce)').matches\n ) {\n handleElement.animate(\n [\n { translate: '0' },\n { translate: '-6px' },\n { translate: '6px' },\n { translate: '-4px' },\n { translate: '4px' },\n { translate: '0' },\n ],\n { duration: 360 },\n );\n }\n },\n );\n\n return () => {\n const locked = !interactive();\n const settling = !dragging;\n const text =\n status === 'verifying'\n ? messages.slideVerifying\n : status === 'success'\n ? messages.slideSuccess\n : status === 'error'\n ? (errorText ?? messages.slideError)\n : (hint ?? messages.slideHint);\n const icon = status === 'success' ? Check : status === 'error' ? X : ChevronsRight;\n const piecePercent = challenge ? ((position * travel()) / challenge.width) * 100 : 0;\n const pieceWidthPercent = challenge ? (challenge.pieceWidth / challenge.width) * 100 : 0;\n const aspect = challenge ? challenge.width / challenge.height : 2;\n return (\n <div\n {...mergeAttrs<'div'>({ sx: slideCaptchaStyles.root }, rest)}\n aria-busy={status === 'verifying' || undefined}\n >\n <div\n {...mergeAttrs<'div'>({\n sx: [slideCaptchaStyles.picture, slideCaptchaDynamicStyles.ratio(aspect)],\n })}\n >\n {challenge ? (\n <>\n <img\n {...mergeAttrs<'img'>({ sx: slideCaptchaStyles.image })}\n src={challenge.background}\n alt=\"\"\n draggable={false}\n />\n <img\n {...mergeAttrs<'img'>({\n sx: [\n slideCaptchaStyles.piece,\n slideCaptchaDynamicStyles.pieceWidth(pieceWidthPercent),\n slideCaptchaDynamicStyles.at(piecePercent),\n settling && slideCaptchaStyles.pieceSettle,\n ],\n })}\n src={challenge.piece}\n alt=\"\"\n draggable={false}\n />\n </>\n ) : (\n <div {...mergeAttrs<'div'>({ sx: slideCaptchaStyles.loading })}>\n <Spinner />\n {messages.slideLoading}\n </div>\n )}\n <IconButton\n sx={slideCaptchaStyles.refresh}\n icon={RefreshCw}\n size=\"sm\"\n variant=\"ghost\"\n tone=\"neutral\"\n aria-label={messages.slideRefresh}\n disabled={status === 'verifying'}\n onClick={() => onRefresh?.()}\n />\n {(status === 'success' || status === 'error') && (\n <div\n {...mergeAttrs<'div'>({\n sx: [\n slideCaptchaStyles.banner,\n status === 'success'\n ? slideCaptchaStyles.bannerSuccess\n : slideCaptchaStyles.bannerError,\n ],\n })}\n >\n {text}\n </div>\n )}\n </div>\n <div\n {...mergeAttrs<'div'>({ sx: slideCaptchaStyles.track }, undefined, {\n ref: _toRef(trackElement),\n })}\n >\n <div\n {...mergeAttrs<'div'>({\n sx: [\n slideCaptchaStyles.fill,\n status === 'success' && slideCaptchaStyles.fillSuccess,\n status === 'error' && slideCaptchaStyles.fillError,\n settling && slideCaptchaStyles.settle,\n slideCaptchaDynamicStyles.fillTo(position),\n ],\n })}\n />\n <span {...mergeAttrs<'span'>({ sx: slideCaptchaStyles.hint })} aria-hidden=\"true\">\n {position === 0 || status !== 'idle' ? text : null}\n </span>\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n slideCaptchaStyles.handle,\n dragging && slideCaptchaStyles.handleActive,\n status === 'success' && slideCaptchaStyles.handleSuccess,\n status === 'error' && slideCaptchaStyles.handleError,\n locked && slideCaptchaStyles.locked,\n settling && slideCaptchaStyles.settle,\n slideCaptchaDynamicStyles.handleAt(position),\n ],\n },\n undefined,\n { ref: _toRef(handleElement) },\n )}\n role=\"slider\"\n aria-label={messages.slideLabel}\n aria-valuemin={0}\n aria-valuemax={travel()}\n aria-valuenow={pixels()}\n aria-valuetext={messages.slidePosition(pixels())}\n aria-disabled={locked || undefined}\n tabindex={locked ? -1 : 0}\n onPointerdown={grab}\n onKeydown={onKeydown}\n >\n <Icon icon={icon} />\n </div>\n </div>\n <VisuallyHidden aria-live=\"polite\">{status === 'idle' ? '' : text}</VisuallyHidden>\n </div>\n );\n };\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EA,IAAM,WAAW;;AAEjB,IAAM,aAAa;;AAEnB,IAAM,SAAS;;AAEf,IAAM,SAAS;;AAGf,IAAa,qBAAqB,IAAI,OAAO;CAC3C,MAAM;EACJ,SAAS;EACT,eAAe;EACf,KAAK;EACL,OAAO;EACP,YAAY;CACd;CACA,SAAS;EACP,UAAU;EACV,WAAW;EACX,OAAO;EACP,UAAU;EACV,cAAc;EACd,aAAa;EACb,aAAa;EACb,aAAa;EACb,iBAAiB;CACnB;CACA,OAAO;EACL,SAAS;EACT,OAAO;EACP,QAAQ;EACR,WAAW;EACX,eAAe;EACf,YAAY;CACd;CACA,OAAO;EACL,UAAU;EACV,KAAK;EACL,QAAQ;EACR,eAAe;EACf,QAAQ;CACV;CACA,aAAa,EAAE,YAAY,QAAQ,SAAS;CAC5C,SAAS;EAAE,UAAU;EAAY,KAAK;EAAO,gBAAgB;CAAM;CAEnE,QAAQ;EACN,UAAU;EACV,aAAa;EACb,QAAQ;EACR,cAAc;EACd,eAAe;EACf,UAAU;EACV,WAAW;EACX,OAAO;CACT;CACA,eAAe,EAAE,iBAAiB,wBAAwB;CAC1D,aAAa,EAAE,iBAAiB,wBAAwB;CACxD,SAAS;EACP,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,KAAK;EACL,QAAQ;EACR,OAAO;EACP,UAAU;CACZ;CACA,OAAO;EACL,UAAU;EACV,WAAW;EACX,QAAQ;EACR,UAAU;EACV,cAAc;EACd,aAAa;EACb,aAAa;EACb,aAAa;EACb,iBAAiB;CACnB;CACA,MAAM;EACJ,UAAU;EACV,YAAY;EACZ,kBAAkB;EAClB,iBAAiB;CACnB;CACA,aAAa,EAAE,iBAAiB,eAAe;CAC/C,WAAW,EAAE,iBAAiB,cAAc;CAC5C,MAAM;EACJ,UAAU;EACV,OAAO;EACP,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,eAAe;EACf,OAAO;EACP,UAAU;EACV,eAAe;CACjB;CACA,QAAQ;EACN,UAAU;EACV,YAAY;EACZ,WAAW;EACX,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,OAAO;EACP,cAAc;EACd,iBAAiB;EACjB,WAAW;EACX,OAAO;EACP,QAAQ;EACR,aAAa;EACb,eAAe;CACjB;CACA,cAAc;EAAE,QAAQ;EAAY,WAAW;CAAM;CACrD,eAAe,EAAE,OAAO,eAAe;CACvC,aAAa,EAAE,OAAO,cAAc;CACpC,QAAQ,EAAE,QAAQ,UAAU;CAE5B,QAAQ,EAAE,YAAY,QAAQ,OAAM,UAAW,SAAS;AAC1D,CAAC;;AAGD,IAAa,4BAA4B,IAAI,OAAO;CAClD,KAAK,aAAqB,EAAE,MAAM,GAAG,QAAO,GAAI;CAChD,aAAa,aAAqB,EAAE,OAAO,GAAG,QAAO,GAAI;CACzD,QAAQ,YAAoB,EAAE,aAAa,OAAO,MAAM,EAAE;CAE1D,WAAW,cAAsB,EAAE,MAAM,QAAQ,SAAQ,aAAc,OAAM,IAAK;CAClF,SAAS,cAAsB,EAAE,OAAO,QAAQ,SAAQ,aAAc,OAAM,MAAO,OAAM,GAAI;AAC/F,CAAC;AAED,IAAM,WAAW,UAAkB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,CAAC;;AAGjE,IAAa,eAAe,0BAC1B,QAAA,SAQyB;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,SAAAA,aAAA,QAAA,UANd,MAAM;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAKZ,OAAIC,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,WAAW,YAAY,YAAY,gBAAgB;CACzD,IAAI,eAAYC,WAAkC,IAAI;CACtD,IAAI,gBAAaA,WAAkC,IAAI;CAEvD,IAAI,WAAQC,IAAU,CAAC;CACvB,IAAI,WAAQA,IAAU,KAAK;CAC3B,IAAI,QAA8B,CAAA;CAClC,IAAI,YAAY;CAEhB,MAAM,eAAgB,UAAA,QAAY,UAAA,MAAU,QAAQ,UAAA,MAAU,aAAa;CAC3E,MAAM,eAAe,KAAK,MAAM,SAAA,QAAW,OAAO,CAAC;CACnD,MAAM,oBAAoB,UAAA,UAAc,QAAQ,OAAA,UAAW;CAE3D,SAAS,SAAS;EAChB,IAAI,CAAC,MAAM,QAAQ,YAAY,YAAY,IAAI;EAC/C,IAAI,MAAM,SAAS,YACjB,MAAM,KAAK;GAAE,GAAG,KAAK,MAAM,YAAY,IAAI,IAAI,SAAS;GAAG,GAAG,OAAO;EAAE,CAAC;CAE5E;CAEA,SAAS,UAAU;EACjB,IAAI,CAAC,MAAM,QAAQ;EACnB,MAAM,SAAsB;GAC1B,GAAG,OAAO;GACV;GACA,UAAU,KAAK,MAAM,YAAY,IAAI,IAAI,SAAS;EACpD;EACA,QAAQ,CAAA;EACR,UAAA,QAAY,MAAM;CACpB;CAEA,SAAS,KAAK,OAAqB;EACjC,IAAI,CAAC,YAAY,KAAK,CAAA,aAAA,SAAiB,CAAA,cAAA,OAAgB;EACvD,MAAM,eAAe;EACrB,cAAA,MAAc,MAAM;EACpB,cAAA,MAAc,kBAAkB,MAAM,SAAS;EAC/C,MAAM,OAAO,aAAA,MAAa,cAAc,cAAA,MAAc;EACtD,MAAM,SAAS,MAAM;EACrB,MAAM,gBAAa,SAAA;EACnB,QAAQ,CAAA;EACR,OAAO;EACP,SAAA,QAAW;EACX,MAAM,QAAQ,SAAuB;GACnC,SAAA,QAAW,QAAQ,iBAAiB,KAAK,UAAU,UAAU,KAAK,IAAI,MAAM,CAAC,CAAC;GAC9E,OAAO;EACT;EACA,MAAM,OAAO,SAAuB;GAClC,cAAA,OAAe,oBAAoB,eAAe,IAAI;GACtD,cAAA,OAAe,oBAAoB,aAAa,GAAG;GACnD,cAAA,OAAe,oBAAoB,iBAAiB,GAAG;GACvD,cAAA,OAAe,sBAAsB,KAAK,SAAS;GACnD,SAAA,QAAW;GACX,IAAI,KAAK,SAAS,iBAAiB;IACjC,SAAA,QAAW;IACX,QAAQ,CAAA;GACV,OACE,QAAQ;EAEZ;EACA,cAAA,MAAc,iBAAiB,eAAe,IAAI;EAClD,cAAA,MAAc,iBAAiB,aAAa,GAAG;EAC/C,cAAA,MAAc,iBAAiB,iBAAiB,GAAG;CACrD;CAEA,SAAS,UAAU,OAAsB;EACvC,IAAI,CAAC,YAAY,GAAG;EACpB,MAAM,OAAO,WAAW,KAAK,IAAI,OAAO,GAAG,CAAC;EAC5C,MAAM,QAAQ,CAAC,CAAA,aAAA,SAAiB,iBAAgB,aAAA,KAAa,CAAC,CAAC,cAAc;EAC7E,MAAM,UAAU,QAAQ,cAAc;EACtC,MAAM,WAAW,QAAQ,eAAe;EACxC,IAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;GAC9C,MAAM,eAAe;GACrB,QAAQ;GACR;EACF;EACA,MAAM,OACJ,MAAM,QAAQ,UACV,QAAQ,SAAA,QAAW,IAAI,IACvB,MAAM,QAAQ,WACZ,QAAQ,SAAA,QAAW,IAAI,IACvB,MAAM,QAAQ,SACZ,IACA,MAAM,QAAQ,QACZ,IACA;EACZ,IAAI,SAAS,MAAM;EACnB,MAAM,eAAe;EACrB,SAAA,QAAW;EACX,OAAO;CACT;CAGA,YACQ,UAAA,OAAW,UACX;EACJ,SAAA,QAAW;EACX,QAAQ,CAAA;CACV,CACF;CACA,YACE,OAAA,QACC,MAAM,aAAa;EAClB,IAAI,SAAS,UAAU,aAAa,QAAQ;GAC1C,SAAA,QAAW;GACX,QAAQ,CAAA;EACV;EACA,IACE,SAAS,WAAO,cAAA,SAEhB,CAAC,WAAW,kCAAkC,CAAC,CAAC,SAEhD,cAAA,MAAc,QACZ;GACE,EAAE,WAAW,IAAI;GACjB,EAAE,WAAW,OAAO;GACpB,EAAE,WAAW,MAAM;GACnB,EAAE,WAAW,OAAO;GACpB,EAAE,WAAW,MAAM;GACnB,EAAE,WAAW,IAAI;EAAC,GAEpB,EAAE,UAAU,IAAI,CAClB;CAEJ,CACF;CAEA,aAAa;EACX,MAAM,SAAS,CAAC,YAAY;EAC5B,MAAM,WAAW,CAAA,SAAA;EACjB,MAAM,OACJ,OAAA,UAAW,cACP,SAAS,iBACT,OAAA,UAAW,YACT,SAAS,eACT,OAAA,UAAW,UACR,UAAA,SAAa,SAAS,aACtB,KAAA,SAAQ,SAAS;EAC5B,MAAM,OAAO,OAAA,UAAW,YAAY,QAAQ,OAAA,UAAW,UAAU,IAAI;EACrE,MAAM,eAAe,UAAA,QAAc,SAAA,QAAW,OAAO,IAAK,UAAA,MAAU,QAAS,MAAM;EACnF,MAAM,oBAAoB,UAAA,QAAa,UAAA,MAAU,aAAa,UAAA,MAAU,QAAS,MAAM;EACvF,MAAM,SAAS,UAAA,QAAY,UAAA,MAAU,QAAQ,UAAA,MAAU,SAAS;EAChE,OAAAC,YAAA,OAAAC,cAEQ,WAAkB,EAAE,IAAI,mBAAmB,KAAK,GAAG,IAAI,GAAC,EAAA,aACjD,OAAA,UAAW,eAAe,KAAA,EAAS,CAAA,GAAA;GAAAD,YAAA,OAGxC,WAAkB,EACpB,IAAI,CAAC,mBAAmB,SAAO,IAAA,KAAE,0BAA0B,aAAK,CAAC,MAAM,CAAA,CAAA,EACzE,CAAC,GAAC;IAED,UAAA,QAAAA,YAAAE,UAAA,MAAA,CAAAF,YAAA,OAAAC,cAGS,WAAkB,EAAE,IAAI,mBAAmB,MAAM,CAAC,GAAC;KAAA,OAClD,UAAA,MAAU;KAAU,OAAA;KAAA,aAEd;IAAK,CAAA,GAAA,IAAA,GAAAD,YAAA,OAAAC,cAGZ,WAAkB,EACpB,IAAI;KACF,mBAAmB;KAAK,IAAA,KACxB,0BAA0B,kBAAU,CAAC,iBAAiB,CAAA;KAAA,IAAA,KACtD,0BAA0B,UAAE,CAAC,YAAY,CAAA;KACzC,YAAY,mBAAmB;IAAW,EAE9C,CAAC,GAAC;KAAA,OACG,UAAA,MAAU;KAAK,OAAA;KAAA,aAET;IAAK,CAAA,GAAA,IAAA,CAAA,CAAA,IAAAD,YAAA,OAIX,WAAkB,EAAE,IAAI,mBAAmB,QAAQ,CAAC,GAAC,CAAAA,YAAA,SAAA,MAAA,IAAA,GAE3D,SAAS,YAAY,CAAA;IAEzBA,YAAA,YAAA;KAAA,MAEK,mBAAmB;KAAO,QACxB;KAAS,QAAA;KAAA,WAAA;KAAA,QAAA;KAAA,cAIH,SAAS;KAAY,YACvB,OAAA,UAAW;KAAW,WAAA,OAAA,OAAA,OAAA,WACjB,UAAA,QAAY;IAAC,GAAA,IAAA;KAE5B,OAAA,UAAW,aAAa,OAAA,UAAW,YAAOA,YAAA,OAEpC,WAAkB,EACpB,IAAI,CACF,mBAAmB,QACnB,OAAA,UAAW,YACP,mBAAmB,gBACnB,mBAAmB,WAAW,EAEtC,CAAC,GAAC,CAED,IAAI,CAAA;GAER,CAAA;GAAAA,YAAA,OAGG,WAAkB,EAAE,IAAI,mBAAmB,MAAM,GAAG,KAAA,GAAW,EACjE,KAAG,aACL,CAAC,GAAC;IAAAA,YAAA,OAGI,WAAkB,EACpB,IAAI;KACF,mBAAmB;KACnB,OAAA,UAAW,aAAa,mBAAmB;KAC3C,OAAA,UAAW,WAAW,mBAAmB;KACzC,YAAY,mBAAmB;KAAM,IAAA,KACrC,0BAA0B,cAAM,CAAA,SAAA,KAAA,GAAA,IAAA;IAAA,EAEpC,CAAC,GAAC,IAAA;IAAAA,YAAA,QAAAC,cAEM,WAAmB,EAAE,IAAI,mBAAmB,KAAK,CAAC,GAAC,EAAA,eAAA,OAAA,CAAA,GAAA,CAC1D,SAAA,UAAa,KAAK,OAAA,UAAW,SAAS,OAAO,IAAI,CAAA;IAAAD,YAAA,OAAAC,cAG9C,WACF,EACE,IAAI;KACF,mBAAmB;KACnB,SAAA,SAAY,mBAAmB;KAC/B,OAAA,UAAW,aAAa,mBAAmB;KAC3C,OAAA,UAAW,WAAW,mBAAmB;KACzC,UAAU,mBAAmB;KAC7B,YAAY,mBAAmB;KAAM,IAAA,KACrC,0BAA0B,gBAAQ,CAAA,SAAA,KAAA,GAAA,IAAA;IAAA,EAEtC,GACA,KAAA,GACA,EAAE,KAAG,cAAwB,CAC/B,GAAC;KAAA,QAAA;KAAA,cAEW,SAAS;KAAU,iBAChB;KAAC,iBACD,OAAO;KAAC,iBACR,OAAO;KAAC,kBACP,SAAS,cAAc,OAAO,CAAC;KAAC,iBACjC,UAAU,KAAA;KAAS,YACxB,SAAS,KAAK;KAAC,iBACV;KAAI,aACR;IAAS,CAAA,GAAA,CAAAD,YAAA,MAAA,EAAA,QAER,KAAI,GAAA,IAAA,CAAA,CAAA;GAAA,CAAA;GAAAA,YAAA,gBAAA,EAAA,aAAA,SAAA,GAAA,EAAA,eAAA,CAGgB,OAAA,UAAW,SAAS,KAAK,IAAI,EAAA,CAAA;EAAA,CAAA;CAGvE;AACF,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
|
@@ -5,8 +5,8 @@ import { Form } from "../form/Form.js";
|
|
|
5
5
|
import { Steps } from "../navigation/Steps.js";
|
|
6
6
|
import { useMessages } from "../config/lang/messages.js";
|
|
7
7
|
import { patternsMessages } from "./messages.js";
|
|
8
|
-
import { createVNode,
|
|
9
|
-
import { _component, bindable, effect, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
8
|
+
import { createVNode, nextTick, shallowRef, useId } from "vue";
|
|
9
|
+
import { _component, bindable, effect, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
10
10
|
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
11
11
|
//#region src/patterns/StepsForm.tsx
|
|
12
12
|
/** 一步 */
|
|
@@ -69,8 +69,8 @@ var StepsForm = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
69
69
|
return () => {
|
|
70
70
|
const step = steps.value[current.value];
|
|
71
71
|
const last = current.value >= steps.value.length - 1;
|
|
72
|
-
return createVNode(Form,
|
|
73
|
-
createVNode(Steps,
|
|
72
|
+
return createVNode(Form, mergeJsxProps(rest, { "controller": form.value }), { default: () => [createVNode("div", css.attrs(stepsFormStyles.root), [
|
|
73
|
+
createVNode(Steps, mergeJsxProps(mergePartProps({ items: items() }, propsSteps.value, {
|
|
74
74
|
items: items(),
|
|
75
75
|
navigable: true
|
|
76
76
|
}), {
|
|
@@ -87,19 +87,19 @@ var StepsForm = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
87
87
|
createVNode(Flex, mergePartProps({
|
|
88
88
|
justify: "end",
|
|
89
89
|
gap: "sm"
|
|
90
|
-
}, propsActions.value), { default: () => [current.value > 0 && createVNode(Button,
|
|
90
|
+
}, propsActions.value), { default: () => [current.value > 0 && createVNode(Button, mergeJsxProps(mergePartProps({
|
|
91
91
|
variant: "ghost",
|
|
92
92
|
tone: "neutral"
|
|
93
93
|
}, propsPrevious.value, { onClick: () => go(current.value - 1) }), { "key": "__site0" }), {
|
|
94
94
|
default: () => [messages.stepsFormPrevious],
|
|
95
95
|
$stable: true
|
|
96
|
-
}), last ? createVNode(Button,
|
|
96
|
+
}), last ? createVNode(Button, mergeJsxProps(mergePartProps({}, propsSubmit.value, {
|
|
97
97
|
type: "submit",
|
|
98
98
|
loading: form.value.submitting
|
|
99
99
|
}), { "key": "__site1" }), {
|
|
100
100
|
default: () => [submitText.value ?? messages.stepsFormSubmit],
|
|
101
101
|
$stable: true
|
|
102
|
-
}) : createVNode(Button,
|
|
102
|
+
}) : createVNode(Button, mergeJsxProps(mergePartProps({}, propsNext.value, {
|
|
103
103
|
loading: form.value.validating(),
|
|
104
104
|
onClick: () => void next()
|
|
105
105
|
}), { "key": "__site2" }), {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StepsForm.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_shallowRef","_createVNode","_mergeProps"],"sources":["../../src/patterns/StepsForm.tsx"],"sourcesContent":["/**\n * 分步表单:一份 useForm 的表单分成几步填(开户、准入申请、配置向导),上方是步骤条,下方是这一步的字段与“上一步 / 下一步 / 提交”。\n * 同 Ant Design ProComponents 的 StepsForm。\n *\n * <StepsForm\n * form={form}\n * _current={step}\n * steps={[\n * { title: '企业信息', fields: ['company', 'contact'], content: () => <>…form.Field…</> },\n * { title: '账号', fields: ['password', 'confirm'], content: () => <>…</> },\n * ]}\n * />\n *\n * - “下一步”只校验这一步的字段(form.validate(fields)),通过才往后走,没通过的错误立即显示;走过的步骤可以点回去。\n * - 最后一步是“提交”(表单的原生提交):整份校验没通过时跳到第一个有错的步骤,那一步在步骤条上标成出错。\n * - 换了一步焦点移到这一步的内容上(读屏念出第几步、标题),键盘接着往下填。\n *\n * 部件:Root(Form)、Steps、Panel(这一步的内容,div)、Actions(Flex)、Previous、Next、Submit(Button)。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { nextTick, useId, type VNodeChild } from 'vue';\nimport { Button, type ButtonProps } from '../base/Button';\nimport { useMessages } from '../config/lang/messages';\nimport { Form, type FormProps } from '../form/Form';\nimport type { FormControllerBase } from '../form/useForm';\nimport { Flex, type FlexProps } from '../layout/Flex';\nimport { Steps, type StepItem, type StepsProps } from '../navigation/Steps';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { patternsMessages } from './messages';\n\n/** 一步 */\nexport interface FormStep {\n /** 步骤条上的标题 */\n title: VNodeChild;\n /** 步骤条上的说明 */\n description?: VNodeChild;\n /** 这一步的字段路径(含子路径):“下一步”只校验它们,出错时这一步标成出错 */\n fields: readonly string[];\n /** 这一步的内容:form.Field 们 */\n content: () => VNodeChild;\n}\n\n/** 未声明的属性落到 Form(form 元素)上;标签布局、只读、不可用同 Form */\nexport interface StepsFormProps extends Omit<FormProps, 'controller' | 'children'> {\n /** useForm 的控制器 */\n form: FormControllerBase;\n /** 各步 */\n steps: readonly FormStep[];\n /** 当前第几步,从 0 开始:父级可以写 _current={x} */\n _current?: number;\n /** 提交按钮的文字,默认取文案(提交) */\n submitText?: VNodeChild;\n /** 步骤条(Steps)的部件属性 */\n propsSteps?: Partial<StepsProps>;\n /** 这一步内容外层的部件属性 */\n propsPanel?: PartProps<'div'>;\n /** 按钮行的部件属性 */\n propsActions?: Partial<FlexProps>;\n /** 上一步按钮的部件属性 */\n propsPrevious?: Partial<ButtonProps>;\n /** 下一步按钮的部件属性 */\n propsNext?: Partial<ButtonProps>;\n /** 提交按钮的部件属性 */\n propsSubmit?: Partial<ButtonProps>;\n}\n\n/** StepsForm 的样式:root、panel */\nexport const stepsFormStyles = css.create({\n root: { display: 'flex', flexDirection: 'column', gap: '_lg' },\n // 换步后焦点在这里:不画焦点环(内容本身不是控件),读屏念出它的名称\n panel: { display: 'flex', flexDirection: 'column', gap: '_md', outline: 'none' },\n});\n\n/** 字段 path 在这一步里:就是它,或是它的子路径(`address.city` 属于 `address`) */\nconst inStep = (step: FormStep, path: string) =>\n step.fields.some((field) => path === field || path.startsWith(`${field}.`));\n\n/** 分步表单:步骤条 + 这一步的字段 + 上一步、下一步、提交 */\nexport const StepsForm = _component(\n ({\n form,\n steps,\n _current: current = _bindable(0),\n submitText,\n propsSteps,\n propsPanel,\n propsActions,\n propsPrevious,\n propsNext,\n propsSubmit,\n ...rest\n }: StepsFormProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const id = useId();\n let panel = _state.raw<HTMLElement | null>(null);\n\n /** 这一步有显示着的错误 */\n const hasError = (step: FormStep) =>\n Object.keys(form.errors).some((path) => inStep(step, path) && form.visibleError(path));\n\n /** 换到第 index 步,焦点移到这一步的内容上 */\n function go(index: number) {\n current = index;\n void nextTick(() => panel?.focus());\n }\n\n async function next() {\n const step = steps[current];\n if (step && (await form.validate(step.fields))) go(current + 1);\n }\n\n // 整份提交没通过:跳到第一个有错的步骤\n _effect(() => {\n if (form.submitCount === 0 || form.submitting) return;\n const first = steps.findIndex(hasError);\n if (first >= 0 && first !== current && !hasError(steps[current]!)) go(first);\n });\n\n const items = (): StepItem[] =>\n steps.map((step, index) => ({\n title: step.title,\n description: step.description,\n status: hasError(step) ? 'error' : undefined,\n // 只能点回走过的步骤\n disabled: index > current,\n }));\n\n return () => {\n const step = steps[current];\n const last = current >= steps.length - 1;\n return (\n <Form {...rest} controller={form}>\n <div {...css.attrs(stepsFormStyles.root)}>\n <Steps\n {...mergePartProps<StepsProps>({ items: items() }, propsSteps, {\n items: items(),\n navigable: true,\n })}\n _current={current}\n onUpdate:_current={(index: number) => go(index)}\n />\n <div\n {...mergeAttrs<'div'>({ sx: stepsFormStyles.panel }, propsPanel, {\n ref: _toRef(panel),\n id: `${id}-panel`,\n tabindex: -1,\n role: 'group',\n 'aria-label': messages.stepsFormStep(current + 1, steps.length),\n })}\n >\n {step?.content()}\n </div>\n <Flex {...mergePartProps<FlexProps>({ justify: 'end', gap: 'sm' }, propsActions)}>\n {current > 0 && (\n <Button\n {...mergePartProps<ButtonProps>(\n { variant: 'ghost', tone: 'neutral' },\n propsPrevious,\n { onClick: () => go(current - 1) },\n )}\n >\n {messages.stepsFormPrevious}\n </Button>\n )}\n {last ? (\n <Button\n {...mergePartProps<ButtonProps>({}, propsSubmit, {\n type: 'submit',\n loading: form.submitting,\n })}\n >\n {submitText ?? messages.stepsFormSubmit}\n </Button>\n ) : (\n <Button\n {...mergePartProps<ButtonProps>({}, propsNext, {\n loading: form.validating(),\n onClick: () => void next(),\n })}\n >\n {messages.stepsFormNext}\n </Button>\n )}\n </Flex>\n </div>\n </Form>\n );\n };\n },\n);\n"],"mappings":";;;;;;;;;;;;;;AAoEA,IAAa,kBAAkB,IAAI,OAAO;CACxC,MAAM;EAAE,SAAS;EAAQ,eAAe;EAAU,KAAK;CAAM;CAE7D,OAAO;EAAE,SAAS;EAAQ,eAAe;EAAU,KAAK;EAAO,SAAS;CAAO;AACjF,CAAC;;AAGD,IAAM,UAAU,MAAgB,SAC9B,KAAK,OAAO,MAAM,UAAU,SAAS,SAAS,KAAK,WAAW,GAAG,MAAK,EAAG,CAAC;;AAG5E,IAAa,YAAY,0BACvB,QAAA,SAYsB;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,QAAAA,aAAA,QAAA,OAAA;CAAA,MAAA,UAAAC,SAAA,QAAA,kBATU,GAAC,KAAA,IAAA;CAAA,MAAA,aAAAD,aAAA,QAAA,YAAA;CAAA,MAAA,aAAAA,aAAA,QAAA,YAAA;CAAA,MAAA,aAAAA,aAAA,QAAA,YAAA;CAAA,MAAA,eAAAA,aAAA,QAAA,cAAA;CAAA,MAAA,gBAAAA,aAAA,QAAA,eAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAQ5B,OAAIE,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,WAAW,YAAY,YAAY,gBAAgB;CACzD,MAAM,KAAK,MAAM;CACjB,IAAI,QAAKC,WAAkC,IAAI;;CAG/C,MAAM,YAAY,SAChB,OAAO,KAAK,KAAA,MAAK,MAAM,CAAC,CAAC,MAAM,SAAS,OAAO,MAAM,IAAI,KAAK,KAAA,MAAK,aAAa,IAAI,CAAC;;CAGvF,SAAS,GAAG,OAAe;EACzB,QAAA,QAAU;EACV,eAAoB,MAAA,OAAO,MAAM,CAAC;CACpC;CAEA,eAAe,OAAO;EACpB,MAAM,OAAO,MAAA,MAAA,QAAA;EACb,IAAI,QAAS,MAAM,KAAA,MAAK,SAAS,KAAK,MAAM,GAAI,GAAG,QAAA,QAAU,CAAC;CAChE;CAGA,aAAc;EACZ,IAAI,KAAA,MAAK,gBAAgB,KAAK,KAAA,MAAK,YAAY;EAC/C,MAAM,QAAQ,MAAA,MAAM,UAAU,QAAQ;EACtC,IAAI,SAAS,KAAK,UAAK,QAAA,SAAgB,CAAC,SAAS,MAAA,MAAA,QAAA,MAAe,GAAG,GAAG,KAAK;CAC7E,CAAC;CAED,MAAM,cACJ,MAAA,MAAM,KAAK,MAAM,WAAW;EAC1B,OAAO,KAAK;EACZ,aAAa,KAAK;EAClB,QAAQ,SAAS,IAAI,IAAI,UAAU,KAAA;EAEnC,UAAU,QAAK,QAAA;CACjB,EAAE;CAEJ,aAAa;EACX,MAAM,OAAO,MAAA,MAAA,QAAA;EACb,MAAM,OAAO,QAAA,SAAW,MAAA,MAAM,SAAS;EACvC,OAAAC,YAAA,MAAAC,
|
|
1
|
+
{"version":3,"file":"StepsForm.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_shallowRef","_createVNode","_mergeProps"],"sources":["../../src/patterns/StepsForm.tsx"],"sourcesContent":["/**\n * 分步表单:一份 useForm 的表单分成几步填(开户、准入申请、配置向导),上方是步骤条,下方是这一步的字段与“上一步 / 下一步 / 提交”。\n * 同 Ant Design ProComponents 的 StepsForm。\n *\n * <StepsForm\n * form={form}\n * _current={step}\n * steps={[\n * { title: '企业信息', fields: ['company', 'contact'], content: () => <>…form.Field…</> },\n * { title: '账号', fields: ['password', 'confirm'], content: () => <>…</> },\n * ]}\n * />\n *\n * - “下一步”只校验这一步的字段(form.validate(fields)),通过才往后走,没通过的错误立即显示;走过的步骤可以点回去。\n * - 最后一步是“提交”(表单的原生提交):整份校验没通过时跳到第一个有错的步骤,那一步在步骤条上标成出错。\n * - 换了一步焦点移到这一步的内容上(读屏念出第几步、标题),键盘接着往下填。\n *\n * 部件:Root(Form)、Steps、Panel(这一步的内容,div)、Actions(Flex)、Previous、Next、Submit(Button)。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { nextTick, useId, type VNodeChild } from 'vue';\nimport { Button, type ButtonProps } from '../base/Button';\nimport { useMessages } from '../config/lang/messages';\nimport { Form, type FormProps } from '../form/Form';\nimport type { FormControllerBase } from '../form/useForm';\nimport { Flex, type FlexProps } from '../layout/Flex';\nimport { Steps, type StepItem, type StepsProps } from '../navigation/Steps';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { patternsMessages } from './messages';\n\n/** 一步 */\nexport interface FormStep {\n /** 步骤条上的标题 */\n title: VNodeChild;\n /** 步骤条上的说明 */\n description?: VNodeChild;\n /** 这一步的字段路径(含子路径):“下一步”只校验它们,出错时这一步标成出错 */\n fields: readonly string[];\n /** 这一步的内容:form.Field 们 */\n content: () => VNodeChild;\n}\n\n/** 未声明的属性落到 Form(form 元素)上;标签布局、只读、不可用同 Form */\nexport interface StepsFormProps extends Omit<FormProps, 'controller' | 'children'> {\n /** useForm 的控制器 */\n form: FormControllerBase;\n /** 各步 */\n steps: readonly FormStep[];\n /** 当前第几步,从 0 开始:父级可以写 _current={x} */\n _current?: number;\n /** 提交按钮的文字,默认取文案(提交) */\n submitText?: VNodeChild;\n /** 步骤条(Steps)的部件属性 */\n propsSteps?: Partial<StepsProps>;\n /** 这一步内容外层的部件属性 */\n propsPanel?: PartProps<'div'>;\n /** 按钮行的部件属性 */\n propsActions?: Partial<FlexProps>;\n /** 上一步按钮的部件属性 */\n propsPrevious?: Partial<ButtonProps>;\n /** 下一步按钮的部件属性 */\n propsNext?: Partial<ButtonProps>;\n /** 提交按钮的部件属性 */\n propsSubmit?: Partial<ButtonProps>;\n}\n\n/** StepsForm 的样式:root、panel */\nexport const stepsFormStyles = css.create({\n root: { display: 'flex', flexDirection: 'column', gap: '_lg' },\n // 换步后焦点在这里:不画焦点环(内容本身不是控件),读屏念出它的名称\n panel: { display: 'flex', flexDirection: 'column', gap: '_md', outline: 'none' },\n});\n\n/** 字段 path 在这一步里:就是它,或是它的子路径(`address.city` 属于 `address`) */\nconst inStep = (step: FormStep, path: string) =>\n step.fields.some((field) => path === field || path.startsWith(`${field}.`));\n\n/** 分步表单:步骤条 + 这一步的字段 + 上一步、下一步、提交 */\nexport const StepsForm = _component(\n ({\n form,\n steps,\n _current: current = _bindable(0),\n submitText,\n propsSteps,\n propsPanel,\n propsActions,\n propsPrevious,\n propsNext,\n propsSubmit,\n ...rest\n }: StepsFormProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const id = useId();\n let panel = _state.raw<HTMLElement | null>(null);\n\n /** 这一步有显示着的错误 */\n const hasError = (step: FormStep) =>\n Object.keys(form.errors).some((path) => inStep(step, path) && form.visibleError(path));\n\n /** 换到第 index 步,焦点移到这一步的内容上 */\n function go(index: number) {\n current = index;\n void nextTick(() => panel?.focus());\n }\n\n async function next() {\n const step = steps[current];\n if (step && (await form.validate(step.fields))) go(current + 1);\n }\n\n // 整份提交没通过:跳到第一个有错的步骤\n _effect(() => {\n if (form.submitCount === 0 || form.submitting) return;\n const first = steps.findIndex(hasError);\n if (first >= 0 && first !== current && !hasError(steps[current]!)) go(first);\n });\n\n const items = (): StepItem[] =>\n steps.map((step, index) => ({\n title: step.title,\n description: step.description,\n status: hasError(step) ? 'error' : undefined,\n // 只能点回走过的步骤\n disabled: index > current,\n }));\n\n return () => {\n const step = steps[current];\n const last = current >= steps.length - 1;\n return (\n <Form {...rest} controller={form}>\n <div {...css.attrs(stepsFormStyles.root)}>\n <Steps\n {...mergePartProps<StepsProps>({ items: items() }, propsSteps, {\n items: items(),\n navigable: true,\n })}\n _current={current}\n onUpdate:_current={(index: number) => go(index)}\n />\n <div\n {...mergeAttrs<'div'>({ sx: stepsFormStyles.panel }, propsPanel, {\n ref: _toRef(panel),\n id: `${id}-panel`,\n tabindex: -1,\n role: 'group',\n 'aria-label': messages.stepsFormStep(current + 1, steps.length),\n })}\n >\n {step?.content()}\n </div>\n <Flex {...mergePartProps<FlexProps>({ justify: 'end', gap: 'sm' }, propsActions)}>\n {current > 0 && (\n <Button\n {...mergePartProps<ButtonProps>(\n { variant: 'ghost', tone: 'neutral' },\n propsPrevious,\n { onClick: () => go(current - 1) },\n )}\n >\n {messages.stepsFormPrevious}\n </Button>\n )}\n {last ? (\n <Button\n {...mergePartProps<ButtonProps>({}, propsSubmit, {\n type: 'submit',\n loading: form.submitting,\n })}\n >\n {submitText ?? messages.stepsFormSubmit}\n </Button>\n ) : (\n <Button\n {...mergePartProps<ButtonProps>({}, propsNext, {\n loading: form.validating(),\n onClick: () => void next(),\n })}\n >\n {messages.stepsFormNext}\n </Button>\n )}\n </Flex>\n </div>\n </Form>\n );\n };\n },\n);\n"],"mappings":";;;;;;;;;;;;;;AAoEA,IAAa,kBAAkB,IAAI,OAAO;CACxC,MAAM;EAAE,SAAS;EAAQ,eAAe;EAAU,KAAK;CAAM;CAE7D,OAAO;EAAE,SAAS;EAAQ,eAAe;EAAU,KAAK;EAAO,SAAS;CAAO;AACjF,CAAC;;AAGD,IAAM,UAAU,MAAgB,SAC9B,KAAK,OAAO,MAAM,UAAU,SAAS,SAAS,KAAK,WAAW,GAAG,MAAK,EAAG,CAAC;;AAG5E,IAAa,YAAY,0BACvB,QAAA,SAYsB;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,QAAAA,aAAA,QAAA,OAAA;CAAA,MAAA,UAAAC,SAAA,QAAA,kBATU,GAAC,KAAA,IAAA;CAAA,MAAA,aAAAD,aAAA,QAAA,YAAA;CAAA,MAAA,aAAAA,aAAA,QAAA,YAAA;CAAA,MAAA,aAAAA,aAAA,QAAA,YAAA;CAAA,MAAA,eAAAA,aAAA,QAAA,cAAA;CAAA,MAAA,gBAAAA,aAAA,QAAA,eAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAQ5B,OAAIE,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,WAAW,YAAY,YAAY,gBAAgB;CACzD,MAAM,KAAK,MAAM;CACjB,IAAI,QAAKC,WAAkC,IAAI;;CAG/C,MAAM,YAAY,SAChB,OAAO,KAAK,KAAA,MAAK,MAAM,CAAC,CAAC,MAAM,SAAS,OAAO,MAAM,IAAI,KAAK,KAAA,MAAK,aAAa,IAAI,CAAC;;CAGvF,SAAS,GAAG,OAAe;EACzB,QAAA,QAAU;EACV,eAAoB,MAAA,OAAO,MAAM,CAAC;CACpC;CAEA,eAAe,OAAO;EACpB,MAAM,OAAO,MAAA,MAAA,QAAA;EACb,IAAI,QAAS,MAAM,KAAA,MAAK,SAAS,KAAK,MAAM,GAAI,GAAG,QAAA,QAAU,CAAC;CAChE;CAGA,aAAc;EACZ,IAAI,KAAA,MAAK,gBAAgB,KAAK,KAAA,MAAK,YAAY;EAC/C,MAAM,QAAQ,MAAA,MAAM,UAAU,QAAQ;EACtC,IAAI,SAAS,KAAK,UAAK,QAAA,SAAgB,CAAC,SAAS,MAAA,MAAA,QAAA,MAAe,GAAG,GAAG,KAAK;CAC7E,CAAC;CAED,MAAM,cACJ,MAAA,MAAM,KAAK,MAAM,WAAW;EAC1B,OAAO,KAAK;EACZ,aAAa,KAAK;EAClB,QAAQ,SAAS,IAAI,IAAI,UAAU,KAAA;EAEnC,UAAU,QAAK,QAAA;CACjB,EAAE;CAEJ,aAAa;EACX,MAAM,OAAO,MAAA,MAAA,QAAA;EACb,MAAM,OAAO,QAAA,SAAW,MAAA,MAAM,SAAS;EACvC,OAAAC,YAAA,MAAAC,cACY,MAAI,EAAA,cAAA,KAAA,MAAA,CAAA,GAAA,EAAA,eAAA,CAAAD,YAAA,OACH,IAAI,MAAM,gBAAgB,IAAI,GAAC;GAAAA,YAAA,OAAAC,cAEhC,eAA2B,EAAE,OAAO,MAAM,EAAE,GAAC,WAAA,OAAc;IAC7D,OAAO,MAAM;IACb,WAAW;GACb,CAAC,GAAC;IAAA,YAAA,QAAA;IAAA,qBAAA,OAAA,OAAA,OAAA,MAEkB,UAAkB,GAAG,KAAK;GAAC,CAAA,GAAA,IAAA;GAAAD,YAAA,OAG3C,WAAkB,EAAE,IAAI,gBAAgB,MAAM,GAAC,WAAA,OAAc;IAC/D,KAAG;IACH,IAAI,GAAG,GAAE;IACT,UAAU;IACV,MAAM;IACN,cAAc,SAAS,cAAc,QAAA,QAAU,GAAG,MAAA,MAAM,MAAM;GAChE,CAAC,GAAC,CAED,MAAM,QAAQ,CAAC,CAAA;GAAAA,YAAA,MAER,eAA0B;IAAE,SAAS;IAAO,KAAK;GAAK,GAAC,aAAA,KAAc,GAAC,EAAA,eAAA,CAC7E,QAAA,QAAU,KAACA,YAAA,QAAAC,cAEJ,eACF;IAAE,SAAS;IAAS,MAAM;GAAU,GAAC,cAAA,OAErC,EAAE,eAAe,GAAG,QAAA,QAAU,CAAC,EAAE,CACnC,GAAC,EAAA,OAAA,UAAA,CAAA,GAAA;IAAA,eAAA,CAEA,SAAS,iBAAiB;IAAA,SAAA;GAAA,CAAA,GAG9B,OAAID,YAAA,QAAAC,cAEG,eAA4B,CAAC,GAAC,YAAA,OAAe;IAC/C,MAAM;IACN,SAAS,KAAA,MAAK;GAChB,CAAC,GAAC,EAAA,OAAA,UAAA,CAAA,GAAA;IAAA,eAAA,CAED,WAAA,SAAc,SAAS,eAAe;IAAA,SAAA;GAAA,CAAA,IAAAD,YAAA,QAAAC,cAInC,eAA4B,CAAC,GAAC,UAAA,OAAa;IAC7C,SAAS,KAAA,MAAK,WAAW;IACzB,eAAe,KAAK,KAAK;GAC3B,CAAC,GAAC,EAAA,OAAA,UAAA,CAAA,GAAA;IAAA,eAAA,CAED,SAAS,aAAa;IAAA,SAAA;GAAA,CAAA,CAE1B,EAAA,CAAA;EAAA,CAAA,CAAA,EAAA,CAAA;CAKX;AACF,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
|
@@ -7,8 +7,8 @@ import { SYSTEM_THEME } from "../config/theme/boot.js";
|
|
|
7
7
|
import { builtinThemeLabel, builtinThemes } from "../config/theme/builtin.js";
|
|
8
8
|
import { useMessages } from "../config/lang/messages.js";
|
|
9
9
|
import { patternsMessages } from "./messages.js";
|
|
10
|
-
import { createVNode, isVNode
|
|
11
|
-
import { _component, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
10
|
+
import { createVNode, isVNode } from "vue";
|
|
11
|
+
import { _component, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
12
12
|
import { Citrus, Coffee, Contrast, Flower2, Monitor, Moon, MoonStar, Palette, PartyPopper, ScrollText, Sun, Trees, Waves, Zap } from "@lucide/icons";
|
|
13
13
|
//#region src/patterns/ThemeSwitcher.tsx
|
|
14
14
|
/**
|
|
@@ -64,7 +64,7 @@ var ThemeSwitcher = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
64
64
|
return () => {
|
|
65
65
|
let _slot2;
|
|
66
66
|
const choices = system.value ? [SYSTEM_THEME, ...preference.value.names] : [...preference.value.names];
|
|
67
|
-
return createVNode(Menu$1,
|
|
67
|
+
return createVNode(Menu$1, mergeJsxProps(rest, { "trigger": _cache[0] || (_cache[0] = (props) => createVNode(IconButton, mergePartProps({
|
|
68
68
|
icon: iconOf(preference.value.preference),
|
|
69
69
|
"aria-label": messages.themeLabel
|
|
70
70
|
}, propsTrigger.value, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeSwitcher.js","names":["_isVNode","_vtp_readonlyProp","_vtp_restProps","_createVNode","Menu","_mergeProps"],"sources":["../../src/patterns/ThemeSwitcher.tsx"],"sourcesContent":["/**\n * 主题切换:图标按钮弹出菜单,列出“跟随系统”与各套主题,配合 createThemePreference 使用。\n *\n * const preference = createThemePreference({ themes: { light: lightTheme, dark: darkTheme, paper: paperTheme } });\n * <ThemeSwitcher preference={preference} />\n *\n * 选中后新主题从按钮处圆形展开(revealTransition;不支持 View Transitions 或减少动态效果时直接切换)。\n * 按钮的图标表示当前的选择;内置主题(builtinThemes)与跟随系统有默认的文字和图标,其他主题用 labels、icons 补上。\n */\nimport {\n Citrus,\n Coffee,\n Contrast,\n Flower2,\n Monitor,\n Moon,\n MoonStar,\n Palette,\n PartyPopper,\n ScrollText,\n Sun,\n Trees,\n Waves,\n Zap,\n} from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { IconButton, type IconButtonProps } from '../base/IconButton';\nimport { useLang } from '../config/context';\nimport { useMessages } from '../config/lang/messages';\nimport { patternsMessages } from './messages';\nimport type { IconData } from '../base/Icon';\nimport { Menu, MenuRadioGroup, MenuRadioItem, type MenuProps } from '../overlay/Menu';\nimport { toElement } from '../utils/dom/element';\nimport { mergePartProps } from '../utils/props';\nimport { SYSTEM_THEME } from '../config/theme/boot';\nimport { builtinThemeLabel, builtinThemes, type BuiltinThemeName } from '../config/theme/builtin';\nimport type { ThemePreference } from '../config/theme/preference';\n\n/** 未声明的属性落到菜单(role=\"menu\" 的 div)上 */\nexport interface ThemeSwitcherProps<Name extends string> extends Omit<\n MenuProps,\n 'trigger' | 'children'\n> {\n /** createThemePreference 的结果 */\n preference: ThemePreference<Name>;\n /** 各选项的文字(键是主题名或 'system');内置主题与跟随系统默认取语言包,其他默认是主题名 */\n labels?: Partial<Record<Name | typeof SYSTEM_THEME, string>>;\n /** 各选项的图标;内置主题与跟随系统有默认图标,其他默认是调色板 */\n icons?: Partial<Record<Name | typeof SYSTEM_THEME, IconData>>;\n /** 列出“跟随系统”,默认 true */\n system?: boolean;\n /** 触发按钮的部件属性 */\n propsTrigger?: Partial<IconButtonProps>;\n}\n\n// 每套内置主题都要有图标:加主题时漏了这里类型检查会报错\nconst DEFAULT_ICONS: Record<BuiltinThemeName | typeof SYSTEM_THEME, IconData> = {\n [SYSTEM_THEME]: Monitor,\n light: Sun,\n dark: Moon,\n paper: ScrollText,\n midnight: MoonStar,\n 'high-contrast': Contrast,\n ocean: Waves,\n forest: Trees,\n lemon: Citrus,\n sakura: Flower2,\n festive: PartyPopper,\n cyberpunk: Zap,\n coffee: Coffee,\n};\n\n/** 主题切换(图标按钮 + 单选菜单):选中后从按钮处圆形展开新主题 */\nexport const ThemeSwitcher = _component(\n <Name extends string>({\n preference,\n labels,\n icons,\n system = true,\n propsTrigger,\n ...rest\n }: ThemeSwitcherProps<Name>) => {\n const lang = useLang();\n const messages = useMessages('patterns', patternsMessages);\n let button: HTMLElement | null = null;\n\n const labelOf = (name: string) =>\n (labels as Record<string, string | undefined> | undefined)?.[name] ??\n (name === SYSTEM_THEME\n ? lang.themeSystem\n : name in builtinThemes\n ? builtinThemeLabel(lang, name as BuiltinThemeName)\n : name);\n const iconOf = (name: string) =>\n (icons as Record<string, IconData | undefined> | undefined)?.[name] ??\n (DEFAULT_ICONS as Record<string, IconData | undefined>)[name] ??\n Palette;\n\n function choose(next: string) {\n const rect = button?.getBoundingClientRect();\n const origin = rect && { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };\n void preference.set(next as Name, { origin });\n }\n\n return () => {\n const choices: string[] = system\n ? [SYSTEM_THEME, ...preference.names]\n : [...preference.names];\n return (\n <Menu\n {...rest}\n trigger={(props) => (\n <IconButton\n {...mergePartProps<IconButtonProps>(\n { icon: iconOf(preference.preference), 'aria-label': messages.themeLabel },\n propsTrigger,\n {\n ...props,\n ref: (value: unknown) => {\n props.ref(value);\n button = toElement(value);\n },\n },\n )}\n />\n )}\n >\n <MenuRadioGroup\n _value={preference.preference}\n onUpdate:_value={choose}\n aria-label={messages.themeLabel}\n >\n {choices.map((name) => (\n <MenuRadioItem key={name} value={name} icon={iconOf(name)}>\n {labelOf(name)}\n </MenuRadioItem>\n ))}\n </MenuRadioGroup>\n </Menu>\n );\n };\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAA,QAAA,GAAA;CAAA,OAAA,OAAA,MAAA,cAAA,OAAA,UAAA,SAAA,KAAA,CAAA,MAAA,qBAAA,CAAAA,QAAA,CAAA;AAAA;AAkBA,IAAM,gBAA0E;EAC7E,eAAe;CAChB,OAAO;CACP,MAAM;CACN,OAAO;CACP,UAAU;CACV,iBAAiB;CACjB,OAAO;CACP,QAAQ;CACR,OAAO;CACP,QAAQ;CACR,SAAS;CACT,WAAW;CACX,QAAQ;AACV;;AAGA,IAAa,gBAAgB,0BAC3B,QAAA,SAOgC;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,aAAAC,aAAA,QAAA,YAAA;CAAA,MAAA,SAAAA,aAAA,QAAA,QAAA;CAAA,MAAA,QAAAA,aAAA,QAAA,OAAA;CAAA,MAAA,SAAAA,aAAA,QAAA,UAHrB,IAAI;CAAA,MAAA,eAAAA,aAAA,QAAA,cAAA;CAAA,MAEV,OAAIC,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,OAAO,QAAQ;CACrB,MAAM,WAAW,YAAY,YAAY,gBAAgB;CACzD,IAAI,SAA6B;CAEjC,MAAM,WAAW,SACf,OAAA,QAA6D,UAC5D,SAAA,WACG,KAAK,cACL,QAAQ,gBACN,kBAAkB,MAAM,IAAwB,IAChD;CACR,MAAM,UAAU,SACd,MAAA,QAA8D,SAC7D,cAAuD,SACxD;CAEF,SAAS,OAAO,MAAc;EAC5B,MAAM,OAAO,QAAQ,sBAAsB;EAC3C,MAAM,SAAS,QAAQ;GAAE,GAAG,KAAK,OAAO,KAAK,QAAQ;GAAG,GAAG,KAAK,MAAM,KAAK,SAAS;EAAE;EACtF,WAAK,MAAW,IAAI,MAAc,EAAE,OAAO,CAAC;CAC9C;CAEA,aAAa;EAAA,IAAA;EACX,MAAM,UAAoB,OAAA,QACtB,CAAC,cAAc,GAAG,WAAA,MAAW,KAAK,IAClC,CAAC,GAAG,WAAA,MAAW,KAAK;EACxB,OAAAC,YAAAC,QAAAC,
|
|
1
|
+
{"version":3,"file":"ThemeSwitcher.js","names":["_isVNode","_vtp_readonlyProp","_vtp_restProps","_createVNode","Menu","_mergeProps"],"sources":["../../src/patterns/ThemeSwitcher.tsx"],"sourcesContent":["/**\n * 主题切换:图标按钮弹出菜单,列出“跟随系统”与各套主题,配合 createThemePreference 使用。\n *\n * const preference = createThemePreference({ themes: { light: lightTheme, dark: darkTheme, paper: paperTheme } });\n * <ThemeSwitcher preference={preference} />\n *\n * 选中后新主题从按钮处圆形展开(revealTransition;不支持 View Transitions 或减少动态效果时直接切换)。\n * 按钮的图标表示当前的选择;内置主题(builtinThemes)与跟随系统有默认的文字和图标,其他主题用 labels、icons 补上。\n */\nimport {\n Citrus,\n Coffee,\n Contrast,\n Flower2,\n Monitor,\n Moon,\n MoonStar,\n Palette,\n PartyPopper,\n ScrollText,\n Sun,\n Trees,\n Waves,\n Zap,\n} from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { IconButton, type IconButtonProps } from '../base/IconButton';\nimport { useLang } from '../config/context';\nimport { useMessages } from '../config/lang/messages';\nimport { patternsMessages } from './messages';\nimport type { IconData } from '../base/Icon';\nimport { Menu, MenuRadioGroup, MenuRadioItem, type MenuProps } from '../overlay/Menu';\nimport { toElement } from '../utils/dom/element';\nimport { mergePartProps } from '../utils/props';\nimport { SYSTEM_THEME } from '../config/theme/boot';\nimport { builtinThemeLabel, builtinThemes, type BuiltinThemeName } from '../config/theme/builtin';\nimport type { ThemePreference } from '../config/theme/preference';\n\n/** 未声明的属性落到菜单(role=\"menu\" 的 div)上 */\nexport interface ThemeSwitcherProps<Name extends string> extends Omit<\n MenuProps,\n 'trigger' | 'children'\n> {\n /** createThemePreference 的结果 */\n preference: ThemePreference<Name>;\n /** 各选项的文字(键是主题名或 'system');内置主题与跟随系统默认取语言包,其他默认是主题名 */\n labels?: Partial<Record<Name | typeof SYSTEM_THEME, string>>;\n /** 各选项的图标;内置主题与跟随系统有默认图标,其他默认是调色板 */\n icons?: Partial<Record<Name | typeof SYSTEM_THEME, IconData>>;\n /** 列出“跟随系统”,默认 true */\n system?: boolean;\n /** 触发按钮的部件属性 */\n propsTrigger?: Partial<IconButtonProps>;\n}\n\n// 每套内置主题都要有图标:加主题时漏了这里类型检查会报错\nconst DEFAULT_ICONS: Record<BuiltinThemeName | typeof SYSTEM_THEME, IconData> = {\n [SYSTEM_THEME]: Monitor,\n light: Sun,\n dark: Moon,\n paper: ScrollText,\n midnight: MoonStar,\n 'high-contrast': Contrast,\n ocean: Waves,\n forest: Trees,\n lemon: Citrus,\n sakura: Flower2,\n festive: PartyPopper,\n cyberpunk: Zap,\n coffee: Coffee,\n};\n\n/** 主题切换(图标按钮 + 单选菜单):选中后从按钮处圆形展开新主题 */\nexport const ThemeSwitcher = _component(\n <Name extends string>({\n preference,\n labels,\n icons,\n system = true,\n propsTrigger,\n ...rest\n }: ThemeSwitcherProps<Name>) => {\n const lang = useLang();\n const messages = useMessages('patterns', patternsMessages);\n let button: HTMLElement | null = null;\n\n const labelOf = (name: string) =>\n (labels as Record<string, string | undefined> | undefined)?.[name] ??\n (name === SYSTEM_THEME\n ? lang.themeSystem\n : name in builtinThemes\n ? builtinThemeLabel(lang, name as BuiltinThemeName)\n : name);\n const iconOf = (name: string) =>\n (icons as Record<string, IconData | undefined> | undefined)?.[name] ??\n (DEFAULT_ICONS as Record<string, IconData | undefined>)[name] ??\n Palette;\n\n function choose(next: string) {\n const rect = button?.getBoundingClientRect();\n const origin = rect && { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };\n void preference.set(next as Name, { origin });\n }\n\n return () => {\n const choices: string[] = system\n ? [SYSTEM_THEME, ...preference.names]\n : [...preference.names];\n return (\n <Menu\n {...rest}\n trigger={(props) => (\n <IconButton\n {...mergePartProps<IconButtonProps>(\n { icon: iconOf(preference.preference), 'aria-label': messages.themeLabel },\n propsTrigger,\n {\n ...props,\n ref: (value: unknown) => {\n props.ref(value);\n button = toElement(value);\n },\n },\n )}\n />\n )}\n >\n <MenuRadioGroup\n _value={preference.preference}\n onUpdate:_value={choose}\n aria-label={messages.themeLabel}\n >\n {choices.map((name) => (\n <MenuRadioItem key={name} value={name} icon={iconOf(name)}>\n {labelOf(name)}\n </MenuRadioItem>\n ))}\n </MenuRadioGroup>\n </Menu>\n );\n };\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAA,QAAA,GAAA;CAAA,OAAA,OAAA,MAAA,cAAA,OAAA,UAAA,SAAA,KAAA,CAAA,MAAA,qBAAA,CAAAA,QAAA,CAAA;AAAA;AAkBA,IAAM,gBAA0E;EAC7E,eAAe;CAChB,OAAO;CACP,MAAM;CACN,OAAO;CACP,UAAU;CACV,iBAAiB;CACjB,OAAO;CACP,QAAQ;CACR,OAAO;CACP,QAAQ;CACR,SAAS;CACT,WAAW;CACX,QAAQ;AACV;;AAGA,IAAa,gBAAgB,0BAC3B,QAAA,SAOgC;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,aAAAC,aAAA,QAAA,YAAA;CAAA,MAAA,SAAAA,aAAA,QAAA,QAAA;CAAA,MAAA,QAAAA,aAAA,QAAA,OAAA;CAAA,MAAA,SAAAA,aAAA,QAAA,UAHrB,IAAI;CAAA,MAAA,eAAAA,aAAA,QAAA,cAAA;CAAA,MAEV,OAAIC,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,OAAO,QAAQ;CACrB,MAAM,WAAW,YAAY,YAAY,gBAAgB;CACzD,IAAI,SAA6B;CAEjC,MAAM,WAAW,SACf,OAAA,QAA6D,UAC5D,SAAA,WACG,KAAK,cACL,QAAQ,gBACN,kBAAkB,MAAM,IAAwB,IAChD;CACR,MAAM,UAAU,SACd,MAAA,QAA8D,SAC7D,cAAuD,SACxD;CAEF,SAAS,OAAO,MAAc;EAC5B,MAAM,OAAO,QAAQ,sBAAsB;EAC3C,MAAM,SAAS,QAAQ;GAAE,GAAG,KAAK,OAAO,KAAK,QAAQ;GAAG,GAAG,KAAK,MAAM,KAAK,SAAS;EAAE;EACtF,WAAK,MAAW,IAAI,MAAc,EAAE,OAAO,CAAC;CAC9C;CAEA,aAAa;EAAA,IAAA;EACX,MAAM,UAAoB,OAAA,QACtB,CAAC,cAAc,GAAG,WAAA,MAAW,KAAK,IAClC,CAAC,GAAG,WAAA,MAAW,KAAK;EACxB,OAAAC,YAAAC,QAAAC,cAEQ,MAAI,EAAA,WAAA,OAAA,OAAA,OAAA,MACE,UAAKF,YAAA,YAEP,eACF;GAAE,MAAM,OAAO,WAAA,MAAW,UAAU;GAAG,cAAc,SAAS;EAAW,GAAC,aAAA,OAE1E;GACE,GAAG;GACH,MAAM,UAAmB;IACvB,MAAM,IAAI,KAAK;IACf,SAAS,UAAU,KAAK;GAC1B;EACF,CACF,GAAC,IAAA,GAEJ,CAAA,GAAA,EAAA,eAAA,CAAAA,YAAA,gBAAA;GAAA,UAGS,WAAA,MAAW;GAAU,mBACZ;GAAM,cACX,SAAS;EAAU,GAAA,QAAA,SAE9B,QAAQ,KAAK,SAAI;GAAA,IAAA;GAAA,OAAAA,YAAA,eAAA;IAAA,OACI;IAAI,SAAS;IAAI,QAAQ,OAAO,IAAI;GAAC,GAAA,QAAA,QACtD,QAAQ,IAAI,CAAC,IAAA,QAAA,EAAA,eAAA,CAAA,KAAA,EAAA,CAAA;EAAA,CAEjB,CAAC,IAAA,SAAA,EAAA,eAAA,CAAA,MAAA,EAAA,CAAA,CAAA,EAAA,CAAA;CAIV;AACF,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
package/dist/patterns/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 页面组合(子路径 `@zerodep-dev/vue-tsx-pro-ui/patterns`):用核心组件拼好的页面级部件,后台应用直接拿来搭页面:
|
|
3
3
|
* 应用外壳 AppShell、页签区 PageTabs、查询表格 QueryTable、分步表单 StepsForm、数据导入 DataImport、吸底操作栏 ActionBar、筛选条 FilterBar、通知中心 NotificationCenter、
|
|
4
|
-
* 留言 CommentThread、看板 KanbanBoard、时限标记 DeadlineBadge、主题切换 ThemeSwitcher、命令式的表单对话框 useFormDialog。
|
|
4
|
+
* 留言 CommentThread、看板 KanbanBoard、时限标记 DeadlineBadge、主题切换 ThemeSwitcher、布局切换 LayoutSwitcher、认证页版式 AuthLayout、拼图滑块验证 SlideCaptcha、命令式的表单对话框 useFormDialog。
|
|
5
5
|
*
|
|
6
6
|
* import { AppShell, FilterBar } from '@zerodep-dev/vue-tsx-pro-ui/patterns';
|
|
7
7
|
*
|
|
@@ -9,15 +9,19 @@
|
|
|
9
9
|
* 文案自带中英文(messages.ts),别的语言在 Lang.messages.patterns 里补。
|
|
10
10
|
*/
|
|
11
11
|
export { ActionBar, actionBarStyles, type ActionBarProps } from './ActionBar';
|
|
12
|
-
export { AppShell, AppShellBrand, appShellBrandStyles, AppShellContext, appShellDynamicStyles, appShellOffset, appShellStyles, type AppShellBrandProps, type AppShellProps, type AppShellSidebarModes, type AppShellState, } from './AppShell';
|
|
12
|
+
export { AppShell, AppShellBrand, appShellBrandStyles, AppShellContext, appShellDynamicStyles, appShellOffset, appShellStyles, type AppShellBrandProps, type AppShellProps, type AppShellSidebarModes, type AppShellSidebarSides, type AppShellState, } from './AppShell';
|
|
13
|
+
export { AuthLayout, authLayoutStyles, type AuthLayoutProps } from './AuthLayout';
|
|
13
14
|
export { CommentComposer, commentStyles, CommentThread, type CommentAttachment, type CommentAuthor, type CommentComposerProps, type CommentDraft, type CommentItem, type CommentThreadProps, } from './CommentThread';
|
|
14
15
|
export { DataImport, dataImportStyles, type DataImportProps, type ImportField } from './DataImport';
|
|
15
16
|
export { DeadlineBadge, type DeadlineBadgeProps } from './DeadlineBadge';
|
|
16
17
|
export { FilterBar, filterBarColumnStyles, filterBarStyles, type FilterBarManaged, type FilterBarProps, } from './FilterBar';
|
|
18
|
+
export { FormDialog, type FormDialogProps } from './FormDialog';
|
|
17
19
|
export { KanbanBoard, kanbanBoardDynamicStyles, kanbanBoardStyles, type KanbanBoardProps, type KanbanCardState, type KanbanColumn, } from './KanbanBoard';
|
|
20
|
+
export { LayoutSwitcher, type LayoutSwitcherProps } from './LayoutSwitcher';
|
|
18
21
|
export { NotificationCenter, notificationCenterStyles, NotificationList, notificationListStyles, type NotificationCenterProps, type NotificationItem, type NotificationListOptions, type NotificationListProps, type NotificationType, } from './NotificationCenter';
|
|
19
|
-
export { PageTabs, type PageTab, type PageTabsProps } from './PageTabs';
|
|
22
|
+
export { PageTabs, type PageTab, type PageTabsProps, type PageTabsSource } from './PageTabs';
|
|
20
23
|
export { QueryTable, queryTableStyles, type QueryTableProps } from './QueryTable';
|
|
24
|
+
export { SlideCaptcha, slideCaptchaDynamicStyles, slideCaptchaStyles, type SlideCaptchaProps, type SlideChallenge, type SlideResult, type SlideStatus, } from './SlideCaptcha';
|
|
21
25
|
export { StepsForm, stepsFormStyles, type FormStep, type StepsFormProps } from './StepsForm';
|
|
22
26
|
export { ThemeSwitcher, type ThemeSwitcherProps } from './ThemeSwitcher';
|
|
23
27
|
export { useFormDialog, type FormDialogField, type FormDialogOptions } from './form-dialog';
|
package/dist/patterns/index.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { ActionBar, actionBarStyles } from "./ActionBar.js";
|
|
2
2
|
import { patternsMessages } from "./messages.js";
|
|
3
3
|
import { AppShell, AppShellBrand, AppShellContext, appShellBrandStyles, appShellDynamicStyles, appShellOffset, appShellStyles } from "./AppShell.js";
|
|
4
|
+
import { AuthLayout, authLayoutStyles } from "./AuthLayout.js";
|
|
4
5
|
import { CommentComposer, CommentThread, commentStyles } from "./CommentThread.js";
|
|
5
6
|
import { DataImport, dataImportStyles } from "./DataImport.js";
|
|
6
7
|
import { DeadlineBadge } from "./DeadlineBadge.js";
|
|
7
8
|
import { FilterBar, filterBarColumnStyles, filterBarStyles } from "./FilterBar.js";
|
|
9
|
+
import { FormDialog } from "./FormDialog.js";
|
|
8
10
|
import { KanbanBoard, kanbanBoardDynamicStyles, kanbanBoardStyles } from "./KanbanBoard.js";
|
|
11
|
+
import { LayoutSwitcher } from "./LayoutSwitcher.js";
|
|
9
12
|
import { NotificationCenter, NotificationList, notificationCenterStyles, notificationListStyles } from "./NotificationCenter.js";
|
|
10
13
|
import { PageTabs } from "./PageTabs.js";
|
|
11
14
|
import { QueryTable, queryTableStyles } from "./QueryTable.js";
|
|
15
|
+
import { SlideCaptcha, slideCaptchaDynamicStyles, slideCaptchaStyles } from "./SlideCaptcha.js";
|
|
12
16
|
import { StepsForm, stepsFormStyles } from "./StepsForm.js";
|
|
13
17
|
import { ThemeSwitcher } from "./ThemeSwitcher.js";
|
|
14
18
|
import { useFormDialog } from "./form-dialog.js";
|
|
15
|
-
export { ActionBar, AppShell, AppShellBrand, AppShellContext, CommentComposer, CommentThread, DataImport, DeadlineBadge, FilterBar, KanbanBoard, NotificationCenter, NotificationList, PageTabs, QueryTable, StepsForm, ThemeSwitcher, actionBarStyles, appShellBrandStyles, appShellDynamicStyles, appShellOffset, appShellStyles, commentStyles, dataImportStyles, filterBarColumnStyles, filterBarStyles, kanbanBoardDynamicStyles, kanbanBoardStyles, notificationCenterStyles, notificationListStyles, patternsMessages, queryTableStyles, stepsFormStyles, useFormDialog };
|
|
19
|
+
export { ActionBar, AppShell, AppShellBrand, AppShellContext, AuthLayout, CommentComposer, CommentThread, DataImport, DeadlineBadge, FilterBar, FormDialog, KanbanBoard, LayoutSwitcher, NotificationCenter, NotificationList, PageTabs, QueryTable, SlideCaptcha, StepsForm, ThemeSwitcher, actionBarStyles, appShellBrandStyles, appShellDynamicStyles, appShellOffset, appShellStyles, authLayoutStyles, commentStyles, dataImportStyles, filterBarColumnStyles, filterBarStyles, kanbanBoardDynamicStyles, kanbanBoardStyles, notificationCenterStyles, notificationListStyles, patternsMessages, queryTableStyles, slideCaptchaDynamicStyles, slideCaptchaStyles, stepsFormStyles, useFormDialog };
|