@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.
Files changed (46) hide show
  1. package/README.md +130 -70
  2. package/dist/decorators.d.ts +1 -0
  3. package/dist/decorators.js +3 -0
  4. package/dist/element/diff.d.ts +3 -1
  5. package/dist/element/diff.js +197 -90
  6. package/dist/element/markers.js +13 -5
  7. package/dist/element/types.d.ts +18 -10
  8. package/dist/element/types.js +3 -0
  9. package/dist/element.d.ts +0 -4
  10. package/dist/element.js +11 -69
  11. package/dist/hydration-mismatch.js +3 -2
  12. package/dist/index.js +1 -1
  13. package/dist/lifecycle.d.ts +17 -0
  14. package/dist/lifecycle.js +66 -3
  15. package/dist/static-host.js +20 -10
  16. package/dist/streaming-activation.d.ts +2 -0
  17. package/dist/streaming-activation.js +13 -0
  18. package/dist/streaming-bootstrap.d.ts +2 -0
  19. package/dist/streaming-bootstrap.js +48 -0
  20. package/dist/streaming-cleanup.d.ts +4 -0
  21. package/dist/streaming-cleanup.js +66 -0
  22. package/dist/streaming-coordinator.d.ts +7 -0
  23. package/dist/streaming-coordinator.js +367 -0
  24. package/dist/streaming-deferred.d.ts +22 -0
  25. package/dist/streaming-deferred.js +273 -0
  26. package/dist/streaming-dom.d.ts +32 -0
  27. package/dist/streaming-dom.js +148 -0
  28. package/dist/streaming-entry.d.ts +1 -0
  29. package/dist/streaming-entry.js +2 -0
  30. package/dist/streaming-install.d.ts +2 -0
  31. package/dist/streaming-install.js +25 -0
  32. package/dist/streaming-mode.d.ts +5 -0
  33. package/dist/streaming-mode.js +14 -0
  34. package/dist/streaming-protocol.d.ts +32 -0
  35. package/dist/streaming-protocol.js +27 -0
  36. package/dist/streaming.d.ts +5 -0
  37. package/dist/streaming.js +9 -0
  38. package/dist/template-element.d.ts +28 -9
  39. package/dist/template-element.js +448 -238
  40. package/dist/template-events.js +1 -1
  41. package/dist/template-roots.d.ts +0 -1
  42. package/dist/template-roots.js +0 -11
  43. package/dist/template-types.d.ts +7 -1
  44. package/dist/template.d.ts +2 -0
  45. package/dist/template.js +46 -19
  46. package/package.json +9 -3
