@redseed/redseed-ui-vue3 6.7.1 → 7.0.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/docs/01-layout-components.md +5 -5
- package/docs/07-interactive-components.md +321 -155
- package/index.js +0 -2
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@ Layout components provide the structural foundation for organizing content on pa
|
|
|
10
10
|
```vue
|
|
11
11
|
<template>
|
|
12
12
|
<SingleColumnLayout>
|
|
13
|
-
<div class="bg-
|
|
13
|
+
<div class="bg-grey-200 text-center p-8">
|
|
14
14
|
Content max width is 768px
|
|
15
15
|
</div>
|
|
16
16
|
</SingleColumnLayout>
|
|
@@ -24,12 +24,12 @@ Layout components provide the structural foundation for organizing content on pa
|
|
|
24
24
|
<template>
|
|
25
25
|
<TwoColumnLayout>
|
|
26
26
|
<template #main>
|
|
27
|
-
<div class="bg-
|
|
27
|
+
<div class="bg-grey-200 text-center p-8">
|
|
28
28
|
Main content
|
|
29
29
|
</div>
|
|
30
30
|
</template>
|
|
31
31
|
<template #aside>
|
|
32
|
-
<div class="bg-
|
|
32
|
+
<div class="bg-grey-200 text-center p-8">
|
|
33
33
|
Aside content
|
|
34
34
|
</div>
|
|
35
35
|
</template>
|
|
@@ -130,7 +130,7 @@ Layout components provide the structural foundation for organizing content on pa
|
|
|
130
130
|
</template>
|
|
131
131
|
</SectionHeader>
|
|
132
132
|
</template>
|
|
133
|
-
|
|
133
|
+
|
|
134
134
|
<ButtonCard>
|
|
135
135
|
<template #icon>
|
|
136
136
|
<FaceSmileIcon />
|
|
@@ -156,4 +156,4 @@ Layout components provide the structural foundation for organizing content on pa
|
|
|
156
156
|
|
|
157
157
|
- Layout components include proper semantic HTML structure
|
|
158
158
|
- Screen readers can navigate through the layout hierarchy
|
|
159
|
-
- Focus management is handled appropriately for keyboard navigation
|
|
159
|
+
- Focus management is handled appropriately for keyboard navigation
|
|
@@ -1,22 +1,38 @@
|
|
|
1
1
|
# Interactive Components
|
|
2
2
|
|
|
3
|
-
Interactive components provide user interface elements that respond to user actions and enable dynamic interactions. They include modals, dropdowns, toggles, and
|
|
3
|
+
Interactive components provide user interface elements that respond to user actions and enable dynamic interactions. They include modals, dropdowns, toggles, disclosures, progress indicators, loaders, empty states, and interactive links.
|
|
4
4
|
|
|
5
5
|
## Modals
|
|
6
6
|
|
|
7
7
|
### Modal
|
|
8
|
-
**When to use:** For displaying important information, forms, or actions that require user attention and should appear above the current page content.
|
|
9
|
-
**
|
|
8
|
+
**When to use:** For displaying important information, forms, or actions that require user attention and should appear above the current page content.
|
|
9
|
+
**Key props:** `show` controls visibility, `closeable` toggles dismissal, size flags (`sm`, `md`, `lg`), and positional flags (`top`, `center`, `bottom`). The `footer-start` prop left-aligns footer actions.
|
|
10
|
+
**Slots:** `header` and `footer` receive a `close` function in their slot scope; the default slot renders the main content.
|
|
11
|
+
|
|
10
12
|
```vue
|
|
13
|
+
<script setup>
|
|
14
|
+
import { ref } from 'vue'
|
|
15
|
+
|
|
16
|
+
const isModalOpen = ref(false)
|
|
17
|
+
|
|
18
|
+
function confirmAction() {
|
|
19
|
+
// handle confirm logic
|
|
20
|
+
isModalOpen.value = false
|
|
21
|
+
}
|
|
22
|
+
</script>
|
|
23
|
+
|
|
11
24
|
<template>
|
|
12
|
-
<ButtonPrimary @click="
|
|
13
|
-
|
|
25
|
+
<ButtonPrimary @click="isModalOpen = true">Open Modal</ButtonPrimary>
|
|
26
|
+
|
|
27
|
+
<Modal :show="isModalOpen" @close="isModalOpen = false" lg>
|
|
14
28
|
<template #header>
|
|
15
29
|
Modal Title
|
|
16
30
|
</template>
|
|
31
|
+
|
|
17
32
|
<p>Are you sure you want to perform this action?</p>
|
|
18
|
-
|
|
19
|
-
|
|
33
|
+
|
|
34
|
+
<template #footer="{ close }">
|
|
35
|
+
<ButtonSecondary @click="close">Cancel</ButtonSecondary>
|
|
20
36
|
<ButtonPrimary @click="confirmAction">Confirm</ButtonPrimary>
|
|
21
37
|
</template>
|
|
22
38
|
</Modal>
|
|
@@ -26,26 +42,48 @@ Interactive components provide user interface elements that respond to user acti
|
|
|
26
42
|
## Dropdowns
|
|
27
43
|
|
|
28
44
|
### DropdownMenu
|
|
29
|
-
**When to use:** For displaying a list of
|
|
30
|
-
**
|
|
45
|
+
**When to use:** For displaying a list of actions or options that appear when triggered, such as user menus or contextual actions.
|
|
46
|
+
**Key props:** `left` and `right` set menu alignment (defaults to left). The default trigger is a tertiary button; use the `trigger` slot to supply a custom activator.
|
|
47
|
+
|
|
31
48
|
```vue
|
|
49
|
+
<script setup>
|
|
50
|
+
function handleEdit() {
|
|
51
|
+
// edit logic
|
|
52
|
+
}
|
|
53
|
+
function handleDelete() {
|
|
54
|
+
// delete logic
|
|
55
|
+
}
|
|
56
|
+
function handleShare() {
|
|
57
|
+
// share logic
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
|
|
32
61
|
<template>
|
|
33
|
-
<DropdownMenu>
|
|
34
|
-
<template #trigger
|
|
62
|
+
<DropdownMenu right>
|
|
63
|
+
<template #trigger="{ open }">
|
|
64
|
+
<ButtonTertiary @click="open">Dropdown Menu</ButtonTertiary>
|
|
65
|
+
</template>
|
|
66
|
+
|
|
35
67
|
<DropdownOption @click="handleEdit">Edit</DropdownOption>
|
|
36
|
-
<DropdownOption @click="handleDelete">Delete</DropdownOption>
|
|
37
68
|
<DropdownOption @click="handleShare">Share</DropdownOption>
|
|
69
|
+
<DropdownOption @click="handleDelete">Delete</DropdownOption>
|
|
38
70
|
</DropdownMenu>
|
|
39
71
|
</template>
|
|
40
72
|
```
|
|
41
73
|
|
|
42
74
|
### DropdownOption
|
|
43
|
-
**When to use:** For individual options within a DropdownMenu.
|
|
44
|
-
|
|
75
|
+
**When to use:** For individual options within a `DropdownMenu`. Emits `click` when selected and renders arbitrary slot content (including icons when provided).
|
|
76
|
+
|
|
45
77
|
```vue
|
|
78
|
+
<script setup>
|
|
79
|
+
function handleAction() {
|
|
80
|
+
// option handling
|
|
81
|
+
}
|
|
82
|
+
</script>
|
|
83
|
+
|
|
46
84
|
<template>
|
|
47
85
|
<DropdownOption @click="handleAction">
|
|
48
|
-
|
|
86
|
+
Share
|
|
49
87
|
</DropdownOption>
|
|
50
88
|
</template>
|
|
51
89
|
```
|
|
@@ -53,68 +91,84 @@ Interactive components provide user interface elements that respond to user acti
|
|
|
53
91
|
## Toggles and Switches
|
|
54
92
|
|
|
55
93
|
### Toggle
|
|
56
|
-
**When to use:** For binary on/off states, such as feature toggles, settings, or boolean preferences.
|
|
57
|
-
**
|
|
94
|
+
**When to use:** For binary on/off states, such as feature toggles, settings, or boolean preferences.
|
|
95
|
+
**Slots:** `label`, `help`, and `error` provide contextual messaging.
|
|
96
|
+
|
|
58
97
|
```vue
|
|
98
|
+
<script setup>
|
|
99
|
+
import { ref } from 'vue'
|
|
100
|
+
|
|
101
|
+
const isEnabled = ref(false)
|
|
102
|
+
|
|
103
|
+
function handleToggleChange(value) {
|
|
104
|
+
// update setting
|
|
105
|
+
console.log(value)
|
|
106
|
+
}
|
|
107
|
+
</script>
|
|
108
|
+
|
|
59
109
|
<template>
|
|
60
110
|
<Toggle v-model="isEnabled" @update="handleToggleChange">
|
|
61
111
|
<template #label>Enable notifications</template>
|
|
112
|
+
<template #help>Send me updates about new features</template>
|
|
62
113
|
</Toggle>
|
|
63
114
|
</template>
|
|
64
115
|
```
|
|
65
116
|
|
|
66
117
|
### Switcher
|
|
67
|
-
**When to use:** For switching between multiple options
|
|
68
|
-
**
|
|
69
|
-
```vue
|
|
70
|
-
<template>
|
|
71
|
-
<Switcher
|
|
72
|
-
:items="switcherItems"
|
|
73
|
-
@change="handleViewChange"
|
|
74
|
-
/>
|
|
75
|
-
</template>
|
|
118
|
+
**When to use:** For switching between multiple options, such as view modes or filters.
|
|
119
|
+
**Key props:** `items` accepts an array of `{ id, label, active?, icon? }`. Use `full` to stretch items across the container. Emits `change` with the selected item.
|
|
76
120
|
|
|
121
|
+
```vue
|
|
77
122
|
<script setup>
|
|
78
|
-
const
|
|
79
|
-
{ id: 'list', label: 'List View' },
|
|
123
|
+
const viewOptions = [
|
|
124
|
+
{ id: 'list', label: 'List View', active: true },
|
|
80
125
|
{ id: 'grid', label: 'Grid View' },
|
|
81
|
-
{ id: 'table', label: 'Table View' }
|
|
126
|
+
{ id: 'table', label: 'Table View' },
|
|
82
127
|
]
|
|
128
|
+
|
|
129
|
+
function handleViewChange(item) {
|
|
130
|
+
console.log(item.id)
|
|
131
|
+
}
|
|
83
132
|
</script>
|
|
84
|
-
```
|
|
85
133
|
|
|
86
|
-
### SwitcherItem
|
|
87
|
-
**When to use:** For individual options within a Switcher component (used internally by Switcher).
|
|
88
|
-
**Implementation:**
|
|
89
|
-
```vue
|
|
90
134
|
<template>
|
|
91
|
-
|
|
92
|
-
<!-- Define items as an array of objects with id and label properties -->
|
|
93
|
-
<Switcher :items="items" @change="handleChange" />
|
|
135
|
+
<Switcher :items="viewOptions" @change="handleViewChange" />
|
|
94
136
|
</template>
|
|
95
|
-
|
|
96
|
-
<script setup>
|
|
97
|
-
const items = [
|
|
98
|
-
{ id: 'option1', label: 'Option 1' },
|
|
99
|
-
{ id: 'option2', label: 'Option 2' },
|
|
100
|
-
{ id: 'option3', label: 'Option 3' }
|
|
101
|
-
]
|
|
102
|
-
</script>
|
|
103
137
|
```
|
|
104
138
|
|
|
139
|
+
### SwitcherItem
|
|
140
|
+
**When to use:** `SwitcherItem` is consumed internally by `Switcher`. Configure options through the `items` prop instead of rendering `SwitcherItem` directly.
|
|
141
|
+
|
|
105
142
|
## Disclosure
|
|
106
143
|
|
|
107
144
|
### Disclosure
|
|
108
|
-
**When to use:** For expandable
|
|
109
|
-
**
|
|
145
|
+
**When to use:** For expandable or collapsible content sections such as FAQs or accordions.
|
|
146
|
+
**Key props:** `open` sets the initial state. Emits `click` with `{ open }` whenever the disclosure toggles.
|
|
147
|
+
**Slots:** Provide layout via the required `child` slot, which exposes `triggerId`, `contentId`, `handleTrigger`, and `isOpen`. Use the `trigger` slot for a custom activator or rely on the default tertiary button.
|
|
148
|
+
|
|
110
149
|
```vue
|
|
150
|
+
<script setup>
|
|
151
|
+
import { ref } from 'vue'
|
|
152
|
+
|
|
153
|
+
const isOpen = ref(false)
|
|
154
|
+
|
|
155
|
+
function handleDisclosure(event) {
|
|
156
|
+
isOpen.value = event.open
|
|
157
|
+
}
|
|
158
|
+
</script>
|
|
159
|
+
|
|
111
160
|
<template>
|
|
112
|
-
<Disclosure
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
161
|
+
<Disclosure :open="isOpen" @click="handleDisclosure">
|
|
162
|
+
<template #child="{ triggerId, contentId }">
|
|
163
|
+
<div>
|
|
164
|
+
<div :id="triggerId"></div>
|
|
165
|
+
<div :id="contentId"></div>
|
|
166
|
+
</div>
|
|
167
|
+
</template>
|
|
168
|
+
|
|
169
|
+
<template #content>
|
|
170
|
+
<p>This content can be expanded or collapsed.</p>
|
|
171
|
+
</template>
|
|
118
172
|
</Disclosure>
|
|
119
173
|
</template>
|
|
120
174
|
```
|
|
@@ -122,99 +176,170 @@ const items = [
|
|
|
122
176
|
## Progress Indicators
|
|
123
177
|
|
|
124
178
|
### ProgressCircle
|
|
125
|
-
**When to use:** For displaying progress in a circular format, such as upload progress
|
|
126
|
-
**
|
|
179
|
+
**When to use:** For displaying progress in a circular format, such as upload progress or completion percentages.
|
|
180
|
+
**Key props:** `percentage` (0–100) with optional size flags (`sm`, `md`, `lg`). Slot content is centered inside the circle.
|
|
181
|
+
|
|
127
182
|
```vue
|
|
128
183
|
<template>
|
|
129
|
-
<ProgressCircle
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
:strokeWidth="8"
|
|
133
|
-
label="75% Complete"
|
|
134
|
-
/>
|
|
184
|
+
<ProgressCircle :percentage="75" md>
|
|
185
|
+
75%
|
|
186
|
+
</ProgressCircle>
|
|
135
187
|
</template>
|
|
136
188
|
```
|
|
137
189
|
|
|
138
190
|
### ProgressTracker
|
|
139
|
-
**When to use:** For multi-step processes
|
|
140
|
-
**
|
|
191
|
+
**When to use:** For multi-step processes where users need to see their progress through a series of steps.
|
|
192
|
+
**Key props:** `steps` is an array of step objects (e.g. `{ label: 'Step 1', active: true, completed: false }`). Use `clickable` and `skippable` to allow direct navigation. Emits `change` with the selected step.
|
|
193
|
+
|
|
141
194
|
```vue
|
|
195
|
+
<script setup>
|
|
196
|
+
import { reactive } from 'vue'
|
|
197
|
+
|
|
198
|
+
const onboardingSteps = reactive([
|
|
199
|
+
{ label: 'Details', active: true, completed: false },
|
|
200
|
+
{ label: 'Schedule', active: false, completed: false },
|
|
201
|
+
{ label: 'Publish', active: false, completed: false },
|
|
202
|
+
])
|
|
203
|
+
|
|
204
|
+
function handleStepChange(step) {
|
|
205
|
+
console.log(step.label)
|
|
206
|
+
}
|
|
207
|
+
</script>
|
|
208
|
+
|
|
142
209
|
<template>
|
|
143
|
-
<ProgressTracker
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
:isCompleted="step.id < currentStep"
|
|
150
|
-
/>
|
|
151
|
-
</ProgressTracker>
|
|
210
|
+
<ProgressTracker
|
|
211
|
+
:steps="onboardingSteps"
|
|
212
|
+
clickable
|
|
213
|
+
skippable
|
|
214
|
+
@change="handleStepChange"
|
|
215
|
+
/>
|
|
152
216
|
</template>
|
|
153
217
|
```
|
|
154
218
|
|
|
155
219
|
### ProgressTrackerStep
|
|
156
|
-
**When to use:** For
|
|
157
|
-
|
|
220
|
+
**When to use:** For custom layouts inside `ProgressTracker`. The default slot of `ProgressTracker` exposes `internalSteps` so you can render `ProgressTrackerStep` manually when extra decoration is needed.
|
|
221
|
+
|
|
158
222
|
```vue
|
|
223
|
+
<script setup>
|
|
224
|
+
import { reactive } from 'vue'
|
|
225
|
+
|
|
226
|
+
const steps = reactive([
|
|
227
|
+
{ label: 'Details', active: true, completed: false },
|
|
228
|
+
{ label: 'Content', active: false, completed: false },
|
|
229
|
+
{ label: 'Review', active: false, completed: false },
|
|
230
|
+
])
|
|
231
|
+
|
|
232
|
+
function handleStepChange(step) {
|
|
233
|
+
console.log(step.label)
|
|
234
|
+
}
|
|
235
|
+
</script>
|
|
236
|
+
|
|
159
237
|
<template>
|
|
160
|
-
<
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
238
|
+
<ProgressTracker :steps="steps">
|
|
239
|
+
<template #default="{ internalSteps }">
|
|
240
|
+
<ProgressTrackerStep
|
|
241
|
+
v-for="(step, index) in internalSteps"
|
|
242
|
+
:key="step.label"
|
|
243
|
+
:step="step"
|
|
244
|
+
:active="step.active"
|
|
245
|
+
:completed="step.completed"
|
|
246
|
+
clickable
|
|
247
|
+
@click="handleStepChange"
|
|
248
|
+
>
|
|
249
|
+
<template #indicator>
|
|
250
|
+
{{ index + 1 }}
|
|
251
|
+
</template>
|
|
252
|
+
{{ step.label }}
|
|
253
|
+
</ProgressTrackerStep>
|
|
254
|
+
</template>
|
|
255
|
+
</ProgressTracker>
|
|
165
256
|
</template>
|
|
166
257
|
```
|
|
167
258
|
|
|
168
259
|
## Loaders
|
|
169
260
|
|
|
170
261
|
### Loader
|
|
171
|
-
**When to use:** For indicating loading states, such as when data is being fetched
|
|
172
|
-
**
|
|
262
|
+
**When to use:** For indicating loading states, such as when data is being fetched or content is processing.
|
|
263
|
+
**Key props:** Boolean flags `primary`, `secondary`, and `white` adjust the palette. The loader inherits size from its container.
|
|
264
|
+
|
|
173
265
|
```vue
|
|
266
|
+
<script setup>
|
|
267
|
+
import { ref } from 'vue'
|
|
268
|
+
|
|
269
|
+
const isLoading = ref(true)
|
|
270
|
+
|
|
271
|
+
function completeLoading() {
|
|
272
|
+
isLoading.value = false
|
|
273
|
+
}
|
|
274
|
+
</script>
|
|
275
|
+
|
|
174
276
|
<template>
|
|
175
|
-
<
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
size="md"
|
|
179
|
-
/>
|
|
277
|
+
<ButtonPrimary @click="completeLoading">Finish</ButtonPrimary>
|
|
278
|
+
|
|
279
|
+
<Loader v-if="isLoading" primary />
|
|
180
280
|
</template>
|
|
181
281
|
```
|
|
182
282
|
|
|
183
283
|
## Empty States
|
|
184
284
|
|
|
185
285
|
### Empty
|
|
186
|
-
**When to use:** For displaying empty states when there
|
|
187
|
-
**
|
|
286
|
+
**When to use:** For displaying empty states when there is no data to show, such as empty lists, search results, or content areas.
|
|
287
|
+
**Key props:** `showImage` toggles the default illustration. Emits `clickPrimaryAction`, `clickSecondaryAction`, and `clickTertiaryAction` when the corresponding default buttons are used.
|
|
288
|
+
**Slots:** `image`, `title`, default (description), `primary-action-label`, `secondary-action-label`, `tertiary-action-label`, or the full `actions` slot.
|
|
289
|
+
|
|
188
290
|
```vue
|
|
291
|
+
<script setup>
|
|
292
|
+
function handleCreate() {
|
|
293
|
+
// start creation flow
|
|
294
|
+
}
|
|
295
|
+
function handleLearnMore() {
|
|
296
|
+
// open documentation
|
|
297
|
+
}
|
|
298
|
+
</script>
|
|
299
|
+
|
|
189
300
|
<template>
|
|
190
|
-
<Empty
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
301
|
+
<Empty
|
|
302
|
+
@clickPrimaryAction="handleCreate"
|
|
303
|
+
@clickSecondaryAction="handleLearnMore"
|
|
304
|
+
>
|
|
305
|
+
<template #title>No items found</template>
|
|
306
|
+
Try adjusting your search criteria or create a new item.
|
|
307
|
+
<template #primary-action-label>Create Item</template>
|
|
308
|
+
<template #secondary-action-label>Learn more</template>
|
|
309
|
+
</Empty>
|
|
197
310
|
</template>
|
|
198
311
|
```
|
|
199
312
|
|
|
200
313
|
## Links
|
|
201
314
|
|
|
202
315
|
### LinkPrimary
|
|
203
|
-
**When to use:** For primary navigation links that should stand out
|
|
204
|
-
**
|
|
316
|
+
**When to use:** For primary navigation links or calls to action that should stand out.
|
|
317
|
+
**Key props:** Size flags (`sm`, `md`, `lg`) and `disabled`. The `icon` slot renders a leading icon.
|
|
318
|
+
|
|
205
319
|
```vue
|
|
206
320
|
<template>
|
|
207
|
-
<LinkPrimary href="/dashboard">
|
|
321
|
+
<LinkPrimary href="/dashboard">
|
|
322
|
+
<template #icon>
|
|
323
|
+
<ArrowRightIcon />
|
|
324
|
+
</template>
|
|
325
|
+
Dashboard
|
|
326
|
+
</LinkPrimary>
|
|
208
327
|
</template>
|
|
209
328
|
```
|
|
210
329
|
|
|
211
330
|
### LinkSlot
|
|
212
|
-
**When to use:** For custom link
|
|
213
|
-
|
|
331
|
+
**When to use:** For composing custom link treatments while keeping RedSeed link styling. Supports the same sizing and disabled props as `LinkPrimary` and emits a `click` event.
|
|
332
|
+
|
|
214
333
|
```vue
|
|
334
|
+
<script setup>
|
|
335
|
+
function handleLinkClick(event) {
|
|
336
|
+
event.preventDefault()
|
|
337
|
+
// custom routing
|
|
338
|
+
}
|
|
339
|
+
</script>
|
|
340
|
+
|
|
215
341
|
<template>
|
|
216
|
-
<LinkSlot href="/custom-page"
|
|
217
|
-
<Icon name="arrow-right" />
|
|
342
|
+
<LinkSlot href="/custom-page" lg @click="handleLinkClick">
|
|
218
343
|
Custom Link Text
|
|
219
344
|
</LinkSlot>
|
|
220
345
|
</template>
|
|
@@ -222,94 +347,135 @@ const items = [
|
|
|
222
347
|
|
|
223
348
|
## Best Practices
|
|
224
349
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
350
|
+
- Provide clear visual feedback for all interactive states (hover, focus, active, disabled).
|
|
351
|
+
- Preserve accessibility when customizing slots: keep semantic elements, aria attributes, and keyboard support intact.
|
|
352
|
+
- Maintain consistent interaction patterns across similar components (e.g. dropdowns should all close on outside click).
|
|
353
|
+
- Surface loading indicators quickly when operations take time.
|
|
354
|
+
- Offer descriptive error messaging and recovery actions.
|
|
355
|
+
- Test on mobile to ensure touch targets remain usable.
|
|
356
|
+
- Monitor performance for complex composites such as nested disclosures or large progress trackers.
|
|
232
357
|
|
|
233
358
|
## Common Use Cases
|
|
234
359
|
|
|
235
360
|
### User Settings Modal
|
|
236
361
|
```vue
|
|
362
|
+
<script setup>
|
|
363
|
+
import { reactive, ref } from 'vue'
|
|
364
|
+
|
|
365
|
+
const showSettings = ref(false)
|
|
366
|
+
const userName = ref('')
|
|
367
|
+
const notifications = ref(true)
|
|
368
|
+
const themeOptions = reactive([
|
|
369
|
+
{ id: 'light', label: 'Light Theme', active: true },
|
|
370
|
+
{ id: 'dark', label: 'Dark Theme', active: false },
|
|
371
|
+
])
|
|
372
|
+
|
|
373
|
+
function saveSettings() {
|
|
374
|
+
showSettings.value = false
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function handleThemeChange(theme) {
|
|
378
|
+
themeOptions.forEach(item => {
|
|
379
|
+
item.active = item.id === theme.id
|
|
380
|
+
})
|
|
381
|
+
}
|
|
382
|
+
</script>
|
|
383
|
+
|
|
237
384
|
<template>
|
|
238
385
|
<ButtonPrimary @click="showSettings = true">Open Settings</ButtonPrimary>
|
|
239
|
-
|
|
386
|
+
|
|
387
|
+
<Modal :show="showSettings" @close="showSettings = false" md>
|
|
240
388
|
<template #header>User Settings</template>
|
|
389
|
+
|
|
241
390
|
<FormWrapperBuild @submit="saveSettings">
|
|
242
|
-
<FormFieldText v-model="userName"
|
|
391
|
+
<FormFieldText v-model="userName">
|
|
392
|
+
<template #label>Display Name</template>
|
|
393
|
+
</FormFieldText>
|
|
394
|
+
|
|
243
395
|
<Toggle v-model="notifications">
|
|
244
396
|
<template #label>Enable notifications</template>
|
|
245
397
|
</Toggle>
|
|
398
|
+
|
|
246
399
|
<Switcher :items="themeOptions" @change="handleThemeChange" />
|
|
247
400
|
</FormWrapperBuild>
|
|
248
|
-
|
|
249
|
-
|
|
401
|
+
|
|
402
|
+
<template #footer="{ close }">
|
|
403
|
+
<ButtonSecondary @click="close">Cancel</ButtonSecondary>
|
|
250
404
|
<ButtonPrimary type="submit">Save Changes</ButtonPrimary>
|
|
251
405
|
</template>
|
|
252
406
|
</Modal>
|
|
253
407
|
</template>
|
|
254
|
-
|
|
255
|
-
<script setup>
|
|
256
|
-
const themeOptions = [
|
|
257
|
-
{ id: 'light', label: 'Light Theme' },
|
|
258
|
-
{ id: 'dark', label: 'Dark Theme' }
|
|
259
|
-
]
|
|
260
|
-
</script>
|
|
261
408
|
```
|
|
262
409
|
|
|
263
410
|
### Action Menu
|
|
264
411
|
```vue
|
|
412
|
+
<script setup>
|
|
413
|
+
function handleEdit() {}
|
|
414
|
+
function handleDuplicate() {}
|
|
415
|
+
function handleDelete() {}
|
|
416
|
+
</script>
|
|
417
|
+
|
|
265
418
|
<template>
|
|
266
419
|
<DropdownMenu>
|
|
267
|
-
<template #trigger
|
|
420
|
+
<template #trigger="{ open }">
|
|
421
|
+
<ButtonTertiary @click="open">Actions</ButtonTertiary>
|
|
422
|
+
</template>
|
|
423
|
+
|
|
268
424
|
<DropdownOption @click="handleEdit">Edit</DropdownOption>
|
|
269
425
|
<DropdownOption @click="handleDuplicate">Duplicate</DropdownOption>
|
|
270
|
-
<DropdownOption @click="handleDelete"
|
|
426
|
+
<DropdownOption @click="handleDelete">Delete</DropdownOption>
|
|
271
427
|
</DropdownMenu>
|
|
272
428
|
</template>
|
|
273
429
|
```
|
|
274
430
|
|
|
275
431
|
### Multi-step Form
|
|
276
432
|
```vue
|
|
433
|
+
<script setup>
|
|
434
|
+
import { reactive, ref } from 'vue'
|
|
435
|
+
|
|
436
|
+
const steps = reactive([
|
|
437
|
+
{ label: 'Details', active: true, completed: false },
|
|
438
|
+
{ label: 'Content', active: false, completed: false },
|
|
439
|
+
{ label: 'Review', active: false, completed: false },
|
|
440
|
+
])
|
|
441
|
+
|
|
442
|
+
const activeStepIndex = ref(0)
|
|
443
|
+
|
|
444
|
+
function goToPrevious() {
|
|
445
|
+
if (activeStepIndex.value === 0) return
|
|
446
|
+
steps[activeStepIndex.value].active = false
|
|
447
|
+
activeStepIndex.value--
|
|
448
|
+
steps[activeStepIndex.value].active = true
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function goToNext() {
|
|
452
|
+
if (activeStepIndex.value === steps.length - 1) return
|
|
453
|
+
steps[activeStepIndex.value].active = false
|
|
454
|
+
steps[activeStepIndex.value].completed = true
|
|
455
|
+
activeStepIndex.value++
|
|
456
|
+
steps[activeStepIndex.value].active = true
|
|
457
|
+
}
|
|
458
|
+
</script>
|
|
459
|
+
|
|
277
460
|
<template>
|
|
278
|
-
<
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
<div class="form-content">
|
|
290
|
-
<!-- Step content here -->
|
|
291
|
-
</div>
|
|
292
|
-
|
|
293
|
-
<div class="form-actions">
|
|
294
|
-
<ButtonSecondary
|
|
295
|
-
v-if="currentStep > 1"
|
|
296
|
-
@click="previousStep"
|
|
297
|
-
>
|
|
298
|
-
Previous
|
|
299
|
-
</ButtonSecondary>
|
|
300
|
-
<ButtonPrimary @click="nextStep">
|
|
301
|
-
{{ currentStep === 3 ? 'Submit' : 'Next' }}
|
|
302
|
-
</ButtonPrimary>
|
|
303
|
-
</div>
|
|
304
|
-
</div>
|
|
461
|
+
<ProgressTracker :steps="steps" />
|
|
462
|
+
|
|
463
|
+
<ButtonSecondary :disabled="activeStepIndex === 0" @click="goToPrevious">
|
|
464
|
+
Previous
|
|
465
|
+
</ButtonSecondary>
|
|
466
|
+
<ButtonPrimary
|
|
467
|
+
:disabled="activeStepIndex === steps.length - 1"
|
|
468
|
+
@click="goToNext"
|
|
469
|
+
>
|
|
470
|
+
Next
|
|
471
|
+
</ButtonPrimary>
|
|
305
472
|
</template>
|
|
306
473
|
```
|
|
307
474
|
|
|
308
475
|
## Accessibility
|
|
309
476
|
|
|
310
|
-
- All interactive components are keyboard accessible
|
|
311
|
-
-
|
|
312
|
-
-
|
|
313
|
-
-
|
|
314
|
-
-
|
|
315
|
-
- Touch targets are appropriately sized for mobile devices
|
|
477
|
+
- All interactive components are keyboard accessible by default; ensure custom slot content keeps focus order intact.
|
|
478
|
+
- Provide descriptive labels or aria attributes when replacing default triggers or buttons.
|
|
479
|
+
- Manage focus when opening and closing modals or disclosures to avoid trapping keyboard users.
|
|
480
|
+
- Ensure sufficient color contrast when composing custom content.
|
|
481
|
+
- Keep touch targets at least 44px in height for comfortable mobile interactions.
|
package/index.js
CHANGED