@dolanske/vui 0.3.4 → 0.4.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.
Files changed (110) hide show
  1. package/LICENSE +673 -673
  2. package/README.md +49 -40
  3. package/dist/components/Dropdown/DropdownItem.vue.d.ts +1 -0
  4. package/dist/style.css +1 -1
  5. package/dist/vui.js +864 -859
  6. package/package.json +68 -68
  7. package/src/App.vue +198 -175
  8. package/src/components/Accordion/Accordion.vue +91 -91
  9. package/src/components/Accordion/AccordionGroup.vue +43 -43
  10. package/src/components/Accordion/accordion.scss +80 -80
  11. package/src/components/Alert/Alert.vue +53 -53
  12. package/src/components/Alert/alert.scss +80 -80
  13. package/src/components/Avatar/Avatar.vue +50 -50
  14. package/src/components/Avatar/avatar.scss +52 -52
  15. package/src/components/Badge/Badge.vue +21 -21
  16. package/src/components/Badge/badge.scss +89 -89
  17. package/src/components/Breadcrumbs/BreadcrumbItem.vue +26 -26
  18. package/src/components/Breadcrumbs/Breadcrumbs.vue +33 -33
  19. package/src/components/Breadcrumbs/breadcrumbs.scss +30 -30
  20. package/src/components/Button/Button.vue +90 -90
  21. package/src/components/Button/button.scss +176 -176
  22. package/src/components/ButtonGroup/ButtonGroup.vue +25 -25
  23. package/src/components/ButtonGroup/button-group.scss +51 -51
  24. package/src/components/Calendar/Calendar.vue +60 -60
  25. package/src/components/Calendar/calendar.scss +56 -56
  26. package/src/components/Card/Card.vue +48 -48
  27. package/src/components/Card/card.scss +53 -53
  28. package/src/components/Checkbox/Checkbox.vue +52 -52
  29. package/src/components/Checkbox/checkbox.scss +66 -66
  30. package/src/components/CopyClipboard/CopyClipboard.vue +82 -82
  31. package/src/components/CopyClipboard/copy-clipboard.scss +17 -17
  32. package/src/components/Divider/Divider.vue +44 -44
  33. package/src/components/Divider/divider.scss +35 -35
  34. package/src/components/Drawer/Drawer.vue +97 -97
  35. package/src/components/Drawer/drawer.scss +36 -36
  36. package/src/components/Dropdown/Dropdown.vue +111 -111
  37. package/src/components/Dropdown/DropdownItem.vue +32 -29
  38. package/src/components/Dropdown/DropdownTitle.vue +8 -8
  39. package/src/components/Dropdown/dropdown.scss +118 -117
  40. package/src/components/Flex/Flex.vue +110 -106
  41. package/src/components/Grid/Grid.vue +57 -54
  42. package/src/components/Input/Counter.vue +70 -70
  43. package/src/components/Input/Dropzone.vue +65 -65
  44. package/src/components/Input/File.vue +15 -15
  45. package/src/components/Input/Input.vue +121 -121
  46. package/src/components/Input/Password.vue +47 -47
  47. package/src/components/Input/Textarea.vue +76 -76
  48. package/src/components/Input/input.scss +208 -208
  49. package/src/components/Kbd/Kbd.vue +48 -48
  50. package/src/components/Kbd/KbdGroup.vue +31 -31
  51. package/src/components/Kbd/kbd.scss +18 -18
  52. package/src/components/Modal/Confirm.vue +56 -56
  53. package/src/components/Modal/Modal.vue +91 -91
  54. package/src/components/Modal/modal.scss +49 -49
  55. package/src/components/OTP/OTP.vue +133 -133
  56. package/src/components/OTP/OTPItem.vue +37 -37
  57. package/src/components/OTP/otp.scss +83 -83
  58. package/src/components/Pagination/Pagination.vue +74 -74
  59. package/src/components/Pagination/pagination.ts +78 -78
  60. package/src/components/Popout/Popout.vue +42 -42
  61. package/src/components/Popout/popout.scss +8 -8
  62. package/src/components/Progress/Progress.vue +90 -90
  63. package/src/components/Progress/progress.scss +41 -41
  64. package/src/components/Radio/Radio.vue +36 -36
  65. package/src/components/Radio/RadioGroup.vue +40 -40
  66. package/src/components/Radio/radio.scss +59 -59
  67. package/src/components/Select/Select.vue +180 -180
  68. package/src/components/Select/select.scss +44 -44
  69. package/src/components/Sheet/Sheet.vue +92 -92
  70. package/src/components/Sheet/sheet.scss +60 -60
  71. package/src/components/Sidebar/Sidebar.vue +85 -0
  72. package/src/components/Sidebar/sidebar.scss +123 -0
  73. package/src/components/Skeleton/Skeleton.vue +43 -43
  74. package/src/components/Skeleton/skeleton.scss +14 -14
  75. package/src/components/Spinner/Spinner.vue +42 -42
  76. package/src/components/Spinner/spinner.scss +46 -46
  77. package/src/components/Switch/Switch.vue +30 -30
  78. package/src/components/Switch/switch.scss +52 -52
  79. package/src/components/Table/Cell.vue +23 -23
  80. package/src/components/Table/Header.vue +59 -59
  81. package/src/components/Table/Row.vue +9 -9
  82. package/src/components/Table/SelectAll.vue +23 -23
  83. package/src/components/Table/SelectRow.vue +29 -29
  84. package/src/components/Table/Table.vue +66 -66
  85. package/src/components/Table/table.scss +134 -134
  86. package/src/components/Table/table.ts +244 -244
  87. package/src/components/Tabs/Tab.vue +27 -27
  88. package/src/components/Tabs/Tabs.vue +82 -82
  89. package/src/components/Tabs/tabs.scss +79 -79
  90. package/src/components/Toast/Toasts.vue +47 -47
  91. package/src/components/Toast/toast.scss +41 -41
  92. package/src/components/Toast/toast.ts +68 -68
  93. package/src/components/Tooltip/Tooltip.vue +86 -86
  94. package/src/components/Tooltip/tooltip.scss +4 -4
  95. package/src/index.scss +1 -1
  96. package/src/index.ts +119 -119
  97. package/src/internal/Backdrop/Backdrop.vue +22 -22
  98. package/src/internal/Backdrop/backdrop.scss +28 -28
  99. package/src/main.ts +5 -5
  100. package/src/shared/helpers.ts +74 -74
  101. package/src/shared/types.ts +29 -29
  102. package/src/style/animation.scss +21 -21
  103. package/src/style/core.scss +148 -148
  104. package/src/style/fonts.scss +53 -53
  105. package/src/style/layout.scss +136 -136
  106. package/src/style/media-query.scss +29 -29
  107. package/src/style/reset.scss +135 -135
  108. package/src/style/tooltip.scss +128 -128
  109. package/src/style/typography.scss +338 -338
  110. package/src/style/utils.scss +36 -36
