@celestia-island/hikari 0.4.4

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 (187) hide show
  1. package/README.md +54 -0
  2. package/package.json +63 -0
  3. package/src/DemoApp.vue +400 -0
  4. package/src/components/HkActionBar.tsx +15 -0
  5. package/src/components/HkAlert.scss +121 -0
  6. package/src/components/HkAlert.tsx +85 -0
  7. package/src/components/HkAvatar.tsx +29 -0
  8. package/src/components/HkBadge.scss +117 -0
  9. package/src/components/HkBadge.tsx +50 -0
  10. package/src/components/HkBreadcrumb.tsx +39 -0
  11. package/src/components/HkButton.scss +128 -0
  12. package/src/components/HkButton.tsx +74 -0
  13. package/src/components/HkCard.scss +49 -0
  14. package/src/components/HkCard.tsx +38 -0
  15. package/src/components/HkCheckbox.scss +102 -0
  16. package/src/components/HkCheckbox.tsx +85 -0
  17. package/src/components/HkCollapse.tsx +48 -0
  18. package/src/components/HkColorPicker.scss +148 -0
  19. package/src/components/HkColorPicker.tsx +204 -0
  20. package/src/components/HkConfirmDialog.scss +12 -0
  21. package/src/components/HkConfirmDialog.tsx +75 -0
  22. package/src/components/HkDateTimePicker.scss +361 -0
  23. package/src/components/HkDateTimePicker.tsx +424 -0
  24. package/src/components/HkDivider.scss +85 -0
  25. package/src/components/HkDivider.tsx +51 -0
  26. package/src/components/HkDraggableGrid.scss +132 -0
  27. package/src/components/HkDraggableGrid.tsx +291 -0
  28. package/src/components/HkDraggableList.scss +110 -0
  29. package/src/components/HkDraggableList.tsx +238 -0
  30. package/src/components/HkDrawer.scss +175 -0
  31. package/src/components/HkDrawer.tsx +171 -0
  32. package/src/components/HkEmptyState.scss +42 -0
  33. package/src/components/HkEmptyState.tsx +41 -0
  34. package/src/components/HkErrorBoundary.scss +64 -0
  35. package/src/components/HkErrorBoundary.tsx +82 -0
  36. package/src/components/HkGaugeRing.scss +4 -0
  37. package/src/components/HkGaugeRing.tsx +130 -0
  38. package/src/components/HkHoverRevealAction.scss +66 -0
  39. package/src/components/HkHoverRevealAction.tsx +77 -0
  40. package/src/components/HkIcon.tsx +43 -0
  41. package/src/components/HkIconButton.tsx +42 -0
  42. package/src/components/HkImageViewer.scss +33 -0
  43. package/src/components/HkImageViewer.tsx +250 -0
  44. package/src/components/HkInput.scss +172 -0
  45. package/src/components/HkInput.tsx +175 -0
  46. package/src/components/HkKbd.tsx +26 -0
  47. package/src/components/HkKeywordSearchModal.scss +148 -0
  48. package/src/components/HkKeywordSearchModal.tsx +308 -0
  49. package/src/components/HkListTransition.scss +131 -0
  50. package/src/components/HkListTransition.tsx +29 -0
  51. package/src/components/HkLocalePickerPopup.tsx +42 -0
  52. package/src/components/HkLogo.scss +36 -0
  53. package/src/components/HkLogo.tsx +93 -0
  54. package/src/components/HkMarkdownRenderer.scss +141 -0
  55. package/src/components/HkMarkdownRenderer.tsx +123 -0
  56. package/src/components/HkMediaControlBar.scss +65 -0
  57. package/src/components/HkMediaControlBar.tsx +115 -0
  58. package/src/components/HkMediaPlayer.scss +29 -0
  59. package/src/components/HkMediaPlayer.tsx +210 -0
  60. package/src/components/HkMediaSlider.scss +54 -0
  61. package/src/components/HkMediaSlider.tsx +81 -0
  62. package/src/components/HkMediaVisualizer.scss +7 -0
  63. package/src/components/HkMediaVisualizer.tsx +119 -0
  64. package/src/components/HkMinimap.scss +76 -0
  65. package/src/components/HkMinimap.tsx +255 -0
  66. package/src/components/HkModal.scss +318 -0
  67. package/src/components/HkModal.tsx +481 -0
  68. package/src/components/HkModalBreadcrumb.scss +55 -0
  69. package/src/components/HkModalBreadcrumb.tsx +113 -0
  70. package/src/components/HkMorphingTabs.scss +141 -0
  71. package/src/components/HkMorphingTabs.tsx +113 -0
  72. package/src/components/HkNavItem.scss +81 -0
  73. package/src/components/HkNavItem.tsx +157 -0
  74. package/src/components/HkNumberInput.scss +154 -0
  75. package/src/components/HkNumberInput.tsx +161 -0
  76. package/src/components/HkPasswordInput.scss +289 -0
  77. package/src/components/HkPasswordInput.tsx +533 -0
  78. package/src/components/HkPhaseTransition.scss +25 -0
  79. package/src/components/HkPhaseTransition.tsx +40 -0
  80. package/src/components/HkPopover.scss +82 -0
  81. package/src/components/HkPopover.tsx +410 -0
  82. package/src/components/HkPopupSelect.scss +136 -0
  83. package/src/components/HkPopupSelect.tsx +199 -0
  84. package/src/components/HkProgressBar.scss +106 -0
  85. package/src/components/HkProgressBar.tsx +61 -0
  86. package/src/components/HkProgressDialog.tsx +67 -0
  87. package/src/components/HkProgressRing.scss +56 -0
  88. package/src/components/HkProgressRing.tsx +82 -0
  89. package/src/components/HkRadio.scss +119 -0
  90. package/src/components/HkRadio.tsx +75 -0
  91. package/src/components/HkRollingNumber.scss +37 -0
  92. package/src/components/HkRollingNumber.tsx +97 -0
  93. package/src/components/HkScrollContainer.scss +101 -0
  94. package/src/components/HkScrollContainer.tsx +362 -0
  95. package/src/components/HkSearchInput.scss +132 -0
  96. package/src/components/HkSearchInput.tsx +117 -0
  97. package/src/components/HkSelect.scss +143 -0
  98. package/src/components/HkSelect.tsx +230 -0
  99. package/src/components/HkSelectionGrid.scss +177 -0
  100. package/src/components/HkSelectionGrid.tsx +99 -0
  101. package/src/components/HkSelectionWaterfall.scss +93 -0
  102. package/src/components/HkSelectionWaterfall.tsx +90 -0
  103. package/src/components/HkSidebar.scss +81 -0
  104. package/src/components/HkSidebar.tsx +45 -0
  105. package/src/components/HkSkeleton.scss +44 -0
  106. package/src/components/HkSkeleton.tsx +30 -0
  107. package/src/components/HkSkeletonList.tsx +35 -0
  108. package/src/components/HkSpinner.scss +67 -0
  109. package/src/components/HkSpinner.tsx +41 -0
  110. package/src/components/HkSplash.scss +60 -0
  111. package/src/components/HkSplash.tsx +40 -0
  112. package/src/components/HkSwitch.scss +157 -0
  113. package/src/components/HkSwitch.tsx +72 -0
  114. package/src/components/HkTable.scss +198 -0
  115. package/src/components/HkTable.tsx +246 -0
  116. package/src/components/HkTabs.scss +109 -0
  117. package/src/components/HkTabs.tsx +111 -0
  118. package/src/components/HkTag.scss +88 -0
  119. package/src/components/HkTag.tsx +44 -0
  120. package/src/components/HkTextarea.scss +72 -0
  121. package/src/components/HkTextarea.tsx +104 -0
  122. package/src/components/HkTimeline.scss +139 -0
  123. package/src/components/HkTimeline.tsx +95 -0
  124. package/src/components/HkToast.scss +168 -0
  125. package/src/components/HkToast.tsx +175 -0
  126. package/src/components/HkTooltip.scss +47 -0
  127. package/src/components/HkTooltip.tsx +108 -0
  128. package/src/components/HkTree.scss +188 -0
  129. package/src/components/HkTree.tsx +217 -0
  130. package/src/components/HkTrendChart.tsx +180 -0
  131. package/src/components/HkWindowedItem.scss +17 -0
  132. package/src/components/HkWindowedItem.tsx +107 -0
  133. package/src/components/HkZoomToolbar.scss +35 -0
  134. package/src/components/HkZoomToolbar.tsx +69 -0
  135. package/src/composables/useBreakpoint.ts +41 -0
  136. package/src/composables/useClipboard.ts +37 -0
  137. package/src/composables/useConfirm.ts +35 -0
  138. package/src/composables/useProgressDialog.ts +52 -0
  139. package/src/composables/useReportedTransition.ts +69 -0
  140. package/src/composables/useScrollWindow.ts +21 -0
  141. package/src/composables/useToast.ts +2 -0
  142. package/src/demo.scss +24 -0
  143. package/src/demo.ts +22 -0
  144. package/src/env.d.ts +6 -0
  145. package/src/i18n/context.ts +89 -0
  146. package/src/i18n/locales/ar/components.json +43 -0
  147. package/src/i18n/locales/ar/components.toml +40 -0
  148. package/src/i18n/locales/de/components.json +43 -0
  149. package/src/i18n/locales/de/components.toml +40 -0
  150. package/src/i18n/locales/en/components.json +43 -0
  151. package/src/i18n/locales/en/components.toml +40 -0
  152. package/src/i18n/locales/es/components.json +43 -0
  153. package/src/i18n/locales/es/components.toml +40 -0
  154. package/src/i18n/locales/fr/components.json +43 -0
  155. package/src/i18n/locales/fr/components.toml +40 -0
  156. package/src/i18n/locales/ja/components.json +43 -0
  157. package/src/i18n/locales/ja/components.toml +40 -0
  158. package/src/i18n/locales/ko/components.json +43 -0
  159. package/src/i18n/locales/ko/components.toml +40 -0
  160. package/src/i18n/locales/pt/components.json +43 -0
  161. package/src/i18n/locales/pt/components.toml +40 -0
  162. package/src/i18n/locales/ru/components.json +43 -0
  163. package/src/i18n/locales/ru/components.toml +40 -0
  164. package/src/i18n/locales/zh-Hans/components.json +43 -0
  165. package/src/i18n/locales/zh-Hans/components.toml +40 -0
  166. package/src/i18n/locales/zh-Hant/components.json +43 -0
  167. package/src/i18n/locales/zh-Hant/components.toml +40 -0
  168. package/src/index.ts +137 -0
  169. package/src/render.ts +18 -0
  170. package/src/runtime/animationBus.ts +278 -0
  171. package/src/runtime/cronBus.ts +25 -0
  172. package/src/runtime/index.ts +12 -0
  173. package/src/runtime/useAsyncData.ts +30 -0
  174. package/src/runtime/useBreakpoint.ts +62 -0
  175. package/src/runtime/useClipboard.ts +37 -0
  176. package/src/runtime/useConfirm.ts +35 -0
  177. package/src/runtime/useOverlay.ts +82 -0
  178. package/src/runtime/usePopupManager.ts +89 -0
  179. package/src/runtime/useToast.ts +140 -0
  180. package/src/styles/index.scss +10 -0
  181. package/src/theme/index.ts +5 -0
  182. package/src/theme/presets.ts +307 -0
  183. package/src/theme/theme.scss +17 -0
  184. package/src/theme/useBackgroundLuminance.ts +145 -0
  185. package/src/theme/useSolarTime.ts +101 -0
  186. package/src/theme/useTheme.ts +141 -0
  187. package/src/tokens.scss +26 -0
