@lushly-dev/fast-element 3.0.1-preview.0 → 3.0.1-preview.2

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 (250) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/command/command.api.json +7857 -0
  3. package/dist/context/context.api.json +1084 -0
  4. package/dist/declarative/declarative.api.json +8130 -0
  5. package/dist/definition-schema/definition-schema.api.json +2689 -0
  6. package/dist/di/di.api.json +4945 -0
  7. package/dist/dts/arrays.d.ts +2 -0
  8. package/dist/dts/binding/binding.d.ts +59 -0
  9. package/dist/dts/binding/exports.d.ts +9 -0
  10. package/dist/dts/binding/normalize.d.ts +9 -0
  11. package/dist/dts/binding/one-time.d.ts +11 -0
  12. package/dist/dts/binding/one-way.d.ts +20 -0
  13. package/dist/dts/binding/signal.d.ts +36 -0
  14. package/dist/dts/binding/two-way.d.ts +44 -0
  15. package/dist/dts/command/behavior.d.ts +94 -0
  16. package/dist/dts/command/behaviors/confirm-behavior.d.ts +139 -0
  17. package/dist/dts/command/behaviors/undo-behavior.d.ts +178 -0
  18. package/dist/dts/command/command.d.ts +79 -0
  19. package/dist/dts/command/component-registry.d.ts +50 -0
  20. package/dist/dts/command/components/confirm-dialog.d.ts +117 -0
  21. package/dist/dts/command/index.d.ts +21 -0
  22. package/dist/dts/command/registry.d.ts +201 -0
  23. package/dist/dts/command/schema-validation.d.ts +45 -0
  24. package/dist/dts/command/types.d.ts +332 -0
  25. package/dist/dts/command/validation.d.ts +116 -0
  26. package/dist/dts/command/weak-ref.d.ts +11 -0
  27. package/dist/dts/components/attributes.d.ts +153 -0
  28. package/dist/dts/components/definition-schema-transforms.d.ts +9 -0
  29. package/dist/dts/components/element-controller.d.ts +349 -0
  30. package/dist/dts/components/element-hydration.d.ts +2 -0
  31. package/dist/dts/components/enable-hydration.d.ts +66 -0
  32. package/dist/dts/components/fast-definitions.d.ts +211 -0
  33. package/dist/dts/components/fast-element-registry.d.ts +23 -0
  34. package/dist/dts/components/fast-element.d.ts +91 -0
  35. package/dist/dts/components/hydration-tracker.d.ts +110 -0
  36. package/dist/dts/components/hydration.d.ts +52 -0
  37. package/dist/dts/components/schema.d.ts +205 -0
  38. package/dist/dts/context.d.ts +171 -0
  39. package/dist/dts/debug.d.ts +5 -0
  40. package/dist/dts/declarative/attribute-map.d.ts +58 -0
  41. package/dist/dts/declarative/debug.d.ts +4 -0
  42. package/dist/dts/declarative/index.d.ts +14 -0
  43. package/dist/dts/declarative/interfaces.d.ts +8 -0
  44. package/dist/dts/declarative/observer-map-utilities.d.ts +58 -0
  45. package/dist/dts/declarative/observer-map.d.ts +89 -0
  46. package/dist/dts/declarative/runtime.d.ts +5 -0
  47. package/dist/dts/declarative/syntax.d.ts +21 -0
  48. package/dist/dts/declarative/template-bridge.d.ts +33 -0
  49. package/dist/dts/declarative/template-parser.d.ts +98 -0
  50. package/dist/dts/declarative/template.d.ts +9 -0
  51. package/dist/dts/declarative/utilities.d.ts +358 -0
  52. package/dist/dts/definition-schema/api.d.ts +42 -0
  53. package/dist/dts/definition-schema/cem.d.ts +78 -0
  54. package/dist/dts/definition-schema/extension.d.ts +18 -0
  55. package/dist/dts/definition-schema/extract.d.ts +38 -0
  56. package/dist/dts/definition-schema/index.d.ts +9 -0
  57. package/dist/dts/definition-schema/storage.d.ts +35 -0
  58. package/dist/dts/definition-schema/types.d.ts +176 -0
  59. package/dist/dts/dev-mode.d.ts +13 -0
  60. package/dist/dts/di/di.d.ts +894 -0
  61. package/dist/dts/dom-policy.d.ts +101 -0
  62. package/dist/dts/dom.d.ts +88 -0
  63. package/dist/dts/hydration/diagnostics.d.ts +93 -0
  64. package/dist/dts/hydration/exports.d.ts +18 -0
  65. package/dist/dts/hydration/hydration-debugger.d.ts +35 -0
  66. package/dist/dts/hydration/messages.d.ts +62 -0
  67. package/dist/dts/hydration/runtime.d.ts +7 -0
  68. package/dist/dts/hydration/target-builder.d.ts +107 -0
  69. package/dist/dts/index.d.ts +51 -0
  70. package/dist/dts/index.debug.d.ts +1 -0
  71. package/dist/dts/index.rollup.d.ts +1 -0
  72. package/dist/dts/index.rollup.debug.d.ts +1 -0
  73. package/dist/dts/interfaces.d.ts +106 -0
  74. package/dist/dts/metadata.d.ts +26 -0
  75. package/dist/dts/observation/arrays.d.ts +248 -0
  76. package/dist/dts/observation/notifier.d.ts +127 -0
  77. package/dist/dts/observation/observable.d.ts +276 -0
  78. package/dist/dts/observation/update-queue.d.ts +40 -0
  79. package/dist/dts/observation/volatile.d.ts +9 -0
  80. package/dist/dts/platform.d.ts +87 -0
  81. package/dist/dts/registry.d.ts +2 -0
  82. package/dist/dts/render.d.ts +7 -0
  83. package/dist/dts/schema.d.ts +1 -0
  84. package/dist/dts/scoped/detection.d.ts +55 -0
  85. package/dist/dts/scoped/index.d.ts +68 -0
  86. package/dist/dts/scoped/mixin.d.ts +118 -0
  87. package/dist/dts/scoped/registry.d.ts +76 -0
  88. package/dist/dts/scoped/types.d.ts +48 -0
  89. package/dist/dts/state/exports.d.ts +3 -0
  90. package/dist/dts/state/reactive.d.ts +8 -0
  91. package/dist/dts/state/state.d.ts +141 -0
  92. package/dist/dts/state/visitor.d.ts +6 -0
  93. package/dist/dts/state/watch.d.ts +10 -0
  94. package/dist/dts/styles/css-directive.d.ts +50 -0
  95. package/dist/dts/styles/css.d.ts +34 -0
  96. package/dist/dts/styles/element-styles.d.ts +61 -0
  97. package/dist/dts/styles/host.d.ts +65 -0
  98. package/dist/dts/styles/style-strategy.d.ts +42 -0
  99. package/dist/dts/templating/children.d.ts +62 -0
  100. package/dist/dts/templating/compiler.d.ts +54 -0
  101. package/dist/dts/templating/html-binding-directive.d.ts +155 -0
  102. package/dist/dts/templating/html-directive.d.ts +218 -0
  103. package/dist/dts/templating/html.d.ts +1 -0
  104. package/dist/dts/templating/hydration-view.d.ts +130 -0
  105. package/dist/dts/templating/markup.d.ts +48 -0
  106. package/dist/dts/templating/node-observation.d.ts +99 -0
  107. package/dist/dts/templating/ref.d.ts +23 -0
  108. package/dist/dts/templating/render.d.ts +318 -0
  109. package/dist/dts/templating/repeat.d.ts +131 -0
  110. package/dist/dts/templating/slotted.d.ts +38 -0
  111. package/dist/dts/templating/template.d.ts +221 -0
  112. package/dist/dts/templating/view.d.ts +268 -0
  113. package/dist/dts/templating/when.d.ts +12 -0
  114. package/dist/dts/testing/exports.d.ts +3 -0
  115. package/dist/dts/testing/fakes.d.ts +16 -0
  116. package/dist/dts/testing/fixture.d.ts +84 -0
  117. package/dist/dts/testing/models.d.ts +20 -0
  118. package/dist/dts/testing/timeout.d.ts +7 -0
  119. package/dist/dts/trust/index.d.ts +8 -0
  120. package/dist/dts/trust/types.d.ts +48 -0
  121. package/dist/dts/trust/utils.d.ts +26 -0
  122. package/dist/dts/tsdoc-metadata.json +11 -0
  123. package/dist/dts/utilities.d.ts +76 -0
  124. package/dist/esm/arrays.js +1 -0
  125. package/dist/esm/binding/binding.js +18 -0
  126. package/dist/esm/binding/exports.js +6 -0
  127. package/dist/esm/binding/normalize.js +17 -0
  128. package/dist/esm/binding/one-time.js +21 -0
  129. package/dist/esm/binding/one-way.js +30 -0
  130. package/dist/esm/binding/signal.js +99 -0
  131. package/dist/esm/binding/two-way.js +109 -0
  132. package/dist/esm/command/behavior.js +155 -0
  133. package/dist/esm/command/behaviors/confirm-behavior.js +225 -0
  134. package/dist/esm/command/behaviors/undo-behavior.js +313 -0
  135. package/dist/esm/command/command.js +104 -0
  136. package/dist/esm/command/component-registry.js +107 -0
  137. package/dist/esm/command/components/confirm-dialog.js +370 -0
  138. package/dist/esm/command/index.js +22 -0
  139. package/dist/esm/command/registry.js +755 -0
  140. package/dist/esm/command/schema-validation.js +194 -0
  141. package/dist/esm/command/types.js +86 -0
  142. package/dist/esm/command/validation.js +508 -0
  143. package/dist/esm/command/weak-ref.js +5 -0
  144. package/dist/esm/components/attributes.js +219 -0
  145. package/dist/esm/components/definition-schema-transforms.js +23 -0
  146. package/dist/esm/components/element-controller.js +840 -0
  147. package/dist/esm/components/element-hydration.js +2 -0
  148. package/dist/esm/components/enable-hydration.js +123 -0
  149. package/dist/esm/components/fast-definitions.js +294 -0
  150. package/dist/esm/components/fast-element-registry.js +67 -0
  151. package/dist/esm/components/fast-element.js +82 -0
  152. package/dist/esm/components/hydration-tracker.js +211 -0
  153. package/dist/esm/components/hydration.js +187 -0
  154. package/dist/esm/components/schema.js +253 -0
  155. package/dist/esm/context.js +185 -0
  156. package/dist/esm/debug.js +71 -0
  157. package/dist/esm/declarative/attribute-map.js +122 -0
  158. package/dist/esm/declarative/debug.js +4 -0
  159. package/dist/esm/declarative/index.js +10 -0
  160. package/dist/esm/declarative/interfaces.js +9 -0
  161. package/dist/esm/declarative/observer-map-utilities.js +565 -0
  162. package/dist/esm/declarative/observer-map.js +216 -0
  163. package/dist/esm/declarative/runtime.js +14 -0
  164. package/dist/esm/declarative/syntax.js +36 -0
  165. package/dist/esm/declarative/template-bridge.js +160 -0
  166. package/dist/esm/declarative/template-parser.js +306 -0
  167. package/dist/esm/declarative/template.js +116 -0
  168. package/dist/esm/declarative/utilities.js +1069 -0
  169. package/dist/esm/definition-schema/api.js +129 -0
  170. package/dist/esm/definition-schema/cem.js +89 -0
  171. package/dist/esm/definition-schema/extension.js +53 -0
  172. package/dist/esm/definition-schema/extract.js +167 -0
  173. package/dist/esm/definition-schema/index.js +8 -0
  174. package/dist/esm/definition-schema/storage.js +58 -0
  175. package/dist/esm/definition-schema/types.js +8 -0
  176. package/dist/esm/dev-mode.js +22 -0
  177. package/dist/esm/di/di.js +1441 -0
  178. package/dist/esm/dom-policy.js +374 -0
  179. package/dist/esm/dom.js +101 -0
  180. package/dist/esm/hydration/diagnostics.js +50 -0
  181. package/dist/esm/hydration/exports.js +6 -0
  182. package/dist/esm/hydration/hydration-debugger.js +112 -0
  183. package/dist/esm/hydration/messages.js +84 -0
  184. package/dist/esm/hydration/runtime.js +33 -0
  185. package/dist/esm/hydration/target-builder.js +264 -0
  186. package/dist/esm/index.debug.js +3 -0
  187. package/dist/esm/index.js +49 -0
  188. package/dist/esm/index.rollup.debug.js +6 -0
  189. package/dist/esm/index.rollup.js +4 -0
  190. package/dist/esm/interfaces.js +55 -0
  191. package/dist/esm/metadata.js +57 -0
  192. package/dist/esm/observation/arrays.js +661 -0
  193. package/dist/esm/observation/notifier.js +162 -0
  194. package/dist/esm/observation/observable.js +324 -0
  195. package/dist/esm/observation/update-queue.js +56 -0
  196. package/dist/esm/observation/volatile.js +16 -0
  197. package/dist/esm/platform.js +98 -0
  198. package/dist/esm/registry.js +2 -0
  199. package/dist/esm/render.js +1 -0
  200. package/dist/esm/schema.js +1 -0
  201. package/dist/esm/scoped/detection.js +102 -0
  202. package/dist/esm/scoped/index.js +69 -0
  203. package/dist/esm/scoped/mixin.js +127 -0
  204. package/dist/esm/scoped/registry.js +125 -0
  205. package/dist/esm/scoped/types.js +4 -0
  206. package/dist/esm/state/exports.js +3 -0
  207. package/dist/esm/state/reactive.js +34 -0
  208. package/dist/esm/state/state.js +148 -0
  209. package/dist/esm/state/visitor.js +28 -0
  210. package/dist/esm/state/watch.js +36 -0
  211. package/dist/esm/styles/css-directive.js +36 -0
  212. package/dist/esm/styles/css.js +56 -0
  213. package/dist/esm/styles/element-styles.js +144 -0
  214. package/dist/esm/styles/host.js +1 -0
  215. package/dist/esm/styles/style-strategy.js +1 -0
  216. package/dist/esm/templating/children.js +67 -0
  217. package/dist/esm/templating/compiler.js +359 -0
  218. package/dist/esm/templating/html-binding-directive.js +288 -0
  219. package/dist/esm/templating/html-directive.js +113 -0
  220. package/dist/esm/templating/html.js +1 -0
  221. package/dist/esm/templating/hydration-view.js +228 -0
  222. package/dist/esm/templating/markup.js +83 -0
  223. package/dist/esm/templating/node-observation.js +84 -0
  224. package/dist/esm/templating/ref.js +21 -0
  225. package/dist/esm/templating/render.js +468 -0
  226. package/dist/esm/templating/repeat.js +434 -0
  227. package/dist/esm/templating/slotted.js +49 -0
  228. package/dist/esm/templating/template.js +206 -0
  229. package/dist/esm/templating/view.js +298 -0
  230. package/dist/esm/templating/when.js +22 -0
  231. package/dist/esm/testing/exports.js +3 -0
  232. package/dist/esm/testing/fakes.js +107 -0
  233. package/dist/esm/testing/fixture.js +86 -0
  234. package/dist/esm/testing/models.js +58 -0
  235. package/dist/esm/testing/timeout.js +24 -0
  236. package/dist/esm/trust/index.js +7 -0
  237. package/dist/esm/trust/types.js +6 -0
  238. package/dist/esm/trust/utils.js +126 -0
  239. package/dist/esm/utilities.js +140 -0
  240. package/dist/fast-element.api.json +26798 -0
  241. package/dist/fast-element.d.ts +3985 -0
  242. package/dist/fast-element.debug.js +7604 -0
  243. package/dist/fast-element.debug.min.js +3 -0
  244. package/dist/fast-element.js +7603 -0
  245. package/dist/fast-element.min.js +3 -0
  246. package/dist/fast-element.untrimmed.d.ts +4103 -0
  247. package/dist/observation/arrays/arrays.api.json +2621 -0
  248. package/dist/scoped/scoped.api.json +891 -0
  249. package/dist/trust/trust.api.json +629 -0
  250. package/package.json +21 -23
