@aspect-ops/exon-ui 0.0.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +929 -54
  2. package/dist/components/Accordion/Accordion.svelte +79 -0
  3. package/dist/components/Accordion/Accordion.svelte.d.ts +10 -0
  4. package/dist/components/Accordion/AccordionItem.svelte +198 -0
  5. package/dist/components/Accordion/AccordionItem.svelte.d.ts +10 -0
  6. package/dist/components/Accordion/index.d.ts +2 -0
  7. package/dist/components/Accordion/index.js +2 -0
  8. package/dist/components/Carousel/Carousel.svelte +454 -0
  9. package/dist/components/Carousel/Carousel.svelte.d.ts +14 -0
  10. package/dist/components/Carousel/CarouselSlide.svelte +22 -0
  11. package/dist/components/Carousel/CarouselSlide.svelte.d.ts +7 -0
  12. package/dist/components/Carousel/index.d.ts +2 -0
  13. package/dist/components/Carousel/index.js +2 -0
  14. package/dist/components/Chip/Chip.svelte +461 -0
  15. package/dist/components/Chip/Chip.svelte.d.ts +17 -0
  16. package/dist/components/Chip/ChipGroup.svelte +76 -0
  17. package/dist/components/Chip/ChipGroup.svelte.d.ts +9 -0
  18. package/dist/components/Chip/index.d.ts +2 -0
  19. package/dist/components/Chip/index.js +2 -0
  20. package/dist/components/DatePicker/DatePicker.svelte +746 -0
  21. package/dist/components/DatePicker/DatePicker.svelte.d.ts +19 -0
  22. package/dist/components/DatePicker/index.d.ts +1 -0
  23. package/dist/components/DatePicker/index.js +1 -0
  24. package/dist/components/FileUpload/FileUpload.svelte +484 -0
  25. package/dist/components/FileUpload/FileUpload.svelte.d.ts +16 -0
  26. package/dist/components/FileUpload/index.d.ts +1 -0
  27. package/dist/components/FileUpload/index.js +1 -0
  28. package/dist/components/Image/Image.svelte +223 -0
  29. package/dist/components/Image/Image.svelte.d.ts +19 -0
  30. package/dist/components/Image/index.d.ts +1 -0
  31. package/dist/components/Image/index.js +1 -0
  32. package/dist/components/NumberInput/NumberInput.svelte +293 -0
  33. package/dist/components/NumberInput/NumberInput.svelte.d.ts +16 -0
  34. package/dist/components/NumberInput/index.d.ts +1 -0
  35. package/dist/components/NumberInput/index.js +1 -0
  36. package/dist/components/OTPInput/OTPInput.svelte +312 -0
  37. package/dist/components/OTPInput/OTPInput.svelte.d.ts +57 -0
  38. package/dist/components/OTPInput/index.d.ts +1 -0
  39. package/dist/components/OTPInput/index.js +1 -0
  40. package/dist/components/Pagination/Pagination.svelte +243 -0
  41. package/dist/components/Pagination/Pagination.svelte.d.ts +10 -0
  42. package/dist/components/Pagination/index.d.ts +1 -0
  43. package/dist/components/Pagination/index.js +1 -0
  44. package/dist/components/Rating/Rating.svelte +316 -0
  45. package/dist/components/Rating/Rating.svelte.d.ts +16 -0
  46. package/dist/components/Rating/index.d.ts +1 -0
  47. package/dist/components/Rating/index.js +1 -0
  48. package/dist/components/SearchInput/SearchInput.svelte +480 -0
  49. package/dist/components/SearchInput/SearchInput.svelte.d.ts +22 -0
  50. package/dist/components/SearchInput/index.d.ts +1 -0
  51. package/dist/components/SearchInput/index.js +1 -0
  52. package/dist/components/Slider/Slider.svelte +324 -0
  53. package/dist/components/Slider/Slider.svelte.d.ts +14 -0
  54. package/dist/components/Slider/index.d.ts +1 -0
  55. package/dist/components/Slider/index.js +1 -0
  56. package/dist/components/Stepper/Stepper.svelte +100 -0
  57. package/dist/components/Stepper/Stepper.svelte.d.ts +11 -0
  58. package/dist/components/Stepper/StepperStep.svelte +391 -0
  59. package/dist/components/Stepper/StepperStep.svelte.d.ts +13 -0
  60. package/dist/components/Stepper/index.d.ts +2 -0
  61. package/dist/components/Stepper/index.js +2 -0
  62. package/dist/components/TimePicker/TimePicker.svelte +803 -0
  63. package/dist/components/TimePicker/TimePicker.svelte.d.ts +17 -0
  64. package/dist/components/TimePicker/index.d.ts +1 -0
  65. package/dist/components/TimePicker/index.js +1 -0
  66. package/dist/components/ToggleGroup/ToggleGroup.svelte +91 -0
  67. package/dist/components/ToggleGroup/ToggleGroup.svelte.d.ts +13 -0
  68. package/dist/components/ToggleGroup/ToggleGroupItem.svelte +158 -0
  69. package/dist/components/ToggleGroup/ToggleGroupItem.svelte.d.ts +9 -0
  70. package/dist/components/ToggleGroup/index.d.ts +3 -0
  71. package/dist/components/ToggleGroup/index.js +2 -0
  72. package/dist/index.d.ts +13 -1
  73. package/dist/index.js +12 -0
  74. package/dist/types/data-display.d.ts +68 -0
  75. package/dist/types/index.d.ts +3 -2
  76. package/dist/types/input.d.ts +82 -0
  77. package/dist/types/input.js +2 -0
  78. package/dist/types/navigation.d.ts +15 -0
  79. package/package.json +1 -1
