sv5ui 1.1.2 → 1.2.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 (155) hide show
  1. package/README.md +6 -0
  2. package/dist/Alert/Alert.svelte +33 -22
  3. package/dist/Alert/Alert.svelte.d.ts +1 -1
  4. package/dist/Alert/alert.types.d.ts +4 -0
  5. package/dist/Avatar/Avatar.svelte +72 -46
  6. package/dist/Avatar/avatar.types.d.ts +36 -3
  7. package/dist/Avatar/avatar.variants.d.ts +138 -0
  8. package/dist/Avatar/avatar.variants.js +23 -12
  9. package/dist/Avatar/index.d.ts +1 -1
  10. package/dist/AvatarGroup/AvatarGroup.svelte +11 -6
  11. package/dist/AvatarGroup/AvatarGroup.svelte.d.ts +1 -1
  12. package/dist/AvatarGroup/avatar-group.types.d.ts +18 -3
  13. package/dist/AvatarGroup/avatar-group.variants.d.ts +85 -0
  14. package/dist/AvatarGroup/avatar-group.variants.js +19 -29
  15. package/dist/Badge/Badge.svelte +4 -3
  16. package/dist/Badge/Badge.svelte.d.ts +1 -1
  17. package/dist/Badge/badge.types.d.ts +9 -0
  18. package/dist/Breadcrumb/Breadcrumb.svelte +20 -7
  19. package/dist/Breadcrumb/Breadcrumb.svelte.d.ts +1 -1
  20. package/dist/Breadcrumb/breadcrumb.types.d.ts +5 -1
  21. package/dist/Breadcrumb/breadcrumb.variants.d.ts +15 -5
  22. package/dist/Breadcrumb/breadcrumb.variants.js +7 -3
  23. package/dist/Button/Button.svelte +71 -16
  24. package/dist/Button/Button.svelte.d.ts +0 -1
  25. package/dist/Button/button.types.d.ts +61 -2
  26. package/dist/Calendar/Calendar.svelte +4 -0
  27. package/dist/Calendar/Calendar.svelte.d.ts +1 -1
  28. package/dist/Calendar/calendar.types.d.ts +4 -0
  29. package/dist/Card/Card.svelte +5 -4
  30. package/dist/Card/Card.svelte.d.ts +1 -1
  31. package/dist/Card/card.types.d.ts +5 -1
  32. package/dist/Checkbox/Checkbox.svelte +37 -11
  33. package/dist/Checkbox/Checkbox.svelte.d.ts +1 -1
  34. package/dist/Checkbox/checkbox.types.d.ts +16 -1
  35. package/dist/Checkbox/checkbox.variants.d.ts +90 -0
  36. package/dist/Checkbox/checkbox.variants.js +73 -4
  37. package/dist/Chip/Chip.svelte +3 -2
  38. package/dist/Chip/Chip.svelte.d.ts +1 -1
  39. package/dist/Chip/chip.types.d.ts +5 -1
  40. package/dist/Chip/chip.variants.d.ts +135 -45
  41. package/dist/Chip/chip.variants.js +9 -9
  42. package/dist/ContextMenu/ContextMenu.svelte +87 -77
  43. package/dist/ContextMenu/ContextMenu.svelte.d.ts +1 -1
  44. package/dist/ContextMenu/context-menu.types.d.ts +9 -3
  45. package/dist/ContextMenu/context-menu.types.js +1 -1
  46. package/dist/ContextMenu/context-menu.variants.d.ts +74 -160
  47. package/dist/ContextMenu/context-menu.variants.js +63 -95
  48. package/dist/DropdownMenu/DropdownMenu.svelte +37 -43
  49. package/dist/DropdownMenu/DropdownMenu.svelte.d.ts +1 -1
  50. package/dist/DropdownMenu/dropdown-menu.types.d.ts +9 -3
  51. package/dist/DropdownMenu/dropdown-menu.types.js +1 -1
  52. package/dist/DropdownMenu/dropdown-menu.variants.d.ts +79 -230
  53. package/dist/DropdownMenu/dropdown-menu.variants.js +68 -111
  54. package/dist/DropdownMenu/index.d.ts +1 -1
  55. package/dist/Empty/Empty.svelte +68 -33
  56. package/dist/Empty/Empty.svelte.d.ts +1 -1
  57. package/dist/Empty/empty.types.d.ts +26 -9
  58. package/dist/Empty/empty.variants.d.ts +150 -130
  59. package/dist/Empty/empty.variants.js +33 -324
  60. package/dist/FieldGroup/FieldGroup.svelte +11 -6
  61. package/dist/FieldGroup/FieldGroup.svelte.d.ts +1 -1
  62. package/dist/FieldGroup/field-group.types.d.ts +4 -0
  63. package/dist/FormField/FormField.svelte +17 -18
  64. package/dist/FormField/FormField.svelte.d.ts +1 -1
  65. package/dist/FormField/form-field.types.d.ts +4 -0
  66. package/dist/Icon/Icon.svelte +13 -7
  67. package/dist/Icon/icon.types.d.ts +18 -9
  68. package/dist/Input/Input.svelte +30 -29
  69. package/dist/Kbd/Kbd.svelte +13 -3
  70. package/dist/Kbd/Kbd.svelte.d.ts +1 -1
  71. package/dist/Kbd/index.d.ts +1 -1
  72. package/dist/Kbd/kbd.types.d.ts +15 -1
  73. package/dist/Kbd/kbd.variants.d.ts +92 -30
  74. package/dist/Kbd/kbd.variants.js +55 -35
  75. package/dist/Kbd/useKbd.svelte.d.ts +2 -2
  76. package/dist/Kbd/useKbd.svelte.js +34 -41
  77. package/dist/Link/Link.svelte +69 -24
  78. package/dist/Link/Link.svelte.d.ts +1 -1
  79. package/dist/Link/link.types.d.ts +26 -8
  80. package/dist/Link/link.variants.d.ts +35 -60
  81. package/dist/Link/link.variants.js +8 -110
  82. package/dist/Modal/Modal.svelte +9 -1
  83. package/dist/Modal/modal.types.d.ts +5 -0
  84. package/dist/Modal/modal.variants.d.ts +5 -0
  85. package/dist/Modal/modal.variants.js +1 -0
  86. package/dist/Pagination/Pagination.svelte +143 -94
  87. package/dist/Pagination/Pagination.svelte.d.ts +1 -1
  88. package/dist/Pagination/index.d.ts +1 -1
  89. package/dist/Pagination/pagination.types.d.ts +21 -2
  90. package/dist/Pagination/pagination.variants.d.ts +21 -387
  91. package/dist/Pagination/pagination.variants.js +63 -59
  92. package/dist/Popover/Popover.svelte +9 -12
  93. package/dist/Popover/Popover.svelte.d.ts +1 -1
  94. package/dist/Popover/popover.types.d.ts +4 -0
  95. package/dist/Popover/popover.variants.d.ts +5 -75
  96. package/dist/Popover/popover.variants.js +6 -16
  97. package/dist/Progress/Progress.svelte +58 -30
  98. package/dist/Progress/progress.types.d.ts +9 -1
  99. package/dist/Progress/progress.variants.d.ts +55 -25
  100. package/dist/Progress/progress.variants.js +34 -28
  101. package/dist/RadioGroup/RadioGroup.svelte +105 -61
  102. package/dist/RadioGroup/RadioGroup.svelte.d.ts +1 -1
  103. package/dist/RadioGroup/radio-group.types.d.ts +16 -1
  104. package/dist/RadioGroup/radio-group.variants.d.ts +90 -0
  105. package/dist/RadioGroup/radio-group.variants.js +73 -4
  106. package/dist/Select/Select.svelte +9 -6
  107. package/dist/Select/Select.svelte.d.ts +1 -1
  108. package/dist/Select/select.types.d.ts +4 -0
  109. package/dist/SelectMenu/SelectMenu.svelte +436 -0
  110. package/dist/SelectMenu/SelectMenu.svelte.d.ts +5 -0
  111. package/dist/SelectMenu/index.d.ts +2 -0
  112. package/dist/SelectMenu/index.js +1 -0
  113. package/dist/SelectMenu/select-menu.types.d.ts +262 -0
  114. package/dist/SelectMenu/select-menu.types.js +1 -0
  115. package/dist/SelectMenu/select-menu.variants.d.ts +759 -0
  116. package/dist/SelectMenu/select-menu.variants.js +33 -0
  117. package/dist/Separator/Separator.svelte +1 -2
  118. package/dist/Separator/separator.variants.d.ts +1 -5
  119. package/dist/Separator/separator.variants.js +2 -2
  120. package/dist/Skeleton/Skeleton.svelte +18 -2
  121. package/dist/Skeleton/Skeleton.svelte.d.ts +1 -1
  122. package/dist/Skeleton/skeleton.types.d.ts +10 -1
  123. package/dist/Slideover/Slideover.svelte +9 -1
  124. package/dist/Slideover/slideover.types.d.ts +5 -0
  125. package/dist/Slideover/slideover.variants.d.ts +20 -5
  126. package/dist/Slideover/slideover.variants.js +4 -29
  127. package/dist/Switch/Switch.svelte +32 -31
  128. package/dist/Switch/Switch.svelte.d.ts +1 -1
  129. package/dist/Switch/switch.types.d.ts +6 -1
  130. package/dist/Switch/switch.variants.js +6 -6
  131. package/dist/Tabs/Tabs.svelte +6 -9
  132. package/dist/Tabs/Tabs.svelte.d.ts +1 -1
  133. package/dist/Tabs/tabs.types.d.ts +4 -0
  134. package/dist/Tabs/tabs.variants.js +2 -0
  135. package/dist/Textarea/Textarea.svelte +26 -25
  136. package/dist/ThemeModeButton/theme-mode-button.types.d.ts +7 -2
  137. package/dist/Timeline/Timeline.svelte +62 -19
  138. package/dist/Timeline/Timeline.svelte.d.ts +1 -1
  139. package/dist/Timeline/index.d.ts +1 -1
  140. package/dist/Timeline/timeline.types.d.ts +8 -0
  141. package/dist/Tooltip/Tooltip.svelte +12 -10
  142. package/dist/Tooltip/Tooltip.svelte.d.ts +1 -1
  143. package/dist/Tooltip/tooltip.types.d.ts +8 -4
  144. package/dist/Tooltip/tooltip.variants.d.ts +10 -75
  145. package/dist/Tooltip/tooltip.variants.js +8 -17
  146. package/dist/User/User.svelte +13 -9
  147. package/dist/User/User.svelte.d.ts +1 -1
  148. package/dist/User/user.types.d.ts +4 -0
  149. package/dist/User/user.variants.d.ts +60 -0
  150. package/dist/User/user.variants.js +13 -1
  151. package/dist/config.d.ts +4 -0
  152. package/dist/config.js +4 -0
  153. package/dist/index.d.ts +1 -1
  154. package/dist/index.js +1 -1
  155. package/package.json +2 -2
