@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
package/dist/base/Tag.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tag.js","names":["_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_createVNode","_mergeProps"
|
|
1
|
+
{"version":3,"file":"Tag.js","names":["_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_createVNode","_mergeProps"],"sources":["../../src/base/Tag.tsx"],"sourcesContent":["/**\n * 标签(span):外观同 Badge,可以带移除按钮(筛选条件、已选项、输入的关键词)。\n *\n * <Tag onRemove={() => remove(item)}>{item.name}</Tag>\n *\n * 移除时标签先淡出缩小(usePresence),离场结束才调用 onRemove。其余标签平移补位交给所在容器:\n * const layout = useLayoutAnimation(() => list);\n * <Tag onRemove={() => layout.change(() => remove(item))}>…</Tag>\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 { useLang } from '../config/context';\nimport { useDefaults } from '../config/defaults';\nimport { useDismissible } from '../utils/motion/presence';\nimport { mergeAttrs, mergePartProps, mergeProps, type PartProps } from '../utils/props';\nimport { plainText } from '../utils/data/text';\nimport { badgeParts, badgeRecipe, type BadgeOptions } from './badge-styles';\nimport { useRecipe } from '../config/styles';\nimport { CloseButton, type PresetIconButtonProps } from './CloseButton';\nimport { Icon, type IconData, type IconProps } from './Icon';\nimport { t } from '../config/theme/tokens';\n\n/** 未声明的属性落到 span 上 */\nexport interface TagProps extends PartProps<'span'>, BadgeOptions {\n /** 文字前的图标 */\n icon?: IconData;\n /** 给了就显示移除按钮:点击后标签先淡出缩小,离场结束再调用;标签随后被移除时,焦点交给同组的下一个(没有就上一个)可聚焦元素;没被移除就重新显示 */\n onRemove?: () => void;\n /** 移除按钮的无障碍名称,默认“移除”加上标签文字(内容只有文字时) */\n removeLabel?: string;\n /** 图标的部件属性 */\n propsIcon?: Partial<IconProps>;\n /** 移除按钮的部件属性 */\n propsRemove?: Partial<PresetIconButtonProps>;\n children?: VNodeChild;\n}\n\n/** Tag 的样式:root、removable、remove、removing */\nexport const tagStyles = css.create({\n // 移除被拒绝(使用方没移除)时从离场状态过渡回来\n root: {\n transition: `opacity ${t.durations.sm} ${t.easings.enter}, scale ${t.durations.sm} ${t.easings.enter}`,\n },\n // 移除按钮紧贴右侧,比独立的图标按钮紧凑(标签之间留有间距,满足目标尺寸的间距例外)\n removable: { paddingInlineEnd: '_xs' },\n remove: {\n width: '1.5em',\n minHeight: '1.5em',\n marginBlock: `calc(-1 * ${t.space.xs})`,\n color: 'inherit',\n },\n removing: {\n opacity: 0,\n scale: 0.8,\n transition: `opacity ${t.durations.sm} ${t.easings.exit}, scale ${t.durations.sm} ${t.easings.exit}`,\n },\n});\n\n/** 标签(span):外观同 Badge,可以带移除按钮 */\nexport const Tag = _component(\n ({\n variant,\n tone,\n size,\n radius,\n icon,\n onRemove,\n removeLabel,\n propsIcon,\n propsRemove,\n children,\n ...rest\n }: TagProps) => {\n const lang = useLang();\n const appearance = useDefaults('Badge', () => ({ variant, tone, size, radius }));\n const recipe = useRecipe(badgeRecipe);\n // 离场结束才交给使用方移除;使用方没移除(如拒绝删除)时标签重新出现\n const dismissible = useDismissible(() => onRemove?.());\n return () => {\n const text = plainText(children);\n const label = removeLabel ?? (text ? `${lang.remove} ${text}` : lang.remove);\n return (\n <span\n {...mergeAttrs<'span'>(\n {\n ...badgeParts(appearance(), recipe).root,\n sx: [\n badgeParts(appearance(), recipe).root.sx,\n tagStyles.root,\n onRemove && tagStyles.removable,\n dismissible.dismissing && tagStyles.removing,\n ],\n },\n rest,\n { ref: dismissible.ref },\n )}\n data-state={dismissible.state}\n >\n {icon && <Icon {...mergePartProps<IconProps>({ icon }, propsIcon)} />}\n {children}\n {onRemove && (\n <CloseButton\n {...mergeProps<PresetIconButtonProps>(\n { sx: tagStyles.remove, 'aria-label': label, radius: 'full' },\n propsRemove,\n { onClick: dismissible.dismiss },\n )}\n />\n )}\n </span>\n );\n };\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;AAuCA,IAAa,YAAY,IAAI,OAAO;CAElC,MAAM,EACJ,YAAY,WAAW,EAAE,UAAU,GAAE,GAAI,EAAE,QAAQ,MAAK,UAAW,EAAE,UAAU,GAAE,GAAI,EAAE,QAAQ,QACjG;CAEA,WAAW,EAAE,kBAAkB,MAAM;CACrC,QAAQ;EACN,OAAO;EACP,WAAW;EACX,aAAa,aAAa,EAAE,MAAM,GAAE;EACpC,OAAO;CACT;CACA,UAAU;EACR,SAAS;EACT,OAAO;EACP,YAAY,WAAW,EAAE,UAAU,GAAE,GAAI,EAAE,QAAQ,KAAI,UAAW,EAAE,UAAU,GAAE,GAAI,EAAE,QAAQ;CAChG;AACF,CAAC;;AAGD,IAAa,MAAM,0BACjB,QAAA,SAYgB;CAAA,MAAA,UAAAA,aAAA,QAAA,SAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,SAAAA,aAAA,QAAA,QAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,WAAAA,aAAA,QAAA,UAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,WAAAC,aAAA,MAAA;CAAA,MADX,OAAIC,UAAA,QAAA,MAAA,KAAA;CAEP,MAAM,OAAO,QAAQ;CACrB,MAAM,aAAa,YAAY,gBAAgB;EAAE,SAAO,QAAA;EAAE,MAAI,KAAA;EAAE,MAAI,KAAA;EAAE,QAAM,OAAA;CAAC,EAAE;CAC/E,MAAM,SAAS,UAAU,WAAW;CAEpC,MAAM,cAAc,qBAAqB,SAAA,QAAW,CAAC;CACrD,aAAa;EACX,MAAM,OAAO,UAAS,SAAA,KAAS;EAC/B,MAAM,QAAQ,YAAA,UAAgB,OAAO,GAAG,KAAK,OAAM,GAAI,SAAS,KAAK;EACrE,OAAAC,YAAA,QAAAC,cAEQ,WACF;GACE,GAAG,WAAW,WAAW,GAAG,MAAM,CAAC,CAAC;GACpC,IAAI;IACF,WAAW,WAAW,GAAG,MAAM,CAAC,CAAC,KAAK;IACtC,UAAU;IACV,SAAA,SAAY,UAAU;IAAS,IAAA,aAC/B,YAAY,cAAc,UAAU,UAAQ,IAAA;GAAA;EAEhD,GACA,MACA,EAAE,KAAK,YAAY,IAAI,CACzB,GAAC,EAAA,cACW,YAAY,MAAK,CAAA,GAAA;GAE5B,KAAA,SAAAD,YAAA,MAAkB,eAA0B,EAAE,MAAI,KAAA,MAAC,GAAC,UAAA,KAAW,GAAC,IAAA;GAAI,SAAA;GAEpE,SAAA,SAAAA,YAAA,aAEO,WACF;IAAE,IAAI,UAAU;IAAQ,cAAc;IAAO,QAAQ;GAAO,GAAC,YAAA,OAE7D,EAAE,SAAS,YAAY,QAAQ,CACjC,GAAC,IAAA;EAEJ,CAAA;CAGP;AACF,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
package/dist/base/button-core.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t } from "../config/theme/tokens.js";
|
|
2
2
|
import { useDefaults } from "../config/defaults.js";
|
|
3
|
-
import { mergeAttrs, mergeProps
|
|
3
|
+
import { mergeAttrs, mergeProps } from "../utils/props.js";
|
|
4
4
|
import { fontSizeStyles } from "../utils/style/typography.js";
|
|
5
5
|
import { focusRingStyles, radiusStyles } from "../utils/style/styles.js";
|
|
6
6
|
import { partAttrs, useRecipe } from "../config/styles.js";
|
|
@@ -619,10 +619,10 @@ function buttonSx({ variant, tone, size, radius, attached, recipe }, state) {
|
|
|
619
619
|
/** 按钮内容:图标、文字、加载指示;加载中内容透明但保留宽度与无障碍名称 */
|
|
620
620
|
function buttonContent({ icon, iconEnd, children, propsContent, propsIcon, propsIconEnd }, { loading, propsSpinner }) {
|
|
621
621
|
return [h("span", mergeAttrs({ sx: buttonStyles.content }, propsContent, { sx: loading && buttonStyles.hidden }), [
|
|
622
|
-
icon && h(Icon, mergeProps
|
|
622
|
+
icon && h(Icon, mergeProps({ icon }, propsIcon)),
|
|
623
623
|
children,
|
|
624
|
-
iconEnd && h(Icon, mergeProps
|
|
625
|
-
]), loading && h(Spinner, mergeProps
|
|
624
|
+
iconEnd && h(Icon, mergeProps({ icon: iconEnd }, propsIconEnd))
|
|
625
|
+
]), loading && h(Spinner, mergeProps({ sx: buttonStyles.spinner }, propsSpinner))];
|
|
626
626
|
}
|
|
627
627
|
/**
|
|
628
628
|
* 按钮家族的行为:不可用(原生 disabled 或可聚焦的 aria-disabled)、加载中、拦截点击、波纹、状态 data 属性。
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button-core.js","names":["mergeProps"],"sources":["../../src/base/button-core.ts"],"sourcesContent":["/**\n * 按钮家族(Button、LinkButton、IconButton、CloseButton、ClearButton)的内核:共用的外观、选项、内容排布与行为。\n * 一个元素一个组件(COMPONENTS.md §1):各组件只管自己元素的行为,样式与内容排布都从这里取。\n *\n * 外观 = variant(样式)× tone(色调)× size(字号档位,内部尺寸随之按 em 缩放)× radius(圆角)。\n */\nimport { h, type VNodeChild } from 'vue';\nimport { createContext } 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 { Icon, type IconData, type IconProps } from './Icon';\nimport { Spinner, type SpinnerProps } from './Spinner';\nimport { useDefaults } from '../config/defaults';\nimport { partAttrs, useRecipe } from '../config/styles';\nimport { rippleHostStyles, useRipple, type RippleHandlers } from '../utils/dom/ripple';\nimport { mergeAttrs, mergeProps, type PartProps } from '../utils/props';\nimport { focusRingStyles, radiusStyles, type Radius } from '../utils/style/styles';\nimport type { StatusTone } from '../utils/style/tone-styles';\nimport {\n AttachedContext,\n attachedStyles,\n type AttachedOrientations,\n} from '../utils/style/attached';\nimport { interactionTransitions } from '../utils/style/transitions';\nimport { fontSizeStyles, type TextSize } from '../utils/style/typography';\nimport { t } from '../config/theme/tokens';\n\n/** 按钮样式 */\nexport interface ButtonVariants {\n /** 实底:一组操作里的主要操作,每组最多一个 */\n solid: unknown;\n /** 浅色底:次要但仍需突出的操作,如卡片里的操作 */\n soft: unknown;\n /** 浅色底加描边:表单、设置页里常用的次要操作 */\n surface: unknown;\n /** 只有描边:次要操作 */\n outline: unknown;\n /** 无底无边,悬停时才有底色:工具栏、行内操作、图标按钮 */\n ghost: unknown;\n /** 看起来像链接(无底、无内边距、悬停下划线):文字中间的操作 */\n link: unknown;\n}\n\n/** 按钮家族共用的外观选项;可以用 createUi / Provider 的 defaults.Button 统一改默认值 */\nexport interface ButtonOptions {\n /**\n * 样式;可以用 createUi / Provider 的 defaults.Button 统一改\n * @default 'solid'\n */\n variant?: keyof ButtonVariants;\n /**\n * 色调:与 Badge、Alert 等同一套\n * @default 'accent'\n */\n tone?: StatusTone;\n /** 字号档位,内部尺寸随之缩放;不写时继承上下文字号 */\n size?: TextSize;\n /**\n * 圆角\n * @default 'md'\n */\n radius?: Radius;\n /** 加载中:显示旋转指示,宽度不变,点击无效,焦点保留;读屏会得知 aria-busy */\n loading?: boolean;\n /** 按下时的波纹效果;不写时沿用 createUi / Provider 的 ripple(默认开启) */\n ripple?: boolean;\n /** 加载指示(Spinner)的部件属性 */\n propsSpinner?: Partial<SpinnerProps>;\n}\n\n/** 有文字的按钮(Button、LinkButton)另有的选项 */\nexport interface ButtonContentOptions {\n /** 文字前的图标:`import { Plus } from '@lucide/icons'` */\n icon?: IconData;\n /** 文字后的图标 */\n iconEnd?: IconData;\n /** 占满容器宽度 */\n fullWidth?: boolean;\n /** 内容层(包着图标与文字的 span)的部件属性 */\n propsContent?: PartProps<'span'>;\n /** 前置图标的部件属性 */\n propsIcon?: Partial<IconProps>;\n /** 后置图标的部件属性 */\n propsIconEnd?: Partial<IconProps>;\n /** 按钮文字;加载中仍保留(读屏名称不变) */\n children?: VNodeChild;\n}\n\n/** 按钮的基础样式:去掉默认外观,继承文字样式,指针与触摸的反馈;Button 家族也用它 */\nexport const buttonBaseStyles = css.create({\n root: {\n boxSizing: 'border-box',\n margin: 0,\n padding: 0,\n borderWidth: 0,\n backgroundColor: 'transparent',\n color: 'inherit',\n fontFamily: 'inherit',\n fontSize: 'inherit',\n lineHeight: 'inherit',\n textAlign: 'inherit',\n textDecoration: 'none',\n cursor: 'pointer',\n userSelect: 'none',\n // 触摸时不等双击缩放的判定,点击立即响应\n touchAction: 'manipulation',\n WebkitTapHighlightColor: 'transparent',\n },\n disabled: { cursor: 'not-allowed' },\n});\n\n/** 按钮家族 的样式:root、square、fullWidth、disabled、content、hidden 等 */\nexport const buttonStyles = css.create({\n // 去掉默认外观、指针与触摸反馈、颜色过渡来自 buttonBaseStyles 与 interactionTransitions(buttonSx 里排在前面)\n root: {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '_sm',\n minHeight: `max(${t.sizes.control}, ${t.sizes.minTarget})`,\n paddingBlock: '_xs',\n paddingInline: '_lg',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: 'transparent',\n fontWeight: '_semibold',\n lineHeight: '_tight',\n whiteSpace: 'nowrap',\n },\n // 图标按钮:宽高相等\n square: {\n paddingInline: 0,\n width: `max(${t.sizes.control}, ${t.sizes.minTarget})`,\n flexShrink: 0,\n },\n fullWidth: { display: 'flex', width: '_full' },\n disabled: { opacity: '_disabled', cursor: 'not-allowed' },\n content: { display: 'inline-flex', alignItems: 'center', gap: '_sm' },\n // 加载中隐藏内容但保留占位与无障碍名称(不用 visibility: hidden,那样读屏也读不到)\n hidden: { opacity: 0 },\n spinner: {\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n\n/** 悬停变色只在可用时:原生 disabled 的按钮照样匹配 :hover,悬停变色会让人以为还能点 */\nconst HOVER = ':hover:not(:disabled,[aria-disabled=\"true\"])';\n\n/**\n * 按钮的样式(配方):样式 × 色调逐项写主题 token(组合档位)。中性色没有专门的实底色,用反色表面。\n * 实底悬停用各色调的 Hover token:主题检查保证它与 Contrast 字色到 4.5:1\n */\nexport const buttonRecipe = css.recipe({\n name: 'Button',\n parts: ['root'],\n variants: {\n variant: {\n solid: {},\n soft: {},\n surface: {},\n outline: {},\n ghost: {},\n link: {\n root: {\n minHeight: 0,\n paddingInline: 0,\n fontWeight: '_medium',\n textUnderlineOffset: '0.2em',\n textDecorationLine: { default: 'none', [HOVER]: 'underline' },\n },\n },\n },\n tone: { neutral: {}, accent: {}, info: {}, success: {}, warning: {}, danger: {} },\n },\n compound: [\n {\n when: { variant: 'solid', tone: 'neutral' },\n root: {\n color: '_inverseText',\n backgroundColor: {\n default: '_inverseSurface',\n [HOVER]: `color-mix(in oklab, ${t.colors.inverseSurface} 85%, ${t.colors.background})`,\n },\n },\n },\n {\n when: { variant: 'solid', tone: 'accent' },\n root: {\n color: '_accentContrast',\n backgroundColor: { default: '_accent', [HOVER]: '_accentHover' },\n },\n },\n {\n when: { variant: 'solid', tone: 'info' },\n root: {\n color: '_infoContrast',\n backgroundColor: { default: '_info', [HOVER]: '_infoHover' },\n },\n },\n {\n when: { variant: 'solid', tone: 'success' },\n root: {\n color: '_successContrast',\n backgroundColor: { default: '_success', [HOVER]: '_successHover' },\n },\n },\n {\n when: { variant: 'solid', tone: 'warning' },\n root: {\n color: '_warningContrast',\n backgroundColor: { default: '_warning', [HOVER]: '_warningHover' },\n },\n },\n {\n when: { variant: 'solid', tone: 'danger' },\n root: {\n color: '_dangerContrast',\n backgroundColor: { default: '_danger', [HOVER]: '_dangerHover' },\n },\n },\n {\n when: { variant: 'soft', tone: 'neutral' },\n root: { color: '_text', backgroundColor: { default: '_fill', [HOVER]: '_fillHover' } },\n },\n {\n when: { variant: 'soft', tone: 'accent' },\n root: {\n color: '_accentText',\n backgroundColor: { default: '_accentSoft', [HOVER]: '_accentSoftHover' },\n },\n },\n {\n when: { variant: 'soft', tone: 'info' },\n root: {\n color: '_infoText',\n backgroundColor: { default: '_infoSoft', [HOVER]: '_infoSoftHover' },\n },\n },\n {\n when: { variant: 'soft', tone: 'success' },\n root: {\n color: '_successText',\n backgroundColor: { default: '_successSoft', [HOVER]: '_successSoftHover' },\n },\n },\n {\n when: { variant: 'soft', tone: 'warning' },\n root: {\n color: '_warningText',\n backgroundColor: { default: '_warningSoft', [HOVER]: '_warningSoftHover' },\n },\n },\n {\n when: { variant: 'soft', tone: 'danger' },\n root: {\n color: '_dangerText',\n backgroundColor: { default: '_dangerSoft', [HOVER]: '_dangerSoftHover' },\n },\n },\n {\n when: { variant: 'surface', tone: 'neutral' },\n root: {\n color: '_text',\n borderColor: '_border',\n backgroundColor: { default: '_surface', [HOVER]: '_fillHover' },\n },\n },\n {\n when: { variant: 'surface', tone: 'accent' },\n root: {\n color: '_accentText',\n borderColor: '_accentBorder',\n backgroundColor: { default: '_accentSoft', [HOVER]: '_accentSoftHover' },\n },\n },\n {\n when: { variant: 'surface', tone: 'info' },\n root: {\n color: '_infoText',\n borderColor: '_infoBorder',\n backgroundColor: { default: '_infoSoft', [HOVER]: '_infoSoftHover' },\n },\n },\n {\n when: { variant: 'surface', tone: 'success' },\n root: {\n color: '_successText',\n borderColor: '_successBorder',\n backgroundColor: { default: '_successSoft', [HOVER]: '_successSoftHover' },\n },\n },\n {\n when: { variant: 'surface', tone: 'warning' },\n root: {\n color: '_warningText',\n borderColor: '_warningBorder',\n backgroundColor: { default: '_warningSoft', [HOVER]: '_warningSoftHover' },\n },\n },\n {\n when: { variant: 'surface', tone: 'danger' },\n root: {\n color: '_dangerText',\n borderColor: '_dangerBorder',\n backgroundColor: { default: '_dangerSoft', [HOVER]: '_dangerSoftHover' },\n },\n },\n {\n when: { variant: 'outline', tone: 'neutral' },\n root: {\n color: '_text',\n borderColor: '_border',\n backgroundColor: { default: 'transparent', [HOVER]: '_fillHover' },\n },\n },\n {\n when: { variant: 'outline', tone: 'accent' },\n root: {\n color: '_accentText',\n borderColor: '_accentBorder',\n backgroundColor: { default: 'transparent', [HOVER]: '_accentSoft' },\n },\n },\n {\n when: { variant: 'outline', tone: 'info' },\n root: {\n color: '_infoText',\n borderColor: '_infoBorder',\n backgroundColor: { default: 'transparent', [HOVER]: '_infoSoft' },\n },\n },\n {\n when: { variant: 'outline', tone: 'success' },\n root: {\n color: '_successText',\n borderColor: '_successBorder',\n backgroundColor: { default: 'transparent', [HOVER]: '_successSoft' },\n },\n },\n {\n when: { variant: 'outline', tone: 'warning' },\n root: {\n color: '_warningText',\n borderColor: '_warningBorder',\n backgroundColor: { default: 'transparent', [HOVER]: '_warningSoft' },\n },\n },\n {\n when: { variant: 'outline', tone: 'danger' },\n root: {\n color: '_dangerText',\n borderColor: '_dangerBorder',\n backgroundColor: { default: 'transparent', [HOVER]: '_dangerSoft' },\n },\n },\n {\n when: { variant: 'ghost', tone: 'neutral' },\n root: { color: '_text', backgroundColor: { default: 'transparent', [HOVER]: '_fillHover' } },\n },\n {\n when: { variant: 'ghost', tone: 'accent' },\n root: {\n color: '_accentText',\n backgroundColor: { default: 'transparent', [HOVER]: '_accentSoft' },\n },\n },\n {\n when: { variant: 'ghost', tone: 'info' },\n root: {\n color: '_infoText',\n backgroundColor: { default: 'transparent', [HOVER]: '_infoSoft' },\n },\n },\n {\n when: { variant: 'ghost', tone: 'success' },\n root: {\n color: '_successText',\n backgroundColor: { default: 'transparent', [HOVER]: '_successSoft' },\n },\n },\n {\n when: { variant: 'ghost', tone: 'warning' },\n root: {\n color: '_warningText',\n backgroundColor: { default: 'transparent', [HOVER]: '_warningSoft' },\n },\n },\n {\n when: { variant: 'ghost', tone: 'danger' },\n root: {\n color: '_dangerText',\n backgroundColor: { default: 'transparent', [HOVER]: '_dangerSoft' },\n },\n },\n { when: { variant: 'link', tone: 'neutral' }, root: { color: '_text' } },\n { when: { variant: 'link', tone: 'accent' }, root: { color: '_accentText' } },\n { when: { variant: 'link', tone: 'info' }, root: { color: '_infoText' } },\n { when: { variant: 'link', tone: 'success' }, root: { color: '_successText' } },\n { when: { variant: 'link', tone: 'warning' }, root: { color: '_warningText' } },\n { when: { variant: 'link', tone: 'danger' }, root: { color: '_dangerText' } },\n ],\n});\n\n/** 按钮的部件(createUi / Provider 的 styles.Button 按它定制) */\nexport type ButtonPart = (typeof buttonRecipe.parts)[number];\n\n/** 按钮根元素的部件标记:`data-scope=\"button\" data-part=\"root\"` */\nexport const buttonPart = partAttrs(buttonRecipe, 'root');\n\n/** ButtonGroup 交给组里按钮的状态:统一的外观(相连的方向经 AttachedContext 给出) */\nexport interface ButtonGroupState {\n /** 组上写的外观,组里的按钮没写的项用它 */\n readonly appearance: Pick<ButtonOptions, 'variant' | 'tone' | 'size' | 'radius'>;\n}\n\n/** 按钮组的上下文:组里的按钮家族据此相连、共用外观 */\nexport const ButtonGroupContext = createContext<ButtonGroupState>('ButtonGroup');\n\n/** 合并后的外观:每一项都有值(size 不写时继承字号);在 ButtonGroup、ControlGroup 里时带上相连的方向 */\nexport type ButtonAppearance = Required<Pick<ButtonOptions, 'variant' | 'tone' | 'radius'>> &\n Pick<ButtonOptions, 'size'> & {\n attached?: keyof AttachedOrientations;\n /** 按钮的配方,带上 createUi / Provider 里 styles.Button 的定制 */\n recipe: ReturnType<typeof useRecipe<ButtonPart, unknown>>;\n };\n\n/**\n * 按钮的外观:显式写的 prop > 所在 ButtonGroup 写的 > createUi / Provider 的 defaults.Button > 组件自己的默认样式。\n * 在 setup 里调用,返回的函数在渲染时调用(读到最新的 prop 与配置)\n */\nexport function useButtonAppearance(\n read: () => Pick<ButtonOptions, 'variant' | 'tone' | 'size' | 'radius'>,\n fallbackVariant: keyof ButtonVariants,\n fallbackTone: StatusTone = 'accent',\n): () => ButtonAppearance {\n const group = ButtonGroupContext.inject(null);\n const attachedGroup = AttachedContext.inject(null);\n const recipe = useRecipe(buttonRecipe);\n return useDefaults(\n 'Button',\n () => {\n const { variant, tone, size, radius } = read();\n const shared = group?.appearance;\n return {\n variant: variant ?? shared?.variant,\n tone: tone ?? shared?.tone,\n size: size ?? shared?.size,\n radius: radius ?? shared?.radius,\n attached: attachedGroup?.orientation,\n recipe,\n };\n },\n // 组里的按钮外观要一致:不论有没有文字都按 Button 的默认色调(拆分按钮的两半同色)\n { variant: fallbackVariant, tone: group ? 'accent' : fallbackTone, radius: 'md' },\n );\n}\n\n/** 外观(已合并默认值)与状态 → 按钮根元素的样式 */\nexport function buttonSx(\n { variant, tone, size, radius, attached, recipe }: ButtonAppearance,\n state: { disabled: boolean; square?: boolean; fullWidth?: boolean },\n): SxStyles {\n return [\n buttonBaseStyles.root,\n interactionTransitions.colors,\n buttonStyles.root,\n focusRingStyles.visible,\n rippleHostStyles.host,\n recipe({ variant, tone }).root.sx,\n radiusStyles[radius],\n attached && attachedStyles[attached],\n size && fontSizeStyles[size],\n state.square && buttonStyles.square,\n state.fullWidth && buttonStyles.fullWidth,\n state.disabled && buttonStyles.disabled,\n ] as SxStyles;\n}\n\n/** 按钮内容:图标、文字、加载指示;加载中内容透明但保留宽度与无障碍名称 */\nexport function buttonContent(\n {\n icon,\n iconEnd,\n children,\n propsContent,\n propsIcon,\n propsIconEnd,\n }: Omit<ButtonContentOptions, 'fullWidth'>,\n { loading, propsSpinner }: Pick<ButtonOptions, 'loading' | 'propsSpinner'>,\n): VNodeChild {\n return [\n h(\n 'span',\n mergeAttrs<'span'>({ sx: buttonStyles.content }, propsContent, {\n sx: loading && buttonStyles.hidden,\n }),\n [\n icon && h(Icon, mergeProps<IconProps>({ icon }, propsIcon) as IconProps),\n children,\n iconEnd && h(Icon, mergeProps<IconProps>({ icon: iconEnd }, propsIconEnd) as IconProps),\n ],\n ),\n loading &&\n h(\n Spinner,\n mergeProps<SpinnerProps>({ sx: buttonStyles.spinner }, propsSpinner) as SpinnerProps,\n ),\n ];\n}\n\n/** useButtonBehavior 每次渲染读取的状态 */\nexport interface ButtonBehaviorState {\n /** 不可用 */\n disabled: boolean;\n /** 加载中:点击无效,焦点保留 */\n loading: boolean;\n /** 不可用时仍可聚焦:用 aria-disabled 代替原生 disabled。链接没有原生 disabled,总是写 true */\n focusableWhenDisabled?: boolean;\n /** 波纹;不写时沿用配置 */\n ripple?: boolean;\n}\n\n/** useButtonBehavior 给出的受管属性:展开到 mergeProps 的受管层 */\nexport interface ButtonBehaviorProps<E extends MouseEvent = MouseEvent> extends RippleHandlers {\n /** 原生 disabled(不可聚焦的不可用) */\n disabled?: boolean;\n /** 可聚焦的不可用 */\n 'aria-disabled'?: true;\n /** 加载中 */\n 'aria-busy'?: true;\n /** 加载中、可聚焦的不可用时拦下点击,否则交给使用方的 onClick */\n onClick: (event: E) => void;\n /** 状态写成 data 属性,样式与测试据此选择 */\n 'data-loading'?: true;\n /** 不可用(两种方式都有) */\n 'data-disabled'?: true;\n}\n\n/**\n * 按钮家族的行为:不可用(原生 disabled 或可聚焦的 aria-disabled)、加载中、拦截点击、波纹、状态 data 属性。\n * 被拦下的点击会 preventDefault:不触发使用方的 onClick,也不提交表单、不跟随链接。\n *\n * const behavior = useButtonBehavior(() => ({ disabled, loading, focusableWhenDisabled, ripple }), () => onClick);\n * mergeAttrs<'button'>(默认, rest, behavior())\n */\nexport function useButtonBehavior<E extends MouseEvent>(\n read: () => ButtonBehaviorState,\n onClick: () => ((event: E) => void) | undefined,\n): () => ButtonBehaviorProps<E> {\n const ripple = useRipple(() => read().ripple);\n function click(event: E) {\n const { disabled, loading, focusableWhenDisabled = false } = read();\n if (loading || (disabled && focusableWhenDisabled)) {\n event.preventDefault();\n return;\n }\n onClick()?.(event);\n }\n return () => {\n const { disabled, loading, focusableWhenDisabled = false } = read();\n const soft = disabled && focusableWhenDisabled;\n return {\n disabled: (disabled && !focusableWhenDisabled) || undefined,\n 'aria-disabled': soft || undefined,\n 'aria-busy': loading || undefined,\n onClick: click,\n ...ripple,\n 'data-loading': loading || undefined,\n 'data-disabled': disabled || undefined,\n };\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"button-core.js","names":[],"sources":["../../src/base/button-core.ts"],"sourcesContent":["/**\n * 按钮家族(Button、LinkButton、IconButton、CloseButton、ClearButton)的内核:共用的外观、选项、内容排布与行为。\n * 一个元素一个组件(COMPONENTS.md §1):各组件只管自己元素的行为,样式与内容排布都从这里取。\n *\n * 外观 = variant(样式)× tone(色调)× size(字号档位,内部尺寸随之按 em 缩放)× radius(圆角)。\n */\nimport { h, type VNodeChild } from 'vue';\nimport { createContext } 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 { Icon, type IconData, type IconProps } from './Icon';\nimport { Spinner, type SpinnerProps } from './Spinner';\nimport { useDefaults } from '../config/defaults';\nimport { partAttrs, useRecipe } from '../config/styles';\nimport { rippleHostStyles, useRipple, type RippleHandlers } from '../utils/dom/ripple';\nimport { mergeAttrs, mergeProps, type PartProps } from '../utils/props';\nimport { focusRingStyles, radiusStyles, type Radius } from '../utils/style/styles';\nimport type { StatusTone } from '../utils/style/tone-styles';\nimport {\n AttachedContext,\n attachedStyles,\n type AttachedOrientations,\n} from '../utils/style/attached';\nimport { interactionTransitions } from '../utils/style/transitions';\nimport { fontSizeStyles, type TextSize } from '../utils/style/typography';\nimport { t } from '../config/theme/tokens';\n\n/** 按钮样式 */\nexport interface ButtonVariants {\n /** 实底:一组操作里的主要操作,每组最多一个 */\n solid: unknown;\n /** 浅色底:次要但仍需突出的操作,如卡片里的操作 */\n soft: unknown;\n /** 浅色底加描边:表单、设置页里常用的次要操作 */\n surface: unknown;\n /** 只有描边:次要操作 */\n outline: unknown;\n /** 无底无边,悬停时才有底色:工具栏、行内操作、图标按钮 */\n ghost: unknown;\n /** 看起来像链接(无底、无内边距、悬停下划线):文字中间的操作 */\n link: unknown;\n}\n\n/** 按钮家族共用的外观选项;可以用 createUi / Provider 的 defaults.Button 统一改默认值 */\nexport interface ButtonOptions {\n /**\n * 样式;可以用 createUi / Provider 的 defaults.Button 统一改\n * @default 'solid'\n */\n variant?: keyof ButtonVariants;\n /**\n * 色调:与 Badge、Alert 等同一套\n * @default 'accent'\n */\n tone?: StatusTone;\n /** 字号档位,内部尺寸随之缩放;不写时继承上下文字号 */\n size?: TextSize;\n /**\n * 圆角\n * @default 'md'\n */\n radius?: Radius;\n /** 加载中:显示旋转指示,宽度不变,点击无效,焦点保留;读屏会得知 aria-busy */\n loading?: boolean;\n /** 按下时的波纹效果;不写时沿用 createUi / Provider 的 ripple(默认开启) */\n ripple?: boolean;\n /** 加载指示(Spinner)的部件属性 */\n propsSpinner?: Partial<SpinnerProps>;\n}\n\n/** 有文字的按钮(Button、LinkButton)另有的选项 */\nexport interface ButtonContentOptions {\n /** 文字前的图标:`import { Plus } from '@lucide/icons'` */\n icon?: IconData;\n /** 文字后的图标 */\n iconEnd?: IconData;\n /** 占满容器宽度 */\n fullWidth?: boolean;\n /** 内容层(包着图标与文字的 span)的部件属性 */\n propsContent?: PartProps<'span'>;\n /** 前置图标的部件属性 */\n propsIcon?: Partial<IconProps>;\n /** 后置图标的部件属性 */\n propsIconEnd?: Partial<IconProps>;\n /** 按钮文字;加载中仍保留(读屏名称不变) */\n children?: VNodeChild;\n}\n\n/** 按钮的基础样式:去掉默认外观,继承文字样式,指针与触摸的反馈;Button 家族也用它 */\nexport const buttonBaseStyles = css.create({\n root: {\n boxSizing: 'border-box',\n margin: 0,\n padding: 0,\n borderWidth: 0,\n backgroundColor: 'transparent',\n color: 'inherit',\n fontFamily: 'inherit',\n fontSize: 'inherit',\n lineHeight: 'inherit',\n textAlign: 'inherit',\n textDecoration: 'none',\n cursor: 'pointer',\n userSelect: 'none',\n // 触摸时不等双击缩放的判定,点击立即响应\n touchAction: 'manipulation',\n WebkitTapHighlightColor: 'transparent',\n },\n disabled: { cursor: 'not-allowed' },\n});\n\n/** 按钮家族 的样式:root、square、fullWidth、disabled、content、hidden 等 */\nexport const buttonStyles = css.create({\n // 去掉默认外观、指针与触摸反馈、颜色过渡来自 buttonBaseStyles 与 interactionTransitions(buttonSx 里排在前面)\n root: {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '_sm',\n minHeight: `max(${t.sizes.control}, ${t.sizes.minTarget})`,\n paddingBlock: '_xs',\n paddingInline: '_lg',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: 'transparent',\n fontWeight: '_semibold',\n lineHeight: '_tight',\n whiteSpace: 'nowrap',\n },\n // 图标按钮:宽高相等\n square: {\n paddingInline: 0,\n width: `max(${t.sizes.control}, ${t.sizes.minTarget})`,\n flexShrink: 0,\n },\n fullWidth: { display: 'flex', width: '_full' },\n disabled: { opacity: '_disabled', cursor: 'not-allowed' },\n content: { display: 'inline-flex', alignItems: 'center', gap: '_sm' },\n // 加载中隐藏内容但保留占位与无障碍名称(不用 visibility: hidden,那样读屏也读不到)\n hidden: { opacity: 0 },\n spinner: {\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n\n/** 悬停变色只在可用时:原生 disabled 的按钮照样匹配 :hover,悬停变色会让人以为还能点 */\nconst HOVER = ':hover:not(:disabled,[aria-disabled=\"true\"])';\n\n/**\n * 按钮的样式(配方):样式 × 色调逐项写主题 token(组合档位)。中性色没有专门的实底色,用反色表面。\n * 实底悬停用各色调的 Hover token:主题检查保证它与 Contrast 字色到 4.5:1\n */\nexport const buttonRecipe = css.recipe({\n name: 'Button',\n parts: ['root'],\n variants: {\n variant: {\n solid: {},\n soft: {},\n surface: {},\n outline: {},\n ghost: {},\n link: {\n root: {\n minHeight: 0,\n paddingInline: 0,\n fontWeight: '_medium',\n textUnderlineOffset: '0.2em',\n textDecorationLine: { default: 'none', [HOVER]: 'underline' },\n },\n },\n },\n tone: { neutral: {}, accent: {}, info: {}, success: {}, warning: {}, danger: {} },\n },\n compound: [\n {\n when: { variant: 'solid', tone: 'neutral' },\n root: {\n color: '_inverseText',\n backgroundColor: {\n default: '_inverseSurface',\n [HOVER]: `color-mix(in oklab, ${t.colors.inverseSurface} 85%, ${t.colors.background})`,\n },\n },\n },\n {\n when: { variant: 'solid', tone: 'accent' },\n root: {\n color: '_accentContrast',\n backgroundColor: { default: '_accent', [HOVER]: '_accentHover' },\n },\n },\n {\n when: { variant: 'solid', tone: 'info' },\n root: {\n color: '_infoContrast',\n backgroundColor: { default: '_info', [HOVER]: '_infoHover' },\n },\n },\n {\n when: { variant: 'solid', tone: 'success' },\n root: {\n color: '_successContrast',\n backgroundColor: { default: '_success', [HOVER]: '_successHover' },\n },\n },\n {\n when: { variant: 'solid', tone: 'warning' },\n root: {\n color: '_warningContrast',\n backgroundColor: { default: '_warning', [HOVER]: '_warningHover' },\n },\n },\n {\n when: { variant: 'solid', tone: 'danger' },\n root: {\n color: '_dangerContrast',\n backgroundColor: { default: '_danger', [HOVER]: '_dangerHover' },\n },\n },\n {\n when: { variant: 'soft', tone: 'neutral' },\n root: { color: '_text', backgroundColor: { default: '_fill', [HOVER]: '_fillHover' } },\n },\n {\n when: { variant: 'soft', tone: 'accent' },\n root: {\n color: '_accentText',\n backgroundColor: { default: '_accentSoft', [HOVER]: '_accentSoftHover' },\n },\n },\n {\n when: { variant: 'soft', tone: 'info' },\n root: {\n color: '_infoText',\n backgroundColor: { default: '_infoSoft', [HOVER]: '_infoSoftHover' },\n },\n },\n {\n when: { variant: 'soft', tone: 'success' },\n root: {\n color: '_successText',\n backgroundColor: { default: '_successSoft', [HOVER]: '_successSoftHover' },\n },\n },\n {\n when: { variant: 'soft', tone: 'warning' },\n root: {\n color: '_warningText',\n backgroundColor: { default: '_warningSoft', [HOVER]: '_warningSoftHover' },\n },\n },\n {\n when: { variant: 'soft', tone: 'danger' },\n root: {\n color: '_dangerText',\n backgroundColor: { default: '_dangerSoft', [HOVER]: '_dangerSoftHover' },\n },\n },\n {\n when: { variant: 'surface', tone: 'neutral' },\n root: {\n color: '_text',\n borderColor: '_border',\n backgroundColor: { default: '_surface', [HOVER]: '_fillHover' },\n },\n },\n {\n when: { variant: 'surface', tone: 'accent' },\n root: {\n color: '_accentText',\n borderColor: '_accentBorder',\n backgroundColor: { default: '_accentSoft', [HOVER]: '_accentSoftHover' },\n },\n },\n {\n when: { variant: 'surface', tone: 'info' },\n root: {\n color: '_infoText',\n borderColor: '_infoBorder',\n backgroundColor: { default: '_infoSoft', [HOVER]: '_infoSoftHover' },\n },\n },\n {\n when: { variant: 'surface', tone: 'success' },\n root: {\n color: '_successText',\n borderColor: '_successBorder',\n backgroundColor: { default: '_successSoft', [HOVER]: '_successSoftHover' },\n },\n },\n {\n when: { variant: 'surface', tone: 'warning' },\n root: {\n color: '_warningText',\n borderColor: '_warningBorder',\n backgroundColor: { default: '_warningSoft', [HOVER]: '_warningSoftHover' },\n },\n },\n {\n when: { variant: 'surface', tone: 'danger' },\n root: {\n color: '_dangerText',\n borderColor: '_dangerBorder',\n backgroundColor: { default: '_dangerSoft', [HOVER]: '_dangerSoftHover' },\n },\n },\n {\n when: { variant: 'outline', tone: 'neutral' },\n root: {\n color: '_text',\n borderColor: '_border',\n backgroundColor: { default: 'transparent', [HOVER]: '_fillHover' },\n },\n },\n {\n when: { variant: 'outline', tone: 'accent' },\n root: {\n color: '_accentText',\n borderColor: '_accentBorder',\n backgroundColor: { default: 'transparent', [HOVER]: '_accentSoft' },\n },\n },\n {\n when: { variant: 'outline', tone: 'info' },\n root: {\n color: '_infoText',\n borderColor: '_infoBorder',\n backgroundColor: { default: 'transparent', [HOVER]: '_infoSoft' },\n },\n },\n {\n when: { variant: 'outline', tone: 'success' },\n root: {\n color: '_successText',\n borderColor: '_successBorder',\n backgroundColor: { default: 'transparent', [HOVER]: '_successSoft' },\n },\n },\n {\n when: { variant: 'outline', tone: 'warning' },\n root: {\n color: '_warningText',\n borderColor: '_warningBorder',\n backgroundColor: { default: 'transparent', [HOVER]: '_warningSoft' },\n },\n },\n {\n when: { variant: 'outline', tone: 'danger' },\n root: {\n color: '_dangerText',\n borderColor: '_dangerBorder',\n backgroundColor: { default: 'transparent', [HOVER]: '_dangerSoft' },\n },\n },\n {\n when: { variant: 'ghost', tone: 'neutral' },\n root: { color: '_text', backgroundColor: { default: 'transparent', [HOVER]: '_fillHover' } },\n },\n {\n when: { variant: 'ghost', tone: 'accent' },\n root: {\n color: '_accentText',\n backgroundColor: { default: 'transparent', [HOVER]: '_accentSoft' },\n },\n },\n {\n when: { variant: 'ghost', tone: 'info' },\n root: {\n color: '_infoText',\n backgroundColor: { default: 'transparent', [HOVER]: '_infoSoft' },\n },\n },\n {\n when: { variant: 'ghost', tone: 'success' },\n root: {\n color: '_successText',\n backgroundColor: { default: 'transparent', [HOVER]: '_successSoft' },\n },\n },\n {\n when: { variant: 'ghost', tone: 'warning' },\n root: {\n color: '_warningText',\n backgroundColor: { default: 'transparent', [HOVER]: '_warningSoft' },\n },\n },\n {\n when: { variant: 'ghost', tone: 'danger' },\n root: {\n color: '_dangerText',\n backgroundColor: { default: 'transparent', [HOVER]: '_dangerSoft' },\n },\n },\n { when: { variant: 'link', tone: 'neutral' }, root: { color: '_text' } },\n { when: { variant: 'link', tone: 'accent' }, root: { color: '_accentText' } },\n { when: { variant: 'link', tone: 'info' }, root: { color: '_infoText' } },\n { when: { variant: 'link', tone: 'success' }, root: { color: '_successText' } },\n { when: { variant: 'link', tone: 'warning' }, root: { color: '_warningText' } },\n { when: { variant: 'link', tone: 'danger' }, root: { color: '_dangerText' } },\n ],\n});\n\n/** 按钮的部件(createUi / Provider 的 styles.Button 按它定制) */\nexport type ButtonPart = (typeof buttonRecipe.parts)[number];\n\n/** 按钮根元素的部件标记:`data-scope=\"button\" data-part=\"root\"` */\nexport const buttonPart = partAttrs(buttonRecipe, 'root');\n\n/** ButtonGroup 交给组里按钮的状态:统一的外观(相连的方向经 AttachedContext 给出) */\nexport interface ButtonGroupState {\n /** 组上写的外观,组里的按钮没写的项用它 */\n readonly appearance: Pick<ButtonOptions, 'variant' | 'tone' | 'size' | 'radius'>;\n}\n\n/** 按钮组的上下文:组里的按钮家族据此相连、共用外观 */\nexport const ButtonGroupContext = createContext<ButtonGroupState>('ButtonGroup');\n\n/** 合并后的外观:每一项都有值(size 不写时继承字号);在 ButtonGroup、ControlGroup 里时带上相连的方向 */\nexport type ButtonAppearance = Required<Pick<ButtonOptions, 'variant' | 'tone' | 'radius'>> &\n Pick<ButtonOptions, 'size'> & {\n attached?: keyof AttachedOrientations;\n /** 按钮的配方,带上 createUi / Provider 里 styles.Button 的定制 */\n recipe: ReturnType<typeof useRecipe<ButtonPart, unknown>>;\n };\n\n/**\n * 按钮的外观:显式写的 prop > 所在 ButtonGroup 写的 > createUi / Provider 的 defaults.Button > 组件自己的默认样式。\n * 在 setup 里调用,返回的函数在渲染时调用(读到最新的 prop 与配置)\n */\nexport function useButtonAppearance(\n read: () => Pick<ButtonOptions, 'variant' | 'tone' | 'size' | 'radius'>,\n fallbackVariant: keyof ButtonVariants,\n fallbackTone: StatusTone = 'accent',\n): () => ButtonAppearance {\n const group = ButtonGroupContext.inject(null);\n const attachedGroup = AttachedContext.inject(null);\n const recipe = useRecipe(buttonRecipe);\n return useDefaults(\n 'Button',\n () => {\n const { variant, tone, size, radius } = read();\n const shared = group?.appearance;\n return {\n variant: variant ?? shared?.variant,\n tone: tone ?? shared?.tone,\n size: size ?? shared?.size,\n radius: radius ?? shared?.radius,\n attached: attachedGroup?.orientation,\n recipe,\n };\n },\n // 组里的按钮外观要一致:不论有没有文字都按 Button 的默认色调(拆分按钮的两半同色)\n { variant: fallbackVariant, tone: group ? 'accent' : fallbackTone, radius: 'md' },\n );\n}\n\n/** 外观(已合并默认值)与状态 → 按钮根元素的样式 */\nexport function buttonSx(\n { variant, tone, size, radius, attached, recipe }: ButtonAppearance,\n state: { disabled: boolean; square?: boolean; fullWidth?: boolean },\n): SxStyles {\n return [\n buttonBaseStyles.root,\n interactionTransitions.colors,\n buttonStyles.root,\n focusRingStyles.visible,\n rippleHostStyles.host,\n recipe({ variant, tone }).root.sx,\n radiusStyles[radius],\n attached && attachedStyles[attached],\n size && fontSizeStyles[size],\n state.square && buttonStyles.square,\n state.fullWidth && buttonStyles.fullWidth,\n state.disabled && buttonStyles.disabled,\n ] as SxStyles;\n}\n\n/** 按钮内容:图标、文字、加载指示;加载中内容透明但保留宽度与无障碍名称 */\nexport function buttonContent(\n {\n icon,\n iconEnd,\n children,\n propsContent,\n propsIcon,\n propsIconEnd,\n }: Omit<ButtonContentOptions, 'fullWidth'>,\n { loading, propsSpinner }: Pick<ButtonOptions, 'loading' | 'propsSpinner'>,\n): VNodeChild {\n return [\n h(\n 'span',\n mergeAttrs<'span'>({ sx: buttonStyles.content }, propsContent, {\n sx: loading && buttonStyles.hidden,\n }),\n [\n icon && h(Icon, mergeProps<IconProps>({ icon }, propsIcon) as IconProps),\n children,\n iconEnd && h(Icon, mergeProps<IconProps>({ icon: iconEnd }, propsIconEnd) as IconProps),\n ],\n ),\n loading &&\n h(\n Spinner,\n mergeProps<SpinnerProps>({ sx: buttonStyles.spinner }, propsSpinner) as SpinnerProps,\n ),\n ];\n}\n\n/** useButtonBehavior 每次渲染读取的状态 */\nexport interface ButtonBehaviorState {\n /** 不可用 */\n disabled: boolean;\n /** 加载中:点击无效,焦点保留 */\n loading: boolean;\n /** 不可用时仍可聚焦:用 aria-disabled 代替原生 disabled。链接没有原生 disabled,总是写 true */\n focusableWhenDisabled?: boolean;\n /** 波纹;不写时沿用配置 */\n ripple?: boolean;\n}\n\n/** useButtonBehavior 给出的受管属性:展开到 mergeProps 的受管层 */\nexport interface ButtonBehaviorProps<E extends MouseEvent = MouseEvent> extends RippleHandlers {\n /** 原生 disabled(不可聚焦的不可用) */\n disabled?: boolean;\n /** 可聚焦的不可用 */\n 'aria-disabled'?: true;\n /** 加载中 */\n 'aria-busy'?: true;\n /** 加载中、可聚焦的不可用时拦下点击,否则交给使用方的 onClick */\n onClick: (event: E) => void;\n /** 状态写成 data 属性,样式与测试据此选择 */\n 'data-loading'?: true;\n /** 不可用(两种方式都有) */\n 'data-disabled'?: true;\n}\n\n/**\n * 按钮家族的行为:不可用(原生 disabled 或可聚焦的 aria-disabled)、加载中、拦截点击、波纹、状态 data 属性。\n * 被拦下的点击会 preventDefault:不触发使用方的 onClick,也不提交表单、不跟随链接。\n *\n * const behavior = useButtonBehavior(() => ({ disabled, loading, focusableWhenDisabled, ripple }), () => onClick);\n * mergeAttrs<'button'>(默认, rest, behavior())\n */\nexport function useButtonBehavior<E extends MouseEvent>(\n read: () => ButtonBehaviorState,\n onClick: () => ((event: E) => void) | undefined,\n): () => ButtonBehaviorProps<E> {\n const ripple = useRipple(() => read().ripple);\n function click(event: E) {\n const { disabled, loading, focusableWhenDisabled = false } = read();\n if (loading || (disabled && focusableWhenDisabled)) {\n event.preventDefault();\n return;\n }\n onClick()?.(event);\n }\n return () => {\n const { disabled, loading, focusableWhenDisabled = false } = read();\n const soft = disabled && focusableWhenDisabled;\n return {\n disabled: (disabled && !focusableWhenDisabled) || undefined,\n 'aria-disabled': soft || undefined,\n 'aria-busy': loading || undefined,\n onClick: click,\n ...ripple,\n 'data-loading': loading || undefined,\n 'data-disabled': disabled || undefined,\n };\n };\n}\n"],"mappings":"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"}
|
package/dist/charts/AreaChart.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { XYChart } from "./XYChart.js";
|
|
2
2
|
import { CURVES } from "./LineChart.js";
|
|
3
|
-
import { computed, createVNode
|
|
4
|
-
import { _component, bindable, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
3
|
+
import { computed, createVNode } from "vue";
|
|
4
|
+
import { _component, bindable, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
5
5
|
import { Area } from "@unovis/ts";
|
|
6
6
|
//#region src/charts/AreaChart.tsx
|
|
7
7
|
/**
|
|
@@ -69,7 +69,7 @@ var AreaChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
}));
|
|
72
|
-
return () => createVNode(XYChart,
|
|
72
|
+
return () => createVNode(XYChart, mergeJsxProps(rest, {
|
|
73
73
|
"_hiddenSeries": hiddenSeries.value,
|
|
74
74
|
"onUpdate:_hiddenSeries": _cache[0] || (_cache[0] = ($event) => hiddenSeries.value = $event),
|
|
75
75
|
"kind": kind.value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AreaChart.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_createVNode","_mergeProps"],"sources":["../../src/charts/AreaChart.tsx"],"sourcesContent":["/**\n * 面积图:每个系列一片面积,上沿描一条线;默认各系列重叠、半透明(看得出谁高谁低),stacked 时依次叠起(看总量与构成),\n * stacked=\"percent\" 时按占比叠满 100%(只看构成的变化)。\n *\n * <AreaChart data={months} index=\"month\" series={[{ key: 'visits', label: '访问量' }]} />\n * <AreaChart stacked data={months} index=\"month\" series={channels} unit=\"万次\" />\n * <AreaChart stacked=\"percent\" data={months} index=\"month\" series={channels} />\n *\n * 坐标轴、十字线(带圆点)与提示框、图例、单位、加载与空状态、给读屏的数据表同 LineChart(见 XYChart)。\n */\nimport { Area } from '@unovis/ts';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { CURVES, type LineCurves } from './LineChart';\nimport { XYChart, type XYChartKind, type XYChartProps } from './XYChart';\n\n/** 未声明的属性落到外层 div(role=\"figure\")上 */\nexport interface AreaChartProps<T extends object> extends XYChartProps<T> {\n /** 堆叠:true 各系列依次叠起,'percent' 按占比叠满 100%(数值轴是百分比);默认重叠、半透明 */\n stacked?: boolean | 'percent';\n /**\n * 曲线的形状\n * @default 'monotone'\n */\n curve?: keyof LineCurves;\n /**\n * 面积的不透明度(0–1);默认重叠时 0.25、堆叠时 0.8\n */\n opacity?: number;\n}\n\n/** 面积图 */\nexport const AreaChart = _component(\n <T extends object>({\n stacked = false,\n curve = 'monotone',\n opacity,\n _hiddenSeries: hiddenSeries = _bindable<string[]>([]),\n ...rest\n }: AreaChartProps<T>) => {\n const kind = _derived<XYChartKind<T>>({\n crosshairDots: true,\n stacked: stacked !== false,\n percent: stacked === 'percent',\n padding: { left: 12, right: 12, top: 8 },\n components: ({ series, x, y, instance }) => {\n const common = {\n x,\n curveType: CURVES[curve],\n line: true,\n lineWidth: 2,\n };\n if (stacked) {\n const area = instance('stacked', () => new Area<T>({ x, y: [] }));\n area.setConfig({\n ...common,\n y: series.map(y),\n color: (_rows: T[], position: number) => series[position]?.color,\n lineColor: (_rows: T[], position: number) => series[position]?.color,\n opacity: opacity ?? 0.8,\n });\n return [area];\n }\n // 重叠:每个系列一个 Area(Unovis 的多个 y 总是堆叠的)\n return series.map((item) => {\n const area = instance(`area:${item.key}`, () => new Area<T>({ x, y: () => 0 }));\n area.setConfig({\n ...common,\n y: y(item),\n color: item.color,\n lineColor: item.color,\n opacity: opacity ?? 0.25,\n });\n return area;\n });\n },\n });\n return () => <XYChart {...rest} _hiddenSeries={hiddenSeries} kind={kind} />;\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;AA+BA,IAAa,YAAY,0BACvB,QAAA,SAMyB;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,UAAAA,aAAA,QAAA,WALb,KAAK;CAAA,MAAA,QAAAA,aAAA,QAAA,SACP,UAAU;CAAA,MAAA,UAAAA,aAAA,QAAA,SAAA;CAAA,MAAA,eAAAC,SAAA,QAAA,uBAEgC,CAAA,GAAE,KAAA,IAAA;CAAA,MACjD,OAAIC,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,OAAIC,gBAA4B;EACpC,eAAe;EACf,SAAS,QAAA,UAAY;EACrB,SAAS,QAAA,UAAY;EACrB,SAAS;GAAE,MAAM;GAAI,OAAO;GAAI,KAAK;EAAE;EACvC,aAAa,EAAE,QAAQ,GAAG,GAAG,eAAe;GAC1C,MAAM,SAAS;IACb;IACA,WAAW,OAAM,MAAA;IACjB,MAAM;IACN,WAAW;GACb;GACA,IAAA,QAAA,OAAa;IACX,MAAM,OAAO,SAAS,iBAAiB,IAAI,KAAQ;KAAE;KAAG,GAAG,CAAA;IAAG,CAAC,CAAC;IAChE,KAAK,UAAU;KACb,GAAG;KACH,GAAG,OAAO,IAAI,CAAC;KACf,QAAQ,OAAY,aAAqB,OAAO,SAAS,EAAE;KAC3D,YAAY,OAAY,aAAqB,OAAO,SAAS,EAAE;KAC/D,SAAS,QAAA,SAAW;IACtB,CAAC;IACD,OAAO,CAAC,IAAI;GACd;GAEA,OAAO,OAAO,KAAK,SAAS;IAC1B,MAAM,OAAO,SAAS,QAAQ,KAAK,aAAa,IAAI,KAAQ;KAAE;KAAG,SAAS;IAAE,CAAC,CAAC;IAC9E,KAAK,UAAU;KACb,GAAG;KACH,GAAG,EAAE,IAAI;KACT,OAAO,KAAK;KACZ,WAAW,KAAK;KAChB,SAAS,QAAA,SAAW;IACtB,CAAC;IACD,OAAO;GACT,CAAC;EACH;CACF,EAAC;CACD,aAAOC,YAAA,SAAAC,
|
|
1
|
+
{"version":3,"file":"AreaChart.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_createVNode","_mergeProps"],"sources":["../../src/charts/AreaChart.tsx"],"sourcesContent":["/**\n * 面积图:每个系列一片面积,上沿描一条线;默认各系列重叠、半透明(看得出谁高谁低),stacked 时依次叠起(看总量与构成),\n * stacked=\"percent\" 时按占比叠满 100%(只看构成的变化)。\n *\n * <AreaChart data={months} index=\"month\" series={[{ key: 'visits', label: '访问量' }]} />\n * <AreaChart stacked data={months} index=\"month\" series={channels} unit=\"万次\" />\n * <AreaChart stacked=\"percent\" data={months} index=\"month\" series={channels} />\n *\n * 坐标轴、十字线(带圆点)与提示框、图例、单位、加载与空状态、给读屏的数据表同 LineChart(见 XYChart)。\n */\nimport { Area } from '@unovis/ts';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { CURVES, type LineCurves } from './LineChart';\nimport { XYChart, type XYChartKind, type XYChartProps } from './XYChart';\n\n/** 未声明的属性落到外层 div(role=\"figure\")上 */\nexport interface AreaChartProps<T extends object> extends XYChartProps<T> {\n /** 堆叠:true 各系列依次叠起,'percent' 按占比叠满 100%(数值轴是百分比);默认重叠、半透明 */\n stacked?: boolean | 'percent';\n /**\n * 曲线的形状\n * @default 'monotone'\n */\n curve?: keyof LineCurves;\n /**\n * 面积的不透明度(0–1);默认重叠时 0.25、堆叠时 0.8\n */\n opacity?: number;\n}\n\n/** 面积图 */\nexport const AreaChart = _component(\n <T extends object>({\n stacked = false,\n curve = 'monotone',\n opacity,\n _hiddenSeries: hiddenSeries = _bindable<string[]>([]),\n ...rest\n }: AreaChartProps<T>) => {\n const kind = _derived<XYChartKind<T>>({\n crosshairDots: true,\n stacked: stacked !== false,\n percent: stacked === 'percent',\n padding: { left: 12, right: 12, top: 8 },\n components: ({ series, x, y, instance }) => {\n const common = {\n x,\n curveType: CURVES[curve],\n line: true,\n lineWidth: 2,\n };\n if (stacked) {\n const area = instance('stacked', () => new Area<T>({ x, y: [] }));\n area.setConfig({\n ...common,\n y: series.map(y),\n color: (_rows: T[], position: number) => series[position]?.color,\n lineColor: (_rows: T[], position: number) => series[position]?.color,\n opacity: opacity ?? 0.8,\n });\n return [area];\n }\n // 重叠:每个系列一个 Area(Unovis 的多个 y 总是堆叠的)\n return series.map((item) => {\n const area = instance(`area:${item.key}`, () => new Area<T>({ x, y: () => 0 }));\n area.setConfig({\n ...common,\n y: y(item),\n color: item.color,\n lineColor: item.color,\n opacity: opacity ?? 0.25,\n });\n return area;\n });\n },\n });\n return () => <XYChart {...rest} _hiddenSeries={hiddenSeries} kind={kind} />;\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;AA+BA,IAAa,YAAY,0BACvB,QAAA,SAMyB;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,UAAAA,aAAA,QAAA,WALb,KAAK;CAAA,MAAA,QAAAA,aAAA,QAAA,SACP,UAAU;CAAA,MAAA,UAAAA,aAAA,QAAA,SAAA;CAAA,MAAA,eAAAC,SAAA,QAAA,uBAEgC,CAAA,GAAE,KAAA,IAAA;CAAA,MACjD,OAAIC,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,OAAIC,gBAA4B;EACpC,eAAe;EACf,SAAS,QAAA,UAAY;EACrB,SAAS,QAAA,UAAY;EACrB,SAAS;GAAE,MAAM;GAAI,OAAO;GAAI,KAAK;EAAE;EACvC,aAAa,EAAE,QAAQ,GAAG,GAAG,eAAe;GAC1C,MAAM,SAAS;IACb;IACA,WAAW,OAAM,MAAA;IACjB,MAAM;IACN,WAAW;GACb;GACA,IAAA,QAAA,OAAa;IACX,MAAM,OAAO,SAAS,iBAAiB,IAAI,KAAQ;KAAE;KAAG,GAAG,CAAA;IAAG,CAAC,CAAC;IAChE,KAAK,UAAU;KACb,GAAG;KACH,GAAG,OAAO,IAAI,CAAC;KACf,QAAQ,OAAY,aAAqB,OAAO,SAAS,EAAE;KAC3D,YAAY,OAAY,aAAqB,OAAO,SAAS,EAAE;KAC/D,SAAS,QAAA,SAAW;IACtB,CAAC;IACD,OAAO,CAAC,IAAI;GACd;GAEA,OAAO,OAAO,KAAK,SAAS;IAC1B,MAAM,OAAO,SAAS,QAAQ,KAAK,aAAa,IAAI,KAAQ;KAAE;KAAG,SAAS;IAAE,CAAC,CAAC;IAC9E,KAAK,UAAU;KACb,GAAG;KACH,GAAG,EAAE,IAAI;KACT,OAAO,KAAK;KACZ,WAAW,KAAK;KAChB,SAAS,QAAA,SAAW;IACtB,CAAC;IACD,OAAO;GACT,CAAC;EACH;CACF,EAAC;CACD,aAAOC,YAAA,SAAAC,cAAmB,MAAI;EAAA,iBAAA,aAAA;EAAA,0BAAA,OAAA,OAAA,OAAA,MAAA,WAAA,aAAA,QAAA;EAAA,QAAA,KAAA;CAAA,CAAA,GAAA,IAAA;AAChC,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
package/dist/charts/BarChart.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { XYChart } from "./XYChart.js";
|
|
2
|
-
import { computed, createVNode
|
|
3
|
-
import { _component, bindable, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
2
|
+
import { computed, createVNode } from "vue";
|
|
3
|
+
import { _component, bindable, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
4
4
|
import { GroupedBar, StackedBar } from "@unovis/ts";
|
|
5
5
|
//#region src/charts/BarChart.tsx
|
|
6
6
|
/**
|
|
@@ -67,7 +67,7 @@ var BarChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
67
67
|
return [bars];
|
|
68
68
|
}
|
|
69
69
|
}));
|
|
70
|
-
return () => createVNode(XYChart,
|
|
70
|
+
return () => createVNode(XYChart, mergeJsxProps(rest, {
|
|
71
71
|
"_hiddenSeries": hiddenSeries.value,
|
|
72
72
|
"onUpdate:_hiddenSeries": _cache[0] || (_cache[0] = ($event) => hiddenSeries.value = $event),
|
|
73
73
|
"kind": kind.value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BarChart.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_createVNode","_mergeProps"],"sources":["../../src/charts/BarChart.tsx"],"sourcesContent":["/**\n * 柱状图:每个类目一组柱子,系列并排(分组)或叠成一根(堆叠),可以横向;stacked=\"percent\" 时每根叠满 100%(看构成)。\n *\n * <BarChart\n * aria-label=\"各项目销售额\"\n * data={projects}\n * index=\"name\"\n * series={[{ key: 'planned', label: '预期销售' }, { key: 'actual', label: '实际销售' }]}\n * unit=\"万元\"\n * />\n *\n * 颜色、坐标轴、提示框、图例、读屏的数据表见 XYChart;悬停一个类目时提示框列出这一组的全部系列。\n */\nimport { GroupedBar, StackedBar } from '@unovis/ts';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { XYChart, type XYChartKind, type XYChartProps } from './XYChart';\n\n/** 柱子的方向 */\nexport interface BarChartOrientations {\n /** 竖向的柱子(默认) */\n vertical: unknown;\n /** 横向的条:类目名较长、类目较多时 */\n horizontal: unknown;\n}\n\n/** 未声明的属性落到外层 div(role=\"figure\")上;aria-label 是图表的名称 */\nexport interface BarChartProps<T extends object> extends XYChartProps<T> {\n /** 堆叠:true 同一类目的系列叠成一根柱子,'percent' 按占比叠满 100%(数值轴是百分比);默认并排 */\n stacked?: boolean | 'percent';\n /** 方向,默认 vertical */\n orientation?: keyof BarChartOrientations;\n /** 柱子末端的圆角(px),默认 4 */\n radius?: number;\n}\n\n/** 柱状图:分组或堆叠,竖向或横向 */\nexport const BarChart = _component(\n <T extends object>({\n stacked = false,\n orientation = 'vertical',\n radius = 4,\n _hiddenSeries: hiddenSeries = _bindable<string[]>([]),\n ...rest\n }: BarChartProps<T>) => {\n const kind = _derived<XYChartKind<T>>({\n horizontal: orientation === 'horizontal',\n skeleton: 'bars',\n stacked: stacked !== false,\n percent: stacked === 'percent',\n itemSelector: stacked ? StackedBar.selectors.bar : GroupedBar.selectors.bar,\n components: ({ series, x, y, instance, events }) => {\n const common = {\n x,\n y: series.map(y),\n color: (_row: T, position: number) => series[position]?.color,\n roundedCorners: radius,\n orientation,\n events,\n };\n if (stacked) {\n const bars = instance('stacked', () => new StackedBar<T>({ x, y: [] }));\n bars.setConfig({ ...common, barPadding: 0.4, barMaxWidth: 48 });\n return [bars];\n }\n const bars = instance('grouped', () => new GroupedBar<T>({ x, y: [] }));\n // 空值不画柱子(Unovis 默认给 2px 的最小高度,空值也会画出一条线)\n bars.setConfig({\n ...common,\n groupPadding: 0.3,\n barPadding: 0.1,\n groupMaxWidth: 96,\n barMinHeight: 0,\n });\n return [bars];\n },\n });\n return () => <XYChart {...rest} _hiddenSeries={hiddenSeries} kind={kind} />;\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoCA,IAAa,WAAW,0BACtB,QAAA,SAMwB;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,UAAAA,aAAA,QAAA,WALZ,KAAK;CAAA,MAAA,cAAAA,aAAA,QAAA,eACD,UAAU;CAAA,MAAA,SAAAA,aAAA,QAAA,UACf,CAAC;CAAA,MAAA,eAAAC,SAAA,QAAA,uBACwC,CAAA,GAAE,KAAA,IAAA;CAAA,MACjD,OAAIC,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,OAAIC,gBAA4B;EACpC,YAAY,YAAA,UAAgB;EAC5B,UAAU;EACV,SAAS,QAAA,UAAY;EACrB,SAAS,QAAA,UAAY;EACrB,cAAc,QAAA,QAAU,WAAW,UAAU,MAAM,WAAW,UAAU;EACxE,aAAa,EAAE,QAAQ,GAAG,GAAG,UAAU,aAAa;GAClD,MAAM,SAAS;IACb;IACA,GAAG,OAAO,IAAI,CAAC;IACf,QAAQ,MAAS,aAAqB,OAAO,SAAS,EAAE;IACxD,gBAAc,OAAA;IACd,aAAW,YAAA;IACX;GACF;GACA,IAAA,QAAA,OAAa;IACX,MAAM,OAAO,SAAS,iBAAiB,IAAI,WAAc;KAAE;KAAG,GAAG,CAAA;IAAG,CAAC,CAAC;IACtE,KAAK,UAAU;KAAE,GAAG;KAAQ,YAAY;KAAK,aAAa;IAAG,CAAC;IAC9D,OAAO,CAAC,IAAI;GACd;GACA,MAAM,OAAO,SAAS,iBAAiB,IAAI,WAAc;IAAE;IAAG,GAAG,CAAA;GAAG,CAAC,CAAC;GAEtE,KAAK,UAAU;IACb,GAAG;IACH,cAAc;IACd,YAAY;IACZ,eAAe;IACf,cAAc;GAChB,CAAC;GACD,OAAO,CAAC,IAAI;EACd;CACF,EAAC;CACD,aAAOC,YAAA,SAAAC,
|
|
1
|
+
{"version":3,"file":"BarChart.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_createVNode","_mergeProps"],"sources":["../../src/charts/BarChart.tsx"],"sourcesContent":["/**\n * 柱状图:每个类目一组柱子,系列并排(分组)或叠成一根(堆叠),可以横向;stacked=\"percent\" 时每根叠满 100%(看构成)。\n *\n * <BarChart\n * aria-label=\"各项目销售额\"\n * data={projects}\n * index=\"name\"\n * series={[{ key: 'planned', label: '预期销售' }, { key: 'actual', label: '实际销售' }]}\n * unit=\"万元\"\n * />\n *\n * 颜色、坐标轴、提示框、图例、读屏的数据表见 XYChart;悬停一个类目时提示框列出这一组的全部系列。\n */\nimport { GroupedBar, StackedBar } from '@unovis/ts';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { XYChart, type XYChartKind, type XYChartProps } from './XYChart';\n\n/** 柱子的方向 */\nexport interface BarChartOrientations {\n /** 竖向的柱子(默认) */\n vertical: unknown;\n /** 横向的条:类目名较长、类目较多时 */\n horizontal: unknown;\n}\n\n/** 未声明的属性落到外层 div(role=\"figure\")上;aria-label 是图表的名称 */\nexport interface BarChartProps<T extends object> extends XYChartProps<T> {\n /** 堆叠:true 同一类目的系列叠成一根柱子,'percent' 按占比叠满 100%(数值轴是百分比);默认并排 */\n stacked?: boolean | 'percent';\n /** 方向,默认 vertical */\n orientation?: keyof BarChartOrientations;\n /** 柱子末端的圆角(px),默认 4 */\n radius?: number;\n}\n\n/** 柱状图:分组或堆叠,竖向或横向 */\nexport const BarChart = _component(\n <T extends object>({\n stacked = false,\n orientation = 'vertical',\n radius = 4,\n _hiddenSeries: hiddenSeries = _bindable<string[]>([]),\n ...rest\n }: BarChartProps<T>) => {\n const kind = _derived<XYChartKind<T>>({\n horizontal: orientation === 'horizontal',\n skeleton: 'bars',\n stacked: stacked !== false,\n percent: stacked === 'percent',\n itemSelector: stacked ? StackedBar.selectors.bar : GroupedBar.selectors.bar,\n components: ({ series, x, y, instance, events }) => {\n const common = {\n x,\n y: series.map(y),\n color: (_row: T, position: number) => series[position]?.color,\n roundedCorners: radius,\n orientation,\n events,\n };\n if (stacked) {\n const bars = instance('stacked', () => new StackedBar<T>({ x, y: [] }));\n bars.setConfig({ ...common, barPadding: 0.4, barMaxWidth: 48 });\n return [bars];\n }\n const bars = instance('grouped', () => new GroupedBar<T>({ x, y: [] }));\n // 空值不画柱子(Unovis 默认给 2px 的最小高度,空值也会画出一条线)\n bars.setConfig({\n ...common,\n groupPadding: 0.3,\n barPadding: 0.1,\n groupMaxWidth: 96,\n barMinHeight: 0,\n });\n return [bars];\n },\n });\n return () => <XYChart {...rest} _hiddenSeries={hiddenSeries} kind={kind} />;\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoCA,IAAa,WAAW,0BACtB,QAAA,SAMwB;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,UAAAA,aAAA,QAAA,WALZ,KAAK;CAAA,MAAA,cAAAA,aAAA,QAAA,eACD,UAAU;CAAA,MAAA,SAAAA,aAAA,QAAA,UACf,CAAC;CAAA,MAAA,eAAAC,SAAA,QAAA,uBACwC,CAAA,GAAE,KAAA,IAAA;CAAA,MACjD,OAAIC,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,OAAIC,gBAA4B;EACpC,YAAY,YAAA,UAAgB;EAC5B,UAAU;EACV,SAAS,QAAA,UAAY;EACrB,SAAS,QAAA,UAAY;EACrB,cAAc,QAAA,QAAU,WAAW,UAAU,MAAM,WAAW,UAAU;EACxE,aAAa,EAAE,QAAQ,GAAG,GAAG,UAAU,aAAa;GAClD,MAAM,SAAS;IACb;IACA,GAAG,OAAO,IAAI,CAAC;IACf,QAAQ,MAAS,aAAqB,OAAO,SAAS,EAAE;IACxD,gBAAc,OAAA;IACd,aAAW,YAAA;IACX;GACF;GACA,IAAA,QAAA,OAAa;IACX,MAAM,OAAO,SAAS,iBAAiB,IAAI,WAAc;KAAE;KAAG,GAAG,CAAA;IAAG,CAAC,CAAC;IACtE,KAAK,UAAU;KAAE,GAAG;KAAQ,YAAY;KAAK,aAAa;IAAG,CAAC;IAC9D,OAAO,CAAC,IAAI;GACd;GACA,MAAM,OAAO,SAAS,iBAAiB,IAAI,WAAc;IAAE;IAAG,GAAG,CAAA;GAAG,CAAC,CAAC;GAEtE,KAAK,UAAU;IACb,GAAG;IACH,cAAc;IACd,YAAY;IACZ,eAAe;IACf,cAAc;GAChB,CAAC;GACD,OAAO,CAAC,IAAI;EACd;CACF,EAAC;CACD,aAAOC,YAAA,SAAAC,cAAmB,MAAI;EAAA,iBAAA,aAAA;EAAA,0BAAA,OAAA,OAAA,OAAA,MAAA,WAAA,aAAA,QAAA;EAAA,QAAA,KAAA;CAAA,CAAA,GAAA,IAAA;AAChC,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
|
@@ -5,8 +5,8 @@ import { Skeleton } from "../feedback/Skeleton.js";
|
|
|
5
5
|
import { ChartFrame, chartTooltip, useChartValueText } from "./frame.js";
|
|
6
6
|
import { flowData, resolveFlowNodes } from "./flow.js";
|
|
7
7
|
import { useSingleChart } from "./single.js";
|
|
8
|
-
import { computed, createVNode,
|
|
9
|
-
import { _component, bindable, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
8
|
+
import { computed, createVNode, shallowRef } from "vue";
|
|
9
|
+
import { _component, bindable, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
10
10
|
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
11
11
|
import { ChordDiagram, ChordLabelAlignment } from "@unovis/ts";
|
|
12
12
|
//#region src/charts/ChordChart.tsx
|
|
@@ -102,7 +102,7 @@ var ChordChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
104
|
}));
|
|
105
|
-
return () => createVNode(ChartFrame,
|
|
105
|
+
return () => createVNode(ChartFrame, mergeJsxProps(rest, {
|
|
106
106
|
"sx": [chordChartStyles.root, sx.value],
|
|
107
107
|
"unit": unit.value,
|
|
108
108
|
"height": height.value,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChordChart.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_vue_shallowRef","_createVNode","_mergeProps"],"sources":["../../src/charts/ChordChart.tsx"],"sourcesContent":["/**\n * 弦图:节点排成一圈,两两之间的流量画成弦,看几方之间相互往来的多少(部门协作、地区间人口流动、贸易往来)。\n *\n * <ChordChart aria-label=\"部门协作\" nodes={departments} links={collaborations} />\n *\n * 数据同 SankeyChart(节点 { id, label?, color? },流向 { source, target, value });弦取起点节点的颜色。\n * 悬停一段弧出这个节点的总量,悬停一根弦出“起点 → 终点”的流量;点图例隐藏一个节点,连同它的弦。\n * 单位、加载与空状态、给读屏的数据表(每条流向一行)同其他图表(见 ChartFrame)。\n */\nimport { ChordDiagram, ChordLabelAlignment } from '@unovis/ts';\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 { useLang } from '../config/context';\nimport { useFormat, type NumericFormat } from '../config/lang/format';\nimport { colors, fontSizes } from '../config/theme/tokens';\nimport { Skeleton } from '../feedback/Skeleton';\nimport type { NumericValue } from '../utils/data/numeric';\nimport type { PartProps } from '../utils/props';\nimport { flowData, resolveFlowNodes, type FlowLink, type FlowNode } from './flow';\nimport { ChartFrame, chartTooltip, useChartValueText, type ChartFrameParts } from './frame';\nimport { useSingleChart, type SingleChart } from './single';\n\n/** 未声明的属性落到外层 div(role=\"figure\")上,aria-label 是图表的名称 */\nexport interface ChordChartProps extends PartProps<'div', 'role' | 'aria-busy'>, ChartFrameParts {\n /** 节点 */\n nodes: readonly FlowNode[];\n /** 流向 */\n links: readonly FlowLink[];\n /** 数值的格式(可缩放,如以万元为单位):提示框、数据表共用 */\n valueFormat?: NumericFormat;\n /** 单位:显示在左上方,数值后面带上 */\n unit?: string;\n /**\n * 绘图区的高度;数字按 px\n * @default 320\n */\n height?: number | string;\n /**\n * 图例:点一下隐藏节点;关掉时节点名写在弧的外侧\n * @default true\n */\n legend?: boolean;\n /**\n * 悬停时的提示框\n * @default true\n */\n tooltip?: boolean;\n /**\n * 加载中:绘图区显示一个圆形的骨架,带 aria-busy\n * @default false\n */\n loading?: boolean;\n /** 隐藏的节点(存 id):点图例时写回,父级可以写 _hiddenNodes={ids} */\n _hiddenNodes?: string[];\n /** 没有数据时显示的内容,默认是“暂无数据” */\n empty?: VNodeChild;\n}\n\n/** ChordChart 的样式:root(--vis-chord-diagram-* 主题变量)、skeleton */\nexport const chordChartStyles = css.create({\n root: {\n '--vis-chord-diagram-link-opacity': '0.45',\n '--vis-chord-diagram-link-stroke-color': colors.surface,\n '--vis-chord-diagram-link-stroke-opacity': '0.6',\n '--vis-chord-diagram-link-highlighted-opacity': '0.85',\n '--vis-chord-diagram-link-dimmed-opacity': '0.12',\n '--vis-chord-diagram-node-dimmed-opacity': '0.3',\n '--vis-chord-diagram-label-text-font-size': fontSizes.xs,\n },\n skeleton: {\n display: 'block',\n height: '100%',\n aspectRatio: '1',\n marginInline: 'auto',\n borderRadius: '50%',\n },\n});\n\n/** 交给 Unovis 的节点、流向 */\ntype Data = ReturnType<typeof flowData>;\ntype Node = Data['nodes'][number];\ntype Link = Data['links'][number];\n\n/** 弦图 */\nexport const ChordChart = _component(\n ({\n nodes,\n links,\n valueFormat,\n unit,\n height = 320,\n legend = true,\n tooltip = true,\n loading = false,\n _hiddenNodes: hiddenNodes = _bindable<string[]>([]),\n empty,\n sx,\n ...rest\n }: ChordChartProps) => {\n const lang = useLang();\n const format = useFormat();\n const resolved = _derived(resolveFlowNodes(nodes));\n const visible = _derived(new Map([...resolved].filter(([id]) => !hiddenNodes.includes(id))));\n const valueText = useChartValueText(() => ({ valueFormat, unit }));\n const labelOf = (id: string) => resolved.get(id)?.label ?? id;\n\n function toggle(id: string) {\n hiddenNodes = hiddenNodes.includes(id)\n ? hiddenNodes.filter((item) => item !== id)\n : [...hiddenNodes, id];\n }\n\n let plot = _state.raw<HTMLElement | null>(null);\n const chart: SingleChart = useSingleChart<{ nodes: Node[]; links?: Link[] }>(() => ({\n element: plot,\n ready: !loading && nodes.length > 0,\n tooltip,\n build: (instance) => {\n const chord = instance(\n 'chord',\n () => new ChordDiagram<Node, Link>({ linkValue: (link) => link.amount }),\n );\n chord.setConfig({\n linkValue: (link) => link.amount,\n nodeColor: (node) => (node as Node).color,\n linkColor: (link) => (link.source as Node).color,\n // 有图例时不在弧旁写名字:径向排的中文在上下两侧是竖着的,不好读\n nodeLabel: (node) => (legend ? '' : (node as Node).label),\n padAngle: 0.04,\n nodeWidth: 14,\n nodeLabelAlignment: ChordLabelAlignment.Perpendicular,\n nodeLabelColor: () => colors.textMuted,\n });\n return {\n component: chord,\n data: flowData(visible, links),\n triggers: {\n [ChordDiagram.selectors.node]: (node: { data: Node; _state: { value?: number } }) =>\n chart.tooltip(\n chartTooltip(node.data.label, [\n {\n label: lang.chartTotal,\n color: node.data.color,\n value: valueText(node._state.value),\n },\n ]),\n ),\n [ChordDiagram.selectors.link]: (ribbon: {\n data: { source: Node; target: Node; value: NumericValue };\n }) =>\n chart.tooltip(\n chartTooltip(\n lang.chartFlowLabel(ribbon.data.source.label, ribbon.data.target.label),\n [\n {\n label: lang.chartValue,\n color: ribbon.data.source.color,\n value: valueText(ribbon.data.value),\n },\n ],\n ),\n ),\n },\n };\n },\n }));\n\n return () => (\n <ChartFrame\n {...rest}\n sx={[chordChartStyles.root, sx]}\n unit={unit}\n height={height}\n legend={\n legend\n ? [...resolved.values()].map((node) => ({\n key: node.id,\n label: node.label,\n color: node.color,\n }))\n : []\n }\n hiddenKeys={hiddenNodes}\n onLegendToggle={toggle}\n loading={loading}\n skeleton={<Skeleton sx={chordChartStyles.skeleton} />}\n isEmpty={nodes.length === 0}\n empty={empty}\n table={{\n columns: [lang.chartFlow, unit ? `${lang.chartValue}(${unit})` : lang.chartValue],\n rows: links\n .filter((link) => visible.has(link.source) && visible.has(link.target))\n .map((link) => ({\n header: lang.chartFlowLabel(labelOf(link.source), labelOf(link.target)),\n cells: [format.number(link.value, valueFormat)],\n })),\n }}\n plotRef={(element) => (plot = element)}\n />\n );\n },\n);\n"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ChordChart.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_vue_shallowRef","_createVNode","_mergeProps"],"sources":["../../src/charts/ChordChart.tsx"],"sourcesContent":["/**\n * 弦图:节点排成一圈,两两之间的流量画成弦,看几方之间相互往来的多少(部门协作、地区间人口流动、贸易往来)。\n *\n * <ChordChart aria-label=\"部门协作\" nodes={departments} links={collaborations} />\n *\n * 数据同 SankeyChart(节点 { id, label?, color? },流向 { source, target, value });弦取起点节点的颜色。\n * 悬停一段弧出这个节点的总量,悬停一根弦出“起点 → 终点”的流量;点图例隐藏一个节点,连同它的弦。\n * 单位、加载与空状态、给读屏的数据表(每条流向一行)同其他图表(见 ChartFrame)。\n */\nimport { ChordDiagram, ChordLabelAlignment } from '@unovis/ts';\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 { useLang } from '../config/context';\nimport { useFormat, type NumericFormat } from '../config/lang/format';\nimport { colors, fontSizes } from '../config/theme/tokens';\nimport { Skeleton } from '../feedback/Skeleton';\nimport type { NumericValue } from '../utils/data/numeric';\nimport type { PartProps } from '../utils/props';\nimport { flowData, resolveFlowNodes, type FlowLink, type FlowNode } from './flow';\nimport { ChartFrame, chartTooltip, useChartValueText, type ChartFrameParts } from './frame';\nimport { useSingleChart, type SingleChart } from './single';\n\n/** 未声明的属性落到外层 div(role=\"figure\")上,aria-label 是图表的名称 */\nexport interface ChordChartProps extends PartProps<'div', 'role' | 'aria-busy'>, ChartFrameParts {\n /** 节点 */\n nodes: readonly FlowNode[];\n /** 流向 */\n links: readonly FlowLink[];\n /** 数值的格式(可缩放,如以万元为单位):提示框、数据表共用 */\n valueFormat?: NumericFormat;\n /** 单位:显示在左上方,数值后面带上 */\n unit?: string;\n /**\n * 绘图区的高度;数字按 px\n * @default 320\n */\n height?: number | string;\n /**\n * 图例:点一下隐藏节点;关掉时节点名写在弧的外侧\n * @default true\n */\n legend?: boolean;\n /**\n * 悬停时的提示框\n * @default true\n */\n tooltip?: boolean;\n /**\n * 加载中:绘图区显示一个圆形的骨架,带 aria-busy\n * @default false\n */\n loading?: boolean;\n /** 隐藏的节点(存 id):点图例时写回,父级可以写 _hiddenNodes={ids} */\n _hiddenNodes?: string[];\n /** 没有数据时显示的内容,默认是“暂无数据” */\n empty?: VNodeChild;\n}\n\n/** ChordChart 的样式:root(--vis-chord-diagram-* 主题变量)、skeleton */\nexport const chordChartStyles = css.create({\n root: {\n '--vis-chord-diagram-link-opacity': '0.45',\n '--vis-chord-diagram-link-stroke-color': colors.surface,\n '--vis-chord-diagram-link-stroke-opacity': '0.6',\n '--vis-chord-diagram-link-highlighted-opacity': '0.85',\n '--vis-chord-diagram-link-dimmed-opacity': '0.12',\n '--vis-chord-diagram-node-dimmed-opacity': '0.3',\n '--vis-chord-diagram-label-text-font-size': fontSizes.xs,\n },\n skeleton: {\n display: 'block',\n height: '100%',\n aspectRatio: '1',\n marginInline: 'auto',\n borderRadius: '50%',\n },\n});\n\n/** 交给 Unovis 的节点、流向 */\ntype Data = ReturnType<typeof flowData>;\ntype Node = Data['nodes'][number];\ntype Link = Data['links'][number];\n\n/** 弦图 */\nexport const ChordChart = _component(\n ({\n nodes,\n links,\n valueFormat,\n unit,\n height = 320,\n legend = true,\n tooltip = true,\n loading = false,\n _hiddenNodes: hiddenNodes = _bindable<string[]>([]),\n empty,\n sx,\n ...rest\n }: ChordChartProps) => {\n const lang = useLang();\n const format = useFormat();\n const resolved = _derived(resolveFlowNodes(nodes));\n const visible = _derived(new Map([...resolved].filter(([id]) => !hiddenNodes.includes(id))));\n const valueText = useChartValueText(() => ({ valueFormat, unit }));\n const labelOf = (id: string) => resolved.get(id)?.label ?? id;\n\n function toggle(id: string) {\n hiddenNodes = hiddenNodes.includes(id)\n ? hiddenNodes.filter((item) => item !== id)\n : [...hiddenNodes, id];\n }\n\n let plot = _state.raw<HTMLElement | null>(null);\n const chart: SingleChart = useSingleChart<{ nodes: Node[]; links?: Link[] }>(() => ({\n element: plot,\n ready: !loading && nodes.length > 0,\n tooltip,\n build: (instance) => {\n const chord = instance(\n 'chord',\n () => new ChordDiagram<Node, Link>({ linkValue: (link) => link.amount }),\n );\n chord.setConfig({\n linkValue: (link) => link.amount,\n nodeColor: (node) => (node as Node).color,\n linkColor: (link) => (link.source as Node).color,\n // 有图例时不在弧旁写名字:径向排的中文在上下两侧是竖着的,不好读\n nodeLabel: (node) => (legend ? '' : (node as Node).label),\n padAngle: 0.04,\n nodeWidth: 14,\n nodeLabelAlignment: ChordLabelAlignment.Perpendicular,\n nodeLabelColor: () => colors.textMuted,\n });\n return {\n component: chord,\n data: flowData(visible, links),\n triggers: {\n [ChordDiagram.selectors.node]: (node: { data: Node; _state: { value?: number } }) =>\n chart.tooltip(\n chartTooltip(node.data.label, [\n {\n label: lang.chartTotal,\n color: node.data.color,\n value: valueText(node._state.value),\n },\n ]),\n ),\n [ChordDiagram.selectors.link]: (ribbon: {\n data: { source: Node; target: Node; value: NumericValue };\n }) =>\n chart.tooltip(\n chartTooltip(\n lang.chartFlowLabel(ribbon.data.source.label, ribbon.data.target.label),\n [\n {\n label: lang.chartValue,\n color: ribbon.data.source.color,\n value: valueText(ribbon.data.value),\n },\n ],\n ),\n ),\n },\n };\n },\n }));\n\n return () => (\n <ChartFrame\n {...rest}\n sx={[chordChartStyles.root, sx]}\n unit={unit}\n height={height}\n legend={\n legend\n ? [...resolved.values()].map((node) => ({\n key: node.id,\n label: node.label,\n color: node.color,\n }))\n : []\n }\n hiddenKeys={hiddenNodes}\n onLegendToggle={toggle}\n loading={loading}\n skeleton={<Skeleton sx={chordChartStyles.skeleton} />}\n isEmpty={nodes.length === 0}\n empty={empty}\n table={{\n columns: [lang.chartFlow, unit ? `${lang.chartValue}(${unit})` : lang.chartValue],\n rows: links\n .filter((link) => visible.has(link.source) && visible.has(link.target))\n .map((link) => ({\n header: lang.chartFlowLabel(labelOf(link.source), labelOf(link.target)),\n cells: [format.number(link.value, valueFormat)],\n })),\n }}\n plotRef={(element) => (plot = element)}\n />\n );\n },\n);\n"],"mappings":"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"}
|
|
@@ -2,8 +2,8 @@ import { colors } from "../config/theme/tokens.js";
|
|
|
2
2
|
import { seriesNumber } from "./series.js";
|
|
3
3
|
import { XYChart } from "./XYChart.js";
|
|
4
4
|
import { CURVES } from "./LineChart.js";
|
|
5
|
-
import { computed, createVNode
|
|
6
|
-
import { _component, bindable, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
5
|
+
import { computed, createVNode } from "vue";
|
|
6
|
+
import { _component, bindable, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
7
7
|
import { GroupedBar, Line, Scatter, StackedBar } from "@unovis/ts";
|
|
8
8
|
//#region src/charts/ComboChart.tsx
|
|
9
9
|
/**
|
|
@@ -131,7 +131,7 @@ var ComboChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
131
131
|
return components;
|
|
132
132
|
}
|
|
133
133
|
}));
|
|
134
|
-
return () => createVNode(XYChart,
|
|
134
|
+
return () => createVNode(XYChart, mergeJsxProps(rest, {
|
|
135
135
|
"series": series.value,
|
|
136
136
|
"_hiddenSeries": hiddenSeries.value,
|
|
137
137
|
"onUpdate:_hiddenSeries": _cache[0] || (_cache[0] = ($event) => hiddenSeries.value = $event),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboChart.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_createVNode","_mergeProps"],"sources":["../../src/charts/ComboChart.tsx"],"sourcesContent":["/**\n * 组合图:同一组类目上既有柱又有线(金额与比率、数量与单价),单位不同的系列画在右侧的第二数值轴上。\n *\n * <ComboChart\n * aria-label=\"月度销售与完成率\"\n * data={months}\n * index=\"month\"\n * series={[\n * { key: 'planned', label: '计划销售' },\n * { key: 'actual', label: '实际销售' },\n * { key: 'rate', label: '完成率', type: 'line', axis: 'secondary' },\n * ]}\n * unit=\"万元\"\n * secondaryFormat={{ style: 'percent' }}\n * />\n *\n * 柱子并排(stacked 时叠起),线可以带数据点。第二数值轴的刻度与主轴对齐(同一组网格线),\n * 它的单位写在右上方,提示框、数据表里按各自的格式与单位显示。其余(图例、目标线、标注、区间、下钻)同 XYChart。\n */\nimport { GroupedBar, Line, Scatter, StackedBar, type XYComponentCore } from '@unovis/ts';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport type { NumericFormat } from '../config/lang/format';\nimport { colors } from '../config/theme/tokens';\nimport { CURVES, type LineCurves, type LineDots } from './LineChart';\nimport type { ChartSeries } from './series';\nimport { seriesNumber } from './series';\nimport { XYChart, type XYChartKind, type XYChartProps } from './XYChart';\n\n/** 组合图的一个系列 */\nexport interface ComboSeries<T> extends ChartSeries<T> {\n /** 画法:bar 柱(默认)、line 线 */\n type?: 'bar' | 'line';\n /** 数值轴:primary 左侧的主轴(默认),secondary 右侧的第二数值轴(单位不同的量) */\n axis?: 'primary' | 'secondary';\n}\n\n/** 未声明的属性落到外层 div(role=\"figure\")上;aria-label 是图表的名称 */\nexport interface ComboChartProps<T extends object> extends Omit<XYChartProps<T>, 'series'> {\n /** 系列:柱或线,画在主轴或第二数值轴上 */\n series: readonly ComboSeries<T>[];\n /** 第二数值轴的格式(比率写 `{ style: 'percent' }`) */\n secondaryFormat?: NumericFormat;\n /** 第二数值轴的单位:显示在右上方 */\n secondaryUnit?: string;\n /** 柱子堆叠:同一类目的柱叠成一根;默认并排 */\n stacked?: boolean;\n /** 线的形状,默认 linear */\n curve?: keyof LineCurves;\n /** 线上的数据点,默认 filled */\n dots?: keyof LineDots;\n /** 柱子末端的圆角(px),默认 4 */\n radius?: number;\n}\n\n/** 数据点的直径(px) */\nconst DOT_SIZE = 8;\n\n/** 组合图:柱 + 线,可以有第二数值轴 */\nexport const ComboChart = _component(\n <T extends object>({\n series,\n secondaryFormat,\n secondaryUnit,\n stacked = false,\n curve = 'linear',\n dots = 'filled',\n radius = 4,\n _hiddenSeries: hiddenSeries = _bindable<string[]>([]),\n ...rest\n }: ComboChartProps<T>) => {\n const isLine = (key: string) => series.find((item) => item.key === key)?.type === 'line';\n const secondaryKeys = _derived(\n series.filter((item) => item.axis === 'secondary').map((item) => item.key as string),\n );\n\n const kind = _derived<XYChartKind<T>>({\n skeleton: 'bars',\n stacked,\n crosshairDots: true,\n // 十字线只在线上画圆点,柱子不画\n crosshairSeries: (item) => isLine(item.key),\n itemSelector: stacked ? StackedBar.selectors.bar : GroupedBar.selectors.bar,\n secondary:\n secondaryKeys.length > 0\n ? { keys: secondaryKeys, format: secondaryFormat, unit: secondaryUnit }\n : undefined,\n // 主轴的范围:堆叠的柱按正负各自的总和,线按各自的值\n domainValues: (rows, shown) =>\n rows.flatMap((row) => {\n const bars = shown.filter((item) => !isLine(item.key));\n const lines = shown.filter((item) => isLine(item.key));\n const values = (list: typeof shown) =>\n list.map((item) => seriesNumber(row, item.key) ?? 0);\n const barValues = values(bars);\n return [\n ...(stacked\n ? [\n barValues.reduce((sum, value) => sum + Math.max(value, 0), 0),\n barValues.reduce((sum, value) => sum + Math.min(value, 0), 0),\n ]\n : barValues),\n ...values(lines),\n ];\n }),\n components: ({ series: shown, x, y, instance, events }) => {\n const bars = shown.filter((item) => !isLine(item.key));\n const lines = shown.filter((item) => isLine(item.key));\n const components: XYComponentCore<T>[] = [];\n if (bars.length > 0) {\n const common = {\n x,\n y: bars.map(y),\n color: (_row: T, position: number) => bars[position]?.color,\n roundedCorners: radius,\n events,\n };\n if (stacked) {\n const stack = instance('stacked', () => new StackedBar<T>({ x, y: [] }));\n stack.setConfig({ ...common, barPadding: 0.4, barMaxWidth: 48 });\n components.push(stack);\n } else {\n const group = instance('grouped', () => new GroupedBar<T>({ x, y: [] }));\n group.setConfig({\n ...common,\n groupPadding: 0.3,\n barPadding: 0.1,\n groupMaxWidth: 96,\n barMinHeight: 0,\n });\n components.push(group);\n }\n }\n if (lines.length > 0) {\n const line = instance('line', () => new Line<T>({ x, y: [] }));\n line.setConfig({\n x,\n y: lines.map(y),\n color: (_rows: T[], position: number) => lines[position]?.color,\n curveType: CURVES[curve],\n lineWidth: 2,\n });\n components.push(line);\n if (dots !== 'none')\n for (const item of lines) {\n const points = instance(`dots:${item.key}`, () => new Scatter<T>({ x, y: () => 0 }));\n points.setConfig({\n x,\n y: y(item),\n size: DOT_SIZE,\n color: dots === 'hollow' ? colors.surface : item.color,\n strokeColor: item.color,\n strokeWidth: dots === 'hollow' ? 2 : 0,\n });\n components.push(points);\n }\n }\n return components;\n },\n });\n return () => <XYChart {...rest} series={series} _hiddenSeries={hiddenSeries} kind={kind} />;\n },\n);\n"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ComboChart.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_createVNode","_mergeProps"],"sources":["../../src/charts/ComboChart.tsx"],"sourcesContent":["/**\n * 组合图:同一组类目上既有柱又有线(金额与比率、数量与单价),单位不同的系列画在右侧的第二数值轴上。\n *\n * <ComboChart\n * aria-label=\"月度销售与完成率\"\n * data={months}\n * index=\"month\"\n * series={[\n * { key: 'planned', label: '计划销售' },\n * { key: 'actual', label: '实际销售' },\n * { key: 'rate', label: '完成率', type: 'line', axis: 'secondary' },\n * ]}\n * unit=\"万元\"\n * secondaryFormat={{ style: 'percent' }}\n * />\n *\n * 柱子并排(stacked 时叠起),线可以带数据点。第二数值轴的刻度与主轴对齐(同一组网格线),\n * 它的单位写在右上方,提示框、数据表里按各自的格式与单位显示。其余(图例、目标线、标注、区间、下钻)同 XYChart。\n */\nimport { GroupedBar, Line, Scatter, StackedBar, type XYComponentCore } from '@unovis/ts';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport type { NumericFormat } from '../config/lang/format';\nimport { colors } from '../config/theme/tokens';\nimport { CURVES, type LineCurves, type LineDots } from './LineChart';\nimport type { ChartSeries } from './series';\nimport { seriesNumber } from './series';\nimport { XYChart, type XYChartKind, type XYChartProps } from './XYChart';\n\n/** 组合图的一个系列 */\nexport interface ComboSeries<T> extends ChartSeries<T> {\n /** 画法:bar 柱(默认)、line 线 */\n type?: 'bar' | 'line';\n /** 数值轴:primary 左侧的主轴(默认),secondary 右侧的第二数值轴(单位不同的量) */\n axis?: 'primary' | 'secondary';\n}\n\n/** 未声明的属性落到外层 div(role=\"figure\")上;aria-label 是图表的名称 */\nexport interface ComboChartProps<T extends object> extends Omit<XYChartProps<T>, 'series'> {\n /** 系列:柱或线,画在主轴或第二数值轴上 */\n series: readonly ComboSeries<T>[];\n /** 第二数值轴的格式(比率写 `{ style: 'percent' }`) */\n secondaryFormat?: NumericFormat;\n /** 第二数值轴的单位:显示在右上方 */\n secondaryUnit?: string;\n /** 柱子堆叠:同一类目的柱叠成一根;默认并排 */\n stacked?: boolean;\n /** 线的形状,默认 linear */\n curve?: keyof LineCurves;\n /** 线上的数据点,默认 filled */\n dots?: keyof LineDots;\n /** 柱子末端的圆角(px),默认 4 */\n radius?: number;\n}\n\n/** 数据点的直径(px) */\nconst DOT_SIZE = 8;\n\n/** 组合图:柱 + 线,可以有第二数值轴 */\nexport const ComboChart = _component(\n <T extends object>({\n series,\n secondaryFormat,\n secondaryUnit,\n stacked = false,\n curve = 'linear',\n dots = 'filled',\n radius = 4,\n _hiddenSeries: hiddenSeries = _bindable<string[]>([]),\n ...rest\n }: ComboChartProps<T>) => {\n const isLine = (key: string) => series.find((item) => item.key === key)?.type === 'line';\n const secondaryKeys = _derived(\n series.filter((item) => item.axis === 'secondary').map((item) => item.key as string),\n );\n\n const kind = _derived<XYChartKind<T>>({\n skeleton: 'bars',\n stacked,\n crosshairDots: true,\n // 十字线只在线上画圆点,柱子不画\n crosshairSeries: (item) => isLine(item.key),\n itemSelector: stacked ? StackedBar.selectors.bar : GroupedBar.selectors.bar,\n secondary:\n secondaryKeys.length > 0\n ? { keys: secondaryKeys, format: secondaryFormat, unit: secondaryUnit }\n : undefined,\n // 主轴的范围:堆叠的柱按正负各自的总和,线按各自的值\n domainValues: (rows, shown) =>\n rows.flatMap((row) => {\n const bars = shown.filter((item) => !isLine(item.key));\n const lines = shown.filter((item) => isLine(item.key));\n const values = (list: typeof shown) =>\n list.map((item) => seriesNumber(row, item.key) ?? 0);\n const barValues = values(bars);\n return [\n ...(stacked\n ? [\n barValues.reduce((sum, value) => sum + Math.max(value, 0), 0),\n barValues.reduce((sum, value) => sum + Math.min(value, 0), 0),\n ]\n : barValues),\n ...values(lines),\n ];\n }),\n components: ({ series: shown, x, y, instance, events }) => {\n const bars = shown.filter((item) => !isLine(item.key));\n const lines = shown.filter((item) => isLine(item.key));\n const components: XYComponentCore<T>[] = [];\n if (bars.length > 0) {\n const common = {\n x,\n y: bars.map(y),\n color: (_row: T, position: number) => bars[position]?.color,\n roundedCorners: radius,\n events,\n };\n if (stacked) {\n const stack = instance('stacked', () => new StackedBar<T>({ x, y: [] }));\n stack.setConfig({ ...common, barPadding: 0.4, barMaxWidth: 48 });\n components.push(stack);\n } else {\n const group = instance('grouped', () => new GroupedBar<T>({ x, y: [] }));\n group.setConfig({\n ...common,\n groupPadding: 0.3,\n barPadding: 0.1,\n groupMaxWidth: 96,\n barMinHeight: 0,\n });\n components.push(group);\n }\n }\n if (lines.length > 0) {\n const line = instance('line', () => new Line<T>({ x, y: [] }));\n line.setConfig({\n x,\n y: lines.map(y),\n color: (_rows: T[], position: number) => lines[position]?.color,\n curveType: CURVES[curve],\n lineWidth: 2,\n });\n components.push(line);\n if (dots !== 'none')\n for (const item of lines) {\n const points = instance(`dots:${item.key}`, () => new Scatter<T>({ x, y: () => 0 }));\n points.setConfig({\n x,\n y: y(item),\n size: DOT_SIZE,\n color: dots === 'hollow' ? colors.surface : item.color,\n strokeColor: item.color,\n strokeWidth: dots === 'hollow' ? 2 : 0,\n });\n components.push(points);\n }\n }\n return components;\n },\n });\n return () => <XYChart {...rest} series={series} _hiddenSeries={hiddenSeries} kind={kind} />;\n },\n);\n"],"mappings":"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"}
|
|
@@ -5,8 +5,8 @@ import { Skeleton } from "../feedback/Skeleton.js";
|
|
|
5
5
|
import { ChartFrame, chartTooltip, useChartValueText } from "./frame.js";
|
|
6
6
|
import { resolveColor, seriesValue } from "./series.js";
|
|
7
7
|
import { useSingleChart } from "./single.js";
|
|
8
|
-
import { computed, createVNode,
|
|
9
|
-
import { _component, bindable, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
8
|
+
import { computed, createVNode, shallowRef } from "vue";
|
|
9
|
+
import { _component, bindable, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
10
10
|
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
11
11
|
import { Donut } from "@unovis/ts";
|
|
12
12
|
//#region src/charts/DonutChart.tsx
|
|
@@ -106,7 +106,7 @@ var DonutChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
108
|
}));
|
|
109
|
-
return () => createVNode(ChartFrame,
|
|
109
|
+
return () => createVNode(ChartFrame, mergeJsxProps(rest, {
|
|
110
110
|
"unit": unit.value,
|
|
111
111
|
"height": height.value,
|
|
112
112
|
"legend": legend.value ? slices.value.map((slice) => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DonutChart.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_vue_shallowRef","_createVNode","_mergeProps"],"sources":["../../src/charts/DonutChart.tsx"],"sourcesContent":["/**\n * 环形图(arcWidth 为 0 时是饼图):每一行一个扇区,中间写合计;悬停扇区出提示框(数值与占比),点图例隐藏一项。\n *\n * <DonutChart aria-label=\"访问来源\" data={channels} index=\"name\" value=\"visits\" />\n * <DonutChart data={budget} index=\"dept\" value=\"amount\" unit=\"万元\" valueFormat={{ maximumFractionDigits: 0 }} />\n *\n * 颜色默认按顺序取图表色板(itemColor 可以按行给);值可以是 number、bigint、Decimal,提示框与数据表按原值显示。\n * 图例、单位、加载与空状态、给读屏的数据表(类目、数值、占比)同其他图表(见 ChartFrame)。\n */\nimport { Donut } from '@unovis/ts';\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 { useLang } from '../config/context';\nimport { useFormat, type NumericFormat } from '../config/lang/format';\nimport { Skeleton } from '../feedback/Skeleton';\nimport { toNumber, type NumericValue } from '../utils/data/numeric';\nimport type { PartProps } from '../utils/props';\nimport { ChartFrame, chartTooltip, useChartValueText, type ChartFrameParts } from './frame';\nimport { useSingleChart, type SingleChart } from './single';\nimport { resolveColor, seriesValue, type ChartColor, type NumericKey } from './series';\n\n/** 未声明的属性落到外层 div(role=\"figure\")上,aria-label 是图表的名称 */\nexport interface DonutChartProps<T extends object>\n extends PartProps<'div', 'role' | 'aria-busy'>, ChartFrameParts {\n /** 数据:每一行一个扇区 */\n data: readonly T[];\n /** 名称字段:图例、提示框、数据表里显示,也是隐藏一项时记的 key */\n index: keyof T & string;\n /** 数值字段 */\n value: NumericKey<T>;\n /** 每一项的颜色;不写时按顺序取图表色板 */\n itemColor?: (row: T, index: number) => ChartColor | undefined;\n /** 数值的格式(可缩放,如以万元为单位):中间的合计、提示框、数据表共用 */\n valueFormat?: NumericFormat;\n /** 单位:显示在左上方,数值后面带上 */\n unit?: string;\n /**\n * 绘图区的高度;数字按 px\n * @default 240\n */\n height?: number | string;\n /**\n * 环的粗细(px);0 是饼图\n * @default 28\n */\n arcWidth?: number;\n /** 中间的大字;默认是显示中各项的合计,false 时不写 */\n centerLabel?: string | false;\n /** 中间大字下面的小字;默认是“合计” */\n centerSubLabel?: string;\n /**\n * 图例\n * @default true\n */\n legend?: boolean;\n /**\n * 悬停时的提示框\n * @default true\n */\n tooltip?: boolean;\n /**\n * 加载中:绘图区显示一个圆形的骨架,带 aria-busy\n * @default false\n */\n loading?: boolean;\n /** 隐藏的项(存名称):点图例时写回,父级可以写 _hiddenItems={names} */\n _hiddenItems?: string[];\n /** 没有数据时显示的内容,默认是“暂无数据” */\n empty?: VNodeChild;\n /**\n * 点了一个扇区(下钻):参数是这一行与它在 data 里的位置;写了它时指针是手形。\n * 点图是鼠标的捷径:同样的去处在页面上还要有链接或按钮\n */\n onItemClick?: (row: T, index: number) => void;\n}\n\n/** DonutChart 的样式:skeleton */\nexport const donutChartStyles = css.create({\n skeleton: {\n display: 'block',\n height: '100%',\n aspectRatio: '1',\n marginInline: 'auto',\n borderRadius: '50%',\n },\n});\n\n/** 扇区的数据:一行加上它的名字与颜色 */\ninterface Slice<T> {\n row: T;\n name: string;\n color: string;\n value: NumericValue | undefined;\n}\n\n/** 环形图 */\nexport const DonutChart = _component(\n <T extends object>({\n data,\n index,\n value,\n itemColor,\n valueFormat,\n unit,\n height = 240,\n arcWidth = 28,\n centerLabel,\n centerSubLabel,\n legend = true,\n tooltip = true,\n loading = false,\n onItemClick,\n _hiddenItems: hiddenItems = _bindable<string[]>([]),\n empty,\n ...rest\n }: DonutChartProps<T>) => {\n const lang = useLang();\n const format = useFormat();\n\n // 颜色按原来的顺序定:隐藏一项时其余扇区不换色\n const slices = _derived.by((): Slice<T>[] => {\n return data.map((row, position) => ({\n row,\n name: String(row[index] ?? ''),\n color: resolveColor(itemColor?.(row, position), position),\n value: seriesValue(row, value),\n }));\n });\n const visible = _derived(slices.filter((slice) => !hiddenItems.includes(slice.name)));\n const total = _derived(\n visible.reduce(\n (sum, slice) => sum + (slice.value === undefined ? 0 : toNumber(slice.value)),\n 0,\n ),\n );\n\n const valueText = useChartValueText(() => ({ valueFormat, unit }));\n const shareText = (amount: NumericValue | undefined) =>\n amount === undefined || total === 0\n ? lang.emptyValue\n : format.number(toNumber(amount) / total, { style: 'percent', maximumFractionDigits: 1 });\n\n function toggle(name: string) {\n hiddenItems = hiddenItems.includes(name)\n ? hiddenItems.filter((item) => item !== name)\n : [...hiddenItems, name];\n }\n\n let plot = _state.raw<HTMLElement | null>(null);\n const chart: SingleChart = useSingleChart<Slice<T>[]>(() => ({\n element: plot,\n ready: !loading && data.length > 0,\n tooltip,\n build: (instance) => {\n const donut = instance('donut', () => new Donut<Slice<T>>({ value: () => 0 }));\n donut.setConfig({\n value: (slice) => (slice.value === undefined ? 0 : toNumber(slice.value)),\n id: (slice) => slice.name,\n color: (slice) => slice.color,\n arcWidth,\n padAngle: arcWidth > 0 ? 0.01 : 0,\n cornerRadius: arcWidth > 0 ? 3 : 0,\n centralLabel:\n centerLabel === false ? undefined : (centerLabel ?? format.number(total, valueFormat)),\n centralSubLabel: centerLabel === false ? undefined : (centerSubLabel ?? lang.chartTotal),\n showBackground: true,\n events: {\n [Donut.selectors.segment]: {\n click: (arc: { data: Slice<T> }) =>\n onItemClick?.(arc.data.row, data.indexOf(arc.data.row)),\n },\n },\n });\n return {\n component: donut,\n data: [...visible],\n triggers: {\n [Donut.selectors.segment]: (arc: { data: Slice<T> }) =>\n chart.tooltip(\n chartTooltip(arc.data.name, [\n {\n label: lang.chartValue,\n color: arc.data.color,\n value: valueText(arc.data.value),\n },\n {\n label: lang.chartShare,\n color: 'transparent',\n value: shareText(arc.data.value),\n },\n ]),\n ),\n },\n };\n },\n }));\n\n return () => (\n <ChartFrame\n {...rest}\n unit={unit}\n height={height}\n legend={\n legend\n ? slices.map((slice) => ({ key: slice.name, label: slice.name, color: slice.color }))\n : []\n }\n hiddenKeys={hiddenItems}\n onLegendToggle={toggle}\n loading={loading}\n skeleton={<Skeleton sx={donutChartStyles.skeleton} />}\n clickable={onItemClick != null}\n isEmpty={data.length === 0}\n empty={empty}\n table={{\n columns: [\n lang.chartCategory,\n unit ? `${lang.chartValue}(${unit})` : lang.chartValue,\n lang.chartShare,\n ],\n rows: visible.map((slice) => ({\n header: slice.name,\n cells: [\n slice.value === undefined ? lang.emptyValue : format.number(slice.value, valueFormat),\n shareText(slice.value),\n ],\n })),\n }}\n plotRef={(element) => (plot = element)}\n />\n );\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DonutChart.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_vue_shallowRef","_createVNode","_mergeProps"],"sources":["../../src/charts/DonutChart.tsx"],"sourcesContent":["/**\n * 环形图(arcWidth 为 0 时是饼图):每一行一个扇区,中间写合计;悬停扇区出提示框(数值与占比),点图例隐藏一项。\n *\n * <DonutChart aria-label=\"访问来源\" data={channels} index=\"name\" value=\"visits\" />\n * <DonutChart data={budget} index=\"dept\" value=\"amount\" unit=\"万元\" valueFormat={{ maximumFractionDigits: 0 }} />\n *\n * 颜色默认按顺序取图表色板(itemColor 可以按行给);值可以是 number、bigint、Decimal,提示框与数据表按原值显示。\n * 图例、单位、加载与空状态、给读屏的数据表(类目、数值、占比)同其他图表(见 ChartFrame)。\n */\nimport { Donut } from '@unovis/ts';\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 { useLang } from '../config/context';\nimport { useFormat, type NumericFormat } from '../config/lang/format';\nimport { Skeleton } from '../feedback/Skeleton';\nimport { toNumber, type NumericValue } from '../utils/data/numeric';\nimport type { PartProps } from '../utils/props';\nimport { ChartFrame, chartTooltip, useChartValueText, type ChartFrameParts } from './frame';\nimport { useSingleChart, type SingleChart } from './single';\nimport { resolveColor, seriesValue, type ChartColor, type NumericKey } from './series';\n\n/** 未声明的属性落到外层 div(role=\"figure\")上,aria-label 是图表的名称 */\nexport interface DonutChartProps<T extends object>\n extends PartProps<'div', 'role' | 'aria-busy'>, ChartFrameParts {\n /** 数据:每一行一个扇区 */\n data: readonly T[];\n /** 名称字段:图例、提示框、数据表里显示,也是隐藏一项时记的 key */\n index: keyof T & string;\n /** 数值字段 */\n value: NumericKey<T>;\n /** 每一项的颜色;不写时按顺序取图表色板 */\n itemColor?: (row: T, index: number) => ChartColor | undefined;\n /** 数值的格式(可缩放,如以万元为单位):中间的合计、提示框、数据表共用 */\n valueFormat?: NumericFormat;\n /** 单位:显示在左上方,数值后面带上 */\n unit?: string;\n /**\n * 绘图区的高度;数字按 px\n * @default 240\n */\n height?: number | string;\n /**\n * 环的粗细(px);0 是饼图\n * @default 28\n */\n arcWidth?: number;\n /** 中间的大字;默认是显示中各项的合计,false 时不写 */\n centerLabel?: string | false;\n /** 中间大字下面的小字;默认是“合计” */\n centerSubLabel?: string;\n /**\n * 图例\n * @default true\n */\n legend?: boolean;\n /**\n * 悬停时的提示框\n * @default true\n */\n tooltip?: boolean;\n /**\n * 加载中:绘图区显示一个圆形的骨架,带 aria-busy\n * @default false\n */\n loading?: boolean;\n /** 隐藏的项(存名称):点图例时写回,父级可以写 _hiddenItems={names} */\n _hiddenItems?: string[];\n /** 没有数据时显示的内容,默认是“暂无数据” */\n empty?: VNodeChild;\n /**\n * 点了一个扇区(下钻):参数是这一行与它在 data 里的位置;写了它时指针是手形。\n * 点图是鼠标的捷径:同样的去处在页面上还要有链接或按钮\n */\n onItemClick?: (row: T, index: number) => void;\n}\n\n/** DonutChart 的样式:skeleton */\nexport const donutChartStyles = css.create({\n skeleton: {\n display: 'block',\n height: '100%',\n aspectRatio: '1',\n marginInline: 'auto',\n borderRadius: '50%',\n },\n});\n\n/** 扇区的数据:一行加上它的名字与颜色 */\ninterface Slice<T> {\n row: T;\n name: string;\n color: string;\n value: NumericValue | undefined;\n}\n\n/** 环形图 */\nexport const DonutChart = _component(\n <T extends object>({\n data,\n index,\n value,\n itemColor,\n valueFormat,\n unit,\n height = 240,\n arcWidth = 28,\n centerLabel,\n centerSubLabel,\n legend = true,\n tooltip = true,\n loading = false,\n onItemClick,\n _hiddenItems: hiddenItems = _bindable<string[]>([]),\n empty,\n ...rest\n }: DonutChartProps<T>) => {\n const lang = useLang();\n const format = useFormat();\n\n // 颜色按原来的顺序定:隐藏一项时其余扇区不换色\n const slices = _derived.by((): Slice<T>[] => {\n return data.map((row, position) => ({\n row,\n name: String(row[index] ?? ''),\n color: resolveColor(itemColor?.(row, position), position),\n value: seriesValue(row, value),\n }));\n });\n const visible = _derived(slices.filter((slice) => !hiddenItems.includes(slice.name)));\n const total = _derived(\n visible.reduce(\n (sum, slice) => sum + (slice.value === undefined ? 0 : toNumber(slice.value)),\n 0,\n ),\n );\n\n const valueText = useChartValueText(() => ({ valueFormat, unit }));\n const shareText = (amount: NumericValue | undefined) =>\n amount === undefined || total === 0\n ? lang.emptyValue\n : format.number(toNumber(amount) / total, { style: 'percent', maximumFractionDigits: 1 });\n\n function toggle(name: string) {\n hiddenItems = hiddenItems.includes(name)\n ? hiddenItems.filter((item) => item !== name)\n : [...hiddenItems, name];\n }\n\n let plot = _state.raw<HTMLElement | null>(null);\n const chart: SingleChart = useSingleChart<Slice<T>[]>(() => ({\n element: plot,\n ready: !loading && data.length > 0,\n tooltip,\n build: (instance) => {\n const donut = instance('donut', () => new Donut<Slice<T>>({ value: () => 0 }));\n donut.setConfig({\n value: (slice) => (slice.value === undefined ? 0 : toNumber(slice.value)),\n id: (slice) => slice.name,\n color: (slice) => slice.color,\n arcWidth,\n padAngle: arcWidth > 0 ? 0.01 : 0,\n cornerRadius: arcWidth > 0 ? 3 : 0,\n centralLabel:\n centerLabel === false ? undefined : (centerLabel ?? format.number(total, valueFormat)),\n centralSubLabel: centerLabel === false ? undefined : (centerSubLabel ?? lang.chartTotal),\n showBackground: true,\n events: {\n [Donut.selectors.segment]: {\n click: (arc: { data: Slice<T> }) =>\n onItemClick?.(arc.data.row, data.indexOf(arc.data.row)),\n },\n },\n });\n return {\n component: donut,\n data: [...visible],\n triggers: {\n [Donut.selectors.segment]: (arc: { data: Slice<T> }) =>\n chart.tooltip(\n chartTooltip(arc.data.name, [\n {\n label: lang.chartValue,\n color: arc.data.color,\n value: valueText(arc.data.value),\n },\n {\n label: lang.chartShare,\n color: 'transparent',\n value: shareText(arc.data.value),\n },\n ]),\n ),\n },\n };\n },\n }));\n\n return () => (\n <ChartFrame\n {...rest}\n unit={unit}\n height={height}\n legend={\n legend\n ? slices.map((slice) => ({ key: slice.name, label: slice.name, color: slice.color }))\n : []\n }\n hiddenKeys={hiddenItems}\n onLegendToggle={toggle}\n loading={loading}\n skeleton={<Skeleton sx={donutChartStyles.skeleton} />}\n clickable={onItemClick != null}\n isEmpty={data.length === 0}\n empty={empty}\n table={{\n columns: [\n lang.chartCategory,\n unit ? `${lang.chartValue}(${unit})` : lang.chartValue,\n lang.chartShare,\n ],\n rows: visible.map((slice) => ({\n header: slice.name,\n cells: [\n slice.value === undefined ? lang.emptyValue : format.number(slice.value, valueFormat),\n shareText(slice.value),\n ],\n })),\n }}\n plotRef={(element) => (plot = element)}\n />\n );\n },\n);\n"],"mappings":"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"}
|
|
@@ -5,8 +5,8 @@ import { useFormat } from "../config/lang/format.js";
|
|
|
5
5
|
import { Skeleton } from "../feedback/Skeleton.js";
|
|
6
6
|
import { ChartFrame } from "./frame.js";
|
|
7
7
|
import { resolveColor } from "./series.js";
|
|
8
|
-
import { computed, createTextVNode, createVNode
|
|
9
|
-
import { _component, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
8
|
+
import { computed, createTextVNode, createVNode } from "vue";
|
|
9
|
+
import { _component, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
10
10
|
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
11
11
|
//#region src/charts/FunnelChart.tsx
|
|
12
12
|
/** 漏斗的一个环节 */
|
|
@@ -122,14 +122,14 @@ var FunnelChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
122
122
|
...valueFormat.value,
|
|
123
123
|
suffix: unit.value
|
|
124
124
|
} : valueFormat.value);
|
|
125
|
-
return () => createVNode(ChartFrame,
|
|
125
|
+
return () => createVNode(ChartFrame, mergeJsxProps(rest, {
|
|
126
126
|
"unit": unit.value,
|
|
127
127
|
"height": "auto",
|
|
128
128
|
"legend": _cache[0] || (_cache[0] = []),
|
|
129
129
|
"hiddenKeys": _cache[1] || (_cache[1] = []),
|
|
130
130
|
"onLegendToggle": _cache[2] || (_cache[2] = () => {}),
|
|
131
131
|
"loading": loading.value,
|
|
132
|
-
"skeleton": createVNode("div",
|
|
132
|
+
"skeleton": createVNode("div", mergeJsxProps({ "aria-hidden": "true" }, css.attrs(funnelChartStyles.skeleton)), [SKELETON_WIDTHS.map((width) => createVNode(Skeleton, {
|
|
133
133
|
"key": width,
|
|
134
134
|
"sx": [funnelChartStyles.skeletonBar, css.bind(funnelChartDynamicStyles.skeletonWidth, () => [width])]
|
|
135
135
|
}, null))]),
|
|
@@ -151,7 +151,7 @@ var FunnelChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
151
151
|
]
|
|
152
152
|
}))
|
|
153
153
|
},
|
|
154
|
-
"plot": createVNode("div", css.attrs(funnelChartStyles.list), [stages.value.map((stage, index) => [index > 0 && rates.value && createVNode("div",
|
|
154
|
+
"plot": createVNode("div", css.attrs(funnelChartStyles.list), [stages.value.map((stage, index) => [index > 0 && rates.value && createVNode("div", mergeJsxProps({ "key": `rate-${index}` }, css.attrs(funnelChartStyles.rate)), [createVNode("span", css.attrs(funnelChartStyles.rateText), [createTextVNode("↓ "), rate(index, index - 1)])]), createVNode("div", mergeJsxProps({ "key": `stage-${index}` }, css.attrs(funnelChartStyles.row, onItemClick.value && funnelChartStyles.rowClickable), { "onClick": onItemClick.value && (() => onItemClick.value(stage, index)) }), [
|
|
155
155
|
createVNode("span", css.attrs(funnelChartStyles.label), [stage.label]),
|
|
156
156
|
createVNode("span", css.attrs(funnelChartStyles.track), [createVNode("span", css.attrs(funnelChartStyles.bar, css.bind(funnelChartDynamicStyles.barSize, () => [max.value ? `${numbers.value[index] / max.value * 100}%` : "0%", resolveColor(stage.color ?? color.value, index)])), null)]),
|
|
157
157
|
createVNode("span", css.attrs(funnelChartStyles.value), [valueText(stage)])
|