webcake-ui-kit 1.0.0 → 1.0.1

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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +358 -8
  3. package/package.json +68 -5
  4. package/src/components/accordion/Accordion.vue +70 -0
  5. package/src/components/accordion/accordion.css +5 -0
  6. package/src/components/accordion-item/AccordionItem.vue +98 -0
  7. package/src/components/accordion-item/accordion-item.css +143 -0
  8. package/src/components/alert-dialog/AlertDialog.vue +82 -0
  9. package/src/components/alert-dialog/alert-dialog.css +33 -0
  10. package/src/components/badge/Badge.vue +2 -2
  11. package/src/components/badge/badge.css +1 -4
  12. package/src/components/breadcrumb/Breadcrumb.vue +85 -0
  13. package/src/components/breadcrumb/breadcrumb.css +90 -0
  14. package/src/components/button/Button.vue +77 -10
  15. package/src/components/button/button.css +258 -24
  16. package/src/components/button-group/ButtonGroup.vue +25 -0
  17. package/src/components/button-group/button-group.css +30 -0
  18. package/src/components/checkbox/Checkbox.vue +55 -0
  19. package/src/components/checkbox/checkbox.css +86 -0
  20. package/src/components/checkbox-group/CheckboxGroup.vue +50 -0
  21. package/src/components/checkbox-group/checkbox-group.css +35 -0
  22. package/src/components/dialog/Dialog.vue +355 -0
  23. package/src/components/dialog/dialog.css +255 -0
  24. package/src/components/divider/Divider.vue +35 -0
  25. package/src/components/divider/divider.css +38 -0
  26. package/src/components/input/Input.vue +99 -0
  27. package/src/components/input/input.css +123 -0
  28. package/src/components/pagination/Pagination.vue +211 -0
  29. package/src/components/pagination/pagination.css +13 -0
  30. package/src/components/radio/Radio.vue +74 -0
  31. package/src/components/radio/radio.css +89 -0
  32. package/src/components/radio-group/RadioGroup.vue +70 -0
  33. package/src/components/radio-group/radio_group.css +11 -0
  34. package/src/components/rich-checkbox-group/RichCheckboxGroup.vue +59 -0
  35. package/src/components/rich-checkbox-group/rich-checkbox-group.css +54 -0
  36. package/src/components/rich-switch-group/RichSwitchGroup.vue +49 -0
  37. package/src/components/rich-switch-group/rich_switch_group.css +45 -0
  38. package/src/components/select/Select.vue +262 -0
  39. package/src/components/select/select.css +207 -0
  40. package/src/components/select-option/SelectOption.vue +82 -0
  41. package/src/components/select-option/select_option.css +60 -0
  42. package/src/components/sidebar-group-label/SidebarGroupLabel.vue +68 -0
  43. package/src/components/sidebar-group-label/sidebar_group_label.css +61 -0
  44. package/src/components/sidebar-item/SidebarItem.vue +110 -0
  45. package/src/components/sidebar-item/sidebar_item.css +142 -0
  46. package/src/components/slider/Slider.vue +255 -0
  47. package/src/components/slider/slider.css +89 -0
  48. package/src/components/spinner/Spinner.vue +47 -0
  49. package/src/components/spinner/spinner.css +48 -0
  50. package/src/components/switch/Switch.vue +32 -0
  51. package/src/components/switch/switch.css +46 -0
  52. package/src/components/switch-group/SwitchGroup.vue +32 -0
  53. package/src/components/switch-group/switch_group.css +28 -0
  54. package/src/components/tabs/Tabs.vue +57 -0
  55. package/src/components/tabs/tabs.css +118 -0
  56. package/src/components/tag/Tag.vue +47 -0
  57. package/src/components/tag/tag.css +115 -0
  58. package/src/components/toggle/Toggle.vue +112 -0
  59. package/src/components/toggle/toggle.css +174 -0
  60. package/src/components/toggle-group/ToggleGroup.vue +57 -0
  61. package/src/components/toggle-group/toggle-group.css +68 -0
  62. package/src/icons/LoaderIcon.vue +22 -0
  63. package/src/index.js +29 -2
  64. package/src/styles/border_radius.css +3 -3
  65. package/src/styles/color_general.css +21 -14
  66. package/src/styles/shadow.css +2 -2
