sv5ui 1.8.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 (45) hide show
  1. package/dist/Alert/alert.types.d.ts +1 -1
  2. package/dist/AvatarGroup/AvatarGroup.svelte +5 -3
  3. package/dist/Button/Button.svelte +7 -6
  4. package/dist/Button/button.types.d.ts +3 -3
  5. package/dist/Collapsible/collapsible.types.d.ts +4 -2
  6. package/dist/Drawer/Drawer.svelte +3 -1
  7. package/dist/DropdownMenu/DropdownMenu.svelte +1 -3
  8. package/dist/DropdownMenu/DropdownMenuTriggerTestWrapper.svelte +12 -0
  9. package/dist/DropdownMenu/DropdownMenuTriggerTestWrapper.svelte.d.ts +7 -0
  10. package/dist/DropdownMenu/dropdown-menu.types.d.ts +17 -9
  11. package/dist/Icon/icon.types.d.ts +4 -1
  12. package/dist/Input/Input.svelte +22 -16
  13. package/dist/Input/input.variants.d.ts +0 -15
  14. package/dist/Input/input.variants.js +1 -20
  15. package/dist/Link/Link.svelte +4 -3
  16. package/dist/Link/link.types.d.ts +2 -2
  17. package/dist/Pagination/Pagination.svelte +7 -1
  18. package/dist/Pagination/pagination.types.d.ts +4 -1
  19. package/dist/Pagination/pagination.variants.d.ts +0 -72
  20. package/dist/Pagination/pagination.variants.js +6 -30
  21. package/dist/Select/Select.svelte +3 -1
  22. package/dist/Select/select.types.d.ts +5 -9
  23. package/dist/SelectMenu/SelectMenu.svelte +6 -5
  24. package/dist/SelectMenu/SelectMenuFormFieldTestWrapper.svelte +11 -0
  25. package/dist/SelectMenu/SelectMenuFormFieldTestWrapper.svelte.d.ts +7 -0
  26. package/dist/SelectMenu/select-menu.types.d.ts +5 -2
  27. package/dist/SelectMenu/select-menu.variants.d.ts +12 -2
  28. package/dist/SelectMenu/select-menu.variants.js +10 -1
  29. package/dist/Separator/Separator.svelte +9 -2
  30. package/dist/Separator/separator.types.d.ts +5 -0
  31. package/dist/Separator/separator.variants.d.ts +25 -0
  32. package/dist/Separator/separator.variants.js +7 -1
  33. package/dist/Tabs/Tabs.svelte +4 -2
  34. package/dist/Tabs/tabs.types.d.ts +4 -6
  35. package/dist/hooks/HookContextProbe.svelte +7 -0
  36. package/dist/hooks/HookContextProbe.svelte.d.ts +18 -0
  37. package/dist/hooks/HookContextProvider.svelte +9 -0
  38. package/dist/hooks/HookContextProvider.svelte.d.ts +18 -0
  39. package/dist/hooks/HookEmitProbe.svelte +14 -0
  40. package/dist/hooks/HookEmitProbe.svelte.d.ts +18 -0
  41. package/dist/hooks/index.d.ts +1 -1
  42. package/dist/hooks/index.js +1 -1
  43. package/dist/hooks/useFormField.svelte.d.ts +0 -31
  44. package/dist/hooks/useFormField.svelte.js +0 -21
  45. package/package.json +1 -1
@@ -43,7 +43,7 @@ export type AlertProps = Omit<HTMLAttributes<HTMLDivElement>, 'class' | 'title'>
43
43
  color?: NonNullable<AlertVariantProps['color']>;
44
44
  /**
45
45
  * The visual style variant.
46
- * @default 'soft'
46
+ * @default 'solid'
47
47
  */
48
48
  variant?: NonNullable<AlertVariantProps['variant']>;
