@pure-ds/storybook 0.5.56 → 0.6.1
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/addons/description/register.js +22 -23
- package/.storybook/addons/html-preview/register.js +6 -31
- package/.storybook/htmlPreview.js +219 -219
- package/.storybook/main.js +6 -11
- package/.storybook/preview.js +2 -16
- package/README.md +3 -45
- package/bin/index.js +1 -15
- package/dist/pds-reference.json +332 -192
- package/package.json +2 -2
- package/public/assets/css/app-legacy.css +1214 -0
- package/public/assets/css/app.css +0 -1214
- package/public/assets/img/favicon.png +0 -0
- package/public/assets/img/icons/github-logo.svg +1 -0
- package/public/assets/img/og.webp +0 -0
- package/public/assets/img/pds-logo.svg +80 -0
- package/public/assets/js/app.js +106 -5636
- package/public/assets/js/pds-manager.js +163 -163
- package/public/assets/js/pds.js +7 -7
- package/public/assets/pds/components/pds-live-edit.js +1555 -0
- package/public/assets/pds/components/pds-omnibox.js +558 -377
- package/public/assets/pds/components/pds-richtext.js +57 -7
- package/public/assets/pds/components/pds-theme.js +58 -0
- package/public/assets/pds/core/pds-manager.js +163 -163
- package/public/assets/pds/custom-elements.json +471 -0
- package/public/assets/pds/pds-css-complete.json +590 -179
- package/public/assets/pds/pds-runtime-config.json +3 -2
- package/public/assets/pds/pds.css-data.json +201 -165
- package/public/assets/pds/styles/pds-components.css +192 -96
- package/public/assets/pds/styles/pds-components.css.js +384 -192
- package/public/assets/pds/styles/pds-primitives.css +1 -1
- package/public/assets/pds/styles/pds-primitives.css.js +2 -2
- package/public/assets/pds/styles/pds-styles.css +201 -102
- package/public/assets/pds/styles/pds-styles.css.js +404 -198
- package/public/assets/pds/styles/pds-tokens.css +5 -4
- package/public/assets/pds/styles/pds-tokens.css.js +12 -2
- package/public/assets/pds/styles/pds-utilities.css +3 -1
- package/public/assets/pds/styles/pds-utilities.css.js +6 -2
- package/public/assets/pds/vscode-custom-data.json +42 -25
- package/scripts/generate-stories.js +1 -1
- package/scripts/package-build.js +1 -6
- package/src/js/app.js +114 -17
- package/src/js/common/common.js +9 -0
- package/src/js/pds-core/pds-config.js +709 -1
- package/src/js/pds-core/pds-enhancers.js +61 -4
- package/src/js/pds-core/pds-generator.js +87 -16
- package/src/js/pds-core/pds-live.js +545 -366
- package/src/js/pds-core/pds-ontology.js +8 -0
- package/src/js/pds-core/pds-start-helpers.js +15 -0
- package/src/js/pds-core/pds-theme-utils.js +33 -0
- package/src/js/pds.d.ts +3 -0
- package/src/js/pds.js +28 -15
- package/stories/WhatIsPDS.md +1 -1
- package/stories/WhatIsPDS.stories.js +34 -34
- package/stories/components/PdsForm.stories.js +4981 -4981
- package/stories/components/PdsTheme/PdsTheme.stories.js +68 -68
- package/stories/enhancements/Accordion.stories.js +309 -309
- package/stories/enhancements/ButtonWorking.stories.js +127 -127
- package/stories/enhancements/_enhancement-header.js +48 -48
- package/stories/foundations/HTMLDefaults.stories.js +470 -470
- package/stories/foundations/Icons.stories.js +1 -1
- package/stories/primitives/Callouts.stories.js +225 -225
- package/stories/reference/ReferenceCatalog.stories.js +28 -28
- package/stories/reference/reference-catalog.js +442 -442
- package/stories/reference/reference-docs.js +470 -470
- package/stories/utils/PdsAsk.stories.js +1 -1
- package/stories/utils/PdsParse.stories.js +4 -5
- package/stories/utils/PdsToast.stories.js +1 -1
- package/.storybook/addons/description/register.cjs +0 -61
- package/.storybook/addons/html-preview/constants.cjs +0 -12
- package/.storybook/addons/html-preview/register.cjs +0 -41
- package/.storybook/addons/pds-configurator/SearchTool.cjs +0 -47
- package/.storybook/addons/pds-configurator/SearchTool.js +0 -46
- package/.storybook/addons/pds-configurator/Tool.cjs +0 -36
- package/.storybook/addons/pds-configurator/Tool.js +0 -34
- package/.storybook/addons/pds-configurator/constants.cjs +0 -16
- package/.storybook/addons/pds-configurator/constants.js +0 -9
- package/.storybook/addons/pds-configurator/preview.js +0 -160
- package/.storybook/addons/pds-configurator/register.cjs +0 -12
- package/.storybook/addons/pds-configurator/register.js +0 -13
- package/public/assets/img/icon-512x512.png +0 -0
- package/public/assets/img/logo-trans.png +0 -0
- package/public/assets/img/logo.png +0 -0
- package/src/js/pds-configurator/figma-export.js +0 -153
- package/src/js/pds-configurator/pds-config-form.js +0 -1049
- package/src/js/pds-configurator/pds-configurator.js +0 -22
- package/src/js/pds-configurator/pds-demo.js +0 -3668
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { addons, types } from '@storybook/manager-api';
|
|
2
2
|
import { AddonPanel } from '@storybook/components';
|
|
3
|
-
import React from 'react';
|
|
3
|
+
import React, { useEffect, useState } from 'react';
|
|
4
4
|
import { Markdown } from '@storybook/blocks';
|
|
5
5
|
|
|
6
6
|
const ADDON_ID = 'pds-description';
|
|
7
7
|
const PANEL_ID = `${ADDON_ID}/panel`;
|
|
8
8
|
|
|
9
9
|
const DescriptionPanel = () => {
|
|
10
|
-
const [description, setDescription] =
|
|
11
|
-
const [title, setTitle] =
|
|
10
|
+
const [description, setDescription] = useState('');
|
|
11
|
+
const [title, setTitle] = useState('');
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
useEffect(() => {
|
|
14
14
|
const channel = addons.getChannel();
|
|
15
15
|
|
|
16
16
|
const updateDescription = (data) => {
|
|
@@ -25,23 +25,23 @@ const DescriptionPanel = () => {
|
|
|
25
25
|
};
|
|
26
26
|
}, []);
|
|
27
27
|
|
|
28
|
-
return React.createElement(
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
return React.createElement('div', {
|
|
29
|
+
style: {
|
|
30
|
+
padding: '1rem',
|
|
31
|
+
height: '100%',
|
|
32
|
+
overflow: 'auto'
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
React.createElement('div', {
|
|
31
36
|
style: {
|
|
32
|
-
|
|
33
|
-
height: '100%',
|
|
34
|
-
overflow: 'auto'
|
|
37
|
+
marginBottom: '0.5rem'
|
|
35
38
|
}
|
|
36
39
|
},
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
{ style: { fontSize: '1rem' } },
|
|
43
|
-
title
|
|
44
|
-
)
|
|
40
|
+
React.createElement('strong', {
|
|
41
|
+
style: {
|
|
42
|
+
fontSize: '1rem'
|
|
43
|
+
}
|
|
44
|
+
}, title)
|
|
45
45
|
),
|
|
46
46
|
React.createElement(Markdown, null, description)
|
|
47
47
|
);
|
|
@@ -51,11 +51,10 @@ addons.register(ADDON_ID, () => {
|
|
|
51
51
|
addons.add(PANEL_ID, {
|
|
52
52
|
type: types.PANEL,
|
|
53
53
|
title: 'Description',
|
|
54
|
-
render: ({ active }) =>
|
|
55
|
-
React.createElement(
|
|
56
|
-
AddonPanel,
|
|
57
|
-
{ active },
|
|
54
|
+
render: ({ active }) => {
|
|
55
|
+
return React.createElement(AddonPanel, { active },
|
|
58
56
|
React.createElement(DescriptionPanel)
|
|
59
|
-
)
|
|
57
|
+
);
|
|
58
|
+
}
|
|
60
59
|
});
|
|
61
60
|
});
|
|
@@ -1,41 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { addons, types } from '@storybook/manager-api';
|
|
3
3
|
import { ADDON_ID, PANEL_ID } from './constants.js';
|
|
4
|
-
|
|
5
|
-
const PanelLoader = ({ active }) => {
|
|
6
|
-
const [PanelComponent, setPanelComponent] = React.useState(null);
|
|
7
|
-
|
|
8
|
-
React.useEffect(() => {
|
|
9
|
-
let mounted = true;
|
|
10
|
-
|
|
11
|
-
import('./Panel.jsx')
|
|
12
|
-
.then((mod) => {
|
|
13
|
-
if (!mounted) return;
|
|
14
|
-
const Resolved = mod.Panel || mod.default || null;
|
|
15
|
-
if (Resolved) setPanelComponent(() => Resolved);
|
|
16
|
-
})
|
|
17
|
-
.catch((error) => {
|
|
18
|
-
console.error('Failed to load html-preview Panel:', error);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
return () => {
|
|
22
|
-
mounted = false;
|
|
23
|
-
};
|
|
24
|
-
}, []);
|
|
25
|
-
|
|
26
|
-
if (!PanelComponent) return null;
|
|
27
|
-
return React.createElement(PanelComponent, { active });
|
|
28
|
-
};
|
|
4
|
+
import { Panel } from './Panel.jsx';
|
|
29
5
|
|
|
30
6
|
addons.register(ADDON_ID, () => {
|
|
31
7
|
addons.add(PANEL_ID, {
|
|
32
8
|
type: types.PANEL,
|
|
33
9
|
title: 'Code',
|
|
34
|
-
render: ({ active, key }) =>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
)
|
|
10
|
+
render: ({ active, key }) => (
|
|
11
|
+
<div style={{ display: active ? 'block' : 'none', height: '100%' }}>
|
|
12
|
+
<Panel key={key} active={active} />
|
|
13
|
+
</div>
|
|
14
|
+
)
|
|
40
15
|
});
|
|
41
16
|
});
|
|
@@ -1,219 +1,219 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTML Preview and Copy Utility for Storybook
|
|
3
|
-
* Provides HTML source viewing and copy functionality for all stories
|
|
4
|
-
* Uses Shiki for syntax highlighting
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { render as litRender } from 'lit';
|
|
8
|
-
import { highlight, getCurrentTheme, escapeHtml, preloadShiki } from './shiki.js';
|
|
9
|
-
|
|
10
|
-
// Pre-load Shiki in the background
|
|
11
|
-
preloadShiki();
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Format HTML string with proper indentation
|
|
15
|
-
*/
|
|
16
|
-
export function formatHTML(html) {
|
|
17
|
-
if (!html) return '';
|
|
18
|
-
|
|
19
|
-
let formatted = '';
|
|
20
|
-
let indent = 0;
|
|
21
|
-
const tab = ' ';
|
|
22
|
-
|
|
23
|
-
const tokens = html.split(/(<[^>]+>)/g).filter(Boolean);
|
|
24
|
-
|
|
25
|
-
tokens.forEach((token) => {
|
|
26
|
-
if (token.startsWith('</')) {
|
|
27
|
-
// Closing tag
|
|
28
|
-
indent = Math.max(0, indent - 1);
|
|
29
|
-
formatted += '\n' + tab.repeat(indent) + token;
|
|
30
|
-
} else if (token.startsWith('<')) {
|
|
31
|
-
// Opening tag
|
|
32
|
-
const isSelfClosing = token.endsWith('/>') || token.match(/<(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)/);
|
|
33
|
-
formatted += '\n' + tab.repeat(indent) + token;
|
|
34
|
-
if (!isSelfClosing) {
|
|
35
|
-
indent++;
|
|
36
|
-
}
|
|
37
|
-
} else if (token.trim()) {
|
|
38
|
-
// Text content
|
|
39
|
-
formatted += token.trim();
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
return formatted.trim();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Extract HTML from a rendered story
|
|
48
|
-
*/
|
|
49
|
-
export function extractHTML(element) {
|
|
50
|
-
if (!element) return '';
|
|
51
|
-
|
|
52
|
-
// Clone the element to avoid modifying the original
|
|
53
|
-
const clone = element.cloneNode(true);
|
|
54
|
-
|
|
55
|
-
// Clean up any Storybook-specific attributes or classes
|
|
56
|
-
const cleanElement = (el) => {
|
|
57
|
-
if (el.removeAttribute) {
|
|
58
|
-
el.removeAttribute('data-story-id');
|
|
59
|
-
el.removeAttribute('data-view-mode');
|
|
60
|
-
}
|
|
61
|
-
if (el.children) {
|
|
62
|
-
Array.from(el.children).forEach(cleanElement);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
cleanElement(clone);
|
|
67
|
-
return clone.innerHTML || '';
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Strip Lit template comments from rendered HTML
|
|
72
|
-
* Removes markers like <!--?lit$641514976$-->, <!---->, etc.
|
|
73
|
-
*/
|
|
74
|
-
export function stripLitComments(html) {
|
|
75
|
-
if (!html) return '';
|
|
76
|
-
|
|
77
|
-
// Remove Lit binding markers: <!--?lit$...$-->
|
|
78
|
-
html = html.replace(/<!--\?lit\$[^$]+\$-->/g, '');
|
|
79
|
-
|
|
80
|
-
// Remove empty Lit comments: <!---->
|
|
81
|
-
html = html.replace(/<!---->/g, '');
|
|
82
|
-
|
|
83
|
-
// Remove Lit template marker comments: <!--lit-part...-->
|
|
84
|
-
html = html.replace(/<!--lit-part[^>]*-->/g, '');
|
|
85
|
-
html = html.replace(/<!--\/lit-part-->/g, '');
|
|
86
|
-
|
|
87
|
-
// Clean up any resulting multiple whitespace/newlines
|
|
88
|
-
html = html.replace(/\n\s*\n\s*\n/g, '\n\n');
|
|
89
|
-
|
|
90
|
-
return html;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Transform Lit template result to HTML string
|
|
95
|
-
*/
|
|
96
|
-
export async function litToHTML(templateResult) {
|
|
97
|
-
if (!templateResult || !templateResult._$litType$) {
|
|
98
|
-
return '';
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const temp = document.createElement('div');
|
|
102
|
-
litRender(templateResult, temp);
|
|
103
|
-
|
|
104
|
-
// Wait for any custom elements to upgrade
|
|
105
|
-
await new Promise(resolve => setTimeout(resolve, 50));
|
|
106
|
-
|
|
107
|
-
// Strip Lit comments before formatting
|
|
108
|
-
const cleanedHTML = stripLitComments(temp.innerHTML);
|
|
109
|
-
|
|
110
|
-
return formatHTML(cleanedHTML);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Global decorator that provides HTML source code display
|
|
115
|
-
*/
|
|
116
|
-
export const withHTMLSource = (storyFn, context) => {
|
|
117
|
-
const story = storyFn();
|
|
118
|
-
|
|
119
|
-
// For docs mode, Storybook will automatically show source
|
|
120
|
-
// We just need to ensure the parameters are set
|
|
121
|
-
if (context.viewMode === 'docs') {
|
|
122
|
-
return story;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// For canvas/story mode, we'll add a source viewer below the story
|
|
126
|
-
const container = document.createElement('div');
|
|
127
|
-
container.style.cssText = 'display: flex; flex-direction: column; gap: 1rem; width: 100%; height: 100%;';
|
|
128
|
-
|
|
129
|
-
// Story container
|
|
130
|
-
const storyContainer = document.createElement('div');
|
|
131
|
-
storyContainer.id = 'story-preview';
|
|
132
|
-
storyContainer.style.cssText = 'flex: 1; overflow: auto;';
|
|
133
|
-
|
|
134
|
-
// Source panel with copy button
|
|
135
|
-
const sourceSection = document.createElement('div');
|
|
136
|
-
sourceSection.className = 'html-source-section';
|
|
137
|
-
sourceSection.innerHTML = `
|
|
138
|
-
<details class="html-source-details surface-subtle">
|
|
139
|
-
<summary class="html-source-summary surface-elevated">
|
|
140
|
-
<span class="html-source-title">
|
|
141
|
-
<span class="html-source-icon">📝</span>
|
|
142
|
-
<span>View HTML Source</span>
|
|
143
|
-
</span>
|
|
144
|
-
<button id="copy-source-btn" class="btn-outline">
|
|
145
|
-
📋 Copy HTML
|
|
146
|
-
</button>
|
|
147
|
-
</summary>
|
|
148
|
-
<div class="html-source-content">
|
|
149
|
-
<pre class="html-source-pre"><code class="html-source-code"></code></pre>
|
|
150
|
-
</div>
|
|
151
|
-
</details>
|
|
152
|
-
`;
|
|
153
|
-
|
|
154
|
-
container.appendChild(storyContainer);
|
|
155
|
-
container.appendChild(sourceSection);
|
|
156
|
-
|
|
157
|
-
// Render the story
|
|
158
|
-
setTimeout(async () => {
|
|
159
|
-
if (story && story._$litType$) {
|
|
160
|
-
litRender(story, storyContainer);
|
|
161
|
-
} else if (story instanceof HTMLElement) {
|
|
162
|
-
storyContainer.appendChild(story);
|
|
163
|
-
} else if (typeof story === 'string') {
|
|
164
|
-
storyContainer.innerHTML = story;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Extract and display HTML
|
|
168
|
-
setTimeout(async () => {
|
|
169
|
-
let html = '';
|
|
170
|
-
|
|
171
|
-
if (story && story._$litType$) {
|
|
172
|
-
html = await litToHTML(story);
|
|
173
|
-
} else {
|
|
174
|
-
html = formatHTML(extractHTML(storyContainer));
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const codeEl = sourceSection.querySelector('.html-source-code');
|
|
178
|
-
const preEl = sourceSection.querySelector('.html-source-pre');
|
|
179
|
-
if (preEl && html) {
|
|
180
|
-
// Use Shiki for syntax highlighting
|
|
181
|
-
const theme = getCurrentTheme();
|
|
182
|
-
const highlighted = await highlight(html, 'html', theme);
|
|
183
|
-
|
|
184
|
-
// Shiki returns complete <pre><code>...</code></pre>, replace the whole pre element
|
|
185
|
-
preEl.outerHTML = highlighted;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Setup copy button
|
|
189
|
-
const copyBtn = sourceSection.querySelector('#copy-source-btn');
|
|
190
|
-
if (copyBtn && html) {
|
|
191
|
-
copyBtn.addEventListener('click', async (e) => {
|
|
192
|
-
e.preventDefault();
|
|
193
|
-
e.stopPropagation();
|
|
194
|
-
|
|
195
|
-
try {
|
|
196
|
-
await navigator.clipboard.writeText(html);
|
|
197
|
-
const originalClass = copyBtn.className;
|
|
198
|
-
copyBtn.textContent = '✅ Copied!';
|
|
199
|
-
copyBtn.className = 'btn-primary';
|
|
200
|
-
|
|
201
|
-
setTimeout(() => {
|
|
202
|
-
copyBtn.textContent = '📋 Copy HTML';
|
|
203
|
-
copyBtn.className = originalClass;
|
|
204
|
-
}, 2000);
|
|
205
|
-
} catch (err) {
|
|
206
|
-
console.error('Copy failed:', err);
|
|
207
|
-
const originalText = copyBtn.textContent;
|
|
208
|
-
copyBtn.textContent = '❌ Failed';
|
|
209
|
-
setTimeout(() => {
|
|
210
|
-
copyBtn.textContent = originalText;
|
|
211
|
-
}, 2000);
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
}, 100);
|
|
216
|
-
}, 0);
|
|
217
|
-
|
|
218
|
-
return container;
|
|
219
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* HTML Preview and Copy Utility for Storybook
|
|
3
|
+
* Provides HTML source viewing and copy functionality for all stories
|
|
4
|
+
* Uses Shiki for syntax highlighting
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { render as litRender } from 'lit';
|
|
8
|
+
import { highlight, getCurrentTheme, escapeHtml, preloadShiki } from './shiki.js';
|
|
9
|
+
|
|
10
|
+
// Pre-load Shiki in the background
|
|
11
|
+
preloadShiki();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Format HTML string with proper indentation
|
|
15
|
+
*/
|
|
16
|
+
export function formatHTML(html) {
|
|
17
|
+
if (!html) return '';
|
|
18
|
+
|
|
19
|
+
let formatted = '';
|
|
20
|
+
let indent = 0;
|
|
21
|
+
const tab = ' ';
|
|
22
|
+
|
|
23
|
+
const tokens = html.split(/(<[^>]+>)/g).filter(Boolean);
|
|
24
|
+
|
|
25
|
+
tokens.forEach((token) => {
|
|
26
|
+
if (token.startsWith('</')) {
|
|
27
|
+
// Closing tag
|
|
28
|
+
indent = Math.max(0, indent - 1);
|
|
29
|
+
formatted += '\n' + tab.repeat(indent) + token;
|
|
30
|
+
} else if (token.startsWith('<')) {
|
|
31
|
+
// Opening tag
|
|
32
|
+
const isSelfClosing = token.endsWith('/>') || token.match(/<(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)/);
|
|
33
|
+
formatted += '\n' + tab.repeat(indent) + token;
|
|
34
|
+
if (!isSelfClosing) {
|
|
35
|
+
indent++;
|
|
36
|
+
}
|
|
37
|
+
} else if (token.trim()) {
|
|
38
|
+
// Text content
|
|
39
|
+
formatted += token.trim();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return formatted.trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Extract HTML from a rendered story
|
|
48
|
+
*/
|
|
49
|
+
export function extractHTML(element) {
|
|
50
|
+
if (!element) return '';
|
|
51
|
+
|
|
52
|
+
// Clone the element to avoid modifying the original
|
|
53
|
+
const clone = element.cloneNode(true);
|
|
54
|
+
|
|
55
|
+
// Clean up any Storybook-specific attributes or classes
|
|
56
|
+
const cleanElement = (el) => {
|
|
57
|
+
if (el.removeAttribute) {
|
|
58
|
+
el.removeAttribute('data-story-id');
|
|
59
|
+
el.removeAttribute('data-view-mode');
|
|
60
|
+
}
|
|
61
|
+
if (el.children) {
|
|
62
|
+
Array.from(el.children).forEach(cleanElement);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
cleanElement(clone);
|
|
67
|
+
return clone.innerHTML || '';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Strip Lit template comments from rendered HTML
|
|
72
|
+
* Removes markers like <!--?lit$641514976$-->, <!---->, etc.
|
|
73
|
+
*/
|
|
74
|
+
export function stripLitComments(html) {
|
|
75
|
+
if (!html) return '';
|
|
76
|
+
|
|
77
|
+
// Remove Lit binding markers: <!--?lit$...$-->
|
|
78
|
+
html = html.replace(/<!--\?lit\$[^$]+\$-->/g, '');
|
|
79
|
+
|
|
80
|
+
// Remove empty Lit comments: <!---->
|
|
81
|
+
html = html.replace(/<!---->/g, '');
|
|
82
|
+
|
|
83
|
+
// Remove Lit template marker comments: <!--lit-part...-->
|
|
84
|
+
html = html.replace(/<!--lit-part[^>]*-->/g, '');
|
|
85
|
+
html = html.replace(/<!--\/lit-part-->/g, '');
|
|
86
|
+
|
|
87
|
+
// Clean up any resulting multiple whitespace/newlines
|
|
88
|
+
html = html.replace(/\n\s*\n\s*\n/g, '\n\n');
|
|
89
|
+
|
|
90
|
+
return html;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Transform Lit template result to HTML string
|
|
95
|
+
*/
|
|
96
|
+
export async function litToHTML(templateResult) {
|
|
97
|
+
if (!templateResult || !templateResult._$litType$) {
|
|
98
|
+
return '';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const temp = document.createElement('div');
|
|
102
|
+
litRender(templateResult, temp);
|
|
103
|
+
|
|
104
|
+
// Wait for any custom elements to upgrade
|
|
105
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
106
|
+
|
|
107
|
+
// Strip Lit comments before formatting
|
|
108
|
+
const cleanedHTML = stripLitComments(temp.innerHTML);
|
|
109
|
+
|
|
110
|
+
return formatHTML(cleanedHTML);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Global decorator that provides HTML source code display
|
|
115
|
+
*/
|
|
116
|
+
export const withHTMLSource = (storyFn, context) => {
|
|
117
|
+
const story = storyFn();
|
|
118
|
+
|
|
119
|
+
// For docs mode, Storybook will automatically show source
|
|
120
|
+
// We just need to ensure the parameters are set
|
|
121
|
+
if (context.viewMode === 'docs') {
|
|
122
|
+
return story;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// For canvas/story mode, we'll add a source viewer below the story
|
|
126
|
+
const container = document.createElement('div');
|
|
127
|
+
container.style.cssText = 'display: flex; flex-direction: column; gap: 1rem; width: 100%; height: 100%;';
|
|
128
|
+
|
|
129
|
+
// Story container
|
|
130
|
+
const storyContainer = document.createElement('div');
|
|
131
|
+
storyContainer.id = 'story-preview';
|
|
132
|
+
storyContainer.style.cssText = 'flex: 1; overflow: auto;';
|
|
133
|
+
|
|
134
|
+
// Source panel with copy button
|
|
135
|
+
const sourceSection = document.createElement('div');
|
|
136
|
+
sourceSection.className = 'html-source-section';
|
|
137
|
+
sourceSection.innerHTML = `
|
|
138
|
+
<details class="html-source-details surface-subtle">
|
|
139
|
+
<summary class="html-source-summary surface-elevated">
|
|
140
|
+
<span class="html-source-title">
|
|
141
|
+
<span class="html-source-icon">📝</span>
|
|
142
|
+
<span>View HTML Source</span>
|
|
143
|
+
</span>
|
|
144
|
+
<button id="copy-source-btn" class="btn-outline">
|
|
145
|
+
📋 Copy HTML
|
|
146
|
+
</button>
|
|
147
|
+
</summary>
|
|
148
|
+
<div class="html-source-content">
|
|
149
|
+
<pre class="html-source-pre"><code class="html-source-code"></code></pre>
|
|
150
|
+
</div>
|
|
151
|
+
</details>
|
|
152
|
+
`;
|
|
153
|
+
|
|
154
|
+
container.appendChild(storyContainer);
|
|
155
|
+
container.appendChild(sourceSection);
|
|
156
|
+
|
|
157
|
+
// Render the story
|
|
158
|
+
setTimeout(async () => {
|
|
159
|
+
if (story && story._$litType$) {
|
|
160
|
+
litRender(story, storyContainer);
|
|
161
|
+
} else if (story instanceof HTMLElement) {
|
|
162
|
+
storyContainer.appendChild(story);
|
|
163
|
+
} else if (typeof story === 'string') {
|
|
164
|
+
storyContainer.innerHTML = story;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Extract and display HTML
|
|
168
|
+
setTimeout(async () => {
|
|
169
|
+
let html = '';
|
|
170
|
+
|
|
171
|
+
if (story && story._$litType$) {
|
|
172
|
+
html = await litToHTML(story);
|
|
173
|
+
} else {
|
|
174
|
+
html = formatHTML(extractHTML(storyContainer));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const codeEl = sourceSection.querySelector('.html-source-code');
|
|
178
|
+
const preEl = sourceSection.querySelector('.html-source-pre');
|
|
179
|
+
if (preEl && html) {
|
|
180
|
+
// Use Shiki for syntax highlighting
|
|
181
|
+
const theme = getCurrentTheme();
|
|
182
|
+
const highlighted = await highlight(html, 'html', theme);
|
|
183
|
+
|
|
184
|
+
// Shiki returns complete <pre><code>...</code></pre>, replace the whole pre element
|
|
185
|
+
preEl.outerHTML = highlighted;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Setup copy button
|
|
189
|
+
const copyBtn = sourceSection.querySelector('#copy-source-btn');
|
|
190
|
+
if (copyBtn && html) {
|
|
191
|
+
copyBtn.addEventListener('click', async (e) => {
|
|
192
|
+
e.preventDefault();
|
|
193
|
+
e.stopPropagation();
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
await navigator.clipboard.writeText(html);
|
|
197
|
+
const originalClass = copyBtn.className;
|
|
198
|
+
copyBtn.textContent = '✅ Copied!';
|
|
199
|
+
copyBtn.className = 'btn-primary';
|
|
200
|
+
|
|
201
|
+
setTimeout(() => {
|
|
202
|
+
copyBtn.textContent = '📋 Copy HTML';
|
|
203
|
+
copyBtn.className = originalClass;
|
|
204
|
+
}, 2000);
|
|
205
|
+
} catch (err) {
|
|
206
|
+
console.error('Copy failed:', err);
|
|
207
|
+
const originalText = copyBtn.textContent;
|
|
208
|
+
copyBtn.textContent = '❌ Failed';
|
|
209
|
+
setTimeout(() => {
|
|
210
|
+
copyBtn.textContent = originalText;
|
|
211
|
+
}, 2000);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}, 100);
|
|
216
|
+
}, 0);
|
|
217
|
+
|
|
218
|
+
return container;
|
|
219
|
+
};
|
package/.storybook/main.js
CHANGED
|
@@ -29,9 +29,8 @@ const config = {
|
|
|
29
29
|
addons: [
|
|
30
30
|
'@storybook/addon-links',
|
|
31
31
|
'@storybook/addon-essentials',
|
|
32
|
-
'./addons/
|
|
33
|
-
'./addons/
|
|
34
|
-
'./addons/description/register.cjs'
|
|
32
|
+
'./addons/html-preview/register.js',
|
|
33
|
+
'./addons/description/register.js'
|
|
35
34
|
],
|
|
36
35
|
framework: {
|
|
37
36
|
name: '@storybook/web-components-vite',
|
|
@@ -74,15 +73,11 @@ const config = {
|
|
|
74
73
|
'#pds/lit': resolve(pdsSrcPath, 'js/lit.js'),
|
|
75
74
|
};
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
aliases['@pds-src/js/pds-configurator'] = resolve(currentDirname, '../../../packages/pds-configurator/src');
|
|
81
|
-
|
|
82
|
-
// Map the local src folder to the monorepo root src folder
|
|
83
|
-
// This eliminates the need for a copied src folder during development
|
|
76
|
+
// In monorepo, map the local src folder to the monorepo root src folder
|
|
77
|
+
// This eliminates the need for a copied src folder during development
|
|
78
|
+
if (!isPackage) {
|
|
84
79
|
aliases[resolve(currentDirname, '../src')] = pdsSrcPath;
|
|
85
|
-
|
|
80
|
+
}
|
|
86
81
|
|
|
87
82
|
aliases['@pds-src'] = pdsSrcPath;
|
|
88
83
|
config.resolve.alias = aliases;
|
package/.storybook/preview.js
CHANGED
|
@@ -2,12 +2,11 @@ import { addons } from '@storybook/preview-api';
|
|
|
2
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
|
-
import { PDS } from '@
|
|
5
|
+
import { PDS } from '@pds-src/js/pds.js';
|
|
6
6
|
import { presets } from '@pds-src/js/pds-core/pds-config.js';
|
|
7
7
|
import { Generator } from '@pds-src/js/pds-core/pds-generator.js';
|
|
8
8
|
import { applyStyles } from '@pds-src/js/pds-core/pds-runtime.js';
|
|
9
9
|
import { config as userConfig } from '@user/pds-config';
|
|
10
|
-
import './addons/pds-configurator/preview.js';
|
|
11
10
|
import { withHTMLExtractor } from './addons/html-preview/preview.js';
|
|
12
11
|
import { withDescription } from './addons/description/preview.js';
|
|
13
12
|
import './htmlPreview.css';
|
|
@@ -17,10 +16,6 @@ import { toastFormData } from '../stories/utils/toast-utils.js';
|
|
|
17
16
|
// Expose toastFormData globally for inline event handlers
|
|
18
17
|
window.toastFormData = toastFormData;
|
|
19
18
|
|
|
20
|
-
console.log('[PDS Storybook] preview.js loaded');
|
|
21
|
-
console.log('[PDS Storybook] PDS import', PDS ? 'OK' : 'MISSING');
|
|
22
|
-
console.log('[PDS Storybook] location', window.location.href);
|
|
23
|
-
|
|
24
19
|
// Get initial preset from storage or URL or default
|
|
25
20
|
const getInitialPreset = () => {
|
|
26
21
|
try {
|
|
@@ -65,7 +60,6 @@ const setLiveGenerator = (generator) => {
|
|
|
65
60
|
};
|
|
66
61
|
|
|
67
62
|
PDS.addEventListener('pds:ready', (event) => {
|
|
68
|
-
console.log('[PDS Storybook] pds:ready event', event?.detail || event);
|
|
69
63
|
if (event?.detail?.generator) {
|
|
70
64
|
setLiveGenerator(event.detail.generator);
|
|
71
65
|
}
|
|
@@ -73,7 +67,6 @@ PDS.addEventListener('pds:ready', (event) => {
|
|
|
73
67
|
|
|
74
68
|
// Wrap top-level await in IIFE for production build compatibility
|
|
75
69
|
(async () => {
|
|
76
|
-
console.log('[PDS Storybook] init start');
|
|
77
70
|
PDS.initializing = true;
|
|
78
71
|
|
|
79
72
|
// NEVER apply global styles on init - only apply in story decorator
|
|
@@ -108,14 +101,7 @@ PDS.addEventListener('pds:ready', (event) => {
|
|
|
108
101
|
// Always override mode to 'live' regardless of user config to prevent style injection failures
|
|
109
102
|
pdsOptions.mode = 'live';
|
|
110
103
|
|
|
111
|
-
|
|
112
|
-
try {
|
|
113
|
-
initResult = await PDS.start(pdsOptions);
|
|
114
|
-
console.log('[PDS Storybook] PDS.start resolved', initResult);
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error('[PDS Storybook] PDS.start failed', error);
|
|
117
|
-
throw error;
|
|
118
|
-
}
|
|
104
|
+
const initResult = await PDS.start(pdsOptions);
|
|
119
105
|
setLiveGenerator(initResult?.generator);
|
|
120
106
|
PDS.initializing = false;
|
|
121
107
|
|