@celestia-island/hikari 0.55.265 → 0.55.266
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 +1 -1
- package/src/components/HkFaceShell.rail.test.tsx +8 -0
- package/src/components/HkFaceShell.tsx +8 -1
- package/src/components/HkSidebar.rail.test.tsx +6 -0
- package/src/components/HkSidebar.scss +12 -8
- package/src/components/HkSidebar.styles.test.ts +4 -3
- package/src/components/HkSidebar.tsx +15 -6
- package/src/components/HkSidebarRailSwap.tsx +4 -3
- package/src/composables/useRailIconFlight.test.ts +7 -2
package/package.json
CHANGED
|
@@ -185,6 +185,10 @@ describe("HkFaceShell rail mode", () => {
|
|
|
185
185
|
|
|
186
186
|
expect(c.aside().style.width).toBe("224px");
|
|
187
187
|
expect(c.aside().getAttribute("data-rail")).toBeNull();
|
|
188
|
+
// The shell declares the rail width token for the rail row square's
|
|
189
|
+
// inline track (same contract as HkSidebar's collapsedWidth); the
|
|
190
|
+
// default rides the 56px fallback.
|
|
191
|
+
expect(c.aside().style.getPropertyValue("--hk-sidebar-rail-w")).toBe("56px");
|
|
188
192
|
expect(c.toggle().getAttribute("aria-expanded")).toBe("true");
|
|
189
193
|
|
|
190
194
|
c.toggle().click();
|
|
@@ -265,6 +269,10 @@ describe("HkFaceShell rail mode", () => {
|
|
|
265
269
|
|
|
266
270
|
it("honors a configured rail width", async () => {
|
|
267
271
|
const c = mountShell({ sidebarCollapsible: true, sidebarCollapsedWidth: "72px" });
|
|
272
|
+
expect(
|
|
273
|
+
c.aside().style.getPropertyValue("--hk-sidebar-rail-w"),
|
|
274
|
+
"a non-default collapsed width retunes the square track via the token",
|
|
275
|
+
).toBe("72px");
|
|
268
276
|
c.toggle().click();
|
|
269
277
|
await vi.advanceTimersByTimeAsync(0);
|
|
270
278
|
expect(c.aside().style.width).toBe("72px");
|
|
@@ -461,7 +461,14 @@ export const HkFaceShell = defineComponent({
|
|
|
461
461
|
// while THIS host's width transition plays — expanding must
|
|
462
462
|
// not replay the sidebar slide.
|
|
463
463
|
data-pinning={rail.pinning.value || undefined}
|
|
464
|
-
style={{
|
|
464
|
+
style={{
|
|
465
|
+
width: hostSidebarWidth.value,
|
|
466
|
+
flexShrink: 0,
|
|
467
|
+
// The rail row square's inline track (HkSidebar.scss)
|
|
468
|
+
// derives from this token, so a non-default
|
|
469
|
+
// sidebarCollapsedWidth keeps producing exact squares.
|
|
470
|
+
"--hk-sidebar-rail-w": props.sidebarCollapsedWidth,
|
|
471
|
+
}}
|
|
465
472
|
onPointerenter={onSidebarHostPointerEnter}
|
|
466
473
|
onPointerleave={rail.onPointerLeave}
|
|
467
474
|
onPointermove={onSidebarHostPointerMove}
|
|
@@ -95,6 +95,12 @@ describe("HkSidebar rail capability", () => {
|
|
|
95
95
|
const c = mountSidebar({ width: "240px", collapsible: true, collapsedWidth: "64px" });
|
|
96
96
|
expect(c.aside().className).toContain("hk-sidebar-rail-host");
|
|
97
97
|
expect(c.aside().style.width).toBe("240px");
|
|
98
|
+
// The rail width travels to the sheet as a token: the rail row
|
|
99
|
+
// square's inline track (HkSidebar.scss) computes from it, so a
|
|
100
|
+
// non-default collapsedWidth keeps exact squares without a second
|
|
101
|
+
// edit. Deleting the declaration regresses non-56 hosts silently
|
|
102
|
+
// (the 56px fallback renders identically at the default).
|
|
103
|
+
expect(c.aside().style.getPropertyValue("--hk-sidebar-rail-w")).toBe("64px");
|
|
98
104
|
|
|
99
105
|
c.toggle().click();
|
|
100
106
|
await vi.advanceTimersByTimeAsync(0);
|
|
@@ -102,13 +102,17 @@
|
|
|
102
102
|
* THE RAIL ROW SQUARE (2026-11-02 user screenshot: the collapsed
|
|
103
103
|
* selected pill read as "not quite square — a bit squat"). The body's
|
|
104
104
|
* inline track narrows so a rail row is EXACTLY its own min-height
|
|
105
|
-
* square: the
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* the vertical rhythm invariant above is untouched. The
|
|
111
|
-
*
|
|
105
|
+
* square: the rail width minus the menu-item grammar's 34px row, half
|
|
106
|
+
* a side each. The row spans its track (width: 100% in the shared
|
|
107
|
+
* spec), so the hover/active pill fills 34×34 instead of the 32×34
|
|
108
|
+
* oblong a 12px track produced. INLINE ONLY: the block paddings — the
|
|
109
|
+
* 20px leading air and the scroll tail — stay with the base rule, so
|
|
110
|
+
* the vertical rhythm invariant above is untouched. The rail width
|
|
111
|
+
* travels as `--hk-sidebar-rail-w`, declared inline by both rail
|
|
112
|
+
* holders (HkSidebar's `collapsedWidth`, HkFaceShell's
|
|
113
|
+
* `sidebarCollapsedWidth`) — a host retuning the width keeps exact
|
|
114
|
+
* squares with no second edit. The 34px minuend stays a literal
|
|
115
|
+
* because --hk-menu-item-min-h is declared ON each row (the
|
|
112
116
|
* custom-property trap), so this ancestor cannot compute from it. */
|
|
113
117
|
.hk-sidebar[data-collapsed],
|
|
114
118
|
.hk-sidebar-rail-generation[data-rail] {
|
|
@@ -122,7 +126,7 @@
|
|
|
122
126
|
}
|
|
123
127
|
|
|
124
128
|
.hk-sidebar-body {
|
|
125
|
-
padding-inline:
|
|
129
|
+
padding-inline: calc((var(--hk-sidebar-rail-w, 56px) - 34px) / 2);
|
|
126
130
|
}
|
|
127
131
|
}
|
|
128
132
|
|
|
@@ -94,7 +94,7 @@ describe("HkSidebar scroll end-cap", () => {
|
|
|
94
94
|
expect(
|
|
95
95
|
bodySquare![1]!.trim(),
|
|
96
96
|
"the body dress is the row square's inline track ALONE — any block spelling here moves rows",
|
|
97
|
-
).toBe("padding-inline:
|
|
97
|
+
).toBe("padding-inline: calc((var(--hk-sidebar-rail-w, 56px) - 34px) / 2);");
|
|
98
98
|
const header = ruleBody(css, ".hk-sidebar-rail-generation[data-rail] .hk-sidebar-header");
|
|
99
99
|
expect(header, "the collapsed header rule exists (grouped density carriers)").not.toBeNull();
|
|
100
100
|
expect(header).toContain("padding: var(--space-16) var(--space-8)");
|
|
@@ -104,7 +104,8 @@ describe("HkSidebar scroll end-cap", () => {
|
|
|
104
104
|
// The stronger, spelling-proof form (whole-dress scan): EVERY
|
|
105
105
|
// padding declaration in ANY dress rule must be EXACTLY one of the
|
|
106
106
|
// two sanctioned values — the expanded two-track shorthand (header/
|
|
107
|
-
// footer bands) or the body's square track — exact,
|
|
107
|
+
// footer bands) or the body's token-derived square track — exact,
|
|
108
|
+
// not a prefix: a
|
|
108
109
|
// three-track spelling (`padding: var(--space-16) var(--space-8)
|
|
109
110
|
// var(--space-12)`) drifts the block END past a block-start-only
|
|
110
111
|
// check, and a longhand (`padding-block-start:`) hides from a
|
|
@@ -146,7 +147,7 @@ describe("HkSidebar scroll end-cap", () => {
|
|
|
146
147
|
expect(
|
|
147
148
|
text,
|
|
148
149
|
`a dress inline track drifted from the row square: ${text}`,
|
|
149
|
-
).toBe("padding-inline:
|
|
150
|
+
).toBe("padding-inline: calc((var(--hk-sidebar-rail-w, 56px) - 34px) / 2)");
|
|
150
151
|
} else {
|
|
151
152
|
expect(
|
|
152
153
|
text,
|
|
@@ -29,11 +29,10 @@ export default defineComponent({
|
|
|
29
29
|
* no peek, `collapsed` stays the plain presentational 56px rail. */
|
|
30
30
|
collapsible: { type: Boolean, default: false },
|
|
31
31
|
/** Layout width of the icon rail (the one-icon column). Mirrors the
|
|
32
|
-
* 56px this component always used for its collapsed mode.
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* pill unless that track is retuned alongside. */
|
|
32
|
+
* 56px this component always used for its collapsed mode. Declared
|
|
33
|
+
* to the sheet as `--hk-sidebar-rail-w` on this aside, so the rail
|
|
34
|
+
* row square (HkSidebar.scss's body inline track) retunes with it —
|
|
35
|
+
* any width yields exact 34px squares. */
|
|
37
36
|
collapsedWidth: { type: String, default: "56px" },
|
|
38
37
|
/** Hover dwell on the rail before the peek drawer opens, ms. */
|
|
39
38
|
peekDelay: { type: Number, default: 400 },
|
|
@@ -268,7 +267,17 @@ export default defineComponent({
|
|
|
268
267
|
// panel and the panel's inner widths snap while THIS host's width
|
|
269
268
|
// transition plays — expanding must not replay the sidebar slide.
|
|
270
269
|
data-pinning={rail.pinning.value || undefined}
|
|
271
|
-
style={
|
|
270
|
+
style={
|
|
271
|
+
props.mobile
|
|
272
|
+
? undefined
|
|
273
|
+
: {
|
|
274
|
+
width: hostWidth.value,
|
|
275
|
+
// The rail row square's inline track (HkSidebar.scss)
|
|
276
|
+
// derives from this token, so a non-default
|
|
277
|
+
// collapsedWidth keeps producing exact squares.
|
|
278
|
+
"--hk-sidebar-rail-w": props.collapsedWidth,
|
|
279
|
+
}
|
|
280
|
+
}
|
|
272
281
|
onPointerenter={railChrome.value ? onHostPointerEnter : undefined}
|
|
273
282
|
onPointerleave={railChrome.value ? rail.onPointerLeave : undefined}
|
|
274
283
|
onPointermove={railChrome.value ? onHostPointerMove : undefined}
|
|
@@ -102,9 +102,10 @@ export default defineComponent({
|
|
|
102
102
|
rail: { type: Boolean, default: false },
|
|
103
103
|
/** Pinned width of the RAIL generation. The host's collapsed-width
|
|
104
104
|
* token (HkSidebar's `collapsedWidth`, the shell's
|
|
105
|
-
* `sidebarCollapsedWidth`, wowsp's 56px).
|
|
106
|
-
*
|
|
107
|
-
*
|
|
105
|
+
* `sidebarCollapsedWidth`, wowsp's 56px). The rail row square's
|
|
106
|
+
* body inline track reads the same width as
|
|
107
|
+
* `--hk-sidebar-rail-w` — declared by the rail holder (HkSidebar /
|
|
108
|
+
* HkFaceShell), so a non-default width keeps exact squares. */
|
|
108
109
|
railWidth: { type: String, default: "56px" },
|
|
109
110
|
/** Pinned width of the EXPANDED generation. The host's full sidebar
|
|
110
111
|
* width token — a CSS length or a var() reference. */
|
|
@@ -32,7 +32,7 @@ import HkSidebarRailSwap from "../components/HkSidebarRailSwap";
|
|
|
32
32
|
*
|
|
33
33
|
* The assertions pin the choreography DECISIONS:
|
|
34
34
|
* 1. A flip with moving keyed icons arms the flight: `data-flipping` on
|
|
35
|
-
* the swap host, a flight layer
|
|
35
|
+
* the swap host, a flight layer ON THE HOST with one clone
|
|
36
36
|
* per PAIRED key, each clone painted at the leaving rect (logical
|
|
37
37
|
* insets relative to the stage) and animated by the signed delta.
|
|
38
38
|
* 2. The pairing contract is visible: matched leaving cells carry the
|
|
@@ -202,6 +202,11 @@ afterEach(() => {
|
|
|
202
202
|
for (const app of mounts.splice(0)) app.unmount();
|
|
203
203
|
containers.splice(0).forEach((c) => c.remove());
|
|
204
204
|
document.body.innerHTML = "";
|
|
205
|
+
// Restores every vi.spyOn (getComputedStyle / matchMedia / rect
|
|
206
|
+
// stubs) even when a test failed before its own inline restore —
|
|
207
|
+
// the probes recorded the leak as harmless, but the class is free
|
|
208
|
+
// to close.
|
|
209
|
+
vi.restoreAllMocks();
|
|
205
210
|
vi.unstubAllGlobals();
|
|
206
211
|
vi.useRealTimers();
|
|
207
212
|
__teardownMediaQueries();
|
|
@@ -219,7 +224,7 @@ describe("enterDelayMs", () => {
|
|
|
219
224
|
});
|
|
220
225
|
|
|
221
226
|
describe("useRailIconFlight", () => {
|
|
222
|
-
it("arms a flight on collapse: matched cells marked, layer on the
|
|
227
|
+
it("arms a flight on collapse: matched cells marked, layer on the swap host, clones at the leaving rects", async () => {
|
|
223
228
|
const h = mountSwap();
|
|
224
229
|
await h.set(true);
|
|
225
230
|
// Expanded geometry: icon cells at x=32 (the leading padding column).
|