@ojiepermana/angular 22.1.10 → 22.1.12

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 (340) hide show
  1. package/README.md +32 -26
  2. package/chart/README.md +272 -0
  3. package/component/README.md +89 -0
  4. package/component/accordion/README.md +195 -0
  5. package/component/alert/README.md +184 -0
  6. package/component/alert-dialog/README.md +255 -0
  7. package/component/aspect-ratio/README.md +116 -0
  8. package/component/avatar/README.md +182 -0
  9. package/component/badge/README.md +143 -0
  10. package/component/breadcrumb/README.md +223 -0
  11. package/component/button/README.md +141 -0
  12. package/component/button-group/README.md +214 -0
  13. package/component/calendar/README.md +137 -0
  14. package/component/card/README.md +232 -0
  15. package/component/carousel/README.md +276 -0
  16. package/component/checkbox/README.md +153 -0
  17. package/component/collapsible/README.md +216 -0
  18. package/component/combobox/README.md +202 -0
  19. package/component/command/README.md +287 -0
  20. package/component/composer/README.md +241 -0
  21. package/component/context-menu/README.md +268 -0
  22. package/component/date-picker/README.md +202 -0
  23. package/component/dialog/README.md +276 -0
  24. package/component/drawer/README.md +147 -0
  25. package/component/dropdown-menu/README.md +336 -0
  26. package/component/editor/README.md +152 -0
  27. package/component/empty/README.md +193 -0
  28. package/component/form/README.md +216 -0
  29. package/component/hover-card/README.md +150 -0
  30. package/component/icon/README.md +25 -0
  31. package/component/input/README.md +160 -0
  32. package/component/input-group/README.md +245 -0
  33. package/component/input-otp/README.md +331 -0
  34. package/component/item/README.md +254 -0
  35. package/component/kanban/README.md +81 -0
  36. package/component/kbd/README.md +141 -0
  37. package/component/label/README.md +137 -0
  38. package/component/menubar/README.md +270 -0
  39. package/component/native-select/README.md +176 -0
  40. package/component/navigation-menu/README.md +165 -0
  41. package/component/pagination/README.md +151 -0
  42. package/component/pillbox/README.md +68 -0
  43. package/component/popover/README.md +54 -0
  44. package/component/progress/README.md +160 -0
  45. package/component/radio/README.md +212 -0
  46. package/component/resizable/README.md +178 -0
  47. package/component/scroll-area/README.md +150 -0
  48. package/component/select/README.md +228 -0
  49. package/component/separator/README.md +175 -0
  50. package/component/sheet/README.md +190 -0
  51. package/component/skeleton/README.md +158 -0
  52. package/component/slider/README.md +207 -0
  53. package/component/spinner/README.md +160 -0
  54. package/component/switch/README.md +170 -0
  55. package/component/table/README.md +292 -0
  56. package/component/tabs/README.md +220 -0
  57. package/component/textarea/README.md +169 -0
  58. package/component/timeline/README.md +94 -0
  59. package/component/toast/README.md +173 -0
  60. package/component/toggle/README.md +133 -0
  61. package/component/toggle-group/README.md +234 -0
  62. package/component/tooltip/README.md +230 -0
  63. package/fesm2022/ojiepermana-angular-chart-area.mjs +289 -3
  64. package/fesm2022/ojiepermana-angular-chart-bar.mjs +722 -3
  65. package/fesm2022/ojiepermana-angular-chart-core.mjs +1482 -3
  66. package/fesm2022/ojiepermana-angular-chart-line.mjs +294 -3
  67. package/fesm2022/ojiepermana-angular-chart-pie.mjs +291 -3
  68. package/fesm2022/ojiepermana-angular-chart-primitives.mjs +1301 -3
  69. package/fesm2022/ojiepermana-angular-chart-radar.mjs +421 -3
  70. package/fesm2022/ojiepermana-angular-chart-radial.mjs +295 -3
  71. package/fesm2022/ojiepermana-angular-chart-scatter.mjs +306 -3
  72. package/fesm2022/ojiepermana-angular-chart.mjs +12 -3
  73. package/fesm2022/ojiepermana-angular-component-accordion.mjs +267 -3
  74. package/fesm2022/ojiepermana-angular-component-alert-dialog.mjs +359 -3
  75. package/fesm2022/ojiepermana-angular-component-alert.mjs +101 -3
  76. package/fesm2022/ojiepermana-angular-component-aspect-ratio.mjs +31 -3
  77. package/fesm2022/ojiepermana-angular-component-avatar.mjs +139 -3
  78. package/fesm2022/ojiepermana-angular-component-badge.mjs +57 -3
  79. package/fesm2022/ojiepermana-angular-component-breadcrumb.mjs +192 -3
  80. package/fesm2022/ojiepermana-angular-component-button-group.mjs +95 -3
  81. package/fesm2022/ojiepermana-angular-component-button.mjs +73 -3
  82. package/fesm2022/ojiepermana-angular-component-calendar.mjs +718 -3
  83. package/fesm2022/ojiepermana-angular-component-card.mjs +167 -3
  84. package/fesm2022/ojiepermana-angular-component-carousel.mjs +326 -3
  85. package/fesm2022/ojiepermana-angular-component-checkbox.mjs +190 -3
  86. package/fesm2022/ojiepermana-angular-component-collapsible.mjs +117 -3
  87. package/fesm2022/ojiepermana-angular-component-combobox.mjs +327 -3
  88. package/fesm2022/ojiepermana-angular-component-command.mjs +351 -3
  89. package/fesm2022/ojiepermana-angular-component-composer.mjs +359 -3
  90. package/fesm2022/ojiepermana-angular-component-context-menu.mjs +158 -3
  91. package/fesm2022/ojiepermana-angular-component-date-picker.mjs +482 -3
  92. package/fesm2022/ojiepermana-angular-component-dialog.mjs +400 -3
  93. package/fesm2022/ojiepermana-angular-component-drawer.mjs +1 -3
  94. package/fesm2022/ojiepermana-angular-component-dropdown-menu.mjs +689 -3
  95. package/fesm2022/ojiepermana-angular-component-editor.mjs +511 -3
  96. package/fesm2022/ojiepermana-angular-component-empty.mjs +134 -3
  97. package/fesm2022/ojiepermana-angular-component-form.mjs +438 -3
  98. package/fesm2022/ojiepermana-angular-component-hover-card.mjs +273 -3
  99. package/fesm2022/ojiepermana-angular-component-icon.mjs +139 -3
  100. package/fesm2022/ojiepermana-angular-component-input-group.mjs +176 -3
  101. package/fesm2022/ojiepermana-angular-component-input-otp.mjs +550 -3
  102. package/fesm2022/ojiepermana-angular-component-input.mjs +52 -3
  103. package/fesm2022/ojiepermana-angular-component-item.mjs +264 -3
  104. package/fesm2022/ojiepermana-angular-component-kanban.mjs +310 -3
  105. package/fesm2022/ojiepermana-angular-component-kbd.mjs +48 -3
  106. package/fesm2022/ojiepermana-angular-component-label.mjs +27 -3
  107. package/fesm2022/ojiepermana-angular-component-menubar.mjs +327 -3
  108. package/fesm2022/ojiepermana-angular-component-native-select.mjs +62 -3
  109. package/fesm2022/ojiepermana-angular-component-navigation-menu.mjs +378 -3
  110. package/fesm2022/ojiepermana-angular-component-pagination.mjs +238 -3
  111. package/fesm2022/ojiepermana-angular-component-pillbox.mjs +999 -3
  112. package/fesm2022/ojiepermana-angular-component-popover.mjs +201 -3
  113. package/fesm2022/ojiepermana-angular-component-progress.mjs +57 -3
  114. package/fesm2022/ojiepermana-angular-component-radio.mjs +180 -3
  115. package/fesm2022/ojiepermana-angular-component-resizable.mjs +493 -3
  116. package/fesm2022/ojiepermana-angular-component-scroll-area.mjs +51 -3
  117. package/fesm2022/ojiepermana-angular-component-segmented-control.mjs +227 -3
  118. package/fesm2022/ojiepermana-angular-component-select.mjs +609 -3
  119. package/fesm2022/ojiepermana-angular-component-separator.mjs +31 -3
  120. package/fesm2022/ojiepermana-angular-component-sheet.mjs +407 -3
  121. package/fesm2022/ojiepermana-angular-component-skeleton.mjs +25 -3
  122. package/fesm2022/ojiepermana-angular-component-slider.mjs +456 -3
  123. package/fesm2022/ojiepermana-angular-component-spinner.mjs +56 -3
  124. package/fesm2022/ojiepermana-angular-component-switch.mjs +145 -3
  125. package/fesm2022/ojiepermana-angular-component-table.mjs +155 -3
  126. package/fesm2022/ojiepermana-angular-component-tabs.mjs +315 -3
  127. package/fesm2022/ojiepermana-angular-component-textarea.mjs +46 -3
  128. package/fesm2022/ojiepermana-angular-component-timeline.mjs +237 -3
  129. package/fesm2022/ojiepermana-angular-component-toast.mjs +241 -3
  130. package/fesm2022/ojiepermana-angular-component-toggle-group.mjs +290 -3
  131. package/fesm2022/ojiepermana-angular-component-toggle.mjs +76 -3
  132. package/fesm2022/ojiepermana-angular-component-tooltip.mjs +519 -3
  133. package/fesm2022/ojiepermana-angular-component-utils-core.mjs +310 -3
  134. package/fesm2022/ojiepermana-angular-component-utils-overlay.mjs +62 -3
  135. package/fesm2022/ojiepermana-angular-component-utils-scale.mjs +34 -3
  136. package/fesm2022/ojiepermana-angular-component-utils.mjs +3 -2
  137. package/fesm2022/ojiepermana-angular-component.mjs +4 -4
  138. package/fesm2022/ojiepermana-angular-navigation-core.mjs +21 -3
  139. package/fesm2022/ojiepermana-angular-navigation-icon.mjs +20 -3
  140. package/fesm2022/ojiepermana-angular-navigation-navigation-badge-count.component-CDBxQ8rk.mjs +91 -0
  141. package/fesm2022/ojiepermana-angular-navigation-navigation-dockbar-menu.component-DBQHdvGa.mjs +512 -0
  142. package/fesm2022/ojiepermana-angular-navigation-navigation-entry-grid.component-Cd-jh6tg.mjs +623 -0
  143. package/fesm2022/ojiepermana-angular-navigation-navigation-flyout-menu.component-BCGvhoYj.mjs +404 -0
  144. package/fesm2022/ojiepermana-angular-navigation-navigation-horizontal.component-Dk_03e-6.mjs +466 -0
  145. package/fesm2022/ojiepermana-angular-navigation-ojiepermana-angular-navigation-DNKU6viC.mjs +2642 -0
  146. package/fesm2022/ojiepermana-angular-navigation-service.mjs +725 -3
  147. package/fesm2022/ojiepermana-angular-navigation-types.mjs +65 -4
  148. package/fesm2022/ojiepermana-angular-navigation.mjs +4 -10
  149. package/fesm2022/ojiepermana-angular-sdk.mjs +204 -4
  150. package/fesm2022/ojiepermana-angular-theme-component-settings.mjs +1387 -3
  151. package/fesm2022/ojiepermana-angular-theme-layout-services.mjs +331 -3
  152. package/fesm2022/ojiepermana-angular-theme-layout-types.mjs +45 -4
  153. package/fesm2022/ojiepermana-angular-theme-layout-wrapper.mjs +1044 -3
  154. package/fesm2022/ojiepermana-angular-theme-layout.mjs +729 -3
  155. package/fesm2022/ojiepermana-angular-theme-page-core.mjs +381 -3
  156. package/fesm2022/ojiepermana-angular-theme-page-filter.mjs +267 -3
  157. package/fesm2022/ojiepermana-angular-theme-page-root.mjs +361 -3
  158. package/fesm2022/ojiepermana-angular-theme-page-side.mjs +198 -3
  159. package/fesm2022/ojiepermana-angular-theme-page-slots.mjs +110 -3
  160. package/fesm2022/ojiepermana-angular-theme-page.mjs +7 -2
  161. package/fesm2022/ojiepermana-angular-theme-shell-services.mjs +338 -3
  162. package/fesm2022/ojiepermana-angular-theme-shell-types.mjs +160 -4
  163. package/fesm2022/ojiepermana-angular-theme-shell.mjs +811 -3
  164. package/fesm2022/ojiepermana-angular-theme-styles.mjs +783 -3
  165. package/fesm2022/ojiepermana-angular-theme.mjs +4 -4
  166. package/fesm2022/ojiepermana-angular.mjs +6 -11
  167. package/navigation/README.md +374 -0
  168. package/package.json +81 -20
  169. package/sdk/README.md +212 -0
  170. package/sdk/schematics/collection.json +20 -0
  171. package/sdk/schematics/init/index.js +83 -0
  172. package/sdk/schematics/init/schema.json +19 -0
  173. package/sdk/schematics/ng-add/index.js +147 -0
  174. package/sdk/schematics/ng-add/schema.json +14 -0
  175. package/sdk/schematics/package.json +3 -0
  176. package/sdk/schematics/sdk/index.js +175 -0
  177. package/sdk/schematics/sdk/schema.json +19 -0
  178. package/sdk/sdk.config.example.json +24 -0
  179. package/sdk/src/config/loader.js +43 -0
  180. package/sdk/src/config/schema.js +59 -0
  181. package/sdk/src/emit/client.js +251 -0
  182. package/sdk/src/emit/metadata.js +310 -0
  183. package/sdk/src/emit/models.js +104 -0
  184. package/sdk/src/emit/navigation.js +56 -0
  185. package/sdk/src/emit/operations.js +118 -0
  186. package/sdk/src/emit/public-api.js +54 -0
  187. package/sdk/src/emit/services.js +110 -0
  188. package/sdk/src/engine.js +67 -0
  189. package/sdk/src/layout/per-domain.js +360 -0
  190. package/sdk/src/package.json +3 -0
  191. package/sdk/src/parser/bundle.js +25 -0
  192. package/sdk/src/parser/ir.js +429 -0
  193. package/sdk/src/parser/types.js +7 -0
  194. package/sdk/src/render/template.js +152 -0
  195. package/sdk/src/writer/index.js +286 -0
  196. package/theme/README.md +126 -0
  197. package/theme/component/settings/README.md +135 -0
  198. package/theme/layout/README.md +539 -0
  199. package/theme/page/README.md +453 -0
  200. package/theme/shell/README.md +340 -0
  201. package/theme/styles/README.md +178 -0
  202. package/theme/styles/css/base/components.css +553 -0
  203. package/theme/styles/css/base/index.css +14 -0
  204. package/theme/styles/css/base/package.css +9 -0
  205. package/theme/styles/css/base/prefers-dark.css +86 -0
  206. package/theme/styles/css/base/print.css +62 -0
  207. package/theme/styles/css/base/status.css +38 -0
  208. package/theme/styles/css/base/tailwind.css +165 -0
  209. package/theme/styles/css/base/theme.css +373 -0
  210. package/theme/styles/css/base/tokens.css +361 -0
  211. package/theme/styles/css/color/amber.css +72 -0
  212. package/theme/styles/css/color/blue.css +72 -0
  213. package/theme/styles/css/color/brand.css +38 -0
  214. package/theme/styles/css/color/cyan.css +72 -0
  215. package/theme/styles/css/color/emerald.css +72 -0
  216. package/theme/styles/css/color/fuchsia.css +72 -0
  217. package/theme/styles/css/color/green.css +72 -0
  218. package/theme/styles/css/color/index.css +19 -0
  219. package/theme/styles/css/color/indigo.css +72 -0
  220. package/theme/styles/css/color/lime.css +72 -0
  221. package/theme/styles/css/color/orange.css +72 -0
  222. package/theme/styles/css/color/pink.css +72 -0
  223. package/theme/styles/css/color/purple.css +72 -0
  224. package/theme/styles/css/color/red.css +72 -0
  225. package/theme/styles/css/color/rose.css +72 -0
  226. package/theme/styles/css/color/sky.css +72 -0
  227. package/theme/styles/css/color/teal.css +72 -0
  228. package/theme/styles/css/color/violet.css +72 -0
  229. package/theme/styles/css/color/yellow.css +72 -0
  230. package/theme/styles/css/index.css +18 -0
  231. package/theme/styles/css/neutral/gray.css +63 -0
  232. package/theme/styles/css/neutral/index.css +11 -0
  233. package/theme/styles/css/neutral/mauve.css +63 -0
  234. package/theme/styles/css/neutral/mist.css +63 -0
  235. package/theme/styles/css/neutral/neutral.css +63 -0
  236. package/theme/styles/css/neutral/olive.css +63 -0
  237. package/theme/styles/css/neutral/slate.css +63 -0
  238. package/theme/styles/css/neutral/stone.css +63 -0
  239. package/theme/styles/css/neutral/taupe.css +63 -0
  240. package/theme/styles/css/neutral/zinc.css +63 -0
  241. package/theme/styles/css/radius/index.css +30 -0
  242. package/theme/styles/css/space/index.css +20 -0
  243. package/types/ojiepermana-angular-chart-area.d.ts +76 -1
  244. package/types/ojiepermana-angular-chart-bar.d.ts +201 -1
  245. package/types/ojiepermana-angular-chart-core.d.ts +811 -1
  246. package/types/ojiepermana-angular-chart-line.d.ts +75 -1
  247. package/types/ojiepermana-angular-chart-pie.d.ts +131 -1
  248. package/types/ojiepermana-angular-chart-primitives.d.ts +291 -1
  249. package/types/ojiepermana-angular-chart-radar.d.ts +116 -1
  250. package/types/ojiepermana-angular-chart-radial.d.ts +116 -1
  251. package/types/ojiepermana-angular-chart-scatter.d.ts +125 -1
  252. package/types/ojiepermana-angular-chart.d.ts +2 -1
  253. package/types/ojiepermana-angular-component-accordion.d.ts +85 -1
  254. package/types/ojiepermana-angular-component-alert-dialog.d.ts +140 -1
  255. package/types/ojiepermana-angular-component-alert.d.ts +42 -1
  256. package/types/ojiepermana-angular-component-aspect-ratio.d.ts +12 -1
  257. package/types/ojiepermana-angular-component-avatar.d.ts +56 -1
  258. package/types/ojiepermana-angular-component-badge.d.ts +24 -1
  259. package/types/ojiepermana-angular-component-breadcrumb.d.ts +46 -1
  260. package/types/ojiepermana-angular-component-button-group.d.ts +26 -1
  261. package/types/ojiepermana-angular-component-button.d.ts +27 -1
  262. package/types/ojiepermana-angular-component-calendar.d.ts +111 -1
  263. package/types/ojiepermana-angular-component-card.d.ts +69 -1
  264. package/types/ojiepermana-angular-component-carousel.d.ts +86 -1
  265. package/types/ojiepermana-angular-component-checkbox.d.ts +42 -1
  266. package/types/ojiepermana-angular-component-collapsible.d.ts +42 -1
  267. package/types/ojiepermana-angular-component-combobox.d.ts +60 -1
  268. package/types/ojiepermana-angular-component-command.d.ts +130 -1
  269. package/types/ojiepermana-angular-component-composer.d.ts +90 -1
  270. package/types/ojiepermana-angular-component-context-menu.d.ts +54 -1
  271. package/types/ojiepermana-angular-component-date-picker.d.ts +109 -1
  272. package/types/ojiepermana-angular-component-dialog.d.ts +136 -1
  273. package/types/ojiepermana-angular-component-drawer.d.ts +1 -1
  274. package/types/ojiepermana-angular-component-dropdown-menu.d.ts +248 -1
  275. package/types/ojiepermana-angular-component-editor.d.ts +135 -1
  276. package/types/ojiepermana-angular-component-empty.d.ts +50 -1
  277. package/types/ojiepermana-angular-component-form.d.ts +159 -1
  278. package/types/ojiepermana-angular-component-hover-card.d.ts +75 -1
  279. package/types/ojiepermana-angular-component-icon.d.ts +89 -1
  280. package/types/ojiepermana-angular-component-input-group.d.ts +51 -1
  281. package/types/ojiepermana-angular-component-input-otp.d.ts +155 -1
  282. package/types/ojiepermana-angular-component-input.d.ts +21 -1
  283. package/types/ojiepermana-angular-component-item.d.ts +93 -1
  284. package/types/ojiepermana-angular-component-kanban.d.ts +70 -1
  285. package/types/ojiepermana-angular-component-kbd.d.ts +16 -1
  286. package/types/ojiepermana-angular-component-label.d.ts +11 -1
  287. package/types/ojiepermana-angular-component-menubar.d.ts +93 -1
  288. package/types/ojiepermana-angular-component-native-select.d.ts +26 -1
  289. package/types/ojiepermana-angular-component-navigation-menu.d.ts +104 -1
  290. package/types/ojiepermana-angular-component-pagination.d.ts +33 -1
  291. package/types/ojiepermana-angular-component-pillbox.d.ts +222 -1
  292. package/types/ojiepermana-angular-component-popover.d.ts +66 -1
  293. package/types/ojiepermana-angular-component-progress.d.ts +17 -1
  294. package/types/ojiepermana-angular-component-radio.d.ts +60 -1
  295. package/types/ojiepermana-angular-component-resizable.d.ts +117 -1
  296. package/types/ojiepermana-angular-component-scroll-area.d.ts +19 -1
  297. package/types/ojiepermana-angular-component-segmented-control.d.ts +58 -1
  298. package/types/ojiepermana-angular-component-select.d.ts +122 -1
  299. package/types/ojiepermana-angular-component-separator.d.ts +14 -1
  300. package/types/ojiepermana-angular-component-sheet.d.ts +130 -1
  301. package/types/ojiepermana-angular-component-skeleton.d.ts +10 -1
  302. package/types/ojiepermana-angular-component-slider.d.ts +83 -1
  303. package/types/ojiepermana-angular-component-spinner.d.ts +13 -1
  304. package/types/ojiepermana-angular-component-switch.d.ts +44 -1
  305. package/types/ojiepermana-angular-component-table.d.ts +57 -1
  306. package/types/ojiepermana-angular-component-tabs.d.ts +124 -1
  307. package/types/ojiepermana-angular-component-textarea.d.ts +17 -1
  308. package/types/ojiepermana-angular-component-timeline.d.ts +64 -1
  309. package/types/ojiepermana-angular-component-toast.d.ts +64 -1
  310. package/types/ojiepermana-angular-component-toggle-group.d.ts +90 -1
  311. package/types/ojiepermana-angular-component-toggle.d.ts +25 -1
  312. package/types/ojiepermana-angular-component-tooltip.d.ts +143 -1
  313. package/types/ojiepermana-angular-component-utils-core.d.ts +184 -1
  314. package/types/ojiepermana-angular-component-utils-overlay.d.ts +21 -1
  315. package/types/ojiepermana-angular-component-utils-scale.d.ts +19 -1
  316. package/types/ojiepermana-angular-component-utils.d.ts +2 -1
  317. package/types/ojiepermana-angular-component.d.ts +2 -1
  318. package/types/ojiepermana-angular-navigation-core.d.ts +59 -1
  319. package/types/ojiepermana-angular-navigation-icon.d.ts +12 -1
  320. package/types/ojiepermana-angular-navigation-service.d.ts +116 -1
  321. package/types/ojiepermana-angular-navigation-types.d.ts +144 -1
  322. package/types/ojiepermana-angular-navigation.d.ts +507 -1
  323. package/types/ojiepermana-angular-sdk.d.ts +89 -1
  324. package/types/ojiepermana-angular-theme-component-settings.d.ts +154 -1
  325. package/types/ojiepermana-angular-theme-layout-services.d.ts +147 -1
  326. package/types/ojiepermana-angular-theme-layout-types.d.ts +40 -1
  327. package/types/ojiepermana-angular-theme-layout-wrapper.d.ts +299 -1
  328. package/types/ojiepermana-angular-theme-layout.d.ts +129 -1
  329. package/types/ojiepermana-angular-theme-page-core.d.ts +157 -1
  330. package/types/ojiepermana-angular-theme-page-filter.d.ts +77 -1
  331. package/types/ojiepermana-angular-theme-page-root.d.ts +88 -1
  332. package/types/ojiepermana-angular-theme-page-side.d.ts +50 -1
  333. package/types/ojiepermana-angular-theme-page-slots.d.ts +40 -1
  334. package/types/ojiepermana-angular-theme-page.d.ts +5 -1
  335. package/types/ojiepermana-angular-theme-shell-services.d.ts +193 -1
  336. package/types/ojiepermana-angular-theme-shell-types.d.ts +105 -1
  337. package/types/ojiepermana-angular-theme-shell.d.ts +100 -1
  338. package/types/ojiepermana-angular-theme-styles.d.ts +319 -1
  339. package/types/ojiepermana-angular-theme.d.ts +2 -1
  340. package/types/ojiepermana-angular.d.ts +5 -5
