@microsoft/fast-element 1.10.0 → 2.0.0-beta.1

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 (97) hide show
  1. package/.eslintrc.json +1 -12
  2. package/CHANGELOG.json +396 -1
  3. package/CHANGELOG.md +68 -2
  4. package/README.md +2 -2
  5. package/dist/dts/components/attributes.d.ts +4 -1
  6. package/dist/dts/components/controller.d.ts +12 -11
  7. package/dist/dts/components/fast-definitions.d.ts +8 -2
  8. package/dist/dts/components/fast-element.d.ts +5 -4
  9. package/dist/dts/debug.d.ts +1 -0
  10. package/dist/dts/hooks.d.ts +20 -0
  11. package/dist/dts/index.d.ts +16 -15
  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 +144 -0
  16. package/dist/dts/observation/arrays.d.ts +207 -0
  17. package/dist/dts/observation/behavior.d.ts +5 -5
  18. package/dist/dts/observation/notifier.d.ts +18 -18
  19. package/dist/dts/observation/observable.d.ts +86 -29
  20. package/dist/dts/observation/splice-strategies.d.ts +13 -0
  21. package/dist/dts/observation/update-queue.d.ts +40 -0
  22. package/dist/dts/platform.d.ts +18 -67
  23. package/dist/dts/polyfills.d.ts +8 -0
  24. package/dist/dts/styles/css-directive.d.ts +43 -5
  25. package/dist/dts/styles/css.d.ts +19 -3
  26. package/dist/dts/styles/element-styles.d.ts +42 -62
  27. package/dist/dts/templating/binding.d.ts +320 -64
  28. package/dist/dts/templating/children.d.ts +18 -15
  29. package/dist/dts/templating/compiler.d.ts +47 -28
  30. package/dist/dts/templating/dom.d.ts +41 -0
  31. package/dist/dts/templating/html-directive.d.ts +179 -43
  32. package/dist/dts/templating/markup.d.ts +48 -0
  33. package/dist/dts/templating/node-observation.d.ts +45 -29
  34. package/dist/dts/templating/ref.d.ts +6 -12
  35. package/dist/dts/templating/repeat.d.ts +72 -14
  36. package/dist/dts/templating/slotted.d.ts +13 -14
  37. package/dist/dts/templating/template.d.ts +78 -23
  38. package/dist/dts/templating/view.d.ts +16 -23
  39. package/dist/dts/utilities.d.ts +40 -0
  40. package/dist/esm/components/attributes.js +25 -24
  41. package/dist/esm/components/controller.js +77 -57
  42. package/dist/esm/components/fast-definitions.js +14 -22
  43. package/dist/esm/debug.js +29 -0
  44. package/dist/esm/hooks.js +32 -0
  45. package/dist/esm/index.debug.js +2 -0
  46. package/dist/esm/index.js +19 -14
  47. package/dist/esm/index.rollup.debug.js +3 -0
  48. package/dist/esm/index.rollup.js +2 -0
  49. package/dist/esm/interfaces.js +8 -1
  50. package/dist/esm/observation/arrays.js +269 -0
  51. package/dist/esm/observation/notifier.js +75 -83
  52. package/dist/esm/observation/observable.js +80 -107
  53. package/dist/esm/observation/{array-change-records.js → splice-strategies.js} +136 -62
  54. package/dist/esm/observation/update-queue.js +67 -0
  55. package/dist/esm/platform.js +36 -42
  56. package/dist/esm/polyfills.js +85 -0
  57. package/dist/esm/styles/css-directive.js +29 -13
  58. package/dist/esm/styles/css.js +27 -40
  59. package/dist/esm/styles/element-styles.js +65 -104
  60. package/dist/esm/templating/binding.js +465 -155
  61. package/dist/esm/templating/children.js +33 -23
  62. package/dist/esm/templating/compiler.js +235 -152
  63. package/dist/esm/templating/dom.js +49 -0
  64. package/dist/esm/templating/html-directive.js +125 -40
  65. package/dist/esm/templating/markup.js +75 -0
  66. package/dist/esm/templating/node-observation.js +50 -45
  67. package/dist/esm/templating/ref.js +7 -16
  68. package/dist/esm/templating/repeat.js +38 -43
  69. package/dist/esm/templating/slotted.js +23 -20
  70. package/dist/esm/templating/template.js +71 -95
  71. package/dist/esm/templating/view.js +44 -43
  72. package/dist/esm/templating/when.js +2 -1
  73. package/dist/esm/utilities.js +139 -0
  74. package/dist/fast-element.api.json +14062 -5235
  75. package/dist/fast-element.d.ts +1434 -579
  76. package/dist/fast-element.debug.js +3824 -0
  77. package/dist/fast-element.debug.min.js +1 -0
  78. package/dist/fast-element.js +3565 -4014
  79. package/dist/fast-element.min.js +1 -1
  80. package/dist/fast-element.untrimmed.d.ts +2908 -0
  81. package/dist/tsdoc-metadata.json +1 -1
  82. package/docs/api-report.md +590 -231
  83. package/docs/fast-element-2-changes.md +15 -0
  84. package/docs/guide/declaring-templates.md +5 -4
  85. package/docs/guide/defining-elements.md +3 -2
  86. package/docs/guide/leveraging-css.md +1 -0
  87. package/docs/guide/next-steps.md +3 -2
  88. package/docs/guide/observables-and-state.md +2 -1
  89. package/docs/guide/using-directives.md +2 -1
  90. package/docs/guide/working-with-shadow-dom.md +1 -0
  91. package/karma.conf.cjs +6 -17
  92. package/package.json +48 -14
  93. package/dist/dts/dom.d.ts +0 -112
  94. package/dist/dts/observation/array-change-records.d.ts +0 -48
  95. package/dist/dts/observation/array-observer.d.ts +0 -9
  96. package/dist/esm/dom.js +0 -207
  97. package/dist/esm/observation/array-observer.js +0 -173
