@celestia-island/hikari 0.55.248 → 0.55.250
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 +5 -1
- package/src/components/HkAffixPicker.scss +15 -43
- package/src/components/HkMenuIdentityItem.scss +4 -0
- package/src/components/HkSolarLocationSetting.test.tsx +31 -21
- package/src/components/HkSolarLocationSetting.tsx +13 -14
- package/src/components/HkTagInput.scss +39 -49
- package/src/components/HkTagInput.tsx +6 -0
- package/src/components/HkTemplateField.scss +24 -10
- package/src/components/HkTimeZoneGlobe.scss +125 -0
- package/src/components/HkTimeZoneGlobe.test.tsx +128 -0
- package/src/components/HkTimeZoneGlobe.tsx +385 -0
- package/src/components/HkUserPicker.scss +11 -18
- package/src/components/HkUserPicker.tsx +5 -0
- package/src/components/HkWaterfall.styles.test.ts +97 -37
- package/src/components/_menu-item.contract.test.ts +66 -0
- package/src/components/_menu-item.scss +19 -2
- package/src/i18n/locales/ar/platform.json +90 -1
- package/src/i18n/locales/de/platform.json +90 -1
- package/src/i18n/locales/en/platform.json +90 -1
- package/src/i18n/locales/es/platform.json +90 -1
- package/src/i18n/locales/fr/platform.json +90 -1
- package/src/i18n/locales/ja/platform.json +90 -1
- package/src/i18n/locales/ko/platform.json +90 -1
- package/src/i18n/locales/pt/platform.json +90 -1
- package/src/i18n/locales/ru/platform.json +90 -1
- package/src/i18n/locales/zh-Hans/platform.json +90 -1
- package/src/i18n/locales/zh-Hant/platform.json +90 -1
- package/src/plugins/boot-recovery.d.mts +132 -0
- package/src/plugins/boot-recovery.install.test.ts +280 -0
- package/src/plugins/boot-recovery.mjs +685 -0
- package/src/plugins/boot-recovery.test.ts +970 -0
- package/src/plugins/index.ts +19 -0
- package/src/runtime/closeGuard.test.ts +279 -0
- package/src/runtime/closeGuard.ts +264 -0
- package/src/runtime/index.ts +7 -0
- package/src/styles/admin-tokens.scss +9 -0
- package/src/theme/solarGlobeAlignment.test.ts +88 -0
- package/src/theme/solarGlobeMap.test.ts +66 -0
- package/src/theme/solarGlobeMap.ts +193 -0
- package/src/theme/solarTimeZoneLocales.test.ts +92 -0
- package/src/theme/solarTimeZones.test.ts +139 -0
- package/src/theme/solarTimeZones.ts +190 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@celestia-island/hikari",
|
|
3
|
-
"version": "0.55.
|
|
3
|
+
"version": "0.55.250",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Hikari UI library, Vue 3 runtime — one design system for Vue 3 and Rust/WASM applications",
|
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
"./plugins/boot-chrome": {
|
|
42
42
|
"import": "./src/plugins/boot-chrome.mjs",
|
|
43
43
|
"types": "./src/plugins/boot-chrome.d.mts"
|
|
44
|
+
},
|
|
45
|
+
"./plugins/boot-recovery": {
|
|
46
|
+
"import": "./src/plugins/boot-recovery.mjs",
|
|
47
|
+
"types": "./src/plugins/boot-recovery.d.mts"
|
|
44
48
|
}
|
|
45
49
|
},
|
|
46
50
|
"files": [
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* their own class on top via chipClass), while the popup — search,
|
|
5
5
|
* selected tags, option rows, custom row — is fully standardized here. */
|
|
6
6
|
|
|
7
|
+
@use "./menu-item" as mi;
|
|
8
|
+
|
|
7
9
|
/* ── chip base ────────────────────────────────────────────────────────
|
|
8
10
|
* Neutral clickable chip; hosts override visually via their own class
|
|
9
11
|
* (HkPhoneInput's dial chip, HkLocalizedInput's badge chip, …). */
|
|
@@ -232,55 +234,25 @@
|
|
|
232
234
|
display: flex;
|
|
233
235
|
flex-direction: column;
|
|
234
236
|
gap: 1px;
|
|
235
|
-
|
|
237
|
+
/* GEOMETRY-NEUTRAL band (menu-item WIDTH invariant): block-axis rhythm
|
|
238
|
+
* only, NO inline term — the hosting panel's inset (popout 8px / sheet
|
|
239
|
+
* 16px) is the one horizontal inset these rows get. The old 6px inline
|
|
240
|
+
* term stacked a second inset and pulled the rows' wash 6px inside
|
|
241
|
+
* every sibling family's in the same sheet. */
|
|
242
|
+
padding-block: var(--space-4, 4px) var(--space-8, 8px);
|
|
236
243
|
}
|
|
237
244
|
|
|
238
245
|
.hk-affix-row {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
border-radius: var(--radius-sm, 6px);
|
|
247
|
-
background: transparent;
|
|
248
|
-
color: rgb(var(--color-text));
|
|
249
|
-
font-size: var(--text-sm, 0.8125rem);
|
|
250
|
-
text-align: start;
|
|
251
|
-
cursor: pointer;
|
|
252
|
-
appearance: none;
|
|
253
|
-
transition: background 0.1s ease;
|
|
254
|
-
|
|
255
|
-
&:hover {
|
|
256
|
-
background: rgb(var(--color-primary) / 10%);
|
|
257
|
-
outline: none;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/* Keyboard position is drawn INSIDE the row (HkSelectionGrid branch-fix
|
|
261
|
-
* pattern) and distinct from the hover tint, so overlapping popup/sheet
|
|
262
|
-
* chrome cannot shave a per-side outer ring and focus reads as focus. */
|
|
263
|
-
&:focus-visible {
|
|
264
|
-
background: rgb(var(--color-primary) / 10%);
|
|
265
|
-
outline: 2px solid rgb(var(--color-primary));
|
|
266
|
-
outline-offset: -2px;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
&[data-active] {
|
|
270
|
-
background: rgb(var(--color-primary) / 16%);
|
|
271
|
-
font-weight: 600;
|
|
272
|
-
}
|
|
246
|
+
/* Unified menu-item spec (_menu-item.scss): one row grammar with the
|
|
247
|
+
* select options and menu rows around it — metrics (34px / 6×12,
|
|
248
|
+
* 44px / 10×16 inside sheets via the shared registration list),
|
|
249
|
+
* reserved 1px border, guarded hover, primary wash + strong border on
|
|
250
|
+
* [data-active], disabled dim, 2px inset focus ring. The sheet reach
|
|
251
|
+
* comes from the registration list, not from here. */
|
|
252
|
+
@include mi.item;
|
|
273
253
|
|
|
274
254
|
&[data-disabled] {
|
|
275
|
-
opacity: 0.5;
|
|
276
255
|
cursor: default;
|
|
277
|
-
|
|
278
|
-
/* The button stays keyboard-focusable (data-disabled only), so the
|
|
279
|
-
* unpickable row must not paint the pickable tint on focus either. */
|
|
280
|
-
&:hover,
|
|
281
|
-
&:focus-visible {
|
|
282
|
-
background: transparent;
|
|
283
|
-
}
|
|
284
256
|
}
|
|
285
257
|
|
|
286
258
|
/* The "Use <query>" free-text row reads as an add affordance. Truncation
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
gap: var(--space-10, 10px);
|
|
9
9
|
width: 100%;
|
|
10
10
|
padding: var(--space-8, 8px) var(--space-12, 12px);
|
|
11
|
+
/* The 1px transparent border is RESERVED, not decorative: the
|
|
12
|
+
HkMenuActionItem rows beside this block draw their content edge at
|
|
13
|
+
1px + pad-x, so an unreserved block sat 1px left of them. */
|
|
14
|
+
border: 1px solid transparent;
|
|
11
15
|
border-radius: var(--radius-sm, 4px);
|
|
12
16
|
min-width: 0;
|
|
13
17
|
}
|
|
@@ -7,7 +7,6 @@ import { setLocale } from "../i18n/context";
|
|
|
7
7
|
import {
|
|
8
8
|
SOLAR_LOCATION_STRATEGY_STORAGE_KEY,
|
|
9
9
|
SOLAR_MANUAL_LOCATION_STORAGE_KEY,
|
|
10
|
-
listTimeZones,
|
|
11
10
|
manualLocationOf,
|
|
12
11
|
setSolarLocationStrategy,
|
|
13
12
|
setSolarManualLocation,
|
|
@@ -21,7 +20,7 @@ import {
|
|
|
21
20
|
* HkSolarLocationSetting — the solar clock's location-strategy row.
|
|
22
21
|
* The strategy engine (theme/useSolarTime.ts) has its own suite; these
|
|
23
22
|
* cases pin the COMPONENT contract: the live summary row, the secondary
|
|
24
|
-
* HkModal editor (strategy tabs /
|
|
23
|
+
* HkModal editor (strategy tabs / time-zone globe / explicit
|
|
25
24
|
* hemisphere), OK applying through setSolarManualLocation →
|
|
26
25
|
* setSolarLocationStrategy → refreshThemeClock with storage evidence,
|
|
27
26
|
* and cancel leaving the engine untouched.
|
|
@@ -127,15 +126,18 @@ function clickStrategy(key: "timezone" | "ip" | "manual"): void {
|
|
|
127
126
|
clickStrategyTab({ timezone: "Time zone", ip: "IP location", manual: "Manual" }[key]);
|
|
128
127
|
}
|
|
129
128
|
|
|
130
|
-
/**
|
|
129
|
+
/** Picks a zone on the manual pane's globe (the fallback sector list in
|
|
130
|
+
* the test DOM — happy-dom has no WebGL). */
|
|
131
131
|
async function pickTimeZone(zone: string): Promise<void> {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
// The picker is a defineAsyncComponent chunk — wait it in.
|
|
133
|
+
let option: HTMLButtonElement | undefined;
|
|
134
|
+
for (let i = 0; i < 20 && !option; i++) {
|
|
135
|
+
option = document.body.querySelector<HTMLButtonElement>(
|
|
136
|
+
'[data-test="hk-timezone-globe-option"][data-zone="' + zone + '"]',
|
|
137
|
+
) ?? undefined;
|
|
138
|
+
if (!option) await settle();
|
|
139
|
+
}
|
|
140
|
+
expect(option, `globe sector for "${zone}" renders`).toBeTruthy();
|
|
139
141
|
option!.click();
|
|
140
142
|
await settle();
|
|
141
143
|
}
|
|
@@ -239,7 +241,13 @@ describe("HkSolarLocationSetting editor", () => {
|
|
|
239
241
|
const editor = document.body.querySelector<HTMLElement>(".hk-solar-editor")!;
|
|
240
242
|
expect(editor.getAttribute("data-strategy")).toBe("manual");
|
|
241
243
|
expect(document.body.querySelector(".hk-solar-manual")).toBeTruthy();
|
|
242
|
-
|
|
244
|
+
// The globe is a defineAsyncComponent chunk — wait it in.
|
|
245
|
+
let globe: HTMLElement | null = null;
|
|
246
|
+
for (let i = 0; i < 20 && !globe; i++) {
|
|
247
|
+
globe = document.body.querySelector<HTMLElement>('[data-test="hk-timezone-globe"]');
|
|
248
|
+
if (!globe) await settle();
|
|
249
|
+
}
|
|
250
|
+
expect(globe, "globe picker renders in the manual pane").toBeTruthy();
|
|
243
251
|
const hemi = hemisphereTriggers();
|
|
244
252
|
expect(hemi.map((b) => b.textContent?.trim())).toEqual(["Northern", "Southern"]);
|
|
245
253
|
// Draft default: the system zone, northern hemisphere (explicit
|
|
@@ -357,23 +365,25 @@ describe("HkSolarLocationSetting i18n", () => {
|
|
|
357
365
|
]);
|
|
358
366
|
});
|
|
359
367
|
|
|
360
|
-
it("
|
|
368
|
+
it("picks from the globe's 24-sector wheel, not the raw IANA catalog", async () => {
|
|
361
369
|
const container = mountSetting();
|
|
362
370
|
await openEditor(container);
|
|
363
371
|
clickStrategy("manual");
|
|
364
372
|
await settle();
|
|
365
373
|
|
|
366
|
-
|
|
367
|
-
trigger!.click();
|
|
368
|
-
await settle();
|
|
374
|
+
await pickTimeZone("Australia/Sydney");
|
|
369
375
|
|
|
370
|
-
const rendered = [...document.body.querySelectorAll<
|
|
371
|
-
.map((el) => el.
|
|
372
|
-
|
|
373
|
-
|
|
376
|
+
const rendered = [...document.body.querySelectorAll<HTMLButtonElement>('[data-test="hk-timezone-globe-option"]')]
|
|
377
|
+
.map((el) => el.dataset.zone);
|
|
378
|
+
// 24 representative zones — NOT the hundreds-of-ids IANA catalog
|
|
379
|
+
// the picker replaced.
|
|
380
|
+
expect(rendered).toHaveLength(24);
|
|
381
|
+
expect(new Set(rendered).size).toBe(24);
|
|
374
382
|
expect(rendered).toContain("Asia/Shanghai");
|
|
375
|
-
// A catalog-present zone on every ICU: "UTC" itself is NOT part of
|
|
376
|
-
// Intl.supportedValuesOf("timeZone") on some ICU builds.
|
|
377
383
|
expect(rendered).toContain("Australia/Sydney");
|
|
384
|
+
// The draft (and its resolved readout) carries the picked zone id.
|
|
385
|
+
const resolved = document.body.querySelector<HTMLElement>('[data-test="hk-solar-resolved"]');
|
|
386
|
+
const geo = manualLocationOf({ timeZone: "Australia/Sydney", hemisphere: "north" });
|
|
387
|
+
expect(resolved!.textContent).toContain(geo.lng.toFixed(1));
|
|
378
388
|
});
|
|
379
389
|
});
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
// AI-generated: developed with the ZCode agent (model GLM-5.3); human-directed.
|
|
2
|
-
import { computed, defineComponent, ref } from "vue";
|
|
2
|
+
import { computed, defineAsyncComponent, defineComponent, ref } from "vue";
|
|
3
3
|
import { Clock, Globe2, MapPin } from "lucide-vue-next";
|
|
4
4
|
|
|
5
5
|
import { useI18n } from "../i18n/context";
|
|
6
6
|
import { refreshThemeClock } from "../theme/useTheme";
|
|
7
7
|
import {
|
|
8
8
|
hydrateSolarLocationState,
|
|
9
|
-
listTimeZones,
|
|
10
9
|
manualLocationOf,
|
|
11
10
|
setSolarLocationStrategy,
|
|
12
11
|
setSolarManualLocation,
|
|
@@ -19,10 +18,15 @@ import {
|
|
|
19
18
|
} from "../theme/useSolarTime";
|
|
20
19
|
import HkButton from "./HkButton";
|
|
21
20
|
import HkModal, { type ModalAction } from "./HkModal";
|
|
22
|
-
import HkSelect, { type HkSelectOption } from "./HkSelect";
|
|
23
21
|
import HTabs, { type TabItem } from "./HkTabs";
|
|
24
22
|
import "./HkSolarLocationSetting.scss";
|
|
25
23
|
|
|
24
|
+
// The globe picker loads lazily (defineAsyncComponent, module level —
|
|
25
|
+
// the async wrapper is created once, import() is module-cached):
|
|
26
|
+
// three.js and the board only enter the bundle graph when a host
|
|
27
|
+
// actually opens the manual pane — the settings row stays weightless.
|
|
28
|
+
const HkTimeZoneGlobe = defineAsyncComponent(() => import("./HkTimeZoneGlobe"));
|
|
29
|
+
|
|
26
30
|
/**
|
|
27
31
|
* HkSolarLocationSetting — the solar clock's location STRATEGY row
|
|
28
32
|
* (2026-10-31 product direction: strategies, not a fixed cascade).
|
|
@@ -89,13 +93,6 @@ export const HkSolarLocationSetting = defineComponent({
|
|
|
89
93
|
open.value = true;
|
|
90
94
|
}
|
|
91
95
|
|
|
92
|
-
/** IANA zone list for the manual pane's select. Plain data (no
|
|
93
|
-
* vnodes), static for the process lifetime — cached once. */
|
|
94
|
-
let timeZoneOptionsCache: HkSelectOption[] | null = null;
|
|
95
|
-
function timeZoneOptions(): HkSelectOption[] {
|
|
96
|
-
timeZoneOptionsCache ??= listTimeZones().map((id) => ({ value: id, label: id }));
|
|
97
|
-
return timeZoneOptionsCache;
|
|
98
|
-
}
|
|
99
96
|
|
|
100
97
|
// ── Live summary (the row's label) ────────────────────────────────
|
|
101
98
|
const summaryText = computed(() => {
|
|
@@ -234,12 +231,14 @@ export const HkSolarLocationSetting = defineComponent({
|
|
|
234
231
|
<p class="hk-solar-hint">{hintText.value}</p>
|
|
235
232
|
{draftStrategy.value === "manual" && (
|
|
236
233
|
<div class="hk-solar-manual">
|
|
237
|
-
|
|
234
|
+
{/* The globe picker (2026-10-31): 24 equal time-sector
|
|
235
|
+
wedges on a spinning, borderless world-plates sphere,
|
|
236
|
+
translated tooltip included — the IANA dropdown it
|
|
237
|
+
replaced listed hundreds of raw zone ids. Still
|
|
238
|
+
writes the same draftTimeZone the engine applies. */}
|
|
239
|
+
<HkTimeZoneGlobe
|
|
238
240
|
modelValue={draftTimeZone.value}
|
|
239
241
|
onUpdate:modelValue={onSelectTimeZone}
|
|
240
|
-
options={timeZoneOptions()}
|
|
241
|
-
label={t("hikari::theme.solar.strategyTimezone")}
|
|
242
|
-
panelTitle={t("hikari::theme.solar.strategyTimezone")}
|
|
243
242
|
/>
|
|
244
243
|
<div class="hk-solar-hemisphere">
|
|
245
244
|
<label class="hk-solar-field-label">{t("hikari::theme.solar.hemisphere")}</label>
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* stretching the panel). Chips and selected rows are reorderable; the
|
|
9
9
|
* drag states (`data-dragging` / `data-drop`) live at the bottom. */
|
|
10
10
|
|
|
11
|
+
@use "./menu-item" as mi;
|
|
11
12
|
@use "../styles/polyfill" as *;
|
|
12
13
|
|
|
13
14
|
.hk-tag-input-wrapper {
|
|
@@ -231,56 +232,41 @@
|
|
|
231
232
|
display: flex;
|
|
232
233
|
flex-direction: column;
|
|
233
234
|
gap: 1px;
|
|
234
|
-
|
|
235
|
+
/* GEOMETRY-NEUTRAL band (menu-item WIDTH invariant): block-axis rhythm
|
|
236
|
+
* only — the hosting panel's inset is the one horizontal inset these
|
|
237
|
+
* rows get (the old 6px inline term stacked a second inset). */
|
|
238
|
+
padding-block: var(--space-4, 4px) var(--space-8, 8px);
|
|
235
239
|
}
|
|
236
240
|
|
|
237
241
|
.hk-tag-input-row {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
* the row stays clickable so it can be toggled back off. */
|
|
259
|
-
&[data-selected] {
|
|
260
|
-
font-weight: 600;
|
|
261
|
-
|
|
262
|
-
&:hover {
|
|
263
|
-
background: rgb(var(--color-primary) / 16%);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/* Keyboard cursor (`aria-activedescendant`): the field's own focus-ring
|
|
268
|
-
* language, inset so the highlight never shifts the row's metrics and
|
|
269
|
-
* never clips at the list edge. Focus itself stays in the field. */
|
|
242
|
+
/* Unified menu-item spec (_menu-item.scss): one row grammar with the
|
|
243
|
+
* select options and menu rows around it — metrics, reserved border,
|
|
244
|
+
* guarded hover, checked face, focus ring; the sheet reach comes from
|
|
245
|
+
* the shared registration list. The tsx emits the grammar's
|
|
246
|
+
* `data-checked` for the selected face (so mi.item's hover guard
|
|
247
|
+
* protects it) ALONGSIDE this family's legacy `data-selected`
|
|
248
|
+
* (tests/hosts key on it). `data-active` is the field's
|
|
249
|
+
* aria-activedescendant keyboard cursor (focus itself stays in the
|
|
250
|
+
* field) — mi.item has no HIGHLIGHT face, and its SELECTED face
|
|
251
|
+
* would bleed through a partial restatement, so the cursor face is
|
|
252
|
+
* restated in full below; mi.item's hover guard already excludes
|
|
253
|
+
* data-active rows. */
|
|
254
|
+
@include mi.item;
|
|
255
|
+
|
|
256
|
+
/* [data-active] is the field's aria-activedescendant keyboard cursor —
|
|
257
|
+
the grammar's HIGHLIGHT role. mi.item has no cursor face, but its
|
|
258
|
+
[data-active]/[data-checked] SELECTED face would bleed through a
|
|
259
|
+
partial restatement, so the cursor face restates the full set back
|
|
260
|
+
to the row's resting values: wash + inset ring only, reserved
|
|
261
|
+
border transparent, normal text color and weight. */
|
|
270
262
|
&[data-active] {
|
|
271
|
-
background: rgb(var(--color-primary) /
|
|
263
|
+
background: rgb(var(--color-primary) / 8%);
|
|
264
|
+
border-color: transparent;
|
|
265
|
+
color: rgb(var(--color-text) / 88%);
|
|
266
|
+
font-weight: var(--hk-menu-item-weight);
|
|
272
267
|
box-shadow: inset 0 0 0 1px rgb(var(--color-focused-border));
|
|
273
268
|
}
|
|
274
269
|
|
|
275
|
-
&[data-disabled] {
|
|
276
|
-
opacity: 0.5;
|
|
277
|
-
cursor: default;
|
|
278
|
-
|
|
279
|
-
&:hover {
|
|
280
|
-
background: transparent;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
270
|
/* The "Use “<query>”" free-text row reads as an add affordance. */
|
|
285
271
|
&[data-custom="true"] {
|
|
286
272
|
color: rgb(var(--color-primary));
|
|
@@ -393,13 +379,17 @@
|
|
|
393
379
|
align-items: center;
|
|
394
380
|
justify-content: center;
|
|
395
381
|
flex: none;
|
|
396
|
-
/* The largest target the row rhythm allows: the row's content box is
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
382
|
+
/* The largest target the row rhythm allows: the row's content box is
|
|
383
|
+
the grammar row's min-height minus its block padding (34px − 2×6px =
|
|
384
|
+
22px; 44px − 2×10px = 24px inside sheets), so the handle grows to
|
|
385
|
+
that without changing the option rows' height — the 44px touch
|
|
386
|
+
guidance cannot fit in a list row, the sheet scrolls through them
|
|
387
|
+
instead. The grip was sized for the RETIRED 2.25rem/4px stock once:
|
|
388
|
+
at 28px it overflowed the 22px box and every selected+reorderable
|
|
389
|
+
row grew 6px taller than its neighbours. */
|
|
400
390
|
width: 1.5rem;
|
|
401
|
-
height: 1.
|
|
402
|
-
margin-inline-end: -
|
|
391
|
+
height: 1.375rem;
|
|
392
|
+
margin-inline-end: -6px;
|
|
403
393
|
color: rgb(var(--color-muted));
|
|
404
394
|
opacity: 0.6;
|
|
405
395
|
cursor: grab;
|
|
@@ -1053,6 +1053,12 @@ export const HkTagInput = defineComponent({
|
|
|
1053
1053
|
aria-selected={selected}
|
|
1054
1054
|
aria-disabled={inert || undefined}
|
|
1055
1055
|
data-selected={selected || undefined}
|
|
1056
|
+
/* Grammar face attribute (_menu-item.scss):
|
|
1057
|
+
* mi.item's checked face AND its hover guard
|
|
1058
|
+
* key on data-checked — emitting it beside the
|
|
1059
|
+
* legacy data-selected keeps the selected row's
|
|
1060
|
+
* face from being flipped by the hover rule. */
|
|
1061
|
+
data-checked={selected || undefined}
|
|
1056
1062
|
data-disabled={inert || undefined}
|
|
1057
1063
|
data-active={activeId === stop.id || undefined}
|
|
1058
1064
|
data-reorder={reorderable ? "true" : undefined}
|
|
@@ -173,7 +173,13 @@
|
|
|
173
173
|
display: flex;
|
|
174
174
|
flex-direction: column;
|
|
175
175
|
gap: 2px;
|
|
176
|
-
|
|
176
|
+
/* GEOMETRY-NEUTRAL band (menu-item WIDTH invariant): the level's gap
|
|
177
|
+
* owns the rhythm and the hosting surface's inset is the one
|
|
178
|
+
* horizontal inset these rows get — the old 4px all-sides pad was the
|
|
179
|
+
* exact "band that pads itself" anti-pattern (a foot/head row in this
|
|
180
|
+
* level would have landed 8px offset from these rows). Flex stays:
|
|
181
|
+
* display is layout, not inset. */
|
|
182
|
+
padding-block: var(--space-4, 4px);
|
|
177
183
|
}
|
|
178
184
|
|
|
179
185
|
// The group is a role="group" WRAPPER carrying the accessible name in
|
|
@@ -211,21 +217,29 @@
|
|
|
211
217
|
}
|
|
212
218
|
|
|
213
219
|
.hk-tpl-row {
|
|
214
|
-
|
|
220
|
+
/* Unified menu-item spec (_menu-item.scss): metrics, reserved border,
|
|
221
|
+
* guarded hover, focus ring, sheet tokens via the shared registration
|
|
222
|
+
* list (this row renders in TWO sheet contexts — the vocabulary
|
|
223
|
+
* HkMenu sheet and the chip editor's sheetOnMobile popover). The
|
|
224
|
+
* stacked two-line shape restates the flex axis after the include. */
|
|
225
|
+
@include mi.item;
|
|
215
226
|
flex-direction: column;
|
|
216
227
|
align-items: flex-start;
|
|
217
228
|
gap: 1px;
|
|
218
|
-
padding: var(--space-4, 4px) var(--space-8, 8px);
|
|
219
|
-
border: none;
|
|
220
|
-
border-radius: var(--radius-sm, 4px);
|
|
221
|
-
background: transparent;
|
|
222
229
|
color: var(--hi-color-text-primary);
|
|
223
|
-
text-align: start;
|
|
224
|
-
cursor: pointer;
|
|
225
230
|
|
|
226
|
-
|
|
231
|
+
/* `data-active` is this family's aria-activedescendant keyboard cursor
|
|
232
|
+
* (the grammar's HIGHLIGHT role, not its selected face — mi.item's
|
|
233
|
+
* hover guard already excludes data-active rows). mi.item has no
|
|
234
|
+
* cursor face and its SELECTED face would bleed through a partial
|
|
235
|
+
* restatement, so this face restates the full set at the grammar's
|
|
236
|
+
* hover values — INCLUDING the weight back to the row's resting 500
|
|
237
|
+
* (the selected face's 600 otherwise lands on the cursor row). */
|
|
227
238
|
&[data-active] {
|
|
228
|
-
background:
|
|
239
|
+
background: var(--c-primary-subtle, rgb(var(--color-primary) / 8%));
|
|
240
|
+
border-color: var(--c-primary-medium, rgb(var(--color-primary) / 25%));
|
|
241
|
+
color: rgb(var(--color-text));
|
|
242
|
+
font-weight: var(--hk-menu-item-weight);
|
|
229
243
|
}
|
|
230
244
|
}
|
|
231
245
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/* HkTimeZoneGlobe — the manual zone picker's globe stage, tooltip and
|
|
2
|
+
* no-WebGL fallback list. AI-generated: developed with the ZCode agent
|
|
3
|
+
* (model GLM-5.3); human-directed. */
|
|
4
|
+
|
|
5
|
+
.hk-timezone-globe {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: var(--space-6, 0.375rem);
|
|
9
|
+
min-width: 0;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* The 3D stage: a fixed-height viewport (the board's canvas and its
|
|
13
|
+
* bottom-right minimap both fill this box). */
|
|
14
|
+
.hk-timezone-globe__stage {
|
|
15
|
+
position: relative;
|
|
16
|
+
height: 15rem;
|
|
17
|
+
min-height: 12rem;
|
|
18
|
+
border-radius: var(--radius-sm, 0.375rem);
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
/* A whisper of surface so the transparent-background board reads as
|
|
21
|
+
* a framed instrument, not a hole in the modal. */
|
|
22
|
+
background: rgb(var(--color-muted) / 0.06);
|
|
23
|
+
touch-action: none;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.hk-timezone-globe__tooltip {
|
|
27
|
+
position: absolute;
|
|
28
|
+
z-index: 2;
|
|
29
|
+
pointer-events: none;
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
gap: var(--space-4, 0.25rem);
|
|
33
|
+
max-width: 15rem;
|
|
34
|
+
padding: var(--space-8, 0.5rem) var(--space-10, 0.625rem);
|
|
35
|
+
border: 1px solid rgb(var(--color-muted) / 0.35);
|
|
36
|
+
border-radius: var(--radius-sm, 0.375rem);
|
|
37
|
+
background: rgb(var(--color-background, 22 24 30) / 0.92);
|
|
38
|
+
color: inherit;
|
|
39
|
+
font-size: var(--text-xs, 0.75rem);
|
|
40
|
+
line-height: 1.45;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.hk-timezone-globe__tooltip-offset {
|
|
44
|
+
color: rgb(var(--color-text-secondary, var(--color-muted)));
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.hk-timezone-globe__tooltip-zone {
|
|
49
|
+
font-family: var(--font-mono, ui-monospace, monospace);
|
|
50
|
+
font-size: var(--text-xs, 0.75rem);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.hk-timezone-globe__tooltip-cities {
|
|
54
|
+
color: rgb(var(--color-text-secondary, var(--color-muted)));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.hk-timezone-globe__hint {
|
|
58
|
+
margin: 0;
|
|
59
|
+
color: rgb(var(--color-muted));
|
|
60
|
+
font-size: var(--text-xs, 0.75rem);
|
|
61
|
+
line-height: 1.5;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* ── No-WebGL fallback: the same 24 sectors as a flat list ─────────────── */
|
|
65
|
+
.hk-timezone-globe__list {
|
|
66
|
+
display: flex;
|
|
67
|
+
flex-direction: column;
|
|
68
|
+
max-height: 17rem;
|
|
69
|
+
overflow-y: auto;
|
|
70
|
+
border: 1px solid rgb(var(--color-muted) / 0.25);
|
|
71
|
+
border-radius: var(--radius-sm, 0.375rem);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.hk-timezone-globe__option {
|
|
75
|
+
display: grid;
|
|
76
|
+
grid-template-columns: auto 1fr;
|
|
77
|
+
grid-template-areas:
|
|
78
|
+
"offset name"
|
|
79
|
+
"zone zone"
|
|
80
|
+
"cities cities";
|
|
81
|
+
gap: 0 var(--space-6, 0.375rem);
|
|
82
|
+
padding: var(--space-6, 0.375rem) var(--space-8, 0.5rem);
|
|
83
|
+
border: 0;
|
|
84
|
+
border-bottom: 1px solid rgb(var(--color-muted) / 0.12);
|
|
85
|
+
background: transparent;
|
|
86
|
+
color: inherit;
|
|
87
|
+
font: inherit;
|
|
88
|
+
text-align: left;
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.hk-timezone-globe__option:last-child {
|
|
93
|
+
border-bottom: 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.hk-timezone-globe__option:hover {
|
|
97
|
+
background: rgb(var(--color-muted) / 0.08);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.hk-timezone-globe__option--selected {
|
|
101
|
+
background: rgb(var(--color-primary) / 0.12);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.hk-timezone-globe__option-offset {
|
|
105
|
+
grid-area: offset;
|
|
106
|
+
font-weight: 600;
|
|
107
|
+
font-variant-numeric: tabular-nums;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.hk-timezone-globe__option-name {
|
|
111
|
+
grid-area: name;
|
|
112
|
+
color: rgb(var(--color-text-secondary, var(--color-muted)));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.hk-timezone-globe__option-zone {
|
|
116
|
+
grid-area: zone;
|
|
117
|
+
font-family: var(--font-mono, ui-monospace, monospace);
|
|
118
|
+
font-size: var(--text-xs, 0.75rem);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.hk-timezone-globe__option-cities {
|
|
122
|
+
grid-area: cities;
|
|
123
|
+
color: rgb(var(--color-muted));
|
|
124
|
+
font-size: var(--text-xs, 0.75rem);
|
|
125
|
+
}
|