@sabrenski/spire-ui 0.0.6 → 0.0.7
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/index.d.ts +167 -4
- package/dist/spire-ui.css +1 -1
- package/dist/spire-ui.es.js +7005 -6741
- package/dist/spire-ui.umd.js +10 -10
- package/package.json +83 -70
- package/src/components/Accordion/AccordionContent.vue +5 -2
- package/src/components/Accordion/AccordionItem.vue +4 -0
- package/src/components/Accordion/AccordionRoot.vue +4 -2
- package/src/components/Accordion/AccordionTrigger.vue +4 -1
- package/src/components/Avatar/Avatar.vue +4 -0
- package/src/components/Badge/Badge.vue +4 -0
- package/src/components/BadgeContainer/BadgeContainer.vue +4 -1
- package/src/components/Breadcrumb/BreadcrumbLink.vue +4 -1
- package/src/components/Breadcrumb/BreadcrumbRoot.vue +4 -1
- package/src/components/Button/Button.vue +5 -1
- package/src/components/Callout/Callout.vue +4 -0
- package/src/components/Card/Card.vue +5 -1
- package/src/components/Card/CardContent.vue +5 -1
- package/src/components/Card/CardFooter.vue +5 -1
- package/src/components/Card/CardHeader.vue +5 -1
- package/src/components/Card/CardImage.vue +4 -2
- package/src/components/Chart/BarChart.vue +4 -0
- package/src/components/Chart/BaseChart.vue +52 -47
- package/src/components/Chart/DonutChart.vue +4 -2
- package/src/components/Chart/LineChart.vue +4 -0
- package/src/components/Checkbox/Checkbox.test.ts +94 -0
- package/src/components/Checkbox/Checkbox.vue +170 -1
- package/src/components/ChoiceChip/ChoiceChip.vue +11 -5
- package/src/components/ChoiceChipGroup/ChoiceChipGroup.vue +4 -2
- package/src/components/ColorPicker/ColorArea.vue +4 -2
- package/src/components/ColorPicker/ColorPicker.vue +4 -2
- package/src/components/ColorPicker/ColorSlider.vue +5 -1
- package/src/components/Combobox/Combobox.vue +97 -91
- package/src/components/DataTable/DataTable.vue +5 -1
- package/src/components/DatePicker/DatePicker.vue +5 -1
- package/src/components/Drawer/Drawer.vue +4 -1
- package/src/components/Dropdown/Dropdown.vue +4 -2
- package/src/components/Dropdown/DropdownItem.vue +4 -0
- package/src/components/Dropdown/DropdownSubTrigger.vue +4 -2
- package/src/components/EmptyState/EmptyState.vue +5 -1
- package/src/components/FileUpload/FileUpload.vue +12 -6
- package/src/components/Heading/Heading.vue +4 -0
- package/src/components/Icon/Icon.vue +5 -2
- package/src/components/Input/Input.vue +5 -1
- package/src/components/Layout/Container.vue +4 -0
- package/src/components/Layout/Grid.vue +4 -1
- package/src/components/Layout/GridItem.vue +4 -1
- package/src/components/Layout/Stack.vue +4 -0
- package/src/components/Modal/Modal.test.ts +68 -13
- package/src/components/Modal/Modal.vue +94 -91
- package/src/components/Pagination/Pagination.vue +5 -1
- package/src/components/Popover/Popover.vue +4 -1
- package/src/components/Progress/Progress.vue +5 -0
- package/src/components/Radio/Radio.test.ts +88 -0
- package/src/components/Radio/Radio.vue +169 -1
- package/src/components/Rating/Rating.vue +5 -1
- package/src/components/SegmentedControl/SegmentedControl.vue +5 -1
- package/src/components/Select/Select.vue +61 -55
- package/src/components/Sidebar/SidebarGroup.vue +4 -0
- package/src/components/Sidebar/SidebarItem.vue +4 -0
- package/src/components/Sidebar/SidebarLayout.vue +5 -2
- package/src/components/Sidebar/SidebarRoot.vue +4 -2
- package/src/components/Skeleton/Skeleton.vue +5 -1
- package/src/components/Slider/Slider.vue +5 -1
- package/src/components/Spinner/Spinner.vue +4 -1
- package/src/components/SpireProvider/SpireProvider.vue +4 -1
- package/src/components/Stepper/StepperItem.vue +4 -0
- package/src/components/Stepper/StepperRoot.vue +4 -2
- package/src/components/Stepper/StepperTrigger.vue +6 -2
- package/src/components/Switch/Switch.vue +5 -1
- package/src/components/Tabs/Tabs.vue +4 -1
- package/src/components/Text/Text.vue +4 -0
- package/src/components/Textarea/Textarea.vue +13 -7
- package/src/components/TimePicker/TimePicker.vue +5 -1
- package/src/components/Timeline/Timeline.vue +4 -0
- package/src/components/Timeline/TimelineItem.vue +4 -0
- package/src/components/Toast/ToastItem.vue +5 -1
- package/src/components/Toast/ToastProvider.vue +5 -3
- package/src/components/ToggleButton/ToggleButton.vue +5 -1
- package/src/components/ToggleGroup/ToggleGroup.vue +5 -1
- package/src/components/Tooltip/Tooltip.vue +9 -1
- package/src/components/TreeView/TreeView.vue +4 -1
- package/src/components/TreeView/TreeViewItem.vue +4 -0
- package/src/index.ts +3 -0
- package/src/styles/main.css +21 -21
- package/src/types/common.ts +4 -0
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
type TooltipModel
|
|
18
18
|
} from 'chart.js'
|
|
19
19
|
import { useChartTheme } from './useChartTheme'
|
|
20
|
+
import type { ClassValue } from '../../types/common'
|
|
20
21
|
|
|
21
22
|
Chart.register(...registerables)
|
|
22
23
|
|
|
@@ -57,6 +58,8 @@ export interface PointClickData {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
export interface BaseChartProps {
|
|
61
|
+
/** Additional CSS classes */
|
|
62
|
+
class?: ClassValue
|
|
60
63
|
/** Chart type (line, bar, doughnut, pie, etc.) */
|
|
61
64
|
type: ChartType
|
|
62
65
|
/** Chart.js data configuration */
|
|
@@ -281,7 +284,7 @@ defineExpose({
|
|
|
281
284
|
</script>
|
|
282
285
|
|
|
283
286
|
<template>
|
|
284
|
-
<div class="ui-chart" ref="containerRef">
|
|
287
|
+
<div :class="[props.class, 'ui-chart']" ref="containerRef">
|
|
285
288
|
<!-- Custom Legend Slot -->
|
|
286
289
|
<slot name="legend" :items="legendItems" :toggle="toggleDataset" />
|
|
287
290
|
|
|
@@ -387,58 +390,60 @@ defineExpose({
|
|
|
387
390
|
|
|
388
391
|
<!-- Tooltip styles must be unscoped because content is teleported to body -->
|
|
389
392
|
<style>
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
393
|
+
@layer spire-components {
|
|
394
|
+
.ui-chart__tooltip {
|
|
395
|
+
position: fixed;
|
|
396
|
+
z-index: var(--z-tooltip, 100);
|
|
397
|
+
padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem);
|
|
398
|
+
background-color: var(--chart-tooltip-bg, var(--tooltip-bg, #1c1917));
|
|
399
|
+
color: var(--chart-tooltip-text, var(--tooltip-text, #fafaf9));
|
|
400
|
+
border-radius: var(--radius-md, 0.375rem);
|
|
401
|
+
font-size: var(--text-sm, 0.875rem);
|
|
402
|
+
font-family: var(--font-sans, system-ui, -apple-system, sans-serif);
|
|
403
|
+
pointer-events: none;
|
|
404
|
+
transform: translate(-50%, calc(-100% - 8px));
|
|
405
|
+
box-shadow: var(--shadow-lg, 0 10px 15px -3px rgb(0 0 0 / 0.1));
|
|
406
|
+
min-width: 120px;
|
|
407
|
+
}
|
|
404
408
|
|
|
405
|
-
.ui-chart__tooltip-title {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
409
|
+
.ui-chart__tooltip-title {
|
|
410
|
+
font-weight: var(--font-medium, 500);
|
|
411
|
+
margin-bottom: var(--space-1, 0.25rem);
|
|
412
|
+
padding-bottom: var(--space-1, 0.25rem);
|
|
413
|
+
border-bottom: 1px solid var(--chart-tooltip-border, rgba(255, 255, 255, 0.1));
|
|
414
|
+
}
|
|
411
415
|
|
|
412
|
-
.ui-chart__tooltip-item {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
416
|
+
.ui-chart__tooltip-item {
|
|
417
|
+
display: flex;
|
|
418
|
+
align-items: center;
|
|
419
|
+
gap: var(--space-2, 0.5rem);
|
|
420
|
+
padding: var(--space-1, 0.25rem) 0;
|
|
421
|
+
}
|
|
418
422
|
|
|
419
|
-
.ui-chart__tooltip-color {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
}
|
|
423
|
+
.ui-chart__tooltip-color {
|
|
424
|
+
width: 10px;
|
|
425
|
+
height: 10px;
|
|
426
|
+
border-radius: var(--radius-sm, 0.25rem);
|
|
427
|
+
flex-shrink: 0;
|
|
428
|
+
}
|
|
425
429
|
|
|
426
|
-
.ui-chart__tooltip-label {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
430
|
+
.ui-chart__tooltip-label {
|
|
431
|
+
flex: 1;
|
|
432
|
+
opacity: 0.8;
|
|
433
|
+
}
|
|
430
434
|
|
|
431
|
-
.ui-chart__tooltip-value {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
}
|
|
435
|
+
.ui-chart__tooltip-value {
|
|
436
|
+
font-weight: var(--font-medium, 500);
|
|
437
|
+
font-variant-numeric: tabular-nums;
|
|
438
|
+
}
|
|
435
439
|
|
|
436
|
-
[data-theme='dark'] .ui-chart__tooltip {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
+
[data-theme='dark'] .ui-chart__tooltip {
|
|
441
|
+
background-color: var(--chart-tooltip-bg, #fafaf9);
|
|
442
|
+
color: var(--chart-tooltip-text, #1c1917);
|
|
443
|
+
}
|
|
440
444
|
|
|
441
|
-
[data-theme='dark'] .ui-chart__tooltip-title {
|
|
442
|
-
|
|
445
|
+
[data-theme='dark'] .ui-chart__tooltip-title {
|
|
446
|
+
border-bottom-color: var(--chart-tooltip-border, rgba(0, 0, 0, 0.1));
|
|
447
|
+
}
|
|
443
448
|
}
|
|
444
449
|
</style>
|
|
@@ -4,6 +4,7 @@ import type { ChartData, ChartOptions } from 'chart.js'
|
|
|
4
4
|
import BaseChart from './BaseChart.vue'
|
|
5
5
|
import type { LegendItem, TooltipData, PointClickData } from './BaseChart.vue'
|
|
6
6
|
import { useChartTheme } from './useChartTheme'
|
|
7
|
+
import type { ClassValue } from '../../types/common'
|
|
7
8
|
|
|
8
9
|
type DonutChartData = ChartData<'doughnut'>
|
|
9
10
|
type DonutChartOptions = ChartOptions<'doughnut'>
|
|
@@ -21,6 +22,8 @@ export interface SegmentClickData {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export interface DonutChartProps {
|
|
25
|
+
/** Additional CSS classes */
|
|
26
|
+
class?: ClassValue
|
|
24
27
|
/** Chart segments */
|
|
25
28
|
segments: DonutChartSegment[]
|
|
26
29
|
/** Chart height */
|
|
@@ -115,8 +118,7 @@ function handlePointClick(data: PointClickData) {
|
|
|
115
118
|
|
|
116
119
|
<template>
|
|
117
120
|
<div
|
|
118
|
-
class="ui-donut-chart"
|
|
119
|
-
:class="[`ui-donut-chart--legend-${legendPosition}`]"
|
|
121
|
+
:class="[props.class, 'ui-donut-chart', `ui-donut-chart--legend-${legendPosition}`]"
|
|
120
122
|
>
|
|
121
123
|
<!-- Legend (conditional position) -->
|
|
122
124
|
<slot v-if="showLegend && (legendPosition === 'top' || legendPosition === 'left')" name="legend" :items="legendItems" :toggle="() => {}">
|
|
@@ -4,6 +4,7 @@ import type { ChartData, ChartOptions } from 'chart.js'
|
|
|
4
4
|
import BaseChart from './BaseChart.vue'
|
|
5
5
|
import type { LegendItem, TooltipData, PointClickData } from './BaseChart.vue'
|
|
6
6
|
import { useChartTheme } from './useChartTheme'
|
|
7
|
+
import type { ClassValue } from '../../types/common'
|
|
7
8
|
|
|
8
9
|
type LineChartData = ChartData<'line'>
|
|
9
10
|
type LineChartOptions = ChartOptions<'line'>
|
|
@@ -14,6 +15,8 @@ export interface LineChartSeries {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export interface LineChartProps {
|
|
18
|
+
/** Additional CSS classes */
|
|
19
|
+
class?: ClassValue
|
|
17
20
|
/** X-axis labels */
|
|
18
21
|
labels: string[]
|
|
19
22
|
/** Data series */
|
|
@@ -126,6 +129,7 @@ const chartOptions = computed(() => ({
|
|
|
126
129
|
|
|
127
130
|
<template>
|
|
128
131
|
<BaseChart
|
|
132
|
+
:class="props.class"
|
|
129
133
|
type="line"
|
|
130
134
|
:data="chartData"
|
|
131
135
|
:options="chartOptions"
|
|
@@ -206,4 +206,98 @@ describe('Checkbox', () => {
|
|
|
206
206
|
expect(wrapper.find('input').attributes('value')).toBe('accepted')
|
|
207
207
|
})
|
|
208
208
|
})
|
|
209
|
+
|
|
210
|
+
describe('Pill variant', () => {
|
|
211
|
+
it('renders pill structure when variant is pill', () => {
|
|
212
|
+
const wrapper = mount(Checkbox, {
|
|
213
|
+
props: { variant: 'pill', label: 'Option A' }
|
|
214
|
+
})
|
|
215
|
+
expect(wrapper.find('.ui-checkbox-pill').exists()).toBe(true)
|
|
216
|
+
expect(wrapper.find('.ui-checkbox').exists()).toBe(false)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('renders default structure when variant is default', () => {
|
|
220
|
+
const wrapper = mount(Checkbox, {
|
|
221
|
+
props: { variant: 'default' }
|
|
222
|
+
})
|
|
223
|
+
expect(wrapper.find('.ui-checkbox').exists()).toBe(true)
|
|
224
|
+
expect(wrapper.find('.ui-checkbox-pill').exists()).toBe(false)
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('defaults to default variant', () => {
|
|
228
|
+
const wrapper = mount(Checkbox)
|
|
229
|
+
expect(wrapper.find('.ui-checkbox').exists()).toBe(true)
|
|
230
|
+
expect(wrapper.find('.ui-checkbox-pill').exists()).toBe(false)
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('renders pill label', () => {
|
|
234
|
+
const wrapper = mount(Checkbox, {
|
|
235
|
+
props: { variant: 'pill', label: 'Option A' }
|
|
236
|
+
})
|
|
237
|
+
expect(wrapper.find('.ui-checkbox-pill__label').text()).toBe('Option A')
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('renders pill check indicator', () => {
|
|
241
|
+
const wrapper = mount(Checkbox, {
|
|
242
|
+
props: { variant: 'pill', label: 'Option A' }
|
|
243
|
+
})
|
|
244
|
+
expect(wrapper.find('.ui-checkbox-pill__indicator').exists()).toBe(true)
|
|
245
|
+
expect(wrapper.find('.ui-checkbox-pill__check').exists()).toBe(true)
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
it('applies checked class when checked', () => {
|
|
249
|
+
const wrapper = mount(Checkbox, {
|
|
250
|
+
props: { modelValue: true, variant: 'pill', label: 'Option A' }
|
|
251
|
+
})
|
|
252
|
+
expect(wrapper.find('.ui-checkbox-pill').classes()).toContain('ui-checkbox-pill--checked')
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
it('does not apply checked class when unchecked', () => {
|
|
256
|
+
const wrapper = mount(Checkbox, {
|
|
257
|
+
props: { modelValue: false, variant: 'pill', label: 'Option A' }
|
|
258
|
+
})
|
|
259
|
+
expect(wrapper.find('.ui-checkbox-pill').classes()).not.toContain('ui-checkbox-pill--checked')
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('applies disabled class when disabled', () => {
|
|
263
|
+
const wrapper = mount(Checkbox, {
|
|
264
|
+
props: { variant: 'pill', label: 'Option A', disabled: true }
|
|
265
|
+
})
|
|
266
|
+
expect(wrapper.find('.ui-checkbox-pill').classes()).toContain('ui-checkbox-pill--disabled')
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
it('emits events on pill change', async () => {
|
|
270
|
+
const wrapper = mount(Checkbox, {
|
|
271
|
+
props: { modelValue: false, variant: 'pill', label: 'Option A' }
|
|
272
|
+
})
|
|
273
|
+
await wrapper.find('input').setValue(true)
|
|
274
|
+
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([true])
|
|
275
|
+
expect(wrapper.emitted('change')?.[0]).toEqual([true])
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
it('toggles off when checked pill is clicked', async () => {
|
|
279
|
+
const wrapper = mount(Checkbox, {
|
|
280
|
+
props: { modelValue: true, variant: 'pill', label: 'Option A' }
|
|
281
|
+
})
|
|
282
|
+
await wrapper.find('input').setValue(false)
|
|
283
|
+
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([false])
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
const sizes = ['sm', 'md', 'lg'] as const
|
|
287
|
+
sizes.forEach(size => {
|
|
288
|
+
it(`applies ${size} size class to pill`, () => {
|
|
289
|
+
const wrapper = mount(Checkbox, {
|
|
290
|
+
props: { variant: 'pill', label: 'Option A', size }
|
|
291
|
+
})
|
|
292
|
+
expect(wrapper.find('.ui-checkbox-pill').classes()).toContain(`ui-checkbox-pill--${size}`)
|
|
293
|
+
})
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
it('pill indicator has aria-hidden', () => {
|
|
297
|
+
const wrapper = mount(Checkbox, {
|
|
298
|
+
props: { variant: 'pill', label: 'Option A' }
|
|
299
|
+
})
|
|
300
|
+
expect(wrapper.find('.ui-checkbox-pill__indicator').attributes('aria-hidden')).toBe('true')
|
|
301
|
+
})
|
|
302
|
+
})
|
|
209
303
|
})
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, ref, watch, onMounted } from 'vue'
|
|
3
3
|
import { useId } from '../../composables'
|
|
4
|
+
import type { ClassValue } from '../../types/common'
|
|
4
5
|
|
|
5
6
|
export interface CheckboxProps {
|
|
7
|
+
/** Additional CSS classes */
|
|
8
|
+
class?: ClassValue
|
|
6
9
|
/** Checked state (v-model) */
|
|
7
10
|
modelValue?: boolean
|
|
8
11
|
/** Indeterminate state (partial selection) */
|
|
9
12
|
indeterminate?: boolean
|
|
13
|
+
/** Visual variant */
|
|
14
|
+
variant?: 'default' | 'pill'
|
|
10
15
|
/** Checkbox size */
|
|
11
16
|
size?: 'sm' | 'md' | 'lg'
|
|
12
17
|
/** Disabled state */
|
|
@@ -26,6 +31,7 @@ export interface CheckboxProps {
|
|
|
26
31
|
const props = withDefaults(defineProps<CheckboxProps>(), {
|
|
27
32
|
modelValue: false,
|
|
28
33
|
indeterminate: false,
|
|
34
|
+
variant: 'default',
|
|
29
35
|
size: 'md',
|
|
30
36
|
disabled: false
|
|
31
37
|
})
|
|
@@ -66,9 +72,54 @@ const indeterminatePathLength = 10
|
|
|
66
72
|
</script>
|
|
67
73
|
|
|
68
74
|
<template>
|
|
75
|
+
<!-- Pill variant -->
|
|
69
76
|
<label
|
|
70
|
-
|
|
77
|
+
v-if="variant === 'pill'"
|
|
71
78
|
:class="[
|
|
79
|
+
props.class,
|
|
80
|
+
'ui-checkbox-pill',
|
|
81
|
+
`ui-checkbox-pill--${size}`,
|
|
82
|
+
{
|
|
83
|
+
'ui-checkbox-pill--disabled': disabled,
|
|
84
|
+
'ui-checkbox-pill--checked': modelValue
|
|
85
|
+
}
|
|
86
|
+
]"
|
|
87
|
+
>
|
|
88
|
+
<input
|
|
89
|
+
ref="inputRef"
|
|
90
|
+
:id="checkboxId"
|
|
91
|
+
type="checkbox"
|
|
92
|
+
:checked="modelValue"
|
|
93
|
+
:disabled="disabled"
|
|
94
|
+
:name="name"
|
|
95
|
+
:value="value"
|
|
96
|
+
class="ui-checkbox-pill__input"
|
|
97
|
+
@change="handleChange"
|
|
98
|
+
/>
|
|
99
|
+
|
|
100
|
+
<span class="ui-checkbox-pill__indicator" aria-hidden="true">
|
|
101
|
+
<svg
|
|
102
|
+
class="ui-checkbox-pill__check"
|
|
103
|
+
viewBox="0 0 16 16"
|
|
104
|
+
fill="none"
|
|
105
|
+
stroke="currentColor"
|
|
106
|
+
stroke-width="2.5"
|
|
107
|
+
stroke-linecap="round"
|
|
108
|
+
stroke-linejoin="round"
|
|
109
|
+
>
|
|
110
|
+
<path d="M3.5 8.5L6.5 11L12.5 5" />
|
|
111
|
+
</svg>
|
|
112
|
+
</span>
|
|
113
|
+
|
|
114
|
+
<span v-if="label" class="ui-checkbox-pill__label">{{ label }}</span>
|
|
115
|
+
</label>
|
|
116
|
+
|
|
117
|
+
<!-- Default variant -->
|
|
118
|
+
<label
|
|
119
|
+
v-else
|
|
120
|
+
:class="[
|
|
121
|
+
props.class,
|
|
122
|
+
'ui-checkbox',
|
|
72
123
|
`ui-checkbox--${size}`,
|
|
73
124
|
{
|
|
74
125
|
'ui-checkbox--disabled': disabled,
|
|
@@ -282,4 +333,122 @@ const indeterminatePathLength = 10
|
|
|
282
333
|
.ui-checkbox--lg .ui-checkbox__description {
|
|
283
334
|
font-size: var(--text-sm);
|
|
284
335
|
}
|
|
336
|
+
|
|
337
|
+
/* Pill variant styles */
|
|
338
|
+
.ui-checkbox-pill {
|
|
339
|
+
display: inline-flex;
|
|
340
|
+
align-items: center;
|
|
341
|
+
gap: var(--space-2);
|
|
342
|
+
height: 36px;
|
|
343
|
+
padding: 0 var(--space-3);
|
|
344
|
+
background: var(--chip-bg);
|
|
345
|
+
border: 1px solid var(--chip-border);
|
|
346
|
+
border-radius: var(--radius-full);
|
|
347
|
+
font-family: var(--font-sans);
|
|
348
|
+
font-size: var(--text-sm);
|
|
349
|
+
font-weight: var(--font-medium);
|
|
350
|
+
color: var(--chip-text);
|
|
351
|
+
cursor: pointer;
|
|
352
|
+
transition:
|
|
353
|
+
background-color var(--duration-fast) var(--ease-default),
|
|
354
|
+
border-color var(--duration-fast) var(--ease-default),
|
|
355
|
+
color var(--duration-fast) var(--ease-default);
|
|
356
|
+
-webkit-tap-highlight-color: transparent;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.ui-checkbox-pill--sm {
|
|
360
|
+
height: 32px;
|
|
361
|
+
font-size: var(--text-xs);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.ui-checkbox-pill--lg {
|
|
365
|
+
height: 44px;
|
|
366
|
+
font-size: var(--text-md);
|
|
367
|
+
padding: 0 var(--space-4);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.ui-checkbox-pill__input {
|
|
371
|
+
position: absolute;
|
|
372
|
+
width: 1px;
|
|
373
|
+
height: 1px;
|
|
374
|
+
padding: 0;
|
|
375
|
+
margin: -1px;
|
|
376
|
+
overflow: hidden;
|
|
377
|
+
clip: rect(0, 0, 0, 0);
|
|
378
|
+
white-space: nowrap;
|
|
379
|
+
border: 0;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.ui-checkbox-pill:not(.ui-checkbox-pill--disabled):hover {
|
|
383
|
+
background: var(--chip-bg-hover);
|
|
384
|
+
border-color: var(--chip-border-hover);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.ui-checkbox-pill:not(.ui-checkbox-pill--disabled):active {
|
|
388
|
+
background: var(--chip-bg-active);
|
|
389
|
+
border-color: var(--chip-border-active);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.ui-checkbox-pill--checked {
|
|
393
|
+
background: var(--chip-bg-selected);
|
|
394
|
+
border-color: var(--chip-border-selected);
|
|
395
|
+
color: var(--chip-text-selected);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.ui-checkbox-pill--checked:not(.ui-checkbox-pill--disabled):hover {
|
|
399
|
+
background: var(--chip-bg-selected-hover);
|
|
400
|
+
border-color: var(--chip-border-selected-hover);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.ui-checkbox-pill--checked:not(.ui-checkbox-pill--disabled):active {
|
|
404
|
+
background: var(--chip-bg-selected-active);
|
|
405
|
+
border-color: var(--chip-border-selected-active);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.ui-checkbox-pill:has(.ui-checkbox-pill__input:focus-visible) {
|
|
409
|
+
outline: 2px solid var(--ring-color);
|
|
410
|
+
outline-offset: 2px;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.ui-checkbox-pill--disabled {
|
|
414
|
+
opacity: 0.5;
|
|
415
|
+
cursor: not-allowed;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.ui-checkbox-pill__indicator {
|
|
419
|
+
display: flex;
|
|
420
|
+
align-items: center;
|
|
421
|
+
justify-content: center;
|
|
422
|
+
width: 0;
|
|
423
|
+
margin-right: calc(-1 * var(--space-2));
|
|
424
|
+
overflow: hidden;
|
|
425
|
+
transition:
|
|
426
|
+
width var(--duration-fast) var(--ease-out-expo),
|
|
427
|
+
margin var(--duration-fast) var(--ease-out-expo);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.ui-checkbox-pill--checked .ui-checkbox-pill__indicator {
|
|
431
|
+
width: 1rem;
|
|
432
|
+
margin-right: 0;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.ui-checkbox-pill__check {
|
|
436
|
+
width: 0.875rem;
|
|
437
|
+
height: 0.875rem;
|
|
438
|
+
flex-shrink: 0;
|
|
439
|
+
opacity: 0;
|
|
440
|
+
transform: scale(0.5);
|
|
441
|
+
transition:
|
|
442
|
+
opacity var(--duration-fast) var(--ease-default),
|
|
443
|
+
transform var(--duration-fast) var(--ease-out-back);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.ui-checkbox-pill--checked .ui-checkbox-pill__check {
|
|
447
|
+
opacity: 1;
|
|
448
|
+
transform: scale(1);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.ui-checkbox-pill__label {
|
|
452
|
+
white-space: nowrap;
|
|
453
|
+
}
|
|
285
454
|
</style>
|
|
@@ -3,8 +3,11 @@ import { computed, inject } from 'vue'
|
|
|
3
3
|
import Icon from '../Icon/Icon.vue'
|
|
4
4
|
import type { IconInput } from '../Icon/Icon.vue'
|
|
5
5
|
import { useId } from '../../composables'
|
|
6
|
+
import type { ClassValue } from '../../types/common'
|
|
6
7
|
|
|
7
8
|
export interface ChoiceChipProps {
|
|
9
|
+
/** Additional CSS classes */
|
|
10
|
+
class?: ClassValue
|
|
8
11
|
/** Selected state for standalone usage (v-model) */
|
|
9
12
|
modelValue?: boolean
|
|
10
13
|
/** Value for group usage */
|
|
@@ -65,11 +68,14 @@ function handleChange() {
|
|
|
65
68
|
|
|
66
69
|
<template>
|
|
67
70
|
<label
|
|
68
|
-
class="
|
|
69
|
-
|
|
70
|
-
'ui-choice-chip
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
:class="[
|
|
72
|
+
props.class,
|
|
73
|
+
'ui-choice-chip',
|
|
74
|
+
{
|
|
75
|
+
'ui-choice-chip--selected': isSelected,
|
|
76
|
+
'ui-choice-chip--disabled': effectiveDisabled
|
|
77
|
+
}
|
|
78
|
+
]"
|
|
73
79
|
>
|
|
74
80
|
<input
|
|
75
81
|
:id="chipId"
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { provide, toRef } from 'vue'
|
|
3
|
+
import type { ClassValue } from '../../types/common'
|
|
3
4
|
|
|
4
5
|
export interface ChoiceChipGroupProps {
|
|
6
|
+
/** Additional CSS classes */
|
|
7
|
+
class?: ClassValue
|
|
5
8
|
/** Selected value(s) - single value for 'single' type, array for 'multiple' */
|
|
6
9
|
modelValue?: string | number | (string | number)[]
|
|
7
10
|
/** Selection type */
|
|
@@ -50,8 +53,7 @@ provide('choiceChipGroup', {
|
|
|
50
53
|
role="group"
|
|
51
54
|
:aria-label="label"
|
|
52
55
|
:aria-disabled="disabled || undefined"
|
|
53
|
-
class="ui-choice-chip-group"
|
|
54
|
-
:class="{ 'ui-choice-chip-group--disabled': disabled }"
|
|
56
|
+
:class="[props.class, 'ui-choice-chip-group', { 'ui-choice-chip-group--disabled': disabled }]"
|
|
55
57
|
>
|
|
56
58
|
<slot />
|
|
57
59
|
</div>
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, computed, onUnmounted } from 'vue'
|
|
3
3
|
import { clamp } from '../../utils/color'
|
|
4
|
+
import type { ClassValue } from '../../types/common'
|
|
4
5
|
|
|
5
6
|
export interface ColorAreaProps {
|
|
7
|
+
/** Additional CSS classes */
|
|
8
|
+
class?: ClassValue
|
|
6
9
|
/** Hue value (0-360) */
|
|
7
10
|
hue: number
|
|
8
11
|
/** Saturation value (0-1) */
|
|
@@ -94,8 +97,7 @@ onUnmounted(() => {
|
|
|
94
97
|
<template>
|
|
95
98
|
<div
|
|
96
99
|
ref="areaRef"
|
|
97
|
-
class="ui-color-area"
|
|
98
|
-
:class="{ 'ui-color-area--dragging': isDragging }"
|
|
100
|
+
:class="[props.class, 'ui-color-area', { 'ui-color-area--dragging': isDragging }]"
|
|
99
101
|
:style="{ '--hue-color': hueColor }"
|
|
100
102
|
@mousedown="handleMouseDown"
|
|
101
103
|
@touchstart="handleTouchStart"
|
|
@@ -12,10 +12,13 @@ import {
|
|
|
12
12
|
formatRgb,
|
|
13
13
|
type HSV
|
|
14
14
|
} from '../../utils/color'
|
|
15
|
+
import type { ClassValue } from '../../types/common'
|
|
15
16
|
|
|
16
17
|
export type ColorFormat = 'hex' | 'rgb' | 'oklch'
|
|
17
18
|
|
|
18
19
|
export interface ColorPickerProps {
|
|
20
|
+
/** Additional CSS classes */
|
|
21
|
+
class?: ClassValue
|
|
19
22
|
/** Current color value */
|
|
20
23
|
modelValue: string
|
|
21
24
|
/** Output format */
|
|
@@ -144,8 +147,7 @@ const hasEyeDropper = computed(() => 'EyeDropper' in window)
|
|
|
144
147
|
>
|
|
145
148
|
<template #trigger>
|
|
146
149
|
<div
|
|
147
|
-
class="ui-color-picker__trigger"
|
|
148
|
-
:class="{ 'ui-color-picker__trigger--disabled': disabled }"
|
|
150
|
+
:class="[props.class, 'ui-color-picker__trigger', { 'ui-color-picker__trigger--disabled': disabled }]"
|
|
149
151
|
>
|
|
150
152
|
<div
|
|
151
153
|
class="ui-color-picker__swatch"
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, computed, onUnmounted } from 'vue'
|
|
3
3
|
import { clamp } from '../../utils/color'
|
|
4
|
+
import type { ClassValue } from '../../types/common'
|
|
4
5
|
|
|
5
6
|
export interface ColorSliderProps {
|
|
7
|
+
/** Additional CSS classes */
|
|
8
|
+
class?: ClassValue
|
|
6
9
|
/** Current value (0-1 for alpha, 0-360 for hue) */
|
|
7
10
|
modelValue: number
|
|
8
11
|
/** Slider type */
|
|
@@ -100,8 +103,9 @@ onUnmounted(() => {
|
|
|
100
103
|
<template>
|
|
101
104
|
<div
|
|
102
105
|
ref="trackRef"
|
|
103
|
-
class="ui-color-slider"
|
|
104
106
|
:class="[
|
|
107
|
+
props.class,
|
|
108
|
+
'ui-color-slider',
|
|
105
109
|
`ui-color-slider--${type}`,
|
|
106
110
|
{ 'ui-color-slider--dragging': isDragging }
|
|
107
111
|
]"
|