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

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 (106) hide show
  1. package/.eslintrc.json +1 -12
  2. package/CHANGELOG.json +432 -1
  3. package/CHANGELOG.md +74 -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 +10 -2
  8. package/dist/dts/components/fast-element.d.ts +12 -5
  9. package/dist/dts/context.d.ts +157 -0
  10. package/dist/dts/debug.d.ts +1 -0
  11. package/dist/dts/hooks.d.ts +20 -0
  12. package/dist/dts/index.d.ts +16 -15
  13. package/dist/dts/index.debug.d.ts +2 -0
  14. package/dist/dts/index.rollup.d.ts +2 -0
  15. package/dist/dts/index.rollup.debug.d.ts +3 -0
  16. package/dist/dts/interfaces.d.ts +144 -0
  17. package/dist/dts/metadata.d.ts +25 -0
  18. package/dist/dts/observation/arrays.d.ts +207 -0
  19. package/dist/dts/observation/behavior.d.ts +4 -4
  20. package/dist/dts/observation/notifier.d.ts +18 -18
  21. package/dist/dts/observation/observable.d.ts +56 -18
  22. package/dist/dts/observation/splice-strategies.d.ts +13 -0
  23. package/dist/dts/observation/update-queue.d.ts +40 -0
  24. package/dist/dts/platform.d.ts +18 -67
  25. package/dist/dts/polyfills.d.ts +8 -0
  26. package/dist/dts/styles/css-directive.d.ts +43 -5
  27. package/dist/dts/styles/css.d.ts +19 -3
  28. package/dist/dts/styles/element-styles.d.ts +42 -62
  29. package/dist/dts/templating/binding-signal.d.ts +38 -0
  30. package/dist/dts/templating/binding-two-way.d.ts +56 -0
  31. package/dist/dts/templating/binding.d.ts +233 -65
  32. package/dist/dts/templating/children.d.ts +18 -15
  33. package/dist/dts/templating/compiler.d.ts +46 -28
  34. package/dist/dts/templating/dom.d.ts +41 -0
  35. package/dist/dts/templating/html-directive.d.ts +179 -43
  36. package/dist/dts/templating/markup.d.ts +48 -0
  37. package/dist/dts/templating/node-observation.d.ts +45 -29
  38. package/dist/dts/templating/ref.d.ts +6 -12
  39. package/dist/dts/templating/repeat.d.ts +26 -14
  40. package/dist/dts/templating/slotted.d.ts +13 -14
  41. package/dist/dts/templating/template.d.ts +27 -21
  42. package/dist/dts/templating/view.d.ts +15 -22
  43. package/dist/{tsdoc-metadata.json → dts/tsdoc-metadata.json} +1 -1
  44. package/dist/dts/utilities.d.ts +40 -0
  45. package/dist/esm/components/attributes.js +25 -24
  46. package/dist/esm/components/controller.js +77 -57
  47. package/dist/esm/components/fast-definitions.js +16 -22
  48. package/dist/esm/components/fast-element.js +10 -2
  49. package/dist/esm/context.js +159 -0
  50. package/dist/esm/debug.js +29 -0
  51. package/dist/esm/hooks.js +32 -0
  52. package/dist/esm/index.debug.js +2 -0
  53. package/dist/esm/index.js +19 -14
  54. package/dist/esm/index.rollup.debug.js +3 -0
  55. package/dist/esm/index.rollup.js +2 -0
  56. package/dist/esm/interfaces.js +8 -1
  57. package/dist/esm/metadata.js +60 -0
  58. package/dist/esm/observation/arrays.js +269 -0
  59. package/dist/esm/observation/notifier.js +75 -83
  60. package/dist/esm/observation/observable.js +93 -68
  61. package/dist/esm/observation/{array-change-records.js → splice-strategies.js} +136 -62
  62. package/dist/esm/observation/update-queue.js +67 -0
  63. package/dist/esm/platform.js +36 -42
  64. package/dist/esm/polyfills.js +85 -0
  65. package/dist/esm/styles/css-directive.js +29 -13
  66. package/dist/esm/styles/css.js +27 -40
  67. package/dist/esm/styles/element-styles.js +65 -104
  68. package/dist/esm/templating/binding-signal.js +84 -0
  69. package/dist/esm/templating/binding-two-way.js +76 -0
  70. package/dist/esm/templating/binding.js +306 -153
  71. package/dist/esm/templating/children.js +33 -23
  72. package/dist/esm/templating/compiler.js +235 -152
  73. package/dist/esm/templating/dom.js +49 -0
  74. package/dist/esm/templating/html-directive.js +125 -40
  75. package/dist/esm/templating/markup.js +75 -0
  76. package/dist/esm/templating/node-observation.js +50 -45
  77. package/dist/esm/templating/ref.js +7 -16
  78. package/dist/esm/templating/repeat.js +39 -36
  79. package/dist/esm/templating/slotted.js +23 -20
  80. package/dist/esm/templating/template.js +51 -95
  81. package/dist/esm/templating/view.js +44 -43
  82. package/dist/esm/templating/when.js +2 -1
  83. package/dist/esm/utilities.js +139 -0
  84. package/dist/fast-element.api.json +11789 -5377
  85. package/dist/fast-element.d.ts +1177 -531
  86. package/dist/fast-element.debug.js +3717 -0
  87. package/dist/fast-element.debug.min.js +1 -0
  88. package/dist/fast-element.js +3466 -4022
  89. package/dist/fast-element.min.js +1 -1
  90. package/dist/fast-element.untrimmed.d.ts +2697 -0
  91. package/docs/api-report.md +472 -219
  92. package/docs/fast-element-2-changes.md +15 -0
  93. package/docs/guide/declaring-templates.md +5 -4
  94. package/docs/guide/defining-elements.md +3 -2
  95. package/docs/guide/leveraging-css.md +1 -0
  96. package/docs/guide/next-steps.md +3 -2
  97. package/docs/guide/observables-and-state.md +2 -1
  98. package/docs/guide/using-directives.md +2 -1
  99. package/docs/guide/working-with-shadow-dom.md +1 -0
  100. package/karma.conf.cjs +6 -17
  101. package/package.json +65 -15
  102. package/dist/dts/dom.d.ts +0 -112
  103. package/dist/dts/observation/array-change-records.d.ts +0 -48
  104. package/dist/dts/observation/array-observer.d.ts +0 -9
  105. package/dist/esm/dom.js +0 -207
  106. package/dist/esm/observation/array-observer.js +0 -173
