@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,787 @@
1
- export * from '@ojiepermana/angular-theme/styles';
1
+ import { DOCUMENT, isPlatformBrowser } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, signal, effect, computed, DestroyRef, InjectionToken, Service, makeEnvironmentProviders, provideEnvironmentInitializer, PLATFORM_ID } from '@angular/core';
4
+ import { documentLocalStorage } from '@ojiepermana/angular/component/utils/core';
2
5
 
3
- // Generated by scripts/gen-umbrella-entrypoints.mjs — do not edit by hand.
6
+ /**
7
+ * One place for the "a preference that lives in localStorage and shows up as an
8
+ * attribute on `<html>`" pattern that every theme axis needs.
9
+ *
10
+ * Each axis service used to hand-roll the same four private methods (`storage`,
11
+ * `readStorage`, `persist`, plus an effect writing the attribute) around a
12
+ * `signal`. That is the whole body of the simpler services, so a fix to any of
13
+ * it had to be made five times.
14
+ *
15
+ * The storage half itself lives in `@ojiepermana/angular/component/utils/core`,
16
+ * because `navigation` needs it too and navigation must not depend on this
17
+ * package.
18
+ */
19
+ /**
20
+ * A persisted value, without the DOM half.
21
+ *
22
+ * Useful on its own for a preference that is not an attribute: the brand axis
23
+ * stores an object and paints custom properties instead.
24
+ *
25
+ * Two timing details the theme services depend on:
26
+ *
27
+ * - the stored value is read SYNCHRONOUSLY here, not in an effect, because
28
+ * every service exposes its state to consumers that read it during the same
29
+ * change detection pass;
30
+ * - the effect writes on its FIRST run, so storage holds the resolved default
31
+ * immediately after construction even when nothing was stored.
32
+ */
33
+ function persistedSignal(options) {
34
+ const documentRef = options.documentRef !== undefined ? options.documentRef : inject(DOCUMENT, { optional: true });
35
+ const storage = documentLocalStorage(documentRef);
36
+ const state = signal(options.parse(storage.read(options.key)), /* @ts-ignore */
37
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
38
+ effect(() => {
39
+ const serialized = options.serialize(state());
40
+ if (serialized === null) {
41
+ storage.remove(options.key);
42
+ }
43
+ else {
44
+ storage.write(options.key, serialized);
45
+ }
46
+ });
47
+ return state;
48
+ }
49
+ /**
50
+ * Mirror a reactive string onto one or more `<html>` attributes. A `null` value
51
+ * removes them.
52
+ */
53
+ function htmlAttributeEffect(options) {
54
+ const documentRef = options.documentRef !== undefined ? options.documentRef : inject(DOCUMENT, { optional: true });
55
+ const attributes = typeof options.attribute === 'string' ? [options.attribute] : options.attribute;
56
+ effect(() => {
57
+ const value = options.source();
58
+ const element = documentRef?.documentElement;
59
+ if (!element)
60
+ return;
61
+ for (const attribute of attributes) {
62
+ if (value === null) {
63
+ element.removeAttribute(attribute);
64
+ }
65
+ else {
66
+ element.setAttribute(attribute, value);
67
+ }
68
+ }
69
+ });
70
+ }
71
+ /** Mirror reactive inline custom properties onto `<html>`. A `null` value removes one. */
72
+ function htmlStylePropertyEffect(options) {
73
+ const documentRef = options.documentRef !== undefined ? options.documentRef : inject(DOCUMENT, { optional: true });
74
+ effect(() => {
75
+ const properties = options.source();
76
+ const element = documentRef?.documentElement;
77
+ if (!element)
78
+ return;
79
+ for (const [property, value] of Object.entries(properties)) {
80
+ if (value === null) {
81
+ element.style.removeProperty(property);
82
+ }
83
+ else {
84
+ element.style.setProperty(property, value);
85
+ }
86
+ }
87
+ });
88
+ }
89
+ /**
90
+ * A persisted preference that also shows up as an attribute on `<html>`.
91
+ *
92
+ * The stored value wins over the configured default, which is the rule every
93
+ * axis already followed.
94
+ */
95
+ function persistedHtmlAttributeSignal(options) {
96
+ const documentRef = options.documentRef !== undefined ? options.documentRef : inject(DOCUMENT, { optional: true });
97
+ const state = persistedSignal({
98
+ key: options.key,
99
+ documentRef,
100
+ // A stored value wins; otherwise fall back to the configured default. Both
101
+ // go through `normalize`, so neither can put a junk value into the signal.
102
+ //
103
+ // An empty stored string counts as ABSENT rather than as a value: no axis
104
+ // has a legal `''`, so a key cleared to it (a storage the browser or another
105
+ // tab emptied, a hand-edited entry) is a key with nothing in it. Reading it
106
+ // as present would send it through `normalize` to the LIBRARY default and
107
+ // silently discard the default the consumer configured.
108
+ parse: (raw) => {
109
+ const stored = raw !== null && raw !== '' ? raw : null;
110
+ return options.normalize(stored ?? options.normalize(options.initial));
111
+ },
112
+ serialize: (value) => value,
113
+ });
114
+ // A computed, so whatever the resolver reads is tracked alongside the stored
115
+ // value. That is what keeps a `'system'` mode following the operating system
116
+ // while still persisting `'system'` rather than the resolved light or dark.
117
+ const resolveAttributeValue = options.resolveAttributeValue;
118
+ const attributeValue = resolveAttributeValue
119
+ ? computed(() => resolveAttributeValue(state()))
120
+ : computed(() => state());
121
+ htmlAttributeEffect({
122
+ attribute: options.attribute,
123
+ source: () => attributeValue(),
124
+ documentRef,
125
+ });
126
+ return {
127
+ value: state.asReadonly(),
128
+ attributeValue,
129
+ set: (next) => state.set(next),
130
+ };
131
+ }
132
+ /**
133
+ * `(prefers-color-scheme: dark)` as a signal, torn down with the injection
134
+ * context that created it.
135
+ *
136
+ * Lives here because it is the other half of the same story: an axis whose
137
+ * PERSISTED value is `'system'` still has to write a concrete `light` or `dark`
138
+ * to the DOM, and this is what makes that reactive.
139
+ */
140
+ function prefersDarkSignal(documentRef) {
141
+ const resolved = documentRef !== undefined ? documentRef : inject(DOCUMENT, { optional: true });
142
+ const mediaQueryList = resolved?.defaultView?.matchMedia?.('(prefers-color-scheme: dark)') ?? null;
143
+ const state = signal(mediaQueryList?.matches ?? false, /* @ts-ignore */
144
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
145
+ if (mediaQueryList) {
146
+ const onChange = (event) => state.set(event.matches);
147
+ mediaQueryList.addEventListener('change', onChange);
148
+ inject(DestroyRef).onDestroy(() => mediaQueryList.removeEventListener('change', onChange));
149
+ }
150
+ return state.asReadonly();
151
+ }
152
+
153
+ /**
154
+ * WCAG contrast math for tokens decided at runtime.
155
+ *
156
+ * A TypeScript port of scripts/theme-contrast.mjs, which is where the built-in
157
+ * palettes are measured at generation time. The two halves have to agree: a
158
+ * brand a consumer sets through `ThemeBrandService` lands on `--primary` and
159
+ * `--ring` exactly like a generated accent does, so it is held to the same
160
+ * thresholds by the same formula rather than a second approximation of it.
161
+ *
162
+ * Everything works on the bare HSL triplet (`H S% L%`) the CSS consumes through
163
+ * `hsl(var(--token))`, so the numbers asserted are the numbers shipped. Parsing
164
+ * returns `null` instead of throwing, because the caller here is a setter on a
165
+ * running app and not a build step.
166
+ */
167
+ /** WCAG 1.4.3 minimum for normal-size text. */
168
+ const TEXT_CONTRAST_MIN = 4.5;
169
+ /** WCAG 1.4.11 minimum for non-text contrast (focus rings, borders, icons). */
170
+ const NON_TEXT_CONTRAST_MIN = 3;
171
+ /**
172
+ * Extra headroom used when solving for a lightness, so a value that only just
173
+ * clears the threshold cannot fail an assertion on a rounding difference.
174
+ */
175
+ const SOLVE_MARGIN = 0.02;
176
+ const NUMBER = String.raw `[+-]?(?:\d+(?:\.\d+)?|\.\d+)`;
177
+ // CSS accepts the components separated by commas or by whitespace, and the hue
178
+ // as any <angle>. Every one of those forms paints, so every one of them has to
179
+ // be measurable — a syntax this rejected used to walk straight past the brand
180
+ // contrast guarantee.
181
+ const SEPARATOR = String.raw `(?:\s*,\s*|\s+)`;
182
+ const HSL_TRIPLET = new RegExp(`^\\s*(${NUMBER})(deg|grad|rad|turn)?${SEPARATOR}(${NUMBER})%${SEPARATOR}(${NUMBER})%\\s*$`, 'i');
183
+ const HEX_COLOR = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
184
+ /** `<angle>` units CSS allows for a hue, as a multiplier onto degrees. */
185
+ const DEGREES_PER_UNIT = {
186
+ deg: 1,
187
+ grad: 0.9,
188
+ rad: 180 / Math.PI,
189
+ turn: 360,
190
+ };
191
+ const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
192
+ /**
193
+ * Wrap a hue into 0..360 the way CSS does, so `-240` and `120` are the same
194
+ * colour here as well and cannot measure differently.
195
+ */
196
+ const normalizeHue = (hue) => ((hue % 360) + 360) % 360;
197
+ /** Read an HSL triplet string (`'221 83% 48%'`). `null` when it is not one. */
198
+ function parseHslTriplet(value) {
199
+ const match = HSL_TRIPLET.exec(value);
200
+ if (!match) {
201
+ return null;
202
+ }
203
+ const [h, s, l] = [Number(match[1]), Number(match[3]), Number(match[4])];
204
+ if (![h, s, l].every(Number.isFinite)) {
205
+ return null;
206
+ }
207
+ return { h: normalizeHue(h * DEGREES_PER_UNIT[match[2]?.toLowerCase() ?? 'deg']), s, l };
208
+ }
209
+ /** Format numbers back into the triplet form the CSS consumes. */
210
+ function formatHslTriplet({ h, s, l }) {
211
+ return `${h} ${clamp(Math.round(s), 0, 100)}% ${clamp(Math.round(l), 0, 100)}%`;
212
+ }
213
+ /** `#rgb` / `#rrggbb` as an HSL triplet. `null` when the input is not a hex color. */
214
+ function hexToHslTriplet(value) {
215
+ const match = HEX_COLOR.exec(value.trim());
216
+ if (!match) {
217
+ return null;
218
+ }
219
+ const digits = match[1];
220
+ const expanded = digits.length === 3
221
+ ? digits
222
+ .split('')
223
+ .map((digit) => digit + digit)
224
+ .join('')
225
+ : digits;
226
+ const [red, green, blue] = [0, 2, 4].map((offset) => Number.parseInt(expanded.slice(offset, offset + 2), 16) / 255);
227
+ const largest = Math.max(red, green, blue);
228
+ const smallest = Math.min(red, green, blue);
229
+ const delta = largest - smallest;
230
+ const lightness = (largest + smallest) / 2;
231
+ let hue = 0;
232
+ if (delta !== 0) {
233
+ if (largest === red)
234
+ hue = 60 * (((green - blue) / delta + 6) % 6);
235
+ else if (largest === green)
236
+ hue = 60 * ((blue - red) / delta + 2);
237
+ else
238
+ hue = 60 * ((red - green) / delta + 4);
239
+ }
240
+ const saturation = delta === 0 ? 0 : delta / (1 - Math.abs(2 * lightness - 1));
241
+ return formatHslTriplet({ h: Math.round(hue), s: saturation * 100, l: lightness * 100 });
242
+ }
243
+ /** HSL to sRGB, each channel in the 0..1 range (CSS Color 4 algorithm). */
244
+ function hslToRgb({ h, s, l }) {
245
+ const saturation = clamp(s, 0, 100) / 100;
246
+ const lightness = clamp(l, 0, 100) / 100;
247
+ const k = (n) => (n + h / 30) % 12;
248
+ const a = saturation * Math.min(lightness, 1 - lightness);
249
+ const channel = (n) => lightness - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
250
+ return [channel(0), channel(8), channel(4)];
251
+ }
252
+ /** sRGB channel to linear light, per the WCAG relative luminance definition. */
253
+ const toLinear = (channel) => channel <= 0.03928 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);
254
+ /** WCAG relative luminance of an HSL triplet. */
255
+ function relativeLuminance(triplet) {
256
+ const parsed = parseHslTriplet(triplet);
257
+ if (!parsed) {
258
+ return 0;
259
+ }
260
+ const [r, g, b] = hslToRgb(parsed);
261
+ return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
262
+ }
263
+ /** WCAG contrast ratio between two HSL triplets. Order does not matter. */
264
+ function contrastRatio(a, b) {
265
+ const ya = relativeLuminance(a);
266
+ const yb = relativeLuminance(b);
267
+ return (Math.max(ya, yb) + 0.05) / (Math.min(ya, yb) + 0.05);
268
+ }
269
+ /**
270
+ * Walk outward from `from` in 1% lightness steps and return the nearest
271
+ * lightness whose color clears `min` against every entry in `against`. Keeping
272
+ * hue and saturation fixed means the brand's identity survives; only how light
273
+ * it is moves, and it moves as little as the threshold allows.
274
+ *
275
+ * `against` is a list so a value that has to hold over more than one surface can
276
+ * be solved in one pass; the brand ring solves against one `--background` per
277
+ * mode, because it is painted per mode.
278
+ *
279
+ * `null` when no lightness in 0..100 satisfies every background.
280
+ */
281
+ function solveLightnessNearest(options) {
282
+ const target = options.min + (options.margin ?? SOLVE_MARGIN);
283
+ const start = Math.round(clamp(options.from, 0, 100));
284
+ for (let delta = 0; delta <= 100; delta += 1) {
285
+ for (const candidate of delta === 0 ? [start] : [start - delta, start + delta]) {
286
+ if (candidate < 0 || candidate > 100)
287
+ continue;
288
+ const triplet = formatHslTriplet({ h: options.h, s: options.s, l: candidate });
289
+ if (options.against.every((background) => contrastRatio(triplet, background) >= target)) {
290
+ return triplet;
291
+ }
292
+ }
293
+ }
294
+ return null;
295
+ }
296
+ /**
297
+ * Keep a designed foreground when it already clears the threshold, otherwise
298
+ * nudge its lightness to the nearest value that does. Anything unparsable is
299
+ * returned untouched — there is nothing to measure.
300
+ */
301
+ function fitForeground(options) {
302
+ const min = options.min ?? TEXT_CONTRAST_MIN;
303
+ if (contrastRatio(options.foreground, options.background) >= min) {
304
+ return options.foreground;
305
+ }
306
+ const parsed = parseHslTriplet(options.foreground);
307
+ if (!parsed) {
308
+ return options.foreground;
309
+ }
310
+ return (solveLightnessNearest({
311
+ h: parsed.h,
312
+ s: parsed.s,
313
+ against: [options.background],
314
+ min,
315
+ from: parsed.l,
316
+ }) ?? options.foreground);
317
+ }
318
+
319
+ const THEME_OPTIONS = new InjectionToken('THEME_OPTIONS');
320
+
321
+ const THEME_MODES = ['light', 'dark', 'system'];
322
+ const RESOLVED_THEME_MODES = ['light', 'dark'];
323
+ /**
324
+ * FluxUI-style accent palettes (axis `theme-color`). `base` = core theme (no
325
+ * override); `brand` = the consumer's `--brand` color. Others re-tint the full
326
+ * palette. Order mirrors the FluxUI accent picker (base…rose) + `brand`.
327
+ */
328
+ const THEME_COLORS = [
329
+ 'base',
330
+ 'red',
331
+ 'orange',
332
+ 'amber',
333
+ 'yellow',
334
+ 'lime',
335
+ 'green',
336
+ 'emerald',
337
+ 'teal',
338
+ 'cyan',
339
+ 'sky',
340
+ 'blue',
341
+ 'indigo',
342
+ 'violet',
343
+ 'purple',
344
+ 'fuchsia',
345
+ 'pink',
346
+ 'rose',
347
+ 'brand',
348
+ ];
349
+ /**
350
+ * Neutral families (axis `theme-neutral`). `base` = core theme neutrals and lets
351
+ * Layout patterns follow the accent. Non-base values override both the gray
352
+ * family and Layout's decorative pattern hue, while composing with any accent.
353
+ */
354
+ const THEME_NEUTRALS = [
355
+ 'base',
356
+ 'slate',
357
+ 'gray',
358
+ 'zinc',
359
+ 'neutral',
360
+ 'stone',
361
+ 'mauve',
362
+ 'olive',
363
+ 'mist',
364
+ 'taupe',
365
+ ];
366
+ /**
367
+ * Corner radius presets (axis `theme-radius`). Each drives the single
368
+ * `--radius-base` knob; the whole `--radius-*` scale + `rounded-*` utilities
369
+ * follow. Every preset takes Tailwind's stock value for that name — `none` 0,
370
+ * `xs` 0.125rem, `sm` 0.25rem, `md` 0.375rem (the default), `xl` 0.75rem — and
371
+ * `full` is the pill extreme.
372
+ */
373
+ const THEME_RADII = ['none', 'xs', 'sm', 'md', 'xl', 'full'];
374
+ /**
375
+ * Spacing density presets (axis `theme-space`). Each drives the single
376
+ * `--spacing-base` knob; every `p-*`/`m-*`/`gap-*`/`w-*`/`h-*` utility follows.
377
+ * `normal` = the 0.25rem default (no-op baseline).
378
+ */
379
+ const THEME_SPACES = ['compact', 'normal'];
380
+ const DEFAULT_THEME_MODE = 'system';
381
+ const DEFAULT_THEME_COLOR = 'base';
382
+ const DEFAULT_THEME_NEUTRAL = 'base';
383
+ const DEFAULT_THEME_RADIUS = 'md';
384
+ const DEFAULT_THEME_SPACE = 'normal';
385
+ const THEME_MODE_STORAGE_KEY = 'theme-mode';
386
+ const THEME_COLOR_STORAGE_KEY = 'theme-color';
387
+ const THEME_NEUTRAL_STORAGE_KEY = 'theme-neutral';
388
+ const THEME_BRAND_STORAGE_KEY = 'theme-brand';
389
+ const THEME_RADIUS_STORAGE_KEY = 'theme-radius';
390
+ const THEME_SPACE_STORAGE_KEY = 'theme-space';
391
+ function isThemeMode(value) {
392
+ return typeof value === 'string' && THEME_MODES.includes(value);
393
+ }
394
+ function isResolvedThemeMode(value) {
395
+ return typeof value === 'string' && RESOLVED_THEME_MODES.includes(value);
396
+ }
397
+ function isThemeColor(value) {
398
+ return typeof value === 'string' && THEME_COLORS.includes(value);
399
+ }
400
+ function isThemeNeutral(value) {
401
+ return typeof value === 'string' && THEME_NEUTRALS.includes(value);
402
+ }
403
+ function isThemeRadius(value) {
404
+ return typeof value === 'string' && THEME_RADII.includes(value);
405
+ }
406
+ function isThemeSpace(value) {
407
+ return typeof value === 'string' && THEME_SPACES.includes(value);
408
+ }
409
+ function normalizeThemeMode(value) {
410
+ return isThemeMode(value) ? value : DEFAULT_THEME_MODE;
411
+ }
412
+ function normalizeThemeColor(value) {
413
+ return isThemeColor(value) ? value : DEFAULT_THEME_COLOR;
414
+ }
415
+ function normalizeThemeNeutral(value) {
416
+ return isThemeNeutral(value) ? value : DEFAULT_THEME_NEUTRAL;
417
+ }
418
+ function normalizeThemeRadius(value) {
419
+ return isThemeRadius(value) ? value : DEFAULT_THEME_RADIUS;
420
+ }
421
+ function normalizeThemeSpace(value) {
422
+ return isThemeSpace(value) ? value : DEFAULT_THEME_SPACE;
423
+ }
424
+
425
+ const DEFAULT_BRAND_FOREGROUND = '0 0% 100%';
426
+ /**
427
+ * `--background` per mode, from css/base/tokens.css.
428
+ *
429
+ * The brand fill is one inline custom property that both modes read, but the
430
+ * focus ring is not: `--brand-ring-light` and `--brand-ring-dark` are solved
431
+ * separately and css/color/brand.css picks the one the current mode needs, so
432
+ * the ring clears the non-text threshold without dragging the fill with it.
433
+ */
434
+ const RING_BACKGROUNDS = { light: '0 0% 100%', dark: '0 0% 5%' };
435
+ /**
436
+ * The brand as the canonical HSL triplet the CSS consumes: hex is converted, and
437
+ * every HSL form CSS accepts (comma separated, an angle-suffixed hue) is
438
+ * normalised, so none of them can paint while skipping the measurement.
439
+ *
440
+ * `null` for anything else — a `var()` reference is a documented escape hatch,
441
+ * and there is nothing in it to measure or convert.
442
+ */
443
+ function toBrandTriplet(color) {
444
+ const trimmed = color.trim();
445
+ const parsed = parseHslTriplet(trimmed);
446
+ return parsed ? formatHslTriplet(parsed) : hexToHslTriplet(trimmed);
447
+ }
448
+ /**
449
+ * Hold a brand to the text half of the standard the generator holds every
450
+ * built-in palette to: text on the brand fill at least 4.5 to 1. Only the
451
+ * FOREGROUND moves, so the fill the consumer chose is the fill that ships —
452
+ * exactly as the generator keeps `--primary` at its design lightness and
453
+ * darkens a separate `--ring` instead.
454
+ *
455
+ * The non-text half is the ring's job; see {@link brandRing}.
456
+ */
457
+ function hardenBrand(brand) {
458
+ const color = toBrandTriplet(brand.color);
459
+ if (!color) {
460
+ return brand;
461
+ }
462
+ return {
463
+ color,
464
+ foreground: fitForeground({
465
+ foreground: brand.foreground,
466
+ background: color,
467
+ min: TEXT_CONTRAST_MIN,
468
+ }),
469
+ };
470
+ }
471
+ /**
472
+ * The focus ring for one mode: the brand itself when it already clears the
473
+ * non-text threshold against that mode's `--background`, otherwise the nearest
474
+ * lightness that does. Hue and saturation never move, so the ring still reads
475
+ * as the brand.
476
+ */
477
+ function brandRing(color, background) {
478
+ const parsed = parseHslTriplet(color);
479
+ if (!parsed) {
480
+ return null;
481
+ }
482
+ if (contrastRatio(color, background) >= NON_TEXT_CONTRAST_MIN) {
483
+ return color;
484
+ }
485
+ return (solveLightnessNearest({
486
+ h: parsed.h,
487
+ s: parsed.s,
488
+ against: [background],
489
+ min: NON_TEXT_CONTRAST_MIN,
490
+ from: parsed.l,
491
+ }) ?? color);
492
+ }
493
+ /** Extract the hue/saturation pair used by Layout's soft pattern tiers. */
494
+ function brandPatternHueSaturation(color) {
495
+ const parsed = parseHslTriplet(color);
496
+ return parsed ? `${parsed.h} ${parsed.s}%` : null;
497
+ }
498
+ /**
499
+ * Consumer-configurable brand color. Writes `--brand`, `--brand-foreground`, the
500
+ * derived `--brand-pattern-hs` hue/saturation source, and a `--brand-ring-light`
501
+ * / `--brand-ring-dark` pair as inline custom properties on `<html>` so solid
502
+ * brand utilities, the focus ring, and the soft Layout pattern tiers can resolve
503
+ * without CSS relative-color syntax.
504
+ *
505
+ * Default comes from `provideUiTheme({ brand })`; `setBrand()` overrides at
506
+ * runtime and persists. When unset, `--brand` falls back to `var(--primary)`.
507
+ */
508
+ class ThemeBrandService {
509
+ options = inject(THEME_OPTIONS, { optional: true });
510
+ /**
511
+ * Storage half shared with the other axes; the DOM half is custom, because the
512
+ * brand paints a set of inline custom properties rather than setting an
513
+ * attribute. `serialize` returning `null` is what clears the stored key.
514
+ */
515
+ brandState = persistedSignal({
516
+ key: THEME_BRAND_STORAGE_KEY,
517
+ parse: (raw) => this.parseStored(raw) ?? this.normalizeOption(this.options?.brand),
518
+ serialize: (brand) => (brand ? JSON.stringify(brand) : null),
519
+ });
520
+ brand = this.brandState.asReadonly();
521
+ constructor() {
522
+ htmlStylePropertyEffect({ source: () => this.brandProperties(this.brandState()) });
523
+ }
524
+ /**
525
+ * Set the brand color. `color`/`foreground` are HSL triplets (e.g.
526
+ * `'221 83% 53%'`); a hex color is converted, since that is what a native
527
+ * picker hands back. The fill is kept exactly as chosen — `--brand-foreground`
528
+ * is moved until text on it clears 4.5 to 1, and the focus ring is solved
529
+ * separately per mode, so a pale brand can never leave the ring invisible.
530
+ */
531
+ setBrand(color, foreground = DEFAULT_BRAND_FOREGROUND) {
532
+ this.brandState.set(hardenBrand({ color, foreground }));
533
+ }
534
+ /** Clear the consumer brand → `--brand` falls back to `var(--primary)`. */
535
+ clearBrand() {
536
+ this.brandState.set(null);
537
+ }
538
+ normalizeOption(brand) {
539
+ if (!brand) {
540
+ return null;
541
+ }
542
+ if (typeof brand === 'string') {
543
+ return hardenBrand({ color: brand, foreground: DEFAULT_BRAND_FOREGROUND });
544
+ }
545
+ return hardenBrand({
546
+ color: brand.color,
547
+ foreground: brand.foreground ?? DEFAULT_BRAND_FOREGROUND,
548
+ });
549
+ }
550
+ /** The custom properties the brand paints. `null` removes one. */
551
+ brandProperties(brand) {
552
+ if (!brand) {
553
+ return {
554
+ '--brand': null,
555
+ '--brand-foreground': null,
556
+ '--brand-pattern-hs': null,
557
+ '--brand-ring-light': null,
558
+ '--brand-ring-dark': null,
559
+ };
560
+ }
561
+ return {
562
+ '--brand': brand.color,
563
+ '--brand-foreground': brand.foreground,
564
+ '--brand-pattern-hs': brandPatternHueSaturation(brand.color),
565
+ '--brand-ring-light': brandRing(brand.color, RING_BACKGROUNDS.light),
566
+ '--brand-ring-dark': brandRing(brand.color, RING_BACKGROUNDS.dark),
567
+ };
568
+ }
569
+ /** Stored brands are JSON. Anything unparsable reads as "no stored brand". */
570
+ parseStored(raw) {
571
+ if (!raw) {
572
+ return null;
573
+ }
574
+ try {
575
+ const parsed = JSON.parse(raw);
576
+ if (typeof parsed?.color !== 'string') {
577
+ return null;
578
+ }
579
+ // Storage is editable by hand and predates the guarantee, so a restored
580
+ // brand is measured exactly like a freshly set one.
581
+ return hardenBrand({
582
+ color: parsed.color,
583
+ foreground: typeof parsed.foreground === 'string' ? parsed.foreground : DEFAULT_BRAND_FOREGROUND,
584
+ });
585
+ }
586
+ catch {
587
+ return null;
588
+ }
589
+ }
590
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeBrandService, deps: [], target: i0.ɵɵFactoryTarget.Service });
591
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.4", ngImport: i0, type: ThemeBrandService });
592
+ }
593
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeBrandService, decorators: [{
594
+ type: Service
595
+ }], ctorParameters: () => [] });
596
+
597
+ /**
598
+ * Drives the two FluxUI-style color axes:
599
+ * - accent → `<html theme-color>` (full tinted palette per color)
600
+ * - neutral → `<html theme-neutral>` (gray family + Layout pattern hue)
601
+ *
602
+ * Both persist to localStorage and seed from `provideUiTheme({ color, neutral })`
603
+ * (a persisted choice wins over the configured default). `base` on either axis
604
+ * means "no override" — the core base theme applies.
605
+ */
606
+ class ThemeColorService {
607
+ options = inject(THEME_OPTIONS, { optional: true });
608
+ colorAxis = persistedHtmlAttributeSignal({
609
+ key: THEME_COLOR_STORAGE_KEY,
610
+ attribute: 'theme-color',
611
+ normalize: normalizeThemeColor,
612
+ // A configured brand with no explicit accent defaults the accent to 'brand'
613
+ // so the consumer's brand color becomes the primary (FluxUI-style). The
614
+ // helper knows nothing about brand, so this stays here.
615
+ initial: this.options?.color ?? (this.options?.brand ? 'brand' : undefined),
616
+ });
617
+ neutralAxis = persistedHtmlAttributeSignal({
618
+ key: THEME_NEUTRAL_STORAGE_KEY,
619
+ attribute: 'theme-neutral',
620
+ normalize: normalizeThemeNeutral,
621
+ initial: this.options?.neutral,
622
+ });
623
+ color = this.colorAxis.value;
624
+ neutral = this.neutralAxis.value;
625
+ setColor(color) {
626
+ this.colorAxis.set(color);
627
+ }
628
+ setNeutral(neutral) {
629
+ this.neutralAxis.set(neutral);
630
+ }
631
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeColorService, deps: [], target: i0.ɵɵFactoryTarget.Service });
632
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.4", ngImport: i0, type: ThemeColorService });
633
+ }
634
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeColorService, decorators: [{
635
+ type: Service
636
+ }] });
637
+
638
+ /**
639
+ * Color mode axis. Writes both `<html theme-mode>` and `<html data-mode>`.
640
+ *
641
+ * The persisted value and the written value differ here, which is the whole
642
+ * point of the axis: `'system'` is what gets stored, while `light` or `dark` is
643
+ * what reaches the DOM, and it keeps following the operating system live.
644
+ */
645
+ class ThemeModeService {
646
+ options = inject(THEME_OPTIONS, { optional: true });
647
+ systemPrefersDark = prefersDarkSignal();
648
+ axis = persistedHtmlAttributeSignal({
649
+ key: THEME_MODE_STORAGE_KEY,
650
+ // One value, two attributes: `theme-mode` for the theme CSS and `data-mode`
651
+ // for the Tailwind dark variant.
652
+ attribute: ['theme-mode', 'data-mode'],
653
+ normalize: normalizeThemeMode,
654
+ initial: this.options?.mode,
655
+ // Read inside the effect, so a system preference change repaints without
656
+ // touching what is persisted.
657
+ resolveAttributeValue: (mode) => mode === 'system' ? (this.systemPrefersDark() ? 'dark' : 'light') : mode,
658
+ });
659
+ mode = this.axis.value;
660
+ resolvedMode = computed(() => this.axis.attributeValue(), /* @ts-ignore */
661
+ ...(ngDevMode ? [{ debugName: "resolvedMode" }] : /* istanbul ignore next */ []));
662
+ isDark = computed(() => this.resolvedMode() === 'dark', /* @ts-ignore */
663
+ ...(ngDevMode ? [{ debugName: "isDark" }] : /* istanbul ignore next */ []));
664
+ setMode(mode) {
665
+ this.axis.set(mode);
666
+ }
667
+ toggle() {
668
+ this.setMode(this.isDark() ? 'light' : 'dark');
669
+ }
670
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeModeService, deps: [], target: i0.ɵɵFactoryTarget.Service });
671
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.4", ngImport: i0, type: ThemeModeService });
672
+ }
673
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeModeService, decorators: [{
674
+ type: Service
675
+ }] });
676
+
677
+ /** Radius in px @ 16px root; `full` uses a finite SVG-friendly sentinel. */
678
+ const RADIUS_PX = {
679
+ none: 0,
680
+ xs: 2,
681
+ sm: 4,
682
+ md: 6,
683
+ xl: 12,
684
+ full: 9999,
685
+ };
686
+ /**
687
+ * Corner radius axis. Writes `<html theme-radius>`; each preset CSS sets the
688
+ * single `--radius-base` knob so the whole `--radius-*` scale + `rounded-*`
689
+ * utilities rescale. Persists to localStorage and seeds from
690
+ * `provideUiTheme({ radius })` (a persisted choice wins over the default).
691
+ */
692
+ class ThemeRadiusService {
693
+ options = inject(THEME_OPTIONS, { optional: true });
694
+ axis = persistedHtmlAttributeSignal({
695
+ key: THEME_RADIUS_STORAGE_KEY,
696
+ attribute: 'theme-radius',
697
+ normalize: normalizeThemeRadius,
698
+ initial: this.options?.radius,
699
+ });
700
+ radius = this.axis.value;
701
+ /** Active preset in px @ 16px root. */
702
+ radiusPx = computed(() => RADIUS_PX[this.radius()] ?? RADIUS_PX.md, /* @ts-ignore */
703
+ ...(ngDevMode ? [{ debugName: "radiusPx" }] : /* istanbul ignore next */ []));
704
+ setRadius(radius) {
705
+ this.axis.set(radius);
706
+ }
707
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeRadiusService, deps: [], target: i0.ɵɵFactoryTarget.Service });
708
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.4", ngImport: i0, type: ThemeRadiusService });
709
+ }
710
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeRadiusService, decorators: [{
711
+ type: Service
712
+ }] });
713
+
714
+ /**
715
+ * Spacing density axis. Writes `<html theme-space>`; each preset CSS sets the
716
+ * single `--spacing-base` knob, which Tailwind's `--spacing` derives from, so
717
+ * every `p-*`/`m-*`/`gap-*`/`w-*`/`h-*` utility rescales. Persists to
718
+ * localStorage and seeds from `provideUiTheme({ space })` (a persisted choice
719
+ * wins over the default).
720
+ */
721
+ class ThemeSpaceService {
722
+ options = inject(THEME_OPTIONS, { optional: true });
723
+ axis = persistedHtmlAttributeSignal({
724
+ key: THEME_SPACE_STORAGE_KEY,
725
+ attribute: 'theme-space',
726
+ normalize: normalizeThemeSpace,
727
+ initial: this.options?.space,
728
+ });
729
+ space = this.axis.value;
730
+ setSpace(space) {
731
+ this.axis.set(space);
732
+ }
733
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSpaceService, deps: [], target: i0.ɵɵFactoryTarget.Service });
734
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.4", ngImport: i0, type: ThemeSpaceService });
735
+ }
736
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: ThemeSpaceService, decorators: [{
737
+ type: Service
738
+ }] });
739
+
740
+ const MATERIAL_SYMBOLS_FONT_ATTR = 'data-icon-font';
741
+ const MATERIAL_SYMBOLS_FONT_ID = 'material-symbols';
742
+ const MATERIAL_SYMBOLS_FONT_HREF = 'https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&family=Material+Symbols+Sharp:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=block';
743
+ function provideUiTheme(options = {}) {
744
+ return makeEnvironmentProviders([
745
+ {
746
+ provide: THEME_OPTIONS,
747
+ useValue: options,
748
+ },
749
+ provideEnvironmentInitializer(() => {
750
+ // Opt-in: the Material Symbols font is only fetched from Google Fonts when
751
+ // explicitly enabled, so the default install makes no external network
752
+ // request (privacy / offline / CSP friendly). Set `icons.materialSymbols: true`
753
+ // or self-host the font and import it yourself.
754
+ if (options.icons?.materialSymbols === true) {
755
+ loadMaterialSymbolsStylesheet();
756
+ }
757
+ inject(ThemeModeService);
758
+ inject(ThemeColorService);
759
+ inject(ThemeBrandService);
760
+ inject(ThemeRadiusService);
761
+ inject(ThemeSpaceService);
762
+ }),
763
+ ]);
764
+ }
765
+ function loadMaterialSymbolsStylesheet() {
766
+ const documentRef = inject(DOCUMENT, { optional: true });
767
+ const platformId = inject(PLATFORM_ID);
768
+ const head = documentRef?.head;
769
+ if (!isPlatformBrowser(platformId) || !head) {
770
+ return;
771
+ }
772
+ const existing = head.querySelector(`link[${MATERIAL_SYMBOLS_FONT_ATTR}="${MATERIAL_SYMBOLS_FONT_ID}"]`);
773
+ if (existing) {
774
+ return;
775
+ }
776
+ const link = head.ownerDocument.createElement('link');
777
+ link.rel = 'stylesheet';
778
+ link.href = MATERIAL_SYMBOLS_FONT_HREF;
779
+ link.setAttribute(MATERIAL_SYMBOLS_FONT_ATTR, MATERIAL_SYMBOLS_FONT_ID);
780
+ head.appendChild(link);
781
+ }
4
782
 
