@microsoft/fast-element 2.0.0-beta.3 → 2.0.0-beta.6

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 (68) hide show
  1. package/CHANGELOG.json +147 -0
  2. package/CHANGELOG.md +42 -1
  3. package/dist/dts/components/fast-definitions.d.ts +9 -8
  4. package/dist/dts/components/fast-element.d.ts +12 -24
  5. package/dist/dts/context.d.ts +1 -1
  6. package/dist/dts/di/di.d.ts +858 -0
  7. package/dist/dts/interfaces.d.ts +43 -7
  8. package/dist/dts/observation/observable.d.ts +19 -13
  9. package/dist/dts/state/exports.d.ts +3 -0
  10. package/dist/dts/state/reactive.d.ts +8 -0
  11. package/dist/dts/state/state.d.ts +141 -0
  12. package/dist/dts/state/visitor.d.ts +6 -0
  13. package/dist/dts/state/watch.d.ts +10 -0
  14. package/dist/dts/styles/element-styles.d.ts +6 -0
  15. package/dist/dts/templating/binding-signal.d.ts +10 -27
  16. package/dist/dts/templating/binding-two-way.d.ts +16 -41
  17. package/dist/dts/templating/binding.d.ts +79 -118
  18. package/dist/dts/templating/html-directive.d.ts +28 -2
  19. package/dist/dts/templating/render.d.ts +277 -0
  20. package/dist/dts/templating/repeat.d.ts +12 -16
  21. package/dist/dts/templating/template.d.ts +3 -3
  22. package/dist/dts/templating/when.d.ts +3 -3
  23. package/dist/dts/testing/exports.d.ts +2 -0
  24. package/dist/dts/testing/fixture.d.ts +90 -0
  25. package/dist/dts/testing/timeout.d.ts +7 -0
  26. package/dist/dts/utilities.d.ts +0 -18
  27. package/dist/esm/components/fast-definitions.js +25 -27
  28. package/dist/esm/components/fast-element.js +20 -11
  29. package/dist/esm/context.js +5 -1
  30. package/dist/esm/debug.js +35 -4
  31. package/dist/esm/di/di.js +1351 -0
  32. package/dist/esm/interfaces.js +4 -0
  33. package/dist/esm/observation/arrays.js +303 -2
  34. package/dist/esm/observation/observable.js +11 -6
  35. package/dist/esm/platform.js +1 -1
  36. package/dist/esm/state/exports.js +3 -0
  37. package/dist/esm/state/reactive.js +34 -0
  38. package/dist/esm/state/state.js +148 -0
  39. package/dist/esm/state/visitor.js +28 -0
  40. package/dist/esm/state/watch.js +36 -0
  41. package/dist/esm/styles/element-styles.js +14 -0
  42. package/dist/esm/templating/binding-signal.js +56 -61
  43. package/dist/esm/templating/binding-two-way.js +51 -35
  44. package/dist/esm/templating/binding.js +137 -156
  45. package/dist/esm/templating/compiler.js +29 -7
  46. package/dist/esm/templating/html-directive.js +12 -1
  47. package/dist/esm/templating/render.js +392 -0
  48. package/dist/esm/templating/repeat.js +57 -40
  49. package/dist/esm/templating/template.js +8 -5
  50. package/dist/esm/templating/view.js +3 -1
  51. package/dist/esm/templating/when.js +5 -4
  52. package/dist/esm/testing/exports.js +2 -0
  53. package/dist/esm/testing/fixture.js +88 -0
  54. package/dist/esm/testing/timeout.js +24 -0
  55. package/dist/esm/utilities.js +0 -95
  56. package/dist/fast-element.api.json +2827 -2757
  57. package/dist/fast-element.d.ts +215 -229
  58. package/dist/fast-element.debug.js +650 -256
  59. package/dist/fast-element.debug.min.js +1 -1
  60. package/dist/fast-element.js +615 -252
  61. package/dist/fast-element.min.js +1 -1
  62. package/dist/fast-element.untrimmed.d.ts +223 -234
  63. package/docs/api-report.md +87 -90
  64. package/package.json +18 -9
  65. package/dist/dts/hooks.d.ts +0 -20
  66. package/dist/dts/observation/splice-strategies.d.ts +0 -13
  67. package/dist/esm/hooks.js +0 -32
  68. package/dist/esm/observation/splice-strategies.js +0 -400
@@ -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;
@@ -474,8 +474,13 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
474
474
  const previousWatcher = watcher;
475
475
  watcher = this.needsRefresh ? this : void 0;
476
476
  this.needsRefresh = this.isVolatileBinding;
477
- const result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
478
- watcher = previousWatcher;
477
+ let result;
478
+ try {
479
+ result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
480
+ }
481
+ finally {
482
+ watcher = previousWatcher;
483
+ }
479
484
  return result;
480
485
  }
