@rokkit/ui 1.0.5 → 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.
Files changed (59) hide show
  1. package/package.json +7 -6
  2. package/src/MarkdownRenderer.svelte +13 -1
  3. package/src/components/AlertList.svelte +1 -13
  4. package/src/components/Avatar.svelte +1 -18
  5. package/src/components/Badge.svelte +1 -16
  6. package/src/components/BreadCrumbs.svelte +2 -21
  7. package/src/components/Card.svelte +1 -18
  8. package/src/components/Carousel.svelte +2 -27
  9. package/src/components/CodeBlock.svelte +130 -0
  10. package/src/components/CodeGroup.svelte +408 -0
  11. package/src/components/Connector.svelte +2 -8
  12. package/src/components/Divider.svelte +1 -8
  13. package/src/components/Frame.svelte +30 -0
  14. package/src/components/ItemContent.svelte +4 -2
  15. package/src/components/Message.svelte +1 -22
  16. package/src/components/NavContent.svelte +1 -16
  17. package/src/components/Pill.svelte +1 -24
  18. package/src/components/ProgressBar.svelte +1 -8
  19. package/src/components/Rating.svelte +2 -22
  20. package/src/components/ResponsiveGrid.svelte +1 -14
  21. package/src/components/Reveal.svelte +1 -23
  22. package/src/components/Select.svelte +90 -9
  23. package/src/components/Shine.svelte +1 -19
  24. package/src/components/Stack.svelte +1 -16
  25. package/src/components/StatusList.svelte +3 -2
  26. package/src/components/Stepper.svelte +2 -41
  27. package/src/components/Tilt.svelte +1 -13
  28. package/src/components/Toggle.svelte +83 -33
  29. package/src/components/index.ts +3 -0
  30. package/src/index.ts +4 -0
  31. package/src/types/alert-list.ts +28 -0
  32. package/src/types/avatar.ts +41 -0
  33. package/src/types/badge.ts +37 -0
  34. package/src/types/breadcrumbs.ts +43 -0
  35. package/src/types/card.ts +38 -0
  36. package/src/types/carousel.ts +49 -0
  37. package/src/types/code.ts +102 -1
  38. package/src/types/connector.ts +22 -0
  39. package/src/types/divider.ts +20 -0
  40. package/src/types/frame.ts +29 -0
  41. package/src/types/index.ts +66 -0
  42. package/src/types/item-content.ts +26 -0
  43. package/src/types/message.ts +54 -0
  44. package/src/types/nav-content.ts +31 -0
  45. package/src/types/pill.ts +43 -0
  46. package/src/types/progress-bar.ts +19 -0
  47. package/src/types/rating.ts +43 -0
  48. package/src/types/responsive-grid.ts +25 -0
  49. package/src/types/reveal.ts +49 -0
  50. package/src/types/shine.ts +37 -0
  51. package/src/types/stack.ts +40 -0
  52. package/src/types/status-list.ts +42 -0
  53. package/src/types/stepper.ts +74 -0
  54. package/src/types/tilt.ts +28 -0
  55. package/src/types/toggle.ts +12 -0
  56. package/src/types/tree.ts +46 -4
  57. package/src/utils/palette.ts +4 -2
  58. package/src/utils/shiki.ts +22 -1
  59. 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
- interface Props {
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
- interface DividerProps {
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
- const { proxy, showIcon = true, showSubtext = true } = $props()
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 { Snippet } from 'svelte'
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
- interface ProgressBarProps {
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 & { label?: string } = $props()
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 { Snippet } from 'svelte'
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',