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
@@ -4,13 +4,12 @@
4
4
  <span>{{ languageLabel }}</span>
5
5
  <button type="button" @click="copyCode">{{ copyLabel }}</button>
6
6
  </figcaption>
7
- <pre><code><span m-for="line in lines" :key="line.number" class="code-line"><span m-if="showLineNumbers" class="line-number">{{ line.number }}</span><span m-html="line.html"></span>
8
- </span></code></pre>
7
+ <pre><code m-html="codeHtml"></code></pre>
9
8
  </figure>
10
9
  </template>
11
10
 
12
11
  <script>
13
- import { computed, ref, watch } from "mikuru";
12
+ import { computed, ref } from "mikuru";
14
13
 
15
14
  const {
16
15
  code = "",
@@ -23,16 +22,9 @@ const {
23
22
  });
24
23
 
25
24
  const copied = ref(false);
26
- const lines = ref([]);
27
25
  const languageLabel = computed(() => language.value || "text");
28
26
  const copyLabel = computed(() => copied.value ? "Copied" : "Copy");
29
-
30
- watch([code, language], () => {
31
- lines.value = code.value.split("\n").map((text, index) => ({
32
- number: index + 1,
33
- html: highlightLine(text, language.value)
34
- }));
35
- }, { immediate: true });
27
+ const codeHtml = computed(() => renderCodeHtml(code.value, language.value, showLineNumbers.value));
36
28
 
