@microsoft/fast-element 2.0.0-beta.5 → 2.0.0-beta.7

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 (80) hide show
  1. package/CHANGELOG.json +72 -0
  2. package/CHANGELOG.md +24 -1
  3. package/dist/dts/components/attributes.d.ts +10 -0
  4. package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +24 -25
  5. package/dist/dts/components/fast-definitions.d.ts +28 -3
  6. package/dist/dts/components/fast-element.d.ts +2 -2
  7. package/dist/dts/di/di.d.ts +41 -0
  8. package/dist/dts/index.d.ts +2 -2
  9. package/dist/dts/interfaces.d.ts +4 -0
  10. package/dist/dts/observation/observable.d.ts +86 -47
  11. package/dist/dts/pending-task.d.ts +20 -0
  12. package/dist/dts/platform.d.ts +6 -0
  13. package/dist/dts/state/exports.d.ts +3 -0
  14. package/dist/dts/state/reactive.d.ts +8 -0
  15. package/dist/dts/state/state.d.ts +141 -0
  16. package/dist/dts/state/visitor.d.ts +6 -0
  17. package/dist/dts/state/watch.d.ts +10 -0
  18. package/dist/dts/styles/css-directive.d.ts +2 -2
  19. package/dist/dts/styles/element-styles.d.ts +3 -3
  20. package/dist/dts/styles/host.d.ts +68 -0
  21. package/dist/dts/templating/binding-signal.d.ts +2 -2
  22. package/dist/dts/templating/binding-two-way.d.ts +11 -3
  23. package/dist/dts/templating/binding.d.ts +21 -119
  24. package/dist/dts/templating/children.d.ts +1 -1
  25. package/dist/dts/templating/html-directive.d.ts +69 -39
  26. package/dist/dts/templating/node-observation.d.ts +4 -5
  27. package/dist/dts/templating/ref.d.ts +5 -13
  28. package/dist/dts/templating/render.d.ts +15 -20
  29. package/dist/dts/templating/repeat.d.ts +11 -16
  30. package/dist/dts/templating/slotted.d.ts +1 -1
  31. package/dist/dts/templating/template.d.ts +4 -4
  32. package/dist/dts/templating/view.d.ts +68 -9
  33. package/dist/dts/templating/when.d.ts +1 -1
  34. package/dist/dts/testing/exports.d.ts +1 -0
  35. package/dist/dts/testing/fakes.d.ts +4 -0
  36. package/dist/dts/testing/fixture.d.ts +0 -6
  37. package/dist/dts/utilities.d.ts +0 -18
  38. package/dist/esm/components/attributes.js +13 -4
  39. package/dist/esm/components/{controller.js → element-controller.js} +95 -105
  40. package/dist/esm/components/fast-definitions.js +3 -1
  41. package/dist/esm/components/fast-element.js +4 -4
  42. package/dist/esm/di/di.js +87 -3
  43. package/dist/esm/index.js +2 -1
  44. package/dist/esm/interfaces.js +4 -0
  45. package/dist/esm/observation/observable.js +59 -126
  46. package/dist/esm/pending-task.js +16 -0
  47. package/dist/esm/platform.js +21 -0
  48. package/dist/esm/state/exports.js +3 -0
  49. package/dist/esm/state/reactive.js +34 -0
  50. package/dist/esm/state/state.js +148 -0
  51. package/dist/esm/state/visitor.js +28 -0
  52. package/dist/esm/state/watch.js +36 -0
  53. package/dist/esm/styles/css.js +4 -4
  54. package/dist/esm/{observation/behavior.js → styles/host.js} +0 -0
  55. package/dist/esm/templating/binding-signal.js +21 -17
  56. package/dist/esm/templating/binding-two-way.js +32 -27
  57. package/dist/esm/templating/binding.js +73 -177
  58. package/dist/esm/templating/html-directive.js +78 -7
  59. package/dist/esm/templating/node-observation.js +9 -8
  60. package/dist/esm/templating/ref.js +4 -12
  61. package/dist/esm/templating/render.js +30 -31
  62. package/dist/esm/templating/repeat.js +37 -38
  63. package/dist/esm/templating/template.js +3 -4
  64. package/dist/esm/templating/view.js +96 -24
  65. package/dist/esm/testing/exports.js +1 -0
  66. package/dist/esm/testing/fakes.js +76 -0
  67. package/dist/esm/testing/fixture.js +1 -3
  68. package/dist/esm/utilities.js +0 -95
  69. package/dist/fast-element.api.json +5720 -5385
  70. package/dist/fast-element.d.ts +510 -399
  71. package/dist/fast-element.debug.js +492 -509
  72. package/dist/fast-element.debug.min.js +1 -1
  73. package/dist/fast-element.js +492 -509
  74. package/dist/fast-element.min.js +1 -1
  75. package/dist/fast-element.untrimmed.d.ts +519 -405
  76. package/docs/api-report.md +197 -129
  77. package/package.json +8 -4
  78. package/dist/dts/hooks.d.ts +0 -20
  79. package/dist/dts/observation/behavior.d.ts +0 -19
  80. package/dist/esm/hooks.js +0 -32
