@celestia-island/hikari 0.53.1 → 0.54.0
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.
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { createApp, h, nextTick } from "vue";
|
|
2
|
+
import { createApp, h, nextTick, ref } from "vue";
|
|
3
3
|
|
|
4
4
|
import { HkThemeToggle } from "./HkThemeToggle";
|
|
5
5
|
import { themePresets, useTheme, type CustomThemePreset } from "../theme";
|
|
@@ -356,3 +356,65 @@ describe("HkThemeToggle item slots", () => {
|
|
|
356
356
|
}
|
|
357
357
|
});
|
|
358
358
|
});
|
|
359
|
+
|
|
360
|
+
describe("HkThemeToggle exposed closeMenu", () => {
|
|
361
|
+
/** Public surface HkThemeToggle exposes via setup expose() —
|
|
362
|
+
* InstanceType does not carry expose() members, so type it
|
|
363
|
+
* structurally (the consumer-side contract this ships for). */
|
|
364
|
+
interface ThemeTogglePublic {
|
|
365
|
+
closeMenu: () => void;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function mountToggleWithRef(): {
|
|
369
|
+
container: HTMLElement;
|
|
370
|
+
toggle: () => ThemeTogglePublic;
|
|
371
|
+
} {
|
|
372
|
+
const container = document.createElement("div");
|
|
373
|
+
document.body.appendChild(container);
|
|
374
|
+
const handle = ref<ThemeTogglePublic | null>(null);
|
|
375
|
+
const app = createApp({
|
|
376
|
+
render: () => h(HkThemeToggle, { ref: handle, externalCustomize: true }),
|
|
377
|
+
});
|
|
378
|
+
app.mount(container);
|
|
379
|
+
mounts.push({ app, container });
|
|
380
|
+
return {
|
|
381
|
+
container,
|
|
382
|
+
toggle: () => {
|
|
383
|
+
const t = handle.value;
|
|
384
|
+
if (!t) throw new Error("HkThemeToggle ref never populated");
|
|
385
|
+
return t;
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
it("closes the open menu through the exposed closeMenu", async () => {
|
|
391
|
+
const { container, toggle } = mountToggleWithRef();
|
|
392
|
+
await settle();
|
|
393
|
+
|
|
394
|
+
openMenu(container);
|
|
395
|
+
await settle();
|
|
396
|
+
expect(document.body.querySelector(".s-theme-menu")).toBeTruthy();
|
|
397
|
+
|
|
398
|
+
// Host-row affordances (e.g. a per-theme edit button) open delegated
|
|
399
|
+
// second-level windows: the desktop popover must be dismissable
|
|
400
|
+
// programmatically — the same teardown path as every internal close.
|
|
401
|
+
toggle().closeMenu();
|
|
402
|
+
await settle();
|
|
403
|
+
|
|
404
|
+
expect(document.body.querySelector(".s-theme-menu")).toBeNull();
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it("closeMenu is a no-op while the menu is closed", async () => {
|
|
408
|
+
const { container, toggle } = mountToggleWithRef();
|
|
409
|
+
await settle();
|
|
410
|
+
|
|
411
|
+
expect(() => toggle().closeMenu()).not.toThrow();
|
|
412
|
+
await settle();
|
|
413
|
+
expect(document.body.querySelector(".s-theme-menu")).toBeNull();
|
|
414
|
+
|
|
415
|
+
// The menu still opens normally afterwards.
|
|
416
|
+
openMenu(container);
|
|
417
|
+
await settle();
|
|
418
|
+
expect(document.body.querySelector(".s-theme-menu")).toBeTruthy();
|
|
419
|
+
});
|
|
420
|
+
});
|
|
@@ -56,6 +56,15 @@ export interface ThemeItemScope {
|
|
|
56
56
|
* group (before the divider). Mode-adjacent host chrome that is NOT a
|
|
57
57
|
* theme-editor concern lives here (e.g. display-scale control).
|
|
58
58
|
*
|
|
59
|
+
* `closeMenu()` (setup expose) — programmatic close for host content
|
|
60
|
+
* living INSIDE the menu (row slot affordances). A host that opens a
|
|
61
|
+
* delegated second-level window from a row (e.g. a per-theme editor)
|
|
62
|
+
* calls it to dismiss the menu; the menu is ONE state in both its forms
|
|
63
|
+
* (desktop popover, mobile bottom sheet), so this closes whichever is
|
|
64
|
+
* showing — a mobile-aware host decides whether to call it at all, no
|
|
65
|
+
* automatic close fires from here. InstanceType does not carry
|
|
66
|
+
* expose() members — type the ref structurally at the consumer.
|
|
67
|
+
*
|
|
59
68
|
* Color-mode group: the unified HTabs strip in segmented (radiogroup)
|
|
60
69
|
* working mode (Auto | Light | Dark) — same pill chrome as every other
|
|
61
70
|
* group. In AUTO mode the Light/Dark halves merge into the strip's
|
|
@@ -84,7 +93,7 @@ export const HkThemeToggle = defineComponent({
|
|
|
84
93
|
"update:scheme": (_theme: HCustomTheme) => true,
|
|
85
94
|
"open-customize": () => true,
|
|
86
95
|
},
|
|
87
|
-
setup(props, { emit, slots }) {
|
|
96
|
+
setup(props, { emit, slots, expose }) {
|
|
88
97
|
const { t } = useI18n();
|
|
89
98
|
const { currentTheme, currentMode, effectiveMode, geo, setTheme, setMode, toggleMode, allThemeList, addCustomTheme, removeCustomTheme, customThemes } = useTheme();
|
|
90
99
|
|
|
@@ -104,6 +113,19 @@ export const HkThemeToggle = defineComponent({
|
|
|
104
113
|
const triggerRef = ref<HTMLElement | null>(null);
|
|
105
114
|
const schemeDialogOpen = ref(false);
|
|
106
115
|
|
|
116
|
+
/** Programmatic close for host content inside the menu (row slot
|
|
117
|
+
* affordances): a host opening a delegated second-level window from
|
|
118
|
+
* a theme row calls this to dismiss the menu. The menu is ONE state
|
|
119
|
+
* in both its forms — desktop popover and mobile bottom sheet — so
|
|
120
|
+
* this closes whichever is showing, with no breakpoint branching
|
|
121
|
+
* here; a mobile-aware host decides whether to call it at all.
|
|
122
|
+
* Same teardown as every internal close — the alt-timer cleanup
|
|
123
|
+
* rides the menuOpen watcher. */
|
|
124
|
+
function closeMenu() {
|
|
125
|
+
menuOpen.value = false;
|
|
126
|
+
}
|
|
127
|
+
expose({ closeMenu });
|
|
128
|
+
|
|
107
129
|
// ── Solar-altitude readout (auto-mode merged cell) ──────────────
|
|
108
130
|
// `geo` comes from the theme clock (timezone estimate until the real
|
|
109
131
|
// fix lands) — the engine and this readout share one resolution.
|
|
@@ -189,7 +211,7 @@ export const HkThemeToggle = defineComponent({
|
|
|
189
211
|
|
|
190
212
|
function onSelectTheme(id: ThemeId) {
|
|
191
213
|
setTheme(id);
|
|
192
|
-
|
|
214
|
+
closeMenu();
|
|
193
215
|
}
|
|
194
216
|
|
|
195
217
|
function onConfirmScheme(theme: HCustomTheme) {
|
|
@@ -334,7 +356,7 @@ export const HkThemeToggle = defineComponent({
|
|
|
334
356
|
type="button"
|
|
335
357
|
class="s-theme-item-btn s-theme-item-customize"
|
|
336
358
|
onClick={() => {
|
|
337
|
-
|
|
359
|
+
closeMenu();
|
|
338
360
|
if (props.externalCustomize) {
|
|
339
361
|
emit("open-customize");
|
|
340
362
|
} else {
|