@microsoft/fast-element 2.0.0-beta.3 → 2.0.0-beta.6

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 (68) hide show
  1. package/CHANGELOG.json +147 -0
  2. package/CHANGELOG.md +42 -1
  3. package/dist/dts/components/fast-definitions.d.ts +9 -8
  4. package/dist/dts/components/fast-element.d.ts +12 -24
  5. package/dist/dts/context.d.ts +1 -1
  6. package/dist/dts/di/di.d.ts +858 -0
  7. package/dist/dts/interfaces.d.ts +43 -7
  8. package/dist/dts/observation/observable.d.ts +19 -13
  9. package/dist/dts/state/exports.d.ts +3 -0
  10. package/dist/dts/state/reactive.d.ts +8 -0
  11. package/dist/dts/state/state.d.ts +141 -0
  12. package/dist/dts/state/visitor.d.ts +6 -0
  13. package/dist/dts/state/watch.d.ts +10 -0
  14. package/dist/dts/styles/element-styles.d.ts +6 -0
  15. package/dist/dts/templating/binding-signal.d.ts +10 -27
  16. package/dist/dts/templating/binding-two-way.d.ts +16 -41
  17. package/dist/dts/templating/binding.d.ts +79 -118
  18. package/dist/dts/templating/html-directive.d.ts +28 -2
  19. package/dist/dts/templating/render.d.ts +277 -0
  20. package/dist/dts/templating/repeat.d.ts +12 -16
  21. package/dist/dts/templating/template.d.ts +3 -3
  22. package/dist/dts/templating/when.d.ts +3 -3
  23. package/dist/dts/testing/exports.d.ts +2 -0
  24. package/dist/dts/testing/fixture.d.ts +90 -0
  25. package/dist/dts/testing/timeout.d.ts +7 -0
  26. package/dist/dts/utilities.d.ts +0 -18
  27. package/dist/esm/components/fast-definitions.js +25 -27
  28. package/dist/esm/components/fast-element.js +20 -11
  29. package/dist/esm/context.js +5 -1
  30. package/dist/esm/debug.js +35 -4
  31. package/dist/esm/di/di.js +1351 -0
  32. package/dist/esm/interfaces.js +4 -0
  33. package/dist/esm/observation/arrays.js +303 -2
  34. package/dist/esm/observation/observable.js +11 -6
  35. package/dist/esm/platform.js +1 -1
  36. package/dist/esm/state/exports.js +3 -0
  37. package/dist/esm/state/reactive.js +34 -0
  38. package/dist/esm/state/state.js +148 -0
  39. package/dist/esm/state/visitor.js +28 -0
  40. package/dist/esm/state/watch.js +36 -0
  41. package/dist/esm/styles/element-styles.js +14 -0
  42. package/dist/esm/templating/binding-signal.js +56 -61
  43. package/dist/esm/templating/binding-two-way.js +51 -35
  44. package/dist/esm/templating/binding.js +137 -156
  45. package/dist/esm/templating/compiler.js +29 -7
  46. package/dist/esm/templating/html-directive.js +12 -1
  47. package/dist/esm/templating/render.js +392 -0
  48. package/dist/esm/templating/repeat.js +57 -40
  49. package/dist/esm/templating/template.js +8 -5
  50. package/dist/esm/templating/view.js +3 -1
  51. package/dist/esm/templating/when.js +5 -4
  52. package/dist/esm/testing/exports.js +2 -0
  53. package/dist/esm/testing/fixture.js +88 -0
  54. package/dist/esm/testing/timeout.js +24 -0
  55. package/dist/esm/utilities.js +0 -95
  56. package/dist/fast-element.api.json +2827 -2757
  57. package/dist/fast-element.d.ts +215 -229
  58. package/dist/fast-element.debug.js +650 -256
  59. package/dist/fast-element.debug.min.js +1 -1
  60. package/dist/fast-element.js +615 -252
  61. package/dist/fast-element.min.js +1 -1
  62. package/dist/fast-element.untrimmed.d.ts +223 -234
  63. package/docs/api-report.md +87 -90
  64. package/package.json +18 -9
  65. package/dist/dts/hooks.d.ts +0 -20
  66. package/dist/dts/observation/splice-strategies.d.ts +0 -13
  67. package/dist/esm/hooks.js +0 -32
  68. package/dist/esm/observation/splice-strategies.js +0 -400
