sv5ui-plus 4.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 (214) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/dist/adapters/notify.d.ts +29 -0
  4. package/dist/adapters/notify.js +88 -0
  5. package/dist/compat.d.ts +6 -0
  6. package/dist/compat.js +6 -0
  7. package/dist/components/BentoGrid/BentoCard.svelte +45 -0
  8. package/dist/components/BentoGrid/BentoCard.svelte.d.ts +4 -0
  9. package/dist/components/BentoGrid/BentoGrid.svelte +9 -0
  10. package/dist/components/BentoGrid/BentoGrid.svelte.d.ts +4 -0
  11. package/dist/components/BentoGrid/bento-grid.types.d.ts +47 -0
  12. package/dist/components/BentoGrid/bento-grid.types.js +1 -0
  13. package/dist/components/BentoGrid/bento-grid.variants.d.ts +30 -0
  14. package/dist/components/BentoGrid/bento-grid.variants.js +16 -0
  15. package/dist/components/BentoGrid/index.d.ts +5 -0
  16. package/dist/components/BentoGrid/index.js +5 -0
  17. package/dist/components/Chart/Chart.svelte +62 -0
  18. package/dist/components/Chart/Chart.svelte.d.ts +4 -0
  19. package/dist/components/Chart/chart.types.d.ts +16 -0
  20. package/dist/components/Chart/chart.types.js +1 -0
  21. package/dist/components/Chart/index.d.ts +2 -0
  22. package/dist/components/Chart/index.js +2 -0
  23. package/dist/components/Chat/ChatBubble.svelte +30 -0
  24. package/dist/components/Chat/ChatBubble.svelte.d.ts +4 -0
  25. package/dist/components/Chat/ChatInput.svelte +50 -0
  26. package/dist/components/Chat/ChatInput.svelte.d.ts +4 -0
  27. package/dist/components/Chat/ChatMessage.svelte +15 -0
  28. package/dist/components/Chat/ChatMessage.svelte.d.ts +4 -0
  29. package/dist/components/Chat/chat.types.d.ts +63 -0
  30. package/dist/components/Chat/chat.types.js +1 -0
  31. package/dist/components/Chat/chat.variants.d.ts +117 -0
  32. package/dist/components/Chat/chat.variants.js +47 -0
  33. package/dist/components/Chat/index.d.ts +6 -0
  34. package/dist/components/Chat/index.js +6 -0
  35. package/dist/components/CodeBlock/CodeBlock.svelte +66 -0
  36. package/dist/components/CodeBlock/CodeBlock.svelte.d.ts +5 -0
  37. package/dist/components/CodeBlock/code-block.types.d.ts +15 -0
  38. package/dist/components/CodeBlock/code-block.types.js +1 -0
  39. package/dist/components/CodeBlock/code-block.variants.d.ts +288 -0
  40. package/dist/components/CodeBlock/code-block.variants.js +69 -0
  41. package/dist/components/CodeBlock/index.d.ts +2 -0
  42. package/dist/components/CodeBlock/index.js +1 -0
  43. package/dist/components/ColorPicker/ColorPicker.svelte +109 -0
  44. package/dist/components/ColorPicker/ColorPicker.svelte.d.ts +4 -0
  45. package/dist/components/ColorPicker/color-picker.types.d.ts +26 -0
  46. package/dist/components/ColorPicker/color-picker.types.js +1 -0
  47. package/dist/components/ColorPicker/color-picker.variants.d.ts +69 -0
  48. package/dist/components/ColorPicker/color-picker.variants.js +13 -0
  49. package/dist/components/ColorPicker/index.d.ts +4 -0
  50. package/dist/components/ColorPicker/index.js +4 -0
  51. package/dist/components/ConfirmDialog/ConfirmDialog.svelte +175 -0
  52. package/dist/components/ConfirmDialog/ConfirmDialog.svelte.d.ts +10 -0
  53. package/dist/components/ConfirmDialog/confirm.d.ts +9 -0
  54. package/dist/components/ConfirmDialog/confirm.js +24 -0
  55. package/dist/components/ConfirmDialog/confirm.types.d.ts +21 -0
  56. package/dist/components/ConfirmDialog/confirm.types.js +1 -0
  57. package/dist/components/ConfirmDialog/index.d.ts +4 -0
  58. package/dist/components/ConfirmDialog/index.js +2 -0
  59. package/dist/components/Fonts/Fonts.svelte +32 -0
  60. package/dist/components/Fonts/Fonts.svelte.d.ts +5 -0
  61. package/dist/components/Fonts/fonts.d.ts +12 -0
  62. package/dist/components/Fonts/fonts.js +126 -0
  63. package/dist/components/Fonts/fonts.types.d.ts +40 -0
  64. package/dist/components/Fonts/fonts.types.js +1 -0
  65. package/dist/components/Fonts/index.d.ts +3 -0
  66. package/dist/components/Fonts/index.js +2 -0
  67. package/dist/components/List/List.svelte +14 -0
  68. package/dist/components/List/List.svelte.d.ts +4 -0
  69. package/dist/components/List/ListItem.svelte +64 -0
  70. package/dist/components/List/ListItem.svelte.d.ts +4 -0
  71. package/dist/components/List/index.d.ts +5 -0
  72. package/dist/components/List/index.js +5 -0
  73. package/dist/components/List/list.types.d.ts +62 -0
  74. package/dist/components/List/list.types.js +1 -0
  75. package/dist/components/List/list.variants.d.ts +99 -0
  76. package/dist/components/List/list.variants.js +42 -0
  77. package/dist/components/LocaleButton/LocaleButton.svelte +181 -0
  78. package/dist/components/LocaleButton/LocaleButton.svelte.d.ts +5 -0
  79. package/dist/components/LocaleButton/index.d.ts +2 -0
  80. package/dist/components/LocaleButton/index.js +1 -0
  81. package/dist/components/LocaleButton/locale-button.types.d.ts +208 -0
  82. package/dist/components/LocaleButton/locale-button.types.js +1 -0
  83. package/dist/components/LocaleButton/locale-button.variants.d.ts +245 -0
  84. package/dist/components/LocaleButton/locale-button.variants.js +98 -0
  85. package/dist/components/Marquee/Marquee.svelte +50 -0
  86. package/dist/components/Marquee/Marquee.svelte.d.ts +4 -0
  87. package/dist/components/Marquee/index.d.ts +4 -0
  88. package/dist/components/Marquee/index.js +4 -0
  89. package/dist/components/Marquee/marquee.types.d.ts +38 -0
  90. package/dist/components/Marquee/marquee.types.js +1 -0
  91. package/dist/components/Marquee/marquee.variants.d.ts +78 -0
  92. package/dist/components/Marquee/marquee.variants.js +28 -0
  93. package/dist/components/NavigationMenu/NavigationMenu.svelte +623 -0
  94. package/dist/components/NavigationMenu/NavigationMenu.svelte.d.ts +5 -0
  95. package/dist/components/NavigationMenu/index.d.ts +2 -0
  96. package/dist/components/NavigationMenu/index.js +1 -0
  97. package/dist/components/NavigationMenu/navigation-menu.types.d.ts +245 -0
  98. package/dist/components/NavigationMenu/navigation-menu.types.js +1 -0
  99. package/dist/components/NavigationMenu/navigation-menu.variants.d.ts +543 -0
  100. package/dist/components/NavigationMenu/navigation-menu.variants.js +287 -0
  101. package/dist/components/NumberTicker/NumberTicker.svelte +59 -0
  102. package/dist/components/NumberTicker/NumberTicker.svelte.d.ts +4 -0
  103. package/dist/components/NumberTicker/index.d.ts +4 -0
  104. package/dist/components/NumberTicker/index.js +4 -0
  105. package/dist/components/NumberTicker/number-ticker.types.d.ts +26 -0
  106. package/dist/components/NumberTicker/number-ticker.types.js +1 -0
  107. package/dist/components/NumberTicker/number-ticker.variants.d.ts +27 -0
  108. package/dist/components/NumberTicker/number-ticker.variants.js +6 -0
  109. package/dist/components/PasswordInput/PasswordInput.svelte +182 -0
  110. package/dist/components/PasswordInput/PasswordInput.svelte.d.ts +5 -0
  111. package/dist/components/PasswordInput/index.d.ts +2 -0
  112. package/dist/components/PasswordInput/index.js +1 -0
  113. package/dist/components/PasswordInput/password-input.types.d.ts +111 -0
  114. package/dist/components/PasswordInput/password-input.types.js +1 -0
  115. package/dist/components/PasswordInput/password-input.variants.d.ts +273 -0
  116. package/dist/components/PasswordInput/password-input.variants.js +105 -0
  117. package/dist/components/Prose/Prose.svelte +13 -0
  118. package/dist/components/Prose/Prose.svelte.d.ts +4 -0
  119. package/dist/components/Prose/index.d.ts +4 -0
  120. package/dist/components/Prose/index.js +4 -0
  121. package/dist/components/Prose/prose.types.d.ts +22 -0
  122. package/dist/components/Prose/prose.types.js +1 -0
  123. package/dist/components/Prose/prose.variants.d.ts +45 -0
  124. package/dist/components/Prose/prose.variants.js +45 -0
  125. package/dist/components/Resizable/Resizable.svelte +9 -0
  126. package/dist/components/Resizable/Resizable.svelte.d.ts +4 -0
  127. package/dist/components/Resizable/index.d.ts +4 -0
  128. package/dist/components/Resizable/index.js +4 -0
  129. package/dist/components/Resizable/resizable.types.d.ts +18 -0
  130. package/dist/components/Resizable/resizable.types.js +1 -0
  131. package/dist/components/Resizable/resizable.variants.d.ts +48 -0
  132. package/dist/components/Resizable/resizable.variants.js +17 -0
  133. package/dist/components/ScrollArea/ScrollArea.svelte +54 -0
  134. package/dist/components/ScrollArea/ScrollArea.svelte.d.ts +4 -0
  135. package/dist/components/ScrollArea/index.d.ts +4 -0
  136. package/dist/components/ScrollArea/index.js +4 -0
  137. package/dist/components/ScrollArea/scroll-area.types.d.ts +27 -0
  138. package/dist/components/ScrollArea/scroll-area.types.js +1 -0
  139. package/dist/components/ScrollArea/scroll-area.variants.d.ts +45 -0
  140. package/dist/components/ScrollArea/scroll-area.variants.js +27 -0
  141. package/dist/components/Search/Search.svelte +94 -0
  142. package/dist/components/Search/Search.svelte.d.ts +4 -0
  143. package/dist/components/Search/index.d.ts +2 -0
  144. package/dist/components/Search/index.js +1 -0
  145. package/dist/components/Search/search.types.d.ts +40 -0
  146. package/dist/components/Search/search.types.js +1 -0
  147. package/dist/components/SortableGroup/SortableGroup.svelte +53 -0
  148. package/dist/components/SortableGroup/SortableGroup.svelte.d.ts +13 -0
  149. package/dist/components/SortableGroup/index.d.ts +1 -0
  150. package/dist/components/SortableGroup/index.js +1 -0
  151. package/dist/components/SortableList/SortableList.svelte +81 -0
  152. package/dist/components/SortableList/SortableList.svelte.d.ts +26 -0
  153. package/dist/components/SortableList/index.d.ts +2 -0
  154. package/dist/components/SortableList/index.js +1 -0
  155. package/dist/components/SortableList/sortable-list.types.d.ts +21 -0
  156. package/dist/components/SortableList/sortable-list.types.js +1 -0
  157. package/dist/components/Spotlight/Spotlight.svelte +31 -0
  158. package/dist/components/Spotlight/Spotlight.svelte.d.ts +4 -0
  159. package/dist/components/Spotlight/index.d.ts +4 -0
  160. package/dist/components/Spotlight/index.js +4 -0
  161. package/dist/components/Spotlight/spotlight.types.d.ts +22 -0
  162. package/dist/components/Spotlight/spotlight.types.js +1 -0
  163. package/dist/components/Spotlight/spotlight.variants.d.ts +39 -0
  164. package/dist/components/Spotlight/spotlight.variants.js +8 -0
  165. package/dist/components/TableBulkActionBar/TableBulkActionBar.svelte +38 -0
  166. package/dist/components/TableBulkActionBar/TableBulkActionBar.svelte.d.ts +5 -0
  167. package/dist/components/TableBulkActionBar/index.d.ts +2 -0
  168. package/dist/components/TableBulkActionBar/index.js +1 -0
  169. package/dist/components/TableBulkActionBar/table-bulk-action-bar.types.d.ts +31 -0
  170. package/dist/components/TableBulkActionBar/table-bulk-action-bar.types.js +1 -0
  171. package/dist/components/TableBulkActionBar/table-bulk-action-bar.variants.d.ts +52 -0
  172. package/dist/components/TableBulkActionBar/table-bulk-action-bar.variants.js +10 -0
  173. package/dist/components/TreeView/TreeView.svelte +95 -0
  174. package/dist/components/TreeView/TreeView.svelte.d.ts +4 -0
  175. package/dist/components/TreeView/index.d.ts +4 -0
  176. package/dist/components/TreeView/index.js +3 -0
  177. package/dist/components/TreeView/tree-view.types.d.ts +68 -0
  178. package/dist/components/TreeView/tree-view.types.js +1 -0
  179. package/dist/components/TreeView/tree-view.variants.d.ts +69 -0
  180. package/dist/components/TreeView/tree-view.variants.js +30 -0
  181. package/dist/config.d.ts +91 -0
  182. package/dist/config.js +124 -0
  183. package/dist/editor.d.ts +2 -0
  184. package/dist/editor.js +2 -0
  185. package/dist/form/resolve-form-field-name.d.ts +5 -0
  186. package/dist/form/resolve-form-field-name.js +11 -0
  187. package/dist/hooks/internal/DragDropProviderScope.svelte +29 -0
  188. package/dist/hooks/internal/DragDropProviderScope.svelte.d.ts +7 -0
  189. package/dist/hooks/internal/SortableProvider.svelte +68 -0
  190. package/dist/hooks/internal/SortableProvider.svelte.d.ts +30 -0
  191. package/dist/hooks/internal/drag-drop-context.d.ts +12 -0
  192. package/dist/hooks/internal/drag-drop-context.js +1 -0
  193. package/dist/hooks/internal/sortable-context.d.ts +26 -0
  194. package/dist/hooks/internal/sortable-context.js +2 -0
  195. package/dist/hooks/useDragDrop/index.d.ts +1 -0
  196. package/dist/hooks/useDragDrop/index.js +1 -0
  197. package/dist/hooks/useDragDrop/useDragDrop.svelte.d.ts +40 -0
  198. package/dist/hooks/useDragDrop/useDragDrop.svelte.js +128 -0
  199. package/dist/hooks/useSortable/index.d.ts +2 -0
  200. package/dist/hooks/useSortable/index.js +2 -0
  201. package/dist/hooks/useSortable/sortable-utils.d.ts +1 -0
  202. package/dist/hooks/useSortable/sortable-utils.js +13 -0
  203. package/dist/hooks/useSortable/useSortable.svelte.d.ts +60 -0
  204. package/dist/hooks/useSortable/useSortable.svelte.js +151 -0
  205. package/dist/hooks/utils.d.ts +1 -0
  206. package/dist/hooks/utils.js +3 -0
  207. package/dist/index.d.ts +27 -0
  208. package/dist/index.js +30 -0
  209. package/dist/mcp/cursor.mcp.json +12 -0
  210. package/dist/mcp/install-template.mjs +55 -0
  211. package/dist/mcp/server.mjs +338 -0
  212. package/dist/mcp/sv5ui-plus-docs.data.json +17 -0
  213. package/dist/theme.css +64 -0
  214. package/package.json +176 -0