481
486
  dispose() {
@@ -604,14 +609,14 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
604
609
  */
605
610
  getAccessors,
606
611
  /**
607
- * Creates a {@link BindingObserver} that can watch the
608
- * provided {@link Binding} for changes.
612
+ * Creates a {@link ExpressionNotifier} that can watch the
613
+ * provided {@link Expression} for changes.
609
614
  * @param binding - The binding to observe.
610
615
  * @param initialSubscriber - An initial subscriber to changes in the binding value.
611
616
  * @param isVolatileBinding - Indicates whether the binding's dependency list must be re-evaluated on every value evaluation.
612
617
  */
613
618
  binding(binding, initialSubscriber, isVolatileBinding = this.isVolatileBinding(binding)) {
614
- return new BindingObserverImplementation(binding, initialSubscriber, isVolatileBinding);
619
+ return new ExpressionNotifierImplementation(binding, initialSubscriber, isVolatileBinding);
615
620
  },
616
621
  /**
617
622
  * Determines whether a binding expression is volatile and needs to have its dependency list re-evaluated
@@ -845,10 +850,311 @@ const SpliceStrategySupport = Object.freeze({
845
850
  const reset = new Splice(0, emptyArray, 0);
846
851
  reset.reset = true;
847
852
  const resetSplices = [reset];
853
+ // Note: This function is *based* on the computation of the Levenshtein
854
+ // "edit" distance. The one change is that "updates" are treated as two
855
+ // edits - not one. With Array splices, an update is really a delete
856
+ // followed by an add. By retaining this, we optimize for "keeping" the
857
+ // maximum array items in the original array. For example:
858
+ //
859
+ // 'xxxx123' to '123yyyy'
860
+ //
861
+ // With 1-edit updates, the shortest path would be just to update all seven
862
+ // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
863
+ // leaves the substring '123' intact.
864
+ function calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd) {
865
+ // "Deletion" columns
866
+ const rowCount = oldEnd - oldStart + 1;
867
+ const columnCount = currentEnd - currentStart + 1;
868
+ const distances = new Array(rowCount);
869
+ let north;
870
+ let west;
871
+ // "Addition" rows. Initialize null column.
872
+ for (let i = 0; i < rowCount; ++i) {
873
+ distances[i] = new Array(columnCount);
874
+ distances[i][0] = i;
875
+ }
876
+ // Initialize null row
877
+ for (let j = 0; j < columnCount; ++j) {
878
+ distances[0][j] = j;
879
+ }
880
+ for (let i = 1; i < rowCount; ++i) {
881
+ for (let j = 1; j < columnCount; ++j) {
882
+ if (current[currentStart + j - 1] === old[oldStart + i - 1]) {
883
+ distances[i][j] = distances[i - 1][j - 1];
884
+ }
885
+ else {
886
+ north = distances[i - 1][j] + 1;
887
+ west = distances[i][j - 1] + 1;
888
+ distances[i][j] = north < west ? north : west;
889
+ }
890
+ }
891
+ }
892
+ return distances;
893
+ }
894
+ // This starts at the final weight, and walks "backward" by finding
895
+ // the minimum previous weight recursively until the origin of the weight
896
+ // matrix.
897
+ function spliceOperationsFromEditDistances(distances) {
898
+ let i = distances.length - 1;
899
+ let j = distances[0].length - 1;
900
+ let current = distances[i][j];
901
+ const edits = [];
902
+ while (i > 0 || j > 0) {
903
+ if (i === 0) {
904
+ edits.push(2 /* Edit.add */);
905
+ j--;
906
+ continue;
907
+ }
908
+ if (j === 0) {
909
+ edits.push(3 /* Edit.delete */);
910
+ i--;
911
+ continue;
912
+ }
913
+ const northWest = distances[i - 1][j - 1];
914
+ const west = distances[i - 1][j];
915
+ const north = distances[i][j - 1];
916
+ let min;
917
+ if (west < north) {
918
+ min = west < northWest ? west : northWest;
919
+ }
920
+ else {
921
+ min = north < northWest ? north : northWest;
922
+ }
923
+ if (min === northWest) {
924
+ if (northWest === current) {
925
+ edits.push(0 /* Edit.leave */);
926
+ }
927
+ else {
928
+ edits.push(1 /* Edit.update */);
929
+ current = northWest;
930
+ }
931
+ i--;
932
+ j--;
933
+ }
934
+ else if (min === west) {
935
+ edits.push(3 /* Edit.delete */);
936
+ i--;
937
+ current = west;
938
+ }
939
+ else {
940
+ edits.push(2 /* Edit.add */);
941
+ j--;
942
+ current = north;
943
+ }
944
+ }
945
+ return edits.reverse();
946
+ }
947
+ function sharedPrefix(current, old, searchLength) {
948
+ for (let i = 0; i < searchLength; ++i) {
949
+ if (current[i] !== old[i]) {
950
+ return i;
951
+ }
952
+ }
953
+ return searchLength;
954
+ }
955
+ function sharedSuffix(current, old, searchLength) {
956
+ let index1 = current.length;
957
+ let index2 = old.length;
958
+ let count = 0;
959
+ while (count < searchLength && current[--index1] === old[--index2]) {
960
+ count++;
961
+ }
962
+ return count;
963
+ }
964
+ function intersect(start1, end1, start2, end2) {
965
+ // Disjoint
966
+ if (end1 < start2 || end2 < start1) {
967
+ return -1;
968
+ }
969
+ // Adjacent
970
+ if (end1 === start2 || end2 === start1) {
971
+ return 0;
972
+ }
973
+ // Non-zero intersect, span1 first
974
+ if (start1 < start2) {
975
+ if (end1 < end2) {
976
+ return end1 - start2; // Overlap
977
+ }
978
+ return end2 - start2; // Contained
979
+ }
980
+ // Non-zero intersect, span2 first
981
+ if (end2 < end1) {
982
+ return end2 - start1; // Overlap
983
+ }
984
+ return end1 - start1; // Contained
985
+ }
986
+ /**
987
+ * @remarks
988
+ * Lacking individual splice mutation information, the minimal set of
989
+ * splices can be synthesized given the previous state and final state of an
990
+ * array. The basic approach is to calculate the edit distance matrix and
991
+ * choose the shortest path through it.
992
+ *
993
+ * Complexity: O(l * p)
994
+ * l: The length of the current array
995
+ * p: The length of the old array
996
+ */
997
+ function calc(current, currentStart, currentEnd, old, oldStart, oldEnd) {
998
+ let prefixCount = 0;
999
+ let suffixCount = 0;
1000
+ const minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
1001
+ if (currentStart === 0 && oldStart === 0) {
1002
+ prefixCount = sharedPrefix(current, old, minLength);
1003
+ }
1004
+ if (currentEnd === current.length && oldEnd === old.length) {
1005
+ suffixCount = sharedSuffix(current, old, minLength - prefixCount);
1006
+ }
1007
+ currentStart += prefixCount;
1008
+ oldStart += prefixCount;
1009
+ currentEnd -= suffixCount;
1010
+ oldEnd -= suffixCount;
1011
+ if (currentEnd - currentStart === 0 && oldEnd - oldStart === 0) {
1012
+ return emptyArray;
1013
+ }
1014
+ if (currentStart === currentEnd) {
1015
+ const splice = new Splice(currentStart, [], 0);
1016
+ while (oldStart < oldEnd) {
1017
+ splice.removed.push(old[oldStart++]);
1018
+ }
1019
+ return [splice];
1020
+ }
1021
+ else if (oldStart === oldEnd) {
1022
+ return [new Splice(currentStart, [], currentEnd - currentStart)];
1023
+ }
1024
+ const ops = spliceOperationsFromEditDistances(calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
1025
+ const splices = [];
1026
+ let splice = void 0;
1027
+ let index = currentStart;
1028
+ let oldIndex = oldStart;
1029
+ for (let i = 0; i < ops.length; ++i) {
1030
+ switch (ops[i]) {
1031
+ case 0 /* Edit.leave */:
1032
+ if (splice !== void 0) {
1033
+ splices.push(splice);
1034
+ splice = void 0;
1035
+ }
1036
+ index++;
1037
+ oldIndex++;
1038
+ break;
1039
+ case 1 /* Edit.update */:
1040
+ if (splice === void 0) {
1041
+ splice = new Splice(index, [], 0);
1042
+ }
1043
+ splice.addedCount++;
1044
+ index++;
1045
+ splice.removed.push(old[oldIndex]);
1046
+ oldIndex++;
1047
+ break;
1048
+ case 2 /* Edit.add */:
1049
+ if (splice === void 0) {
1050
+ splice = new Splice(index, [], 0);
1051
+ }
1052
+ splice.addedCount++;
1053
+ index++;
1054
+ break;
1055
+ case 3 /* Edit.delete */:
1056
+ if (splice === void 0) {
1057
+ splice = new Splice(index, [], 0);
1058
+ }
1059
+ splice.removed.push(old[oldIndex]);
1060
+ oldIndex++;
1061
+ break;
1062
+ // no default
1063
+ }
1064
+ }
1065
+ if (splice !== void 0) {
1066
+ splices.push(splice);
1067
+ }
1068
+ return splices;
1069
+ }
1070
+ function merge(splice, splices) {
1071
+ let inserted = false;
1072
+ let insertionOffset = 0;
1073
+ for (let i = 0; i < splices.length; i++) {
1074
+ const current = splices[i];
1075
+ current.index += insertionOffset;
1076
+ if (inserted) {
1077
+ continue;
1078
+ }
1079
+ const intersectCount = intersect(splice.index, splice.index + splice.removed.length, current.index, current.index + current.addedCount);
1080
+ if (intersectCount >= 0) {
1081
+ // Merge the two splices
1082
+ splices.splice(i, 1);
1083
+ i--;
1084
+ insertionOffset -= current.addedCount - current.removed.length;
1085
+ splice.addedCount += current.addedCount - intersectCount;
1086
+ const deleteCount = splice.removed.length + current.removed.length - intersectCount;
1087
+ if (!splice.addedCount && !deleteCount) {
1088
+ // merged splice is a noop. discard.
1089
+ inserted = true;
1090
+ }
1091
+ else {
1092
+ let currentRemoved = current.removed;
1093
+ if (splice.index < current.index) {
1094
+ // some prefix of splice.removed is prepended to current.removed.
1095
+ const prepend = splice.removed.slice(0, current.index - splice.index);
1096
+ prepend.push(...currentRemoved);
1097
+ currentRemoved = prepend;
1098
+ }
1099
+ if (splice.index + splice.removed.length >
1100
+ current.index + current.addedCount) {
1101
+ // some suffix of splice.removed is appended to current.removed.
1102
+ const append = splice.removed.slice(current.index + current.addedCount - splice.index);
1103
+ currentRemoved.push(...append);
1104
+ }
1105
+ splice.removed = currentRemoved;
1106
+ if (current.index < splice.index) {
1107
+ splice.index = current.index;
1108
+ }
1109
+ }
1110
+ }
1111
+ else if (splice.index < current.index) {
1112
+ // Insert splice here.
1113
+ inserted = true;
1114
+ splices.splice(i, 0, splice);
1115
+ i++;
1116
+ const offset = splice.addedCount - splice.removed.length;
1117
+ current.index += offset;
1118
+ insertionOffset += offset;
1119
+ }
1120
+ }
1121
+ if (!inserted) {
1122
+ splices.push(splice);
1123
+ }
1124
+ }
1125
+ function project(array, changes) {
1126
+ let splices = [];
1127
+ const initialSplices = [];
1128
+ for (let i = 0, ii = changes.length; i < ii; i++) {
1129
+ merge(changes[i], initialSplices);
1130
+ }
1131
+ for (let i = 0, ii = initialSplices.length; i < ii; ++i) {
1132
+ const splice = initialSplices[i];
1133
+ if (splice.addedCount === 1 && splice.removed.length === 1) {
1134
+ if (splice.removed[0] !== array[splice.index]) {
1135
+ splices.push(splice);
1136
+ }
1137
+ continue;
1138
+ }
1139
+ splices = splices.concat(calc(array, splice.index, splice.index + splice.addedCount, splice.removed, 0, splice.removed.length));
1140
+ }
1141
+ return splices;
1142
+ }
1143
+ /**
1144
+ * A SpliceStrategy that attempts to merge all splices into the minimal set of
1145
+ * splices needed to represent the change from the old array to the new array.
1146
+ * @public
1147
+ */
848
1148
  let defaultSpliceStrategy = Object.freeze({
849
- support: SpliceStrategySupport.splice,
1149
+ support: SpliceStrategySupport.optimized,
850
1150
  normalize(previous, current, changes) {
851
- return previous === void 0 ? changes !== null && changes !== void 0 ? changes : emptyArray : resetSplices;
1151
+ if (previous === void 0) {
1152
+ if (changes === void 0) {
1153
+ return emptyArray;
1154
+ }
1155
+ return changes.length > 1 ? project(current, changes) : changes;
1156
+ }
1157
+ return resetSplices;
852
1158
  },
853
1159
  pop(array, observer, pop, args) {
854
1160
  const notEmpty = array.length > 0;
@@ -1117,6 +1423,20 @@ class ElementStyles {
1117
1423
  static setDefaultStrategy(Strategy) {
1118
1424
  DefaultStyleStrategy = Strategy;
1119
1425
  }
1426
+ /**
1427
+ * Normalizes a set of composable style options.
1428
+ * @param styles - The style options to normalize.
1429
+ * @returns A singular ElementStyles instance or undefined.
1430
+ */
1431
+ static normalize(styles) {
1432
+ return styles === void 0
1433
+ ? void 0
1434
+ : Array.isArray(styles)
1435
+ ? new ElementStyles(styles)
1436
+ : styles instanceof ElementStyles
1437
+ ? styles
1438
+ : new ElementStyles([styles]);
1439
+ }
1120
1440
  }
1121
1441
  /**
1122
1442
  * Indicates whether the DOM supports the adoptedStyleSheets feature.
@@ -1443,6 +1763,13 @@ function htmlDirective(options) {
1443
1763
  HTMLDirective.define(type, options);
1444
1764
  };
1445
1765
  }
1766
+ /**
1767
+ * Captures a binding expression along with related information and capabilities.
1768
+ *
1769
+ * @public
1770
+ */
1771
+ class Binding {
1772
+ }
1446
1773
  /**
1447
1774
  * The type of HTML aspect to target.
1448
1775
  * @public
@@ -1536,6 +1863,10 @@ class StatelessAttachedAttributeDirective {
1536
1863
  */
1537
1864
  constructor(options) {
1538
1865
  this.options = options;
1866
+ /**
1867
+ * The unique id of the factory.
1868
+ */
1869
+ this.id = nextId();
1539
1870
  }
1540
1871
  /**
1541
1872
  * Creates a behavior.
@@ -1564,99 +1895,28 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
1564
1895
  throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
1565
1896
  }
1566
1897
  : (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;
1898
+ class OnChangeBinding extends Binding {
1899
+ constructor(evaluate, isVolatile) {
1900
+ super();
1901
+ this.evaluate = evaluate;
1902
+ this.isVolatile = isVolatile;
1625
1903
  }
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;
1904
+ createObserver(_, subscriber) {
1905
+ return Observable.binding(this.evaluate, subscriber, this.isVolatile);
1646
1906
  }
1647
1907
  }
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
- };
1908
+ class OneTimeBinding extends Binding {
1909
+ constructor(evaluate) {
1910
+ super();
1911
+ this.evaluate = evaluate;
1912
+ }
1913
+ createObserver() {
1914
+ return this;
1915
+ }
1916
+ observe(source, context) {
1917
+ return this.evaluate(source, context);
1918
+ }
1919
+ dispose() { }
1660
1920
  }
1661
1921
  function updateContentTarget(target, aspect, value, source, context) {
1662
1922
  // If there's no actual value, then this equates to the
@@ -1664,7 +1924,7 @@ function updateContentTarget(target, aspect, value, source, context) {
1664
1924
  if (value === null || value === undefined) {
1665
1925
  value = "";
1666
1926
  }
1667
- // If the value has a "create" method, then it's a template-like.
1927
+ // If the value has a "create" method, then it's a ContentTemplate.
1668
1928
  if (value.create) {
1669
1929
  target.textContent = "";
1670
1930
  let view = target.$fastView;
@@ -1750,46 +2010,21 @@ function updateTokenListTarget(target, aspect, value) {
1750
2010
  }
1751
2011
  }
1752
2012
  }
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
2013
  /**
1770
2014
  * A binding behavior for bindings that change.
1771
2015
  * @public
1772
2016
  */
1773
- class ChangeBinding extends UpdateBinding {
2017
+ class BindingBehavior {
1774
2018
  /**
1775
2019
  * Creates an instance of ChangeBinding.
1776
2020
  * @param directive - The directive that has the configuration for this behavior.
1777
2021
  * @param updateTarget - The function used to update the target with the latest value.
1778
2022
  */
1779
2023
  constructor(directive, updateTarget) {
1780
- super(directive, updateTarget);
1781
- this.isBindingVolatile = Observable.isVolatileBinding(directive.binding);
2024
+ this.directive = directive;
2025
+ this.updateTarget = updateTarget;
1782
2026
  this.observerProperty = `${directive.id}-o`;
1783
2027
  }
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
2028
  /**
1794
2029
  * Bind this behavior to the source.
1795
2030
  * @param source - The source to bind to.
@@ -1826,12 +2061,49 @@ class ChangeBinding extends UpdateBinding {
1826
2061
  const context = observer.context;
1827
2062
  this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
1828
2063
  }
2064
+ /**
2065
+ * Returns the binding observer used to update the node.
2066
+ * @param target - The target node.
2067
+ * @returns A BindingObserver.
2068
+ */
2069
+ getObserver(target) {
2070
+ var _a;
2071
+ return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = this.directive.dataBinding.createObserver(this.directive, this)));
2072
+ }
2073
+ /**
2074
+ * Creates a behavior.
2075
+ * @param targets - The targets available for behaviors to be attached to.
2076
+ */
2077
+ createBehavior(targets) {
2078
+ return this;
2079
+ }
2080
+ }
2081
+ /**
2082
+ * A special binding behavior that can bind node content.
2083
+ * @public
2084
+ */
2085
+ class ContentBehavior extends BindingBehavior {
2086
+ /**
2087
+ * Unbinds this behavior from the source.
2088
+ * @param source - The source to unbind from.
2089
+ * @param context - The execution context that the binding is operating within.
2090
+ * @param targets - The targets that behaviors in a view can attach to.
2091
+ */
2092
+ unbind(source, context, targets) {
2093
+ super.unbind(source, context, targets);
2094
+ const target = targets[this.directive.nodeId];
2095
+ const view = target.$fastView;
2096
+ if (view !== void 0 && view.isComposed) {
2097
+ view.unbind();
2098
+ view.needsBindOnly = true;
2099
+ }
2100
+ }
1829
2101
  }
1830
2102
  /**
1831
2103
  * A binding behavior for handling events.
1832
2104
  * @public
1833
2105
  */
1834
- class EventBinding {
2106
+ class EventBehavior {
1835
2107
  /**
1836
2108
  * Creates an instance of EventBinding.
1837
2109
  * @param directive - The directive that has the configuration for this behavior.
@@ -1852,7 +2124,7 @@ class EventBinding {
1852
2124
  const target = targets[directive.nodeId];
1853
2125
  target[this.sourceProperty] = source;
1854
2126
  target[this.contextProperty] = context;
1855
- target.addEventListener(directive.targetAspect, this, directive.options);
2127
+ target.addEventListener(directive.targetAspect, this, directive.dataBinding.options);
1856
2128
  }
1857
2129
  /**
1858
2130
  * Unbinds this behavior from the source.
@@ -1864,7 +2136,7 @@ class EventBinding {
1864
2136
  const directive = this.directive;
1865
2137
  const target = targets[directive.nodeId];
1866
2138
  target[this.sourceProperty] = target[this.contextProperty] = null;
1867
- target.removeEventListener(directive.targetAspect, this, directive.options);
2139
+ target.removeEventListener(directive.targetAspect, this, directive.dataBinding.options);
1868
2140
  }
1869
2141
  /**
1870
2142
  * Creates a behavior.
@@ -1879,25 +2151,13 @@ class EventBinding {
1879
2151
  handleEvent(event) {
1880
2152
  const target = event.currentTarget;
1881
2153
  ExecutionContext.setEvent(event);
1882
- const result = this.directive.binding(target[this.sourceProperty], target[this.contextProperty]);
2154
+ const result = this.directive.dataBinding.evaluate(target[this.sourceProperty], target[this.contextProperty]);
1883
2155
  ExecutionContext.setEvent(null);
1884
2156
  if (result !== true) {
1885
2157
  event.preventDefault();
1886
2158
  }
1887
2159
  }
1888
2160
  }
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
2161
  /**
1902
2162
  * A directive that applies bindings.
1903
2163
  * @public
@@ -1905,15 +2165,15 @@ const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
1905
2165
  class HTMLBindingDirective {
1906
2166
  /**
1907
2167
  * 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.
2168
+ * @param dataBinding - The binding configuration to apply.
1911
2169
  */
1912
- constructor(binding, mode, options) {
1913
- this.binding = binding;
1914
- this.mode = mode;
1915
- this.options = options;
2170
+ constructor(dataBinding) {
2171
+ this.dataBinding = dataBinding;
1916
2172
  this.factory = null;
2173
+ /**
2174
+ * The unique id of the factory.
2175
+ */
2176
+ this.id = nextId();
1917
2177
  /**
1918
2178
  * The type of aspect to target.
1919
2179
  */
@@ -1933,26 +2193,78 @@ class HTMLBindingDirective {
1933
2193
  createBehavior(targets) {
1934
2194
  if (this.factory == null) {
1935
2195
  if (this.targetAspect === "innerHTML") {
1936
- this.binding = createInnerHTMLBinding(this.binding);
2196
+ this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
2197
+ }
2198
+ switch (this.aspectType) {
2199
+ case 1:
2200
+ this.factory = new BindingBehavior(this, DOM.setAttribute);
2201
+ break;
2202
+ case 2:
2203
+ this.factory = new BindingBehavior(this, DOM.setBooleanAttribute);
2204
+ break;
2205
+ case 3:
2206
+ this.factory = new BindingBehavior(this, (t, a, v) => (t[a] = v));
2207
+ break;
2208
+ case 4:
2209
+ this.factory = new ContentBehavior(this, updateContentTarget);
2210
+ break;
2211
+ case 5:
2212
+ this.factory = new BindingBehavior(this, updateTokenListTarget);
2213
+ break;
2214
+ case 6:
2215
+ this.factory = new EventBehavior(this);
2216
+ break;
2217
+ default:
2218
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
1937
2219
  }
1938
- this.factory = this.mode[this.aspectType](this);
1939
2220
  }
1940
2221
  return this.factory.createBehavior(targets);
1941
2222
  }
1942
2223
  }
1943
2224
  HTMLDirective.define(HTMLBindingDirective, { aspected: true });
1944
2225
  /**
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.
2226
+ * Creates an standard binding.
2227
+ * @param binding - The binding to refresh when changed.
2228
+ * @param isVolatile - Indicates whether the binding is volatile or not.
2229
+ * @returns A binding configuration.
1949
2230
  * @public
1950
2231
  */
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);
2232
+ function bind(binding, isVolatile = Observable.isVolatileBinding(binding)) {
2233
+ return new OnChangeBinding(binding, isVolatile);
2234
+ }
2235
+ /**
2236
+ * Creates a one time binding
2237
+ * @param binding - The binding to refresh when signaled.
2238
+ * @returns A binding configuration.
2239
+ * @public
2240
+ */
2241
+ function oneTime(binding) {
2242
+ return new OneTimeBinding(binding);
2243
+ }
2244
+ /**
2245
+ * Creates an event listener binding.
2246
+ * @param binding - The binding to invoke when the event is raised.
2247
+ * @param options - Event listener options.
2248
+ * @returns A binding configuration.
2249
+ * @public
2250
+ */
2251
+ function listener(binding, options) {
2252
+ const config = new OnChangeBinding(binding, false);
2253
+ config.options = options;
2254
+ return config;
2255
+ }
2256
+ /**
2257
+ * Normalizes the input value into a binding.
2258
+ * @param value - The value to create the default binding for.
2259
+ * @returns A binding configuration for the provided value.
2260
+ * @public
2261
+ */
2262
+ function normalizeBinding(value) {
2263
+ return isFunction(value)
2264
+ ? bind(value)
2265
+ : value instanceof Binding
2266
+ ? value
2267
+ : oneTime(() => value);
1956
2268
  }
1957
2269
 
1958
2270
  function removeNodeSequence(firstNode, lastNode) {
@@ -2008,8 +2320,10 @@ class HTMLView {
2008
2320
  node.parentNode.insertBefore(this.fragment, node);
2009
2321
  }
2010
2322
  else {
2011
- const parentNode = node.parentNode;
2012
2323
  const end = this.lastChild;
2324
+ if (node.previousSibling === end)
2325
+ return;
2326
+ const parentNode = node.parentNode;
2013
2327
  let current = this.firstChild;
2014
2328
  let next;
2015
2329
  while (current !== end) {
@@ -2119,6 +2433,22 @@ const next = {
2119
2433
  index: 0,
2120
2434
  node: null,
2121
2435
  };
2436
+ function tryWarn(name) {
2437
+ if (!name.startsWith("fast-")) {
2438
+ FAST.warn(1204 /* Message.hostBindingWithoutHost */, { name });
2439
+ }
2440
+ }
2441
+ const warningHost = new Proxy(document.createElement("div"), {
2442
+ get(target, property) {
2443
+ tryWarn(property);
2444
+ const value = Reflect.get(target, property);
2445
+ return isFunction(value) ? value.bind(target) : value;
2446
+ },
2447
+ set(target, property, value) {
2448
+ tryWarn(property);
2449
+ return Reflect.set(target, property, value);
2450
+ },
2451
+ });
2122
2452
  class CompilationContext {
2123
2453
  constructor(fragment, directives) {
2124
2454
  this.fragment = fragment;
@@ -2169,7 +2499,7 @@ class CompilationContext {
2169
2499
  const fragment = this.fragment.cloneNode(true);
2170
2500
  const targets = Object.create(this.proto);
2171
2501
  targets.r = fragment;
2172
- targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : fragment;
2502
+ targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
2173
2503
  for (const id of this.nodeIds) {
2174
2504
  targets[id]; // trigger locator
2175
2505
  }
@@ -2186,7 +2516,7 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2186
2516
  let result = null;
2187
2517
  if (parseResult === null) {
2188
2518
  if (includeBasicValues) {
2189
- result = bind(() => attrValue, oneTime);
2519
+ result = new HTMLBindingDirective(oneTime(() => attrValue));
2190
2520
  Aspect.assign(result, attr.name);
2191
2521
  }
2192
2522
  }
@@ -2356,22 +2686,28 @@ const Compiler = {
2356
2686
  return parts[0];
2357
2687
  }
2358
2688
  let sourceAspect;
2689
+ let binding;
2690
+ let isVolatile = false;
2359
2691
  const partCount = parts.length;
2360
2692
  const finalParts = parts.map((x) => {
2361
2693
  if (isString(x)) {
2362
2694
  return () => x;
2363
2695
  }
2364
2696
  sourceAspect = x.sourceAspect || sourceAspect;
2365
- return x.binding;
2697
+ binding = x.dataBinding || binding;
2698
+ isVolatile = isVolatile || x.dataBinding.isVolatile;
2699
+ return x.dataBinding.evaluate;
2366
2700
  });
2367
- const binding = (scope, context) => {
2701
+ const expression = (scope, context) => {
2368
2702
  let output = "";
2369
2703
  for (let i = 0; i < partCount; ++i) {
2370
2704
  output += finalParts[i](scope, context);
2371
2705
  }
2372
2706
  return output;
2373
2707
  };
2374
- const directive = bind(binding);
2708
+ binding.evaluate = expression;
2709
+ binding.isVolatile = isVolatile;
2710
+ const directive = new HTMLBindingDirective(binding);
2375
2711
  Aspect.assign(directive, sourceAspect);
2376
2712
  return directive;
2377
2713
  },
@@ -2451,12 +2787,12 @@ function html(strings, ...values) {
2451
2787
  let definition;
2452
2788
  html += currentString;
2453
2789
  if (isFunction(currentValue)) {
2454
- html += createAspectedHTML(bind(currentValue), currentString, add);
2790
+ html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
2455
2791
  }
2456
2792
  else if (isString(currentValue)) {
2457
2793
  const match = lastAttributeNameRegex.exec(currentString);
2458
2794
  if (match !== null) {
2459
- const directive = bind(() => currentValue, oneTime);
2795
+ const directive = new HTMLBindingDirective(oneTime(() => currentValue));
2460
2796
  Aspect.assign(directive, match[2]);
2461
2797
  html += directive.createHTML(add);
2462
2798
  }
@@ -2464,8 +2800,11 @@ function html(strings, ...values) {
2464
2800
  html += currentValue;
2465
2801
  }
2466
2802
  }
2803
+ else if (currentValue instanceof Binding) {
2804
+ html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
2805
+ }
2467
2806
  else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
2468
- html += createAspectedHTML(bind(() => currentValue, oneTime), currentString, add);
2807
+ html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
2469
2808
  }
2470
2809
  else {
2471
2810
  if (definition.aspected) {
@@ -2510,16 +2849,17 @@ const ref = (propertyName) => new RefDirective(propertyName);
2510
2849
 
2511
2850
  /**
2512
2851
  * A directive that enables basic conditional rendering in a template.
2513
- * @param binding - The condition to test for rendering.
2852
+ * @param condition - The condition to test for rendering.
2514
2853
  * @param templateOrTemplateBinding - The template or a binding that gets
2515
2854
  * the template to render when the condition is true.
2516
2855
  * @public
2517
2856
  */
2518
- function when(binding, templateOrTemplateBinding) {
2519
- const getTemplate = isFunction(templateOrTemplateBinding)
2857
+ function when(condition, templateOrTemplateBinding) {
2858
+ const dataBinding = isFunction(condition) ? condition : () => condition;
2859
+ const templateBinding = isFunction(templateOrTemplateBinding)
2520
2860
  ? templateOrTemplateBinding
2521
2861
  : () => templateOrTemplateBinding;
2522
- return (source, context) => binding(source, context) ? getTemplate(source, context) : null;
2862
+ return (source, context) => dataBinding(source, context) ? templateBinding(source, context) : null;
2523
2863
  }
2524
2864
 
2525
2865
  const defaultRepeatOptions = Object.freeze({
@@ -2540,17 +2880,15 @@ class RepeatBehavior {
2540
2880
  /**
2541
2881
  * Creates an instance of RepeatBehavior.
2542
2882
  * @param location - The location in the DOM to render the repeat.
2543
- * @param itemsBinding - The array to render.
2883
+ * @param dataBinding - The array to render.
2544
2884
  * @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
2545
2885
  * @param templateBinding - The template to render for each item.
2546
2886
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
2547
2887
  * @param options - Options used to turn on special repeat features.
2548
2888
  */
2549
- constructor(location, itemsBinding, isItemsBindingVolatile, templateBinding, isTemplateBindingVolatile, options) {
2889
+ constructor(directive, location) {
2890
+ this.directive = directive;
2550
2891
  this.location = location;
2551
- this.itemsBinding = itemsBinding;
2552
- this.templateBinding = templateBinding;
2553
- this.options = options;
2554
2892
  this.source = null;
2555
2893
  this.views = [];
2556
2894
  this.items = null;
@@ -2558,9 +2896,9 @@ class RepeatBehavior {
2558
2896
  this.context = void 0;
2559
2897
  this.childContext = void 0;
2560
2898
  this.bindView = bindWithoutPositioning;
2561
- this.itemsBindingObserver = Observable.binding(itemsBinding, this, isItemsBindingVolatile);
2562
- this.templateBindingObserver = Observable.binding(templateBinding, this, isTemplateBindingVolatile);
2563
- if (options.positioning) {
2899
+ this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
2900
+ this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
2901
+ if (directive.options.positioning) {
2564
2902
  this.bindView = bindWithPositioning;
2565
2903
  }
2566
2904
  }
@@ -2598,15 +2936,18 @@ class RepeatBehavior {
2598
2936
  * @param args - The details about what was changed.
2599
2937
  */
2600
2938
  handleChange(source, args) {
2601
- if (source === this.itemsBinding) {
2939
+ if (args === this.itemsBindingObserver) {
2602
2940
  this.items = this.itemsBindingObserver.observe(this.source, this.context);
2603
2941
  this.observeItems();
2604
2942
  this.refreshAllViews();
2605
2943
  }
2606
- else if (source === this.templateBinding) {
2944
+ else if (args === this.templateBindingObserver) {
2607
2945
  this.template = this.templateBindingObserver.observe(this.source, this.context);
2608
2946
  this.refreshAllViews(true);
2609
2947
  }
2948
+ else if (!args[0]) {
2949
+ return;
2950
+ }
2610
2951
  else if (args[0].reset) {
2611
2952
  this.refreshAllViews();
2612
2953
  }
@@ -2632,36 +2973,51 @@ class RepeatBehavior {
2632
2973
  updateViews(splices) {
2633
2974
  const views = this.views;
2634
2975
  const childContext = this.childContext;
2635
- const totalRemoved = [];
2636
2976
  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
2977
  const items = this.items;
2645
2978
  const template = this.template;
2979
+ const recycle = this.directive.options.recycle;
2980
+ const leftoverViews = [];
2981
+ let leftoverIndex = 0;
2982
+ let availableViews = 0;
2646
2983
  for (let i = 0, ii = splices.length; i < ii; ++i) {
2647
2984
  const splice = splices[i];
2985
+ const removed = splice.removed;
2986
+ let removeIndex = 0;
2648
2987
  let addIndex = splice.index;
2649
2988
  const end = addIndex + splice.addedCount;
2989
+ const removedViews = views.splice(splice.index, removed.length);
2990
+ availableViews = leftoverViews.length + removedViews.length;
2650
2991
  for (; addIndex < end; ++addIndex) {
2651
2992
  const neighbor = views[addIndex];
2652
2993
  const location = neighbor ? neighbor.firstChild : this.location;
2653
- const view = this.options.recycle && totalRemoved.length > 0
2654
- ? totalRemoved.shift()
2655
- : template.create();
2994
+ let view;
2995
+ if (recycle && availableViews > 0) {
2996
+ if (removeIndex <= availableViews && removedViews.length > 0) {
2997
+ view = removedViews[removeIndex];
2998
+ removeIndex++;
2999
+ }
3000
+ else {
3001
+ view = leftoverViews[leftoverIndex];
3002
+ leftoverIndex++;
3003
+ }
3004
+ availableViews--;
3005
+ }
3006
+ else {
3007
+ view = template.create();
3008
+ }
2656
3009
  views.splice(addIndex, 0, view);
2657
3010
  bindView(view, items, addIndex, childContext);
2658
3011
  view.insertBefore(location);
2659
3012
  }
3013
+ if (removedViews[removeIndex]) {
3014
+ leftoverViews.push(...removedViews.slice(removeIndex));
3015
+ }
2660
3016
  }
2661
- for (let i = 0, ii = totalRemoved.length; i < ii; ++i) {
2662
- totalRemoved[i].dispose();
3017
+ for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
3018
+ leftoverViews[i].dispose();
2663
3019
  }
2664
- if (this.options.positioning) {
3020
+ if (this.directive.options.positioning) {
2665
3021
  for (let i = 0, ii = views.length; i < ii; ++i) {
2666
3022
  views[i].context.updatePosition(i, ii);
2667
3023
  }
@@ -2676,7 +3032,7 @@ class RepeatBehavior {
2676
3032
  let itemsLength = items.length;
2677
3033
  let views = this.views;
2678
3034
  let viewsLength = views.length;
2679
- if (itemsLength === 0 || templateChanged || !this.options.recycle) {
3035
+ if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
2680
3036
  // all views need to be removed
2681
3037
  HTMLView.disposeContiguousBatch(views);
2682
3038
  viewsLength = 0;
@@ -2726,17 +3082,19 @@ class RepeatBehavior {
2726
3082
  class RepeatDirective {
2727
3083
  /**
2728
3084
  * Creates an instance of RepeatDirective.
2729
- * @param itemsBinding - The binding that provides the array to render.
3085
+ * @param dataBinding - The binding that provides the array to render.
2730
3086
  * @param templateBinding - The template binding used to obtain a template to render for each item in the array.
2731
3087
  * @param options - Options used to turn on special repeat features.
2732
3088
  */
2733
- constructor(itemsBinding, templateBinding, options) {
2734
- this.itemsBinding = itemsBinding;
3089
+ constructor(dataBinding, templateBinding, options) {
3090
+ this.dataBinding = dataBinding;
2735
3091
  this.templateBinding = templateBinding;
2736
3092
  this.options = options;
3093
+ /**
3094
+ * The unique id of the factory.
3095
+ */
3096
+ this.id = nextId();
2737
3097
  ArrayObserver.enable();
2738
- this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
2739
- this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
2740
3098
  }
2741
3099
  /**
2742
3100
  * Creates a placeholder string based on the directive's index within the template.
@@ -2750,23 +3108,22 @@ class RepeatDirective {
2750
3108
  * @param target - The node instance to create the behavior for.
2751
3109
  */
2752
3110
  createBehavior(targets) {
2753
- return new RepeatBehavior(targets[this.nodeId], this.itemsBinding, this.isItemsBindingVolatile, this.templateBinding, this.isTemplateBindingVolatile, this.options);
3111
+ return new RepeatBehavior(this, targets[this.nodeId]);
2754
3112
  }
2755
3113
  }
2756
3114
  HTMLDirective.define(RepeatDirective);
2757
3115
  /**
2758
3116
  * 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
3117
+ * @param items - The array to render.
3118
+ * @param template - The template or a template binding used obtain a template
2761
3119
  * to render for each item in the array.
2762
3120
  * @param options - Options used to turn on special repeat features.
2763
3121
  * @public
2764
3122
  */
2765
- function repeat(itemsBinding, templateOrTemplateBinding, options = defaultRepeatOptions) {
2766
- const templateBinding = isFunction(templateOrTemplateBinding)
2767
- ? templateOrTemplateBinding
2768
- : () => templateOrTemplateBinding;
2769
- return new RepeatDirective(itemsBinding, templateBinding, options);
3123
+ function repeat(items, template, options = defaultRepeatOptions) {
3124
+ const dataBinding = normalizeBinding(items);
3125
+ const templateBinding = normalizeBinding(template);
3126
+ return new RepeatDirective(dataBinding, templateBinding, Object.assign(Object.assign({}, defaultRepeatOptions), options));
2770
3127
  }
2771
3128
 
2772
3129
  const selectElements = (value) => value.nodeType === 1;
@@ -3151,19 +3508,15 @@ const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */
3151
3508
  * @public
3152
3509
  */
3153
3510
  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
3511
  constructor(type, nameOrConfig = type.definition) {
3512
+ this.platformDefined = false;
3161
3513
  if (isString(nameOrConfig)) {
3162
3514
  nameOrConfig = { name: nameOrConfig };
3163
3515
  }
3164
3516
  this.type = type;
3165
3517
  this.name = nameOrConfig.name;
3166
3518
  this.template = nameOrConfig.template;
3519
+ const proto = type.prototype;
3167
3520
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3168
3521
  const observedAttributes = new Array(attributes.length);
3169
3522
  const propertyLookup = {};
@@ -3173,9 +3526,13 @@ class FASTElementDefinition {
3173
3526
  observedAttributes[i] = current.attribute;
3174
3527
  propertyLookup[current.name] = current;
3175
3528
  attributeLookup[current.attribute] = current;
3529
+ Observable.defineProperty(proto, current);
3176
3530
  }
3531
+ Reflect.defineProperty(type, "observedAttributes", {
3532
+ value: observedAttributes,
3533
+ enumerable: true,
3534
+ });
3177
3535
  this.attributes = attributes;
3178
- this.observedAttributes = observedAttributes;
3179
3536
  this.propertyLookup = propertyLookup;
3180
3537
  this.attributeLookup = attributeLookup;
3181
3538
  this.shadowOptions =
@@ -3188,20 +3545,14 @@ class FASTElementDefinition {
3188
3545
  nameOrConfig.elementOptions === void 0
3189
3546
  ? defaultElementOptions
3190
3547
  : 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]);
3548
+ this.styles = ElementStyles.normalize(nameOrConfig.styles);
3549
+ fastElementRegistry.register(this);
3199
3550
  }
3200
3551
  /**
3201
3552
  * Indicates if this element has been defined in at least one registry.
3202
3553
  */
3203
3554
  get isDefined() {
3204
- return !!fastElementRegistry.getByType(this.type);
3555
+ return this.platformDefined;
3205
3556
  }
3206
3557
  /**
3207
3558
  * Defines a custom element based on this definition.
@@ -3211,22 +3562,26 @@ class FASTElementDefinition {
3211
3562
  */
3212
3563
  define(registry = customElements) {
3213
3564
  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
3565
  if (!registry.get(this.name)) {
3566
+ this.platformDefined = true;
3226
3567
  registry.define(this.name, type, this.elementOptions);
3227
3568
  }
3228
3569
  return this;
3229
3570
  }
3571
+ /**
3572
+ * Creates an instance of FASTElementDefinition.
3573
+ * @param type - The type this definition is being created for.
3574
+ * @param nameOrDef - The name of the element to define or a config object
3575
+ * that describes the element to define.
3576
+ */
3577
+ static compose(type, nameOrDef) {
3578
+ const found = fastElementRegistry.getByType(type);
3579
+ if (found) {
3580
+ return new FASTElementDefinition(class extends type {
3581
+ }, nameOrDef);
3582
+ }
3583
+ return new FASTElementDefinition(type, nameOrDef);
3584
+ }
3230
3585
  }
3231
3586
  /**
3232
3587
  * Gets the element definition associated with the specified type.
@@ -3644,6 +3999,21 @@ function createFASTElement(BaseType) {
3644
3999
  }
3645
4000
  };
3646
4001
  }
4002
+ function compose(type, nameOrDef) {
4003
+ if (isFunction(type)) {
4004
+ return FASTElementDefinition.compose(type, nameOrDef);
4005
+ }
4006
+ return FASTElementDefinition.compose(this, type);
4007
+ }
4008
+ function define(type, nameOrDef) {
4009
+ if (isFunction(type)) {
4010
+ return FASTElementDefinition.compose(type, nameOrDef).define().type;
4011
+ }
4012
+ return FASTElementDefinition.compose(this, type).define().type;
4013
+ }
4014
+ function from(BaseType) {
4015
+ return createFASTElement(BaseType);
4016
+ }
3647
4017
  /**
3648
4018
  * A minimal base class for FASTElements that also provides
3649
4019
  * static helpers for working with FASTElements.
@@ -3655,26 +4025,19 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3655
4025
  * provided base type.
3656
4026
  * @param BaseType - The base element type to inherit from.
3657
4027
  */
3658
- from(BaseType) {
3659
- return createFASTElement(BaseType);
3660
- },
4028
+ from,
3661
4029
  /**
3662
4030
  * Defines a platform custom element based on the provided type and definition.
3663
4031
  * @param type - The custom element type to define.
3664
4032
  * @param nameOrDef - The name of the element to define or a definition object
3665
4033
  * that describes the element to define.
3666
4034
  */
3667
- define(type, nameOrDef) {
3668
- return this.metadata(type, nameOrDef).define().type;
3669
- },
4035
+ define,
3670
4036
  /**
3671
4037
  * 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
4038
  * @public
3674
4039
  */
3675
- metadata(type, nameOrDef) {
3676
- return new FASTElementDefinition(type, nameOrDef);
3677
- },
4040
+ compose,
3678
4041
  });
3679
4042
  /**
3680
4043
  * Decorator: Defines a platform custom element based on `FASTElement`.
@@ -3685,8 +4048,8 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3685
4048
  function customElement(nameOrDef) {
3686
4049
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
3687
4050
  return function (type) {
3688
- FASTElement.define(type, nameOrDef);
4051
+ define(type, nameOrDef);
3689
4052
  };
3690
4053
  }
3691
4054
 
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 };
4055
+ export { AdoptedStyleSheetsStrategy, ArrayObserver, Aspect, AttributeDefinition, Binding, BindingBehavior, CSSDirective, ChildrenDirective, Compiler, ContentBehavior, Controller, DOM, ElementStyles, EventBehavior, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SlottedDirective, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, Updates, ViewTemplate, attr, bind, booleanConverter, children, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };