@umbra.ui/core 0.1.18 → 0.1.19
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/dist/components/controls/Button/Button.vue +417 -0
- package/dist/components/controls/Button/README.md +348 -0
- package/dist/components/controls/Button/theme.css +200 -0
- package/dist/components/controls/Checkbox/Checkbox.vue +164 -0
- package/dist/components/controls/Checkbox/README.md +441 -0
- package/dist/components/controls/Checkbox/theme.css +36 -0
- package/dist/components/controls/Dropdown/Dropdown.vue +476 -0
- package/dist/components/controls/Dropdown/README.md +370 -0
- package/dist/components/controls/Dropdown/theme.css +50 -0
- package/dist/components/controls/Dropdown/types.ts +6 -0
- package/dist/components/controls/IconButton/IconButton.vue +267 -0
- package/dist/components/controls/IconButton/README.md +502 -0
- package/dist/components/controls/IconButton/theme.css +89 -0
- package/dist/components/controls/Radio/README.md +591 -0
- package/dist/components/controls/Radio/Radio.vue +89 -0
- package/dist/components/controls/Radio/theme.css +14 -0
- package/dist/components/controls/RangeSlider/README.md +608 -0
- package/dist/components/controls/RangeSlider/RangeSlider.vue +535 -0
- package/dist/components/controls/RangeSlider/theme.css +80 -0
- package/dist/components/controls/SegmentedControl/README.md +587 -0
- package/dist/components/controls/SegmentedControl/SegmentedControl.vue +284 -0
- package/dist/components/controls/SegmentedControl/theme.css +60 -0
- package/dist/components/controls/SegmentedControl/types.ts +5 -0
- package/dist/components/controls/Slider/README.md +627 -0
- package/dist/components/controls/Slider/Slider.vue +260 -0
- package/dist/components/controls/Slider/theme.css +74 -0
- package/dist/components/controls/Stepper/README.md +601 -0
- package/dist/components/controls/Stepper/Stepper.vue +103 -0
- package/dist/components/controls/Stepper/theme.css +53 -0
- package/dist/components/controls/Switch/README.md +667 -0
- package/dist/components/controls/Switch/Switch.vue +127 -0
- package/dist/components/controls/Switch/theme.css +42 -0
- package/dist/components/dialogs/Alert/Alert.vue +218 -0
- package/dist/components/dialogs/Alert/README.md +450 -0
- package/dist/components/dialogs/Alert/theme.css +44 -0
- package/dist/components/dialogs/Alert/types.ts +11 -0
- package/dist/components/dialogs/Toast/README.md +522 -0
- package/dist/components/dialogs/Toast/Toast.vue +296 -0
- package/dist/components/dialogs/Toast/ToastContainer.vue +330 -0
- package/dist/components/dialogs/Toast/theme.css +44 -0
- package/dist/components/dialogs/Toast/types.ts +46 -0
- package/dist/components/dialogs/Toast/useToast.ts +127 -0
- package/dist/components/indicators/ProgressBar/ProgressBar.vue +98 -0
- package/dist/components/indicators/ProgressBar/README.md +744 -0
- package/dist/components/indicators/ProgressBar/theme.css +36 -0
- package/dist/components/indicators/Tooltip/README.md +723 -0
- package/dist/components/indicators/Tooltip/TooltipProvider.vue +142 -0
- package/dist/components/indicators/Tooltip/theme.css +18 -0
- package/dist/components/indicators/Tooltip/tooltip.ts +48 -0
- package/dist/components/indicators/Tooltip/types.ts +15 -0
- package/dist/components/indicators/Tooltip/useTooltip.ts +71 -0
- package/dist/components/inputs/AutogrowTextView/AutogrowTextView.vue +110 -0
- package/dist/components/inputs/AutogrowTextView/README.md +643 -0
- package/dist/components/inputs/AutogrowTextView/theme.css +28 -0
- package/dist/components/inputs/InputCard/InputCard.vue +600 -0
- package/dist/components/inputs/InputCard/README.md +636 -0
- package/dist/components/inputs/InputEmail/InputEmail.vue +698 -0
- package/dist/components/inputs/InputEmail/README.md +764 -0
- package/dist/components/inputs/InputNumber/InputNumber.vue +300 -0
- package/dist/components/inputs/InputNumber/README.md +749 -0
- package/dist/components/inputs/InputPhone/InputPhone.vue +645 -0
- package/dist/components/inputs/InputPhone/README.md +636 -0
- package/dist/components/inputs/InputSecure/InputSecure.vue +646 -0
- package/dist/components/inputs/InputSecure/README.md +771 -0
- package/dist/components/inputs/InputText/InputText.vue +225 -0
- package/dist/components/inputs/InputText/README.md +844 -0
- package/dist/components/inputs/OTP/OTP.vue +349 -0
- package/dist/components/inputs/OTP/README.md +736 -0
- package/dist/components/inputs/OTP/theme.css +50 -0
- package/dist/components/inputs/StringCapture/README.md +718 -0
- package/dist/components/inputs/StringCapture/StringCapture.vue +315 -0
- package/dist/components/inputs/StringCapture/theme.css +86 -0
- package/dist/components/inputs/Tags/README.md +897 -0
- package/dist/components/inputs/Tags/TagBar.vue +793 -0
- package/dist/components/inputs/Tags/TagCreation.vue +219 -0
- package/dist/components/inputs/Tags/TagPicker.vue +380 -0
- package/dist/components/inputs/Tags/tag-bar-styles.ts +354 -0
- package/dist/components/inputs/Tags/theme.css +121 -0
- package/dist/components/inputs/Tags/types.ts +346 -0
- package/dist/components/inputs/search/README.md +759 -0
- package/dist/components/inputs/search/SearchBar.vue +394 -0
- package/dist/components/inputs/search/SearchResults.vue +310 -0
- package/dist/components/inputs/search/theme.css +187 -0
- package/dist/components/inputs/search/types.ts +8 -0
- package/dist/components/inputs/theme.css +102 -0
- package/dist/components/menus/ActionMenu/ActionMenu.vue +383 -0
- package/dist/components/menus/ActionMenu/README.md +825 -0
- package/dist/components/menus/ActionMenu/theme.css +93 -0
- package/dist/components/models/Popover/Popover.vue +551 -0
- package/dist/components/models/Popover/README.md +885 -0
- package/dist/components/models/Popover/theme.css +52 -0
- package/dist/components/models/Sheet/README.md +1159 -0
- package/dist/components/models/Sheet/Sheet.vue +465 -0
- package/dist/components/models/Sheet/theme.css +72 -0
- package/dist/components/models/Sidebar/README.md +1228 -0
- package/dist/components/models/Sidebar/Sidebar.vue +480 -0
- package/dist/components/models/Sidebar/theme.css +90 -0
- package/dist/components/navigation/adaptive/AdaptiveLayout.vue +779 -0
- package/dist/components/navigation/adaptive/AdaptiveLayoutBreadcrumbs.vue +192 -0
- package/dist/components/navigation/adaptive/AdaptiveLayoutMenuButton.vue +149 -0
- package/dist/components/navigation/adaptive/README.md +768 -0
- package/dist/components/navigation/adaptive/types.ts +19 -0
- package/dist/components/navigation/adaptive/useAdaptiveLayout.ts +89 -0
- package/dist/components/navigation/adaptive/useBreakpoints.ts +41 -0
- package/dist/components/navigation/adaptive/useContainerMonitor.ts +214 -0
- package/dist/components/navigation/adaptive/useViewAnimation.ts +721 -0
- package/dist/components/navigation/adaptive/useViewResize.ts +211 -0
- package/dist/components/navigation/navstack/NavigationStack.vue +180 -0
- package/dist/components/navigation/navstack/README.md +994 -0
- package/dist/components/navigation/navstack/useNavigationStack.ts +164 -0
- package/dist/components/navigation/slideover/README.md +1275 -0
- package/dist/components/navigation/slideover/SlideoverController.vue +287 -0
- package/dist/components/navigation/slideover/useSlideoverController.ts +320 -0
- package/dist/components/navigation/splitview/README.md +1115 -0
- package/dist/components/navigation/splitview/SplitViewController.vue +176 -0
- package/dist/components/navigation/splitview/useSplitViewController.ts +388 -0
- package/dist/components/navigation/tabcontroller/README.md +919 -0
- package/dist/components/navigation/tabcontroller/TabController.vue +307 -0
- package/dist/components/navigation/tabcontroller/TabItem.vue +57 -0
- package/dist/components/navigation/tabcontroller/types.ts +24 -0
- package/dist/components/navigation/tabcontroller/useTabController.ts +18 -0
- package/dist/components/navigation/theme.css +91 -0
- package/dist/components/navigation/types.ts +7 -0
- package/dist/components/pickers/CollectionPicker/CollectionPicker.vue +398 -0
- package/dist/components/pickers/CollectionPicker/README.md +1115 -0
- package/dist/components/pickers/CollectionPicker/theme.css +14 -0
- package/dist/components/pickers/CollectionPicker/types.ts +11 -0
- package/dist/components/pickers/ColorPicker/ColorPicker.vue +376 -0
- package/dist/components/pickers/ColorPicker/README.md +1439 -0
- package/dist/components/pickers/ColorPicker/colors.ts +299 -0
- package/dist/components/pickers/ColorPicker/theme.css +32 -0
- package/dist/components/pickers/DatePicker/DatePicker.vue +660 -0
- package/dist/components/pickers/DatePicker/README.md +1195 -0
- package/dist/components/pickers/DatePicker/theme.css +22 -0
- package/dist/components/pickers/FilePicker/FilePicker.vue +534 -0
- package/dist/components/pickers/FilePicker/README.md +1542 -0
- package/dist/components/pickers/FilePicker/theme.css +48 -0
- package/dist/components/pickers/FilePicker/types.ts +10 -0
- package/dist/components/pickers/IconPicker/IconPicker.vue +327 -0
- package/dist/components/pickers/IconPicker/README.md +1161 -0
- package/dist/components/pickers/IconPicker/theme.css +28 -0
- package/dist/components/pickers/theme.css +82 -0
- package/dist/components/views/MarkdownViewer/MarkdownViewer.vue +442 -0
- package/dist/components/views/MarkdownViewer/README.md +833 -0
- package/dist/components/views/MarkdownViewer/theme.css +130 -0
- package/package.json +3 -2
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
# Radio
|
|
2
|
+
|
|
3
|
+
A radio button group component built with Vue 3 Composition API and TypeScript. The Radio component provides a set of mutually exclusive options with customizable layouts and visual styles.
|
|
4
|
+
|
|
5
|
+
## Installation/Import
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { Radio } from "@umbra-ui/core";
|
|
9
|
+
import type { ControlItem } from "@umbra-ui/core";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**Dependencies:**
|
|
13
|
+
|
|
14
|
+
- Vue 3.x
|
|
15
|
+
- @umbra-ui/core (Checkbox component)
|
|
16
|
+
|
|
17
|
+
## Basic Usage
|
|
18
|
+
|
|
19
|
+
```vue
|
|
20
|
+
<script setup lang="ts">
|
|
21
|
+
import { ref } from "vue";
|
|
22
|
+
import { Radio } from "@umbra-ui/core";
|
|
23
|
+
import type { ControlItem } from "@umbra-ui/core";
|
|
24
|
+
|
|
25
|
+
const selectedIndex = ref(0);
|
|
26
|
+
|
|
27
|
+
const radioItems: ControlItem[] = [
|
|
28
|
+
{ title: "Option 1" },
|
|
29
|
+
{ title: "Option 2" },
|
|
30
|
+
{ title: "Option 3" },
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const handleRadioClick = (index: number, item: ControlItem) => {
|
|
34
|
+
console.log("Selected:", item.title, "at index:", index);
|
|
35
|
+
selectedIndex.value = index;
|
|
36
|
+
};
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<Radio
|
|
41
|
+
:items="radioItems"
|
|
42
|
+
:selected-index="selectedIndex"
|
|
43
|
+
@click="handleRadioClick"
|
|
44
|
+
/>
|
|
45
|
+
</template>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Props
|
|
49
|
+
|
|
50
|
+
| Prop Name | Type | Required | Default | Description |
|
|
51
|
+
| --------------- | --------------------------------- | -------- | ---------------------------------------------------------- | -------------------------------------- |
|
|
52
|
+
| `items` | `ControlItem[]` | Yes | `[{ title: "One" }, { title: "Two" }, { title: "Three" }]` | Array of radio options to display |
|
|
53
|
+
| `selectedIndex` | `number` | Yes | `0` | Index of the currently selected option |
|
|
54
|
+
| `checkboxType` | `"circle" \| "square" \| "plain"` | No | `"circle"` | Visual style of the radio buttons |
|
|
55
|
+
| `layout` | `"horizontal" \| "vertical"` | No | `"horizontal"` | Layout direction of the radio group |
|
|
56
|
+
|
|
57
|
+
### ControlItem Interface
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
interface ControlItem {
|
|
61
|
+
title: string;
|
|
62
|
+
subtitle?: string;
|
|
63
|
+
disabled?: boolean;
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Checkbox Types
|
|
68
|
+
|
|
69
|
+
- **`circle`**: Circular radio buttons (default)
|
|
70
|
+
- **`square`**: Square radio buttons with rounded corners
|
|
71
|
+
- **`plain`**: Minimal radio buttons with no background
|
|
72
|
+
|
|
73
|
+
### Layout Options
|
|
74
|
+
|
|
75
|
+
- **`horizontal`**: Radio buttons arranged in a row
|
|
76
|
+
- **`vertical`**: Radio buttons arranged in a column
|
|
77
|
+
|
|
78
|
+
## Events
|
|
79
|
+
|
|
80
|
+
| Event Name | Payload Type | Description |
|
|
81
|
+
| ---------- | ------------------------------------ | --------------------------------------- |
|
|
82
|
+
| `click` | `[index: number, item: ControlItem]` | Emitted when a radio option is selected |
|
|
83
|
+
|
|
84
|
+
### Event Examples
|
|
85
|
+
|
|
86
|
+
```vue
|
|
87
|
+
<script setup lang="ts">
|
|
88
|
+
import { ref } from "vue";
|
|
89
|
+
import { Radio } from "@umbra-ui/core";
|
|
90
|
+
import type { ControlItem } from "@umbra-ui/core";
|
|
91
|
+
|
|
92
|
+
const selectedIndex = ref(0);
|
|
93
|
+
|
|
94
|
+
const handleRadioClick = (index: number, item: ControlItem) => {
|
|
95
|
+
console.log("Selected option:", item.title);
|
|
96
|
+
console.log("Selected index:", index);
|
|
97
|
+
selectedIndex.value = index;
|
|
98
|
+
};
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<template>
|
|
102
|
+
<Radio
|
|
103
|
+
:items="radioItems"
|
|
104
|
+
:selected-index="selectedIndex"
|
|
105
|
+
@click="handleRadioClick"
|
|
106
|
+
/>
|
|
107
|
+
</template>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Slots
|
|
111
|
+
|
|
112
|
+
This component does not use slots. All content is controlled via props.
|
|
113
|
+
|
|
114
|
+
## Exposed Methods/Refs
|
|
115
|
+
|
|
116
|
+
This component does not expose any methods or refs via `defineExpose`.
|
|
117
|
+
|
|
118
|
+
## CSS Customization
|
|
119
|
+
|
|
120
|
+
The Radio component uses CSS custom properties that can be overridden to customize the appearance:
|
|
121
|
+
|
|
122
|
+
### Color Variables
|
|
123
|
+
|
|
124
|
+
```css
|
|
125
|
+
/* Radio item colors */
|
|
126
|
+
--radio-item-color: #8b8d98;
|
|
127
|
+
--radio-item-selected-color: #1a1d23;
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Custom Styling Example
|
|
131
|
+
|
|
132
|
+
```vue
|
|
133
|
+
<template>
|
|
134
|
+
<Radio :items="items" :selected-index="selectedIndex" class="custom-radio" />
|
|
135
|
+
</template>
|
|
136
|
+
|
|
137
|
+
<style>
|
|
138
|
+
.custom-radio {
|
|
139
|
+
--radio-item-color: #666;
|
|
140
|
+
--radio-item-selected-color: #ff6b6b;
|
|
141
|
+
}
|
|
142
|
+
</style>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Examples
|
|
146
|
+
|
|
147
|
+
### Basic Radio Group
|
|
148
|
+
|
|
149
|
+
```vue
|
|
150
|
+
<script setup lang="ts">
|
|
151
|
+
import { ref } from "vue";
|
|
152
|
+
import { Radio } from "@umbra-ui/core";
|
|
153
|
+
import type { ControlItem } from "@umbra-ui/core";
|
|
154
|
+
|
|
155
|
+
const selectedIndex = ref(0);
|
|
156
|
+
|
|
157
|
+
const options: ControlItem[] = [
|
|
158
|
+
{ title: "Small" },
|
|
159
|
+
{ title: "Medium" },
|
|
160
|
+
{ title: "Large" },
|
|
161
|
+
];
|
|
162
|
+
</script>
|
|
163
|
+
|
|
164
|
+
<template>
|
|
165
|
+
<Radio
|
|
166
|
+
:items="options"
|
|
167
|
+
:selected-index="selectedIndex"
|
|
168
|
+
@click="(index) => (selectedIndex = index)"
|
|
169
|
+
/>
|
|
170
|
+
</template>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Different Layouts
|
|
174
|
+
|
|
175
|
+
```vue
|
|
176
|
+
<script setup lang="ts">
|
|
177
|
+
import { ref } from "vue";
|
|
178
|
+
import { Radio } from "@umbra-ui/core";
|
|
179
|
+
import type { ControlItem } from "@umbra-ui/core";
|
|
180
|
+
|
|
181
|
+
const horizontalSelected = ref(0);
|
|
182
|
+
const verticalSelected = ref(0);
|
|
183
|
+
|
|
184
|
+
const options: ControlItem[] = [
|
|
185
|
+
{ title: "Option 1" },
|
|
186
|
+
{ title: "Option 2" },
|
|
187
|
+
{ title: "Option 3" },
|
|
188
|
+
];
|
|
189
|
+
</script>
|
|
190
|
+
|
|
191
|
+
<template>
|
|
192
|
+
<div class="layout-examples">
|
|
193
|
+
<div class="example">
|
|
194
|
+
<h3>Horizontal Layout</h3>
|
|
195
|
+
<Radio
|
|
196
|
+
:items="options"
|
|
197
|
+
:selected-index="horizontalSelected"
|
|
198
|
+
layout="horizontal"
|
|
199
|
+
@click="(index) => (horizontalSelected = index)"
|
|
200
|
+
/>
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<div class="example">
|
|
204
|
+
<h3>Vertical Layout</h3>
|
|
205
|
+
<Radio
|
|
206
|
+
:items="options"
|
|
207
|
+
:selected-index="verticalSelected"
|
|
208
|
+
layout="vertical"
|
|
209
|
+
@click="(index) => (verticalSelected = index)"
|
|
210
|
+
/>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</template>
|
|
214
|
+
|
|
215
|
+
<style module>
|
|
216
|
+
.layout-examples {
|
|
217
|
+
display: flex;
|
|
218
|
+
flex-direction: column;
|
|
219
|
+
gap: 2rem;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.example {
|
|
223
|
+
display: flex;
|
|
224
|
+
flex-direction: column;
|
|
225
|
+
gap: 1rem;
|
|
226
|
+
}
|
|
227
|
+
</style>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Different Checkbox Types
|
|
231
|
+
|
|
232
|
+
```vue
|
|
233
|
+
<script setup lang="ts">
|
|
234
|
+
import { ref } from "vue";
|
|
235
|
+
import { Radio } from "@umbra-ui/core";
|
|
236
|
+
import type { ControlItem } from "@umbra-ui/core";
|
|
237
|
+
|
|
238
|
+
const circleSelected = ref(0);
|
|
239
|
+
const squareSelected = ref(0);
|
|
240
|
+
const plainSelected = ref(0);
|
|
241
|
+
|
|
242
|
+
const options: ControlItem[] = [
|
|
243
|
+
{ title: "Circle" },
|
|
244
|
+
{ title: "Square" },
|
|
245
|
+
{ title: "Plain" },
|
|
246
|
+
];
|
|
247
|
+
</script>
|
|
248
|
+
|
|
249
|
+
<template>
|
|
250
|
+
<div class="type-examples">
|
|
251
|
+
<div class="example">
|
|
252
|
+
<h3>Circle Type</h3>
|
|
253
|
+
<Radio
|
|
254
|
+
:items="options"
|
|
255
|
+
:selected-index="circleSelected"
|
|
256
|
+
checkbox-type="circle"
|
|
257
|
+
@click="(index) => (circleSelected = index)"
|
|
258
|
+
/>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<div class="example">
|
|
262
|
+
<h3>Square Type</h3>
|
|
263
|
+
<Radio
|
|
264
|
+
:items="options"
|
|
265
|
+
:selected-index="squareSelected"
|
|
266
|
+
checkbox-type="square"
|
|
267
|
+
@click="(index) => (squareSelected = index)"
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
<div class="example">
|
|
272
|
+
<h3>Plain Type</h3>
|
|
273
|
+
<Radio
|
|
274
|
+
:items="options"
|
|
275
|
+
:selected-index="plainSelected"
|
|
276
|
+
checkbox-type="plain"
|
|
277
|
+
@click="(index) => (plainSelected = index)"
|
|
278
|
+
/>
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
</template>
|
|
282
|
+
|
|
283
|
+
<style module>
|
|
284
|
+
.type-examples {
|
|
285
|
+
display: flex;
|
|
286
|
+
flex-direction: column;
|
|
287
|
+
gap: 2rem;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.example {
|
|
291
|
+
display: flex;
|
|
292
|
+
flex-direction: column;
|
|
293
|
+
gap: 1rem;
|
|
294
|
+
}
|
|
295
|
+
</style>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Form Integration
|
|
299
|
+
|
|
300
|
+
```vue
|
|
301
|
+
<script setup lang="ts">
|
|
302
|
+
import { ref } from "vue";
|
|
303
|
+
import { Radio } from "@umbra-ui/core";
|
|
304
|
+
import type { ControlItem } from "@umbra-ui/core";
|
|
305
|
+
|
|
306
|
+
interface FormData {
|
|
307
|
+
size: number;
|
|
308
|
+
color: number;
|
|
309
|
+
material: number;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const formData = ref<FormData>({
|
|
313
|
+
size: 0,
|
|
314
|
+
color: 0,
|
|
315
|
+
material: 0,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
const sizes: ControlItem[] = [
|
|
319
|
+
{ title: "Small" },
|
|
320
|
+
{ title: "Medium" },
|
|
321
|
+
{ title: "Large" },
|
|
322
|
+
{ title: "Extra Large" },
|
|
323
|
+
];
|
|
324
|
+
|
|
325
|
+
const colors: ControlItem[] = [
|
|
326
|
+
{ title: "Red" },
|
|
327
|
+
{ title: "Blue" },
|
|
328
|
+
{ title: "Green" },
|
|
329
|
+
{ title: "Yellow" },
|
|
330
|
+
];
|
|
331
|
+
|
|
332
|
+
const materials: ControlItem[] = [
|
|
333
|
+
{ title: "Cotton" },
|
|
334
|
+
{ title: "Polyester" },
|
|
335
|
+
{ title: "Wool" },
|
|
336
|
+
{ title: "Silk" },
|
|
337
|
+
];
|
|
338
|
+
|
|
339
|
+
const submitForm = () => {
|
|
340
|
+
console.log("Form submitted:", formData.value);
|
|
341
|
+
};
|
|
342
|
+
</script>
|
|
343
|
+
|
|
344
|
+
<template>
|
|
345
|
+
<form class="form-example" @submit.prevent="submitForm">
|
|
346
|
+
<div class="form-group">
|
|
347
|
+
<label>Size</label>
|
|
348
|
+
<Radio
|
|
349
|
+
:items="sizes"
|
|
350
|
+
:selected-index="formData.size"
|
|
351
|
+
@click="(index) => (formData.size = index)"
|
|
352
|
+
/>
|
|
353
|
+
</div>
|
|
354
|
+
|
|
355
|
+
<div class="form-group">
|
|
356
|
+
<label>Color</label>
|
|
357
|
+
<Radio
|
|
358
|
+
:items="colors"
|
|
359
|
+
:selected-index="formData.color"
|
|
360
|
+
@click="(index) => (formData.color = index)"
|
|
361
|
+
/>
|
|
362
|
+
</div>
|
|
363
|
+
|
|
364
|
+
<div class="form-group">
|
|
365
|
+
<label>Material</label>
|
|
366
|
+
<Radio
|
|
367
|
+
:items="materials"
|
|
368
|
+
:selected-index="formData.material"
|
|
369
|
+
@click="(index) => (formData.material = index)"
|
|
370
|
+
/>
|
|
371
|
+
</div>
|
|
372
|
+
|
|
373
|
+
<button type="submit">Submit</button>
|
|
374
|
+
</form>
|
|
375
|
+
</template>
|
|
376
|
+
|
|
377
|
+
<style module>
|
|
378
|
+
.form-example {
|
|
379
|
+
display: flex;
|
|
380
|
+
flex-direction: column;
|
|
381
|
+
gap: 2rem;
|
|
382
|
+
max-width: 400px;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.form-group {
|
|
386
|
+
display: flex;
|
|
387
|
+
flex-direction: column;
|
|
388
|
+
gap: 1rem;
|
|
389
|
+
}
|
|
390
|
+
</style>
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Settings Panel
|
|
394
|
+
|
|
395
|
+
```vue
|
|
396
|
+
<script setup lang="ts">
|
|
397
|
+
import { ref } from "vue";
|
|
398
|
+
import { Radio } from "@umbra-ui/core";
|
|
399
|
+
import type { ControlItem } from "@umbra-ui/core";
|
|
400
|
+
|
|
401
|
+
const themeSelected = ref(0);
|
|
402
|
+
const languageSelected = ref(0);
|
|
403
|
+
const notificationsSelected = ref(0);
|
|
404
|
+
|
|
405
|
+
const themes: ControlItem[] = [
|
|
406
|
+
{ title: "Light" },
|
|
407
|
+
{ title: "Dark" },
|
|
408
|
+
{ title: "Auto" },
|
|
409
|
+
];
|
|
410
|
+
|
|
411
|
+
const languages: ControlItem[] = [
|
|
412
|
+
{ title: "English" },
|
|
413
|
+
{ title: "Spanish" },
|
|
414
|
+
{ title: "French" },
|
|
415
|
+
{ title: "German" },
|
|
416
|
+
];
|
|
417
|
+
|
|
418
|
+
const notifications: ControlItem[] = [
|
|
419
|
+
{ title: "All" },
|
|
420
|
+
{ title: "Important only" },
|
|
421
|
+
{ title: "None" },
|
|
422
|
+
];
|
|
423
|
+
|
|
424
|
+
const saveSettings = () => {
|
|
425
|
+
const settings = {
|
|
426
|
+
theme: themes[themeSelected.value].title,
|
|
427
|
+
language: languages[languageSelected.value].title,
|
|
428
|
+
notifications: notifications[notificationsSelected.value].title,
|
|
429
|
+
};
|
|
430
|
+
console.log("Settings saved:", settings);
|
|
431
|
+
};
|
|
432
|
+
</script>
|
|
433
|
+
|
|
434
|
+
<template>
|
|
435
|
+
<div class="settings-panel">
|
|
436
|
+
<h2>Settings</h2>
|
|
437
|
+
|
|
438
|
+
<div class="setting-group">
|
|
439
|
+
<h3>Theme</h3>
|
|
440
|
+
<Radio
|
|
441
|
+
:items="themes"
|
|
442
|
+
:selected-index="themeSelected"
|
|
443
|
+
@click="(index) => (themeSelected = index)"
|
|
444
|
+
/>
|
|
445
|
+
</div>
|
|
446
|
+
|
|
447
|
+
<div class="setting-group">
|
|
448
|
+
<h3>Language</h3>
|
|
449
|
+
<Radio
|
|
450
|
+
:items="languages"
|
|
451
|
+
:selected-index="languageSelected"
|
|
452
|
+
@click="(index) => (languageSelected = index)"
|
|
453
|
+
/>
|
|
454
|
+
</div>
|
|
455
|
+
|
|
456
|
+
<div class="setting-group">
|
|
457
|
+
<h3>Notifications</h3>
|
|
458
|
+
<Radio
|
|
459
|
+
:items="notifications"
|
|
460
|
+
:selected-index="notificationsSelected"
|
|
461
|
+
@click="(index) => (notificationsSelected = index)"
|
|
462
|
+
/>
|
|
463
|
+
</div>
|
|
464
|
+
|
|
465
|
+
<button @click="saveSettings">Save Settings</button>
|
|
466
|
+
</div>
|
|
467
|
+
</template>
|
|
468
|
+
|
|
469
|
+
<style module>
|
|
470
|
+
.settings-panel {
|
|
471
|
+
display: flex;
|
|
472
|
+
flex-direction: column;
|
|
473
|
+
gap: 2rem;
|
|
474
|
+
max-width: 400px;
|
|
475
|
+
padding: 2rem;
|
|
476
|
+
background-color: #f5f5f5;
|
|
477
|
+
border-radius: 0.5rem;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.setting-group {
|
|
481
|
+
display: flex;
|
|
482
|
+
flex-direction: column;
|
|
483
|
+
gap: 1rem;
|
|
484
|
+
}
|
|
485
|
+
</style>
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### Dynamic Radio Groups
|
|
489
|
+
|
|
490
|
+
```vue
|
|
491
|
+
<script setup lang="ts">
|
|
492
|
+
import { ref, computed } from "vue";
|
|
493
|
+
import { Radio } from "@umbra-ui/core";
|
|
494
|
+
import type { ControlItem } from "@umbra-ui/core";
|
|
495
|
+
|
|
496
|
+
const selectedCategory = ref(0);
|
|
497
|
+
const selectedSubcategory = ref(0);
|
|
498
|
+
|
|
499
|
+
const categories: ControlItem[] = [
|
|
500
|
+
{ title: "Electronics" },
|
|
501
|
+
{ title: "Clothing" },
|
|
502
|
+
{ title: "Books" },
|
|
503
|
+
{ title: "Home & Garden" },
|
|
504
|
+
];
|
|
505
|
+
|
|
506
|
+
const subcategories = computed(() => {
|
|
507
|
+
const categoryMap: Record<number, ControlItem[]> = {
|
|
508
|
+
0: [
|
|
509
|
+
{ title: "Smartphones" },
|
|
510
|
+
{ title: "Laptops" },
|
|
511
|
+
{ title: "Tablets" },
|
|
512
|
+
{ title: "Accessories" },
|
|
513
|
+
],
|
|
514
|
+
1: [
|
|
515
|
+
{ title: "Men's Clothing" },
|
|
516
|
+
{ title: "Women's Clothing" },
|
|
517
|
+
{ title: "Kids' Clothing" },
|
|
518
|
+
{ title: "Shoes" },
|
|
519
|
+
],
|
|
520
|
+
2: [
|
|
521
|
+
{ title: "Fiction" },
|
|
522
|
+
{ title: "Non-fiction" },
|
|
523
|
+
{ title: "Textbooks" },
|
|
524
|
+
{ title: "Children's Books" },
|
|
525
|
+
],
|
|
526
|
+
3: [
|
|
527
|
+
{ title: "Furniture" },
|
|
528
|
+
{ title: "Decor" },
|
|
529
|
+
{ title: "Garden Tools" },
|
|
530
|
+
{ title: "Kitchen" },
|
|
531
|
+
],
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
return categoryMap[selectedCategory.value] || [];
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
const handleCategoryChange = (index: number) => {
|
|
538
|
+
selectedCategory.value = index;
|
|
539
|
+
selectedSubcategory.value = 0; // Reset subcategory selection
|
|
540
|
+
};
|
|
541
|
+
</script>
|
|
542
|
+
|
|
543
|
+
<template>
|
|
544
|
+
<div class="dynamic-radio">
|
|
545
|
+
<div class="radio-group">
|
|
546
|
+
<h3>Category</h3>
|
|
547
|
+
<Radio
|
|
548
|
+
:items="categories"
|
|
549
|
+
:selected-index="selectedCategory"
|
|
550
|
+
@click="handleCategoryChange"
|
|
551
|
+
/>
|
|
552
|
+
</div>
|
|
553
|
+
|
|
554
|
+
<div class="radio-group" v-if="subcategories.length > 0">
|
|
555
|
+
<h3>Subcategory</h3>
|
|
556
|
+
<Radio
|
|
557
|
+
:items="subcategories"
|
|
558
|
+
:selected-index="selectedSubcategory"
|
|
559
|
+
@click="(index) => (selectedSubcategory = index)"
|
|
560
|
+
/>
|
|
561
|
+
</div>
|
|
562
|
+
</div>
|
|
563
|
+
</template>
|
|
564
|
+
|
|
565
|
+
<style module>
|
|
566
|
+
.dynamic-radio {
|
|
567
|
+
display: flex;
|
|
568
|
+
flex-direction: column;
|
|
569
|
+
gap: 2rem;
|
|
570
|
+
max-width: 500px;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.radio-group {
|
|
574
|
+
display: flex;
|
|
575
|
+
flex-direction: column;
|
|
576
|
+
gap: 1rem;
|
|
577
|
+
}
|
|
578
|
+
</style>
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
## Notes
|
|
582
|
+
|
|
583
|
+
- The component uses the Checkbox component internally for the radio button appearance
|
|
584
|
+
- Only one option can be selected at a time (mutually exclusive)
|
|
585
|
+
- The component supports both light and dark themes
|
|
586
|
+
- Hover effects include a subtle scale transform for better user feedback
|
|
587
|
+
- Selected items have different text color styling
|
|
588
|
+
- The component automatically handles prop changes and updates the internal state
|
|
589
|
+
- Layout spacing is optimized for both horizontal and vertical arrangements
|
|
590
|
+
- The component is fully accessible with proper click handling
|
|
591
|
+
- Radio groups are commonly used in forms for single-choice selections
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, watch } from "vue";
|
|
3
|
+
import type { ControlItem } from "../SegmentedControl/types";
|
|
4
|
+
import Checkbox from "../Checkbox/Checkbox.vue";
|
|
5
|
+
import "./theme.css";
|
|
6
|
+
export interface Props {
|
|
7
|
+
items: ControlItem[];
|
|
8
|
+
selectedIndex: number;
|
|
9
|
+
checkboxType?: "circle" | "square" | "plain";
|
|
10
|
+
layout?: "horizontal" | "vertical";
|
|
11
|
+
}
|
|
12
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
13
|
+
items: () => [{ title: "One" }, { title: "Two" }, { title: "Three" }],
|
|
14
|
+
selectedIndex: 0,
|
|
15
|
+
checkboxType: "circle",
|
|
16
|
+
layout: "horizontal",
|
|
17
|
+
});
|
|
18
|
+
const emit = defineEmits<{
|
|
19
|
+
click: [index: number, item: ControlItem];
|
|
20
|
+
}>();
|
|
21
|
+
|
|
22
|
+
const handleClick = (index: number, item: ControlItem) => {
|
|
23
|
+
selectedIndex.value = index;
|
|
24
|
+
emit("click", index, item);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const selectedIndex = ref(props.selectedIndex);
|
|
28
|
+
|
|
29
|
+
// Watch for changes to selectedIndex prop
|
|
30
|
+
watch(
|
|
31
|
+
() => props.selectedIndex,
|
|
32
|
+
(newSelectedIndex) => {
|
|
33
|
+
selectedIndex.value = newSelectedIndex;
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<div :class="[$style.container, $style[`container_${layout}`]]">
|
|
40
|
+
<div
|
|
41
|
+
:class="$style.item"
|
|
42
|
+
v-for="(item, index) in items"
|
|
43
|
+
:key="index"
|
|
44
|
+
@click="handleClick(index, item)"
|
|
45
|
+
>
|
|
46
|
+
<Checkbox
|
|
47
|
+
:isChecked="selectedIndex === index"
|
|
48
|
+
@update:isChecked="selectedIndex = index"
|
|
49
|
+
:checkboxType="checkboxType"
|
|
50
|
+
/>
|
|
51
|
+
<p :class="['callout', selectedIndex === index && $style.selected]">
|
|
52
|
+
{{ item.title }}
|
|
53
|
+
</p>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<style module>
|
|
59
|
+
.container {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
cursor: default;
|
|
63
|
+
}
|
|
64
|
+
.container_horizontal {
|
|
65
|
+
flex-direction: row;
|
|
66
|
+
gap: 22px;
|
|
67
|
+
}
|
|
68
|
+
.container_vertical {
|
|
69
|
+
flex-direction: column;
|
|
70
|
+
gap: 15px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.item {
|
|
74
|
+
display: flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
gap: 12px;
|
|
77
|
+
color: var(--radio-item-color);
|
|
78
|
+
transition: transform 0.2s ease-in-out;
|
|
79
|
+
}
|
|
80
|
+
.item:hover {
|
|
81
|
+
transform: scale(1.03);
|
|
82
|
+
}
|
|
83
|
+
.item p {
|
|
84
|
+
transition: color 0.2s ease-in-out;
|
|
85
|
+
}
|
|
86
|
+
.selected {
|
|
87
|
+
color: var(--radio-item-selected-color);
|
|
88
|
+
}
|
|
89
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* Light theme using Colors */
|
|
2
|
+
:root {
|
|
3
|
+
/* Radio item colors */
|
|
4
|
+
--radio-item-color: #8b8d98; /* gray9 - muted text for light mode */
|
|
5
|
+
--radio-item-selected-color: #1a1d23; /* gray12 - dark text for selected state in light mode */
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* Dark theme */
|
|
9
|
+
.dark,
|
|
10
|
+
.dark-theme {
|
|
11
|
+
/* Radio item colors */
|
|
12
|
+
--radio-item-color: #b4b4b4; /* Original dark mode value */
|
|
13
|
+
--radio-item-selected-color: #eeeeee; /* Original dark mode value */
|
|
14
|
+
}
|