@@ -1,64 +1,149 @@
1
- import { DOM } from "../dom.js";
1
+ import { createTypeRegistry } from "../platform.js";
2
+ import { Markup } from "./markup.js";
3
+ const registry = createTypeRegistry();
2
4
  /**
3
5
  * Instructs the template engine to apply behavior to a node.
4
6
  * @public
5
7
  */
6
- export class HTMLDirective {
7
- constructor() {
8
- /**
9
- * The index of the DOM node to which the created behavior will apply.
10
- */
11
- this.targetIndex = 0;
12
- }
13
- }
8
+ export const HTMLDirective = Object.freeze({
9
+ /**
10
+ * Gets the directive definition associated with the instance.
11
+ * @param instance - The directive instance to retrieve the definition for.
12
+ */
13
+ getForInstance: registry.getForInstance,
14
+ /**
15
+ * Gets the directive definition associated with the specified type.
16
+ * @param type - The directive type to retrieve the definition for.
17
+ */
18
+ getByType: registry.getByType,
19
+ /**
20
+ * Defines an HTMLDirective based on the options.
21
+ * @param type - The type to define as a directive.
22
+ * @param options - Options that specify the directive's application.
23
+ */
24
+ define(type, options) {
25
+ options = options || {};
26
+ options.type = type;
27
+ registry.register(options);
28
+ return type;
29
+ },
30
+ });
14
31
  /**
15
- * A {@link HTMLDirective} that targets a named attribute or property on a node.
32
+ * Decorator: Defines an HTMLDirective.
33
+ * @param options - Provides options that specify the directive's application.
16
34
  * @public
17
35
  */
