@microsoft/fast-element 2.0.0-beta.2 → 2.0.0-beta.21

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 (115) hide show
  1. package/CHANGELOG.json +509 -0
  2. package/CHANGELOG.md +189 -1
  3. package/dist/dts/components/attributes.d.ts +15 -0
  4. package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +74 -28
  5. package/dist/dts/components/fast-definitions.d.ts +41 -9
  6. package/dist/dts/components/fast-element.d.ts +14 -26
  7. package/dist/dts/components/hydration.d.ts +14 -0
  8. package/dist/{esm/observation/behavior.js → dts/components/install-hydration.d.ts} +0 -0
  9. package/dist/dts/context.d.ts +7 -7
  10. package/dist/dts/di/di.d.ts +894 -0
  11. package/dist/dts/dom-policy.d.ts +83 -0
  12. package/dist/dts/dom.d.ts +100 -0
  13. package/dist/dts/index.d.ts +5 -4
  14. package/dist/dts/index.rollup.d.ts +0 -1
  15. package/dist/dts/index.rollup.debug.d.ts +0 -1
  16. package/dist/dts/interfaces.d.ts +62 -80
  17. package/dist/dts/metadata.d.ts +5 -5
  18. package/dist/dts/observation/observable.d.ts +99 -54
  19. package/dist/dts/pending-task.d.ts +32 -0
  20. package/dist/dts/platform.d.ts +8 -1
  21. package/dist/dts/polyfills.d.ts +0 -8
  22. package/dist/dts/state/exports.d.ts +3 -0
  23. package/dist/dts/state/reactive.d.ts +8 -0
  24. package/dist/dts/state/state.d.ts +141 -0
  25. package/dist/dts/state/visitor.d.ts +6 -0
  26. package/dist/dts/state/watch.d.ts +10 -0
  27. package/dist/dts/styles/css-directive.d.ts +2 -2
  28. package/dist/dts/styles/css.d.ts +0 -5
  29. package/dist/dts/styles/element-styles.d.ts +10 -17
  30. package/dist/dts/styles/host.d.ts +68 -0
  31. package/dist/dts/styles/style-strategy.d.ts +42 -0
  32. package/dist/dts/templating/binding-signal.d.ts +12 -27
  33. package/dist/dts/templating/binding-two-way.d.ts +22 -37
  34. package/dist/dts/templating/binding.d.ts +76 -208
  35. package/dist/dts/templating/children.d.ts +1 -1
  36. package/dist/dts/templating/compiler.d.ts +11 -13
  37. package/dist/dts/templating/html-directive.d.ts +91 -97
  38. package/dist/dts/templating/node-observation.d.ts +15 -6
  39. package/dist/dts/templating/ref.d.ts +7 -11
  40. package/dist/dts/templating/render.d.ts +296 -0
  41. package/dist/dts/templating/repeat.d.ts +23 -34
  42. package/dist/dts/templating/slotted.d.ts +1 -1
  43. package/dist/dts/templating/template.d.ts +92 -14
  44. package/dist/dts/templating/view.d.ts +81 -11
  45. package/dist/dts/templating/when.d.ts +3 -3
  46. package/dist/dts/testing/exports.d.ts +3 -0
  47. package/dist/dts/testing/fakes.d.ts +14 -0
  48. package/dist/dts/testing/fixture.d.ts +84 -0
  49. package/dist/dts/testing/timeout.d.ts +7 -0
  50. package/dist/dts/utilities.d.ts +55 -19
  51. package/dist/esm/components/attributes.js +28 -5
  52. package/dist/esm/components/{controller.js → element-controller.js} +238 -137
  53. package/dist/esm/components/fast-definitions.js +38 -30
  54. package/dist/esm/components/fast-element.js +27 -16
  55. package/dist/esm/components/hydration.js +35 -0
  56. package/dist/esm/components/install-hydration.js +2 -0
  57. package/dist/esm/context.js +7 -3
  58. package/dist/esm/debug.js +41 -5
  59. package/dist/esm/di/di.js +1430 -0
  60. package/dist/esm/dom-policy.js +345 -0
  61. package/dist/esm/dom.js +101 -0
  62. package/dist/esm/index.js +4 -2
  63. package/dist/esm/index.rollup.debug.js +3 -1
  64. package/dist/esm/index.rollup.js +3 -1
  65. package/dist/esm/interfaces.js +52 -0
  66. package/dist/esm/metadata.js +9 -8
  67. package/dist/esm/observation/arrays.js +303 -2
  68. package/dist/esm/observation/observable.js +88 -142
  69. package/dist/esm/observation/update-queue.js +2 -2
  70. package/dist/esm/pending-task.js +28 -0
  71. package/dist/esm/platform.js +28 -3
  72. package/dist/esm/polyfills.js +3 -61
  73. package/dist/esm/state/exports.js +3 -0
  74. package/dist/esm/state/reactive.js +34 -0
  75. package/dist/esm/state/state.js +148 -0
  76. package/dist/esm/state/visitor.js +28 -0
  77. package/dist/esm/state/watch.js +36 -0
  78. package/dist/esm/styles/css.js +4 -9
  79. package/dist/esm/styles/element-styles.js +14 -33
  80. package/dist/esm/styles/host.js +1 -0
  81. package/dist/esm/styles/style-strategy.js +1 -0
  82. package/dist/esm/templating/binding-signal.js +67 -62
  83. package/dist/esm/templating/binding-two-way.js +72 -39
  84. package/dist/esm/templating/binding.js +142 -286
  85. package/dist/esm/templating/children.js +8 -4
  86. package/dist/esm/templating/compiler.js +59 -43
  87. package/dist/esm/templating/html-directive.js +56 -75
  88. package/dist/esm/templating/node-observation.js +20 -13
  89. package/dist/esm/templating/ref.js +4 -12
  90. package/dist/esm/templating/render.js +402 -0
  91. package/dist/esm/templating/repeat.js +88 -75
  92. package/dist/esm/templating/template.js +132 -60
  93. package/dist/esm/templating/view.js +113 -29
  94. package/dist/esm/templating/when.js +5 -4
  95. package/dist/esm/testing/exports.js +3 -0
  96. package/dist/esm/testing/fakes.js +107 -0
  97. package/dist/esm/testing/fixture.js +86 -0
  98. package/dist/esm/testing/timeout.js +24 -0
  99. package/dist/esm/utilities.js +97 -96
  100. package/dist/fast-element.api.json +9741 -8201
  101. package/dist/fast-element.d.ts +889 -646
  102. package/dist/fast-element.debug.js +2001 -1167
  103. package/dist/fast-element.debug.min.js +1 -1
  104. package/dist/fast-element.js +1907 -1109
  105. package/dist/fast-element.min.js +1 -1
  106. package/dist/fast-element.untrimmed.d.ts +913 -703
  107. package/docs/api-report.md +331 -258
  108. package/package.json +38 -16
  109. package/dist/dts/hooks.d.ts +0 -20
  110. package/dist/dts/observation/behavior.d.ts +0 -19
  111. package/dist/dts/observation/splice-strategies.d.ts +0 -13
  112. package/dist/dts/templating/dom.d.ts +0 -41
  113. package/dist/esm/hooks.js +0 -32
  114. package/dist/esm/observation/splice-strategies.js +0 -400
  115. package/dist/esm/templating/dom.js +0 -49
