@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,45 +1,66 @@
1
- import { BindingConfig, BindingMode, ChangeBinding, } from "./binding.js";
1
+ import { isString, noop } from "../interfaces.js";
2
+ import { Observable, } from "../observation/observable.js";
3
+ import { FAST } from "../platform.js";
4
+ import { Binding } from "./html-directive.js";
5
+ const defaultOptions = {
6
+ fromView: v => v,
7
+ };
2
8
  let twoWaySettings = {
3
9
  determineChangeEvent() {
4
10
  return "change";
5
11
  },
6
12
  };
7
- /**
8
- * A binding behavior for bindings that update in two directions.
9
- * @public
10
- */
11
- export class TwoWayBinding extends ChangeBinding {
13
+ export const TwoWaySettings = Object.freeze({
12
14
  /**
13
- * Bind this behavior to the source.
14
- * @param source - The source to bind to.
15
- * @param context - The execution context that the binding is operating within.
16
- * @param targets - The targets that behaviors in a view can attach to.
15
+ * Configures two-way binding.
16
+ * @param settings - The settings to use for the two-way binding system.
17
17
  */
18
- bind(source, context, targets) {
18
+ configure(settings) {
19
+ twoWaySettings = settings;
20
+ },
21
+ });
22
+ class TwoWayObserver {
23
+ constructor(directive, subscriber, dataBinding) {
24
+ this.directive = directive;
25
+ this.subscriber = subscriber;
26
+ this.dataBinding = dataBinding;
27
+ this.isNotBound = true;
28
+ /**
29
+ * Opts out of JSON stringification.
30
+ * @internal
31
+ */
32
+ this.toJSON = noop;
33
+ this.notifier = Observable.binding(dataBinding.evaluate, this, dataBinding.isVolatile);
34
+ }
35
+ bind(controller) {
19
36
  var _a;
20
- super.bind(source, context, targets);
21
- const directive = this.directive;
22
- const target = targets[directive.nodeId];
23
37
  if (!this.changeEvent) {
24
38
  this.changeEvent =
25
- (_a = directive.options.changeEvent) !== null && _a !== void 0 ? _a : twoWaySettings.determineChangeEvent(directive, target);
39
+ (_a = this.dataBinding.options.changeEvent) !== null && _a !== void 0 ? _a : twoWaySettings.determineChangeEvent(this.directive, this.target);
26
40
  }
27
- target.addEventListener(this.changeEvent, this);
41
+ if (this.isNotBound) {
42
+ this.target.addEventListener(this.changeEvent, this);
43
+ controller.onUnbind(this);
44
+ this.isNotBound = false;
45
+ }
46
+ return this.notifier.bind(controller);
28
47
  }
29
- /**
30
- * Unbinds this behavior from the source.
31
- * @param source - The source to unbind from.
32
- * @param context - The execution context that the binding is operating within.
33
- * @param targets - The targets that behaviors in a view can attach to.
34
- */
35
- unbind(source, context, targets) {
36
- super.unbind(source, context, targets);
37
- targets[this.directive.nodeId].removeEventListener(this.changeEvent, this);
48
+ unbind(controller) {
49
+ this.isNotBound = true;
50
+ this.target.removeEventListener(this.changeEvent, this);
51
+ }
52
+ handleChange(subject, args) {
53
+ this.subscriber.handleChange(this.dataBinding.evaluate, this);
38
54
  }
39
- /** @internal */
40
55
  handleEvent(event) {
41
56
  const directive = this.directive;
42
57
  const target = event.currentTarget;
58
+ const notifier = this.notifier;
59
+ const last = notifier.last; // using internal API!!!
60
+ if (!last) {
61
+ FAST.warn(1203 /* Message.twoWayBindingRequiresObservables */);
62
+ return;
63
+ }
43
64
  let value;
44
65
  switch (directive.aspectType) {
45
66
  case 1:
@@ -55,22 +76,34 @@ export class TwoWayBinding extends ChangeBinding {
55
76
  value = target[directive.targetAspect];
56
77
  break;
57
78
  }
58
- const observer = this.getObserver(target);
59
- const last = observer.last; // using internal API!!!
60
- last.propertySource[last.propertyName] = directive.options.fromView(value);
79
+ last.propertySource[last.propertyName] = this.dataBinding.options.fromView(value);
61
80
  }
62
- /**
63
- * Configures two-way binding.
64
- * @param settings - The settings to use for the two-way binding system.
65
- */
66
- static configure(settings) {
67
- twoWaySettings = settings;
81
+ }
82
+ class TwoWayBinding extends Binding {
83
+ createObserver(directive, subscriber) {
84
+ return new TwoWayObserver(directive, subscriber, this);
68
85
  }
69
86
  }
70
87
  /**
71
- * The default twoWay binding configuration.
88
+ * Creates a default binding.
89
+ * @param expression - The binding to refresh when changed.
90
+ * @param optionsOrChangeEvent - The binding options or the name of the change event to use.
91
+ * @param policy - The security policy to associate with the binding.
92
+ * @param isBindingVolatile - Indicates whether the binding is volatile or not.
93
+ * @returns A binding.
72
94
  * @public
73
95
  */
74
- export const twoWay = BindingConfig.define(BindingMode.define(TwoWayBinding), {
75
- fromView: v => v,
76
- });
96
+ export function twoWay(expression, optionsOrChangeEvent, policy, isBindingVolatile = Observable.isVolatileBinding(expression)) {
97
+ if (isString(optionsOrChangeEvent)) {
98
+ optionsOrChangeEvent = { changeEvent: optionsOrChangeEvent };
99
+ }
100
+ if (!optionsOrChangeEvent) {
101
+ optionsOrChangeEvent = defaultOptions;
102
+ }
103
+ else if (!optionsOrChangeEvent.fromView) {
104
+ optionsOrChangeEvent.fromView = defaultOptions.fromView;
105
+ }
106
+ const binding = new TwoWayBinding(expression, policy, isBindingVolatile);
107
+ binding.options = optionsOrChangeEvent;
108
+ return binding;
109
+ }
@@ -1,119 +1,37 @@
1
- import "../interfaces.js";
1
+ import { isFunction, noop } from "../interfaces.js";
2
2
  import { ExecutionContext, Observable, } from "../observation/observable.js";
3
3
  import { FAST } from "../platform.js";
4
- import { DOM } from "./dom.js";
5
- import { Aspect, HTMLDirective, } from "./html-directive.js";
4
+ import { DOM, DOMAspect } from "../dom.js";
5
+ import { Binding, HTMLDirective, } from "./html-directive.js";
6
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 */);
7
+ class OnChangeBinding extends Binding {
8
+ createObserver(_, subscriber) {
9
+ return Observable.binding(this.evaluate, subscriber, this.isVolatile);
14
10
  }
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;
11
+ }
12
+ class OneTimeBinding extends Binding {
13
+ constructor() {
14
+ super(...arguments);
15
+ /**
16
+ * Opts out of JSON stringification.
17
+ * @internal
18
+ */
19
+ this.toJSON = noop;
74
20
  }
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) {
21
+ createObserver() {
94
22
  return this;
95
23
  }
24
+ bind(controller) {
25
+ return this.evaluate(controller.source, controller.context);
26
+ }
96
27
  }
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
- };
109
- }
110
- function updateContentTarget(target, aspect, value, source, context) {
28
+ function updateContent(target, aspect, value, controller) {
111
29
  // If there's no actual value, then this equates to the
112
30
  // empty string for the purposes of content bindings.
113
31
  if (value === null || value === undefined) {
114
32
  value = "";
115
33
  }
116
- // If the value has a "create" method, then it's a template-like.
34
+ // If the value has a "create" method, then it's a ContentTemplate.
117
35
  if (value.create) {
118
36
  target.textContent = "";
119
37
  let view = target.$fastView;
@@ -139,14 +57,14 @@ function updateContentTarget(target, aspect, value, source, context) {
139
57
  // and that there's actually no need to compose it.
140
58
  if (!view.isComposed) {
141
59
  view.isComposed = true;
142
- view.bind(source, context);
60
+ view.bind(controller.source, controller.context);
143
61
  view.insertBefore(target);
144
62
  target.$fastView = view;
145
63
  target.$fastTemplate = value;
146
64
  }
147
65
  else if (view.needsBindOnly) {
148
66
  view.needsBindOnly = false;
149
- view.bind(source, context);
67
+ view.bind(controller.source, controller.context);
150
68
  }
151
69
  }
152
70
  else {
@@ -166,13 +84,12 @@ function updateContentTarget(target, aspect, value, source, context) {
166
84
  target.textContent = value;
167
85
  }
168
86
  }
169
- function updateTokenListTarget(target, aspect, value) {
87
+ function updateTokenList(target, aspect, value) {
170
88
  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;
89
+ const lookup = `${this.id}-t`;
90
+ const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { v: 0, cv: Object.create(null) });
91
+ const classVersions = state.cv;
92
+ let version = state.v;
176
93
  const tokenList = target[aspect];
177
94
  // Add the classes, tracking the version at which they were added.
178
95
  if (value !== null && value !== undefined && value.length) {
@@ -182,224 +99,163 @@ function updateTokenListTarget(target, aspect, value) {
182
99
  if (currentName === "") {
183
100
  continue;
184
101
  }
185
- versions[currentName] = currentVersion;
102
+ classVersions[currentName] = version;
186
103
  tokenList.add(currentName);
187
104
  }
188
105
  }
189
- state.v = currentVersion + 1;
106
+ state.v = version + 1;
190
107
  // If this is the first call to add classes, there's no need to remove old ones.
191
- if (currentVersion === 0) {
108
+ if (version === 0) {
192
109
  return;
193
110
  }
194
111
  // Remove classes from the previous version.
195
- currentVersion -= 1;
196
- for (const name in versions) {
197
- if (versions[name] === currentVersion) {
112
+ version -= 1;
113
+ for (const name in classVersions) {
114
+ if (classVersions[name] === version) {
198
115
  tokenList.remove(name);
199
116
  }
200
117
  }
201
118
  }
119
+ const sinkLookup = {
120
+ [DOMAspect.attribute]: DOM.setAttribute,
121
+ [DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
122
+ [DOMAspect.property]: (t, a, v) => (t[a] = v),
123
+ [DOMAspect.content]: updateContent,
124
+ [DOMAspect.tokenList]: updateTokenList,
125
+ [DOMAspect.event]: () => void 0,
126
+ };
202
127
  /**
203
- * A binding behavior for one-time bindings.
128
+ * A directive that applies bindings.
204
129
  * @public
205
130
  */
206
- export class OneTimeBinding extends UpdateBinding {
131
+ export class HTMLBindingDirective {
207
132
  /**
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.
133
+ * Creates an instance of HTMLBindingDirective.
134
+ * @param dataBinding - The binding configuration to apply.
212
135
  */
213
- bind(source, context, targets) {
214
- const directive = this.directive;
215
- this.updateTarget(targets[directive.nodeId], directive.targetAspect, directive.binding(source, context), source, context);
136
+ constructor(dataBinding) {
137
+ this.dataBinding = dataBinding;
138
+ this.updateTarget = null;
139
+ /**
140
+ * The type of aspect to target.
141
+ */
142
+ this.aspectType = DOMAspect.content;
216
143
  }
217
- }
218
- /**
219
- * A binding behavior for bindings that change.
220
- * @public
221
- */
222
- export class ChangeBinding extends UpdateBinding {
223
144
  /**
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.
145
+ * Creates HTML to be used within a template.
146
+ * @param add - Can be used to add behavior factories to a template.
227
147
  */
228
- constructor(directive, updateTarget) {
229
- super(directive, updateTarget);
230
- this.isBindingVolatile = Observable.isVolatileBinding(directive.binding);
231
- this.observerProperty = `${directive.id}-o`;
148
+ createHTML(add) {
149
+ return Markup.interpolation(add(this));
232
150
  }
233
151
  /**
234
- * Returns the binding observer used to update the node.
235
- * @param target - The target node.
236
- * @returns A BindingObserver.
152
+ * Creates a behavior.
237
153
  */
238
- getObserver(target) {
154
+ createBehavior() {
239
155
  var _a;
240
- return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = Observable.binding(this.directive.binding, this, this.isBindingVolatile)));
156
+ if (this.updateTarget === null) {
157
+ const sink = sinkLookup[this.aspectType];
158
+ const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
159
+ if (!sink) {
160
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
161
+ }
162
+ this.data = `${this.id}-d`;
163
+ this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
164
+ }
165
+ return this;
241
166
  }
242
- /**
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.
247
- */
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);
167
+ /** @internal */
168
+ bind(controller) {
169
+ var _a;
170
+ const target = controller.targets[this.targetNodeId];
171
+ switch (this.aspectType) {
172
+ case DOMAspect.event:
173
+ target[this.data] = controller;
174
+ target.addEventListener(this.targetAspect, this, this.dataBinding.options);
175
+ break;
176
+ case DOMAspect.content:
177
+ controller.onUnbind(this);
178
+ // intentional fall through
179
+ default:
180
+ const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
181
+ observer.target = target;
182
+ observer.controller = controller;
183
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
184
+ break;
185
+ }
256
186
  }
257
- /**
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.
262
- */
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;
187
+ /** @internal */
188
+ unbind(controller) {
189
+ const target = controller.targets[this.targetNodeId];
190
+ const view = target.$fastView;
191
+ if (view !== void 0 && view.isComposed) {
192
+ view.unbind();
193
+ view.needsBindOnly = true;
194
+ }
195
+ }
196
+ /** @internal */
197
+ handleEvent(event) {
198
+ const controller = event.currentTarget[this.data];
199
+ if (controller.isBound) {
200
+ ExecutionContext.setEvent(event);
201
+ const result = this.dataBinding.evaluate(controller.source, controller.context);
202
+ ExecutionContext.setEvent(null);
203
+ if (result !== true) {
204
+ event.preventDefault();
205
+ }
206
+ }
270
207
  }
271
208
  /** @internal */
272
209
  handleChange(binding, observer) {
273
210
  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);
211
+ const controller = observer.controller;
212
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
277
213
  }
278
214
  }
215
+ HTMLDirective.define(HTMLBindingDirective, { aspected: true });
279
216
  /**
280
- * A binding behavior for handling events.
217
+ * Creates an standard binding.
218
+ * @param expression - The binding to refresh when changed.
219
+ * @param policy - The security policy to associate with th binding.
220
+ * @param isVolatile - Indicates whether the binding is volatile or not.
221
+ * @returns A binding configuration.
281
222
  * @public
282
223
  */
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`;
292
- }
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);
305
- }
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
- */
328
- handleEvent(event) {
329
- const target = event.currentTarget;
330
- ExecutionContext.setEvent(event);
331
- const result = this.directive.binding(target[this.sourceProperty], target[this.contextProperty]);
332
- ExecutionContext.setEvent(null);
333
- if (result !== true) {
334
- event.preventDefault();
335
- }
336
- }
224
+ export function bind(expression, policy, isVolatile = Observable.isVolatileBinding(expression)) {
225
+ return new OnChangeBinding(expression, policy, isVolatile);
337
226
  }
338
227
  /**
339
- * The default onChange binding configuration.
228
+ * Creates a one time binding
229
+ * @param expression - The binding to refresh when signaled.
230
+ * @param policy - The security policy to associate with th binding.
231
+ * @returns A binding configuration.
340
232
  * @public
341
233
  */
342
- export const onChange = BindingConfig.define(BindingMode.define(ChangeBinding), {});
234
+ export function oneTime(expression, policy) {
235
+ return new OneTimeBinding(expression, policy);
236
+ }
343
237
  /**
344
- * The default onTime binding configuration.
238
+ * Creates an event listener binding.
239
+ * @param expression - The binding to invoke when the event is raised.
240
+ * @param options - Event listener options.
241
+ * @returns A binding configuration.
345
242
  * @public
346
243
  */
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
- }
244
+ export function listener(expression, options) {
245
+ const config = new OnChangeBinding(expression);
246
+ config.options = options;
247
+ return config;
391
248
  }
392
- HTMLDirective.define(HTMLBindingDirective, { aspected: true });
393
249
  /**
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.
250
+ * Normalizes the input value into a binding.
251
+ * @param value - The value to create the default binding for.
252
+ * @returns A binding configuration for the provided value.
398
253
  * @public
399
254
  */
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);
255
+ export function normalizeBinding(value) {
256
+ return isFunction(value)
257
+ ? bind(value)
258
+ : value instanceof Binding
259
+ ? value
260
+ : oneTime(() => value);
405
261
  }