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,77 @@
1
+ <template>
2
+ <label class="mikuru-slider">
3
+ <span class="slider-header">
4
+ <span>{{ label }}</span>
5
+ <output>{{ modelValue }}</output>
6
+ </span>
7
+ <input
8
+ type="range"
9
+ :min="min"
10
+ :max="max"
11
+ :step="step"
12
+ :value="modelValue"
13
+ :disabled="disabled"
14
+ @input="updateValue($event)"
15
+ />
16
+ <small m-if="help">{{ help }}</small>
17
+ </label>
18
+ </template>
19
+
20
+ <script>
21
+ const {
22
+ label = "Slider",
23
+ modelValue = 0,
24
+ min = 0,
25
+ max = 100,
26
+ step = 1,
27
+ help = "",
28
+ disabled = false
29
+ } = defineProps({
30
+ label: String,
31
+ modelValue: Number,
32
+ min: Number,
33
+ max: Number,
34
+ step: Number,
35
+ help: String,
36
+ disabled: Boolean
37
+ });
38
+
39
+ const emit = defineEmits(["update:modelValue", "change", "input"]);
40
+
41
+ function updateValue(event) {
42
+ const nextValue = Number(event.target.value);
43
+ emit("update:modelValue", nextValue);
44
+ emit("input", nextValue);
45
+ emit("change", nextValue);
46
+ }
47
+ </script>
48
+
49
+ <style scoped>
50
+ .mikuru-slider {
51
+ display: grid;
52
+ gap: 8px;
53
+ color: #111827;
54
+ font: inherit;
55
+ }
56
+
57
+ .slider-header {
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: space-between;
61
+ gap: 12px;
62
+ font-weight: 650;
63
+ }
64
+
65
+ output {
66
+ color: #2563eb;
67
+ }
68
+
69
+ input {
70
+ width: 100%;
71
+ accent-color: #2563eb;
72
+ }
73
+
74
+ small {
75
+ color: #64748b;
76
+ }
77
+ </style>
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <article class="mikuru-stat-card" :data-tone="tone">
3
+ <span>{{ label }}</span>
4
+ <strong>{{ value }}</strong>
5
+ <small m-if="detail">{{ detail }}</small>
6
+ </article>
7
+ </template>
8
+
9
+ <script>
10
+ const {
11
+ label = "Metric",
12
+ value = "0",
13
+ detail = "",
14
+ tone = "neutral"
15
+ } = defineProps({
16
+ label: String,
17
+ value: String,
18
+ detail: String,
19
+ tone: String
20
+ });
21
+ </script>
22
+
23
+ <style scoped>
24
+ .mikuru-stat-card {
25
+ display: grid;
26
+ gap: 6px;
27
+ border: 1px solid #e5e7eb;
28
+ border-radius: 8px;
29
+ padding: 14px;
30
+ color: #334155;
31
+ background: #ffffff;
32
+ }
33
+
34
+ span {
35
+ font-size: 0.85rem;
36
+ font-weight: 650;
37
+ }
38
+
39
+ strong {
40
+ color: #111827;
41
+ font-size: 1.65rem;
42
+ line-height: 1.1;
43
+ }
44
+
45
+ small {
46
+ color: #64748b;
47
+ }
48
+
49
+ .mikuru-stat-card[data-tone="success"] {
50
+ border-color: #bbf7d0;
51
+ background: #f0fdf4;
52
+ }
53
+
54
+ .mikuru-stat-card[data-tone="info"] {
55
+ border-color: #bfdbfe;
56
+ background: #eff6ff;
57
+ }
58
+
59
+ .mikuru-stat-card[data-tone="warning"] {
60
+ border-color: #fde68a;
61
+ background: #fffbeb;
62
+ }
63
+ </style>
@@ -0,0 +1,123 @@
1
+ <template>
2
+ <ol class="mikuru-stepper" :data-orientation="orientation">
3
+ <li
4
+ m-for="step in steps"
5
+ :key="stepKey(step)"
6
+ :data-state="stepState(step)"
7
+ >
8
+ <button type="button" @click="selectStep(step)">
9
+ <span class="step-index">{{ step.index || stepNumber(step) }}</span>
10
+ <span class="step-copy">
11
+ <strong>{{ step.label }}</strong>
12
+ <small m-if="step.description">{{ step.description }}</small>
13
+ </span>
14
+ </button>
15
+ </li>
16
+ </ol>
17
+ </template>
18
+
19
+ <script>
20
+ const {
21
+ steps = [],
22
+ current = "",
23
+ orientation = "horizontal"
24
+ } = defineProps({
25
+ steps: Array,
26
+ current: String,
27
+ orientation: String
28
+ });
29
+
30
+ const emit = defineEmits(["change"]);
31
+
32
+ function stepKey(step) {
33
+ return step.value || step.label;
34
+ }
35
+
36
+ function stepNumber(step) {
37
+ return steps.value.indexOf(step) + 1;
38
+ }
39
+
40
+ function stepState(step) {
41
+ if (step.value === current.value) return "current";
42
+ if (step.completed) return "complete";
43
+ return "pending";
44
+ }
45
+
46
+ function selectStep(step) {
47
+ emit("change", step.value, step);
48
+ }
49
+ </script>
50
+
51
+ <style scoped>
52
+ .mikuru-stepper {
53
+ display: flex;
54
+ gap: 10px;
55
+ margin: 0;
56
+ padding: 0;
57
+ list-style: none;
58
+ }
59
+
60
+ .mikuru-stepper[data-orientation="vertical"] {
61
+ flex-direction: column;
62
+ }
63
+
64
+ li {
65
+ flex: 1 1 0;
66
+ }
67
+
68
+ button {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: 10px;
72
+ width: 100%;
73
+ border: 1px solid #cbd5e1;
74
+ border-radius: 8px;
75
+ padding: 10px;
76
+ color: #334155;
77
+ background: #ffffff;
78
+ font: inherit;
79
+ text-align: left;
80
+ cursor: pointer;
81
+ }
82
+
83
+ li[data-state="current"] button {
84
+ border-color: #2563eb;
85
+ color: #1e3a8a;
86
+ background: #eff6ff;
87
+ }
88
+
89
+ li[data-state="complete"] button {
90
+ border-color: #86efac;
91
+ color: #166534;
92
+ background: #f0fdf4;
93
+ }
94
+
95
+ .step-index {
96
+ display: grid;
97
+ place-items: center;
98
+ width: 28px;
99
+ height: 28px;
100
+ flex: 0 0 auto;
101
+ border-radius: 999px;
102
+ color: #ffffff;
103
+ background: #64748b;
104
+ font-weight: 800;
105
+ }
106
+
107
+ li[data-state="current"] .step-index {
108
+ background: #2563eb;
109
+ }
110
+
111
+ li[data-state="complete"] .step-index {
112
+ background: #16a34a;
113
+ }
114
+
115
+ .step-copy {
116
+ display: grid;
117
+ gap: 2px;
118
+ }
119
+
120
+ small {
121
+ color: #64748b;
122
+ }
123
+ </style>
@@ -0,0 +1,104 @@
1
+ <template>
2
+ <label class="mikuru-switch" :data-disabled="disabled ? 'true' : 'false'">
3
+ <span class="switch-copy">
4
+ <span class="switch-label">{{ label }}</span>
5
+ <small m-if="help">{{ help }}</small>
6
+ </span>
7
+ <button
8
+ type="button"
9
+ role="switch"
10
+ class="switch-control"
11
+ :aria-checked="modelValue ? 'true' : 'false'"
12
+ :disabled="disabled"
13
+ @click="toggle"
14
+ >
15
+ <span></span>
16
+ </button>
17
+ </label>
18
+ </template>
19
+
20
+ <script>
21
+ const {
22
+ label = "Switch",
23
+ modelValue = false,
24
+ help = "",
25
+ disabled = false
26
+ } = defineProps({
27
+ label: String,
28
+ modelValue: Boolean,
29
+ help: String,
30
+ disabled: Boolean
31
+ });
32
+
33
+ const emit = defineEmits(["update:modelValue", "change"]);
34
+
35
+ function toggle() {
36
+ if (disabled.value) return;
37
+ const nextValue = !modelValue.value;
38
+ emit("update:modelValue", nextValue);
39
+ emit("change", nextValue);
40
+ }
41
+ </script>
42
+
43
+ <style scoped>
44
+ .mikuru-switch {
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: space-between;
48
+ gap: 14px;
49
+ color: #111827;
50
+ font: inherit;
51
+ }
52
+
53
+ .switch-copy {
54
+ display: grid;
55
+ gap: 3px;
56
+ }
57
+
58
+ .switch-label {
59
+ font-weight: 650;
60
+ }
61
+
62
+ small {
63
+ color: #64748b;
64
+ }
65
+
66
+ .switch-control {
67
+ position: relative;
68
+ width: 46px;
69
+ height: 26px;
70
+ flex: 0 0 auto;
71
+ border: 1px solid #cbd5e1;
72
+ border-radius: 999px;
73
+ padding: 2px;
74
+ background: #e2e8f0;
75
+ cursor: pointer;
76
+ }
77
+
78
+ .switch-control span {
79
+ display: block;
80
+ width: 20px;
81
+ height: 20px;
82
+ border-radius: 999px;
83
+ background: #ffffff;
84
+ box-shadow: 0 1px 3px rgb(15 23 42 / 24%);
85
+ transition: transform 140ms ease;
86
+ }
87
+
88
+ .switch-control[aria-checked="true"] {
89
+ border-color: #2563eb;
90
+ background: #2563eb;
91
+ }
92
+
93
+ .switch-control[aria-checked="true"] span {
94
+ transform: translateX(20px);
95
+ }
96
+
97
+ .switch-control:focus-visible {
98
+ outline: 3px solid rgb(37 99 235 / 18%);
99
+ }
100
+
101
+ .mikuru-switch[data-disabled="true"] {
102
+ opacity: 0.58;
103
+ }
104
+ </style>
@@ -0,0 +1,242 @@
1
+ <template>
2
+ <div class="mikuru-table-wrap">
3
+ <table class="mikuru-table">
4
+ <caption m-if="caption">{{ caption }}</caption>
5
+ <thead>
6
+ <tr>
7
+ <th
8
+ m-for="column in normalizedColumns"
9
+ :key="column.key"
10
+ :style="columnStyle(column)"
11
+ :aria-sort="ariaSort(column)"
12
+ >
13
+ <button
14
+ m-if="column.sortable"
15
+ type="button"
16
+ class="sort-button"
17
+ @click="sortBy(column)"
18
+ >
19
+ <span>{{ column.label }}</span>
20
+ <span aria-hidden="true">{{ sortMark(column) }}</span>
21
+ </button>
22
+ <span m-else>{{ column.label }}</span>
23
+ </th>
24
+ </tr>
25
+ </thead>
26
+ <tbody>
27
+ <tr m-if="loading">
28
+ <td :colspan="columnCount()">Loading...</td>
29
+ </tr>
30
+ <tr m-else-if="sortedRows.length === 0">
31
+ <td :colspan="columnCount()">{{ emptyText }}</td>
32
+ </tr>
33
+ <tr
34
+ m-for="row in sortedRows"
35
+ :key="rowKeyValue(row)"
36
+ @click="selectRow(row)"
37
+ >
38
+ <td
39
+ m-for="column in normalizedColumns"
40
+ :key="column.key"
41
+ :style="columnStyle(column)"
42
+ >
43
+ {{ cellValue(row, column) }}
44
+ </td>
45
+ </tr>
46
+ </tbody>
47
+ </table>
48
+ </div>
49
+ </template>
50
+
51
+ <script>
52
+ import { computed, ref, watch } from "mikuru";
53
+
54
+ const {
55
+ columns = [],
56
+ rows = [],
57
+ caption = "",
58
+ emptyText = "No rows",
59
+ loading = false,
60
+ rowKey = "id",
61
+ sortKey = "",
62
+ sortDirection = "asc"
63
+ } = defineProps({
64
+ columns: Array,
65
+ rows: Array,
66
+ caption: String,
67
+ emptyText: String,
68
+ loading: Boolean,
69
+ rowKey: String,
70
+ sortKey: String,
71
+ sortDirection: String
72
+ });
73
+
74
+ const emit = defineEmits(["row-click", "sort"]);
75
+ const activeSortKey = ref("");
76
+ const activeSortDirection = ref("asc");
77
+ const normalizedColumns = ref([]);
78
+ let columnsSignature = "";
79
+
80
+ watch(columns, syncColumns, { immediate: true });
81
+ watch(sortKey, syncSort, { immediate: true });
82
+ watch(sortDirection, syncSort, { immediate: true });
83
+
84
+ const sortedRows = computed(() => {
85
+ const source = Array.isArray(rows.value) ? rows.value.slice() : [];
86
+ const key = activeSortKey.value;
87
+ if (!key) return source;
88
+ const direction = activeSortDirection.value === "desc" ? -1 : 1;
89
+ return source.sort((left, right) => compareValues(left?.[key], right?.[key]) * direction);
90
+ });
91
+
92
+ function syncColumns() {
93
+ const source = Array.isArray(columns.value) ? columns.value : [];
94
+ const nextColumns = source.map((column, index) => {
95
+ if (typeof column === "string") {
96
+ return { key: column, label: column, sortable: false, align: "left", width: "" };
97
+ }
98
+ const key = String(column.key ?? column.value ?? index);
99
+ return {
100
+ key,
101
+ label: column.label || key,
102
+ sortable: Boolean(column.sortable),
103
+ align: column.align || "left",
104
+ width: column.width || ""
105
+ };
106
+ });
107
+ const nextSignature = nextColumns
108
+ .map((column) => `${column.key}\u0000${column.label}\u0000${column.sortable}\u0000${column.align}\u0000${column.width}`)
109
+ .join("\u0001");
110
+ if (nextSignature === columnsSignature) return;
111
+ columnsSignature = nextSignature;
112
+ normalizedColumns.value = nextColumns;
113
+ }
114
+
115
+ function syncSort() {
116
+ activeSortKey.value = sortKey.value || "";
117
+ activeSortDirection.value = sortDirection.value === "desc" ? "desc" : "asc";
118
+ }
119
+
120
+ function compareValues(left, right) {
121
+ if (left === right) return 0;
122
+ if (left == null) return -1;
123
+ if (right == null) return 1;
124
+ if (typeof left === "number" && typeof right === "number") {
125
+ return left - right;
126
+ }
127
+ return String(left).localeCompare(String(right), undefined, { numeric: true, sensitivity: "base" });
128
+ }
129
+
130
+ function sortBy(column) {
131
+ if (!column.sortable) return;
132
+ const nextDirection =
133
+ activeSortKey.value === column.key && activeSortDirection.value === "asc"
134
+ ? "desc"
135
+ : "asc";
136
+ activeSortKey.value = column.key;
137
+ activeSortDirection.value = nextDirection;
138
+ emit("sort", { key: column.key, direction: nextDirection });
139
+ }
140
+
141
+ function selectRow(row) {
142
+ emit("row-click", row);
143
+ }
144
+
145
+ function rowKeyValue(row) {
146
+ return row?.[rowKey.value] ?? JSON.stringify(row);
147
+ }
148
+
149
+ function cellValue(row, column) {
150
+ const value = row?.[column.key];
151
+ return value == null ? "" : String(value);
152
+ }
153
+
154
+ function columnStyle(column) {
155
+ const declarations = [];
156
+ if (column.align) declarations.push(`text-align: ${column.align}`);
157
+ if (column.width) declarations.push(`width: ${column.width}`);
158
+ return declarations.join("; ");
159
+ }
160
+
161
+ function columnCount() {
162
+ return Math.max(normalizedColumns.value.length, 1);
163
+ }
164
+
165
+ function sortMark(column) {
166
+ if (activeSortKey.value !== column.key) return "↕";
167
+ return activeSortDirection.value === "desc" ? "↓" : "↑";
168
+ }
169
+
170
+ function ariaSort(column) {
171
+ if (activeSortKey.value !== column.key) return "none";
172
+ return activeSortDirection.value === "desc" ? "descending" : "ascending";
173
+ }
174
+ </script>
175
+
176
+ <style scoped>
177
+ .mikuru-table-wrap {
178
+ width: 100%;
179
+ overflow: auto;
180
+ border: 1px solid #e5e7eb;
181
+ border-radius: 8px;
182
+ background: #ffffff;
183
+ }
184
+
185
+ .mikuru-table {
186
+ width: 100%;
187
+ border-collapse: collapse;
188
+ color: #111827;
189
+ font: inherit;
190
+ }
191
+
192
+ caption {
193
+ padding: 12px;
194
+ color: #475569;
195
+ text-align: left;
196
+ font-weight: 650;
197
+ }
198
+
199
+ th,
200
+ td {
201
+ border-bottom: 1px solid #e5e7eb;
202
+ padding: 11px 12px;
203
+ text-align: left;
204
+ white-space: nowrap;
205
+ }
206
+
207
+ th {
208
+ color: #475569;
209
+ background: #f8fafc;
210
+ font-size: 0.92rem;
211
+ font-weight: 700;
212
+ }
213
+
214
+ tbody tr {
215
+ cursor: pointer;
216
+ }
217
+
218
+ tbody tr:hover {
219
+ background: #f8fafc;
220
+ }
221
+
222
+ tbody tr:last-child td {
223
+ border-bottom: 0;
224
+ }
225
+
226
+ .sort-button {
227
+ display: inline-flex;
228
+ align-items: center;
229
+ gap: 6px;
230
+ border: 0;
231
+ padding: 0;
232
+ color: inherit;
233
+ background: transparent;
234
+ font: inherit;
235
+ font-weight: 700;
236
+ cursor: pointer;
237
+ }
238
+
239
+ .sort-button:focus-visible {
240
+ outline: 3px solid rgb(37 99 235 / 18%);
241
+ }
242
+ </style>