@celestia-island/hikari 0.55.64 → 0.55.65
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
|
@@ -1268,4 +1268,71 @@ describe("useSizeMorph heightMorph (round 18)", () => {
|
|
|
1268
1268
|
expect(h.frame.style.transition).toBe("");
|
|
1269
1269
|
h.stop();
|
|
1270
1270
|
});
|
|
1271
|
+
|
|
1272
|
+
it("does not replay a clip conceal for the settle transient after a shrink morph", async () => {
|
|
1273
|
+
// Round-21 mechanism (rig REPORT-R21-shrink): a stepflow shrink's
|
|
1274
|
+
// heightMorph targets the natural height measured the instant the slide
|
|
1275
|
+
// settled — e.g. 503 — which sits a few px off the frame's settled rest
|
|
1276
|
+
// height (483; the chrome-allowance transient, the long-standing +20px
|
|
1277
|
+
// drift). The observer's settle-debounced remeasure then read that
|
|
1278
|
+
// residual as a fresh shrink and replayed a CLIP CONCEAL ~152ms into
|
|
1279
|
+
// the morph — re-promoting the riders and sweeping a moving edge for
|
|
1280
|
+
// 300ms, which is the shrink-time flash the phone kept reporting. The
|
|
1281
|
+
// remeasure must DEFER for the morph's duration, and the morph's
|
|
1282
|
+
// restore must absorb the drift silently (snap the pin to the settled
|
|
1283
|
+
// height, no clip sweep, no rider re-promotion).
|
|
1284
|
+
const rider = document.createElement("div");
|
|
1285
|
+
const h = mountHarness(786, 366, {
|
|
1286
|
+
collectRide: () => [{ el: rider }],
|
|
1287
|
+
});
|
|
1288
|
+
h.frame.style.setProperty("--hk-sheet-morph", "clip");
|
|
1289
|
+
h.content.appendChild(rider);
|
|
1290
|
+
h.start();
|
|
1291
|
+
expect(h.frame.style.height).toBe("786px");
|
|
1292
|
+
expect(rider.style.willChange).toBe("transform");
|
|
1293
|
+
|
|
1294
|
+
// The shrink morph: natural reads 503 at the settle instant.
|
|
1295
|
+
h.setNatural(503);
|
|
1296
|
+
h.heightMorph(300);
|
|
1297
|
+
expect(h.frame.style.height).toBe("503px");
|
|
1298
|
+
expect(h.frame.style.transition).toContain("height");
|
|
1299
|
+
expect(h.frame.hasAttribute("data-hk-morphing")).toBe(true);
|
|
1300
|
+
expect(rider.style.willChange).toBe("");
|
|
1301
|
+
|
|
1302
|
+
// Mid-morph the transient resolves: the settled rest height is 483.
|
|
1303
|
+
// The observer fires on the content change — it must NOT start a clip
|
|
1304
|
+
// conceal against the running morph.
|
|
1305
|
+
h.setNatural(483);
|
|
1306
|
+
FakeResizeObserver.instances[0]!.callback();
|
|
1307
|
+
await settle();
|
|
1308
|
+
// No clip was staged and the rider was NOT re-promoted: the remeasure
|
|
1309
|
+
// deferred instead of replaying a conceal.
|
|
1310
|
+
expect(h.frame.style.clipPath).toBe("");
|
|
1311
|
+
expect(rider.style.willChange).toBe("");
|
|
1312
|
+
// The morph's own height transition is untouched (still running to 503).
|
|
1313
|
+
expect(h.frame.style.transition).toContain("height");
|
|
1314
|
+
expect(h.frame.hasAttribute("data-hk-morphing")).toBe(true);
|
|
1315
|
+
|
|
1316
|
+
// The morph's transition completes: restore absorbs the drift — the pin
|
|
1317
|
+
// snaps to the settled 483 silently, with no clip sweep and no rider
|
|
1318
|
+
// re-promotion beyond the restore of the ORIGINAL promotion.
|
|
1319
|
+
const ev = new Event("transitionend");
|
|
1320
|
+
Object.defineProperty(ev, "propertyName", { value: "height" });
|
|
1321
|
+
h.frame.dispatchEvent(ev);
|
|
1322
|
+
expect(h.frame.hasAttribute("data-hk-morphing")).toBe(false);
|
|
1323
|
+
expect(h.frame.style.clipPath).toBe("");
|
|
1324
|
+
expect(h.frame.style.height).toBe("483px");
|
|
1325
|
+
// The resident promotions are restored to their armed values (the frame
|
|
1326
|
+
// is at rest), not left demoted and not re-swept.
|
|
1327
|
+
expect(h.frame.style.willChange).toBe("clip-path");
|
|
1328
|
+
expect(rider.style.willChange).toBe("transform");
|
|
1329
|
+
|
|
1330
|
+
// Nothing is left pending: a later observer tick finds the pin already
|
|
1331
|
+
// settled and stays quiet (no delayed conceal).
|
|
1332
|
+
FakeResizeObserver.instances[0]!.callback();
|
|
1333
|
+
await settle();
|
|
1334
|
+
expect(h.frame.style.clipPath).toBe("");
|
|
1335
|
+
expect(h.frame.style.height).toBe("483px");
|
|
1336
|
+
h.stop();
|
|
1337
|
+
});
|
|
1271
1338
|
});
|
|
@@ -625,12 +625,23 @@ export function useSizeMorph(
|
|
|
625
625
|
|
|
626
626
|
function remeasure(interrupt = true): void {
|
|
627
627
|
if (!armed) return;
|
|
628
|
-
if (!interrupt && revealEl !== null) {
|
|
628
|
+
if (!interrupt && (revealEl !== null || morphDemoted)) {
|
|
629
629
|
// A background measurement must never tear down a live sweep: doing so
|
|
630
630
|
// republished the sweep's own teardown as its LANDING, so a consumer
|
|
631
631
|
// parking geometry for that fold released it ~2ms in and the sheet
|
|
632
632
|
// undid and replayed the fold (real-engine finding). The change is
|
|
633
633
|
// measured as soon as the sweep lands instead.
|
|
634
|
+
// The same holds while a heightMorph is in flight (morphDemoted): a
|
|
635
|
+
// stepflow's morph targets the natural height measured the instant the
|
|
636
|
+
// slide settled, which can sit a few px off the frame's settled rest
|
|
637
|
+
// height (the chrome-allowance transient, the long-standing +20px
|
|
638
|
+
// drift). If the observer's settle-debounced remeasure runs mid-morph
|
|
639
|
+
// it reads that residual as a fresh shrink and replays a CLIP CONCEAL
|
|
640
|
+
// — re-promoting the riders and sweeping a moving edge for 300ms,
|
|
641
|
+
// which is exactly the shrink-time flash the phone kept reporting
|
|
642
|
+
// (round-21 rig: the conceal fired ~152ms into the morph, clobbering
|
|
643
|
+
// the running height transition before transitionend). Defer it; the
|
|
644
|
+
// morph's own restore absorbs the drift silently instead.
|
|
634
645
|
pendingMeasure = true;
|
|
635
646
|
return;
|
|
636
647
|
}
|
|
@@ -1011,6 +1022,32 @@ export function useSizeMorph(
|
|
|
1011
1022
|
morphSavedRiderWills = [];
|
|
1012
1023
|
morphSavedFrameWill = "";
|
|
1013
1024
|
morphDemoted = false;
|
|
1025
|
+
// Absorb the settle transient silently. The morph targeted the natural
|
|
1026
|
+
// height measured the instant the slide settled; that figure can sit a
|
|
1027
|
+
// few px off the frame's settled rest height (the chrome-allowance
|
|
1028
|
+
// transient — the long-standing +20px drift). The observer's
|
|
1029
|
+
// settle-debounced remeasure was deferred for the morph's duration
|
|
1030
|
+
// (see the morphDemoted gate in remeasure); if it ran now against the
|
|
1031
|
+
// stale pin it would read the residual as a fresh shrink and replay a
|
|
1032
|
+
// CLIP CONCEAL (re-promoting riders, sweeping a moving edge) — the
|
|
1033
|
+
// shrink-time flash. Re-measure at rest and snap the pin to the
|
|
1034
|
+
// settled height in one transition-disabled task instead: the few-px
|
|
1035
|
+
// correction lands in the same frame the animation ends, so it is
|
|
1036
|
+
// imperceptible, and the deferred remeasure finds nothing to sweep.
|
|
1037
|
+
if (pendingMeasure) {
|
|
1038
|
+
pendingMeasure = false;
|
|
1039
|
+
const inlineT = f.style.transition;
|
|
1040
|
+
f.style.transition = "none";
|
|
1041
|
+
f.style.height = "";
|
|
1042
|
+
const settled = Math.round(f.offsetHeight);
|
|
1043
|
+
if (settled > 0) {
|
|
1044
|
+
f.style.height = `${settled}px`;
|
|
1045
|
+
pinned = settled;
|
|
1046
|
+
} else if (pinned > 0) {
|
|
1047
|
+
f.style.height = `${pinned}px`;
|
|
1048
|
+
}
|
|
1049
|
+
f.style.transition = inlineT;
|
|
1050
|
+
}
|
|
1014
1051
|
};
|
|
1015
1052
|
const onEnd = (ev: TransitionEvent): void => {
|
|
1016
1053
|
if (ev.target === f && ev.propertyName === "height") {
|