@microsoft/fast-element 1.10.5 → 2.0.0-beta.10

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 (122) hide show
  1. package/.eslintrc.json +1 -12
  2. package/CHANGELOG.json +629 -6
  3. package/CHANGELOG.md +152 -5
  4. package/dist/dts/components/attributes.d.ts +14 -1
  5. package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +32 -32
  6. package/dist/dts/components/fast-definitions.d.ts +51 -11
  7. package/dist/dts/components/fast-element.d.ts +18 -23
  8. package/dist/dts/context.d.ts +157 -0
  9. package/dist/{esm/observation/behavior.js → dts/debug.d.ts} +0 -0
  10. package/dist/dts/di/di.d.ts +899 -0
  11. package/dist/dts/index.d.ts +17 -16
  12. package/dist/dts/index.debug.d.ts +2 -0
  13. package/dist/dts/index.rollup.d.ts +2 -0
  14. package/dist/dts/index.rollup.debug.d.ts +3 -0
  15. package/dist/dts/interfaces.d.ts +176 -0
  16. package/dist/dts/metadata.d.ts +25 -0
  17. package/dist/dts/observation/arrays.d.ts +207 -0
  18. package/dist/dts/observation/notifier.d.ts +18 -18
  19. package/dist/dts/observation/observable.d.ts +117 -34
  20. package/dist/dts/observation/update-queue.d.ts +40 -0
  21. package/dist/dts/pending-task.d.ts +20 -0
  22. package/dist/dts/platform.d.ts +23 -66
  23. package/dist/dts/polyfills.d.ts +8 -0
  24. package/dist/dts/state/exports.d.ts +3 -0
  25. package/dist/dts/state/reactive.d.ts +8 -0
  26. package/dist/dts/state/state.d.ts +141 -0
  27. package/dist/dts/state/visitor.d.ts +6 -0
  28. package/dist/dts/state/watch.d.ts +10 -0
  29. package/dist/dts/styles/css-directive.d.ts +44 -6
  30. package/dist/dts/styles/css.d.ts +19 -3
  31. package/dist/dts/styles/element-styles.d.ts +49 -63
  32. package/dist/dts/styles/host.d.ts +68 -0
  33. package/dist/dts/templating/binding-signal.d.ts +21 -0
  34. package/dist/dts/templating/binding-two-way.d.ts +39 -0
  35. package/dist/dts/templating/binding.d.ts +101 -70
  36. package/dist/dts/templating/children.d.ts +18 -15
  37. package/dist/dts/templating/compiler.d.ts +46 -28
  38. package/dist/dts/templating/dom.d.ts +41 -0
  39. package/dist/dts/templating/html-directive.d.ts +239 -45
  40. package/dist/dts/templating/markup.d.ts +48 -0
  41. package/dist/dts/templating/node-observation.d.ts +45 -30
  42. package/dist/dts/templating/ref.d.ts +6 -20
  43. package/dist/dts/templating/render.d.ts +272 -0
  44. package/dist/dts/templating/repeat.d.ts +36 -33
  45. package/dist/dts/templating/slotted.d.ts +13 -14
  46. package/dist/dts/templating/template.d.ts +28 -22
  47. package/dist/dts/templating/view.d.ts +82 -24
  48. package/dist/dts/templating/when.d.ts +3 -3
  49. package/dist/dts/testing/exports.d.ts +3 -0
  50. package/dist/dts/testing/fakes.d.ts +4 -0
  51. package/dist/dts/testing/fixture.d.ts +84 -0
  52. package/dist/dts/testing/timeout.d.ts +7 -0
  53. package/dist/{tsdoc-metadata.json → dts/tsdoc-metadata.json} +1 -1
  54. package/dist/dts/utilities.d.ts +22 -0
  55. package/dist/esm/components/attributes.js +38 -28
  56. package/dist/esm/components/{controller.js → element-controller.js} +150 -140
  57. package/dist/esm/components/fast-definitions.js +48 -46
  58. package/dist/esm/components/fast-element.js +31 -12
  59. package/dist/esm/context.js +163 -0
  60. package/dist/esm/debug.js +61 -0
  61. package/dist/esm/di/di.js +1435 -0
  62. package/dist/esm/index.debug.js +2 -0
  63. package/dist/esm/index.js +20 -14
  64. package/dist/esm/index.rollup.debug.js +3 -0
  65. package/dist/esm/index.rollup.js +2 -0
  66. package/dist/esm/interfaces.js +12 -1
  67. package/dist/esm/metadata.js +60 -0
  68. package/dist/esm/observation/arrays.js +570 -0
  69. package/dist/esm/observation/notifier.js +27 -35
  70. package/dist/esm/observation/observable.js +116 -149
  71. package/dist/esm/observation/update-queue.js +67 -0
  72. package/dist/esm/pending-task.js +16 -0
  73. package/dist/esm/platform.js +60 -42
  74. package/dist/esm/polyfills.js +85 -0
  75. package/dist/esm/state/exports.js +3 -0
  76. package/dist/esm/state/reactive.js +34 -0
  77. package/dist/esm/state/state.js +148 -0
  78. package/dist/esm/state/visitor.js +28 -0
  79. package/dist/esm/state/watch.js +36 -0
  80. package/dist/esm/styles/css-directive.js +29 -13
  81. package/dist/esm/styles/css.js +29 -42
  82. package/dist/esm/styles/element-styles.js +79 -104
  83. package/dist/esm/styles/host.js +1 -0
  84. package/dist/esm/templating/binding-signal.js +83 -0
  85. package/dist/esm/templating/binding-two-way.js +103 -0
  86. package/dist/esm/templating/binding.js +189 -159
  87. package/dist/esm/templating/children.js +33 -23
  88. package/dist/esm/templating/compiler.js +258 -152
  89. package/dist/esm/templating/dom.js +49 -0
  90. package/dist/esm/templating/html-directive.js +193 -36
  91. package/dist/esm/templating/markup.js +75 -0
  92. package/dist/esm/templating/node-observation.js +51 -45
  93. package/dist/esm/templating/ref.js +8 -25
  94. package/dist/esm/templating/render.js +391 -0
  95. package/dist/esm/templating/repeat.js +83 -79
  96. package/dist/esm/templating/slotted.js +23 -20
  97. package/dist/esm/templating/template.js +51 -93
  98. package/dist/esm/templating/view.js +125 -46
  99. package/dist/esm/templating/when.js +6 -4
  100. package/dist/esm/testing/exports.js +3 -0
  101. package/dist/esm/testing/fakes.js +76 -0
  102. package/dist/esm/testing/fixture.js +86 -0
  103. package/dist/esm/testing/timeout.js +24 -0
  104. package/dist/esm/utilities.js +44 -0
  105. package/dist/fast-element.api.json +12153 -5373
  106. package/dist/fast-element.d.ts +1448 -696
  107. package/dist/fast-element.debug.js +4107 -0
  108. package/dist/fast-element.debug.min.js +1 -0
  109. package/dist/fast-element.js +3817 -4029
  110. package/dist/fast-element.min.js +1 -1
  111. package/dist/fast-element.untrimmed.d.ts +2814 -0
  112. package/docs/api-report.md +567 -254
  113. package/docs/fast-element-2-changes.md +15 -0
  114. package/karma.conf.cjs +6 -17
  115. package/package.json +76 -15
  116. package/dist/dts/dom.d.ts +0 -112
  117. package/dist/dts/observation/array-change-records.d.ts +0 -48
  118. package/dist/dts/observation/array-observer.d.ts +0 -9
  119. package/dist/dts/observation/behavior.d.ts +0 -19
  120. package/dist/esm/dom.js +0 -207
  121. package/dist/esm/observation/array-change-records.js +0 -326
  122. package/dist/esm/observation/array-observer.js +0 -177
