sv5ui 1.7.0 → 2.0.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 (85) hide show
  1. package/dist/Alert/alert.types.d.ts +1 -1
  2. package/dist/AvatarGroup/AvatarGroup.svelte +5 -3
  3. package/dist/Banner/Banner.svelte +162 -0
  4. package/dist/Banner/Banner.svelte.d.ts +5 -0
  5. package/dist/Banner/banner.types.d.ts +148 -0
  6. package/dist/Banner/banner.types.js +1 -0
  7. package/dist/Banner/banner.variants.d.ts +293 -0
  8. package/dist/Banner/banner.variants.js +86 -0
  9. package/dist/Banner/index.d.ts +2 -0
  10. package/dist/Banner/index.js +1 -0
  11. package/dist/Button/Button.svelte +7 -6
  12. package/dist/Button/button.types.d.ts +3 -3
  13. package/dist/Collapsible/collapsible.types.d.ts +4 -2
  14. package/dist/Drawer/Drawer.svelte +3 -1
  15. package/dist/DropdownMenu/DropdownMenu.svelte +1 -3
  16. package/dist/DropdownMenu/DropdownMenuTriggerTestWrapper.svelte +12 -0
  17. package/dist/DropdownMenu/DropdownMenuTriggerTestWrapper.svelte.d.ts +7 -0
  18. package/dist/DropdownMenu/dropdown-menu.types.d.ts +17 -9
  19. package/dist/Editor/Editor.svelte +738 -0
  20. package/dist/Editor/Editor.svelte.d.ts +6 -0
  21. package/dist/Editor/EditorUrlPrompt.svelte +111 -0
  22. package/dist/Editor/EditorUrlPrompt.svelte.d.ts +15 -0
  23. package/dist/Editor/SlashPopup.svelte +67 -0
  24. package/dist/Editor/SlashPopup.svelte.d.ts +9 -0
  25. package/dist/Editor/editor.extensions.d.ts +23 -0
  26. package/dist/Editor/editor.extensions.js +123 -0
  27. package/dist/Editor/editor.schemas.d.ts +4 -0
  28. package/dist/Editor/editor.schemas.js +3 -0
  29. package/dist/Editor/editor.slash.svelte.d.ts +34 -0
  30. package/dist/Editor/editor.slash.svelte.js +273 -0
  31. package/dist/Editor/editor.suggestion.d.ts +7 -0
  32. package/dist/Editor/editor.suggestion.js +142 -0
  33. package/dist/Editor/editor.toolbar.d.ts +11 -0
  34. package/dist/Editor/editor.toolbar.js +212 -0
  35. package/dist/Editor/editor.types.d.ts +347 -0
  36. package/dist/Editor/editor.types.js +1 -0
  37. package/dist/Editor/editor.variants.d.ts +308 -0
  38. package/dist/Editor/editor.variants.js +150 -0
  39. package/dist/Editor/index.d.ts +53 -0
  40. package/dist/Editor/index.js +52 -0
  41. package/dist/Icon/icon.types.d.ts +4 -1
  42. package/dist/Input/Input.svelte +22 -16
  43. package/dist/Input/input.variants.d.ts +0 -15
  44. package/dist/Input/input.variants.js +1 -20
  45. package/dist/Link/Link.svelte +4 -3
  46. package/dist/Link/link.types.d.ts +2 -2
  47. package/dist/Pagination/Pagination.svelte +7 -1
  48. package/dist/Pagination/pagination.types.d.ts +4 -1
  49. package/dist/Pagination/pagination.variants.d.ts +0 -72
  50. package/dist/Pagination/pagination.variants.js +6 -30
  51. package/dist/Select/Select.svelte +3 -1
  52. package/dist/Select/select.types.d.ts +5 -9
  53. package/dist/SelectMenu/SelectMenu.svelte +6 -5
  54. package/dist/SelectMenu/SelectMenuFormFieldTestWrapper.svelte +11 -0
  55. package/dist/SelectMenu/SelectMenuFormFieldTestWrapper.svelte.d.ts +7 -0
  56. package/dist/SelectMenu/select-menu.types.d.ts +5 -2
  57. package/dist/SelectMenu/select-menu.variants.d.ts +12 -2
  58. package/dist/SelectMenu/select-menu.variants.js +10 -1
  59. package/dist/Separator/Separator.svelte +9 -2
  60. package/dist/Separator/separator.types.d.ts +5 -0
  61. package/dist/Separator/separator.variants.d.ts +25 -0
  62. package/dist/Separator/separator.variants.js +7 -1
  63. package/dist/Stepper/Stepper.svelte +292 -0
  64. package/dist/Stepper/Stepper.svelte.d.ts +5 -0
  65. package/dist/Stepper/index.d.ts +2 -0
  66. package/dist/Stepper/index.js +1 -0
  67. package/dist/Stepper/stepper.types.d.ts +223 -0
  68. package/dist/Stepper/stepper.types.js +1 -0
  69. package/dist/Stepper/stepper.variants.d.ts +428 -0
  70. package/dist/Stepper/stepper.variants.js +204 -0
  71. package/dist/Tabs/Tabs.svelte +4 -2
  72. package/dist/Tabs/tabs.types.d.ts +4 -6
  73. package/dist/hooks/HookContextProbe.svelte +7 -0
  74. package/dist/hooks/HookContextProbe.svelte.d.ts +18 -0
  75. package/dist/hooks/HookContextProvider.svelte +9 -0
  76. package/dist/hooks/HookContextProvider.svelte.d.ts +18 -0
  77. package/dist/hooks/HookEmitProbe.svelte +14 -0
  78. package/dist/hooks/HookEmitProbe.svelte.d.ts +18 -0
  79. package/dist/hooks/index.d.ts +1 -1
  80. package/dist/hooks/index.js +1 -1
  81. package/dist/hooks/useFormField.svelte.d.ts +0 -31
  82. package/dist/hooks/useFormField.svelte.js +0 -21
  83. package/dist/index.d.ts +2 -0
  84. package/dist/index.js +2 -0
  85. 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';
