@fragments-sdk/cli 0.9.0 → 0.9.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/dist/bin.js +83 -33
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-WI6SLMSO.js → chunk-5GT62FCB.js} +2 -2
- package/dist/{chunk-CJEGT3WD.js → chunk-BW3ZATBW.js} +20 -3
- package/dist/chunk-BW3ZATBW.js.map +1 -0
- package/dist/{chunk-2JIKCJX3.js → chunk-D7372LQX.js} +13 -6
- package/dist/chunk-D7372LQX.js.map +1 -0
- package/dist/chunk-EZYXYWNF.js +131 -0
- package/dist/chunk-EZYXYWNF.js.map +1 -0
- package/dist/{chunk-NGIMCIK2.js → chunk-GF6OVPIN.js} +2 -2
- package/dist/{chunk-GOVI6COW.js → chunk-NVSPGSKB.js} +12 -4
- package/dist/chunk-NVSPGSKB.js.map +1 -0
- package/dist/core/index.d.ts +105 -3
- package/dist/core/index.js +12 -2
- package/dist/{defineFragment-D0UTve-I.d.ts → defineFragment-CBMS7Bab.d.ts} +21 -1
- package/dist/generate-LQA2R7FN.js +461 -0
- package/dist/generate-LQA2R7FN.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/{init-KSAAS7X3.js → init-2GEGVIUQ.js} +13 -75
- package/dist/init-2GEGVIUQ.js.map +1 -0
- package/dist/mcp-bin.js +4 -3
- package/dist/mcp-bin.js.map +1 -1
- package/dist/{scan-65RH3QMM.js → scan-JGS65S7P.js} +6 -5
- package/dist/{service-A5GIGGGK.js → service-XP2EAJXD.js} +4 -3
- package/dist/{static-viewer-NSODM5VX.js → static-viewer-XCS7UJTO.js} +4 -3
- package/dist/storyFilters-3LUYAFZF.js +15 -0
- package/dist/storyFilters-3LUYAFZF.js.map +1 -0
- package/dist/{test-RPWZAYSJ.js → test-TD6TJNVY.js} +3 -3
- package/dist/{tokens-NIXSZRX7.js → tokens-2EXPCVP3.js} +5 -4
- package/dist/{tokens-NIXSZRX7.js.map → tokens-2EXPCVP3.js.map} +1 -1
- package/dist/{viewer-SBTJDMP7.js → viewer-RFA2KVBG.js} +243 -18
- package/dist/viewer-RFA2KVBG.js.map +1 -0
- package/package.json +1 -1
- package/src/build.ts +12 -2
- package/src/commands/build.ts +16 -2
- package/src/commands/generate.ts +383 -68
- package/src/commands/init.ts +9 -51
- package/src/core/config.ts +15 -2
- package/src/core/generators/typescript-extractor.ts +10 -0
- package/src/core/index.ts +15 -0
- package/src/core/schema.ts +10 -2
- package/src/core/storyFilters.test.ts +350 -0
- package/src/core/storyFilters.ts +253 -0
- package/src/core/types.ts +22 -0
- package/src/migrate/converter.ts +9 -1
- package/src/migrate/parser.ts +2 -0
- package/src/migrate/types.ts +2 -0
- package/src/setup.ts +69 -24
- package/src/viewer/__tests__/viewer-integration.test.ts +1 -1
- package/src/viewer/components/AccessibilityPanel.tsx +305 -312
- package/src/viewer/components/ActionsPanel.tsx +31 -29
- package/src/viewer/components/AllVariantsPreview.tsx +78 -0
- package/src/viewer/components/App.tsx +187 -740
- package/src/viewer/components/BottomPanel.tsx +228 -132
- package/src/viewer/components/CodePanel.tsx +1 -1
- package/src/viewer/components/CommandPalette.tsx +7 -10
- package/src/viewer/components/ComponentDocView.tsx +164 -0
- package/src/viewer/components/ComponentGraph.tsx +111 -142
- package/src/viewer/components/ContractPanel.tsx +6 -6
- package/src/viewer/components/EmptyVariantMessage.tsx +54 -0
- package/src/viewer/components/FigmaEmbed.tsx +20 -18
- package/src/viewer/components/FragmentEditor.tsx +92 -115
- package/src/viewer/components/HeaderSearch.tsx +24 -0
- package/src/viewer/components/HealthDashboard.tsx +16 -2
- package/src/viewer/components/Icons.tsx +9 -0
- package/src/viewer/components/InteractionsPanel.tsx +101 -117
- package/src/viewer/components/IsolatedPreviewFrame.tsx +1 -0
- package/src/viewer/components/LandingPage.tsx +3 -3
- package/src/viewer/components/LeftSidebar.tsx +141 -63
- package/src/viewer/components/LoadErrorMessage.tsx +102 -0
- package/src/viewer/components/MultiViewportPreview.tsx +61 -142
- package/src/viewer/components/NoVariantsMessage.tsx +59 -0
- package/src/viewer/components/PanelShell.tsx +161 -0
- package/src/viewer/components/PerformancePanel.tsx +31 -28
- package/src/viewer/components/PreviewArea.tsx +1 -1
- package/src/viewer/components/PreviewAside.tsx +168 -0
- package/src/viewer/components/PreviewFrameHost.tsx +3 -3
- package/src/viewer/components/PropsEditor.tsx +70 -156
- package/src/viewer/components/ResizablePanel.tsx +103 -263
- package/src/viewer/components/RightSidebar.tsx +3 -9
- package/src/viewer/components/SkeletonLoader.tsx +13 -13
- package/src/viewer/components/TokenStylePanel.tsx +182 -209
- package/src/viewer/components/TopToolbar.tsx +159 -0
- package/src/viewer/components/VariantMatrix.tsx +42 -86
- package/src/viewer/components/VariantTabs.tsx +3 -3
- package/src/viewer/components/ViewerHeader.tsx +69 -0
- package/src/viewer/components/WebMCPDevTools.tsx +17 -23
- package/src/viewer/components/viewer-utils.ts +16 -0
- package/src/viewer/entry.tsx +5 -0
- package/src/viewer/hooks/useAppState.ts +27 -4
- package/src/viewer/hooks/usePreviewBridge.ts +2 -2
- package/src/viewer/preview-frame.html +6 -12
- package/src/viewer/server.ts +169 -2
- package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss +10 -0
- package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss.d.ts +2 -0
- package/src/viewer/vendor/shared/src/ComponentDocContent.tsx +274 -0
- package/src/viewer/vendor/shared/src/DocsHeaderBar.tsx +6 -18
- package/src/viewer/vendor/shared/src/DocsPageShell.tsx +5 -0
- package/src/viewer/vendor/shared/src/DocsSidebarNav.tsx +5 -16
- package/src/viewer/vendor/shared/src/PropsTable.module.scss +68 -0
- package/src/viewer/vendor/shared/src/PropsTable.module.scss.d.ts +2 -0
- package/src/viewer/vendor/shared/src/PropsTable.tsx +76 -0
- package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss +122 -0
- package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss.d.ts +2 -0
- package/src/viewer/vendor/shared/src/VariantPreviewCard.tsx +134 -0
- package/src/viewer/vendor/shared/src/index.ts +8 -0
- package/src/viewer/vendor/shared/src/types.ts +12 -0
- package/src/viewer/vite-plugin.ts +109 -4
- package/dist/chunk-2JIKCJX3.js.map +0 -1
- package/dist/chunk-CJEGT3WD.js.map +0 -1
- package/dist/chunk-GOVI6COW.js.map +0 -1
- package/dist/generate-35OIMW4Y.js +0 -252
- package/dist/generate-35OIMW4Y.js.map +0 -1
- package/dist/init-KSAAS7X3.js.map +0 -1
- package/dist/viewer-SBTJDMP7.js.map +0 -1
- /package/dist/{chunk-WI6SLMSO.js.map → chunk-5GT62FCB.js.map} +0 -0
- /package/dist/{chunk-NGIMCIK2.js.map → chunk-GF6OVPIN.js.map} +0 -0
- /package/dist/{scan-65RH3QMM.js.map → scan-JGS65S7P.js.map} +0 -0
- /package/dist/{service-A5GIGGGK.js.map → service-XP2EAJXD.js.map} +0 -0
- /package/dist/{static-viewer-NSODM5VX.js.map → static-viewer-XCS7UJTO.js.map} +0 -0
- /package/dist/{test-RPWZAYSJ.js.map → test-TD6TJNVY.js.map} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useState, useCallback } from "react";
|
|
2
2
|
import { useForm, useFieldArray, Controller } from "react-hook-form";
|
|
3
3
|
import type { Fragment, FragmentUsage, FragmentMeta } from "../../core/index.js";
|
|
4
|
+
import { Box, Text, Button, Stack } from "@fragments-sdk/ui";
|
|
4
5
|
import { ChevronDownIcon } from "./Icons.js";
|
|
5
6
|
|
|
6
7
|
interface FragmentEditorProps {
|
|
@@ -50,32 +51,6 @@ const inputStyle: React.CSSProperties = {
|
|
|
50
51
|
boxSizing: 'border-box',
|
|
51
52
|
};
|
|
52
53
|
|
|
53
|
-
const labelStyle: React.CSSProperties = {
|
|
54
|
-
display: 'block',
|
|
55
|
-
fontSize: '12px',
|
|
56
|
-
fontWeight: 500,
|
|
57
|
-
color: 'var(--text-secondary)',
|
|
58
|
-
marginBottom: '6px',
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const removeButtonStyle: React.CSSProperties = {
|
|
62
|
-
padding: '0 8px',
|
|
63
|
-
color: 'var(--text-secondary)',
|
|
64
|
-
background: 'none',
|
|
65
|
-
border: 'none',
|
|
66
|
-
cursor: 'pointer',
|
|
67
|
-
fontSize: '16px',
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const addButtonStyle: React.CSSProperties = {
|
|
71
|
-
fontSize: '12px',
|
|
72
|
-
color: 'var(--color-accent)',
|
|
73
|
-
background: 'none',
|
|
74
|
-
border: 'none',
|
|
75
|
-
cursor: 'pointer',
|
|
76
|
-
padding: 0,
|
|
77
|
-
};
|
|
78
|
-
|
|
79
54
|
export function FragmentEditor({
|
|
80
55
|
componentName,
|
|
81
56
|
fragment,
|
|
@@ -196,20 +171,20 @@ export function FragmentEditor({
|
|
|
196
171
|
style={{ height: '100%', display: 'flex', flexDirection: 'column', background: 'var(--bg-primary)' }}
|
|
197
172
|
>
|
|
198
173
|
{/* Header */}
|
|
199
|
-
<
|
|
200
|
-
<h3 style={{
|
|
201
|
-
<
|
|
174
|
+
<Box paddingX="md" paddingY="sm" borderBottom background="secondary">
|
|
175
|
+
<Text as="h3" size="sm" weight="semibold" style={{ margin: 0 }}>Fragment Editor</Text>
|
|
176
|
+
<Text size="xs" color="secondary" style={{ marginTop: '2px' }}>
|
|
202
177
|
Enrich {componentName} with metadata
|
|
203
|
-
</
|
|
204
|
-
</
|
|
178
|
+
</Text>
|
|
179
|
+
</Box>
|
|
205
180
|
|
|
206
181
|
{/* Scrollable content */}
|
|
207
|
-
<
|
|
182
|
+
<Box overflow="auto" style={{ flex: 1 }}>
|
|
208
183
|
{/* Basic Info */}
|
|
209
|
-
<
|
|
210
|
-
<label style={
|
|
184
|
+
<Box padding="md" borderBottom>
|
|
185
|
+
<Text as="label" size="xs" weight="medium" color="secondary" style={{ display: 'block', marginBottom: '6px' }}>
|
|
211
186
|
Description
|
|
212
|
-
</
|
|
187
|
+
</Text>
|
|
213
188
|
<textarea
|
|
214
189
|
{...register("description")}
|
|
215
190
|
rows={3}
|
|
@@ -219,7 +194,7 @@ export function FragmentEditor({
|
|
|
219
194
|
}}
|
|
220
195
|
placeholder="Brief description of the component's purpose..."
|
|
221
196
|
/>
|
|
222
|
-
</
|
|
197
|
+
</Box>
|
|
223
198
|
|
|
224
199
|
{/* Usage Section */}
|
|
225
200
|
<Section
|
|
@@ -229,44 +204,47 @@ export function FragmentEditor({
|
|
|
229
204
|
>
|
|
230
205
|
{/* When to Use */}
|
|
231
206
|
<div style={{ marginBottom: '16px' }}>
|
|
232
|
-
<label style={{
|
|
207
|
+
<Text as="label" size="xs" weight="medium" color="secondary" style={{ display: 'block', marginBottom: '8px' }}>
|
|
233
208
|
When to Use
|
|
234
|
-
</
|
|
235
|
-
<
|
|
209
|
+
</Text>
|
|
210
|
+
<Stack gap="sm">
|
|
236
211
|
{whenFields.map((field, index) => (
|
|
237
|
-
<
|
|
212
|
+
<Stack key={field.id} direction="row" gap="sm">
|
|
238
213
|
<input
|
|
239
214
|
{...register(`usage.when.${index}` as const)}
|
|
240
215
|
style={{ ...inputStyle, flex: 1 }}
|
|
241
216
|
placeholder="Scenario..."
|
|
242
217
|
/>
|
|
243
|
-
<
|
|
218
|
+
<Button
|
|
244
219
|
type="button"
|
|
220
|
+
variant="ghost"
|
|
221
|
+
size="sm"
|
|
245
222
|
onClick={() => removeWhen(index)}
|
|
246
|
-
style={removeButtonStyle}
|
|
247
223
|
>
|
|
248
224
|
×
|
|
249
|
-
</
|
|
250
|
-
</
|
|
225
|
+
</Button>
|
|
226
|
+
</Stack>
|
|
251
227
|
))}
|
|
252
|
-
<
|
|
228
|
+
<Button
|
|
253
229
|
type="button"
|
|
230
|
+
variant="ghost"
|
|
231
|
+
size="sm"
|
|
254
232
|
onClick={() => appendWhen("")}
|
|
255
|
-
style={
|
|
233
|
+
style={{ padding: 0, alignSelf: 'flex-start' }}
|
|
256
234
|
>
|
|
257
235
|
+ Add scenario
|
|
258
|
-
</
|
|
259
|
-
</
|
|
236
|
+
</Button>
|
|
237
|
+
</Stack>
|
|
260
238
|
</div>
|
|
261
239
|
|
|
262
240
|
{/* Do Not */}
|
|
263
241
|
<div>
|
|
264
|
-
<label style={{
|
|
242
|
+
<Text as="label" size="xs" weight="medium" color="secondary" style={{ display: 'block', marginBottom: '8px' }}>
|
|
265
243
|
Do Not
|
|
266
|
-
</
|
|
267
|
-
<
|
|
244
|
+
</Text>
|
|
245
|
+
<Stack gap="sm">
|
|
268
246
|
{doNotFields.map((field, index) => (
|
|
269
|
-
<
|
|
247
|
+
<Stack key={field.id} direction="row" gap="sm">
|
|
270
248
|
<input
|
|
271
249
|
{...register(`usage.doNot.${index}.text` as const)}
|
|
272
250
|
style={{ ...inputStyle, flex: 1 }}
|
|
@@ -277,23 +255,26 @@ export function FragmentEditor({
|
|
|
277
255
|
style={{ ...inputStyle, width: '128px' }}
|
|
278
256
|
placeholder="Instead..."
|
|
279
257
|
/>
|
|
280
|
-
<
|
|
258
|
+
<Button
|
|
281
259
|
type="button"
|
|
260
|
+
variant="ghost"
|
|
261
|
+
size="sm"
|
|
282
262
|
onClick={() => removeDoNot(index)}
|
|
283
|
-
style={removeButtonStyle}
|
|
284
263
|
>
|
|
285
264
|
×
|
|
286
|
-
</
|
|
287
|
-
</
|
|
265
|
+
</Button>
|
|
266
|
+
</Stack>
|
|
288
267
|
))}
|
|
289
|
-
<
|
|
268
|
+
<Button
|
|
290
269
|
type="button"
|
|
270
|
+
variant="ghost"
|
|
271
|
+
size="sm"
|
|
291
272
|
onClick={() => appendDoNot({ text: "", instead: "" })}
|
|
292
|
-
style={
|
|
273
|
+
style={{ padding: 0, alignSelf: 'flex-start' }}
|
|
293
274
|
>
|
|
294
275
|
+ Add anti-pattern
|
|
295
|
-
</
|
|
296
|
-
</
|
|
276
|
+
</Button>
|
|
277
|
+
</Stack>
|
|
297
278
|
</div>
|
|
298
279
|
</Section>
|
|
299
280
|
|
|
@@ -304,9 +285,9 @@ export function FragmentEditor({
|
|
|
304
285
|
onToggle={() => toggleSection("accessibility")}
|
|
305
286
|
>
|
|
306
287
|
<div style={{ marginBottom: '16px' }}>
|
|
307
|
-
<label style={
|
|
288
|
+
<Text as="label" size="xs" weight="medium" color="secondary" style={{ display: 'block', marginBottom: '6px' }}>
|
|
308
289
|
ARIA Role
|
|
309
|
-
</
|
|
290
|
+
</Text>
|
|
310
291
|
<input
|
|
311
292
|
{...register("accessibility.role")}
|
|
312
293
|
style={inputStyle}
|
|
@@ -315,12 +296,12 @@ export function FragmentEditor({
|
|
|
315
296
|
</div>
|
|
316
297
|
|
|
317
298
|
<div>
|
|
318
|
-
<label style={{
|
|
299
|
+
<Text as="label" size="xs" weight="medium" color="secondary" style={{ display: 'block', marginBottom: '8px' }}>
|
|
319
300
|
Requirements
|
|
320
|
-
</
|
|
321
|
-
<
|
|
301
|
+
</Text>
|
|
302
|
+
<Stack gap="sm">
|
|
322
303
|
{requirementsFields.map((field, index) => (
|
|
323
|
-
<
|
|
304
|
+
<Stack key={field.id} direction="row" gap="sm">
|
|
324
305
|
<input
|
|
325
306
|
{...register(
|
|
326
307
|
`accessibility.requirements.${index}` as const
|
|
@@ -328,23 +309,26 @@ export function FragmentEditor({
|
|
|
328
309
|
style={{ ...inputStyle, flex: 1 }}
|
|
329
310
|
placeholder="Requirement..."
|
|
330
311
|
/>
|
|
331
|
-
<
|
|
312
|
+
<Button
|
|
332
313
|
type="button"
|
|
314
|
+
variant="ghost"
|
|
315
|
+
size="sm"
|
|
333
316
|
onClick={() => removeRequirement(index)}
|
|
334
|
-
style={removeButtonStyle}
|
|
335
317
|
>
|
|
336
318
|
×
|
|
337
|
-
</
|
|
338
|
-
</
|
|
319
|
+
</Button>
|
|
320
|
+
</Stack>
|
|
339
321
|
))}
|
|
340
|
-
<
|
|
322
|
+
<Button
|
|
341
323
|
type="button"
|
|
324
|
+
variant="ghost"
|
|
325
|
+
size="sm"
|
|
342
326
|
onClick={() => appendRequirement("")}
|
|
343
|
-
style={
|
|
327
|
+
style={{ padding: 0, alignSelf: 'flex-start' }}
|
|
344
328
|
>
|
|
345
329
|
+ Add requirement
|
|
346
|
-
</
|
|
347
|
-
</
|
|
330
|
+
</Button>
|
|
331
|
+
</Stack>
|
|
348
332
|
</div>
|
|
349
333
|
</Section>
|
|
350
334
|
|
|
@@ -385,9 +369,9 @@ export function FragmentEditor({
|
|
|
385
369
|
>
|
|
386
370
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px', marginBottom: '16px' }}>
|
|
387
371
|
<div>
|
|
388
|
-
<label style={
|
|
372
|
+
<Text as="label" size="xs" weight="medium" color="secondary" style={{ display: 'block', marginBottom: '6px' }}>
|
|
389
373
|
Status
|
|
390
|
-
</
|
|
374
|
+
</Text>
|
|
391
375
|
<select
|
|
392
376
|
{...register("meta.status")}
|
|
393
377
|
style={inputStyle}
|
|
@@ -400,9 +384,9 @@ export function FragmentEditor({
|
|
|
400
384
|
</select>
|
|
401
385
|
</div>
|
|
402
386
|
<div>
|
|
403
|
-
<label style={
|
|
387
|
+
<Text as="label" size="xs" weight="medium" color="secondary" style={{ display: 'block', marginBottom: '6px' }}>
|
|
404
388
|
Since Version
|
|
405
|
-
</
|
|
389
|
+
</Text>
|
|
406
390
|
<input
|
|
407
391
|
{...register("meta.since")}
|
|
408
392
|
style={inputStyle}
|
|
@@ -411,9 +395,9 @@ export function FragmentEditor({
|
|
|
411
395
|
</div>
|
|
412
396
|
</div>
|
|
413
397
|
<div style={{ marginBottom: '16px' }}>
|
|
414
|
-
<label style={
|
|
398
|
+
<Text as="label" size="xs" weight="medium" color="secondary" style={{ display: 'block', marginBottom: '6px' }}>
|
|
415
399
|
Owner
|
|
416
|
-
</
|
|
400
|
+
</Text>
|
|
417
401
|
<input
|
|
418
402
|
{...register("meta.owner")}
|
|
419
403
|
style={inputStyle}
|
|
@@ -421,29 +405,19 @@ export function FragmentEditor({
|
|
|
421
405
|
/>
|
|
422
406
|
</div>
|
|
423
407
|
</Section>
|
|
424
|
-
</
|
|
408
|
+
</Box>
|
|
425
409
|
|
|
426
410
|
{/* Footer with save button */}
|
|
427
|
-
<
|
|
428
|
-
<
|
|
411
|
+
<Box paddingX="md" paddingY="sm" borderTop background="secondary">
|
|
412
|
+
<Button
|
|
429
413
|
type="submit"
|
|
414
|
+
variant="primary"
|
|
415
|
+
fullWidth
|
|
430
416
|
disabled={!isDirty || saving}
|
|
431
|
-
style={{
|
|
432
|
-
width: '100%',
|
|
433
|
-
padding: '8px 16px',
|
|
434
|
-
fontSize: '14px',
|
|
435
|
-
fontWeight: 500,
|
|
436
|
-
borderRadius: '8px',
|
|
437
|
-
transition: 'background-color 150ms',
|
|
438
|
-
border: 'none',
|
|
439
|
-
cursor: isDirty && !saving ? 'pointer' : 'not-allowed',
|
|
440
|
-
background: isDirty && !saving ? 'var(--color-accent)' : 'var(--bg-secondary)',
|
|
441
|
-
color: isDirty && !saving ? 'white' : 'var(--text-tertiary)',
|
|
442
|
-
}}
|
|
443
417
|
>
|
|
444
418
|
{saving ? "Saving..." : "Save Fragment"}
|
|
445
|
-
</
|
|
446
|
-
</
|
|
419
|
+
</Button>
|
|
420
|
+
</Box>
|
|
447
421
|
</form>
|
|
448
422
|
);
|
|
449
423
|
}
|
|
@@ -459,7 +433,7 @@ function Section({ title, expanded, onToggle, children }: SectionProps) {
|
|
|
459
433
|
const [hovered, setHovered] = useState(false);
|
|
460
434
|
|
|
461
435
|
return (
|
|
462
|
-
<
|
|
436
|
+
<Box borderBottom>
|
|
463
437
|
<button
|
|
464
438
|
type="button"
|
|
465
439
|
onClick={onToggle}
|
|
@@ -477,9 +451,9 @@ function Section({ title, expanded, onToggle, children }: SectionProps) {
|
|
|
477
451
|
cursor: 'pointer',
|
|
478
452
|
}}
|
|
479
453
|
>
|
|
480
|
-
<
|
|
454
|
+
<Text size="xs" weight="semibold" style={{ textTransform: 'uppercase', letterSpacing: '0.05em' }}>
|
|
481
455
|
{title}
|
|
482
|
-
</
|
|
456
|
+
</Text>
|
|
483
457
|
<ChevronDownIcon
|
|
484
458
|
style={{
|
|
485
459
|
width: '16px',
|
|
@@ -490,8 +464,8 @@ function Section({ title, expanded, onToggle, children }: SectionProps) {
|
|
|
490
464
|
}}
|
|
491
465
|
/>
|
|
492
466
|
</button>
|
|
493
|
-
{expanded && <
|
|
494
|
-
</
|
|
467
|
+
{expanded && <Box padding="md">{children}</Box>}
|
|
468
|
+
</Box>
|
|
495
469
|
);
|
|
496
470
|
}
|
|
497
471
|
|
|
@@ -514,35 +488,38 @@ function RelatedField({
|
|
|
514
488
|
|
|
515
489
|
return (
|
|
516
490
|
<div style={{ marginBottom: '16px' }}>
|
|
517
|
-
<label style={{ display: 'block',
|
|
491
|
+
<Text as="label" size="xs" weight="medium" color="secondary" style={{ display: 'block', marginBottom: '2px' }}>
|
|
518
492
|
{label}
|
|
519
|
-
</
|
|
520
|
-
<
|
|
521
|
-
<
|
|
493
|
+
</Text>
|
|
494
|
+
<Text size="xs" color="tertiary" style={{ marginBottom: '8px', marginTop: 0, fontSize: '10px' }}>{description}</Text>
|
|
495
|
+
<Stack gap="sm">
|
|
522
496
|
{fields.map((field, index) => (
|
|
523
|
-
<
|
|
497
|
+
<Stack key={field.id} direction="row" gap="sm">
|
|
524
498
|
<input
|
|
525
499
|
{...register(`${name}.${index}` as const)}
|
|
526
500
|
style={{ ...inputStyle, flex: 1 }}
|
|
527
501
|
placeholder="ComponentName"
|
|
528
502
|
/>
|
|
529
|
-
<
|
|
503
|
+
<Button
|
|
530
504
|
type="button"
|
|
505
|
+
variant="ghost"
|
|
506
|
+
size="sm"
|
|
531
507
|
onClick={() => remove(index)}
|
|
532
|
-
style={removeButtonStyle}
|
|
533
508
|
>
|
|
534
509
|
×
|
|
535
|
-
</
|
|
536
|
-
</
|
|
510
|
+
</Button>
|
|
511
|
+
</Stack>
|
|
537
512
|
))}
|
|
538
|
-
<
|
|
513
|
+
<Button
|
|
539
514
|
type="button"
|
|
515
|
+
variant="ghost"
|
|
516
|
+
size="sm"
|
|
540
517
|
onClick={() => append("")}
|
|
541
|
-
style={
|
|
518
|
+
style={{ padding: 0, alignSelf: 'flex-start' }}
|
|
542
519
|
>
|
|
543
520
|
+ Add component
|
|
544
|
-
</
|
|
545
|
-
</
|
|
521
|
+
</Button>
|
|
522
|
+
</Stack>
|
|
546
523
|
</div>
|
|
547
524
|
);
|
|
548
525
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { RefObject } from "react";
|
|
2
|
+
import { Header, Input } from "@fragments-sdk/ui";
|
|
3
|
+
|
|
4
|
+
interface HeaderSearchProps {
|
|
5
|
+
value: string;
|
|
6
|
+
onChange: (value: string) => void;
|
|
7
|
+
inputRef: RefObject<HTMLInputElement>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function HeaderSearch({ value, onChange, inputRef }: HeaderSearchProps) {
|
|
11
|
+
return (
|
|
12
|
+
<Header.Search expandable>
|
|
13
|
+
<Input
|
|
14
|
+
ref={inputRef}
|
|
15
|
+
value={value}
|
|
16
|
+
onChange={onChange}
|
|
17
|
+
placeholder="Search components"
|
|
18
|
+
aria-label="Search components"
|
|
19
|
+
size="sm"
|
|
20
|
+
style={{ width: "240px" }}
|
|
21
|
+
/>
|
|
22
|
+
</Header.Search>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -16,6 +16,20 @@ import {
|
|
|
16
16
|
} from '../hooks/useA11yCache.js';
|
|
17
17
|
import type { A11ySummary, CachedA11yResult } from '../types/a11y.js';
|
|
18
18
|
|
|
19
|
+
/** Normalize category to Title Case for display */
|
|
20
|
+
function titleCase(str: string): string {
|
|
21
|
+
return str.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Normalize category to a canonical key so "Form" and "Forms" merge into one group */
|
|
25
|
+
function categoryKey(raw: string): string {
|
|
26
|
+
const tc = titleCase(raw.trim());
|
|
27
|
+
if (tc.length > 4 && tc.endsWith('s') && !tc.endsWith('ss')) {
|
|
28
|
+
return tc.slice(0, -1);
|
|
29
|
+
}
|
|
30
|
+
return tc;
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
interface HealthDashboardProps {
|
|
20
34
|
fragments: Array<{ path: string; fragment: FragmentDefinition }>;
|
|
21
35
|
onNavigate?: (componentName: string) => void;
|
|
@@ -68,7 +82,7 @@ function calculateCoverage(
|
|
|
68
82
|
|
|
69
83
|
for (const { fragment } of fragments) {
|
|
70
84
|
const cat = fragment.meta.category || 'uncategorized';
|
|
71
|
-
categories.add(cat);
|
|
85
|
+
categories.add(categoryKey(cat));
|
|
72
86
|
|
|
73
87
|
if (fragment.meta.description && fragment.meta.description.trim().length > 10) {
|
|
74
88
|
documented++;
|
|
@@ -89,7 +103,7 @@ function calculateCoverage(
|
|
|
89
103
|
|
|
90
104
|
components.push({
|
|
91
105
|
name: fragment.meta.name,
|
|
92
|
-
category: cat,
|
|
106
|
+
category: titleCase(cat),
|
|
93
107
|
variantCount,
|
|
94
108
|
status: fragment.meta.status || 'stable',
|
|
95
109
|
});
|
|
@@ -301,6 +301,15 @@ export function EmptyIcon({ className, style }: IconProps) {
|
|
|
301
301
|
);
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
+
// GitHub
|
|
305
|
+
export function GitHubIcon({ className, style }: IconProps) {
|
|
306
|
+
return (
|
|
307
|
+
<svg className={className} style={style} width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
|
308
|
+
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
|
309
|
+
</svg>
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
304
313
|
// Figma
|
|
305
314
|
export function FigmaIcon({ className, style }: IconProps) {
|
|
306
315
|
return (
|