@mrintel/villain-ui 0.2.2 → 0.6.3

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 (159) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +3490 -1296
  3. package/dist/components/buttons/Button.svelte +27 -0
  4. package/dist/components/buttons/Button.svelte.d.ts +14 -0
  5. package/dist/components/buttons/ButtonGroup.svelte +17 -0
  6. package/dist/components/buttons/ButtonGroup.svelte.d.ts +8 -0
  7. package/dist/components/buttons/FloatingActionButton.svelte +20 -0
  8. package/dist/components/buttons/FloatingActionButton.svelte.d.ts +12 -0
  9. package/dist/components/buttons/IconButton.svelte +23 -0
  10. package/dist/components/buttons/IconButton.svelte.d.ts +14 -0
  11. package/dist/components/buttons/LinkButton.svelte +24 -0
  12. package/dist/components/buttons/LinkButton.svelte.d.ts +15 -0
  13. package/dist/components/buttons/buttonClasses.d.ts +15 -0
  14. package/dist/components/buttons/buttonClasses.js +15 -0
  15. package/dist/components/buttons/index.d.ts +5 -0
  16. package/dist/components/buttons/index.js +5 -0
  17. package/dist/components/cards/Card.svelte +60 -0
  18. package/dist/components/cards/Card.svelte.d.ts +15 -0
  19. package/dist/components/cards/Container.svelte +17 -0
  20. package/dist/components/cards/Container.svelte.d.ts +10 -0
  21. package/dist/components/cards/Divider.svelte +36 -0
  22. package/dist/components/cards/Divider.svelte.d.ts +11 -0
  23. package/dist/components/cards/Grid.svelte +55 -0
  24. package/dist/components/cards/Grid.svelte.d.ts +10 -0
  25. package/dist/components/cards/Panel.svelte +18 -0
  26. package/dist/components/cards/Panel.svelte.d.ts +11 -0
  27. package/dist/components/cards/SectionHeader.svelte +24 -0
  28. package/dist/components/cards/SectionHeader.svelte.d.ts +12 -0
  29. package/dist/components/cards/index.d.ts +6 -0
  30. package/dist/components/cards/index.js +6 -0
  31. package/dist/components/data/Avatar.svelte +48 -0
  32. package/dist/components/data/Avatar.svelte.d.ts +10 -0
  33. package/dist/components/data/Badge.svelte +45 -0
  34. package/dist/components/data/Badge.svelte.d.ts +14 -0
  35. package/dist/components/data/CalendarGrid.svelte +433 -0
  36. package/dist/components/data/CalendarGrid.svelte.d.ts +25 -0
  37. package/dist/components/data/CalendarGrid.types.d.ts +7 -0
  38. package/dist/components/data/CalendarGrid.types.js +1 -0
  39. package/dist/components/data/CodeBlock.svelte +119 -0
  40. package/dist/components/data/CodeBlock.svelte.d.ts +40 -0
  41. package/dist/components/data/List.svelte +87 -0
  42. package/dist/components/data/List.svelte.d.ts +15 -0
  43. package/dist/components/data/Pagination.svelte +121 -0
  44. package/dist/components/data/Pagination.svelte.d.ts +14 -0
  45. package/dist/components/data/Sparkline.svelte +117 -0
  46. package/dist/components/data/Sparkline.svelte.d.ts +43 -0
  47. package/dist/components/data/Stat.svelte +92 -0
  48. package/dist/components/data/Stat.svelte.d.ts +11 -0
  49. package/dist/components/data/Table.svelte +443 -0
  50. package/dist/components/data/Table.svelte.d.ts +30 -0
  51. package/dist/components/data/Table.types.d.ts +14 -0
  52. package/dist/components/data/Table.types.js +1 -0
  53. package/dist/components/data/Tag.svelte +51 -0
  54. package/dist/components/data/Tag.svelte.d.ts +13 -0
  55. package/dist/components/data/index.d.ts +12 -0
  56. package/dist/components/data/index.js +10 -0
  57. package/dist/components/forms/Checkbox.svelte +39 -0
  58. package/dist/components/forms/Checkbox.svelte.d.ts +12 -0
  59. package/dist/components/forms/DatePicker.svelte +61 -0
  60. package/dist/components/forms/DatePicker.svelte.d.ts +15 -0
  61. package/dist/components/forms/DateTimePicker.svelte +63 -0
  62. package/dist/components/forms/DateTimePicker.svelte.d.ts +16 -0
  63. package/dist/components/forms/FileUpload.svelte +136 -0
  64. package/dist/components/forms/FileUpload.svelte.d.ts +23 -0
  65. package/dist/components/forms/Input.svelte +282 -0
  66. package/dist/components/forms/Input.svelte.d.ts +19 -0
  67. package/dist/components/forms/InputGroup.svelte +7 -0
  68. package/dist/components/forms/InputGroup.svelte.d.ts +20 -0
  69. package/dist/components/forms/RadioGroup.svelte +77 -0
  70. package/dist/components/forms/RadioGroup.svelte.d.ts +17 -0
  71. package/dist/components/forms/RangeSlider.svelte +90 -0
  72. package/dist/components/forms/RangeSlider.svelte.d.ts +14 -0
  73. package/dist/components/forms/Select.svelte +106 -0
  74. package/dist/components/forms/Select.svelte.d.ts +18 -0
  75. package/dist/components/forms/Switch.svelte +44 -0
  76. package/dist/components/forms/Switch.svelte.d.ts +12 -0
  77. package/dist/components/forms/Textarea.svelte +52 -0
  78. package/dist/components/forms/Textarea.svelte.d.ts +15 -0
  79. package/dist/components/forms/TimePicker.svelte +63 -0
  80. package/dist/components/forms/TimePicker.svelte.d.ts +16 -0
  81. package/dist/components/forms/formClasses.d.ts +3 -0
  82. package/dist/components/forms/formClasses.js +3 -0
  83. package/dist/components/forms/index.d.ts +12 -0
  84. package/dist/components/forms/index.js +12 -0
  85. package/dist/components/navigation/Breadcrumbs.svelte +56 -0
  86. package/dist/components/navigation/Breadcrumbs.svelte.d.ts +15 -0
  87. package/dist/components/navigation/ContextMenu.svelte +133 -0
  88. package/dist/components/navigation/ContextMenu.svelte.d.ts +18 -0
  89. package/dist/components/navigation/DropdownMenu.svelte +139 -0
  90. package/dist/components/navigation/DropdownMenu.svelte.d.ts +17 -0
  91. package/dist/components/navigation/Menu.svelte +72 -0
  92. package/dist/components/navigation/Menu.svelte.d.ts +15 -0
  93. package/dist/components/navigation/Navbar.svelte +111 -0
  94. package/dist/components/navigation/Navbar.svelte.d.ts +15 -0
  95. package/dist/components/navigation/Sidebar.svelte +236 -0
  96. package/dist/components/navigation/Sidebar.svelte.d.ts +12 -0
  97. package/dist/components/navigation/Tabs.svelte +86 -0
  98. package/dist/components/navigation/Tabs.svelte.d.ts +19 -0
  99. package/dist/components/navigation/index.d.ts +7 -0
  100. package/dist/components/navigation/index.js +7 -0
  101. package/dist/components/overlays/Alert.svelte +81 -0
  102. package/dist/components/overlays/Alert.svelte.d.ts +15 -0
  103. package/dist/components/overlays/CommandPalette.svelte +182 -0
  104. package/dist/components/overlays/CommandPalette.svelte.d.ts +16 -0
  105. package/dist/components/overlays/Drawer.svelte +158 -0
  106. package/dist/components/overlays/Drawer.svelte.d.ts +16 -0
  107. package/dist/components/overlays/Dropdown.svelte +62 -0
  108. package/dist/components/overlays/Dropdown.svelte.d.ts +11 -0
  109. package/dist/components/overlays/Modal.svelte +125 -0
  110. package/dist/components/overlays/Modal.svelte.d.ts +15 -0
  111. package/dist/components/overlays/Popover.svelte +106 -0
  112. package/dist/components/overlays/Popover.svelte.d.ts +11 -0
  113. package/dist/components/overlays/ProgressBar.svelte +29 -0
  114. package/dist/components/overlays/ProgressBar.svelte.d.ts +10 -0
  115. package/dist/components/overlays/SkeletonLoader.svelte +66 -0
  116. package/dist/components/overlays/SkeletonLoader.svelte.d.ts +9 -0
  117. package/dist/components/overlays/Spinner.svelte +33 -0
  118. package/dist/components/overlays/Spinner.svelte.d.ts +7 -0
  119. package/dist/components/overlays/Toast.svelte +111 -0
  120. package/dist/components/overlays/Toast.svelte.d.ts +16 -0
  121. package/dist/components/overlays/Tooltip.svelte +94 -0
  122. package/dist/components/overlays/Tooltip.svelte.d.ts +12 -0
  123. package/dist/components/overlays/index.d.ts +11 -0
  124. package/dist/components/overlays/index.js +11 -0
  125. package/dist/components/typography/Code.svelte +10 -0
  126. package/dist/components/typography/Code.svelte.d.ts +6 -0
  127. package/dist/components/typography/Heading.svelte +15 -0
  128. package/dist/components/typography/Heading.svelte.d.ts +10 -0
  129. package/dist/components/typography/Text.svelte +21 -0
  130. package/dist/components/typography/Text.svelte.d.ts +10 -0
  131. package/dist/components/typography/index.d.ts +3 -0
  132. package/dist/components/typography/index.js +3 -0
  133. package/dist/components/utilities/Accordion.svelte +54 -0
  134. package/dist/components/utilities/Accordion.svelte.d.ts +17 -0
  135. package/dist/components/utilities/Carousel.svelte +124 -0
  136. package/dist/components/utilities/Carousel.svelte.d.ts +16 -0
  137. package/dist/components/utilities/Collapse.svelte +46 -0
  138. package/dist/components/utilities/Collapse.svelte.d.ts +10 -0
  139. package/dist/components/utilities/Hero.svelte +42 -0
  140. package/dist/components/utilities/Hero.svelte.d.ts +10 -0
  141. package/dist/components/utilities/Portal.svelte +47 -0
  142. package/dist/components/utilities/Portal.svelte.d.ts +21 -0
  143. package/dist/components/utilities/ScrollArea.svelte +33 -0
  144. package/dist/components/utilities/ScrollArea.svelte.d.ts +8 -0
  145. package/dist/components/utilities/SystemConsole.svelte +310 -0
  146. package/dist/components/utilities/SystemConsole.svelte.d.ts +20 -0
  147. package/dist/components/utilities/SystemInterface.svelte +726 -0
  148. package/dist/components/utilities/SystemInterface.svelte.d.ts +19 -0
  149. package/dist/components/utilities/index.d.ts +9 -0
  150. package/dist/components/utilities/index.js +8 -0
  151. package/dist/components/utilities/utilities.types.d.ts +46 -0
  152. package/dist/components/utilities/utilities.types.js +4 -0
  153. package/dist/index.d.ts +60 -175
  154. package/dist/index.js +24 -4560
  155. package/dist/lib/internal/id.d.ts +12 -0
  156. package/dist/lib/internal/id.js +15 -0
  157. package/dist/theme.css +2821 -0
  158. package/package.json +83 -75
  159. package/dist/index.css +0 -1
