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

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 (58) hide show
  1. package/CHANGELOG.json +147 -0
  2. package/CHANGELOG.md +42 -1
  3. package/dist/dts/components/fast-definitions.d.ts +11 -8
  4. package/dist/dts/components/fast-element.d.ts +13 -3
  5. package/dist/dts/context.d.ts +157 -0
  6. package/dist/dts/di/di.d.ts +854 -0
  7. package/dist/dts/hooks.d.ts +2 -2
  8. package/dist/dts/interfaces.d.ts +39 -7
  9. package/dist/dts/metadata.d.ts +25 -0
  10. package/dist/dts/observation/arrays.d.ts +1 -1
  11. package/dist/dts/observation/behavior.d.ts +4 -4
  12. package/dist/dts/observation/observable.d.ts +59 -72
  13. package/dist/dts/styles/element-styles.d.ts +6 -0
  14. package/dist/dts/templating/binding-signal.d.ts +21 -0
  15. package/dist/dts/templating/binding-two-way.d.ts +31 -0
  16. package/dist/dts/templating/binding.d.ts +74 -201
  17. package/dist/dts/templating/compiler.d.ts +1 -2
  18. package/dist/dts/templating/html-directive.d.ts +31 -3
  19. package/dist/dts/templating/render.d.ts +277 -0
  20. package/dist/dts/templating/repeat.d.ts +13 -63
  21. package/dist/dts/templating/template.d.ts +11 -60
  22. package/dist/dts/templating/view.d.ts +9 -9
  23. package/dist/dts/templating/when.d.ts +3 -3
  24. package/dist/dts/testing/exports.d.ts +2 -0
  25. package/dist/dts/testing/fixture.d.ts +90 -0
  26. package/dist/dts/testing/timeout.d.ts +7 -0
  27. package/dist/{tsdoc-metadata.json → dts/tsdoc-metadata.json} +0 -0
  28. package/dist/esm/components/fast-definitions.js +27 -27
  29. package/dist/esm/components/fast-element.js +20 -4
  30. package/dist/esm/context.js +163 -0
  31. package/dist/esm/debug.js +35 -4
  32. package/dist/esm/di/di.js +1349 -0
  33. package/dist/esm/metadata.js +60 -0
  34. package/dist/esm/observation/arrays.js +1 -1
  35. package/dist/esm/observation/observable.js +73 -21
  36. package/dist/esm/platform.js +1 -1
  37. package/dist/esm/styles/element-styles.js +14 -0
  38. package/dist/esm/templating/binding-signal.js +79 -0
  39. package/dist/esm/templating/binding-two-way.js +98 -0
  40. package/dist/esm/templating/binding.js +137 -313
  41. package/dist/esm/templating/compiler.js +30 -7
  42. package/dist/esm/templating/html-directive.js +16 -2
  43. package/dist/esm/templating/render.js +392 -0
  44. package/dist/esm/templating/repeat.js +60 -38
  45. package/dist/esm/templating/template.js +9 -26
  46. package/dist/esm/templating/when.js +5 -4
  47. package/dist/esm/testing/exports.js +2 -0
  48. package/dist/esm/testing/fixture.js +88 -0
  49. package/dist/esm/testing/timeout.js +24 -0
  50. package/dist/fast-element.api.json +8509 -10358
  51. package/dist/fast-element.d.ts +315 -522
  52. package/dist/fast-element.debug.js +417 -438
  53. package/dist/fast-element.debug.min.js +1 -1
  54. package/dist/fast-element.js +382 -434
  55. package/dist/fast-element.min.js +1 -1
  56. package/dist/fast-element.untrimmed.d.ts +324 -529
  57. package/docs/api-report.md +124 -232
  58. package/package.json +32 -4
@@ -97,19 +97,50 @@ const debugMessages = {
97
97
  [1101 /* needsArrayObservation */]: "Must call enableArrayObservation before observing arrays.",
98
98
  [1201 /* onlySetHTMLPolicyOnce */]: "The HTML policy can only be set once.",
99
99
  [1202 /* bindingInnerHTMLRequiresTrustedTypes */]: "To bind innerHTML, you must use a TrustedTypesPolicy.",
100
+ [1203 /* twoWayBindingRequiresObservables */]: "View=>Model update skipped. To use twoWay binding, the target property must be observable.",
101
+ [1204 /* hostBindingWithoutHost */]: "No host element is present. Cannot bind host with ${name}.",
102
+ [1205 /* unsupportedBindingBehavior */]: "The requested binding behavior is not supported by the binding engine.",
100
103
  [1401 /* missingElementDefinition */]: "Missing FASTElement definition.",
104
+ [1501 /* noRegistrationForContext */]: "No registration for Context/Interface '${name}'.",
105
+ [1502 /* noFactoryForResolver */]: "Dependency injection resolver for '${key}' returned a null factory.",
106
+ [1503 /* invalidResolverStrategy */]: "Invalid dependency injection resolver strategy specified '${strategy}'.",
107
+ [1504 /* cannotAutoregisterDependency */]: "Unable to autoregister dependency.",
108
+ [1505 /* cannotResolveKey */]: "Unable to resolve dependency injection key '${key}'.",
109
+ [1506 /* cannotConstructNativeFunction */]: "'${name}' is a native function and therefore cannot be safely constructed by DI. If this is intentional, please use a callback or cachedCallback resolver.",
110
+ [1507 /* cannotJITRegisterNonConstructor */]: "Attempted to jitRegister something that is not a constructor '${value}'. Did you forget to register this dependency?",
111
+ [1508 /* cannotJITRegisterIntrinsic */]: "Attempted to jitRegister an intrinsic type '${value}'. Did you forget to add @inject(Key)?",
112
+ [1509 /* cannotJITRegisterInterface */]: "Attempted to jitRegister an interface '${value}'.",
113
+ [1510 /* invalidResolver */]: "A valid resolver was not returned from the register method.",
114
+ [1511 /* invalidKey */]: "Key/value cannot be null or undefined. Are you trying to inject/register something that doesn't exist with DI?",
115
+ [1512 /* noDefaultResolver */]: "'${key}' not registered. Did you forget to add @singleton()?",
116
+ [1513 /* cyclicDependency */]: "Cyclic dependency found '${name}'.",
101
117
  };
