@zerodep-dev/vue-tsx-pro-ui 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base/Badge.js +3 -3
- package/dist/base/Badge.js.map +1 -1
- package/dist/base/Can.d.ts +12 -0
- package/dist/base/Can.js +20 -0
- package/dist/base/Can.js.map +1 -0
- package/dist/base/ClientOnly.d.ts +10 -0
- package/dist/base/ClientOnly.js +18 -0
- package/dist/base/ClientOnly.js.map +1 -0
- package/dist/base/CloseButton.js +3 -3
- package/dist/base/CloseButton.js.map +1 -1
- package/dist/base/CodeBlock.js +5 -5
- package/dist/base/CodeBlock.js.map +1 -1
- package/dist/base/CopyButton.js +3 -3
- package/dist/base/CopyButton.js.map +1 -1
- package/dist/base/Icon.js +3 -3
- package/dist/base/Icon.js.map +1 -1
- package/dist/base/Indicator.js +3 -3
- package/dist/base/Indicator.js.map +1 -1
- package/dist/base/StatusDot.js +3 -3
- package/dist/base/StatusDot.js.map +1 -1
- package/dist/base/Tag.js +5 -5
- package/dist/base/Tag.js.map +1 -1
- package/dist/base/button-core.js +4 -4
- package/dist/base/button-core.js.map +1 -1
- package/dist/charts/AreaChart.js +3 -3
- package/dist/charts/AreaChart.js.map +1 -1
- package/dist/charts/BarChart.js +3 -3
- package/dist/charts/BarChart.js.map +1 -1
- package/dist/charts/ChordChart.js +3 -3
- package/dist/charts/ChordChart.js.map +1 -1
- package/dist/charts/ComboChart.js +3 -3
- package/dist/charts/ComboChart.js.map +1 -1
- package/dist/charts/DonutChart.js +3 -3
- package/dist/charts/DonutChart.js.map +1 -1
- package/dist/charts/FunnelChart.js +5 -5
- package/dist/charts/FunnelChart.js.map +1 -1
- package/dist/charts/GraphChart.js +3 -3
- package/dist/charts/GraphChart.js.map +1 -1
- package/dist/charts/LineChart.js +3 -3
- package/dist/charts/LineChart.js.map +1 -1
- package/dist/charts/MapChart.js +7 -7
- package/dist/charts/MapChart.js.map +1 -1
- package/dist/charts/RadarChart.js +16 -15
- package/dist/charts/RadarChart.js.map +1 -1
- package/dist/charts/SankeyChart.js +3 -3
- package/dist/charts/SankeyChart.js.map +1 -1
- package/dist/charts/ScatterChart.js +3 -3
- package/dist/charts/ScatterChart.js.map +1 -1
- package/dist/charts/TimelineChart.js +6 -5
- package/dist/charts/TimelineChart.js.map +1 -1
- package/dist/charts/TreemapChart.js +3 -3
- package/dist/charts/TreemapChart.js.map +1 -1
- package/dist/charts/WaterfallChart.js +3 -3
- package/dist/charts/WaterfallChart.js.map +1 -1
- package/dist/charts/XYChart.js +9 -7
- package/dist/charts/XYChart.js.map +1 -1
- package/dist/charts/frame.js +10 -8
- package/dist/charts/frame.js.map +1 -1
- package/dist/charts/single.js +4 -3
- package/dist/charts/single.js.map +1 -1
- package/dist/config/Provider.d.ts +3 -1
- package/dist/config/Provider.js +9 -4
- package/dist/config/Provider.js.map +1 -1
- package/dist/config/context.d.ts +18 -0
- package/dist/config/context.js +16 -1
- package/dist/config/context.js.map +1 -1
- package/dist/config/createUi.d.ts +20 -3
- package/dist/config/createUi.js +80 -49
- package/dist/config/createUi.js.map +1 -1
- package/dist/config/layout/preference.d.ts +43 -0
- package/dist/config/layout/preference.js +108 -0
- package/dist/config/layout/preference.js.map +1 -0
- package/dist/date/Calendar.js +3 -3
- package/dist/date/Calendar.js.map +1 -1
- package/dist/date/CalendarBoard.js +9 -9
- package/dist/date/CalendarBoard.js.map +1 -1
- package/dist/date/CalendarView.js +7 -7
- package/dist/date/CalendarView.js.map +1 -1
- package/dist/date/DateField.js +3 -3
- package/dist/date/DateField.js.map +1 -1
- package/dist/date/DatePicker.js +5 -5
- package/dist/date/DatePicker.js.map +1 -1
- package/dist/date/DateRangePicker.js +5 -5
- package/dist/date/DateRangePicker.js.map +1 -1
- package/dist/date/DateSegmentGroup.js +5 -5
- package/dist/date/DateSegmentGroup.js.map +1 -1
- package/dist/date/QuarterCalendar.js +6 -6
- package/dist/date/QuarterCalendar.js.map +1 -1
- package/dist/date/RangeCalendar.js +3 -3
- package/dist/date/RangeCalendar.js.map +1 -1
- package/dist/date/SegmentedField.js +3 -3
- package/dist/date/SegmentedField.js.map +1 -1
- package/dist/date/TimeField.js +3 -3
- package/dist/date/TimeField.js.map +1 -1
- package/dist/date/TimePicker.js +4 -4
- package/dist/date/TimePicker.js.map +1 -1
- package/dist/date/date-segments.js +1 -1
- package/dist/date/date-segments.js.map +1 -1
- package/dist/date/picker-popover.js +3 -2
- package/dist/date/picker-popover.js.map +1 -1
- package/dist/diagram/Diagram.js +12 -12
- package/dist/diagram/Diagram.js.map +1 -1
- package/dist/diagram/DiagramLanes.js +3 -3
- package/dist/diagram/DiagramLanes.js.map +1 -1
- package/dist/diagram/DiagramMinimap.js +4 -4
- package/dist/diagram/DiagramMinimap.js.map +1 -1
- package/dist/display/Accordion.js +6 -6
- package/dist/display/Accordion.js.map +1 -1
- package/dist/display/AnimatedNumber.js +4 -4
- package/dist/display/AnimatedNumber.js.map +1 -1
- package/dist/display/Avatar.js +4 -4
- package/dist/display/Avatar.js.map +1 -1
- package/dist/display/Checklist.js +7 -7
- package/dist/display/Checklist.js.map +1 -1
- package/dist/display/Collapse.js +8 -5
- package/dist/display/Collapse.js.map +1 -1
- package/dist/display/Collapsible.js +3 -3
- package/dist/display/Collapsible.js.map +1 -1
- package/dist/display/Descriptions.js +5 -5
- package/dist/display/Descriptions.js.map +1 -1
- package/dist/display/Ellipsis.js +3 -3
- package/dist/display/Ellipsis.js.map +1 -1
- package/dist/display/FieldCompare.js +4 -4
- package/dist/display/FieldCompare.js.map +1 -1
- package/dist/display/Gauge.js +11 -11
- package/dist/display/Gauge.js.map +1 -1
- package/dist/display/Image.js +4 -4
- package/dist/display/Image.js.map +1 -1
- package/dist/display/Marquee.js +3 -3
- package/dist/display/Marquee.js.map +1 -1
- package/dist/display/ProportionBar.js +4 -4
- package/dist/display/ProportionBar.js.map +1 -1
- package/dist/display/Sortable.js +4 -4
- package/dist/display/Sortable.js.map +1 -1
- package/dist/display/Sparkline.js +6 -6
- package/dist/display/Sparkline.js.map +1 -1
- package/dist/display/Stat.js +3 -3
- package/dist/display/Stat.js.map +1 -1
- package/dist/display/Timeline.js +7 -7
- package/dist/display/Timeline.js.map +1 -1
- package/dist/display/Tree.js +10 -10
- package/dist/display/Tree.js.map +1 -1
- package/dist/display/VirtualList.js +7 -7
- package/dist/display/VirtualList.js.map +1 -1
- package/dist/display/Watermark.js +3 -3
- package/dist/display/Watermark.js.map +1 -1
- package/dist/docs-kit/CodeGroup.js +5 -5
- package/dist/docs-kit/CodeGroup.js.map +1 -1
- package/dist/docs-kit/CodePreview.js +6 -6
- package/dist/docs-kit/CodePreview.js.map +1 -1
- package/dist/docs-kit/PropsTable.js +4 -4
- package/dist/docs-kit/PropsTable.js.map +1 -1
- package/dist/feedback/Alert.js +5 -5
- package/dist/feedback/Alert.js.map +1 -1
- package/dist/feedback/Banner.js +5 -5
- package/dist/feedback/Banner.js.map +1 -1
- package/dist/feedback/Empty.js +4 -4
- package/dist/feedback/Empty.js.map +1 -1
- package/dist/feedback/LoadingBar.js +20 -13
- package/dist/feedback/LoadingBar.js.map +1 -1
- package/dist/feedback/LoadingOverlay.js +3 -3
- package/dist/feedback/LoadingOverlay.js.map +1 -1
- package/dist/feedback/Progress.js +14 -14
- package/dist/feedback/Progress.js.map +1 -1
- package/dist/feedback/Result.js +4 -4
- package/dist/feedback/Result.js.map +1 -1
- package/dist/feedback/SkeletonText.js +3 -3
- package/dist/feedback/SkeletonText.js.map +1 -1
- package/dist/feedback/Toast.js +6 -6
- package/dist/feedback/Toast.js.map +1 -1
- package/dist/form/Field.js +7 -7
- package/dist/form/Field.js.map +1 -1
- package/dist/form/Form.js +3 -3
- package/dist/form/Form.js.map +1 -1
- package/dist/form/FormErrorSummary.js +3 -3
- package/dist/form/FormErrorSummary.js.map +1 -1
- package/dist/form/FormSection.js +6 -6
- package/dist/form/FormSection.js.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.js +10 -5
- package/dist/input/Autocomplete.js +3 -3
- package/dist/input/Autocomplete.js.map +1 -1
- package/dist/input/Cascader.js +7 -7
- package/dist/input/Cascader.js.map +1 -1
- package/dist/input/CharacterCount.js +3 -3
- package/dist/input/CharacterCount.js.map +1 -1
- package/dist/input/Checkbox.js +5 -5
- package/dist/input/Checkbox.js.map +1 -1
- package/dist/input/ColorField.js +4 -4
- package/dist/input/ColorField.js.map +1 -1
- package/dist/input/Combobox.js +4 -4
- package/dist/input/Combobox.js.map +1 -1
- package/dist/input/ConditionBuilder.js +15 -14
- package/dist/input/ConditionBuilder.js.map +1 -1
- package/dist/input/FileDrop.js +3 -3
- package/dist/input/FileDrop.js.map +1 -1
- package/dist/input/Listbox.js +9 -9
- package/dist/input/Listbox.js.map +1 -1
- package/dist/input/Mentions.js +3 -3
- package/dist/input/Mentions.js.map +1 -1
- package/dist/input/NumberField.js +3 -3
- package/dist/input/NumberField.js.map +1 -1
- package/dist/input/NumberRangeField.js +5 -5
- package/dist/input/NumberRangeField.js.map +1 -1
- package/dist/input/OtpField.js +5 -5
- package/dist/input/OtpField.js.map +1 -1
- package/dist/input/PeoplePicker.js +6 -6
- package/dist/input/PeoplePicker.js.map +1 -1
- package/dist/input/PeoplePickerPanel.js +4 -4
- package/dist/input/PeoplePickerPanel.js.map +1 -1
- package/dist/input/Radio.js +4 -4
- package/dist/input/Radio.js.map +1 -1
- package/dist/input/Rating.js +5 -5
- package/dist/input/Rating.js.map +1 -1
- package/dist/input/Select.js +4 -4
- package/dist/input/Select.js.map +1 -1
- package/dist/input/SelectedTags.js +4 -4
- package/dist/input/SelectedTags.js.map +1 -1
- package/dist/input/Slider.js +6 -6
- package/dist/input/Slider.js.map +1 -1
- package/dist/input/Switch.js +3 -3
- package/dist/input/Switch.js.map +1 -1
- package/dist/input/TagsInput.js +5 -5
- package/dist/input/TagsInput.js.map +1 -1
- package/dist/input/TextField.js +5 -5
- package/dist/input/TextField.js.map +1 -1
- package/dist/input/Textarea.js +4 -4
- package/dist/input/Textarea.js.map +1 -1
- package/dist/input/ToggleGroup.js +4 -4
- package/dist/input/ToggleGroup.js.map +1 -1
- package/dist/input/Transfer.js +8 -8
- package/dist/input/Transfer.js.map +1 -1
- package/dist/input/TreeSelect.js +6 -6
- package/dist/input/TreeSelect.js.map +1 -1
- package/dist/input/Upload.js +8 -8
- package/dist/input/Upload.js.map +1 -1
- package/dist/layout/Affix.js +4 -4
- package/dist/layout/Affix.js.map +1 -1
- package/dist/layout/ResizeHandle.d.ts +6 -2
- package/dist/layout/ResizeHandle.js +32 -9
- package/dist/layout/ResizeHandle.js.map +1 -1
- package/dist/layout/ScrollArea.js +3 -3
- package/dist/layout/ScrollArea.js.map +1 -1
- package/dist/layout/Separator.js +3 -3
- package/dist/layout/Separator.js.map +1 -1
- package/dist/layout/Splitter.js +3 -3
- package/dist/layout/Splitter.js.map +1 -1
- package/dist/layout/ViewportFill.d.ts +2 -2
- package/dist/layout/ViewportFill.js +4 -4
- package/dist/layout/ViewportFill.js.map +1 -1
- package/dist/navigation/Breadcrumbs.js +4 -4
- package/dist/navigation/Breadcrumbs.js.map +1 -1
- package/dist/navigation/NavBar.js +6 -6
- package/dist/navigation/NavBar.js.map +1 -1
- package/dist/navigation/NavList.d.ts +1 -0
- package/dist/navigation/NavList.js +20 -12
- package/dist/navigation/NavList.js.map +1 -1
- package/dist/navigation/NavMenu.d.ts +27 -0
- package/dist/navigation/NavMenu.js +54 -0
- package/dist/navigation/NavMenu.js.map +1 -0
- package/dist/navigation/PageJump.js +3 -3
- package/dist/navigation/PageJump.js.map +1 -1
- package/dist/navigation/PageSizeSelect.js +4 -4
- package/dist/navigation/PageSizeSelect.js.map +1 -1
- package/dist/navigation/Pagination.js +6 -6
- package/dist/navigation/Pagination.js.map +1 -1
- package/dist/navigation/PaginationBar.js +5 -5
- package/dist/navigation/PaginationBar.js.map +1 -1
- package/dist/navigation/Steps.js +4 -4
- package/dist/navigation/Steps.js.map +1 -1
- package/dist/navigation/TableOfContents.js +5 -5
- package/dist/navigation/TableOfContents.js.map +1 -1
- package/dist/navigation/Tabs.js +7 -7
- package/dist/navigation/Tabs.js.map +1 -1
- package/dist/navigation/Toolbar.js +3 -3
- package/dist/navigation/Toolbar.js.map +1 -1
- package/dist/overlay/AlertDialog.js +4 -4
- package/dist/overlay/AlertDialog.js.map +1 -1
- package/dist/overlay/CommandPalette.js +4 -4
- package/dist/overlay/CommandPalette.js.map +1 -1
- package/dist/overlay/ContextMenu.js +3 -3
- package/dist/overlay/ContextMenu.js.map +1 -1
- package/dist/overlay/Dialog.js +4 -4
- package/dist/overlay/Dialog.js.map +1 -1
- package/dist/overlay/Drawer.js +5 -5
- package/dist/overlay/Drawer.js.map +1 -1
- package/dist/overlay/HoverCard.js +5 -3
- package/dist/overlay/HoverCard.js.map +1 -1
- package/dist/overlay/Lightbox.js +5 -5
- package/dist/overlay/Lightbox.js.map +1 -1
- package/dist/overlay/Menu.js +7 -7
- package/dist/overlay/Menu.js.map +1 -1
- package/dist/overlay/ModalCore.js +9 -9
- package/dist/overlay/ModalCore.js.map +1 -1
- package/dist/overlay/Popconfirm.js +6 -6
- package/dist/overlay/Popconfirm.js.map +1 -1
- package/dist/overlay/Popover.js +5 -3
- package/dist/overlay/Popover.js.map +1 -1
- package/dist/overlay/Tooltip.js +5 -3
- package/dist/overlay/Tooltip.js.map +1 -1
- package/dist/overlay/Tour.js +8 -8
- package/dist/overlay/Tour.js.map +1 -1
- package/dist/overlay/detached.d.ts +2 -0
- package/dist/overlay/detached.js +73 -42
- package/dist/overlay/detached.js.map +1 -1
- package/dist/patterns/ActionBar.js +3 -3
- package/dist/patterns/ActionBar.js.map +1 -1
- package/dist/patterns/AppShell.d.ts +28 -1
- package/dist/patterns/AppShell.js +158 -52
- package/dist/patterns/AppShell.js.map +1 -1
- package/dist/patterns/AuthLayout.d.ts +28 -0
- package/dist/patterns/AuthLayout.js +77 -0
- package/dist/patterns/AuthLayout.js.map +1 -0
- package/dist/patterns/CommentThread.js +9 -9
- package/dist/patterns/CommentThread.js.map +1 -1
- package/dist/patterns/DataImport.js +4 -4
- package/dist/patterns/DataImport.js.map +1 -1
- package/dist/patterns/DeadlineBadge.js +3 -3
- package/dist/patterns/DeadlineBadge.js.map +1 -1
- package/dist/patterns/FilterBar.d.ts +11 -1
- package/dist/patterns/FilterBar.js +39 -10
- package/dist/patterns/FilterBar.js.map +1 -1
- package/dist/patterns/FormDialog.d.ts +18 -0
- package/dist/patterns/FormDialog.js +46 -0
- package/dist/patterns/FormDialog.js.map +1 -0
- package/dist/patterns/KanbanBoard.js +8 -8
- package/dist/patterns/KanbanBoard.js.map +1 -1
- package/dist/patterns/LayoutSwitcher.d.ts +12 -0
- package/dist/patterns/LayoutSwitcher.js +79 -0
- package/dist/patterns/LayoutSwitcher.js.map +1 -0
- package/dist/patterns/NotificationCenter.js +8 -8
- package/dist/patterns/NotificationCenter.js.map +1 -1
- package/dist/patterns/PageTabs.d.ts +12 -7
- package/dist/patterns/PageTabs.js +41 -29
- package/dist/patterns/PageTabs.js.map +1 -1
- package/dist/patterns/QueryTable.d.ts +7 -1
- package/dist/patterns/QueryTable.js +116 -81
- package/dist/patterns/QueryTable.js.map +1 -1
- package/dist/patterns/SlideCaptcha.d.ts +80 -0
- package/dist/patterns/SlideCaptcha.js +354 -0
- package/dist/patterns/SlideCaptcha.js.map +1 -0
- package/dist/patterns/StepsForm.js +7 -7
- package/dist/patterns/StepsForm.js.map +1 -1
- package/dist/patterns/ThemeSwitcher.js +3 -3
- package/dist/patterns/ThemeSwitcher.js.map +1 -1
- package/dist/patterns/index.d.ts +7 -3
- package/dist/patterns/index.js +5 -1
- package/dist/patterns/messages.d.ts +37 -1
- package/dist/patterns/messages.js +38 -2
- package/dist/patterns/messages.js.map +1 -1
- package/dist/table/ColumnHeaderActions.js +3 -3
- package/dist/table/ColumnHeaderActions.js.map +1 -1
- package/dist/table/ColumnResizer.js +3 -3
- package/dist/table/ColumnResizer.js.map +1 -1
- package/dist/table/ColumnSettings.js +5 -5
- package/dist/table/ColumnSettings.js.map +1 -1
- package/dist/table/ColumnTable.js +8 -8
- package/dist/table/ColumnTable.js.map +1 -1
- package/dist/table/DataTable.js +8 -8
- package/dist/table/DataTable.js.map +1 -1
- package/dist/table/DataTableView.js +21 -21
- package/dist/table/DataTableView.js.map +1 -1
- package/dist/table/PivotTable.js +3 -3
- package/dist/table/PivotTable.js.map +1 -1
- package/dist/table/SelectionBar.js +4 -4
- package/dist/table/SelectionBar.js.map +1 -1
- package/dist/table/TreeDataTable.js +5 -5
- package/dist/table/TreeDataTable.js.map +1 -1
- package/dist/theme-customize/ThemeCustomizer.js +7 -6
- package/dist/theme-customize/ThemeCustomizer.js.map +1 -1
- package/dist/theme-customize/ThemeThumbnail.js +3 -3
- package/dist/theme-customize/ThemeThumbnail.js.map +1 -1
- package/dist/three/background/AuroraBackground.js +3 -3
- package/dist/three/background/AuroraBackground.js.map +1 -1
- package/dist/three/background/MeshGradient.js +3 -3
- package/dist/three/background/MeshGradient.js.map +1 -1
- package/dist/three/background/ShaderBackground.js +3 -3
- package/dist/three/background/ShaderBackground.js.map +1 -1
- package/dist/three/core/EffectSurface.js +3 -3
- package/dist/three/core/EffectSurface.js.map +1 -1
- package/dist/three/core/ThreeCanvas.js +4 -4
- package/dist/three/core/ThreeCanvas.js.map +1 -1
- package/dist/three/effects/Confetti.js +3 -3
- package/dist/three/effects/Confetti.js.map +1 -1
- package/dist/three/effects/ParticleField.js +3 -3
- package/dist/three/effects/ParticleField.js.map +1 -1
- package/dist/three/effects/WaveGrid.js +3 -3
- package/dist/three/effects/WaveGrid.js.map +1 -1
- package/dist/three/globe/Globe.js +3 -3
- package/dist/three/globe/Globe.js.map +1 -1
- package/dist/three/model/ModelViewer.js +3 -3
- package/dist/three/model/ModelViewer.js.map +1 -1
- package/dist/utils/data/number.d.ts +5 -0
- package/dist/utils/data/number.js +8 -1
- package/dist/utils/data/number.js.map +1 -1
- package/dist/utils/dom/element.d.ts +3 -1
- package/dist/utils/dom/element.js +8 -4
- package/dist/utils/dom/element.js.map +1 -1
- package/dist/utils/dom/fullscreen.d.ts +1 -1
- package/dist/utils/dom/fullscreen.js +12 -8
- package/dist/utils/dom/fullscreen.js.map +1 -1
- package/dist/utils/dom/mounted.d.ts +7 -0
- package/dist/utils/dom/mounted.js +16 -0
- package/dist/utils/dom/mounted.js.map +1 -0
- package/dist/utils/dom/overflow.js +2 -1
- package/dist/utils/dom/overflow.js.map +1 -1
- package/dist/utils/dom/pan-zoom.js +2 -1
- package/dist/utils/dom/pan-zoom.js.map +1 -1
- package/dist/utils/props.js +3 -13
- package/dist/utils/props.js.map +1 -1
- package/dist/utils/request/http.d.ts +12 -0
- package/dist/utils/request/http.js +6 -2
- package/dist/utils/request/http.js.map +1 -1
- package/dist/utils/request/use-request.js +4 -2
- package/dist/utils/request/use-request.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppShell.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_childrenProp","_vtp_restProps","_vtp_untrack","_vue_shallowRef","_createVNode","MenuIcon","_mergeProps","_vue_unref"],"sources":["../../src/patterns/AppShell.tsx"],"sourcesContent":["/**\n * 应用外壳:粘性顶栏、左侧栏(导航)、正文、右侧栏(本页目录),文档站与后台的整页骨架。\n *\n * <AppShell\n * brand={<AppShellBrand href=\"/\" icon={Boxes}>采购管理平台</AppShellBrand>}\n * header={<><CommandPalette … /><ThemeSwitcher preference={preference} /></>}\n * sidebar={<NavList aria-label=\"文档\">…</NavList>}\n * aside={<TableOfContents items={headings} />}\n * >\n * <Prose>…</Prose>\n * </AppShell>\n *\n * 响应式(宽度按容器档位 sizes.lg / sizes.xl):\n * - 64rem 以上显示左侧栏;更窄时收进从起始边滑出的 Drawer,顶栏最前面出现打开它的按钮。\n * 抽屉里点了链接(换页)、或窗口变宽时自动关闭。\n * - 80rem 以上显示右侧栏;更窄时不显示(目录这类辅助内容)。\n * 两侧栏粘在顶栏下面,内容多时各自滚动(ScrollArea)。顶栏半透明,滚动的正文从下面透出(backdrop-filter)。\n * 整页与两侧栏都是浮层滚动条(PageScrollbar、ScrollArea):不占宽度,滚动时出现。\n *\n * 写了 brand 时,侧栏常驻的宽度下品牌这一格与侧栏同宽,侧栏的竖线一直延伸到顶栏顶端,\n * header 的内容从竖线之后开始、与正文的起始边对齐(本页的页签放在这里)。\n *\n * sidebarMode=\"overlay\":侧栏改为盖在页面上面的抽屉,开不开都不占正文的位置(正文总是整宽,布局不随侧栏变化)。\n * “菜单”按钮固定在顶栏品牌格的结束边,开关都是它(aria-expanded);抽屉从顶栏下面弹出、盖在正文上。\n * 抽屉结束边可以拖动调整宽度(ResizeHandle,也能聚焦后用 ← / →,双击回到初始宽度),父级写 _sidebarWidth={x} 记住;\n * 顶栏的品牌格按初始宽度,拖动时不变。焦点在抽屉里时按 Esc 收起、焦点回到“菜单”;窄屏时点了链接也收起。\n * 拖到比 160px 窄时侧栏变紧凑:里面的 NavList 只显示图标(名称在提示里),小节标题只剩图标(NavCompactContext)。\n *\n * 品牌格里一般放 AppShellBrand:品牌色的标志与产品名,点了回首页;自己的标志图写在它的 logo 里。\n *\n * 几种布局与区域:\n * - nav:顶栏里品牌后面的导航(NavBar)。只有 nav 时是顶部导航布局,和 sidebar 一起时是混合布局(顶栏一级、侧栏二级)。\n * - tabs:正文上面的页签区(PageTabs,和路由同步用 useRouteTabs);footer:正文下面的页脚,内容少时也贴着底部。\n * - sidebarCollapsible:docked 侧栏底部有“收起侧栏”,收起后只剩图标栏(宽屏时;导航变紧凑、分组悬停弹出,\n * 品牌格只剩标志),父级写 _sidebarCollapsed={x} 记住。\n *\n * 自带 SkipLink(跳到正文);顶栏的高度经 layoutVars.stickyOffset 交给里面的标题,锚点跳转后不被顶栏挡住。\n */\nimport { Menu as MenuIcon, PanelLeftClose, PanelLeftOpen } from '@lucide/icons';\nimport { createContext, _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { useId, watch, type VNodeChild } from 'vue';\nimport { Button, type ButtonProps } from '../base/Button';\nimport { useMessages } from '../config/lang/messages';\nimport { patternsMessages } from './messages';\nimport { Icon, type IconData, type IconProps } from '../base/Icon';\nimport { visuallyHiddenStyles } from '../base/VisuallyHidden';\nimport { IconButton, type IconButtonProps } from '../base/IconButton';\nimport { NavCompactContext } from '../navigation/NavList';\nimport { SkipLink } from '../navigation/SkipLink';\nimport { PageScrollbar } from '../layout/PageScrollbar';\nimport { ResizeHandle, type ResizeHandleProps } from '../layout/ResizeHandle';\nimport { ScrollArea, type ScrollAreaProps } from '../layout/ScrollArea';\nimport { Drawer, type DrawerProps } from '../overlay/Drawer';\nimport { useMediaQuery } from '../utils/dom/media-query';\nimport { toElement } from '../utils/dom/element';\nimport { useRouterLink } from '../utils/dom/link';\nimport { hasContent, mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { breakpoints, t } from '../config/theme/tokens';\nimport { focusRingStyles } from '../utils/style/styles';\nimport { layoutVars } from '../utils/style/vars';\n\n/** 顶栏高度:也是两侧栏粘住的位置、标题跳转时让开的距离 */\nconst HEADER_HEIGHT = '3rem';\n/** 左侧栏的宽度:顶栏的品牌格与它同宽 */\nconst SIDEBAR_WIDTH = '16rem';\n/** 侧栏收起成图标栏时的宽度 */\nconst RAIL_WIDTH = '3.5rem';\n/** 抽屉式侧栏的默认宽度与可以拖动的范围(px) */\nconst OVERLAY_WIDTH = 256;\n// 最窄可以收到只剩滚动条那么宽\nconst OVERLAY_MIN_WIDTH = 16;\nconst OVERLAY_MAX_WIDTH = 480;\n// 比它窄时侧栏只放得下图标:导航变紧凑\nconst OVERLAY_COMPACT_WIDTH = 160;\n// 运行时判断侧栏是否常驻(useMediaQuery 要的是不带 @media 的查询)\nconst WIDE_QUERY = breakpoints.lg.replace('@media ', '');\n\n/** 左侧栏的形式 */\nexport interface AppShellSidebarModes {\n /** 宽屏时占一列、窄屏时收进抽屉(默认) */\n docked: unknown;\n /** 各宽度都是盖在页面上面的抽屉,不占正文的位置,可以收起、拖动宽度 */\n overlay: unknown;\n}\n\n/** 未声明的属性落到外层 div 上 */\nexport interface AppShellProps extends PartProps<'div'> {\n /** 顶栏起始边的品牌(标志与产品名):有侧栏时这一格与侧栏同宽,侧栏的竖线延伸到顶 */\n brand?: VNodeChild;\n /** 顶栏的内容:搜索、本页的页签、主题切换…(没写 brand 时标志也放这里) */\n header?: VNodeChild;\n /** 左侧栏:站点导航;窄屏时收进抽屉 */\n sidebar?: VNodeChild;\n /** 右侧栏:本页目录等辅助内容;较窄时不显示 */\n aside?: VNodeChild;\n /** 顶栏里品牌后面的导航(NavBar):只有它时是顶部导航布局,和 sidebar 一起时是混合布局(顶栏一级、侧栏二级) */\n nav?: VNodeChild;\n /** 正文上面的页签区(PageTabs:打开过的页面) */\n tabs?: VNodeChild;\n /** 正文下面的页脚(版权、备案号、版本) */\n footer?: VNodeChild;\n /** docked 侧栏底部有“收起侧栏”按钮:收起后只剩图标栏(导航变紧凑,分组的子项悬停弹出) */\n sidebarCollapsible?: boolean;\n /** docked 侧栏收起了:父级可以写 _sidebarCollapsed={x} 记住 */\n _sidebarCollapsed?: boolean;\n /** 左侧栏的形式,默认 docked */\n sidebarMode?: keyof AppShellSidebarModes;\n /** 侧栏抽屉是否打开(docked 时只在窄屏有抽屉):父级可以写 _sidebarOpen={x} */\n _sidebarOpen?: boolean;\n /**\n * overlay 抽屉的宽度(px),默认 256,范围 16–480(窄到几乎只剩滚动条;比 160 窄时导航只显示图标):\n * 父级写 _sidebarWidth={x} 记住拖动后的宽度\n */\n _sidebarWidth?: number;\n /** 页面最前面的“跳到正文”链接,默认 true */\n skipLink?: boolean;\n /** 整页用浮层滚动条(PageScrollbar:不占宽度,滚动时出现),默认 true */\n pageScrollbar?: boolean;\n /** 顶栏(header)的部件属性 */\n propsHeader?: PartProps<'header'>;\n /** 品牌格(div)的部件属性 */\n propsBrand?: PartProps<'div'>;\n /** 顶栏里 header 内容那一格(div)的部件属性 */\n propsHeaderContent?: PartProps<'div'>;\n /** 顶栏下面的主体(侧栏、正文、右栏的栅格)的部件属性 */\n propsBody?: PartProps<'div'>;\n /** 左侧栏(div)的部件属性 */\n propsSidebar?: PartProps<'div'>;\n /** 左侧栏里的滚动区域的部件属性(docked 的侧栏与 overlay 的抽屉共用) */\n propsSidebarScroll?: Partial<ScrollAreaProps>;\n /** 正文(main)的部件属性 */\n propsMain?: PartProps<'main'>;\n /** 顶栏里导航那一格的部件属性 */\n propsNav?: PartProps<'div'>;\n /** 页签区的部件属性 */\n propsTabs?: PartProps<'div'>;\n /** 页脚(footer)的部件属性 */\n propsFooter?: PartProps<'footer'>;\n /** “收起侧栏”按钮的部件属性 */\n propsCollapse?: Partial<IconButtonProps>;\n /** 右侧栏(aside)的部件属性 */\n propsAside?: PartProps<'aside'>;\n /** 右侧栏里的滚动区域的部件属性 */\n propsAsideScroll?: Partial<ScrollAreaProps>;\n /** docked 时窄屏打开侧栏的按钮的部件属性 */\n propsMenuButton?: Partial<IconButtonProps>;\n /** overlay 时品牌格里开关抽屉的“菜单”按钮的部件属性 */\n propsSidebarToggle?: Partial<ButtonProps>;\n /** overlay 抽屉宽度调整手柄的部件属性 */\n propsResizeHandle?: Partial<ResizeHandleProps>;\n /** 窄屏时的侧栏抽屉的部件属性 */\n propsDrawer?: Partial<DrawerProps>;\n /** 正文 */\n children?: VNodeChild;\n}\n\n/** AppShell 交给里面的部件的状态:侧栏收起时品牌只剩标志 */\nexport interface AppShellState {\n /** docked 侧栏收起成了图标栏(宽屏时) */\n collapsed: () => boolean;\n}\n\n/** AppShell 的上下文:AppShellBrand 据此在侧栏收起时只显示标志;不在 AppShell 里时为 null */\nexport const AppShellContext = createContext<AppShellState | null>('AppShell');\n\n/** 外壳把顶栏高度交给里面的标题与粘性侧栏 */\nexport const appShellOffset = css.createTheme(layoutVars, { stickyOffset: HEADER_HEIGHT });\n\n/** AppShell 的样式:root、header、menuButton、body、sidebar、main、aside 等 */\nexport const appShellStyles = css.create({\n // 整页的界面基准文字(同 Provider):没有 Provider 时页面是浏览器默认的 16px,与组件的 md 档位对不上\n root: {\n display: 'flex',\n flexDirection: 'column',\n minHeight: '100dvh',\n color: '_text',\n fontSize: '_md',\n lineHeight: '_normal',\n },\n header: {\n position: 'sticky',\n top: 0,\n // 盖住滚动的正文与粘性侧栏;低于浮层(顶层渲染)与 SkipLink\n zIndex: 10,\n boxSizing: 'border-box',\n display: 'flex',\n alignItems: 'center',\n gap: '_md',\n height: HEADER_HEIGHT,\n flexShrink: 0,\n paddingInline: '_lg',\n borderBlockEndWidth: '_xs',\n borderBlockEndStyle: 'solid',\n borderBlockEndColor: '_border',\n backgroundColor: '_surfaceTranslucent',\n backdropFilter: 'blur(12px) saturate(1.5)',\n },\n menuButton: { display: { default: 'inline-flex', [breakpoints.lg]: 'none' }, flexShrink: 0 },\n // 有品牌格与侧栏:宽屏时品牌格贴着起始边;品牌格与内容之间隔开正文的内边距,内容与正文的起始边对齐\n headerWithBrand: {\n paddingInlineStart: { default: null, [breakpoints.lg]: 0 },\n columnGap: { default: null, [breakpoints.lg]: '_xl' },\n },\n brand: { display: 'flex', alignItems: 'center', gap: '_sm', flexShrink: 0, minWidth: 0 },\n // 宽屏时与侧栏同宽,结束边的竖线与侧栏的竖线接上\n brandRail: {\n alignSelf: { default: null, [breakpoints.lg]: 'stretch' },\n boxSizing: 'border-box',\n width: { default: null, [breakpoints.lg]: SIDEBAR_WIDTH },\n paddingInline: { default: null, [breakpoints.lg]: '_lg' },\n borderInlineEndWidth: { default: null, [breakpoints.lg]: '_xs' },\n borderInlineEndStyle: { default: null, [breakpoints.lg]: 'solid' },\n borderInlineEndColor: { default: null, [breakpoints.lg]: '_border' },\n },\n headerContent: { display: 'flex', alignItems: 'center', gap: '_md', flexGrow: 1, minWidth: 0 },\n body: { display: 'grid', flexGrow: 1, gridTemplateColumns: 'minmax(0, 1fr)' },\n withSidebar: {\n gridTemplateColumns: { default: null, [breakpoints.lg]: `${SIDEBAR_WIDTH} minmax(0, 1fr)` },\n },\n withAside: { gridTemplateColumns: { default: null, [breakpoints.xl]: 'minmax(0, 1fr) 14rem' } },\n // 一个属性上两个断点:窄的一档用 lgOnly,不和 xl 重叠(见 breakpoints 的说明)\n withBoth: {\n gridTemplateColumns: {\n default: null,\n [breakpoints.lgOnly]: `${SIDEBAR_WIDTH} minmax(0, 1fr)`,\n [breakpoints.xl]: `${SIDEBAR_WIDTH} minmax(0, 1fr) 14rem`,\n },\n },\n // 两侧栏:粘在顶栏下面、占满剩下的视口高度,内容多时各自滚动,滚到头不带动页面\n rail: {\n position: 'sticky',\n top: layoutVars.stickyOffset,\n boxSizing: 'border-box',\n height: `calc(100dvh - ${layoutVars.stickyOffset})`,\n },\n // 侧栏里的滚动区域:浮层滚动条,滚到头不带动页面;内边距放在内容层上,滚动条贴着侧栏边缘\n railScroll: { height: '_full' },\n railViewport: { overscrollBehavior: 'contain' },\n sidebar: {\n display: { default: 'none', [breakpoints.lg]: 'block' },\n borderInlineEndWidth: '_xs',\n borderInlineEndStyle: 'solid',\n borderInlineEndColor: '_border',\n },\n sidebarContent: { paddingBlock: '_xl', paddingInline: '_md' },\n // 紧凑的侧栏:两边只留一点,图标居中\n sidebarContentCompact: { paddingInline: '_xs' },\n aside: { display: { default: 'none', [breakpoints.xl]: 'block' } },\n asideContent: { paddingBlock: '_xl', paddingInline: '_lg' },\n // 正文由 SkipLink 聚焦(tabindex=-1):不显示焦点框\n main: { minWidth: 0, paddingBlock: '_xl', paddingInline: '_xl', outlineStyle: 'none' },\n // overlay:从顶栏下面弹出、盖在正文上(低于顶栏与顶层渲染的浮层);从起始边滑入,收起后不显示(inert,不能聚焦)。\n // 打开时 translate 是 none:否则抽屉会成为里面 fixed 元素的包含块(同 Drawer)\n overlay: {\n position: 'fixed',\n top: HEADER_HEIGHT,\n bottom: 0,\n insetInlineStart: 0,\n zIndex: 9,\n boxSizing: 'border-box',\n display: 'flex',\n flexDirection: 'column',\n maxWidth: 'calc(100vw - 3rem)',\n // 拖得很窄时内容裁在抽屉里,不溢出到正文上\n overflow: 'hidden',\n backgroundColor: '_surface',\n borderInlineEndWidth: '_xs',\n borderInlineEndStyle: 'solid',\n borderInlineEndColor: '_border',\n boxShadow: '_lg',\n // 收起:滑出去之后再隐藏(visibility 延迟到滑完)\n visibility: 'hidden',\n translate: { default: '-100% 0', ':dir(rtl)': '100% 0' },\n transition: `translate ${t.durations.md} ${t.easings.exit}, visibility 0s linear ${t.durations.md}`,\n },\n // 打开:立刻可见,再滑进来\n overlayOpen: {\n visibility: 'visible',\n translate: 'none',\n transition: `translate ${t.durations.lg} ${t.easings.enter}, visibility 0s`,\n },\n overlayScroll: { flexGrow: 1, minHeight: 0 },\n // overlay 时顶栏的品牌格:宽屏时按抽屉的初始宽度,“菜单”在它的结束边\n brandOverlay: {\n alignSelf: { default: null, [breakpoints.lg]: 'stretch' },\n justifyContent: 'space-between',\n boxSizing: 'border-box',\n paddingInlineStart: { default: null, [breakpoints.lg]: '_lg' },\n paddingInlineEnd: { default: null, [breakpoints.lg]: '_sm' },\n borderInlineEndWidth: { default: null, [breakpoints.lg]: '_xs' },\n borderInlineEndStyle: { default: null, [breakpoints.lg]: 'solid' },\n borderInlineEndColor: { default: null, [breakpoints.lg]: '_border' },\n },\n toggle: { flexShrink: 0 },\n // 侧栏收起成图标栏:栅格的第一列、品牌格都变窄,品牌只剩标志\n withSidebarCollapsed: {\n gridTemplateColumns: { default: null, [breakpoints.lg]: `${RAIL_WIDTH} minmax(0, 1fr)` },\n },\n withBothCollapsed: {\n gridTemplateColumns: {\n default: null,\n [breakpoints.lgOnly]: `${RAIL_WIDTH} minmax(0, 1fr)`,\n [breakpoints.xl]: `${RAIL_WIDTH} minmax(0, 1fr) 14rem`,\n },\n },\n brandRailCollapsed: {\n width: { default: null, [breakpoints.lg]: RAIL_WIDTH },\n justifyContent: { default: null, [breakpoints.lg]: 'center' },\n paddingInline: { default: null, [breakpoints.lg]: '_xs' },\n },\n // 有收起按钮的侧栏:滚动区域占满剩下的高度,按钮在最下面\n sidebarColumn: {\n display: { default: 'none', [breakpoints.lg]: 'flex' },\n flexDirection: 'column',\n },\n sidebarScroll: { flexGrow: 1, minHeight: 0, height: 'auto' },\n collapseBar: {\n display: 'flex',\n justifyContent: 'flex-end',\n flexShrink: 0,\n padding: '_sm',\n borderBlockStartWidth: '_xs',\n borderBlockStartStyle: 'solid',\n borderBlockStartColor: '_borderMuted',\n },\n collapseBarCollapsed: { justifyContent: 'center' },\n // 顶栏的导航:占品牌与右边工具之间的位置;有导航时右边的工具按内容宽、靠结束边\n nav: { display: 'flex', alignItems: 'center', alignSelf: 'stretch', flexGrow: 1, minWidth: 0 },\n headerContentAfterNav: { flexGrow: 0, justifyContent: 'flex-end' },\n // 正文那一列:页签区、正文、页脚上下排,页脚在内容少时也贴着底部\n column: { display: 'flex', flexDirection: 'column', minWidth: 0 },\n mainGrow: { flexGrow: 1 },\n tabs: { paddingBlockStart: '_sm', paddingInline: '_xl' },\n footer: {\n paddingBlock: '_lg',\n paddingInline: '_xl',\n borderBlockStartWidth: '_xs',\n borderBlockStartStyle: 'solid',\n borderBlockStartColor: '_borderMuted',\n color: '_textMuted',\n fontSize: '_sm',\n },\n});\n\n/** AppShell 的动态样式:抽屉的宽度、宽屏时品牌格的宽度(px) */\nexport const appShellDynamicStyles = css.create({\n width: (px: number) => ({ width: px }),\n brandWidth: (px: number) => ({ width: { default: null, [breakpoints.lg]: px } }),\n});\n\n/** 应用外壳:粘性顶栏、左侧栏、正文、右侧栏;窄屏时左侧栏收进抽屉 */\nexport const AppShell = _component(\n ({\n brand,\n header,\n sidebar,\n aside,\n nav,\n tabs,\n footer,\n sidebarMode = 'docked',\n _sidebarOpen: sidebarOpen = _bindable(false),\n _sidebarWidth: sidebarWidth = _bindable(OVERLAY_WIDTH),\n sidebarCollapsible = false,\n _sidebarCollapsed: sidebarCollapsed = _bindable(false),\n skipLink = true,\n pageScrollbar = true,\n propsHeader,\n propsBrand,\n propsSidebar,\n propsMain,\n propsAside,\n propsMenuButton,\n propsSidebarToggle,\n propsResizeHandle,\n propsDrawer,\n propsHeaderContent,\n propsBody,\n propsSidebarScroll,\n propsAsideScroll,\n propsNav,\n propsTabs,\n propsFooter,\n propsCollapse,\n children,\n ...rest\n }: AppShellProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const id = useId();\n const mainId = `${id}-main`;\n const sidebarId = `${id}-sidebar`;\n const overlay = () => sidebarMode === 'overlay';\n const docked = () => hasContent(sidebar) && !overlay();\n const wide = useMediaQuery(WIDE_QUERY);\n // docked 的侧栏收起成图标栏:只在宽屏(窄屏时侧栏在抽屉里,抽屉总是完整的)\n const collapsed = () => docked() && sidebarCollapsible && sidebarCollapsed && wide.value;\n const compact = () => (overlay() && sidebarWidth < OVERLAY_COMPACT_WIDTH) || collapsed();\n NavCompactContext.provide(compact);\n AppShellContext.provide({ collapsed });\n // docked:变宽后侧栏已经显示出来,抽屉没必要再开着\n watch(wide, (isWide) => {\n if (isWide && !overlay()) sidebarOpen = false;\n });\n // 抽屉里点了链接(通常是换页):关闭抽屉;overlay 在宽屏时不关(挡不住正文的主要部分,常要连着换几页)\n function closeOnNavigate(event: MouseEvent) {\n if (overlay() && wide.value) return;\n if (event.target instanceof Element && event.target.closest('a[href]')) sidebarOpen = false;\n }\n\n // overlay:品牌格按初始宽度,拖动抽屉时不变;双击手柄回到它\n // 记住的宽度是拖窄后的紧凑宽度时,品牌格与双击回到的宽度按默认宽度(品牌格太窄会和顶栏的内容叠在一起)\n const remembered = _untrack(sidebarWidth);\n const initialWidth = remembered < OVERLAY_COMPACT_WIDTH ? OVERLAY_WIDTH : remembered;\n // Esc 收起:焦点在抽屉里,收起后它不能聚焦了,回到“菜单”\n let toggleButton = _state.raw<HTMLElement | null>(null);\n function onOverlayKeydown(event: KeyboardEvent) {\n if (event.key !== 'Escape' || event.defaultPrevented) return;\n event.preventDefault();\n sidebarOpen = false;\n toggleButton?.focus();\n }\n\n /** overlay 的“菜单”:开关抽屉,图标随开关切换 */\n const sidebarToggle = () => (\n <Button\n {...mergePartProps<ButtonProps>(\n {\n variant: 'ghost',\n tone: 'neutral',\n size: 'sm',\n icon: sidebarOpen ? PanelLeftClose : PanelLeftOpen,\n sx: appShellStyles.toggle,\n },\n propsSidebarToggle,\n {\n ref: (element: unknown) => (toggleButton = toElement(element)),\n 'aria-expanded': sidebarOpen,\n 'aria-controls': sidebarId,\n onClick: () => (sidebarOpen = !sidebarOpen),\n },\n )}\n >\n {messages.navigationMenu}\n </Button>\n );\n\n return () => (\n <div {...mergeAttrs<'div'>({ sx: [appShellStyles.root, appShellOffset] }, rest)}>\n {skipLink && <SkipLink href={`#${mainId}`} />}\n {pageScrollbar && <PageScrollbar />}\n <header\n {...mergeAttrs<'header'>(\n {\n sx: [\n appShellStyles.header,\n hasContent(brand) && hasContent(sidebar) && appShellStyles.headerWithBrand,\n ],\n },\n propsHeader,\n )}\n >\n {hasContent(sidebar) && !overlay() && (\n <IconButton\n {...mergePartProps<IconButtonProps>(\n {\n sx: appShellStyles.menuButton,\n icon: MenuIcon,\n 'aria-label': messages.navigationOpen,\n },\n propsMenuButton,\n { 'aria-expanded': sidebarOpen, onClick: () => (sidebarOpen = true) },\n )}\n />\n )}\n {hasContent(brand) && (\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n appShellStyles.brand,\n hasContent(sidebar) &&\n (overlay()\n ? [\n appShellStyles.brandOverlay,\n appShellDynamicStyles.brandWidth(initialWidth),\n ]\n : [\n appShellStyles.brandRail,\n collapsed() && appShellStyles.brandRailCollapsed,\n ]),\n ],\n },\n propsBrand,\n )}\n >\n {brand}\n {hasContent(sidebar) && overlay() && sidebarToggle()}\n </div>\n )}\n {!hasContent(brand) && hasContent(sidebar) && overlay() && sidebarToggle()}\n {hasContent(nav) && (\n <div {...mergeAttrs<'div'>({ sx: appShellStyles.nav }, propsNav)}>{nav}</div>\n )}\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n appShellStyles.headerContent,\n hasContent(nav) && appShellStyles.headerContentAfterNav,\n ],\n },\n propsHeaderContent,\n )}\n >\n {header}\n </div>\n </header>\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n appShellStyles.body,\n docked() && hasContent(aside)\n ? collapsed()\n ? appShellStyles.withBothCollapsed\n : appShellStyles.withBoth\n : docked()\n ? collapsed()\n ? appShellStyles.withSidebarCollapsed\n : appShellStyles.withSidebar\n : hasContent(aside) && appShellStyles.withAside,\n ],\n },\n propsBody,\n )}\n >\n {docked() && (\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n appShellStyles.rail,\n appShellStyles.sidebar,\n sidebarCollapsible && appShellStyles.sidebarColumn,\n ],\n },\n propsSidebar,\n )}\n >\n <ScrollArea\n {...mergePartProps<ScrollAreaProps>(\n {\n sx: [\n appShellStyles.railScroll,\n sidebarCollapsible && appShellStyles.sidebarScroll,\n ],\n propsViewport: { sx: appShellStyles.railViewport },\n propsContent: {\n sx: [\n appShellStyles.sidebarContent,\n collapsed() && appShellStyles.sidebarContentCompact,\n ],\n },\n },\n propsSidebarScroll,\n )}\n >\n {sidebar}\n </ScrollArea>\n {sidebarCollapsible && (\n <div\n {...css.attrs(\n appShellStyles.collapseBar,\n collapsed() && appShellStyles.collapseBarCollapsed,\n )}\n >\n <IconButton\n {...mergePartProps<IconButtonProps>(\n {\n icon: sidebarCollapsed ? PanelLeftOpen : PanelLeftClose,\n size: 'sm',\n variant: 'ghost',\n propsIcon: { flipInRtl: true },\n 'aria-label': sidebarCollapsed\n ? messages.sidebarExpand\n : messages.sidebarCollapse,\n },\n propsCollapse,\n { onClick: () => (sidebarCollapsed = !sidebarCollapsed) },\n )}\n />\n </div>\n )}\n </div>\n )}\n <div {...css.attrs(appShellStyles.column)}>\n {hasContent(tabs) && (\n <div {...mergeAttrs<'div'>({ sx: appShellStyles.tabs }, propsTabs)}>{tabs}</div>\n )}\n <main\n {...mergeAttrs<'main'>(\n { sx: [appShellStyles.main, appShellStyles.mainGrow] },\n propsMain,\n {\n id: mainId,\n tabindex: -1,\n },\n )}\n >\n {children}\n </main>\n {hasContent(footer) && (\n <footer {...mergeAttrs<'footer'>({ sx: appShellStyles.footer }, propsFooter)}>\n {footer}\n </footer>\n )}\n </div>\n {hasContent(aside) && (\n <aside\n {...mergeAttrs<'aside'>(\n { sx: [appShellStyles.rail, appShellStyles.aside] },\n propsAside,\n )}\n >\n <ScrollArea\n {...mergePartProps<ScrollAreaProps>(\n {\n sx: appShellStyles.railScroll,\n propsViewport: { sx: appShellStyles.railViewport },\n propsContent: { sx: appShellStyles.asideContent },\n },\n propsAsideScroll,\n )}\n >\n {aside}\n </ScrollArea>\n </aside>\n )}\n </div>\n {hasContent(sidebar) && overlay() && (\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n appShellStyles.overlay,\n sidebarOpen && appShellStyles.overlayOpen,\n appShellDynamicStyles.width(sidebarWidth),\n ],\n },\n propsSidebar,\n {\n id: sidebarId,\n inert: !sidebarOpen,\n onKeydown: onOverlayKeydown,\n onClick: closeOnNavigate,\n },\n )}\n >\n <ScrollArea\n {...mergePartProps<ScrollAreaProps>(\n {\n sx: appShellStyles.overlayScroll,\n propsViewport: { sx: appShellStyles.railViewport },\n propsContent: {\n sx: [\n appShellStyles.sidebarContent,\n compact() && appShellStyles.sidebarContentCompact,\n ],\n },\n },\n propsSidebarScroll,\n )}\n >\n {sidebar}\n </ScrollArea>\n <ResizeHandle\n aria-label={messages.navigationResize}\n {...propsResizeHandle}\n width={sidebarWidth}\n minWidth={OVERLAY_MIN_WIDTH}\n maxWidth={OVERLAY_MAX_WIDTH}\n defaultWidth={initialWidth}\n onWidthChange={(px: number) => (sidebarWidth = px)}\n />\n </div>\n )}\n {docked() && (\n <Drawer\n {...mergePartProps<DrawerProps>(\n { title: messages.navigation, side: 'start' },\n propsDrawer,\n {\n onClick: closeOnNavigate,\n },\n )}\n _open={sidebarOpen}\n >\n {sidebar}\n </Drawer>\n )}\n </div>\n );\n },\n);\n\n/** 未声明的属性落到链接(a)上 */\nexport interface AppShellBrandProps extends PartProps<'a', 'href'> {\n /** 点了去哪儿,一般是首页 */\n href: string;\n /** 标志的图标(品牌色) */\n icon?: IconData;\n /** 标志的图片等,写了时不画 icon */\n logo?: VNodeChild;\n /** 图标的部件属性 */\n propsIcon?: Partial<IconProps>;\n /** 产品名 */\n children?: VNodeChild;\n}\n\n/** AppShellBrand 的样式:root、mark */\nexport const appShellBrandStyles = css.create({\n root: {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '_sm',\n flexShrink: 0,\n color: '_text',\n textDecoration: 'none',\n fontWeight: '_semibold',\n fontSize: '_lg',\n borderRadius: '_sm',\n },\n mark: { color: '_accentText' },\n});\n\n/**\n * 外壳顶栏的品牌:品牌色的标志(装饰,对读屏隐藏)与产品名,名字就是链接的名称,点了回首页(走路由适配)。\n * 放在 AppShell 的 brand 里\n */\nexport const AppShellBrand = _component(\n ({ href, icon, logo, propsIcon, children, ...rest }: AppShellBrandProps) => {\n const router = useRouterLink();\n const shell = AppShellContext.inject(null);\n return () => (\n <a\n {...mergeAttrs<'a'>({ sx: [appShellBrandStyles.root, focusRingStyles.visible] }, rest, {\n href: router.href(href),\n onClick: (event: MouseEvent) => router.onClick(event, href),\n })}\n >\n {logo ??\n (icon && (\n <Icon\n {...mergePartProps<IconProps>({ icon, sx: appShellBrandStyles.mark }, propsIcon)}\n />\n ))}\n {/* 侧栏收起成图标栏时品牌格只放得下标志:产品名留给读屏(仍是链接的名称) */}\n <span {...css.attrs(shell?.collapsed() && visuallyHiddenStyles.hidden)}>{children}</span>\n </a>\n );\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AppShell.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_childrenProp","_vtp_restProps","_vtp_untrack","_vue_shallowRef","_createVNode","_mergeProps","MenuIcon","_vue_unref"],"sources":["../../src/patterns/AppShell.tsx"],"sourcesContent":["/**\n * 应用外壳:粘性顶栏、左侧栏(导航)、正文、右侧栏(本页目录),文档站与后台的整页骨架。\n *\n * <AppShell\n * brand={<AppShellBrand href=\"/\" icon={Boxes}>采购管理平台</AppShellBrand>}\n * header={<><CommandPalette … /><ThemeSwitcher preference={preference} /></>}\n * sidebar={<NavList aria-label=\"文档\">…</NavList>}\n * aside={<TableOfContents items={headings} />}\n * >\n * <Prose>…</Prose>\n * </AppShell>\n *\n * 响应式(宽度按容器档位 sizes.lg / sizes.xl):\n * - 64rem 以上显示左侧栏;更窄时收进从起始边滑出的 Drawer,顶栏最前面出现打开它的按钮。\n * 抽屉里点了链接(换页)、或窗口变宽时自动关闭。\n * - 80rem 以上显示右侧栏;更窄时不显示(目录这类辅助内容)。\n * 两侧栏粘在顶栏下面,内容多时各自滚动(ScrollArea)。顶栏半透明,滚动的正文从下面透出(backdrop-filter)。\n * 整页与两侧栏都是浮层滚动条(PageScrollbar、ScrollArea):不占宽度,滚动时出现。\n *\n * 写了 brand 时,侧栏常驻的宽度下品牌这一格与侧栏同宽,侧栏的竖线一直延伸到顶栏顶端,\n * header 的内容从竖线之后开始、与正文的起始边对齐(本页的页签放在这里)。\n *\n * sidebarMode=\"overlay\":侧栏改为盖在页面上面的抽屉,开不开都不占正文的位置(正文总是整宽,布局不随侧栏变化)。\n * “菜单”按钮固定在顶栏品牌格的结束边,开关都是它(aria-expanded);抽屉从顶栏下面弹出、盖在正文上。\n * 抽屉结束边可以拖动调整宽度(ResizeHandle,也能聚焦后用 ← / →,双击回到初始宽度),父级写 _sidebarWidth={x} 记住;\n * 顶栏的品牌格按初始宽度,拖动时不变。焦点在抽屉里时按 Esc 收起、焦点回到“菜单”;窄屏时点了链接也收起。\n * 拖到比 160px 窄时侧栏变紧凑:里面的 NavList 只显示图标(名称在提示里),小节标题只剩图标(NavCompactContext)。\n * sidebarSide 选抽屉从哪一边滑出:start(默认)、end、top、bottom。start / end 是左右两侧抽屉(随排版方向,从右到左时互换),\n * end 时“菜单”在顶栏结束边;top / bottom 是盖满宽度的上下抽屉,导航的小节排成多列,拖动贴着正文的那条边调整高度\n * (_sidebarHeight 记住)。上下抽屉盖住的正文范围大,点了链接就收起(左右抽屉在宽屏时不收)。\n * 想让用户自己选,用 layout:createLayoutPreference 的结果,侧栏的形式、方向、开关与尺寸都从它读、写回它并存盘,\n * 配合顶栏里的 LayoutSwitcher;写了 layout 时 sidebarMode、sidebarSide、_sidebarOpen 等不再起作用。\n *\n * 品牌格里一般放 AppShellBrand:品牌色的标志与产品名,点了回首页;自己的标志图写在它的 logo 里。\n *\n * 几种布局与区域:\n * - nav:顶栏里品牌后面的导航(NavBar)。只有 nav 时是顶部导航布局,和 sidebar 一起时是混合布局(顶栏一级、侧栏二级)。\n * - tabs:正文上面的页签区(PageTabs,和路由同步用 useRouteTabs);footer:正文下面的页脚,内容少时也贴着底部。\n * - sidebarCollapsible:docked 侧栏底部有“收起侧栏”,收起后只剩图标栏(宽屏时;导航变紧凑、分组悬停弹出,\n * 品牌格只剩标志),父级写 _sidebarCollapsed={x} 记住。\n *\n * 自带 SkipLink(跳到正文);顶栏的高度经 layoutVars.stickyOffset 交给里面的标题,锚点跳转后不被顶栏挡住。\n */\nimport {\n Menu as MenuIcon,\n PanelBottomClose,\n PanelBottomOpen,\n PanelLeftClose,\n PanelLeftOpen,\n PanelRightClose,\n PanelRightOpen,\n PanelTopClose,\n PanelTopOpen,\n} from '@lucide/icons';\nimport { createContext, _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { useId, watch, type VNodeChild } from 'vue';\nimport { Button, type ButtonProps } from '../base/Button';\nimport { useMessages } from '../config/lang/messages';\nimport { patternsMessages } from './messages';\nimport { Icon, type IconData, type IconProps } from '../base/Icon';\nimport { visuallyHiddenStyles } from '../base/VisuallyHidden';\nimport { IconButton, type IconButtonProps } from '../base/IconButton';\nimport { NavCompactContext } from '../navigation/NavList';\nimport { SkipLink } from '../navigation/SkipLink';\nimport { PageScrollbar } from '../layout/PageScrollbar';\nimport { ResizeHandle, type ResizeHandleProps } from '../layout/ResizeHandle';\nimport { ScrollArea, type ScrollAreaProps } from '../layout/ScrollArea';\nimport { Drawer, type DrawerProps } from '../overlay/Drawer';\nimport { useMediaQuery } from '../utils/dom/media-query';\nimport { toElement } from '../utils/dom/element';\nimport { useRouterLink } from '../utils/dom/link';\nimport { hasContent, mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport type { LayoutPreference } from '../config/layout/preference';\nimport { breakpoints, t } from '../config/theme/tokens';\nimport { focusRingStyles } from '../utils/style/styles';\nimport { layoutVars } from '../utils/style/vars';\n\n/** 顶栏高度:也是两侧栏粘住的位置、标题跳转时让开的距离 */\nconst HEADER_HEIGHT = '3rem';\n/** 左侧栏的宽度:顶栏的品牌格与它同宽 */\nconst SIDEBAR_WIDTH = '16rem';\n/** 侧栏收起成图标栏时的宽度 */\nconst RAIL_WIDTH = '3.5rem';\n/** 抽屉式侧栏的默认宽度与可以拖动的范围(px) */\nconst OVERLAY_WIDTH = 256;\n// 最窄可以收到只剩滚动条那么宽\nconst OVERLAY_MIN_WIDTH = 16;\nconst OVERLAY_MAX_WIDTH = 480;\n// 上下两侧抽屉的默认高度与可以拖动的范围(px)\nconst OVERLAY_HEIGHT = 320;\nconst OVERLAY_MIN_HEIGHT = 96;\nconst OVERLAY_MAX_HEIGHT = 640;\n// 比它窄时侧栏只放得下图标:导航变紧凑\nconst OVERLAY_COMPACT_WIDTH = 160;\n// 运行时判断侧栏是否常驻(useMediaQuery 要的是不带 @media 的查询)\nconst WIDE_QUERY = breakpoints.lg.replace('@media ', '');\n\n/** 左侧栏的形式 */\nexport interface AppShellSidebarModes {\n /** 宽屏时占一列、窄屏时收进抽屉(默认) */\n docked: unknown;\n /** 各宽度都是盖在页面上面的抽屉,不占正文的位置,可以收起、拖动宽度 */\n overlay: unknown;\n}\n\n/** 抽屉式侧栏从哪一边滑出(sidebarMode=\"overlay\") */\nexport interface AppShellSidebarSides {\n /** 起始边(左,从右到左排版时是右):默认 */\n start: unknown;\n /** 结束边(右,从右到左排版时是左):“菜单”在顶栏结束边 */\n end: unknown;\n /** 顶栏下面,盖满宽度,导航的小节排成多列 */\n top: unknown;\n /** 窗口底部,盖满宽度,导航的小节排成多列 */\n bottom: unknown;\n}\n\n/** 未声明的属性落到外层 div 上 */\nexport interface AppShellProps extends PartProps<'div'> {\n /** 顶栏起始边的品牌(标志与产品名):有侧栏时这一格与侧栏同宽,侧栏的竖线延伸到顶 */\n brand?: VNodeChild;\n /** 顶栏的内容:搜索、本页的页签、主题切换…(没写 brand 时标志也放这里) */\n header?: VNodeChild;\n /** 左侧栏:站点导航;窄屏时收进抽屉 */\n sidebar?: VNodeChild;\n /** 右侧栏:本页目录等辅助内容;较窄时不显示 */\n aside?: VNodeChild;\n /** 顶栏里品牌后面的导航(NavBar):只有它时是顶部导航布局,和 sidebar 一起时是混合布局(顶栏一级、侧栏二级) */\n nav?: VNodeChild;\n /** 正文上面的页签区(PageTabs:打开过的页面) */\n tabs?: VNodeChild;\n /** 正文下面的页脚(版权、备案号、版本) */\n footer?: VNodeChild;\n /** docked 侧栏底部有“收起侧栏”按钮:收起后只剩图标栏(导航变紧凑,分组的子项悬停弹出) */\n sidebarCollapsible?: boolean;\n /** docked 侧栏收起了:父级可以写 _sidebarCollapsed={x} 记住 */\n _sidebarCollapsed?: boolean;\n /** 左侧栏的形式,默认 docked */\n sidebarMode?: keyof AppShellSidebarModes;\n /** 抽屉式侧栏从哪一边滑出,默认 start;只在 overlay 时有效 */\n sidebarSide?: keyof AppShellSidebarSides;\n /**\n * 用户选的布局(createLayoutPreference 的结果):侧栏的形式、方向、开关与尺寸从它读、写回它并存盘,\n * 写了它,sidebarMode、sidebarSide、_sidebarOpen、_sidebarWidth、_sidebarHeight、_sidebarCollapsed 就不再起作用\n */\n layout?: LayoutPreference;\n /** 侧栏抽屉是否打开(docked 时只在窄屏有抽屉):父级可以写 _sidebarOpen={x} */\n _sidebarOpen?: boolean;\n /**\n * overlay 抽屉的宽度(px),默认 256,范围 16–480(窄到几乎只剩滚动条;比 160 窄时导航只显示图标):\n * 父级写 _sidebarWidth={x} 记住拖动后的宽度\n */\n _sidebarWidth?: number;\n /** 上下两侧抽屉的高度(px),默认 320,范围 96–640:父级写 _sidebarHeight={x} 记住拖动后的高度 */\n _sidebarHeight?: number;\n /** 页面最前面的“跳到正文”链接,默认 true */\n skipLink?: boolean;\n /** 整页用浮层滚动条(PageScrollbar:不占宽度,滚动时出现),默认 true */\n pageScrollbar?: boolean;\n /** 顶栏(header)的部件属性 */\n propsHeader?: PartProps<'header'>;\n /** 品牌格(div)的部件属性 */\n propsBrand?: PartProps<'div'>;\n /** 顶栏里 header 内容那一格(div)的部件属性 */\n propsHeaderContent?: PartProps<'div'>;\n /** 顶栏下面的主体(侧栏、正文、右栏的栅格)的部件属性 */\n propsBody?: PartProps<'div'>;\n /** 左侧栏(div)的部件属性 */\n propsSidebar?: PartProps<'div'>;\n /** 左侧栏里的滚动区域的部件属性(docked 的侧栏与 overlay 的抽屉共用) */\n propsSidebarScroll?: Partial<ScrollAreaProps>;\n /** 正文(main)的部件属性 */\n propsMain?: PartProps<'main'>;\n /** 顶栏里导航那一格的部件属性 */\n propsNav?: PartProps<'div'>;\n /** 页签区的部件属性 */\n propsTabs?: PartProps<'div'>;\n /** 页脚(footer)的部件属性 */\n propsFooter?: PartProps<'footer'>;\n /** “收起侧栏”按钮的部件属性 */\n propsCollapse?: Partial<IconButtonProps>;\n /** 右侧栏(aside)的部件属性 */\n propsAside?: PartProps<'aside'>;\n /** 右侧栏里的滚动区域的部件属性 */\n propsAsideScroll?: Partial<ScrollAreaProps>;\n /** docked 时窄屏打开侧栏的按钮的部件属性 */\n propsMenuButton?: Partial<IconButtonProps>;\n /** overlay 时品牌格里开关抽屉的“菜单”按钮的部件属性 */\n propsSidebarToggle?: Partial<ButtonProps>;\n /** overlay 抽屉宽度调整手柄的部件属性 */\n propsResizeHandle?: Partial<ResizeHandleProps>;\n /** 窄屏时的侧栏抽屉的部件属性 */\n propsDrawer?: Partial<DrawerProps>;\n /** 正文 */\n children?: VNodeChild;\n}\n\n/** AppShell 交给里面的部件的状态:侧栏收起时品牌只剩标志 */\nexport interface AppShellState {\n /** docked 侧栏收起成了图标栏(宽屏时) */\n collapsed: () => boolean;\n}\n\n/** AppShell 的上下文:AppShellBrand 据此在侧栏收起时只显示标志;不在 AppShell 里时为 null */\nexport const AppShellContext = createContext<AppShellState | null>('AppShell');\n\n/** 外壳把顶栏高度交给里面的标题与粘性侧栏 */\nexport const appShellOffset = css.createTheme(layoutVars, { stickyOffset: HEADER_HEIGHT });\n\n/** AppShell 的样式:root、header、menuButton、body、sidebar、main、aside 等 */\nexport const appShellStyles = css.create({\n // 整页的界面基准文字(同 Provider):没有 Provider 时页面是浏览器默认的 16px,与组件的 md 档位对不上\n root: {\n display: 'flex',\n flexDirection: 'column',\n minHeight: '100dvh',\n color: '_text',\n fontSize: '_md',\n lineHeight: '_normal',\n },\n header: {\n position: 'sticky',\n top: 0,\n // 盖住滚动的正文与粘性侧栏;低于浮层(顶层渲染)与 SkipLink\n zIndex: 10,\n boxSizing: 'border-box',\n display: 'flex',\n alignItems: 'center',\n gap: '_md',\n height: HEADER_HEIGHT,\n flexShrink: 0,\n paddingInline: '_lg',\n borderBlockEndWidth: '_xs',\n borderBlockEndStyle: 'solid',\n borderBlockEndColor: '_border',\n backgroundColor: '_surfaceTranslucent',\n backdropFilter: 'blur(12px) saturate(1.5)',\n },\n menuButton: { display: { default: 'inline-flex', [breakpoints.lg]: 'none' }, flexShrink: 0 },\n // 有品牌格与侧栏:宽屏时品牌格贴着起始边;品牌格与内容之间隔开正文的内边距,内容与正文的起始边对齐\n headerWithBrand: {\n paddingInlineStart: { default: null, [breakpoints.lg]: 0 },\n columnGap: { default: null, [breakpoints.lg]: '_xl' },\n },\n brand: { display: 'flex', alignItems: 'center', gap: '_sm', flexShrink: 0, minWidth: 0 },\n // 宽屏时与侧栏同宽,结束边的竖线与侧栏的竖线接上\n brandRail: {\n alignSelf: { default: null, [breakpoints.lg]: 'stretch' },\n boxSizing: 'border-box',\n width: { default: null, [breakpoints.lg]: SIDEBAR_WIDTH },\n paddingInline: { default: null, [breakpoints.lg]: '_lg' },\n borderInlineEndWidth: { default: null, [breakpoints.lg]: '_xs' },\n borderInlineEndStyle: { default: null, [breakpoints.lg]: 'solid' },\n borderInlineEndColor: { default: null, [breakpoints.lg]: '_border' },\n },\n headerContent: { display: 'flex', alignItems: 'center', gap: '_md', flexGrow: 1, minWidth: 0 },\n body: { display: 'grid', flexGrow: 1, gridTemplateColumns: 'minmax(0, 1fr)' },\n withSidebar: {\n gridTemplateColumns: { default: null, [breakpoints.lg]: `${SIDEBAR_WIDTH} minmax(0, 1fr)` },\n },\n withAside: { gridTemplateColumns: { default: null, [breakpoints.xl]: 'minmax(0, 1fr) 14rem' } },\n // 一个属性上两个断点:窄的一档用 lgOnly,不和 xl 重叠(见 breakpoints 的说明)\n withBoth: {\n gridTemplateColumns: {\n default: null,\n [breakpoints.lgOnly]: `${SIDEBAR_WIDTH} minmax(0, 1fr)`,\n [breakpoints.xl]: `${SIDEBAR_WIDTH} minmax(0, 1fr) 14rem`,\n },\n },\n // 两侧栏:粘在顶栏下面、占满剩下的视口高度,内容多时各自滚动,滚到头不带动页面\n rail: {\n position: 'sticky',\n top: layoutVars.stickyOffset,\n boxSizing: 'border-box',\n height: `calc(100dvh - ${layoutVars.stickyOffset})`,\n },\n // 侧栏里的滚动区域:浮层滚动条,滚到头不带动页面;内边距放在内容层上,滚动条贴着侧栏边缘\n railScroll: { height: '_full' },\n railViewport: { overscrollBehavior: 'contain' },\n sidebar: {\n display: { default: 'none', [breakpoints.lg]: 'block' },\n borderInlineEndWidth: '_xs',\n borderInlineEndStyle: 'solid',\n borderInlineEndColor: '_border',\n },\n sidebarContent: { paddingBlock: '_xl', paddingInline: '_md' },\n // 紧凑的侧栏:两边只留一点,图标居中\n sidebarContentCompact: { paddingInline: '_xs' },\n aside: { display: { default: 'none', [breakpoints.xl]: 'block' } },\n asideContent: { paddingBlock: '_xl', paddingInline: '_lg' },\n // 正文由 SkipLink 聚焦(tabindex=-1):不显示焦点框\n main: { minWidth: 0, paddingBlock: '_xl', paddingInline: '_xl', outlineStyle: 'none' },\n // overlay:从顶栏下面弹出、盖在正文上(低于顶栏与顶层渲染的浮层);各方向的位置见 overlayStart 等,收起后不显示(inert,不能聚焦)。\n // 打开时 translate 是 none:否则抽屉会成为里面 fixed 元素的包含块(同 Drawer)\n overlay: {\n position: 'fixed',\n zIndex: 9,\n boxSizing: 'border-box',\n display: 'flex',\n flexDirection: 'column',\n // 拖得很窄时内容裁在抽屉里,不溢出到正文上\n overflow: 'hidden',\n backgroundColor: '_surface',\n boxShadow: '_lg',\n // 收起:滑出去之后再隐藏(visibility 延迟到滑完)\n visibility: 'hidden',\n transition: `translate ${t.durations.md} ${t.easings.exit}, visibility 0s linear ${t.durations.md}`,\n },\n // 左右两侧:顶栏下面到窗口底部;位置用逻辑属性,translate 是物理方向,用 :dir() 区分\n overlayStart: {\n top: HEADER_HEIGHT,\n bottom: 0,\n insetInlineStart: 0,\n maxWidth: 'calc(100vw - 3rem)',\n borderInlineEndWidth: '_xs',\n borderInlineEndStyle: 'solid',\n borderInlineEndColor: '_border',\n translate: { default: '-100% 0', ':dir(rtl)': '100% 0' },\n },\n overlayEnd: {\n top: HEADER_HEIGHT,\n bottom: 0,\n insetInlineEnd: 0,\n maxWidth: 'calc(100vw - 3rem)',\n borderInlineStartWidth: '_xs',\n borderInlineStartStyle: 'solid',\n borderInlineStartColor: '_border',\n translate: { default: '100% 0', ':dir(rtl)': '-100% 0' },\n },\n // 上下两侧:盖满宽度,高度由 _sidebarHeight 定,最高留出一截正文\n overlayTop: {\n top: HEADER_HEIGHT,\n insetInlineStart: 0,\n insetInlineEnd: 0,\n maxHeight: `calc(100dvh - ${HEADER_HEIGHT} - 3rem)`,\n borderBlockEndWidth: '_xs',\n borderBlockEndStyle: 'solid',\n borderBlockEndColor: '_border',\n translate: '0 -100%',\n },\n overlayBottom: {\n bottom: 0,\n insetInlineStart: 0,\n insetInlineEnd: 0,\n maxHeight: `calc(100dvh - ${HEADER_HEIGHT} - 3rem)`,\n borderBlockStartWidth: '_xs',\n borderBlockStartStyle: 'solid',\n borderBlockStartColor: '_border',\n translate: '0 100%',\n },\n // 打开:立刻可见,再滑进来\n overlayOpen: {\n visibility: 'visible',\n translate: 'none',\n transition: `translate ${t.durations.lg} ${t.easings.enter}, visibility 0s`,\n },\n overlayScroll: { flexGrow: 1, minHeight: 0 },\n // 上下抽屉:导航的小节排成多列(CSS 多栏,每个小节不被拆开,见 NavSection)\n sidebarContentColumns: { columnWidth: '14rem', columnGap: '_xl' },\n // overlay 时顶栏的品牌格:宽屏时按抽屉的初始宽度,“菜单”在它的结束边(只有抽屉在起始边时;其他方向品牌格不变)\n brandOverlay: {\n alignSelf: { default: null, [breakpoints.lg]: 'stretch' },\n justifyContent: 'space-between',\n boxSizing: 'border-box',\n paddingInlineStart: { default: null, [breakpoints.lg]: '_lg' },\n paddingInlineEnd: { default: null, [breakpoints.lg]: '_sm' },\n borderInlineEndWidth: { default: null, [breakpoints.lg]: '_xs' },\n borderInlineEndStyle: { default: null, [breakpoints.lg]: 'solid' },\n borderInlineEndColor: { default: null, [breakpoints.lg]: '_border' },\n },\n toggle: { flexShrink: 0 },\n // 侧栏收起成图标栏:栅格的第一列、品牌格都变窄,品牌只剩标志\n withSidebarCollapsed: {\n gridTemplateColumns: { default: null, [breakpoints.lg]: `${RAIL_WIDTH} minmax(0, 1fr)` },\n },\n withBothCollapsed: {\n gridTemplateColumns: {\n default: null,\n [breakpoints.lgOnly]: `${RAIL_WIDTH} minmax(0, 1fr)`,\n [breakpoints.xl]: `${RAIL_WIDTH} minmax(0, 1fr) 14rem`,\n },\n },\n brandRailCollapsed: {\n width: { default: null, [breakpoints.lg]: RAIL_WIDTH },\n justifyContent: { default: null, [breakpoints.lg]: 'center' },\n paddingInline: { default: null, [breakpoints.lg]: '_xs' },\n },\n // 有收起按钮的侧栏:滚动区域占满剩下的高度,按钮在最下面\n sidebarColumn: {\n display: { default: 'none', [breakpoints.lg]: 'flex' },\n flexDirection: 'column',\n },\n sidebarScroll: { flexGrow: 1, minHeight: 0, height: 'auto' },\n collapseBar: {\n display: 'flex',\n justifyContent: 'flex-end',\n flexShrink: 0,\n padding: '_sm',\n borderBlockStartWidth: '_xs',\n borderBlockStartStyle: 'solid',\n borderBlockStartColor: '_borderMuted',\n },\n collapseBarCollapsed: { justifyContent: 'center' },\n // 顶栏的导航:占品牌与右边工具之间的位置;有导航时右边的工具按内容宽、靠结束边\n nav: { display: 'flex', alignItems: 'center', alignSelf: 'stretch', flexGrow: 1, minWidth: 0 },\n headerContentAfterNav: { flexGrow: 0, justifyContent: 'flex-end' },\n // 正文那一列:页签区、正文、页脚上下排,页脚在内容少时也贴着底部\n column: { display: 'flex', flexDirection: 'column', minWidth: 0 },\n mainGrow: { flexGrow: 1 },\n tabs: { paddingBlockStart: '_sm', paddingInline: '_xl' },\n footer: {\n paddingBlock: '_lg',\n paddingInline: '_xl',\n borderBlockStartWidth: '_xs',\n borderBlockStartStyle: 'solid',\n borderBlockStartColor: '_borderMuted',\n color: '_textMuted',\n fontSize: '_sm',\n },\n});\n\n/** AppShell 的动态样式:抽屉的宽度与高度、宽屏时品牌格的宽度(px) */\nexport const appShellDynamicStyles = css.create({\n width: (px: number) => ({ width: px }),\n height: (px: number) => ({ height: px }),\n brandWidth: (px: number) => ({ width: { default: null, [breakpoints.lg]: px } }),\n});\n\n/** 应用外壳:粘性顶栏、左侧栏、正文、右侧栏;窄屏时左侧栏收进抽屉 */\nexport const AppShell = _component(\n ({\n brand,\n header,\n sidebar,\n aside,\n nav,\n tabs,\n footer,\n sidebarMode = 'docked',\n sidebarSide = 'start',\n layout,\n _sidebarOpen: sidebarOpen = _bindable(false),\n _sidebarWidth: sidebarWidth = _bindable(OVERLAY_WIDTH),\n _sidebarHeight: sidebarHeight = _bindable(OVERLAY_HEIGHT),\n sidebarCollapsible = false,\n _sidebarCollapsed: sidebarCollapsed = _bindable(false),\n skipLink = true,\n pageScrollbar = true,\n propsHeader,\n propsBrand,\n propsSidebar,\n propsMain,\n propsAside,\n propsMenuButton,\n propsSidebarToggle,\n propsResizeHandle,\n propsDrawer,\n propsHeaderContent,\n propsBody,\n propsSidebarScroll,\n propsAsideScroll,\n propsNav,\n propsTabs,\n propsFooter,\n propsCollapse,\n children,\n ...rest\n }: AppShellProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const id = useId();\n const mainId = `${id}-main`;\n const sidebarId = `${id}-sidebar`;\n // 写了 layout 时各项从它读、写回它;没写时读、写自己的绑定\n const mode = () => (layout ? layout.mode : sidebarMode);\n const side = () => (layout ? layout.side : sidebarSide);\n const isOpen = () => (layout ? layout.open : sidebarOpen);\n const setOpen = (value: boolean) => {\n if (layout) layout.open = value;\n else sidebarOpen = value;\n };\n const widthOf = () => (layout ? layout.width : sidebarWidth);\n const setWidth = (px: number) => {\n if (layout) layout.width = px;\n else sidebarWidth = px;\n };\n const heightOf = () => (layout ? layout.height : sidebarHeight);\n const setHeight = (px: number) => {\n if (layout) layout.height = px;\n else sidebarHeight = px;\n };\n const isCollapsed = () => (layout ? layout.collapsed : sidebarCollapsed);\n const setCollapsed = (value: boolean) => {\n if (layout) layout.collapsed = value;\n else sidebarCollapsed = value;\n };\n\n const overlay = () => mode() === 'overlay';\n const docked = () => hasContent(sidebar) && !overlay();\n // 左右两侧的抽屉调宽度,上下两侧的调高度\n const lateral = () => side() === 'start' || side() === 'end';\n const wide = useMediaQuery(WIDE_QUERY);\n // docked 的侧栏收起成图标栏:只在宽屏(窄屏时侧栏在抽屉里,抽屉总是完整的)\n const collapsed = () => docked() && sidebarCollapsible && isCollapsed() && wide.value;\n const compact = () =>\n (overlay() && lateral() && widthOf() < OVERLAY_COMPACT_WIDTH) || collapsed();\n NavCompactContext.provide(compact);\n AppShellContext.provide({ collapsed });\n // docked:变宽后侧栏已经显示出来,抽屉没必要再开着\n watch(wide, (isWide) => {\n if (isWide && !overlay()) setOpen(false);\n });\n // 抽屉里点了链接(通常是换页):关闭抽屉;左右抽屉在宽屏时不关(挡不住正文的主要部分,常要连着换几页),上下抽屉盖住的多,总是关\n function closeOnNavigate(event: MouseEvent) {\n if (overlay() && lateral() && wide.value) return;\n if (event.target instanceof Element && event.target.closest('a[href]')) setOpen(false);\n }\n\n // overlay:品牌格按初始宽度,拖动抽屉时不变;双击手柄回到它\n // 记住的宽度是拖窄后的紧凑宽度时,品牌格与双击回到的宽度按默认宽度(品牌格太窄会和顶栏的内容叠在一起)\n const remembered = _untrack(widthOf());\n const initialWidth = remembered < OVERLAY_COMPACT_WIDTH ? OVERLAY_WIDTH : remembered;\n // Esc 收起:焦点在抽屉里,收起后它不能聚焦了,回到“菜单”\n let toggleButton = _state.raw<HTMLElement | null>(null);\n function onOverlayKeydown(event: KeyboardEvent) {\n if (event.key !== 'Escape' || event.defaultPrevented) return;\n event.preventDefault();\n setOpen(false);\n toggleButton?.focus();\n }\n\n // 抽屉在结束边时,“菜单”放在顶栏的结束边;其他方向在品牌格里\n const toggleAtEnd = () => overlay() && side() === 'end';\n const toggleIcon = () => {\n const open = isOpen();\n switch (side()) {\n case 'end':\n return open ? PanelRightClose : PanelRightOpen;\n case 'top':\n return open ? PanelTopClose : PanelTopOpen;\n case 'bottom':\n return open ? PanelBottomClose : PanelBottomOpen;\n default:\n return open ? PanelLeftClose : PanelLeftOpen;\n }\n };\n\n /** overlay 的“菜单”:开关抽屉,图标随开关和方向切换 */\n const sidebarToggle = () => (\n <Button\n {...mergePartProps<ButtonProps>(\n {\n variant: 'ghost',\n tone: 'neutral',\n size: 'sm',\n icon: toggleIcon(),\n sx: appShellStyles.toggle,\n },\n propsSidebarToggle,\n {\n ref: (element: unknown) => (toggleButton = toElement(element)),\n 'aria-expanded': isOpen(),\n 'aria-controls': sidebarId,\n onClick: () => setOpen(!isOpen()),\n },\n )}\n >\n {messages.navigationMenu}\n </Button>\n );\n\n return () => (\n <div {...mergeAttrs<'div'>({ sx: [appShellStyles.root, appShellOffset] }, rest)}>\n {skipLink && <SkipLink href={`#${mainId}`} />}\n {pageScrollbar && <PageScrollbar />}\n <header\n {...mergeAttrs<'header'>(\n {\n sx: [\n appShellStyles.header,\n hasContent(brand) && hasContent(sidebar) && appShellStyles.headerWithBrand,\n ],\n },\n propsHeader,\n )}\n >\n {hasContent(sidebar) && !overlay() && (\n <IconButton\n {...mergePartProps<IconButtonProps>(\n {\n sx: appShellStyles.menuButton,\n icon: MenuIcon,\n 'aria-label': messages.navigationOpen,\n },\n propsMenuButton,\n { 'aria-expanded': isOpen(), onClick: () => setOpen(true) },\n )}\n />\n )}\n {hasContent(brand) && (\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n appShellStyles.brand,\n hasContent(sidebar) &&\n (overlay()\n ? side() === 'start' && [\n appShellStyles.brandOverlay,\n appShellDynamicStyles.brandWidth(initialWidth),\n ]\n : [\n appShellStyles.brandRail,\n collapsed() && appShellStyles.brandRailCollapsed,\n ]),\n ],\n },\n propsBrand,\n )}\n >\n {brand}\n {hasContent(sidebar) && overlay() && !toggleAtEnd() && sidebarToggle()}\n </div>\n )}\n {!hasContent(brand) &&\n hasContent(sidebar) &&\n overlay() &&\n !toggleAtEnd() &&\n sidebarToggle()}\n {hasContent(nav) && (\n <div {...mergeAttrs<'div'>({ sx: appShellStyles.nav }, propsNav)}>{nav}</div>\n )}\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n appShellStyles.headerContent,\n hasContent(nav) && appShellStyles.headerContentAfterNav,\n ],\n },\n propsHeaderContent,\n )}\n >\n {header}\n </div>\n {hasContent(sidebar) && toggleAtEnd() && sidebarToggle()}\n </header>\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n appShellStyles.body,\n docked() && hasContent(aside)\n ? collapsed()\n ? appShellStyles.withBothCollapsed\n : appShellStyles.withBoth\n : docked()\n ? collapsed()\n ? appShellStyles.withSidebarCollapsed\n : appShellStyles.withSidebar\n : hasContent(aside) && appShellStyles.withAside,\n ],\n },\n propsBody,\n )}\n >\n {docked() && (\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n appShellStyles.rail,\n appShellStyles.sidebar,\n sidebarCollapsible && appShellStyles.sidebarColumn,\n ],\n },\n propsSidebar,\n )}\n >\n <ScrollArea\n {...mergePartProps<ScrollAreaProps>(\n {\n sx: [\n appShellStyles.railScroll,\n sidebarCollapsible && appShellStyles.sidebarScroll,\n ],\n propsViewport: { sx: appShellStyles.railViewport },\n propsContent: {\n sx: [\n appShellStyles.sidebarContent,\n collapsed() && appShellStyles.sidebarContentCompact,\n ],\n },\n },\n propsSidebarScroll,\n )}\n >\n {sidebar}\n </ScrollArea>\n {sidebarCollapsible && (\n <div\n {...css.attrs(\n appShellStyles.collapseBar,\n collapsed() && appShellStyles.collapseBarCollapsed,\n )}\n >\n <IconButton\n {...mergePartProps<IconButtonProps>(\n {\n icon: isCollapsed() ? PanelLeftOpen : PanelLeftClose,\n size: 'sm',\n variant: 'ghost',\n propsIcon: { flipInRtl: true },\n 'aria-label': isCollapsed()\n ? messages.sidebarExpand\n : messages.sidebarCollapse,\n },\n propsCollapse,\n { onClick: () => setCollapsed(!isCollapsed()) },\n )}\n />\n </div>\n )}\n </div>\n )}\n <div {...css.attrs(appShellStyles.column)}>\n {hasContent(tabs) && (\n <div {...mergeAttrs<'div'>({ sx: appShellStyles.tabs }, propsTabs)}>{tabs}</div>\n )}\n <main\n {...mergeAttrs<'main'>(\n { sx: [appShellStyles.main, appShellStyles.mainGrow] },\n propsMain,\n {\n id: mainId,\n tabindex: -1,\n },\n )}\n >\n {children}\n </main>\n {hasContent(footer) && (\n <footer {...mergeAttrs<'footer'>({ sx: appShellStyles.footer }, propsFooter)}>\n {footer}\n </footer>\n )}\n </div>\n {hasContent(aside) && (\n <aside\n {...mergeAttrs<'aside'>(\n { sx: [appShellStyles.rail, appShellStyles.aside] },\n propsAside,\n )}\n >\n <ScrollArea\n {...mergePartProps<ScrollAreaProps>(\n {\n sx: appShellStyles.railScroll,\n propsViewport: { sx: appShellStyles.railViewport },\n propsContent: { sx: appShellStyles.asideContent },\n },\n propsAsideScroll,\n )}\n >\n {aside}\n </ScrollArea>\n </aside>\n )}\n </div>\n {hasContent(sidebar) && overlay() && (\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n appShellStyles.overlay,\n side() === 'start' && appShellStyles.overlayStart,\n side() === 'end' && appShellStyles.overlayEnd,\n side() === 'top' && appShellStyles.overlayTop,\n side() === 'bottom' && appShellStyles.overlayBottom,\n isOpen() && appShellStyles.overlayOpen,\n lateral()\n ? appShellDynamicStyles.width(widthOf())\n : appShellDynamicStyles.height(heightOf()),\n ],\n },\n propsSidebar,\n {\n id: sidebarId,\n inert: !isOpen(),\n onKeydown: onOverlayKeydown,\n onClick: closeOnNavigate,\n },\n )}\n >\n <ScrollArea\n {...mergePartProps<ScrollAreaProps>(\n {\n sx: appShellStyles.overlayScroll,\n propsViewport: { sx: appShellStyles.railViewport },\n propsContent: {\n sx: [\n appShellStyles.sidebarContent,\n compact() && appShellStyles.sidebarContentCompact,\n !lateral() && appShellStyles.sidebarContentColumns,\n ],\n },\n },\n propsSidebarScroll,\n )}\n >\n {sidebar}\n </ScrollArea>\n {lateral() ? (\n <ResizeHandle\n aria-label={messages.navigationResize}\n {...propsResizeHandle}\n edge={side() === 'end' ? 'start' : 'end'}\n width={widthOf()}\n minWidth={OVERLAY_MIN_WIDTH}\n maxWidth={OVERLAY_MAX_WIDTH}\n defaultWidth={initialWidth}\n onWidthChange={setWidth}\n />\n ) : (\n <ResizeHandle\n aria-label={messages.navigationResizeHeight}\n {...propsResizeHandle}\n orientation=\"horizontal\"\n edge={side() === 'top' ? 'end' : 'start'}\n width={heightOf()}\n minWidth={OVERLAY_MIN_HEIGHT}\n maxWidth={OVERLAY_MAX_HEIGHT}\n defaultWidth={OVERLAY_HEIGHT}\n onWidthChange={setHeight}\n />\n )}\n </div>\n )}\n {docked() && (\n <Drawer\n {...mergePartProps<DrawerProps>(\n { title: messages.navigation, side: 'start' },\n propsDrawer,\n {\n onClick: closeOnNavigate,\n },\n )}\n _open={isOpen()}\n onUpdate:_open={setOpen}\n >\n {sidebar}\n </Drawer>\n )}\n </div>\n );\n },\n);\n\n/** 未声明的属性落到链接(a)上 */\nexport interface AppShellBrandProps extends PartProps<'a', 'href'> {\n /** 点了去哪儿,一般是首页 */\n href: string;\n /** 标志的图标(品牌色) */\n icon?: IconData;\n /** 标志的图片等,写了时不画 icon */\n logo?: VNodeChild;\n /** 图标的部件属性 */\n propsIcon?: Partial<IconProps>;\n /** 产品名 */\n children?: VNodeChild;\n}\n\n/** AppShellBrand 的样式:root、mark */\nexport const appShellBrandStyles = css.create({\n root: {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '_sm',\n flexShrink: 0,\n color: '_text',\n textDecoration: 'none',\n fontWeight: '_semibold',\n fontSize: '_lg',\n borderRadius: '_sm',\n },\n mark: { color: '_accentText' },\n});\n\n/**\n * 外壳顶栏的品牌:品牌色的标志(装饰,对读屏隐藏)与产品名,名字就是链接的名称,点了回首页(走路由适配)。\n * 放在 AppShell 的 brand 里\n */\nexport const AppShellBrand = _component(\n ({ href, icon, logo, propsIcon, children, ...rest }: AppShellBrandProps) => {\n const router = useRouterLink();\n const shell = AppShellContext.inject(null);\n return () => (\n <a\n {...mergeAttrs<'a'>({ sx: [appShellBrandStyles.root, focusRingStyles.visible] }, rest, {\n href: router.href(href),\n onClick: (event: MouseEvent) => router.onClick(event, href),\n })}\n >\n {logo ??\n (icon && (\n <Icon\n {...mergePartProps<IconProps>({ icon, sx: appShellBrandStyles.mark }, propsIcon)}\n />\n ))}\n {/* 侧栏收起成图标栏时品牌格只放得下标志:产品名留给读屏(仍是链接的名称) */}\n <span {...css.attrs(shell?.collapsed() && visuallyHiddenStyles.hidden)}>{children}</span>\n </a>\n );\n },\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';
|
|
2
|
+
import type { VNodeChild } from 'vue';
|
|
3
|
+
import { type PartProps } from '../utils/props';
|
|
4
|
+
/** 未声明的属性落到外层 div 上 */
|
|
5
|
+
export interface AuthLayoutProps extends PartProps<'div'> {
|
|
6
|
+
/** 品牌区:宽屏在左半边,窄屏在顶部;不写时不分栏 */
|
|
7
|
+
hero?: VNodeChild;
|
|
8
|
+
/** 表单这一半结束边上角的工具(主题切换、语言切换) */
|
|
9
|
+
tools?: VNodeChild;
|
|
10
|
+
/** 品牌区(div)的部件属性 */
|
|
11
|
+
propsHero?: PartProps<'div'>;
|
|
12
|
+
/** 表单区(main)的部件属性 */
|
|
13
|
+
propsMain?: PartProps<'main'>;
|
|
14
|
+
/** 工具区(div)的部件属性 */
|
|
15
|
+
propsTools?: PartProps<'div'>;
|
|
16
|
+
/** 表单 */
|
|
17
|
+
children?: VNodeChild;
|
|
18
|
+
}
|
|
19
|
+
/** AuthLayout 的样式:root、split、hero、main、tools */
|
|
20
|
+
export declare const authLayoutStyles: Readonly<{
|
|
21
|
+
hero: css.CompiledStyles;
|
|
22
|
+
main: css.CompiledStyles;
|
|
23
|
+
root: css.CompiledStyles;
|
|
24
|
+
split: css.CompiledStyles;
|
|
25
|
+
tools: css.CompiledStyles;
|
|
26
|
+
}>;
|
|
27
|
+
/** 认证页的整页版式:左边品牌区、右边表单;窄屏品牌区收成顶部一块 */
|
|
28
|
+
export declare const AuthLayout: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<AuthLayoutProps>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { breakpoints } from "../config/theme/tokens.js";
|
|
2
|
+
import { hasContent, mergeAttrs } from "../utils/props.js";
|
|
3
|
+
import { createVNode } from "vue";
|
|
4
|
+
import { _component, childrenProp, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
5
|
+
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
6
|
+
//#region src/patterns/AuthLayout.tsx
|
|
7
|
+
/** 未声明的属性落到外层 div 上 */
|
|
8
|
+
/** AuthLayout 的样式:root、split、hero、main、tools */
|
|
9
|
+
var authLayoutStyles = css.create({
|
|
10
|
+
root: {
|
|
11
|
+
display: "grid",
|
|
12
|
+
gridTemplateColumns: "minmax(0, 1fr)",
|
|
13
|
+
gridTemplateRows: "minmax(0, 1fr)",
|
|
14
|
+
minHeight: "100dvh",
|
|
15
|
+
backgroundColor: "_background",
|
|
16
|
+
color: "_text"
|
|
17
|
+
},
|
|
18
|
+
split: {
|
|
19
|
+
gridTemplateColumns: {
|
|
20
|
+
default: "minmax(0, 1fr)",
|
|
21
|
+
[breakpoints.lg]: "minmax(0, 1.15fr) minmax(0, 1fr)"
|
|
22
|
+
},
|
|
23
|
+
gridTemplateRows: {
|
|
24
|
+
default: "auto minmax(0, 1fr)",
|
|
25
|
+
[breakpoints.lg]: "minmax(0, 1fr)"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
hero: { minHeight: {
|
|
29
|
+
default: "auto",
|
|
30
|
+
[breakpoints.lg]: "100dvh"
|
|
31
|
+
} },
|
|
32
|
+
main: {
|
|
33
|
+
position: "relative",
|
|
34
|
+
display: "flex",
|
|
35
|
+
alignItems: {
|
|
36
|
+
default: "flex-start",
|
|
37
|
+
[breakpoints.lg]: "center"
|
|
38
|
+
},
|
|
39
|
+
justifyContent: "center",
|
|
40
|
+
boxSizing: "border-box",
|
|
41
|
+
padding: "_lg",
|
|
42
|
+
paddingTop: {
|
|
43
|
+
default: "_xl",
|
|
44
|
+
[breakpoints.lg]: "_lg"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
tools: {
|
|
48
|
+
position: "absolute",
|
|
49
|
+
top: "_md",
|
|
50
|
+
insetInlineEnd: "_md",
|
|
51
|
+
zIndex: 1
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
/** 认证页的整页版式:左边品牌区、右边表单;窄屏品牌区收成顶部一块 */
|
|
55
|
+
var AuthLayout = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
56
|
+
const hero = readonlyProp(_props, "hero");
|
|
57
|
+
const tools = readonlyProp(_props, "tools");
|
|
58
|
+
const propsHero = readonlyProp(_props, "propsHero");
|
|
59
|
+
const propsMain = readonlyProp(_props, "propsMain");
|
|
60
|
+
const propsTools = readonlyProp(_props, "propsTools");
|
|
61
|
+
const children = childrenProp(_props);
|
|
62
|
+
const rest = restProps(_props, _ctx, false);
|
|
63
|
+
return () => createVNode("div", mergeAttrs({ sx: [authLayoutStyles.root, hasContent(hero.value) && authLayoutStyles.split] }, rest), [hasContent(hero.value) && createVNode("div", mergeAttrs({ sx: authLayoutStyles.hero }, propsHero.value), [hero.value]), createVNode("main", mergeAttrs({ sx: authLayoutStyles.main }, propsMain.value), [hasContent(tools.value) && createVNode("div", mergeAttrs({ sx: authLayoutStyles.tools }, propsTools.value), [tools.value]), children.value])]);
|
|
64
|
+
}, {
|
|
65
|
+
name: "AuthLayout",
|
|
66
|
+
props: [
|
|
67
|
+
"hero",
|
|
68
|
+
"tools",
|
|
69
|
+
"propsHero",
|
|
70
|
+
"propsMain",
|
|
71
|
+
"propsTools"
|
|
72
|
+
]
|
|
73
|
+
});
|
|
74
|
+
//#endregion
|
|
75
|
+
export { AuthLayout, authLayoutStyles };
|
|
76
|
+
|
|
77
|
+
//# sourceMappingURL=AuthLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthLayout.js","names":["_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_createVNode"],"sources":["../../src/patterns/AuthLayout.tsx"],"sourcesContent":["/**\n * 登录、注册、找回密码这类认证页的整页版式:宽屏时左边是品牌区(hero)、右边是表单,窄屏时品牌区收成顶部一块、表单紧跟其后。\n *\n * <AuthLayout hero={<LoginHero />} tools={<ThemeSwitcher preference={preference} />}>\n * <Card>…登录表单…</Card>\n * </AuthLayout>\n *\n * hero 是品牌区的内容(标志、口号、背景动画),铺满它那一半;不写 hero 时表单居中,不分栏。tools 放在表单这一半的结束边上角\n * (主题切换、语言切换)。表单区把 children 垂直居中(宽屏)或贴近顶部(窄屏,登录卡片不被品牌区挤到第二屏)。\n * 分栏的断点是 sizes.lg(64rem),与 AppShell 一致。\n *\n * 部件:Root(div)、Hero(div)、Main(main)、Tools(div)。\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 { breakpoints } from '../config/theme/tokens';\nimport { hasContent, mergeAttrs, type PartProps } from '../utils/props';\n\n/** 未声明的属性落到外层 div 上 */\nexport interface AuthLayoutProps extends PartProps<'div'> {\n /** 品牌区:宽屏在左半边,窄屏在顶部;不写时不分栏 */\n hero?: VNodeChild;\n /** 表单这一半结束边上角的工具(主题切换、语言切换) */\n tools?: VNodeChild;\n /** 品牌区(div)的部件属性 */\n propsHero?: PartProps<'div'>;\n /** 表单区(main)的部件属性 */\n propsMain?: PartProps<'main'>;\n /** 工具区(div)的部件属性 */\n propsTools?: PartProps<'div'>;\n /** 表单 */\n children?: VNodeChild;\n}\n\n/** AuthLayout 的样式:root、split、hero、main、tools */\nexport const authLayoutStyles = css.create({\n root: {\n display: 'grid',\n gridTemplateColumns: 'minmax(0, 1fr)',\n gridTemplateRows: 'minmax(0, 1fr)',\n minHeight: '100dvh',\n backgroundColor: '_background',\n color: '_text',\n },\n // 有品牌区:窄屏上下两行(品牌区按内容高、表单占剩下的),宽屏左右两栏\n split: {\n gridTemplateColumns: {\n default: 'minmax(0, 1fr)',\n [breakpoints.lg]: 'minmax(0, 1.15fr) minmax(0, 1fr)',\n },\n gridTemplateRows: { default: 'auto minmax(0, 1fr)', [breakpoints.lg]: 'minmax(0, 1fr)' },\n },\n hero: { minHeight: { default: 'auto', [breakpoints.lg]: '100dvh' } },\n main: {\n position: 'relative',\n display: 'flex',\n alignItems: { default: 'flex-start', [breakpoints.lg]: 'center' },\n justifyContent: 'center',\n boxSizing: 'border-box',\n padding: '_lg',\n paddingTop: { default: '_xl', [breakpoints.lg]: '_lg' },\n },\n tools: { position: 'absolute', top: '_md', insetInlineEnd: '_md', zIndex: 1 },\n});\n\n/** 认证页的整页版式:左边品牌区、右边表单;窄屏品牌区收成顶部一块 */\nexport const AuthLayout = _component(\n ({ hero, tools, propsHero, propsMain, propsTools, children, ...rest }: AuthLayoutProps) =>\n () => (\n <div\n {...mergeAttrs<'div'>(\n { sx: [authLayoutStyles.root, hasContent(hero) && authLayoutStyles.split] },\n rest,\n )}\n >\n {hasContent(hero) && (\n <div {...mergeAttrs<'div'>({ sx: authLayoutStyles.hero }, propsHero)}>{hero}</div>\n )}\n <main {...mergeAttrs<'main'>({ sx: authLayoutStyles.main }, propsMain)}>\n {hasContent(tools) && (\n <div {...mergeAttrs<'div'>({ sx: authLayoutStyles.tools }, propsTools)}>{tools}</div>\n )}\n {children}\n </main>\n </div>\n ),\n);\n"],"mappings":";;;;;;;;AAoCA,IAAa,mBAAmB,IAAI,OAAO;CACzC,MAAM;EACJ,SAAS;EACT,qBAAqB;EACrB,kBAAkB;EAClB,WAAW;EACX,iBAAiB;EACjB,OAAO;CACT;CAEA,OAAO;EACL,qBAAqB;GACnB,SAAS;IACR,YAAY,KAAK;EACpB;EACA,kBAAkB;GAAE,SAAS;IAAwB,YAAY,KAAK;EAAiB;CACzF;CACA,MAAM,EAAE,WAAW;EAAE,SAAS;GAAS,YAAY,KAAK;CAAS,EAAE;CACnE,MAAM;EACJ,UAAU;EACV,SAAS;EACT,YAAY;GAAE,SAAS;IAAe,YAAY,KAAK;EAAS;EAChE,gBAAgB;EAChB,WAAW;EACX,SAAS;EACT,YAAY;GAAE,SAAS;IAAQ,YAAY,KAAK;EAAM;CACxD;CACA,OAAO;EAAE,UAAU;EAAY,KAAK;EAAO,gBAAgB;EAAO,QAAQ;CAAE;AAC9E,CAAC;;AAGD,IAAa,aAAa,0BACxB,QAAA,SAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,MAAA;CAAA,MAAA,QAAAA,aAAA,QAAA,OAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,aAAAA,aAAA,QAAA,YAAA;CAAA,MAAA,WAAAC,aAAA,MAAA;CAAA,MAA+D,OAAIC,UAAA,QAAA,MAAA,KAAA;CAAA,aACjEC,YAAA,OAEQ,WACF,EAAE,IAAI,CAAC,iBAAiB,MAAM,WAAU,KAAA,KAAK,KAAK,iBAAiB,KAAK,EAAE,GAC1E,IACF,GAAC,CAEA,WAAU,KAAA,KAAK,KAACA,YAAA,OACN,WAAkB,EAAE,IAAI,iBAAiB,KAAK,GAAC,UAAA,KAAW,GAAC,CAAA,KAAA,KAAA,CAAA,GACrEA,YAAA,QACS,WAAmB,EAAE,IAAI,iBAAiB,KAAK,GAAC,UAAA,KAAW,GAAC,CACnE,WAAU,MAAA,KAAM,KAACA,YAAA,OACP,WAAkB,EAAE,IAAI,iBAAiB,MAAM,GAAC,WAAA,KAAY,GAAC,CAAA,MAAA,KAAA,CAAA,GACvE,SAAA,KAAA,CAAA,CAAA,CAAA;AAIN,GAAA;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACL"}
|
|
@@ -9,8 +9,8 @@ import { MentionText, Mentions } from "../input/Mentions.js";
|
|
|
9
9
|
import { Upload as Upload$1 } from "../input/Upload.js";
|
|
10
10
|
import { useMessages } from "../config/lang/messages.js";
|
|
11
11
|
import { patternsMessages } from "./messages.js";
|
|
12
|
-
import { Fragment, createVNode,
|
|
13
|
-
import { _component, readonlyProp, restProps, untrack } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
12
|
+
import { Fragment, createVNode, onMounted, ref, shallowRef } from "vue";
|
|
13
|
+
import { _component, mergeJsxProps, readonlyProp, restProps, untrack } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
14
14
|
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
15
15
|
import { Paperclip } from "@lucide/icons";
|
|
16
16
|
//#region src/patterns/CommentThread.tsx
|
|
@@ -204,7 +204,7 @@ var CommentComposer = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
204
204
|
} else if (event.key === "Escape" && onCancel.value && !event.defaultPrevented) onCancel.value();
|
|
205
205
|
}
|
|
206
206
|
return () => createVNode("div", mergeAttrs({ sx: [commentStyles.composer, !author.value && commentStyles.composerNoAvatar] }, rest, { ref: root }), [author.value && authorAvatar(author.value.name, author.value.avatar), createVNode("div", css.attrs(commentStyles.composerMain), [
|
|
207
|
-
createVNode(Mentions,
|
|
207
|
+
createVNode(Mentions, mergeJsxProps(mergePartProps({
|
|
208
208
|
rows: 2,
|
|
209
209
|
maxRows: 10,
|
|
210
210
|
placeholder: placeholder.value ?? messages.commentPlaceholder,
|
|
@@ -224,7 +224,7 @@ var CommentComposer = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
224
224
|
"upload": upload.value,
|
|
225
225
|
"multiple": true
|
|
226
226
|
}, null),
|
|
227
|
-
failed.value && createVNode("div",
|
|
227
|
+
failed.value && createVNode("div", mergeJsxProps({ "role": "alert" }, css.attrs(commentStyles.error)), [messages.commentSendFailed]),
|
|
228
228
|
createVNode("div", css.attrs(commentStyles.composerFooter), [createVNode("div", css.attrs(commentStyles.composerTools), [upload.value && createVNode(Button, {
|
|
229
229
|
"variant": "ghost",
|
|
230
230
|
"tone": "neutral",
|
|
@@ -291,11 +291,11 @@ var CommentThread = /*#__PURE__*/ _component((_props2, _ctx2) => {
|
|
|
291
291
|
const item = (comment, thread) => {
|
|
292
292
|
const own = typeof propsItem.value === "function" ? propsItem.value(comment) : propsItem.value;
|
|
293
293
|
const top = comment === thread;
|
|
294
|
-
return createVNode("li",
|
|
294
|
+
return createVNode("li", mergeJsxProps({ "key": comment.id }, mergeAttrs({ sx: commentStyles.item }, own)), [authorAvatar(comment.author, comment.avatar), createVNode("article", mergeJsxProps(css.attrs(commentStyles.body), { "aria-label": comment.author }), [
|
|
295
295
|
createVNode("div", css.attrs(commentStyles.header), [
|
|
296
296
|
createVNode("span", css.attrs(commentStyles.author), [comment.author]),
|
|
297
297
|
comment.role,
|
|
298
|
-
createVNode("time",
|
|
298
|
+
createVNode("time", mergeJsxProps({
|
|
299
299
|
"datetime": comment.time,
|
|
300
300
|
"title": format.date(comment.time, {
|
|
301
301
|
dateStyle: "medium",
|
|
@@ -309,7 +309,7 @@ var CommentThread = /*#__PURE__*/ _component((_props2, _ctx2) => {
|
|
|
309
309
|
"options": people.value,
|
|
310
310
|
"sx": commentStyles.content
|
|
311
311
|
}, null),
|
|
312
|
-
comment.attachments && comment.attachments.length > 0 && createVNode("ul",
|
|
312
|
+
comment.attachments && comment.attachments.length > 0 && createVNode("ul", mergeJsxProps(css.attrs(commentStyles.attachments), { "aria-label": messages.commentAttachments }), [comment.attachments.map((file) => createVNode("li", mergeJsxProps({ "key": file.name }, css.attrs(commentStyles.attachment)), [
|
|
313
313
|
createVNode(Icon, { "icon": Paperclip }, null),
|
|
314
314
|
file.url ? createVNode(Link, { "href": file.url }, { default: () => [file.name] }) : file.name,
|
|
315
315
|
file.size != null && createVNode("span", css.attrs(commentStyles.meta), [format.fileSize(file.size)])
|
|
@@ -335,7 +335,7 @@ var CommentThread = /*#__PURE__*/ _component((_props2, _ctx2) => {
|
|
|
335
335
|
const list = thread.replies ?? [];
|
|
336
336
|
const open = replying.value?.thread === thread.id;
|
|
337
337
|
if (list.length === 0 && !open) return null;
|
|
338
|
-
return createVNode(Fragment, null, [list.length > 0 && createVNode("ul",
|
|
338
|
+
return createVNode(Fragment, null, [list.length > 0 && createVNode("ul", mergeJsxProps(css.attrs(commentStyles.replies), { "aria-label": messages.commentReplies(list.length) }), [list.map((reply) => item(reply, thread))]), open && onSend.value && createVNode(CommentComposer, mergeJsxProps(mergePartProps({
|
|
339
339
|
people: people.value,
|
|
340
340
|
upload: upload.value,
|
|
341
341
|
author: currentUser.value
|
|
@@ -359,7 +359,7 @@ var CommentThread = /*#__PURE__*/ _component((_props2, _ctx2) => {
|
|
|
359
359
|
return () => createVNode("div", mergeAttrs({ sx: commentStyles.root }, rest), [comments.value.length === 0 ? hasContent(empty.value) ? empty.value : createVNode(Empty, {
|
|
360
360
|
"size": "sm",
|
|
361
361
|
"title": messages.commentsEmpty
|
|
362
|
-
}, null) : createVNode("ul",
|
|
362
|
+
}, null) : createVNode("ul", mergeJsxProps(mergeAttrs({ sx: commentStyles.list }, propsList.value), { "aria-label": messages.comments }), [comments.value.map((comment) => item(comment, comment))]), onSend.value && createVNode(CommentComposer, mergeJsxProps(mergePartProps({
|
|
363
363
|
people: people.value,
|
|
364
364
|
upload: upload.value,
|
|
365
365
|
author: currentUser.value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommentThread.js","names":["_createVNode","_vtp_readonlyProp","_vtp_restProps","_vue_ref","_vtp_untrack","_vue_shallowRef","_mergeProps","Upload","_Fragment"],"sources":["../../src/patterns/CommentThread.tsx"],"sourcesContent":["/**\n * 留言:一串留言与回复(沟通记录、协商的逐条回应、审批时的讨论),下面是写留言的输入框。\n * 留言里可以 @ 人(Mentions)、带附件(Upload),回复只分一层:回复一条回复时仍挂在那串下面,开头写上“@作者 ”。\n *\n * <CommentThread\n * comments={comments} // { id, author, avatar?, role?, content, time, attachments?, replies? }\n * people={people} // 可以 @ 的人(ListboxOption),留言里“@名字”按它高亮\n * upload={(file, context) => api.upload(file, context)} // 不给时不能带附件\n * onSend={async (draft) => await api.comment(draft)} // { content, mentions, attachments, replyTo? };不给时只读\n * />\n *\n * 发送:按钮或 Ctrl + Enter;onSend 返回 Promise 时按钮转圈,成功后清空,失败时保留内容并提示。\n * 留言存在哪、发出去以后怎样出现在 comments 里由应用决定(乐观更新或重新取)。\n * 时间写成相对现在(3 分钟前),悬停看完整时间。actions 给每条留言加操作(编辑、删除、标为已解决)。\n *\n * 单独的输入框用 CommentComposer(协商回复、审批意见)。\n * 部件:Root(div)、List(ul)、Item(li,可以按条计算)、Composer(CommentComposer)。\n */\nimport { Paperclip } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { onMounted, type VNodeChild } from 'vue';\nimport { Button } from '../base/Button';\nimport { Icon } from '../base/Icon';\nimport { Link } from '../base/Link';\nimport { useFormat } from '../config/lang/format';\nimport { useMessages } from '../config/lang/messages';\nimport { Avatar, initials } from '../display/Avatar';\nimport { Empty } from '../feedback/Empty';\nimport { Mentions, MentionText, type MentionsProps } from '../input/Mentions';\nimport type { ListboxOption } from '../input/list-state';\nimport { Upload, type UploadFile, type UploadHandler } from '../input/Upload';\nimport { hasContent, mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { patternsMessages } from './messages';\n\n/** 留言的一个附件 */\nexport interface CommentAttachment {\n /** 文件名 */\n name: string;\n /** 地址:文件名链到它(预览、下载) */\n url?: string;\n /** 大小(字节) */\n size?: number;\n}\n\n/** 一条留言 */\nexport interface CommentItem {\n /** 标识 */\n id: string;\n /** 作者 */\n author: string;\n /** 作者的头像地址;没有时显示首字母 */\n avatar?: string;\n /** 作者的身份(岗位、“供应商”标记),在名字后面 */\n role?: VNodeChild;\n /** 内容:文字,“@名字”按 people 高亮 */\n content: string;\n /** 发表的时间(ISO) */\n time: string;\n /** 改过 */\n edited?: boolean;\n /** 附件 */\n attachments?: readonly CommentAttachment[];\n /** 内容下面的补充(逐条回应的“采纳 / 不采纳”、引用的字段对比) */\n extra?: VNodeChild;\n /** 回复(只分一层,回复里的 replies 不显示) */\n replies?: readonly CommentItem[];\n}\n\n/** 写好的一条留言:onSend 收到它 */\nexport interface CommentDraft {\n /** 内容(去掉首尾空白) */\n content: string;\n /** 内容里 @ 到的人(people 的 value) */\n mentions: string[];\n /** 上传完成的附件 */\n attachments: UploadFile[];\n /** 回复哪一串(那一串第一条留言的 id);新的一串没有 */\n replyTo?: string;\n}\n\n/** 写留言的人:输入框前面的头像 */\nexport interface CommentAuthor {\n /** 名字 */\n name: string;\n /** 头像地址 */\n avatar?: string;\n}\n\n/** 未声明的属性落到外层 div 上 */\nexport interface CommentComposerProps extends PartProps<'div'> {\n /** 发送:返回 Promise 时等它完成,成功后清空;失败(reject)时保留内容并提示 */\n onSend: (draft: Omit<CommentDraft, 'replyTo'>) => unknown;\n /** 取消(回复时):有它时显示取消按钮,按 Esc 也是取消 */\n onCancel?: () => void;\n /** 可以 @ 的人 */\n people?: readonly ListboxOption[];\n /** 上传附件;不给时不能带附件 */\n upload?: UploadHandler;\n /** 写留言的人:输入框前面的头像 */\n author?: CommentAuthor;\n /** 一开始的内容(回复时的“@作者 ”) */\n initialText?: string;\n /** 挂载后聚焦输入框(点了回复) */\n autofocus?: boolean;\n /** 输入框的提示,默认取文案 */\n placeholder?: string;\n /** 输入框(Mentions)的部件属性;它的 aria-label 是输入框的名称 */\n propsMentions?: Partial<MentionsProps>;\n}\n\n/** CommentThread、CommentComposer 的样式 */\nexport const commentStyles = css.create({\n root: { display: 'grid', gap: '_lg' },\n list: { display: 'grid', gap: '_lg', margin: 0, padding: 0, listStyle: 'none' },\n // 头像一列、内容一列\n item: { display: 'grid', gridTemplateColumns: 'auto 1fr', columnGap: '_sm', alignItems: 'start' },\n body: { display: 'grid', gap: '_xs', minWidth: 0 },\n header: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n columnGap: '_sm',\n rowGap: '_xs',\n },\n author: { fontWeight: '_semibold', color: '_text' },\n meta: { fontSize: '_sm', color: '_textMuted' },\n content: { color: '_text' },\n attachments: {\n display: 'flex',\n flexWrap: 'wrap',\n columnGap: '_md',\n rowGap: '_xs',\n margin: 0,\n padding: 0,\n listStyle: 'none',\n fontSize: '_sm',\n },\n attachment: { display: 'inline-flex', alignItems: 'center', gap: '0.25em' },\n actions: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '_sm', fontSize: '_sm' },\n // 回复:缩进在内容列里,左边一条竖线\n replies: {\n display: 'grid',\n gap: '_md',\n margin: 0,\n marginBlockStart: '_xs',\n paddingInlineStart: '_md',\n listStyle: 'none',\n borderInlineStartWidth: '_sm',\n borderInlineStartStyle: 'solid',\n borderInlineStartColor: '_borderMuted',\n },\n composer: {\n display: 'grid',\n gridTemplateColumns: 'auto 1fr',\n columnGap: '_sm',\n alignItems: 'start',\n },\n composerNoAvatar: { gridTemplateColumns: '1fr' },\n composerMain: { display: 'grid', gap: '_xs', minWidth: 0 },\n composerFooter: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '_xs',\n },\n composerTools: { display: 'flex', alignItems: 'center', gap: '_sm' },\n hint: { fontSize: '_xs', color: '_textMuted' },\n error: { fontSize: '_sm', color: '_dangerText' },\n});\n\n/** 头像:有图片用图片,没有时是名字的首字母(名字就在旁边,头像只是装饰) */\nconst authorAvatar = (name: string, avatar: string | undefined) => (\n <Avatar src={avatar} fallback={initials(name)} size=\"sm\" />\n);\n\n/** 写留言的输入框:提及、附件、发送 */\nexport const CommentComposer = _component(\n ({\n onSend,\n onCancel,\n people = [],\n upload,\n author,\n initialText = '',\n autofocus = false,\n placeholder,\n propsMentions,\n ...rest\n }: CommentComposerProps) => {\n const messages = useMessages('patterns', patternsMessages);\n // 只取一开始的值:回复时的“@作者 ”,之后由用户编辑\n let text = _state(_untrack(initialText));\n let mentions = _state<string[]>([]);\n let files = _state<UploadFile[]>([]);\n let attaching = _state(false);\n let sending = _state(false);\n let failed = _state(false);\n let root = _state.raw<HTMLElement | null>(null);\n\n onMounted(() => {\n if (!autofocus) return;\n const input = root?.querySelector('textarea');\n input?.focus();\n input?.setSelectionRange(input.value.length, input.value.length);\n });\n\n // 还在上传的附件:等它完成才能发送\n const uploading = () =>\n files.some((file) => file.status === 'uploading' || file.status === 'selected');\n const sendable = () => text.trim() !== '' && !uploading() && !sending;\n\n async function send() {\n if (!sendable()) return;\n sending = true;\n failed = false;\n try {\n await onSend({\n content: text.trim(),\n mentions,\n attachments: files.filter((file) => file.status === 'done'),\n });\n text = '';\n files = [];\n attaching = false;\n } catch {\n failed = true;\n } finally {\n sending = false;\n }\n }\n\n function onKeydown(event: KeyboardEvent) {\n if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {\n event.preventDefault();\n void send();\n } else if (event.key === 'Escape' && onCancel && !event.defaultPrevented) {\n // 提及列表打开时 Esc 先关列表(它会 preventDefault)\n onCancel();\n }\n }\n\n return () => (\n <div\n {...mergeAttrs<'div'>(\n { sx: [commentStyles.composer, !author && commentStyles.composerNoAvatar] },\n rest,\n { ref: _toRef(root) },\n )}\n >\n {author && authorAvatar(author.name, author.avatar)}\n <div {...css.attrs(commentStyles.composerMain)}>\n <Mentions\n {...mergePartProps<MentionsProps>(\n {\n rows: 2,\n maxRows: 10,\n placeholder: placeholder ?? messages.commentPlaceholder,\n 'aria-label': messages.comments,\n },\n propsMentions,\n { items: people, onKeydown },\n )}\n _value={text}\n _mentions={mentions}\n />\n {upload && attaching && <Upload _value={files} upload={upload} multiple />}\n {failed && (\n <div role=\"alert\" {...css.attrs(commentStyles.error)}>\n {messages.commentSendFailed}\n </div>\n )}\n <div {...css.attrs(commentStyles.composerFooter)}>\n <div {...css.attrs(commentStyles.composerTools)}>\n {upload && (\n <Button\n variant=\"ghost\"\n tone=\"neutral\"\n size=\"sm\"\n icon={Paperclip}\n aria-pressed={attaching}\n onClick={() => (attaching = !attaching)}\n >\n {files.length > 0\n ? `${messages.commentAttach} ${files.length}`\n : messages.commentAttach}\n </Button>\n )}\n <span {...css.attrs(commentStyles.hint)}>{messages.commentSendHint}</span>\n </div>\n <div {...css.attrs(commentStyles.composerTools)}>\n {onCancel && (\n <Button variant=\"ghost\" tone=\"neutral\" size=\"sm\" onClick={onCancel}>\n {messages.commentCancel}\n </Button>\n )}\n <Button\n size=\"sm\"\n loading={sending}\n disabled={!sendable()}\n onClick={() => void send()}\n >\n {messages.commentSend}\n </Button>\n </div>\n </div>\n </div>\n </div>\n );\n },\n);\n\n/** 未声明的属性落到外层 div 上 */\nexport interface CommentThreadProps extends PartProps<'div'> {\n /** 留言,旧的在前;回复在各自的 replies 里 */\n comments: readonly CommentItem[];\n /** 可以 @ 的人:输入框的候选,也是留言里“@名字”高亮的依据 */\n people?: readonly ListboxOption[];\n /** 发送一条留言或回复;不给时只读(没有输入框与回复按钮) */\n onSend?: (draft: CommentDraft) => unknown;\n /** 上传附件;不给时不能带附件 */\n upload?: UploadHandler;\n /** 当前用户:输入框前面的头像 */\n currentUser?: CommentAuthor;\n /** 每条留言的操作(编辑、删除、标为已解决),在回复按钮后面 */\n actions?: (comment: CommentItem) => VNodeChild;\n /** 没有留言时显示的内容,默认取文案 */\n empty?: VNodeChild;\n /** “现在”的时间戳:相对时间按它算,默认是当前时间(文档与测试里固定它) */\n now?: number;\n /** 列表(ul)的部件属性 */\n propsList?: PartProps<'ul'>;\n /** 每条留言(li)的部件属性,可以按条计算 */\n propsItem?: PartProps<'li'> | ((comment: CommentItem) => PartProps<'li'>);\n /** 输入框(CommentComposer)的部件属性 */\n propsComposer?: Partial<Omit<CommentComposerProps, 'onSend'>>;\n}\n\n/** 一串留言与回复,下面是输入框 */\nexport const CommentThread = _component(\n ({\n comments,\n people = [],\n onSend,\n upload,\n currentUser,\n actions,\n empty,\n now,\n propsList,\n propsItem,\n propsComposer,\n ...rest\n }: CommentThreadProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const format = useFormat();\n // 正在回复:哪一串(第一条的 id)与回复谁\n let replying = _state<{ thread: string; to: string } | null>(null);\n\n const item = (comment: CommentItem, thread: CommentItem): VNodeChild => {\n const own = typeof propsItem === 'function' ? propsItem(comment) : propsItem;\n const top = comment === thread;\n return (\n <li key={comment.id} {...mergeAttrs<'li'>({ sx: commentStyles.item }, own)}>\n {authorAvatar(comment.author, comment.avatar)}\n <article {...css.attrs(commentStyles.body)} aria-label={comment.author}>\n <div {...css.attrs(commentStyles.header)}>\n <span {...css.attrs(commentStyles.author)}>{comment.author}</span>\n {comment.role}\n <time\n datetime={comment.time}\n title={format.date(comment.time, { dateStyle: 'medium', timeStyle: 'short' })}\n {...css.attrs(commentStyles.meta)}\n >\n {format.relative(comment.time, { now })}\n </time>\n {comment.edited && (\n <span {...css.attrs(commentStyles.meta)}>{messages.commentEdited}</span>\n )}\n </div>\n <MentionText text={comment.content} options={people} sx={commentStyles.content} />\n {comment.attachments && comment.attachments.length > 0 && (\n <ul\n {...css.attrs(commentStyles.attachments)}\n aria-label={messages.commentAttachments}\n >\n {comment.attachments.map((file) => (\n <li key={file.name} {...css.attrs(commentStyles.attachment)}>\n <Icon icon={Paperclip} />\n {file.url ? <Link href={file.url}>{file.name}</Link> : file.name}\n {file.size != null && (\n <span {...css.attrs(commentStyles.meta)}>{format.fileSize(file.size)}</span>\n )}\n </li>\n ))}\n </ul>\n )}\n {comment.extra}\n {(onSend || actions) && (\n <div {...css.attrs(commentStyles.actions)}>\n {onSend && (\n <Button\n variant=\"link\"\n tone=\"neutral\"\n size=\"sm\"\n onClick={() => (replying = { thread: thread.id, to: comment.author })}\n >\n {messages.commentReply}\n </Button>\n )}\n {actions?.(comment)}\n </div>\n )}\n {top && replies(thread)}\n </article>\n </li>\n );\n };\n\n /** 一串的回复与回复的输入框 */\n const replies = (thread: CommentItem) => {\n const list = thread.replies ?? [];\n const open = replying?.thread === thread.id;\n if (list.length === 0 && !open) return null;\n return (\n <>\n {list.length > 0 && (\n <ul\n {...css.attrs(commentStyles.replies)}\n aria-label={messages.commentReplies(list.length)}\n >\n {list.map((reply) => item(reply, thread))}\n </ul>\n )}\n {open && onSend && (\n <CommentComposer\n {...mergePartProps<Partial<CommentComposerProps>>(\n { people, upload, author: currentUser },\n propsComposer,\n {\n key: `${thread.id}:${replying!.to}`,\n initialText: `@${replying!.to} `,\n autofocus: true,\n propsMentions: {\n ...propsComposer?.propsMentions,\n 'aria-label': messages.commentReplyTo(replying!.to),\n },\n onCancel: () => (replying = null),\n },\n )}\n // onSend 有返回值(Promise),不经 mergePartProps:它会把 on 开头的属性当事件串起来\n onSend={async (draft) => {\n await onSend({ ...draft, replyTo: thread.id });\n replying = null;\n }}\n />\n )}\n </>\n );\n };\n\n return () => (\n <div {...mergeAttrs<'div'>({ sx: commentStyles.root }, rest)}>\n {comments.length === 0 ? (\n hasContent(empty) ? (\n empty\n ) : (\n <Empty size=\"sm\" title={messages.commentsEmpty} />\n )\n ) : (\n <ul\n {...mergeAttrs<'ul'>({ sx: commentStyles.list }, propsList)}\n aria-label={messages.comments}\n >\n {comments.map((comment) => item(comment, comment))}\n </ul>\n )}\n {onSend && (\n <CommentComposer\n {...mergePartProps<Partial<CommentComposerProps>>(\n { people, upload, author: currentUser },\n propsComposer,\n )}\n onSend={onSend}\n />\n )}\n </div>\n );\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CommentThread.js","names":["_createVNode","_vtp_readonlyProp","_vtp_restProps","_vue_ref","_vtp_untrack","_vue_shallowRef","_mergeProps","Upload","_Fragment"],"sources":["../../src/patterns/CommentThread.tsx"],"sourcesContent":["/**\n * 留言:一串留言与回复(沟通记录、协商的逐条回应、审批时的讨论),下面是写留言的输入框。\n * 留言里可以 @ 人(Mentions)、带附件(Upload),回复只分一层:回复一条回复时仍挂在那串下面,开头写上“@作者 ”。\n *\n * <CommentThread\n * comments={comments} // { id, author, avatar?, role?, content, time, attachments?, replies? }\n * people={people} // 可以 @ 的人(ListboxOption),留言里“@名字”按它高亮\n * upload={(file, context) => api.upload(file, context)} // 不给时不能带附件\n * onSend={async (draft) => await api.comment(draft)} // { content, mentions, attachments, replyTo? };不给时只读\n * />\n *\n * 发送:按钮或 Ctrl + Enter;onSend 返回 Promise 时按钮转圈,成功后清空,失败时保留内容并提示。\n * 留言存在哪、发出去以后怎样出现在 comments 里由应用决定(乐观更新或重新取)。\n * 时间写成相对现在(3 分钟前),悬停看完整时间。actions 给每条留言加操作(编辑、删除、标为已解决)。\n *\n * 单独的输入框用 CommentComposer(协商回复、审批意见)。\n * 部件:Root(div)、List(ul)、Item(li,可以按条计算)、Composer(CommentComposer)。\n */\nimport { Paperclip } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { onMounted, type VNodeChild } from 'vue';\nimport { Button } from '../base/Button';\nimport { Icon } from '../base/Icon';\nimport { Link } from '../base/Link';\nimport { useFormat } from '../config/lang/format';\nimport { useMessages } from '../config/lang/messages';\nimport { Avatar, initials } from '../display/Avatar';\nimport { Empty } from '../feedback/Empty';\nimport { Mentions, MentionText, type MentionsProps } from '../input/Mentions';\nimport type { ListboxOption } from '../input/list-state';\nimport { Upload, type UploadFile, type UploadHandler } from '../input/Upload';\nimport { hasContent, mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { patternsMessages } from './messages';\n\n/** 留言的一个附件 */\nexport interface CommentAttachment {\n /** 文件名 */\n name: string;\n /** 地址:文件名链到它(预览、下载) */\n url?: string;\n /** 大小(字节) */\n size?: number;\n}\n\n/** 一条留言 */\nexport interface CommentItem {\n /** 标识 */\n id: string;\n /** 作者 */\n author: string;\n /** 作者的头像地址;没有时显示首字母 */\n avatar?: string;\n /** 作者的身份(岗位、“供应商”标记),在名字后面 */\n role?: VNodeChild;\n /** 内容:文字,“@名字”按 people 高亮 */\n content: string;\n /** 发表的时间(ISO) */\n time: string;\n /** 改过 */\n edited?: boolean;\n /** 附件 */\n attachments?: readonly CommentAttachment[];\n /** 内容下面的补充(逐条回应的“采纳 / 不采纳”、引用的字段对比) */\n extra?: VNodeChild;\n /** 回复(只分一层,回复里的 replies 不显示) */\n replies?: readonly CommentItem[];\n}\n\n/** 写好的一条留言:onSend 收到它 */\nexport interface CommentDraft {\n /** 内容(去掉首尾空白) */\n content: string;\n /** 内容里 @ 到的人(people 的 value) */\n mentions: string[];\n /** 上传完成的附件 */\n attachments: UploadFile[];\n /** 回复哪一串(那一串第一条留言的 id);新的一串没有 */\n replyTo?: string;\n}\n\n/** 写留言的人:输入框前面的头像 */\nexport interface CommentAuthor {\n /** 名字 */\n name: string;\n /** 头像地址 */\n avatar?: string;\n}\n\n/** 未声明的属性落到外层 div 上 */\nexport interface CommentComposerProps extends PartProps<'div'> {\n /** 发送:返回 Promise 时等它完成,成功后清空;失败(reject)时保留内容并提示 */\n onSend: (draft: Omit<CommentDraft, 'replyTo'>) => unknown;\n /** 取消(回复时):有它时显示取消按钮,按 Esc 也是取消 */\n onCancel?: () => void;\n /** 可以 @ 的人 */\n people?: readonly ListboxOption[];\n /** 上传附件;不给时不能带附件 */\n upload?: UploadHandler;\n /** 写留言的人:输入框前面的头像 */\n author?: CommentAuthor;\n /** 一开始的内容(回复时的“@作者 ”) */\n initialText?: string;\n /** 挂载后聚焦输入框(点了回复) */\n autofocus?: boolean;\n /** 输入框的提示,默认取文案 */\n placeholder?: string;\n /** 输入框(Mentions)的部件属性;它的 aria-label 是输入框的名称 */\n propsMentions?: Partial<MentionsProps>;\n}\n\n/** CommentThread、CommentComposer 的样式 */\nexport const commentStyles = css.create({\n root: { display: 'grid', gap: '_lg' },\n list: { display: 'grid', gap: '_lg', margin: 0, padding: 0, listStyle: 'none' },\n // 头像一列、内容一列\n item: { display: 'grid', gridTemplateColumns: 'auto 1fr', columnGap: '_sm', alignItems: 'start' },\n body: { display: 'grid', gap: '_xs', minWidth: 0 },\n header: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n columnGap: '_sm',\n rowGap: '_xs',\n },\n author: { fontWeight: '_semibold', color: '_text' },\n meta: { fontSize: '_sm', color: '_textMuted' },\n content: { color: '_text' },\n attachments: {\n display: 'flex',\n flexWrap: 'wrap',\n columnGap: '_md',\n rowGap: '_xs',\n margin: 0,\n padding: 0,\n listStyle: 'none',\n fontSize: '_sm',\n },\n attachment: { display: 'inline-flex', alignItems: 'center', gap: '0.25em' },\n actions: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '_sm', fontSize: '_sm' },\n // 回复:缩进在内容列里,左边一条竖线\n replies: {\n display: 'grid',\n gap: '_md',\n margin: 0,\n marginBlockStart: '_xs',\n paddingInlineStart: '_md',\n listStyle: 'none',\n borderInlineStartWidth: '_sm',\n borderInlineStartStyle: 'solid',\n borderInlineStartColor: '_borderMuted',\n },\n composer: {\n display: 'grid',\n gridTemplateColumns: 'auto 1fr',\n columnGap: '_sm',\n alignItems: 'start',\n },\n composerNoAvatar: { gridTemplateColumns: '1fr' },\n composerMain: { display: 'grid', gap: '_xs', minWidth: 0 },\n composerFooter: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '_xs',\n },\n composerTools: { display: 'flex', alignItems: 'center', gap: '_sm' },\n hint: { fontSize: '_xs', color: '_textMuted' },\n error: { fontSize: '_sm', color: '_dangerText' },\n});\n\n/** 头像:有图片用图片,没有时是名字的首字母(名字就在旁边,头像只是装饰) */\nconst authorAvatar = (name: string, avatar: string | undefined) => (\n <Avatar src={avatar} fallback={initials(name)} size=\"sm\" />\n);\n\n/** 写留言的输入框:提及、附件、发送 */\nexport const CommentComposer = _component(\n ({\n onSend,\n onCancel,\n people = [],\n upload,\n author,\n initialText = '',\n autofocus = false,\n placeholder,\n propsMentions,\n ...rest\n }: CommentComposerProps) => {\n const messages = useMessages('patterns', patternsMessages);\n // 只取一开始的值:回复时的“@作者 ”,之后由用户编辑\n let text = _state(_untrack(initialText));\n let mentions = _state<string[]>([]);\n let files = _state<UploadFile[]>([]);\n let attaching = _state(false);\n let sending = _state(false);\n let failed = _state(false);\n let root = _state.raw<HTMLElement | null>(null);\n\n onMounted(() => {\n if (!autofocus) return;\n const input = root?.querySelector('textarea');\n input?.focus();\n input?.setSelectionRange(input.value.length, input.value.length);\n });\n\n // 还在上传的附件:等它完成才能发送\n const uploading = () =>\n files.some((file) => file.status === 'uploading' || file.status === 'selected');\n const sendable = () => text.trim() !== '' && !uploading() && !sending;\n\n async function send() {\n if (!sendable()) return;\n sending = true;\n failed = false;\n try {\n await onSend({\n content: text.trim(),\n mentions,\n attachments: files.filter((file) => file.status === 'done'),\n });\n text = '';\n files = [];\n attaching = false;\n } catch {\n failed = true;\n } finally {\n sending = false;\n }\n }\n\n function onKeydown(event: KeyboardEvent) {\n if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {\n event.preventDefault();\n void send();\n } else if (event.key === 'Escape' && onCancel && !event.defaultPrevented) {\n // 提及列表打开时 Esc 先关列表(它会 preventDefault)\n onCancel();\n }\n }\n\n return () => (\n <div\n {...mergeAttrs<'div'>(\n { sx: [commentStyles.composer, !author && commentStyles.composerNoAvatar] },\n rest,\n { ref: _toRef(root) },\n )}\n >\n {author && authorAvatar(author.name, author.avatar)}\n <div {...css.attrs(commentStyles.composerMain)}>\n <Mentions\n {...mergePartProps<MentionsProps>(\n {\n rows: 2,\n maxRows: 10,\n placeholder: placeholder ?? messages.commentPlaceholder,\n 'aria-label': messages.comments,\n },\n propsMentions,\n { items: people, onKeydown },\n )}\n _value={text}\n _mentions={mentions}\n />\n {upload && attaching && <Upload _value={files} upload={upload} multiple />}\n {failed && (\n <div role=\"alert\" {...css.attrs(commentStyles.error)}>\n {messages.commentSendFailed}\n </div>\n )}\n <div {...css.attrs(commentStyles.composerFooter)}>\n <div {...css.attrs(commentStyles.composerTools)}>\n {upload && (\n <Button\n variant=\"ghost\"\n tone=\"neutral\"\n size=\"sm\"\n icon={Paperclip}\n aria-pressed={attaching}\n onClick={() => (attaching = !attaching)}\n >\n {files.length > 0\n ? `${messages.commentAttach} ${files.length}`\n : messages.commentAttach}\n </Button>\n )}\n <span {...css.attrs(commentStyles.hint)}>{messages.commentSendHint}</span>\n </div>\n <div {...css.attrs(commentStyles.composerTools)}>\n {onCancel && (\n <Button variant=\"ghost\" tone=\"neutral\" size=\"sm\" onClick={onCancel}>\n {messages.commentCancel}\n </Button>\n )}\n <Button\n size=\"sm\"\n loading={sending}\n disabled={!sendable()}\n onClick={() => void send()}\n >\n {messages.commentSend}\n </Button>\n </div>\n </div>\n </div>\n </div>\n );\n },\n);\n\n/** 未声明的属性落到外层 div 上 */\nexport interface CommentThreadProps extends PartProps<'div'> {\n /** 留言,旧的在前;回复在各自的 replies 里 */\n comments: readonly CommentItem[];\n /** 可以 @ 的人:输入框的候选,也是留言里“@名字”高亮的依据 */\n people?: readonly ListboxOption[];\n /** 发送一条留言或回复;不给时只读(没有输入框与回复按钮) */\n onSend?: (draft: CommentDraft) => unknown;\n /** 上传附件;不给时不能带附件 */\n upload?: UploadHandler;\n /** 当前用户:输入框前面的头像 */\n currentUser?: CommentAuthor;\n /** 每条留言的操作(编辑、删除、标为已解决),在回复按钮后面 */\n actions?: (comment: CommentItem) => VNodeChild;\n /** 没有留言时显示的内容,默认取文案 */\n empty?: VNodeChild;\n /** “现在”的时间戳:相对时间按它算,默认是当前时间(文档与测试里固定它) */\n now?: number;\n /** 列表(ul)的部件属性 */\n propsList?: PartProps<'ul'>;\n /** 每条留言(li)的部件属性,可以按条计算 */\n propsItem?: PartProps<'li'> | ((comment: CommentItem) => PartProps<'li'>);\n /** 输入框(CommentComposer)的部件属性 */\n propsComposer?: Partial<Omit<CommentComposerProps, 'onSend'>>;\n}\n\n/** 一串留言与回复,下面是输入框 */\nexport const CommentThread = _component(\n ({\n comments,\n people = [],\n onSend,\n upload,\n currentUser,\n actions,\n empty,\n now,\n propsList,\n propsItem,\n propsComposer,\n ...rest\n }: CommentThreadProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const format = useFormat();\n // 正在回复:哪一串(第一条的 id)与回复谁\n let replying = _state<{ thread: string; to: string } | null>(null);\n\n const item = (comment: CommentItem, thread: CommentItem): VNodeChild => {\n const own = typeof propsItem === 'function' ? propsItem(comment) : propsItem;\n const top = comment === thread;\n return (\n <li key={comment.id} {...mergeAttrs<'li'>({ sx: commentStyles.item }, own)}>\n {authorAvatar(comment.author, comment.avatar)}\n <article {...css.attrs(commentStyles.body)} aria-label={comment.author}>\n <div {...css.attrs(commentStyles.header)}>\n <span {...css.attrs(commentStyles.author)}>{comment.author}</span>\n {comment.role}\n <time\n datetime={comment.time}\n title={format.date(comment.time, { dateStyle: 'medium', timeStyle: 'short' })}\n {...css.attrs(commentStyles.meta)}\n >\n {format.relative(comment.time, { now })}\n </time>\n {comment.edited && (\n <span {...css.attrs(commentStyles.meta)}>{messages.commentEdited}</span>\n )}\n </div>\n <MentionText text={comment.content} options={people} sx={commentStyles.content} />\n {comment.attachments && comment.attachments.length > 0 && (\n <ul\n {...css.attrs(commentStyles.attachments)}\n aria-label={messages.commentAttachments}\n >\n {comment.attachments.map((file) => (\n <li key={file.name} {...css.attrs(commentStyles.attachment)}>\n <Icon icon={Paperclip} />\n {file.url ? <Link href={file.url}>{file.name}</Link> : file.name}\n {file.size != null && (\n <span {...css.attrs(commentStyles.meta)}>{format.fileSize(file.size)}</span>\n )}\n </li>\n ))}\n </ul>\n )}\n {comment.extra}\n {(onSend || actions) && (\n <div {...css.attrs(commentStyles.actions)}>\n {onSend && (\n <Button\n variant=\"link\"\n tone=\"neutral\"\n size=\"sm\"\n onClick={() => (replying = { thread: thread.id, to: comment.author })}\n >\n {messages.commentReply}\n </Button>\n )}\n {actions?.(comment)}\n </div>\n )}\n {top && replies(thread)}\n </article>\n </li>\n );\n };\n\n /** 一串的回复与回复的输入框 */\n const replies = (thread: CommentItem) => {\n const list = thread.replies ?? [];\n const open = replying?.thread === thread.id;\n if (list.length === 0 && !open) return null;\n return (\n <>\n {list.length > 0 && (\n <ul\n {...css.attrs(commentStyles.replies)}\n aria-label={messages.commentReplies(list.length)}\n >\n {list.map((reply) => item(reply, thread))}\n </ul>\n )}\n {open && onSend && (\n <CommentComposer\n {...mergePartProps<Partial<CommentComposerProps>>(\n { people, upload, author: currentUser },\n propsComposer,\n {\n key: `${thread.id}:${replying!.to}`,\n initialText: `@${replying!.to} `,\n autofocus: true,\n propsMentions: {\n ...propsComposer?.propsMentions,\n 'aria-label': messages.commentReplyTo(replying!.to),\n },\n onCancel: () => (replying = null),\n },\n )}\n // onSend 有返回值(Promise),不经 mergePartProps:它会把 on 开头的属性当事件串起来\n onSend={async (draft) => {\n await onSend({ ...draft, replyTo: thread.id });\n replying = null;\n }}\n />\n )}\n </>\n );\n };\n\n return () => (\n <div {...mergeAttrs<'div'>({ sx: commentStyles.root }, rest)}>\n {comments.length === 0 ? (\n hasContent(empty) ? (\n empty\n ) : (\n <Empty size=\"sm\" title={messages.commentsEmpty} />\n )\n ) : (\n <ul\n {...mergeAttrs<'ul'>({ sx: commentStyles.list }, propsList)}\n aria-label={messages.comments}\n >\n {comments.map((comment) => item(comment, comment))}\n </ul>\n )}\n {onSend && (\n <CommentComposer\n {...mergePartProps<Partial<CommentComposerProps>>(\n { people, upload, author: currentUser },\n propsComposer,\n )}\n onSend={onSend}\n />\n )}\n </div>\n );\n },\n);\n"],"mappings":"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"}
|
|
@@ -15,8 +15,8 @@ import { DataTable } from "../table/DataTable.js";
|
|
|
15
15
|
import { readCsvFile } from "../table/sheet.js";
|
|
16
16
|
import { useMessages } from "../config/lang/messages.js";
|
|
17
17
|
import { patternsMessages } from "./messages.js";
|
|
18
|
-
import { createVNode, isVNode,
|
|
19
|
-
import { _component, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
18
|
+
import { createVNode, isVNode, ref, shallowRef, unref } from "vue";
|
|
19
|
+
import { _component, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
20
20
|
import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
21
21
|
import { CircleAlert, CircleCheck, Download } from "@lucide/icons";
|
|
22
22
|
import { safeParse } from "zod/v4/core";
|
|
@@ -393,7 +393,7 @@ var DataImport = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
393
393
|
default: () => [messages.dataImportOnlyErrors],
|
|
394
394
|
$stable: true
|
|
395
395
|
})] }),
|
|
396
|
-
createVNode(DataTable,
|
|
396
|
+
createVNode(DataTable, mergeJsxProps(mergePartProps({
|
|
397
397
|
"aria-label": messages.dataImportPreview,
|
|
398
398
|
propsRoot: { sx: dataImportStyles.previewTable },
|
|
399
399
|
density: "compact"
|
|
@@ -415,7 +415,7 @@ var DataImport = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
415
415
|
let _slot4;
|
|
416
416
|
const { valid } = counts();
|
|
417
417
|
return createVNode("div", mergeAttrs({ sx: dataImportStyles.root }, rest), [
|
|
418
|
-
createVNode(Steps,
|
|
418
|
+
createVNode(Steps, mergeJsxProps(mergePartProps({ items: messages.dataImportSteps.map((title) => ({ title })) }, propsSteps.value, { items: messages.dataImportSteps.map((title) => ({ title })) }), {
|
|
419
419
|
"_current": step.value,
|
|
420
420
|
"onUpdate:_current": _cache[5] || (_cache[5] = ($event) => step.value = $event)
|
|
421
421
|
}), null),
|