@@ -0,0 +1,175 @@
1
+ .hk-drawer-overlay {
2
+ position: fixed;
3
+ inset: 0;
4
+ background: var(--hk-drawer-overlay-bg, rgba(0, 0, 0, 0.45));
5
+ backdrop-filter: var(--hk-drawer-overlay-blur, blur(6px));
6
+ }
7
+
8
+ .hk-drawer-overlay-enter-active {
9
+ transition: opacity var(--hi-duration-normal, 0.3s) var(--hi-ease-out, cubic-bezier(0.16, 1, 0.3, 1));
10
+ }
11
+
12
+ .hk-drawer-overlay-leave-active {
13
+ transition: opacity var(--hi-duration-normal, 0.3s) var(--hi-ease-in, cubic-bezier(0.4, 0, 1, 1));
14
+ }
15
+
16
+ .hk-drawer-overlay-enter-from,
17
+ .hk-drawer-overlay-leave-to {
18
+ opacity: 0;
19
+ }
20
+
21
+ .hk-drawer-panel {
22
+ position: fixed;
23
+ display: flex;
24
+ flex-direction: column;
25
+ background: var(--hk-drawer-bg, var(--hi-color-surface, rgba(255, 255, 255, 0.95)));
26
+ backdrop-filter: blur(12px);
27
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16);
28
+ outline: none;
29
+ transition:
30
+ width var(--hi-duration-normal, 0.3s) ease,
31
+ height var(--hi-duration-normal, 0.3s) ease;
32
+ }
33
+
34
+ .hk-drawer-left {
35
+ top: 0;
36
+ bottom: 0;
37
+ left: 0;
38
+ border-right: 1px solid var(--hi-color-border, rgba(0, 0, 0, 0.08));
39
+ border-radius: 0 var(--hi-radius-md, 0.5rem) var(--hi-radius-md, 0.5rem) 0;
40
+ }
41
+
42
+ .hk-drawer-right {
43
+ top: 0;
44
+ bottom: 0;
45
+ right: 0;
46
+ border-left: 1px solid var(--hi-color-border, rgba(0, 0, 0, 0.08));
47
+ border-radius: var(--hi-radius-md, 0.5rem) 0 0 var(--hi-radius-md, 0.5rem);
48
+ }
49
+
50
+ .hk-drawer-top {
51
+ top: 0;
52
+ left: 0;
53
+ right: 0;
54
+ border-bottom: 1px solid var(--hi-color-border, rgba(0, 0, 0, 0.08));
55
+ border-radius: 0 0 var(--hi-radius-md, 0.5rem) var(--hi-radius-md, 0.5rem);
56
+ }
57
+
58
+ .hk-drawer-bottom {
59
+ bottom: 0;
60
+ left: 0;
61
+ right: 0;
62
+ border-top: 1px solid var(--hi-color-border, rgba(0, 0, 0, 0.08));
63
+ border-radius: var(--hi-radius-md, 0.5rem) var(--hi-radius-md, 0.5rem) 0 0;
64
+ }
65
+
66
+ .hk-drawer-left-enter-active,
67
+ .hk-drawer-right-enter-active,
68
+ .hk-drawer-top-enter-active,
69
+ .hk-drawer-bottom-enter-active {
70
+ transition:
71
+ opacity var(--hi-duration-normal, 0.3s) cubic-bezier(0.16, 1, 0.3, 1),
72
+ transform var(--hi-duration-normal, 0.3s) cubic-bezier(0.34, 1.56, 0.64, 1);
73
+ }
74
+
75
+ .hk-drawer-left-leave-active,
76
+ .hk-drawer-right-leave-active,
77
+ .hk-drawer-top-leave-active,
78
+ .hk-drawer-bottom-leave-active {
79
+ transition:
80
+ opacity var(--hi-duration-normal, 0.3s) cubic-bezier(0.4, 0, 1, 1),
81
+ transform var(--hi-duration-normal, 0.3s) cubic-bezier(0.4, 0, 1, 1);
82
+ }
83
+
84
+ .hk-drawer-left-enter-from,
85
+ .hk-drawer-left-leave-to {
86
+ opacity: 0;
87
+ transform: translateX(-100%);
88
+ }
89
+
90
+ .hk-drawer-right-enter-from,
91
+ .hk-drawer-right-leave-to {
92
+ opacity: 0;
93
+ transform: translateX(100%);
94
+ }
95
+
96
+ .hk-drawer-top-enter-from,
97
+ .hk-drawer-top-leave-to {
98
+ opacity: 0;
99
+ transform: translateY(-100%);
100
+ }
101
+
102
+ .hk-drawer-bottom-enter-from,
103
+ .hk-drawer-bottom-leave-to {
104
+ opacity: 0;
105
+ transform: translateY(100%);
106
+ }
107
+
108
+ .hk-drawer-header {
109
+ display: flex;
110
+ align-items: center;
111
+ justify-content: space-between;
112
+ padding: var(--hi-space-12, 0.75rem) var(--hi-space-16, 1rem);
113
+ border-bottom: 1px solid var(--hi-color-border, rgba(0, 0, 0, 0.06));
114
+ flex-shrink: 0;
115
+ }
116
+
117
+ .hk-drawer-title {
118
+ font-size: var(--hi-text-base, 1rem);
119
+ font-weight: 700;
120
+ color: var(--hi-color-text-primary, #333);
121
+ }
122
+
123
+ .hk-drawer-close {
124
+ flex-shrink: 0;
125
+ width: 2rem;
126
+ height: 2rem;
127
+ display: inline-flex;
128
+ align-items: center;
129
+ justify-content: center;
130
+ padding: 0;
131
+ background: transparent;
132
+ border: none;
133
+ border-radius: var(--hi-radius-md, 0.5rem);
134
+ color: var(--hi-color-text-secondary, #666);
135
+ cursor: pointer;
136
+ transition:
137
+ background 0.15s ease,
138
+ color 0.15s ease;
139
+
140
+ &:hover {
141
+ background: var(--hi-secondary-bg, rgba(0, 0, 0, 0.06));
142
+ color: var(--hi-color-text-primary, #333);
143
+ }
144
+
145
+ &:focus-visible {
146
+ outline: 2px solid var(--hi-color-primary, #58a6ff);
147
+ outline-offset: 2px;
148
+ }
149
+ }
150
+
151
+ .hk-drawer-body {
152
+ flex: 1;
153
+ min-height: 0;
154
+ overflow-y: auto;
155
+ padding: var(--hi-space-16, 1rem);
156
+
157
+ &::-webkit-scrollbar {
158
+ width: 6px;
159
+ }
160
+
161
+ &::-webkit-scrollbar-track {
162
+ background: transparent;
163
+ }
164
+
165
+ &::-webkit-scrollbar-thumb {
166
+ background: var(--hi-color-border, rgba(0, 0, 0, 0.12));
167
+ border-radius: 3px;
168
+ }
169
+ }
170
+
171
+ .hk-drawer-footer {
172
+ padding: var(--hi-space-12, 0.75rem) var(--hi-space-16, 1rem);
173
+ border-top: 1px solid var(--hi-color-border, rgba(0, 0, 0, 0.06));
174
+ flex-shrink: 0;
175
+ }
@@ -0,0 +1,171 @@
1
+ import {
2
+ computed,
3
+ defineComponent,
4
+ nextTick,
5
+ onBeforeUnmount,
6
+ onMounted,
7
+ ref,
8
+ Teleport,
9
+ Transition,
10
+ watch,
11
+ type PropType,
12
+ } from "vue";
13
+
14
+ import { useI18n } from "../i18n/context";
15
+ import "./HkDrawer.scss";
16
+ import { useOverlay } from "../runtime/useOverlay";
17
+ import { usePopupManager } from "../runtime/usePopupManager";
18
+
19
+ type DrawerSide = "left" | "right" | "top" | "bottom";
20
+
21
+ export default defineComponent({
22
+ name: "HkDrawer",
23
+ props: {
24
+ modelValue: { type: Boolean, required: true },
25
+ side: { type: String as PropType<DrawerSide>, default: "right" },
26
+ title: { type: String, default: undefined },
27
+ closable: { type: Boolean, default: true },
28
+ overlay: { type: Boolean, default: true },
29
+ size: { type: String, default: "320px" },
30
+ },
31
+ emits: {
32
+ "update:modelValue": (_value: boolean) => true,
33
+ },
34
+ setup(props, { emit, slots }) {
35
+ const { t } = useI18n();
36
+ const manager = usePopupManager();
37
+ const overlayHook = useOverlay({ name: "hk-drawer" });
38
+
39
+ const handle = ref<{ id: string; zIndex: number } | null>(null);
40
+ const panelRef = ref<HTMLElement>();
41
+ let unmounted = false;
42
+
43
+ const isVertical = computed(
44
+ () => props.side === "left" || props.side === "right",
45
+ );
46
+
47
+ const overlayZ = computed(() => handle.value?.zIndex ?? 0);
48
+ const panelZ = computed(() => (handle.value?.zIndex ?? 0) + 1);
49
+
50
+ const panelStyle = computed(() => {
51
+ const base: Record<string, string | number> =
52
+ isVertical.value
53
+ ? { width: props.size, maxWidth: "85vw" }
54
+ : { height: props.size, maxHeight: "70vh" };
55
+ base.zIndex = panelZ.value;
56
+ return base;
57
+ });
58
+
59
+ function close() {
60
+ emit("update:modelValue", false);
61
+ }
62
+
63
+ function onOverlayClick() {
64
+ if (props.closable && props.overlay) close();
65
+ }
66
+
67
+ function onEscape() {
68
+ if (props.closable) close();
69
+ }
70
+
71
+ function onDrawerAfterLeave() {
72
+ cleanup();
73
+ }
74
+
75
+ function cleanup() {
76
+ if (handle.value) {
77
+ manager.unregister(handle.value.id);
78
+ handle.value = null;
79
+ }
80
+ }
81
+
82
+ watch(
83
+ () => props.modelValue,
84
+ (val) => {
85
+ if (unmounted) return;
86
+ if (val) {
87
+ cleanup();
88
+ handle.value = manager.register("drawer", true);
89
+ overlayHook.open();
90
+ nextTick(() => {
91
+ panelRef.value?.focus();
92
+ });
93
+ }
94
+ },
95
+ { immediate: true },
96
+ );
97
+
98
+ onMounted(() => {
99
+ unmounted = false;
100
+ });
101
+
102
+ onBeforeUnmount(() => {
103
+ unmounted = true;
104
+ cleanup();
105
+ });
106
+
107
+ return () => (
108
+ <Teleport to="body">
109
+ <Transition name="hk-drawer-overlay" appear>
110
+ {props.modelValue && props.overlay ? (
111
+ <div
112
+ class="hk-drawer-overlay"
113
+ style={{ zIndex: overlayZ.value }}
114
+ onClick={onOverlayClick}
115
+ />
116
+ ) : null}
117
+ </Transition>
118
+ <Transition name={`hk-drawer-${props.side}`} appear onAfterLeave={onDrawerAfterLeave}>
119
+ {props.modelValue ? (
120
+ <div
121
+ ref={panelRef}
122
+ class={["hk-drawer-panel", `hk-drawer-${props.side}`]}
123
+ style={panelStyle.value}
124
+ role="dialog"
125
+ aria-label={props.title}
126
+ aria-modal={props.overlay}
127
+ tabindex={-1}
128
+ onKeydown={(e: KeyboardEvent) => {
129
+ if (e.key === "Escape") onEscape();
130
+ }}
131
+ >
132
+ {props.title || slots.header ? (
133
+ <div class="hk-drawer-header">
134
+ {slots.header ? (
135
+ slots.header()
136
+ ) : (
137
+ <span class="hk-drawer-title">{props.title}</span>
138
+ )}
139
+ {props.closable ? (
140
+ <button
141
+ class="hk-drawer-close"
142
+ aria-label={t("hk.drawer.close", "Close")}
143
+ onClick={close}
144
+ >
145
+ <svg
146
+ viewBox="0 0 24 24"
147
+ fill="none"
148
+ stroke="currentColor"
149
+ stroke-width="2"
150
+ stroke-linecap="round"
151
+ width="16"
152
+ height="16"
153
+ >
154
+ <line x1="18" y1="6" x2="6" y2="18" />
155
+ <line x1="6" y1="6" x2="18" y2="18" />
156
+ </svg>
157
+ </button>
158
+ ) : null}
159
+ </div>
160
+ ) : null}
161
+ <div class="hk-drawer-body">{slots.default?.()}</div>
162
+ {slots.footer ? (
163
+ <div class="hk-drawer-footer">{slots.footer()}</div>
164
+ ) : null}
165
+ </div>
166
+ ) : null}
167
+ </Transition>
168
+ </Teleport>
169
+ );
170
+ },
171
+ });
@@ -0,0 +1,42 @@
1
+ .hk-empty-state {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: center;
5
+ justify-content: center;
6
+ padding: var(--hi-space-48, 3rem) var(--hi-space-16, 1rem);
7
+ text-align: center;
8
+ background: var(--hk-empty-bg, var(--hi-color-surface, rgba(255, 255, 255, 0.95)));
9
+ backdrop-filter: blur(8px);
10
+ border: 1px solid var(--hi-color-border, rgba(0, 0, 0, 0.06));
11
+ border-radius: var(--hi-radius-md, 0.5rem);
12
+ }
13
+
14
+ .hk-empty-icon {
15
+ width: 48px;
16
+ height: 48px;
17
+ margin-bottom: var(--hi-space-12, 0.75rem);
18
+ color: var(--hi-color-text-secondary, #666);
19
+ opacity: 0.5;
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ }
24
+
25
+ .hk-empty-title {
26
+ font-size: var(--hi-text-base, 1rem);
27
+ font-weight: 600;
28
+ color: var(--hi-color-text-primary, #333);
29
+ margin: 0 0 var(--hi-space-8, 0.5rem);
30
+ }
31
+
32
+ .hk-empty-desc {
33
+ font-size: var(--hi-text-sm, 0.8125rem);
34
+ color: var(--hi-color-text-secondary, #666);
35
+ margin: 0 0 var(--hi-space-16, 1rem);
36
+ max-width: 24rem;
37
+ line-height: 1.5;
38
+ }
39
+
40
+ .hk-empty-action {
41
+ margin-top: var(--hi-space-4, 0.25rem);
42
+ }
@@ -0,0 +1,41 @@
1
+ import { defineComponent } from "vue";
2
+
3
+ import "./HkEmptyState.scss";
4
+
5
+ export default defineComponent({
6
+ name: "HkEmptyState",
7
+ props: {
8
+ title: { type: String, required: true },
9
+ description: { type: String, default: undefined },
10
+ },
11
+ setup(props, { slots }) {
12
+ return () => (
13
+ <div class="hk-empty-state">
14
+ <div class="hk-empty-icon">
15
+ {slots.icon ? (
16
+ slots.icon()
17
+ ) : (
18
+ <svg
19
+ viewBox="0 0 24 24"
20
+ fill="none"
21
+ stroke="currentColor"
22
+ stroke-width="1.5"
23
+ stroke-linecap="round"
24
+ stroke-linejoin="round"
25
+ width="48"
26
+ height="48"
27
+ >
28
+ <polyline points="22 12 16 12 14 15 10 15 8 12 2 12" />
29
+ <path d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" />
30
+ </svg>
31
+ )}
32
+ </div>
33
+ <p class="hk-empty-title">{props.title}</p>
34
+ {props.description ? (
35
+ <p class="hk-empty-desc">{props.description}</p>
36
+ ) : null}
37
+ <div class="hk-empty-action">{slots.action?.()}</div>
38
+ </div>
39
+ );
40
+ },
41
+ });
@@ -0,0 +1,64 @@
1
+ .hk-error-boundary {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ padding: 2rem;
6
+ }
7
+
8
+ .hk-error-boundary-card {
9
+ max-width: 28rem;
10
+ width: 100%;
11
+ background: rgb(from var(--hi-color-error, #f85149) r g b / 0.06);
12
+ border: 1px solid rgb(from var(--hi-color-error, #f85149) r g b / 0.18);
13
+ border-radius: 8px;
14
+ padding: 1.25rem;
15
+ display: flex;
16
+ flex-direction: column;
17
+ gap: 0.75rem;
18
+ }
19
+
20
+ .hk-error-boundary-header {
21
+ display: flex;
22
+ align-items: center;
23
+ gap: 0.5rem;
24
+ }
25
+
26
+ .hk-error-boundary-icon {
27
+ color: var(--hi-color-error, #f85149);
28
+ flex-shrink: 0;
29
+ }
30
+
31
+ .hk-error-boundary-label {
32
+ font-size: 0.75rem;
33
+ font-weight: 600;
34
+ color: var(--hi-color-error, #f85149);
35
+ letter-spacing: 0.02em;
36
+ text-transform: uppercase;
37
+ }
38
+
39
+ .hk-error-boundary-tag {
40
+ font-size: 0.75rem;
41
+ font-weight: 500;
42
+ padding: 0.0625rem 0.375rem;
43
+ border-radius: 4px;
44
+ background: rgb(from var(--hi-color-error, #f85149) r g b / 0.12);
45
+ color: var(--hi-color-error, #f85149);
46
+ font-family: ui-monospace, monospace;
47
+ }
48
+
49
+ .hk-error-boundary-msg {
50
+ font-size: 0.8125rem;
51
+ line-height: 1.5;
52
+ color: var(--hi-color-text-primary, #c9d1d9);
53
+ opacity: 0.85;
54
+ word-break: break-word;
55
+ white-space: pre-wrap;
56
+ font-family: ui-monospace, monospace;
57
+ }
58
+
59
+ .hk-error-boundary-actions {
60
+ display: flex;
61
+ align-items: center;
62
+ justify-content: flex-end;
63
+ gap: 0.5rem;
64
+ }
@@ -0,0 +1,82 @@
1
+ import { defineComponent, onErrorCaptured, ref, type PropType, type VNode } from "vue";
2
+ import { AlertTriangle, Copy, RefreshCw } from "lucide-vue-next";
3
+ import { useClipboard } from "../runtime/useClipboard";
4
+ import { useI18n } from "../i18n/context";
5
+ import HButton from "./HkButton";
6
+ import HScrollContainer from "./HkScrollContainer";
7
+ import "./HkErrorBoundary.scss";
8
+
9
+ export default defineComponent({
10
+ name: "HkErrorBoundary",
11
+ props: {
12
+ name: { type: String, default: "unknown" },
13
+ fallback: { type: Function as PropType<(err: string, retry: () => void) => VNode>, default: undefined },
14
+ errorTitle: { type: String, default: "" },
15
+ copyErrorLabel: { type: String, default: "" },
16
+ retryLabel: { type: String, default: "" },
17
+ },
18
+ setup(props, { slots }) {
19
+ const clipboard = useClipboard();
20
+ const { t } = useI18n();
21
+ const error = ref<string | null>(null);
22
+
23
+ onErrorCaptured((err) => {
24
+ const msg =
25
+ err instanceof Error
26
+ ? `${err.name}: ${err.message}\n\n${err.stack || ""}`
27
+ : String(err);
28
+ console.error(`[ErrorBoundary:${props.name}]`, msg);
29
+ error.value = msg;
30
+ return false;
31
+ });
32
+
33
+ function retry() {
34
+ error.value = null;
35
+ }
36
+
37
+ function copyError() {
38
+ clipboard.copy(error.value!);
39
+ }
40
+
41
+ return () => {
42
+ if (error.value === null) {
43
+ return slots.default?.();
44
+ }
45
+
46
+ if (props.fallback) {
47
+ return props.fallback(error.value, retry);
48
+ }
49
+
50
+ return (
51
+ <div class="hk-error-boundary">
52
+ <div class="hk-error-boundary-card">
53
+ <div class="hk-error-boundary-header">
54
+ <AlertTriangle size={16} class="hk-error-boundary-icon" />
55
+ <span class="hk-error-boundary-label">{props.errorTitle || t("hk.errorBoundary.title", "Component Error")}</span>
56
+ {props.name !== "unknown" && (
57
+ <span class="hk-error-boundary-tag">{props.name}</span>
58
+ )}
59
+ </div>
60
+ <div class="hk-error-boundary-msg">
61
+ <div style={{ maxHeight: "12rem" }}>
62
+ <HScrollContainer>
63
+ {error.value}
64
+ </HScrollContainer>
65
+ </div>
66
+ </div>
67
+ <div class="hk-error-boundary-actions">
68
+ <HButton variant="ghost" size="sm" onClick={copyError}>
69
+ <Copy size={12} />
70
+ {props.copyErrorLabel || t("hk.errorBoundary.copyError", "Copy Error")}
71
+ </HButton>
72
+ <HButton variant="outline" size="sm" onClick={retry}>
73
+ <RefreshCw size={12} />
74
+ {props.retryLabel || t("hk.errorBoundary.retry", "Retry")}
75
+ </HButton>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ );
80
+ };
81
+ },
82
+ });
@@ -0,0 +1,4 @@
1
+ .hk-gauge-ring {
2
+ position: relative;
3
+ display: inline-flex;
4
+ }