118
+ const allPlaceholders = /(\$\{\w+?})/g;
119
+ const placeholder = /\$\{(\w+?)}/g;
120
+ const noValues = Object.freeze({});
121
+ function formatMessage(message, values) {
122
+ return message
123
+ .split(allPlaceholders)
124
+ .map(v => {
125
+ var _a;
126
+ const replaced = v.replace(placeholder, "$1");
127
+ return String((_a = values[replaced]) !== null && _a !== void 0 ? _a : v);
128
+ })
129
+ .join("");
130
+ }
102
131
  Object.assign(FAST$1, {
103
132
  addMessages(messages) {
104
133
  Object.assign(debugMessages, messages);
105
134
  },
106
- warn(code, ...args) {
135
+ warn(code, values = noValues) {
107
136
  var _a;
108
- console.warn((_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Warning");
137
+ const message = (_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Warning";
138
+ console.warn(formatMessage(message, values));
109
139
  },
110
- error(code, ...args) {
140
+ error(code, values = noValues) {
111
141
  var _a;
112
- return new Error((_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Error");
142
+ const message = (_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Error";
143
+ return new Error(formatMessage(message, values));
113
144
  },
114
145
  });
115
146
 
@@ -141,7 +172,7 @@ if (FAST.error === void 0) {
141
172
  Object.assign(FAST, {
142
173
  warn() { },
143
174
  error(code) {
144
- return new Error(`Code ${code}`);
175
+ return new Error(`Error ${code}`);
145
176
  },
146
177
  addMessages() { },
147
178
  });
@@ -478,7 +509,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
478
509
  }
479
510
  }
480
511
  }
481
- class BindingObserverImplementation extends SubscriberSet {
512
+ class ExpressionNotifierImplementation extends SubscriberSet {
482
513
  constructor(binding, initialSubscriber, isVolatileBinding = false) {
483
514
  super(binding, initialSubscriber);
484
515
  this.binding = binding;
@@ -633,14 +664,14 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
633
664
  */
634
665
  getAccessors,
635
666
  /**
636
- * Creates a {@link BindingObserver} that can watch the
637
- * provided {@link Binding} for changes.
667
+ * Creates a {@link ExpressionNotifier} that can watch the
668
+ * provided {@link Expression} for changes.
638
669
  * @param binding - The binding to observe.
639
670
  * @param initialSubscriber - An initial subscriber to changes in the binding value.
640
671
  * @param isVolatileBinding - Indicates whether the binding's dependency list must be re-evaluated on every value evaluation.
641
672
  */
642
673
  binding(binding, initialSubscriber, isVolatileBinding = this.isVolatileBinding(binding)) {
643
- return new BindingObserverImplementation(binding, initialSubscriber, isVolatileBinding);
674
+ return new ExpressionNotifierImplementation(binding, initialSubscriber, isVolatileBinding);
644
675
  },
645
676
  /**
646
677
  * Determines whether a binding expression is volatile and needs to have its dependency list re-evaluated
@@ -687,75 +718,127 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
687
718
  },
688
719
  };
689
720
  });
690
- class DefaultExecutionContext {
721
+ /**
722
+ * Provides additional contextual information available to behaviors and expressions.
723
+ * @public
724
+ */
725
+ class ExecutionContext {
691
726
  constructor(parentSource = null, parentContext = null) {
727
+ /**
728
+ * The index of the current item within a repeat context.
729
+ */
692
730
  this.index = 0;
731
+ /**
732
+ * The length of the current collection within a repeat context.
733
+ */
693
734
  this.length = 0;
694
735
  this.parent = parentSource;
695
736
  this.parentContext = parentContext;
696
737
  }
738
+ /**
739
+ * The current event within an event handler.
740
+ */
697
741
  get event() {
698
742
  return contextEvent.get();
699
743
  }
744
+ /**
745
+ * Indicates whether the current item within a repeat context
746
+ * has an even index.
747
+ */
700
748
  get isEven() {
701
749
  return this.index % 2 === 0;
702
750
  }
751
+ /**
752
+ * Indicates whether the current item within a repeat context
753
+ * has an odd index.
754
+ */
703
755
  get isOdd() {
704
756
  return this.index % 2 !== 0;
705
757
  }
758
+ /**
759
+ * Indicates whether the current item within a repeat context
760
+ * is the first item in the collection.
761
+ */
706
762
  get isFirst() {
707
763
  return this.index === 0;
708
764
  }
765
+ /**
766
+ * Indicates whether the current item within a repeat context
767
+ * is somewhere in the middle of the collection.
768
+ */
709
769
  get isInMiddle() {
710
770
  return !this.isFirst && !this.isLast;
711
771
  }
772
+ /**
773
+ * Indicates whether the current item within a repeat context
774
+ * is the last item in the collection.
775
+ */
712
776
  get isLast() {
713
777
  return this.index === this.length - 1;
714
778
  }
779
+ /**
780
+ * Returns the typed event detail of a custom event.
781
+ */
715
782
  eventDetail() {
716
783
  return this.event.detail;
717
784
  }
785
+ /**
786
+ * Returns the typed event target of the event.
787
+ */
718
788
  eventTarget() {
719
789
  return this.event.target;
720
790
  }
791
+ /**
792
+ * Updates the position/size on a context associated with a list item.
793
+ * @param index - The new index of the item.
794
+ * @param length - The new length of the list.
795
+ */
721
796
  updatePosition(index, length) {
722
797
  this.index = index;
723
798
  this.length = length;
724
799
  }
800
+ /**
801
+ * Creates a new execution context descendent from the current context.
802
+ * @param source - The source for the context if different than the parent.
803
+ * @returns A child execution context.
804
+ */
725
805
  createChildContext(parentSource) {
726
- return new DefaultExecutionContext(parentSource, this);
806
+ return new ExecutionContext(parentSource, this);
727
807
  }
808
+ /**
809
+ * Creates a new execution context descent suitable for use in list rendering.
810
+ * @param item - The list item to serve as the source.
811
+ * @param index - The index of the item in the list.
812
+ * @param length - The length of the list.
813
+ */
728
814
  createItemContext(index, length) {
729
815
  const childContext = Object.create(this);
730
816
  childContext.index = index;
731
817
  childContext.length = length;
732
818
  return childContext;
733
819
  }
734
- }
735
- Observable.defineProperty(DefaultExecutionContext.prototype, "index");
736
- Observable.defineProperty(DefaultExecutionContext.prototype, "length");
737
- /**
738
- * The common execution context APIs.
739
- * @public
740
- */
741
- const ExecutionContext = Object.freeze({
742
- default: new DefaultExecutionContext(),
743
820
  /**
744
821
  * Sets the event for the current execution context.
745
822
  * @param event - The event to set.
746
823
  * @internal
747
824
  */
748
- setEvent(event) {
825
+ static setEvent(event) {
749
826
  contextEvent.set(event);
750
- },
827
+ }
751
828
  /**
752
829
  * Creates a new root execution context.
753
830
  * @returns A new execution context.
754
831
  */
755
- create() {
756
- return new DefaultExecutionContext();
757
- },
758
- });
832
+ static create() {
833
+ return new ExecutionContext();
834
+ }
835
+ }
836
+ /**
837
+ * The default execution context.
838
+ */
839
+ ExecutionContext.default = new ExecutionContext();
840
+ Observable.defineProperty(ExecutionContext.prototype, "index");
841
+ Observable.defineProperty(ExecutionContext.prototype, "length");
759
842
 
760
843
  /**
761
844
  * A splice map is a representation of how a previous array of items
@@ -1010,7 +1093,7 @@ const ArrayObserver = Object.freeze({
1010
1093
  * @returns The length of the array.
1011
1094
  * @public
1012
1095
  */
1013
- function length(array) {
1096
+ function lengthOf(array) {
1014
1097
  if (!array) {
1015
1098
  return 0;
1016
1099
  }
@@ -1094,6 +1177,20 @@ class ElementStyles {
1094
1177
  static setDefaultStrategy(Strategy) {
1095
1178
  DefaultStyleStrategy = Strategy;
1096
1179
  }
1180
+ /**
1181
+ * Normalizes a set of composable style options.
1182
+ * @param styles - The style options to normalize.
1183
+ * @returns A singular ElementStyles instance or undefined.
1184
+ */
1185
+ static normalize(styles) {
1186
+ return styles === void 0
1187
+ ? void 0
1188
+ : Array.isArray(styles)
1189
+ ? new ElementStyles(styles)
1190
+ : styles instanceof ElementStyles
1191
+ ? styles
1192
+ : new ElementStyles([styles]);
1193
+ }
1097
1194
  }
1098
1195
  /**
1099
1196
  * Indicates whether the DOM supports the adoptedStyleSheets feature.
@@ -1420,6 +1517,13 @@ function htmlDirective(options) {
1420
1517
  HTMLDirective.define(type, options);
1421
1518
  };
1422
1519
  }
1520
+ /**
1521
+ * Captures a binding expression along with related information and capabilities.
1522
+ *
1523
+ * @public
1524
+ */
1525
+ class Binding {
1526
+ }
1423
1527
  /**
1424
1528
  * The type of HTML aspect to target.
1425
1529
  * @public
@@ -1457,12 +1561,15 @@ const Aspect = Object.freeze({
1457
1561
  *
1458
1562
  * @param directive - The directive to assign the aspect to.
1459
1563
  * @param value - The value to base the aspect determination on.
1564
+ * @remarks
1565
+ * If a falsy value is provided, then the content aspect will be assigned.
1460
1566
  */
1461
1567
  assign(directive, value) {
1462
- directive.sourceAspect = value;
1463
1568
  if (!value) {
1569
+ directive.aspectType = Aspect.content;
1464
1570
  return;
1465
1571
  }
1572
+ directive.sourceAspect = value;
1466
1573
  switch (value[0]) {
1467
1574
  case ":":
1468
1575
  directive.targetAspect = value.substring(1);
@@ -1510,6 +1617,10 @@ class StatelessAttachedAttributeDirective {
1510
1617
  */
1511
1618
  constructor(options) {
1512
1619
  this.options = options;
1620
+ /**
1621
+ * The unique id of the factory.
1622
+ */
1623
+ this.id = nextId();
1513
1624
  }
1514
1625
  /**
1515
1626
  * Creates a behavior.
@@ -1538,99 +1649,28 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
1538
1649
  throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
1539
1650
  }
1540
1651
  : (binding) => binding;
1541
- /**
1542
- * Describes how aspects of an HTML element will be affected by bindings.
1543
- * @public
1544
- */
1545
- const BindingMode = Object.freeze({
1546
- /**
1547
- * Creates a binding mode based on the supplied behavior types.
1548
- * @param UpdateType - The base behavior type used to update aspects.
1549
- * @param EventType - The base behavior type used to respond to events.
1550
- * @returns A new binding mode.
1551
- */
1552
- define(UpdateType, EventType = EventBinding) {
1553
- return Object.freeze({
1554
- [1]: d => new UpdateType(d, DOM.setAttribute),
1555
- [2]: d => new UpdateType(d, DOM.setBooleanAttribute),
1556
- [3]: d => new UpdateType(d, (t, a, v) => (t[a] = v)),
1557
- [4]: d => new (createContentBinding(UpdateType))(d, updateContentTarget),
1558
- [5]: d => new UpdateType(d, updateTokenListTarget),
1559
- [6]: d => new EventType(d),
1560
- });
1561
- },
1562
- });
1563
- /**
1564
- * Describes the configuration for a binding expression.
1565
- * @public
1566
- */
1567
- const BindingConfig = Object.freeze({
1568
- /**
1569
- * Creates a binding configuration based on the provided mode and options.
1570
- * @param mode - The mode to use for the configuration.
1571
- * @param defaultOptions - The default options to use for the configuration.
1572
- * @returns A new binding configuration.
1573
- */
1574
- define(mode, defaultOptions) {
1575
- const config = (options) => {
1576
- return {
1577
- mode: config.mode,
1578
- options: Object.assign({}, defaultOptions, options),
1579
- };
1580
- };
1581
- config.options = defaultOptions;
1582
- config.mode = mode;
1583
- return config;
1584
- },
1585
- });
1586
- /**
1587
- * A base binding behavior for DOM updates.
1588
- * @public
1589
- */
1590
- class UpdateBinding {
1591
- /**
1592
- * Creates an instance of UpdateBinding.
1593
- * @param directive - The directive that has the configuration for this behavior.
1594
- * @param updateTarget - The function used to update the target with the latest value.
1595
- */
1596
- constructor(directive, updateTarget) {
1597
- this.directive = directive;
1598
- this.updateTarget = updateTarget;
1652
+ class OnChangeBinding extends Binding {
1653
+ constructor(evaluate, isVolatile) {
1654
+ super();
1655
+ this.evaluate = evaluate;
1656
+ this.isVolatile = isVolatile;
1599
1657
  }
1600
- /**
1601
- * Bind this behavior to the source.
1602
- * @param source - The source to bind to.
1603
- * @param context - The execution context that the binding is operating within.
1604
- * @param targets - The targets that behaviors in a view can attach to.
1605
- */
1606
- bind(source, context, targets) { }
1607
- /**
1608
- * Unbinds this behavior from the source.
1609
- * @param source - The source to unbind from.
1610
- * @param context - The execution context that the binding is operating within.
1611
- * @param targets - The targets that behaviors in a view can attach to.
1612
- */
1613
- unbind(source, context, targets) { }
1614
- /**
1615
- * Creates a behavior.
1616
- * @param targets - The targets available for behaviors to be attached to.
1617
- */
1618
- createBehavior(targets) {
1619
- return this;
1658
+ createObserver(_, subscriber) {
1659
+ return Observable.binding(this.evaluate, subscriber, this.isVolatile);
1620
1660
  }
1621
1661
  }
1622
- function createContentBinding(Type) {
1623
- return class extends Type {
1624
- unbind(source, context, targets) {
1625
- super.unbind(source, context, targets);
1626
- const target = targets[this.directive.nodeId];
1627
- const view = target.$fastView;
1628
- if (view !== void 0 && view.isComposed) {
1629
- view.unbind();
1630
- view.needsBindOnly = true;
1631
- }
1632
- }
1633
- };
1662
+ class OneTimeBinding extends Binding {
1663
+ constructor(evaluate) {
1664
+ super();
1665
+ this.evaluate = evaluate;
1666
+ }
1667
+ createObserver() {
1668
+ return this;
1669
+ }
1670
+ observe(source, context) {
1671
+ return this.evaluate(source, context);
1672
+ }
1673
+ dispose() { }
1634
1674
  }
1635
1675
  function updateContentTarget(target, aspect, value, source, context) {
1636
1676
  // If there's no actual value, then this equates to the
@@ -1638,7 +1678,7 @@ function updateContentTarget(target, aspect, value, source, context) {
1638
1678
  if (value === null || value === undefined) {
1639
1679
  value = "";
1640
1680
  }
1641
- // If the value has a "create" method, then it's a template-like.
1681
+ // If the value has a "create" method, then it's a ContentTemplate.
1642
1682
  if (value.create) {
1643
1683
  target.textContent = "";
1644
1684
  let view = target.$fastView;
@@ -1724,118 +1764,21 @@ function updateTokenListTarget(target, aspect, value) {
1724
1764
  }
1725
1765
  }
1726
1766
  }
1727
- /**
1728
- * A binding behavior for one-time bindings.
1729
- * @public
1730
- */
1731
- class OneTimeBinding extends UpdateBinding {
1732
- /**
1733
- * Bind this behavior to the source.
1734
- * @param source - The source to bind to.
1735
- * @param context - The execution context that the binding is operating within.
1736
- * @param targets - The targets that behaviors in a view can attach to.
1737
- */
1738
- bind(source, context, targets) {
1739
- const directive = this.directive;
1740
- this.updateTarget(targets[directive.nodeId], directive.targetAspect, directive.binding(source, context), source, context);
1741
- }
1742
- }
1743
- const signals = Object.create(null);
1744
- /**
1745
- * A binding behavior for signal bindings.
1746
- * @public
1747
- */
1748
- class SignalBinding extends UpdateBinding {
1749
- constructor() {
1750
- super(...arguments);
1751
- this.handlerProperty = `${this.directive.id}-h`;
1752
- }
1753
- /**
1754
- * Bind this behavior to the source.
1755
- * @param source - The source to bind to.
1756
- * @param context - The execution context that the binding is operating within.
1757
- * @param targets - The targets that behaviors in a view can attach to.
1758
- */
1759
- bind(source, context, targets) {
1760
- const directive = this.directive;
1761
- const target = targets[directive.nodeId];
1762
- const signal = this.getSignal(source, context);
1763
- const handler = (target[this.handlerProperty] = () => {
1764
- this.updateTarget(target, directive.targetAspect, directive.binding(source, context), source, context);
1765
- });
1766
- handler();
1767
- const found = signals[signal];
1768
- if (found) {
1769
- Array.isArray(found)
1770
- ? found.push(handler)
1771
- : (signals[signal] = [found, handler]);
1772
- }
1773
- else {
1774
- signals[signal] = handler;
1775
- }
1776
- }
1777
- /**
1778
- * Unbinds this behavior from the source.
1779
- * @param source - The source to unbind from.
1780
- * @param context - The execution context that the binding is operating within.
1781
- * @param targets - The targets that behaviors in a view can attach to.
1782
- */
1783
- unbind(source, context, targets) {
1784
- const signal = this.getSignal(source, context);
1785
- const found = signals[signal];
1786
- if (found && Array.isArray(found)) {
1787
- const directive = this.directive;
1788
- const target = targets[directive.nodeId];
1789
- const handler = target[this.handlerProperty];
1790
- const index = found.indexOf(handler);
1791
- if (index !== -1) {
1792
- found.splice(index, 1);
1793
- }
1794
- }
1795
- else {
1796
- signals[signal] = void 0;
1797
- }
1798
- }
1799
- getSignal(source, context) {
1800
- const options = this.directive.options;
1801
- return isString(options) ? options : options(source, context);
1802
- }
1803
- /**
1804
- * Sends the specified signal to signaled bindings.
1805
- * @param signal - The signal to send.
1806
- * @public
1807
- */
1808
- static send(signal) {
1809
- const found = signals[signal];
1810
- if (found) {
1811
- Array.isArray(found) ? found.forEach(x => x()) : found();
1812
- }
1813
- }
1814
- }
1815
1767
  /**
1816
1768
  * A binding behavior for bindings that change.
1817
1769
  * @public
1818
1770
  */
1819
- class ChangeBinding extends UpdateBinding {
1771
+ class BindingBehavior {
1820
1772
  /**
1821
1773
  * Creates an instance of ChangeBinding.
1822
1774
  * @param directive - The directive that has the configuration for this behavior.
1823
1775
  * @param updateTarget - The function used to update the target with the latest value.
1824
1776
  */
1825
1777
  constructor(directive, updateTarget) {
1826
- super(directive, updateTarget);
1827
- this.isBindingVolatile = Observable.isVolatileBinding(directive.binding);
1778
+ this.directive = directive;
1779
+ this.updateTarget = updateTarget;
1828
1780
  this.observerProperty = `${directive.id}-o`;
1829
1781
  }
1830
- /**
1831
- * Returns the binding observer used to update the node.
1832
- * @param target - The target node.
1833
- * @returns A BindingObserver.
1834
- */
1835
- getObserver(target) {
1836
- var _a;
1837
- return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = Observable.binding(this.directive.binding, this, this.isBindingVolatile)));
1838
- }
1839
1782
  /**
1840
1783
  * Bind this behavior to the source.
1841
1784
  * @param source - The source to bind to.
@@ -1872,12 +1815,49 @@ class ChangeBinding extends UpdateBinding {
1872
1815
  const context = observer.context;
1873
1816
  this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
1874
1817
  }
1818
+ /**
1819
+ * Returns the binding observer used to update the node.
1820
+ * @param target - The target node.
1821
+ * @returns A BindingObserver.
1822
+ */
1823
+ getObserver(target) {
1824
+ var _a;
1825
+ return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = this.directive.dataBinding.createObserver(this.directive, this)));
1826
+ }
1827
+ /**
1828
+ * Creates a behavior.
1829
+ * @param targets - The targets available for behaviors to be attached to.
1830
+ */
1831
+ createBehavior(targets) {
1832
+ return this;
1833
+ }
1834
+ }
1835
+ /**
1836
+ * A special binding behavior that can bind node content.
1837
+ * @public
1838
+ */
1839
+ class ContentBehavior extends BindingBehavior {
1840
+ /**
1841
+ * Unbinds this behavior from the source.
1842
+ * @param source - The source to unbind from.
1843
+ * @param context - The execution context that the binding is operating within.
1844
+ * @param targets - The targets that behaviors in a view can attach to.
1845
+ */
1846
+ unbind(source, context, targets) {
1847
+ super.unbind(source, context, targets);
1848
+ const target = targets[this.directive.nodeId];
1849
+ const view = target.$fastView;
1850
+ if (view !== void 0 && view.isComposed) {
1851
+ view.unbind();
1852
+ view.needsBindOnly = true;
1853
+ }
1854
+ }
1875
1855
  }
1876
1856
  /**
1877
1857
  * A binding behavior for handling events.
1878
1858
  * @public
1879
1859
  */
1880
- class EventBinding {
1860
+ class EventBehavior {
1881
1861
  /**
1882
1862
  * Creates an instance of EventBinding.
1883
1863
  * @param directive - The directive that has the configuration for this behavior.
@@ -1898,7 +1878,7 @@ class EventBinding {
1898
1878
  const target = targets[directive.nodeId];
1899
1879
  target[this.sourceProperty] = source;
1900
1880
  target[this.contextProperty] = context;
1901
- target.addEventListener(directive.targetAspect, this, directive.options);
1881
+ target.addEventListener(directive.targetAspect, this, directive.dataBinding.options);
1902
1882
  }
1903
1883
  /**
1904
1884
  * Unbinds this behavior from the source.
@@ -1910,7 +1890,7 @@ class EventBinding {
1910
1890
  const directive = this.directive;
1911
1891
  const target = targets[directive.nodeId];
1912
1892
  target[this.sourceProperty] = target[this.contextProperty] = null;
1913
- target.removeEventListener(directive.targetAspect, this, directive.options);
1893
+ target.removeEventListener(directive.targetAspect, this, directive.dataBinding.options);
1914
1894
  }
1915
1895
  /**
1916
1896
  * Creates a behavior.
@@ -1925,110 +1905,13 @@ class EventBinding {
1925
1905
  handleEvent(event) {
1926
1906
  const target = event.currentTarget;
1927
1907
  ExecutionContext.setEvent(event);
1928
- const result = this.directive.binding(target[this.sourceProperty], target[this.contextProperty]);
1908
+ const result = this.directive.dataBinding.evaluate(target[this.sourceProperty], target[this.contextProperty]);
1929
1909
  ExecutionContext.setEvent(null);
1930
1910
  if (result !== true) {
1931
1911
  event.preventDefault();
1932
1912
  }
1933
1913
  }
1934
1914
  }
1935
- let twoWaySettings = {
1936
- determineChangeEvent() {
1937
- return "change";
1938
- },
1939
- };
1940
- /**
1941
- * A binding behavior for bindings that update in two directions.
1942
- * @public
1943
- */
1944
- class TwoWayBinding extends ChangeBinding {
1945
- /**
1946
- * Bind this behavior to the source.
1947
- * @param source - The source to bind to.
1948
- * @param context - The execution context that the binding is operating within.
1949
- * @param targets - The targets that behaviors in a view can attach to.
1950
- */
1951
- bind(source, context, targets) {
1952
- var _a;
1953
- super.bind(source, context, targets);
1954
- const directive = this.directive;
1955
- const target = targets[directive.nodeId];
1956
- if (!this.changeEvent) {
1957
- this.changeEvent =
1958
- (_a = directive.options.changeEvent) !== null && _a !== void 0 ? _a : twoWaySettings.determineChangeEvent(directive, target);
1959
- }
1960
- target.addEventListener(this.changeEvent, this);
1961
- }
1962
- /**
1963
- * Unbinds this behavior from the source.
1964
- * @param source - The source to unbind from.
1965
- * @param context - The execution context that the binding is operating within.
1966
- * @param targets - The targets that behaviors in a view can attach to.
1967
- */
1968
- unbind(source, context, targets) {
1969
- super.unbind(source, context, targets);
1970
- targets[this.directive.nodeId].removeEventListener(this.changeEvent, this);
1971
- }
1972
- /** @internal */
1973
- handleEvent(event) {
1974
- const directive = this.directive;
1975
- const target = event.currentTarget;
1976
- let value;
1977
- switch (directive.aspectType) {
1978
- case 1:
1979
- value = target.getAttribute(directive.targetAspect);
1980
- break;
1981
- case 2:
1982
- value = target.hasAttribute(directive.targetAspect);
1983
- break;
1984
- case 4:
1985
- value = target.innerText;
1986
- break;
1987
- default:
1988
- value = target[directive.targetAspect];
1989
- break;
1990
- }
1991
- const observer = this.getObserver(target);
1992
- const last = observer.last; // using internal API!!!
1993
- last.propertySource[last.propertyName] = directive.options.fromView(value);
1994
- }
1995
- /**
1996
- * Configures two-way binding.
1997
- * @param settings - The settings to use for the two-way binding system.
1998
- */
1999
- static configure(settings) {
2000
- twoWaySettings = settings;
2001
- }
2002
- }
2003
- /**
2004
- * The default onChange binding configuration.
2005
- * @public
2006
- */
2007
- const onChange = BindingConfig.define(BindingMode.define(ChangeBinding), {});
2008
- /**
2009
- * The default twoWay binding configuration.
2010
- * @public
2011
- */
2012
- const twoWay = BindingConfig.define(BindingMode.define(TwoWayBinding), {
2013
- fromView: v => v,
2014
- });
2015
- /**
2016
- * The default onTime binding configuration.
2017
- * @public
2018
- */
2019
- const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
2020
- once: true,
2021
- });
2022
- const signalMode = BindingMode.define(SignalBinding);
2023
- /**
2024
- * Creates a signal binding configuration with the supplied options.
2025
- * @param options - The signal name or a binding to use to retrieve the signal name.
2026
- * @returns A binding configuration.
2027
- * @public
2028
- */
2029
- const signal = (options) => {
2030
- return { mode: signalMode, options };
2031
- };
2032
1915
  /**
2033
1916
  * A directive that applies bindings.
2034
1917
  * @public
@@ -2036,15 +1919,15 @@ const signal = (options) => {
2036
1919
  class HTMLBindingDirective {
2037
1920
  /**
2038
1921
  * Creates an instance of HTMLBindingDirective.
2039
- * @param binding - The binding to apply.
2040
- * @param mode - The binding mode to use when applying the binding.
2041
- * @param options - The options to configure the binding with.
1922
+ * @param dataBinding - The binding configuration to apply.
2042
1923
  */
2043
- constructor(binding, mode, options) {
2044
- this.binding = binding;
2045
- this.mode = mode;
2046
- this.options = options;
1924
+ constructor(dataBinding) {
1925
+ this.dataBinding = dataBinding;
2047
1926
  this.factory = null;
1927
+ /**
1928
+ * The unique id of the factory.
1929
+ */
1930
+ this.id = nextId();
2048
1931
  /**
2049
1932
  * The type of aspect to target.
2050
1933
  */
@@ -2064,26 +1947,78 @@ class HTMLBindingDirective {
2064
1947
  createBehavior(targets) {
2065
1948
  if (this.factory == null) {
2066
1949
  if (this.targetAspect === "innerHTML") {
2067
- this.binding = createInnerHTMLBinding(this.binding);
1950
+ this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
1951
+ }
1952
+ switch (this.aspectType) {
1953
+ case 1:
1954
+ this.factory = new BindingBehavior(this, DOM.setAttribute);
1955
+ break;
1956
+ case 2:
1957
+ this.factory = new BindingBehavior(this, DOM.setBooleanAttribute);
1958
+ break;
1959
+ case 3:
1960
+ this.factory = new BindingBehavior(this, (t, a, v) => (t[a] = v));
1961
+ break;
1962
+ case 4:
1963
+ this.factory = new ContentBehavior(this, updateContentTarget);
1964
+ break;
1965
+ case 5:
1966
+ this.factory = new BindingBehavior(this, updateTokenListTarget);
1967
+ break;
1968
+ case 6:
1969
+ this.factory = new EventBehavior(this);
1970
+ break;
1971
+ default:
1972
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2068
1973
  }
2069
- this.factory = this.mode[this.aspectType](this);
2070
1974
  }
2071
1975
  return this.factory.createBehavior(targets);
2072
1976
  }
2073
1977
  }
2074
1978
  HTMLDirective.define(HTMLBindingDirective, { aspected: true });
2075
1979
  /**
2076
- * Creates a binding directive with the specified configuration.
2077
- * @param binding - The binding expression.
2078
- * @param config - The binding configuration.
2079
- * @returns A binding directive.
1980
+ * Creates an standard binding.
1981
+ * @param binding - The binding to refresh when changed.
1982
+ * @param isVolatile - Indicates whether the binding is volatile or not.
1983
+ * @returns A binding configuration.
2080
1984
  * @public
2081
1985
  */
2082
- function bind(binding, config = onChange) {
2083
- if (!("mode" in config)) {
2084
- config = onChange(config);
2085
- }
2086
- return new HTMLBindingDirective(binding, config.mode, config.options);
1986
+ function bind(binding, isVolatile = Observable.isVolatileBinding(binding)) {
1987
+ return new OnChangeBinding(binding, isVolatile);
1988
+ }
1989
+ /**
1990
+ * Creates a one time binding
1991
+ * @param binding - The binding to refresh when signaled.
1992
+ * @returns A binding configuration.
1993
+ * @public
1994
+ */
1995
+ function oneTime(binding) {
1996
+ return new OneTimeBinding(binding);
1997
+ }
1998
+ /**
1999
+ * Creates an event listener binding.
2000
+ * @param binding - The binding to invoke when the event is raised.
2001
+ * @param options - Event listener options.
2002
+ * @returns A binding configuration.
2003
+ * @public
2004
+ */
2005
+ function listener(binding, options) {
2006
+ const config = new OnChangeBinding(binding, false);
2007
+ config.options = options;
2008
+ return config;
2009
+ }
2010
+ /**
2011
+ * Normalizes the input value into a binding.
2012
+ * @param value - The value to create the default binding for.
2013
+ * @returns A binding configuration for the provided value.
2014
+ * @public
2015
+ */
2016
+ function normalizeBinding(value) {
2017
+ return isFunction(value)
2018
+ ? bind(value)
2019
+ : value instanceof Binding
2020
+ ? value
2021
+ : oneTime(() => value);
2087
2022
  }
2088
2023
 
2089
2024
  function removeNodeSequence(firstNode, lastNode) {
@@ -2250,6 +2185,22 @@ const next = {
2250
2185
  index: 0,
2251
2186
  node: null,
2252
2187
  };
2188
+ function tryWarn(name) {
2189
+ if (!name.startsWith("fast-")) {
2190
+ FAST.warn(1204 /* Message.hostBindingWithoutHost */, { name });
2191
+ }
2192
+ }
2193
+ const warningHost = new Proxy(document.createElement("div"), {
2194
+ get(target, property) {
2195
+ tryWarn(property);
2196
+ const value = Reflect.get(target, property);
2197
+ return isFunction(value) ? value.bind(target) : value;
2198
+ },
2199
+ set(target, property, value) {
2200
+ tryWarn(property);
2201
+ return Reflect.set(target, property, value);
2202
+ },
2203
+ });
2253
2204
  class CompilationContext {
2254
2205
  constructor(fragment, directives) {
2255
2206
  this.fragment = fragment;
@@ -2300,7 +2251,7 @@ class CompilationContext {
2300
2251
  const fragment = this.fragment.cloneNode(true);
2301
2252
  const targets = Object.create(this.proto);
2302
2253
  targets.r = fragment;
2303
- targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : fragment;
2254
+ targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
2304
2255
  for (const id of this.nodeIds) {
2305
2256
  targets[id]; // trigger locator
2306
2257
  }
@@ -2317,7 +2268,7 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2317
2268
  let result = null;
2318
2269
  if (parseResult === null) {
2319
2270
  if (includeBasicValues) {
2320
- result = bind(() => attrValue, oneTime);
2271
+ result = new HTMLBindingDirective(oneTime(() => attrValue));
2321
2272
  Aspect.assign(result, attr.name);
2322
2273
  }
2323
2274
  }
@@ -2354,6 +2305,7 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
2354
2305
  }
2355
2306
  else {
2356
2307
  currentNode.textContent = " ";
2308
+ Aspect.assign(currentPart);
2357
2309
  context.addFactory(currentPart, parentId, nodeId, nodeIndex);
2358
2310
  }
2359
2311
  lastNode = currentNode;
@@ -2486,22 +2438,28 @@ const Compiler = {
2486
2438
  return parts[0];
2487
2439
  }
2488
2440
  let sourceAspect;
2441
+ let binding;
2442
+ let isVolatile = false;
2489
2443
  const partCount = parts.length;
2490
2444
  const finalParts = parts.map((x) => {
2491
2445
  if (isString(x)) {
2492
2446
  return () => x;
2493
2447
  }
2494
2448
  sourceAspect = x.sourceAspect || sourceAspect;
2495
- return x.binding;
2449
+ binding = x.dataBinding || binding;
2450
+ isVolatile = isVolatile || x.dataBinding.isVolatile;
2451
+ return x.dataBinding.evaluate;
2496
2452
  });
2497
- const binding = (scope, context) => {
2453
+ const expression = (scope, context) => {
2498
2454
  let output = "";
2499
2455
  for (let i = 0; i < partCount; ++i) {
2500
2456
  output += finalParts[i](scope, context);
2501
2457
  }
2502
2458
  return output;
2503
2459
  };
2504
- const directive = bind(binding);
2460
+ binding.evaluate = expression;
2461
+ binding.isVolatile = isVolatile;
2462
+ const directive = new HTMLBindingDirective(binding);
2505
2463
  Aspect.assign(directive, sourceAspect);
2506
2464
  return directive;
2507
2465
  },
@@ -2581,12 +2539,12 @@ function html(strings, ...values) {
2581
2539
  let definition;
2582
2540
  html += currentString;
2583
2541
  if (isFunction(currentValue)) {
2584
- html += createAspectedHTML(bind(currentValue), currentString, add);
2542
+ html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
2585
2543
  }
2586
2544
  else if (isString(currentValue)) {
2587
2545
  const match = lastAttributeNameRegex.exec(currentString);
2588
2546
  if (match !== null) {
2589
- const directive = bind(() => currentValue, oneTime);
2547
+ const directive = new HTMLBindingDirective(oneTime(() => currentValue));
2590
2548
  Aspect.assign(directive, match[2]);
2591
2549
  html += directive.createHTML(add);
2592
2550
  }
@@ -2594,8 +2552,11 @@ function html(strings, ...values) {
2594
2552
  html += currentValue;
2595
2553
  }
2596
2554
  }
2555
+ else if (currentValue instanceof Binding) {
2556
+ html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
2557
+ }
2597
2558
  else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
2598
- html += createAspectedHTML(bind(() => currentValue, oneTime), currentString, add);
2559
+ html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
2599
2560
  }
2600
2561
  else {
2601
2562
  if (definition.aspected) {
@@ -2608,26 +2569,6 @@ function html(strings, ...values) {
2608
2569
  }
2609
2570
  return new ViewTemplate(html + strings[strings.length - 1], factories);
2610
2571
  }
2611
- /**
2612
- * Transforms a template literal string into a ChildViewTemplate.
2613
- * @param strings - The string fragments that are interpolated with the values.
2614
- * @param values - The values that are interpolated with the string fragments.
2615
- * @remarks
2616
- * The html helper supports interpolation of strings, numbers, binding expressions,
2617
- * other template instances, and Directive instances.
2618
- * @public
2619
- */
2620
- const child = html;
2621
- /**
2622
- * Transforms a template literal string into an ItemViewTemplate.
2623
- * @param strings - The string fragments that are interpolated with the values.
2624
- * @param values - The values that are interpolated with the string fragments.
2625
- * @remarks
2626
- * The html helper supports interpolation of strings, numbers, binding expressions,
2627
- * other template instances, and Directive instances.
2628
- * @public
2629
- */
2630
- const item = html;
2631
2572
 
2632
2573
  /**
2633
2574
  * The runtime behavior for template references.
@@ -2660,16 +2601,17 @@ const ref = (propertyName) => new RefDirective(propertyName);
2660
2601
 
2661
2602
  /**
2662
2603
  * A directive that enables basic conditional rendering in a template.
2663
- * @param binding - The condition to test for rendering.
2604
+ * @param condition - The condition to test for rendering.
2664
2605
  * @param templateOrTemplateBinding - The template or a binding that gets
2665
2606
  * the template to render when the condition is true.
2666
2607
  * @public
2667
2608
  */
2668
- function when(binding, templateOrTemplateBinding) {
2669
- const getTemplate = isFunction(templateOrTemplateBinding)
2609
+ function when(condition, templateOrTemplateBinding) {
2610
+ const dataBinding = isFunction(condition) ? condition : () => condition;
2611
+ const templateBinding = isFunction(templateOrTemplateBinding)
2670
2612
  ? templateOrTemplateBinding
2671
2613
  : () => templateOrTemplateBinding;
2672
- return (source, context) => binding(source, context) ? getTemplate(source, context) : null;
2614
+ return (source, context) => dataBinding(source, context) ? templateBinding(source, context) : null;
2673
2615
  }
2674
2616
 
2675
2617
  const defaultRepeatOptions = Object.freeze({
@@ -2690,17 +2632,15 @@ class RepeatBehavior {
2690
2632
  /**
2691
2633
  * Creates an instance of RepeatBehavior.
2692
2634
  * @param location - The location in the DOM to render the repeat.
2693
- * @param itemsBinding - The array to render.
2635
+ * @param dataBinding - The array to render.
2694
2636
  * @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
2695
2637
  * @param templateBinding - The template to render for each item.
2696
2638
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
2697
2639
  * @param options - Options used to turn on special repeat features.
2698
2640
  */
2699
- constructor(location, itemsBinding, isItemsBindingVolatile, templateBinding, isTemplateBindingVolatile, options) {
2641
+ constructor(directive, location) {
2642
+ this.directive = directive;
2700
2643
  this.location = location;
2701
- this.itemsBinding = itemsBinding;
2702
- this.templateBinding = templateBinding;
2703
- this.options = options;
2704
2644
  this.source = null;
2705
2645
  this.views = [];
2706
2646
  this.items = null;
@@ -2708,9 +2648,9 @@ class RepeatBehavior {
2708
2648
  this.context = void 0;
2709
2649
  this.childContext = void 0;
2710
2650
  this.bindView = bindWithoutPositioning;
2711
- this.itemsBindingObserver = Observable.binding(itemsBinding, this, isItemsBindingVolatile);
2712
- this.templateBindingObserver = Observable.binding(templateBinding, this, isTemplateBindingVolatile);
2713
- if (options.positioning) {
2651
+ this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
2652
+ this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
2653
+ if (directive.options.positioning) {
2714
2654
  this.bindView = bindWithPositioning;
2715
2655
  }
2716
2656
  }
@@ -2748,12 +2688,12 @@ class RepeatBehavior {
2748
2688
  * @param args - The details about what was changed.
2749
2689
  */
2750
2690
  handleChange(source, args) {
2751
- if (source === this.itemsBinding) {
2691
+ if (args === this.itemsBindingObserver) {
2752
2692
  this.items = this.itemsBindingObserver.observe(this.source, this.context);
2753
2693
  this.observeItems();
2754
2694
  this.refreshAllViews();
2755
2695
  }
2756
- else if (source === this.templateBinding) {
2696
+ else if (args === this.templateBindingObserver) {
2757
2697
  this.template = this.templateBindingObserver.observe(this.source, this.context);
2758
2698
  this.refreshAllViews(true);
2759
2699
  }
@@ -2782,36 +2722,51 @@ class RepeatBehavior {
2782
2722
  updateViews(splices) {
2783
2723
  const views = this.views;
2784
2724
  const childContext = this.childContext;
2785
- const totalRemoved = [];
2786
2725
  const bindView = this.bindView;
2787
- let removeDelta = 0;
2788
- for (let i = 0, ii = splices.length; i < ii; ++i) {
2789
- const splice = splices[i];
2790
- const removed = splice.removed;
2791
- totalRemoved.push(...views.splice(splice.index + removeDelta, removed.length));
2792
- removeDelta -= splice.addedCount;
2793
- }
2794
2726
  const items = this.items;
2795
2727
  const template = this.template;
2728
+ const recycle = this.directive.options.recycle;
2729
+ const leftoverViews = [];
2730
+ let leftoverIndex = 0;
2731
+ let availableViews = 0;
2796
2732
  for (let i = 0, ii = splices.length; i < ii; ++i) {
2797
2733
  const splice = splices[i];
2734
+ const removed = splice.removed;
2735
+ let removeIndex = 0;
2798
2736
  let addIndex = splice.index;
2799
2737
  const end = addIndex + splice.addedCount;
2738
+ const removedViews = views.splice(splice.index, removed.length);
2739
+ availableViews = leftoverViews.length + removedViews.length;
2800
2740
  for (; addIndex < end; ++addIndex) {
2801
2741
  const neighbor = views[addIndex];
2802
2742
  const location = neighbor ? neighbor.firstChild : this.location;
2803
- const view = this.options.recycle && totalRemoved.length > 0
2804
- ? totalRemoved.shift()
2805
- : template.create();
2743
+ let view;
2744
+ if (recycle && availableViews > 0) {
2745
+ if (removeIndex <= availableViews && removedViews.length > 0) {
2746
+ view = removedViews[removeIndex];
2747
+ removeIndex++;
2748
+ }
2749
+ else {
2750
+ view = leftoverViews[leftoverIndex];
2751
+ leftoverIndex++;
2752
+ }
2753
+ availableViews--;
2754
+ }
2755
+ else {
2756
+ view = template.create();
2757
+ }
2806
2758
  views.splice(addIndex, 0, view);
2807
2759
  bindView(view, items, addIndex, childContext);
2808
2760
  view.insertBefore(location);
2809
2761
  }
2762
+ if (removedViews[removeIndex]) {
2763
+ leftoverViews.push(...removedViews.slice(removeIndex));
2764
+ }
2810
2765
  }
2811
- for (let i = 0, ii = totalRemoved.length; i < ii; ++i) {
2812
- totalRemoved[i].dispose();
2766
+ for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
2767
+ leftoverViews[i].dispose();
2813
2768
  }
2814
- if (this.options.positioning) {
2769
+ if (this.directive.options.positioning) {
2815
2770
  for (let i = 0, ii = views.length; i < ii; ++i) {
2816
2771
  views[i].context.updatePosition(i, ii);
2817
2772
  }
@@ -2826,7 +2781,7 @@ class RepeatBehavior {
2826
2781
  let itemsLength = items.length;
2827
2782
  let views = this.views;
2828
2783
  let viewsLength = views.length;
2829
- if (itemsLength === 0 || templateChanged) {
2784
+ if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
2830
2785
  // all views need to be removed
2831
2786
  HTMLView.disposeContiguousBatch(views);
2832
2787
  viewsLength = 0;
@@ -2876,17 +2831,19 @@ class RepeatBehavior {
2876
2831
  class RepeatDirective {
2877
2832
  /**
2878
2833
  * Creates an instance of RepeatDirective.
2879
- * @param itemsBinding - The binding that provides the array to render.
2834
+ * @param dataBinding - The binding that provides the array to render.
2880
2835
  * @param templateBinding - The template binding used to obtain a template to render for each item in the array.
2881
2836
  * @param options - Options used to turn on special repeat features.
2882
2837
  */
2883
- constructor(itemsBinding, templateBinding, options) {
2884
- this.itemsBinding = itemsBinding;
2838
+ constructor(dataBinding, templateBinding, options) {
2839
+ this.dataBinding = dataBinding;
2885
2840
  this.templateBinding = templateBinding;
2886
2841
  this.options = options;
2842
+ /**
2843
+ * The unique id of the factory.
2844
+ */
2845
+ this.id = nextId();
2887
2846
  ArrayObserver.enable();
2888
- this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
2889
- this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
2890
2847
  }
2891
2848
  /**
2892
2849
  * Creates a placeholder string based on the directive's index within the template.
@@ -2900,15 +2857,22 @@ class RepeatDirective {
2900
2857
  * @param target - The node instance to create the behavior for.
2901
2858
  */
2902
2859
  createBehavior(targets) {
2903
- return new RepeatBehavior(targets[this.nodeId], this.itemsBinding, this.isItemsBindingVolatile, this.templateBinding, this.isTemplateBindingVolatile, this.options);
2860
+ return new RepeatBehavior(this, targets[this.nodeId]);
2904
2861
  }
2905
2862
  }
2906
2863
  HTMLDirective.define(RepeatDirective);
2907
- function repeat(itemsBinding, templateOrTemplateBinding, options = defaultRepeatOptions) {
2908
- const templateBinding = isFunction(templateOrTemplateBinding)
2909
- ? templateOrTemplateBinding
2910
- : () => templateOrTemplateBinding;
2911
- return new RepeatDirective(itemsBinding, templateBinding, options);
2864
+ /**
2865
+ * A directive that enables list rendering.
2866
+ * @param items - The array to render.
2867
+ * @param template - The template or a template binding used obtain a template
2868
+ * to render for each item in the array.
2869
+ * @param options - Options used to turn on special repeat features.
2870
+ * @public
2871
+ */
2872
+ function repeat(items, template, options = defaultRepeatOptions) {
2873
+ const dataBinding = normalizeBinding(items);
2874
+ const templateBinding = normalizeBinding(template);
2875
+ return new RepeatDirective(dataBinding, templateBinding, options);
2912
2876
  }
2913
2877
 
2914
2878
  const selectElements = (value) => value.nodeType === 1;
@@ -3293,19 +3257,15 @@ const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */
3293
3257
  * @public
3294
3258
  */
3295
3259
  class FASTElementDefinition {
3296
- /**
3297
- * Creates an instance of FASTElementDefinition.
3298
- * @param type - The type this definition is being created for.
3299
- * @param nameOrConfig - The name of the element to define or a config object
3300
- * that describes the element to define.
3301
- */
3302
3260
  constructor(type, nameOrConfig = type.definition) {
3261
+ this.platformDefined = false;
3303
3262
  if (isString(nameOrConfig)) {
3304
3263
  nameOrConfig = { name: nameOrConfig };
3305
3264
  }
3306
3265
  this.type = type;
3307
3266
  this.name = nameOrConfig.name;
3308
3267
  this.template = nameOrConfig.template;
3268
+ const proto = type.prototype;
3309
3269
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3310
3270
  const observedAttributes = new Array(attributes.length);
3311
3271
  const propertyLookup = {};
@@ -3315,9 +3275,13 @@ class FASTElementDefinition {
3315
3275
  observedAttributes[i] = current.attribute;
3316
3276
  propertyLookup[current.name] = current;
3317
3277
  attributeLookup[current.attribute] = current;
3278
+ Observable.defineProperty(proto, current);
3318
3279
  }
3280
+ Reflect.defineProperty(type, "observedAttributes", {
3281
+ value: observedAttributes,
3282
+ enumerable: true,
3283
+ });
3319
3284
  this.attributes = attributes;
3320
- this.observedAttributes = observedAttributes;
3321
3285
  this.propertyLookup = propertyLookup;
3322
3286
  this.attributeLookup = attributeLookup;
3323
3287
  this.shadowOptions =
@@ -3330,43 +3294,43 @@ class FASTElementDefinition {
3330
3294
  nameOrConfig.elementOptions === void 0
3331
3295
  ? defaultElementOptions
3332
3296
  : Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
3333
- this.styles =
3334
- nameOrConfig.styles === void 0
3335
- ? void 0
3336
- : Array.isArray(nameOrConfig.styles)
3337
- ? new ElementStyles(nameOrConfig.styles)
3338
- : nameOrConfig.styles instanceof ElementStyles
3339
- ? nameOrConfig.styles
3340
- : new ElementStyles([nameOrConfig.styles]);
3297
+ this.styles = ElementStyles.normalize(nameOrConfig.styles);
3298
+ fastElementRegistry.register(this);
3341
3299
  }
3342
3300
  /**
3343
3301
  * Indicates if this element has been defined in at least one registry.
3344
3302
  */
3345
3303
  get isDefined() {
3346
- return !!fastElementRegistry.getByType(this.type);
3304
+ return this.platformDefined;
3347
3305
  }
3348
3306
  /**
3349
3307
  * Defines a custom element based on this definition.
3350
3308
  * @param registry - The element registry to define the element in.
3309
+ * @remarks
3310
+ * This operation is idempotent per registry.
3351
3311
  */
3352
3312
  define(registry = customElements) {
3353
3313
  const type = this.type;
3354
- if (fastElementRegistry.register(this)) {
3355
- const attributes = this.attributes;
3356
- const proto = type.prototype;
3357
- for (let i = 0, ii = attributes.length; i < ii; ++i) {
3358
- Observable.defineProperty(proto, attributes[i]);
3359
- }
3360
- Reflect.defineProperty(type, "observedAttributes", {
3361
- value: this.observedAttributes,
3362
- enumerable: true,
3363
- });
3364
- }
3365
3314
  if (!registry.get(this.name)) {
3315
+ this.platformDefined = true;
3366
3316
  registry.define(this.name, type, this.elementOptions);
3367
3317
  }
3368
3318
  return this;
3369
3319
  }
3320
+ /**
3321
+ * Creates an instance of FASTElementDefinition.
3322
+ * @param type - The type this definition is being created for.
3323
+ * @param nameOrDef - The name of the element to define or a config object
3324
+ * that describes the element to define.
3325
+ */
3326
+ static compose(type, nameOrDef) {
3327
+ const found = fastElementRegistry.getByType(type);
3328
+ if (found) {
3329
+ return new FASTElementDefinition(class extends type {
3330
+ }, nameOrDef);
3331
+ }
3332
+ return new FASTElementDefinition(type, nameOrDef);
3333
+ }
3370
3334
  }
3371
3335
  /**
3372
3336
  * Gets the element definition associated with the specified type.
@@ -3784,6 +3748,18 @@ function createFASTElement(BaseType) {
3784
3748
  }
3785
3749
  };
3786
3750
  }
3751
+ function compose(type, nameOrDef) {
3752
+ if (isFunction(type)) {
3753
+ return FASTElementDefinition.compose(type, nameOrDef);
3754
+ }
3755
+ return FASTElementDefinition.compose(this, type);
3756
+ }
3757
+ function define(type, nameOrDef) {
3758
+ if (isFunction(type)) {
3759
+ return FASTElementDefinition.compose(type, nameOrDef).define().type;
3760
+ }
3761
+ return FASTElementDefinition.compose(this, type).define().type;
3762
+ }
3787
3763
  /**
3788
3764
  * A minimal base class for FASTElements that also provides
3789
3765
  * static helpers for working with FASTElements.
@@ -3804,9 +3780,12 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3804
3780
  * @param nameOrDef - The name of the element to define or a definition object
3805
3781
  * that describes the element to define.
3806
3782
  */
3807
- define(type, nameOrDef) {
3808
- return new FASTElementDefinition(type, nameOrDef).define().type;
3809
- },
3783
+ define,
3784
+ /**
3785
+ * Defines metadata for a FASTElement which can be used to later define the element.
3786
+ * @public
3787
+ */
3788
+ compose,
3810
3789
  });
3811
3790
  /**
3812
3791
  * Decorator: Defines a platform custom element based on `FASTElement`.
@@ -3817,8 +3796,8 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3817
3796
  function customElement(nameOrDef) {
3818
3797
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
3819
3798
  return function (type) {
3820
- new FASTElementDefinition(type, nameOrDef).define();
3799
+ define(type, nameOrDef);
3821
3800
  };
3822
3801
  }
3823
3802
 
3824
- export { AdoptedStyleSheetsStrategy, ArrayObserver, Aspect, AttributeDefinition, BindingConfig, BindingMode, CSSDirective, ChangeBinding, ChildrenDirective, Compiler, Controller, DOM, ElementStyles, EventBinding, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, OneTimeBinding, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SignalBinding, SlottedDirective, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, TwoWayBinding, UpdateBinding, Updates, ViewTemplate, attr, bind, booleanConverter, child, children, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, item, length, nullableNumberConverter, observable, onChange, oneTime, ref, repeat, signal, slotted, twoWay, volatile, when };
3803
+ 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 };