luxen-ui 0.18.3 → 0.19.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 +1 -0
- package/cdn/color-scheme.d.ts +76 -0
- package/cdn/color-scheme.d.ts.map +1 -0
- package/cdn/color-scheme.js +2 -0
- package/cdn/color-scheme.js.map +1 -0
- package/cdn/config.d.ts +23 -0
- package/cdn/config.d.ts.map +1 -1
- package/cdn/custom-elements.json +136 -1
- package/cdn/elements/color-scheme-icon/color-scheme-icon.d.ts +55 -0
- package/cdn/elements/color-scheme-icon/color-scheme-icon.d.ts.map +1 -0
- package/cdn/elements/color-scheme-icon/color-scheme-icon.js +44 -0
- package/cdn/elements/color-scheme-icon/color-scheme-icon.js.map +1 -0
- package/cdn/elements/color-scheme-icon/index.d.ts +2 -0
- package/cdn/elements/color-scheme-icon/index.d.ts.map +1 -0
- package/cdn/elements/color-scheme-icon/index.js +2 -0
- package/cdn/elements/color-scheme-icon/index.js.map +1 -0
- package/cdn/elements/dropdown-item/dropdown-item.d.ts +9 -1
- package/cdn/elements/dropdown-item/dropdown-item.d.ts.map +1 -1
- package/cdn/elements/dropdown-item/dropdown-item.js +32 -23
- package/cdn/elements/dropdown-item/dropdown-item.js.map +1 -1
- package/cdn/elements/segmented-control/segmented-control.d.ts +2 -0
- package/cdn/elements/segmented-control/segmented-control.d.ts.map +1 -1
- package/cdn/elements/segmented-control/segmented-control.js +1 -1
- package/cdn/elements/segmented-control/segmented-control.js.map +1 -1
- package/cdn/elements/tabs/tabs.d.ts +7 -2
- package/cdn/elements/tabs/tabs.d.ts.map +1 -1
- package/cdn/elements/tabs/tabs.js.map +1 -1
- package/cdn/index.d.ts +2 -0
- package/cdn/index.d.ts.map +1 -1
- package/cdn/index.js +1 -1
- package/cdn/registry.d.ts +1 -1
- package/cdn/registry.d.ts.map +1 -1
- package/cdn/registry.js.map +1 -1
- package/cdn/standalone.js.map +1 -1
- package/dist/color-scheme.d.ts +76 -0
- package/dist/color-scheme.d.ts.map +1 -0
- package/dist/color-scheme.js +203 -0
- package/dist/config.d.ts +23 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/custom-elements.json +136 -1
- package/dist/elements/color-scheme-icon/color-scheme-icon.css +98 -0
- package/dist/elements/color-scheme-icon/color-scheme-icon.d.ts +55 -0
- package/dist/elements/color-scheme-icon/color-scheme-icon.d.ts.map +1 -0
- package/dist/elements/color-scheme-icon/color-scheme-icon.js +138 -0
- package/dist/elements/color-scheme-icon/index.d.ts +2 -0
- package/dist/elements/color-scheme-icon/index.d.ts.map +1 -0
- package/dist/elements/color-scheme-icon/index.js +4 -0
- package/dist/elements/dropdown-item/dropdown-item.d.ts +9 -1
- package/dist/elements/dropdown-item/dropdown-item.d.ts.map +1 -1
- package/dist/elements/dropdown-item/dropdown-item.js +49 -26
- package/dist/elements/segmented-control/segmented-control.d.ts +2 -0
- package/dist/elements/segmented-control/segmented-control.d.ts.map +1 -1
- package/dist/elements/segmented-control/segmented-control.js +11 -1
- package/dist/elements/tabs/tabs.d.ts +7 -2
- package/dist/elements/tabs/tabs.d.ts.map +1 -1
- package/dist/elements/tabs/tabs.js +7 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/metadata/color-scheme-icon.json +66 -0
- package/dist/metadata/dropdown-item.json +9 -1
- package/dist/metadata/index.json +76 -2
- package/dist/registry.d.ts +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/templates/elements/color-scheme-icon.md +279 -0
- package/dist/templates/elements/dropdown.md +54 -1
- package/dist/templates/elements/tabs.md +4 -1
- package/elements.json +8 -0
- package/package.json +5 -1
- package/vite-plugin.ts +71 -8
package/README.md
CHANGED
|
@@ -35,6 +35,7 @@ Visit https://luxen-ui.com to explore the documentation.
|
|
|
35
35
|
| Carousel item | `<l-carousel-item>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
36
36
|
| Checkbox | `<input>` | ⏣ Native HTML Element |
|
|
37
37
|
| Close button | `<button>` | ⏣ Native HTML Element |
|
|
38
|
+
| Color scheme icon | `<l-color-scheme-icon>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
38
39
|
| Combobox | `<l-combobox>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
39
40
|
| Dialog | `<l-dialog>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
40
41
|
| Disclosure | `<details>` | ⏣ Native HTML Element |
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page-level light/dark state, shared by every control that shows or changes it.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately not an element: the scheme is one value per document, and a
|
|
5
|
+
* document can hold several controls for it — a header button, a menu row, a
|
|
6
|
+
* settings page. Whichever one the user touches, all of them have to agree, so
|
|
7
|
+
* the state belongs to the page rather than to any of them.
|
|
8
|
+
*
|
|
9
|
+
* The model is the one argued in Lea Verou's *Dark mode toggles should be a
|
|
10
|
+
* two-state switch* (2026): three values — an explicit `light`, an explicit
|
|
11
|
+
* `dark`, or no preference at all — but only two ever shown. That falls out of
|
|
12
|
+
* two rules, both implemented here:
|
|
13
|
+
*
|
|
14
|
+
* 1. An override is stored **only when it differs from the OS preference**.
|
|
15
|
+
* Choosing the scheme the OS already reports removes the override instead,
|
|
16
|
+
* handing control back to the system. So a user on a dark OS who switches to
|
|
17
|
+
* light and back is following the system again, not pinned to dark — and
|
|
18
|
+
* "system" stays reachable without a third button.
|
|
19
|
+
* 2. The stored value is read **only when the user acts**. If the OS flips while
|
|
20
|
+
* an explicit override exists, the override survives: the user asked for it,
|
|
21
|
+
* and nothing they did says otherwise.
|
|
22
|
+
*
|
|
23
|
+
* SSR-safe: nothing here touches `window`, `document` or `localStorage` at
|
|
24
|
+
* module scope, and listeners are attached lazily on the first subscription.
|
|
25
|
+
*/
|
|
26
|
+
export type ColorScheme = 'light' | 'dark';
|
|
27
|
+
/** Called whenever the effective scheme changes, from any source. */
|
|
28
|
+
export type ColorSchemeListener = (scheme: ColorScheme, overridden: boolean) => void;
|
|
29
|
+
/** Where the store writes `color-scheme`, if anywhere. */
|
|
30
|
+
export type ColorSchemeApply = 'root' | false;
|
|
31
|
+
export interface ColorSchemeConfig {
|
|
32
|
+
/**
|
|
33
|
+
* `localStorage` key holding the override. Set to `''` to disable persistence
|
|
34
|
+
* entirely — the choice then lasts for the session only.
|
|
35
|
+
*/
|
|
36
|
+
storageKey?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Write `color-scheme` on `<html>` when the scheme changes. Off by default:
|
|
39
|
+
* most applications already own a color-mode story, and silently rewriting
|
|
40
|
+
* `documentElement` would fight it. Turn it on for a plain page.
|
|
41
|
+
*/
|
|
42
|
+
apply?: ColorSchemeApply;
|
|
43
|
+
}
|
|
44
|
+
declare class ColorSchemeStore {
|
|
45
|
+
#private;
|
|
46
|
+
/** The scheme actually in effect: a stored override, else the OS preference. */
|
|
47
|
+
get current(): ColorScheme;
|
|
48
|
+
/** Whether an override is in place, rather than the OS preference being followed. */
|
|
49
|
+
get overridden(): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Choose a scheme. Rule 1 lives here: the override is written only when it
|
|
52
|
+
* disagrees with the OS, and released when it agrees.
|
|
53
|
+
*/
|
|
54
|
+
set(next: ColorScheme): void;
|
|
55
|
+
/** Flip to the other scheme. Returns the scheme now in effect. */
|
|
56
|
+
toggle(): ColorScheme;
|
|
57
|
+
/**
|
|
58
|
+
* Observe the effective scheme. The listener is called **immediately with the
|
|
59
|
+
* current value**, then on a user change, on an OS change with no override in
|
|
60
|
+
* place, and on a change made in another tab. Returns an unsubscribe function;
|
|
61
|
+
* the last unsubscribe releases the listeners.
|
|
62
|
+
*
|
|
63
|
+
* The immediate call is deliberate. A subscriber's job is almost always to
|
|
64
|
+
* mirror the scheme somewhere — an `aria-pressed`, a `checked`, a class — and
|
|
65
|
+
* a change-only contract leaves that mirror wrong until the first change,
|
|
66
|
+
* which is a bug nobody sees until a screen reader hits it. Callers that only
|
|
67
|
+
* want transitions can ignore the first call.
|
|
68
|
+
*/
|
|
69
|
+
subscribe(listener: ColorSchemeListener): () => void;
|
|
70
|
+
/** Set the storage key and whether the store writes `color-scheme` on `<html>`. */
|
|
71
|
+
configure(config: ColorSchemeConfig): void;
|
|
72
|
+
}
|
|
73
|
+
/** The document's light/dark state. One instance per page. */
|
|
74
|
+
export declare const colorScheme: ColorSchemeStore;
|
|
75
|
+
export {};
|
|
76
|
+
//# sourceMappingURL=color-scheme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-scheme.d.ts","sourceRoot":"","sources":["../src/html/color-scheme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3C,qEAAqE;AACrE,MAAM,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;AAErF,0DAA0D;AAC1D,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,KAAK,CAAC;AAE9C,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;CAC1B;AA8BD,cAAM,gBAAgB;;IAuBpB,gFAAgF;IAChF,IAAI,OAAO,IAAI,WAAW,CAEzB;IAED,qFAAqF;IACrF,IAAI,UAAU,IAAI,OAAO,CAExB;IAMD;;;OAGG;IACH,GAAG,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAuB5B,kEAAkE;IAClE,MAAM,IAAI,WAAW;IAKrB;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,IAAI;IAUpD,mFAAmF;IACnF,SAAS,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;CAuD3C;AAED,8DAA8D;AAC9D,eAAO,MAAM,WAAW,kBAAyB,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=`luxen-color-scheme`,t=!1,n=`(prefers-color-scheme: dark)`;function r(){return globalThis.matchMedia?.(n).matches?`dark`:`light`}function i(e){if(!e)return null;try{let t=localStorage.getItem(e);return t===`light`||t===`dark`?t:null}catch{return null}}var a=new class{#e=null;#t=new Set;#n=null;#r=null;#i=!1;#a=()=>this.#l();#o=t=>{t.key!==null&&t.key!==e||(e&&(this.#e=null),this.#l())};get current(){return this.#s??r()}get overridden(){return this.#s!==null}get#s(){return i(e)??this.#e}set(t){let n=t===r();if(e)try{n?localStorage.removeItem(e):localStorage.setItem(e,t),this.#e=null,this.#c(),this.#l();return}catch{}this.#e=n?null:t,this.#c(),this.#l()}toggle(){return this.set(this.current===`dark`?`light`:`dark`),this.current}subscribe(e){return this.#t.add(e),this.#u(),e(this.current,this.overridden),()=>{this.#t.delete(e),this.#t.size===0&&this.#d()}}configure(n){if(n.storageKey!==void 0&&(e=n.storageKey),n.apply!==void 0){let e=t===`root`;t=n.apply,e&&n.apply===!1&&globalThis.document?.documentElement.style.removeProperty(`color-scheme`)}this.#c(),this.#l()}#c(){if(t!==`root`)return;let e=globalThis.document?.documentElement;e&&(e.style.colorScheme=this.#s??`light dark`)}#l(){let e=this.current,t=this.overridden;if(!(e===this.#r&&t===this.#i)){this.#r=e,this.#i=t;for(let n of this.#t)n(e,t)}}#u(){this.#n||(this.#n=globalThis.matchMedia?.(n)??null,this.#n?.addEventListener(`change`,this.#a),globalThis.addEventListener?.(`storage`,this.#o),this.#r=this.current,this.#i=this.overridden,this.#c())}#d(){this.#n?.removeEventListener(`change`,this.#a),this.#n=null,globalThis.removeEventListener?.(`storage`,this.#o)}};export{a as colorScheme};
|
|
2
|
+
//# sourceMappingURL=color-scheme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-scheme.js","names":["#notify","#session","#override","#applyToRoot","#listeners","#bind","#unbind","#last","#lastOverridden","#media","#onOsChange","#onStorageChange"],"sources":["../src/html/color-scheme.ts"],"sourcesContent":["/**\n * Page-level light/dark state, shared by every control that shows or changes it.\n *\n * Deliberately not an element: the scheme is one value per document, and a\n * document can hold several controls for it — a header button, a menu row, a\n * settings page. Whichever one the user touches, all of them have to agree, so\n * the state belongs to the page rather than to any of them.\n *\n * The model is the one argued in Lea Verou's *Dark mode toggles should be a\n * two-state switch* (2026): three values — an explicit `light`, an explicit\n * `dark`, or no preference at all — but only two ever shown. That falls out of\n * two rules, both implemented here:\n *\n * 1. An override is stored **only when it differs from the OS preference**.\n * Choosing the scheme the OS already reports removes the override instead,\n * handing control back to the system. So a user on a dark OS who switches to\n * light and back is following the system again, not pinned to dark — and\n * \"system\" stays reachable without a third button.\n * 2. The stored value is read **only when the user acts**. If the OS flips while\n * an explicit override exists, the override survives: the user asked for it,\n * and nothing they did says otherwise.\n *\n * SSR-safe: nothing here touches `window`, `document` or `localStorage` at\n * module scope, and listeners are attached lazily on the first subscription.\n */\n\nexport type ColorScheme = 'light' | 'dark';\n\n/** Called whenever the effective scheme changes, from any source. */\nexport type ColorSchemeListener = (scheme: ColorScheme, overridden: boolean) => void;\n\n/** Where the store writes `color-scheme`, if anywhere. */\nexport type ColorSchemeApply = 'root' | false;\n\nexport interface ColorSchemeConfig {\n /**\n * `localStorage` key holding the override. Set to `''` to disable persistence\n * entirely — the choice then lasts for the session only.\n */\n storageKey?: string;\n /**\n * Write `color-scheme` on `<html>` when the scheme changes. Off by default:\n * most applications already own a color-mode story, and silently rewriting\n * `documentElement` would fight it. Turn it on for a plain page.\n */\n apply?: ColorSchemeApply;\n}\n\n/*\n * Defaults live as module-level bindings, not class fields, so that\n * `luxen-ui/vite-plugin` can bake a project's `luxen.config.mjs` values in at\n * build time by rewriting these two initialisers — the same mechanism it uses\n * for `_elementPrefix` / `_cssPrefix` in `registry.ts`. `configure()` stays the\n * runtime path for anything the build cannot know.\n */\nlet _storageKey = 'luxen-color-scheme';\nlet _apply: ColorSchemeApply = false;\n\nconst DARK_QUERY = '(prefers-color-scheme: dark)';\n\n/** The OS-level preference. `light` when the query is unsupported. */\nfunction osScheme(): ColorScheme {\n return globalThis.matchMedia?.(DARK_QUERY).matches ? 'dark' : 'light';\n}\n\nfunction readStored(key: string): ColorScheme | null {\n if (!key) return null;\n try {\n const value = localStorage.getItem(key);\n return value === 'light' || value === 'dark' ? value : null;\n } catch {\n // Safari private mode, disabled storage, sandboxed iframe.\n return null;\n }\n}\n\nclass ColorSchemeStore {\n /**\n * In-memory copy of the override, used when persistence is off or when the\n * browser refuses to write. Without it a blocked write would leave every\n * control unable to move at all.\n */\n #session: ColorScheme | null = null;\n #listeners = new Set<ColorSchemeListener>();\n #media: MediaQueryList | null = null;\n #last: ColorScheme | null = null;\n #lastOverridden = false;\n\n #onOsChange = () => this.#notify();\n\n #onStorageChange = (event: StorageEvent) => {\n // `key === null` means the whole store was cleared.\n if (event.key !== null && event.key !== _storageKey) return;\n // Another tab is the authority now: drop this page's session copy so a\n // release made over there is not undone by a stale local value.\n if (_storageKey) this.#session = null;\n this.#notify();\n };\n\n /** The scheme actually in effect: a stored override, else the OS preference. */\n get current(): ColorScheme {\n return this.#override ?? osScheme();\n }\n\n /** Whether an override is in place, rather than the OS preference being followed. */\n get overridden(): boolean {\n return this.#override !== null;\n }\n\n get #override(): ColorScheme | null {\n return readStored(_storageKey) ?? this.#session;\n }\n\n /**\n * Choose a scheme. Rule 1 lives here: the override is written only when it\n * disagrees with the OS, and released when it agrees.\n */\n set(next: ColorScheme): void {\n const release = next === osScheme();\n\n if (_storageKey) {\n try {\n if (release) localStorage.removeItem(_storageKey);\n else localStorage.setItem(_storageKey, next);\n // Storage took it, so it is the single authority — drop any session copy\n // left over from a period when it was unavailable.\n this.#session = null;\n this.#applyToRoot();\n this.#notify();\n return;\n } catch {\n // Refused — fall through to memory.\n }\n }\n\n this.#session = release ? null : next;\n this.#applyToRoot();\n this.#notify();\n }\n\n /** Flip to the other scheme. Returns the scheme now in effect. */\n toggle(): ColorScheme {\n this.set(this.current === 'dark' ? 'light' : 'dark');\n return this.current;\n }\n\n /**\n * Observe the effective scheme. The listener is called **immediately with the\n * current value**, then on a user change, on an OS change with no override in\n * place, and on a change made in another tab. Returns an unsubscribe function;\n * the last unsubscribe releases the listeners.\n *\n * The immediate call is deliberate. A subscriber's job is almost always to\n * mirror the scheme somewhere — an `aria-pressed`, a `checked`, a class — and\n * a change-only contract leaves that mirror wrong until the first change,\n * which is a bug nobody sees until a screen reader hits it. Callers that only\n * want transitions can ignore the first call.\n */\n subscribe(listener: ColorSchemeListener): () => void {\n this.#listeners.add(listener);\n this.#bind();\n listener(this.current, this.overridden);\n return () => {\n this.#listeners.delete(listener);\n if (this.#listeners.size === 0) this.#unbind();\n };\n }\n\n /** Set the storage key and whether the store writes `color-scheme` on `<html>`. */\n configure(config: ColorSchemeConfig): void {\n if (config.storageKey !== undefined) _storageKey = config.storageKey;\n\n if (config.apply !== undefined) {\n const wasApplying = _apply === 'root';\n _apply = config.apply;\n // Leave no trace when application is switched off.\n if (wasApplying && config.apply === false) {\n globalThis.document?.documentElement.style.removeProperty('color-scheme');\n }\n }\n\n this.#applyToRoot();\n this.#notify();\n }\n\n /**\n * With no override, writes `light dark` rather than nothing: it declares\n * support for both schemes, so the used value follows `prefers-color-scheme`\n * and \"follow the system\" comes from the platform instead of being simulated.\n */\n #applyToRoot(): void {\n if (_apply !== 'root') return;\n const root = globalThis.document?.documentElement;\n if (root) root.style.colorScheme = this.#override ?? 'light dark';\n }\n\n #notify(): void {\n const next = this.current;\n const overridden = this.overridden;\n // Both halves of the payload are compared, not just the scheme: releasing an\n // override that already agreed with the OS leaves `current` untouched while\n // `overridden` flips, and a subscriber mirroring it — a \"following the\n // system\" badge, a tri-state settings control — would never hear about it.\n if (next === this.#last && overridden === this.#lastOverridden) return;\n this.#last = next;\n this.#lastOverridden = overridden;\n for (const listener of this.#listeners) listener(next, overridden);\n }\n\n #bind(): void {\n if (this.#media) return;\n this.#media = globalThis.matchMedia?.(DARK_QUERY) ?? null;\n this.#media?.addEventListener('change', this.#onOsChange);\n globalThis.addEventListener?.('storage', this.#onStorageChange);\n this.#last = this.current;\n this.#lastOverridden = this.overridden;\n this.#applyToRoot();\n }\n\n #unbind(): void {\n this.#media?.removeEventListener('change', this.#onOsChange);\n this.#media = null;\n globalThis.removeEventListener?.('storage', this.#onStorageChange);\n }\n}\n\n/** The document's light/dark state. One instance per page. */\nexport const colorScheme = new ColorSchemeStore();\n"],"mappings":"AAuDA,IAAI,EAAc,qBACd,EAA2B,GAEzB,EAAa,+BAGnB,SAAS,GAAwB,CAC/B,OAAO,WAAW,aAAa,CAAU,CAAC,CAAC,QAAU,OAAS,OAChE,CAEA,SAAS,EAAW,EAAiC,CACnD,GAAI,CAAC,EAAK,OAAO,KACjB,GAAI,CACF,IAAM,EAAQ,aAAa,QAAQ,CAAG,EACtC,OAAO,IAAU,SAAW,IAAU,OAAS,EAAQ,IACzD,MAAQ,CAEN,OAAO,IACT,CACF,CAyJA,IAAa,EAAc,IAAI,KAvJR,CAMrB,GAA+B,KAC/B,GAAa,IAAI,IACjB,GAAgC,KAChC,GAA4B,KAC5B,GAAkB,GAElB,OAAoB,KAAKA,GAAQ,EAEjC,GAAoB,GAAwB,CAEtC,EAAM,MAAQ,MAAQ,EAAM,MAAQ,IAGpC,IAAa,KAAKC,GAAW,MACjC,KAAKD,GAAQ,EACf,EAGA,IAAI,SAAuB,CACzB,OAAO,KAAKE,IAAa,EAAS,CACpC,CAGA,IAAI,YAAsB,CACxB,OAAO,KAAKA,KAAc,IAC5B,CAEA,GAAIA,IAAgC,CAClC,OAAO,EAAW,CAAW,GAAK,KAAKD,EACzC,CAMA,IAAI,EAAyB,CAC3B,IAAM,EAAU,IAAS,EAAS,EAElC,GAAI,EACF,GAAI,CACE,EAAS,aAAa,WAAW,CAAW,EAC3C,aAAa,QAAQ,EAAa,CAAI,EAG3C,KAAKA,GAAW,KAChB,KAAKE,GAAa,EAClB,KAAKH,GAAQ,EACb,MACF,MAAQ,CAER,CAGF,KAAKC,GAAW,EAAU,KAAO,EACjC,KAAKE,GAAa,EAClB,KAAKH,GAAQ,CACf,CAGA,QAAsB,CAEpB,OADA,KAAK,IAAI,KAAK,UAAY,OAAS,QAAU,MAAM,EAC5C,KAAK,OACd,CAcA,UAAU,EAA2C,CAInD,OAHA,KAAKI,GAAW,IAAI,CAAQ,EAC5B,KAAKC,GAAM,EACX,EAAS,KAAK,QAAS,KAAK,UAAU,MACzB,CACX,KAAKD,GAAW,OAAO,CAAQ,EAC3B,KAAKA,GAAW,OAAS,GAAG,KAAKE,GAAQ,CAC/C,CACF,CAGA,UAAU,EAAiC,CAGzC,GAFI,EAAO,aAAe,IAAA,KAAW,EAAc,EAAO,YAEtD,EAAO,QAAU,IAAA,GAAW,CAC9B,IAAM,EAAc,IAAW,OAC/B,EAAS,EAAO,MAEZ,GAAe,EAAO,QAAU,IAClC,WAAW,UAAU,gBAAgB,MAAM,eAAe,cAAc,CAE5E,CAEA,KAAKH,GAAa,EAClB,KAAKH,GAAQ,CACf,CAOA,IAAqB,CACnB,GAAI,IAAW,OAAQ,OACvB,IAAM,EAAO,WAAW,UAAU,gBAC9B,IAAM,EAAK,MAAM,YAAc,KAAKE,IAAa,aACvD,CAEA,IAAgB,CACd,IAAM,EAAO,KAAK,QACZ,EAAa,KAAK,WAKpB,SAAS,KAAKK,IAAS,IAAe,KAAKC,IAE/C,CADA,KAAKD,GAAQ,EACb,KAAKC,GAAkB,EACvB,IAAK,IAAM,KAAY,KAAKJ,GAAY,EAAS,EAAM,CAAU,CAD1C,CAEzB,CAEA,IAAc,CACR,KAAKK,KACT,KAAKA,GAAS,WAAW,aAAa,CAAU,GAAK,KACrD,KAAKA,IAAQ,iBAAiB,SAAU,KAAKC,EAAW,EACxD,WAAW,mBAAmB,UAAW,KAAKC,EAAgB,EAC9D,KAAKJ,GAAQ,KAAK,QAClB,KAAKC,GAAkB,KAAK,WAC5B,KAAKL,GAAa,EACpB,CAEA,IAAgB,CACd,KAAKM,IAAQ,oBAAoB,SAAU,KAAKC,EAAW,EAC3D,KAAKD,GAAS,KACd,WAAW,sBAAsB,UAAW,KAAKE,EAAgB,CACnE,CACF"}
|
package/cdn/config.d.ts
CHANGED
|
@@ -43,6 +43,24 @@ export interface LuxenEmitTypesConfig {
|
|
|
43
43
|
/** Overwrite the file even when it exists. Default `false`. */
|
|
44
44
|
force?: boolean;
|
|
45
45
|
}
|
|
46
|
+
/** Build-time defaults for the `luxen-ui/color-scheme` store. */
|
|
47
|
+
export interface LuxenColorSchemeConfig {
|
|
48
|
+
/**
|
|
49
|
+
* `localStorage` key holding the light/dark override. Default
|
|
50
|
+
* `'luxen-color-scheme'`. Set to `''` to disable persistence entirely.
|
|
51
|
+
*
|
|
52
|
+
* Change it here and the before-first-paint snippet in your `<head>` has to
|
|
53
|
+
* read the same key — that snippet runs before any module and cannot be
|
|
54
|
+
* rewritten by the build.
|
|
55
|
+
*/
|
|
56
|
+
storageKey?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Have the store write `color-scheme` on `<html>` itself. Default `false`,
|
|
59
|
+
* because most applications already own a color-mode story; `'root'` is the
|
|
60
|
+
* batteries-included path for a plain page.
|
|
61
|
+
*/
|
|
62
|
+
apply?: 'root' | false;
|
|
63
|
+
}
|
|
46
64
|
/**
|
|
47
65
|
* Shape of `luxen.config.mjs`. Both the Vite plugin and the CLI read this
|
|
48
66
|
* file; each tool ignores the fields that don't apply to it.
|
|
@@ -66,6 +84,11 @@ export interface LuxenConfig {
|
|
|
66
84
|
cssImportPath?: string;
|
|
67
85
|
/** npm specifier used in `import` examples in `references/`. Default `'luxen-ui'`. */
|
|
68
86
|
jsImportPath?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Defaults for `luxen-ui/color-scheme`, baked in at build time so a project
|
|
89
|
+
* never has to call `colorScheme.configure()` from its entry point.
|
|
90
|
+
*/
|
|
91
|
+
colorScheme?: LuxenColorSchemeConfig;
|
|
69
92
|
/**
|
|
70
93
|
* Emit a prefix-aware `.d.ts` for `HTMLElementTagNameMap` (and optionally
|
|
71
94
|
* Vue's `GlobalComponents`). Pass a path string for the default behaviour,
|
package/cdn/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/html/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,mDAAmD;AACnD,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAE1C,kEAAkE;AAClE,MAAM,WAAW,oBAAoB;IACnC,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAC3C;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAE1B,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,uDAAuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2FAA2F;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,oBAAoB,CAAC;CAC3C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,WAAW,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAEhE"}
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/html/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,mDAAmD;AACnD,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAE1C,kEAAkE;AAClE,MAAM,WAAW,oBAAoB;IACnC,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAC3C;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,iEAAiE;AACjE,MAAM,WAAW,sBAAsB;IACrC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAE1B,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,uDAAuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2FAA2F;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB;;;OAGG;IACH,WAAW,CAAC,EAAE,sBAAsB,CAAC;IAGrC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,oBAAoB,CAAC;CAC3C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,WAAW,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAEhE"}
|
package/cdn/custom-elements.json
CHANGED
|
@@ -2205,6 +2205,121 @@
|
|
|
2205
2205
|
}
|
|
2206
2206
|
]
|
|
2207
2207
|
},
|
|
2208
|
+
{
|
|
2209
|
+
"kind": "javascript-module",
|
|
2210
|
+
"path": "src/html/elements/color-scheme-icon/color-scheme-icon.ts",
|
|
2211
|
+
"declarations": [
|
|
2212
|
+
{
|
|
2213
|
+
"kind": "class",
|
|
2214
|
+
"description": "",
|
|
2215
|
+
"name": "ColorSchemeIcon",
|
|
2216
|
+
"cssProperties": [
|
|
2217
|
+
{
|
|
2218
|
+
"description": "Width and height of the glyph.",
|
|
2219
|
+
"name": "--size",
|
|
2220
|
+
"default": "1em"
|
|
2221
|
+
},
|
|
2222
|
+
{
|
|
2223
|
+
"description": "Glyph color.",
|
|
2224
|
+
"name": "--color",
|
|
2225
|
+
"default": "currentColor"
|
|
2226
|
+
}
|
|
2227
|
+
],
|
|
2228
|
+
"cssParts": [
|
|
2229
|
+
{
|
|
2230
|
+
"description": "The root `<svg>`, for overriding the morph's timing or tilt.",
|
|
2231
|
+
"name": "base"
|
|
2232
|
+
}
|
|
2233
|
+
],
|
|
2234
|
+
"members": [
|
|
2235
|
+
{
|
|
2236
|
+
"kind": "field",
|
|
2237
|
+
"name": "scheme",
|
|
2238
|
+
"type": {
|
|
2239
|
+
"text": "ColorScheme | undefined"
|
|
2240
|
+
},
|
|
2241
|
+
"description": "Which glyph to show. Leave it unset and the icon follows the page's scheme\n(`colorScheme.current` — a stored override, else the OS preference), so it\nstays in step with every other control without wiring. Set it to pin the\nglyph, for a legend or a preview that must not move.",
|
|
2242
|
+
"attribute": "scheme",
|
|
2243
|
+
"reflects": true
|
|
2244
|
+
},
|
|
2245
|
+
{
|
|
2246
|
+
"kind": "field",
|
|
2247
|
+
"name": "label",
|
|
2248
|
+
"type": {
|
|
2249
|
+
"text": "string | undefined"
|
|
2250
|
+
},
|
|
2251
|
+
"description": "Accessible label. When set, the glyph becomes meaningful (`role=\"img\"`);\nwhen absent it is decorative and hidden from assistive tech — the right\ndefault inside a row or button that already carries the name.",
|
|
2252
|
+
"attribute": "label"
|
|
2253
|
+
}
|
|
2254
|
+
],
|
|
2255
|
+
"attributes": [
|
|
2256
|
+
{
|
|
2257
|
+
"name": "scheme",
|
|
2258
|
+
"type": {
|
|
2259
|
+
"text": "ColorScheme | undefined"
|
|
2260
|
+
},
|
|
2261
|
+
"description": "Which glyph to show. Leave it unset and the icon follows the page's scheme\n(`colorScheme.current` — a stored override, else the OS preference), so it\nstays in step with every other control without wiring. Set it to pin the\nglyph, for a legend or a preview that must not move.",
|
|
2262
|
+
"fieldName": "scheme"
|
|
2263
|
+
},
|
|
2264
|
+
{
|
|
2265
|
+
"name": "label",
|
|
2266
|
+
"type": {
|
|
2267
|
+
"text": "string | undefined"
|
|
2268
|
+
},
|
|
2269
|
+
"description": "Accessible label. When set, the glyph becomes meaningful (`role=\"img\"`);\nwhen absent it is decorative and hidden from assistive tech — the right\ndefault inside a row or button that already carries the name.",
|
|
2270
|
+
"fieldName": "label"
|
|
2271
|
+
}
|
|
2272
|
+
],
|
|
2273
|
+
"superclass": {
|
|
2274
|
+
"name": "LuxenElement",
|
|
2275
|
+
"module": "/src/html/shared/luxen-element.js"
|
|
2276
|
+
},
|
|
2277
|
+
"tagName": "l-color-scheme-icon",
|
|
2278
|
+
"customElement": true,
|
|
2279
|
+
"summary": "A sun that morphs into a moon. Purely presentational: it shows a\ncolor scheme, it does not choose or store one.\n\nThe two glyphs are a single shape — the disc grows, a mask slides in to carve\nthe crescent, and the rays retract in a ripple — so the change reads as one\nobject turning rather than two icons swapping. That is why this is authored\nSVG rather than an `<l-icon>`: an Iconify icon is opaque, and you cannot grow\none into another or animate a mask inside it.\n\nUse it wherever a scheme needs a glyph and the surrounding element is already\nthe control — a `menuitemcheckbox` row, a button, a legend. With no `scheme`\nit follows the page's scheme from `luxen-ui/color-scheme`, so it needs no\nwiring; set `scheme` to pin it.",
|
|
2280
|
+
"examples": [
|
|
2281
|
+
{
|
|
2282
|
+
"title": "In a menu row, where the row is the control",
|
|
2283
|
+
"language": "html",
|
|
2284
|
+
"code": "```html\n<l-dropdown-item type=\"checkbox\" checked>\n<l-color-scheme-icon slot=\"prefix\" scheme=\"dark\"></l-color-scheme-icon>\nDark theme\n</l-dropdown-item>\n```"
|
|
2285
|
+
}
|
|
2286
|
+
]
|
|
2287
|
+
}
|
|
2288
|
+
],
|
|
2289
|
+
"exports": [
|
|
2290
|
+
{
|
|
2291
|
+
"kind": "js",
|
|
2292
|
+
"name": "ColorScheme",
|
|
2293
|
+
"declaration": {
|
|
2294
|
+
"name": "ColorScheme",
|
|
2295
|
+
"module": "src/html/elements/color-scheme-icon/color-scheme-icon.ts"
|
|
2296
|
+
}
|
|
2297
|
+
},
|
|
2298
|
+
{
|
|
2299
|
+
"kind": "js",
|
|
2300
|
+
"name": "ColorSchemeIcon",
|
|
2301
|
+
"declaration": {
|
|
2302
|
+
"name": "ColorSchemeIcon",
|
|
2303
|
+
"module": "src/html/elements/color-scheme-icon/color-scheme-icon.ts"
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
]
|
|
2307
|
+
},
|
|
2308
|
+
{
|
|
2309
|
+
"kind": "javascript-module",
|
|
2310
|
+
"path": "src/html/elements/color-scheme-icon/index.ts",
|
|
2311
|
+
"declarations": [],
|
|
2312
|
+
"exports": [
|
|
2313
|
+
{
|
|
2314
|
+
"kind": "js",
|
|
2315
|
+
"name": "*",
|
|
2316
|
+
"declaration": {
|
|
2317
|
+
"name": "*",
|
|
2318
|
+
"module": "src/html/elements/color-scheme-icon/color-scheme-icon.js"
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
]
|
|
2322
|
+
},
|
|
2208
2323
|
{
|
|
2209
2324
|
"kind": "javascript-module",
|
|
2210
2325
|
"path": "src/html/elements/combobox/combobox.ts",
|
|
@@ -3706,7 +3821,7 @@
|
|
|
3706
3821
|
"name": ""
|
|
3707
3822
|
},
|
|
3708
3823
|
{
|
|
3709
|
-
"description": "Leading content (e.g. icon).",
|
|
3824
|
+
"description": "Leading content (e.g. icon). A `type=\"checkbox\"` item ignores it unless `check-placement=\"end\"` frees the leading column.",
|
|
3710
3825
|
"name": "prefix"
|
|
3711
3826
|
},
|
|
3712
3827
|
{
|
|
@@ -3783,6 +3898,17 @@
|
|
|
3783
3898
|
"attribute": "submenu-open",
|
|
3784
3899
|
"reflects": true
|
|
3785
3900
|
},
|
|
3901
|
+
{
|
|
3902
|
+
"kind": "field",
|
|
3903
|
+
"name": "checkPlacement",
|
|
3904
|
+
"type": {
|
|
3905
|
+
"text": "'start' | 'end'"
|
|
3906
|
+
},
|
|
3907
|
+
"default": "'start'",
|
|
3908
|
+
"description": "Which side of a `type=\"checkbox\"` item carries the check mark. `start` (the\ndefault) puts it in the leading column, where it takes the place of any\n`prefix`. `end` moves it to the trailing edge, freeing that column so the\nitem can show its own icon alongside the state.",
|
|
3909
|
+
"attribute": "check-placement",
|
|
3910
|
+
"reflects": true
|
|
3911
|
+
},
|
|
3786
3912
|
{
|
|
3787
3913
|
"kind": "field",
|
|
3788
3914
|
"name": "hasSubmenu",
|
|
@@ -3877,6 +4003,15 @@
|
|
|
3877
4003
|
"default": "false",
|
|
3878
4004
|
"description": "Whether this item's submenu is open. Managed by the parent `l-dropdown`.",
|
|
3879
4005
|
"fieldName": "submenuOpen"
|
|
4006
|
+
},
|
|
4007
|
+
{
|
|
4008
|
+
"name": "check-placement",
|
|
4009
|
+
"type": {
|
|
4010
|
+
"text": "'start' | 'end'"
|
|
4011
|
+
},
|
|
4012
|
+
"default": "'start'",
|
|
4013
|
+
"description": "Which side of a `type=\"checkbox\"` item carries the check mark. `start` (the\ndefault) puts it in the leading column, where it takes the place of any\n`prefix`. `end` moves it to the trailing edge, freeing that column so the\nitem can show its own icon alongside the state.",
|
|
4014
|
+
"fieldName": "checkPlacement"
|
|
3880
4015
|
}
|
|
3881
4016
|
],
|
|
3882
4017
|
"superclass": {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ColorScheme } from '../../color-scheme.js';
|
|
2
|
+
import { LuxenElement } from '../../shared/luxen-element.js';
|
|
3
|
+
export type { ColorScheme };
|
|
4
|
+
/**
|
|
5
|
+
* @summary A sun that morphs into a moon. Purely presentational: it shows a
|
|
6
|
+
* color scheme, it does not choose or store one.
|
|
7
|
+
*
|
|
8
|
+
* The two glyphs are a single shape — the disc grows, a mask slides in to carve
|
|
9
|
+
* the crescent, and the rays retract in a ripple — so the change reads as one
|
|
10
|
+
* object turning rather than two icons swapping. That is why this is authored
|
|
11
|
+
* SVG rather than an `<l-icon>`: an Iconify icon is opaque, and you cannot grow
|
|
12
|
+
* one into another or animate a mask inside it.
|
|
13
|
+
*
|
|
14
|
+
* Use it wherever a scheme needs a glyph and the surrounding element is already
|
|
15
|
+
* the control — a `menuitemcheckbox` row, a button, a legend. With no `scheme`
|
|
16
|
+
* it follows the page's scheme from `luxen-ui/color-scheme`, so it needs no
|
|
17
|
+
* wiring; set `scheme` to pin it.
|
|
18
|
+
*
|
|
19
|
+
* @example In a menu row, where the row is the control
|
|
20
|
+
* ```html
|
|
21
|
+
* <l-dropdown-item type="checkbox" checked>
|
|
22
|
+
* <l-color-scheme-icon slot="prefix" scheme="dark"></l-color-scheme-icon>
|
|
23
|
+
* Dark theme
|
|
24
|
+
* </l-dropdown-item>
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @cssproperty [--size=1em] - Width and height of the glyph.
|
|
28
|
+
* @cssproperty [--color=currentColor] - Glyph color.
|
|
29
|
+
*
|
|
30
|
+
* @csspart base - The root `<svg>`, for overriding the morph's timing or tilt.
|
|
31
|
+
*
|
|
32
|
+
* @customElement l-color-scheme-icon
|
|
33
|
+
*/
|
|
34
|
+
export declare class ColorSchemeIcon extends LuxenElement {
|
|
35
|
+
static styles: import('lit').CSSResult[];
|
|
36
|
+
/**
|
|
37
|
+
* Which glyph to show. Leave it unset and the icon follows the page's scheme
|
|
38
|
+
* (`colorScheme.current` — a stored override, else the OS preference), so it
|
|
39
|
+
* stays in step with every other control without wiring. Set it to pin the
|
|
40
|
+
* glyph, for a legend or a preview that must not move.
|
|
41
|
+
*/
|
|
42
|
+
scheme?: ColorScheme;
|
|
43
|
+
/**
|
|
44
|
+
* Accessible label. When set, the glyph becomes meaningful (`role="img"`);
|
|
45
|
+
* when absent it is decorative and hidden from assistive tech — the right
|
|
46
|
+
* default inside a row or button that already carries the name.
|
|
47
|
+
*/
|
|
48
|
+
label?: string;
|
|
49
|
+
private _unsubscribe?;
|
|
50
|
+
connectedCallback(): void;
|
|
51
|
+
disconnectedCallback(): void;
|
|
52
|
+
willUpdate(): void;
|
|
53
|
+
render(): import('lit').TemplateResult<1>;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=color-scheme-icon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-scheme-icon.d.ts","sourceRoot":"","sources":["../../../src/html/elements/color-scheme-icon/color-scheme-icon.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAM7D,YAAY,EAAE,WAAW,EAAE,CAAC;AAY5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,OAAgB,MAAM,4BAAwB;IAE9C;;;;;OAKG;IAEH,MAAM,CAAC,EAAE,WAAW,CAAC;IAErB;;;;OAIG;IAEH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,OAAO,CAAC,YAAY,CAAC,CAAa;IAEzB,iBAAiB;IAOjB,oBAAoB;IAMpB,UAAU;IAsBV,MAAM;CA+ChB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import{colorScheme as e}from"../../color-scheme.js";import{i as t}from"../../chunks/lit.js";import{i as n,t as r}from"../../chunks/decorate.js";import{a as i,o as a}from"../../chunks/lit-html.js";import{LuxenElement as o}from"../../shared/luxen-element.js";import s from"../../shared/styles/host.styles.js";var c=t(`:host{--size:1em;--color:currentColor;inline-size:var(--size);block-size:var(--size);color:var(--color);flex:none;display:inline-flex}svg{fill:currentColor;block-size:100%;inline-size:100%;transition:rotate .5s cubic-bezier(.16,1,.3,1);rotate:0deg}.disc,.ray,.biter{transform-box:fill-box;transform-origin:50%}.disc{transition:scale .5s cubic-bezier(.16,1,.3,1);scale:.56}.biter{transition:translate .5s cubic-bezier(.16,1,.3,1);translate:16px -16px}.ray{transition:scale .3s cubic-bezier(.34,1.56,.64,1);transition-delay:calc(var(--i) * 18ms);scale:1}:host([data-scheme=dark]) svg{rotate:40deg}:host([data-scheme=dark]) .disc{scale:1}:host([data-scheme=dark]) .biter{translate:5px -5px}:host([data-scheme=dark]) .ray{scale:0}@media (prefers-reduced-motion:reduce){svg,.disc,.biter,.ray{transition-duration:0s;transition-delay:0s}}`),l=Array.from({length:8},(e,t)=>{let n=t*Math.PI/4;return{i:t,cx:(12+Math.cos(n)*9.2).toFixed(2),cy:(12+Math.sin(n)*9.2).toFixed(2)}}),u=class extends o{static{this.styles=[s,c]}connectedCallback(){super.connectedCallback(),this._unsubscribe=e.subscribe(()=>this.requestUpdate())}disconnectedCallback(){super.disconnectedCallback(),this._unsubscribe?.(),this._unsubscribe=void 0}willUpdate(){this.dataset.scheme=this.scheme??e.current,this.label?(this.setAttribute(`role`,`img`),this.setAttribute(`aria-label`,this.label),this.removeAttribute(`aria-hidden`)):(this.removeAttribute(`role`),this.removeAttribute(`aria-label`),this.setAttribute(`aria-hidden`,`true`))}render(){return i`
|
|
2
|
+
<svg
|
|
3
|
+
part="base"
|
|
4
|
+
viewBox="0 0 24 24"
|
|
5
|
+
aria-hidden="true"
|
|
6
|
+
>
|
|
7
|
+
<mask id="crescent">
|
|
8
|
+
<rect
|
|
9
|
+
width="24"
|
|
10
|
+
height="24"
|
|
11
|
+
fill="#fff"
|
|
12
|
+
/>
|
|
13
|
+
<circle
|
|
14
|
+
class="biter"
|
|
15
|
+
cx="12"
|
|
16
|
+
cy="12"
|
|
17
|
+
r="11"
|
|
18
|
+
fill="#000"
|
|
19
|
+
/>
|
|
20
|
+
</mask>
|
|
21
|
+
|
|
22
|
+
<circle
|
|
23
|
+
class="disc"
|
|
24
|
+
cx="12"
|
|
25
|
+
cy="12"
|
|
26
|
+
r="9"
|
|
27
|
+
mask="url(#crescent)"
|
|
28
|
+
/>
|
|
29
|
+
|
|
30
|
+
<!-- \`svg\`, not \`html\`: a nested template is parsed as HTML even when it
|
|
31
|
+
sits inside an <svg>, so these circles would land in the HTML
|
|
32
|
+
namespace and render nothing at all. -->
|
|
33
|
+
${l.map(e=>a`
|
|
34
|
+
<circle
|
|
35
|
+
class="ray"
|
|
36
|
+
cx=${e.cx}
|
|
37
|
+
cy=${e.cy}
|
|
38
|
+
r="1.5"
|
|
39
|
+
style="--i:${e.i}"
|
|
40
|
+
/>
|
|
41
|
+
`)}
|
|
42
|
+
</svg>
|
|
43
|
+
`}};r([n({reflect:!0})],u.prototype,`scheme`,void 0),r([n()],u.prototype,`label`,void 0);export{u as ColorSchemeIcon};
|
|
44
|
+
//# sourceMappingURL=color-scheme-icon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-scheme-icon.js","names":[],"sources":["../../../src/html/elements/color-scheme-icon/color-scheme-icon.css?inline","../../../src/html/elements/color-scheme-icon/color-scheme-icon.ts"],"sourcesContent":["/* Sun ↔ moon, morphed rather than swapped.\n\n The two glyphs are one shape: a disc that grows, a mask circle that slides in\n to bite a crescent out of it, and eight ray dots that collapse to nothing.\n Nothing is added or removed between the states — which is the whole reason\n this is authored SVG and not an `<l-icon>`. An Iconify icon is an opaque\n document fetched from a set: you can cross-fade two of them, but you cannot\n grow one into the other or animate a mask inside it.\n\n Shadow DOM is doing real work here beyond encapsulation: the mask needs an\n `id`, and ids are global in light DOM. Two icons on one page would collide and\n the second would render through the first's mask. A shadow root scopes them\n per instance for free. */\n\n:host {\n --size: 1em;\n --color: currentColor;\n\n flex: none;\n display: inline-flex;\n inline-size: var(--size);\n block-size: var(--size);\n color: var(--color);\n}\n\nsvg {\n inline-size: 100%;\n block-size: 100%;\n fill: currentColor;\n /* The tilt is what sells the transition: the glyph turns as it changes, so the\n eye reads one object moving instead of two states cross-fading. */\n rotate: 0deg;\n transition: rotate 500ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n/* Every animated part scales about its own centre. `fill-box` is what makes\n `center` mean the shape's own box rather than the whole viewBox. */\n.disc,\n.ray,\n.biter {\n transform-box: fill-box;\n transform-origin: center;\n}\n\n.disc {\n /* Sun-sized. The dark state grows it to the full radius; the mask then takes\n the bite that turns it into a crescent. */\n scale: 0.56;\n transition: scale 500ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.biter {\n /* Parked far enough out that it clears the sun entirely. The clearance must be\n computed against the disc's *authored* r=9, not the 5.04 the sun appears to\n be: masking happens in the element's own user space, before its `scale` is\n applied, so the mask sees the full-size circle. 16px on both axes puts the\n centres 22.6 apart against a 9+11 sum — the earlier 13px looked ample and\n still bit a notch out of the sun's upper right.\n It slides in rather than fading, so the crescent is carved, never dissolved. */\n translate: 16px -16px;\n transition: translate 500ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.ray {\n scale: 1;\n transition: scale 300ms cubic-bezier(0.34, 1.56, 0.64, 1);\n /* Staggered from the index each ray carries, so they retract and return as a\n ripple around the disc instead of blinking in unison. */\n transition-delay: calc(var(--i) * 18ms);\n}\n\n:host([data-scheme='dark']) {\n svg {\n rotate: 40deg;\n }\n\n .disc {\n scale: 1;\n }\n\n .biter {\n translate: 5px -5px;\n }\n\n .ray {\n scale: 0;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n svg,\n .disc,\n .biter,\n .ray {\n transition-duration: 0ms;\n transition-delay: 0ms;\n }\n}\n","import { html, svg, unsafeCSS } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { colorScheme, type ColorScheme } from '../../color-scheme.js';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './color-scheme-icon.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\nexport type { ColorScheme };\n\n/** Eight ray dots on a circle of radius 9.2 around the glyph's centre. */\nconst RAYS = Array.from({ length: 8 }, (_, i) => {\n const angle = (i * Math.PI) / 4;\n return {\n i,\n cx: (12 + Math.cos(angle) * 9.2).toFixed(2),\n cy: (12 + Math.sin(angle) * 9.2).toFixed(2),\n };\n});\n\n/**\n * @summary A sun that morphs into a moon. Purely presentational: it shows a\n * color scheme, it does not choose or store one.\n *\n * The two glyphs are a single shape — the disc grows, a mask slides in to carve\n * the crescent, and the rays retract in a ripple — so the change reads as one\n * object turning rather than two icons swapping. That is why this is authored\n * SVG rather than an `<l-icon>`: an Iconify icon is opaque, and you cannot grow\n * one into another or animate a mask inside it.\n *\n * Use it wherever a scheme needs a glyph and the surrounding element is already\n * the control — a `menuitemcheckbox` row, a button, a legend. With no `scheme`\n * it follows the page's scheme from `luxen-ui/color-scheme`, so it needs no\n * wiring; set `scheme` to pin it.\n *\n * @example In a menu row, where the row is the control\n * ```html\n * <l-dropdown-item type=\"checkbox\" checked>\n * <l-color-scheme-icon slot=\"prefix\" scheme=\"dark\"></l-color-scheme-icon>\n * Dark theme\n * </l-dropdown-item>\n * ```\n *\n * @cssproperty [--size=1em] - Width and height of the glyph.\n * @cssproperty [--color=currentColor] - Glyph color.\n *\n * @csspart base - The root `<svg>`, for overriding the morph's timing or tilt.\n *\n * @customElement l-color-scheme-icon\n */\nexport class ColorSchemeIcon extends LuxenElement {\n static override styles = [hostStyles, styles];\n\n /**\n * Which glyph to show. Leave it unset and the icon follows the page's scheme\n * (`colorScheme.current` — a stored override, else the OS preference), so it\n * stays in step with every other control without wiring. Set it to pin the\n * glyph, for a legend or a preview that must not move.\n */\n @property({ reflect: true })\n scheme?: ColorScheme;\n\n /**\n * Accessible label. When set, the glyph becomes meaningful (`role=\"img\"`);\n * when absent it is decorative and hidden from assistive tech — the right\n * default inside a row or button that already carries the name.\n */\n @property()\n label?: string;\n\n private _unsubscribe?: () => void;\n\n override connectedCallback() {\n super.connectedCallback();\n // Follows the page's scheme, not the OS directly: an override chosen\n // elsewhere on the page — or in another tab — has to move this glyph too.\n this._unsubscribe = colorScheme.subscribe(() => this.requestUpdate());\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._unsubscribe?.();\n this._unsubscribe = undefined;\n }\n\n override willUpdate() {\n // Set *before* the first render, not after: the shapes are then created\n // already carrying the right values, and a page that loads in dark mode\n // shows a moon instead of a sun animating into one.\n //\n // The CSS keys off `data-scheme` rather than the `scheme` attribute so the\n // inherited value styles exactly like an explicit one — one selector, not two.\n this.dataset.scheme = this.scheme ?? colorScheme.current;\n\n // Not gated on a changed `label`: the decorative default has to be applied\n // on the very first update too, when nothing has changed yet.\n if (this.label) {\n this.setAttribute('role', 'img');\n this.setAttribute('aria-label', this.label);\n this.removeAttribute('aria-hidden');\n } else {\n this.removeAttribute('role');\n this.removeAttribute('aria-label');\n this.setAttribute('aria-hidden', 'true');\n }\n }\n\n override render() {\n return html`\n <svg\n part=\"base\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <mask id=\"crescent\">\n <rect\n width=\"24\"\n height=\"24\"\n fill=\"#fff\"\n />\n <circle\n class=\"biter\"\n cx=\"12\"\n cy=\"12\"\n r=\"11\"\n fill=\"#000\"\n />\n </mask>\n\n <circle\n class=\"disc\"\n cx=\"12\"\n cy=\"12\"\n r=\"9\"\n mask=\"url(#crescent)\"\n />\n\n <!-- \\`svg\\`, not \\`html\\`: a nested template is parsed as HTML even when it\n sits inside an <svg>, so these circles would land in the HTML\n namespace and render nothing at all. -->\n ${RAYS.map(\n (ray) => svg`\n <circle\n class=\"ray\"\n cx=${ray.cx}\n cy=${ray.cy}\n r=\"1.5\"\n style=\"--i:${ray.i}\"\n />\n `,\n )}\n </svg>\n `;\n }\n}\n"],"mappings":"mTCOA,IAAM,EAAS,EAAU,i0BAAS,EAK5B,EAAO,MAAM,KAAK,CAAE,OAAQ,CAAE,GAAI,EAAG,IAAM,CAC/C,IAAM,EAAS,EAAI,KAAK,GAAM,EAC9B,MAAO,CACL,IACA,IAAK,GAAK,KAAK,IAAI,CAAK,EAAI,IAAA,CAAK,QAAQ,CAAC,EAC1C,IAAK,GAAK,KAAK,IAAI,CAAK,EAAI,IAAA,CAAK,QAAQ,CAAC,CAC5C,CACF,CAAC,EAgCY,EAAb,cAAqC,CAAa,oBACvB,CAAC,EAAY,CAAM,EAqB5C,mBAA6B,CAC3B,MAAM,kBAAkB,EAGxB,KAAK,aAAe,EAAY,cAAgB,KAAK,cAAc,CAAC,CACtE,CAEA,sBAAgC,CAC9B,MAAM,qBAAqB,EAC3B,KAAK,eAAe,EACpB,KAAK,aAAe,IAAA,EACtB,CAEA,YAAsB,CAOpB,KAAK,QAAQ,OAAS,KAAK,QAAU,EAAY,QAI7C,KAAK,OACP,KAAK,aAAa,OAAQ,KAAK,EAC/B,KAAK,aAAa,aAAc,KAAK,KAAK,EAC1C,KAAK,gBAAgB,aAAa,IAElC,KAAK,gBAAgB,MAAM,EAC3B,KAAK,gBAAgB,YAAY,EACjC,KAAK,aAAa,cAAe,MAAM,EAE3C,CAEA,QAAkB,CAChB,MAAO,EAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAgCL,EAAK,IACJ,GAAQ,CAAG;;;mBAGH,EAAI,GAAG;mBACP,EAAI,GAAG;;2BAEC,EAAI,EAAE;;WAGzB,EAAE;;KAGR,CACF,KA/FG,EAAS,CAAE,QAAS,EAAK,CAAC,CAAA,EAAA,EAAA,UAAA,SAAA,IAAA,EAAA,KAQ1B,EAAS,CAAA,EAAA,EAAA,UAAA,QAAA,IAAA,EAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/html/elements/color-scheme-icon/index.ts"],"names":[],"mappings":"AAEA,cAAc,wBAAwB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/html/elements/color-scheme-icon/index.ts"],"sourcesContent":["import { define } from '../../define.js';\nimport { ColorSchemeIcon } from './color-scheme-icon.js';\nexport * from './color-scheme-icon.js';\ndefine('color-scheme-icon', ColorSchemeIcon);\n"],"mappings":"kGAGA,EAAO,oBAAqB,CAAe"}
|
|
@@ -4,7 +4,7 @@ import { LuxenElement } from '../../shared/luxen-element.js';
|
|
|
4
4
|
* A menu item for use inside `<l-dropdown>`.
|
|
5
5
|
*
|
|
6
6
|
* @slot - Label text.
|
|
7
|
-
* @slot prefix - Leading content (e.g. icon).
|
|
7
|
+
* @slot prefix - Leading content (e.g. icon). A `type="checkbox"` item ignores it unless `check-placement="end"` frees the leading column.
|
|
8
8
|
* @slot suffix - Trailing content.
|
|
9
9
|
* @slot submenu - Nested `l-dropdown-item` elements rendered in a submenu panel anchored to this item. Drop an `<hr>` between them for a separator.
|
|
10
10
|
*
|
|
@@ -31,6 +31,13 @@ export declare class DropdownItem extends LuxenElement {
|
|
|
31
31
|
accessor checked: boolean;
|
|
32
32
|
/** Whether this item's submenu is open. Managed by the parent `l-dropdown`. */
|
|
33
33
|
accessor submenuOpen: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Which side of a `type="checkbox"` item carries the check mark. `start` (the
|
|
36
|
+
* default) puts it in the leading column, where it takes the place of any
|
|
37
|
+
* `prefix`. `end` moves it to the trailing edge, freeing that column so the
|
|
38
|
+
* item can show its own icon alongside the state.
|
|
39
|
+
*/
|
|
40
|
+
accessor checkPlacement: 'start' | 'end';
|
|
34
41
|
private accessor _hasSubmenu;
|
|
35
42
|
/** Whether this item has nested `slot="submenu"` items. */
|
|
36
43
|
get hasSubmenu(): boolean;
|
|
@@ -47,6 +54,7 @@ export declare class DropdownItem extends LuxenElement {
|
|
|
47
54
|
updated(changed: PropertyValues<this>): void;
|
|
48
55
|
private _getDuration;
|
|
49
56
|
private _handleSubmenuOpenChange;
|
|
57
|
+
private _renderCheck;
|
|
50
58
|
private _onSubmenuSlotChange;
|
|
51
59
|
render(): import('lit').TemplateResult<1>;
|
|
52
60
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown-item.d.ts","sourceRoot":"","sources":["../../../src/html/elements/dropdown-item/dropdown-item.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAU7D;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAgB,MAAM,4BAAwB;IAE9C,OAAO,CAAC,gBAAgB,CAIrB;IAEH,2CAA2C;IAE3C,QAAQ,CAAC,KAAK,SAAM;IAEpB,yBAAyB;IAEzB,QAAQ,CAAC,QAAQ,UAAS;IAE1B,gDAAgD;IAEhD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAY;IAEhD,wEAAwE;IAExE,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAAa;IAExD,4CAA4C;IAE5C,QAAQ,CAAC,OAAO,UAAS;IAEzB,+EAA+E;IAE/E,QAAQ,CAAC,WAAW,UAAS;
|
|
1
|
+
{"version":3,"file":"dropdown-item.d.ts","sourceRoot":"","sources":["../../../src/html/elements/dropdown-item/dropdown-item.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAU7D;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAgB,MAAM,4BAAwB;IAE9C,OAAO,CAAC,gBAAgB,CAIrB;IAEH,2CAA2C;IAE3C,QAAQ,CAAC,KAAK,SAAM;IAEpB,yBAAyB;IAEzB,QAAQ,CAAC,QAAQ,UAAS;IAE1B,gDAAgD;IAEhD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAY;IAEhD,wEAAwE;IAExE,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAAa;IAExD,4CAA4C;IAE5C,QAAQ,CAAC,OAAO,UAAS;IAEzB,+EAA+E;IAE/E,QAAQ,CAAC,WAAW,UAAS;IAE7B;;;;;OAKG;IAEH,QAAQ,CAAC,cAAc,EAAE,OAAO,GAAG,KAAK,CAAW;IAGnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC,2DAA2D;IAC3D,IAAI,UAAU,IAAI,OAAO,CAExB;IAEQ,iBAAiB;IAU1B,OAAO,KAAK,UAAU,GAErB;IAED,2EAA2E;IAC3E,YAAY,IAAI,MAAM;IAUtB,uFAAuF;IACvF,eAAe,IAAI,YAAY,EAAE;IAQjC,oEAAoE;IACpE,WAAW;IAKX,4EAA4E;IAC5E,YAAY;IAKH,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC;IAM9C,OAAO,CAAC,YAAY;YAKN,wBAAwB;IA2BtC,OAAO,CAAC,YAAY;IAwBpB,OAAO,CAAC,oBAAoB,CAK1B;IAEO,MAAM;CAqDhB"}
|
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
import{tagName as e}from"../../registry.js";import{i as t}from"../../chunks/lit.js";import{i as n,r,t as i}from"../../chunks/decorate.js";import{a,t as o}from"../../chunks/lit-html.js";import{LuxenElement as s}from"../../shared/luxen-element.js";import c from"../../shared/styles/host.styles.js";import{PopoverController as l}from"../../shared/controllers/popover.js";var u=t(`:host{display:block}:host([disabled]){pointer-events:none;opacity:.5}.item{box-sizing:border-box;cursor:pointer;min-block-size:36px;color:var(--l-color-text-primary);white-space:nowrap;text-align:start;border-radius:4px;outline:none;align-items:center;gap:8px;padding:.375rem .5rem;font-size:.875rem;line-height:1.5;display:flex}.item:focus-visible{background:var(--l-color-bg-state-hover);outline:2px solid var(--l-focus-ring);outline-offset:-2px}@media (hover:hover){.item:hover{background:var(--l-color-bg-state-hover)}}:host([submenu-open]) .item{background:var(--l-color-bg-state-hover)}:host([variant=destructive]) .item{color:var(--l-color-text-danger)}:host([variant=destructive]) .item:focus-visible{background:var(--l-color-bg-fill-danger-soft);outline-color:var(--l-color-text-danger)}@media (hover:hover){:host([variant=destructive]) .item:hover{background:var(--l-color-bg-fill-danger-soft)}}.check{flex-shrink:0;width:16px;display:flex}.submenu-indicator{background-color:currentColor;flex-shrink:0;block-size:16px;inline-size:16px;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 50%/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 50%/contain no-repeat}:host(:dir(rtl)) .submenu-indicator{scale:-1 1}.submenu{inset:unset;box-sizing:border-box;width:max-content;padding:var(--padding,.25rem);border:1px solid var(--l-color-border-overlay);border-radius:var(--border-radius,6px);background:var(--background,var(--l-color-surface-overlay));color:var(--l-color-text-primary);box-shadow:var(--shadow,0 4px 6px -1px #00000014, 0 2px 4px -2px #0000000f);margin:0;font-size:.875rem;line-height:1.5;overflow:visible}slot[name=submenu]::slotted(hr){height:1px;margin-block:var(--padding,.25rem);margin-inline:calc(var(--padding,.25rem) * -1);background:var(--l-color-divider);border:0;display:block}:host(:not([checked])) .check svg{visibility:hidden}::slotted([slot=prefix]),::slotted([slot=suffix]){flex-shrink:0;display:flex}.label{flex:1}`),d=class extends s{constructor(...t){super(...t),this._submenuFloating=new l(this,{getTriggerElement:()=>this,getFloatingElement:()=>this._submenuEl,getArrowElement:()=>null}),this.#e=``,this.#t=!1,this.#n=`normal`,this.#r=void 0,this.#i=!1,this.#a=!1,this.#o=!1,this._onSubmenuSlotChange=t=>{let n=t.target;this._hasSubmenu=n.assignedElements().some(t=>t.tagName===e(`dropdown-item`).toUpperCase())}}static{this.styles=[c,u]}#e;get value(){return this.#e}set value(e){this.#e=e}#t;get disabled(){return this.#t}set disabled(e){this.#t=e}#n;get type(){return this.#n}set type(e){this.#n=e}#r;get variant(){return this.#r}set variant(e){this.#r=e}#i;get checked(){return this.#i}set checked(e){this.#i=e}#a;get submenuOpen(){return this.#a}set submenuOpen(e){this.#a=e}#o;get
|
|
1
|
+
import{tagName as e}from"../../registry.js";import{i as t}from"../../chunks/lit.js";import{i as n,r,t as i}from"../../chunks/decorate.js";import{a,t as o}from"../../chunks/lit-html.js";import{LuxenElement as s}from"../../shared/luxen-element.js";import c from"../../shared/styles/host.styles.js";import{PopoverController as l}from"../../shared/controllers/popover.js";var u=t(`:host{display:block}:host([disabled]){pointer-events:none;opacity:.5}.item{box-sizing:border-box;cursor:pointer;min-block-size:36px;color:var(--l-color-text-primary);white-space:nowrap;text-align:start;border-radius:4px;outline:none;align-items:center;gap:8px;padding:.375rem .5rem;font-size:.875rem;line-height:1.5;display:flex}.item:focus-visible{background:var(--l-color-bg-state-hover);outline:2px solid var(--l-focus-ring);outline-offset:-2px}@media (hover:hover){.item:hover{background:var(--l-color-bg-state-hover)}}:host([submenu-open]) .item{background:var(--l-color-bg-state-hover)}:host([variant=destructive]) .item{color:var(--l-color-text-danger)}:host([variant=destructive]) .item:focus-visible{background:var(--l-color-bg-fill-danger-soft);outline-color:var(--l-color-text-danger)}@media (hover:hover){:host([variant=destructive]) .item:hover{background:var(--l-color-bg-fill-danger-soft)}}.check{flex-shrink:0;width:16px;display:flex}.submenu-indicator{background-color:currentColor;flex-shrink:0;block-size:16px;inline-size:16px;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 50%/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 50%/contain no-repeat}:host(:dir(rtl)) .submenu-indicator{scale:-1 1}.submenu{inset:unset;box-sizing:border-box;width:max-content;padding:var(--padding,.25rem);border:1px solid var(--l-color-border-overlay);border-radius:var(--border-radius,6px);background:var(--background,var(--l-color-surface-overlay));color:var(--l-color-text-primary);box-shadow:var(--shadow,0 4px 6px -1px #00000014, 0 2px 4px -2px #0000000f);margin:0;font-size:.875rem;line-height:1.5;overflow:visible}slot[name=submenu]::slotted(hr){height:1px;margin-block:var(--padding,.25rem);margin-inline:calc(var(--padding,.25rem) * -1);background:var(--l-color-divider);border:0;display:block}:host(:not([checked])) .check svg{visibility:hidden}::slotted([slot=prefix]),::slotted([slot=suffix]){flex-shrink:0;display:flex}.label{flex:1}`),d=class extends s{constructor(...t){super(...t),this._submenuFloating=new l(this,{getTriggerElement:()=>this,getFloatingElement:()=>this._submenuEl,getArrowElement:()=>null}),this.#e=``,this.#t=!1,this.#n=`normal`,this.#r=void 0,this.#i=!1,this.#a=!1,this.#o=`start`,this.#s=!1,this._onSubmenuSlotChange=t=>{let n=t.target;this._hasSubmenu=n.assignedElements().some(t=>t.tagName===e(`dropdown-item`).toUpperCase())}}static{this.styles=[c,u]}#e;get value(){return this.#e}set value(e){this.#e=e}#t;get disabled(){return this.#t}set disabled(e){this.#t=e}#n;get type(){return this.#n}set type(e){this.#n=e}#r;get variant(){return this.#r}set variant(e){this.#r=e}#i;get checked(){return this.#i}set checked(e){this.#i=e}#a;get submenuOpen(){return this.#a}set submenuOpen(e){this.#a=e}#o;get checkPlacement(){return this.#o}set checkPlacement(e){this.#o=e}#s;get _hasSubmenu(){return this.#s}set _hasSubmenu(e){this.#s=e}get hasSubmenu(){return this._hasSubmenu}connectedCallback(){super.connectedCallback(),this.hasAttribute(`role`)||this.setAttribute(`role`,`none`)}get _submenuEl(){return this.shadowRoot.querySelector(`.submenu`)}getTextLabel(){let e=this.shadowRoot?.querySelector(`slot:not([name])`);return e?e.assignedNodes({flatten:!0}).map(e=>e.textContent??``).join(``).trim():(this.textContent??``).trim()}getSubmenuItems(){let t=this.shadowRoot?.querySelector(`slot[name="submenu"]`);return t?t.assignedElements().filter(t=>t.tagName===e(`dropdown-item`).toUpperCase()&&!t.disabled):[]}openSubmenu(){!this._hasSubmenu||this.disabled||this.submenuOpen||(this.submenuOpen=!0)}closeSubmenu(){for(let e of this.getSubmenuItems())e.closeSubmenu();this.submenuOpen&&=!1}updated(e){e.has(`submenuOpen`)&&this._handleSubmenuOpenChange()}_getDuration(e){let t=parseFloat(getComputedStyle(this).getPropertyValue(e));return Number.isNaN(t)?150:t}async _handleSubmenuOpenChange(){let e=this._submenuEl;if(!e)return;let t={placement:getComputedStyle(this).direction===`rtl`?`left-start`:`right-start`,distance:0,skidding:-parseFloat(getComputedStyle(e).paddingTop||`0`)};if(this.submenuOpen){if(!this.isConnected||(e.showPopover(),await this._submenuFloating.updatePosition(t),!this.submenuOpen))return;await this._submenuFloating.animateShow(e,this._getDuration(`--show-duration`)),this._submenuFloating.startPositioning(t)}else{if(!e.matches(`:popover-open`))return;this._submenuFloating.stopPositioning(),await this._submenuFloating.animateHide(e,this._getDuration(`--hide-duration`)),e.matches(`:popover-open`)&&e.hidePopover()}}_renderCheck(){return a`
|
|
2
|
+
<span
|
|
3
|
+
class="check"
|
|
4
|
+
aria-hidden="true"
|
|
5
|
+
>
|
|
6
|
+
<svg
|
|
7
|
+
width="16"
|
|
8
|
+
height="16"
|
|
9
|
+
viewBox="0 0 16 16"
|
|
10
|
+
fill="none"
|
|
11
|
+
>
|
|
12
|
+
<path
|
|
13
|
+
d="M3.5 8.5L6.5 11.5L12.5 4.5"
|
|
14
|
+
stroke="currentColor"
|
|
15
|
+
stroke-width="1.5"
|
|
16
|
+
stroke-linecap="round"
|
|
17
|
+
stroke-linejoin="round"
|
|
18
|
+
/>
|
|
19
|
+
</svg>
|
|
20
|
+
</span>
|
|
21
|
+
`}render(){let e=this.type===`checkbox`,t=e&&this.checkPlacement===`start`;return a`
|
|
2
22
|
<div
|
|
3
23
|
class="item"
|
|
4
24
|
role=${e?`menuitemcheckbox`:`menuitem`}
|
|
@@ -9,33 +29,22 @@ import{tagName as e}from"../../registry.js";import{i as t}from"../../chunks/lit.
|
|
|
9
29
|
aria-controls=${this._hasSubmenu?`submenu`:o}
|
|
10
30
|
tabindex="-1"
|
|
11
31
|
>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
fill="none"
|
|
22
|
-
>
|
|
23
|
-
<path
|
|
24
|
-
d="M3.5 8.5L6.5 11.5L12.5 4.5"
|
|
25
|
-
stroke="currentColor"
|
|
26
|
-
stroke-width="1.5"
|
|
27
|
-
stroke-linecap="round"
|
|
28
|
-
stroke-linejoin="round"
|
|
29
|
-
/>
|
|
30
|
-
</svg>
|
|
31
|
-
</span>
|
|
32
|
-
`:a` <slot name="prefix"></slot> `}
|
|
32
|
+
<!-- The check owns the leading column by default, which is why a
|
|
33
|
+
checkbox item has no room for a prefix there. Moving it to the
|
|
34
|
+
trailing edge frees that column, so an item can carry its own icon
|
|
35
|
+
and still show whether it is on — a theme row with a sun/moon, a
|
|
36
|
+
Show-grid row with a grid glyph. The aria-checked above is what
|
|
37
|
+
assistive tech reads, and it is unaffected by where the mark sits.
|
|
38
|
+
(No backticks in this comment: it lives inside a template literal.) -->
|
|
39
|
+
${t?this._renderCheck():o}
|
|
40
|
+
${t?o:a`<slot name="prefix"></slot>`}
|
|
33
41
|
<span
|
|
34
42
|
class="label"
|
|
35
43
|
id="label"
|
|
36
44
|
><slot></slot
|
|
37
45
|
></span>
|
|
38
46
|
<slot name="suffix"></slot>
|
|
47
|
+
${e&&this.checkPlacement===`end`?this._renderCheck():o}
|
|
39
48
|
${this._hasSubmenu?a`<span
|
|
40
49
|
class="submenu-indicator"
|
|
41
50
|
aria-hidden="true"
|
|
@@ -54,5 +63,5 @@ import{tagName as e}from"../../registry.js";import{i as t}from"../../chunks/lit.
|
|
|
54
63
|
@slotchange=${this._onSubmenuSlotChange}
|
|
55
64
|
></slot>
|
|
56
65
|
</div>
|
|
57
|
-
`}};i([n()],d.prototype,`value`,null),i([n({type:Boolean,reflect:!0})],d.prototype,`disabled`,null),i([n()],d.prototype,`type`,null),i([n({reflect:!0})],d.prototype,`variant`,null),i([n({type:Boolean,reflect:!0})],d.prototype,`checked`,null),i([n({type:Boolean,reflect:!0,attribute:`submenu-open`})],d.prototype,`submenuOpen`,null),i([r()],d.prototype,`_hasSubmenu`,null);export{d as DropdownItem};
|
|
66
|
+
`}};i([n()],d.prototype,`value`,null),i([n({type:Boolean,reflect:!0})],d.prototype,`disabled`,null),i([n()],d.prototype,`type`,null),i([n({reflect:!0})],d.prototype,`variant`,null),i([n({type:Boolean,reflect:!0})],d.prototype,`checked`,null),i([n({type:Boolean,reflect:!0,attribute:`submenu-open`})],d.prototype,`submenuOpen`,null),i([n({reflect:!0,attribute:`check-placement`})],d.prototype,`checkPlacement`,null),i([r()],d.prototype,`_hasSubmenu`,null);export{d as DropdownItem};
|
|
58
67
|
//# sourceMappingURL=dropdown-item.js.map
|