@sovereignfs/ui 0.54.0 → 0.83.1

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 (277) hide show
  1. package/dist/Accordion.module.css +1 -1
  2. package/dist/Alert.module.css +1 -1
  3. package/dist/AppsLauncher.module.css +96 -0
  4. package/dist/Avatar.module.css +1 -1
  5. package/dist/Badge.module.css +30 -7
  6. package/dist/BalanceChip.module.css +1 -1
  7. package/dist/Button.module.css +18 -2
  8. package/dist/Card.module.css +1 -1
  9. package/dist/CardTile.module.css +181 -0
  10. package/dist/CodeTextarea.module.css +1 -1
  11. package/dist/Collapsible.module.css +1 -1
  12. package/dist/ColorPicker.module.css +132 -0
  13. package/dist/Combobox.module.css +2 -2
  14. package/dist/Command.module.css +1 -1
  15. package/dist/ConfirmDialog.module.css +2 -2
  16. package/dist/ConsentPrompt.module.css +21 -0
  17. package/dist/DatePicker.module.css +1 -1
  18. package/dist/Dialog.module.css +130 -21
  19. package/dist/Drawer.module.css +36 -7
  20. package/dist/Header.module.css +62 -0
  21. package/dist/HeaderFooterLayout.module.css +21 -0
  22. package/dist/IconPicker.module.css +1 -1
  23. package/dist/Input.module.css +1 -1
  24. package/dist/Kbd.module.css +1 -1
  25. package/dist/Markdown.module.css +24 -0
  26. package/dist/Menu.module.css +1 -0
  27. package/dist/Message.module.css +1 -1
  28. package/dist/MessageScroller.module.css +12 -1
  29. package/dist/MobileAppsDrawer.module.css +10 -9
  30. package/dist/MobileFooter.module.css +1 -1
  31. package/dist/MobileHeader.module.css +14 -1
  32. package/dist/NavList.module.css +130 -0
  33. package/dist/NavTabs.module.css +1 -1
  34. package/dist/NotificationsPanel.module.css +258 -0
  35. package/dist/OverlayHeader.module.css +1 -1
  36. package/dist/PageContainer.module.css +62 -0
  37. package/dist/PageHeader.module.css +54 -0
  38. package/dist/PageLayout.module.css +51 -0
  39. package/dist/Popover.module.css +1 -1
  40. package/dist/QuantityStepper.module.css +1 -1
  41. package/dist/Resizable.module.css +3 -3
  42. package/dist/RootLayout.module.css +12 -0
  43. package/dist/SegmentedControl.module.css +1 -1
  44. package/dist/Select.module.css +1 -1
  45. package/dist/Sheet.module.css +4 -1
  46. package/dist/SplitPane.module.css +4 -4
  47. package/dist/StatusBadge.module.css +1 -1
  48. package/dist/StepStrip.module.css +53 -0
  49. package/dist/SuggestionInput.module.css +2 -2
  50. package/dist/SwipableMobileCarousel.module.css +12 -3
  51. package/dist/SwipableMobileCarouselDots.module.css +15 -0
  52. package/dist/SwipeStack.module.css +101 -0
  53. package/dist/SystemBanner.module.css +10 -0
  54. package/dist/Table.module.css +2 -2
  55. package/dist/Tabs.module.css +1 -1
  56. package/dist/TagInput.module.css +2 -2
  57. package/dist/Textarea.module.css +9 -1
  58. package/dist/ThreeColumnLayout.module.css +35 -0
  59. package/dist/Toast.module.css +62 -3
  60. package/dist/Tooltip.module.css +1 -1
  61. package/dist/UserMenu.module.css +90 -0
  62. package/dist/index.d.ts +1047 -45
  63. package/dist/index.js +3908 -1456
  64. package/dist/tokens/primitives.css +11 -0
  65. package/dist/tokens/semantic.css +32 -0
  66. package/dist/tokens/theme-presets.css +33 -0
  67. package/package.json +25 -9
  68. package/src/__tests__/package-json.test.ts +8 -0
  69. package/src/components/Accordion/Accordion.module.css +1 -1
  70. package/src/components/Alert/Alert.module.css +1 -1
  71. package/src/components/AppsLauncher/AppsLauncher.module.css +96 -0
  72. package/src/components/AppsLauncher/AppsLauncher.stories.tsx +58 -0
  73. package/src/components/AppsLauncher/AppsLauncher.tsx +117 -0
  74. package/src/components/AppsLauncher/__tests__/AppsLauncher.test.tsx +52 -0
  75. package/src/components/Avatar/Avatar.module.css +1 -1
  76. package/src/components/Badge/Badge.module.css +30 -7
  77. package/src/components/Badge/Badge.stories.tsx +16 -4
  78. package/src/components/Badge/Badge.tsx +7 -4
  79. package/src/components/Badge/__tests__/Badge.test.tsx +4 -2
  80. package/src/components/BalanceChip/BalanceChip.module.css +1 -1
  81. package/src/components/Button/Button.module.css +18 -2
  82. package/src/components/Card/Card.module.css +1 -1
  83. package/src/components/CardTile/CardTile.module.css +181 -0
  84. package/src/components/CardTile/CardTile.stories.tsx +86 -0
  85. package/src/components/CardTile/CardTile.tsx +128 -0
  86. package/src/components/CardTile/__tests__/CardTile.test.tsx +56 -0
  87. package/src/components/CodeTextarea/CodeTextarea.module.css +1 -1
  88. package/src/components/Collapsible/Collapsible.module.css +1 -1
  89. package/src/components/ColorPicker/ColorPicker.module.css +132 -0
  90. package/src/components/ColorPicker/ColorPicker.stories.tsx +80 -0
  91. package/src/components/ColorPicker/ColorPicker.tsx +157 -0
  92. package/src/components/ColorPicker/__tests__/ColorPicker.test.tsx +117 -0
  93. package/src/components/Combobox/Combobox.module.css +2 -2
  94. package/src/components/Command/Command.module.css +1 -1
  95. package/src/components/ConfirmDialog/ConfirmDialog.module.css +2 -2
  96. package/src/components/ConfirmDialog/ConfirmDialog.tsx +28 -2
  97. package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +71 -3
  98. package/src/components/ConsentPrompt/ConsentPrompt.module.css +21 -0
  99. package/src/components/ConsentPrompt/ConsentPrompt.stories.tsx +31 -0
  100. package/src/components/ConsentPrompt/ConsentPrompt.tsx +66 -0
  101. package/src/components/ContextMenu/ContextMenu.tsx +1 -1
  102. package/src/components/DatePicker/DatePicker.module.css +1 -1
  103. package/src/components/Dialog/Dialog.module.css +130 -21
  104. package/src/components/Dialog/Dialog.stories.tsx +179 -2
  105. package/src/components/Dialog/Dialog.tsx +129 -35
  106. package/src/components/Dialog/__tests__/Dialog.test.tsx +190 -9
  107. package/src/components/DragHandleRow/DragHandleRow.tsx +2 -21
  108. package/src/components/Drawer/Drawer.module.css +36 -7
  109. package/src/components/Drawer/Drawer.tsx +9 -10
  110. package/src/components/Drawer/__tests__/Drawer.test.tsx +33 -6
  111. package/src/components/GripIcon/GripIcon.tsx +27 -0
  112. package/src/components/Header/Header.module.css +62 -0
  113. package/src/components/Header/Header.stories.tsx +452 -0
  114. package/src/components/Header/Header.tsx +102 -0
  115. package/src/components/Header/__tests__/Header.test.tsx +104 -0
  116. package/src/components/HeaderFooterLayout/HeaderFooterLayout.module.css +21 -0
  117. package/src/components/HeaderFooterLayout/HeaderFooterLayout.stories.tsx +111 -0
  118. package/src/components/HeaderFooterLayout/HeaderFooterLayout.tsx +54 -0
  119. package/src/components/HeaderFooterLayout/__tests__/HeaderFooterLayout.test.tsx +58 -0
  120. package/src/components/Icon/icons/arrow-left-right.tsx +23 -0
  121. package/src/components/Icon/icons/bold.tsx +20 -0
  122. package/src/components/Icon/icons/calendar.tsx +4 -4
  123. package/src/components/Icon/icons/carrot.tsx +3 -3
  124. package/src/components/Icon/icons/circle-chevron-left.tsx +21 -0
  125. package/src/components/Icon/icons/code.tsx +21 -0
  126. package/src/components/Icon/icons/copy.tsx +21 -0
  127. package/src/components/Icon/icons/credit-card.tsx +21 -0
  128. package/src/components/Icon/icons/database.tsx +22 -0
  129. package/src/components/Icon/icons/download.tsx +22 -0
  130. package/src/components/Icon/icons/file-text.tsx +24 -0
  131. package/src/components/Icon/icons/folder-closed.tsx +21 -0
  132. package/src/components/Icon/icons/folder-open.tsx +20 -0
  133. package/src/components/Icon/icons/folder.tsx +20 -0
  134. package/src/components/Icon/icons/folders.tsx +21 -0
  135. package/src/components/Icon/icons/hat-glasses.tsx +24 -0
  136. package/src/components/Icon/icons/history.tsx +22 -0
  137. package/src/components/Icon/icons/inbox.tsx +21 -0
  138. package/src/components/Icon/icons/index.ts +161 -1
  139. package/src/components/Icon/icons/italic.tsx +22 -0
  140. package/src/components/Icon/icons/layers.tsx +22 -0
  141. package/src/components/Icon/icons/layout-dashboard.tsx +23 -0
  142. package/src/components/Icon/icons/layout-grid.tsx +23 -0
  143. package/src/components/Icon/icons/link.tsx +21 -0
  144. package/src/components/Icon/icons/list-ordered.tsx +25 -0
  145. package/src/components/Icon/icons/list.tsx +25 -0
  146. package/src/components/Icon/icons/luggage.tsx +24 -0
  147. package/src/components/Icon/icons/map-pin.tsx +21 -0
  148. package/src/components/Icon/icons/minus.tsx +20 -0
  149. package/src/components/Icon/icons/paint-bucket.tsx +23 -0
  150. package/src/components/Icon/icons/panel-left.tsx +21 -0
  151. package/src/components/Icon/icons/pin.tsx +21 -0
  152. package/src/components/Icon/icons/rectangle-ellipsis.tsx +23 -0
  153. package/src/components/Icon/icons/refresh-cw.tsx +23 -0
  154. package/src/components/Icon/icons/rotate-cw.tsx +21 -0
  155. package/src/components/Icon/icons/route.tsx +22 -0
  156. package/src/components/Icon/icons/send.tsx +21 -0
  157. package/src/components/Icon/icons/share-2.tsx +24 -0
  158. package/src/components/Icon/icons/sheet.tsx +24 -0
  159. package/src/components/Icon/icons/square-kanban.tsx +23 -0
  160. package/src/components/Icon/icons/table.tsx +23 -0
  161. package/src/components/Icon/icons/user-round-plus.tsx +23 -0
  162. package/src/components/Icon/icons/users.tsx +23 -0
  163. package/src/components/IconPicker/IconPicker.module.css +1 -1
  164. package/src/components/Input/Input.module.css +1 -1
  165. package/src/components/Input/Input.tsx +8 -2
  166. package/src/components/Kbd/Kbd.module.css +1 -1
  167. package/src/components/Markdown/Markdown.module.css +24 -0
  168. package/src/components/Markdown/Markdown.stories.tsx +10 -0
  169. package/src/components/Markdown/Markdown.tsx +166 -29
  170. package/src/components/Markdown/__tests__/Markdown.test.tsx +144 -0
  171. package/src/components/Menu/Menu.module.css +1 -0
  172. package/src/components/Menu/Menu.tsx +72 -22
  173. package/src/components/Menu/__tests__/Menu.test.tsx +30 -0
  174. package/src/components/Menubar/Menubar.tsx +1 -1
  175. package/src/components/Message/Message.module.css +1 -1
  176. package/src/components/MessageScroller/MessageScroller.module.css +12 -1
  177. package/src/components/MessageScroller/MessageScroller.tsx +12 -0
  178. package/src/components/MobileAppsDrawer/MobileAppsDrawer.module.css +10 -9
  179. package/src/components/MobileFooter/MobileFooter.module.css +1 -1
  180. package/src/components/MobileFooter/MobileFooter.tsx +15 -2
  181. package/src/components/MobileFooter/__tests__/MobileFooter.test.tsx +58 -0
  182. package/src/components/MobileHeader/MobileHeader.module.css +14 -1
  183. package/src/components/MobileHeader/MobileHeader.tsx +15 -2
  184. package/src/components/MobileHeader/__tests__/MobileHeader.test.tsx +56 -1
  185. package/src/components/NavList/NavList.module.css +130 -0
  186. package/src/components/NavList/NavList.tsx +136 -0
  187. package/src/components/NavList/__tests__/NavList.test.tsx +109 -0
  188. package/src/components/NavTabs/NavTabs.module.css +1 -1
  189. package/src/components/NotificationsPanel/NotificationsPanel.module.css +258 -0
  190. package/src/components/NotificationsPanel/NotificationsPanel.stories.tsx +61 -0
  191. package/src/components/NotificationsPanel/NotificationsPanel.tsx +187 -0
  192. package/src/components/NotificationsPanel/__tests__/NotificationsPanel.test.tsx +78 -0
  193. package/src/components/OverlayHeader/OverlayHeader.module.css +1 -1
  194. package/src/components/OverlayHeader/OverlayHeader.tsx +19 -4
  195. package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +18 -0
  196. package/src/components/PageContainer/PageContainer.module.css +62 -0
  197. package/src/components/PageContainer/PageContainer.tsx +37 -4
  198. package/src/components/PageContainer/__tests__/PageContainer.test.tsx +41 -8
  199. package/src/components/PageHeader/PageHeader.module.css +54 -0
  200. package/src/components/PageHeader/PageHeader.tsx +146 -5
  201. package/src/components/PageHeader/__tests__/PageHeader.test.tsx +103 -0
  202. package/src/components/PageLayout/PageLayout.module.css +51 -0
  203. package/src/components/PageLayout/PageLayout.stories.tsx +73 -0
  204. package/src/components/PageLayout/PageLayout.tsx +51 -0
  205. package/src/components/PageLayout/__tests__/PageLayout.test.tsx +34 -0
  206. package/src/components/Popover/Popover.module.css +1 -1
  207. package/src/components/Popover/Popover.tsx +86 -4
  208. package/src/components/Popover/__tests__/Popover.test.tsx +165 -0
  209. package/src/components/QuantityStepper/QuantityStepper.module.css +1 -1
  210. package/src/components/Resizable/Resizable.module.css +3 -3
  211. package/src/components/RootLayout/RootLayout.module.css +12 -0
  212. package/src/components/RootLayout/RootLayout.stories.tsx +125 -0
  213. package/src/components/RootLayout/RootLayout.tsx +185 -0
  214. package/src/components/RootLayout/__tests__/RootLayout.test.tsx +161 -0
  215. package/src/components/SegmentedControl/SegmentedControl.module.css +1 -1
  216. package/src/components/Select/Select.module.css +1 -1
  217. package/src/components/Sheet/Sheet.module.css +4 -1
  218. package/src/components/Sheet/Sheet.tsx +5 -5
  219. package/src/components/SplitPane/SplitPane.module.css +4 -4
  220. package/src/components/StatusBadge/StatusBadge.module.css +1 -1
  221. package/src/components/StepStrip/StepStrip.module.css +53 -0
  222. package/src/components/StepStrip/StepStrip.tsx +74 -0
  223. package/src/components/SuggestionInput/SuggestionInput.module.css +2 -2
  224. package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.module.css +12 -3
  225. package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.tsx +25 -9
  226. package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarousel.test.tsx +81 -0
  227. package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.module.css +15 -0
  228. package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.tsx +12 -1
  229. package/src/components/SwipeStack/SwipeStack.module.css +101 -0
  230. package/src/components/SwipeStack/SwipeStack.tsx +199 -0
  231. package/src/components/SwipeStack/SwipeStackCard.tsx +22 -0
  232. package/src/components/SwipeStack/__tests__/SwipeStack.test.tsx +169 -0
  233. package/src/components/SystemBanner/SystemBanner.module.css +10 -0
  234. package/src/components/Table/Table.module.css +2 -2
  235. package/src/components/Tabs/Tabs.module.css +1 -1
  236. package/src/components/TagInput/TagInput.module.css +2 -2
  237. package/src/components/Textarea/Textarea.module.css +9 -1
  238. package/src/components/Textarea/Textarea.tsx +28 -5
  239. package/src/components/Textarea/__tests__/Textarea.test.tsx +23 -1
  240. package/src/components/ThreeColumnLayout/ThreeColumnLayout.module.css +35 -0
  241. package/src/components/ThreeColumnLayout/ThreeColumnLayout.stories.tsx +152 -0
  242. package/src/components/ThreeColumnLayout/ThreeColumnLayout.tsx +87 -0
  243. package/src/components/ThreeColumnLayout/__tests__/ThreeColumnLayout.test.tsx +78 -0
  244. package/src/components/Toast/Toast.module.css +62 -3
  245. package/src/components/Toast/Toast.stories.tsx +1 -1
  246. package/src/components/Tooltip/Tooltip.module.css +1 -1
  247. package/src/components/UserMenu/UserMenu.module.css +90 -0
  248. package/src/components/UserMenu/UserMenu.stories.tsx +49 -0
  249. package/src/components/UserMenu/UserMenu.tsx +132 -0
  250. package/src/components/UserMenu/__tests__/UserMenu.test.tsx +77 -0
  251. package/src/hooks/__tests__/usePublishShellChromeHeight.test.tsx +118 -0
  252. package/src/hooks/__tests__/useReorderSensors.test.ts +26 -0
  253. package/src/hooks/__tests__/useSnapCarousel.test.tsx +82 -0
  254. package/src/hooks/__tests__/useSwipeStack.test.tsx +155 -0
  255. package/src/hooks/index.ts +9 -0
  256. package/src/hooks/usePublishShellChromeHeight.ts +96 -0
  257. package/src/hooks/useReorderSensors.ts +88 -0
  258. package/src/hooks/useSnapCarousel.ts +211 -6
  259. package/src/hooks/useSwipeStack.ts +235 -0
  260. package/src/index.ts +61 -1
  261. package/src/overlay-shell.ts +65 -0
  262. package/src/stories/DesignSystemOverview.stories.tsx +617 -11
  263. package/src/stories/GripIcon.stories.tsx +29 -0
  264. package/src/stories/LayoutSystem.stories.tsx +639 -0
  265. package/src/stories/MenuEntries.stories.tsx +86 -0
  266. package/src/stories/NavList.stories.tsx +147 -0
  267. package/src/stories/PageContainer.stories.tsx +52 -7
  268. package/src/stories/PageHeader.stories.tsx +54 -1
  269. package/src/stories/StepStrip.stories.tsx +76 -0
  270. package/src/stories/SwipableMobileCarousel.stories.tsx +58 -0
  271. package/src/stories/SwipeStack.stories.tsx +200 -0
  272. package/src/stories/Textarea.stories.tsx +12 -0
  273. package/src/stories/TokenGallery.stories.tsx +50 -0
  274. package/src/tokens/primitives.css +11 -0
  275. package/src/tokens/semantic.css +32 -0
  276. package/src/tokens/theme-presets.css +33 -0
  277. package/src/tokens/theme-presets.ts +72 -0
