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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CHANGELOG.json +72 -0
  2. package/CHANGELOG.md +24 -1
  3. package/dist/dts/components/attributes.d.ts +10 -0
  4. package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +24 -25
  5. package/dist/dts/components/fast-definitions.d.ts +28 -3
  6. package/dist/dts/components/fast-element.d.ts +2 -2
  7. package/dist/dts/di/di.d.ts +41 -0
  8. package/dist/dts/index.d.ts +2 -2
  9. package/dist/dts/interfaces.d.ts +4 -0
  10. package/dist/dts/observation/observable.d.ts +86 -47
  11. package/dist/dts/pending-task.d.ts +20 -0
  12. package/dist/dts/platform.d.ts +6 -0
  13. package/dist/dts/state/exports.d.ts +3 -0
  14. package/dist/dts/state/reactive.d.ts +8 -0
  15. package/dist/dts/state/state.d.ts +141 -0
  16. package/dist/dts/state/visitor.d.ts +6 -0
  17. package/dist/dts/state/watch.d.ts +10 -0
  18. package/dist/dts/styles/css-directive.d.ts +2 -2
  19. package/dist/dts/styles/element-styles.d.ts +3 -3
  20. package/dist/dts/styles/host.d.ts +68 -0
  21. package/dist/dts/templating/binding-signal.d.ts +2 -2
  22. package/dist/dts/templating/binding-two-way.d.ts +11 -3
  23. package/dist/dts/templating/binding.d.ts +21 -119
  24. package/dist/dts/templating/children.d.ts +1 -1
  25. package/dist/dts/templating/html-directive.d.ts +69 -39
  26. package/dist/dts/templating/node-observation.d.ts +4 -5
  27. package/dist/dts/templating/ref.d.ts +5 -13
  28. package/dist/dts/templating/render.d.ts +15 -20
  29. package/dist/dts/templating/repeat.d.ts +11 -16
  30. package/dist/dts/templating/slotted.d.ts +1 -1
  31. package/dist/dts/templating/template.d.ts +4 -4
  32. package/dist/dts/templating/view.d.ts +68 -9
  33. package/dist/dts/templating/when.d.ts +1 -1
  34. package/dist/dts/testing/exports.d.ts +1 -0
  35. package/dist/dts/testing/fakes.d.ts +4 -0
  36. package/dist/dts/testing/fixture.d.ts +0 -6
  37. package/dist/dts/utilities.d.ts +0 -18
  38. package/dist/esm/components/attributes.js +13 -4
  39. package/dist/esm/components/{controller.js → element-controller.js} +95 -105
  40. package/dist/esm/components/fast-definitions.js +3 -1
  41. package/dist/esm/components/fast-element.js +4 -4
  42. package/dist/esm/di/di.js +87 -3
  43. package/dist/esm/index.js +2 -1
  44. package/dist/esm/interfaces.js +4 -0
  45. package/dist/esm/observation/observable.js +59 -126
  46. package/dist/esm/pending-task.js +16 -0
  47. package/dist/esm/platform.js +21 -0
  48. package/dist/esm/state/exports.js +3 -0
  49. package/dist/esm/state/reactive.js +34 -0
  50. package/dist/esm/state/state.js +148 -0
  51. package/dist/esm/state/visitor.js +28 -0
  52. package/dist/esm/state/watch.js +36 -0
  53. package/dist/esm/styles/css.js +4 -4
  54. package/dist/esm/{observation/behavior.js → styles/host.js} +0 -0
  55. package/dist/esm/templating/binding-signal.js +21 -17
  56. package/dist/esm/templating/binding-two-way.js +32 -27
  57. package/dist/esm/templating/binding.js +73 -177
  58. package/dist/esm/templating/html-directive.js +78 -7
  59. package/dist/esm/templating/node-observation.js +9 -8
  60. package/dist/esm/templating/ref.js +4 -12
  61. package/dist/esm/templating/render.js +30 -31
  62. package/dist/esm/templating/repeat.js +37 -38
  63. package/dist/esm/templating/template.js +3 -4
  64. package/dist/esm/templating/view.js +96 -24
  65. package/dist/esm/testing/exports.js +1 -0
  66. package/dist/esm/testing/fakes.js +76 -0
  67. package/dist/esm/testing/fixture.js +1 -3
  68. package/dist/esm/utilities.js +0 -95
  69. package/dist/fast-element.api.json +5720 -5385
  70. package/dist/fast-element.d.ts +510 -399
  71. package/dist/fast-element.debug.js +492 -509
  72. package/dist/fast-element.debug.min.js +1 -1
  73. package/dist/fast-element.js +492 -509
  74. package/dist/fast-element.min.js +1 -1
  75. package/dist/fast-element.untrimmed.d.ts +519 -405
  76. package/docs/api-report.md +197 -129
  77. package/package.json +8 -4
  78. package/dist/dts/hooks.d.ts +0 -20
  79. package/dist/dts/observation/behavior.d.ts +0 -19
  80. package/dist/esm/hooks.js +0 -32
@@ -147,6 +147,27 @@ function createTypeRegistry() {
147
147
  },
148
148
  });
149
149
  }
150
+ /**
151
+ * Creates a function capable of locating metadata associated with a type.
152
+ * @returns A metadata locator function.
153
+ * @internal
154
+ */
155
+ function createMetadataLocator() {
156
+ const metadataLookup = new WeakMap();
157
+ return function (target) {
158
+ let metadata = metadataLookup.get(target);
159
+ if (metadata === void 0) {
160
+ let currentTarget = Reflect.getPrototypeOf(target);
161
+ while (metadata === void 0 && currentTarget !== null) {
162
+ metadata = metadataLookup.get(currentTarget);
163
+ currentTarget = Reflect.getPrototypeOf(currentTarget);
164
+ }
165
+ metadata = metadata === void 0 ? [] : metadata.slice(0);
166
+ metadataLookup.set(target, metadata);
167
+ }
168
+ return metadata;
169
+ };
170
+ }
150
171
 
151
172
  /**
152
173
  * @internal
@@ -388,6 +409,21 @@ class PropertyChangeNotifier {
388
409
  }
389
410
  }
390
411
 
412
+ /**
413
+ * Describes how the source's lifetime relates to its controller's lifetime.
414
+ * @public
415
+ */
416
+ const SourceLifetime = Object.freeze({
417
+ /**
418
+ * The source to controller lifetime relationship is unknown.
419
+ */
420
+ unknown: void 0,
421
+ /**
422
+ * The source and controller lifetimes are coupled to one another.
423
+ * They can/will be GC'd together.
424
+ */
425
+ coupled: 1,
426
+ });
391
427
  /**
392
428
  * Common Observable APIs.
393
429
  * @public
@@ -396,7 +432,6 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
396
432
  const queueUpdate = Updates.enqueue;
397
433
  const volatileRegex = /(:|&&|\|\||if)/;
398
434
  const notifierLookup = new WeakMap();
399
- const accessorLookup = new WeakMap();
400
435
  let watcher = void 0;
401
436
  let createArrayObserver = (array) => {
402
437
  throw FAST.error(1101 /* Message.needsArrayObservation */);
@@ -411,19 +446,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
411
446
  }
412
447
  return found;
413
448
  }