@@ -1,76 +1,76 @@
1
- <script setup lang="ts">
2
- import type { InputProps } from './Input.vue'
3
- import { computed, useId } from 'vue'
4
- import '../Input/input.scss'
5
-
6
- type Props = Omit<InputProps, 'type'> & {
7
- resize?: boolean | 'vertical' | 'horizontal'
8
- autoResize?: boolean
9
- }
10
-
11
- const {
12
- limit,
13
- label,
14
- expand = false,
15
- hint,
16
- placeholder,
17
- required,
18
- modelValue = '',
19
- readonly,
20
- resize = 'vertical',
21
- autoResize,
22
- errors = [] as string[],
23
- } = defineProps<Props>()
24
-
25
- const model = defineModel<string>({
26
- default: '',
27
- set(newValue) {
28
- if (readonly)
29
- return modelValue
30
-
31
- if (newValue.length > Number(limit)) {
32
- return modelValue
33
- }
34
- return newValue
35
- },
36
- })
37
- const id = useId()
38
-
39
- const r = computed(() => resize === true ? 'both' : (resize || 'initial'))
40
- const fS = computed(() => autoResize ? 'content' : 'auto')
41
- </script>
42
-
43
- <template>
44
- <div class="vui-input-container" :class="{ expand, required, readonly, 'has-errors': errors.length > 0 }">
45
- <slot name="before" />
46
- <div class="vui-input">
47
- <label v-if="label" for="id">{{ label }}</label>
48
- <p v-if="hint" class="vui-input-hint">
49
- {{ hint }}
50
- </p>
51
- <textarea
52
- :id
53
- v-model="model"
54
- :readonly
55
- name="id"
56
- :placeholder
57
- :required
58
- :max="limit"
59
- :style="{
60
- resize: r,
61
- // @ts-expect-error Early-adoption CSS attribute
62
- fieldSizing: fS,
63
- }"
64
- />
65
- </div>
66
- <p v-if="limit" class="vui-input-limit">
67
- {{ `${model.length}/${limit}` }}
68
- </p>
69
- <ul v-if="errors.length > 0" class="vui-input-errors">
70
- <li v-for="err in errors" :key="err">
71
- {{ err }}
72
- </li>
73
- </ul>
74
- <slot name="after" />
75
- </div>
76
- </template>
1
+ <script setup lang="ts">
2
+ import type { InputProps } from './Input.vue'
3
+ import { computed, useId } from 'vue'
4
+ import '../Input/input.scss'
5
+
6
+ type Props = Omit<InputProps, 'type'> & {
7
+ resize?: boolean | 'vertical' | 'horizontal'
8
+ autoResize?: boolean
9
+ }
10
+
11
+ const {
12
+ limit,
13
+ label,
14
+ expand = false,
15
+ hint,
16
+ placeholder,
17
+ required,
18
+ modelValue = '',
19
+ readonly,
20
+ resize = 'vertical',
21
+ autoResize,
22
+ errors = [] as string[],
23
+ } = defineProps<Props>()
24
+
25
+ const model = defineModel<string>({
26
+ default: '',
27
+ set(newValue) {
28
+ if (readonly)
29
+ return modelValue
30
+
31
+ if (newValue.length > Number(limit)) {
32
+ return modelValue
33
+ }
34
+ return newValue
35
+ },
36
+ })
37
+ const id = useId()
38
+
39
+ const r = computed(() => resize === true ? 'both' : (resize || 'initial'))
40
+ const fS = computed(() => autoResize ? 'content' : 'auto')
41
+ </script>
42
+
43
+ <template>
44
+ <div class="vui-input-container" :class="{ expand, required, readonly, 'has-errors': errors.length > 0 }">
45
+ <slot name="before" />
46
+ <div class="vui-input">
47
+ <label v-if="label" for="id">{{ label }}</label>
48
+ <p v-if="hint" class="vui-input-hint">
49
+ {{ hint }}
50
+ </p>
51
+ <textarea
52
+ :id
53
+ v-model="model"
54
+ :readonly
55
+ name="id"
56
+ :placeholder
57
+ :required
58
+ :max="limit"
59
+ :style="{
60
+ resize: r,
61
+ // @ts-expect-error Early-adoption CSS attribute
62
+ fieldSizing: fS,
63
+ }"
64
+ />
65
+ </div>
66
+ <p v-if="limit" class="vui-input-limit">
67
+ {{ `${model.length}/${limit}` }}
68
+ </p>
69
+ <ul v-if="errors.length > 0" class="vui-input-errors">
70
+ <li v-for="err in errors" :key="err">
71
+ {{ err }}
72
+ </li>
73
+ </ul>
74
+ <slot name="after" />
75
+ </div>
76
+ </template>
@@ -1,208 +1,208 @@
1
- .vui-input-container {
2
- // Each component should have its own specification as it allows more granual
3
- // modification via CSS variables if needed
4
- --input-color-text-red: var(--color-text-red);
5
- --input-color-text: var(--color-text);
6
- --input-color-text-light: var(--color-text-light);
7
- --input-color-text-lighter: var(--color-text-lighter);
8
- --input-color-border: var(--color-border);
9
- --input-color-border-strong: var(--color-border-strong);
10
- --input-color-border-weak: var(--color-border-weak);
11
-
12
- --input-height: 34px;
13
- --input-padding: 8px;
14
- --textarea-padding: 8px;
15
-
16
- //
17
-
18
- width: 224px;
19
-
20
- &.disabled input,
21
- &.disabled textarea {
22
- cursor: not-allowed;
23
- }
24
-
25
- &.expand {
26
- width: 100%;
27
- }
28
-
29
- &.required .vui-input > label:after {
30
- content: '*';
31
- color: var(--input-color-text-red);
32
- }
33
-
34
- &.readonly .vui-input {
35
- input,
36
- textarea,
37
- .vui-input-style {
38
- pointer-events: none;
39
- color: var(--input-color-text-light);
40
- border-color: var(--input-color-border-weak);
41
- }
42
- }
43
-
44
- &.has-errors {
45
- .vui-input {
46
- .vui-input-style,
47
- textarea {
48
- border-color: var(--color-text-red) !important;
49
- }
50
- }
51
- }
52
-
53
- .vui-input {
54
- label {
55
- display: block;
56
- text-align: left;
57
- margin-bottom: 8px;
58
- font-size: var(--font-size-ms);
59
- color: var(--input-color-text);
60
- }
61
-
62
- .vui-input-hint {
63
- margin-bottom: 8px;
64
- margin-top: -4px;
65
- color: var(--input-color-text-lighter);
66
- font-size: var(--font-size-s);
67
- display: block;
68
- text-align: left;
69
- }
70
-
71
- ::placeholder {
72
- font-weight: 400;
73
- color: var(--color-text-lighter);
74
- font-family: var(--global-font);
75
- }
76
-
77
- .vui-input-style,
78
- textarea {
79
- display: block;
80
- border-radius: var(--border-radius-s);
81
- border: 1px solid var(--input-color-border);
82
- background: transparent;
83
- height: var(--input-height);
84
- line-height: var(--input-height);
85
- color: var(--color-text);
86
- outline: none !important;
87
- padding-inline: var(--input-padding);
88
- font-size: var(--font-size-ms);
89
- width: 100%;
90
- transition: var(--transition);
91
-
92
- &:has(input:focus-visible),
93
- &:focus-visible {
94
- border-color: var(--input-color-border-strong);
95
- }
96
- }
97
-
98
- input,
99
- textarea {
100
- font-family: var(--global-font);
101
- }
102
-
103
- input[type='file']::file-selector-button {
104
- background-color: var(--color-bg);
105
- color: var(--color-text);
106
- border: none;
107
- }
108
-
109
- input[type='range'] {
110
- -webkit-appearance: none; /* Override default CSS styles */
111
- appearance: none;
112
- height: 4px;
113
- border-radius: 2px;
114
- background-color: var(--color-border);
115
- }
116
-
117
- ::-moz-range-thumb,
118
- ::-webkit-slider-thumb {
119
- width: 16px;
120
- height: 16px;
121
- background-color: var(--color-text-light);
122
- border: none;
123
- }
124
-
125
- input {
126
- display: block;
127
- width: 100%;
128
- border: none;
129
- height: calc(var(--input-height) - 1px);
130
- line-height: calc(var(--input-height) - 1px);
131
- background: transparent;
132
- outline: none;
133
- font-size: var(--font-size-ms);
134
- color: var(--color-text);
135
-
136
- &:-webkit-autofill {
137
- box-shadow: 0 0 0px 1000px var(--color-bg) inset;
138
- -webkit-text-fill-color: var(--color-text);
139
- }
140
- }
141
-
142
- textarea {
143
- line-height: 1.3em;
144
- height: auto;
145
- min-height: 4lh;
146
- field-sizing: content;
147
- padding: var(--textarea-padding);
148
- transition: none;
149
- }
150
- }
151
-
152
- .vui-input-limit {
153
- display: block;
154
- margin-top: 6px;
155
- text-align: left;
156
- font-size: var(--font-size-xs);
157
- color: var(--input-color-text-lighter);
158
- }
159
-
160
- .vui-input-errors {
161
- display: flex;
162
- flex-direction: column;
163
- gap: 6px;
164
- padding-top: 6px;
165
-
166
- li {
167
- display: block;
168
- font-size: var(--font-size-s);
169
- color: var(--color-text-red);
170
- }
171
- }
172
-
173
- &.vui-dropzone {
174
- &.dragging .vui-input .vui-input-style {
175
- border-color: var(--color-border-accent);
176
-
177
- &:hover {
178
- border-color: var(--color-border);
179
- }
180
- }
181
- .vui-input {
182
- .vui-input-style {
183
- height: unset;
184
- border-width: 2px;
185
- border-style: dashed;
186
- border-radius: var(--border-radius-m);
187
- line-height: 1.2em;
188
-
189
- &:hover {
190
- border-color: var(--color-border-strong);
191
- }
192
-
193
- input {
194
- display: none;
195
-
196
- & + label {
197
- display: flex;
198
- align-items: center;
199
- justify-content: center;
200
- width: 100%;
201
- min-height: 96px;
202
- color: var(--color-text-light);
203
- }
204
- }
205
- }
206
- }
207
- }
208
- }
1
+ .vui-input-container {
2
+ // Each component should have its own specification as it allows more granual
3
+ // modification via CSS variables if needed
4
+ --input-color-text-red: var(--color-text-red);
5
+ --input-color-text: var(--color-text);
6
+ --input-color-text-light: var(--color-text-light);
7
+ --input-color-text-lighter: var(--color-text-lighter);
8
+ --input-color-border: var(--color-border);
9
+ --input-color-border-strong: var(--color-border-strong);
10
+ --input-color-border-weak: var(--color-border-weak);
11
+
12
+ --input-height: 34px;
13
+ --input-padding: 8px;
14
+ --textarea-padding: 8px;
15
+
16
+ //
17
+
18
+ width: 224px;
19
+
20
+ &.disabled input,
21
+ &.disabled textarea {
22
+ cursor: not-allowed;
23
+ }
24
+
25
+ &.expand {
26
+ width: 100%;
27
+ }
28
+
29
+ &.required .vui-input > label:after {
30
+ content: '*';
31
+ color: var(--input-color-text-red);
32
+ }
33
+
34
+ &.readonly .vui-input {
35
+ input,
36
+ textarea,
37
+ .vui-input-style {
38
+ pointer-events: none;
39
+ color: var(--input-color-text-light);
40
+ border-color: var(--input-color-border-weak);
41
+ }
42
+ }
43
+
44
+ &.has-errors {
45
+ .vui-input {
46
+ .vui-input-style,
47
+ textarea {
48
+ border-color: var(--color-text-red) !important;
49
+ }
50
+ }
51
+ }
52
+
53
+ .vui-input {
54
+ label {
55
+ display: block;
56
+ text-align: left;
57
+ margin-bottom: 8px;
58
+ font-size: var(--font-size-ms);
59
+ color: var(--input-color-text);
60
+ }
61
+
62
+ .vui-input-hint {
63
+ margin-bottom: 8px;
64
+ margin-top: -4px;
65
+ color: var(--input-color-text-lighter);
66
+ font-size: var(--font-size-s);
67
+ display: block;
68
+ text-align: left;
69
+ }
70
+
71
+ ::placeholder {
72
+ font-weight: 400;
73
+ color: var(--color-text-lighter);
74
+ font-family: var(--global-font);
75
+ }
76
+
77
+ .vui-input-style,
78
+ textarea {
79
+ display: block;
80
+ border-radius: var(--border-radius-s);
81
+ border: 1px solid var(--input-color-border);
82
+ background: transparent;
83
+ height: var(--input-height);
84
+ line-height: var(--input-height);
85
+ color: var(--color-text);
86
+ outline: none !important;
87
+ padding-inline: var(--input-padding);
88
+ font-size: var(--font-size-ms);
89
+ width: 100%;
90
+ transition: var(--transition);
91
+
92
+ &:has(input:focus-visible),
93
+ &:focus-visible {
94
+ border-color: var(--input-color-border-strong);
95
+ }
96
+ }
97
+
98
+ input,
99
+ textarea {
100
+ font-family: var(--global-font);
101
+ }
102
+
103
+ input[type='file']::file-selector-button {
104
+ background-color: var(--color-bg);
105
+ color: var(--color-text);
106
+ border: none;
107
+ }
108
+
109
+ input[type='range'] {
110
+ -webkit-appearance: none; /* Override default CSS styles */
111
+ appearance: none;
112
+ height: 4px;
113
+ border-radius: 2px;
114
+ background-color: var(--color-border);
115
+ }
116
+
117
+ ::-moz-range-thumb,
118
+ ::-webkit-slider-thumb {
119
+ width: 16px;
120
+ height: 16px;
121
+ background-color: var(--color-text-light);
122
+ border: none;
123
+ }
124
+
125
+ input {
126
+ display: block;
127
+ width: 100%;
128
+ border: none;
129
+ height: calc(var(--input-height) - 1px);
130
+ line-height: calc(var(--input-height) - 1px);
131
+ background: transparent;
132
+ outline: none;
133
+ font-size: var(--font-size-ms);
134
+ color: var(--color-text);
135
+
136
+ &:-webkit-autofill {
137
+ box-shadow: 0 0 0px 1000px var(--color-bg) inset;
138
+ -webkit-text-fill-color: var(--color-text);
139
+ }
140
+ }
141
+
142
+ textarea {
143
+ line-height: 1.3em;
144
+ height: auto;
145
+ min-height: 4lh;
146
+ field-sizing: content;
147
+ padding: var(--textarea-padding);
148
+ transition: none;
149
+ }
150
+ }
151
+
152
+ .vui-input-limit {
153
+ display: block;
154
+ margin-top: 6px;
155
+ text-align: left;
156
+ font-size: var(--font-size-xs);
157
+ color: var(--input-color-text-lighter);
158
+ }
159
+
160
+ .vui-input-errors {
161
+ display: flex;
162
+ flex-direction: column;
163
+ gap: 6px;
164
+ padding-top: 6px;
165
+
166
+ li {
167
+ display: block;
168
+ font-size: var(--font-size-s);
169
+ color: var(--color-text-red);
170
+ }
171
+ }
172
+
173
+ &.vui-dropzone {
174
+ &.dragging .vui-input .vui-input-style {
175
+ border-color: var(--color-border-accent);
176
+
177
+ &:hover {
178
+ border-color: var(--color-border);
179
+ }
180
+ }
181
+ .vui-input {
182
+ .vui-input-style {
183
+ height: unset;
184
+ border-width: 2px;
185
+ border-style: dashed;
186
+ border-radius: var(--border-radius-m);
187
+ line-height: 1.2em;
188
+
189
+ &:hover {
190
+ border-color: var(--color-border-strong);
191
+ }
192
+
193
+ input {
194
+ display: none;
195
+
196
+ & + label {
197
+ display: flex;
198
+ align-items: center;
199
+ justify-content: center;
200
+ width: 100%;
201
+ min-height: 96px;
202
+ color: var(--color-text-light);
203
+ }
204
+ }
205
+ }
206
+ }
207
+ }
208
+ }
@@ -1,48 +1,48 @@
1
- <script setup lang='ts'>
2
- import { useMagicKeys, whenever } from '@vueuse/core'
3
- import { computed } from 'vue'
4
- import './kbd.scss'
5
-
6
- interface Props {
7
- /**
8
- * Specify the key or the combination of keys connected with "+". Make sure
9
- * there are no white spaces between letters.
10
- *
11
- * keys="Escape" keys="Ctrl+A"
12
- */
13
- keys: string
14
- /**
15
- * Display custom label instead of the automatically formatted keys.
16
- */
17
- label?: string
18
- /**
19
- * Show active state when this combination of keys is pressed.
20
- */
21
- highlight?: boolean
22
- }
23
-
24
- const props = defineProps<Props>()
25
- const emits = defineEmits<{
26
- trigger: []
27
- }>()
28
- const keyHandler = useMagicKeys()
29
-
30
- whenever(keyHandler[props.keys], () => {
31
- emits('trigger')
32
- })
33
-
34
- const isActive = computed(() => {
35
- if (!props.highlight)
36
- return false
37
-
38
- return props.keys.split('+').every((key) => {
39
- return keyHandler.current.has(key.toLowerCase())
40
- })
41
- })
42
- </script>
43
-
44
- <template>
45
- <kbd class="vui-kbd" :class="{ active: isActive }">
46
- {{ props.label ?? props.keys.replaceAll("+", " + ") }}
47
- </kbd>
48
- </template>
1
+ <script setup lang='ts'>
2
+ import { useMagicKeys, whenever } from '@vueuse/core'
3
+ import { computed } from 'vue'
4
+ import './kbd.scss'
5
+
6
+ interface Props {
7
+ /**
8
+ * Specify the key or the combination of keys connected with "+". Make sure
9
+ * there are no white spaces between letters.
10
+ *
11
+ * keys="Escape" keys="Ctrl+A"
12
+ */
13
+ keys: string
14
+ /**
15
+ * Display custom label instead of the automatically formatted keys.
16
+ */
17
+ label?: string
18
+ /**
19
+ * Show active state when this combination of keys is pressed.
20
+ */
21
+ highlight?: boolean
22
+ }
23
+
24
+ const props = defineProps<Props>()
25
+ const emits = defineEmits<{
26
+ trigger: []
27
+ }>()
28
+ const keyHandler = useMagicKeys()
29
+
30
+ whenever(keyHandler[props.keys], () => {
31
+ emits('trigger')
32
+ })
33
+
34
+ const isActive = computed(() => {
35
+ if (!props.highlight)
36
+ return false
37
+
38
+ return props.keys.split('+').every((key) => {
39
+ return keyHandler.current.has(key.toLowerCase())
40
+ })
41
+ })
42
+ </script>
43
+
44
+ <template>
45
+ <kbd class="vui-kbd" :class="{ active: isActive }">
46
+ {{ props.label ?? props.keys.replaceAll("+", " + ") }}
47
+ </kbd>
48
+ </template>