@@ -1,4 +1,4 @@
1
- import { isString } from "../interfaces.js";
1
+ import { isString, noop } from "../interfaces.js";
2
2
  import { HTMLDirective } from "./html-directive.js";
3
3
  import { NodeObservationDirective } from "./node-observation.js";
4
4
  /**
@@ -24,9 +24,13 @@ export class ChildrenDirective extends NodeObservationDirective {
24
24
  * @param target - The target to observe.
25
25
  */
26
26
  observe(target) {
27
- var _a;
28
- const observer = (_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = new MutationObserver(this.handleEvent));
29
- observer.target = target;
27
+ let observer = target[this.observerProperty];
28
+ if (!observer) {
29
+ observer = new MutationObserver(this.handleEvent);
30
+ observer.toJSON = noop;
31
+ observer.target = target;
32
+ target[this.observerProperty] = observer;
33
+ }
30
34
  observer.observe(target, this.options);
31
35
  }
32
36
  /**
@@ -1,8 +1,9 @@
1
- import { isString } from "../interfaces.js";
1
+ import { isFunction, isString } from "../interfaces.js";
2
2
  import { FAST } from "../platform.js";
3
- import { Parser } from "./markup.js";
4
- import { bind, oneTime } from "./binding.js";
5
- import { Aspect } from "./html-directive.js";
3
+ import { DOM } from "../dom.js";
4
+ import { nextId, Parser } from "./markup.js";
5
+ import { HTMLBindingDirective, oneTime } from "./binding.js";
6
+ import { HTMLDirective, } from "./html-directive.js";
6
7
  import { HTMLView } from "./view.js";
7
8
  const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
8
9
  const descriptorCache = {};
@@ -11,21 +12,42 @@ const next = {
11
12
  index: 0,
12
13
  node: null,
13
14
  };
15
+ function tryWarn(name) {
16
+ if (!name.startsWith("fast-")) {
17
+ FAST.warn(1204 /* Message.hostBindingWithoutHost */, { name });
18
+ }
19
+ }
20
+ const warningHost = new Proxy(document.createElement("div"), {
21
+ get(target, property) {
22
+ tryWarn(property);
23
+ const value = Reflect.get(target, property);
24
+ return isFunction(value) ? value.bind(target) : value;
25
+ },
26
+ set(target, property, value) {
27
+ tryWarn(property);
28
+ return Reflect.set(target, property, value);
29
+ },
30
+ });
14
31
  class CompilationContext {
15
- constructor(fragment, directives) {
32
+ constructor(fragment, directives, policy) {
16
33
  this.fragment = fragment;
17
34
  this.directives = directives;
35
+ this.policy = policy;
18
36
  this.proto = null;
19
37
  this.nodeIds = new Set();
20
38
  this.descriptors = {};
21
39
  this.factories = [];
22
40
  }
23
- addFactory(factory, parentId, nodeId, targetIndex) {
41
+ addFactory(factory, parentId, nodeId, targetIndex, tagName) {
42
+ var _a, _b;
24
43
  if (!this.nodeIds.has(nodeId)) {
25
44
  this.nodeIds.add(nodeId);
26
45
  this.addTargetDescriptor(parentId, nodeId, targetIndex);
27
46
  }
28
- factory.nodeId = nodeId;
47
+ factory.id = (_a = factory.id) !== null && _a !== void 0 ? _a : nextId();
48
+ factory.targetNodeId = nodeId;
49
+ factory.targetTagName = tagName;
50
+ factory.policy = (_b = factory.policy) !== null && _b !== void 0 ? _b : this.policy;
29
51
  this.factories.push(factory);
30
52
  }
31
53
  freeze() {
@@ -61,7 +83,7 @@ class CompilationContext {
61
83
  const fragment = this.fragment.cloneNode(true);
62
84
  const targets = Object.create(this.proto);
63
85
  targets.r = fragment;
64
- targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : fragment;
86
+ targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
65
87
  for (const id of this.nodeIds) {
66
88
  targets[id]; // trigger locator
67
89
  }
@@ -78,19 +100,19 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
78
100
  let result = null;
79
101
  if (parseResult === null) {
80
102
  if (includeBasicValues) {
81
- result = bind(() => attrValue, oneTime);
82
- Aspect.assign(result, attr.name);
103
+ result = new HTMLBindingDirective(oneTime(() => attrValue, context.policy));
104
+ HTMLDirective.assignAspect(result, attr.name);
83
105
  }
84
106
  }
85
107
  else {
86
108
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
87
- result = Compiler.aggregate(parseResult);
109
+ result = Compiler.aggregate(parseResult, context.policy);
88
110
  }
89
111
  if (result !== null) {
90
112
  node.removeAttributeNode(attr);
91
113
  i--;
92
114
  ii--;
93
- context.addFactory(result, parentId, nodeId, nodeIndex);
115
+ context.addFactory(result, parentId, nodeId, nodeIndex, node.tagName);
94
116
  }
95
117
  }
96
118
  }
@@ -115,7 +137,8 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
115
137
  }
116
138
  else {
117
139
  currentNode.textContent = " ";
118
- context.addFactory(currentPart, parentId, nodeId, nodeIndex);
140
+ HTMLDirective.assignAspect(currentPart);
141
+ context.addFactory(currentPart, parentId, nodeId, nodeIndex, null);
119
142
  }
120
143
  lastNode = currentNode;
121
144
  }
@@ -147,7 +170,7 @@ function compileNode(context, parentId, node, nodeIndex) {
147
170
  if (parts !== null) {
148
171
  context.addFactory(
149
172
  /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
150
- Compiler.aggregate(parts), parentId, nodeId, nodeIndex);
173
+ Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
151
174
  }
152
175
  break;
153
176
  }
@@ -161,45 +184,28 @@ function isMarker(node, directives) {
161
184
  Parser.parse(node.data, directives) !== null);
162
185
  }