@@ -11,57 +11,48 @@ import { html, } from "./template.js";
11
11
  export class RenderBehavior {
12
12
  /**
13
13
  * Creates an instance of RenderBehavior.
14
- * @param location - A Node representing the location where this behavior will render.
15
- * @param dataBinding - A binding expression that returns the data to render.
16
- * @param templateBinding - A binding expression that returns the template to use with the data.
14
+ * @param directive - The render directive that created this behavior.
17
15
  */
18
- constructor(directive, location) {
16
+ constructor(directive) {
19
17
  this.directive = directive;
20
- this.location = location;
21
- this.source = null;
18
+ this.location = null;
19
+ this.controller = null;
22
20
  this.view = null;
23
21
  this.data = null;
24
- this.originalContext = void 0;
25
- this.childContext = void 0;
26
22
  this.dataBindingObserver = directive.dataBinding.createObserver(directive, this);
27
23
  this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
28
24
  }
29
25
  /**
30
- * Bind this behavior to the source.
31
- * @param source - The source to bind to.
32
- * @param context - The execution context that the binding is operating within.
26
+ * Bind this behavior.
27
+ * @param controller - The view controller that manages the lifecycle of this behavior.
33
28
  */
34
- bind(source, context) {
35
- this.source = source;
36
- this.originalContext = context;
37
- this.childContext = context.createChildContext(source);
38
- this.data = this.dataBindingObserver.observe(source, this.originalContext);
39
- this.template = this.templateBindingObserver.observe(source, this.originalContext);
29
+ bind(controller) {
30
+ this.location = controller.targets[this.directive.nodeId];
31
+ this.controller = controller;
32
+ this.data = this.dataBindingObserver.bind(controller);
33
+ this.template = this.templateBindingObserver.bind(controller);
34
+ controller.onUnbind(this);
40
35
  this.refreshView();
41
36
  }
42
37
  /**
43
- * Unbinds this behavior from the source.
44
- * @param source - The source to unbind from.
38
+ * Unbinds this behavior.
39
+ * @param controller - The view controller that manages the lifecycle of this behavior.
45
40
  */
46
- unbind(source, context) {
47
- this.source = null;
48
- this.data = null;
41
+ unbind(controller) {
49
42
  const view = this.view;
50
43
  if (view !== null && view.isComposed) {
51
44
  view.unbind();
52
45
  view.needsBindOnly = true;
53
46
  }
54
- this.dataBindingObserver.dispose();
55
- this.templateBindingObserver.dispose();
56
47
  }
57
48
  /** @internal */
58
49
  handleChange(source, observer) {
59
50
  if (observer === this.dataBindingObserver) {
60
- this.data = this.dataBindingObserver.observe(this.source, this.originalContext);
51
+ this.data = this.dataBindingObserver.bind(this.controller);
61
52
  }
62
53
  if (this.directive.templateBindingDependsOnData ||
63
54
  observer === this.templateBindingObserver) {
64
- this.template = this.templateBindingObserver.observe(this.source, this.originalContext);
55
+ this.template = this.templateBindingObserver.bind(this.controller);
65
56
  }
66
57
  this.refreshView();
67
58
  }
@@ -70,6 +61,8 @@ export class RenderBehavior {
70
61
  const template = this.template;
71
62
  if (view === null) {
72
63
  this.view = view = template.create();
64
+ this.view.context.parent = this.controller.source;
65
+ this.view.context.parentContext = this.controller.context;
73
66
  }
74
67
  else {
75
68
  // If there is a previous view, but it wasn't created
@@ -82,19 +75,21 @@ export class RenderBehavior {
82
75
  view.unbind();
83
76
  }
84
77
  this.view = view = template.create();
78
+ this.view.context.parent = this.controller.source;
79
+ this.view.context.parentContext = this.controller.context;
85
80
  }
86
81
  }
87
82
  // It's possible that the value is the same as the previous template
88
83
  // and that there's actually no need to compose it.
89
84
  if (!view.isComposed) {
90
85
  view.isComposed = true;
91
- view.bind(this.data, this.childContext);
86
+ view.bind(this.data);
92
87
  view.insertBefore(this.location);
93
88
  view.$fastTemplate = template;
94
89
  }
95
90
  else if (view.needsBindOnly) {
96
91
  view.needsBindOnly = false;
97
- view.bind(this.data, this.childContext);
92
+ view.bind(this.data);
98
93
  }
99
94
  }
100
95
  }
@@ -124,8 +119,8 @@ export class RenderDirective {
124
119
  * Creates a behavior.
125
120
  * @param targets - The targets available for behaviors to be attached to.
126
121
  */
127
- createBehavior(targets) {
128
- return new RenderBehavior(this, targets[this.nodeId]);
122
+ createBehavior() {
123
+ return new RenderBehavior(this);
129
124
  }
130
125
  }
131
126
  HTMLDirective.define(RenderDirective);
@@ -297,7 +292,11 @@ export class NodeTemplate {
297
292
  this.node = node;
298
293
  node.$fastTemplate = this;
299
294
  }
300
- bind(source, context) { }
295
+ get context() {
296
+ // HACK
297
+ return this;
298
+ }
299
+ bind(source) { }
301
300
  unbind() { }
302
301
  insertBefore(refNode) {
303
302
  refNode.parentNode.insertBefore(this.node, refNode);
@@ -1,4 +1,4 @@
1
- import { Observable, } from "../observation/observable.js";
1
+ import { Observable } from "../observation/observable.js";
2
2
  import { emptyArray } from "../platform.js";
3
3
  import { ArrayObserver } from "../observation/arrays.js";
4
4
  import { Markup, nextId } from "./markup.js";
@@ -9,11 +9,17 @@ const defaultRepeatOptions = Object.freeze({
9
9
  positioning: false,
10
10
  recycle: true,
11
11
  });
12
- function bindWithoutPositioning(view, items, index, context) {
13
- view.bind(items[index], context);
12
+ function bindWithoutPositioning(view, items, index, controller) {
13
+ view.context.parent = controller.source;
14
+ view.context.parentContext = controller.context;
15
+ view.bind(items[index]);
14
16
  }
15
- function bindWithPositioning(view, items, index, context) {
16
- view.bind(items[index], context.createItemContext(index, items.length));
17
+ function bindWithPositioning(view, items, index, controller) {
18
+ view.context.parent = controller.source;
19
+ view.context.parentContext = controller.context;
20
+ view.context.length = items.length;
21
+ view.context.index = index;
22
+ view.bind(items[index]);
17
23
  }
18
24
  /**
19
25
  * A behavior that renders a template for each item in an array.
@@ -29,15 +35,11 @@ export class RepeatBehavior {
29
35
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
30
36
  * @param options - Options used to turn on special repeat features.
31
37
  */
32
- constructor(directive, location) {
38
+ constructor(directive) {
33
39
  this.directive = directive;
34
- this.location = location;
35
- this.source = null;
36
40
  this.views = [];
37
41
  this.items = null;
38
42
  this.itemsObserver = null;
39
- this.context = void 0;
40
- this.childContext = void 0;
41
43
  this.bindView = bindWithoutPositioning;
42
44
  this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
43
45
  this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
@@ -46,32 +48,26 @@ export class RepeatBehavior {
46
48
  }
47
49
  }
48
50
  /**
49
- * Bind this behavior to the source.
50
- * @param source - The source to bind to.
51
- * @param context - The execution context that the binding is operating within.
51
+ * Bind this behavior.
52
+ * @param controller - The view controller that manages the lifecycle of this behavior.
52
53
  */
53
- bind(source, context) {
54
- this.source = source;
55
- this.context = context;
56
- this.childContext = context.createChildContext(source);
57
- this.items = this.itemsBindingObserver.observe(source, this.context);
58
- this.template = this.templateBindingObserver.observe(source, this.context);
54
+ bind(controller) {
55
+ this.location = controller.targets[this.directive.nodeId];
56
+ this.controller = controller;
57
+ this.items = this.itemsBindingObserver.bind(controller);
58
+ this.template = this.templateBindingObserver.bind(controller);
59
59
  this.observeItems(true);
60
60
  this.refreshAllViews();
61
+ controller.onUnbind(this);
61
62
  }
62
63
  /**
63
- * Unbinds this behavior from the source.
64
- * @param source - The source to unbind from.
64
+ * Unbinds this behavior.
65
65
  */
66
66
  unbind() {
67
- this.source = null;
68
- this.items = null;
69
67
  if (this.itemsObserver !== null) {
70
68
  this.itemsObserver.unsubscribe(this);
71
69
  }
72
70
  this.unbindAllViews();
73
- this.itemsBindingObserver.dispose();
74
- this.templateBindingObserver.dispose();
75
71
  }
76
72
  /**
77
73
  * Handles changes in the array, its items, and the repeat template.
@@ -80,12 +76,12 @@ export class RepeatBehavior {
80
76
  */
81
77
  handleChange(source, args) {
82
78
  if (args === this.itemsBindingObserver) {
83
- this.items = this.itemsBindingObserver.observe(this.source, this.context);
79
+ this.items = this.itemsBindingObserver.bind(this.controller);
84
80
  this.observeItems();
85
81
  this.refreshAllViews();
86
82
  }
87
83
  else if (args === this.templateBindingObserver) {
88
- this.template = this.templateBindingObserver.observe(this.source, this.context);
84
+ this.template = this.templateBindingObserver.bind(this.controller);
89
85
  this.refreshAllViews(true);
90
86
  }
91
87
  else if (!args[0]) {
@@ -115,10 +111,10 @@ export class RepeatBehavior {
115
111
  }
116
112
  updateViews(splices) {
117
113
  const views = this.views;
118
- const childContext = this.childContext;
119
114
  const bindView = this.bindView;
120
115
  const items = this.items;
121
116
  const template = this.template;
117
+ const controller = this.controller;
122
118
  const recycle = this.directive.options.recycle;
123
119
  const leftoverViews = [];
124
120
  let leftoverIndex = 0;
@@ -130,13 +126,14 @@ export class RepeatBehavior {
130
126
  let addIndex = splice.index;
131
127
  const end = addIndex + splice.addedCount;
132
128
  const removedViews = views.splice(splice.index, removed.length);
133
- availableViews = leftoverViews.length + removedViews.length;
129
+ const totalAvailableViews = (availableViews =
130
+ leftoverViews.length + removedViews.length);
134
131
  for (; addIndex < end; ++addIndex) {
135
132
  const neighbor = views[addIndex];
136
133
  const location = neighbor ? neighbor.firstChild : this.location;
137
134
  let view;
138
135
  if (recycle && availableViews > 0) {
139
- if (removeIndex <= availableViews && removedViews.length > 0) {
136
+ if (removeIndex <= totalAvailableViews && removedViews.length > 0) {
140
137
  view = removedViews[removeIndex];
141
138
  removeIndex++;
142
139
  }
@@ -150,7 +147,7 @@ export class RepeatBehavior {
150
147
  view = template.create();
151
148
  }
152
149
  views.splice(addIndex, 0, view);
153
- bindView(view, items, addIndex, childContext);
150
+ bindView(view, items, addIndex, controller);
154
151
  view.insertBefore(location);
155
152
  }
156
153
  if (removedViews[removeIndex]) {
@@ -162,7 +159,9 @@ export class RepeatBehavior {
162
159
  }
163
160
  if (this.directive.options.positioning) {
164
161
  for (let i = 0, ii = views.length; i < ii; ++i) {
165
- views[i].context.updatePosition(i, ii);
162
+ const context = views[i].context;
163
+ context.length = i;
164
+ context.index = ii;
166
165
  }
167
166
  }
168
167
  }
@@ -171,7 +170,7 @@ export class RepeatBehavior {
171
170
  const template = this.template;
172
171
  const location = this.location;
173
172
  const bindView = this.bindView;
174
- const childContext = this.childContext;
173
+ const controller = this.controller;
175
174
  let itemsLength = items.length;
176
175
  let views = this.views;
177
176
  let viewsLength = views.length;
@@ -185,7 +184,7 @@ export class RepeatBehavior {
185
184
  this.views = views = new Array(itemsLength);
186
185
  for (let i = 0; i < itemsLength; ++i) {
187
186
  const view = template.create();
188
- bindView(view, items, i, childContext);
187
+ bindView(view, items, i, controller);
189
188
  views[i] = view;
190
189
  view.insertBefore(location);
191
190
  }
@@ -196,11 +195,11 @@ export class RepeatBehavior {
196
195
  for (; i < itemsLength; ++i) {
197
196
  if (i < viewsLength) {
198
197
  const view = views[i];
199
- bindView(view, items, i, childContext);
198
+ bindView(view, items, i, controller);
200
199
  }
201
200
  else {
202
201
  const view = template.create();
203
- bindView(view, items, i, childContext);
202
+ bindView(view, items, i, controller);
204
203
  views.push(view);
205
204
  view.insertBefore(location);
206
205
  }
@@ -250,8 +249,8 @@ export class RepeatDirective {
250
249
  * Creates a behavior for the provided target node.
251
250
  * @param target - The node instance to create the behavior for.
252
251
  */
253
- createBehavior(targets) {
254
- return new RepeatBehavior(this, targets[this.nodeId]);
252
+ createBehavior() {
253
+ return new RepeatBehavior(this);
255
254
  }
256
255
  }
257
256
  HTMLDirective.define(RepeatDirective);
@@ -1,5 +1,4 @@
1
1
  import { isFunction, isString } from "../interfaces.js";
2
- import { ExecutionContext } from "../observation/observable.js";
3
2
  import { bind, HTMLBindingDirective, oneTime } from "./binding.js";
4
3
  import { Compiler } from "./compiler.js";
5
4
  import { Aspect, Binding, HTMLDirective, } from "./html-directive.js";
@@ -36,9 +35,9 @@ export class ViewTemplate {
36
35
  * @param hostBindingTarget - An HTML element to target the host bindings at if different from the
37
36
  * host that the template is being attached to.
38
37
  */
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);
38
+ render(source, host, hostBindingTarget) {
39
+ const view = this.create(hostBindingTarget);
40
+ view.bind(source);
42
41
  view.appendTo(host);
43
42
  return view;
44
43
  }
@@ -1,3 +1,4 @@
1
+ import { ExecutionContext, Observable } from "../observation/observable.js";
1
2
  function removeNodeSequence(firstNode, lastNode) {
2
3
  const parent = firstNode.parentNode;
3
4
  let current = firstNode;
@@ -24,17 +25,83 @@ export class HTMLView {
24
25
  this.factories = factories;
25
26
  this.targets = targets;
26
27
  this.behaviors = null;
28
+ this.unbindables = [];
27
29
  /**
28
30
  * The data that the view is bound to.
29
31
  */
30
32
  this.source = null;
33
+ this.isBound = false;
34
+ this.selfContained = false;
31
35
  /**
32
- * The execution context the view is running within.
36
+ * The index of the current item within a repeat context.
33
37
  */
34
- this.context = null;
38
+ this.index = 0;
39
+ /**
40
+ * The length of the current collection within a repeat context.
41
+ */
42
+ this.length = 0;
35
43
  this.firstChild = fragment.firstChild;
36
44
  this.lastChild = fragment.lastChild;
37
45
  }
46
+ /**
47
+ * The execution context the view is running within.
48
+ */
49
+ get context() {
50
+ return this;
51
+ }
52
+ /**
53
+ * The current event within an event handler.
54
+ */
55
+ get event() {
56
+ return ExecutionContext.getEvent();
57
+ }
58
+ /**
59
+ * Indicates whether the current item within a repeat context
60
+ * has an even index.
61
+ */
62
+ get isEven() {
63
+ return this.index % 2 === 0;
64
+ }
65
+ /**
66
+ * Indicates whether the current item within a repeat context
67
+ * has an odd index.
68
+ */
69
+ get isOdd() {
70
+ return this.index % 2 !== 0;
71
+ }
72
+ /**
73
+ * Indicates whether the current item within a repeat context
74
+ * is the first item in the collection.
75
+ */
76
+ get isFirst() {
77
+ return this.index === 0;
78
+ }
79
+ /**
80
+ * Indicates whether the current item within a repeat context
81
+ * is somewhere in the middle of the collection.
82
+ */
83
+ get isInMiddle() {
84
+ return !this.isFirst && !this.isLast;
85
+ }
86
+ /**
87
+ * Indicates whether the current item within a repeat context
88
+ * is the last item in the collection.
89
+ */
90
+ get isLast() {
91
+ return this.index === this.length - 1;
92
+ }
93
+ /**
94
+ * Returns the typed event detail of a custom event.
95
+ */
96
+ eventDetail() {
97
+ return this.event.detail;
98
+ }
99
+ /**
100
+ * Returns the typed event target of the event.
101
+ */
102
+ eventTarget() {
103
+ return this.event.target;
104
+ }
38
105
  /**
39
106
  * Appends the view's DOM nodes to the referenced node.
40
107
  * @param node - The parent node to append the view's DOM nodes to.
@@ -89,58 +156,61 @@ export class HTMLView {
89
156
  removeNodeSequence(this.firstChild, this.lastChild);
90
157
  this.unbind();
91
158
  }
159
+ onUnbind(behavior) {
160
+ this.unbindables.push(behavior);
161
+ }
92
162
  /**
93
163
  * Binds a view's behaviors to its binding source.
94
164
  * @param source - The binding source for the view's binding behaviors.
95
165
  * @param context - The execution context to run the behaviors within.
96
166
  */
97
- bind(source, context) {
98
- let behaviors = this.behaviors;
167
+ bind(source) {
99
168
  const oldSource = this.source;
100
169
  if (oldSource === source) {
101
170
  return;
102
171
  }
172
+ let behaviors = this.behaviors;
103
173
  this.source = source;
104
- this.context = context;
105
- const targets = this.targets;
106
- if (oldSource !== null) {
107
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
108
- const current = behaviors[i];
109
- current.unbind(oldSource, context, targets);
110
- current.bind(source, context, targets);
111
- }
112
- }
113
- else if (behaviors === null) {
174
+ if (behaviors === null) {
114
175
  this.behaviors = behaviors = new Array(this.factories.length);
115
176
  const factories = this.factories;
116
177
  for (let i = 0, ii = factories.length; i < ii; ++i) {
117
- const behavior = factories[i].createBehavior(targets);
118
- behavior.bind(source, context, targets);
178
+ const behavior = factories[i].createBehavior();
179
+ behavior.bind(this);
119
180
  behaviors[i] = behavior;
120
181
  }
121
182
  }
122
183
  else {
184
+ if (oldSource !== null) {
185
+ this.evaluateUnbindables();
186
+ }
123
187
  for (let i = 0, ii = behaviors.length; i < ii; ++i) {
124
- behaviors[i].bind(source, context, targets);
188
+ behaviors[i].bind(this);
125
189
  }
126
190
  }
191
+ this.isBound = true;
127
192
  }
128
193
  /**
129
194
  * Unbinds a view's behaviors from its binding source.
130
195
  */
131
196
  unbind() {
197
+ if (!this.isBound) {
198
+ return;
199
+ }
132
200
  const oldSource = this.source;
133
201
  if (oldSource === null) {
134
202
  return;
135
203
  }
136
- const targets = this.targets;
137
- const context = this.context;
138
- const behaviors = this.behaviors;
139
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
140
- behaviors[i].unbind(oldSource, context, targets);
141
- }
204
+ this.evaluateUnbindables();
142
205
  this.source = null;
143
- this.context = null;
206
+ this.isBound = false;
207
+ }
208
+ evaluateUnbindables() {
209
+ const unbindables = this.unbindables;
210
+ for (let i = 0, ii = unbindables.length; i < ii; ++i) {
211
+ unbindables[i].unbind(this);
212
+ }
213
+ unbindables.length = 0;
144
214
  }
145
215
  /**
146
216
  * Efficiently disposes of a contiguous range of synthetic view instances.
@@ -156,3 +226,5 @@ export class HTMLView {
156
226
  }
157
227
  }
158
228
  }
229
+ Observable.defineProperty(HTMLView.prototype, "index");
230
+ Observable.defineProperty(HTMLView.prototype, "length");
@@ -1,2 +1,3 @@
1
1
  export { timeout } from "./timeout.js";
2
2
  export * from "./fixture.js";
3
+ export * from "./fakes.js";
@@ -0,0 +1,76 @@
1
+ import { ExecutionContext } from "../index.js";
2
+ export const Fake = Object.freeze({
3
+ executionContext(parent, parentContext) {
4
+ return {
5
+ /**
6
+ * The index of the current item within a repeat context.
7
+ */
8
+ index: 0,
9
+ /**
10
+ * The length of the current collection within a repeat context.
11
+ */
12
+ length: 0,
13
+ /**
14
+ * The parent data source within a nested context.
15
+ */
16
+ parent: parent,
17
+ /**
18
+ * The parent execution context when in nested context scenarios.
19
+ */
20
+ parentContext: parentContext,
21
+ /**
22
+ * The current event within an event handler.
23
+ */
24
+ get event() {
25
+ return ExecutionContext.getEvent();
26
+ },
27
+ /**
28
+ * Indicates whether the current item within a repeat context
29
+ * has an even index.
30
+ */
31
+ get isEven() {
32
+ return this.index % 2 === 0;
33
+ },
34
+ /**
35
+ * Indicates whether the current item within a repeat context
36
+ * has an odd index.
37
+ */
38
+ get isOdd() {
39
+ return this.index % 2 !== 0;
40
+ },
41
+ /**
42
+ * Indicates whether the current item within a repeat context
43
+ * is the first item in the collection.
44
+ */
45
+ get isFirst() {
46
+ return this.index === 0;
47
+ },
48
+ /**
49
+ * Indicates whether the current item within a repeat context
50
+ * is somewhere in the middle of the collection.
51
+ */
52
+ get isInMiddle() {
53
+ return !this.isFirst && !this.isLast;
54
+ },
55
+ /**
56
+ * Indicates whether the current item within a repeat context
57
+ * is the last item in the collection.
58
+ */
59
+ get isLast() {
60
+ return this.index === this.length - 1;
61
+ },
62
+ /**
63
+ * Returns the typed event detail of a custom event.
64
+ */
65
+ eventDetail() {
66
+ return this.event.detail;
67
+ },
68
+ /**
69
+ * Returns the typed event target of the event.
70
+ */
71
+ eventTarget() {
72
+ return this.event.target;
73
+ },
74
+ };
75
+ },
76
+ });
@@ -8,7 +8,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { FASTElementDefinition } from "../components/fast-definitions.js";
11
- import { ExecutionContext } from "../observation/observable.js";
12
11
  import { ViewTemplate } from "../templating/template.js";
13
12
  function findElement(view) {
14
13
  let current = view.firstChild;
@@ -38,7 +37,6 @@ export function fixture(templateNameOrType, options = {}) {
38
37
  const document = options.document || globalThis.document;
39
38
  const parent = options.parent || document.createElement("div");
40
39
  const source = options.source || {};
41
- const context = options.context || ExecutionContext.default;
42
40
  if (typeof templateNameOrType === "function") {
43
41
  const def = FASTElementDefinition.getByType(templateNameOrType);
44
42
  if (!def) {
@@ -53,7 +51,7 @@ export function fixture(templateNameOrType, options = {}) {
53
51
  const view = templateNameOrType.create();
54
52
  const element = findElement(view);
55
53
  let isConnected = false;
56
- view.bind(source, context);
54
+ view.bind(source);
57
55
  view.appendTo(parent);
58
56
  customElements.upgrade(parent);
59
57
  // Hook into the Microtask Queue to ensure the DOM is settled