@@ -0,0 +1,118 @@
1
+ .ui-tabs {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ padding: 3px;
5
+ background: var(--secondary-bg);
6
+ border-radius: var(--radius-10);
7
+ box-sizing: border-box;
8
+ }
9
+
10
+ .ui-tabs--mini {
11
+ padding: 2px;
12
+ }
13
+ .ui-tabs--md {
14
+ padding: 4px;
15
+ }
16
+ .ui-tabs--lg {
17
+ padding: 4px;
18
+ }
19
+
20
+ /* — tab item — */
21
+ .ui-tabs__item {
22
+ display: inline-flex;
23
+ align-items: center;
24
+ justify-content: center;
25
+ gap: 6px;
26
+ height: 26px;
27
+ padding: 0 var(--spacing-xs);
28
+ border: none;
29
+ background: none;
30
+ border-radius: var(--radius);
31
+ font-family: var(--font-family-body);
32
+ font-size: var(--paragraph-small-font-size);
33
+ line-height: var(--paragraph-small-line-height);
34
+ font-weight: 500;
35
+ color: var(--secondary-fg);
36
+ cursor: pointer;
37
+ white-space: nowrap;
38
+ user-select: none;
39
+ outline: none;
40
+ box-sizing: border-box;
41
+ flex-shrink: 0;
42
+ transition: color 0.1s;
43
+ }
44
+
45
+ .ui-tabs__item:hover:not(.ui-tabs__item--active):not(.ui-tabs__item--disabled) {
46
+ color: var(--primary-fg);
47
+ }
48
+
49
+ .ui-tabs__item:focus-visible {
50
+ box-shadow: var(--shadow-focus-ring);
51
+ }
52
+
53
+ .ui-tabs__item--active {
54
+ background: var(--primary-bg);
55
+ color: var(--primary-fg);
56
+ box-shadow: var(--shadow-sm);
57
+ cursor: default;
58
+ }
59
+
60
+ .ui-tabs__item--disabled {
61
+ opacity: 0.4;
62
+ cursor: not-allowed;
63
+ pointer-events: none;
64
+ }
65
+
66
+ /* — sizes — */
67
+ .ui-tabs--mini .ui-tabs__item {
68
+ height: 20px;
69
+ padding: 0 6px;
70
+ font-size: var(--paragraph-mini-font-size);
71
+ line-height: var(--paragraph-mini-line-height);
72
+ }
73
+
74
+ .ui-tabs--md .ui-tabs__item {
75
+ height: 28px;
76
+ }
77
+
78
+ .ui-tabs--lg .ui-tabs__item {
79
+ height: 32px;
80
+ padding: 0 var(--spacing-10);
81
+ }
82
+
83
+ /* — icon + label elements — */
84
+ .ui-tabs__icon {
85
+ display: inline-flex;
86
+ align-items: center;
87
+ justify-content: center;
88
+ width: 16px;
89
+ height: 16px;
90
+ flex-shrink: 0;
91
+ }
92
+
93
+ .ui-tabs--mini .ui-tabs__icon {
94
+ width: 12px;
95
+ height: 12px;
96
+ }
97
+
98
+ .ui-tabs__label {
99
+ flex-shrink: 0;
100
+ }
101
+
102
+ /* — counter badge — */
103
+ .ui-tabs__counter {
104
+ display: inline-flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ min-width: 16px;
108
+ height: 16px;
109
+ padding: 0 4px;
110
+ border-radius: var(--rounded-full);
111
+ background: var(--tertiary-bg);
112
+ color: var(--primary-fg);
113
+ font-size: var(--paragraph-mini-font-size);
114
+ line-height: var(--paragraph-mini-line-height);
115
+ font-weight: 600;
116
+ flex-shrink: 0;
117
+ box-sizing: border-box;
118
+ }
@@ -0,0 +1,47 @@
1
+ <template>
2
+ <span class="ui-tag" :class="[`ui-tag--type-${type}`, `ui-tag--${size}`, { 'ui-tag--with-icon': hasIcon }]">
3
+ <span v-if="hasIcon" class="ui-tag__icon"><slot name="icon" /></span>
4
+ <span class="ui-tag__label"
5
+ ><slot>{{ label }}</slot></span
6
+ >
7
+ <span v-if="counter > 0" class="ui-tag__counter">{{ counter }}</span>
8
+ <button v-if="closable" class="ui-tag__close" type="button" @click.stop="$emit('close')">
9
+ <svg class="ui-tag__close-icon" viewBox="0 0 12 12" fill="none">
10
+ <path d="M2.5 2.5L9.5 9.5M9.5 2.5L2.5 9.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
11
+ </svg>
12
+ </button>
13
+ </span>
14
+ </template>
15
+
16
+ <script>
17
+ export default {
18
+ name: 'Tag',
19
+ props: {
20
+ type: {
21
+ type: String,
22
+ default: 'outline',
23
+ validator: function (v) {
24
+ return ['default', 'outline'].includes(v)
25
+ }
26
+ },
27
+ size: {
28
+ type: String,
29
+ default: 'md',
30
+ validator: function (v) {
31
+ return ['sm', 'md', 'lg'].includes(v)
32
+ }
33
+ },
34
+ counter: { type: Number, default: 0 },
35
+ closable: { type: Boolean, default: false },
36
+ label: { type: String, default: '' }
37
+ },
38
+ emits: ['close'],
39
+ computed: {
40
+ hasIcon: function () {
41
+ return !!((this.$scopedSlots && this.$scopedSlots['icon']) || this.$slots['icon'])
42
+ }
43
+ }
44
+ }
45
+ </script>
46
+
47
+ <style src="./tag.css" scoped></style>
@@ -0,0 +1,115 @@
1
+ .ui-tag {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ gap: 6px;
5
+ padding: var(--spacing-3xs) var(--spacing-xs);
6
+ border-radius: var(--rounded-lg);
7
+ font-family: var(--font-family-body);
8
+ font-size: var(--paragraph-mini-font-size);
9
+ line-height: var(--paragraph-mini-line-height);
10
+ white-space: nowrap;
11
+ user-select: none;
12
+ box-sizing: border-box;
13
+ vertical-align: middle;
14
+ }
15
+
16
+ /* — type — */
17
+ .ui-tag--type-default {
18
+ background: var(--secondary-bg);
19
+ color: var(--primary-fg);
20
+ }
21
+ .ui-tag--type-default:hover {
22
+ background: #f0f0f0;
23
+ }
24
+
25
+ .ui-tag--type-outline {
26
+ background: rgba(255, 255, 255, 0.1);
27
+ border: 1px solid var(--border-primary);
28
+ color: var(--secondary-fg);
29
+ }
30
+ .ui-tag--type-outline:hover {
31
+ background: rgba(0, 0, 0, 0.05);
32
+ color: var(--primary-fg);
33
+ }
34
+
35
+ /* — sizes — */
36
+ /* sm: mini font, 6px radius, 2px 8px padding — same as base */
37
+
38
+ .ui-tag--md {
39
+ border-radius: var(--radius);
40
+ font-size: var(--paragraph-small-font-size);
41
+ line-height: var(--paragraph-small-line-height);
42
+ }
43
+
44
+ .ui-tag--lg {
45
+ padding: var(--spacing-2xs) 10px;
46
+ border-radius: var(--radius-10);
47
+ font-size: var(--paragraph-small-font-size);
48
+ line-height: var(--paragraph-small-line-height);
49
+ }
50
+
51
+ /* reduce left padding when icon slot is present */
52
+ .ui-tag--with-icon.ui-tag--sm,
53
+ .ui-tag--with-icon.ui-tag--md {
54
+ padding-left: var(--spacing-2xs);
55
+ }
56
+ .ui-tag--with-icon.ui-tag--lg {
57
+ padding-left: 6px;
58
+ }
59
+
60
+ /* — elements — */
61
+ .ui-tag__icon {
62
+ display: inline-flex;
63
+ align-items: center;
64
+ justify-content: center;
65
+ width: 16px;
66
+ height: 16px;
67
+ flex-shrink: 0;
68
+ border-radius: var(--rounded-full);
69
+ overflow: hidden;
70
+ }
71
+
72
+ .ui-tag__counter {
73
+ display: inline-flex;
74
+ align-items: center;
75
+ justify-content: center;
76
+ min-width: 16px;
77
+ height: 16px;
78
+ padding: 0 4px;
79
+ border-radius: var(--rounded-full);
80
+ background: var(--tertiary-bg);
81
+ color: var(--primary-fg);
82
+ font-size: var(--paragraph-mini-font-size);
83
+ line-height: var(--paragraph-mini-line-height);
84
+ font-weight: 600;
85
+ flex-shrink: 0;
86
+ box-sizing: border-box;
87
+ }
88
+
89
+ .ui-tag__close {
90
+ display: inline-flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ width: 16px;
94
+ height: 16px;
95
+ padding: 2px;
96
+ border: none;
97
+ background: none;
98
+ cursor: pointer;
99
+ color: inherit;
100
+ border-radius: var(--rounded-md);
101
+ flex-shrink: 0;
102
+ outline: none;
103
+ box-sizing: border-box;
104
+ }
105
+
106
+ .ui-tag__close:hover {
107
+ background: rgba(0, 0, 0, 0.1);
108
+ }
109
+
110
+ .ui-tag__close-icon {
111
+ width: 12px;
112
+ height: 12px;
113
+ display: block;
114
+ flex-shrink: 0;
115
+ }
@@ -0,0 +1,112 @@
1
+ <template>
2
+ <button
3
+ type="button"
4
+ :class="[
5
+ 'ui-toggle',
6
+ 'ui-toggle--variant-' + variant,
7
+ 'ui-toggle--' + computedSize,
8
+ 'ui-toggle--round-' + roundness,
9
+ isActive && 'ui-toggle--active',
10
+ isIconOnly && 'ui-toggle--icon-only',
11
+ isInGroup && 'ui-toggle--in-group'
12
+ ]"
13
+ :disabled="disabled"
14
+ :aria-pressed="isActive ? 'true' : 'false'"
15
+ @click="handleClick"
16
+ >
17
+ <span v-if="hasIconLeft" class="ui-toggle__icon">
18
+ <slot name="icon-left" />
19
+ </span>
20
+ <span v-if="hasIcon" class="ui-toggle__icon">
21
+ <slot name="icon" />
22
+ </span>
23
+ <span v-if="!isIconOnly && (hasDefaultSlot || label)" class="ui-toggle__label">
24
+ <slot>{{ label }}</slot>
25
+ </span>
26
+ <span v-if="hasIconRight" class="ui-toggle__icon">
27
+ <slot name="icon-right" />
28
+ </span>
29
+ </button>
30
+ </template>
31
+
32
+ <script>
33
+ export default {
34
+ name: 'Toggle',
35
+ inject: {
36
+ toggleGroupCtx: { default: null },
37
+ groupSize: { default: 'md' }
38
+ },
39
+ model: { prop: 'active', event: 'change' },
40
+ props: {
41
+ variant: {
42
+ type: String,
43
+ default: 'outlined',
44
+ validator: v => ['outlined', 'ghost'].includes(v)
45
+ },
46
+ size: {
47
+ type: String,
48
+ default: 'md',
49
+ validator: v => ['xs', 'sm', 'md', 'lg'].includes(v)
50
+ },
51
+ roundness: {
52
+ type: String,
53
+ default: 'default',
54
+ validator: v => ['default', 'round'].includes(v)
55
+ },
56
+ active: { type: Boolean, default: false },
57
+ modelValue: { type: Boolean, default: undefined },
58
+ value: { type: [String, Number], default: null },
59
+ label: { type: String, default: '' },
60
+ disabled: { type: Boolean, default: false }
61
+ },
62
+ emits: ['change', 'update:modelValue'],
63
+ computed: {
64
+ computedSize() {
65
+ return this.toggleGroupCtx ? this.groupSize : this.size
66
+ },
67
+ isInGroup() {
68
+ return !!this.toggleGroupCtx
69
+ },
70
+ standaloneActive() {
71
+ return this.modelValue !== undefined ? this.modelValue : this.active
72
+ },
73
+ isActive() {
74
+ if (this.isInGroup) {
75
+ return this.toggleGroupCtx.isSelected(this.value)
76
+ }
77
+ return this.standaloneActive
78
+ },
79
+ hasIconLeft() {
80
+ return !!this.$slots['icon-left']
81
+ },
82
+ hasIconRight() {
83
+ return !!this.$slots['icon-right']
84
+ },
85
+ hasIcon() {
86
+ return !!this.$slots['icon']
87
+ },
88
+ hasDefaultSlot() {
89
+ return !!this.$slots['default']
90
+ },
91
+ isIconOnly() {
92
+ return !this.label && !this.hasDefaultSlot && this.hasIcon && !this.hasIconLeft && !this.hasIconRight
93
+ }
94
+ },
95
+ methods: {
96
+ handleClick() {
97
+ if (this.disabled) return
98
+ if (this.isInGroup) {
99
+ if (this.value !== null) {
100
+ this.toggleGroupCtx.select(this.value)
101
+ }
102
+ return
103
+ }
104
+ const next = !this.standaloneActive
105
+ this.$emit('change', next)
106
+ this.$emit('update:modelValue', next)
107
+ }
108
+ }
109
+ }
110
+ </script>
111
+
112
+ <style src="./toggle.css" scoped></style>
@@ -0,0 +1,174 @@
1
+ /* ============================================================
2
+ Base
3
+ ============================================================ */
4
+ .ui-toggle {
5
+ display: inline-flex;
6
+ align-items: center;
7
+ justify-content: center;
8
+ border: 0;
9
+ margin: 0;
10
+ cursor: pointer;
11
+ font-family: var(--font-family-body);
12
+ font-weight: var(--paragraph-medium-font-weight);
13
+ text-align: center;
14
+ white-space: nowrap;
15
+ background: transparent;
16
+ color: var(--primary-fg);
17
+ transition:
18
+ background-color 0.12s ease,
19
+ border-color 0.12s ease,
20
+ color 0.12s ease;
21
+ -webkit-appearance: none;
22
+ appearance: none;
23
+ }
24
+
25
+ .ui-toggle__label {
26
+ display: inline-flex;
27
+ }
28
+
29
+ .ui-toggle__icon {
30
+ display: inline-flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ flex-shrink: 0;
34
+ overflow: hidden;
35
+ }
36
+
37
+ /* ============================================================
38
+ Sizes (Mini → xs, Small → sm, Default → md, Large → lg)
39
+ ============================================================ */
40
+ .ui-toggle--xs {
41
+ height: 24px;
42
+ padding: var(--spacing-3) var(--spacing-2xs);
43
+ gap: var(--spacing-6);
44
+ font-size: var(--paragraph-mini-font-size);
45
+ line-height: var(--paragraph-mini-line-height);
46
+ letter-spacing: var(--paragraph-mini-letter-spacing);
47
+ }
48
+ .ui-toggle--xs .ui-toggle__icon {
49
+ width: 16px;
50
+ height: 16px;
51
+ }
52
+
53
+ .ui-toggle--sm {
54
+ height: 32px;
55
+ padding: var(--spacing-5p5) var(--spacing-6);
56
+ gap: var(--spacing-6);
57
+ font-size: var(--paragraph-small-font-size);
58
+ line-height: var(--paragraph-small-line-height);
59
+ letter-spacing: var(--paragraph-small-letter-spacing);
60
+ }
61
+ .ui-toggle--sm .ui-toggle__icon {
62
+ width: 20px;
63
+ height: 20px;
64
+ }
65
+
66
+ .ui-toggle--md {
67
+ height: 36px;
68
+ padding: var(--spacing-7p5) var(--spacing-xs);
69
+ gap: var(--spacing-xs);
70
+ font-size: var(--paragraph-small-font-size);
71
+ line-height: var(--paragraph-small-line-height);
72
+ letter-spacing: var(--paragraph-small-letter-spacing);
73
+ }
74
+ .ui-toggle--md .ui-toggle__icon {
75
+ width: 20px;
76
+ height: 20px;
77
+ }
78
+
79
+ .ui-toggle--lg {
80
+ height: 40px;
81
+ padding: var(--spacing-9p5) var(--spacing-sm);
82
+ gap: var(--spacing-xs);
83
+ font-size: var(--paragraph-small-font-size);
84
+ line-height: var(--paragraph-small-line-height);
85
+ letter-spacing: var(--paragraph-small-letter-spacing);
86
+ }
87
+ .ui-toggle--lg .ui-toggle__icon {
88
+ width: 20px;
89
+ height: 20px;
90
+ }
91
+
92
+ /* ============================================================
93
+ Roundness
94
+ ============================================================ */
95
+ .ui-toggle--round-default {
96
+ border-radius: var(--radius-10);
97
+ }
98
+ .ui-toggle--xs.ui-toggle--round-default {
99
+ border-radius: var(--rounded-lg);
100
+ }
101
+ .ui-toggle--lg.ui-toggle--round-default {
102
+ border-radius: var(--rounded-2xl);
103
+ }
104
+ .ui-toggle--round-round {
105
+ border-radius: var(--rounded-full);
106
+ }
107
+
108
+ /* ============================================================
109
+ Variant — Outlined
110
+ ============================================================ */
111
+ .ui-toggle--variant-outlined {
112
+ background: var(--primary-bg);
113
+ color: var(--primary-fg);
114
+ border: 1px solid var(--border-primary);
115
+ box-shadow: var(--shadow-xs);
116
+ }
117
+ .ui-toggle--variant-outlined.ui-toggle--active {
118
+ background: var(--outline-active);
119
+ color: var(--primary-fg);
120
+ }
121
+
122
+ /* ============================================================
123
+ Variant — Ghost
124
+ ============================================================ */
125
+ .ui-toggle--variant-ghost {
126
+ background: var(--ghost);
127
+ color: var(--primary-fg);
128
+ border: 1px solid transparent;
129
+ }
130
+ .ui-toggle--variant-ghost.ui-toggle--active {
131
+ background: var(--secondary-bg);
132
+ color: var(--primary-fg);
133
+ }
134
+
135
+ /* ============================================================
136
+ Focus
137
+ ============================================================ */
138
+ .ui-toggle:focus-visible {
139
+ outline: none;
140
+ box-shadow: var(--shadow-focus-ring);
141
+ }
142
+
143
+ /* ============================================================
144
+ Disabled
145
+ ============================================================ */
146
+ .ui-toggle:disabled {
147
+ opacity: 0.5;
148
+ cursor: not-allowed;
149
+ pointer-events: none;
150
+ }
151
+
152
+ /* ============================================================
153
+ Icon-only — square button per size
154
+ ============================================================ */
155
+ .ui-toggle--icon-only {
156
+ gap: 0;
157
+ }
158
+ .ui-toggle--icon-only.ui-toggle--xs {
159
+ min-height: 28px;
160
+ min-width: 24px;
161
+ padding: var(--spacing-3xs);
162
+ }
163
+ .ui-toggle--icon-only.ui-toggle--sm {
164
+ min-width: 32px;
165
+ padding: var(--spacing-6);
166
+ }
167
+ .ui-toggle--icon-only.ui-toggle--md {
168
+ min-width: 36px;
169
+ padding: var(--spacing-xs);
170
+ }
171
+ .ui-toggle--icon-only.ui-toggle--lg {
172
+ min-width: 40px;
173
+ padding: var(--spacing-10);
174
+ }
@@ -0,0 +1,57 @@
1
+ <template>
2
+ <div class="ui-toggle-group" role="group" :aria-label="label || undefined">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'ToggleGroup',
10
+ model: { prop: 'value', event: 'change' },
11
+ props: {
12
+ value: { type: [String, Number, Array], default: null },
13
+ modelValue: { type: [String, Number, Array], default: undefined },
14
+ multiple: { type: Boolean, default: false },
15
+ label: { type: String, default: '' },
16
+ size: {
17
+ type: String,
18
+ default: 'md',
19
+ validator: v => ['xs', 'sm', 'md', 'lg'].includes(v)
20
+ }
21
+ },
22
+ emits: ['change', 'update:modelValue'],
23
+ provide() {
24
+ return { toggleGroupCtx: this, groupSize: this.size }
25
+ },
26
+ computed: {
27
+ computedValue() {
28
+ return this.modelValue !== undefined ? this.modelValue : this.value
29
+ }
30
+ },
31
+ methods: {
32
+ select(val) {
33
+ if (val === null || val === undefined) return
34
+ let next
35
+ if (this.multiple) {
36
+ const arr = Array.isArray(this.computedValue) ? [...this.computedValue] : []
37
+ const idx = arr.indexOf(val)
38
+ if (idx >= 0) arr.splice(idx, 1)
39
+ else arr.push(val)
40
+ next = arr
41
+ } else {
42
+ next = this.computedValue === val ? null : val
43
+ }
44
+ this.$emit('change', next)
45
+ this.$emit('update:modelValue', next)
46
+ },
47
+ isSelected(val) {
48
+ if (val === null || val === undefined) return false
49
+ const v = this.computedValue
50
+ if (this.multiple) return Array.isArray(v) && v.includes(val)
51
+ return v === val
52
+ }
53
+ }
54
+ }
55
+ </script>
56
+
57
+ <style src="./toggle-group.css"></style>
@@ -0,0 +1,68 @@
1
+ .ui-toggle-group {
2
+ display: inline-flex;
3
+ align-items: stretch;
4
+ }
5
+
6
+ /* ── In-group: reset radius, then re-apply on first/last for connected look ── */
7
+ .ui-toggle-group .ui-toggle--in-group:not(:first-child):not(:last-child) {
8
+ border-radius: 0;
9
+ }
10
+
11
+ /* default (sm / md) */
12
+ .ui-toggle-group .ui-toggle--in-group:first-child {
13
+ border-top-left-radius: var(--rounded-xl);
14
+ border-bottom-left-radius: var(--rounded-xl);
15
+ border-top-right-radius: 0;
16
+ border-bottom-right-radius: 0;
17
+ }
18
+ .ui-toggle-group .ui-toggle--in-group:last-child {
19
+ border-top-right-radius: var(--rounded-xl);
20
+ border-bottom-right-radius: var(--rounded-xl);
21
+ border-top-left-radius: 0;
22
+ border-bottom-left-radius: 0;
23
+ }
24
+ .ui-toggle-group .ui-toggle--in-group:first-child:last-child {
25
+ border-radius: var(--rounded-xl);
26
+ }
27
+
28
+ /* xs */
29
+ .ui-toggle-group .ui-toggle--xs.ui-toggle--in-group:first-child {
30
+ border-top-left-radius: var(--rounded-lg);
31
+ border-bottom-left-radius: var(--rounded-lg);
32
+ }
33
+ .ui-toggle-group .ui-toggle--xs.ui-toggle--in-group:last-child {
34
+ border-top-right-radius: var(--rounded-lg);
35
+ border-bottom-right-radius: var(--rounded-lg);
36
+ }
37
+ .ui-toggle-group .ui-toggle--xs.ui-toggle--in-group:first-child:last-child {
38
+ border-radius: var(--rounded-lg);
39
+ }
40
+
41
+ /* lg */
42
+ .ui-toggle-group .ui-toggle--lg.ui-toggle--in-group:first-child {
43
+ border-top-left-radius: var(--rounded-2xl);
44
+ border-bottom-left-radius: var(--rounded-2xl);
45
+ }
46
+ .ui-toggle-group .ui-toggle--lg.ui-toggle--in-group:last-child {
47
+ border-top-right-radius: var(--rounded-2xl);
48
+ border-bottom-right-radius: var(--rounded-2xl);
49
+ }
50
+ .ui-toggle-group .ui-toggle--lg.ui-toggle--in-group:first-child:last-child {
51
+ border-radius: var(--rounded-2xl);
52
+ }
53
+
54
+ /* ── Outlined toggles: collapse shared borders, lift on interaction ── */
55
+ .ui-toggle-group .ui-toggle--variant-outlined.ui-toggle--in-group:not(:first-child) {
56
+ margin-left: -1px;
57
+ }
58
+ .ui-toggle-group .ui-toggle--variant-outlined.ui-toggle--in-group:hover:not(:disabled),
59
+ .ui-toggle-group .ui-toggle--variant-outlined.ui-toggle--in-group:focus-visible {
60
+ position: relative;
61
+ z-index: 1;
62
+ }
63
+
64
+ /* ── Active state lifts above siblings so its borders show ── */
65
+ .ui-toggle-group .ui-toggle--in-group.ui-toggle--active {
66
+ position: relative;
67
+ z-index: 1;
68
+ }
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="24"
5
+ height="24"
6
+ viewBox="0 0 24 24"
7
+ fill="none"
8
+ stroke="currentColor"
9
+ stroke-width="1.75"
10
+ stroke-linecap="round"
11
+ stroke-linejoin="round"
12
+ class="lucide lucide-loader-circle-icon lucide-loader-circle"
13
+ >
14
+ <path d="M21 12a9 9 0 1 1-6.219-8.56" />
15
+ </svg>
16
+ </template>
17
+
18
+ <script>
19
+ export default {
20
+ name: 'LoaderIcon'
21
+ }
22
+ </script>