@aspect-ops/exon-ui 0.0.1 → 0.0.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.
- package/README.md +438 -0
- package/dist/components/ActionSheet/ActionSheet.svelte +270 -0
- package/dist/components/ActionSheet/ActionSheet.svelte.d.ts +12 -0
- package/dist/components/ActionSheet/ActionSheetItem.svelte +151 -0
- package/dist/components/ActionSheet/ActionSheetItem.svelte.d.ts +10 -0
- package/dist/components/ActionSheet/index.d.ts +3 -0
- package/dist/components/ActionSheet/index.js +2 -0
- package/dist/components/Alert/Alert.svelte +165 -0
- package/dist/components/Alert/Alert.svelte.d.ts +11 -0
- package/dist/components/Alert/index.d.ts +1 -0
- package/dist/components/Alert/index.js +1 -0
- package/dist/components/AspectRatio/AspectRatio.svelte +42 -0
- package/dist/components/AspectRatio/AspectRatio.svelte.d.ts +9 -0
- package/dist/components/AspectRatio/index.d.ts +1 -0
- package/dist/components/AspectRatio/index.js +1 -0
- package/dist/components/Avatar/Avatar.svelte +147 -0
- package/dist/components/Avatar/Avatar.svelte.d.ts +12 -0
- package/dist/components/Avatar/AvatarGroup.svelte +153 -0
- package/dist/components/Avatar/AvatarGroup.svelte.d.ts +12 -0
- package/dist/components/Avatar/index.d.ts +2 -0
- package/dist/components/Avatar/index.js +2 -0
- package/dist/components/BottomSheet/BottomSheet.svelte +230 -0
- package/dist/components/BottomSheet/BottomSheet.svelte.d.ts +7 -0
- package/dist/components/BottomSheet/BottomSheetBody.svelte +20 -0
- package/dist/components/BottomSheet/BottomSheetBody.svelte.d.ts +7 -0
- package/dist/components/BottomSheet/BottomSheetHeader.svelte +27 -0
- package/dist/components/BottomSheet/BottomSheetHeader.svelte.d.ts +7 -0
- package/dist/components/BottomSheet/index.d.ts +3 -0
- package/dist/components/BottomSheet/index.js +3 -0
- package/dist/components/Box/Box.svelte +41 -0
- package/dist/components/Box/Box.svelte.d.ts +7 -0
- package/dist/components/Box/index.d.ts +1 -0
- package/dist/components/Box/index.js +1 -0
- package/dist/components/Card/Card.svelte +95 -0
- package/dist/components/Card/Card.svelte.d.ts +10 -0
- package/dist/components/Card/CardBody.svelte +32 -0
- package/dist/components/Card/CardBody.svelte.d.ts +7 -0
- package/dist/components/Card/CardFooter.svelte +34 -0
- package/dist/components/Card/CardFooter.svelte.d.ts +7 -0
- package/dist/components/Card/CardHeader.svelte +67 -0
- package/dist/components/Card/CardHeader.svelte.d.ts +9 -0
- package/dist/components/Card/index.d.ts +4 -0
- package/dist/components/Card/index.js +4 -0
- package/dist/components/Center/Center.svelte +28 -0
- package/dist/components/Center/Center.svelte.d.ts +8 -0
- package/dist/components/Center/index.d.ts +1 -0
- package/dist/components/Center/index.js +1 -0
- package/dist/components/Container/Container.svelte +58 -0
- package/dist/components/Container/Container.svelte.d.ts +10 -0
- package/dist/components/Container/index.d.ts +1 -0
- package/dist/components/Container/index.js +1 -0
- package/dist/components/Divider/Divider.svelte +38 -0
- package/dist/components/Divider/Divider.svelte.d.ts +9 -0
- package/dist/components/Divider/index.d.ts +1 -0
- package/dist/components/Divider/index.js +1 -0
- package/dist/components/EmptyState/EmptyState.svelte +164 -0
- package/dist/components/EmptyState/EmptyState.svelte.d.ts +12 -0
- package/dist/components/EmptyState/index.d.ts +1 -0
- package/dist/components/EmptyState/index.js +1 -0
- package/dist/components/FAB/FAB.svelte +242 -0
- package/dist/components/FAB/FAB.svelte.d.ts +9 -0
- package/dist/components/FAB/FABGroup.svelte +449 -0
- package/dist/components/FAB/FABGroup.svelte.d.ts +9 -0
- package/dist/components/FAB/index.d.ts +3 -0
- package/dist/components/FAB/index.js +2 -0
- package/dist/components/Grid/Grid.svelte +136 -0
- package/dist/components/Grid/Grid.svelte.d.ts +12 -0
- package/dist/components/Grid/GridItem.svelte +21 -0
- package/dist/components/Grid/GridItem.svelte.d.ts +7 -0
- package/dist/components/Grid/index.d.ts +2 -0
- package/dist/components/Grid/index.js +2 -0
- package/dist/components/List/List.svelte +42 -0
- package/dist/components/List/List.svelte.d.ts +18 -0
- package/dist/components/List/ListItem.svelte +139 -0
- package/dist/components/List/ListItem.svelte.d.ts +36 -0
- package/dist/components/List/index.d.ts +2 -0
- package/dist/components/List/index.js +2 -0
- package/dist/components/Modal/Modal.svelte +204 -0
- package/dist/components/Modal/Modal.svelte.d.ts +7 -0
- package/dist/components/Modal/ModalBody.svelte +50 -0
- package/dist/components/Modal/ModalBody.svelte.d.ts +7 -0
- package/dist/components/Modal/ModalFooter.svelte +37 -0
- package/dist/components/Modal/ModalFooter.svelte.d.ts +7 -0
- package/dist/components/Modal/ModalHeader.svelte +73 -0
- package/dist/components/Modal/ModalHeader.svelte.d.ts +7 -0
- package/dist/components/Modal/index.d.ts +4 -0
- package/dist/components/Modal/index.js +4 -0
- package/dist/components/Popover/Popover.svelte +14 -0
- package/dist/components/Popover/Popover.svelte.d.ts +7 -0
- package/dist/components/Popover/PopoverContent.svelte +63 -0
- package/dist/components/Popover/PopoverContent.svelte.d.ts +7 -0
- package/dist/components/Popover/PopoverTrigger.svelte +14 -0
- package/dist/components/Popover/PopoverTrigger.svelte.d.ts +7 -0
- package/dist/components/Popover/index.d.ts +3 -0
- package/dist/components/Popover/index.js +3 -0
- package/dist/components/Progress/ProgressBar.svelte +86 -0
- package/dist/components/Progress/ProgressBar.svelte.d.ts +11 -0
- package/dist/components/Progress/ProgressCircle.svelte +134 -0
- package/dist/components/Progress/ProgressCircle.svelte.d.ts +12 -0
- package/dist/components/Progress/Spinner.svelte +68 -0
- package/dist/components/Progress/Spinner.svelte.d.ts +8 -0
- package/dist/components/Progress/index.d.ts +3 -0
- package/dist/components/Progress/index.js +3 -0
- package/dist/components/PullToRefresh/PullToRefresh.svelte +304 -0
- package/dist/components/PullToRefresh/PullToRefresh.svelte.d.ts +20 -0
- package/dist/components/PullToRefresh/index.d.ts +1 -0
- package/dist/components/PullToRefresh/index.js +1 -0
- package/dist/components/SafeArea/SafeArea.svelte +33 -0
- package/dist/components/SafeArea/SafeArea.svelte.d.ts +7 -0
- package/dist/components/Select/Select.svelte +55 -12
- package/dist/components/Skeleton/Skeleton.svelte +59 -0
- package/dist/components/Skeleton/Skeleton.svelte.d.ts +10 -0
- package/dist/components/Skeleton/index.d.ts +1 -0
- package/dist/components/Skeleton/index.js +1 -0
- package/dist/components/Spacer/Spacer.svelte +56 -0
- package/dist/components/Spacer/Spacer.svelte.d.ts +6 -0
- package/dist/components/Spacer/index.d.ts +1 -0
- package/dist/components/Spacer/index.js +1 -0
- package/dist/components/Stack/Stack.svelte +117 -0
- package/dist/components/Stack/Stack.svelte.d.ts +13 -0
- package/dist/components/Stack/index.d.ts +1 -0
- package/dist/components/Stack/index.js +1 -0
- package/dist/components/SwipeActions/SwipeAction.svelte +43 -0
- package/dist/components/SwipeActions/SwipeAction.svelte.d.ts +8 -0
- package/dist/components/SwipeActions/SwipeActions.svelte +193 -0
- package/dist/components/SwipeActions/SwipeActions.svelte.d.ts +9 -0
- package/dist/components/SwipeActions/index.d.ts +2 -0
- package/dist/components/SwipeActions/index.js +2 -0
- package/dist/components/Switch/Switch.svelte +29 -9
- package/dist/components/Table/Table.svelte +175 -0
- package/dist/components/Table/Table.svelte.d.ts +38 -0
- package/dist/components/Table/TableBody.svelte +26 -0
- package/dist/components/Table/TableBody.svelte.d.ts +13 -0
- package/dist/components/Table/TableCell.svelte +85 -0
- package/dist/components/Table/TableCell.svelte.d.ts +28 -0
- package/dist/components/Table/TableHead.svelte +36 -0
- package/dist/components/Table/TableHead.svelte.d.ts +13 -0
- package/dist/components/Table/TableHeader.svelte +217 -0
- package/dist/components/Table/TableHeader.svelte.d.ts +32 -0
- package/dist/components/Table/TableRow.svelte +92 -0
- package/dist/components/Table/TableRow.svelte.d.ts +28 -0
- package/dist/components/Table/index.d.ts +6 -0
- package/dist/components/Table/index.js +6 -0
- package/dist/components/Tag/Tag.svelte +189 -0
- package/dist/components/Tag/Tag.svelte.d.ts +13 -0
- package/dist/components/Tag/index.d.ts +1 -0
- package/dist/components/Tag/index.js +1 -0
- package/dist/components/Toast/Toast.svelte +241 -0
- package/dist/components/Toast/Toast.svelte.d.ts +18 -0
- package/dist/components/Toast/ToastContainer.svelte +110 -0
- package/dist/components/Toast/ToastContainer.svelte.d.ts +8 -0
- package/dist/components/Toast/index.d.ts +3 -0
- package/dist/components/Toast/index.js +3 -0
- package/dist/components/Toast/toast.d.ts +13 -0
- package/dist/components/Toast/toast.js +55 -0
- package/dist/components/Tooltip/Tooltip.svelte +71 -0
- package/dist/components/Tooltip/Tooltip.svelte.d.ts +7 -0
- package/dist/components/Tooltip/index.d.ts +2 -0
- package/dist/components/Tooltip/index.js +1 -0
- package/dist/index.d.ts +29 -1
- package/dist/index.js +32 -0
- package/dist/styles/tokens.css +5 -0
- package/dist/types/data-display.d.ts +93 -0
- package/dist/types/data-display.js +1 -0
- package/dist/types/feedback.d.ts +92 -0
- package/dist/types/feedback.js +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/layout.d.ts +57 -0
- package/dist/types/layout.js +1 -0
- package/dist/types/mobile.d.ts +91 -0
- package/dist/types/mobile.js +1 -0
- package/dist/utils/gestures.d.ts +219 -0
- package/dist/utils/gestures.js +492 -0
- package/dist/utils/haptics.d.ts +89 -0
- package/dist/utils/haptics.js +198 -0
- package/dist/utils/platform.d.ts +47 -0
- package/dist/utils/platform.js +156 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
# @aspect-ops/exon-ui
|
|
2
|
+
|
|
3
|
+
A modern, accessible UI component library for **Svelte 5** with first-class support for **Capacitor** mobile apps.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@aspect-ops/exon-ui)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Svelte 5** - Built with runes and modern Svelte patterns
|
|
11
|
+
- **Mobile-first** - Designed for Capacitor native app conversion
|
|
12
|
+
- **Accessible** - WCAG 2.1 compliant with proper ARIA attributes
|
|
13
|
+
- **Themeable** - CSS custom properties for light/dark themes
|
|
14
|
+
- **Tree-shakeable** - Import only what you need
|
|
15
|
+
- **TypeScript** - Full type definitions included
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @aspect-ops/exon-ui
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```svelte
|
|
26
|
+
<script>
|
|
27
|
+
import { Button, Typography } from '@aspect-ops/exon-ui';
|
|
28
|
+
import '@aspect-ops/exon-ui/styles';
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<Typography variant="h1">Hello World</Typography>
|
|
32
|
+
<Button variant="primary">Click Me</Button>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Components
|
|
36
|
+
|
|
37
|
+
### Core Components
|
|
38
|
+
|
|
39
|
+
| Component | Description |
|
|
40
|
+
| ------------ | --------------------------------------------- |
|
|
41
|
+
| `Button` | Buttons with variants, sizes, loading states |
|
|
42
|
+
| `Typography` | Semantic text with heading and body variants |
|
|
43
|
+
| `Icon` | SVG icon component with size options |
|
|
44
|
+
| `Badge` | Status badges with color variants |
|
|
45
|
+
| `Link` | Accessible links with external link detection |
|
|
46
|
+
|
|
47
|
+
### Form Components
|
|
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 |
|
|
60
|
+
|
|
61
|
+
### Navigation Components
|
|
62
|
+
|
|
63
|
+
| Component | Description |
|
|
64
|
+
| ------------------------------------------------ | ---------------------------------- |
|
|
65
|
+
| `Tabs`, `TabList`, `TabTrigger`, `TabContent` | Accessible tabs with Bits UI |
|
|
66
|
+
| `Menu`, `MenuTrigger`, `MenuContent`, `MenuItem` | Dropdown menus with submenus |
|
|
67
|
+
| `Breadcrumbs`, `BreadcrumbItem` | Navigation breadcrumbs |
|
|
68
|
+
| `BottomNav`, `BottomNavItem` | Mobile bottom navigation |
|
|
69
|
+
| `Navbar`, `NavItem` | Responsive header with mobile menu |
|
|
70
|
+
| `Sidebar`, `SidebarItem`, `SidebarGroup` | Collapsible sidebar navigation |
|
|
71
|
+
|
|
72
|
+
## Usage Examples
|
|
73
|
+
|
|
74
|
+
### Button
|
|
75
|
+
|
|
76
|
+
```svelte
|
|
77
|
+
<script>
|
|
78
|
+
import { Button } from '@aspect-ops/exon-ui';
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<!-- Variants -->
|
|
82
|
+
<Button variant="primary">Primary</Button>
|
|
83
|
+
<Button variant="secondary">Secondary</Button>
|
|
84
|
+
<Button variant="outline">Outline</Button>
|
|
85
|
+
<Button variant="ghost">Ghost</Button>
|
|
86
|
+
<Button variant="destructive">Delete</Button>
|
|
87
|
+
|
|
88
|
+
<!-- Sizes -->
|
|
89
|
+
<Button size="sm">Small</Button>
|
|
90
|
+
<Button size="md">Medium</Button>
|
|
91
|
+
<Button size="lg">Large</Button>
|
|
92
|
+
|
|
93
|
+
<!-- States -->
|
|
94
|
+
<Button loading>Loading...</Button>
|
|
95
|
+
<Button disabled>Disabled</Button>
|
|
96
|
+
<Button fullWidth>Full Width</Button>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Typography
|
|
100
|
+
|
|
101
|
+
```svelte
|
|
102
|
+
<script>
|
|
103
|
+
import { Typography } from '@aspect-ops/exon-ui';
|
|
104
|
+
</script>
|
|
105
|
+
|
|
106
|
+
<Typography variant="h1">Heading 1</Typography>
|
|
107
|
+
<Typography variant="h2">Heading 2</Typography>
|
|
108
|
+
<Typography variant="body">Body text</Typography>
|
|
109
|
+
<Typography variant="body-sm">Small text</Typography>
|
|
110
|
+
<Typography variant="caption">Caption text</Typography>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Form Components
|
|
114
|
+
|
|
115
|
+
```svelte
|
|
116
|
+
<script>
|
|
117
|
+
import { FormField, TextInput, Select, Checkbox, Switch } from '@aspect-ops/exon-ui';
|
|
118
|
+
|
|
119
|
+
let email = $state('');
|
|
120
|
+
let country = $state('');
|
|
121
|
+
let newsletter = $state(false);
|
|
122
|
+
let darkMode = $state(false);
|
|
123
|
+
</script>
|
|
124
|
+
|
|
125
|
+
<FormField label="Email" required helperText="We'll never share your email">
|
|
126
|
+
<TextInput type="email" bind:value={email} placeholder="you@example.com" />
|
|
127
|
+
</FormField>
|
|
128
|
+
|
|
129
|
+
<FormField label="Country">
|
|
130
|
+
<Select
|
|
131
|
+
bind:value={country}
|
|
132
|
+
options={[
|
|
133
|
+
{ value: 'us', label: 'United States' },
|
|
134
|
+
{ value: 'uk', label: 'United Kingdom' },
|
|
135
|
+
{ value: 'in', label: 'India' }
|
|
136
|
+
]}
|
|
137
|
+
/>
|
|
138
|
+
</FormField>
|
|
139
|
+
|
|
140
|
+
<Checkbox bind:checked={newsletter}>
|
|
141
|
+
{#snippet children()}
|
|
142
|
+
Subscribe to newsletter
|
|
143
|
+
{/snippet}
|
|
144
|
+
</Checkbox>
|
|
145
|
+
|
|
146
|
+
<Switch bind:checked={darkMode}>
|
|
147
|
+
{#snippet children()}
|
|
148
|
+
Dark Mode
|
|
149
|
+
{/snippet}
|
|
150
|
+
</Switch>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Tabs
|
|
154
|
+
|
|
155
|
+
```svelte
|
|
156
|
+
<script>
|
|
157
|
+
import { Tabs, TabList, TabTrigger, TabContent } from '@aspect-ops/exon-ui';
|
|
158
|
+
|
|
159
|
+
let activeTab = $state('tab1');
|
|
160
|
+
</script>
|
|
161
|
+
|
|
162
|
+
<Tabs bind:value={activeTab}>
|
|
163
|
+
<TabList>
|
|
164
|
+
<TabTrigger value="tab1">Account</TabTrigger>
|
|
165
|
+
<TabTrigger value="tab2">Security</TabTrigger>
|
|
166
|
+
<TabTrigger value="tab3">Notifications</TabTrigger>
|
|
167
|
+
</TabList>
|
|
168
|
+
|
|
169
|
+
<TabContent value="tab1">
|
|
170
|
+
<p>Account settings content</p>
|
|
171
|
+
</TabContent>
|
|
172
|
+
<TabContent value="tab2">
|
|
173
|
+
<p>Security settings content</p>
|
|
174
|
+
</TabContent>
|
|
175
|
+
<TabContent value="tab3">
|
|
176
|
+
<p>Notification preferences</p>
|
|
177
|
+
</TabContent>
|
|
178
|
+
</Tabs>
|
|
179
|
+
|
|
180
|
+
<!-- Scrollable tabs -->
|
|
181
|
+
<Tabs>
|
|
182
|
+
<TabList scrollable>
|
|
183
|
+
{#each Array(10) as _, i}
|
|
184
|
+
<TabTrigger value="tab-{i}">Tab {i + 1}</TabTrigger>
|
|
185
|
+
{/each}
|
|
186
|
+
</TabList>
|
|
187
|
+
</Tabs>
|
|
188
|
+
|
|
189
|
+
<!-- Vertical tabs -->
|
|
190
|
+
<Tabs orientation="vertical">
|
|
191
|
+
<TabList>
|
|
192
|
+
<TabTrigger value="v1">Item 1</TabTrigger>
|
|
193
|
+
<TabTrigger value="v2">Item 2</TabTrigger>
|
|
194
|
+
</TabList>
|
|
195
|
+
</Tabs>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Menu / Dropdown
|
|
199
|
+
|
|
200
|
+
```svelte
|
|
201
|
+
<script>
|
|
202
|
+
import {
|
|
203
|
+
Menu,
|
|
204
|
+
MenuTrigger,
|
|
205
|
+
MenuContent,
|
|
206
|
+
MenuItem,
|
|
207
|
+
MenuSeparator,
|
|
208
|
+
MenuSub,
|
|
209
|
+
MenuSubTrigger,
|
|
210
|
+
MenuSubContent
|
|
211
|
+
} from '@aspect-ops/exon-ui';
|
|
212
|
+
</script>
|
|
213
|
+
|
|
214
|
+
<Menu>
|
|
215
|
+
<MenuTrigger>
|
|
216
|
+
<button>Open Menu</button>
|
|
217
|
+
</MenuTrigger>
|
|
218
|
+
<MenuContent>
|
|
219
|
+
<MenuItem icon="📝">Edit</MenuItem>
|
|
220
|
+
<MenuItem icon="📋">Copy</MenuItem>
|
|
221
|
+
<MenuSeparator />
|
|
222
|
+
<MenuSub>
|
|
223
|
+
<MenuSubTrigger>More Options</MenuSubTrigger>
|
|
224
|
+
<MenuSubContent>
|
|
225
|
+
<MenuItem>Option 1</MenuItem>
|
|
226
|
+
<MenuItem>Option 2</MenuItem>
|
|
227
|
+
</MenuSubContent>
|
|
228
|
+
</MenuSub>
|
|
229
|
+
<MenuSeparator />
|
|
230
|
+
<MenuItem icon="🗑️">Delete</MenuItem>
|
|
231
|
+
</MenuContent>
|
|
232
|
+
</Menu>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Breadcrumbs
|
|
236
|
+
|
|
237
|
+
```svelte
|
|
238
|
+
<script>
|
|
239
|
+
import { Breadcrumbs } from '@aspect-ops/exon-ui';
|
|
240
|
+
|
|
241
|
+
const items = [
|
|
242
|
+
{ label: 'Home', href: '/' },
|
|
243
|
+
{ label: 'Products', href: '/products' },
|
|
244
|
+
{ label: 'Category', href: '/products/category' },
|
|
245
|
+
{ label: 'Current Page' }
|
|
246
|
+
];
|
|
247
|
+
</script>
|
|
248
|
+
|
|
249
|
+
<Breadcrumbs {items} />
|
|
250
|
+
|
|
251
|
+
<!-- Custom separator -->
|
|
252
|
+
<Breadcrumbs {items}>
|
|
253
|
+
{#snippet separator()}
|
|
254
|
+
→
|
|
255
|
+
{/snippet}
|
|
256
|
+
</Breadcrumbs>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Bottom Navigation (Mobile)
|
|
260
|
+
|
|
261
|
+
```svelte
|
|
262
|
+
<script>
|
|
263
|
+
import { BottomNav } from '@aspect-ops/exon-ui';
|
|
264
|
+
|
|
265
|
+
let activeIndex = $state(0);
|
|
266
|
+
|
|
267
|
+
const items = [
|
|
268
|
+
{ label: 'Home', icon: '🏠' },
|
|
269
|
+
{ label: 'Search', icon: '🔍' },
|
|
270
|
+
{ label: 'Inbox', icon: '📬', badge: 5 },
|
|
271
|
+
{ label: 'Profile', icon: '👤' }
|
|
272
|
+
];
|
|
273
|
+
</script>
|
|
274
|
+
|
|
275
|
+
<BottomNav {items} {activeIndex} onchange={(index) => (activeIndex = index)} />
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Navbar
|
|
279
|
+
|
|
280
|
+
```svelte
|
|
281
|
+
<script>
|
|
282
|
+
import { Navbar, NavItem, Button } from '@aspect-ops/exon-ui';
|
|
283
|
+
</script>
|
|
284
|
+
|
|
285
|
+
<Navbar>
|
|
286
|
+
{#snippet logo()}
|
|
287
|
+
<span>MyApp</span>
|
|
288
|
+
{/snippet}
|
|
289
|
+
|
|
290
|
+
<NavItem label="Home" href="/" active />
|
|
291
|
+
<NavItem label="Products" href="/products" />
|
|
292
|
+
<NavItem label="About" href="/about" />
|
|
293
|
+
|
|
294
|
+
{#snippet actions()}
|
|
295
|
+
<Button size="sm">Sign In</Button>
|
|
296
|
+
{/snippet}
|
|
297
|
+
</Navbar>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Sidebar
|
|
301
|
+
|
|
302
|
+
```svelte
|
|
303
|
+
<script>
|
|
304
|
+
import { Sidebar, SidebarItem, SidebarGroup } from '@aspect-ops/exon-ui';
|
|
305
|
+
|
|
306
|
+
let collapsed = $state(false);
|
|
307
|
+
</script>
|
|
308
|
+
|
|
309
|
+
<Sidebar bind:collapsed>
|
|
310
|
+
<SidebarGroup label="Main">
|
|
311
|
+
<SidebarItem icon="🏠" label="Dashboard" active />
|
|
312
|
+
<SidebarItem icon="📊" label="Analytics" badge={3} />
|
|
313
|
+
<SidebarItem icon="📁" label="Projects" />
|
|
314
|
+
</SidebarGroup>
|
|
315
|
+
|
|
316
|
+
<SidebarGroup label="Settings" collapsible>
|
|
317
|
+
<SidebarItem icon="👤" label="Profile" />
|
|
318
|
+
<SidebarItem icon="🔒" label="Security" />
|
|
319
|
+
</SidebarGroup>
|
|
320
|
+
</Sidebar>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Theming
|
|
324
|
+
|
|
325
|
+
### CSS Custom Properties
|
|
326
|
+
|
|
327
|
+
Import the default styles and customize with CSS variables:
|
|
328
|
+
|
|
329
|
+
```svelte
|
|
330
|
+
<script>
|
|
331
|
+
import '@aspect-ops/exon-ui/styles';
|
|
332
|
+
</script>
|
|
333
|
+
|
|
334
|
+
<style>
|
|
335
|
+
:root {
|
|
336
|
+
/* Colors */
|
|
337
|
+
--color-primary: #3b82f6;
|
|
338
|
+
--color-primary-hover: #2563eb;
|
|
339
|
+
--color-bg: #ffffff;
|
|
340
|
+
--color-bg-muted: #f9fafb;
|
|
341
|
+
--color-text: #1f2937;
|
|
342
|
+
--color-border: #e5e7eb;
|
|
343
|
+
|
|
344
|
+
/* Spacing */
|
|
345
|
+
--space-xs: 0.25rem;
|
|
346
|
+
--space-sm: 0.5rem;
|
|
347
|
+
--space-md: 1rem;
|
|
348
|
+
--space-lg: 1.5rem;
|
|
349
|
+
--space-xl: 2rem;
|
|
350
|
+
|
|
351
|
+
/* Border Radius */
|
|
352
|
+
--radius-sm: 0.25rem;
|
|
353
|
+
--radius-md: 0.5rem;
|
|
354
|
+
--radius-lg: 0.75rem;
|
|
355
|
+
|
|
356
|
+
/* Transitions */
|
|
357
|
+
--transition-fast: 150ms ease;
|
|
358
|
+
--transition-base: 200ms ease;
|
|
359
|
+
}
|
|
360
|
+
</style>
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Dark Mode
|
|
364
|
+
|
|
365
|
+
Toggle dark mode by setting `data-theme="dark"` on the html element:
|
|
366
|
+
|
|
367
|
+
```svelte
|
|
368
|
+
<script>
|
|
369
|
+
function toggleTheme() {
|
|
370
|
+
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
|
371
|
+
document.documentElement.setAttribute('data-theme', isDark ? 'light' : 'dark');
|
|
372
|
+
}
|
|
373
|
+
</script>
|
|
374
|
+
|
|
375
|
+
<button onclick={toggleTheme}>Toggle Theme</button>
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Dark theme variables are automatically applied:
|
|
379
|
+
|
|
380
|
+
```css
|
|
381
|
+
[data-theme='dark'] {
|
|
382
|
+
--color-bg: #111827;
|
|
383
|
+
--color-bg-muted: #1f2937;
|
|
384
|
+
--color-bg-elevated: #1f2937;
|
|
385
|
+
--color-text: #f9fafb;
|
|
386
|
+
--color-border: #374151;
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
## Mobile / Capacitor Support
|
|
391
|
+
|
|
392
|
+
Components are designed with mobile-first principles:
|
|
393
|
+
|
|
394
|
+
- **44px minimum touch targets** for buttons and interactive elements
|
|
395
|
+
- **Safe area inset handling** for notched devices (iPhone, Android gesture bar)
|
|
396
|
+
- **Responsive breakpoints** for adaptive layouts
|
|
397
|
+
- **Hardware-accelerated animations** for smooth 60fps performance
|
|
398
|
+
|
|
399
|
+
### Safe Area Example
|
|
400
|
+
|
|
401
|
+
```svelte
|
|
402
|
+
<BottomNav {items} />
|
|
403
|
+
<!-- Automatically includes padding-bottom: env(safe-area-inset-bottom) -->
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## TypeScript
|
|
407
|
+
|
|
408
|
+
All components include TypeScript definitions:
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
import type {
|
|
412
|
+
ButtonProps,
|
|
413
|
+
ButtonVariant,
|
|
414
|
+
TypographyProps,
|
|
415
|
+
TabsProps,
|
|
416
|
+
MenuItemProps
|
|
417
|
+
} from '@aspect-ops/exon-ui';
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Browser Support
|
|
421
|
+
|
|
422
|
+
- Chrome/Edge 88+
|
|
423
|
+
- Firefox 78+
|
|
424
|
+
- Safari 14+
|
|
425
|
+
- iOS Safari 14+
|
|
426
|
+
- Chrome for Android 88+
|
|
427
|
+
|
|
428
|
+
## Contributing
|
|
429
|
+
|
|
430
|
+
Contributions are welcome! Please read our contributing guidelines before submitting a PR.
|
|
431
|
+
|
|
432
|
+
## License
|
|
433
|
+
|
|
434
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
Built with Svelte 5 and Bits UI
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ActionSheetProps } from '../../types/index.js';
|
|
3
|
+
import { Dialog } from 'bits-ui';
|
|
4
|
+
|
|
5
|
+
interface Props extends ActionSheetProps {
|
|
6
|
+
/** Bindable open state */
|
|
7
|
+
open?: boolean;
|
|
8
|
+
/** Slot for ActionSheetItem children */
|
|
9
|
+
actions?: import('svelte').Snippet;
|
|
10
|
+
/** Default slot for custom content */
|
|
11
|
+
children?: import('svelte').Snippet;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
open = $bindable(false),
|
|
16
|
+
title,
|
|
17
|
+
description,
|
|
18
|
+
cancelLabel = 'Cancel',
|
|
19
|
+
showCancel = true,
|
|
20
|
+
closeOnSelect = true,
|
|
21
|
+
class: className = '',
|
|
22
|
+
actions,
|
|
23
|
+
children
|
|
24
|
+
}: Props = $props();
|
|
25
|
+
|
|
26
|
+
function handleOpenChange(value: boolean) {
|
|
27
|
+
open = value;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function handleBackdropClick() {
|
|
31
|
+
open = false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function handleCancel() {
|
|
35
|
+
open = false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Provide context for ActionSheetItem to access closeOnSelect
|
|
39
|
+
import { setContext } from 'svelte';
|
|
40
|
+
|
|
41
|
+
setContext('actionsheet', {
|
|
42
|
+
get closeOnSelect() {
|
|
43
|
+
return closeOnSelect;
|
|
44
|
+
},
|
|
45
|
+
close: () => {
|
|
46
|
+
open = false;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<Dialog.Root {open} onOpenChange={handleOpenChange}>
|
|
52
|
+
<Dialog.Portal>
|
|
53
|
+
<Dialog.Overlay class="actionsheet-overlay" onclick={handleBackdropClick} />
|
|
54
|
+
<Dialog.Content
|
|
55
|
+
class="actionsheet-content {className}"
|
|
56
|
+
aria-labelledby={title ? 'actionsheet-title' : undefined}
|
|
57
|
+
>
|
|
58
|
+
<!-- Header section (optional) -->
|
|
59
|
+
{#if title || description}
|
|
60
|
+
<div class="actionsheet-header">
|
|
61
|
+
{#if title}
|
|
62
|
+
<Dialog.Title id="actionsheet-title" class="actionsheet-title">{title}</Dialog.Title>
|
|
63
|
+
{/if}
|
|
64
|
+
{#if description}
|
|
65
|
+
<Dialog.Description class="actionsheet-description">{description}</Dialog.Description>
|
|
66
|
+
{/if}
|
|
67
|
+
</div>
|
|
68
|
+
{/if}
|
|
69
|
+
|
|
70
|
+
<!-- Actions section -->
|
|
71
|
+
<div class="actionsheet-actions" role="group">
|
|
72
|
+
{#if actions}
|
|
73
|
+
{@render actions()}
|
|
74
|
+
{/if}
|
|
75
|
+
{#if children}
|
|
76
|
+
{@render children()}
|
|
77
|
+
{/if}
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<!-- Cancel button (separated) -->
|
|
81
|
+
{#if showCancel}
|
|
82
|
+
<div class="actionsheet-cancel-container">
|
|
83
|
+
<button
|
|
84
|
+
type="button"
|
|
85
|
+
class="actionsheet-cancel"
|
|
86
|
+
onclick={handleCancel}
|
|
87
|
+
aria-label="Cancel"
|
|
88
|
+
>
|
|
89
|
+
{cancelLabel}
|
|
90
|
+
</button>
|
|
91
|
+
</div>
|
|
92
|
+
{/if}
|
|
93
|
+
</Dialog.Content>
|
|
94
|
+
</Dialog.Portal>
|
|
95
|
+
</Dialog.Root>
|
|
96
|
+
|
|
97
|
+
<style>
|
|
98
|
+
/* Overlay */
|
|
99
|
+
:global(.actionsheet-overlay) {
|
|
100
|
+
position: fixed;
|
|
101
|
+
inset: 0;
|
|
102
|
+
background: rgba(0, 0, 0, 0.4);
|
|
103
|
+
z-index: 100;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
:global(.actionsheet-overlay[data-state='open']) {
|
|
107
|
+
animation: actionsheet-fade-in 200ms ease-out;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
:global(.actionsheet-overlay[data-state='closed']) {
|
|
111
|
+
animation: actionsheet-fade-out 150ms ease-in;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@keyframes -global-actionsheet-fade-in {
|
|
115
|
+
from {
|
|
116
|
+
opacity: 0;
|
|
117
|
+
}
|
|
118
|
+
to {
|
|
119
|
+
opacity: 1;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@keyframes -global-actionsheet-fade-out {
|
|
124
|
+
from {
|
|
125
|
+
opacity: 1;
|
|
126
|
+
}
|
|
127
|
+
to {
|
|
128
|
+
opacity: 0;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Content */
|
|
133
|
+
:global(.actionsheet-content) {
|
|
134
|
+
/* F33: Must set font-family explicitly - portal doesn't inherit */
|
|
135
|
+
font-family: var(--font-family, system-ui, -apple-system, sans-serif);
|
|
136
|
+
position: fixed;
|
|
137
|
+
bottom: 0;
|
|
138
|
+
left: 0;
|
|
139
|
+
right: 0;
|
|
140
|
+
z-index: 101;
|
|
141
|
+
display: flex;
|
|
142
|
+
flex-direction: column;
|
|
143
|
+
max-height: 90vh;
|
|
144
|
+
padding: var(--space-sm, 0.5rem);
|
|
145
|
+
padding-bottom: calc(var(--space-sm, 0.5rem) + env(safe-area-inset-bottom, 0px));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
:global(.actionsheet-content[data-state='open']) {
|
|
149
|
+
animation: actionsheet-slide-up 250ms cubic-bezier(0.32, 0.72, 0, 1);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
:global(.actionsheet-content[data-state='closed']) {
|
|
153
|
+
animation: actionsheet-slide-down 200ms cubic-bezier(0.32, 0.72, 0, 1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@keyframes -global-actionsheet-slide-up {
|
|
157
|
+
from {
|
|
158
|
+
opacity: 0;
|
|
159
|
+
transform: translateY(100%);
|
|
160
|
+
}
|
|
161
|
+
to {
|
|
162
|
+
opacity: 1;
|
|
163
|
+
transform: translateY(0);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@keyframes -global-actionsheet-slide-down {
|
|
168
|
+
from {
|
|
169
|
+
opacity: 1;
|
|
170
|
+
transform: translateY(0);
|
|
171
|
+
}
|
|
172
|
+
to {
|
|
173
|
+
opacity: 0;
|
|
174
|
+
transform: translateY(100%);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Header */
|
|
179
|
+
:global(.actionsheet-header) {
|
|
180
|
+
text-align: center;
|
|
181
|
+
padding: var(--space-md, 1rem) var(--space-lg, 1.5rem);
|
|
182
|
+
background: var(--color-bg, #ffffff);
|
|
183
|
+
border-radius: var(--radius-lg, 0.75rem) var(--radius-lg, 0.75rem) 0 0;
|
|
184
|
+
border-bottom: 1px solid var(--color-border, rgba(0, 0, 0, 0.1));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
:global(.actionsheet-title) {
|
|
188
|
+
margin: 0;
|
|
189
|
+
font-size: var(--text-sm, 0.875rem);
|
|
190
|
+
font-weight: 600;
|
|
191
|
+
color: var(--color-text-secondary, #6b7280);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
:global(.actionsheet-description) {
|
|
195
|
+
margin: var(--space-xs, 0.25rem) 0 0;
|
|
196
|
+
font-size: var(--text-xs, 0.75rem);
|
|
197
|
+
color: var(--color-text-muted, #9ca3af);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* Actions container */
|
|
201
|
+
:global(.actionsheet-actions) {
|
|
202
|
+
display: flex;
|
|
203
|
+
flex-direction: column;
|
|
204
|
+
background: var(--color-bg, #ffffff);
|
|
205
|
+
border-radius: var(--radius-lg, 0.75rem);
|
|
206
|
+
overflow: hidden;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* When there's a header, remove top border radius from actions */
|
|
210
|
+
:global(.actionsheet-header + .actionsheet-actions) {
|
|
211
|
+
border-radius: 0 0 var(--radius-lg, 0.75rem) var(--radius-lg, 0.75rem);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* Cancel button container */
|
|
215
|
+
:global(.actionsheet-cancel-container) {
|
|
216
|
+
margin-top: var(--space-sm, 0.5rem);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Cancel button - iOS style */
|
|
220
|
+
:global(.actionsheet-cancel) {
|
|
221
|
+
/* F33: Font inheritance for portal */
|
|
222
|
+
font-family: var(--font-family, system-ui, -apple-system, sans-serif);
|
|
223
|
+
width: 100%;
|
|
224
|
+
/* F20: Minimum touch target */
|
|
225
|
+
min-height: 56px;
|
|
226
|
+
padding: var(--space-md, 1rem);
|
|
227
|
+
border: none;
|
|
228
|
+
border-radius: var(--radius-lg, 0.75rem);
|
|
229
|
+
background: var(--color-bg, #ffffff);
|
|
230
|
+
color: var(--color-primary, #3b82f6);
|
|
231
|
+
font-size: var(--text-lg, 1.125rem);
|
|
232
|
+
font-weight: 600;
|
|
233
|
+
cursor: pointer;
|
|
234
|
+
transition: background-color var(--transition-fast, 150ms ease);
|
|
235
|
+
-webkit-tap-highlight-color: transparent;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
:global(.actionsheet-cancel:hover) {
|
|
239
|
+
background: var(--color-bg-hover, #f9fafb);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
:global(.actionsheet-cancel:active) {
|
|
243
|
+
background: var(--color-bg-active, #f3f4f6);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
:global(.actionsheet-cancel:focus-visible) {
|
|
247
|
+
outline: 2px solid var(--color-primary, #3b82f6);
|
|
248
|
+
outline-offset: 2px;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* Platform-specific styling */
|
|
252
|
+
:global([data-platform='ios']) :global(.actionsheet-content) {
|
|
253
|
+
padding: var(--space-sm, 0.5rem);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
:global([data-platform='android']) :global(.actionsheet-content) {
|
|
257
|
+
padding: 0;
|
|
258
|
+
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
:global([data-platform='android']) :global(.actionsheet-actions),
|
|
262
|
+
:global([data-platform='android']) :global(.actionsheet-cancel) {
|
|
263
|
+
border-radius: 0;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
:global([data-platform='android']) :global(.actionsheet-cancel-container) {
|
|
267
|
+
margin-top: 0;
|
|
268
|
+
border-top: 1px solid var(--color-border, rgba(0, 0, 0, 0.1));
|
|
269
|
+
}
|
|
270
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ActionSheetProps } from '../../types/index.js';
|
|
2
|
+
interface Props extends ActionSheetProps {
|
|
3
|
+
/** Bindable open state */
|
|
4
|
+
open?: boolean;
|
|
5
|
+
/** Slot for ActionSheetItem children */
|
|
6
|
+
actions?: import('svelte').Snippet;
|
|
7
|
+
/** Default slot for custom content */
|
|
8
|
+
children?: import('svelte').Snippet;
|
|
9
|
+
}
|
|
10
|
+
declare const ActionSheet: import("svelte").Component<Props, {}, "open">;
|
|
11
|
+
type ActionSheet = ReturnType<typeof ActionSheet>;
|
|
12
|
+
export default ActionSheet;
|