@microsoft/fast-element 2.0.0-beta.6 → 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 (64) hide show
  1. package/CHANGELOG.json +57 -0
  2. package/CHANGELOG.md +16 -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 +21 -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 +96 -24
  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 +492 -509
  58. package/dist/fast-element.debug.min.js +1 -1
  59. package/dist/fast-element.js +492 -509
  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
@@ -208,6 +208,27 @@ function createTypeRegistry() {
208
208
  },
209
209
  });
210
210
  }
211
+ /**
212
+ * Creates a function capable of locating metadata associated with a type.
213
+ * @returns A metadata locator function.
214
+ * @internal
215
+ */
216
+ function createMetadataLocator() {
217
+ const metadataLookup = new WeakMap();
218
+ return function (target) {
219
+ let metadata = metadataLookup.get(target);
220
+ if (metadata === void 0) {
221
+ let currentTarget = Reflect.getPrototypeOf(target);
222
+ while (metadata === void 0 && currentTarget !== null) {
223
+ metadata = metadataLookup.get(currentTarget);
224
+ currentTarget = Reflect.getPrototypeOf(currentTarget);
225
+ }
226
+ metadata = metadata === void 0 ? [] : metadata.slice(0);
227
+ metadataLookup.set(target, metadata);
228
+ }
229
+ return metadata;
230
+ };
231
+ }
211
232
 
212
233
  /**
213
234
  * @internal
@@ -449,6 +470,21 @@ class PropertyChangeNotifier {
449
470
  }
450
471
  }
451
472
 
473
+ /**
474
+ * Describes how the source's lifetime relates to its controller's lifetime.
475
+ * @public
476
+ */
477
+ const SourceLifetime = Object.freeze({
478
+ /**
479
+ * The source to controller lifetime relationship is unknown.
480
+ */
481
+ unknown: void 0,
482
+ /**
483
+ * The source and controller lifetimes are coupled to one another.
484
+ * They can/will be GC'd together.
485
+ */
486
+ coupled: 1,
487
+ });
452
488
  /**
453
489
  * Common Observable APIs.
454
490
  * @public
@@ -457,7 +493,6 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
457
493
  const queueUpdate = Updates.enqueue;
458
494
  const volatileRegex = /(:|&&|\|\||if)/;
459
495
  const notifierLookup = new WeakMap();
460
- const accessorLookup = new WeakMap();
461
496
  let watcher = void 0;
462
497
  let createArrayObserver = (array) => {
463
498
  throw FAST.error(1101 /* Message.needsArrayObservation */);
@@ -472,19 +507,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
472
507
  }
473
508
  return found;
474
509
  }