18
- export class TargetedHTMLDirective extends HTMLDirective {
19
- constructor() {
20
- super(...arguments);
21
- /**
22
- * Creates a placeholder string based on the directive's index within the template.
23
- * @param index - The index of the directive within the template.
24
- */
25
- this.createPlaceholder = DOM.createInterpolationPlaceholder;
26
- }
36
+ export function htmlDirective(options) {
37
+ /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
38
+ return function (type) {
39
+ HTMLDirective.define(type, options);
40
+ };
27
41
  }
28
42
  /**
29
- * A directive that attaches special behavior to an element via a custom attribute.
43
+ * The type of HTML aspect to target.
30
44
  * @public
31
45
  */
32
- export class AttachedBehaviorHTMLDirective extends HTMLDirective {
46
+ export const Aspect = Object.freeze({
47
+ /**
48
+ * Not aspected.
49
+ */
50
+ none: 0,
51
+ /**
52
+ * An attribute.
53
+ */
54
+ attribute: 1,
55
+ /**
56
+ * A boolean attribute.
57
+ */
58
+ booleanAttribute: 2,
59
+ /**
60
+ * A property.
61
+ */
62
+ property: 3,
63
+ /**
64
+ * Content
65
+ */
66
+ content: 4,
67
+ /**
68
+ * A token list.
69
+ */
70
+ tokenList: 5,
71
+ /**
72
+ * An event.
73
+ */
74
+ event: 6,
33
75
  /**
34
76
  *
35
- * @param name - The name of the behavior; used as a custom attribute on the element.
36
- * @param behavior - The behavior to instantiate and attach to the element.
37
- * @param options - Options to pass to the behavior during creation.
77
+ * @param directive - The directive to assign the aspect to.
78
+ * @param value - The value to base the aspect determination on.
38
79
  */
39
- constructor(name, behavior, options) {
40
- super();
41
- this.name = name;
42
- this.behavior = behavior;
80
+ assign(directive, value) {
81
+ directive.sourceAspect = value;
82
+ if (!value) {
83
+ return;
84
+ }
85
+ switch (value[0]) {
86
+ case ":":
87
+ directive.targetAspect = value.substring(1);
88
+ switch (directive.targetAspect) {
89
+ case "innerHTML":
90
+ directive.aspectType = Aspect.property;
91
+ break;
92
+ case "classList":
93
+ directive.aspectType = Aspect.tokenList;
94
+ break;
95
+ default:
96
+ directive.aspectType = Aspect.property;
97
+ break;
98
+ }
99
+ break;
100
+ case "?":
101
+ directive.targetAspect = value.substring(1);
102
+ directive.aspectType = Aspect.booleanAttribute;
103
+ break;
104
+ case "@":
105
+ directive.targetAspect = value.substring(1);
106
+ directive.aspectType = Aspect.event;
107
+ break;
108
+ default:
109
+ if (value === "class") {
110
+ directive.targetAspect = "className";
111
+ directive.aspectType = Aspect.property;
112
+ }
113
+ else {
114
+ directive.targetAspect = value;
115
+ directive.aspectType = Aspect.attribute;
116
+ }
117
+ break;
118
+ }
119
+ },
120
+ });
121
+ /**
122
+ * A base class used for attribute directives that don't need internal state.
123
+ * @public
124
+ */
125
+ export class StatelessAttachedAttributeDirective {
126
+ /**
127
+ * Creates an instance of RefDirective.
128
+ * @param options - The options to use in configuring the directive.
129
+ */
130
+ constructor(options) {
43
131
  this.options = options;
44
132
  }
45
133
  /**
46
- * Creates a placeholder string based on the directive's index within the template.
47
- * @param index - The index of the directive within the template.
48
- * @remarks
49
- * Creates a custom attribute placeholder.
134
+ * Creates a behavior.
135
+ * @param targets - The targets available for behaviors to be attached to.
50
136
  */
51
- createPlaceholder(index) {
52
- return DOM.createCustomAttributePlaceholder(this.name, index);
137
+ createBehavior(targets) {
138
+ return this;
53
139
  }
54
140
  /**
55
- * Creates a behavior for the provided target node.
56
- * @param target - The node instance to create the behavior for.
141
+ * Creates a placeholder string based on the directive's index within the template.
142
+ * @param index - The index of the directive within the template.
57
143
  * @remarks
58
- * Creates an instance of the `behavior` type this directive was constructed with
59
- * and passes the target and options to that `behavior`'s constructor.
144
+ * Creates a custom attribute placeholder.
60
145
  */
61
- createBehavior(target) {
62
- return new this.behavior(target, this.options);
146
+ createHTML(add) {
147
+ return Markup.attribute(add(this));
63
148
  }
64
149
  }
@@ -0,0 +1,75 @@
1
+ const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
2
+ const interpolationStart = `${marker}{`;
3
+ const interpolationEnd = `}${marker}`;
4
+ const interpolationEndLength = interpolationEnd.length;
5
+ let id = 0;
6
+ /** @internal */
7
+ export const nextId = () => `${marker}-${++id}`;
8
+ /**
9
+ * Common APIs related to markup generation.
10
+ * @public
11
+ */
12
+ export const Markup = Object.freeze({
13
+ /**
14
+ * Creates a placeholder string suitable for marking out a location *within*
15
+ * an attribute value or HTML content.
16
+ * @param index - The directive index to create the placeholder for.
17
+ * @remarks
18
+ * Used internally by binding directives.
19
+ */
20
+ interpolation: (id) => `${interpolationStart}${id}${interpolationEnd}`,
21
+ /**
22
+ * Creates a placeholder that manifests itself as an attribute on an
23
+ * element.
24
+ * @param attributeName - The name of the custom attribute.
25
+ * @param index - The directive index to create the placeholder for.
26
+ * @remarks
27
+ * Used internally by attribute directives such as `ref`, `slotted`, and `children`.
28
+ */
29
+ attribute: (id) => `${nextId()}="${interpolationStart}${id}${interpolationEnd}"`,
30
+ /**
31
+ * Creates a placeholder that manifests itself as a marker within the DOM structure.
32
+ * @param index - The directive index to create the placeholder for.
33
+ * @remarks
34
+ * Used internally by structural directives such as `repeat`.
35
+ */
36
+ comment: (id) => `<!--${interpolationStart}${id}${interpolationEnd}-->`,
37
+ });
38
+ /**
39
+ * Common APIs related to content parsing.
40
+ * @public
41
+ */
42
+ export const Parser = Object.freeze({
43
+ /**
44
+ * Parses text content or HTML attribute content, separating out the static strings
45
+ * from the directives.
46
+ * @param value - The content or attribute string to parse.
47
+ * @param factories - A list of directives to search for in the string.
48
+ * @returns A heterogeneous array of static strings interspersed with
49
+ * directives or null if no directives are found in the string.
50
+ */
51
+ parse(value, factories) {
52
+ const parts = value.split(interpolationStart);
53
+ if (parts.length === 1) {
54
+ return null;
55
+ }
56
+ const result = [];
57
+ for (let i = 0, ii = parts.length; i < ii; ++i) {
58
+ const current = parts[i];
59
+ const index = current.indexOf(interpolationEnd);
60
+ let literal;
61
+ if (index === -1) {
62
+ literal = current;
63
+ }
64
+ else {
65
+ const factoryId = current.substring(0, index);
66
+ result.push(factories[factoryId]);
67
+ literal = current.substring(index + interpolationEndLength);
68
+ }
69
+ if (literal !== "") {
70
+ result.push(literal);
71
+ }
72
+ }
73
+ return result;
74
+ },
75
+ });
@@ -1,72 +1,77 @@
1
- import { Observable } from "../observation/observable.js";
2
1
  import { emptyArray } from "../platform.js";
2
+ import { StatelessAttachedAttributeDirective, } from "./html-directive.js";
3
+ const selectElements = (value) => value.nodeType === 1;
3
4
  /**
4
5
  * Creates a function that can be used to filter a Node array, selecting only elements.
5
6
  * @param selector - An optional selector to restrict the filter to.
6
7
  * @public
7
8
  */
8
- export function elements(selector) {
9
- if (selector) {
10
- return function (value, index, array) {
11
- return value.nodeType === 1 && value.matches(selector);
12
- };
13
- }
14
- return function (value, index, array) {
15
- return value.nodeType === 1;
16
- };
17
- }
9
+ export const elements = (selector) => selector
10
+ ? value => value.nodeType === 1 && value.matches(selector)
11
+ : selectElements;
18
12
  /**
19
13
  * A base class for node observation.
20
- * @internal
14
+ * @public
15
+ * @remarks
16
+ * Internally used by the SlottedDirective and the ChildrenDirective.
21
17
  */
22
- export class NodeObservationBehavior {
23
- /**
24
- * Creates an instance of NodeObservationBehavior.
25
- * @param target - The target to assign the nodes property on.
26
- * @param options - The options to use in configuring node observation.
27
- */
28
- constructor(target, options) {
29
- this.target = target;
30
- this.options = options;
31
- this.source = null;
18
+ export class NodeObservationDirective extends StatelessAttachedAttributeDirective {
19
+ constructor() {
20
+ super(...arguments);
21
+ this.sourceProperty = `${this.id}-s`;
32
22
  }
33
23
  /**
34
24
  * Bind this behavior to the source.
35
25
  * @param source - The source to bind to.
36
26
  * @param context - The execution context that the binding is operating within.
27
+ * @param targets - The targets that behaviors in a view can attach to.
37
28
  */
38
- bind(source) {
39
- const name = this.options.property;
40
- this.shouldUpdate = Observable.getAccessors(source).some((x) => x.name === name);
41
- this.source = source;
42
- this.updateTarget(this.computeNodes());
43
- if (this.shouldUpdate) {
44
- this.observe();
45
- }
29
+ bind(source, context, targets) {
30
+ const target = targets[this.nodeId];
31
+ target[this.sourceProperty] = source;
32
+ this.updateTarget(source, this.computeNodes(target));
33
+ this.observe(target);
46
34
  }
47
35
  /**
48
36
  * Unbinds this behavior from the source.
49
37
  * @param source - The source to unbind from.
38
+ * @param context - The execution context that the binding is operating within.
39
+ * @param targets - The targets that behaviors in a view can attach to.
50
40
  */
51
- unbind() {
52
- this.updateTarget(emptyArray);
53
- this.source = null;
54
- if (this.shouldUpdate) {
55
- this.disconnect();
56
- }
41
+ unbind(source, context, targets) {
42
+ const target = targets[this.nodeId];
43
+ this.updateTarget(source, emptyArray);
44
+ this.disconnect(target);
45
+ target[this.sourceProperty] = null;
57
46
  }
58
- /** @internal */
59
- handleEvent() {
60
- this.updateTarget(this.computeNodes());
47
+ /**
48
+ * Gets the data source for the target.
49
+ * @param target - The target to get the source for.
50
+ * @returns The source.
51
+ */
52
+ getSource(target) {
53
+ return target[this.sourceProperty];
54
+ }
55
+ /**
56
+ * Updates the source property with the computed nodes.
57
+ * @param source - The source object to assign the nodes property to.
58
+ * @param value - The nodes to assign to the source object property.
59
+ */
60
+ updateTarget(source, value) {
61
+ source[this.options.property] = value;
61
62
  }
62
- computeNodes() {
63
- let nodes = this.getNodes();
64
- if (this.options.filter !== void 0) {
63
+ /**
64
+ * Computes the set of nodes that should be assigned to the source property.
65
+ * @param target - The target to compute the nodes for.
66
+ * @returns The computed nodes.
67
+ * @remarks
68
+ * Applies filters if provided.
69
+ */
70
+ computeNodes(target) {
71
+ let nodes = this.getNodes(target);
72
+ if ("filter" in this.options) {
65
73
  nodes = nodes.filter(this.options.filter);
66
74
  }
67
75
  return nodes;
68
76
  }
69
- updateTarget(value) {
70
- this.source[this.options.property] = value;
71
- }
72
77
  }
@@ -1,25 +1,17 @@
1
- import { AttachedBehaviorHTMLDirective } from "./html-directive.js";
1
+ import { HTMLDirective, StatelessAttachedAttributeDirective, } from "./html-directive.js";
2
2
  /**
3
3
  * The runtime behavior for template references.
4
4
  * @public
5
5
  */
6
- export class RefBehavior {
7
- /**
8
- * Creates an instance of RefBehavior.
9
- * @param target - The element to reference.
10
- * @param propertyName - The name of the property to assign the reference to.
11
- */
12
- constructor(target, propertyName) {
13
- this.target = target;
14
- this.propertyName = propertyName;
15
- }
6
+ export class RefDirective extends StatelessAttachedAttributeDirective {
16
7
  /**
17
8
  * Bind this behavior to the source.
18
9
  * @param source - The source to bind to.
19
10
  * @param context - The execution context that the binding is operating within.
11
+ * @param targets - The targets that behaviors in a view can attach to.
20
12
  */
21
- bind(source) {
22
- source[this.propertyName] = this.target;
13
+ bind(source, context, targets) {
14
+ source[this.options] = targets[this.nodeId];
23
15
  }
24
16
  /**
25
17
  * Unbinds this behavior from the source.
@@ -28,11 +20,10 @@ export class RefBehavior {
28
20
  /* eslint-disable-next-line @typescript-eslint/no-empty-function */
29
21
  unbind() { }
30
22
  }
23
+ HTMLDirective.define(RefDirective);
31
24
  /**
32
25
  * A directive that observes the updates a property with a reference to the element.
33
26
  * @param propertyName - The name of the property to assign the reference to.
34
27
  * @public
35
28
  */
36
- export function ref(propertyName) {
37
- return new AttachedBehaviorHTMLDirective("fast-ref", RefBehavior, propertyName);
38
- }
29
+ export const ref = (propertyName) => new RefDirective(propertyName);
@@ -1,8 +1,9 @@
1
- import { DOM } from "../dom.js";
1
+ import { isFunction } from "../interfaces.js";
2
2
  import { Observable, } from "../observation/observable.js";
3
- import { enableArrayObservation } from "../observation/array-observer.js";
4
3
  import { emptyArray } from "../platform.js";
5
- import { HTMLDirective } from "./html-directive.js";
4
+ import { ArrayObserver } from "../observation/arrays.js";
5
+ import { Markup } from "./markup.js";
6
+ import { HTMLDirective, } from "./html-directive.js";
6
7
  import { HTMLView } from "./view.js";
7
8
  const defaultRepeatOptions = Object.freeze({
8
9
  positioning: false,
@@ -12,10 +13,7 @@ function bindWithoutPositioning(view, items, index, context) {
12
13
  view.bind(items[index], context);
13
14
  }
14
15
  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);
16
+ view.bind(items[index], context.createItemContext(index, items.length));
19
17
  }
20
18
  /**
21
19
  * A behavior that renders a template for each item in an array.
@@ -40,7 +38,7 @@ export class RepeatBehavior {
40
38
  this.views = [];
41
39
  this.items = null;
42
40
  this.itemsObserver = null;
43
- this.originalContext = void 0;
41
+ this.context = void 0;
44
42
  this.childContext = void 0;
45
43
  this.bindView = bindWithoutPositioning;
46
44
  this.itemsBindingObserver = Observable.binding(itemsBinding, this, isItemsBindingVolatile);
@@ -56,12 +54,10 @@ export class RepeatBehavior {
56
54
  */
57
55
  bind(source, context) {
58
56
  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);
57
+ this.context = context;
58
+ this.childContext = context.createChildContext(source);
59
+ this.items = this.itemsBindingObserver.observe(source, this.context);
60
+ this.template = this.templateBindingObserver.observe(source, this.context);
65
61
  this.observeItems(true);
66
62
  this.refreshAllViews();
67
63
  }
@@ -76,20 +72,27 @@ export class RepeatBehavior {
76
72
  this.itemsObserver.unsubscribe(this);
77
73
  }
78
74
  this.unbindAllViews();
79
- this.itemsBindingObserver.disconnect();
80
- this.templateBindingObserver.disconnect();
75
+ this.itemsBindingObserver.dispose();
76
+ this.templateBindingObserver.dispose();
81
77
  }
82
- /** @internal */
78
+ /**
79
+ * Handles changes in the array, its items, and the repeat template.
80
+ * @param source - The source of the change.
81
+ * @param args - The details about what was changed.
82
+ */
83
83
  handleChange(source, args) {
84
84
  if (source === this.itemsBinding) {
85
- this.items = this.itemsBindingObserver.observe(this.source, this.originalContext);
85
+ this.items = this.itemsBindingObserver.observe(this.source, this.context);
86
86
  this.observeItems();
87
87
  this.refreshAllViews();
88
88
  }
89
89
  else if (source === this.templateBinding) {
90
- this.template = this.templateBindingObserver.observe(this.source, this.originalContext);
90
+ this.template = this.templateBindingObserver.observe(this.source, this.context);
91
91
  this.refreshAllViews(true);
92
92
  }
93
+ else if (args[0].reset) {
94
+ this.refreshAllViews();
95
+ }
93
96
  else {
94
97
  this.updateViews(args);
95
98
  }
@@ -110,8 +113,8 @@ export class RepeatBehavior {
110
113
  }
111
114
  }
112
115
  updateViews(splices) {
113
- const childContext = this.childContext;
114
116
  const views = this.views;
117
+ const childContext = this.childContext;
115
118
  const totalRemoved = [];
116
119
  const bindView = this.bindView;
117
120
  let removeDelta = 0;
@@ -143,18 +146,16 @@ export class RepeatBehavior {
143
146
  }
144
147
  if (this.options.positioning) {
145
148
  for (let i = 0, ii = views.length; i < ii; ++i) {
146
- const currentContext = views[i].context;
147
- currentContext.length = ii;
148
- currentContext.index = i;
149
+ views[i].context.updatePosition(i, ii);
149
150
  }
150
151
  }
151
152
  }
152
153
  refreshAllViews(templateChanged = false) {
153
154
  const items = this.items;
154
- const childContext = this.childContext;
155
155
  const template = this.template;
156
156
  const location = this.location;
157
157
  const bindView = this.bindView;
158
+ const childContext = this.childContext;
158
159
  let itemsLength = items.length;
159
160
  let views = this.views;
160
161
  let viewsLength = views.length;
@@ -205,7 +206,7 @@ export class RepeatBehavior {
205
206
  * A directive that configures list rendering.
206
207
  * @public
207
208
  */
208
- export class RepeatDirective extends HTMLDirective {
209
+ export class RepeatDirective {
209
210
  /**
210
211
  * Creates an instance of RepeatDirective.
211
212
  * @param itemsBinding - The binding that provides the array to render.
@@ -213,37 +214,31 @@ export class RepeatDirective extends HTMLDirective {
213
214
  * @param options - Options used to turn on special repeat features.
214
215
  */
215
216
  constructor(itemsBinding, templateBinding, options) {
216
- super();
217
217
  this.itemsBinding = itemsBinding;
218
218
  this.templateBinding = templateBinding;
219
219
  this.options = options;
220
- /**
221
- * Creates a placeholder string based on the directive's index within the template.
222
- * @param index - The index of the directive within the template.
223
- */
224
- this.createPlaceholder = DOM.createBlockPlaceholder;
225
- enableArrayObservation();
220
+ ArrayObserver.enable();
226
221
  this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
227
222
  this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
228
223
  }
224
+ /**
225
+ * Creates a placeholder string based on the directive's index within the template.
226
+ * @param index - The index of the directive within the template.
227
+ */
228
+ createHTML(add) {
229
+ return Markup.comment(add(this));
230
+ }
229
231
  /**
230
232
  * Creates a behavior for the provided target node.
231
233
  * @param target - The node instance to create the behavior for.
232
234
  */
233
- createBehavior(target) {
234
- return new RepeatBehavior(target, this.itemsBinding, this.isItemsBindingVolatile, this.templateBinding, this.isTemplateBindingVolatile, this.options);
235
+ createBehavior(targets) {
236
+ return new RepeatBehavior(targets[this.nodeId], this.itemsBinding, this.isItemsBindingVolatile, this.templateBinding, this.isTemplateBindingVolatile, this.options);
235
237
  }
236
238
  }
237
- /**
238
- * A directive that enables list rendering.
239
- * @param itemsBinding - The array to render.
240
- * @param templateOrTemplateBinding - The template or a template binding used obtain a template
241
- * to render for each item in the array.
242
- * @param options - Options used to turn on special repeat features.
243
- * @public
244
- */
239
+ HTMLDirective.define(RepeatDirective);
245
240
  export function repeat(itemsBinding, templateOrTemplateBinding, options = defaultRepeatOptions) {
246
- const templateBinding = typeof templateOrTemplateBinding === "function"
241
+ const templateBinding = isFunction(templateOrTemplateBinding)
247
242
  ? templateOrTemplateBinding
248
243
  : () => templateOrTemplateBinding;
249
244
  return new RepeatDirective(itemsBinding, templateBinding, options);
@@ -1,37 +1,40 @@
1
- import { AttachedBehaviorHTMLDirective } from "./html-directive.js";
2
- import { NodeObservationBehavior } from "./node-observation.js";
1
+ import { isString } from "../interfaces.js";
2
+ import { HTMLDirective } from "./html-directive.js";
3
+ import { NodeObservationDirective } from "./node-observation.js";
4
+ const slotEvent = "slotchange";
3
5
  /**
4
6
  * The runtime behavior for slotted node observation.
5
7
  * @public
6
8
  */
7
- export class SlottedBehavior extends NodeObservationBehavior {
8
- /**
9
- * Creates an instance of SlottedBehavior.
10
- * @param target - The slot element target to observe.
11
- * @param options - The options to use when observing the slot.
12
- */
13
- constructor(target, options) {
14
- super(target, options);
15
- }
9
+ export class SlottedDirective extends NodeObservationDirective {
16
10
  /**
17
11
  * Begins observation of the nodes.
12
+ * @param target - The target to observe.
18
13
  */
19
- observe() {
20
- this.target.addEventListener("slotchange", this);
14
+ observe(target) {
15
+ target.addEventListener(slotEvent, this);
21
16
  }
22
17
  /**
23
18
  * Disconnects observation of the nodes.
19
+ * @param target - The target to unobserve.
24
20
  */
25
- disconnect() {
26
- this.target.removeEventListener("slotchange", this);
21
+ disconnect(target) {
22
+ target.removeEventListener(slotEvent, this);
27
23
  }
28
24
  /**
29
- * Retrieves the nodes that should be assigned to the target.
25
+ * Retrieves the raw nodes that should be assigned to the source property.
26
+ * @param target - The target to get the node to.
30
27
  */
31
- getNodes() {
32
- return this.target.assignedNodes(this.options);
28
+ getNodes(target) {
29
+ return target.assignedNodes(this.options);
30
+ }
31
+ /** @internal */
32
+ handleEvent(event) {
33
+ const target = event.currentTarget;
34
+ this.updateTarget(this.getSource(target), this.computeNodes(target));
33
35
  }
34
36
  }
37
+ HTMLDirective.define(SlottedDirective);
35
38
  /**
36
39
  * A directive that observes the `assignedNodes()` of a slot and updates a property
37
40
  * whenever they change.
@@ -39,8 +42,8 @@ export class SlottedBehavior extends NodeObservationBehavior {
39
42
  * @public
40
43
  */
41
44
  export function slotted(propertyOrOptions) {
42
- if (typeof propertyOrOptions === "string") {
45
+ if (isString(propertyOrOptions)) {
43
46
  propertyOrOptions = { property: propertyOrOptions };
44
47
  }
45
- return new AttachedBehaviorHTMLDirective("fast-slotted", SlottedBehavior, propertyOrOptions);
48
+ return new SlottedDirective(propertyOrOptions);
46
49
  }