mikuru 1.0.37 → 1.0.39

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 (76) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/components/MikuruAlertDialog.mikuru +183 -0
  3. package/components/MikuruAvatar.mikuru +60 -0
  4. package/components/MikuruAvatarGroup.mikuru +66 -0
  5. package/components/MikuruBadge.mikuru +62 -0
  6. package/components/MikuruBreadcrumb.mikuru +86 -0
  7. package/components/MikuruCalendar.mikuru +142 -0
  8. package/components/MikuruChip.mikuru +64 -0
  9. package/components/MikuruCodeBlock.mikuru +20 -13
  10. package/components/MikuruCodeView.mikuru +21 -0
  11. package/components/MikuruColorPicker.mikuru +63 -0
  12. package/components/MikuruCommandPalette.mikuru +197 -0
  13. package/components/MikuruContextMenu.mikuru +137 -0
  14. package/components/MikuruDataList.mikuru +61 -0
  15. package/components/MikuruDatePicker.mikuru +293 -0
  16. package/components/MikuruDrawer.mikuru +115 -0
  17. package/components/MikuruEmptyState.mikuru +72 -0
  18. package/components/MikuruFileUpload.mikuru +161 -0
  19. package/components/MikuruKbd.mikuru +28 -0
  20. package/components/MikuruMarkdownEditor.mikuru +561 -0
  21. package/components/MikuruPagination.mikuru +109 -0
  22. package/components/MikuruPopover.mikuru +152 -0
  23. package/components/MikuruRadioGroup.mikuru +111 -0
  24. package/components/MikuruRangeSlider.mikuru +96 -0
  25. package/components/MikuruRating.mikuru +72 -0
  26. package/components/MikuruSearchInput.mikuru +97 -0
  27. package/components/MikuruSegmentedControl.mikuru +70 -0
  28. package/components/MikuruSkeleton.mikuru +74 -0
  29. package/components/MikuruSlider.mikuru +77 -0
  30. package/components/MikuruStatCard.mikuru +63 -0
  31. package/components/MikuruStepper.mikuru +123 -0
  32. package/components/MikuruSwitch.mikuru +104 -0
  33. package/components/MikuruTable.mikuru +242 -0
  34. package/components/MikuruTagInput.mikuru +127 -0
  35. package/components/MikuruTimePicker.mikuru +61 -0
  36. package/components/MikuruTimeline.mikuru +93 -0
  37. package/components/MikuruTreeView.mikuru +72 -0
  38. package/components/MikuruVideoPlayer.mikuru +44 -13
  39. package/components/MikuruWysiwygEditor.mikuru +259 -0
  40. package/package.json +289 -1
  41. package/types/components/MikuruAlertDialog.d.ts +16 -0
  42. package/types/components/MikuruAvatar.d.ts +12 -0
  43. package/types/components/MikuruAvatarGroup.d.ts +19 -0
  44. package/types/components/MikuruBadge.d.ts +11 -0
  45. package/types/components/MikuruBreadcrumb.d.ts +16 -0
  46. package/types/components/MikuruCalendar.d.ts +11 -0
  47. package/types/components/MikuruChip.d.ts +12 -0
  48. package/types/components/MikuruCodeView.d.ts +11 -0
  49. package/types/components/MikuruColorPicker.d.ts +11 -0
  50. package/types/components/MikuruCommandPalette.d.ts +20 -0
  51. package/types/components/MikuruContextMenu.d.ts +18 -0
  52. package/types/components/MikuruDataList.d.ts +17 -0
  53. package/types/components/MikuruDatePicker.d.ts +12 -0
  54. package/types/components/MikuruDrawer.d.ts +14 -0
  55. package/types/components/MikuruEmptyState.d.ts +12 -0
  56. package/types/components/MikuruFileUpload.d.ts +14 -0
  57. package/types/components/MikuruKbd.d.ts +9 -0
  58. package/types/components/MikuruMarkdownEditor.d.ts +15 -0
  59. package/types/components/MikuruPagination.d.ts +12 -0
  60. package/types/components/MikuruPopover.d.ts +13 -0
  61. package/types/components/MikuruRadioGroup.d.ts +21 -0
  62. package/types/components/MikuruRangeSlider.d.ts +15 -0
  63. package/types/components/MikuruRating.d.ts +13 -0
  64. package/types/components/MikuruSearchInput.d.ts +12 -0
  65. package/types/components/MikuruSegmentedControl.d.ts +18 -0
  66. package/types/components/MikuruSkeleton.d.ts +13 -0
  67. package/types/components/MikuruSlider.d.ts +15 -0
  68. package/types/components/MikuruStatCard.d.ts +12 -0
  69. package/types/components/MikuruStepper.d.ts +19 -0
  70. package/types/components/MikuruSwitch.d.ts +12 -0
  71. package/types/components/MikuruTable.d.ts +27 -0
  72. package/types/components/MikuruTagInput.d.ts +13 -0
  73. package/types/components/MikuruTimePicker.d.ts +12 -0
  74. package/types/components/MikuruTimeline.d.ts +17 -0
  75. package/types/components/MikuruTreeView.d.ts +17 -0
  76. package/types/components/MikuruWysiwygEditor.d.ts +12 -0