475
- function getAccessors(target) {
476
- let accessors = accessorLookup.get(target);
477
- if (accessors === void 0) {
478
- let currentTarget = Reflect.getPrototypeOf(target);
479
- while (accessors === void 0 && currentTarget !== null) {
480
- accessors = accessorLookup.get(currentTarget);
481
- currentTarget = Reflect.getPrototypeOf(currentTarget);
482
- }
483
- accessors = accessors === void 0 ? [] : accessors.slice(0);
484
- accessorLookup.set(target, accessors);
485
- }
486
- return accessors;
487
- }
510
+ const getAccessors = createMetadataLocator();
488
511
  class DefaultObservableAccessor {
489
512
  constructor(name) {
490
513
  this.name = name;
@@ -528,6 +551,22 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
528
551
  setMode(isAsync) {
529
552
  this.isAsync = this.needsQueue = isAsync;
530
553
  }
554
+ bind(controller) {
555
+ this.controller = controller;
556
+ const value = this.observe(controller.source, controller.context);
557
+ if (!controller.isBound && this.requiresUnbind(controller)) {
558
+ controller.onUnbind(this);
559
+ }
560
+ return value;
561
+ }
562
+ requiresUnbind(controller) {
563
+ return (controller.sourceLifetime !== SourceLifetime.coupled ||
564
+ this.first !== this.last ||
565
+ this.first.propertySource !== controller.source);
566
+ }
567
+ unbind(controller) {
568
+ this.dispose();
569
+ }
531
570
  observe(source, context) {
532
571
  if (this.needsRefresh && this.last !== null) {
533
572
  this.dispose();
@@ -537,7 +576,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
537
576
  this.needsRefresh = this.isVolatileBinding;
538
577
  let result;
539
578
  try {
540
- result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
579
+ result = this.binding(source, context);
541
580
  }
542
581
  finally {
543
582
  watcher = previousWatcher;
@@ -728,123 +767,38 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
728
767
  * Provides additional contextual information available to behaviors and expressions.
729
768
  * @public
730
769
  */
731
- class ExecutionContext {
732
- constructor(parentSource = null, parentContext = null) {
733
- /**
734
- * The index of the current item within a repeat context.
735
- */
736
- this.index = 0;
737
- /**
738
- * The length of the current collection within a repeat context.
739
- */
740
- this.length = 0;
741
- this.parent = parentSource;
742
- this.parentContext = parentContext;
743
- }
744
- /**
745
- * The current event within an event handler.
746
- */
747
- get event() {
748
- return contextEvent.get();
749
- }
750
- /**
751
- * Indicates whether the current item within a repeat context
752
- * has an even index.
753
- */
754
- get isEven() {
755
- return this.index % 2 === 0;
756
- }
757
- /**
758
- * Indicates whether the current item within a repeat context
759
- * has an odd index.
760
- */
761
- get isOdd() {
762
- return this.index % 2 !== 0;
763
- }
764
- /**
765
- * Indicates whether the current item within a repeat context
766
- * is the first item in the collection.
767
- */
768
- get isFirst() {
769
- return this.index === 0;
770
- }
771
- /**
772
- * Indicates whether the current item within a repeat context
773
- * is somewhere in the middle of the collection.
774
- */
775
- get isInMiddle() {
776
- return !this.isFirst && !this.isLast;
777
- }
778
- /**
779
- * Indicates whether the current item within a repeat context
780
- * is the last item in the collection.
781
- */
782
- get isLast() {
783
- return this.index === this.length - 1;
784
- }
785
- /**
786
- * Returns the typed event detail of a custom event.
787
- */
788
- eventDetail() {
789
- return this.event.detail;
790
- }
791
- /**
792
- * Returns the typed event target of the event.
793
- */
794
- eventTarget() {
795
- return this.event.target;
796
- }
770
+ const ExecutionContext = Object.freeze({
797
771
  /**
798
- * Updates the position/size on a context associated with a list item.
799
- * @param index - The new index of the item.
800
- * @param length - The new length of the list.
772
+ * A default execution context.
801
773
  */
802
- updatePosition(index, length) {
803
- this.index = index;
804
- this.length = length;
805
- }
806
- /**
807
- * Creates a new execution context descendent from the current context.
808
- * @param source - The source for the context if different than the parent.
809
- * @returns A child execution context.
810
- */
811
- createChildContext(parentSource) {
812
- return new ExecutionContext(parentSource, this);
813
- }
774
+ default: {
775
+ index: 0,
776
+ length: 0,
777
+ get event() {
778
+ return ExecutionContext.getEvent();
779
+ },
780
+ eventDetail() {
781
+ return this.event.detail;
782
+ },
783
+ eventTarget() {
784
+ return this.event.target;
785
+ },
786
+ },
814
787
  /**
815
- * Creates a new execution context descent suitable for use in list rendering.
816
- * @param item - The list item to serve as the source.
817
- * @param index - The index of the item in the list.
818
- * @param length - The length of the list.
788
+ * Gets the current event.
789
+ * @returns An event object.
819
790
  */
820
- createItemContext(index, length) {
821
- const childContext = Object.create(this);
822
- childContext.index = index;
823
- childContext.length = length;
824
- return childContext;
825
- }
791
+ getEvent() {
792
+ return contextEvent.get();
793
+ },
826
794
  /**
827
- * Sets the event for the current execution context.
828
- * @param event - The event to set.
829
- * @internal
795
+ * Sets the current event.
796
+ * @param event - An event object.
830
797
  */
831
- static setEvent(event) {
798
+ setEvent(event) {
832
799
  contextEvent.set(event);
833
- }
834
- /**
835
- * Creates a new root execution context.
836
- * @returns A new execution context.
837
- */
838
- static create() {
839
- return new ExecutionContext();
840
- }
841
- }
842
- /**
843
- * The default execution context.
844
- */
845
- ExecutionContext.default = new ExecutionContext();
846
- Observable.defineProperty(ExecutionContext.prototype, "index");
847
- Observable.defineProperty(ExecutionContext.prototype, "length");
800
+ },
801
+ });
848
802
 
849
803
  /**
850
804
  * A splice map is a representation of how a previous array of items
@@ -1642,11 +1596,11 @@ class CSSPartial {
1642
1596
  }
1643
1597
  return this.css;
1644
1598
  }
1645
- bind(el) {
1646
- el.$fastController.addStyles(this.styles);
1599
+ addedCallback(controller) {
1600
+ controller.addStyles(this.styles);
1647
1601
  }
1648
- unbind(el) {
1649
- el.$fastController.removeStyles(this.styles);
1602
+ removedCallback(controller) {
1603
+ controller.removeStyles(this.styles);
1650
1604
  }
1651
1605
  }
1652
1606
  CSSDirective.define(CSSPartial);
@@ -1785,6 +1739,67 @@ const Parser = Object.freeze({
1785
1739
  },
1786
1740
  });
1787
1741
 
1742
+ /**
1743
+ * Bridges between ViewBehaviors and HostBehaviors, enabling a host to
1744
+ * control ViewBehaviors.
1745
+ * @public
1746
+ */
1747
+ const ViewBehaviorOrchestrator = Object.freeze({
1748
+ /**
1749
+ * Creates a ViewBehaviorOrchestrator.
1750
+ * @param source - The source to to associate behaviors with.
1751
+ * @returns A ViewBehaviorOrchestrator.
1752
+ */
1753
+ create(source) {
1754
+ const behaviors = [];
1755
+ const targets = {};
1756
+ let unbindables = null;
1757
+ let isConnected = false;
1758
+ return {
1759
+ source,
1760
+ context: ExecutionContext.default,
1761
+ targets,
1762
+ get isBound() {
1763
+ return isConnected;
1764
+ },
1765
+ addBehaviorFactory(factory, target) {
1766
+ const nodeId = factory.nodeId || (factory.nodeId = nextId());
1767
+ factory.id || (factory.id = nextId());
1768
+ this.addTarget(nodeId, target);
1769
+ this.addBehavior(factory.createBehavior());
1770
+ },
1771
+ addTarget(nodeId, target) {
1772
+ targets[nodeId] = target;
1773
+ },
1774
+ addBehavior(behavior) {
1775
+ behaviors.push(behavior);
1776
+ if (isConnected) {
1777
+ behavior.bind(this);
1778
+ }
1779
+ },
1780
+ onUnbind(unbindable) {
1781
+ if (unbindables === null) {
1782
+ unbindables = [];
1783
+ }
1784
+ unbindables.push(unbindable);
1785
+ },
1786
+ connectedCallback(controller) {
1787
+ if (!isConnected) {
1788
+ isConnected = true;
1789
+ behaviors.forEach(x => x.bind(this));
1790
+ }
1791
+ },
1792
+ disconnectedCallback(controller) {
1793
+ if (isConnected) {
1794
+ isConnected = false;
1795
+ if (unbindables !== null) {
1796
+ unbindables.forEach(x => x.unbind(this));
1797
+ }
1798
+ }
1799
+ },
1800
+ };
1801
+ },
1802
+ });
1788
1803
  const registry = createTypeRegistry();
1789
1804
  /**
1790
1805
  * Instructs the template engine to apply behavior to a node.
@@ -1830,6 +1845,15 @@ function htmlDirective(options) {
1830
1845
  * @public
1831
1846
  */
1832
1847
  class Binding {
1848
+ /**
1849
+ * Creates a binding.
1850
+ * @param evaluate - Evaluates the binding.
1851
+ * @param isVolatile - Indicates whether the binding is volatile.
1852
+ */
1853
+ constructor(evaluate, isVolatile = false) {
1854
+ this.evaluate = evaluate;
1855
+ this.isVolatile = isVolatile;
1856
+ }
1833
1857
  }
1834
1858
  /**
1835
1859
  * The type of HTML aspect to target.
@@ -1929,13 +1953,6 @@ class StatelessAttachedAttributeDirective {
1929
1953
  */
1930
1954
  this.id = nextId();
1931
1955
  }
1932
- /**
1933
- * Creates a behavior.
1934
- * @param targets - The targets available for behaviors to be attached to.
1935
- */
1936
- createBehavior(targets) {
1937
- return this;
1938
- }
1939
1956
  /**
1940
1957
  * Creates a placeholder string based on the directive's index within the template.
1941
1958
  * @param index - The index of the directive within the template.
@@ -1945,6 +1962,13 @@ class StatelessAttachedAttributeDirective {
1945
1962
  createHTML(add) {
1946
1963
  return Markup.attribute(add(this));
1947
1964
  }
1965
+ /**
1966
+ * Creates a behavior.
1967
+ * @param targets - The targets available for behaviors to be attached to.
1968
+ */
1969
+ createBehavior() {
1970
+ return this;
1971
+ }
1948
1972
  }
1949
1973
 
1950
1974
  const createInnerHTMLBinding = globalThis.TrustedHTML
@@ -1957,29 +1981,19 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
1957
1981
  }
1958
1982
  : (binding) => binding;
1959
1983
  class OnChangeBinding extends Binding {
1960
- constructor(evaluate, isVolatile) {
1961
- super();
1962
- this.evaluate = evaluate;
1963
- this.isVolatile = isVolatile;
1964
- }
1965
1984
  createObserver(_, subscriber) {
1966
1985
  return Observable.binding(this.evaluate, subscriber, this.isVolatile);
1967
1986
  }
1968
1987
  }
1969
1988
  class OneTimeBinding extends Binding {
1970
- constructor(evaluate) {
1971
- super();
1972
- this.evaluate = evaluate;
1973
- }
1974
1989
  createObserver() {
1975
1990
  return this;
1976
1991
  }
1977
- observe(source, context) {
1978
- return this.evaluate(source, context);
1992
+ bind(controller) {
1993
+ return this.evaluate(controller.source, controller.context);
1979
1994
  }
1980
- dispose() { }
1981
1995
  }
1982
- function updateContentTarget(target, aspect, value, source, context) {
1996
+ function updateContent(target, aspect, value, controller) {
1983
1997
  // If there's no actual value, then this equates to the
1984
1998
  // empty string for the purposes of content bindings.
1985
1999
  if (value === null || value === undefined) {
@@ -2011,14 +2025,14 @@ function updateContentTarget(target, aspect, value, source, context) {
2011
2025
  // and that there's actually no need to compose it.
2012
2026
  if (!view.isComposed) {
2013
2027
  view.isComposed = true;
2014
- view.bind(source, context);
2028
+ view.bind(controller.source);
2015
2029
  view.insertBefore(target);
2016
2030
  target.$fastView = view;
2017
2031
  target.$fastTemplate = value;
2018
2032
  }
2019
2033
  else if (view.needsBindOnly) {
2020
2034
  view.needsBindOnly = false;
2021
- view.bind(source, context);
2035
+ view.bind(controller.source);
2022
2036
  }
2023
2037
  }
2024
2038
  else {
@@ -2038,10 +2052,9 @@ function updateContentTarget(target, aspect, value, source, context) {
2038
2052
  target.textContent = value;
2039
2053
  }
2040
2054
  }
2041
- function updateTokenListTarget(target, aspect, value) {
2055
+ function updateTokenList(target, aspect, value) {
2042
2056
  var _a;
2043
- const directive = this.directive;
2044
- const lookup = `${directive.id}-t`;
2057
+ const lookup = `${this.id}-t`;
2045
2058
  const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { c: 0, v: Object.create(null) });
2046
2059
  const versions = state.v;
2047
2060
  let currentVersion = state.c;
@@ -2071,154 +2084,8 @@ function updateTokenListTarget(target, aspect, value) {
2071
2084
  }
2072
2085
  }
2073
2086
  }
2074
- /**
2075
- * A binding behavior for bindings that change.
2076
- * @public
2077
- */
2078
- class BindingBehavior {
2079
- /**
2080
- * Creates an instance of ChangeBinding.
2081
- * @param directive - The directive that has the configuration for this behavior.
2082
- * @param updateTarget - The function used to update the target with the latest value.
2083
- */
2084
- constructor(directive, updateTarget) {
2085
- this.directive = directive;
2086
- this.updateTarget = updateTarget;
2087
- this.observerProperty = `${directive.id}-o`;
2088
- }
2089
- /**
2090
- * Bind this behavior to the source.
2091
- * @param source - The source to bind to.
2092
- * @param context - The execution context that the binding is operating within.
2093
- * @param targets - The targets that behaviors in a view can attach to.
2094
- */
2095
- bind(source, context, targets) {
2096
- const directive = this.directive;
2097
- const target = targets[directive.nodeId];
2098
- const observer = this.getObserver(target);
2099
- observer.target = target;
2100
- observer.source = source;
2101
- observer.context = context;
2102
- this.updateTarget(target, directive.targetAspect, observer.observe(source, context), source, context);
2103
- }
2104
- /**
2105
- * Unbinds this behavior from the source.
2106
- * @param source - The source to unbind from.
2107
- * @param context - The execution context that the binding is operating within.
2108
- * @param targets - The targets that behaviors in a view can attach to.
2109
- */
2110
- unbind(source, context, targets) {
2111
- const target = targets[this.directive.nodeId];
2112
- const observer = this.getObserver(target);
2113
- observer.dispose();
2114
- observer.target = null;
2115
- observer.source = null;
2116
- observer.context = null;
2117
- }
2118
- /** @internal */
2119
- handleChange(binding, observer) {
2120
- const target = observer.target;
2121
- const source = observer.source;
2122
- const context = observer.context;
2123
- this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
2124
- }
2125
- /**
2126
- * Returns the binding observer used to update the node.
2127
- * @param target - The target node.
2128
- * @returns A BindingObserver.
2129
- */
2130
- getObserver(target) {
2131
- var _a;
2132
- return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = this.directive.dataBinding.createObserver(this.directive, this)));
2133
- }
2134
- /**
2135
- * Creates a behavior.
2136
- * @param targets - The targets available for behaviors to be attached to.
2137
- */
2138
- createBehavior(targets) {
2139
- return this;
2140
- }
2141
- }
2142
- /**
2143
- * A special binding behavior that can bind node content.
2144
- * @public
2145
- */
2146
- class ContentBehavior extends BindingBehavior {
2147
- /**
2148
- * Unbinds this behavior from the source.
2149
- * @param source - The source to unbind from.
2150
- * @param context - The execution context that the binding is operating within.
2151
- * @param targets - The targets that behaviors in a view can attach to.
2152
- */
2153
- unbind(source, context, targets) {
2154
- super.unbind(source, context, targets);
2155
- const target = targets[this.directive.nodeId];
2156
- const view = target.$fastView;
2157
- if (view !== void 0 && view.isComposed) {
2158
- view.unbind();
2159
- view.needsBindOnly = true;
2160
- }
2161
- }
2162
- }
2163
- /**
2164
- * A binding behavior for handling events.
2165
- * @public
2166
- */
2167
- class EventBehavior {
2168
- /**
2169
- * Creates an instance of EventBinding.
2170
- * @param directive - The directive that has the configuration for this behavior.
2171
- */
2172
- constructor(directive) {
2173
- this.directive = directive;
2174
- this.sourceProperty = `${directive.id}-s`;
2175
- this.contextProperty = `${directive.id}-c`;
2176
- }
2177
- /**
2178
- * Bind this behavior to the source.
2179
- * @param source - The source to bind to.
2180
- * @param context - The execution context that the binding is operating within.
2181
- * @param targets - The targets that behaviors in a view can attach to.
2182
- */
2183
- bind(source, context, targets) {
2184
- const directive = this.directive;
2185
- const target = targets[directive.nodeId];
2186
- target[this.sourceProperty] = source;
2187
- target[this.contextProperty] = context;
2188
- target.addEventListener(directive.targetAspect, this, directive.dataBinding.options);
2189
- }
2190
- /**
2191
- * Unbinds this behavior from the source.
2192
- * @param source - The source to unbind from.
2193
- * @param context - The execution context that the binding is operating within.
2194
- * @param targets - The targets that behaviors in a view can attach to.
2195
- */
2196
- unbind(source, context, targets) {
2197
- const directive = this.directive;
2198
- const target = targets[directive.nodeId];
2199
- target[this.sourceProperty] = target[this.contextProperty] = null;
2200
- target.removeEventListener(directive.targetAspect, this, directive.dataBinding.options);
2201
- }
2202
- /**
2203
- * Creates a behavior.
2204
- * @param targets - The targets available for behaviors to be attached to.
2205
- */
2206
- createBehavior(targets) {
2207
- return this;
2208
- }
2209
- /**
2210
- * @internal
2211
- */
2212
- handleEvent(event) {
2213
- const target = event.currentTarget;
2214
- ExecutionContext.setEvent(event);
2215
- const result = this.directive.dataBinding.evaluate(target[this.sourceProperty], target[this.contextProperty]);
2216
- ExecutionContext.setEvent(null);
2217
- if (result !== true) {
2218
- event.preventDefault();
2219
- }
2220
- }
2221
- }
2087
+ const setProperty = (t, a, v) => (t[a] = v);
2088
+ const eventTarget = () => void 0;
2222
2089
  /**
2223
2090
  * A directive that applies bindings.
2224
2091
  * @public
@@ -2230,7 +2097,7 @@ class HTMLBindingDirective {
2230
2097
  */
2231
2098
  constructor(dataBinding) {
2232
2099
  this.dataBinding = dataBinding;
2233
- this.factory = null;
2100
+ this.updateTarget = null;
2234
2101
  /**
2235
2102
  * The unique id of the factory.
2236
2103
  */
@@ -2239,6 +2106,9 @@ class HTMLBindingDirective {
2239
2106
  * The type of aspect to target.
2240
2107
  */
2241
2108
  this.aspectType = Aspect.content;
2109
+ /** @internal */
2110
+ this.bind = this.bindDefault;
2111
+ this.data = `${this.id}-d`;
2242
2112
  }
2243
2113
  /**
2244
2114
  * Creates HTML to be used within a template.
@@ -2249,37 +2119,87 @@ class HTMLBindingDirective {
2249
2119
  }
2250
2120
  /**
2251
2121
  * Creates a behavior.
2252
- * @param targets - The targets available for behaviors to be attached to.
2253
2122
  */
2254
- createBehavior(targets) {
2255
- if (this.factory == null) {
2123
+ createBehavior() {
2124
+ if (this.updateTarget === null) {
2256
2125
  if (this.targetAspect === "innerHTML") {
2257
2126
  this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
2258
2127
  }
2259
2128
  switch (this.aspectType) {
2260
2129
  case 1:
2261
- this.factory = new BindingBehavior(this, DOM.setAttribute);
2130
+ this.updateTarget = DOM.setAttribute;
2262
2131
  break;
2263
2132
  case 2:
2264
- this.factory = new BindingBehavior(this, DOM.setBooleanAttribute);
2133
+ this.updateTarget = DOM.setBooleanAttribute;
2265
2134
  break;
2266
2135
  case 3:
2267
- this.factory = new BindingBehavior(this, (t, a, v) => (t[a] = v));
2136
+ this.updateTarget = setProperty;
2268
2137
  break;
2269
2138
  case 4:
2270
- this.factory = new ContentBehavior(this, updateContentTarget);
2139
+ this.bind = this.bindContent;
2140
+ this.updateTarget = updateContent;
2271
2141
  break;
2272
2142
  case 5:
2273
- this.factory = new BindingBehavior(this, updateTokenListTarget);
2143
+ this.updateTarget = updateTokenList;
2274
2144
  break;
2275
2145
  case 6:
2276
- this.factory = new EventBehavior(this);
2146
+ this.bind = this.bindEvent;
2147
+ this.updateTarget = eventTarget;
2277
2148
  break;
2278
2149
  default:
2279
2150
  throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2280
2151
  }
2281
2152
  }
2282
- return this.factory.createBehavior(targets);
2153
+ return this;
2154
+ }
2155
+ /** @internal */
2156
+ bindDefault(controller) {
2157
+ var _a;
2158
+ const target = controller.targets[this.nodeId];
2159
+ const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
2160
+ observer.target = target;
2161
+ observer.controller = controller;
2162
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
2163
+ if (this.updateTarget === updateContent) {
2164
+ controller.onUnbind(this);
2165
+ }
2166
+ }
2167
+ /** @internal */
2168
+ bindContent(controller) {
2169
+ this.bindDefault(controller);
2170
+ controller.onUnbind(this);
2171
+ }
2172
+ /** @internal */
2173
+ bindEvent(controller) {
2174
+ const target = controller.targets[this.nodeId];
2175
+ target[this.data] = controller;
2176
+ target.addEventListener(this.targetAspect, this, this.dataBinding.options);
2177
+ }
2178
+ /** @internal */
2179
+ unbind(controller) {
2180
+ const target = controller.targets[this.nodeId];
2181
+ const view = target.$fastView;
2182
+ if (view !== void 0 && view.isComposed) {
2183
+ view.unbind();
2184
+ view.needsBindOnly = true;
2185
+ }
2186
+ }
2187
+ /** @internal */
2188
+ handleEvent(event) {
2189
+ const target = event.currentTarget;
2190
+ ExecutionContext.setEvent(event);
2191
+ const controller = target[this.data];
2192
+ const result = this.dataBinding.evaluate(controller.source, controller.context);
2193
+ ExecutionContext.setEvent(null);
2194
+ if (result !== true) {
2195
+ event.preventDefault();
2196
+ }
2197
+ }
2198
+ /** @internal */
2199
+ handleChange(binding, observer) {
2200
+ const target = observer.target;
2201
+ const controller = observer.controller;
2202
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
2283
2203
  }
2284
2204
  }
2285
2205
  HTMLDirective.define(HTMLBindingDirective, { aspected: true });
@@ -2354,17 +2274,83 @@ class HTMLView {
2354
2274
  this.factories = factories;
2355
2275
  this.targets = targets;
2356
2276
  this.behaviors = null;
2277
+ this.unbindables = [];
2357
2278
  /**
2358
2279
  * The data that the view is bound to.
2359
2280
  */
2360
2281
  this.source = null;
2282
+ this.isBound = false;
2283
+ this.selfContained = false;
2361
2284
  /**
2362
- * The execution context the view is running within.
2285
+ * The index of the current item within a repeat context.
2363
2286
  */
2364
- this.context = null;
2287
+ this.index = 0;
2288
+ /**
2289
+ * The length of the current collection within a repeat context.
2290
+ */
2291
+ this.length = 0;
2365
2292
  this.firstChild = fragment.firstChild;
2366
2293
  this.lastChild = fragment.lastChild;
2367
2294
  }
2295
+ /**
2296
+ * The execution context the view is running within.
2297
+ */
2298
+ get context() {
2299
+ return this;
2300
+ }
2301
+ /**
2302
+ * The current event within an event handler.
2303
+ */
2304
+ get event() {
2305
+ return ExecutionContext.getEvent();
2306
+ }
2307
+ /**
2308
+ * Indicates whether the current item within a repeat context
2309
+ * has an even index.
2310
+ */
2311
+ get isEven() {
2312
+ return this.index % 2 === 0;
2313
+ }
2314
+ /**
2315
+ * Indicates whether the current item within a repeat context
2316
+ * has an odd index.
2317
+ */
2318
+ get isOdd() {
2319
+ return this.index % 2 !== 0;
2320
+ }
2321
+ /**
2322
+ * Indicates whether the current item within a repeat context
2323
+ * is the first item in the collection.
2324
+ */
2325
+ get isFirst() {
2326
+ return this.index === 0;
2327
+ }
2328
+ /**
2329
+ * Indicates whether the current item within a repeat context
2330
+ * is somewhere in the middle of the collection.
2331
+ */
2332
+ get isInMiddle() {
2333
+ return !this.isFirst && !this.isLast;
2334
+ }
2335
+ /**
2336
+ * Indicates whether the current item within a repeat context
2337
+ * is the last item in the collection.
2338
+ */
2339
+ get isLast() {
2340
+ return this.index === this.length - 1;
2341
+ }
2342
+ /**
2343
+ * Returns the typed event detail of a custom event.
2344
+ */
2345
+ eventDetail() {
2346
+ return this.event.detail;
2347
+ }
2348
+ /**
2349
+ * Returns the typed event target of the event.
2350
+ */
2351
+ eventTarget() {
2352
+ return this.event.target;
2353
+ }
2368
2354
  /**
2369
2355
  * Appends the view's DOM nodes to the referenced node.
2370
2356
  * @param node - The parent node to append the view's DOM nodes to.
@@ -2419,58 +2405,61 @@ class HTMLView {
2419
2405
  removeNodeSequence(this.firstChild, this.lastChild);
2420
2406
  this.unbind();
2421
2407
  }
2408
+ onUnbind(behavior) {
2409
+ this.unbindables.push(behavior);
2410
+ }
2422
2411
  /**
2423
2412
  * Binds a view's behaviors to its binding source.
2424
2413
  * @param source - The binding source for the view's binding behaviors.
2425
2414
  * @param context - The execution context to run the behaviors within.
2426
2415
  */
2427
- bind(source, context) {
2428
- let behaviors = this.behaviors;
2416
+ bind(source) {
2429
2417
  const oldSource = this.source;
2430
2418
  if (oldSource === source) {
2431
2419
  return;
2432
2420
  }
2421
+ let behaviors = this.behaviors;
2433
2422
  this.source = source;
2434
- this.context = context;
2435
- const targets = this.targets;
2436
- if (oldSource !== null) {
2437
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2438
- const current = behaviors[i];
2439
- current.unbind(oldSource, context, targets);
2440
- current.bind(source, context, targets);
2441
- }
2442
- }
2443
- else if (behaviors === null) {
2423
+ if (behaviors === null) {
2444
2424
  this.behaviors = behaviors = new Array(this.factories.length);
2445
2425
  const factories = this.factories;
2446
2426
  for (let i = 0, ii = factories.length; i < ii; ++i) {
2447
- const behavior = factories[i].createBehavior(targets);
2448
- behavior.bind(source, context, targets);
2427
+ const behavior = factories[i].createBehavior();
2428
+ behavior.bind(this);
2449
2429
  behaviors[i] = behavior;
2450
2430
  }
2451
2431
  }
2452
2432
  else {
2433
+ if (oldSource !== null) {
2434
+ this.evaluateUnbindables();
2435
+ }
2453
2436
  for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2454
- behaviors[i].bind(source, context, targets);
2437
+ behaviors[i].bind(this);
2455
2438
  }
2456
2439
  }
2440
+ this.isBound = true;
2457
2441
  }
2458
2442
  /**
2459
2443
  * Unbinds a view's behaviors from its binding source.
2460
2444
  */
2461
2445
  unbind() {
2446
+ if (!this.isBound) {
2447
+ return;
2448
+ }
2462
2449
  const oldSource = this.source;
2463
2450
  if (oldSource === null) {
2464
2451
  return;
2465
2452
  }
2466
- const targets = this.targets;
2467
- const context = this.context;
2468
- const behaviors = this.behaviors;
2469
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2470
- behaviors[i].unbind(oldSource, context, targets);
2471
- }
2453
+ this.evaluateUnbindables();
2472
2454
  this.source = null;
2473
- this.context = null;
2455
+ this.isBound = false;
2456
+ }
2457
+ evaluateUnbindables() {
2458
+ const unbindables = this.unbindables;
2459
+ for (let i = 0, ii = unbindables.length; i < ii; ++i) {
2460
+ unbindables[i].unbind(this);
2461
+ }
2462
+ unbindables.length = 0;
2474
2463
  }
2475
2464
  /**
2476
2465
  * Efficiently disposes of a contiguous range of synthetic view instances.
@@ -2486,6 +2475,8 @@ class HTMLView {
2486
2475
  }
2487
2476
  }
2488
2477
  }
2478
+ Observable.defineProperty(HTMLView.prototype, "index");
2479
+ Observable.defineProperty(HTMLView.prototype, "length");
2489
2480
 
2490
2481
  const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
2491
2482
  const descriptorCache = {};
@@ -2806,9 +2797,9 @@ class ViewTemplate {
2806
2797
  * @param hostBindingTarget - An HTML element to target the host bindings at if different from the
2807
2798
  * host that the template is being attached to.
2808
2799
  */
2809
- render(source, host, hostBindingTarget, context) {
2810
- const view = this.create(hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : host);
2811
- view.bind(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
2800
+ render(source, host, hostBindingTarget) {
2801
+ const view = this.create(hostBindingTarget);
2802
+ view.bind(source);
2812
2803
  view.appendTo(host);
2813
2804
  return view;
2814
2805
  }
@@ -2885,20 +2876,12 @@ function html(strings, ...values) {
2885
2876
  */
2886
2877
  class RefDirective extends StatelessAttachedAttributeDirective {
2887
2878
  /**
2888
- * Bind this behavior to the source.
2889
- * @param source - The source to bind to.
2890
- * @param context - The execution context that the binding is operating within.
2891
- * @param targets - The targets that behaviors in a view can attach to.
2879
+ * Bind this behavior.
2880
+ * @param controller - The view controller that manages the lifecycle of this behavior.
2892
2881
  */
2893
- bind(source, context, targets) {
2894
- source[this.options] = targets[this.nodeId];
2882
+ bind(controller) {
2883
+ controller.source[this.options] = controller.targets[this.nodeId];
2895
2884
  }
2896
- /**
2897
- * Unbinds this behavior from the source.
2898
- * @param source - The source to unbind from.
2899
- */
2900
- /* eslint-disable-next-line @typescript-eslint/no-empty-function */
2901
- unbind() { }
2902
2885
  }
2903
2886
  HTMLDirective.define(RefDirective);
2904
2887
  /**
@@ -2927,11 +2910,17 @@ const defaultRepeatOptions = Object.freeze({
2927
2910
  positioning: false,
2928
2911
  recycle: true,
2929
2912
  });
2930
- function bindWithoutPositioning(view, items, index, context) {
2931
- view.bind(items[index], context);
2913
+ function bindWithoutPositioning(view, items, index, controller) {
2914
+ view.context.parent = controller.source;
2915
+ view.context.parentContext = controller.context;
2916
+ view.bind(items[index]);
2932
2917
  }
2933
- function bindWithPositioning(view, items, index, context) {
2934
- view.bind(items[index], context.createItemContext(index, items.length));
2918
+ function bindWithPositioning(view, items, index, controller) {
2919
+ view.context.parent = controller.source;
2920
+ view.context.parentContext = controller.context;
2921
+ view.context.length = items.length;
2922
+ view.context.index = index;
2923
+ view.bind(items[index]);
2935
2924
  }
2936
2925
  /**
2937
2926
  * A behavior that renders a template for each item in an array.
@@ -2947,15 +2936,11 @@ class RepeatBehavior {
2947
2936
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
2948
2937
  * @param options - Options used to turn on special repeat features.
2949
2938
  */
2950
- constructor(directive, location) {
2939
+ constructor(directive) {
2951
2940
  this.directive = directive;
2952
- this.location = location;
2953
- this.source = null;
2954
2941
  this.views = [];
2955
2942
  this.items = null;
2956
2943
  this.itemsObserver = null;
2957
- this.context = void 0;
2958
- this.childContext = void 0;
2959
2944
  this.bindView = bindWithoutPositioning;
2960
2945
  this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
2961
2946
  this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
@@ -2964,32 +2949,26 @@ class RepeatBehavior {
2964
2949
  }
2965
2950
  }
2966
2951
  /**
2967
- * Bind this behavior to the source.
2968
- * @param source - The source to bind to.
2969
- * @param context - The execution context that the binding is operating within.
2952
+ * Bind this behavior.
2953
+ * @param controller - The view controller that manages the lifecycle of this behavior.
2970
2954
  */
2971
- bind(source, context) {
2972
- this.source = source;
2973
- this.context = context;
2974
- this.childContext = context.createChildContext(source);
2975
- this.items = this.itemsBindingObserver.observe(source, this.context);
2976
- this.template = this.templateBindingObserver.observe(source, this.context);
2955
+ bind(controller) {
2956
+ this.location = controller.targets[this.directive.nodeId];
2957
+ this.controller = controller;
2958
+ this.items = this.itemsBindingObserver.bind(controller);
2959
+ this.template = this.templateBindingObserver.bind(controller);
2977
2960
  this.observeItems(true);
2978
2961
  this.refreshAllViews();
2962
+ controller.onUnbind(this);
2979
2963
  }
2980
2964
  /**
2981
- * Unbinds this behavior from the source.
2982
- * @param source - The source to unbind from.
2965
+ * Unbinds this behavior.
2983
2966
  */
2984
2967
  unbind() {
2985
- this.source = null;
2986
- this.items = null;
2987
2968
  if (this.itemsObserver !== null) {
2988
2969
  this.itemsObserver.unsubscribe(this);
2989
2970
  }
2990
2971
  this.unbindAllViews();
2991
- this.itemsBindingObserver.dispose();
2992
- this.templateBindingObserver.dispose();
2993
2972
  }
2994
2973
  /**
2995
2974
  * Handles changes in the array, its items, and the repeat template.
@@ -2998,12 +2977,12 @@ class RepeatBehavior {
2998
2977
  */
2999
2978
  handleChange(source, args) {
3000
2979
  if (args === this.itemsBindingObserver) {
3001
- this.items = this.itemsBindingObserver.observe(this.source, this.context);
2980
+ this.items = this.itemsBindingObserver.bind(this.controller);
3002
2981
  this.observeItems();
3003
2982
  this.refreshAllViews();
3004
2983
  }
3005
2984
  else if (args === this.templateBindingObserver) {
3006
- this.template = this.templateBindingObserver.observe(this.source, this.context);
2985
+ this.template = this.templateBindingObserver.bind(this.controller);
3007
2986
  this.refreshAllViews(true);
3008
2987
  }
3009
2988
  else if (!args[0]) {
@@ -3033,10 +3012,10 @@ class RepeatBehavior {
3033
3012
  }
3034
3013
  updateViews(splices) {
3035
3014
  const views = this.views;
3036
- const childContext = this.childContext;
3037
3015
  const bindView = this.bindView;
3038
3016
  const items = this.items;
3039
3017
  const template = this.template;
3018
+ const controller = this.controller;
3040
3019
  const recycle = this.directive.options.recycle;
3041
3020
  const leftoverViews = [];
3042
3021
  let leftoverIndex = 0;
@@ -3048,13 +3027,14 @@ class RepeatBehavior {
3048
3027
  let addIndex = splice.index;
3049
3028
  const end = addIndex + splice.addedCount;
3050
3029
  const removedViews = views.splice(splice.index, removed.length);
3051
- availableViews = leftoverViews.length + removedViews.length;
3030
+ const totalAvailableViews = (availableViews =
3031
+ leftoverViews.length + removedViews.length);
3052
3032
  for (; addIndex < end; ++addIndex) {
3053
3033
  const neighbor = views[addIndex];
3054
3034
  const location = neighbor ? neighbor.firstChild : this.location;
3055
3035
  let view;
3056
3036
  if (recycle && availableViews > 0) {
3057
- if (removeIndex <= availableViews && removedViews.length > 0) {
3037
+ if (removeIndex <= totalAvailableViews && removedViews.length > 0) {
3058
3038
  view = removedViews[removeIndex];
3059
3039
  removeIndex++;
3060
3040
  }
@@ -3068,7 +3048,7 @@ class RepeatBehavior {
3068
3048
  view = template.create();
3069
3049
  }
3070
3050
  views.splice(addIndex, 0, view);
3071
- bindView(view, items, addIndex, childContext);
3051
+ bindView(view, items, addIndex, controller);
3072
3052
  view.insertBefore(location);
3073
3053
  }
3074
3054
  if (removedViews[removeIndex]) {
@@ -3080,7 +3060,9 @@ class RepeatBehavior {
3080
3060
  }
3081
3061
  if (this.directive.options.positioning) {
3082
3062
  for (let i = 0, ii = views.length; i < ii; ++i) {
3083
- views[i].context.updatePosition(i, ii);
3063
+ const context = views[i].context;
3064
+ context.length = i;
3065
+ context.index = ii;
3084
3066
  }
3085
3067
  }
3086
3068
  }
@@ -3089,7 +3071,7 @@ class RepeatBehavior {
3089
3071
  const template = this.template;
3090
3072
  const location = this.location;
3091
3073
  const bindView = this.bindView;
3092
- const childContext = this.childContext;
3074
+ const controller = this.controller;
3093
3075
  let itemsLength = items.length;
3094
3076
  let views = this.views;
3095
3077
  let viewsLength = views.length;
@@ -3103,7 +3085,7 @@ class RepeatBehavior {
3103
3085
  this.views = views = new Array(itemsLength);
3104
3086
  for (let i = 0; i < itemsLength; ++i) {
3105
3087
  const view = template.create();
3106
- bindView(view, items, i, childContext);
3088
+ bindView(view, items, i, controller);
3107
3089
  views[i] = view;
3108
3090
  view.insertBefore(location);
3109
3091
  }
@@ -3114,11 +3096,11 @@ class RepeatBehavior {
3114
3096
  for (; i < itemsLength; ++i) {
3115
3097
  if (i < viewsLength) {
3116
3098
  const view = views[i];
3117
- bindView(view, items, i, childContext);
3099
+ bindView(view, items, i, controller);
3118
3100
  }
3119
3101
  else {
3120
3102
  const view = template.create();
3121
- bindView(view, items, i, childContext);
3103
+ bindView(view, items, i, controller);
3122
3104
  views.push(view);
3123
3105
  view.insertBefore(location);
3124
3106
  }
@@ -3168,8 +3150,8 @@ class RepeatDirective {
3168
3150
  * Creates a behavior for the provided target node.
3169
3151
  * @param target - The node instance to create the behavior for.
3170
3152
  */
3171
- createBehavior(targets) {
3172
- return new RepeatBehavior(this, targets[this.nodeId]);
3153
+ createBehavior() {
3154
+ return new RepeatBehavior(this);
3173
3155
  }
3174
3156
  }
3175
3157
  HTMLDirective.define(RepeatDirective);
@@ -3213,11 +3195,12 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3213
3195
  * @param context - The execution context that the binding is operating within.
3214
3196
  * @param targets - The targets that behaviors in a view can attach to.
3215
3197
  */
3216
- bind(source, context, targets) {
3217
- const target = targets[this.nodeId];
3218
- target[this.sourceProperty] = source;
3219
- this.updateTarget(source, this.computeNodes(target));
3198
+ bind(controller) {
3199
+ const target = controller.targets[this.nodeId];
3200
+ target[this.sourceProperty] = controller.source;
3201
+ this.updateTarget(controller.source, this.computeNodes(target));
3220
3202
  this.observe(target);
3203
+ controller.onUnbind(this);
3221
3204
  }
3222
3205
  /**
3223
3206
  * Unbinds this behavior from the source.
@@ -3225,9 +3208,9 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3225
3208
  * @param context - The execution context that the binding is operating within.
3226
3209
  * @param targets - The targets that behaviors in a view can attach to.
3227
3210
  */
3228
- unbind(source, context, targets) {
3229
- const target = targets[this.nodeId];
3230
- this.updateTarget(source, emptyArray);
3211
+ unbind(controller) {
3212
+ const target = controller.targets[this.nodeId];
3213
+ this.updateTarget(controller.source, emptyArray);
3231
3214
  this.disconnect(target);
3232
3215
  target[this.sourceProperty] = null;
3233
3216
  }
@@ -3376,6 +3359,16 @@ function children(propertyOrOptions) {
3376
3359
 
3377
3360
  const booleanMode = "boolean";
3378
3361
  const reflectMode = "reflect";
3362
+ /**
3363
+ * Metadata used to configure a custom attribute's behavior.
3364
+ * @public
3365
+ */
3366
+ const AttributeConfiguration = Object.freeze({
3367
+ /**
3368
+ * Locates all attribute configurations associated with a type.
3369
+ */
3370
+ locate: createMetadataLocator(),
3371
+ });
3379
3372
  /**
3380
3373
  * A {@link ValueConverter} that converts to and from `boolean` values.
3381
3374
  * @remarks
@@ -3513,7 +3506,7 @@ class AttributeDefinition {
3513
3506
  */
3514
3507
  static collect(Owner, ...attributeLists) {
3515
3508
  const attributes = [];
3516
- attributeLists.push(Owner.attributes);
3509
+ attributeLists.push(AttributeConfiguration.locate(Owner));
3517
3510
  for (let i = 0, ii = attributeLists.length; i < ii; ++i) {
3518
3511
  const list = attributeLists[i];
3519
3512
  if (list === void 0) {
@@ -3543,9 +3536,7 @@ function attr(configOrTarget, prop) {
3543
3536
  // - @attr({...opts})
3544
3537
  config.property = $prop;
3545
3538
  }
3546
- const attributes = $target.constructor.attributes ||
3547
- ($target.constructor.attributes = []);
3548
- attributes.push(config);
3539
+ AttributeConfiguration.locate($target.constructor).push(config);
3549
3540
  }
3550
3541
  if (arguments.length > 1) {
3551
3542
  // Non invocation:
@@ -3570,6 +3561,7 @@ const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */
3570
3561
  */
3571
3562
  class FASTElementDefinition {
3572
3563
  constructor(type, nameOrConfig = type.definition) {
3564
+ var _a;
3573
3565
  this.platformDefined = false;
3574
3566
  if (isString(nameOrConfig)) {
3575
3567
  nameOrConfig = { name: nameOrConfig };
@@ -3577,6 +3569,7 @@ class FASTElementDefinition {
3577
3569
  this.type = type;
3578
3570
  this.name = nameOrConfig.name;
3579
3571
  this.template = nameOrConfig.template;
3572
+ this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
3580
3573
  const proto = type.prototype;
3581
3574
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3582
3575
  const observedAttributes = new Array(attributes.length);
@@ -3621,7 +3614,7 @@ class FASTElementDefinition {
3621
3614
  * @remarks
3622
3615
  * This operation is idempotent per registry.
3623
3616
  */
3624
- define(registry = customElements) {
3617
+ define(registry = this.registry) {
3625
3618
  const type = this.type;
3626
3619
  if (!registry.get(this.name)) {
3627
3620
  this.platformDefined = true;
@@ -3655,22 +3648,22 @@ FASTElementDefinition.getByType = fastElementRegistry.getByType;
3655
3648
  */
3656
3649
  FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
3657
3650
 
3658
- const shadowRoots = new WeakMap();
3659
3651
  const defaultEventOptions = {
3660
3652
  bubbles: true,
3661
3653
  composed: true,
3662
3654
  cancelable: true,
3663
3655
  };
3656
+ const isConnectedPropertyName = "isConnected";
3657
+ const shadowRoots = new WeakMap();
3664
3658
  function getShadowRoot(element) {
3665
3659
  var _a, _b;
3666
3660
  return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
3667
3661
  }
3668
- const isConnectedPropertyName = "isConnected";
3669
3662
  /**
3670
3663
  * Controls the lifecycle and rendering of a `FASTElement`.
3671
3664
  * @public
3672
3665
  */
3673
- class Controller extends PropertyChangeNotifier {
3666
+ class ElementController extends PropertyChangeNotifier {
3674
3667
  /**
3675
3668
  * Creates a Controller to control the specified element.
3676
3669
  * @param element - The element to be controlled by this controller.
@@ -3681,12 +3674,12 @@ class Controller extends PropertyChangeNotifier {
3681
3674
  constructor(element, definition) {
3682
3675
  super(element);
3683
3676
  this.boundObservables = null;
3684
- this.behaviors = null;
3685
3677
  this.needsInitialization = true;
3686
3678
  this.hasExistingShadowRoot = false;
3687
3679
  this._template = null;
3688
- this._styles = null;
3689
3680
  this._isConnected = false;
3681
+ this.behaviors = null;
3682
+ this._mainStyles = null;
3690
3683
  /**
3691
3684
  * This allows Observable.getNotifier(...) to return the Controller
3692
3685
  * when the notifier for the Controller itself is being requested. The
@@ -3702,7 +3695,7 @@ class Controller extends PropertyChangeNotifier {
3702
3695
  * If `null` then the element is managing its own rendering.
3703
3696
  */
3704
3697
  this.view = null;
3705
- this.element = element;
3698
+ this.source = element;
3706
3699
  this.definition = definition;
3707
3700
  const shadowOptions = definition.shadowOptions;
3708
3701
  if (shadowOptions !== void 0) {
@@ -3756,9 +3749,9 @@ class Controller extends PropertyChangeNotifier {
3756
3749
  // 1. Template overrides take top precedence.
3757
3750
  if (this._template === null) {
3758
3751
  const definition = this.definition;
3759
- if (this.element.resolveTemplate) {
3752
+ if (this.source.resolveTemplate) {
3760
3753
  // 2. Allow for element instance overrides next.
3761
- this._template = this.element.resolveTemplate();
3754
+ this._template = this.source.resolveTemplate();
3762
3755
  }
3763
3756
  else if (definition.template) {
3764
3757
  // 3. Default to the static definition.
@@ -3777,48 +3770,92 @@ class Controller extends PropertyChangeNotifier {
3777
3770
  }
3778
3771
  }
3779
3772
  /**
3780
- * Gets/sets the primary styles used for the component.
3781
- * @remarks
3782
- * This value can only be accurately read after connect but can be set at any time.
3773
+ * The main set of styles used for the component, independent
3774
+ * of any dynamically added styles.
3783
3775
  */
3784
- get styles() {
3776
+ get mainStyles() {
3785
3777
  var _a;
3786
3778
  // 1. Styles overrides take top precedence.
3787
- if (this._styles === null) {
3779
+ if (this._mainStyles === null) {
3788
3780
  const definition = this.definition;
3789
- if (this.element.resolveStyles) {
3781
+ if (this.source.resolveStyles) {
3790
3782
  // 2. Allow for element instance overrides next.
3791
- this._styles = this.element.resolveStyles();
3783
+ this._mainStyles = this.source.resolveStyles();
3792
3784
  }
3793
3785
  else if (definition.styles) {
3794
3786
  // 3. Default to the static definition.
3795
- this._styles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
3787
+ this._mainStyles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
3796
3788
  }
3797
3789
  }
3798
- return this._styles;
3790
+ return this._mainStyles;
3799
3791
  }
3800
- set styles(value) {
3801
- if (this._styles === value) {
3792
+ set mainStyles(value) {
3793
+ if (this._mainStyles === value) {
3802
3794
  return;
3803
3795
  }
3804
- if (this._styles !== null) {
3805
- this.removeStyles(this._styles);
3796
+ if (this._mainStyles !== null) {
3797
+ this.removeStyles(this._mainStyles);
3806
3798
  }
3807
- this._styles = value;
3799
+ this._mainStyles = value;
3808
3800
  if (!this.needsInitialization) {
3809
3801
  this.addStyles(value);
3810
3802
  }
3811
3803
  }
3804
+ /**
3805
+ * Adds the behavior to the component.
3806
+ * @param behavior - The behavior to add.
3807
+ */
3808
+ addBehavior(behavior) {
3809
+ var _a, _b;
3810
+ const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
3811
+ const count = (_b = targetBehaviors.get(behavior)) !== null && _b !== void 0 ? _b : 0;
3812
+ if (count === 0) {
3813
+ targetBehaviors.set(behavior, 1);
3814
+ behavior.addedCallback && behavior.addedCallback(this);
3815
+ if (behavior.connectedCallback && this.isConnected) {
3816
+ behavior.connectedCallback(this);
3817
+ }
3818
+ }
3819
+ else {
3820
+ targetBehaviors.set(behavior, count + 1);
3821
+ }
3822
+ }
3823
+ /**
3824
+ * Removes the behavior from the component.
3825
+ * @param behavior - The behavior to remove.
3826
+ * @param force - Forces removal even if this behavior was added more than once.
3827
+ */
3828
+ removeBehavior(behavior, force = false) {
3829
+ const targetBehaviors = this.behaviors;
3830
+ if (targetBehaviors === null) {
3831
+ return;
3832
+ }
3833
+ const count = targetBehaviors.get(behavior);
3834
+ if (count === void 0) {
3835
+ return;
3836
+ }
3837
+ if (count === 1 || force) {
3838
+ targetBehaviors.delete(behavior);
3839
+ if (behavior.disconnectedCallback && this.isConnected) {
3840
+ behavior.disconnectedCallback(this);
3841
+ }
3842
+ behavior.removedCallback && behavior.removedCallback(this);
3843
+ }
3844
+ else {
3845
+ targetBehaviors.set(behavior, count - 1);
3846
+ }
3847
+ }
3812
3848
  /**
3813
3849
  * Adds styles to this element. Providing an HTMLStyleElement will attach the element instance to the shadowRoot.
3814
3850
  * @param styles - The styles to add.
3815
3851
  */
3816
3852
  addStyles(styles) {
3853
+ var _a;
3817
3854
  if (!styles) {
3818
3855
  return;
3819
3856
  }
3820
- const target = getShadowRoot(this.element) ||
3821
- this.element.getRootNode();
3857
+ const source = this.source;
3858
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
3822
3859
  if (styles instanceof HTMLElement) {
3823
3860
  target.append(styles);
3824
3861
  }
@@ -3826,7 +3863,9 @@ class Controller extends PropertyChangeNotifier {
3826
3863
  const sourceBehaviors = styles.behaviors;
3827
3864
  styles.addStylesTo(target);
3828
3865
  if (sourceBehaviors !== null) {
3829
- this.addBehaviors(sourceBehaviors);
3866
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
3867
+ this.addBehavior(sourceBehaviors[i]);
3868
+ }
3830
3869
  }
3831
3870
  }
3832
3871
  }
@@ -3835,11 +3874,12 @@ class Controller extends PropertyChangeNotifier {
3835
3874
  * @param styles - the styles to remove.
3836
3875
  */
3837
3876
  removeStyles(styles) {
3877
+ var _a;
3838
3878
  if (!styles) {
3839
3879
  return;
3840
3880
  }
3841
- const target = getShadowRoot(this.element) ||
3842
- this.element.getRootNode();
3881
+ const source = this.source;
3882
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
3843
3883
  if (styles instanceof HTMLElement) {
3844
3884
  target.removeChild(styles);
3845
3885
  }
@@ -3847,85 +3887,29 @@ class Controller extends PropertyChangeNotifier {
3847
3887
  const sourceBehaviors = styles.behaviors;
3848
3888
  styles.removeStylesFrom(target);
3849
3889
  if (sourceBehaviors !== null) {
3850
- this.removeBehaviors(sourceBehaviors);
3851
- }
3852
- }
3853
- }
3854
- /**
3855
- * Adds behaviors to this element.
3856
- * @param behaviors - The behaviors to add.
3857
- */
3858
- addBehaviors(behaviors) {
3859
- var _a;
3860
- const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
3861
- const length = behaviors.length;
3862
- const behaviorsToBind = [];
3863
- for (let i = 0; i < length; ++i) {
3864
- const behavior = behaviors[i];
3865
- if (targetBehaviors.has(behavior)) {
3866
- targetBehaviors.set(behavior, targetBehaviors.get(behavior) + 1);
3867
- }
3868
- else {
3869
- targetBehaviors.set(behavior, 1);
3870
- behaviorsToBind.push(behavior);
3871
- }
3872
- }
3873
- if (this._isConnected) {
3874
- const element = this.element;
3875
- const context = ExecutionContext.default;
3876
- for (let i = 0; i < behaviorsToBind.length; ++i) {
3877
- behaviorsToBind[i].bind(element, context);
3878
- }
3879
- }
3880
- }
3881
- /**
3882
- * Removes behaviors from this element.
3883
- * @param behaviors - The behaviors to remove.
3884
- * @param force - Forces unbinding of behaviors.
3885
- */
3886
- removeBehaviors(behaviors, force = false) {
3887
- const targetBehaviors = this.behaviors;
3888
- if (targetBehaviors === null) {
3889
- return;
3890
- }
3891
- const length = behaviors.length;
3892
- const behaviorsToUnbind = [];
3893
- for (let i = 0; i < length; ++i) {
3894
- const behavior = behaviors[i];
3895
- if (targetBehaviors.has(behavior)) {
3896
- const count = targetBehaviors.get(behavior) - 1;
3897
- count === 0 || force
3898
- ? targetBehaviors.delete(behavior) && behaviorsToUnbind.push(behavior)
3899
- : targetBehaviors.set(behavior, count);
3900
- }
3901
- }
3902
- if (this._isConnected) {
3903
- const element = this.element;
3904
- const context = ExecutionContext.default;
3905
- for (let i = 0; i < behaviorsToUnbind.length; ++i) {
3906
- behaviorsToUnbind[i].unbind(element, context);
3890
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
3891
+ this.addBehavior(sourceBehaviors[i]);
3892
+ }
3907
3893
  }
3908
3894
  }
3909
3895
  }
3910
3896
  /**
3911
3897
  * Runs connected lifecycle behavior on the associated element.
3912
3898
  */
3913
- onConnectedCallback() {
3899
+ connect() {
3914
3900
  if (this._isConnected) {
3915
3901
  return;
3916
3902
  }
3917
- const element = this.element;
3918
- const context = ExecutionContext.default;
3919
3903
  if (this.needsInitialization) {
3920
3904
  this.finishInitialization();
3921
3905
  }
3922
3906
  else if (this.view !== null) {
3923
- this.view.bind(element, context);
3907
+ this.view.bind(this.source);
3924
3908
  }
3925
3909
  const behaviors = this.behaviors;
3926
3910
  if (behaviors !== null) {
3927
- for (const behavior of behaviors.keys()) {
3928
- behavior.bind(element, context);
3911
+ for (const key of behaviors.keys()) {
3912
+ key.connectedCallback && key.connectedCallback(this);
3929
3913
  }
3930
3914
  }
3931
3915
  this.setIsConnected(true);
@@ -3933,21 +3917,18 @@ class Controller extends PropertyChangeNotifier {
3933
3917
  /**
3934
3918
  * Runs disconnected lifecycle behavior on the associated element.
3935
3919
  */
3936
- onDisconnectedCallback() {
3920
+ disconnect() {
3937
3921
  if (!this._isConnected) {
3938
3922
  return;
3939
3923
  }
3940
3924
  this.setIsConnected(false);
3941
- const view = this.view;
3942
- if (view !== null) {
3943
- view.unbind();
3925
+ if (this.view !== null) {
3926
+ this.view.unbind();
3944
3927
  }
3945
3928
  const behaviors = this.behaviors;
3946
3929
  if (behaviors !== null) {
3947
- const element = this.element;
3948
- const context = ExecutionContext.default;
3949
- for (const behavior of behaviors.keys()) {
3950
- behavior.unbind(element, context);
3930
+ for (const key of behaviors.keys()) {
3931
+ key.disconnectedCallback && key.disconnectedCallback(this);
3951
3932
  }
3952
3933
  }
3953
3934
  }
@@ -3960,7 +3941,7 @@ class Controller extends PropertyChangeNotifier {
3960
3941
  onAttributeChangedCallback(name, oldValue, newValue) {
3961
3942
  const attrDef = this.definition.attributeLookup[name];
3962
3943
  if (attrDef !== void 0) {
3963
- attrDef.onAttributeChangedCallback(this.element, newValue);
3944
+ attrDef.onAttributeChangedCallback(this.source, newValue);
3964
3945
  }
3965
3946
  }
3966
3947
  /**
@@ -3973,12 +3954,12 @@ class Controller extends PropertyChangeNotifier {
3973
3954
  */
3974
3955
  emit(type, detail, options) {
3975
3956
  if (this._isConnected) {
3976
- return this.element.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
3957
+ return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
3977
3958
  }
3978
3959
  return false;
3979
3960
  }
3980
3961
  finishInitialization() {
3981
- const element = this.element;
3962
+ const element = this.source;
3982
3963
  const boundObservables = this.boundObservables;
3983
3964
  // If we have any observables that were bound, re-apply their values.
3984
3965
  if (boundObservables !== null) {
@@ -3990,15 +3971,15 @@ class Controller extends PropertyChangeNotifier {
3990
3971
  this.boundObservables = null;
3991
3972
  }
3992
3973
  this.renderTemplate(this.template);
3993
- this.addStyles(this.styles);
3974
+ this.addStyles(this.mainStyles);
3994
3975
  this.needsInitialization = false;
3995
3976
  }
3996
3977
  renderTemplate(template) {
3997
3978
  var _a;
3998
- const element = this.element;
3999
3979
  // When getting the host to render to, we start by looking
4000
3980
  // up the shadow root. If there isn't one, then that means
4001
3981
  // we're doing a Light DOM render to the element's direct children.
3982
+ const element = this.source;
4002
3983
  const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
4003
3984
  if (this.view !== null) {
4004
3985
  // If there's already a view, we need to unbind and remove through dispose.
@@ -4015,6 +3996,8 @@ class Controller extends PropertyChangeNotifier {
4015
3996
  if (template) {
4016
3997
  // If a new template was provided, render it.
4017
3998
  this.view = template.render(element, host, element);
3999
+ this.view.sourceLifetime =
4000
+ SourceLifetime.coupled;
4018
4001
  }
4019
4002
  }
4020
4003
  /**
@@ -4034,7 +4017,7 @@ class Controller extends PropertyChangeNotifier {
4034
4017
  if (definition === void 0) {
4035
4018
  throw FAST.error(1401 /* Message.missingElementDefinition */);
4036
4019
  }
4037
- return (element.$fastController = new Controller(element, definition));
4020
+ return (element.$fastController = new ElementController(element, definition));
4038
4021
  }
4039
4022
  }
4040
4023
 
@@ -4044,16 +4027,16 @@ function createFASTElement(BaseType) {
4044
4027
  constructor() {
4045
4028
  /* eslint-disable-next-line */
4046
4029
  super();
4047
- Controller.forCustomElement(this);
4030
+ ElementController.forCustomElement(this);
4048
4031
  }
4049
4032
  $emit(type, detail, options) {
4050
4033
  return this.$fastController.emit(type, detail, options);
4051
4034
  }
4052
4035
  connectedCallback() {
4053
- this.$fastController.onConnectedCallback();
4036
+ this.$fastController.connect();
4054
4037
  }
4055
4038
  disconnectedCallback() {
4056
- this.$fastController.onDisconnectedCallback();
4039
+ this.$fastController.disconnect();
4057
4040
  }
4058
4041
  attributeChangedCallback(name, oldValue, newValue) {
4059
4042
  this.$fastController.onAttributeChangedCallback(name, oldValue, newValue);
@@ -4113,4 +4096,4 @@ function customElement(nameOrDef) {
4113
4096
  };
4114
4097
  }
4115
4098
 
4116
- export { AdoptedStyleSheetsStrategy, ArrayObserver, Aspect, AttributeDefinition, Binding, BindingBehavior, CSSDirective, ChildrenDirective, Compiler, ContentBehavior, Controller, DOM, ElementStyles, EventBehavior, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SlottedDirective, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, Updates, ViewTemplate, attr, bind, booleanConverter, children, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };
4099
+ export { AdoptedStyleSheetsStrategy, ArrayObserver, Aspect, AttributeConfiguration, AttributeDefinition, Binding, CSSDirective, ChildrenDirective, Compiler, DOM, ElementController, ElementStyles, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SlottedDirective, SourceLifetime, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, Updates, ViewBehaviorOrchestrator, ViewTemplate, attr, bind, booleanConverter, children, createMetadataLocator, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };