sv5ui 1.7.0 → 1.8.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 (41) hide show
  1. package/dist/Banner/Banner.svelte +162 -0
  2. package/dist/Banner/Banner.svelte.d.ts +5 -0
  3. package/dist/Banner/banner.types.d.ts +148 -0
  4. package/dist/Banner/banner.types.js +1 -0
  5. package/dist/Banner/banner.variants.d.ts +293 -0
  6. package/dist/Banner/banner.variants.js +86 -0
  7. package/dist/Banner/index.d.ts +2 -0
  8. package/dist/Banner/index.js +1 -0
  9. package/dist/Editor/Editor.svelte +738 -0
  10. package/dist/Editor/Editor.svelte.d.ts +6 -0
  11. package/dist/Editor/EditorUrlPrompt.svelte +111 -0
  12. package/dist/Editor/EditorUrlPrompt.svelte.d.ts +15 -0
  13. package/dist/Editor/SlashPopup.svelte +67 -0
  14. package/dist/Editor/SlashPopup.svelte.d.ts +9 -0
  15. package/dist/Editor/editor.extensions.d.ts +23 -0
  16. package/dist/Editor/editor.extensions.js +123 -0
  17. package/dist/Editor/editor.schemas.d.ts +4 -0
  18. package/dist/Editor/editor.schemas.js +3 -0
  19. package/dist/Editor/editor.slash.svelte.d.ts +34 -0
  20. package/dist/Editor/editor.slash.svelte.js +273 -0
  21. package/dist/Editor/editor.suggestion.d.ts +7 -0
  22. package/dist/Editor/editor.suggestion.js +142 -0
  23. package/dist/Editor/editor.toolbar.d.ts +11 -0
  24. package/dist/Editor/editor.toolbar.js +212 -0
  25. package/dist/Editor/editor.types.d.ts +347 -0
  26. package/dist/Editor/editor.types.js +1 -0
  27. package/dist/Editor/editor.variants.d.ts +308 -0
  28. package/dist/Editor/editor.variants.js +150 -0
  29. package/dist/Editor/index.d.ts +53 -0
  30. package/dist/Editor/index.js +52 -0
  31. package/dist/Stepper/Stepper.svelte +292 -0
  32. package/dist/Stepper/Stepper.svelte.d.ts +5 -0
  33. package/dist/Stepper/index.d.ts +2 -0
  34. package/dist/Stepper/index.js +1 -0
  35. package/dist/Stepper/stepper.types.d.ts +223 -0
  36. package/dist/Stepper/stepper.types.js +1 -0
  37. package/dist/Stepper/stepper.variants.d.ts +428 -0
  38. package/dist/Stepper/stepper.variants.js +204 -0
  39. package/dist/index.d.ts +2 -0
  40. package/dist/index.js +2 -0
  41. package/package.json +97 -1