@@ -0,0 +1,15 @@
1
+ export interface Props {
2
+ value?: string;
3
+ min?: string;
4
+ max?: string;
5
+ placeholder?: string;
6
+ disabled?: boolean;
7
+ error?: boolean;
8
+ label?: string;
9
+ id?: string;
10
+ onchange?: (event: Event) => void;
11
+ class?: string;
12
+ }
13
+ declare const DatePicker: import("svelte").Component<Props, {}, "value">;
14
+ type DatePicker = ReturnType<typeof DatePicker>;
15
+ export default DatePicker;
@@ -0,0 +1,63 @@
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ import { baseInputClasses, focusClasses, disabledClasses, } from './formClasses';
3
+ let { value = $bindable(''), min, max, step, placeholder, disabled = false, error = false, label, id = createId('datetimepicker'), onchange, class: className = '', } = $props();
4
+ const errorClasses = $derived(error ? 'error-state' : '');
5
+ </script>
6
+
7
+ {#if label}
8
+ <div>
9
+ <label for={id} class="text-text-soft text-sm mb-2 block">
10
+ {label}
11
+ </label>
12
+ <input
13
+ type="datetime-local"
14
+ {id}
15
+ {placeholder}
16
+ {disabled}
17
+ {min}
18
+ {max}
19
+ {step}
20
+ bind:value
21
+ {onchange}
22
+ class="{baseInputClasses} {focusClasses} {errorClasses} {disabled
23
+ ? disabledClasses
24
+ : ''} {className}"
25
+ />
26
+ </div>
27
+ {:else}
28
+ <input
29
+ type="datetime-local"
30
+ {id}
31
+ {placeholder}
32
+ {disabled}
33
+ {min}
34
+ {max}
35
+ {step}
36
+ bind:value
37
+ {onchange}
38
+ class="{baseInputClasses} {focusClasses} {errorClasses} {disabled
39
+ ? disabledClasses
40
+ : ''} {className}"
41
+ />
42
+ {/if}
43
+
44
+ <style>
45
+ input[type='datetime-local'] {
46
+ min-height: 3rem;
47
+ cursor: pointer;
48
+ }
49
+
50
+ input[type='datetime-local']::-webkit-calendar-picker-indicator {
51
+ cursor: pointer;
52
+ filter: invert(0.7);
53
+ transition: filter 120ms var(--ease-sharp);
54
+ }
55
+
56
+ input[type='datetime-local']:hover::-webkit-calendar-picker-indicator {
57
+ filter: invert(1);
58
+ }
59
+
60
+ input[type='datetime-local']:disabled::-webkit-calendar-picker-indicator {
61
+ cursor: not-allowed;
62
+ opacity: 0.4;
63
+ }</style>
@@ -0,0 +1,16 @@
1
+ export interface Props {
2
+ value?: string;
3
+ min?: string;
4
+ max?: string;
5
+ step?: number;
6
+ placeholder?: string;
7
+ disabled?: boolean;
8
+ error?: boolean;
9
+ label?: string;
10
+ id?: string;
11
+ onchange?: (event: Event) => void;
12
+ class?: string;
13
+ }
14
+ declare const DateTimePicker: import("svelte").Component<Props, {}, "value">;
15
+ type DateTimePicker = ReturnType<typeof DateTimePicker>;
16
+ export default DateTimePicker;
@@ -0,0 +1,136 @@
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ let { files = $bindable(null), accept, multiple = false, disabled = false, label, id = createId('file-upload'), onchange, icon } = $props();
3
+ let isDragging = $state(false);
4
+ let inputElement;
5
+ function handleDragOver(event) {
6
+ event.preventDefault();
7
+ if (!disabled) {
8
+ isDragging = true;
9
+ }
10
+ }
11
+ function handleDragLeave() {
12
+ isDragging = false;
13
+ }
14
+ function handleDrop(event) {
15
+ event.preventDefault();
16
+ isDragging = false;
17
+ if (!disabled && event.dataTransfer?.files) {
18
+ files = event.dataTransfer.files;
19
+ if (onchange) {
20
+ // Call onchange directly with synthetic event object
21
+ onchange({ target: { files: event.dataTransfer.files } });
22
+ }
23
+ }
24
+ }
25
+ function handleClick() {
26
+ if (!disabled) {
27
+ inputElement?.click();
28
+ }
29
+ }
30
+ function handleInputChange(event) {
31
+ const input = event.target;
32
+ if (onchange && input.files) {
33
+ // Call onchange with consistent event shape
34
+ onchange({ target: { files: input.files } });
35
+ }
36
+ }
37
+ const fileList = $derived(files ? Array.from(files) : []);
38
+ </script>
39
+
40
+ <div>
41
+ {#if label}
42
+ <div class="text-text-soft text-sm mb-2 block">
43
+ {label}
44
+ </div>
45
+ {/if}
46
+
47
+ <input
48
+ type="file"
49
+ {id}
50
+ {accept}
51
+ {multiple}
52
+ {disabled}
53
+ bind:this={inputElement}
54
+ bind:files
55
+ onchange={handleInputChange}
56
+ class="hidden"
57
+ />
58
+
59
+ <div
60
+ role="button"
61
+ tabindex={disabled ? -1 : 0}
62
+ aria-label="Click or drag files to upload"
63
+ ondragover={handleDragOver}
64
+ ondragleave={handleDragLeave}
65
+ ondrop={handleDrop}
66
+ onclick={handleClick}
67
+ onkeydown={(e) => e.key === 'Enter' && handleClick()}
68
+ class="panel-raised rounded-[var(--radius-lg)] p-8 text-center cursor-pointer transition-all duration-300 ease-luxe {isDragging ? 'border-2 border-accent accent-glow bg-base-2' : ''} {disabled ? 'opacity-50 cursor-not-allowed pointer-events-none' : ''}"
69
+ >
70
+ <div class="flex flex-col items-center gap-2">
71
+ {#if icon}
72
+ <span class="inline-flex items-center justify-center text-text-soft">
73
+ {@render icon()}
74
+ </span>
75
+ {:else}
76
+ <svg
77
+ xmlns="http://www.w3.org/2000/svg"
78
+ width="48"
79
+ height="48"
80
+ viewBox="0 0 24 24"
81
+ fill="none"
82
+ stroke="currentColor"
83
+ stroke-width="2"
84
+ stroke-linecap="round"
85
+ stroke-linejoin="round"
86
+ class="text-text-soft"
87
+ >
88
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
89
+ <polyline points="17 8 12 3 7 8" />
90
+ <line x1="12" y1="3" x2="12" y2="15" />
91
+ </svg>
92
+ {/if}
93
+
94
+ <div>
95
+ <p class="text-text text-sm font-medium">
96
+ Click to upload or drag and drop
97
+ </p>
98
+ <p class="text-text-muted text-xs mt-1">
99
+ {accept ? `Accepted formats: ${accept}` : 'Any file type'}
100
+ </p>
101
+ </div>
102
+ </div>
103
+ </div>
104
+
105
+ {#if fileList.length > 0}
106
+ <div class="mt-4">
107
+ <p class="text-text-soft text-xs mb-2">
108
+ Selected files:
109
+ </p>
110
+ <ul class="space-y-1">
111
+ {#each fileList as file}
112
+ <li class="text-text text-xs flex items-center gap-2">
113
+ <svg
114
+ xmlns="http://www.w3.org/2000/svg"
115
+ width="12"
116
+ height="12"
117
+ viewBox="0 0 24 24"
118
+ fill="none"
119
+ stroke="currentColor"
120
+ stroke-width="2"
121
+ stroke-linecap="round"
122
+ stroke-linejoin="round"
123
+ >
124
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
125
+ <polyline points="14 2 14 8 20 8" />
126
+ </svg>
127
+ <span>{file.name}</span>
128
+ <span class="text-text-muted">
129
+ ({(file.size / 1024).toFixed(1)} KB)
130
+ </span>
131
+ </li>
132
+ {/each}
133
+ </ul>
134
+ </div>
135
+ {/if}
136
+ </div>
@@ -0,0 +1,23 @@
1
+ /**
2
+ * FileUpload component props.
3
+ *
4
+ * The `onchange` callback receives an event object with the following shape:
5
+ * `{ target: { files: FileList } }` for both drag-and-drop and click-based selection.
6
+ */
7
+ interface Props {
8
+ files?: FileList | null;
9
+ accept?: string;
10
+ multiple?: boolean;
11
+ disabled?: boolean;
12
+ label?: string;
13
+ id?: string;
14
+ onchange?: (event: {
15
+ target: {
16
+ files: FileList;
17
+ };
18
+ }) => void;
19
+ icon?: import('svelte').Snippet;
20
+ }
21
+ declare const FileUpload: import("svelte").Component<Props, {}, "files">;
22
+ type FileUpload = ReturnType<typeof FileUpload>;
23
+ export default FileUpload;
@@ -0,0 +1,282 @@
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ import { baseInputClasses, focusClasses, disabledClasses, } from './formClasses';
3
+ let { type = 'text', value = $bindable(''), placeholder, disabled = false, error = false, label, id = createId('input'), oninput, iconBefore, iconAfter, validate, validationMessage, showValidation = true, class: className = '', } = $props();
4
+ // Built-in validation patterns
5
+ const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
6
+ const urlPattern = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
7
+ const telPattern = /^[\d\s\-\+\(\)]+$/;
8
+ // Validation logic
9
+ let validationError = $state(null);
10
+ function performValidation(val) {
11
+ if (!val || val === '') {
12
+ validationError = null;
13
+ return true;
14
+ }
15
+ // Custom validation function
16
+ if (validate) {
17
+ const result = validate(val);
18
+ if (result === true) {
19
+ validationError = null;
20
+ return true;
21
+ }
22
+ else if (typeof result === 'string') {
23
+ validationError = result;
24
+ return false;
25
+ }
26
+ else {
27
+ validationError = validationMessage || 'Invalid input';
28
+ return false;
29
+ }
30
+ }
31
+ // Built-in validation based on type
32
+ const stringVal = String(val);
33
+ switch (type) {
34
+ case 'email':
35
+ if (!emailPattern.test(stringVal)) {
36
+ validationError = validationMessage || 'Please enter a valid email address';
37
+ return false;
38
+ }
39
+ break;
40
+ case 'url':
41
+ if (!urlPattern.test(stringVal)) {
42
+ validationError = validationMessage || 'Please enter a valid URL';
43
+ return false;
44
+ }
45
+ break;
46
+ case 'tel':
47
+ if (!telPattern.test(stringVal)) {
48
+ validationError = validationMessage || 'Please enter a valid phone number';
49
+ return false;
50
+ }
51
+ break;
52
+ }
53
+ validationError = null;
54
+ return true;
55
+ }
56
+ // Validate on value change
57
+ $effect(() => {
58
+ if (value !== undefined && value !== '') {
59
+ performValidation(value);
60
+ }
61
+ });
62
+ const hasError = $derived(error || (validationError !== null));
63
+ const errorClasses = $derived(hasError ? 'error-state' : '');
64
+ function increment() {
65
+ if (disabled || type !== 'number')
66
+ return;
67
+ value = Number(value || 0) + 1;
68
+ }
69
+ function decrement() {
70
+ if (disabled || type !== 'number')
71
+ return;
72
+ value = Number(value || 0) - 1;
73
+ }
74
+ </script>
75
+
76
+ {#if label}
77
+ <div>
78
+ <label for={id} class="text-text-soft text-sm mb-2 block">
79
+ {label}
80
+ </label>
81
+ <div class="relative flex items-center">
82
+ <!-- BEFORE ICON -->
83
+ {#if iconBefore}
84
+ <span
85
+ class="absolute left-4 z-10 inline-flex items-center justify-center text-text-soft pointer-events-none"
86
+ >
87
+ {@render iconBefore()}
88
+ </span>
89
+ {/if}
90
+
91
+ <!-- INPUT FIELD -->
92
+ <input
93
+ {type}
94
+ {id}
95
+ {placeholder}
96
+ {disabled}
97
+ bind:value
98
+ {oninput}
99
+ class="{baseInputClasses} {focusClasses} {errorClasses} {disabled
100
+ ? disabledClasses
101
+ : ''} {className}"
102
+ class:pl-12={iconBefore}
103
+ class:pr-12={iconAfter || type === 'number'}
104
+ />
105
+
106
+ <!-- AFTER ICON (non-number) -->
107
+ {#if iconAfter && type !== 'number'}
108
+ <span
109
+ class="absolute right-4 z-10 inline-flex items-center justify-center text-text-soft pointer-events-none"
110
+ >
111
+ {@render iconAfter()}
112
+ </span>
113
+ {/if}
114
+
115
+ <!-- CUSTOM ARROWS (NUMBER INPUT ONLY) -->
116
+ {#if type === 'number'}
117
+ <div class="number-arrows">
118
+ <button
119
+ type="button"
120
+ onclick={increment}
121
+ class="arrow-btn arrow-up"
122
+ tabindex="-1"
123
+ >
124
+
125
+ </button>
126
+ <button
127
+ type="button"
128
+ onclick={decrement}
129
+ class="arrow-btn arrow-down"
130
+ tabindex="-1"
131
+ >
132
+
133
+ </button>
134
+ </div>
135
+ {/if}
136
+ </div>
137
+ {#if showValidation && validationError}
138
+ <p class="text-error text-xs mt-1.5">
139
+ {validationError}
140
+ </p>
141
+ {/if}
142
+ </div>
143
+ {:else}
144
+ <div>
145
+ <div class="relative flex items-center">
146
+ {#if iconBefore}
147
+ <span
148
+ class="absolute left-4 z-10 inline-flex items-center justify-center text-text-soft pointer-events-none"
149
+ >
150
+ {@render iconBefore()}
151
+ </span>
152
+ {/if}
153
+
154
+ <input
155
+ {type}
156
+ {id}
157
+ {placeholder}
158
+ {disabled}
159
+ bind:value
160
+ {oninput}
161
+ class="{baseInputClasses} {focusClasses} {errorClasses} {disabled
162
+ ? disabledClasses
163
+ : ''} {className}"
164
+ class:pl-12={iconBefore}
165
+ class:pr-12={iconAfter || type === 'number'}
166
+ />
167
+
168
+ {#if iconAfter && type !== 'number'}
169
+ <span
170
+ class="absolute right-4 z-10 inline-flex items-center justify-center text-text-soft pointer-events-none"
171
+ >
172
+ {@render iconAfter()}
173
+ </span>
174
+ {/if}
175
+
176
+ {#if type === 'number'}
177
+ <div class="number-arrows">
178
+ <button
179
+ type="button"
180
+ onclick={increment}
181
+ class="arrow-btn arrow-up"
182
+ tabindex="-1"
183
+ >
184
+
185
+ </button>
186
+ <button
187
+ type="button"
188
+ onclick={decrement}
189
+ class="arrow-btn arrow-down"
190
+ tabindex="-1"
191
+ >
192
+
193
+ </button>
194
+ </div>
195
+ {/if}
196
+ </div>
197
+ {#if showValidation && validationError}
198
+ <p class="text-error text-xs mt-1.5">
199
+ {validationError}
200
+ </p>
201
+ {/if}
202
+ </div>
203
+ {/if}
204
+
205
+ <style>
206
+ .number-arrows {
207
+ position: absolute;
208
+ top: 0;
209
+ right: 0;
210
+ height: 100%;
211
+ width: 2.25rem; /* doesn't overlap your icons */
212
+ display: flex;
213
+ flex-direction: column;
214
+ border-left: 1px solid var(--color-border-strong);
215
+ background: color-mix(in srgb, var(--color-accent) 8%, transparent);
216
+ border-radius: 0 var(--radius-md) var(--radius-md) 0;
217
+ overflow: hidden;
218
+ }
219
+
220
+ .arrow-btn {
221
+ flex: 1;
222
+ display: flex;
223
+ align-items: center;
224
+ justify-content: center;
225
+ background: transparent;
226
+ color: var(--color-text-soft);
227
+ font-size: 0.7rem;
228
+ cursor: pointer;
229
+ transition: all 120ms var(--ease-sharp);
230
+ user-select: none;
231
+ }
232
+
233
+ .arrow-btn:hover {
234
+ background: var(--color-accent-overlay-20);
235
+ color: var(--color-accent-soft);
236
+ }
237
+
238
+ .arrow-btn:active {
239
+ background: var(--color-accent-overlay-30);
240
+ color: var(--color-accent);
241
+ }
242
+
243
+ .arrow-btn:disabled,
244
+ .arrow-btn[disabled] {
245
+ opacity: 0.4;
246
+ cursor: not-allowed;
247
+ }
248
+
249
+ /* Remove default browser arrows */
250
+ input[type='number']::-webkit-inner-spin-button,
251
+ input[type='number']::-webkit-outer-spin-button {
252
+ -webkit-appearance: none;
253
+ margin: 0;
254
+ }
255
+
256
+ input[type='number'] {
257
+ -moz-appearance: textfield;
258
+ appearance: textfield;
259
+ }
260
+
261
+ /* Color input styling */
262
+ input[type='color'] {
263
+ width: 60px;
264
+ min-width: 60px;
265
+ min-height: 3rem;
266
+ padding: 0.375rem;
267
+ cursor: pointer;
268
+ }
269
+
270
+ input[type='color']::-webkit-color-swatch-wrapper {
271
+ padding: 0;
272
+ }
273
+
274
+ input[type='color']::-webkit-color-swatch {
275
+ border: 1px solid var(--color-border-strong);
276
+ border-radius: calc(var(--radius-md) - 2px);
277
+ }
278
+
279
+ input[type='color']::-moz-color-swatch {
280
+ border: 1px solid var(--color-border-strong);
281
+ border-radius: calc(var(--radius-md) - 2px);
282
+ }</style>
@@ -0,0 +1,19 @@
1
+ export interface Props {
2
+ type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'color' | 'search';
3
+ value?: string | number;
4
+ placeholder?: string;
5
+ disabled?: boolean;
6
+ error?: boolean;
7
+ label?: string;
8
+ id?: string;
9
+ oninput?: (event: Event) => void;
10
+ iconBefore?: import('svelte').Snippet;
11
+ iconAfter?: import('svelte').Snippet;
12
+ validate?: (value: string | number) => boolean | string;
13
+ validationMessage?: string;
14
+ showValidation?: boolean;
15
+ class?: string;
16
+ }
17
+ declare const Input: import("svelte").Component<Props, {}, "value">;
18
+ type Input = ReturnType<typeof Input>;
19
+ export default Input;
@@ -0,0 +1,7 @@
1
+ <script lang="ts">"use strict";
2
+ // No props needed for standard slot usage
3
+ </script>
4
+
5
+ <div class="flex items-stretch rounded-[var(--radius-lg)] overflow-hidden panel-raised [&>*:not(:first-child)]:ml-[-1px] [&>*:not(:first-child):not(:last-child)]:rounded-none [&>*:first-child]:rounded-r-none [&>*:last-child]:rounded-l-none">
6
+ <slot />
7
+ </div>
@@ -0,0 +1,20 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const InputGroup: $$__sveltets_2_IsomorphicComponent<any, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {
17
+ default: {};
18
+ }, {}, string>;
19
+ type InputGroup = InstanceType<typeof InputGroup>;
20
+ export default InputGroup;
@@ -0,0 +1,77 @@
1
+ <script lang="ts">let { value = $bindable(''), options, name, disabled = false, orientation = 'vertical', label, onchange, class: className = '' } = $props();
2
+ const containerClasses = $derived(orientation === 'vertical' ? 'flex flex-col gap-3' : 'flex flex-row gap-4');
3
+ export {};
4
+ </script>
5
+
6
+ {#if label}
7
+ <fieldset class="{disabled ? 'opacity-50' : ''} {className}">
8
+ <legend class="text-[var(--color-text-soft)] text-sm mb-3 block">
9
+ {label}
10
+ </legend>
11
+ <div class={containerClasses}>
12
+ {#each options as option}
13
+ {@const radioId = `${name}-${option.value}`}
14
+ <label for={radioId} class="flex items-center gap-2 cursor-pointer {disabled ? 'cursor-not-allowed' : ''}">
15
+ <input
16
+ type="radio"
17
+ id={radioId}
18
+ {name}
19
+ value={option.value}
20
+ {disabled}
21
+ bind:group={value}
22
+ onchange={onchange}
23
+ class="w-6 h-6 rounded-[var(--radius-pill)] border-2 border-[var(--color-border-strong)] bg-transparent appearance-none transition-all duration-200 ease-[var(--ease-luxe)] cursor-pointer checked:border-[var(--color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent)] focus:ring-offset-2 focus:ring-offset-[var(--color-base-1)] relative {disabled ? 'cursor-not-allowed' : ''}"
24
+ />
25
+ {#if option.iconBefore}
26
+ <span class="inline-flex items-center justify-center text-text-soft">
27
+ {@render option.iconBefore()}
28
+ </span>
29
+ {/if}
30
+ <span class="text-[var(--color-text)] text-sm select-none">
31
+ {option.label}
32
+ </span>
33
+ </label>
34
+ {/each}
35
+ </div>
36
+ </fieldset>
37
+ {:else}
38
+ <div class="{containerClasses} {className}">
39
+ {#each options as option}
40
+ {@const radioId = `${name}-${option.value}`}
41
+ <label for={radioId} class="flex items-center gap-2 cursor-pointer {disabled ? 'opacity-50 cursor-not-allowed' : ''}">
42
+ <input
43
+ type="radio"
44
+ id={radioId}
45
+ {name}
46
+ value={option.value}
47
+ {disabled}
48
+ bind:group={value}
49
+ onchange={onchange}
50
+ class="w-6 h-6 rounded-[var(--radius-pill)] border-2 border-[var(--color-border-strong)] bg-transparent appearance-none transition-all duration-200 ease-[var(--ease-luxe)] cursor-pointer checked:border-[var(--color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent)] focus:ring-offset-2 focus:ring-offset-[var(--color-base-1)] relative {disabled ? 'cursor-not-allowed' : ''}"
51
+ />
52
+ {#if option.iconBefore}
53
+ <span class="inline-flex items-center justify-center text-text-soft">
54
+ {@render option.iconBefore()}
55
+ </span>
56
+ {/if}
57
+ <span class="text-[var(--color-text)] text-sm select-none">
58
+ {option.label}
59
+ </span>
60
+ </label>
61
+ {/each}
62
+ </div>
63
+ {/if}
64
+
65
+ <style>
66
+ input[type="radio"]:checked::after {
67
+ content: '';
68
+ position: absolute;
69
+ left: 50%;
70
+ top: 50%;
71
+ transform: translate(-50%, -50%);
72
+ width: 0.75rem;
73
+ height: 0.75rem;
74
+ border-radius: var(--radius-pill);
75
+ background: var(--color-accent);
76
+ box-shadow: var(--shadow-accent-glow);
77
+ }</style>
@@ -0,0 +1,17 @@
1
+ export interface Props {
2
+ value?: string;
3
+ options: Array<{
4
+ value: string;
5
+ label: string;
6
+ iconBefore?: import('svelte').Snippet;
7
+ }>;
8
+ name: string;
9
+ disabled?: boolean;
10
+ orientation?: 'vertical' | 'horizontal';
11
+ label?: string;
12
+ onchange?: (event: Event) => void;
13
+ class?: string;
14
+ }
15
+ declare const RadioGroup: import("svelte").Component<Props, {}, "value">;
16
+ type RadioGroup = ReturnType<typeof RadioGroup>;
17
+ export default RadioGroup;