@@ -0,0 +1,391 @@
1
+ import { FASTElementDefinition } from "../components/fast-definitions.js";
2
+ import { isFunction, isString } from "../interfaces.js";
3
+ import { bind, normalizeBinding, oneTime, } from "./binding.js";
4
+ import { Binding, HTMLDirective, } from "./html-directive.js";
5
+ import { Markup } from "./markup.js";
6
+ import { html, } from "./template.js";
7
+ /**
8
+ * A Behavior that enables advanced rendering.
9
+ * @public
10
+ */
11
+ export class RenderBehavior {
12
+ /**
13
+ * Creates an instance of RenderBehavior.
14
+ * @param directive - The render directive that created this behavior.
15
+ */
16
+ constructor(directive) {
17
+ this.directive = directive;
18
+ this.location = null;
19
+ this.controller = null;
20
+ this.view = null;
21
+ this.data = null;
22
+ this.dataBindingObserver = directive.dataBinding.createObserver(directive, this);
23
+ this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
24
+ }
25
+ /**
26
+ * Bind this behavior.
27
+ * @param controller - The view controller that manages the lifecycle of this behavior.
28
+ */
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);
35
+ this.refreshView();
36
+ }
37
+ /**
38
+ * Unbinds this behavior.
39
+ * @param controller - The view controller that manages the lifecycle of this behavior.
40
+ */
41
+ unbind(controller) {
42
+ const view = this.view;
43
+ if (view !== null && view.isComposed) {
44
+ view.unbind();
45
+ view.needsBindOnly = true;
46
+ }
47
+ }
48
+ /** @internal */
49
+ handleChange(source, observer) {
50
+ if (observer === this.dataBindingObserver) {
51
+ this.data = this.dataBindingObserver.bind(this.controller);
52
+ }
53
+ if (this.directive.templateBindingDependsOnData ||
54
+ observer === this.templateBindingObserver) {
55
+ this.template = this.templateBindingObserver.bind(this.controller);
56
+ }
57
+ this.refreshView();
58
+ }
59
+ refreshView() {
60
+ let view = this.view;
61
+ const template = this.template;
62
+ if (view === null) {
63
+ this.view = view = template.create();
64
+ this.view.context.parent = this.controller.source;
65
+ this.view.context.parentContext = this.controller.context;
66
+ }
67
+ else {
68
+ // If there is a previous view, but it wasn't created
69
+ // from the same template as the new value, then we
70
+ // need to remove the old view if it's still in the DOM
71
+ // and create a new view from the template.
72
+ if (view.$fastTemplate !== template) {
73
+ if (view.isComposed) {
74
+ view.remove();
75
+ view.unbind();
76
+ }
77
+ this.view = view = template.create();
78
+ this.view.context.parent = this.controller.source;
79
+ this.view.context.parentContext = this.controller.context;
80
+ }
81
+ }
82
+ // It's possible that the value is the same as the previous template
83
+ // and that there's actually no need to compose it.
84
+ if (!view.isComposed) {
85
+ view.isComposed = true;
86
+ view.bind(this.data);
87
+ view.insertBefore(this.location);
88
+ view.$fastTemplate = template;
89
+ }
90
+ else if (view.needsBindOnly) {
91
+ view.needsBindOnly = false;
92
+ view.bind(this.data);
93
+ }
94
+ }
95
+ }
96
+ /**
97
+ * A Directive that enables use of the RenderBehavior.
98
+ * @public
99
+ */
100
+ export class RenderDirective {
101
+ /**
102
+ * Creates an instance of RenderDirective.
103
+ * @param dataBinding - A binding expression that returns the data to render.
104
+ * @param templateBinding - A binding expression that returns the template to use to render the data.
105
+ */
106
+ constructor(dataBinding, templateBinding, templateBindingDependsOnData) {
107
+ this.dataBinding = dataBinding;
108
+ this.templateBinding = templateBinding;
109
+ this.templateBindingDependsOnData = templateBindingDependsOnData;
110
+ }
111
+ /**
112
+ * Creates HTML to be used within a template.
113
+ * @param add - Can be used to add behavior factories to a template.
114
+ */
115
+ createHTML(add) {
116
+ return Markup.comment(add(this));
117
+ }
118
+ /**
119
+ * Creates a behavior.
120
+ * @param targets - The targets available for behaviors to be attached to.
121
+ */
122
+ createBehavior() {
123
+ return new RenderBehavior(this);
124
+ }
125
+ }
126
+ HTMLDirective.define(RenderDirective);
127
+ function isElementRenderOptions(object) {
128
+ return !!object.element || !!object.tagName;
129
+ }
130
+ const typeToInstructionLookup = new Map();
131
+ /* eslint @typescript-eslint/naming-convention: "off"*/
132
+ const defaultAttributes = { ":model": x => x };
133
+ const brand = Symbol("RenderInstruction");
134
+ const defaultViewName = "default-view";
135
+ const nullTemplate = html `
136
+  
137
+ `;
138
+ function instructionToTemplate(def) {
139
+ if (def === void 0) {
140
+ return nullTemplate;
141
+ }
142
+ return def.template;
143
+ }
144
+ function createElementTemplate(tagName, attributes, content) {
145
+ const markup = [];
146
+ const values = [];
147
+ if (attributes) {
148
+ const attrNames = Object.getOwnPropertyNames(attributes);
149
+ markup.push(`<${tagName}`);
150
+ for (let i = 0, ii = attrNames.length; i < ii; ++i) {
151
+ const name = attrNames[i];
152
+ if (i === 0) {
153
+ markup[0] = `${markup[0]} ${name}="`;
154
+ }
155
+ else {
156
+ markup.push(`" ${name}="`);
157
+ }
158
+ values.push(attributes[name]);
159
+ }
160
+ markup.push(`">`);
161
+ }
162
+ else {
163
+ markup.push(`<${tagName}>`);
164
+ }
165
+ if (content && isFunction(content.create)) {
166
+ values.push(content);
167
+ markup.push(`</${tagName}>`);
168
+ }
169
+ else {
170
+ const lastIndex = markup.length - 1;
171
+ markup[lastIndex] = `${markup[lastIndex]}${content !== null && content !== void 0 ? content : ""}</${tagName}>`;
172
+ }
173
+ return html(markup, ...values);
174
+ }
175
+ function create(options) {
176
+ var _a, _b;
177
+ const name = (_a = options.name) !== null && _a !== void 0 ? _a : defaultViewName;
178
+ let template;
179
+ if (isElementRenderOptions(options)) {
180
+ let tagName = options.tagName;
181
+ if (!tagName) {
182
+ const def = FASTElementDefinition.getByType(options.element);
183
+ if (def) {
184
+ tagName = def.name;
185
+ }
186
+ else {
187
+ throw new Error("Invalid element for model rendering.");
188
+ }
189
+ }
190
+ template = createElementTemplate(tagName, (_b = options.attributes) !== null && _b !== void 0 ? _b : defaultAttributes, options.content);
191
+ }
192
+ else {
193
+ template = options.template;
194
+ }
195
+ return {
196
+ brand,
197
+ type: options.type,
198
+ name,
199
+ template,
200
+ };
201
+ }
202
+ function instanceOf(object) {
203
+ return object && object.brand === brand;
204
+ }
205
+ function register(optionsOrInstruction) {
206
+ let lookup = typeToInstructionLookup.get(optionsOrInstruction.type);
207
+ if (lookup === void 0) {
208
+ typeToInstructionLookup.set(optionsOrInstruction.type, (lookup = Object.create(null)));
209
+ }
210
+ const instruction = instanceOf(optionsOrInstruction)
211
+ ? optionsOrInstruction
212
+ : create(optionsOrInstruction);
213
+ return (lookup[instruction.name] = instruction);
214
+ }
215
+ function getByType(type, name) {
216
+ const entries = typeToInstructionLookup.get(type);
217
+ if (entries === void 0) {
218
+ return void 0;
219
+ }
220
+ return entries[name !== null && name !== void 0 ? name : defaultViewName];
221
+ }
222
+ function getForInstance(object, name) {
223
+ if (object) {
224
+ return getByType(object.constructor, name);
225
+ }
226
+ return void 0;
227
+ }
228
+ /**
229
+ * Provides APIs for creating and interacting with render instructions.
230
+ * @public
231
+ */
232
+ export const RenderInstruction = Object.freeze({
233
+ /**
234
+ * Checks whether the provided object is a RenderInstruction.
235
+ * @param object - The object to check.
236
+ * @returns true if the object is a RenderInstruction; false otherwise
237
+ */
238
+ instanceOf,
239
+ /**
240
+ * Creates a RenderInstruction for a set of options.
241
+ * @param options - The options to use when creating the RenderInstruction.
242
+ */
243
+ create,
244
+ /**
245
+ * Creates a template based on a tag name.
246
+ * @param tagName - The tag name to use when creating the template.
247
+ * @param attributes - The attributes to apply to the element.
248
+ * @param content - The content to insert into the element.
249
+ * @returns A template based on the provided specifications.
250
+ */
251
+ createElementTemplate,
252
+ /**
253
+ * Creates and registers an instruction.
254
+ * @param options The options to use when creating the RenderInstruction.
255
+ * @remarks
256
+ * A previously created RenderInstruction can also be registered.
257
+ */
258
+ register,
259
+ /**
260
+ * Finds a previously registered RenderInstruction by type and optionally by name.
261
+ * @param type - The type to retrieve the RenderInstruction for.
262
+ * @param name - An optional name used in differentiating between multiple registered instructions.
263
+ * @returns The located RenderInstruction that matches the criteria or undefined if none is found.
264
+ */
265
+ getByType,
266
+ /**
267
+ * Finds a previously registered RenderInstruction for the instance's type and optionally by name.
268
+ * @param object - The instance to retrieve the RenderInstruction for.
269
+ * @param name - An optional name used in differentiating between multiple registered instructions.
270
+ * @returns The located RenderInstruction that matches the criteria or undefined if none is found.
271
+ */
272
+ getForInstance,
273
+ });
274
+ export function renderWith(value, name) {
275
+ return function (type) {
276
+ if (isFunction(value)) {
277
+ register({ type, element: value, name });
278
+ }
279
+ else if (isFunction(value.create)) {
280
+ register({ type, template: value, name });
281
+ }
282
+ else {
283
+ register(Object.assign({ type }, value));
284
+ }
285
+ };
286
+ }
287
+ /**
288
+ * @internal
289
+ */
290
+ export class NodeTemplate {
291
+ constructor(node) {
292
+ this.node = node;
293
+ node.$fastTemplate = this;
294
+ }
295
+ get context() {
296
+ // HACK
297
+ return this;
298
+ }
299
+ bind(source) { }
300
+ unbind() { }
301
+ insertBefore(refNode) {
302
+ refNode.parentNode.insertBefore(this.node, refNode);
303
+ }
304
+ remove() {
305
+ this.node.parentNode.removeChild(this.node);
306
+ }
307
+ create() {
308
+ return this;
309
+ }
310
+ }
311
+ /**
312
+ * Creates a RenderDirective for use in advanced rendering scenarios.
313
+ * @param value - The binding expression that returns the data to be rendered. The expression
314
+ * can also return a Node to render directly.
315
+ * @param template - A template to render the data with
316
+ * or a string to indicate which RenderInstruction to use when looking up a RenderInstruction.
317
+ * Expressions can also be provided to dynamically determine either the template or the name.
318
+ * @returns A RenderDirective suitable for use in a template.
319
+ * @remarks
320
+ * If no binding is provided, then a default binding that returns the source is created.
321
+ * If no template is provided, then a binding is created that will use registered
322
+ * RenderInstructions to determine the view.
323
+ * If the template binding returns a string, then it will be used to look up a
324
+ * RenderInstruction to determine the view.
325
+ * @public
326
+ */
327
+ export function render(value, template) {
328
+ let dataBinding;
329
+ if (value === void 0) {
330
+ dataBinding = oneTime((source) => source);
331
+ }
332
+ else {
333
+ dataBinding = normalizeBinding(value);
334
+ }
335
+ let templateBinding;
336
+ let templateBindingDependsOnData = false;
337
+ if (template === void 0) {
338
+ templateBindingDependsOnData = true;
339
+ templateBinding = oneTime((s, c) => {
340
+ var _a;
341
+ const data = dataBinding.evaluate(s, c);
342
+ if (data instanceof Node) {
343
+ return (_a = data.$fastTemplate) !== null && _a !== void 0 ? _a : new NodeTemplate(data);
344
+ }
345
+ return instructionToTemplate(getForInstance(data));
346
+ });
347
+ }
348
+ else if (isFunction(template)) {
349
+ templateBinding = bind((s, c) => {
350
+ var _a;
351
+ let result = template(s, c);
352
+ if (isString(result)) {
353
+ result = instructionToTemplate(getForInstance(dataBinding.evaluate(s, c), result));
354
+ }
355
+ else if (result instanceof Node) {
356
+ result = (_a = result.$fastTemplate) !== null && _a !== void 0 ? _a : new NodeTemplate(result);
357
+ }
358
+ return result;
359
+ }, true);
360
+ }
361
+ else if (isString(template)) {
362
+ templateBindingDependsOnData = true;
363
+ templateBinding = oneTime((s, c) => {
364
+ var _a;
365
+ const data = dataBinding.evaluate(s, c);
366
+ if (data instanceof Node) {
367
+ return (_a = data.$fastTemplate) !== null && _a !== void 0 ? _a : new NodeTemplate(data);
368
+ }
369
+ return instructionToTemplate(getForInstance(data, template));
370
+ });
371
+ }
372
+ else if (template instanceof Binding) {
373
+ const evaluateTemplate = template.evaluate;
374
+ template.evaluate = (s, c) => {
375
+ var _a;
376
+ let result = evaluateTemplate(s, c);
377
+ if (isString(result)) {
378
+ result = instructionToTemplate(getForInstance(dataBinding.evaluate(s, c), result));
379
+ }
380
+ else if (result instanceof Node) {
381
+ result = (_a = result.$fastTemplate) !== null && _a !== void 0 ? _a : new NodeTemplate(result);
382
+ }
383
+ return result;
384
+ };
385
+ templateBinding = template;
386
+ }
387
+ else {
388
+ templateBinding = oneTime((s, c) => template);
389
+ }
390
+ return new RenderDirective(dataBinding, templateBinding, templateBindingDependsOnData);
391
+ }
@@ -1,21 +1,25 @@
1
- import { DOM } from "../dom.js";
2
- import { Observable, } from "../observation/observable.js";
3
- import { enableArrayObservation } from "../observation/array-observer.js";
1
+ import { Observable } from "../observation/observable.js";
4
2
  import { emptyArray } from "../platform.js";
