sdocs 0.0.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/README.md +43 -0
- package/bin/sdocs.js +2 -0
- package/dist/Sdocs.svelte +1210 -0
- package/dist/Sdocs.svelte.d.ts +5 -0
- package/dist/cli/app-plugin.d.ts +7 -0
- package/dist/cli/app-plugin.js +69 -0
- package/dist/cli/config.d.ts +12 -0
- package/dist/cli/config.js +34 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +72 -0
- package/dist/cli/server.d.ts +2 -0
- package/dist/cli/server.js +62 -0
- package/dist/docgen.d.ts +47 -0
- package/dist/docgen.js +463 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/internal/ComponentPreview.svelte +58 -0
- package/dist/internal/ComponentPreview.svelte.d.ts +17 -0
- package/dist/internal/CssPropsTable.svelte +239 -0
- package/dist/internal/CssPropsTable.svelte.d.ts +11 -0
- package/dist/internal/Home.svelte +92 -0
- package/dist/internal/Home.svelte.d.ts +9 -0
- package/dist/internal/MethodsTable.svelte +72 -0
- package/dist/internal/MethodsTable.svelte.d.ts +7 -0
- package/dist/internal/PropsTable.svelte +342 -0
- package/dist/internal/PropsTable.svelte.d.ts +12 -0
- package/dist/internal/Showcase.svelte +130 -0
- package/dist/internal/Showcase.svelte.d.ts +21 -0
- package/dist/types.d.ts +162 -0
- package/dist/types.js +1 -0
- package/dist/ui/Badge/Badge.docs.svelte +46 -0
- package/dist/ui/Badge/Badge.docs.svelte.d.ts +26 -0
- package/dist/ui/Badge/Badge.svelte +59 -0
- package/dist/ui/Badge/Badge.svelte.d.ts +17 -0
- package/dist/ui/Badge/index.d.ts +1 -0
- package/dist/ui/Badge/index.js +1 -0
- package/dist/ui/Checkbox/Checkbox.docs.svelte +51 -0
- package/dist/ui/Checkbox/Checkbox.docs.svelte.d.ts +27 -0
- package/dist/ui/Checkbox/Checkbox.svelte +169 -0
- package/dist/ui/Checkbox/Checkbox.svelte.d.ts +18 -0
- package/dist/ui/Checkbox/index.d.ts +1 -0
- package/dist/ui/Checkbox/index.js +1 -0
- package/dist/ui/CodeBlock/CodeBlock.docs.svelte +28 -0
- package/dist/ui/CodeBlock/CodeBlock.docs.svelte.d.ts +24 -0
- package/dist/ui/CodeBlock/CodeBlock.svelte +101 -0
- package/dist/ui/CodeBlock/CodeBlock.svelte.d.ts +7 -0
- package/dist/ui/CodeBlock/index.d.ts +1 -0
- package/dist/ui/CodeBlock/index.js +1 -0
- package/dist/ui/Frame/Frame.docs.svelte +140 -0
- package/dist/ui/Frame/Frame.docs.svelte.d.ts +26 -0
- package/dist/ui/Frame/Frame.svelte +88 -0
- package/dist/ui/Frame/Frame.svelte.d.ts +15 -0
- package/dist/ui/Frame/index.d.ts +1 -0
- package/dist/ui/Frame/index.js +1 -0
- package/dist/ui/InputNumber/InputNumber.docs.svelte +50 -0
- package/dist/ui/InputNumber/InputNumber.docs.svelte.d.ts +26 -0
- package/dist/ui/InputNumber/InputNumber.svelte +275 -0
- package/dist/ui/InputNumber/InputNumber.svelte.d.ts +26 -0
- package/dist/ui/InputNumber/index.d.ts +1 -0
- package/dist/ui/InputNumber/index.js +1 -0
- package/dist/ui/InputText/InputText.docs.svelte +43 -0
- package/dist/ui/InputText/InputText.docs.svelte.d.ts +26 -0
- package/dist/ui/InputText/InputText.svelte +116 -0
- package/dist/ui/InputText/InputText.svelte.d.ts +22 -0
- package/dist/ui/InputText/index.d.ts +1 -0
- package/dist/ui/InputText/index.js +1 -0
- package/dist/ui/Panel/CollapsiblePanel.docs.svelte +45 -0
- package/dist/ui/Panel/CollapsiblePanel.docs.svelte.d.ts +25 -0
- package/dist/ui/Panel/CollapsiblePanel.svelte +93 -0
- package/dist/ui/Panel/CollapsiblePanel.svelte.d.ts +14 -0
- package/dist/ui/Panel/index.d.ts +1 -0
- package/dist/ui/Panel/index.js +1 -0
- package/dist/ui/Placeholder/Placeholder.docs.svelte +49 -0
- package/dist/ui/Placeholder/Placeholder.docs.svelte.d.ts +26 -0
- package/dist/ui/Placeholder/Placeholder.svelte +99 -0
- package/dist/ui/Placeholder/Placeholder.svelte.d.ts +21 -0
- package/dist/ui/Placeholder/index.d.ts +1 -0
- package/dist/ui/Placeholder/index.js +1 -0
- package/dist/ui/Radio/Radio.docs.svelte +67 -0
- package/dist/ui/Radio/Radio.docs.svelte.d.ts +27 -0
- package/dist/ui/Radio/Radio.svelte +165 -0
- package/dist/ui/Radio/Radio.svelte.d.ts +22 -0
- package/dist/ui/Radio/RadioGroup.docs.svelte +70 -0
- package/dist/ui/Radio/RadioGroup.docs.svelte.d.ts +27 -0
- package/dist/ui/Radio/RadioGroup.svelte +98 -0
- package/dist/ui/Radio/RadioGroup.svelte.d.ts +27 -0
- package/dist/ui/Radio/index.d.ts +2 -0
- package/dist/ui/Radio/index.js +2 -0
- package/dist/ui/SegmentControl/SegmentControl.docs.svelte +54 -0
- package/dist/ui/SegmentControl/SegmentControl.docs.svelte.d.ts +25 -0
- package/dist/ui/SegmentControl/SegmentControl.svelte +120 -0
- package/dist/ui/SegmentControl/SegmentControl.svelte.d.ts +18 -0
- package/dist/ui/SegmentControl/index.d.ts +1 -0
- package/dist/ui/SegmentControl/index.js +1 -0
- package/dist/ui/Stack/Stack.docs.svelte +63 -0
- package/dist/ui/Stack/Stack.docs.svelte.d.ts +26 -0
- package/dist/ui/Stack/Stack.svelte +45 -0
- package/dist/ui/Stack/Stack.svelte.d.ts +19 -0
- package/dist/ui/Stack/index.d.ts +1 -0
- package/dist/ui/Stack/index.js +1 -0
- package/dist/ui/Table/Body.svelte +17 -0
- package/dist/ui/Table/Body.svelte.d.ts +11 -0
- package/dist/ui/Table/Caption.svelte +17 -0
- package/dist/ui/Table/Caption.svelte.d.ts +11 -0
- package/dist/ui/Table/Cell.svelte +24 -0
- package/dist/ui/Table/Cell.svelte.d.ts +15 -0
- package/dist/ui/Table/Foot.svelte +17 -0
- package/dist/ui/Table/Foot.svelte.d.ts +11 -0
- package/dist/ui/Table/Head.svelte +17 -0
- package/dist/ui/Table/Head.svelte.d.ts +11 -0
- package/dist/ui/Table/Header.svelte +27 -0
- package/dist/ui/Table/Header.svelte.d.ts +17 -0
- package/dist/ui/Table/Row.svelte +19 -0
- package/dist/ui/Table/Row.svelte.d.ts +13 -0
- package/dist/ui/Table/Table.docs.svelte +197 -0
- package/dist/ui/Table/Table.docs.svelte.d.ts +28 -0
- package/dist/ui/Table/Table.svelte +140 -0
- package/dist/ui/Table/Table.svelte.d.ts +27 -0
- package/dist/ui/Table/index.js +10 -0
- package/dist/ui/css/colors.css +377 -0
- package/dist/ui/css/global.css +10 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.js +12 -0
- package/dist/virtual-sdocs.d.ts +20 -0
- package/dist/vite-plugin.d.ts +18 -0
- package/dist/vite-plugin.js +206 -0
- package/dist/vite.d.ts +2 -0
- package/dist/vite.js +2 -0
- package/package.json +76 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ArgType, ArgTypes } from '../types.js';
|
|
3
|
+
import { Checkbox, SegmentControl, InputText, InputNumber, Placeholder, Table } from '../ui/index.js';
|
|
4
|
+
import type { default as PlaceholderType } from '../ui/Placeholder/Placeholder.svelte';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
argTypes: ArgTypes;
|
|
8
|
+
args: Record<string, unknown>;
|
|
9
|
+
/** Callback when args change (enables interactive controls) */
|
|
10
|
+
onchange?: (args: Record<string, unknown>) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let { argTypes, args, onchange }: Props = $props();
|
|
14
|
+
|
|
15
|
+
let isInteractive = $derived(!!onchange);
|
|
16
|
+
|
|
17
|
+
let bangRefs: Record<string, PlaceholderType> = {};
|
|
18
|
+
|
|
19
|
+
// Get sorted prop names
|
|
20
|
+
let propNames = $derived(Object.keys(argTypes).sort());
|
|
21
|
+
|
|
22
|
+
function getTypeName(argType: ArgTypes[string]): string {
|
|
23
|
+
if (argType.type) {
|
|
24
|
+
if (typeof argType.type === 'string') return argType.type;
|
|
25
|
+
if (argType.type.name) return argType.type.name;
|
|
26
|
+
}
|
|
27
|
+
if (argType.control) {
|
|
28
|
+
if (typeof argType.control === 'string') return argType.control;
|
|
29
|
+
if (argType.control.type) return argType.control.type;
|
|
30
|
+
}
|
|
31
|
+
return '-';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getDefaultValue(name: string, argType: ArgTypes[string]): string {
|
|
35
|
+
if (argType.defaultValue !== undefined) {
|
|
36
|
+
return formatValue(argType.defaultValue);
|
|
37
|
+
}
|
|
38
|
+
if (args[name] !== undefined) {
|
|
39
|
+
return formatValue(args[name]);
|
|
40
|
+
}
|
|
41
|
+
return '-';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function formatValue(value: unknown): string {
|
|
45
|
+
if (value === null) return 'null';
|
|
46
|
+
if (value === undefined) return 'undefined';
|
|
47
|
+
if (typeof value === 'string') return value;
|
|
48
|
+
if (typeof value === 'boolean') return value ? 'true' : 'false';
|
|
49
|
+
if (typeof value === 'number') return String(value);
|
|
50
|
+
if (typeof value === 'function') return 'Function';
|
|
51
|
+
if (Array.isArray(value)) return JSON.stringify(value);
|
|
52
|
+
if (typeof value === 'object') return JSON.stringify(value);
|
|
53
|
+
return String(value);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getDescription(argType: ArgTypes[string]): string {
|
|
57
|
+
return argType.description ?? '-';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isRequired(argType: ArgTypes[string]): boolean {
|
|
61
|
+
return argType.required === true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Control helpers
|
|
65
|
+
function getControlType(key: string, argType: ArgType): string | false {
|
|
66
|
+
if (argType.control === false) return false;
|
|
67
|
+
if (typeof argType.control === 'string') return argType.control;
|
|
68
|
+
if (typeof argType.control === 'object') return argType.control.type;
|
|
69
|
+
// Infer from value
|
|
70
|
+
const value = args[key];
|
|
71
|
+
if (typeof value === 'boolean') return 'boolean';
|
|
72
|
+
if (typeof value === 'number') return 'number';
|
|
73
|
+
return 'text';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getControlOptions(argType: ArgType): string[] | undefined {
|
|
77
|
+
if (argType && typeof argType.control === 'object') {
|
|
78
|
+
return argType.control.options;
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getControlConfig(argType: ArgType): { min?: number; max?: number; step?: number } {
|
|
84
|
+
if (argType && typeof argType.control === 'object') {
|
|
85
|
+
return { min: argType.control.min, max: argType.control.max, step: argType.control.step };
|
|
86
|
+
}
|
|
87
|
+
return {};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isFunctionType(argType: ArgType): boolean {
|
|
91
|
+
const typeName = typeof argType.type === 'string' ? argType.type : argType.type?.name ?? '';
|
|
92
|
+
return typeName.includes('=>') || typeName.toLowerCase() === 'function' || typeName.startsWith('(');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function bangProp(name: string) {
|
|
96
|
+
bangRefs[name]?.bang();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function updateArg(key: string, value: unknown) {
|
|
100
|
+
onchange?.({ ...args, [key]: value });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function handleInput(key: string, event: Event) {
|
|
104
|
+
const target = event.target as HTMLInputElement;
|
|
105
|
+
const argType = argTypes[key];
|
|
106
|
+
const controlType = getControlType(key, argType);
|
|
107
|
+
const currentValue = args[key];
|
|
108
|
+
|
|
109
|
+
if (controlType === 'boolean') {
|
|
110
|
+
updateArg(key, target.checked);
|
|
111
|
+
} else if (controlType === 'number' || controlType === 'range') {
|
|
112
|
+
updateArg(key, Number(target.value));
|
|
113
|
+
} else if ((controlType === 'radio' || controlType === 'select') && typeof currentValue === 'number') {
|
|
114
|
+
updateArg(key, Number(target.value));
|
|
115
|
+
} else {
|
|
116
|
+
updateArg(key, target.value);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
{#if propNames.length > 0}
|
|
122
|
+
<Table compact fixed borderless>
|
|
123
|
+
<Table.Head>
|
|
124
|
+
<Table.Row>
|
|
125
|
+
<Table.Header width="15%">Name</Table.Header>
|
|
126
|
+
<Table.Header width="18%">Type</Table.Header>
|
|
127
|
+
<Table.Header width="12%">Default</Table.Header>
|
|
128
|
+
<Table.Header width={isInteractive ? '25%' : '55%'}>Description</Table.Header>
|
|
129
|
+
{#if isInteractive}
|
|
130
|
+
<Table.Header width="30%">Control</Table.Header>
|
|
131
|
+
{/if}
|
|
132
|
+
</Table.Row>
|
|
133
|
+
</Table.Head>
|
|
134
|
+
<Table.Body>
|
|
135
|
+
{#each propNames as name (name)}
|
|
136
|
+
{@const argType = argTypes[name]}
|
|
137
|
+
{@const controlType = getControlType(name, argType)}
|
|
138
|
+
{@const options = getControlOptions(argType)}
|
|
139
|
+
{@const config = getControlConfig(argType)}
|
|
140
|
+
<Table.Row>
|
|
141
|
+
<Table.Cell>
|
|
142
|
+
<span class="prop-name">
|
|
143
|
+
<code>{name}</code>
|
|
144
|
+
{#if isRequired(argType)}
|
|
145
|
+
<span class="required">*</span>
|
|
146
|
+
{/if}
|
|
147
|
+
</span>
|
|
148
|
+
</Table.Cell>
|
|
149
|
+
<Table.Cell>
|
|
150
|
+
<code class="prop-type">{getTypeName(argType)}</code>
|
|
151
|
+
</Table.Cell>
|
|
152
|
+
<Table.Cell>
|
|
153
|
+
<code class="prop-default">{getDefaultValue(name, argType)}</code>
|
|
154
|
+
</Table.Cell>
|
|
155
|
+
<Table.Cell>
|
|
156
|
+
<span class="prop-description">{getDescription(argType)}</span>
|
|
157
|
+
</Table.Cell>
|
|
158
|
+
{#if isInteractive}
|
|
159
|
+
<Table.Cell>
|
|
160
|
+
<div class="prop-control">
|
|
161
|
+
{#if controlType === false && isFunctionType(argType)}
|
|
162
|
+
<Placeholder bind:this={bangRefs[name]} height="28px" radius={6} />
|
|
163
|
+
{:else if controlType === false}
|
|
164
|
+
<span class="no-control">-</span>
|
|
165
|
+
{:else if controlType === 'boolean'}
|
|
166
|
+
<Checkbox
|
|
167
|
+
checked={Boolean(args[name])}
|
|
168
|
+
size="s"
|
|
169
|
+
onchange={(v) => updateArg(name, v)}
|
|
170
|
+
/>
|
|
171
|
+
{:else if controlType === 'select' && options}
|
|
172
|
+
<select
|
|
173
|
+
value={String(args[name])}
|
|
174
|
+
onchange={(e) => handleInput(name, e)}
|
|
175
|
+
>
|
|
176
|
+
{#each options as option (option)}
|
|
177
|
+
<option value={option} selected={String(args[name]) === option}>{option}</option>
|
|
178
|
+
{/each}
|
|
179
|
+
</select>
|
|
180
|
+
{:else if controlType === 'radio' && options}
|
|
181
|
+
<SegmentControl
|
|
182
|
+
{options}
|
|
183
|
+
value={String(args[name])}
|
|
184
|
+
size="s"
|
|
185
|
+
onchange={(v) => updateArg(name, typeof args[name] === 'number' ? Number(v) : v)}
|
|
186
|
+
/>
|
|
187
|
+
{:else if controlType === 'number'}
|
|
188
|
+
<InputNumber
|
|
189
|
+
value={Number(args[name])}
|
|
190
|
+
min={config.min}
|
|
191
|
+
max={config.max}
|
|
192
|
+
step={config.step}
|
|
193
|
+
size="s"
|
|
194
|
+
oninput={(v) => updateArg(name, v)}
|
|
195
|
+
/>
|
|
196
|
+
{:else if controlType === 'range'}
|
|
197
|
+
<div class="range-control">
|
|
198
|
+
<input
|
|
199
|
+
type="range"
|
|
200
|
+
value={Number(args[name])}
|
|
201
|
+
min={config.min ?? 0}
|
|
202
|
+
max={config.max ?? 100}
|
|
203
|
+
step={config.step ?? 1}
|
|
204
|
+
oninput={(e) => handleInput(name, e)}
|
|
205
|
+
/>
|
|
206
|
+
<span class="range-value">{args[name]}</span>
|
|
207
|
+
</div>
|
|
208
|
+
{:else if controlType === 'color'}
|
|
209
|
+
<input
|
|
210
|
+
type="color"
|
|
211
|
+
value={String(args[name])}
|
|
212
|
+
oninput={(e) => handleInput(name, e)}
|
|
213
|
+
/>
|
|
214
|
+
{:else}
|
|
215
|
+
<InputText
|
|
216
|
+
value={String(args[name])}
|
|
217
|
+
size="s"
|
|
218
|
+
oninput={(v: string) => updateArg(name, v)}
|
|
219
|
+
/>
|
|
220
|
+
{/if}
|
|
221
|
+
</div>
|
|
222
|
+
</Table.Cell>
|
|
223
|
+
{/if}
|
|
224
|
+
</Table.Row>
|
|
225
|
+
{/each}
|
|
226
|
+
</Table.Body>
|
|
227
|
+
</Table>
|
|
228
|
+
{:else}
|
|
229
|
+
<p class="no-props">No props defined</p>
|
|
230
|
+
{/if}
|
|
231
|
+
|
|
232
|
+
<style>
|
|
233
|
+
.prop-name {
|
|
234
|
+
white-space: nowrap;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.prop-name code {
|
|
238
|
+
font-family: var(--font-mono);
|
|
239
|
+
font-weight: 600;
|
|
240
|
+
color: var(--color-text);
|
|
241
|
+
background: none;
|
|
242
|
+
padding: 0;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.required {
|
|
246
|
+
color: var(--danger-500);
|
|
247
|
+
margin-left: 2px;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.prop-type {
|
|
251
|
+
font-family: var(--font-mono);
|
|
252
|
+
color: var(--pink-600);
|
|
253
|
+
background: var(--pink-50);
|
|
254
|
+
padding: 2px 6px;
|
|
255
|
+
border-radius: 4px;
|
|
256
|
+
font-size: 12px;
|
|
257
|
+
white-space: nowrap;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.prop-default {
|
|
261
|
+
font-family: var(--font-mono);
|
|
262
|
+
color: var(--color-text-secondary);
|
|
263
|
+
background: var(--color-bg-hover);
|
|
264
|
+
padding: 2px 6px;
|
|
265
|
+
border-radius: 4px;
|
|
266
|
+
font-size: 12px;
|
|
267
|
+
white-space: nowrap;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.prop-description {
|
|
271
|
+
color: var(--color-text-secondary);
|
|
272
|
+
line-height: 1.5;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.no-props {
|
|
276
|
+
color: var(--color-text-muted);
|
|
277
|
+
font-style: italic;
|
|
278
|
+
margin: 0;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* Control column */
|
|
282
|
+
.prop-control {
|
|
283
|
+
overflow: hidden;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.prop-control :global(input.InputText) {
|
|
287
|
+
width: 100%;
|
|
288
|
+
max-width: 100%;
|
|
289
|
+
box-sizing: border-box;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.prop-control input[type='color'] {
|
|
293
|
+
width: 32px;
|
|
294
|
+
height: 28px;
|
|
295
|
+
border: 1px solid var(--color-border);
|
|
296
|
+
border-radius: 4px;
|
|
297
|
+
cursor: pointer;
|
|
298
|
+
padding: 2px;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.prop-control select {
|
|
302
|
+
width: 100%;
|
|
303
|
+
max-width: 160px;
|
|
304
|
+
height: 28px;
|
|
305
|
+
padding: 0 8px;
|
|
306
|
+
border: 1px solid var(--color-border);
|
|
307
|
+
border-radius: 4px;
|
|
308
|
+
font-size: 12px;
|
|
309
|
+
font-family: var(--font-mono);
|
|
310
|
+
background: var(--color-bg-elevated);
|
|
311
|
+
color: var(--color-text);
|
|
312
|
+
cursor: pointer;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.prop-control select:focus {
|
|
316
|
+
outline: none;
|
|
317
|
+
border-color: var(--color-primary);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.range-control {
|
|
321
|
+
display: flex;
|
|
322
|
+
align-items: center;
|
|
323
|
+
gap: 8px;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.range-control input[type='range'] {
|
|
327
|
+
flex: 1;
|
|
328
|
+
accent-color: var(--action-500);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.range-value {
|
|
332
|
+
min-width: 32px;
|
|
333
|
+
font-size: 12px;
|
|
334
|
+
color: var(--color-text-secondary);
|
|
335
|
+
font-weight: 500;
|
|
336
|
+
text-align: right;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.no-control {
|
|
340
|
+
color: var(--color-text-muted);
|
|
341
|
+
}
|
|
342
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ArgTypes } from '../types.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
argTypes: ArgTypes;
|
|
4
|
+
args: Record<string, unknown>;
|
|
5
|
+
/** Callback when args change (enables interactive controls) */
|
|
6
|
+
onchange?: (args: Record<string, unknown>) => void;
|
|
7
|
+
}
|
|
8
|
+
declare const PropsTable: import("svelte").Component<Props, {
|
|
9
|
+
bangProp: (name: string) => void;
|
|
10
|
+
}, "">;
|
|
11
|
+
type PropsTable = ReturnType<typeof PropsTable>;
|
|
12
|
+
export default PropsTable;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { ArgType, CssProps, MethodType } from '../types.js';
|
|
4
|
+
import PropsTable from './PropsTable.svelte';
|
|
5
|
+
import CssPropsTable from './CssPropsTable.svelte';
|
|
6
|
+
import MethodsTable from './MethodsTable.svelte';
|
|
7
|
+
import { CodeBlock, CollapsiblePanel } from '../ui/index.js';
|
|
8
|
+
import ComponentPreview from './ComponentPreview.svelte';
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
/** The render function for the component preview */
|
|
12
|
+
render: Snippet<[Record<string, unknown>]>;
|
|
13
|
+
/** Current args values */
|
|
14
|
+
args: Record<string, unknown>;
|
|
15
|
+
/** Arg type definitions for controls and props table */
|
|
16
|
+
argTypes?: Record<string, ArgType>;
|
|
17
|
+
/** CSS custom properties that can be set on the component */
|
|
18
|
+
cssProps?: CssProps;
|
|
19
|
+
/** Exported component methods */
|
|
20
|
+
methods?: MethodType[];
|
|
21
|
+
/** Callback when args change */
|
|
22
|
+
onchange?: (args: Record<string, unknown>) => void;
|
|
23
|
+
/** Optional source code to display */
|
|
24
|
+
source?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let {
|
|
28
|
+
render,
|
|
29
|
+
args,
|
|
30
|
+
argTypes = {},
|
|
31
|
+
cssProps,
|
|
32
|
+
methods,
|
|
33
|
+
onchange,
|
|
34
|
+
source
|
|
35
|
+
}: Props = $props();
|
|
36
|
+
|
|
37
|
+
// State for CSS prop values
|
|
38
|
+
let cssValues = $state<Record<string, string>>({});
|
|
39
|
+
|
|
40
|
+
// Expand source to include CSS props when they're set
|
|
41
|
+
let expandedSource = $derived.by(() => {
|
|
42
|
+
if (!source) return source;
|
|
43
|
+
|
|
44
|
+
const activeCssProps = Object.entries(cssValues).filter(([_, v]) => v);
|
|
45
|
+
if (activeCssProps.length === 0) return source;
|
|
46
|
+
|
|
47
|
+
// Build CSS props string with proper indentation
|
|
48
|
+
const cssPropsStr = activeCssProps
|
|
49
|
+
.map(([k, v]) => `\t${k}="${v}"`)
|
|
50
|
+
.join('\n');
|
|
51
|
+
|
|
52
|
+
// Insert before the closing /> (self-closing tag)
|
|
53
|
+
return source.replace(/(\n?\t*)(\/>)/, `\n${cssPropsStr}$1$2`);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
let propsTableRef: PropsTable | undefined;
|
|
57
|
+
|
|
58
|
+
function isFunctionType(argType: ArgType): boolean {
|
|
59
|
+
const typeName = typeof argType.type === 'string' ? argType.type : argType.type?.name ?? '';
|
|
60
|
+
return typeName.includes('=>') || typeName.toLowerCase() === 'function' || typeName.startsWith('(');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Create wrapper functions for function-type props that trigger bang in PropsTable
|
|
64
|
+
let renderArgs = $derived.by(() => {
|
|
65
|
+
const result = { ...args };
|
|
66
|
+
for (const [name, argType] of Object.entries(argTypes)) {
|
|
67
|
+
if (argType.control === false && isFunctionType(argType)) {
|
|
68
|
+
result[name] = (...fnArgs: unknown[]) => {
|
|
69
|
+
propsTableRef?.bangProp(name);
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
function handleArgsChange(newArgs: Record<string, unknown>) {
|
|
77
|
+
onchange?.(newArgs);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function handleCssChange(newValues: Record<string, string>) {
|
|
81
|
+
cssValues = newValues;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let hasProps = $derived(Object.keys(argTypes).length > 0);
|
|
85
|
+
let hasCssProps = $derived(cssProps && Object.keys(cssProps).length > 0);
|
|
86
|
+
let hasMethods = $derived(methods && methods.length > 0);
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
<div class="Showcase">
|
|
90
|
+
<ComponentPreview cssVars={cssValues}>
|
|
91
|
+
{@render render(renderArgs)}
|
|
92
|
+
</ComponentPreview>
|
|
93
|
+
|
|
94
|
+
{#if hasProps}
|
|
95
|
+
<CollapsiblePanel title="Props" no_content_padding>
|
|
96
|
+
<PropsTable bind:this={propsTableRef} {argTypes} {args} onchange={onchange ? handleArgsChange : undefined} />
|
|
97
|
+
</CollapsiblePanel>
|
|
98
|
+
{/if}
|
|
99
|
+
|
|
100
|
+
{#if hasCssProps && cssProps}
|
|
101
|
+
<CollapsiblePanel title="CSS Props" no_content_padding>
|
|
102
|
+
<CssPropsTable {cssProps} values={cssValues} onchange={handleCssChange} />
|
|
103
|
+
</CollapsiblePanel>
|
|
104
|
+
{/if}
|
|
105
|
+
|
|
106
|
+
{#if hasMethods && methods}
|
|
107
|
+
<CollapsiblePanel title="Methods" no_content_padding>
|
|
108
|
+
<MethodsTable {methods} />
|
|
109
|
+
</CollapsiblePanel>
|
|
110
|
+
{/if}
|
|
111
|
+
|
|
112
|
+
{#if expandedSource}
|
|
113
|
+
<CollapsiblePanel title="Code" open={false} no_content_padding>
|
|
114
|
+
<CodeBlock code={expandedSource} />
|
|
115
|
+
</CollapsiblePanel>
|
|
116
|
+
{/if}
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<style>
|
|
120
|
+
.Showcase {
|
|
121
|
+
display: flex;
|
|
122
|
+
flex-direction: column;
|
|
123
|
+
gap: 1px;
|
|
124
|
+
background: var(--color-border);
|
|
125
|
+
border: 1px solid var(--color-border);
|
|
126
|
+
border-radius: 8px;
|
|
127
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
128
|
+
overflow: hidden;
|
|
129
|
+
}
|
|
130
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { ArgType, CssProps, MethodType } from '../types.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** The render function for the component preview */
|
|
5
|
+
render: Snippet<[Record<string, unknown>]>;
|
|
6
|
+
/** Current args values */
|
|
7
|
+
args: Record<string, unknown>;
|
|
8
|
+
/** Arg type definitions for controls and props table */
|
|
9
|
+
argTypes?: Record<string, ArgType>;
|
|
10
|
+
/** CSS custom properties that can be set on the component */
|
|
11
|
+
cssProps?: CssProps;
|
|
12
|
+
/** Exported component methods */
|
|
13
|
+
methods?: MethodType[];
|
|
14
|
+
/** Callback when args change */
|
|
15
|
+
onchange?: (args: Record<string, unknown>) => void;
|
|
16
|
+
/** Optional source code to display */
|
|
17
|
+
source?: string;
|
|
18
|
+
}
|
|
19
|
+
declare const Showcase: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type Showcase = ReturnType<typeof Showcase>;
|
|
21
|
+
export default Showcase;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { Component, Snippet } from 'svelte';
|
|
2
|
+
/**
|
|
3
|
+
* Control types for the props panel
|
|
4
|
+
*/
|
|
5
|
+
export type ControlType = 'text' | 'number' | 'boolean' | 'select' | 'radio' | 'color' | 'range';
|
|
6
|
+
/**
|
|
7
|
+
* Control configuration for a single arg
|
|
8
|
+
*/
|
|
9
|
+
export interface ArgType {
|
|
10
|
+
/** Control type or false to disable control (prop still shows in table) */
|
|
11
|
+
control: ControlType | {
|
|
12
|
+
type: ControlType;
|
|
13
|
+
options?: string[];
|
|
14
|
+
min?: number;
|
|
15
|
+
max?: number;
|
|
16
|
+
step?: number;
|
|
17
|
+
} | false;
|
|
18
|
+
/** Description of the prop */
|
|
19
|
+
description?: string;
|
|
20
|
+
/** Type information for the props table */
|
|
21
|
+
type?: {
|
|
22
|
+
name: string;
|
|
23
|
+
summary?: string;
|
|
24
|
+
};
|
|
25
|
+
/** Whether this prop is required */
|
|
26
|
+
required?: boolean;
|
|
27
|
+
/** Default value for display in props table */
|
|
28
|
+
defaultValue?: unknown;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Map of prop names to their ArgType configuration
|
|
32
|
+
*/
|
|
33
|
+
export type ArgTypes = Record<string, ArgType>;
|
|
34
|
+
/**
|
|
35
|
+
* Control types for CSS custom properties
|
|
36
|
+
*/
|
|
37
|
+
export type CssControlType = 'color' | 'text' | 'length' | 'number' | 'select';
|
|
38
|
+
/**
|
|
39
|
+
* Configuration for a CSS custom property
|
|
40
|
+
*/
|
|
41
|
+
export interface CssPropType {
|
|
42
|
+
/** Description of the CSS prop */
|
|
43
|
+
description?: string;
|
|
44
|
+
/** Default value */
|
|
45
|
+
default?: string;
|
|
46
|
+
/** Control type or config. Use false to disable control. */
|
|
47
|
+
control?: CssControlType | {
|
|
48
|
+
type: CssControlType;
|
|
49
|
+
options?: string[];
|
|
50
|
+
} | false;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Map of CSS prop names to their configuration
|
|
54
|
+
*/
|
|
55
|
+
export type CssProps = Record<string, CssPropType>;
|
|
56
|
+
/**
|
|
57
|
+
* Exported component method
|
|
58
|
+
*/
|
|
59
|
+
export interface MethodType {
|
|
60
|
+
name: string;
|
|
61
|
+
params?: string;
|
|
62
|
+
returnType?: string;
|
|
63
|
+
description?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Metadata exported from a .docs.svelte or .docs.svx file
|
|
67
|
+
*/
|
|
68
|
+
export interface DocMeta {
|
|
69
|
+
title?: string;
|
|
70
|
+
/** Component description (extracted from JSDoc) */
|
|
71
|
+
description?: string;
|
|
72
|
+
/** Component to document (if this is a component doc) */
|
|
73
|
+
component?: Component<any>;
|
|
74
|
+
/** Initial values for props (only for props that exist in the component) */
|
|
75
|
+
args?: Record<string, unknown>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Parsed metadata with auto-generated fields (internal use)
|
|
79
|
+
*/
|
|
80
|
+
export interface ParsedDocMeta extends DocMeta {
|
|
81
|
+
/** Auto-generated from component props */
|
|
82
|
+
args?: Record<string, unknown>;
|
|
83
|
+
/** Auto-generated from component interface Props */
|
|
84
|
+
argTypes?: Record<string, ArgType>;
|
|
85
|
+
/** Auto-generated from @cssvar JSDoc tags */
|
|
86
|
+
cssProps?: CssProps;
|
|
87
|
+
/** Auto-generated from exported functions */
|
|
88
|
+
methods?: MethodType[];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A single example extracted from a doc file
|
|
92
|
+
*/
|
|
93
|
+
export interface Example {
|
|
94
|
+
name: string;
|
|
95
|
+
render: Snippet<[args: Record<string, unknown>]> | Snippet<[]>;
|
|
96
|
+
/** Source code of the snippet (extracted by vite plugin) */
|
|
97
|
+
source?: string;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Parsed doc file with metadata and examples
|
|
101
|
+
*/
|
|
102
|
+
export interface DocFile {
|
|
103
|
+
path: string;
|
|
104
|
+
meta: ParsedDocMeta;
|
|
105
|
+
examples: Example[];
|
|
106
|
+
module: Record<string, unknown>;
|
|
107
|
+
/** Type of doc: 'component' has a component in meta, 'page' is standalone documentation */
|
|
108
|
+
type: 'component' | 'page';
|
|
109
|
+
/** Whether this file is a markdown (.svx) file */
|
|
110
|
+
isMarkdown?: boolean;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Sidebar configuration options
|
|
114
|
+
*/
|
|
115
|
+
export interface SidebarOptions {
|
|
116
|
+
/**
|
|
117
|
+
* Custom order for sidebar items. Use '*' for "everything else" (alphabetically).
|
|
118
|
+
*
|
|
119
|
+
* Can be an array for top-level ordering, or an object for nested folder ordering.
|
|
120
|
+
* Use path keys (e.g., 'Components/Forms') for deep nesting.
|
|
121
|
+
* @example
|
|
122
|
+
* // Top-level only
|
|
123
|
+
* order: ['Specs', 'Components', '*']
|
|
124
|
+
*
|
|
125
|
+
* // With nested folder ordering
|
|
126
|
+
* order: {
|
|
127
|
+
* root: ['Specs', '*'],
|
|
128
|
+
* Specs: ['Sidebar', 'Doc Files', '*'],
|
|
129
|
+
* 'Components/Forms': ['Input', 'Select', '*']
|
|
130
|
+
* }
|
|
131
|
+
*/
|
|
132
|
+
order?: string[] | Record<string, string[]>;
|
|
133
|
+
/**
|
|
134
|
+
* Paths to expand by default in the sidebar.
|
|
135
|
+
* @example
|
|
136
|
+
* open: ['Layout', 'Layout/Frame', 'Components']
|
|
137
|
+
*/
|
|
138
|
+
open?: string[];
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Sdocs configuration options
|
|
142
|
+
*/
|
|
143
|
+
export interface SdocsOptions {
|
|
144
|
+
/** Sidebar configuration */
|
|
145
|
+
sidebar?: SidebarOptions;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Props for the main Sdocs component
|
|
149
|
+
*/
|
|
150
|
+
export interface SdocsProps {
|
|
151
|
+
/**
|
|
152
|
+
* Configuration options for Sdocs.
|
|
153
|
+
* @example
|
|
154
|
+
* options: {
|
|
155
|
+
* sidebar: {
|
|
156
|
+
* order: { root: ['Layout', '*'] },
|
|
157
|
+
* open: ['Layout']
|
|
158
|
+
* }
|
|
159
|
+
* }
|
|
160
|
+
*/
|
|
161
|
+
options?: SdocsOptions;
|
|
162
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|