@microsoft/fast-html 1.0.0-alpha.34 → 1.0.0-alpha.35
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.
- package/dist/esm/components/element.js +51 -5
- package/dist/esm/components/observer-map.js +15 -9
- package/dist/esm/components/observer-map.spec.js +29 -9
- package/dist/esm/components/schema.spec.js +96 -97
- package/dist/esm/components/template.js +120 -130
- package/dist/esm/components/utilities.js +89 -21
- package/dist/esm/components/utilities.spec.js +249 -135
- package/package.json +5 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
2
|
import { attr, children, elements, FAST, FASTElement, FASTElementDefinition, fastElementRegistry, HydratableElementController, ref, repeat, slotted, ViewTemplate, when, } from "@microsoft/fast-element";
|
|
3
3
|
import "@microsoft/fast-element/install-hydratable-view-templates.js";
|
|
4
4
|
import { ObserverMap } from "./observer-map.js";
|
|
@@ -23,7 +23,7 @@ class TemplateElement extends FASTElement {
|
|
|
23
23
|
static config(callbacks) {
|
|
24
24
|
TemplateElement.lifecycleCallbacks = callbacks;
|
|
25
25
|
// Pass the hydration-specific callbacks to HydratableElementController
|
|
26
|
-
HydratableElementController.config(
|
|
26
|
+
HydratableElementController.config({ ...callbacks });
|
|
27
27
|
return this;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
@@ -68,7 +68,7 @@ class TemplateElement extends FASTElement {
|
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
this.schema = new Schema(name);
|
|
71
|
-
FASTElementDefinition.registerAsync(name).then((value) =>
|
|
71
|
+
FASTElementDefinition.registerAsync(name).then(async (value) => {
|
|
72
72
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
73
73
|
(_b = (_a = TemplateElement.lifecycleCallbacks).elementDidRegister) === null || _b === void 0 ? void 0 : _b.call(_a, name);
|
|
74
74
|
if (!((_c = TemplateElement.elementOptions) === null || _c === void 0 ? void 0 : _c[name])) {
|
|
@@ -84,7 +84,7 @@ class TemplateElement extends FASTElement {
|
|
|
84
84
|
(_f = (_e = TemplateElement.lifecycleCallbacks).templateWillUpdate) === null || _f === void 0 ? void 0 : _f.call(_e, name);
|
|
85
85
|
const innerHTML = transformInnerHTML(this.innerHTML);
|
|
86
86
|
// Cache paths during template processing (pass undefined if observerMap is not available)
|
|
87
|
-
const { strings, values } =
|
|
87
|
+
const { strings, values } = await this.resolveStringsAndValues(null, innerHTML, false, null, 0, this.schema, this.observerMap);
|
|
88
88
|
// Define the root properties cached in the observer map as observable (only if observerMap exists)
|
|
89
89
|
(_g = this.observerMap) === null || _g === void 0 ? void 0 : _g.defineProperties();
|
|
90
90
|
if (registeredFastElement) {
|
|
@@ -102,7 +102,7 @@ class TemplateElement extends FASTElement {
|
|
|
102
102
|
else {
|
|
103
103
|
throw FAST.error(2000 /* Message.noTemplateProvided */, { name: this.name });
|
|
104
104
|
}
|
|
105
|
-
})
|
|
105
|
+
});
|
|
106
106
|
}
|
|
107
107
|
/**
|
|
108
108
|
* Resolve strings and values from an innerHTML string
|
|
@@ -110,17 +110,15 @@ class TemplateElement extends FASTElement {
|
|
|
110
110
|
* @param self - Indicates that this should refer to itself instead of a property when creating bindings.
|
|
111
111
|
* @param observerMap - ObserverMap instance for caching binding paths (optional).
|
|
112
112
|
*/
|
|
113
|
-
resolveStringsAndValues(rootPropertyName, innerHTML, self = false, parentContext, level, schema, observerMap) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
};
|
|
123
|
-
});
|
|
113
|
+
async resolveStringsAndValues(rootPropertyName, innerHTML, self = false, parentContext, level, schema, observerMap) {
|
|
114
|
+
const strings = [];
|
|
115
|
+
const values = []; // these can be bindings, directives, etc.
|
|
116
|
+
await this.resolveInnerHTML(rootPropertyName, innerHTML, strings, values, self, parentContext, level, schema, observerMap);
|
|
117
|
+
strings.raw = strings.map(value => String.raw({ raw: value }));
|
|
118
|
+
return {
|
|
119
|
+
strings,
|
|
120
|
+
values,
|
|
121
|
+
};
|
|
124
122
|
}
|
|
125
123
|
/**
|
|
126
124
|
* Resolve a template or behavior
|
|
@@ -138,27 +136,25 @@ class TemplateElement extends FASTElement {
|
|
|
138
136
|
* @param self - Indicates that this should refer to itself instead of a property when creating bindings.
|
|
139
137
|
* @param observerMap - ObserverMap instance for caching binding paths (optional).
|
|
140
138
|
*/
|
|
141
|
-
resolveTemplateDirective(rootPropertyName, behaviorConfig, externalValues, innerHTML, self = false, parentContext, level, schema, observerMap) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
151
|
-
case "repeat": {
|
|
152
|
-
const valueAttr = behaviorConfig.value.split(" "); // syntax {{x in y}}
|
|
153
|
-
const updatedLevel = level + 1;
|
|
154
|
-
rootPropertyName = getRootPropertyName(rootPropertyName, valueAttr[2], parentContext, behaviorConfig.name);
|
|
155
|
-
const binding = bindingResolver(null, rootPropertyName, valueAttr[2], parentContext, behaviorConfig.name, schema, valueAttr[0], level);
|
|
156
|
-
const { strings, values } = yield this.resolveStringsAndValues(rootPropertyName, innerHTML.slice(behaviorConfig.openingTagEndIndex, behaviorConfig.closingTagStartIndex), true, valueAttr[0], updatedLevel, schema, observerMap);
|
|
157
|
-
externalValues.push(repeat((x, c) => binding(x, c), this.resolveTemplateOrBehavior(strings, values)));
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
139
|
+
async resolveTemplateDirective(rootPropertyName, behaviorConfig, externalValues, innerHTML, self = false, parentContext, level, schema, observerMap) {
|
|
140
|
+
switch (behaviorConfig.name) {
|
|
141
|
+
case "when": {
|
|
142
|
+
const expressionChain = getExpressionChain(behaviorConfig.value);
|
|
143
|
+
const whenLogic = resolveWhen(rootPropertyName, expressionChain, parentContext, level, schema);
|
|
144
|
+
const { strings, values } = await this.resolveStringsAndValues(rootPropertyName, innerHTML.slice(behaviorConfig.openingTagEndIndex, behaviorConfig.closingTagStartIndex), self, parentContext, level, schema, observerMap);
|
|
145
|
+
externalValues.push(when(whenLogic, this.resolveTemplateOrBehavior(strings, values)));
|
|
146
|
+
break;
|
|
160
147
|
}
|
|
161
|
-
|
|
148
|
+
case "repeat": {
|
|
149
|
+
const valueAttr = behaviorConfig.value.split(" "); // syntax {{x in y}}
|
|
150
|
+
const updatedLevel = level + 1;
|
|
151
|
+
rootPropertyName = getRootPropertyName(rootPropertyName, valueAttr[2], parentContext, behaviorConfig.name);
|
|
152
|
+
const binding = bindingResolver(null, rootPropertyName, valueAttr[2], parentContext, behaviorConfig.name, schema, valueAttr[0], level);
|
|
153
|
+
const { strings, values } = await this.resolveStringsAndValues(rootPropertyName, innerHTML.slice(behaviorConfig.openingTagEndIndex, behaviorConfig.closingTagStartIndex), true, valueAttr[0], updatedLevel, schema, observerMap);
|
|
154
|
+
externalValues.push(repeat((x, c) => binding(x, c), this.resolveTemplateOrBehavior(strings, values)));
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
162
158
|
}
|
|
163
159
|
/**
|
|
164
160
|
* Resolve a template directive
|
|
@@ -166,36 +162,34 @@ class TemplateElement extends FASTElement {
|
|
|
166
162
|
* @param propName - The property name to pass to the directive.
|
|
167
163
|
* @param externalValues - The interpreted values from the parent.
|
|
168
164
|
*/
|
|
169
|
-
resolveAttributeDirective(name, propName, externalValues) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (parts[1]) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
});
|
|
188
|
-
}
|
|
165
|
+
async resolveAttributeDirective(name, propName, externalValues) {
|
|
166
|
+
switch (name) {
|
|
167
|
+
case "children": {
|
|
168
|
+
externalValues.push(children(propName));
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case "slotted": {
|
|
172
|
+
const parts = propName.trim().split(" filter ");
|
|
173
|
+
const slottedOption = {
|
|
174
|
+
property: parts[0],
|
|
175
|
+
};
|
|
176
|
+
if (parts[1]) {
|
|
177
|
+
if (parts[1].startsWith("elements(")) {
|
|
178
|
+
let params = parts[1].replace("elements(", "");
|
|
179
|
+
params = params.substring(0, params.lastIndexOf(")"));
|
|
180
|
+
Object.assign(slottedOption, {
|
|
181
|
+
filter: elements(params || undefined),
|
|
182
|
+
});
|
|
189
183
|
}
|
|
190
|
-
externalValues.push(slotted(slottedOption));
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
193
|
-
case "ref": {
|
|
194
|
-
externalValues.push(ref(propName));
|
|
195
|
-
break;
|
|
196
184
|
}
|
|
185
|
+
externalValues.push(slotted(slottedOption));
|
|
186
|
+
break;
|
|
197
187
|
}
|
|
198
|
-
|
|
188
|
+
case "ref": {
|
|
189
|
+
externalValues.push(ref(propName));
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
199
193
|
}
|
|
200
194
|
/**
|
|
201
195
|
* Resolver of a data binding
|
|
@@ -206,60 +200,58 @@ class TemplateElement extends FASTElement {
|
|
|
206
200
|
* @param behaviorConfig - The binding behavior configuration object.
|
|
207
201
|
* @param observerMap - ObserverMap instance for caching binding paths (optional).
|
|
208
202
|
*/
|
|
209
|
-
resolveDataBinding(rootPropertyName, innerHTML, strings, values, self = false, behaviorConfig, parentContext, level, schema, observerMap) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
203
|
+
async resolveDataBinding(rootPropertyName, innerHTML, strings, values, self = false, behaviorConfig, parentContext, level, schema, observerMap) {
|
|
204
|
+
switch (behaviorConfig.subtype) {
|
|
205
|
+
case "content": {
|
|
206
|
+
strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex));
|
|
207
|
+
const type = "access";
|
|
208
|
+
const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
|
|
209
|
+
rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
|
|
210
|
+
const binding = bindingResolver(strings.join(""), rootPropertyName, propName, parentContext, type, schema, parentContext, level);
|
|
211
|
+
const contentBinding = (x, c) => binding(x, c);
|
|
212
|
+
values.push(contentBinding);
|
|
213
|
+
await this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
case "attribute": {
|
|
217
|
+
strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex));
|
|
218
|
+
if (behaviorConfig.aspect === "@") {
|
|
219
|
+
const bindingHTML = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
|
|
220
|
+
const openingParenthesis = bindingHTML.indexOf("(");
|
|
221
|
+
const closingParenthesis = bindingHTML.indexOf(")");
|
|
222
|
+
const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex -
|
|
223
|
+
(closingParenthesis - openingParenthesis) -
|
|
224
|
+
1);
|
|
225
|
+
const type = "event";
|
|
216
226
|
rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
|
|
227
|
+
const arg = bindingHTML.slice(openingParenthesis + 1, closingParenthesis);
|
|
217
228
|
const binding = bindingResolver(strings.join(""), rootPropertyName, propName, parentContext, type, schema, parentContext, level);
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex));
|
|
225
|
-
if (behaviorConfig.aspect === "@") {
|
|
226
|
-
const bindingHTML = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
|
|
227
|
-
const openingParenthesis = bindingHTML.indexOf("(");
|
|
228
|
-
const closingParenthesis = bindingHTML.indexOf(")");
|
|
229
|
-
const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex -
|
|
230
|
-
(closingParenthesis - openingParenthesis) -
|
|
231
|
-
1);
|
|
232
|
-
const type = "event";
|
|
233
|
-
rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
|
|
234
|
-
const arg = bindingHTML.slice(openingParenthesis + 1, closingParenthesis);
|
|
235
|
-
const binding = bindingResolver(strings.join(""), rootPropertyName, propName, parentContext, type, schema, parentContext, level);
|
|
236
|
-
const attributeBinding = (x, c) => binding(x, c).bind(x)(...(arg === "e" ? [c.event] : []), ...(arg !== "e" && arg !== ""
|
|
237
|
-
? [
|
|
238
|
-
bindingResolver(strings.join(""), rootPropertyName, arg, parentContext, type, schema, parentContext, level)(x, c),
|
|
239
|
-
]
|
|
240
|
-
: []));
|
|
241
|
-
values.push(attributeBinding);
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
|
|
245
|
-
const type = "access";
|
|
246
|
-
rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
|
|
247
|
-
const binding = bindingResolver(strings.join(""), rootPropertyName, propName, parentContext, type, schema, parentContext, level);
|
|
248
|
-
const attributeBinding = (x, c) => binding(x, c);
|
|
249
|
-
values.push(attributeBinding);
|
|
250
|
-
}
|
|
251
|
-
yield this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
|
|
252
|
-
break;
|
|
229
|
+
const attributeBinding = (x, c) => binding(x, c).bind(x)(...(arg === "e" ? [c.event] : []), ...(arg !== "e" && arg !== ""
|
|
230
|
+
? [
|
|
231
|
+
bindingResolver(strings.join(""), rootPropertyName, arg, parentContext, type, schema, parentContext, level)(x, c),
|
|
232
|
+
]
|
|
233
|
+
: []));
|
|
234
|
+
values.push(attributeBinding);
|
|
253
235
|
}
|
|
254
|
-
|
|
255
|
-
strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex - behaviorConfig.name.length - 4));
|
|
236
|
+
else {
|
|
256
237
|
const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
238
|
+
const type = "access";
|
|
239
|
+
rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
|
|
240
|
+
const binding = bindingResolver(strings.join(""), rootPropertyName, propName, parentContext, type, schema, parentContext, level);
|
|
241
|
+
const attributeBinding = (x, c) => binding(x, c);
|
|
242
|
+
values.push(attributeBinding);
|
|
260
243
|
}
|
|
244
|
+
await this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
|
|
245
|
+
break;
|
|
261
246
|
}
|
|
262
|
-
|
|
247
|
+
case "attributeDirective": {
|
|
248
|
+
strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex - behaviorConfig.name.length - 4));
|
|
249
|
+
const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
|
|
250
|
+
await this.resolveAttributeDirective(behaviorConfig.name, propName, values);
|
|
251
|
+
await this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex + 1, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
263
255
|
}
|
|
264
256
|
/**
|
|
265
257
|
* Resolver of the innerHTML string
|
|
@@ -269,27 +261,25 @@ class TemplateElement extends FASTElement {
|
|
|
269
261
|
* @param self - Indicates that this should refer to itself instead of a property when creating bindings.
|
|
270
262
|
* @param observerMap - ObserverMap instance for caching binding paths (optional).
|
|
271
263
|
*/
|
|
272
|
-
resolveInnerHTML(rootPropertyName, innerHTML, strings, values, self = false, parentContext, level, schema, observerMap) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
break;
|
|
289
|
-
}
|
|
264
|
+
async resolveInnerHTML(rootPropertyName, innerHTML, strings, values, self = false, parentContext, level, schema, observerMap) {
|
|
265
|
+
const behaviorConfig = getNextBehavior(innerHTML);
|
|
266
|
+
if (behaviorConfig === null) {
|
|
267
|
+
strings.push(innerHTML);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
switch (behaviorConfig.type) {
|
|
271
|
+
case "dataBinding": {
|
|
272
|
+
await this.resolveDataBinding(rootPropertyName, innerHTML, strings, values, self, behaviorConfig, parentContext, level, schema, observerMap);
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
case "templateDirective": {
|
|
276
|
+
strings.push(innerHTML.slice(0, behaviorConfig.openingTagStartIndex));
|
|
277
|
+
await this.resolveTemplateDirective(rootPropertyName, behaviorConfig, values, innerHTML, self, parentContext, level, schema, observerMap);
|
|
278
|
+
await this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingTagEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
|
|
279
|
+
break;
|
|
290
280
|
}
|
|
291
281
|
}
|
|
292
|
-
}
|
|
282
|
+
}
|
|
293
283
|
}
|
|
294
284
|
}
|
|
295
285
|
/**
|
|
@@ -28,7 +28,10 @@ const ComparisonOperator = {
|
|
|
28
28
|
NOT: "!",
|
|
29
29
|
NOT_EQUALS: "!=",
|
|
30
30
|
};
|
|
31
|
-
const Operator =
|
|
31
|
+
const Operator = {
|
|
32
|
+
...LogicalOperator,
|
|
33
|
+
...ComparisonOperator,
|
|
34
|
+
};
|
|
32
35
|
/**
|
|
33
36
|
* A map of proxied objects
|
|
34
37
|
*/
|
|
@@ -141,7 +144,11 @@ function getAttributeDataBindingConfig(innerHTML, config) {
|
|
|
141
144
|
firstCharOfAttribute === ":"
|
|
142
145
|
? firstCharOfAttribute
|
|
143
146
|
: null;
|
|
144
|
-
return
|
|
147
|
+
return {
|
|
148
|
+
...config,
|
|
149
|
+
subtype: "attribute",
|
|
150
|
+
aspect,
|
|
151
|
+
};
|
|
145
152
|
}
|
|
146
153
|
/**
|
|
147
154
|
* Get the attribute directive binding config
|
|
@@ -154,7 +161,11 @@ function getAttributeDirectiveDataBindingConfig(innerHTML, config) {
|
|
|
154
161
|
const lastItem = splitInnerHTML[splitInnerHTML.length - 1];
|
|
155
162
|
const equals = lastItem.indexOf("=");
|
|
156
163
|
const name = lastItem.slice(2, equals);
|
|
157
|
-
return
|
|
164
|
+
return {
|
|
165
|
+
...config,
|
|
166
|
+
subtype: "attributeDirective",
|
|
167
|
+
name: name,
|
|
168
|
+
};
|
|
158
169
|
}
|
|
159
170
|
/**
|
|
160
171
|
* Get the content data binding config
|
|
@@ -162,7 +173,10 @@ function getAttributeDirectiveDataBindingConfig(innerHTML, config) {
|
|
|
162
173
|
* @returns ContentDataBindingBehaviorConfig
|
|
163
174
|
*/
|
|
164
175
|
function getContentDataBindingConfig(config) {
|
|
165
|
-
return
|
|
176
|
+
return {
|
|
177
|
+
...config,
|
|
178
|
+
subtype: "content",
|
|
179
|
+
};
|
|
166
180
|
}
|
|
167
181
|
/**
|
|
168
182
|
* Finds the next data binding in innerHTML and determines its type and indices
|
|
@@ -403,7 +417,10 @@ function evaluatePartsInExpressionChain(parts, operator) {
|
|
|
403
417
|
while (current.next) {
|
|
404
418
|
current = current.next;
|
|
405
419
|
}
|
|
406
|
-
current.next =
|
|
420
|
+
current.next = {
|
|
421
|
+
operator,
|
|
422
|
+
...nextPart,
|
|
423
|
+
};
|
|
407
424
|
}
|
|
408
425
|
}
|
|
409
426
|
return firstPart;
|
|
@@ -536,7 +553,16 @@ function resolveExpression(x, c, level, contextPath, expression, rootSchema) {
|
|
|
536
553
|
return resolvedLeft < resolvedRight;
|
|
537
554
|
}
|
|
538
555
|
default: {
|
|
539
|
-
|
|
556
|
+
if (typeof resolvedLeft === "boolean") {
|
|
557
|
+
return resolvedLeft;
|
|
558
|
+
}
|
|
559
|
+
if (typeof resolvedLeft === "number") {
|
|
560
|
+
return resolvedLeft !== 0;
|
|
561
|
+
}
|
|
562
|
+
if (typeof resolvedLeft === "string") {
|
|
563
|
+
return resolvedLeft.length > 0;
|
|
564
|
+
}
|
|
565
|
+
return !!resolvedLeft;
|
|
540
566
|
}
|
|
541
567
|
}
|
|
542
568
|
}
|
|
@@ -882,10 +908,14 @@ function notifyObservables(targetObject) {
|
|
|
882
908
|
* @returns Proxy object
|
|
883
909
|
*/
|
|
884
910
|
export function assignProxy(schema, rootSchema, target, rootProperty, object) {
|
|
885
|
-
if (object.$isProxy
|
|
911
|
+
if (!object.$isProxy) {
|
|
886
912
|
// Create a proxy for the object that triggers Observable.notify on mutations
|
|
887
913
|
const proxy = new Proxy(object, {
|
|
888
914
|
set: (obj, prop, value) => {
|
|
915
|
+
const currentValue = obj[prop];
|
|
916
|
+
if (deepEqual(currentValue, value)) {
|
|
917
|
+
return true;
|
|
918
|
+
}
|
|
889
919
|
obj[prop] = assignObservables(schema, rootSchema, value, target, rootProperty);
|
|
890
920
|
notifyObservables(proxy);
|
|
891
921
|
return true;
|
|
@@ -970,6 +1000,49 @@ function getAttributeName(previousString) {
|
|
|
970
1000
|
}
|
|
971
1001
|
return attributeName;
|
|
972
1002
|
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Determine if an object has an observable accessor for a backing field
|
|
1005
|
+
* @param object - The object to check
|
|
1006
|
+
* @param backingField - The backing field name
|
|
1007
|
+
* @returns True if the object has an observable accessor for the backing field, false otherwise
|
|
1008
|
+
*/
|
|
1009
|
+
function hasObservableAccessor(object, backingField) {
|
|
1010
|
+
const accessors = Observable.getAccessors(object);
|
|
1011
|
+
if (!accessors) {
|
|
1012
|
+
return false;
|
|
1013
|
+
}
|
|
1014
|
+
return accessors.some((accessor) => accessor.name === backingField);
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Determine if a key should be skipped during deep comparison
|
|
1018
|
+
*
|
|
1019
|
+
* @param object - The object to check
|
|
1020
|
+
* @param key - The key to evaluate
|
|
1021
|
+
* @returns True if the key should be skipped during comparison, false otherwise
|
|
1022
|
+
*/
|
|
1023
|
+
function shouldSkipKey(object, key) {
|
|
1024
|
+
if (key[0] !== "_" || key === "_") {
|
|
1025
|
+
return false;
|
|
1026
|
+
}
|
|
1027
|
+
return hasObservableAccessor(object, key.slice(1));
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Get comparable keys from an object, excluding those that should be skipped
|
|
1031
|
+
*
|
|
1032
|
+
* @param object - The object to extract keys from
|
|
1033
|
+
* @returns An array of keys that should be compared
|
|
1034
|
+
*/
|
|
1035
|
+
function getComparableKeys(object) {
|
|
1036
|
+
const hasOwn = Object.prototype.hasOwnProperty;
|
|
1037
|
+
const keys = [];
|
|
1038
|
+
for (const key in object) {
|
|
1039
|
+
if (!hasOwn.call(object, key) || shouldSkipKey(object, key)) {
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
1042
|
+
keys.push(key);
|
|
1043
|
+
}
|
|
1044
|
+
return keys;
|
|
1045
|
+
}
|
|
973
1046
|
/**
|
|
974
1047
|
* Deeply compares two objects for equality.
|
|
975
1048
|
*
|
|
@@ -1007,22 +1080,17 @@ export function deepEqual(obj1, obj2) {
|
|
|
1007
1080
|
return true;
|
|
1008
1081
|
}
|
|
1009
1082
|
const hasOwn = Object.prototype.hasOwnProperty;
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
if (!hasOwn.call(obj2, key) || !deepEqual(obj1[key], obj2[key])) {
|
|
1015
|
-
return false;
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1083
|
+
const obj1Keys = getComparableKeys(obj1);
|
|
1084
|
+
const obj2Keys = getComparableKeys(obj2);
|
|
1085
|
+
if (obj1Keys.length !== obj2Keys.length) {
|
|
1086
|
+
return false;
|
|
1018
1087
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
obj2KeyCount++;
|
|
1088
|
+
for (const key of obj1Keys) {
|
|
1089
|
+
if (!hasOwn.call(obj2, key) || !deepEqual(obj1[key], obj2[key])) {
|
|
1090
|
+
return false;
|
|
1023
1091
|
}
|
|
1024
1092
|
}
|
|
1025
|
-
return
|
|
1093
|
+
return true;
|
|
1026
1094
|
}
|
|
1027
1095
|
/**
|
|
1028
1096
|
* Deeply merges the source object into the target object.
|
|
@@ -1048,7 +1116,7 @@ export function deepMerge(target, source) {
|
|
|
1048
1116
|
const isSourceArray = Array.isArray(sourceValue);
|
|
1049
1117
|
if (isSourceArray) {
|
|
1050
1118
|
const isTargetArray = Array.isArray(targetValue);
|
|
1051
|
-
const clonedItems = sourceValue.map((item) => item && typeof item === "object" ?
|
|
1119
|
+
const clonedItems = sourceValue.map((item) => item && typeof item === "object" ? { ...item } : item);
|
|
1052
1120
|
if (isTargetArray) {
|
|
1053
1121
|
// Use splice to maintain observable array tracking
|
|
1054
1122
|
targetValue.splice(0, targetValue.length, ...clonedItems);
|