@microsoft/fast-element 2.0.0-beta.6 → 2.0.0-beta.8

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 (64) hide show
  1. package/CHANGELOG.json +78 -0
  2. package/CHANGELOG.md +25 -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/observation/observable.d.ts +86 -47
  10. package/dist/dts/pending-task.d.ts +20 -0
  11. package/dist/dts/platform.d.ts +6 -0
  12. package/dist/dts/styles/css-directive.d.ts +2 -2
  13. package/dist/dts/styles/element-styles.d.ts +3 -3
  14. package/dist/dts/styles/host.d.ts +68 -0
  15. package/dist/dts/templating/binding-signal.d.ts +2 -2
  16. package/dist/dts/templating/binding-two-way.d.ts +11 -3
  17. package/dist/dts/templating/binding.d.ts +21 -119
  18. package/dist/dts/templating/children.d.ts +1 -1
  19. package/dist/dts/templating/html-directive.d.ts +69 -39
  20. package/dist/dts/templating/node-observation.d.ts +4 -5
  21. package/dist/dts/templating/ref.d.ts +5 -13
  22. package/dist/dts/templating/render.d.ts +15 -20
  23. package/dist/dts/templating/repeat.d.ts +11 -16
  24. package/dist/dts/templating/slotted.d.ts +1 -1
  25. package/dist/dts/templating/template.d.ts +4 -4
  26. package/dist/dts/templating/view.d.ts +68 -9
  27. package/dist/dts/templating/when.d.ts +1 -1
  28. package/dist/dts/testing/exports.d.ts +1 -0
  29. package/dist/dts/testing/fakes.d.ts +4 -0
  30. package/dist/dts/testing/fixture.d.ts +0 -6
  31. package/dist/esm/components/attributes.js +13 -4
  32. package/dist/esm/components/{controller.js → element-controller.js} +95 -105
  33. package/dist/esm/components/fast-definitions.js +3 -1
  34. package/dist/esm/components/fast-element.js +4 -4
  35. package/dist/esm/di/di.js +87 -3
  36. package/dist/esm/index.js +2 -1
  37. package/dist/esm/observation/observable.js +59 -126
  38. package/dist/esm/pending-task.js +16 -0
  39. package/dist/esm/platform.js +24 -0
  40. package/dist/esm/styles/css.js +4 -4
  41. package/dist/esm/{observation/behavior.js → styles/host.js} +0 -0
  42. package/dist/esm/templating/binding-signal.js +21 -17
  43. package/dist/esm/templating/binding-two-way.js +32 -27
  44. package/dist/esm/templating/binding.js +73 -177
  45. package/dist/esm/templating/html-directive.js +78 -7
  46. package/dist/esm/templating/node-observation.js +9 -8
  47. package/dist/esm/templating/ref.js +4 -12
  48. package/dist/esm/templating/render.js +30 -31
  49. package/dist/esm/templating/repeat.js +37 -38
  50. package/dist/esm/templating/template.js +3 -4
  51. package/dist/esm/templating/view.js +98 -29
  52. package/dist/esm/testing/exports.js +1 -0
  53. package/dist/esm/testing/fakes.js +76 -0
  54. package/dist/esm/testing/fixture.js +1 -3
  55. package/dist/fast-element.api.json +5720 -5385
  56. package/dist/fast-element.d.ts +510 -399
  57. package/dist/fast-element.debug.js +497 -514
  58. package/dist/fast-element.debug.min.js +1 -1
  59. package/dist/fast-element.js +497 -514
  60. package/dist/fast-element.min.js +1 -1
  61. package/dist/fast-element.untrimmed.d.ts +519 -405
  62. package/docs/api-report.md +197 -129
  63. package/package.json +5 -1
  64. package/dist/dts/observation/behavior.d.ts +0 -19
@@ -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,58 @@ 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;
99
- const oldSource = this.source;
100
- if (oldSource === source) {
167
+ bind(source) {
168
+ if (this.source === source) {
101
169
  return;
102
170
  }
103
- 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) {
171
+ let behaviors = this.behaviors;
172
+ if (behaviors === null) {
173
+ this.source = source;
114
174
  this.behaviors = behaviors = new Array(this.factories.length);
115
175
  const factories = this.factories;
116
176
  for (let i = 0, ii = factories.length; i < ii; ++i) {
117
- const behavior = factories[i].createBehavior(targets);
118
- behavior.bind(source, context, targets);
177
+ const behavior = factories[i].createBehavior();
178
+ behavior.bind(this);
119
179
  behaviors[i] = behavior;
120
180
  }
121
181
  }
122
182
  else {
183
+ if (this.source !== null) {
184
+ this.evaluateUnbindables();
185
+ }
186
+ this.isBound = false;
187
+ this.source = source;
123
188
  for (let i = 0, ii = behaviors.length; i < ii; ++i) {
124
- behaviors[i].bind(source, context, targets);
189
+ behaviors[i].bind(this);
125
190
  }
126
191
  }
192
+ this.isBound = true;
127
193
  }
128
194
  /**
129
195
  * Unbinds a view's behaviors from its binding source.
130
196
  */
131
197
  unbind() {
132
- const oldSource = this.source;
133
- if (oldSource === null) {
198
+ if (!this.isBound || this.source === null) {
134
199
  return;
135
200
  }
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
- }
201
+ this.evaluateUnbindables();
142
202
  this.source = null;
143
- this.context = null;
203
+ this.isBound = false;
204
+ }
205
+ evaluateUnbindables() {
206
+ const unbindables = this.unbindables;
207
+ for (let i = 0, ii = unbindables.length; i < ii; ++i) {
208
+ unbindables[i].unbind(this);
209
+ }
210
+ unbindables.length = 0;
144
211
  }
145
212
  /**
146
213
  * Efficiently disposes of a contiguous range of synthetic view instances.
@@ -156,3 +223,5 @@ export class HTMLView {
156
223
  }
157
224
  }
158
225
  }
226
+ Observable.defineProperty(HTMLView.prototype, "index");
227
+ 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