@ojiepermana/angular 22.1.14 → 22.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/SUPPORT.md +3 -3
- package/chart/README.md +1 -1
- package/component/README.md +2 -2
- package/component/calendar/README.md +3 -15
- package/component/drawer/README.md +1 -2
- package/fesm2022/ojiepermana-angular-chart-area.mjs +352 -137
- package/fesm2022/ojiepermana-angular-chart-bar.mjs +744 -524
- package/fesm2022/ojiepermana-angular-chart-core.mjs +1140 -1344
- package/fesm2022/ojiepermana-angular-chart-line.mjs +393 -147
- package/fesm2022/ojiepermana-angular-chart-pie.mjs +408 -179
- package/fesm2022/ojiepermana-angular-chart-primitives.mjs +1262 -920
- package/fesm2022/ojiepermana-angular-chart-radar.mjs +473 -205
- package/fesm2022/ojiepermana-angular-chart-radial.mjs +425 -182
- package/fesm2022/ojiepermana-angular-chart-scatter.mjs +386 -210
- package/fesm2022/ojiepermana-angular-chart.mjs +3 -19
- package/fesm2022/ojiepermana-angular-component-accordion.mjs +485 -243
- package/fesm2022/ojiepermana-angular-component-alert-dialog.mjs +963 -325
- package/fesm2022/ojiepermana-angular-component-alert.mjs +266 -103
- package/fesm2022/ojiepermana-angular-component-aspect-ratio.mjs +80 -35
- package/fesm2022/ojiepermana-angular-component-avatar.mjs +402 -135
- package/fesm2022/ojiepermana-angular-component-badge.mjs +109 -59
- package/fesm2022/ojiepermana-angular-component-breadcrumb.mjs +410 -142
- package/fesm2022/ojiepermana-angular-component-button-group.mjs +249 -96
- package/fesm2022/ojiepermana-angular-component-button.mjs +153 -75
- package/fesm2022/ojiepermana-angular-component-calendar.mjs +531 -418
- package/fesm2022/ojiepermana-angular-component-card.mjs +457 -171
- package/fesm2022/ojiepermana-angular-component-carousel.mjs +642 -272
- package/fesm2022/ojiepermana-angular-component-checkbox.mjs +312 -108
- package/fesm2022/ojiepermana-angular-component-collapsible.mjs +299 -115
- package/fesm2022/ojiepermana-angular-component-combobox.mjs +396 -183
- package/fesm2022/ojiepermana-angular-component-command.mjs +807 -347
- package/fesm2022/ojiepermana-angular-component-composer.mjs +807 -330
- package/fesm2022/ojiepermana-angular-component-context-menu.mjs +196 -161
- package/fesm2022/ojiepermana-angular-component-date-picker.mjs +650 -338
- package/fesm2022/ojiepermana-angular-component-dialog.mjs +710 -316
- package/fesm2022/ojiepermana-angular-component-drawer.mjs +2 -5
- package/fesm2022/ojiepermana-angular-component-dropdown-menu.mjs +1212 -646
- package/fesm2022/ojiepermana-angular-component-editor.mjs +987 -502
- package/fesm2022/ojiepermana-angular-component-empty.mjs +350 -137
- package/fesm2022/ojiepermana-angular-component-form.mjs +813 -434
- package/fesm2022/ojiepermana-angular-component-hover-card.mjs +486 -277
- package/fesm2022/ojiepermana-angular-component-icon.mjs +241 -140
- package/fesm2022/ojiepermana-angular-component-input-group.mjs +449 -176
- package/fesm2022/ojiepermana-angular-component-input-otp.mjs +891 -488
- package/fesm2022/ojiepermana-angular-component-input.mjs +86 -55
- package/fesm2022/ojiepermana-angular-component-item.mjs +713 -261
- package/fesm2022/ojiepermana-angular-component-kanban.mjs +714 -216
- package/fesm2022/ojiepermana-angular-component-kbd.mjs +124 -52
- package/fesm2022/ojiepermana-angular-component-label.mjs +82 -31
- package/fesm2022/ojiepermana-angular-component-menubar.mjs +479 -329
- package/fesm2022/ojiepermana-angular-component-native-select.mjs +167 -65
- package/fesm2022/ojiepermana-angular-component-navigation-menu.mjs +683 -380
- package/fesm2022/ojiepermana-angular-component-pagination.mjs +277 -94
- package/fesm2022/ojiepermana-angular-component-pillbox.mjs +1283 -607
- package/fesm2022/ojiepermana-angular-component-popover.mjs +291 -204
- package/fesm2022/ojiepermana-angular-component-progress.mjs +176 -53
- package/fesm2022/ojiepermana-angular-component-radio.mjs +449 -142
- package/fesm2022/ojiepermana-angular-component-resizable.mjs +659 -453
- package/fesm2022/ojiepermana-angular-component-scroll-area.mjs +117 -39
- package/fesm2022/ojiepermana-angular-component-segmented-control.mjs +351 -172
- package/fesm2022/ojiepermana-angular-component-select.mjs +475 -345
- package/fesm2022/ojiepermana-angular-component-separator.mjs +102 -35
- package/fesm2022/ojiepermana-angular-component-sheet.mjs +797 -347
- package/fesm2022/ojiepermana-angular-component-skeleton.mjs +62 -29
- package/fesm2022/ojiepermana-angular-component-slider.mjs +592 -376
- package/fesm2022/ojiepermana-angular-component-spinner.mjs +135 -38
- package/fesm2022/ojiepermana-angular-component-switch.mjs +315 -103
- package/fesm2022/ojiepermana-angular-component-table.mjs +440 -153
- package/fesm2022/ojiepermana-angular-component-tabs.mjs +590 -317
- package/fesm2022/ojiepermana-angular-component-textarea.mjs +85 -50
- package/fesm2022/ojiepermana-angular-component-timeline.mjs +609 -231
- package/fesm2022/ojiepermana-angular-component-toast.mjs +229 -243
- package/fesm2022/ojiepermana-angular-component-toggle-group.mjs +573 -288
- package/fesm2022/ojiepermana-angular-component-toggle.mjs +187 -78
- package/fesm2022/ojiepermana-angular-component-tooltip.mjs +798 -517
- package/fesm2022/ojiepermana-angular-component-utils-core.mjs +159 -292
- package/fesm2022/ojiepermana-angular-component-utils-overlay.mjs +46 -58
- package/fesm2022/ojiepermana-angular-component-utils-scale.mjs +76 -38
- package/fesm2022/ojiepermana-angular-component-utils.mjs +2 -8
- package/fesm2022/ojiepermana-angular-component.mjs +0 -10
- package/fesm2022/ojiepermana-angular-navigation-core.mjs +13 -23
- package/fesm2022/ojiepermana-angular-navigation-icon.mjs +35 -24
- package/fesm2022/ojiepermana-angular-navigation-navigation-badge-count.component-BKCeWzml.mjs +129 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-dockbar-menu.component-DFEJK2jD.mjs +811 -0
- package/fesm2022/{ojiepermana-angular-navigation-navigation-entry-grid.component-COQ-60DS.mjs → ojiepermana-angular-navigation-navigation-entry-grid.component-mpK9Ar08.mjs} +387 -183
- package/fesm2022/ojiepermana-angular-navigation-navigation-flyout-menu.component-BPgH7FJk.mjs +650 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-horizontal.component-DMpYcD9t.mjs +662 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-item.component-Basp5SIk.mjs +1228 -0
- package/fesm2022/ojiepermana-angular-navigation-navigation-list.component-YUzHI9Pj.mjs +311 -0
- package/fesm2022/ojiepermana-angular-navigation-service.mjs +542 -724
- package/fesm2022/ojiepermana-angular-navigation-types.mjs +27 -56
- package/fesm2022/ojiepermana-angular-navigation.mjs +3052 -4
- package/fesm2022/ojiepermana-angular-sdk.mjs +91 -201
- package/fesm2022/ojiepermana-angular-theme-component-settings.mjs +1115 -737
- package/fesm2022/ojiepermana-angular-theme-layout-services.mjs +245 -328
- package/fesm2022/ojiepermana-angular-theme-layout-types.mjs +35 -38
- package/fesm2022/ojiepermana-angular-theme-layout-wrapper.mjs +1719 -552
- package/fesm2022/ojiepermana-angular-theme-layout.mjs +847 -496
- package/fesm2022/ojiepermana-angular-theme-page-core.mjs +288 -358
- package/fesm2022/ojiepermana-angular-theme-page-filter.mjs +362 -224
- package/fesm2022/ojiepermana-angular-theme-page-overview.mjs +88 -0
- package/fesm2022/ojiepermana-angular-theme-page-root.mjs +576 -254
- package/fesm2022/ojiepermana-angular-theme-page-side.mjs +317 -177
- package/fesm2022/ojiepermana-angular-theme-page-slots.mjs +256 -112
- package/fesm2022/ojiepermana-angular-theme-page.mjs +7 -12
- package/fesm2022/ojiepermana-angular-theme-shell-services.mjs +215 -326
- package/fesm2022/ojiepermana-angular-theme-shell-types.mjs +68 -129
- package/fesm2022/ojiepermana-angular-theme-shell.mjs +907 -378
- package/fesm2022/ojiepermana-angular-theme-styles.mjs +521 -720
- package/fesm2022/ojiepermana-angular-theme.mjs +0 -10
- package/fesm2022/ojiepermana-angular.mjs +5 -24
- package/navigation/README.md +1 -1
- package/package.json +16 -12
- package/sdk/README.md +1 -1
- package/sdk/src/writer/index.js +2 -2
- package/theme/README.md +1 -1
- package/theme/layout/README.md +2 -4
- package/theme/page/README.md +50 -0
- package/theme/styles/README.md +10 -10
- package/theme/styles/css/base/prefers-dark.css +1 -1
- package/theme/styles/css/base/status.css +1 -1
- package/theme/styles/css/color/amber.css +1 -1
- package/theme/styles/css/color/blue.css +1 -1
- package/theme/styles/css/color/cyan.css +1 -1
- package/theme/styles/css/color/emerald.css +1 -1
- package/theme/styles/css/color/fuchsia.css +1 -1
- package/theme/styles/css/color/green.css +1 -1
- package/theme/styles/css/color/indigo.css +1 -1
- package/theme/styles/css/color/lime.css +1 -1
- package/theme/styles/css/color/orange.css +1 -1
- package/theme/styles/css/color/pink.css +1 -1
- package/theme/styles/css/color/purple.css +1 -1
- package/theme/styles/css/color/red.css +1 -1
- package/theme/styles/css/color/rose.css +1 -1
- package/theme/styles/css/color/sky.css +1 -1
- package/theme/styles/css/color/teal.css +1 -1
- package/theme/styles/css/color/violet.css +1 -1
- package/theme/styles/css/color/yellow.css +1 -1
- package/theme/styles/css/neutral/gray.css +1 -1
- package/theme/styles/css/neutral/mauve.css +1 -1
- package/theme/styles/css/neutral/mist.css +1 -1
- package/theme/styles/css/neutral/neutral.css +1 -1
- package/theme/styles/css/neutral/olive.css +1 -1
- package/theme/styles/css/neutral/slate.css +1 -1
- package/theme/styles/css/neutral/stone.css +1 -1
- package/theme/styles/css/neutral/taupe.css +1 -1
- package/theme/styles/css/neutral/zinc.css +1 -1
- package/types/ojiepermana-angular-chart-area.d.ts +126 -63
- package/types/ojiepermana-angular-chart-bar.d.ts +244 -154
- package/types/ojiepermana-angular-chart-core.d.ts +409 -398
- package/types/ojiepermana-angular-chart-line.d.ts +141 -68
- package/types/ojiepermana-angular-chart-pie.d.ts +192 -121
- package/types/ojiepermana-angular-chart-primitives.d.ts +283 -214
- package/types/ojiepermana-angular-chart-radar.d.ts +189 -108
- package/types/ojiepermana-angular-chart-radial.d.ts +177 -106
- package/types/ojiepermana-angular-chart-scatter.d.ts +166 -110
- package/types/ojiepermana-angular-chart.d.ts +3 -2
- package/types/ojiepermana-angular-component-accordion.d.ts +135 -81
- package/types/ojiepermana-angular-component-alert-dialog.d.ts +271 -123
- package/types/ojiepermana-angular-component-alert.d.ts +69 -39
- package/types/ojiepermana-angular-component-aspect-ratio.d.ts +20 -12
- package/types/ojiepermana-angular-component-avatar.d.ts +103 -49
- package/types/ojiepermana-angular-component-badge.d.ts +36 -24
- package/types/ojiepermana-angular-component-breadcrumb.d.ts +79 -40
- package/types/ojiepermana-angular-component-button-group.d.ts +48 -24
- package/types/ojiepermana-angular-component-button.d.ts +44 -27
- package/types/ojiepermana-angular-component-calendar.d.ts +145 -107
- package/types/ojiepermana-angular-component-card.d.ts +112 -63
- package/types/ojiepermana-angular-component-carousel.d.ts +150 -81
- package/types/ojiepermana-angular-component-checkbox.d.ts +96 -38
- package/types/ojiepermana-angular-component-collapsible.d.ts +65 -39
- package/types/ojiepermana-angular-component-combobox.d.ts +100 -55
- package/types/ojiepermana-angular-component-command.d.ts +206 -121
- package/types/ojiepermana-angular-component-composer.d.ts +184 -84
- package/types/ojiepermana-angular-component-context-menu.d.ts +51 -41
- package/types/ojiepermana-angular-component-date-picker.d.ts +199 -109
- package/types/ojiepermana-angular-component-dialog.d.ts +200 -105
- package/types/ojiepermana-angular-component-drawer.d.ts +2 -1
- package/types/ojiepermana-angular-component-dropdown-menu.d.ts +324 -202
- package/types/ojiepermana-angular-component-editor.d.ts +215 -117
- package/types/ojiepermana-angular-component-empty.d.ts +80 -45
- package/types/ojiepermana-angular-component-form.d.ts +185 -121
- package/types/ojiepermana-angular-component-hover-card.d.ts +130 -72
- package/types/ojiepermana-angular-component-icon.d.ts +87 -57
- package/types/ojiepermana-angular-component-input-group.d.ts +93 -46
- package/types/ojiepermana-angular-component-input-otp.d.ts +243 -150
- package/types/ojiepermana-angular-component-input.d.ts +27 -17
- package/types/ojiepermana-angular-component-item.d.ts +160 -84
- package/types/ojiepermana-angular-component-kanban.d.ts +157 -62
- package/types/ojiepermana-angular-component-kbd.d.ts +24 -15
- package/types/ojiepermana-angular-component-label.d.ts +19 -11
- package/types/ojiepermana-angular-component-menubar.d.ts +120 -90
- package/types/ojiepermana-angular-component-native-select.d.ts +37 -24
- package/types/ojiepermana-angular-component-navigation-menu.d.ts +157 -98
- package/types/ojiepermana-angular-component-pagination.d.ts +78 -28
- package/types/ojiepermana-angular-component-pillbox.d.ts +361 -204
- package/types/ojiepermana-angular-component-popover.d.ts +94 -64
- package/types/ojiepermana-angular-component-progress.d.ts +45 -17
- package/types/ojiepermana-angular-component-radio.d.ts +129 -53
- package/types/ojiepermana-angular-component-resizable.d.ts +160 -112
- package/types/ojiepermana-angular-component-scroll-area.d.ts +33 -15
- package/types/ojiepermana-angular-component-segmented-control.d.ts +94 -50
- package/types/ojiepermana-angular-component-select.d.ts +94 -77
- package/types/ojiepermana-angular-component-separator.d.ts +26 -14
- package/types/ojiepermana-angular-component-sheet.d.ts +226 -116
- package/types/ojiepermana-angular-component-skeleton.d.ts +13 -10
- package/types/ojiepermana-angular-component-slider.d.ts +140 -75
- package/types/ojiepermana-angular-component-spinner.d.ts +31 -13
- package/types/ojiepermana-angular-component-switch.d.ts +97 -40
- package/types/ojiepermana-angular-component-table.d.ts +102 -50
- package/types/ojiepermana-angular-component-tabs.d.ts +174 -115
- package/types/ojiepermana-angular-component-textarea.d.ts +27 -17
- package/types/ojiepermana-angular-component-timeline.d.ts +141 -59
- package/types/ojiepermana-angular-component-toast.d.ts +51 -55
- package/types/ojiepermana-angular-component-toggle-group.d.ts +159 -88
- package/types/ojiepermana-angular-component-toggle.d.ts +47 -25
- package/types/ojiepermana-angular-component-tooltip.d.ts +222 -132
- package/types/ojiepermana-angular-component-utils-core.d.ts +81 -90
- package/types/ojiepermana-angular-component-utils-overlay.d.ts +7 -11
- package/types/ojiepermana-angular-component-utils-scale.d.ts +20 -12
- package/types/ojiepermana-angular-component-utils.d.ts +2 -2
- package/types/ojiepermana-angular-component.d.ts +1 -2
- package/types/ojiepermana-angular-navigation-core.d.ts +46 -50
- package/types/ojiepermana-angular-navigation-icon.d.ts +9 -12
- package/types/ojiepermana-angular-navigation-service.d.ts +112 -115
- package/types/ojiepermana-angular-navigation-types.d.ts +121 -125
- package/types/ojiepermana-angular-navigation.d.ts +663 -374
- package/types/ojiepermana-angular-sdk.d.ts +69 -71
- package/types/ojiepermana-angular-theme-component-settings.d.ts +115 -83
- package/types/ojiepermana-angular-theme-layout-services.d.ts +119 -124
- package/types/ojiepermana-angular-theme-layout-types.d.ts +27 -31
- package/types/ojiepermana-angular-theme-layout-wrapper.d.ts +399 -187
- package/types/ojiepermana-angular-theme-layout.d.ts +201 -113
- package/types/ojiepermana-angular-theme-page-core.d.ts +136 -142
- package/types/ojiepermana-angular-theme-page-filter.d.ts +112 -62
- package/types/ojiepermana-angular-theme-page-overview.d.ts +28 -0
- package/types/ojiepermana-angular-theme-page-root.d.ts +172 -88
- package/types/ojiepermana-angular-theme-page-side.d.ts +89 -49
- package/types/ojiepermana-angular-theme-page-slots.d.ts +58 -37
- package/types/ojiepermana-angular-theme-page.d.ts +7 -5
- package/types/ojiepermana-angular-theme-shell-services.d.ts +119 -125
- package/types/ojiepermana-angular-theme-shell-types.d.ts +38 -41
- package/types/ojiepermana-angular-theme-shell.d.ts +139 -61
- package/types/ojiepermana-angular-theme-styles.d.ts +184 -196
- package/types/ojiepermana-angular-theme.d.ts +1 -2
- package/types/ojiepermana-angular.d.ts +5 -5
- package/fesm2022/ojiepermana-angular-navigation-navigation-badge-count.component-Be3IqzG4.mjs +0 -91
- package/fesm2022/ojiepermana-angular-navigation-navigation-dockbar-menu.component-Bc7DWK3R.mjs +0 -522
- package/fesm2022/ojiepermana-angular-navigation-navigation-flyout-menu.component-DA8VPEn7.mjs +0 -404
- package/fesm2022/ojiepermana-angular-navigation-navigation-horizontal.component-U2TJTzlW.mjs +0 -466
- package/fesm2022/ojiepermana-angular-navigation-ojiepermana-angular-navigation-CIUhmytH.mjs +0 -2668
|
@@ -1,787 +1,588 @@
|
|
|
1
|
-
import { DOCUMENT, isPlatformBrowser } from
|
|
2
|
-
import * as i0 from
|
|
3
|
-
import {
|
|
4
|
-
import { documentLocalStorage } from
|
|
5
|
-
|
|
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
|
-
*/
|
|
1
|
+
import { DOCUMENT, isPlatformBrowser } from "@angular/common";
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
import { DestroyRef, InjectionToken, PLATFORM_ID, Service, computed, effect, inject, makeEnvironmentProviders, provideEnvironmentInitializer, signal } from "@angular/core";
|
|
4
|
+
import { documentLocalStorage } from "@ojiepermana/angular/component/utils/core";
|
|
33
5
|
function persistedSignal(options) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
else {
|
|
44
|
-
storage.write(options.key, serialized);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
return state;
|
|
6
|
+
const documentRef = options.documentRef !== void 0 ? options.documentRef : inject(DOCUMENT, { optional: true });
|
|
7
|
+
const storage = documentLocalStorage(documentRef);
|
|
8
|
+
const state = signal(options.parse(storage.read(options.key)), ...ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []);
|
|
9
|
+
effect(() => {
|
|
10
|
+
const serialized = options.serialize(state());
|
|
11
|
+
if (serialized === null) storage.remove(options.key);
|
|
12
|
+
else storage.write(options.key, serialized);
|
|
13
|
+
});
|
|
14
|
+
return state;
|
|
48
15
|
}
|
|
49
|
-
/**
|
|
50
|
-
* Mirror a reactive string onto one or more `<html>` attributes. A `null` value
|
|
51
|
-
* removes them.
|
|
52
|
-
*/
|
|
53
16
|
function htmlAttributeEffect(options) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
element.removeAttribute(attribute);
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
element.setAttribute(attribute, value);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
});
|
|
17
|
+
const documentRef = options.documentRef !== void 0 ? options.documentRef : inject(DOCUMENT, { optional: true });
|
|
18
|
+
const attributes = typeof options.attribute === "string" ? [options.attribute] : options.attribute;
|
|
19
|
+
effect(() => {
|
|
20
|
+
const value = options.source();
|
|
21
|
+
const element = documentRef?.documentElement;
|
|
22
|
+
if (!element) return;
|
|
23
|
+
for (const attribute of attributes) if (value === null) element.removeAttribute(attribute);
|
|
24
|
+
else element.setAttribute(attribute, value);
|
|
25
|
+
});
|
|
70
26
|
}
|
|
71
|
-
/** Mirror reactive inline custom properties onto `<html>`. A `null` value removes one. */
|
|
72
27
|
function htmlStylePropertyEffect(options) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
element.style.removeProperty(property);
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
element.style.setProperty(property, value);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
});
|
|
28
|
+
const documentRef = options.documentRef !== void 0 ? options.documentRef : inject(DOCUMENT, { optional: true });
|
|
29
|
+
effect(() => {
|
|
30
|
+
const properties = options.source();
|
|
31
|
+
const element = documentRef?.documentElement;
|
|
32
|
+
if (!element) return;
|
|
33
|
+
for (const [property, value] of Object.entries(properties)) if (value === null) element.style.removeProperty(property);
|
|
34
|
+
else element.style.setProperty(property, value);
|
|
35
|
+
});
|
|
88
36
|
}
|
|
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
37
|
function persistedHtmlAttributeSignal(options) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
};
|
|
38
|
+
const documentRef = options.documentRef !== void 0 ? options.documentRef : inject(DOCUMENT, { optional: true });
|
|
39
|
+
const state = persistedSignal({
|
|
40
|
+
key: options.key,
|
|
41
|
+
documentRef,
|
|
42
|
+
parse: (raw) => {
|
|
43
|
+
const stored = raw !== null && raw !== "" ? raw : null;
|
|
44
|
+
return options.normalize(stored ?? options.normalize(options.initial));
|
|
45
|
+
},
|
|
46
|
+
serialize: (value) => value
|
|
47
|
+
});
|
|
48
|
+
const resolveAttributeValue = options.resolveAttributeValue;
|
|
49
|
+
const attributeValue = resolveAttributeValue ? computed(() => resolveAttributeValue(state())) : computed(() => state());
|
|
50
|
+
htmlAttributeEffect({
|
|
51
|
+
attribute: options.attribute,
|
|
52
|
+
source: () => attributeValue(),
|
|
53
|
+
documentRef
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
value: state.asReadonly(),
|
|
57
|
+
attributeValue,
|
|
58
|
+
set: (next) => state.set(next)
|
|
59
|
+
};
|
|
131
60
|
}
|
|
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
61
|
function prefersDarkSignal(documentRef) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
return state.asReadonly();
|
|
62
|
+
const mediaQueryList = (documentRef !== void 0 ? documentRef : inject(DOCUMENT, { optional: true }))?.defaultView?.matchMedia?.("(prefers-color-scheme: dark)") ?? null;
|
|
63
|
+
const state = signal(mediaQueryList?.matches ?? false, ...ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []);
|
|
64
|
+
if (mediaQueryList) {
|
|
65
|
+
const onChange = (event) => state.set(event.matches);
|
|
66
|
+
mediaQueryList.addEventListener("change", onChange);
|
|
67
|
+
inject(DestroyRef).onDestroy(() => mediaQueryList.removeEventListener("change", onChange));
|
|
68
|
+
}
|
|
69
|
+
return state.asReadonly();
|
|
151
70
|
}
|
|
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
71
|
const TEXT_CONTRAST_MIN = 4.5;
|
|
169
|
-
|
|
170
|
-
const
|
|
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');
|
|
72
|
+
const NUMBER = String.raw`[+-]?(?:\d+(?:\.\d+)?|\.\d+)`;
|
|
73
|
+
const SEPARATOR = String.raw`(?:\s*,\s*|\s+)`;
|
|
74
|
+
const HSL_TRIPLET = new RegExp(`^\\s*(${NUMBER})(deg|grad|rad|turn)?${SEPARATOR}(${NUMBER})%${SEPARATOR}(${NUMBER})%\\s*$`, "i");
|
|
183
75
|
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
76
|
const DEGREES_PER_UNIT = {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
77
|
+
deg: 1,
|
|
78
|
+
grad: .9,
|
|
79
|
+
rad: 180 / Math.PI,
|
|
80
|
+
turn: 360
|
|
190
81
|
};
|
|
191
82
|
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. */
|
|
83
|
+
const normalizeHue = (hue) => (hue % 360 + 360) % 360;
|
|
198
84
|
function parseHslTriplet(value) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
85
|
+
const match = HSL_TRIPLET.exec(value);
|
|
86
|
+
if (!match) return null;
|
|
87
|
+
const [h, s, l] = [
|
|
88
|
+
Number(match[1]),
|
|
89
|
+
Number(match[3]),
|
|
90
|
+
Number(match[4])
|
|
91
|
+
];
|
|
92
|
+
if (![
|
|
93
|
+
h,
|
|
94
|
+
s,
|
|
95
|
+
l
|
|
96
|
+
].every(Number.isFinite)) return null;
|
|
97
|
+
return {
|
|
98
|
+
h: normalizeHue(h * DEGREES_PER_UNIT[match[2]?.toLowerCase() ?? "deg"]),
|
|
99
|
+
s,
|
|
100
|
+
l
|
|
101
|
+
};
|
|
208
102
|
}
|
|
209
|
-
/** Format numbers back into the triplet form the CSS consumes. */
|
|
210
103
|
function formatHslTriplet({ h, s, l }) {
|
|
211
|
-
|
|
104
|
+
return `${h} ${clamp(Math.round(s), 0, 100)}% ${clamp(Math.round(l), 0, 100)}%`;
|
|
212
105
|
}
|
|
213
|
-
/** `#rgb` / `#rrggbb` as an HSL triplet. `null` when the input is not a hex color. */
|
|
214
106
|
function hexToHslTriplet(value) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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 });
|
|
107
|
+
const match = HEX_COLOR.exec(value.trim());
|
|
108
|
+
if (!match) return null;
|
|
109
|
+
const digits = match[1];
|
|
110
|
+
const expanded = digits.length === 3 ? digits.split("").map((digit) => digit + digit).join("") : digits;
|
|
111
|
+
const [red, green, blue] = [
|
|
112
|
+
0,
|
|
113
|
+
2,
|
|
114
|
+
4
|
|
115
|
+
].map((offset) => Number.parseInt(expanded.slice(offset, offset + 2), 16) / 255);
|
|
116
|
+
const largest = Math.max(red, green, blue);
|
|
117
|
+
const smallest = Math.min(red, green, blue);
|
|
118
|
+
const delta = largest - smallest;
|
|
119
|
+
const lightness = (largest + smallest) / 2;
|
|
120
|
+
let hue = 0;
|
|
121
|
+
if (delta !== 0) {
|
|
122
|
+
if (largest === red) hue = 60 * (((green - blue) / delta + 6) % 6);
|
|
123
|
+
else if (largest === green) hue = 60 * ((blue - red) / delta + 2);
|
|
124
|
+
else hue = 60 * ((red - green) / delta + 4);
|
|
125
|
+
}
|
|
126
|
+
const saturation = delta === 0 ? 0 : delta / (1 - Math.abs(2 * lightness - 1));
|
|
127
|
+
return formatHslTriplet({
|
|
128
|
+
h: Math.round(hue),
|
|
129
|
+
s: saturation * 100,
|
|
130
|
+
l: lightness * 100
|
|
131
|
+
});
|
|
242
132
|
}
|
|
243
|
-
/** HSL to sRGB, each channel in the 0..1 range (CSS Color 4 algorithm). */
|
|
244
133
|
function hslToRgb({ h, s, l }) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
134
|
+
const saturation = clamp(s, 0, 100) / 100;
|
|
135
|
+
const lightness = clamp(l, 0, 100) / 100;
|
|
136
|
+
const k = (n) => (n + h / 30) % 12;
|
|
137
|
+
const a = saturation * Math.min(lightness, 1 - lightness);
|
|
138
|
+
const channel = (n) => lightness - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
|
|
139
|
+
return [
|
|
140
|
+
channel(0),
|
|
141
|
+
channel(8),
|
|
142
|
+
channel(4)
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
const toLinear = (channel) => channel <= .03928 ? channel / 12.92 : Math.pow((channel + .055) / 1.055, 2.4);
|
|
255
146
|
function relativeLuminance(triplet) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const [r, g, b] = hslToRgb(parsed);
|
|
261
|
-
return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
|
|
147
|
+
const parsed = parseHslTriplet(triplet);
|
|
148
|
+
if (!parsed) return 0;
|
|
149
|
+
const [r, g, b] = hslToRgb(parsed);
|
|
150
|
+
return .2126 * toLinear(r) + .7152 * toLinear(g) + .0722 * toLinear(b);
|
|
262
151
|
}
|
|
263
|
-
/** WCAG contrast ratio between two HSL triplets. Order does not matter. */
|
|
264
152
|
function contrastRatio(a, b) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
153
|
+
const ya = relativeLuminance(a);
|
|
154
|
+
const yb = relativeLuminance(b);
|
|
155
|
+
return (Math.max(ya, yb) + .05) / (Math.min(ya, yb) + .05);
|
|
268
156
|
}
|
|
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
157
|
function solveLightnessNearest(options) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
return null;
|
|
158
|
+
const target = options.min + (options.margin ?? .02);
|
|
159
|
+
const start = Math.round(clamp(options.from, 0, 100));
|
|
160
|
+
for (let delta = 0; delta <= 100; delta += 1) for (const candidate of delta === 0 ? [start] : [start - delta, start + delta]) {
|
|
161
|
+
if (candidate < 0 || candidate > 100) continue;
|
|
162
|
+
const triplet = formatHslTriplet({
|
|
163
|
+
h: options.h,
|
|
164
|
+
s: options.s,
|
|
165
|
+
l: candidate
|
|
166
|
+
});
|
|
167
|
+
if (options.against.every((background) => contrastRatio(triplet, background) >= target)) return triplet;
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
295
170
|
}
|
|
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
171
|
function fitForeground(options) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
*/
|
|
172
|
+
const min = options.min ?? 4.5;
|
|
173
|
+
if (contrastRatio(options.foreground, options.background) >= min) return options.foreground;
|
|
174
|
+
const parsed = parseHslTriplet(options.foreground);
|
|
175
|
+
if (!parsed) return options.foreground;
|
|
176
|
+
return solveLightnessNearest({
|
|
177
|
+
h: parsed.h,
|
|
178
|
+
s: parsed.s,
|
|
179
|
+
against: [options.background],
|
|
180
|
+
min,
|
|
181
|
+
from: parsed.l
|
|
182
|
+
}) ?? options.foreground;
|
|
183
|
+
}
|
|
184
|
+
const THEME_OPTIONS = new InjectionToken("THEME_OPTIONS");
|
|
185
|
+
const THEME_MODES = [
|
|
186
|
+
"light",
|
|
187
|
+
"dark",
|
|
188
|
+
"system"
|
|
189
|
+
];
|
|
190
|
+
const RESOLVED_THEME_MODES = ["light", "dark"];
|
|
328
191
|
const THEME_COLORS = [
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
192
|
+
"base",
|
|
193
|
+
"red",
|
|
194
|
+
"orange",
|
|
195
|
+
"amber",
|
|
196
|
+
"yellow",
|
|
197
|
+
"lime",
|
|
198
|
+
"green",
|
|
199
|
+
"emerald",
|
|
200
|
+
"teal",
|
|
201
|
+
"cyan",
|
|
202
|
+
"sky",
|
|
203
|
+
"blue",
|
|
204
|
+
"indigo",
|
|
205
|
+
"violet",
|
|
206
|
+
"purple",
|
|
207
|
+
"fuchsia",
|
|
208
|
+
"pink",
|
|
209
|
+
"rose",
|
|
210
|
+
"brand"
|
|
348
211
|
];
|
|
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
212
|
const THEME_NEUTRALS = [
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
213
|
+
"base",
|
|
214
|
+
"slate",
|
|
215
|
+
"gray",
|
|
216
|
+
"zinc",
|
|
217
|
+
"neutral",
|
|
218
|
+
"stone",
|
|
219
|
+
"mauve",
|
|
220
|
+
"olive",
|
|
221
|
+
"mist",
|
|
222
|
+
"taupe"
|
|
365
223
|
];
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
const
|
|
380
|
-
const
|
|
381
|
-
const
|
|
382
|
-
const
|
|
383
|
-
const
|
|
384
|
-
const
|
|
385
|
-
const
|
|
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';
|
|
224
|
+
const THEME_RADII = [
|
|
225
|
+
"none",
|
|
226
|
+
"xs",
|
|
227
|
+
"sm",
|
|
228
|
+
"md",
|
|
229
|
+
"xl",
|
|
230
|
+
"full"
|
|
231
|
+
];
|
|
232
|
+
const THEME_SPACES = ["compact", "normal"];
|
|
233
|
+
const DEFAULT_THEME_MODE = "system";
|
|
234
|
+
const DEFAULT_THEME_COLOR = "base";
|
|
235
|
+
const DEFAULT_THEME_NEUTRAL = "base";
|
|
236
|
+
const DEFAULT_THEME_RADIUS = "md";
|
|
237
|
+
const DEFAULT_THEME_SPACE = "normal";
|
|
238
|
+
const THEME_MODE_STORAGE_KEY = "theme-mode";
|
|
239
|
+
const THEME_COLOR_STORAGE_KEY = "theme-color";
|
|
240
|
+
const THEME_NEUTRAL_STORAGE_KEY = "theme-neutral";
|
|
241
|
+
const THEME_BRAND_STORAGE_KEY = "theme-brand";
|
|
242
|
+
const THEME_RADIUS_STORAGE_KEY = "theme-radius";
|
|
243
|
+
const THEME_SPACE_STORAGE_KEY = "theme-space";
|
|
391
244
|
function isThemeMode(value) {
|
|
392
|
-
|
|
245
|
+
return typeof value === "string" && THEME_MODES.includes(value);
|
|
393
246
|
}
|
|
394
247
|
function isResolvedThemeMode(value) {
|
|
395
|
-
|
|
248
|
+
return typeof value === "string" && RESOLVED_THEME_MODES.includes(value);
|
|
396
249
|
}
|
|
397
250
|
function isThemeColor(value) {
|
|
398
|
-
|
|
251
|
+
return typeof value === "string" && THEME_COLORS.includes(value);
|
|
399
252
|
}
|
|
400
253
|
function isThemeNeutral(value) {
|
|
401
|
-
|
|
254
|
+
return typeof value === "string" && THEME_NEUTRALS.includes(value);
|
|
402
255
|
}
|
|
403
256
|
function isThemeRadius(value) {
|
|
404
|
-
|
|
257
|
+
return typeof value === "string" && THEME_RADII.includes(value);
|
|
405
258
|
}
|
|
406
259
|
function isThemeSpace(value) {
|
|
407
|
-
|
|
260
|
+
return typeof value === "string" && THEME_SPACES.includes(value);
|
|
408
261
|
}
|
|
409
262
|
function normalizeThemeMode(value) {
|
|
410
|
-
|
|
263
|
+
return isThemeMode(value) ? value : DEFAULT_THEME_MODE;
|
|
411
264
|
}
|
|
412
265
|
function normalizeThemeColor(value) {
|
|
413
|
-
|
|
266
|
+
return isThemeColor(value) ? value : DEFAULT_THEME_COLOR;
|
|
414
267
|
}
|
|
415
268
|
function normalizeThemeNeutral(value) {
|
|
416
|
-
|
|
269
|
+
return isThemeNeutral(value) ? value : DEFAULT_THEME_NEUTRAL;
|
|
417
270
|
}
|
|
418
271
|
function normalizeThemeRadius(value) {
|
|
419
|
-
|
|
272
|
+
return isThemeRadius(value) ? value : "md";
|
|
420
273
|
}
|
|
421
274
|
function normalizeThemeSpace(value) {
|
|
422
|
-
|
|
275
|
+
return isThemeSpace(value) ? value : DEFAULT_THEME_SPACE;
|
|
423
276
|
}
|
|
424
|
-
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
|
|
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
|
-
*/
|
|
277
|
+
const DEFAULT_BRAND_FOREGROUND = "0 0% 100%";
|
|
278
|
+
const RING_BACKGROUNDS = {
|
|
279
|
+
light: "0 0% 100%",
|
|
280
|
+
dark: "0 0% 5%"
|
|
281
|
+
};
|
|
443
282
|
function toBrandTriplet(color) {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
283
|
+
const trimmed = color.trim();
|
|
284
|
+
const parsed = parseHslTriplet(trimmed);
|
|
285
|
+
return parsed ? formatHslTriplet(parsed) : hexToHslTriplet(trimmed);
|
|
447
286
|
}
|
|
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
287
|
function hardenBrand(brand) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}),
|
|
469
|
-
};
|
|
288
|
+
const color = toBrandTriplet(brand.color);
|
|
289
|
+
if (!color) return brand;
|
|
290
|
+
return {
|
|
291
|
+
color,
|
|
292
|
+
foreground: fitForeground({
|
|
293
|
+
foreground: brand.foreground,
|
|
294
|
+
background: color,
|
|
295
|
+
min: TEXT_CONTRAST_MIN
|
|
296
|
+
})
|
|
297
|
+
};
|
|
470
298
|
}
|
|
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
299
|
function brandRing(color, background) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
against: [background],
|
|
489
|
-
min: NON_TEXT_CONTRAST_MIN,
|
|
490
|
-
from: parsed.l,
|
|
491
|
-
}) ?? color);
|
|
300
|
+
const parsed = parseHslTriplet(color);
|
|
301
|
+
if (!parsed) return null;
|
|
302
|
+
if (contrastRatio(color, background) >= 3) return color;
|
|
303
|
+
return solveLightnessNearest({
|
|
304
|
+
h: parsed.h,
|
|
305
|
+
s: parsed.s,
|
|
306
|
+
against: [background],
|
|
307
|
+
min: 3,
|
|
308
|
+
from: parsed.l
|
|
309
|
+
}) ?? color;
|
|
492
310
|
}
|
|
493
|
-
/** Extract the hue/saturation pair used by Layout's soft pattern tiers. */
|
|
494
311
|
function brandPatternHueSaturation(color) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
}
|
|
667
|
-
toggle() {
|
|
668
|
-
this.setMode(this.isDark() ? 'light' : 'dark');
|
|
669
|
-
}
|
|
670
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ThemeModeService, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
671
|
-
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.6", ngImport: i0, type: ThemeModeService });
|
|
672
|
-
}
|
|
673
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: ThemeModeService, decorators: [{
|
|
674
|
-
type: Service
|
|
675
|
-
}] });
|
|
676
|
-
|
|
677
|
-
/** Radius in px @ 16px root; `full` uses a finite SVG-friendly sentinel. */
|
|
312
|
+
const parsed = parseHslTriplet(color);
|
|
313
|
+
return parsed ? `${parsed.h} ${parsed.s}%` : null;
|
|
314
|
+
}
|
|
315
|
+
var ThemeBrandService = class ThemeBrandService {
|
|
316
|
+
options = inject(THEME_OPTIONS, { optional: true });
|
|
317
|
+
brandState = persistedSignal({
|
|
318
|
+
key: THEME_BRAND_STORAGE_KEY,
|
|
319
|
+
parse: (raw) => this.parseStored(raw) ?? this.normalizeOption(this.options?.brand),
|
|
320
|
+
serialize: (brand) => brand ? JSON.stringify(brand) : null
|
|
321
|
+
});
|
|
322
|
+
brand = this.brandState.asReadonly();
|
|
323
|
+
constructor() {
|
|
324
|
+
htmlStylePropertyEffect({ source: () => this.brandProperties(this.brandState()) });
|
|
325
|
+
}
|
|
326
|
+
setBrand(color, foreground = DEFAULT_BRAND_FOREGROUND) {
|
|
327
|
+
this.brandState.set(hardenBrand({
|
|
328
|
+
color,
|
|
329
|
+
foreground
|
|
330
|
+
}));
|
|
331
|
+
}
|
|
332
|
+
clearBrand() {
|
|
333
|
+
this.brandState.set(null);
|
|
334
|
+
}
|
|
335
|
+
normalizeOption(brand) {
|
|
336
|
+
if (!brand) return null;
|
|
337
|
+
if (typeof brand === "string") return hardenBrand({
|
|
338
|
+
color: brand,
|
|
339
|
+
foreground: DEFAULT_BRAND_FOREGROUND
|
|
340
|
+
});
|
|
341
|
+
return hardenBrand({
|
|
342
|
+
color: brand.color,
|
|
343
|
+
foreground: brand.foreground ?? DEFAULT_BRAND_FOREGROUND
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
brandProperties(brand) {
|
|
347
|
+
if (!brand) return {
|
|
348
|
+
"--brand": null,
|
|
349
|
+
"--brand-foreground": null,
|
|
350
|
+
"--brand-pattern-hs": null,
|
|
351
|
+
"--brand-ring-light": null,
|
|
352
|
+
"--brand-ring-dark": null
|
|
353
|
+
};
|
|
354
|
+
return {
|
|
355
|
+
"--brand": brand.color,
|
|
356
|
+
"--brand-foreground": brand.foreground,
|
|
357
|
+
"--brand-pattern-hs": brandPatternHueSaturation(brand.color),
|
|
358
|
+
"--brand-ring-light": brandRing(brand.color, RING_BACKGROUNDS.light),
|
|
359
|
+
"--brand-ring-dark": brandRing(brand.color, RING_BACKGROUNDS.dark)
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
parseStored(raw) {
|
|
363
|
+
if (!raw) return null;
|
|
364
|
+
try {
|
|
365
|
+
const parsed = JSON.parse(raw);
|
|
366
|
+
if (typeof parsed?.color !== "string") return null;
|
|
367
|
+
return hardenBrand({
|
|
368
|
+
color: parsed.color,
|
|
369
|
+
foreground: typeof parsed.foreground === "string" ? parsed.foreground : DEFAULT_BRAND_FOREGROUND
|
|
370
|
+
});
|
|
371
|
+
} catch {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
376
|
+
minVersion: "12.0.0",
|
|
377
|
+
version: "22.2.0",
|
|
378
|
+
ngImport: i0,
|
|
379
|
+
type: ThemeBrandService,
|
|
380
|
+
deps: [],
|
|
381
|
+
target: i0.ɵɵFactoryTarget.Service
|
|
382
|
+
});
|
|
383
|
+
static ɵprov = i0.ɵɵngDeclareService({
|
|
384
|
+
minVersion: "22.0.0",
|
|
385
|
+
version: "22.2.0",
|
|
386
|
+
ngImport: i0,
|
|
387
|
+
type: ThemeBrandService
|
|
388
|
+
});
|
|
389
|
+
};
|
|
390
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
391
|
+
minVersion: "12.0.0",
|
|
392
|
+
version: "22.2.0",
|
|
393
|
+
ngImport: i0,
|
|
394
|
+
type: ThemeBrandService,
|
|
395
|
+
decorators: [{ type: Service }],
|
|
396
|
+
ctorParameters: () => []
|
|
397
|
+
});
|
|
398
|
+
var ThemeColorService = class ThemeColorService {
|
|
399
|
+
options = inject(THEME_OPTIONS, { optional: true });
|
|
400
|
+
colorAxis = persistedHtmlAttributeSignal({
|
|
401
|
+
key: THEME_COLOR_STORAGE_KEY,
|
|
402
|
+
attribute: "theme-color",
|
|
403
|
+
normalize: normalizeThemeColor,
|
|
404
|
+
initial: this.options?.color ?? (this.options?.brand ? "brand" : void 0)
|
|
405
|
+
});
|
|
406
|
+
neutralAxis = persistedHtmlAttributeSignal({
|
|
407
|
+
key: THEME_NEUTRAL_STORAGE_KEY,
|
|
408
|
+
attribute: "theme-neutral",
|
|
409
|
+
normalize: normalizeThemeNeutral,
|
|
410
|
+
initial: this.options?.neutral
|
|
411
|
+
});
|
|
412
|
+
color = this.colorAxis.value;
|
|
413
|
+
neutral = this.neutralAxis.value;
|
|
414
|
+
setColor(color) {
|
|
415
|
+
this.colorAxis.set(color);
|
|
416
|
+
}
|
|
417
|
+
setNeutral(neutral) {
|
|
418
|
+
this.neutralAxis.set(neutral);
|
|
419
|
+
}
|
|
420
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
421
|
+
minVersion: "12.0.0",
|
|
422
|
+
version: "22.2.0",
|
|
423
|
+
ngImport: i0,
|
|
424
|
+
type: ThemeColorService,
|
|
425
|
+
deps: [],
|
|
426
|
+
target: i0.ɵɵFactoryTarget.Service
|
|
427
|
+
});
|
|
428
|
+
static ɵprov = i0.ɵɵngDeclareService({
|
|
429
|
+
minVersion: "22.0.0",
|
|
430
|
+
version: "22.2.0",
|
|
431
|
+
ngImport: i0,
|
|
432
|
+
type: ThemeColorService
|
|
433
|
+
});
|
|
434
|
+
};
|
|
435
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
436
|
+
minVersion: "12.0.0",
|
|
437
|
+
version: "22.2.0",
|
|
438
|
+
ngImport: i0,
|
|
439
|
+
type: ThemeColorService,
|
|
440
|
+
decorators: [{ type: Service }]
|
|
441
|
+
});
|
|
442
|
+
var ThemeModeService = class ThemeModeService {
|
|
443
|
+
options = inject(THEME_OPTIONS, { optional: true });
|
|
444
|
+
systemPrefersDark = prefersDarkSignal();
|
|
445
|
+
axis = persistedHtmlAttributeSignal({
|
|
446
|
+
key: THEME_MODE_STORAGE_KEY,
|
|
447
|
+
attribute: ["theme-mode", "data-mode"],
|
|
448
|
+
normalize: normalizeThemeMode,
|
|
449
|
+
initial: this.options?.mode,
|
|
450
|
+
resolveAttributeValue: (mode) => mode === "system" ? this.systemPrefersDark() ? "dark" : "light" : mode
|
|
451
|
+
});
|
|
452
|
+
mode = this.axis.value;
|
|
453
|
+
resolvedMode = computed(() => this.axis.attributeValue(), ...ngDevMode ? [{ debugName: "resolvedMode" }] : /* istanbul ignore next */ []);
|
|
454
|
+
isDark = computed(() => this.resolvedMode() === "dark", ...ngDevMode ? [{ debugName: "isDark" }] : /* istanbul ignore next */ []);
|
|
455
|
+
setMode(mode) {
|
|
456
|
+
this.axis.set(mode);
|
|
457
|
+
}
|
|
458
|
+
toggle() {
|
|
459
|
+
this.setMode(this.isDark() ? "light" : "dark");
|
|
460
|
+
}
|
|
461
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
462
|
+
minVersion: "12.0.0",
|
|
463
|
+
version: "22.2.0",
|
|
464
|
+
ngImport: i0,
|
|
465
|
+
type: ThemeModeService,
|
|
466
|
+
deps: [],
|
|
467
|
+
target: i0.ɵɵFactoryTarget.Service
|
|
468
|
+
});
|
|
469
|
+
static ɵprov = i0.ɵɵngDeclareService({
|
|
470
|
+
minVersion: "22.0.0",
|
|
471
|
+
version: "22.2.0",
|
|
472
|
+
ngImport: i0,
|
|
473
|
+
type: ThemeModeService
|
|
474
|
+
});
|
|
475
|
+
};
|
|
476
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
477
|
+
minVersion: "12.0.0",
|
|
478
|
+
version: "22.2.0",
|
|
479
|
+
ngImport: i0,
|
|
480
|
+
type: ThemeModeService,
|
|
481
|
+
decorators: [{ type: Service }]
|
|
482
|
+
});
|
|
678
483
|
const RADIUS_PX = {
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
484
|
+
none: 0,
|
|
485
|
+
xs: 2,
|
|
486
|
+
sm: 4,
|
|
487
|
+
md: 6,
|
|
488
|
+
xl: 12,
|
|
489
|
+
full: 9999
|
|
685
490
|
};
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
class ThemeSpaceService {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
491
|
+
var ThemeRadiusService = class ThemeRadiusService {
|
|
492
|
+
options = inject(THEME_OPTIONS, { optional: true });
|
|
493
|
+
axis = persistedHtmlAttributeSignal({
|
|
494
|
+
key: THEME_RADIUS_STORAGE_KEY,
|
|
495
|
+
attribute: "theme-radius",
|
|
496
|
+
normalize: normalizeThemeRadius,
|
|
497
|
+
initial: this.options?.radius
|
|
498
|
+
});
|
|
499
|
+
radius = this.axis.value;
|
|
500
|
+
radiusPx = computed(() => RADIUS_PX[this.radius()] ?? RADIUS_PX.md, ...ngDevMode ? [{ debugName: "radiusPx" }] : /* istanbul ignore next */ []);
|
|
501
|
+
setRadius(radius) {
|
|
502
|
+
this.axis.set(radius);
|
|
503
|
+
}
|
|
504
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
505
|
+
minVersion: "12.0.0",
|
|
506
|
+
version: "22.2.0",
|
|
507
|
+
ngImport: i0,
|
|
508
|
+
type: ThemeRadiusService,
|
|
509
|
+
deps: [],
|
|
510
|
+
target: i0.ɵɵFactoryTarget.Service
|
|
511
|
+
});
|
|
512
|
+
static ɵprov = i0.ɵɵngDeclareService({
|
|
513
|
+
minVersion: "22.0.0",
|
|
514
|
+
version: "22.2.0",
|
|
515
|
+
ngImport: i0,
|
|
516
|
+
type: ThemeRadiusService
|
|
517
|
+
});
|
|
518
|
+
};
|
|
519
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
520
|
+
minVersion: "12.0.0",
|
|
521
|
+
version: "22.2.0",
|
|
522
|
+
ngImport: i0,
|
|
523
|
+
type: ThemeRadiusService,
|
|
524
|
+
decorators: [{ type: Service }]
|
|
525
|
+
});
|
|
526
|
+
var ThemeSpaceService = class ThemeSpaceService {
|
|
527
|
+
options = inject(THEME_OPTIONS, { optional: true });
|
|
528
|
+
axis = persistedHtmlAttributeSignal({
|
|
529
|
+
key: THEME_SPACE_STORAGE_KEY,
|
|
530
|
+
attribute: "theme-space",
|
|
531
|
+
normalize: normalizeThemeSpace,
|
|
532
|
+
initial: this.options?.space
|
|
533
|
+
});
|
|
534
|
+
space = this.axis.value;
|
|
535
|
+
setSpace(space) {
|
|
536
|
+
this.axis.set(space);
|
|
537
|
+
}
|
|
538
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
539
|
+
minVersion: "12.0.0",
|
|
540
|
+
version: "22.2.0",
|
|
541
|
+
ngImport: i0,
|
|
542
|
+
type: ThemeSpaceService,
|
|
543
|
+
deps: [],
|
|
544
|
+
target: i0.ɵɵFactoryTarget.Service
|
|
545
|
+
});
|
|
546
|
+
static ɵprov = i0.ɵɵngDeclareService({
|
|
547
|
+
minVersion: "22.0.0",
|
|
548
|
+
version: "22.2.0",
|
|
549
|
+
ngImport: i0,
|
|
550
|
+
type: ThemeSpaceService
|
|
551
|
+
});
|
|
552
|
+
};
|
|
553
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
554
|
+
minVersion: "12.0.0",
|
|
555
|
+
version: "22.2.0",
|
|
556
|
+
ngImport: i0,
|
|
557
|
+
type: ThemeSpaceService,
|
|
558
|
+
decorators: [{ type: Service }]
|
|
559
|
+
});
|
|
560
|
+
const MATERIAL_SYMBOLS_FONT_ATTR = "data-icon-font";
|
|
561
|
+
const MATERIAL_SYMBOLS_FONT_ID = "material-symbols";
|
|
562
|
+
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
563
|
function provideUiTheme(options = {}) {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
loadMaterialSymbolsStylesheet();
|
|
756
|
-
}
|
|
757
|
-
inject(ThemeModeService);
|
|
758
|
-
inject(ThemeColorService);
|
|
759
|
-
inject(ThemeBrandService);
|
|
760
|
-
inject(ThemeRadiusService);
|
|
761
|
-
inject(ThemeSpaceService);
|
|
762
|
-
}),
|
|
763
|
-
]);
|
|
564
|
+
return makeEnvironmentProviders([{
|
|
565
|
+
provide: THEME_OPTIONS,
|
|
566
|
+
useValue: options
|
|
567
|
+
}, provideEnvironmentInitializer(() => {
|
|
568
|
+
if (options.icons?.materialSymbols === true) loadMaterialSymbolsStylesheet();
|
|
569
|
+
inject(ThemeModeService);
|
|
570
|
+
inject(ThemeColorService);
|
|
571
|
+
inject(ThemeBrandService);
|
|
572
|
+
inject(ThemeRadiusService);
|
|
573
|
+
inject(ThemeSpaceService);
|
|
574
|
+
})]);
|
|
764
575
|
}
|
|
765
576
|
function loadMaterialSymbolsStylesheet() {
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
link.href = MATERIAL_SYMBOLS_FONT_HREF;
|
|
779
|
-
link.setAttribute(MATERIAL_SYMBOLS_FONT_ATTR, MATERIAL_SYMBOLS_FONT_ID);
|
|
780
|
-
head.appendChild(link);
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
/**
|
|
784
|
-
* Generated bundle index. Do not edit.
|
|
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 };
|
|
577
|
+
const documentRef = inject(DOCUMENT, { optional: true });
|
|
578
|
+
const platformId = inject(PLATFORM_ID);
|
|
579
|
+
const head = documentRef?.head;
|
|
580
|
+
if (!isPlatformBrowser(platformId) || !head) return;
|
|
581
|
+
if (head.querySelector(`link[${MATERIAL_SYMBOLS_FONT_ATTR}="${MATERIAL_SYMBOLS_FONT_ID}"]`)) return;
|
|
582
|
+
const link = head.ownerDocument.createElement("link");
|
|
583
|
+
link.rel = "stylesheet";
|
|
584
|
+
link.href = MATERIAL_SYMBOLS_FONT_HREF;
|
|
585
|
+
link.setAttribute(MATERIAL_SYMBOLS_FONT_ATTR, MATERIAL_SYMBOLS_FONT_ID);
|
|
586
|
+
head.appendChild(link);
|
|
587
|
+
}
|
|
588
|
+
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 };
|