@microsoft/fast-element 2.0.0-beta.3 → 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 (50) hide show
  1. package/CHANGELOG.json +81 -0
  2. package/CHANGELOG.md +20 -1
  3. package/dist/dts/components/fast-definitions.d.ts +9 -8
  4. package/dist/dts/components/fast-element.d.ts +8 -4
  5. package/dist/dts/context.d.ts +1 -1
  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 +38 -7
  9. package/dist/dts/observation/observable.d.ts +19 -13
  10. package/dist/dts/styles/element-styles.d.ts +6 -0
  11. package/dist/dts/templating/binding-signal.d.ts +10 -27
  12. package/dist/dts/templating/binding-two-way.d.ts +16 -41
  13. package/dist/dts/templating/binding.d.ts +79 -118
  14. package/dist/dts/templating/html-directive.d.ts +28 -2
  15. package/dist/dts/templating/render.d.ts +277 -0
  16. package/dist/dts/templating/repeat.d.ts +12 -16
  17. package/dist/dts/templating/template.d.ts +3 -3
  18. package/dist/dts/templating/when.d.ts +3 -3
  19. package/dist/dts/testing/exports.d.ts +2 -0
  20. package/dist/dts/testing/fixture.d.ts +90 -0
  21. package/dist/dts/testing/timeout.d.ts +7 -0
  22. package/dist/esm/components/fast-definitions.js +25 -27
  23. package/dist/esm/components/fast-element.js +16 -8
  24. package/dist/esm/context.js +5 -1
  25. package/dist/esm/debug.js +34 -4
  26. package/dist/esm/di/di.js +1349 -0
  27. package/dist/esm/observation/observable.js +4 -4
  28. package/dist/esm/platform.js +1 -1
  29. package/dist/esm/styles/element-styles.js +14 -0
  30. package/dist/esm/templating/binding-signal.js +56 -61
  31. package/dist/esm/templating/binding-two-way.js +51 -35
  32. package/dist/esm/templating/binding.js +137 -156
  33. package/dist/esm/templating/compiler.js +29 -7
  34. package/dist/esm/templating/html-directive.js +12 -1
  35. package/dist/esm/templating/render.js +392 -0
  36. package/dist/esm/templating/repeat.js +54 -40
  37. package/dist/esm/templating/template.js +8 -5
  38. package/dist/esm/templating/when.js +5 -4
  39. package/dist/esm/testing/exports.js +2 -0
  40. package/dist/esm/testing/fixture.js +88 -0
  41. package/dist/esm/testing/timeout.js +24 -0
  42. package/dist/fast-element.api.json +3257 -3151
  43. package/dist/fast-element.d.ts +210 -209
  44. package/dist/fast-element.debug.js +329 -248
  45. package/dist/fast-element.debug.min.js +1 -1
  46. package/dist/fast-element.js +295 -244
  47. package/dist/fast-element.min.js +1 -1
  48. package/dist/fast-element.untrimmed.d.ts +218 -214
  49. package/docs/api-report.md +83 -85
  50. package/package.json +13 -1
@@ -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
@@ -1117,6 +1117,20 @@ class ElementStyles {
1117
1117
  static setDefaultStrategy(Strategy) {
1118
1118
  DefaultStyleStrategy = Strategy;
1119
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
+ }
1120
1134
  }
1121
1135
  /**
1122
1136
  * Indicates whether the DOM supports the adoptedStyleSheets feature.
@@ -1443,6 +1457,13 @@ function htmlDirective(options) {
1443
1457
  HTMLDirective.define(type, options);
1444
1458
  };
1445
1459
  }
1460
+ /**
1461
+ * Captures a binding expression along with related information and capabilities.
1462
+ *
1463
+ * @public
1464
+ */
1465
+ class Binding {
1466
+ }
1446
1467
  /**
1447
1468
  * The type of HTML aspect to target.
1448
1469
  * @public
@@ -1536,6 +1557,10 @@ class StatelessAttachedAttributeDirective {
1536
1557
  */
1537
1558
  constructor(options) {
1538
1559
  this.options = options;
1560
+ /**
1561
+ * The unique id of the factory.
1562
+ */
1563
+ this.id = nextId();
1539
1564
  }
