@adia-ai/web-components 0.0.29 → 0.0.34

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 (295) hide show
  1. package/components/accordion/accordion.a2ui.json +1 -1
  2. package/components/accordion/accordion.js +6 -6
  3. package/components/accordion/accordion.yaml +1 -1
  4. package/components/action-list/action-list.a2ui.json +1 -1
  5. package/components/action-list/action-list.js +6 -6
  6. package/components/action-list/action-list.yaml +1 -1
  7. package/components/agent-artifact/agent-artifact.a2ui.json +1 -1
  8. package/components/agent-artifact/agent-artifact.js +4 -4
  9. package/components/agent-artifact/agent-artifact.yaml +1 -1
  10. package/components/agent-feedback-bar/agent-feedback-bar.a2ui.json +1 -1
  11. package/components/agent-feedback-bar/agent-feedback-bar.js +4 -4
  12. package/components/agent-feedback-bar/agent-feedback-bar.yaml +1 -1
  13. package/components/agent-questions/agent-questions.a2ui.json +1 -1
  14. package/components/agent-questions/agent-questions.js +4 -4
  15. package/components/agent-questions/agent-questions.yaml +1 -1
  16. package/components/agent-reasoning/agent-reasoning.a2ui.json +3 -3
  17. package/components/agent-reasoning/agent-reasoning.js +4 -4
  18. package/components/agent-reasoning/agent-reasoning.yaml +3 -3
  19. package/components/agent-suggestions/agent-suggestions.a2ui.json +1 -1
  20. package/components/agent-suggestions/agent-suggestions.js +4 -4
  21. package/components/agent-suggestions/agent-suggestions.yaml +1 -1
  22. package/components/agent-trace/agent-trace.a2ui.json +1 -1
  23. package/components/agent-trace/agent-trace.js +4 -4
  24. package/components/agent-trace/agent-trace.yaml +1 -1
  25. package/components/alert/alert.a2ui.json +1 -1
  26. package/components/alert/alert.js +4 -4
  27. package/components/alert/alert.yaml +1 -1
  28. package/components/aside/aside.a2ui.json +1 -1
  29. package/components/aside/aside.yaml +1 -1
  30. package/components/avatar/avatar.a2ui.json +1 -1
  31. package/components/avatar/avatar.js +8 -8
  32. package/components/avatar/avatar.yaml +1 -1
  33. package/components/badge/badge.a2ui.json +1 -1
  34. package/components/badge/badge.js +4 -4
  35. package/components/badge/badge.yaml +1 -1
  36. package/components/block/block.a2ui.json +1 -1
  37. package/components/block/block.js +4 -4
  38. package/components/block/block.yaml +1 -1
  39. package/components/breadcrumb/breadcrumb.a2ui.json +1 -1
  40. package/components/breadcrumb/breadcrumb.js +4 -4
  41. package/components/breadcrumb/breadcrumb.yaml +1 -1
  42. package/components/button/button.a2ui.json +1 -1
  43. package/components/button/button.js +4 -4
  44. package/components/button/button.yaml +1 -1
  45. package/components/calendar-picker/calendar-picker.a2ui.json +1 -1
  46. package/components/calendar-picker/calendar-picker.js +6 -6
  47. package/components/calendar-picker/calendar-picker.yaml +1 -1
  48. package/components/canvas/canvas.a2ui.json +1 -1
  49. package/components/canvas/canvas.js +4 -4
  50. package/components/canvas/canvas.yaml +1 -1
  51. package/components/card/card.a2ui.json +1 -1
  52. package/components/card/card.js +4 -4
  53. package/components/card/card.yaml +1 -1
  54. package/components/chart/chart.a2ui.json +1 -1
  55. package/components/chart/chart.js +5 -5
  56. package/components/chart/chart.yaml +1 -1
  57. package/components/chart-legend/chart-legend.a2ui.json +1 -1
  58. package/components/chart-legend/chart-legend.js +7 -7
  59. package/components/chart-legend/chart-legend.yaml +1 -1
  60. package/components/{chat → chat-thread}/chat-input.js +5 -5
  61. package/components/{chat/chat.a2ui.json → chat-thread/chat-thread.a2ui.json} +6 -6
  62. package/components/{chat/chat.css → chat-thread/chat-thread.css} +2 -2
  63. package/components/{chat/chat.js → chat-thread/chat-thread.js} +7 -7
  64. package/components/{chat/chat.yaml → chat-thread/chat-thread.yaml} +4 -4
  65. package/components/check/check.a2ui.json +1 -1
  66. package/components/check/check.js +5 -5
  67. package/components/check/check.yaml +1 -1
  68. package/components/code/code.a2ui.json +1 -1
  69. package/components/code/code.js +4 -4
  70. package/components/code/code.yaml +1 -1
  71. package/components/col/col.a2ui.json +1 -1
  72. package/components/col/col.js +4 -4
  73. package/components/col/col.yaml +1 -1
  74. package/components/color-picker/color-picker.a2ui.json +1 -1
  75. package/components/color-picker/color-picker.js +6 -6
  76. package/components/color-picker/color-picker.yaml +1 -1
  77. package/components/command/command.a2ui.json +1 -1
  78. package/components/command/command.js +5 -5
  79. package/components/command/command.yaml +1 -1
  80. package/components/description-list/description-list.a2ui.json +1 -1
  81. package/components/description-list/description-list.js +4 -4
  82. package/components/description-list/description-list.yaml +1 -1
  83. package/components/divider/divider.a2ui.json +1 -1
  84. package/components/divider/divider.js +4 -4
  85. package/components/divider/divider.yaml +1 -1
  86. package/components/drawer/drawer.a2ui.json +1 -1
  87. package/components/drawer/drawer.js +4 -4
  88. package/components/drawer/drawer.yaml +1 -1
  89. package/components/embed/embed.a2ui.json +1 -1
  90. package/components/embed/embed.js +4 -4
  91. package/components/embed/embed.yaml +1 -1
  92. package/components/empty-state/empty-state.a2ui.json +1 -1
  93. package/components/empty-state/empty-state.js +4 -4
  94. package/components/empty-state/empty-state.yaml +1 -1
  95. package/components/feed/feed-item.yaml +2 -2
  96. package/components/feed/feed.a2ui.json +2 -2
  97. package/components/feed/feed.css +12 -3
  98. package/components/feed/feed.js +22 -22
  99. package/components/feed/feed.yaml +2 -2
  100. package/components/field/field.a2ui.json +1 -1
  101. package/components/field/field.js +10 -10
  102. package/components/field/field.yaml +2 -2
  103. package/components/footer/footer.a2ui.json +1 -1
  104. package/components/footer/footer.yaml +1 -1
  105. package/components/grid/grid.a2ui.json +1 -1
  106. package/components/grid/grid.js +4 -4
  107. package/components/grid/grid.yaml +1 -1
  108. package/components/header/header.a2ui.json +1 -1
  109. package/components/header/header.yaml +1 -1
  110. package/components/heatmap/heatmap.a2ui.json +1 -1
  111. package/components/heatmap/heatmap.js +4 -4
  112. package/components/heatmap/heatmap.yaml +1 -1
  113. package/components/icon/icon.a2ui.json +1 -1
  114. package/components/icon/icon.js +4 -4
  115. package/components/icon/icon.yaml +1 -1
  116. package/components/image/image.a2ui.json +1 -1
  117. package/components/image/image.js +4 -4
  118. package/components/image/image.yaml +1 -1
  119. package/components/index.js +89 -85
  120. package/components/input/input.a2ui.json +1 -1
  121. package/components/input/input.js +7 -7
  122. package/components/input/input.yaml +1 -1
  123. package/components/inspector/inspector.a2ui.json +1 -1
  124. package/components/inspector/inspector.js +4 -4
  125. package/components/inspector/inspector.yaml +1 -1
  126. package/components/kbd/kbd.a2ui.json +1 -1
  127. package/components/kbd/kbd.js +4 -4
  128. package/components/kbd/kbd.yaml +1 -1
  129. package/components/list/list.a2ui.json +1 -1
  130. package/components/list/list.js +6 -6
  131. package/components/list/list.yaml +1 -1
  132. package/components/menu/menu.a2ui.json +1 -1
  133. package/components/menu/menu.js +8 -8
  134. package/components/menu/menu.yaml +1 -1
  135. package/components/modal/modal.a2ui.json +1 -1
  136. package/components/modal/modal.js +4 -4
  137. package/components/modal/modal.yaml +1 -1
  138. package/components/nav/nav.a2ui.json +98 -0
  139. package/components/nav/nav.css +133 -0
  140. package/components/nav/nav.js +140 -0
  141. package/components/nav/nav.test.js +428 -0
  142. package/components/nav/nav.yaml +114 -0
  143. package/components/nav-group/nav-group.a2ui.json +100 -0
  144. package/components/nav-group/nav-group.css +317 -0
  145. package/components/nav-group/nav-group.js +142 -0
  146. package/components/nav-group/nav-group.yaml +69 -0
  147. package/components/nav-item/nav-item.a2ui.json +106 -0
  148. package/components/nav-item/nav-item.css +194 -0
  149. package/components/nav-item/nav-item.js +76 -0
  150. package/components/nav-item/nav-item.yaml +73 -0
  151. package/components/noodles/noodles.a2ui.json +1 -1
  152. package/components/noodles/noodles.js +4 -4
  153. package/components/noodles/noodles.yaml +1 -1
  154. package/components/option-card/option-card.a2ui.json +1 -1
  155. package/components/option-card/option-card.js +6 -6
  156. package/components/option-card/option-card.yaml +1 -1
  157. package/components/otp-input/otp-input.a2ui.json +1 -1
  158. package/components/otp-input/otp-input.js +5 -5
  159. package/components/otp-input/otp-input.yaml +1 -1
  160. package/components/page/page.a2ui.json +3 -3
  161. package/components/page/page.js +4 -4
  162. package/components/page/page.yaml +3 -3
  163. package/components/pagination/pagination.a2ui.json +1 -1
  164. package/components/pagination/pagination.js +4 -4
  165. package/components/pagination/pagination.yaml +1 -1
  166. package/components/pane/pane.a2ui.json +1 -1
  167. package/components/pane/pane.js +4 -4
  168. package/components/pane/pane.yaml +1 -1
  169. package/components/pipeline-status/pipeline-status.a2ui.json +1 -1
  170. package/components/pipeline-status/pipeline-status.js +4 -4
  171. package/components/pipeline-status/pipeline-status.yaml +1 -1
  172. package/components/popover/popover.a2ui.json +1 -1
  173. package/components/popover/popover.js +4 -4
  174. package/components/popover/popover.yaml +1 -1
  175. package/components/progress/progress.a2ui.json +1 -1
  176. package/components/progress/progress.js +4 -4
  177. package/components/progress/progress.yaml +1 -1
  178. package/components/progress-row/progress-row.a2ui.json +1 -1
  179. package/components/progress-row/progress-row.js +4 -4
  180. package/components/progress-row/progress-row.yaml +1 -1
  181. package/components/radio/radio.a2ui.json +1 -1
  182. package/components/radio/radio.js +5 -5
  183. package/components/radio/radio.yaml +1 -1
  184. package/components/range/range.a2ui.json +1 -1
  185. package/components/range/range.js +7 -7
  186. package/components/range/range.yaml +1 -1
  187. package/components/rating/rating.a2ui.json +1 -1
  188. package/components/rating/rating.js +6 -6
  189. package/components/rating/rating.yaml +1 -1
  190. package/components/richtext/richtext.a2ui.json +1 -1
  191. package/components/richtext/richtext.js +4 -4
  192. package/components/richtext/richtext.yaml +1 -1
  193. package/components/row/row.a2ui.json +1 -1
  194. package/components/row/row.js +4 -4
  195. package/components/row/row.yaml +1 -1
  196. package/components/search/search.a2ui.json +1 -1
  197. package/components/search/search.js +5 -5
  198. package/components/search/search.yaml +1 -1
  199. package/components/section/section.a2ui.json +1 -1
  200. package/components/section/section.yaml +1 -1
  201. package/components/segment/segment.a2ui.json +1 -1
  202. package/components/segment/segment.js +4 -4
  203. package/components/segment/segment.yaml +1 -1
  204. package/components/segmented/segmented.a2ui.json +1 -1
  205. package/components/segmented/segmented.css +6 -0
  206. package/components/segmented/segmented.js +7 -7
  207. package/components/segmented/segmented.yaml +1 -1
  208. package/components/select/select.a2ui.json +1 -1
  209. package/components/select/select.js +5 -5
  210. package/components/select/select.yaml +1 -1
  211. package/components/skeleton/skeleton.a2ui.json +1 -1
  212. package/components/skeleton/skeleton.js +4 -4
  213. package/components/skeleton/skeleton.yaml +1 -1
  214. package/components/slider/slider.a2ui.json +1 -1
  215. package/components/slider/slider.js +7 -7
  216. package/components/slider/slider.yaml +1 -1
  217. package/components/stack/stack.a2ui.json +1 -1
  218. package/components/stack/stack.js +4 -4
  219. package/components/stack/stack.yaml +1 -1
  220. package/components/stat/stat.a2ui.json +1 -1
  221. package/components/stat/stat.js +4 -4
  222. package/components/stat/stat.yaml +1 -1
  223. package/components/step-progress/step-progress.a2ui.json +111 -0
  224. package/components/step-progress/step-progress.css +61 -0
  225. package/components/step-progress/step-progress.js +88 -0
  226. package/components/step-progress/step-progress.test.js +118 -0
  227. package/components/step-progress/step-progress.yaml +93 -0
  228. package/components/stepper/stepper.a2ui.json +1 -1
  229. package/components/stepper/stepper.js +6 -6
  230. package/components/stepper/stepper.yaml +1 -1
  231. package/components/stream/stream.a2ui.json +1 -1
  232. package/components/stream/stream.js +4 -4
  233. package/components/stream/stream.yaml +1 -1
  234. package/components/swatch/swatch.a2ui.json +1 -1
  235. package/components/swatch/swatch.js +4 -4
  236. package/components/swatch/swatch.yaml +1 -1
  237. package/components/swiper/swiper.a2ui.json +1 -1
  238. package/components/swiper/swiper.js +4 -4
  239. package/components/swiper/swiper.yaml +1 -1
  240. package/components/switch/switch.a2ui.json +1 -1
  241. package/components/switch/switch.js +5 -5
  242. package/components/switch/switch.yaml +1 -1
  243. package/components/table/table.a2ui.json +1 -1
  244. package/components/table/table.js +4 -4
  245. package/components/table/table.yaml +1 -1
  246. package/components/table-toolbar/table-toolbar.a2ui.json +1 -1
  247. package/components/table-toolbar/table-toolbar.js +4 -4
  248. package/components/table-toolbar/table-toolbar.yaml +1 -1
  249. package/components/tabs/tab.js +4 -4
  250. package/components/tabs/tabs.a2ui.json +1 -1
  251. package/components/tabs/tabs.js +5 -5
  252. package/components/tabs/tabs.yaml +1 -1
  253. package/components/tag/tag.a2ui.json +1 -1
  254. package/components/tag/tag.js +4 -4
  255. package/components/tag/tag.yaml +1 -1
  256. package/components/text/text.a2ui.json +1 -1
  257. package/components/text/text.js +4 -4
  258. package/components/text/text.yaml +1 -1
  259. package/components/textarea/textarea.a2ui.json +1 -1
  260. package/components/textarea/textarea.js +5 -5
  261. package/components/textarea/textarea.yaml +1 -1
  262. package/components/timeline/timeline.a2ui.json +1 -1
  263. package/components/timeline/timeline.js +6 -6
  264. package/components/timeline/timeline.yaml +1 -1
  265. package/components/toast/toast.a2ui.json +1 -1
  266. package/components/toast/toast.js +18 -18
  267. package/components/toast/toast.yaml +1 -1
  268. package/components/toggle-group/toggle-group.a2ui.json +1 -1
  269. package/components/toggle-group/toggle-group.js +6 -6
  270. package/components/toggle-group/toggle-group.yaml +1 -1
  271. package/components/toolbar/toolbar.a2ui.json +1 -1
  272. package/components/toolbar/toolbar.js +6 -6
  273. package/components/toolbar/toolbar.yaml +1 -1
  274. package/components/tooltip/tooltip.a2ui.json +1 -1
  275. package/components/tooltip/tooltip.js +7 -7
  276. package/components/tooltip/tooltip.yaml +1 -1
  277. package/components/tree/tree.a2ui.json +1 -1
  278. package/components/tree/tree.js +6 -6
  279. package/components/tree/tree.yaml +1 -1
  280. package/components/upload/upload.a2ui.json +1 -1
  281. package/components/upload/upload.js +6 -6
  282. package/components/upload/upload.yaml +1 -1
  283. package/core/element.js +4 -4
  284. package/core/element.test.js +18 -18
  285. package/core/form.js +9 -9
  286. package/core/index.js +2 -2
  287. package/core/provider.js +7 -7
  288. package/core/template.js +1 -1
  289. package/index.css +1 -1
  290. package/index.js +10 -8
  291. package/package.json +1 -1
  292. package/styles/components.css +11 -6
  293. package/styles/resets.css +1 -1
  294. package/traits/define.js +2 -2
  295. /package/components/{chat → chat-thread}/chat-input.css +0 -0