@@ -0,0 +1,150 @@
1
+ import { tv } from 'tailwind-variants';
2
+ export const editorVariants = tv({
3
+ slots: {
4
+ root: [
5
+ 'relative w-full rounded-lg border border-outline-variant bg-surface',
6
+ 'transition-shadow',
7
+ 'focus-within:ring-2 focus-within:ring-offset-0',
8
+ 'data-[disabled=true]:opacity-60 data-[disabled=true]:pointer-events-none'
9
+ ],
10
+ toolbar: [
11
+ 'flex flex-wrap items-center gap-0.5',
12
+ 'border-b border-outline-variant',
13
+ 'bg-surface-container-low',
14
+ 'rounded-t-lg p-1.5'
15
+ ],
16
+ toolbarGroup: 'flex items-center gap-0.5',
17
+ toolbarButton: [
18
+ 'inline-flex items-center justify-center rounded text-on-surface-variant',
19
+ 'hover:bg-surface-container-high hover:text-on-surface',
20
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary',
21
+ 'data-[active=true]:bg-primary-container data-[active=true]:text-on-primary-container',
22
+ 'disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent',
23
+ 'transition-colors'
24
+ ],
25
+ toolbarSeparator: 'mx-1 h-5 w-px shrink-0 bg-outline-variant',
26
+ content: [
27
+ 'text-on-surface',
28
+ // ----- Inner ProseMirror element (the actual contenteditable) -----
29
+ '[&_.ProseMirror]:outline-none',
30
+ '[&_.ProseMirror]:focus:outline-none',
31
+ '[&_.ProseMirror]:focus-visible:outline-none',
32
+ '[&_.ProseMirror]:p-4',
33
+ '[&_.ProseMirror]:min-h-32',
34
+ '[&_.ProseMirror]:whitespace-pre-wrap',
35
+ '[&_.ProseMirror]:break-words',
36
+ // ----- Placeholder (when editor is empty) -----
37
+ '[&_.is-editor-empty]:before:content-[attr(data-placeholder)]',
38
+ '[&_.is-editor-empty]:before:text-on-surface-variant/60',
39
+ '[&_.is-editor-empty]:before:pointer-events-none',
40
+ '[&_.is-editor-empty]:before:float-left',
41
+ '[&_.is-editor-empty]:before:h-0',
42
+ // ----- Content typography (lightweight in-house prose) -----
43
+ '[&_p]:my-2 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0',
44
+ '[&_h1]:text-2xl [&_h1]:font-bold [&_h1]:my-3',
45
+ '[&_h2]:text-xl [&_h2]:font-bold [&_h2]:my-3',
46
+ '[&_h3]:text-lg [&_h3]:font-semibold [&_h3]:my-2',
47
+ '[&_ul]:list-disc [&_ul]:ps-6 [&_ul]:my-2',
48
+ '[&_ol]:list-decimal [&_ol]:ps-6 [&_ol]:my-2',
49
+ '[&_li>p]:my-0',
50
+ '[&_blockquote]:border-l-4 [&_blockquote]:border-outline-variant [&_blockquote]:ps-3 [&_blockquote]:italic [&_blockquote]:text-on-surface-variant [&_blockquote]:my-3',
51
+ '[&_code]:rounded [&_code]:bg-surface-container-highest [&_code]:px-1 [&_code]:py-0.5 [&_code]:text-sm [&_code]:font-mono',
52
+ '[&_pre]:rounded-md [&_pre]:bg-surface-container-highest [&_pre]:p-3 [&_pre]:my-3 [&_pre]:overflow-x-auto',
53
+ '[&_pre_code]:bg-transparent [&_pre_code]:p-0',
54
+ '[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-2',
55
+ '[&_a:hover]:text-primary/80',
56
+ '[&_hr]:border-outline-variant [&_hr]:my-4',
57
+ '[&_strong]:font-semibold',
58
+ '[&_em]:italic',
59
+ // ----- Image -----
60
+ '[&_img]:max-w-full [&_img]:h-auto [&_img]:rounded-md [&_img]:my-2',
61
+ // ----- YouTube embed (responsive 16:9) -----
62
+ '[&_iframe[src*="youtube"]]:aspect-video [&_iframe[src*="youtube"]]:w-full [&_iframe[src*="youtube"]]:rounded-md [&_iframe[src*="youtube"]]:my-3 [&_iframe[src*="youtube"]]:border-0',
63
+ // ----- Mention chip -----
64
+ '[&_.sv5ui-editor-mention]:inline-flex [&_.sv5ui-editor-mention]:items-center',
65
+ '[&_.sv5ui-editor-mention]:rounded [&_.sv5ui-editor-mention]:bg-primary-container/60',
66
+ '[&_.sv5ui-editor-mention]:text-on-primary-container',
67
+ '[&_.sv5ui-editor-mention]:px-1.5 [&_.sv5ui-editor-mention]:py-0.5',
68
+ '[&_.sv5ui-editor-mention]:text-sm [&_.sv5ui-editor-mention]:font-medium',
69
+ // ----- Table -----
70
+ '[&_.tableWrapper]:my-3 [&_.tableWrapper]:overflow-x-auto',
71
+ '[&_table]:w-full [&_table]:border-collapse [&_table]:table-fixed',
72
+ '[&_table]:border [&_table]:border-outline-variant [&_table]:rounded-md',
73
+ '[&_th]:border [&_th]:border-outline-variant',
74
+ '[&_th]:bg-surface-container-low [&_th]:px-2 [&_th]:py-1.5',
75
+ '[&_th]:font-semibold [&_th]:text-start [&_th]:align-top',
76
+ '[&_th]:relative [&_th]:min-w-16',
77
+ '[&_td]:border [&_td]:border-outline-variant',
78
+ '[&_td]:px-2 [&_td]:py-1.5 [&_td]:align-top',
79
+ '[&_td]:relative [&_td]:min-w-16',
80
+ // Tiptap selectedCell highlight + column resize handle
81
+ '[&_.selectedCell]:bg-primary/10',
82
+ '[&_.column-resize-handle]:absolute [&_.column-resize-handle]:top-0 [&_.column-resize-handle]:bottom-[-2px]',
83
+ '[&_.column-resize-handle]:-right-0.5 [&_.column-resize-handle]:w-1',
84
+ '[&_.column-resize-handle]:bg-primary/40 [&_.column-resize-handle]:pointer-events-none',
85
+ // ----- Selection styling -----
86
+ '[&_::selection]:bg-primary/20'
87
+ ],
88
+ footer: [
89
+ 'flex items-center justify-between gap-2',
90
+ 'border-t border-outline-variant',
91
+ 'bg-surface-container-low',
92
+ 'px-3 py-1.5 rounded-b-lg',
93
+ 'text-xs text-on-surface-variant'
94
+ ],
95
+ countLabel: 'tabular-nums',
96
+ bubbleMenu: [
97
+ // ⚠️ Tiptap's BubbleMenu only sets `visibility:hidden` initially,
98
+ // leaving the element in normal flow (taking up space below the editor)
99
+ // until the user first selects text. Setting `position:absolute` upfront
100
+ // keeps it out of flow from mount — extension's Floating UI positioning
101
+ // then overrides left/top inline on selection.
102
+ 'absolute top-0 left-0 z-50 invisible',
103
+ 'flex items-center gap-0.5 p-1',
104
+ 'rounded-lg border border-outline-variant bg-surface',
105
+ 'shadow-md'
106
+ ]
107
+ },
108
+ variants: {
109
+ size: {
110
+ sm: {
111
+ toolbarButton: 'size-7 *:size-3.5',
112
+ content: 'text-sm [&_.ProseMirror]:p-3 [&_.ProseMirror]:min-h-24',
113
+ footer: 'text-[11px]'
114
+ },
115
+ md: {
116
+ toolbarButton: 'size-8 *:size-4',
117
+ content: 'text-sm',
118
+ footer: ''
119
+ },
120
+ lg: {
121
+ toolbarButton: 'size-9 *:size-5',
122
+ content: 'text-base [&_.ProseMirror]:p-5 [&_.ProseMirror]:min-h-40',
123
+ footer: ''
124
+ }
125
+ },
126
+ color: {
127
+ primary: { root: 'focus-within:ring-primary' },
128
+ secondary: { root: 'focus-within:ring-secondary' },
129
+ tertiary: { root: 'focus-within:ring-tertiary' },
130
+ success: { root: 'focus-within:ring-success' },
131
+ warning: { root: 'focus-within:ring-warning' },
132
+ error: { root: 'focus-within:ring-error' },
133
+ info: { root: 'focus-within:ring-info' },
134
+ surface: { root: 'focus-within:ring-outline' }
135
+ },
136
+ sticky: {
137
+ true: { toolbar: 'sticky top-0 z-10 backdrop-blur-sm' },
138
+ false: ''
139
+ }
140
+ },
141
+ defaultVariants: {
142
+ size: 'md',
143
+ color: 'primary',
144
+ sticky: false
145
+ }
146
+ });
147
+ export const editorDefaults = {
148
+ defaultVariants: editorVariants.defaultVariants,
149
+ slots: {}
150
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * SV5UI Editor — rich-text WYSIWYG built on Tiptap v3 + ProseMirror.
3
+ *
4
+ * ## Installation
5
+ *
6
+ * Tiptap is declared as an optional peer dependency of sv5ui, so consumers
7
+ * who don't use `<Editor>` pay zero install/bundle cost. When you do use it,
8
+ * install Tiptap alongside sv5ui:
9
+ *
10
+ * ```bash
11
+ * pnpm add sv5ui \
12
+ * @tiptap/core @tiptap/pm @tiptap/starter-kit \
13
+ * @tiptap/extension-bubble-menu \
14
+ * @tiptap/extension-character-count \
15
+ * @tiptap/extension-drag-handle \
16
+ * @tiptap/extension-image \
17
+ * @tiptap/extension-mention \
18
+ * @tiptap/extension-placeholder \
19
+ * @tiptap/extension-table \
20
+ * @tiptap/extension-table-cell \
21
+ * @tiptap/extension-table-header \
22
+ * @tiptap/extension-table-row \
23
+ * @tiptap/extension-text-align \
24
+ * @tiptap/extension-typography \
25
+ * @tiptap/extension-youtube \
26
+ * @tiptap/suggestion \
27
+ * tiptap-markdown
28
+ * ```
29
+ *
30
+ * (`npm add` / `yarn add` work equally well.)
31
+ *
32
+ * All 18 packages are required, even if you only enable a subset of
33
+ * features. Editor imports every extension at module load so toggling
34
+ * `image`, `tables`, `youtube`, etc. via props doesn't trigger a dynamic
35
+ * import. Missing peers fail at runtime with module-resolution errors.
36
+ *
37
+ * ## Usage
38
+ *
39
+ * ```svelte
40
+ * <script lang="ts">
41
+ * import { Editor, type EditorApi } from 'sv5ui/editor'
42
+ * let value = $state('<p>Hello</p>')
43
+ * let api = $state<EditorApi>()
44
+ * </script>
45
+ *
46
+ * <Editor bind:value bind:api placeholder="Write something…" />
47
+ * ```
48
+ *
49
+ * See {@link EditorProps} for every option.
50
+ */
51
+ export { default as Editor } from './Editor.svelte';
52
+ export { buildDefaultSlashCommands } from './editor.slash.svelte.js';
53
+ export type { EditorProps, EditorApi, EditorJSON, EditorReactiveState, MentionItem, SlashCommand, ToolbarAction } from './editor.types.js';
@@ -0,0 +1,52 @@
1
+ /**
2
+ * SV5UI Editor — rich-text WYSIWYG built on Tiptap v3 + ProseMirror.
3
+ *
4
+ * ## Installation
5
+ *
6
+ * Tiptap is declared as an optional peer dependency of sv5ui, so consumers
7
+ * who don't use `<Editor>` pay zero install/bundle cost. When you do use it,
8
+ * install Tiptap alongside sv5ui:
9
+ *
10
+ * ```bash
11
+ * pnpm add sv5ui \
12
+ * @tiptap/core @tiptap/pm @tiptap/starter-kit \
13
+ * @tiptap/extension-bubble-menu \
14
+ * @tiptap/extension-character-count \
15
+ * @tiptap/extension-drag-handle \
16
+ * @tiptap/extension-image \
17
+ * @tiptap/extension-mention \
18
+ * @tiptap/extension-placeholder \
19
+ * @tiptap/extension-table \
20
+ * @tiptap/extension-table-cell \
21
+ * @tiptap/extension-table-header \
22
+ * @tiptap/extension-table-row \
23
+ * @tiptap/extension-text-align \
24
+ * @tiptap/extension-typography \
25
+ * @tiptap/extension-youtube \
26
+ * @tiptap/suggestion \
27
+ * tiptap-markdown
28
+ * ```
29
+ *
30
+ * (`npm add` / `yarn add` work equally well.)
31
+ *
32
+ * All 18 packages are required, even if you only enable a subset of
33
+ * features. Editor imports every extension at module load so toggling
34
+ * `image`, `tables`, `youtube`, etc. via props doesn't trigger a dynamic
35
+ * import. Missing peers fail at runtime with module-resolution errors.
36
+ *
37
+ * ## Usage
38
+ *
39
+ * ```svelte
40
+ * <script lang="ts">
41
+ * import { Editor, type EditorApi } from 'sv5ui/editor'
42
+ * let value = $state('<p>Hello</p>')
43
+ * let api = $state<EditorApi>()
44
+ * </script>
45
+ *
46
+ * <Editor bind:value bind:api placeholder="Write something…" />
47
+ * ```
48
+ *
49
+ * See {@link EditorProps} for every option.
50
+ */
51
+ export { default as Editor } from './Editor.svelte';
52
+ export { buildDefaultSlashCommands } from './editor.slash.svelte.js';
@@ -0,0 +1,292 @@
1
+ <script lang="ts" module>
2
+ import type { StepperProps } from './stepper.types.js'
3
+
4
+ export type Props = StepperProps
5
+ </script>
6
+
7
+ <script lang="ts">
8
+ import type { ClassNameValue } from 'tailwind-merge'
9
+ import { untrack } from 'svelte'
10
+ import { stepperVariants, stepperDefaults } from './stepper.variants.js'
11
+ import type { StepperItem, StepperItemState, StepperApi } from './stepper.types.js'
12
+ import { getComponentConfig } from '../config.js'
13
+ import Icon from '../Icon/Icon.svelte'
14
+
15
+ const config = getComponentConfig('stepper', stepperDefaults)
16
+
17
+ let {
18
+ ref = $bindable(null),
19
+ as = 'div',
20
+ items = [],
21
+ value = $bindable(),
22
+ defaultValue,
23
+ onValueChange,
24
+ api = $bindable(),
25
+ linear = true,
26
+ disabled = false,
27
+ orientation = config.defaultVariants.orientation ?? 'horizontal',
28
+ size = config.defaultVariants.size ?? 'md',
29
+ color = config.defaultVariants.color ?? 'primary',
30
+ content: showContent = true,
31
+ class: className,
32
+ ui,
33
+ indicator: indicatorSlot,
34
+ titleSlot,
35
+ descriptionSlot,
36
+ body,
37
+ ...restProps
38
+ }: Props = $props()
39
+
40
+ if (value === undefined) {
41
+ value = untrack(() => defaultValue ?? items[0]?.value ?? 0)
42
+ }
43
+
44
+ let triggers: (HTMLButtonElement | null)[] = $state([])
45
+
46
+ function getItemValue(item: StepperItem, index: number): string | number {
47
+ return item.value ?? index
48
+ }
49
+
50
+ const activeIndex = $derived(items.findIndex((item, i) => getItemValue(item, i) === value))
51
+
52
+ function getState(index: number): StepperItemState {
53
+ if (activeIndex === -1) return 'pending'
54
+ if (index === activeIndex) return 'active'
55
+ return index < activeIndex ? 'completed' : 'pending'
56
+ }
57
+
58
+ function canActivate(index: number): boolean {
59
+ if (disabled || items[index]?.disabled) return false
60
+ if (!linear) return true
61
+ return index <= activeIndex + 1
62
+ }
63
+
64
+ function setValue(next: string | number) {
65
+ if (next === value) return
66
+ value = next
67
+ onValueChange?.(next)
68
+ }
69
+
70
+ function handleClick(item: StepperItem, index: number) {
71
+ if (!canActivate(index)) return
72
+ setValue(getItemValue(item, index))
73
+ }
74
+
75
+ function findEnabled(start: number, dir: 1 | -1): number {
76
+ for (let i = start; i >= 0 && i < items.length; i += dir) {
77
+ if (!items[i]?.disabled) return i
78
+ }
79
+ return -1
80
+ }
81
+
82
+ function handleKeydown(e: KeyboardEvent, index: number) {
83
+ const horizontal = orientation === 'horizontal'
84
+ const nextKey = horizontal ? 'ArrowRight' : 'ArrowDown'
85
+ const prevKey = horizontal ? 'ArrowLeft' : 'ArrowUp'
86
+
87
+ let target = -1
88
+ if (e.key === nextKey) target = findEnabled(index + 1, 1)
89
+ else if (e.key === prevKey) target = findEnabled(index - 1, -1)
90
+ else if (e.key === 'Home') target = findEnabled(0, 1)
91
+ else if (e.key === 'End') target = findEnabled(items.length - 1, -1)
92
+ else return
93
+
94
+ e.preventDefault()
95
+ if (target >= 0) triggers[target]?.focus()
96
+ }
97
+
98
+ const apiInstance: StepperApi = {
99
+ next() {
100
+ if (activeIndex >= items.length - 1) return
101
+ const target = items[activeIndex + 1]
102
+ if (!target) return
103
+ setValue(getItemValue(target, activeIndex + 1))
104
+ },
105
+ prev() {
106
+ if (activeIndex <= 0) return
107
+ const target = items[activeIndex - 1]
108
+ if (!target) return
109
+ setValue(getItemValue(target, activeIndex - 1))
110
+ },
111
+ goTo(next) {
112
+ setValue(next)
113
+ },
114
+ get hasNext() {
115
+ return activeIndex >= 0 && activeIndex < items.length - 1
116
+ },
117
+ get hasPrev() {
118
+ return activeIndex > 0
119
+ },
120
+ get activeIndex() {
121
+ return activeIndex
122
+ }
123
+ }
124
+
125
+ $effect.pre(() => {
126
+ api = apiInstance
127
+ })
128
+
129
+ const variantSlots = $derived(stepperVariants({ orientation, size, color }))
130
+
131
+ type ItemUi = StepperItem['ui']
132
+
133
+ const classes = $derived.by(() => {
134
+ const c = config.slots
135
+ const slots = variantSlots
136
+ const u = ui ?? {}
137
+
138
+ const _item = slots.item({ class: [c.item, u.item] })
139
+ const _trigger = slots.trigger({ class: [c.trigger, u.trigger] })
140
+ const _container = slots.container({ class: [c.container, u.container] })
141
+ const _indicator = slots.indicator({ class: [c.indicator, u.indicator] })
142
+ const _separator = slots.separator({ class: [c.separator, u.separator] })
143
+ const _wrapper = slots.wrapper({ class: [c.wrapper, u.wrapper] })
144
+ const _title = slots.title({ class: [c.title, u.title] })
145
+ const _description = slots.description({ class: [c.description, u.description] })
146
+ const _content = slots.content({ class: [c.content, u.content] })
147
+
148
+ return {
149
+ root: slots.root({ class: [c.root, className, u.root] }),
150
+ list: slots.list({ class: [c.list, u.list] }),
151
+ item: (itemClass?: ClassNameValue, itemUi?: ItemUi) =>
152
+ itemClass || itemUi
153
+ ? slots.item({ class: [c.item, u.item, itemUi?.item, itemClass] })
154
+ : _item,
155
+ trigger: (itemUi?: ItemUi) =>
156
+ itemUi
157
+ ? slots.trigger({ class: [c.trigger, u.trigger, itemUi.trigger] })
158
+ : _trigger,
159
+ container: (itemUi?: ItemUi) =>
160
+ itemUi
161
+ ? slots.container({ class: [c.container, u.container, itemUi.container] })
162
+ : _container,
163
+ indicator: (itemUi?: ItemUi) =>
164
+ itemUi
165
+ ? slots.indicator({ class: [c.indicator, u.indicator, itemUi.indicator] })
166
+ : _indicator,
167
+ separator: (itemUi?: ItemUi) =>
168
+ itemUi
169
+ ? slots.separator({ class: [c.separator, u.separator, itemUi.separator] })
170
+ : _separator,
171
+ wrapper: (itemUi?: ItemUi) =>
172
+ itemUi
173
+ ? slots.wrapper({ class: [c.wrapper, u.wrapper, itemUi.wrapper] })
174
+ : _wrapper,
175
+ title: (itemUi?: ItemUi) =>
176
+ itemUi ? slots.title({ class: [c.title, u.title, itemUi.title] }) : _title,
177
+ description: (itemUi?: ItemUi) =>
178
+ itemUi
179
+ ? slots.description({
180
+ class: [c.description, u.description, itemUi.description]
181
+ })
182
+ : _description,
183
+ content: (itemUi?: ItemUi) =>
184
+ itemUi ? slots.content({ class: [c.content, u.content, itemUi.content] }) : _content
185
+ }
186
+ })
187
+ </script>
188
+
189
+ <svelte:element
190
+ this={as}
191
+ bind:this={ref}
192
+ class={classes.root}
193
+ data-orientation={orientation}
194
+ {...restProps}
195
+ >
196
+ <ol class={classes.list}>
197
+ {#each items as item, index (item.value ?? index)}
198
+ {@const state = getState(index)}
199
+ {@const number = index + 1}
200
+ {@const active = state === 'active'}
201
+ {@const itemDisabled = disabled || item.disabled || !canActivate(index)}
202
+ {@const slotProps = { item, index, number, state, active }}
203
+ {@const hasVisibleText = !!(
204
+ titleSlot ||
205
+ descriptionSlot ||
206
+ item.title ||
207
+ item.description
208
+ )}
209
+ <li
210
+ class={classes.item(item.class, item.ui)}
211
+ data-state={state}
212
+ data-orientation={orientation}
213
+ data-stepper-item=""
214
+ >
215
+ <button
216
+ type="button"
217
+ bind:this={triggers[index]}
218
+ class={classes.trigger(item.ui)}
219
+ disabled={itemDisabled}
220
+ aria-current={active ? 'step' : undefined}
221
+ aria-label={hasVisibleText ? undefined : `Step ${number}`}
222
+ tabindex={active ? 0 : -1}
223
+ onclick={() => handleClick(item, index)}
224
+ onkeydown={(e) => handleKeydown(e, index)}
225
+ >
226
+ <span class={classes.container(item.ui)}>
227
+ {#if indicatorSlot}
228
+ {@render indicatorSlot(slotProps)}
229
+ {:else}
230
+ <span
231
+ class={classes.indicator(item.ui)}
232
+ data-stepper-indicator=""
233
+ aria-hidden="true"
234
+ >
235
+ {#if item.icon}
236
+ <Icon name={item.icon} />
237
+ {:else if state === 'completed'}
238
+ <Icon name="lucide:check" />
239
+ {:else}
240
+ {number}
241
+ {/if}
242
+ </span>
243
+ {/if}
244
+
245
+ {#if index < items.length - 1}
246
+ <span
247
+ class={classes.separator(item.ui)}
248
+ data-stepper-separator=""
249
+ aria-hidden="true"
250
+ ></span>
251
+ {/if}
252
+ </span>
253
+
254
+ {#if hasVisibleText}
255
+ <span class={classes.wrapper(item.ui)}>
256
+ {#if titleSlot}
257
+ {@render titleSlot(slotProps)}
258
+ {:else if item.title}
259
+ <span class={classes.title(item.ui)}>{item.title}</span>
260
+ {/if}
261
+
262
+ {#if descriptionSlot}
263
+ {@render descriptionSlot(slotProps)}
264
+ {:else if item.description}
265
+ <span class={classes.description(item.ui)}>
266
+ {item.description}
267
+ </span>
268
+ {/if}
269
+ </span>
270
+ {/if}
271
+ </button>
272
+ </li>
273
+ {/each}
274
+ </ol>
275
+
276
+ {#if showContent}
277
+ {#each items as item, index (item.value ?? index)}
278
+ {@const state = getState(index)}
279
+ {#if state === 'active' && (body || item.content !== undefined)}
280
+ {@const number = index + 1}
281
+ {@const slotProps = { item, index, number, state, active: true }}
282
+ <div class={classes.content(item.ui)} role="region" data-stepper-content="">
283
+ {#if body}
284
+ {@render body(slotProps)}
285
+ {:else}
286
+ {item.content}
287
+ {/if}
288
+ </div>
289
+ {/if}
290
+ {/each}
291
+ {/if}
292
+ </svelte:element>
@@ -0,0 +1,5 @@
1
+ import type { StepperProps } from './stepper.types.js';
2
+ export type Props = StepperProps;
3
+ declare const Stepper: import("svelte").Component<StepperProps, {}, "ref" | "value" | "api">;
4
+ type Stepper = ReturnType<typeof Stepper>;
5
+ export default Stepper;
@@ -0,0 +1,2 @@
1
+ export { default as Stepper } from './Stepper.svelte';
2
+ export type { StepperProps, StepperItem, StepperItemState, StepperApi } from './stepper.types.js';
@@ -0,0 +1 @@
1
+ export { default as Stepper } from './Stepper.svelte';