@@ -1,46 +1,113 @@
1
- import { DOM } from "../dom.js";
1
+ import "../interfaces.js";
2
2
  import { ExecutionContext, Observable, } from "../observation/observable.js";
3
- import { TargetedHTMLDirective } from "./html-directive.js";
4
- function normalBind(source, context) {
5
- this.source = source;
6
- this.context = context;
7
- if (this.bindingObserver === null) {
8
- this.bindingObserver = Observable.binding(this.binding, this, this.isBindingVolatile);
9
- }
10
- this.updateTarget(this.bindingObserver.observe(source, context));
11
- }
12
- function triggerBind(source, context) {
13
- this.source = source;
14
- this.context = context;
15
- this.target.addEventListener(this.targetName, this);
16
- }
17
- function normalUnbind() {
18
- this.bindingObserver.disconnect();
19
- this.source = null;
20
- this.context = null;
21
- }
22
- function contentUnbind() {
23
- this.bindingObserver.disconnect();
24
- this.source = null;
25
- this.context = null;
26
- const view = this.target.$fastView;
27
- if (view !== void 0 && view.isComposed) {
28
- view.unbind();
29
- view.needsBindOnly = true;
3
+ import { FAST } from "../platform.js";
4
+ import { DOM } from "./dom.js";
5
+ import { Aspect, HTMLDirective, } from "./html-directive.js";
6
+ import { Markup } from "./markup.js";
7
+ const createInnerHTMLBinding = globalThis.TrustedHTML
8
+ ? (binding) => (s, c) => {
9
+ const value = binding(s, c);
10
+ if (value instanceof TrustedHTML) {
11
+ return value;
12
+ }
13
+ throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
14
+ }
15
+ : (binding) => binding;
16
+ /**
17
+ * Describes how aspects of an HTML element will be affected by bindings.
18
+ * @public
19
+ */
20
+ export const BindingMode = Object.freeze({
21
+ /**
22
+ * Creates a binding mode based on the supplied behavior types.
23
+ * @param UpdateType - The base behavior type used to update aspects.
24
+ * @param EventType - The base behavior type used to respond to events.
25
+ * @returns A new binding mode.
26
+ */
27
+ define(UpdateType, EventType = EventBinding) {
28
+ return Object.freeze({
29
+ [1]: d => new UpdateType(d, DOM.setAttribute),
30
+ [2]: d => new UpdateType(d, DOM.setBooleanAttribute),
31
+ [3]: d => new UpdateType(d, (t, a, v) => (t[a] = v)),
32
+ [4]: d => new (createContentBinding(UpdateType))(d, updateContentTarget),
33
+ [5]: d => new UpdateType(d, updateTokenListTarget),
34
+ [6]: d => new EventType(d),
35
+ });
36
+ },
37
+ });
38
+ /**
39
+ * Describes the configuration for a binding expression.
40
+ * @public
41
+ */
42
+ export const BindingConfig = Object.freeze({
43
+ /**
44
+ * Creates a binding configuration based on the provided mode and options.
45
+ * @param mode - The mode to use for the configuration.
46
+ * @param defaultOptions - The default options to use for the configuration.
47
+ * @returns A new binding configuration.
48
+ */
49
+ define(mode, defaultOptions) {
50
+ const config = (options) => {
51
+ return {
52
+ mode: config.mode,
53
+ options: Object.assign({}, defaultOptions, options),
54
+ };
55
+ };
56
+ config.options = defaultOptions;
57
+ config.mode = mode;
58
+ return config;
59
+ },
60
+ });
61
+ /**
62
+ * A base binding behavior for DOM updates.
63
+ * @public
64
+ */
65
+ export class UpdateBinding {
66
+ /**
67
+ * Creates an instance of UpdateBinding.
68
+ * @param directive - The directive that has the configuration for this behavior.
69
+ * @param updateTarget - The function used to update the target with the latest value.
70
+ */
71
+ constructor(directive, updateTarget) {
72
+ this.directive = directive;
73
+ this.updateTarget = updateTarget;
74
+ }
75
+ /**
76
+ * Bind this behavior to the source.
77
+ * @param source - The source to bind to.
78
+ * @param context - The execution context that the binding is operating within.
79
+ * @param targets - The targets that behaviors in a view can attach to.
80
+ */
81
+ bind(source, context, targets) { }
82
+ /**
83
+ * Unbinds this behavior from the source.
84
+ * @param source - The source to unbind from.
85
+ * @param context - The execution context that the binding is operating within.
86
+ * @param targets - The targets that behaviors in a view can attach to.
87
+ */
88
+ unbind(source, context, targets) { }
89
+ /**
90
+ * Creates a behavior.
91
+ * @param targets - The targets available for behaviors to be attached to.
92
+ */
93
+ createBehavior(targets) {
94
+ return this;
30
95
  }
31
96
  }
32
- function triggerUnbind() {
33
- this.target.removeEventListener(this.targetName, this);
34
- this.source = null;
35
- this.context = null;
36
- }
37
- function updateAttributeTarget(value) {
38
- DOM.setAttribute(this.target, this.targetName, value);
39
- }
40
- function updateBooleanAttributeTarget(value) {
41
- DOM.setBooleanAttribute(this.target, this.targetName, value);
97
+ function createContentBinding(Type) {
98
+ return class extends Type {
99
+ unbind(source, context, targets) {
100
+ super.unbind(source, context, targets);
101
+ const target = targets[this.directive.nodeId];
102
+ const view = target.$fastView;
103
+ if (view !== void 0 && view.isComposed) {
104
+ view.unbind();
105
+ view.needsBindOnly = true;
106
+ }
107
+ }
108
+ };
42
109
  }
43
- function updateContentTarget(value) {
110
+ function updateContentTarget(target, aspect, value, source, context) {
44
111
  // If there's no actual value, then this equates to the
45
112
  // empty string for the purposes of content bindings.
46
113
  if (value === null || value === undefined) {
@@ -48,8 +115,8 @@ function updateContentTarget(value) {
48
115
  }
49
116
  // If the value has a "create" method, then it's a template-like.
50
117
  if (value.create) {
51
- this.target.textContent = "";
52
- let view = this.target.$fastView;
118
+ target.textContent = "";
119
+ let view = target.$fastView;
53
120
  // If there's no previous view that we might be able to
54
121
  // reuse then create a new view from the template.
55
122
  if (view === void 0) {
@@ -60,7 +127,7 @@ function updateContentTarget(value) {
60
127
  // from the same template as the new value, then we
61
128
  // need to remove the old view if it's still in the DOM
62
129
  // and create a new view from the template.
63
- if (this.target.$fastTemplate !== value) {
130
+ if (target.$fastTemplate !== value) {
64
131
  if (view.isComposed) {
65
132
  view.remove();
66
133
  view.unbind();
@@ -72,18 +139,18 @@ function updateContentTarget(value) {
72
139
  // and that there's actually no need to compose it.
73
140
  if (!view.isComposed) {
74
141
  view.isComposed = true;
75
- view.bind(this.source, this.context);
76
- view.insertBefore(this.target);
77
- this.target.$fastView = view;
78
- this.target.$fastTemplate = value;
142
+ view.bind(source, context);
143
+ view.insertBefore(target);
144
+ target.$fastView = view;
145
+ target.$fastTemplate = value;
79
146
  }
80
147
  else if (view.needsBindOnly) {
81
148
  view.needsBindOnly = false;
82
- view.bind(this.source, this.context);
149
+ view.bind(source, context);
83
150
  }
84
151
  }
85
152
  else {
86
- const view = this.target.$fastView;
153
+ const view = target.$fastView;
87
154
  // If there is a view and it's currently composed into
88
155
  // the DOM, then we need to remove it.
89
156
  if (view !== void 0 && view.isComposed) {
@@ -96,16 +163,17 @@ function updateContentTarget(value) {
96
163
  view.unbind();
97
164
  }
98
165
  }
99
- this.target.textContent = value;
166
+ target.textContent = value;
100
167
  }
101
168
  }
102
- function updatePropertyTarget(value) {
103
- this.target[this.targetName] = value;
104
- }
105
- function updateClassTarget(value) {
106
- const classVersions = this.classVersions || Object.create(null);
107
- const target = this.target;
108
- let version = this.version || 0;
169
+ function updateTokenListTarget(target, aspect, value) {
170
+ var _a;
171
+ const directive = this.directive;
172
+ const lookup = `${directive.id}-t`;
173
+ const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { c: 0, v: Object.create(null) });
174
+ const versions = state.v;
175
+ let currentVersion = state.c;
176
+ const tokenList = target[aspect];
109
177
  // Add the classes, tracking the version at which they were added.
110
178
  if (value !== null && value !== undefined && value.length) {
111
179
  const names = value.split(/\s+/);
@@ -114,139 +182,224 @@ function updateClassTarget(value) {
114
182
  if (currentName === "") {
115
183
  continue;
116
184
  }
117
- classVersions[currentName] = version;
118
- target.classList.add(currentName);
185
+ versions[currentName] = currentVersion;
186
+ tokenList.add(currentName);
119
187
  }
120
188
  }
121
- this.classVersions = classVersions;
122
- this.version = version + 1;
189
+ state.v = currentVersion + 1;
123
190
  // If this is the first call to add classes, there's no need to remove old ones.
124
- if (version === 0) {
191
+ if (currentVersion === 0) {
125
192
  return;
126
193
  }
127
194
  // Remove classes from the previous version.
128
- version -= 1;
129
- for (const name in classVersions) {
130
- if (classVersions[name] === version) {
131
- target.classList.remove(name);
195
+ currentVersion -= 1;
196
+ for (const name in versions) {
197
+ if (versions[name] === currentVersion) {
198
+ tokenList.remove(name);
132
199
  }
133
200
  }
134
201
  }
135
202
  /**
136
- * A directive that configures data binding to element content and attributes.
203
+ * A binding behavior for one-time bindings.
137
204
  * @public
138
205
  */
139
- export class HTMLBindingDirective extends TargetedHTMLDirective {
206
+ export class OneTimeBinding extends UpdateBinding {
140
207
  /**
141
- * Creates an instance of BindingDirective.
142
- * @param binding - A binding that returns the data used to update the DOM.
208
+ * Bind this behavior to the source.
209
+ * @param source - The source to bind to.
210
+ * @param context - The execution context that the binding is operating within.
211
+ * @param targets - The targets that behaviors in a view can attach to.
143
212
  */
144
- constructor(binding) {
145
- super();
146
- this.binding = binding;
147
- this.bind = normalBind;
148
- this.unbind = normalUnbind;
149
- this.updateTarget = updateAttributeTarget;
150
- this.isBindingVolatile = Observable.isVolatileBinding(this.binding);
213
+ bind(source, context, targets) {
214
+ const directive = this.directive;
215
+ this.updateTarget(targets[directive.nodeId], directive.targetAspect, directive.binding(source, context), source, context);
151
216
  }
217
+ }
218
+ /**
219
+ * A binding behavior for bindings that change.
220
+ * @public
221
+ */
222
+ export class ChangeBinding extends UpdateBinding {
152
223
  /**
153
- * Gets/sets the name of the attribute or property that this
154
- * binding is targeting.
224
+ * Creates an instance of ChangeBinding.
225
+ * @param directive - The directive that has the configuration for this behavior.
226
+ * @param updateTarget - The function used to update the target with the latest value.
155
227
  */
156
- get targetName() {
157
- return this.originalTargetName;
228
+ constructor(directive, updateTarget) {
229
+ super(directive, updateTarget);
230
+ this.isBindingVolatile = Observable.isVolatileBinding(directive.binding);
231
+ this.observerProperty = `${directive.id}-o`;
158
232
  }
159
- set targetName(value) {
160
- this.originalTargetName = value;
161
- if (value === void 0) {
162
- return;
163
- }
164
- switch (value[0]) {
165
- case ":":
166
- this.cleanedTargetName = value.substr(1);
167
- this.updateTarget = updatePropertyTarget;
168
- if (this.cleanedTargetName === "innerHTML") {
169
- const binding = this.binding;
170
- this.binding = (s, c) => DOM.createHTML(binding(s, c));
171
- }
172
- break;
173
- case "?":
174
- this.cleanedTargetName = value.substr(1);
175
- this.updateTarget = updateBooleanAttributeTarget;
176
- break;
177
- case "@":
178
- this.cleanedTargetName = value.substr(1);
179
- this.bind = triggerBind;
180
- this.unbind = triggerUnbind;
181
- break;
182
- default:
183
- this.cleanedTargetName = value;
184
- if (value === "class") {
185
- this.updateTarget = updateClassTarget;
186
- }
187
- break;
188
- }
233
+ /**
234
+ * Returns the binding observer used to update the node.
235
+ * @param target - The target node.
236
+ * @returns A BindingObserver.
237
+ */
238
+ getObserver(target) {
239
+ var _a;
240
+ return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = Observable.binding(this.directive.binding, this, this.isBindingVolatile)));
189
241
  }
190
242
  /**
191
- * Makes this binding target the content of an element rather than
192
- * a particular attribute or property.
243
+ * Bind this behavior to the source.
244
+ * @param source - The source to bind to.
245
+ * @param context - The execution context that the binding is operating within.
246
+ * @param targets - The targets that behaviors in a view can attach to.
193
247
  */
194
- targetAtContent() {
195
- this.updateTarget = updateContentTarget;
196
- this.unbind = contentUnbind;
248
+ bind(source, context, targets) {
249
+ const directive = this.directive;
250
+ const target = targets[directive.nodeId];
251
+ const observer = this.getObserver(target);
252
+ observer.target = target;
253
+ observer.source = source;
254
+ observer.context = context;
255
+ this.updateTarget(target, directive.targetAspect, observer.observe(source, context), source, context);
197
256
  }
198
257
  /**
199
- * Creates the runtime BindingBehavior instance based on the configuration
200
- * information stored in the BindingDirective.
201
- * @param target - The target node that the binding behavior should attach to.
258
+ * Unbinds this behavior from the source.
259
+ * @param source - The source to unbind from.
260
+ * @param context - The execution context that the binding is operating within.
261
+ * @param targets - The targets that behaviors in a view can attach to.
202
262
  */
203
- createBehavior(target) {
204
- /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
205
- return new BindingBehavior(target, this.binding, this.isBindingVolatile, this.bind, this.unbind, this.updateTarget, this.cleanedTargetName);
263
+ unbind(source, context, targets) {
264
+ const target = targets[this.directive.nodeId];
265
+ const observer = this.getObserver(target);
266
+ observer.dispose();
267
+ observer.target = null;
268
+ observer.source = null;
269
+ observer.context = null;
270
+ }
271
+ /** @internal */
272
+ handleChange(binding, observer) {
273
+ const target = observer.target;
274
+ const source = observer.source;
275
+ const context = observer.context;
276
+ this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
206
277
  }
207
278
  }
208
279
  /**
209
- * A behavior that updates content and attributes based on a configured
210
- * BindingDirective.
280
+ * A binding behavior for handling events.
211
281
  * @public
212
282
  */
213
- export class BindingBehavior {
214
- /**
215
- * Creates an instance of BindingBehavior.
216
- * @param target - The target of the data updates.
217
- * @param binding - The binding that returns the latest value for an update.
218
- * @param isBindingVolatile - Indicates whether the binding has volatile dependencies.
219
- * @param bind - The operation to perform during binding.
220
- * @param unbind - The operation to perform during unbinding.
221
- * @param updateTarget - The operation to perform when updating.
222
- * @param targetName - The name of the target attribute or property to update.
223
- */
224
- constructor(target, binding, isBindingVolatile, bind, unbind, updateTarget, targetName) {
225
- /** @internal */
226
- this.source = null;
227
- /** @internal */
228
- this.context = null;
229
- /** @internal */
230
- this.bindingObserver = null;
231
- this.target = target;
232
- this.binding = binding;
233
- this.isBindingVolatile = isBindingVolatile;
234
- this.bind = bind;
235
- this.unbind = unbind;
236
- this.updateTarget = updateTarget;
237
- this.targetName = targetName;
283
+ export class EventBinding {
284
+ /**
285
+ * Creates an instance of EventBinding.
286
+ * @param directive - The directive that has the configuration for this behavior.
287
+ */
288
+ constructor(directive) {
289
+ this.directive = directive;
290
+ this.sourceProperty = `${directive.id}-s`;
291
+ this.contextProperty = `${directive.id}-c`;
238
292
  }
239
- /** @internal */
240
- handleChange() {
241
- this.updateTarget(this.bindingObserver.observe(this.source, this.context));
293
+ /**
294
+ * Bind this behavior to the source.
295
+ * @param source - The source to bind to.
296
+ * @param context - The execution context that the binding is operating within.
297
+ * @param targets - The targets that behaviors in a view can attach to.
298
+ */
299
+ bind(source, context, targets) {
300
+ const directive = this.directive;
301
+ const target = targets[directive.nodeId];
302
+ target[this.sourceProperty] = source;
303
+ target[this.contextProperty] = context;
304
+ target.addEventListener(directive.targetAspect, this, directive.options);
242
305
  }
243
- /** @internal */
306
+ /**
307
+ * Unbinds this behavior from the source.
308
+ * @param source - The source to unbind from.
309
+ * @param context - The execution context that the binding is operating within.
310
+ * @param targets - The targets that behaviors in a view can attach to.
311
+ */
312
+ unbind(source, context, targets) {
313
+ const directive = this.directive;
314
+ const target = targets[directive.nodeId];
315
+ target[this.sourceProperty] = target[this.contextProperty] = null;
316
+ target.removeEventListener(directive.targetAspect, this, directive.options);
317
+ }
318
+ /**
319
+ * Creates a behavior.
320
+ * @param targets - The targets available for behaviors to be attached to.
321
+ */
322
+ createBehavior(targets) {
323
+ return this;
324
+ }
325
+ /**
326
+ * @internal
327
+ */
244
328
  handleEvent(event) {
329
+ const target = event.currentTarget;
245
330
  ExecutionContext.setEvent(event);
246
- const result = this.binding(this.source, this.context);
331
+ const result = this.directive.binding(target[this.sourceProperty], target[this.contextProperty]);
247
332
  ExecutionContext.setEvent(null);
248
333
  if (result !== true) {
249
334
  event.preventDefault();
250
335
  }
251
336
  }
252
337
  }
338
+ /**
339
+ * The default onChange binding configuration.
340
+ * @public
341
+ */
342
+ export const onChange = BindingConfig.define(BindingMode.define(ChangeBinding), {});
343
+ /**
344
+ * The default onTime binding configuration.
345
+ * @public
346
+ */
347
+ export const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
348
+ once: true,
349
+ });
350
+ /**
351
+ * A directive that applies bindings.
352
+ * @public
353
+ */
354
+ export class HTMLBindingDirective {
355
+ /**
356
+ * Creates an instance of HTMLBindingDirective.
357
+ * @param binding - The binding to apply.
358
+ * @param mode - The binding mode to use when applying the binding.
359
+ * @param options - The options to configure the binding with.
360
+ */
361
+ constructor(binding, mode, options) {
362
+ this.binding = binding;
363
+ this.mode = mode;
364
+ this.options = options;
365
+ this.factory = null;
366
+ /**
367
+ * The type of aspect to target.
368
+ */
369
+ this.aspectType = Aspect.content;
370
+ }
371
+ /**
372
+ * Creates HTML to be used within a template.
373
+ * @param add - Can be used to add behavior factories to a template.
374
+ */
375
+ createHTML(add) {
376
+ return Markup.interpolation(add(this));
377
+ }
378
+ /**
379
+ * Creates a behavior.
380
+ * @param targets - The targets available for behaviors to be attached to.
381
+ */
382
+ createBehavior(targets) {
383
+ if (this.factory == null) {
384
+ if (this.targetAspect === "innerHTML") {
385
+ this.binding = createInnerHTMLBinding(this.binding);
386
+ }
387
+ this.factory = this.mode[this.aspectType](this);
388
+ }
389
+ return this.factory.createBehavior(targets);
390
+ }
391
+ }
392
+ HTMLDirective.define(HTMLBindingDirective, { aspected: true });
393
+ /**
394
+ * Creates a binding directive with the specified configuration.
395
+ * @param binding - The binding expression.
396
+ * @param config - The binding configuration.
397
+ * @returns A binding directive.
398
+ * @public
399
+ */
400
+ export function bind(binding, config = onChange) {
401
+ if (!("mode" in config)) {
402
+ config = onChange(config);
403
+ }
404
+ return new HTMLBindingDirective(binding, config.mode, config.options);
405
+ }
@@ -1,45 +1,55 @@
1
- import { AttachedBehaviorHTMLDirective } from "./html-directive.js";
2
- import { NodeObservationBehavior } from "./node-observation.js";
1
+ import { isString } from "../interfaces.js";
2
+ import { HTMLDirective } from "./html-directive.js";
3
+ import { NodeObservationDirective } from "./node-observation.js";
3
4
  /**
4
5
  * The runtime behavior for child node observation.
5
6
  * @public
6
7
  */
7
- export class ChildrenBehavior extends NodeObservationBehavior {
8
+ export class ChildrenDirective extends NodeObservationDirective {
8
9
  /**
9
- * Creates an instance of ChildrenBehavior.
10
- * @param target - The element target to observe children on.
11
- * @param options - The options to use when observing the element children.
10
+ * Creates an instance of ChildrenDirective.
11
+ * @param options - The options to use in configuring the child observation behavior.
12
12
  */
13
- constructor(target, options) {
14
- super(target, options);
15
- this.observer = null;
13
+ constructor(options) {
14
+ super(options);
15
+ this.observerProperty = `${this.id}-o`;
16
+ this.handleEvent = (mutations, observer) => {
17
+ const target = observer.target;
18
+ this.updateTarget(this.getSource(target), this.computeNodes(target));
19
+ };
16
20
  options.childList = true;
17
21
  }
18
22
  /**
19
23
  * Begins observation of the nodes.
24
+ * @param target - The target to observe.
20
25
  */
21
- observe() {
22
- if (this.observer === null) {
23
- this.observer = new MutationObserver(this.handleEvent.bind(this));
24
- }
25
- this.observer.observe(this.target, this.options);
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;
30
+ observer.observe(target, this.options);
26
31
  }
27
32
  /**
28
33
  * Disconnects observation of the nodes.
34
+ * @param target - The target to unobserve.
29
35
  */
30
- disconnect() {
31
- this.observer.disconnect();
36
+ disconnect(target) {
37
+ const observer = target[this.observerProperty];
38
+ observer.target = null;
39
+ observer.disconnect();
32
40
  }
33
41
  /**
34
- * Retrieves the nodes that should be assigned to the target.
42
+ * Retrieves the raw nodes that should be assigned to the source property.
43
+ * @param target - The target to get the node to.
35
44
  */
36
- getNodes() {
37
- if ("subtree" in this.options) {
38
- return Array.from(this.target.querySelectorAll(this.options.selector));
45
+ getNodes(target) {
46
+ if ("selector" in this.options) {
47
+ return Array.from(target.querySelectorAll(this.options.selector));
39
48
  }
40
- return Array.from(this.target.childNodes);
49
+ return Array.from(target.childNodes);
41
50
  }
42
51
  }
52
+ HTMLDirective.define(ChildrenDirective);
43
53
  /**
44
54
  * A directive that observes the `childNodes` of an element and updates a property
45
55
  * whenever they change.
@@ -47,10 +57,10 @@ export class ChildrenBehavior extends NodeObservationBehavior {
47
57
  * @public
48
58
  */
49
59
  export function children(propertyOrOptions) {
50
- if (typeof propertyOrOptions === "string") {
60
+ if (isString(propertyOrOptions)) {
51
61
  propertyOrOptions = {
52
62
  property: propertyOrOptions,
53
63
  };
54
64
  }
55
- return new AttachedBehaviorHTMLDirective("fast-children", ChildrenBehavior, propertyOrOptions);
65
+ return new ChildrenDirective(propertyOrOptions);
56
66
  }