@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.
Files changed (416) hide show
  1. package/dist/base/Badge.js +3 -3
  2. package/dist/base/Badge.js.map +1 -1
  3. package/dist/base/Can.d.ts +12 -0
  4. package/dist/base/Can.js +20 -0
  5. package/dist/base/Can.js.map +1 -0
  6. package/dist/base/ClientOnly.d.ts +10 -0
  7. package/dist/base/ClientOnly.js +18 -0
  8. package/dist/base/ClientOnly.js.map +1 -0
  9. package/dist/base/CloseButton.js +3 -3
  10. package/dist/base/CloseButton.js.map +1 -1
  11. package/dist/base/CodeBlock.js +5 -5
  12. package/dist/base/CodeBlock.js.map +1 -1
  13. package/dist/base/CopyButton.js +3 -3
  14. package/dist/base/CopyButton.js.map +1 -1
  15. package/dist/base/Icon.js +3 -3
  16. package/dist/base/Icon.js.map +1 -1
  17. package/dist/base/Indicator.js +3 -3
  18. package/dist/base/Indicator.js.map +1 -1
  19. package/dist/base/StatusDot.js +3 -3
  20. package/dist/base/StatusDot.js.map +1 -1
  21. package/dist/base/Tag.js +5 -5
  22. package/dist/base/Tag.js.map +1 -1
  23. package/dist/base/button-core.js +4 -4
  24. package/dist/base/button-core.js.map +1 -1
  25. package/dist/charts/AreaChart.js +3 -3
  26. package/dist/charts/AreaChart.js.map +1 -1
  27. package/dist/charts/BarChart.js +3 -3
  28. package/dist/charts/BarChart.js.map +1 -1
  29. package/dist/charts/ChordChart.js +3 -3
  30. package/dist/charts/ChordChart.js.map +1 -1
  31. package/dist/charts/ComboChart.js +3 -3
  32. package/dist/charts/ComboChart.js.map +1 -1
  33. package/dist/charts/DonutChart.js +3 -3
  34. package/dist/charts/DonutChart.js.map +1 -1
  35. package/dist/charts/FunnelChart.js +5 -5
  36. package/dist/charts/FunnelChart.js.map +1 -1
  37. package/dist/charts/GraphChart.js +3 -3
  38. package/dist/charts/GraphChart.js.map +1 -1
  39. package/dist/charts/LineChart.js +3 -3
  40. package/dist/charts/LineChart.js.map +1 -1
  41. package/dist/charts/MapChart.js +7 -7
  42. package/dist/charts/MapChart.js.map +1 -1
  43. package/dist/charts/RadarChart.js +16 -15
  44. package/dist/charts/RadarChart.js.map +1 -1
  45. package/dist/charts/SankeyChart.js +3 -3
  46. package/dist/charts/SankeyChart.js.map +1 -1
  47. package/dist/charts/ScatterChart.js +3 -3
  48. package/dist/charts/ScatterChart.js.map +1 -1
  49. package/dist/charts/TimelineChart.js +6 -5
  50. package/dist/charts/TimelineChart.js.map +1 -1
  51. package/dist/charts/TreemapChart.js +3 -3
  52. package/dist/charts/TreemapChart.js.map +1 -1
  53. package/dist/charts/WaterfallChart.js +3 -3
  54. package/dist/charts/WaterfallChart.js.map +1 -1
  55. package/dist/charts/XYChart.js +9 -7
  56. package/dist/charts/XYChart.js.map +1 -1
  57. package/dist/charts/frame.js +10 -8
  58. package/dist/charts/frame.js.map +1 -1
  59. package/dist/charts/single.js +4 -3
  60. package/dist/charts/single.js.map +1 -1
  61. package/dist/config/Provider.d.ts +3 -1
  62. package/dist/config/Provider.js +9 -4
  63. package/dist/config/Provider.js.map +1 -1
  64. package/dist/config/context.d.ts +18 -0
  65. package/dist/config/context.js +16 -1
  66. package/dist/config/context.js.map +1 -1
  67. package/dist/config/createUi.d.ts +20 -3
  68. package/dist/config/createUi.js +80 -49
  69. package/dist/config/createUi.js.map +1 -1
  70. package/dist/config/layout/preference.d.ts +43 -0
  71. package/dist/config/layout/preference.js +108 -0
  72. package/dist/config/layout/preference.js.map +1 -0
  73. package/dist/date/Calendar.js +3 -3
  74. package/dist/date/Calendar.js.map +1 -1
  75. package/dist/date/CalendarBoard.js +9 -9
  76. package/dist/date/CalendarBoard.js.map +1 -1
  77. package/dist/date/CalendarView.js +7 -7
  78. package/dist/date/CalendarView.js.map +1 -1
  79. package/dist/date/DateField.js +3 -3
  80. package/dist/date/DateField.js.map +1 -1
  81. package/dist/date/DatePicker.js +5 -5
  82. package/dist/date/DatePicker.js.map +1 -1
  83. package/dist/date/DateRangePicker.js +5 -5
  84. package/dist/date/DateRangePicker.js.map +1 -1
  85. package/dist/date/DateSegmentGroup.js +5 -5
  86. package/dist/date/DateSegmentGroup.js.map +1 -1
  87. package/dist/date/QuarterCalendar.js +6 -6
  88. package/dist/date/QuarterCalendar.js.map +1 -1
  89. package/dist/date/RangeCalendar.js +3 -3
  90. package/dist/date/RangeCalendar.js.map +1 -1
  91. package/dist/date/SegmentedField.js +3 -3
  92. package/dist/date/SegmentedField.js.map +1 -1
  93. package/dist/date/TimeField.js +3 -3
  94. package/dist/date/TimeField.js.map +1 -1
  95. package/dist/date/TimePicker.js +4 -4
  96. package/dist/date/TimePicker.js.map +1 -1
  97. package/dist/date/date-segments.js +1 -1
  98. package/dist/date/date-segments.js.map +1 -1
  99. package/dist/date/picker-popover.js +3 -2
  100. package/dist/date/picker-popover.js.map +1 -1
  101. package/dist/diagram/Diagram.js +12 -12
  102. package/dist/diagram/Diagram.js.map +1 -1
  103. package/dist/diagram/DiagramLanes.js +3 -3
  104. package/dist/diagram/DiagramLanes.js.map +1 -1
  105. package/dist/diagram/DiagramMinimap.js +4 -4
  106. package/dist/diagram/DiagramMinimap.js.map +1 -1
  107. package/dist/display/Accordion.js +6 -6
  108. package/dist/display/Accordion.js.map +1 -1
  109. package/dist/display/AnimatedNumber.js +4 -4
  110. package/dist/display/AnimatedNumber.js.map +1 -1
  111. package/dist/display/Avatar.js +4 -4
  112. package/dist/display/Avatar.js.map +1 -1
  113. package/dist/display/Checklist.js +7 -7
  114. package/dist/display/Checklist.js.map +1 -1
  115. package/dist/display/Collapse.js +8 -5
  116. package/dist/display/Collapse.js.map +1 -1
  117. package/dist/display/Collapsible.js +3 -3
  118. package/dist/display/Collapsible.js.map +1 -1
  119. package/dist/display/Descriptions.js +5 -5
  120. package/dist/display/Descriptions.js.map +1 -1
  121. package/dist/display/Ellipsis.js +3 -3
  122. package/dist/display/Ellipsis.js.map +1 -1
  123. package/dist/display/FieldCompare.js +4 -4
  124. package/dist/display/FieldCompare.js.map +1 -1
  125. package/dist/display/Gauge.js +11 -11
  126. package/dist/display/Gauge.js.map +1 -1
  127. package/dist/display/Image.js +4 -4
  128. package/dist/display/Image.js.map +1 -1
  129. package/dist/display/Marquee.js +3 -3
  130. package/dist/display/Marquee.js.map +1 -1
  131. package/dist/display/ProportionBar.js +4 -4
  132. package/dist/display/ProportionBar.js.map +1 -1
  133. package/dist/display/Sortable.js +4 -4
  134. package/dist/display/Sortable.js.map +1 -1
  135. package/dist/display/Sparkline.js +6 -6
  136. package/dist/display/Sparkline.js.map +1 -1
  137. package/dist/display/Stat.js +3 -3
  138. package/dist/display/Stat.js.map +1 -1
  139. package/dist/display/Timeline.js +7 -7
  140. package/dist/display/Timeline.js.map +1 -1
  141. package/dist/display/Tree.js +10 -10
  142. package/dist/display/Tree.js.map +1 -1
  143. package/dist/display/VirtualList.js +7 -7
  144. package/dist/display/VirtualList.js.map +1 -1
  145. package/dist/display/Watermark.js +3 -3
  146. package/dist/display/Watermark.js.map +1 -1
  147. package/dist/docs-kit/CodeGroup.js +5 -5
  148. package/dist/docs-kit/CodeGroup.js.map +1 -1
  149. package/dist/docs-kit/CodePreview.js +6 -6
  150. package/dist/docs-kit/CodePreview.js.map +1 -1
  151. package/dist/docs-kit/PropsTable.js +4 -4
  152. package/dist/docs-kit/PropsTable.js.map +1 -1
  153. package/dist/feedback/Alert.js +5 -5
  154. package/dist/feedback/Alert.js.map +1 -1
  155. package/dist/feedback/Banner.js +5 -5
  156. package/dist/feedback/Banner.js.map +1 -1
  157. package/dist/feedback/Empty.js +4 -4
  158. package/dist/feedback/Empty.js.map +1 -1
  159. package/dist/feedback/LoadingBar.js +20 -13
  160. package/dist/feedback/LoadingBar.js.map +1 -1
  161. package/dist/feedback/LoadingOverlay.js +3 -3
  162. package/dist/feedback/LoadingOverlay.js.map +1 -1
  163. package/dist/feedback/Progress.js +14 -14
  164. package/dist/feedback/Progress.js.map +1 -1
  165. package/dist/feedback/Result.js +4 -4
  166. package/dist/feedback/Result.js.map +1 -1
  167. package/dist/feedback/SkeletonText.js +3 -3
  168. package/dist/feedback/SkeletonText.js.map +1 -1
  169. package/dist/feedback/Toast.js +6 -6
  170. package/dist/feedback/Toast.js.map +1 -1
  171. package/dist/form/Field.js +7 -7
  172. package/dist/form/Field.js.map +1 -1
  173. package/dist/form/Form.js +3 -3
  174. package/dist/form/Form.js.map +1 -1
  175. package/dist/form/FormErrorSummary.js +3 -3
  176. package/dist/form/FormErrorSummary.js.map +1 -1
  177. package/dist/form/FormSection.js +6 -6
  178. package/dist/form/FormSection.js.map +1 -1
  179. package/dist/index.d.ts +11 -6
  180. package/dist/index.js +10 -5
  181. package/dist/input/Autocomplete.js +3 -3
  182. package/dist/input/Autocomplete.js.map +1 -1
  183. package/dist/input/Cascader.js +7 -7
  184. package/dist/input/Cascader.js.map +1 -1
  185. package/dist/input/CharacterCount.js +3 -3
  186. package/dist/input/CharacterCount.js.map +1 -1
  187. package/dist/input/Checkbox.js +5 -5
  188. package/dist/input/Checkbox.js.map +1 -1
  189. package/dist/input/ColorField.js +4 -4
  190. package/dist/input/ColorField.js.map +1 -1
  191. package/dist/input/Combobox.js +4 -4
  192. package/dist/input/Combobox.js.map +1 -1
  193. package/dist/input/ConditionBuilder.js +15 -14
  194. package/dist/input/ConditionBuilder.js.map +1 -1
  195. package/dist/input/FileDrop.js +3 -3
  196. package/dist/input/FileDrop.js.map +1 -1
  197. package/dist/input/Listbox.js +9 -9
  198. package/dist/input/Listbox.js.map +1 -1
  199. package/dist/input/Mentions.js +3 -3
  200. package/dist/input/Mentions.js.map +1 -1
  201. package/dist/input/NumberField.js +3 -3
  202. package/dist/input/NumberField.js.map +1 -1
  203. package/dist/input/NumberRangeField.js +5 -5
  204. package/dist/input/NumberRangeField.js.map +1 -1
  205. package/dist/input/OtpField.js +5 -5
  206. package/dist/input/OtpField.js.map +1 -1
  207. package/dist/input/PeoplePicker.js +6 -6
  208. package/dist/input/PeoplePicker.js.map +1 -1
  209. package/dist/input/PeoplePickerPanel.js +4 -4
  210. package/dist/input/PeoplePickerPanel.js.map +1 -1
  211. package/dist/input/Radio.js +4 -4
  212. package/dist/input/Radio.js.map +1 -1
  213. package/dist/input/Rating.js +5 -5
  214. package/dist/input/Rating.js.map +1 -1
  215. package/dist/input/Select.js +4 -4
  216. package/dist/input/Select.js.map +1 -1
  217. package/dist/input/SelectedTags.js +4 -4
  218. package/dist/input/SelectedTags.js.map +1 -1
  219. package/dist/input/Slider.js +6 -6
  220. package/dist/input/Slider.js.map +1 -1
  221. package/dist/input/Switch.js +3 -3
  222. package/dist/input/Switch.js.map +1 -1
  223. package/dist/input/TagsInput.js +5 -5
  224. package/dist/input/TagsInput.js.map +1 -1
  225. package/dist/input/TextField.js +5 -5
  226. package/dist/input/TextField.js.map +1 -1
  227. package/dist/input/Textarea.js +4 -4
  228. package/dist/input/Textarea.js.map +1 -1
  229. package/dist/input/ToggleGroup.js +4 -4
  230. package/dist/input/ToggleGroup.js.map +1 -1
  231. package/dist/input/Transfer.js +8 -8
  232. package/dist/input/Transfer.js.map +1 -1
  233. package/dist/input/TreeSelect.js +6 -6
  234. package/dist/input/TreeSelect.js.map +1 -1
  235. package/dist/input/Upload.js +8 -8
  236. package/dist/input/Upload.js.map +1 -1
  237. package/dist/layout/Affix.js +4 -4
  238. package/dist/layout/Affix.js.map +1 -1
  239. package/dist/layout/ResizeHandle.d.ts +6 -2
  240. package/dist/layout/ResizeHandle.js +32 -9
  241. package/dist/layout/ResizeHandle.js.map +1 -1
  242. package/dist/layout/ScrollArea.js +3 -3
  243. package/dist/layout/ScrollArea.js.map +1 -1
  244. package/dist/layout/Separator.js +3 -3
  245. package/dist/layout/Separator.js.map +1 -1
  246. package/dist/layout/Splitter.js +3 -3
  247. package/dist/layout/Splitter.js.map +1 -1
  248. package/dist/layout/ViewportFill.d.ts +2 -2
  249. package/dist/layout/ViewportFill.js +4 -4
  250. package/dist/layout/ViewportFill.js.map +1 -1
  251. package/dist/navigation/Breadcrumbs.js +4 -4
  252. package/dist/navigation/Breadcrumbs.js.map +1 -1
  253. package/dist/navigation/NavBar.js +6 -6
  254. package/dist/navigation/NavBar.js.map +1 -1
  255. package/dist/navigation/NavList.d.ts +1 -0
  256. package/dist/navigation/NavList.js +20 -12
  257. package/dist/navigation/NavList.js.map +1 -1
  258. package/dist/navigation/NavMenu.d.ts +27 -0
  259. package/dist/navigation/NavMenu.js +54 -0
  260. package/dist/navigation/NavMenu.js.map +1 -0
  261. package/dist/navigation/PageJump.js +3 -3
  262. package/dist/navigation/PageJump.js.map +1 -1
  263. package/dist/navigation/PageSizeSelect.js +4 -4
  264. package/dist/navigation/PageSizeSelect.js.map +1 -1
  265. package/dist/navigation/Pagination.js +6 -6
  266. package/dist/navigation/Pagination.js.map +1 -1
  267. package/dist/navigation/PaginationBar.js +5 -5
  268. package/dist/navigation/PaginationBar.js.map +1 -1
  269. package/dist/navigation/Steps.js +4 -4
  270. package/dist/navigation/Steps.js.map +1 -1
  271. package/dist/navigation/TableOfContents.js +5 -5
  272. package/dist/navigation/TableOfContents.js.map +1 -1
  273. package/dist/navigation/Tabs.js +7 -7
  274. package/dist/navigation/Tabs.js.map +1 -1
  275. package/dist/navigation/Toolbar.js +3 -3
  276. package/dist/navigation/Toolbar.js.map +1 -1
  277. package/dist/overlay/AlertDialog.js +4 -4
  278. package/dist/overlay/AlertDialog.js.map +1 -1
  279. package/dist/overlay/CommandPalette.js +4 -4
  280. package/dist/overlay/CommandPalette.js.map +1 -1
  281. package/dist/overlay/ContextMenu.js +3 -3
  282. package/dist/overlay/ContextMenu.js.map +1 -1
  283. package/dist/overlay/Dialog.js +4 -4
  284. package/dist/overlay/Dialog.js.map +1 -1
  285. package/dist/overlay/Drawer.js +5 -5
  286. package/dist/overlay/Drawer.js.map +1 -1
  287. package/dist/overlay/HoverCard.js +5 -3
  288. package/dist/overlay/HoverCard.js.map +1 -1
  289. package/dist/overlay/Lightbox.js +5 -5
  290. package/dist/overlay/Lightbox.js.map +1 -1
  291. package/dist/overlay/Menu.js +7 -7
  292. package/dist/overlay/Menu.js.map +1 -1
  293. package/dist/overlay/ModalCore.js +9 -9
  294. package/dist/overlay/ModalCore.js.map +1 -1
  295. package/dist/overlay/Popconfirm.js +6 -6
  296. package/dist/overlay/Popconfirm.js.map +1 -1
  297. package/dist/overlay/Popover.js +5 -3
  298. package/dist/overlay/Popover.js.map +1 -1
  299. package/dist/overlay/Tooltip.js +5 -3
  300. package/dist/overlay/Tooltip.js.map +1 -1
  301. package/dist/overlay/Tour.js +8 -8
  302. package/dist/overlay/Tour.js.map +1 -1
  303. package/dist/overlay/detached.d.ts +2 -0
  304. package/dist/overlay/detached.js +73 -42
  305. package/dist/overlay/detached.js.map +1 -1
  306. package/dist/patterns/ActionBar.js +3 -3
  307. package/dist/patterns/ActionBar.js.map +1 -1
  308. package/dist/patterns/AppShell.d.ts +28 -1
  309. package/dist/patterns/AppShell.js +158 -52
  310. package/dist/patterns/AppShell.js.map +1 -1
  311. package/dist/patterns/AuthLayout.d.ts +28 -0
  312. package/dist/patterns/AuthLayout.js +77 -0
  313. package/dist/patterns/AuthLayout.js.map +1 -0
  314. package/dist/patterns/CommentThread.js +9 -9
  315. package/dist/patterns/CommentThread.js.map +1 -1
  316. package/dist/patterns/DataImport.js +4 -4
  317. package/dist/patterns/DataImport.js.map +1 -1
  318. package/dist/patterns/DeadlineBadge.js +3 -3
  319. package/dist/patterns/DeadlineBadge.js.map +1 -1
  320. package/dist/patterns/FilterBar.d.ts +11 -1
  321. package/dist/patterns/FilterBar.js +39 -10
  322. package/dist/patterns/FilterBar.js.map +1 -1
  323. package/dist/patterns/FormDialog.d.ts +18 -0
  324. package/dist/patterns/FormDialog.js +46 -0
  325. package/dist/patterns/FormDialog.js.map +1 -0
  326. package/dist/patterns/KanbanBoard.js +8 -8
  327. package/dist/patterns/KanbanBoard.js.map +1 -1
  328. package/dist/patterns/LayoutSwitcher.d.ts +12 -0
  329. package/dist/patterns/LayoutSwitcher.js +79 -0
  330. package/dist/patterns/LayoutSwitcher.js.map +1 -0
  331. package/dist/patterns/NotificationCenter.js +8 -8
  332. package/dist/patterns/NotificationCenter.js.map +1 -1
  333. package/dist/patterns/PageTabs.d.ts +12 -7
  334. package/dist/patterns/PageTabs.js +41 -29
  335. package/dist/patterns/PageTabs.js.map +1 -1
  336. package/dist/patterns/QueryTable.d.ts +7 -1
  337. package/dist/patterns/QueryTable.js +116 -81
  338. package/dist/patterns/QueryTable.js.map +1 -1
  339. package/dist/patterns/SlideCaptcha.d.ts +80 -0
  340. package/dist/patterns/SlideCaptcha.js +354 -0
  341. package/dist/patterns/SlideCaptcha.js.map +1 -0
  342. package/dist/patterns/StepsForm.js +7 -7
  343. package/dist/patterns/StepsForm.js.map +1 -1
  344. package/dist/patterns/ThemeSwitcher.js +3 -3
  345. package/dist/patterns/ThemeSwitcher.js.map +1 -1
  346. package/dist/patterns/index.d.ts +7 -3
  347. package/dist/patterns/index.js +5 -1
  348. package/dist/patterns/messages.d.ts +37 -1
  349. package/dist/patterns/messages.js +38 -2
  350. package/dist/patterns/messages.js.map +1 -1
  351. package/dist/table/ColumnHeaderActions.js +3 -3
  352. package/dist/table/ColumnHeaderActions.js.map +1 -1
  353. package/dist/table/ColumnResizer.js +3 -3
  354. package/dist/table/ColumnResizer.js.map +1 -1
  355. package/dist/table/ColumnSettings.js +5 -5
  356. package/dist/table/ColumnSettings.js.map +1 -1
  357. package/dist/table/ColumnTable.js +8 -8
  358. package/dist/table/ColumnTable.js.map +1 -1
  359. package/dist/table/DataTable.js +8 -8
  360. package/dist/table/DataTable.js.map +1 -1
  361. package/dist/table/DataTableView.js +21 -21
  362. package/dist/table/DataTableView.js.map +1 -1
  363. package/dist/table/PivotTable.js +3 -3
  364. package/dist/table/PivotTable.js.map +1 -1
  365. package/dist/table/SelectionBar.js +4 -4
  366. package/dist/table/SelectionBar.js.map +1 -1
  367. package/dist/table/TreeDataTable.js +5 -5
  368. package/dist/table/TreeDataTable.js.map +1 -1
  369. package/dist/theme-customize/ThemeCustomizer.js +7 -6
  370. package/dist/theme-customize/ThemeCustomizer.js.map +1 -1
  371. package/dist/theme-customize/ThemeThumbnail.js +3 -3
  372. package/dist/theme-customize/ThemeThumbnail.js.map +1 -1
  373. package/dist/three/background/AuroraBackground.js +3 -3
  374. package/dist/three/background/AuroraBackground.js.map +1 -1
  375. package/dist/three/background/MeshGradient.js +3 -3
  376. package/dist/three/background/MeshGradient.js.map +1 -1
  377. package/dist/three/background/ShaderBackground.js +3 -3
  378. package/dist/three/background/ShaderBackground.js.map +1 -1
  379. package/dist/three/core/EffectSurface.js +3 -3
  380. package/dist/three/core/EffectSurface.js.map +1 -1
  381. package/dist/three/core/ThreeCanvas.js +4 -4
  382. package/dist/three/core/ThreeCanvas.js.map +1 -1
  383. package/dist/three/effects/Confetti.js +3 -3
  384. package/dist/three/effects/Confetti.js.map +1 -1
  385. package/dist/three/effects/ParticleField.js +3 -3
  386. package/dist/three/effects/ParticleField.js.map +1 -1
  387. package/dist/three/effects/WaveGrid.js +3 -3
  388. package/dist/three/effects/WaveGrid.js.map +1 -1
  389. package/dist/three/globe/Globe.js +3 -3
  390. package/dist/three/globe/Globe.js.map +1 -1
  391. package/dist/three/model/ModelViewer.js +3 -3
  392. package/dist/three/model/ModelViewer.js.map +1 -1
  393. package/dist/utils/data/number.d.ts +5 -0
  394. package/dist/utils/data/number.js +8 -1
  395. package/dist/utils/data/number.js.map +1 -1
  396. package/dist/utils/dom/element.d.ts +3 -1
  397. package/dist/utils/dom/element.js +8 -4
  398. package/dist/utils/dom/element.js.map +1 -1
  399. package/dist/utils/dom/fullscreen.d.ts +1 -1
  400. package/dist/utils/dom/fullscreen.js +12 -8
  401. package/dist/utils/dom/fullscreen.js.map +1 -1
  402. package/dist/utils/dom/mounted.d.ts +7 -0
  403. package/dist/utils/dom/mounted.js +16 -0
  404. package/dist/utils/dom/mounted.js.map +1 -0
  405. package/dist/utils/dom/overflow.js +2 -1
  406. package/dist/utils/dom/overflow.js.map +1 -1
  407. package/dist/utils/dom/pan-zoom.js +2 -1
  408. package/dist/utils/dom/pan-zoom.js.map +1 -1
  409. package/dist/utils/props.js +3 -13
  410. package/dist/utils/props.js.map +1 -1
  411. package/dist/utils/request/http.d.ts +12 -0
  412. package/dist/utils/request/http.js +6 -2
  413. package/dist/utils/request/http.js.map +1 -1
  414. package/dist/utils/request/use-request.js +4 -2
  415. package/dist/utils/request/use-request.js.map +1 -1
  416. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"KanbanBoard.js","names":["_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_vue_shallowRef","_createVNode","_mergeProps"],"sources":["../../src/patterns/KanbanBoard.tsx"],"sourcesContent":["/**\n * 看板:一列一个状态(或阶段、负责人),卡片在列之间移动。任务看板、招聘流程、工单。\n *\n * <KanbanBoard\n * aria-label=\"任务看板\"\n * columns={[{ key: 'designing', label: '设计中' }, { key: 'verifying', label: '测试中' }]}\n * items={tasks}\n * getKey={(task) => task.code}\n * getColumn={(task) => task.status}\n * getLabel={(task) => task.name}\n * canMove={(task, to) => (to === next(task.status) ? null : '只能拖到下一列')}\n * onMove={(task, to) => openProcessDialog(task, to)}\n * >\n * {(task) => <TaskCard task={task} />}\n * </KanbanBoard>\n *\n * 数据由使用方控制:看板不自己挪卡片,只在放下时报告 onMove(使用方处理完、刷新数据,卡片才换列)。\n * canMove 返回不能移动的原因(null 是可以):拖动时经过的列按它高亮(可以放的是强调色,不能放的标红并写出原因),\n * 落点处画一条插入线;放在不能放的列上时卡片弹回、读屏播报原因,并报告 onReject。\n * 给了 onReorder 时列内也能排序(数据立即重排,写回整个数组)。\n *\n * 拖动、键盘、读屏播报、自动滚动建在 useSortable 的分组上(utils/dom/sortable.ts):\n * - 指针:在卡片上按下拖动超过 3px 开始(点在卡片里的链接、按钮上不算),影子卡跟着指针走,原位留着变淡的占位;\n * - 键盘:每张卡片有把手按钮,空格 / Enter 拿起,← / → 换列(从右到左排版下镜像),↑ / ↓ 调整位置,\n * 空格 / Enter 放下,Esc 取消;每一步都有读屏播报(aria-live)。\n * 列多时整块横向滚动(ScrollArea),每列里的卡片多时列自己竖向滚动。\n */\nimport { GripVertical } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { Badge } from '../base/Badge';\nimport { useMessages } from '../config/lang/messages';\nimport { patternsMessages } from './messages';\nimport { ButtonBase } from '../base/ButtonBase';\nimport { Icon } from '../base/Icon';\nimport { VisuallyHidden } from '../base/VisuallyHidden';\nimport { useLang } from '../config/context';\nimport { ScrollArea } from '../layout/ScrollArea';\nimport { useSortable } from '../utils/dom/sortable';\nimport { mergeAttrs, type PartProps } from '../utils/props';\nimport type { StatusTone } from '../utils/style/tone-styles';\nimport { t } from '../config/theme/tokens';\n\n/** 看板的一列 */\nexport interface KanbanColumn {\n /** 列的标识:getColumn 返回它 */\n key: string;\n /** 列名 */\n label: string;\n /** 列名旁计数的色调(超时、异常这类要注意的列) */\n tone?: StatusTone;\n}\n\n/** 卡片的渲染状态 */\nexport interface KanbanCardState {\n /** 正在被拖动或被键盘拿起 */\n dragging: boolean;\n}\n\n/** 未声明的属性落到外层 div 上;aria-label 是看板的名称 */\nexport interface KanbanBoardProps<T> extends PartProps<'div'> {\n /** 各列:从起始边排 */\n columns: readonly KanbanColumn[];\n /** 全部卡片的数据 */\n items: readonly T[];\n /** 卡片的标识 */\n getKey: (item: T) => string;\n /** 卡片在哪一列 */\n getColumn: (item: T) => string;\n /** 卡片的名称:把手的名称、读屏播报;默认是 key */\n getLabel?: (item: T) => string;\n /** 能不能移到 to 列:返回不能的原因,null、undefined 是可以 */\n canMove?: (item: T, to: string) => string | null | undefined;\n /** 放到了可以放的列:使用方处理(打开处理的对话框、调接口),完成后刷新数据;index 是在目标列里的位置 */\n onMove: (item: T, to: string, index: number) => void;\n /** 列内排序:写回新的数组(所有列的卡片);不给时列内不能排序 */\n onReorder?: (items: T[]) => void;\n /** 放到了不能放的列:卡片已经弹回,可以在这里提示原因 */\n onReject?: (item: T, to: string, reason: string) => void;\n /**\n * 每列至少多宽(CSS 长度):放得下时各列平分整行,放不下时横向滚动\n * @default '16rem'\n */\n columnWidth?: string;\n /**\n * 每列的最大高度(CSS 长度):卡片多时列里竖向滚动\n * @default '70vh'\n */\n columnHeight?: string;\n /** 一列没有卡片时显示的内容,默认“没有” */\n empty?: VNodeChild;\n /** 卡片的内容 */\n children: (item: T, state: KanbanCardState) => VNodeChild;\n}\n\n/** KanbanBoard 的样式:board、column、header、list、card、handle… */\nexport const kanbanBoardStyles = css.create({\n board: { display: 'flex', gap: '_md', alignItems: 'flex-start', paddingBlockEnd: '_sm' },\n column: {\n display: 'flex',\n flexDirection: 'column',\n flexShrink: 0,\n borderRadius: '_lg',\n backgroundColor: '_surfaceMuted',\n outlineWidth: '_borderWidths.sm',\n outlineStyle: 'solid',\n outlineColor: 'transparent',\n outlineOffset: '-2px',\n transition: `outline-color ${t.durations.sm} ${t.easings.standard}`,\n },\n allowed: { outlineColor: '_accent' },\n denied: { outlineColor: '_danger' },\n header: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '_sm',\n paddingBlock: '_sm',\n paddingInline: '_md',\n fontWeight: '_semibold',\n fontSize: '_sm',\n },\n reason: { paddingInline: '_md', paddingBlockEnd: '_xs', color: '_dangerText', fontSize: '_xs' },\n list: {\n display: 'flex',\n flexDirection: 'column',\n gap: '_sm',\n margin: 0,\n paddingInline: '_sm',\n paddingBlockEnd: '_sm',\n listStyle: 'none',\n overflowY: 'auto',\n minHeight: '3rem',\n },\n card: {\n position: 'relative',\n display: 'grid',\n gridTemplateColumns: 'minmax(0, 1fr) auto',\n alignItems: 'start',\n gap: '_xs',\n padding: '_sm',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_border',\n borderRadius: '_md',\n backgroundColor: '_surfaceRaised',\n cursor: 'grab',\n touchAction: 'none',\n transition: `translate ${t.durations.md} ${t.easings.standard}`,\n },\n // 指针拖动中:原位的卡片变淡,跟着指针走的是影子卡(画在滚动区域外面,不被列裁掉)\n placeholder: { opacity: 0.4 },\n ghost: {\n position: 'fixed',\n zIndex: 50,\n pointerEvents: 'none',\n cursor: 'grabbing',\n boxShadow: '_lg',\n transition: 'none',\n rotate: '1.5deg',\n },\n // 键盘拿起:高亮边框\n lifted: { borderColor: '_accent', boxShadow: '_md' },\n handle: { color: '_textMuted', borderRadius: '_sm', padding: '2px', cursor: 'grab' },\n empty: { color: '_textMuted', fontSize: '_xs', paddingBlock: '_sm', textAlign: 'center' },\n // 拖到别的列时的落点:卡片之间的一条线\n indicator: {\n height: '_borderWidths.md',\n marginBlock: `calc(-1 * ${t.space.xs})`,\n borderRadius: '_full',\n backgroundColor: '_accent',\n },\n indicatorDenied: { backgroundColor: '_danger' },\n});\n\n/** KanbanBoard 的动态样式:列宽、列高、拖动的位移 */\nexport const kanbanBoardDynamicStyles = css.create({\n // 至少这么宽,富余的空间各列平分(同 Jira 的看板),放不下时外层横向滚动\n width: (width: string) => ({ flexBasis: width, flexGrow: 1, minWidth: width }),\n height: (height: string) => ({ maxHeight: height }),\n ghost: (left: number, top: number, width: number) => ({ left, top, width }),\n});\n\n/** 看板 */\nexport const KanbanBoard = _component(\n <T,>({\n columns,\n items,\n getKey,\n getColumn,\n getLabel,\n canMove,\n onMove,\n onReorder,\n onReject,\n columnWidth = '16rem',\n columnHeight = '70vh',\n empty,\n children,\n ...rest\n }: KanbanBoardProps<T>) => {\n const lang = useLang();\n const messages = useMessages('patterns', patternsMessages);\n let board = _state.raw<HTMLElement | null>(null);\n const labelOf = (item: T) => getLabel?.(item) ?? getKey(item);\n const itemOf = (key: string) => items.find((item) => getKey(item) === key);\n const sortable = useSortable({\n items: () => items,\n getKey: (item) => getKey(item),\n getLabel: labelOf,\n // 用时再读属性:写没写 onReorder 决定了列内能不能排序,父级后来才给也要跟上\n get onChange() {\n return onReorder && ((next: T[]) => onReorder(next));\n },\n container: () => board,\n groups: {\n keys: () => columns.map((column) => column.key),\n of: (item) => getColumn(item),\n label: (key) => columns.find((column) => column.key === key)?.label ?? key,\n canMove: (item, to) => canMove?.(item, to),\n onMove: (item, to, index) => onMove(item, to, index),\n onReject: (item, to, reason) => onReject?.(item, to, reason),\n },\n });\n\n /** 整张卡片都能拖:卡片里的链接、按钮(把手除外)、输入框照常点 */\n function onPointerdown(event: PointerEvent, key: string) {\n const target = event.target as Element;\n if (target.closest('a, input, select, textarea, button:not([data-handle])')) return;\n (sortable.handleProps(key).onPointerdown as (event: PointerEvent) => void)(event);\n }\n\n return () => {\n const dragging = sortable.active;\n const drop = sortable.target;\n const ghost = sortable.ghost;\n const ghostItem = ghost && itemOf(ghost.key);\n const draggedItem = dragging === undefined ? undefined : itemOf(dragging);\n const draggedFrom = draggedItem === undefined ? undefined : getColumn(draggedItem);\n return (\n <div {...mergeAttrs<'div'>({}, rest, { role: 'group' })}>\n <ScrollArea axis=\"x\">\n <div\n ref={(element: unknown) => (board = element instanceof HTMLElement ? element : null)}\n {...css.attrs(kanbanBoardStyles.board)}\n >\n {columns.map((column) => {\n const cards = items.filter((item) => getColumn(item) === column.key);\n // 拖到了别的列:高亮这一列,在落点画线\n const over = drop && draggedFrom !== column.key && drop.group === column.key;\n const reason = over ? drop.reason : null;\n const headingId = `${sortable.instructionsId}-${column.key}`;\n const indicator = (\n <li\n key=\"drop-indicator\"\n aria-hidden=\"true\"\n {...css.attrs(\n kanbanBoardStyles.indicator,\n reason != null && kanbanBoardStyles.indicatorDenied,\n )}\n />\n );\n return (\n <section\n key={column.key}\n aria-labelledby={headingId}\n {...sortable.groupProps(column.key)}\n {...css.attrs(\n kanbanBoardStyles.column,\n kanbanBoardDynamicStyles.width(columnWidth),\n over && (reason ? kanbanBoardStyles.denied : kanbanBoardStyles.allowed),\n )}\n >\n <div {...css.attrs(kanbanBoardStyles.header)}>\n <span id={headingId}>{column.label}</span>\n <Badge\n radius=\"full\"\n tone={cards.length > 0 ? (column.tone ?? 'neutral') : 'neutral'}\n >\n {cards.length}\n </Badge>\n </div>\n {reason && <div {...css.attrs(kanbanBoardStyles.reason)}>{reason}</div>}\n <ul\n {...css.attrs(\n kanbanBoardStyles.list,\n kanbanBoardDynamicStyles.height(columnHeight),\n )}\n >\n {cards.length === 0 && !over && (\n <li {...css.attrs(kanbanBoardStyles.empty)}>\n {empty ?? messages.kanbanEmpty}\n </li>\n )}\n {cards.flatMap((item, index) => {\n const key = getKey(item);\n const active = dragging === key;\n const { onPointerdown: _, ...handle } = sortable.handleProps(key);\n const card = (\n <li\n key={key}\n {...sortable.itemProps(key)}\n {...css.attrs(\n kanbanBoardStyles.card,\n active && ghost && kanbanBoardStyles.placeholder,\n active && !ghost && kanbanBoardStyles.lifted,\n )}\n data-state={active ? 'dragging' : undefined}\n onPointerdown={(event: PointerEvent) => onPointerdown(event, key)}\n >\n <div>{children(item, { dragging: active })}</div>\n <ButtonBase\n {...handle}\n data-handle=\"\"\n sx={kanbanBoardStyles.handle}\n aria-label={lang.sortableHandle(labelOf(item))}\n >\n <Icon icon={GripVertical} />\n </ButtonBase>\n </li>\n );\n return over && drop.index === index ? [indicator, card] : [card];\n })}\n {over && drop.index >= cards.length && indicator}\n </ul>\n </section>\n );\n })}\n </div>\n </ScrollArea>\n {ghost && ghostItem !== undefined && (\n <div\n aria-hidden=\"true\"\n {...css.attrs(\n kanbanBoardStyles.card,\n kanbanBoardStyles.ghost,\n kanbanBoardDynamicStyles.ghost(ghost.left, ghost.top, ghost.width),\n )}\n >\n <div>{children(ghostItem, { dragging: true })}</div>\n </div>\n )}\n <VisuallyHidden id={sortable.instructionsId}>\n {messages.kanbanInstructions}\n </VisuallyHidden>\n <VisuallyHidden aria-live=\"assertive\">{sortable.announcement}</VisuallyHidden>\n </div>\n );\n };\n },\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"KanbanBoard.js","names":["_vtp_readonlyProp","_vtp_childrenProp","_vtp_restProps","_vue_shallowRef","_createVNode","_mergeProps"],"sources":["../../src/patterns/KanbanBoard.tsx"],"sourcesContent":["/**\n * 看板:一列一个状态(或阶段、负责人),卡片在列之间移动。任务看板、招聘流程、工单。\n *\n * <KanbanBoard\n * aria-label=\"任务看板\"\n * columns={[{ key: 'designing', label: '设计中' }, { key: 'verifying', label: '测试中' }]}\n * items={tasks}\n * getKey={(task) => task.code}\n * getColumn={(task) => task.status}\n * getLabel={(task) => task.name}\n * canMove={(task, to) => (to === next(task.status) ? null : '只能拖到下一列')}\n * onMove={(task, to) => openProcessDialog(task, to)}\n * >\n * {(task) => <TaskCard task={task} />}\n * </KanbanBoard>\n *\n * 数据由使用方控制:看板不自己挪卡片,只在放下时报告 onMove(使用方处理完、刷新数据,卡片才换列)。\n * canMove 返回不能移动的原因(null 是可以):拖动时经过的列按它高亮(可以放的是强调色,不能放的标红并写出原因),\n * 落点处画一条插入线;放在不能放的列上时卡片弹回、读屏播报原因,并报告 onReject。\n * 给了 onReorder 时列内也能排序(数据立即重排,写回整个数组)。\n *\n * 拖动、键盘、读屏播报、自动滚动建在 useSortable 的分组上(utils/dom/sortable.ts):\n * - 指针:在卡片上按下拖动超过 3px 开始(点在卡片里的链接、按钮上不算),影子卡跟着指针走,原位留着变淡的占位;\n * - 键盘:每张卡片有把手按钮,空格 / Enter 拿起,← / → 换列(从右到左排版下镜像),↑ / ↓ 调整位置,\n * 空格 / Enter 放下,Esc 取消;每一步都有读屏播报(aria-live)。\n * 列多时整块横向滚动(ScrollArea),每列里的卡片多时列自己竖向滚动。\n */\nimport { GripVertical } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { Badge } from '../base/Badge';\nimport { useMessages } from '../config/lang/messages';\nimport { patternsMessages } from './messages';\nimport { ButtonBase } from '../base/ButtonBase';\nimport { Icon } from '../base/Icon';\nimport { VisuallyHidden } from '../base/VisuallyHidden';\nimport { useLang } from '../config/context';\nimport { ScrollArea } from '../layout/ScrollArea';\nimport { useSortable } from '../utils/dom/sortable';\nimport { mergeAttrs, type PartProps } from '../utils/props';\nimport type { StatusTone } from '../utils/style/tone-styles';\nimport { t } from '../config/theme/tokens';\n\n/** 看板的一列 */\nexport interface KanbanColumn {\n /** 列的标识:getColumn 返回它 */\n key: string;\n /** 列名 */\n label: string;\n /** 列名旁计数的色调(超时、异常这类要注意的列) */\n tone?: StatusTone;\n}\n\n/** 卡片的渲染状态 */\nexport interface KanbanCardState {\n /** 正在被拖动或被键盘拿起 */\n dragging: boolean;\n}\n\n/** 未声明的属性落到外层 div 上;aria-label 是看板的名称 */\nexport interface KanbanBoardProps<T> extends PartProps<'div'> {\n /** 各列:从起始边排 */\n columns: readonly KanbanColumn[];\n /** 全部卡片的数据 */\n items: readonly T[];\n /** 卡片的标识 */\n getKey: (item: T) => string;\n /** 卡片在哪一列 */\n getColumn: (item: T) => string;\n /** 卡片的名称:把手的名称、读屏播报;默认是 key */\n getLabel?: (item: T) => string;\n /** 能不能移到 to 列:返回不能的原因,null、undefined 是可以 */\n canMove?: (item: T, to: string) => string | null | undefined;\n /** 放到了可以放的列:使用方处理(打开处理的对话框、调接口),完成后刷新数据;index 是在目标列里的位置 */\n onMove: (item: T, to: string, index: number) => void;\n /** 列内排序:写回新的数组(所有列的卡片);不给时列内不能排序 */\n onReorder?: (items: T[]) => void;\n /** 放到了不能放的列:卡片已经弹回,可以在这里提示原因 */\n onReject?: (item: T, to: string, reason: string) => void;\n /**\n * 每列至少多宽(CSS 长度):放得下时各列平分整行,放不下时横向滚动\n * @default '16rem'\n */\n columnWidth?: string;\n /**\n * 每列的最大高度(CSS 长度):卡片多时列里竖向滚动\n * @default '70vh'\n */\n columnHeight?: string;\n /** 一列没有卡片时显示的内容,默认“没有” */\n empty?: VNodeChild;\n /** 卡片的内容 */\n children: (item: T, state: KanbanCardState) => VNodeChild;\n}\n\n/** KanbanBoard 的样式:board、column、header、list、card、handle… */\nexport const kanbanBoardStyles = css.create({\n board: { display: 'flex', gap: '_md', alignItems: 'flex-start', paddingBlockEnd: '_sm' },\n column: {\n display: 'flex',\n flexDirection: 'column',\n flexShrink: 0,\n borderRadius: '_lg',\n backgroundColor: '_surfaceMuted',\n outlineWidth: '_borderWidths.sm',\n outlineStyle: 'solid',\n outlineColor: 'transparent',\n outlineOffset: '-2px',\n transition: `outline-color ${t.durations.sm} ${t.easings.standard}`,\n },\n allowed: { outlineColor: '_accent' },\n denied: { outlineColor: '_danger' },\n header: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '_sm',\n paddingBlock: '_sm',\n paddingInline: '_md',\n fontWeight: '_semibold',\n fontSize: '_sm',\n },\n reason: { paddingInline: '_md', paddingBlockEnd: '_xs', color: '_dangerText', fontSize: '_xs' },\n list: {\n display: 'flex',\n flexDirection: 'column',\n gap: '_sm',\n margin: 0,\n paddingInline: '_sm',\n paddingBlockEnd: '_sm',\n listStyle: 'none',\n overflowY: 'auto',\n minHeight: '3rem',\n },\n card: {\n position: 'relative',\n display: 'grid',\n gridTemplateColumns: 'minmax(0, 1fr) auto',\n alignItems: 'start',\n gap: '_xs',\n padding: '_sm',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_border',\n borderRadius: '_md',\n backgroundColor: '_surfaceRaised',\n cursor: 'grab',\n touchAction: 'none',\n transition: `translate ${t.durations.md} ${t.easings.standard}`,\n },\n // 指针拖动中:原位的卡片变淡,跟着指针走的是影子卡(画在滚动区域外面,不被列裁掉)\n placeholder: { opacity: 0.4 },\n ghost: {\n position: 'fixed',\n zIndex: 50,\n pointerEvents: 'none',\n cursor: 'grabbing',\n boxShadow: '_lg',\n transition: 'none',\n rotate: '1.5deg',\n },\n // 键盘拿起:高亮边框\n lifted: { borderColor: '_accent', boxShadow: '_md' },\n handle: { color: '_textMuted', borderRadius: '_sm', padding: '2px', cursor: 'grab' },\n empty: { color: '_textMuted', fontSize: '_xs', paddingBlock: '_sm', textAlign: 'center' },\n // 拖到别的列时的落点:卡片之间的一条线\n indicator: {\n height: '_borderWidths.md',\n marginBlock: `calc(-1 * ${t.space.xs})`,\n borderRadius: '_full',\n backgroundColor: '_accent',\n },\n indicatorDenied: { backgroundColor: '_danger' },\n});\n\n/** KanbanBoard 的动态样式:列宽、列高、拖动的位移 */\nexport const kanbanBoardDynamicStyles = css.create({\n // 至少这么宽,富余的空间各列平分(同 Jira 的看板),放不下时外层横向滚动\n width: (width: string) => ({ flexBasis: width, flexGrow: 1, minWidth: width }),\n height: (height: string) => ({ maxHeight: height }),\n ghost: (left: number, top: number, width: number) => ({ left, top, width }),\n});\n\n/** 看板 */\nexport const KanbanBoard = _component(\n <T,>({\n columns,\n items,\n getKey,\n getColumn,\n getLabel,\n canMove,\n onMove,\n onReorder,\n onReject,\n columnWidth = '16rem',\n columnHeight = '70vh',\n empty,\n children,\n ...rest\n }: KanbanBoardProps<T>) => {\n const lang = useLang();\n const messages = useMessages('patterns', patternsMessages);\n let board = _state.raw<HTMLElement | null>(null);\n const labelOf = (item: T) => getLabel?.(item) ?? getKey(item);\n const itemOf = (key: string) => items.find((item) => getKey(item) === key);\n const sortable = useSortable({\n items: () => items,\n getKey: (item) => getKey(item),\n getLabel: labelOf,\n // 用时再读属性:写没写 onReorder 决定了列内能不能排序,父级后来才给也要跟上\n get onChange() {\n return onReorder && ((next: T[]) => onReorder(next));\n },\n container: () => board,\n groups: {\n keys: () => columns.map((column) => column.key),\n of: (item) => getColumn(item),\n label: (key) => columns.find((column) => column.key === key)?.label ?? key,\n canMove: (item, to) => canMove?.(item, to),\n onMove: (item, to, index) => onMove(item, to, index),\n onReject: (item, to, reason) => onReject?.(item, to, reason),\n },\n });\n\n /** 整张卡片都能拖:卡片里的链接、按钮(把手除外)、输入框照常点 */\n function onPointerdown(event: PointerEvent, key: string) {\n const target = event.target as Element;\n if (target.closest('a, input, select, textarea, button:not([data-handle])')) return;\n (sortable.handleProps(key).onPointerdown as (event: PointerEvent) => void)(event);\n }\n\n return () => {\n const dragging = sortable.active;\n const drop = sortable.target;\n const ghost = sortable.ghost;\n const ghostItem = ghost && itemOf(ghost.key);\n const draggedItem = dragging === undefined ? undefined : itemOf(dragging);\n const draggedFrom = draggedItem === undefined ? undefined : getColumn(draggedItem);\n return (\n <div {...mergeAttrs<'div'>({}, rest, { role: 'group' })}>\n <ScrollArea axis=\"x\">\n <div\n ref={(element: unknown) => (board = element instanceof HTMLElement ? element : null)}\n {...css.attrs(kanbanBoardStyles.board)}\n >\n {columns.map((column) => {\n const cards = items.filter((item) => getColumn(item) === column.key);\n // 拖到了别的列:高亮这一列,在落点画线\n const over = drop && draggedFrom !== column.key && drop.group === column.key;\n const reason = over ? drop.reason : null;\n const headingId = `${sortable.instructionsId}-${column.key}`;\n const indicator = (\n <li\n key=\"drop-indicator\"\n aria-hidden=\"true\"\n {...css.attrs(\n kanbanBoardStyles.indicator,\n reason != null && kanbanBoardStyles.indicatorDenied,\n )}\n />\n );\n return (\n <section\n key={column.key}\n aria-labelledby={headingId}\n {...sortable.groupProps(column.key)}\n {...css.attrs(\n kanbanBoardStyles.column,\n kanbanBoardDynamicStyles.width(columnWidth),\n over && (reason ? kanbanBoardStyles.denied : kanbanBoardStyles.allowed),\n )}\n >\n <div {...css.attrs(kanbanBoardStyles.header)}>\n <span id={headingId}>{column.label}</span>\n <Badge\n radius=\"full\"\n tone={cards.length > 0 ? (column.tone ?? 'neutral') : 'neutral'}\n >\n {cards.length}\n </Badge>\n </div>\n {reason && <div {...css.attrs(kanbanBoardStyles.reason)}>{reason}</div>}\n <ul\n {...css.attrs(\n kanbanBoardStyles.list,\n kanbanBoardDynamicStyles.height(columnHeight),\n )}\n >\n {cards.length === 0 && !over && (\n <li {...css.attrs(kanbanBoardStyles.empty)}>\n {empty ?? messages.kanbanEmpty}\n </li>\n )}\n {cards.flatMap((item, index) => {\n const key = getKey(item);\n const active = dragging === key;\n const { onPointerdown: _, ...handle } = sortable.handleProps(key);\n const card = (\n <li\n key={key}\n {...sortable.itemProps(key)}\n {...css.attrs(\n kanbanBoardStyles.card,\n active && ghost && kanbanBoardStyles.placeholder,\n active && !ghost && kanbanBoardStyles.lifted,\n )}\n data-state={active ? 'dragging' : undefined}\n onPointerdown={(event: PointerEvent) => onPointerdown(event, key)}\n >\n <div>{children(item, { dragging: active })}</div>\n <ButtonBase\n {...handle}\n data-handle=\"\"\n sx={kanbanBoardStyles.handle}\n aria-label={lang.sortableHandle(labelOf(item))}\n >\n <Icon icon={GripVertical} />\n </ButtonBase>\n </li>\n );\n return over && drop.index === index ? [indicator, card] : [card];\n })}\n {over && drop.index >= cards.length && indicator}\n </ul>\n </section>\n );\n })}\n </div>\n </ScrollArea>\n {ghost && ghostItem !== undefined && (\n <div\n aria-hidden=\"true\"\n {...css.attrs(\n kanbanBoardStyles.card,\n kanbanBoardStyles.ghost,\n kanbanBoardDynamicStyles.ghost(ghost.left, ghost.top, ghost.width),\n )}\n >\n <div>{children(ghostItem, { dragging: true })}</div>\n </div>\n )}\n <VisuallyHidden id={sortable.instructionsId}>\n {messages.kanbanInstructions}\n </VisuallyHidden>\n <VisuallyHidden aria-live=\"assertive\">{sortable.announcement}</VisuallyHidden>\n </div>\n );\n };\n },\n);\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type IconButtonProps } from '../base/IconButton';
2
+ import { type MenuProps } from '../overlay/Menu';
3
+ import { type LayoutPreference } from '../config/layout/preference';
4
+ /** 未声明的属性落到菜单(role="menu" 的 div)上 */
5
+ export interface LayoutSwitcherProps extends Omit<MenuProps, 'trigger' | 'children'> {
6
+ /** createLayoutPreference 的结果 */
7
+ layout: LayoutPreference;
8
+ /** 触发按钮的部件属性 */
9
+ propsTrigger?: Partial<IconButtonProps>;
10
+ }
11
+ /** 布局切换(图标按钮 + 两组单选菜单) */
12
+ export declare const LayoutSwitcher: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<LayoutSwitcherProps>;
@@ -0,0 +1,79 @@
1
+ import { mergePartProps } from "../utils/props.js";
2
+ import { IconButton } from "../base/IconButton.js";
3
+ import { Menu as Menu$1, MenuGroup, MenuRadioGroup, MenuRadioItem } from "../overlay/Menu.js";
4
+ import { LAYOUT_MODES, LAYOUT_SIDES } from "../config/layout/preference.js";
5
+ import { useMessages } from "../config/lang/messages.js";
6
+ import { patternsMessages } from "./messages.js";
7
+ import { createVNode, isVNode } from "vue";
8
+ import { _component, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
9
+ import { Columns2, PanelBottom, PanelLeft, PanelRight, PanelTop } from "@lucide/icons";
10
+ //#region src/patterns/LayoutSwitcher.tsx
11
+ /**
12
+ * 布局切换:图标按钮弹出菜单,两组单选——侧栏的形式(固定 / 抽屉)和抽屉从哪一边滑出(左 / 右 / 上 / 下),
13
+ * 配合 createLayoutPreference 与 AppShell 的 layout 使用,和 ThemeSwitcher 并排放在顶栏里。
14
+ *
15
+ * const layout = createLayoutPreference({ defaults: { mode: 'overlay' } });
16
+ * <AppShell layout={layout} header={<><LayoutSwitcher layout={layout} /><ThemeSwitcher preference={theme} /></>} …>
17
+ *
18
+ * 选了“固定”时抽屉位置不可选(固定侧栏总是在起始边)。触发按钮的图标表示当前的位置。
19
+ */
20
+ /** 未声明的属性落到菜单(role="menu" 的 div)上 */
21
+ function _isSlot(s) {
22
+ return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !isVNode(s);
23
+ }
24
+ var SIDE_ICONS = {
25
+ start: PanelLeft,
26
+ end: PanelRight,
27
+ top: PanelTop,
28
+ bottom: PanelBottom
29
+ };
30
+ /** 布局切换(图标按钮 + 两组单选菜单) */
31
+ var LayoutSwitcher = /*#__PURE__*/ _component((_props, _ctx) => {
32
+ const _cache = [];
33
+ const layout = readonlyProp(_props, "layout");
34
+ const propsTrigger = readonlyProp(_props, "propsTrigger");
35
+ const rest = restProps(_props, _ctx, true);
36
+ const messages = useMessages("patterns", patternsMessages);
37
+ const modeLabels = {
38
+ docked: messages.layoutModeDocked,
39
+ overlay: messages.layoutModeOverlay
40
+ };
41
+ const sideLabels = {
42
+ start: messages.layoutSideStart,
43
+ end: messages.layoutSideEnd,
44
+ top: messages.layoutSideTop,
45
+ bottom: messages.layoutSideBottom
46
+ };
47
+ const triggerIcon = () => layout.value.mode === "overlay" ? SIDE_ICONS[layout.value.side] : Columns2;
48
+ return () => {
49
+ let _slot, _slot2;
50
+ return createVNode(Menu$1, mergeJsxProps(rest, { "trigger": _cache[0] || (_cache[0] = (props) => createVNode(IconButton, mergePartProps({
51
+ icon: triggerIcon(),
52
+ "aria-label": messages.layoutLabel
53
+ }, propsTrigger.value, props), null)) }), { default: () => [createVNode(MenuGroup, { "label": messages.layoutMode }, { default: () => [createVNode(MenuRadioGroup, {
54
+ "_value": layout.value.mode,
55
+ "onUpdate:_value": _cache[1] || (_cache[1] = (value) => layout.value.mode = value),
56
+ "aria-label": messages.layoutMode
57
+ }, _isSlot(_slot = LAYOUT_MODES.map((mode) => createVNode(MenuRadioItem, {
58
+ "key": mode,
59
+ "value": mode,
60
+ "icon": mode === "overlay" ? PanelLeft : Columns2
61
+ }, { default: () => [modeLabels[mode]] }))) ? _slot : { default: () => [_slot] })] }), createVNode(MenuGroup, { "label": messages.layoutSide }, { default: () => [createVNode(MenuRadioGroup, {
62
+ "_value": layout.value.side,
63
+ "onUpdate:_value": _cache[2] || (_cache[2] = (value) => layout.value.side = value),
64
+ "aria-label": messages.layoutSide
65
+ }, _isSlot(_slot2 = LAYOUT_SIDES.map((side) => createVNode(MenuRadioItem, {
66
+ "key": side,
67
+ "value": side,
68
+ "icon": SIDE_ICONS[side],
69
+ "disabled": layout.value.mode !== "overlay"
70
+ }, { default: () => [sideLabels[side]] }))) ? _slot2 : { default: () => [_slot2] })] })] });
71
+ };
72
+ }, {
73
+ name: "LayoutSwitcher",
74
+ props: ["layout", "propsTrigger"]
75
+ });
76
+ //#endregion
77
+ export { LayoutSwitcher };
78
+
79
+ //# sourceMappingURL=LayoutSwitcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LayoutSwitcher.js","names":["_isVNode","_vtp_readonlyProp","_vtp_restProps","_createVNode","Menu","_mergeProps"],"sources":["../../src/patterns/LayoutSwitcher.tsx"],"sourcesContent":["/**\n * 布局切换:图标按钮弹出菜单,两组单选——侧栏的形式(固定 / 抽屉)和抽屉从哪一边滑出(左 / 右 / 上 / 下),\n * 配合 createLayoutPreference 与 AppShell 的 layout 使用,和 ThemeSwitcher 并排放在顶栏里。\n *\n * const layout = createLayoutPreference({ defaults: { mode: 'overlay' } });\n * <AppShell layout={layout} header={<><LayoutSwitcher layout={layout} /><ThemeSwitcher preference={theme} /></>} …>\n *\n * 选了“固定”时抽屉位置不可选(固定侧栏总是在起始边)。触发按钮的图标表示当前的位置。\n */\nimport { Columns2, PanelBottom, PanelLeft, PanelRight, PanelTop } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { IconButton, type IconButtonProps } from '../base/IconButton';\nimport { useMessages } from '../config/lang/messages';\nimport { patternsMessages } from './messages';\nimport type { IconData } from '../base/Icon';\nimport { Menu, MenuGroup, MenuRadioGroup, MenuRadioItem, type MenuProps } from '../overlay/Menu';\nimport {\n LAYOUT_MODES,\n LAYOUT_SIDES,\n type LayoutMode,\n type LayoutPreference,\n type LayoutSide,\n} from '../config/layout/preference';\nimport { mergePartProps } from '../utils/props';\n\n/** 未声明的属性落到菜单(role=\"menu\" 的 div)上 */\nexport interface LayoutSwitcherProps extends Omit<MenuProps, 'trigger' | 'children'> {\n /** createLayoutPreference 的结果 */\n layout: LayoutPreference;\n /** 触发按钮的部件属性 */\n propsTrigger?: Partial<IconButtonProps>;\n}\n\nconst SIDE_ICONS: Record<LayoutSide, IconData> = {\n start: PanelLeft,\n end: PanelRight,\n top: PanelTop,\n bottom: PanelBottom,\n};\n\n/** 布局切换(图标按钮 + 两组单选菜单) */\nexport const LayoutSwitcher = _component(\n ({ layout, propsTrigger, ...rest }: LayoutSwitcherProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const modeLabels: Record<LayoutMode, string> = {\n docked: messages.layoutModeDocked,\n overlay: messages.layoutModeOverlay,\n };\n const sideLabels: Record<LayoutSide, string> = {\n start: messages.layoutSideStart,\n end: messages.layoutSideEnd,\n top: messages.layoutSideTop,\n bottom: messages.layoutSideBottom,\n };\n // 触发按钮:抽屉时显示它的方向,固定时显示“两栏”\n const triggerIcon = () => (layout.mode === 'overlay' ? SIDE_ICONS[layout.side] : Columns2);\n\n return () => (\n <Menu\n {...rest}\n trigger={(props) => (\n <IconButton\n {...mergePartProps<IconButtonProps>(\n { icon: triggerIcon(), 'aria-label': messages.layoutLabel },\n propsTrigger,\n props,\n )}\n />\n )}\n >\n <MenuGroup label={messages.layoutMode}>\n <MenuRadioGroup\n _value={layout.mode}\n onUpdate:_value={(value: string) => (layout.mode = value as LayoutMode)}\n aria-label={messages.layoutMode}\n >\n {LAYOUT_MODES.map((mode) => (\n <MenuRadioItem\n key={mode}\n value={mode}\n icon={mode === 'overlay' ? PanelLeft : Columns2}\n >\n {modeLabels[mode]}\n </MenuRadioItem>\n ))}\n </MenuRadioGroup>\n </MenuGroup>\n <MenuGroup label={messages.layoutSide}>\n <MenuRadioGroup\n _value={layout.side}\n onUpdate:_value={(value: string) => (layout.side = value as LayoutSide)}\n aria-label={messages.layoutSide}\n >\n {LAYOUT_SIDES.map((side) => (\n <MenuRadioItem\n key={side}\n value={side}\n icon={SIDE_ICONS[side]}\n disabled={layout.mode !== 'overlay'}\n >\n {sideLabels[side]}\n </MenuRadioItem>\n ))}\n </MenuRadioGroup>\n </MenuGroup>\n </Menu>\n );\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAyBA,SAAA,QAAA,GAAA;CAAA,OAAA,OAAA,MAAA,cAAA,OAAA,UAAA,SAAA,KAAA,CAAA,MAAA,qBAAA,CAAAA,QAAA,CAAA;AAAA;AAQA,IAAM,aAA2C;CAC/C,OAAO;CACP,KAAK;CACL,KAAK;CACL,QAAQ;AACV;;AAGA,IAAa,iBAAiB,0BAC5B,QAAA,SAA4D;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,SAAAC,aAAA,QAAA,QAAA;CAAA,MAAA,eAAAA,aAAA,QAAA,cAAA;CAAA,MAAhC,OAAIC,UAAA,QAAA,MAAA,IAAA;CAC9B,MAAM,WAAW,YAAY,YAAY,gBAAgB;CACzD,MAAM,aAAyC;EAC7C,QAAQ,SAAS;EACjB,SAAS,SAAS;CACpB;CACA,MAAM,aAAyC;EAC7C,OAAO,SAAS;EAChB,KAAK,SAAS;EACd,KAAK,SAAS;EACd,QAAQ,SAAS;CACnB;CAEA,MAAM,oBAAqB,OAAA,MAAO,SAAS,YAAY,WAAW,OAAA,MAAO,QAAQ;CAEjF,aAAO;EAAA,IAAA,OAAA;EAAA,OAAAC,YAAAC,QAAAC,cAEC,MAAI,EAAA,WAAA,OAAA,OAAA,OAAA,MACE,UAAKF,YAAA,YAEP,eACF;GAAE,MAAM,YAAY;GAAG,cAAc,SAAS;EAAY,GAAC,aAAA,OAE3D,KACF,GAAC,IAAA,GAEJ,CAAA,GAAA,EAAA,eAAA,CAAAA,YAAA,WAAA,EAAA,SAEiB,SAAS,WAAU,GAAA,EAAA,eAAA,CAAAA,YAAA,gBAAA;GAAA,UAEzB,OAAA,MAAO;GAAI,mBAAA,OAAA,OAAA,OAAA,MACD,UAAmB,OAAA,MAAO,OAAO;GAAoB,cAC3D,SAAS;EAAU,GAAA,QAAA,QAE9B,aAAa,KAAK,SAAIA,YAAA,eAAA;GAAA,OAEd;GAAI,SACF;GAAI,QACL,SAAS,YAAY,YAAY;EAAQ,GAAA,EAAA,eAAA,CAE9C,WAAW,KAAK,EAAA,CAAA,CAEpB,CAAC,IAAA,QAAA,EAAA,eAAA,CAAA,KAAA,EAAA,CAAA,CAAA,EAAA,CAAA,GAAAA,YAAA,WAAA,EAAA,SAGY,SAAS,WAAU,GAAA,EAAA,eAAA,CAAAA,YAAA,gBAAA;GAAA,UAEzB,OAAA,MAAO;GAAI,mBAAA,OAAA,OAAA,OAAA,MACD,UAAmB,OAAA,MAAO,OAAO;GAAoB,cAC3D,SAAS;EAAU,GAAA,QAAA,SAE9B,aAAa,KAAK,SAAIA,YAAA,eAAA;GAAA,OAEd;GAAI,SACF;GAAI,QACL,WAAW;GAAK,YACZ,OAAA,MAAO,SAAS;EAAS,GAAA,EAAA,eAAA,CAElC,WAAW,KAAK,EAAA,CAAA,CAEpB,CAAC,IAAA,SAAA,EAAA,eAAA,CAAA,MAAA,EAAA,CAAA,CAAA,EAAA,CAAA,CAAA,EAAA,CAAA;CAAA;AAKZ,GAAC;CAAA,MAAA;CAAA,OAAA,CAAA,UAAA,cAAA;AAAA,CACH"}
@@ -13,8 +13,8 @@ import { ToggleGroup, ToggleItem } from "../input/ToggleGroup.js";
13
13
  import { Switch } from "../input/Switch.js";
14
14
  import { useMessages } from "../config/lang/messages.js";
15
15
  import { patternsMessages } from "./messages.js";
16
- import { computed, createVNode, mergeProps, ref } from "vue";
17
- import { _component, bindable, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
16
+ import { computed, createVNode, ref } from "vue";
17
+ import { _component, bindable, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
18
18
  import * as css from "@zerodep-dev/vue-tsx-pro-tsx/styles";
19
19
  import { Bell } from "@lucide/icons";
20
20
  //#region src/patterns/NotificationCenter.tsx
@@ -162,7 +162,7 @@ var NotificationList = /*#__PURE__*/ _component((_props, _ctx) => {
162
162
  if (item.href) router.onClick(event, item.href);
163
163
  }
164
164
  return () => {
165
- const markAll = createVNode(Button, mergeProps(mergePartProps({
165
+ const markAll = createVNode(Button, mergeJsxProps(mergePartProps({
166
166
  size: "xs",
167
167
  variant: "ghost",
168
168
  sx: title.value === false && notificationListStyles.end
@@ -176,7 +176,7 @@ var NotificationList = /*#__PURE__*/ _component((_props, _ctx) => {
176
176
  return createVNode("div", mergeAttrs({ sx: notificationListStyles.root }, rest), [
177
177
  title.value !== false && createVNode("div", mergeAttrs({ sx: notificationListStyles.head }, propsHead.value), [createVNode("span", css.attrs(notificationListStyles.title), [title.value ?? messages.notifications]), markAll]),
178
178
  createVNode("div", mergeAttrs({ sx: notificationListStyles.filters }, propsFilters.value), [
179
- types.value?.length ? createVNode(ToggleGroup, mergeProps(mergePartProps({
179
+ types.value?.length ? createVNode(ToggleGroup, mergeJsxProps(mergePartProps({
180
180
  size: "xs",
181
181
  sx: notificationListStyles.types,
182
182
  "aria-label": messages.notifications
@@ -197,7 +197,7 @@ var NotificationList = /*#__PURE__*/ _component((_props, _ctx) => {
197
197
  }, { default: () => [item.label] }))],
198
198
  $stable: true
199
199
  }) : null,
200
- createVNode(Switch, mergeProps(mergePartProps({
200
+ createVNode(Switch, mergeJsxProps(mergePartProps({
201
201
  size: "sm",
202
202
  sx: notificationListStyles.end
203
203
  }, propsUnreadOnly.value), {
@@ -213,7 +213,7 @@ var NotificationList = /*#__PURE__*/ _component((_props, _ctx) => {
213
213
  shown.value.length === 0 ? createVNode("div", mergeAttrs({ sx: notificationListStyles.empty }, propsEmpty.value), [empty.value ?? createVNode(Empty, {
214
214
  "size": "sm",
215
215
  "title": messages.notificationsEmpty
216
- }, null)]) : createVNode("ul", mergeAttrs({ sx: notificationListStyles.list }, propsList.value), [shown.value.map((item) => createVNode("li", { "key": item.id }, [createVNode("a", mergeProps(mergeAttrs({ sx: [notificationListStyles.item, focusRingStyles.visible] }, typeof propsItem.value === "function" ? propsItem.value(item) : propsItem.value, {
216
+ }, null)]) : createVNode("ul", mergeAttrs({ sx: notificationListStyles.list }, propsList.value), [shown.value.map((item) => createVNode("li", { "key": item.id }, [createVNode("a", mergeJsxProps(mergeAttrs({ sx: [notificationListStyles.item, focusRingStyles.visible] }, typeof propsItem.value === "function" ? propsItem.value(item) : propsItem.value, {
217
217
  href: item.href ? router.href(item.href) : void 0,
218
218
  role: item.href ? void 0 : "button",
219
219
  tabindex: 0,
@@ -230,7 +230,7 @@ var NotificationList = /*#__PURE__*/ _component((_props, _ctx) => {
230
230
  default: () => [messages.notificationUnreadMark, " "],
231
231
  $stable: true
232
232
  }), item.title]), item.description && createVNode("span", css.attrs(notificationListStyles.muted), [item.description])]),
233
- createVNode("time", mergeProps({
233
+ createVNode("time", mergeJsxProps({
234
234
  "datetime": item.time,
235
235
  "title": format.date(item.time, {
236
236
  dateStyle: "medium",
@@ -289,7 +289,7 @@ var NotificationCenter = /*#__PURE__*/ _component((_props2, _ctx2) => {
289
289
  const rest = restProps(_props2, _ctx2, true);
290
290
  const messages = useMessages("patterns", patternsMessages);
291
291
  const unread = computed(() => items.value.filter((item) => !item.read).length);
292
- return () => createVNode(Popover, mergeProps(rest, {
292
+ return () => createVNode(Popover, mergeJsxProps(rest, {
293
293
  "placement": rest.placement ?? "bottom-end",
294
294
  "sx": [notificationCenterStyles.panel, sx.value],
295
295
  "_open": open.value,
@@ -1 +1 @@
1
- {"version":3,"file":"NotificationCenter.js","names":["_vtp_readonlyProp","_vtp_restProps","_vue_ref","_vue_computed","_createVNode","_mergeProps","_vtp_bindable"],"sources":["../../src/patterns/NotificationCenter.tsx"],"sourcesContent":["/**\n * 通知:顶栏的铃铛 NotificationCenter(右上角是未读数,点开是通知列表),与列表本身 NotificationList\n * (通知页面、侧边抽屉里直接放)。列表新的在前:按类型筛选、只看未读、全部标为已读;点一条标为已读并去它的地址(走路由适配)。\n * 通知存在哪、何时算已读由应用决定:组件只报告 onRead。\n *\n * <NotificationCenter\n * items={notices} // { id, title, description?, time, type?, read, href?, tone? }\n * types={[{ key: 'todo', label: '待办' }, { key: 'remind', label: '催办' }]}\n * onRead={(ids) => markRead(ids)}\n * footer={<Link href=\"/inbox\">打开收件箱</Link>}\n * />\n * <NotificationList items={notices} types={types} onRead={markRead} /> 通知页面:同一份列表,不放进弹出层\n *\n * 时间写成相对现在(3 分钟前、昨天),悬停看完整时间。未读的前面一个色点(tone 决定颜色,默认 accent),\n * 读屏念“未读”。列表为空时显示 empty(默认“没有通知”)。和 Toast 的分工:Toast 是此刻发生的事、几秒后消失;\n * 通知是留着、可以回头看的。\n *\n * NotificationList 的部件:Root(div)、Head(div)、Title(span)、MarkAll(Button)、Filters(div)、Types(ToggleGroup)、\n * UnreadOnly(Switch)、List(ul)、Item(a,可以按条计算)、Empty(div)、Footer(div)。\n */\nimport { Bell } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { Button, type ButtonProps } from '../base/Button';\nimport { IconButton, type IconButtonProps } from '../base/IconButton';\nimport { Indicator } from '../base/Indicator';\nimport { StatusDot } from '../base/StatusDot';\nimport { VisuallyHidden } from '../base/VisuallyHidden';\nimport { useFormat } from '../config/lang/format';\nimport { useMessages } from '../config/lang/messages';\nimport { Empty } from '../feedback/Empty';\nimport { Switch, type SwitchProps } from '../input/Switch';\nimport { ToggleGroup, ToggleItem, type ToggleGroupBaseProps } from '../input/ToggleGroup';\nimport { Popover, type PopoverProps } from '../overlay/Popover';\nimport { useRouterLink } from '../utils/dom/link';\nimport { hasContent, mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { focusRingStyles } from '../utils/style/styles';\nimport type { SingleSelectionProps } from '../utils/data/selection';\nimport type { StatusTone } from '../utils/style/tone-styles';\nimport { patternsMessages } from './messages';\n\n/** 一条通知 */\nexport interface NotificationItem {\n /** 标识 */\n id: string;\n /** 标题:一句话说清发生了什么 */\n title: string;\n /** 补充说明(谁、哪张单) */\n description?: string;\n /** 发生的时间(ISO) */\n time: string;\n /** 类型:按 types 筛选 */\n type?: string;\n /** 读过了 */\n read: boolean;\n /** 点了去哪儿 */\n href?: string;\n /** 未读色点的色调,默认 accent;超时、被退回用 danger、warning */\n tone?: StatusTone;\n}\n\n/** 通知的一种类型(筛选按钮) */\nexport interface NotificationType {\n /** 类型:对应 NotificationItem.type */\n key: string;\n /** 名称 */\n label: string;\n}\n\n/** 通知列表的内容与回调:NotificationList 与 NotificationCenter 共用 */\nexport interface NotificationListOptions {\n /** 通知:新的在前(组件不排序) */\n items: readonly NotificationItem[];\n /** 类型:写了时列表上方有按类型筛选的按钮 */\n types?: readonly NotificationType[];\n /** 这些通知读过了:点了一条、全部标为已读 */\n onRead?: (ids: string[]) => void;\n /** 点了一条(标为已读之后);有 href 时还会去那里 */\n onItemClick?: (item: NotificationItem) => void;\n /** 列表下面的内容(“查看全部”) */\n footer?: VNodeChild;\n /** 没有通知时显示的内容,默认是“没有通知” */\n empty?: VNodeChild;\n /** 相对时间以哪个时刻为现在(毫秒时间戳),默认此刻;演示数据固定日期时传 */\n now?: number;\n}\n\n/** 未声明的属性落到外层 div 上;原生的 title 属性(悬停提示)让给标题 */\nexport interface NotificationListProps extends PartProps<'div', 'title'>, NotificationListOptions {\n /** 标题,默认“通知”;false 不要标题行(“全部标为已读”挪到筛选那一行) */\n title?: VNodeChild | false;\n /** 标题行的部件属性 */\n propsHead?: PartProps<'div'>;\n /** “全部标为已读”按钮的部件属性 */\n propsMarkAll?: Partial<ButtonProps>;\n /** 筛选行的部件属性 */\n propsFilters?: PartProps<'div'>;\n /** 类型筛选按钮组的部件属性 */\n propsTypes?: Partial<ToggleGroupBaseProps>;\n /** “只看未读”开关的部件属性 */\n propsUnreadOnly?: Partial<SwitchProps>;\n /** 列表(ul)的部件属性 */\n propsList?: PartProps<'ul'>;\n /** 每一条(a)的部件属性,可以按条计算 */\n propsItem?: PartProps<'a'> | ((item: NotificationItem) => PartProps<'a'>);\n /** 列表为空时那一块的部件属性 */\n propsEmpty?: PartProps<'div'>;\n /** 列表下面那一行的部件属性 */\n propsFooter?: PartProps<'div'>;\n}\n\n/** NotificationList 的样式:root、head、title、filters、list、item、dot… */\nexport const notificationListStyles = css.create({\n root: { display: 'flex', flexDirection: 'column', minWidth: 0 },\n head: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '_sm',\n paddingBlock: '_sm',\n paddingInline: '_md',\n borderBlockEndWidth: '_xs',\n borderBlockEndStyle: 'solid',\n borderBlockEndColor: '_border',\n },\n title: { fontWeight: '_semibold' },\n // 类型多时筛选按钮换行;“只看未读”总在最后、靠结束边\n filters: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '_sm',\n paddingBlock: '_sm',\n paddingInline: '_md',\n },\n types: { flexWrap: 'wrap' },\n end: { marginInlineStart: 'auto' },\n list: { listStyle: 'none', margin: 0, padding: 0, overflowY: 'auto' },\n item: {\n display: 'grid',\n gridTemplateColumns: '0.5rem minmax(0, 1fr) auto',\n columnGap: '_sm',\n alignItems: 'baseline',\n paddingBlock: '_sm',\n paddingInline: '_md',\n color: 'inherit',\n textDecoration: 'none',\n borderBlockStartWidth: '_xs',\n borderBlockStartStyle: 'solid',\n borderBlockStartColor: '_borderMuted',\n backgroundColor: { default: 'transparent', ':hover': '_fillHover' },\n cursor: 'pointer',\n },\n // 未读的色点贴着标题的第一行\n dot: { display: 'flex', alignItems: 'center', height: '1lh', alignSelf: 'start' },\n body: { display: 'flex', flexDirection: 'column', gap: '2px', minWidth: 0 },\n itemTitle: { fontSize: '_sm' },\n itemTitleUnread: { fontWeight: '_semibold' },\n muted: { color: '_textMuted', fontSize: '_xs' },\n time: { color: '_textMuted', fontSize: '_xs', whiteSpace: 'nowrap' },\n foot: {\n display: 'flex',\n justifyContent: 'center',\n paddingBlock: '_sm',\n borderBlockStartWidth: '_xs',\n borderBlockStartStyle: 'solid',\n borderBlockStartColor: '_border',\n },\n empty: { paddingBlock: '_lg' },\n});\n\n/** 通知列表:标题行、筛选、通知(新的在前)、底部;直接放在页面、抽屉里,或由 NotificationCenter 放进弹出层 */\nexport const NotificationList = _component(\n ({\n items,\n types,\n onRead,\n onItemClick,\n footer,\n empty,\n now,\n title,\n propsHead,\n propsMarkAll,\n propsFilters,\n propsTypes,\n propsUnreadOnly,\n propsList,\n propsItem,\n propsEmpty,\n propsFooter,\n ...rest\n }: NotificationListProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const format = useFormat();\n const router = useRouterLink();\n let type = _state('');\n let unreadOnly = _state(false);\n const unread = _derived(items.filter((item) => !item.read).length);\n const shown = _derived(\n items.filter((item) => (!type || item.type === type) && (!unreadOnly || !item.read)),\n );\n\n function select(item: NotificationItem, event: MouseEvent) {\n if (!item.read) onRead?.([item.id]);\n onItemClick?.(item);\n if (item.href) router.onClick(event, item.href);\n }\n\n return () => {\n const markAll = (\n <Button\n {...mergePartProps<ButtonProps>(\n { size: 'xs', variant: 'ghost', sx: title === false && notificationListStyles.end },\n propsMarkAll,\n {\n disabled: unread === 0,\n onClick: () => onRead?.(items.filter((item) => !item.read).map((item) => item.id)),\n },\n )}\n >\n {messages.notificationsMarkAllRead}\n </Button>\n );\n return (\n <div {...mergeAttrs<'div'>({ sx: notificationListStyles.root }, rest)}>\n {title !== false && (\n <div {...mergeAttrs<'div'>({ sx: notificationListStyles.head }, propsHead)}>\n <span {...css.attrs(notificationListStyles.title)}>\n {title ?? messages.notifications}\n </span>\n {markAll}\n </div>\n )}\n <div {...mergeAttrs<'div'>({ sx: notificationListStyles.filters }, propsFilters)}>\n {types?.length ? (\n <ToggleGroup\n {...mergePartProps<ToggleGroupBaseProps & SingleSelectionProps>(\n {\n size: 'xs',\n sx: notificationListStyles.types,\n 'aria-label': messages.notifications,\n },\n propsTypes,\n )}\n _value={type || 'all'}\n onUpdate:_value={(next: string | null) =>\n (type = !next || next === 'all' ? '' : next)\n }\n >\n <ToggleItem value=\"all\">{messages.notificationsAll}</ToggleItem>\n {types.map((item) => (\n <ToggleItem key={item.key} value={item.key}>\n {item.label}\n </ToggleItem>\n ))}\n </ToggleGroup>\n ) : null}\n <Switch\n {...mergePartProps<SwitchProps>(\n { size: 'sm', sx: notificationListStyles.end },\n propsUnreadOnly,\n )}\n _checked={unreadOnly}\n >\n {messages.notificationsUnreadOnly}\n </Switch>\n {title === false && markAll}\n </div>\n {shown.length === 0 ? (\n <div {...mergeAttrs<'div'>({ sx: notificationListStyles.empty }, propsEmpty)}>\n {empty ?? <Empty size=\"sm\" title={messages.notificationsEmpty} />}\n </div>\n ) : (\n <ul {...mergeAttrs<'ul'>({ sx: notificationListStyles.list }, propsList)}>\n {shown.map((item) => (\n <li key={item.id}>\n <a\n {...mergeAttrs<'a'>(\n { sx: [notificationListStyles.item, focusRingStyles.visible] },\n typeof propsItem === 'function' ? propsItem(item) : propsItem,\n {\n href: item.href ? router.href(item.href) : undefined,\n role: item.href ? undefined : 'button',\n tabindex: 0,\n onClick: (event: MouseEvent) => select(item, event),\n onKeydown: (event: KeyboardEvent) => {\n if (!item.href && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n select(item, event as unknown as MouseEvent);\n }\n },\n },\n )}\n data-state={item.read ? 'read' : 'unread'}\n >\n <span {...css.attrs(notificationListStyles.dot)}>\n {!item.read && <StatusDot tone={item.tone ?? 'accent'} />}\n </span>\n <span {...css.attrs(notificationListStyles.body)}>\n <span\n {...css.attrs(\n notificationListStyles.itemTitle,\n !item.read && notificationListStyles.itemTitleUnread,\n )}\n >\n {!item.read && (\n <VisuallyHidden>{messages.notificationUnreadMark} </VisuallyHidden>\n )}\n {item.title}\n </span>\n {item.description && (\n <span {...css.attrs(notificationListStyles.muted)}>{item.description}</span>\n )}\n </span>\n <time\n datetime={item.time}\n title={format.date(item.time, { dateStyle: 'medium', timeStyle: 'short' })}\n {...css.attrs(notificationListStyles.time)}\n >\n {format.relative(item.time, { now })}\n </time>\n </a>\n </li>\n ))}\n </ul>\n )}\n {hasContent(footer) && (\n <div {...mergeAttrs<'div'>({ sx: notificationListStyles.foot }, propsFooter)}>\n {footer}\n </div>\n )}\n </div>\n );\n };\n },\n);\n\n/** 未声明的属性落到弹出层的面板上 */\nexport interface NotificationCenterProps\n extends Omit<PopoverProps, 'trigger' | 'children'>, NotificationListOptions {\n /** 铃铛按钮的部件属性 */\n propsTrigger?: Partial<IconButtonProps>;\n /** 面板里的通知列表的部件属性 */\n propsNotificationList?: Partial<NotificationListProps>;\n}\n\n/** NotificationCenter 的样式:panel(弹出层的宽度)、list(列表的最大高度) */\nexport const notificationCenterStyles = css.create({\n panel: { width: 'min(26rem, calc(100vw - 2rem))', padding: 0 },\n list: { maxHeight: '24rem' },\n});\n\n/** 通知中心:顶栏的铃铛(带未读数),点开是 NotificationList;点了一条就收起 */\nexport const NotificationCenter = _component(\n ({\n items,\n types,\n onRead,\n onItemClick,\n footer,\n empty,\n now,\n _open: open = _bindable(false),\n propsTrigger,\n propsNotificationList,\n sx,\n ...rest\n }: NotificationCenterProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const unread = _derived(items.filter((item) => !item.read).length);\n return () => (\n <Popover\n {...rest}\n placement={rest.placement ?? 'bottom-end'}\n sx={[notificationCenterStyles.panel, sx]}\n _open={open}\n trigger={(trigger) => (\n <Indicator count={unread} label={messages.notificationsUnread(unread)}>\n <IconButton\n {...mergePartProps<IconButtonProps>(\n { icon: Bell, 'aria-label': messages.notificationsUnread(unread) },\n propsTrigger,\n trigger,\n )}\n />\n </Indicator>\n )}\n >\n <NotificationList\n {...mergePartProps<NotificationListProps>(\n { items, propsList: { sx: notificationCenterStyles.list } },\n propsNotificationList,\n {\n types,\n onRead,\n footer,\n empty,\n now,\n onItemClick: (item: NotificationItem) => {\n onItemClick?.(item);\n open = false;\n },\n },\n )}\n />\n </Popover>\n );\n },\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"NotificationCenter.js","names":["_vtp_readonlyProp","_vtp_restProps","_vue_ref","_vue_computed","_createVNode","_mergeProps","_vtp_bindable"],"sources":["../../src/patterns/NotificationCenter.tsx"],"sourcesContent":["/**\n * 通知:顶栏的铃铛 NotificationCenter(右上角是未读数,点开是通知列表),与列表本身 NotificationList\n * (通知页面、侧边抽屉里直接放)。列表新的在前:按类型筛选、只看未读、全部标为已读;点一条标为已读并去它的地址(走路由适配)。\n * 通知存在哪、何时算已读由应用决定:组件只报告 onRead。\n *\n * <NotificationCenter\n * items={notices} // { id, title, description?, time, type?, read, href?, tone? }\n * types={[{ key: 'todo', label: '待办' }, { key: 'remind', label: '催办' }]}\n * onRead={(ids) => markRead(ids)}\n * footer={<Link href=\"/inbox\">打开收件箱</Link>}\n * />\n * <NotificationList items={notices} types={types} onRead={markRead} /> 通知页面:同一份列表,不放进弹出层\n *\n * 时间写成相对现在(3 分钟前、昨天),悬停看完整时间。未读的前面一个色点(tone 决定颜色,默认 accent),\n * 读屏念“未读”。列表为空时显示 empty(默认“没有通知”)。和 Toast 的分工:Toast 是此刻发生的事、几秒后消失;\n * 通知是留着、可以回头看的。\n *\n * NotificationList 的部件:Root(div)、Head(div)、Title(span)、MarkAll(Button)、Filters(div)、Types(ToggleGroup)、\n * UnreadOnly(Switch)、List(ul)、Item(a,可以按条计算)、Empty(div)、Footer(div)。\n */\nimport { Bell } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { Button, type ButtonProps } from '../base/Button';\nimport { IconButton, type IconButtonProps } from '../base/IconButton';\nimport { Indicator } from '../base/Indicator';\nimport { StatusDot } from '../base/StatusDot';\nimport { VisuallyHidden } from '../base/VisuallyHidden';\nimport { useFormat } from '../config/lang/format';\nimport { useMessages } from '../config/lang/messages';\nimport { Empty } from '../feedback/Empty';\nimport { Switch, type SwitchProps } from '../input/Switch';\nimport { ToggleGroup, ToggleItem, type ToggleGroupBaseProps } from '../input/ToggleGroup';\nimport { Popover, type PopoverProps } from '../overlay/Popover';\nimport { useRouterLink } from '../utils/dom/link';\nimport { hasContent, mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { focusRingStyles } from '../utils/style/styles';\nimport type { SingleSelectionProps } from '../utils/data/selection';\nimport type { StatusTone } from '../utils/style/tone-styles';\nimport { patternsMessages } from './messages';\n\n/** 一条通知 */\nexport interface NotificationItem {\n /** 标识 */\n id: string;\n /** 标题:一句话说清发生了什么 */\n title: string;\n /** 补充说明(谁、哪张单) */\n description?: string;\n /** 发生的时间(ISO) */\n time: string;\n /** 类型:按 types 筛选 */\n type?: string;\n /** 读过了 */\n read: boolean;\n /** 点了去哪儿 */\n href?: string;\n /** 未读色点的色调,默认 accent;超时、被退回用 danger、warning */\n tone?: StatusTone;\n}\n\n/** 通知的一种类型(筛选按钮) */\nexport interface NotificationType {\n /** 类型:对应 NotificationItem.type */\n key: string;\n /** 名称 */\n label: string;\n}\n\n/** 通知列表的内容与回调:NotificationList 与 NotificationCenter 共用 */\nexport interface NotificationListOptions {\n /** 通知:新的在前(组件不排序) */\n items: readonly NotificationItem[];\n /** 类型:写了时列表上方有按类型筛选的按钮 */\n types?: readonly NotificationType[];\n /** 这些通知读过了:点了一条、全部标为已读 */\n onRead?: (ids: string[]) => void;\n /** 点了一条(标为已读之后);有 href 时还会去那里 */\n onItemClick?: (item: NotificationItem) => void;\n /** 列表下面的内容(“查看全部”) */\n footer?: VNodeChild;\n /** 没有通知时显示的内容,默认是“没有通知” */\n empty?: VNodeChild;\n /** 相对时间以哪个时刻为现在(毫秒时间戳),默认此刻;演示数据固定日期时传 */\n now?: number;\n}\n\n/** 未声明的属性落到外层 div 上;原生的 title 属性(悬停提示)让给标题 */\nexport interface NotificationListProps extends PartProps<'div', 'title'>, NotificationListOptions {\n /** 标题,默认“通知”;false 不要标题行(“全部标为已读”挪到筛选那一行) */\n title?: VNodeChild | false;\n /** 标题行的部件属性 */\n propsHead?: PartProps<'div'>;\n /** “全部标为已读”按钮的部件属性 */\n propsMarkAll?: Partial<ButtonProps>;\n /** 筛选行的部件属性 */\n propsFilters?: PartProps<'div'>;\n /** 类型筛选按钮组的部件属性 */\n propsTypes?: Partial<ToggleGroupBaseProps>;\n /** “只看未读”开关的部件属性 */\n propsUnreadOnly?: Partial<SwitchProps>;\n /** 列表(ul)的部件属性 */\n propsList?: PartProps<'ul'>;\n /** 每一条(a)的部件属性,可以按条计算 */\n propsItem?: PartProps<'a'> | ((item: NotificationItem) => PartProps<'a'>);\n /** 列表为空时那一块的部件属性 */\n propsEmpty?: PartProps<'div'>;\n /** 列表下面那一行的部件属性 */\n propsFooter?: PartProps<'div'>;\n}\n\n/** NotificationList 的样式:root、head、title、filters、list、item、dot… */\nexport const notificationListStyles = css.create({\n root: { display: 'flex', flexDirection: 'column', minWidth: 0 },\n head: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '_sm',\n paddingBlock: '_sm',\n paddingInline: '_md',\n borderBlockEndWidth: '_xs',\n borderBlockEndStyle: 'solid',\n borderBlockEndColor: '_border',\n },\n title: { fontWeight: '_semibold' },\n // 类型多时筛选按钮换行;“只看未读”总在最后、靠结束边\n filters: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '_sm',\n paddingBlock: '_sm',\n paddingInline: '_md',\n },\n types: { flexWrap: 'wrap' },\n end: { marginInlineStart: 'auto' },\n list: { listStyle: 'none', margin: 0, padding: 0, overflowY: 'auto' },\n item: {\n display: 'grid',\n gridTemplateColumns: '0.5rem minmax(0, 1fr) auto',\n columnGap: '_sm',\n alignItems: 'baseline',\n paddingBlock: '_sm',\n paddingInline: '_md',\n color: 'inherit',\n textDecoration: 'none',\n borderBlockStartWidth: '_xs',\n borderBlockStartStyle: 'solid',\n borderBlockStartColor: '_borderMuted',\n backgroundColor: { default: 'transparent', ':hover': '_fillHover' },\n cursor: 'pointer',\n },\n // 未读的色点贴着标题的第一行\n dot: { display: 'flex', alignItems: 'center', height: '1lh', alignSelf: 'start' },\n body: { display: 'flex', flexDirection: 'column', gap: '2px', minWidth: 0 },\n itemTitle: { fontSize: '_sm' },\n itemTitleUnread: { fontWeight: '_semibold' },\n muted: { color: '_textMuted', fontSize: '_xs' },\n time: { color: '_textMuted', fontSize: '_xs', whiteSpace: 'nowrap' },\n foot: {\n display: 'flex',\n justifyContent: 'center',\n paddingBlock: '_sm',\n borderBlockStartWidth: '_xs',\n borderBlockStartStyle: 'solid',\n borderBlockStartColor: '_border',\n },\n empty: { paddingBlock: '_lg' },\n});\n\n/** 通知列表:标题行、筛选、通知(新的在前)、底部;直接放在页面、抽屉里,或由 NotificationCenter 放进弹出层 */\nexport const NotificationList = _component(\n ({\n items,\n types,\n onRead,\n onItemClick,\n footer,\n empty,\n now,\n title,\n propsHead,\n propsMarkAll,\n propsFilters,\n propsTypes,\n propsUnreadOnly,\n propsList,\n propsItem,\n propsEmpty,\n propsFooter,\n ...rest\n }: NotificationListProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const format = useFormat();\n const router = useRouterLink();\n let type = _state('');\n let unreadOnly = _state(false);\n const unread = _derived(items.filter((item) => !item.read).length);\n const shown = _derived(\n items.filter((item) => (!type || item.type === type) && (!unreadOnly || !item.read)),\n );\n\n function select(item: NotificationItem, event: MouseEvent) {\n if (!item.read) onRead?.([item.id]);\n onItemClick?.(item);\n if (item.href) router.onClick(event, item.href);\n }\n\n return () => {\n const markAll = (\n <Button\n {...mergePartProps<ButtonProps>(\n { size: 'xs', variant: 'ghost', sx: title === false && notificationListStyles.end },\n propsMarkAll,\n {\n disabled: unread === 0,\n onClick: () => onRead?.(items.filter((item) => !item.read).map((item) => item.id)),\n },\n )}\n >\n {messages.notificationsMarkAllRead}\n </Button>\n );\n return (\n <div {...mergeAttrs<'div'>({ sx: notificationListStyles.root }, rest)}>\n {title !== false && (\n <div {...mergeAttrs<'div'>({ sx: notificationListStyles.head }, propsHead)}>\n <span {...css.attrs(notificationListStyles.title)}>\n {title ?? messages.notifications}\n </span>\n {markAll}\n </div>\n )}\n <div {...mergeAttrs<'div'>({ sx: notificationListStyles.filters }, propsFilters)}>\n {types?.length ? (\n <ToggleGroup\n {...mergePartProps<ToggleGroupBaseProps & SingleSelectionProps>(\n {\n size: 'xs',\n sx: notificationListStyles.types,\n 'aria-label': messages.notifications,\n },\n propsTypes,\n )}\n _value={type || 'all'}\n onUpdate:_value={(next: string | null) =>\n (type = !next || next === 'all' ? '' : next)\n }\n >\n <ToggleItem value=\"all\">{messages.notificationsAll}</ToggleItem>\n {types.map((item) => (\n <ToggleItem key={item.key} value={item.key}>\n {item.label}\n </ToggleItem>\n ))}\n </ToggleGroup>\n ) : null}\n <Switch\n {...mergePartProps<SwitchProps>(\n { size: 'sm', sx: notificationListStyles.end },\n propsUnreadOnly,\n )}\n _checked={unreadOnly}\n >\n {messages.notificationsUnreadOnly}\n </Switch>\n {title === false && markAll}\n </div>\n {shown.length === 0 ? (\n <div {...mergeAttrs<'div'>({ sx: notificationListStyles.empty }, propsEmpty)}>\n {empty ?? <Empty size=\"sm\" title={messages.notificationsEmpty} />}\n </div>\n ) : (\n <ul {...mergeAttrs<'ul'>({ sx: notificationListStyles.list }, propsList)}>\n {shown.map((item) => (\n <li key={item.id}>\n <a\n {...mergeAttrs<'a'>(\n { sx: [notificationListStyles.item, focusRingStyles.visible] },\n typeof propsItem === 'function' ? propsItem(item) : propsItem,\n {\n href: item.href ? router.href(item.href) : undefined,\n role: item.href ? undefined : 'button',\n tabindex: 0,\n onClick: (event: MouseEvent) => select(item, event),\n onKeydown: (event: KeyboardEvent) => {\n if (!item.href && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n select(item, event as unknown as MouseEvent);\n }\n },\n },\n )}\n data-state={item.read ? 'read' : 'unread'}\n >\n <span {...css.attrs(notificationListStyles.dot)}>\n {!item.read && <StatusDot tone={item.tone ?? 'accent'} />}\n </span>\n <span {...css.attrs(notificationListStyles.body)}>\n <span\n {...css.attrs(\n notificationListStyles.itemTitle,\n !item.read && notificationListStyles.itemTitleUnread,\n )}\n >\n {!item.read && (\n <VisuallyHidden>{messages.notificationUnreadMark} </VisuallyHidden>\n )}\n {item.title}\n </span>\n {item.description && (\n <span {...css.attrs(notificationListStyles.muted)}>{item.description}</span>\n )}\n </span>\n <time\n datetime={item.time}\n title={format.date(item.time, { dateStyle: 'medium', timeStyle: 'short' })}\n {...css.attrs(notificationListStyles.time)}\n >\n {format.relative(item.time, { now })}\n </time>\n </a>\n </li>\n ))}\n </ul>\n )}\n {hasContent(footer) && (\n <div {...mergeAttrs<'div'>({ sx: notificationListStyles.foot }, propsFooter)}>\n {footer}\n </div>\n )}\n </div>\n );\n };\n },\n);\n\n/** 未声明的属性落到弹出层的面板上 */\nexport interface NotificationCenterProps\n extends Omit<PopoverProps, 'trigger' | 'children'>, NotificationListOptions {\n /** 铃铛按钮的部件属性 */\n propsTrigger?: Partial<IconButtonProps>;\n /** 面板里的通知列表的部件属性 */\n propsNotificationList?: Partial<NotificationListProps>;\n}\n\n/** NotificationCenter 的样式:panel(弹出层的宽度)、list(列表的最大高度) */\nexport const notificationCenterStyles = css.create({\n panel: { width: 'min(26rem, calc(100vw - 2rem))', padding: 0 },\n list: { maxHeight: '24rem' },\n});\n\n/** 通知中心:顶栏的铃铛(带未读数),点开是 NotificationList;点了一条就收起 */\nexport const NotificationCenter = _component(\n ({\n items,\n types,\n onRead,\n onItemClick,\n footer,\n empty,\n now,\n _open: open = _bindable(false),\n propsTrigger,\n propsNotificationList,\n sx,\n ...rest\n }: NotificationCenterProps) => {\n const messages = useMessages('patterns', patternsMessages);\n const unread = _derived(items.filter((item) => !item.read).length);\n return () => (\n <Popover\n {...rest}\n placement={rest.placement ?? 'bottom-end'}\n sx={[notificationCenterStyles.panel, sx]}\n _open={open}\n trigger={(trigger) => (\n <Indicator count={unread} label={messages.notificationsUnread(unread)}>\n <IconButton\n {...mergePartProps<IconButtonProps>(\n { icon: Bell, 'aria-label': messages.notificationsUnread(unread) },\n propsTrigger,\n trigger,\n )}\n />\n </Indicator>\n )}\n >\n <NotificationList\n {...mergePartProps<NotificationListProps>(\n { items, propsList: { sx: notificationCenterStyles.list } },\n propsNotificationList,\n {\n types,\n onRead,\n footer,\n empty,\n now,\n onItemClick: (item: NotificationItem) => {\n onItemClick?.(item);\n open = false;\n },\n },\n )}\n />\n </Popover>\n );\n },\n);\n"],"mappings":"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"}
@@ -9,18 +9,23 @@ export interface PageTab {
9
9
  /** 固定:不能关闭(首页、工作台) */
10
10
  pinned?: boolean;
11
11
  }
12
- /** 未声明的属性落到外层(Tabs 的 div)上 */
13
- export interface PageTabsProps extends PartProps<'div'> {
12
+ /** 页签区的状态与操作:useRouteTabs 的结果就是这个形状 */
13
+ export interface PageTabsSource {
14
14
  /** 打开的页面,按显示的顺序 */
15
- pages: readonly PageTab[];
15
+ readonly pages: readonly PageTab[];
16
16
  /** 当前页面的地址 */
17
- current: string;
17
+ readonly current: string;
18
18
  /** 点了一个页签(或 Enter):跳过去 */
19
- onNavigate: (path: string) => void;
19
+ onNavigate(path: string): void;
20
20
  /** 关闭页签(关闭按钮、Delete、中键,或右键菜单里的“关闭其他”“关闭右侧”):参数是要关的地址 */
21
- onClose?: (paths: string[]) => void;
21
+ onClose?(paths: string[]): void;
22
22
  /** 拖动排序:新的顺序 */
23
- onReorder?: (paths: string[]) => void;
23
+ onReorder?(paths: string[]): void;
24
+ }
25
+ /** 未声明的属性落到外层(Tabs 的 div)上 */
26
+ export interface PageTabsProps extends PartProps<'div'>, Partial<PageTabsSource> {
27
+ /** 整个状态:useRouteTabs 的结果,页签区随它更新;写了它就不用再写 pages、current、onNavigate 等 */
28
+ tabs?: PageTabsSource;
24
29
  /** 标签栏右边“全部标签页”的菜单,默认 true */
25
30
  showAllMenu?: boolean;
26
31
  /** 标签栏(TabList)的部件属性 */
@@ -4,52 +4,64 @@ import { ContextMenu } from "../overlay/ContextMenu.js";
4
4
  import { Tab, TabList, Tabs } from "../navigation/Tabs.js";
5
5
  import { useMessages } from "../config/lang/messages.js";
6
6
  import { patternsMessages } from "./messages.js";
7
- import { Fragment, createVNode, isVNode, mergeProps, ref } from "vue";
8
- import { _component, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
7
+ import { Fragment, createVNode, isVNode, ref } from "vue";
8
+ import { _component, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
9
9
  //#region src/patterns/PageTabs.tsx
10
10
  /** 一个页签 */
11
+ /** 页签区的状态与操作:useRouteTabs 的结果就是这个形状 */
11
12
  /** 未声明的属性落到外层(Tabs 的 div)上 */
12
13
  function _isSlot(s) {
13
14
  return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !isVNode(s);
14
15
  }
15
16
  /** 页签区:打开过的页面,切换、关闭、排序 */
16
17
  var PageTabs = /*#__PURE__*/ _component((_props, _ctx) => {
17
- const _cache = [];
18
- const pages = readonlyProp(_props, "pages");
19
- const current = readonlyProp(_props, "current");
20
- const onNavigate = readonlyProp(_props, "onNavigate");
21
- const onClose = readonlyProp(_props, "onClose");
22
- const onReorder = readonlyProp(_props, "onReorder");
18
+ const tabs = readonlyProp(_props, "tabs");
19
+ const ownPages = readonlyProp(_props, "pages");
20
+ const ownCurrent = readonlyProp(_props, "current");
21
+ const ownNavigate = readonlyProp(_props, "onNavigate");
22
+ const ownClose = readonlyProp(_props, "onClose");
23
+ const ownReorder = readonlyProp(_props, "onReorder");
23
24
  const showAllMenu = readonlyProp(_props, "showAllMenu", true);
24
25
  const propsList = readonlyProp(_props, "propsList");
25
26
  const rest = restProps(_props, _ctx, true);
26
27
  const messages = useMessages("patterns", patternsMessages);
28
+ const state = () => ({
29
+ pages: tabs.value?.pages ?? ownPages.value ?? [],
30
+ current: tabs.value?.current ?? ownCurrent.value ?? "",
31
+ onNavigate: tabs.value?.onNavigate.bind(tabs.value) ?? ownNavigate.value,
32
+ onClose: tabs.value?.onClose?.bind(tabs.value) ?? ownClose.value,
33
+ onReorder: tabs.value?.onReorder?.bind(tabs.value) ?? ownReorder.value
34
+ });
27
35
  let menuFor = ref(null);
28
36
  /** 右键菜单里关闭 path 之外的、它右边的 */
29
- const others = (path) => pages.value.filter((page) => page.path !== path && !page.pinned).map((page) => page.path);
30
- const toRight = (path) => pages.value.slice(pages.value.findIndex((page) => page.path === path) + 1).filter((page) => !page.pinned).map((page) => page.path);
37
+ const others = (path) => state().pages.filter((page) => page.path !== path && !page.pinned).map((page) => page.path);
38
+ const toRight = (path) => {
39
+ const pages = state().pages;
40
+ return pages.slice(pages.findIndex((page) => page.path === path) + 1).filter((page) => !page.pinned).map((page) => page.path);
41
+ };
31
42
  function renderMenu() {
43
+ const { pages, onClose } = state();
32
44
  const path = menuFor.value;
33
- const page = pages.value.find((item) => item.path === path);
45
+ const page = pages.find((item) => item.path === path);
34
46
  if (!path || !page) return null;
35
47
  return createVNode(Fragment, null, [
36
48
  createVNode(MenuItem, {
37
49
  "disabled": page.pinned,
38
- "onAction": () => onClose.value?.([path])
50
+ "onAction": () => onClose?.([path])
39
51
  }, {
40
52
  default: () => [messages.pageTabsClose],
41
53
  $stable: true
42
54
  }),
43
55
  createVNode(MenuItem, {
44
56
  "disabled": others(path).length === 0,
45
- "onAction": () => onClose.value?.(others(path))
57
+ "onAction": () => onClose?.(others(path))
46
58
  }, {
47
59
  default: () => [messages.pageTabsCloseOthers],
48
60
  $stable: true
49
61
  }),
50
62
  createVNode(MenuItem, {
51
63
  "disabled": toRight(path).length === 0,
52
- "onAction": () => onClose.value?.(toRight(path))
64
+ "onAction": () => onClose?.(toRight(path))
53
65
  }, {
54
66
  default: () => [messages.pageTabsCloseRight],
55
67
  $stable: true
@@ -57,24 +69,23 @@ var PageTabs = /*#__PURE__*/ _component((_props, _ctx) => {
57
69
  ]);
58
70
  }
59
71
  return () => {
60
- const list = createVNode(TabList, mergeProps(mergePartProps({
72
+ let _slot;
73
+ const { pages, current, onNavigate, onClose, onReorder } = state();
74
+ const list = createVNode(TabList, mergePartProps({
61
75
  "aria-label": messages.pageTabsLabel,
62
76
  showAllMenu: showAllMenu.value,
63
- onReorder: onReorder.value
64
- }, propsList.value), { "key": "__site0" }), {
65
- default: () => [pages.value.map((page) => createVNode(Tab, {
66
- "key": page.path,
67
- "value": page.path,
68
- "onClose": page.pinned || !onClose.value ? void 0 : () => onClose.value([page.path]),
69
- "onContextmenu": () => menuFor.value = page.path
70
- }, { default: () => [page.title] }))],
71
- $stable: true
72
- });
73
- return createVNode(Tabs, mergeProps(rest, {
74
- "_value": current.value,
75
- "onUpdate:_value": _cache[0] || (_cache[0] = (next) => next !== void 0 && next !== current.value && onNavigate.value(next)),
77
+ onReorder
78
+ }, propsList.value), _isSlot(_slot = pages.map((page) => createVNode(Tab, {
79
+ "key": page.path,
80
+ "value": page.path,
81
+ "onClose": page.pinned || !onClose ? void 0 : () => onClose([page.path]),
82
+ "onContextmenu": () => menuFor.value = page.path
83
+ }, { default: () => [page.title] }))) ? _slot : { default: () => [_slot] });
84
+ return createVNode(Tabs, mergeJsxProps(rest, {
85
+ "_value": current,
86
+ "onUpdate:_value": (next) => next !== void 0 && next !== current && onNavigate?.(next),
76
87
  "activation": "manual"
77
- }), { default: () => [onClose.value ? createVNode(ContextMenu, {
88
+ }), { default: () => [onClose ? createVNode(ContextMenu, {
78
89
  "menu": renderMenu(),
79
90
  "label": messages.pageTabsMenu
80
91
  }, _isSlot(list) ? list : { default: () => [list] }) : list] });
@@ -82,6 +93,7 @@ var PageTabs = /*#__PURE__*/ _component((_props, _ctx) => {
82
93
  }, {
83
94
  name: "PageTabs",
84
95
  props: [
96
+ "tabs",
85
97
  "pages",
86
98
  "current",
87
99
  "onNavigate",
@@ -1 +1 @@
1
- {"version":3,"file":"PageTabs.js","names":["_isVNode","_vtp_readonlyProp","_vtp_restProps","_vue_ref","_createVNode","_Fragment","_mergeProps"],"sources":["../../src/patterns/PageTabs.tsx"],"sourcesContent":["/**\n * 页签区(后台的多页签):打开过的页面各一个标签,点了切过去,可以关闭、拖动排序,右键有“关闭其他”“关闭右侧”。\n * 放在 AppShell 的 tabs 里;和路由同步交给 useRouteTabs(@zerodep-dev/vue-tsx-pro-ui/vue-router),也可以自己维护 pages。\n *\n * const tabs = useRouteTabs(router, { home: '/' });\n * <AppShell tabs={<PageTabs {...tabs} />} …>\n *\n * 标签就是 Tabs(只当导航,没有 TabPanel;activation=\"manual\":方向键只移动焦点,Enter 才切过去,免得逐个跳页面):\n * 关闭按钮、Delete、鼠标中键关闭,拖动或 Alt + ←→ 排序,放不下时滚动按钮与“全部标签页”。固定的页(pinned)不能关。\n *\n * 部件:Root(Tabs)、List(TabList)、Tab、Menu(右键菜单)。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { useMessages } from '../config/lang/messages';\nimport { Tab, TabList, Tabs, type TabListProps } from '../navigation/Tabs';\nimport { ContextMenu } from '../overlay/ContextMenu';\nimport { MenuItem } from '../overlay/Menu';\nimport { mergePartProps, type PartProps } from '../utils/props';\nimport { patternsMessages } from './messages';\n\n/** 一个页签 */\nexport interface PageTab {\n /** 页面的地址:也是页签的 value */\n path: string;\n /** 页签上的文字 */\n title: string;\n /** 固定:不能关闭(首页、工作台) */\n pinned?: boolean;\n}\n\n/** 未声明的属性落到外层(Tabs 的 div)上 */\nexport interface PageTabsProps extends PartProps<'div'> {\n /** 打开的页面,按显示的顺序 */\n pages: readonly PageTab[];\n /** 当前页面的地址 */\n current: string;\n /** 点了一个页签(或 Enter):跳过去 */\n onNavigate: (path: string) => void;\n /** 关闭页签(关闭按钮、Delete、中键,或右键菜单里的“关闭其他”“关闭右侧”):参数是要关的地址 */\n onClose?: (paths: string[]) => void;\n /** 拖动排序:新的顺序 */\n onReorder?: (paths: string[]) => void;\n /** 标签栏右边“全部标签页”的菜单,默认 true */\n showAllMenu?: boolean;\n /** 标签栏(TabList)的部件属性 */\n propsList?: Partial<TabListProps>;\n}\n\n/** 页签区:打开过的页面,切换、关闭、排序 */\nexport const PageTabs = _component(\n ({\n pages,\n current,\n onNavigate,\n onClose,\n onReorder,\n showAllMenu = true,\n propsList,\n ...rest\n }: PageTabsProps) => {\n const messages = useMessages('patterns', patternsMessages);\n // 右键菜单是给哪个页签打开的(页签的 contextmenu 先于外层的 ContextMenu 收到)\n let menuFor = _state<string | null>(null);\n\n /** 右键菜单里关闭 path 之外的、它右边的 */\n const others = (path: string) =>\n pages.filter((page) => page.path !== path && !page.pinned).map((page) => page.path);\n const toRight = (path: string) =>\n pages\n .slice(pages.findIndex((page) => page.path === path) + 1)\n .filter((page) => !page.pinned)\n .map((page) => page.path);\n\n function renderMenu() {\n const path = menuFor;\n const page = pages.find((item) => item.path === path);\n if (!path || !page) return null;\n return (\n <>\n <MenuItem disabled={page.pinned} onAction={() => onClose?.([path])}>\n {messages.pageTabsClose}\n </MenuItem>\n <MenuItem disabled={others(path).length === 0} onAction={() => onClose?.(others(path))}>\n {messages.pageTabsCloseOthers}\n </MenuItem>\n <MenuItem disabled={toRight(path).length === 0} onAction={() => onClose?.(toRight(path))}>\n {messages.pageTabsCloseRight}\n </MenuItem>\n </>\n );\n }\n\n return () => {\n const list = (\n <TabList\n {...mergePartProps<TabListProps>(\n { 'aria-label': messages.pageTabsLabel, showAllMenu, onReorder },\n propsList,\n )}\n >\n {pages.map((page) => (\n <Tab\n key={page.path}\n value={page.path}\n onClose={page.pinned || !onClose ? undefined : () => onClose([page.path])}\n onContextmenu={() => (menuFor = page.path)}\n >\n {page.title}\n </Tab>\n ))}\n </TabList>\n );\n return (\n <Tabs\n {...rest}\n _value={current}\n onUpdate:_value={(next: string | undefined) =>\n next !== undefined && next !== current && onNavigate(next)\n }\n activation=\"manual\"\n >\n {onClose ? (\n <ContextMenu menu={renderMenu()} label={messages.pageTabsMenu}>\n {list}\n </ContextMenu>\n ) : (\n list\n )}\n </Tabs>\n );\n };\n },\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"PageTabs.js","names":["_isVNode","_vtp_readonlyProp","_vtp_restProps","_vue_ref","_createVNode","_Fragment","_mergeProps"],"sources":["../../src/patterns/PageTabs.tsx"],"sourcesContent":["/**\n * 页签区(后台的多页签):打开过的页面各一个标签,点了切过去,可以关闭、拖动排序,右键有“关闭其他”“关闭右侧”。\n * 放在 AppShell 的 tabs 里;和路由同步交给 useRouteTabs(@zerodep-dev/vue-tsx-pro-ui/vue-router),也可以自己维护 pages。\n *\n * const tabs = useRouteTabs(router, { home: '/' });\n * <AppShell tabs={<PageTabs tabs={tabs} />} …>\n *\n * 路由页签的状态整个写在 tabs 里;不要写成 {...tabs} 展开:展开只取当时的值,之后打开新页面页签区不会更新。\n *\n * 标签就是 Tabs(只当导航,没有 TabPanel;activation=\"manual\":方向键只移动焦点,Enter 才切过去,免得逐个跳页面):\n * 关闭按钮、Delete、鼠标中键关闭,拖动或 Alt + ←→ 排序,放不下时滚动按钮与“全部标签页”。固定的页(pinned)不能关。\n *\n * 部件:Root(Tabs)、List(TabList)、Tab、Menu(右键菜单)。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { useMessages } from '../config/lang/messages';\nimport { Tab, TabList, Tabs, type TabListProps } from '../navigation/Tabs';\nimport { ContextMenu } from '../overlay/ContextMenu';\nimport { MenuItem } from '../overlay/Menu';\nimport { mergePartProps, type PartProps } from '../utils/props';\nimport { patternsMessages } from './messages';\n\n/** 一个页签 */\nexport interface PageTab {\n /** 页面的地址:也是页签的 value */\n path: string;\n /** 页签上的文字 */\n title: string;\n /** 固定:不能关闭(首页、工作台) */\n pinned?: boolean;\n}\n\n/** 页签区的状态与操作:useRouteTabs 的结果就是这个形状 */\nexport interface PageTabsSource {\n /** 打开的页面,按显示的顺序 */\n readonly pages: readonly PageTab[];\n /** 当前页面的地址 */\n readonly current: string;\n /** 点了一个页签(或 Enter):跳过去 */\n onNavigate(path: string): void;\n /** 关闭页签(关闭按钮、Delete、中键,或右键菜单里的“关闭其他”“关闭右侧”):参数是要关的地址 */\n onClose?(paths: string[]): void;\n /** 拖动排序:新的顺序 */\n onReorder?(paths: string[]): void;\n}\n\n/** 未声明的属性落到外层(Tabs 的 div)上 */\nexport interface PageTabsProps extends PartProps<'div'>, Partial<PageTabsSource> {\n /** 整个状态:useRouteTabs 的结果,页签区随它更新;写了它就不用再写 pages、current、onNavigate 等 */\n tabs?: PageTabsSource;\n /** 标签栏右边“全部标签页”的菜单,默认 true */\n showAllMenu?: boolean;\n /** 标签栏(TabList)的部件属性 */\n propsList?: Partial<TabListProps>;\n}\n\n/** 页签区:打开过的页面,切换、关闭、排序 */\nexport const PageTabs = _component(\n ({\n tabs,\n pages: ownPages,\n current: ownCurrent,\n onNavigate: ownNavigate,\n onClose: ownClose,\n onReorder: ownReorder,\n showAllMenu = true,\n propsList,\n ...rest\n }: PageTabsProps) => {\n const messages = useMessages('patterns', patternsMessages);\n // tabs 里的是实时的(读它的 getter 才会跟着路由更新),所以每次渲染都重新取\n const state = () => ({\n pages: tabs?.pages ?? ownPages ?? [],\n current: tabs?.current ?? ownCurrent ?? '',\n onNavigate: tabs?.onNavigate.bind(tabs) ?? ownNavigate,\n onClose: tabs?.onClose?.bind(tabs) ?? ownClose,\n onReorder: tabs?.onReorder?.bind(tabs) ?? ownReorder,\n });\n // 右键菜单是给哪个页签打开的(页签的 contextmenu 先于外层的 ContextMenu 收到)\n let menuFor = _state<string | null>(null);\n\n /** 右键菜单里关闭 path 之外的、它右边的 */\n const others = (path: string) =>\n state()\n .pages.filter((page) => page.path !== path && !page.pinned)\n .map((page) => page.path);\n const toRight = (path: string) => {\n const pages = state().pages;\n return pages\n .slice(pages.findIndex((page) => page.path === path) + 1)\n .filter((page) => !page.pinned)\n .map((page) => page.path);\n };\n\n function renderMenu() {\n const { pages, onClose } = state();\n const path = menuFor;\n const page = pages.find((item) => item.path === path);\n if (!path || !page) return null;\n return (\n <>\n <MenuItem disabled={page.pinned} onAction={() => onClose?.([path])}>\n {messages.pageTabsClose}\n </MenuItem>\n <MenuItem disabled={others(path).length === 0} onAction={() => onClose?.(others(path))}>\n {messages.pageTabsCloseOthers}\n </MenuItem>\n <MenuItem disabled={toRight(path).length === 0} onAction={() => onClose?.(toRight(path))}>\n {messages.pageTabsCloseRight}\n </MenuItem>\n </>\n );\n }\n\n return () => {\n const { pages, current, onNavigate, onClose, onReorder } = state();\n const list = (\n <TabList\n {...mergePartProps<TabListProps>(\n { 'aria-label': messages.pageTabsLabel, showAllMenu, onReorder },\n propsList,\n )}\n >\n {pages.map((page) => (\n <Tab\n key={page.path}\n value={page.path}\n onClose={page.pinned || !onClose ? undefined : () => onClose([page.path])}\n onContextmenu={() => (menuFor = page.path)}\n >\n {page.title}\n </Tab>\n ))}\n </TabList>\n );\n return (\n <Tabs\n {...rest}\n _value={current}\n onUpdate:_value={(next: string | undefined) =>\n next !== undefined && next !== current && onNavigate?.(next)\n }\n activation=\"manual\"\n >\n {onClose ? (\n <ContextMenu menu={renderMenu()} label={messages.pageTabsMenu}>\n {list}\n </ContextMenu>\n ) : (\n list\n )}\n </Tabs>\n );\n };\n },\n);\n"],"mappings":"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"}