@@ -1,25 +1,26 @@
1
1
  import { SvelteSet } from 'svelte/reactivity';
2
2
  import { kbdKeysMap, kbdKeysPlatformMap } from './kbd.variants.js';
3
3
  let cachedIsMac;
4
- const detectPlatform = () => {
4
+ export function isMac() {
5
5
  if (cachedIsMac === undefined) {
6
6
  cachedIsMac =
7
7
  typeof navigator !== 'undefined' &&
8
8
  /Macintosh|Mac OS|iPhone|iPad|iPod/i.test(navigator.userAgent);
9
9
  }
10
10
  return cachedIsMac;
11
- };
11
+ }
12
12
  /** @internal */
13
13
  export const __resetPlatformCache = () => {
14
14
  cachedIsMac = undefined;
15
15
  };
16
- export const isMac = detectPlatform();
17
- export function resolveKey(value) {
16
+ export function resolveKey(value, platformReady = true) {
18
17
  if (!value)
19
18
  return null;
20
19
  const key = value.toLowerCase();
21
20
  if (key in kbdKeysPlatformMap) {
22
- return isMac ? kbdKeysPlatformMap[key].mac : kbdKeysPlatformMap[key].other;
21
+ if (!platformReady)
22
+ return null;
23
+ return isMac() ? kbdKeysPlatformMap[key].mac : kbdKeysPlatformMap[key].other;
23
24
  }
24
25
  return kbdKeysMap[key] ?? value;
25
26
  }
@@ -42,7 +43,7 @@ export function normalizeKey(eventKey) {
42
43
  const lower = eventKey.toLowerCase();
43
44
  return KEY_NORMALIZE[lower] ?? lower;
44
45
  }
45
- const CAPTURABLE_MODIFIERS = { alt: true, meta: true };
46
+ const CAPTURABLE_MODIFIERS = new Set(['alt', 'meta']);
46
47
  const MODIFIER_PROPS = [
47
48
  ['ctrl', 'ctrlKey'],
48
49
  ['shift', 'shiftKey'],
@@ -142,6 +143,7 @@ function parseShortcuts(shortcuts) {
142
143
  }
143
144
  export function useKbd(options = {}) {
144
145
  const _pressedKeys = new SvelteSet();
146
+ const { captureModifiers = false, repeat: allowRepeat = false, preventDefault: shouldPrevent = true } = options;
145
147
  let _shortcutsRef;
146
148
  let _parsedShortcuts = [];
147
149
  if (typeof options.shortcuts !== 'function' && options.shortcuts) {
@@ -165,7 +167,6 @@ export function useKbd(options = {}) {
165
167
  const t = typeof options.target === 'function' ? options.target() : options.target;
166
168
  return t ?? (typeof window !== 'undefined' ? window : null);
167
169
  }
168
- /** Sync modifier state from event booleans to fix missed keyup (e.g. Alt+Tab) */
169
170
  function reconcileModifiers(event) {
170
171
  for (const [key, prop] of MODIFIER_PROPS) {
171
172
  if (event[prop])
@@ -180,17 +181,15 @@ export function useKbd(options = {}) {
180
181
  reconcileModifiers(event);
181
182
  const key = normalizeKey(event.key);
182
183
  _pressedKeys.add(key);
183
- if (options.captureModifiers && key in CAPTURABLE_MODIFIERS) {
184
+ if (captureModifiers && CAPTURABLE_MODIFIERS.has(key)) {
184
185
  event.preventDefault();
185
186
  }
186
- if (event.repeat && !options.repeat)
187
+ if (event.repeat && !allowRepeat)
187
188
  return;
188
- const bindings = getParsedShortcuts();
189
- for (const binding of bindings) {
189
+ for (const binding of getParsedShortcuts()) {
190
190
  if (matchesShortcut(event, binding)) {
191
- if (options.preventDefault !== false) {
191
+ if (shouldPrevent)
192
192
  event.preventDefault();
193
- }
194
193
  binding.callback(event);
195
194
  }
196
195
  }
@@ -199,12 +198,21 @@ export function useKbd(options = {}) {
199
198
  if (!getEnabled())
200
199
  return;
201
200
  reconcileModifiers(event);
202
- const key = normalizeKey(event.key);
203
- _pressedKeys.delete(key);
201
+ _pressedKeys.delete(normalizeKey(event.key));
204
202
  }
205
203
  function handleClear() {
206
204
  _pressedKeys.clear();
207
205
  }
206
+ const callbacks = {
207
+ keydown: handleKeyDown,
208
+ keyup: handleKeyUp,
209
+ clear: handleClear
210
+ };
211
+ const clearOnlyCallbacks = {
212
+ keydown: () => { },
213
+ keyup: () => { },
214
+ clear: handleClear
215
+ };
208
216
  $effect(() => {
209
217
  if (!getEnabled()) {
210
218
  _pressedKeys.clear();
@@ -213,40 +221,25 @@ export function useKbd(options = {}) {
213
221
  const target = getTarget();
214
222
  if (!target)
215
223
  return;
216
- const isWindowTarget = typeof window !== 'undefined' && target === window;
217
- const callbacks = {
218
- keydown: handleKeyDown,
219
- keyup: handleKeyUp,
220
- clear: handleClear
221
- };
222
- if (isWindowTarget) {
224
+ if (typeof window !== 'undefined' && target === window) {
223
225
  kbdRegistry.register(callbacks);
224
226
  return () => {
225
227
  kbdRegistry.unregister(callbacks);
226
228
  _pressedKeys.clear();
227
229
  };
228
230
  }
229
- else {
230
- const clearOnly = {
231
- keydown: () => { },
232
- keyup: () => { },
233
- clear: handleClear
234
- };
235
- target.addEventListener('keydown', handleKeyDown);
236
- target.addEventListener('keyup', handleKeyUp);
237
- kbdRegistry.register(clearOnly);
238
- return () => {
239
- target.removeEventListener('keydown', handleKeyDown);
240
- target.removeEventListener('keyup', handleKeyUp);
241
- kbdRegistry.unregister(clearOnly);
242
- _pressedKeys.clear();
243
- };
244
- }
231
+ target.addEventListener('keydown', handleKeyDown);
232
+ target.addEventListener('keyup', handleKeyUp);
233
+ kbdRegistry.register(clearOnlyCallbacks);
234
+ return () => {
235
+ target.removeEventListener('keydown', handleKeyDown);
236
+ target.removeEventListener('keyup', handleKeyUp);
237
+ kbdRegistry.unregister(clearOnlyCallbacks);
238
+ _pressedKeys.clear();
239
+ };
245
240
  });
246
241
  return {
247
- isPressed(key) {
248
- return _pressedKeys.has(normalizeKey(key));
249
- },
242
+ isPressed: (key) => _pressedKeys.has(normalizeKey(key)),
250
243
  get pressedKeys() {
251
244
  return _pressedKeys;
252
245
  }
@@ -28,11 +28,15 @@
28
28
  if (mode === false) return true
29
29
  if (mode === 'partial') {
30
30
  for (const [key, value] of linkQuery) {
31
- if (currentQuery.get(key) !== value) return false
31
+ if (!currentQuery.getAll(key).includes(value)) return false
32
32
  }
33
33
  return true
34
34
  }
35
- return linkQuery.toString() === currentQuery.toString()
35
+
36
+ // Exact: check size first (O(1) bail-out), then compare sorted strings
37
+ if (linkQuery.size !== currentQuery.size) return false
38
+ const sorted = (p: URLSearchParams) => new URLSearchParams([...p].sort()).toString()
39
+ return sorted(linkQuery) === sorted(currentQuery)
36
40
  }
37
41
 
38
42
  function isPathnameMatch(linkPath: string, currentPath: string, exactMatch: boolean): boolean {
@@ -53,8 +57,9 @@
53
57
  const config = getComponentConfig('link', linkDefaults)
54
58
 
55
59
  let {
60
+ ref = $bindable(null),
56
61
  href,
57
- color = config.defaultVariants.color,
62
+ type,
58
63
  active,
59
64
  exact = false,
60
65
  exactQuery = false,
@@ -69,25 +74,36 @@
69
74
  ui,
70
75
  target,
71
76
  rel,
77
+ onclick,
72
78
  ...restProps
73
79
  }: Props = $props()
74
80
 
81
+ const isLink = $derived(!!href)
82
+
75
83
  const isExternal = $derived(
76
- external ??
77
- (href.startsWith('http://') || href.startsWith('https://') || href.startsWith('//'))
84
+ isLink &&
85
+ (external ??
86
+ (href!.startsWith('http://') ||
87
+ href!.startsWith('https://') ||
88
+ href!.startsWith('//')))
78
89
  )
79
90
 
80
- const resolvedTarget = $derived(target ?? (isExternal ? '_blank' : undefined))
91
+ const resolvedTarget = $derived(
92
+ isLink ? (target ?? (isExternal ? '_blank' : undefined)) : undefined
93
+ )
81
94
 
82
95
  const resolvedRel = $derived(
83
- rel ?? (isExternal || resolvedTarget === '_blank' ? 'noopener noreferrer' : undefined)
96
+ isLink
97
+ ? (rel ??
98
+ (isExternal || resolvedTarget === '_blank' ? 'noopener noreferrer' : undefined))
99
+ : undefined
84
100
  )
85
101
 
86
102
  const isActive = $derived.by(() => {
87
103
  if (active !== undefined) return active
88
- if (!page.url || isExternal) return false
104
+ if (!isLink || !page.url || isExternal) return false
89
105
 
90
- const link = parseUrl(href, page.url)
106
+ const link = parseUrl(href!, page.url)
91
107
 
92
108
  if (exactHash && link.hash !== page.url.hash) return false
93
109
  if (!isQueryMatch(link.query, page.url.searchParams, exactQuery)) return false
@@ -99,24 +115,53 @@
99
115
  const stateClass = isActive ? activeClass : inactiveClass
100
116
  if (raw) return [className, stateClass].filter(Boolean).join(' ')
101
117
 
102
- const slots = linkVariants({ color, active: isActive, disabled, raw })
118
+ const slots = linkVariants({ active: isActive, disabled, raw })
103
119
  return slots.base({ class: [config.slots.base, stateClass, className, ui?.base] })
104
120
  })
105
121
 
106
122
  const ariaCurrent = $derived(isActive && exact ? ('page' as const) : undefined)
123
+
124
+ function handleClick(e: MouseEvent) {
125
+ if (disabled) {
126
+ e.preventDefault()
127
+ e.stopPropagation()
128
+ return
129
+ }
130
+
131
+ if (typeof onclick === 'function') {
132
+ ;(onclick as (e: MouseEvent) => void)(e)
133
+ }
134
+ }
107
135
  </script>
108
136
 
109
- <!-- eslint-disable svelte/no-navigation-without-resolve -->
110
- <a
111
- href={disabled ? undefined : href}
112
- class={baseClass}
113
- target={resolvedTarget}
114
- rel={resolvedRel}
115
- aria-disabled={disabled ? 'true' : undefined}
116
- aria-current={ariaCurrent}
117
- tabindex={disabled ? -1 : undefined}
118
- {...restProps}
119
- >
120
- <!-- eslint-enable svelte/no-navigation-without-resolve -->
121
- {@render children?.()}
122
- </a>
137
+ {#if isLink}
138
+ <!-- eslint-disable svelte/no-navigation-without-resolve -->
139
+ <a
140
+ bind:this={ref}
141
+ href={disabled ? undefined : href}
142
+ class={baseClass}
143
+ target={resolvedTarget}
144
+ rel={resolvedRel}
145
+ role={disabled ? 'link' : undefined}
146
+ aria-disabled={disabled ? 'true' : undefined}
147
+ aria-current={ariaCurrent}
148
+ tabindex={disabled ? -1 : undefined}
149
+ onclick={handleClick}
150
+ {...restProps}
151
+ >
152
+ <!-- eslint-enable svelte/no-navigation-without-resolve -->
153
+ {@render children?.()}
154
+ </a>
155
+ {:else}
156
+ <button
157
+ bind:this={ref}
158
+ type={type ?? 'button'}
159
+ class={baseClass}
160
+ {disabled}
161
+ aria-current={ariaCurrent}
162
+ onclick={handleClick}
163
+ {...restProps}
164
+ >
165
+ {@render children?.()}
166
+ </button>
167
+ {/if}
@@ -1,5 +1,5 @@
1
1
  import type { LinkProps } from './link.types.js';
2
2
  export type Props = LinkProps;
3
- declare const Link: import("svelte").Component<LinkProps, {}, "">;
3
+ declare const Link: import("svelte").Component<LinkProps, {}, "ref">;
4
4
  type Link = ReturnType<typeof Link>;
5
5
  export default Link;
@@ -1,16 +1,17 @@
1
- import type { HTMLAnchorAttributes } from 'svelte/elements';
2
- import type { LinkVariantProps, LinkSlots } from './link.variants.js';
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import type { LinkSlots } from './link.variants.js';
3
4
  import type { ClassNameValue } from 'tailwind-merge';
4
- export type LinkProps = Omit<HTMLAnchorAttributes, 'class' | 'href'> & {
5
+ export type LinkProps = Omit<HTMLAttributes<HTMLElement>, 'class'> & {
5
6
  /**
6
- * The destination URL for the anchor element.
7
+ * Bindable reference to the root DOM element.
7
8
  */
8
- href: string;
9
+ ref?: HTMLElement | null;
9
10
  /**
10
- * Sets the color scheme applied to the link.
11
- * @default 'primary'
11
+ * The destination URL for the anchor element.
12
+ * When omitted, renders as a `<button>` element.
12
13
  */
13
- color?: NonNullable<LinkVariantProps['color']>;
14
+ href?: string;
14
15
  /**
15
16
  * Overrides the auto-detected active state.
16
17
  * When omitted, the active state is inferred from the current route.
@@ -57,6 +58,19 @@ export type LinkProps = Omit<HTMLAnchorAttributes, 'class' | 'href'> & {
57
58
  * Auto-detected from the `href` when omitted.
58
59
  */
59
60
  external?: boolean;
61
+ /**
62
+ * The button type attribute. Only applies when rendering as `<button>`.
63
+ * @default 'button'
64
+ */
65
+ type?: 'button' | 'submit' | 'reset';
66
+ /**
67
+ * The link target attribute. Only applies when rendering as `<a>`.
68
+ */
69
+ target?: string;
70
+ /**
71
+ * The link rel attribute. Only applies when rendering as `<a>`.
72
+ */
73
+ rel?: string;
60
74
  /**
61
75
  * Additional CSS classes for the root element.
62
76
  */
@@ -65,4 +79,8 @@ export type LinkProps = Omit<HTMLAnchorAttributes, 'class' | 'href'> & {
65
79
  * Override styles for specific link slots.
66
80
  */
67
81
  ui?: Partial<Record<LinkSlots, ClassNameValue>>;
82
+ /**
83
+ * Content rendered inside the link.
84
+ */
85
+ children?: Snippet;
68
86
  };
@@ -1,17 +1,12 @@
1
1
  import { type VariantProps } from 'tailwind-variants';
2
2
  export declare const linkVariants: import("tailwind-variants").TVReturnType<{
3
- color: {
4
- primary: string;
5
- secondary: string;
6
- tertiary: string;
7
- success: string;
8
- warning: string;
9
- error: string;
10
- info: string;
11
- };
12
3
  active: {
13
- true: string;
14
- false: string;
4
+ true: {
5
+ base: string;
6
+ };
7
+ false: {
8
+ base: string;
9
+ };
15
10
  };
16
11
  disabled: {
17
12
  true: {
@@ -24,20 +19,15 @@ export declare const linkVariants: import("tailwind-variants").TVReturnType<{
24
19
  false: string;
25
20
  };
26
21
  }, {
27
- base: string[];
22
+ base: string;
28
23
  }, undefined, {
29
- color: {
30
- primary: string;
31
- secondary: string;
32
- tertiary: string;
33
- success: string;
34
- warning: string;
35
- error: string;
36
- info: string;
37
- };
38
24
  active: {
39
- true: string;
40
- false: string;
25
+ true: {
26
+ base: string;
27
+ };
28
+ false: {
29
+ base: string;
30
+ };
41
31
  };
42
32
  disabled: {
43
33
  true: {
@@ -50,20 +40,15 @@ export declare const linkVariants: import("tailwind-variants").TVReturnType<{
50
40
  false: string;
51
41
  };
52
42
  }, {
53
- base: string[];
43
+ base: string;
54
44
  }, import("tailwind-variants").TVReturnType<{
55
- color: {
56
- primary: string;
57
- secondary: string;
58
- tertiary: string;
59
- success: string;
60
- warning: string;
61
- error: string;
62
- info: string;
63
- };
64
45
  active: {
65
- true: string;
66
- false: string;
46
+ true: {
47
+ base: string;
48
+ };
49
+ false: {
50
+ base: string;
51
+ };
67
52
  };
68
53
  disabled: {
69
54
  true: {
@@ -76,24 +61,19 @@ export declare const linkVariants: import("tailwind-variants").TVReturnType<{
76
61
  false: string;
77
62
  };
78
63
  }, {
79
- base: string[];
64
+ base: string;
80
65
  }, undefined, unknown, unknown, undefined>>;
81
66
  export type LinkVariantProps = VariantProps<typeof linkVariants>;
82
67
  export type LinkSlots = keyof ReturnType<typeof linkVariants>;
83
68
  export declare const linkDefaults: {
84
69
  defaultVariants: import("tailwind-variants").TVDefaultVariants<{
85
- color: {
86
- primary: string;
87
- secondary: string;
88
- tertiary: string;
89
- success: string;
90
- warning: string;
91
- error: string;
92
- info: string;
93
- };
94
70
  active: {
95
- true: string;
96
- false: string;
71
+ true: {
72
+ base: string;
73
+ };
74
+ false: {
75
+ base: string;
76
+ };
97
77
  };
98
78
  disabled: {
99
79
  true: {
@@ -106,20 +86,15 @@ export declare const linkDefaults: {
106
86
  false: string;
107
87
  };
108
88
  }, {
109
- base: string[];
89
+ base: string;
110
90
  }, {
111
- color: {
112
- primary: string;
113
- secondary: string;
114
- tertiary: string;
115
- success: string;
116
- warning: string;
117
- error: string;
118
- info: string;
119
- };
120
91
  active: {
121
- true: string;
122
- false: string;
92
+ true: {
93
+ base: string;
94
+ };
95
+ false: {
96
+ base: string;
97
+ };
123
98
  };
124
99
  disabled: {
125
100
  true: {
@@ -132,7 +107,7 @@ export declare const linkDefaults: {
132
107
  false: string;
133
108
  };
134
109
  }, {
135
- base: string[];
110
+ base: string;
136
111
  }>;
137
112
  slots: Partial<Record<LinkSlots, string>>;
138
113
  };
@@ -1,25 +1,16 @@
1
1
  import { tv } from 'tailwind-variants';
2
2
  export const linkVariants = tv({
3
3
  slots: {
4
- base: [
5
- 'inline-flex items-center',
6
- 'transition-colors duration-200',
7
- 'focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary'
8
- ]
4
+ base: 'focus-visible:outline-primary'
9
5
  },
10
6
  variants: {
11
- color: {
12
- primary: '',
13
- secondary: '',
14
- tertiary: '',
15
- success: '',
16
- warning: '',
17
- error: '',
18
- info: ''
19
- },
20
7
  active: {
21
- true: '',
22
- false: ''
8
+ true: {
9
+ base: 'text-primary'
10
+ },
11
+ false: {
12
+ base: 'text-on-surface-variant'
13
+ }
23
14
  },
24
15
  disabled: {
25
16
  true: {
@@ -33,107 +24,14 @@ export const linkVariants = tv({
33
24
  }
34
25
  },
35
26
  compoundVariants: [
36
- // ========== PRIMARY ==========
37
- {
38
- color: 'primary',
39
- active: true,
40
- raw: false,
41
- class: { base: 'text-primary' }
42
- },
43
- {
44
- color: 'primary',
45
- active: false,
46
- disabled: false,
47
- raw: false,
48
- class: { base: 'text-primary/80 hover:text-primary' }
49
- },
50
- // ========== SECONDARY ==========
51
- {
52
- color: 'secondary',
53
- active: true,
54
- raw: false,
55
- class: { base: 'text-secondary' }
56
- },
57
- {
58
- color: 'secondary',
59
- active: false,
60
- disabled: false,
61
- raw: false,
62
- class: { base: 'text-secondary/80 hover:text-secondary' }
63
- },
64
- // ========== TERTIARY ==========
65
- {
66
- color: 'tertiary',
67
- active: true,
68
- raw: false,
69
- class: { base: 'text-tertiary' }
70
- },
71
- {
72
- color: 'tertiary',
73
- active: false,
74
- disabled: false,
75
- raw: false,
76
- class: { base: 'text-tertiary/80 hover:text-tertiary' }
77
- },
78
- // ========== SUCCESS ==========
79
- {
80
- color: 'success',
81
- active: true,
82
- raw: false,
83
- class: { base: 'text-success' }
84
- },
85
- {
86
- color: 'success',
87
- active: false,
88
- disabled: false,
89
- raw: false,
90
- class: { base: 'text-success/80 hover:text-success' }
91
- },
92
- // ========== WARNING ==========
93
- {
94
- color: 'warning',
95
- active: true,
96
- raw: false,
97
- class: { base: 'text-warning' }
98
- },
99
- {
100
- color: 'warning',
101
- active: false,
102
- disabled: false,
103
- raw: false,
104
- class: { base: 'text-warning/80 hover:text-warning' }
105
- },
106
- // ========== ERROR ==========
107
- {
108
- color: 'error',
109
- active: true,
110
- raw: false,
111
- class: { base: 'text-error' }
112
- },
113
- {
114
- color: 'error',
115
- active: false,
116
- disabled: false,
117
- raw: false,
118
- class: { base: 'text-error/80 hover:text-error' }
119
- },
120
- // ========== INFO ==========
121
- {
122
- color: 'info',
123
- active: true,
124
- raw: false,
125
- class: { base: 'text-info' }
126
- },
127
27
  {
128
- color: 'info',
129
28
  active: false,
130
29
  disabled: false,
131
30
  raw: false,
132
- class: { base: 'text-info/80 hover:text-info' }
31
+ class: { base: 'hover:text-on-surface transition-colors' }
133
32
  }
134
33
  ],
135
34
  defaultVariants: {
136
- color: 'primary',
137
35
  active: false,
138
36
  disabled: false,
139
37
  raw: false
@@ -40,6 +40,7 @@
40
40
  header: headerSlot,
41
41
  titleSlot,
42
42
  descriptionSlot,
43
+ actions: actionsSlot,
43
44
  body: bodySlot,
44
45
  footer: footerSlot,
45
46
  closeSlot
@@ -51,7 +52,9 @@
51
52
  const hasTitle = $derived(!!title || !!titleSlot)
52
53
  const hasDescription = $derived(!!description || !!descriptionSlot)
53
54
  const hasHeading = $derived(hasTitle || hasDescription)
54
- const hasHeader = $derived(!!headerSlot || hasHeading || showClose || !!closeSlot)
55
+ const hasHeader = $derived(
56
+ !!headerSlot || hasHeading || !!actionsSlot || showClose || !!closeSlot
57
+ )
55
58
 
56
59
  const variantSlots = $derived(
57
60
  modalVariants({ transition, fullscreen, overlay: showOverlay, scrollable })
@@ -66,6 +69,7 @@
66
69
  description: variantSlots.description({
67
70
  class: [config.slots.description, ui?.description]
68
71
  }),
72
+ actions: variantSlots.actions({ class: [config.slots.actions, ui?.actions] }),
69
73
  body: variantSlots.body({ class: [config.slots.body, ui?.body] }),
70
74
  footer: variantSlots.footer({ class: [config.slots.footer, ui?.footer] }),
71
75
  close: variantSlots.close({ class: [config.slots.close, ui?.close] })
@@ -132,6 +136,10 @@
132
136
  {/if}
133
137
  </div>
134
138
 
139
+ {#if actionsSlot}
140
+ <div class={classes.actions}>{@render actionsSlot()}</div>
141
+ {/if}
142
+
135
143
  {#if showClose || closeSlot}
136
144
  <Dialog.Close>
137
145
  {#snippet child({ props })}