@@ -0,0 +1,86 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { MenuEntries } from '../components/Menu/Menu';
3
+ import type { MenuEntry } from '../components/Menu/Menu';
4
+
5
+ /** Plain action entries — no separator, label, or checkable state. */
6
+ const actionItems: MenuEntry[] = [
7
+ { label: 'Rename', icon: 'pencil' },
8
+ { label: 'Duplicate', icon: 'plus' },
9
+ { label: 'Delete', icon: 'trash-2', destructive: true },
10
+ ];
11
+
12
+ const meta = {
13
+ title: 'Components/MenuEntries',
14
+ component: MenuEntries,
15
+ parameters: { layout: 'padded' },
16
+ args: {
17
+ items: actionItems,
18
+ onSelect: (entry) => console.log(`MenuEntries selected: ${entry.label}`),
19
+ },
20
+ } satisfies Meta<typeof MenuEntries>;
21
+
22
+ export default meta;
23
+ type Story = StoryObj<typeof meta>;
24
+
25
+ /**
26
+ * The `<ul role="menu">` item list itself, factored out of `Menu` so other
27
+ * adaptive-surface components (`ContextMenu`, `Menubar`) can render the exact
28
+ * same items/destructive/checked/icon markup inside their own positioning
29
+ * shell (a floating `Popover` panel, a `Drawer` bottom sheet, or a `Menubar`
30
+ * flyout) instead of re-deriving it and risking drift. Renders with no panel
31
+ * chrome of its own here — the surrounding background, border, and shadow
32
+ * belong to whichever surface hosts it; see `Menu` in this gallery for the
33
+ * composed, positioned result.
34
+ */
35
+ export const Default: Story = {};
36
+
37
+ /**
38
+ * The full entry vocabulary in one list: a `{ type: 'label' }` section
39
+ * heading, a checkable `menuitemradio` group (pass `checked` on every item in
40
+ * the group, including the unchecked ones, so their labels stay aligned with
41
+ * the checked one's reserved checkmark gutter), a `{ type: 'separator' }`
42
+ * divider, and a run of plain/destructive actions — the same composition
43
+ * `Menu`'s own "List actions" demo (Components/Menu) renders.
44
+ */
45
+ export const GroupedWithLabelAndSeparator: Story = {
46
+ args: {
47
+ items: [
48
+ { type: 'label', label: 'Sort by' },
49
+ { label: 'Manual', checked: true },
50
+ { label: 'Title', checked: false },
51
+ { label: 'Due date', checked: false },
52
+ { type: 'separator' },
53
+ ...actionItems,
54
+ ],
55
+ },
56
+ };
57
+
58
+ /**
59
+ * A `disabled` item stays in the list but renders `disabled` on its
60
+ * `<button>` — it can't be activated and `onSelect` never fires for it.
61
+ */
62
+ export const WithDisabledItem: Story = {
63
+ args: {
64
+ items: [
65
+ { label: 'Rename', icon: 'pencil' },
66
+ { label: 'Duplicate', icon: 'plus', disabled: true },
67
+ { label: 'Delete', icon: 'trash-2', destructive: true },
68
+ ],
69
+ },
70
+ };
71
+
72
+ /**
73
+ * An `href` entry renders as an `<a role="menuitem">` instead of a
74
+ * `<button>` — same href-vs-onClick convention as `MobileAppsDrawer`'s
75
+ * items — for a pure navigation entry that needs no extra `onSelect`.
76
+ */
77
+ export const WithLinkItems: Story = {
78
+ args: {
79
+ items: [
80
+ { label: 'View profile', icon: 'user', href: '#profile' },
81
+ { label: 'Settings', icon: 'settings', href: '#settings' },
82
+ { type: 'separator' },
83
+ { label: 'Sign out', icon: 'log-out', destructive: true },
84
+ ],
85
+ },
86
+ };
@@ -0,0 +1,147 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { NavList } from '../components/NavList/NavList';
3
+
4
+ const overviewGroup = {
5
+ id: 'overview',
6
+ items: [
7
+ { id: 'overview', label: 'Overview', href: '#overview', icon: 'layout-dashboard' as const },
8
+ ],
9
+ };
10
+
11
+ const peopleGroup = {
12
+ id: 'people',
13
+ label: 'People',
14
+ items: [
15
+ { id: 'users', label: 'Users', href: '#users', icon: 'users' as const },
16
+ { id: 'groups', label: 'Groups', href: '#groups', icon: 'layers' as const },
17
+ ],
18
+ };
19
+
20
+ const appsGroup = {
21
+ id: 'apps',
22
+ label: 'Apps',
23
+ items: [
24
+ { id: 'plugins', label: 'Apps', href: '#plugins', icon: 'layout-grid' as const },
25
+ { id: 'entitlements', label: 'Entitlements', href: '#entitlements', icon: 'shield' as const },
26
+ ],
27
+ };
28
+
29
+ const meta = {
30
+ title: 'Components/NavList',
31
+ component: NavList,
32
+ parameters: { layout: 'padded' },
33
+ args: {
34
+ groups: [overviewGroup, peopleGroup, appsGroup],
35
+ variant: 'static',
36
+ 'aria-label': 'Example sections',
37
+ },
38
+ } satisfies Meta<typeof NavList>;
39
+
40
+ export default meta;
41
+ type Story = StoryObj<typeof meta>;
42
+
43
+ /** Desktop sidebar shape — active row highlighted, no chevron. */
44
+ export const StaticActiveItem: Story = {
45
+ args: {
46
+ groups: [
47
+ {
48
+ id: 'overview',
49
+ items: [
50
+ {
51
+ id: 'overview',
52
+ label: 'Overview',
53
+ href: '#overview',
54
+ icon: 'layout-dashboard',
55
+ active: true,
56
+ },
57
+ ],
58
+ },
59
+ peopleGroup,
60
+ appsGroup,
61
+ ],
62
+ },
63
+ };
64
+
65
+ /**
66
+ * `density="compact"` — shrinks row height/padding for a short, always-
67
+ * visible `static` list (e.g. Warden's sidebar "New chat"/"Providers"/
68
+ * "Models" rows above its longer session list), where the default's full
69
+ * touch-target height reads as needlessly spaced out. Only meaningful with
70
+ * `variant="static"`; every other story on this page stays at the default
71
+ * density, unaffected.
72
+ */
73
+ export const CompactDensity: Story = {
74
+ args: {
75
+ density: 'compact',
76
+ groups: [
77
+ {
78
+ id: 'primary',
79
+ items: [
80
+ { id: 'new', label: 'New chat', href: '#new', icon: 'plus' },
81
+ { id: 'providers', label: 'Providers', href: '#providers', icon: 'link' },
82
+ { id: 'models', label: 'Models', href: '#models', icon: 'layers' },
83
+ ],
84
+ },
85
+ ],
86
+ },
87
+ };
88
+
89
+ /** Mobile index shape — every row gets a trailing chevron, no active state; tapping navigates to a full-screen section. */
90
+ export const Drilldown: Story = {
91
+ args: { variant: 'drilldown' },
92
+ parameters: { viewport: { defaultViewport: 'mobile' } },
93
+ };
94
+
95
+ /** `group.label` omitted entirely renders a flat list with no section headers. */
96
+ export const Ungrouped: Story = {
97
+ args: {
98
+ groups: [
99
+ {
100
+ id: 'flat',
101
+ items: [
102
+ { id: 'a', label: 'First', href: '#a', icon: 'house' },
103
+ { id: 'b', label: 'Second', href: '#b', icon: 'settings' },
104
+ ],
105
+ },
106
+ ],
107
+ },
108
+ };
109
+
110
+ /** Simulates a consumer swapping in a client-side router link (e.g. Next's `<Link>`) instead of a plain `<a>`. */
111
+ export const WithCustomLinkRenderer: Story = {
112
+ args: {
113
+ groups: [
114
+ {
115
+ id: 'overview',
116
+ items: [
117
+ {
118
+ id: 'overview',
119
+ label: 'Overview',
120
+ href: '#overview',
121
+ icon: 'layout-dashboard',
122
+ active: true,
123
+ },
124
+ ],
125
+ },
126
+ peopleGroup,
127
+ ],
128
+ renderLink: (item, linkProps) => (
129
+ <button
130
+ type="button"
131
+ className={linkProps.className}
132
+ aria-current={linkProps['aria-current']}
133
+ onClick={() => console.log(`client-side navigate to ${item.href}`)}
134
+ style={{
135
+ background: 'none',
136
+ border: 'none',
137
+ cursor: 'pointer',
138
+ font: 'inherit',
139
+ textAlign: 'left',
140
+ width: '100%',
141
+ }}
142
+ >
143
+ {linkProps.children}
144
+ </button>
145
+ ),
146
+ },
147
+ };
@@ -10,28 +10,73 @@ const demoBlock = (
10
10
  padding: 'var(--sv-space-4)',
11
11
  }}
