@cplieger/reactive 2.1.0 → 2.1.1

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/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/reactive",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "license": "Apache-2.0",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/reactive",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -17,16 +17,16 @@
17
17
  "@stryker-mutator/typescript-checker": "10.0.0",
18
18
  "@stryker-mutator/vitest-runner": "10.0.0",
19
19
  "@typescript/native": "npm:typescript@7.0.2",
20
- "@vitest/browser": "4.1.11",
21
- "@vitest/browser-playwright": "4.1.11",
22
- "@vitest/coverage-v8": "4.1.11",
23
- "eslint": "10.9.1",
24
- "fast-check": "4.9.0",
25
- "playwright": "1.62.1",
20
+ "@vitest/browser": "5.0.0",
21
+ "@vitest/browser-playwright": "5.0.0",
22
+ "@vitest/coverage-v8": "5.0.0",
23
+ "eslint": "10.10.0",
24
+ "fast-check": "4.10.0",
25
+ "playwright": "1.63.0",
26
26
  "prettier": "3.9.6",
27
27
  "typescript": "npm:@typescript/typescript6@6.0.2",
28
- "typescript-eslint": "8.69.0",
29
- "vitest": "4.1.11"
28
+ "typescript-eslint": "8.70.0",
29
+ "vitest": "5.0.0"
30
30
  },
31
31
  "overrides": {
32
32
  "qs": "^6.15.2"
package/src/reconcile.ts CHANGED
@@ -30,10 +30,31 @@ export function reconcile<T>(
30
30
  }
31
31
  }
32
32
 
33
- let target: Node | null = null;
34
- for (let i = items.length - 1; i >= 0; i--) {
35
- const item = items[i] as T;
33
+ // DEPARTING ELEMENTS GO FIRST, before anything is placed. The walk below runs
34
+ // backwards and skips an element already sitting before the last one it placed; a
35
+ // departing element still in the tree makes every PREDECESSOR's `nextSibling` point at
36
+ // a node about to vanish, so the guard sees a mismatch and re-seats a row nothing
37
+ // changed — which restarts its animations and drops `:hover` and focus. So `onRemove`
38
+ // fires before the survivors are placed, holding its own intact element; only the
39
+ // siblings' positions are not yet final. `key` is still called exactly once per item,
40
+ // and the pairs carry it into the walk so neither loop indexes an array.
41
+ const pairs: [T, string][] = [];
42
+ const wanted = new Set<string>();
43
+ for (const item of items) {
36
44
  const k = spec.key(item);
45
+ pairs.push([item, k]);
46
+ wanted.add(k);
47
+ }
48
+ for (const [k, el] of existing) {
49
+ if (!wanted.has(k)) {
50
+ spec.onRemove?.(el, k);
51
+ el.remove();
52
+ existing.delete(k);
53
+ }
54
+ }
55
+
56
+ let target: Node | null = null;
57
+ for (const [item, k] of pairs.reverse()) {
37
58
  let el = existing.get(k);
38
59
  if (el === undefined) {
39
60
  el = spec.mount(item);
@@ -52,9 +73,4 @@ export function reconcile<T>(
52
73
  }
53
74
  target = el;
54
75
  }
55
-
56
- for (const [k, el] of existing) {
57
- spec.onRemove?.(el, k);
58
- el.remove();
59
- }
60
76
  }