5
- import { HTMLDirective } from "./html-directive.js";
3
+ import { ArrayObserver } from "../observation/arrays.js";
4
+ import { Markup, nextId } from "./markup.js";
5
+ import { HTMLDirective, } from "./html-directive.js";
6
6
  import { HTMLView } from "./view.js";
7
+ import { normalizeBinding } from "./binding.js";
7
8
  const defaultRepeatOptions = Object.freeze({
8
9
  positioning: false,
9
10
  recycle: true,
10
11
  });
11
- function bindWithoutPositioning(view, items, index, context) {
12
- 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]);
13
16
  }
14
- function bindWithPositioning(view, items, index, context) {
15
- const childContext = Object.create(context);
16
- childContext.index = index;
17
- childContext.length = items.length;
18
- view.bind(items[index], childContext);
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]);
19
23
  }
20
24
  /**
21
25
  * A behavior that renders a template for each item in an array.
@@ -25,71 +29,67 @@ export class RepeatBehavior {
25
29
  /**
26
30
  * Creates an instance of RepeatBehavior.
27
31
  * @param location - The location in the DOM to render the repeat.
28
- * @param itemsBinding - The array to render.
32
+ * @param dataBinding - The array to render.
29
33
  * @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
30
34
  * @param templateBinding - The template to render for each item.
31
35
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
32
36
  * @param options - Options used to turn on special repeat features.
33
37
  */
