@pure-ds/storybook 0.4.5 → 0.4.7
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/html-preview/Panel.jsx +56 -107
- package/.storybook/addons/pds-configurator/SearchTool.js +44 -0
- package/.storybook/htmlPreview.css +15 -41
- package/.storybook/htmlPreview.js +12 -90
- package/.storybook/manager.js +2 -1
- package/.storybook/preview.js +1 -1
- package/.storybook/shiki.js +14 -0
- package/dist/pds-reference.json +1 -1
- package/package.json +2 -2
- package/public/assets/js/app.js +1 -1
- package/public/assets/pds/components/pds-jsonform.js +9 -29
- package/public/assets/pds/styles/pds-styles.css +2 -6
- package/src/js/pds-configurator/pds-demo.js +11 -12
- package/stories/GettingStarted.stories.js +10 -123
- package/stories/WhatIsPDS.md +115 -137
- package/stories/WhatIsPDS.stories.js +10 -123
- package/stories/components/PdsJsonform.stories.js +1 -1
- package/stories/reference/reference-helpers.js +25 -86
- package/stories/utils/markdown.js +86 -0
- package/stories/utils/shiki.js +108 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import React, { useState, useCallback } from 'react';
|
|
1
|
+
import React, { useState, useCallback, useEffect, useRef } from 'react';
|
|
2
2
|
import { useChannel } from '@storybook/manager-api';
|
|
3
3
|
import { IconButton } from '@storybook/components';
|
|
4
4
|
import { EVENTS } from './constants.js';
|
|
5
5
|
import { styled } from '@storybook/theming';
|
|
6
|
+
import { loadShiki, escapeHtml } from '../../shiki.js';
|
|
6
7
|
|
|
7
8
|
const Container = styled.div`
|
|
8
9
|
position: relative;
|
|
@@ -21,22 +22,16 @@ const CodeBlock = styled.pre`
|
|
|
21
22
|
color: ${props => props.theme.color.defaultText};
|
|
22
23
|
background: transparent;
|
|
23
24
|
tab-size: 2;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
color: #d19a66;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.html-token-value {
|
|
34
|
-
color: #98c379;
|
|
25
|
+
|
|
26
|
+
/* Shiki generates its own pre/code, style the inner content */
|
|
27
|
+
.shiki {
|
|
28
|
+
background: transparent !important;
|
|
29
|
+
margin: 0;
|
|
30
|
+
padding: 0;
|
|
35
31
|
}
|
|
36
|
-
|
|
37
|
-
.
|
|
38
|
-
|
|
39
|
-
font-style: italic;
|
|
32
|
+
|
|
33
|
+
.shiki code {
|
|
34
|
+
background: transparent;
|
|
40
35
|
}
|
|
41
36
|
`;
|
|
42
37
|
|
|
@@ -115,6 +110,50 @@ const CheckIcon = () => (
|
|
|
115
110
|
export const Panel = ({ active }) => {
|
|
116
111
|
const [source, setSource] = useState({ markup: '', jsonForms: [] });
|
|
117
112
|
const [copied, setCopied] = useState(false);
|
|
113
|
+
const [highlightedMarkup, setHighlightedMarkup] = useState('');
|
|
114
|
+
const shikiRef = useRef(null);
|
|
115
|
+
|
|
116
|
+
// Load Shiki once on mount
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
loadShiki().then(highlighter => {
|
|
119
|
+
shikiRef.current = highlighter;
|
|
120
|
+
});
|
|
121
|
+
}, []);
|
|
122
|
+
|
|
123
|
+
// Highlight markup when source changes
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (!source.markup) {
|
|
126
|
+
setHighlightedMarkup('');
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const highlighter = shikiRef.current;
|
|
131
|
+
if (highlighter) {
|
|
132
|
+
try {
|
|
133
|
+
const html = highlighter.codeToHtml(source.markup, {
|
|
134
|
+
lang: 'html',
|
|
135
|
+
theme: 'github-dark'
|
|
136
|
+
});
|
|
137
|
+
setHighlightedMarkup(html);
|
|
138
|
+
} catch (err) {
|
|
139
|
+
setHighlightedMarkup(`<pre><code>${escapeHtml(source.markup)}</code></pre>`);
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
// Fallback while Shiki loads
|
|
143
|
+
setHighlightedMarkup(`<pre><code>${escapeHtml(source.markup)}</code></pre>`);
|
|
144
|
+
// Retry when Shiki becomes available
|
|
145
|
+
loadShiki().then(hl => {
|
|
146
|
+
if (hl && source.markup) {
|
|
147
|
+
try {
|
|
148
|
+
const html = hl.codeToHtml(source.markup, { lang: 'html', theme: 'github-dark' });
|
|
149
|
+
setHighlightedMarkup(html);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
// Keep fallback
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}, [source.markup]);
|
|
118
157
|
|
|
119
158
|
useChannel({
|
|
120
159
|
[EVENTS.UPDATE_HTML]: (payload) => {
|
|
@@ -162,94 +201,6 @@ export const Panel = ({ active }) => {
|
|
|
162
201
|
}
|
|
163
202
|
}, [source.markup]);
|
|
164
203
|
|
|
165
|
-
const highlightHTML = useCallback((code) => {
|
|
166
|
-
if (!code) return '';
|
|
167
|
-
|
|
168
|
-
let result = '';
|
|
169
|
-
let i = 0;
|
|
170
|
-
|
|
171
|
-
const escapeHtml = (text) => {
|
|
172
|
-
return text
|
|
173
|
-
.replace(/&/g, '&')
|
|
174
|
-
.replace(/</g, '<')
|
|
175
|
-
.replace(/>/g, '>');
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
while (i < code.length) {
|
|
179
|
-
// Handle HTML comments
|
|
180
|
-
if (code.substr(i, 4) === '<!--') {
|
|
181
|
-
const end = code.indexOf('-->', i);
|
|
182
|
-
if (end !== -1) {
|
|
183
|
-
const comment = code.substring(i, end + 3);
|
|
184
|
-
result += `<span class="html-token-comment">${escapeHtml(comment)}</span>`;
|
|
185
|
-
i = end + 3;
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Handle tags
|
|
191
|
-
if (code[i] === '<') {
|
|
192
|
-
const tagEnd = code.indexOf('>', i);
|
|
193
|
-
if (tagEnd !== -1) {
|
|
194
|
-
const tagContent = code.substring(i + 1, tagEnd);
|
|
195
|
-
result += '<';
|
|
196
|
-
|
|
197
|
-
// Check if it's a closing tag
|
|
198
|
-
if (tagContent[0] === '/') {
|
|
199
|
-
result += '/';
|
|
200
|
-
const tagName = tagContent.substring(1).split(/[\s>]/)[0];
|
|
201
|
-
result += `<span class="html-token-tag">${escapeHtml(tagName)}</span>`;
|
|
202
|
-
} else {
|
|
203
|
-
// Parse tag name and attributes
|
|
204
|
-
const parts = tagContent.match(/^([\w-]+)([\s\S]*?)(\/?)?$/);
|
|
205
|
-
if (parts) {
|
|
206
|
-
const [, tagName, attrsStr, slash] = parts;
|
|
207
|
-
result += `<span class="html-token-tag">${escapeHtml(tagName)}</span>`;
|
|
208
|
-
|
|
209
|
-
// Parse attributes
|
|
210
|
-
if (attrsStr.trim()) {
|
|
211
|
-
const attrRegex = /([\w-]+)(?:=(?:"([^"]*)"|'([^']*)'|([^\s>]*)))?/g;
|
|
212
|
-
let match;
|
|
213
|
-
let lastIndex = 0;
|
|
214
|
-
|
|
215
|
-
while ((match = attrRegex.exec(attrsStr)) !== null) {
|
|
216
|
-
result += escapeHtml(attrsStr.substring(lastIndex, match.index));
|
|
217
|
-
|
|
218
|
-
const [fullMatch, attrName, doubleQuoted, singleQuoted, unquoted] = match;
|
|
219
|
-
result += `<span class="html-token-attr">${escapeHtml(attrName)}</span>`;
|
|
220
|
-
|
|
221
|
-
if (doubleQuoted !== undefined) {
|
|
222
|
-
result += `=<span class="html-token-value">"${escapeHtml(doubleQuoted)}"</span>`;
|
|
223
|
-
} else if (singleQuoted !== undefined) {
|
|
224
|
-
result += `=<span class="html-token-value">'${escapeHtml(singleQuoted)}'</span>`;
|
|
225
|
-
} else if (unquoted !== undefined) {
|
|
226
|
-
result += `=<span class="html-token-value">${escapeHtml(unquoted)}</span>`;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
lastIndex = match.index + fullMatch.length;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
result += escapeHtml(attrsStr.substring(lastIndex));
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (slash) result += '/';
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
result += '>';
|
|
240
|
-
i = tagEnd + 1;
|
|
241
|
-
continue;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Regular text
|
|
246
|
-
result += escapeHtml(code[i]);
|
|
247
|
-
i++;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return result;
|
|
251
|
-
}, []);
|
|
252
|
-
|
|
253
204
|
const hasMarkup = Boolean(source.markup);
|
|
254
205
|
const hasJsonForms = source.jsonForms.length > 0;
|
|
255
206
|
|
|
@@ -269,9 +220,7 @@ export const Panel = ({ active }) => {
|
|
|
269
220
|
{hasMarkup && (
|
|
270
221
|
<SectionWrapper>
|
|
271
222
|
<SectionHeading>Markup</SectionHeading>
|
|
272
|
-
<CodeBlock
|
|
273
|
-
<code dangerouslySetInnerHTML={{ __html: highlightHTML(source.markup) }} />
|
|
274
|
-
</CodeBlock>
|
|
223
|
+
<CodeBlock dangerouslySetInnerHTML={{ __html: highlightedMarkup }} />
|
|
275
224
|
</SectionWrapper>
|
|
276
225
|
)}
|
|
277
226
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React, { useState, useCallback } from 'react';
|
|
2
|
+
import { useChannel } from '@storybook/manager-api';
|
|
3
|
+
import { IconButton } from '@storybook/components';
|
|
4
|
+
import { EVENTS } from './constants.js';
|
|
5
|
+
|
|
6
|
+
export const SearchTool = () => {
|
|
7
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
8
|
+
|
|
9
|
+
const channel = useChannel({
|
|
10
|
+
[EVENTS.QUERY_EXECUTED + '_RESPONSE']: (data) => {
|
|
11
|
+
console.log('Query response received:', data);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const toggleSearch = useCallback(() => {
|
|
16
|
+
const newState = !isOpen;
|
|
17
|
+
console.log('Toggle search - current isOpen:', isOpen, 'newState:', newState);
|
|
18
|
+
|
|
19
|
+
if (newState) {
|
|
20
|
+
// Emit event to open search in preview
|
|
21
|
+
const searchQuery = prompt('Enter search query (e.g., "primary color", "spacing", "button"):');
|
|
22
|
+
if (searchQuery) {
|
|
23
|
+
console.log('Executing search query:', searchQuery);
|
|
24
|
+
channel.emit(EVENTS.QUERY_EXECUTED, { query: searchQuery });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setIsOpen(newState);
|
|
29
|
+
}, [isOpen, channel]);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<IconButton
|
|
33
|
+
key="search-tool"
|
|
34
|
+
active={isOpen}
|
|
35
|
+
title="Quick Search PDS"
|
|
36
|
+
onClick={toggleSearch}
|
|
37
|
+
>
|
|
38
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
39
|
+
<circle cx="11" cy="11" r="8" />
|
|
40
|
+
<path d="m21 21-4.35-4.35" />
|
|
41
|
+
</svg>
|
|
42
|
+
</IconButton>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
@@ -33,59 +33,33 @@
|
|
|
33
33
|
padding: var(--spacing-4, 1rem);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
/* Shiki-generated code blocks */
|
|
37
|
+
.html-source-content pre {
|
|
37
38
|
margin: 0;
|
|
38
39
|
padding: var(--spacing-4, 1rem);
|
|
39
40
|
border-radius: var(--radius-md, 4px);
|
|
40
41
|
overflow-x: auto;
|
|
41
|
-
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
|
|
42
42
|
font-size: 0.875rem;
|
|
43
43
|
line-height: 1.6;
|
|
44
44
|
tab-size: 2;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
:
|
|
49
|
-
--code-bg: #282c34;
|
|
50
|
-
--code-text: #abb2bf;
|
|
51
|
-
--code-tag: #e06c75;
|
|
52
|
-
--code-attr: #d19a66;
|
|
53
|
-
--code-value: #98c379;
|
|
54
|
-
--code-comment: #5c6370;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
[data-theme="dark"] {
|
|
58
|
-
--code-bg: #1e1e1e;
|
|
59
|
-
--code-text: #d4d4d4;
|
|
60
|
-
--code-tag: #569cd6;
|
|
61
|
-
--code-attr: #9cdcfe;
|
|
62
|
-
--code-value: #ce9178;
|
|
63
|
-
--code-comment: #6a9955;
|
|
47
|
+
.html-source-content pre code {
|
|
48
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
|
|
64
49
|
}
|
|
65
50
|
|
|
51
|
+
/* Fallback pre styling (before Shiki loads) */
|
|
66
52
|
.html-source-pre {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
.html-token-attr {
|
|
79
|
-
color: var(--code-attr);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.html-token-value {
|
|
83
|
-
color: var(--code-value);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.html-token-comment {
|
|
87
|
-
color: var(--code-comment);
|
|
88
|
-
font-style: italic;
|
|
53
|
+
margin: 0;
|
|
54
|
+
padding: var(--spacing-4, 1rem);
|
|
55
|
+
border-radius: var(--radius-md, 4px);
|
|
56
|
+
overflow-x: auto;
|
|
57
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
|
|
58
|
+
font-size: 0.875rem;
|
|
59
|
+
line-height: 1.6;
|
|
60
|
+
tab-size: 2;
|
|
61
|
+
background: var(--color-surface-raised, #282c34);
|
|
62
|
+
color: var(--color-text-primary, #abb2bf);
|
|
89
63
|
}
|
|
90
64
|
|
|
91
65
|
pds-config-form {
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* HTML Preview and Copy Utility for Storybook
|
|
3
3
|
* Provides HTML source viewing and copy functionality for all stories
|
|
4
|
-
* Uses
|
|
4
|
+
* Uses Shiki for syntax highlighting
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
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();
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
14
|
* Format HTML string with proper indentation
|
|
@@ -145,96 +149,14 @@ export const withHTMLSource = (storyFn, context) => {
|
|
|
145
149
|
}
|
|
146
150
|
|
|
147
151
|
const codeEl = sourceSection.querySelector('.html-source-code');
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
while (i < code.length) {
|
|
155
|
-
// Handle HTML comments
|
|
156
|
-
if (code.substr(i, 4) === '<!--') {
|
|
157
|
-
const end = code.indexOf('-->', i);
|
|
158
|
-
if (end !== -1) {
|
|
159
|
-
const comment = code.substring(i, end + 3);
|
|
160
|
-
result += `<span class="html-token-comment">${escapeHtml(comment)}</span>`;
|
|
161
|
-
i = end + 3;
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Handle tags
|
|
167
|
-
if (code[i] === '<') {
|
|
168
|
-
const tagEnd = code.indexOf('>', i);
|
|
169
|
-
if (tagEnd !== -1) {
|
|
170
|
-
const tagContent = code.substring(i + 1, tagEnd);
|
|
171
|
-
result += '<';
|
|
172
|
-
|
|
173
|
-
// Check if it's a closing tag
|
|
174
|
-
if (tagContent[0] === '/') {
|
|
175
|
-
result += '/';
|
|
176
|
-
const tagName = tagContent.substring(1).split(/[\s>]/)[0];
|
|
177
|
-
result += `<span class="html-token-tag">${escapeHtml(tagName)}</span>`;
|
|
178
|
-
} else {
|
|
179
|
-
// Parse tag name and attributes
|
|
180
|
-
const parts = tagContent.match(/^([\w-]+)([\s\S]*?)(\/?)?$/);
|
|
181
|
-
if (parts) {
|
|
182
|
-
const [, tagName, attrsStr, slash] = parts;
|
|
183
|
-
result += `<span class="html-token-tag">${escapeHtml(tagName)}</span>`;
|
|
184
|
-
|
|
185
|
-
// Parse attributes
|
|
186
|
-
if (attrsStr.trim()) {
|
|
187
|
-
const attrRegex = /([\w-]+)(?:=(?:"([^"]*)"|'([^']*)'|([^\s>]*)))?/g;
|
|
188
|
-
let match;
|
|
189
|
-
let lastIndex = 0;
|
|
190
|
-
|
|
191
|
-
while ((match = attrRegex.exec(attrsStr)) !== null) {
|
|
192
|
-
// Add whitespace before attribute
|
|
193
|
-
result += escapeHtml(attrsStr.substring(lastIndex, match.index));
|
|
194
|
-
|
|
195
|
-
const [fullMatch, attrName, doubleQuoted, singleQuoted, unquoted] = match;
|
|
196
|
-
result += `<span class="html-token-attr">${escapeHtml(attrName)}</span>`;
|
|
197
|
-
|
|
198
|
-
if (doubleQuoted !== undefined) {
|
|
199
|
-
result += `=<span class="html-token-value">"${escapeHtml(doubleQuoted)}"</span>`;
|
|
200
|
-
} else if (singleQuoted !== undefined) {
|
|
201
|
-
result += `=<span class="html-token-value">'${escapeHtml(singleQuoted)}'</span>`;
|
|
202
|
-
} else if (unquoted !== undefined) {
|
|
203
|
-
result += `=<span class="html-token-value">${escapeHtml(unquoted)}</span>`;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
lastIndex = match.index + fullMatch.length;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
result += escapeHtml(attrsStr.substring(lastIndex));
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (slash) result += '/';
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
result += '>';
|
|
217
|
-
i = tagEnd + 1;
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Regular text
|
|
223
|
-
result += escapeHtml(code[i]);
|
|
224
|
-
i++;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return result;
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
const escapeHtml = (text) => {
|
|
231
|
-
return text
|
|
232
|
-
.replace(/&/g, '&')
|
|
233
|
-
.replace(/</g, '<')
|
|
234
|
-
.replace(/>/g, '>');
|
|
235
|
-
};
|
|
152
|
+
const preEl = sourceSection.querySelector('.html-source-pre');
|
|
153
|
+
if (preEl && html) {
|
|
154
|
+
// Use Shiki for syntax highlighting
|
|
155
|
+
const theme = getCurrentTheme();
|
|
156
|
+
const highlighted = await highlight(html, 'html', theme);
|
|
236
157
|
|
|
237
|
-
|
|
158
|
+
// Shiki returns complete <pre><code>...</code></pre>, replace the whole pre element
|
|
159
|
+
preEl.outerHTML = highlighted;
|
|
238
160
|
}
|
|
239
161
|
|
|
240
162
|
// Setup copy button
|
package/.storybook/manager.js
CHANGED
|
@@ -32,7 +32,8 @@ async function loadOntology() {
|
|
|
32
32
|
if (referenceData) return referenceData;
|
|
33
33
|
|
|
34
34
|
try {
|
|
35
|
-
|
|
35
|
+
// Use relative path so it works both in dev and when deployed to a subdirectory
|
|
36
|
+
const response = await fetch('./pds-data/pds-reference.json');
|
|
36
37
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
37
38
|
referenceData = await response.json();
|
|
38
39
|
buildIndices();
|
package/.storybook/preview.js
CHANGED
|
@@ -287,7 +287,7 @@ const withGlobalsHandler = (story, context) => {
|
|
|
287
287
|
const DEFAULT_STORY_TAGS = new Set(['dev', 'test', 'story', 'stories', 'autodocs', 'example', 'examples']);
|
|
288
288
|
|
|
289
289
|
const TAG_SYNONYMS = new Map([
|
|
290
|
-
['padding', 'spacing'],
|
|
290
|
+
['padding', 'spacing', 'space'],
|
|
291
291
|
['gap', 'spacing'],
|
|
292
292
|
['grid', 'layout'],
|
|
293
293
|
['flex', 'layout'],
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Shiki syntax highlighter for PDS Storybook
|
|
3
|
+
* Provides consistent code highlighting across all stories and previews
|
|
4
|
+
*
|
|
5
|
+
* Re-exports from stories/utils/shiki.js to maintain a single source of truth
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
loadShiki,
|
|
10
|
+
highlight,
|
|
11
|
+
getCurrentTheme,
|
|
12
|
+
escapeHtml,
|
|
13
|
+
preloadShiki
|
|
14
|
+
} from '../stories/utils/shiki.js';
|
package/dist/pds-reference.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pure-ds/storybook",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"description": "Storybook showcase for Pure Design System with live configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"storybook:build": "npm run build-storybook"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@pure-ds/core": "^0.4.
|
|
38
|
+
"@pure-ds/core": "^0.4.7"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@custom-elements-manifest/analyzer": "^0.11.0",
|
package/public/assets/js/app.js
CHANGED
|
@@ -3684,7 +3684,7 @@ export const pdsConfig = ${JSON.stringify(this.config,null,2)};
|
|
|
3684
3684
|
`,setTimeout(()=>{p.innerHTML=`
|
|
3685
3685
|
<pds-icon icon="clipboard" size="sm"></pds-icon>
|
|
3686
3686
|
Copy HTML
|
|
3687
|
-
`},2e3)})})},100)}async loadShiki(){if(this.#a)return this.#a;if(this.#e){for(;this.#e;)await new Promise(r=>setTimeout(r,
|
|
3687
|
+
`},2e3)})})},100)}async loadShiki(){if(this.#a)return this.#a;if(this.#e){for(;this.#e;)await new Promise(r=>setTimeout(r,50));return this.#a}this.#e=!0;try{let r=await import("https://esm.sh/shiki@1.0.0");return this.#a=await r.getHighlighter({themes:["github-dark","github-light"],langs:["html","css","javascript","json"]}),this.#a}catch(r){return console.error("Failed to load Shiki:",r),null}finally{this.#e=!1}}async highlightWithShiki(r,e="html"){let t=await this.loadShiki();if(!t)return this.escapeHTML(r);try{let o=document.documentElement.getAttribute("data-theme")==="dark"?"github-dark":"github-light",s=t.codeToHtml(r,{lang:e,theme:o}).match(/<code[^>]*>([\s\S]*)<\/code>/);return s?s[1]:this.escapeHTML(r)}catch(a){return console.error("Shiki highlighting failed:",a),this.escapeHTML(r)}}escapeHTML(r){return r.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}async getShowdownConverter(){if(this._showdown)return this._showdown;let r=await this.loadShowdownFromCDN();return!r||!r.Converter?null:(this._showdown=new r.Converter({ghCompatibleHeaderId:!0,tables:!0,strikethrough:!0,tasklists:!0}),this._showdown)}async loadShowdownFromCDN(){if(typeof window<"u"&&window.showdown)return window.showdown;if(this._showdownLoading){for(;this._showdownLoading;)await new Promise(e=>setTimeout(e,50));return window.showdown||null}this._showdownLoading=!0;let r=["https://cdn.jsdelivr.net/npm/showdown@2.1.0/dist/showdown.min.js","https://unpkg.com/showdown@2.1.0/dist/showdown.min.js"];for(let e of r)try{if(await this._injectScript(e,"showdown"),window.showdown)return this._showdownLoading=!1,window.showdown}catch{}return this._showdownLoading=!1,null}_injectScript(r,e){return new Promise((t,a)=>{if(document.querySelector(`script[data-lib="${e}"][src="${r}"]`)){setTimeout(t,0);return}let o=document.createElement("script");o.src=r,o.async=!0,o.defer=!0,o.dataset.lib=e||"lib",o.onload=()=>t(),o.onerror=()=>{o.remove(),a(new Error(`Failed to load script: ${r}`))},document.head.appendChild(o)})}scrollToRelevantSection(r){console.log("\u{1F3AF} Scrolling to section for field:",r);let e=r.startsWith("/")?r.slice(1):r;console.log(" Normalized path:",e);let t={"colors/primary":"color-system","colors/secondary":"color-system","colors/accent":"color-system","colors/background":"color-system","colors/success":"color-system","colors/warning":"color-system","colors/danger":"color-system","colors/info":"color-system","typography/":"typography","spatialRhythm/":"spacing","layers/":"surfaces-shadows","shape/":"buttons","behavior/transitionSpeed":"interactive-states","behavior/":"interactive-states","components/forms":"forms","components/alerts":"alerts","components/badges":"badges","components/tables":"tables","components/toasts":"toasts","components/modals":"modals","components/tabStrip":"tabs","icons/":"icons"},a=null;for(let[o,i]of Object.entries(t))if(e.startsWith(o)){a=i,console.log(` \u2713 Matched pattern "${o}" \u2192 section "${i}"`);break}if(a){let o=this.querySelector(`[data-section="${a}"]`);console.log(` Searching for section: [data-section="${a}"]`,o?"\u2713 Found":"\u2717 Not found"),o?(o.scrollIntoView({behavior:"smooth",block:"start"}),o.style.transition="background-color 0.3s ease",o.style.backgroundColor="var(--color-primary-50)",setTimeout(()=>{o.style.backgroundColor=""},1500),console.log(" \u2713 Scrolled and highlighted section")):console.warn(` \u2717 Section [data-section="${a}"] not found in DOM`)}else console.warn(` \u2717 No section mapping found for field: ${r}`)}renderDisabledSection(r,e){return S`
|
|
3688
3688
|
<section class="showcase-section disabled">
|
|
3689
3689
|
<h2>${r}</h2>
|
|
3690
3690
|
<p class="disabled-message">${e}</p>
|
|
@@ -53,7 +53,7 @@ const DEFAULT_OPTIONS = {
|
|
|
53
53
|
*
|
|
54
54
|
* 5. Completely custom actions (hides default buttons):
|
|
55
55
|
* <pds-jsonform .jsonSchema=${schema} hide-actions>
|
|
56
|
-
* <div slot="actions"
|
|
56
|
+
* <div slot="actions" class="flex gap-md">
|
|
57
57
|
* <button type="submit" class="btn btn-primary">Custom Submit</button>
|
|
58
58
|
* <button type="button" class="btn">Custom Action</button>
|
|
59
59
|
* </div>
|
|
@@ -416,10 +416,7 @@ export class SchemaForm extends LitElement {
|
|
|
416
416
|
render() {
|
|
417
417
|
const tree = this.#compiled;
|
|
418
418
|
if (!tree)
|
|
419
|
-
return html`<div
|
|
420
|
-
class="pds-jsonform-error"
|
|
421
|
-
style="color: red; padding: 1rem; border: 1px solid red; background: #fee;"
|
|
422
|
-
>
|
|
419
|
+
return html`<div class="alert alert-error">
|
|
423
420
|
<p>Failed to generate form schema.</p>
|
|
424
421
|
<pre>${JSON.stringify(this.#data, null, 2)}</pre>
|
|
425
422
|
</div>`;
|
|
@@ -431,6 +428,7 @@ export class SchemaForm extends LitElement {
|
|
|
431
428
|
: "post";
|
|
432
429
|
return html`
|
|
433
430
|
<form
|
|
431
|
+
?data-required=${this.hasAttribute("data-required")}
|
|
434
432
|
method=${m}
|
|
435
433
|
action=${this.action ?? nothing}
|
|
436
434
|
@submit=${this.#onSubmit}
|
|
@@ -501,9 +499,8 @@ export class SchemaForm extends LitElement {
|
|
|
501
499
|
// Check for surface wrapping
|
|
502
500
|
const surface = ui?.["ui:surface"] || pathOptions.surface;
|
|
503
501
|
|
|
504
|
-
// Build layout classes
|
|
502
|
+
// Build layout classes using PDS utilities
|
|
505
503
|
const layoutClasses = [];
|
|
506
|
-
let layoutStyle = "";
|
|
507
504
|
const layoutOptions = ui?.["ui:layoutOptions"] || {};
|
|
508
505
|
|
|
509
506
|
if (layout === "flex") {
|
|
@@ -511,16 +508,8 @@ export class SchemaForm extends LitElement {
|
|
|
511
508
|
if (layoutOptions.wrap) layoutClasses.push("flex-wrap");
|
|
512
509
|
if (layoutOptions.direction === "column") layoutClasses.push("flex-col");
|
|
513
510
|
if (layoutOptions.gap) {
|
|
514
|
-
//
|
|
515
|
-
|
|
516
|
-
layoutOptions.gap.startsWith("var(") ||
|
|
517
|
-
layoutOptions.gap.includes("px") ||
|
|
518
|
-
layoutOptions.gap.includes("rem")
|
|
519
|
-
) {
|
|
520
|
-
layoutStyle += `gap: ${layoutOptions.gap};`;
|
|
521
|
-
} else {
|
|
522
|
-
layoutClasses.push(`gap-${layoutOptions.gap}`);
|
|
523
|
-
}
|
|
511
|
+
// Use PDS gap utility classes (xs, sm, md, lg, xl, 0)
|
|
512
|
+
layoutClasses.push(`gap-${layoutOptions.gap}`);
|
|
524
513
|
}
|
|
525
514
|
} else if (layout === "grid") {
|
|
526
515
|
layoutClasses.push("grid");
|
|
@@ -532,16 +521,8 @@ export class SchemaForm extends LitElement {
|
|
|
532
521
|
layoutClasses.push(`grid-cols-${cols}`);
|
|
533
522
|
}
|
|
534
523
|
if (layoutOptions.gap) {
|
|
535
|
-
//
|
|
536
|
-
|
|
537
|
-
layoutOptions.gap.startsWith("var(") ||
|
|
538
|
-
layoutOptions.gap.includes("px") ||
|
|
539
|
-
layoutOptions.gap.includes("rem")
|
|
540
|
-
) {
|
|
541
|
-
layoutStyle += `gap: ${layoutOptions.gap};`;
|
|
542
|
-
} else {
|
|
543
|
-
layoutClasses.push(`gap-${layoutOptions.gap}`);
|
|
544
|
-
}
|
|
524
|
+
// Use PDS gap utility classes (xs, sm, md, lg, xl, 0)
|
|
525
|
+
layoutClasses.push(`gap-${layoutOptions.gap}`);
|
|
545
526
|
}
|
|
546
527
|
}
|
|
547
528
|
|
|
@@ -553,7 +534,6 @@ export class SchemaForm extends LitElement {
|
|
|
553
534
|
<fieldset
|
|
554
535
|
data-path=${node.path}
|
|
555
536
|
class=${ifDefined(fieldsetClass)}
|
|
556
|
-
style=${ifDefined(layoutStyle || undefined)}
|
|
557
537
|
>
|
|
558
538
|
${!this.hideLegend && !context.hideLegend
|
|
559
539
|
? html`<legend>${legend}</legend>`
|
|
@@ -1612,7 +1592,7 @@ export class SchemaForm extends LitElement {
|
|
|
1612
1592
|
richtextOpts.placeholder || attrs.placeholder
|
|
1613
1593
|
)}
|
|
1614
1594
|
.value=${value ?? ""}
|
|
1615
|
-
toolbar=${
|
|
1595
|
+
?toolbar=${richtextOpts.toolbar}
|
|
1616
1596
|
?required=${!!attrs.required}
|
|
1617
1597
|
?submit-on-enter=${richtextOpts.submitOnEnter ?? false}
|
|
1618
1598
|
spellcheck=${richtextOpts.spellcheck ?? true ? "true" : "false"}
|
|
@@ -733,12 +733,7 @@ html[data-theme="dark"] {
|
|
|
733
733
|
overflow-x: hidden;
|
|
734
734
|
-webkit-overflow-scrolling: touch;
|
|
735
735
|
}
|
|
736
|
-
|
|
737
|
-
:where(body.drawer-open) {
|
|
738
|
-
/* overflow: hidden; */
|
|
739
|
-
/* scrollbar-gutter: stable; */
|
|
740
|
-
}
|
|
741
|
-
|
|
736
|
+
|
|
742
737
|
/* Button primitives */
|
|
743
738
|
:where(button) {
|
|
744
739
|
all: unset;
|
|
@@ -1459,6 +1454,7 @@ input[type="range"]:active::-moz-range-thumb {
|
|
|
1459
1454
|
}
|
|
1460
1455
|
|
|
1461
1456
|
input[type="color"] {
|
|
1457
|
+
appearance: none;
|
|
1462
1458
|
-webkit-appearance: none;
|
|
1463
1459
|
padding: 0;
|
|
1464
1460
|
width: 3rem;
|