@@ -0,0 +1,223 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ src: string;
4
+ alt: string;
5
+ width?: number | string;
6
+ height?: number | string;
7
+ loading?: 'lazy' | 'eager';
8
+ objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
9
+ objectPosition?: string;
10
+ placeholder?: string;
11
+ fallback?: string;
12
+ rounded?: boolean | 'sm' | 'md' | 'lg' | 'full';
13
+ aspectRatio?: string;
14
+ class?: string;
15
+ onload?: () => void;
16
+ onerror?: () => void;
17
+ }
18
+
19
+ let {
20
+ src,
21
+ alt,
22
+ width,
23
+ height,
24
+ loading = 'lazy',
25
+ objectFit = 'cover',
26
+ objectPosition = 'center',
27
+ placeholder,
28
+ fallback,
29
+ rounded = false,
30
+ aspectRatio,
31
+ class: className = '',
32
+ onload,
33
+ onerror
34
+ }: Props = $props();
35
+
36
+ let status = $state<'loading' | 'loaded' | 'error'>('loading');
37
+
38
+ const currentSrc = $derived(status === 'error' && fallback ? fallback : src);
39
+
40
+ const roundedClass = $derived(() => {
41
+ if (rounded === false) return '';
42
+ if (rounded === true) return 'image-container--rounded-md';
43
+ return `image-container--rounded-${rounded}`;
44
+ });
45
+
46
+ const containerStyle = $derived(() => {
47
+ const styles: string[] = [];
48
+ if (aspectRatio) {
49
+ styles.push(`aspect-ratio: ${aspectRatio}`);
50
+ }
51
+ if (width) {
52
+ styles.push(`width: ${typeof width === 'number' ? `${width}px` : width}`);
53
+ }
54
+ if (height && !aspectRatio) {
55
+ styles.push(`height: ${typeof height === 'number' ? `${height}px` : height}`);
56
+ }
57
+ return styles.join('; ');
58
+ });
59
+
60
+ const imageStyle = $derived(() => {
61
+ const styles: string[] = [];
62
+ styles.push(`object-fit: ${objectFit}`);
63
+ styles.push(`object-position: ${objectPosition}`);
64
+ return styles.join('; ');
65
+ });
66
+
67
+ function handleLoad() {
68
+ status = 'loaded';
69
+ onload?.();
70
+ }
71
+
72
+ function handleError() {
73
+ status = 'error';
74
+ onerror?.();
75
+ }
76
+
77
+ // Reset status when src changes
78
+ $effect(() => {
79
+ src;
80
+ status = 'loading';
81
+ });
82
+ </script>
83
+
84
+ <div
85
+ class="image-container {roundedClass()} {className}"
86
+ style={containerStyle()}
87
+ role="img"
88
+ aria-label={alt}
89
+ >
90
+ {#if status === 'loading' && placeholder}
91
+ <div
92
+ class="image-placeholder"
93
+ style={typeof placeholder === 'string' && placeholder.startsWith('#')
94
+ ? `background-color: ${placeholder}`
95
+ : `background-image: url(${placeholder})`}
96
+ ></div>
97
+ {/if}
98
+
99
+ <img
100
+ class="image image--{status}"
101
+ src={currentSrc}
102
+ {alt}
103
+ {loading}
104
+ style={imageStyle()}
105
+ onload={handleLoad}
106
+ onerror={handleError}
107
+ />
108
+
109
+ {#if status === 'error' && !fallback}
110
+ <div class="image-error">
111
+ <svg
112
+ class="image-error__icon"
113
+ viewBox="0 0 24 24"
114
+ fill="none"
115
+ xmlns="http://www.w3.org/2000/svg"
116
+ aria-hidden="true"
117
+ >
118
+ <path
119
+ d="M21 19V5C21 3.9 20.1 3 19 3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19ZM8.5 13.5L11 16.51L14.5 12L19 18H5L8.5 13.5Z"
120
+ fill="currentColor"
121
+ />
122
+ <line
123
+ x1="4"
124
+ y1="4"
125
+ x2="20"
126
+ y2="20"
127
+ stroke="currentColor"
128
+ stroke-width="2"
129
+ stroke-linecap="round"
130
+ />
131
+ </svg>
132
+ <span class="image-error__text">{alt}</span>
133
+ </div>
134
+ {/if}
135
+ </div>
136
+
137
+ <style>
138
+ .image-container {
139
+ position: relative;
140
+ overflow: hidden;
141
+ display: inline-block;
142
+ background: var(--color-bg-muted, #f3f4f6);
143
+ }
144
+
145
+ .image {
146
+ width: 100%;
147
+ height: 100%;
148
+ display: block;
149
+ transition: opacity var(--transition-base, 0.2s ease);
150
+ }
151
+
152
+ .image--loading {
153
+ opacity: 0;
154
+ }
155
+
156
+ .image--loaded {
157
+ opacity: 1;
158
+ }
159
+
160
+ .image--error {
161
+ opacity: 0;
162
+ }
163
+
164
+ /* Rounded variants */
165
+ .image-container--rounded-sm {
166
+ border-radius: var(--radius-sm, 0.25rem);
167
+ }
168
+
169
+ .image-container--rounded-md {
170
+ border-radius: var(--radius-md, 0.375rem);
171
+ }
172
+
173
+ .image-container--rounded-lg {
174
+ border-radius: var(--radius-lg, 0.5rem);
175
+ }
176
+
177
+ .image-container--rounded-full {
178
+ border-radius: var(--radius-full, 9999px);
179
+ }
180
+
181
+ /* Placeholder */
182
+ .image-placeholder {
183
+ position: absolute;
184
+ inset: 0;
185
+ background-size: cover;
186
+ background-position: center;
187
+ background-repeat: no-repeat;
188
+ z-index: 1;
189
+ }
190
+
191
+ /* Error state */
192
+ .image-error {
193
+ position: absolute;
194
+ inset: 0;
195
+ display: flex;
196
+ flex-direction: column;
197
+ align-items: center;
198
+ justify-content: center;
199
+ background: var(--color-bg-muted, #f3f4f6);
200
+ color: var(--color-text-muted, #6b7280);
201
+ padding: 1rem;
202
+ text-align: center;
203
+ z-index: 2;
204
+ }
205
+
206
+ .image-error__icon {
207
+ width: 3rem;
208
+ height: 3rem;
209
+ margin-bottom: 0.5rem;
210
+ opacity: 0.5;
211
+ }
212
+
213
+ .image-error__text {
214
+ font-size: 0.875rem;
215
+ max-width: 100%;
216
+ overflow: hidden;
217
+ text-overflow: ellipsis;
218
+ display: -webkit-box;
219
+ line-clamp: 2;
220
+ -webkit-line-clamp: 2;
221
+ -webkit-box-orient: vertical;
222
+ }
223
+ </style>
@@ -0,0 +1,19 @@
1
+ interface Props {
2
+ src: string;
3
+ alt: string;
4
+ width?: number | string;
5
+ height?: number | string;
6
+ loading?: 'lazy' | 'eager';
7
+ objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
8
+ objectPosition?: string;
9
+ placeholder?: string;
10
+ fallback?: string;
11
+ rounded?: boolean | 'sm' | 'md' | 'lg' | 'full';
12
+ aspectRatio?: string;
13
+ class?: string;
14
+ onload?: () => void;
15
+ onerror?: () => void;
16
+ }
17
+ declare const Image: import("svelte").Component<Props, {}, "">;
18
+ type Image = ReturnType<typeof Image>;
19
+ export default Image;
@@ -0,0 +1 @@
1
+ export { default as Image } from './Image.svelte';
@@ -0,0 +1 @@
1
+ export { default as Image } from './Image.svelte';
@@ -0,0 +1,293 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ value?: number | null;
4
+ min?: number;
5
+ max?: number;
6
+ step?: number;
7
+ disabled?: boolean;
8
+ placeholder?: string;
9
+ name?: string;
10
+ id?: string;
11
+ error?: boolean;
12
+ class?: string;
13
+ onValueChange?: (value: number | null) => void;
14
+ }
15
+
16
+ let {
17
+ value = $bindable(null),
18
+ min,
19
+ max,
20
+ step = 1,
21
+ disabled = false,
22
+ placeholder,
23
+ name,
24
+ id,
25
+ error = false,
26
+ class: className = '',
27
+ onValueChange
28
+ }: Props = $props();
29
+
30
+ let inputValue = $state(value?.toString() ?? '');
31
+
32
+ // Sync internal state with external value prop
33
+ $effect(() => {
34
+ inputValue = value?.toString() ?? '';
35
+ });
36
+
37
+ const isDecrementDisabled = $derived(
38
+ disabled || (min !== undefined && value !== null && value <= min)
39
+ );
40
+ const isIncrementDisabled = $derived(
41
+ disabled || (max !== undefined && value !== null && value >= max)
42
+ );
43
+
44
+ function handleIncrement() {
45
+ if (isIncrementDisabled) return;
46
+
47
+ const currentVal = value ?? 0;
48
+ const newValue = currentVal + step;
49
+ const clampedValue = max !== undefined ? Math.min(newValue, max) : newValue;
50
+
51
+ value = clampedValue;
52
+ onValueChange?.(clampedValue);
53
+ }
54
+
55
+ function handleDecrement() {
56
+ if (isDecrementDisabled) return;
57
+
58
+ const currentVal = value ?? 0;
59
+ const newValue = currentVal - step;
60
+ const clampedValue = min !== undefined ? Math.max(newValue, min) : newValue;
61
+
62
+ value = clampedValue;
63
+ onValueChange?.(clampedValue);
64
+ }
65
+
66
+ function handleInput(e: Event & { currentTarget: HTMLInputElement }) {
67
+ inputValue = e.currentTarget.value;
68
+
69
+ if (inputValue === '') {
70
+ value = null;
71
+ onValueChange?.(null);
72
+ return;
73
+ }
74
+
75
+ const parsed = parseFloat(inputValue);
76
+ if (!isNaN(parsed)) {
77
+ value = parsed;
78
+ onValueChange?.(parsed);
79
+ }
80
+ }
81
+
82
+ function handleBlur() {
83
+ if (value === null) {
84
+ inputValue = '';
85
+ return;
86
+ }
87
+
88
+ // Clamp value to min/max on blur
89
+ let clampedValue = value;
90
+ if (min !== undefined && clampedValue < min) {
91
+ clampedValue = min;
92
+ }
93
+ if (max !== undefined && clampedValue > max) {
94
+ clampedValue = max;
95
+ }
96
+
97
+ if (clampedValue !== value) {
98
+ value = clampedValue;
99
+ onValueChange?.(clampedValue);
100
+ }
101
+
102
+ inputValue = clampedValue.toString();
103
+ }
104
+
105
+ function handleKeyDown(e: KeyboardEvent) {
106
+ if (e.key === 'ArrowUp') {
107
+ e.preventDefault();
108
+ handleIncrement();
109
+ } else if (e.key === 'ArrowDown') {
110
+ e.preventDefault();
111
+ handleDecrement();
112
+ }
113
+ }
114
+ </script>
115
+
116
+ <div
117
+ class="number-input {error ? 'number-input--error' : ''} {disabled
118
+ ? 'number-input--disabled'
119
+ : ''} {className}"
120
+ >
121
+ <button
122
+ type="button"
123
+ class="number-input__button number-input__button--decrement"
124
+ disabled={isDecrementDisabled}
125
+ onclick={handleDecrement}
126
+ aria-label="Decrement value"
127
+ >
128
+ <svg
129
+ xmlns="http://www.w3.org/2000/svg"
130
+ width="16"
131
+ height="16"
132
+ viewBox="0 0 24 24"
133
+ fill="none"
134
+ stroke="currentColor"
135
+ stroke-width="2"
136
+ stroke-linecap="round"
137
+ stroke-linejoin="round"
138
+ >
139
+ <line x1="5" y1="12" x2="19" y2="12"></line>
140
+ </svg>
141
+ </button>
142
+
143
+ <input
144
+ type="text"
145
+ inputmode="decimal"
146
+ class="number-input__field"
147
+ role="spinbutton"
148
+ aria-valuemin={min}
149
+ aria-valuemax={max}
150
+ aria-valuenow={value ?? undefined}
151
+ {name}
152
+ {id}
153
+ {placeholder}
154
+ {disabled}
155
+ aria-invalid={error}
156
+ value={inputValue}
157
+ oninput={handleInput}
158
+ onblur={handleBlur}
159
+ onkeydown={handleKeyDown}
160
+ />
161
+
162
+ <button
163
+ type="button"
164
+ class="number-input__button number-input__button--increment"
165
+ disabled={isIncrementDisabled}
166
+ onclick={handleIncrement}
167
+ aria-label="Increment value"
168
+ >
169
+ <svg
170
+ xmlns="http://www.w3.org/2000/svg"
171
+ width="16"
172
+ height="16"
173
+ viewBox="0 0 24 24"
174
+ fill="none"
175
+ stroke="currentColor"
176
+ stroke-width="2"
177
+ stroke-linecap="round"
178
+ stroke-linejoin="round"
179
+ >
180
+ <line x1="12" y1="5" x2="12" y2="19"></line>
181
+ <line x1="5" y1="12" x2="19" y2="12"></line>
182
+ </svg>
183
+ </button>
184
+ </div>
185
+
186
+ <style>
187
+ .number-input {
188
+ display: flex;
189
+ align-items: stretch;
190
+ width: 100%;
191
+ gap: 0;
192
+ }
193
+
194
+ .number-input--disabled {
195
+ opacity: 0.5;
196
+ }
197
+
198
+ .number-input__button {
199
+ display: flex;
200
+ align-items: center;
201
+ justify-content: center;
202
+ min-width: var(--touch-target-min, 44px);
203
+ min-height: var(--touch-target-min, 44px);
204
+ padding: 0;
205
+ border: 1px solid var(--color-border, #e5e7eb);
206
+ background: var(--color-bg, #ffffff);
207
+ color: var(--color-text, #1f2937);
208
+ font-family: inherit;
209
+ cursor: pointer;
210
+ transition: all var(--transition-fast, 150ms ease);
211
+ -webkit-tap-highlight-color: transparent;
212
+ }
213
+
214
+ .number-input__button:hover:not(:disabled) {
215
+ background: var(--color-bg-muted, #f3f4f6);
216
+ border-color: var(--color-border-hover, #d1d5db);
217
+ }
218
+
219
+ .number-input__button:active:not(:disabled) {
220
+ background: var(--color-border, #e5e7eb);
221
+ }
222
+
223
+ .number-input__button:focus-visible {
224
+ outline: 2px solid var(--color-primary, #3b82f6);
225
+ outline-offset: 2px;
226
+ z-index: 1;
227
+ }
228
+
229
+ .number-input__button:disabled {
230
+ opacity: 0.3;
231
+ cursor: not-allowed;
232
+ pointer-events: none;
233
+ }
234
+
235
+ .number-input__button--decrement {
236
+ border-top-left-radius: var(--radius-md, 0.375rem);
237
+ border-bottom-left-radius: var(--radius-md, 0.375rem);
238
+ border-right: none;
239
+ }
240
+
241
+ .number-input__button--increment {
242
+ border-top-right-radius: var(--radius-md, 0.375rem);
243
+ border-bottom-right-radius: var(--radius-md, 0.375rem);
244
+ border-left: none;
245
+ }
246
+
247
+ .number-input__field {
248
+ flex: 1;
249
+ min-width: 0;
250
+ min-height: var(--touch-target-min, 44px);
251
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
252
+ border: 1px solid var(--color-border, #e5e7eb);
253
+ border-left: none;
254
+ border-right: none;
255
+ background: var(--color-bg, #ffffff);
256
+ color: var(--color-text, #1f2937);
257
+ font-family: inherit;
258
+ font-size: var(--text-sm, 0.875rem);
259
+ line-height: 1.5;
260
+ text-align: center;
261
+ transition:
262
+ border-color var(--transition-fast, 150ms ease),
263
+ box-shadow var(--transition-fast, 150ms ease);
264
+ -webkit-tap-highlight-color: transparent;
265
+ }
266
+
267
+ .number-input__field::placeholder {
268
+ color: var(--color-text-muted, #9ca3af);
269
+ }
270
+
271
+ .number-input__field:focus {
272
+ outline: none;
273
+ border-color: var(--color-primary, #3b82f6);
274
+ box-shadow: 0 0 0 3px var(--color-primary-alpha, rgba(59, 130, 246, 0.1));
275
+ z-index: 1;
276
+ }
277
+
278
+ .number-input__field:disabled {
279
+ background: var(--color-bg-muted, #f3f4f6);
280
+ cursor: not-allowed;
281
+ }
282
+
283
+ /* Error state */
284
+ .number-input--error .number-input__field,
285
+ .number-input--error .number-input__button {
286
+ border-color: var(--color-error, #ef4444);
287
+ }
288
+
289
+ .number-input--error .number-input__field:focus {
290
+ border-color: var(--color-error, #ef4444);
291
+ box-shadow: 0 0 0 3px var(--color-error-alpha, rgba(239, 68, 68, 0.1));
292
+ }
293
+ </style>
@@ -0,0 +1,16 @@
1
+ interface Props {
2
+ value?: number | null;
3
+ min?: number;
4
+ max?: number;
5
+ step?: number;
6
+ disabled?: boolean;
7
+ placeholder?: string;
8
+ name?: string;
9
+ id?: string;
10
+ error?: boolean;
11
+ class?: string;
12
+ onValueChange?: (value: number | null) => void;
13
+ }
14
+ declare const NumberInput: import("svelte").Component<Props, {}, "value">;
15
+ type NumberInput = ReturnType<typeof NumberInput>;
16
+ export default NumberInput;
@@ -0,0 +1 @@
1
+ export { default as NumberInput } from './NumberInput.svelte';
@@ -0,0 +1 @@
1
+ export { default as NumberInput } from './NumberInput.svelte';