@@ -0,0 +1,293 @@
1
+ <template>
2
+ <div class="mikuru-date-picker" ref="rootEl" @keydown="handleKeydown">
3
+ <label>
4
+ <span>{{ label }}</span>
5
+ <button
6
+ class="date-trigger"
7
+ type="button"
8
+ :aria-expanded="isOpen ? 'true' : 'false'"
9
+ aria-haspopup="dialog"
10
+ @click="toggle"
11
+ >
12
+ {{ displayValue }}
13
+ </button>
14
+ </label>
15
+
16
+ <section m-if="isOpen" class="calendar-panel" role="dialog" :aria-label="label">
17
+ <header>
18
+ <button type="button" aria-label="Previous month" @click="previousMonth">‹</button>
19
+ <strong>{{ monthLabel }}</strong>
20
+ <button type="button" aria-label="Next month" @click="nextMonth">›</button>
21
+ </header>
22
+
23
+ <div class="weekday-row" aria-hidden="true">
24
+ <span m-for="day in weekdays" :key="day">{{ day }}</span>
25
+ </div>
26
+
27
+ <div class="day-grid">
28
+ <button
29
+ m-for="day in calendarDays"
30
+ :key="day.key"
31
+ type="button"
32
+ class="day-button"
33
+ :class="{ muted: !day.inMonth, selected: day.value === selectedValue, today: day.value === todayValue }"
34
+ @click="selectDate(day)"
35
+ >
36
+ {{ day.label }}
37
+ </button>
38
+ </div>
39
+ </section>
40
+ </div>
41
+ </template>
42
+
43
+ <script>
44
+ import { computed, onMounted, onUnmounted, ref, watch } from "mikuru";
45
+
46
+ const {
47
+ label = "Date",
48
+ modelValue = "",
49
+ placeholder = "Select date",
50
+ locale = "en-US"
51
+ } = defineProps({
52
+ label: String,
53
+ modelValue: String,
54
+ placeholder: String,
55
+ locale: String
56
+ });
57
+
58
+ const emit = defineEmits(["update:modelValue", "change", "open", "close"]);
59
+ const rootEl = ref(null);
60
+ const isOpen = ref(false);
61
+ const selectedValue = ref("");
62
+ const viewYear = ref(new Date().getFullYear());
63
+ const viewMonth = ref(new Date().getMonth());
64
+ const weekdays = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
65
+ const todayValue = formatDateValue(new Date());
66
+
67
+ watch(modelValue, syncSelectedDate, { immediate: true });
68
+
69
+ const displayValue = computed(() => {
70
+ if (!selectedValue.value) return placeholder.value;
71
+ return formatDateLabel(parseDateValue(selectedValue.value));
72
+ });
73
+
74
+ const monthLabel = computed(() => {
75
+ const date = new Date(viewYear.value, viewMonth.value, 1);
76
+ return date.toLocaleDateString(locale.value || "en-US", { month: "long", year: "numeric" });
77
+ });
78
+
79
+ const calendarDays = computed(() => {
80
+ const firstDay = new Date(viewYear.value, viewMonth.value, 1);
81
+ const startOffset = firstDay.getDay();
82
+ const startDate = new Date(viewYear.value, viewMonth.value, 1 - startOffset);
83
+ const days = [];
84
+ for (let index = 0; index < 42; index += 1) {
85
+ const date = new Date(startDate);
86
+ date.setDate(startDate.getDate() + index);
87
+ const value = formatDateValue(date);
88
+ days.push({
89
+ key: value,
90
+ value,
91
+ label: String(date.getDate()),
92
+ inMonth: date.getMonth() === viewMonth.value
93
+ });
94
+ }
95
+ return days;
96
+ });
97
+
98
+ onMounted(() => {
99
+ document.addEventListener("pointerdown", handleDocumentPointer);
100
+ });
101
+
102
+ onUnmounted(() => {
103
+ document.removeEventListener("pointerdown", handleDocumentPointer);
104
+ });
105
+
106
+ function syncSelectedDate() {
107
+ selectedValue.value = normalizeDateValue(modelValue.value);
108
+ const selected = parseDateValue(selectedValue.value);
109
+ if (!selected) return;
110
+ viewYear.value = selected.getFullYear();
111
+ viewMonth.value = selected.getMonth();
112
+ }
113
+
114
+ function open() {
115
+ if (isOpen.value) return;
116
+ isOpen.value = true;
117
+ emit("open");
118
+ }
119
+
120
+ function close() {
121
+ if (!isOpen.value) return;
122
+ isOpen.value = false;
123
+ emit("close");
124
+ }
125
+
126
+ function toggle() {
127
+ if (isOpen.value) {
128
+ close();
129
+ return;
130
+ }
131
+ open();
132
+ }
133
+
134
+ function selectDate(day) {
135
+ selectedValue.value = day.value;
136
+ emit("update:modelValue", day.value);
137
+ emit("change", day.value);
138
+ close();
139
+ }
140
+
141
+ function previousMonth() {
142
+ const date = new Date(viewYear.value, viewMonth.value - 1, 1);
143
+ viewYear.value = date.getFullYear();
144
+ viewMonth.value = date.getMonth();
145
+ }
146
+
147
+ function nextMonth() {
148
+ const date = new Date(viewYear.value, viewMonth.value + 1, 1);
149
+ viewYear.value = date.getFullYear();
150
+ viewMonth.value = date.getMonth();
151
+ }
152
+
153
+ function handleDocumentPointer(event) {
154
+ const root = rootEl.value;
155
+ if (!root || root.contains(event.target)) return;
156
+ close();
157
+ }
158
+
159
+ function handleKeydown(event) {
160
+ if (event.key === "Escape") {
161
+ close();
162
+ }
163
+ }
164
+
165
+ function normalizeDateValue(value) {
166
+ const parsed = parseDateValue(value);
167
+ return parsed ? formatDateValue(parsed) : "";
168
+ }
169
+
170
+ function parseDateValue(value) {
171
+ if (!value || typeof value !== "string") return null;
172
+ const match = value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
173
+ if (!match) return null;
174
+ const date = new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
175
+ return Number.isNaN(date.getTime()) ? null : date;
176
+ }
177
+
178
+ function formatDateValue(date) {
179
+ const year = date.getFullYear();
180
+ const month = String(date.getMonth() + 1).padStart(2, "0");
181
+ const day = String(date.getDate()).padStart(2, "0");
182
+ return `${year}-${month}-${day}`;
183
+ }
184
+
185
+ function formatDateLabel(date) {
186
+ if (!date) return placeholder.value;
187
+ return date.toLocaleDateString(locale.value || "en-US", {
188
+ year: "numeric",
189
+ month: "short",
190
+ day: "numeric"
191
+ });
192
+ }
193
+ </script>
194
+
195
+ <style scoped>
196
+ .mikuru-date-picker {
197
+ position: relative;
198
+ display: inline-block;
199
+ color: #111827;
200
+ font: inherit;
201
+ }
202
+
203
+ label {
204
+ display: grid;
205
+ gap: 6px;
206
+ font-weight: 650;
207
+ }
208
+
209
+ .date-trigger {
210
+ min-width: 190px;
211
+ border: 1px solid #cbd5e1;
212
+ border-radius: 8px;
213
+ padding: 10px 12px;
214
+ color: #111827;
215
+ background: #ffffff;
216
+ text-align: left;
217
+ font: inherit;
218
+ cursor: pointer;
219
+ }
220
+
221
+ .calendar-panel {
222
+ position: absolute;
223
+ top: calc(100% + 8px);
224
+ left: 0;
225
+ z-index: 45;
226
+ display: grid;
227
+ gap: 10px;
228
+ width: 286px;
229
+ border: 1px solid #e5e7eb;
230
+ border-radius: 8px;
231
+ padding: 12px;
232
+ background: #ffffff;
233
+ box-shadow: 0 18px 48px rgb(15 23 42 / 16%);
234
+ }
235
+
236
+ header,
237
+ .weekday-row,
238
+ .day-grid {
239
+ display: grid;
240
+ grid-template-columns: repeat(7, minmax(0, 1fr));
241
+ gap: 4px;
242
+ }
243
+
244
+ header {
245
+ grid-template-columns: 34px 1fr 34px;
246
+ align-items: center;
247
+ }
248
+
249
+ header strong {
250
+ text-align: center;
251
+ }
252
+
253
+ button {
254
+ border: 1px solid transparent;
255
+ border-radius: 8px;
256
+ color: #111827;
257
+ background: #ffffff;
258
+ font: inherit;
259
+ cursor: pointer;
260
+ }
261
+
262
+ header button,
263
+ .day-button {
264
+ min-height: 34px;
265
+ }
266
+
267
+ .weekday-row span {
268
+ color: #64748b;
269
+ text-align: center;
270
+ font-size: 0.82rem;
271
+ }
272
+
273
+ .day-button:hover,
274
+ .day-button:focus-visible,
275
+ .date-trigger:focus-visible {
276
+ border-color: #2563eb;
277
+ outline: 3px solid rgb(37 99 235 / 16%);
278
+ }
279
+
280
+ .day-button.muted {
281
+ color: #94a3b8;
282
+ }
283
+
284
+ .day-button.today {
285
+ border-color: #93c5fd;
286
+ }
287
+
288
+ .day-button.selected {
289
+ border-color: #2563eb;
290
+ color: #ffffff;
291
+ background: #2563eb;
292
+ }
293
+ </style>
@@ -0,0 +1,115 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <div m-if="open" class="drawer-layer" :data-side="side">
4
+ <button
5
+ class="drawer-backdrop"
6
+ type="button"
7
+ aria-label="Close drawer"
8
+ @click="handleBackdrop"
9
+ ></button>
10
+ <aside class="mikuru-drawer" role="dialog" aria-modal="true" :aria-label="title || 'Drawer'">
11
+ <header class="drawer-header">
12
+ <strong>{{ title }}</strong>
13
+ <button type="button" class="drawer-close" @click="close">{{ closeLabel }}</button>
14
+ </header>
15
+ <div class="drawer-body">
16
+ <slot>{{ body }}</slot>
17
+ </div>
18
+ </aside>
19
+ </div>
20
+ </Teleport>
21
+ </template>
22
+
23
+ <script>
24
+ const {
25
+ open = false,
26
+ title = "Drawer",
27
+ body = "",
28
+ side = "right",
29
+ closeLabel = "Close",
30
+ closeOnBackdrop = true
31
+ } = defineProps({
32
+ open: Boolean,
33
+ title: String,
34
+ body: String,
35
+ side: String,
36
+ closeLabel: String,
37
+ closeOnBackdrop: Boolean
38
+ });
39
+
40
+ const emit = defineEmits(["close"]);
41
+
42
+ function close() {
43
+ emit("close");
44
+ }
45
+
46
+ function handleBackdrop() {
47
+ if (closeOnBackdrop.value) close();
48
+ }
49
+ </script>
50
+
51
+ <style scoped>
52
+ .drawer-layer {
53
+ position: fixed;
54
+ inset: 0;
55
+ z-index: 70;
56
+ display: flex;
57
+ }
58
+
59
+ .drawer-layer[data-side="right"] {
60
+ justify-content: flex-end;
61
+ }
62
+
63
+ .drawer-layer[data-side="left"] {
64
+ justify-content: flex-start;
65
+ }
66
+
67
+ .drawer-backdrop {
68
+ position: absolute;
69
+ inset: 0;
70
+ border: 0;
71
+ background: rgb(15 23 42 / 46%);
72
+ cursor: pointer;
73
+ }
74
+
75
+ .mikuru-drawer {
76
+ position: relative;
77
+ z-index: 1;
78
+ display: grid;
79
+ grid-template-rows: auto 1fr;
80
+ width: min(420px, 92vw);
81
+ height: 100%;
82
+ color: #111827;
83
+ background: #ffffff;
84
+ box-shadow: 0 24px 80px rgb(15 23 42 / 24%);
85
+ }
86
+
87
+ .drawer-header {
88
+ display: flex;
89
+ align-items: center;
90
+ justify-content: space-between;
91
+ gap: 12px;
92
+ border-bottom: 1px solid #e5e7eb;
93
+ padding: 16px;
94
+ }
95
+
96
+ .drawer-close {
97
+ border: 1px solid #cbd5e1;
98
+ border-radius: 8px;
99
+ padding: 7px 10px;
100
+ color: #111827;
101
+ background: #ffffff;
102
+ font: inherit;
103
+ cursor: pointer;
104
+ }
105
+
106
+ .drawer-close:focus-visible {
107
+ outline: 3px solid rgb(37 99 235 / 18%);
108
+ }
109
+
110
+ .drawer-body {
111
+ overflow: auto;
112
+ padding: 16px;
113
+ line-height: 1.6;
114
+ }
115
+ </style>
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <section class="mikuru-empty-state">
3
+ <div class="empty-icon" aria-hidden="true">{{ icon }}</div>
4
+ <h3>{{ title }}</h3>
5
+ <p>{{ description }}</p>
6
+ <button m-if="actionLabel" type="button" @click="emitAction">{{ actionLabel }}</button>
7
+ </section>
8
+ </template>
9
+
10
+ <script>
11
+ const {
12
+ icon = "∅",
13
+ title = "No results",
14
+ description = "There is nothing to show yet.",
15
+ actionLabel = ""
16
+ } = defineProps({
17
+ icon: String,
18
+ title: String,
19
+ description: String,
20
+ actionLabel: String
21
+ });
22
+
23
+ const emit = defineEmits(["action"]);
24
+
25
+ function emitAction() {
26
+ emit("action");
27
+ }
28
+ </script>
29
+
30
+ <style scoped>
31
+ .mikuru-empty-state {
32
+ display: grid;
33
+ gap: 8px;
34
+ justify-items: center;
35
+ border: 1px solid #e5e7eb;
36
+ border-radius: 8px;
37
+ padding: 24px;
38
+ color: #334155;
39
+ background: #ffffff;
40
+ text-align: center;
41
+ }
42
+
43
+ .empty-icon {
44
+ display: grid;
45
+ place-items: center;
46
+ width: 44px;
47
+ height: 44px;
48
+ border-radius: 999px;
49
+ color: #2563eb;
50
+ background: #eff6ff;
51
+ font-weight: 800;
52
+ }
53
+
54
+ h3,
55
+ p {
56
+ margin: 0;
57
+ }
58
+
59
+ h3 {
60
+ color: #111827;
61
+ }
62
+
63
+ button {
64
+ border: 1px solid #2563eb;
65
+ border-radius: 8px;
66
+ padding: 8px 12px;
67
+ color: #ffffff;
68
+ background: #2563eb;
69
+ font: inherit;
70
+ cursor: pointer;
71
+ }
72
+ </style>
@@ -0,0 +1,161 @@
1
+ <template>
2
+ <section class="mikuru-file-upload" :data-dragging="dragging ? 'true' : 'false'">
3
+ <label :for="inputId">{{ label }}</label>
4
+ <div
5
+ class="drop-zone"
6
+ @dragover="handleDragOver"
7
+ @dragleave="handleDragLeave"
8
+ @drop="handleDrop"
9
+ >
10
+ <input
11
+ :id="inputId"
12
+ type="file"
13
+ :accept="accept"
14
+ :multiple="multiple"
15
+ :disabled="disabled"
16
+ @change="handleInput"
17
+ />
18
+ <span>{{ prompt }}</span>
19
+ <small m-if="help">{{ help }}</small>
20
+ </div>
21
+ <ul m-if="fileItems.length">
22
+ <li m-for="file in fileItems" :key="file.name">
23
+ <span>{{ file.name }}</span>
24
+ <small>{{ file.size }}</small>
25
+ </li>
26
+ </ul>
27
+ <button m-if="fileItems.length" type="button" @click="clear">Clear files</button>
28
+ </section>
29
+ </template>
30
+
31
+ <script>
32
+ import { ref } from "mikuru";
33
+
34
+ const {
35
+ label = "Upload files",
36
+ prompt = "Choose files or drop them here",
37
+ help = "",
38
+ accept = "",
39
+ multiple = false,
40
+ disabled = false
41
+ } = defineProps({
42
+ label: String,
43
+ prompt: String,
44
+ help: String,
45
+ accept: String,
46
+ multiple: Boolean,
47
+ disabled: Boolean
48
+ });
49
+
50
+ const emit = defineEmits(["change", "clear"]);
51
+ const inputId = `mikuru-file-${Math.random().toString(36).slice(2)}`;
52
+ const dragging = ref(false);
53
+ const fileItems = ref([]);
54
+
55
+ function handleInput(event) {
56
+ setFiles(event.target.files || []);
57
+ }
58
+
59
+ function handleDragOver(event) {
60
+ if (disabled.value) return;
61
+ event.preventDefault();
62
+ dragging.value = true;
63
+ }
64
+
65
+ function handleDragLeave() {
66
+ dragging.value = false;
67
+ }
68
+
69
+ function handleDrop(event) {
70
+ if (disabled.value) return;
71
+ event.preventDefault();
72
+ dragging.value = false;
73
+ setFiles(event.dataTransfer?.files || []);
74
+ }
75
+
76
+ function setFiles(fileList) {
77
+ const files = Array.from(fileList);
78
+ fileItems.value = files.map((file) => ({
79
+ name: file.name,
80
+ size: formatSize(file.size)
81
+ }));
82
+ emit("change", files);
83
+ }
84
+
85
+ function clear() {
86
+ fileItems.value = [];
87
+ emit("clear");
88
+ }
89
+
90
+ function formatSize(size) {
91
+ if (size < 1024) return `${size} B`;
92
+ if (size < 1024 * 1024) return `${Math.round(size / 102.4) / 10} KB`;
93
+ return `${Math.round(size / 1024 / 102.4) / 10} MB`;
94
+ }
95
+ </script>
96
+
97
+ <style scoped>
98
+ .mikuru-file-upload {
99
+ display: grid;
100
+ gap: 8px;
101
+ color: #111827;
102
+ font: inherit;
103
+ }
104
+
105
+ label {
106
+ font-weight: 650;
107
+ }
108
+
109
+ .drop-zone {
110
+ display: grid;
111
+ gap: 6px;
112
+ justify-items: center;
113
+ border: 1px dashed #94a3b8;
114
+ border-radius: 8px;
115
+ padding: 18px;
116
+ background: #f8fafc;
117
+ text-align: center;
118
+ }
119
+
120
+ .mikuru-file-upload[data-dragging="true"] .drop-zone {
121
+ border-color: #2563eb;
122
+ background: #eff6ff;
123
+ }
124
+
125
+ input {
126
+ max-width: 100%;
127
+ }
128
+
129
+ small {
130
+ color: #64748b;
131
+ }
132
+
133
+ ul {
134
+ display: grid;
135
+ gap: 6px;
136
+ margin: 0;
137
+ padding: 0;
138
+ list-style: none;
139
+ }
140
+
141
+ li {
142
+ display: flex;
143
+ justify-content: space-between;
144
+ gap: 10px;
145
+ border: 1px solid #e5e7eb;
146
+ border-radius: 8px;
147
+ padding: 8px 10px;
148
+ background: #ffffff;
149
+ }
150
+
151
+ button {
152
+ width: fit-content;
153
+ border: 1px solid #cbd5e1;
154
+ border-radius: 8px;
155
+ padding: 7px 10px;
156
+ color: #111827;
157
+ background: #ffffff;
158
+ font: inherit;
159
+ cursor: pointer;
160
+ }
161
+ </style>
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <kbd class="mikuru-kbd"><slot>{{ label }}</slot></kbd>
3
+ </template>
4
+
5
+ <script>
6
+ const {
7
+ label = "Ctrl"
8
+ } = defineProps({
9
+ label: String
10
+ });
11
+ </script>
12
+
13
+ <style scoped>
14
+ .mikuru-kbd {
15
+ display: inline-flex;
16
+ align-items: center;
17
+ min-width: 1.6em;
18
+ min-height: 1.55em;
19
+ border: 1px solid #cbd5e1;
20
+ border-bottom-color: #94a3b8;
21
+ border-radius: 6px;
22
+ padding: 1px 6px;
23
+ color: #111827;
24
+ background: #f8fafc;
25
+ box-shadow: inset 0 -1px 0 #cbd5e1;
26
+ font: 0.82em/1.2 ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace;
27
+ }
28
+ </style>