@zerodep-dev/vue-tsx-pro-ui 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base/Badge.js +3 -3
- package/dist/base/Badge.js.map +1 -1
- package/dist/base/Can.d.ts +12 -0
- package/dist/base/Can.js +20 -0
- package/dist/base/Can.js.map +1 -0
- package/dist/base/ClientOnly.d.ts +10 -0
- package/dist/base/ClientOnly.js +18 -0
- package/dist/base/ClientOnly.js.map +1 -0
- package/dist/base/CloseButton.js +3 -3
- package/dist/base/CloseButton.js.map +1 -1
- package/dist/base/CodeBlock.js +5 -5
- package/dist/base/CodeBlock.js.map +1 -1
- package/dist/base/CopyButton.js +3 -3
- package/dist/base/CopyButton.js.map +1 -1
- package/dist/base/Icon.js +3 -3
- package/dist/base/Icon.js.map +1 -1
- package/dist/base/Indicator.js +3 -3
- package/dist/base/Indicator.js.map +1 -1
- package/dist/base/StatusDot.js +3 -3
- package/dist/base/StatusDot.js.map +1 -1
- package/dist/base/Tag.js +5 -5
- package/dist/base/Tag.js.map +1 -1
- package/dist/base/button-core.js +4 -4
- package/dist/base/button-core.js.map +1 -1
- package/dist/charts/AreaChart.js +3 -3
- package/dist/charts/AreaChart.js.map +1 -1
- package/dist/charts/BarChart.js +3 -3
- package/dist/charts/BarChart.js.map +1 -1
- package/dist/charts/ChordChart.js +3 -3
- package/dist/charts/ChordChart.js.map +1 -1
- package/dist/charts/ComboChart.js +3 -3
- package/dist/charts/ComboChart.js.map +1 -1
- package/dist/charts/DonutChart.js +3 -3
- package/dist/charts/DonutChart.js.map +1 -1
- package/dist/charts/FunnelChart.js +5 -5
- package/dist/charts/FunnelChart.js.map +1 -1
- package/dist/charts/GraphChart.js +3 -3
- package/dist/charts/GraphChart.js.map +1 -1
- package/dist/charts/LineChart.js +3 -3
- package/dist/charts/LineChart.js.map +1 -1
- package/dist/charts/MapChart.js +7 -7
- package/dist/charts/MapChart.js.map +1 -1
- package/dist/charts/RadarChart.js +16 -15
- package/dist/charts/RadarChart.js.map +1 -1
- package/dist/charts/SankeyChart.js +3 -3
- package/dist/charts/SankeyChart.js.map +1 -1
- package/dist/charts/ScatterChart.js +3 -3
- package/dist/charts/ScatterChart.js.map +1 -1
- package/dist/charts/TimelineChart.js +6 -5
- package/dist/charts/TimelineChart.js.map +1 -1
- package/dist/charts/TreemapChart.js +3 -3
- package/dist/charts/TreemapChart.js.map +1 -1
- package/dist/charts/WaterfallChart.js +3 -3
- package/dist/charts/WaterfallChart.js.map +1 -1
- package/dist/charts/XYChart.js +9 -7
- package/dist/charts/XYChart.js.map +1 -1
- package/dist/charts/frame.js +10 -8
- package/dist/charts/frame.js.map +1 -1
- package/dist/charts/single.js +4 -3
- package/dist/charts/single.js.map +1 -1
- package/dist/config/Provider.d.ts +3 -1
- package/dist/config/Provider.js +9 -4
- package/dist/config/Provider.js.map +1 -1
- package/dist/config/context.d.ts +18 -0
- package/dist/config/context.js +16 -1
- package/dist/config/context.js.map +1 -1
- package/dist/config/createUi.d.ts +20 -3
- package/dist/config/createUi.js +80 -49
- package/dist/config/createUi.js.map +1 -1
- package/dist/config/layout/preference.d.ts +43 -0
- package/dist/config/layout/preference.js +108 -0
- package/dist/config/layout/preference.js.map +1 -0
- package/dist/date/Calendar.js +3 -3
- package/dist/date/Calendar.js.map +1 -1
- package/dist/date/CalendarBoard.js +9 -9
- package/dist/date/CalendarBoard.js.map +1 -1
- package/dist/date/CalendarView.js +7 -7
- package/dist/date/CalendarView.js.map +1 -1
- package/dist/date/DateField.js +3 -3
- package/dist/date/DateField.js.map +1 -1
- package/dist/date/DatePicker.js +5 -5
- package/dist/date/DatePicker.js.map +1 -1
- package/dist/date/DateRangePicker.js +5 -5
- package/dist/date/DateRangePicker.js.map +1 -1
- package/dist/date/DateSegmentGroup.js +5 -5
- package/dist/date/DateSegmentGroup.js.map +1 -1
- package/dist/date/QuarterCalendar.js +6 -6
- package/dist/date/QuarterCalendar.js.map +1 -1
- package/dist/date/RangeCalendar.js +3 -3
- package/dist/date/RangeCalendar.js.map +1 -1
- package/dist/date/SegmentedField.js +3 -3
- package/dist/date/SegmentedField.js.map +1 -1
- package/dist/date/TimeField.js +3 -3
- package/dist/date/TimeField.js.map +1 -1
- package/dist/date/TimePicker.js +4 -4
- package/dist/date/TimePicker.js.map +1 -1
- package/dist/date/date-segments.js +1 -1
- package/dist/date/date-segments.js.map +1 -1
- package/dist/date/picker-popover.js +3 -2
- package/dist/date/picker-popover.js.map +1 -1
- package/dist/diagram/Diagram.js +12 -12
- package/dist/diagram/Diagram.js.map +1 -1
- package/dist/diagram/DiagramLanes.js +3 -3
- package/dist/diagram/DiagramLanes.js.map +1 -1
- package/dist/diagram/DiagramMinimap.js +4 -4
- package/dist/diagram/DiagramMinimap.js.map +1 -1
- package/dist/display/Accordion.js +6 -6
- package/dist/display/Accordion.js.map +1 -1
- package/dist/display/AnimatedNumber.js +4 -4
- package/dist/display/AnimatedNumber.js.map +1 -1
- package/dist/display/Avatar.js +4 -4
- package/dist/display/Avatar.js.map +1 -1
- package/dist/display/Checklist.js +7 -7
- package/dist/display/Checklist.js.map +1 -1
- package/dist/display/Collapse.js +8 -5
- package/dist/display/Collapse.js.map +1 -1
- package/dist/display/Collapsible.js +3 -3
- package/dist/display/Collapsible.js.map +1 -1
- package/dist/display/Descriptions.js +5 -5
- package/dist/display/Descriptions.js.map +1 -1
- package/dist/display/Ellipsis.js +3 -3
- package/dist/display/Ellipsis.js.map +1 -1
- package/dist/display/FieldCompare.js +4 -4
- package/dist/display/FieldCompare.js.map +1 -1
- package/dist/display/Gauge.js +11 -11
- package/dist/display/Gauge.js.map +1 -1
- package/dist/display/Image.js +4 -4
- package/dist/display/Image.js.map +1 -1
- package/dist/display/Marquee.js +3 -3
- package/dist/display/Marquee.js.map +1 -1
- package/dist/display/ProportionBar.js +4 -4
- package/dist/display/ProportionBar.js.map +1 -1
- package/dist/display/Sortable.js +4 -4
- package/dist/display/Sortable.js.map +1 -1
- package/dist/display/Sparkline.js +6 -6
- package/dist/display/Sparkline.js.map +1 -1
- package/dist/display/Stat.js +3 -3
- package/dist/display/Stat.js.map +1 -1
- package/dist/display/Timeline.js +7 -7
- package/dist/display/Timeline.js.map +1 -1
- package/dist/display/Tree.js +10 -10
- package/dist/display/Tree.js.map +1 -1
- package/dist/display/VirtualList.js +7 -7
- package/dist/display/VirtualList.js.map +1 -1
- package/dist/display/Watermark.js +3 -3
- package/dist/display/Watermark.js.map +1 -1
- package/dist/docs-kit/CodeGroup.js +5 -5
- package/dist/docs-kit/CodeGroup.js.map +1 -1
- package/dist/docs-kit/CodePreview.js +6 -6
- package/dist/docs-kit/CodePreview.js.map +1 -1
- package/dist/docs-kit/PropsTable.js +4 -4
- package/dist/docs-kit/PropsTable.js.map +1 -1
- package/dist/feedback/Alert.js +5 -5
- package/dist/feedback/Alert.js.map +1 -1
- package/dist/feedback/Banner.js +5 -5
- package/dist/feedback/Banner.js.map +1 -1
- package/dist/feedback/Empty.js +4 -4
- package/dist/feedback/Empty.js.map +1 -1
- package/dist/feedback/LoadingBar.js +20 -13
- package/dist/feedback/LoadingBar.js.map +1 -1
- package/dist/feedback/LoadingOverlay.js +3 -3
- package/dist/feedback/LoadingOverlay.js.map +1 -1
- package/dist/feedback/Progress.js +14 -14
- package/dist/feedback/Progress.js.map +1 -1
- package/dist/feedback/Result.js +4 -4
- package/dist/feedback/Result.js.map +1 -1
- package/dist/feedback/SkeletonText.js +3 -3
- package/dist/feedback/SkeletonText.js.map +1 -1
- package/dist/feedback/Toast.js +6 -6
- package/dist/feedback/Toast.js.map +1 -1
- package/dist/form/Field.js +7 -7
- package/dist/form/Field.js.map +1 -1
- package/dist/form/Form.js +3 -3
- package/dist/form/Form.js.map +1 -1
- package/dist/form/FormErrorSummary.js +3 -3
- package/dist/form/FormErrorSummary.js.map +1 -1
- package/dist/form/FormSection.js +6 -6
- package/dist/form/FormSection.js.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.js +10 -5
- package/dist/input/Autocomplete.js +3 -3
- package/dist/input/Autocomplete.js.map +1 -1
- package/dist/input/Cascader.js +7 -7
- package/dist/input/Cascader.js.map +1 -1
- package/dist/input/CharacterCount.js +3 -3
- package/dist/input/CharacterCount.js.map +1 -1
- package/dist/input/Checkbox.js +5 -5
- package/dist/input/Checkbox.js.map +1 -1
- package/dist/input/ColorField.js +4 -4
- package/dist/input/ColorField.js.map +1 -1
- package/dist/input/Combobox.js +4 -4
- package/dist/input/Combobox.js.map +1 -1
- package/dist/input/ConditionBuilder.js +15 -14
- package/dist/input/ConditionBuilder.js.map +1 -1
- package/dist/input/FileDrop.js +3 -3
- package/dist/input/FileDrop.js.map +1 -1
- package/dist/input/Listbox.js +9 -9
- package/dist/input/Listbox.js.map +1 -1
- package/dist/input/Mentions.js +3 -3
- package/dist/input/Mentions.js.map +1 -1
- package/dist/input/NumberField.js +3 -3
- package/dist/input/NumberField.js.map +1 -1
- package/dist/input/NumberRangeField.js +5 -5
- package/dist/input/NumberRangeField.js.map +1 -1
- package/dist/input/OtpField.js +5 -5
- package/dist/input/OtpField.js.map +1 -1
- package/dist/input/PeoplePicker.js +6 -6
- package/dist/input/PeoplePicker.js.map +1 -1
- package/dist/input/PeoplePickerPanel.js +4 -4
- package/dist/input/PeoplePickerPanel.js.map +1 -1
- package/dist/input/Radio.js +4 -4
- package/dist/input/Radio.js.map +1 -1
- package/dist/input/Rating.js +5 -5
- package/dist/input/Rating.js.map +1 -1
- package/dist/input/Select.js +4 -4
- package/dist/input/Select.js.map +1 -1
- package/dist/input/SelectedTags.js +4 -4
- package/dist/input/SelectedTags.js.map +1 -1
- package/dist/input/Slider.js +6 -6
- package/dist/input/Slider.js.map +1 -1
- package/dist/input/Switch.js +3 -3
- package/dist/input/Switch.js.map +1 -1
- package/dist/input/TagsInput.js +5 -5
- package/dist/input/TagsInput.js.map +1 -1
- package/dist/input/TextField.js +5 -5
- package/dist/input/TextField.js.map +1 -1
- package/dist/input/Textarea.js +4 -4
- package/dist/input/Textarea.js.map +1 -1
- package/dist/input/ToggleGroup.js +4 -4
- package/dist/input/ToggleGroup.js.map +1 -1
- package/dist/input/Transfer.js +8 -8
- package/dist/input/Transfer.js.map +1 -1
- package/dist/input/TreeSelect.js +6 -6
- package/dist/input/TreeSelect.js.map +1 -1
- package/dist/input/Upload.js +8 -8
- package/dist/input/Upload.js.map +1 -1
- package/dist/layout/Affix.js +4 -4
- package/dist/layout/Affix.js.map +1 -1
- package/dist/layout/ResizeHandle.d.ts +6 -2
- package/dist/layout/ResizeHandle.js +32 -9
- package/dist/layout/ResizeHandle.js.map +1 -1
- package/dist/layout/ScrollArea.js +3 -3
- package/dist/layout/ScrollArea.js.map +1 -1
- package/dist/layout/Separator.js +3 -3
- package/dist/layout/Separator.js.map +1 -1
- package/dist/layout/Splitter.js +3 -3
- package/dist/layout/Splitter.js.map +1 -1
- package/dist/layout/ViewportFill.d.ts +2 -2
- package/dist/layout/ViewportFill.js +4 -4
- package/dist/layout/ViewportFill.js.map +1 -1
- package/dist/navigation/Breadcrumbs.js +4 -4
- package/dist/navigation/Breadcrumbs.js.map +1 -1
- package/dist/navigation/NavBar.js +6 -6
- package/dist/navigation/NavBar.js.map +1 -1
- package/dist/navigation/NavList.d.ts +1 -0
- package/dist/navigation/NavList.js +20 -12
- package/dist/navigation/NavList.js.map +1 -1
- package/dist/navigation/NavMenu.d.ts +27 -0
- package/dist/navigation/NavMenu.js +54 -0
- package/dist/navigation/NavMenu.js.map +1 -0
- package/dist/navigation/PageJump.js +3 -3
- package/dist/navigation/PageJump.js.map +1 -1
- package/dist/navigation/PageSizeSelect.js +4 -4
- package/dist/navigation/PageSizeSelect.js.map +1 -1
- package/dist/navigation/Pagination.js +6 -6
- package/dist/navigation/Pagination.js.map +1 -1
- package/dist/navigation/PaginationBar.js +5 -5
- package/dist/navigation/PaginationBar.js.map +1 -1
- package/dist/navigation/Steps.js +4 -4
- package/dist/navigation/Steps.js.map +1 -1
- package/dist/navigation/TableOfContents.js +5 -5
- package/dist/navigation/TableOfContents.js.map +1 -1
- package/dist/navigation/Tabs.js +7 -7
- package/dist/navigation/Tabs.js.map +1 -1
- package/dist/navigation/Toolbar.js +3 -3
- package/dist/navigation/Toolbar.js.map +1 -1
- package/dist/overlay/AlertDialog.js +4 -4
- package/dist/overlay/AlertDialog.js.map +1 -1
- package/dist/overlay/CommandPalette.js +4 -4
- package/dist/overlay/CommandPalette.js.map +1 -1
- package/dist/overlay/ContextMenu.js +3 -3
- package/dist/overlay/ContextMenu.js.map +1 -1
- package/dist/overlay/Dialog.js +4 -4
- package/dist/overlay/Dialog.js.map +1 -1
- package/dist/overlay/Drawer.js +5 -5
- package/dist/overlay/Drawer.js.map +1 -1
- package/dist/overlay/HoverCard.js +5 -3
- package/dist/overlay/HoverCard.js.map +1 -1
- package/dist/overlay/Lightbox.js +5 -5
- package/dist/overlay/Lightbox.js.map +1 -1
- package/dist/overlay/Menu.js +7 -7
- package/dist/overlay/Menu.js.map +1 -1
- package/dist/overlay/ModalCore.js +9 -9
- package/dist/overlay/ModalCore.js.map +1 -1
- package/dist/overlay/Popconfirm.js +6 -6
- package/dist/overlay/Popconfirm.js.map +1 -1
- package/dist/overlay/Popover.js +5 -3
- package/dist/overlay/Popover.js.map +1 -1
- package/dist/overlay/Tooltip.js +5 -3
- package/dist/overlay/Tooltip.js.map +1 -1
- package/dist/overlay/Tour.js +8 -8
- package/dist/overlay/Tour.js.map +1 -1
- package/dist/overlay/detached.d.ts +2 -0
- package/dist/overlay/detached.js +73 -42
- package/dist/overlay/detached.js.map +1 -1
- package/dist/patterns/ActionBar.js +3 -3
- package/dist/patterns/ActionBar.js.map +1 -1
- package/dist/patterns/AppShell.d.ts +28 -1
- package/dist/patterns/AppShell.js +158 -52
- package/dist/patterns/AppShell.js.map +1 -1
- package/dist/patterns/AuthLayout.d.ts +28 -0
- package/dist/patterns/AuthLayout.js +77 -0
- package/dist/patterns/AuthLayout.js.map +1 -0
- package/dist/patterns/CommentThread.js +9 -9
- package/dist/patterns/CommentThread.js.map +1 -1
- package/dist/patterns/DataImport.js +4 -4
- package/dist/patterns/DataImport.js.map +1 -1
- package/dist/patterns/DeadlineBadge.js +3 -3
- package/dist/patterns/DeadlineBadge.js.map +1 -1
- package/dist/patterns/FilterBar.d.ts +11 -1
- package/dist/patterns/FilterBar.js +39 -10
- package/dist/patterns/FilterBar.js.map +1 -1
- package/dist/patterns/FormDialog.d.ts +18 -0
- package/dist/patterns/FormDialog.js +46 -0
- package/dist/patterns/FormDialog.js.map +1 -0
- package/dist/patterns/KanbanBoard.js +8 -8
- package/dist/patterns/KanbanBoard.js.map +1 -1
- package/dist/patterns/LayoutSwitcher.d.ts +12 -0
- package/dist/patterns/LayoutSwitcher.js +79 -0
- package/dist/patterns/LayoutSwitcher.js.map +1 -0
- package/dist/patterns/NotificationCenter.js +8 -8
- package/dist/patterns/NotificationCenter.js.map +1 -1
- package/dist/patterns/PageTabs.d.ts +12 -7
- package/dist/patterns/PageTabs.js +41 -29
- package/dist/patterns/PageTabs.js.map +1 -1
- package/dist/patterns/QueryTable.d.ts +7 -1
- package/dist/patterns/QueryTable.js +116 -81
- package/dist/patterns/QueryTable.js.map +1 -1
- package/dist/patterns/SlideCaptcha.d.ts +80 -0
- package/dist/patterns/SlideCaptcha.js +354 -0
- package/dist/patterns/SlideCaptcha.js.map +1 -0
- package/dist/patterns/StepsForm.js +7 -7
- package/dist/patterns/StepsForm.js.map +1 -1
- package/dist/patterns/ThemeSwitcher.js +3 -3
- package/dist/patterns/ThemeSwitcher.js.map +1 -1
- package/dist/patterns/index.d.ts +7 -3
- package/dist/patterns/index.js +5 -1
- package/dist/patterns/messages.d.ts +37 -1
- package/dist/patterns/messages.js +38 -2
- package/dist/patterns/messages.js.map +1 -1
- package/dist/table/ColumnHeaderActions.js +3 -3
- package/dist/table/ColumnHeaderActions.js.map +1 -1
- package/dist/table/ColumnResizer.js +3 -3
- package/dist/table/ColumnResizer.js.map +1 -1
- package/dist/table/ColumnSettings.js +5 -5
- package/dist/table/ColumnSettings.js.map +1 -1
- package/dist/table/ColumnTable.js +8 -8
- package/dist/table/ColumnTable.js.map +1 -1
- package/dist/table/DataTable.js +8 -8
- package/dist/table/DataTable.js.map +1 -1
- package/dist/table/DataTableView.js +21 -21
- package/dist/table/DataTableView.js.map +1 -1
- package/dist/table/PivotTable.js +3 -3
- package/dist/table/PivotTable.js.map +1 -1
- package/dist/table/SelectionBar.js +4 -4
- package/dist/table/SelectionBar.js.map +1 -1
- package/dist/table/TreeDataTable.js +5 -5
- package/dist/table/TreeDataTable.js.map +1 -1
- package/dist/theme-customize/ThemeCustomizer.js +7 -6
- package/dist/theme-customize/ThemeCustomizer.js.map +1 -1
- package/dist/theme-customize/ThemeThumbnail.js +3 -3
- package/dist/theme-customize/ThemeThumbnail.js.map +1 -1
- package/dist/three/background/AuroraBackground.js +3 -3
- package/dist/three/background/AuroraBackground.js.map +1 -1
- package/dist/three/background/MeshGradient.js +3 -3
- package/dist/three/background/MeshGradient.js.map +1 -1
- package/dist/three/background/ShaderBackground.js +3 -3
- package/dist/three/background/ShaderBackground.js.map +1 -1
- package/dist/three/core/EffectSurface.js +3 -3
- package/dist/three/core/EffectSurface.js.map +1 -1
- package/dist/three/core/ThreeCanvas.js +4 -4
- package/dist/three/core/ThreeCanvas.js.map +1 -1
- package/dist/three/effects/Confetti.js +3 -3
- package/dist/three/effects/Confetti.js.map +1 -1
- package/dist/three/effects/ParticleField.js +3 -3
- package/dist/three/effects/ParticleField.js.map +1 -1
- package/dist/three/effects/WaveGrid.js +3 -3
- package/dist/three/effects/WaveGrid.js.map +1 -1
- package/dist/three/globe/Globe.js +3 -3
- package/dist/three/globe/Globe.js.map +1 -1
- package/dist/three/model/ModelViewer.js +3 -3
- package/dist/three/model/ModelViewer.js.map +1 -1
- package/dist/utils/data/number.d.ts +5 -0
- package/dist/utils/data/number.js +8 -1
- package/dist/utils/data/number.js.map +1 -1
- package/dist/utils/dom/element.d.ts +3 -1
- package/dist/utils/dom/element.js +8 -4
- package/dist/utils/dom/element.js.map +1 -1
- package/dist/utils/dom/fullscreen.d.ts +1 -1
- package/dist/utils/dom/fullscreen.js +12 -8
- package/dist/utils/dom/fullscreen.js.map +1 -1
- package/dist/utils/dom/mounted.d.ts +7 -0
- package/dist/utils/dom/mounted.js +16 -0
- package/dist/utils/dom/mounted.js.map +1 -0
- package/dist/utils/dom/overflow.js +2 -1
- package/dist/utils/dom/overflow.js.map +1 -1
- package/dist/utils/dom/pan-zoom.js +2 -1
- package/dist/utils/dom/pan-zoom.js.map +1 -1
- package/dist/utils/props.js +3 -13
- package/dist/utils/props.js.map +1 -1
- package/dist/utils/request/http.d.ts +12 -0
- package/dist/utils/request/http.js +6 -2
- package/dist/utils/request/http.js.map +1 -1
- package/dist/utils/request/use-request.js +4 -2
- package/dist/utils/request/use-request.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mergeAttrs } from "../utils/props.js";
|
|
2
2
|
import { StatusDot, statusDotToneStyles } from "../base/StatusDot.js";
|
|
3
|
-
import { createVNode
|
|
4
|
-
import { _component, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
3
|
+
import { createVNode } from "vue";
|
|
4
|
+
import { _component, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
5
5
|
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
6
6
|
//#region src/display/ProportionBar.tsx
|
|
7
7
|
/** 比例条的一段 */
|
|
@@ -69,10 +69,10 @@ var ProportionBar = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
69
69
|
const label = readonlyProp(_props, "ariaLabel");
|
|
70
70
|
const rest = restProps(_props, _ctx, true);
|
|
71
71
|
const toneOf = (segment, index) => segment.tone ?? ORDER[index % ORDER.length];
|
|
72
|
-
return () => createVNode("div", mergeAttrs({ sx: proportionBarStyles.root }, rest), [createVNode("div",
|
|
72
|
+
return () => createVNode("div", mergeAttrs({ sx: proportionBarStyles.root }, rest), [createVNode("div", mergeJsxProps({
|
|
73
73
|
"role": "img",
|
|
74
74
|
"aria-label": `${label.value}:${segments.value.map((segment) => `${segment.label} ${segment.value}`).join(",")}`
|
|
75
|
-
}, css.attrs(proportionBarStyles.bar, proportionBarStyles[size.value])), [segments.value.map((segment, index) => segment.value > 0 && createVNode("span",
|
|
75
|
+
}, css.attrs(proportionBarStyles.bar, proportionBarStyles[size.value])), [segments.value.map((segment, index) => segment.value > 0 && createVNode("span", mergeJsxProps({ "key": segment.key }, css.attrs(css.bind(proportionBarDynamicStyles.share, () => [segment.value]), segment.color ? css.bind(proportionBarDynamicStyles.color, () => [segment.color]) : statusDotToneStyles[toneOf(segment, index)])), null))]), legend.value && createVNode("ul", css.attrs(proportionBarStyles.legend), [segments.value.map((segment, index) => createVNode("li", mergeJsxProps({ "key": segment.key }, css.attrs(proportionBarStyles.legendItem)), [
|
|
76
76
|
createVNode(StatusDot, {
|
|
77
77
|
"size": "md",
|
|
78
78
|
"tone": toneOf(segment, index),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProportionBar.js","names":["_vtp_readonlyProp","_vtp_restProps","_createVNode","_mergeProps"],"sources":["../../src/display/ProportionBar.tsx"],"sourcesContent":["/**\n * 比例条:一根横条按各部分的数量分段,下面是图例(色点、名称、数量),看一个总数由哪几块构成\n * (工单的三种状态、各状态的占比、存储空间)。部分之间互斥、加起来是总数时用它;要比较趋势用图表。\n *\n * <ProportionBar\n * aria-label=\"工单构成\"\n * segments={[\n * { key: 'done', label: '已完成', value: 25, tone: 'success' },\n * { key: 'blocked', label: '受阻', value: 4, tone: 'warning' },\n * { key: 'open', label: '进行中', value: 35, tone: 'info' },\n * ]}\n * valueText={(segment) => `${segment.value} 项`}\n * />\n *\n * 横条对读屏是一张图(role=\"img\"),名称后面自动带上各段的数量;图例是普通文字。\n * 数量为 0 的段不画但留在图例里。颜色用 tone(状态色)或 color(任意 CSS 颜色,如图表色板)。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { mergeAttrs, type PartProps } from '../utils/props';\nimport type { StatusTone } from '../utils/style/tone-styles';\nimport { StatusDot, statusDotToneStyles } from '../base/StatusDot';\n\n/** 比例条的一段 */\nexport interface ProportionSegment {\n /** 标识 */\n key: string;\n /** 名称 */\n label: string;\n /** 数量 */\n value: number;\n /** 色调;不写时按顺序取 accent、info、success、warning、danger、neutral */\n tone?: StatusTone;\n /** 任意 CSS 颜色;写了时不看 tone */\n color?: string;\n}\n\n/** 未声明的属性落到外层 div 上 */\nexport interface ProportionBarProps extends PartProps<'div'> {\n /** 各段:按顺序从起始边排 */\n segments: readonly ProportionSegment[];\n /** 横条的名称(读屏):后面自动带上各段的数量 */\n 'aria-label': string;\n /** 图例里数量的写法,默认是数字 */\n valueText?: (segment: ProportionSegment) => VNodeChild;\n /**\n * 下面的图例\n * @default true\n */\n legend?: boolean;\n /**\n * 横条的粗细\n * @default 'md'\n */\n size?: 'sm' | 'md';\n /** 图例每一项后面的补充(“待定价”) */\n legendExtra?: (segment: ProportionSegment) => VNodeChild;\n}\n\nconst ORDER: readonly StatusTone[] = ['accent', 'info', 'success', 'warning', 'danger', 'neutral'];\n\n/** ProportionBar 的样式:root、bar、sm、md、legend、legendItem、value */\nexport const proportionBarStyles = css.create({\n root: { display: 'flex', flexDirection: 'column', gap: '_sm', minWidth: 0 },\n bar: { display: 'flex', gap: '2px', borderRadius: '_full', overflow: 'hidden' },\n sm: { height: '0.5rem' },\n md: { height: '0.75rem' },\n legend: {\n display: 'flex',\n flexWrap: 'wrap',\n justifyContent: 'space-between',\n columnGap: '_md',\n rowGap: '_xs',\n margin: 0,\n padding: 0,\n listStyle: 'none',\n fontSize: '_sm',\n },\n legendItem: { display: 'inline-flex', alignItems: 'center', gap: '_sm' },\n value: { fontWeight: '_semibold', fontVariantNumeric: 'tabular-nums' },\n});\n\n/** ProportionBar 的动态样式:段的份额、任意颜色 */\nexport const proportionBarDynamicStyles = css.create({\n share: (value: number) => ({ flexGrow: value, flexBasis: 0 }),\n color: (color: string) => ({ backgroundColor: color }),\n});\n\n/** 比例条 */\nexport const ProportionBar = _component(\n ({\n segments,\n valueText = (segment) => String(segment.value),\n legend = true,\n size = 'md',\n legendExtra,\n 'aria-label': label,\n ...rest\n }: ProportionBarProps) => {\n const toneOf = (segment: ProportionSegment, index: number) =>\n segment.tone ?? ORDER[index % ORDER.length]!;\n return () => (\n <div {...mergeAttrs<'div'>({ sx: proportionBarStyles.root }, rest)}>\n <div\n role=\"img\"\n aria-label={`${label}:${segments\n .map((segment) => `${segment.label} ${segment.value}`)\n .join(',')}`}\n {...css.attrs(proportionBarStyles.bar, proportionBarStyles[size])}\n >\n {segments.map(\n (segment, index) =>\n segment.value > 0 && (\n <span\n key={segment.key}\n {...css.attrs(\n proportionBarDynamicStyles.share(segment.value),\n segment.color\n ? proportionBarDynamicStyles.color(segment.color)\n : statusDotToneStyles[toneOf(segment, index)],\n )}\n />\n ),\n )}\n </div>\n {legend && (\n <ul {...css.attrs(proportionBarStyles.legend)}>\n {segments.map((segment, index) => (\n <li key={segment.key} {...css.attrs(proportionBarStyles.legendItem)}>\n <StatusDot size=\"md\" tone={toneOf(segment, index)} color={segment.color} />\n <span>{segment.label}</span>\n <span {...css.attrs(proportionBarStyles.value)}>{valueText(segment)}</span>\n {legendExtra?.(segment)}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n },\n);\n"],"mappings":";;;;;;;;AA4DA,IAAM,QAA+B;CAAC;CAAU;CAAQ;CAAW;CAAW;CAAU;AAAS;;AAGjG,IAAa,sBAAsB,IAAI,OAAO;CAC5C,MAAM;EAAE,SAAS;EAAQ,eAAe;EAAU,KAAK;EAAO,UAAU;CAAE;CAC1E,KAAK;EAAE,SAAS;EAAQ,KAAK;EAAO,cAAc;EAAS,UAAU;CAAS;CAC9E,IAAI,EAAE,QAAQ,SAAS;CACvB,IAAI,EAAE,QAAQ,UAAU;CACxB,QAAQ;EACN,SAAS;EACT,UAAU;EACV,gBAAgB;EAChB,WAAW;EACX,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,WAAW;EACX,UAAU;CACZ;CACA,YAAY;EAAE,SAAS;EAAe,YAAY;EAAU,KAAK;CAAM;CACvE,OAAO;EAAE,YAAY;EAAa,oBAAoB;CAAe;AACvE,CAAC;;AAGD,IAAa,6BAA6B,IAAI,OAAO;CACnD,QAAQ,WAAmB;EAAE,UAAU;EAAO,WAAW;CAAE;CAC3D,QAAQ,WAAmB,EAAE,iBAAiB,MAAM;AACtD,CAAC;;AAGD,IAAa,gBAAgB,0BAC3B,QAAA,SAQ0B;CAAA,MAAA,WAAAA,aAAA,QAAA,UAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,oBANX,YAAY,OAAO,QAAQ,KAAK,CAAC;CAAA,MAAA,SAAAA,aAAA,QAAA,UACrC,IAAI;CAAA,MAAA,OAAAA,aAAA,QAAA,QACN,IAAI;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,QAAAA,aAAA,QAAA,WAAA;CAAA,MAGR,OAAIC,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,UAAU,SAA4B,UAC1C,QAAQ,QAAQ,MAAM,QAAQ,MAAM;CACtC,aAAOC,YAAA,OACI,WAAkB,EAAE,IAAI,oBAAoB,KAAK,GAAG,IAAI,GAAC,CAAAA,YAAA,OAAAC,
|
|
1
|
+
{"version":3,"file":"ProportionBar.js","names":["_vtp_readonlyProp","_vtp_restProps","_createVNode","_mergeProps"],"sources":["../../src/display/ProportionBar.tsx"],"sourcesContent":["/**\n * 比例条:一根横条按各部分的数量分段,下面是图例(色点、名称、数量),看一个总数由哪几块构成\n * (工单的三种状态、各状态的占比、存储空间)。部分之间互斥、加起来是总数时用它;要比较趋势用图表。\n *\n * <ProportionBar\n * aria-label=\"工单构成\"\n * segments={[\n * { key: 'done', label: '已完成', value: 25, tone: 'success' },\n * { key: 'blocked', label: '受阻', value: 4, tone: 'warning' },\n * { key: 'open', label: '进行中', value: 35, tone: 'info' },\n * ]}\n * valueText={(segment) => `${segment.value} 项`}\n * />\n *\n * 横条对读屏是一张图(role=\"img\"),名称后面自动带上各段的数量;图例是普通文字。\n * 数量为 0 的段不画但留在图例里。颜色用 tone(状态色)或 color(任意 CSS 颜色,如图表色板)。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { mergeAttrs, type PartProps } from '../utils/props';\nimport type { StatusTone } from '../utils/style/tone-styles';\nimport { StatusDot, statusDotToneStyles } from '../base/StatusDot';\n\n/** 比例条的一段 */\nexport interface ProportionSegment {\n /** 标识 */\n key: string;\n /** 名称 */\n label: string;\n /** 数量 */\n value: number;\n /** 色调;不写时按顺序取 accent、info、success、warning、danger、neutral */\n tone?: StatusTone;\n /** 任意 CSS 颜色;写了时不看 tone */\n color?: string;\n}\n\n/** 未声明的属性落到外层 div 上 */\nexport interface ProportionBarProps extends PartProps<'div'> {\n /** 各段:按顺序从起始边排 */\n segments: readonly ProportionSegment[];\n /** 横条的名称(读屏):后面自动带上各段的数量 */\n 'aria-label': string;\n /** 图例里数量的写法,默认是数字 */\n valueText?: (segment: ProportionSegment) => VNodeChild;\n /**\n * 下面的图例\n * @default true\n */\n legend?: boolean;\n /**\n * 横条的粗细\n * @default 'md'\n */\n size?: 'sm' | 'md';\n /** 图例每一项后面的补充(“待定价”) */\n legendExtra?: (segment: ProportionSegment) => VNodeChild;\n}\n\nconst ORDER: readonly StatusTone[] = ['accent', 'info', 'success', 'warning', 'danger', 'neutral'];\n\n/** ProportionBar 的样式:root、bar、sm、md、legend、legendItem、value */\nexport const proportionBarStyles = css.create({\n root: { display: 'flex', flexDirection: 'column', gap: '_sm', minWidth: 0 },\n bar: { display: 'flex', gap: '2px', borderRadius: '_full', overflow: 'hidden' },\n sm: { height: '0.5rem' },\n md: { height: '0.75rem' },\n legend: {\n display: 'flex',\n flexWrap: 'wrap',\n justifyContent: 'space-between',\n columnGap: '_md',\n rowGap: '_xs',\n margin: 0,\n padding: 0,\n listStyle: 'none',\n fontSize: '_sm',\n },\n legendItem: { display: 'inline-flex', alignItems: 'center', gap: '_sm' },\n value: { fontWeight: '_semibold', fontVariantNumeric: 'tabular-nums' },\n});\n\n/** ProportionBar 的动态样式:段的份额、任意颜色 */\nexport const proportionBarDynamicStyles = css.create({\n share: (value: number) => ({ flexGrow: value, flexBasis: 0 }),\n color: (color: string) => ({ backgroundColor: color }),\n});\n\n/** 比例条 */\nexport const ProportionBar = _component(\n ({\n segments,\n valueText = (segment) => String(segment.value),\n legend = true,\n size = 'md',\n legendExtra,\n 'aria-label': label,\n ...rest\n }: ProportionBarProps) => {\n const toneOf = (segment: ProportionSegment, index: number) =>\n segment.tone ?? ORDER[index % ORDER.length]!;\n return () => (\n <div {...mergeAttrs<'div'>({ sx: proportionBarStyles.root }, rest)}>\n <div\n role=\"img\"\n aria-label={`${label}:${segments\n .map((segment) => `${segment.label} ${segment.value}`)\n .join(',')}`}\n {...css.attrs(proportionBarStyles.bar, proportionBarStyles[size])}\n >\n {segments.map(\n (segment, index) =>\n segment.value > 0 && (\n <span\n key={segment.key}\n {...css.attrs(\n proportionBarDynamicStyles.share(segment.value),\n segment.color\n ? proportionBarDynamicStyles.color(segment.color)\n : statusDotToneStyles[toneOf(segment, index)],\n )}\n />\n ),\n )}\n </div>\n {legend && (\n <ul {...css.attrs(proportionBarStyles.legend)}>\n {segments.map((segment, index) => (\n <li key={segment.key} {...css.attrs(proportionBarStyles.legendItem)}>\n <StatusDot size=\"md\" tone={toneOf(segment, index)} color={segment.color} />\n <span>{segment.label}</span>\n <span {...css.attrs(proportionBarStyles.value)}>{valueText(segment)}</span>\n {legendExtra?.(segment)}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n },\n);\n"],"mappings":";;;;;;;;AA4DA,IAAM,QAA+B;CAAC;CAAU;CAAQ;CAAW;CAAW;CAAU;AAAS;;AAGjG,IAAa,sBAAsB,IAAI,OAAO;CAC5C,MAAM;EAAE,SAAS;EAAQ,eAAe;EAAU,KAAK;EAAO,UAAU;CAAE;CAC1E,KAAK;EAAE,SAAS;EAAQ,KAAK;EAAO,cAAc;EAAS,UAAU;CAAS;CAC9E,IAAI,EAAE,QAAQ,SAAS;CACvB,IAAI,EAAE,QAAQ,UAAU;CACxB,QAAQ;EACN,SAAS;EACT,UAAU;EACV,gBAAgB;EAChB,WAAW;EACX,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,WAAW;EACX,UAAU;CACZ;CACA,YAAY;EAAE,SAAS;EAAe,YAAY;EAAU,KAAK;CAAM;CACvE,OAAO;EAAE,YAAY;EAAa,oBAAoB;CAAe;AACvE,CAAC;;AAGD,IAAa,6BAA6B,IAAI,OAAO;CACnD,QAAQ,WAAmB;EAAE,UAAU;EAAO,WAAW;CAAE;CAC3D,QAAQ,WAAmB,EAAE,iBAAiB,MAAM;AACtD,CAAC;;AAGD,IAAa,gBAAgB,0BAC3B,QAAA,SAQ0B;CAAA,MAAA,WAAAA,aAAA,QAAA,UAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,oBANX,YAAY,OAAO,QAAQ,KAAK,CAAC;CAAA,MAAA,SAAAA,aAAA,QAAA,UACrC,IAAI;CAAA,MAAA,OAAAA,aAAA,QAAA,QACN,IAAI;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,QAAAA,aAAA,QAAA,WAAA;CAAA,MAGR,OAAIC,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,UAAU,SAA4B,UAC1C,QAAQ,QAAQ,MAAM,QAAQ,MAAM;CACtC,aAAOC,YAAA,OACI,WAAkB,EAAE,IAAI,oBAAoB,KAAK,GAAG,IAAI,GAAC,CAAAA,YAAA,OAAAC,cAAA;EAAA,QAAA;EAAA,cAGlD,GAAA,MAAA,MAAA,GAAY,SAAA,MACrB,KAAK,YAAY,GAAG,QAAQ,MAAK,GAAI,QAAQ,OAAO,CAAC,CACrD,KAAK,GAAG;CAAG,GACV,IAAI,MAAM,oBAAoB,KAAK,oBAAmB,KAAA,MAAM,CAAC,GAAA,CAEhE,SAAA,MAAS,KACP,SAAS,UACR,QAAQ,QAAQ,KAACD,YAAA,QAAAC,cAAA,EAAA,OAER,QAAQ,IAAG,GACZ,IAAI,MAAK,IAAA,KACX,2BAA2B,aAAK,CAAC,QAAQ,KAAK,CAAA,GAC9C,QAAQ,QAAK,IAAA,KACT,2BAA2B,aAAK,CAAC,QAAQ,KAAK,CAAA,IAC9C,oBAAoB,OAAO,SAAS,KAAK,EAC/C,CAAC,GAAA,IAAA,CAGT,CAAC,CAAA,GAEF,OAAA,SAAAD,YAAA,MACS,IAAI,MAAM,oBAAoB,MAAM,GAAC,CAC1C,SAAA,MAAS,KAAK,SAAS,UAAKA,YAAA,MAAAC,cAAA,EAAA,OAClB,QAAQ,IAAG,GAAM,IAAI,MAAM,oBAAoB,UAAU,CAAC,GAAA;EAAAD,YAAA,WAAA;GAAA,QAAA;GAAA,QACtC,OAAO,SAAS,KAAK;GAAC,SAAS,QAAQ;EAAK,GAAA,IAAA;EAAAA,YAAA,QAAA,MAAA,CAChE,QAAQ,KAAK,CAAA;EAAAA,YAAA,QACV,IAAI,MAAM,oBAAoB,KAAK,GAAC,CAAG,UAAA,MAAU,OAAO,CAAC,CAAA;EAClE,YAAA,QAAc,OAAO;CAAC,CAAA,CAE1B,CAAC,CAAA,CAEL,CAAA;AAGP,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
package/dist/display/Sortable.js
CHANGED
|
@@ -7,8 +7,8 @@ import { ButtonBase } from "../base/ButtonBase.js";
|
|
|
7
7
|
import { toElement } from "../utils/dom/element.js";
|
|
8
8
|
import { FlexList } from "../layout/Flex.js";
|
|
9
9
|
import { useSortable } from "../utils/dom/sortable.js";
|
|
10
|
-
import { Fragment, createVNode,
|
|
11
|
-
import { _component, bindable, childrenProp, childrenStable, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
10
|
+
import { Fragment, createVNode, shallowRef } from "vue";
|
|
11
|
+
import { _component, bindable, childrenProp, childrenStable, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
12
12
|
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
13
13
|
import { GripVertical } from "@lucide/icons";
|
|
14
14
|
//#region src/display/Sortable.tsx
|
|
@@ -96,7 +96,7 @@ var Sortable = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
96
96
|
disabled: () => disabled.value
|
|
97
97
|
});
|
|
98
98
|
return () => createVNode(Fragment, null, [
|
|
99
|
-
createVNode(FlexList,
|
|
99
|
+
createVNode(FlexList, mergeJsxProps(mergePartProps({
|
|
100
100
|
direction: orientation.value === "horizontal" ? "row" : "column",
|
|
101
101
|
gap: "xs"
|
|
102
102
|
}, rest, { ref: (value) => root.value = toElement(value) }), {
|
|
@@ -110,7 +110,7 @@ var Sortable = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
110
110
|
active: sortable.active === key
|
|
111
111
|
};
|
|
112
112
|
const own = typeof propsItem.value === "function" ? propsItem.value(item, state) : propsItem.value;
|
|
113
|
-
return createVNode("li",
|
|
113
|
+
return createVNode("li", mergeJsxProps({ "key": key }, mergeAttrs({ sx: [
|
|
114
114
|
sortableStyles.item,
|
|
115
115
|
orientation.value === "horizontal" && sortableStyles.itemHorizontal,
|
|
116
116
|
state.active && sortableStyles.active
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sortable.js","names":["_vtp_bindable","_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_vue_shallowRef","_createVNode","_Fragment","_mergeProps","_vtp_childrenStable"],"sources":["../../src/display/Sortable.tsx"],"sourcesContent":["/**\n * 可拖动排序的列表(ul):每项前有一个把手,拖动或用键盘改变顺序,数据双向绑定。\n *\n * <Sortable _items={tasks} getKey={(task) => task.id} getLabel={(task) => task.title} aria-label=\"待办\">\n * {(task) => <Text>{task.title}</Text>}\n * </Sortable>\n *\n * 拖动、键盘、读屏播报、自动滚动、布局动画见 useSortable(utils/dom/sortable.ts)。\n * 列表自己不滚动:项多时在外面包 ScrollArea(横排写 axis=\"x\"),拖到边缘时自动滚动会找到它的视口,滚动条与整站一致。\n * 把手是按钮:名称“移动 项名”,操作说明经 aria-describedby 关联,拿起时 aria-pressed。\n * 部件:Root(ul)、Item(li)、Handle(button)、Content(div)。\n */\nimport { GripVertical } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { ButtonBase, type ButtonBaseProps } from '../base/ButtonBase';\nimport { Icon } from '../base/Icon';\nimport { VisuallyHidden } from '../base/VisuallyHidden';\nimport { useLang } from '../config/context';\nimport { useSortable, type SortableOrientations } from '../utils/dom/sortable';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { FlexList, type FlexProps } from '../layout/Flex';\nimport { toElement } from '../utils/dom/element';\nimport { t } from '../config/theme/tokens';\n\n/** 项的渲染状态 */\nexport interface SortableItemState {\n /** 当前位置,从 0 开始 */\n index: number;\n /** 正在被拖动或被键盘拿起 */\n active: boolean;\n}\n\n/** 未声明的属性落到 ul 上;aria-label 或 aria-labelledby 是列表的名称 */\nexport interface SortableProps<T> extends PartProps<'ul'> {\n /** 各项:父级写 _items={list},顺序变了写回新的数组 */\n _items?: T[];\n /** 项的 key,同一列表里唯一 */\n getKey: (item: T) => string;\n /** 项的名称:把手的名称与读屏播报用它;默认是 key */\n getLabel?: (item: T) => string;\n /** 排列方向,默认 vertical */\n orientation?: keyof SortableOrientations;\n /** 不可用:不能拖动 */\n disabled?: boolean;\n /** 每项的内容 */\n children?: (item: T, state: SortableItemState) => VNodeChild;\n /** 每一项(li)的部件属性,可以按项计算 */\n propsItem?: PartProps<'li'> | ((item: T, state: SortableItemState) => PartProps<'li'>);\n /** 把手的部件属性 */\n propsHandle?: Partial<ButtonBaseProps>;\n /** 内容的部件属性 */\n propsContent?: PartProps<'div'>;\n}\n\n/** Sortable 的样式:item、active、handle、content */\nexport const sortableStyles = css.create({\n // 方向、间距、去掉圆点由 FlexList 给\n item: {\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n gap: '_sm',\n paddingBlock: '_xs',\n // 起始边是把手(自带留白),结束边多留一点,文字不贴着边框\n paddingInlineStart: '_xs',\n paddingInlineEnd: '_sm',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_borderMuted',\n borderRadius: '_md',\n backgroundColor: '_surface',\n color: '_text',\n },\n // 横排时项不收缩:放不下就溢出(外面包 ScrollArea 横向滚动),不把文字挤成一字一行\n itemHorizontal: { flexShrink: 0 },\n // 拿起时浮起来:阴影与品牌色边框,盖在其他项上面\n active: { zIndex: 1, borderColor: '_accent', boxShadow: '_md' },\n // 去掉按钮默认外观、焦点环由 ButtonBase 给出\n handle: {\n display: 'grid',\n placeItems: 'center',\n flexShrink: 0,\n width: `max(1.75em, ${t.sizes.minTarget})`,\n height: `max(1.75em, ${t.sizes.minTarget})`,\n borderRadius: '_sm',\n backgroundColor: { default: 'transparent', ':hover': '_fillHover' },\n color: '_textMuted',\n cursor: { default: 'grab', ':active': 'grabbing' },\n // 触摸拖动时不滚动页面\n touchAction: 'none',\n },\n content: { flexGrow: 1, minWidth: 0 },\n});\n\n/** 可拖动排序的列表:把手拖动或键盘移动 */\nexport const Sortable = _component(\n <T,>({\n _items: items = _bindable<T[]>([]),\n getKey,\n getLabel,\n orientation = 'vertical',\n disabled = false,\n children,\n propsItem,\n propsHandle,\n propsContent,\n ...rest\n }: SortableProps<T>) => {\n const lang = useLang();\n let root = _state.raw<HTMLElement | null>(null);\n const sortable = useSortable<T>({\n items: () => items,\n getKey: (item) => getKey(item),\n getLabel: (item) => getLabel?.(item) ?? getKey(item),\n onChange: (next) => (items = next),\n container: () => root,\n orientation: () => orientation,\n disabled: () => disabled,\n });\n\n return () => (\n <>\n <FlexList\n {...mergePartProps<FlexProps<'ul'>>(\n {\n direction: orientation === 'horizontal' ? 'row' : 'column',\n gap: 'xs',\n },\n rest,\n { ref: (value: unknown) => (root = toElement(value)) },\n )}\n data-orientation={orientation}\n >\n {items.map((item, index) => {\n const key = getKey(item);\n const state: SortableItemState = { index, active: sortable.active === key };\n const own = typeof propsItem === 'function' ? propsItem(item, state) : propsItem;\n return (\n <li\n key={key}\n {...mergeAttrs<'li'>(\n {\n sx: [\n sortableStyles.item,\n orientation === 'horizontal' && sortableStyles.itemHorizontal,\n state.active && sortableStyles.active,\n ],\n },\n own,\n sortable.itemProps(key),\n )}\n >\n <ButtonBase\n {...mergePartProps<ButtonBaseProps>(\n // 拖动的把手不出波纹\n { sx: sortableStyles.handle, ripple: false },\n propsHandle,\n {\n disabled,\n 'aria-label': lang.sortableHandle(getLabel?.(item) ?? key),\n ...sortable.handleProps(key),\n },\n )}\n >\n <Icon icon={GripVertical} />\n </ButtonBase>\n <div {...mergeAttrs<'div'>({ sx: sortableStyles.content }, propsContent)}>\n {children?.(item, state)}\n </div>\n </li>\n );\n })}\n </FlexList>\n <VisuallyHidden id={sortable.instructionsId}>{lang.sortableInstructions}</VisuallyHidden>\n <VisuallyHidden aria-live=\"assertive\">{sortable.announcement}</VisuallyHidden>\n </>\n );\n },\n { name: 'Sortable' },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,IAAa,iBAAiB,IAAI,OAAO;CAEvC,MAAM;EACJ,UAAU;EACV,SAAS;EACT,YAAY;EACZ,KAAK;EACL,cAAc;EAEd,oBAAoB;EACpB,kBAAkB;EAClB,aAAa;EACb,aAAa;EACb,aAAa;EACb,cAAc;EACd,iBAAiB;EACjB,OAAO;CACT;CAEA,gBAAgB,EAAE,YAAY,EAAE;CAEhC,QAAQ;EAAE,QAAQ;EAAG,aAAa;EAAW,WAAW;CAAM;CAE9D,QAAQ;EACN,SAAS;EACT,YAAY;EACZ,YAAY;EACZ,OAAO,eAAe,EAAE,MAAM,UAAS;EACvC,QAAQ,eAAe,EAAE,MAAM,UAAS;EACxC,cAAc;EACd,iBAAiB;GAAE,SAAS;GAAe,UAAU;EAAa;EAClE,OAAO;EACP,QAAQ;GAAE,SAAS;GAAQ,WAAW;EAAW;EAEjD,aAAa;CACf;CACA,SAAS;EAAE,UAAU;EAAG,UAAU;CAAE;AACtC,CAAC;;AAGD,IAAa,WAAW,0BACtB,QAAA,SAWwB;CAAA,MAAA,QAAAA,SAAA,QAAA,gBAVS,CAAA,GAAE,KAAA,IAAA;CAAA,MAAA,SAAAC,aAAA,QAAA,QAAA;CAAA,MAAA,WAAAA,aAAA,QAAA,UAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,eAGnB,UAAU;CAAA,MAAA,WAAAA,aAAA,QAAA,YACb,KAAK;CAAA,MAAA,WAAAC,aAAA,MAAA;CAAA,MAAA,YAAAD,aAAA,QAAA,WAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,eAAAA,aAAA,QAAA,cAAA;CAAA,MAKb,OAAIE,UAAA,QAAA,MAAA,KAAA;CAEP,MAAM,OAAO,QAAQ;CACrB,IAAI,OAAIC,WAAkC,IAAI;CAC9C,MAAM,WAAW,YAAe;EAC9B,aAAO,MAAA;EACP,SAAS,SAAS,OAAA,MAAO,IAAI;EAC7B,WAAW,SAAS,SAAA,QAAW,IAAI,KAAK,OAAA,MAAO,IAAI;EACnD,WAAW,SAAU,MAAA,QAAQ;EAC7B,iBAAW,KAAA;EACX,mBAAa,YAAA;EACb,gBAAU,SAAA;CACZ,CAAC;CAED,aAAOC,YAAAC,UAAA,MAAA;EAAAD,YAAA,UAAAE,
|
|
1
|
+
{"version":3,"file":"Sortable.js","names":["_vtp_bindable","_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_vue_shallowRef","_createVNode","_Fragment","_mergeProps","_vtp_childrenStable"],"sources":["../../src/display/Sortable.tsx"],"sourcesContent":["/**\n * 可拖动排序的列表(ul):每项前有一个把手,拖动或用键盘改变顺序,数据双向绑定。\n *\n * <Sortable _items={tasks} getKey={(task) => task.id} getLabel={(task) => task.title} aria-label=\"待办\">\n * {(task) => <Text>{task.title}</Text>}\n * </Sortable>\n *\n * 拖动、键盘、读屏播报、自动滚动、布局动画见 useSortable(utils/dom/sortable.ts)。\n * 列表自己不滚动:项多时在外面包 ScrollArea(横排写 axis=\"x\"),拖到边缘时自动滚动会找到它的视口,滚动条与整站一致。\n * 把手是按钮:名称“移动 项名”,操作说明经 aria-describedby 关联,拿起时 aria-pressed。\n * 部件:Root(ul)、Item(li)、Handle(button)、Content(div)。\n */\nimport { GripVertical } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { ButtonBase, type ButtonBaseProps } from '../base/ButtonBase';\nimport { Icon } from '../base/Icon';\nimport { VisuallyHidden } from '../base/VisuallyHidden';\nimport { useLang } from '../config/context';\nimport { useSortable, type SortableOrientations } from '../utils/dom/sortable';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { FlexList, type FlexProps } from '../layout/Flex';\nimport { toElement } from '../utils/dom/element';\nimport { t } from '../config/theme/tokens';\n\n/** 项的渲染状态 */\nexport interface SortableItemState {\n /** 当前位置,从 0 开始 */\n index: number;\n /** 正在被拖动或被键盘拿起 */\n active: boolean;\n}\n\n/** 未声明的属性落到 ul 上;aria-label 或 aria-labelledby 是列表的名称 */\nexport interface SortableProps<T> extends PartProps<'ul'> {\n /** 各项:父级写 _items={list},顺序变了写回新的数组 */\n _items?: T[];\n /** 项的 key,同一列表里唯一 */\n getKey: (item: T) => string;\n /** 项的名称:把手的名称与读屏播报用它;默认是 key */\n getLabel?: (item: T) => string;\n /** 排列方向,默认 vertical */\n orientation?: keyof SortableOrientations;\n /** 不可用:不能拖动 */\n disabled?: boolean;\n /** 每项的内容 */\n children?: (item: T, state: SortableItemState) => VNodeChild;\n /** 每一项(li)的部件属性,可以按项计算 */\n propsItem?: PartProps<'li'> | ((item: T, state: SortableItemState) => PartProps<'li'>);\n /** 把手的部件属性 */\n propsHandle?: Partial<ButtonBaseProps>;\n /** 内容的部件属性 */\n propsContent?: PartProps<'div'>;\n}\n\n/** Sortable 的样式:item、active、handle、content */\nexport const sortableStyles = css.create({\n // 方向、间距、去掉圆点由 FlexList 给\n item: {\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n gap: '_sm',\n paddingBlock: '_xs',\n // 起始边是把手(自带留白),结束边多留一点,文字不贴着边框\n paddingInlineStart: '_xs',\n paddingInlineEnd: '_sm',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_borderMuted',\n borderRadius: '_md',\n backgroundColor: '_surface',\n color: '_text',\n },\n // 横排时项不收缩:放不下就溢出(外面包 ScrollArea 横向滚动),不把文字挤成一字一行\n itemHorizontal: { flexShrink: 0 },\n // 拿起时浮起来:阴影与品牌色边框,盖在其他项上面\n active: { zIndex: 1, borderColor: '_accent', boxShadow: '_md' },\n // 去掉按钮默认外观、焦点环由 ButtonBase 给出\n handle: {\n display: 'grid',\n placeItems: 'center',\n flexShrink: 0,\n width: `max(1.75em, ${t.sizes.minTarget})`,\n height: `max(1.75em, ${t.sizes.minTarget})`,\n borderRadius: '_sm',\n backgroundColor: { default: 'transparent', ':hover': '_fillHover' },\n color: '_textMuted',\n cursor: { default: 'grab', ':active': 'grabbing' },\n // 触摸拖动时不滚动页面\n touchAction: 'none',\n },\n content: { flexGrow: 1, minWidth: 0 },\n});\n\n/** 可拖动排序的列表:把手拖动或键盘移动 */\nexport const Sortable = _component(\n <T,>({\n _items: items = _bindable<T[]>([]),\n getKey,\n getLabel,\n orientation = 'vertical',\n disabled = false,\n children,\n propsItem,\n propsHandle,\n propsContent,\n ...rest\n }: SortableProps<T>) => {\n const lang = useLang();\n let root = _state.raw<HTMLElement | null>(null);\n const sortable = useSortable<T>({\n items: () => items,\n getKey: (item) => getKey(item),\n getLabel: (item) => getLabel?.(item) ?? getKey(item),\n onChange: (next) => (items = next),\n container: () => root,\n orientation: () => orientation,\n disabled: () => disabled,\n });\n\n return () => (\n <>\n <FlexList\n {...mergePartProps<FlexProps<'ul'>>(\n {\n direction: orientation === 'horizontal' ? 'row' : 'column',\n gap: 'xs',\n },\n rest,\n { ref: (value: unknown) => (root = toElement(value)) },\n )}\n data-orientation={orientation}\n >\n {items.map((item, index) => {\n const key = getKey(item);\n const state: SortableItemState = { index, active: sortable.active === key };\n const own = typeof propsItem === 'function' ? propsItem(item, state) : propsItem;\n return (\n <li\n key={key}\n {...mergeAttrs<'li'>(\n {\n sx: [\n sortableStyles.item,\n orientation === 'horizontal' && sortableStyles.itemHorizontal,\n state.active && sortableStyles.active,\n ],\n },\n own,\n sortable.itemProps(key),\n )}\n >\n <ButtonBase\n {...mergePartProps<ButtonBaseProps>(\n // 拖动的把手不出波纹\n { sx: sortableStyles.handle, ripple: false },\n propsHandle,\n {\n disabled,\n 'aria-label': lang.sortableHandle(getLabel?.(item) ?? key),\n ...sortable.handleProps(key),\n },\n )}\n >\n <Icon icon={GripVertical} />\n </ButtonBase>\n <div {...mergeAttrs<'div'>({ sx: sortableStyles.content }, propsContent)}>\n {children?.(item, state)}\n </div>\n </li>\n );\n })}\n </FlexList>\n <VisuallyHidden id={sortable.instructionsId}>{lang.sortableInstructions}</VisuallyHidden>\n <VisuallyHidden aria-live=\"assertive\">{sortable.announcement}</VisuallyHidden>\n </>\n );\n },\n { name: 'Sortable' },\n);\n"],"mappings":"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"}
|
|
@@ -4,8 +4,8 @@ import { useLang } from "../config/context.js";
|
|
|
4
4
|
import { mergeAttrs } from "../utils/props.js";
|
|
5
5
|
import { toneFillStyles } from "../utils/style/tone-styles.js";
|
|
6
6
|
import { useFormat } from "../config/lang/format.js";
|
|
7
|
-
import { createVNode
|
|
8
|
-
import { _component, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
7
|
+
import { createVNode } from "vue";
|
|
8
|
+
import { _component, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
9
9
|
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
10
10
|
//#region src/display/Sparkline.tsx
|
|
11
11
|
/** 迷你图的画法 */
|
|
@@ -96,20 +96,20 @@ var Sparkline = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
96
96
|
] }, rest, {
|
|
97
97
|
role: "img",
|
|
98
98
|
"aria-label": label.value ? `${label.value}:${summary}` : summary
|
|
99
|
-
}), [createVNode("svg",
|
|
99
|
+
}), [createVNode("svg", mergeJsxProps(css.attrs(sparklineStyles.svg), {
|
|
100
100
|
"viewBox": `0 0 ${width} 100`,
|
|
101
101
|
"preserveAspectRatio": "none",
|
|
102
102
|
"aria-hidden": "true"
|
|
103
|
-
}), [bars ? values.map((value, position) => value === void 0 ? null : createVNode("rect",
|
|
103
|
+
}), [bars ? values.map((value, position) => value === void 0 ? null : createVNode("rect", mergeJsxProps({
|
|
104
104
|
"key": position,
|
|
105
105
|
"x": position + .15,
|
|
106
106
|
"width": .7,
|
|
107
107
|
"y": Math.min(yOf(value), yOf(Math.max(low, 0))),
|
|
108
108
|
"height": Math.abs(yOf(value) - yOf(Math.max(low, 0)))
|
|
109
|
-
}, css.attrs(sparklineStyles.bar)), null)) : segments.map((points, position) => createVNode("g", { "key": position }, [variant.value === "area" && points.length > 1 && createVNode("path",
|
|
109
|
+
}, css.attrs(sparklineStyles.bar)), null)) : segments.map((points, position) => createVNode("g", { "key": position }, [variant.value === "area" && points.length > 1 && createVNode("path", mergeJsxProps({ "d": `${path(points)} L${points.at(-1)[0]} 100 L${points[0][0]} 100 Z` }, css.attrs(sparklineStyles.area)), null), createVNode("path", mergeJsxProps({
|
|
110
110
|
"d": path(points),
|
|
111
111
|
"vector-effect": "non-scaling-stroke"
|
|
112
|
-
}, css.attrs(sparklineStyles.line)), null)]))]), showLast.value && !bars && lastIndex >= 0 && createVNode("span",
|
|
112
|
+
}, css.attrs(sparklineStyles.line)), null)]))]), showLast.value && !bars && lastIndex >= 0 && createVNode("span", mergeJsxProps({ "aria-hidden": "true" }, css.attrs(sparklineStyles.last, css.bind(sparklineStyles.position, () => [`${lastIndex / width * 100}%`, `${yOf(values[lastIndex])}%`]))), null)]);
|
|
113
113
|
};
|
|
114
114
|
}, {
|
|
115
115
|
name: "Sparkline",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sparkline.js","names":["_vtp_readonlyProp","_vtp_restProps","_createVNode","_mergeProps"],"sources":["../../src/display/Sparkline.tsx"],"sourcesContent":["/**\n * 迷你图:没有坐标轴的小折线、面积或柱,嵌在指标卡(Stat 的 children)、表格的格子、列表的一行里看走势。\n * 纯 SVG,不依赖图表库;宽度占满容器,高度默认 2em(随字号缩放)。\n *\n * <Stat label=\"实际销售\" value={910.36} unit=\"万元\">\n * <Sparkline data={monthly} variant=\"area\" label=\"近 12 个月\" />\n * </Stat>\n * <Sparkline data={[3, 5, null, 4, 8]} variant=\"bar\" tone=\"success\" />\n *\n * 空值(null、undefined)处断开(柱是空位);默认标出最后一个点。读屏念成一句话:几个值、从哪到哪、最低最高\n * (按 valueFormat 与当前语言格式化),label 写在前面。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { useLang } from '../config/context';\nimport { useFormat, type NumericFormat } from '../config/lang/format';\nimport { toNumber, type NumericValue } from '../utils/data/numeric';\nimport { mergeAttrs, type PartProps } from '../utils/props';\nimport { toneFillStyles, type StatusTone } from '../utils/style/tone-styles';\nimport { t } from '../config/theme/tokens';\n\n/** 迷你图的画法 */\nexport interface SparklineVariants {\n /** 折线(默认) */\n line: unknown;\n /** 折线下面填一层浅色 */\n area: unknown;\n /** 柱:每个值一根,从 0 起 */\n bar: unknown;\n}\n\n/** 角色与名称由组件给出(role=\"img\",名称是 label 加上数据的概括) */\nexport type SparklineManaged = 'role' | 'aria-label';\n\n/** 未声明的属性落到外层 span 上 */\nexport interface SparklineProps extends PartProps<'span', SparklineManaged> {\n /** 各个值,按时间先后;空值处断开 */\n data: readonly (NumericValue | null | undefined)[];\n /** 画法,默认 line */\n variant?: keyof SparklineVariants;\n /** 色调,默认 accent */\n tone?: StatusTone;\n /** 高度(CSS 长度),默认 2em */\n height?: string;\n /** 标出最后一个点(折线、面积),默认 true */\n showLast?: boolean;\n /** 纵向范围 [最小, 最大];不写的一端按数据取(柱从 0 起) */\n domain?: readonly [number | undefined, number | undefined];\n /** 读屏念的数值格式 */\n valueFormat?: NumericFormat;\n /** 名称:读屏先念它(“近 12 个月实际销售”) */\n label?: string;\n}\n\n/** Sparkline 的样式 */\nexport const sparklineStyles = css.create({\n root: { position: 'relative', display: 'block', width: '100%' },\n svg: { display: 'block', width: '100%', height: '100%', overflow: 'visible' },\n line: {\n fill: 'none',\n stroke: 'var(--tone-fill)',\n strokeWidth: 1.5,\n strokeLinejoin: 'round',\n strokeLinecap: 'round',\n },\n area: { fill: 'var(--tone-fill)', fillOpacity: 0.15, stroke: 'none' },\n bar: { fill: 'var(--tone-fill)', fillOpacity: 0.85 },\n // 最后一个点:HTML 圆点按百分比定位(SVG 拉伸时圆不变形)\n last: {\n position: 'absolute',\n width: '6px',\n height: '6px',\n borderRadius: '_full',\n backgroundColor: 'var(--tone-fill)',\n // 一圈表面色把点与线分开\n boxShadow: `0 0 0 2px ${t.colors.surface}`,\n transform: 'translate(-50%, -50%)',\n pointerEvents: 'none',\n },\n height: (height: string) => ({ height }),\n position: (left: string, top: string) => ({ left, top }),\n});\n\n/** 迷你图:小折线、面积或柱 */\nexport const Sparkline = _component(\n ({\n data,\n variant = 'line',\n tone = 'accent',\n height = '2em',\n showLast = true,\n domain,\n valueFormat,\n label,\n ...rest\n }: SparklineProps) => {\n const lang = useLang();\n const format = useFormat();\n return () => {\n const values = data.map((value) => (value == null ? undefined : toNumber(value)));\n const present = values.filter((value): value is number => value !== undefined);\n const bars = variant === 'bar';\n const low = domain?.[0] ?? Math.min(...present, ...(bars ? [0] : []));\n const high = domain?.[1] ?? Math.max(...present, ...(bars ? [0] : []));\n const span = high - low || 1;\n // 纵坐标 0(上)–100(下);横坐标按位置,柱每个占 1 格\n const yOf = (value: number) => 100 - ((value - low) / span) * 100;\n const width = bars ? values.length : Math.max(values.length - 1, 1);\n\n // 连续的段:空值处断开\n const segments: [number, number][][] = [];\n let current: [number, number][] = [];\n values.forEach((value, position) => {\n if (value === undefined) {\n if (current.length) segments.push(current);\n current = [];\n } else current.push([position, yOf(value)]);\n });\n if (current.length) segments.push(current);\n const path = (points: [number, number][]) =>\n points.map(([x, y], i) => `${i ? 'L' : 'M'}${x} ${y}`).join(' ');\n\n const lastIndex = values.findLastIndex((value) => value !== undefined);\n const summary =\n present.length === 0\n ? lang.emptyValue\n : lang.sparklineSummary(\n present.length,\n format.number(data[values.findIndex((value) => value !== undefined)]!, valueFormat),\n format.number(data[lastIndex]!, valueFormat),\n format.number(Math.min(...present), valueFormat),\n format.number(Math.max(...present), valueFormat),\n );\n\n return (\n <span\n {...mergeAttrs<'span'>(\n { sx: [sparklineStyles.root, sparklineStyles.height(height), toneFillStyles[tone]] },\n rest,\n { role: 'img', 'aria-label': label ? `${label}:${summary}` : summary },\n )}\n >\n <svg\n {...css.attrs(sparklineStyles.svg)}\n viewBox={`0 0 ${width} 100`}\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n >\n {bars\n ? values.map((value, position) =>\n value === undefined ? null : (\n <rect\n key={position}\n x={position + 0.15}\n width={0.7}\n y={Math.min(yOf(value), yOf(Math.max(low, 0)))}\n height={Math.abs(yOf(value) - yOf(Math.max(low, 0)))}\n {...css.attrs(sparklineStyles.bar)}\n />\n ),\n )\n : segments.map((points, position) => (\n <g key={position}>\n {variant === 'area' && points.length > 1 && (\n <path\n d={`${path(points)} L${points.at(-1)![0]} 100 L${points[0]![0]} 100 Z`}\n {...css.attrs(sparklineStyles.area)}\n />\n )}\n <path\n d={path(points)}\n vector-effect=\"non-scaling-stroke\"\n {...css.attrs(sparklineStyles.line)}\n />\n </g>\n ))}\n </svg>\n {showLast && !bars && lastIndex >= 0 && (\n <span\n aria-hidden=\"true\"\n {...css.attrs(\n sparklineStyles.last,\n sparklineStyles.position(\n `${(lastIndex / width) * 100}%`,\n `${yOf(values[lastIndex]!)}%`,\n ),\n )}\n />\n )}\n </span>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Sparkline.js","names":["_vtp_readonlyProp","_vtp_restProps","_createVNode","_mergeProps"],"sources":["../../src/display/Sparkline.tsx"],"sourcesContent":["/**\n * 迷你图:没有坐标轴的小折线、面积或柱,嵌在指标卡(Stat 的 children)、表格的格子、列表的一行里看走势。\n * 纯 SVG,不依赖图表库;宽度占满容器,高度默认 2em(随字号缩放)。\n *\n * <Stat label=\"实际销售\" value={910.36} unit=\"万元\">\n * <Sparkline data={monthly} variant=\"area\" label=\"近 12 个月\" />\n * </Stat>\n * <Sparkline data={[3, 5, null, 4, 8]} variant=\"bar\" tone=\"success\" />\n *\n * 空值(null、undefined)处断开(柱是空位);默认标出最后一个点。读屏念成一句话:几个值、从哪到哪、最低最高\n * (按 valueFormat 与当前语言格式化),label 写在前面。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { useLang } from '../config/context';\nimport { useFormat, type NumericFormat } from '../config/lang/format';\nimport { toNumber, type NumericValue } from '../utils/data/numeric';\nimport { mergeAttrs, type PartProps } from '../utils/props';\nimport { toneFillStyles, type StatusTone } from '../utils/style/tone-styles';\nimport { t } from '../config/theme/tokens';\n\n/** 迷你图的画法 */\nexport interface SparklineVariants {\n /** 折线(默认) */\n line: unknown;\n /** 折线下面填一层浅色 */\n area: unknown;\n /** 柱:每个值一根,从 0 起 */\n bar: unknown;\n}\n\n/** 角色与名称由组件给出(role=\"img\",名称是 label 加上数据的概括) */\nexport type SparklineManaged = 'role' | 'aria-label';\n\n/** 未声明的属性落到外层 span 上 */\nexport interface SparklineProps extends PartProps<'span', SparklineManaged> {\n /** 各个值,按时间先后;空值处断开 */\n data: readonly (NumericValue | null | undefined)[];\n /** 画法,默认 line */\n variant?: keyof SparklineVariants;\n /** 色调,默认 accent */\n tone?: StatusTone;\n /** 高度(CSS 长度),默认 2em */\n height?: string;\n /** 标出最后一个点(折线、面积),默认 true */\n showLast?: boolean;\n /** 纵向范围 [最小, 最大];不写的一端按数据取(柱从 0 起) */\n domain?: readonly [number | undefined, number | undefined];\n /** 读屏念的数值格式 */\n valueFormat?: NumericFormat;\n /** 名称:读屏先念它(“近 12 个月实际销售”) */\n label?: string;\n}\n\n/** Sparkline 的样式 */\nexport const sparklineStyles = css.create({\n root: { position: 'relative', display: 'block', width: '100%' },\n svg: { display: 'block', width: '100%', height: '100%', overflow: 'visible' },\n line: {\n fill: 'none',\n stroke: 'var(--tone-fill)',\n strokeWidth: 1.5,\n strokeLinejoin: 'round',\n strokeLinecap: 'round',\n },\n area: { fill: 'var(--tone-fill)', fillOpacity: 0.15, stroke: 'none' },\n bar: { fill: 'var(--tone-fill)', fillOpacity: 0.85 },\n // 最后一个点:HTML 圆点按百分比定位(SVG 拉伸时圆不变形)\n last: {\n position: 'absolute',\n width: '6px',\n height: '6px',\n borderRadius: '_full',\n backgroundColor: 'var(--tone-fill)',\n // 一圈表面色把点与线分开\n boxShadow: `0 0 0 2px ${t.colors.surface}`,\n transform: 'translate(-50%, -50%)',\n pointerEvents: 'none',\n },\n height: (height: string) => ({ height }),\n position: (left: string, top: string) => ({ left, top }),\n});\n\n/** 迷你图:小折线、面积或柱 */\nexport const Sparkline = _component(\n ({\n data,\n variant = 'line',\n tone = 'accent',\n height = '2em',\n showLast = true,\n domain,\n valueFormat,\n label,\n ...rest\n }: SparklineProps) => {\n const lang = useLang();\n const format = useFormat();\n return () => {\n const values = data.map((value) => (value == null ? undefined : toNumber(value)));\n const present = values.filter((value): value is number => value !== undefined);\n const bars = variant === 'bar';\n const low = domain?.[0] ?? Math.min(...present, ...(bars ? [0] : []));\n const high = domain?.[1] ?? Math.max(...present, ...(bars ? [0] : []));\n const span = high - low || 1;\n // 纵坐标 0(上)–100(下);横坐标按位置,柱每个占 1 格\n const yOf = (value: number) => 100 - ((value - low) / span) * 100;\n const width = bars ? values.length : Math.max(values.length - 1, 1);\n\n // 连续的段:空值处断开\n const segments: [number, number][][] = [];\n let current: [number, number][] = [];\n values.forEach((value, position) => {\n if (value === undefined) {\n if (current.length) segments.push(current);\n current = [];\n } else current.push([position, yOf(value)]);\n });\n if (current.length) segments.push(current);\n const path = (points: [number, number][]) =>\n points.map(([x, y], i) => `${i ? 'L' : 'M'}${x} ${y}`).join(' ');\n\n const lastIndex = values.findLastIndex((value) => value !== undefined);\n const summary =\n present.length === 0\n ? lang.emptyValue\n : lang.sparklineSummary(\n present.length,\n format.number(data[values.findIndex((value) => value !== undefined)]!, valueFormat),\n format.number(data[lastIndex]!, valueFormat),\n format.number(Math.min(...present), valueFormat),\n format.number(Math.max(...present), valueFormat),\n );\n\n return (\n <span\n {...mergeAttrs<'span'>(\n { sx: [sparklineStyles.root, sparklineStyles.height(height), toneFillStyles[tone]] },\n rest,\n { role: 'img', 'aria-label': label ? `${label}:${summary}` : summary },\n )}\n >\n <svg\n {...css.attrs(sparklineStyles.svg)}\n viewBox={`0 0 ${width} 100`}\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n >\n {bars\n ? values.map((value, position) =>\n value === undefined ? null : (\n <rect\n key={position}\n x={position + 0.15}\n width={0.7}\n y={Math.min(yOf(value), yOf(Math.max(low, 0)))}\n height={Math.abs(yOf(value) - yOf(Math.max(low, 0)))}\n {...css.attrs(sparklineStyles.bar)}\n />\n ),\n )\n : segments.map((points, position) => (\n <g key={position}>\n {variant === 'area' && points.length > 1 && (\n <path\n d={`${path(points)} L${points.at(-1)![0]} 100 L${points[0]![0]} 100 Z`}\n {...css.attrs(sparklineStyles.area)}\n />\n )}\n <path\n d={path(points)}\n vector-effect=\"non-scaling-stroke\"\n {...css.attrs(sparklineStyles.line)}\n />\n </g>\n ))}\n </svg>\n {showLast && !bars && lastIndex >= 0 && (\n <span\n aria-hidden=\"true\"\n {...css.attrs(\n sparklineStyles.last,\n sparklineStyles.position(\n `${(lastIndex / width) * 100}%`,\n `${yOf(values[lastIndex]!)}%`,\n ),\n )}\n />\n )}\n </span>\n );\n };\n },\n);\n"],"mappings":"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"}
|
package/dist/display/Stat.js
CHANGED
|
@@ -6,8 +6,8 @@ import { useDelayedLoading } from "../utils/motion/delayed-loading.js";
|
|
|
6
6
|
import { AnimatedNumber } from "./AnimatedNumber.js";
|
|
7
7
|
import { shimmerStyles } from "../utils/style/shimmer.js";
|
|
8
8
|
import { Skeleton } from "../feedback/Skeleton.js";
|
|
9
|
-
import { Fragment, createVNode
|
|
10
|
-
import { _component, childrenProp, createContext, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
9
|
+
import { Fragment, createVNode } from "vue";
|
|
10
|
+
import { _component, childrenProp, createContext, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
11
11
|
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
12
12
|
import { TrendingDown, TrendingUp } from "@lucide/icons";
|
|
13
13
|
//#region src/display/Stat.tsx
|
|
@@ -189,7 +189,7 @@ var Stat = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
189
189
|
value: value.value,
|
|
190
190
|
format: format.value
|
|
191
191
|
}), null) : createVNode("span", mergeAttrs({ sx: [statStyles.number, tone.value && toneStyles[tone.value]] }, propsNumber.value), [value.value]), unit.value != null && !skeleton.value && createVNode("span", mergeAttrs({ sx: statStyles.unit }, propsUnit.value), [unit.value])]),
|
|
192
|
-
change.value !== void 0 && createVNode("dd",
|
|
192
|
+
change.value !== void 0 && createVNode("dd", mergeJsxProps(mergeAttrs({ sx: [statStyles.change, reveal] }, propsChange.value), { "data-trend": skeleton.value ? void 0 : direction }), [skeleton.value ? createVNode(Skeleton, { "sx": statStyles.changeSkeleton }, null) : createVNode(Fragment, null, [createVNode("span", css.attrs(toneStyles[changeTone]), [
|
|
193
193
|
direction !== "flat" && createVNode(Icon, {
|
|
194
194
|
"icon": direction === "up" ? TrendingUp : TrendingDown,
|
|
195
195
|
"aria-hidden": "true"
|
package/dist/display/Stat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Stat.js","names":["_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_createVNode","_mergeProps","_Fragment"],"sources":["../../src/display/Stat.tsx"],"sourcesContent":["/**\n * 统计数值(dl):指标名、数值与单位、可选的图标与环比变化。仪表盘顶部的一排卡片、详情页的关键指标。\n * 外观只是内容,放进 Card、Grid 里组合:\n *\n * <Card><Stat label=\"实际销售额\" value={910.36} unit=\"万元\" icon={Wallet} change={0.123} changeLabel=\"较上月\" /></Card>\n * <Stat label=\"测算销售\" value={a} format={wan} unit=\"万元\" footer={<><span>年度目标 2,000 万元</span><span>完成 81%</span></>}>\n * <Progress value={81} size=\"xs\" aria-label=\"年度目标完成度\" />\n * </Stat>\n *\n * 排版(参考 Ant Design Pro 的 ChartCard):第一行是图标与指标名、结束边是变化量;第二行是数值;\n * children 是数值下面占满整宽的补充内容(进度条、迷你图);footer 是底栏,上方一条分隔线,放两项时分别靠两端。\n * 卡片再宽,内容也铺满整行,不在中间留一大块空白。\n *\n * 一排几个指标用 StatGroup:按宽度换行,指标之间一条竖线(同一行里):\n *\n * <StatGroup><Stat label=\"超时\" value={10} tone=\"danger\" /><Stat label=\"待验收\" value={5} /></StatGroup>\n *\n * 数值变化时滚动过渡(AnimatedNumber),读屏只读终值;变化量按正负着色并带箭头,\n * 成本、耗时这类“越低越好”的指标写 invertChange。加载中(loading)数值与变化量换成扫光的骨架,指标名照常显示。\n */\nimport { TrendingDown, TrendingUp } from '@lucide/icons';\nimport { createContext, _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { Icon, type IconData } from '../base/Icon';\nimport type { NumericFormat } from '../config/lang/format';\nimport { Skeleton } from '../feedback/Skeleton';\nimport { useDelayedLoading } from '../utils/motion/delayed-loading';\nimport { hasContent, mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { shimmerStyles } from '../utils/style/shimmer';\nimport { toneStyles, type Tone } from '../utils/style/typography';\nimport type { NumericValue } from '../utils/data/numeric';\nimport { AnimatedNumber, type AnimatedNumberProps } from './AnimatedNumber';\nimport { t } from '../config/theme/tokens';\n\n/** 忙碌由组件按 loading 管理 */\nexport type StatManaged = 'aria-busy';\n\n/** 未声明的属性落到外层 dl 上 */\nexport interface StatProps extends PartProps<'dl', StatManaged> {\n /** 指标名 */\n label: VNodeChild;\n /** 数值:number、bigint、Decimal 按当前语言格式化并在变化时滚动;字符串原样显示(如“—”、“98.5%”) */\n value: NumericValue | string;\n /**\n * 数值的格式,如两位小数 `{ minimumFractionDigits: 2 }`、百分比 `{ style: 'percent' }`;\n * 值以元给出、按万元显示时写 `{ scale: 10_000, maximumFractionDigits: 2 }` 并配 `unit=\"万元\"`\n */\n format?: NumericFormat;\n /** 单位,跟在数值后面、字号小一档 */\n unit?: VNodeChild;\n /** 数值的色调:超时、缺漏这类要引起注意的数用 danger、warning;不写时是正文色 */\n tone?: Tone;\n /** 指标名前面的图标(装饰,对读屏隐藏) */\n icon?: IconData;\n /** 变化量(环比、同比):正数向上、负数向下;默认按百分比显示(0.12 → +12%) */\n change?: number;\n /** 变化量的格式,默认 `{ style: 'percent', maximumFractionDigits: 1, signDisplay: 'exceptZero' }` */\n changeFormat?: NumericFormat;\n /** 变化量后面的说明,如“较上月” */\n changeLabel?: VNodeChild;\n /** 下降是好事(成本、耗时、故障数):下降显示成功色、上升显示危险色 */\n invertChange?: boolean;\n /** 数值下面的补充说明(一行小字) */\n description?: VNodeChild;\n /** 底栏:上方一条分隔线;放两项时分别靠两端,如“年度目标 2,000 万元”与“完成 81%” */\n footer?: VNodeChild;\n /** 数值下面占满整宽的补充内容:进度条、迷你图 */\n children?: VNodeChild;\n /**\n * 加载中:数值与变化量换成扫光的骨架,带 aria-busy;很快完成时不闪\n * @default false\n */\n loading?: boolean;\n /** 指标名(dt)的部件属性 */\n propsLabel?: PartProps<'dt'>;\n /** 数值(dd)的部件属性 */\n propsValue?: PartProps<'dd'>;\n /** 图标外层的部件属性 */\n propsIcon?: PartProps<'span', 'aria-hidden'>;\n /** 数字本身(AnimatedNumber 或字符串值的 span)的部件属性 */\n propsNumber?: PartProps<'span', 'prefix'>;\n /** 单位的部件属性 */\n propsUnit?: PartProps<'span'>;\n /** 变化(dd)的部件属性 */\n propsChange?: PartProps<'dd'>;\n /** 变化后面的说明的部件属性 */\n propsChangeLabel?: PartProps<'span'>;\n /** 补充说明(dd)的部件属性 */\n propsDescription?: PartProps<'dd'>;\n /** 补充内容(dd,children 的外层)的部件属性 */\n propsBody?: PartProps<'dd'>;\n /** 底栏(dd)的部件属性 */\n propsFooter?: PartProps<'dd'>;\n}\n\nconst CHANGE_FORMAT: NumericFormat = {\n style: 'percent',\n maximumFractionDigits: 1,\n signDisplay: 'exceptZero',\n};\n\n/** 在 StatGroup 里:指标之间画竖线 */\nconst StatGroupContext = createContext<boolean>('StatGroup');\n\n/** Stat 的样式:root、label、icon、value、number、unit、change、description、body、footer、skeleton… */\nexport const statStyles = css.create({\n // 第一行是指标名与变化量(结束边),其余各占一整行。可换行的弹性布局而不是两列网格:\n // 卡片窄时变化量换到下一行,指标名不被挤成一字一行;DOM 顺序不变(读屏仍是名称、数值、变化量)\n root: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n columnGap: '_sm',\n rowGap: '_xs',\n margin: 0,\n minWidth: 0,\n },\n // dt 里放图标与指标名:dl 的子元素只能是 dt、dd\n label: {\n order: 0,\n flexGrow: 1,\n display: 'flex',\n alignItems: 'center',\n gap: '_sm',\n minWidth: 0,\n margin: 0,\n color: '_textMuted',\n fontSize: '_sm',\n },\n icon: {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n width: '1.75em',\n height: '1.75em',\n borderRadius: '_md',\n backgroundColor: '_accentSoft',\n color: '_accentText',\n },\n value: {\n order: 2,\n flexBasis: '100%',\n display: 'flex',\n alignItems: 'baseline',\n flexWrap: 'wrap',\n gap: '_xs',\n margin: 0,\n },\n number: {\n fontSize: '_xxl',\n fontWeight: '_bold',\n lineHeight: '_tight',\n color: '_text',\n fontVariantNumeric: 'tabular-nums',\n },\n unit: { color: '_textMuted', fontSize: '_xs' },\n // 变化量在指标名那一行的结束边\n change: {\n order: 1,\n display: 'flex',\n alignItems: 'center',\n gap: '_xs',\n fontSize: '_xs',\n margin: 0,\n whiteSpace: 'nowrap',\n },\n changeLabel: { color: '_textMuted' },\n description: { order: 3, flexBasis: '100%', color: '_textMuted', fontSize: '_xs', margin: 0 },\n body: { order: 3, flexBasis: '100%', margin: 0, minWidth: 0 },\n // 底栏:两项时分别靠两端,多了换行\n footer: {\n order: 3,\n flexBasis: '100%',\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'baseline',\n justifyContent: 'space-between',\n columnGap: '_md',\n rowGap: '_xs',\n margin: 0,\n marginBlockStart: '_xs',\n paddingBlockStart: '_sm',\n borderBlockStartWidth: '_xs',\n borderBlockStartStyle: 'solid',\n borderBlockStartColor: '_borderMuted',\n color: '_textMuted',\n fontSize: '_xs',\n },\n // 骨架与数值、变化量同高:加载完换成内容时不跳\n numberSkeleton: { fontSize: '_xxl', width: '4em', height: '1.25em' },\n changeSkeleton: { width: '6em', height: '1.5em' },\n});\n\n/** 统计数值:指标名、数值与单位、图标、变化量 */\nexport const Stat = _component(\n ({\n label,\n value,\n format,\n unit,\n tone,\n icon,\n change,\n changeFormat = CHANGE_FORMAT,\n changeLabel,\n invertChange = false,\n description,\n footer,\n children,\n loading = false,\n propsLabel,\n propsValue,\n propsIcon,\n propsNumber,\n propsUnit,\n propsChange,\n propsChangeLabel,\n propsDescription,\n propsBody,\n propsFooter,\n ...rest\n }: StatProps) => {\n const grouped = StatGroupContext.inject(false);\n // 骨架防闪烁;aria-busy 按真实的 loading\n const skeleton = useDelayedLoading(() => loading);\n // 出现过骨架之后,换回数值时淡入(样式在切换的那一刻加上,动画才会播)\n let revealing = false;\n return () => {\n if (skeleton.value) revealing = true;\n const reveal = revealing && !skeleton.value && shimmerStyles.reveal;\n const direction = change === undefined || change === 0 ? 'flat' : change > 0 ? 'up' : 'down';\n const improved = invertChange ? direction === 'down' : direction === 'up';\n const changeTone = direction === 'flat' ? 'muted' : improved ? 'success' : 'danger';\n return (\n <dl\n {...mergeAttrs<'dl'>({ sx: [statStyles.root, grouped && statGroupStyles.item] }, rest, {\n 'aria-busy': loading || undefined,\n })}\n >\n <dt {...mergeAttrs<'dt'>({ sx: statStyles.label }, propsLabel)}>\n {icon && (\n <span\n {...mergeAttrs<'span'>({ sx: statStyles.icon }, propsIcon, { 'aria-hidden': true })}\n >\n <Icon icon={icon} />\n </span>\n )}\n <span>{label}</span>\n </dt>\n <dd {...mergeAttrs<'dd'>({ sx: [statStyles.value, reveal] }, propsValue)}>\n {skeleton.value ? (\n <Skeleton sx={statStyles.numberSkeleton} />\n ) : typeof value !== 'string' ? (\n <AnimatedNumber\n {...mergePartProps<AnimatedNumberProps>(\n { sx: [statStyles.number, tone && toneStyles[tone]], value },\n propsNumber,\n {\n value,\n format,\n },\n )}\n />\n ) : (\n <span\n {...mergeAttrs<'span'>(\n { sx: [statStyles.number, tone && toneStyles[tone]] },\n propsNumber,\n )}\n >\n {value}\n </span>\n )}\n {unit != null && !skeleton.value && (\n <span {...mergeAttrs<'span'>({ sx: statStyles.unit }, propsUnit)}>{unit}</span>\n )}\n </dd>\n {change !== undefined && (\n <dd\n {...mergeAttrs<'dd'>({ sx: [statStyles.change, reveal] }, propsChange)}\n data-trend={skeleton.value ? undefined : direction}\n >\n {skeleton.value ? (\n <Skeleton sx={statStyles.changeSkeleton} />\n ) : (\n <>\n <span {...css.attrs(toneStyles[changeTone])}>\n {direction !== 'flat' && (\n <Icon\n icon={direction === 'up' ? TrendingUp : TrendingDown}\n aria-hidden=\"true\"\n />\n )}{' '}\n <AnimatedNumber value={change} format={changeFormat} />\n </span>\n {changeLabel != null && (\n <span {...mergeAttrs<'span'>({ sx: statStyles.changeLabel }, propsChangeLabel)}>\n {changeLabel}\n </span>\n )}\n </>\n )}\n </dd>\n )}\n {hasContent(description) && (\n <dd {...mergeAttrs<'dd'>({ sx: statStyles.description }, propsDescription)}>\n {description}\n </dd>\n )}\n {hasContent(children) && (\n <dd {...mergeAttrs<'dd'>({ sx: statStyles.body }, propsBody)}>{children}</dd>\n )}\n {hasContent(footer) && (\n <dd {...mergeAttrs<'dd'>({ sx: statStyles.footer }, propsFooter)}>{footer}</dd>\n )}\n </dl>\n );\n };\n },\n);\n\n/** 未声明的属性落到外层 div 上 */\nexport interface StatGroupProps extends PartProps<'div'> {\n /**\n * 每个指标最窄多宽(CSS 长度):放不下就换行\n * @default '9rem'\n */\n minItemWidth?: string;\n /** Stat */\n children?: VNodeChild;\n}\n\n/** StatGroup 的样式:root、item */\nexport const statGroupStyles = css.create({\n // 每个指标的起始边一条竖线;整组往起始方向挪一条线的位置,每行第一个的竖线藏到外面(overflow 裁掉)\n root: { display: 'grid', rowGap: '_md', overflow: 'hidden' },\n item: {\n paddingInline: '_md',\n marginInlineStart: `calc(-1 * ${t.borderWidths.xs})`,\n borderInlineStartWidth: '_xs',\n borderInlineStartStyle: 'solid',\n borderInlineStartColor: '_borderMuted',\n },\n});\n\n/** StatGroup 的动态样式:列宽 */\nexport const statGroupDynamicStyles = css.create({\n columns: (min: string) => ({\n gridTemplateColumns: `repeat(auto-fit, minmax(min(${min}, 100%), 1fr))`,\n }),\n});\n\n/** 一排指标:按宽度换行,同一行里指标之间一条竖线 */\nexport const StatGroup = _component(\n ({ minItemWidth = '9rem', children, ...rest }: StatGroupProps) => {\n StatGroupContext.provide(true);\n return () => (\n <div\n {...mergeAttrs<'div'>(\n { sx: [statGroupStyles.root, statGroupDynamicStyles.columns(minItemWidth)] },\n rest,\n )}\n >\n {children}\n </div>\n );\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Stat.js","names":["_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_createVNode","_mergeProps","_Fragment"],"sources":["../../src/display/Stat.tsx"],"sourcesContent":["/**\n * 统计数值(dl):指标名、数值与单位、可选的图标与环比变化。仪表盘顶部的一排卡片、详情页的关键指标。\n * 外观只是内容,放进 Card、Grid 里组合:\n *\n * <Card><Stat label=\"实际销售额\" value={910.36} unit=\"万元\" icon={Wallet} change={0.123} changeLabel=\"较上月\" /></Card>\n * <Stat label=\"测算销售\" value={a} format={wan} unit=\"万元\" footer={<><span>年度目标 2,000 万元</span><span>完成 81%</span></>}>\n * <Progress value={81} size=\"xs\" aria-label=\"年度目标完成度\" />\n * </Stat>\n *\n * 排版(参考 Ant Design Pro 的 ChartCard):第一行是图标与指标名、结束边是变化量;第二行是数值;\n * children 是数值下面占满整宽的补充内容(进度条、迷你图);footer 是底栏,上方一条分隔线,放两项时分别靠两端。\n * 卡片再宽,内容也铺满整行,不在中间留一大块空白。\n *\n * 一排几个指标用 StatGroup:按宽度换行,指标之间一条竖线(同一行里):\n *\n * <StatGroup><Stat label=\"超时\" value={10} tone=\"danger\" /><Stat label=\"待验收\" value={5} /></StatGroup>\n *\n * 数值变化时滚动过渡(AnimatedNumber),读屏只读终值;变化量按正负着色并带箭头,\n * 成本、耗时这类“越低越好”的指标写 invertChange。加载中(loading)数值与变化量换成扫光的骨架,指标名照常显示。\n */\nimport { TrendingDown, TrendingUp } from '@lucide/icons';\nimport { createContext, _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { Icon, type IconData } from '../base/Icon';\nimport type { NumericFormat } from '../config/lang/format';\nimport { Skeleton } from '../feedback/Skeleton';\nimport { useDelayedLoading } from '../utils/motion/delayed-loading';\nimport { hasContent, mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { shimmerStyles } from '../utils/style/shimmer';\nimport { toneStyles, type Tone } from '../utils/style/typography';\nimport type { NumericValue } from '../utils/data/numeric';\nimport { AnimatedNumber, type AnimatedNumberProps } from './AnimatedNumber';\nimport { t } from '../config/theme/tokens';\n\n/** 忙碌由组件按 loading 管理 */\nexport type StatManaged = 'aria-busy';\n\n/** 未声明的属性落到外层 dl 上 */\nexport interface StatProps extends PartProps<'dl', StatManaged> {\n /** 指标名 */\n label: VNodeChild;\n /** 数值:number、bigint、Decimal 按当前语言格式化并在变化时滚动;字符串原样显示(如“—”、“98.5%”) */\n value: NumericValue | string;\n /**\n * 数值的格式,如两位小数 `{ minimumFractionDigits: 2 }`、百分比 `{ style: 'percent' }`;\n * 值以元给出、按万元显示时写 `{ scale: 10_000, maximumFractionDigits: 2 }` 并配 `unit=\"万元\"`\n */\n format?: NumericFormat;\n /** 单位,跟在数值后面、字号小一档 */\n unit?: VNodeChild;\n /** 数值的色调:超时、缺漏这类要引起注意的数用 danger、warning;不写时是正文色 */\n tone?: Tone;\n /** 指标名前面的图标(装饰,对读屏隐藏) */\n icon?: IconData;\n /** 变化量(环比、同比):正数向上、负数向下;默认按百分比显示(0.12 → +12%) */\n change?: number;\n /** 变化量的格式,默认 `{ style: 'percent', maximumFractionDigits: 1, signDisplay: 'exceptZero' }` */\n changeFormat?: NumericFormat;\n /** 变化量后面的说明,如“较上月” */\n changeLabel?: VNodeChild;\n /** 下降是好事(成本、耗时、故障数):下降显示成功色、上升显示危险色 */\n invertChange?: boolean;\n /** 数值下面的补充说明(一行小字) */\n description?: VNodeChild;\n /** 底栏:上方一条分隔线;放两项时分别靠两端,如“年度目标 2,000 万元”与“完成 81%” */\n footer?: VNodeChild;\n /** 数值下面占满整宽的补充内容:进度条、迷你图 */\n children?: VNodeChild;\n /**\n * 加载中:数值与变化量换成扫光的骨架,带 aria-busy;很快完成时不闪\n * @default false\n */\n loading?: boolean;\n /** 指标名(dt)的部件属性 */\n propsLabel?: PartProps<'dt'>;\n /** 数值(dd)的部件属性 */\n propsValue?: PartProps<'dd'>;\n /** 图标外层的部件属性 */\n propsIcon?: PartProps<'span', 'aria-hidden'>;\n /** 数字本身(AnimatedNumber 或字符串值的 span)的部件属性 */\n propsNumber?: PartProps<'span', 'prefix'>;\n /** 单位的部件属性 */\n propsUnit?: PartProps<'span'>;\n /** 变化(dd)的部件属性 */\n propsChange?: PartProps<'dd'>;\n /** 变化后面的说明的部件属性 */\n propsChangeLabel?: PartProps<'span'>;\n /** 补充说明(dd)的部件属性 */\n propsDescription?: PartProps<'dd'>;\n /** 补充内容(dd,children 的外层)的部件属性 */\n propsBody?: PartProps<'dd'>;\n /** 底栏(dd)的部件属性 */\n propsFooter?: PartProps<'dd'>;\n}\n\nconst CHANGE_FORMAT: NumericFormat = {\n style: 'percent',\n maximumFractionDigits: 1,\n signDisplay: 'exceptZero',\n};\n\n/** 在 StatGroup 里:指标之间画竖线 */\nconst StatGroupContext = createContext<boolean>('StatGroup');\n\n/** Stat 的样式:root、label、icon、value、number、unit、change、description、body、footer、skeleton… */\nexport const statStyles = css.create({\n // 第一行是指标名与变化量(结束边),其余各占一整行。可换行的弹性布局而不是两列网格:\n // 卡片窄时变化量换到下一行,指标名不被挤成一字一行;DOM 顺序不变(读屏仍是名称、数值、变化量)\n root: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n columnGap: '_sm',\n rowGap: '_xs',\n margin: 0,\n minWidth: 0,\n },\n // dt 里放图标与指标名:dl 的子元素只能是 dt、dd\n label: {\n order: 0,\n flexGrow: 1,\n display: 'flex',\n alignItems: 'center',\n gap: '_sm',\n minWidth: 0,\n margin: 0,\n color: '_textMuted',\n fontSize: '_sm',\n },\n icon: {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n width: '1.75em',\n height: '1.75em',\n borderRadius: '_md',\n backgroundColor: '_accentSoft',\n color: '_accentText',\n },\n value: {\n order: 2,\n flexBasis: '100%',\n display: 'flex',\n alignItems: 'baseline',\n flexWrap: 'wrap',\n gap: '_xs',\n margin: 0,\n },\n number: {\n fontSize: '_xxl',\n fontWeight: '_bold',\n lineHeight: '_tight',\n color: '_text',\n fontVariantNumeric: 'tabular-nums',\n },\n unit: { color: '_textMuted', fontSize: '_xs' },\n // 变化量在指标名那一行的结束边\n change: {\n order: 1,\n display: 'flex',\n alignItems: 'center',\n gap: '_xs',\n fontSize: '_xs',\n margin: 0,\n whiteSpace: 'nowrap',\n },\n changeLabel: { color: '_textMuted' },\n description: { order: 3, flexBasis: '100%', color: '_textMuted', fontSize: '_xs', margin: 0 },\n body: { order: 3, flexBasis: '100%', margin: 0, minWidth: 0 },\n // 底栏:两项时分别靠两端,多了换行\n footer: {\n order: 3,\n flexBasis: '100%',\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'baseline',\n justifyContent: 'space-between',\n columnGap: '_md',\n rowGap: '_xs',\n margin: 0,\n marginBlockStart: '_xs',\n paddingBlockStart: '_sm',\n borderBlockStartWidth: '_xs',\n borderBlockStartStyle: 'solid',\n borderBlockStartColor: '_borderMuted',\n color: '_textMuted',\n fontSize: '_xs',\n },\n // 骨架与数值、变化量同高:加载完换成内容时不跳\n numberSkeleton: { fontSize: '_xxl', width: '4em', height: '1.25em' },\n changeSkeleton: { width: '6em', height: '1.5em' },\n});\n\n/** 统计数值:指标名、数值与单位、图标、变化量 */\nexport const Stat = _component(\n ({\n label,\n value,\n format,\n unit,\n tone,\n icon,\n change,\n changeFormat = CHANGE_FORMAT,\n changeLabel,\n invertChange = false,\n description,\n footer,\n children,\n loading = false,\n propsLabel,\n propsValue,\n propsIcon,\n propsNumber,\n propsUnit,\n propsChange,\n propsChangeLabel,\n propsDescription,\n propsBody,\n propsFooter,\n ...rest\n }: StatProps) => {\n const grouped = StatGroupContext.inject(false);\n // 骨架防闪烁;aria-busy 按真实的 loading\n const skeleton = useDelayedLoading(() => loading);\n // 出现过骨架之后,换回数值时淡入(样式在切换的那一刻加上,动画才会播)\n let revealing = false;\n return () => {\n if (skeleton.value) revealing = true;\n const reveal = revealing && !skeleton.value && shimmerStyles.reveal;\n const direction = change === undefined || change === 0 ? 'flat' : change > 0 ? 'up' : 'down';\n const improved = invertChange ? direction === 'down' : direction === 'up';\n const changeTone = direction === 'flat' ? 'muted' : improved ? 'success' : 'danger';\n return (\n <dl\n {...mergeAttrs<'dl'>({ sx: [statStyles.root, grouped && statGroupStyles.item] }, rest, {\n 'aria-busy': loading || undefined,\n })}\n >\n <dt {...mergeAttrs<'dt'>({ sx: statStyles.label }, propsLabel)}>\n {icon && (\n <span\n {...mergeAttrs<'span'>({ sx: statStyles.icon }, propsIcon, { 'aria-hidden': true })}\n >\n <Icon icon={icon} />\n </span>\n )}\n <span>{label}</span>\n </dt>\n <dd {...mergeAttrs<'dd'>({ sx: [statStyles.value, reveal] }, propsValue)}>\n {skeleton.value ? (\n <Skeleton sx={statStyles.numberSkeleton} />\n ) : typeof value !== 'string' ? (\n <AnimatedNumber\n {...mergePartProps<AnimatedNumberProps>(\n { sx: [statStyles.number, tone && toneStyles[tone]], value },\n propsNumber,\n {\n value,\n format,\n },\n )}\n />\n ) : (\n <span\n {...mergeAttrs<'span'>(\n { sx: [statStyles.number, tone && toneStyles[tone]] },\n propsNumber,\n )}\n >\n {value}\n </span>\n )}\n {unit != null && !skeleton.value && (\n <span {...mergeAttrs<'span'>({ sx: statStyles.unit }, propsUnit)}>{unit}</span>\n )}\n </dd>\n {change !== undefined && (\n <dd\n {...mergeAttrs<'dd'>({ sx: [statStyles.change, reveal] }, propsChange)}\n data-trend={skeleton.value ? undefined : direction}\n >\n {skeleton.value ? (\n <Skeleton sx={statStyles.changeSkeleton} />\n ) : (\n <>\n <span {...css.attrs(toneStyles[changeTone])}>\n {direction !== 'flat' && (\n <Icon\n icon={direction === 'up' ? TrendingUp : TrendingDown}\n aria-hidden=\"true\"\n />\n )}{' '}\n <AnimatedNumber value={change} format={changeFormat} />\n </span>\n {changeLabel != null && (\n <span {...mergeAttrs<'span'>({ sx: statStyles.changeLabel }, propsChangeLabel)}>\n {changeLabel}\n </span>\n )}\n </>\n )}\n </dd>\n )}\n {hasContent(description) && (\n <dd {...mergeAttrs<'dd'>({ sx: statStyles.description }, propsDescription)}>\n {description}\n </dd>\n )}\n {hasContent(children) && (\n <dd {...mergeAttrs<'dd'>({ sx: statStyles.body }, propsBody)}>{children}</dd>\n )}\n {hasContent(footer) && (\n <dd {...mergeAttrs<'dd'>({ sx: statStyles.footer }, propsFooter)}>{footer}</dd>\n )}\n </dl>\n );\n };\n },\n);\n\n/** 未声明的属性落到外层 div 上 */\nexport interface StatGroupProps extends PartProps<'div'> {\n /**\n * 每个指标最窄多宽(CSS 长度):放不下就换行\n * @default '9rem'\n */\n minItemWidth?: string;\n /** Stat */\n children?: VNodeChild;\n}\n\n/** StatGroup 的样式:root、item */\nexport const statGroupStyles = css.create({\n // 每个指标的起始边一条竖线;整组往起始方向挪一条线的位置,每行第一个的竖线藏到外面(overflow 裁掉)\n root: { display: 'grid', rowGap: '_md', overflow: 'hidden' },\n item: {\n paddingInline: '_md',\n marginInlineStart: `calc(-1 * ${t.borderWidths.xs})`,\n borderInlineStartWidth: '_xs',\n borderInlineStartStyle: 'solid',\n borderInlineStartColor: '_borderMuted',\n },\n});\n\n/** StatGroup 的动态样式:列宽 */\nexport const statGroupDynamicStyles = css.create({\n columns: (min: string) => ({\n gridTemplateColumns: `repeat(auto-fit, minmax(min(${min}, 100%), 1fr))`,\n }),\n});\n\n/** 一排指标:按宽度换行,同一行里指标之间一条竖线 */\nexport const StatGroup = _component(\n ({ minItemWidth = '9rem', children, ...rest }: StatGroupProps) => {\n StatGroupContext.provide(true);\n return () => (\n <div\n {...mergeAttrs<'div'>(\n { sx: [statGroupStyles.root, statGroupDynamicStyles.columns(minItemWidth)] },\n rest,\n )}\n >\n {children}\n </div>\n );\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgGA,IAAM,gBAA+B;CACnC,OAAO;CACP,uBAAuB;CACvB,aAAa;AACf;;AAGA,IAAM,mBAAmB,cAAuB,WAAW;;AAG3D,IAAa,aAAa,IAAI,OAAO;CAGnC,MAAM;EACJ,SAAS;EACT,UAAU;EACV,YAAY;EACZ,WAAW;EACX,QAAQ;EACR,QAAQ;EACR,UAAU;CACZ;CAEA,OAAO;EACL,OAAO;EACP,UAAU;EACV,SAAS;EACT,YAAY;EACZ,KAAK;EACL,UAAU;EACV,QAAQ;EACR,OAAO;EACP,UAAU;CACZ;CACA,MAAM;EACJ,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,YAAY;EACZ,OAAO;EACP,QAAQ;EACR,cAAc;EACd,iBAAiB;EACjB,OAAO;CACT;CACA,OAAO;EACL,OAAO;EACP,WAAW;EACX,SAAS;EACT,YAAY;EACZ,UAAU;EACV,KAAK;EACL,QAAQ;CACV;CACA,QAAQ;EACN,UAAU;EACV,YAAY;EACZ,YAAY;EACZ,OAAO;EACP,oBAAoB;CACtB;CACA,MAAM;EAAE,OAAO;EAAc,UAAU;CAAM;CAE7C,QAAQ;EACN,OAAO;EACP,SAAS;EACT,YAAY;EACZ,KAAK;EACL,UAAU;EACV,QAAQ;EACR,YAAY;CACd;CACA,aAAa,EAAE,OAAO,aAAa;CACnC,aAAa;EAAE,OAAO;EAAG,WAAW;EAAQ,OAAO;EAAc,UAAU;EAAO,QAAQ;CAAE;CAC5F,MAAM;EAAE,OAAO;EAAG,WAAW;EAAQ,QAAQ;EAAG,UAAU;CAAE;CAE5D,QAAQ;EACN,OAAO;EACP,WAAW;EACX,SAAS;EACT,UAAU;EACV,YAAY;EACZ,gBAAgB;EAChB,WAAW;EACX,QAAQ;EACR,QAAQ;EACR,kBAAkB;EAClB,mBAAmB;EACnB,uBAAuB;EACvB,uBAAuB;EACvB,uBAAuB;EACvB,OAAO;EACP,UAAU;CACZ;CAEA,gBAAgB;EAAE,UAAU;EAAQ,OAAO;EAAO,QAAQ;CAAS;CACnE,gBAAgB;EAAE,OAAO;EAAO,QAAQ;CAAQ;AAClD,CAAC;;AAGD,IAAa,OAAO,0BAClB,QAAA,SA0BiB;CAAA,MAAA,QAAAA,aAAA,QAAA,OAAA;CAAA,MAAA,QAAAA,aAAA,QAAA,OAAA;CAAA,MAAA,SAAAA,aAAA,QAAA,QAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,SAAAA,aAAA,QAAA,QAAA;CAAA,MAAA,eAAAA,aAAA,QAAA,sBAlBA,aAAa;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,eAAAA,aAAA,QAAA,gBAEb,KAAK;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,SAAAA,aAAA,QAAA,QAAA;CAAA,MAAA,WAAAC,aAAA,MAAA;CAAA,MAAA,UAAAD,aAAA,QAAA,WAIV,KAAK;CAAA,MAAA,aAAAA,aAAA,QAAA,YAAA;CAAA,MAAA,aAAAA,aAAA,QAAA,YAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,mBAAAA,aAAA,QAAA,kBAAA;CAAA,MAAA,mBAAAA,aAAA,QAAA,kBAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAWZ,OAAIE,UAAA,QAAA,MAAA,KAAA;CAEP,MAAM,UAAU,iBAAiB,OAAO,KAAK;CAE7C,MAAM,WAAW,wBAAkB,QAAA,KAAa;CAEhD,IAAI,YAAY;CAChB,aAAa;EACX,IAAI,SAAS,OAAO,YAAY;EAChC,MAAM,SAAS,aAAa,CAAC,SAAS,SAAS,cAAc;EAC7D,MAAM,YAAY,OAAA,UAAW,KAAA,KAAa,OAAA,UAAW,IAAI,SAAS,OAAA,QAAS,IAAI,OAAO;EACtF,MAAM,WAAW,aAAA,QAAe,cAAc,SAAS,cAAc;EACrE,MAAM,aAAa,cAAc,SAAS,UAAU,WAAW,YAAY;EAC3E,OAAAC,YAAA,MAEQ,WAAiB,EAAE,IAAI,CAAC,WAAW,MAAM,WAAW,gBAAgB,IAAI,EAAE,GAAG,MAAM,EACrF,aAAa,QAAA,SAAW,KAAA,EAC1B,CAAC,GAAC;GAAAA,YAAA,MAEM,WAAiB,EAAE,IAAI,WAAW,MAAM,GAAC,WAAA,KAAY,GAAC,CAC3D,KAAA,SAAAA,YAAA,QAEO,WAAmB,EAAE,IAAI,WAAW,KAAK,GAAC,UAAA,OAAa,EAAE,eAAe,KAAK,CAAC,GAAC,CAAAA,YAAA,MAAA,EAAA,QAAA,KAAA,MAAA,GAAA,IAAA,CAAA,CAAA,GAItFA,YAAA,QAAA,MAAA,CAAA,MAAA,KAAA,CAAA,CAAA,CAAA;GAAAA,YAAA,MAGK,WAAiB,EAAE,IAAI,CAAC,WAAW,OAAO,MAAM,EAAE,GAAC,WAAA,KAAY,GAAC,CACrE,SAAS,QAAKA,YAAA,UAAA,EAAA,MACC,WAAW,eAAc,GAAA,IAAA,IACrC,OAAA,MAAA,UAAiB,WAAQA,YAAA,gBAErB,eACF;IAAE,IAAI,CAAC,WAAW,QAAQ,KAAA,SAAQ,WAAU,KAAA,MAAM;IAAG,OAAK,MAAA;GAAC,GAAC,YAAA,OAE5D;IACE,OAAK,MAAA;IACL,QAAM,OAAA;GACR,CACF,GAAC,IAAA,IAAAA,YAAA,QAIG,WACF,EAAE,IAAI,CAAC,WAAW,QAAQ,KAAA,SAAQ,WAAU,KAAA,MAAM,EAAE,GAAC,YAAA,KAEvD,GAAC,CAAA,MAAA,KAAA,CAAA,GAKJ,KAAA,SAAQ,QAAQ,CAAC,SAAS,SAAKA,YAAA,QACpB,WAAmB,EAAE,IAAI,WAAW,KAAK,GAAC,UAAA,KAAW,GAAC,CAAA,KAAA,KAAA,CAAA,CACjE,CAAA;GAEF,OAAA,UAAW,KAAA,KAASA,YAAA,MAAAC,cAEb,WAAiB,EAAE,IAAI,CAAC,WAAW,QAAQ,MAAM,EAAE,GAAC,YAAA,KAAa,GAAC,EAAA,cAC1D,SAAS,QAAQ,KAAA,IAAY,UAAS,CAAA,GAAA,CAEjD,SAAS,QAAKD,YAAA,UAAA,EAAA,MACC,WAAW,eAAc,GAAA,IAAA,IAAAA,YAAAE,UAAA,MAAA,CAAAF,YAAA,QAG3B,IAAI,MAAM,WAAW,WAAW,GAAC;IACxC,cAAc,UAAMA,YAAA,MAAA;KAAA,QAEX,cAAc,OAAO,aAAa;KAAY,eAAA;IAAA,GAAA,IAAA;IAGrD;IAAGA,YAAA,gBAAA;KAAA,SAAA,OAAA;KAAA,UAAA,aAAA;IAAA,GAAA,IAAA;GAAA,CAAA,GAGP,YAAA,SAAe,QAAIA,YAAA,QACR,WAAmB,EAAE,IAAI,WAAW,YAAY,GAAC,iBAAA,KAAkB,GAAC,CAAA,YAAA,KAAA,CAAA,CAG/E,CAAA,CAEJ,CAAA;GAGJ,WAAU,YAAA,KAAY,KAACA,YAAA,MACd,WAAiB,EAAE,IAAI,WAAW,YAAY,GAAC,iBAAA,KAAkB,GAAC,CAAA,YAAA,KAAA,CAAA;GAI3E,WAAU,SAAA,KAAS,KAACA,YAAA,MACX,WAAiB,EAAE,IAAI,WAAW,KAAK,GAAC,UAAA,KAAW,GAAC,CAAA,SAAA,KAAA,CAAA;GAE7D,WAAU,OAAA,KAAO,KAACA,YAAA,MACT,WAAiB,EAAE,IAAI,WAAW,OAAO,GAAC,YAAA,KAAa,GAAC,CAAA,OAAA,KAAA,CAAA;EACjE,CAAA;CAGP;AACF,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH;;;AAcA,IAAa,kBAAkB,IAAI,OAAO;CAExC,MAAM;EAAE,SAAS;EAAQ,QAAQ;EAAO,UAAU;CAAS;CAC3D,MAAM;EACJ,eAAe;EACf,mBAAmB,aAAa,EAAE,aAAa,GAAE;EACjD,wBAAwB;EACxB,wBAAwB;EACxB,wBAAwB;CAC1B;AACF,CAAC;;AAGD,IAAa,yBAAyB,IAAI,OAAO,EAC/C,UAAU,SAAiB,EACzB,qBAAqB,+BAA+B,IAAG,gBACzD,GACF,CAAC;;AAGD,IAAa,YAAY,0BACvB,SAAA,UAAkE;CAAA,MAAA,eAAAH,aAAA,SAAA,gBAAhD,MAAM;CAAA,MAAA,WAAAC,aAAA,OAAA;CAAA,MAAe,OAAIC,UAAA,SAAA,OAAA,KAAA;CACzC,iBAAiB,QAAQ,IAAI;CAC7B,aAAOC,YAAA,OAEC,WACF,EAAE,IAAI,CAAC,gBAAgB,MAAI,IAAA,KAAE,uBAAuB,eAAO,CAAA,aAAA,KAAA,GAAA,IAAA,CAAA,EAAgB,GAC3E,IACF,GAAC,CAAA,SAAA,KAAA,CAAA;AAKP,GAAC;CAAA,MAAA;CAAA,OAAA,CAAA,cAAA;AAAA,CACH"}
|
package/dist/display/Timeline.js
CHANGED
|
@@ -7,8 +7,8 @@ import { Button } from "../base/Button.js";
|
|
|
7
7
|
import { FlexOrderedList } from "../layout/Flex.js";
|
|
8
8
|
import { Avatar, initials } from "./Avatar.js";
|
|
9
9
|
import { Collapsible } from "./Collapsible.js";
|
|
10
|
-
import { createVNode
|
|
11
|
-
import { _component, bindable, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
10
|
+
import { createVNode } from "vue";
|
|
11
|
+
import { _component, bindable, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
12
12
|
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
13
13
|
import { ChevronDown, ChevronUp } from "@lucide/icons";
|
|
14
14
|
//#region src/display/Timeline.tsx
|
|
@@ -158,7 +158,7 @@ var Timeline = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
158
158
|
const rest = restProps(_props, _ctx, true);
|
|
159
159
|
const lang = useLang();
|
|
160
160
|
/** 收起的那几项所在的位置:节点列一段虚线,内容是展开、收起的按钮 */
|
|
161
|
-
const toggle = (hidden) => createVNode("li",
|
|
161
|
+
const toggle = (hidden) => createVNode("li", mergeJsxProps({ "key": "toggle" }, css.attrs(timelineStyles.item)), [createVNode("div", mergeAttrs({ sx: timelineStyles.rail }, propsRail.value), [createVNode("span", mergeJsxProps(css.attrs(timelineStyles.toggleLine), { "aria-hidden": "true" }), null)]), createVNode("div", css.attrs(timelineStyles.toggleContent), [createVNode(Button, mergePartProps({
|
|
162
162
|
variant: "link",
|
|
163
163
|
size: "sm",
|
|
164
164
|
iconEnd: expanded.value ? ChevronUp : ChevronDown
|
|
@@ -175,7 +175,7 @@ var Timeline = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
175
175
|
}] : []];
|
|
176
176
|
const all = reverse.value ? chronological.toReversed() : chronological;
|
|
177
177
|
const toggleAtEnd = hidden > 0 && reverse.value;
|
|
178
|
-
return createVNode(FlexOrderedList,
|
|
178
|
+
return createVNode(FlexOrderedList, mergeJsxProps(mergePartProps({
|
|
179
179
|
direction: "column",
|
|
180
180
|
sx: [timelineStyles.root, size.value && fontSizeStyles[size.value]]
|
|
181
181
|
}, rest), {
|
|
@@ -194,7 +194,7 @@ var Timeline = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
194
194
|
item.icon && timelineStyles.markerIcon,
|
|
195
195
|
css.select(() => timelineToneStyles[item.tone ?? "neutral"])
|
|
196
196
|
] }, propsMarker.value, { "aria-hidden": true }), [item.icon && createVNode(Icon, { "icon": item.icon }, null)]);
|
|
197
|
-
return createVNode("li",
|
|
197
|
+
return createVNode("li", mergeJsxProps({ "key": item.key ?? index }, mergeAttrs({ sx: [timelineStyles.item, alternate.value && timelineStyles.itemAlternate] }, own), {
|
|
198
198
|
"data-state": item.pending ? "pending" : void 0,
|
|
199
199
|
"aria-busy": item.pending || void 0
|
|
200
200
|
}), [
|
|
@@ -208,10 +208,10 @@ var Timeline = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
208
208
|
"sx": timelineStyles.avatar
|
|
209
209
|
}, null), item.actor]), inlineTime]) : inlineTime,
|
|
210
210
|
hasContent(item.description) && createVNode("div", mergeAttrs({ sx: timelineStyles.description }, propsDescription.value), [item.description]),
|
|
211
|
-
hasContent(item.details) && createVNode(Collapsible,
|
|
211
|
+
hasContent(item.details) && createVNode(Collapsible, mergeJsxProps(mergePartProps({
|
|
212
212
|
lazy: true,
|
|
213
213
|
propsContent: { sx: timelineStyles.detailsContent }
|
|
214
|
-
}, propsDetails.value), { "trigger": _cache[1] || (_cache[1] = (props) => createVNode(Button,
|
|
214
|
+
}, propsDetails.value), { "trigger": _cache[1] || (_cache[1] = (props) => createVNode(Button, mergeJsxProps({
|
|
215
215
|
"variant": "link",
|
|
216
216
|
"size": "sm",
|
|
217
217
|
"iconEnd": props["aria-expanded"] ? ChevronUp : ChevronDown,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Timeline.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_createVNode","_mergeProps"],"sources":["../../src/display/Timeline.tsx"],"sourcesContent":["/**\n * 时间线(ol):按时间排列的事件(订单进度、操作记录、版本历史)。每项一个节点(圆点或图标,带色调)+ 标题、时间、说明,\n * 节点之间连一条线;可以左右交替排布;还在进行中时最后一项显示加载指示(pending)。参考 Ant Design Timeline。\n *\n * <Timeline\n * items={[\n * { title: '已下单', time: '10-18 09:12' },\n * { title: '已发货', time: '10-18 15:40', tone: 'success', icon: Truck },\n * ]}\n * pending=\"正在派送\"\n * />\n *\n * items 按时间先后给;reverse 时新的在前(操作记录、流转记录常这样看),进行中的一项也在最前。\n * 流程的流转记录每条事件一项:actor 是处理人,处理结果按种类换 icon 与 tone。\n *\n * 操作记录:项写 actor(操作人,可以带 avatar 头像)显示在时间前面;description 里可以放 FieldCompare 列出改了哪些字段;\n * 长的内容(完整的字段对比、附件、原文)放 details,项里一个“查看详情”的按钮点开才显示。\n * 记录多时写 maxVisible:只显示最新的几项,其余收起在“展开其余 N 条”后面(按钮在旧的那一端,展开后变成“收起”)。\n *\n * 语义是有序列表;时间用 <time>(给了 dateTime 时写上机器可读的时间)。\n * 部件:Root(ol)、Item(li)、Rail(节点与连接线所在的列)、Marker、Line(连接线)、Content、Title、Meta(操作人与时间)、\n * Time、Description、Details(Collapsible)、Toggle(展开其余各项的按钮)。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { SxStyles } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { ChevronDown, ChevronUp } from '@lucide/icons';\nimport type { VNodeChild } from 'vue';\nimport { Button, type ButtonProps } from '../base/Button';\nimport { Icon, type IconData } from '../base/Icon';\nimport { Spinner } from '../base/Spinner';\nimport { useLang } from '../config/context';\nimport { Avatar, initials } from './Avatar';\nimport { Collapsible, type CollapsibleProps } from './Collapsible';\nimport { hasContent, mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { fontSizeStyles, type TextSize } from '../utils/style/typography';\nimport type { StatusTone } from '../utils/style/tone-styles';\nimport { FlexOrderedList, type FlexProps } from '../layout/Flex';\n\n/** 时间线上的一项 */\nexport interface TimelineItem {\n /** 唯一的 key;不写时用位置 */\n key?: string;\n /** 标题 */\n title: VNodeChild;\n /** 时间(显示的文字) */\n time?: VNodeChild;\n /** 机器可读的时间(ISO 8601),写到 <time datetime> 上 */\n dateTime?: string;\n /** 说明:一段文字,或字段差异(FieldCompare layout=\"list\")这类内容 */\n description?: VNodeChild;\n /** 操作人:显示在时间前面 */\n actor?: VNodeChild;\n /** 操作人的头像地址;actor 是文字时没有图片也显示首字母 */\n avatar?: string;\n /** 收起的详情:项里一个“查看详情”的按钮,点开才显示 */\n details?: VNodeChild;\n /** 节点的图标;不写时是圆点 */\n icon?: IconData;\n /** 节点的色调,默认 neutral */\n tone?: StatusTone;\n}\n\n/** 未声明的属性落到 ol 上 */\nexport interface TimelineProps extends PartProps<'ol'> {\n /** 各项,按时间先后(旧的在前) */\n items: readonly TimelineItem[];\n /** 还在进行中:最后多一项,节点是加载指示,内容是它 */\n pending?: VNodeChild;\n /** 新的在前:倒过来显示(进行中的一项也在最前),ol 带 reversed */\n reverse?: boolean;\n /** 左右交替排布(时间在对侧);窄的容器里仍是单侧 */\n alternate?: boolean;\n /** 字号档位;不写时继承 */\n size?: TextSize;\n /** 最多显示几项(不算进行中的一项):只显示最新的这几项,其余收起在“展开其余 N 条”后面 */\n maxVisible?: number;\n /** 收起的各项展开了没有(可绑定) */\n _expanded?: boolean;\n /** 每一项(li)的部件属性,可以按项计算 */\n propsItem?: PartProps<'li'> | ((item: TimelineItem, index: number) => PartProps<'li'>);\n /** 节点与连接线所在的列的部件属性 */\n propsRail?: PartProps<'div'>;\n /** 节点的部件属性 */\n propsMarker?: PartProps<'span'>;\n /** 连接线的部件属性 */\n propsLine?: PartProps<'span'>;\n /** 内容(标题、时间、说明)的部件属性 */\n propsContent?: PartProps<'div'>;\n /** 标题的部件属性 */\n propsTitle?: PartProps<'div'>;\n /** 操作人与时间那一行的部件属性 */\n propsMeta?: PartProps<'div'>;\n /** 时间的部件属性 */\n propsTime?: PartProps<'time', 'datetime'>;\n /** 说明的部件属性 */\n propsDescription?: PartProps<'div'>;\n /** 详情(Collapsible)的部件属性 */\n propsDetails?: Partial<CollapsibleProps>;\n /** 展开其余各项的按钮的部件属性 */\n propsToggle?: Partial<ButtonProps>;\n}\n\n/** Timeline 的样式:root、item、marker、line、content、title、time、description 等 */\nexport const timelineStyles = css.create({\n // 竖排、去掉编号由 FlexOrderedList 给;交替排布按容器宽度切换\n root: { containerType: 'inline-size' },\n // 节点一列、内容一列;交替时三列:左内容、节点、右内容。\n // 每项是独立的 grid,节点列至少有图标节点那么宽,圆点、图标、加载指示混排时各项才对得齐\n item: { display: 'grid', gridTemplateColumns: 'minmax(1.5em, auto) 1fr', columnGap: '_md' },\n itemAlternate: {\n gridTemplateColumns: {\n default: '1fr minmax(1.5em, auto) 1fr',\n '@container (max-width: 32em)': 'minmax(1.5em, auto) 1fr',\n },\n },\n rail: { display: 'flex', flexDirection: 'column', alignItems: 'center' },\n marker: {\n boxSizing: 'border-box',\n flexShrink: 0,\n width: '0.75em',\n height: '0.75em',\n marginBlock: '0.375em',\n borderRadius: '_full',\n backgroundColor: 'currentColor',\n },\n // 有图标时节点大一些,图标描边用色调\n markerIcon: {\n display: 'grid',\n placeItems: 'center',\n width: '1.5em',\n height: '1.5em',\n marginBlock: 0,\n backgroundColor: '_surface',\n borderWidth: '_sm',\n borderStyle: 'solid',\n borderColor: 'currentColor',\n },\n pending: { marginBlock: '0.25em' },\n // 连到下一项的线;最后一项没有\n line: {\n flexGrow: 1,\n width: '_borderWidths.sm',\n minHeight: '1em',\n backgroundColor: '_borderMuted',\n },\n content: { minWidth: 0, paddingBlockEnd: '_lg', display: 'grid', gap: '_xs' },\n // 交替排布时偶数项的内容在节点左侧、靠右对齐\n contentEnd: {\n gridColumn: { default: '1', '@container (max-width: 32em)': '2' },\n gridRow: '1',\n textAlign: { default: 'end', '@container (max-width: 32em)': 'start' },\n },\n title: { fontWeight: '_semibold', color: '_text' },\n // 操作人与时间:一行,放不下时换行\n meta: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n columnGap: '_sm',\n rowGap: '_xs',\n fontSize: '_sm',\n color: '_textMuted',\n },\n actor: { display: 'inline-flex', alignItems: 'center', gap: '_xs', color: '_text' },\n avatar: { width: '1.75em', height: '1.75em' },\n time: { fontSize: '_sm', color: '_textMuted' },\n // 交替排布时时间放在对侧(它自己占一格);窄的容器里不占格,改在内容里显示\n timeOpposite: {\n paddingBlockStart: '0.125em',\n display: { default: 'block', '@container (max-width: 32em)': 'none' },\n },\n timeInline: { display: { default: 'none', '@container (max-width: 32em)': 'block' } },\n description: { color: '_text' },\n // 详情按钮像链接,靠起始边;展开的内容与上面隔开一点\n detailsToggle: { justifySelf: 'start' },\n detailsContent: { paddingBlockStart: '_xs' },\n // 收起的那几项的位置:节点列是一段虚线,内容是按钮\n toggleLine: {\n flexGrow: 1,\n minHeight: '2em',\n borderInlineStartWidth: '_sm',\n borderInlineStartStyle: 'dashed',\n borderInlineStartColor: '_borderMuted',\n },\n toggleContent: { paddingBlockEnd: '_lg' },\n});\n\n/** Timeline 的样式:节点的色调 */\nexport const timelineToneStyles = css.create({\n neutral: { color: '_borderStrong' },\n accent: { color: '_accent' },\n info: { color: '_infoText' },\n success: { color: '_successText' },\n warning: { color: '_warningText' },\n danger: { color: '_dangerText' },\n});\n\n/** 时间线:节点 + 内容,节点之间连线 */\nexport const Timeline = _component(\n ({\n items,\n pending,\n alternate = false,\n size,\n reverse = false,\n maxVisible,\n _expanded: expanded = _bindable(false),\n propsItem,\n propsRail,\n propsMarker,\n propsLine,\n propsContent,\n propsTitle,\n propsMeta,\n propsTime,\n propsDescription,\n propsDetails,\n propsToggle,\n ...rest\n }: TimelineProps) => {\n const lang = useLang();\n\n /** 收起的那几项所在的位置:节点列一段虚线,内容是展开、收起的按钮 */\n const toggle = (hidden: number) => (\n <li key=\"toggle\" {...css.attrs(timelineStyles.item)}>\n <div {...mergeAttrs<'div'>({ sx: timelineStyles.rail }, propsRail)}>\n <span {...css.attrs(timelineStyles.toggleLine)} aria-hidden=\"true\" />\n </div>\n <div {...css.attrs(timelineStyles.toggleContent)}>\n <Button\n {...mergePartProps<ButtonProps>(\n {\n variant: 'link',\n size: 'sm',\n iconEnd: expanded ? ChevronUp : ChevronDown,\n },\n propsToggle,\n { 'aria-expanded': expanded, onClick: () => (expanded = !expanded) },\n )}\n >\n {expanded ? lang.timelineShowLess : lang.timelineShowMore(hidden)}\n </Button>\n </div>\n </li>\n );\n\n return () => {\n const hidden =\n maxVisible !== undefined && items.length > maxVisible ? items.length - maxVisible : 0;\n const chronological: (TimelineItem & { pending?: true })[] = [\n ...(hidden > 0 && !expanded ? items.slice(hidden) : items),\n ...(hasContent(pending)\n ? [{ key: 'pending', title: pending, pending: true as const }]\n : []),\n ];\n const all = reverse ? chronological.toReversed() : chronological;\n // 按钮在旧的那一端:倒序时在最后,最后一项仍连线到它\n const toggleAtEnd = hidden > 0 && reverse;\n return (\n <FlexOrderedList\n {...mergePartProps<FlexProps<'ol'>>(\n { direction: 'column', sx: [timelineStyles.root, size && fontSizeStyles[size]] },\n rest,\n )}\n data-alternate={alternate || undefined}\n reversed={reverse || undefined}\n >\n {hidden > 0 && !reverse && toggle(hidden)}\n {all.map((item, index) => {\n const own = typeof propsItem === 'function' ? propsItem(item, index) : propsItem;\n const last = index === all.length - 1 && !toggleAtEnd;\n const end = alternate && index % 2 === 1;\n // 交替排布时时间放在对侧,占一格(没有时间也占,保持三列对齐);窄的容器里改在内容里显示\n const time = (sx: SxStyles) =>\n hasContent(item.time) ? (\n <time\n {...mergeAttrs<'time'>({ sx: [timelineStyles.time, sx] }, propsTime, {\n datetime: item.dateTime,\n })}\n >\n {item.time}\n </time>\n ) : (\n alternate && <span {...css.attrs(sx)} />\n );\n // 内容里的时间:交替排布时只在窄的容器里显示(宽时在对侧)\n const inlineTime = alternate\n ? hasContent(item.time) && time(timelineStyles.timeInline)\n : time(null);\n const marker = item.pending ? (\n <Spinner sx={[timelineStyles.pending, timelineToneStyles.accent]} />\n ) : (\n <span\n {...mergeAttrs<'span'>(\n {\n sx: [\n timelineStyles.marker,\n item.icon && timelineStyles.markerIcon,\n timelineToneStyles[item.tone ?? 'neutral'],\n ],\n },\n propsMarker,\n { 'aria-hidden': true },\n )}\n >\n {item.icon && <Icon icon={item.icon} />}\n </span>\n );\n return (\n <li\n key={item.key ?? index}\n {...mergeAttrs<'li'>(\n { sx: [timelineStyles.item, alternate && timelineStyles.itemAlternate] },\n own,\n )}\n data-state={item.pending ? 'pending' : undefined}\n aria-busy={item.pending || undefined}\n >\n {/* 交替排布:奇数项左侧放时间,偶数项右侧放时间 */}\n {alternate && !end && time(timelineStyles.timeOpposite)}\n <div {...mergeAttrs<'div'>({ sx: timelineStyles.rail }, propsRail)}>\n {marker}\n {!last && (\n <span\n {...mergeAttrs<'span'>({ sx: timelineStyles.line }, propsLine, {\n 'aria-hidden': true,\n })}\n />\n )}\n </div>\n <div\n {...mergeAttrs<'div'>(\n { sx: [timelineStyles.content, end && timelineStyles.contentEnd] },\n propsContent,\n )}\n >\n <div {...mergeAttrs<'div'>({ sx: timelineStyles.title }, propsTitle)}>\n {item.title}\n </div>\n {hasContent(item.actor) ? (\n <div {...mergeAttrs<'div'>({ sx: timelineStyles.meta }, propsMeta)}>\n <span {...css.attrs(timelineStyles.actor)}>\n {(item.avatar || typeof item.actor === 'string') && (\n <Avatar\n src={item.avatar}\n fallback={\n typeof item.actor === 'string' ? initials(item.actor) : undefined\n }\n sx={timelineStyles.avatar}\n />\n )}\n {item.actor}\n </span>\n {inlineTime}\n </div>\n ) : (\n inlineTime\n )}\n {hasContent(item.description) && (\n <div\n {...mergeAttrs<'div'>({ sx: timelineStyles.description }, propsDescription)}\n >\n {item.description}\n </div>\n )}\n {hasContent(item.details) && (\n <Collapsible\n {...mergePartProps<Partial<CollapsibleProps>>(\n { lazy: true, propsContent: { sx: timelineStyles.detailsContent } },\n propsDetails,\n )}\n trigger={(props) => (\n <Button\n variant=\"link\"\n size=\"sm\"\n iconEnd={props['aria-expanded'] ? ChevronUp : ChevronDown}\n sx={timelineStyles.detailsToggle}\n {...props}\n >\n {lang.timelineDetails}\n </Button>\n )}\n >\n {item.details}\n </Collapsible>\n )}\n </div>\n {end && time(timelineStyles.timeOpposite)}\n </li>\n );\n })}\n {toggleAtEnd && toggle(hidden)}\n </FlexOrderedList>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Timeline.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_createVNode","_mergeProps"],"sources":["../../src/display/Timeline.tsx"],"sourcesContent":["/**\n * 时间线(ol):按时间排列的事件(订单进度、操作记录、版本历史)。每项一个节点(圆点或图标,带色调)+ 标题、时间、说明,\n * 节点之间连一条线;可以左右交替排布;还在进行中时最后一项显示加载指示(pending)。参考 Ant Design Timeline。\n *\n * <Timeline\n * items={[\n * { title: '已下单', time: '10-18 09:12' },\n * { title: '已发货', time: '10-18 15:40', tone: 'success', icon: Truck },\n * ]}\n * pending=\"正在派送\"\n * />\n *\n * items 按时间先后给;reverse 时新的在前(操作记录、流转记录常这样看),进行中的一项也在最前。\n * 流程的流转记录每条事件一项:actor 是处理人,处理结果按种类换 icon 与 tone。\n *\n * 操作记录:项写 actor(操作人,可以带 avatar 头像)显示在时间前面;description 里可以放 FieldCompare 列出改了哪些字段;\n * 长的内容(完整的字段对比、附件、原文)放 details,项里一个“查看详情”的按钮点开才显示。\n * 记录多时写 maxVisible:只显示最新的几项,其余收起在“展开其余 N 条”后面(按钮在旧的那一端,展开后变成“收起”)。\n *\n * 语义是有序列表;时间用 <time>(给了 dateTime 时写上机器可读的时间)。\n * 部件:Root(ol)、Item(li)、Rail(节点与连接线所在的列)、Marker、Line(连接线)、Content、Title、Meta(操作人与时间)、\n * Time、Description、Details(Collapsible)、Toggle(展开其余各项的按钮)。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { SxStyles } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { ChevronDown, ChevronUp } from '@lucide/icons';\nimport type { VNodeChild } from 'vue';\nimport { Button, type ButtonProps } from '../base/Button';\nimport { Icon, type IconData } from '../base/Icon';\nimport { Spinner } from '../base/Spinner';\nimport { useLang } from '../config/context';\nimport { Avatar, initials } from './Avatar';\nimport { Collapsible, type CollapsibleProps } from './Collapsible';\nimport { hasContent, mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { fontSizeStyles, type TextSize } from '../utils/style/typography';\nimport type { StatusTone } from '../utils/style/tone-styles';\nimport { FlexOrderedList, type FlexProps } from '../layout/Flex';\n\n/** 时间线上的一项 */\nexport interface TimelineItem {\n /** 唯一的 key;不写时用位置 */\n key?: string;\n /** 标题 */\n title: VNodeChild;\n /** 时间(显示的文字) */\n time?: VNodeChild;\n /** 机器可读的时间(ISO 8601),写到 <time datetime> 上 */\n dateTime?: string;\n /** 说明:一段文字,或字段差异(FieldCompare layout=\"list\")这类内容 */\n description?: VNodeChild;\n /** 操作人:显示在时间前面 */\n actor?: VNodeChild;\n /** 操作人的头像地址;actor 是文字时没有图片也显示首字母 */\n avatar?: string;\n /** 收起的详情:项里一个“查看详情”的按钮,点开才显示 */\n details?: VNodeChild;\n /** 节点的图标;不写时是圆点 */\n icon?: IconData;\n /** 节点的色调,默认 neutral */\n tone?: StatusTone;\n}\n\n/** 未声明的属性落到 ol 上 */\nexport interface TimelineProps extends PartProps<'ol'> {\n /** 各项,按时间先后(旧的在前) */\n items: readonly TimelineItem[];\n /** 还在进行中:最后多一项,节点是加载指示,内容是它 */\n pending?: VNodeChild;\n /** 新的在前:倒过来显示(进行中的一项也在最前),ol 带 reversed */\n reverse?: boolean;\n /** 左右交替排布(时间在对侧);窄的容器里仍是单侧 */\n alternate?: boolean;\n /** 字号档位;不写时继承 */\n size?: TextSize;\n /** 最多显示几项(不算进行中的一项):只显示最新的这几项,其余收起在“展开其余 N 条”后面 */\n maxVisible?: number;\n /** 收起的各项展开了没有(可绑定) */\n _expanded?: boolean;\n /** 每一项(li)的部件属性,可以按项计算 */\n propsItem?: PartProps<'li'> | ((item: TimelineItem, index: number) => PartProps<'li'>);\n /** 节点与连接线所在的列的部件属性 */\n propsRail?: PartProps<'div'>;\n /** 节点的部件属性 */\n propsMarker?: PartProps<'span'>;\n /** 连接线的部件属性 */\n propsLine?: PartProps<'span'>;\n /** 内容(标题、时间、说明)的部件属性 */\n propsContent?: PartProps<'div'>;\n /** 标题的部件属性 */\n propsTitle?: PartProps<'div'>;\n /** 操作人与时间那一行的部件属性 */\n propsMeta?: PartProps<'div'>;\n /** 时间的部件属性 */\n propsTime?: PartProps<'time', 'datetime'>;\n /** 说明的部件属性 */\n propsDescription?: PartProps<'div'>;\n /** 详情(Collapsible)的部件属性 */\n propsDetails?: Partial<CollapsibleProps>;\n /** 展开其余各项的按钮的部件属性 */\n propsToggle?: Partial<ButtonProps>;\n}\n\n/** Timeline 的样式:root、item、marker、line、content、title、time、description 等 */\nexport const timelineStyles = css.create({\n // 竖排、去掉编号由 FlexOrderedList 给;交替排布按容器宽度切换\n root: { containerType: 'inline-size' },\n // 节点一列、内容一列;交替时三列:左内容、节点、右内容。\n // 每项是独立的 grid,节点列至少有图标节点那么宽,圆点、图标、加载指示混排时各项才对得齐\n item: { display: 'grid', gridTemplateColumns: 'minmax(1.5em, auto) 1fr', columnGap: '_md' },\n itemAlternate: {\n gridTemplateColumns: {\n default: '1fr minmax(1.5em, auto) 1fr',\n '@container (max-width: 32em)': 'minmax(1.5em, auto) 1fr',\n },\n },\n rail: { display: 'flex', flexDirection: 'column', alignItems: 'center' },\n marker: {\n boxSizing: 'border-box',\n flexShrink: 0,\n width: '0.75em',\n height: '0.75em',\n marginBlock: '0.375em',\n borderRadius: '_full',\n backgroundColor: 'currentColor',\n },\n // 有图标时节点大一些,图标描边用色调\n markerIcon: {\n display: 'grid',\n placeItems: 'center',\n width: '1.5em',\n height: '1.5em',\n marginBlock: 0,\n backgroundColor: '_surface',\n borderWidth: '_sm',\n borderStyle: 'solid',\n borderColor: 'currentColor',\n },\n pending: { marginBlock: '0.25em' },\n // 连到下一项的线;最后一项没有\n line: {\n flexGrow: 1,\n width: '_borderWidths.sm',\n minHeight: '1em',\n backgroundColor: '_borderMuted',\n },\n content: { minWidth: 0, paddingBlockEnd: '_lg', display: 'grid', gap: '_xs' },\n // 交替排布时偶数项的内容在节点左侧、靠右对齐\n contentEnd: {\n gridColumn: { default: '1', '@container (max-width: 32em)': '2' },\n gridRow: '1',\n textAlign: { default: 'end', '@container (max-width: 32em)': 'start' },\n },\n title: { fontWeight: '_semibold', color: '_text' },\n // 操作人与时间:一行,放不下时换行\n meta: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n columnGap: '_sm',\n rowGap: '_xs',\n fontSize: '_sm',\n color: '_textMuted',\n },\n actor: { display: 'inline-flex', alignItems: 'center', gap: '_xs', color: '_text' },\n avatar: { width: '1.75em', height: '1.75em' },\n time: { fontSize: '_sm', color: '_textMuted' },\n // 交替排布时时间放在对侧(它自己占一格);窄的容器里不占格,改在内容里显示\n timeOpposite: {\n paddingBlockStart: '0.125em',\n display: { default: 'block', '@container (max-width: 32em)': 'none' },\n },\n timeInline: { display: { default: 'none', '@container (max-width: 32em)': 'block' } },\n description: { color: '_text' },\n // 详情按钮像链接,靠起始边;展开的内容与上面隔开一点\n detailsToggle: { justifySelf: 'start' },\n detailsContent: { paddingBlockStart: '_xs' },\n // 收起的那几项的位置:节点列是一段虚线,内容是按钮\n toggleLine: {\n flexGrow: 1,\n minHeight: '2em',\n borderInlineStartWidth: '_sm',\n borderInlineStartStyle: 'dashed',\n borderInlineStartColor: '_borderMuted',\n },\n toggleContent: { paddingBlockEnd: '_lg' },\n});\n\n/** Timeline 的样式:节点的色调 */\nexport const timelineToneStyles = css.create({\n neutral: { color: '_borderStrong' },\n accent: { color: '_accent' },\n info: { color: '_infoText' },\n success: { color: '_successText' },\n warning: { color: '_warningText' },\n danger: { color: '_dangerText' },\n});\n\n/** 时间线:节点 + 内容,节点之间连线 */\nexport const Timeline = _component(\n ({\n items,\n pending,\n alternate = false,\n size,\n reverse = false,\n maxVisible,\n _expanded: expanded = _bindable(false),\n propsItem,\n propsRail,\n propsMarker,\n propsLine,\n propsContent,\n propsTitle,\n propsMeta,\n propsTime,\n propsDescription,\n propsDetails,\n propsToggle,\n ...rest\n }: TimelineProps) => {\n const lang = useLang();\n\n /** 收起的那几项所在的位置:节点列一段虚线,内容是展开、收起的按钮 */\n const toggle = (hidden: number) => (\n <li key=\"toggle\" {...css.attrs(timelineStyles.item)}>\n <div {...mergeAttrs<'div'>({ sx: timelineStyles.rail }, propsRail)}>\n <span {...css.attrs(timelineStyles.toggleLine)} aria-hidden=\"true\" />\n </div>\n <div {...css.attrs(timelineStyles.toggleContent)}>\n <Button\n {...mergePartProps<ButtonProps>(\n {\n variant: 'link',\n size: 'sm',\n iconEnd: expanded ? ChevronUp : ChevronDown,\n },\n propsToggle,\n { 'aria-expanded': expanded, onClick: () => (expanded = !expanded) },\n )}\n >\n {expanded ? lang.timelineShowLess : lang.timelineShowMore(hidden)}\n </Button>\n </div>\n </li>\n );\n\n return () => {\n const hidden =\n maxVisible !== undefined && items.length > maxVisible ? items.length - maxVisible : 0;\n const chronological: (TimelineItem & { pending?: true })[] = [\n ...(hidden > 0 && !expanded ? items.slice(hidden) : items),\n ...(hasContent(pending)\n ? [{ key: 'pending', title: pending, pending: true as const }]\n : []),\n ];\n const all = reverse ? chronological.toReversed() : chronological;\n // 按钮在旧的那一端:倒序时在最后,最后一项仍连线到它\n const toggleAtEnd = hidden > 0 && reverse;\n return (\n <FlexOrderedList\n {...mergePartProps<FlexProps<'ol'>>(\n { direction: 'column', sx: [timelineStyles.root, size && fontSizeStyles[size]] },\n rest,\n )}\n data-alternate={alternate || undefined}\n reversed={reverse || undefined}\n >\n {hidden > 0 && !reverse && toggle(hidden)}\n {all.map((item, index) => {\n const own = typeof propsItem === 'function' ? propsItem(item, index) : propsItem;\n const last = index === all.length - 1 && !toggleAtEnd;\n const end = alternate && index % 2 === 1;\n // 交替排布时时间放在对侧,占一格(没有时间也占,保持三列对齐);窄的容器里改在内容里显示\n const time = (sx: SxStyles) =>\n hasContent(item.time) ? (\n <time\n {...mergeAttrs<'time'>({ sx: [timelineStyles.time, sx] }, propsTime, {\n datetime: item.dateTime,\n })}\n >\n {item.time}\n </time>\n ) : (\n alternate && <span {...css.attrs(sx)} />\n );\n // 内容里的时间:交替排布时只在窄的容器里显示(宽时在对侧)\n const inlineTime = alternate\n ? hasContent(item.time) && time(timelineStyles.timeInline)\n : time(null);\n const marker = item.pending ? (\n <Spinner sx={[timelineStyles.pending, timelineToneStyles.accent]} />\n ) : (\n <span\n {...mergeAttrs<'span'>(\n {\n sx: [\n timelineStyles.marker,\n item.icon && timelineStyles.markerIcon,\n timelineToneStyles[item.tone ?? 'neutral'],\n ],\n },\n propsMarker,\n { 'aria-hidden': true },\n )}\n >\n {item.icon && <Icon icon={item.icon} />}\n </span>\n );\n return (\n <li\n key={item.key ?? index}\n {...mergeAttrs<'li'>(\n { sx: [timelineStyles.item, alternate && timelineStyles.itemAlternate] },\n own,\n )}\n data-state={item.pending ? 'pending' : undefined}\n aria-busy={item.pending || undefined}\n >\n {/* 交替排布:奇数项左侧放时间,偶数项右侧放时间 */}\n {alternate && !end && time(timelineStyles.timeOpposite)}\n <div {...mergeAttrs<'div'>({ sx: timelineStyles.rail }, propsRail)}>\n {marker}\n {!last && (\n <span\n {...mergeAttrs<'span'>({ sx: timelineStyles.line }, propsLine, {\n 'aria-hidden': true,\n })}\n />\n )}\n </div>\n <div\n {...mergeAttrs<'div'>(\n { sx: [timelineStyles.content, end && timelineStyles.contentEnd] },\n propsContent,\n )}\n >\n <div {...mergeAttrs<'div'>({ sx: timelineStyles.title }, propsTitle)}>\n {item.title}\n </div>\n {hasContent(item.actor) ? (\n <div {...mergeAttrs<'div'>({ sx: timelineStyles.meta }, propsMeta)}>\n <span {...css.attrs(timelineStyles.actor)}>\n {(item.avatar || typeof item.actor === 'string') && (\n <Avatar\n src={item.avatar}\n fallback={\n typeof item.actor === 'string' ? initials(item.actor) : undefined\n }\n sx={timelineStyles.avatar}\n />\n )}\n {item.actor}\n </span>\n {inlineTime}\n </div>\n ) : (\n inlineTime\n )}\n {hasContent(item.description) && (\n <div\n {...mergeAttrs<'div'>({ sx: timelineStyles.description }, propsDescription)}\n >\n {item.description}\n </div>\n )}\n {hasContent(item.details) && (\n <Collapsible\n {...mergePartProps<Partial<CollapsibleProps>>(\n { lazy: true, propsContent: { sx: timelineStyles.detailsContent } },\n propsDetails,\n )}\n trigger={(props) => (\n <Button\n variant=\"link\"\n size=\"sm\"\n iconEnd={props['aria-expanded'] ? ChevronUp : ChevronDown}\n sx={timelineStyles.detailsToggle}\n {...props}\n >\n {lang.timelineDetails}\n </Button>\n )}\n >\n {item.details}\n </Collapsible>\n )}\n </div>\n {end && time(timelineStyles.timeOpposite)}\n </li>\n );\n })}\n {toggleAtEnd && toggle(hidden)}\n </FlexOrderedList>\n );\n };\n },\n);\n"],"mappings":"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"}
|