414
- function getAccessors(target) {
415
- let accessors = accessorLookup.get(target);
416
- if (accessors === void 0) {
417
- let currentTarget = Reflect.getPrototypeOf(target);
418
- while (accessors === void 0 && currentTarget !== null) {
419
- accessors = accessorLookup.get(currentTarget);
420
- currentTarget = Reflect.getPrototypeOf(currentTarget);
421
- }
422
- accessors = accessors === void 0 ? [] : accessors.slice(0);
423
- accessorLookup.set(target, accessors);
424
- }
425
- return accessors;
426
- }
449
+ const getAccessors = createMetadataLocator();
427
450
  class DefaultObservableAccessor {
428
451
  constructor(name) {
429
452
  this.name = name;
@@ -467,6 +490,22 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
467
490
  setMode(isAsync) {
468
491
  this.isAsync = this.needsQueue = isAsync;
469
492
  }
493
+ bind(controller) {
494
+ this.controller = controller;
495
+ const value = this.observe(controller.source, controller.context);
496
+ if (!controller.isBound && this.requiresUnbind(controller)) {
497
+ controller.onUnbind(this);
498
+ }
499
+ return value;
500
+ }
501
+ requiresUnbind(controller) {
502
+ return (controller.sourceLifetime !== SourceLifetime.coupled ||
503
+ this.first !== this.last ||
504
+ this.first.propertySource !== controller.source);
505
+ }
506
+ unbind(controller) {
507
+ this.dispose();
508
+ }
470
509
  observe(source, context) {
471
510
  if (this.needsRefresh && this.last !== null) {
472
511
  this.dispose();
@@ -476,7 +515,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
476
515
  this.needsRefresh = this.isVolatileBinding;
477
516
  let result;
478
517
  try {
479
- result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
518
+ result = this.binding(source, context);
480
519
  }
481
520
  finally {
482
521
  watcher = previousWatcher;
@@ -667,123 +706,38 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
667
706
  * Provides additional contextual information available to behaviors and expressions.
668
707
  * @public
669
708
  */
670
- class ExecutionContext {
671
- constructor(parentSource = null, parentContext = null) {
672
- /**
673
- * The index of the current item within a repeat context.
674
- */
675
- this.index = 0;
676
- /**
677
- * The length of the current collection within a repeat context.
678
- */
679
- this.length = 0;
680
- this.parent = parentSource;
681
- this.parentContext = parentContext;
682
- }
683
- /**
684
- * The current event within an event handler.
685
- */
686
- get event() {
687
- return contextEvent.get();
688
- }
689
- /**
690
- * Indicates whether the current item within a repeat context
691
- * has an even index.
692
- */
693
- get isEven() {
694
- return this.index % 2 === 0;
695
- }
696
- /**
697
- * Indicates whether the current item within a repeat context
698
- * has an odd index.
699
- */
700
- get isOdd() {
701
- return this.index % 2 !== 0;
702
- }
703
- /**
704
- * Indicates whether the current item within a repeat context
705
- * is the first item in the collection.
706
- */
707
- get isFirst() {
708
- return this.index === 0;
709
- }
710
- /**
711
- * Indicates whether the current item within a repeat context
712
- * is somewhere in the middle of the collection.
713
- */
714
- get isInMiddle() {
715
- return !this.isFirst && !this.isLast;
716
- }
717
- /**
718
- * Indicates whether the current item within a repeat context
719
- * is the last item in the collection.
720
- */
721
- get isLast() {
722
- return this.index === this.length - 1;
723
- }
724
- /**
725
- * Returns the typed event detail of a custom event.
726
- */
727
- eventDetail() {
728
- return this.event.detail;
729
- }
730
- /**
731
- * Returns the typed event target of the event.
732
- */
733
- eventTarget() {
734
- return this.event.target;
735
- }
709
+ const ExecutionContext = Object.freeze({
736
710
  /**
737
- * Updates the position/size on a context associated with a list item.
738
- * @param index - The new index of the item.
739
- * @param length - The new length of the list.
711
+ * A default execution context.
740
712
  */
741
- updatePosition(index, length) {
742
- this.index = index;
743
- this.length = length;
744
- }
745
- /**
746
- * Creates a new execution context descendent from the current context.
747
- * @param source - The source for the context if different than the parent.
748
- * @returns A child execution context.
749
- */
750
- createChildContext(parentSource) {
751
- return new ExecutionContext(parentSource, this);
752
- }
713
+ default: {
714
+ index: 0,
715
+ length: 0,
716
+ get event() {
717
+ return ExecutionContext.getEvent();
718
+ },
719
+ eventDetail() {
720
+ return this.event.detail;
721
+ },
722
+ eventTarget() {
723
+ return this.event.target;
724
+ },
725
+ },
753
726
  /**
754
- * Creates a new execution context descent suitable for use in list rendering.
755
- * @param item - The list item to serve as the source.
756
- * @param index - The index of the item in the list.
757
- * @param length - The length of the list.
727
+ * Gets the current event.
728
+ * @returns An event object.
758
729
  */
759
- createItemContext(index, length) {
760
- const childContext = Object.create(this);
761
- childContext.index = index;
762
- childContext.length = length;
763
- return childContext;
764
- }
730
+ getEvent() {
731
+ return contextEvent.get();
732
+ },
765
733
  /**
766
- * Sets the event for the current execution context.
767
- * @param event - The event to set.
768
- * @internal
734
+ * Sets the current event.
735
+ * @param event - An event object.
769
736
  */
770
- static setEvent(event) {
737
+ setEvent(event) {
771
738
  contextEvent.set(event);
772
- }
773
- /**
774
- * Creates a new root execution context.
775
- * @returns A new execution context.
776
- */
777
- static create() {
778
- return new ExecutionContext();
779
- }
780
- }
781
- /**
782
- * The default execution context.
783
- */
784
- ExecutionContext.default = new ExecutionContext();
785
- Observable.defineProperty(ExecutionContext.prototype, "index");
786
- Observable.defineProperty(ExecutionContext.prototype, "length");
739
+ },
740
+ });
787
741
 
788
742
  /**
789
743
  * A splice map is a representation of how a previous array of items
@@ -1581,11 +1535,11 @@ class CSSPartial {
1581
1535
  }
1582
1536
  return this.css;
1583
1537
  }
1584
- bind(el) {
1585
- el.$fastController.addStyles(this.styles);
1538
+ addedCallback(controller) {
1539
+ controller.addStyles(this.styles);
1586
1540
  }
1587
- unbind(el) {
1588
- el.$fastController.removeStyles(this.styles);
1541
+ removedCallback(controller) {
1542
+ controller.removeStyles(this.styles);
1589
1543
  }
1590
1544
  }
1591
1545
  CSSDirective.define(CSSPartial);
@@ -1724,6 +1678,67 @@ const Parser = Object.freeze({
1724
1678
  },
1725
1679
  });
1726
1680
 
1681
+ /**
1682
+ * Bridges between ViewBehaviors and HostBehaviors, enabling a host to
1683
+ * control ViewBehaviors.
1684
+ * @public
1685
+ */
1686
+ const ViewBehaviorOrchestrator = Object.freeze({
1687
+ /**
1688
+ * Creates a ViewBehaviorOrchestrator.
1689
+ * @param source - The source to to associate behaviors with.
1690
+ * @returns A ViewBehaviorOrchestrator.
1691
+ */
1692
+ create(source) {
1693
+ const behaviors = [];
1694
+ const targets = {};
1695
+ let unbindables = null;
1696
+ let isConnected = false;
1697
+ return {
1698
+ source,
1699
+ context: ExecutionContext.default,
1700
+ targets,
1701
+ get isBound() {
1702
+ return isConnected;
1703
+ },
1704
+ addBehaviorFactory(factory, target) {
1705
+ const nodeId = factory.nodeId || (factory.nodeId = nextId());
1706
+ factory.id || (factory.id = nextId());
1707
+ this.addTarget(nodeId, target);
1708
+ this.addBehavior(factory.createBehavior());
1709
+ },
1710
+ addTarget(nodeId, target) {
1711
+ targets[nodeId] = target;
1712
+ },
1713
+ addBehavior(behavior) {
1714
+ behaviors.push(behavior);
1715
+ if (isConnected) {
1716
+ behavior.bind(this);
1717
+ }
1718
+ },
1719
+ onUnbind(unbindable) {
1720
+ if (unbindables === null) {
1721
+ unbindables = [];
1722
+ }
1723
+ unbindables.push(unbindable);
1724
+ },
1725
+ connectedCallback(controller) {
1726
+ if (!isConnected) {
1727
+ isConnected = true;
1728
+ behaviors.forEach(x => x.bind(this));
1729
+ }
1730
+ },
1731
+ disconnectedCallback(controller) {
1732
+ if (isConnected) {
1733
+ isConnected = false;
1734
+ if (unbindables !== null) {
1735
+ unbindables.forEach(x => x.unbind(this));
1736
+ }
1737
+ }
1738
+ },
1739
+ };
1740
+ },
1741
+ });
1727
1742
  const registry = createTypeRegistry();
1728
1743
  /**
1729
1744
  * Instructs the template engine to apply behavior to a node.
@@ -1769,6 +1784,15 @@ function htmlDirective(options) {
1769
1784
  * @public
1770
1785
  */
1771
1786
  class Binding {
1787
+ /**
1788
+ * Creates a binding.
1789
+ * @param evaluate - Evaluates the binding.
1790
+ * @param isVolatile - Indicates whether the binding is volatile.
1791
+ */
1792
+ constructor(evaluate, isVolatile = false) {
1793
+ this.evaluate = evaluate;
1794
+ this.isVolatile = isVolatile;
1795
+ }
1772
1796
  }
1773
1797
  /**
1774
1798
  * The type of HTML aspect to target.
@@ -1868,13 +1892,6 @@ class StatelessAttachedAttributeDirective {
1868
1892
  */
1869
1893
  this.id = nextId();
1870
1894
  }
1871
- /**
1872
- * Creates a behavior.
1873
- * @param targets - The targets available for behaviors to be attached to.
1874
- */
1875
- createBehavior(targets) {
1876
- return this;
1877
- }
1878
1895
  /**
1879
1896
  * Creates a placeholder string based on the directive's index within the template.
1880
1897
  * @param index - The index of the directive within the template.
@@ -1884,6 +1901,13 @@ class StatelessAttachedAttributeDirective {
1884
1901
  createHTML(add) {
1885
1902
  return Markup.attribute(add(this));
1886
1903
  }
1904
+ /**
1905
+ * Creates a behavior.
1906
+ * @param targets - The targets available for behaviors to be attached to.
1907
+ */
1908
+ createBehavior() {
1909
+ return this;
1910
+ }
1887
1911
  }
1888
1912
 
1889
1913
  const createInnerHTMLBinding = globalThis.TrustedHTML
@@ -1896,29 +1920,19 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
1896
1920
  }
1897
1921
  : (binding) => binding;
1898
1922
  class OnChangeBinding extends Binding {
1899
- constructor(evaluate, isVolatile) {
1900
- super();
1901
- this.evaluate = evaluate;
1902
- this.isVolatile = isVolatile;
1903
- }
1904
1923
  createObserver(_, subscriber) {
1905
1924
  return Observable.binding(this.evaluate, subscriber, this.isVolatile);
1906
1925
  }
1907
1926
  }
1908
1927
  class OneTimeBinding extends Binding {
1909
- constructor(evaluate) {
1910
- super();
1911
- this.evaluate = evaluate;
1912
- }
1913
1928
  createObserver() {
1914
1929
  return this;
1915
1930
  }
1916
- observe(source, context) {
1917
- return this.evaluate(source, context);
1931
+ bind(controller) {
1932
+ return this.evaluate(controller.source, controller.context);
1918
1933
  }
1919
- dispose() { }
1920
1934
  }
1921
- function updateContentTarget(target, aspect, value, source, context) {
1935
+ function updateContent(target, aspect, value, controller) {
1922
1936
  // If there's no actual value, then this equates to the
1923
1937
  // empty string for the purposes of content bindings.
1924
1938
  if (value === null || value === undefined) {
@@ -1950,14 +1964,14 @@ function updateContentTarget(target, aspect, value, source, context) {
1950
1964
  // and that there's actually no need to compose it.
1951
1965
  if (!view.isComposed) {
1952
1966
  view.isComposed = true;
1953
- view.bind(source, context);
1967
+ view.bind(controller.source);
1954
1968
  view.insertBefore(target);
1955
1969
  target.$fastView = view;
1956
1970
  target.$fastTemplate = value;
1957
1971
  }
1958
1972
  else if (view.needsBindOnly) {
1959
1973
  view.needsBindOnly = false;
1960
- view.bind(source, context);
1974
+ view.bind(controller.source);
1961
1975
  }
1962
1976
  }
1963
1977
  else {
@@ -1977,10 +1991,9 @@ function updateContentTarget(target, aspect, value, source, context) {
1977
1991
  target.textContent = value;
1978
1992
  }
1979
1993
  }
1980
- function updateTokenListTarget(target, aspect, value) {
1994
+ function updateTokenList(target, aspect, value) {
1981
1995
  var _a;
1982
- const directive = this.directive;
1983
- const lookup = `${directive.id}-t`;
1996
+ const lookup = `${this.id}-t`;
1984
1997
  const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { c: 0, v: Object.create(null) });
1985
1998
  const versions = state.v;
1986
1999
  let currentVersion = state.c;
@@ -2010,154 +2023,8 @@ function updateTokenListTarget(target, aspect, value) {
2010
2023
  }
2011
2024
  }
2012
2025
  }
2013
- /**
2014
- * A binding behavior for bindings that change.
2015
- * @public
2016
- */
2017
- class BindingBehavior {
2018
- /**
2019
- * Creates an instance of ChangeBinding.
2020
- * @param directive - The directive that has the configuration for this behavior.
2021
- * @param updateTarget - The function used to update the target with the latest value.
2022
- */
2023
- constructor(directive, updateTarget) {
2024
- this.directive = directive;
2025
- this.updateTarget = updateTarget;
2026
- this.observerProperty = `${directive.id}-o`;
2027
- }
2028
- /**
2029
- * Bind this behavior to the source.
2030
- * @param source - The source to bind to.
2031
- * @param context - The execution context that the binding is operating within.
2032
- * @param targets - The targets that behaviors in a view can attach to.
2033
- */
2034
- bind(source, context, targets) {
2035
- const directive = this.directive;
2036
- const target = targets[directive.nodeId];
2037
- const observer = this.getObserver(target);
2038
- observer.target = target;
2039
- observer.source = source;
2040
- observer.context = context;
2041
- this.updateTarget(target, directive.targetAspect, observer.observe(source, context), source, context);
2042
- }
2043
- /**
2044
- * Unbinds this behavior from the source.
2045
- * @param source - The source to unbind from.
2046
- * @param context - The execution context that the binding is operating within.
2047
- * @param targets - The targets that behaviors in a view can attach to.
2048
- */
2049
- unbind(source, context, targets) {
2050
- const target = targets[this.directive.nodeId];
2051
- const observer = this.getObserver(target);
2052
- observer.dispose();
2053
- observer.target = null;
2054
- observer.source = null;
2055
- observer.context = null;
2056
- }
2057
- /** @internal */
2058
- handleChange(binding, observer) {
2059
- const target = observer.target;
2060
- const source = observer.source;
2061
- const context = observer.context;
2062
- this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
2063
- }
2064
- /**
2065
- * Returns the binding observer used to update the node.
2066
- * @param target - The target node.
2067
- * @returns A BindingObserver.
2068
- */
2069
- getObserver(target) {
2070
- var _a;
2071
- return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = this.directive.dataBinding.createObserver(this.directive, this)));
2072
- }
2073
- /**
2074
- * Creates a behavior.
2075
- * @param targets - The targets available for behaviors to be attached to.
2076
- */
2077
- createBehavior(targets) {
2078
- return this;
2079
- }
2080
- }
2081
- /**
2082
- * A special binding behavior that can bind node content.
2083
- * @public
2084
- */
2085
- class ContentBehavior extends BindingBehavior {
2086
- /**
2087
- * Unbinds this behavior from the source.
2088
- * @param source - The source to unbind from.
2089
- * @param context - The execution context that the binding is operating within.
2090
- * @param targets - The targets that behaviors in a view can attach to.
2091
- */
2092
- unbind(source, context, targets) {
2093
- super.unbind(source, context, targets);
2094
- const target = targets[this.directive.nodeId];
2095
- const view = target.$fastView;
2096
- if (view !== void 0 && view.isComposed) {
2097
- view.unbind();
2098
- view.needsBindOnly = true;
2099
- }
2100
- }
2101
- }
2102
- /**
2103
- * A binding behavior for handling events.
2104
- * @public
2105
- */
2106
- class EventBehavior {
2107
- /**
2108
- * Creates an instance of EventBinding.
2109
- * @param directive - The directive that has the configuration for this behavior.
2110
- */
2111
- constructor(directive) {
2112
- this.directive = directive;
2113
- this.sourceProperty = `${directive.id}-s`;
2114
- this.contextProperty = `${directive.id}-c`;
2115
- }
2116
- /**
2117
- * Bind this behavior to the source.
2118
- * @param source - The source to bind to.
2119
- * @param context - The execution context that the binding is operating within.
2120
- * @param targets - The targets that behaviors in a view can attach to.
2121
- */
2122
- bind(source, context, targets) {
2123
- const directive = this.directive;
2124
- const target = targets[directive.nodeId];
2125
- target[this.sourceProperty] = source;
2126
- target[this.contextProperty] = context;
2127
- target.addEventListener(directive.targetAspect, this, directive.dataBinding.options);
2128
- }
2129
- /**
2130
- * Unbinds this behavior from the source.
2131
- * @param source - The source to unbind from.
2132
- * @param context - The execution context that the binding is operating within.
2133
- * @param targets - The targets that behaviors in a view can attach to.
2134
- */
2135
- unbind(source, context, targets) {
2136
- const directive = this.directive;
2137
- const target = targets[directive.nodeId];
2138
- target[this.sourceProperty] = target[this.contextProperty] = null;
2139
- target.removeEventListener(directive.targetAspect, this, directive.dataBinding.options);
2140
- }
2141
- /**
2142
- * Creates a behavior.
2143
- * @param targets - The targets available for behaviors to be attached to.
2144
- */
2145
- createBehavior(targets) {
2146
- return this;
2147
- }
2148
- /**
2149
- * @internal
2150
- */
2151
- handleEvent(event) {
2152
- const target = event.currentTarget;
2153
- ExecutionContext.setEvent(event);
2154
- const result = this.directive.dataBinding.evaluate(target[this.sourceProperty], target[this.contextProperty]);
2155
- ExecutionContext.setEvent(null);
2156
- if (result !== true) {
2157
- event.preventDefault();
2158
- }
2159
- }
2160
- }
2026
+ const setProperty = (t, a, v) => (t[a] = v);
2027
+ const eventTarget = () => void 0;
2161
2028
  /**
2162
2029
  * A directive that applies bindings.
2163
2030
  * @public
@@ -2169,7 +2036,7 @@ class HTMLBindingDirective {
2169
2036
  */
2170
2037
  constructor(dataBinding) {
2171
2038
  this.dataBinding = dataBinding;
2172
- this.factory = null;
2039
+ this.updateTarget = null;
2173
2040
  /**
2174
2041
  * The unique id of the factory.
2175
2042
  */
@@ -2178,6 +2045,9 @@ class HTMLBindingDirective {
2178
2045
  * The type of aspect to target.
2179
2046
  */
2180
2047
  this.aspectType = Aspect.content;
2048
+ /** @internal */
2049
+ this.bind = this.bindDefault;
2050
+ this.data = `${this.id}-d`;
2181
2051
  }
2182
2052
  /**
2183
2053
  * Creates HTML to be used within a template.
@@ -2188,37 +2058,87 @@ class HTMLBindingDirective {
2188
2058
  }
2189
2059
  /**
2190
2060
  * Creates a behavior.
2191
- * @param targets - The targets available for behaviors to be attached to.
2192
2061
  */
2193
- createBehavior(targets) {
2194
- if (this.factory == null) {
2062
+ createBehavior() {
2063
+ if (this.updateTarget === null) {
2195
2064
  if (this.targetAspect === "innerHTML") {
2196
2065
  this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
2197
2066
  }
2198
2067
  switch (this.aspectType) {
2199
2068
  case 1:
2200
- this.factory = new BindingBehavior(this, DOM.setAttribute);
2069
+ this.updateTarget = DOM.setAttribute;
2201
2070
  break;
2202
2071
  case 2:
2203
- this.factory = new BindingBehavior(this, DOM.setBooleanAttribute);
2072
+ this.updateTarget = DOM.setBooleanAttribute;
2204
2073
  break;
2205
2074
  case 3:
2206
- this.factory = new BindingBehavior(this, (t, a, v) => (t[a] = v));
2075
+ this.updateTarget = setProperty;
2207
2076
  break;
2208
2077
  case 4:
2209
- this.factory = new ContentBehavior(this, updateContentTarget);
2078
+ this.bind = this.bindContent;
2079
+ this.updateTarget = updateContent;
2210
2080
  break;
2211
2081
  case 5:
2212
- this.factory = new BindingBehavior(this, updateTokenListTarget);
2082
+ this.updateTarget = updateTokenList;
2213
2083
  break;
2214
2084
  case 6:
2215
- this.factory = new EventBehavior(this);
2085
+ this.bind = this.bindEvent;
2086
+ this.updateTarget = eventTarget;
2216
2087
  break;
2217
2088
  default:
2218
2089
  throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2219
2090
  }
2220
2091
  }
2221
- return this.factory.createBehavior(targets);
2092
+ return this;
2093
+ }
2094
+ /** @internal */
2095
+ bindDefault(controller) {
2096
+ var _a;
2097
+ const target = controller.targets[this.nodeId];
2098
+ const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
2099
+ observer.target = target;
2100
+ observer.controller = controller;
2101
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
2102
+ if (this.updateTarget === updateContent) {
2103
+ controller.onUnbind(this);
2104
+ }
2105
+ }
2106
+ /** @internal */
2107
+ bindContent(controller) {
2108
+ this.bindDefault(controller);
2109
+ controller.onUnbind(this);
2110
+ }
2111
+ /** @internal */
2112
+ bindEvent(controller) {
2113
+ const target = controller.targets[this.nodeId];
2114
+ target[this.data] = controller;
2115
+ target.addEventListener(this.targetAspect, this, this.dataBinding.options);
2116
+ }
2117
+ /** @internal */
2118
+ unbind(controller) {
2119
+ const target = controller.targets[this.nodeId];
2120
+ const view = target.$fastView;
2121
+ if (view !== void 0 && view.isComposed) {
2122
+ view.unbind();
2123
+ view.needsBindOnly = true;
2124
+ }
2125
+ }
2126
+ /** @internal */
2127
+ handleEvent(event) {
2128
+ const target = event.currentTarget;
2129
+ ExecutionContext.setEvent(event);
2130
+ const controller = target[this.data];
2131
+ const result = this.dataBinding.evaluate(controller.source, controller.context);
2132
+ ExecutionContext.setEvent(null);
2133
+ if (result !== true) {
2134
+ event.preventDefault();
2135
+ }
2136
+ }
2137
+ /** @internal */
2138
+ handleChange(binding, observer) {
2139
+ const target = observer.target;
2140
+ const controller = observer.controller;
2141
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
2222
2142
  }
2223
2143
  }
2224
2144
  HTMLDirective.define(HTMLBindingDirective, { aspected: true });
@@ -2293,17 +2213,83 @@ class HTMLView {
2293
2213
  this.factories = factories;
2294
2214
  this.targets = targets;
2295
2215
  this.behaviors = null;
2216
+ this.unbindables = [];
2296
2217
  /**
2297
2218
  * The data that the view is bound to.
2298
2219
  */
2299
2220
  this.source = null;
2221
+ this.isBound = false;
2222
+ this.selfContained = false;
2300
2223
  /**
2301
- * The execution context the view is running within.
2224
+ * The index of the current item within a repeat context.
2302
2225
  */
2303
- this.context = null;
2226
+ this.index = 0;
2227
+ /**
2228
+ * The length of the current collection within a repeat context.
2229
+ */
2230
+ this.length = 0;
2304
2231
  this.firstChild = fragment.firstChild;
2305
2232
  this.lastChild = fragment.lastChild;
2306
2233
  }
2234
+ /**
2235
+ * The execution context the view is running within.
2236
+ */
2237
+ get context() {
2238
+ return this;
2239
+ }
2240
+ /**
2241
+ * The current event within an event handler.
2242
+ */
2243
+ get event() {
2244
+ return ExecutionContext.getEvent();
2245
+ }
2246
+ /**
2247
+ * Indicates whether the current item within a repeat context
2248
+ * has an even index.
2249
+ */
2250
+ get isEven() {
2251
+ return this.index % 2 === 0;
2252
+ }
2253
+ /**
2254
+ * Indicates whether the current item within a repeat context
2255
+ * has an odd index.
2256
+ */
2257
+ get isOdd() {
2258
+ return this.index % 2 !== 0;
2259
+ }
2260
+ /**
2261
+ * Indicates whether the current item within a repeat context
2262
+ * is the first item in the collection.
2263
+ */
2264
+ get isFirst() {
2265
+ return this.index === 0;
2266
+ }
2267
+ /**
2268
+ * Indicates whether the current item within a repeat context
2269
+ * is somewhere in the middle of the collection.
2270
+ */
2271
+ get isInMiddle() {
2272
+ return !this.isFirst && !this.isLast;
2273
+ }
2274
+ /**
2275
+ * Indicates whether the current item within a repeat context
2276
+ * is the last item in the collection.
2277
+ */
2278
+ get isLast() {
2279
+ return this.index === this.length - 1;
2280
+ }
2281
+ /**
2282
+ * Returns the typed event detail of a custom event.
2283
+ */
2284
+ eventDetail() {
2285
+ return this.event.detail;
2286
+ }
2287
+ /**
2288
+ * Returns the typed event target of the event.
2289
+ */
2290
+ eventTarget() {
2291
+ return this.event.target;
2292
+ }
2307
2293
  /**
2308
2294
  * Appends the view's DOM nodes to the referenced node.
2309
2295
  * @param node - The parent node to append the view's DOM nodes to.
@@ -2358,58 +2344,61 @@ class HTMLView {
2358
2344
  removeNodeSequence(this.firstChild, this.lastChild);
2359
2345
  this.unbind();
2360
2346
  }
2347
+ onUnbind(behavior) {
2348
+ this.unbindables.push(behavior);
2349
+ }
2361
2350
  /**
2362
2351
  * Binds a view's behaviors to its binding source.
2363
2352
  * @param source - The binding source for the view's binding behaviors.
2364
2353
  * @param context - The execution context to run the behaviors within.
2365
2354
  */
2366
- bind(source, context) {
2367
- let behaviors = this.behaviors;
2355
+ bind(source) {
2368
2356
  const oldSource = this.source;
2369
2357
  if (oldSource === source) {
2370
2358
  return;
2371
2359
  }
2360
+ let behaviors = this.behaviors;
2372
2361
  this.source = source;
2373
- this.context = context;
2374
- const targets = this.targets;
2375
- if (oldSource !== null) {
2376
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2377
- const current = behaviors[i];
2378
- current.unbind(oldSource, context, targets);
2379
- current.bind(source, context, targets);
2380
- }
2381
- }
2382
- else if (behaviors === null) {
2362
+ if (behaviors === null) {
2383
2363
  this.behaviors = behaviors = new Array(this.factories.length);
2384
2364
  const factories = this.factories;
2385
2365
  for (let i = 0, ii = factories.length; i < ii; ++i) {
2386
- const behavior = factories[i].createBehavior(targets);
2387
- behavior.bind(source, context, targets);
2366
+ const behavior = factories[i].createBehavior();
2367
+ behavior.bind(this);
2388
2368
  behaviors[i] = behavior;
2389
2369
  }
2390
2370
  }
2391
2371
  else {
2372
+ if (oldSource !== null) {
2373
+ this.evaluateUnbindables();
2374
+ }
2392
2375
  for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2393
- behaviors[i].bind(source, context, targets);
2376
+ behaviors[i].bind(this);
2394
2377
  }
2395
2378
  }
2379
+ this.isBound = true;
2396
2380
  }
2397
2381
  /**
2398
2382
  * Unbinds a view's behaviors from its binding source.
2399
2383
  */
2400
2384
  unbind() {
2385
+ if (!this.isBound) {
2386
+ return;
2387
+ }
2401
2388
  const oldSource = this.source;
2402
2389
  if (oldSource === null) {
2403
2390
  return;
2404
2391
  }
2405
- const targets = this.targets;
2406
- const context = this.context;
2407
- const behaviors = this.behaviors;
2408
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2409
- behaviors[i].unbind(oldSource, context, targets);
2410
- }
2392
+ this.evaluateUnbindables();
2411
2393
  this.source = null;
2412
- this.context = null;
2394
+ this.isBound = false;
2395
+ }
2396
+ evaluateUnbindables() {
2397
+ const unbindables = this.unbindables;
2398
+ for (let i = 0, ii = unbindables.length; i < ii; ++i) {
2399
+ unbindables[i].unbind(this);
2400
+ }
2401
+ unbindables.length = 0;
2413
2402
  }
2414
2403
  /**
2415
2404
  * Efficiently disposes of a contiguous range of synthetic view instances.
@@ -2425,6 +2414,8 @@ class HTMLView {
2425
2414
  }
2426
2415
  }
2427
2416
  }
2417
+ Observable.defineProperty(HTMLView.prototype, "index");
2418
+ Observable.defineProperty(HTMLView.prototype, "length");
2428
2419
 
2429
2420
  const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
2430
2421
  const descriptorCache = {};
@@ -2745,9 +2736,9 @@ class ViewTemplate {
2745
2736
  * @param hostBindingTarget - An HTML element to target the host bindings at if different from the
2746
2737
  * host that the template is being attached to.
2747
2738
  */
2748
- render(source, host, hostBindingTarget, context) {
2749
- const view = this.create(hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : host);
2750
- view.bind(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
2739
+ render(source, host, hostBindingTarget) {
2740
+ const view = this.create(hostBindingTarget);
2741
+ view.bind(source);
2751
2742
  view.appendTo(host);
2752
2743
  return view;
2753
2744
  }
@@ -2824,20 +2815,12 @@ function html(strings, ...values) {
2824
2815
  */
2825
2816
  class RefDirective extends StatelessAttachedAttributeDirective {
2826
2817
  /**
2827
- * Bind this behavior to the source.
2828
- * @param source - The source to bind to.
2829
- * @param context - The execution context that the binding is operating within.
2830
- * @param targets - The targets that behaviors in a view can attach to.
2818
+ * Bind this behavior.
2819
+ * @param controller - The view controller that manages the lifecycle of this behavior.
2831
2820
  */
2832
- bind(source, context, targets) {
2833
- source[this.options] = targets[this.nodeId];
2821
+ bind(controller) {
2822
+ controller.source[this.options] = controller.targets[this.nodeId];
2834
2823
  }
2835
- /**
2836
- * Unbinds this behavior from the source.
2837
- * @param source - The source to unbind from.
2838
- */
2839
- /* eslint-disable-next-line @typescript-eslint/no-empty-function */
2840
- unbind() { }
2841
2824
  }
2842
2825
  HTMLDirective.define(RefDirective);
2843
2826
  /**
@@ -2866,11 +2849,17 @@ const defaultRepeatOptions = Object.freeze({
2866
2849
  positioning: false,
2867
2850
  recycle: true,
2868
2851
  });
2869
- function bindWithoutPositioning(view, items, index, context) {
2870
- view.bind(items[index], context);
2852
+ function bindWithoutPositioning(view, items, index, controller) {
2853
+ view.context.parent = controller.source;
2854
+ view.context.parentContext = controller.context;
2855
+ view.bind(items[index]);
2871
2856
  }
2872
- function bindWithPositioning(view, items, index, context) {
2873
- view.bind(items[index], context.createItemContext(index, items.length));
2857
+ function bindWithPositioning(view, items, index, controller) {
2858
+ view.context.parent = controller.source;
2859
+ view.context.parentContext = controller.context;
2860
+ view.context.length = items.length;
2861
+ view.context.index = index;
2862
+ view.bind(items[index]);
2874
2863
  }
2875
2864
  /**
2876
2865
  * A behavior that renders a template for each item in an array.
@@ -2886,15 +2875,11 @@ class RepeatBehavior {
2886
2875
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
2887
2876
  * @param options - Options used to turn on special repeat features.
2888
2877
  */
2889
- constructor(directive, location) {
2878
+ constructor(directive) {
2890
2879
  this.directive = directive;
2891
- this.location = location;
2892
- this.source = null;
2893
2880
  this.views = [];
2894
2881
  this.items = null;
2895
2882
  this.itemsObserver = null;
2896
- this.context = void 0;
2897
- this.childContext = void 0;
2898
2883
  this.bindView = bindWithoutPositioning;
2899
2884
  this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
2900
2885
  this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
@@ -2903,32 +2888,26 @@ class RepeatBehavior {
2903
2888
  }
2904
2889
  }
2905
2890
  /**
2906
- * Bind this behavior to the source.
2907
- * @param source - The source to bind to.
2908
- * @param context - The execution context that the binding is operating within.
2891
+ * Bind this behavior.
2892
+ * @param controller - The view controller that manages the lifecycle of this behavior.
2909
2893
  */
2910
- bind(source, context) {
2911
- this.source = source;
2912
- this.context = context;
2913
- this.childContext = context.createChildContext(source);
2914
- this.items = this.itemsBindingObserver.observe(source, this.context);
2915
- this.template = this.templateBindingObserver.observe(source, this.context);
2894
+ bind(controller) {
2895
+ this.location = controller.targets[this.directive.nodeId];
2896
+ this.controller = controller;
2897
+ this.items = this.itemsBindingObserver.bind(controller);
2898
+ this.template = this.templateBindingObserver.bind(controller);
2916
2899
  this.observeItems(true);
2917
2900
  this.refreshAllViews();
2901
+ controller.onUnbind(this);
2918
2902
  }
2919
2903
  /**
2920
- * Unbinds this behavior from the source.
2921
- * @param source - The source to unbind from.
2904
+ * Unbinds this behavior.
2922
2905
  */
2923
2906
  unbind() {
2924
- this.source = null;
2925
- this.items = null;
2926
2907
  if (this.itemsObserver !== null) {
2927
2908
  this.itemsObserver.unsubscribe(this);
2928
2909
  }
2929
2910
  this.unbindAllViews();
2930
- this.itemsBindingObserver.dispose();
2931
- this.templateBindingObserver.dispose();
2932
2911
  }
2933
2912
  /**
2934
2913
  * Handles changes in the array, its items, and the repeat template.
@@ -2937,12 +2916,12 @@ class RepeatBehavior {
2937
2916
  */
2938
2917
  handleChange(source, args) {
2939
2918
  if (args === this.itemsBindingObserver) {
2940
- this.items = this.itemsBindingObserver.observe(this.source, this.context);
2919
+ this.items = this.itemsBindingObserver.bind(this.controller);
2941
2920
  this.observeItems();
2942
2921
  this.refreshAllViews();
2943
2922
  }
2944
2923
  else if (args === this.templateBindingObserver) {
2945
- this.template = this.templateBindingObserver.observe(this.source, this.context);
2924
+ this.template = this.templateBindingObserver.bind(this.controller);
2946
2925
  this.refreshAllViews(true);
2947
2926
  }
2948
2927
  else if (!args[0]) {
@@ -2972,10 +2951,10 @@ class RepeatBehavior {
2972
2951
  }
2973
2952
  updateViews(splices) {
2974
2953
  const views = this.views;
2975
- const childContext = this.childContext;
2976
2954
  const bindView = this.bindView;
2977
2955
  const items = this.items;
2978
2956
  const template = this.template;
2957
+ const controller = this.controller;
2979
2958
  const recycle = this.directive.options.recycle;
2980
2959
  const leftoverViews = [];
2981
2960
  let leftoverIndex = 0;
@@ -2987,13 +2966,14 @@ class RepeatBehavior {
2987
2966
  let addIndex = splice.index;
2988
2967
  const end = addIndex + splice.addedCount;
2989
2968
  const removedViews = views.splice(splice.index, removed.length);
2990
- availableViews = leftoverViews.length + removedViews.length;
2969
+ const totalAvailableViews = (availableViews =
2970
+ leftoverViews.length + removedViews.length);
2991
2971
  for (; addIndex < end; ++addIndex) {
2992
2972
  const neighbor = views[addIndex];
2993
2973
  const location = neighbor ? neighbor.firstChild : this.location;
2994
2974
  let view;
2995
2975
  if (recycle && availableViews > 0) {
2996
- if (removeIndex <= availableViews && removedViews.length > 0) {
2976
+ if (removeIndex <= totalAvailableViews && removedViews.length > 0) {
2997
2977
  view = removedViews[removeIndex];
2998
2978
  removeIndex++;
2999
2979
  }
@@ -3007,7 +2987,7 @@ class RepeatBehavior {
3007
2987
  view = template.create();
3008
2988
  }
3009
2989
  views.splice(addIndex, 0, view);
3010
- bindView(view, items, addIndex, childContext);
2990
+ bindView(view, items, addIndex, controller);
3011
2991
  view.insertBefore(location);
3012
2992
  }
3013
2993
  if (removedViews[removeIndex]) {
@@ -3019,7 +2999,9 @@ class RepeatBehavior {
3019
2999
  }
3020
3000
  if (this.directive.options.positioning) {
3021
3001
  for (let i = 0, ii = views.length; i < ii; ++i) {
3022
- views[i].context.updatePosition(i, ii);
3002
+ const context = views[i].context;
3003
+ context.length = i;
3004
+ context.index = ii;
3023
3005
  }
3024
3006
  }
3025
3007
  }
@@ -3028,7 +3010,7 @@ class RepeatBehavior {
3028
3010
  const template = this.template;
3029
3011
  const location = this.location;
3030
3012
  const bindView = this.bindView;
3031
- const childContext = this.childContext;
3013
+ const controller = this.controller;
3032
3014
  let itemsLength = items.length;
3033
3015
  let views = this.views;
3034
3016
  let viewsLength = views.length;
@@ -3042,7 +3024,7 @@ class RepeatBehavior {
3042
3024
  this.views = views = new Array(itemsLength);
3043
3025
  for (let i = 0; i < itemsLength; ++i) {
3044
3026
  const view = template.create();
3045
- bindView(view, items, i, childContext);
3027
+ bindView(view, items, i, controller);
3046
3028
  views[i] = view;
3047
3029
  view.insertBefore(location);
3048
3030
  }
@@ -3053,11 +3035,11 @@ class RepeatBehavior {
3053
3035
  for (; i < itemsLength; ++i) {
3054
3036
  if (i < viewsLength) {
3055
3037
  const view = views[i];
3056
- bindView(view, items, i, childContext);
3038
+ bindView(view, items, i, controller);
3057
3039
  }
3058
3040
  else {
3059
3041
  const view = template.create();
3060
- bindView(view, items, i, childContext);
3042
+ bindView(view, items, i, controller);
3061
3043
  views.push(view);
3062
3044
  view.insertBefore(location);
3063
3045
  }
@@ -3107,8 +3089,8 @@ class RepeatDirective {
3107
3089
  * Creates a behavior for the provided target node.
3108
3090
  * @param target - The node instance to create the behavior for.
3109
3091
  */
3110
- createBehavior(targets) {
3111
- return new RepeatBehavior(this, targets[this.nodeId]);
3092
+ createBehavior() {
3093
+ return new RepeatBehavior(this);
3112
3094
  }
3113
3095
  }
3114
3096
  HTMLDirective.define(RepeatDirective);
@@ -3152,11 +3134,12 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3152
3134
  * @param context - The execution context that the binding is operating within.
3153
3135
  * @param targets - The targets that behaviors in a view can attach to.
3154
3136
  */
3155
- bind(source, context, targets) {
3156
- const target = targets[this.nodeId];
3157
- target[this.sourceProperty] = source;
3158
- this.updateTarget(source, this.computeNodes(target));
3137
+ bind(controller) {
3138
+ const target = controller.targets[this.nodeId];
3139
+ target[this.sourceProperty] = controller.source;
3140
+ this.updateTarget(controller.source, this.computeNodes(target));
3159
3141
  this.observe(target);
3142
+ controller.onUnbind(this);
3160
3143
  }
3161
3144
  /**
3162
3145
  * Unbinds this behavior from the source.
@@ -3164,9 +3147,9 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3164
3147
  * @param context - The execution context that the binding is operating within.
3165
3148
  * @param targets - The targets that behaviors in a view can attach to.
3166
3149
  */
3167
- unbind(source, context, targets) {
3168
- const target = targets[this.nodeId];
3169
- this.updateTarget(source, emptyArray);
3150
+ unbind(controller) {
3151
+ const target = controller.targets[this.nodeId];
3152
+ this.updateTarget(controller.source, emptyArray);
3170
3153
  this.disconnect(target);
3171
3154
  target[this.sourceProperty] = null;
3172
3155
  }
@@ -3315,6 +3298,16 @@ function children(propertyOrOptions) {
3315
3298
 
3316
3299
  const booleanMode = "boolean";
3317
3300
  const reflectMode = "reflect";
3301
+ /**
3302
+ * Metadata used to configure a custom attribute's behavior.
3303
+ * @public
3304
+ */
3305
+ const AttributeConfiguration = Object.freeze({
3306
+ /**
3307
+ * Locates all attribute configurations associated with a type.
3308
+ */
3309
+ locate: createMetadataLocator(),
3310
+ });
3318
3311
  /**
3319
3312
  * A {@link ValueConverter} that converts to and from `boolean` values.
3320
3313
  * @remarks
@@ -3452,7 +3445,7 @@ class AttributeDefinition {
3452
3445
  */
3453
3446
  static collect(Owner, ...attributeLists) {
3454
3447
  const attributes = [];
3455
- attributeLists.push(Owner.attributes);
3448
+ attributeLists.push(AttributeConfiguration.locate(Owner));
3456
3449
  for (let i = 0, ii = attributeLists.length; i < ii; ++i) {
3457
3450
  const list = attributeLists[i];
3458
3451
  if (list === void 0) {
@@ -3482,9 +3475,7 @@ function attr(configOrTarget, prop) {
3482
3475
  // - @attr({...opts})
3483
3476
  config.property = $prop;
3484
3477
  }
3485
- const attributes = $target.constructor.attributes ||
3486
- ($target.constructor.attributes = []);
3487
- attributes.push(config);
3478
+ AttributeConfiguration.locate($target.constructor).push(config);
3488
3479
  }
3489
3480
  if (arguments.length > 1) {
3490
3481
  // Non invocation:
@@ -3509,6 +3500,7 @@ const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */
3509
3500
  */
3510
3501
  class FASTElementDefinition {
3511
3502
  constructor(type, nameOrConfig = type.definition) {
3503
+ var _a;
3512
3504
  this.platformDefined = false;
3513
3505
  if (isString(nameOrConfig)) {
3514
3506
  nameOrConfig = { name: nameOrConfig };
@@ -3516,6 +3508,7 @@ class FASTElementDefinition {
3516
3508
  this.type = type;
3517
3509
  this.name = nameOrConfig.name;
3518
3510
  this.template = nameOrConfig.template;
3511
+ this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
3519
3512
  const proto = type.prototype;
3520
3513
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3521
3514
  const observedAttributes = new Array(attributes.length);
@@ -3560,7 +3553,7 @@ class FASTElementDefinition {
3560
3553
  * @remarks
3561
3554
  * This operation is idempotent per registry.
3562
3555
  */
3563
- define(registry = customElements) {
3556
+ define(registry = this.registry) {
3564
3557
  const type = this.type;
3565
3558
  if (!registry.get(this.name)) {
3566
3559
  this.platformDefined = true;
@@ -3594,22 +3587,22 @@ FASTElementDefinition.getByType = fastElementRegistry.getByType;
3594
3587
  */
3595
3588
  FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
3596
3589
 
3597
- const shadowRoots = new WeakMap();
3598
3590
  const defaultEventOptions = {
3599
3591
  bubbles: true,
3600
3592
  composed: true,
3601
3593
  cancelable: true,
3602
3594
  };
3595
+ const isConnectedPropertyName = "isConnected";
3596
+ const shadowRoots = new WeakMap();
3603
3597
  function getShadowRoot(element) {
3604
3598
  var _a, _b;
3605
3599
  return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
3606
3600
  }
3607
- const isConnectedPropertyName = "isConnected";
3608
3601
  /**
3609
3602
  * Controls the lifecycle and rendering of a `FASTElement`.
3610
3603
  * @public
3611
3604
  */
3612
- class Controller extends PropertyChangeNotifier {
3605
+ class ElementController extends PropertyChangeNotifier {
3613
3606
  /**
3614
3607
  * Creates a Controller to control the specified element.
3615
3608
  * @param element - The element to be controlled by this controller.
@@ -3620,12 +3613,12 @@ class Controller extends PropertyChangeNotifier {
3620
3613
  constructor(element, definition) {
3621
3614
  super(element);
3622
3615
  this.boundObservables = null;
3623
- this.behaviors = null;
3624
3616
  this.needsInitialization = true;
3625
3617
  this.hasExistingShadowRoot = false;
3626
3618
  this._template = null;
3627
- this._styles = null;
3628
3619
  this._isConnected = false;
3620
+ this.behaviors = null;
3621
+ this._mainStyles = null;
3629
3622
  /**
3630
3623
  * This allows Observable.getNotifier(...) to return the Controller
3631
3624
  * when the notifier for the Controller itself is being requested. The
@@ -3641,7 +3634,7 @@ class Controller extends PropertyChangeNotifier {
3641
3634
  * If `null` then the element is managing its own rendering.
3642
3635
  */
3643
3636
  this.view = null;
3644
- this.element = element;
3637
+ this.source = element;
3645
3638
  this.definition = definition;
3646
3639
  const shadowOptions = definition.shadowOptions;
3647
3640
  if (shadowOptions !== void 0) {
@@ -3695,9 +3688,9 @@ class Controller extends PropertyChangeNotifier {
3695
3688
  // 1. Template overrides take top precedence.
3696
3689
  if (this._template === null) {
3697
3690
  const definition = this.definition;
3698
- if (this.element.resolveTemplate) {
3691
+ if (this.source.resolveTemplate) {
3699
3692
  // 2. Allow for element instance overrides next.
3700
- this._template = this.element.resolveTemplate();
3693
+ this._template = this.source.resolveTemplate();
3701
3694
  }
3702
3695
  else if (definition.template) {
3703
3696
  // 3. Default to the static definition.
@@ -3716,48 +3709,92 @@ class Controller extends PropertyChangeNotifier {
3716
3709
  }
3717
3710
  }
3718
3711
  /**
3719
- * Gets/sets the primary styles used for the component.
3720
- * @remarks
3721
- * This value can only be accurately read after connect but can be set at any time.
3712
+ * The main set of styles used for the component, independent
3713
+ * of any dynamically added styles.
3722
3714
  */
3723
- get styles() {
3715
+ get mainStyles() {
3724
3716
  var _a;
3725
3717
  // 1. Styles overrides take top precedence.
3726
- if (this._styles === null) {
3718
+ if (this._mainStyles === null) {
3727
3719
  const definition = this.definition;
3728
- if (this.element.resolveStyles) {
3720
+ if (this.source.resolveStyles) {
3729
3721
  // 2. Allow for element instance overrides next.
3730
- this._styles = this.element.resolveStyles();
3722
+ this._mainStyles = this.source.resolveStyles();
3731
3723
  }
3732
3724
  else if (definition.styles) {
3733
3725
  // 3. Default to the static definition.
3734
- this._styles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
3726
+ this._mainStyles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
3735
3727
  }
3736
3728
  }
3737
- return this._styles;
3729
+ return this._mainStyles;
3738
3730
  }
3739
- set styles(value) {
3740
- if (this._styles === value) {
3731
+ set mainStyles(value) {
3732
+ if (this._mainStyles === value) {
3741
3733
  return;
3742
3734
  }
3743
- if (this._styles !== null) {
3744
- this.removeStyles(this._styles);
3735
+ if (this._mainStyles !== null) {
3736
+ this.removeStyles(this._mainStyles);
3745
3737
  }
3746
- this._styles = value;
3738
+ this._mainStyles = value;
3747
3739
  if (!this.needsInitialization) {
3748
3740
  this.addStyles(value);
3749
3741
  }
3750
3742
  }
3743
+ /**
3744
+ * Adds the behavior to the component.
3745
+ * @param behavior - The behavior to add.
3746
+ */
3747
+ addBehavior(behavior) {
3748
+ var _a, _b;
3749
+ const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
3750
+ const count = (_b = targetBehaviors.get(behavior)) !== null && _b !== void 0 ? _b : 0;
3751
+ if (count === 0) {
3752
+ targetBehaviors.set(behavior, 1);
3753
+ behavior.addedCallback && behavior.addedCallback(this);
3754
+ if (behavior.connectedCallback && this.isConnected) {
3755
+ behavior.connectedCallback(this);
3756
+ }
3757
+ }
3758
+ else {
3759
+ targetBehaviors.set(behavior, count + 1);
3760
+ }
3761
+ }
3762
+ /**
3763
+ * Removes the behavior from the component.
3764
+ * @param behavior - The behavior to remove.
3765
+ * @param force - Forces removal even if this behavior was added more than once.
3766
+ */
3767
+ removeBehavior(behavior, force = false) {
3768
+ const targetBehaviors = this.behaviors;
3769
+ if (targetBehaviors === null) {
3770
+ return;
3771
+ }
3772
+ const count = targetBehaviors.get(behavior);
3773
+ if (count === void 0) {
3774
+ return;
3775
+ }
3776
+ if (count === 1 || force) {
3777
+ targetBehaviors.delete(behavior);
3778
+ if (behavior.disconnectedCallback && this.isConnected) {
3779
+ behavior.disconnectedCallback(this);
3780
+ }
3781
+ behavior.removedCallback && behavior.removedCallback(this);
3782
+ }
3783
+ else {
3784
+ targetBehaviors.set(behavior, count - 1);
3785
+ }
3786
+ }
3751
3787
  /**
3752
3788
  * Adds styles to this element. Providing an HTMLStyleElement will attach the element instance to the shadowRoot.
3753
3789
  * @param styles - The styles to add.
3754
3790
  */
3755
3791
  addStyles(styles) {
3792
+ var _a;
3756
3793
  if (!styles) {
3757
3794
  return;
3758
3795
  }
3759
- const target = getShadowRoot(this.element) ||
3760
- this.element.getRootNode();
3796
+ const source = this.source;
3797
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
3761
3798
  if (styles instanceof HTMLElement) {
3762
3799
  target.append(styles);
3763
3800
  }
@@ -3765,7 +3802,9 @@ class Controller extends PropertyChangeNotifier {
3765
3802
  const sourceBehaviors = styles.behaviors;
3766
3803
  styles.addStylesTo(target);
3767
3804
  if (sourceBehaviors !== null) {
3768
- this.addBehaviors(sourceBehaviors);
3805
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
3806
+ this.addBehavior(sourceBehaviors[i]);
3807
+ }
3769
3808
  }
3770
3809
  }
3771
3810
  }
@@ -3774,11 +3813,12 @@ class Controller extends PropertyChangeNotifier {
3774
3813
  * @param styles - the styles to remove.
3775
3814
  */
3776
3815
  removeStyles(styles) {
3816
+ var _a;
3777
3817
  if (!styles) {
3778
3818
  return;
3779
3819
  }
3780
- const target = getShadowRoot(this.element) ||
3781
- this.element.getRootNode();
3820
+ const source = this.source;
3821
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
3782
3822
  if (styles instanceof HTMLElement) {
3783
3823
  target.removeChild(styles);
3784
3824
  }
@@ -3786,85 +3826,29 @@ class Controller extends PropertyChangeNotifier {
3786
3826
  const sourceBehaviors = styles.behaviors;
3787
3827
  styles.removeStylesFrom(target);
3788
3828
  if (sourceBehaviors !== null) {
3789
- this.removeBehaviors(sourceBehaviors);
3790
- }
3791
- }
3792
- }
3793
- /**
3794
- * Adds behaviors to this element.
3795
- * @param behaviors - The behaviors to add.
3796
- */
3797
- addBehaviors(behaviors) {
3798
- var _a;
3799
- const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
3800
- const length = behaviors.length;
3801
- const behaviorsToBind = [];
3802
- for (let i = 0; i < length; ++i) {
3803
- const behavior = behaviors[i];
3804
- if (targetBehaviors.has(behavior)) {
3805
- targetBehaviors.set(behavior, targetBehaviors.get(behavior) + 1);
3806
- }
3807
- else {
3808
- targetBehaviors.set(behavior, 1);
3809
- behaviorsToBind.push(behavior);
3810
- }
3811
- }
3812
- if (this._isConnected) {
3813
- const element = this.element;
3814
- const context = ExecutionContext.default;
3815
- for (let i = 0; i < behaviorsToBind.length; ++i) {
3816
- behaviorsToBind[i].bind(element, context);
3817
- }
3818
- }
3819
- }
3820
- /**
3821
- * Removes behaviors from this element.
3822
- * @param behaviors - The behaviors to remove.
3823
- * @param force - Forces unbinding of behaviors.
3824
- */
3825
- removeBehaviors(behaviors, force = false) {
3826
- const targetBehaviors = this.behaviors;
3827
- if (targetBehaviors === null) {
3828
- return;
3829
- }
3830
- const length = behaviors.length;
3831
- const behaviorsToUnbind = [];
3832
- for (let i = 0; i < length; ++i) {
3833
- const behavior = behaviors[i];
3834
- if (targetBehaviors.has(behavior)) {
3835
- const count = targetBehaviors.get(behavior) - 1;
3836
- count === 0 || force
3837
- ? targetBehaviors.delete(behavior) && behaviorsToUnbind.push(behavior)
3838
- : targetBehaviors.set(behavior, count);
3839
- }
3840
- }
3841
- if (this._isConnected) {
3842
- const element = this.element;
3843
- const context = ExecutionContext.default;
3844
- for (let i = 0; i < behaviorsToUnbind.length; ++i) {
3845
- behaviorsToUnbind[i].unbind(element, context);
3829
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
3830
+ this.addBehavior(sourceBehaviors[i]);
3831
+ }
3846
3832
  }
3847
3833
  }
3848
3834
  }
3849
3835
  /**
3850
3836
  * Runs connected lifecycle behavior on the associated element.
3851
3837
  */
3852
- onConnectedCallback() {
3838
+ connect() {
3853
3839
  if (this._isConnected) {
3854
3840
  return;
3855
3841
  }
3856
- const element = this.element;
3857
- const context = ExecutionContext.default;
3858
3842
  if (this.needsInitialization) {
3859
3843
  this.finishInitialization();
3860
3844
  }
3861
3845
  else if (this.view !== null) {
3862
- this.view.bind(element, context);
3846
+ this.view.bind(this.source);
3863
3847
  }
3864
3848
  const behaviors = this.behaviors;
3865
3849
  if (behaviors !== null) {
3866
- for (const behavior of behaviors.keys()) {
3867
- behavior.bind(element, context);
3850
+ for (const key of behaviors.keys()) {
3851
+ key.connectedCallback && key.connectedCallback(this);
3868
3852
  }
3869
3853
  }
3870
3854
  this.setIsConnected(true);
@@ -3872,21 +3856,18 @@ class Controller extends PropertyChangeNotifier {
3872
3856
  /**
3873
3857
  * Runs disconnected lifecycle behavior on the associated element.
3874
3858
  */
3875
- onDisconnectedCallback() {
3859
+ disconnect() {
3876
3860
  if (!this._isConnected) {
3877
3861
  return;
3878
3862
  }
3879
3863
  this.setIsConnected(false);
3880
- const view = this.view;
3881
- if (view !== null) {
3882
- view.unbind();
3864
+ if (this.view !== null) {
3865
+ this.view.unbind();
3883
3866
  }
3884
3867
  const behaviors = this.behaviors;
3885
3868
  if (behaviors !== null) {
3886
- const element = this.element;
3887
- const context = ExecutionContext.default;
3888
- for (const behavior of behaviors.keys()) {
3889
- behavior.unbind(element, context);
3869
+ for (const key of behaviors.keys()) {
3870
+ key.disconnectedCallback && key.disconnectedCallback(this);
3890
3871
  }
3891
3872
  }
3892
3873
  }
@@ -3899,7 +3880,7 @@ class Controller extends PropertyChangeNotifier {
3899
3880
  onAttributeChangedCallback(name, oldValue, newValue) {
3900
3881
  const attrDef = this.definition.attributeLookup[name];
3901
3882
  if (attrDef !== void 0) {
3902
- attrDef.onAttributeChangedCallback(this.element, newValue);
3883
+ attrDef.onAttributeChangedCallback(this.source, newValue);
3903
3884
  }
3904
3885
  }
3905
3886
  /**
@@ -3912,12 +3893,12 @@ class Controller extends PropertyChangeNotifier {
3912
3893
  */
3913
3894
  emit(type, detail, options) {
3914
3895
  if (this._isConnected) {
3915
- return this.element.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
3896
+ return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
3916
3897
  }
3917
3898
  return false;
3918
3899
  }
3919
3900
  finishInitialization() {
3920
- const element = this.element;
3901
+ const element = this.source;
3921
3902
  const boundObservables = this.boundObservables;
3922
3903
  // If we have any observables that were bound, re-apply their values.
3923
3904
  if (boundObservables !== null) {
@@ -3929,15 +3910,15 @@ class Controller extends PropertyChangeNotifier {
3929
3910
  this.boundObservables = null;
3930
3911
  }
3931
3912
  this.renderTemplate(this.template);
3932
- this.addStyles(this.styles);
3913
+ this.addStyles(this.mainStyles);
3933
3914
  this.needsInitialization = false;
3934
3915
  }
3935
3916
  renderTemplate(template) {
3936
3917
  var _a;
3937
- const element = this.element;
3938
3918
  // When getting the host to render to, we start by looking
3939
3919
  // up the shadow root. If there isn't one, then that means
3940
3920
  // we're doing a Light DOM render to the element's direct children.
3921
+ const element = this.source;
3941
3922
  const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
3942
3923
  if (this.view !== null) {
3943
3924
  // If there's already a view, we need to unbind and remove through dispose.
@@ -3954,6 +3935,8 @@ class Controller extends PropertyChangeNotifier {
3954
3935
  if (template) {
3955
3936
  // If a new template was provided, render it.
3956
3937
  this.view = template.render(element, host, element);
3938
+ this.view.sourceLifetime =
3939
+ SourceLifetime.coupled;
3957
3940
  }
3958
3941
  }
3959
3942
  /**
@@ -3973,7 +3956,7 @@ class Controller extends PropertyChangeNotifier {
3973
3956
  if (definition === void 0) {
3974
3957
  throw FAST.error(1401 /* Message.missingElementDefinition */);
3975
3958
  }
3976
- return (element.$fastController = new Controller(element, definition));
3959
+ return (element.$fastController = new ElementController(element, definition));
3977
3960
  }
3978
3961
  }
3979
3962
 
@@ -3983,16 +3966,16 @@ function createFASTElement(BaseType) {
3983
3966
  constructor() {
3984
3967
  /* eslint-disable-next-line */
3985
3968
  super();
3986
- Controller.forCustomElement(this);
3969
+ ElementController.forCustomElement(this);
3987
3970
  }
3988
3971
  $emit(type, detail, options) {
3989
3972
  return this.$fastController.emit(type, detail, options);
3990
3973
  }
3991
3974
  connectedCallback() {
3992
- this.$fastController.onConnectedCallback();
3975
+ this.$fastController.connect();
3993
3976
  }
3994
3977
  disconnectedCallback() {
3995
- this.$fastController.onDisconnectedCallback();
3978
+ this.$fastController.disconnect();
3996
3979
  }
3997
3980
  attributeChangedCallback(name, oldValue, newValue) {
3998
3981
  this.$fastController.onAttributeChangedCallback(name, oldValue, newValue);
@@ -4052,4 +4035,4 @@ function customElement(nameOrDef) {
4052
4035
  };
4053
4036
  }
4054
4037
 
4055
- 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 };
4038
+ 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 };