@mrintel/villain-ui 0.3.0 → 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 (128) 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 +60 -46
  15. package/dist/components/cards/Card.svelte.d.ts +6 -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 +282 -57
  57. package/dist/components/forms/Input.svelte.d.ts +9 -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/Switch.svelte +44 -56
  65. package/dist/components/forms/Switch.svelte.d.ts +3 -1
  66. package/dist/components/forms/Textarea.svelte +52 -57
  67. package/dist/components/forms/Textarea.svelte.d.ts +3 -1
  68. package/dist/components/forms/TimePicker.svelte +63 -0
  69. package/dist/components/forms/TimePicker.svelte.d.ts +16 -0
  70. package/dist/components/forms/formClasses.d.ts +3 -0
  71. package/dist/components/forms/formClasses.js +3 -0
  72. package/dist/components/forms/index.d.ts +3 -0
  73. package/dist/components/forms/index.js +3 -0
  74. package/dist/components/navigation/Breadcrumbs.svelte +56 -59
  75. package/dist/components/navigation/Breadcrumbs.svelte.d.ts +1 -0
  76. package/dist/components/navigation/ContextMenu.svelte +133 -83
  77. package/dist/components/navigation/ContextMenu.svelte.d.ts +8 -1
  78. package/dist/components/navigation/DropdownMenu.svelte +139 -80
  79. package/dist/components/navigation/DropdownMenu.svelte.d.ts +8 -1
  80. package/dist/components/navigation/Menu.svelte +72 -48
  81. package/dist/components/navigation/Navbar.svelte +111 -32
  82. package/dist/components/navigation/Navbar.svelte.d.ts +6 -0
  83. package/dist/components/navigation/Sidebar.svelte +236 -35
  84. package/dist/components/navigation/Sidebar.svelte.d.ts +2 -0
  85. package/dist/components/navigation/Tabs.svelte +86 -54
  86. package/dist/components/navigation/Tabs.svelte.d.ts +5 -1
  87. package/dist/components/overlays/Alert.svelte +81 -99
  88. package/dist/components/overlays/Alert.svelte.d.ts +5 -1
  89. package/dist/components/overlays/CommandPalette.svelte +182 -217
  90. package/dist/components/overlays/Drawer.svelte +158 -167
  91. package/dist/components/overlays/Drawer.svelte.d.ts +3 -1
  92. package/dist/components/overlays/Dropdown.svelte +62 -30
  93. package/dist/components/overlays/Dropdown.svelte.d.ts +2 -0
  94. package/dist/components/overlays/Modal.svelte +125 -130
  95. package/dist/components/overlays/Modal.svelte.d.ts +3 -1
  96. package/dist/components/overlays/Popover.svelte +106 -131
  97. package/dist/components/overlays/ProgressBar.svelte +29 -45
  98. package/dist/components/overlays/SkeletonLoader.svelte +66 -82
  99. package/dist/components/overlays/Spinner.svelte +33 -43
  100. package/dist/components/overlays/Toast.svelte +111 -140
  101. package/dist/components/overlays/Toast.svelte.d.ts +3 -0
  102. package/dist/components/overlays/Tooltip.svelte +94 -115
  103. package/dist/components/overlays/Tooltip.svelte.d.ts +3 -1
  104. package/dist/components/typography/Code.svelte +10 -14
  105. package/dist/components/typography/Heading.svelte +15 -22
  106. package/dist/components/typography/Heading.svelte.d.ts +1 -0
  107. package/dist/components/typography/Text.svelte +21 -24
  108. package/dist/components/typography/Text.svelte.d.ts +2 -1
  109. package/dist/components/utilities/Accordion.svelte +54 -67
  110. package/dist/components/utilities/Accordion.svelte.d.ts +4 -1
  111. package/dist/components/utilities/Carousel.svelte +124 -152
  112. package/dist/components/utilities/Collapse.svelte +46 -60
  113. package/dist/components/utilities/Hero.svelte +42 -0
  114. package/dist/components/utilities/Hero.svelte.d.ts +10 -0
  115. package/dist/components/utilities/Portal.svelte +47 -72
  116. package/dist/components/utilities/ScrollArea.svelte +33 -41
  117. package/dist/components/utilities/SystemConsole.svelte +310 -0
  118. package/dist/components/utilities/SystemConsole.svelte.d.ts +20 -0
  119. package/dist/components/utilities/SystemInterface.svelte +726 -0
  120. package/dist/components/utilities/SystemInterface.svelte.d.ts +19 -0
  121. package/dist/components/utilities/index.d.ts +4 -0
  122. package/dist/components/utilities/index.js +3 -0
  123. package/dist/components/utilities/utilities.types.d.ts +46 -0
  124. package/dist/components/utilities/utilities.types.js +4 -0
  125. package/dist/index.d.ts +49 -4
  126. package/dist/index.js +4 -4
  127. package/dist/theme.css +2821 -218
  128. package/package.json +83 -76
