@microsoft/fast-element 1.10.0 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/.eslintrc.json +1 -12
  2. package/CHANGELOG.json +396 -1
  3. package/CHANGELOG.md +68 -2
  4. package/README.md +2 -2
  5. package/dist/dts/components/attributes.d.ts +4 -1
  6. package/dist/dts/components/controller.d.ts +12 -11
  7. package/dist/dts/components/fast-definitions.d.ts +8 -2
  8. package/dist/dts/components/fast-element.d.ts +5 -4
  9. package/dist/dts/debug.d.ts +1 -0
  10. package/dist/dts/hooks.d.ts +20 -0
  11. package/dist/dts/index.d.ts +16 -15
  12. package/dist/dts/index.debug.d.ts +2 -0
  13. package/dist/dts/index.rollup.d.ts +2 -0
  14. package/dist/dts/index.rollup.debug.d.ts +3 -0
  15. package/dist/dts/interfaces.d.ts +144 -0
  16. package/dist/dts/observation/arrays.d.ts +207 -0
  17. package/dist/dts/observation/behavior.d.ts +5 -5
  18. package/dist/dts/observation/notifier.d.ts +18 -18
  19. package/dist/dts/observation/observable.d.ts +86 -29
  20. package/dist/dts/observation/splice-strategies.d.ts +13 -0
  21. package/dist/dts/observation/update-queue.d.ts +40 -0
  22. package/dist/dts/platform.d.ts +18 -67
  23. package/dist/dts/polyfills.d.ts +8 -0
  24. package/dist/dts/styles/css-directive.d.ts +43 -5
  25. package/dist/dts/styles/css.d.ts +19 -3
  26. package/dist/dts/styles/element-styles.d.ts +42 -62
  27. package/dist/dts/templating/binding.d.ts +320 -64
  28. package/dist/dts/templating/children.d.ts +18 -15
  29. package/dist/dts/templating/compiler.d.ts +47 -28
  30. package/dist/dts/templating/dom.d.ts +41 -0
  31. package/dist/dts/templating/html-directive.d.ts +179 -43
  32. package/dist/dts/templating/markup.d.ts +48 -0
  33. package/dist/dts/templating/node-observation.d.ts +45 -29
  34. package/dist/dts/templating/ref.d.ts +6 -12
  35. package/dist/dts/templating/repeat.d.ts +72 -14
  36. package/dist/dts/templating/slotted.d.ts +13 -14
  37. package/dist/dts/templating/template.d.ts +78 -23
  38. package/dist/dts/templating/view.d.ts +16 -23
  39. package/dist/dts/utilities.d.ts +40 -0
  40. package/dist/esm/components/attributes.js +25 -24
  41. package/dist/esm/components/controller.js +77 -57
  42. package/dist/esm/components/fast-definitions.js +14 -22
  43. package/dist/esm/debug.js +29 -0
  44. package/dist/esm/hooks.js +32 -0
  45. package/dist/esm/index.debug.js +2 -0
  46. package/dist/esm/index.js +19 -14
  47. package/dist/esm/index.rollup.debug.js +3 -0
  48. package/dist/esm/index.rollup.js +2 -0
  49. package/dist/esm/interfaces.js +8 -1
  50. package/dist/esm/observation/arrays.js +269 -0
  51. package/dist/esm/observation/notifier.js +75 -83
  52. package/dist/esm/observation/observable.js +80 -107
  53. package/dist/esm/observation/{array-change-records.js → splice-strategies.js} +136 -62
  54. package/dist/esm/observation/update-queue.js +67 -0
  55. package/dist/esm/platform.js +36 -42
  56. package/dist/esm/polyfills.js +85 -0
  57. package/dist/esm/styles/css-directive.js +29 -13
  58. package/dist/esm/styles/css.js +27 -40
  59. package/dist/esm/styles/element-styles.js +65 -104
  60. package/dist/esm/templating/binding.js +465 -155
  61. package/dist/esm/templating/children.js +33 -23
  62. package/dist/esm/templating/compiler.js +235 -152
  63. package/dist/esm/templating/dom.js +49 -0
  64. package/dist/esm/templating/html-directive.js +125 -40
  65. package/dist/esm/templating/markup.js +75 -0
  66. package/dist/esm/templating/node-observation.js +50 -45
  67. package/dist/esm/templating/ref.js +7 -16
  68. package/dist/esm/templating/repeat.js +38 -43
  69. package/dist/esm/templating/slotted.js +23 -20
  70. package/dist/esm/templating/template.js +71 -95
  71. package/dist/esm/templating/view.js +44 -43
  72. package/dist/esm/templating/when.js +2 -1
  73. package/dist/esm/utilities.js +139 -0
  74. package/dist/fast-element.api.json +14062 -5235
  75. package/dist/fast-element.d.ts +1434 -579
  76. package/dist/fast-element.debug.js +3824 -0
  77. package/dist/fast-element.debug.min.js +1 -0
  78. package/dist/fast-element.js +3565 -4014
  79. package/dist/fast-element.min.js +1 -1
  80. package/dist/fast-element.untrimmed.d.ts +2908 -0
  81. package/dist/tsdoc-metadata.json +1 -1
  82. package/docs/api-report.md +590 -231
  83. package/docs/fast-element-2-changes.md +15 -0
  84. package/docs/guide/declaring-templates.md +5 -4
  85. package/docs/guide/defining-elements.md +3 -2
  86. package/docs/guide/leveraging-css.md +1 -0
  87. package/docs/guide/next-steps.md +3 -2
  88. package/docs/guide/observables-and-state.md +2 -1
  89. package/docs/guide/using-directives.md +2 -1
  90. package/docs/guide/working-with-shadow-dom.md +1 -0
  91. package/karma.conf.cjs +6 -17
  92. package/package.json +48 -14
  93. package/dist/dts/dom.d.ts +0 -112
  94. package/dist/dts/observation/array-change-records.d.ts +0 -48
  95. package/dist/dts/observation/array-observer.d.ts +0 -9
  96. package/dist/esm/dom.js +0 -207
  97. package/dist/esm/observation/array-observer.js +0 -173
