@pure-ds/storybook 0.5.30 → 0.5.31
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/.storybook/preview.js +60 -1
- package/dist/pds-reference.json +112 -1
- package/package.json +2 -2
- package/public/assets/pds/components/pds-theme.js +170 -0
- package/public/assets/pds/vscode-custom-data.json +10 -0
- package/stories/components/PdsTheme/PdsTheme.stories.js +68 -0
- package/stories/getting-started.md +0 -1
package/.storybook/preview.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { addons } from '@storybook/preview-api';
|
|
2
|
-
import { SELECT_STORY } from '@storybook/core-events';
|
|
2
|
+
import { SELECT_STORY, UPDATE_GLOBALS } from '@storybook/core-events';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { Title, Subtitle, Description as DocsDescription, Controls } from '@storybook/blocks';
|
|
5
5
|
import { PDS } from '@pds-src/js/pds.js';
|
|
@@ -121,6 +121,61 @@ PDS.addEventListener('pds:ready', (event) => {
|
|
|
121
121
|
*/
|
|
122
122
|
// Track current view mode globally for other code to check
|
|
123
123
|
let currentViewMode = 'story';
|
|
124
|
+
let toolbarTheme = null;
|
|
125
|
+
|
|
126
|
+
const setToolbarThemeValue = (value) => {
|
|
127
|
+
if (!value) return;
|
|
128
|
+
toolbarTheme = value;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const emitThemeGlobals = (value) => {
|
|
132
|
+
if (!value || value === toolbarTheme) return;
|
|
133
|
+
|
|
134
|
+
const channel = addons?.getChannel?.();
|
|
135
|
+
if (!channel) return;
|
|
136
|
+
|
|
137
|
+
toolbarTheme = value;
|
|
138
|
+
channel.emit(UPDATE_GLOBALS, {
|
|
139
|
+
globals: { theme: value }
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const initThemeSync = (() => {
|
|
144
|
+
let initialized = false;
|
|
145
|
+
return () => {
|
|
146
|
+
if (initialized || typeof window === 'undefined') return;
|
|
147
|
+
initialized = true;
|
|
148
|
+
|
|
149
|
+
PDS.addEventListener('pds:theme:changed', (event) => {
|
|
150
|
+
emitThemeGlobals(event?.detail?.theme ?? PDS.theme);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const watchDomTheme = () => {
|
|
154
|
+
const targets = [document.body, document.documentElement].filter(Boolean);
|
|
155
|
+
if (!targets.length) return;
|
|
156
|
+
|
|
157
|
+
const syncFromDom = () => {
|
|
158
|
+
const nextTheme = document.body?.getAttribute('data-theme')
|
|
159
|
+
|| document.documentElement?.getAttribute('data-theme');
|
|
160
|
+
emitThemeGlobals(nextTheme);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const observer = new MutationObserver(syncFromDom);
|
|
164
|
+
targets.forEach((target) => {
|
|
165
|
+
observer.observe(target, { attributes: true, attributeFilter: ['data-theme'] });
|
|
166
|
+
});
|
|
167
|
+
syncFromDom();
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
if (!document.body && document.readyState === 'loading') {
|
|
171
|
+
document.addEventListener('DOMContentLoaded', watchDomTheme, { once: true });
|
|
172
|
+
} else {
|
|
173
|
+
watchDomTheme();
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
})();
|
|
177
|
+
|
|
178
|
+
initThemeSync();
|
|
124
179
|
|
|
125
180
|
const withPDS = (story, context) => {
|
|
126
181
|
currentViewMode = context.viewMode;
|
|
@@ -318,6 +373,9 @@ const withGlobalsHandler = (story, context) => {
|
|
|
318
373
|
document.body.setAttribute('data-theme', globals.theme);
|
|
319
374
|
PDS.theme = globals.theme;
|
|
320
375
|
}
|
|
376
|
+
if (globals?.theme) {
|
|
377
|
+
setToolbarThemeValue(globals.theme);
|
|
378
|
+
}
|
|
321
379
|
|
|
322
380
|
return story();
|
|
323
381
|
};
|
|
@@ -1452,6 +1510,7 @@ if (typeof window !== 'undefined') {
|
|
|
1452
1510
|
PDS.theme = globals.theme;
|
|
1453
1511
|
|
|
1454
1512
|
console.log('✅ Theme applied:', globals.theme);
|
|
1513
|
+
setToolbarThemeValue(globals.theme);
|
|
1455
1514
|
}
|
|
1456
1515
|
|
|
1457
1516
|
if (globals?.preset) {
|
package/dist/pds-reference.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-01-
|
|
2
|
+
"generatedAt": "2026-01-23T07:20:42.584Z",
|
|
3
3
|
"sources": {
|
|
4
4
|
"customElements": "custom-elements.json",
|
|
5
5
|
"ontology": "src\\js\\pds-core\\pds-ontology.js",
|
|
@@ -1928,6 +1928,90 @@
|
|
|
1928
1928
|
],
|
|
1929
1929
|
"notes": []
|
|
1930
1930
|
},
|
|
1931
|
+
"pds-theme": {
|
|
1932
|
+
"tag": "pds-theme",
|
|
1933
|
+
"className": "PdsTheme",
|
|
1934
|
+
"displayName": "Pds Theme",
|
|
1935
|
+
"storyTitle": "Components/Pds Theme",
|
|
1936
|
+
"category": "Components",
|
|
1937
|
+
"description": null,
|
|
1938
|
+
"docsDescription": null,
|
|
1939
|
+
"pdsTags": [
|
|
1940
|
+
"appearance",
|
|
1941
|
+
"autodocs",
|
|
1942
|
+
"pds-theme",
|
|
1943
|
+
"switcher",
|
|
1944
|
+
"theme",
|
|
1945
|
+
"toggle"
|
|
1946
|
+
],
|
|
1947
|
+
"ontology": null,
|
|
1948
|
+
"stories": [],
|
|
1949
|
+
"sourceModule": "public/assets/pds/components/pds-theme.js",
|
|
1950
|
+
"superclass": "HTMLElement",
|
|
1951
|
+
"attributes": [
|
|
1952
|
+
{
|
|
1953
|
+
"name": "label",
|
|
1954
|
+
"description": null,
|
|
1955
|
+
"type": null,
|
|
1956
|
+
"default": null,
|
|
1957
|
+
"fieldName": null
|
|
1958
|
+
}
|
|
1959
|
+
],
|
|
1960
|
+
"properties": [
|
|
1961
|
+
{
|
|
1962
|
+
"name": "label",
|
|
1963
|
+
"attribute": "label",
|
|
1964
|
+
"description": "Gets the legend/aria-label text to display.",
|
|
1965
|
+
"type": null,
|
|
1966
|
+
"default": null,
|
|
1967
|
+
"reflects": false,
|
|
1968
|
+
"privacy": "public"
|
|
1969
|
+
}
|
|
1970
|
+
],
|
|
1971
|
+
"methods": [
|
|
1972
|
+
{
|
|
1973
|
+
"name": "attributeChangedCallback",
|
|
1974
|
+
"description": null,
|
|
1975
|
+
"parameters": [
|
|
1976
|
+
{
|
|
1977
|
+
"name": "name",
|
|
1978
|
+
"type": "any",
|
|
1979
|
+
"description": null,
|
|
1980
|
+
"optional": false
|
|
1981
|
+
},
|
|
1982
|
+
{
|
|
1983
|
+
"name": "oldValue",
|
|
1984
|
+
"type": "any",
|
|
1985
|
+
"description": null,
|
|
1986
|
+
"optional": false
|
|
1987
|
+
},
|
|
1988
|
+
{
|
|
1989
|
+
"name": "newValue",
|
|
1990
|
+
"type": "any",
|
|
1991
|
+
"description": null,
|
|
1992
|
+
"optional": false
|
|
1993
|
+
}
|
|
1994
|
+
],
|
|
1995
|
+
"return": "void"
|
|
1996
|
+
},
|
|
1997
|
+
{
|
|
1998
|
+
"name": "connectedCallback",
|
|
1999
|
+
"description": null,
|
|
2000
|
+
"parameters": [],
|
|
2001
|
+
"return": "void"
|
|
2002
|
+
},
|
|
2003
|
+
{
|
|
2004
|
+
"name": "disconnectedCallback",
|
|
2005
|
+
"description": null,
|
|
2006
|
+
"parameters": [],
|
|
2007
|
+
"return": "void"
|
|
2008
|
+
}
|
|
2009
|
+
],
|
|
2010
|
+
"events": [],
|
|
2011
|
+
"slots": [],
|
|
2012
|
+
"cssParts": [],
|
|
2013
|
+
"notes": []
|
|
2014
|
+
},
|
|
1931
2015
|
"pds-toaster": {
|
|
1932
2016
|
"tag": "pds-toaster",
|
|
1933
2017
|
"className": "AppToaster",
|
|
@@ -4949,6 +5033,33 @@
|
|
|
4949
5033
|
"packages\\pds-storybook\\stories\\components\\PdsTabstrip.stories.js"
|
|
4950
5034
|
]
|
|
4951
5035
|
},
|
|
5036
|
+
"pds-theme": {
|
|
5037
|
+
"slug": "pds-theme",
|
|
5038
|
+
"storyTitle": "Components/Pds Theme",
|
|
5039
|
+
"category": "Components",
|
|
5040
|
+
"name": "Pds Theme",
|
|
5041
|
+
"description": null,
|
|
5042
|
+
"tags": [
|
|
5043
|
+
"appearance",
|
|
5044
|
+
"autodocs",
|
|
5045
|
+
"pds-theme",
|
|
5046
|
+
"switcher",
|
|
5047
|
+
"theme",
|
|
5048
|
+
"toggle"
|
|
5049
|
+
],
|
|
5050
|
+
"pdsParameters": {
|
|
5051
|
+
"tags": [
|
|
5052
|
+
"theme",
|
|
5053
|
+
"appearance",
|
|
5054
|
+
"toggle",
|
|
5055
|
+
"pds-theme"
|
|
5056
|
+
]
|
|
5057
|
+
},
|
|
5058
|
+
"stories": [],
|
|
5059
|
+
"files": [
|
|
5060
|
+
"packages\\pds-storybook\\stories\\components\\PdsTheme\\PdsTheme.stories.js"
|
|
5061
|
+
]
|
|
5062
|
+
},
|
|
4952
5063
|
"pds-toaster": {
|
|
4953
5064
|
"slug": "pds-toaster",
|
|
4954
5065
|
"storyTitle": "Components/Pds Toaster",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pure-ds/storybook",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.31",
|
|
4
4
|
"description": "Storybook showcase for Pure Design System with live configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"pds:build-icons": "pds-build-icons"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@pure-ds/core": "^0.5.
|
|
41
|
+
"@pure-ds/core": "^0.5.31"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@custom-elements-manifest/analyzer": "^0.11.0",
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<pds-theme>` exposes a zero-config theme toggle that updates `PDS.theme` and
|
|
3
|
+
* keeps its UI in sync with programmatic theme changes.
|
|
4
|
+
*
|
|
5
|
+
* @element pds-theme
|
|
6
|
+
* @attr {string} label - Optional legend text (defaults to "Theme").
|
|
7
|
+
*/
|
|
8
|
+
const THEME_OPTIONS = [
|
|
9
|
+
{ value: "system", label: "System", icon: "moon-stars" },
|
|
10
|
+
{ value: "light", label: "Light", icon: "sun" },
|
|
11
|
+
{ value: "dark", label: "Dark", icon: "moon" },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const DEFAULT_LABEL = "Theme";
|
|
15
|
+
const LAYERS = ["tokens", "primitives", "components", "utilities"];
|
|
16
|
+
|
|
17
|
+
class PdsTheme extends HTMLElement {
|
|
18
|
+
static get observedAttributes() {
|
|
19
|
+
return ["label"];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#observer;
|
|
23
|
+
#listening = false;
|
|
24
|
+
|
|
25
|
+
constructor() {
|
|
26
|
+
super();
|
|
27
|
+
this.attachShadow({ mode: "open" });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
connectedCallback() {
|
|
31
|
+
if (!this.shadowRoot.hasChildNodes()) {
|
|
32
|
+
void this.#setup();
|
|
33
|
+
} else {
|
|
34
|
+
this.#attachObserver();
|
|
35
|
+
this.#syncLegend();
|
|
36
|
+
this.#syncCheckedState();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
disconnectedCallback() {
|
|
41
|
+
this.#teardownObserver();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
45
|
+
if (oldValue === newValue) return;
|
|
46
|
+
if (name === "label") {
|
|
47
|
+
this.#syncLegend();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Gets the legend/aria-label text to display.
|
|
53
|
+
* @returns {string}
|
|
54
|
+
*/
|
|
55
|
+
get label() {
|
|
56
|
+
return this.getAttribute("label")?.trim() || DEFAULT_LABEL;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
set label(value) {
|
|
60
|
+
if (value == null || value === "") {
|
|
61
|
+
this.removeAttribute("label");
|
|
62
|
+
} else {
|
|
63
|
+
this.setAttribute("label", value);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async #setup() {
|
|
68
|
+
const componentStyles = PDS.createStylesheet(`
|
|
69
|
+
:host {
|
|
70
|
+
display: block;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
label span {
|
|
74
|
+
display: inline-flex;
|
|
75
|
+
gap: var(--spacing-xs, 0.35rem);
|
|
76
|
+
align-items: center;
|
|
77
|
+
}
|
|
78
|
+
`);
|
|
79
|
+
|
|
80
|
+
await PDS.adoptLayers(this.shadowRoot, LAYERS, [componentStyles]);
|
|
81
|
+
|
|
82
|
+
this.shadowRoot.innerHTML = this.#template();
|
|
83
|
+
|
|
84
|
+
if (!this.#listening) {
|
|
85
|
+
this.shadowRoot.addEventListener("change", this.#handleChange);
|
|
86
|
+
this.#listening = true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.#attachObserver();
|
|
90
|
+
this.#syncLegend();
|
|
91
|
+
this.#syncCheckedState();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#template() {
|
|
95
|
+
const optionsMarkup = THEME_OPTIONS.map(
|
|
96
|
+
(option) => /*html*/`
|
|
97
|
+
<label part="option">
|
|
98
|
+
<input type="radio" name="theme" value="${option.value}" />
|
|
99
|
+
<span>
|
|
100
|
+
<pds-icon icon="${option.icon}" size="sm"></pds-icon>
|
|
101
|
+
${option.label}
|
|
102
|
+
</span>
|
|
103
|
+
</label>`,
|
|
104
|
+
).join("");
|
|
105
|
+
|
|
106
|
+
return /*html*/`
|
|
107
|
+
<form part="form">
|
|
108
|
+
<fieldset part="fieldset" role="radiogroup" aria-label="${DEFAULT_LABEL}" class="buttons">
|
|
109
|
+
<legend part="legend">${DEFAULT_LABEL}</legend>
|
|
110
|
+
${optionsMarkup}
|
|
111
|
+
</fieldset>
|
|
112
|
+
</form>`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
#handleChange = (event) => {
|
|
116
|
+
const target = event.target;
|
|
117
|
+
if (!(target instanceof HTMLInputElement) || target.name !== "theme") {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const { value } = target;
|
|
122
|
+
if (!THEME_OPTIONS.some((option) => option.value === value)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (PDS.theme !== value) {
|
|
127
|
+
PDS.theme = value;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
#attachObserver() {
|
|
133
|
+
if (this.#observer) return;
|
|
134
|
+
|
|
135
|
+
this.#observer = new MutationObserver(() => {
|
|
136
|
+
this.#syncCheckedState();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
this.#observer.observe(document.documentElement, {
|
|
140
|
+
attributes: true,
|
|
141
|
+
attributeFilter: ["data-theme"],
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#teardownObserver() {
|
|
146
|
+
if (!this.#observer) return;
|
|
147
|
+
this.#observer.disconnect();
|
|
148
|
+
this.#observer = undefined;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
#syncLegend() {
|
|
152
|
+
const legend = this.shadowRoot.querySelector("legend");
|
|
153
|
+
const fieldset = this.shadowRoot.querySelector("fieldset");
|
|
154
|
+
if (legend) legend.textContent = this.label;
|
|
155
|
+
if (fieldset) fieldset.setAttribute("aria-label", this.label);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#syncCheckedState() {
|
|
159
|
+
const currentTheme = PDS.theme || "system";
|
|
160
|
+
this.shadowRoot
|
|
161
|
+
.querySelectorAll('input[type="radio"]')
|
|
162
|
+
.forEach((radio) => {
|
|
163
|
+
radio.checked = radio.value === currentTheme;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!customElements.get("pds-theme")) {
|
|
169
|
+
customElements.define("pds-theme", PdsTheme);
|
|
170
|
+
}
|
|
@@ -775,6 +775,16 @@
|
|
|
775
775
|
}
|
|
776
776
|
]
|
|
777
777
|
},
|
|
778
|
+
{
|
|
779
|
+
"name": "pds-theme",
|
|
780
|
+
"description": "PdsTheme component",
|
|
781
|
+
"attributes": [
|
|
782
|
+
{
|
|
783
|
+
"name": "label",
|
|
784
|
+
"description": ""
|
|
785
|
+
}
|
|
786
|
+
]
|
|
787
|
+
},
|
|
778
788
|
{
|
|
779
789
|
"name": "pds-toaster",
|
|
780
790
|
"description": "AppToaster component"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import { createComponentDocsPage } from '../../reference/reference-docs.js';
|
|
3
|
+
|
|
4
|
+
const componentDescription = `The \`<pds-theme>\` component lets users switch between **system**, **light**, and **dark** modes.
|
|
5
|
+
It updates \`PDS.theme\`, stays in sync with programmatic changes, and emits \`pds:theme:changed\` so bootstrap code can react (for example, to show a toast).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Quick Reference
|
|
10
|
+
|
|
11
|
+
| Attribute | Type | Default | Description |
|
|
12
|
+
|-----------|------|---------|-------------|
|
|
13
|
+
| \`label\` | string | \`"Theme"\` | Custom legend + aria-label |
|
|
14
|
+
|
|
15
|
+
Listen for global theme updates with \`PDS.addEventListener('pds:theme:changed', handler)\` when other UI needs to respond.
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const docsParameters = {
|
|
19
|
+
description: {
|
|
20
|
+
component: componentDescription
|
|
21
|
+
},
|
|
22
|
+
page: createComponentDocsPage('pds-theme')
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const renderThemeToggle = (args) => html`
|
|
26
|
+
<pds-theme
|
|
27
|
+
label=${args.label || ''}
|
|
28
|
+
></pds-theme>
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
title: 'Components/Pds Theme',
|
|
33
|
+
tags: ['autodocs', 'theme', 'appearance', 'pds-theme', 'switcher'],
|
|
34
|
+
parameters: {
|
|
35
|
+
pds: {
|
|
36
|
+
tags: ['theme', 'appearance', 'toggle', 'pds-theme']
|
|
37
|
+
},
|
|
38
|
+
docs: docsParameters
|
|
39
|
+
},
|
|
40
|
+
argTypes: {
|
|
41
|
+
label: {
|
|
42
|
+
control: 'text',
|
|
43
|
+
description: 'Legend + aria-label text'
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const Playground = {
|
|
49
|
+
name: 'Interactive Playground',
|
|
50
|
+
args: {
|
|
51
|
+
label: 'Theme'
|
|
52
|
+
},
|
|
53
|
+
parameters: {
|
|
54
|
+
docs: {
|
|
55
|
+
description: {
|
|
56
|
+
story: 'Use the controls to adjust the component label.'
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
render: (args) => html`
|
|
61
|
+
<div class="stack-md">
|
|
62
|
+
<p class="text-muted">The toggle updates <code>PDS.theme</code> so the rest of your UI stays in sync.</p>
|
|
63
|
+
${renderThemeToggle(args)}
|
|
64
|
+
</div>
|
|
65
|
+
`
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
|
|
@@ -21,7 +21,6 @@ What you get (key files):
|
|
|
21
21
|
- `pds.config.js` — your design system config
|
|
22
22
|
- `src/js/app.js` — bootstraps PDS and mounts the app
|
|
23
23
|
- `public/assets/my/my-home.js` — your first web component
|
|
24
|
-
- `public/assets/my/my-theme.js` — simple dark/light mode switcher
|
|
25
24
|
- `public/index.html` — app shell
|
|
26
25
|
|
|
27
26
|
**Next edits:**
|