@@ -1,6 +1,9 @@
1
1
  import type { IconProps as IconifyProps } from '@iconify/svelte';
2
+ import type { SVGAttributes } from 'svelte/elements';
2
3
  import type { ClassNameValue } from 'tailwind-merge';
3
- export interface IconProps extends Omit<IconifyProps, 'icon' | 'width' | 'height' | 'rotate' | 'flip' | 'class'> {
4
+ export interface IconProps extends Omit<IconifyProps, 'icon' | 'width' | 'height' | 'rotate' | 'flip' | 'class'>, Pick<SVGAttributes<SVGSVGElement>, 'role' | 'tabindex' | 'aria-label' | 'aria-labelledby' | 'aria-describedby' | 'aria-hidden' | 'onclick' | 'onkeydown' | 'onmouseenter' | 'onmouseleave' | 'onfocus' | 'onblur'> {
5
+ /** Custom data attributes are forwarded to the rendered `<svg>`. */
6
+ [key: `data-${string}`]: unknown;
4
7
  /**
5
8
  * Icon name in Iconify format: "collection:icon-name"
6
9
  * @example "lucide:home", "mdi:account", "heroicons:star"
@@ -94,19 +94,14 @@
94
94
  const resolvedId = $derived(id ?? formFieldContext?.ariaId)
95
95
  const resolvedName = $derived(name ?? formFieldContext?.name)
96
96
 
97
- const isLeading = $derived(
98
- (!!icon && !trailing) || (loading && !trailing) || !!leadingIcon || !!avatar
99
- )
100
- const isTrailing = $derived((!!icon && trailing) || (loading && trailing) || !!trailingIcon)
97
+ const loadingLeading = $derived(loading && !trailing)
98
+ const loadingTrailing = $derived(loading && trailing)
101
99
 
102
- const leadingIconName = $derived(
103
- loading && isLeading
104
- ? loadingIcon
105
- : leadingIcon || (isLeading && !trailing && !avatar ? icon : undefined)
106
- )
107
- const trailingIconName = $derived(
108
- loading && isTrailing ? loadingIcon : trailingIcon || (trailing ? icon : undefined)
109
- )
100
+ const isLeading = $derived((!!icon && !trailing) || !!leadingIcon || !!avatar || loadingLeading)
101
+ const isTrailing = $derived((!!icon && trailing) || !!trailingIcon || loadingTrailing)
102
+
103
+ const leadingIconName = $derived(leadingIcon || (!!icon && !trailing ? icon : undefined))
104
+ const trailingIconName = $derived(trailingIcon || (!!icon && trailing ? icon : undefined))
110
105
 
111
106
  const ariaDescribedBy = $derived(
112
107
  !formFieldContext
@@ -123,7 +118,6 @@
123
118
  size: resolvedSize,
124
119
  leading: isLeading,
125
120
  trailing: isTrailing,
126
- loading,
127
121
  highlight: resolvedHighlight
128
122
  })
129
123
  )
@@ -154,14 +148,20 @@
154
148
  <span class={classes.leading}>
155
149
  {@render leadingSlot()}
156
150
  </span>
157
- {:else if isLeading && leadingIconName}
151
+ {:else if loadingLeading}
158
152
  <span class={classes.leading}>
159
- <Icon name={leadingIconName} class={classes.leadingIcon} />
153
+ <span class="inline-flex animate-spin">
154
+ <Icon name={loadingIcon} class={classes.leadingIcon} />
155
+ </span>
160
156
  </span>
161
157
  {:else if avatar}
162
158
  <span class={classes.leading}>
163
159
  <Avatar {...avatar} size={classes.leadingAvatarSize} class={classes.leadingAvatar} />
164
160
  </span>
161
+ {:else if leadingIconName}
162
+ <span class={classes.leading}>
163
+ <Icon name={leadingIconName} class={classes.leadingIcon} />
164
+ </span>
165
165
  {/if}
166
166
 
167
167
  <input
@@ -185,7 +185,13 @@
185
185
  <span class={classes.trailing}>
186
186
  {@render trailingSlot()}
187
187
  </span>
188
- {:else if isTrailing && trailingIconName}
188
+ {:else if loadingTrailing}
189
+ <span class={classes.trailing}>
190
+ <span class="inline-flex animate-spin">
191
+ <Icon name={loadingIcon} class={classes.trailingIcon} />
192
+ </span>
193
+ </span>
194
+ {:else if trailingIconName}
189
195
  <span class={classes.trailing}>
190
196
  <Icon name={trailingIconName} class={classes.trailingIcon} />
191
197
  </span>
@@ -65,9 +65,6 @@ export declare const inputVariants: import("tailwind-variants").TVReturnType<{
65
65
  trailing: {
66
66
  true: string;
67
67
  };
68
- loading: {
69
- true: string;
70
- };
71
68
  highlight: {
72
69
  true: string;
73
70
  };
@@ -146,9 +143,6 @@ export declare const inputVariants: import("tailwind-variants").TVReturnType<{
146
143
  trailing: {
147
144
  true: string;
148
145
  };
149
- loading: {
150
- true: string;
151
- };
152
146
  highlight: {
153
147
  true: string;
154
148
  };
@@ -227,9 +221,6 @@ export declare const inputVariants: import("tailwind-variants").TVReturnType<{
227
221
  trailing: {
228
222
  true: string;
229
223
  };
230
- loading: {
231
- true: string;
232
- };
233
224
  highlight: {
234
225
  true: string;
235
226
  };
@@ -312,9 +303,6 @@ export declare const inputDefaults: {
312
303
  trailing: {
313
304
  true: string;
314
305
  };
315
- loading: {
316
- true: string;
317
- };
318
306
  highlight: {
319
307
  true: string;
320
308
  };
@@ -393,9 +381,6 @@ export declare const inputDefaults: {
393
381
  trailing: {
394
382
  true: string;
395
383
  };
396
- loading: {
397
- true: string;
398
- };
399
384
  highlight: {
400
385
  true: string;
401
386
  };
@@ -81,9 +81,6 @@ export const inputVariants = tv({
81
81
  trailing: {
82
82
  true: ''
83
83
  },
84
- loading: {
85
- true: ''
86
- },
87
84
  highlight: {
88
85
  true: ''
89
86
  }
@@ -376,23 +373,7 @@ export const inputVariants = tv({
376
373
  { trailing: true, size: 'sm', class: { base: 'pe-8' } },
377
374
  { trailing: true, size: 'md', class: { base: 'pe-9' } },
378
375
  { trailing: true, size: 'lg', class: { base: 'pe-10' } },
379
- { trailing: true, size: 'xl', class: { base: 'pe-12' } },
380
- // ========== LOADING ICON ANIMATION ==========
381
- {
382
- loading: true,
383
- leading: true,
384
- class: {
385
- leadingIcon: 'animate-spin'
386
- }
387
- },
388
- {
389
- loading: true,
390
- leading: false,
391
- trailing: true,
392
- class: {
393
- trailingIcon: 'animate-spin'
394
- }
395
- }
376
+ { trailing: true, size: 'xl', class: { base: 'pe-12' } }
396
377
  ],
397
378
  defaultVariants: {
398
379
  variant: 'outline',
@@ -51,6 +51,7 @@
51
51
 
52
52
  <script lang="ts">
53
53
  import { page } from '$app/state'
54
+ import { twMerge } from 'tailwind-merge'
54
55
  import { linkVariants, linkDefaults } from './link.variants.js'
55
56
  import { getComponentConfig } from '../config.js'
56
57
 
@@ -113,7 +114,7 @@
113
114
 
114
115
  const baseClass = $derived.by(() => {
115
116
  const stateClass = isActive ? activeClass : inactiveClass
116
- if (raw) return [className, stateClass].filter(Boolean).join(' ')
117
+ if (raw) return twMerge(stateClass, className)
117
118
 
118
119
  const slots = linkVariants({ active: isActive, disabled, raw })
119
120
  return slots.base({ class: [config.slots.base, stateClass, className, ui?.base] })
@@ -138,6 +139,7 @@
138
139
  <!-- eslint-disable svelte/no-navigation-without-resolve -->
139
140
  <a
140
141
  bind:this={ref}
142
+ {...restProps}
141
143
  href={disabled ? undefined : href}
142
144
  class={baseClass}
143
145
  target={resolvedTarget}
@@ -147,7 +149,6 @@
147
149
  aria-current={ariaCurrent}
148
150
  tabindex={disabled ? -1 : undefined}
149
151
  onclick={handleClick}
150
- {...restProps}
151
152
  >
152
153
  <!-- eslint-enable svelte/no-navigation-without-resolve -->
153
154
  {@render children?.()}
@@ -155,12 +156,12 @@
155
156
  {:else}
156
157
  <button
157
158
  bind:this={ref}
159
+ {...restProps}
158
160
  type={type ?? 'button'}
159
161
  class={baseClass}
160
162
  {disabled}
161
163
  aria-current={ariaCurrent}
162
164
  onclick={handleClick}
163
- {...restProps}
164
165
  >
165
166
  {@render children?.()}
166
167
  </button>
@@ -1,8 +1,8 @@
1
1
  import type { Snippet } from 'svelte';
2
- import type { HTMLAttributes } from 'svelte/elements';
2
+ import type { HTMLAttributes, HTMLButtonAttributes, HTMLAnchorAttributes } from 'svelte/elements';
3
3
  import type { LinkSlots } from './link.variants.js';
4
4
  import type { ClassNameValue } from 'tailwind-merge';
5
- export type LinkProps = Omit<HTMLAttributes<HTMLElement>, 'class'> & {
5
+ export type LinkProps = Omit<HTMLAttributes<HTMLElement>, 'class'> & Pick<HTMLButtonAttributes, 'name' | 'value' | 'form' | 'formaction' | 'formenctype' | 'formmethod' | 'formnovalidate' | 'formtarget' | 'popovertarget' | 'popovertargetaction'> & Pick<HTMLAnchorAttributes, 'download' | 'hreflang' | 'ping' | 'media' | 'referrerpolicy'> & {
6
6
  /**
7
7
  * Bindable reference to the root DOM element.
8
8
  */
@@ -47,7 +47,8 @@
47
47
  nextSlot,
48
48
  lastSlot,
49
49
  ellipsisSlot,
50
- itemSlot
50
+ itemSlot,
51
+ ...restProps
51
52
  }: Props = $props()
52
53
 
53
54
  if (page === undefined) {
@@ -94,6 +95,7 @@
94
95
  </script>
95
96
 
96
97
  <Pagination.Root
98
+ {...restProps}
97
99
  bind:ref
98
100
  count={total}
99
101
  perPage={itemsPerPage}
@@ -144,6 +146,7 @@
144
146
  square
145
147
  {size}
146
148
  class={classes.prev}
149
+ aria-label="Previous page"
147
150
  >
148
151
  {@render prevSlot({ page: page!, disabled: prevDisabled })}
149
152
  </ButtonComponent>
@@ -156,6 +159,7 @@
156
159
  {size}
157
160
  icon={prevIcon}
158
161
  class={classes.prev}
162
+ aria-label="Previous page"
159
163
  />
160
164
  {/if}
161
165
  {/snippet}
@@ -196,6 +200,7 @@
196
200
  square
197
201
  {size}
198
202
  class={classes.next}
203
+ aria-label="Next page"
199
204
  >
200
205
  {@render nextSlot({ page: page!, disabled: nextDisabled })}
201
206
  </ButtonComponent>
@@ -208,6 +213,7 @@
208
213
  {size}
209
214
  icon={nextIcon}
210
215
  class={classes.next}
216
+ aria-label="Next page"
211
217
  />
212
218
  {/if}
213
219
  {/snippet}
@@ -1,5 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { ClassNameValue } from 'tailwind-merge';
3
+ import type { PaginationRootProps } from 'bits-ui';
3
4
  import type { PaginationSlots, PaginationVariantProps } from './pagination.variants.js';
4
5
  import type { ButtonProps } from '../Button/button.types.js';
5
6
  /**
@@ -38,7 +39,9 @@ export interface PaginationItemSlotProps {
38
39
  *
39
40
  * @see https://bits-ui.com/docs/components/pagination
40
41
  */
41
- export interface PaginationProps {
42
+ export interface PaginationProps extends Pick<PaginationRootProps, 'id' | 'style' | 'title' | 'role' | 'tabindex' | 'aria-label' | 'aria-labelledby' | 'aria-describedby' | 'onclick' | 'onkeydown' | 'onmouseenter' | 'onmouseleave' | 'onfocus' | 'onblur'> {
43
+ /** Custom data attributes are forwarded to the root element. */
44
+ [key: `data-${string}`]: unknown;
42
45
  /**
43
46
  * Bindable reference to the root DOM element.
44
47
  */