@@ -1,10 +1,10 @@
1
- import { isFunction } from "../interfaces.js";
2
1
  import { Observable, } from "../observation/observable.js";
3
2
  import { emptyArray } from "../platform.js";
4
3
  import { ArrayObserver } from "../observation/arrays.js";
5
- import { Markup } from "./markup.js";
4
+ import { Markup, nextId } from "./markup.js";
6
5
  import { HTMLDirective, } from "./html-directive.js";
7
6
  import { HTMLView } from "./view.js";
7
+ import { normalizeBinding } from "./binding.js";
8
8
  const defaultRepeatOptions = Object.freeze({
9
9
  positioning: false,
10
10
  recycle: true,
@@ -23,17 +23,15 @@ export class RepeatBehavior {
23
23
  /**
24
24
  * Creates an instance of RepeatBehavior.
25
25
  * @param location - The location in the DOM to render the repeat.
26
- * @param itemsBinding - The array to render.
26
+ * @param dataBinding - The array to render.
27
27
  * @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
28
28
  * @param templateBinding - The template to render for each item.
29
29
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
30
30
  * @param options - Options used to turn on special repeat features.
31
31
  */
32
- constructor(location, itemsBinding, isItemsBindingVolatile, templateBinding, isTemplateBindingVolatile, options) {
32
+ constructor(directive, location) {
33
+ this.directive = directive;
33
34
  this.location = location;
34
- this.itemsBinding = itemsBinding;
35
- this.templateBinding = templateBinding;
36
- this.options = options;
37
35
  this.source = null;
38
36
  this.views = [];
39
37
  this.items = null;
@@ -41,9 +39,9 @@ export class RepeatBehavior {
41
39
  this.context = void 0;
42
40
  this.childContext = void 0;
43
41
  this.bindView = bindWithoutPositioning;
44
- this.itemsBindingObserver = Observable.binding(itemsBinding, this, isItemsBindingVolatile);
45
- this.templateBindingObserver = Observable.binding(templateBinding, this, isTemplateBindingVolatile);
46
- if (options.positioning) {
42
+ this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
43
+ this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
44
+ if (directive.options.positioning) {
47
45
  this.bindView = bindWithPositioning;
48
46
  }
49
47
  }
@@ -81,15 +79,18 @@ export class RepeatBehavior {
81
79
  * @param args - The details about what was changed.
82
80
  */
83
81
  handleChange(source, args) {
84
- if (source === this.itemsBinding) {
82
+ if (args === this.itemsBindingObserver) {
85
83
  this.items = this.itemsBindingObserver.observe(this.source, this.context);
86
84
  this.observeItems();
87
85
  this.refreshAllViews();
88
86
  }
89
- else if (source === this.templateBinding) {
87
+ else if (args === this.templateBindingObserver) {
90
88
  this.template = this.templateBindingObserver.observe(this.source, this.context);
91
89
  this.refreshAllViews(true);
92
90
  }
91
+ else if (!args[0]) {
92
+ return;
93
+ }
93
94
  else if (args[0].reset) {
94
95
  this.refreshAllViews();
95
96
  }
@@ -115,36 +116,51 @@ export class RepeatBehavior {
115
116
  updateViews(splices) {
116
117
  const views = this.views;
117
118
  const childContext = this.childContext;
118
- const totalRemoved = [];
119
119
  const bindView = this.bindView;
120
- let removeDelta = 0;
121
- for (let i = 0, ii = splices.length; i < ii; ++i) {
122
- const splice = splices[i];
123
- const removed = splice.removed;
124
- totalRemoved.push(...views.splice(splice.index + removeDelta, removed.length));
125
- removeDelta -= splice.addedCount;
126
- }
127
120
  const items = this.items;
128
121
  const template = this.template;
122
+ const recycle = this.directive.options.recycle;
123
+ const leftoverViews = [];
124
+ let leftoverIndex = 0;
125
+ let availableViews = 0;
129
126
  for (let i = 0, ii = splices.length; i < ii; ++i) {
130
127
  const splice = splices[i];
128
+ const removed = splice.removed;
129
+ let removeIndex = 0;
131
130
  let addIndex = splice.index;
132
131
  const end = addIndex + splice.addedCount;
132
+ const removedViews = views.splice(splice.index, removed.length);
133
+ availableViews = leftoverViews.length + removedViews.length;
133
134
  for (; addIndex < end; ++addIndex) {
134
135
  const neighbor = views[addIndex];
135
136
  const location = neighbor ? neighbor.firstChild : this.location;
136
- const view = this.options.recycle && totalRemoved.length > 0
137
- ? totalRemoved.shift()
138
- : template.create();
137
+ let view;
138
+ if (recycle && availableViews > 0) {
139
+ if (removeIndex <= availableViews && removedViews.length > 0) {
140
+ view = removedViews[removeIndex];
141
+ removeIndex++;
142
+ }
143
+ else {
144
+ view = leftoverViews[leftoverIndex];
145
+ leftoverIndex++;
146
+ }
147
+ availableViews--;
148
+ }
149
+ else {
150
+ view = template.create();
151
+ }
139
152
  views.splice(addIndex, 0, view);
140
153
  bindView(view, items, addIndex, childContext);
141
154
  view.insertBefore(location);
142
155
  }
156
+ if (removedViews[removeIndex]) {
157
+ leftoverViews.push(...removedViews.slice(removeIndex));
158
+ }
143
159
  }
144
- for (let i = 0, ii = totalRemoved.length; i < ii; ++i) {
145
- totalRemoved[i].dispose();
160
+ for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
161
+ leftoverViews[i].dispose();
146
162
  }
147
- if (this.options.positioning) {
163
+ if (this.directive.options.positioning) {
148
164
  for (let i = 0, ii = views.length; i < ii; ++i) {
149
165
  views[i].context.updatePosition(i, ii);
150
166
  }
@@ -159,7 +175,7 @@ export class RepeatBehavior {
159
175
  let itemsLength = items.length;
160
176
  let views = this.views;
161
177
  let viewsLength = views.length;
162
- if (itemsLength === 0 || templateChanged || !this.options.recycle) {
178
+ if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
163
179
  // all views need to be removed
164
180
  HTMLView.disposeContiguousBatch(views);
165
181
  viewsLength = 0;
@@ -209,17 +225,19 @@ export class RepeatBehavior {
209
225
  export class RepeatDirective {
210
226
  /**
211
227
  * Creates an instance of RepeatDirective.
212
- * @param itemsBinding - The binding that provides the array to render.
228
+ * @param dataBinding - The binding that provides the array to render.
213
229
  * @param templateBinding - The template binding used to obtain a template to render for each item in the array.
214
230
  * @param options - Options used to turn on special repeat features.
215
231
  */
216
- constructor(itemsBinding, templateBinding, options) {
217
- this.itemsBinding = itemsBinding;
232
+ constructor(dataBinding, templateBinding, options) {
233
+ this.dataBinding = dataBinding;
218
234
  this.templateBinding = templateBinding;
219
235
  this.options = options;
236
+ /**
237
+ * The unique id of the factory.
238
+ */
239
+ this.id = nextId();
220
240
  ArrayObserver.enable();
221
- this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
222
- this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
223
241
  }
224
242
  /**
225
243
  * Creates a placeholder string based on the directive's index within the template.
@@ -233,21 +251,20 @@ export class RepeatDirective {
233
251
  * @param target - The node instance to create the behavior for.
234
252
  */
235
253
  createBehavior(targets) {
236
- return new RepeatBehavior(targets[this.nodeId], this.itemsBinding, this.isItemsBindingVolatile, this.templateBinding, this.isTemplateBindingVolatile, this.options);
254
+ return new RepeatBehavior(this, targets[this.nodeId]);
237
255
  }
238
256
  }
239
257
  HTMLDirective.define(RepeatDirective);
240
258
  /**
241
259
  * A directive that enables list rendering.
242
- * @param itemsBinding - The array to render.
243
- * @param templateOrTemplateBinding - The template or a template binding used obtain a template
260
+ * @param items - The array to render.
261
+ * @param template - The template or a template binding used obtain a template
244
262
  * to render for each item in the array.
245
263
  * @param options - Options used to turn on special repeat features.
246
264
  * @public
247
265
  */
248
- export function repeat(itemsBinding, templateOrTemplateBinding, options = defaultRepeatOptions) {
249
- const templateBinding = isFunction(templateOrTemplateBinding)
250
- ? templateOrTemplateBinding
251
- : () => templateOrTemplateBinding;
252
- return new RepeatDirective(itemsBinding, templateBinding, options);
266
+ export function repeat(items, template, options = defaultRepeatOptions) {
267
+ const dataBinding = normalizeBinding(items);
268
+ const templateBinding = normalizeBinding(template);
269
+ return new RepeatDirective(dataBinding, templateBinding, Object.assign(Object.assign({}, defaultRepeatOptions), options));
253
270
  }
@@ -1,8 +1,8 @@
1
1
  import { isFunction, isString } from "../interfaces.js";
2
2
  import { ExecutionContext } from "../observation/observable.js";
3
- import { bind, oneTime } from "./binding.js";
3
+ import { bind, HTMLBindingDirective, oneTime } from "./binding.js";
4
4
  import { Compiler } from "./compiler.js";
5
- import { Aspect, HTMLDirective, } from "./html-directive.js";
5
+ import { Aspect, Binding, HTMLDirective, } from "./html-directive.js";
6
6
  import { nextId } from "./markup.js";
7
7
  /**
8
8
  * A template capable of creating HTMLView instances or rendering directly to DOM.
@@ -78,12 +78,12 @@ export function html(strings, ...values) {
78
78
  let definition;
79
79
  html += currentString;
80
80
  if (isFunction(currentValue)) {
81
- html += createAspectedHTML(bind(currentValue), currentString, add);
81
+ html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
82
82
  }
83
83
  else if (isString(currentValue)) {
84
84
  const match = lastAttributeNameRegex.exec(currentString);
85
85
  if (match !== null) {
86
- const directive = bind(() => currentValue, oneTime);
86
+ const directive = new HTMLBindingDirective(oneTime(() => currentValue));
87
87
  Aspect.assign(directive, match[2]);
88
88
  html += directive.createHTML(add);
89
89
  }
@@ -91,8 +91,11 @@ export function html(strings, ...values) {
91
91
  html += currentValue;
92
92
  }
93
93
  }
94
+ else if (currentValue instanceof Binding) {
95
+ html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
96
+ }
94
97
  else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
95
- html += createAspectedHTML(bind(() => currentValue, oneTime), currentString, add);
98
+ html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
96
99
  }
97
100
  else {
98
101
  if (definition.aspected) {
@@ -51,8 +51,10 @@ export class HTMLView {
51
51
  node.parentNode.insertBefore(this.fragment, node);
52
52
  }
53
53
  else {
54
- const parentNode = node.parentNode;
55
54
  const end = this.lastChild;
55
+ if (node.previousSibling === end)
56
+ return;
57
+ const parentNode = node.parentNode;
56
58
  let current = this.firstChild;
57
59
  let next;
58
60
  while (current !== end) {
@@ -1,14 +1,15 @@
1
1
  import { isFunction } from "../interfaces.js";
2
2
  /**
3
3
  * A directive that enables basic conditional rendering in a template.
4
- * @param binding - The condition to test for rendering.
4
+ * @param condition - The condition to test for rendering.
5
5
  * @param templateOrTemplateBinding - The template or a binding that gets
6
6
  * the template to render when the condition is true.
7
7
  * @public
8
8
  */
9
- export function when(binding, templateOrTemplateBinding) {
10
- const getTemplate = isFunction(templateOrTemplateBinding)
9
+ export function when(condition, templateOrTemplateBinding) {
10
+ const dataBinding = isFunction(condition) ? condition : () => condition;
11
+ const templateBinding = isFunction(templateOrTemplateBinding)
11
12
  ? templateOrTemplateBinding
12
13
  : () => templateOrTemplateBinding;
13
- return (source, context) => binding(source, context) ? getTemplate(source, context) : null;
14
+ return (source, context) => dataBinding(source, context) ? templateBinding(source, context) : null;
14
15
  }
@@ -0,0 +1,2 @@
1
+ export { timeout } from "./timeout.js";
2
+ export * from "./fixture.js";
@@ -0,0 +1,88 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { FASTElementDefinition } from "../components/fast-definitions.js";
11
+ import { ExecutionContext } from "../observation/observable.js";
12
+ import { ViewTemplate } from "../templating/template.js";
13
+ function findElement(view) {
14
+ let current = view.firstChild;
15
+ while (current !== null && current.nodeType !== 1) {
16
+ current = current.nextSibling;
17
+ }
18
+ return current;
19
+ }
20
+ /**
21
+ * Creates a random, unique name suitable for use as a Custom Element name.
22
+ * @public
23
+ */
24
+ export function uniqueElementName(prefix = "fast-unique") {
25
+ return `${prefix}-${Math.random().toString(36).substring(7)}`;
26
+ }
27
+ /**
28
+ * Creates a test fixture suitable for testing custom elements, templates, and bindings.
29
+ * @param templateNameOrType An HTML template or single element name to create the fixture for.
30
+ * @param options Enables customizing fixture creation behavior.
31
+ * @remarks
32
+ * Yields control to the caller one Microtask later, in order to
33
+ * ensure that the DOM has settled.
34
+ * @public
35
+ */
36
+ export function fixture(templateNameOrType, options = {}) {
37
+ return __awaiter(this, void 0, void 0, function* () {
38
+ const document = options.document || globalThis.document;
39
+ const parent = options.parent || document.createElement("div");
40
+ const source = options.source || {};
41
+ const context = options.context || ExecutionContext.default;
42
+ if (typeof templateNameOrType === "function") {
43
+ const def = FASTElementDefinition.getByType(templateNameOrType);
44
+ if (!def) {
45
+ throw new Error("Missing FASTElement definition.");
46
+ }
47
+ templateNameOrType = def.name;
48
+ }
49
+ if (typeof templateNameOrType === "string") {
50
+ const html = `<${templateNameOrType}></${templateNameOrType}>`;
51
+ templateNameOrType = new ViewTemplate(html, {});
52
+ }
53
+ const view = templateNameOrType.create();
54
+ const element = findElement(view);
55
+ let isConnected = false;
56
+ view.bind(source, context);
57
+ view.appendTo(parent);
58
+ customElements.upgrade(parent);
59
+ // Hook into the Microtask Queue to ensure the DOM is settled
60
+ // before yielding control to the caller.
61
+ yield Promise.resolve();
62
+ const connect = () => __awaiter(this, void 0, void 0, function* () {
63
+ if (isConnected) {
64
+ return;
65
+ }
66
+ isConnected = true;
67
+ document.body.appendChild(parent);
68
+ yield Promise.resolve();
69
+ });
70
+ const disconnect = () => __awaiter(this, void 0, void 0, function* () {
71
+ if (!isConnected) {
72
+ return;
73
+ }
74
+ isConnected = false;
75
+ document.body.removeChild(parent);
76
+ yield Promise.resolve();
77
+ });
78
+ return {
79
+ document,
80
+ template: templateNameOrType,
81
+ view,
82
+ parent,
83
+ element,
84
+ connect,
85
+ disconnect,
86
+ };
87
+ });
88
+ }
@@ -0,0 +1,24 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ /**
11
+ * A timeout helper for use in tests.
12
+ * @param timeout The length of the timeout.
13
+ * @returns A promise that resolves once the configured time has elapsed.
14
+ * @public
15
+ */
16
+ export function timeout(timeout = 0) {
17
+ return __awaiter(this, void 0, void 0, function* () {
18
+ return new Promise((resolve, reject) => {
19
+ window.setTimeout(() => {
20
+ resolve(void 0);
21
+ }, timeout);
22
+ });
23
+ });
24
+ }
@@ -1,98 +1,3 @@
1
- import { ArrayObserver } from "./index.debug.js";
2
- import { isFunction } from "./interfaces.js";
3
- import { Observable } from "./observation/observable.js";
4
- function shouldTraverse(value, traversed) {
5
- return (value !== null &&
6
- value !== void 0 &&
7
- typeof value === "object" &&
8
- !traversed.has(value));
9
- }
10
- function traverseObject(object, deep, visitor, data, traversed) {
11
- if (!shouldTraverse(object, traversed)) {
12
- return;
13
- }
14
- traversed.add(object);
15
- if (Array.isArray(object)) {
16
- visitor.visitArray(object, data);
17
- for (const item of object) {
18
- traverseObject(item, deep, visitor, data, traversed);
19
- }
20
- }
21
- else {
22
- visitor.visitObject(object, data);
23
- for (const key in object) {
24
- const value = object[key];
25
- visitor.visitProperty(object, key, value, data);
26
- if (deep) {
27
- traverseObject(value, deep, visitor, data, traversed);
28
- }
29
- }
30
- }
31
- }
32
- const noop = () => void 0;
33
- const observed = new WeakSet();
34
- const makeObserverVisitor = {
35
- visitObject: noop,
36
- visitArray: noop,
37
- visitProperty(object, propertyName, value) {
38
- Reflect.defineProperty(object, propertyName, {
39
- enumerable: true,
40
- get() {
41
- Observable.track(object, propertyName);
42
- return value;
43
- },
44
- set(newValue) {
45
- if (value !== newValue) {
46
- value = newValue;
47
- Observable.notify(object, propertyName);
48
- }
49
- },
50
- });
51
- },
52
- };
53
- function watchObject(object, data) {
54
- const notifier = Observable.getNotifier(object);
55
- notifier.subscribe(data.subscriber);
56
- data.notifiers.push(notifier);
57
- }
58
- const watchVisitor = {
59
- visitProperty: noop,
60
- visitObject: watchObject,
61
- visitArray: watchObject,
62
- };
63
- /**
64
- * Converts a plain object to an observable object.
65
- * @param object - The object to make observable.
66
- * @param deep - Indicates whether or not to deeply convert the oject.
67
- * @returns The converted object.
68
- * @beta
69
- */
70
- export function makeObservable(object, deep = false) {
71
- traverseObject(object, deep, makeObserverVisitor, void 0, observed);
72
- return object;
73
- }
74
- /**
75
- * Deeply subscribes to changes in existing observable objects.
76
- * @param object - The observable object to watch.
77
- * @param subscriber - The handler to call when changes are made to the object.
78
- * @returns A disposable that can be used to unsubscribe from change updates.
79
- * @beta
80
- */
81
- export function watch(object, subscriber) {
82
- const data = {
83
- notifiers: [],
84
- subscriber: isFunction(subscriber) ? { handleChange: subscriber } : subscriber,
85
- };
86
- ArrayObserver.enable();
87
- traverseObject(object, true, watchVisitor, data, new Set());
88
- return {
89
- dispose() {
90
- for (const n of data.notifiers) {
91
- n.unsubscribe(data.subscriber);
92
- }
93
- },
94
- };
95
- }
96
1
  /**
97
2
  * Retrieves the "composed parent" element of a node, ignoring DOM tree boundaries.
98
3
  * When the parent of a node is a shadow-root, it will return the host