163
186
  const templateTag = "TEMPLATE";
164
- const policyOptions = { createHTML: html => html };
165
- let htmlPolicy = globalThis.trustedTypes
166
- ? globalThis.trustedTypes.createPolicy("fast-html", policyOptions)
167
- : policyOptions;
168
- const fastHTMLPolicy = htmlPolicy;
169
187
  /**
170
188
  * Common APIs related to compilation.
171
189
  * @public
172
190
  */
173
191
  export const Compiler = {
174
- /**
175
- * Sets the HTML trusted types policy used by the compiler.
176
- * @param policy - The policy to set for HTML.
177
- * @remarks
178
- * This API can only be called once, for security reasons. It should be
179
- * called by the application developer at the start of their program.
180
- */
181
- setHTMLPolicy(policy) {
182
- if (htmlPolicy !== fastHTMLPolicy) {
183
- throw FAST.error(1201 /* Message.onlySetHTMLPolicyOnce */);
184
- }
185
- htmlPolicy = policy;
186
- },
187
192
  /**
188
193
  * Compiles a template and associated directives into a compilation
189
194
  * result which can be used to create views.
190
195
  * @param html - The html string or template element to compile.
191
- * @param directives - The directives referenced by the template.
196
+ * @param factories - The behavior factories referenced by the template.
197
+ * @param policy - The security policy to compile the html with.
192
198
  * @remarks
193
199
  * The template that is provided for compilation is altered in-place
194
200
  * and cannot be compiled again. If the original template must be preserved,
195
201
  * it is recommended that you clone the original and pass the clone to this API.
196
202
  * @public
197
203
  */
198
- compile(html, directives) {
204
+ compile(html, factories, policy = DOM.policy) {
199
205
  let template;
200
206
  if (isString(html)) {
201
207
  template = document.createElement(templateTag);
202
- template.innerHTML = htmlPolicy.createHTML(html);
208
+ template.innerHTML = policy.createHTML(html);
203
209
  const fec = template.content.firstElementChild;
204
210
  if (fec !== null && fec.tagName === templateTag) {
205
211
  template = fec;
@@ -210,18 +216,18 @@ export const Compiler = {
210
216
  }
211
217
  // https://bugs.chromium.org/p/chromium/issues/detail?id=1111864
212
218
  const fragment = document.adoptNode(template.content);
213
- const context = new CompilationContext(fragment, directives);
219
+ const context = new CompilationContext(fragment, factories, policy);
214
220
  compileAttributes(context, "", template, /* host */ "h", 0, true);
215
221
  if (
216
222
  // If the first node in a fragment is a marker, that means it's an unstable first node,
217
223
  // because something like a when, repeat, etc. could add nodes before the marker.
218
224
  // To mitigate this, we insert a stable first node. However, if we insert a node,
219
225
  // that will alter the result of the TreeWalker. So, we also need to offset the target index.
220
- isMarker(fragment.firstChild, directives) ||
226
+ isMarker(fragment.firstChild, factories) ||
221
227
  // Or if there is only one node and a directive, it means the template's content
222
228
  // is *only* the directive. In that case, HTMLView.dispose() misses any nodes inserted by
223
229
  // the directive. Inserting a new node ensures proper disposal of nodes added by the directive.
224
- (fragment.childNodes.length === 1 && Object.keys(directives).length > 0)) {
230
+ (fragment.childNodes.length === 1 && Object.keys(factories).length > 0)) {
225
231
  fragment.insertBefore(document.createComment(""), fragment.firstChild);
226
232
  }
227
233
  compileChildren(context, fragment, /* root */ "r");
@@ -240,30 +246,40 @@ export const Compiler = {
240
246
  * Aggregates an array of strings and directives into a single directive.
241
247
  * @param parts - A heterogeneous array of static strings interspersed with
242
248
  * directives.
249
+ * @param policy - The security policy to use with the aggregated bindings.
243
250
  * @returns A single inline directive that aggregates the behavior of all the parts.
244
251
  */
245
- aggregate(parts) {
252
+ aggregate(parts, policy = DOM.policy) {
246
253
  if (parts.length === 1) {
247
254
  return parts[0];
248
255
  }
249
256
  let sourceAspect;
257
+ let binding;
258
+ let isVolatile = false;
259
+ let bindingPolicy = void 0;
250
260
  const partCount = parts.length;
251
261
  const finalParts = parts.map((x) => {
252
262
  if (isString(x)) {
253
263
  return () => x;
254
264
  }
255
265
  sourceAspect = x.sourceAspect || sourceAspect;
256
- return x.binding;
266
+ binding = x.dataBinding || binding;
267
+ isVolatile = isVolatile || x.dataBinding.isVolatile;
268
+ bindingPolicy = bindingPolicy || x.dataBinding.policy;
269
+ return x.dataBinding.evaluate;
257
270
  });
258
- const binding = (scope, context) => {
271
+ const expression = (scope, context) => {
259
272
  let output = "";
260
273
  for (let i = 0; i < partCount; ++i) {
261
274
  output += finalParts[i](scope, context);
262
275
  }
263
276
  return output;
264
277
  };
265
- const directive = bind(binding);
266
- Aspect.assign(directive, sourceAspect);
278
+ binding.evaluate = expression;
279
+ binding.isVolatile = isVolatile;
280
+ binding.policy = bindingPolicy !== null && bindingPolicy !== void 0 ? bindingPolicy : policy;
281
+ const directive = new HTMLBindingDirective(binding);
282
+ HTMLDirective.assignAspect(directive, sourceAspect);
267
283
  return directive;
268
284
  },
269
285
  };
@@ -1,3 +1,5 @@
1
+ import { DOMAspect } from "../dom.js";
2
+ import { noop } from "../interfaces.js";
1
3
  import { createTypeRegistry } from "../platform.js";
2
4
  import { Markup } from "./markup.js";
3
5
  const registry = createTypeRegistry();
@@ -27,97 +29,71 @@ export const HTMLDirective = Object.freeze({
27
29
  registry.register(options);
28
30
  return type;
29
31
  },
30
- });
31
- /**
32
- * Decorator: Defines an HTMLDirective.
33
- * @param options - Provides options that specify the directive's application.
34
- * @public
35
- */
36
- export function htmlDirective(options) {
37
- /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
38
- return function (type) {
39
- HTMLDirective.define(type, options);
40
- };
41
- }
42
- /**
43
- * The type of HTML aspect to target.
44
- * @public
45
- */
46
- export const Aspect = Object.freeze({
47
- /**
48
- * Not aspected.
49
- */
50
- none: 0,
51
- /**
52
- * An attribute.
53
- */
54
- attribute: 1,
55
- /**
56
- * A boolean attribute.
57
- */
58
- booleanAttribute: 2,
59
- /**
60
- * A property.
61
- */
62
- property: 3,
63
- /**
64
- * Content
65
- */
66
- content: 4,
67
- /**
68
- * A token list.
69
- */
70
- tokenList: 5,
71
- /**
72
- * An event.
73
- */
74
- event: 6,
75
32
  /**
76
33
  *
77
34
  * @param directive - The directive to assign the aspect to.
78
35
  * @param value - The value to base the aspect determination on.
36
+ * @remarks
37
+ * If a falsy value is provided, then the content aspect will be assigned.
79
38
  */
80
- assign(directive, value) {
81
- directive.sourceAspect = value;
39
+ assignAspect(directive, value) {
82
40
  if (!value) {
41
+ directive.aspectType = DOMAspect.content;
83
42
  return;
84
43
  }
44
+ directive.sourceAspect = value;
85
45
  switch (value[0]) {
86
46
  case ":":
87
47
  directive.targetAspect = value.substring(1);
88
- switch (directive.targetAspect) {
89
- case "innerHTML":
90
- directive.aspectType = Aspect.property;
91
- break;
92
- case "classList":
93
- directive.aspectType = Aspect.tokenList;
94
- break;
95
- default:
96
- directive.aspectType = Aspect.property;
97
- break;
98
- }
48
+ directive.aspectType =
49
+ directive.targetAspect === "classList"
50
+ ? DOMAspect.tokenList
51
+ : DOMAspect.property;
99
52
  break;
100
53
  case "?":
101
54
  directive.targetAspect = value.substring(1);
102
- directive.aspectType = Aspect.booleanAttribute;
55
+ directive.aspectType = DOMAspect.booleanAttribute;
103
56
  break;
104
57
  case "@":
105
58
  directive.targetAspect = value.substring(1);
106
- directive.aspectType = Aspect.event;
59
+ directive.aspectType = DOMAspect.event;
107
60
  break;
108
61
  default:
109
- if (value === "class") {
110
- directive.targetAspect = "className";
111
- directive.aspectType = Aspect.property;
112
- }
113
- else {
114
- directive.targetAspect = value;
115
- directive.aspectType = Aspect.attribute;
116
- }
62
+ directive.targetAspect = value;
63
+ directive.aspectType = DOMAspect.attribute;
117
64
  break;
118
65
  }
119
66
  },
120
67
  });
68
+ /**
69
+ * Decorator: Defines an HTMLDirective.
70
+ * @param options - Provides options that specify the directive's application.
71
+ * @public
72
+ */
73
+ export function htmlDirective(options) {
74
+ /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
75
+ return function (type) {
76
+ HTMLDirective.define(type, options);
77
+ };
78
+ }
79
+ /**
80
+ * Captures a binding expression along with related information and capabilities.
81
+ *
82
+ * @public
83
+ */
84
+ export class Binding {
85
+ /**
86
+ * Creates a binding.
87
+ * @param evaluate - Evaluates the binding.
88
+ * @param policy - The security policy to associate with this binding.
89
+ * @param isVolatile - Indicates whether the binding is volatile.
90
+ */
91
+ constructor(evaluate, policy, isVolatile = false) {
92
+ this.evaluate = evaluate;
93
+ this.policy = policy;
94
+ this.isVolatile = isVolatile;
95
+ }
96
+ }
121
97
  /**
122
98
  * A base class used for attribute directives that don't need internal state.
123
99
  * @public
@@ -129,13 +105,11 @@ export class StatelessAttachedAttributeDirective {
129
105
  */
130
106
  constructor(options) {
131
107
  this.options = options;
132
- }
133
- /**
134
- * Creates a behavior.
135
- * @param targets - The targets available for behaviors to be attached to.
136
- */
137
- createBehavior(targets) {
138
- return this;
108
+ /**
109
+ * Opts out of JSON stringification.
110
+ * @internal
111
+ */
112
+ this.toJSON = noop;
139
113
  }
140
114
  /**
141
115
  * Creates a placeholder string based on the directive's index within the template.
@@ -146,4 +120,11 @@ export class StatelessAttachedAttributeDirective {
146
120
  createHTML(add) {
147
121
  return Markup.attribute(add(this));
148
122
  }
123
+ /**
124
+ * Creates a behavior.
125
+ * @param targets - The targets available for behaviors to be attached to.
126
+ */
127
+ createBehavior() {
128
+ return this;
129
+ }
149
130
  }
@@ -1,5 +1,5 @@
1
1
  import { emptyArray } from "../platform.js";
2
- import { StatelessAttachedAttributeDirective, } from "./html-directive.js";
2
+ import { StatelessAttachedAttributeDirective } from "./html-directive.js";
3
3
  const selectElements = (value) => value.nodeType === 1;
4
4
  /**
5
5
  * Creates a function that can be used to filter a Node array, selecting only elements.
@@ -16,9 +16,15 @@ export const elements = (selector) => selector
16
16
  * Internally used by the SlottedDirective and the ChildrenDirective.
17
17
  */
18
18
  export class NodeObservationDirective extends StatelessAttachedAttributeDirective {
19
- constructor() {
20
- super(...arguments);
21
- this.sourceProperty = `${this.id}-s`;
19
+ /**
20
+ * The unique id of the factory.
21
+ */
22
+ get id() {
23
+ return this._id;
24
+ }
25
+ set id(value) {
26
+ this._id = value;
27
+ this._controllerProperty = `${value}-c`;
22
28
  }
23
29
  /**
24
30
  * Bind this behavior to the source.
@@ -26,11 +32,12 @@ export class NodeObservationDirective extends StatelessAttachedAttributeDirectiv
26
32
  * @param context - The execution context that the binding is operating within.
27
33
  * @param targets - The targets that behaviors in a view can attach to.
28
34
  */
29
- bind(source, context, targets) {
30
- const target = targets[this.nodeId];
31
- target[this.sourceProperty] = source;
32
- this.updateTarget(source, this.computeNodes(target));
35
+ bind(controller) {
36
+ const target = controller.targets[this.targetNodeId];
37
+ target[this._controllerProperty] = controller;
38
+ this.updateTarget(controller.source, this.computeNodes(target));
33
39
  this.observe(target);
40
+ controller.onUnbind(this);
34
41
  }
35
42
  /**
36
43
  * Unbinds this behavior from the source.
@@ -38,11 +45,11 @@ export class NodeObservationDirective extends StatelessAttachedAttributeDirectiv
38
45
  * @param context - The execution context that the binding is operating within.
39
46
  * @param targets - The targets that behaviors in a view can attach to.
40
47
  */
41
- unbind(source, context, targets) {
42
- const target = targets[this.nodeId];
43
- this.updateTarget(source, emptyArray);
48
+ unbind(controller) {
49
+ const target = controller.targets[this.targetNodeId];
50
+ this.updateTarget(controller.source, emptyArray);
44
51
  this.disconnect(target);
45
- target[this.sourceProperty] = null;
52
+ target[this._controllerProperty] = null;
46
53
  }
47
54
  /**
48
55
  * Gets the data source for the target.
@@ -50,7 +57,7 @@ export class NodeObservationDirective extends StatelessAttachedAttributeDirectiv
50
57
  * @returns The source.
51
58
  */
52
59
  getSource(target) {
53
- return target[this.sourceProperty];
60
+ return target[this._controllerProperty].source;
54
61
  }
55
62
  /**
56
63
  * Updates the source property with the computed nodes.
@@ -5,20 +5,12 @@ import { HTMLDirective, StatelessAttachedAttributeDirective, } from "./html-dire
5
5
  */
6
6
  export class RefDirective extends StatelessAttachedAttributeDirective {
7
7
  /**
8
- * Bind this behavior to the source.
9
- * @param source - The source to bind to.
10
- * @param context - The execution context that the binding is operating within.
11
- * @param targets - The targets that behaviors in a view can attach to.
8
+ * Bind this behavior.
9
+ * @param controller - The view controller that manages the lifecycle of this behavior.
12
10
  */
13
- bind(source, context, targets) {
14
- source[this.options] = targets[this.nodeId];
11
+ bind(controller) {
12
+ controller.source[this.options] = controller.targets[this.targetNodeId];
15
13
  }
16
- /**
17
- * Unbinds this behavior from the source.
18
- * @param source - The source to unbind from.
19
- */
20
- /* eslint-disable-next-line @typescript-eslint/no-empty-function */
21
- unbind() { }
22
14
  }
23
15
  HTMLDirective.define(RefDirective);
24
16
  /**