12
12
  >
13
- Page content goes here. The runtime shell already pads this area — this component only
14
- constrains and centers width, it adds no padding of its own.
13
+ Page content goes here. This component supplies the page&apos;s gutter and constrains its width
14
+ — the runtime shell contributes neither.
15
15
  </div>
16
16
  );
17
17
 
18
18
  const meta = {
19
19
  title: 'Components/PageContainer',
20
20
  component: PageContainer,
21
- parameters: { layout: 'fullscreen' },
21
+ parameters: {
22
+ layout: 'fullscreen',
23
+ docs: {
24
+ description: {
25
+ component:
26
+ 'The wrapper every plugin page renders through. `padding` is the page gutter (the ' +
27
+ 'runtime shell no longer applies one) and `maxWidth` constrains content, padding ' +
28
+ 'included. Both default to the common case: a full-width page with the standard ' +
29
+ '32px/16px gutter.',
30
+ },
31
+ },
32
+ },
22
33
  args: { children: demoBlock },
23
34
  } satisfies Meta<typeof PageContainer>;
24
35
 
25
36
  export default meta;
26
37
  type Story = StoryObj<typeof meta>;
27
38
 
28
- export const Medium: Story = {};
39
+ /** Defaults: full width, `md` gutter. */
40
+ export const Default: Story = {};
29
41
 