@@ -1,8 +1,3 @@
1
- function asParent(node) {
2
- if (!node)
3
- return null;
4
- return 'childNodes' in node ? node : null;
5
- }
6
1
  export function dotWalk(cursor, path, from) {
7
2
  let start = from;
8
3
  for (let i = from; i <= path.length; i++) {
@@ -15,111 +10,223 @@ export function dotWalk(cursor, path, from) {
15
10
  }
16
11
  return cursor;
17
12
  }
18
- function itemKey(item, keyPath) {
19
- if (keyPath === undefined || keyPath === '')
20
- return null;
21
- const v = dotWalk(item, keyPath, 0);
22
- return v != null ? String(v) : '';
23
- }
24
13
  function itemScope(rep, item) {
25
- return { name: rep.itemVar, value: item, parent: rep.scope };
14
+ return { name: rep.itemVar, value: item, parent: rep.scope, known: true };
26
15
  }
27
- export function syncRepeat(host, rep) {
28
- const resolved = host.$resolveValue(rep.collection, rep.scope);
29
- const items = Array.isArray(resolved) ? resolved : [];
30
- let container = rep.container
31
- ?? (rep.start ? asParent(rep.start.parentNode) : null)
32
- ?? (rep.owner.nodes[0] ? asParent(rep.owner.nodes[0].parentNode) : null);
33
- if (!container)
16
+ export function createRepeatKeyState(path) {
17
+ return {
18
+ path,
19
+ warned: false,
20
+ keys: [],
21
+ nextKeys: [],
22
+ map: new Map(),
23
+ };
24
+ }
25
+ export function seedHydratedRepeatKeys(rep, items) {
26
+ const state = rep.keyState;
27
+ if (!state || items.length !== rep.instances.length)
34
28
  return;
35
- rep.container = container;
36
- if (!rep.synced && items.length === 0 && rep.instances.length > 0)
29
+ if (!collectRepeatKeys(items, state))
37
30
  return;
38
- rep.synced = true;
39
- if (items.length === 0) {
40
- for (let i = 0; i < rep.instances.length; i += 1) {
41
- host.$removeInstance(rep.instances[i].instance);
42
- }
43
- rep.instances = [];
31
+ commitKeyIdentity(state);
32
+ }
33
+ function setItemScope(instance, item) {
34
+ if (!instance.scope)
44
35
  return;
36
+ instance.scope.value = item;
37
+ instance.scope.known = true;
38
+ }
39
+ function syncPositional(host, rep, items, container) {
40
+ const instances = rep.instances;
41
+ const oldLength = instances.length;
42
+ const reuseCount = Math.min(oldLength, items.length);
43
+ let nextCount = reuseCount;
44
+ let created = false;
45
+ for (let i = 0; i < reuseCount; i += 1) {
46
+ setItemScope(instances[i], items[i]);
45
47
  }
46
- const keyPath = rep.keyPath;
47
- const hasKeys = keyPath !== undefined && keyPath !== '';
48
- const oldInstances = rep.instances;
49
- if (!hasKeys) {
50
- const oldLength = oldInstances.length;
51
- const reuseCount = Math.min(oldLength, items.length);
52
- let nextCount = reuseCount;
53
- for (let i = 0; i < reuseCount; i += 1) {
54
- const entry = oldInstances[i];
55
- entry.value = items[i];
56
- if (entry.instance.scope)
57
- entry.instance.scope.value = items[i];
58
- }
59
- for (let i = reuseCount; i < items.length; i += 1) {
60
- const scope = itemScope(rep, items[i]);
61
- const instance = host.$createBlockInstance(rep.blockIndex, scope);
62
- if (instance) {
63
- oldInstances[nextCount] = { key: null, value: items[i], instance };
64
- nextCount += 1;
65
- }
48
+ for (let i = reuseCount; i < items.length; i += 1) {
49
+ const instance = host.$createBlockInstance(rep.blockIndex, itemScope(rep, items[i]), rep.owner, container);
50
+ if (instance) {
51
+ instances[nextCount] = instance;
52
+ nextCount += 1;
53
+ created = true;
66
54
  }
67
- for (let i = reuseCount; i < oldLength; i += 1) {
68
- host.$removeInstance(oldInstances[i].instance);
55
+ }
56
+ for (let i = reuseCount; i < oldLength; i += 1) {
57
+ host.$removeInstance(instances[i]);
58
+ }
59
+ instances.length = nextCount;
60
+ const removed = oldLength > reuseCount;
61
+ let cursor = rep.start;
62
+ for (let i = 0; i < instances.length; i += 1) {
63
+ cursor = host.$insertInstanceAfter(cursor, container, instances[i]);
64
+ }
65
+ for (let i = 0; i < reuseCount; i += 1) {
66
+ host.$updateInstance(instances[i]);
67
+ }
68
+ if (created || removed)
69
+ host.$changeStructure(removed ? rep.owner : undefined);
70
+ }
71
+ function readRepeatKey(item, path) {
72
+ if (path.length === 0)
73
+ return item;
74
+ return dotWalk(item, path, 0);
75
+ }
76
+ function isRepeatKey(value) {
77
+ return (typeof value === 'string' ||
78
+ (typeof value === 'number' && Number.isFinite(value)));
79
+ }
80
+ function collectRepeatKeys(items, state) {
81
+ const keys = state.nextKeys;
82
+ const seen = state.map;
83
+ keys.length = items.length;
84
+ seen.clear();
85
+ for (let i = 0; i < items.length; i += 1) {
86
+ let value;
87
+ try {
88
+ value = readRepeatKey(items[i], state.path);
69
89
  }
70
- oldInstances.length = nextCount;
71
- let cursor = rep.start;
72
- for (let i = 0; i < oldInstances.length; i += 1) {
73
- cursor = host.$insertInstanceAfter(cursor, container, oldInstances[i].instance);
90
+ catch {
91
+ keys.length = 0;
92
+ seen.clear();
93
+ return false;
74
94
  }
75
- for (let i = 0; i < reuseCount; i += 1) {
76
- host.$updateInstance(oldInstances[i].instance);
95
+ if (!isRepeatKey(value) || seen.has(value)) {
96
+ keys.length = 0;
97
+ seen.clear();
98
+ return false;
77
99
  }
78
- return;
79
- }
80
- const oldByKey = new Map();
81
- for (let i = 0; i < oldInstances.length; i += 1) {
82
- const entry = oldInstances[i];
83
- const k = entry.key;
84
- if (k != null)
85
- oldByKey.set(k, entry);
100
+ keys[i] = value;
101
+ seen.set(value, null);
86
102
  }
103
+ seen.clear();
104
+ return true;
105
+ }
106
+ function commitKeyIdentity(state) {
107
+ const oldKeys = state.keys;
108
+ state.keys = state.nextKeys;
109
+ state.nextKeys = oldKeys;
110
+ state.nextKeys.length = 0;
111
+ }
112
+ function clearKeyIdentity(state) {
113
+ state.keys.length = 0;
114
+ state.nextKeys.length = 0;
115
+ state.map.clear();
116
+ }
117
+ function warnKeyFallback(rep, state) {
118
+ if (state.warned)
119
+ return;
120
+ state.warned = true;
121
+ const key = state.path.length === 0
122
+ ? rep.itemVar
123
+ : `${rep.itemVar}.${state.path}`;
124
+ console.warn(`[webui] repeat "${rep.collection}" produced duplicate or invalid values for child key="${key}"; using positional reconciliation`);
125
+ }
126
+ function reconcileByKey(host, rep, items, container, state) {
127
+ const instances = rep.instances;
128
+ const map = state.map;
87
129
  let nextCount = 0;
130
+ let created = false;
131
+ let removed = false;
88
132
  for (let i = 0; i < items.length; i += 1) {
89
- const item = items[i];
90
- const key = itemKey(item, keyPath);
91
- const existing = key != null ? oldByKey.get(key) : undefined;
92
- if (existing) {
93
- oldByKey.set(key, undefined);
94
- existing.value = item;
95
- existing.key = key;
96
- if (existing.instance.scope)
97
- existing.instance.scope.value = item;
98
- oldInstances[nextCount] = existing;
99
- nextCount += 1;
133
+ const key = state.nextKeys[i];
134
+ let instance = map.get(key);
135
+ if (instance) {
136
+ map.set(key, null);
137
+ setItemScope(instance, items[i]);
100
138
  }
101
139
  else {
102
- const scope = itemScope(rep, item);
103
- const instance = host.$createBlockInstance(rep.blockIndex, scope);
104
- if (instance) {
105
- oldInstances[nextCount] = { key: key ?? null, value: item, instance };
106
- nextCount += 1;
107
- }
140
+ instance = host.$createBlockInstance(rep.blockIndex, itemScope(rep, items[i]), rep.owner, container) ?? undefined;
141
+ if (instance)
142
+ created = true;
143
+ }
144
+ if (instance) {
145
+ instances[nextCount] = instance;
146
+ state.nextKeys[nextCount] = key;
147
+ nextCount += 1;
108
148
  }
109
149
  }
110
- for (const leftover of oldByKey.values()) {
111
- if (leftover)
112
- host.$removeInstance(leftover.instance);
150
+ state.nextKeys.length = nextCount;
151
+ for (const instance of map.values()) {
152
+ if (instance) {
153
+ host.$removeInstance(instance);
154
+ removed = true;
155
+ }
113
156
  }
114
- oldInstances.length = nextCount;
115
157
  let cursor = rep.start;
116
- for (let i = 0; i < oldInstances.length; i += 1) {
117
- cursor = host.$insertInstanceAfter(cursor, container, oldInstances[i].instance);
158
+ for (let i = 0; i < nextCount; i += 1) {
159
+ cursor = host.$insertInstanceAfter(cursor, container, instances[i]);
160
+ }
161
+ for (let i = 0; i < nextCount; i += 1) {
162
+ if (map.get(state.nextKeys[i]) === null) {
163
+ host.$updateInstance(instances[i]);
164
+ }
165
+ }
166
+ map.clear();
167
+ instances.length = nextCount;
168
+ commitKeyIdentity(state);
169
+ if (created || removed)
170
+ host.$changeStructure(removed ? rep.owner : undefined);
171
+ }
172
+ function syncKeyed(host, rep, items, container, state) {
173
+ if (!collectRepeatKeys(items, state)) {
174
+ warnKeyFallback(rep, state);
175
+ clearKeyIdentity(state);
176
+ syncPositional(host, rep, items, container);
177
+ return;
178
+ }
179
+ let sharedOrder = true;
180
+ const sharedLength = Math.min(state.keys.length, state.nextKeys.length);
181
+ for (let i = 0; sharedOrder && i < sharedLength; i += 1) {
182
+ sharedOrder = state.keys[i] === state.nextKeys[i];
118
183
  }
119
- for (let i = 0; i < oldInstances.length; i += 1) {
120
- const entry = oldInstances[i];
121
- if (entry.key != null && oldByKey.has(entry.key) && oldByKey.get(entry.key) === undefined) {
122
- host.$updateInstance(entry.instance);
184
+ if (state.keys.length !== rep.instances.length || sharedOrder) {
185
+ syncPositional(host, rep, items, container);
186
+ if (rep.instances.length === items.length) {
187
+ commitKeyIdentity(state);
188
+ }
189
+ else {
190
+ clearKeyIdentity(state);
123
191
  }
192
+ return;
193
+ }
194
+ for (let i = 0; i < state.keys.length; i += 1) {
195
+ state.map.set(state.keys[i], rep.instances[i]);
196
+ }
197
+ reconcileByKey(host, rep, items, container, state);
198
+ }
199
+ export function syncRepeat(host, rep) {
200
+ const resolved = host.$resolveValue(rep.collection, rep.scope);
201
+ const items = Array.isArray(resolved) ? resolved : [];
202
+ const container = (rep.container
203
+ ?? rep.start?.parentNode
204
+ ?? rep.owner.nodes[0]?.parentNode);
205
+ if (!container)
206
+ return;
207
+ rep.container = container;
208
+ if (!rep.synced
209
+ && rep.instances.length > 0
210
+ && !host.$hasStateRoot(rep.collection, rep.scope))
211
+ return;
212
+ rep.synced = true;
213
+ if (items.length === 0) {
214
+ const hadInstances = rep.instances.length !== 0;
215
+ for (let i = 0; i < rep.instances.length; i += 1) {
216
+ host.$removeInstance(rep.instances[i]);
217
+ }
218
+ rep.instances.length = 0;
219
+ if (hadInstances)
220
+ host.$changeStructure(rep.owner);
221
+ if (rep.keyState) {
222
+ clearKeyIdentity(rep.keyState);
223
+ }
224
+ return;
225
+ }
226
+ if (rep.keyState) {
227
+ syncKeyed(host, rep, items, container, rep.keyState);
228
+ }
229
+ else {
230
+ syncPositional(host, rep, items, container);
124
231
  }
125
232
  }
@@ -6,16 +6,24 @@ const MARKER_REPEAT_ITEM = 'wi';
6
6
  export function collectItemMarkers(repeatStart) {
7
7
  const items = [];
8
8
  let end = null;
9
+ let depth = 1;
9
10
  let node = repeatStart.nextSibling;
10
11
  while (node) {
11
12
  if (node.nodeType === 8) {
12
13
  const data = node.data;
13
- if (data === MARKER_REPEAT_END) {
14
- end = node;
15
- break;
14
+ if (data === MARKER_REPEAT_START) {
15
+ depth++;
16
16
  }
17
- if (data === MARKER_REPEAT_ITEM)
17
+ else if (data === MARKER_REPEAT_END) {
18
+ depth--;
19
+ if (depth === 0) {
20
+ end = node;
21
+ break;
22
+ }
23
+ }
24
+ else if (data === MARKER_REPEAT_ITEM && depth === 1) {
18
25
  items.push(node);
26
+ }
19
27
  }
20
28
  node = node.nextSibling;
21
29
  }
@@ -28,7 +36,7 @@ export function nextElement(marker) {
28
36
  return node;
29
37
  if (node.nodeType === 8) {
30
38
  const data = node.data;
31
- if (data === MARKER_REPEAT_END || data === MARKER_REPEAT_ITEM)
39
+ if (data === MARKER_REPEAT_END || data === MARKER_REPEAT_ITEM || data === MARKER_COND_END)
32
40
  return null;
33
41
  }
34
42
  node = node.nextSibling;
@@ -11,6 +11,7 @@ export declare const ATTR_KIND_ATTRIBUTE = 0;
11
11
  export declare const ATTR_KIND_COMPLEX = 1;
12
12
  export declare const ATTR_KIND_BOOLEAN = 2;
13
13
  export declare const ATTR_KIND_TEMPLATE = 3;
14
+ export declare function hasNativeLiveProperty(element: Element, name: string): boolean;
14
15
  export interface AttrBinding {
15
16
  element: Element;
16
17
  name: string;
@@ -24,9 +25,12 @@ export interface ScopeFrame {
24
25
  name: string;
25
26
  value: unknown;
26
27
  parent?: ScopeFrame;
28
+ known?: boolean;
27
29
  }
28
30
  export interface TemplateInstance {
29
31
  scope?: ScopeFrame;
32
+ parent?: TemplateInstance;
33
+ container: (ParentNode & Node) | null;
30
34
  nodes: Node[];
31
35
  texts: TextBinding[];
32
36
  attrs: AttrBinding[];
@@ -39,8 +43,17 @@ export interface CondBinding {
39
43
  blockIndex: number;
40
44
  anchor: Comment;
41
45
  scope?: ScopeFrame;
46
+ owner: TemplateInstance;
42
47
  instance: TemplateInstance | null;
43
48
  }
49
+ export type RepeatKey = string | number;
50
+ export interface RepeatKeyState {
51
+ path: string;
52
+ warned: boolean;
53
+ keys: RepeatKey[];
54
+ nextKeys: RepeatKey[];
55
+ map: Map<RepeatKey, TemplateInstance | null>;
56
+ }
44
57
  export interface RepeatBinding {
45
58
  markerId: number;
46
59
  collection: string;
@@ -51,21 +64,16 @@ export interface RepeatBinding {
51
64
  end: Comment | null;
52
65
  scope?: ScopeFrame;
53
66
  owner: TemplateInstance;
54
- instances: RepeatItemInstance[];
55
- rootTag: string | null;
56
- keyAttribute?: string;
57
- keyPath?: string;
67
+ instances: TemplateInstance[];
68
+ keyState?: RepeatKeyState;
58
69
  synced?: boolean;
59
70
  }
60
- export interface RepeatItemInstance {
61
- key: string | null;
62
- value: unknown;
63
- instance: TemplateInstance;
64
- }
65
71
  export interface RepeatHost {
66
72
  $resolveValue(path: string, scope?: ScopeFrame): unknown;
67
- $createBlockInstance(blockIndex: number, scope?: ScopeFrame): TemplateInstance | null;
73
+ $hasStateRoot(path: string, scope?: ScopeFrame): boolean;
74
+ $createBlockInstance(blockIndex: number, scope?: ScopeFrame, parent?: TemplateInstance, container?: ParentNode & Node): TemplateInstance | null;
68
75
  $updateInstance(instance: TemplateInstance): void;
69
76
  $removeInstance(instance: TemplateInstance): void;
77
+ $changeStructure(removedFrom?: TemplateInstance): void;
70
78
  $insertInstanceAfter(cursor: Node | null, container: ParentNode & Node, instance: TemplateInstance): Node | null;
71
79
  }
@@ -2,3 +2,6 @@ export const ATTR_KIND_ATTRIBUTE = 0;
2
2
  export const ATTR_KIND_COMPLEX = 1;
3
3
  export const ATTR_KIND_BOOLEAN = 2;
4
4
  export const ATTR_KIND_TEMPLATE = 3;
5
+ export function hasNativeLiveProperty(element, name) {
6
+ return element.localName.indexOf('-') === -1 && name in element;
7
+ }
package/dist/element.d.ts CHANGED
@@ -8,14 +8,10 @@ export declare class WebUIElement extends TemplateElement {
8
8
  $emit(name: string, detail?: unknown): boolean;
9
9
  protected $finalize(instance: TemplateInstance, root: Node, meta: TemplateBlockMeta, resolver: (root: Node, path: TemplateNodePath) => Node | null, scope?: ScopeFrame): void;
10
10
  private $wireEvents;
11
- private $resolveDelegatedEvents;
12
11
  private $wireRoot;
13
- private $addDelegatedEvent;
14
- private $dispatchDelegatedEvent;
15
12
  private $addEvent;
16
13
  private $addCleanup;
17
14
  private $callEventHandler;
18
- private $callEventHandlerWithCurrentTarget;
19
15
  private $resolveEventArgs;
20
16
  private $resolveEventArg;
21
17
  private $wireRefs;
package/dist/element.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import { TemplateElement } from './template-element.js';
2
- import { getObservableNames, isAttributeProperty, syncAttrProperties, } from './decorators.js';
2
+ import { attributeNameForProperty, getObservableNames, syncAttrProperties, } from './decorators.js';
3
3
  export class WebUIElement extends TemplateElement {
4
4
  $observableNames() {
5
5
  return getObservableNames(this.constructor);
6
6
  }
7
7
  $shouldApplySSRState(key) {
8
- return !isAttributeProperty(this.constructor, key);
8
+ const attribute = attributeNameForProperty(this.constructor, key);
9
+ return attribute === undefined || !this.hasAttribute(attribute);
9
10
  }
10
11
  $syncAuthoredAttributes() {
11
12
  syncAttrProperties(this, this.constructor);
@@ -28,32 +29,16 @@ export class WebUIElement extends TemplateElement {
28
29
  const groups = meta.eg;
29
30
  if (!groups)
30
31
  return;
31
- const delegateTarget = this.shadowRoot ?? this;
32
32
  for (let i = 0; i < groups.length; i++) {
33
33
  const [eventName, bindings] = groups[i];
34
- const bucket = this.$resolveDelegatedEvents(root, bindings, resolver, scope);
35
- this.$addDelegatedEvent(instance, delegateTarget, eventName, bucket);
36
- }
37
- }
38
- $resolveDelegatedEvents(root, bindings, resolver, scope) {
39
- const entries = [];
40
- for (let i = 0; i < bindings.length; i++) {
41
- const [handlerName, args, target, usesEvent] = bindings[i];
42
- const el = resolver(root, target);
43
- if (!el || el.nodeType !== 1)
44
- continue;
45
- const method = this[handlerName];
46
- if (typeof method !== 'function')
47
- continue;
48
- entries.push({
49
- target: el,
50
- method: method,
51
- args,
52
- usesEvent: usesEvent === 1,
53
- scope,
54
- });
34
+ for (let j = 0; j < bindings.length; j++) {
35
+ const [handlerName, args, target] = bindings[j];
36
+ const el = resolver(root, target);
37
+ if (el && el.nodeType === 1) {
38
+ this.$addEvent(instance, el, eventName, handlerName, args, scope);
39
+ }
40
+ }
55
41
  }
56
- return entries;
57
42
  }
58
43
  $wireRoot(instance, re) {
59
44
  const target = this.shadowRoot ?? this;
@@ -61,27 +46,6 @@ export class WebUIElement extends TemplateElement {
61
46
  this.$addEvent(instance, target, re[i][0], re[i][1], re[i][2], undefined);
62
47
  }
63
48
  }
64
- $addDelegatedEvent(instance, target, eventName, entries) {
65
- if (entries.length === 0)
66
- return;
67
- const listener = (event) => {
68
- this.$dispatchDelegatedEvent(entries, event);
69
- };
70
- target.addEventListener(eventName, listener);
71
- this.$addCleanup(instance, () => target.removeEventListener(eventName, listener));
72
- }
73
- $dispatchDelegatedEvent(entries, event) {
74
- let current = event.target;
75
- while (current) {
76
- for (let i = 0; i < entries.length; i++) {
77
- const entry = entries[i];
78
- if (entry.target === current) {
79
- this.$callEventHandler(entry.method, entry.args, event, entry.scope, entry.target, entry.usesEvent);
80
- }
81
- }
82
- current = current.parentNode;
83
- }
84
- }
85
49
  $addEvent(instance, target, eventName, handlerName, args, scope) {
86
50
  const method = this[handlerName];
87
51
  if (typeof method !== 'function')
@@ -93,11 +57,7 @@ export class WebUIElement extends TemplateElement {
93
57
  $addCleanup(instance, cleanup) {
94
58
  (instance.cleanups ??= []).push(cleanup);
95
59
  }
96
- $callEventHandler(method, args, event, scope, currentTarget, usesEvent = false) {
97
- if (currentTarget && usesEvent) {
98
- this.$callEventHandlerWithCurrentTarget(method, args, event, scope, currentTarget);
99
- return;
100
- }
60
+ $callEventHandler(method, args, event, scope) {
101
61
  switch (args.length) {
102
62
  case 0:
103
63
  method.call(this);
@@ -118,24 +78,6 @@ export class WebUIElement extends TemplateElement {
118
78
  method.apply(this, this.$resolveEventArgs(args, event, scope));
119
79
  }
120
80
  }
121
- $callEventHandlerWithCurrentTarget(method, args, event, scope, currentTarget) {
122
- const descriptor = Object.getOwnPropertyDescriptor(event, 'currentTarget');
123
- Object.defineProperty(event, 'currentTarget', {
124
- configurable: true,
125
- value: currentTarget,
126
- });
127
- try {
128
- this.$callEventHandler(method, args, event, scope);
129
- }
130
- finally {
131
- if (descriptor) {
132
- Object.defineProperty(event, 'currentTarget', descriptor);
133
- }
134
- else {
135
- delete event.currentTarget;
136
- }
137
- }
138
- }
139
81
  $resolveEventArgs(args, event, scope) {
140
82
  const resolved = [];
141
83
  for (let i = 0; i < args.length; i++) {
@@ -1,4 +1,4 @@
1
- import { ATTR_KIND_BOOLEAN, ATTR_KIND_COMPLEX, ATTR_KIND_TEMPLATE, } from './element/types.js';
1
+ import { ATTR_KIND_BOOLEAN, ATTR_KIND_COMPLEX, ATTR_KIND_TEMPLATE, hasNativeLiveProperty, } from './element/types.js';
2
2
  export function reportHydrationMismatch(tag, writes, index, ctx) {
3
3
  const mismatched = [];
4
4
  for (const path of writes) {
@@ -51,7 +51,8 @@ export function attrDiffersFromDom(b, ctx) {
51
51
  case ATTR_KIND_TEMPLATE:
52
52
  return (el.getAttribute(b.name) ?? '') !== ctx.resolveParts(b.parts, b.scope);
53
53
  default: {
54
- if (b.name === 'value' || b.name === 'checked' || b.name === 'selected')
54
+ if ((b.name === 'value' || b.name === 'checked' || b.name === 'selected')
55
+ && hasNativeLiveProperty(el, b.name))
55
56
  return false;
56
57
  const v = ctx.resolveValue(b.path, b.scope);
57
58
  return (el.getAttribute(b.name) ?? '') !== (v == null ? '' : String(v));
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { installTemplateElementRuntime } from './static-host.js';
2
- installTemplateElementRuntime();
2
+ setTimeout(installTemplateElementRuntime, 0);
3
3
  export { WebUIElement } from './element.js';
4
4
  export { observable, attr } from './decorators.js';
5
5
  export { getTemplate, registerTemplateData } from './template.js';
@@ -1,2 +1,19 @@
1
1
  export declare function hydrationStart(): void;
2
2
  export declare function hydrationEnd(): void;
3
+ export declare function beginStreamingGate(): void;
4
+ export declare function abortStreamingGate(): void;
5
+ export declare function markBoundaryPending(): void;
6
+ export declare function markBoundaryCommitted(terminal: boolean): void;
7
+ export declare function markLateActivationPending(): void;
8
+ export declare function settleLateActivation(): void;
9
+ export declare function __getLifecycleStateForTests(): {
10
+ pendingCount: number;
11
+ started: boolean;
12
+ completed: boolean;
13
+ streamingGateActive: boolean;
14
+ streamingGateAborted: boolean;
15
+ terminalReached: boolean;
16
+ pendingBoundaries: number;
17
+ pendingLateActivations: number;
18
+ };
19
+ export declare function __resetLifecycleForTests(): void;