@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.
- 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
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
filterPlaceholder = 'Search...',
|
|
64
64
|
align = 'start',
|
|
65
65
|
direction = 'down',
|
|
66
|
+
maxRows = 8,
|
|
66
67
|
icons: userIcons = {} as SelectIcons,
|
|
67
68
|
onchange,
|
|
68
69
|
class: className = '',
|
|
@@ -79,6 +80,7 @@
|
|
|
79
80
|
filterPlaceholder?: string
|
|
80
81
|
align?: 'start' | 'end'
|
|
81
82
|
direction?: 'up' | 'down'
|
|
83
|
+
maxRows?: number
|
|
82
84
|
icons?: SelectIcons
|
|
83
85
|
onchange?: (value: unknown, item: unknown) => void
|
|
84
86
|
class?: string
|
|
@@ -216,9 +218,29 @@
|
|
|
216
218
|
requestAnimationFrame(() => {
|
|
217
219
|
if (filterable) {
|
|
218
220
|
filterInputRef?.focus()
|
|
219
|
-
|
|
220
|
-
focusSelectedOrFirst()
|
|
221
|
+
return
|
|
221
222
|
}
|
|
223
|
+
focusSelectedOrFirst()
|
|
224
|
+
// Move DOM focus to the wrapper's focused item so subsequent
|
|
225
|
+
// keydown events fire inside the dropdown (Navigator is
|
|
226
|
+
// listening there). preventScroll + dropdown-only scroll
|
|
227
|
+
// to avoid moving ancestor containers.
|
|
228
|
+
requestAnimationFrame(() => {
|
|
229
|
+
if (!dropdownRef) return
|
|
230
|
+
const key = wrapper.focusedKey
|
|
231
|
+
if (!key) return
|
|
232
|
+
const el = dropdownRef.querySelector(`[data-path="${key}"]`) as HTMLElement | null
|
|
233
|
+
if (el) {
|
|
234
|
+
el.focus({ preventScroll: true })
|
|
235
|
+
const top = el.offsetTop
|
|
236
|
+
const bottom = top + el.offsetHeight
|
|
237
|
+
if (top < dropdownRef.scrollTop) {
|
|
238
|
+
dropdownRef.scrollTop = top
|
|
239
|
+
} else if (bottom > dropdownRef.scrollTop + dropdownRef.clientHeight) {
|
|
240
|
+
dropdownRef.scrollTop = bottom - dropdownRef.clientHeight
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
})
|
|
222
244
|
})
|
|
223
245
|
},
|
|
224
246
|
onclose: () => {
|
|
@@ -254,19 +276,78 @@
|
|
|
254
276
|
return () => nav.destroy()
|
|
255
277
|
})
|
|
256
278
|
|
|
257
|
-
//
|
|
279
|
+
// Apply maxRows by measuring a real option after open and capping max-height.
|
|
280
|
+
// Runs once per open to avoid jitter; the CSS var still wins if the consumer
|
|
281
|
+
// sets --select-dropdown-max-height explicitly.
|
|
258
282
|
$effect(() => {
|
|
259
|
-
|
|
260
|
-
if (!isOpen || !dropdownRef || !key) return
|
|
283
|
+
if (!isOpen || !dropdownRef) return
|
|
261
284
|
requestAnimationFrame(() => {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
285
|
+
if (!dropdownRef) return
|
|
286
|
+
const firstOption = dropdownRef.querySelector('[data-select-option]') as HTMLElement | null
|
|
287
|
+
if (!firstOption) return
|
|
288
|
+
const itemH = firstOption.offsetHeight
|
|
289
|
+
if (itemH > 0) {
|
|
290
|
+
dropdownRef.style.setProperty(
|
|
291
|
+
'--select-dropdown-max-height',
|
|
292
|
+
`${maxRows * itemH}px`
|
|
293
|
+
)
|
|
266
294
|
}
|
|
267
295
|
})
|
|
268
296
|
})
|
|
269
297
|
|
|
298
|
+
// Position the dropdown relative to the trigger using `position: fixed`
|
|
299
|
+
// so it escapes any ancestor `overflow: auto/hidden` clipping (common
|
|
300
|
+
// when Select lives inside a scrollable card/modal/canvas). Reposition
|
|
301
|
+
// on resize and ancestor scroll; close on the same so it doesn't drift
|
|
302
|
+
// away from the trigger.
|
|
303
|
+
function positionDropdown() {
|
|
304
|
+
if (!dropdownRef || !triggerRef) return
|
|
305
|
+
const r = triggerRef.getBoundingClientRect()
|
|
306
|
+
const gap = 4
|
|
307
|
+
dropdownRef.style.position = 'fixed'
|
|
308
|
+
dropdownRef.style.minWidth = `${r.width}px`
|
|
309
|
+
if (direction === 'up') {
|
|
310
|
+
dropdownRef.style.top = 'auto'
|
|
311
|
+
dropdownRef.style.bottom = `${window.innerHeight - r.top + gap}px`
|
|
312
|
+
} else {
|
|
313
|
+
dropdownRef.style.top = `${r.bottom + gap}px`
|
|
314
|
+
dropdownRef.style.bottom = 'auto'
|
|
315
|
+
}
|
|
316
|
+
if (align === 'end') {
|
|
317
|
+
dropdownRef.style.left = 'auto'
|
|
318
|
+
dropdownRef.style.right = `${window.innerWidth - r.right}px`
|
|
319
|
+
} else {
|
|
320
|
+
dropdownRef.style.left = `${r.left}px`
|
|
321
|
+
dropdownRef.style.right = 'auto'
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
$effect(() => {
|
|
326
|
+
if (!isOpen || !dropdownRef || !triggerRef) return
|
|
327
|
+
positionDropdown()
|
|
328
|
+
const onResize = () => positionDropdown()
|
|
329
|
+
const onScroll = (e: Event) => {
|
|
330
|
+
// Don't close on the dropdown's OWN internal scroll (mouse wheel
|
|
331
|
+
// inside the list, or scrollItemIntoView from keyboard nav).
|
|
332
|
+
// Only close when an ANCESTOR / outer container scrolls — at that
|
|
333
|
+
// point the trigger has moved and the fixed-position dropdown
|
|
334
|
+
// would drift away from it.
|
|
335
|
+
if (dropdownRef && e.target instanceof Node && dropdownRef.contains(e.target)) return
|
|
336
|
+
isOpen = false
|
|
337
|
+
}
|
|
338
|
+
window.addEventListener('resize', onResize)
|
|
339
|
+
window.addEventListener('scroll', onScroll, true)
|
|
340
|
+
return () => {
|
|
341
|
+
window.removeEventListener('resize', onResize)
|
|
342
|
+
window.removeEventListener('scroll', onScroll, true)
|
|
343
|
+
}
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
// Focus sync is owned by Navigator (#syncFocus). The previous duplicate
|
|
347
|
+
// $effect here competed with Navigator's own focus + scroll logic and
|
|
348
|
+
// caused race conditions (page-scroll on arrow nav, layout shift on
|
|
349
|
+
// reopen). Single source of truth lives in the Navigator class.
|
|
350
|
+
|
|
270
351
|
// ─── Filter keyboard (native listener, fires before Navigator) ───────────
|
|
271
352
|
|
|
272
353
|
function selectFocused() {
|
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte'
|
|
3
2
|
import { id } from '@rokkit/core'
|
|
4
|
-
|
|
5
|
-
interface ShineProps {
|
|
6
|
-
/** Light color (default: 'rgb(var(--primary-500))') */
|
|
7
|
-
color?: string
|
|
8
|
-
/** Light source distance/height — controls spread (default: 300) */
|
|
9
|
-
radius?: number
|
|
10
|
-
/** Gaussian blur depth (default: 1) */
|
|
11
|
-
depth?: number
|
|
12
|
-
/** Height of the surface for the light filter (default: 2) */
|
|
13
|
-
surfaceScale?: number
|
|
14
|
-
/** The bigger the value the bigger the reflection (default: 0.75) */
|
|
15
|
-
specularConstant?: number
|
|
16
|
-
/** Controls focus for the light source — bigger = brighter (default: 120) */
|
|
17
|
-
specularExponent?: number
|
|
18
|
-
/** Additional CSS class */
|
|
19
|
-
class?: string
|
|
20
|
-
children?: Snippet
|
|
21
|
-
}
|
|
3
|
+
import type { ShineProps } from '../types/shine.js'
|
|
22
4
|
|
|
23
5
|
const {
|
|
24
6
|
color = 'rgb(var(--primary-500))',
|
|
@@ -1,20 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
interface StackProps {
|
|
5
|
-
/** Layout direction */
|
|
6
|
-
direction?: 'vertical' | 'horizontal'
|
|
7
|
-
/** Gap size between children */
|
|
8
|
-
gap?: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
|
9
|
-
/** Cross-axis alignment */
|
|
10
|
-
align?: 'start' | 'center' | 'end' | 'stretch'
|
|
11
|
-
/** Main-axis justification */
|
|
12
|
-
justify?: 'start' | 'center' | 'end' | 'between' | 'around'
|
|
13
|
-
/** Stack children */
|
|
14
|
-
children: Snippet
|
|
15
|
-
/** Additional CSS class */
|
|
16
|
-
class?: string
|
|
17
|
-
}
|
|
2
|
+
import type { StackProps } from '../types/stack.js'
|
|
18
3
|
|
|
19
4
|
const {
|
|
20
5
|
direction = 'vertical',
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
2
|
import { DEFAULT_STATE_ICONS } from '@rokkit/core'
|
|
3
|
+
import type { StatusListProps } from '../types/status-list.js'
|
|
3
4
|
|
|
4
5
|
const DEFAULT_ICONS = DEFAULT_STATE_ICONS.badge
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
const { class: className = '', items, icons = {} }: StatusListProps = $props()
|
|
7
8
|
|
|
8
9
|
const resolvedIcons = $derived({ ...DEFAULT_ICONS, ...icons })
|
|
9
10
|
</script>
|
|
@@ -1,46 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte'
|
|
3
2
|
import { DEFAULT_STATE_ICONS } from '@rokkit/core'
|
|
4
3
|
import { messages } from '@rokkit/states'
|
|
5
|
-
|
|
6
|
-
interface StepperStep {
|
|
7
|
-
/** Step label (shown below circle) */
|
|
8
|
-
text: string
|
|
9
|
-
/** Short text inside circle (default: step number) */
|
|
10
|
-
label?: string
|
|
11
|
-
/** Step is finished (shows checkmark) */
|
|
12
|
-
completed?: boolean
|
|
13
|
-
/** Step cannot be navigated to */
|
|
14
|
-
disabled?: boolean
|
|
15
|
-
/** Number of sub-stages within this step (default: 1) */
|
|
16
|
-
stages?: number
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface StepperIcons {
|
|
20
|
-
/** Icon class for check/completed state */
|
|
21
|
-
check?: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface StepperProps {
|
|
25
|
-
/** Array of step definitions */
|
|
26
|
-
steps?: StepperStep[]
|
|
27
|
-
/** Current step index (bindable) */
|
|
28
|
-
current?: number
|
|
29
|
-
/** Current sub-stage within step (bindable, 0-based) */
|
|
30
|
-
currentStage?: number
|
|
31
|
-
/** Only allow clicking completed steps + first incomplete */
|
|
32
|
-
linear?: boolean
|
|
33
|
-
/** Layout orientation */
|
|
34
|
-
orientation?: 'horizontal' | 'vertical'
|
|
35
|
-
/** Custom icons */
|
|
36
|
-
icons?: StepperIcons
|
|
37
|
-
/** Callback when a step or dot is clicked */
|
|
38
|
-
onclick?: (step: number, stage?: number) => void
|
|
39
|
-
/** Content snippet rendered below the stepper */
|
|
40
|
-
content?: Snippet<[StepperStep, number]>
|
|
41
|
-
/** Additional CSS class */
|
|
42
|
-
class?: string
|
|
43
|
-
}
|
|
4
|
+
import type { StepperIcons, StepperProps } from '../types/stepper.js'
|
|
44
5
|
|
|
45
6
|
const defaultIcons: StepperIcons = {
|
|
46
7
|
check: DEFAULT_STATE_ICONS.action.check
|
|
@@ -57,7 +18,7 @@
|
|
|
57
18
|
onclick,
|
|
58
19
|
content,
|
|
59
20
|
class: className = ''
|
|
60
|
-
}: StepperProps
|
|
21
|
+
}: StepperProps = $props()
|
|
61
22
|
|
|
62
23
|
const icons = $derived<StepperIcons>({ ...defaultIcons, ...userIcons })
|
|
63
24
|
|
|
@@ -1,17 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
interface TiltProps {
|
|
5
|
-
/** Maximum rotation angle in degrees (default: 10) */
|
|
6
|
-
maxRotation?: number
|
|
7
|
-
/** Whether to adjust brightness based on mouse Y position */
|
|
8
|
-
setBrightness?: boolean
|
|
9
|
-
/** CSS perspective value in pixels (default: 600) */
|
|
10
|
-
perspective?: number
|
|
11
|
-
/** Additional CSS class */
|
|
12
|
-
class?: string
|
|
13
|
-
children?: Snippet
|
|
14
|
-
}
|
|
2
|
+
import type { TiltProps } from '../types/tilt.js'
|
|
15
3
|
|
|
16
4
|
const {
|
|
17
5
|
maxRotation = 10,
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { resolveSnippet } from '@rokkit/core'
|
|
7
7
|
|
|
8
8
|
let {
|
|
9
|
+
variant = 'group',
|
|
9
10
|
options = [],
|
|
10
11
|
fields: userFields = {},
|
|
11
12
|
value = $bindable(),
|
|
@@ -23,12 +24,12 @@
|
|
|
23
24
|
const proxyTree = $derived(new ProxyTree(options, userFields))
|
|
24
25
|
const wrapper = $derived(new Wrapper(proxyTree, { onselect: handleSelect }))
|
|
25
26
|
|
|
26
|
-
// ─── Navigator (horizontal
|
|
27
|
+
// ─── Navigator (horizontal — only for group variant) ───────────────────────
|
|
27
28
|
|
|
28
29
|
let containerRef = $state<HTMLElement | null>(null)
|
|
29
30
|
|
|
30
31
|
$effect(() => {
|
|
31
|
-
if (!containerRef) return
|
|
32
|
+
if (!containerRef || variant !== 'group') return
|
|
32
33
|
const dir = getComputedStyle(containerRef).direction
|
|
33
34
|
const nav = new Navigator(containerRef, wrapper, { orientation: 'horizontal', dir })
|
|
34
35
|
return () => nav.destroy()
|
|
@@ -49,37 +50,44 @@
|
|
|
49
50
|
onchange?.(extractedValue, proxy.original)
|
|
50
51
|
}
|
|
51
52
|
}
|
|
53
|
+
|
|
54
|
+
// ─── Button variant — derive the next option in the cycle ─────────────────
|
|
55
|
+
// The single button represents the NEXT value (what you'd switch to on click).
|
|
56
|
+
// We compute the index of the current value, then point at index+1 (mod len).
|
|
57
|
+
|
|
58
|
+
const nodes = $derived(wrapper.flatView)
|
|
59
|
+
const currentIndex = $derived(nodes.findIndex((n) => n.proxy.value === value))
|
|
60
|
+
const nextIndex = $derived(
|
|
61
|
+
nodes.length === 0 ? -1 : (currentIndex >= 0 ? (currentIndex + 1) % nodes.length : 0)
|
|
62
|
+
)
|
|
63
|
+
const nextNode = $derived(nextIndex >= 0 ? nodes[nextIndex] : null)
|
|
64
|
+
|
|
65
|
+
function cycle() {
|
|
66
|
+
if (disabled || !nextNode) return
|
|
67
|
+
const proxy = nextNode.proxy
|
|
68
|
+
handleSelect(proxy.value, proxy)
|
|
69
|
+
}
|
|
52
70
|
</script>
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
{
|
|
67
|
-
{
|
|
68
|
-
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
data-toggle-option
|
|
72
|
-
data-path={node.key}
|
|
73
|
-
data-selected={sel || undefined}
|
|
74
|
-
data-disabled={proxy.disabled || undefined}
|
|
75
|
-
role="radio"
|
|
76
|
-
aria-checked={sel}
|
|
77
|
-
aria-label={proxy.label}
|
|
78
|
-
title={proxy.get('description') || proxy.label}
|
|
79
|
-
disabled={proxy.disabled || disabled}
|
|
80
|
-
>
|
|
72
|
+
{#if variant === 'button'}
|
|
73
|
+
{@const proxy = nextNode?.proxy}
|
|
74
|
+
{@const content = proxy ? resolveSnippet(snippets, proxy) : null}
|
|
75
|
+
<button
|
|
76
|
+
type="button"
|
|
77
|
+
data-toggle
|
|
78
|
+
data-toggle-variant="button"
|
|
79
|
+
data-toggle-size={size}
|
|
80
|
+
data-toggle-disabled={disabled || undefined}
|
|
81
|
+
data-toggle-labels={showLabels || undefined}
|
|
82
|
+
class={className || undefined}
|
|
83
|
+
title={proxy?.get('description') || proxy?.label || label}
|
|
84
|
+
aria-label={proxy?.label || label}
|
|
85
|
+
disabled={disabled || !proxy}
|
|
86
|
+
onclick={cycle}
|
|
87
|
+
>
|
|
88
|
+
{#if proxy}
|
|
81
89
|
{#if content}
|
|
82
|
-
{@render content(proxy,
|
|
90
|
+
{@render content(proxy, false)}
|
|
83
91
|
{:else}
|
|
84
92
|
{#if proxy.get('icon')}
|
|
85
93
|
<span data-toggle-icon class={proxy.get('icon')} aria-hidden="true"></span>
|
|
@@ -88,6 +96,48 @@
|
|
|
88
96
|
<span data-toggle-label>{proxy.label}</span>
|
|
89
97
|
{/if}
|
|
90
98
|
{/if}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
{/if}
|
|
100
|
+
</button>
|
|
101
|
+
{:else}
|
|
102
|
+
<div
|
|
103
|
+
bind:this={containerRef}
|
|
104
|
+
data-toggle
|
|
105
|
+
data-toggle-variant="group"
|
|
106
|
+
data-toggle-size={size}
|
|
107
|
+
data-toggle-disabled={disabled || undefined}
|
|
108
|
+
data-toggle-labels={showLabels || undefined}
|
|
109
|
+
class={className || undefined}
|
|
110
|
+
role="radiogroup"
|
|
111
|
+
aria-label={label}
|
|
112
|
+
aria-disabled={disabled || undefined}
|
|
113
|
+
>
|
|
114
|
+
{#each wrapper.flatView as node (node.key)}
|
|
115
|
+
{@const proxy = node.proxy}
|
|
116
|
+
{@const sel = proxy.value === value}
|
|
117
|
+
{@const content = resolveSnippet(snippets, proxy)}
|
|
118
|
+
<button
|
|
119
|
+
type="button"
|
|
120
|
+
data-toggle-option
|
|
121
|
+
data-path={node.key}
|
|
122
|
+
data-selected={sel || undefined}
|
|
123
|
+
data-disabled={proxy.disabled || undefined}
|
|
124
|
+
role="radio"
|
|
125
|
+
aria-checked={sel}
|
|
126
|
+
aria-label={proxy.label}
|
|
127
|
+
title={proxy.get('description') || proxy.label}
|
|
128
|
+
disabled={proxy.disabled || disabled}
|
|
129
|
+
>
|
|
130
|
+
{#if content}
|
|
131
|
+
{@render content(proxy, sel)}
|
|
132
|
+
{:else}
|
|
133
|
+
{#if proxy.get('icon')}
|
|
134
|
+
<span data-toggle-icon class={proxy.get('icon')} aria-hidden="true"></span>
|
|
135
|
+
{/if}
|
|
136
|
+
{#if showLabels && proxy.label}
|
|
137
|
+
<span data-toggle-label>{proxy.label}</span>
|
|
138
|
+
{/if}
|
|
139
|
+
{/if}
|
|
140
|
+
</button>
|
|
141
|
+
{/each}
|
|
142
|
+
</div>
|
|
143
|
+
{/if}
|
package/src/components/index.ts
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
export { default as Button } from './Button.svelte'
|
|
3
3
|
export { default as ButtonGroup } from './ButtonGroup.svelte'
|
|
4
4
|
export { default as Code } from './Code.svelte'
|
|
5
|
+
export { default as CodeBlock } from './CodeBlock.svelte'
|
|
6
|
+
export { default as CodeGroup } from './CodeGroup.svelte'
|
|
7
|
+
export { default as Frame } from './Frame.svelte'
|
|
5
8
|
export { default as Menu } from './Menu.svelte'
|
|
6
9
|
export { default as Dropdown } from './Dropdown.svelte'
|
|
7
10
|
export { default as Select } from './Select.svelte'
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AlertList Component Types
|
|
3
|
+
*
|
|
4
|
+
* Renders a portalled toast stack at a screen-edge position, reading from
|
|
5
|
+
* the shared `alerts` state.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Screen position for the toast stack.
|
|
10
|
+
*/
|
|
11
|
+
export type AlertListPosition =
|
|
12
|
+
| 'top-right'
|
|
13
|
+
| 'top-center'
|
|
14
|
+
| 'top-left'
|
|
15
|
+
| 'bottom-right'
|
|
16
|
+
| 'bottom-center'
|
|
17
|
+
| 'bottom-left'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Props for the AlertList component.
|
|
21
|
+
*/
|
|
22
|
+
export interface AlertListProps {
|
|
23
|
+
/** Screen position for the toast stack. */
|
|
24
|
+
position?: AlertListPosition
|
|
25
|
+
|
|
26
|
+
/** Additional CSS class. */
|
|
27
|
+
class?: string
|
|
28
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Avatar Component Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Avatar size scale. */
|
|
6
|
+
export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
|
7
|
+
|
|
8
|
+
/** Avatar shape. */
|
|
9
|
+
export type AvatarShape = 'circle' | 'square'
|
|
10
|
+
|
|
11
|
+
/** Online presence status. */
|
|
12
|
+
export type AvatarStatus = 'online' | 'offline' | 'away' | 'busy'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Props for the Avatar component.
|
|
16
|
+
*/
|
|
17
|
+
export interface AvatarProps {
|
|
18
|
+
/** Image source URL. */
|
|
19
|
+
src?: string
|
|
20
|
+
|
|
21
|
+
/** Alt text for the image. */
|
|
22
|
+
alt?: string
|
|
23
|
+
|
|
24
|
+
/** Explicit initials to display as fallback. */
|
|
25
|
+
initials?: string
|
|
26
|
+
|
|
27
|
+
/** Full name — auto-derives initials if `initials` prop not provided. */
|
|
28
|
+
name?: string
|
|
29
|
+
|
|
30
|
+
/** Size variant. */
|
|
31
|
+
size?: AvatarSize
|
|
32
|
+
|
|
33
|
+
/** Online presence status. */
|
|
34
|
+
status?: AvatarStatus
|
|
35
|
+
|
|
36
|
+
/** Shape variant. */
|
|
37
|
+
shape?: AvatarShape
|
|
38
|
+
|
|
39
|
+
/** Additional CSS class. */
|
|
40
|
+
class?: string
|
|
41
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Badge Component Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Snippet } from 'svelte'
|
|
6
|
+
|
|
7
|
+
/** Semantic color variant for a badge. */
|
|
8
|
+
export type BadgeVariant = 'default' | 'primary' | 'success' | 'warning' | 'error'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Props for the Badge component.
|
|
12
|
+
*
|
|
13
|
+
* Badge can be used standalone or wrapped around a child via the `children`
|
|
14
|
+
* snippet, in which case it positions itself absolutely over the child.
|
|
15
|
+
*/
|
|
16
|
+
export interface BadgeProps {
|
|
17
|
+
/** Numeric count to display. */
|
|
18
|
+
count?: number
|
|
19
|
+
|
|
20
|
+
/** Maximum count before rendering as `${max}+`. Default `99`. */
|
|
21
|
+
max?: number
|
|
22
|
+
|
|
23
|
+
/** Visual variant. */
|
|
24
|
+
variant?: BadgeVariant
|
|
25
|
+
|
|
26
|
+
/** Render as a small dot without content. */
|
|
27
|
+
dot?: boolean
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Content to wrap. When provided, the badge positions absolutely over
|
|
31
|
+
* the rendered child.
|
|
32
|
+
*/
|
|
33
|
+
children?: Snippet
|
|
34
|
+
|
|
35
|
+
/** Additional CSS class. */
|
|
36
|
+
class?: string
|
|
37
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BreadCrumbs Component Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Snippet } from 'svelte'
|
|
6
|
+
import type { ProxyItem } from '@rokkit/states'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Icon overrides for the BreadCrumbs component.
|
|
10
|
+
*/
|
|
11
|
+
export interface BreadCrumbsIcons {
|
|
12
|
+
/** Icon class used for the separator between crumbs. */
|
|
13
|
+
separator?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Props for the BreadCrumbs component.
|
|
18
|
+
*/
|
|
19
|
+
export interface BreadCrumbsProps {
|
|
20
|
+
/** Array of breadcrumb items. */
|
|
21
|
+
items?: unknown[]
|
|
22
|
+
|
|
23
|
+
/** Custom field mappings used by the internal `ProxyItem`. */
|
|
24
|
+
fields?: Record<string, string>
|
|
25
|
+
|
|
26
|
+
/** Accessible label for the surrounding `<nav>`. */
|
|
27
|
+
label?: string
|
|
28
|
+
|
|
29
|
+
/** Custom icon overrides. */
|
|
30
|
+
icons?: BreadCrumbsIcons
|
|
31
|
+
|
|
32
|
+
/** Called when a crumb is clicked. */
|
|
33
|
+
onclick?: (value: unknown, item: unknown) => void
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Custom snippet for rendering each crumb. Receives the crumb's
|
|
37
|
+
* `ProxyItem` and a flag indicating whether it is the last (current) crumb.
|
|
38
|
+
*/
|
|
39
|
+
crumb?: Snippet<[ProxyItem, boolean]>
|
|
40
|
+
|
|
41
|
+
/** Additional CSS class. */
|
|
42
|
+
class?: string
|
|
43
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card Component Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Snippet } from 'svelte'
|
|
6
|
+
|
|
7
|
+
/** Semantic color variant for a card. */
|
|
8
|
+
export type CardVariant = 'default' | 'primary' | 'secondary' | 'tertiary'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Props for the Card component.
|
|
12
|
+
*
|
|
13
|
+
* When `href` is provided the card renders as an anchor. When `onclick` is
|
|
14
|
+
* provided (and no `href`) it renders as a button. Otherwise it renders as
|
|
15
|
+
* a plain container.
|
|
16
|
+
*/
|
|
17
|
+
export interface CardProps {
|
|
18
|
+
/** Optional href — renders the card as an `<a>` link. */
|
|
19
|
+
href?: string
|
|
20
|
+
|
|
21
|
+
/** Click handler — applies only when no `href` is provided. */
|
|
22
|
+
onclick?: () => void
|
|
23
|
+
|
|
24
|
+
/** Visual variant. */
|
|
25
|
+
variant?: CardVariant
|
|
26
|
+
|
|
27
|
+
/** Additional CSS class. */
|
|
28
|
+
class?: string
|
|
29
|
+
|
|
30
|
+
/** Card header snippet. */
|
|
31
|
+
header?: Snippet
|
|
32
|
+
|
|
33
|
+
/** Card footer snippet. */
|
|
34
|
+
footer?: Snippet
|
|
35
|
+
|
|
36
|
+
/** Card body content. */
|
|
37
|
+
children?: Snippet
|
|
38
|
+
}
|