1540
1565
  /**
1541
1566
  * Creates a behavior.
@@ -1564,99 +1589,28 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
1564
1589
  throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
1565
1590
  }
1566
1591
  : (binding) => binding;
1567
- /**
1568
- * Describes how aspects of an HTML element will be affected by bindings.
1569
- * @public
1570
- */
1571
- const BindingMode = Object.freeze({
1572
- /**
1573
- * Creates a binding mode based on the supplied behavior types.
1574
- * @param UpdateType - The base behavior type used to update aspects.
1575
- * @param EventType - The base behavior type used to respond to events.
1576
- * @returns A new binding mode.
1577
- */
1578
- define(UpdateType, EventType = EventBinding) {
1579
- return Object.freeze({
1580
- [1]: d => new UpdateType(d, DOM.setAttribute),
1581
- [2]: d => new UpdateType(d, DOM.setBooleanAttribute),
1582
- [3]: d => new UpdateType(d, (t, a, v) => (t[a] = v)),
1583
- [4]: d => new (createContentBinding(UpdateType))(d, updateContentTarget),
1584
- [5]: d => new UpdateType(d, updateTokenListTarget),
1585
- [6]: d => new EventType(d),
1586
- });
1587
- },
1588
- });
1589
- /**
1590
- * Describes the configuration for a binding expression.
1591
- * @public
1592
- */
1593
- const BindingConfig = Object.freeze({
1594
- /**
1595
- * Creates a binding configuration based on the provided mode and options.
1596
- * @param mode - The mode to use for the configuration.
1597
- * @param defaultOptions - The default options to use for the configuration.
1598
- * @returns A new binding configuration.
1599
- */
1600
- define(mode, defaultOptions) {
1601
- const config = (options) => {
1602
- return {
1603
- mode: config.mode,
1604
- options: Object.assign({}, defaultOptions, options),
1605
- };
1606
- };
1607
- config.options = defaultOptions;
1608
- config.mode = mode;
1609
- return config;
1610
- },
1611
- });
1612
- /**
1613
- * A base binding behavior for DOM updates.
1614
- * @public
1615
- */
1616
- class UpdateBinding {
1617
- /**
1618
- * Creates an instance of UpdateBinding.
1619
- * @param directive - The directive that has the configuration for this behavior.
1620
- * @param updateTarget - The function used to update the target with the latest value.
1621
- */
1622
- constructor(directive, updateTarget) {
1623
- this.directive = directive;
1624
- this.updateTarget = updateTarget;
1592
+ class OnChangeBinding extends Binding {
1593
+ constructor(evaluate, isVolatile) {
1594
+ super();
1595
+ this.evaluate = evaluate;
1596
+ this.isVolatile = isVolatile;
1625
1597
  }
1626
- /**
1627
- * Bind this behavior to the source.
1628
- * @param source - The source to bind to.
1629
- * @param context - The execution context that the binding is operating within.
1630
- * @param targets - The targets that behaviors in a view can attach to.
1631
- */
1632
- bind(source, context, targets) { }
1633
- /**
1634
- * Unbinds this behavior from the source.
1635
- * @param source - The source to unbind from.
1636
- * @param context - The execution context that the binding is operating within.
1637
- * @param targets - The targets that behaviors in a view can attach to.
1638
- */
1639
- unbind(source, context, targets) { }
1640
- /**
1641
- * Creates a behavior.
1642
- * @param targets - The targets available for behaviors to be attached to.
1643
- */
1644
- createBehavior(targets) {
1645
- return this;
1598
+ createObserver(_, subscriber) {
1599
+ return Observable.binding(this.evaluate, subscriber, this.isVolatile);
1646
1600
  }
1647
1601
  }
1648
- function createContentBinding(Type) {
1649
- return class extends Type {
1650
- unbind(source, context, targets) {
1651
- super.unbind(source, context, targets);
1652
- const target = targets[this.directive.nodeId];
1653
- const view = target.$fastView;
1654
- if (view !== void 0 && view.isComposed) {
1655
- view.unbind();
1656
- view.needsBindOnly = true;
1657
- }
1658
- }
1659
- };
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() { }
1660
1614
  }
1661
1615
  function updateContentTarget(target, aspect, value, source, context) {
1662
1616
  // If there's no actual value, then this equates to the
@@ -1664,7 +1618,7 @@ function updateContentTarget(target, aspect, value, source, context) {
1664
1618
  if (value === null || value === undefined) {
1665
1619
  value = "";
1666
1620
  }
1667
- // 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.
1668
1622
  if (value.create) {
1669
1623
  target.textContent = "";
1670
1624
  let view = target.$fastView;
@@ -1750,46 +1704,21 @@ function updateTokenListTarget(target, aspect, value) {
1750
1704
  }
1751
1705
  }
1752
1706
  }
1753
- /**
1754
- * A binding behavior for one-time bindings.
1755
- * @public
1756
- */
1757
- class OneTimeBinding extends UpdateBinding {
1758
- /**
1759
- * Bind this behavior to the source.
1760
- * @param source - The source to bind to.
1761
- * @param context - The execution context that the binding is operating within.
1762
- * @param targets - The targets that behaviors in a view can attach to.
1763
- */
1764
- bind(source, context, targets) {
1765
- const directive = this.directive;
1766
- this.updateTarget(targets[directive.nodeId], directive.targetAspect, directive.binding(source, context), source, context);
1767
- }
1768
- }
1769
1707
  /**
1770
1708
  * A binding behavior for bindings that change.
1771
1709
  * @public
1772
1710
  */
1773
- class ChangeBinding extends UpdateBinding {
1711
+ class BindingBehavior {
1774
1712
  /**
1775
1713
  * Creates an instance of ChangeBinding.
1776
1714
  * @param directive - The directive that has the configuration for this behavior.
1777
1715
  * @param updateTarget - The function used to update the target with the latest value.
1778
1716
  */
1779
1717
  constructor(directive, updateTarget) {
1780
- super(directive, updateTarget);
1781
- this.isBindingVolatile = Observable.isVolatileBinding(directive.binding);
1718
+ this.directive = directive;
1719
+ this.updateTarget = updateTarget;
1782
1720
  this.observerProperty = `${directive.id}-o`;
1783
1721
  }
1784
- /**
1785
- * Returns the binding observer used to update the node.
1786
- * @param target - The target node.
1787
- * @returns A BindingObserver.
1788
- */
1789
- getObserver(target) {
1790
- var _a;
1791
- return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = Observable.binding(this.directive.binding, this, this.isBindingVolatile)));
1792
- }
1793
1722
  /**
1794
1723
  * Bind this behavior to the source.
1795
1724
  * @param source - The source to bind to.
@@ -1826,12 +1755,49 @@ class ChangeBinding extends UpdateBinding {
1826
1755
  const context = observer.context;
1827
1756
  this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
1828
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
+ }
1829
1795
  }
1830
1796
  /**
1831
1797
  * A binding behavior for handling events.
1832
1798
  * @public
1833
1799
  */
1834
- class EventBinding {
1800
+ class EventBehavior {
1835
1801
  /**
1836
1802
  * Creates an instance of EventBinding.
1837
1803
  * @param directive - The directive that has the configuration for this behavior.
@@ -1852,7 +1818,7 @@ class EventBinding {
1852
1818
  const target = targets[directive.nodeId];
1853
1819
  target[this.sourceProperty] = source;
1854
1820
  target[this.contextProperty] = context;
1855
- target.addEventListener(directive.targetAspect, this, directive.options);
1821
+ target.addEventListener(directive.targetAspect, this, directive.dataBinding.options);
1856
1822
  }
1857
1823
  /**
1858
1824
  * Unbinds this behavior from the source.
@@ -1864,7 +1830,7 @@ class EventBinding {
1864
1830
  const directive = this.directive;
1865
1831
  const target = targets[directive.nodeId];
1866
1832
  target[this.sourceProperty] = target[this.contextProperty] = null;
1867
- target.removeEventListener(directive.targetAspect, this, directive.options);
1833
+ target.removeEventListener(directive.targetAspect, this, directive.dataBinding.options);
1868
1834
  }
1869
1835
  /**
1870
1836
  * Creates a behavior.
@@ -1879,25 +1845,13 @@ class EventBinding {
1879
1845
  handleEvent(event) {
1880
1846
  const target = event.currentTarget;
1881
1847
  ExecutionContext.setEvent(event);
1882
- const result = this.directive.binding(target[this.sourceProperty], target[this.contextProperty]);
1848
+ const result = this.directive.dataBinding.evaluate(target[this.sourceProperty], target[this.contextProperty]);
1883
1849
  ExecutionContext.setEvent(null);
1884
1850
  if (result !== true) {
1885
1851
  event.preventDefault();
1886
1852
  }
1887
1853
  }
1888
1854
  }
1889
- /**
1890
- * The default onChange binding configuration.
1891
- * @public
1892
- */
1893
- const onChange = BindingConfig.define(BindingMode.define(ChangeBinding), {});
1894
- /**
1895
- * The default onTime binding configuration.
1896
- * @public
1897
- */
1898
- const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
1899
- once: true,
1900
- });
1901
1855
  /**
1902
1856
  * A directive that applies bindings.
1903
1857
  * @public
@@ -1905,15 +1859,15 @@ const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
1905
1859
  class HTMLBindingDirective {
1906
1860
  /**
1907
1861
  * Creates an instance of HTMLBindingDirective.
1908
- * @param binding - The binding to apply.
1909
- * @param mode - The binding mode to use when applying the binding.
1910
- * @param options - The options to configure the binding with.
1862
+ * @param dataBinding - The binding configuration to apply.
1911
1863
  */
1912
- constructor(binding, mode, options) {
1913
- this.binding = binding;
1914
- this.mode = mode;
1915
- this.options = options;
1864
+ constructor(dataBinding) {
1865
+ this.dataBinding = dataBinding;
1916
1866
  this.factory = null;
1867
+ /**
1868
+ * The unique id of the factory.
1869
+ */
1870
+ this.id = nextId();
1917
1871
  /**
1918
1872
  * The type of aspect to target.
1919
1873
  */
@@ -1933,26 +1887,78 @@ class HTMLBindingDirective {
1933
1887
  createBehavior(targets) {
1934
1888
  if (this.factory == null) {
1935
1889
  if (this.targetAspect === "innerHTML") {
1936
- 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 */);
1937
1913
  }
1938
- this.factory = this.mode[this.aspectType](this);
1939
1914
  }
1940
1915
  return this.factory.createBehavior(targets);
1941
1916
  }
1942
1917
  }
1943
1918
  HTMLDirective.define(HTMLBindingDirective, { aspected: true });
1944
1919
  /**
1945
- * Creates a binding directive with the specified configuration.
1946
- * @param binding - The binding expression.
1947
- * @param config - The binding configuration.
1948
- * @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.
1949
1924
  * @public
1950
1925
  */
1951
- function bind(binding, config = onChange) {
1952
- if (!("mode" in config)) {
1953
- config = onChange(config);
1954
- }
1955
- 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);
1956
1962
  }
1957
1963
 
1958
1964
  function removeNodeSequence(firstNode, lastNode) {
@@ -2119,6 +2125,22 @@ const next = {
2119
2125
  index: 0,
2120
2126
  node: null,
2121
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
+ });
2122
2144
  class CompilationContext {
2123
2145
  constructor(fragment, directives) {
2124
2146
  this.fragment = fragment;
@@ -2169,7 +2191,7 @@ class CompilationContext {
2169
2191
  const fragment = this.fragment.cloneNode(true);
2170
2192
  const targets = Object.create(this.proto);
2171
2193
  targets.r = fragment;
2172
- targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : fragment;
2194
+ targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
2173
2195
  for (const id of this.nodeIds) {
2174
2196
  targets[id]; // trigger locator
2175
2197
  }
@@ -2186,7 +2208,7 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2186
2208
  let result = null;
2187
2209
  if (parseResult === null) {
2188
2210
  if (includeBasicValues) {
2189
- result = bind(() => attrValue, oneTime);
2211
+ result = new HTMLBindingDirective(oneTime(() => attrValue));
2190
2212
  Aspect.assign(result, attr.name);
2191
2213
  }
2192
2214
  }
@@ -2356,22 +2378,28 @@ const Compiler = {
2356
2378
  return parts[0];
2357
2379
  }
2358
2380
  let sourceAspect;
2381
+ let binding;
2382
+ let isVolatile = false;
2359
2383
  const partCount = parts.length;
2360
2384
  const finalParts = parts.map((x) => {
2361
2385
  if (isString(x)) {
2362
2386
  return () => x;
2363
2387
  }
2364
2388
  sourceAspect = x.sourceAspect || sourceAspect;
2365
- return x.binding;
2389
+ binding = x.dataBinding || binding;
2390
+ isVolatile = isVolatile || x.dataBinding.isVolatile;
2391
+ return x.dataBinding.evaluate;
2366
2392
  });
2367
- const binding = (scope, context) => {
2393
+ const expression = (scope, context) => {
2368
2394
  let output = "";
2369
2395
  for (let i = 0; i < partCount; ++i) {
2370
2396
  output += finalParts[i](scope, context);
2371
2397
  }
2372
2398
  return output;
2373
2399
  };
2374
- const directive = bind(binding);
2400
+ binding.evaluate = expression;
2401
+ binding.isVolatile = isVolatile;
2402
+ const directive = new HTMLBindingDirective(binding);
2375
2403
  Aspect.assign(directive, sourceAspect);
2376
2404
  return directive;
2377
2405
  },
@@ -2451,12 +2479,12 @@ function html(strings, ...values) {
2451
2479
  let definition;
2452
2480
  html += currentString;
2453
2481
  if (isFunction(currentValue)) {
2454
- html += createAspectedHTML(bind(currentValue), currentString, add);
2482
+ html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
2455
2483
  }
2456
2484
  else if (isString(currentValue)) {
2457
2485
  const match = lastAttributeNameRegex.exec(currentString);
2458
2486
  if (match !== null) {
2459
- const directive = bind(() => currentValue, oneTime);
2487
+ const directive = new HTMLBindingDirective(oneTime(() => currentValue));
2460
2488
  Aspect.assign(directive, match[2]);
2461
2489
  html += directive.createHTML(add);
2462
2490
  }
@@ -2464,8 +2492,11 @@ function html(strings, ...values) {
2464
2492
  html += currentValue;
2465
2493
  }
2466
2494
  }
2495
+ else if (currentValue instanceof Binding) {
2496
+ html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
2497
+ }
2467
2498
  else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
2468
- html += createAspectedHTML(bind(() => currentValue, oneTime), currentString, add);
2499
+ html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
2469
2500
  }
2470
2501
  else {
2471
2502
  if (definition.aspected) {
@@ -2510,16 +2541,17 @@ const ref = (propertyName) => new RefDirective(propertyName);
2510
2541
 
2511
2542
  /**
2512
2543
  * A directive that enables basic conditional rendering in a template.
2513
- * @param binding - The condition to test for rendering.
2544
+ * @param condition - The condition to test for rendering.
2514
2545
  * @param templateOrTemplateBinding - The template or a binding that gets
2515
2546
  * the template to render when the condition is true.
2516
2547
  * @public
2517
2548
  */
2518
- function when(binding, templateOrTemplateBinding) {
2519
- const getTemplate = isFunction(templateOrTemplateBinding)
2549
+ function when(condition, templateOrTemplateBinding) {
2550
+ const dataBinding = isFunction(condition) ? condition : () => condition;
2551
+ const templateBinding = isFunction(templateOrTemplateBinding)
2520
2552
  ? templateOrTemplateBinding
2521
2553
  : () => templateOrTemplateBinding;
2522
- return (source, context) => binding(source, context) ? getTemplate(source, context) : null;
2554
+ return (source, context) => dataBinding(source, context) ? templateBinding(source, context) : null;
2523
2555
  }
2524
2556
 
2525
2557
  const defaultRepeatOptions = Object.freeze({
@@ -2540,17 +2572,15 @@ class RepeatBehavior {
2540
2572
  /**
2541
2573
  * Creates an instance of RepeatBehavior.
2542
2574
  * @param location - The location in the DOM to render the repeat.
2543
- * @param itemsBinding - The array to render.
2575
+ * @param dataBinding - The array to render.
2544
2576
  * @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
2545
2577
  * @param templateBinding - The template to render for each item.
2546
2578
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
2547
2579
  * @param options - Options used to turn on special repeat features.
2548
2580
  */
2549
- constructor(location, itemsBinding, isItemsBindingVolatile, templateBinding, isTemplateBindingVolatile, options) {
2581
+ constructor(directive, location) {
2582
+ this.directive = directive;
2550
2583
  this.location = location;
2551
- this.itemsBinding = itemsBinding;
2552
- this.templateBinding = templateBinding;
2553
- this.options = options;
2554
2584
  this.source = null;
2555
2585
  this.views = [];
2556
2586
  this.items = null;
@@ -2558,9 +2588,9 @@ class RepeatBehavior {
2558
2588
  this.context = void 0;
2559
2589
  this.childContext = void 0;
2560
2590
  this.bindView = bindWithoutPositioning;
2561
- this.itemsBindingObserver = Observable.binding(itemsBinding, this, isItemsBindingVolatile);
2562
- this.templateBindingObserver = Observable.binding(templateBinding, this, isTemplateBindingVolatile);
2563
- if (options.positioning) {
2591
+ this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
2592
+ this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
2593
+ if (directive.options.positioning) {
2564
2594
  this.bindView = bindWithPositioning;
2565
2595
  }
2566
2596
  }
@@ -2598,12 +2628,12 @@ class RepeatBehavior {
2598
2628
  * @param args - The details about what was changed.
2599
2629
  */
2600
2630
  handleChange(source, args) {
2601
- if (source === this.itemsBinding) {
2631
+ if (args === this.itemsBindingObserver) {
2602
2632
  this.items = this.itemsBindingObserver.observe(this.source, this.context);
2603
2633
  this.observeItems();
2604
2634
  this.refreshAllViews();
2605
2635
  }
2606
- else if (source === this.templateBinding) {
2636
+ else if (args === this.templateBindingObserver) {
2607
2637
  this.template = this.templateBindingObserver.observe(this.source, this.context);
2608
2638
  this.refreshAllViews(true);
2609
2639
  }
@@ -2632,36 +2662,51 @@ class RepeatBehavior {
2632
2662
  updateViews(splices) {
2633
2663
  const views = this.views;
2634
2664
  const childContext = this.childContext;
2635
- const totalRemoved = [];
2636
2665
  const bindView = this.bindView;
2637
- let removeDelta = 0;
2638
- for (let i = 0, ii = splices.length; i < ii; ++i) {
2639
- const splice = splices[i];
2640
- const removed = splice.removed;
2641
- totalRemoved.push(...views.splice(splice.index + removeDelta, removed.length));
2642
- removeDelta -= splice.addedCount;
2643
- }
2644
2666
  const items = this.items;
2645
2667
  const template = this.template;
2668
+ const recycle = this.directive.options.recycle;
2669
+ const leftoverViews = [];
2670
+ let leftoverIndex = 0;
2671
+ let availableViews = 0;
2646
2672
  for (let i = 0, ii = splices.length; i < ii; ++i) {
2647
2673
  const splice = splices[i];
2674
+ const removed = splice.removed;
2675
+ let removeIndex = 0;
2648
2676
  let addIndex = splice.index;
2649
2677
  const end = addIndex + splice.addedCount;
2678
+ const removedViews = views.splice(splice.index, removed.length);
2679
+ availableViews = leftoverViews.length + removedViews.length;
2650
2680
  for (; addIndex < end; ++addIndex) {
2651
2681
  const neighbor = views[addIndex];
2652
2682
  const location = neighbor ? neighbor.firstChild : this.location;
2653
- const view = this.options.recycle && totalRemoved.length > 0
2654
- ? totalRemoved.shift()
2655
- : 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
+ }
2656
2698
  views.splice(addIndex, 0, view);
2657
2699
  bindView(view, items, addIndex, childContext);
2658
2700
  view.insertBefore(location);
2659
2701
  }
2702
+ if (removedViews[removeIndex]) {
2703
+ leftoverViews.push(...removedViews.slice(removeIndex));
2704
+ }
2660
2705
  }
2661
- for (let i = 0, ii = totalRemoved.length; i < ii; ++i) {
2662
- totalRemoved[i].dispose();
2706
+ for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
2707
+ leftoverViews[i].dispose();
2663
2708
  }
2664
- if (this.options.positioning) {
2709
+ if (this.directive.options.positioning) {
2665
2710
  for (let i = 0, ii = views.length; i < ii; ++i) {
2666
2711
  views[i].context.updatePosition(i, ii);
2667
2712
  }
@@ -2676,7 +2721,7 @@ class RepeatBehavior {
2676
2721
  let itemsLength = items.length;
2677
2722
  let views = this.views;
2678
2723
  let viewsLength = views.length;
2679
- if (itemsLength === 0 || templateChanged || !this.options.recycle) {
2724
+ if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
2680
2725
  // all views need to be removed
2681
2726
  HTMLView.disposeContiguousBatch(views);
2682
2727
  viewsLength = 0;
@@ -2726,17 +2771,19 @@ class RepeatBehavior {
2726
2771
  class RepeatDirective {
2727
2772
  /**
2728
2773
  * Creates an instance of RepeatDirective.
2729
- * @param itemsBinding - The binding that provides the array to render.
2774
+ * @param dataBinding - The binding that provides the array to render.
2730
2775
  * @param templateBinding - The template binding used to obtain a template to render for each item in the array.
2731
2776
  * @param options - Options used to turn on special repeat features.
2732
2777
  */
2733
- constructor(itemsBinding, templateBinding, options) {
2734
- this.itemsBinding = itemsBinding;
2778
+ constructor(dataBinding, templateBinding, options) {
2779
+ this.dataBinding = dataBinding;
2735
2780
  this.templateBinding = templateBinding;
2736
2781
  this.options = options;
2782
+ /**
2783
+ * The unique id of the factory.
2784
+ */
2785
+ this.id = nextId();
2737
2786
  ArrayObserver.enable();
2738
- this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
2739
- this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
2740
2787
  }
2741
2788
  /**
2742
2789
  * Creates a placeholder string based on the directive's index within the template.
@@ -2750,23 +2797,22 @@ class RepeatDirective {
2750
2797
  * @param target - The node instance to create the behavior for.
2751
2798
  */
2752
2799
  createBehavior(targets) {
2753
- return new RepeatBehavior(targets[this.nodeId], this.itemsBinding, this.isItemsBindingVolatile, this.templateBinding, this.isTemplateBindingVolatile, this.options);
2800
+ return new RepeatBehavior(this, targets[this.nodeId]);
2754
2801
  }
2755
2802
  }
2756
2803
  HTMLDirective.define(RepeatDirective);
2757
2804
  /**
2758
2805
  * A directive that enables list rendering.
2759
- * @param itemsBinding - The array to render.
2760
- * @param templateOrTemplateBinding - The template or a template binding used obtain a template
2806
+ * @param items - The array to render.
2807
+ * @param template - The template or a template binding used obtain a template
2761
2808
  * to render for each item in the array.
2762
2809
  * @param options - Options used to turn on special repeat features.
2763
2810
  * @public
2764
2811
  */
2765
- function repeat(itemsBinding, templateOrTemplateBinding, options = defaultRepeatOptions) {
2766
- const templateBinding = isFunction(templateOrTemplateBinding)
2767
- ? templateOrTemplateBinding
2768
- : () => templateOrTemplateBinding;
2769
- return new RepeatDirective(itemsBinding, templateBinding, options);
2812
+ function repeat(items, template, options = defaultRepeatOptions) {
2813
+ const dataBinding = normalizeBinding(items);
2814
+ const templateBinding = normalizeBinding(template);
2815
+ return new RepeatDirective(dataBinding, templateBinding, options);
2770
2816
  }
2771
2817
 
2772
2818
  const selectElements = (value) => value.nodeType === 1;
@@ -3151,19 +3197,15 @@ const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */
3151
3197
  * @public
3152
3198
  */
3153
3199
  class FASTElementDefinition {
3154
- /**
3155
- * Creates an instance of FASTElementDefinition.
3156
- * @param type - The type this definition is being created for.
3157
- * @param nameOrConfig - The name of the element to define or a config object
3158
- * that describes the element to define.
3159
- */
3160
3200
  constructor(type, nameOrConfig = type.definition) {
3201
+ this.platformDefined = false;
3161
3202
  if (isString(nameOrConfig)) {
3162
3203
  nameOrConfig = { name: nameOrConfig };
3163
3204
  }
3164
3205
  this.type = type;
3165
3206
  this.name = nameOrConfig.name;
3166
3207
  this.template = nameOrConfig.template;
3208
+ const proto = type.prototype;
3167
3209
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3168
3210
  const observedAttributes = new Array(attributes.length);
3169
3211
  const propertyLookup = {};
@@ -3173,9 +3215,13 @@ class FASTElementDefinition {
3173
3215
  observedAttributes[i] = current.attribute;
3174
3216
  propertyLookup[current.name] = current;
3175
3217
  attributeLookup[current.attribute] = current;
3218
+ Observable.defineProperty(proto, current);
3176
3219
  }
3220
+ Reflect.defineProperty(type, "observedAttributes", {
3221
+ value: observedAttributes,
3222
+ enumerable: true,
3223
+ });
3177
3224
  this.attributes = attributes;
3178
- this.observedAttributes = observedAttributes;
3179
3225
  this.propertyLookup = propertyLookup;
3180
3226
  this.attributeLookup = attributeLookup;
3181
3227
  this.shadowOptions =
@@ -3188,20 +3234,14 @@ class FASTElementDefinition {
3188
3234
  nameOrConfig.elementOptions === void 0
3189
3235
  ? defaultElementOptions
3190
3236
  : Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
3191
- this.styles =
3192
- nameOrConfig.styles === void 0
3193
- ? void 0
3194
- : Array.isArray(nameOrConfig.styles)
3195
- ? new ElementStyles(nameOrConfig.styles)
3196
- : nameOrConfig.styles instanceof ElementStyles
3197
- ? nameOrConfig.styles
3198
- : new ElementStyles([nameOrConfig.styles]);
3237
+ this.styles = ElementStyles.normalize(nameOrConfig.styles);
3238
+ fastElementRegistry.register(this);
3199
3239
  }
3200
3240
  /**
3201
3241
  * Indicates if this element has been defined in at least one registry.
3202
3242
  */
3203
3243
  get isDefined() {
3204
- return !!fastElementRegistry.getByType(this.type);
3244
+ return this.platformDefined;
3205
3245
  }
3206
3246
  /**
3207
3247
  * Defines a custom element based on this definition.
@@ -3211,22 +3251,26 @@ class FASTElementDefinition {
3211
3251
  */
3212
3252
  define(registry = customElements) {
3213
3253
  const type = this.type;
3214
- if (fastElementRegistry.register(this)) {
3215
- const attributes = this.attributes;
3216
- const proto = type.prototype;
3217
- for (let i = 0, ii = attributes.length; i < ii; ++i) {
3218
- Observable.defineProperty(proto, attributes[i]);
3219
- }
3220
- Reflect.defineProperty(type, "observedAttributes", {
3221
- value: this.observedAttributes,
3222
- enumerable: true,
3223
- });
3224
- }
3225
3254
  if (!registry.get(this.name)) {
3255
+ this.platformDefined = true;
3226
3256
  registry.define(this.name, type, this.elementOptions);
3227
3257
  }
3228
3258
  return this;
3229
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
+ }
3230
3274
  }
3231
3275
  /**
3232
3276
  * Gets the element definition associated with the specified type.
@@ -3644,6 +3688,18 @@ function createFASTElement(BaseType) {
3644
3688
  }
3645
3689
  };
3646
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
+ }
3647
3703
  /**
3648
3704
  * A minimal base class for FASTElements that also provides
3649
3705
  * static helpers for working with FASTElements.
@@ -3664,17 +3720,12 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3664
3720
  * @param nameOrDef - The name of the element to define or a definition object
3665
3721
  * that describes the element to define.
3666
3722
  */
3667
- define(type, nameOrDef) {
3668
- return this.metadata(type, nameOrDef).define().type;
3669
- },
3723
+ define,
3670
3724
  /**
3671
3725
  * Defines metadata for a FASTElement which can be used to later define the element.
3672
- * IMPORTANT: This API will be renamed to "compose" in a future beta.
3673
3726
  * @public
3674
3727
  */
3675
- metadata(type, nameOrDef) {
3676
- return new FASTElementDefinition(type, nameOrDef);
3677
- },
3728
+ compose,
3678
3729
  });
3679
3730
  /**
3680
3731
  * Decorator: Defines a platform custom element based on `FASTElement`.
@@ -3685,8 +3736,8 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3685
3736
  function customElement(nameOrDef) {
3686
3737
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
3687
3738
  return function (type) {
3688
- FASTElement.define(type, nameOrDef);
3739
+ define(type, nameOrDef);
3689
3740
  };
3690
3741
  }
3691
3742
 
3692
- 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, SlottedDirective, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, UpdateBinding, Updates, ViewTemplate, attr, bind, booleanConverter, children, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, lengthOf, nullableNumberConverter, observable, onChange, oneTime, ref, repeat, slotted, 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 };