49
49
  /**
@@ -46,9 +46,11 @@
46
46
  }
47
47
  })
48
48
 
49
- const visibleAvatars = $derived(
50
- !avatars ? [] : max && max > 0 ? avatars.slice(0, max) : avatars
51
- )
49
+ const visibleAvatars = $derived.by(() => {
50
+ if (!avatars) return []
51
+ const shown = max && max > 0 ? avatars.slice(0, max) : avatars
52
+ return [...shown].reverse()
53
+ })
52
54
 
53
55
  const overflowCount = $derived(
54
56
  avatars && max && max > 0 ? Math.max(0, avatars.length - max) : 0
@@ -76,13 +76,14 @@
76
76
  const isLeading = $derived((!!icon && !trailing) || (isLoading && !trailing) || !!leadingIcon)
77
77
  const isTrailing = $derived((!!icon && trailing) || (isLoading && trailing) || !!trailingIcon)
78
78
 
79
+ const spinLeading = $derived(isLoading && !trailing)
80
+ const spinTrailing = $derived(isLoading && trailing)
81
+
79
82
  const leadingIconName = $derived(
80
- isLoading && isLeading
81
- ? loadingIcon
82
- : leadingIcon || (isLeading && !trailing ? icon : undefined)
83
+ spinLeading ? loadingIcon : leadingIcon || (!trailing ? icon : undefined)
83
84
  )
84
85
  const trailingIconName = $derived(
85
- isLoading && isTrailing ? loadingIcon : trailingIcon || (trailing ? icon : undefined)
86
+ spinTrailing ? loadingIcon : trailingIcon || (trailing ? icon : undefined)
86
87
  )
87
88
 
88
89
  const resolvedColor = $derived(active && activeColor ? activeColor : color)
@@ -96,8 +97,8 @@
96
97
  block,
97
98
  square: isIconOnly,
98
99
  loading: isLoading,
99
- leading: isLeading,
100
- trailing: isTrailing
100
+ leading: spinLeading,
101
+ trailing: spinTrailing
101
102
  })
102
103
  return {
103
104
  base: slots.base({ class: [config.slots.base, fieldGroupClass, className, ui?.base] }),
@@ -1,9 +1,9 @@
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 { ClassNameValue } from 'tailwind-merge';
4
4
  import type { ButtonVariantProps, ButtonSlots } from './button.variants.js';
5
5
  import type { AvatarProps } from '../Avatar/avatar.types.js';
6
- export type ButtonProps = Omit<HTMLAttributes<HTMLElement>, 'class' | 'color'> & {
6
+ export type ButtonProps = Omit<HTMLAttributes<HTMLElement>, 'class' | 'color'> & Pick<HTMLButtonAttributes, 'name' | 'value' | 'form' | 'formaction' | 'formenctype' | 'formmethod' | 'formnovalidate' | 'formtarget' | 'popovertarget' | 'popovertargetaction'> & Pick<HTMLAnchorAttributes, 'download' | 'hreflang' | 'ping' | 'media' | 'referrerpolicy'> & {
7
7
  /**
8
8
  * Bindable reference to the root DOM element.
9
9
  */
@@ -147,5 +147,5 @@ export type ButtonProps = Omit<HTMLAttributes<HTMLElement>, 'class' | 'color'> &
147
147
  /**
148
148
  * Override styles for specific button slots.
149
149
  */
150
- ui?: Partial<Record<ButtonSlots, ClassNameValue>>;
150
+ ui?: Partial<Record<Exclude<ButtonSlots, 'leadingAvatarSize'>, ClassNameValue>>;
151
151
  };
@@ -1,6 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { ClassNameValue } from 'tailwind-merge';
3
- import type { CollapsibleRootPropsWithoutHTML } from 'bits-ui';
3
+ import type { CollapsibleRootProps, CollapsibleRootPropsWithoutHTML } from 'bits-ui';
4
4
  import type { CollapsibleSlots } from './collapsible.variants.js';
5
5
  /**
6
6
  * Props for the Collapsible component.
@@ -21,7 +21,9 @@ import type { CollapsibleSlots } from './collapsible.variants.js';
21
21
  *
22
22
  * @see https://bits-ui.com/docs/components/collapsible
23
23
  */