30
- export const Small: Story = { args: { maxWidth: 'sm' } };
31
- export const Large: Story = { args: { maxWidth: 'lg' } };
32
- export const Full: Story = { args: { maxWidth: 'full' } };
42
+ // ── maxWidth ──────────────────────────────────────────────────────────────
33
43
 
44
+ export const WidthSmall: Story = { args: { maxWidth: 'sm' } };
45
+ export const WidthMedium: Story = { args: { maxWidth: 'md' } };
46
+ export const WidthLarge: Story = { args: { maxWidth: 'lg' } };
47
+
48
+ // ── padding ───────────────────────────────────────────────────────────────
49
+
50
+ /** For a page whose ancestor already supplies the gutter, or a full-bleed layout. */
51
+ export const PaddingNone: Story = { args: { padding: 'none' } };
52
+ export const PaddingSmall: Story = { args: { padding: 'sm' } };
53
+ export const PaddingLarge: Story = { args: { padding: 'lg' } };
54
+
55
+ /** Every gutter step tightens below 768px. */
34
56
  export const MobileViewport: Story = {
35
57
  args: { maxWidth: 'md' },
36
58
  parameters: { viewport: { defaultViewport: 'mobile1' } },
37
59
  };
60
+
61
+ /**
62
+ * An ancestor that already pads its content sets `--sv-page-gutter: 0` and
63
+ * PageContainer stands its own gutter down — how `Dialog` keeps an overlay
64
+ * plugin from being padded twice when the same page renders inside it.
65
+ */
66
+ export const GutterSuppressedByAncestor: Story = {
67
+ args: { maxWidth: 'md' },
68
+ decorators: [
69
+ (Story) => (
70
+ <div
71
+ style={{
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- CSS custom property
73
+ ['--sv-page-gutter' as any]: '0px',
74
+ border: '1px solid var(--sv-color-border)',
75
+ padding: 'var(--sv-space-6)',
76
+ }}
77
+ >
78
+ <Story />
79
+ </div>
80
+ ),
81
+ ],
82
+ };
@@ -5,7 +5,15 @@ import { Button } from '../components/Button/Button';
5
5
  const meta = {
6
6
  title: 'Components/PageHeader',
7
7
  component: PageHeader,
8
- parameters: { layout: 'padded' },
8
+ parameters: {
9
+ layout: 'padded',
10
+ docs: {
11
+ description: {
12
+ component:
13
+ "A page's title section. Web: title + description on the left, action on the right. Mobile: an optional back button, a single-line title (description drops out), the same action slot, and an optional vertical-ellipsis menu trigger — a compact single row, generalized from Kanban's own hand-rolled MobileBoardHeader. The web/mobile fork is ResponsiveSurface; only one tree is ever mounted.",
14
+ },
15
+ },
16
+ },
9
17
  } satisfies Meta<typeof PageHeader>;