@@ -1,86 +1,33 @@
1
- import { DOM } from "../dom.js";
2
- import { defaultExecutionContext } from "../observation/observable.js";
3
- import { compileTemplate } from "./compiler.js";
4
- import { HTMLView } from "./view.js";
5
- import { HTMLDirective, TargetedHTMLDirective, } from "./html-directive.js";
6
- import { HTMLBindingDirective } from "./binding.js";
1
+ import { isFunction, isString } from "../interfaces.js";
2
+ import { ExecutionContext, } from "../observation/observable.js";
3
+ import { bind, oneTime } from "./binding.js";
4
+ import { Compiler } from "./compiler.js";
5
+ import { Aspect, HTMLDirective, } from "./html-directive.js";
6
+ import { nextId } from "./markup.js";
7
7
  /**
8
8
  * A template capable of creating HTMLView instances or rendering directly to DOM.
9
9
  * @public
10
10
  */
11
- /* eslint-disable-next-line @typescript-eslint/no-unused-vars */
12
11
  export class ViewTemplate {
13
12
  /**
14
13
  * Creates an instance of ViewTemplate.
15
14
  * @param html - The html representing what this template will instantiate, including placeholders for directives.
16
- * @param directives - The directives that will be connected to placeholders in the html.
15
+ * @param factories - The directives that will be connected to placeholders in the html.
17
16
  */
18
- constructor(html, directives) {
19
- this.behaviorCount = 0;
20
- this.hasHostBehaviors = false;
21
- this.fragment = null;
22
- this.targetOffset = 0;
23
- this.viewBehaviorFactories = null;
24
- this.hostBehaviorFactories = null;
17
+ constructor(html, factories) {
18
+ this.result = null;
25
19
  this.html = html;
26
- this.directives = directives;
20
+ this.factories = factories;
27
21
  }
28
22
  /**
29
23
  * Creates an HTMLView instance based on this template definition.
30
24
  * @param hostBindingTarget - The element that host behaviors will be bound to.
31
25
  */
32
26
  create(hostBindingTarget) {
33
- if (this.fragment === null) {
34
- let template;
35
- const html = this.html;
36
- if (typeof html === "string") {
37
- template = document.createElement("template");
38
- template.innerHTML = DOM.createHTML(html);
39
- const fec = template.content.firstElementChild;
40
- if (fec !== null && fec.tagName === "TEMPLATE") {
41
- template = fec;
42
- }
43
- }
44
- else {
45
- template = html;
46
- }
47
- const result = compileTemplate(template, this.directives);
48
- this.fragment = result.fragment;
49
- this.viewBehaviorFactories = result.viewBehaviorFactories;
50
- this.hostBehaviorFactories = result.hostBehaviorFactories;
51
- this.targetOffset = result.targetOffset;
52
- this.behaviorCount =
53
- this.viewBehaviorFactories.length + this.hostBehaviorFactories.length;
54
- this.hasHostBehaviors = this.hostBehaviorFactories.length > 0;
55
- }
56
- const fragment = this.fragment.cloneNode(true);
57
- const viewFactories = this.viewBehaviorFactories;
58
- const behaviors = new Array(this.behaviorCount);
59
- const walker = DOM.createTemplateWalker(fragment);
60
- let behaviorIndex = 0;
61
- let targetIndex = this.targetOffset;
62
- let node = walker.nextNode();
63
- for (let ii = viewFactories.length; behaviorIndex < ii; ++behaviorIndex) {
64
- const factory = viewFactories[behaviorIndex];
65
- const factoryIndex = factory.targetIndex;
66
- while (node !== null) {
67
- if (targetIndex === factoryIndex) {
68
- behaviors[behaviorIndex] = factory.createBehavior(node);
69
- break;
70
- }
71
- else {
72
- node = walker.nextNode();
73
- targetIndex++;
74
- }
75
- }
27
+ if (this.result === null) {
28
+ this.result = Compiler.compile(this.html, this.factories);
76
29
  }
77
- if (this.hasHostBehaviors) {
78
- const hostFactories = this.hostBehaviorFactories;
79
- for (let i = 0, ii = hostFactories.length; i < ii; ++i, ++behaviorIndex) {
80
- behaviors[behaviorIndex] = hostFactories[i].createBehavior(hostBindingTarget);
81
- }
82
- }
83
- return new HTMLView(fragment, behaviors);
30
+ return this.result.createView(hostBindingTarget);
84
31
  }
85
32
  /**
86
33
  * Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
@@ -89,15 +36,9 @@ export class ViewTemplate {
89
36
  * @param hostBindingTarget - An HTML element to target the host bindings at if different from the
90
37
  * host that the template is being attached to.
91
38
  */
92
- render(source, host, hostBindingTarget) {
93
- if (typeof host === "string") {
94
- host = document.getElementById(host);
95
- }
96
- if (hostBindingTarget === void 0) {
97
- hostBindingTarget = host;
98
- }
99
- const view = this.create(hostBindingTarget);
100
- view.bind(source, defaultExecutionContext);
39
+ render(source, host, hostBindingTarget, context) {
40
+ const view = this.create(hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : host);
41
+ view.bind(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
101
42
  view.appendTo(host);
102
43
  return view;
103
44
  }
@@ -106,8 +47,15 @@ export class ViewTemplate {
106
47
  const lastAttributeNameRegex =
107
48
  /* eslint-disable-next-line no-control-regex */
108
49
  /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
50
+ function createAspectedHTML(value, prevString, add) {
51
+ const match = lastAttributeNameRegex.exec(prevString);
52
+ if (match !== null) {
53
+ Aspect.assign(value, match[2]);
54
+ }
55
+ return value.createHTML(add);
56
+ }
109
57
  /**
110
- * Transforms a template literal string into a renderable ViewTemplate.
58
+ * Transforms a template literal string into a ViewTemplate.
111
59
  * @param strings - The string fragments that are interpolated with the values.
112
60
  * @param values - The values that are interpolated with the string fragments.
113
61
  * @remarks
@@ -116,36 +64,64 @@ const lastAttributeNameRegex =
116
64
  * @public
117
65
  */
118
66
  export function html(strings, ...values) {
119
- const directives = [];
120
67
  let html = "";
68
+ const factories = Object.create(null);
69
+ const add = (factory) => {
70
+ var _a;
71
+ const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
72
+ factories[id] = factory;
73
+ return id;
74
+ };
121
75
  for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
122
76
  const currentString = strings[i];
123
- let value = values[i];
77
+ const currentValue = values[i];
78
+ let definition;
124
79
  html += currentString;
125
- if (value instanceof ViewTemplate) {
126
- const template = value;
127
- value = () => template;
80
+ if (isFunction(currentValue)) {
81
+ html += createAspectedHTML(bind(currentValue), currentString, add);
128
82
  }
129
- if (typeof value === "function") {
130
- value = new HTMLBindingDirective(value);
131
- }
132
- if (value instanceof TargetedHTMLDirective) {
83
+ else if (isString(currentValue)) {
133
84
  const match = lastAttributeNameRegex.exec(currentString);
134
85
  if (match !== null) {
135
- value.targetName = match[2];
86
+ const directive = bind(() => currentValue, oneTime);
87
+ Aspect.assign(directive, match[2]);
88
+ html += directive.createHTML(add);
89
+ }
90
+ else {
91
+ html += currentValue;
136
92
  }
137
93
  }
138
- if (value instanceof HTMLDirective) {
139
- // Since not all values are directives, we can't use i
140
- // as the index for the placeholder. Instead, we need to
141
- // use directives.length to get the next index.
142
- html += value.createPlaceholder(directives.length);
143
- directives.push(value);
94
+ else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
95
+ html += createAspectedHTML(bind(() => currentValue, oneTime), currentString, add);
144
96
  }
145
97
  else {
146
- html += value;
98
+ if (definition.aspected) {
99
+ html += createAspectedHTML(currentValue, currentString, add);
100
+ }
101
+ else {
102
+ html += currentValue.createHTML(add);
103
+ }
147
104
  }
148
105
  }
149
- html += strings[strings.length - 1];
150
- return new ViewTemplate(html, directives);
106
+ return new ViewTemplate(html + strings[strings.length - 1], factories);
151
107
  }
108
+ /**
109
+ * Transforms a template literal string into a ChildViewTemplate.
110
+ * @param strings - The string fragments that are interpolated with the values.
111
+ * @param values - The values that are interpolated with the string fragments.
112
+ * @remarks
113
+ * The html helper supports interpolation of strings, numbers, binding expressions,
114
+ * other template instances, and Directive instances.
115
+ * @public
116
+ */
117
+ export const child = html;
118
+ /**
119
+ * Transforms a template literal string into an ItemViewTemplate.
120
+ * @param strings - The string fragments that are interpolated with the values.
121
+ * @param values - The values that are interpolated with the string fragments.
122
+ * @remarks
123
+ * The html helper supports interpolation of strings, numbers, binding expressions,
124
+ * other template instances, and Directive instances.
125
+ * @public
126
+ */
127
+ export const item = html;
@@ -1,6 +1,14 @@
1
- // A singleton Range instance used to efficiently remove ranges of DOM nodes.
2
- // See the implementation of HTMLView below for further details.
3
- const range = document.createRange();
1
+ function removeNodeSequence(firstNode, lastNode) {
2
+ const parent = firstNode.parentNode;
3
+ let current = firstNode;
4
+ let next;
5
+ while (current !== lastNode) {
6
+ next = current.nextSibling;
7
+ parent.removeChild(current);
8
+ current = next;
9
+ }
10
+ parent.removeChild(lastNode);
11
+ }
4
12
  /**
5
13
  * The standard View implementation, which also implements ElementView and SyntheticView.
6
14
  * @public
@@ -11,9 +19,11 @@ export class HTMLView {
11
19
  * @param fragment - The html fragment that contains the nodes for this view.
12
20
  * @param behaviors - The behaviors to be applied to this view.
13
21
  */
14
- constructor(fragment, behaviors) {
22
+ constructor(fragment, factories, targets) {
15
23
  this.fragment = fragment;
16
- this.behaviors = behaviors;
24
+ this.factories = factories;
25
+ this.targets = targets;
26
+ this.behaviors = null;
17
27
  /**
18
28
  * The data that the view is bound to.
19
29
  */
@@ -74,21 +84,8 @@ export class HTMLView {
74
84
  * Once a view has been disposed, it cannot be inserted or bound again.
75
85
  */
76
86
  dispose() {
77
- const parent = this.firstChild.parentNode;
78
- const end = this.lastChild;
79
- let current = this.firstChild;
80
- let next;
81
- while (current !== end) {
82
- next = current.nextSibling;
83
- parent.removeChild(current);
84
- current = next;
85
- }
86
- parent.removeChild(end);
87
- const behaviors = this.behaviors;
88
- const oldSource = this.source;
89
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
90
- behaviors[i].unbind(oldSource);
91
- }
87
+ removeNodeSequence(this.firstChild, this.lastChild);
88
+ this.unbind();
92
89
  }
93
90
  /**
94
91
  * Binds a view's behaviors to its binding source.
@@ -96,25 +93,33 @@ export class HTMLView {
96
93
  * @param context - The execution context to run the behaviors within.
97
94
  */
98
95
  bind(source, context) {
99
- const behaviors = this.behaviors;
100
- if (this.source === source) {
96
+ let behaviors = this.behaviors;
97
+ const oldSource = this.source;
98
+ if (oldSource === source) {
101
99
  return;
102
100
  }
103
- else if (this.source !== null) {
104
- const oldSource = this.source;
105
- this.source = source;
106
- this.context = context;
101
+ this.source = source;
102
+ this.context = context;
103
+ const targets = this.targets;
104
+ if (oldSource !== null) {
107
105
  for (let i = 0, ii = behaviors.length; i < ii; ++i) {
108
106
  const current = behaviors[i];
109
- current.unbind(oldSource);
110
- current.bind(source, context);
107
+ current.unbind(oldSource, context, targets);
108
+ current.bind(source, context, targets);
109
+ }
110
+ }
111
+ else if (behaviors === null) {
112
+ this.behaviors = behaviors = new Array(this.factories.length);
113
+ const factories = this.factories;
114
+ for (let i = 0, ii = factories.length; i < ii; ++i) {
115
+ const behavior = factories[i].createBehavior(targets);
116
+ behavior.bind(source, context, targets);
117
+ behaviors[i] = behavior;
111
118
  }
112
119
  }
113
120
  else {
114
- this.source = source;
115
- this.context = context;
116
121
  for (let i = 0, ii = behaviors.length; i < ii; ++i) {
117
- behaviors[i].bind(source, context);
122
+ behaviors[i].bind(source, context, targets);
118
123
  }
119
124
  }
120
125
  }
@@ -122,15 +127,18 @@ export class HTMLView {
122
127
  * Unbinds a view's behaviors from its binding source.
123
128
  */
124
129
  unbind() {
125
- if (this.source === null) {
130
+ const oldSource = this.source;
131
+ if (oldSource === null) {
126
132
  return;
127
133
  }
134
+ const targets = this.targets;
135
+ const context = this.context;
128
136
  const behaviors = this.behaviors;
129
- const oldSource = this.source;
130
137
  for (let i = 0, ii = behaviors.length; i < ii; ++i) {
131
- behaviors[i].unbind(oldSource);
138
+ behaviors[i].unbind(oldSource, context, targets);
132
139
  }
133
140
  this.source = null;
141
+ this.context = null;
134
142
  }
135
143
  /**
136
144
  * Efficiently disposes of a contiguous range of synthetic view instances.
@@ -140,16 +148,9 @@ export class HTMLView {
140
148
  if (views.length === 0) {
141
149
  return;
142
150
  }
143
- range.setStartBefore(views[0].firstChild);
144
- range.setEndAfter(views[views.length - 1].lastChild);
145
- range.deleteContents();
151
+ removeNodeSequence(views[0].firstChild, views[views.length - 1].lastChild);
146
152
  for (let i = 0, ii = views.length; i < ii; ++i) {
147
- const view = views[i];
148
- const behaviors = view.behaviors;
149
- const oldSource = view.source;
150
- for (let j = 0, jj = behaviors.length; j < jj; ++j) {
151
- behaviors[j].unbind(oldSource);
152
- }
153
+ views[i].unbind();
153
154
  }
154
155
  }
155
156
  }
@@ -1,3 +1,4 @@
1
+ import { isFunction } from "../interfaces.js";
1
2
  /**
2
3
  * A directive that enables basic conditional rendering in a template.
3
4
  * @param binding - The condition to test for rendering.
@@ -6,7 +7,7 @@
6
7
  * @public
7
8
  */
8
9
  export function when(binding, templateOrTemplateBinding) {
9
- const getTemplate = typeof templateOrTemplateBinding === "function"
10
+ const getTemplate = isFunction(templateOrTemplateBinding)
10
11
  ? templateOrTemplateBinding
11
12
  : () => templateOrTemplateBinding;
12
13
  return (source, context) => binding(source, context) ? getTemplate(source, context) : null;
@@ -0,0 +1,139 @@
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
+ /**
97
+ * Retrieves the "composed parent" element of a node, ignoring DOM tree boundaries.
98
+ * When the parent of a node is a shadow-root, it will return the host
99
+ * element of the shadow root. Otherwise it will return the parent node or null if
100
+ * no parent node exists.
101
+ * @param element - The element for which to retrieve the composed parent
102
+ *
103
+ * @public
104
+ */
105
+ export function composedParent(element) {
106
+ const parentNode = element.parentElement;
107
+ if (parentNode) {
108
+ return parentNode;
109
+ }
110
+ else {
111
+ const rootNode = element.getRootNode();
112
+ if (rootNode.host instanceof HTMLElement) {
113
+ // this is shadow-root
114
+ return rootNode.host;
115
+ }
116
+ }
117
+ return null;
118
+ }
119
+ /**
120
+ * Determines if the reference element contains the test element in a "composed" DOM tree that
121
+ * ignores shadow DOM boundaries.
122
+ *
123
+ * Returns true of the test element is a descendent of the reference, or exist in
124
+ * a shadow DOM that is a logical descendent of the reference. Otherwise returns false.
125
+ * @param reference - The element to test for containment against.
126
+ * @param test - The element being tested for containment.
127
+ *
128
+ * @public
129
+ */
130
+ export function composedContains(reference, test) {
131
+ let current = test;
132
+ while (current !== null) {
133
+ if (current === reference) {
134
+ return true;
135
+ }
136
+ current = composedParent(current);
137
+ }
138
+ return false;
139
+ }