@@ -0,0 +1,126 @@
1
+ const defaultSansFallback = "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
2
+ const defaultMonoFallback = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace";
3
+ export const defaultFontFamilies = [
4
+ {
5
+ name: 'Inter',
6
+ variable: '--font-sans-family',
7
+ weights: [400, 500, 600, 700]
8
+ },
9
+ {
10
+ name: 'Inter',
11
+ variable: '--font-heading-family',
12
+ weights: [400, 500, 600, 700]
13
+ },
14
+ {
15
+ name: 'JetBrains Mono',
16
+ variable: '--font-mono-family',
17
+ weights: [400, 500, 700]
18
+ }
19
+ ];
20
+ export const fontsDefaults = {
21
+ families: defaultFontFamilies,
22
+ display: 'swap',
23
+ preconnect: true
24
+ };
25
+ function isLocalFont(font) {
26
+ return font.provider === 'local';
27
+ }
28
+ function normalizeFontName(name) {
29
+ return name.trim().replace(/\s+/g, ' ');
30
+ }
31
+ function getFontWeights(weights) {
32
+ const normalized = (weights ?? [400]).filter((weight, index, source) => Number.isInteger(weight) &&
33
+ weight >= 100 &&
34
+ weight <= 900 &&
35
+ source.indexOf(weight) === index);
36
+ return normalized.length > 0 ? [...normalized].sort((a, b) => a - b) : [400];
37
+ }
38
+ function getFontStyles(styles) {
39
+ const normalized = (styles ?? ['normal']).filter((style, index, source) => (style === 'normal' || style === 'italic') && source.indexOf(style) === index);
40
+ if (normalized.length === 0)
41
+ return ['normal'];
42
+ if (normalized.includes('normal') && normalized.includes('italic')) {
43
+ return ['normal', 'italic'];
44
+ }
45
+ return normalized[0] === 'italic' ? ['italic'] : ['normal'];
46
+ }
47
+ function encodeFamilyName(name) {
48
+ return normalizeFontName(name).replaceAll(' ', '+');
49
+ }
50
+ function escapeFontName(name) {
51
+ return name.replaceAll('\\', '\\\\').replaceAll("'", "\\'");
52
+ }
53
+ function getDefaultFallback(variable) {
54
+ return variable?.includes('mono') ? defaultMonoFallback : defaultSansFallback;
55
+ }
56
+ function buildFamilyQuery(font) {
57
+ const name = encodeFamilyName(font.name);
58
+ const weights = getFontWeights(font.weights);
59
+ const styles = getFontStyles(font.styles);
60
+ if (styles.length === 1 && styles[0] === 'normal') {
61
+ return `${name}:wght@${weights.join(';')}`;
62
+ }
63
+ const pairs = styles.flatMap((style) => weights.map((weight) => `${style === 'italic' ? 1 : 0},${weight}`));
64
+ return `${name}:ital,wght@${pairs.join(';')}`;
65
+ }
66
+ export function buildGoogleFontsUrl(fonts, display = 'swap') {
67
+ const families = fonts
68
+ .filter((font) => !isLocalFont(font))
69
+ .map((font) => buildFamilyQuery(font))
70
+ .filter((family, index, source) => family.length > 0 && source.indexOf(family) === index);
71
+ if (families.length === 0)
72
+ return '';
73
+ const familyParams = families
74
+ .map((f) => `family=${encodeURIComponent(f).replaceAll('%2B', '+')}`)
75
+ .join('&');
76
+ return `https://fonts.googleapis.com/css2?${familyParams}&display=${display}`;
77
+ }
78
+ export function buildFontFamily(font) {
79
+ const family = `'${escapeFontName(normalizeFontName(font.name))}'`;
80
+ return `${family}, ${font.fallback ?? getDefaultFallback(font.variable)}`;
81
+ }
82
+ function buildLocalFontSourceReference(source) {
83
+ const url = `url('${source.src.replaceAll("'", "\\'")}')`;
84
+ return source.format ? `${url} format('${source.format}')` : url;
85
+ }
86
+ export function buildLocalFontFaceCss(fonts, defaultDisplay = 'swap') {
87
+ const declarations = fonts.filter(isLocalFont).flatMap((font) => font.sources.map((source) => {
88
+ const lines = [
89
+ '@font-face {',
90
+ ` font-family: '${escapeFontName(normalizeFontName(font.name))}';`,
91
+ ` src: ${buildLocalFontSourceReference(source)};`,
92
+ ` font-display: ${font.display ?? defaultDisplay};`,
93
+ ` font-style: ${source.style ?? 'normal'};`,
94
+ ` font-weight: ${source.weight ?? 400};`
95
+ ];
96
+ if (source.unicodeRange) {
97
+ lines.push(` unicode-range: ${source.unicodeRange};`);
98
+ }
99
+ lines.push('}');
100
+ return lines.join('\n');
101
+ }));
102
+ return declarations.join('\n\n');
103
+ }
104
+ export function buildFontVariablesCss(fonts) {
105
+ const declarations = fonts
106
+ .filter((font) => typeof font.variable === 'string' && font.variable.length > 2)
107
+ .map((font) => ` ${font.variable}: ${buildFontFamily(font)};`);
108
+ if (declarations.length === 0)
109
+ return '';
110
+ return `:root {\n${declarations.join('\n')}\n}`;
111
+ }
112
+ export function resolveFontsOptions(config) {
113
+ if (config === false)
114
+ return false;
115
+ if (config === true) {
116
+ return fontsDefaults;
117
+ }
118
+ if (config && typeof config === 'object') {
119
+ return {
120
+ families: config.families ?? fontsDefaults.families,
121
+ display: config.display ?? fontsDefaults.display,
122
+ preconnect: config.preconnect ?? fontsDefaults.preconnect
123
+ };
124
+ }
125
+ return fontsDefaults;
126
+ }
@@ -0,0 +1,40 @@
1
+ export type GoogleFontStyle = 'normal' | 'italic';
2
+ export type GoogleFontDisplay = 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
3
+ export type FontProvider = 'google' | 'local';
4
+ export type GoogleFontDefinition = {
5
+ provider?: 'google';
6
+ name: string;
7
+ weights?: number[];
8
+ styles?: GoogleFontStyle[];
9
+ variable?: `--${string}`;
10
+ fallback?: string;
11
+ };
12
+ export type LocalFontSourceFormat = 'woff2' | 'woff' | 'truetype' | 'opentype' | 'embedded-opentype' | 'svg';
13
+ export type LocalFontSource = {
14
+ src: string;
15
+ format?: LocalFontSourceFormat;
16
+ weight?: number | `${number} ${number}`;
17
+ style?: GoogleFontStyle;
18
+ unicodeRange?: string;
19
+ };
20
+ export type LocalFontDefinition = {
21
+ provider: 'local';
22
+ name: string;
23
+ sources: LocalFontSource[];
24
+ variable?: `--${string}`;
25
+ fallback?: string;
26
+ display?: GoogleFontDisplay;
27
+ };
28
+ export type FontDefinition = GoogleFontDefinition | LocalFontDefinition;
29
+ export type FontsOptions = {
30
+ families?: FontDefinition[];
31
+ display?: GoogleFontDisplay;
32
+ preconnect?: boolean;
33
+ };
34
+ export type FontsConfig = boolean | FontsOptions;
35
+ export type FontsProps = {
36
+ fonts?: FontDefinition[];
37
+ families?: FontDefinition[];
38
+ display?: GoogleFontDisplay;
39
+ preconnect?: boolean;
40
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export { default as Fonts } from './Fonts.svelte';
2
+ export { buildFontFamily, buildFontVariablesCss, buildGoogleFontsUrl, buildLocalFontFaceCss, defaultFontFamilies, fontsDefaults, resolveFontsOptions } from './fonts.js';
3
+ export type { FontDefinition, FontsConfig, FontsOptions, GoogleFontDefinition, GoogleFontDisplay, GoogleFontStyle, LocalFontDefinition, LocalFontSource, LocalFontSourceFormat, FontsProps, FontProvider } from './fonts.types.js';
@@ -0,0 +1,2 @@
1
+ export { default as Fonts } from './Fonts.svelte';
2
+ export { buildFontFamily, buildFontVariablesCss, buildGoogleFontsUrl, buildLocalFontFaceCss, defaultFontFamilies, fontsDefaults, resolveFontsOptions } from './fonts.js';
@@ -0,0 +1,14 @@
1
+ <script lang="ts">import { twMerge } from "tailwind-merge";
2
+ import { setContext } from "svelte";
3
+ import { listVariants } from "./list.variants.js";
4
+ let { variant = "default", class: className, children, ...restProps } = $props();
5
+ let styles = $derived(listVariants({ variant }));
6
+ // Pass the variant down to children if needed, or just let them style themselves
7
+ // Since the variant affects the parent 'base', but 'bordered' also affects 'item',
8
+ // we should provide the list context so ListItems know the current variant.
9
+ setContext("list-variant", () => variant);
10
+ </script>
11
+
12
+ <ul class={twMerge(styles.base(), className)} {...restProps}>
13
+ {@render children?.()}
14
+ </ul>
@@ -0,0 +1,4 @@
1
+ import type { ListProps } from './list.types.js';
2
+ declare const List: import("svelte").Component<ListProps, {}, "">;
3
+ type List = ReturnType<typeof List>;
4
+ export default List;
@@ -0,0 +1,64 @@
1
+ <script lang="ts">import { twMerge } from "tailwind-merge";
2
+ import { getContext } from "svelte";
3
+ import { listVariants } from "./list.variants.js";
4
+ let { title, description, interactive = false, disabled = false, href, target, class: className, leading, trailing, children, ...restProps } = $props();
5
+ // Get the parent list variant, default to 'default' if used outside a List
6
+ const getListVariant = getContext("list-variant");
7
+ let parentVariant = $derived(getListVariant ? getListVariant() : "default");
8
+ // Determine if it should be interactive automatically if it's a link
9
+ let isInteractive = $derived(interactive || !!href);
10
+ let styles = $derived(listVariants({
11
+ variant: parentVariant,
12
+ interactive: isInteractive,
13
+ disabled
14
+ }));
15
+ // Using svelte:element with conditional tags and spreading restProps causes TS errors.
16
+ // We use a snippet to keep the markup DRY while using explicit if/else tags.
17
+ </script>
18
+
19
+ {#snippet contentWrapper()}
20
+ {#if leading}
21
+ <div class={styles.leading()}>
22
+ {@render leading()}
23
+ </div>
24
+ {/if}
25
+
26
+ <div class={styles.itemContent()}>
27
+ {#if children}
28
+ {@render children()}
29
+ {:else}
30
+ {#if title}
31
+ <div class={styles.itemTitle()}>{title}</div>
32
+ {/if}
33
+ {#if description}
34
+ <div class={styles.itemDescription()}>{description}</div>
35
+ {/if}
36
+ {/if}
37
+ </div>
38
+
39
+ {#if trailing}
40
+ <div class={styles.trailing()}>
41
+ {@render trailing()}
42
+ </div>
43
+ {/if}
44
+ {/snippet}
45
+
46
+ <li>
47
+ {#if href}
48
+ <a
49
+ {href}
50
+ {target}
51
+ class={twMerge(styles.item(), className)}
52
+ {...restProps as any}
53
+ >
54
+ {@render contentWrapper()}
55
+ </a>
56
+ {:else}
57
+ <div
58
+ class={twMerge(styles.item(), className)}
59
+ {...restProps as any}
60
+ >
61
+ {@render contentWrapper()}
62
+ </div>
63
+ {/if}
64
+ </li>
@@ -0,0 +1,4 @@
1
+ import type { ListItemProps } from './list.types.js';
2
+ declare const ListItem: import("svelte").Component<ListItemProps, {}, "">;
3
+ type ListItem = ReturnType<typeof ListItem>;
4
+ export default ListItem;
@@ -0,0 +1,5 @@
1
+ import List from './List.svelte';
2
+ import ListItem from './ListItem.svelte';
3
+ export { List, ListItem };
4
+ export * from './list.types.js';
5
+ export * from './list.variants.js';
@@ -0,0 +1,5 @@
1
+ import List from './List.svelte';
2
+ import ListItem from './ListItem.svelte';
3
+ export { List, ListItem };
4
+ export * from './list.types.js';
5
+ export * from './list.variants.js';
@@ -0,0 +1,62 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes, HTMLLiAttributes } from 'svelte/elements';
3
+ import type { ListVariantProps } from './list.variants.js';
4
+ export interface ListProps extends HTMLAttributes<HTMLUListElement> {
5
+ /**
6
+ * The variant of the list.
7
+ * @default 'default'
8
+ */
9
+ variant?: ListVariantProps['variant'];
10
+ /**
11
+ * Additional CSS classes.
12
+ */
13
+ class?: string;
14
+ /**
15
+ * Content of the list.
16
+ */
17
+ children?: Snippet;
18
+ }
19
+ export interface ListItemProps extends HTMLLiAttributes {
20
+ /**
21
+ * Title of the list item.
22
+ */
23
+ title?: string;
24
+ /**
25
+ * Description below the title.
26
+ */
27
+ description?: string;
28
+ /**
29
+ * Whether the item is interactive (hover/active states).
30
+ * @default false
31
+ */
32
+ interactive?: boolean;
33
+ /**
34
+ * Whether the item is disabled.
35
+ * @default false
36
+ */
37
+ disabled?: boolean;
38
+ /**
39
+ * Optional href. If provided, the item renders as an anchor tag `<a>`.
40
+ */
41
+ href?: string;
42
+ /**
43
+ * Target attribute for the anchor tag if `href` is provided.
44
+ */
45
+ target?: string;
46
+ /**
47
+ * Additional CSS classes.
48
+ */
49
+ class?: string;
50
+ /**
51
+ * Snippet for the leading section (e.g., Avatar, Icon).
52
+ */
53
+ leading?: Snippet;
54
+ /**
55
+ * Snippet for the trailing section (e.g., Badge, Chevron, Switch).
56
+ */
57
+ trailing?: Snippet;
58
+ /**
59
+ * Snippet to replace the default title/description content completely.
60
+ */
61
+ children?: Snippet;
62
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,99 @@
1
+ import { type VariantProps } from 'tailwind-variants';
2
+ export declare const listVariants: import("tailwind-variants").TVReturnType<{
3
+ variant: {
4
+ default: {
5
+ base: string;
6
+ };
7
+ bordered: {
8
+ base: string;
9
+ item: string;
10
+ };
11
+ separated: {
12
+ base: string;
13
+ item: string;
14
+ };
15
+ };
16
+ interactive: {
17
+ true: {
18
+ item: string;
19
+ };
20
+ };
21
+ disabled: {
22
+ true: {
23
+ item: string;
24
+ };
25
+ };
26
+ }, {
27
+ base: string;
28
+ item: string;
29
+ itemContent: string;
30
+ itemTitle: string;
31
+ itemDescription: string;
32
+ leading: string;
33
+ trailing: string;
34
+ }, undefined, {
35
+ variant: {
36
+ default: {
37
+ base: string;
38
+ };
39
+ bordered: {
40
+ base: string;
41
+ item: string;
42
+ };
43
+ separated: {
44
+ base: string;
45
+ item: string;
46
+ };
47
+ };
48
+ interactive: {
49
+ true: {
50
+ item: string;
51
+ };
52
+ };
53
+ disabled: {
54
+ true: {
55
+ item: string;
56
+ };
57
+ };
58
+ }, {
59
+ base: string;
60
+ item: string;
61
+ itemContent: string;
62
+ itemTitle: string;
63
+ itemDescription: string;
64
+ leading: string;
65
+ trailing: string;
66
+ }, import("tailwind-variants").TVReturnTypeLike<{
67
+ variant: {
68
+ default: {
69
+ base: string;
70
+ };
71
+ bordered: {
72
+ base: string;
73
+ item: string;
74
+ };
75
+ separated: {
76
+ base: string;
77
+ item: string;
78
+ };
79
+ };
80
+ interactive: {
81
+ true: {
82
+ item: string;
83
+ };
84
+ };
85
+ disabled: {
86
+ true: {
87
+ item: string;
88
+ };
89
+ };
90
+ }, {
91
+ base: string;
92
+ item: string;
93
+ itemContent: string;
94
+ itemTitle: string;
95
+ itemDescription: string;
96
+ leading: string;
97
+ trailing: string;
98
+ }>>;
99
+ export type ListVariantProps = VariantProps<typeof listVariants>;
@@ -0,0 +1,42 @@
1
+ import { tv } from 'tailwind-variants';
2
+ export const listVariants = tv({
3
+ slots: {
4
+ base: 'flex flex-col w-full text-surface-900 dark:text-surface-50',
5
+ item: 'flex items-center justify-between w-full px-4 py-3',
6
+ itemContent: 'flex flex-col flex-1 min-w-0',
7
+ itemTitle: 'text-sm font-medium truncate',
8
+ itemDescription: 'text-xs text-surface-500 dark:text-surface-400 truncate mt-0.5',
9
+ leading: 'mr-3 shrink-0 flex items-center justify-center',
10
+ trailing: 'ml-3 shrink-0 flex items-center justify-center text-surface-500 dark:text-surface-400'
11
+ },
12
+ variants: {
13
+ variant: {
14
+ default: {
15
+ base: ''
16
+ },
17
+ bordered: {
18
+ base: 'border border-outline-variant rounded-lg overflow-hidden',
19
+ item: 'border-b border-outline-variant last:border-b-0'
20
+ },
21
+ separated: {
22
+ base: 'gap-2',
23
+ item: 'rounded-lg border border-outline-variant'
24
+ }
25
+ },
26
+ interactive: {
27
+ true: {
28
+ item: 'transition-colors hover:bg-surface-100 dark:hover:bg-surface-800 cursor-pointer active:bg-surface-200 dark:active:bg-surface-700'
29
+ }
30
+ },
31
+ disabled: {
32
+ true: {
33
+ item: 'opacity-50 cursor-not-allowed hover:bg-transparent dark:hover:bg-transparent active:bg-transparent dark:active:bg-transparent'
34
+ }
35
+ }
36
+ },
37
+ defaultVariants: {
38
+ variant: 'default',
39
+ interactive: false,
40
+ disabled: false
41
+ }
42
+ });
@@ -0,0 +1,181 @@
1
+ <script lang="ts" module>export {};
2
+ </script>
3
+
4
+ <script lang="ts">import { Button } from "sv5ui";
5
+ import { getComponentConfig, iconsDefaults } from "../../config.js";
6
+ import { DropdownMenu } from "sv5ui";
7
+ import { Icon } from "sv5ui";
8
+ import { Link } from "sv5ui";
9
+ import { localeButtonDefaults, localeButtonVariants } from "./locale-button.variants.js";
10
+ const config = getComponentConfig("localeButton", localeButtonDefaults);
11
+ const icons = getComponentConfig("icons", iconsDefaults);
12
+ let { ref = $bindable(null), locales = [], locale, variant = config.defaultVariants.variant ?? "outline", color = config.defaultVariants.color ?? "surface", size = config.defaultVariants.size ?? "md", fit = config.defaultVariants.fit ?? false, loading = false, disabled = false, block = false, square = false, placeholder = "Select language", ariaLabel = "Change language", menuLabel = "Language", icon = "lucide:languages", chevronIcon = icons.chevronDown, showIcon = true, showChevron = true, showCode = true, showIndicator = true, disableCurrentLocale = true, closeOnSelect = true, getLocaleHref, onLocaleChange, open = $bindable(false), onOpenChange, side = "bottom", sideOffset = 4, align = "start", alignOffset = 0, avoidCollisions = true, collisionBoundary, collisionPadding = 8, sticky = "partial", hideWhenDetached = false, onEscapeKeydown, onInteractOutside, onCloseAutoFocus, forceMount, loop = true, portal = true, arrow = false, ui, children: triggerContent, item: itemSnippet, menu: menuSnippet, class: className, ...restProps } = $props();
13
+ const classes = $derived.by(() => {
14
+ const slots = localeButtonVariants({
15
+ size,
16
+ fit
17
+ });
18
+ return {
19
+ base: slots.base({ class: [
20
+ config.slots.base,
21
+ className,
22
+ ui?.base
23
+ ] }),
24
+ triggerLabel: slots.triggerLabel({ class: [config.slots.triggerLabel, ui?.triggerLabel] }),
25
+ triggerIcon: slots.triggerIcon({ class: [config.slots.triggerIcon, ui?.triggerIcon] }),
26
+ triggerChevron: slots.triggerChevron({ class: [config.slots.triggerChevron, ui?.triggerChevron] }),
27
+ menu: slots.menu({ class: [config.slots.menu, ui?.menu] }),
28
+ menuLabel: slots.menuLabel({ class: [config.slots.menuLabel, ui?.menuLabel] }),
29
+ item: slots.item({ class: [config.slots.item, ui?.item] }),
30
+ itemIdle: slots.itemIdle({ class: [config.slots.itemIdle, ui?.itemIdle] }),
31
+ itemCurrent: slots.itemCurrent({ class: [config.slots.itemCurrent, ui?.itemCurrent] }),
32
+ itemLeading: slots.itemLeading({ class: [config.slots.itemLeading, ui?.itemLeading] }),
33
+ itemText: slots.itemText({ class: [config.slots.itemText, ui?.itemText] }),
34
+ itemLabel: slots.itemLabel({ class: [config.slots.itemLabel, ui?.itemLabel] }),
35
+ itemDescription: slots.itemDescription({ class: [config.slots.itemDescription, ui?.itemDescription] }),
36
+ itemCode: slots.itemCode({ class: [config.slots.itemCode, ui?.itemCode] }),
37
+ itemIndicator: slots.itemIndicator({ class: [config.slots.itemIndicator, ui?.itemIndicator] })
38
+ };
39
+ });
40
+ const currentLocale = $derived(locales.find((item) => item.code === locale) ?? locales[0]);
41
+ const triggerLabel = $derived(currentLocale?.shortLabel ?? currentLocale?.label ?? placeholder);
42
+ const triggerDisabled = $derived(disabled || loading || locales.length <= 1);
43
+ function isCurrentLocale(item) {
44
+ return item.code === currentLocale?.code;
45
+ }
46
+ function getResolvedHref(item) {
47
+ return item.href ?? getLocaleHref?.(item.code, item);
48
+ }
49
+ function isItemDisabled(item) {
50
+ return disabled || loading || item.disabled || disableCurrentLocale && isCurrentLocale(item);
51
+ }
52
+ async function handleItemClick(event, item, close) {
53
+ if (isItemDisabled(item)) {
54
+ event.preventDefault();
55
+ event.stopPropagation();
56
+ return;
57
+ }
58
+ if (onLocaleChange) {
59
+ await onLocaleChange(item.code, item);
60
+ }
61
+ if (!getResolvedHref(item) && closeOnSelect) {
62
+ close();
63
+ }
64
+ }
65
+ </script>
66
+
67
+ <DropdownMenu
68
+ bind:open
69
+ {onOpenChange}
70
+ {side}
71
+ {sideOffset}
72
+ {align}
73
+ {alignOffset}
74
+ {avoidCollisions}
75
+ {collisionBoundary}
76
+ {collisionPadding}
77
+ {sticky}
78
+ {hideWhenDetached}
79
+ {onEscapeKeydown}
80
+ {onInteractOutside}
81
+ {onCloseAutoFocus}
82
+ {forceMount}
83
+ {loop}
84
+ {portal}
85
+ {arrow}
86
+ {size}
87
+ ui={fit ? { content: 'w-fit min-w-0' } : undefined}
88
+ >
89
+ {#snippet children({ open: isOpen, props })}
90
+ <Button
91
+ {...props}
92
+ bind:ref
93
+ {variant}
94
+ {color}
95
+ {size}
96
+ {loading}
97
+ disabled={triggerDisabled}
98
+ {block}
99
+ {square}
100
+ class={classes.base}
101
+ aria-label={ariaLabel}
102
+ {...restProps}
103
+ >
104
+ {#if triggerContent}
105
+ {@render triggerContent({ open: isOpen, currentLocale })}
106
+ {:else}
107
+ {#if showIcon}
108
+ <span class={classes.triggerIcon}>
109
+ <Icon name={icon} />
110
+ </span>
111
+ {/if}
112
+ <span class={classes.triggerLabel}>{triggerLabel}</span>
113
+ {#if showChevron}
114
+ <span class={classes.triggerChevron}>
115
+ <Icon name={chevronIcon} />
116
+ </span>
117
+ {/if}
118
+ {/if}
119
+ </Button>
120
+ {/snippet}
121
+
122
+ {#snippet content({ close })}
123
+ {#if menuSnippet}
124
+ {@render menuSnippet({ close })}
125
+ {:else}
126
+ <div class={classes.menu}>
127
+ {#if menuLabel}
128
+ <p class={classes.menuLabel}>{menuLabel}</p>
129
+ {/if}
130
+
131
+ {#each locales as item (item.code)}
132
+ {@const current = isCurrentLocale(item)}
133
+ {@const href = getResolvedHref(item)}
134
+ {@const itemDisabled = isItemDisabled(item)}
135
+
136
+ {#if itemSnippet}
137
+ {@render itemSnippet({ item, current, disabled: itemDisabled, close })}
138
+ {:else}
139
+ <Link
140
+ href={href}
141
+ hreflang={item.hreflang ?? item.code}
142
+ raw
143
+ role="menuitemradio"
144
+ aria-checked={current}
145
+ disabled={itemDisabled}
146
+ class={[
147
+ classes.item,
148
+ current ? classes.itemCurrent : classes.itemIdle,
149
+ item.class
150
+ ]}
151
+ onclick={(event) => handleItemClick(event, item, close)}
152
+ >
153
+ <span class={classes.itemLeading}>
154
+ {#if item.icon}
155
+ <Icon name={item.icon} />
156
+ {/if}
157
+ <span class={classes.itemText}>
158
+ <span class={classes.itemLabel}>{item.label}</span>
159
+ {#if item.description}
160
+ <span class={classes.itemDescription}>{item.description}</span>
161
+ {/if}
162
+ </span>
163
+ </span>
164
+
165
+ <span class="flex items-center gap-2">
166
+ {#if showCode}
167
+ <span class={classes.itemCode}>{item.shortLabel ?? item.code}</span>
168
+ {/if}
169
+ {#if showIndicator && current}
170
+ <span class={classes.itemIndicator}>
171
+ <Icon name={icons.check} />
172
+ </span>
173
+ {/if}
174
+ </span>
175
+ </Link>
176
+ {/if}
177
+ {/each}
178
+ </div>
179
+ {/if}
180
+ {/snippet}
181
+ </DropdownMenu>
@@ -0,0 +1,5 @@
1
+ import type { LocaleButtonProps } from './locale-button.types.js';
2
+ export type Props = LocaleButtonProps;
3
+ declare const LocaleButton: import("svelte").Component<LocaleButtonProps, {}, "ref" | "open">;
4
+ type LocaleButton = ReturnType<typeof LocaleButton>;
5
+ export default LocaleButton;