@lilydesignsystem/html-picker-bar 0.1.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.
@@ -0,0 +1,110 @@
1
+ import { ThemePickerProps, ThemePicker } from '@lilydesignsystem/html-theme-picker';
2
+ import { LocalePickerProps, LocalePicker } from '@lilydesignsystem/html-locale-picker';
3
+ import { TextSizePickerProps, TextSizePicker } from '@lilydesignsystem/html-text-size-picker';
4
+ import { ShareTarget, SharePickerProps, SharePicker } from '@lilydesignsystem/html-share-picker';
5
+ export { ShareTarget } from '@lilydesignsystem/html-share-picker';
6
+
7
+ /**
8
+ * `<picker-bar>` — Lily Design System HTML helper.
9
+ *
10
+ * See `./spec/index.md` for the canonical contract. This file implements
11
+ * the custom-element class but does NOT register it. The `index.ts`
12
+ * barrel registers it on import.
13
+ *
14
+ * A thin composition: renders `<theme-picker>`, `<locale-picker>`,
15
+ * `<text-size-picker>`, and `<share-picker>` — each imported from its
16
+ * own published package, not vendored — inside one
17
+ * `<div class="picker-bar {class}">` row. Owns no lifecycle of its
18
+ * own beyond two catalog-specific defaults (§5.1, §5.2 of the spec).
19
+ */
20
+
21
+ /**
22
+ * All 45 Lily reference theme slugs (see `themes/` at the repo root),
23
+ * sorted alphabetically except the United Kingdom and United States
24
+ * government/public-sector themes, which sort last as one alphabetical
25
+ * group of their own. Mirrors `theme-picker`'s own title-casing of each
26
+ * slug, so no `theme-labels` override is needed for these to read well.
27
+ */
28
+ declare const DEFAULT_THEMES: string[];
29
+ /**
30
+ * The seven-step text-size scale. Each slug title-cases to exactly the
31
+ * requested label ("largest" → "Largest", …) via `text-size-picker`'s
32
+ * own default label resolver, so no `size-labels` override is needed
33
+ * either.
34
+ */
35
+ declare const DEFAULT_SIZES: string[];
36
+ /** Accessible names for the four pickers. Required — no English default. */
37
+ type PickerBarLabels = {
38
+ /** Accessible name for the theme picker's button and listbox. */
39
+ theme: string;
40
+ /** Accessible name for the locale picker's button and listbox. */
41
+ locale: string;
42
+ /** Accessible name for the text-size picker's button and listbox. */
43
+ textSize: string;
44
+ /** Accessible name for the share picker's button and list. */
45
+ share: string;
46
+ };
47
+ /**
48
+ * Extra per-picker configuration, applied to that picker's element
49
+ * instance (via property assignment, which each sibling package
50
+ * reflects onto its own attributes) BEFORE the element is connected —
51
+ * so persistence, detection, and initial-value props still take
52
+ * effect on first mount. Applied after `<picker-bar>`'s own defaults,
53
+ * so anything here overrides them. Excludes the props `<picker-bar>`
54
+ * already lifts to the top level.
55
+ */
56
+ type ThemePickerExtra = Partial<Omit<ThemePickerProps, "label" | "themesUrl" | "themes">>;
57
+ type LocalePickerExtra = Partial<Omit<LocalePickerProps, "label" | "locales">>;
58
+ type TextSizePickerExtra = Partial<Omit<TextSizePickerProps, "label" | "sizes">>;
59
+ type SharePickerExtra = Partial<Omit<SharePickerProps, "label" | "targets">>;
60
+ /** Mirrors the observed attributes / properties for typing convenience. */
61
+ type PickerBarProps = {
62
+ labels: PickerBarLabels;
63
+ themesUrl: string;
64
+ themes?: string[];
65
+ themeProps?: ThemePickerExtra;
66
+ locales: string[];
67
+ localeProps?: LocalePickerExtra;
68
+ sizes?: string[];
69
+ textSizeProps?: TextSizePickerExtra;
70
+ shareTargets?: ShareTarget[];
71
+ shareProps?: SharePickerExtra;
72
+ class?: string;
73
+ };
74
+ /** Custom-element class implementing `<picker-bar>`. */
75
+ declare class PickerBar extends HTMLElement {
76
+ #private;
77
+ static get observedAttributes(): string[];
78
+ get labels(): PickerBarLabels;
79
+ set labels(v: PickerBarLabels);
80
+ get themesUrl(): string;
81
+ set themesUrl(v: string);
82
+ get themes(): string[];
83
+ set themes(v: string[]);
84
+ get locales(): string[];
85
+ set locales(v: string[]);
86
+ get sizes(): string[];
87
+ set sizes(v: string[]);
88
+ get shareTargets(): ShareTarget[];
89
+ set shareTargets(v: ShareTarget[]);
90
+ get themeProps(): ThemePickerExtra;
91
+ set themeProps(v: ThemePickerExtra);
92
+ get localeProps(): LocalePickerExtra;
93
+ set localeProps(v: LocalePickerExtra);
94
+ get textSizeProps(): TextSizePickerExtra;
95
+ set textSizeProps(v: TextSizePickerExtra);
96
+ get shareProps(): SharePickerExtra;
97
+ set shareProps(v: SharePickerExtra);
98
+ /** The rendered `<theme-picker>` instance, once connected. */
99
+ get themePicker(): ThemePicker | null;
100
+ /** The rendered `<locale-picker>` instance, once connected. */
101
+ get localePicker(): LocalePicker | null;
102
+ /** The rendered `<text-size-picker>` instance, once connected. */
103
+ get textSizePicker(): TextSizePicker | null;
104
+ /** The rendered `<share-picker>` instance, once connected. */
105
+ get sharePicker(): SharePicker | null;
106
+ connectedCallback(): void;
107
+ attributeChangedCallback(name: string, _old: string | null, value: string | null): void;
108
+ }
109
+
110
+ export { DEFAULT_SIZES, DEFAULT_THEMES, type LocalePickerExtra, PickerBar, type PickerBarLabels, type PickerBarProps, type SharePickerExtra, type TextSizePickerExtra, type ThemePickerExtra };
package/dist/index.js ADDED
@@ -0,0 +1,274 @@
1
+ // lily-design-system-html-picker-bar/picker-bar.ts
2
+ import "@lilydesignsystem/html-theme-picker";
3
+ import "@lilydesignsystem/html-locale-picker";
4
+ import "@lilydesignsystem/html-text-size-picker";
5
+ import "@lilydesignsystem/html-share-picker";
6
+ var DEFAULT_THEMES = [
7
+ "abyss",
8
+ "acid",
9
+ "adobe-spectrum",
10
+ "aqua",
11
+ "autumn",
12
+ "black",
13
+ "bumblebee",
14
+ "business",
15
+ "caramellatte",
16
+ "cmyk",
17
+ "coffee",
18
+ "corporate",
19
+ "cupcake",
20
+ "cyberpunk",
21
+ "dark",
22
+ "dim",
23
+ "dracula",
24
+ "emerald",
25
+ "fantasy",
26
+ "forest",
27
+ "garden",
28
+ "halloween",
29
+ "lemonade",
30
+ "light",
31
+ "lofi",
32
+ "luxury",
33
+ "mozilla-protocol",
34
+ "night",
35
+ "nord",
36
+ "pastel",
37
+ "retro",
38
+ "silk",
39
+ "sunset",
40
+ "synthwave",
41
+ "valentine",
42
+ "winter",
43
+ "wireframe",
44
+ "united-kingdom-government-digital-service",
45
+ "united-kingdom-national-health-service-england-for-patients",
46
+ "united-kingdom-national-health-service-england-for-practitioners",
47
+ "united-kingdom-national-health-service-scotland-for-patients",
48
+ "united-kingdom-national-health-service-scotland-for-practitioners",
49
+ "united-kingdom-national-health-service-wales-for-patients",
50
+ "united-kingdom-national-health-service-wales-for-practitioners",
51
+ "united-states-web-design-system"
52
+ ];
53
+ var DEFAULT_SIZES = [
54
+ "largest",
55
+ "larger",
56
+ "large",
57
+ "normal",
58
+ "small",
59
+ "smaller",
60
+ "smallest"
61
+ ];
62
+ var DEFAULT_LABELS = {
63
+ theme: "",
64
+ locale: "",
65
+ textSize: "",
66
+ share: ""
67
+ };
68
+ var PickerBar = class extends HTMLElement {
69
+ static get observedAttributes() {
70
+ return ["themes-url", "themes", "locales", "sizes", "class"];
71
+ }
72
+ // Backing storage for property-only members (no honest attribute
73
+ // encoding — labels/targets carry non-string data, and the *Extra
74
+ // bags are applied at property-assignment time, not re-derivable
75
+ // from an attribute).
76
+ #labels = DEFAULT_LABELS;
77
+ #themes = DEFAULT_THEMES;
78
+ #locales = [];
79
+ #sizes = DEFAULT_SIZES;
80
+ #shareTargets = [];
81
+ #themeProps = {};
82
+ #localeProps = {};
83
+ #textSizeProps = {};
84
+ #shareProps = {};
85
+ // Rendered-DOM references. Null until #render() has run.
86
+ #rootEl = null;
87
+ #themePickerEl = null;
88
+ #localePickerEl = null;
89
+ #textSizePickerEl = null;
90
+ #sharePickerEl = null;
91
+ // ---- Property accessors ----
92
+ get labels() {
93
+ return this.#labels;
94
+ }
95
+ set labels(v) {
96
+ this.#labels = v ?? DEFAULT_LABELS;
97
+ this.#render();
98
+ }
99
+ get themesUrl() {
100
+ return this.getAttribute("themes-url") ?? "";
101
+ }
102
+ set themesUrl(v) {
103
+ this.setAttribute("themes-url", v);
104
+ }
105
+ get themes() {
106
+ return [...this.#themes];
107
+ }
108
+ set themes(v) {
109
+ this.#themes = Array.isArray(v) && v.length > 0 ? v.slice() : DEFAULT_THEMES;
110
+ const csv = this.#themes.join(",");
111
+ if (this.getAttribute("themes") !== csv) {
112
+ this.setAttribute("themes", csv);
113
+ return;
114
+ }
115
+ this.#render();
116
+ }
117
+ get locales() {
118
+ return [...this.#locales];
119
+ }
120
+ set locales(v) {
121
+ this.#locales = Array.isArray(v) ? v.slice() : [];
122
+ const csv = this.#locales.join(",");
123
+ if (this.getAttribute("locales") !== csv) {
124
+ this.setAttribute("locales", csv);
125
+ return;
126
+ }
127
+ this.#render();
128
+ }
129
+ get sizes() {
130
+ return [...this.#sizes];
131
+ }
132
+ set sizes(v) {
133
+ this.#sizes = Array.isArray(v) && v.length > 0 ? v.slice() : DEFAULT_SIZES;
134
+ const csv = this.#sizes.join(",");
135
+ if (this.getAttribute("sizes") !== csv) {
136
+ this.setAttribute("sizes", csv);
137
+ return;
138
+ }
139
+ this.#render();
140
+ }
141
+ get shareTargets() {
142
+ return [...this.#shareTargets];
143
+ }
144
+ set shareTargets(v) {
145
+ this.#shareTargets = Array.isArray(v) ? v.slice() : [];
146
+ this.#render();
147
+ }
148
+ get themeProps() {
149
+ return this.#themeProps;
150
+ }
151
+ set themeProps(v) {
152
+ this.#themeProps = v ?? {};
153
+ this.#render();
154
+ }
155
+ get localeProps() {
156
+ return this.#localeProps;
157
+ }
158
+ set localeProps(v) {
159
+ this.#localeProps = v ?? {};
160
+ this.#render();
161
+ }
162
+ get textSizeProps() {
163
+ return this.#textSizeProps;
164
+ }
165
+ set textSizeProps(v) {
166
+ this.#textSizeProps = v ?? {};
167
+ this.#render();
168
+ }
169
+ get shareProps() {
170
+ return this.#shareProps;
171
+ }
172
+ set shareProps(v) {
173
+ this.#shareProps = v ?? {};
174
+ this.#render();
175
+ }
176
+ /** The rendered `<theme-picker>` instance, once connected. */
177
+ get themePicker() {
178
+ return this.#themePickerEl;
179
+ }
180
+ /** The rendered `<locale-picker>` instance, once connected. */
181
+ get localePicker() {
182
+ return this.#localePickerEl;
183
+ }
184
+ /** The rendered `<text-size-picker>` instance, once connected. */
185
+ get textSizePicker() {
186
+ return this.#textSizePickerEl;
187
+ }
188
+ /** The rendered `<share-picker>` instance, once connected. */
189
+ get sharePicker() {
190
+ return this.#sharePickerEl;
191
+ }
192
+ // ---- Lifecycle ----
193
+ connectedCallback() {
194
+ const themesAttr = this.getAttribute("themes");
195
+ if (themesAttr !== null) this.#themes = parseCsv(themesAttr);
196
+ const localesAttr = this.getAttribute("locales");
197
+ if (localesAttr !== null) this.#locales = parseCsv(localesAttr);
198
+ const sizesAttr = this.getAttribute("sizes");
199
+ if (sizesAttr !== null) this.#sizes = parseCsv(sizesAttr);
200
+ this.#render();
201
+ }
202
+ attributeChangedCallback(name, _old, value) {
203
+ switch (name) {
204
+ case "themes":
205
+ this.#themes = value === null ? DEFAULT_THEMES : parseCsv(value);
206
+ this.#render();
207
+ break;
208
+ case "locales":
209
+ this.#locales = value === null ? [] : parseCsv(value);
210
+ this.#render();
211
+ break;
212
+ case "sizes":
213
+ this.#sizes = value === null ? DEFAULT_SIZES : parseCsv(value);
214
+ this.#render();
215
+ break;
216
+ case "themes-url":
217
+ case "class":
218
+ this.#render();
219
+ break;
220
+ default:
221
+ break;
222
+ }
223
+ }
224
+ // ---- Rendering ----
225
+ #render() {
226
+ if (!this.isConnected) return;
227
+ const extraClass = this.getAttribute("class") ?? "";
228
+ const root = document.createElement("div");
229
+ root.className = `picker-bar ${extraClass}`.trim();
230
+ const themeEl = document.createElement("theme-picker");
231
+ themeEl.label = this.#labels.theme;
232
+ themeEl.themesUrl = this.themesUrl;
233
+ themeEl.themes = this.#themes;
234
+ Object.assign(themeEl, this.#themeProps);
235
+ root.appendChild(themeEl);
236
+ const localeEl = document.createElement("locale-picker");
237
+ localeEl.label = this.#labels.locale;
238
+ localeEl.locales = this.#locales;
239
+ Object.assign(localeEl, this.#localeProps);
240
+ root.appendChild(localeEl);
241
+ const textSizeEl = document.createElement(
242
+ "text-size-picker"
243
+ );
244
+ textSizeEl.label = this.#labels.textSize;
245
+ textSizeEl.sizes = this.#sizes;
246
+ textSizeEl.defaultValue = "normal";
247
+ Object.assign(textSizeEl, this.#textSizeProps);
248
+ root.appendChild(textSizeEl);
249
+ const shareEl = document.createElement("share-picker");
250
+ shareEl.label = this.#labels.share;
251
+ shareEl.targets = this.#shareTargets;
252
+ Object.assign(shareEl, this.#shareProps);
253
+ root.appendChild(shareEl);
254
+ this.replaceChildren(root);
255
+ this.#rootEl = root;
256
+ this.#themePickerEl = themeEl;
257
+ this.#localePickerEl = localeEl;
258
+ this.#textSizePickerEl = textSizeEl;
259
+ this.#sharePickerEl = shareEl;
260
+ }
261
+ };
262
+ function parseCsv(s) {
263
+ return s.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
264
+ }
265
+
266
+ // lily-design-system-html-picker-bar/index.ts
267
+ if (typeof customElements !== "undefined" && !customElements.get("picker-bar")) {
268
+ customElements.define("picker-bar", PickerBar);
269
+ }
270
+ export {
271
+ DEFAULT_SIZES,
272
+ DEFAULT_THEMES,
273
+ PickerBar
274
+ };
package/index.md ADDED
@@ -0,0 +1,149 @@
1
+ # Lily Design System™ — HTML PickerBar
2
+
3
+ A single page-header custom element, `<picker-bar>`, that composes four
4
+ of the Lily [`*-picker` helpers](../index.md) — theme, locale, text
5
+ size, and share — with two catalog-wide defaults pre-wired, so you can
6
+ drop one element into a header instead of assembling and configuring
7
+ four.
8
+
9
+ `motion-picker` and `date-time-picker` are not part of the bar: motion
10
+ has no natural spot next to the other three header preferences, and
11
+ `date-time-picker` is a form control, not a header control.
12
+
13
+ ## Install
14
+
15
+ ```sh
16
+ npm install @lilydesignsystem/html-picker-bar
17
+ ```
18
+
19
+ `@lilydesignsystem/html-theme-picker`, `-locale-picker`,
20
+ `-text-size-picker`, and `-share-picker` install automatically as
21
+ regular dependencies — `<picker-bar>` is a thin wrapper around them,
22
+ not a reimplementation.
23
+
24
+ ## Usage
25
+
26
+ ```js
27
+ import "@lilydesignsystem/html-picker-bar";
28
+
29
+ const bar = document.createElement("picker-bar");
30
+ bar.labels = {
31
+ theme: "Theme",
32
+ locale: "Language",
33
+ textSize: "Text size",
34
+ share: "Share",
35
+ };
36
+ bar.themesUrl = "/assets/themes/";
37
+ bar.locales = ["en", "cy", "gd", "ga"];
38
+ bar.shareTargets = [
39
+ {
40
+ id: "email",
41
+ label: "Email",
42
+ href: (url, title) => `mailto:?subject=${title}&body=${url}`,
43
+ },
44
+ ];
45
+ document.querySelector("header").appendChild(bar);
46
+ ```
47
+
48
+ Or in markup, for the parts that fit plain attributes:
49
+
50
+ ```html
51
+ <script type="module" src="/node_modules/lily-design-system-html-picker-bar/dist/index.js"></script>
52
+ <picker-bar themes-url="/assets/themes/" locales="en,cy,gd,ga"></picker-bar>
53
+ <script type="module">
54
+ document.querySelector("picker-bar").labels = {
55
+ theme: "Theme",
56
+ locale: "Language",
57
+ textSize: "Text size",
58
+ share: "Share",
59
+ };
60
+ </script>
61
+ ```
62
+
63
+ `labels` has to be set as a JS property (no honest attribute encoding
64
+ exists for an object), same as `shareTargets` — see
65
+ `<date-time-picker>`'s `labels` and `<share-picker>`'s `targets` for the
66
+ same pattern elsewhere in this catalog.
67
+
68
+ ## Defaults
69
+
70
+ - **`themes`** defaults to `DEFAULT_THEMES` — all 45 Lily reference
71
+ theme slugs, alphabetical, with the 8 United Kingdom / United States
72
+ government themes moved to their own alphabetical group at the
73
+ bottom. Set your own `themes` attribute (comma-separated) or
74
+ `.themes` property (array) to override.
75
+ - **`sizes`** defaults to `DEFAULT_SIZES` — the seven-step scale
76
+ `largest`, `larger`, `large`, `normal`, `small`, `smaller`,
77
+ `smallest` — and the text-size picker starts on `normal`. Set your
78
+ own `sizes` to override (and `textSizeProps = { defaultValue: "…" }`
79
+ for a different starting point).
80
+
81
+ Both are exported as named constants:
82
+
83
+ ```js
84
+ import { DEFAULT_THEMES, DEFAULT_SIZES } from "@lilydesignsystem/html-picker-bar";
85
+ ```
86
+
87
+ ## Passing extra configuration to one picker
88
+
89
+ Each wrapped picker takes a `*Props` bag — a property, not an
90
+ attribute — for anything beyond what `<picker-bar>` lifts to the top
91
+ level: persistence, initial value, detection, a `*Labels` override map.
92
+
93
+ ```js
94
+ bar.themeProps = { storageKey: "lily-theme", detectFromSystem: true };
95
+ bar.localeProps = { storageKey: "lily-locale", detectFromNavigator: true };
96
+ bar.textSizeProps = { storageKey: "lily-text-size" };
97
+ bar.shareProps = { copyLabel: "Copy link", copiedLabel: "Copied" };
98
+ ```
99
+
100
+ Set these **before** appending `bar` to the document — they apply to
101
+ the wrapped picker's element instance before it connects, so
102
+ persistence and initial-value resolution see them on first mount, the
103
+ same as if you had configured that picker directly.
104
+
105
+ ## Reading a wrapped picker directly
106
+
107
+ After `<picker-bar>` has rendered (i.e. once it's connected to the
108
+ document), each wrapped element is available as a read-only property:
109
+
110
+ ```js
111
+ bar.themePicker; // the <theme-picker> instance
112
+ bar.localePicker; // the <locale-picker> instance
113
+ bar.textSizePicker; // the <text-size-picker> instance
114
+ bar.sharePicker; // the <share-picker> instance
115
+ ```
116
+
117
+ Useful for attaching a `themechange` listener, reading `.value`, or any
118
+ other post-mount interaction — the elements are real, unmodified
119
+ instances of their own packages.
120
+
121
+ ## Styling
122
+
123
+ `<picker-bar>` renders no CSS of its own beyond the `picker-bar` root
124
+ wrapper class — style each child through its own package's class hooks
125
+ (`theme-picker`, `locale-picker`, `text-size-picker`, `share-picker`;
126
+ see each package's own `index.md`). A typical header layout:
127
+
128
+ ```css
129
+ .picker-bar {
130
+ display: flex;
131
+ gap: var(--theme-space-sm, 0.5rem);
132
+ align-items: center;
133
+ }
134
+ ```
135
+
136
+ ## Accessibility
137
+
138
+ Every accessible name comes from `labels` — there is no English
139
+ default, because a set of names this catalog invented is exactly the
140
+ case the rest of Lily's i18n rule exists for. Each wrapped picker keeps
141
+ its own WAI-ARIA APG contract unchanged; see that picker's own `index.md`.
142
+
143
+ ## Full contract
144
+
145
+ See [`spec/index.md`](./spec/index.md).
146
+
147
+ ---
148
+
149
+ Lily™ and Lily Design System™ are trademarks.
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@lilydesignsystem/html-picker-bar",
3
+ "version": "0.1.0",
4
+ "engines": {
5
+ "node": "=26"
6
+ },
7
+ "description": "Lily Design System HTML picker bar: composes theme-picker, locale-picker, text-size-picker, and share-picker into one page-header row. Headless, SSR-safe, no CSS.",
8
+ "type": "module",
9
+ "main": "./dist/index.js",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "index.md",
21
+ "README.md"
22
+ ],
23
+ "scripts": {
24
+ "prepublishOnly": "cd .. && npm run build"
25
+ },
26
+ "dependencies": {
27
+ "@lilydesignsystem/html-theme-picker": "^0.1.0",
28
+ "@lilydesignsystem/html-locale-picker": "^0.1.0",
29
+ "@lilydesignsystem/html-text-size-picker": "^0.1.0",
30
+ "@lilydesignsystem/html-share-picker": "^0.1.0"
31
+ },
32
+ "keywords": [
33
+ "lily",
34
+ "design",
35
+ "system",
36
+ "html",
37
+ "picker",
38
+ "picker-bar",
39
+ "theme",
40
+ "locale",
41
+ "text-size",
42
+ "share"
43
+ ],
44
+ "author": "Joel Parker Henderson <joel@joelparkerhenderson.com>",
45
+ "license": "MIT OR Apache-2.0 OR GPL-2.0-only OR GPL-3.0-only OR BSD-3-Clause",
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "git+https://github.com/LilyDesignSystem/lily-design-system-html-helpers.git",
49
+ "directory": "lily-design-system-html-picker-bar"
50
+ },
51
+ "homepage": "https://lilydesignsystem.com/",
52
+ "bugs": {
53
+ "url": "https://github.com/LilyDesignSystem/lily-design-system/issues"
54
+ }
55
+ }