@window-splitter/state 1.1.5--canary.f61d2d9.0 → 1.2.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
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@window-splitter/state",
4
4
  "sideEffects": false,
5
- "version": "1.1.5--canary.f61d2d9.0",
5
+ "version": "1.2.0",
6
6
  "description": "A state machine for a WAI-ARIA compliant window splitter",
7
7
  "homepage": "https://react-window-splitter-six.vercel.app",
8
8
  "repository": {
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "license": "MIT",
31
31
  "devDependencies": {
32
- "@internal/eslint-config": "1.1.5--canary.f61d2d9.0",
32
+ "@internal/eslint-config": "1.2.0",
33
33
  "@testing-library/react": "^16.0.0",
34
34
  "@types/big.js": "^6.2.2",
35
35
  "@vitest/browser": "catalog:",
@@ -77,5 +77,5 @@
77
77
  "window"
78
78
  ],
79
79
  "types": "./dist/commonjs/index.d.ts",
80
- "gitHead": "f61d2d9e76797f8d8a4061be8952e8ad6ca2f7be"
80
+ "gitHead": "0349b0b301c05e2a1a9b281abe7b93cccc9d9b9b"
81
81
  }
package/src/index.ts CHANGED
@@ -2021,7 +2021,7 @@ export function groupMachine(
2021
2021
  },
2022
2022
  };
2023
2023
 
2024
- function transition(to: State) {
2024
+ function transition(to: State): boolean {
2025
2025
  // exit
2026
2026
  switch (state.current) {
2027
2027
  case "dragging":
@@ -2031,6 +2031,7 @@ export function groupMachine(
2031
2031
  }
2032
2032
 
2033
2033
  // enter
2034
+ let skipOnUpdate = false;
2034
2035
  switch (to) {
2035
2036
  case "idle":
2036
2037
  actions.onAutosave();
@@ -2041,12 +2042,34 @@ export function groupMachine(
2041
2042
  case "togglingCollapse":
2042
2043
  actions.prepare();
2043
2044
  actions.clearLastKnownSize();
2045
+ // Write post-prepare template to DOM instead of routing through React.
2046
+ // `prepare()` converts % to px (same rendered width) — committing
2047
+ // through React fires a Layout pass before the raf loop starts,
2048
+ // producing a visible hitch on frame 0. Callers propagate the
2049
+ // returned flag into send()'s skipOnUpdate so the trailing onUpdate
2050
+ // there is suppressed too.
2051
+ if (getGroupElement) {
2052
+ const el = getGroupElement();
2053
+ if (el) {
2054
+ const template = buildTemplate(context);
2055
+ if (context.orientation === "horizontal") {
2056
+ el.style.gridTemplateColumns = template;
2057
+ } else {
2058
+ el.style.gridTemplateRows = template;
2059
+ }
2060
+ skipOnUpdate = true;
2061
+ }
2062
+ }
2044
2063
  break;
2045
2064
  }
2046
2065
 
2047
2066
  state.current = to;
2048
2067
 
2049
- onUpdate?.(context);
2068
+ if (!skipOnUpdate) {
2069
+ onUpdate?.(context);
2070
+ }
2071
+
2072
+ return skipOnUpdate;
2050
2073
  }
2051
2074
 
2052
2075
  function send(event: GroupMachineEvent) {
@@ -2309,7 +2332,7 @@ export function groupMachine(
2309
2332
  const panel = getPanelWithId(context, event.panelId);
2310
2333
 
2311
2334
  if (!panel.collapsed) {
2312
- transition("togglingCollapse");
2335
+ if (transition("togglingCollapse")) skipOnUpdate = true;
2313
2336
  abortController.abort();
2314
2337
  animationActor(context, event, send, abortController).then(
2315
2338
  (output) => {
@@ -2332,7 +2355,7 @@ export function groupMachine(
2332
2355
  const panel = getPanelWithId(context, event.panelId);
2333
2356
 
2334
2357
  if (panel.collapsed) {
2335
- transition("togglingCollapse");
2358
+ if (transition("togglingCollapse")) skipOnUpdate = true;
2336
2359
  abortController.abort();
2337
2360
  animationActor(context, event, send, abortController).then(
2338
2361
  (output) => {
@@ -627,6 +627,34 @@ describe("constraints", () => {
627
627
  document.body.removeChild(groupEl);
628
628
  });
629
629
 
630
+ test("collapsePanel does not fire onUpdate when getGroupElement is provided (frame-0 hitch)", async () => {
631
+ const groupEl = document.createElement("div");
632
+ groupEl.style.display = "grid";
633
+ document.body.appendChild(groupEl);
634
+
635
+ let updateCount = 0;
636
+ const actor = setupCollapsibleActor(groupEl, () => {
637
+ updateCount++;
638
+ });
639
+ registerCollapsibleLayout(actor);
640
+
641
+ const updatesBeforeCollapse = updateCount;
642
+
643
+ actor.send({ type: "collapsePanel", panelId: "panel-2" });
644
+
645
+ expect(actor.state.current).toBe("togglingCollapse");
646
+ // Neither transition() nor send()'s trailing onUpdate should fire —
647
+ // otherwise React commits + Layout runs before frame 1 of the animation.
648
+ expect(updateCount).toBe(updatesBeforeCollapse);
649
+
650
+ // DOM template must be written synchronously so the animation actor
651
+ // starts against pixel-form values.
652
+ expect(groupEl.style.gridTemplateColumns).toBeTruthy();
653
+
654
+ await waitForIdle(actor);
655
+ document.body.removeChild(groupEl);
656
+ });
657
+
630
658
  test("onUpdate IS called after animation ends (final sync)", async () => {
631
659
  const groupEl = document.createElement("div");
632
660
  document.body.appendChild(groupEl);