@@ -0,0 +1,98 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://adiaui.dev/a2ui/v0_9/components/Nav.json",
4
+ "title": "Nav",
5
+ "description": "Navigation rail. Consolidates the prior `app-nav-ui` + `section-nav-ui`\npair per ADR-0015 § Nav consolidation. [variant] drives visual\ntreatment; behavior is unified.\n\nDefault variant (\"primary\") is the app-sidebar nav: ResizeObserver\ncollapses to icon-only below 96px, groups open a popover when\ncollapsed. [variant=\"section\"] is a subnav rail with quieter chrome\nand a [heading] kicker rendered via CSS.\n",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "common_types.json#/$defs/ComponentCommon"
10
+ },
11
+ {
12
+ "$ref": "common_types.json#/$defs/CatalogComponentCommon"
13
+ }
14
+ ],
15
+ "properties": {
16
+ "collapsed": {
17
+ "description": "Primary-variant only. Force icon-only collapse regardless of viewport width.",
18
+ "type": "boolean",
19
+ "default": false
20
+ },
21
+ "component": {
22
+ "const": "Nav"
23
+ },
24
+ "divider": {
25
+ "description": "Auto-place dividers between adjacent groups + items.",
26
+ "type": "boolean",
27
+ "default": false
28
+ },
29
+ "heading": {
30
+ "description": "Optional kicker label. Section variant renders it via ::before; primary uses it as aria-label only.",
31
+ "type": "string",
32
+ "default": ""
33
+ },
34
+ "variant": {
35
+ "description": "Visual treatment. primary = app sidebar; section = subnav rail.",
36
+ "type": "string",
37
+ "enum": [
38
+ "primary",
39
+ "section"
40
+ ],
41
+ "default": "primary"
42
+ }
43
+ },
44
+ "required": [
45
+ "component"
46
+ ],
47
+ "unevaluatedProperties": false,
48
+ "x-adiaui": {
49
+ "anti_patterns": [],
50
+ "category": "layout",
51
+ "events": {
52
+ "nav-select": {
53
+ "description": "Bubbles from <nav-item-ui> children when one is selected. Detail: { item, text, value }."
54
+ }
55
+ },
56
+ "examples": [
57
+ {
58
+ "description": "App sidebar nav with groups + items.",
59
+ "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Nav\",\n \"children\": [\"g1\", \"i1\"]\n },\n {\n \"id\": \"g1\",\n \"component\": \"NavGroup\",\n \"text\": \"Settings\",\n \"icon\": \"gear\",\n \"children\": [\"g1i1\"]\n },\n {\n \"id\": \"g1i1\",\n \"component\": \"NavItem\",\n \"text\": \"General\",\n \"value\": \"/settings/general\"\n },\n {\n \"id\": \"i1\",\n \"component\": \"NavItem\",\n \"text\": \"Profile\",\n \"icon\": \"user\",\n \"value\": \"/profile\"\n }\n]",
60
+ "name": "primary"
61
+ },
62
+ {
63
+ "description": "Subnav rail with heading.",
64
+ "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Nav\",\n \"variant\": \"section\",\n \"heading\": \"On this page\",\n \"children\": [\"i1\", \"i2\"]\n },\n {\n \"id\": \"i1\",\n \"component\": \"NavItem\",\n \"text\": \"Overview\",\n \"value\": \"#overview\"\n },\n {\n \"id\": \"i2\",\n \"component\": \"NavItem\",\n \"text\": \"API\",\n \"value\": \"#api\"\n }\n]",
65
+ "name": "section"
66
+ }
67
+ ],
68
+ "keywords": [
69
+ "nav",
70
+ "navigation",
71
+ "sidebar",
72
+ "menu",
73
+ "links"
74
+ ],
75
+ "name": "UINav",
76
+ "related": [],
77
+ "slots": {
78
+ "default": {
79
+ "description": "Primary slot — accepts <nav-group-ui> + <nav-item-ui> children, plus <hr data-nav-divider> for hand-placed dividers."
80
+ }
81
+ },
82
+ "states": [
83
+ {
84
+ "description": "Default, not collapsed.",
85
+ "name": "idle"
86
+ },
87
+ {
88
+ "description": "Primary variant when [collapsed] or container width <= 96px.",
89
+ "name": "collapsed"
90
+ }
91
+ ],
92
+ "synonyms": {},
93
+ "tag": "nav-ui",
94
+ "tokens": {},
95
+ "traits": [],
96
+ "version": 1
97
+ }
98
+ }
@@ -0,0 +1,133 @@
1
+ /* ═══════════════════════════════════════════
2
+ nav-ui — Consolidated navigation rail
3
+ Variants: primary (default, app sidebar) / section (subnav rail)
4
+ ═══════════════════════════════════════════ */
5
+
6
+ @scope (nav-ui) {
7
+ :where(:scope) {
8
+ /* Shared */
9
+ --nav-gap: var(--a-space-1);
10
+ --nav-px: var(--a-space-2);
11
+ --nav-py: var(--a-space-2);
12
+ --nav-font-size: var(--a-ui-size);
13
+ --nav-bg: transparent;
14
+ --nav-divider-bg: var(--a-border-subtle);
15
+ --nav-divider-my: var(--a-space-1);
16
+
17
+ /* Primary-variant collapse */
18
+ --nav-width: auto;
19
+ --nav-width-collapsed: 48px;
20
+ --nav-duration: var(--a-duration-base, 200ms);
21
+ --nav-easing: var(--a-easing, ease);
22
+
23
+ /* Group-heading kicker */
24
+ --nav-label-fg: var(--a-fg-muted);
25
+ --nav-label-font-size: var(--a-kicker-sm);
26
+ --nav-label-weight: var(--a-weight-medium);
27
+ --nav-label-px: var(--a-space-2);
28
+ --nav-label-py: var(--a-space-3) var(--a-space-1);
29
+ }
30
+
31
+ :scope {
32
+ box-sizing: border-box;
33
+ display: flex;
34
+ flex-direction: column;
35
+ gap: var(--nav-gap);
36
+ padding: var(--nav-py) var(--nav-px);
37
+ background: var(--nav-bg);
38
+ font-size: var(--nav-font-size);
39
+ min-width: 0;
40
+ overflow-y: auto;
41
+ overflow-x: hidden;
42
+ scrollbar-width: none;
43
+ }
44
+
45
+ :scope::-webkit-scrollbar { display: none; }
46
+
47
+ /* ── Primary variant (default) ── */
48
+
49
+ :scope:not([variant="section"]) {
50
+ width: var(--nav-width);
51
+ transition: width var(--nav-duration) var(--nav-easing);
52
+ }
53
+
54
+ :scope:not([variant="section"])[collapsed] {
55
+ width: var(--nav-width-collapsed);
56
+ --nav-px: var(--a-space-1);
57
+ }
58
+
59
+ /* Container-query collapse — when nav lives in a sidebar that goes
60
+ narrow, behave as if [collapsed] regardless of attribute. */
61
+ @container sidebar (max-width: 96px) {
62
+ :scope:not([variant="section"]) {
63
+ --nav-px: var(--a-space-1);
64
+ }
65
+ }
66
+
67
+ /* Group label kicker (group header text inside nav-group) */
68
+ [data-nav-label] {
69
+ display: block;
70
+ padding: var(--nav-label-py) var(--nav-label-px);
71
+ font-size: var(--nav-label-font-size);
72
+ font-weight: var(--nav-label-weight);
73
+ color: var(--nav-label-fg);
74
+ text-transform: uppercase;
75
+ letter-spacing: 0.06em;
76
+ white-space: nowrap;
77
+ overflow: hidden;
78
+ }
79
+
80
+ /* Hand-placed dividers */
81
+ [data-nav-divider] {
82
+ border: none;
83
+ height: 1px;
84
+ background: var(--nav-divider-bg);
85
+ margin: var(--nav-divider-my) 0;
86
+ }
87
+
88
+ /* Auto-dividers between groups when [divider] set */
89
+ :scope[divider] > nav-group-ui + nav-group-ui,
90
+ :scope[divider] > nav-item-ui + nav-group-ui,
91
+ :scope[divider] > nav-group-ui + nav-item-ui {
92
+ border-top: 1px solid var(--nav-divider-bg);
93
+ margin-top: var(--nav-divider-my);
94
+ padding-top: var(--nav-divider-my);
95
+ }
96
+
97
+ /* Spacer — pushes items below to bottom */
98
+ [data-nav-spacer] { flex: 1; }
99
+
100
+ /* Collapsed (primary): hide labels + dividers */
101
+ :scope:not([variant="section"])[collapsed] [data-nav-label],
102
+ :scope:not([variant="section"])[collapsed] [data-nav-divider] {
103
+ display: none;
104
+ }
105
+
106
+ @container sidebar (max-width: 96px) {
107
+ :scope:not([variant="section"]) [data-nav-label],
108
+ :scope:not([variant="section"]) [data-nav-divider] {
109
+ display: none;
110
+ }
111
+ }
112
+
113
+ /* ── Section variant ── */
114
+
115
+ :scope[variant="section"] {
116
+ --nav-px: 0;
117
+ --nav-py: 0;
118
+ }
119
+
120
+ /* Heading rendered from [heading] attribute on section variant. */
121
+ :scope[variant="section"][heading]:not([heading=""])::before {
122
+ content: attr(heading);
123
+ display: block;
124
+ padding: var(--nav-label-py) var(--nav-label-px);
125
+ font-size: var(--nav-label-font-size);
126
+ font-weight: var(--nav-label-weight);
127
+ color: var(--nav-label-fg);
128
+ text-transform: uppercase;
129
+ letter-spacing: 0.06em;
130
+ white-space: nowrap;
131
+ overflow: hidden;
132
+ }
133
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * <nav-ui> — Navigation rail (consolidated primary + section variants).
3
+ *
4
+ * Consolidates the prior `app-nav-ui` + `section-nav-ui` dual-family
5
+ * per ADR-0015 § Nav consolidation. Variant drives visual treatment;
6
+ * behavior is unified.
7
+ *
8
+ * Variants:
9
+ * primary — default. App sidebar. ResizeObserver collapses to icon-only
10
+ * below 96px container width or when [collapsed] set; groups
11
+ * open a popover with their children when collapsed.
12
+ * section — subnav rail. Quieter chrome; no collapse behavior.
13
+ * Optional [heading] attribute renders an uppercase label
14
+ * via ::before pseudo (CSS-only).
15
+ *
16
+ * Structure:
17
+ * <nav-ui>
18
+ * <nav-group-ui icon="gear" text="Settings" badge="3">
19
+ * <nav-item-ui text="General" value="/settings/general"></nav-item-ui>
20
+ * </nav-group-ui>
21
+ * <nav-item-ui icon="user" text="Profile" value="/profile"></nav-item-ui>
22
+ * </nav-ui>
23
+ *
24
+ * <nav-ui variant="section" heading="On this page">
25
+ * <nav-item-ui text="Overview" value="#overview"></nav-item-ui>
26
+ * </nav-ui>
27
+ *
28
+ * Event: nav-select (bubbles from nav-item-ui). detail: { item, text, value }
29
+ */
30
+
31
+ import { UIElement } from '../../core/element.js';
32
+
33
+ class UINav extends UIElement {
34
+ static properties = {
35
+ variant: { type: String, default: 'primary', reflect: true },
36
+ collapsed: { type: Boolean, default: false, reflect: true },
37
+ divider: { type: Boolean, default: false, reflect: true },
38
+ heading: { type: String, default: '', reflect: true },
39
+ };
40
+
41
+ static template = () => null;
42
+
43
+ #ro = null;
44
+
45
+ connected() {
46
+ this.setAttribute('role', 'navigation');
47
+ if (this.heading) this.setAttribute('aria-label', this.heading);
48
+ this.addEventListener('click', this.#onClick);
49
+
50
+ // ResizeObserver only meaningful for primary variant — section is static.
51
+ if (this.variant !== 'section') {
52
+ this.#ro = new ResizeObserver(() => this.#updateTooltips());
53
+ this.#ro.observe(this);
54
+ }
55
+ }
56
+
57
+ render() {
58
+ if (this.heading) this.setAttribute('aria-label', this.heading);
59
+ else this.removeAttribute('aria-label');
60
+ if (this.variant !== 'section') this.#updateTooltips();
61
+ }
62
+
63
+ #updateTooltips() {
64
+ const isCollapsed = this.collapsed || this.getBoundingClientRect().width <= 96;
65
+ for (const group of this.querySelectorAll(':scope > nav-group-ui')) {
66
+ const header = group.querySelector(':scope > [slot="header"]');
67
+ if (header) header.title = isCollapsed ? group.text : '';
68
+ }
69
+ for (const item of this.querySelectorAll(':scope > nav-item-ui')) {
70
+ item.title = isCollapsed ? item.text : '';
71
+ }
72
+ }
73
+
74
+ get selectedItem() {
75
+ return this.querySelector('nav-item-ui[selected]');
76
+ }
77
+
78
+ select(item) {
79
+ const prev = this.selectedItem;
80
+ if (prev && prev !== item) prev.removeAttribute('selected');
81
+ if (item) {
82
+ item.setAttribute('selected', '');
83
+ this.dispatchEvent(new CustomEvent('nav-select', {
84
+ bubbles: true,
85
+ detail: { item, text: item.text, value: item.value },
86
+ }));
87
+ }
88
+ }
89
+
90
+ toggle() {
91
+ if (this.variant === 'section') return; // no-op for section variant
92
+ this.collapsed = !this.collapsed;
93
+ }
94
+
95
+ #onClick = (e) => {
96
+ const item = e.target.closest('nav-item-ui');
97
+ if (item && this.contains(item)) {
98
+ if (item.disabled) return;
99
+ this.select(item);
100
+ this.#flushHoverState();
101
+ return;
102
+ }
103
+
104
+ // Group expand/popover — primary variant only.
105
+ if (this.variant === 'section') return;
106
+
107
+ const group = e.target.closest('nav-group-ui');
108
+ if (group && this.contains(group)) {
109
+ const isCollapsed = this.collapsed || this.getBoundingClientRect().width <= 96;
110
+ if (isCollapsed) {
111
+ group.showPopover?.();
112
+ } else {
113
+ const header = group.querySelector(':scope > [slot="header"]');
114
+ if (header && (e.target === header || header.contains(e.target))) {
115
+ group.open = !group.open;
116
+ }
117
+ }
118
+ }
119
+ };
120
+
121
+ // Safari macOS leaves :hover stuck on items the cursor passed through
122
+ // when the DOM mutates during click+route navigation. Toggling pointer-
123
+ // events forces re-evaluation of hover state on next paint.
124
+ // See docs/BROWSER-COMPAT.md §3a.
125
+ #flushHoverState() {
126
+ this.style.pointerEvents = 'none';
127
+ requestAnimationFrame(() => {
128
+ this.style.pointerEvents = '';
129
+ });
130
+ }
131
+
132
+ disconnected() {
133
+ this.removeEventListener('click', this.#onClick);
134
+ this.#ro?.disconnect();
135
+ this.#ro = null;
136
+ }
137
+ }
138
+
139
+ customElements.define('nav-ui', UINav);
140
+ export { UINav };