@frame-ui-ng/foundation 0.3.0-beta.0 → 0.4.0-beta.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
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# Foundation
|
|
2
2
|
|
|
3
|
-
`@frame-ui-ng/foundation` is the stable base layer for FrameUI.
|
|
4
|
-
|
|
5
|
-
Documentation: https://frame-ui.com
|
|
3
|
+
`@frame-ui-ng/foundation` is the stable base layer for FrameUI.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
5
|
+
Documentation: https://frame-ui.com
|
|
6
|
+
|
|
7
|
+
Current scope:
|
|
8
|
+
|
|
9
|
+
- CSS-variable token contract
|
|
10
|
+
- Angular theme switching via `data-theme` or a shared `.dark` class
|
|
11
|
+
- class merge helpers for future slot-based primitives
|
|
12
|
+
- Vitest unit tests
|
|
13
13
|
|
|
14
14
|
No primitives or complex components are included here.
|
|
15
15
|
|
|
@@ -79,70 +79,80 @@ Bad token naming:
|
|
|
79
79
|
--frame-card-border-hover
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
## Light And Dark
|
|
83
|
-
|
|
84
|
-
The foundation layer only models `light` and `dark`.
|
|
85
|
-
|
|
86
|
-
Brand, product, or campaign differences should be handled by the host app's tokens or by scoped CSS-variable overrides. They should not become additional registered theme names.
|
|
87
|
-
|
|
88
|
-
## Theme Ownership
|
|
89
|
-
|
|
90
|
-
The foundation layer should expose a token contract, not force itself to be the only dark mode owner.
|
|
91
|
-
|
|
92
|
-
There are two recommended ownership models:
|
|
93
|
-
|
|
94
|
-
- library-managed: the FrameUI writes the active theme to the root element
|
|
95
|
-
- externally managed: another system such as Tailwind owns the root selector and the FrameUI follows it
|
|
96
|
-
|
|
97
|
-
### Library-managed with `data-theme`
|
|
98
|
-
|
|
99
|
-
This is the current default:
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
provideFrameUI({
|
|
103
|
-
defaultTheme: 'light',
|
|
104
|
-
});
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Library-managed with Tailwind's `.dark` class
|
|
108
|
-
|
|
109
|
-
If you want the FrameUI service to be the single source of truth, but Tailwind utilities should respond too, switch to class strategy:
|
|
110
|
-
|
|
111
|
-
```ts
|
|
112
|
-
provideFrameUI({
|
|
113
|
-
strategy: 'class',
|
|
114
|
-
className: 'dark',
|
|
115
|
-
});
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
`ThemeService.setTheme('dark')` now adds `.dark` to the root element, so both the FrameUI tokens and Tailwind `dark:` utilities react to the same switch.
|
|
119
|
-
|
|
120
|
-
### Externally managed by Tailwind or another app shell
|
|
121
|
-
|
|
122
|
-
If the host app already owns dark mode, let the FrameUI observe instead of write:
|
|
123
|
-
|
|
124
|
-
```ts
|
|
125
|
-
provideFrameUI({
|
|
126
|
-
strategy: 'class',
|
|
127
|
-
mode: 'observe',
|
|
128
|
-
className: 'dark',
|
|
129
|
-
});
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
In this mode the library does not write to the DOM. It reads the current root class and keeps `ThemeService.theme()` in sync with that external source of truth.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
|
|
82
|
+
## Light And Dark
|
|
83
|
+
|
|
84
|
+
The foundation layer only models `light` and `dark`.
|
|
85
|
+
|
|
86
|
+
Brand, product, or campaign differences should be handled by the host app's tokens or by scoped CSS-variable overrides. They should not become additional registered theme names.
|
|
87
|
+
|
|
88
|
+
## Theme Ownership
|
|
89
|
+
|
|
90
|
+
The foundation layer should expose a token contract, not force itself to be the only dark mode owner.
|
|
91
|
+
|
|
92
|
+
There are two recommended ownership models:
|
|
93
|
+
|
|
94
|
+
- library-managed: the FrameUI writes the active theme to the root element
|
|
95
|
+
- externally managed: another system such as Tailwind owns the root selector and the FrameUI follows it
|
|
96
|
+
|
|
97
|
+
### Library-managed with `data-theme`
|
|
98
|
+
|
|
99
|
+
This is the current default:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
provideFrameUI({
|
|
103
|
+
defaultTheme: 'light',
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Library-managed with Tailwind's `.dark` class
|
|
108
|
+
|
|
109
|
+
If you want the FrameUI service to be the single source of truth, but Tailwind utilities should respond too, switch to class strategy:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
provideFrameUI({
|
|
113
|
+
strategy: 'class',
|
|
114
|
+
className: 'dark',
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`ThemeService.setTheme('dark')` now adds `.dark` to the root element, so both the FrameUI tokens and Tailwind `dark:` utilities react to the same switch.
|
|
119
|
+
|
|
120
|
+
### Externally managed by Tailwind or another app shell
|
|
121
|
+
|
|
122
|
+
If the host app already owns dark mode, let the FrameUI observe instead of write:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
provideFrameUI({
|
|
126
|
+
strategy: 'class',
|
|
127
|
+
mode: 'observe',
|
|
128
|
+
className: 'dark',
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
In this mode the library does not write to the DOM. It reads the current root class and keeps `ThemeService.theme()` in sync with that external source of truth.
|
|
133
|
+
|
|
134
|
+
## Global Appearance Options
|
|
135
|
+
|
|
136
|
+
FrameUI components include small blueprint-style corner handles by default. Disable them for the whole app from the foundation provider:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
provideFrameUI({
|
|
140
|
+
disableCornerHandles: true,
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Use scoped overrides for local brand moments:
|
|
145
|
+
|
|
146
|
+
```css
|
|
147
|
+
.marketing-hero {
|
|
148
|
+
--frame-primary: oklch(0.69 0.19 38);
|
|
149
|
+
--frame-primary-foreground: oklch(0.99 0.01 95);
|
|
150
|
+
--frame-radius-lg: 1rem;
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Commands
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npm install @frame-ui-ng/foundation
|
|
158
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DOCUMENT } from '@angular/common';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { InjectionToken, makeEnvironmentProviders, inject, signal, computed, Injectable } from '@angular/core';
|
|
3
|
+
import { InjectionToken, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, inject, signal, computed, Injectable } from '@angular/core';
|
|
4
4
|
|
|
5
5
|
function cx(...values) {
|
|
6
6
|
const classNames = [];
|
|
@@ -39,9 +39,11 @@ const DEFAULT_CONFIG = {
|
|
|
39
39
|
attribute: 'data-theme',
|
|
40
40
|
className: 'dark',
|
|
41
41
|
defaultTheme: 'light',
|
|
42
|
+
disableCornerHandles: false,
|
|
42
43
|
mode: 'managed',
|
|
43
44
|
strategy: 'attribute',
|
|
44
45
|
};
|
|
46
|
+
const CORNER_HANDLES_ATTRIBUTE = 'data-frame-corner-handles';
|
|
45
47
|
const FRAME_UI_CONFIG = new InjectionToken('FRAME_UI_CONFIG', {
|
|
46
48
|
factory: () => DEFAULT_CONFIG,
|
|
47
49
|
});
|
|
@@ -52,6 +54,13 @@ function provideFrameUI(options = {}) {
|
|
|
52
54
|
useValue: createFrameUIConfig(options),
|
|
53
55
|
},
|
|
54
56
|
ThemeService,
|
|
57
|
+
{
|
|
58
|
+
provide: ENVIRONMENT_INITIALIZER,
|
|
59
|
+
multi: true,
|
|
60
|
+
useValue: () => {
|
|
61
|
+
inject(ThemeService);
|
|
62
|
+
},
|
|
63
|
+
},
|
|
55
64
|
]);
|
|
56
65
|
}
|
|
57
66
|
function createFrameUIConfig(options = {}) {
|
|
@@ -60,6 +69,7 @@ function createFrameUIConfig(options = {}) {
|
|
|
60
69
|
attribute: options.attribute ?? DEFAULT_CONFIG.attribute,
|
|
61
70
|
className: options.className ?? DEFAULT_CONFIG.className,
|
|
62
71
|
defaultTheme,
|
|
72
|
+
disableCornerHandles: options.disableCornerHandles ?? DEFAULT_CONFIG.disableCornerHandles,
|
|
63
73
|
mode: options.mode ?? DEFAULT_CONFIG.mode,
|
|
64
74
|
strategy: options.strategy ?? DEFAULT_CONFIG.strategy,
|
|
65
75
|
};
|
|
@@ -72,6 +82,7 @@ class ThemeService {
|
|
|
72
82
|
theme = this.activeTheme.asReadonly();
|
|
73
83
|
isDark = computed(() => this.activeTheme() === 'dark', ...(ngDevMode ? [{ debugName: "isDark" }] : /* istanbul ignore next */ []));
|
|
74
84
|
constructor() {
|
|
85
|
+
this.applyCornerHandlesPreference();
|
|
75
86
|
if (this.config.mode === 'observe') {
|
|
76
87
|
this.syncFromDom();
|
|
77
88
|
this.observeThemeChanges();
|
|
@@ -109,6 +120,17 @@ class ThemeService {
|
|
|
109
120
|
}
|
|
110
121
|
root.setAttribute(this.config.attribute, name);
|
|
111
122
|
}
|
|
123
|
+
applyCornerHandlesPreference() {
|
|
124
|
+
const root = this.document?.documentElement;
|
|
125
|
+
if (!root) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (this.config.disableCornerHandles) {
|
|
129
|
+
root.setAttribute(CORNER_HANDLES_ATTRIBUTE, 'false');
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
root.removeAttribute(CORNER_HANDLES_ATTRIBUTE);
|
|
133
|
+
}
|
|
112
134
|
observeThemeChanges() {
|
|
113
135
|
const root = this.document?.documentElement;
|
|
114
136
|
if (!root || typeof MutationObserver === 'undefined') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame-ui-ng-foundation.mjs","sources":["../../../projects/foundation/src/lib/class-names.ts","../../../projects/foundation/src/lib/frame-ui.ts","../../../projects/foundation/src/frame-ui-ng-foundation.ts"],"sourcesContent":["export type ClassDictionary = Readonly<Record<string, boolean | null | undefined>>;\nexport type ClassValue =\n | ClassDictionary\n | ClassValue[]\n | false\n | null\n | string\n | undefined;\n\nexport type SlotClasses<TSlot extends string> = Readonly<Record<TSlot, string>>;\nexport type SlotClassOverrides<TSlot extends string> = Partial<Record<TSlot, ClassValue>>;\n\nexport function cx(...values: readonly ClassValue[]): string {\n const classNames: string[] = [];\n\n for (const value of values) {\n if (!value) {\n continue;\n }\n\n if (typeof value === 'string') {\n classNames.push(value);\n continue;\n }\n\n if (Array.isArray(value)) {\n const nested = cx(...value);\n\n if (nested) {\n classNames.push(nested);\n }\n\n continue;\n }\n\n for (const [className, enabled] of Object.entries(value)) {\n if (enabled) {\n classNames.push(className);\n }\n }\n }\n\n return classNames.join(' ');\n}\n\nexport function withClassOverrides<TSlot extends string>(\n slots: SlotClasses<TSlot>,\n overrides?: SlotClassOverrides<TSlot>,\n): Record<TSlot, string> {\n const mergedSlots = {} as Record<TSlot, string>;\n\n for (const slot of Object.keys(slots) as TSlot[]) {\n mergedSlots[slot] = cx(slots[slot], overrides?.[slot]);\n }\n\n return mergedSlots;\n}\n","import { DOCUMENT } from '@angular/common';\nimport {\n EnvironmentProviders,\n Injectable,\n InjectionToken,\n OnDestroy,\n Signal,\n computed,\n inject,\n makeEnvironmentProviders,\n signal,\n} from '@angular/core';\n\nexport type ThemeBindingStrategy = 'attribute' | 'class';\nexport type ThemeSyncMode = 'managed' | 'observe';\nexport type FrameUITheme = 'light' | 'dark';\n\nexport interface FrameUIConfig {\n attribute: string;\n className: string;\n defaultTheme: FrameUITheme;\n mode: ThemeSyncMode;\n strategy: ThemeBindingStrategy;\n}\n\nconst DEFAULT_CONFIG: FrameUIConfig = {\n attribute: 'data-theme',\n className: 'dark',\n defaultTheme: 'light',\n mode: 'managed',\n strategy: 'attribute',\n};\n\nexport const FRAME_UI_CONFIG = new InjectionToken<FrameUIConfig>(\n 'FRAME_UI_CONFIG',\n {\n factory: () => DEFAULT_CONFIG,\n },\n);\n\nexport interface FrameUIOptions {\n attribute?: string;\n className?: string;\n defaultTheme?: FrameUITheme;\n mode?: ThemeSyncMode;\n strategy?: ThemeBindingStrategy;\n}\n\nexport function provideFrameUI(\n options: FrameUIOptions = {},\n): EnvironmentProviders {\n return makeEnvironmentProviders([\n {\n provide: FRAME_UI_CONFIG,\n useValue: createFrameUIConfig(options),\n },\n ThemeService,\n ]);\n}\n\nexport function createFrameUIConfig(\n options: FrameUIOptions = {},\n): FrameUIConfig {\n const defaultTheme = options.defaultTheme ?? DEFAULT_CONFIG.defaultTheme;\n\n return {\n attribute: options.attribute ?? DEFAULT_CONFIG.attribute,\n className: options.className ?? DEFAULT_CONFIG.className,\n defaultTheme,\n mode: options.mode ?? DEFAULT_CONFIG.mode,\n strategy: options.strategy ?? DEFAULT_CONFIG.strategy,\n };\n}\n\n@Injectable()\nexport class ThemeService implements OnDestroy {\n private readonly document = inject(DOCUMENT);\n private readonly config = inject(FRAME_UI_CONFIG);\n private readonly activeTheme = signal(this.config.defaultTheme);\n private observer: MutationObserver | null = null;\n\n readonly theme: Signal<FrameUITheme> = this.activeTheme.asReadonly();\n\n readonly isDark = computed(() => this.activeTheme() === 'dark');\n\n constructor() {\n if (this.config.mode === 'observe') {\n this.syncFromDom();\n this.observeThemeChanges();\n return;\n }\n\n this.applyTheme(this.activeTheme());\n }\n\n setTheme(name: FrameUITheme): void {\n if (!isFrameUITheme(name)) {\n throw new Error(`Unknown theme \"${name}\".`);\n }\n\n if (this.config.mode === 'observe') {\n throw new Error(\n 'ThemeService is configured to observe external theme state and cannot set the theme.',\n );\n }\n\n this.activeTheme.set(name);\n this.applyTheme(name);\n }\n\n toggleTheme(): FrameUITheme {\n const nextTheme = this.activeTheme() === 'dark' ? 'light' : 'dark';\n\n this.setTheme(nextTheme);\n\n return nextTheme;\n }\n\n ngOnDestroy(): void {\n this.observer?.disconnect();\n this.observer = null;\n }\n\n private applyTheme(name: FrameUITheme): void {\n const root = this.document?.documentElement;\n\n if (!root) {\n return;\n }\n\n if (this.config.strategy === 'class') {\n root.classList.toggle(this.config.className, name === 'dark');\n return;\n }\n\n root.setAttribute(this.config.attribute, name);\n }\n\n private observeThemeChanges(): void {\n const root = this.document?.documentElement;\n\n if (!root || typeof MutationObserver === 'undefined') {\n return;\n }\n\n const attributeFilter =\n this.config.strategy === 'class' ? ['class'] : [this.config.attribute];\n\n this.observer = new MutationObserver(() => {\n this.syncFromDom();\n });\n this.observer.observe(root, {\n attributeFilter,\n attributes: true,\n });\n }\n\n private syncFromDom(): void {\n const root = this.document?.documentElement;\n\n if (!root) {\n return;\n }\n\n this.activeTheme.set(this.readThemeFromDom(root));\n }\n\n private readThemeFromDom(root: HTMLElement): FrameUITheme {\n if (this.config.strategy === 'class') {\n return root.classList.contains(this.config.className) ? 'dark' : 'light';\n }\n\n const theme = root.getAttribute(this.config.attribute);\n\n return isFrameUITheme(theme) ? theme : this.config.defaultTheme;\n }\n}\n\nfunction isFrameUITheme(value: unknown): value is FrameUITheme {\n return value === 'light' || value === 'dark';\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"frame-ui-ng-foundation.mjs","sources":["../../../projects/foundation/src/lib/class-names.ts","../../../projects/foundation/src/lib/frame-ui.ts","../../../projects/foundation/src/frame-ui-ng-foundation.ts"],"sourcesContent":["export type ClassDictionary = Readonly<Record<string, boolean | null | undefined>>;\nexport type ClassValue =\n | ClassDictionary\n | ClassValue[]\n | false\n | null\n | string\n | undefined;\n\nexport type SlotClasses<TSlot extends string> = Readonly<Record<TSlot, string>>;\nexport type SlotClassOverrides<TSlot extends string> = Partial<Record<TSlot, ClassValue>>;\n\nexport function cx(...values: readonly ClassValue[]): string {\n const classNames: string[] = [];\n\n for (const value of values) {\n if (!value) {\n continue;\n }\n\n if (typeof value === 'string') {\n classNames.push(value);\n continue;\n }\n\n if (Array.isArray(value)) {\n const nested = cx(...value);\n\n if (nested) {\n classNames.push(nested);\n }\n\n continue;\n }\n\n for (const [className, enabled] of Object.entries(value)) {\n if (enabled) {\n classNames.push(className);\n }\n }\n }\n\n return classNames.join(' ');\n}\n\nexport function withClassOverrides<TSlot extends string>(\n slots: SlotClasses<TSlot>,\n overrides?: SlotClassOverrides<TSlot>,\n): Record<TSlot, string> {\n const mergedSlots = {} as Record<TSlot, string>;\n\n for (const slot of Object.keys(slots) as TSlot[]) {\n mergedSlots[slot] = cx(slots[slot], overrides?.[slot]);\n }\n\n return mergedSlots;\n}\n","import { DOCUMENT } from '@angular/common';\r\nimport {\r\n EnvironmentProviders,\r\n ENVIRONMENT_INITIALIZER,\r\n Injectable,\r\n InjectionToken,\r\n OnDestroy,\r\n Signal,\r\n computed,\r\n inject,\r\n makeEnvironmentProviders,\r\n signal,\r\n} from '@angular/core';\r\n\r\nexport type ThemeBindingStrategy = 'attribute' | 'class';\r\nexport type ThemeSyncMode = 'managed' | 'observe';\r\nexport type FrameUITheme = 'light' | 'dark';\r\n\r\nexport interface FrameUIConfig {\r\n attribute: string;\r\n className: string;\r\n defaultTheme: FrameUITheme;\r\n disableCornerHandles: boolean;\r\n mode: ThemeSyncMode;\r\n strategy: ThemeBindingStrategy;\r\n}\r\n\r\nconst DEFAULT_CONFIG: FrameUIConfig = {\r\n attribute: 'data-theme',\r\n className: 'dark',\r\n defaultTheme: 'light',\r\n disableCornerHandles: false,\r\n mode: 'managed',\r\n strategy: 'attribute',\r\n};\r\n\r\nconst CORNER_HANDLES_ATTRIBUTE = 'data-frame-corner-handles';\r\n\r\nexport const FRAME_UI_CONFIG = new InjectionToken<FrameUIConfig>(\r\n 'FRAME_UI_CONFIG',\r\n {\r\n factory: () => DEFAULT_CONFIG,\r\n },\r\n);\r\n\r\nexport interface FrameUIOptions {\r\n attribute?: string;\r\n className?: string;\r\n defaultTheme?: FrameUITheme;\r\n disableCornerHandles?: boolean;\r\n mode?: ThemeSyncMode;\r\n strategy?: ThemeBindingStrategy;\r\n}\r\n\r\nexport function provideFrameUI(\r\n options: FrameUIOptions = {},\r\n): EnvironmentProviders {\r\n return makeEnvironmentProviders([\r\n {\r\n provide: FRAME_UI_CONFIG,\r\n useValue: createFrameUIConfig(options),\r\n },\r\n ThemeService,\r\n {\r\n provide: ENVIRONMENT_INITIALIZER,\r\n multi: true,\r\n useValue: () => {\r\n inject(ThemeService);\r\n },\r\n },\r\n ]);\r\n}\r\n\r\nexport function createFrameUIConfig(\r\n options: FrameUIOptions = {},\r\n): FrameUIConfig {\r\n const defaultTheme = options.defaultTheme ?? DEFAULT_CONFIG.defaultTheme;\r\n\r\n return {\r\n attribute: options.attribute ?? DEFAULT_CONFIG.attribute,\r\n className: options.className ?? DEFAULT_CONFIG.className,\r\n defaultTheme,\r\n disableCornerHandles:\r\n options.disableCornerHandles ?? DEFAULT_CONFIG.disableCornerHandles,\r\n mode: options.mode ?? DEFAULT_CONFIG.mode,\r\n strategy: options.strategy ?? DEFAULT_CONFIG.strategy,\r\n };\r\n}\r\n\r\n@Injectable()\r\nexport class ThemeService implements OnDestroy {\r\n private readonly document = inject(DOCUMENT);\r\n private readonly config = inject(FRAME_UI_CONFIG);\r\n private readonly activeTheme = signal(this.config.defaultTheme);\r\n private observer: MutationObserver | null = null;\r\n\r\n readonly theme: Signal<FrameUITheme> = this.activeTheme.asReadonly();\r\n\r\n readonly isDark = computed(() => this.activeTheme() === 'dark');\r\n\r\n constructor() {\r\n this.applyCornerHandlesPreference();\r\n\r\n if (this.config.mode === 'observe') {\r\n this.syncFromDom();\r\n this.observeThemeChanges();\r\n return;\r\n }\r\n\r\n this.applyTheme(this.activeTheme());\r\n }\r\n\r\n setTheme(name: FrameUITheme): void {\r\n if (!isFrameUITheme(name)) {\r\n throw new Error(`Unknown theme \"${name}\".`);\r\n }\r\n\r\n if (this.config.mode === 'observe') {\r\n throw new Error(\r\n 'ThemeService is configured to observe external theme state and cannot set the theme.',\r\n );\r\n }\r\n\r\n this.activeTheme.set(name);\r\n this.applyTheme(name);\r\n }\r\n\r\n toggleTheme(): FrameUITheme {\r\n const nextTheme = this.activeTheme() === 'dark' ? 'light' : 'dark';\r\n\r\n this.setTheme(nextTheme);\r\n\r\n return nextTheme;\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.observer?.disconnect();\r\n this.observer = null;\r\n }\r\n\r\n private applyTheme(name: FrameUITheme): void {\r\n const root = this.document?.documentElement;\r\n\r\n if (!root) {\r\n return;\r\n }\r\n\r\n if (this.config.strategy === 'class') {\r\n root.classList.toggle(this.config.className, name === 'dark');\r\n return;\r\n }\r\n\r\n root.setAttribute(this.config.attribute, name);\r\n }\r\n\r\n private applyCornerHandlesPreference(): void {\r\n const root = this.document?.documentElement;\r\n\r\n if (!root) {\r\n return;\r\n }\r\n\r\n if (this.config.disableCornerHandles) {\r\n root.setAttribute(CORNER_HANDLES_ATTRIBUTE, 'false');\r\n return;\r\n }\r\n\r\n root.removeAttribute(CORNER_HANDLES_ATTRIBUTE);\r\n }\r\n\r\n private observeThemeChanges(): void {\r\n const root = this.document?.documentElement;\r\n\r\n if (!root || typeof MutationObserver === 'undefined') {\r\n return;\r\n }\r\n\r\n const attributeFilter =\r\n this.config.strategy === 'class' ? ['class'] : [this.config.attribute];\r\n\r\n this.observer = new MutationObserver(() => {\r\n this.syncFromDom();\r\n });\r\n this.observer.observe(root, {\r\n attributeFilter,\r\n attributes: true,\r\n });\r\n }\r\n\r\n private syncFromDom(): void {\r\n const root = this.document?.documentElement;\r\n\r\n if (!root) {\r\n return;\r\n }\r\n\r\n this.activeTheme.set(this.readThemeFromDom(root));\r\n }\r\n\r\n private readThemeFromDom(root: HTMLElement): FrameUITheme {\r\n if (this.config.strategy === 'class') {\r\n return root.classList.contains(this.config.className) ? 'dark' : 'light';\r\n }\r\n\r\n const theme = root.getAttribute(this.config.attribute);\r\n\r\n return isFrameUITheme(theme) ? theme : this.config.defaultTheme;\r\n }\r\n}\r\n\r\nfunction isFrameUITheme(value: unknown): value is FrameUITheme {\r\n return value === 'light' || value === 'dark';\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -15,6 +15,7 @@ interface FrameUIConfig {
|
|
|
15
15
|
attribute: string;
|
|
16
16
|
className: string;
|
|
17
17
|
defaultTheme: FrameUITheme;
|
|
18
|
+
disableCornerHandles: boolean;
|
|
18
19
|
mode: ThemeSyncMode;
|
|
19
20
|
strategy: ThemeBindingStrategy;
|
|
20
21
|
}
|
|
@@ -23,6 +24,7 @@ interface FrameUIOptions {
|
|
|
23
24
|
attribute?: string;
|
|
24
25
|
className?: string;
|
|
25
26
|
defaultTheme?: FrameUITheme;
|
|
27
|
+
disableCornerHandles?: boolean;
|
|
26
28
|
mode?: ThemeSyncMode;
|
|
27
29
|
strategy?: ThemeBindingStrategy;
|
|
28
30
|
}
|
|
@@ -40,6 +42,7 @@ declare class ThemeService implements OnDestroy {
|
|
|
40
42
|
toggleTheme(): FrameUITheme;
|
|
41
43
|
ngOnDestroy(): void;
|
|
42
44
|
private applyTheme;
|
|
45
|
+
private applyCornerHandlesPreference;
|
|
43
46
|
private observeThemeChanges;
|
|
44
47
|
private syncFromDom;
|
|
45
48
|
private readThemeFromDom;
|