@rokkit/ui 1.0.4 → 1.1.0
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/package.json +7 -6
- package/src/MarkdownRenderer.svelte +13 -1
- package/src/components/AlertList.svelte +1 -13
- package/src/components/Avatar.svelte +1 -18
- package/src/components/Badge.svelte +1 -16
- package/src/components/BreadCrumbs.svelte +2 -21
- package/src/components/Card.svelte +1 -18
- package/src/components/Carousel.svelte +2 -27
- package/src/components/CodeBlock.svelte +130 -0
- package/src/components/CodeGroup.svelte +408 -0
- package/src/components/Connector.svelte +2 -8
- package/src/components/Divider.svelte +1 -8
- package/src/components/Frame.svelte +30 -0
- package/src/components/ItemContent.svelte +4 -2
- package/src/components/Message.svelte +1 -22
- package/src/components/NavContent.svelte +1 -16
- package/src/components/Pill.svelte +1 -24
- package/src/components/ProgressBar.svelte +1 -8
- package/src/components/Rating.svelte +2 -22
- package/src/components/ResponsiveGrid.svelte +1 -14
- package/src/components/Reveal.svelte +1 -23
- package/src/components/Select.svelte +90 -9
- package/src/components/Shine.svelte +1 -19
- package/src/components/Stack.svelte +1 -16
- package/src/components/StatusList.svelte +3 -2
- package/src/components/Stepper.svelte +2 -41
- package/src/components/Tilt.svelte +1 -13
- package/src/components/Toggle.svelte +83 -33
- package/src/components/index.ts +3 -0
- package/src/index.ts +4 -0
- package/src/types/alert-list.ts +28 -0
- package/src/types/avatar.ts +41 -0
- package/src/types/badge.ts +37 -0
- package/src/types/breadcrumbs.ts +43 -0
- package/src/types/card.ts +38 -0
- package/src/types/carousel.ts +49 -0
- package/src/types/code.ts +102 -1
- package/src/types/connector.ts +22 -0
- package/src/types/divider.ts +20 -0
- package/src/types/frame.ts +29 -0
- package/src/types/index.ts +66 -0
- package/src/types/item-content.ts +26 -0
- package/src/types/message.ts +54 -0
- package/src/types/nav-content.ts +31 -0
- package/src/types/pill.ts +43 -0
- package/src/types/progress-bar.ts +19 -0
- package/src/types/rating.ts +43 -0
- package/src/types/responsive-grid.ts +25 -0
- package/src/types/reveal.ts +49 -0
- package/src/types/shine.ts +37 -0
- package/src/types/stack.ts +40 -0
- package/src/types/status-list.ts +42 -0
- package/src/types/stepper.ts +74 -0
- package/src/types/tilt.ts +28 -0
- package/src/types/toggle.ts +12 -0
- package/src/types/tree.ts +46 -4
- package/src/utils/palette.ts +4 -2
- package/src/utils/shiki.ts +22 -1
- package/src/utils/upload.js +8 -8
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
/**
|
|
4
|
+
* CodeGroup — multi-file code display with a hierarchical tree picker
|
|
5
|
+
* and an optional live preview panel.
|
|
6
|
+
*
|
|
7
|
+
* Tree-based (not tabs) because real projects have nested folders that
|
|
8
|
+
* flatten poorly. On viewports wider than 768px the tree sits as a
|
|
9
|
+
* left rail; below that the tree collapses to a top "current file" pill
|
|
10
|
+
* that opens an overlay drawer, keeping code at full viewport width.
|
|
11
|
+
*
|
|
12
|
+
* Preview is rendered from an optional Svelte snippet, collapsed by
|
|
13
|
+
* default — users opt in via the "Show preview" toggle.
|
|
14
|
+
*
|
|
15
|
+
* Icons follow the Rokkit semantic-icon pattern: defaults come from
|
|
16
|
+
* `DEFAULT_STATE_ICONS` (the `file` / `folder` / `view` / `action`
|
|
17
|
+
* groups). Consumers override per-group via the `icons` prop, same
|
|
18
|
+
* shape as Tree / BreadCrumbs / Dropdown.
|
|
19
|
+
*/
|
|
20
|
+
import { DEFAULT_STATE_ICONS } from '@rokkit/core'
|
|
21
|
+
import type { CodeGroupFile, CodeGroupProps } from '../types/code.js'
|
|
22
|
+
import Tree from './Tree.svelte'
|
|
23
|
+
import CodeBlock from './CodeBlock.svelte'
|
|
24
|
+
|
|
25
|
+
let {
|
|
26
|
+
files = [],
|
|
27
|
+
initialFile,
|
|
28
|
+
class: className = '',
|
|
29
|
+
icons: userIcons = {},
|
|
30
|
+
preview,
|
|
31
|
+
showCopyButton = true
|
|
32
|
+
}: CodeGroupProps = $props()
|
|
33
|
+
|
|
34
|
+
const icons = $derived({
|
|
35
|
+
doc: { ...DEFAULT_STATE_ICONS.doc, ...(userIcons.doc ?? {}) },
|
|
36
|
+
folder: { ...DEFAULT_STATE_ICONS.folder, ...(userIcons.folder ?? {}) },
|
|
37
|
+
view: { ...DEFAULT_STATE_ICONS.view, ...(userIcons.view ?? {}) },
|
|
38
|
+
action: { ...DEFAULT_STATE_ICONS.action, ...(userIcons.action ?? {}) }
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
let selectedPath = $state('')
|
|
42
|
+
$effect(() => {
|
|
43
|
+
if (!selectedPath && files.length > 0) {
|
|
44
|
+
selectedPath = initialFile ?? files[0].path
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
let drawerOpen = $state(false)
|
|
48
|
+
let previewExpanded = $state(false)
|
|
49
|
+
|
|
50
|
+
// Map file extensions to icon keys in icons.doc. Unknown extensions fall
|
|
51
|
+
// back to icons.doc.default. Consumers can extend by passing an icon
|
|
52
|
+
// override on individual file entries or via the icons prop.
|
|
53
|
+
const EXT_TO_KEY: Record<string, string> = {
|
|
54
|
+
svelte: 'svelte',
|
|
55
|
+
js: 'js',
|
|
56
|
+
mjs: 'js',
|
|
57
|
+
cjs: 'js',
|
|
58
|
+
ts: 'ts',
|
|
59
|
+
tsx: 'ts',
|
|
60
|
+
css: 'css',
|
|
61
|
+
scss: 'css',
|
|
62
|
+
sass: 'css',
|
|
63
|
+
html: 'html',
|
|
64
|
+
htm: 'html',
|
|
65
|
+
json: 'json',
|
|
66
|
+
yaml: 'json',
|
|
67
|
+
yml: 'json',
|
|
68
|
+
md: 'md',
|
|
69
|
+
mdx: 'md'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function iconForFile(file: CodeGroupFile): string {
|
|
73
|
+
if (file.icon) return file.icon
|
|
74
|
+
const ext = (file.name ?? file.path).split('.').pop()?.toLowerCase() ?? ''
|
|
75
|
+
const key = EXT_TO_KEY[ext] ?? 'default'
|
|
76
|
+
return icons.doc[key] ?? icons.doc.default
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function iconForPath(path: string): string {
|
|
80
|
+
const ext = path.split('.').pop()?.toLowerCase() ?? ''
|
|
81
|
+
const key = EXT_TO_KEY[ext] ?? 'default'
|
|
82
|
+
return icons.doc[key] ?? icons.doc.default
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const activeFile = $derived(files.find((f) => f.path === selectedPath))
|
|
86
|
+
const activeFileName = $derived(
|
|
87
|
+
activeFile ? (activeFile.name ?? activeFile.path.split('/').pop()) : ''
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
// Build a hierarchical tree from the flat path list. Intermediate
|
|
91
|
+
// segments become folder nodes; the final segment becomes a file leaf
|
|
92
|
+
// carrying the original path as its id. Folder nodes get their icon
|
|
93
|
+
// from the Tree's default state (it owns folder open/closed via its
|
|
94
|
+
// own icons prop); file leaves get the per-extension semantic icon.
|
|
95
|
+
const tree = $derived.by(() => {
|
|
96
|
+
const root: unknown[] = []
|
|
97
|
+
const lookup = new Map<string, Record<string, unknown>>()
|
|
98
|
+
for (const f of files) {
|
|
99
|
+
const parts = f.path.split('/').filter(Boolean)
|
|
100
|
+
let parentChildren = root
|
|
101
|
+
let acc = ''
|
|
102
|
+
for (let i = 0; i < parts.length; i++) {
|
|
103
|
+
const part = parts[i]
|
|
104
|
+
const isLeaf = i === parts.length - 1
|
|
105
|
+
acc = acc ? `${acc}/${part}` : part
|
|
106
|
+
let node = lookup.get(acc) as Record<string, unknown> | undefined
|
|
107
|
+
if (!node) {
|
|
108
|
+
node = {
|
|
109
|
+
id: acc,
|
|
110
|
+
name: part,
|
|
111
|
+
icon: isLeaf ? iconForFile(f) : undefined,
|
|
112
|
+
children: isLeaf ? undefined : []
|
|
113
|
+
}
|
|
114
|
+
lookup.set(acc, node)
|
|
115
|
+
parentChildren.push(node)
|
|
116
|
+
}
|
|
117
|
+
if (!isLeaf) parentChildren = node.children as unknown[]
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return root
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
function handleTreeSelect(value: unknown) {
|
|
124
|
+
// Tree fires onselect for folders too; only switch the panel when
|
|
125
|
+
// the selection is a real file path. Closes the drawer on mobile
|
|
126
|
+
// after a pick so users see their selection immediately.
|
|
127
|
+
if (typeof value !== 'string') return
|
|
128
|
+
const file = files.find((f) => f.path === value)
|
|
129
|
+
if (!file) return
|
|
130
|
+
selectedPath = value
|
|
131
|
+
drawerOpen = false
|
|
132
|
+
}
|
|
133
|
+
</script>
|
|
134
|
+
|
|
135
|
+
<div class="code-group {className}" data-preview-open={previewExpanded ? '' : undefined}>
|
|
136
|
+
{#if files.length === 0}
|
|
137
|
+
<div class="empty">No files to display.</div>
|
|
138
|
+
{:else}
|
|
139
|
+
<!-- Mobile-only top picker. Hidden on desktop via CSS. -->
|
|
140
|
+
<button
|
|
141
|
+
type="button"
|
|
142
|
+
class="picker"
|
|
143
|
+
onclick={() => (drawerOpen = !drawerOpen)}
|
|
144
|
+
aria-haspopup="dialog"
|
|
145
|
+
aria-expanded={drawerOpen}
|
|
146
|
+
aria-controls="codegroup-drawer"
|
|
147
|
+
>
|
|
148
|
+
<span class="picker-icon {activeFile ? iconForPath(activeFile.path) : icons.doc.default}" aria-hidden="true"></span>
|
|
149
|
+
<span class="picker-name">{activeFileName || 'Choose file'}</span>
|
|
150
|
+
<span class="picker-chevron {icons.action.expand ?? 'navigate-down'}" aria-hidden="true"></span>
|
|
151
|
+
</button>
|
|
152
|
+
|
|
153
|
+
<!-- Tree rail (desktop). On mobile, becomes a drawer toggled by the
|
|
154
|
+
picker; the same DOM serves both roles via CSS. -->
|
|
155
|
+
<aside
|
|
156
|
+
id="codegroup-drawer"
|
|
157
|
+
class="rail"
|
|
158
|
+
data-open={drawerOpen ? '' : undefined}
|
|
159
|
+
aria-label="Files"
|
|
160
|
+
>
|
|
161
|
+
<div class="rail-header">Files</div>
|
|
162
|
+
<Tree
|
|
163
|
+
items={tree}
|
|
164
|
+
fields={{ value: 'id', label: 'name', icon: 'icon' }}
|
|
165
|
+
value={selectedPath}
|
|
166
|
+
size="sm"
|
|
167
|
+
onselect={handleTreeSelect}
|
|
168
|
+
icons={{ opened: icons.folder.opened, closed: icons.folder.closed }}
|
|
169
|
+
/>
|
|
170
|
+
</aside>
|
|
171
|
+
|
|
172
|
+
<!-- Backdrop covers the code area when the drawer is open on mobile. -->
|
|
173
|
+
{#if drawerOpen}
|
|
174
|
+
<button
|
|
175
|
+
type="button"
|
|
176
|
+
class="backdrop"
|
|
177
|
+
aria-label="Close file picker"
|
|
178
|
+
onclick={() => (drawerOpen = false)}
|
|
179
|
+
></button>
|
|
180
|
+
{/if}
|
|
181
|
+
|
|
182
|
+
<section class="code-pane" aria-label="Code">
|
|
183
|
+
{#if activeFile}
|
|
184
|
+
<CodeBlock
|
|
185
|
+
code={activeFile.code}
|
|
186
|
+
language={activeFile.language}
|
|
187
|
+
filename={activeFileName}
|
|
188
|
+
allowCopy={showCopyButton}
|
|
189
|
+
>
|
|
190
|
+
{#snippet actions()}
|
|
191
|
+
{#if preview}
|
|
192
|
+
<button
|
|
193
|
+
type="button"
|
|
194
|
+
onclick={() => (previewExpanded = !previewExpanded)}
|
|
195
|
+
aria-expanded={previewExpanded}
|
|
196
|
+
title={previewExpanded ? 'Hide preview' : 'Show preview'}
|
|
197
|
+
>
|
|
198
|
+
<span class={previewExpanded ? icons.view.off : icons.view.preview} aria-hidden="true"></span>
|
|
199
|
+
<span>{previewExpanded ? 'Hide preview' : 'Show preview'}</span>
|
|
200
|
+
</button>
|
|
201
|
+
{/if}
|
|
202
|
+
{/snippet}
|
|
203
|
+
</CodeBlock>
|
|
204
|
+
{/if}
|
|
205
|
+
</section>
|
|
206
|
+
|
|
207
|
+
{#if preview && previewExpanded}
|
|
208
|
+
<section class="preview-pane" aria-label="Live preview">
|
|
209
|
+
<header class="preview-head">Preview</header>
|
|
210
|
+
<div class="preview-body">
|
|
211
|
+
{@render preview()}
|
|
212
|
+
</div>
|
|
213
|
+
</section>
|
|
214
|
+
{/if}
|
|
215
|
+
{/if}
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<style>
|
|
219
|
+
.code-group {
|
|
220
|
+
display: grid;
|
|
221
|
+
grid-template-columns: 240px 1fr;
|
|
222
|
+
grid-template-rows: auto 1fr;
|
|
223
|
+
grid-template-areas:
|
|
224
|
+
'rail code'
|
|
225
|
+
'rail preview';
|
|
226
|
+
gap: 0;
|
|
227
|
+
min-height: 280px;
|
|
228
|
+
border: 1px solid var(--paper-edge);
|
|
229
|
+
border-radius: 8px;
|
|
230
|
+
background: var(--paper);
|
|
231
|
+
overflow: hidden;
|
|
232
|
+
position: relative;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.code-group[data-preview-open] {
|
|
236
|
+
grid-template-rows: 1fr 1fr;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.empty {
|
|
240
|
+
grid-column: 1 / -1;
|
|
241
|
+
padding: 32px;
|
|
242
|
+
text-align: center;
|
|
243
|
+
color: var(--ink-soft);
|
|
244
|
+
font: 400 13px var(--font-ui);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* ─── Mobile top picker (hidden on desktop) ────────────────────────── */
|
|
248
|
+
.picker {
|
|
249
|
+
display: none;
|
|
250
|
+
align-items: center;
|
|
251
|
+
gap: 8px;
|
|
252
|
+
width: 100%;
|
|
253
|
+
padding: 10px 14px;
|
|
254
|
+
border: 0;
|
|
255
|
+
border-bottom: 1px solid var(--paper-edge);
|
|
256
|
+
background: var(--paper-soft);
|
|
257
|
+
color: var(--ink);
|
|
258
|
+
font: 500 13px var(--font-ui);
|
|
259
|
+
cursor: pointer;
|
|
260
|
+
text-align: left;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.picker-icon {
|
|
264
|
+
width: 16px;
|
|
265
|
+
height: 16px;
|
|
266
|
+
flex-shrink: 0;
|
|
267
|
+
color: var(--ink-mute);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.picker-name {
|
|
271
|
+
flex: 1;
|
|
272
|
+
overflow: hidden;
|
|
273
|
+
text-overflow: ellipsis;
|
|
274
|
+
white-space: nowrap;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.picker-chevron {
|
|
278
|
+
width: 16px;
|
|
279
|
+
height: 16px;
|
|
280
|
+
color: var(--ink-soft);
|
|
281
|
+
flex-shrink: 0;
|
|
282
|
+
transition: transform 160ms ease;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.picker[aria-expanded='true'] .picker-chevron {
|
|
286
|
+
transform: rotate(180deg);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/* ─── Tree rail ────────────────────────────────────────────────────── */
|
|
290
|
+
.rail {
|
|
291
|
+
grid-area: rail;
|
|
292
|
+
border-right: 1px solid var(--paper-edge);
|
|
293
|
+
background: var(--paper-soft);
|
|
294
|
+
padding: 12px 8px;
|
|
295
|
+
min-width: 0;
|
|
296
|
+
overflow-y: auto;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.rail-header {
|
|
300
|
+
padding: 4px 8px 10px;
|
|
301
|
+
font: 500 10px var(--font-mono);
|
|
302
|
+
color: var(--ink-soft);
|
|
303
|
+
letter-spacing: 0.16em;
|
|
304
|
+
text-transform: uppercase;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* ─── Code pane ───────────────────────────────────────────────────── */
|
|
308
|
+
.code-pane {
|
|
309
|
+
grid-area: code;
|
|
310
|
+
display: flex;
|
|
311
|
+
flex-direction: column;
|
|
312
|
+
min-width: 0;
|
|
313
|
+
min-height: 0;
|
|
314
|
+
overflow: hidden;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.code-pane :global([data-code-block]) {
|
|
318
|
+
flex: 1;
|
|
319
|
+
min-height: 0;
|
|
320
|
+
display: flex;
|
|
321
|
+
flex-direction: column;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/* ─── Preview pane ─────────────────────────────────────────────────── */
|
|
325
|
+
.preview-pane {
|
|
326
|
+
grid-area: preview;
|
|
327
|
+
display: flex;
|
|
328
|
+
flex-direction: column;
|
|
329
|
+
border-top: 1px solid var(--paper-edge);
|
|
330
|
+
background: var(--paper-soft);
|
|
331
|
+
min-height: 0;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.preview-head {
|
|
335
|
+
padding: 6px 14px;
|
|
336
|
+
font: 500 10px var(--font-mono);
|
|
337
|
+
color: var(--ink-soft);
|
|
338
|
+
letter-spacing: 0.16em;
|
|
339
|
+
text-transform: uppercase;
|
|
340
|
+
border-bottom: 1px solid var(--paper-edge);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.preview-body {
|
|
344
|
+
flex: 1;
|
|
345
|
+
padding: 16px;
|
|
346
|
+
overflow: auto;
|
|
347
|
+
min-height: 0;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* ─── Backdrop (mobile drawer-open state) ─────────────────────────── */
|
|
351
|
+
.backdrop {
|
|
352
|
+
display: none;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/* ─── Mobile (< 768px) ────────────────────────────────────────────── */
|
|
356
|
+
@media (max-width: 767px) {
|
|
357
|
+
.code-group {
|
|
358
|
+
grid-template-columns: 1fr;
|
|
359
|
+
grid-template-rows: auto 1fr;
|
|
360
|
+
grid-template-areas:
|
|
361
|
+
'picker'
|
|
362
|
+
'code';
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.code-group[data-preview-open] {
|
|
366
|
+
grid-template-rows: auto 1fr 1fr;
|
|
367
|
+
grid-template-areas:
|
|
368
|
+
'picker'
|
|
369
|
+
'code'
|
|
370
|
+
'preview';
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.picker {
|
|
374
|
+
display: inline-flex;
|
|
375
|
+
grid-area: picker;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.rail {
|
|
379
|
+
position: absolute;
|
|
380
|
+
top: 41px;
|
|
381
|
+
left: 0;
|
|
382
|
+
bottom: 0;
|
|
383
|
+
width: min(280px, 100%);
|
|
384
|
+
z-index: 10;
|
|
385
|
+
border-right: 1px solid var(--paper-edge);
|
|
386
|
+
box-shadow: 0 8px 28px -8px color-mix(in oklab, var(--ink) 18%, transparent);
|
|
387
|
+
transform: translateX(-100%);
|
|
388
|
+
transition: transform 200ms ease;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.rail[data-open] {
|
|
392
|
+
transform: translateX(0);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.backdrop {
|
|
396
|
+
display: block;
|
|
397
|
+
position: absolute;
|
|
398
|
+
top: 41px;
|
|
399
|
+
left: 0;
|
|
400
|
+
right: 0;
|
|
401
|
+
bottom: 0;
|
|
402
|
+
background: color-mix(in oklab, var(--ink) 28%, transparent);
|
|
403
|
+
border: 0;
|
|
404
|
+
cursor: pointer;
|
|
405
|
+
z-index: 9;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
</style>
|
|
@@ -9,15 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { ConnectorType } from '../types/tree.js'
|
|
12
|
+
import type { ConnectorProps } from '../types/connector.js'
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
/** The type of connector to render */
|
|
15
|
-
type?: ConnectorType
|
|
16
|
-
/** Right-to-left layout support */
|
|
17
|
-
rtl?: boolean
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const { type = 'empty', rtl = false }: Props = $props()
|
|
14
|
+
const { type = 'empty', rtl = false }: ConnectorProps = $props()
|
|
21
15
|
|
|
22
16
|
const validTypes: ConnectorType[] = ['last', 'child', 'sibling', 'empty']
|
|
23
17
|
const validatedType = $derived(validTypes.includes(type) ? type : 'empty')
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
/** Orientation of the divider */
|
|
4
|
-
orientation?: 'horizontal' | 'vertical'
|
|
5
|
-
/** Optional label text displayed in the center */
|
|
6
|
-
label?: string
|
|
7
|
-
/** Additional CSS class */
|
|
8
|
-
class?: string
|
|
9
|
-
}
|
|
2
|
+
import type { DividerProps } from '../types/divider.js'
|
|
10
3
|
|
|
11
4
|
const { orientation = 'horizontal', label, class: className = '' }: DividerProps = $props()
|
|
12
5
|
</script>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Frame — generic header / body / footer container.
|
|
4
|
+
*
|
|
5
|
+
* The canonical "card-shaped UI block" primitive. Hands the structure
|
|
6
|
+
* to themes via `[data-frame]`, `[data-frame-header]`,
|
|
7
|
+
* `[data-frame-body]`, `[data-frame-footer]`; consumers decide what
|
|
8
|
+
* goes inside each slot.
|
|
9
|
+
*
|
|
10
|
+
* Used by PlotPlugin to wrap a chart, by CodeBlock to wrap a code
|
|
11
|
+
* block's chrome, and anywhere else a header/body/footer card is
|
|
12
|
+
* the right pattern. For an interactive card (link or button), use
|
|
13
|
+
* `<Card/>` instead.
|
|
14
|
+
*/
|
|
15
|
+
import type { FrameProps } from '../types/frame.js'
|
|
16
|
+
|
|
17
|
+
const { header, children, footer, flush = false }: FrameProps = $props()
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<div data-frame>
|
|
21
|
+
{#if header}
|
|
22
|
+
<div data-frame-header>{@render header()}</div>
|
|
23
|
+
{/if}
|
|
24
|
+
{#if children}
|
|
25
|
+
<div data-frame-body data-flush={flush || undefined}>{@render children()}</div>
|
|
26
|
+
{/if}
|
|
27
|
+
{#if footer}
|
|
28
|
+
<div data-frame-footer>{@render footer()}</div>
|
|
29
|
+
{/if}
|
|
30
|
+
</div>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ItemContentProps } from '../types/item-content.js'
|
|
2
3
|
import { isIconClass } from '@rokkit/core'
|
|
3
|
-
|
|
4
|
+
|
|
5
|
+
const { proxy, showIcon = true, showSubtext = true }: ItemContentProps = $props()
|
|
4
6
|
</script>
|
|
5
7
|
|
|
6
8
|
{#if showIcon}
|
|
@@ -1,27 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte'
|
|
3
2
|
import { DEFAULT_STATE_ICONS } from '@rokkit/core'
|
|
4
|
-
|
|
5
|
-
interface MessageProps {
|
|
6
|
-
/** Alert type — controls color and icon */
|
|
7
|
-
type?: 'error' | 'info' | 'success' | 'warning'
|
|
8
|
-
/** Icon class overrides per type */
|
|
9
|
-
icons?: { error?: string; info?: string; success?: string; warning?: string }
|
|
10
|
-
/** Text content (shorthand; children takes precedence) */
|
|
11
|
-
text?: string
|
|
12
|
-
/** Show dismiss button */
|
|
13
|
-
dismissible?: boolean
|
|
14
|
-
/** Auto-dismiss after N ms. Defaults to 4000 unless dismissible (persistent). Pass 0 to disable. */
|
|
15
|
-
timeout?: number
|
|
16
|
-
/** Optional action buttons snippet */
|
|
17
|
-
actions?: Snippet
|
|
18
|
-
/** Rich content (takes precedence over text) */
|
|
19
|
-
children?: Snippet
|
|
20
|
-
/** Called when dismissed (button click or timeout) */
|
|
21
|
-
ondismiss?: () => void
|
|
22
|
-
/** Additional CSS class */
|
|
23
|
-
class?: string
|
|
24
|
-
}
|
|
3
|
+
import type { MessageProps } from '../types/message.js'
|
|
25
4
|
|
|
26
5
|
const {
|
|
27
6
|
type = 'error',
|
|
@@ -1,20 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
interface NavContentProps {
|
|
5
|
-
/** Layout orientation */
|
|
6
|
-
orientation?: 'horizontal' | 'vertical'
|
|
7
|
-
/** Nav panel width (horizontal) or height (vertical) */
|
|
8
|
-
navSize?: string
|
|
9
|
-
/** Collapse nav on small screens */
|
|
10
|
-
collapsible?: boolean
|
|
11
|
-
/** Nav panel snippet */
|
|
12
|
-
nav: Snippet
|
|
13
|
-
/** Content panel snippet */
|
|
14
|
-
content: Snippet
|
|
15
|
-
/** Additional CSS class */
|
|
16
|
-
class?: string
|
|
17
|
-
}
|
|
2
|
+
import type { NavContentProps } from '../types/nav-content.js'
|
|
18
3
|
|
|
19
4
|
const {
|
|
20
5
|
orientation = 'horizontal',
|
|
@@ -1,31 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte'
|
|
3
2
|
import { ProxyItem } from '@rokkit/states'
|
|
4
3
|
import { keyboard } from '@rokkit/actions'
|
|
5
4
|
import { DEFAULT_STATE_ICONS } from '@rokkit/core'
|
|
6
|
-
|
|
7
|
-
interface PillIcons {
|
|
8
|
-
remove?: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface PillProps {
|
|
12
|
-
/** Item data (string or object) */
|
|
13
|
-
value: unknown
|
|
14
|
-
/** Custom field mappings */
|
|
15
|
-
fields?: Record<string, string>
|
|
16
|
-
/** Show remove button */
|
|
17
|
-
removable?: boolean
|
|
18
|
-
/** Disabled state */
|
|
19
|
-
disabled?: boolean
|
|
20
|
-
/** Custom icons */
|
|
21
|
-
icons?: PillIcons
|
|
22
|
-
/** Called when remove is triggered (click or Delete/Backspace key) */
|
|
23
|
-
onremove?: (value: unknown) => void
|
|
24
|
-
/** Custom content snippet */
|
|
25
|
-
content?: Snippet<[ProxyItem]>
|
|
26
|
-
/** Additional CSS class */
|
|
27
|
-
class?: string
|
|
28
|
-
}
|
|
5
|
+
import type { PillIcons, PillProps } from '../types/pill.js'
|
|
29
6
|
|
|
30
7
|
const {
|
|
31
8
|
value,
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
/** Current progress value (null = indeterminate) */
|
|
4
|
-
value?: number | null
|
|
5
|
-
/** Maximum value (default: 100) */
|
|
6
|
-
max?: number
|
|
7
|
-
/** Additional CSS class */
|
|
8
|
-
class?: string
|
|
9
|
-
}
|
|
2
|
+
import type { ProgressBarProps } from '../types/progress-bar.js'
|
|
10
3
|
|
|
11
4
|
const { value = null, max = 100, class: className = '' }: ProgressBarProps = $props()
|
|
12
5
|
|
|
@@ -1,27 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { DEFAULT_STATE_ICONS } from '@rokkit/core'
|
|
3
3
|
import { messages } from '@rokkit/states'
|
|
4
|
-
|
|
5
|
-
interface RatingIcons {
|
|
6
|
-
filled?: string
|
|
7
|
-
empty?: string
|
|
8
|
-
half?: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface RatingProps {
|
|
12
|
-
/** Current rating value (bindable) */
|
|
13
|
-
value?: number
|
|
14
|
-
/** Maximum number of stars (default: 5) */
|
|
15
|
-
max?: number
|
|
16
|
-
/** Disabled state */
|
|
17
|
-
disabled?: boolean
|
|
18
|
-
/** Custom icons for rating states */
|
|
19
|
-
icons?: RatingIcons
|
|
20
|
-
/** Called when value changes */
|
|
21
|
-
onchange?: (value: number) => void
|
|
22
|
-
/** Additional CSS class */
|
|
23
|
-
class?: string
|
|
24
|
-
}
|
|
4
|
+
import type { RatingIcons, RatingProps } from '../types/rating.js'
|
|
25
5
|
|
|
26
6
|
let {
|
|
27
7
|
value = $bindable(0),
|
|
@@ -31,7 +11,7 @@
|
|
|
31
11
|
icons: userIcons = {} as RatingIcons,
|
|
32
12
|
onchange,
|
|
33
13
|
class: className = ''
|
|
34
|
-
}: RatingProps
|
|
14
|
+
}: RatingProps = $props()
|
|
35
15
|
|
|
36
16
|
const icons = $derived({ ...DEFAULT_STATE_ICONS.rating, ...userIcons })
|
|
37
17
|
|
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
interface ResponsiveGridProps {
|
|
5
|
-
/** Minimum column width (CSS value) */
|
|
6
|
-
minWidth?: string
|
|
7
|
-
/** Gap between cells (CSS value) */
|
|
8
|
-
gap?: string
|
|
9
|
-
/** Maximum number of columns */
|
|
10
|
-
maxCols?: number
|
|
11
|
-
/** Grid children */
|
|
12
|
-
children: Snippet
|
|
13
|
-
/** Additional CSS class */
|
|
14
|
-
class?: string
|
|
15
|
-
}
|
|
2
|
+
import type { ResponsiveGridProps } from '../types/responsive-grid.js'
|
|
16
3
|
|
|
17
4
|
const {
|
|
18
5
|
minWidth = '240px',
|
|
@@ -1,28 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte'
|
|
3
2
|
import { reveal } from '@rokkit/actions'
|
|
4
|
-
|
|
5
|
-
interface RevealProps {
|
|
6
|
-
/** Slide direction (default: 'up') */
|
|
7
|
-
direction?: 'up' | 'down' | 'left' | 'right' | 'none'
|
|
8
|
-
/** Slide distance CSS value (default: '1.5rem') */
|
|
9
|
-
distance?: string
|
|
10
|
-
/** Transition duration in ms (default: 600) */
|
|
11
|
-
duration?: number
|
|
12
|
-
/** Delay before animation starts in ms (default: 0) */
|
|
13
|
-
delay?: number
|
|
14
|
-
/** Delay increment per direct child in ms for stagger (default: 0) */
|
|
15
|
-
stagger?: number
|
|
16
|
-
/** Only animate once (default: true) */
|
|
17
|
-
once?: boolean
|
|
18
|
-
/** IntersectionObserver threshold 0–1 (default: 0.1) */
|
|
19
|
-
threshold?: number
|
|
20
|
-
/** CSS easing function */
|
|
21
|
-
easing?: string
|
|
22
|
-
/** Additional CSS class */
|
|
23
|
-
class?: string
|
|
24
|
-
children?: Snippet
|
|
25
|
-
}
|
|
3
|
+
import type { RevealProps } from '../types/reveal.js'
|
|
26
4
|
|
|
27
5
|
const {
|
|
28
6
|
direction = 'up',
|