@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
@@ -112,7 +112,7 @@ if (FAST.error === void 0) {
112
112
  Object.assign(FAST, {
113
113
  warn() { },
114
114
  error(code) {
115
- return new Error(`Code ${code}`);
115
+ return new Error(`Error ${code}`);
116
116
  },
117
117
  addMessages() { },
118
118
  });
@@ -449,7 +449,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
449
449
  }
450
450
  }
451
451
  }
452
- class BindingObserverImplementation extends SubscriberSet {
452
+ class ExpressionNotifierImplementation extends SubscriberSet {
453
453
  constructor(binding, initialSubscriber, isVolatileBinding = false) {
454
454
  super(binding, initialSubscriber);
455
455
  this.binding = binding;
@@ -604,14 +604,14 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
604
604
  */
605
605
  getAccessors,
606
606
  /**
607
- * Creates a {@link BindingObserver} that can watch the
608
- * provided {@link Binding} for changes.
607
+ * Creates a {@link ExpressionNotifier} that can watch the
608
+ * provided {@link Expression} for changes.
609
609
  * @param binding - The binding to observe.
610
610
  * @param initialSubscriber - An initial subscriber to changes in the binding value.
611
611
  * @param isVolatileBinding - Indicates whether the binding's dependency list must be re-evaluated on every value evaluation.
612
612
  */
613
613
  binding(binding, initialSubscriber, isVolatileBinding = this.isVolatileBinding(binding)) {
614
- return new BindingObserverImplementation(binding, initialSubscriber, isVolatileBinding);
614
+ return new ExpressionNotifierImplementation(binding, initialSubscriber, isVolatileBinding);
615
615
  },
616
616
  /**
617
617
  * Determines whether a binding expression is volatile and needs to have its dependency list re-evaluated
@@ -658,75 +658,127 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
658
658
  },
659
659
  };
660
660
  });
661
- class DefaultExecutionContext {
661
+ /**
662
+ * Provides additional contextual information available to behaviors and expressions.
663
+ * @public
664
+ */
665
+ class ExecutionContext {
662
666
  constructor(parentSource = null, parentContext = null) {
667
+ /**
668
+ * The index of the current item within a repeat context.
669
+ */
663
670
  this.index = 0;
671
+ /**
672
+ * The length of the current collection within a repeat context.
673
+ */
664
674
  this.length = 0;
665
675
  this.parent = parentSource;
666
676
  this.parentContext = parentContext;
667
677
  }
678
+ /**
679
+ * The current event within an event handler.
680
+ */
668
681
  get event() {
669
682
  return contextEvent.get();
670
683
  }
684
+ /**
685
+ * Indicates whether the current item within a repeat context
686
+ * has an even index.
687
+ */
671
688
  get isEven() {
672
689
  return this.index % 2 === 0;
673
690
  }
691
+ /**
692
+ * Indicates whether the current item within a repeat context
693
+ * has an odd index.
694
+ */
674
695
  get isOdd() {
675
696
  return this.index % 2 !== 0;
676
697
  }
698
+ /**
699
+ * Indicates whether the current item within a repeat context
700
+ * is the first item in the collection.
701
+ */
677
702
  get isFirst() {
678
703
  return this.index === 0;
679
704
  }
705
+ /**
706
+ * Indicates whether the current item within a repeat context
707
+ * is somewhere in the middle of the collection.
708
+ */
680
709
  get isInMiddle() {
681
710
  return !this.isFirst && !this.isLast;
682
711
  }
712
+ /**
713
+ * Indicates whether the current item within a repeat context
714
+ * is the last item in the collection.
715
+ */
683
716
  get isLast() {
684
717
  return this.index === this.length - 1;
685
718
  }
719
+ /**
720
+ * Returns the typed event detail of a custom event.
721
+ */
686
722
  eventDetail() {
687
723
  return this.event.detail;
688
724
  }
725
+ /**
726
+ * Returns the typed event target of the event.
727
+ */
689
728
  eventTarget() {
690
729
  return this.event.target;
691
730
  }
731
+ /**
732
+ * Updates the position/size on a context associated with a list item.
733
+ * @param index - The new index of the item.
734
+ * @param length - The new length of the list.
735
+ */
692
736
  updatePosition(index, length) {
693
737
  this.index = index;
694
738
  this.length = length;
695
739
  }
740
+ /**
741
+ * Creates a new execution context descendent from the current context.
742
+ * @param source - The source for the context if different than the parent.
743
+ * @returns A child execution context.
744
+ */
696
745
  createChildContext(parentSource) {
697
- return new DefaultExecutionContext(parentSource, this);
746
+ return new ExecutionContext(parentSource, this);
698
747
  }
748
+ /**
749
+ * Creates a new execution context descent suitable for use in list rendering.
750
+ * @param item - The list item to serve as the source.
751
+ * @param index - The index of the item in the list.
752
+ * @param length - The length of the list.
753
+ */
699
754
  createItemContext(index, length) {
700
755
  const childContext = Object.create(this);
701
756
  childContext.index = index;
702
757
  childContext.length = length;
703
758
  return childContext;
704
759
  }
705
- }
706
- Observable.defineProperty(DefaultExecutionContext.prototype, "index");
707
- Observable.defineProperty(DefaultExecutionContext.prototype, "length");
708
- /**
709
- * The common execution context APIs.
710
- * @public
711
- */
712
- const ExecutionContext = Object.freeze({
713
- default: new DefaultExecutionContext(),
714
760
  /**
715
761
  * Sets the event for the current execution context.
716
762
  * @param event - The event to set.
717
763
  * @internal
718
764
  */
719
- setEvent(event) {
765
+ static setEvent(event) {
720
766
  contextEvent.set(event);
721
- },
767
+ }
722
768
  /**
723
769
  * Creates a new root execution context.
724
770
  * @returns A new execution context.
725
771
  */
726
- create() {
727
- return new DefaultExecutionContext();
728
- },
729
- });
772
+ static create() {
773
+ return new ExecutionContext();
774
+ }
775
+ }
776
+ /**
777
+ * The default execution context.
778
+ */
779
+ ExecutionContext.default = new ExecutionContext();
780
+ Observable.defineProperty(ExecutionContext.prototype, "index");
781
+ Observable.defineProperty(ExecutionContext.prototype, "length");
730
782
 
731
783
  /**
732
784
  * A splice map is a representation of how a previous array of items
@@ -981,7 +1033,7 @@ const ArrayObserver = Object.freeze({
981
1033
  * @returns The length of the array.
982
1034
  * @public
983
1035
  */
984
- function length(array) {
1036
+ function lengthOf(array) {
985
1037
  if (!array) {
986
1038
  return 0;
987
1039
  }
@@ -1065,6 +1117,20 @@ class ElementStyles {
1065
1117
  static setDefaultStrategy(Strategy) {
1066
1118
  DefaultStyleStrategy = Strategy;
1067
1119
  }
1120
+ /**
1121
+ * Normalizes a set of composable style options.
1122
+ * @param styles - The style options to normalize.
1123
+ * @returns A singular ElementStyles instance or undefined.
1124
+ */
1125
+ static normalize(styles) {
1126
+ return styles === void 0
1127
+ ? void 0
1128
+ : Array.isArray(styles)
1129
+ ? new ElementStyles(styles)
1130
+ : styles instanceof ElementStyles
1131
+ ? styles
1132
+ : new ElementStyles([styles]);
1133
+ }
1068
1134
  }
1069
1135
  /**
1070
1136
  * Indicates whether the DOM supports the adoptedStyleSheets feature.
@@ -1391,6 +1457,13 @@ function htmlDirective(options) {
1391
1457
  HTMLDirective.define(type, options);
1392
1458
  };
1393
1459
  }
1460
+ /**
1461
+ * Captures a binding expression along with related information and capabilities.
1462
+ *
1463
+ * @public
1464
+ */
1465
+ class Binding {
1466
+ }
1394
1467
  /**
1395
1468
  * The type of HTML aspect to target.
1396
1469
  * @public
@@ -1428,12 +1501,15 @@ const Aspect = Object.freeze({
1428
1501
  *
1429
1502
  * @param directive - The directive to assign the aspect to.
1430
1503
  * @param value - The value to base the aspect determination on.
1504
+ * @remarks
1505
+ * If a falsy value is provided, then the content aspect will be assigned.
1431
1506
  */
1432
1507
  assign(directive, value) {
1433
- directive.sourceAspect = value;
1434
1508
  if (!value) {
1509
+ directive.aspectType = Aspect.content;
1435
1510
  return;
1436
1511
  }
1512
+ directive.sourceAspect = value;
1437
1513
  switch (value[0]) {
1438
1514
  case ":":
1439
1515
  directive.targetAspect = value.substring(1);
@@ -1481,6 +1557,10 @@ class StatelessAttachedAttributeDirective {
1481
1557
  */
1482
1558
  constructor(options) {
1483
1559
  this.options = options;
1560
+ /**
1561
+ * The unique id of the factory.
1562
+ */
1563
+ this.id = nextId();
1484
1564
  }
1485
1565
  /**
1486
1566
  * Creates a behavior.
@@ -1509,99 +1589,28 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
1509
1589
  throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
1510
1590
  }
1511
1591
  : (binding) => binding;
1512
- /**
1513
- * Describes how aspects of an HTML element will be affected by bindings.
1514
- * @public
1515
- */
1516
- const BindingMode = Object.freeze({
1517
- /**
1518
- * Creates a binding mode based on the supplied behavior types.
1519
- * @param UpdateType - The base behavior type used to update aspects.
1520
- * @param EventType - The base behavior type used to respond to events.
1521
- * @returns A new binding mode.
1522
- */
1523
- define(UpdateType, EventType = EventBinding) {
1524
- return Object.freeze({
1525
- [1]: d => new UpdateType(d, DOM.setAttribute),
1526
- [2]: d => new UpdateType(d, DOM.setBooleanAttribute),
1527
- [3]: d => new UpdateType(d, (t, a, v) => (t[a] = v)),
1528
- [4]: d => new (createContentBinding(UpdateType))(d, updateContentTarget),
1529
- [5]: d => new UpdateType(d, updateTokenListTarget),
1530
- [6]: d => new EventType(d),
1531
- });
1532
- },
1533
- });
1534
- /**
1535
- * Describes the configuration for a binding expression.
1536
- * @public
1537
- */
1538
- const BindingConfig = Object.freeze({
1539
- /**
1540
- * Creates a binding configuration based on the provided mode and options.
1541
- * @param mode - The mode to use for the configuration.
1542
- * @param defaultOptions - The default options to use for the configuration.
1543
- * @returns A new binding configuration.
1544
- */
1545
- define(mode, defaultOptions) {
1546
- const config = (options) => {
1547
- return {
1548
- mode: config.mode,
1549
- options: Object.assign({}, defaultOptions, options),
1550
- };
1551
- };
1552
- config.options = defaultOptions;
1553
- config.mode = mode;
1554
- return config;
1555
- },
1556
- });
1557
- /**
1558
- * A base binding behavior for DOM updates.
1559
- * @public
1560
- */
1561
- class UpdateBinding {
1562
- /**
1563
- * Creates an instance of UpdateBinding.
1564
- * @param directive - The directive that has the configuration for this behavior.
1565
- * @param updateTarget - The function used to update the target with the latest value.
1566
- */
1567
- constructor(directive, updateTarget) {
1568
- this.directive = directive;
1569
- this.updateTarget = updateTarget;
1592
+ class OnChangeBinding extends Binding {
1593
+ constructor(evaluate, isVolatile) {
1594
+ super();
1595
+ this.evaluate = evaluate;
1596
+ this.isVolatile = isVolatile;
1570
1597
  }
1571
- /**
1572
- * Bind this behavior to the source.
1573
- * @param source - The source to bind to.
1574
- * @param context - The execution context that the binding is operating within.
1575
- * @param targets - The targets that behaviors in a view can attach to.
1576
- */
1577
- bind(source, context, targets) { }
1578
- /**
1579
- * Unbinds this behavior from the source.
1580
- * @param source - The source to unbind from.
1581
- * @param context - The execution context that the binding is operating within.
1582
- * @param targets - The targets that behaviors in a view can attach to.
1583
- */
1584
- unbind(source, context, targets) { }
1585
- /**
1586
- * Creates a behavior.
1587
- * @param targets - The targets available for behaviors to be attached to.
1588
- */
1589
- createBehavior(targets) {
1590
- return this;
1598
+ createObserver(_, subscriber) {
1599
+ return Observable.binding(this.evaluate, subscriber, this.isVolatile);
1591
1600
  }
1592
1601
  }
1593
- function createContentBinding(Type) {
1594
- return class extends Type {
1595
- unbind(source, context, targets) {
1596
- super.unbind(source, context, targets);
1597
- const target = targets[this.directive.nodeId];
1598
- const view = target.$fastView;
1599
- if (view !== void 0 && view.isComposed) {
1600
- view.unbind();
1601
- view.needsBindOnly = true;
1602
- }
1603
- }
1604
- };
1602
+ class OneTimeBinding extends Binding {
1603
+ constructor(evaluate) {
1604
+ super();
1605
+ this.evaluate = evaluate;
1606
+ }
1607
+ createObserver() {
1608
+ return this;
1609
+ }
1610
+ observe(source, context) {
1611
+ return this.evaluate(source, context);
1612
+ }
1613
+ dispose() { }
1605
1614
  }
1606
1615
  function updateContentTarget(target, aspect, value, source, context) {
1607
1616
  // If there's no actual value, then this equates to the
@@ -1609,7 +1618,7 @@ function updateContentTarget(target, aspect, value, source, context) {
1609
1618
  if (value === null || value === undefined) {
1610
1619
  value = "";
1611
1620
  }
1612
- // If the value has a "create" method, then it's a template-like.
1621
+ // If the value has a "create" method, then it's a ContentTemplate.
1613
1622
  if (value.create) {
1614
1623
  target.textContent = "";
1615
1624
  let view = target.$fastView;
@@ -1695,118 +1704,21 @@ function updateTokenListTarget(target, aspect, value) {
1695
1704
  }
1696
1705
  }
1697
1706
  }
1698
- /**
1699
- * A binding behavior for one-time bindings.
1700
- * @public
1701
- */
1702
- class OneTimeBinding extends UpdateBinding {
1703
- /**
1704
- * Bind this behavior to the source.
1705
- * @param source - The source to bind to.
1706
- * @param context - The execution context that the binding is operating within.
1707
- * @param targets - The targets that behaviors in a view can attach to.
1708
- */
1709
- bind(source, context, targets) {
1710
- const directive = this.directive;
1711
- this.updateTarget(targets[directive.nodeId], directive.targetAspect, directive.binding(source, context), source, context);
1712
- }
1713
- }
1714
- const signals = Object.create(null);
1715
- /**
1716
- * A binding behavior for signal bindings.
1717
- * @public
1718
- */
1719
- class SignalBinding extends UpdateBinding {
1720
- constructor() {
1721
- super(...arguments);
1722
- this.handlerProperty = `${this.directive.id}-h`;
1723
- }
1724
- /**
1725
- * Bind this behavior to the source.
1726
- * @param source - The source to bind to.
1727
- * @param context - The execution context that the binding is operating within.
1728
- * @param targets - The targets that behaviors in a view can attach to.
1729
- */
1730
- bind(source, context, targets) {
1731
- const directive = this.directive;
1732
- const target = targets[directive.nodeId];
1733
- const signal = this.getSignal(source, context);
1734
- const handler = (target[this.handlerProperty] = () => {
1735
- this.updateTarget(target, directive.targetAspect, directive.binding(source, context), source, context);
1736
- });
1737
- handler();
1738
- const found = signals[signal];
1739
- if (found) {
1740
- Array.isArray(found)
1741
- ? found.push(handler)
1742
- : (signals[signal] = [found, handler]);
1743
- }
1744
- else {
1745
- signals[signal] = handler;
1746
- }
1747
- }
1748
- /**
1749
- * Unbinds this behavior from the source.
1750
- * @param source - The source to unbind from.
1751
- * @param context - The execution context that the binding is operating within.
1752
- * @param targets - The targets that behaviors in a view can attach to.
1753
- */
1754
- unbind(source, context, targets) {
1755
- const signal = this.getSignal(source, context);
1756
- const found = signals[signal];
1757
- if (found && Array.isArray(found)) {
1758
- const directive = this.directive;
1759
- const target = targets[directive.nodeId];
1760
- const handler = target[this.handlerProperty];
1761
- const index = found.indexOf(handler);
1762
- if (index !== -1) {
1763
- found.splice(index, 1);
1764
- }
1765
- }
1766
- else {
1767
- signals[signal] = void 0;
1768
- }
1769
- }
1770
- getSignal(source, context) {
1771
- const options = this.directive.options;
1772
- return isString(options) ? options : options(source, context);
1773
- }
1774
- /**
1775
- * Sends the specified signal to signaled bindings.
1776
- * @param signal - The signal to send.
1777
- * @public
1778
- */
1779
- static send(signal) {
1780
- const found = signals[signal];
1781
- if (found) {
1782
- Array.isArray(found) ? found.forEach(x => x()) : found();
1783
- }
1784
- }
1785
- }
1786
1707
  /**
1787
1708
  * A binding behavior for bindings that change.
1788
1709
  * @public
1789
1710
  */
1790
- class ChangeBinding extends UpdateBinding {
1711
+ class BindingBehavior {
1791
1712
  /**
1792
1713
  * Creates an instance of ChangeBinding.
1793
1714
  * @param directive - The directive that has the configuration for this behavior.
1794
1715
  * @param updateTarget - The function used to update the target with the latest value.
1795
1716
  */
1796
1717
  constructor(directive, updateTarget) {
1797
- super(directive, updateTarget);
1798
- this.isBindingVolatile = Observable.isVolatileBinding(directive.binding);
1718
+ this.directive = directive;
1719
+ this.updateTarget = updateTarget;
1799
1720
  this.observerProperty = `${directive.id}-o`;
1800
1721
  }
1801
- /**
1802
- * Returns the binding observer used to update the node.
1803
- * @param target - The target node.
1804
- * @returns A BindingObserver.
1805
- */
1806
- getObserver(target) {
1807
- var _a;
1808
- return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = Observable.binding(this.directive.binding, this, this.isBindingVolatile)));
1809
- }
1810
1722
  /**
1811
1723
  * Bind this behavior to the source.
1812
1724
  * @param source - The source to bind to.
@@ -1843,12 +1755,49 @@ class ChangeBinding extends UpdateBinding {
1843
1755
  const context = observer.context;
1844
1756
  this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
1845
1757
  }
1758
+ /**
1759
+ * Returns the binding observer used to update the node.
1760
+ * @param target - The target node.
1761
+ * @returns A BindingObserver.
1762
+ */
1763
+ getObserver(target) {
1764
+ var _a;
1765
+ return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = this.directive.dataBinding.createObserver(this.directive, this)));
1766
+ }
1767
+ /**
1768
+ * Creates a behavior.
1769
+ * @param targets - The targets available for behaviors to be attached to.
1770
+ */
1771
+ createBehavior(targets) {
1772
+ return this;
1773
+ }
1774
+ }
1775
+ /**
1776
+ * A special binding behavior that can bind node content.
1777
+ * @public
1778
+ */
1779
+ class ContentBehavior extends BindingBehavior {
1780
+ /**
1781
+ * Unbinds this behavior from the source.
1782
+ * @param source - The source to unbind from.
1783
+ * @param context - The execution context that the binding is operating within.
1784
+ * @param targets - The targets that behaviors in a view can attach to.
1785
+ */
1786
+ unbind(source, context, targets) {
1787
+ super.unbind(source, context, targets);
1788
+ const target = targets[this.directive.nodeId];
1789
+ const view = target.$fastView;
1790
+ if (view !== void 0 && view.isComposed) {
1791
+ view.unbind();
1792
+ view.needsBindOnly = true;
1793
+ }
1794
+ }
1846
1795
  }
1847
1796
  /**
1848
1797
  * A binding behavior for handling events.
1849
1798
  * @public
1850
1799
  */
1851
- class EventBinding {
1800
+ class EventBehavior {
1852
1801
  /**
1853
1802
  * Creates an instance of EventBinding.
1854
1803
  * @param directive - The directive that has the configuration for this behavior.
@@ -1869,7 +1818,7 @@ class EventBinding {
1869
1818
  const target = targets[directive.nodeId];
1870
1819
  target[this.sourceProperty] = source;
1871
1820
  target[this.contextProperty] = context;
1872
- target.addEventListener(directive.targetAspect, this, directive.options);
1821
+ target.addEventListener(directive.targetAspect, this, directive.dataBinding.options);
1873
1822
  }
1874
1823
  /**
1875
1824
  * Unbinds this behavior from the source.
@@ -1881,7 +1830,7 @@ class EventBinding {
1881
1830
  const directive = this.directive;
1882
1831
  const target = targets[directive.nodeId];
1883
1832
  target[this.sourceProperty] = target[this.contextProperty] = null;
1884
- target.removeEventListener(directive.targetAspect, this, directive.options);
1833
+ target.removeEventListener(directive.targetAspect, this, directive.dataBinding.options);
1885
1834
  }
1886
1835
  /**
1887
1836
  * Creates a behavior.
@@ -1896,110 +1845,13 @@ class EventBinding {
1896
1845
  handleEvent(event) {
1897
1846
  const target = event.currentTarget;
1898
1847
  ExecutionContext.setEvent(event);
1899
- const result = this.directive.binding(target[this.sourceProperty], target[this.contextProperty]);
1848
+ const result = this.directive.dataBinding.evaluate(target[this.sourceProperty], target[this.contextProperty]);
1900
1849
  ExecutionContext.setEvent(null);
1901
1850
  if (result !== true) {
1902
1851
  event.preventDefault();
1903
1852
  }
1904
1853
  }
1905
1854
  }
1906
- let twoWaySettings = {
1907
- determineChangeEvent() {
1908
- return "change";
1909
- },
1910
- };
1911
- /**
1912
- * A binding behavior for bindings that update in two directions.
1913
- * @public
1914
- */
1915
- class TwoWayBinding extends ChangeBinding {
1916
- /**
1917
- * Bind this behavior to the source.
1918
- * @param source - The source to bind to.
1919
- * @param context - The execution context that the binding is operating within.
1920
- * @param targets - The targets that behaviors in a view can attach to.
1921
- */
1922
- bind(source, context, targets) {
1923
- var _a;
1924
- super.bind(source, context, targets);
1925
- const directive = this.directive;
1926
- const target = targets[directive.nodeId];
1927
- if (!this.changeEvent) {
1928
- this.changeEvent =
1929
- (_a = directive.options.changeEvent) !== null && _a !== void 0 ? _a : twoWaySettings.determineChangeEvent(directive, target);
1930
- }
1931
- target.addEventListener(this.changeEvent, this);
1932
- }
1933
- /**
1934
- * Unbinds this behavior from the source.
1935
- * @param source - The source to unbind from.
1936
- * @param context - The execution context that the binding is operating within.
1937
- * @param targets - The targets that behaviors in a view can attach to.
1938
- */
1939
- unbind(source, context, targets) {
1940
- super.unbind(source, context, targets);
1941
- targets[this.directive.nodeId].removeEventListener(this.changeEvent, this);
1942
- }
1943
- /** @internal */
1944
- handleEvent(event) {
1945
- const directive = this.directive;
1946
- const target = event.currentTarget;
1947
- let value;
1948
- switch (directive.aspectType) {
1949
- case 1:
1950
- value = target.getAttribute(directive.targetAspect);
1951
- break;
1952
- case 2:
1953
- value = target.hasAttribute(directive.targetAspect);
1954
- break;
1955
- case 4:
1956
- value = target.innerText;
1957
- break;
1958
- default:
1959
- value = target[directive.targetAspect];
1960
- break;
1961
- }
1962
- const observer = this.getObserver(target);
1963
- const last = observer.last; // using internal API!!!
1964
- last.propertySource[last.propertyName] = directive.options.fromView(value);
1965
- }
1966
- /**
1967
- * Configures two-way binding.
1968
- * @param settings - The settings to use for the two-way binding system.
1969
- */
1970
- static configure(settings) {
1971
- twoWaySettings = settings;
1972
- }
1973
- }
1974
- /**
1975
- * The default onChange binding configuration.
1976
- * @public
1977
- */
1978
- const onChange = BindingConfig.define(BindingMode.define(ChangeBinding), {});
1979
- /**
1980
- * The default twoWay binding configuration.
1981
- * @public
1982
- */
1983
- const twoWay = BindingConfig.define(BindingMode.define(TwoWayBinding), {
1984
- fromView: v => v,
1985
- });
1986
- /**
1987
- * The default onTime binding configuration.
1988
- * @public
1989
- */
1990
- const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
1991
- once: true,
1992
- });
1993
- const signalMode = BindingMode.define(SignalBinding);
1994
- /**
1995
- * Creates a signal binding configuration with the supplied options.
1996
- * @param options - The signal name or a binding to use to retrieve the signal name.
1997
- * @returns A binding configuration.
1998
- * @public
1999
- */
2000
- const signal = (options) => {
2001
- return { mode: signalMode, options };
2002
- };
2003
1855
  /**
2004
1856
  * A directive that applies bindings.
2005
1857
  * @public
@@ -2007,15 +1859,15 @@ const signal = (options) => {
2007
1859
  class HTMLBindingDirective {
2008
1860
  /**
2009
1861
  * Creates an instance of HTMLBindingDirective.
2010
- * @param binding - The binding to apply.
2011
- * @param mode - The binding mode to use when applying the binding.
2012
- * @param options - The options to configure the binding with.
1862
+ * @param dataBinding - The binding configuration to apply.
2013
1863
  */
2014
- constructor(binding, mode, options) {
2015
- this.binding = binding;
2016
- this.mode = mode;
2017
- this.options = options;
1864
+ constructor(dataBinding) {
1865
+ this.dataBinding = dataBinding;
2018
1866
  this.factory = null;
1867
+ /**
1868
+ * The unique id of the factory.
1869
+ */
1870
+ this.id = nextId();
2019
1871
  /**
2020
1872
  * The type of aspect to target.
2021
1873
  */
@@ -2035,26 +1887,78 @@ class HTMLBindingDirective {
2035
1887
  createBehavior(targets) {
2036
1888
  if (this.factory == null) {
2037
1889
  if (this.targetAspect === "innerHTML") {
2038
- this.binding = createInnerHTMLBinding(this.binding);
1890
+ this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
1891
+ }
1892
+ switch (this.aspectType) {
1893
+ case 1:
1894
+ this.factory = new BindingBehavior(this, DOM.setAttribute);
1895
+ break;
1896
+ case 2:
1897
+ this.factory = new BindingBehavior(this, DOM.setBooleanAttribute);
1898
+ break;
1899
+ case 3:
1900
+ this.factory = new BindingBehavior(this, (t, a, v) => (t[a] = v));
1901
+ break;
1902
+ case 4:
1903
+ this.factory = new ContentBehavior(this, updateContentTarget);
1904
+ break;
1905
+ case 5:
1906
+ this.factory = new BindingBehavior(this, updateTokenListTarget);
1907
+ break;
1908
+ case 6:
1909
+ this.factory = new EventBehavior(this);
1910
+ break;
1911
+ default:
1912
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2039
1913
  }
2040
- this.factory = this.mode[this.aspectType](this);
2041
1914
  }
2042
1915
  return this.factory.createBehavior(targets);
2043
1916
  }
2044
1917
  }
2045
1918
  HTMLDirective.define(HTMLBindingDirective, { aspected: true });
2046
1919
  /**
2047
- * Creates a binding directive with the specified configuration.
2048
- * @param binding - The binding expression.
2049
- * @param config - The binding configuration.
2050
- * @returns A binding directive.
1920
+ * Creates an standard binding.
1921
+ * @param binding - The binding to refresh when changed.
1922
+ * @param isVolatile - Indicates whether the binding is volatile or not.
1923
+ * @returns A binding configuration.
2051
1924
  * @public
2052
1925
  */
2053
- function bind(binding, config = onChange) {
2054
- if (!("mode" in config)) {
2055
- config = onChange(config);
2056
- }
2057
- return new HTMLBindingDirective(binding, config.mode, config.options);
1926
+ function bind(binding, isVolatile = Observable.isVolatileBinding(binding)) {
1927
+ return new OnChangeBinding(binding, isVolatile);
1928
+ }
1929
+ /**
1930
+ * Creates a one time binding
1931
+ * @param binding - The binding to refresh when signaled.
1932
+ * @returns A binding configuration.
1933
+ * @public
1934
+ */
1935
+ function oneTime(binding) {
1936
+ return new OneTimeBinding(binding);
1937
+ }
1938
+ /**
1939
+ * Creates an event listener binding.
1940
+ * @param binding - The binding to invoke when the event is raised.
1941
+ * @param options - Event listener options.
1942
+ * @returns A binding configuration.
1943
+ * @public
1944
+ */
1945
+ function listener(binding, options) {
1946
+ const config = new OnChangeBinding(binding, false);
1947
+ config.options = options;
1948
+ return config;
1949
+ }
1950
+ /**
1951
+ * Normalizes the input value into a binding.
1952
+ * @param value - The value to create the default binding for.
1953
+ * @returns A binding configuration for the provided value.
1954
+ * @public
1955
+ */
1956
+ function normalizeBinding(value) {
1957
+ return isFunction(value)
1958
+ ? bind(value)
1959
+ : value instanceof Binding
1960
+ ? value
1961
+ : oneTime(() => value);
2058
1962
  }
2059
1963
 
2060
1964
  function removeNodeSequence(firstNode, lastNode) {
@@ -2221,6 +2125,22 @@ const next = {
2221
2125
  index: 0,
2222
2126
  node: null,
2223
2127
  };
2128
+ function tryWarn(name) {
2129
+ if (!name.startsWith("fast-")) {
2130
+ FAST.warn(1204 /* Message.hostBindingWithoutHost */, { name });
2131
+ }
2132
+ }
2133
+ const warningHost = new Proxy(document.createElement("div"), {
2134
+ get(target, property) {
2135
+ tryWarn(property);
2136
+ const value = Reflect.get(target, property);
2137
+ return isFunction(value) ? value.bind(target) : value;
2138
+ },
2139
+ set(target, property, value) {
2140
+ tryWarn(property);
2141
+ return Reflect.set(target, property, value);
2142
+ },
2143
+ });
2224
2144
  class CompilationContext {
2225
2145
  constructor(fragment, directives) {
2226
2146
  this.fragment = fragment;
@@ -2271,7 +2191,7 @@ class CompilationContext {
2271
2191
  const fragment = this.fragment.cloneNode(true);
2272
2192
  const targets = Object.create(this.proto);
2273
2193
  targets.r = fragment;
2274
- targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : fragment;
2194
+ targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
2275
2195
  for (const id of this.nodeIds) {
2276
2196
  targets[id]; // trigger locator
2277
2197
  }
@@ -2288,7 +2208,7 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2288
2208
  let result = null;
2289
2209
  if (parseResult === null) {
2290
2210
  if (includeBasicValues) {
2291
- result = bind(() => attrValue, oneTime);
2211
+ result = new HTMLBindingDirective(oneTime(() => attrValue));
2292
2212
  Aspect.assign(result, attr.name);
2293
2213
  }
2294
2214
  }
@@ -2325,6 +2245,7 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
2325
2245
  }
2326
2246
  else {
2327
2247
  currentNode.textContent = " ";
2248
+ Aspect.assign(currentPart);
2328
2249
  context.addFactory(currentPart, parentId, nodeId, nodeIndex);
2329
2250
  }
2330
2251
  lastNode = currentNode;
@@ -2457,22 +2378,28 @@ const Compiler = {
2457
2378
  return parts[0];
2458
2379
  }
2459
2380
  let sourceAspect;
2381
+ let binding;
2382
+ let isVolatile = false;
2460
2383
  const partCount = parts.length;
2461
2384
  const finalParts = parts.map((x) => {
2462
2385
  if (isString(x)) {
2463
2386
  return () => x;
2464
2387
  }
2465
2388
  sourceAspect = x.sourceAspect || sourceAspect;
2466
- return x.binding;
2389
+ binding = x.dataBinding || binding;
2390
+ isVolatile = isVolatile || x.dataBinding.isVolatile;
2391
+ return x.dataBinding.evaluate;
2467
2392
  });
2468
- const binding = (scope, context) => {
2393
+ const expression = (scope, context) => {
2469
2394
  let output = "";
2470
2395
  for (let i = 0; i < partCount; ++i) {
2471
2396
  output += finalParts[i](scope, context);
2472
2397
  }
2473
2398
  return output;
2474
2399
  };
2475
- const directive = bind(binding);
2400
+ binding.evaluate = expression;
2401
+ binding.isVolatile = isVolatile;
2402
+ const directive = new HTMLBindingDirective(binding);
2476
2403
  Aspect.assign(directive, sourceAspect);
2477
2404
  return directive;
2478
2405
  },
@@ -2552,12 +2479,12 @@ function html(strings, ...values) {
2552
2479
  let definition;
2553
2480
  html += currentString;
2554
2481
  if (isFunction(currentValue)) {
2555
- html += createAspectedHTML(bind(currentValue), currentString, add);
2482
+ html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
2556
2483
  }
2557
2484
  else if (isString(currentValue)) {
2558
2485
  const match = lastAttributeNameRegex.exec(currentString);
2559
2486
  if (match !== null) {
2560
- const directive = bind(() => currentValue, oneTime);
2487
+ const directive = new HTMLBindingDirective(oneTime(() => currentValue));
2561
2488
  Aspect.assign(directive, match[2]);
2562
2489
  html += directive.createHTML(add);
2563
2490
  }
@@ -2565,8 +2492,11 @@ function html(strings, ...values) {
2565
2492
  html += currentValue;
2566
2493
  }
2567
2494
  }
2495
+ else if (currentValue instanceof Binding) {
2496
+ html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
2497
+ }
2568
2498
  else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
2569
- html += createAspectedHTML(bind(() => currentValue, oneTime), currentString, add);
2499
+ html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
2570
2500
  }
2571
2501
  else {
2572
2502
  if (definition.aspected) {
@@ -2579,26 +2509,6 @@ function html(strings, ...values) {
2579
2509
  }
2580
2510
  return new ViewTemplate(html + strings[strings.length - 1], factories);
2581
2511
  }
2582
- /**
2583
- * Transforms a template literal string into a ChildViewTemplate.
2584
- * @param strings - The string fragments that are interpolated with the values.
2585
- * @param values - The values that are interpolated with the string fragments.
2586
- * @remarks
2587
- * The html helper supports interpolation of strings, numbers, binding expressions,
2588
- * other template instances, and Directive instances.
2589
- * @public
2590
- */
2591
- const child = html;
2592
- /**
2593
- * Transforms a template literal string into an ItemViewTemplate.
2594
- * @param strings - The string fragments that are interpolated with the values.
2595
- * @param values - The values that are interpolated with the string fragments.
2596
- * @remarks
2597
- * The html helper supports interpolation of strings, numbers, binding expressions,
2598
- * other template instances, and Directive instances.
2599
- * @public
2600
- */
2601
- const item = html;
2602
2512
 
2603
2513
  /**
2604
2514
  * The runtime behavior for template references.
@@ -2631,16 +2541,17 @@ const ref = (propertyName) => new RefDirective(propertyName);
2631
2541
 
2632
2542
  /**
2633
2543
  * A directive that enables basic conditional rendering in a template.
2634
- * @param binding - The condition to test for rendering.
2544
+ * @param condition - The condition to test for rendering.
2635
2545
  * @param templateOrTemplateBinding - The template or a binding that gets
2636
2546
  * the template to render when the condition is true.
2637
2547
  * @public
2638
2548
  */
2639
- function when(binding, templateOrTemplateBinding) {
2640
- const getTemplate = isFunction(templateOrTemplateBinding)
2549
+ function when(condition, templateOrTemplateBinding) {
2550
+ const dataBinding = isFunction(condition) ? condition : () => condition;
2551
+ const templateBinding = isFunction(templateOrTemplateBinding)
2641
2552
  ? templateOrTemplateBinding
2642
2553
  : () => templateOrTemplateBinding;
2643
- return (source, context) => binding(source, context) ? getTemplate(source, context) : null;
2554
+ return (source, context) => dataBinding(source, context) ? templateBinding(source, context) : null;
2644
2555
  }
2645
2556
 
2646
2557
  const defaultRepeatOptions = Object.freeze({
@@ -2661,17 +2572,15 @@ class RepeatBehavior {
2661
2572
  /**
2662
2573
  * Creates an instance of RepeatBehavior.
2663
2574
  * @param location - The location in the DOM to render the repeat.
2664
- * @param itemsBinding - The array to render.
2575
+ * @param dataBinding - The array to render.
2665
2576
  * @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
2666
2577
  * @param templateBinding - The template to render for each item.
2667
2578
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
2668
2579
  * @param options - Options used to turn on special repeat features.
2669
2580
  */
2670
- constructor(location, itemsBinding, isItemsBindingVolatile, templateBinding, isTemplateBindingVolatile, options) {
2581
+ constructor(directive, location) {
2582
+ this.directive = directive;
2671
2583
  this.location = location;
2672
- this.itemsBinding = itemsBinding;
2673
- this.templateBinding = templateBinding;
2674
- this.options = options;
2675
2584
  this.source = null;
2676
2585
  this.views = [];
2677
2586
  this.items = null;
@@ -2679,9 +2588,9 @@ class RepeatBehavior {
2679
2588
  this.context = void 0;
2680
2589
  this.childContext = void 0;
2681
2590
  this.bindView = bindWithoutPositioning;
2682
- this.itemsBindingObserver = Observable.binding(itemsBinding, this, isItemsBindingVolatile);
2683
- this.templateBindingObserver = Observable.binding(templateBinding, this, isTemplateBindingVolatile);
2684
- if (options.positioning) {
2591
+ this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
2592
+ this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
2593
+ if (directive.options.positioning) {
2685
2594
  this.bindView = bindWithPositioning;
2686
2595
  }
2687
2596
  }
@@ -2719,12 +2628,12 @@ class RepeatBehavior {
2719
2628
  * @param args - The details about what was changed.
2720
2629
  */
2721
2630
  handleChange(source, args) {
2722
- if (source === this.itemsBinding) {
2631
+ if (args === this.itemsBindingObserver) {
2723
2632
  this.items = this.itemsBindingObserver.observe(this.source, this.context);
2724
2633
  this.observeItems();
2725
2634
  this.refreshAllViews();
2726
2635
  }
2727
- else if (source === this.templateBinding) {
2636
+ else if (args === this.templateBindingObserver) {
2728
2637
  this.template = this.templateBindingObserver.observe(this.source, this.context);
2729
2638
  this.refreshAllViews(true);
2730
2639
  }
@@ -2753,36 +2662,51 @@ class RepeatBehavior {
2753
2662
  updateViews(splices) {
2754
2663
  const views = this.views;
2755
2664
  const childContext = this.childContext;
2756
- const totalRemoved = [];
2757
2665
  const bindView = this.bindView;
2758
- let removeDelta = 0;
2759
- for (let i = 0, ii = splices.length; i < ii; ++i) {
2760
- const splice = splices[i];
2761
- const removed = splice.removed;
2762
- totalRemoved.push(...views.splice(splice.index + removeDelta, removed.length));
2763
- removeDelta -= splice.addedCount;
2764
- }
2765
2666
  const items = this.items;
2766
2667
  const template = this.template;
2668
+ const recycle = this.directive.options.recycle;
2669
+ const leftoverViews = [];
2670
+ let leftoverIndex = 0;
2671
+ let availableViews = 0;
2767
2672
  for (let i = 0, ii = splices.length; i < ii; ++i) {
2768
2673
  const splice = splices[i];
2674
+ const removed = splice.removed;
2675
+ let removeIndex = 0;
2769
2676
  let addIndex = splice.index;
2770
2677
  const end = addIndex + splice.addedCount;
2678
+ const removedViews = views.splice(splice.index, removed.length);
2679
+ availableViews = leftoverViews.length + removedViews.length;
2771
2680
  for (; addIndex < end; ++addIndex) {
2772
2681
  const neighbor = views[addIndex];
2773
2682
  const location = neighbor ? neighbor.firstChild : this.location;
2774
- const view = this.options.recycle && totalRemoved.length > 0
2775
- ? totalRemoved.shift()
2776
- : template.create();
2683
+ let view;
2684
+ if (recycle && availableViews > 0) {
2685
+ if (removeIndex <= availableViews && removedViews.length > 0) {
2686
+ view = removedViews[removeIndex];
2687
+ removeIndex++;
2688
+ }
2689
+ else {
2690
+ view = leftoverViews[leftoverIndex];
2691
+ leftoverIndex++;
2692
+ }
2693
+ availableViews--;
2694
+ }
2695
+ else {
2696
+ view = template.create();
2697
+ }
2777
2698
  views.splice(addIndex, 0, view);
2778
2699
  bindView(view, items, addIndex, childContext);
2779
2700
  view.insertBefore(location);
2780
2701
  }
2702
+ if (removedViews[removeIndex]) {
2703
+ leftoverViews.push(...removedViews.slice(removeIndex));
2704
+ }
2781
2705
  }
2782
- for (let i = 0, ii = totalRemoved.length; i < ii; ++i) {
2783
- totalRemoved[i].dispose();
2706
+ for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
2707
+ leftoverViews[i].dispose();
2784
2708
  }
2785
- if (this.options.positioning) {
2709
+ if (this.directive.options.positioning) {
2786
2710
  for (let i = 0, ii = views.length; i < ii; ++i) {
2787
2711
  views[i].context.updatePosition(i, ii);
2788
2712
  }
@@ -2797,7 +2721,7 @@ class RepeatBehavior {
2797
2721
  let itemsLength = items.length;
2798
2722
  let views = this.views;
2799
2723
  let viewsLength = views.length;
2800
- if (itemsLength === 0 || templateChanged) {
2724
+ if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
2801
2725
  // all views need to be removed
2802
2726
  HTMLView.disposeContiguousBatch(views);
2803
2727
  viewsLength = 0;
@@ -2847,17 +2771,19 @@ class RepeatBehavior {
2847
2771
  class RepeatDirective {
2848
2772
  /**
2849
2773
  * Creates an instance of RepeatDirective.
2850
- * @param itemsBinding - The binding that provides the array to render.
2774
+ * @param dataBinding - The binding that provides the array to render.
2851
2775
  * @param templateBinding - The template binding used to obtain a template to render for each item in the array.
2852
2776
  * @param options - Options used to turn on special repeat features.
2853
2777
  */
2854
- constructor(itemsBinding, templateBinding, options) {
2855
- this.itemsBinding = itemsBinding;
2778
+ constructor(dataBinding, templateBinding, options) {
2779
+ this.dataBinding = dataBinding;
2856
2780
  this.templateBinding = templateBinding;
2857
2781
  this.options = options;
2782
+ /**
2783
+ * The unique id of the factory.
2784
+ */
2785
+ this.id = nextId();
2858
2786
  ArrayObserver.enable();
2859
- this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
2860
- this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
2861
2787
  }
2862
2788
  /**
2863
2789
  * Creates a placeholder string based on the directive's index within the template.
@@ -2871,15 +2797,22 @@ class RepeatDirective {
2871
2797
  * @param target - The node instance to create the behavior for.
2872
2798
  */
2873
2799
  createBehavior(targets) {
2874
- return new RepeatBehavior(targets[this.nodeId], this.itemsBinding, this.isItemsBindingVolatile, this.templateBinding, this.isTemplateBindingVolatile, this.options);
2800
+ return new RepeatBehavior(this, targets[this.nodeId]);
2875
2801
  }
2876
2802
  }
2877
2803
  HTMLDirective.define(RepeatDirective);
2878
- function repeat(itemsBinding, templateOrTemplateBinding, options = defaultRepeatOptions) {
2879
- const templateBinding = isFunction(templateOrTemplateBinding)
2880
- ? templateOrTemplateBinding
2881
- : () => templateOrTemplateBinding;
2882
- return new RepeatDirective(itemsBinding, templateBinding, options);
2804
+ /**
2805
+ * A directive that enables list rendering.
2806
+ * @param items - The array to render.
2807
+ * @param template - The template or a template binding used obtain a template
2808
+ * to render for each item in the array.
2809
+ * @param options - Options used to turn on special repeat features.
2810
+ * @public
2811
+ */
2812
+ function repeat(items, template, options = defaultRepeatOptions) {
2813
+ const dataBinding = normalizeBinding(items);
2814
+ const templateBinding = normalizeBinding(template);
2815
+ return new RepeatDirective(dataBinding, templateBinding, options);
2883
2816
  }
2884
2817
 
2885
2818
  const selectElements = (value) => value.nodeType === 1;
@@ -3264,19 +3197,15 @@ const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */
3264
3197
  * @public
3265
3198
  */
3266
3199
  class FASTElementDefinition {
3267
- /**
3268
- * Creates an instance of FASTElementDefinition.
3269
- * @param type - The type this definition is being created for.
3270
- * @param nameOrConfig - The name of the element to define or a config object
3271
- * that describes the element to define.
3272
- */
3273
3200
  constructor(type, nameOrConfig = type.definition) {
3201
+ this.platformDefined = false;
3274
3202
  if (isString(nameOrConfig)) {
3275
3203
  nameOrConfig = { name: nameOrConfig };
3276
3204
  }
3277
3205
  this.type = type;
3278
3206
  this.name = nameOrConfig.name;
3279
3207
  this.template = nameOrConfig.template;
3208
+ const proto = type.prototype;
3280
3209
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3281
3210
  const observedAttributes = new Array(attributes.length);
3282
3211
  const propertyLookup = {};
@@ -3286,9 +3215,13 @@ class FASTElementDefinition {
3286
3215
  observedAttributes[i] = current.attribute;
3287
3216
  propertyLookup[current.name] = current;
3288
3217
  attributeLookup[current.attribute] = current;
3218
+ Observable.defineProperty(proto, current);
3289
3219
  }
3220
+ Reflect.defineProperty(type, "observedAttributes", {
3221
+ value: observedAttributes,
3222
+ enumerable: true,
3223
+ });
3290
3224
  this.attributes = attributes;
3291
- this.observedAttributes = observedAttributes;
3292
3225
  this.propertyLookup = propertyLookup;
3293
3226
  this.attributeLookup = attributeLookup;
3294
3227
  this.shadowOptions =
@@ -3301,43 +3234,43 @@ class FASTElementDefinition {
3301
3234
  nameOrConfig.elementOptions === void 0
3302
3235
  ? defaultElementOptions
3303
3236
  : Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
3304
- this.styles =
3305
- nameOrConfig.styles === void 0
3306
- ? void 0
3307
- : Array.isArray(nameOrConfig.styles)
3308
- ? new ElementStyles(nameOrConfig.styles)
3309
- : nameOrConfig.styles instanceof ElementStyles
3310
- ? nameOrConfig.styles
3311
- : new ElementStyles([nameOrConfig.styles]);
3237
+ this.styles = ElementStyles.normalize(nameOrConfig.styles);
3238
+ fastElementRegistry.register(this);
3312
3239
  }
3313
3240
  /**
3314
3241
  * Indicates if this element has been defined in at least one registry.
3315
3242
  */
3316
3243
  get isDefined() {
3317
- return !!fastElementRegistry.getByType(this.type);
3244
+ return this.platformDefined;
3318
3245
  }
3319
3246
  /**
3320
3247
  * Defines a custom element based on this definition.
3321
3248
  * @param registry - The element registry to define the element in.
3249
+ * @remarks
3250
+ * This operation is idempotent per registry.
3322
3251
  */
3323
3252
  define(registry = customElements) {
3324
3253
  const type = this.type;
3325
- if (fastElementRegistry.register(this)) {
3326
- const attributes = this.attributes;
3327
- const proto = type.prototype;
3328
- for (let i = 0, ii = attributes.length; i < ii; ++i) {
3329
- Observable.defineProperty(proto, attributes[i]);
3330
- }
3331
- Reflect.defineProperty(type, "observedAttributes", {
3332
- value: this.observedAttributes,
3333
- enumerable: true,
3334
- });
3335
- }
3336
3254
  if (!registry.get(this.name)) {
3255
+ this.platformDefined = true;
3337
3256
  registry.define(this.name, type, this.elementOptions);
3338
3257
  }
3339
3258
  return this;
3340
3259
  }
3260
+ /**
3261
+ * Creates an instance of FASTElementDefinition.
3262
+ * @param type - The type this definition is being created for.
3263
+ * @param nameOrDef - The name of the element to define or a config object
3264
+ * that describes the element to define.
3265
+ */
3266
+ static compose(type, nameOrDef) {
3267
+ const found = fastElementRegistry.getByType(type);
3268
+ if (found) {
3269
+ return new FASTElementDefinition(class extends type {
3270
+ }, nameOrDef);
3271
+ }
3272
+ return new FASTElementDefinition(type, nameOrDef);
3273
+ }
3341
3274
  }
3342
3275
  /**
3343
3276
  * Gets the element definition associated with the specified type.
@@ -3755,6 +3688,18 @@ function createFASTElement(BaseType) {
3755
3688
  }
3756
3689
  };
3757
3690
  }
3691
+ function compose(type, nameOrDef) {
3692
+ if (isFunction(type)) {
3693
+ return FASTElementDefinition.compose(type, nameOrDef);
3694
+ }
3695
+ return FASTElementDefinition.compose(this, type);
3696
+ }
3697
+ function define(type, nameOrDef) {
3698
+ if (isFunction(type)) {
3699
+ return FASTElementDefinition.compose(type, nameOrDef).define().type;
3700
+ }
3701
+ return FASTElementDefinition.compose(this, type).define().type;
3702
+ }
3758
3703
  /**
3759
3704
  * A minimal base class for FASTElements that also provides
3760
3705
  * static helpers for working with FASTElements.
@@ -3775,9 +3720,12 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3775
3720
  * @param nameOrDef - The name of the element to define or a definition object
3776
3721
  * that describes the element to define.
3777
3722
  */
3778
- define(type, nameOrDef) {
3779
- return new FASTElementDefinition(type, nameOrDef).define().type;
3780
- },
3723
+ define,
3724
+ /**
3725
+ * Defines metadata for a FASTElement which can be used to later define the element.
3726
+ * @public
3727
+ */
3728
+ compose,
3781
3729
  });
3782
3730
  /**
3783
3731
  * Decorator: Defines a platform custom element based on `FASTElement`.
@@ -3788,8 +3736,8 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3788
3736
  function customElement(nameOrDef) {
3789
3737
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
3790
3738
  return function (type) {
3791
- new FASTElementDefinition(type, nameOrDef).define();
3739
+ define(type, nameOrDef);
3792
3740
  };
3793
3741
  }
3794
3742
 
3795
- 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 };
3743
+ 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 };