@@ -0,0 +1,288 @@
1
+ import { isHydratable } from "../components/hydration.js";
2
+ import { DOM, DOMAspect } from "../dom.js";
3
+ import { Message } from "../interfaces.js";
4
+ import { ExecutionContext, } from "../observation/observable.js";
5
+ import { FAST } from "../platform.js";
6
+ import { HTMLDirective, } from "./html-directive.js";
7
+ import { HydrationStage } from "./hydration-view.js";
8
+ import { Markup } from "./markup.js";
9
+ import { createChildView } from "./view.js";
10
+ function isContentTemplate(value) {
11
+ return value.create !== undefined;
12
+ }
13
+ /**
14
+ * Sink function for DOMAspect.content bindings (text content interpolation).
15
+ * Handles two cases:
16
+ * - If the value is a ContentTemplate (has a create() method), it composes a child
17
+ * view into the DOM, managing view lifecycle (create/reuse/remove/bind).
18
+ * - If the value is a primitive, it sets target.textContent directly, first removing
19
+ * any previously composed view.
20
+ */
21
+ function updateContent(target, aspect, value, controller) {
22
+ // If there's no actual value, then this equates to the
23
+ // empty string for the purposes of content bindings.
24
+ if (value === null || value === undefined) {
25
+ value = "";
26
+ }
27
+ // If the value has a "create" method, then it's a ContentTemplate.
28
+ if (isContentTemplate(value)) {
29
+ target.textContent = "";
30
+ let view = target.$fastView;
31
+ // If there's no previous view that we might be able to
32
+ // reuse then create a new view from the template.
33
+ if (view === void 0) {
34
+ if (isHydratable(controller) &&
35
+ isHydratable(value) &&
36
+ controller.bindingViewBoundaries[this.targetNodeId] !== undefined &&
37
+ controller.hydrationStage !== HydrationStage.hydrated) {
38
+ const viewNodes = controller.bindingViewBoundaries[this.targetNodeId];
39
+ view = value.hydrate(viewNodes.first, viewNodes.last);
40
+ }
41
+ else {
42
+ view = createChildView(value, controller);
43
+ }
44
+ }
45
+ else {
46
+ // If there is a previous view, but it wasn't created
47
+ // from the same template as the new value, then we
48
+ // need to remove the old view if it's still in the DOM
49
+ // and create a new view from the template.
50
+ if (target.$fastTemplate !== value) {
51
+ if (view.isComposed) {
52
+ view.remove();
53
+ view.unbind();
54
+ }
55
+ view = createChildView(value, controller);
56
+ }
57
+ }
58
+ // It's possible that the value is the same as the previous template
59
+ // and that there's actually no need to compose it.
60
+ if (!view.isComposed) {
61
+ view.isComposed = true;
62
+ view.bind(controller.source, controller.context);
63
+ view.insertBefore(target);
64
+ target.$fastView = view;
65
+ target.$fastTemplate = value;
66
+ }
67
+ else if (view.needsBindOnly) {
68
+ view.needsBindOnly = false;
69
+ view.bind(controller.source, controller.context);
70
+ }
71
+ }
72
+ else {
73
+ const view = target.$fastView;
74
+ // If there is a view and it's currently composed into
75
+ // the DOM, then we need to remove it.
76
+ if (view !== void 0 && view.isComposed) {
77
+ view.isComposed = false;
78
+ view.remove();
79
+ if (view.needsBindOnly) {
80
+ view.needsBindOnly = false;
81
+ }
82
+ else {
83
+ view.unbind();
84
+ }
85
+ }
86
+ target.textContent = value;
87
+ }
88
+ }
89
+ /**
90
+ * Sink function for DOMAspect.tokenList bindings (e.g., :classList).
91
+ * Uses a versioning scheme to efficiently track which CSS classes were added
92
+ * in the current update vs. the previous one. Classes from the previous version
93
+ * that aren't present in the new value are automatically removed.
94
+ */
95
+ function updateTokenList(target, aspect, value) {
96
+ var _a;
97
+ const lookup = `${this.id}-t`;
98
+ const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { v: 0, cv: Object.create(null) });
99
+ const classVersions = state.cv;
100
+ let version = state.v;
101
+ const tokenList = target[aspect];
102
+ // Add the classes, tracking the version at which they were added.
103
+ if (value !== null && value !== undefined && value.length) {
104
+ const names = value.split(/\s+/);
105
+ for (let i = 0, ii = names.length; i < ii; ++i) {
106
+ const currentName = names[i];
107
+ if (currentName === "") {
108
+ continue;
109
+ }
110
+ classVersions[currentName] = version;
111
+ tokenList.add(currentName);
112
+ }
113
+ }
114
+ state.v = version + 1;
115
+ // If this is the first call to add classes, there's no need to remove old ones.
116
+ if (version === 0) {
117
+ return;
118
+ }
119
+ // Remove classes from the previous version.
120
+ version -= 1;
121
+ for (const name in classVersions) {
122
+ if (classVersions[name] === version) {
123
+ tokenList.remove(name);
124
+ }
125
+ }
126
+ }
127
+ /**
128
+ * Maps each DOMAspect type to its corresponding DOM update ("sink") function.
129
+ * When a binding value changes, the sink function for the binding's aspect type
130
+ * is called to push the new value into the DOM. Events are handled separately
131
+ * via addEventListener in bind(), so the event sink is a no-op.
132
+ */
133
+ const sinkLookup = {
134
+ [DOMAspect.attribute]: DOM.setAttribute,
135
+ [DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
136
+ [DOMAspect.property]: (t, a, v) => (t[a] = v),
137
+ [DOMAspect.content]: updateContent,
138
+ [DOMAspect.tokenList]: updateTokenList,
139
+ [DOMAspect.event]: () => void 0,
140
+ };
141
+ /**
142
+ * The central binding directive that bridges data expressions and DOM updates.
143
+ *
144
+ * HTMLBindingDirective fulfills three roles simultaneously:
145
+ * - **HTMLDirective**: Produces placeholder HTML via createHTML() during template authoring.
146
+ * - **ViewBehaviorFactory**: Creates behaviors (returns itself) during view creation.
147
+ * - **ViewBehavior / EventListener**: Attaches to a DOM node during bind, manages
148
+ * expression observers for reactive updates, and handles DOM events directly.
149
+ *
150
+ * The aspectType (set by HTMLDirective.assignAspect during template processing)
151
+ * determines which DOM "sink" function is used to apply values — e.g.,
152
+ * setAttribute for attributes, addEventListener for events, textContent for content.
153
+ *
154
+ * @public
155
+ */
156
+ export class HTMLBindingDirective {
157
+ /**
158
+ * Creates an instance of HTMLBindingDirective.
159
+ * @param dataBinding - The binding configuration to apply.
160
+ */
161
+ constructor(dataBinding) {
162
+ this.updateTarget = null;
163
+ /**
164
+ * The type of aspect to target.
165
+ */
166
+ this.aspectType = DOMAspect.content;
167
+ this.dataBinding = dataBinding;
168
+ }
169
+ /**
170
+ * Creates HTML to be used within a template.
171
+ * @param add - Can be used to add behavior factories to a template.
172
+ */
173
+ createHTML(add) {
174
+ return Markup.interpolation(add(this));
175
+ }
176
+ /**
177
+ * Creates a behavior.
178
+ */
179
+ createBehavior() {
180
+ var _a;
181
+ if (this.updateTarget === null) {
182
+ const sink = sinkLookup[this.aspectType];
183
+ const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
184
+ if (!sink) {
185
+ throw FAST.error(Message.unsupportedBindingBehavior);
186
+ }
187
+ this.data = `${this.id}-d`;
188
+ this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
189
+ }
190
+ return this;
191
+ }
192
+ /**
193
+ * Attaches this binding to its target DOM node.
194
+ * - For events: stores the controller reference on the target element and registers
195
+ * this directive as the EventListener via addEventListener. The directive's
196
+ * handleEvent() method will be called when the event fires.
197
+ * - For content bindings: registers an unbind handler, then falls through to the
198
+ * default path.
199
+ * - For all non-event bindings: creates (or reuses) an ExpressionObserver, evaluates
200
+ * the binding expression, and applies the result to the DOM via the updateTarget
201
+ * sink function. The observer will call handleChange() on future data changes.
202
+ * @internal
203
+ */
204
+ bind(controller) {
205
+ var _a;
206
+ const target = controller.targets[this.targetNodeId];
207
+ switch (this.aspectType) {
208
+ case DOMAspect.event:
209
+ target[this.data] = controller;
210
+ target.addEventListener(this.targetAspect, this, this.dataBinding.options);
211
+ break;
212
+ case DOMAspect.content:
213
+ controller.onUnbind(this);
214
+ // intentional fall through
215
+ default: {
216
+ const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
217
+ observer.target = target;
218
+ observer.controller = controller;
219
+ // Evaluate to establish dependency tracking
220
+ const value = observer.bind(controller);
221
+ // Skip DOM update for attribute bindings when prerendered —
222
+ // the server has already rendered the correct attribute values.
223
+ // Content, property, and tokenList bindings must still run
224
+ // so structural directives, host properties, and class lists
225
+ // are properly initialized.
226
+ if (controller._skipAttrUpdates &&
227
+ (this.aspectType === DOMAspect.attribute ||
228
+ this.aspectType === DOMAspect.booleanAttribute)) {
229
+ break;
230
+ }
231
+ this.updateTarget(target, this.targetAspect, value, controller);
232
+ break;
233
+ }
234
+ }
235
+ }
236
+ /** @internal */
237
+ unbind(controller) {
238
+ const target = controller.targets[this.targetNodeId];
239
+ const view = target.$fastView;
240
+ if (view !== void 0 && view.isComposed) {
241
+ view.unbind();
242
+ view.needsBindOnly = true;
243
+ }
244
+ }
245
+ /**
246
+ * Implements the EventListener interface. When a DOM event fires on the target
247
+ * element, this method retrieves the ViewController stored on the element,
248
+ * sets the event on the ExecutionContext so `c.event` is available to the
249
+ * binding expression, and evaluates the expression. If the expression returns
250
+ * anything other than `true`, the event's default action is prevented.
251
+ * @internal
252
+ */
253
+ handleEvent(event) {
254
+ const controller = event.currentTarget[this.data];
255
+ if (controller.isBound) {
256
+ ExecutionContext.setEvent(event);
257
+ const result = this.dataBinding.evaluate(controller.source, controller.context);
258
+ ExecutionContext.setEvent(null);
259
+ if (result !== true) {
260
+ event.preventDefault();
261
+ }
262
+ }
263
+ }
264
+ /**
265
+ * Called by the ExpressionObserver when a tracked dependency changes.
266
+ * Re-evaluates the binding expression via observer.bind() and pushes
267
+ * the new value to the DOM through the updateTarget sink function.
268
+ * This is the reactive update path that keeps the DOM in sync with data.
269
+ *
270
+ * Guards against stale notifications: when a view is unbound (e.g., after
271
+ * a parent `when` directive tears down a child element), coupled-lifetime
272
+ * observers may still hold active subscriptions. If a property change fires
273
+ * on the source element while the view is inactive, this guard prevents
274
+ * the binding expression from evaluating with a null source.
275
+ * @internal
276
+ */
277
+ handleChange(binding, observer) {
278
+ const controller = observer.controller;
279
+ // https://github.com/microsoft/fast/issues/7444
280
+ // This guard will be reconsidered in the next major version.
281
+ if (!controller.isBound) {
282
+ return;
283
+ }
284
+ const target = observer.target;
285
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
286
+ }
287
+ }
288
+ HTMLDirective.define(HTMLBindingDirective, { aspected: true });
@@ -0,0 +1,113 @@
1
+ import { DOMAspect } from "../dom.js";
2
+ import { createTypeRegistry, makeSerializationNoop } from "../platform.js";
3
+ import { Markup } from "./markup.js";
4
+ const registry = createTypeRegistry();
5
+ /**
6
+ * Instructs the template engine to apply behavior to a node.
7
+ * @public
8
+ */
9
+ export const HTMLDirective = Object.freeze({
10
+ /**
11
+ * Gets the directive definition associated with the instance.
12
+ * @param instance - The directive instance to retrieve the definition for.
13
+ */
14
+ getForInstance: registry.getForInstance,
15
+ /**
16
+ * Gets the directive definition associated with the specified type.
17
+ * @param type - The directive type to retrieve the definition for.
18
+ */
19
+ getByType: registry.getByType,
20
+ /**
21
+ * Defines an HTMLDirective based on the options.
22
+ * @param type - The type to define as a directive.
23
+ * @param options - Options that specify the directive's application.
24
+ */
25
+ define(type, options) {
26
+ options = options || {};
27
+ options.type = type;
28
+ registry.register(options);
29
+ return type;
30
+ },
31
+ /**
32
+ * Determines the DOM aspect type for a directive based on attribute name prefix.
33
+ * The prefix convention maps to aspect types as follows:
34
+ * - No prefix (e.g. "class") → DOMAspect.attribute
35
+ * - ":" prefix (e.g. ":value") → DOMAspect.property (":classList" → DOMAspect.tokenList)
36
+ * - "?" prefix (e.g. "?disabled") → DOMAspect.booleanAttribute
37
+ * - `@` prefix (e.g. `@click`) → DOMAspect.event
38
+ * - Falsy or absent value → DOMAspect.content (see remarks)
39
+ * @param directive - The directive to assign the aspect to.
40
+ * @param value - The value to base the aspect determination on.
41
+ * @remarks
42
+ * If a falsy value is provided, then the content aspect will be assigned.
43
+ */
44
+ assignAspect(directive, value) {
45
+ if (!value) {
46
+ directive.aspectType = DOMAspect.content;
47
+ return;
48
+ }
49
+ directive.sourceAspect = value;
50
+ switch (value[0]) {
51
+ case ":":
52
+ directive.targetAspect = value.substring(1);
53
+ directive.aspectType =
54
+ directive.targetAspect === "classList"
55
+ ? DOMAspect.tokenList
56
+ : DOMAspect.property;
57
+ break;
58
+ case "?":
59
+ directive.targetAspect = value.substring(1);
60
+ directive.aspectType = DOMAspect.booleanAttribute;
61
+ break;
62
+ case "@":
63
+ directive.targetAspect = value.substring(1);
64
+ directive.aspectType = DOMAspect.event;
65
+ break;
66
+ default:
67
+ directive.targetAspect = value;
68
+ directive.aspectType = DOMAspect.attribute;
69
+ break;
70
+ }
71
+ },
72
+ });
73
+ /**
74
+ * Decorator: Defines an HTMLDirective.
75
+ * @param options - Provides options that specify the directive's application.
76
+ * @public
77
+ */
78
+ export function htmlDirective(options) {
79
+ /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
80
+ return function (type) {
81
+ HTMLDirective.define(type, options);
82
+ };
83
+ }
84
+ /**
85
+ * A base class used for attribute directives that don't need internal state.
86
+ * @public
87
+ */
88
+ export class StatelessAttachedAttributeDirective {
89
+ /**
90
+ * Creates an instance of RefDirective.
91
+ * @param options - The options to use in configuring the directive.
92
+ */
93
+ constructor(options) {
94
+ this.options = options;
95
+ }
96
+ /**
97
+ * Creates a placeholder string based on the directive's index within the template.
98
+ * @param index - The index of the directive within the template.
99
+ * @remarks
100
+ * Creates a custom attribute placeholder.
101
+ */
102
+ createHTML(add) {
103
+ return Markup.attribute(add(this));
104
+ }
105
+ /**
106
+ * Creates a behavior.
107
+ * @param targets - The targets available for behaviors to be attached to.
108
+ */
109
+ createBehavior() {
110
+ return this;
111
+ }
112
+ }
113
+ makeSerializationNoop(StatelessAttachedAttributeDirective);
@@ -0,0 +1 @@
1
+ export { html } from "./template.js";
@@ -0,0 +1,228 @@
1
+ var _a;
2
+ import { Hydratable } from "../components/hydration.js";
3
+ import { getHostName, getHydrationDiagnostic, } from "../hydration/diagnostics.js";
4
+ import { buildViewBindingTargets, createRangeForNodes, HydrationTargetElementError, targetFactory, } from "../hydration/target-builder.js";
5
+ import { SourceLifetime } from "../observation/observable.js";
6
+ import { makeSerializationNoop } from "../platform.js";
7
+ import { DefaultExecutionContext, removeNodeSequence, } from "./view.js";
8
+ /** @public */
9
+ export const HydrationStage = {
10
+ unhydrated: "unhydrated",
11
+ hydrating: "hydrating",
12
+ hydrated: "hydrated",
13
+ };
14
+ /** @public */
15
+ export class HydrationBindingError extends Error {
16
+ constructor(
17
+ /**
18
+ * The error message
19
+ */
20
+ message,
21
+ /**
22
+ * The factory that was unable to be bound
23
+ */
24
+ factory,
25
+ /**
26
+ * A DocumentFragment containing a clone of the
27
+ * view's Nodes.
28
+ */
29
+ fragment,
30
+ /**
31
+ * String representation of the HTML in the template that
32
+ * threw the binding error.
33
+ */
34
+ templateString,
35
+ /**
36
+ * Structured description of the binding the hydration walk was
37
+ * attempting to apply when the mismatch was detected.
38
+ */
39
+ expected,
40
+ /**
41
+ * Structured description of the server-rendered DOM that was
42
+ * encountered at the mismatch point.
43
+ */
44
+ received) {
45
+ super(message);
46
+ this.factory = factory;
47
+ this.fragment = fragment;
48
+ this.templateString = templateString;
49
+ this.expected = expected;
50
+ this.received = received;
51
+ }
52
+ }
53
+ export class HydrationView extends DefaultExecutionContext {
54
+ get hydrationStage() {
55
+ return this._hydrationStage;
56
+ }
57
+ get targets() {
58
+ return this._targets;
59
+ }
60
+ get bindingViewBoundaries() {
61
+ return this._bindingViewBoundaries;
62
+ }
63
+ constructor(firstChild, lastChild, sourceTemplate, hostBindingTarget) {
64
+ super();
65
+ this.firstChild = firstChild;
66
+ this.lastChild = lastChild;
67
+ this.sourceTemplate = sourceTemplate;
68
+ this.hostBindingTarget = hostBindingTarget;
69
+ this[_a] = Hydratable;
70
+ this.context = this;
71
+ this.source = null;
72
+ this.isBound = false;
73
+ this.sourceLifetime = SourceLifetime.unknown;
74
+ this.unbindables = [];
75
+ this.fragment = null;
76
+ this.behaviors = null;
77
+ this._hydrationStage = HydrationStage.unhydrated;
78
+ this._bindingViewBoundaries = {};
79
+ this._targets = {};
80
+ this.factories = sourceTemplate.compile().factories;
81
+ }
82
+ /**
83
+ * no-op. Hydrated views are don't need to be moved from a documentFragment
84
+ * to the target node.
85
+ */
86
+ insertBefore(node) {
87
+ // No-op in cases where this is called before the view is removed,
88
+ // because the nodes will already be in the document and just need hydrating.
89
+ if (this.fragment === null) {
90
+ return;
91
+ }
92
+ if (this.fragment.hasChildNodes()) {
93
+ node.parentNode.insertBefore(this.fragment, node);
94
+ }
95
+ else {
96
+ const end = this.lastChild;
97
+ if (node.previousSibling === end)
98
+ return;
99
+ const parentNode = node.parentNode;
100
+ let current = this.firstChild;
101
+ let next;
102
+ while (current !== end) {
103
+ next = current.nextSibling;
104
+ parentNode.insertBefore(current, node);
105
+ current = next;
106
+ }
107
+ parentNode.insertBefore(end, node);
108
+ }
109
+ }
110
+ /**
111
+ * Appends the view to a node. In cases where this is called before the
112
+ * view has been removed, the method will no-op.
113
+ * @param node - the node to append the view to.
114
+ */
115
+ appendTo(node) {
116
+ if (this.fragment !== null) {
117
+ node.appendChild(this.fragment);
118
+ }
119
+ }
120
+ remove() {
121
+ const fragment = this.fragment || (this.fragment = document.createDocumentFragment());
122
+ const end = this.lastChild;
123
+ let current = this.firstChild;
124
+ let next;
125
+ while (current !== end) {
126
+ next = current.nextSibling;
127
+ if (!next) {
128
+ throw new Error(`Unmatched first/last child inside "${end.getRootNode().host.nodeName}".`);
129
+ }
130
+ fragment.appendChild(current);
131
+ current = next;
132
+ }
133
+ fragment.appendChild(end);
134
+ }
135
+ bind(source, context = this) {
136
+ if (this.source === source && this.context === context) {
137
+ return;
138
+ }
139
+ if (this.hydrationStage !== HydrationStage.hydrated) {
140
+ this._hydrationStage = HydrationStage.hydrating;
141
+ }
142
+ let behaviors = this.behaviors;
143
+ if (behaviors === null) {
144
+ this.source = source;
145
+ this.context = context;
146
+ try {
147
+ const { targets, boundaries } = buildViewBindingTargets(this.firstChild, this.lastChild, this.factories);
148
+ this._targets = targets;
149
+ this._bindingViewBoundaries = boundaries;
150
+ }
151
+ catch (error) {
152
+ if (error instanceof HydrationTargetElementError) {
153
+ let templateString = this.sourceTemplate.html;
154
+ if (typeof templateString !== "string") {
155
+ templateString = templateString.innerHTML;
156
+ }
157
+ error.templateString = templateString;
158
+ }
159
+ throw error;
160
+ }
161
+ this.behaviors = behaviors = new Array(this.factories.length);
162
+ const factories = this.factories;
163
+ for (let i = 0, ii = factories.length; i < ii; ++i) {
164
+ const factory = factories[i];
165
+ if (factory.targetNodeId === "h" && this.hostBindingTarget) {
166
+ targetFactory(factory, this.hostBindingTarget, this._targets);
167
+ }
168
+ // If the binding has been targeted or it is a host binding and the view has a hostBindingTarget
169
+ if (factory.targetNodeId in this.targets) {
170
+ const behavior = factory.createBehavior();
171
+ behavior.bind(this);
172
+ behaviors[i] = behavior;
173
+ }
174
+ else {
175
+ let templateString = this.sourceTemplate.html;
176
+ if (typeof templateString !== "string") {
177
+ templateString = templateString.innerHTML;
178
+ }
179
+ const fragment = createRangeForNodes(this.firstChild, this.lastChild).cloneContents();
180
+ const result = getHydrationDiagnostic().formatBindingMismatch(factory, this.firstChild, this.lastChild, getHostName(this.firstChild));
181
+ throw new HydrationBindingError(result.message, factory, fragment, templateString, result.expected, result.received);
182
+ }
183
+ }
184
+ }
185
+ else {
186
+ if (this.source !== null) {
187
+ this.evaluateUnbindables();
188
+ }
189
+ this.isBound = false;
190
+ this.source = source;
191
+ this.context = context;
192
+ for (let i = 0, ii = behaviors.length; i < ii; ++i) {
193
+ behaviors[i].bind(this);
194
+ }
195
+ }
196
+ this.isBound = true;
197
+ this._hydrationStage = HydrationStage.hydrated;
198
+ }
199
+ unbind() {
200
+ if (!this.isBound || this.source === null) {
201
+ return;
202
+ }
203
+ this.evaluateUnbindables();
204
+ this.source = null;
205
+ this.context = this;
206
+ this.isBound = false;
207
+ }
208
+ /**
209
+ * Removes the view and unbinds its behaviors, disposing of DOM nodes afterward.
210
+ * Once a view has been disposed, it cannot be inserted or bound again.
211
+ */
212
+ dispose() {
213
+ removeNodeSequence(this.firstChild, this.lastChild);
214
+ this.unbind();
215
+ }
216
+ onUnbind(behavior) {
217
+ this.unbindables.push(behavior);
218
+ }
219
+ evaluateUnbindables() {
220
+ const unbindables = this.unbindables;
221
+ for (let i = 0, ii = unbindables.length; i < ii; ++i) {
222
+ unbindables[i].unbind(this);
223
+ }
224
+ unbindables.length = 0;
225
+ }
226
+ }
227
+ _a = Hydratable;
228
+ makeSerializationNoop(HydrationView);