@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.
- package/README.md +929 -54
- package/dist/components/Accordion/Accordion.svelte +79 -0
- package/dist/components/Accordion/Accordion.svelte.d.ts +10 -0
- package/dist/components/Accordion/AccordionItem.svelte +198 -0
- package/dist/components/Accordion/AccordionItem.svelte.d.ts +10 -0
- package/dist/components/Accordion/index.d.ts +2 -0
- package/dist/components/Accordion/index.js +2 -0
- package/dist/components/Carousel/Carousel.svelte +454 -0
- package/dist/components/Carousel/Carousel.svelte.d.ts +14 -0
- package/dist/components/Carousel/CarouselSlide.svelte +22 -0
- package/dist/components/Carousel/CarouselSlide.svelte.d.ts +7 -0
- package/dist/components/Carousel/index.d.ts +2 -0
- package/dist/components/Carousel/index.js +2 -0
- package/dist/components/Chip/Chip.svelte +461 -0
- package/dist/components/Chip/Chip.svelte.d.ts +17 -0
- package/dist/components/Chip/ChipGroup.svelte +76 -0
- package/dist/components/Chip/ChipGroup.svelte.d.ts +9 -0
- package/dist/components/Chip/index.d.ts +2 -0
- package/dist/components/Chip/index.js +2 -0
- package/dist/components/DatePicker/DatePicker.svelte +746 -0
- package/dist/components/DatePicker/DatePicker.svelte.d.ts +19 -0
- package/dist/components/DatePicker/index.d.ts +1 -0
- package/dist/components/DatePicker/index.js +1 -0
- package/dist/components/FileUpload/FileUpload.svelte +484 -0
- package/dist/components/FileUpload/FileUpload.svelte.d.ts +16 -0
- package/dist/components/FileUpload/index.d.ts +1 -0
- package/dist/components/FileUpload/index.js +1 -0
- package/dist/components/Image/Image.svelte +223 -0
- package/dist/components/Image/Image.svelte.d.ts +19 -0
- package/dist/components/Image/index.d.ts +1 -0
- package/dist/components/Image/index.js +1 -0
- package/dist/components/NumberInput/NumberInput.svelte +293 -0
- package/dist/components/NumberInput/NumberInput.svelte.d.ts +16 -0
- package/dist/components/NumberInput/index.d.ts +1 -0
- package/dist/components/NumberInput/index.js +1 -0
- package/dist/components/OTPInput/OTPInput.svelte +312 -0
- package/dist/components/OTPInput/OTPInput.svelte.d.ts +57 -0
- package/dist/components/OTPInput/index.d.ts +1 -0
- package/dist/components/OTPInput/index.js +1 -0
- package/dist/components/Pagination/Pagination.svelte +243 -0
- package/dist/components/Pagination/Pagination.svelte.d.ts +10 -0
- package/dist/components/Pagination/index.d.ts +1 -0
- package/dist/components/Pagination/index.js +1 -0
- package/dist/components/Rating/Rating.svelte +316 -0
- package/dist/components/Rating/Rating.svelte.d.ts +16 -0
- package/dist/components/Rating/index.d.ts +1 -0
- package/dist/components/Rating/index.js +1 -0
- package/dist/components/SearchInput/SearchInput.svelte +480 -0
- package/dist/components/SearchInput/SearchInput.svelte.d.ts +22 -0
- package/dist/components/SearchInput/index.d.ts +1 -0
- package/dist/components/SearchInput/index.js +1 -0
- package/dist/components/Slider/Slider.svelte +324 -0
- package/dist/components/Slider/Slider.svelte.d.ts +14 -0
- package/dist/components/Slider/index.d.ts +1 -0
- package/dist/components/Slider/index.js +1 -0
- package/dist/components/Stepper/Stepper.svelte +100 -0
- package/dist/components/Stepper/Stepper.svelte.d.ts +11 -0
- package/dist/components/Stepper/StepperStep.svelte +391 -0
- package/dist/components/Stepper/StepperStep.svelte.d.ts +13 -0
- package/dist/components/Stepper/index.d.ts +2 -0
- package/dist/components/Stepper/index.js +2 -0
- package/dist/components/TimePicker/TimePicker.svelte +803 -0
- package/dist/components/TimePicker/TimePicker.svelte.d.ts +17 -0
- package/dist/components/TimePicker/index.d.ts +1 -0
- package/dist/components/TimePicker/index.js +1 -0
- package/dist/components/ToggleGroup/ToggleGroup.svelte +91 -0
- package/dist/components/ToggleGroup/ToggleGroup.svelte.d.ts +13 -0
- package/dist/components/ToggleGroup/ToggleGroupItem.svelte +158 -0
- package/dist/components/ToggleGroup/ToggleGroupItem.svelte.d.ts +9 -0
- package/dist/components/ToggleGroup/index.d.ts +3 -0
- package/dist/components/ToggleGroup/index.js +2 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +12 -0
- package/dist/types/data-display.d.ts +68 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/input.d.ts +82 -0
- package/dist/types/input.js +2 -0
- package/dist/types/navigation.d.ts +15 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,17 +46,24 @@ npm install @aspect-ops/exon-ui
|
|
|
46
46
|
|
|
47
47
|
### Form Components
|
|
48
48
|
|
|
49
|
-
| Component | Description
|
|
50
|
-
| --------------- |
|
|
51
|
-
| `TextInput` | Text input with validation states
|
|
52
|
-
| `Textarea` | Multi-line input with auto-resize
|
|
53
|
-
| `Select` | Dropdown select with keyboard navigation
|
|
54
|
-
| `Checkbox` | Single checkbox with indeterminate state
|
|
55
|
-
| `CheckboxGroup` | Group of checkboxes with shared state
|
|
56
|
-
| `Radio` | Single radio button
|
|
57
|
-
| `RadioGroup` | Radio button group with orientation
|
|
58
|
-
| `Switch` | Toggle switch component
|
|
59
|
-
| `FormField` | Label wrapper with helper/error text
|
|
49
|
+
| Component | Description |
|
|
50
|
+
| --------------- | ------------------------------------------ |
|
|
51
|
+
| `TextInput` | Text input with validation states |
|
|
52
|
+
| `Textarea` | Multi-line input with auto-resize |
|
|
53
|
+
| `Select` | Dropdown select with keyboard navigation |
|
|
54
|
+
| `Checkbox` | Single checkbox with indeterminate state |
|
|
55
|
+
| `CheckboxGroup` | Group of checkboxes with shared state |
|
|
56
|
+
| `Radio` | Single radio button |
|
|
57
|
+
| `RadioGroup` | Radio button group with orientation |
|
|
58
|
+
| `Switch` | Toggle switch component |
|
|
59
|
+
| `FormField` | Label wrapper with helper/error text |
|
|
60
|
+
| `SearchInput` | Search with autocomplete suggestions |
|
|
61
|
+
| `DatePicker` | Date selection with calendar popup |
|
|
62
|
+
| `TimePicker` | Time selection with hour/minute picker |
|
|
63
|
+
| `FileUpload` | Drag-drop file upload with previews |
|
|
64
|
+
| `OTPInput` | One-time password input |
|
|
65
|
+
| `NumberInput` | Number input with +/- buttons and keyboard |
|
|
66
|
+
| `ToggleGroup` | Single/multi select button group |
|
|
60
67
|
|
|
61
68
|
### Navigation Components
|
|
62
69
|
|
|
@@ -68,6 +75,31 @@ npm install @aspect-ops/exon-ui
|
|
|
68
75
|
| `BottomNav`, `BottomNavItem` | Mobile bottom navigation |
|
|
69
76
|
| `Navbar`, `NavItem` | Responsive header with mobile menu |
|
|
70
77
|
| `Sidebar`, `SidebarItem`, `SidebarGroup` | Collapsible sidebar navigation |
|
|
78
|
+
| `Stepper`, `StepperStep` | Multi-step progress indicator |
|
|
79
|
+
| `Pagination` | Page navigation with ellipsis |
|
|
80
|
+
|
|
81
|
+
### Data Display Components
|
|
82
|
+
|
|
83
|
+
| Component | Description |
|
|
84
|
+
| ---------------------------- | -------------------------------------------- |
|
|
85
|
+
| `Accordion`, `AccordionItem` | Collapsible content panels |
|
|
86
|
+
| `Slider` | Range slider with drag/keyboard support |
|
|
87
|
+
| `Carousel`, `CarouselSlide` | Image/content carousel with swipe |
|
|
88
|
+
| `Image` | Lazy loading image with placeholder/fallback |
|
|
89
|
+
| `Rating` | Star rating display/input |
|
|
90
|
+
| `Chip`, `ChipGroup` | Tag/filter chips with selection |
|
|
91
|
+
|
|
92
|
+
### Mobile Components
|
|
93
|
+
|
|
94
|
+
| Component | Description |
|
|
95
|
+
| ----------------------------------------------------- | ------------------------------------------- |
|
|
96
|
+
| `ActionSheet`, `ActionSheetItem` | iOS/Android-style bottom action menu |
|
|
97
|
+
| `BottomSheet`, `BottomSheetHeader`, `BottomSheetBody` | Draggable bottom sheet with snap points |
|
|
98
|
+
| `FAB` | Floating action button with positions |
|
|
99
|
+
| `FABGroup` | Speed dial FAB with expandable actions |
|
|
100
|
+
| `PullToRefresh` | Pull-to-refresh gesture for lists |
|
|
101
|
+
| `SwipeActions` | Swipe-to-reveal actions on list items |
|
|
102
|
+
| `SafeArea` | Safe area inset wrapper for notched devices |
|
|
71
103
|
|
|
72
104
|
## Usage Examples
|
|
73
105
|
|
|
@@ -320,73 +352,901 @@ npm install @aspect-ops/exon-ui
|
|
|
320
352
|
</Sidebar>
|
|
321
353
|
```
|
|
322
354
|
|
|
323
|
-
##
|
|
355
|
+
## Advanced Form Components
|
|
324
356
|
|
|
325
|
-
###
|
|
357
|
+
### SearchInput
|
|
326
358
|
|
|
327
|
-
|
|
359
|
+
Search input with autocomplete suggestions and keyboard navigation.
|
|
360
|
+
|
|
361
|
+
**Props:**
|
|
362
|
+
|
|
363
|
+
| Prop | Type | Default | Description |
|
|
364
|
+
| ---------------- | ----------- | ---------- | --------------------------------- |
|
|
365
|
+
| `value` | `string` | `''` | Bindable search value |
|
|
366
|
+
| `placeholder` | `string` | `'Search'` | Input placeholder text |
|
|
367
|
+
| `suggestions` | `string[]` | `[]` | Array of autocomplete suggestions |
|
|
368
|
+
| `size` | `InputSize` | `'md'` | Input size: sm, md, lg |
|
|
369
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
370
|
+
| `loading` | `boolean` | `false` | Show loading spinner |
|
|
371
|
+
| `maxSuggestions` | `number` | `5` | Maximum suggestions to display |
|
|
372
|
+
| `onselect` | `function` | - | Callback when suggestion selected |
|
|
373
|
+
|
|
374
|
+
**Usage:**
|
|
328
375
|
|
|
329
376
|
```svelte
|
|
330
377
|
<script>
|
|
331
|
-
import '@aspect-ops/exon-ui
|
|
378
|
+
import { SearchInput } from '@aspect-ops/exon-ui';
|
|
379
|
+
|
|
380
|
+
let query = $state('');
|
|
381
|
+
const suggestions = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry'];
|
|
332
382
|
</script>
|
|
333
383
|
|
|
334
|
-
<
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
384
|
+
<SearchInput bind:value={query} {suggestions} onselect={(val) => console.log('Selected:', val)} />
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### DatePicker
|
|
388
|
+
|
|
389
|
+
Date selection with calendar popup and keyboard navigation.
|
|
390
|
+
|
|
391
|
+
**Props:**
|
|
392
|
+
|
|
393
|
+
| Prop | Type | Default | Description |
|
|
394
|
+
| ------------- | ----------- | --------------- | ----------------------- |
|
|
395
|
+
| `value` | `Date` | `null` | Bindable selected date |
|
|
396
|
+
| `placeholder` | `string` | `'Select date'` | Input placeholder |
|
|
397
|
+
| `min` | `Date` | `null` | Minimum selectable date |
|
|
398
|
+
| `max` | `Date` | `null` | Maximum selectable date |
|
|
399
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
400
|
+
| `size` | `InputSize` | `'md'` | Input size: sm, md, lg |
|
|
401
|
+
| `format` | `string` | `'yyyy-MM-dd'` | Date format string |
|
|
402
|
+
|
|
403
|
+
**Usage:**
|
|
404
|
+
|
|
405
|
+
```svelte
|
|
406
|
+
<script>
|
|
407
|
+
import { DatePicker } from '@aspect-ops/exon-ui';
|
|
408
|
+
|
|
409
|
+
let selectedDate = $state(null);
|
|
410
|
+
</script>
|
|
411
|
+
|
|
412
|
+
<DatePicker bind:value={selectedDate} placeholder="Choose a date" />
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### TimePicker
|
|
416
|
+
|
|
417
|
+
Time selection with scrollable hour/minute/period picker.
|
|
418
|
+
|
|
419
|
+
**Props:**
|
|
420
|
+
|
|
421
|
+
| Prop | Type | Default | Description |
|
|
422
|
+
| ------------ | ---------------- | ------- | --------------------------- |
|
|
423
|
+
| `value` | `string` | `''` | Bindable time value (HH:mm) |
|
|
424
|
+
| `format` | `'12h' \| '24h'` | `'24h'` | Time format |
|
|
425
|
+
| `minuteStep` | `number` | `1` | Minute increment step |
|
|
426
|
+
| `min` | `string` | - | Minimum selectable time |
|
|
427
|
+
| `max` | `string` | - | Maximum selectable time |
|
|
428
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
429
|
+
| `size` | `InputSize` | `'md'` | Input size: sm, md, lg |
|
|
430
|
+
|
|
431
|
+
**Usage:**
|
|
432
|
+
|
|
433
|
+
```svelte
|
|
434
|
+
<script>
|
|
435
|
+
import { TimePicker } from '@aspect-ops/exon-ui';
|
|
436
|
+
|
|
437
|
+
let time = $state('');
|
|
438
|
+
</script>
|
|
439
|
+
|
|
440
|
+
<TimePicker bind:value={time} format="12h" minuteStep={15} />
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### FileUpload
|
|
444
|
+
|
|
445
|
+
Drag-and-drop file upload with image previews and validation.
|
|
446
|
+
|
|
447
|
+
**Props:**
|
|
448
|
+
|
|
449
|
+
| Prop | Type | Default | Description |
|
|
450
|
+
| ------------- | --------- | ------- | -------------------------------- |
|
|
451
|
+
| `files` | `File[]` | `[]` | Bindable array of uploaded files |
|
|
452
|
+
| `accept` | `string` | `''` | Accepted file types |
|
|
453
|
+
| `multiple` | `boolean` | `false` | Allow multiple file selection |
|
|
454
|
+
| `maxSize` | `number` | `10MB` | Maximum file size in bytes |
|
|
455
|
+
| `maxFiles` | `number` | `10` | Maximum number of files |
|
|
456
|
+
| `showPreview` | `boolean` | `true` | Show image previews |
|
|
457
|
+
|
|
458
|
+
**Usage:**
|
|
459
|
+
|
|
460
|
+
```svelte
|
|
461
|
+
<script>
|
|
462
|
+
import { FileUpload } from '@aspect-ops/exon-ui';
|
|
463
|
+
|
|
464
|
+
let files = $state([]);
|
|
465
|
+
</script>
|
|
466
|
+
|
|
467
|
+
<FileUpload bind:files multiple accept="image/*" maxSize={5242880} />
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### OTPInput
|
|
471
|
+
|
|
472
|
+
One-time password input with auto-focus and paste support.
|
|
473
|
+
|
|
474
|
+
**Props:**
|
|
475
|
+
|
|
476
|
+
| Prop | Type | Default | Description |
|
|
477
|
+
| ------------ | ----------------------------- | ----------- | -------------------------------- |
|
|
478
|
+
| `value` | `string` | `''` | Bindable OTP value |
|
|
479
|
+
| `length` | `number` | `6` | Number of OTP digits |
|
|
480
|
+
| `type` | `'numeric' \| 'alphanumeric'` | `'numeric'` | Input validation type |
|
|
481
|
+
| `masked` | `boolean` | `false` | Show dots instead of characters |
|
|
482
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
483
|
+
| `size` | `Size` | `'md'` | Input size: sm, md, lg |
|
|
484
|
+
| `oncomplete` | `function` | - | Callback when all digits entered |
|
|
485
|
+
|
|
486
|
+
**Usage:**
|
|
487
|
+
|
|
488
|
+
```svelte
|
|
489
|
+
<script>
|
|
490
|
+
import { OTPInput } from '@aspect-ops/exon-ui';
|
|
491
|
+
|
|
492
|
+
let otp = $state('');
|
|
493
|
+
</script>
|
|
494
|
+
|
|
495
|
+
<OTPInput bind:value={otp} length={6} oncomplete={(code) => console.log('OTP:', code)} />
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### NumberInput
|
|
499
|
+
|
|
500
|
+
Number input with increment/decrement buttons and keyboard support.
|
|
501
|
+
|
|
502
|
+
**Props:**
|
|
503
|
+
|
|
504
|
+
| Prop | Type | Default | Description |
|
|
505
|
+
| --------------- | ------------------------- | ------- | --------------------------- |
|
|
506
|
+
| `value` | `number \| null` | `null` | Bindable number value |
|
|
507
|
+
| `min` | `number` | - | Minimum allowed value |
|
|
508
|
+
| `max` | `number` | - | Maximum allowed value |
|
|
509
|
+
| `step` | `number` | `1` | Increment/decrement step |
|
|
510
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
511
|
+
| `placeholder` | `string` | - | Input placeholder |
|
|
512
|
+
| `error` | `boolean` | `false` | Error state styling |
|
|
513
|
+
| `onValueChange` | `(value: number) => void` | - | Callback when value changes |
|
|
514
|
+
|
|
515
|
+
**Usage:**
|
|
516
|
+
|
|
517
|
+
```svelte
|
|
518
|
+
<script>
|
|
519
|
+
import { NumberInput } from '@aspect-ops/exon-ui';
|
|
520
|
+
|
|
521
|
+
let quantity = $state(1);
|
|
522
|
+
</script>
|
|
523
|
+
|
|
524
|
+
<NumberInput bind:value={quantity} min={1} max={99} step={1} />
|
|
525
|
+
|
|
526
|
+
<!-- With error state -->
|
|
527
|
+
<NumberInput bind:value={quantity} min={0} max={10} error={quantity > 10} />
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### ToggleGroup
|
|
531
|
+
|
|
532
|
+
Button group for single or multiple selection.
|
|
533
|
+
|
|
534
|
+
**Props (ToggleGroup):**
|
|
535
|
+
|
|
536
|
+
| Prop | Type | Default | Description |
|
|
537
|
+
| --------------- | ---------------------------- | -------------- | --------------------------- |
|
|
538
|
+
| `type` | `'single' \| 'multiple'` | `'single'` | Selection mode |
|
|
539
|
+
| `value` | `string \| string[]` | `'' \| []` | Bindable selected value(s) |
|
|
540
|
+
| `onValueChange` | `(value) => void` | - | Callback when value changes |
|
|
541
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
542
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout direction |
|
|
543
|
+
|
|
544
|
+
**Props (ToggleGroupItem):**
|
|
545
|
+
|
|
546
|
+
| Prop | Type | Default | Description |
|
|
547
|
+
| ---------- | --------- | ------- | --------------------- |
|
|
548
|
+
| `value` | `string` | - | Item value (required) |
|
|
549
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
550
|
+
|
|
551
|
+
**Usage:**
|
|
552
|
+
|
|
553
|
+
```svelte
|
|
554
|
+
<script>
|
|
555
|
+
import { ToggleGroup, ToggleGroupItem } from '@aspect-ops/exon-ui';
|
|
556
|
+
|
|
557
|
+
let alignment = $state('left');
|
|
558
|
+
let formats = $state([]);
|
|
559
|
+
</script>
|
|
560
|
+
|
|
561
|
+
<!-- Single selection (radio-like) -->
|
|
562
|
+
<ToggleGroup bind:value={alignment} type="single">
|
|
563
|
+
{#snippet children()}
|
|
564
|
+
<ToggleGroupItem value="left">Left</ToggleGroupItem>
|
|
565
|
+
<ToggleGroupItem value="center">Center</ToggleGroupItem>
|
|
566
|
+
<ToggleGroupItem value="right">Right</ToggleGroupItem>
|
|
567
|
+
{/snippet}
|
|
568
|
+
</ToggleGroup>
|
|
569
|
+
|
|
570
|
+
<!-- Multiple selection (checkbox-like) -->
|
|
571
|
+
<ToggleGroup bind:value={formats} type="multiple">
|
|
572
|
+
{#snippet children()}
|
|
573
|
+
<ToggleGroupItem value="bold">B</ToggleGroupItem>
|
|
574
|
+
<ToggleGroupItem value="italic">I</ToggleGroupItem>
|
|
575
|
+
<ToggleGroupItem value="underline">U</ToggleGroupItem>
|
|
576
|
+
{/snippet}
|
|
577
|
+
</ToggleGroup>
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### Pagination
|
|
581
|
+
|
|
582
|
+
Page navigation with ellipsis for large page ranges.
|
|
583
|
+
|
|
584
|
+
**Props:**
|
|
585
|
+
|
|
586
|
+
| Prop | Type | Default | Description |
|
|
587
|
+
| -------------- | ------------------------ | ------- | --------------------------------- |
|
|
588
|
+
| `currentPage` | `number` | - | Current page number (required) |
|
|
589
|
+
| `totalPages` | `number` | - | Total number of pages (required) |
|
|
590
|
+
| `siblingCount` | `number` | `1` | Pages to show around current page |
|
|
591
|
+
| `onPageChange` | `(page: number) => void` | - | Callback when page changes |
|
|
592
|
+
|
|
593
|
+
**Usage:**
|
|
594
|
+
|
|
595
|
+
```svelte
|
|
596
|
+
<script>
|
|
597
|
+
import { Pagination } from '@aspect-ops/exon-ui';
|
|
598
|
+
|
|
599
|
+
let currentPage = $state(1);
|
|
600
|
+
const totalPages = 20;
|
|
601
|
+
</script>
|
|
602
|
+
|
|
603
|
+
<Pagination {currentPage} {totalPages} onPageChange={(page) => (currentPage = page)} />
|
|
604
|
+
|
|
605
|
+
<!-- With more visible siblings -->
|
|
606
|
+
<Pagination
|
|
607
|
+
{currentPage}
|
|
608
|
+
{totalPages}
|
|
609
|
+
siblingCount={2}
|
|
610
|
+
onPageChange={(page) => (currentPage = page)}
|
|
611
|
+
/>
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
**Keyboard Navigation:**
|
|
615
|
+
|
|
616
|
+
- `←` / `→`: Previous / Next page
|
|
617
|
+
- `Home`: First page
|
|
618
|
+
- `End`: Last page
|
|
619
|
+
|
|
620
|
+
## Data Display Components
|
|
621
|
+
|
|
622
|
+
### Accordion
|
|
623
|
+
|
|
624
|
+
Collapsible content panels with single or multiple expansion.
|
|
625
|
+
|
|
626
|
+
**Props:**
|
|
627
|
+
|
|
628
|
+
| Prop | Type | Default | Description |
|
|
629
|
+
| ---------- | -------------------- | ------- | ------------------------------- |
|
|
630
|
+
| `value` | `string \| string[]` | `[]` | Bindable expanded item value(s) |
|
|
631
|
+
| `multiple` | `boolean` | `false` | Allow multiple panels open |
|
|
632
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
633
|
+
|
|
634
|
+
**Usage:**
|
|
635
|
+
|
|
636
|
+
```svelte
|
|
637
|
+
<script>
|
|
638
|
+
import { Accordion, AccordionItem } from '@aspect-ops/exon-ui';
|
|
639
|
+
|
|
640
|
+
let expanded = $state([]);
|
|
641
|
+
</script>
|
|
642
|
+
|
|
643
|
+
<Accordion bind:value={expanded} multiple>
|
|
644
|
+
{#snippet children()}
|
|
645
|
+
<AccordionItem value="item1" title="Section 1">Content for section 1</AccordionItem>
|
|
646
|
+
<AccordionItem value="item2" title="Section 2">Content for section 2</AccordionItem>
|
|
647
|
+
{/snippet}
|
|
648
|
+
</Accordion>
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
### Slider
|
|
652
|
+
|
|
653
|
+
Range slider with drag, keyboard support, and optional value display.
|
|
654
|
+
|
|
655
|
+
**Props:**
|
|
656
|
+
|
|
657
|
+
| Prop | Type | Default | Description |
|
|
658
|
+
| ----------- | --------- | ------- | --------------------- |
|
|
659
|
+
| `value` | `number` | `0` | Bindable slider value |
|
|
660
|
+
| `min` | `number` | `0` | Minimum value |
|
|
661
|
+
| `max` | `number` | `100` | Maximum value |
|
|
662
|
+
| `step` | `number` | `1` | Value increment step |
|
|
663
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
664
|
+
| `showValue` | `boolean` | `false` | Show value tooltip |
|
|
665
|
+
| `showTicks` | `boolean` | `false` | Show tick marks |
|
|
666
|
+
|
|
667
|
+
**Usage:**
|
|
668
|
+
|
|
669
|
+
```svelte
|
|
670
|
+
<script>
|
|
671
|
+
import { Slider } from '@aspect-ops/exon-ui';
|
|
672
|
+
|
|
673
|
+
let volume = $state(50);
|
|
674
|
+
</script>
|
|
675
|
+
|
|
676
|
+
<Slider bind:value={volume} min={0} max={100} showValue />
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
### Carousel
|
|
680
|
+
|
|
681
|
+
Image/content carousel with swipe gestures and autoplay.
|
|
682
|
+
|
|
683
|
+
**Props:**
|
|
684
|
+
|
|
685
|
+
| Prop | Type | Default | Description |
|
|
686
|
+
| ------------------ | --------- | ------- | --------------------------- |
|
|
687
|
+
| `activeIndex` | `number` | `0` | Bindable active slide index |
|
|
688
|
+
| `autoplay` | `boolean` | `false` | Enable autoplay |
|
|
689
|
+
| `autoplayInterval` | `number` | `5000` | Autoplay interval (ms) |
|
|
690
|
+
| `loop` | `boolean` | `true` | Loop slides |
|
|
691
|
+
| `showIndicators` | `boolean` | `true` | Show dot indicators |
|
|
692
|
+
| `showArrows` | `boolean` | `true` | Show navigation arrows |
|
|
693
|
+
|
|
694
|
+
**Usage:**
|
|
695
|
+
|
|
696
|
+
```svelte
|
|
697
|
+
<script>
|
|
698
|
+
import { Carousel, CarouselSlide } from '@aspect-ops/exon-ui';
|
|
699
|
+
|
|
700
|
+
let activeSlide = $state(0);
|
|
701
|
+
</script>
|
|
702
|
+
|
|
703
|
+
<Carousel bind:activeIndex={activeSlide} autoplay loop>
|
|
704
|
+
{#snippet children()}
|
|
705
|
+
<CarouselSlide><img src="slide1.jpg" alt="Slide 1" /></CarouselSlide>
|
|
706
|
+
<CarouselSlide><img src="slide2.jpg" alt="Slide 2" /></CarouselSlide>
|
|
707
|
+
<CarouselSlide><img src="slide3.jpg" alt="Slide 3" /></CarouselSlide>
|
|
708
|
+
{/snippet}
|
|
709
|
+
</Carousel>
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### Image
|
|
713
|
+
|
|
714
|
+
Lazy-loading image with placeholder and fallback support.
|
|
715
|
+
|
|
716
|
+
**Props:**
|
|
717
|
+
|
|
718
|
+
| Prop | Type | Default | Description |
|
|
719
|
+
| ------------- | ------------------- | --------- | ------------------------------- |
|
|
720
|
+
| `src` | `string` | - | Image source URL (required) |
|
|
721
|
+
| `alt` | `string` | - | Alt text (required) |
|
|
722
|
+
| `loading` | `'lazy' \| 'eager'` | `'lazy'` | Loading strategy |
|
|
723
|
+
| `objectFit` | `string` | `'cover'` | CSS object-fit value |
|
|
724
|
+
| `placeholder` | `string` | - | Placeholder image or color |
|
|
725
|
+
| `fallback` | `string` | - | Fallback image on error |
|
|
726
|
+
| `rounded` | `boolean \| string` | `false` | Border radius: sm, md, lg, full |
|
|
727
|
+
| `aspectRatio` | `string` | - | CSS aspect ratio |
|
|
728
|
+
|
|
729
|
+
**Usage:**
|
|
730
|
+
|
|
731
|
+
```svelte
|
|
732
|
+
<script>
|
|
733
|
+
import { Image } from '@aspect-ops/exon-ui';
|
|
734
|
+
</script>
|
|
735
|
+
|
|
736
|
+
<Image
|
|
737
|
+
src="profile.jpg"
|
|
738
|
+
alt="User profile"
|
|
739
|
+
rounded="full"
|
|
740
|
+
aspectRatio="1/1"
|
|
741
|
+
placeholder="#e5e7eb"
|
|
742
|
+
fallback="default-avatar.png"
|
|
743
|
+
/>
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
### Rating
|
|
747
|
+
|
|
748
|
+
Star rating display and input with half-star support.
|
|
749
|
+
|
|
750
|
+
**Props:**
|
|
751
|
+
|
|
752
|
+
| Prop | Type | Default | Description |
|
|
753
|
+
| ----------- | --------- | ------- | ----------------------- |
|
|
754
|
+
| `value` | `number` | `0` | Bindable rating value |
|
|
755
|
+
| `max` | `number` | `5` | Maximum rating |
|
|
756
|
+
| `allowHalf` | `boolean` | `false` | Allow half-star ratings |
|
|
757
|
+
| `readonly` | `boolean` | `false` | Read-only display mode |
|
|
758
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
759
|
+
| `size` | `Size` | `'md'` | Star size: sm, md, lg |
|
|
760
|
+
| `showValue` | `boolean` | `false` | Show numeric value |
|
|
761
|
+
|
|
762
|
+
**Usage:**
|
|
763
|
+
|
|
764
|
+
```svelte
|
|
765
|
+
<script>
|
|
766
|
+
import { Rating } from '@aspect-ops/exon-ui';
|
|
767
|
+
|
|
768
|
+
let rating = $state(4.5);
|
|
769
|
+
</script>
|
|
770
|
+
|
|
771
|
+
<Rating bind:value={rating} allowHalf showValue />
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
## Navigation & Feedback Components
|
|
775
|
+
|
|
776
|
+
### Stepper
|
|
777
|
+
|
|
778
|
+
Multi-step progress indicator with linear or non-linear navigation.
|
|
779
|
+
|
|
780
|
+
**Props:**
|
|
781
|
+
|
|
782
|
+
| Prop | Type | Default | Description |
|
|
783
|
+
| ------------- | ---------------------------- | -------------- | ----------------------------- |
|
|
784
|
+
| `activeStep` | `number` | `0` | Bindable active step index |
|
|
785
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout orientation |
|
|
786
|
+
| `linear` | `boolean` | `true` | Enforce sequential navigation |
|
|
787
|
+
|
|
788
|
+
**Usage:**
|
|
789
|
+
|
|
790
|
+
```svelte
|
|
791
|
+
<script>
|
|
792
|
+
import { Stepper, StepperStep } from '@aspect-ops/exon-ui';
|
|
793
|
+
|
|
794
|
+
let currentStep = $state(0);
|
|
795
|
+
</script>
|
|
796
|
+
|
|
797
|
+
<Stepper bind:activeStep={currentStep}>
|
|
798
|
+
{#snippet children()}
|
|
799
|
+
<StepperStep label="Personal Info" description="Enter your details" />
|
|
800
|
+
<StepperStep label="Address" description="Shipping information" />
|
|
801
|
+
<StepperStep label="Review" description="Confirm your order" />
|
|
802
|
+
{/snippet}
|
|
803
|
+
</Stepper>
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
### Chip
|
|
807
|
+
|
|
808
|
+
Compact tag/filter chips with selection and removal.
|
|
809
|
+
|
|
810
|
+
**Props:**
|
|
811
|
+
|
|
812
|
+
| Prop | Type | Default | Description |
|
|
813
|
+
| ----------- | ------------- | ----------- | ------------------------------------------------ |
|
|
814
|
+
| `variant` | `ChipVariant` | `'filled'` | Chip style: filled, outlined, soft |
|
|
815
|
+
| `color` | `ChipColor` | `'default'` | Color: default, primary, success, warning, error |
|
|
816
|
+
| `size` | `ChipSize` | `'md'` | Size: sm, md, lg |
|
|
817
|
+
| `removable` | `boolean` | `false` | Show remove button |
|
|
818
|
+
| `selected` | `boolean` | `false` | Selected state |
|
|
819
|
+
| `disabled` | `boolean` | `false` | Disabled state |
|
|
820
|
+
|
|
821
|
+
**Usage:**
|
|
822
|
+
|
|
823
|
+
```svelte
|
|
824
|
+
<script>
|
|
825
|
+
import { Chip, ChipGroup } from '@aspect-ops/exon-ui';
|
|
826
|
+
|
|
827
|
+
let selected = $state(['tag1']);
|
|
828
|
+
</script>
|
|
829
|
+
|
|
830
|
+
<ChipGroup>
|
|
831
|
+
<Chip onclick={() => console.log('clicked')}>JavaScript</Chip>
|
|
832
|
+
<Chip color="primary" removable onremove={() => console.log('removed')}>Svelte</Chip>
|
|
833
|
+
<Chip variant="outlined" color="success">TypeScript</Chip>
|
|
834
|
+
</ChipGroup>
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
## Mobile Components
|
|
838
|
+
|
|
839
|
+
Components optimized for Capacitor mobile apps with gesture support, haptic feedback, and safe area handling.
|
|
840
|
+
|
|
841
|
+
| Component | Description |
|
|
842
|
+
| --------------- | ------------------------------------------- |
|
|
843
|
+
| `ActionSheet` | iOS/Android-style bottom action menu |
|
|
844
|
+
| `BottomSheet` | Draggable bottom sheet with snap points |
|
|
845
|
+
| `FAB` | Floating action button with positions |
|
|
846
|
+
| `FABGroup` | Speed dial FAB with expandable actions |
|
|
847
|
+
| `PullToRefresh` | Pull-to-refresh gesture for lists |
|
|
848
|
+
| `SwipeActions` | Swipe-to-reveal actions on list items |
|
|
849
|
+
| `SafeArea` | Safe area inset wrapper for notched devices |
|
|
850
|
+
|
|
851
|
+
### ActionSheet
|
|
852
|
+
|
|
853
|
+
```svelte
|
|
854
|
+
<script>
|
|
855
|
+
import { ActionSheet, ActionSheetItem, Button } from '@aspect-ops/exon-ui';
|
|
856
|
+
|
|
857
|
+
let open = $state(false);
|
|
858
|
+
</script>
|
|
859
|
+
|
|
860
|
+
<Button onclick={() => (open = true)}>Show Actions</Button>
|
|
861
|
+
|
|
862
|
+
<ActionSheet bind:open title="Choose an action" description="Select what you want to do">
|
|
863
|
+
{#snippet actions()}
|
|
864
|
+
<ActionSheetItem onclick={() => console.log('Edit')}>Edit</ActionSheetItem>
|
|
865
|
+
<ActionSheetItem onclick={() => console.log('Share')}>Share</ActionSheetItem>
|
|
866
|
+
<ActionSheetItem destructive onclick={() => console.log('Delete')}>Delete</ActionSheetItem>
|
|
867
|
+
{/snippet}
|
|
868
|
+
</ActionSheet>
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
**Props:**
|
|
872
|
+
|
|
873
|
+
- `open` - Bindable open state
|
|
874
|
+
- `title` - Optional header title
|
|
875
|
+
- `description` - Optional header description
|
|
876
|
+
- `cancelLabel` - Cancel button text (default: "Cancel")
|
|
877
|
+
- `showCancel` - Show cancel button (default: true)
|
|
878
|
+
- `closeOnSelect` - Close when action selected (default: true)
|
|
879
|
+
|
|
880
|
+
### BottomSheet
|
|
881
|
+
|
|
882
|
+
```svelte
|
|
883
|
+
<script>
|
|
884
|
+
import { BottomSheet, BottomSheetHeader, BottomSheetBody, Button } from '@aspect-ops/exon-ui';
|
|
885
|
+
|
|
886
|
+
let open = $state(false);
|
|
887
|
+
</script>
|
|
888
|
+
|
|
889
|
+
<Button onclick={() => (open = true)}>Open Sheet</Button>
|
|
890
|
+
|
|
891
|
+
<BottomSheet bind:open snapPoints={['half', 'full']} defaultSnapPoint="half">
|
|
892
|
+
<BottomSheetHeader>
|
|
893
|
+
<h3>Sheet Title</h3>
|
|
894
|
+
</BottomSheetHeader>
|
|
895
|
+
<BottomSheetBody>
|
|
896
|
+
<p>Drag the handle to resize or swipe down to close.</p>
|
|
897
|
+
</BottomSheetBody>
|
|
898
|
+
</BottomSheet>
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
**Props:**
|
|
902
|
+
|
|
903
|
+
- `open` - Bindable open state
|
|
904
|
+
- `snapPoints` - Array of snap points: `'min'` | `'half'` | `'full'` | number (px)
|
|
905
|
+
- `defaultSnapPoint` - Initial snap point (default: 'half')
|
|
906
|
+
- `showHandle` - Show drag handle (default: true)
|
|
907
|
+
- `closeOnBackdrop` - Close on backdrop click (default: true)
|
|
908
|
+
- `closeOnEscape` - Close on Escape key (default: true)
|
|
909
|
+
|
|
910
|
+
### FAB (Floating Action Button)
|
|
911
|
+
|
|
912
|
+
```svelte
|
|
913
|
+
<script>
|
|
914
|
+
import { FAB } from '@aspect-ops/exon-ui';
|
|
915
|
+
</script>
|
|
916
|
+
|
|
917
|
+
<!-- Basic FAB -->
|
|
918
|
+
<FAB onclick={() => console.log('clicked')}>+</FAB>
|
|
919
|
+
|
|
920
|
+
<!-- Positioned FAB -->
|
|
921
|
+
<FAB position="bottom-left" size="lg">📝</FAB>
|
|
922
|
+
|
|
923
|
+
<!-- Extended FAB with label -->
|
|
924
|
+
<FAB extended position="bottom-right">
|
|
925
|
+
{#snippet children()}➕{/snippet}
|
|
926
|
+
{#snippet label()}Add Item{/snippet}
|
|
927
|
+
</FAB>
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
**Props:**
|
|
931
|
+
|
|
932
|
+
- `size` - `'sm'` | `'md'` | `'lg'` (44px, 56px, 72px)
|
|
933
|
+
- `position` - `'bottom-right'` | `'bottom-left'` | `'bottom-center'` | `'top-right'` | `'top-left'`
|
|
934
|
+
- `extended` - Extended FAB with label
|
|
935
|
+
- `disabled` - Disable the button
|
|
936
|
+
|
|
937
|
+
### FABGroup (Speed Dial)
|
|
938
|
+
|
|
939
|
+
```svelte
|
|
940
|
+
<script>
|
|
941
|
+
import { FABGroup } from '@aspect-ops/exon-ui';
|
|
942
|
+
|
|
943
|
+
const actions = [
|
|
944
|
+
{ icon: '📷', label: 'Camera', onAction: () => console.log('Camera') },
|
|
945
|
+
{ icon: '🖼️', label: 'Gallery', onAction: () => console.log('Gallery') },
|
|
946
|
+
{ icon: '📎', label: 'Attach', onAction: () => console.log('Attach') }
|
|
947
|
+
];
|
|
948
|
+
</script>
|
|
949
|
+
|
|
950
|
+
<FABGroup {actions} icon="+" closeIcon="×" position="bottom-right" direction="up" />
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
**Props:**
|
|
954
|
+
|
|
955
|
+
- `actions` - Array of `{ icon, label, onAction }` objects
|
|
956
|
+
- `icon` - Main FAB icon (default: '+')
|
|
957
|
+
- `closeIcon` - Icon when open (default: '×')
|
|
958
|
+
- `position` - Same as FAB positions
|
|
959
|
+
- `direction` - `'up'` | `'down'` | `'left'` | `'right'`
|
|
960
|
+
|
|
961
|
+
### PullToRefresh
|
|
962
|
+
|
|
963
|
+
```svelte
|
|
964
|
+
<script>
|
|
965
|
+
import { PullToRefresh } from '@aspect-ops/exon-ui';
|
|
966
|
+
|
|
967
|
+
let refreshing = $state(false);
|
|
968
|
+
|
|
969
|
+
async function handleRefresh() {
|
|
970
|
+
// Fetch new data
|
|
971
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
972
|
+
refreshing = false;
|
|
359
973
|
}
|
|
360
|
-
</
|
|
974
|
+
</script>
|
|
975
|
+
|
|
976
|
+
<PullToRefresh bind:refreshing onrefresh={handleRefresh}>
|
|
977
|
+
<div class="content">
|
|
978
|
+
<p>Pull down to refresh...</p>
|
|
979
|
+
<!-- Your scrollable content -->
|
|
980
|
+
</div>
|
|
981
|
+
</PullToRefresh>
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
**Props:**
|
|
985
|
+
|
|
986
|
+
- `refreshing` - Bindable loading state
|
|
987
|
+
- `threshold` - Pull distance to trigger (default: 80px)
|
|
988
|
+
- `maxPull` - Maximum pull distance (default: 150px)
|
|
989
|
+
- `disabled` - Disable pull-to-refresh
|
|
990
|
+
- `onrefresh` - Callback when threshold reached
|
|
991
|
+
|
|
992
|
+
### SwipeActions
|
|
993
|
+
|
|
994
|
+
```svelte
|
|
995
|
+
<script>
|
|
996
|
+
import { SwipeActions } from '@aspect-ops/exon-ui';
|
|
997
|
+
|
|
998
|
+
const leftActions = [
|
|
999
|
+
{ icon: '📌', label: 'Pin', color: '#3b82f6', onAction: () => console.log('Pin') }
|
|
1000
|
+
];
|
|
1001
|
+
|
|
1002
|
+
const rightActions = [
|
|
1003
|
+
{ icon: '🗑️', label: 'Delete', color: '#ef4444', onAction: () => console.log('Delete') }
|
|
1004
|
+
];
|
|
1005
|
+
</script>
|
|
1006
|
+
|
|
1007
|
+
<SwipeActions {leftActions} {rightActions}>
|
|
1008
|
+
<div class="list-item">
|
|
1009
|
+
<p>Swipe me left or right</p>
|
|
1010
|
+
</div>
|
|
1011
|
+
</SwipeActions>
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
**Props:**
|
|
1015
|
+
|
|
1016
|
+
- `leftActions` - Actions revealed on swipe right
|
|
1017
|
+
- `rightActions` - Actions revealed on swipe left
|
|
1018
|
+
- `threshold` - Swipe distance to reveal (default: 60px)
|
|
1019
|
+
- `disabled` - Disable swipe gestures
|
|
1020
|
+
|
|
1021
|
+
**Action object:**
|
|
1022
|
+
|
|
1023
|
+
- `icon` - Icon string or emoji
|
|
1024
|
+
- `label` - Action label text
|
|
1025
|
+
- `color` - Background color
|
|
1026
|
+
- `onAction` - Callback when tapped
|
|
1027
|
+
|
|
1028
|
+
### SafeArea
|
|
1029
|
+
|
|
1030
|
+
```svelte
|
|
1031
|
+
<script>
|
|
1032
|
+
import { SafeArea } from '@aspect-ops/exon-ui';
|
|
1033
|
+
</script>
|
|
1034
|
+
|
|
1035
|
+
<!-- All edges (default) -->
|
|
1036
|
+
<SafeArea>
|
|
1037
|
+
<main>Content with safe area padding on all sides</main>
|
|
1038
|
+
</SafeArea>
|
|
1039
|
+
|
|
1040
|
+
<!-- Specific edges -->
|
|
1041
|
+
<SafeArea edges={['top', 'bottom']}>
|
|
1042
|
+
<main>Only top and bottom safe area</main>
|
|
1043
|
+
</SafeArea>
|
|
1044
|
+
```
|
|
1045
|
+
|
|
1046
|
+
**Props:**
|
|
1047
|
+
|
|
1048
|
+
- `edges` - Array of edges: `'top'` | `'right'` | `'bottom'` | `'left'` (default: all)
|
|
1049
|
+
|
|
1050
|
+
## Theming & Customization
|
|
1051
|
+
|
|
1052
|
+
### Quick Setup
|
|
1053
|
+
|
|
1054
|
+
```svelte
|
|
1055
|
+
<script>
|
|
1056
|
+
// In your root layout (+layout.svelte)
|
|
1057
|
+
import '@aspect-ops/exon-ui/styles';
|
|
1058
|
+
</script>
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
### Custom Brand Colors
|
|
1062
|
+
|
|
1063
|
+
Override CSS variables in your app's global styles:
|
|
1064
|
+
|
|
1065
|
+
```css
|
|
1066
|
+
/* src/app.css or global styles */
|
|
1067
|
+
:root {
|
|
1068
|
+
/* Primary brand color */
|
|
1069
|
+
--color-primary: #8b5cf6; /* Purple */
|
|
1070
|
+
--color-primary-hover: #7c3aed;
|
|
1071
|
+
--color-primary-active: #6d28d9;
|
|
1072
|
+
--color-primary-bg: #ede9fe;
|
|
1073
|
+
|
|
1074
|
+
/* Secondary */
|
|
1075
|
+
--color-secondary: #64748b;
|
|
1076
|
+
--color-secondary-hover: #475569;
|
|
1077
|
+
|
|
1078
|
+
/* Destructive/Error */
|
|
1079
|
+
--color-destructive: #dc2626;
|
|
1080
|
+
--color-error: #dc2626;
|
|
1081
|
+
|
|
1082
|
+
/* Success */
|
|
1083
|
+
--color-success: #16a34a;
|
|
1084
|
+
|
|
1085
|
+
/* Warning */
|
|
1086
|
+
--color-warning: #d97706;
|
|
1087
|
+
}
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
### Full Design Token Reference
|
|
1091
|
+
|
|
1092
|
+
```css
|
|
1093
|
+
:root {
|
|
1094
|
+
/* ===== COLORS ===== */
|
|
1095
|
+
|
|
1096
|
+
/* Backgrounds */
|
|
1097
|
+
--color-bg: #fcfcfc; /* Main background */
|
|
1098
|
+
--color-bg-muted: #f9f9f9; /* Subtle background */
|
|
1099
|
+
--color-bg-subtle: #f0f0f0; /* More contrast */
|
|
1100
|
+
--color-bg-hover: #e8e8e8; /* Hover state */
|
|
1101
|
+
--color-bg-active: #e0e0e0; /* Active/pressed */
|
|
1102
|
+
--color-bg-elevated: #ffffff; /* Cards, modals */
|
|
1103
|
+
--color-bg-card: #ffffff; /* Card backgrounds */
|
|
1104
|
+
|
|
1105
|
+
/* Text */
|
|
1106
|
+
--color-text: #202020; /* Primary text */
|
|
1107
|
+
--color-text-muted: #646464; /* Secondary text */
|
|
1108
|
+
--color-text-subtle: #838383; /* Tertiary text */
|
|
1109
|
+
--color-text-inverse: #ffffff; /* Text on dark backgrounds */
|
|
1110
|
+
|
|
1111
|
+
/* Borders */
|
|
1112
|
+
--color-border: #d9d9d9;
|
|
1113
|
+
--color-border-hover: #cecece;
|
|
1114
|
+
--color-border-active: #bbbbbb;
|
|
1115
|
+
|
|
1116
|
+
/* ===== TYPOGRAPHY ===== */
|
|
1117
|
+
|
|
1118
|
+
--font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
|
1119
|
+
--font-family-mono: ui-monospace, 'SF Mono', Menlo, Monaco, monospace;
|
|
1120
|
+
|
|
1121
|
+
/* Fluid type scale (responsive) */
|
|
1122
|
+
--text-xs: clamp(0.69rem, 0.66rem + 0.14vw, 0.78rem); /* ~11-12px */
|
|
1123
|
+
--text-sm: clamp(0.83rem, 0.79rem + 0.21vw, 0.97rem); /* ~13-16px */
|
|
1124
|
+
--text-base: clamp(1rem, 0.93rem + 0.33vw, 1.125rem); /* 16-18px */
|
|
1125
|
+
--text-lg: clamp(1.13rem, 1.03rem + 0.47vw, 1.41rem); /* ~18-23px */
|
|
1126
|
+
--text-xl: clamp(1.27rem, 1.14rem + 0.65vw, 1.76rem); /* ~20-28px */
|
|
1127
|
+
--text-2xl: clamp(1.42rem, 1.25rem + 0.89vw, 2.2rem); /* ~23-35px */
|
|
1128
|
+
--text-3xl: clamp(1.6rem, 1.37rem + 1.19vw, 2.75rem); /* ~26-44px */
|
|
1129
|
+
--text-4xl: clamp(1.8rem, 1.49rem + 1.57vw, 3.43rem); /* ~29-55px */
|
|
1130
|
+
|
|
1131
|
+
/* Font weights */
|
|
1132
|
+
--font-normal: 400;
|
|
1133
|
+
--font-medium: 500;
|
|
1134
|
+
--font-semibold: 600;
|
|
1135
|
+
--font-bold: 700;
|
|
1136
|
+
|
|
1137
|
+
/* ===== SPACING ===== */
|
|
1138
|
+
|
|
1139
|
+
--space-xs: clamp(0.25rem, 0.23rem + 0.11vw, 0.31rem); /* 4-5px */
|
|
1140
|
+
--space-sm: clamp(0.5rem, 0.46rem + 0.22vw, 0.63rem); /* 8-10px */
|
|
1141
|
+
--space-md: clamp(1rem, 0.93rem + 0.33vw, 1.125rem); /* 16-18px */
|
|
1142
|
+
--space-lg: clamp(1.5rem, 1.39rem + 0.54vw, 1.75rem); /* 24-28px */
|
|
1143
|
+
--space-xl: clamp(2rem, 1.85rem + 0.76vw, 2.5rem); /* 32-40px */
|
|
1144
|
+
--space-2xl: clamp(3rem, 2.78rem + 1.09vw, 3.75rem); /* 48-60px */
|
|
1145
|
+
|
|
1146
|
+
/* ===== BORDER RADIUS ===== */
|
|
1147
|
+
|
|
1148
|
+
--radius-sm: 0.25rem; /* 4px */
|
|
1149
|
+
--radius-md: 0.375rem; /* 6px */
|
|
1150
|
+
--radius-lg: 0.5rem; /* 8px */
|
|
1151
|
+
--radius-xl: 0.75rem; /* 12px */
|
|
1152
|
+
--radius-2xl: 1rem; /* 16px */
|
|
1153
|
+
--radius-full: 9999px; /* Pill/circle */
|
|
1154
|
+
|
|
1155
|
+
/* ===== SHADOWS ===== */
|
|
1156
|
+
|
|
1157
|
+
--shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1);
|
|
1158
|
+
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
|
|
1159
|
+
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
|
|
1160
|
+
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1);
|
|
1161
|
+
|
|
1162
|
+
/* ===== TRANSITIONS ===== */
|
|
1163
|
+
|
|
1164
|
+
--transition-fast: 150ms ease;
|
|
1165
|
+
--transition-base: 200ms ease;
|
|
1166
|
+
--transition-slow: 300ms ease;
|
|
1167
|
+
|
|
1168
|
+
/* ===== MOBILE ===== */
|
|
1169
|
+
|
|
1170
|
+
--touch-target-min: 44px;
|
|
1171
|
+
--safe-area-top: env(safe-area-inset-top, 0px);
|
|
1172
|
+
--safe-area-bottom: env(safe-area-inset-bottom, 0px);
|
|
1173
|
+
}
|
|
361
1174
|
```
|
|
362
1175
|
|
|
363
1176
|
### Dark Mode
|
|
364
1177
|
|
|
365
|
-
|
|
1178
|
+
The library supports dark mode via `data-theme` attribute or system preference:
|
|
366
1179
|
|
|
367
1180
|
```svelte
|
|
368
1181
|
<script>
|
|
1182
|
+
let isDark = $state(false);
|
|
1183
|
+
|
|
369
1184
|
function toggleTheme() {
|
|
370
|
-
|
|
371
|
-
document.documentElement.setAttribute('data-theme', isDark ? '
|
|
1185
|
+
isDark = !isDark;
|
|
1186
|
+
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
|
|
372
1187
|
}
|
|
1188
|
+
|
|
1189
|
+
// Respect system preference on load
|
|
1190
|
+
import { onMount } from 'svelte';
|
|
1191
|
+
onMount(() => {
|
|
1192
|
+
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
1193
|
+
isDark = true;
|
|
1194
|
+
document.documentElement.setAttribute('data-theme', 'dark');
|
|
1195
|
+
}
|
|
1196
|
+
});
|
|
373
1197
|
</script>
|
|
374
1198
|
|
|
375
|
-
<
|
|
1199
|
+
<Switch bind:checked={isDark} onchange={toggleTheme}>
|
|
1200
|
+
{#snippet children()}Dark Mode{/snippet}
|
|
1201
|
+
</Switch>
|
|
376
1202
|
```
|
|
377
1203
|
|
|
378
|
-
Dark theme
|
|
1204
|
+
Dark theme automatically adjusts all colors:
|
|
379
1205
|
|
|
380
1206
|
```css
|
|
381
1207
|
[data-theme='dark'] {
|
|
382
|
-
--color-bg: #
|
|
383
|
-
--color-bg-muted: #
|
|
384
|
-
--color-bg-elevated: #
|
|
385
|
-
--color-text: #
|
|
386
|
-
--color-
|
|
1208
|
+
--color-bg: #111111;
|
|
1209
|
+
--color-bg-muted: #191919;
|
|
1210
|
+
--color-bg-elevated: #222222;
|
|
1211
|
+
--color-text: #eeeeee;
|
|
1212
|
+
--color-text-muted: #b4b4b4;
|
|
1213
|
+
--color-border: #3a3a3a;
|
|
1214
|
+
/* All other colors auto-adapt */
|
|
387
1215
|
}
|
|
388
1216
|
```
|
|
389
1217
|
|
|
1218
|
+
### Component-Level Customization
|
|
1219
|
+
|
|
1220
|
+
Override styles for specific components using CSS classes:
|
|
1221
|
+
|
|
1222
|
+
```svelte
|
|
1223
|
+
<Button class="my-custom-button" variant="primary">Custom Button</Button>
|
|
1224
|
+
|
|
1225
|
+
<style>
|
|
1226
|
+
:global(.my-custom-button) {
|
|
1227
|
+
--color-primary: #10b981;
|
|
1228
|
+
border-radius: var(--radius-full);
|
|
1229
|
+
text-transform: uppercase;
|
|
1230
|
+
}
|
|
1231
|
+
</style>
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
### Platform-Specific Styling
|
|
1235
|
+
|
|
1236
|
+
Components adapt to iOS/Android automatically. Set platform manually:
|
|
1237
|
+
|
|
1238
|
+
```svelte
|
|
1239
|
+
<script>
|
|
1240
|
+
import { onMount } from 'svelte';
|
|
1241
|
+
|
|
1242
|
+
onMount(() => {
|
|
1243
|
+
// Auto-detect or set manually
|
|
1244
|
+
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
1245
|
+
document.documentElement.setAttribute('data-platform', isIOS ? 'ios' : 'android');
|
|
1246
|
+
});
|
|
1247
|
+
</script>
|
|
1248
|
+
```
|
|
1249
|
+
|
|
390
1250
|
## Mobile / Capacitor Support
|
|
391
1251
|
|
|
392
1252
|
Components are designed with mobile-first principles:
|
|
@@ -395,12 +1255,27 @@ Components are designed with mobile-first principles:
|
|
|
395
1255
|
- **Safe area inset handling** for notched devices (iPhone, Android gesture bar)
|
|
396
1256
|
- **Responsive breakpoints** for adaptive layouts
|
|
397
1257
|
- **Hardware-accelerated animations** for smooth 60fps performance
|
|
1258
|
+
- **Haptic feedback** integration (requires Capacitor Haptics plugin)
|
|
1259
|
+
- **Platform-adaptive styling** for iOS and Android
|
|
1260
|
+
|
|
1261
|
+
### Capacitor Integration
|
|
398
1262
|
|
|
399
|
-
|
|
1263
|
+
```bash
|
|
1264
|
+
# Install Capacitor plugins for full mobile support
|
|
1265
|
+
npm install @capacitor/haptics @capacitor/status-bar
|
|
1266
|
+
```
|
|
400
1267
|
|
|
401
1268
|
```svelte
|
|
402
|
-
<
|
|
403
|
-
|
|
1269
|
+
<script>
|
|
1270
|
+
import { SafeArea, BottomNav, PullToRefresh } from '@aspect-ops/exon-ui';
|
|
1271
|
+
</script>
|
|
1272
|
+
|
|
1273
|
+
<SafeArea edges={['top', 'bottom']}>
|
|
1274
|
+
<PullToRefresh onrefresh={handleRefresh}>
|
|
1275
|
+
<main>Your content here</main>
|
|
1276
|
+
</PullToRefresh>
|
|
1277
|
+
<BottomNav {items} />
|
|
1278
|
+
</SafeArea>
|
|
404
1279
|
```
|
|
405
1280
|
|
|
406
1281
|
## TypeScript
|