10
18
 
11
19
  export default meta;
@@ -30,6 +38,16 @@ export const WithAction: Story = {
30
38
  },
31
39
  };
32
40
 
41
+ /** A leading icon before the title text, on both breakpoints — e.g. a
42
+ * folder-detail page pairing `folder-open` with the folder's own name. */
43
+ export const WithIcon: Story = {
44
+ args: {
45
+ title: 'Handbook',
46
+ icon: 'folder-open',
47
+ action: <Button size="sm">Share</Button>,
48
+ },
49
+ };
50
+
33
51
  /** For use under a shell/plugin that already renders its own `<h1>` — e.g.
34
52
  * Console's per-page headers sitting below `<h1>Console</h1>`. Visually
35
53
  * identical to the default story; only the rendered tag changes. */
@@ -40,3 +58,38 @@ export const NestedHeadingLevel: Story = {
40
58
  headingLevel: 2,
41
59
  },
42
60
  };
61
+
62
+ /** Below the mobile breakpoint: description drops out of the compact row
63
+ * (generalized from Kanban's own hand-rolled MobileBoardHeader, which has
64
+ * no subtitle handling either), action stays — same content, adapts itself
65
+ * if it needs to look different at this width. */
66
+ export const Mobile: Story = {
67
+ parameters: { viewport: { defaultViewport: 'mobile1' } },
68
+ args: {
69
+ title: 'Users',
70
+ description: 'Manage who has access to this instance.',
71
+ action: <Button size="sm">Invite user</Button>,
72
+ },
73
+ };
74
+
75
+ /** onBack shows a back button to the left of the title — mobile only, no
76
+ * web equivalent. */
77
+ export const MobileWithBack: Story = {
78
+ parameters: { viewport: { defaultViewport: 'mobile1' } },
79
+ args: {
80
+ title: 'Sv Wallet',
81
+ onBack: () => undefined,
82
+ },
83
+ };
84
+
85
+ /** onMenuClick shows a vertical-ellipsis trigger at the far right — mobile
86
+ * only, no web equivalent. Typically opens a drawer/menu; PageHeader has no
87
+ * opinion on what it contains. */
88
+ export const MobileWithMenu: Story = {
89
+ parameters: { viewport: { defaultViewport: 'mobile1' } },
90
+ args: {
91
+ title: 'Sovereign Platform',
92
+ onBack: () => undefined,
93
+ onMenuClick: () => undefined,
94
+ },
95
+ };
@@ -0,0 +1,76 @@
1
+ import { useState } from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react-vite';
3
+ import { StepStrip, type StepStripItem } from '../components/StepStrip/StepStrip';
4
+
5
+ const meta: Meta<typeof StepStrip> = {
6
+ title: 'Components/StepStrip',
7
+ component: StepStrip,
8
+ parameters: { layout: 'padded' },
9
+ tags: ['autodocs'],
10
+ };
11
+
12
+ export default meta;
13
+ type Story = StoryObj<typeof StepStrip>;
14
+
15
+ interface Stop extends StepStripItem {
16
+ label: string;
17
+ sublabel: string;
18
+ }
19
+
20
+ const STOPS: Stop[] = [
21
+ { id: 'lisbon', label: 'Lisbon', sublabel: 'Aug 26 – 28 · 2 days' },
22
+ { id: 'porto', label: 'Porto', sublabel: 'Aug 28 – 30 · 2 days' },
23
+ ];
24
+
25
+ const chipStyle = (isActive: boolean) => ({
26
+ boxSizing: 'border-box' as const,
27
+ minWidth: '160px',
28
+ padding: 'var(--sv-space-3) var(--sv-space-4)',
29
+ borderRadius: 'var(--sv-radius-lg)',
30
+ border: `1px solid ${isActive ? 'var(--sv-color-text-primary)' : 'var(--sv-color-border)'}`,
31
+ background: isActive ? 'var(--sv-color-text-primary)' : 'var(--sv-color-surface-raised)',
32
+ color: isActive ? 'var(--sv-color-surface)' : 'var(--sv-color-text-primary)',
33
+ cursor: 'pointer',
34
+ textAlign: 'left' as const,
35
+ });
36
+
37
+ function StopStripDemo() {
38
+ const [activeId, setActiveId] = useState<string>('lisbon');
39
+
40
+ return (
41
+ <StepStrip
42
+ items={STOPS}
43
+ activeId={activeId}
44
+ aria-label="Trip stops"
45
+ onAdd={() => alert('Add a stop')}
46
+ addLabel="Add a stop"
47
+ renderItem={(stop, { isActive }) => (
48
+ <button type="button" style={chipStyle(isActive)} onClick={() => setActiveId(stop.id)}>
49
+ <div style={{ fontWeight: 600, fontSize: 'var(--sv-font-size-sm)' }}>{stop.label}</div>
50
+ <div style={{ fontSize: 'var(--sv-font-size-xs)', opacity: 0.8 }}>{stop.sublabel}</div>
51
+ </button>
52
+ )}
53
+ />
54
+ );
55
+ }
56
+
57
+ export const Default: Story = {
58
+ render: () => <StopStripDemo />,
59
+ name: 'Stop strip (click to select, trailing add chip)',
60
+ };
61
+
62
+ export const NoAddAffordance: Story = {
63
+ render: () => (
64
+ <StepStrip
65
+ items={STOPS}
66
+ activeId="porto"
67
+ aria-label="Trip stops"
68
+ renderItem={(stop, { isActive }) => (
69
+ <div style={chipStyle(isActive)}>
70
+ <div style={{ fontWeight: 600, fontSize: 'var(--sv-font-size-sm)' }}>{stop.label}</div>
71
+ <div style={{ fontSize: 'var(--sv-font-size-xs)', opacity: 0.8 }}>{stop.sublabel}</div>
72
+ </div>
73
+ )}
74
+ />
75
+ ),
76
+ };
@@ -234,6 +234,40 @@ function DotsStandaloneDemo() {
234
234
  );