24
- export interface CollapsibleProps extends Pick<CollapsibleRootPropsWithoutHTML, 'open' | 'onOpenChange' | 'onOpenChangeComplete' | 'disabled'> {
24
+ export interface CollapsibleProps extends Pick<CollapsibleRootPropsWithoutHTML, 'open' | 'onOpenChange' | 'onOpenChangeComplete' | 'disabled'>, Pick<CollapsibleRootProps, 'id' | 'style' | 'title' | 'role' | 'tabindex' | 'aria-label' | 'aria-labelledby' | 'aria-describedby' | 'onclick' | 'onkeydown' | 'onmouseenter' | 'onmouseleave' | 'onfocus' | 'onblur'> {
25
+ /** Custom data attributes are forwarded to the root element. */
26
+ [key: `data-${string}`]: unknown;
25
27
  /**
26
28
  * Bindable reference to the root DOM element.
27
29
  */
@@ -44,7 +44,8 @@
44
44
  titleSlot,
45
45
  descriptionSlot,
46
46
  body: bodySlot,
47
- footer: footerSlot
47
+ footer: footerSlot,
48
+ ...rest
48
49
  }: Props = $props()
49
50
 
50
51
  const hasTitle = $derived(!!title || !!titleSlot)
@@ -81,6 +82,7 @@
81
82
 
82
83
  const rootProps = $derived.by(() => {
83
84
  const base = {
85
+ ...rest,
84
86
  open,
85
87
  onOpenChange: handleOpenChange,
86
88
  direction,
@@ -348,9 +348,7 @@
348
348
  {#if children}
349
349
  <DropdownMenu.Trigger>
350
350
  {#snippet child({ props })}
351
- <span {...props} class={className as string}>
352
- {@render children({ open })}
353
- </span>
351
+ {@render children({ open, props })}
354
352
  {/snippet}
355
353
  </DropdownMenu.Trigger>
356
354
  {/if}
@@ -0,0 +1,12 @@
1
+ <script lang="ts">
2
+ import DropdownMenu from './DropdownMenu.svelte'
3
+ import type { DropdownMenuItem } from './dropdown-menu.types.js'
4
+
5
+ let { items = [] }: { items?: DropdownMenuItem[] } = $props()
6
+ </script>
7
+
8
+ <DropdownMenu {items}>
9
+ {#snippet children({ open, props })}
10
+ <button data-testid="trigger" data-open={open} {...props}>Open</button>
11
+ {/snippet}
12
+ </DropdownMenu>
@@ -0,0 +1,7 @@
1
+ import type { DropdownMenuItem } from './dropdown-menu.types.js';
2
+ type $$ComponentProps = {
3
+ items?: DropdownMenuItem[];
4
+ };
5
+ declare const DropdownMenuTriggerTestWrapper: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type DropdownMenuTriggerTestWrapper = ReturnType<typeof DropdownMenuTriggerTestWrapper>;
7
+ export default DropdownMenuTriggerTestWrapper;
@@ -129,10 +129,6 @@ export type DropdownMenuItem = DropdownMenuItemAction | DropdownMenuItemCheckbox
129
129
  * Configuration for a radio group within the dropdown menu.
130
130
  */
131
131
  export interface DropdownMenuRadioGroup {
132
- /**
133
- * Unique identifier for the radio group.
134
- */
135
- name: string;
136
132
  /**
137
133
  * Currently selected value in the group.
138
134
  */
@@ -178,8 +174,9 @@ export interface DropdownMenuProps extends RootProps, ContentProps {
178
174
  */
179
175
  items?: DropdownMenuItem[];
180
176
  /**
181
- * Radio groups configuration for managing radio item selections.
182
- * @example [{ name: 'theme', value: 'dark', onValueChange: (v) => theme = v }]
177
+ * Radio group configuration for managing radio item selections.
178
+ * Only the first entry is used — all `type: 'radio'` items belong to it.
179
+ * @example [{ value: 'dark', onValueChange: (v) => theme = v }]
183
180
  */
184
181
  radioGroups?: DropdownMenuRadioGroup[];
185
182
  /**
@@ -214,7 +211,9 @@ export interface DropdownMenuProps extends RootProps, ContentProps {
214
211
  */
215
212
  size?: NonNullable<DropdownMenuVariantProps['size']>;
216
213
  /**
217
- * Additional CSS class for the trigger wrapper.
214
+ * Additional CSS class for the dropdown content.
215
+ * Applied only when no trigger `children` are provided (style your own
216
+ * trigger element directly otherwise).
218
217
  */
219
218
  class?: ClassNameValue;
220
219
  /**
@@ -222,11 +221,20 @@ export interface DropdownMenuProps extends RootProps, ContentProps {
222
221
  */
223
222
  ui?: Partial<Record<DropdownMenuSlots, ClassNameValue>>;
224
223
  /**
225
- * Default slot content used as the trigger element.
226
- * When provided, clicking this element opens the dropdown.
224
+ * Trigger content. Spread the provided `props` onto your own focusable
225
+ * element (e.g. a `<Button>`) so the trigger's ARIA and event handlers land
226
+ * on the real control.
227
+ *
228
+ * @example
229
+ * ```svelte
230
+ * {#snippet children({ open, props })}
231
+ * <Button {...props}>{open ? 'Close' : 'Open'}</Button>
232
+ * {/snippet}
233
+ * ```
227
234
  */
228
235
  children?: Snippet<[{
229
236
  open: boolean;
237
+ props: Record<string, unknown>;
230
238
  }]>;
231
239
  /**
232
240
  * Custom content to render at the top of the dropdown menu.
@@ -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
  */