@@ -1,7 +1,1391 @@
1
- export * from '@ojiepermana/angular-theme/component/settings';
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, PLATFORM_ID, signal, effect, Service, computed, Component, model, input } from '@angular/core';
3
+ import { DrawerCloseDirective, DrawerComponent, DrawerContentComponent, DrawerHeaderComponent, DrawerTitleComponent } from '@ojiepermana/angular/component/drawer';
4
+ import { IconComponent } from '@ojiepermana/angular/component/icon';
5
+ import { SegmentedControlComponent } from '@ojiepermana/angular/component/segmented-control';
6
+ import { globalLocalStorage, injectUniqueId, cn } from '@ojiepermana/angular/component/utils/core';
7
+ import { LAYOUT_TYPE_STORAGE_KEY, LAYOUT_LOCKED_WIDTH } from '@ojiepermana/angular/theme/layout/types';
8
+ import { ShellService } from '@ojiepermana/angular/theme/shell/services';
9
+ import { isPlatformBrowser } from '@angular/common';
10
+ import { LayoutService } from '@ojiepermana/angular/theme/layout/services';
11
+ import { resolveShellDeviceForMode, shellDevicesForMode, SHELL_MODES, isShellDeviceForMode, SHELL_FRAMES, SHELL_COLORS } from '@ojiepermana/angular/theme/shell/types';
12
+ import { ThemeModeService, ThemeColorService, ThemeBrandService, ThemeRadiusService, ThemeSpaceService, THEME_COLORS, THEME_NEUTRALS, isThemeMode, isThemeRadius, isThemeSpace } from '@ojiepermana/angular/theme/styles';
2
13
 