5
783
  /**
6
784
  * Generated bundle index. Do not edit.
7
- */
785
+ */
786
+
787
+ export { DEFAULT_THEME_COLOR, DEFAULT_THEME_MODE, DEFAULT_THEME_NEUTRAL, DEFAULT_THEME_RADIUS, DEFAULT_THEME_SPACE, RESOLVED_THEME_MODES, THEME_BRAND_STORAGE_KEY, THEME_COLORS, THEME_COLOR_STORAGE_KEY, THEME_MODES, THEME_MODE_STORAGE_KEY, THEME_NEUTRALS, THEME_NEUTRAL_STORAGE_KEY, THEME_OPTIONS, THEME_RADII, THEME_RADIUS_STORAGE_KEY, THEME_SPACES, THEME_SPACE_STORAGE_KEY, ThemeBrandService, ThemeColorService, ThemeModeService, ThemeRadiusService, ThemeSpaceService, htmlAttributeEffect, htmlStylePropertyEffect, isResolvedThemeMode, isThemeColor, isThemeMode, isThemeNeutral, isThemeRadius, isThemeSpace, normalizeThemeColor, normalizeThemeMode, normalizeThemeNeutral, normalizeThemeRadius, normalizeThemeSpace, persistedHtmlAttributeSignal, persistedSignal, prefersDarkSignal, provideUiTheme };