@microsoft/webui-framework 0.0.18 → 0.0.20
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/README.md +130 -70
- package/dist/decorators.d.ts +1 -0
- package/dist/decorators.js +3 -0
- package/dist/element/diff.d.ts +3 -1
- package/dist/element/diff.js +197 -90
- package/dist/element/markers.js +13 -5
- package/dist/element/types.d.ts +18 -10
- package/dist/element/types.js +3 -0
- package/dist/element.d.ts +0 -4
- package/dist/element.js +11 -69
- package/dist/hydration-mismatch.js +3 -2
- package/dist/index.js +1 -1
- package/dist/lifecycle.d.ts +17 -0
- package/dist/lifecycle.js +66 -3
- package/dist/static-host.js +20 -10
- package/dist/streaming-activation.d.ts +2 -0
- package/dist/streaming-activation.js +13 -0
- package/dist/streaming-bootstrap.d.ts +2 -0
- package/dist/streaming-bootstrap.js +48 -0
- package/dist/streaming-cleanup.d.ts +4 -0
- package/dist/streaming-cleanup.js +66 -0
- package/dist/streaming-coordinator.d.ts +7 -0
- package/dist/streaming-coordinator.js +367 -0
- package/dist/streaming-deferred.d.ts +22 -0
- package/dist/streaming-deferred.js +273 -0
- package/dist/streaming-dom.d.ts +32 -0
- package/dist/streaming-dom.js +148 -0
- package/dist/streaming-entry.d.ts +1 -0
- package/dist/streaming-entry.js +2 -0
- package/dist/streaming-install.d.ts +2 -0
- package/dist/streaming-install.js +25 -0
- package/dist/streaming-mode.d.ts +5 -0
- package/dist/streaming-mode.js +14 -0
- package/dist/streaming-protocol.d.ts +32 -0
- package/dist/streaming-protocol.js +27 -0
- package/dist/streaming.d.ts +5 -0
- package/dist/streaming.js +9 -0
- package/dist/template-element.d.ts +28 -9
- package/dist/template-element.js +448 -238
- package/dist/template-events.js +1 -1
- package/dist/template-roots.d.ts +0 -1
- package/dist/template-roots.js +0 -11
- package/dist/template-types.d.ts +7 -1
- package/dist/template.d.ts +2 -0
- package/dist/template.js +46 -19
- package/package.json +9 -3
package/dist/template-element.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { getTemplate } from './template.js';
|
|
1
|
+
import { deferTemplateDefinition, getTemplate } from './template.js';
|
|
2
2
|
import { hydrationStart, hydrationEnd } from './lifecycle.js';
|
|
3
|
-
import {
|
|
3
|
+
import { isStreamingHydrationMode, PENDING_ROOT_CONNECTED, STREAMED_HOST_ATTR, STREAMING_BOUNDARY_ACTIVATE, } from './streaming-mode.js';
|
|
4
|
+
import { createRepeatKeyState, seedHydratedRepeatKeys, syncRepeat, dotWalk, } from './element/diff.js';
|
|
4
5
|
import { collectItemMarkers, nextElement, findByOrdinal, MARKER_COND_START, MARKER_COND_END, MARKER_REPEAT_START, } from './element/markers.js';
|
|
5
6
|
import { injectModuleStyle, } from './element/styles.js';
|
|
6
|
-
import { ATTR_KIND_BOOLEAN, ATTR_KIND_COMPLEX, ATTR_KIND_TEMPLATE, } from './element/types.js';
|
|
7
|
+
import { ATTR_KIND_BOOLEAN, ATTR_KIND_COMPLEX, ATTR_KIND_TEMPLATE, hasNativeLiveProperty, } from './element/types.js';
|
|
7
8
|
import { templateHasRoot, templateRootForAttribute } from './template-roots.js';
|
|
8
9
|
const DEV = typeof __WEBUI_DEV__ === 'undefined' || __WEBUI_DEV__;
|
|
9
10
|
const templateCache = new WeakMap();
|
|
@@ -35,28 +36,11 @@ function getTplOrdinals(tplNode) {
|
|
|
35
36
|
const EMPTY_ARR = [];
|
|
36
37
|
const EMPTY_SET = Object.freeze(new Set());
|
|
37
38
|
const WEBUI_SET_STATE_KEY = Symbol.for('microsoft.webui.setStateKey');
|
|
39
|
+
const STREAMING_BOUNDARY_ABANDON = Symbol.for('microsoft.webui.boundaryAbandon');
|
|
40
|
+
const ACTIVATION_ACTIVATED = 1;
|
|
41
|
+
const ACTIVATION_STATIC_HOST_OPT_OUT = 2;
|
|
42
|
+
const ACTIVATION_MISSING_TEMPLATE = 3;
|
|
38
43
|
const templateMetaByCtor = new WeakMap();
|
|
39
|
-
let domReadyQueue = null;
|
|
40
|
-
function runWhenDomReady(callback) {
|
|
41
|
-
if (document.readyState !== 'loading') {
|
|
42
|
-
callback();
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
if (domReadyQueue) {
|
|
46
|
-
domReadyQueue.push(callback);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
domReadyQueue = [callback];
|
|
50
|
-
document.addEventListener('DOMContentLoaded', flushDomReadyQueue, { once: true });
|
|
51
|
-
}
|
|
52
|
-
function flushDomReadyQueue() {
|
|
53
|
-
const queue = domReadyQueue;
|
|
54
|
-
domReadyQueue = null;
|
|
55
|
-
if (!queue)
|
|
56
|
-
return;
|
|
57
|
-
for (let i = 0; i < queue.length; i++)
|
|
58
|
-
queue[i]();
|
|
59
|
-
}
|
|
60
44
|
function childNodesArray(parent) {
|
|
61
45
|
const children = parent.childNodes;
|
|
62
46
|
const len = children.length;
|
|
@@ -74,8 +58,7 @@ function getTemplateDom(meta) {
|
|
|
74
58
|
templateDOMCache.set(meta, div);
|
|
75
59
|
return div;
|
|
76
60
|
}
|
|
77
|
-
function installTemplateObservedAttributes(ctor, tagName) {
|
|
78
|
-
const meta = getTemplate(tagName);
|
|
61
|
+
function installTemplateObservedAttributes(ctor, tagName, meta = getTemplate(tagName)) {
|
|
79
62
|
if (!meta)
|
|
80
63
|
return;
|
|
81
64
|
templateMetaByCtor.set(ctor, meta);
|
|
@@ -111,6 +94,10 @@ function installTemplateObservedAttributes(ctor, tagName) {
|
|
|
111
94
|
configurable: true,
|
|
112
95
|
});
|
|
113
96
|
}
|
|
97
|
+
function defineTemplateConstructor(ctor, tagName, meta) {
|
|
98
|
+
installTemplateObservedAttributes(ctor, tagName, meta);
|
|
99
|
+
customElements.define(tagName, ctor);
|
|
100
|
+
}
|
|
114
101
|
function hasAuthoredMember(instance, key) {
|
|
115
102
|
if (Object.prototype.hasOwnProperty.call(instance, key))
|
|
116
103
|
return true;
|
|
@@ -128,6 +115,10 @@ export class TemplateElement extends HTMLElement {
|
|
|
128
115
|
this.$root = null;
|
|
129
116
|
this.$ready = false;
|
|
130
117
|
this.$hydrated = false;
|
|
118
|
+
this.$hydratedCallbackCalled = false;
|
|
119
|
+
this.$deferredSSR = false;
|
|
120
|
+
this.$activatingDeferredSSR = false;
|
|
121
|
+
this.$hasUnknownScopes = false;
|
|
131
122
|
this.$templateState = null;
|
|
132
123
|
this.$dirtyPaths = null;
|
|
133
124
|
this.$pendingFlush = false;
|
|
@@ -135,39 +126,85 @@ export class TemplateElement extends HTMLElement {
|
|
|
135
126
|
this.$resolver = (p, s) => this.$resolveValue(p, s);
|
|
136
127
|
}
|
|
137
128
|
[WEBUI_SET_STATE_KEY](key, value) {
|
|
138
|
-
|
|
129
|
+
const wasDeferred = this.$deferredSSR;
|
|
130
|
+
this.$beforeExternalStateWrite();
|
|
131
|
+
const owned = this.$setStateKey(key, value);
|
|
132
|
+
this.$afterExternalStateWrite(owned);
|
|
133
|
+
if (owned && wasDeferred && !this.$deferredSSR)
|
|
134
|
+
this.$update(key);
|
|
135
|
+
return owned;
|
|
136
|
+
}
|
|
137
|
+
[STREAMING_BOUNDARY_ACTIVATE](state) {
|
|
138
|
+
if (!this.$deferredSSR && this.hasAttribute(STREAMED_HOST_ATTR)) {
|
|
139
|
+
this.$deferredSSR = true;
|
|
140
|
+
this.$ready = true;
|
|
141
|
+
}
|
|
142
|
+
if (!this.$deferredSSR)
|
|
143
|
+
return ACTIVATION_ACTIVATED;
|
|
144
|
+
const meta = this.$templateMeta();
|
|
145
|
+
if (!meta) {
|
|
146
|
+
return ACTIVATION_MISSING_TEMPLATE;
|
|
147
|
+
}
|
|
148
|
+
this.$meta = meta;
|
|
149
|
+
if (!this.$shouldActivateOnBoundaryCommit())
|
|
150
|
+
return ACTIVATION_STATIC_HOST_OPT_OUT;
|
|
151
|
+
this.$activateDeferredSSR(state);
|
|
152
|
+
return ACTIVATION_ACTIVATED;
|
|
153
|
+
}
|
|
154
|
+
[STREAMING_BOUNDARY_ABANDON]() {
|
|
155
|
+
this.$deferredSSR = false;
|
|
156
|
+
this.$activatingDeferredSSR = false;
|
|
157
|
+
this.$ready = false;
|
|
158
|
+
this.$preReadyWrites = null;
|
|
139
159
|
}
|
|
140
160
|
static define(tagName) {
|
|
141
|
-
|
|
142
|
-
|
|
161
|
+
const ctor = this;
|
|
162
|
+
const meta = getTemplate(tagName);
|
|
163
|
+
if (!meta && isStreamingHydrationMode()) {
|
|
164
|
+
deferTemplateDefinition(tagName, ctor, () => {
|
|
165
|
+
defineTemplateConstructor(ctor, tagName, getTemplate(tagName));
|
|
166
|
+
});
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
defineTemplateConstructor(ctor, tagName, meta);
|
|
143
170
|
}
|
|
144
171
|
connectedCallback() {
|
|
145
172
|
const tag = this.tagName.toLowerCase();
|
|
173
|
+
if (this.$deferredSSR) {
|
|
174
|
+
this.$ready = true;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
146
177
|
if (this.$hydrated && this.$root) {
|
|
147
178
|
hydrationStart();
|
|
179
|
+
try {
|
|
180
|
+
this.$ready = true;
|
|
181
|
+
this.$update();
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
hydrationEnd();
|
|
185
|
+
}
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (isStreamingHydrationMode() && this.hasAttribute(STREAMED_HOST_ATTR)) {
|
|
189
|
+
this.$deferredSSR = true;
|
|
148
190
|
this.$ready = true;
|
|
149
|
-
this
|
|
150
|
-
|
|
191
|
+
const resume = this[PENDING_ROOT_CONNECTED];
|
|
192
|
+
if (typeof resume === 'function')
|
|
193
|
+
resume.call(this);
|
|
151
194
|
return;
|
|
152
195
|
}
|
|
153
|
-
const meta =
|
|
196
|
+
const meta = this.$templateMeta();
|
|
154
197
|
if (!meta) {
|
|
155
198
|
console.warn(`[WebUI] Template metadata for <${tag}> not found. ` +
|
|
156
199
|
`Ensure the component is included in the SSR output or registered via __webui.templates.`);
|
|
157
200
|
return;
|
|
158
201
|
}
|
|
159
202
|
this.$meta = meta;
|
|
160
|
-
|
|
161
|
-
runWhenDomReady(() => this.$mount(meta));
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
this.$mount(meta);
|
|
165
|
-
}
|
|
203
|
+
this.$mount(meta, false);
|
|
166
204
|
}
|
|
167
|
-
$mount(meta) {
|
|
205
|
+
$mount(meta, forceSSR, ssrState) {
|
|
168
206
|
if (this.$hydrated)
|
|
169
207
|
return;
|
|
170
|
-
hydrationStart();
|
|
171
208
|
const hasShadow = !!this.shadowRoot;
|
|
172
209
|
const wantShadow = hasShadow || !!meta.sd;
|
|
173
210
|
let root;
|
|
@@ -189,33 +226,48 @@ export class TemplateElement extends HTMLElement {
|
|
|
189
226
|
root = this;
|
|
190
227
|
isSSR = false;
|
|
191
228
|
}
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
this.$
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
clientRoot = this.$createStagingRoot(meta);
|
|
200
|
-
this.$root = this.$wire(clientRoot, meta);
|
|
229
|
+
if (isSSR && !forceSSR && this.$shouldDeferSSRHydration()) {
|
|
230
|
+
this.$meta = meta;
|
|
231
|
+
this.$deferredSSR = true;
|
|
232
|
+
this.$ready = true;
|
|
233
|
+
return;
|
|
201
234
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
this.$
|
|
235
|
+
hydrationStart();
|
|
236
|
+
try {
|
|
237
|
+
if (meta.sa)
|
|
238
|
+
injectModuleStyle(meta.sa, this.shadowRoot);
|
|
239
|
+
if (isSSR) {
|
|
240
|
+
if (this.$shouldApplySSRBootstrapState()) {
|
|
241
|
+
this.$applySSRState(forceSSR ? ssrState : window.__webui?.state);
|
|
242
|
+
}
|
|
243
|
+
this.$root = this.$hydrate(root, meta, getTemplateDom(meta));
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
clientRoot = this.$createStagingRoot(meta);
|
|
247
|
+
this.$root = this.$wire(clientRoot, meta);
|
|
215
248
|
}
|
|
216
|
-
this.$
|
|
249
|
+
this.$meta = meta;
|
|
250
|
+
this.$hydrated = true;
|
|
251
|
+
this.$ready = true;
|
|
252
|
+
this.$syncAuthoredAttributes();
|
|
253
|
+
if (isSSR && DEV)
|
|
254
|
+
this.$checkHydrationMismatch();
|
|
255
|
+
else
|
|
256
|
+
this.$preReadyWrites = null;
|
|
257
|
+
if (!isSSR && clientRoot) {
|
|
258
|
+
this.$updateInstance(this.$root);
|
|
259
|
+
if (this.$root.repeats.length !== 0 || this.$root.conds.length !== 0) {
|
|
260
|
+
this.$root.nodes = childNodesArray(clientRoot);
|
|
261
|
+
this.$replaceInstanceContainer(this.$root, clientRoot, root);
|
|
262
|
+
}
|
|
263
|
+
this.$appendStagedChildren(root, clientRoot);
|
|
264
|
+
this.$root.container = root;
|
|
265
|
+
}
|
|
266
|
+
this.$notifyHydrated();
|
|
267
|
+
}
|
|
268
|
+
finally {
|
|
269
|
+
hydrationEnd();
|
|
217
270
|
}
|
|
218
|
-
hydrationEnd();
|
|
219
271
|
}
|
|
220
272
|
disconnectedCallback() {
|
|
221
273
|
if (this.$root) {
|
|
@@ -226,9 +278,13 @@ export class TemplateElement extends HTMLElement {
|
|
|
226
278
|
}
|
|
227
279
|
}
|
|
228
280
|
$destroy() {
|
|
229
|
-
if (!this.$root)
|
|
281
|
+
if (!this.$root) {
|
|
282
|
+
this.$deferredSSR = false;
|
|
283
|
+
this.$ready = false;
|
|
284
|
+
this.$hasUnknownScopes = false;
|
|
230
285
|
return;
|
|
231
|
-
|
|
286
|
+
}
|
|
287
|
+
this.$disposeInstance(this.$root, false);
|
|
232
288
|
this.$root = null;
|
|
233
289
|
this.$pathIndex = undefined;
|
|
234
290
|
this.$wildcardBindings = undefined;
|
|
@@ -236,67 +292,115 @@ export class TemplateElement extends HTMLElement {
|
|
|
236
292
|
this.$pendingFlush = false;
|
|
237
293
|
this.$preReadyWrites = null;
|
|
238
294
|
this.$ready = false;
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
295
|
+
this.$hasUnknownScopes = false;
|
|
296
|
+
}
|
|
297
|
+
$clearRepeatBinding(repeat) {
|
|
298
|
+
repeat.instances.length = 0;
|
|
299
|
+
repeat.container = null;
|
|
300
|
+
repeat.start = null;
|
|
301
|
+
repeat.end = null;
|
|
302
|
+
const state = repeat.keyState;
|
|
303
|
+
if (state) {
|
|
304
|
+
state.keys.length = 0;
|
|
305
|
+
state.nextKeys.length = 0;
|
|
306
|
+
state.map.clear();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
$clearInstance(instance) {
|
|
310
|
+
instance.scope = undefined;
|
|
311
|
+
instance.parent = undefined;
|
|
312
|
+
instance.container = null;
|
|
255
313
|
instance.nodes.length = 0;
|
|
256
314
|
instance.texts.length = 0;
|
|
257
315
|
instance.attrs.length = 0;
|
|
258
316
|
instance.conds.length = 0;
|
|
259
317
|
instance.repeats.length = 0;
|
|
260
318
|
}
|
|
261
|
-
$teardownInstanceCleanups(instance) {
|
|
262
|
-
const cleanups = instance.cleanups;
|
|
263
|
-
if (!cleanups)
|
|
264
|
-
return;
|
|
265
|
-
for (let i = 0; i < cleanups.length; i++)
|
|
266
|
-
cleanups[i]();
|
|
267
|
-
cleanups.length = 0;
|
|
268
|
-
}
|
|
269
319
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
270
320
|
if (Object.is(oldValue, newValue))
|
|
271
321
|
return;
|
|
322
|
+
const wasDeferred = this.$deferredSSR;
|
|
323
|
+
this.$beforeExternalStateWrite();
|
|
272
324
|
const property = this.$templateRootForAttribute(name);
|
|
325
|
+
let changed = false;
|
|
273
326
|
if (property && this.$usesTemplateState(property)) {
|
|
274
|
-
this.$setTemplateState(property, newValue);
|
|
327
|
+
changed = this.$setTemplateState(property, newValue);
|
|
275
328
|
}
|
|
329
|
+
this.$afterExternalStateWrite(changed);
|
|
330
|
+
if (changed && property && wasDeferred && !this.$deferredSSR)
|
|
331
|
+
this.$update(property);
|
|
276
332
|
}
|
|
277
333
|
setState(state) {
|
|
334
|
+
const wasDeferred = this.$deferredSSR;
|
|
335
|
+
this.$beforeExternalStateWrite();
|
|
278
336
|
const keys = Object.keys(state);
|
|
337
|
+
let owned = false;
|
|
279
338
|
for (let i = 0; i < keys.length; i++) {
|
|
280
339
|
const key = keys[i];
|
|
281
|
-
this.$setStateKey(key, state[key]);
|
|
340
|
+
owned = this.$setStateKey(key, state[key]) || owned;
|
|
341
|
+
}
|
|
342
|
+
this.$afterExternalStateWrite(owned);
|
|
343
|
+
if (owned && wasDeferred && !this.$deferredSSR) {
|
|
344
|
+
for (let i = 0; i < keys.length; i++)
|
|
345
|
+
this.$update(keys[i]);
|
|
282
346
|
}
|
|
283
347
|
this.$flushUpdates();
|
|
284
348
|
}
|
|
285
349
|
$observableNames() {
|
|
286
350
|
return EMPTY_SET;
|
|
287
351
|
}
|
|
352
|
+
$beforeExternalStateWrite() {
|
|
353
|
+
}
|
|
354
|
+
$afterExternalStateWrite(_applied) {
|
|
355
|
+
}
|
|
356
|
+
$shouldDeferSSRHydration() {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
$shouldActivateOnBoundaryCommit() {
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
$activateDeferredSSR(state) {
|
|
363
|
+
if (!this.$deferredSSR)
|
|
364
|
+
return;
|
|
365
|
+
const meta = this.$meta;
|
|
366
|
+
if (!meta)
|
|
367
|
+
return;
|
|
368
|
+
this.$deferredSSR = false;
|
|
369
|
+
this.$ready = false;
|
|
370
|
+
this.$preReadyWrites = null;
|
|
371
|
+
this.$activatingDeferredSSR = true;
|
|
372
|
+
try {
|
|
373
|
+
this.$mount(meta, true, state);
|
|
374
|
+
}
|
|
375
|
+
finally {
|
|
376
|
+
this.$activatingDeferredSSR = false;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
$shouldApplySSRBootstrapState() {
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
288
382
|
$shouldApplySSRState(_key) {
|
|
289
383
|
return true;
|
|
290
384
|
}
|
|
291
385
|
$syncAuthoredAttributes() {
|
|
292
386
|
}
|
|
387
|
+
hydratedCallback() {
|
|
388
|
+
}
|
|
389
|
+
$notifyHydrated() {
|
|
390
|
+
if (this.$hydratedCallbackCalled)
|
|
391
|
+
return;
|
|
392
|
+
this.$hydratedCallbackCalled = true;
|
|
393
|
+
this.hydratedCallback();
|
|
394
|
+
}
|
|
293
395
|
$shouldApplyTemplateStateFromSSR(_key) {
|
|
294
396
|
return true;
|
|
295
397
|
}
|
|
296
398
|
$setTemplateState(key, value) {
|
|
297
|
-
|
|
399
|
+
const changed = this.$writeTemplateState(key, value);
|
|
400
|
+
if (changed) {
|
|
298
401
|
this.$update(key);
|
|
299
402
|
}
|
|
403
|
+
return changed;
|
|
300
404
|
}
|
|
301
405
|
$writeTemplateState(key, value) {
|
|
302
406
|
if (!this.$templateState) {
|
|
@@ -335,8 +439,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
335
439
|
}
|
|
336
440
|
return false;
|
|
337
441
|
}
|
|
338
|
-
$applySSRState() {
|
|
339
|
-
const state = window.__webui?.state;
|
|
442
|
+
$applySSRState(state) {
|
|
340
443
|
if (!state || typeof state !== 'object')
|
|
341
444
|
return;
|
|
342
445
|
const observableNames = this.$observableNames();
|
|
@@ -382,24 +485,34 @@ export class TemplateElement extends HTMLElement {
|
|
|
382
485
|
this.$flush();
|
|
383
486
|
}
|
|
384
487
|
$flush() {
|
|
385
|
-
if (!this.$ready || !this.$root
|
|
488
|
+
if (!this.$ready || !this.$root) {
|
|
386
489
|
this.$dirtyPaths = null;
|
|
387
490
|
this.$pendingFlush = false;
|
|
388
491
|
return;
|
|
389
492
|
}
|
|
493
|
+
if (!this.$pathIndex)
|
|
494
|
+
this.$buildPathIndex();
|
|
495
|
+
if (!this.$pathIndex)
|
|
496
|
+
return;
|
|
390
497
|
while (this.$dirtyPaths && this.$dirtyPaths.size > 0) {
|
|
391
498
|
const dirty = this.$dirtyPaths;
|
|
392
499
|
this.$dirtyPaths = null;
|
|
393
500
|
for (const path of dirty) {
|
|
394
|
-
|
|
501
|
+
if (!this.$pathIndex)
|
|
502
|
+
this.$buildPathIndex();
|
|
503
|
+
const entry = this.$pathIndex?.get(path);
|
|
395
504
|
if (entry) {
|
|
396
505
|
this.$updateBindings(entry.texts, entry.attrs, entry.conds, entry.repeats);
|
|
397
506
|
}
|
|
398
507
|
}
|
|
508
|
+
if (!this.$pathIndex)
|
|
509
|
+
this.$buildPathIndex();
|
|
399
510
|
if (this.$wildcardBindings) {
|
|
400
511
|
const wc = this.$wildcardBindings;
|
|
401
512
|
this.$updateBindings(wc.texts, wc.attrs, wc.conds, wc.repeats);
|
|
402
513
|
}
|
|
514
|
+
if (!this.$pathIndex)
|
|
515
|
+
this.$buildPathIndex();
|
|
403
516
|
}
|
|
404
517
|
this.$pendingFlush = false;
|
|
405
518
|
}
|
|
@@ -501,34 +614,34 @@ export class TemplateElement extends HTMLElement {
|
|
|
501
614
|
}
|
|
502
615
|
root.appendChild(fragment);
|
|
503
616
|
}
|
|
504
|
-
$
|
|
505
|
-
if (
|
|
506
|
-
return;
|
|
507
|
-
if (instance.repeats.length === 0 && instance.conds.length === 0)
|
|
617
|
+
$replaceInstanceContainer(instance, previous, next) {
|
|
618
|
+
if (previous === next)
|
|
508
619
|
return;
|
|
509
620
|
const stack = [instance];
|
|
510
621
|
while (stack.length > 0) {
|
|
511
622
|
const current = stack.pop();
|
|
512
623
|
if (!current)
|
|
513
624
|
continue;
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
if (repeat.container === stagingRoot)
|
|
517
|
-
repeat.container = null;
|
|
518
|
-
for (let j = 0; j < repeat.instances.length; j++) {
|
|
519
|
-
stack.push(repeat.instances[j].instance);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
625
|
+
if (current.container === previous)
|
|
626
|
+
current.container = next;
|
|
522
627
|
for (let i = 0; i < current.conds.length; i++) {
|
|
523
628
|
const child = current.conds[i].instance;
|
|
524
629
|
if (child)
|
|
525
630
|
stack.push(child);
|
|
526
631
|
}
|
|
632
|
+
for (let i = 0; i < current.repeats.length; i++) {
|
|
633
|
+
const repeat = current.repeats[i];
|
|
634
|
+
if (repeat.container === previous)
|
|
635
|
+
repeat.container = next;
|
|
636
|
+
const children = repeat.instances;
|
|
637
|
+
for (let j = 0; j < children.length; j++)
|
|
638
|
+
stack.push(children[j]);
|
|
639
|
+
}
|
|
527
640
|
}
|
|
528
641
|
}
|
|
529
642
|
$wire(root, meta, scope) {
|
|
530
643
|
const instance = {
|
|
531
|
-
scope, nodes: childNodesArray(root),
|
|
644
|
+
scope, container: root, nodes: childNodesArray(root),
|
|
532
645
|
texts: [], attrs: [], conds: [], repeats: [],
|
|
533
646
|
};
|
|
534
647
|
const textRefs = new Array(meta.tx?.length ?? 0);
|
|
@@ -563,12 +676,19 @@ export class TemplateElement extends HTMLElement {
|
|
|
563
676
|
let repRefCount = 0;
|
|
564
677
|
if (meta.r) {
|
|
565
678
|
for (let i = 0; i < meta.r.length; i++) {
|
|
566
|
-
const [collection, itemVar, blockIndex, slotMeta] = meta.r[i];
|
|
679
|
+
const [collection, itemVar, blockIndex, slotMeta, keyPath] = meta.r[i];
|
|
567
680
|
const [parentPath, beforeIndex] = slotMeta;
|
|
568
681
|
const parent = parentPath.length > 0 ? this.$resolve(root, parentPath) : root;
|
|
569
682
|
if (!parent || (parent.nodeType !== 1 && parent.nodeType !== 11))
|
|
570
683
|
continue;
|
|
571
|
-
repRefs[repRefCount] = {
|
|
684
|
+
repRefs[repRefCount] = {
|
|
685
|
+
parent,
|
|
686
|
+
ref: parent.childNodes[beforeIndex] || null,
|
|
687
|
+
collection,
|
|
688
|
+
itemVar,
|
|
689
|
+
blockIndex,
|
|
690
|
+
keyPath,
|
|
691
|
+
};
|
|
572
692
|
repRefCount += 1;
|
|
573
693
|
}
|
|
574
694
|
}
|
|
@@ -594,19 +714,28 @@ export class TemplateElement extends HTMLElement {
|
|
|
594
714
|
const c = condRefs[i];
|
|
595
715
|
const anchor = document.createComment('');
|
|
596
716
|
c.parent.insertBefore(anchor, c.ref);
|
|
597
|
-
instance.conds.push({
|
|
717
|
+
instance.conds.push({
|
|
718
|
+
condition: c.condition,
|
|
719
|
+
blockIndex: c.blockIndex,
|
|
720
|
+
anchor,
|
|
721
|
+
scope,
|
|
722
|
+
owner: instance,
|
|
723
|
+
instance: null,
|
|
724
|
+
});
|
|
598
725
|
}
|
|
599
726
|
for (let i = 0; i < repRefCount; i++) {
|
|
600
727
|
const r = repRefs[i];
|
|
601
728
|
const anchor = document.createComment('');
|
|
602
729
|
r.parent.insertBefore(anchor, r.ref);
|
|
603
|
-
const
|
|
604
|
-
instance.repeats.push({
|
|
730
|
+
const binding = {
|
|
605
731
|
markerId: i, collection: r.collection, itemVar: r.itemVar, blockIndex: r.blockIndex,
|
|
606
732
|
container: r.parent, start: anchor, end: null,
|
|
607
|
-
scope, owner: instance, instances: [],
|
|
608
|
-
|
|
609
|
-
|
|
733
|
+
scope, owner: instance, instances: [],
|
|
734
|
+
};
|
|
735
|
+
if (r.keyPath !== undefined) {
|
|
736
|
+
binding.keyState = createRepeatKeyState(r.keyPath);
|
|
737
|
+
}
|
|
738
|
+
instance.repeats.push(binding);
|
|
610
739
|
}
|
|
611
740
|
for (let i = 0; i < instance.conds.length; i++)
|
|
612
741
|
this.$toggleCond(instance.conds[i]);
|
|
@@ -615,6 +744,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
615
744
|
$hydrate(ssrRoot, meta, tplDom, scope, pathStart = 0) {
|
|
616
745
|
const instance = {
|
|
617
746
|
scope,
|
|
747
|
+
container: (pathStart > 0 ? ssrRoot.parentNode : ssrRoot),
|
|
618
748
|
nodes: pathStart > 0 ? [ssrRoot] : childNodesArray(ssrRoot),
|
|
619
749
|
texts: [], attrs: [], conds: [], repeats: [],
|
|
620
750
|
};
|
|
@@ -657,7 +787,6 @@ export class TemplateElement extends HTMLElement {
|
|
|
657
787
|
const [parentPath] = slotMeta;
|
|
658
788
|
const ssrParent = this.$resolveSSR(ssrRoot, tplDom, parentPath, pathStart) ?? ssrRoot;
|
|
659
789
|
const blockMeta = this.$block(blockIndex);
|
|
660
|
-
const shown = condition[0](this.$resolver, scope);
|
|
661
790
|
let condInstance = null;
|
|
662
791
|
if (ssrParent !== lastCondParent) {
|
|
663
792
|
lastCondMarker = null;
|
|
@@ -676,11 +805,10 @@ export class TemplateElement extends HTMLElement {
|
|
|
676
805
|
}
|
|
677
806
|
if (marker)
|
|
678
807
|
lastCondMarker = marker;
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
if (
|
|
682
|
-
condInstance =
|
|
683
|
-
}
|
|
808
|
+
if (marker && blockMeta) {
|
|
809
|
+
condInstance = this.$hydrateCondContent(condAnchor, blockMeta, scope);
|
|
810
|
+
if (condInstance)
|
|
811
|
+
condInstance.parent = instance;
|
|
684
812
|
}
|
|
685
813
|
if (marker) {
|
|
686
814
|
const lastNode = condInstance ? condInstance.nodes[condInstance.nodes.length - 1] : condAnchor;
|
|
@@ -692,7 +820,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
692
820
|
instance.conds.push({
|
|
693
821
|
condition: condition, blockIndex,
|
|
694
822
|
anchor: condAnchor,
|
|
695
|
-
scope, instance: condInstance,
|
|
823
|
+
scope, owner: instance, instance: condInstance,
|
|
696
824
|
});
|
|
697
825
|
}
|
|
698
826
|
}
|
|
@@ -700,7 +828,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
700
828
|
let lastRepMarker = null;
|
|
701
829
|
let lastRepParent = null;
|
|
702
830
|
for (let i = 0; i < meta.r.length; i++) {
|
|
703
|
-
const [collection, itemVar, blockIndex, slotMeta] = meta.r[i];
|
|
831
|
+
const [collection, itemVar, blockIndex, slotMeta, keyPath] = meta.r[i];
|
|
704
832
|
const [parentPath] = slotMeta;
|
|
705
833
|
const ssrParent = this.$resolveSSR(ssrRoot, tplDom, parentPath, pathStart) ?? ssrRoot;
|
|
706
834
|
if (ssrParent !== lastRepParent) {
|
|
@@ -708,7 +836,6 @@ export class TemplateElement extends HTMLElement {
|
|
|
708
836
|
lastRepParent = ssrParent;
|
|
709
837
|
}
|
|
710
838
|
const blockMeta = this.$block(blockIndex);
|
|
711
|
-
const { keyAttribute, keyPath } = this.$repeatMaps(blockIndex, itemVar);
|
|
712
839
|
const blockTplDom = blockMeta ? getTemplateDom(blockMeta) : null;
|
|
713
840
|
const rootTag = blockMeta && blockTplDom?.childNodes.length === 1 && blockTplDom.children.length === 1
|
|
714
841
|
? this.$rootTag(blockMeta)
|
|
@@ -726,29 +853,37 @@ export class TemplateElement extends HTMLElement {
|
|
|
726
853
|
const insertRef = ssrParent.childNodes[Math.min(beforeIndex ?? staticCount, ssrParent.childNodes.length)] ?? null;
|
|
727
854
|
ssrParent.insertBefore(anchor, insertRef);
|
|
728
855
|
}
|
|
729
|
-
lastRepMarker = anchor;
|
|
730
856
|
const repeatInsts = [];
|
|
857
|
+
const hasCollectionState = this.$hasStateRoot(collection, scope);
|
|
731
858
|
const itemsArr = this.$resolveValue(collection, scope);
|
|
732
859
|
const items = Array.isArray(itemsArr) ? itemsArr : [];
|
|
733
860
|
const { items: itemMarkers, end: endMarker } = marker
|
|
734
861
|
? collectItemMarkers(anchor)
|
|
735
862
|
: { items: [], end: null };
|
|
736
|
-
|
|
737
|
-
|
|
863
|
+
lastRepMarker = endMarker ?? anchor;
|
|
864
|
+
if (blockMeta && blockTplDom && anchor.parentNode && itemMarkers.length > 0) {
|
|
865
|
+
if (!this.$activatingDeferredSSR
|
|
866
|
+
&& hasCollectionState
|
|
867
|
+
&& itemMarkers.length !== items.length) {
|
|
738
868
|
console.warn(`[webui] hydration: repeat marker count (${itemMarkers.length}) ≠ data length (${items.length}) for "${collection}"`);
|
|
739
869
|
}
|
|
740
|
-
|
|
741
|
-
for (let j = 0; j < limit; j++) {
|
|
870
|
+
for (let j = 0; j < itemMarkers.length; j++) {
|
|
742
871
|
const itemValue = items[j];
|
|
743
|
-
const
|
|
872
|
+
const known = hasCollectionState && j < items.length;
|
|
873
|
+
if (!known)
|
|
874
|
+
this.$hasUnknownScopes = true;
|
|
875
|
+
const itemScope = {
|
|
876
|
+
name: itemVar,
|
|
877
|
+
value: itemValue,
|
|
878
|
+
parent: scope,
|
|
879
|
+
known,
|
|
880
|
+
};
|
|
744
881
|
if (rootTag) {
|
|
745
882
|
const itemEl = nextElement(itemMarkers[j]);
|
|
746
883
|
if (itemEl) {
|
|
747
|
-
const key = keyAttribute !== undefined
|
|
748
|
-
? itemEl.getAttribute(keyAttribute)
|
|
749
|
-
: String(j);
|
|
750
884
|
const childInstance = this.$hydrate(itemEl, blockMeta, blockTplDom, itemScope, 1);
|
|
751
|
-
|
|
885
|
+
childInstance.parent = instance;
|
|
886
|
+
repeatInsts.push(childInstance);
|
|
752
887
|
}
|
|
753
888
|
}
|
|
754
889
|
else {
|
|
@@ -762,6 +897,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
762
897
|
cursor = next;
|
|
763
898
|
}
|
|
764
899
|
const inst = this.$hydrate(wrapper, blockMeta, blockTplDom, itemScope);
|
|
900
|
+
inst.parent = instance;
|
|
765
901
|
inst.nodes = childNodesArray(wrapper);
|
|
766
902
|
let afterNode = itemMarkers[j];
|
|
767
903
|
for (let nodeIndex = 0; nodeIndex < inst.nodes.length; nodeIndex++) {
|
|
@@ -769,8 +905,10 @@ export class TemplateElement extends HTMLElement {
|
|
|
769
905
|
itemParent?.insertBefore(node, afterNode.nextSibling);
|
|
770
906
|
afterNode = node;
|
|
771
907
|
}
|
|
772
|
-
|
|
773
|
-
|
|
908
|
+
if (itemParent) {
|
|
909
|
+
this.$replaceInstanceContainer(inst, wrapper, itemParent);
|
|
910
|
+
}
|
|
911
|
+
repeatInsts.push(inst);
|
|
774
912
|
}
|
|
775
913
|
}
|
|
776
914
|
for (let m = 0; m < itemMarkers.length; m++) {
|
|
@@ -779,43 +917,76 @@ export class TemplateElement extends HTMLElement {
|
|
|
779
917
|
}
|
|
780
918
|
if (endMarker)
|
|
781
919
|
staleMarkers.push(endMarker);
|
|
782
|
-
|
|
920
|
+
const binding = {
|
|
783
921
|
markerId: i, collection, itemVar, blockIndex,
|
|
784
922
|
container: (anchor.parentNode ?? ssrRoot),
|
|
785
923
|
start: anchor, end: null,
|
|
786
|
-
scope, owner: instance, instances: repeatInsts,
|
|
787
|
-
|
|
788
|
-
}
|
|
924
|
+
scope, owner: instance, instances: repeatInsts,
|
|
925
|
+
synced: hasCollectionState,
|
|
926
|
+
};
|
|
927
|
+
if (keyPath !== undefined) {
|
|
928
|
+
binding.keyState = createRepeatKeyState(keyPath);
|
|
929
|
+
if (hasCollectionState) {
|
|
930
|
+
seedHydratedRepeatKeys(binding, items);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
instance.repeats.push(binding);
|
|
789
934
|
}
|
|
790
935
|
}
|
|
791
936
|
this.$finalize(instance, ssrRoot, meta, (r, p) => this.$resolveSSR(r, tplDom, p, pathStart), scope);
|
|
792
937
|
for (let i = 0; i < staleMarkers.length; i++) {
|
|
793
938
|
staleMarkers[i].parentNode?.removeChild(staleMarkers[i]);
|
|
794
939
|
}
|
|
940
|
+
this.$compactNodeArray(instance);
|
|
795
941
|
return instance;
|
|
796
942
|
}
|
|
797
|
-
$
|
|
943
|
+
$collectConditionalRange(start) {
|
|
798
944
|
const nodes = [];
|
|
945
|
+
let depth = 0;
|
|
799
946
|
let node = start.nextSibling;
|
|
800
947
|
while (node) {
|
|
801
|
-
if (node.nodeType === 8
|
|
802
|
-
|
|
948
|
+
if (node.nodeType === 8) {
|
|
949
|
+
const data = node.data;
|
|
950
|
+
if (data === MARKER_COND_START) {
|
|
951
|
+
depth++;
|
|
952
|
+
}
|
|
953
|
+
else if (data === MARKER_COND_END) {
|
|
954
|
+
if (depth === 0)
|
|
955
|
+
break;
|
|
956
|
+
depth--;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
803
959
|
nodes.push(node);
|
|
804
960
|
node = node.nextSibling;
|
|
805
961
|
}
|
|
806
962
|
return nodes;
|
|
807
963
|
}
|
|
964
|
+
$hasRootStructuralSlot(meta) {
|
|
965
|
+
if (meta.c) {
|
|
966
|
+
for (let i = 0; i < meta.c.length; i++) {
|
|
967
|
+
if (meta.c[i][2][0].length === 0)
|
|
968
|
+
return true;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
if (meta.r) {
|
|
972
|
+
for (let i = 0; i < meta.r.length; i++) {
|
|
973
|
+
if (meta.r[i][3][0].length === 0)
|
|
974
|
+
return true;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
return false;
|
|
978
|
+
}
|
|
808
979
|
$hydrateCondContent(condAnchor, blockMeta, scope) {
|
|
809
980
|
const rootTag = this.$rootTag(blockMeta);
|
|
810
981
|
const tplDom = getTemplateDom(blockMeta);
|
|
811
|
-
if (rootTag && tplDom.children.length === 1) {
|
|
982
|
+
if (rootTag && tplDom.children.length === 1 && !this.$hasRootStructuralSlot(blockMeta)) {
|
|
812
983
|
const el = nextElement(condAnchor);
|
|
813
984
|
if (el) {
|
|
814
985
|
return this.$hydrate(el, blockMeta, tplDom, scope, 1);
|
|
815
986
|
}
|
|
816
987
|
return null;
|
|
817
988
|
}
|
|
818
|
-
const condNodes = this.$
|
|
989
|
+
const condNodes = this.$collectConditionalRange(condAnchor);
|
|
819
990
|
if (condNodes.length === 0)
|
|
820
991
|
return null;
|
|
821
992
|
const wrapper = document.createElement('div');
|
|
@@ -828,6 +999,9 @@ export class TemplateElement extends HTMLElement {
|
|
|
828
999
|
condAnchor.parentNode?.insertBefore(inst.nodes[cn], afterNode.nextSibling);
|
|
829
1000
|
afterNode = inst.nodes[cn];
|
|
830
1001
|
}
|
|
1002
|
+
const container = condAnchor.parentNode;
|
|
1003
|
+
if (container)
|
|
1004
|
+
this.$replaceInstanceContainer(inst, wrapper, container);
|
|
831
1005
|
return inst;
|
|
832
1006
|
}
|
|
833
1007
|
$findMarker(parent, data, after) {
|
|
@@ -840,16 +1014,6 @@ export class TemplateElement extends HTMLElement {
|
|
|
840
1014
|
}
|
|
841
1015
|
return null;
|
|
842
1016
|
}
|
|
843
|
-
$hasContentAfterMarker(anchor, endData) {
|
|
844
|
-
let sibling = anchor.nextSibling;
|
|
845
|
-
while (sibling) {
|
|
846
|
-
if (sibling.nodeType === 8 && sibling.data === endData) {
|
|
847
|
-
return false;
|
|
848
|
-
}
|
|
849
|
-
return true;
|
|
850
|
-
}
|
|
851
|
-
return false;
|
|
852
|
-
}
|
|
853
1017
|
$findSSRText(ssrParent, tplParent, beforeIndex) {
|
|
854
1018
|
const ordinals = getTplOrdinals(tplParent);
|
|
855
1019
|
let textOrd = 0;
|
|
@@ -926,32 +1090,6 @@ export class TemplateElement extends HTMLElement {
|
|
|
926
1090
|
return { element: el, name, kind, parts: entry[2], scope };
|
|
927
1091
|
return { element: el, name, kind: kind, path: entry[2] || '', scope };
|
|
928
1092
|
}
|
|
929
|
-
$repeatMaps(blockIndex, itemVar) {
|
|
930
|
-
let keyAttribute;
|
|
931
|
-
let keyPath;
|
|
932
|
-
const bm = this.$block(blockIndex);
|
|
933
|
-
if (bm?.a && bm.ag) {
|
|
934
|
-
for (let g = 0; g < bm.ag.length; g++) {
|
|
935
|
-
const [tp, s, c] = bm.ag[g];
|
|
936
|
-
const isRoot = tp.length === 0 || (tp.length === 1 && tp[0] === 0);
|
|
937
|
-
if (isRoot) {
|
|
938
|
-
for (let j = 0; j < c; j++) {
|
|
939
|
-
const entry = bm.a[s + j];
|
|
940
|
-
if (entry) {
|
|
941
|
-
if (entry[1] === 0 || entry[1] === 3) {
|
|
942
|
-
const dp = this.$singleDynamic(entry[1] === 3 ? entry[2] : [[entry[2]]]);
|
|
943
|
-
if (dp && keyPath === undefined && dp.path.startsWith(itemVar + '.')) {
|
|
944
|
-
keyAttribute = entry[0];
|
|
945
|
-
keyPath = dp.path.slice(itemVar.length + 1);
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
return { keyAttribute, keyPath };
|
|
954
|
-
}
|
|
955
1093
|
$buildPathIndex() {
|
|
956
1094
|
if (!this.$root)
|
|
957
1095
|
return;
|
|
@@ -1025,7 +1163,7 @@ export class TemplateElement extends HTMLElement {
|
|
|
1025
1163
|
ensure(keyFor(rep.collection)).repeats.push(rep);
|
|
1026
1164
|
}
|
|
1027
1165
|
for (let i = 0; i < rep.instances.length; i++) {
|
|
1028
|
-
visit(rep.instances[i]
|
|
1166
|
+
visit(rep.instances[i]);
|
|
1029
1167
|
}
|
|
1030
1168
|
}
|
|
1031
1169
|
};
|
|
@@ -1041,16 +1179,37 @@ export class TemplateElement extends HTMLElement {
|
|
|
1041
1179
|
this.$pathIndex = index;
|
|
1042
1180
|
}
|
|
1043
1181
|
$updateBindings(texts, attrs, conds, repeats) {
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
this.$
|
|
1048
|
-
|
|
1049
|
-
|
|
1182
|
+
const gated = this.$hasUnknownScopes;
|
|
1183
|
+
for (let i = 0; i < texts.length; i++) {
|
|
1184
|
+
const binding = texts[i];
|
|
1185
|
+
if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
|
|
1186
|
+
this.$patchText(binding);
|
|
1187
|
+
}
|
|
1188
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
1189
|
+
const binding = attrs[i];
|
|
1190
|
+
if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
|
|
1191
|
+
this.$patchAttr(binding);
|
|
1192
|
+
}
|
|
1193
|
+
for (let i = 0; i < conds.length; i++) {
|
|
1194
|
+
const binding = conds[i];
|
|
1195
|
+
if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
|
|
1196
|
+
this.$toggleCond(binding);
|
|
1197
|
+
}
|
|
1050
1198
|
for (let i = 0; i < repeats.length; i++) {
|
|
1051
|
-
|
|
1199
|
+
const binding = repeats[i];
|
|
1200
|
+
if (!gated || !binding.scope || this.$scopeIsKnown(binding.scope))
|
|
1201
|
+
syncRepeat(this, binding);
|
|
1052
1202
|
}
|
|
1053
1203
|
}
|
|
1204
|
+
$scopeIsKnown(scope) {
|
|
1205
|
+
let frame = scope;
|
|
1206
|
+
while (frame) {
|
|
1207
|
+
if (frame.known === false)
|
|
1208
|
+
return false;
|
|
1209
|
+
frame = frame.parent;
|
|
1210
|
+
}
|
|
1211
|
+
return true;
|
|
1212
|
+
}
|
|
1054
1213
|
$updateInstance(instance) {
|
|
1055
1214
|
this.$updateBindings(instance.texts, instance.attrs, instance.conds, instance.repeats);
|
|
1056
1215
|
}
|
|
@@ -1099,7 +1258,8 @@ export class TemplateElement extends HTMLElement {
|
|
|
1099
1258
|
el.setAttribute(b.name, '');
|
|
1100
1259
|
else
|
|
1101
1260
|
el.removeAttribute(b.name);
|
|
1102
|
-
if (b.name === 'checked' || b.name === 'selected' || b.name === 'disabled')
|
|
1261
|
+
if ((b.name === 'checked' || b.name === 'selected' || b.name === 'disabled')
|
|
1262
|
+
&& hasNativeLiveProperty(el, b.name)) {
|
|
1103
1263
|
el[b.name] = show;
|
|
1104
1264
|
}
|
|
1105
1265
|
break;
|
|
@@ -1113,12 +1273,14 @@ export class TemplateElement extends HTMLElement {
|
|
|
1113
1273
|
default: {
|
|
1114
1274
|
const v = this.$resolveValue(b.path, b.scope);
|
|
1115
1275
|
const s = v == null ? '' : String(v);
|
|
1116
|
-
if (b.name === 'checked' || b.name === 'selected')
|
|
1276
|
+
if ((b.name === 'checked' || b.name === 'selected')
|
|
1277
|
+
&& hasNativeLiveProperty(el, b.name)) {
|
|
1117
1278
|
el[b.name] = !!v && v !== 'false' && v !== '0';
|
|
1118
1279
|
}
|
|
1119
|
-
else if (b.name === 'value') {
|
|
1120
|
-
|
|
1121
|
-
|
|
1280
|
+
else if (b.name === 'value' && hasNativeLiveProperty(el, b.name)) {
|
|
1281
|
+
const target = el;
|
|
1282
|
+
if (target.value !== s)
|
|
1283
|
+
target.value = s;
|
|
1122
1284
|
}
|
|
1123
1285
|
else {
|
|
1124
1286
|
if (el.getAttribute(b.name) !== s)
|
|
@@ -1131,22 +1293,25 @@ export class TemplateElement extends HTMLElement {
|
|
|
1131
1293
|
$toggleCond(c) {
|
|
1132
1294
|
const show = c.condition[0](this.$resolver, c.scope);
|
|
1133
1295
|
if (show) {
|
|
1296
|
+
let created = false;
|
|
1297
|
+
const container = c.anchor.parentNode;
|
|
1134
1298
|
if (!c.instance) {
|
|
1135
|
-
c.instance = this.$createBlockInstance(c.blockIndex, c.scope);
|
|
1136
|
-
|
|
1137
|
-
const frag = document.createDocumentFragment();
|
|
1138
|
-
for (const n of c.instance.nodes)
|
|
1139
|
-
frag.appendChild(n);
|
|
1140
|
-
c.anchor.parentNode?.insertBefore(frag, c.anchor.nextSibling);
|
|
1141
|
-
}
|
|
1299
|
+
c.instance = this.$createBlockInstance(c.blockIndex, c.scope, c.owner, container ?? undefined);
|
|
1300
|
+
created = c.instance !== null;
|
|
1142
1301
|
}
|
|
1143
1302
|
else {
|
|
1144
1303
|
this.$updateInstance(c.instance);
|
|
1145
1304
|
}
|
|
1305
|
+
if (c.instance && container) {
|
|
1306
|
+
this.$insertInstanceAfter(c.anchor, container, c.instance);
|
|
1307
|
+
if (created)
|
|
1308
|
+
this.$changeStructure();
|
|
1309
|
+
}
|
|
1146
1310
|
}
|
|
1147
1311
|
else if (c.instance) {
|
|
1148
1312
|
this.$removeInstance(c.instance);
|
|
1149
1313
|
c.instance = null;
|
|
1314
|
+
this.$changeStructure(c.owner);
|
|
1150
1315
|
}
|
|
1151
1316
|
}
|
|
1152
1317
|
$resolveValue(path, scope) {
|
|
@@ -1164,6 +1329,23 @@ export class TemplateElement extends HTMLElement {
|
|
|
1164
1329
|
return this.$resolveComponentRoot(path);
|
|
1165
1330
|
return dotWalk(this.$resolveComponentRoot(path.substring(0, dot)), path, dot + 1);
|
|
1166
1331
|
}
|
|
1332
|
+
$hasStateRoot(path, scope) {
|
|
1333
|
+
let frame = scope;
|
|
1334
|
+
while (frame) {
|
|
1335
|
+
if (path === frame.name
|
|
1336
|
+
|| (path.length > frame.name.length
|
|
1337
|
+
&& path.charCodeAt(frame.name.length) === 46
|
|
1338
|
+
&& path.startsWith(frame.name))) {
|
|
1339
|
+
return frame.value !== undefined;
|
|
1340
|
+
}
|
|
1341
|
+
frame = frame.parent;
|
|
1342
|
+
}
|
|
1343
|
+
const dot = path.indexOf('.');
|
|
1344
|
+
const root = dot === -1 ? path : path.substring(0, dot);
|
|
1345
|
+
return hasAuthoredMember(this, root)
|
|
1346
|
+
|| (this.$templateState !== null
|
|
1347
|
+
&& Object.prototype.hasOwnProperty.call(this.$templateState, root));
|
|
1348
|
+
}
|
|
1167
1349
|
$resolveComponentRoot(root) {
|
|
1168
1350
|
const instance = this;
|
|
1169
1351
|
if (this.$templateState &&
|
|
@@ -1189,64 +1371,92 @@ export class TemplateElement extends HTMLElement {
|
|
|
1189
1371
|
$block(blockIndex) {
|
|
1190
1372
|
return this.$meta?.b?.[blockIndex];
|
|
1191
1373
|
}
|
|
1192
|
-
$createBlockInstance(blockIndex, scope) {
|
|
1374
|
+
$createBlockInstance(blockIndex, scope, parent, container) {
|
|
1193
1375
|
const bm = this.$block(blockIndex);
|
|
1194
1376
|
if (!bm)
|
|
1195
1377
|
return null;
|
|
1196
1378
|
const wrapper = this.$createStagingRoot(bm);
|
|
1197
1379
|
const inst = this.$wire(wrapper, bm, scope);
|
|
1380
|
+
inst.parent = parent;
|
|
1198
1381
|
inst.nodes = childNodesArray(wrapper);
|
|
1199
1382
|
this.$updateInstance(inst);
|
|
1200
1383
|
if (inst.repeats.length !== 0 || inst.conds.length !== 0) {
|
|
1201
1384
|
inst.nodes = childNodesArray(wrapper);
|
|
1202
|
-
this.$
|
|
1385
|
+
this.$replaceInstanceContainer(inst, wrapper, container ?? null);
|
|
1386
|
+
}
|
|
1387
|
+
else if (container) {
|
|
1388
|
+
inst.container = container;
|
|
1203
1389
|
}
|
|
1204
1390
|
return inst;
|
|
1205
1391
|
}
|
|
1206
1392
|
$removeInstance(instance) {
|
|
1207
|
-
this.$
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1393
|
+
this.$disposeInstance(instance, true);
|
|
1394
|
+
}
|
|
1395
|
+
$disposeInstance(root, removeNodes) {
|
|
1396
|
+
const stack = [root];
|
|
1397
|
+
while (stack.length > 0) {
|
|
1398
|
+
const instance = stack.pop();
|
|
1399
|
+
if (!instance)
|
|
1400
|
+
continue;
|
|
1401
|
+
const cleanups = instance.cleanups;
|
|
1402
|
+
if (cleanups) {
|
|
1403
|
+
for (const cleanup of cleanups)
|
|
1404
|
+
cleanup();
|
|
1405
|
+
cleanups.length = 0;
|
|
1406
|
+
}
|
|
1407
|
+
if (removeNodes) {
|
|
1408
|
+
for (const node of instance.nodes)
|
|
1409
|
+
node.parentNode?.removeChild(node);
|
|
1410
|
+
}
|
|
1411
|
+
for (const binding of instance.conds) {
|
|
1412
|
+
if (binding.instance)
|
|
1413
|
+
stack.push(binding.instance);
|
|
1414
|
+
binding.instance = null;
|
|
1415
|
+
}
|
|
1416
|
+
for (const repeat of instance.repeats) {
|
|
1417
|
+
for (const child of repeat.instances)
|
|
1418
|
+
stack.push(child);
|
|
1419
|
+
this.$clearRepeatBinding(repeat);
|
|
1420
|
+
}
|
|
1421
|
+
this.$clearInstance(instance);
|
|
1213
1422
|
}
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1423
|
+
}
|
|
1424
|
+
$compactNodeArray(instance) {
|
|
1425
|
+
const container = instance.container;
|
|
1426
|
+
if (!container)
|
|
1427
|
+
return;
|
|
1428
|
+
const nodes = instance.nodes;
|
|
1429
|
+
let write = 0;
|
|
1430
|
+
for (let read = 0; read < nodes.length; read++) {
|
|
1431
|
+
const node = nodes[read];
|
|
1432
|
+
if (node.parentNode === container) {
|
|
1433
|
+
nodes[write] = node;
|
|
1434
|
+
write++;
|
|
1435
|
+
}
|
|
1217
1436
|
}
|
|
1437
|
+
nodes.length = write;
|
|
1438
|
+
}
|
|
1439
|
+
$changeStructure(removedFrom) {
|
|
1440
|
+
if (removedFrom) {
|
|
1441
|
+
let current = removedFrom;
|
|
1442
|
+
while (current) {
|
|
1443
|
+
this.$compactNodeArray(current);
|
|
1444
|
+
current = current.parent;
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
this.$pathIndex = undefined;
|
|
1448
|
+
this.$wildcardBindings = undefined;
|
|
1218
1449
|
}
|
|
1219
1450
|
$insertInstanceAfter(cursor, container, instance) {
|
|
1451
|
+
this.$replaceInstanceContainer(instance, instance.container, container);
|
|
1220
1452
|
const nodes = instance.nodes;
|
|
1221
1453
|
if (nodes.length === 0)
|
|
1222
1454
|
return cursor;
|
|
1223
1455
|
const ref = cursor ? cursor.nextSibling : container.firstChild;
|
|
1224
1456
|
if (nodes[0] === ref)
|
|
1225
1457
|
return nodes[nodes.length - 1];
|
|
1226
|
-
const frag = document.createDocumentFragment();
|
|
1227
1458
|
for (let i = 0; i < nodes.length; i++)
|
|
1228
|
-
|
|
1229
|
-
container.insertBefore(frag, ref);
|
|
1459
|
+
container.insertBefore(nodes[i], ref);
|
|
1230
1460
|
return nodes[nodes.length - 1];
|
|
1231
1461
|
}
|
|
1232
|
-
$singleDynamic(parts) {
|
|
1233
|
-
let path = '';
|
|
1234
|
-
let prefix = '';
|
|
1235
|
-
let suffix = '';
|
|
1236
|
-
let seen = false;
|
|
1237
|
-
for (const p of parts) {
|
|
1238
|
-
if (typeof p === 'string') {
|
|
1239
|
-
if (seen)
|
|
1240
|
-
suffix += p;
|
|
1241
|
-
else
|
|
1242
|
-
prefix += p;
|
|
1243
|
-
continue;
|
|
1244
|
-
}
|
|
1245
|
-
if (seen)
|
|
1246
|
-
return null;
|
|
1247
|
-
path = p[0];
|
|
1248
|
-
seen = true;
|
|
1249
|
-
}
|
|
1250
|
-
return seen ? { path, prefix, suffix } : null;
|
|
1251
|
-
}
|
|
1252
1462
|
}
|