@ds-mo/ui 9.2.1 → 11.0.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 (326) hide show
  1. package/README.md +2 -2
  2. package/dist/.build-stamp +1 -1
  3. package/dist/agent-patterns.json +15 -9
  4. package/dist/agent.json +473 -150
  5. package/dist/angular/boolean-value-accessor.js +3 -3
  6. package/dist/angular/ds-agent-activity.js +3 -3
  7. package/dist/angular/ds-agent-response.js +3 -3
  8. package/dist/angular/ds-agent-source-list.js +3 -3
  9. package/dist/angular/ds-agent-tool-call.js +3 -3
  10. package/dist/angular/ds-app-shell.js +3 -3
  11. package/dist/angular/ds-attachment-list.js +3 -3
  12. package/dist/angular/ds-avatar.js +3 -3
  13. package/dist/angular/ds-badge.js +3 -3
  14. package/dist/angular/ds-bar-nav.js +3 -3
  15. package/dist/angular/ds-button-filled.js +3 -3
  16. package/dist/angular/ds-button-unfilled.js +3 -3
  17. package/dist/angular/ds-card-data-viz-donut.js +3 -3
  18. package/dist/angular/ds-card-setting.d.ts +5 -4
  19. package/dist/angular/ds-card-setting.js +6 -6
  20. package/dist/angular/ds-card-shell-data-viz.js +3 -3
  21. package/dist/angular/ds-chart-bar.js +3 -3
  22. package/dist/angular/ds-chart-donut.js +3 -3
  23. package/dist/angular/ds-chart-legend.js +3 -3
  24. package/dist/angular/ds-chart-line.js +3 -3
  25. package/dist/angular/ds-checkbox.js +3 -3
  26. package/dist/angular/ds-chip.js +3 -3
  27. package/dist/angular/ds-code-block.js +3 -3
  28. package/dist/angular/ds-conversation-list-item.js +3 -3
  29. package/dist/angular/ds-conversation-list-section.js +3 -3
  30. package/dist/angular/ds-conversation-list.js +3 -3
  31. package/dist/angular/ds-divider.js +3 -3
  32. package/dist/angular/ds-empty-state.js +3 -3
  33. package/dist/angular/ds-field.js +3 -3
  34. package/dist/angular/ds-icon.js +3 -3
  35. package/dist/angular/ds-input.js +3 -3
  36. package/dist/angular/ds-loader.js +3 -3
  37. package/dist/angular/ds-markdown.js +3 -3
  38. package/dist/angular/ds-menu.js +3 -3
  39. package/dist/angular/ds-message-bubble.js +3 -3
  40. package/dist/angular/ds-message-composer.js +3 -3
  41. package/dist/angular/ds-message-scroller.js +3 -3
  42. package/dist/angular/ds-message.js +3 -3
  43. package/dist/angular/ds-modal.d.ts +12 -3
  44. package/dist/angular/ds-modal.js +9 -6
  45. package/dist/angular/ds-panel-nav.js +3 -3
  46. package/dist/angular/ds-panel-sub-nav.js +3 -3
  47. package/dist/angular/ds-panel-tool-header.js +3 -3
  48. package/dist/angular/ds-panel-tool-search.js +3 -3
  49. package/dist/angular/ds-panel-tools.js +3 -3
  50. package/dist/angular/ds-radio.js +3 -3
  51. package/dist/angular/ds-select-multi.js +3 -3
  52. package/dist/angular/ds-select.js +3 -3
  53. package/dist/angular/ds-shell-gradient-picker.js +3 -3
  54. package/dist/angular/ds-shell-gradient-swatch.js +3 -3
  55. package/dist/angular/ds-skeleton.js +3 -3
  56. package/dist/angular/ds-slider.js +3 -3
  57. package/dist/angular/ds-swatch-picker.js +3 -3
  58. package/dist/angular/ds-switch.js +3 -3
  59. package/dist/angular/ds-tab-group.js +3 -3
  60. package/dist/angular/ds-tag.js +3 -3
  61. package/dist/angular/ds-text.js +3 -3
  62. package/dist/angular/ds-toast.d.ts +22 -0
  63. package/dist/angular/{ds-banner.js → ds-toast.js} +21 -15
  64. package/dist/angular/ds-tooltip-data-viz.js +3 -3
  65. package/dist/angular/ds-tooltip.js +3 -3
  66. package/dist/angular/ds-typing-indicator.d.ts +12 -0
  67. package/dist/angular/{ds-card.js → ds-typing-indicator.js} +12 -12
  68. package/dist/angular/index.d.ts +1 -1
  69. package/dist/angular/index.js +3 -3
  70. package/dist/angular/proxies.d.ts +2 -2
  71. package/dist/angular/proxies.js +2 -2
  72. package/dist/angular/radio-value-accessor.js +3 -3
  73. package/dist/angular/select-value-accessor.js +3 -3
  74. package/dist/angular/text-value-accessor.js +3 -3
  75. package/dist/angular/value-accessor.js +3 -3
  76. package/dist/components/ds-agent-activity.js +1 -1
  77. package/dist/components/ds-agent-response.js +1 -1
  78. package/dist/components/ds-agent-response.js.map +1 -1
  79. package/dist/components/ds-agent-source-list.js +1 -1
  80. package/dist/components/ds-agent-tool-call.js +1 -1
  81. package/dist/components/ds-app-shell.js +1 -1
  82. package/dist/components/ds-attachment-list.js +1 -1
  83. package/dist/components/ds-avatar.js +1 -1
  84. package/dist/components/ds-avatar.js.map +1 -1
  85. package/dist/components/ds-badge.js +1 -1
  86. package/dist/components/ds-bar-nav.js +1 -1
  87. package/dist/components/ds-bar-nav.js.map +1 -1
  88. package/dist/components/ds-button-filled.js +1 -1
  89. package/dist/components/ds-button-unfilled.js +1 -1
  90. package/dist/components/ds-card-data-viz-donut.js +1 -1
  91. package/dist/components/ds-card-data-viz-donut.js.map +1 -1
  92. package/dist/components/ds-card-setting.js +1 -1
  93. package/dist/components/ds-card-setting.js.map +1 -1
  94. package/dist/components/ds-card-shell-data-viz.js +1 -1
  95. package/dist/components/ds-chart-bar.js +1 -1
  96. package/dist/components/ds-chart-donut.js +1 -1
  97. package/dist/components/ds-chart-legend.js +1 -1
  98. package/dist/components/ds-chart-legend.js.map +1 -1
  99. package/dist/components/ds-chart-line.js +1 -1
  100. package/dist/components/ds-checkbox.js +1 -1
  101. package/dist/components/ds-chip.js +1 -1
  102. package/dist/components/ds-chip.js.map +1 -1
  103. package/dist/components/ds-code-block.js +1 -1
  104. package/dist/components/ds-conversation-list-item.js +1 -1
  105. package/dist/components/ds-conversation-list-item.js.map +1 -1
  106. package/dist/components/ds-conversation-list-section.js +1 -1
  107. package/dist/components/ds-conversation-list.js +1 -1
  108. package/dist/components/ds-divider.js +1 -1
  109. package/dist/components/ds-empty-state.js +1 -1
  110. package/dist/components/ds-field.js +1 -1
  111. package/dist/components/ds-icon.js +1 -1
  112. package/dist/components/ds-input.js +1 -1
  113. package/dist/components/ds-input.js.map +1 -1
  114. package/dist/components/ds-markdown.js +1 -1
  115. package/dist/components/ds-menu.js +1 -1
  116. package/dist/components/ds-message-bubble.js +1 -1
  117. package/dist/components/ds-message-bubble.js.map +1 -1
  118. package/dist/components/ds-message-composer.js +1 -1
  119. package/dist/components/ds-message-composer.js.map +1 -1
  120. package/dist/components/ds-message-scroller.js +1 -1
  121. package/dist/components/ds-message-scroller.js.map +1 -1
  122. package/dist/components/ds-message.js +1 -1
  123. package/dist/components/ds-modal.js +1 -1
  124. package/dist/components/ds-modal.js.map +1 -1
  125. package/dist/components/ds-panel-nav.js +1 -1
  126. package/dist/components/ds-panel-nav.js.map +1 -1
  127. package/dist/components/ds-panel-sub-nav.js +1 -1
  128. package/dist/components/ds-panel-sub-nav.js.map +1 -1
  129. package/dist/components/ds-panel-tool-header.js +1 -1
  130. package/dist/components/ds-panel-tool-search.js +1 -1
  131. package/dist/components/ds-panel-tool-search.js.map +1 -1
  132. package/dist/components/ds-panel-tools.js +1 -1
  133. package/dist/components/ds-panel-tools.js.map +1 -1
  134. package/dist/components/ds-radio.js +1 -1
  135. package/dist/components/ds-radio.js.map +1 -1
  136. package/dist/components/ds-select-multi.js +1 -1
  137. package/dist/components/ds-select-multi.js.map +1 -1
  138. package/dist/components/ds-select.js +1 -1
  139. package/dist/components/ds-select.js.map +1 -1
  140. package/dist/components/ds-shell-gradient-picker.js +1 -1
  141. package/dist/components/ds-shell-gradient-swatch.js +1 -1
  142. package/dist/components/ds-skeleton.js +1 -1
  143. package/dist/components/ds-skeleton.js.map +1 -1
  144. package/dist/components/ds-slider.js +1 -1
  145. package/dist/components/ds-slider.js.map +1 -1
  146. package/dist/components/ds-swatch-picker.js +1 -1
  147. package/dist/components/ds-switch.js +1 -1
  148. package/dist/components/ds-tab-group.js +1 -1
  149. package/dist/components/ds-tab-group.js.map +1 -1
  150. package/dist/components/ds-tag.js +1 -1
  151. package/dist/components/ds-tag.js.map +1 -1
  152. package/dist/components/ds-text.js +1 -1
  153. package/dist/components/{ds-card.d.ts → ds-toast.d.ts} +4 -4
  154. package/dist/components/ds-toast.js +2 -0
  155. package/dist/components/ds-toast.js.map +1 -0
  156. package/dist/components/ds-tooltip-data-viz.js +1 -1
  157. package/dist/components/ds-tooltip-data-viz.js.map +1 -1
  158. package/dist/components/ds-tooltip.js +1 -1
  159. package/dist/components/{ds-banner.d.ts → ds-typing-indicator.d.ts} +4 -4
  160. package/dist/components/ds-typing-indicator.js +2 -0
  161. package/dist/components/ds-typing-indicator.js.map +1 -0
  162. package/dist/components/{p-DnMCRAwx.js → p-0kEg-Z4f.js} +2 -2
  163. package/dist/components/p-B8C_yfLQ.js +2 -0
  164. package/dist/components/p-B8C_yfLQ.js.map +1 -0
  165. package/dist/components/{p-DiooIO2B.js → p-BEWtqFX7.js} +2 -2
  166. package/dist/components/p-BMwnjAIq.js +2 -0
  167. package/dist/components/p-BMwnjAIq.js.map +1 -0
  168. package/dist/components/{p-Ci_nVk5P.js → p-BkwBtZs3.js} +2 -2
  169. package/dist/components/p-C3nDIMX5.js +2 -0
  170. package/dist/components/{p-Dj5MejHv.js.map → p-C3nDIMX5.js.map} +1 -1
  171. package/dist/components/{p-CJlgONwR.js → p-C8e2A66f.js} +2 -2
  172. package/dist/components/{p-BRfOCc_h.js → p-CLn7QnE0.js} +2 -2
  173. package/dist/components/{p-kezp2_Xu.js → p-CPPAndX2.js} +2 -2
  174. package/dist/components/p-CTFBf97-.js +2 -0
  175. package/dist/components/{p-CblAABEo.js.map → p-CTFBf97-.js.map} +1 -1
  176. package/dist/components/{p-BSOZXagj.js → p-CZZYGqKz.js} +2 -2
  177. package/dist/components/p-C_0Q8Cnp.js +2 -0
  178. package/dist/components/p-C_0Q8Cnp.js.map +1 -0
  179. package/dist/components/{p-T0Mzhv3-.js → p-CbckeueL.js} +2 -2
  180. package/dist/components/p-CjP8sYlo.js +2 -0
  181. package/dist/components/{p-xB0GPG3-.js.map → p-CjP8sYlo.js.map} +1 -1
  182. package/dist/components/{p-DaQ0ZE_V.js → p-DJ78wdNv.js} +2 -2
  183. package/dist/components/p-DTqCBjUj.js +2 -0
  184. package/dist/components/{p-Dst-OliP.js.map → p-DTqCBjUj.js.map} +1 -1
  185. package/dist/components/{p-CeFBA7Dx.js → p-Dh4q5TSS.js} +2 -2
  186. package/dist/components/p-DirD-b2y.js +2 -0
  187. package/dist/components/{p-BHpWYKtP.js.map → p-DirD-b2y.js.map} +1 -1
  188. package/dist/components/{p-CiwdnKKp.js → p-DrDHC7V6.js} +2 -2
  189. package/dist/components/p-DuNZGFkV.js +2 -0
  190. package/dist/components/p-DuNZGFkV.js.map +1 -0
  191. package/dist/components/{p-Cijzn3YQ.js → p-Hw-HMjCk.js} +2 -2
  192. package/dist/components/p-M5pt9I13.js +2 -0
  193. package/dist/components/p-M5pt9I13.js.map +1 -0
  194. package/dist/components/{p-B1pWGQn1.js → p-bDx_rlLm.js} +2 -2
  195. package/dist/docs/components.json +1509 -1530
  196. package/dist/framework/select-multi-value-accessor.js +3 -3
  197. package/dist/lib/toast/index.d.ts +2 -0
  198. package/dist/lib/toast/index.js +158 -0
  199. package/dist/lib/toast/index.js.map +7 -0
  200. package/dist/lib/toast/toast-manager.d.ts +3 -0
  201. package/dist/lib/toast/toast-types.d.ts +85 -0
  202. package/dist/mcp-data/registry/agent-activity.json +1 -1
  203. package/dist/mcp-data/registry/agent-response.json +6 -6
  204. package/dist/mcp-data/registry/agent-source-list.json +1 -1
  205. package/dist/mcp-data/registry/agent-tool-call.json +1 -1
  206. package/dist/mcp-data/registry/app-shell.json +1 -1
  207. package/dist/mcp-data/registry/attachment-list.json +1 -1
  208. package/dist/mcp-data/registry/avatar.json +1 -1
  209. package/dist/mcp-data/registry/badge.json +1 -1
  210. package/dist/mcp-data/registry/bar-nav.json +1 -1
  211. package/dist/mcp-data/registry/button-filled.json +1 -1
  212. package/dist/mcp-data/registry/button-unfilled.json +1 -1
  213. package/dist/mcp-data/registry/card-data-viz-donut.json +2 -2
  214. package/dist/mcp-data/registry/card-setting.json +99 -13
  215. package/dist/mcp-data/registry/card-shell-data-viz.json +8 -8
  216. package/dist/mcp-data/registry/chart-bar.json +1 -1
  217. package/dist/mcp-data/registry/chart-donut.json +1 -1
  218. package/dist/mcp-data/registry/chart-legend.json +1 -1
  219. package/dist/mcp-data/registry/chart-line.json +1 -1
  220. package/dist/mcp-data/registry/checkbox.json +1 -1
  221. package/dist/mcp-data/registry/chip.json +1 -1
  222. package/dist/mcp-data/registry/code-block.json +1 -1
  223. package/dist/mcp-data/registry/conversation-list-item.json +5 -5
  224. package/dist/mcp-data/registry/conversation-list-section.json +1 -1
  225. package/dist/mcp-data/registry/conversation-list.json +1 -1
  226. package/dist/mcp-data/registry/divider.json +5 -5
  227. package/dist/mcp-data/registry/empty-state.json +1 -1
  228. package/dist/mcp-data/registry/field.json +1 -1
  229. package/dist/mcp-data/registry/icon.json +2 -2
  230. package/dist/mcp-data/registry/input.json +1 -1
  231. package/dist/mcp-data/registry/loader.json +1 -1
  232. package/dist/mcp-data/registry/markdown.json +1 -1
  233. package/dist/mcp-data/registry/menu.json +1 -1
  234. package/dist/mcp-data/registry/message-bubble.json +21 -16
  235. package/dist/mcp-data/registry/message-composer.json +6 -4
  236. package/dist/mcp-data/registry/message-scroller.json +2 -2
  237. package/dist/mcp-data/registry/message.json +7 -6
  238. package/dist/mcp-data/registry/modal.json +154 -24
  239. package/dist/mcp-data/registry/panel-nav.json +4 -3
  240. package/dist/mcp-data/registry/panel-sub-nav.json +2 -2
  241. package/dist/mcp-data/registry/panel-tool-header.json +1 -1
  242. package/dist/mcp-data/registry/panel-tool-search.json +1 -1
  243. package/dist/mcp-data/registry/panel-tools.json +1 -1
  244. package/dist/mcp-data/registry/radio.json +1 -1
  245. package/dist/mcp-data/registry/registry.json +479 -156
  246. package/dist/mcp-data/registry/select-multi.json +1 -1
  247. package/dist/mcp-data/registry/select.json +1 -1
  248. package/dist/mcp-data/registry/shell-gradient-picker.json +1 -1
  249. package/dist/mcp-data/registry/shell-gradient-swatch.json +1 -1
  250. package/dist/mcp-data/registry/skeleton.json +4 -4
  251. package/dist/mcp-data/registry/slider.json +1 -1
  252. package/dist/mcp-data/registry/swatch-picker.json +1 -1
  253. package/dist/mcp-data/registry/switch.json +1 -1
  254. package/dist/mcp-data/registry/tab-group.json +1 -1
  255. package/dist/mcp-data/registry/tag.json +2 -2
  256. package/dist/mcp-data/registry/text.json +2 -2
  257. package/dist/mcp-data/registry/toast.json +318 -0
  258. package/dist/mcp-data/registry/tooltip-data-viz.json +1 -1
  259. package/dist/mcp-data/registry/tooltip.json +1 -1
  260. package/dist/mcp-data/registry/typing-indicator.json +127 -0
  261. package/dist/react/components.d.ts +2 -2
  262. package/dist/react/components.js +2 -2
  263. package/dist/react/ds-card-setting.d.ts +2 -2
  264. package/dist/react/ds-card-setting.js +1 -1
  265. package/dist/react/ds-modal.d.ts +3 -2
  266. package/dist/react/ds-modal.js +4 -1
  267. package/dist/react/ds-toast.d.ts +14 -0
  268. package/dist/react/ds-toast.js +16 -0
  269. package/dist/react/{ds-card.d.ts → ds-typing-indicator.d.ts} +3 -3
  270. package/dist/react/ds-typing-indicator.js +12 -0
  271. package/dist/types/components/CardSetting/CardSetting.d.ts +9 -6
  272. package/dist/types/components/CardSetting/index.d.ts +1 -1
  273. package/dist/types/components/MessageBubble/MessageBubble.d.ts +1 -1
  274. package/dist/types/components/Modal/Modal.d.ts +21 -18
  275. package/dist/types/components/Toast/Toast.d.ts +106 -0
  276. package/dist/types/components/Toast/index.d.ts +2 -0
  277. package/dist/types/components/TypingIndicator/TypingIndicator.d.ts +6 -0
  278. package/dist/types/components.d.ts +164 -182
  279. package/dist/types/toast/index.d.ts +3 -0
  280. package/dist/types/toast/toast-manager.d.ts +4 -0
  281. package/dist/types/toast/toast-types.d.ts +86 -0
  282. package/package.json +12 -8
  283. package/dist/angular/ds-banner.d.ts +0 -15
  284. package/dist/angular/ds-card.d.ts +0 -12
  285. package/dist/components/ds-banner.js +0 -2
  286. package/dist/components/ds-banner.js.map +0 -1
  287. package/dist/components/ds-card.js +0 -2
  288. package/dist/components/ds-card.js.map +0 -1
  289. package/dist/components/p-BHpWYKtP.js +0 -2
  290. package/dist/components/p-BO6C8abO.js +0 -2
  291. package/dist/components/p-BO6C8abO.js.map +0 -1
  292. package/dist/components/p-CBYuvZKM.js +0 -2
  293. package/dist/components/p-CBYuvZKM.js.map +0 -1
  294. package/dist/components/p-CWNdl52Y.js +0 -2
  295. package/dist/components/p-CWNdl52Y.js.map +0 -1
  296. package/dist/components/p-CblAABEo.js +0 -2
  297. package/dist/components/p-CiKGqsA2.js +0 -2
  298. package/dist/components/p-CiKGqsA2.js.map +0 -1
  299. package/dist/components/p-DFxqk71w.js +0 -2
  300. package/dist/components/p-DFxqk71w.js.map +0 -1
  301. package/dist/components/p-Dj5MejHv.js +0 -2
  302. package/dist/components/p-Dst-OliP.js +0 -2
  303. package/dist/components/p-drBA73M_.js +0 -2
  304. package/dist/components/p-drBA73M_.js.map +0 -1
  305. package/dist/components/p-xB0GPG3-.js +0 -2
  306. package/dist/mcp-data/registry/banner.json +0 -206
  307. package/dist/mcp-data/registry/card.json +0 -126
  308. package/dist/react/ds-banner.d.ts +0 -12
  309. package/dist/react/ds-banner.js +0 -12
  310. package/dist/react/ds-card.js +0 -12
  311. package/dist/types/components/Banner/Banner.d.ts +0 -31
  312. package/dist/types/components/Card/Card.d.ts +0 -21
  313. package/dist/types/components/Card/index.d.ts +0 -2
  314. /package/dist/components/{p-DnMCRAwx.js.map → p-0kEg-Z4f.js.map} +0 -0
  315. /package/dist/components/{p-DiooIO2B.js.map → p-BEWtqFX7.js.map} +0 -0
  316. /package/dist/components/{p-Ci_nVk5P.js.map → p-BkwBtZs3.js.map} +0 -0
  317. /package/dist/components/{p-CJlgONwR.js.map → p-C8e2A66f.js.map} +0 -0
  318. /package/dist/components/{p-BRfOCc_h.js.map → p-CLn7QnE0.js.map} +0 -0
  319. /package/dist/components/{p-kezp2_Xu.js.map → p-CPPAndX2.js.map} +0 -0
  320. /package/dist/components/{p-BSOZXagj.js.map → p-CZZYGqKz.js.map} +0 -0
  321. /package/dist/components/{p-T0Mzhv3-.js.map → p-CbckeueL.js.map} +0 -0
  322. /package/dist/components/{p-DaQ0ZE_V.js.map → p-DJ78wdNv.js.map} +0 -0
  323. /package/dist/components/{p-CeFBA7Dx.js.map → p-Dh4q5TSS.js.map} +0 -0
  324. /package/dist/components/{p-CiwdnKKp.js.map → p-DrDHC7V6.js.map} +0 -0
  325. /package/dist/components/{p-Cijzn3YQ.js.map → p-Hw-HMjCk.js.map} +0 -0
  326. /package/dist/components/{p-B1pWGQn1.js.map → p-bDx_rlLm.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"names":["scrollEdgeFadeClassMap","opts","edges","Array","isArray","classes","scrollAware","hidden","edge","atEnd","PANEL_NAV_USER_MENU_PLACEMENT","side","align","anchorAlignment","sideOffset","alignOffset","NAV_STYLE_HINT_ATTR","readNavStyleAttr","attr","undefined","resolveNavChromeStyle","styleProp","hostAttr","docHint","fromHost","hint","document","documentElement","getAttribute","fromHint","shouldResyncNavChromeStyle","renderedStyle","style","hrefMatchesPath","path","href","startsWith","deriveActiveIdFromUrl","items","bestId","bestLen","item","length","id","countPanelNavItems","groups","reduce","sum","g","resolvePanelNavToggle","collapsed","viewportNarrow","parseCssTimeList","value","split","map","parseCssTimeMs","trim","panelNavWidthTransitionMs","properties","transitionProperty","durations","transitionDuration","delays","transitionDelay","max","property","index","duration","delay","Math","parsePanelNavGroups","parsed","JSON","parse","resolvePanelNavDisableVt","prop","shouldResyncPanelNavGroups","parsedGroups","PANEL_NAV_USER_MENU_ANCHOR_ID","scrollEdgeFadeCss","panelNavCss","PanelNav","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","navStyle","disableViewTransition","routerMode","activeId","breakpoint","storageKey","currentUrl","userName","userInitial","dashboardLabel","settingsLabel","accountLabel","accountMenuExpanded","dashboardNavigationLabel","settingsNavigationLabel","expandNavigationLabel","collapseNavigationLabel","navigationItemsLabel","isAnimating","rovingIndex","urlDerivedActiveId","bodyScrollable","transitionFallbackTimer","initialRenderComplete","isDragging","dragStartX","didSnap","lastDeltaX","edgeOverlayTimer","showEdgeOverlay","effectiveActiveId","effectiveDisableViewTransition","el","onCollapsedChange","_next","prev","startCollapseAnimation","onViewportNarrowChange","next","getFirstRovingIndex","onBreakpointChange","disconnectResizeObserver","effectiveBreakpoint","connectResizeObserver","onGroupsChange","val","syncActiveFromUrl","onActiveIdChange","onCurrentUrlChange","onNavStyleChange","newVal","componentWillLoad","resolvePanelNavStyle","isViewportNarrow","stored","localStorage","getItem","componentDidLoad","syncHostPropsIfNeeded","scheduleDeferredHostPropSync","connectScrollRegionObserver","updateBodyScrollable","componentDidRender","disconnectedCallback","disconnectScrollRegionObserver","clearCollapseAnimationCompletion","querySelector","clearEdgeOverlayTimer","globalMouseMoveHandler","window","removeEventListener","globalMouseUpHandler","dsChromeTransitionStart","emit","source","panel","widthBefore","getBoundingClientRect","width","transitionCompletionHandler","e","propertyName","finishCollapseAnimation","addEventListener","requestAnimationFrame","widthAfter","abs","fallbackMs","getComputedStyle","setTimeout","wasAnimating","dsChromeTransitionEnd","clearTimeout","attributeValue","Number","isFinite","innerWidth","resizeObserver","ResizeObserver","observe","disconnect","bodyEl","scrollRegionObserver","Boolean","scrollHeight","clientHeight","getAllItems","shouldResyncPanelNavStyle","remaining","HOST_PROP_SYNC_BUDGET","tick","queueMicrotask","applyToggle","setItem","String","dsNavToggle","flatMap","getFooterRovingIndex","getUserRovingIndex","getRovingElement","itemCount","querySelectorAll","focusRovingAt","focus","preventScroll","activateRovingIndex","handleToggle","handleItemClick","handleFooterAction","anchor","dsNavUserAction","menuPlacement","handleRovingKeyDown","key","preventDefault","footerIdx","userIdx","firstIdx","dsNavSelect","toggleCollapsed","dsNavFooterAction","handleUserAction","currentTarget","handleResizeHandleMouseEnter","prefersReducedMotion","handleResizeHandleMouseLeave","handleResizeHandleMouseDown","stopPropagation","wasCollapsed","clientX","body","cursor","userSelect","onMove","ev","deltaX","onUp","renderFooterAction","isDashboardChrome","footerLabel","h","label","size","variant","class","icon","activeFill","hasBorder","focusTabIndex","onDsClick","onKeyDown","onFocusin","renderFooterUser","tipLabel","userButton","type","tabIndex","onClick","onFocus","as","emphasis","color","name","renderNavItem","idx","isActive","rovingPosition","itemContent","dot","hasRing","sharedProps","useAnchor","control","render","chromeStyle","isDashboard","navCls","Host","display","position","disabled","viewBox","fill","xmlns","focusable","d","ref","element","role","flatIdx","group","onMouseEnter","onMouseLeave","onMouseDown"],"sources":["src/wc/utils/scroll-edge-fade.ts","src/wc/components/Menu/menu-placement.ts","src/wc/shell/nav-chrome.ts","src/wc/components/PanelNav/panel-nav-utils.ts","src/wc/components/PanelNav/panel-nav-types.ts","src/wc/utils/scroll-edge-fade.css?tag=ds-panel-nav&encapsulation=scoped","src/wc/components/PanelNav/PanelNav.css?tag=ds-panel-nav&encapsulation=scoped","src/wc/components/PanelNav/PanelNav.tsx"],"sourcesContent":["/** CSS custom property for scroll-edge fade depth (dimension token or length). */\nexport const SCROLL_EDGE_FADE_SIZE_VAR = '--ds-scroll-edge-fade-size';\n\nexport const SCROLL_EDGE_FADE_DEFAULT_SIZE = 'var(--dimension-size-600)';\n\nexport type ScrollEdgeFadeEdge = 'top' | 'bottom' | 'left' | 'right';\n\nexport type ScrollEdgeFadeSizeToken =\n | 'size-000'\n | 'size-050'\n | 'size-075'\n | 'size-100'\n | 'size-150'\n | 'size-200'\n | 'size-250'\n | 'size-300'\n | 'size-400'\n | 'size-500'\n | 'size-600'\n | 'size-800';\n\nexport type ScrollEdgeFadeSize = ScrollEdgeFadeSizeToken | (string & {});\n\nconst SIZE_VALUE: Record<ScrollEdgeFadeSizeToken, string> = {\n 'size-000': 'var(--dimension-size-000)',\n 'size-050': 'var(--dimension-size-050)',\n 'size-075': 'var(--dimension-size-075)',\n 'size-100': 'var(--dimension-size-100)',\n 'size-150': 'var(--dimension-size-150)',\n 'size-200': 'var(--dimension-size-200)',\n 'size-250': 'var(--dimension-size-250)',\n 'size-300': 'var(--dimension-size-300)',\n 'size-400': 'var(--dimension-size-400)',\n 'size-500': 'var(--dimension-size-500)',\n 'size-600': 'var(--dimension-size-600)',\n 'size-800': 'var(--dimension-size-800)',\n};\n\nconst SIZE_REF = `var(${SCROLL_EDGE_FADE_SIZE_VAR}, ${SCROLL_EDGE_FADE_DEFAULT_SIZE})`;\n\nexport interface ScrollEdgeFadeOptions {\n /** One or more edges to fade. */\n edges: ScrollEdgeFadeEdge | ScrollEdgeFadeEdge[];\n /**\n * Reveal and hide configured fades from the container's own scroll position.\n * Uses CSS scroll-driven animations with a static-fade fallback; no listener required.\n */\n scrollAware?: boolean;\n /**\n * When true, removes the complete mask. Pass a map to remove only configured\n * edges that are flush when scroll state already lives in JavaScript.\n */\n atEnd?: boolean | Partial<Record<ScrollEdgeFadeEdge, boolean>>;\n /** Force the mask off regardless of scroll position (`ds-fade` `visible={false}`). */\n hidden?: boolean;\n}\n\n/** Resolve a dimension size token name or raw CSS length for `--ds-scroll-edge-fade-size`. */\nexport function resolveScrollEdgeFadeSize(\n size: ScrollEdgeFadeSize = 'size-600',\n height?: string,\n): string {\n if (height) return height;\n return Object.prototype.hasOwnProperty.call(SIZE_VALUE, size)\n ? SIZE_VALUE[size as ScrollEdgeFadeSizeToken]\n : size;\n}\n\n/** Inline style map setting fade depth on a scroll container. */\nexport function scrollEdgeFadeSizeStyle(\n size: ScrollEdgeFadeSize = 'size-600',\n height?: string,\n): Record<string, string> {\n return {\n [SCROLL_EDGE_FADE_SIZE_VAR]: resolveScrollEdgeFadeSize(size, height),\n };\n}\n\n/**\n * Alpha mask for a scroll container edge — content fades to transparent so\n * textured backgrounds (shell gradients, images) show through.\n */\nexport function scrollEdgeFadeMaskImage(edge: ScrollEdgeFadeEdge): string {\n switch (edge) {\n case 'top':\n return `linear-gradient(to bottom, transparent 0, #000 ${SIZE_REF}, #000 100%)`;\n case 'bottom':\n return `linear-gradient(to bottom, #000 0, #000 calc(100% - ${SIZE_REF}), transparent 100%)`;\n case 'left':\n return `linear-gradient(to right, transparent 0, #000 ${SIZE_REF}, #000 100%)`;\n case 'right':\n return `linear-gradient(to right, #000 0, #000 calc(100% - ${SIZE_REF}), transparent 100%)`;\n }\n}\n\n/** Inline style map for a masked scroll region (single edge). */\nexport function scrollEdgeFadeMaskStyle(\n edge: ScrollEdgeFadeEdge,\n size: ScrollEdgeFadeSize = 'size-600',\n height?: string,\n): Record<string, string> {\n const mask = scrollEdgeFadeMaskImage(edge);\n return {\n ...scrollEdgeFadeSizeStyle(size, height),\n WebkitMaskImage: mask,\n maskImage: mask,\n WebkitMaskSize: '100% 100%',\n maskSize: '100% 100%',\n };\n}\n\n/** Class map for `scroll-edge-fade` modifiers — pair with `scroll-edge-fade.css`. */\nexport function scrollEdgeFadeClassMap(opts: ScrollEdgeFadeOptions): Record<string, boolean> {\n const edges = Array.isArray(opts.edges) ? opts.edges : [opts.edges];\n const classes: Record<string, boolean> = {\n 'scroll-edge-fade': true,\n 'scroll-edge-fade--scroll-aware': opts.scrollAware === true,\n 'scroll-edge-fade--hidden': opts.hidden === true,\n };\n\n for (const edge of edges) {\n classes[`scroll-edge-fade--${edge}`] = true;\n }\n\n const { atEnd } = opts;\n if (atEnd === true) {\n classes['scroll-edge-fade--at-end'] = true;\n } else if (atEnd && typeof atEnd === 'object') {\n for (const edge of edges) {\n if (atEnd[edge]) {\n classes[`scroll-edge-fade--${edge}-at-edge`] = true;\n }\n }\n }\n\n return classes;\n}\n\n/** Whether a scroll container is flush with the given edge (within `threshold` px). */\nexport function isScrollAtEdge(\n el: HTMLElement,\n edge: ScrollEdgeFadeEdge,\n threshold = 2,\n): boolean {\n switch (edge) {\n case 'top':\n return el.scrollTop <= threshold;\n case 'bottom':\n return el.scrollHeight - el.scrollTop - el.clientHeight <= threshold;\n case 'left':\n return el.scrollLeft <= threshold;\n case 'right':\n return el.scrollWidth - el.scrollLeft - el.clientWidth <= threshold;\n }\n}\n","import type { ChoicePopupAnchorAlignment } from '../../utils/choice-popup-alignment';\nimport type { MenuAlign, MenuSide } from './menu-position';\n\n/** Placement props bindable on `ds-menu` (side, align, offsets). */\nexport interface MenuPlacement {\n side: MenuSide;\n align: MenuAlign;\n anchorAlignment: ChoicePopupAnchorAlignment;\n sideOffset: number | string;\n alignOffset: number | string;\n}\n\n/**\n * Canonical `ds-menu` placement for the **panel-nav footer user menu**.\n * Anchor from `dsNavUserAction` detail `{ anchor }` (id `ds-panel-nav-user-menu-anchor`).\n *\n * Not for BarNav overflow menus — those use `side=\"bottom\"` / `align=\"end\"` internally.\n * `align: 'end'` bottom-aligns the last choice cell on the anchor; the side offset nudges into the content area:\n * - `sideOffset` space-100 + space-050 (12px): 4px past the panel-nav outer edge (anchor is ~8px inset).\n * - `anchorAlignment: 'choice-cell'`: the shared section inset puts the last row edge on the footer user row.\n */\nexport const PANEL_NAV_USER_MENU_PLACEMENT = {\n side: 'right',\n align: 'end',\n anchorAlignment: 'choice-cell',\n sideOffset: 'calc(var(--dimension-space-100) + var(--dimension-space-050))',\n alignOffset: 0,\n} as const satisfies MenuPlacement;\n\n/**\n * Canonical placement for a menu opened by a PanelTools header action.\n * Matches BarNav overflow: the 32px trigger is 8px inside the 48px header, so\n * a 12px side offset places the popup 4px below the header edge. Choice-cell\n * alignment keeps the final menu item edge aligned with the trigger.\n */\nexport const PANEL_TOOLS_HEADER_MENU_PLACEMENT = {\n side: 'bottom',\n align: 'end',\n anchorAlignment: 'choice-cell',\n sideOffset: 'calc(var(--dimension-space-100) + var(--dimension-space-050))',\n alignOffset: 0,\n} as const satisfies MenuPlacement;\n","/** Nav chrome style slot — dashboard vs settings. Colors unified for now; texture hooks use the class. */\nexport type NavChromeStyle = 'dashboard' | 'settings';\n\n/** Document hint for first paint before framework props land. */\nexport const NAV_STYLE_HINT_ATTR = 'data-nav-style';\n\nexport function readNavStyleAttr(attr: string | null): NavChromeStyle | undefined {\n if (attr === 'dashboard' || attr === 'settings') return attr;\n return undefined;\n}\n\nexport function setNavStyleHint(style: NavChromeStyle): void {\n if (typeof document === 'undefined') return;\n document.documentElement.setAttribute(NAV_STYLE_HINT_ATTR, style);\n}\n\nexport function clearNavStyleHint(): void {\n if (typeof document === 'undefined') return;\n document.documentElement.removeAttribute(NAV_STYLE_HINT_ATTR);\n}\n\nexport function resolveNavChromeStyle(\n styleProp: NavChromeStyle,\n hostAttr: string | null,\n docHint?: string | null,\n): NavChromeStyle {\n const fromHost = readNavStyleAttr(hostAttr);\n if (fromHost) return fromHost;\n\n const hint =\n docHint !== undefined\n ? docHint\n : typeof document !== 'undefined'\n ? document.documentElement.getAttribute(NAV_STYLE_HINT_ATTR)\n : null;\n const fromHint = readNavStyleAttr(hint);\n if (fromHint) return fromHint;\n\n return styleProp;\n}\n\nexport function shouldResyncNavChromeStyle(\n renderedStyle: NavChromeStyle,\n style: NavChromeStyle,\n): boolean {\n return renderedStyle !== style;\n}\n","import type { PanelNavGroup, PanelNavItem } from './panel-nav-types';\nimport { parseCssTimeMs } from '../../utils/resolve-css-time-ms';\nimport {\n NAV_STYLE_HINT_ATTR,\n clearNavStyleHint,\n readNavStyleAttr,\n resolveNavChromeStyle,\n setNavStyleHint,\n shouldResyncNavChromeStyle,\n} from '../../shell/nav-chrome';\n\nexport type { NavChromeStyle } from '../../shell/nav-chrome';\nexport {\n NAV_STYLE_HINT_ATTR,\n readNavStyleAttr,\n setNavStyleHint,\n clearNavStyleHint,\n resolveNavChromeStyle as resolvePanelNavStyle,\n shouldResyncNavChromeStyle as shouldResyncPanelNavStyle,\n};\n\n/** Whether `path` matches `href` at a segment boundary (exact or child path). */\nexport function hrefMatchesPath(path: string, href: string): boolean {\n return path === href || path.startsWith(`${href}/`);\n}\n\n/** Derive the active nav item id from a URL using longest segment-boundary prefix match. */\nexport function deriveActiveIdFromUrl(path: string, items: PanelNavItem[]): string {\n if (!path) return '';\n\n let bestId = '';\n let bestLen = 0;\n\n for (const item of items) {\n if (!item.href) continue;\n if (hrefMatchesPath(path, item.href) && item.href.length > bestLen) {\n bestId = item.id;\n bestLen = item.href.length;\n }\n }\n\n return bestId;\n}\n\n/** Total nav item count across all groups. */\nexport function countPanelNavItems(groups: PanelNavGroup[]): number {\n return groups.reduce((sum, g) => sum + g.items.length, 0);\n}\n\n/** Resolve a user toggle without mutating the persisted desktop preference while breakpoint-locked. */\nexport function resolvePanelNavToggle(\n collapsed: boolean,\n viewportNarrow: boolean,\n): boolean | null {\n return viewportNarrow ? null : !collapsed;\n}\n\nexport interface PanelNavTransitionStyle {\n transitionProperty: string;\n transitionDuration: string;\n transitionDelay: string;\n}\n\nfunction parseCssTimeList(value: string): number[] {\n return value.split(',').map(item => parseCssTimeMs(item.trim(), 0));\n}\n\n/**\n * Maximum computed time for PanelNav's width transition. CSS repeats shorter\n * duration/delay lists to match the property list, so mirror that behavior.\n */\nexport function panelNavWidthTransitionMs(style: PanelNavTransitionStyle): number {\n const properties = style.transitionProperty.split(',').map(item => item.trim());\n const durations = parseCssTimeList(style.transitionDuration);\n const delays = parseCssTimeList(style.transitionDelay);\n\n return properties.reduce((max, property, index) => {\n if (property !== 'all' && property !== 'width' && property !== 'min-width') return max;\n const duration = durations[index % durations.length] ?? 0;\n const delay = delays[index % delays.length] ?? 0;\n return Math.max(max, duration + delay, 0);\n }, 0);\n}\n\n/** Parse `groups` from either a JSON attribute string or a JS property array. */\nexport function parsePanelNavGroups(groups: string | unknown): PanelNavGroup[] {\n if (Array.isArray(groups)) return groups;\n if (typeof groups === 'string') {\n try {\n const parsed = JSON.parse(groups);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n }\n return [];\n}\n\n/** Resolve whether VT is disabled from prop and/or host attribute. */\nexport function resolvePanelNavDisableVt(prop: boolean, attr: string | null): boolean {\n if (prop) return true;\n if (attr === null) return false;\n return attr !== 'false';\n}\n\n/** True when host `groups` has items but internal parsed state is still empty. */\nexport function shouldResyncPanelNavGroups(\n parsedGroups: PanelNavGroup[],\n groups: string | unknown,\n): boolean {\n return (\n countPanelNavItems(parsedGroups) === 0 &&\n countPanelNavItems(parsePanelNavGroups(groups)) > 0\n );\n}\n","import type { MenuPlacement } from '../Menu/menu-placement';\nimport { PANEL_NAV_USER_MENU_PLACEMENT } from '../Menu/menu-placement';\nimport type { NavChromeStyle } from '../../shell/nav-chrome';\n\nexport type { NavChromeStyle };\nexport type PanelNavRouterMode = 'anchor' | 'event';\n\nexport interface PanelNavItem {\n id: string;\n icon: string;\n label: string;\n /** Show a notification dot badge on the item */\n dot?: boolean;\n /** Route path used for `currentUrl` matching. In `anchor` mode also sets `<a href>`.\n * In `event` mode navigation is delegated to the host via `dsNavSelect`. */\n href?: string;\n}\n\nexport interface PanelNavGroup {\n id?: string;\n label?: string;\n items: PanelNavItem[];\n}\n\n/** Stable `id` on the footer user button — use with `ds-menu` `anchor-id`. */\nexport const PANEL_NAV_USER_MENU_ANCHOR_ID = 'ds-panel-nav-user-menu-anchor';\n\n/** Detail for `dsNavUserAction` — anchor element for an external `ds-menu`. */\nexport interface PanelNavUserActionDetail {\n anchor: HTMLElement;\n /** Recommended `ds-menu` placement — spread onto external user menu instance. */\n menuPlacement: MenuPlacement;\n}\n\nexport { PANEL_NAV_USER_MENU_PLACEMENT };\n","/**\n * Scroll-edge alpha fade — the single edge-fade utility for overflow containers.\n * Masks scrolling content to transparent so flat surfaces, shell gradients, and\n * images show through.\n *\n * Static fades are the default (PanelNav intentionally keeps its bottom fade).\n * Add `.scroll-edge-fade--scroll-aware` to reveal/hide each configured edge from\n * the container's own scroll position without JavaScript. Browsers without CSS\n * scroll-driven animations retain the configured static fades.\n *\n * @see scroll-edge-fade.ts — `scrollEdgeFadeClassMap`, `isScrollAtEdge`, size helpers.\n */\n\n@property --ds-scroll-edge-fade-top {\n syntax: '<length-percentage>';\n inherits: false;\n /* stylelint-disable-next-line length-zero-no-unit -- @property requires a unit-compatible initial value. */\n initial-value: 0px;\n}\n\n@property --ds-scroll-edge-fade-bottom {\n syntax: '<length-percentage>';\n inherits: false;\n /* stylelint-disable-next-line length-zero-no-unit -- @property requires a unit-compatible initial value. */\n initial-value: 0px;\n}\n\n@property --ds-scroll-edge-fade-left {\n syntax: '<length-percentage>';\n inherits: false;\n /* stylelint-disable-next-line length-zero-no-unit -- @property requires a unit-compatible initial value. */\n initial-value: 0px;\n}\n\n@property --ds-scroll-edge-fade-right {\n syntax: '<length-percentage>';\n inherits: false;\n /* stylelint-disable-next-line length-zero-no-unit -- @property requires a unit-compatible initial value. */\n initial-value: 0px;\n}\n\n@keyframes ds-scroll-edge-fade-reveal-top {\n from { --ds-scroll-edge-fade-top: 0px; }\n to { --ds-scroll-edge-fade-top: var(--ds-scroll-edge-fade-target-top); }\n}\n\n@keyframes ds-scroll-edge-fade-reveal-bottom {\n from { --ds-scroll-edge-fade-bottom: var(--ds-scroll-edge-fade-target-bottom); }\n to { --ds-scroll-edge-fade-bottom: 0px; }\n}\n\n@keyframes ds-scroll-edge-fade-reveal-left {\n from { --ds-scroll-edge-fade-left: 0px; }\n to { --ds-scroll-edge-fade-left: var(--ds-scroll-edge-fade-target-left); }\n}\n\n@keyframes ds-scroll-edge-fade-reveal-right {\n from { --ds-scroll-edge-fade-right: var(--ds-scroll-edge-fade-target-right); }\n to { --ds-scroll-edge-fade-right: 0px; }\n}\n\n/* Mask gradients use #000 / transparent as alpha stops — not theme colors. */\n/* stylelint-disable color-no-hex */\n\n.scroll-edge-fade {\n --ds-scroll-edge-fade-size: var(--dimension-size-600);\n --ds-scroll-edge-fade-reveal-distance: var(--dimension-size-800);\n --ds-scroll-edge-fade-target-top: 0px;\n --ds-scroll-edge-fade-target-bottom: 0px;\n --ds-scroll-edge-fade-target-left: 0px;\n --ds-scroll-edge-fade-target-right: 0px;\n --ds-scroll-edge-fade-top: var(--ds-scroll-edge-fade-target-top);\n --ds-scroll-edge-fade-bottom: var(--ds-scroll-edge-fade-target-bottom);\n --ds-scroll-edge-fade-left: var(--ds-scroll-edge-fade-target-left);\n --ds-scroll-edge-fade-right: var(--ds-scroll-edge-fade-target-right);\n --ds-scroll-edge-fade-block-mask: linear-gradient(\n to bottom,\n transparent 0,\n #000 var(--ds-scroll-edge-fade-top),\n #000 calc(100% - var(--ds-scroll-edge-fade-bottom)),\n transparent 100%\n );\n --ds-scroll-edge-fade-inline-mask: linear-gradient(\n to right,\n transparent 0,\n #000 var(--ds-scroll-edge-fade-left),\n #000 calc(100% - var(--ds-scroll-edge-fade-right)),\n transparent 100%\n );\n\n /* Intersecting the block and inline masks lets every edge compose correctly. */\n -webkit-mask-image:\n var(--ds-scroll-edge-fade-block-mask),\n var(--ds-scroll-edge-fade-inline-mask);\n mask-image:\n var(--ds-scroll-edge-fade-block-mask),\n var(--ds-scroll-edge-fade-inline-mask);\n -webkit-mask-composite: source-in;\n mask-composite: intersect;\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.scroll-edge-fade--top {\n --ds-scroll-edge-fade-target-top: var(--ds-scroll-edge-fade-size);\n}\n\n.scroll-edge-fade--bottom {\n --ds-scroll-edge-fade-target-bottom: var(--ds-scroll-edge-fade-size);\n}\n\n.scroll-edge-fade--left {\n --ds-scroll-edge-fade-target-left: var(--ds-scroll-edge-fade-size);\n}\n\n.scroll-edge-fade--right {\n --ds-scroll-edge-fade-target-right: var(--ds-scroll-edge-fade-size);\n}\n\n/* Manual per-edge state remains available when scroll state already lives in JS. */\n.scroll-edge-fade--top-at-edge {\n --ds-scroll-edge-fade-target-top: 0px;\n}\n\n.scroll-edge-fade--bottom-at-edge {\n --ds-scroll-edge-fade-target-bottom: 0px;\n}\n\n.scroll-edge-fade--left-at-edge {\n --ds-scroll-edge-fade-target-left: 0px;\n}\n\n.scroll-edge-fade--right-at-edge {\n --ds-scroll-edge-fade-target-right: 0px;\n}\n\n@supports (animation-timeline: scroll()) {\n .scroll-edge-fade--scroll-aware {\n animation-name:\n ds-scroll-edge-fade-reveal-top,\n ds-scroll-edge-fade-reveal-bottom,\n ds-scroll-edge-fade-reveal-left,\n ds-scroll-edge-fade-reveal-right;\n /* stylelint-disable-next-line declaration-property-value-disallowed-list -- scroll timelines require a nonzero sentinel duration; elapsed time is ignored. */\n animation-duration: 1ms;\n animation-timing-function: ease-in-out;\n animation-fill-mode: both;\n animation-timeline: scroll(self y), scroll(self y), scroll(self x), scroll(self x);\n animation-range:\n 0 var(--ds-scroll-edge-fade-reveal-distance),\n calc(100% - var(--ds-scroll-edge-fade-reveal-distance)) 100%,\n 0 var(--ds-scroll-edge-fade-reveal-distance),\n calc(100% - var(--ds-scroll-edge-fade-reveal-distance)) 100%;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .scroll-edge-fade--scroll-aware {\n animation: none;\n }\n }\n}\n\n.scroll-edge-fade--at-end,\n.scroll-edge-fade--hidden {\n -webkit-mask-image: none;\n mask-image: none;\n}\n\n/**\n * Masks a long content element against a scrollport-relative block window.\n * The owner updates the start/end offsets as the scrollport moves. Keeping the\n * mask on the content instead of the overflow element leaves native scrollbars\n * outside the fade.\n */\n.scroll-edge-fade--block-window {\n --ds-scroll-edge-fade-window-start: 100%;\n --ds-scroll-edge-fade-window-end: 100%;\n\n -webkit-mask-image: linear-gradient(\n to bottom,\n #000 0,\n #000 var(--ds-scroll-edge-fade-window-start),\n transparent var(--ds-scroll-edge-fade-window-end)\n );\n mask-image: linear-gradient(\n to bottom,\n #000 0,\n #000 var(--ds-scroll-edge-fade-window-start),\n transparent var(--ds-scroll-edge-fade-window-end)\n );\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n}\n\n/* stylelint-enable color-no-hex */\n","@import '../../utils/control-density.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/interaction-fill.css';\n\n/* ── Theme variables ──────────────────────────────────────────────────────── */\n\n.panel-nav {\n /* ─ Sizes ─ */\n --_nav-width: var(--dimension-panel-width-2xs);\n --_nav-width-collapsed: var(--dimension-size-600);\n --_nav-item-height: var(--dimension-size-400, 32px);\n --_nav-padding-x: var(--dimension-space-100, 8px);\n --_nav-padding-y: var(--dimension-space-100, 8px);\n /* gear button outer width = padding*2 + icon */\n --_nav-footer-btn-w: calc(var(--dimension-space-075, 6px) * 2 + var(--dimension-iconography-md, 20px));\n\n /* ─ Motion ─ */\n /* --ds-panel-nav-speed: unitless multiplier; override in Storybook to slow-mo (e.g. 10 = 10×).\n *-dur vars are duration-only (needed for calc); *-motion vars add easing for use in transitions. */\n --_nav-speed: var(--ds-panel-nav-speed, 1);\n --_nav-expand-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-expand-motion: var(--_nav-expand-dur) var(--effect-animation-easing-ease-in-out, ease);\n --_nav-label-fade-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-label-fade-motion: var(--_nav-label-fade-dur) var(--effect-animation-easing-ease-in-out, ease);\n --_nav-label-fade-out-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-label-fade-out-motion: var(--_nav-label-fade-out-dur) var(--effect-animation-easing-ease-in-out, ease);\n\n /* ─ Colours – default chrome (app surfaces) ─ */\n --_nav-bg: var(--color-background-secondary);\n --_nav-border: var(--color-border-tertiary);\n --_nav-divider: var(--color-border-tertiary);\n --_nav-fg-primary: var(--color-foreground-primary);\n --_nav-fg-secondary: var(--color-foreground-secondary);\n --_nav-fg-tertiary: var(--color-foreground-tertiary);\n --_nav-hover: var(--color-interaction-hover);\n --_nav-pressed: var(--color-interaction-pressed);\n --_nav-focus: var(--color-interaction-focus);\n --_nav-dot: var(--color-foreground-bold-brand);\n --ds-interaction-hover: var(--_nav-hover);\n --ds-interaction-pressed: var(--_nav-pressed);\n --ds-focus-ring-color: var(--_nav-focus);\n}\n\n/* ── Default chrome overrides ────────────────────────────────────────────── */\n\n/* Shell-owned chrome (bg + wash) — panel is a transparent frame. */\n:host-context(ds-app-shell:not([gradient-preset='none'])) .panel-nav {\n background-color: transparent;\n}\n\n/* Style slots — same app tokens for now; hook texture/glyph layers on these classes later. */\n.panel-nav--dashboard,\n.panel-nav--settings {\n --_nav-bg: var(--color-background-secondary);\n --_nav-border: var(--color-border-tertiary);\n --_nav-divider: var(--color-border-tertiary);\n --_nav-fg-primary: var(--color-foreground-primary);\n --_nav-fg-secondary: var(--color-foreground-secondary);\n --_nav-fg-tertiary: var(--color-foreground-tertiary);\n --_nav-hover: var(--color-interaction-hover);\n --_nav-pressed: var(--color-interaction-pressed);\n --_nav-focus: var(--color-interaction-focus);\n --_nav-dot: var(--color-foreground-bold-brand);\n --ds-interaction-hover: var(--_nav-hover);\n --ds-interaction-pressed: var(--_nav-pressed);\n --ds-focus-ring-color: var(--_nav-focus);\n}\n\n/* ── Shell ───────────────────────────────────────────────────────────────── */\n\n.panel-nav {\n display: flex;\n flex-direction: column;\n position: relative;\n width: var(--_nav-width);\n min-width: var(--_nav-width);\n height: 100%;\n background-color: var(--_nav-bg);\n border-right: var(--dimension-stroke-width-012, 1px) solid var(--_nav-border);\n box-sizing: border-box;\n overflow: visible;\n transition:\n width var(--_nav-expand-motion),\n min-width var(--_nav-expand-motion),\n background-color var(--effect-motion-short-3, 200ms ease),\n border-color var(--effect-motion-short-3, 200ms ease);\n font-family: var(--typography-font-family, system-ui);\n isolation: isolate;\n}\n\n.panel-nav__header,\n.panel-nav__body,\n.panel-nav__footer {\n position: relative;\n z-index: 3;\n}\n\n.panel-nav--collapsed {\n width: var(--_nav-width-collapsed);\n min-width: var(--_nav-width-collapsed);\n}\n\n/* ── Header ──────────────────────────────────────────────────────────────── */\n\n.panel-nav__header {\n display: flex;\n align-items: center;\n height: var(--dimension-size-600, 48px);\n padding: var(--dimension-space-100, 8px);\n flex-shrink: 0;\n box-sizing: border-box;\n border-bottom: var(--dimension-stroke-width-012, 1px) solid var(--_nav-divider);\n transition: border-color var(--effect-motion-short-3, 200ms ease);\n}\n\n.panel-nav__header-btn {\n all: unset;\n /* Re-assert after unset — required for `.ds-interaction-fill` stacking. */\n position: relative;\n z-index: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--dimension-space-075, 6px);\n border-radius: var(--dimension-radius-025, 2px);\n cursor: pointer;\n color: var(--_nav-fg-primary);\n flex-shrink: 0;\n box-sizing: border-box;\n transition: color var(--effect-motion-short-3, 200ms ease);\n /* Hover/press: `.ds-interaction-fill` (tokens from --_nav-* via --ds-interaction-*). */\n}\n\n.panel-nav--breakpoint-locked .panel-nav__header-btn {\n cursor: default;\n}\n\n/* Logo — visible by default; instant swap to toggle on hover (no crossfade overlap) */\n.panel-nav__header-logo {\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 2;\n opacity: 1;\n}\n\n.panel-nav__m-mark {\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n display: block;\n}\n\n/* Toggle icon — stacked on logo; under hover wash (::after z-index 3) */\n.panel-nav__header-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n inset: 0;\n z-index: 2;\n opacity: 0;\n pointer-events: none;\n}\n\n.panel-nav:not(.panel-nav--breakpoint-locked) .panel-nav__header-btn:hover .panel-nav__header-logo { opacity: 0; }\n.panel-nav:not(.panel-nav--breakpoint-locked) .panel-nav__header-btn:hover .panel-nav__header-toggle { opacity: 1; }\n\n/* ── Scrollable body ─────────────────────────────────────────────────────── */\n\n.panel-nav__body {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-200, 16px);\n padding: var(--dimension-space-100, 8px);\n box-sizing: border-box;\n scrollbar-width: none;\n}\n\n.panel-nav__body::-webkit-scrollbar { display: none; }\n\n/* ── Group ───────────────────────────────────────────────────────────────── */\n\n.panel-nav__group {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050, 4px);\n}\n\n/*\n * Section headers — typography via ds-text (text-caption + emphasis).\n * Element selector so display:flex beats Text's inline-block, and padding\n * isn't clobbered by sharing `.panel-nav__item-label-text` on the same host.\n */\nds-text.panel-nav__group-label {\n display: flex;\n align-items: center;\n box-sizing: border-box;\n text-transform: uppercase;\n color: var(--_nav-fg-secondary);\n user-select: none;\n height: var(--dimension-size-300, 24px);\n /* Former nested pad: group-label (075) + item-label-text (025). */\n padding: 0 calc(var(--dimension-space-075, 6px) + var(--ds-control-label-inset));\n white-space: nowrap;\n overflow: hidden;\n max-height: var(--dimension-size-300, 24px);\n opacity: 1;\n transition:\n opacity var(--_nav-label-fade-motion) 0ms,\n max-height var(--_nav-label-fade-motion) 0ms,\n padding var(--_nav-label-fade-motion) 0ms,\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Item ────────────────────────────────────────────────────────────────── */\n\n.panel-nav__item {\n all: unset;\n /* Re-assert after unset — required for `.ds-interaction-fill` stacking. */\n position: relative;\n z-index: 0;\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050, 4px);\n padding: 0 var(--dimension-space-075, 6px);\n height: var(--dimension-size-400, 32px);\n border-radius: var(--dimension-radius-025, 2px);\n cursor: pointer;\n color: var(--_nav-fg-secondary);\n box-sizing: border-box;\n width: 100%;\n user-select: none;\n transition: color var(--effect-motion-short-1, 50ms ease-in-out);\n /* Hover/press: `.ds-interaction-fill` — no --selected (icon/type weight only). */\n}\n\n/* ── Active item ─────────────────────────────────────────────────────────── */\n\n.panel-nav__item--active {\n color: var(--_nav-fg-primary);\n /* Bold typography via ds-text (text-body-medium + emphasis) on the label */\n}\n\n/* ── Item icon ───────────────────────────────────────────────────────────── */\n\n.panel-nav__item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n position: relative;\n z-index: 2;\n}\n\n/* ── Item label ──────────────────────────────────────────────────────────── */\n\n.panel-nav__item-label {\n position: relative;\n z-index: 2;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n height: var(--typography-lineheight-md, 20px);\n opacity: 1;\n /* Gradient wipe: mask sweeps left-to-right on expand, right-to-left on collapse */\n /* stylelint-disable-next-line color-named -- mask luminance stops, not theme colors */\n mask-image: linear-gradient(to right, black 50%, transparent 100%);\n /* stylelint-disable-next-line color-named -- mask luminance stops, not theme colors */\n -webkit-mask-image: linear-gradient(to right, black 50%, transparent 100%);\n mask-size: 200% 100%;\n -webkit-mask-size: 200% 100%;\n mask-position: 0% 0;\n -webkit-mask-position: 0% 0;\n transition:\n opacity var(--_nav-label-fade-motion) 0ms,\n mask-position var(--_nav-label-fade-motion) 0ms,\n -webkit-mask-position var(--_nav-label-fade-motion) 0ms;\n}\n\n.panel-nav__item-label-text {\n padding: 0 var(--ds-control-label-inset);\n}\n\n/* ── Item notification dot — 20px suffix zone matches the icon geometry ──── */\n\n.panel-nav__item-dot-box {\n position: absolute;\n z-index: 2;\n right: var(--dimension-space-075, 6px);\n top: var(--dimension-space-075, 6px);\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n pointer-events: none;\n}\n\n.panel-nav__item-dot {\n transition:\n transform var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__item-dot {\n transform: translate(\n calc(var(--dimension-space-075, 6px) + var(--dimension-stroke-width-012, 1px)),\n calc(0px - var(--dimension-space-075, 6px) - var(--dimension-stroke-width-012, 1px))\n );\n}\n\n.panel-nav--animating .panel-nav__item-label {\n text-overflow: clip;\n}\n\n.panel-nav--collapsed .panel-nav__item-label {\n opacity: 0;\n pointer-events: none;\n text-overflow: clip;\n mask-position: 100% 0;\n -webkit-mask-position: 100% 0;\n transition:\n opacity var(--_nav-label-fade-out-motion) 0ms,\n mask-position var(--_nav-label-fade-out-motion) 0ms,\n -webkit-mask-position var(--_nav-label-fade-out-motion) 0ms;\n}\n\n.panel-nav--collapsed ds-text.panel-nav__group-label {\n opacity: 0;\n max-height: 0;\n padding: 0 calc(var(--dimension-space-075, 6px) + var(--ds-control-label-inset));\n pointer-events: none;\n transition:\n opacity var(--_nav-label-fade-out-motion) 0ms,\n max-height var(--_nav-label-fade-out-motion) 0ms,\n padding var(--_nav-label-fade-out-motion) 0ms,\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Footer ──────────────────────────────────────────────────────────────── */\n\n.panel-nav__footer {\n position: relative;\n flex-shrink: 0;\n /* single row: padding*2 + item */\n height: calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height));\n transition: height var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer {\n /* two rows: padding*2 + item*2 + gap */\n height: calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height) * 2 + var(--dimension-space-050, 4px));\n}\n\n/* ── Footer icon button (gear / dashboard) ───────────────────────────────── */\n\n.panel-nav__footer-btn {\n position: absolute;\n left: var(--_nav-padding-x);\n top: var(--_nav-padding-y);\n --ds-button-unfilled-fg: var(--_nav-fg-secondary);\n --ds-button-unfilled-fg-active: var(--_nav-fg-primary);\n --ds-interaction-hover: var(--_nav-hover);\n --ds-interaction-pressed: var(--_nav-pressed);\n --ds-focus-ring-color: var(--_nav-focus);\n}\n\n/* ── Footer user — absolutely positioned, slides from row to below gear ───── */\n\n.panel-nav__footer-user {\n position: absolute;\n /* Expanded row: balanced shell inset with an 8px gear-to-user gap. */\n left: calc(var(--_nav-padding-x) + var(--_nav-footer-btn-w) + var(--dimension-space-100, 8px));\n right: var(--_nav-padding-x);\n top: var(--_nav-padding-y);\n width: auto;\n min-width: 0;\n transition:\n left var(--_nav-expand-motion),\n right var(--_nav-expand-motion),\n top var(--_nav-expand-motion);\n}\n\n/* Popup-open state keeps the native footer trigger visibly pressed. */\n.panel-nav__footer-user--menu-expanded::after,\n.panel-nav__footer-user--menu-expanded:hover:not(:disabled)::after {\n background: var(--_nav-pressed);\n}\n\n.panel-nav__footer-user--menu-expanded {\n color: var(--_nav-fg-primary);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user {\n /* Compact column keeps the intentionally tighter 4px vertical gap. */\n left: var(--_nav-padding-x);\n top: calc(var(--_nav-padding-y) + var(--_nav-item-height) + var(--dimension-space-050, 4px));\n}\n\n/* Label: absolute so it stays in place and only fades — no mask slide */\n.panel-nav__footer-user-label {\n text-align: right;\n position: absolute;\n left: var(--dimension-space-075, 6px);\n right: calc(var(--dimension-space-075, 6px) + var(--dimension-iconography-md, 20px) + var(--dimension-space-050, 4px));\n top: 50%;\n transform: translateY(-50%);\n flex: none;\n height: var(--typography-lineheight-md, 20px);\n mask-image: none;\n -webkit-mask-image: none;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user-label {\n opacity: 0;\n pointer-events: none;\n mask-position: 0% 0;\n -webkit-mask-position: 0% 0;\n}\n\n/* ── Footer user icon — cross-fades chevron ↔ circle+initial ────────────── */\n\n/* Absolute — right-anchored in expanded, left-anchored in collapsed to match regular item icon position */\n.panel-nav__footer-user-icon {\n position: absolute;\n left: calc(100% - var(--dimension-iconography-md, 20px) - var(--dimension-space-075, 6px));\n top: 0;\n bottom: 0;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: left var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user-icon {\n left: var(--dimension-space-075, 6px);\n}\n\n.panel-nav__footer-icon-expanded,\n.panel-nav__footer-icon-collapsed {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.panel-nav__footer-icon-expanded {\n opacity: 1;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav__footer-icon-collapsed {\n opacity: 0;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-icon-expanded {\n opacity: 0;\n transition: opacity var(--_nav-label-fade-out-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-icon-collapsed {\n opacity: 1;\n transition: opacity var(--_nav-label-fade-out-motion);\n}\n\n/* ── User initial — overlaid on the circle icon ─────────────────────────── */\n\n/*\n * Center the caption glyph on the Circle icon. Don't use inset:0 + flex — the\n * inner `.ds-text__element { width: 100% }` stretches and left-aligns the letter.\n * Translate-centering keeps the complete caption metric box on the icon center;\n * the caption variant remains the sole owner of its line-height.\n * (Do not set `inset: auto` after top/left — it resets them.)\n */\nds-text.panel-nav__user-initial,\nds-text.panel-nav__user-initial.ds-text--caption {\n position: absolute;\n top: 50%;\n left: 50%;\n right: auto;\n bottom: auto;\n transform: translate(-50%, -50%);\n display: block;\n width: max-content;\n max-width: none;\n min-width: 0;\n text-align: center;\n color: inherit;\n pointer-events: none;\n}\n\n/* ── Drag-to-resize handle ───────────────────────────────────────────────── */\n\n/* The resize overlay line — extends past the right edge; keep above workspace content */\n.panel-nav__resize-handle {\n position: absolute;\n top: 0;\n /* Centered on the right edge: --dimension-space-050 outside + inside = --dimension-space-100 hit zone */\n right: var(--dimension-space-n050, -4px);\n bottom: 0;\n width: var(--dimension-space-100, 8px);\n z-index: 20;\n cursor: ew-resize;\n touch-action: none;\n /* Needs a real background for Safari hit-testing */\n background: transparent;\n}\n\n/* Safari requires non-transparent fill for hit testing */\n.panel-nav__resize-handle::before {\n content: '';\n position: absolute;\n inset: 0;\n /* stylelint-disable-next-line color-named -- near-invisible hit-test fill; not a theme color */\n background: rgba(0, 0, 0, 0.001);\n}\n\n/* Resize overlay line */\n.panel-nav__resize-handle::after {\n content: '';\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n width: var(--dimension-stroke-width-025, 2px);\n background: var(--color-foreground-primary);\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--effect-motion-short-2, 100ms ease-in-out);\n}\n\n.panel-nav__resize-handle--overlay::after {\n opacity: 1;\n}\n\n/* ── Reduced motion ──────────────────────────────────────────────────────── */\n\n@media (prefers-reduced-motion: reduce) {\n .panel-nav,\n .panel-nav *,\n .panel-nav *::before,\n .panel-nav *::after,\n .panel-nav__resize-handle,\n .panel-nav__resize-handle::before,\n .panel-nav__resize-handle::after {\n /* Near-instant — browsers treat 0 as \"no transition\" and can skip the end state. */\n transition-duration: var(--effect-animation-duration-instant) !important;\n animation-duration: var(--effect-animation-duration-instant) !important;\n transition-delay: var(--effect-animation-duration-instant) !important;\n }\n}\n","import {\n Component,\n Prop,\n Event,\n EventEmitter,\n Watch,\n State,\n Element,\n Method,\n h,\n Host,\n} from '@stencil/core';\nimport type { ChromeTransitionDetail } from '../../shell/chrome-transition';\nimport type { NavChromeStyle } from '../../shell/nav-chrome';\nimport {\n deriveActiveIdFromUrl,\n panelNavWidthTransitionMs,\n parsePanelNavGroups,\n resolvePanelNavDisableVt,\n resolvePanelNavStyle,\n resolvePanelNavToggle,\n shouldResyncPanelNavGroups,\n shouldResyncPanelNavStyle,\n} from './panel-nav-utils';\nimport {\n PANEL_NAV_USER_MENU_ANCHOR_ID,\n PANEL_NAV_USER_MENU_PLACEMENT,\n type PanelNavGroup,\n type PanelNavItem,\n type PanelNavRouterMode,\n type PanelNavUserActionDetail,\n} from './panel-nav-types';\nimport { scrollEdgeFadeClassMap } from '../../utils/scroll-edge-fade';\nimport { prefersReducedMotion } from '../../utils/resolve-css-time-ms';\n\n@Component({\n tag: 'ds-panel-nav',\n styleUrl: 'PanelNav.css',\n styleUrls: ['../../utils/scroll-edge-fade.css'],\n scoped: true,\n})\nexport class PanelNav {\n /** Style slot: `dashboard` or `settings`. Colors match for now;\n * class hooks reserved for texture/glyph layers. Property: `navStyle`. Attribute: `nav-style`. */\n @Prop({ attribute: 'nav-style', reflect: true }) navStyle: NavChromeStyle = 'dashboard';\n\n /** When `true`, style changes apply synchronously — host app owns view transitions. */\n @Prop() disableViewTransition: boolean = false;\n\n /** Nav groups — set via JS property (`el.groups = [...]`) or JSON string attribute. */\n @Prop() groups: string | PanelNavGroup[] = '[]';\n\n /** How items with `href` render:\n * - `anchor` (default): native `<a href>` — works with routers that intercept anchors.\n * - `event`: always `<button>`; host handles navigation via `dsNavSelect`. */\n @Prop() routerMode: PanelNavRouterMode = 'anchor';\n\n /** ID of the currently active/selected nav item. Overridden by `currentUrl` matching when set. */\n @Prop() activeId: string = '';\n\n /** Whether the nav is in collapsed (icon-only) state.\n * Set `storageKey` to persist across reloads. `dsNavToggle` still fires on change. */\n @Prop({ mutable: true }) collapsed: boolean = false;\n\n /** Viewport width (px) below which the nav locks in icon-only mode. 0 = disabled. */\n @Prop() breakpoint: number = 0;\n\n /** `localStorage` key used to persist the collapsed state across page loads.\n * When set, collapsed state is restored on mount and written on each toggle. */\n @Prop() storageKey: string = '';\n\n /** Current route URL (e.g. `window.location.pathname` or the router's active URL).\n * When set the component derives the active item by matching item `href` values\n * against this string (longest segment-boundary prefix wins), overriding `activeId`. */\n @Prop() currentUrl: string = '';\n\n /** Display name for the footer user section */\n @Prop() userName: string = '';\n\n /** Single character shown in the collapsed avatar */\n @Prop() userInitial: string = '';\n\n @Prop() dashboardLabel: string = 'Dashboard';\n @Prop() settingsLabel: string = 'Settings';\n @Prop() accountLabel: string = 'Account';\n /** Keep the account-menu trigger visually active while its popup is open or closing. */\n @Prop() accountMenuExpanded: boolean = false;\n @Prop() dashboardNavigationLabel: string = 'Dashboard navigation';\n @Prop() settingsNavigationLabel: string = 'Settings navigation';\n @Prop() expandNavigationLabel: string = 'Expand navigation';\n @Prop() collapseNavigationLabel: string = 'Collapse navigation';\n /** Localized name for the body when overflow makes it a keyboard-scrollable region. */\n @Prop() navigationItemsLabel: string = 'Navigation items';\n\n /** Emitted when a nav item is clicked. Detail = the item's `id`. */\n @Event() dsNavSelect!: EventEmitter<string>;\n\n /** Emitted when the collapse toggle is clicked. Detail = new collapsed state. */\n @Event() dsNavToggle!: EventEmitter<boolean>;\n\n /** Bubbling lifecycle — `ds-app-shell` pauses chrome metrics during width motion. */\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionStart!: EventEmitter<ChromeTransitionDetail>;\n\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionEnd!: EventEmitter<ChromeTransitionDetail>;\n\n /** Emitted when the footer left button (gear / dashboard) is clicked. */\n @Event() dsNavFooterAction!: EventEmitter<void>;\n\n /** Emitted when the footer user button is clicked. Detail includes the anchor for `ds-menu`. */\n @Event() dsNavUserAction!: EventEmitter<PanelNavUserActionDetail>;\n\n @Element() el!: HTMLElement;\n\n @State() private renderedStyle: NavChromeStyle = 'dashboard';\n @State() private parsedGroups: PanelNavGroup[] = [];\n @State() private isAnimating = false;\n @State() private rovingIndex: number = 0;\n @State() private viewportNarrow: boolean = false;\n @State() private urlDerivedActiveId: string = '';\n @State() private bodyScrollable = false;\n\n private transitionCompletionHandler?: (e: TransitionEvent) => void;\n private transitionFallbackTimer: number | null = null;\n private resizeObserver?: ResizeObserver;\n private scrollRegionObserver?: ResizeObserver;\n private bodyEl?: HTMLElement;\n private initialRenderComplete = false;\n\n // Drag-to-resize state (not @State — no re-render needed)\n private isDragging = false;\n private dragStartX = 0;\n private didSnap = false;\n private lastDeltaX = 0;\n private edgeOverlayTimer: number | null = null;\n private globalMouseMoveHandler?: (e: MouseEvent) => void;\n private globalMouseUpHandler?: () => void;\n\n @State() private showEdgeOverlay = false;\n\n private static readonly HOST_PROP_SYNC_BUDGET = 8;\n\n /** The ID that should be treated as active. `currentUrl` matching takes precedence\n * over `activeId` when both are present. */\n private get effectiveActiveId(): string {\n return this.urlDerivedActiveId || this.activeId;\n }\n\n private get effectiveDisableViewTransition(): boolean {\n return resolvePanelNavDisableVt(\n this.disableViewTransition,\n this.el.getAttribute('disable-view-transition')\n );\n }\n\n @Watch('collapsed')\n onCollapsedChange(_next: boolean, prev: boolean | undefined) {\n if (prev === undefined || !this.initialRenderComplete) return;\n this.startCollapseAnimation();\n }\n\n @Watch('viewportNarrow')\n onViewportNarrowChange(next: boolean, prev: boolean | undefined) {\n if (prev === undefined || !this.initialRenderComplete) return;\n if (next && this.rovingIndex === 0) {\n this.rovingIndex = this.getFirstRovingIndex();\n }\n this.startCollapseAnimation();\n }\n\n @Watch('breakpoint')\n onBreakpointChange() {\n this.disconnectResizeObserver();\n if (this.effectiveBreakpoint() > 0) this.connectResizeObserver();\n }\n\n @Watch('groups')\n onGroupsChange(val: string | PanelNavGroup[]) {\n this.parsedGroups = parsePanelNavGroups(val);\n this.rovingIndex = 0;\n this.syncActiveFromUrl();\n }\n\n @Watch('activeId')\n @Watch('urlDerivedActiveId')\n onActiveIdChange() {\n /* Active route updates aria-current only — roving tab stop stays user-controlled. */\n }\n\n @Watch('currentUrl')\n onCurrentUrlChange() {\n this.syncActiveFromUrl();\n }\n\n @Watch('navStyle')\n onNavStyleChange(newVal: NavChromeStyle) {\n this.renderedStyle = newVal;\n }\n\n componentWillLoad() {\n this.renderedStyle = resolvePanelNavStyle(this.navStyle, this.el.getAttribute('nav-style'));\n this.viewportNarrow = this.isViewportNarrow();\n if (this.storageKey) {\n try {\n const stored = localStorage.getItem(this.storageKey);\n if (stored !== null) this.collapsed = stored === 'true';\n } catch {\n /* localStorage unavailable */\n }\n }\n this.onGroupsChange(this.groups);\n this.syncActiveFromUrl();\n }\n\n componentDidLoad() {\n this.syncHostPropsIfNeeded();\n this.scheduleDeferredHostPropSync();\n if (this.effectiveBreakpoint() > 0) this.connectResizeObserver();\n this.connectScrollRegionObserver();\n this.updateBodyScrollable();\n this.initialRenderComplete = true;\n }\n\n componentDidRender() {\n this.updateBodyScrollable();\n }\n\n disconnectedCallback() {\n this.disconnectResizeObserver();\n this.disconnectScrollRegionObserver();\n this.clearCollapseAnimationCompletion(\n this.el.querySelector('.panel-nav') as HTMLElement | null\n );\n this.clearEdgeOverlayTimer();\n if (this.globalMouseMoveHandler) {\n window.removeEventListener('mousemove', this.globalMouseMoveHandler);\n }\n if (this.globalMouseUpHandler) {\n window.removeEventListener('mouseup', this.globalMouseUpHandler);\n }\n }\n\n private startCollapseAnimation() {\n if (this.isAnimating) return;\n this.isAnimating = true;\n this.dsChromeTransitionStart.emit({ source: 'panel-nav' });\n const panel = this.el.querySelector('.panel-nav') as HTMLElement | null;\n const widthBefore = panel?.getBoundingClientRect().width ?? 0;\n this.clearCollapseAnimationCompletion(panel);\n this.transitionCompletionHandler = (e: TransitionEvent) => {\n if (e.propertyName === 'width' || e.propertyName === 'min-width') {\n this.finishCollapseAnimation(panel);\n }\n };\n panel?.addEventListener('transitionend', this.transitionCompletionHandler);\n panel?.addEventListener('transitioncancel', this.transitionCompletionHandler);\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (!this.isAnimating) return;\n const widthAfter = panel?.getBoundingClientRect().width ?? 0;\n if (Math.abs(widthAfter - widthBefore) < 1) {\n this.finishCollapseAnimation(panel);\n return;\n }\n if (!panel) {\n this.finishCollapseAnimation(panel);\n return;\n }\n\n const fallbackMs = panelNavWidthTransitionMs(getComputedStyle(panel));\n if (fallbackMs <= 0) {\n this.finishCollapseAnimation(panel);\n return;\n }\n this.transitionFallbackTimer = window.setTimeout(() => {\n this.finishCollapseAnimation(panel);\n }, fallbackMs);\n });\n });\n }\n\n private finishCollapseAnimation(panel: HTMLElement | null) {\n const wasAnimating = this.isAnimating;\n this.clearCollapseAnimationCompletion(panel);\n if (!wasAnimating) return;\n this.isAnimating = false;\n this.dsChromeTransitionEnd.emit({ source: 'panel-nav' });\n }\n\n private clearCollapseAnimationCompletion(panel: HTMLElement | null) {\n if (this.transitionCompletionHandler) {\n panel?.removeEventListener('transitionend', this.transitionCompletionHandler);\n panel?.removeEventListener('transitioncancel', this.transitionCompletionHandler);\n this.transitionCompletionHandler = undefined;\n }\n if (this.transitionFallbackTimer !== null) {\n window.clearTimeout(this.transitionFallbackTimer);\n this.transitionFallbackTimer = null;\n }\n }\n\n private effectiveBreakpoint(): number {\n if (this.breakpoint > 0) return this.breakpoint;\n const attributeValue = Number(this.el.getAttribute('breakpoint') ?? 0);\n return Number.isFinite(attributeValue) && attributeValue > 0 ? attributeValue : 0;\n }\n\n private isViewportNarrow(): boolean {\n const breakpoint = this.effectiveBreakpoint();\n return typeof window !== 'undefined' && breakpoint > 0 && window.innerWidth < breakpoint;\n }\n\n private connectResizeObserver() {\n this.viewportNarrow = this.isViewportNarrow();\n this.resizeObserver = new ResizeObserver(() => {\n this.viewportNarrow = this.isViewportNarrow();\n });\n this.resizeObserver.observe(document.documentElement);\n }\n\n private disconnectResizeObserver() {\n this.resizeObserver?.disconnect();\n this.resizeObserver = undefined;\n }\n\n private connectScrollRegionObserver() {\n this.disconnectScrollRegionObserver();\n if (!this.bodyEl || typeof ResizeObserver === 'undefined') return;\n this.scrollRegionObserver = new ResizeObserver(() => this.updateBodyScrollable());\n this.scrollRegionObserver.observe(this.bodyEl);\n }\n\n private disconnectScrollRegionObserver() {\n this.scrollRegionObserver?.disconnect();\n this.scrollRegionObserver = undefined;\n }\n\n private updateBodyScrollable() {\n const next = Boolean(this.bodyEl && this.bodyEl.scrollHeight > this.bodyEl.clientHeight + 1);\n if (next !== this.bodyScrollable) this.bodyScrollable = next;\n }\n\n /** Derive active ID from the current URL by matching item `href` values.\n * Uses longest segment-boundary prefix match. */\n private syncActiveFromUrl() {\n if (!this.currentUrl) {\n this.urlDerivedActiveId = '';\n return;\n }\n this.urlDerivedActiveId = deriveActiveIdFromUrl(this.currentUrl, this.getAllItems());\n }\n\n /** Re-parse props assigned by the host after componentWillLoad (Angular ngAfterViewInit). */\n private syncHostPropsIfNeeded() {\n if (shouldResyncPanelNavStyle(this.renderedStyle, this.navStyle)) {\n this.renderedStyle = this.navStyle;\n }\n\n if (shouldResyncPanelNavGroups(this.parsedGroups, this.groups)) {\n this.onGroupsChange(this.groups);\n } else if (this.currentUrl) {\n this.syncActiveFromUrl();\n }\n }\n\n /** Poll across animation frames — host props may land without triggering @Watch. */\n private scheduleDeferredHostPropSync() {\n let remaining = PanelNav.HOST_PROP_SYNC_BUDGET;\n const tick = () => {\n this.syncHostPropsIfNeeded();\n if (--remaining > 0) {\n requestAnimationFrame(tick);\n }\n };\n queueMicrotask(tick);\n }\n\n /** Centralised toggle: updates the desktop preference unless breakpoint-locked. */\n private applyToggle(next: boolean) {\n if (this.viewportNarrow) return;\n this.collapsed = next;\n if (this.storageKey) {\n try {\n localStorage.setItem(this.storageKey, String(next));\n } catch {\n /* unavailable */\n }\n }\n this.dsNavToggle.emit(next);\n }\n\n private getAllItems(): PanelNavItem[] {\n return this.parsedGroups.flatMap(g => g.items);\n }\n\n private getFirstRovingIndex(): number {\n return this.viewportNarrow ? 1 : 0;\n }\n\n private getFooterRovingIndex(): number {\n return 1 + this.getAllItems().length;\n }\n\n private getUserRovingIndex(): number {\n return this.getFooterRovingIndex() + 1;\n }\n\n private getRovingElement(index: number): HTMLElement | null {\n const itemCount = this.getAllItems().length;\n if (index === 0) {\n return this.viewportNarrow ? null : this.el.querySelector('.panel-nav__header-btn');\n }\n if (index >= 1 && index <= itemCount) {\n const items = this.el.querySelectorAll<HTMLElement>('.panel-nav__body .panel-nav__item');\n return items[index - 1] ?? null;\n }\n if (index === this.getFooterRovingIndex()) {\n return this.el.querySelector('.panel-nav__footer-btn .button-unfilled');\n }\n if (index === this.getUserRovingIndex()) {\n return this.el.querySelector('.panel-nav__footer-user');\n }\n return null;\n }\n\n private focusRovingAt(index: number) {\n this.rovingIndex = index;\n this.getRovingElement(index)?.focus({ preventScroll: true });\n }\n\n private activateRovingIndex(index: number) {\n const itemCount = this.getAllItems().length;\n const items = this.getAllItems();\n if (index === 0) {\n this.handleToggle();\n return;\n }\n if (index >= 1 && index <= itemCount) {\n this.handleItemClick(items[index - 1].id);\n return;\n }\n if (index === this.getFooterRovingIndex()) {\n this.handleFooterAction();\n return;\n }\n if (index === this.getUserRovingIndex()) {\n const anchor = this.el.querySelector(\n `#${PANEL_NAV_USER_MENU_ANCHOR_ID}`\n ) as HTMLElement | null;\n if (anchor)\n this.dsNavUserAction.emit({ anchor, menuPlacement: PANEL_NAV_USER_MENU_PLACEMENT });\n }\n }\n\n private handleRovingKeyDown(e: KeyboardEvent, index: number) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this.activateRovingIndex(index);\n return;\n }\n\n const footerIdx = this.getFooterRovingIndex();\n const userIdx = this.getUserRovingIndex();\n const firstIdx = this.getFirstRovingIndex();\n let next: number | undefined;\n\n switch (e.key) {\n case 'ArrowDown':\n if (index === userIdx) return;\n if (index === footerIdx) next = userIdx;\n else next = index + 1;\n break;\n case 'ArrowUp':\n if (index === firstIdx) return;\n if (index === userIdx) next = footerIdx;\n else if (index === footerIdx) next = footerIdx - 1;\n else next = index - 1;\n break;\n case 'ArrowRight':\n if (index === footerIdx) next = userIdx;\n else return;\n break;\n case 'ArrowLeft':\n if (index === userIdx) next = footerIdx;\n else return;\n break;\n case 'Home':\n e.preventDefault();\n next = firstIdx;\n break;\n case 'End':\n e.preventDefault();\n next = userIdx;\n break;\n default:\n return;\n }\n\n if (next === undefined || next === index) return;\n e.preventDefault();\n this.focusRovingAt(next);\n }\n\n private handleItemClick(id: string) {\n this.dsNavSelect.emit(id);\n }\n\n private handleToggle() {\n const next = resolvePanelNavToggle(this.collapsed, this.viewportNarrow);\n if (next !== null) this.applyToggle(next);\n }\n\n /** Toggle the desktop preference. No-op while the breakpoint locks the panel collapsed. */\n @Method()\n async toggleCollapsed() {\n this.handleToggle();\n }\n\n private handleFooterAction() {\n this.dsNavFooterAction.emit();\n }\n\n private handleUserAction(e: MouseEvent) {\n const anchor = e.currentTarget as HTMLElement;\n this.dsNavUserAction.emit({ anchor, menuPlacement: PANEL_NAV_USER_MENU_PLACEMENT });\n }\n\n private clearEdgeOverlayTimer() {\n if (this.edgeOverlayTimer !== null) {\n window.clearTimeout(this.edgeOverlayTimer);\n this.edgeOverlayTimer = null;\n }\n }\n\n private handleResizeHandleMouseEnter() {\n this.clearEdgeOverlayTimer();\n if (prefersReducedMotion()) {\n this.showEdgeOverlay = true;\n return;\n }\n this.edgeOverlayTimer = window.setTimeout(() => {\n this.showEdgeOverlay = true;\n this.edgeOverlayTimer = null;\n }, 500);\n }\n\n private handleResizeHandleMouseLeave() {\n if (this.isDragging) return;\n this.clearEdgeOverlayTimer();\n this.showEdgeOverlay = false;\n }\n\n private handleResizeHandleMouseDown(e: MouseEvent) {\n e.preventDefault();\n e.stopPropagation();\n\n const wasCollapsed = this.collapsed || this.viewportNarrow;\n this.isDragging = true;\n this.dragStartX = e.clientX;\n this.didSnap = false;\n this.lastDeltaX = 0;\n this.clearEdgeOverlayTimer();\n this.showEdgeOverlay = false;\n\n document.body.style.cursor = 'ew-resize';\n document.body.style.userSelect = 'none';\n\n const onMove = (ev: MouseEvent) => {\n const deltaX = ev.clientX - this.dragStartX;\n this.lastDeltaX = Math.abs(deltaX);\n if (this.didSnap) return;\n if (!wasCollapsed && deltaX < -8) {\n this.applyToggle(true);\n this.didSnap = true;\n } else if (wasCollapsed && deltaX > 8) {\n this.applyToggle(false);\n this.didSnap = true;\n }\n };\n\n const onUp = () => {\n if (!this.didSnap && this.lastDeltaX < 3) {\n this.applyToggle(!wasCollapsed);\n }\n this.isDragging = false;\n this.showEdgeOverlay = false;\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n this.globalMouseMoveHandler = undefined;\n this.globalMouseUpHandler = undefined;\n };\n\n this.globalMouseMoveHandler = onMove;\n this.globalMouseUpHandler = onUp;\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n }\n\n private renderFooterAction(isDashboardChrome: boolean) {\n // Short chrome labels — same string for tip + aria (not \"Open/Go to …\").\n const footerLabel = isDashboardChrome ? this.settingsLabel : this.dashboardLabel;\n return (\n <ds-tooltip label={footerLabel} side=\"right\" size=\"sm\">\n <ds-button-unfilled\n variant=\"icon\"\n class=\"panel-nav__footer-btn\"\n icon={isDashboardChrome ? 'Gear' : 'Dashboard'}\n activeFill={false}\n hasBorder={false}\n focusTabIndex={this.rovingIndex === this.getFooterRovingIndex() ? 0 : -1}\n aria-label={footerLabel}\n onDsClick={() => this.handleFooterAction()}\n onKeyDown={(e: KeyboardEvent) => this.handleRovingKeyDown(e, this.getFooterRovingIndex())}\n onFocusin={() => {\n this.rovingIndex = this.getFooterRovingIndex();\n }}\n />\n </ds-tooltip>\n );\n }\n\n private renderFooterUser(collapsed: boolean, userName: string, userInitial: string) {\n // Always wrap so collapse morph CSS can transition on a stable button node.\n // Tip only when collapsed (empty label → ds-tooltip skips show).\n const tipLabel = collapsed ? this.accountLabel : '';\n const userButton = (\n <button\n type=\"button\"\n id={PANEL_NAV_USER_MENU_ANCHOR_ID}\n class={{\n 'panel-nav__item': true,\n 'panel-nav__item--active': this.accountMenuExpanded,\n 'panel-nav__footer-user': true,\n 'panel-nav__footer-user--menu-expanded': this.accountMenuExpanded,\n 'ds-focus-ring-inset': true,\n 'ds-interaction-fill': true,\n }}\n tabIndex={this.rovingIndex === this.getUserRovingIndex() ? 0 : -1}\n aria-label={this.accountLabel}\n aria-haspopup=\"menu\"\n aria-expanded={String(this.accountMenuExpanded)}\n onClick={e => this.handleUserAction(e)}\n onKeyDown={(e: KeyboardEvent) => this.handleRovingKeyDown(e, this.getUserRovingIndex())}\n onFocus={() => {\n this.rovingIndex = this.getUserRovingIndex();\n }}\n >\n <ds-text\n class=\"panel-nav__item-label panel-nav__footer-user-label panel-nav__item-label-text ds-control--md\"\n as=\"span\"\n variant=\"text-body-medium\"\n emphasis\n color=\"inherit\"\n >\n {userName}\n </ds-text>\n <span class=\"panel-nav__item-icon panel-nav__footer-user-icon\" aria-hidden=\"true\">\n <span class=\"panel-nav__footer-icon-expanded\">\n <ds-icon name=\"ChevronUpDown\" size=\"md\" color=\"inherit\" />\n </span>\n <span class=\"panel-nav__footer-icon-collapsed\">\n <ds-icon name=\"Circle\" size=\"md\" color=\"inherit\" />\n <ds-text\n class=\"panel-nav__user-initial\"\n as=\"span\"\n variant=\"text-caption\"\n emphasis\n align=\"center\"\n color=\"inherit\"\n >\n {userInitial}\n </ds-text>\n </span>\n </span>\n </button>\n );\n\n return (\n <ds-tooltip label={tipLabel} side=\"right\" size=\"sm\">\n {userButton}\n </ds-tooltip>\n );\n }\n\n private renderNavItem(item: PanelNavItem, idx: number, collapsed: boolean) {\n const isActive = item.id === this.effectiveActiveId;\n const rovingPosition = idx + 1;\n\n const itemContent = [\n <span class=\"panel-nav__item-icon\">\n <ds-icon name={item.icon} size=\"md\" color=\"inherit\" />\n </span>,\n <ds-text\n class=\"panel-nav__item-label panel-nav__item-label-text ds-control--md\"\n as=\"span\"\n variant=\"text-body-medium\"\n emphasis={isActive}\n color=\"inherit\"\n >\n {item.label}\n </ds-text>,\n item.dot && (\n <span class=\"panel-nav__item-dot-box\" aria-hidden=\"true\">\n <ds-badge class=\"panel-nav__item-dot\" variant=\"dot\" hasRing={collapsed} label=\"\" />\n </span>\n ),\n ];\n\n const sharedProps = {\n class: {\n 'panel-nav__item': true,\n 'panel-nav__item--active': isActive,\n 'ds-focus-ring-inset': true,\n 'ds-interaction-fill': true,\n },\n 'aria-current': isActive ? ('page' as const) : undefined,\n tabIndex: rovingPosition === this.rovingIndex ? 0 : -1,\n onClick: () => this.handleItemClick(item.id),\n onKeyDown: (e: KeyboardEvent) => this.handleRovingKeyDown(e, rovingPosition),\n onFocus: () => {\n this.rovingIndex = rovingPosition;\n },\n };\n\n const useAnchor = this.routerMode === 'anchor' && item.href;\n const control = useAnchor ? (\n <a {...sharedProps} href={item.href}>\n {itemContent}\n </a>\n ) : (\n <button {...sharedProps} type=\"button\">\n {itemContent}\n </button>\n );\n\n // Always wrap so label fade CSS can transition on a stable item node.\n // Tip only when collapsed (empty label → ds-tooltip skips show).\n return (\n <ds-tooltip label={collapsed ? item.label : ''} side=\"right\" size=\"sm\">\n {control}\n </ds-tooltip>\n );\n }\n\n render() {\n const chromeStyle = this.renderedStyle;\n const isDashboardChrome = chromeStyle === 'dashboard';\n const isDashboard = this.navStyle === 'dashboard';\n const collapsed = this.collapsed || this.viewportNarrow;\n const { userName, userInitial } = this;\n\n const navCls: Record<string, boolean> = {\n 'panel-nav': true,\n 'panel-nav--dashboard': isDashboard,\n 'panel-nav--settings': !isDashboard,\n 'panel-nav--collapsed': collapsed,\n 'panel-nav--breakpoint-locked': this.viewportNarrow,\n 'panel-nav--animating': this.isAnimating,\n };\n\n return (\n <Host style={{ display: 'block', position: 'relative' }}>\n <nav\n class={navCls}\n aria-label={\n isDashboardChrome ? this.dashboardNavigationLabel : this.settingsNavigationLabel\n }\n >\n {/* ── Header: Motive logo, reveals collapse toggle on hover ── */}\n <div class=\"panel-nav__header\">\n <button\n type=\"button\"\n class=\"panel-nav__header-btn ds-focus-ring-inset ds-interaction-fill\"\n disabled={this.viewportNarrow}\n tabIndex={!this.viewportNarrow && this.rovingIndex === 0 ? 0 : -1}\n onClick={() => this.handleToggle()}\n onKeyDown={(e: KeyboardEvent) => this.handleRovingKeyDown(e, 0)}\n onFocus={() => {\n this.rovingIndex = 0;\n }}\n aria-label={collapsed ? this.expandNavigationLabel : this.collapseNavigationLabel}\n aria-expanded={collapsed ? 'false' : 'true'}\n >\n {/* Motive M mark — fades out on hover to reveal collapse toggle */}\n <span class=\"panel-nav__header-logo\" aria-hidden=\"true\">\n <svg\n class=\"panel-nav__m-mark\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n focusable=\"false\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M11.1159 4.31537H7.67021L2.53401 13.0703H0V15.6875H4.02716L8.24319 8.49978V15.6846H11.6342L15.8289 8.47829V15.6846H18.7122V4.3125H15.2559L11.1159 11.3648V4.31537Z\"\n fill=\"currentColor\"\n />\n </svg>\n </span>\n {/* Collapse / expand icon — revealed on hover via CSS */}\n <span class=\"panel-nav__header-toggle\" aria-hidden=\"true\">\n <ds-icon\n name={collapsed ? 'LeftExpandB' : 'LeftCollapseB'}\n size=\"md\"\n color=\"inherit\"\n />\n </span>\n </button>\n </div>\n\n {/* ── Scrollable body ── */}\n <div\n class={{\n 'panel-nav__body': true,\n 'ds-focus-ring': this.bodyScrollable,\n ...scrollEdgeFadeClassMap({ edges: 'bottom' }),\n }}\n ref={element => {\n const next = (element as HTMLElement) ?? undefined;\n if (next === this.bodyEl) return;\n this.bodyEl = next;\n this.connectScrollRegionObserver();\n }}\n role={this.bodyScrollable ? 'region' : undefined}\n aria-label={this.bodyScrollable ? this.navigationItemsLabel : undefined}\n tabIndex={this.bodyScrollable ? 0 : undefined}\n >\n {(() => {\n let flatIdx = 0;\n return this.parsedGroups.map(group => (\n <div class=\"panel-nav__group\">\n {group.label && (\n <ds-text\n class=\"panel-nav__group-label ds-control--md\"\n as=\"span\"\n variant=\"text-caption\"\n emphasis\n color=\"inherit\"\n >\n {group.label}\n </ds-text>\n )}\n {group.items.map(item => this.renderNavItem(item, flatIdx++, collapsed))}\n </div>\n ));\n })()}\n </div>\n\n {/* ── Footer ── */}\n <div class=\"panel-nav__footer\">\n {this.renderFooterAction(isDashboardChrome)}\n {this.renderFooterUser(collapsed, userName, userInitial)}\n </div>\n </nav>\n\n {/* Drag-to-resize handle — always rendered, hidden only when auto-collapsed by breakpoint */}\n {!this.viewportNarrow && (\n <div\n class={{\n 'panel-nav__resize-handle': true,\n 'panel-nav__resize-handle--overlay': this.showEdgeOverlay,\n }}\n onMouseEnter={() => this.handleResizeHandleMouseEnter()}\n onMouseLeave={() => this.handleResizeHandleMouseLeave()}\n onMouseDown={(e: MouseEvent) => this.handleResizeHandleMouseDown(e)}\n aria-hidden=\"true\"\n />\n )}\n </Host>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["scrollEdgeFadeClassMap","opts","edges","Array","isArray","classes","scrollAware","hidden","edge","atEnd","PANEL_NAV_USER_MENU_PLACEMENT","side","align","anchorAlignment","sideOffset","alignOffset","NAV_STYLE_HINT_ATTR","readNavStyleAttr","attr","undefined","resolveNavChromeStyle","styleProp","hostAttr","docHint","fromHost","hint","document","documentElement","getAttribute","fromHint","shouldResyncNavChromeStyle","renderedStyle","style","hrefMatchesPath","path","href","startsWith","deriveActiveIdFromUrl","items","bestId","bestLen","item","length","id","countPanelNavItems","groups","reduce","sum","g","resolvePanelNavToggle","collapsed","viewportNarrow","parseCssTimeList","value","split","map","parseCssTimeMs","trim","panelNavWidthTransitionMs","properties","transitionProperty","durations","transitionDuration","delays","transitionDelay","max","property","index","duration","delay","Math","parsePanelNavGroups","parsed","JSON","parse","resolvePanelNavDisableVt","prop","shouldResyncPanelNavGroups","parsedGroups","PANEL_NAV_USER_MENU_ANCHOR_ID","scrollEdgeFadeCss","panelNavCss","PanelNav","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","navStyle","disableViewTransition","routerMode","activeId","breakpoint","storageKey","currentUrl","userName","userInitial","dashboardLabel","settingsLabel","accountLabel","accountMenuExpanded","dashboardNavigationLabel","settingsNavigationLabel","expandNavigationLabel","collapseNavigationLabel","navigationItemsLabel","isAnimating","rovingIndex","urlDerivedActiveId","bodyScrollable","transitionFallbackTimer","initialRenderComplete","isDragging","dragStartX","didSnap","lastDeltaX","edgeOverlayTimer","showEdgeOverlay","effectiveActiveId","effectiveDisableViewTransition","el","onCollapsedChange","_next","prev","startCollapseAnimation","onViewportNarrowChange","next","getFirstRovingIndex","onBreakpointChange","disconnectResizeObserver","effectiveBreakpoint","connectResizeObserver","onGroupsChange","val","syncActiveFromUrl","onActiveIdChange","onCurrentUrlChange","onNavStyleChange","newVal","componentWillLoad","resolvePanelNavStyle","isViewportNarrow","stored","localStorage","getItem","componentDidLoad","syncHostPropsIfNeeded","scheduleDeferredHostPropSync","connectScrollRegionObserver","updateBodyScrollable","componentDidRender","disconnectedCallback","disconnectScrollRegionObserver","clearCollapseAnimationCompletion","querySelector","clearEdgeOverlayTimer","globalMouseMoveHandler","window","removeEventListener","globalMouseUpHandler","dsChromeTransitionStart","emit","source","panel","widthBefore","getBoundingClientRect","width","transitionCompletionHandler","e","propertyName","finishCollapseAnimation","addEventListener","requestAnimationFrame","widthAfter","abs","fallbackMs","getComputedStyle","setTimeout","wasAnimating","dsChromeTransitionEnd","clearTimeout","attributeValue","Number","isFinite","innerWidth","resizeObserver","ResizeObserver","observe","disconnect","bodyEl","scrollRegionObserver","Boolean","scrollHeight","clientHeight","getAllItems","shouldResyncPanelNavStyle","remaining","HOST_PROP_SYNC_BUDGET","tick","queueMicrotask","applyToggle","setItem","String","dsNavToggle","flatMap","getFooterRovingIndex","getUserRovingIndex","getRovingElement","itemCount","querySelectorAll","focusRovingAt","focus","preventScroll","activateRovingIndex","handleToggle","handleItemClick","handleFooterAction","anchor","dsNavUserAction","menuPlacement","handleRovingKeyDown","key","preventDefault","footerIdx","userIdx","firstIdx","dsNavSelect","toggleCollapsed","dsNavFooterAction","handleUserAction","currentTarget","handleResizeHandleMouseEnter","prefersReducedMotion","handleResizeHandleMouseLeave","handleResizeHandleMouseDown","stopPropagation","wasCollapsed","clientX","body","cursor","userSelect","onMove","ev","deltaX","onUp","renderFooterAction","isDashboardChrome","footerLabel","h","label","size","variant","class","icon","activeFill","hasBorder","focusTabIndex","onDsClick","onKeyDown","onFocusin","renderFooterUser","tipLabel","userButton","type","tabIndex","onClick","onFocus","as","emphasis","color","name","renderNavItem","idx","isActive","rovingPosition","itemContent","dot","hasRing","sharedProps","useAnchor","control","render","chromeStyle","isDashboard","navCls","Host","display","position","disabled","viewBox","fill","xmlns","focusable","d","ref","element","role","flatIdx","group","onMouseEnter","onMouseLeave","onMouseDown"],"sources":["src/wc/utils/scroll-edge-fade.ts","src/wc/components/Menu/menu-placement.ts","src/wc/shell/nav-chrome.ts","src/wc/components/PanelNav/panel-nav-utils.ts","src/wc/components/PanelNav/panel-nav-types.ts","src/wc/utils/scroll-edge-fade.css?tag=ds-panel-nav&encapsulation=scoped","src/wc/components/PanelNav/PanelNav.css?tag=ds-panel-nav&encapsulation=scoped","src/wc/components/PanelNav/PanelNav.tsx"],"sourcesContent":["/** CSS custom property for scroll-edge fade depth (dimension token or length). */\nexport const SCROLL_EDGE_FADE_SIZE_VAR = '--ds-scroll-edge-fade-size';\n\nexport const SCROLL_EDGE_FADE_DEFAULT_SIZE = 'var(--dimension-size-600)';\n\nexport type ScrollEdgeFadeEdge = 'top' | 'bottom' | 'left' | 'right';\n\nexport type ScrollEdgeFadeSizeToken =\n | 'size-000'\n | 'size-050'\n | 'size-075'\n | 'size-100'\n | 'size-150'\n | 'size-200'\n | 'size-250'\n | 'size-300'\n | 'size-400'\n | 'size-500'\n | 'size-600'\n | 'size-800';\n\nexport type ScrollEdgeFadeSize = ScrollEdgeFadeSizeToken | (string & {});\n\nconst SIZE_VALUE: Record<ScrollEdgeFadeSizeToken, string> = {\n 'size-000': 'var(--dimension-size-000)',\n 'size-050': 'var(--dimension-size-050)',\n 'size-075': 'var(--dimension-size-075)',\n 'size-100': 'var(--dimension-size-100)',\n 'size-150': 'var(--dimension-size-150)',\n 'size-200': 'var(--dimension-size-200)',\n 'size-250': 'var(--dimension-size-250)',\n 'size-300': 'var(--dimension-size-300)',\n 'size-400': 'var(--dimension-size-400)',\n 'size-500': 'var(--dimension-size-500)',\n 'size-600': 'var(--dimension-size-600)',\n 'size-800': 'var(--dimension-size-800)',\n};\n\nconst SIZE_REF = `var(${SCROLL_EDGE_FADE_SIZE_VAR}, ${SCROLL_EDGE_FADE_DEFAULT_SIZE})`;\n\nexport interface ScrollEdgeFadeOptions {\n /** One or more edges to fade. */\n edges: ScrollEdgeFadeEdge | ScrollEdgeFadeEdge[];\n /**\n * Reveal and hide configured fades from the container's own scroll position.\n * Uses CSS scroll-driven animations with a static-fade fallback; no listener required.\n */\n scrollAware?: boolean;\n /**\n * When true, removes the complete mask. Pass a map to remove only configured\n * edges that are flush when scroll state already lives in JavaScript.\n */\n atEnd?: boolean | Partial<Record<ScrollEdgeFadeEdge, boolean>>;\n /** Force the mask off regardless of scroll position (`ds-fade` `visible={false}`). */\n hidden?: boolean;\n}\n\n/** Resolve a dimension size token name or raw CSS length for `--ds-scroll-edge-fade-size`. */\nexport function resolveScrollEdgeFadeSize(\n size: ScrollEdgeFadeSize = 'size-600',\n height?: string,\n): string {\n if (height) return height;\n return Object.prototype.hasOwnProperty.call(SIZE_VALUE, size)\n ? SIZE_VALUE[size as ScrollEdgeFadeSizeToken]\n : size;\n}\n\n/** Inline style map setting fade depth on a scroll container. */\nexport function scrollEdgeFadeSizeStyle(\n size: ScrollEdgeFadeSize = 'size-600',\n height?: string,\n): Record<string, string> {\n return {\n [SCROLL_EDGE_FADE_SIZE_VAR]: resolveScrollEdgeFadeSize(size, height),\n };\n}\n\n/**\n * Alpha mask for a scroll container edge — content fades to transparent so\n * textured backgrounds (shell gradients, images) show through.\n */\nexport function scrollEdgeFadeMaskImage(edge: ScrollEdgeFadeEdge): string {\n switch (edge) {\n case 'top':\n return `linear-gradient(to bottom, transparent 0, #000 ${SIZE_REF}, #000 100%)`;\n case 'bottom':\n return `linear-gradient(to bottom, #000 0, #000 calc(100% - ${SIZE_REF}), transparent 100%)`;\n case 'left':\n return `linear-gradient(to right, transparent 0, #000 ${SIZE_REF}, #000 100%)`;\n case 'right':\n return `linear-gradient(to right, #000 0, #000 calc(100% - ${SIZE_REF}), transparent 100%)`;\n }\n}\n\n/** Inline style map for a masked scroll region (single edge). */\nexport function scrollEdgeFadeMaskStyle(\n edge: ScrollEdgeFadeEdge,\n size: ScrollEdgeFadeSize = 'size-600',\n height?: string,\n): Record<string, string> {\n const mask = scrollEdgeFadeMaskImage(edge);\n return {\n ...scrollEdgeFadeSizeStyle(size, height),\n WebkitMaskImage: mask,\n maskImage: mask,\n WebkitMaskSize: '100% 100%',\n maskSize: '100% 100%',\n };\n}\n\n/** Class map for `scroll-edge-fade` modifiers — pair with `scroll-edge-fade.css`. */\nexport function scrollEdgeFadeClassMap(opts: ScrollEdgeFadeOptions): Record<string, boolean> {\n const edges = Array.isArray(opts.edges) ? opts.edges : [opts.edges];\n const classes: Record<string, boolean> = {\n 'scroll-edge-fade': true,\n 'scroll-edge-fade--scroll-aware': opts.scrollAware === true,\n 'scroll-edge-fade--hidden': opts.hidden === true,\n };\n\n for (const edge of edges) {\n classes[`scroll-edge-fade--${edge}`] = true;\n }\n\n const { atEnd } = opts;\n if (atEnd === true) {\n classes['scroll-edge-fade--at-end'] = true;\n } else if (atEnd && typeof atEnd === 'object') {\n for (const edge of edges) {\n if (atEnd[edge]) {\n classes[`scroll-edge-fade--${edge}-at-edge`] = true;\n }\n }\n }\n\n return classes;\n}\n\n/** Whether a scroll container is flush with the given edge (within `threshold` px). */\nexport function isScrollAtEdge(\n el: HTMLElement,\n edge: ScrollEdgeFadeEdge,\n threshold = 2,\n): boolean {\n switch (edge) {\n case 'top':\n return el.scrollTop <= threshold;\n case 'bottom':\n return el.scrollHeight - el.scrollTop - el.clientHeight <= threshold;\n case 'left':\n return el.scrollLeft <= threshold;\n case 'right':\n return el.scrollWidth - el.scrollLeft - el.clientWidth <= threshold;\n }\n}\n","import type { ChoicePopupAnchorAlignment } from '../../utils/choice-popup-alignment';\nimport type { MenuAlign, MenuSide } from './menu-position';\n\n/** Placement props bindable on `ds-menu` (side, align, offsets). */\nexport interface MenuPlacement {\n side: MenuSide;\n align: MenuAlign;\n anchorAlignment: ChoicePopupAnchorAlignment;\n sideOffset: number | string;\n alignOffset: number | string;\n}\n\n/**\n * Canonical `ds-menu` placement for the **panel-nav footer user menu**.\n * Anchor from `dsNavUserAction` detail `{ anchor }` (id `ds-panel-nav-user-menu-anchor`).\n *\n * Not for BarNav overflow menus — those use `side=\"bottom\"` / `align=\"end\"` internally.\n * `align: 'end'` bottom-aligns the last choice cell on the anchor; the side offset nudges into the content area:\n * - `sideOffset` space-100 + space-050 (12px): 4px past the panel-nav outer edge (anchor is ~8px inset).\n * - `anchorAlignment: 'choice-cell'`: the shared section inset puts the last row edge on the footer user row.\n */\nexport const PANEL_NAV_USER_MENU_PLACEMENT = {\n side: 'right',\n align: 'end',\n anchorAlignment: 'choice-cell',\n sideOffset: 'calc(var(--dimension-space-100) + var(--dimension-space-050))',\n alignOffset: 0,\n} as const satisfies MenuPlacement;\n\n/**\n * Canonical placement for a menu opened by a PanelTools header action.\n * Matches BarNav overflow: the 32px trigger is 8px inside the 48px header, so\n * a 12px side offset places the popup 4px below the header edge. Choice-cell\n * alignment keeps the final menu item edge aligned with the trigger.\n */\nexport const PANEL_TOOLS_HEADER_MENU_PLACEMENT = {\n side: 'bottom',\n align: 'end',\n anchorAlignment: 'choice-cell',\n sideOffset: 'calc(var(--dimension-space-100) + var(--dimension-space-050))',\n alignOffset: 0,\n} as const satisfies MenuPlacement;\n","/** Nav chrome style slot — dashboard vs settings. Colors unified for now; texture hooks use the class. */\nexport type NavChromeStyle = 'dashboard' | 'settings';\n\n/** Document hint for first paint before framework props land. */\nexport const NAV_STYLE_HINT_ATTR = 'data-nav-style';\n\nexport function readNavStyleAttr(attr: string | null): NavChromeStyle | undefined {\n if (attr === 'dashboard' || attr === 'settings') return attr;\n return undefined;\n}\n\nexport function setNavStyleHint(style: NavChromeStyle): void {\n if (typeof document === 'undefined') return;\n document.documentElement.setAttribute(NAV_STYLE_HINT_ATTR, style);\n}\n\nexport function clearNavStyleHint(): void {\n if (typeof document === 'undefined') return;\n document.documentElement.removeAttribute(NAV_STYLE_HINT_ATTR);\n}\n\nexport function resolveNavChromeStyle(\n styleProp: NavChromeStyle,\n hostAttr: string | null,\n docHint?: string | null,\n): NavChromeStyle {\n const fromHost = readNavStyleAttr(hostAttr);\n if (fromHost) return fromHost;\n\n const hint =\n docHint !== undefined\n ? docHint\n : typeof document !== 'undefined'\n ? document.documentElement.getAttribute(NAV_STYLE_HINT_ATTR)\n : null;\n const fromHint = readNavStyleAttr(hint);\n if (fromHint) return fromHint;\n\n return styleProp;\n}\n\nexport function shouldResyncNavChromeStyle(\n renderedStyle: NavChromeStyle,\n style: NavChromeStyle,\n): boolean {\n return renderedStyle !== style;\n}\n","import type { PanelNavGroup, PanelNavItem } from './panel-nav-types';\nimport { parseCssTimeMs } from '../../utils/resolve-css-time-ms';\nimport {\n NAV_STYLE_HINT_ATTR,\n clearNavStyleHint,\n readNavStyleAttr,\n resolveNavChromeStyle,\n setNavStyleHint,\n shouldResyncNavChromeStyle,\n} from '../../shell/nav-chrome';\n\nexport type { NavChromeStyle } from '../../shell/nav-chrome';\nexport {\n NAV_STYLE_HINT_ATTR,\n readNavStyleAttr,\n setNavStyleHint,\n clearNavStyleHint,\n resolveNavChromeStyle as resolvePanelNavStyle,\n shouldResyncNavChromeStyle as shouldResyncPanelNavStyle,\n};\n\n/** Whether `path` matches `href` at a segment boundary (exact or child path). */\nexport function hrefMatchesPath(path: string, href: string): boolean {\n return path === href || path.startsWith(`${href}/`);\n}\n\n/** Derive the active nav item id from a URL using longest segment-boundary prefix match. */\nexport function deriveActiveIdFromUrl(path: string, items: PanelNavItem[]): string {\n if (!path) return '';\n\n let bestId = '';\n let bestLen = 0;\n\n for (const item of items) {\n if (!item.href) continue;\n if (hrefMatchesPath(path, item.href) && item.href.length > bestLen) {\n bestId = item.id;\n bestLen = item.href.length;\n }\n }\n\n return bestId;\n}\n\n/** Total nav item count across all groups. */\nexport function countPanelNavItems(groups: PanelNavGroup[]): number {\n return groups.reduce((sum, g) => sum + g.items.length, 0);\n}\n\n/** Resolve a user toggle without mutating the persisted desktop preference while breakpoint-locked. */\nexport function resolvePanelNavToggle(\n collapsed: boolean,\n viewportNarrow: boolean,\n): boolean | null {\n return viewportNarrow ? null : !collapsed;\n}\n\nexport interface PanelNavTransitionStyle {\n transitionProperty: string;\n transitionDuration: string;\n transitionDelay: string;\n}\n\nfunction parseCssTimeList(value: string): number[] {\n return value.split(',').map(item => parseCssTimeMs(item.trim(), 0));\n}\n\n/**\n * Maximum computed time for PanelNav's width transition. CSS repeats shorter\n * duration/delay lists to match the property list, so mirror that behavior.\n */\nexport function panelNavWidthTransitionMs(style: PanelNavTransitionStyle): number {\n const properties = style.transitionProperty.split(',').map(item => item.trim());\n const durations = parseCssTimeList(style.transitionDuration);\n const delays = parseCssTimeList(style.transitionDelay);\n\n return properties.reduce((max, property, index) => {\n if (property !== 'all' && property !== 'width' && property !== 'min-width') return max;\n const duration = durations[index % durations.length] ?? 0;\n const delay = delays[index % delays.length] ?? 0;\n return Math.max(max, duration + delay, 0);\n }, 0);\n}\n\n/** Parse `groups` from either a JSON attribute string or a JS property array. */\nexport function parsePanelNavGroups(groups: string | unknown): PanelNavGroup[] {\n if (Array.isArray(groups)) return groups;\n if (typeof groups === 'string') {\n try {\n const parsed = JSON.parse(groups);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n }\n return [];\n}\n\n/** Resolve whether VT is disabled from prop and/or host attribute. */\nexport function resolvePanelNavDisableVt(prop: boolean, attr: string | null): boolean {\n if (prop) return true;\n if (attr === null) return false;\n return attr !== 'false';\n}\n\n/** True when host `groups` has items but internal parsed state is still empty. */\nexport function shouldResyncPanelNavGroups(\n parsedGroups: PanelNavGroup[],\n groups: string | unknown,\n): boolean {\n return (\n countPanelNavItems(parsedGroups) === 0 &&\n countPanelNavItems(parsePanelNavGroups(groups)) > 0\n );\n}\n","import type { MenuPlacement } from '../Menu/menu-placement';\nimport { PANEL_NAV_USER_MENU_PLACEMENT } from '../Menu/menu-placement';\nimport type { NavChromeStyle } from '../../shell/nav-chrome';\n\nexport type { NavChromeStyle };\nexport type PanelNavRouterMode = 'anchor' | 'event';\n\nexport interface PanelNavItem {\n id: string;\n icon: string;\n label: string;\n /** Show a notification dot badge on the item */\n dot?: boolean;\n /** Route path used for `currentUrl` matching. In `anchor` mode also sets `<a href>`.\n * In `event` mode navigation is delegated to the host via `dsNavSelect`. */\n href?: string;\n}\n\nexport interface PanelNavGroup {\n id?: string;\n label?: string;\n items: PanelNavItem[];\n}\n\n/** Stable `id` on the footer user button — use with `ds-menu` `anchor-id`. */\nexport const PANEL_NAV_USER_MENU_ANCHOR_ID = 'ds-panel-nav-user-menu-anchor';\n\n/** Detail for `dsNavUserAction` — anchor element for an external `ds-menu`. */\nexport interface PanelNavUserActionDetail {\n anchor: HTMLElement;\n /** Recommended `ds-menu` placement — spread onto external user menu instance. */\n menuPlacement: MenuPlacement;\n}\n\nexport { PANEL_NAV_USER_MENU_PLACEMENT };\n","/**\n * Scroll-edge alpha fade — the single edge-fade utility for overflow containers.\n * Masks scrolling content to transparent so flat surfaces, shell gradients, and\n * images show through.\n *\n * Static fades are the default (PanelNav intentionally keeps its bottom fade).\n * Add `.scroll-edge-fade--scroll-aware` to reveal/hide each configured edge from\n * the container's own scroll position without JavaScript. Browsers without CSS\n * scroll-driven animations retain the configured static fades.\n *\n * @see scroll-edge-fade.ts — `scrollEdgeFadeClassMap`, `isScrollAtEdge`, size helpers.\n */\n\n@property --ds-scroll-edge-fade-top {\n syntax: '<length-percentage>';\n inherits: false;\n /* stylelint-disable-next-line length-zero-no-unit -- @property requires a unit-compatible initial value. */\n initial-value: 0px;\n}\n\n@property --ds-scroll-edge-fade-bottom {\n syntax: '<length-percentage>';\n inherits: false;\n /* stylelint-disable-next-line length-zero-no-unit -- @property requires a unit-compatible initial value. */\n initial-value: 0px;\n}\n\n@property --ds-scroll-edge-fade-left {\n syntax: '<length-percentage>';\n inherits: false;\n /* stylelint-disable-next-line length-zero-no-unit -- @property requires a unit-compatible initial value. */\n initial-value: 0px;\n}\n\n@property --ds-scroll-edge-fade-right {\n syntax: '<length-percentage>';\n inherits: false;\n /* stylelint-disable-next-line length-zero-no-unit -- @property requires a unit-compatible initial value. */\n initial-value: 0px;\n}\n\n@keyframes ds-scroll-edge-fade-reveal-top {\n from { --ds-scroll-edge-fade-top: 0px; }\n to { --ds-scroll-edge-fade-top: var(--ds-scroll-edge-fade-target-top); }\n}\n\n@keyframes ds-scroll-edge-fade-reveal-bottom {\n from { --ds-scroll-edge-fade-bottom: var(--ds-scroll-edge-fade-target-bottom); }\n to { --ds-scroll-edge-fade-bottom: 0px; }\n}\n\n@keyframes ds-scroll-edge-fade-reveal-left {\n from { --ds-scroll-edge-fade-left: 0px; }\n to { --ds-scroll-edge-fade-left: var(--ds-scroll-edge-fade-target-left); }\n}\n\n@keyframes ds-scroll-edge-fade-reveal-right {\n from { --ds-scroll-edge-fade-right: var(--ds-scroll-edge-fade-target-right); }\n to { --ds-scroll-edge-fade-right: 0px; }\n}\n\n/* Mask gradients use #000 / transparent as alpha stops — not theme colors. */\n/* stylelint-disable color-no-hex */\n\n.scroll-edge-fade {\n --ds-scroll-edge-fade-size: var(--dimension-size-600);\n --ds-scroll-edge-fade-reveal-distance: var(--dimension-size-800);\n --ds-scroll-edge-fade-target-top: 0px;\n --ds-scroll-edge-fade-target-bottom: 0px;\n --ds-scroll-edge-fade-target-left: 0px;\n --ds-scroll-edge-fade-target-right: 0px;\n --ds-scroll-edge-fade-top: var(--ds-scroll-edge-fade-target-top);\n --ds-scroll-edge-fade-bottom: var(--ds-scroll-edge-fade-target-bottom);\n --ds-scroll-edge-fade-left: var(--ds-scroll-edge-fade-target-left);\n --ds-scroll-edge-fade-right: var(--ds-scroll-edge-fade-target-right);\n --ds-scroll-edge-fade-block-mask: linear-gradient(\n to bottom,\n transparent 0,\n #000 var(--ds-scroll-edge-fade-top),\n #000 calc(100% - var(--ds-scroll-edge-fade-bottom)),\n transparent 100%\n );\n --ds-scroll-edge-fade-inline-mask: linear-gradient(\n to right,\n transparent 0,\n #000 var(--ds-scroll-edge-fade-left),\n #000 calc(100% - var(--ds-scroll-edge-fade-right)),\n transparent 100%\n );\n\n /* Intersecting the block and inline masks lets every edge compose correctly. */\n -webkit-mask-image:\n var(--ds-scroll-edge-fade-block-mask),\n var(--ds-scroll-edge-fade-inline-mask);\n mask-image:\n var(--ds-scroll-edge-fade-block-mask),\n var(--ds-scroll-edge-fade-inline-mask);\n -webkit-mask-composite: source-in;\n mask-composite: intersect;\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n -webkit-mask-size: 100% 100%;\n mask-size: 100% 100%;\n}\n\n.scroll-edge-fade--top {\n --ds-scroll-edge-fade-target-top: var(--ds-scroll-edge-fade-size);\n}\n\n.scroll-edge-fade--bottom {\n --ds-scroll-edge-fade-target-bottom: var(--ds-scroll-edge-fade-size);\n}\n\n.scroll-edge-fade--left {\n --ds-scroll-edge-fade-target-left: var(--ds-scroll-edge-fade-size);\n}\n\n.scroll-edge-fade--right {\n --ds-scroll-edge-fade-target-right: var(--ds-scroll-edge-fade-size);\n}\n\n/* Manual per-edge state remains available when scroll state already lives in JS. */\n.scroll-edge-fade--top-at-edge {\n --ds-scroll-edge-fade-target-top: 0px;\n}\n\n.scroll-edge-fade--bottom-at-edge {\n --ds-scroll-edge-fade-target-bottom: 0px;\n}\n\n.scroll-edge-fade--left-at-edge {\n --ds-scroll-edge-fade-target-left: 0px;\n}\n\n.scroll-edge-fade--right-at-edge {\n --ds-scroll-edge-fade-target-right: 0px;\n}\n\n@supports (animation-timeline: scroll()) {\n .scroll-edge-fade--scroll-aware {\n animation-name:\n ds-scroll-edge-fade-reveal-top,\n ds-scroll-edge-fade-reveal-bottom,\n ds-scroll-edge-fade-reveal-left,\n ds-scroll-edge-fade-reveal-right;\n /* stylelint-disable-next-line declaration-property-value-disallowed-list -- scroll timelines require a nonzero sentinel duration; elapsed time is ignored. */\n animation-duration: 1ms;\n animation-timing-function: ease-in-out;\n animation-fill-mode: both;\n animation-timeline: scroll(self y), scroll(self y), scroll(self x), scroll(self x);\n animation-range:\n 0 var(--ds-scroll-edge-fade-reveal-distance),\n calc(100% - var(--ds-scroll-edge-fade-reveal-distance)) 100%,\n 0 var(--ds-scroll-edge-fade-reveal-distance),\n calc(100% - var(--ds-scroll-edge-fade-reveal-distance)) 100%;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .scroll-edge-fade--scroll-aware {\n animation: none;\n }\n }\n}\n\n.scroll-edge-fade--at-end,\n.scroll-edge-fade--hidden {\n -webkit-mask-image: none;\n mask-image: none;\n}\n\n/**\n * Masks a long content element against a scrollport-relative block window.\n * The owner updates the start/end offsets as the scrollport moves. Keeping the\n * mask on the content instead of the overflow element leaves native scrollbars\n * outside the fade.\n */\n.scroll-edge-fade--block-window {\n --ds-scroll-edge-fade-window-start: 100%;\n --ds-scroll-edge-fade-window-end: 100%;\n\n -webkit-mask-image: linear-gradient(\n to bottom,\n #000 0,\n #000 var(--ds-scroll-edge-fade-window-start),\n transparent var(--ds-scroll-edge-fade-window-end)\n );\n mask-image: linear-gradient(\n to bottom,\n #000 0,\n #000 var(--ds-scroll-edge-fade-window-start),\n transparent var(--ds-scroll-edge-fade-window-end)\n );\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n}\n\n/* stylelint-enable color-no-hex */\n","@import '../../utils/control-density.css';\n@import '../../utils/focus-ring.css';\n@import '../../utils/interaction-fill.css';\n\n/* ── Theme variables ──────────────────────────────────────────────────────── */\n\n.panel-nav {\n /* ─ Sizes ─ */\n --_nav-width: var(--dimension-panel-width-2xs);\n --_nav-width-collapsed: var(--dimension-size-600);\n --_nav-item-height: var(--dimension-size-400, 32px);\n --_nav-padding-x: var(--dimension-space-100, 8px);\n --_nav-padding-y: var(--dimension-space-100, 8px);\n /* gear button outer width = padding*2 + icon */\n --_nav-footer-btn-w: calc(var(--dimension-space-075, 6px) * 2 + var(--dimension-iconography-md, 20px));\n\n /* ─ Motion ─ */\n /* --ds-panel-nav-speed: unitless multiplier; override in Storybook to slow-mo (e.g. 10 = 10×).\n *-dur vars are duration-only (needed for calc); *-motion vars add easing for use in transitions. */\n --_nav-speed: var(--ds-panel-nav-speed, 1);\n --_nav-expand-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-expand-motion: var(--_nav-expand-dur) var(--effect-animation-easing-ease-in-out, ease);\n --_nav-label-fade-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-label-fade-motion: var(--_nav-label-fade-dur) var(--effect-animation-easing-ease-in-out, ease);\n --_nav-label-fade-out-dur: calc(var(--_nav-speed) * var(--effect-animation-duration-medium-1, 300ms));\n --_nav-label-fade-out-motion: var(--_nav-label-fade-out-dur) var(--effect-animation-easing-ease-in-out, ease);\n\n /* ─ Colours – default chrome (app surfaces) ─ */\n --_nav-bg: var(--color-background-secondary);\n --_nav-border: var(--color-border-tertiary);\n --_nav-divider: var(--color-border-tertiary);\n --_nav-fg-primary: var(--color-foreground-primary);\n --_nav-fg-secondary: var(--color-foreground-secondary);\n --_nav-fg-tertiary: var(--color-foreground-tertiary);\n --_nav-hover: var(--color-interaction-hover);\n --_nav-pressed: var(--color-interaction-pressed);\n --_nav-focus: var(--color-interaction-focus);\n --_nav-dot: var(--color-foreground-bold-brand);\n --ds-interaction-hover: var(--_nav-hover);\n --ds-interaction-pressed: var(--_nav-pressed);\n --ds-focus-ring-color: var(--_nav-focus);\n}\n\n/* ── Default chrome overrides ────────────────────────────────────────────── */\n\n/* Shell-owned chrome (bg + wash) — panel is a transparent frame. */\n:host-context(ds-app-shell:not([gradient-preset='none'])) .panel-nav {\n background-color: transparent;\n}\n\n/* Style slots — same app tokens for now; hook texture/glyph layers on these classes later. */\n.panel-nav--dashboard,\n.panel-nav--settings {\n --_nav-bg: var(--color-background-secondary);\n --_nav-border: var(--color-border-tertiary);\n --_nav-divider: var(--color-border-tertiary);\n --_nav-fg-primary: var(--color-foreground-primary);\n --_nav-fg-secondary: var(--color-foreground-secondary);\n --_nav-fg-tertiary: var(--color-foreground-tertiary);\n --_nav-hover: var(--color-interaction-hover);\n --_nav-pressed: var(--color-interaction-pressed);\n --_nav-focus: var(--color-interaction-focus);\n --_nav-dot: var(--color-foreground-bold-brand);\n --ds-interaction-hover: var(--_nav-hover);\n --ds-interaction-pressed: var(--_nav-pressed);\n --ds-focus-ring-color: var(--_nav-focus);\n}\n\n/* ── Shell ───────────────────────────────────────────────────────────────── */\n\n.panel-nav {\n display: flex;\n flex-direction: column;\n position: relative;\n width: var(--_nav-width);\n min-width: var(--_nav-width);\n height: 100%;\n background-color: var(--_nav-bg);\n border-right: var(--dimension-stroke-width-012, 1px) solid var(--_nav-border);\n box-sizing: border-box;\n overflow: visible;\n transition:\n width var(--_nav-expand-motion),\n min-width var(--_nav-expand-motion),\n background-color var(--effect-motion-short-3, 200ms ease),\n border-color var(--effect-motion-short-3, 200ms ease);\n font-family: var(--typography-font-family, system-ui);\n isolation: isolate;\n}\n\n.panel-nav__header,\n.panel-nav__body,\n.panel-nav__footer {\n position: relative;\n z-index: 3;\n}\n\n.panel-nav--collapsed {\n width: var(--_nav-width-collapsed);\n min-width: var(--_nav-width-collapsed);\n}\n\n/* ── Header ──────────────────────────────────────────────────────────────── */\n\n.panel-nav__header {\n display: flex;\n align-items: center;\n height: var(--dimension-size-600, 48px);\n padding: var(--dimension-space-100, 8px);\n flex-shrink: 0;\n box-sizing: border-box;\n border-bottom: var(--dimension-stroke-width-012, 1px) solid var(--_nav-divider);\n transition: border-color var(--effect-motion-short-3, 200ms ease);\n}\n\n.panel-nav__header-btn {\n all: unset;\n /* Re-assert after unset — required for `.ds-interaction-fill` stacking. */\n position: relative;\n z-index: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--dimension-space-075, 6px);\n border-radius: var(--ds-control-radius, var(--dimension-radius-025, 2px));\n cursor: pointer;\n color: var(--_nav-fg-primary);\n flex-shrink: 0;\n box-sizing: border-box;\n transition: color var(--effect-motion-short-3, 200ms ease);\n /* Hover/press: `.ds-interaction-fill` (tokens from --_nav-* via --ds-interaction-*). */\n}\n\n.panel-nav--breakpoint-locked .panel-nav__header-btn {\n cursor: default;\n}\n\n/* Logo — visible by default; instant swap to toggle on hover (no crossfade overlap) */\n.panel-nav__header-logo {\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 2;\n opacity: 1;\n}\n\n.panel-nav__m-mark {\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n display: block;\n}\n\n/* Toggle icon — stacked on logo; under hover wash (::after z-index 3) */\n.panel-nav__header-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n inset: 0;\n z-index: 2;\n opacity: 0;\n pointer-events: none;\n}\n\n.panel-nav:not(.panel-nav--breakpoint-locked) .panel-nav__header-btn:hover .panel-nav__header-logo { opacity: 0; }\n.panel-nav:not(.panel-nav--breakpoint-locked) .panel-nav__header-btn:hover .panel-nav__header-toggle { opacity: 1; }\n\n/* ── Scrollable body ─────────────────────────────────────────────────────── */\n\n.panel-nav__body {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-200, 16px);\n padding: var(--dimension-space-100, 8px);\n box-sizing: border-box;\n scrollbar-width: none;\n}\n\n.panel-nav__body::-webkit-scrollbar { display: none; }\n\n/* ── Group ───────────────────────────────────────────────────────────────── */\n\n.panel-nav__group {\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050, 4px);\n}\n\n/*\n * Section headers — typography via ds-text (text-caption + emphasis).\n * Element selector so display:flex beats Text's inline-block, and padding\n * isn't clobbered by sharing `.panel-nav__item-label-text` on the same host.\n */\nds-text.panel-nav__group-label {\n display: flex;\n align-items: center;\n box-sizing: border-box;\n text-transform: uppercase;\n color: var(--_nav-fg-secondary);\n user-select: none;\n height: var(--dimension-size-300, 24px);\n /* Former nested pad: group-label (075) + item-label-text (025). */\n padding: 0 calc(var(--dimension-space-075, 6px) + var(--ds-control-label-inset));\n white-space: nowrap;\n overflow: hidden;\n max-height: var(--dimension-size-300, 24px);\n opacity: 1;\n transition:\n opacity var(--_nav-label-fade-motion) 0ms,\n max-height var(--_nav-label-fade-motion) 0ms,\n padding var(--_nav-label-fade-motion) 0ms,\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Item ────────────────────────────────────────────────────────────────── */\n\n.panel-nav__item {\n all: unset;\n /* Re-assert after unset — required for `.ds-interaction-fill` stacking. */\n position: relative;\n z-index: 0;\n display: flex;\n align-items: center;\n gap: var(--dimension-space-050, 4px);\n padding: 0 var(--dimension-space-075, 6px);\n height: var(--dimension-size-400, 32px);\n border-radius: var(--ds-control-radius, var(--dimension-radius-025, 2px));\n cursor: pointer;\n color: var(--_nav-fg-secondary);\n box-sizing: border-box;\n width: 100%;\n user-select: none;\n transition: color var(--effect-motion-short-1, 50ms ease-in-out);\n /* Hover/press: `.ds-interaction-fill` — no --selected (icon/type weight only). */\n}\n\n/* ── Active item ─────────────────────────────────────────────────────────── */\n\n.panel-nav__item--active {\n color: var(--_nav-fg-primary);\n /* Bold typography via ds-text (text-body-medium + emphasis) on the label */\n}\n\n/* ── Item icon ───────────────────────────────────────────────────────────── */\n\n.panel-nav__item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n position: relative;\n z-index: 2;\n}\n\n/* ── Item label ──────────────────────────────────────────────────────────── */\n\n.panel-nav__item-label {\n position: relative;\n z-index: 2;\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n height: var(--typography-lineheight-md, 20px);\n opacity: 1;\n /* Gradient wipe: mask sweeps left-to-right on expand, right-to-left on collapse */\n /* stylelint-disable-next-line color-named -- mask luminance stops, not theme colors */\n mask-image: linear-gradient(to right, black 50%, transparent 100%);\n /* stylelint-disable-next-line color-named -- mask luminance stops, not theme colors */\n -webkit-mask-image: linear-gradient(to right, black 50%, transparent 100%);\n mask-size: 200% 100%;\n -webkit-mask-size: 200% 100%;\n mask-position: 0% 0;\n -webkit-mask-position: 0% 0;\n transition:\n opacity var(--_nav-label-fade-motion) 0ms,\n mask-position var(--_nav-label-fade-motion) 0ms,\n -webkit-mask-position var(--_nav-label-fade-motion) 0ms;\n}\n\n.panel-nav__item-label-text {\n padding: 0 var(--ds-control-label-inset);\n}\n\n/* ── Item notification dot — 20px suffix zone matches the icon geometry ──── */\n\n.panel-nav__item-dot-box {\n position: absolute;\n z-index: 2;\n right: var(--dimension-space-075, 6px);\n top: var(--dimension-space-075, 6px);\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--dimension-iconography-md, 20px);\n height: var(--dimension-iconography-md, 20px);\n pointer-events: none;\n}\n\n.panel-nav__item-dot {\n transition:\n transform var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__item-dot {\n transform: translate(\n calc(var(--dimension-space-075, 6px) + var(--dimension-stroke-width-012, 1px)),\n calc(0px - var(--dimension-space-075, 6px) - var(--dimension-stroke-width-012, 1px))\n );\n}\n\n.panel-nav--animating .panel-nav__item-label {\n text-overflow: clip;\n}\n\n.panel-nav--collapsed .panel-nav__item-label {\n opacity: 0;\n pointer-events: none;\n text-overflow: clip;\n mask-position: 100% 0;\n -webkit-mask-position: 100% 0;\n transition:\n opacity var(--_nav-label-fade-out-motion) 0ms,\n mask-position var(--_nav-label-fade-out-motion) 0ms,\n -webkit-mask-position var(--_nav-label-fade-out-motion) 0ms;\n}\n\n.panel-nav--collapsed ds-text.panel-nav__group-label {\n opacity: 0;\n max-height: 0;\n padding: 0 calc(var(--dimension-space-075, 6px) + var(--ds-control-label-inset));\n pointer-events: none;\n transition:\n opacity var(--_nav-label-fade-out-motion) 0ms,\n max-height var(--_nav-label-fade-out-motion) 0ms,\n padding var(--_nav-label-fade-out-motion) 0ms,\n color var(--effect-motion-short-3, 200ms ease);\n}\n\n/* ── Footer ──────────────────────────────────────────────────────────────── */\n\n.panel-nav__footer {\n position: relative;\n flex-shrink: 0;\n /* single row: padding*2 + item */\n height: calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height));\n transition: height var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer {\n /* two rows: padding*2 + item*2 + gap */\n height: calc(var(--_nav-padding-y) * 2 + var(--_nav-item-height) * 2 + var(--dimension-space-050, 4px));\n}\n\n/* ── Footer icon button (gear / dashboard) ───────────────────────────────── */\n\n.panel-nav__footer-btn {\n position: absolute;\n left: var(--_nav-padding-x);\n top: var(--_nav-padding-y);\n --ds-button-unfilled-fg: var(--_nav-fg-secondary);\n --ds-button-unfilled-fg-active: var(--_nav-fg-primary);\n --ds-interaction-hover: var(--_nav-hover);\n --ds-interaction-pressed: var(--_nav-pressed);\n --ds-focus-ring-color: var(--_nav-focus);\n}\n\n/* ── Footer user — absolutely positioned, slides from row to below gear ───── */\n\n.panel-nav__footer-user {\n position: absolute;\n /* Expanded row: balanced shell inset with an 8px gear-to-user gap. */\n left: calc(var(--_nav-padding-x) + var(--_nav-footer-btn-w) + var(--dimension-space-100, 8px));\n right: var(--_nav-padding-x);\n top: var(--_nav-padding-y);\n width: auto;\n min-width: 0;\n transition:\n left var(--_nav-expand-motion),\n right var(--_nav-expand-motion),\n top var(--_nav-expand-motion);\n}\n\n/* Popup-open state keeps the native footer trigger visibly pressed. */\n.panel-nav__footer-user--menu-expanded::after,\n.panel-nav__footer-user--menu-expanded:hover:not(:disabled)::after {\n background: var(--_nav-pressed);\n}\n\n.panel-nav__footer-user--menu-expanded {\n color: var(--_nav-fg-primary);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user {\n /* Compact column keeps the intentionally tighter 4px vertical gap. */\n left: var(--_nav-padding-x);\n top: calc(var(--_nav-padding-y) + var(--_nav-item-height) + var(--dimension-space-050, 4px));\n}\n\n/* Label: absolute so it stays in place and only fades — no mask slide */\n.panel-nav__footer-user-label {\n text-align: right;\n position: absolute;\n left: var(--dimension-space-075, 6px);\n right: calc(var(--dimension-space-075, 6px) + var(--dimension-iconography-md, 20px) + var(--dimension-space-050, 4px));\n top: 50%;\n transform: translateY(-50%);\n flex: none;\n height: var(--typography-lineheight-md, 20px);\n mask-image: none;\n -webkit-mask-image: none;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user-label {\n opacity: 0;\n pointer-events: none;\n mask-position: 0% 0;\n -webkit-mask-position: 0% 0;\n}\n\n/* ── Footer user icon — cross-fades chevron ↔ circle+initial ────────────── */\n\n/* Absolute — right-anchored in expanded, left-anchored in collapsed to match regular item icon position */\n.panel-nav__footer-user-icon {\n position: absolute;\n left: calc(100% - var(--dimension-iconography-md, 20px) - var(--dimension-space-075, 6px));\n top: 0;\n bottom: 0;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: left var(--_nav-expand-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-user-icon {\n left: var(--dimension-space-075, 6px);\n}\n\n.panel-nav__footer-icon-expanded,\n.panel-nav__footer-icon-collapsed {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.panel-nav__footer-icon-expanded {\n opacity: 1;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav__footer-icon-collapsed {\n opacity: 0;\n transition: opacity var(--_nav-label-fade-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-icon-expanded {\n opacity: 0;\n transition: opacity var(--_nav-label-fade-out-motion);\n}\n\n.panel-nav--collapsed .panel-nav__footer-icon-collapsed {\n opacity: 1;\n transition: opacity var(--_nav-label-fade-out-motion);\n}\n\n/* ── User initial — overlaid on the circle icon ─────────────────────────── */\n\n/*\n * Center the caption glyph on the Circle icon. Don't use inset:0 + flex — the\n * inner `.ds-text__element { width: 100% }` stretches and left-aligns the letter.\n * Translate-centering keeps the complete caption metric box on the icon center;\n * the caption variant remains the sole owner of its line-height.\n * (Do not set `inset: auto` after top/left — it resets them.)\n */\nds-text.panel-nav__user-initial,\nds-text.panel-nav__user-initial.ds-text--caption {\n position: absolute;\n top: 50%;\n left: 50%;\n right: auto;\n bottom: auto;\n transform: translate(-50%, -50%);\n display: block;\n width: max-content;\n max-width: none;\n min-width: 0;\n text-align: center;\n color: inherit;\n pointer-events: none;\n}\n\n/* ── Drag-to-resize handle ───────────────────────────────────────────────── */\n\n/* The resize overlay line — extends past the right edge; keep above workspace content */\n.panel-nav__resize-handle {\n position: absolute;\n top: 0;\n /* Centered on the right edge: --dimension-space-050 outside + inside = --dimension-space-100 hit zone */\n right: var(--dimension-space-n050, -4px);\n bottom: 0;\n width: var(--dimension-space-100, 8px);\n z-index: 20;\n cursor: ew-resize;\n touch-action: none;\n /* Needs a real background for Safari hit-testing */\n background: transparent;\n}\n\n/* Safari requires non-transparent fill for hit testing */\n.panel-nav__resize-handle::before {\n content: '';\n position: absolute;\n inset: 0;\n /* stylelint-disable-next-line color-named -- near-invisible hit-test fill; not a theme color */\n background: rgba(0, 0, 0, 0.001);\n}\n\n/* Resize overlay line */\n.panel-nav__resize-handle::after {\n content: '';\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n width: var(--dimension-stroke-width-025, 2px);\n background: var(--color-foreground-primary);\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--effect-motion-short-2, 100ms ease-in-out);\n}\n\n.panel-nav__resize-handle--overlay::after {\n opacity: 1;\n}\n\n/* ── Reduced motion ──────────────────────────────────────────────────────── */\n\n@media (prefers-reduced-motion: reduce) {\n .panel-nav,\n .panel-nav *,\n .panel-nav *::before,\n .panel-nav *::after,\n .panel-nav__resize-handle,\n .panel-nav__resize-handle::before,\n .panel-nav__resize-handle::after {\n /* Near-instant — browsers treat 0 as \"no transition\" and can skip the end state. */\n transition-duration: var(--effect-animation-duration-instant) !important;\n animation-duration: var(--effect-animation-duration-instant) !important;\n transition-delay: var(--effect-animation-duration-instant) !important;\n }\n}\n","import {\n Component,\n Prop,\n Event,\n EventEmitter,\n Watch,\n State,\n Element,\n Method,\n h,\n Host,\n} from '@stencil/core';\nimport type { ChromeTransitionDetail } from '../../shell/chrome-transition';\nimport type { NavChromeStyle } from '../../shell/nav-chrome';\nimport {\n deriveActiveIdFromUrl,\n panelNavWidthTransitionMs,\n parsePanelNavGroups,\n resolvePanelNavDisableVt,\n resolvePanelNavStyle,\n resolvePanelNavToggle,\n shouldResyncPanelNavGroups,\n shouldResyncPanelNavStyle,\n} from './panel-nav-utils';\nimport {\n PANEL_NAV_USER_MENU_ANCHOR_ID,\n PANEL_NAV_USER_MENU_PLACEMENT,\n type PanelNavGroup,\n type PanelNavItem,\n type PanelNavRouterMode,\n type PanelNavUserActionDetail,\n} from './panel-nav-types';\nimport { scrollEdgeFadeClassMap } from '../../utils/scroll-edge-fade';\nimport { prefersReducedMotion } from '../../utils/resolve-css-time-ms';\n\n@Component({\n tag: 'ds-panel-nav',\n styleUrl: 'PanelNav.css',\n styleUrls: ['../../utils/scroll-edge-fade.css'],\n scoped: true,\n})\nexport class PanelNav {\n /** Style slot: `dashboard` or `settings`. Colors match for now;\n * class hooks reserved for texture/glyph layers. Property: `navStyle`. Attribute: `nav-style`. */\n @Prop({ attribute: 'nav-style', reflect: true }) navStyle: NavChromeStyle = 'dashboard';\n\n /** When `true`, style changes apply synchronously — host app owns view transitions. */\n @Prop() disableViewTransition: boolean = false;\n\n /** Nav groups — set via JS property (`el.groups = [...]`) or JSON string attribute. */\n @Prop() groups: string | PanelNavGroup[] = '[]';\n\n /** How items with `href` render:\n * - `anchor` (default): native `<a href>` — works with routers that intercept anchors.\n * - `event`: always `<button>`; host handles navigation via `dsNavSelect`. */\n @Prop() routerMode: PanelNavRouterMode = 'anchor';\n\n /** ID of the currently active/selected nav item. Overridden by `currentUrl` matching when set. */\n @Prop() activeId: string = '';\n\n /** Whether the nav is in collapsed (icon-only) state.\n * Set `storageKey` to persist across reloads. `dsNavToggle` still fires on change. */\n @Prop({ mutable: true }) collapsed: boolean = false;\n\n /** Viewport width (px) below which the nav locks in icon-only mode. 0 = disabled. */\n @Prop() breakpoint: number = 0;\n\n /** `localStorage` key used to persist the collapsed state across page loads.\n * When set, collapsed state is restored on mount and written on each toggle. */\n @Prop() storageKey: string = '';\n\n /** Current route URL (e.g. `window.location.pathname` or the router's active URL).\n * When set the component derives the active item by matching item `href` values\n * against this string (longest segment-boundary prefix wins), overriding `activeId`. */\n @Prop() currentUrl: string = '';\n\n /** Display name for the footer user section */\n @Prop() userName: string = '';\n\n /** Single character shown in the collapsed avatar */\n @Prop() userInitial: string = '';\n\n @Prop() dashboardLabel: string = 'Dashboard';\n @Prop() settingsLabel: string = 'Settings';\n @Prop() accountLabel: string = 'Account';\n /** Keep the account-menu trigger visually active while its popup is open or closing. */\n @Prop() accountMenuExpanded: boolean = false;\n @Prop() dashboardNavigationLabel: string = 'Dashboard navigation';\n @Prop() settingsNavigationLabel: string = 'Settings navigation';\n @Prop() expandNavigationLabel: string = 'Expand navigation';\n @Prop() collapseNavigationLabel: string = 'Collapse navigation';\n /** Localized name for the body when overflow makes it a keyboard-scrollable region. */\n @Prop() navigationItemsLabel: string = 'Navigation items';\n\n /** Emitted when a nav item is clicked. Detail = the item's `id`. */\n @Event() dsNavSelect!: EventEmitter<string>;\n\n /** Emitted when the collapse toggle is clicked. Detail = new collapsed state. */\n @Event() dsNavToggle!: EventEmitter<boolean>;\n\n /** Bubbling lifecycle — `ds-app-shell` pauses chrome metrics during width motion. */\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionStart!: EventEmitter<ChromeTransitionDetail>;\n\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionEnd!: EventEmitter<ChromeTransitionDetail>;\n\n /** Emitted when the footer left button (gear / dashboard) is clicked. */\n @Event() dsNavFooterAction!: EventEmitter<void>;\n\n /** Emitted when the footer user button is clicked. Detail includes the anchor for `ds-menu`. */\n @Event() dsNavUserAction!: EventEmitter<PanelNavUserActionDetail>;\n\n @Element() el!: HTMLElement;\n\n @State() private renderedStyle: NavChromeStyle = 'dashboard';\n @State() private parsedGroups: PanelNavGroup[] = [];\n @State() private isAnimating = false;\n @State() private rovingIndex: number = 0;\n @State() private viewportNarrow: boolean = false;\n @State() private urlDerivedActiveId: string = '';\n @State() private bodyScrollable = false;\n\n private transitionCompletionHandler?: (e: TransitionEvent) => void;\n private transitionFallbackTimer: number | null = null;\n private resizeObserver?: ResizeObserver;\n private scrollRegionObserver?: ResizeObserver;\n private bodyEl?: HTMLElement;\n private initialRenderComplete = false;\n\n // Drag-to-resize state (not @State — no re-render needed)\n private isDragging = false;\n private dragStartX = 0;\n private didSnap = false;\n private lastDeltaX = 0;\n private edgeOverlayTimer: number | null = null;\n private globalMouseMoveHandler?: (e: MouseEvent) => void;\n private globalMouseUpHandler?: () => void;\n\n @State() private showEdgeOverlay = false;\n\n private static readonly HOST_PROP_SYNC_BUDGET = 8;\n\n /** The ID that should be treated as active. `currentUrl` matching takes precedence\n * over `activeId` when both are present. */\n private get effectiveActiveId(): string {\n return this.urlDerivedActiveId || this.activeId;\n }\n\n private get effectiveDisableViewTransition(): boolean {\n return resolvePanelNavDisableVt(\n this.disableViewTransition,\n this.el.getAttribute('disable-view-transition')\n );\n }\n\n @Watch('collapsed')\n onCollapsedChange(_next: boolean, prev: boolean | undefined) {\n if (prev === undefined || !this.initialRenderComplete) return;\n this.startCollapseAnimation();\n }\n\n @Watch('viewportNarrow')\n onViewportNarrowChange(next: boolean, prev: boolean | undefined) {\n if (prev === undefined || !this.initialRenderComplete) return;\n if (next && this.rovingIndex === 0) {\n this.rovingIndex = this.getFirstRovingIndex();\n }\n this.startCollapseAnimation();\n }\n\n @Watch('breakpoint')\n onBreakpointChange() {\n this.disconnectResizeObserver();\n if (this.effectiveBreakpoint() > 0) this.connectResizeObserver();\n }\n\n @Watch('groups')\n onGroupsChange(val: string | PanelNavGroup[]) {\n this.parsedGroups = parsePanelNavGroups(val);\n this.rovingIndex = 0;\n this.syncActiveFromUrl();\n }\n\n @Watch('activeId')\n @Watch('urlDerivedActiveId')\n onActiveIdChange() {\n /* Active route updates aria-current only — roving tab stop stays user-controlled. */\n }\n\n @Watch('currentUrl')\n onCurrentUrlChange() {\n this.syncActiveFromUrl();\n }\n\n @Watch('navStyle')\n onNavStyleChange(newVal: NavChromeStyle) {\n this.renderedStyle = newVal;\n }\n\n componentWillLoad() {\n this.renderedStyle = resolvePanelNavStyle(this.navStyle, this.el.getAttribute('nav-style'));\n this.viewportNarrow = this.isViewportNarrow();\n if (this.storageKey) {\n try {\n const stored = localStorage.getItem(this.storageKey);\n if (stored !== null) this.collapsed = stored === 'true';\n } catch {\n /* localStorage unavailable */\n }\n }\n this.onGroupsChange(this.groups);\n this.syncActiveFromUrl();\n }\n\n componentDidLoad() {\n this.syncHostPropsIfNeeded();\n this.scheduleDeferredHostPropSync();\n if (this.effectiveBreakpoint() > 0) this.connectResizeObserver();\n this.connectScrollRegionObserver();\n this.updateBodyScrollable();\n this.initialRenderComplete = true;\n }\n\n componentDidRender() {\n this.updateBodyScrollable();\n }\n\n disconnectedCallback() {\n this.disconnectResizeObserver();\n this.disconnectScrollRegionObserver();\n this.clearCollapseAnimationCompletion(\n this.el.querySelector('.panel-nav') as HTMLElement | null\n );\n this.clearEdgeOverlayTimer();\n if (this.globalMouseMoveHandler) {\n window.removeEventListener('mousemove', this.globalMouseMoveHandler);\n }\n if (this.globalMouseUpHandler) {\n window.removeEventListener('mouseup', this.globalMouseUpHandler);\n }\n }\n\n private startCollapseAnimation() {\n if (this.isAnimating) return;\n this.isAnimating = true;\n this.dsChromeTransitionStart.emit({ source: 'panel-nav' });\n const panel = this.el.querySelector('.panel-nav') as HTMLElement | null;\n const widthBefore = panel?.getBoundingClientRect().width ?? 0;\n this.clearCollapseAnimationCompletion(panel);\n this.transitionCompletionHandler = (e: TransitionEvent) => {\n if (e.propertyName === 'width' || e.propertyName === 'min-width') {\n this.finishCollapseAnimation(panel);\n }\n };\n panel?.addEventListener('transitionend', this.transitionCompletionHandler);\n panel?.addEventListener('transitioncancel', this.transitionCompletionHandler);\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (!this.isAnimating) return;\n const widthAfter = panel?.getBoundingClientRect().width ?? 0;\n if (Math.abs(widthAfter - widthBefore) < 1) {\n this.finishCollapseAnimation(panel);\n return;\n }\n if (!panel) {\n this.finishCollapseAnimation(panel);\n return;\n }\n\n const fallbackMs = panelNavWidthTransitionMs(getComputedStyle(panel));\n if (fallbackMs <= 0) {\n this.finishCollapseAnimation(panel);\n return;\n }\n this.transitionFallbackTimer = window.setTimeout(() => {\n this.finishCollapseAnimation(panel);\n }, fallbackMs);\n });\n });\n }\n\n private finishCollapseAnimation(panel: HTMLElement | null) {\n const wasAnimating = this.isAnimating;\n this.clearCollapseAnimationCompletion(panel);\n if (!wasAnimating) return;\n this.isAnimating = false;\n this.dsChromeTransitionEnd.emit({ source: 'panel-nav' });\n }\n\n private clearCollapseAnimationCompletion(panel: HTMLElement | null) {\n if (this.transitionCompletionHandler) {\n panel?.removeEventListener('transitionend', this.transitionCompletionHandler);\n panel?.removeEventListener('transitioncancel', this.transitionCompletionHandler);\n this.transitionCompletionHandler = undefined;\n }\n if (this.transitionFallbackTimer !== null) {\n window.clearTimeout(this.transitionFallbackTimer);\n this.transitionFallbackTimer = null;\n }\n }\n\n private effectiveBreakpoint(): number {\n if (this.breakpoint > 0) return this.breakpoint;\n const attributeValue = Number(this.el.getAttribute('breakpoint') ?? 0);\n return Number.isFinite(attributeValue) && attributeValue > 0 ? attributeValue : 0;\n }\n\n private isViewportNarrow(): boolean {\n const breakpoint = this.effectiveBreakpoint();\n return typeof window !== 'undefined' && breakpoint > 0 && window.innerWidth < breakpoint;\n }\n\n private connectResizeObserver() {\n this.viewportNarrow = this.isViewportNarrow();\n this.resizeObserver = new ResizeObserver(() => {\n this.viewportNarrow = this.isViewportNarrow();\n });\n this.resizeObserver.observe(document.documentElement);\n }\n\n private disconnectResizeObserver() {\n this.resizeObserver?.disconnect();\n this.resizeObserver = undefined;\n }\n\n private connectScrollRegionObserver() {\n this.disconnectScrollRegionObserver();\n if (!this.bodyEl || typeof ResizeObserver === 'undefined') return;\n this.scrollRegionObserver = new ResizeObserver(() => this.updateBodyScrollable());\n this.scrollRegionObserver.observe(this.bodyEl);\n }\n\n private disconnectScrollRegionObserver() {\n this.scrollRegionObserver?.disconnect();\n this.scrollRegionObserver = undefined;\n }\n\n private updateBodyScrollable() {\n const next = Boolean(this.bodyEl && this.bodyEl.scrollHeight > this.bodyEl.clientHeight + 1);\n if (next !== this.bodyScrollable) this.bodyScrollable = next;\n }\n\n /** Derive active ID from the current URL by matching item `href` values.\n * Uses longest segment-boundary prefix match. */\n private syncActiveFromUrl() {\n if (!this.currentUrl) {\n this.urlDerivedActiveId = '';\n return;\n }\n this.urlDerivedActiveId = deriveActiveIdFromUrl(this.currentUrl, this.getAllItems());\n }\n\n /** Re-parse props assigned by the host after componentWillLoad (Angular ngAfterViewInit). */\n private syncHostPropsIfNeeded() {\n if (shouldResyncPanelNavStyle(this.renderedStyle, this.navStyle)) {\n this.renderedStyle = this.navStyle;\n }\n\n if (shouldResyncPanelNavGroups(this.parsedGroups, this.groups)) {\n this.onGroupsChange(this.groups);\n } else if (this.currentUrl) {\n this.syncActiveFromUrl();\n }\n }\n\n /** Poll across animation frames — host props may land without triggering @Watch. */\n private scheduleDeferredHostPropSync() {\n let remaining = PanelNav.HOST_PROP_SYNC_BUDGET;\n const tick = () => {\n this.syncHostPropsIfNeeded();\n if (--remaining > 0) {\n requestAnimationFrame(tick);\n }\n };\n queueMicrotask(tick);\n }\n\n /** Centralised toggle: updates the desktop preference unless breakpoint-locked. */\n private applyToggle(next: boolean) {\n if (this.viewportNarrow) return;\n this.collapsed = next;\n if (this.storageKey) {\n try {\n localStorage.setItem(this.storageKey, String(next));\n } catch {\n /* unavailable */\n }\n }\n this.dsNavToggle.emit(next);\n }\n\n private getAllItems(): PanelNavItem[] {\n return this.parsedGroups.flatMap(g => g.items);\n }\n\n private getFirstRovingIndex(): number {\n return this.viewportNarrow ? 1 : 0;\n }\n\n private getFooterRovingIndex(): number {\n return 1 + this.getAllItems().length;\n }\n\n private getUserRovingIndex(): number {\n return this.getFooterRovingIndex() + 1;\n }\n\n private getRovingElement(index: number): HTMLElement | null {\n const itemCount = this.getAllItems().length;\n if (index === 0) {\n return this.viewportNarrow ? null : this.el.querySelector('.panel-nav__header-btn');\n }\n if (index >= 1 && index <= itemCount) {\n const items = this.el.querySelectorAll<HTMLElement>('.panel-nav__body .panel-nav__item');\n return items[index - 1] ?? null;\n }\n if (index === this.getFooterRovingIndex()) {\n return this.el.querySelector('.panel-nav__footer-btn .button-unfilled');\n }\n if (index === this.getUserRovingIndex()) {\n return this.el.querySelector('.panel-nav__footer-user');\n }\n return null;\n }\n\n private focusRovingAt(index: number) {\n this.rovingIndex = index;\n this.getRovingElement(index)?.focus({ preventScroll: true });\n }\n\n private activateRovingIndex(index: number) {\n const itemCount = this.getAllItems().length;\n const items = this.getAllItems();\n if (index === 0) {\n this.handleToggle();\n return;\n }\n if (index >= 1 && index <= itemCount) {\n this.handleItemClick(items[index - 1].id);\n return;\n }\n if (index === this.getFooterRovingIndex()) {\n this.handleFooterAction();\n return;\n }\n if (index === this.getUserRovingIndex()) {\n const anchor = this.el.querySelector(\n `#${PANEL_NAV_USER_MENU_ANCHOR_ID}`\n ) as HTMLElement | null;\n if (anchor)\n this.dsNavUserAction.emit({ anchor, menuPlacement: PANEL_NAV_USER_MENU_PLACEMENT });\n }\n }\n\n private handleRovingKeyDown(e: KeyboardEvent, index: number) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this.activateRovingIndex(index);\n return;\n }\n\n const footerIdx = this.getFooterRovingIndex();\n const userIdx = this.getUserRovingIndex();\n const firstIdx = this.getFirstRovingIndex();\n let next: number | undefined;\n\n switch (e.key) {\n case 'ArrowDown':\n if (index === userIdx) return;\n if (index === footerIdx) next = userIdx;\n else next = index + 1;\n break;\n case 'ArrowUp':\n if (index === firstIdx) return;\n if (index === userIdx) next = footerIdx;\n else if (index === footerIdx) next = footerIdx - 1;\n else next = index - 1;\n break;\n case 'ArrowRight':\n if (index === footerIdx) next = userIdx;\n else return;\n break;\n case 'ArrowLeft':\n if (index === userIdx) next = footerIdx;\n else return;\n break;\n case 'Home':\n e.preventDefault();\n next = firstIdx;\n break;\n case 'End':\n e.preventDefault();\n next = userIdx;\n break;\n default:\n return;\n }\n\n if (next === undefined || next === index) return;\n e.preventDefault();\n this.focusRovingAt(next);\n }\n\n private handleItemClick(id: string) {\n this.dsNavSelect.emit(id);\n }\n\n private handleToggle() {\n const next = resolvePanelNavToggle(this.collapsed, this.viewportNarrow);\n if (next !== null) this.applyToggle(next);\n }\n\n /** Toggle the desktop preference. No-op while the breakpoint locks the panel collapsed. */\n @Method()\n async toggleCollapsed() {\n this.handleToggle();\n }\n\n private handleFooterAction() {\n this.dsNavFooterAction.emit();\n }\n\n private handleUserAction(e: MouseEvent) {\n const anchor = e.currentTarget as HTMLElement;\n this.dsNavUserAction.emit({ anchor, menuPlacement: PANEL_NAV_USER_MENU_PLACEMENT });\n }\n\n private clearEdgeOverlayTimer() {\n if (this.edgeOverlayTimer !== null) {\n window.clearTimeout(this.edgeOverlayTimer);\n this.edgeOverlayTimer = null;\n }\n }\n\n private handleResizeHandleMouseEnter() {\n this.clearEdgeOverlayTimer();\n if (prefersReducedMotion()) {\n this.showEdgeOverlay = true;\n return;\n }\n this.edgeOverlayTimer = window.setTimeout(() => {\n this.showEdgeOverlay = true;\n this.edgeOverlayTimer = null;\n }, 500);\n }\n\n private handleResizeHandleMouseLeave() {\n if (this.isDragging) return;\n this.clearEdgeOverlayTimer();\n this.showEdgeOverlay = false;\n }\n\n private handleResizeHandleMouseDown(e: MouseEvent) {\n e.preventDefault();\n e.stopPropagation();\n\n const wasCollapsed = this.collapsed || this.viewportNarrow;\n this.isDragging = true;\n this.dragStartX = e.clientX;\n this.didSnap = false;\n this.lastDeltaX = 0;\n this.clearEdgeOverlayTimer();\n this.showEdgeOverlay = false;\n\n document.body.style.cursor = 'ew-resize';\n document.body.style.userSelect = 'none';\n\n const onMove = (ev: MouseEvent) => {\n const deltaX = ev.clientX - this.dragStartX;\n this.lastDeltaX = Math.abs(deltaX);\n if (this.didSnap) return;\n if (!wasCollapsed && deltaX < -8) {\n this.applyToggle(true);\n this.didSnap = true;\n } else if (wasCollapsed && deltaX > 8) {\n this.applyToggle(false);\n this.didSnap = true;\n }\n };\n\n const onUp = () => {\n if (!this.didSnap && this.lastDeltaX < 3) {\n this.applyToggle(!wasCollapsed);\n }\n this.isDragging = false;\n this.showEdgeOverlay = false;\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n this.globalMouseMoveHandler = undefined;\n this.globalMouseUpHandler = undefined;\n };\n\n this.globalMouseMoveHandler = onMove;\n this.globalMouseUpHandler = onUp;\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n }\n\n private renderFooterAction(isDashboardChrome: boolean) {\n // Short chrome labels — same string for tip + aria (not \"Open/Go to …\").\n const footerLabel = isDashboardChrome ? this.settingsLabel : this.dashboardLabel;\n return (\n <ds-tooltip label={footerLabel} side=\"right\" size=\"sm\">\n <ds-button-unfilled\n variant=\"icon\"\n class=\"panel-nav__footer-btn\"\n icon={isDashboardChrome ? 'Gear' : 'Dashboard'}\n activeFill={false}\n hasBorder={false}\n focusTabIndex={this.rovingIndex === this.getFooterRovingIndex() ? 0 : -1}\n aria-label={footerLabel}\n onDsClick={() => this.handleFooterAction()}\n onKeyDown={(e: KeyboardEvent) => this.handleRovingKeyDown(e, this.getFooterRovingIndex())}\n onFocusin={() => {\n this.rovingIndex = this.getFooterRovingIndex();\n }}\n />\n </ds-tooltip>\n );\n }\n\n private renderFooterUser(collapsed: boolean, userName: string, userInitial: string) {\n // Always wrap so collapse morph CSS can transition on a stable button node.\n // Tip only when collapsed (empty label → ds-tooltip skips show).\n const tipLabel = collapsed ? this.accountLabel : '';\n const userButton = (\n <button\n type=\"button\"\n id={PANEL_NAV_USER_MENU_ANCHOR_ID}\n class={{\n 'panel-nav__item': true,\n 'panel-nav__item--active': this.accountMenuExpanded,\n 'panel-nav__footer-user': true,\n 'panel-nav__footer-user--menu-expanded': this.accountMenuExpanded,\n 'ds-focus-ring-inset': true,\n 'ds-interaction-fill': true,\n }}\n tabIndex={this.rovingIndex === this.getUserRovingIndex() ? 0 : -1}\n aria-label={this.accountLabel}\n aria-haspopup=\"menu\"\n aria-expanded={String(this.accountMenuExpanded)}\n onClick={e => this.handleUserAction(e)}\n onKeyDown={(e: KeyboardEvent) => this.handleRovingKeyDown(e, this.getUserRovingIndex())}\n onFocus={() => {\n this.rovingIndex = this.getUserRovingIndex();\n }}\n >\n <ds-text\n class=\"panel-nav__item-label panel-nav__footer-user-label panel-nav__item-label-text ds-control--md\"\n as=\"span\"\n variant=\"text-body-medium\"\n emphasis\n color=\"inherit\"\n >\n {userName}\n </ds-text>\n <span class=\"panel-nav__item-icon panel-nav__footer-user-icon\" aria-hidden=\"true\">\n <span class=\"panel-nav__footer-icon-expanded\">\n <ds-icon name=\"ChevronUpDown\" size=\"md\" color=\"inherit\" />\n </span>\n <span class=\"panel-nav__footer-icon-collapsed\">\n <ds-icon name=\"Circle\" size=\"md\" color=\"inherit\" />\n <ds-text\n class=\"panel-nav__user-initial\"\n as=\"span\"\n variant=\"text-caption\"\n emphasis\n align=\"center\"\n color=\"inherit\"\n >\n {userInitial}\n </ds-text>\n </span>\n </span>\n </button>\n );\n\n return (\n <ds-tooltip label={tipLabel} side=\"right\" size=\"sm\">\n {userButton}\n </ds-tooltip>\n );\n }\n\n private renderNavItem(item: PanelNavItem, idx: number, collapsed: boolean) {\n const isActive = item.id === this.effectiveActiveId;\n const rovingPosition = idx + 1;\n\n const itemContent = [\n <span class=\"panel-nav__item-icon\">\n <ds-icon name={item.icon} size=\"md\" color=\"inherit\" />\n </span>,\n <ds-text\n class=\"panel-nav__item-label panel-nav__item-label-text ds-control--md\"\n as=\"span\"\n variant=\"text-body-medium\"\n emphasis={isActive}\n color=\"inherit\"\n >\n {item.label}\n </ds-text>,\n item.dot && (\n <span class=\"panel-nav__item-dot-box\" aria-hidden=\"true\">\n <ds-badge class=\"panel-nav__item-dot\" variant=\"dot\" hasRing={collapsed} label=\"\" />\n </span>\n ),\n ];\n\n const sharedProps = {\n class: {\n 'panel-nav__item': true,\n 'panel-nav__item--active': isActive,\n 'ds-focus-ring-inset': true,\n 'ds-interaction-fill': true,\n },\n 'aria-current': isActive ? ('page' as const) : undefined,\n tabIndex: rovingPosition === this.rovingIndex ? 0 : -1,\n onClick: () => this.handleItemClick(item.id),\n onKeyDown: (e: KeyboardEvent) => this.handleRovingKeyDown(e, rovingPosition),\n onFocus: () => {\n this.rovingIndex = rovingPosition;\n },\n };\n\n const useAnchor = this.routerMode === 'anchor' && item.href;\n const control = useAnchor ? (\n <a {...sharedProps} href={item.href}>\n {itemContent}\n </a>\n ) : (\n <button {...sharedProps} type=\"button\">\n {itemContent}\n </button>\n );\n\n // Always wrap so label fade CSS can transition on a stable item node.\n // Tip only when collapsed (empty label → ds-tooltip skips show).\n return (\n <ds-tooltip label={collapsed ? item.label : ''} side=\"right\" size=\"sm\">\n {control}\n </ds-tooltip>\n );\n }\n\n render() {\n const chromeStyle = this.renderedStyle;\n const isDashboardChrome = chromeStyle === 'dashboard';\n const isDashboard = this.navStyle === 'dashboard';\n const collapsed = this.collapsed || this.viewportNarrow;\n const { userName, userInitial } = this;\n\n const navCls: Record<string, boolean> = {\n 'panel-nav': true,\n 'ds-control--md': true,\n 'panel-nav--dashboard': isDashboard,\n 'panel-nav--settings': !isDashboard,\n 'panel-nav--collapsed': collapsed,\n 'panel-nav--breakpoint-locked': this.viewportNarrow,\n 'panel-nav--animating': this.isAnimating,\n };\n\n return (\n <Host style={{ display: 'block', position: 'relative' }}>\n <nav\n class={navCls}\n aria-label={\n isDashboardChrome ? this.dashboardNavigationLabel : this.settingsNavigationLabel\n }\n >\n {/* ── Header: Motive logo, reveals collapse toggle on hover ── */}\n <div class=\"panel-nav__header\">\n <button\n type=\"button\"\n class=\"panel-nav__header-btn ds-focus-ring-inset ds-interaction-fill\"\n disabled={this.viewportNarrow}\n tabIndex={!this.viewportNarrow && this.rovingIndex === 0 ? 0 : -1}\n onClick={() => this.handleToggle()}\n onKeyDown={(e: KeyboardEvent) => this.handleRovingKeyDown(e, 0)}\n onFocus={() => {\n this.rovingIndex = 0;\n }}\n aria-label={collapsed ? this.expandNavigationLabel : this.collapseNavigationLabel}\n aria-expanded={collapsed ? 'false' : 'true'}\n >\n {/* Motive M mark — fades out on hover to reveal collapse toggle */}\n <span class=\"panel-nav__header-logo\" aria-hidden=\"true\">\n <svg\n class=\"panel-nav__m-mark\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n focusable=\"false\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M11.1159 4.31537H7.67021L2.53401 13.0703H0V15.6875H4.02716L8.24319 8.49978V15.6846H11.6342L15.8289 8.47829V15.6846H18.7122V4.3125H15.2559L11.1159 11.3648V4.31537Z\"\n fill=\"currentColor\"\n />\n </svg>\n </span>\n {/* Collapse / expand icon — revealed on hover via CSS */}\n <span class=\"panel-nav__header-toggle\" aria-hidden=\"true\">\n <ds-icon\n name={collapsed ? 'LeftExpandB' : 'LeftCollapseB'}\n size=\"md\"\n color=\"inherit\"\n />\n </span>\n </button>\n </div>\n\n {/* ── Scrollable body ── */}\n <div\n class={{\n 'panel-nav__body': true,\n 'ds-focus-ring': this.bodyScrollable,\n ...scrollEdgeFadeClassMap({ edges: 'bottom' }),\n }}\n ref={element => {\n const next = (element as HTMLElement) ?? undefined;\n if (next === this.bodyEl) return;\n this.bodyEl = next;\n this.connectScrollRegionObserver();\n }}\n role={this.bodyScrollable ? 'region' : undefined}\n aria-label={this.bodyScrollable ? this.navigationItemsLabel : undefined}\n tabIndex={this.bodyScrollable ? 0 : undefined}\n >\n {(() => {\n let flatIdx = 0;\n return this.parsedGroups.map(group => (\n <div class=\"panel-nav__group\">\n {group.label && (\n <ds-text\n class=\"panel-nav__group-label ds-control--md\"\n as=\"span\"\n variant=\"text-caption\"\n emphasis\n color=\"inherit\"\n >\n {group.label}\n </ds-text>\n )}\n {group.items.map(item => this.renderNavItem(item, flatIdx++, collapsed))}\n </div>\n ));\n })()}\n </div>\n\n {/* ── Footer ── */}\n <div class=\"panel-nav__footer\">\n {this.renderFooterAction(isDashboardChrome)}\n {this.renderFooterUser(collapsed, userName, userInitial)}\n </div>\n </nav>\n\n {/* Drag-to-resize handle — always rendered, hidden only when auto-collapsed by breakpoint */}\n {!this.viewportNarrow && (\n <div\n class={{\n 'panel-nav__resize-handle': true,\n 'panel-nav__resize-handle--overlay': this.showEdgeOverlay,\n }}\n onMouseEnter={() => this.handleResizeHandleMouseEnter()}\n onMouseLeave={() => this.handleResizeHandleMouseLeave()}\n onMouseDown={(e: MouseEvent) => this.handleResizeHandleMouseDown(e)}\n aria-hidden=\"true\"\n />\n )}\n </Host>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,2 +1,2 @@
1
- import{p as n,H as s,c as e,h as o,a,t as i}from"./index.js";import{d as r}from"./p-BSOZXagj.js";const t=()=>`.ds-control--md.sc-ds-panel-sub-nav-h,.ds-control--md.sc-ds-panel-sub-nav{--ds-control-height:var(--dimension-size-400);--ds-control-icon:var(--dimension-iconography-md);--ds-control-padding-inline:var(--dimension-space-075);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:var(--dimension-space-050);--ds-control-radius:var(--dimension-radius-025)}.ds-control--sm.sc-ds-panel-sub-nav-h,.ds-control--sm.sc-ds-panel-sub-nav{--ds-control-height:var(--dimension-size-300);--ds-control-icon:var(--dimension-iconography-sm);--ds-control-padding-inline:var(--dimension-space-050);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:var(--dimension-space-025);--ds-control-radius:var(--dimension-radius-025)}.ds-control--xs.sc-ds-panel-sub-nav-h,.ds-control--xs.sc-ds-panel-sub-nav{--ds-control-height:var(--dimension-size-200);--ds-control-icon:var(--dimension-iconography-xs);--ds-control-padding-inline:var(--dimension-space-025);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:0;--ds-control-radius:var(--dimension-radius-025)}.sc-ds-panel-sub-nav-h{--ds-focus-ring-width:var(--dimension-stroke-width-025, 2px);--ds-focus-ring-offset:var(--dimension-space-025, 2px);--ds-focus-ring-color:var(--color-interaction-focus)}.ds-focus-ring.sc-ds-panel-sub-nav-h:focus-visible,.ds-focus-ring.sc-ds-panel-sub-nav:focus-visible{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:var(--ds-focus-ring-offset)}.ds-focus-ring.ds-focus-ring--visible.sc-ds-panel-sub-nav-h,.ds-focus-ring.ds-focus-ring--visible.sc-ds-panel-sub-nav{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:var(--ds-focus-ring-offset)}.ds-focus-ring-inset.sc-ds-panel-sub-nav-h:focus-visible,.ds-focus-ring-inset.sc-ds-panel-sub-nav:focus-visible{outline:none}.ds-focus-ring-inset.sc-ds-panel-sub-nav-h:focus-visible::after,.ds-focus-ring-inset.sc-ds-panel-sub-nav:focus-visible::after{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:calc(-1 * var(--ds-focus-ring-width))}.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-panel-sub-nav-h,.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-panel-sub-nav{outline:none}.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-panel-sub-nav-h::after,.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-panel-sub-nav::after{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:calc(-1 * var(--ds-focus-ring-width))}.ds-interaction-fill.sc-ds-panel-sub-nav-h,.ds-interaction-fill.sc-ds-panel-sub-nav{position:relative;z-index:0;--ds-interaction-hover:var(--color-interaction-hover);--ds-interaction-pressed:var(--color-interaction-pressed);--ds-interaction-active:var(--color-interaction-active-brand);--ds-interaction-border-width:0px;--ds-interaction-border-color:transparent}.ds-interaction-fill.sc-ds-panel-sub-nav-h::before,.ds-interaction-fill.sc-ds-panel-sub-nav::before,.ds-interaction-fill.sc-ds-panel-sub-nav-h::after,.ds-interaction-fill.sc-ds-panel-sub-nav::after{content:'';position:absolute;inset:0;border-radius:inherit;background:transparent;pointer-events:none;transition:none}.ds-interaction-fill.sc-ds-panel-sub-nav-h::before,.ds-interaction-fill.sc-ds-panel-sub-nav::before{z-index:1}.ds-interaction-fill.sc-ds-panel-sub-nav-h::after,.ds-interaction-fill.sc-ds-panel-sub-nav::after{z-index:3;box-shadow:inset 0 0 0 var(--ds-interaction-border-width) var(--ds-interaction-border-color)}.ds-interaction-fill--bordered.sc-ds-panel-sub-nav-h,.ds-interaction-fill--bordered.sc-ds-panel-sub-nav{--ds-interaction-border-width:var(--dimension-stroke-width-012);--ds-interaction-border-color:var(--color-border-secondary)}.ds-interaction-fill--selected.sc-ds-panel-sub-nav-h::before,.ds-interaction-fill--selected.sc-ds-panel-sub-nav::before{background:var(--ds-interaction-active)}.ds-interaction-fill--selected.sc-ds-panel-sub-nav-h,.ds-interaction-fill--selected.sc-ds-panel-sub-nav{--ds-interaction-dot-ring:var(--ds-interaction-active)}.ds-interaction-fill.sc-ds-panel-sub-nav-h:hover:not(:disabled)::after,.ds-interaction-fill.sc-ds-panel-sub-nav:hover:not(:disabled)::after{background:var(--ds-interaction-hover)}.ds-interaction-fill.sc-ds-panel-sub-nav-h:active:not(:disabled)::after,.ds-interaction-fill.sc-ds-panel-sub-nav:active:not(:disabled)::after{background:var(--ds-interaction-pressed)}.ds-interaction-fill.sc-ds-panel-sub-nav-h>.ds-interaction-fill__content.sc-ds-panel-sub-nav,.ds-interaction-fill.sc-ds-panel-sub-nav>.ds-interaction-fill__content.sc-ds-panel-sub-nav,.ds-interaction-fill__content.sc-ds-panel-sub-nav{position:relative;z-index:2}.ds-interaction-fill--on-faint.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-faint.sc-ds-panel-sub-nav{--ds-interaction-active:var(--color-interaction-active)}.ds-interaction-fill--on-medium.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-medium.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-interaction-on-medium-background-hover);--ds-interaction-pressed:var(--color-interaction-on-medium-background-pressed);--ds-interaction-active:var(--color-interaction-on-medium-background-active);--ds-focus-ring-color:var(--color-interaction-on-medium-background-focus)}.ds-interaction-fill--on-bold.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-bold.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-interaction-on-bold-background-hover);--ds-interaction-pressed:var(--color-interaction-on-bold-background-pressed);--ds-interaction-active:var(--color-interaction-on-bold-background-active);--ds-focus-ring-color:var(--color-interaction-on-bold-background-focus)}.ds-interaction-fill--on-strong.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-strong.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-interaction-on-strong-background-hover);--ds-interaction-pressed:var(--color-interaction-on-strong-background-pressed);--ds-interaction-active:var(--color-interaction-on-strong-background-active);--ds-focus-ring-color:var(--color-interaction-on-strong-background-focus)}.ds-interaction-fill--on-translucent.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-translucent.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-translucent-interaction-hover);--ds-interaction-pressed:var(--color-translucent-interaction-pressed);--ds-interaction-active:var(--color-translucent-interaction-active);--ds-focus-ring-color:var(--color-translucent-interaction-focus)}.ds-interaction-fill--on-inverted.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-inverted.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-inverted-interaction-hover);--ds-interaction-pressed:var(--color-inverted-interaction-pressed);--ds-interaction-active:var(--color-inverted-interaction-active);--ds-focus-ring-color:var(--color-inverted-interaction-focus)}.ds-interaction-fill--on-media.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-media.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-media-interaction-hover);--ds-interaction-pressed:var(--color-media-interaction-pressed);--ds-interaction-active:var(--color-media-interaction-active);--ds-focus-ring-color:var(--color-media-interaction-focus)}.ds-interaction-fill--on-always-dark.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-always-dark.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-always-dark-interaction-hover);--ds-interaction-pressed:var(--color-always-dark-interaction-pressed);--ds-interaction-active:var(--color-always-dark-interaction-active);--ds-focus-ring-color:var(--color-always-dark-interaction-focus)}.ds-interaction-fill--on-navigation.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-navigation.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-navigation-interaction-hover);--ds-interaction-pressed:var(--color-navigation-interaction-pressed);--ds-interaction-active:var(--color-navigation-interaction-active);--ds-focus-ring-color:var(--color-navigation-interaction-focus)}.ds-control-inactive.sc-ds-panel-sub-nav-h,.ds-control-inactive.sc-ds-panel-sub-nav{opacity:0.25;pointer-events:none;cursor:not-allowed}.sc-ds-panel-sub-nav-h{display:block;width:100%}.panel-sub-nav.sc-ds-panel-sub-nav{display:flex;flex-direction:column;gap:var(--dimension-space-050);width:100%}.panel-sub-nav__item.sc-ds-panel-sub-nav{display:flex;align-items:center;justify-content:flex-start;gap:var(--ds-control-gap);width:100%;height:var(--ds-control-height);padding:0 var(--ds-control-padding-inline);box-sizing:border-box;border:none;border-radius:var(--ds-control-radius);background:transparent;color:var(--color-foreground-secondary);cursor:pointer;text-align:left;-webkit-user-select:none;user-select:none;transition:color var(--effect-motion-short-2)}.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-foreground-primary)}.panel-sub-nav__item--on-medium.sc-ds-panel-sub-nav{color:var(--color-foreground-on-medium-background-secondary)}.panel-sub-nav__item--on-medium.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-foreground-on-medium-background-primary)}.panel-sub-nav__item--on-bold.sc-ds-panel-sub-nav{color:var(--color-foreground-on-bold-background-secondary)}.panel-sub-nav__item--on-bold.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-foreground-on-bold-background-primary)}.panel-sub-nav__item--on-strong.sc-ds-panel-sub-nav{color:var(--color-foreground-on-strong-background-secondary)}.panel-sub-nav__item--on-strong.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-foreground-on-strong-background-primary)}.panel-sub-nav__item--on-translucent.sc-ds-panel-sub-nav{color:var(--color-translucent-foreground-secondary)}.panel-sub-nav__item--on-translucent.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-translucent-foreground-primary)}.panel-sub-nav__item--on-inverted.sc-ds-panel-sub-nav{color:var(--color-inverted-foreground-secondary)}.panel-sub-nav__item--on-inverted.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-inverted-foreground-primary)}.panel-sub-nav__item--on-media.sc-ds-panel-sub-nav{color:var(--color-media-foreground-secondary)}.panel-sub-nav__item--on-media.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-media-foreground-primary)}.panel-sub-nav__item--on-always-dark.sc-ds-panel-sub-nav{color:var(--color-always-dark-foreground-secondary)}.panel-sub-nav__item--on-always-dark.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-always-dark-foreground-primary)}ds-text.panel-sub-nav__label.sc-ds-panel-sub-nav{flex:1;min-width:0;padding:0 var(--ds-control-label-inset)}`;const c=n(class n extends s{constructor(n){super();if(n!==false){this.__registerHost()}this.dsChange=e(this,"dsChange");this.value="";this.items=[];this.ariaLabel=null}get enabledItems(){return this.items.filter((n=>!n.isInactive))}get focusableId(){return this.enabledItems.some((n=>n.id===this.value))?this.value:this.enabledItems[0]?.id??""}selectItem(n){this.value=n;this.dsChange.emit(n);this.focusItem(n)}focusItem(n){const s=this.el.querySelector(`[data-panel-sub-nav-id="${n}"]`);s?.focus({preventScroll:true})}onValueChange(n,s){if(s===undefined||n===s)return;requestAnimationFrame((()=>{const s=document.activeElement;if(!s||!this.el.contains(s))return;this.focusItem(this.enabledItems.some((s=>s.id===n))?n:this.focusableId)}))}findEnabled(n,s){const e=this.items.length;for(let o=1;o<=e;o++){const a=(n+s*o+e*o)%e;if(!this.items[a]?.isInactive)return a}return n}handleKeyDown(n){if(!this.enabledItems.length)return;const s=n.target?.closest("[data-panel-sub-nav-id]")?.dataset["panelSubNavId"];const e=this.items.findIndex((n=>n.id===s));const o=this.items.findIndex((n=>n.id===this.value&&!n.isInactive));const a=e>=0?e:o>=0?o:n.key==="ArrowUp"?0:-1;let i=null;if(n.key==="ArrowDown"){i=this.findEnabled(a,1)}else if(n.key==="ArrowUp"){i=this.findEnabled(a,-1)}else if(n.key==="Home"){i=this.items.findIndex((n=>!n.isInactive))}else if(n.key==="End"){const n=[...this.items].reverse().findIndex((n=>!n.isInactive));i=n<0?null:this.items.length-1-n}if(i===null||i<0)return;const r=this.items[i];if(!r||r.isInactive)return;n.preventDefault();this.selectItem(r.id)}render(){const n=this.focusableId;const s=this.background;return o(a,{key:"0bcd21e2e986834c0d2d03c865da4fe98b2dff4e"},o("div",{key:"4df431b1ee20e88b0621d586a020a804454fd10b",class:"panel-sub-nav",role:"tablist","aria-orientation":"vertical","aria-label":this.ariaLabel,"aria-labelledby":this.ariaLabelledby},this.items.map((e=>{const a=e.id===this.value;return o("button",{id:e.id,type:"button",role:"tab","data-panel-sub-nav-id":e.id,class:{"panel-sub-nav__item":true,"panel-sub-nav__item--selected":a,"ds-control--md":true,"ds-focus-ring-inset":true,"ds-interaction-fill":!e.isInactive,"ds-interaction-fill--selected":a&&!e.isInactive,"ds-interaction-fill--on-faint":s==="faint","ds-interaction-fill--on-medium":s==="medium","ds-interaction-fill--on-bold":s==="bold","ds-interaction-fill--on-strong":s==="strong","ds-interaction-fill--on-translucent":s==="translucent","ds-interaction-fill--on-inverted":s==="inverted","ds-interaction-fill--on-media":s==="media","ds-interaction-fill--on-always-dark":s==="always-dark",[`panel-sub-nav__item--on-${s}`]:!!s,"ds-control-inactive":!!e.isInactive},"aria-selected":a?"true":"false","aria-controls":e.panelId,"aria-disabled":e.isInactive?"true":undefined,disabled:e.isInactive,tabIndex:e.id===n?0:-1,onClick:()=>!e.isInactive&&this.selectItem(e.id)},o("ds-text",{class:"panel-sub-nav__label ds-interaction-fill__content",as:"span",variant:"text-body-medium",color:"inherit"},e.label))}))))}get el(){return this}static get watchers(){return{value:[{onValueChange:0}]}}static get style(){return t()}},[2,"ds-panel-sub-nav",{value:[1025],items:[16],background:[1],ariaLabel:[1,"aria-label"],ariaLabelledby:[1,"aria-labelledby"]},[[0,"keydown","handleKeyDown"]],{value:[{onValueChange:0}]}]);function d(){if(typeof customElements==="undefined"){return}const n=["ds-panel-sub-nav","ds-text"];n.forEach((n=>{switch(n){case"ds-panel-sub-nav":if(!customElements.get(i(n))){customElements.define(i(n),c)}break;case"ds-text":if(!customElements.get(i(n))){r()}break}}))}d();const l=c;const u=d;export{l as DsPanelSubNav,u as defineCustomElement};
1
+ import{p as n,H as s,c as e,h as o,a,t as i}from"./index.js";import{d as r}from"./p-CZZYGqKz.js";const t=()=>`.ds-control--md.sc-ds-panel-sub-nav-h,.ds-control--md.sc-ds-panel-sub-nav,.ds-control--sm.sc-ds-panel-sub-nav-h,.ds-control--sm.sc-ds-panel-sub-nav,.ds-control--xs.sc-ds-panel-sub-nav-h,.ds-control--xs.sc-ds-panel-sub-nav{--ds-control-radius:var(--dimension-radius-025)}.ds-control--md.sc-ds-panel-sub-nav-h,.ds-control--md.sc-ds-panel-sub-nav{--ds-control-height:var(--dimension-size-400);--ds-control-icon:var(--dimension-iconography-md);--ds-control-padding-inline:var(--dimension-space-075);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:var(--dimension-space-050)}.ds-control--sm.sc-ds-panel-sub-nav-h,.ds-control--sm.sc-ds-panel-sub-nav{--ds-control-height:var(--dimension-size-300);--ds-control-icon:var(--dimension-iconography-sm);--ds-control-padding-inline:var(--dimension-space-050);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:var(--dimension-space-025)}.ds-control--xs.sc-ds-panel-sub-nav-h,.ds-control--xs.sc-ds-panel-sub-nav{--ds-control-height:var(--dimension-size-200);--ds-control-icon:var(--dimension-iconography-xs);--ds-control-padding-inline:var(--dimension-space-025);--ds-control-label-inset:var(--dimension-space-025);--ds-control-gap:0}.sc-ds-panel-sub-nav-h{--ds-focus-ring-width:var(--dimension-stroke-width-025, 2px);--ds-focus-ring-offset:var(--dimension-space-025, 2px);--ds-focus-ring-color:var(--color-interaction-focus)}.ds-focus-ring.sc-ds-panel-sub-nav-h:focus-visible,.ds-focus-ring.sc-ds-panel-sub-nav:focus-visible{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:var(--ds-focus-ring-offset)}.ds-focus-ring.ds-focus-ring--visible.sc-ds-panel-sub-nav-h,.ds-focus-ring.ds-focus-ring--visible.sc-ds-panel-sub-nav{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:var(--ds-focus-ring-offset)}.ds-focus-ring-inset.sc-ds-panel-sub-nav-h:focus-visible,.ds-focus-ring-inset.sc-ds-panel-sub-nav:focus-visible{outline:none}.ds-focus-ring-inset.sc-ds-panel-sub-nav-h:focus-visible::after,.ds-focus-ring-inset.sc-ds-panel-sub-nav:focus-visible::after{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:calc(-1 * var(--ds-focus-ring-width))}.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-panel-sub-nav-h,.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-panel-sub-nav{outline:none}.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-panel-sub-nav-h::after,.ds-focus-ring-inset.ds-focus-ring--visible.sc-ds-panel-sub-nav::after{outline:var(--ds-focus-ring-width) solid var(--ds-focus-ring-color);outline-offset:calc(-1 * var(--ds-focus-ring-width))}.ds-interaction-fill.sc-ds-panel-sub-nav-h,.ds-interaction-fill.sc-ds-panel-sub-nav{position:relative;z-index:0;--ds-interaction-hover:var(--color-interaction-hover);--ds-interaction-pressed:var(--color-interaction-pressed);--ds-interaction-active:var(--color-interaction-active-brand);--ds-interaction-border-width:0px;--ds-interaction-border-color:transparent}.ds-interaction-fill.sc-ds-panel-sub-nav-h::before,.ds-interaction-fill.sc-ds-panel-sub-nav::before,.ds-interaction-fill.sc-ds-panel-sub-nav-h::after,.ds-interaction-fill.sc-ds-panel-sub-nav::after{content:'';position:absolute;inset:0;border-radius:inherit;background:transparent;pointer-events:none;transition:none}.ds-interaction-fill.sc-ds-panel-sub-nav-h::before,.ds-interaction-fill.sc-ds-panel-sub-nav::before{z-index:1}.ds-interaction-fill.sc-ds-panel-sub-nav-h::after,.ds-interaction-fill.sc-ds-panel-sub-nav::after{z-index:3;box-shadow:inset 0 0 0 var(--ds-interaction-border-width) var(--ds-interaction-border-color)}.ds-interaction-fill--bordered.sc-ds-panel-sub-nav-h,.ds-interaction-fill--bordered.sc-ds-panel-sub-nav{--ds-interaction-border-width:var(--dimension-stroke-width-012);--ds-interaction-border-color:var(--color-border-secondary)}.ds-interaction-fill--selected.sc-ds-panel-sub-nav-h::before,.ds-interaction-fill--selected.sc-ds-panel-sub-nav::before{background:var(--ds-interaction-active)}.ds-interaction-fill--selected.sc-ds-panel-sub-nav-h,.ds-interaction-fill--selected.sc-ds-panel-sub-nav{--ds-interaction-dot-ring:var(--ds-interaction-active)}.ds-interaction-fill.sc-ds-panel-sub-nav-h:hover:not(:disabled)::after,.ds-interaction-fill.sc-ds-panel-sub-nav:hover:not(:disabled)::after{background:var(--ds-interaction-hover)}.ds-interaction-fill.sc-ds-panel-sub-nav-h:active:not(:disabled)::after,.ds-interaction-fill.sc-ds-panel-sub-nav:active:not(:disabled)::after{background:var(--ds-interaction-pressed)}.ds-interaction-fill.sc-ds-panel-sub-nav-h>.ds-interaction-fill__content.sc-ds-panel-sub-nav,.ds-interaction-fill.sc-ds-panel-sub-nav>.ds-interaction-fill__content.sc-ds-panel-sub-nav,.ds-interaction-fill__content.sc-ds-panel-sub-nav{position:relative;z-index:2}.ds-interaction-fill--on-faint.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-faint.sc-ds-panel-sub-nav{--ds-interaction-active:var(--color-interaction-active)}.ds-interaction-fill--on-medium.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-medium.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-interaction-on-medium-background-hover);--ds-interaction-pressed:var(--color-interaction-on-medium-background-pressed);--ds-interaction-active:var(--color-interaction-on-medium-background-active);--ds-focus-ring-color:var(--color-interaction-on-medium-background-focus)}.ds-interaction-fill--on-bold.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-bold.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-interaction-on-bold-background-hover);--ds-interaction-pressed:var(--color-interaction-on-bold-background-pressed);--ds-interaction-active:var(--color-interaction-on-bold-background-active);--ds-focus-ring-color:var(--color-interaction-on-bold-background-focus)}.ds-interaction-fill--on-strong.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-strong.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-interaction-on-strong-background-hover);--ds-interaction-pressed:var(--color-interaction-on-strong-background-pressed);--ds-interaction-active:var(--color-interaction-on-strong-background-active);--ds-focus-ring-color:var(--color-interaction-on-strong-background-focus)}.ds-interaction-fill--on-translucent.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-translucent.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-translucent-interaction-hover);--ds-interaction-pressed:var(--color-translucent-interaction-pressed);--ds-interaction-active:var(--color-translucent-interaction-active);--ds-focus-ring-color:var(--color-translucent-interaction-focus)}.ds-interaction-fill--on-inverted.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-inverted.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-inverted-interaction-hover);--ds-interaction-pressed:var(--color-inverted-interaction-pressed);--ds-interaction-active:var(--color-inverted-interaction-active);--ds-focus-ring-color:var(--color-inverted-interaction-focus)}.ds-interaction-fill--on-media.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-media.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-media-interaction-hover);--ds-interaction-pressed:var(--color-media-interaction-pressed);--ds-interaction-active:var(--color-media-interaction-active);--ds-focus-ring-color:var(--color-media-interaction-focus)}.ds-interaction-fill--on-always-dark.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-always-dark.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-always-dark-interaction-hover);--ds-interaction-pressed:var(--color-always-dark-interaction-pressed);--ds-interaction-active:var(--color-always-dark-interaction-active);--ds-focus-ring-color:var(--color-always-dark-interaction-focus)}.ds-interaction-fill--on-navigation.sc-ds-panel-sub-nav-h,.ds-interaction-fill--on-navigation.sc-ds-panel-sub-nav{--ds-interaction-hover:var(--color-navigation-interaction-hover);--ds-interaction-pressed:var(--color-navigation-interaction-pressed);--ds-interaction-active:var(--color-navigation-interaction-active);--ds-focus-ring-color:var(--color-navigation-interaction-focus)}.ds-control-inactive.sc-ds-panel-sub-nav-h,.ds-control-inactive.sc-ds-panel-sub-nav{opacity:0.25;pointer-events:none;cursor:not-allowed}.sc-ds-panel-sub-nav-h{display:block;width:100%}.panel-sub-nav.sc-ds-panel-sub-nav{display:flex;flex-direction:column;gap:var(--dimension-space-050);width:100%}.panel-sub-nav__item.sc-ds-panel-sub-nav{display:flex;align-items:center;justify-content:flex-start;gap:var(--ds-control-gap);width:100%;height:var(--ds-control-height);padding:0 var(--ds-control-padding-inline);box-sizing:border-box;border:none;border-radius:var(--ds-control-radius);background:transparent;color:var(--color-foreground-secondary);cursor:pointer;text-align:left;-webkit-user-select:none;user-select:none;transition:color var(--effect-motion-short-2)}.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-foreground-primary)}.panel-sub-nav__item--on-medium.sc-ds-panel-sub-nav{color:var(--color-foreground-on-medium-background-secondary)}.panel-sub-nav__item--on-medium.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-foreground-on-medium-background-primary)}.panel-sub-nav__item--on-bold.sc-ds-panel-sub-nav{color:var(--color-foreground-on-bold-background-secondary)}.panel-sub-nav__item--on-bold.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-foreground-on-bold-background-primary)}.panel-sub-nav__item--on-strong.sc-ds-panel-sub-nav{color:var(--color-foreground-on-strong-background-secondary)}.panel-sub-nav__item--on-strong.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-foreground-on-strong-background-primary)}.panel-sub-nav__item--on-translucent.sc-ds-panel-sub-nav{color:var(--color-translucent-foreground-secondary)}.panel-sub-nav__item--on-translucent.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-translucent-foreground-primary)}.panel-sub-nav__item--on-inverted.sc-ds-panel-sub-nav{color:var(--color-inverted-foreground-secondary)}.panel-sub-nav__item--on-inverted.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-inverted-foreground-primary)}.panel-sub-nav__item--on-media.sc-ds-panel-sub-nav{color:var(--color-media-foreground-secondary)}.panel-sub-nav__item--on-media.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-media-foreground-primary)}.panel-sub-nav__item--on-always-dark.sc-ds-panel-sub-nav{color:var(--color-always-dark-foreground-secondary)}.panel-sub-nav__item--on-always-dark.panel-sub-nav__item--selected.sc-ds-panel-sub-nav{color:var(--color-always-dark-foreground-primary)}ds-text.panel-sub-nav__label.sc-ds-panel-sub-nav{flex:1;min-width:0;padding:0 var(--ds-control-label-inset)}`;const c=n(class n extends s{constructor(n){super();if(n!==false){this.__registerHost()}this.dsChange=e(this,"dsChange");this.value="";this.items=[];this.ariaLabel=null}get enabledItems(){return this.items.filter((n=>!n.isInactive))}get focusableId(){return this.enabledItems.some((n=>n.id===this.value))?this.value:this.enabledItems[0]?.id??""}selectItem(n){this.value=n;this.dsChange.emit(n);this.focusItem(n)}focusItem(n){const s=this.el.querySelector(`[data-panel-sub-nav-id="${n}"]`);s?.focus({preventScroll:true})}onValueChange(n,s){if(s===undefined||n===s)return;requestAnimationFrame((()=>{const s=document.activeElement;if(!s||!this.el.contains(s))return;this.focusItem(this.enabledItems.some((s=>s.id===n))?n:this.focusableId)}))}findEnabled(n,s){const e=this.items.length;for(let o=1;o<=e;o++){const a=(n+s*o+e*o)%e;if(!this.items[a]?.isInactive)return a}return n}handleKeyDown(n){if(!this.enabledItems.length)return;const s=n.target?.closest("[data-panel-sub-nav-id]")?.dataset["panelSubNavId"];const e=this.items.findIndex((n=>n.id===s));const o=this.items.findIndex((n=>n.id===this.value&&!n.isInactive));const a=e>=0?e:o>=0?o:n.key==="ArrowUp"?0:-1;let i=null;if(n.key==="ArrowDown"){i=this.findEnabled(a,1)}else if(n.key==="ArrowUp"){i=this.findEnabled(a,-1)}else if(n.key==="Home"){i=this.items.findIndex((n=>!n.isInactive))}else if(n.key==="End"){const n=[...this.items].reverse().findIndex((n=>!n.isInactive));i=n<0?null:this.items.length-1-n}if(i===null||i<0)return;const r=this.items[i];if(!r||r.isInactive)return;n.preventDefault();this.selectItem(r.id)}render(){const n=this.focusableId;const s=this.background;return o(a,{key:"de50cdced379e9060ba7d5fccf25ae0f247de561"},o("div",{key:"c4e384a920dc2e2fdf5abc0e4328e79014e9784f",class:"panel-sub-nav",role:"tablist","aria-orientation":"vertical","aria-label":this.ariaLabel,"aria-labelledby":this.ariaLabelledby},this.items.map((e=>{const a=e.id===this.value;return o("button",{id:e.id,type:"button",role:"tab","data-panel-sub-nav-id":e.id,class:{"panel-sub-nav__item":true,"panel-sub-nav__item--selected":a,"ds-control--md":true,"ds-focus-ring-inset":true,"ds-interaction-fill":!e.isInactive,"ds-interaction-fill--selected":a&&!e.isInactive,"ds-interaction-fill--on-faint":s==="faint","ds-interaction-fill--on-medium":s==="medium","ds-interaction-fill--on-bold":s==="bold","ds-interaction-fill--on-strong":s==="strong","ds-interaction-fill--on-translucent":s==="translucent","ds-interaction-fill--on-inverted":s==="inverted","ds-interaction-fill--on-media":s==="media","ds-interaction-fill--on-always-dark":s==="always-dark",[`panel-sub-nav__item--on-${s}`]:!!s,"ds-control-inactive":!!e.isInactive},"aria-selected":a?"true":"false","aria-controls":e.panelId,"aria-disabled":e.isInactive?"true":undefined,disabled:e.isInactive,tabIndex:e.id===n?0:-1,onClick:()=>!e.isInactive&&this.selectItem(e.id)},o("ds-text",{class:"panel-sub-nav__label ds-interaction-fill__content",as:"span",variant:"text-body-medium",color:"inherit"},e.label))}))))}get el(){return this}static get watchers(){return{value:[{onValueChange:0}]}}static get style(){return t()}},[2,"ds-panel-sub-nav",{value:[1025],items:[16],background:[1],ariaLabel:[1,"aria-label"],ariaLabelledby:[1,"aria-labelledby"]},[[0,"keydown","handleKeyDown"]],{value:[{onValueChange:0}]}]);function d(){if(typeof customElements==="undefined"){return}const n=["ds-panel-sub-nav","ds-text"];n.forEach((n=>{switch(n){case"ds-panel-sub-nav":if(!customElements.get(i(n))){customElements.define(i(n),c)}break;case"ds-text":if(!customElements.get(i(n))){r()}break}}))}d();const l=c;const u=d;export{l as DsPanelSubNav,u as defineCustomElement};
2
2
  //# sourceMappingURL=ds-panel-sub-nav.js.map