235
235
  }
236
236
 
237
+ // Matches the real scenario `density="compact"` was added for: a many-list
238
+ // mobile task manager whose default-spacing dot row reads as long/cramped in
239
+ // a 375px viewport — see docs/design-system.md's carousel section.
240
+ const MANY_LABELS = [
241
+ 'Lists',
242
+ 'Starred',
243
+ 'List 1',
244
+ 'Groceries',
245
+ 'Work',
246
+ 'List 2',
247
+ 'List 3',
248
+ 'List 4',
249
+ 'List 5',
250
+ 'List 6',
251
+ 'List 7',
252
+ 'List 8',
253
+ ];
254
+
255
+ function DotsManyDemo({ density }: { density?: 'default' | 'compact' }) {
256
+ const [activeIndex, setActiveIndex] = useState(2);
257
+ return (
258
+ <div style={{ maxWidth: 375, margin: '0 auto' }}>
259
+ <SwipableMobileCarouselDots
260
+ count={MANY_LABELS.length}
261
+ activeIndex={activeIndex}
262
+ onJump={setActiveIndex}
263
+ labels={MANY_LABELS}
264
+ aria-label="Task lists"
265
+ density={density}
266
+ />
267
+ </div>
268
+ );
269
+ }
270
+
237
271
  const meta = {
238
272
  title: 'Components/SwipableMobileCarousel',
239
273
  component: SwipableMobileCarousel,
@@ -302,3 +336,27 @@ export const DotsStandalone: Story = {
302
336
  },
303
337
  },
304
338
  };
339
+
340
+ export const DotsManyDefault: Story = {
341
+ render: () => <DotsManyDemo />,
342
+ parameters: {
343
+ docs: {
344
+ description: {
345
+ story:
346
+ 'Default density with 12 slides in a 375px viewport — the row reads as long/cramped once an instance has more than a handful of lists. Compare to DotsManyCompact.',
347
+ },
348
+ },
349
+ },
350
+ };
351
+
352
+ export const DotsManyCompact: Story = {
353
+ render: () => <DotsManyDemo density="compact" />,
354
+ parameters: {
355
+ docs: {
356
+ description: {
357
+ story:
358
+ 'density="compact" halves the gap between dots and shrinks each dot\'s own hit target from 20px to 16px — the visible 6px dot itself is unchanged; only its surrounding tap-target box shrinks. Opt-in: every existing consumer\'s spacing is unchanged unless it passes this prop.',
359
+ },
360
+ },
361
+ },
362
+ };