@@ -1,116 +1,90 @@
1
- <script lang="ts">
2
- import { createId } from '../../lib/internal/id.js';
3
-
4
- interface Props {
5
- value?: number;
6
- min?: number;
7
- max?: number;
8
- step?: number;
9
- disabled?: boolean;
10
- label?: string;
11
- showValue?: boolean;
12
- id?: string;
13
- oninput?: (event: Event) => void;
14
- }
15
-
16
- let {
17
- value = $bindable(0),
18
- min = 0,
19
- max = 100,
20
- step = 1,
21
- disabled = false,
22
- label,
23
- showValue = true,
24
- id = createId('range'),
25
- oninput
26
- }: Props = $props();
27
-
28
- const percentage = $derived(max === min ? 0 : ((value - min) / (max - min)) * 100);
29
- </script>
30
-
31
- <div>
32
- {#if label || showValue}
33
- <div class="flex justify-between items-center mb-2">
34
- {#if label}
35
- <label for={id} class="text-text-soft text-sm">
36
- {label}
37
- </label>
38
- {/if}
39
- {#if showValue}
40
- <span class="text-text-soft text-sm">
41
- {value}
42
- </span>
43
- {/if}
44
- </div>
45
- {/if}
46
- <input
47
- type="range"
48
- {id}
49
- {min}
50
- {max}
51
- {step}
52
- {disabled}
53
- bind:value
54
- oninput={oninput}
55
- aria-valuemin={min}
56
- aria-valuemax={max}
57
- aria-valuenow={value}
58
- class="w-full h-2 rounded-pill appearance-none cursor-pointer transition-opacity duration-200 {disabled ? 'opacity-50 cursor-not-allowed' : ''}"
59
- 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);"
60
- />
61
- </div>
62
-
63
- <style>
64
- input[type="range"]::-webkit-slider-thumb {
65
- appearance: none;
66
- width: 1.25rem;
67
- height: 1.25rem;
68
- border-radius: var(--radius-pill);
69
- background: var(--color-accent);
70
- box-shadow: var(--shadow-accent-glow);
71
- cursor: pointer;
72
- transition: transform 0.2s var(--ease-luxe);
73
- }
74
-
75
- input[type="range"]::-webkit-slider-thumb:hover {
76
- transform: scale(1.1);
77
- }
78
-
79
- input[type="range"]::-moz-range-thumb {
80
- appearance: none;
81
- width: 1.25rem;
82
- height: 1.25rem;
83
- border-radius: var(--radius-pill);
84
- background: var(--color-accent);
85
- box-shadow: var(--shadow-accent-glow);
86
- cursor: pointer;
87
- transition: transform 0.2s var(--ease-luxe);
88
- border: none;
89
- }
90
-
91
- input[type="range"]::-moz-range-thumb:hover {
92
- transform: scale(1.1);
93
- }
94
-
95
- input[type="range"]:focus::-webkit-slider-thumb {
96
- box-shadow:
97
- var(--shadow-accent-glow),
98
- 0 0 0 3px var(--color-base-1),
99
- 0 0 0 5px var(--color-accent);
100
- }
101
-
102
- input[type="range"]:focus::-moz-range-thumb {
103
- box-shadow:
104
- var(--shadow-accent-glow),
105
- 0 0 0 3px var(--color-base-1),
106
- 0 0 0 5px var(--color-accent);
107
- }
108
-
109
- input[type="range"]:disabled::-webkit-slider-thumb {
110
- cursor: not-allowed;
111
- }
112
-
113
- input[type="range"]:disabled::-moz-range-thumb {
114
- cursor: not-allowed;
115
- }
116
- </style>
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>
@@ -1,71 +1,106 @@
1
- <script lang="ts">
2
- import { createId } from '../../lib/internal/id.js';
3
-
4
- interface Props {
5
- value?: string;
6
- options: Array<{ value: string; label: string }>;
7
- placeholder?: string;
8
- disabled?: boolean;
9
- error?: boolean;
10
- label?: string;
11
- id?: string;
12
- onchange?: (event: Event) => void;
13
- }
14
-
15
- let {
16
- value = $bindable(''),
17
- options,
18
- placeholder,
19
- disabled = false,
20
- error = false,
21
- label,
22
- id = createId('select'),
23
- onchange
24
- }: Props = $props();
25
-
26
- const chevronIcon = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%23ADADAD' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E";
27
-
28
- const baseClasses = `glass-panel rounded-lg px-4 py-3 pr-10 font-body text-text transition-all duration-300 ease-luxe w-full appearance-none bg-no-repeat bg-[right_1rem_center] bg-[length:12px]`;
29
- const focusClasses = 'focus:outline-none focus:border-accent focus:accent-glow';
30
- const errorClasses = error ? 'border-error' : '';
31
- const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : '';
32
- </script>
33
-
34
- {#if label}
35
- <div>
36
- <label for={id} class="text-text-soft text-sm mb-2 block">
37
- {label}
38
- </label>
39
- <select
40
- {id}
41
- {disabled}
42
- bind:value
43
- onchange={onchange}
44
- class="{baseClasses} {focusClasses} {errorClasses} {disabledClasses}"
45
- style={`background-image: url('${chevronIcon}')`}
46
- >
47
- {#if placeholder}
48
- <option disabled value="">{placeholder}</option>
49
- {/if}
50
- {#each options as option}
51
- <option value={option.value}>{option.label}</option>
52
- {/each}
53
- </select>
54
- </div>
55
- {:else}
56
- <select
57
- {id}
58
- {disabled}
59
- bind:value
60
- onchange={onchange}
61
- class="{baseClasses} {focusClasses} {errorClasses} {disabledClasses}"
62
- style={`background-image: url('${chevronIcon}')`}
63
- >
64
- {#if placeholder}
65
- <option disabled value="">{placeholder}</option>
66
- {/if}
67
- {#each options as option}
68
- <option value={option.value}>{option.label}</option>
69
- {/each}
70
- </select>
71
- {/if}
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>
@@ -1,4 +1,4 @@
1
- interface Props {
1
+ export interface Props {
2
2
  value?: string;
3
3
  options: Array<{
4
4
  value: string;
@@ -10,6 +10,8 @@ interface Props {
10
10
  label?: string;
11
11
  id?: string;
12
12
  onchange?: (event: Event) => void;
13
+ iconBefore?: import('svelte').Snippet;
14
+ class?: string;
13
15
  }
14
16
  declare const Select: import("svelte").Component<Props, {}, "value">;
15
17
  type Select = ReturnType<typeof Select>;
@@ -1,56 +1,44 @@
1
- <script lang="ts">
2
- import { createId } from '../../lib/internal/id.js';
3
-
4
- interface Props {
5
- checked?: boolean;
6
- disabled?: boolean;
7
- label?: string;
8
- id?: string;
9
- onchange?: (event: Event) => void;
10
- }
11
-
12
- let {
13
- checked = $bindable(false),
14
- disabled = false,
15
- label,
16
- id = createId('switch'),
17
- onchange
18
- }: Props = $props();
19
- </script>
20
-
21
- <label for={id} class="flex items-center gap-2 cursor-pointer {disabled ? 'opacity-50 cursor-not-allowed' : ''}">
22
- <input
23
- type="checkbox"
24
- role="switch"
25
- aria-checked={checked}
26
- {id}
27
- {disabled}
28
- bind:checked
29
- onchange={onchange}
30
- class="w-11 h-6 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' : ''}"
31
- />
32
- {#if label}
33
- <span class="text-text text-sm select-none">
34
- {label}
35
- </span>
36
- {/if}
37
- </label>
38
-
39
- <style>
40
- input[type="checkbox"]::after {
41
- content: '';
42
- position: absolute;
43
- left: 0.25rem;
44
- top: 50%;
45
- transform: translateY(-50%);
46
- width: 1rem;
47
- height: 1rem;
48
- border-radius: var(--radius-pill);
49
- background: var(--color-text);
50
- transition: all 0.3s var(--ease-luxe);
51
- }
52
-
53
- input[type="checkbox"]:checked::after {
54
- transform: translate(1.25rem, -50%);
55
- }
56
- </style>
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>
@@ -1,9 +1,11 @@
1
- interface Props {
1
+ export interface Props {
2
2
  checked?: boolean;
3
3
  disabled?: boolean;
4
4
  label?: string;
5
5
  id?: string;
6
6
  onchange?: (event: Event) => void;
7
+ iconBefore?: import('svelte').Snippet;
8
+ class?: string;
7
9
  }
8
10
  declare const Switch: import("svelte").Component<Props, {}, "checked">;
9
11
  type Switch = ReturnType<typeof Switch>;
@@ -1,57 +1,52 @@
1
- <script lang="ts">
2
- import { createId } from '../../lib/internal/id.js';
3
-
4
- interface Props {
5
- value?: string;
6
- placeholder?: string;
7
- rows?: number;
8
- disabled?: boolean;
9
- error?: boolean;
10
- label?: string;
11
- id?: string;
12
- oninput?: (event: Event) => void;
13
- }
14
-
15
- let {
16
- value = $bindable(''),
17
- placeholder,
18
- rows = 4,
19
- disabled = false,
20
- error = false,
21
- label,
22
- id = createId('textarea'),
23
- oninput
24
- }: Props = $props();
25
-
26
- const baseClasses = 'glass-panel rounded-lg px-4 py-3 font-body text-text placeholder:text-text-muted transition-all duration-300 ease-luxe w-full resize-y min-h-[100px]';
27
- const focusClasses = 'focus:outline-none focus:border-accent focus:accent-glow';
28
- const errorClasses = error ? 'border-error' : '';
29
- const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : '';
30
- </script>
31
-
32
- {#if label}
33
- <div>
34
- <label for={id} class="text-text-soft text-sm mb-2 block">
35
- {label}
36
- </label>
37
- <textarea
38
- {id}
39
- {placeholder}
40
- {disabled}
41
- {rows}
42
- bind:value
43
- oninput={oninput}
44
- class="{baseClasses} {focusClasses} {errorClasses} {disabledClasses}"
45
- ></textarea>
46
- </div>
47
- {:else}
48
- <textarea
49
- {id}
50
- {placeholder}
51
- {disabled}
52
- {rows}
53
- bind:value
54
- oninput={oninput}
55
- class="{baseClasses} {focusClasses} {errorClasses} {disabledClasses}"
56
- ></textarea>
57
- {/if}
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}
@@ -1,4 +1,4 @@
1
- interface Props {
1
+ export interface Props {
2
2
  value?: string;
3
3
  placeholder?: string;
4
4
  rows?: number;
@@ -7,6 +7,8 @@ interface Props {
7
7
  label?: string;
8
8
  id?: string;
9
9
  oninput?: (event: Event) => void;
10
+ iconBefore?: import('svelte').Snippet;
11
+ class?: string;
10
12
  }
11
13
  declare const Textarea: import("svelte").Component<Props, {}, "value">;
12
14
  type Textarea = ReturnType<typeof Textarea>;