@mrintel/villain-ui 0.3.0 → 0.7.1

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 (140) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +3490 -1296
  3. package/dist/components/buttons/Button.svelte +27 -33
  4. package/dist/components/buttons/Button.svelte.d.ts +4 -1
  5. package/dist/components/buttons/ButtonGroup.svelte +17 -30
  6. package/dist/components/buttons/FloatingActionButton.svelte +20 -44
  7. package/dist/components/buttons/FloatingActionButton.svelte.d.ts +2 -1
  8. package/dist/components/buttons/IconButton.svelte +23 -53
  9. package/dist/components/buttons/IconButton.svelte.d.ts +2 -1
  10. package/dist/components/buttons/LinkButton.svelte +24 -37
  11. package/dist/components/buttons/LinkButton.svelte.d.ts +4 -1
  12. package/dist/components/buttons/buttonClasses.d.ts +5 -0
  13. package/dist/components/buttons/buttonClasses.js +8 -3
  14. package/dist/components/cards/Card.svelte +54 -46
  15. package/dist/components/cards/Card.svelte.d.ts +9 -2
  16. package/dist/components/cards/Container.svelte +17 -33
  17. package/dist/components/cards/Divider.svelte +36 -52
  18. package/dist/components/cards/Divider.svelte.d.ts +2 -0
  19. package/dist/components/cards/Grid.svelte +55 -44
  20. package/dist/components/cards/Panel.svelte +18 -32
  21. package/dist/components/cards/Panel.svelte.d.ts +2 -1
  22. package/dist/components/cards/SectionHeader.svelte +24 -38
  23. package/dist/components/cards/SectionHeader.svelte.d.ts +1 -0
  24. package/dist/components/data/Avatar.svelte +48 -67
  25. package/dist/components/data/Badge.svelte +45 -32
  26. package/dist/components/data/Badge.svelte.d.ts +7 -1
  27. package/dist/components/data/CalendarGrid.svelte +433 -0
  28. package/dist/components/data/CalendarGrid.svelte.d.ts +25 -0
  29. package/dist/components/data/CalendarGrid.types.d.ts +7 -0
  30. package/dist/components/data/CalendarGrid.types.js +1 -0
  31. package/dist/components/data/CodeBlock.svelte +119 -121
  32. package/dist/components/data/CodeBlock.svelte.d.ts +8 -0
  33. package/dist/components/data/List.svelte +87 -64
  34. package/dist/components/data/List.svelte.d.ts +7 -0
  35. package/dist/components/data/Pagination.svelte +121 -123
  36. package/dist/components/data/Pagination.svelte.d.ts +5 -0
  37. package/dist/components/data/Sparkline.svelte +117 -0
  38. package/dist/components/data/Sparkline.svelte.d.ts +43 -0
  39. package/dist/components/data/Stat.svelte +92 -103
  40. package/dist/components/data/Table.svelte +443 -76
  41. package/dist/components/data/Table.svelte.d.ts +23 -2
  42. package/dist/components/data/Table.types.d.ts +14 -0
  43. package/dist/components/data/Table.types.js +1 -0
  44. package/dist/components/data/Tag.svelte +51 -53
  45. package/dist/components/data/Tag.svelte.d.ts +5 -1
  46. package/dist/components/data/index.d.ts +4 -0
  47. package/dist/components/data/index.js +2 -0
  48. package/dist/components/forms/Checkbox.svelte +39 -51
  49. package/dist/components/forms/Checkbox.svelte.d.ts +3 -1
  50. package/dist/components/forms/DatePicker.svelte +61 -0
  51. package/dist/components/forms/DatePicker.svelte.d.ts +15 -0
  52. package/dist/components/forms/DateTimePicker.svelte +63 -0
  53. package/dist/components/forms/DateTimePicker.svelte.d.ts +16 -0
  54. package/dist/components/forms/FileUpload.svelte +136 -164
  55. package/dist/components/forms/FileUpload.svelte.d.ts +1 -0
  56. package/dist/components/forms/Input.svelte +284 -57
  57. package/dist/components/forms/Input.svelte.d.ts +10 -3
  58. package/dist/components/forms/InputGroup.svelte +7 -7
  59. package/dist/components/forms/RadioGroup.svelte +77 -87
  60. package/dist/components/forms/RadioGroup.svelte.d.ts +3 -1
  61. package/dist/components/forms/RangeSlider.svelte +90 -116
  62. package/dist/components/forms/Select.svelte +106 -71
  63. package/dist/components/forms/Select.svelte.d.ts +3 -1
  64. package/dist/components/forms/Step.svelte +25 -0
  65. package/dist/components/forms/Step.svelte.d.ts +12 -0
  66. package/dist/components/forms/StepContext.d.ts +3 -0
  67. package/dist/components/forms/StepContext.js +5 -0
  68. package/dist/components/forms/Stepper.types.d.ts +37 -0
  69. package/dist/components/forms/Stepper.types.js +1 -0
  70. package/dist/components/forms/StepperForm.svelte +183 -0
  71. package/dist/components/forms/StepperForm.svelte.d.ts +17 -0
  72. package/dist/components/forms/Switch.svelte +44 -56
  73. package/dist/components/forms/Switch.svelte.d.ts +3 -1
  74. package/dist/components/forms/Textarea.svelte +52 -57
  75. package/dist/components/forms/Textarea.svelte.d.ts +3 -1
  76. package/dist/components/forms/TimePicker.svelte +63 -0
  77. package/dist/components/forms/TimePicker.svelte.d.ts +16 -0
  78. package/dist/components/forms/formClasses.d.ts +3 -0
  79. package/dist/components/forms/formClasses.js +3 -0
  80. package/dist/components/forms/index.d.ts +6 -0
  81. package/dist/components/forms/index.js +5 -0
  82. package/dist/components/navigation/Breadcrumbs.svelte +56 -59
  83. package/dist/components/navigation/Breadcrumbs.svelte.d.ts +1 -0
  84. package/dist/components/navigation/ContextMenu.svelte +133 -83
  85. package/dist/components/navigation/ContextMenu.svelte.d.ts +8 -1
  86. package/dist/components/navigation/DropdownMenu.svelte +139 -80
  87. package/dist/components/navigation/DropdownMenu.svelte.d.ts +8 -1
  88. package/dist/components/navigation/Menu.svelte +72 -48
  89. package/dist/components/navigation/Navbar.svelte +111 -32
  90. package/dist/components/navigation/Navbar.svelte.d.ts +6 -0
  91. package/dist/components/navigation/Sidebar.svelte +236 -35
  92. package/dist/components/navigation/Sidebar.svelte.d.ts +2 -0
  93. package/dist/components/navigation/Stepper.svelte +204 -0
  94. package/dist/components/navigation/Stepper.svelte.d.ts +34 -0
  95. package/dist/components/navigation/Tabs.svelte +86 -54
  96. package/dist/components/navigation/Tabs.svelte.d.ts +5 -1
  97. package/dist/components/navigation/index.d.ts +1 -0
  98. package/dist/components/navigation/index.js +1 -0
  99. package/dist/components/overlays/Alert.svelte +81 -99
  100. package/dist/components/overlays/Alert.svelte.d.ts +5 -1
  101. package/dist/components/overlays/CommandPalette.svelte +182 -217
  102. package/dist/components/overlays/Drawer.svelte +158 -167
  103. package/dist/components/overlays/Drawer.svelte.d.ts +3 -1
  104. package/dist/components/overlays/Dropdown.svelte +62 -30
  105. package/dist/components/overlays/Dropdown.svelte.d.ts +2 -0
  106. package/dist/components/overlays/Modal.svelte +125 -130
  107. package/dist/components/overlays/Modal.svelte.d.ts +3 -1
  108. package/dist/components/overlays/Popover.svelte +106 -131
  109. package/dist/components/overlays/ProgressBar.svelte +29 -45
  110. package/dist/components/overlays/SkeletonLoader.svelte +66 -82
  111. package/dist/components/overlays/Spinner.svelte +33 -43
  112. package/dist/components/overlays/Toast.svelte +111 -140
  113. package/dist/components/overlays/Toast.svelte.d.ts +3 -0
  114. package/dist/components/overlays/Tooltip.svelte +94 -115
  115. package/dist/components/overlays/Tooltip.svelte.d.ts +3 -1
  116. package/dist/components/typography/Code.svelte +10 -14
  117. package/dist/components/typography/Heading.svelte +15 -22
  118. package/dist/components/typography/Heading.svelte.d.ts +1 -0
  119. package/dist/components/typography/Text.svelte +21 -24
  120. package/dist/components/typography/Text.svelte.d.ts +2 -1
  121. package/dist/components/utilities/Accordion.svelte +54 -67
  122. package/dist/components/utilities/Accordion.svelte.d.ts +4 -1
  123. package/dist/components/utilities/Carousel.svelte +124 -152
  124. package/dist/components/utilities/Collapse.svelte +46 -60
  125. package/dist/components/utilities/Hero.svelte +42 -0
  126. package/dist/components/utilities/Hero.svelte.d.ts +10 -0
  127. package/dist/components/utilities/Portal.svelte +47 -72
  128. package/dist/components/utilities/ScrollArea.svelte +33 -41
  129. package/dist/components/utilities/SystemConsole.svelte +310 -0
  130. package/dist/components/utilities/SystemConsole.svelte.d.ts +20 -0
  131. package/dist/components/utilities/SystemInterface.svelte +726 -0
  132. package/dist/components/utilities/SystemInterface.svelte.d.ts +19 -0
  133. package/dist/components/utilities/index.d.ts +4 -0
  134. package/dist/components/utilities/index.js +3 -0
  135. package/dist/components/utilities/utilities.types.d.ts +46 -0
  136. package/dist/components/utilities/utilities.types.js +4 -0
  137. package/dist/index.d.ts +57 -5
  138. package/dist/index.js +5 -5
  139. package/dist/theme.css +2889 -218
  140. package/package.json +83 -76
