@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,90 @@
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ let { value = $bindable(0), min = 0, max = 100, step = 1, disabled = false, label, showValue = true, id = createId('range'), oninput } = $props();
3
+ const percentage = $derived(max === min ? 0 : ((value - min) / (max - min)) * 100);
4
+ </script>
5
+
6
+ <div>
7
+ {#if label || showValue}
8
+ <div class="flex justify-between items-center mb-2">
9
+ {#if label}
10
+ <label for={id} class="text-text-soft text-sm">
11
+ {label}
12
+ </label>
13
+ {/if}
14
+ {#if showValue}
15
+ <span class="text-text-soft text-sm">
16
+ {value}
17
+ </span>
18
+ {/if}
19
+ </div>
20
+ {/if}
21
+ <input
22
+ type="range"
23
+ {id}
24
+ {min}
25
+ {max}
26
+ {step}
27
+ {disabled}
28
+ bind:value
29
+ oninput={oninput}
30
+ aria-valuemin={min}
31
+ aria-valuemax={max}
32
+ aria-valuenow={value}
33
+ class="w-full h-2 rounded-pill appearance-none cursor-pointer transition-opacity duration-200 {disabled ? 'opacity-50 cursor-not-allowed' : ''}"
34
+ style="background: linear-gradient(to right, var(--color-accent) 0%, var(--color-accent) {percentage}%, var(--color-base-3) {percentage}%, var(--color-base-3) 100%); border: 1px solid var(--color-border);"
35
+ />
36
+ </div>
37
+
38
+ <style>
39
+ input[type="range"]::-webkit-slider-thumb {
40
+ appearance: none;
41
+ width: 1.25rem;
42
+ height: 1.25rem;
43
+ border-radius: var(--radius-pill);
44
+ background: var(--color-accent);
45
+ box-shadow: var(--shadow-accent-glow);
46
+ cursor: pointer;
47
+ transition: transform 0.2s var(--ease-luxe);
48
+ }
49
+
50
+ input[type="range"]::-webkit-slider-thumb:hover {
51
+ transform: scale(1.1);
52
+ }
53
+
54
+ input[type="range"]::-moz-range-thumb {
55
+ appearance: none;
56
+ width: 1.25rem;
57
+ height: 1.25rem;
58
+ border-radius: var(--radius-pill);
59
+ background: var(--color-accent);
60
+ box-shadow: var(--shadow-accent-glow);
61
+ cursor: pointer;
62
+ transition: transform 0.2s var(--ease-luxe);
63
+ border: none;
64
+ }
65
+
66
+ input[type="range"]::-moz-range-thumb:hover {
67
+ transform: scale(1.1);
68
+ }
69
+
70
+ input[type="range"]:focus::-webkit-slider-thumb {
71
+ box-shadow:
72
+ var(--shadow-accent-glow),
73
+ 0 0 0 3px var(--color-base-1),
74
+ 0 0 0 5px var(--color-accent);
75
+ }
76
+
77
+ input[type="range"]:focus::-moz-range-thumb {
78
+ box-shadow:
79
+ var(--shadow-accent-glow),
80
+ 0 0 0 3px var(--color-base-1),
81
+ 0 0 0 5px var(--color-accent);
82
+ }
83
+
84
+ input[type="range"]:disabled::-webkit-slider-thumb {
85
+ cursor: not-allowed;
86
+ }
87
+
88
+ input[type="range"]:disabled::-moz-range-thumb {
89
+ cursor: not-allowed;
90
+ }</style>
@@ -0,0 +1,14 @@
1
+ interface Props {
2
+ value?: number;
3
+ min?: number;
4
+ max?: number;
5
+ step?: number;
6
+ disabled?: boolean;
7
+ label?: string;
8
+ showValue?: boolean;
9
+ id?: string;
10
+ oninput?: (event: Event) => void;
11
+ }
12
+ declare const RangeSlider: import("svelte").Component<Props, {}, "value">;
13
+ type RangeSlider = ReturnType<typeof RangeSlider>;
14
+ export default RangeSlider;
@@ -0,0 +1,106 @@
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ import { baseInputClasses, focusClasses, disabledClasses, } from './formClasses';
3
+ let { value = $bindable(''), options, placeholder, disabled = false, error = false, label, id = createId('select'), onchange, iconBefore, class: className = '', } = $props();
4
+ const chevronIcon = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none'%3E%3Cpath d='M6 9l6 6 6-6' stroke='%23A855F7' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E";
5
+ const selectClasses = `${baseInputClasses} pr-12 appearance-none bg-no-repeat bg-[right_1rem_center] bg-[length:16px] cursor-pointer`;
6
+ const errorClasses = $derived(error ? 'error-state' : '');
7
+ </script>
8
+
9
+ {#if label}
10
+ <div>
11
+ <label for={id} class="text-text-soft text-sm mb-2 block">
12
+ {label}
13
+ </label>
14
+ <div class="relative">
15
+ {#if iconBefore}
16
+ <span
17
+ class="absolute left-4 top-1/2 -translate-y-1/2 z-10 inline-flex items-center justify-center text-text-soft pointer-events-none"
18
+ >
19
+ {@render iconBefore()}
20
+ </span>
21
+ {/if}
22
+
23
+ <select
24
+ {id}
25
+ {disabled}
26
+ bind:value
27
+ {onchange}
28
+ class="{selectClasses} {focusClasses} {errorClasses} {disabled
29
+ ? disabledClasses
30
+ : ''} {className}"
31
+ class:pl-12={iconBefore}
32
+ style={`background-image: url('${chevronIcon}')`}
33
+ >
34
+ {#if placeholder}
35
+ <option disabled value="" aria-hidden="true" hidden
36
+ >{placeholder}</option
37
+ >
38
+ {/if}
39
+
40
+ {#each options as option}
41
+ <option value={option.value}>{option.label}</option>
42
+ {/each}
43
+ </select>
44
+ </div>
45
+ </div>
46
+ {:else}
47
+ <div class="relative">
48
+ {#if iconBefore}
49
+ <span
50
+ class="absolute left-4 top-1/2 -translate-y-1/2 z-10 inline-flex items-center justify-center text-text-soft pointer-events-none"
51
+ >
52
+ {@render iconBefore()}
53
+ </span>
54
+ {/if}
55
+
56
+ <select
57
+ {id}
58
+ {disabled}
59
+ bind:value
60
+ {onchange}
61
+ class="{selectClasses} {focusClasses} {errorClasses} {disabled
62
+ ? disabledClasses
63
+ : ''} {className}"
64
+ class:pl-12={iconBefore}
65
+ style={`background-image: url('${chevronIcon}')`}
66
+ >
67
+ {#if placeholder}
68
+ <option disabled value="" aria-hidden="true" hidden
69
+ >{placeholder}</option
70
+ >
71
+ {/if}
72
+
73
+ {#each options as option}
74
+ <option value={option.value}>{option.label}</option>
75
+ {/each}
76
+ </select>
77
+ </div>
78
+ {/if}
79
+
80
+ <style>
81
+ /* Force dark dropdown (Firefox + Chromium) */
82
+ select {
83
+ color-scheme: var(--color-base-0);
84
+ }
85
+
86
+ /* Option styling */
87
+ select option {
88
+ background-color: var(--color-base-2);
89
+ color: var(--color-text);
90
+ padding: 0.5rem;
91
+ }
92
+
93
+ select option:hover {
94
+ background-color: var(--color-accent-overlay-50);
95
+ color: var(--color-text);
96
+ }
97
+ select option:focus,
98
+ select option:checked {
99
+ background-color: var(--color-accent);
100
+ color: var(--color-text);
101
+ }
102
+
103
+ select option:disabled {
104
+ color: var(--color-text-muted);
105
+ opacity: 0.5;
106
+ }</style>
@@ -0,0 +1,18 @@
1
+ export interface Props {
2
+ value?: string;
3
+ options: Array<{
4
+ value: string;
5
+ label: string;
6
+ }>;
7
+ placeholder?: string;
8
+ disabled?: boolean;
9
+ error?: boolean;
10
+ label?: string;
11
+ id?: string;
12
+ onchange?: (event: Event) => void;
13
+ iconBefore?: import('svelte').Snippet;
14
+ class?: string;
15
+ }
16
+ declare const Select: import("svelte").Component<Props, {}, "value">;
17
+ type Select = ReturnType<typeof Select>;
18
+ export default Select;
@@ -0,0 +1,44 @@
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ let { checked = $bindable(false), disabled = false, label, id = createId('switch'), onchange, iconBefore, class: className = '' } = $props();
3
+ </script>
4
+
5
+ <label for={id} class="flex items-center gap-2 cursor-pointer {disabled ? 'opacity-50 cursor-not-allowed' : ''} {className}">
6
+ <input
7
+ type="checkbox"
8
+ role="switch"
9
+ aria-checked={checked}
10
+ {id}
11
+ {disabled}
12
+ bind:checked
13
+ onchange={onchange}
14
+ class="w-14 h-7 rounded-pill bg-base-3 border border-border appearance-none transition-all duration-300 ease-luxe cursor-pointer checked:bg-accent checked:accent-glow focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 focus:ring-offset-base-1 relative {disabled ? 'cursor-not-allowed' : ''}"
15
+ />
16
+ {#if iconBefore}
17
+ <span class="inline-flex items-center justify-center text-text-soft">
18
+ {@render iconBefore()}
19
+ </span>
20
+ {/if}
21
+ {#if label}
22
+ <span class="text-text text-sm select-none">
23
+ {label}
24
+ </span>
25
+ {/if}
26
+ </label>
27
+
28
+ <style>
29
+ input[type="checkbox"]::after {
30
+ content: '';
31
+ position: absolute;
32
+ left: 0.375rem;
33
+ top: 50%;
34
+ transform: translateY(-50%);
35
+ width: 1.25rem;
36
+ height: 1.25rem;
37
+ border-radius: var(--radius-pill);
38
+ background: var(--color-text);
39
+ transition: all 0.3s var(--ease-luxe);
40
+ }
41
+
42
+ input[type="checkbox"]:checked::after {
43
+ transform: translate(1.75rem, -50%);
44
+ }</style>
@@ -0,0 +1,12 @@
1
+ export interface Props {
2
+ checked?: boolean;
3
+ disabled?: boolean;
4
+ label?: string;
5
+ id?: string;
6
+ onchange?: (event: Event) => void;
7
+ iconBefore?: import('svelte').Snippet;
8
+ class?: string;
9
+ }
10
+ declare const Switch: import("svelte").Component<Props, {}, "checked">;
11
+ type Switch = ReturnType<typeof Switch>;
12
+ export default Switch;
@@ -0,0 +1,52 @@
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ import { baseInputClasses, focusClasses, disabledClasses } from './formClasses';
3
+ let { value = $bindable(''), placeholder, rows = 4, disabled = false, error = false, label, id = createId('textarea'), oninput, iconBefore, class: className = '' } = $props();
4
+ // Icon spacing: pl-12 (3rem) = left-4 (1rem) icon position + ~2rem for icon width and spacing
5
+ // top-4 keeps icon fixed at top when textarea is resized
6
+ // This ensures text doesn't overlap with the absolutely positioned icon
7
+ const textareaClasses = `${baseInputClasses} resize-y min-h-[100px]`;
8
+ const errorClasses = $derived(error ? 'error-state' : '');
9
+ </script>
10
+
11
+ {#if label}
12
+ <div>
13
+ <label for={id} class="text-text-soft text-sm mb-2 block">
14
+ {label}
15
+ </label>
16
+ <div class="relative">
17
+ {#if iconBefore}
18
+ <span class="absolute left-4 top-4 z-10 inline-flex items-center justify-center text-text-soft pointer-events-none">
19
+ {@render iconBefore()}
20
+ </span>
21
+ {/if}
22
+ <textarea
23
+ {id}
24
+ {placeholder}
25
+ {disabled}
26
+ {rows}
27
+ bind:value
28
+ oninput={oninput}
29
+ class="{textareaClasses} {focusClasses} {errorClasses} {disabled ? disabledClasses : ''} {className}"
30
+ class:pl-12={iconBefore}
31
+ ></textarea>
32
+ </div>
33
+ </div>
34
+ {:else}
35
+ <div class="relative">
36
+ {#if iconBefore}
37
+ <span class="absolute left-4 top-4 z-10 inline-flex items-center justify-center text-text-soft pointer-events-none">
38
+ {@render iconBefore()}
39
+ </span>
40
+ {/if}
41
+ <textarea
42
+ {id}
43
+ {placeholder}
44
+ {disabled}
45
+ {rows}
46
+ bind:value
47
+ oninput={oninput}
48
+ class="{textareaClasses} {focusClasses} {errorClasses} {disabled ? disabledClasses : ''} {className}"
49
+ class:pl-12={iconBefore}
50
+ ></textarea>
51
+ </div>
52
+ {/if}
@@ -0,0 +1,15 @@
1
+ export interface Props {
2
+ value?: string;
3
+ placeholder?: string;
4
+ rows?: number;
5
+ disabled?: boolean;
6
+ error?: boolean;
7
+ label?: string;
8
+ id?: string;
9
+ oninput?: (event: Event) => void;
10
+ iconBefore?: import('svelte').Snippet;
11
+ class?: string;
12
+ }
13
+ declare const Textarea: import("svelte").Component<Props, {}, "value">;
14
+ type Textarea = ReturnType<typeof Textarea>;
15
+ export default Textarea;
@@ -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('timepicker'), 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="time"
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="time"
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='time'] {
46
+ min-height: 3rem;
47
+ cursor: pointer;
48
+ }
49
+
50
+ input[type='time']::-webkit-calendar-picker-indicator {
51
+ cursor: pointer;
52
+ filter: invert(0.7);
53
+ transition: filter 120ms var(--ease-sharp);
54
+ }
55
+
56
+ input[type='time']:hover::-webkit-calendar-picker-indicator {
57
+ filter: invert(1);
58
+ }
59
+
60
+ input[type='time']: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 TimePicker: import("svelte").Component<Props, {}, "value">;
15
+ type TimePicker = ReturnType<typeof TimePicker>;
16
+ export default TimePicker;
@@ -0,0 +1,3 @@
1
+ export declare const baseInputClasses = "glass-panel rounded-[var(--radius-lg)] px-5 py-3.5 font-body text-text placeholder:text-text-muted transition-all duration-300 ease-luxe w-full";
2
+ export declare const focusClasses = "focus:outline-none focus:border-accent focus:accent-glow";
3
+ export declare const disabledClasses = "opacity-50 cursor-not-allowed";
@@ -0,0 +1,3 @@
1
+ export const baseInputClasses = 'glass-panel rounded-[var(--radius-lg)] px-5 py-3.5 font-body text-text placeholder:text-text-muted transition-all duration-300 ease-luxe w-full';
2
+ export const focusClasses = 'focus:outline-none focus:border-accent focus:accent-glow';
3
+ export const disabledClasses = 'opacity-50 cursor-not-allowed';
@@ -0,0 +1,12 @@
1
+ export { default as Input } from './Input.svelte';
2
+ export { default as Textarea } from './Textarea.svelte';
3
+ export { default as Select } from './Select.svelte';
4
+ export { default as Checkbox } from './Checkbox.svelte';
5
+ export { default as Switch } from './Switch.svelte';
6
+ export { default as RadioGroup } from './RadioGroup.svelte';
7
+ export { default as RangeSlider } from './RangeSlider.svelte';
8
+ export { default as FileUpload } from './FileUpload.svelte';
9
+ export { default as InputGroup } from './InputGroup.svelte';
10
+ export { default as DatePicker } from './DatePicker.svelte';
11
+ export { default as TimePicker } from './TimePicker.svelte';
12
+ export { default as DateTimePicker } from './DateTimePicker.svelte';
@@ -0,0 +1,12 @@
1
+ export { default as Input } from './Input.svelte';
2
+ export { default as Textarea } from './Textarea.svelte';
3
+ export { default as Select } from './Select.svelte';
4
+ export { default as Checkbox } from './Checkbox.svelte';
5
+ export { default as Switch } from './Switch.svelte';
6
+ export { default as RadioGroup } from './RadioGroup.svelte';
7
+ export { default as RangeSlider } from './RangeSlider.svelte';
8
+ export { default as FileUpload } from './FileUpload.svelte';
9
+ export { default as InputGroup } from './InputGroup.svelte';
10
+ export { default as DatePicker } from './DatePicker.svelte';
11
+ export { default as TimePicker } from './TimePicker.svelte';
12
+ export { default as DateTimePicker } from './DateTimePicker.svelte';
@@ -0,0 +1,56 @@
1
+ <script lang="ts">let { items, separator = '/', separatorIcon } = $props();
2
+ export {};
3
+ </script>
4
+
5
+ <nav role="navigation" aria-label="Breadcrumb">
6
+ <ol class="flex items-center gap-2">
7
+ {#each items as item, index}
8
+ <li class="flex items-center gap-2">
9
+ {#if item.href}
10
+ <a
11
+ href={item.href}
12
+ class="flex items-center gap-2 text-sm font-[var(--font-body)] text-[var(--color-text-soft)] hover:text-[var(--color-accent)] hover:underline transition-colors duration-200 ease-[var(--ease-luxe)]"
13
+ >
14
+ {#if item.icon}
15
+ <span class="inline-flex items-center justify-center">
16
+ {@render item.icon()}
17
+ </span>
18
+ {/if}
19
+ {item.label}
20
+ </a>
21
+ {:else if item.onclick}
22
+ <button
23
+ onclick={item.onclick}
24
+ class="flex items-center gap-2 text-sm font-[var(--font-body)] text-[var(--color-text-soft)] hover:text-[var(--color-accent)] hover:underline transition-colors duration-200 ease-[var(--ease-luxe)] cursor-pointer"
25
+ >
26
+ {#if item.icon}
27
+ <span class="inline-flex items-center justify-center">
28
+ {@render item.icon()}
29
+ </span>
30
+ {/if}
31
+ {item.label}
32
+ </button>
33
+ {:else}
34
+ <span class="flex items-center gap-2 text-sm font-[var(--font-body)] {index === items.length - 1 ? 'text-[var(--color-text)]' : 'text-[var(--color-text-soft)]'}">
35
+ {#if item.icon}
36
+ <span class="inline-flex items-center justify-center">
37
+ {@render item.icon()}
38
+ </span>
39
+ {/if}
40
+ {item.label}
41
+ </span>
42
+ {/if}
43
+
44
+ {#if index < items.length - 1}
45
+ <span class="text-[var(--color-text-muted)] text-sm" aria-hidden="true">
46
+ {#if separatorIcon}
47
+ {@render separatorIcon()}
48
+ {:else}
49
+ {separator}
50
+ {/if}
51
+ </span>
52
+ {/if}
53
+ </li>
54
+ {/each}
55
+ </ol>
56
+ </nav>
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface BreadcrumbItem {
3
+ label: string;
4
+ href?: string;
5
+ onclick?: () => void;
6
+ icon?: Snippet;
7
+ }
8
+ interface Props {
9
+ items: BreadcrumbItem[];
10
+ separator?: string;
11
+ separatorIcon?: Snippet;
12
+ }
13
+ declare const Breadcrumbs: import("svelte").Component<Props, {}, "">;
14
+ type Breadcrumbs = ReturnType<typeof Breadcrumbs>;
15
+ export default Breadcrumbs;
@@ -0,0 +1,133 @@
1
+ <script lang="ts">import { createId } from '../../lib/internal/id.js';
2
+ let { items, open = $bindable(false), x = $bindable(0), y = $bindable(0), trigger } = $props();
3
+ let menuElement = $state();
4
+ let selectedIndex = $state(0);
5
+ const menuId = createId('context-menu');
6
+ function handleContextMenu(event) {
7
+ event.preventDefault();
8
+ x = event.clientX;
9
+ y = event.clientY;
10
+ open = true;
11
+ selectedIndex = 0;
12
+ // Adjust position if menu would overflow viewport
13
+ requestAnimationFrame(() => {
14
+ if (menuElement) {
15
+ const menuRect = menuElement.getBoundingClientRect();
16
+ const viewportWidth = window.innerWidth;
17
+ const viewportHeight = window.innerHeight;
18
+ if (x + menuRect.width > viewportWidth) {
19
+ x = viewportWidth - menuRect.width - 10;
20
+ }
21
+ if (y + menuRect.height > viewportHeight) {
22
+ y = viewportHeight - menuRect.height - 10;
23
+ }
24
+ }
25
+ });
26
+ }
27
+ function handleItemClick(item) {
28
+ if (item.disabled)
29
+ return;
30
+ item.onclick?.();
31
+ open = false;
32
+ }
33
+ function handleMenuKeyDown(event) {
34
+ // Derive current index from focused element to stay in sync
35
+ const focusedElement = document.activeElement;
36
+ const dataIndex = focusedElement?.getAttribute('data-index');
37
+ if (dataIndex !== null) {
38
+ const currentFocusedIndex = parseInt(dataIndex || '0', 10);
39
+ if (!isNaN(currentFocusedIndex) && currentFocusedIndex >= 0 && currentFocusedIndex < items.length) {
40
+ selectedIndex = currentFocusedIndex;
41
+ }
42
+ }
43
+ const enabledItems = items.filter(item => !item.disabled);
44
+ const currentEnabledIndex = enabledItems.findIndex(item => item.id === items[selectedIndex]?.id);
45
+ let nextIndex = currentEnabledIndex;
46
+ switch (event.key) {
47
+ case 'ArrowDown':
48
+ event.preventDefault();
49
+ nextIndex = currentEnabledIndex < enabledItems.length - 1 ? currentEnabledIndex + 1 : 0;
50
+ selectedIndex = items.findIndex(item => item.id === enabledItems[nextIndex]?.id);
51
+ break;
52
+ case 'ArrowUp':
53
+ event.preventDefault();
54
+ nextIndex = currentEnabledIndex > 0 ? currentEnabledIndex - 1 : enabledItems.length - 1;
55
+ selectedIndex = items.findIndex(item => item.id === enabledItems[nextIndex]?.id);
56
+ break;
57
+ case 'Home':
58
+ event.preventDefault();
59
+ selectedIndex = items.findIndex(item => item.id === enabledItems[0]?.id);
60
+ break;
61
+ case 'End':
62
+ event.preventDefault();
63
+ selectedIndex = items.findIndex(item => item.id === enabledItems[enabledItems.length - 1]?.id);
64
+ break;
65
+ case 'Enter':
66
+ case ' ':
67
+ event.preventDefault();
68
+ if (items[selectedIndex] && !items[selectedIndex].disabled) {
69
+ handleItemClick(items[selectedIndex]);
70
+ }
71
+ break;
72
+ case 'Escape':
73
+ event.preventDefault();
74
+ open = false;
75
+ break;
76
+ }
77
+ }
78
+ function handleClickOutside(event) {
79
+ if (menuElement && !menuElement.contains(event.target)) {
80
+ open = false;
81
+ }
82
+ }
83
+ $effect(() => {
84
+ if (open) {
85
+ document.addEventListener('click', handleClickOutside);
86
+ requestAnimationFrame(() => {
87
+ const firstItem = menuElement?.querySelector('[role="menuitem"]');
88
+ if (firstItem) {
89
+ const dataIndex = firstItem.getAttribute('data-index');
90
+ if (dataIndex !== null) {
91
+ selectedIndex = parseInt(dataIndex, 10);
92
+ }
93
+ firstItem.focus();
94
+ }
95
+ });
96
+ return () => {
97
+ document.removeEventListener('click', handleClickOutside);
98
+ };
99
+ }
100
+ });
101
+ </script>
102
+
103
+ <div oncontextmenu={handleContextMenu} role="presentation" class="contents">
104
+ {@render trigger?.()}
105
+ </div>
106
+
107
+ {#if open}
108
+ <div
109
+ bind:this={menuElement}
110
+ id={menuId}
111
+ role="menu"
112
+ tabindex="-1"
113
+ onkeydown={handleMenuKeyDown}
114
+ class="fixed z-[var(--z-50)] glass-panel rounded-[var(--radius-lg)] shadow-[var(--shadow-deep)] min-w-[12rem] animate-[fade-in_0.15s_var(--ease-luxe)]"
115
+ style="left: {x}px; top: {y}px;"
116
+ >
117
+ {#each items as item, index}
118
+ <button
119
+ role="menuitem"
120
+ data-index={index}
121
+ tabindex={index === selectedIndex ? 0 : -1}
122
+ onclick={() => handleItemClick(item)}
123
+ disabled={item.disabled}
124
+ class="w-full text-left px-4 py-2 text-sm text-text hover:bg-base-3 transition-colors duration-200 flex items-center gap-2 {item.disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}"
125
+ >
126
+ {#if item.icon}
127
+ {@render item.icon()}
128
+ {/if}
129
+ {item.label}
130
+ </button>
131
+ {/each}
132
+ </div>
133
+ {/if}