37
29
  async function copyCode() {
38
30
  if (!navigator.clipboard) return;
@@ -60,6 +52,13 @@ function highlightLine(line, lang) {
60
52
  return escapeHtml(line);
61
53
  }
62
54
 
55
+ function renderCodeHtml(source, lang, withLineNumbers) {
56
+ return String(source || "").split("\n").map((text, index) => {
57
+ const lineNumber = withLineNumbers ? `<span class="line-number">${index + 1}</span>` : "";
58
+ return `<span class="code-line">${lineNumber}<span>${highlightLine(text, lang)}</span></span>`;
59
+ }).join("");
60
+ }
61
+
63
62
  function highlightScript(line) {
64
63
  const keywords = new Set([
65
64
  "async", "await", "break", "case", "catch", "class", "const", "continue", "default", "do", "else",
@@ -283,7 +282,15 @@ pre {
283
282
  font: 0.9rem/1.55 ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace;
284
283
  }
285
284
 
286
- .code-line {
285
+ .mikuru-code-block pre code {
286
+ display: block;
287
+ padding: 0;
288
+ border-radius: 0;
289
+ background: transparent;
290
+ color: inherit;
291
+ }
292
+
293
+ :deep(.code-line) {
287
294
  display: grid;
288
295
  grid-template-columns: auto 1fr;
289
296
  min-width: max-content;
@@ -291,7 +298,7 @@ pre {
291
298
  white-space: pre;
292
299
  }
293
300
 
294
- .line-number {
301
+ :deep(.line-number) {
295
302
  min-width: 3ch;
296
303
  margin-right: 14px;
297
304
  color: #64748b;
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <MikuruCodeBlock
3
+ :code="code"
4
+ :language="language"
5
+ :showLineNumbers="showLineNumbers"
6
+ />
7
+ </template>
8
+
9
+ <script>
10
+ import MikuruCodeBlock from "./MikuruCodeBlock.mikuru";
11
+
12
+ const {
13
+ code = "",
14
+ language = "text",
15
+ showLineNumbers = true
16
+ } = defineProps({
17
+ code: String,
18
+ language: String,
19
+ showLineNumbers: Boolean
20
+ });
21
+ </script>
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <label class="mikuru-color-picker">
3
+ <span>{{ label }}</span>
4
+ <span class="color-row">
5
+ <input type="color" :value="modelValue" :disabled="disabled" @input="updateValue($event)" />
6
+ <code>{{ modelValue }}</code>
7
+ </span>
8
+ </label>
9
+ </template>
10
+
11
+ <script>
12
+ const {
13
+ label = "Color",
14
+ modelValue = "#2563eb",
15
+ disabled = false
16
+ } = defineProps({
17
+ label: String,
18
+ modelValue: String,
19
+ disabled: Boolean
20
+ });
21
+
22
+ const emit = defineEmits(["update:modelValue", "change"]);
23
+
24
+ function updateValue(event) {
25
+ emit("update:modelValue", event.target.value);
26
+ emit("change", event.target.value);
27
+ }
28
+ </script>
29
+
30
+ <style scoped>
31
+ .mikuru-color-picker {
32
+ display: grid;
33
+ gap: 6px;
34
+ color: #111827;
35
+ font: inherit;
36
+ }
37
+
38
+ .mikuru-color-picker > span:first-child {
39
+ font-weight: 650;
40
+ }
41
+
42
+ .color-row {
43
+ display: flex;
44
+ align-items: center;
45
+ gap: 10px;
46
+ }
47
+
48
+ input {
49
+ width: 44px;
50
+ height: 34px;
51
+ border: 1px solid #cbd5e1;
52
+ border-radius: 8px;
53
+ padding: 2px;
54
+ background: #ffffff;
55
+ }
56
+
57
+ code {
58
+ border-radius: 6px;
59
+ padding: 3px 7px;
60
+ color: #334155;
61
+ background: #f1f5f9;
62
+ }
63
+ </style>
@@ -0,0 +1,197 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <section m-if="isOpen" class="command-layer" role="dialog" aria-modal="true" aria-label="Command palette">
4
+ <button class="command-backdrop" type="button" aria-label="Close command palette" @click="close"></button>
5
+ <div class="mikuru-command-palette">
6
+ <input
7
+ ref="inputEl"
8
+ :placeholder="placeholder"
9
+ :value="query"
10
+ @input="updateQuery($event)"
11
+ @keydown="handleInputKeydown"
12
+ />
13
+ <ul>
14
+ <li m-for="command in filteredCommands" :key="command.id || command.label">
15
+ <button type="button" @click="selectCommand(command)">
16
+ <span>{{ command.label }}</span>
17
+ <small m-if="command.description">{{ command.description }}</small>
18
+ </button>
19
+ </li>
20
+ </ul>
21
+ <p m-if="!filteredCommands.length">{{ emptyText }}</p>
22
+ </div>
23
+ </section>
24
+ </Teleport>
25
+ </template>
26
+
27
+ <script>
28
+ import { computed, onMounted, onUnmounted, queueJob, ref, watch } from "mikuru";
29
+
30
+ const {
31
+ open = false,
32
+ commands = [],
33
+ placeholder = "Search commands...",
34
+ emptyText = "No commands found",
35
+ shortcut = true
36
+ } = defineProps({
37
+ open: Boolean,
38
+ commands: Array,
39
+ placeholder: String,
40
+ emptyText: String,
41
+ shortcut: Boolean
42
+ });
43
+
44
+ const emit = defineEmits(["open", "close", "select", "update:open"]);
45
+ const internalOpen = ref(false);
46
+ const query = ref("");
47
+ const inputEl = ref(null);
48
+ const isOpen = computed(() => open.value || internalOpen.value);
49
+ const filteredCommands = computed(() => {
50
+ const term = query.value.trim().toLowerCase();
51
+ if (!term) return commands.value;
52
+ return commands.value.filter((command) => {
53
+ return `${command.label || ""} ${command.description || ""} ${command.keywords || ""}`.toLowerCase().includes(term);
54
+ });
55
+ });
56
+
57
+ watch(open, () => {
58
+ if (open.value) focusInput();
59
+ });
60
+
61
+ onMounted(() => {
62
+ document.addEventListener("keydown", handleDocumentKeydown);
63
+ });
64
+
65
+ onUnmounted(() => {
66
+ document.removeEventListener("keydown", handleDocumentKeydown);
67
+ });
68
+
69
+ function openPalette() {
70
+ internalOpen.value = true;
71
+ emit("update:open", true);
72
+ emit("open");
73
+ focusInput();
74
+ }
75
+
76
+ function close() {
77
+ internalOpen.value = false;
78
+ query.value = "";
79
+ emit("update:open", false);
80
+ emit("close");
81
+ }
82
+
83
+ function focusInput() {
84
+ queueJob(() => {
85
+ inputEl.value?.focus();
86
+ });
87
+ }
88
+
89
+ function updateQuery(event) {
90
+ query.value = event.target.value;
91
+ }
92
+
93
+ function handleDocumentKeydown(event) {
94
+ if (!shortcut.value) return;
95
+ if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "k") {
96
+ event.preventDefault();
97
+ if (isOpen.value) {
98
+ close();
99
+ } else {
100
+ openPalette();
101
+ }
102
+ }
103
+ }
104
+
105
+ function handleInputKeydown(event) {
106
+ if (event.key === "Escape") close();
107
+ if (event.key === "Enter" && filteredCommands.value[0]) selectCommand(filteredCommands.value[0]);
108
+ }
109
+
110
+ function selectCommand(command) {
111
+ emit("select", command);
112
+ close();
113
+ }
114
+ </script>
115
+
116
+ <style scoped>
117
+ .command-layer {
118
+ position: fixed;
119
+ inset: 0;
120
+ z-index: 80;
121
+ display: grid;
122
+ place-items: start center;
123
+ padding-top: 12vh;
124
+ }
125
+
126
+ .command-backdrop {
127
+ position: absolute;
128
+ inset: 0;
129
+ border: 0;
130
+ background: rgb(15 23 42 / 42%);
131
+ }
132
+
133
+ .mikuru-command-palette {
134
+ position: relative;
135
+ z-index: 1;
136
+ display: grid;
137
+ gap: 8px;
138
+ width: min(560px, calc(100vw - 32px));
139
+ border: 1px solid #e5e7eb;
140
+ border-radius: 8px;
141
+ padding: 10px;
142
+ color: #111827;
143
+ background: #ffffff;
144
+ box-shadow: 0 24px 80px rgb(15 23 42 / 24%);
145
+ }
146
+
147
+ input {
148
+ width: 100%;
149
+ box-sizing: border-box;
150
+ border: 1px solid #cbd5e1;
151
+ border-radius: 8px;
152
+ padding: 11px 12px;
153
+ color: #111827;
154
+ font: inherit;
155
+ }
156
+
157
+ input:focus {
158
+ border-color: #2563eb;
159
+ outline: 3px solid rgb(37 99 235 / 18%);
160
+ }
161
+
162
+ ul {
163
+ display: grid;
164
+ gap: 4px;
165
+ max-height: 320px;
166
+ overflow: auto;
167
+ margin: 0;
168
+ padding: 0;
169
+ list-style: none;
170
+ }
171
+
172
+ li button {
173
+ display: grid;
174
+ gap: 2px;
175
+ width: 100%;
176
+ border: 0;
177
+ border-radius: 8px;
178
+ padding: 10px;
179
+ color: #111827;
180
+ background: transparent;
181
+ font: inherit;
182
+ text-align: left;
183
+ cursor: pointer;
184
+ }
185
+
186
+ li button:hover,
187
+ li button:focus-visible {
188
+ background: #eff6ff;
189
+ outline: none;
190
+ }
191
+
192
+ small,
193
+ p {
194
+ margin: 0;
195
+ color: #64748b;
196
+ }
197
+ </style>
@@ -0,0 +1,137 @@
1
+ <template>
2
+ <span class="context-host" ref="rootEl" @contextmenu="openFromEvent">
3
+ <slot>{{ label }}</slot>
4
+ <Teleport to="body">
5
+ <div
6
+ m-if="isOpen"
7
+ class="mikuru-context-menu"
8
+ role="menu"
9
+ :style="menuStyle"
10
+ >
11
+ <button
12
+ m-for="item in items"
13
+ :key="itemKey(item)"
14
+ type="button"
15
+ role="menuitem"
16
+ :disabled="item.disabled"
17
+ @click="selectItem(item)"
18
+ >
19
+ <span>{{ item.label }}</span>
20
+ <small m-if="item.shortcut">{{ item.shortcut }}</small>
21
+ </button>
22
+ </div>
23
+ </Teleport>
24
+ </span>
25
+ </template>
26
+
27
+ <script>
28
+ import { computed, onMounted, onUnmounted, ref } from "mikuru";
29
+
30
+ const {
31
+ label = "Right click menu",
32
+ items = []
33
+ } = defineProps({
34
+ label: String,
35
+ items: Array
36
+ });
37
+
38
+ const emit = defineEmits(["select", "open", "close"]);
39
+ const rootEl = ref(null);
40
+ const isOpen = ref(false);
41
+ const x = ref(0);
42
+ const y = ref(0);
43
+ const menuStyle = computed(() => `left: ${x.value}px; top: ${y.value}px;`);
44
+
45
+ onMounted(() => {
46
+ document.addEventListener("pointerdown", handlePointerDown);
47
+ document.addEventListener("keydown", handleKeydown);
48
+ });
49
+
50
+ onUnmounted(() => {
51
+ document.removeEventListener("pointerdown", handlePointerDown);
52
+ document.removeEventListener("keydown", handleKeydown);
53
+ });
54
+
55
+ function openFromEvent(event) {
56
+ event.preventDefault();
57
+ x.value = event.clientX;
58
+ y.value = event.clientY;
59
+ isOpen.value = true;
60
+ emit("open");
61
+ }
62
+
63
+ function close() {
64
+ if (!isOpen.value) return;
65
+ isOpen.value = false;
66
+ emit("close");
67
+ }
68
+
69
+ function selectItem(item) {
70
+ if (item.disabled) return;
71
+ emit("select", item);
72
+ close();
73
+ }
74
+
75
+ function handlePointerDown(event) {
76
+ if (!isOpen.value) return;
77
+ if (rootEl.value?.contains(event.target)) return;
78
+ close();
79
+ }
80
+
81
+ function handleKeydown(event) {
82
+ if (event.key === "Escape") close();
83
+ }
84
+
85
+ function itemKey(item) {
86
+ return item.id || item.value || item.label;
87
+ }
88
+ </script>
89
+
90
+ <style scoped>
91
+ .context-host {
92
+ display: inline-block;
93
+ }
94
+
95
+ .mikuru-context-menu {
96
+ position: fixed;
97
+ z-index: 90;
98
+ display: grid;
99
+ gap: 3px;
100
+ min-width: 180px;
101
+ border: 1px solid #e5e7eb;
102
+ border-radius: 8px;
103
+ padding: 6px;
104
+ background: #ffffff;
105
+ box-shadow: 0 18px 48px rgb(15 23 42 / 18%);
106
+ }
107
+
108
+ button {
109
+ display: flex;
110
+ align-items: center;
111
+ justify-content: space-between;
112
+ gap: 16px;
113
+ border: 0;
114
+ border-radius: 6px;
115
+ padding: 8px 10px;
116
+ color: #111827;
117
+ background: transparent;
118
+ font: inherit;
119
+ text-align: left;
120
+ cursor: pointer;
121
+ }
122
+
123
+ button:hover,
124
+ button:focus-visible {
125
+ background: #eff6ff;
126
+ outline: none;
127
+ }
128
+
129
+ button:disabled {
130
+ color: #94a3b8;
131
+ cursor: not-allowed;
132
+ }
133
+
134
+ small {
135
+ color: #64748b;
136
+ }
137
+ </style>
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <section class="mikuru-data-list" :aria-label="label">
3
+ <article m-for="item in items" :key="itemKey(item)" class="data-row">
4
+ <div>
5
+ <strong>{{ item.title }}</strong>
6
+ <p m-if="item.description">{{ item.description }}</p>
7
+ </div>
8
+ <span m-if="item.meta">{{ item.meta }}</span>
9
+ </article>
10
+ </section>
11
+ </template>
12
+
13
+ <script>
14
+ const {
15
+ label = "Data list",
16
+ items = []
17
+ } = defineProps({
18
+ label: String,
19
+ items: Array
20
+ });
21
+
22
+ function itemKey(item) {
23
+ return item.id || item.title;
24
+ }
25
+ </script>
26
+
27
+ <style scoped>
28
+ .mikuru-data-list {
29
+ display: grid;
30
+ border: 1px solid #e5e7eb;
31
+ border-radius: 8px;
32
+ overflow: hidden;
33
+ background: #ffffff;
34
+ }
35
+
36
+ .data-row {
37
+ display: flex;
38
+ justify-content: space-between;
39
+ gap: 12px;
40
+ padding: 12px;
41
+ }
42
+
43
+ .data-row + .data-row {
44
+ border-top: 1px solid #e5e7eb;
45
+ }
46
+
47
+ strong {
48
+ color: #111827;
49
+ }
50
+
51
+ p {
52
+ margin: 3px 0 0;
53
+ color: #64748b;
54
+ }
55
+
56
+ .data-row > span {
57
+ flex: 0 0 auto;
58
+ color: #2563eb;
59
+ font-weight: 650;
60
+ }
61
+ </style>