@@ -1,217 +1,182 @@
1
- <script lang="ts">
2
- import type { Snippet } from 'svelte';
3
- import { createId } from '../../lib/internal/id.js';
4
-
5
- interface Command {
6
- id: string;
7
- label: string;
8
- icon?: Snippet;
9
- onSelect: () => void;
10
- keywords?: string[];
11
- }
12
-
13
- interface Props {
14
- open?: boolean;
15
- commands: Command[];
16
- placeholder?: string;
17
- }
18
-
19
- let {
20
- open = $bindable(false),
21
- commands,
22
- placeholder = 'Search commands...'
23
- }: Props = $props();
24
-
25
- let query = $state('');
26
- let selectedIndex = $state(0);
27
- let inputElement = $state<HTMLInputElement>();
28
- let previousFocus = $state<HTMLElement | null>(null);
29
-
30
- const paletteId = createId('command-palette');
31
-
32
- // Inline fuzzy matching function
33
- function fuzzyMatch(text: string, search: string): boolean {
34
- const searchChars = search.toLowerCase().split('');
35
- const textLower = text.toLowerCase();
36
- let searchIndex = 0;
37
-
38
- for (let i = 0; i < textLower.length && searchIndex < searchChars.length; i++) {
39
- if (textLower[i] === searchChars[searchIndex]) {
40
- searchIndex++;
41
- }
42
- }
43
-
44
- return searchIndex === searchChars.length;
45
- }
46
-
47
- let filteredCommands = $derived(
48
- query.trim() === ''
49
- ? commands
50
- : commands.filter(cmd => {
51
- // Match against label
52
- if (fuzzyMatch(cmd.label, query)) return true;
53
-
54
- // Match against keywords
55
- if (cmd.keywords) {
56
- return cmd.keywords.some(keyword => fuzzyMatch(keyword, query));
57
- }
58
-
59
- return false;
60
- })
61
- );
62
-
63
- function handleClose() {
64
- open = false;
65
- query = '';
66
- selectedIndex = 0;
67
- }
68
-
69
- function handleBackdropClick(event: MouseEvent) {
70
- if (event.target === event.currentTarget) {
71
- handleClose();
72
- }
73
- }
74
-
75
- function handleSelectCommand(command: Command) {
76
- command.onSelect();
77
- handleClose();
78
- }
79
-
80
- function handleKeyDown(event: KeyboardEvent) {
81
- if (event.key === 'ArrowDown') {
82
- if (!filteredCommands.length) return;
83
- event.preventDefault();
84
- selectedIndex = (selectedIndex + 1) % filteredCommands.length;
85
- } else if (event.key === 'ArrowUp') {
86
- if (!filteredCommands.length) return;
87
- event.preventDefault();
88
- selectedIndex = (selectedIndex - 1 + filteredCommands.length) % filteredCommands.length;
89
- } else if (event.key === 'Enter') {
90
- event.preventDefault();
91
- if (filteredCommands[selectedIndex]) {
92
- handleSelectCommand(filteredCommands[selectedIndex]);
93
- } else if (filteredCommands.length) {
94
- selectedIndex = 0;
95
- }
96
- } else if (event.key === 'Escape') {
97
- event.preventDefault();
98
- handleClose();
99
- }
100
- }
101
-
102
- // Reset and clamp selected index when filtered commands change
103
- $effect(() => {
104
- if (filteredCommands.length === 0) {
105
- selectedIndex = -1;
106
- } else if (selectedIndex >= filteredCommands.length || selectedIndex < 0) {
107
- selectedIndex = 0;
108
- }
109
- });
110
-
111
- $effect(() => {
112
- if (typeof document === 'undefined') return;
113
-
114
- if (open) {
115
- // Store previous focus
116
- previousFocus = document.activeElement as HTMLElement;
117
-
118
- // Prevent body scroll
119
- document.body.style.overflow = 'hidden';
120
-
121
- // Focus input element
122
- requestAnimationFrame(() => {
123
- inputElement?.focus();
124
- });
125
-
126
- return () => {
127
- // Restore body scroll
128
- document.body.style.overflow = '';
129
-
130
- // Restore previous focus
131
- previousFocus?.focus();
132
- };
133
- }
134
- });
135
- </script>
136
-
137
- {#if open}
138
- <div
139
- class="fixed inset-0 z-50 flex items-start justify-center pt-[20vh] p-4 bg-overlay backdrop-blur-md animate-[fade-in_0.2s_var(--ease-luxe)]"
140
- onclick={handleBackdropClick}
141
- role="presentation"
142
- >
143
- <div
144
- class="glass-panel rounded-xl shadow-deep w-full max-w-[36rem] animate-[fade-up_0.3s_var(--ease-luxe)]"
145
- role="combobox"
146
- aria-expanded="true"
147
- aria-haspopup="listbox"
148
- aria-controls={paletteId}
149
- aria-activedescendant={selectedIndex >= 0 && selectedIndex < filteredCommands.length && filteredCommands[selectedIndex] ? `cmd-${filteredCommands[selectedIndex].id}` : undefined}
150
- tabindex="0"
151
- >
152
- <div class="p-4 border-b border-border">
153
- <input
154
- bind:this={inputElement}
155
- bind:value={query}
156
- type="text"
157
- placeholder={placeholder}
158
- onkeydown={handleKeyDown}
159
- class="w-full bg-transparent border-none outline-none text-text text-lg placeholder:text-text-muted"
160
- aria-autocomplete="list"
161
- />
162
- </div>
163
-
164
- <div
165
- id={paletteId}
166
- role="listbox"
167
- class="max-h-[400px] overflow-y-auto"
168
- style="scrollbar-width: thin; scrollbar-color: var(--color-accent) var(--color-base-3);"
169
- >
170
- {#if filteredCommands.length === 0}
171
- <div class="p-8 text-center text-text-muted">
172
- No commands found
173
- </div>
174
- {:else}
175
- {#each filteredCommands as command, index (command.id)}
176
- <button
177
- id="cmd-{command.id}"
178
- type="button"
179
- role="option"
180
- aria-selected={index === selectedIndex}
181
- tabindex={index === selectedIndex ? 0 : -1}
182
- onclick={() => handleSelectCommand(command)}
183
- class="w-full flex items-center gap-3 p-4 text-left text-text hover:bg-base-3 transition-colors {index === selectedIndex ? 'accent-glow bg-base-3' : ''}"
184
- >
185
- {#if command.icon}
186
- <div class="shrink-0">
187
- {@render command.icon()}
188
- </div>
189
- {/if}
190
- <span>{command.label}</span>
191
- </button>
192
- {/each}
193
- {/if}
194
- </div>
195
- </div>
196
- </div>
197
- {/if}
198
-
199
- <style>
200
- div[role="listbox"]::-webkit-scrollbar {
201
- width: 8px;
202
- }
203
-
204
- div[role="listbox"]::-webkit-scrollbar-track {
205
- background: var(--color-base-3);
206
- border-radius: var(--radius-sm);
207
- }
208
-
209
- div[role="listbox"]::-webkit-scrollbar-thumb {
210
- background: var(--color-accent);
211
- border-radius: var(--radius-sm);
212
- }
213
-
214
- div[role="listbox"]::-webkit-scrollbar-thumb:hover {
215
- background: var(--color-accent-soft);
216
- }
217
- </style>
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ let { open = $bindable(false), commands, placeholder = 'Search commands...' } = $props();
3
+ let query = $state('');
4
+ let selectedIndex = $state(0);
5
+ let inputElement = $state();
6
+ let previousFocus = $state(null);
7
+ const paletteId = createId('command-palette');
8
+ // Inline fuzzy matching function
9
+ function fuzzyMatch(text, search) {
10
+ const searchChars = search.toLowerCase().split('');
11
+ const textLower = text.toLowerCase();
12
+ let searchIndex = 0;
13
+ for (let i = 0; i < textLower.length && searchIndex < searchChars.length; i++) {
14
+ if (textLower[i] === searchChars[searchIndex]) {
15
+ searchIndex++;
16
+ }
17
+ }
18
+ return searchIndex === searchChars.length;
19
+ }
20
+ let filteredCommands = $derived(query.trim() === ''
21
+ ? commands
22
+ : commands.filter(cmd => {
23
+ // Match against label
24
+ if (fuzzyMatch(cmd.label, query))
25
+ return true;
26
+ // Match against keywords
27
+ if (cmd.keywords) {
28
+ return cmd.keywords.some(keyword => fuzzyMatch(keyword, query));
29
+ }
30
+ return false;
31
+ }));
32
+ function handleClose() {
33
+ open = false;
34
+ query = '';
35
+ selectedIndex = 0;
36
+ }
37
+ function handleBackdropClick(event) {
38
+ if (event.target === event.currentTarget) {
39
+ handleClose();
40
+ }
41
+ }
42
+ function handleSelectCommand(command) {
43
+ command.onSelect();
44
+ handleClose();
45
+ }
46
+ function handleKeyDown(event) {
47
+ if (event.key === 'ArrowDown') {
48
+ if (!filteredCommands.length)
49
+ return;
50
+ event.preventDefault();
51
+ selectedIndex = (selectedIndex + 1) % filteredCommands.length;
52
+ }
53
+ else if (event.key === 'ArrowUp') {
54
+ if (!filteredCommands.length)
55
+ return;
56
+ event.preventDefault();
57
+ selectedIndex = (selectedIndex - 1 + filteredCommands.length) % filteredCommands.length;
58
+ }
59
+ else if (event.key === 'Enter') {
60
+ event.preventDefault();
61
+ if (filteredCommands[selectedIndex]) {
62
+ handleSelectCommand(filteredCommands[selectedIndex]);
63
+ }
64
+ else if (filteredCommands.length) {
65
+ selectedIndex = 0;
66
+ }
67
+ }
68
+ else if (event.key === 'Escape') {
69
+ event.preventDefault();
70
+ handleClose();
71
+ }
72
+ }
73
+ // Reset and clamp selected index when filtered commands change
74
+ $effect(() => {
75
+ if (filteredCommands.length === 0) {
76
+ selectedIndex = -1;
77
+ }
78
+ else if (selectedIndex >= filteredCommands.length || selectedIndex < 0) {
79
+ selectedIndex = 0;
80
+ }
81
+ });
82
+ $effect(() => {
83
+ if (typeof document === 'undefined')
84
+ return;
85
+ if (open) {
86
+ // Store previous focus
87
+ previousFocus = document.activeElement;
88
+ // Prevent body scroll
89
+ document.body.style.overflow = 'hidden';
90
+ // Focus input element
91
+ requestAnimationFrame(() => {
92
+ inputElement?.focus();
93
+ });
94
+ return () => {
95
+ // Restore body scroll
96
+ document.body.style.overflow = '';
97
+ // Restore previous focus
98
+ previousFocus?.focus();
99
+ };
100
+ }
101
+ });
102
+ </script>
103
+
104
+ {#if open}
105
+ <div
106
+ class="fixed inset-0 z-[var(--z-50)] flex items-start justify-center pt-[20vh] p-4 bg-overlay backdrop-blur-md animate-[fade-in_0.2s_var(--ease-luxe)]"
107
+ onclick={handleBackdropClick}
108
+ role="presentation"
109
+ >
110
+ <div
111
+ class="panel-floating rounded-xl shadow-deep w-full max-w-[36rem] animate-[fade-up_0.3s_var(--ease-luxe)]"
112
+ role="combobox"
113
+ aria-expanded="true"
114
+ aria-haspopup="listbox"
115
+ aria-controls={paletteId}
116
+ >
117
+ <div class="p-4 border-b border-border">
118
+ <input
119
+ bind:this={inputElement}
120
+ bind:value={query}
121
+ type="text"
122
+ placeholder={placeholder}
123
+ onkeydown={handleKeyDown}
124
+ class="w-full bg-transparent border-none outline-none text-text text-lg placeholder:text-text-muted"
125
+ aria-autocomplete="list"
126
+ aria-activedescendant={selectedIndex >= 0 && selectedIndex < filteredCommands.length && filteredCommands[selectedIndex] ? `cmd-${filteredCommands[selectedIndex].id}` : undefined}
127
+ />
128
+ </div>
129
+
130
+ <div
131
+ id={paletteId}
132
+ role="listbox"
133
+ class="max-h-[400px] overflow-y-auto"
134
+ style="scrollbar-width: thin; scrollbar-color: var(--color-accent) var(--color-base-3);"
135
+ >
136
+ {#if filteredCommands.length === 0}
137
+ <div class="p-8 text-center text-text-muted">
138
+ No commands found
139
+ </div>
140
+ {:else}
141
+ {#each filteredCommands as command, index (command.id)}
142
+ <button
143
+ id="cmd-{command.id}"
144
+ type="button"
145
+ role="option"
146
+ aria-selected={index === selectedIndex}
147
+ tabindex={index === selectedIndex ? 0 : -1}
148
+ onclick={() => handleSelectCommand(command)}
149
+ class="w-full flex items-center gap-3 p-4 text-left text-text hover:bg-base-3 transition-colors {index === selectedIndex ? 'accent-glow bg-base-3' : ''}"
150
+ >
151
+ {#if command.icon}
152
+ <div class="shrink-0">
153
+ {@render command.icon()}
154
+ </div>
155
+ {/if}
156
+ <span>{command.label}</span>
157
+ </button>
158
+ {/each}
159
+ {/if}
160
+ </div>
161
+ </div>
162
+ </div>
163
+ {/if}
164
+
165
+ <style>
166
+ div[role="listbox"]::-webkit-scrollbar {
167
+ width: 8px;
168
+ }
169
+
170
+ div[role="listbox"]::-webkit-scrollbar-track {
171
+ background: var(--color-base-3);
172
+ border-radius: var(--radius-sm);
173
+ }
174
+
175
+ div[role="listbox"]::-webkit-scrollbar-thumb {
176
+ background: var(--color-accent);
177
+ border-radius: var(--radius-sm);
178
+ }
179
+
180
+ div[role="listbox"]::-webkit-scrollbar-thumb:hover {
181
+ background: var(--color-accent-soft);
182
+ }</style>