synstate-dom 0.6.0 → 0.7.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/dist/control/for.d.mts +4 -0
- package/dist/control/for.d.mts.map +1 -1
- package/dist/control/for.mjs +25 -15
- package/dist/control/for.mjs.map +1 -1
- package/dist/control/mount-switch.d.mts +4 -0
- package/dist/control/mount-switch.d.mts.map +1 -1
- package/dist/control/mount-switch.mjs +25 -12
- package/dist/control/mount-switch.mjs.map +1 -1
- package/dist/control/portal.d.mts.map +1 -1
- package/dist/control/portal.mjs +2 -2
- package/dist/control/portal.mjs.map +1 -1
- package/dist/internal/after-attach.d.mts +35 -0
- package/dist/internal/after-attach.d.mts.map +1 -0
- package/dist/internal/after-attach.mjs +122 -0
- package/dist/internal/after-attach.mjs.map +1 -0
- package/dist/internal/index.d.mts +1 -0
- package/dist/internal/index.d.mts.map +1 -1
- package/dist/internal/index.mjs +1 -0
- package/dist/internal/index.mjs.map +1 -1
- package/dist/jsx/jsx.mjs +2 -2
- package/dist/jsx/jsx.mjs.map +1 -1
- package/dist/render.d.mts +2 -1
- package/dist/render.d.mts.map +1 -1
- package/dist/render.mjs +19 -4
- package/dist/render.mjs.map +1 -1
- package/package.json +10 -10
- package/src/control/for.mts +41 -19
- package/src/control/mount-switch.mts +36 -15
- package/src/control/portal.mts +7 -3
- package/src/internal/after-attach.mts +173 -0
- package/src/internal/index.mts +1 -0
- package/src/jsx/jsx.mts +2 -2
- package/src/render.mts +32 -6
package/dist/control/for.d.mts
CHANGED
|
@@ -17,6 +17,10 @@ import { type JSX } from '../jsx/index.mjs';
|
|
|
17
17
|
* nodes; the rows that stay are moved as little as the longest increasing
|
|
18
18
|
* subsequence allows.
|
|
19
19
|
*
|
|
20
|
+
* An update that arrives before the anchor is in a parent (a later sibling
|
|
21
|
+
* wrote `items` while the enclosing element was still mounting its children) is
|
|
22
|
+
* applied, with the latest `items`, once the nodes `For` returned are inserted.
|
|
23
|
+
*
|
|
20
24
|
* `key` is required, as in React; `(_, i) => i` matches rows by position. A key
|
|
21
25
|
* that appears twice is reported with `console.error`, and its later
|
|
22
26
|
* occurrences are rebuilt on every update.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"for.d.mts","sourceRoot":"","sources":["../../src/control/for.mts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,qBAAqB,EAG3B,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"for.d.mts","sourceRoot":"","sources":["../../src/control/for.mts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,qBAAqB,EAG3B,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAOpE,OAAO,EAAsB,KAAK,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAGhE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,GAAG,GAAI,CAAC,6BAIlB,QAAQ,CAAC;IACV,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAC9B,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,QAAQ,EAAE,CACR,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,EAC/B,MAAM,EAAE,qBAAqB,CAAC,MAAM,CAAC,KAClC,GAAG,CAAC,OAAO,CAAC;CAClB,CAAC,KAAG,GAAG,CAAC,OAqNL,CAAC"}
|
package/dist/control/for.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createInitializedSyncChildObservable, createScope, getCurrentScope, map, onScopeDispose, } from 'synstate';
|
|
2
2
|
import { Arr, Optional } from 'ts-data-forge';
|
|
3
3
|
import { toObservable } from '../constant/index.mjs';
|
|
4
|
-
import { bindValue } from '../internal/index.mjs';
|
|
4
|
+
import { applyWhenAttached, bindValue, insertBefore, mountThenInsert, } from '../internal/index.mjs';
|
|
5
5
|
import { mountable, toNodes } from '../jsx/index.mjs';
|
|
6
6
|
import { longestIncreasingSubsequence } from './longest-increasing-subsequence.mjs';
|
|
7
7
|
|
|
@@ -20,6 +20,10 @@ import { longestIncreasingSubsequence } from './longest-increasing-subsequence.m
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
23
27
|
|
|
24
28
|
|
|
25
29
|
|
|
@@ -35,7 +39,10 @@ export const For = ({ items, key, children, }) => mountable(() => {
|
|
|
35
39
|
const callChildren = children;
|
|
36
40
|
const mut_rows = new Map();
|
|
37
41
|
const rowStarts = new WeakSet();
|
|
38
|
-
const mut_initial = {
|
|
42
|
+
const mut_initial = {
|
|
43
|
+
nodes: [],
|
|
44
|
+
built: false,
|
|
45
|
+
};
|
|
39
46
|
|
|
40
47
|
const createRow = (rowKey, entry) => {
|
|
41
48
|
const scope = createScope(parent);
|
|
@@ -86,11 +93,10 @@ export const For = ({ items, key, children, }) => mountable(() => {
|
|
|
86
93
|
range.setEndBefore(anchor);
|
|
87
94
|
range.deleteContents();
|
|
88
95
|
};
|
|
89
|
-
|
|
96
|
+
|
|
97
|
+
const update = (entries) => {
|
|
90
98
|
const previousOrder = Array.from(mut_rows.keys());
|
|
91
|
-
|
|
92
|
-
removeAllAtOnceIfEveryKeyGoes(previousOrder, entries);
|
|
93
|
-
}
|
|
99
|
+
removeAllAtOnceIfEveryKeyGoes(previousOrder, entries);
|
|
94
100
|
for (const [rowKey, row] of mut_rows) {
|
|
95
101
|
if (entries.byKey.has(rowKey)) {
|
|
96
102
|
continue;
|
|
@@ -98,13 +104,6 @@ export const For = ({ items, key, children, }) => mountable(() => {
|
|
|
98
104
|
row.scope.dispose();
|
|
99
105
|
mut_rows.delete(rowKey);
|
|
100
106
|
}
|
|
101
|
-
if (anchor.parentNode === null) {
|
|
102
|
-
mut_initial.nodes = entries.order.flatMap((rowKey) => {
|
|
103
|
-
const entry = entries.byKey.get(rowKey);
|
|
104
|
-
return entry === undefined ? [] : createRow(rowKey, entry);
|
|
105
|
-
});
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
107
|
if (Arr.eq(previousOrder, entries.order)) {
|
|
109
108
|
return;
|
|
110
109
|
}
|
|
@@ -124,9 +123,8 @@ export const For = ({ items, key, children, }) => mountable(() => {
|
|
|
124
123
|
if (entry === undefined) {
|
|
125
124
|
continue;
|
|
126
125
|
}
|
|
127
|
-
const [start
|
|
126
|
+
const [start] = mountThenInsert((e) => createRow(rowKey, e), entry, insertBefore, mut_next);
|
|
128
127
|
if (start !== undefined) {
|
|
129
|
-
mut_next.before(start, ...rest);
|
|
130
128
|
mut_next = start;
|
|
131
129
|
}
|
|
132
130
|
continue;
|
|
@@ -137,6 +135,18 @@ export const For = ({ items, key, children, }) => mountable(() => {
|
|
|
137
135
|
mut_next = existing.start;
|
|
138
136
|
}
|
|
139
137
|
reorderMap(mut_rows, entries.order);
|
|
138
|
+
};
|
|
139
|
+
const updateWhenAttached = applyWhenAttached(anchor, update);
|
|
140
|
+
bindValue(entries$, (entries) => {
|
|
141
|
+
if (!mut_initial.built) {
|
|
142
|
+
mut_initial.built = true;
|
|
143
|
+
mut_initial.nodes = entries.order.flatMap((rowKey) => {
|
|
144
|
+
const entry = entries.byKey.get(rowKey);
|
|
145
|
+
return entry === undefined ? [] : createRow(rowKey, entry);
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
updateWhenAttached(entries);
|
|
140
150
|
});
|
|
141
151
|
return Arr.toPushed(mut_initial.nodes, anchor);
|
|
142
152
|
});
|
package/dist/control/for.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"for.mjs","sourceRoot":"","sources":["../../src/control/for.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,oCAAoC,EACpC,WAAW,EACX,eAAe,EACf,GAAG,EACH,cAAc,GAIf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAiB,MAAM,uBAAuB,CAAC;AACpE,OAAO,
|
|
1
|
+
{"version":3,"file":"for.mjs","sourceRoot":"","sources":["../../src/control/for.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,oCAAoC,EACpC,WAAW,EACX,eAAe,EACf,GAAG,EACH,cAAc,GAIf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAiB,MAAM,uBAAuB,CAAC;AACpE,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,YAAY,EACZ,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,OAAO,EAAY,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,CAAK,EACtB,KAAK,EACL,GAAG,EACH,QAAQ,GAQR,EAAe,EAAE,CACjB,SAAS,CAAC,GAAG,EAAE;IACb,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IAEjC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAE1C,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,IAAI,CACvC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CACpC,CAAC;IAEF,yEAAyE;IACzE,4EAA4E;IAC5E,yBAAyB;IACzB,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAC;IAEvC,MAAM,YAAY,GAKd,QAAQ,CAAC;IAEb,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAgB,CAAC;IAEzC,MAAM,SAAS,GAAG,IAAI,OAAO,EAAQ,CAAC;IAEtC,MAAM,WAAW,GAAoD;QACnE,KAAK,EAAE,EAAE;QACT,KAAK,EAAE,KAAK;KACb,CAAC;IAEF,uEAAuE;IACvE,MAAM,SAAS,GAAG,CAChB,MAAe,EACf,KAAe,EACO,EAAE;QACxB,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QAElC,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QAEzC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAErB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE;YAC3B,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAEpE,OAAO,OAAO,CACZ,CAAC,SAAS;gBACR,CAAC,CAAC,YAAY,CACV,KAAK,EACL,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CACxD;gBACH,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CACtB,CAAC,KAAK,EAAE,CACV,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,MAAM,GAAG,GAAQ,EAAE,KAAK,EAAE,KAAK,EAAW,CAAC;QAE3C,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE;YACb,cAAc,CAAC,GAAG,EAAE;gBAClB,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;oBACjC,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAE1B,OAAO,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC,CAAC;IAEF,6EAA6E;IAC7E,MAAM,QAAQ,GAAG,CAAC,GAAQ,EAAwB,EAAE;QAClD,MAAM,SAAS,GAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAE3C,KACE,IAAI,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,EACpC,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EACpE,QAAQ,GAAG,QAAQ,CAAC,WAAW,EAC/B,CAAC;YACD,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;IAEF;;;;;OAKG;IACH,MAAM,6BAA6B,GAAG,CACpC,aAAiC,EACjC,OAAmB,EACb,EAAE;QACR,IACE,CAAC,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC;YAC9B,aAAa,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EACzD,CAAC;YACD,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QAErC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAElC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAE3B,KAAK,CAAC,cAAc,EAAE,CAAC;IACzB,CAAC,CAAC;IAEF,uDAAuD;IACvD,MAAM,MAAM,GAAG,CAAC,OAAmB,EAAQ,EAAE;QAC3C,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QAElD,6BAA6B,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAEtD,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,QAAQ,EAAE,CAAC;YACrC,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC9B,SAAS;YACX,CAAC;YAED,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YAEpB,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC1B,CAAC;QAED,IAAI,GAAG,CAAC,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzC,OAAO;QACT,CAAC;QAED,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAC9B,aAAa;aACV,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;aACxC,GAAG,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CACjD,CAAC;QAEF,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CACjC,CAAC,MAAM,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAC/C,CAAC;QAEF,MAAM,IAAI,GAAG,4BAA4B,CACvC,SAAS,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,IAAI,CAAC,CAAC,CAC9C,CAAC;QAEF,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,SAAS;aACN,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,IAAI,CAAC,CAAC;aACnC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CACjC,CAAC;QAEF,IAAI,QAAQ,GAAc,MAAM,CAAC;QAEjC,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC;YAChD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAEtC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAExC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACxB,SAAS;gBACX,CAAC;gBAED,MAAM,CAAC,KAAK,CAAC,GAAG,eAAe,CAC7B,CAAC,CAAW,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,EACrC,KAAK,EACL,YAAY,EACZ,QAAQ,CACT,CAAC;gBAEF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACxB,QAAQ,GAAG,KAAK,CAAC;gBACnB,CAAC;gBAED,SAAS;YACX,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrD,QAAQ,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;YACzC,CAAC;YAED,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC;QAC5B,CAAC;QAED,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC,CAAC;IAEF,MAAM,kBAAkB,GAAG,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7D,SAAS,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,EAAE;QAC9B,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;YACvB,WAAW,CAAC,KAAK,GAAG,IAAI,CAAC;YAEzB,WAAW,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;gBACnD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAExC,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAC7D,CAAC,CAAC,CAAC;YAEH,OAAO;QACT,CAAC;QAED,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAgBL,MAAM,SAAS,GAAG,CAChB,IAAkB,EAClB,GAAwC,EAC5B,EAAE;IACd,MAAM,SAAS,GAAG,IAAI,GAAG,EAAqB,CAAC;IAE/C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACrC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAEjC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAExC,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,OAAO,CAAC,KAAK,CACX,iCAAiC,EACjC,OAAO,EACP,sCAAsC,CACvC,CAAC;QAEF,yDAAyD;QACzD,MAAM,YAAY,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;QAE7C,SAAS,CAAC,GAAG,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAE7C,OAAO,YAAY,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AACrC,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,QAAQ,GAAG,CACf,QAAgC,EAChC,MAAe,EACf,YAAe,EACf,KAA6B,EACH,EAAE,CAC5B,oCAAoC,CAClC,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,EAClE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;IACd,IAAI,YAAY,GAAG,YAAY,CAAC;IAEhC,OAAO,CAAC,WAAW,EAAE,EAAE;QACrB,MAAM,EAAE,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QAElC,IAAI,QAAQ,CAAC,WAAW,KAAK,WAAW,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YAChE,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAEzC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QAE1B,IAAI,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,CAAC;YAClC,OAAO;QACT,CAAC;QAED,YAAY,GAAG,IAAI,CAAC;QAEpB,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC7B,CAAC,CAAC;AACJ,CAAC,CACF,CAAC;AAEJ,+EAA+E;AAC/E,MAAM,UAAU,GAAG,CACjB,QAA2B,EAC3B,KAAyB,EACnB,EAAE;IACR,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACjC,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAEjC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAU,CAAC;YAC/B,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,KAAK,EAAE,CAAC;IAEjB,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;QACjC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -13,6 +13,10 @@ import { type Mountable, type MountedNodes } from '../jsx/index.mjs';
|
|
|
13
13
|
*
|
|
14
14
|
* `branch` runs inside the new scope, so whatever it creates belongs to the
|
|
15
15
|
* branch.
|
|
16
|
+
*
|
|
17
|
+
* A value that arrives before the anchor is in a parent (a later sibling wrote
|
|
18
|
+
* it while the enclosing element was still mounting its children) is kept, and
|
|
19
|
+
* the switch follows the latest one once the nodes it returned are inserted.
|
|
16
20
|
*/
|
|
17
21
|
export declare const mountSwitch: <V, K>(value: Bindable<V>, keyOf: (current: V) => K, branch: (key: K, current: V) => Mountable | undefined) => MountedNodes;
|
|
18
22
|
//# sourceMappingURL=mount-switch.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount-switch.d.mts","sourceRoot":"","sources":["../../src/control/mount-switch.mts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"mount-switch.d.mts","sourceRoot":"","sources":["../../src/control/mount-switch.mts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAOtD,OAAO,EAAW,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE9E;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,EAAE,CAAC,SACvB,QAAQ,CAAC,CAAC,CAAC,SACX,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,UAChB,CAAC,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,SAAS,GAAG,SAAS,KACpD,YAyDF,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createScope, getCurrentScope, onScopeDispose, } from 'synstate';
|
|
2
2
|
import { Arr } from 'ts-data-forge';
|
|
3
|
-
import { bindValue } from '../internal/index.mjs';
|
|
3
|
+
import { applyWhenAttached, bindValue, insertBefore, mountThenInsert, } from '../internal/index.mjs';
|
|
4
4
|
import { toNodes } from '../jsx/index.mjs';
|
|
5
5
|
|
|
6
6
|
|
|
@@ -16,18 +16,16 @@ import { toNodes } from '../jsx/index.mjs';
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
19
23
|
export const mountSwitch = (value, keyOf, branch) => {
|
|
20
24
|
const parent = getCurrentScope();
|
|
21
25
|
const anchor = document.createComment('');
|
|
22
26
|
const mut_state = { current: undefined, initialNodes: [] };
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (mut_state.current !== undefined) {
|
|
26
|
-
if (Object.is(mut_state.current.key, key)) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
mut_state.current.scope.dispose();
|
|
30
|
-
}
|
|
27
|
+
|
|
28
|
+
const mountBranch = (key, v) => {
|
|
31
29
|
const scope = createScope(parent);
|
|
32
30
|
const nodes = scope.run(() => {
|
|
33
31
|
const mounted = toNodes(branch(key, v)?.mount() ?? []);
|
|
@@ -39,11 +37,26 @@ export const mountSwitch = (value, keyOf, branch) => {
|
|
|
39
37
|
return mounted;
|
|
40
38
|
});
|
|
41
39
|
mut_state.current = { key, scope };
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
return nodes;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const switchTo = (v) => {
|
|
44
|
+
const key = keyOf(v);
|
|
45
|
+
if (mut_state.current !== undefined) {
|
|
46
|
+
if (Object.is(mut_state.current.key, key)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
mut_state.current.scope.dispose();
|
|
50
|
+
}
|
|
51
|
+
mountThenInsert((k) => mountBranch(k, v), key, insertBefore, anchor);
|
|
52
|
+
};
|
|
53
|
+
const switchWhenAttached = applyWhenAttached(anchor, switchTo);
|
|
54
|
+
bindValue(value, (v) => {
|
|
55
|
+
if (mut_state.current === undefined) {
|
|
56
|
+
mut_state.initialNodes = mountBranch(keyOf(v), v);
|
|
44
57
|
}
|
|
45
58
|
else {
|
|
46
|
-
|
|
59
|
+
switchWhenAttached(v);
|
|
47
60
|
}
|
|
48
61
|
});
|
|
49
62
|
return Arr.toPushed(mut_state.initialNodes, anchor);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount-switch.mjs","sourceRoot":"","sources":["../../src/control/mount-switch.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,eAAe,EACf,cAAc,GAEf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEpC,OAAO,
|
|
1
|
+
{"version":3,"file":"mount-switch.mjs","sourceRoot":"","sources":["../../src/control/mount-switch.mts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,eAAe,EACf,cAAc,GAEf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEpC,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,YAAY,EACZ,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAqC,MAAM,kBAAkB,CAAC;AAE9E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,KAAkB,EAClB,KAAwB,EACxB,MAAqD,EACvC,EAAE;IAChB,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IAEjC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAE1C,MAAM,SAAS,GAGX,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC;IAE7C,6EAA6E;IAC7E,MAAM,WAAW,GAAG,CAAC,GAAM,EAAE,CAAI,EAAwB,EAAE;QACzD,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QAElC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE;YAC3B,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAEvD,cAAc,CAAC,GAAG,EAAE;gBAClB,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;oBAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,OAAO,OAAO,CAAC;QACjB,CAAC,CAAC,CAAC;QAEH,SAAS,CAAC,OAAO,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;QAEnC,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,CAAC,CAAI,EAAQ,EAAE;QAC9B,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAErB,IAAI,SAAS,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACpC,IAAI,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;gBAC1C,OAAO;YACT,CAAC;YAED,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACpC,CAAC;QAED,eAAe,CAAC,CAAC,CAAI,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;IAC1E,CAAC,CAAC;IAEF,MAAM,kBAAkB,GAAG,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAE/D,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE;QACrB,IAAI,SAAS,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACpC,SAAS,CAAC,YAAY,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,kBAAkB,CAAC,CAAC,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portal.d.mts","sourceRoot":"","sources":["../../src/control/portal.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"portal.d.mts","sourceRoot":"","sources":["../../src/control/portal.mts"],"names":[],"mappings":"AAEA,OAAO,EAAyB,KAAK,KAAK,EAAE,KAAK,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE/E;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,0BAGhB,QAAQ,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC;CACjB,CAAC,KAAG,GAAG,CAAC,OAgBL,CAAC"}
|
package/dist/control/portal.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { onScopeDispose } from 'synstate';
|
|
2
|
+
import { appendTo, mountThenInsert } from '../internal/index.mjs';
|
|
2
3
|
import { mountChild, mountable } from '../jsx/index.mjs';
|
|
3
4
|
|
|
4
5
|
|
|
@@ -10,8 +11,7 @@ import { mountChild, mountable } from '../jsx/index.mjs';
|
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
export const Portal = ({ target, children, }) => mountable(() => {
|
|
13
|
-
const nodes = mountChild
|
|
14
|
-
(target ?? document.body).append(...nodes);
|
|
14
|
+
const nodes = mountThenInsert(mountChild, children, appendTo, target ?? document.body);
|
|
15
15
|
onScopeDispose(() => {
|
|
16
16
|
for (const node of nodes) {
|
|
17
17
|
node.remove();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portal.mjs","sourceRoot":"","sources":["../../src/control/portal.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAwB,MAAM,kBAAkB,CAAC;AAE/E;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EACrB,MAAM,EACN,QAAQ,GAIR,EAAe,EAAE,CACjB,SAAS,CAAC,GAAG,EAAE;IACb,MAAM,KAAK,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"portal.mjs","sourceRoot":"","sources":["../../src/control/portal.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAwB,MAAM,kBAAkB,CAAC;AAE/E;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EACrB,MAAM,EACN,QAAQ,GAIR,EAAe,EAAE,CACjB,SAAS,CAAC,GAAG,EAAE;IACb,MAAM,KAAK,GAAG,eAAe,CAC3B,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,MAAM,IAAI,QAAQ,CAAC,IAAI,CACxB,CAAC;IAEF,cAAc,CAAC,GAAG,EAAE;QAClB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,CAAC;AACZ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nodes are mounted before they are inserted: an element mounts all its
|
|
3
|
+
* children and then appends them, `render` mounts the app and then appends it,
|
|
4
|
+
* and `For` and `If` mount a row or a branch and then insert it. A child
|
|
5
|
+
* mounted later that writes to what an earlier `For` or `If` reads updates them
|
|
6
|
+
* while their anchor is in no parent yet (#2935).
|
|
7
|
+
*
|
|
8
|
+
* Every mount-and-insert goes through {@link mountThenInsert}. A `For` or a
|
|
9
|
+
* switch takes {@link applyWhenAttached} when it is mounted, which notes the
|
|
10
|
+
* mount open then; a value that arrives while its anchor is detached is kept,
|
|
11
|
+
* and the latest one is applied once that mount has inserted its nodes.
|
|
12
|
+
*
|
|
13
|
+
* `mount` takes `arg` rather than closing over it, so that the element and
|
|
14
|
+
* `render` paths allocate nothing; a mount that nothing defers to allocates no
|
|
15
|
+
* frame.
|
|
16
|
+
*/
|
|
17
|
+
export declare const mountThenInsert: <A, N, T>(mount: (input: A) => N, arg: A, insert: (into: T, mounted: N) => void, target: T) => N;
|
|
18
|
+
/** Appends `nodes` to `parent`: an `insert` for {@link mountThenInsert}. */
|
|
19
|
+
export declare const appendTo: (parent: Element, nodes: readonly ChildNode[]) => void;
|
|
20
|
+
/** Inserts `nodes` in front of `next`: an `insert` for {@link mountThenInsert}. */
|
|
21
|
+
export declare const insertBefore: (next: ChildNode, nodes: readonly ChildNode[]) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Returns the function a `For` or a switch calls with each value after its
|
|
24
|
+
* first. While `anchor` is attached the value is applied at once, and replaces
|
|
25
|
+
* any value still waiting. While it is detached the value is kept, and the
|
|
26
|
+
* latest one is applied once the mount open now has inserted its nodes, and
|
|
27
|
+
* only if `anchor` is then attached: it is not when the `For` or switch was
|
|
28
|
+
* disposed in between.
|
|
29
|
+
*
|
|
30
|
+
* Mounted outside every {@link mountThenInsert} (a description mounted by hand),
|
|
31
|
+
* there is nothing to wait for: a value that arrives while `anchor` is detached
|
|
32
|
+
* is dropped.
|
|
33
|
+
*/
|
|
34
|
+
export declare const applyWhenAttached: <V>(anchor: Node, apply: (value: V) => void) => ((value: V) => void);
|
|
35
|
+
//# sourceMappingURL=after-attach.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"after-attach.d.mts","sourceRoot":"","sources":["../../src/internal/after-attach.mts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,SAC9B,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,OACjB,CAAC,UACE,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,IAAI,UAC7B,CAAC,KACR,CAuBF,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,QAAQ,WACX,OAAO,SACR,SAAS,SAAS,EAAE,KAC1B,IAEF,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,YAAY,SACjB,SAAS,SACR,SAAS,SAAS,EAAE,KAC1B,IAEF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,UACzB,IAAI,SACL,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,KACxB,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAmDrB,CAAC"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export const mountThenInsert = (mount, arg, insert, target) => {
|
|
18
|
+
mut_state.depth += 1;
|
|
19
|
+
mut_state.lastId += 1;
|
|
20
|
+
const { depth } = mut_state;
|
|
21
|
+
mut_state.ids[depth] = mut_state.lastId;
|
|
22
|
+
|
|
23
|
+
takeFrame(depth);
|
|
24
|
+
const nodes = runMount(mount, arg, depth);
|
|
25
|
+
const frame = takeFrame(depth);
|
|
26
|
+
insert(target, nodes);
|
|
27
|
+
if (frame !== undefined) {
|
|
28
|
+
runAll(frame);
|
|
29
|
+
}
|
|
30
|
+
return nodes;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const appendTo = (parent, nodes) => {
|
|
34
|
+
parent.append(...nodes);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const insertBefore = (next, nodes) => {
|
|
38
|
+
next.before(...nodes);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
export const applyWhenAttached = (anchor, apply) => {
|
|
53
|
+
const { depth } = mut_state;
|
|
54
|
+
const id = mut_state.ids[depth];
|
|
55
|
+
const mut_pending = {
|
|
56
|
+
latest: undefined,
|
|
57
|
+
};
|
|
58
|
+
const flush = () => {
|
|
59
|
+
const { latest } = mut_pending;
|
|
60
|
+
mut_pending.latest = undefined;
|
|
61
|
+
if (latest !== undefined && anchor.parentNode !== null) {
|
|
62
|
+
apply(latest.value);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
return (value) => {
|
|
66
|
+
if (anchor.parentNode !== null) {
|
|
67
|
+
mut_pending.latest = undefined;
|
|
68
|
+
apply(value);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const scheduled = mut_pending.latest !== undefined;
|
|
72
|
+
mut_pending.latest = { value };
|
|
73
|
+
if (scheduled) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (depth === 0 || mut_state.depth < depth || mut_state.ids[depth] !== id) {
|
|
77
|
+
|
|
78
|
+
flush();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const mut_frame = mut_state.frames[depth];
|
|
82
|
+
if (mut_frame === undefined) {
|
|
83
|
+
mut_state.frames[depth] = [flush];
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
mut_frame.push(flush);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
const mut_state = { depth: 0, lastId: 0, ids: [0], frames: [] };
|
|
91
|
+
|
|
92
|
+
const takeFrame = (depth) => {
|
|
93
|
+
const frame = mut_state.frames[depth];
|
|
94
|
+
mut_state.frames[depth] = undefined;
|
|
95
|
+
return frame;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
const runAll = (frame) => {
|
|
103
|
+
for (const [index, apply] of frame.entries()) {
|
|
104
|
+
try {
|
|
105
|
+
apply();
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
runAll(frame.slice(index + 1));
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const runMount = (mount, arg, depth) => {
|
|
115
|
+
try {
|
|
116
|
+
return mount(arg);
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
mut_state.depth = depth - 1;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=after-attach.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"after-attach.mjs","sourceRoot":"","sources":["../../src/internal/after-attach.mts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../src/internal/index.mts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../src/internal/index.mts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC"}
|
package/dist/internal/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../src/internal/index.mts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../src/internal/index.mts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC"}
|
package/dist/jsx/jsx.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { hasKey, isRecord } from 'ts-data-forge';
|
|
2
2
|
import { bindProps } from '../bind/index.mjs';
|
|
3
|
-
import { svgTagNames } from '../internal/index.mjs';
|
|
3
|
+
import { appendTo, mountThenInsert, svgTagNames } from '../internal/index.mjs';
|
|
4
4
|
import { isChild, mountChild } from './child.mjs';
|
|
5
5
|
import { mountable } from './mountable.mjs';
|
|
6
6
|
|
|
@@ -36,7 +36,7 @@ const mountElement = (tag, props) => {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
if (hasKey(props, 'children') && isChild(props.children)) {
|
|
39
|
-
|
|
39
|
+
mountThenInsert(mountChild, props.children, appendTo, element);
|
|
40
40
|
}
|
|
41
41
|
bindProps(element, props);
|
|
42
42
|
return element;
|
package/dist/jsx/jsx.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsx.mjs","sourceRoot":"","sources":["../../src/jsx/jsx.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"jsx.mjs","sourceRoot":"","sources":["../../src/jsx/jsx.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAE,UAAU,EAAc,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAkB,MAAM,iBAAiB,CAAC;AAE5D;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,IAAiD,EACjD,KAAQ,EACR,GAAa,EACF,EAAE;IACb,6EAA6E;IAC7E,sEAAsE;IACtE,MAAM,YAAY,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,GAAG,KAAK,EAAE,GAAG,EAAY,CAAC;IAE9E,OAAO,OAAO,IAAI,KAAK,QAAQ;QAC7B,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;AAClD,CAAC,CAAC;AAEF,iEAAiE;AACjE,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAqC,EAAa,EAAE,CAC3E,SAAS,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;AAEpD,MAAM,aAAa,GAAG,CAAC,KAAc,EAAuC,EAAE,CAC5E,OAAO,KAAK,KAAK,UAAU,CAAC;AAE9B,MAAM,YAAY,GAAG,CAAC,GAAW,EAAE,KAAc,EAAW,EAAE;IAC5D,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;QACpC,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,YAAY,EAAE,GAAG,CAAC;QAC7C,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IAEhC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrD,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACrB,CAAC;IAED,wEAAwE;IACxE,IAAI,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzD,eAAe,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;IAED,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAE1B,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,4BAA4B,CAAC;AAElD,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC"}
|
package/dist/render.d.mts
CHANGED
|
@@ -5,7 +5,8 @@ import { type JSX } from './jsx/index.mjs';
|
|
|
5
5
|
*
|
|
6
6
|
* `app` is called once, inside a new root scope; everything created while it
|
|
7
7
|
* and the description it returns are mounted belongs to that scope, and
|
|
8
|
-
* unmounting disposes it and removes the nodes.
|
|
8
|
+
* unmounting disposes it and removes the nodes. If mounting throws, what was
|
|
9
|
+
* mounted is disposed and removed before the error is rethrown.
|
|
9
10
|
*
|
|
10
11
|
* `render(App, el)` rather than `render(<App />, el)`: taking the component
|
|
11
12
|
* makes it plain that it is evaluated here.
|
package/dist/render.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.mts","sourceRoot":"","sources":["../src/render.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"render.d.mts","sourceRoot":"","sources":["../src/render.mts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,GAAG,EAAW,MAAM,iBAAiB,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM,QACZ,MAAM,GAAG,CAAC,OAAO,aACX,OAAO,KACjB,CAAC,MAAM,IAAI,CAmCb,CAAC"}
|
package/dist/render.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createScope } from 'synstate';
|
|
2
|
+
import { appendTo, mountThenInsert } from './internal/index.mjs';
|
|
2
3
|
import { toNodes } from './jsx/index.mjs';
|
|
3
4
|
|
|
4
5
|
|
|
@@ -11,15 +12,29 @@ import { toNodes } from './jsx/index.mjs';
|
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
|
|
15
|
+
|
|
14
16
|
export const render = (app, container) => {
|
|
15
17
|
const root = createScope(undefined);
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
return () => {
|
|
18
|
+
const mut_inserted = { nodes: [] };
|
|
19
|
+
const unmount = () => {
|
|
19
20
|
root.dispose();
|
|
20
|
-
for (const node of nodes) {
|
|
21
|
+
for (const node of mut_inserted.nodes) {
|
|
21
22
|
node.remove();
|
|
22
23
|
}
|
|
23
24
|
};
|
|
25
|
+
try {
|
|
26
|
+
root.run(() => mountThenInsert(mountApp, app, (into, nodes) => {
|
|
27
|
+
mut_inserted.nodes = nodes;
|
|
28
|
+
appendTo(into, nodes);
|
|
29
|
+
}, container));
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
unmount();
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
return unmount;
|
|
24
38
|
};
|
|
39
|
+
const mountApp = (app) => toNodes(app().mount());
|
|
25
40
|
//# sourceMappingURL=render.mjs.map
|
package/dist/render.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.mjs","sourceRoot":"","sources":["../src/render.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAY,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAEpD
|
|
1
|
+
{"version":3,"file":"render.mjs","sourceRoot":"","sources":["../src/render.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAY,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CACpB,GAAsB,EACtB,SAAkB,EACJ,EAAE;IAChB,MAAM,IAAI,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAEpC,MAAM,YAAY,GAAoC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAEpE,MAAM,OAAO,GAAG,GAAS,EAAE;QACzB,IAAI,CAAC,OAAO,EAAE,CAAC;QAEf,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,KAAK,EAAE,CAAC;YACtC,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CACZ,eAAe,CACb,QAAQ,EACR,GAAG,EACH,CAAC,IAAa,EAAE,KAA2B,EAAE,EAAE;YAC7C,YAAY,CAAC,KAAK,GAAG,KAAK,CAAC;YAE3B,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACxB,CAAC,EACD,SAAS,CACV,CACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,yEAAyE;QACzE,0EAA0E;QAC1E,OAAO,EAAE,CAAC;QAEV,MAAM,KAAK,CAAC;IACd,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,QAAQ,GAAG,CAAC,GAAsB,EAAwB,EAAE,CAChE,OAAO,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "synstate-dom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Compiler-free fine-grained UI library that binds synstate observables directly to the DOM",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -44,21 +44,21 @@
|
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@vitest/browser-playwright": "5.0.5",
|
|
47
|
-
"eslint-config-typed": "7.
|
|
48
|
-
"eslint-plugin-ts-data-forge": "0.10.
|
|
49
|
-
"eslint-plugin-ts-fortress": "1.3.
|
|
50
|
-
"eslint-plugin-ts-type-forge": "0.8.
|
|
47
|
+
"eslint-config-typed": "7.4.0",
|
|
48
|
+
"eslint-plugin-ts-data-forge": "0.10.9",
|
|
49
|
+
"eslint-plugin-ts-fortress": "1.3.13",
|
|
50
|
+
"eslint-plugin-ts-type-forge": "0.8.18",
|
|
51
51
|
"playwright": "1.64.1",
|
|
52
52
|
"prettier": "3.9.10",
|
|
53
53
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
54
54
|
"prettier-plugin-packagejson": "3.0.2",
|
|
55
|
-
"synstate": "
|
|
56
|
-
"ts-data-forge": "14.
|
|
57
|
-
"ts-repo-utils": "10.7.
|
|
55
|
+
"synstate": "7.0.0",
|
|
56
|
+
"ts-data-forge": "14.11.0",
|
|
57
|
+
"ts-repo-utils": "10.7.2"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
|
-
"synstate": ">=
|
|
61
|
-
"ts-data-forge": ">=14.
|
|
60
|
+
"synstate": ">=7.0.0",
|
|
61
|
+
"ts-data-forge": ">=14.11.0"
|
|
62
62
|
},
|
|
63
63
|
"engines": {
|
|
64
64
|
"node": ">=22.23.4",
|
package/src/control/for.mts
CHANGED
|
@@ -11,7 +11,12 @@ import {
|
|
|
11
11
|
import { Arr, Optional } from 'ts-data-forge';
|
|
12
12
|
import type { BivariantHack } from 'ts-type-forge';
|
|
13
13
|
import { toObservable, type Bindable } from '../constant/index.mjs';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
applyWhenAttached,
|
|
16
|
+
bindValue,
|
|
17
|
+
insertBefore,
|
|
18
|
+
mountThenInsert,
|
|
19
|
+
} from '../internal/index.mjs';
|
|
15
20
|
import { mountable, toNodes, type JSX } from '../jsx/index.mjs';
|
|
16
21
|
import { longestIncreasingSubsequence } from './longest-increasing-subsequence.mjs';
|
|
17
22
|
|
|
@@ -31,6 +36,10 @@ import { longestIncreasingSubsequence } from './longest-increasing-subsequence.m
|
|
|
31
36
|
* nodes; the rows that stay are moved as little as the longest increasing
|
|
32
37
|
* subsequence allows.
|
|
33
38
|
*
|
|
39
|
+
* An update that arrives before the anchor is in a parent (a later sibling
|
|
40
|
+
* wrote `items` while the enclosing element was still mounting its children) is
|
|
41
|
+
* applied, with the latest `items`, once the nodes `For` returned are inserted.
|
|
42
|
+
*
|
|
34
43
|
* `key` is required, as in React; `(_, i) => i` matches rows by position. A key
|
|
35
44
|
* that appears twice is reported with `console.error`, and its later
|
|
36
45
|
* occurrences are rebuilt on every update.
|
|
@@ -72,7 +81,10 @@ export const For = <T,>({
|
|
|
72
81
|
|
|
73
82
|
const rowStarts = new WeakSet<Node>();
|
|
74
83
|
|
|
75
|
-
const mut_initial: { nodes: readonly ChildNode[] } = {
|
|
84
|
+
const mut_initial: { nodes: readonly ChildNode[]; built: boolean } = {
|
|
85
|
+
nodes: [],
|
|
86
|
+
built: false,
|
|
87
|
+
};
|
|
76
88
|
|
|
77
89
|
/** Creates a row in a scope of its own and returns its first nodes. */
|
|
78
90
|
const createRow = (
|
|
@@ -161,12 +173,11 @@ export const For = <T,>({
|
|
|
161
173
|
range.deleteContents();
|
|
162
174
|
};
|
|
163
175
|
|
|
164
|
-
|
|
176
|
+
/** Brings the attached rows in line with `entries`. */
|
|
177
|
+
const update = (entries: Entries<T>): void => {
|
|
165
178
|
const previousOrder = Array.from(mut_rows.keys());
|
|
166
179
|
|
|
167
|
-
|
|
168
|
-
removeAllAtOnceIfEveryKeyGoes(previousOrder, entries);
|
|
169
|
-
}
|
|
180
|
+
removeAllAtOnceIfEveryKeyGoes(previousOrder, entries);
|
|
170
181
|
|
|
171
182
|
for (const [rowKey, row] of mut_rows) {
|
|
172
183
|
if (entries.byKey.has(rowKey)) {
|
|
@@ -178,16 +189,6 @@ export const For = <T,>({
|
|
|
178
189
|
mut_rows.delete(rowKey);
|
|
179
190
|
}
|
|
180
191
|
|
|
181
|
-
if (anchor.parentNode === null) {
|
|
182
|
-
mut_initial.nodes = entries.order.flatMap((rowKey) => {
|
|
183
|
-
const entry = entries.byKey.get(rowKey);
|
|
184
|
-
|
|
185
|
-
return entry === undefined ? [] : createRow(rowKey, entry);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
192
|
if (Arr.eq(previousOrder, entries.order)) {
|
|
192
193
|
return;
|
|
193
194
|
}
|
|
@@ -224,11 +225,14 @@ export const For = <T,>({
|
|
|
224
225
|
continue;
|
|
225
226
|
}
|
|
226
227
|
|
|
227
|
-
const [start
|
|
228
|
+
const [start] = mountThenInsert(
|
|
229
|
+
(e: Entry<T>) => createRow(rowKey, e),
|
|
230
|
+
entry,
|
|
231
|
+
insertBefore,
|
|
232
|
+
mut_next,
|
|
233
|
+
);
|
|
228
234
|
|
|
229
235
|
if (start !== undefined) {
|
|
230
|
-
mut_next.before(start, ...rest);
|
|
231
|
-
|
|
232
236
|
mut_next = start;
|
|
233
237
|
}
|
|
234
238
|
|
|
@@ -243,6 +247,24 @@ export const For = <T,>({
|
|
|
243
247
|
}
|
|
244
248
|
|
|
245
249
|
reorderMap(mut_rows, entries.order);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const updateWhenAttached = applyWhenAttached(anchor, update);
|
|
253
|
+
|
|
254
|
+
bindValue(entries$, (entries) => {
|
|
255
|
+
if (!mut_initial.built) {
|
|
256
|
+
mut_initial.built = true;
|
|
257
|
+
|
|
258
|
+
mut_initial.nodes = entries.order.flatMap((rowKey) => {
|
|
259
|
+
const entry = entries.byKey.get(rowKey);
|
|
260
|
+
|
|
261
|
+
return entry === undefined ? [] : createRow(rowKey, entry);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
updateWhenAttached(entries);
|
|
246
268
|
});
|
|
247
269
|
|
|
248
270
|
return Arr.toPushed(mut_initial.nodes, anchor);
|
|
@@ -6,7 +6,12 @@ import {
|
|
|
6
6
|
} from 'synstate';
|
|
7
7
|
import { Arr } from 'ts-data-forge';
|
|
8
8
|
import type { Bindable } from '../constant/index.mjs';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
applyWhenAttached,
|
|
11
|
+
bindValue,
|
|
12
|
+
insertBefore,
|
|
13
|
+
mountThenInsert,
|
|
14
|
+
} from '../internal/index.mjs';
|
|
10
15
|
import { toNodes, type Mountable, type MountedNodes } from '../jsx/index.mjs';
|
|
11
16
|
|
|
12
17
|
/**
|
|
@@ -22,6 +27,10 @@ import { toNodes, type Mountable, type MountedNodes } from '../jsx/index.mjs';
|
|
|
22
27
|
*
|
|
23
28
|
* `branch` runs inside the new scope, so whatever it creates belongs to the
|
|
24
29
|
* branch.
|
|
30
|
+
*
|
|
31
|
+
* A value that arrives before the anchor is in a parent (a later sibling wrote
|
|
32
|
+
* it while the enclosing element was still mounting its children) is kept, and
|
|
33
|
+
* the switch follows the latest one once the nodes it returned are inserted.
|
|
25
34
|
*/
|
|
26
35
|
export const mountSwitch = <V, K>(
|
|
27
36
|
value: Bindable<V>,
|
|
@@ -37,17 +46,8 @@ export const mountSwitch = <V, K>(
|
|
|
37
46
|
initialNodes: readonly ChildNode[];
|
|
38
47
|
} = { current: undefined, initialNodes: [] };
|
|
39
48
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (mut_state.current !== undefined) {
|
|
44
|
-
if (Object.is(mut_state.current.key, key)) {
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
mut_state.current.scope.dispose();
|
|
49
|
-
}
|
|
50
|
-
|
|
49
|
+
/** Mounts the branch for `v` in a scope of its own and returns its nodes. */
|
|
50
|
+
const mountBranch = (key: K, v: V): readonly ChildNode[] => {
|
|
51
51
|
const scope = createScope(parent);
|
|
52
52
|
|
|
53
53
|
const nodes = scope.run(() => {
|
|
@@ -64,10 +64,31 @@ export const mountSwitch = <V, K>(
|
|
|
64
64
|
|
|
65
65
|
mut_state.current = { key, scope };
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
return nodes;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** Switches the attached switch to the branch for `v`. */
|
|
71
|
+
const switchTo = (v: V): void => {
|
|
72
|
+
const key = keyOf(v);
|
|
73
|
+
|
|
74
|
+
if (mut_state.current !== undefined) {
|
|
75
|
+
if (Object.is(mut_state.current.key, key)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
mut_state.current.scope.dispose();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
mountThenInsert((k: K) => mountBranch(k, v), key, insertBefore, anchor);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const switchWhenAttached = applyWhenAttached(anchor, switchTo);
|
|
86
|
+
|
|
87
|
+
bindValue(value, (v) => {
|
|
88
|
+
if (mut_state.current === undefined) {
|
|
89
|
+
mut_state.initialNodes = mountBranch(keyOf(v), v);
|
|
69
90
|
} else {
|
|
70
|
-
|
|
91
|
+
switchWhenAttached(v);
|
|
71
92
|
}
|
|
72
93
|
});
|
|
73
94
|
|
package/src/control/portal.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { onScopeDispose } from 'synstate';
|
|
2
|
+
import { appendTo, mountThenInsert } from '../internal/index.mjs';
|
|
2
3
|
import { mountChild, mountable, type Child, type JSX } from '../jsx/index.mjs';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -18,9 +19,12 @@ export const Portal = ({
|
|
|
18
19
|
children: Child;
|
|
19
20
|
}>): JSX.Element =>
|
|
20
21
|
mountable(() => {
|
|
21
|
-
const nodes =
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const nodes = mountThenInsert(
|
|
23
|
+
mountChild,
|
|
24
|
+
children,
|
|
25
|
+
appendTo,
|
|
26
|
+
target ?? document.body,
|
|
27
|
+
);
|
|
24
28
|
|
|
25
29
|
onScopeDispose(() => {
|
|
26
30
|
for (const node of nodes) {
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nodes are mounted before they are inserted: an element mounts all its
|
|
3
|
+
* children and then appends them, `render` mounts the app and then appends it,
|
|
4
|
+
* and `For` and `If` mount a row or a branch and then insert it. A child
|
|
5
|
+
* mounted later that writes to what an earlier `For` or `If` reads updates them
|
|
6
|
+
* while their anchor is in no parent yet (#2935).
|
|
7
|
+
*
|
|
8
|
+
* Every mount-and-insert goes through {@link mountThenInsert}. A `For` or a
|
|
9
|
+
* switch takes {@link applyWhenAttached} when it is mounted, which notes the
|
|
10
|
+
* mount open then; a value that arrives while its anchor is detached is kept,
|
|
11
|
+
* and the latest one is applied once that mount has inserted its nodes.
|
|
12
|
+
*
|
|
13
|
+
* `mount` takes `arg` rather than closing over it, so that the element and
|
|
14
|
+
* `render` paths allocate nothing; a mount that nothing defers to allocates no
|
|
15
|
+
* frame.
|
|
16
|
+
*/
|
|
17
|
+
export const mountThenInsert = <A, N, T>(
|
|
18
|
+
mount: (input: A) => N,
|
|
19
|
+
arg: A,
|
|
20
|
+
insert: (into: T, mounted: N) => void,
|
|
21
|
+
target: T,
|
|
22
|
+
): N => {
|
|
23
|
+
mut_state.depth += 1;
|
|
24
|
+
|
|
25
|
+
mut_state.lastId += 1;
|
|
26
|
+
|
|
27
|
+
const { depth } = mut_state;
|
|
28
|
+
|
|
29
|
+
mut_state.ids[depth] = mut_state.lastId;
|
|
30
|
+
|
|
31
|
+
// Left behind by a mount at this depth that threw.
|
|
32
|
+
takeFrame(depth);
|
|
33
|
+
|
|
34
|
+
const nodes = runMount(mount, arg, depth);
|
|
35
|
+
|
|
36
|
+
const frame = takeFrame(depth);
|
|
37
|
+
|
|
38
|
+
insert(target, nodes);
|
|
39
|
+
|
|
40
|
+
if (frame !== undefined) {
|
|
41
|
+
runAll(frame);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return nodes;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** Appends `nodes` to `parent`: an `insert` for {@link mountThenInsert}. */
|
|
48
|
+
export const appendTo = (
|
|
49
|
+
parent: Element,
|
|
50
|
+
nodes: readonly ChildNode[],
|
|
51
|
+
): void => {
|
|
52
|
+
parent.append(...nodes);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** Inserts `nodes` in front of `next`: an `insert` for {@link mountThenInsert}. */
|
|
56
|
+
export const insertBefore = (
|
|
57
|
+
next: ChildNode,
|
|
58
|
+
nodes: readonly ChildNode[],
|
|
59
|
+
): void => {
|
|
60
|
+
next.before(...nodes);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Returns the function a `For` or a switch calls with each value after its
|
|
65
|
+
* first. While `anchor` is attached the value is applied at once, and replaces
|
|
66
|
+
* any value still waiting. While it is detached the value is kept, and the
|
|
67
|
+
* latest one is applied once the mount open now has inserted its nodes, and
|
|
68
|
+
* only if `anchor` is then attached: it is not when the `For` or switch was
|
|
69
|
+
* disposed in between.
|
|
70
|
+
*
|
|
71
|
+
* Mounted outside every {@link mountThenInsert} (a description mounted by hand),
|
|
72
|
+
* there is nothing to wait for: a value that arrives while `anchor` is detached
|
|
73
|
+
* is dropped.
|
|
74
|
+
*/
|
|
75
|
+
export const applyWhenAttached = <V,>(
|
|
76
|
+
anchor: Node,
|
|
77
|
+
apply: (value: V) => void,
|
|
78
|
+
): ((value: V) => void) => {
|
|
79
|
+
const { depth } = mut_state;
|
|
80
|
+
|
|
81
|
+
const id = mut_state.ids[depth];
|
|
82
|
+
|
|
83
|
+
const mut_pending: { latest: Readonly<{ value: V }> | undefined } = {
|
|
84
|
+
latest: undefined,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const flush = (): void => {
|
|
88
|
+
const { latest } = mut_pending;
|
|
89
|
+
|
|
90
|
+
mut_pending.latest = undefined;
|
|
91
|
+
|
|
92
|
+
if (latest !== undefined && anchor.parentNode !== null) {
|
|
93
|
+
apply(latest.value);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return (value) => {
|
|
98
|
+
if (anchor.parentNode !== null) {
|
|
99
|
+
mut_pending.latest = undefined;
|
|
100
|
+
|
|
101
|
+
apply(value);
|
|
102
|
+
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const scheduled = mut_pending.latest !== undefined;
|
|
107
|
+
|
|
108
|
+
mut_pending.latest = { value };
|
|
109
|
+
|
|
110
|
+
if (scheduled) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (depth === 0 || mut_state.depth < depth || mut_state.ids[depth] !== id) {
|
|
115
|
+
// That mount has ended: its nodes are inserted.
|
|
116
|
+
flush();
|
|
117
|
+
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const mut_frame = mut_state.frames[depth];
|
|
122
|
+
|
|
123
|
+
if (mut_frame === undefined) {
|
|
124
|
+
mut_state.frames[depth] = [flush];
|
|
125
|
+
} else {
|
|
126
|
+
mut_frame.push(flush);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const mut_state: {
|
|
132
|
+
depth: number;
|
|
133
|
+
lastId: number;
|
|
134
|
+
/** The id of the mount open at each depth. */
|
|
135
|
+
ids: number[];
|
|
136
|
+
/** What was deferred to the mount open at each depth, in order. */
|
|
137
|
+
frames: ((() => void)[] | undefined)[];
|
|
138
|
+
} = { depth: 0, lastId: 0, ids: [0], frames: [] };
|
|
139
|
+
|
|
140
|
+
/** Takes what was deferred to the mount at `depth` out of the state. */
|
|
141
|
+
const takeFrame = (depth: number): readonly (() => void)[] | undefined => {
|
|
142
|
+
const frame = mut_state.frames[depth];
|
|
143
|
+
|
|
144
|
+
mut_state.frames[depth] = undefined;
|
|
145
|
+
|
|
146
|
+
return frame;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Runs what was deferred to a mount. One that throws does not keep the rest
|
|
151
|
+
* from running, or they would keep their value waiting for good; the first
|
|
152
|
+
* error is rethrown once they have run.
|
|
153
|
+
*/
|
|
154
|
+
const runAll = (frame: readonly (() => void)[]): void => {
|
|
155
|
+
for (const [index, apply] of frame.entries()) {
|
|
156
|
+
try {
|
|
157
|
+
apply();
|
|
158
|
+
} catch (error) {
|
|
159
|
+
runAll(frame.slice(index + 1));
|
|
160
|
+
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/** A mount that throws must not leave the depth raised. */
|
|
167
|
+
const runMount = <A, N>(mount: (input: A) => N, arg: A, depth: number): N => {
|
|
168
|
+
try {
|
|
169
|
+
return mount(arg);
|
|
170
|
+
} finally {
|
|
171
|
+
mut_state.depth = depth - 1;
|
|
172
|
+
}
|
|
173
|
+
};
|
package/src/internal/index.mts
CHANGED
package/src/jsx/jsx.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { hasKey, isRecord } from 'ts-data-forge';
|
|
2
2
|
import { bindProps } from '../bind/index.mjs';
|
|
3
|
-
import { svgTagNames } from '../internal/index.mjs';
|
|
3
|
+
import { appendTo, mountThenInsert, svgTagNames } from '../internal/index.mjs';
|
|
4
4
|
import { isChild, mountChild, type Child } from './child.mjs';
|
|
5
5
|
import { mountable, type Mountable } from './mountable.mjs';
|
|
6
6
|
|
|
@@ -50,7 +50,7 @@ const mountElement = (tag: string, props: unknown): Element => {
|
|
|
50
50
|
|
|
51
51
|
// Children first, so that a `<select>`'s `value` finds its `<option>`s.
|
|
52
52
|
if (hasKey(props, 'children') && isChild(props.children)) {
|
|
53
|
-
|
|
53
|
+
mountThenInsert(mountChild, props.children, appendTo, element);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
bindProps(element, props);
|
package/src/render.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createScope } from 'synstate';
|
|
2
|
+
import { appendTo, mountThenInsert } from './internal/index.mjs';
|
|
2
3
|
import { type JSX, toNodes } from './jsx/index.mjs';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -7,7 +8,8 @@ import { type JSX, toNodes } from './jsx/index.mjs';
|
|
|
7
8
|
*
|
|
8
9
|
* `app` is called once, inside a new root scope; everything created while it
|
|
9
10
|
* and the description it returns are mounted belongs to that scope, and
|
|
10
|
-
* unmounting disposes it and removes the nodes.
|
|
11
|
+
* unmounting disposes it and removes the nodes. If mounting throws, what was
|
|
12
|
+
* mounted is disposed and removed before the error is rethrown.
|
|
11
13
|
*
|
|
12
14
|
* `render(App, el)` rather than `render(<App />, el)`: taking the component
|
|
13
15
|
* makes it plain that it is evaluated here.
|
|
@@ -18,15 +20,39 @@ export const render = (
|
|
|
18
20
|
): (() => void) => {
|
|
19
21
|
const root = createScope(undefined);
|
|
20
22
|
|
|
21
|
-
const nodes =
|
|
23
|
+
const mut_inserted: { nodes: readonly ChildNode[] } = { nodes: [] };
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return () => {
|
|
25
|
+
const unmount = (): void => {
|
|
26
26
|
root.dispose();
|
|
27
27
|
|
|
28
|
-
for (const node of nodes) {
|
|
28
|
+
for (const node of mut_inserted.nodes) {
|
|
29
29
|
node.remove();
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
root.run(() =>
|
|
35
|
+
mountThenInsert(
|
|
36
|
+
mountApp,
|
|
37
|
+
app,
|
|
38
|
+
(into: Element, nodes: readonly ChildNode[]) => {
|
|
39
|
+
mut_inserted.nodes = nodes;
|
|
40
|
+
|
|
41
|
+
appendTo(into, nodes);
|
|
42
|
+
},
|
|
43
|
+
container,
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
// An update applied after the app was appended can throw; take back what
|
|
48
|
+
// was mounted rather than leave it in `container` with no way to unmount.
|
|
49
|
+
unmount();
|
|
50
|
+
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return unmount;
|
|
32
55
|
};
|
|
56
|
+
|
|
57
|
+
const mountApp = (app: () => JSX.Element): readonly ChildNode[] =>
|
|
58
|
+
toNodes(app().mount());
|