@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,410 @@
1
+ import {
2
+ computed,
3
+ defineComponent,
4
+ nextTick,
5
+ onActivated,
6
+ onDeactivated,
7
+ onUnmounted,
8
+ ref,
9
+ Teleport,
10
+ Transition,
11
+ useAttrs,
12
+ watch,
13
+ type PropType,
14
+ } from "vue";
15
+
16
+ import { usePopupManager, type PopupHandle } from "../runtime/usePopupManager";
17
+ import { useReportedTransition } from "../composables/useReportedTransition";
18
+ import { onceFrame } from "../runtime/animationBus";
19
+ import "./HkPopover.scss";
20
+
21
+ export type PopupPlacement =
22
+ | "top" | "top-start" | "top-end"
23
+ | "bottom" | "bottom-start" | "bottom-end"
24
+ | "left" | "left-start" | "left-end"
25
+ | "right" | "right-start" | "right-end";
26
+
27
+ type BaseSide = "top" | "bottom" | "left" | "right";
28
+ type Align = "start" | "center" | "end";
29
+
30
+ function parsePlacement(p: PopupPlacement): { side: BaseSide; align: Align } {
31
+ const [side, align] = p.split("-") as [BaseSide, Align | undefined];
32
+ return { side, align: align ?? "center" };
33
+ }
34
+
35
+ const VIEWPORT_PAD = 8;
36
+
37
+ export default defineComponent({
38
+ name: "HkPopover",
39
+ props: {
40
+ modelValue: { type: Boolean, required: true },
41
+ placement: {
42
+ type: String as PropType<PopupPlacement>,
43
+ default: "bottom",
44
+ },
45
+ offset: { type: Number, default: 4 },
46
+ autoFlip: { type: Boolean, default: true },
47
+ backdrop: { type: Boolean, default: true },
48
+ closeOnBackdrop: { type: Boolean, default: true },
49
+ closeOnEscape: { type: Boolean, default: true },
50
+ glass: { type: Boolean, default: true },
51
+ anchorRef: { type: Object as PropType<HTMLElement | null>, default: null },
52
+ },
53
+ emits: {
54
+ "update:modelValue": (_v: boolean) => true,
55
+ },
56
+ setup(props, { emit, slots }) {
57
+ const attrs = useAttrs();
58
+ const manager = usePopupManager();
59
+ const handle = ref<PopupHandle | null>(null);
60
+ const panelRef = ref<HTMLElement>();
61
+ const resolvedPlacement = ref<PopupPlacement>(props.placement);
62
+ const coords = ref<{ top?: number; left?: number; bottom?: number; right?: number }>({});
63
+
64
+ const animBus = useReportedTransition(300);
65
+
66
+ // Suppress native browser tooltip on the anchor while the popover is open.
67
+ let savedTitle: string | null = null;
68
+ watch(
69
+ () => props.modelValue,
70
+ (open) => {
71
+ const el = props.anchorRef;
72
+ if (!el) return;
73
+ if (open) {
74
+ savedTitle = el.getAttribute("title");
75
+ if (savedTitle !== null && savedTitle !== "") {
76
+ el.setAttribute("title", "");
77
+ } else {
78
+ savedTitle = null;
79
+ }
80
+ } else {
81
+ if (savedTitle !== null) {
82
+ el.setAttribute("title", savedTitle);
83
+ } else if (savedTitle === null && el.getAttribute("title") === "") {
84
+ el.removeAttribute("title");
85
+ }
86
+ savedTitle = null;
87
+ }
88
+ },
89
+ );
90
+
91
+ function close() {
92
+ emit("update:modelValue", false);
93
+ }
94
+
95
+ let resizeObserver: ResizeObserver | null = null;
96
+ let positionScheduled = false;
97
+
98
+ function schedulePosition() {
99
+ if (positionScheduled) return;
100
+ positionScheduled = true;
101
+ onceFrame(() => {
102
+ positionScheduled = false;
103
+ computePosition();
104
+ });
105
+ }
106
+
107
+ function computeInitialCoords() {
108
+ const anchor = props.anchorRef;
109
+ if (!anchor) return;
110
+ const anchorRect = anchor.getBoundingClientRect();
111
+ const panel = panelRef.value;
112
+ const panelRect = panel ? panel.getBoundingClientRect() : null;
113
+ const { side, align } = parsePlacement(props.placement);
114
+ const off = props.offset;
115
+ const c: typeof coords.value = {};
116
+ const vw = window.innerWidth;
117
+ const vh = window.innerHeight;
118
+
119
+ const isVertical = side === "top" || side === "bottom";
120
+ const panelSize = panelRect ? (isVertical ? panelRect.width : panelRect.height) : 0;
121
+ const anchorStart = isVertical ? anchorRect.left : anchorRect.top;
122
+ const anchorEnd = isVertical ? anchorRect.right : anchorRect.bottom;
123
+ const anchorSize = isVertical ? anchorRect.width : anchorRect.height;
124
+
125
+ let crossPos: number;
126
+ if (align === "start") {
127
+ crossPos = anchorStart;
128
+ } else if (align === "end") {
129
+ crossPos = anchorEnd - panelSize;
130
+ } else {
131
+ crossPos = anchorStart + (anchorSize - panelSize) / 2;
132
+ }
133
+
134
+ if (side === "bottom") {
135
+ c.top = anchorRect.bottom + off;
136
+ c.left = crossPos;
137
+ } else if (side === "top") {
138
+ c.bottom = vh - anchorRect.top + off;
139
+ c.left = crossPos;
140
+ } else if (side === "right") {
141
+ c.left = anchorRect.right + off;
142
+ c.top = crossPos;
143
+ } else {
144
+ c.right = vw - anchorRect.left + off;
145
+ c.top = crossPos;
146
+ }
147
+
148
+ coords.value = c;
149
+ }
150
+
151
+ function computePosition() {
152
+ const anchor = props.anchorRef;
153
+ const panel = panelRef.value;
154
+ if (!anchor || !panel) return;
155
+
156
+ const anchorRect = anchor.getBoundingClientRect();
157
+ const panelRect = panel.getBoundingClientRect();
158
+ if (panelRect.width === 0 && panelRect.height === 0) {
159
+ schedulePosition();
160
+ return;
161
+ }
162
+ const vw = window.innerWidth;
163
+ const vh = window.innerHeight;
164
+
165
+ let { side } = parsePlacement(props.placement);
166
+ const { align } = parsePlacement(props.placement);
167
+
168
+ if (props.autoFlip) {
169
+ if (side === "bottom") {
170
+ const spaceBelow = vh - anchorRect.bottom;
171
+ const spaceAbove = anchorRect.top;
172
+ if (spaceBelow < panelRect.height + VIEWPORT_PAD && spaceAbove > spaceBelow) {
173
+ side = "top";
174
+ }
175
+ } else if (side === "top") {
176
+ const spaceAbove = anchorRect.top;
177
+ const spaceBelow = vh - anchorRect.bottom;
178
+ if (spaceAbove < panelRect.height + VIEWPORT_PAD && spaceBelow > spaceAbove) {
179
+ side = "bottom";
180
+ }
181
+ } else if (side === "right") {
182
+ const spaceRight = vw - anchorRect.right;
183
+ const spaceLeft = anchorRect.left;
184
+ if (spaceRight < panelRect.width + VIEWPORT_PAD && spaceLeft > spaceRight) {
185
+ side = "left";
186
+ }
187
+ } else if (side === "left") {
188
+ const spaceLeft = anchorRect.left;
189
+ const spaceRight = vw - anchorRect.right;
190
+ if (spaceLeft < panelRect.width + VIEWPORT_PAD && spaceRight > spaceLeft) {
191
+ side = "right";
192
+ }
193
+ }
194
+ }
195
+
196
+ resolvedPlacement.value = `${side}${align !== "center" ? `-${align}` : ""}` as PopupPlacement;
197
+ const off = props.offset;
198
+ const c: typeof coords.value = {};
199
+
200
+ const isVertical = side === "top" || side === "bottom";
201
+ const panelSize = isVertical ? panelRect.width : panelRect.height;
202
+ const anchorStart = isVertical ? anchorRect.left : anchorRect.top;
203
+ const anchorEnd = isVertical ? anchorRect.right : anchorRect.bottom;
204
+ const anchorSize = isVertical ? anchorRect.width : anchorRect.height;
205
+ const viewportSize = isVertical ? vw : vh;
206
+
207
+ let crossPos: number;
208
+ if (align === "start") {
209
+ crossPos = anchorStart;
210
+ } else if (align === "end") {
211
+ crossPos = anchorEnd - panelSize;
212
+ } else {
213
+ crossPos = anchorStart + (anchorSize - panelSize) / 2;
214
+ }
215
+ crossPos = Math.max(VIEWPORT_PAD, Math.min(crossPos, viewportSize - panelSize - VIEWPORT_PAD));
216
+
217
+ if (side === "bottom") {
218
+ c.top = anchorRect.bottom + off;
219
+ c.left = crossPos;
220
+ } else if (side === "top") {
221
+ c.bottom = vh - anchorRect.top + off;
222
+ c.left = crossPos;
223
+ } else if (side === "right") {
224
+ c.left = anchorRect.right + off;
225
+ c.top = crossPos;
226
+ } else {
227
+ c.right = vw - anchorRect.left + off;
228
+ c.top = crossPos;
229
+ }
230
+
231
+ if (side === "bottom") {
232
+ c.top = Math.max(VIEWPORT_PAD, Math.min(c.top!, vh - panelRect.height - VIEWPORT_PAD));
233
+ } else if (side === "top") {
234
+ c.bottom = Math.max(VIEWPORT_PAD, Math.min(c.bottom ?? 0, vh - panelRect.height - VIEWPORT_PAD));
235
+ } else if (side === "right") {
236
+ c.left = Math.max(VIEWPORT_PAD, Math.min(c.left!, vw - panelRect.width - VIEWPORT_PAD));
237
+ } else {
238
+ c.right = Math.max(VIEWPORT_PAD, Math.min(c.right ?? 0, vw - panelRect.width - VIEWPORT_PAD));
239
+ }
240
+
241
+ coords.value = c;
242
+ observePanel();
243
+ }
244
+
245
+ function attachObservers() {
246
+ if (resizeObserver) return;
247
+ resizeObserver = new ResizeObserver(() => { schedulePosition(); });
248
+ window.addEventListener("resize", schedulePosition);
249
+ if (props.anchorRef) resizeObserver.observe(props.anchorRef);
250
+ }
251
+
252
+ function observePanel() {
253
+ if (resizeObserver && panelRef.value) {
254
+ resizeObserver.observe(panelRef.value);
255
+ }
256
+ }
257
+
258
+ function detachObservers() {
259
+ positionScheduled = false;
260
+ if (resizeObserver) { resizeObserver.disconnect(); resizeObserver = null; }
261
+ window.removeEventListener("resize", schedulePosition);
262
+ }
263
+
264
+ function cleanup() {
265
+ detachObservers();
266
+ if (handle.value) {
267
+ manager.unregister(handle.value.id);
268
+ handle.value = null;
269
+ }
270
+ }
271
+
272
+ function fullCleanup() {
273
+ cleanup();
274
+ coords.value = {};
275
+ }
276
+
277
+ function onPopupAfterLeave() {
278
+ fullCleanup();
279
+ }
280
+
281
+ watch(
282
+ () => props.anchorRef,
283
+ (anchor) => {
284
+ if (anchor && props.modelValue) {
285
+ if (resizeObserver) resizeObserver.observe(anchor);
286
+ schedulePosition();
287
+ }
288
+ },
289
+ );
290
+
291
+ watch(
292
+ () => props.modelValue,
293
+ (open) => {
294
+ if (open) {
295
+ fullCleanup();
296
+ handle.value = manager.register("dropdown", false);
297
+ computeInitialCoords();
298
+ attachObservers();
299
+ attachOutsideClickShield();
300
+ nextTick(() => {
301
+ computePosition();
302
+ schedulePosition();
303
+ });
304
+ } else {
305
+ detachObservers();
306
+ detachOutsideClickShield();
307
+ }
308
+ },
309
+ { immediate: true },
310
+ );
311
+
312
+ onUnmounted(() => {
313
+ detachOutsideClickShield();
314
+ fullCleanup();
315
+ });
316
+ onDeactivated(() => {
317
+ if (props.modelValue) {
318
+ detachObservers();
319
+ detachOutsideClickShield();
320
+ }
321
+ });
322
+ onActivated(() => {
323
+ if (props.modelValue) {
324
+ attachObservers();
325
+ attachOutsideClickShield();
326
+ schedulePosition();
327
+ }
328
+ });
329
+
330
+ function onDocumentClick(e: MouseEvent) {
331
+ if (!props.closeOnBackdrop || !props.modelValue) return;
332
+ const target = e.target as Node;
333
+ if (panelRef.value?.contains(target)) return;
334
+ if (props.anchorRef?.contains(target)) return;
335
+ close();
336
+ }
337
+
338
+ function attachOutsideClickShield() {
339
+ document.addEventListener("click", onDocumentClick, true);
340
+ }
341
+
342
+ function detachOutsideClickShield() {
343
+ document.removeEventListener("click", onDocumentClick, true);
344
+ }
345
+
346
+ function onPanelKeydown(e: KeyboardEvent) {
347
+ if (e.key === "Escape" && props.closeOnEscape) {
348
+ e.preventDefault();
349
+ close();
350
+ }
351
+ }
352
+
353
+ const backdropZ = computed(() => (handle.value?.zIndex ?? 0));
354
+ const panelZ = computed(() => (handle.value?.zIndex ?? 0) + 1);
355
+
356
+ const panelStyle = computed(() => {
357
+ const c = coords.value;
358
+ return {
359
+ ...(c.top != null ? { top: `${c.top}px` } : {}),
360
+ ...(c.left != null ? { left: `${c.left}px` } : {}),
361
+ ...(c.bottom != null ? { bottom: `${c.bottom}px` } : {}),
362
+ ...(c.right != null ? { right: `${c.right}px` } : {}),
363
+ position: "fixed" as const,
364
+ pointerEvents: "auto" as const,
365
+ zIndex: panelZ.value,
366
+ };
367
+ });
368
+
369
+ return () => (
370
+ <Teleport to="body">
371
+ {props.backdrop && props.modelValue && (
372
+ <div
373
+ class="hk-popover-backdrop"
374
+ style={{ zIndex: backdropZ.value }}
375
+ />
376
+ )}
377
+ <Transition
378
+ name="hk-popover"
379
+ appear
380
+ onBeforeEnter={() => animBus.run()}
381
+ onAfterEnter={() => animBus.cancel()}
382
+ onBeforeLeave={() => animBus.run()}
383
+ onAfterLeave={() => { animBus.cancel(); onPopupAfterLeave(); }}
384
+ >
385
+ {props.modelValue ? (
386
+ <div
387
+ ref={panelRef}
388
+ class={[
389
+ "hk-popover-panel",
390
+ `hk-popover-${resolvedPlacement.value}`,
391
+ props.glass ? "hii-dropdown-content" : "",
392
+ ...(typeof attrs.class === "string" ? [attrs.class]
393
+ : Array.isArray(attrs.class) ? attrs.class as string[]
394
+ : attrs.class && typeof attrs.class === "object"
395
+ ? Object.entries(attrs.class as Record<string, unknown>)
396
+ .filter(([, v]) => v).map(([k]) => k)
397
+ : []),
398
+ ]}
399
+ style={panelStyle.value}
400
+ role="dialog"
401
+ onKeydown={onPanelKeydown}
402
+ >
403
+ {slots.default?.()}
404
+ </div>
405
+ ) : null}
406
+ </Transition>
407
+ </Teleport>
408
+ );
409
+ },
410
+ });
@@ -0,0 +1,136 @@
1
+ @use "../tokens";
2
+
3
+ /* HkPopupSelect */
4
+
5
+ .hk-popup-select-wrapper {
6
+ display: block;
7
+ width: 100%;
8
+ }
9
+
10
+ .hk-popup-select-label {
11
+ display: block;
12
+ font-size: var(--text-base);
13
+ font-weight: 500;
14
+ margin-bottom: var(--space-6);
15
+ color: rgb(var(--color-text));
16
+ }
17
+
18
+ .hk-popup-select-required {
19
+ margin-left: var(--space-2);
20
+ color: rgb(var(--color-error));
21
+ }
22
+
23
+ .hk-popup-select-trigger {
24
+ display: flex;
25
+ align-items: center;
26
+ width: 100%;
27
+ padding: var(--space-8) var(--space-32) var(--space-8) var(--space-12);
28
+ min-height: 2.5rem;
29
+ background: rgb(var(--color-surface) / var(--opacity-half));
30
+ backdrop-filter: blur(var(--blur-sm));
31
+ border: 1px solid var(--border-input);
32
+ border-radius: var(--radius-md);
33
+ font-family: inherit;
34
+ font-size: var(--text-base);
35
+ line-height: 1.5;
36
+ color: rgb(var(--color-text));
37
+ text-align: center;
38
+ cursor: pointer;
39
+ outline: none;
40
+ transition: background-color, border-color, color, box-shadow, opacity, transform, filter var(--duration-normal) var(--ease-standard);
41
+ position: relative;
42
+ }
43
+
44
+ .hk-popup-select-trigger:hover:not([data-disabled]) {
45
+ border-color: var(--c-primary-strong);
46
+ }
47
+
48
+ .hk-popup-select-trigger:focus,
49
+ .hk-popup-select-trigger[data-state="open"] {
50
+ border-color: rgb(var(--color-focused-border));
51
+ box-shadow: var(--shadow-focus);
52
+ background: rgb(var(--color-surface));
53
+ }
54
+
55
+ .hk-popup-select-trigger[data-error] {
56
+ border-color: var(--c-error-strong);
57
+ }
58
+
59
+ .hk-popup-select-trigger[data-error]:focus,
60
+ .hk-popup-select-trigger[data-error][data-state="open"] {
61
+ border-color: rgb(var(--color-error));
62
+ box-shadow: var(--shadow-focus-error);
63
+ }
64
+
65
+ .hk-popup-select-trigger[data-disabled] {
66
+ opacity: 0.5;
67
+ cursor: not-allowed;
68
+ }
69
+
70
+ .hk-popup-select-value {
71
+ flex: 1;
72
+ white-space: nowrap;
73
+ overflow: hidden;
74
+ text-overflow: ellipsis;
75
+ }
76
+
77
+ .hk-popup-select-arrow {
78
+ position: absolute;
79
+ right: 0.75rem;
80
+ color: color-mix(in srgb, rgb(var(--color-muted)) 70%, rgb(var(--color-background)));
81
+ pointer-events: none;
82
+ flex-shrink: 0;
83
+ transition: transform var(--duration-normal) ease;
84
+ }
85
+
86
+ .hk-popup-select-trigger[data-state="open"] .hk-popup-select-arrow {
87
+ transform: rotate(180deg);
88
+ }
89
+
90
+ .hk-popup-select-content {
91
+ min-width: 180px;
92
+ max-height: 240px;
93
+ display: flex;
94
+ flex-direction: column;
95
+ border-radius: var(--radius-sm);
96
+ overflow: hidden;
97
+ }
98
+
99
+ .hk-popup-select-viewport {
100
+ flex: 1;
101
+ min-height: 0;
102
+ }
103
+
104
+ .hk-popup-select-item {
105
+ display: flex;
106
+ align-items: center;
107
+ gap: var(--space-8);
108
+ padding: var(--space-10) var(--space-16);
109
+ font-size: var(--text-base);
110
+ color: rgb(var(--color-text));
111
+ border: 1px solid transparent;
112
+ border-radius: var(--radius-md);
113
+ cursor: pointer;
114
+ outline: none;
115
+ transition: background-color, border-color, color, box-shadow, opacity, transform, filter var(--duration-normal) ease;
116
+ text-align: left;
117
+ }
118
+
119
+ .hk-popup-select-item:hover,
120
+ .hk-popup-select-item[data-highlighted] {
121
+ background: var(--c-primary-subtle);
122
+ border-color: var(--c-primary-medium);
123
+ }
124
+
125
+ .hk-popup-select-item[data-checked] {
126
+ background: var(--c-primary-dim);
127
+ border-color: var(--c-primary-strong);
128
+ color: rgb(var(--color-primary));
129
+ font-weight: 600;
130
+ }
131
+
132
+ .hk-popup-select-error {
133
+ margin-top: var(--space-4);
134
+ font-size: var(--text-xs);
135
+ color: rgb(var(--color-error));
136
+ }