34
- constructor(location, itemsBinding, isItemsBindingVolatile, templateBinding, isTemplateBindingVolatile, options) {
35
- this.location = location;
36
- this.itemsBinding = itemsBinding;
37
- this.templateBinding = templateBinding;
38
- this.options = options;
39
- this.source = null;
38
+ constructor(directive) {
39
+ this.directive = directive;
40
40
  this.views = [];
41
41
  this.items = null;
42
42
  this.itemsObserver = null;
43
- this.originalContext = void 0;
44
- this.childContext = void 0;
45
43
  this.bindView = bindWithoutPositioning;
46
- this.itemsBindingObserver = Observable.binding(itemsBinding, this, isItemsBindingVolatile);
47
- this.templateBindingObserver = Observable.binding(templateBinding, this, isTemplateBindingVolatile);
48
- if (options.positioning) {
44
+ this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
45
+ this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
46
+ if (directive.options.positioning) {
49
47
  this.bindView = bindWithPositioning;
50
48
  }
51
49
  }
52
50
  /**
53
- * Bind this behavior to the source.
54
- * @param source - The source to bind to.
55
- * @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.
56
53
  */
57
- bind(source, context) {
58
- this.source = source;
59
- this.originalContext = context;
60
- this.childContext = Object.create(context);
61
- this.childContext.parent = source;
62
- this.childContext.parentContext = this.originalContext;
63
- this.items = this.itemsBindingObserver.observe(source, this.originalContext);
64
- this.template = this.templateBindingObserver.observe(source, this.originalContext);
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);
65
59
  this.observeItems(true);
66
60
  this.refreshAllViews();
61
+ controller.onUnbind(this);
67
62
  }
68
63
  /**
69
- * Unbinds this behavior from the source.
70
- * @param source - The source to unbind from.
64
+ * Unbinds this behavior.
71
65
  */
72
66
  unbind() {
73
- this.source = null;
74
- this.items = null;
75
67
  if (this.itemsObserver !== null) {
76
68
  this.itemsObserver.unsubscribe(this);
77
69
  }
78
70
  this.unbindAllViews();
79
- this.itemsBindingObserver.disconnect();
80
- this.templateBindingObserver.disconnect();
81
71
  }
82
- /** @internal */
72
+ /**
73
+ * Handles changes in the array, its items, and the repeat template.
74
+ * @param source - The source of the change.
75
+ * @param args - The details about what was changed.
76
+ */
83
77
  handleChange(source, args) {
84
- if (source === this.itemsBinding) {
85
- this.items = this.itemsBindingObserver.observe(this.source, this.originalContext);
78
+ if (args === this.itemsBindingObserver) {
79
+ this.items = this.itemsBindingObserver.bind(this.controller);
86
80
  this.observeItems();
87
81
  this.refreshAllViews();
88
82
  }
89
- else if (source === this.templateBinding) {
90
- this.template = this.templateBindingObserver.observe(this.source, this.originalContext);
83
+ else if (args === this.templateBindingObserver) {
84
+ this.template = this.templateBindingObserver.bind(this.controller);
91
85
  this.refreshAllViews(true);
92
86
  }
87
+ else if (!args[0]) {
88
+ return;
89
+ }
90
+ else if (args[0].reset) {
91
+ this.refreshAllViews();
92
+ }
93
93
  else {
94
94
  this.updateViews(args);
95
95
  }
@@ -110,12 +110,12 @@ export class RepeatBehavior {
110
110
  }
111
111
  }
112
112
  updateViews(splices) {
113
- const childContext = this.childContext;
114
113
  const views = this.views;
115
114
  const bindView = this.bindView;
116
115
  const items = this.items;
117
116
  const template = this.template;
118
- const recycle = this.options.recycle;
117
+ const controller = this.controller;
118
+ const recycle = this.directive.options.recycle;
119
119
  const leftoverViews = [];
120
120
  let leftoverIndex = 0;
121
121
  let availableViews = 0;
@@ -126,13 +126,14 @@ export class RepeatBehavior {
126
126
  let addIndex = splice.index;
127
127
  const end = addIndex + splice.addedCount;
128
128
  const removedViews = views.splice(splice.index, removed.length);
129
- availableViews = leftoverViews.length + removedViews.length;
129
+ const totalAvailableViews = (availableViews =
130
+ leftoverViews.length + removedViews.length);
130
131
  for (; addIndex < end; ++addIndex) {
131
132
  const neighbor = views[addIndex];
132
133
  const location = neighbor ? neighbor.firstChild : this.location;
133
134
  let view;
134
135
  if (recycle && availableViews > 0) {
135
- if (removeIndex <= availableViews && removedViews.length > 0) {
136
+ if (removeIndex <= totalAvailableViews && removedViews.length > 0) {
136
137
  view = removedViews[removeIndex];
137
138
  removeIndex++;
138
139
  }
@@ -146,7 +147,7 @@ export class RepeatBehavior {
146
147
  view = template.create();
147
148
  }
148
149
  views.splice(addIndex, 0, view);
149
- bindView(view, items, addIndex, childContext);
150
+ bindView(view, items, addIndex, controller);
150
151
  view.insertBefore(location);
151
152
  }
152
153
  if (removedViews[removeIndex]) {
@@ -156,24 +157,24 @@ export class RepeatBehavior {
156
157
  for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
157
158
  leftoverViews[i].dispose();
158
159
  }
159
- if (this.options.positioning) {
160
+ if (this.directive.options.positioning) {
160
161
  for (let i = 0, ii = views.length; i < ii; ++i) {
161
- const currentContext = views[i].context;
162
- currentContext.length = ii;
163
- currentContext.index = i;
162
+ const context = views[i].context;
163
+ context.length = i;
164
+ context.index = ii;
164
165
  }
165
166
  }
166
167
  }
167
168
  refreshAllViews(templateChanged = false) {
168
169
  const items = this.items;
169
- const childContext = this.childContext;
170
170
  const template = this.template;
171
171
  const location = this.location;
172
172
  const bindView = this.bindView;
173
+ const controller = this.controller;
173
174
  let itemsLength = items.length;
174
175
  let views = this.views;
175
176
  let viewsLength = views.length;
176
- if (itemsLength === 0 || templateChanged || !this.options.recycle) {
177
+ if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
177
178
  // all views need to be removed
178
179
  HTMLView.disposeContiguousBatch(views);
179
180
  viewsLength = 0;
@@ -183,7 +184,7 @@ export class RepeatBehavior {
183
184
  this.views = views = new Array(itemsLength);
184
185
  for (let i = 0; i < itemsLength; ++i) {
185
186
  const view = template.create();
186
- bindView(view, items, i, childContext);
187
+ bindView(view, items, i, controller);
187
188
  views[i] = view;
188
189
  view.insertBefore(location);
189
190
  }
@@ -194,11 +195,11 @@ export class RepeatBehavior {
194
195
  for (; i < itemsLength; ++i) {
195
196
  if (i < viewsLength) {
196
197
  const view = views[i];
197
- bindView(view, items, i, childContext);
198
+ bindView(view, items, i, controller);
198
199
  }
199
200
  else {
200
201
  const view = template.create();
201
- bindView(view, items, i, childContext);
202
+ bindView(view, items, i, controller);
202
203
  views.push(view);
203
204
  view.insertBefore(location);
204
205
  }
@@ -220,46 +221,49 @@ export class RepeatBehavior {
220
221
  * A directive that configures list rendering.
221
222
  * @public
222
223
  */
223
- export class RepeatDirective extends HTMLDirective {
224
+ export class RepeatDirective {
224
225
  /**
225
226
  * Creates an instance of RepeatDirective.
226
- * @param itemsBinding - The binding that provides the array to render.
227
+ * @param dataBinding - The binding that provides the array to render.
227
228
  * @param templateBinding - The template binding used to obtain a template to render for each item in the array.
228
229
  * @param options - Options used to turn on special repeat features.
229
230
  */
230
- constructor(itemsBinding, templateBinding, options) {
231
- super();
232
- this.itemsBinding = itemsBinding;
231
+ constructor(dataBinding, templateBinding, options) {
232
+ this.dataBinding = dataBinding;
233
233
  this.templateBinding = templateBinding;
234
234
  this.options = options;
235
235
  /**
236
- * Creates a placeholder string based on the directive's index within the template.
237
- * @param index - The index of the directive within the template.
236
+ * The unique id of the factory.
238
237
  */
239
- this.createPlaceholder = DOM.createBlockPlaceholder;
240
- enableArrayObservation();
241
- this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
242
- this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
238
+ this.id = nextId();
239
+ ArrayObserver.enable();
240
+ }
241
+ /**
242
+ * Creates a placeholder string based on the directive's index within the template.
243
+ * @param index - The index of the directive within the template.
244
+ */
245
+ createHTML(add) {
246
+ return Markup.comment(add(this));
243
247
  }
244
248
  /**
245
249
  * Creates a behavior for the provided target node.
246
250
  * @param target - The node instance to create the behavior for.
247
251
  */
248
- createBehavior(target) {
249
- return new RepeatBehavior(target, this.itemsBinding, this.isItemsBindingVolatile, this.templateBinding, this.isTemplateBindingVolatile, this.options);
252
+ createBehavior() {
253
+ return new RepeatBehavior(this);
250
254
  }
251
255
  }
256
+ HTMLDirective.define(RepeatDirective);
252
257
  /**
253
258
  * A directive that enables list rendering.
254
- * @param itemsBinding - The array to render.
255
- * @param templateOrTemplateBinding - The template or a template binding used obtain a template
259
+ * @param items - The array to render.
260
+ * @param template - The template or a template binding used obtain a template
256
261
  * to render for each item in the array.
257
262
  * @param options - Options used to turn on special repeat features.
258
263
  * @public
259
264
  */
260
- export function repeat(itemsBinding, templateOrTemplateBinding, options = defaultRepeatOptions) {
261
- const templateBinding = typeof templateOrTemplateBinding === "function"
262
- ? templateOrTemplateBinding
263
- : () => templateOrTemplateBinding;
264
- return new RepeatDirective(itemsBinding, templateBinding, Object.assign(Object.assign({}, defaultRepeatOptions), options));
265
+ export function repeat(items, template, options = defaultRepeatOptions) {
266
+ const dataBinding = normalizeBinding(items);
267
+ const templateBinding = normalizeBinding(template);
268
+ return new RepeatDirective(dataBinding, templateBinding, Object.assign(Object.assign({}, defaultRepeatOptions), options));
265
269
  }