3
- // Generated by scripts/gen-umbrella-entrypoints.mjs — do not edit by hand.
14
+ /** Provide this from an application's persisted layout-settings facade. */
15
+ const THEME_SETTINGS_ADAPTER = new InjectionToken('THEME_SETTINGS_ADAPTER');
16
+
17
+ /** Storage key for the persisted navigation type. Owned by the package. */
18
+ const NAV_TYPE_STORAGE_KEY = 'nav-type';
19
+ /** Storage key for the persisted navigation presentation mode. Owned by the package. */
20
+ const NAV_TYPE_MODE_STORAGE_KEY = 'nav-type-mode';
21
+ const NAV_TYPES = [
22
+ 'sidebar',
23
+ 'dockbar',
24
+ 'navbar',
25
+ 'flyout',
26
+ 'desktop',
27
+ ];
28
+ const NAV_TYPE_MODES = ['default', 'collapsed', 'drawer'];
29
+ /**
30
+ * Nav types valid for a layout/shell pair — mirrors `LayoutWrapperDefault`
31
+ * (`navbar`/`flyout` → web horizontal, `sidebar`/`dockbar` → vertical).
32
+ * A desktop-mode horizontal shell owns exactly one navigation shape: `desktop`.
33
+ * `empty` and `fluid` shells are brand-only, so no nav type applies.
34
+ */
35
+ function allowedNavTypes$1(layoutType, shellMode) {
36
+ switch (layoutType) {
37
+ case 'vertical':
38
+ return ['sidebar', 'dockbar'];
39
+ case 'horizontal':
40
+ return shellMode === 'desktop' ? ['desktop'] : ['navbar', 'flyout'];
41
+ default:
42
+ return [];
43
+ }
44
+ }
45
+ /**
46
+ * Presentation modes meaningful to a navigation type: sidebar collapses, dockbar
47
+ * turns into a drawer, and every other type has one presentation only.
48
+ */
49
+ function allowedNavTypeModes(navType) {
50
+ switch (navType) {
51
+ case 'sidebar':
52
+ return ['default', 'collapsed'];
53
+ case 'dockbar':
54
+ return ['default', 'drawer'];
55
+ default:
56
+ return ['default'];
57
+ }
58
+ }
59
+ /** `fluid` width only has an effect when the layout type is also `fluid`. */
60
+ function isWidthEnabled(layoutType, width) {
61
+ return width !== 'fluid' || layoutType === 'fluid';
62
+ }
63
+ /**
64
+ * Default `ThemeSettingsAdapter`: the axis coherence rules that make runtime
65
+ * layout and navigation switching safe, published so every consumer shares one
66
+ * tested implementation instead of copying it.
67
+ *
68
+ * - `surface` / `appearance` / `width` delegate to the library `LayoutService`
69
+ * (already reactive and persisted, with no nav-derived override to drift from).
70
+ * - `layoutType` / `navType` / `navTypeMode` are owned here: `<Layout>` writes
71
+ * `type` back to the service (`persist: false`) to follow nav orientation, so
72
+ * reading `layout.type()` would drift from the operator's choice. Nav type is
73
+ * not `LayoutService` territory at all.
74
+ *
75
+ * Incoherent axis pairs auto-correct, so the shell never renders an impossible
76
+ * state. Only presentation choices are stored, never identity or session data,
77
+ * and storage that is unavailable or holds a foreign value falls back to the
78
+ * default without throwing.
79
+ *
80
+ * Install with {@link provideThemeSettingsAdapter}.
81
+ */
82
+ class ThemeSettingsDefaultAdapter {
83
+ platformId = inject(PLATFORM_ID);
84
+ storage = globalLocalStorage(isPlatformBrowser(this.platformId));
85
+ layout = inject(LayoutService);
86
+ shell = inject(ShellService);
87
+ surface = this.layout.surface;
88
+ appearance = this.layout.appearance;
89
+ width = this.layout.width;
90
+ layoutTypeState = signal(this.layout.getStoredType(), /* @ts-ignore */
91
+ ...(ngDevMode ? [{ debugName: "layoutTypeState" }] : /* istanbul ignore next */ []));
92
+ navTypeState = signal(this.readStoredNavType(), /* @ts-ignore */
93
+ ...(ngDevMode ? [{ debugName: "navTypeState" }] : /* istanbul ignore next */ []));
94
+ navTypeModeState = signal(this.readStoredNavTypeMode(), /* @ts-ignore */
95
+ ...(ngDevMode ? [{ debugName: "navTypeModeState" }] : /* istanbul ignore next */ []));
96
+ layoutType = this.layoutTypeState.asReadonly();
97
+ navType = this.navTypeState.asReadonly();
98
+ navTypeMode = this.navTypeModeState.asReadonly();
99
+ /** Shell mode the nav-type constraints are resolved against. */
100
+ shellMode = this.shell.mode;
101
+ constructor() {
102
+ // Keep external shell-mode changes coherent too: desktop+horizontal owns
103
+ // exactly `desktop`, while that nav type cannot survive another shell mode.
104
+ effect(() => this.enforceConstraints(this.layoutTypeState(), this.shell.mode()));
105
+ }
106
+ setSurface(surface) {
107
+ this.layout.setSurface(surface);
108
+ }
109
+ setAppearance(appearance) {
110
+ this.layout.setAppearance(appearance);
111
+ }
112
+ setWidth(width) {
113
+ this.layout.setWidth(width);
114
+ }
115
+ setLayoutType(type) {
116
+ this.layoutTypeState.set(type);
117
+ this.storage.write(LAYOUT_TYPE_STORAGE_KEY, type);
118
+ this.enforceConstraints(type, this.shell.mode());
119
+ }
120
+ setNavType(type) {
121
+ if (!allowedNavTypes$1(this.layoutTypeState(), this.shell.mode()).includes(type)) {
122
+ return;
123
+ }
124
+ this.navTypeState.set(type);
125
+ this.storage.write(NAV_TYPE_STORAGE_KEY, type);
126
+ this.enforceNavTypeMode();
127
+ }
128
+ setNavTypeMode(mode) {
129
+ if (!allowedNavTypeModes(this.navTypeState()).includes(mode)) {
130
+ return;
131
+ }
132
+ this.navTypeModeState.set(mode);
133
+ this.storage.write(NAV_TYPE_MODE_STORAGE_KEY, mode);
134
+ }
135
+ /**
136
+ * Auto-correct the lower-priority axes a layout type invalidates, so the
137
+ * rendered shell never lands in an inconsistent state (e.g. `vertical` +
138
+ * `navbar`). Idempotent — only writes when a value is actually out of range.
139
+ */
140
+ enforceConstraints(type, shellMode) {
141
+ const navs = allowedNavTypes$1(type, shellMode);
142
+ if (navs.length && !navs.includes(this.navTypeState())) {
143
+ this.setNavType(navs[0]);
144
+ }
145
+ this.enforceNavTypeMode();
146
+ if (type === 'fluid' && this.width() !== 'fluid') {
147
+ this.setWidth('fluid');
148
+ }
149
+ else if (type !== 'fluid' && this.width() === 'fluid') {
150
+ this.setWidth('wide');
151
+ }
152
+ }
153
+ enforceNavTypeMode() {
154
+ const available = allowedNavTypeModes(this.navTypeState());
155
+ if (!available.includes(this.navTypeModeState())) {
156
+ this.setNavTypeMode(available[0]);
157
+ }
158
+ }
159
+ readStoredNavType() {
160
+ const stored = this.storage.read(NAV_TYPE_STORAGE_KEY);
161
+ return stored && NAV_TYPES.includes(stored)
162
+ ? stored
163
+ : 'dockbar';
164
+ }
165
+ readStoredNavTypeMode() {
166
+ const stored = this.storage.read(NAV_TYPE_MODE_STORAGE_KEY);
167
+ return stored && NAV_TYPE_MODES.includes(stored)
168
+ ? stored
169
+ : 'default';
170
+ }
171
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSettingsDefaultAdapter, deps: [], target: i0.ɵɵFactoryTarget.Service });
172
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.4", ngImport: i0, type: ThemeSettingsDefaultAdapter });
173
+ }
174
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSettingsDefaultAdapter, decorators: [{
175
+ type: Service
176
+ }], ctorParameters: () => [] });
177
+ /**
178
+ * Install the published {@link ThemeSettingsDefaultAdapter} on
179
+ * `THEME_SETTINGS_ADAPTER`, which turns on the layout and navigation sections of
180
+ * `<ThemeSettings>`:
181
+ *
182
+ * ```ts
183
+ * providers: [provideThemeSettingsAdapter()];
184
+ * ```
185
+ *
186
+ * Additive: consumers that provide their own adapter, or none at all, are
187
+ * unaffected.
188
+ */
189
+ function provideThemeSettingsAdapter() {
190
+ return [{ provide: THEME_SETTINGS_ADAPTER, useExisting: ThemeSettingsDefaultAdapter }];
191
+ }
192
+
193
+ /**
194
+ * The one rule the settings sections share.
195
+ *
196
+ * Two of the four sections change something that can invalidate the navigation
197
+ * type: the layout section (layout type) and the shell section (shell mode). Both
198
+ * have to run the same correction, and before the split they called one private
199
+ * method. Pure functions here, so neither section owns it and neither can drift.
200
+ */
201
+ /** Nav types the active layout and shell pair allows. Empty without an adapter. */
202
+ function allowedNavTypes(adapter, shellMode) {
203
+ const layoutType = adapter?.layoutType();
204
+ return layoutType ? allowedNavTypes$1(layoutType, shellMode) : [];
205
+ }
206
+ /**
207
+ * Correct a navigation type the new layout or shell pair no longer allows.
208
+ * Idempotent, and a no-op without an adapter.
209
+ */
210
+ function normalizeNavType(adapter, shellMode) {
211
+ const current = adapter?.navType();
212
+ const allowed = allowedNavTypes(adapter, shellMode);
213
+ if (current && allowed.length && !allowed.includes(current)) {
214
+ adapter?.setNavType(allowed[0]);
215
+ }
216
+ }
217
+
218
+ /*
219
+ * Option tables and swatch palettes for `ThemeSettings`.
220
+ *
221
+ * Pure data, extracted from the component so the drawer stays inside the
222
+ * 700-line source-unit budget (scripts/check-library-boundaries.mjs) as axes are
223
+ * added. Each option list is the full domain of one axis; the component decides
224
+ * which entries are `disabled` in the current combination.
225
+ */
226
+ const DEFAULT_BRAND_COLOR = '262 83% 58%';
227
+ const DEFAULT_BRAND_HSL = [262, 83, 58];
228
+ const MODE_OPTIONS = [
229
+ { value: 'light', label: 'Light', icon: 'light_mode' },
230
+ { value: 'dark', label: 'Dark', icon: 'dark_mode' },
231
+ { value: 'system', label: 'System', icon: 'desktop_windows' },
232
+ ];
233
+ /**
234
+ * Shell delivery targets (`@ojiepermana/angular/theme/shell`). Everything but
235
+ * `web` owns its whole surface, which is what locks the layout width to `full`.
236
+ */
237
+ const SHELL_MODE_OPTIONS = [
238
+ { value: 'web', label: 'Web', icon: 'public' },
239
+ { value: 'desktop', label: 'Desktop', icon: 'desktop_windows' },
240
+ { value: 'tablet', label: 'Tablet', icon: 'tablet' },
241
+ { value: 'mobile', label: 'Mobile', icon: 'smartphone' },
242
+ ];
243
+ /**
244
+ * Where the shell's own chrome takes its colors from. `sync` follows the theme
245
+ * axes above; `default` paints the active device's native titlebar instead.
246
+ */
247
+ const SHELL_COLOR_OPTIONS = [
248
+ { value: 'sync', label: 'Sync', icon: 'palette' },
249
+ { value: 'default', label: 'Native', icon: 'desktop_mac' },
250
+ ];
251
+ /**
252
+ * How the app sits in its window. `modern` insets it as a panel and fuses the
253
+ * titlebar with the surround; only a desktop window has a frame to shape.
254
+ */
255
+ const SHELL_FRAME_OPTIONS = [
256
+ { value: 'classic', label: 'Classic', icon: 'crop_din' },
257
+ { value: 'modern', label: 'Modern', icon: 'fullscreen_exit' },
258
+ ];
259
+ /**
260
+ * Host devices, keyed so the drawer can offer only the ones the active shell mode
261
+ * can actually run on (`SHELL_MODE_DEVICES`) — with `web` that is a single entry,
262
+ * so a flat 7-option control would be mostly disabled noise.
263
+ */
264
+ const SHELL_DEVICE_OPTIONS = {
265
+ browser: { value: 'browser', label: 'Browser', icon: 'language' },
266
+ macos: { value: 'macos', label: 'macOS', icon: 'laptop_mac' },
267
+ windows: { value: 'windows', label: 'Windows', icon: 'laptop_windows' },
268
+ linux: { value: 'linux', label: 'Linux', icon: 'terminal' },
269
+ ipad: { value: 'ipad', label: 'iPad', icon: 'tablet_mac' },
270
+ android: { value: 'android', label: 'Android', icon: 'android' },
271
+ ios: { value: 'ios', label: 'iOS', icon: 'phone_iphone' },
272
+ };
273
+ /** Corner radius axis; each preset carries Tailwind's stock value for its name. */
274
+ const RADIUS_OPTIONS = [
275
+ { value: 'none', label: 'None', icon: 'crop_square' },
276
+ { value: 'xs', label: 'Xs', icon: 'rounded_corner' },
277
+ { value: 'sm', label: 'Sm', icon: 'rounded_corner' },
278
+ { value: 'md', label: 'Md', icon: 'rounded_corner' },
279
+ { value: 'xl', label: 'Xl', icon: 'rounded_corner' },
280
+ { value: 'full', label: 'Full', icon: 'circle' },
281
+ ];
282
+ const SPACE_OPTIONS = [
283
+ { value: 'compact', label: 'Compact', icon: 'density_small' },
284
+ { value: 'normal', label: 'Normal', icon: 'density_medium' },
285
+ ];
286
+ const APPEARANCE_OPTIONS = [
287
+ { value: 'flat', label: 'Flat', icon: 'web_asset' },
288
+ { value: 'border-rail', label: 'Border rail', icon: 'border_outer' },
289
+ ];
290
+ const SURFACE_OPTIONS = [
291
+ { value: 'flat', label: 'Flat', icon: 'format_color_reset' },
292
+ { value: 'grid', label: 'Grid', icon: 'grid_4x4' },
293
+ { value: 'grid-line', label: 'Grid line', icon: 'grid_view' },
294
+ { value: 'honeycomb', label: 'Honeycomb', icon: 'hexagon' },
295
+ { value: 'matrix', label: 'Matrix', icon: 'data_object' },
296
+ { value: 'circuit', label: 'Circuit', icon: 'memory' },
297
+ { value: 'line-vertical', label: 'Vertical lines', icon: 'view_column' },
298
+ { value: 'line-horizontal', label: 'Horizontal lines', icon: 'view_agenda' },
299
+ ];
300
+ const LAYOUT_TYPE_OPTIONS = [
301
+ { value: 'vertical', label: 'Vertical', icon: 'view_sidebar' },
302
+ { value: 'horizontal', label: 'Horizontal', icon: 'view_stream' },
303
+ { value: 'empty', label: 'Empty', icon: 'crop_free' },
304
+ { value: 'fluid', label: 'Fluid', icon: 'fit_screen' },
305
+ ];
306
+ const WIDTH_OPTIONS = [
307
+ { value: 'full', label: 'Full', icon: 'width_full' },
308
+ { value: 'wide', label: 'Wide', icon: 'width_wide' },
309
+ { value: 'container', label: 'Container', icon: 'width_normal' },
310
+ { value: 'fluid', label: 'Fluid', icon: 'aspect_ratio' },
311
+ ];
312
+ const NAV_TYPE_OPTIONS = [
313
+ { value: 'sidebar', label: 'Sidebar', icon: 'left_panel_open' },
314
+ { value: 'dockbar', label: 'Dockbar', icon: 'dock_to_left' },
315
+ { value: 'navbar', label: 'Navbar', icon: 'menu' },
316
+ { value: 'flyout', label: 'Flyout', icon: 'open_in_new' },
317
+ { value: 'desktop', label: 'Desktop', icon: 'desktop_windows' },
318
+ ];
319
+ const ACCENT_SWATCH = {
320
+ base: 'hsl(240 6% 10%)',
321
+ red: 'hsl(0 72% 48%)',
322
+ orange: 'hsl(24 95% 48%)',
323
+ amber: 'hsl(38 92% 48%)',
324
+ yellow: 'hsl(48 96% 48%)',
325
+ lime: 'hsl(84 78% 44%)',
326
+ green: 'hsl(142 69% 40%)',
327
+ emerald: 'hsl(160 78% 38%)',
328
+ teal: 'hsl(173 75% 38%)',
329
+ cyan: 'hsl(191 85% 40%)',
330
+ sky: 'hsl(199 89% 46%)',
331
+ blue: 'hsl(221 83% 53%)',
332
+ indigo: 'hsl(239 78% 60%)',
333
+ violet: 'hsl(258 86% 62%)',
334
+ purple: 'hsl(271 81% 56%)',
335
+ fuchsia: 'hsl(292 80% 56%)',
336
+ pink: 'hsl(330 78% 54%)',
337
+ rose: 'hsl(349 84% 52%)',
338
+ brand: 'hsl(var(--brand))',
339
+ };
340
+ const NEUTRAL_SWATCH = {
341
+ base: 'hsl(220 9% 46%)',
342
+ slate: 'hsl(215 16% 47%)',
343
+ gray: 'hsl(220 9% 46%)',
344
+ zinc: 'hsl(240 5% 46%)',
345
+ neutral: 'hsl(0 0% 46%)',
346
+ stone: 'hsl(30 6% 46%)',
347
+ mauve: 'hsl(280 6% 47%)',
348
+ olive: 'hsl(80 8% 42%)',
349
+ mist: 'hsl(200 14% 50%)',
350
+ taupe: 'hsl(28 10% 46%)',
351
+ };
352
+
353
+ /**
354
+ * The Layout section of `ThemeSettings`: appearance, surface, layout type, width.
355
+ * Internal, see the Theme section for why.
356
+ *
357
+ * The adapter is injected NON-optionally: the parent only renders this section
358
+ * when an adapter is present, which is what lets a theme-only consumer use
359
+ * `ThemeSettings` with just the Theme and Shell sections.
360
+ */
361
+ class ThemeSettingsLayoutSectionComponent {
362
+ shell = inject(ShellService);
363
+ settings = inject(THEME_SETTINGS_ADAPTER);
364
+ shellMode = this.shell.mode;
365
+ /** Every mode but `web` owns its surface, which is what locks the width axis. */
366
+ widthLocked = computed(() => this.shellMode() !== 'web', /* @ts-ignore */
367
+ ...(ngDevMode ? [{ debugName: "widthLocked" }] : /* istanbul ignore next */ []));
368
+ appearanceOptions = APPEARANCE_OPTIONS;
369
+ surfaceOptions = SURFACE_OPTIONS;
370
+ layoutTypeOptions = LAYOUT_TYPE_OPTIONS;
371
+ widthOptions = computed(() => WIDTH_OPTIONS.map((option) => ({
372
+ ...option,
373
+ disabled:
374
+ // The shell mode lock leaves exactly one legal width; `fluid` additionally
375
+ // needs the fluid layout type to mean anything.
376
+ (this.widthLocked() && option.value !== LAYOUT_LOCKED_WIDTH) ||
377
+ (option.value === 'fluid' && this.settings.layoutType() !== 'fluid'),
378
+ })), /* @ts-ignore */
379
+ ...(ngDevMode ? [{ debugName: "widthOptions" }] : /* istanbul ignore next */ []));
380
+ layoutGroupId = injectUniqueId('theme-settings-layout');
381
+ appearanceLabelId = injectUniqueId('theme-settings-appearance');
382
+ surfaceLabelId = injectUniqueId('theme-settings-surface');
383
+ layoutTypeLabelId = injectUniqueId('theme-settings-layout-type');
384
+ widthLabelId = injectUniqueId('theme-settings-width');
385
+ setAppearance(value) {
386
+ if (value === 'flat' || value === 'border-rail')
387
+ this.settings.setAppearance(value);
388
+ }
389
+ setSurface(value) {
390
+ if (SURFACE_OPTIONS.some((option) => option.value === value))
391
+ this.settings.setSurface(value);
392
+ }
393
+ setLayoutType(value) {
394
+ if (LAYOUT_TYPE_OPTIONS.some((option) => option.value === value)) {
395
+ this.settings.setLayoutType(value);
396
+ normalizeNavType(this.settings, this.shellMode());
397
+ }
398
+ }
399
+ setWidth(value) {
400
+ // Refuse the change rather than record a preference the lock would hide.
401
+ if (this.widthLocked())
402
+ return;
403
+ if (WIDTH_OPTIONS.some((option) => option.value === value))
404
+ this.settings.setWidth(value);
405
+ }
406
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSettingsLayoutSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
407
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: ThemeSettingsLayoutSectionComponent, isStandalone: true, selector: "ThemeSettingsLayoutSection", host: { classAttribute: "contents" }, ngImport: i0, template: `
408
+ <section
409
+ class="flex flex-col gap-5 border-t border-border pt-7"
410
+ [attr.aria-labelledby]="layoutGroupId"
411
+ >
412
+ <h2 [id]="layoutGroupId" class="nav-heading text-foreground">Layout</h2>
413
+ <div class="flex flex-col gap-2">
414
+ <h3 [id]="appearanceLabelId" class="nav-heading text-muted-foreground">
415
+ Layout appearance
416
+ </h3>
417
+ <SegmentedControl
418
+ appearance="icon-text"
419
+ space="compact"
420
+ [value]="settings.appearance()"
421
+ [options]="appearanceOptions"
422
+ [aria-labelledby]="appearanceLabelId"
423
+ (valueChange)="setAppearance($event)"
424
+ />
425
+ </div>
426
+ <div class="flex flex-col gap-2">
427
+ <h3 [id]="surfaceLabelId" class="nav-heading text-muted-foreground">Layout surface</h3>
428
+ <SegmentedControl
429
+ appearance="icon-text"
430
+ space="compact"
431
+ [value]="settings.surface()"
432
+ [options]="surfaceOptions"
433
+ [aria-labelledby]="surfaceLabelId"
434
+ (valueChange)="setSurface($event)"
435
+ />
436
+ </div>
437
+ <div class="flex flex-col gap-2">
438
+ <h3 [id]="layoutTypeLabelId" class="nav-heading text-muted-foreground">Layout type</h3>
439
+ <SegmentedControl
440
+ appearance="icon-text"
441
+ space="compact"
442
+ [value]="settings.layoutType()"
443
+ [options]="layoutTypeOptions"
444
+ [aria-labelledby]="layoutTypeLabelId"
445
+ (valueChange)="setLayoutType($event)"
446
+ />
447
+ </div>
448
+ <div class="flex flex-col gap-2">
449
+ <h3 [id]="widthLabelId" class="nav-heading text-muted-foreground">Layout width</h3>
450
+ <SegmentedControl
451
+ appearance="icon-text"
452
+ space="compact"
453
+ [value]="settings.width()"
454
+ [options]="widthOptions()"
455
+ [aria-labelledby]="widthLabelId"
456
+ (valueChange)="setWidth($event)"
457
+ />
458
+ @if (widthLocked()) {
459
+ <p class="text-xs text-muted-foreground" aria-live="polite">
460
+ Locked to full on {{ shellMode() }}: the app is the whole surface, so there is no page
461
+ to inset the frame within.
462
+ </p>
463
+ }
464
+ </div>
465
+ </section>
466
+ `, isInline: true, dependencies: [{ kind: "component", type: SegmentedControlComponent, selector: "SegmentedControl", inputs: ["options", "value", "appearance", "space", "disabled", "aria-label", "aria-labelledby", "aria-describedby", "class"], outputs: ["valueChange"] }] });
467
+ }
468
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSettingsLayoutSectionComponent, decorators: [{
469
+ type: Component,
470
+ args: [{
471
+ selector: 'ThemeSettingsLayoutSection',
472
+ imports: [SegmentedControlComponent],
473
+ host: { class: 'contents' },
474
+ template: `
475
+ <section
476
+ class="flex flex-col gap-5 border-t border-border pt-7"
477
+ [attr.aria-labelledby]="layoutGroupId"
478
+ >
479
+ <h2 [id]="layoutGroupId" class="nav-heading text-foreground">Layout</h2>
480
+ <div class="flex flex-col gap-2">
481
+ <h3 [id]="appearanceLabelId" class="nav-heading text-muted-foreground">
482
+ Layout appearance
483
+ </h3>
484
+ <SegmentedControl
485
+ appearance="icon-text"
486
+ space="compact"
487
+ [value]="settings.appearance()"
488
+ [options]="appearanceOptions"
489
+ [aria-labelledby]="appearanceLabelId"
490
+ (valueChange)="setAppearance($event)"
491
+ />
492
+ </div>
493
+ <div class="flex flex-col gap-2">
494
+ <h3 [id]="surfaceLabelId" class="nav-heading text-muted-foreground">Layout surface</h3>
495
+ <SegmentedControl
496
+ appearance="icon-text"
497
+ space="compact"
498
+ [value]="settings.surface()"
499
+ [options]="surfaceOptions"
500
+ [aria-labelledby]="surfaceLabelId"
501
+ (valueChange)="setSurface($event)"
502
+ />
503
+ </div>
504
+ <div class="flex flex-col gap-2">
505
+ <h3 [id]="layoutTypeLabelId" class="nav-heading text-muted-foreground">Layout type</h3>
506
+ <SegmentedControl
507
+ appearance="icon-text"
508
+ space="compact"
509
+ [value]="settings.layoutType()"
510
+ [options]="layoutTypeOptions"
511
+ [aria-labelledby]="layoutTypeLabelId"
512
+ (valueChange)="setLayoutType($event)"
513
+ />
514
+ </div>
515
+ <div class="flex flex-col gap-2">
516
+ <h3 [id]="widthLabelId" class="nav-heading text-muted-foreground">Layout width</h3>
517
+ <SegmentedControl
518
+ appearance="icon-text"
519
+ space="compact"
520
+ [value]="settings.width()"
521
+ [options]="widthOptions()"
522
+ [aria-labelledby]="widthLabelId"
523
+ (valueChange)="setWidth($event)"
524
+ />
525
+ @if (widthLocked()) {
526
+ <p class="text-xs text-muted-foreground" aria-live="polite">
527
+ Locked to full on {{ shellMode() }}: the app is the whole surface, so there is no page
528
+ to inset the frame within.
529
+ </p>
530
+ }
531
+ </div>
532
+ </section>
533
+ `,
534
+ }]
535
+ }] });
536
+
537
+ /**
538
+ * The Navigation section of `ThemeSettings`: navigation type and its mode.
539
+ * Internal, see the Theme section for why.
540
+ */
541
+ class ThemeSettingsNavigationSectionComponent {
542
+ shell = inject(ShellService);
543
+ settings = inject(THEME_SETTINGS_ADAPTER);
544
+ navTypeOptions = computed(() => NAV_TYPE_OPTIONS.map((option) => ({
545
+ ...option,
546
+ disabled: !allowedNavTypes(this.settings, this.shell.mode()).includes(option.value),
547
+ })), /* @ts-ignore */
548
+ ...(ngDevMode ? [{ debugName: "navTypeOptions" }] : /* istanbul ignore next */ []));
549
+ // Mirrors `allowedNavTypeModes` in the default adapter, which decides which of
550
+ // these the adapter will actually accept; here they also carry their labels,
551
+ // and dockbar names its default "Sticky".
552
+ navTypeModeOptions = computed(() => {
553
+ switch (this.settings.navType()) {
554
+ case 'sidebar':
555
+ return [
556
+ { value: 'default', label: 'Default', icon: 'view_sidebar' },
557
+ { value: 'collapsed', label: 'Collapsed', icon: 'first_page' },
558
+ ];
559
+ case 'dockbar':
560
+ return [
561
+ { value: 'default', label: 'Sticky', icon: 'dock_to_left' },
562
+ { value: 'drawer', label: 'Drawer', icon: 'side_navigation' },
563
+ ];
564
+ default:
565
+ return [{ value: 'default', label: 'Default', icon: 'tune' }];
566
+ }
567
+ }, /* @ts-ignore */
568
+ ...(ngDevMode ? [{ debugName: "navTypeModeOptions" }] : /* istanbul ignore next */ []));
569
+ navigationGroupId = injectUniqueId('theme-settings-navigation');
570
+ navTypeLabelId = injectUniqueId('theme-settings-nav-type');
571
+ navTypeModeLabelId = injectUniqueId('theme-settings-nav-type-mode');
572
+ setNavType(value) {
573
+ if (value &&
574
+ allowedNavTypes(this.settings, this.shell.mode()).includes(value)) {
575
+ this.settings.setNavType(value);
576
+ }
577
+ }
578
+ setNavTypeMode(value) {
579
+ if (value === 'default' || value === 'collapsed' || value === 'drawer')
580
+ this.settings.setNavTypeMode(value);
581
+ }
582
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSettingsNavigationSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
583
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.4", type: ThemeSettingsNavigationSectionComponent, isStandalone: true, selector: "ThemeSettingsNavigationSection", host: { classAttribute: "contents" }, ngImport: i0, template: `
584
+ <section
585
+ class="flex flex-col gap-5 border-t border-border pt-7"
586
+ [attr.aria-labelledby]="navigationGroupId"
587
+ >
588
+ <h2 [id]="navigationGroupId" class="nav-heading text-foreground">Navigation</h2>
589
+ <div class="flex flex-col gap-2">
590
+ <h3 [id]="navTypeLabelId" class="nav-heading text-muted-foreground">Nav type</h3>
591
+ <SegmentedControl
592
+ appearance="icon-text"
593
+ space="compact"
594
+ [value]="settings.navType()"
595
+ [options]="navTypeOptions()"
596
+ [aria-labelledby]="navTypeLabelId"
597
+ (valueChange)="setNavType($event)"
598
+ />
599
+ </div>
600
+ <div class="flex flex-col gap-2">
601
+ <h3 [id]="navTypeModeLabelId" class="nav-heading text-muted-foreground">Nav type mode</h3>
602
+ <SegmentedControl
603
+ appearance="icon-text"
604
+ space="compact"
605
+ [value]="settings.navTypeMode()"
606
+ [options]="navTypeModeOptions()"
607
+ [aria-labelledby]="navTypeModeLabelId"
608
+ (valueChange)="setNavTypeMode($event)"
609
+ />
610
+ </div>
611
+ </section>
612
+ `, isInline: true, dependencies: [{ kind: "component", type: SegmentedControlComponent, selector: "SegmentedControl", inputs: ["options", "value", "appearance", "space", "disabled", "aria-label", "aria-labelledby", "aria-describedby", "class"], outputs: ["valueChange"] }] });
613
+ }
614
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSettingsNavigationSectionComponent, decorators: [{
615
+ type: Component,
616
+ args: [{
617
+ selector: 'ThemeSettingsNavigationSection',
618
+ imports: [SegmentedControlComponent],
619
+ host: { class: 'contents' },
620
+ template: `
621
+ <section
622
+ class="flex flex-col gap-5 border-t border-border pt-7"
623
+ [attr.aria-labelledby]="navigationGroupId"
624
+ >
625
+ <h2 [id]="navigationGroupId" class="nav-heading text-foreground">Navigation</h2>
626
+ <div class="flex flex-col gap-2">
627
+ <h3 [id]="navTypeLabelId" class="nav-heading text-muted-foreground">Nav type</h3>
628
+ <SegmentedControl
629
+ appearance="icon-text"
630
+ space="compact"
631
+ [value]="settings.navType()"
632
+ [options]="navTypeOptions()"
633
+ [aria-labelledby]="navTypeLabelId"
634
+ (valueChange)="setNavType($event)"
635
+ />
636
+ </div>
637
+ <div class="flex flex-col gap-2">
638
+ <h3 [id]="navTypeModeLabelId" class="nav-heading text-muted-foreground">Nav type mode</h3>
639
+ <SegmentedControl
640
+ appearance="icon-text"
641
+ space="compact"
642
+ [value]="settings.navTypeMode()"
643
+ [options]="navTypeModeOptions()"
644
+ [aria-labelledby]="navTypeModeLabelId"
645
+ (valueChange)="setNavTypeMode($event)"
646
+ />
647
+ </div>
648
+ </section>
649
+ `,
650
+ }]
651
+ }] });
652
+
653
+ /**
654
+ * The Shell section of `ThemeSettings`: delivery mode, device, frame, chrome
655
+ * colour. Internal, see the Theme section for why.
656
+ *
657
+ * It injects the adapter optionally because a theme-only consumer has none, and
658
+ * it only needs it to run the shared navigation-type correction after a mode
659
+ * change.
660
+ */
661
+ class ThemeSettingsShellSectionComponent {
662
+ shell = inject(ShellService);
663
+ settings = inject(THEME_SETTINGS_ADAPTER, { optional: true });
664
+ shellMode = this.shell.mode;
665
+ shellColor = this.shell.color;
666
+ shellFrame = this.shell.frame;
667
+ /** Only a desktop window has a frame to shape, so `modern` is offered only there. */
668
+ shellFrameOptions = computed(() => SHELL_FRAME_OPTIONS.map((option) => ({
669
+ ...option,
670
+ disabled: option.value === 'modern' && this.shellMode() !== 'desktop',
671
+ })), /* @ts-ignore */
672
+ ...(ngDevMode ? [{ debugName: "shellFrameOptions" }] : /* istanbul ignore next */ []));
673
+ /**
674
+ * The device actually in effect: `ShellService.device()` is a preference that may
675
+ * belong to another form factor (it deliberately survives a temporary mode change),
676
+ * so the control shows the value `<Shell>` would render for the active mode.
677
+ */
678
+ shellDevice = computed(() => resolveShellDeviceForMode(this.shellMode(), this.shell.device()), /* @ts-ignore */
679
+ ...(ngDevMode ? [{ debugName: "shellDevice" }] : /* istanbul ignore next */ []));
680
+ shellDeviceOptions = computed(() => shellDevicesForMode(this.shellMode()).map((device) => SHELL_DEVICE_OPTIONS[device]), /* @ts-ignore */
681
+ ...(ngDevMode ? [{ debugName: "shellDeviceOptions" }] : /* istanbul ignore next */ []));
682
+ shellModeOptions = SHELL_MODE_OPTIONS;
683
+ shellColorOptions = SHELL_COLOR_OPTIONS;
684
+ shellGroupId = injectUniqueId('theme-settings-shell');
685
+ shellModeLabelId = injectUniqueId('theme-settings-shell-mode');
686
+ shellDeviceLabelId = injectUniqueId('theme-settings-shell-device');
687
+ shellColorLabelId = injectUniqueId('theme-settings-shell-color');
688
+ shellFrameLabelId = injectUniqueId('theme-settings-shell-frame');
689
+ setShellMode(value) {
690
+ if (SHELL_MODES.includes(value)) {
691
+ this.shell.setMode(value);
692
+ normalizeNavType(this.settings, this.shellMode());
693
+ }
694
+ }
695
+ setShellDevice(value) {
696
+ // Guard on the active mode, not just the union: a device from another form
697
+ // factor would be corrected away on render anyway.
698
+ if (value && isShellDeviceForMode(this.shellMode(), value)) {
699
+ this.shell.setDevice(value);
700
+ }
701
+ }
702
+ setShellFrame(value) {
703
+ // Refuse `modern` off desktop rather than store a preference the shell would
704
+ // coerce away on render.
705
+ if (value === 'modern' && this.shellMode() !== 'desktop')
706
+ return;
707
+ if (SHELL_FRAMES.includes(value))
708
+ this.shell.setFrame(value);
709
+ }
710
+ setShellColor(value) {
711
+ if (SHELL_COLORS.includes(value))
712
+ this.shell.setColor(value);
713
+ }
714
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSettingsShellSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
715
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: ThemeSettingsShellSectionComponent, isStandalone: true, selector: "ThemeSettingsShellSection", host: { classAttribute: "contents" }, ngImport: i0, template: `
716
+ <section
717
+ class="flex flex-col gap-5 border-t border-border pt-7"
718
+ [attr.aria-labelledby]="shellGroupId"
719
+ >
720
+ <h2 [id]="shellGroupId" class="nav-heading text-foreground">Shell</h2>
721
+ <div class="flex flex-col gap-2">
722
+ <h3 [id]="shellModeLabelId" class="nav-heading text-muted-foreground">Shell mode</h3>
723
+ <SegmentedControl
724
+ appearance="icon-text"
725
+ space="compact"
726
+ [value]="shellMode()"
727
+ [options]="shellModeOptions"
728
+ [aria-labelledby]="shellModeLabelId"
729
+ (valueChange)="setShellMode($event)"
730
+ />
731
+ <p class="text-xs text-muted-foreground">
732
+ Where the app is delivered. Only <code>web</code> is a page inside a browser; the others
733
+ own the whole surface, so the layout width locks to full.
734
+ </p>
735
+ </div>
736
+ <div class="flex flex-col gap-2">
737
+ <h3 [id]="shellDeviceLabelId" class="nav-heading text-muted-foreground">Shell device</h3>
738
+ <SegmentedControl
739
+ appearance="icon-text"
740
+ space="compact"
741
+ [value]="shellDevice()"
742
+ [options]="shellDeviceOptions()"
743
+ [aria-labelledby]="shellDeviceLabelId"
744
+ (valueChange)="setShellDevice($event)"
745
+ />
746
+ <p class="text-xs text-muted-foreground">
747
+ Which host the shell runs on. Only the devices the active mode can host are offered —
748
+ <code>android</code> ships on both tablet and mobile, so only the mode tells those apart.
749
+ </p>
750
+ </div>
751
+ <div class="flex flex-col gap-2">
752
+ <h3 [id]="shellFrameLabelId" class="nav-heading text-muted-foreground">Shell frame</h3>
753
+ <SegmentedControl
754
+ appearance="icon-text"
755
+ space="compact"
756
+ [value]="shellFrame()"
757
+ [options]="shellFrameOptions()"
758
+ [aria-labelledby]="shellFrameLabelId"
759
+ (valueChange)="setShellFrame($event)"
760
+ />
761
+ <p class="text-xs text-muted-foreground">
762
+ @if (shellMode() === 'desktop') {
763
+ <code>modern</code> insets the app as a panel and fuses the titlebar with the surround
764
+ into one frame; <code>classic</code> keeps it flush under a bordered bar.
765
+ } @else {
766
+ Only a desktop window has a frame to shape, so this stays
767
+ <code>classic</code> on {{ shellMode() }}.
768
+ }
769
+ </p>
770
+ </div>
771
+ <div class="flex flex-col gap-2">
772
+ <h3 [id]="shellColorLabelId" class="nav-heading text-muted-foreground">Shell color</h3>
773
+ <SegmentedControl
774
+ appearance="icon-text"
775
+ space="compact"
776
+ [value]="shellColor()"
777
+ [options]="shellColorOptions"
778
+ [aria-labelledby]="shellColorLabelId"
779
+ (valueChange)="setShellColor($event)"
780
+ />
781
+ <p class="text-xs text-muted-foreground">
782
+ Whether the desktop titlebar follows the theme axes above, or paints the active device's
783
+ own native chrome.
784
+ </p>
785
+ </div>
786
+ </section>
787
+ `, isInline: true, dependencies: [{ kind: "component", type: SegmentedControlComponent, selector: "SegmentedControl", inputs: ["options", "value", "appearance", "space", "disabled", "aria-label", "aria-labelledby", "aria-describedby", "class"], outputs: ["valueChange"] }] });
788
+ }
789
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSettingsShellSectionComponent, decorators: [{
790
+ type: Component,
791
+ args: [{
792
+ selector: 'ThemeSettingsShellSection',
793
+ imports: [SegmentedControlComponent],
794
+ host: { class: 'contents' },
795
+ template: `
796
+ <section
797
+ class="flex flex-col gap-5 border-t border-border pt-7"
798
+ [attr.aria-labelledby]="shellGroupId"
799
+ >
800
+ <h2 [id]="shellGroupId" class="nav-heading text-foreground">Shell</h2>
801
+ <div class="flex flex-col gap-2">
802
+ <h3 [id]="shellModeLabelId" class="nav-heading text-muted-foreground">Shell mode</h3>
803
+ <SegmentedControl
804
+ appearance="icon-text"
805
+ space="compact"
806
+ [value]="shellMode()"
807
+ [options]="shellModeOptions"
808
+ [aria-labelledby]="shellModeLabelId"
809
+ (valueChange)="setShellMode($event)"
810
+ />
811
+ <p class="text-xs text-muted-foreground">
812
+ Where the app is delivered. Only <code>web</code> is a page inside a browser; the others
813
+ own the whole surface, so the layout width locks to full.
814
+ </p>
815
+ </div>
816
+ <div class="flex flex-col gap-2">
817
+ <h3 [id]="shellDeviceLabelId" class="nav-heading text-muted-foreground">Shell device</h3>
818
+ <SegmentedControl
819
+ appearance="icon-text"
820
+ space="compact"
821
+ [value]="shellDevice()"
822
+ [options]="shellDeviceOptions()"
823
+ [aria-labelledby]="shellDeviceLabelId"
824
+ (valueChange)="setShellDevice($event)"
825
+ />
826
+ <p class="text-xs text-muted-foreground">
827
+ Which host the shell runs on. Only the devices the active mode can host are offered —
828
+ <code>android</code> ships on both tablet and mobile, so only the mode tells those apart.
829
+ </p>
830
+ </div>
831
+ <div class="flex flex-col gap-2">
832
+ <h3 [id]="shellFrameLabelId" class="nav-heading text-muted-foreground">Shell frame</h3>
833
+ <SegmentedControl
834
+ appearance="icon-text"
835
+ space="compact"
836
+ [value]="shellFrame()"
837
+ [options]="shellFrameOptions()"
838
+ [aria-labelledby]="shellFrameLabelId"
839
+ (valueChange)="setShellFrame($event)"
840
+ />
841
+ <p class="text-xs text-muted-foreground">
842
+ @if (shellMode() === 'desktop') {
843
+ <code>modern</code> insets the app as a panel and fuses the titlebar with the surround
844
+ into one frame; <code>classic</code> keeps it flush under a bordered bar.
845
+ } @else {
846
+ Only a desktop window has a frame to shape, so this stays
847
+ <code>classic</code> on {{ shellMode() }}.
848
+ }
849
+ </p>
850
+ </div>
851
+ <div class="flex flex-col gap-2">
852
+ <h3 [id]="shellColorLabelId" class="nav-heading text-muted-foreground">Shell color</h3>
853
+ <SegmentedControl
854
+ appearance="icon-text"
855
+ space="compact"
856
+ [value]="shellColor()"
857
+ [options]="shellColorOptions"
858
+ [aria-labelledby]="shellColorLabelId"
859
+ (valueChange)="setShellColor($event)"
860
+ />
861
+ <p class="text-xs text-muted-foreground">
862
+ Whether the desktop titlebar follows the theme axes above, or paints the active device's
863
+ own native chrome.
864
+ </p>
865
+ </div>
866
+ </section>
867
+ `,
868
+ }]
869
+ }] });
870
+
871
+ /*
872
+ * Brand-color math for `ThemeSettings`.
873
+ *
874
+ * The drawer's brand picker is a native `<input type="color">`, which speaks hex,
875
+ * while the design system's tokens are HSL triplets (`H S% L%`). These pure
876
+ * conversions bridge the two, plus pick a readable foreground for a chosen brand.
877
+ *
878
+ * Extracted from the component so the drawer stays inside the 700-line source-unit
879
+ * budget (scripts/check-library-boundaries.mjs) as shell axes are added.
880
+ */
881
+ function hslToHex(color) {
882
+ const match = /^(\d+(?:\.\d+)?)\s+(\d+(?:\.\d+)?)%\s+(\d+(?:\.\d+)?)%$/.exec(color);
883
+ const [hue, saturation, lightness] = match
884
+ ? [Number(match[1]), Number(match[2]), Number(match[3])]
885
+ : DEFAULT_BRAND_HSL;
886
+ const chroma = (1 - Math.abs(2 * (lightness / 100) - 1)) * (saturation / 100);
887
+ const segment = hue / 60;
888
+ const x = chroma * (1 - Math.abs((segment % 2) - 1));
889
+ const matchLightness = lightness / 100 - chroma / 2;
890
+ const [red, green, blue] = segment < 1
891
+ ? [chroma, x, 0]
892
+ : segment < 2
893
+ ? [x, chroma, 0]
894
+ : segment < 3
895
+ ? [0, chroma, x]
896
+ : segment < 4
897
+ ? [0, x, chroma]
898
+ : segment < 5
899
+ ? [x, 0, chroma]
900
+ : [chroma, 0, x];
901
+ const channel = (value) => Math.round((value + matchLightness) * 255)
902
+ .toString(16)
903
+ .padStart(2, '0');
904
+ return `#${channel(red)}${channel(green)}${channel(blue)}`;
905
+ }
906
+ function hexToHsl(color) {
907
+ const match = /^#([0-9a-f]{6})$/i.exec(color);
908
+ if (!match)
909
+ return null;
910
+ const hex = match[1];
911
+ const [red, green, blue] = [0, 2, 4].map((index) => Number.parseInt(hex.slice(index, index + 2), 16) / 255);
912
+ const max = Math.max(red, green, blue);
913
+ const min = Math.min(red, green, blue);
914
+ const delta = max - min;
915
+ const lightness = (max + min) / 2;
916
+ let hue = 0;
917
+ if (delta !== 0)
918
+ hue =
919
+ max === red
920
+ ? 60 * (((green - blue) / delta) % 6)
921
+ : max === green
922
+ ? 60 * ((blue - red) / delta + 2)
923
+ : 60 * ((red - green) / delta + 4);
924
+ if (hue < 0)
925
+ hue += 360;
926
+ const saturation = delta === 0 ? 0 : delta / (1 - Math.abs(2 * lightness - 1));
927
+ return `${Math.round(hue)} ${Math.round(saturation * 100)}% ${Math.round(lightness * 100)}%`;
928
+ }
929
+ function brandForeground(color) {
930
+ const match = /^#([0-9a-f]{6})$/i.exec(color);
931
+ if (!match)
932
+ return '0 0% 100%';
933
+ const channels = [0, 2, 4].map((index) => Number.parseInt(match[1].slice(index, index + 2), 16));
934
+ const linear = (channel) => {
935
+ const value = channel / 255;
936
+ return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
937
+ };
938
+ const luminance = linear(channels[0]) * 0.2126 + linear(channels[1]) * 0.7152 + linear(channels[2]) * 0.0722;
939
+ return luminance > 0.179 ? '0 0% 0%' : '0 0% 100%';
940
+ }
941
+
942
+ /**
943
+ * The Theme section of `ThemeSettings`: mode, brand, accent, neutral, space,
944
+ * radius.
945
+ *
946
+ * Internal. Not exported from the entry point: it exists so `ThemeSettings`
947
+ * stays under the 700-line source guard and so each axis group can be read on
948
+ * its own. Every dependency arrives by injection, so the parent composes it with
949
+ * no inputs and no outputs.
950
+ *
951
+ * `class: 'contents'` keeps the host out of the layout: the parent's flex column
952
+ * spaces the `<section>` inside, and a custom element defaults to `display:
953
+ * inline`, which would break that spacing.
954
+ */
955
+ class ThemeSettingsThemeSectionComponent {
956
+ themeMode = inject(ThemeModeService);
957
+ themeColor = inject(ThemeColorService);
958
+ themeBrand = inject(ThemeBrandService);
959
+ themeRadius = inject(ThemeRadiusService);
960
+ themeSpace = inject(ThemeSpaceService);
961
+ mode = this.themeMode.mode;
962
+ resolvedMode = this.themeMode.resolvedMode;
963
+ color = this.themeColor.color;
964
+ neutralColor = this.themeColor.neutral;
965
+ brand = this.themeBrand.brand;
966
+ brandHex = computed(() => hslToHex(this.brand()?.color ?? DEFAULT_BRAND_COLOR), /* @ts-ignore */
967
+ ...(ngDevMode ? [{ debugName: "brandHex" }] : /* istanbul ignore next */ []));
968
+ radius = this.themeRadius.radius;
969
+ space = this.themeSpace.space;
970
+ accents = THEME_COLORS;
971
+ neutrals = THEME_NEUTRALS;
972
+ accentSwatch = ACCENT_SWATCH;
973
+ neutralSwatch = NEUTRAL_SWATCH;
974
+ modeOptions = MODE_OPTIONS;
975
+ radiusOptions = RADIUS_OPTIONS;
976
+ spaceOptions = SPACE_OPTIONS;
977
+ themeGroupId = injectUniqueId('theme-settings-theme');
978
+ modeLabelId = injectUniqueId('theme-settings-mode');
979
+ brandLabelId = injectUniqueId('theme-settings-brand');
980
+ accentLabelId = injectUniqueId('theme-settings-accent');
981
+ neutralLabelId = injectUniqueId('theme-settings-neutral');
982
+ spaceLabelId = injectUniqueId('theme-settings-space');
983
+ radiusLabelId = injectUniqueId('theme-settings-radius');
984
+ swatchClasses(active) {
985
+ return cn('inline-flex size-[18px] rounded-full ring-1 ring-black/10 transition-[transform,box-shadow] duration-200 peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-background motion-reduce:transition-none', active
986
+ ? 'ring-2 ring-ring ring-offset-2 ring-offset-background shadow-sm'
987
+ : 'hover:scale-105');
988
+ }
989
+ setMode(value) {
990
+ if (isThemeMode(value))
991
+ this.themeMode.setMode(value);
992
+ }
993
+ setColor(value) {
994
+ this.themeColor.setColor(value);
995
+ }
996
+ setNeutral(value) {
997
+ this.themeColor.setNeutral(value);
998
+ }
999
+ setRadius(value) {
1000
+ if (isThemeRadius(value))
1001
+ this.themeRadius.setRadius(value);
1002
+ }
1003
+ setSpace(value) {
1004
+ if (isThemeSpace(value))
1005
+ this.themeSpace.setSpace(value);
1006
+ }
1007
+ setBrandFromPicker(event) {
1008
+ if (!(event.target instanceof HTMLInputElement))
1009
+ return;
1010
+ const color = event.target.value;
1011
+ const hsl = hexToHsl(color);
1012
+ if (!hsl)
1013
+ return;
1014
+ this.themeBrand.setBrand(hsl, brandForeground(color));
1015
+ this.themeColor.setColor('brand');
1016
+ }
1017
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSettingsThemeSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1018
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: ThemeSettingsThemeSectionComponent, isStandalone: true, selector: "ThemeSettingsThemeSection", host: { classAttribute: "contents" }, ngImport: i0, template: `
1019
+ <section class="flex flex-col gap-5" [attr.aria-labelledby]="themeGroupId">
1020
+ <h2 [id]="themeGroupId" class="nav-heading text-foreground">Theme</h2>
1021
+ <div class="flex flex-col gap-2">
1022
+ <div class="flex items-baseline justify-between gap-3">
1023
+ <h3 [id]="modeLabelId" class="nav-heading text-muted-foreground">Theme mode</h3>
1024
+ <span class="text-xs text-muted-foreground">{{ resolvedMode() }}</span>
1025
+ </div>
1026
+ <SegmentedControl
1027
+ appearance="icon-text"
1028
+ space="compact"
1029
+ [value]="mode()"
1030
+ [options]="modeOptions"
1031
+ [aria-labelledby]="modeLabelId"
1032
+ (valueChange)="setMode($event)"
1033
+ />
1034
+ </div>
1035
+ <div class="flex flex-col gap-2">
1036
+ <h3 [id]="brandLabelId" class="nav-heading text-muted-foreground">Theme brand</h3>
1037
+ <input
1038
+ type="color"
1039
+ class="size-10 cursor-pointer appearance-none overflow-hidden rounded-full border border-border bg-transparent p-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background [&::-moz-color-swatch]:rounded-full [&::-webkit-color-swatch-wrapper]:p-0 [&::-webkit-color-swatch]:rounded-full"
1040
+ [value]="brandHex()"
1041
+ [attr.aria-labelledby]="brandLabelId"
1042
+ (input)="setBrandFromPicker($event)"
1043
+ />
1044
+ </div>
1045
+ <div class="flex flex-col gap-2">
1046
+ <h3 [id]="accentLabelId" class="nav-heading text-muted-foreground">Theme color</h3>
1047
+ <fieldset class="grid grid-cols-5 gap-2 sm:grid-cols-6">
1048
+ <legend class="sr-only">Choose theme color</legend>
1049
+ @for (accent of accents; track accent) {
1050
+ <label class="inline-flex size-10 cursor-pointer items-center justify-center"
1051
+ ><input
1052
+ class="peer sr-only"
1053
+ type="radio"
1054
+ name="theme-settings-accent"
1055
+ [value]="accent"
1056
+ [checked]="color() === accent"
1057
+ (change)="setColor(accent)"
1058
+ /><span
1059
+ [class]="swatchClasses(color() === accent)"
1060
+ [style.background]="accentSwatch[accent]"
1061
+ ><span class="sr-only">{{ accent }}</span></span
1062
+ ></label
1063
+ >
1064
+ }
1065
+ </fieldset>
1066
+ </div>
1067
+ <div class="flex flex-col gap-2">
1068
+ <h3 [id]="neutralLabelId" class="nav-heading text-muted-foreground">Theme neutral</h3>
1069
+ <fieldset class="grid grid-cols-5 gap-2 sm:grid-cols-6">
1070
+ <legend class="sr-only">Choose theme neutral</legend>
1071
+ @for (neutral of neutrals; track neutral) {
1072
+ <label class="inline-flex size-10 cursor-pointer items-center justify-center"
1073
+ ><input
1074
+ class="peer sr-only"
1075
+ type="radio"
1076
+ name="theme-settings-neutral"
1077
+ [value]="neutral"
1078
+ [checked]="neutralColor() === neutral"
1079
+ (change)="setNeutral(neutral)"
1080
+ /><span
1081
+ [class]="swatchClasses(neutralColor() === neutral)"
1082
+ [style.background]="neutralSwatch[neutral]"
1083
+ ><span class="sr-only">{{ neutral }}</span></span
1084
+ ></label
1085
+ >
1086
+ }
1087
+ </fieldset>
1088
+ </div>
1089
+ <div class="flex flex-col gap-2">
1090
+ <h3 [id]="spaceLabelId" class="nav-heading text-muted-foreground">Theme space</h3>
1091
+ <SegmentedControl
1092
+ appearance="icon-text"
1093
+ space="compact"
1094
+ [value]="space()"
1095
+ [options]="spaceOptions"
1096
+ [aria-labelledby]="spaceLabelId"
1097
+ (valueChange)="setSpace($event)"
1098
+ />
1099
+ </div>
1100
+ <div class="flex flex-col gap-2">
1101
+ <h3 [id]="radiusLabelId" class="nav-heading text-muted-foreground">Theme radius</h3>
1102
+ <SegmentedControl
1103
+ appearance="icon-text"
1104
+ space="compact"
1105
+ [value]="radius()"
1106
+ [options]="radiusOptions"
1107
+ [aria-labelledby]="radiusLabelId"
1108
+ (valueChange)="setRadius($event)"
1109
+ />
1110
+ </div>
1111
+ </section>
1112
+ `, isInline: true, dependencies: [{ kind: "component", type: SegmentedControlComponent, selector: "SegmentedControl", inputs: ["options", "value", "appearance", "space", "disabled", "aria-label", "aria-labelledby", "aria-describedby", "class"], outputs: ["valueChange"] }] });
1113
+ }
1114
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSettingsThemeSectionComponent, decorators: [{
1115
+ type: Component,
1116
+ args: [{
1117
+ selector: 'ThemeSettingsThemeSection',
1118
+ imports: [SegmentedControlComponent],
1119
+ host: { class: 'contents' },
1120
+ template: `
1121
+ <section class="flex flex-col gap-5" [attr.aria-labelledby]="themeGroupId">
1122
+ <h2 [id]="themeGroupId" class="nav-heading text-foreground">Theme</h2>
1123
+ <div class="flex flex-col gap-2">
1124
+ <div class="flex items-baseline justify-between gap-3">
1125
+ <h3 [id]="modeLabelId" class="nav-heading text-muted-foreground">Theme mode</h3>
1126
+ <span class="text-xs text-muted-foreground">{{ resolvedMode() }}</span>
1127
+ </div>
1128
+ <SegmentedControl
1129
+ appearance="icon-text"
1130
+ space="compact"
1131
+ [value]="mode()"
1132
+ [options]="modeOptions"
1133
+ [aria-labelledby]="modeLabelId"
1134
+ (valueChange)="setMode($event)"
1135
+ />
1136
+ </div>
1137
+ <div class="flex flex-col gap-2">
1138
+ <h3 [id]="brandLabelId" class="nav-heading text-muted-foreground">Theme brand</h3>
1139
+ <input
1140
+ type="color"
1141
+ class="size-10 cursor-pointer appearance-none overflow-hidden rounded-full border border-border bg-transparent p-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background [&::-moz-color-swatch]:rounded-full [&::-webkit-color-swatch-wrapper]:p-0 [&::-webkit-color-swatch]:rounded-full"
1142
+ [value]="brandHex()"
1143
+ [attr.aria-labelledby]="brandLabelId"
1144
+ (input)="setBrandFromPicker($event)"
1145
+ />
1146
+ </div>
1147
+ <div class="flex flex-col gap-2">
1148
+ <h3 [id]="accentLabelId" class="nav-heading text-muted-foreground">Theme color</h3>
1149
+ <fieldset class="grid grid-cols-5 gap-2 sm:grid-cols-6">
1150
+ <legend class="sr-only">Choose theme color</legend>
1151
+ @for (accent of accents; track accent) {
1152
+ <label class="inline-flex size-10 cursor-pointer items-center justify-center"
1153
+ ><input
1154
+ class="peer sr-only"
1155
+ type="radio"
1156
+ name="theme-settings-accent"
1157
+ [value]="accent"
1158
+ [checked]="color() === accent"
1159
+ (change)="setColor(accent)"
1160
+ /><span
1161
+ [class]="swatchClasses(color() === accent)"
1162
+ [style.background]="accentSwatch[accent]"
1163
+ ><span class="sr-only">{{ accent }}</span></span
1164
+ ></label
1165
+ >
1166
+ }
1167
+ </fieldset>
1168
+ </div>
1169
+ <div class="flex flex-col gap-2">
1170
+ <h3 [id]="neutralLabelId" class="nav-heading text-muted-foreground">Theme neutral</h3>
1171
+ <fieldset class="grid grid-cols-5 gap-2 sm:grid-cols-6">
1172
+ <legend class="sr-only">Choose theme neutral</legend>
1173
+ @for (neutral of neutrals; track neutral) {
1174
+ <label class="inline-flex size-10 cursor-pointer items-center justify-center"
1175
+ ><input
1176
+ class="peer sr-only"
1177
+ type="radio"
1178
+ name="theme-settings-neutral"
1179
+ [value]="neutral"
1180
+ [checked]="neutralColor() === neutral"
1181
+ (change)="setNeutral(neutral)"
1182
+ /><span
1183
+ [class]="swatchClasses(neutralColor() === neutral)"
1184
+ [style.background]="neutralSwatch[neutral]"
1185
+ ><span class="sr-only">{{ neutral }}</span></span
1186
+ ></label
1187
+ >
1188
+ }
1189
+ </fieldset>
1190
+ </div>
1191
+ <div class="flex flex-col gap-2">
1192
+ <h3 [id]="spaceLabelId" class="nav-heading text-muted-foreground">Theme space</h3>
1193
+ <SegmentedControl
1194
+ appearance="icon-text"
1195
+ space="compact"
1196
+ [value]="space()"
1197
+ [options]="spaceOptions"
1198
+ [aria-labelledby]="spaceLabelId"
1199
+ (valueChange)="setSpace($event)"
1200
+ />
1201
+ </div>
1202
+ <div class="flex flex-col gap-2">
1203
+ <h3 [id]="radiusLabelId" class="nav-heading text-muted-foreground">Theme radius</h3>
1204
+ <SegmentedControl
1205
+ appearance="icon-text"
1206
+ space="compact"
1207
+ [value]="radius()"
1208
+ [options]="radiusOptions"
1209
+ [aria-labelledby]="radiusLabelId"
1210
+ (valueChange)="setRadius($event)"
1211
+ />
1212
+ </div>
1213
+ </section>
1214
+ `,
1215
+ }]
1216
+ }] });
1217
+
1218
+ /** A navigation-aligned, right-side inspector for runtime theme and shell settings. */
1219
+ class ThemeSettingsComponent {
1220
+ /**
1221
+ * Present only when the app provided an adapter. It is a render gate here: the
1222
+ * Layout and Navigation sections inject the token non-optionally, so a
1223
+ * theme-only consumer simply gets the Theme and Shell sections.
1224
+ */
1225
+ settings = inject(THEME_SETTINGS_ADAPTER, { optional: true });
1226
+ open = model(false, /* @ts-ignore */
1227
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
1228
+ trigger = input('button', /* @ts-ignore */
1229
+ ...(ngDevMode ? [{ debugName: "trigger" }] : /* istanbul ignore next */ []));
1230
+ triggerLabel = input('Theme settings', /* @ts-ignore */
1231
+ ...(ngDevMode ? [{ debugName: "triggerLabel" }] : /* istanbul ignore next */ []));
1232
+ triggerIconOnly = input(false, { ...(ngDevMode ? { debugName: "triggerIconOnly" } : /* istanbul ignore next */ {}), alias: 'trigger-icon-only' });
1233
+ showTrigger = input(true, { ...(ngDevMode ? { debugName: "showTrigger" } : /* istanbul ignore next */ {}), alias: 'show-trigger' });
1234
+ side = input('right', /* @ts-ignore */
1235
+ ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
1236
+ class = input('', { ...(ngDevMode ? { debugName: "class" } : /* istanbul ignore next */ {}), alias: 'class' });
1237
+ triggerClasses = computed(() => cn('inline-flex items-center gap-2 rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none', this.trigger() === 'link'
1238
+ ? this.triggerIconOnly()
1239
+ ? 'size-11 p-0 text-primary underline-offset-4 hover:underline'
1240
+ : 'min-h-11 px-1 text-primary underline-offset-4 hover:underline'
1241
+ : this.triggerIconOnly()
1242
+ ? 'size-11 justify-center p-0 text-muted-foreground hover:bg-accent hover:text-foreground'
1243
+ : 'min-h-11 justify-center px-3 text-muted-foreground hover:bg-accent hover:text-accent-foreground', this.class()), /* @ts-ignore */
1244
+ ...(ngDevMode ? [{ debugName: "triggerClasses" }] : /* istanbul ignore next */ []));
1245
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1246
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: ThemeSettingsComponent, isStandalone: true, selector: "ThemeSettings", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, trigger: { classPropertyName: "trigger", publicName: "trigger", isSignal: true, isRequired: false, transformFunction: null }, triggerLabel: { classPropertyName: "triggerLabel", publicName: "triggerLabel", isSignal: true, isRequired: false, transformFunction: null }, triggerIconOnly: { classPropertyName: "triggerIconOnly", publicName: "trigger-icon-only", isSignal: true, isRequired: false, transformFunction: null }, showTrigger: { classPropertyName: "showTrigger", publicName: "show-trigger", isSignal: true, isRequired: false, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { classAttribute: "contents" }, ngImport: i0, template: `
1247
+ @if (showTrigger()) {
1248
+ <button
1249
+ type="button"
1250
+ [class]="triggerClasses()"
1251
+ [attr.aria-label]="triggerIconOnly() ? triggerLabel() : null"
1252
+ [attr.title]="triggerIconOnly() ? triggerLabel() : null"
1253
+ aria-haspopup="dialog"
1254
+ [attr.aria-expanded]="open()"
1255
+ (click)="open.set(true)"
1256
+ >
1257
+ <Icon name="palette" [size]="18" />
1258
+ @if (triggerIconOnly()) {
1259
+ <span class="sr-only">{{ triggerLabel() }}</span>
1260
+ } @else {
1261
+ <span>{{ triggerLabel() }}</span>
1262
+ }
1263
+ </button>
1264
+ }
1265
+
1266
+ @if (open()) {
1267
+ <Drawer
1268
+ [(open)]="open"
1269
+ [side]="side()"
1270
+ [showCloseButton]="false"
1271
+ class="ThemeSettings w-full max-w-md gap-0! p-0! sm:w-100"
1272
+ >
1273
+ <DrawerHeader
1274
+ class="flex h-(--layout-topbar-height) shrink-0 flex-row items-center gap-3 border-b border-border bg-background px-3 text-left"
1275
+ >
1276
+ <span
1277
+ aria-hidden="true"
1278
+ class="grid size-8 shrink-0 place-items-center rounded-md bg-primary/10 text-primary ring-1 ring-primary/20"
1279
+ ><Icon name="palette" [size]="18"
1280
+ /></span>
1281
+ <div class="min-w-0 flex-1">
1282
+ <DrawerTitle>Theme settings</DrawerTitle>
1283
+ </div>
1284
+ <button
1285
+ type="button"
1286
+ DrawerClose
1287
+ class="inline-flex h-8 shrink-0 items-center rounded-md bg-primary px-2 text-xs font-medium text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background motion-reduce:transition-none"
1288
+ >
1289
+ Done
1290
+ </button>
1291
+ </DrawerHeader>
1292
+
1293
+ <DrawerContent class="min-h-0 px-4 py-5 sm:px-5">
1294
+ <div class="flex flex-col gap-8">
1295
+ <ThemeSettingsThemeSection />
1296
+ <ThemeSettingsShellSection />
1297
+ @if (settings) {
1298
+ <ThemeSettingsLayoutSection />
1299
+ <ThemeSettingsNavigationSection />
1300
+ }
1301
+ </div>
1302
+ </DrawerContent>
1303
+ </Drawer>
1304
+ }
1305
+ `, isInline: true, dependencies: [{ kind: "directive", type: DrawerCloseDirective, selector: "button[SheetClose], a[SheetClose], button[DrawerClose], a[DrawerClose]" }, { kind: "component", type: DrawerComponent, selector: "Sheet, Drawer", inputs: ["open", "side", "variant", "closeOnEscape", "closeOnBackdropClick", "backdrop", "showCloseButton", "closeButtonLabel", "restoreFocus", "aria-labelledby", "aria-describedby", "role", "class"], outputs: ["openChange", "openedChange"] }, { kind: "component", type: DrawerContentComponent, selector: "SheetContent, DrawerContent", inputs: ["class"] }, { kind: "component", type: DrawerHeaderComponent, selector: "SheetHeader, DrawerHeader", inputs: ["class"] }, { kind: "component", type: DrawerTitleComponent, selector: "SheetTitle, DrawerTitle, h2[SheetTitle], h2[DrawerTitle]", inputs: ["id", "class"] }, { kind: "component", type: IconComponent, selector: "Icon", inputs: ["name", "class", "size", "fill", "weight", "grade", "opticalSize"] }, { kind: "component", type: ThemeSettingsLayoutSectionComponent, selector: "ThemeSettingsLayoutSection" }, { kind: "component", type: ThemeSettingsNavigationSectionComponent, selector: "ThemeSettingsNavigationSection" }, { kind: "component", type: ThemeSettingsShellSectionComponent, selector: "ThemeSettingsShellSection" }, { kind: "component", type: ThemeSettingsThemeSectionComponent, selector: "ThemeSettingsThemeSection" }] });
1306
+ }
1307
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSettingsComponent, decorators: [{
1308
+ type: Component,
1309
+ args: [{
1310
+ selector: 'ThemeSettings',
1311
+ imports: [
1312
+ DrawerCloseDirective,
1313
+ DrawerComponent,
1314
+ DrawerContentComponent,
1315
+ DrawerHeaderComponent,
1316
+ DrawerTitleComponent,
1317
+ IconComponent,
1318
+ ThemeSettingsLayoutSectionComponent,
1319
+ ThemeSettingsNavigationSectionComponent,
1320
+ ThemeSettingsShellSectionComponent,
1321
+ ThemeSettingsThemeSectionComponent,
1322
+ ],
1323
+ host: { class: 'contents' },
1324
+ template: `
1325
+ @if (showTrigger()) {
1326
+ <button
1327
+ type="button"
1328
+ [class]="triggerClasses()"
1329
+ [attr.aria-label]="triggerIconOnly() ? triggerLabel() : null"
1330
+ [attr.title]="triggerIconOnly() ? triggerLabel() : null"
1331
+ aria-haspopup="dialog"
1332
+ [attr.aria-expanded]="open()"
1333
+ (click)="open.set(true)"
1334
+ >
1335
+ <Icon name="palette" [size]="18" />
1336
+ @if (triggerIconOnly()) {
1337
+ <span class="sr-only">{{ triggerLabel() }}</span>
1338
+ } @else {
1339
+ <span>{{ triggerLabel() }}</span>
1340
+ }
1341
+ </button>
1342
+ }
1343
+
1344
+ @if (open()) {
1345
+ <Drawer
1346
+ [(open)]="open"
1347
+ [side]="side()"
1348
+ [showCloseButton]="false"
1349
+ class="ThemeSettings w-full max-w-md gap-0! p-0! sm:w-100"
1350
+ >
1351
+ <DrawerHeader
1352
+ class="flex h-(--layout-topbar-height) shrink-0 flex-row items-center gap-3 border-b border-border bg-background px-3 text-left"
1353
+ >
1354
+ <span
1355
+ aria-hidden="true"
1356
+ class="grid size-8 shrink-0 place-items-center rounded-md bg-primary/10 text-primary ring-1 ring-primary/20"
1357
+ ><Icon name="palette" [size]="18"
1358
+ /></span>
1359
+ <div class="min-w-0 flex-1">
1360
+ <DrawerTitle>Theme settings</DrawerTitle>
1361
+ </div>
1362
+ <button
1363
+ type="button"
1364
+ DrawerClose
1365
+ class="inline-flex h-8 shrink-0 items-center rounded-md bg-primary px-2 text-xs font-medium text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background motion-reduce:transition-none"
1366
+ >
1367
+ Done
1368
+ </button>
1369
+ </DrawerHeader>
1370
+
1371
+ <DrawerContent class="min-h-0 px-4 py-5 sm:px-5">
1372
+ <div class="flex flex-col gap-8">
1373
+ <ThemeSettingsThemeSection />
1374
+ <ThemeSettingsShellSection />
1375
+ @if (settings) {
1376
+ <ThemeSettingsLayoutSection />
1377
+ <ThemeSettingsNavigationSection />
1378
+ }
1379
+ </div>
1380
+ </DrawerContent>
1381
+ </Drawer>
1382
+ }
1383
+ `,
1384
+ }]
1385
+ }], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], trigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "trigger", required: false }] }], triggerLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerLabel", required: false }] }], triggerIconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "trigger-icon-only", required: false }] }], showTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "show-trigger", required: false }] }], side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
4
1386
 
5
1387
  /**
6
1388
  * Generated bundle index. Do not edit.
7
- */
1389
+ */
1390
+
1391
+ export { NAV_TYPE_MODE_STORAGE_KEY, NAV_TYPE_STORAGE_KEY, THEME_SETTINGS_ADAPTER, ThemeSettingsComponent, ThemeSettingsDefaultAdapter, allowedNavTypeModes, allowedNavTypes$1 as allowedNavTypes, isWidthEnabled, provideThemeSettingsAdapter };