@jinntec/fore 2.4.2 → 2.6.0
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/fore-dev.js +34262 -3
- package/dist/fore.js +34124 -3
- package/index.js +2 -0
- package/package.json +2 -2
- package/resources/fore.css +30 -1
- package/src/DataObserver.js +181 -0
- package/src/DependentXPathQueries.js +32 -0
- package/src/ForeElementMixin.js +289 -254
- package/src/actions/abstract-action.js +38 -5
- package/src/actions/fx-append.js +2 -0
- package/src/actions/fx-delete.js +13 -1
- package/src/actions/fx-hide.js +1 -1
- package/src/actions/fx-insert.js +47 -39
- package/src/actions/fx-load.js +7 -2
- package/src/actions/fx-send.js +0 -2
- package/src/actions/fx-setvalue.js +104 -82
- package/src/actions/fx-show.js +4 -2
- package/src/fore.js +40 -22
- package/src/fx-bind.js +3 -2
- package/src/fx-fore.js +320 -164
- package/src/fx-instance.js +3 -2
- package/src/fx-model.js +63 -34
- package/src/fx-submission.js +38 -22
- package/src/getInScopeContext.js +2 -1
- package/src/modelitem.js +107 -91
- package/src/ui/UIElement.js +91 -0
- package/src/ui/abstract-control.js +14 -9
- package/src/ui/fx-container.js +7 -4
- package/src/ui/fx-control-menu.js +198 -0
- package/src/ui/fx-control.js +64 -41
- package/src/ui/fx-dialog.js +2 -2
- package/src/ui/fx-group.js +14 -0
- package/src/ui/fx-repeat.js +42 -3
- package/src/ui/fx-repeatitem.js +3 -0
- package/src/ui/fx-upload.js +304 -0
- package/src/xpath-util.js +310 -168
- package/dist/fore-dev.js.map +0 -1
- package/dist/fore.js.map +0 -1
package/index.js
CHANGED
|
@@ -32,6 +32,7 @@ import './src/tools/fx-devtools.js';
|
|
|
32
32
|
import './src/tools/fx-dom-inspector.js';
|
|
33
33
|
import './src/lab/fore-component.js';
|
|
34
34
|
import './src/tools/fx-json-instance.js';
|
|
35
|
+
import './src/ui/fx-upload.js';
|
|
35
36
|
// import './src/tools/fx-minimap.js';
|
|
36
37
|
|
|
37
38
|
// import './src/ui/fx-checkbox-group.js';
|
|
@@ -62,6 +63,7 @@ import './src/actions/fx-call.js';
|
|
|
62
63
|
import './src/actions/fx-setattribute.js';
|
|
63
64
|
import './src/actions/fx-construct-done.js';
|
|
64
65
|
import './src/actions/fx-unmodified.js';
|
|
66
|
+
import './src/ui/fx-control-menu.js';
|
|
65
67
|
|
|
66
68
|
import './src/functions/fx-function.js';
|
|
67
69
|
import './src/functions/fx-functionlib.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jinntec/fore",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "Fore - declarative user interfaces in plain HTML",
|
|
5
5
|
"module": "./index.js",
|
|
6
6
|
"publishConfig": {
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@skypack/package-check": "^0.2.2",
|
|
50
50
|
"@types/cypress": "^1.1.3",
|
|
51
51
|
"@webcomponents/webcomponentsjs": "^2.6.0",
|
|
52
|
-
"cypress": "^13.
|
|
52
|
+
"cypress": "^13.16.0",
|
|
53
53
|
"deepmerge": "^4.2.2",
|
|
54
54
|
"es-dev-server": "^2.1.0",
|
|
55
55
|
"eslint": "^8.16.0",
|
package/resources/fore.css
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
--inspector-pre-bg: var(--paper-grey-100);
|
|
9
9
|
--inspector-color: var(--paper-grey-800);
|
|
10
10
|
--inspector-instance-height: 200px;
|
|
11
|
-
--inspector-handle-bg: var(--paper-blue-
|
|
11
|
+
--inspector-handle-bg: var(--paper-blue-700);
|
|
12
12
|
--inspector-handle-width: 30px;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -146,6 +146,7 @@
|
|
|
146
146
|
|
|
147
147
|
fx-group, fx-switch, fx-repeat, fx-dialog {
|
|
148
148
|
display: block;
|
|
149
|
+
position: relative;
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
fx-hint {
|
|
@@ -215,6 +216,28 @@
|
|
|
215
216
|
display: block;
|
|
216
217
|
}
|
|
217
218
|
|
|
219
|
+
/* for on-demand elements */
|
|
220
|
+
[role=group][on-demand=true] {
|
|
221
|
+
display: none;
|
|
222
|
+
}
|
|
223
|
+
.trash {
|
|
224
|
+
display: inline-block;
|
|
225
|
+
position: absolute;
|
|
226
|
+
right:0;
|
|
227
|
+
top:0;
|
|
228
|
+
z-index: 10;
|
|
229
|
+
font-size: 0.8rem;
|
|
230
|
+
}
|
|
231
|
+
.trash svg{
|
|
232
|
+
width: 1rem;
|
|
233
|
+
height: 1rem;
|
|
234
|
+
color:#666666;
|
|
235
|
+
}
|
|
236
|
+
.trash svg:hover{
|
|
237
|
+
color:black;
|
|
238
|
+
|
|
239
|
+
}
|
|
240
|
+
|
|
218
241
|
/* fx-action-log styles */
|
|
219
242
|
|
|
220
243
|
fx-fore.action-log{
|
|
@@ -267,3 +290,9 @@
|
|
|
267
290
|
background: var(--paper-orange-500);
|
|
268
291
|
}
|
|
269
292
|
|
|
293
|
+
/*
|
|
294
|
+
* For on-demand stuff, if it has no value initially,hide it
|
|
295
|
+
*/
|
|
296
|
+
[on-demand] {
|
|
297
|
+
display: none
|
|
298
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/* Usage:
|
|
2
|
+
|
|
3
|
+
// Create or load your XML document
|
|
4
|
+
const xmlString = `
|
|
5
|
+
<root>
|
|
6
|
+
<item id="1">Initial Value</item>
|
|
7
|
+
</root>
|
|
8
|
+
`;
|
|
9
|
+
const parser = new DOMParser();
|
|
10
|
+
const xmlDoc = parser.parseFromString(xmlString, "application/xml");
|
|
11
|
+
const rootNode = xmlDoc.querySelector("root");
|
|
12
|
+
|
|
13
|
+
// Create an instance of the DataObserver
|
|
14
|
+
const xmlObserver = new DataObserver(200);
|
|
15
|
+
|
|
16
|
+
// Define a callback to process mutations
|
|
17
|
+
const handleXMLMutations = (mutationsList) => {
|
|
18
|
+
console.log("XML Mutations:", mutationsList);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Attach the observer to the XML root node
|
|
22
|
+
xmlObserver.observe(rootNode, handleXMLMutations);
|
|
23
|
+
|
|
24
|
+
// Modify the XML document to trigger mutations
|
|
25
|
+
setTimeout(() => {
|
|
26
|
+
const newItem = xmlDoc.createElement("item");
|
|
27
|
+
newItem.textContent = "Newly Added Item";
|
|
28
|
+
rootNode.appendChild(newItem); // This triggers a mutation
|
|
29
|
+
}, 1000);
|
|
30
|
+
|
|
31
|
+
For JSON:
|
|
32
|
+
// Create a JSON object
|
|
33
|
+
const jsonObject = {
|
|
34
|
+
name: "John Doe",
|
|
35
|
+
age: 30,
|
|
36
|
+
hobbies: ["reading", "coding"],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Create an instance of the DataObserver
|
|
40
|
+
const jsonObserver = new DataObserver(200);
|
|
41
|
+
|
|
42
|
+
// Define a callback to process changes
|
|
43
|
+
const handleJSONChanges = (changesList) => {
|
|
44
|
+
console.log("JSON Changes:", changesList);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Attach the observer to the JSON object
|
|
48
|
+
jsonObserver.observe(jsonObject, handleJSONChanges);
|
|
49
|
+
|
|
50
|
+
// Modify the JSON object to trigger changes
|
|
51
|
+
setTimeout(() => {
|
|
52
|
+
jsonObject.name = "Jane Doe"; // This triggers a mutation
|
|
53
|
+
jsonObject.age = 31; // Another mutation
|
|
54
|
+
delete jsonObject.hobbies; // This triggers a delete mutation
|
|
55
|
+
}, 1000);
|
|
56
|
+
|
|
57
|
+
*/
|
|
58
|
+
export class DataObserver {
|
|
59
|
+
constructor(debounceTime = 0) {
|
|
60
|
+
this.observer = null; // Placeholder for MutationObserver (for XML)
|
|
61
|
+
this.debounceTime = debounceTime; // Time in milliseconds for optional debouncing
|
|
62
|
+
this.mutationsQueue = []; // To batch process mutations
|
|
63
|
+
this.debounceTimer = null; // Timer for debouncing
|
|
64
|
+
this.jsonProxy = null; // Proxy for JSON observation
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Attaches an observer to the given rootNode (DOM Node or JSON object)
|
|
69
|
+
* @param {Node|Object} rootNode - The XML DOM node or JSON object to observe
|
|
70
|
+
* @param {Function} callback - Function to process batch mutations
|
|
71
|
+
*/
|
|
72
|
+
observe(rootNode, callback) {
|
|
73
|
+
if (rootNode instanceof Node) {
|
|
74
|
+
// Handle XML Node
|
|
75
|
+
this.observeXML(rootNode, callback);
|
|
76
|
+
} else if (typeof rootNode === "object" && rootNode !== null) {
|
|
77
|
+
// Handle JSON Object
|
|
78
|
+
this.observeJSON(rootNode, callback);
|
|
79
|
+
} else {
|
|
80
|
+
throw new Error("Invalid rootNode. Must be a DOM Node or a JSON object.");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Observes changes in an XML DOM node
|
|
86
|
+
* @param {Node} xmlNode - The XML DOM node to observe
|
|
87
|
+
* @param {Function} callback - Function to process batch mutations
|
|
88
|
+
*/
|
|
89
|
+
observeXML(xmlNode, callback) {
|
|
90
|
+
// Initialize a new MutationObserver
|
|
91
|
+
this.observer = new MutationObserver((mutationsList) => {
|
|
92
|
+
this.mutationsQueue.push(...mutationsList);
|
|
93
|
+
|
|
94
|
+
if (this.debounceTime > 0) {
|
|
95
|
+
clearTimeout(this.debounceTimer); // Clear previous timer
|
|
96
|
+
this.debounceTimer = setTimeout(() => {
|
|
97
|
+
this.processMutations(callback);
|
|
98
|
+
}, this.debounceTime);
|
|
99
|
+
} else {
|
|
100
|
+
this.processMutations(callback);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Start observing the XML node
|
|
105
|
+
this.observer.observe(xmlNode, {
|
|
106
|
+
characterData: true,
|
|
107
|
+
childList: true,
|
|
108
|
+
subtree: true,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Observes changes in a JSON object using a proxy
|
|
114
|
+
* @param {Object} jsonObject - The JSON object to observe
|
|
115
|
+
* @param {Function} callback - Function to process changes
|
|
116
|
+
*/
|
|
117
|
+
observeJSON(jsonObject, callback) {
|
|
118
|
+
const handler = {
|
|
119
|
+
set: (target, key, value) => {
|
|
120
|
+
this.mutationsQueue.push({ type: "update", target, key, value });
|
|
121
|
+
|
|
122
|
+
if (this.debounceTime > 0) {
|
|
123
|
+
clearTimeout(this.debounceTimer); // Clear previous timer
|
|
124
|
+
this.debounceTimer = setTimeout(() => {
|
|
125
|
+
this.processMutations(callback);
|
|
126
|
+
}, this.debounceTime);
|
|
127
|
+
} else {
|
|
128
|
+
this.processMutations(callback);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Perform the actual update
|
|
132
|
+
target[key] = value;
|
|
133
|
+
return true;
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
deleteProperty: (target, key) => {
|
|
137
|
+
this.mutationsQueue.push({ type: "delete", target, key });
|
|
138
|
+
|
|
139
|
+
if (this.debounceTime > 0) {
|
|
140
|
+
clearTimeout(this.debounceTimer); // Clear previous timer
|
|
141
|
+
this.debounceTimer = setTimeout(() => {
|
|
142
|
+
this.processMutations(callback);
|
|
143
|
+
}, this.debounceTime);
|
|
144
|
+
} else {
|
|
145
|
+
this.processMutations(callback);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Perform the actual delete
|
|
149
|
+
return delete target[key];
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
this.jsonProxy = new Proxy(jsonObject, handler);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Processes the mutations batch and clears the queue
|
|
158
|
+
* @param {Function} callback - Function to handle the batch of mutations
|
|
159
|
+
*/
|
|
160
|
+
processMutations(callback) {
|
|
161
|
+
if (this.mutationsQueue.length > 0) {
|
|
162
|
+
callback(this.mutationsQueue); // Pass the batch to the callback
|
|
163
|
+
this.mutationsQueue = []; // Clear the queue
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Disconnects the observer and clears any pending debounce timer
|
|
169
|
+
*/
|
|
170
|
+
disconnect() {
|
|
171
|
+
if (this.observer) {
|
|
172
|
+
this.observer.disconnect(); // Disconnect the MutationObserver
|
|
173
|
+
this.observer = null;
|
|
174
|
+
}
|
|
175
|
+
if (this.debounceTimer) {
|
|
176
|
+
clearTimeout(this.debounceTimer); // Clear any pending debounce timer
|
|
177
|
+
}
|
|
178
|
+
this.mutationsQueue = []; // Clear the mutation queue
|
|
179
|
+
this.jsonProxy = null; // Clear JSON proxy reference
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -25,6 +25,38 @@ export default class DependentXPathQueries {
|
|
|
25
25
|
return !!this._parentDependencies?.isInvalidatedByIndexFunction();
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Detects whether there are XPaths that may be affected by child list changes to nodes with the given local names
|
|
30
|
+
*
|
|
31
|
+
* It does this pessimistically, assuming that any descendant axis can be affected by these changes
|
|
32
|
+
*
|
|
33
|
+
* @param {string[]} affectedLocalNames
|
|
34
|
+
*/
|
|
35
|
+
isInvalidatedByChildlistChanges(affectedLocalNames) {
|
|
36
|
+
// Scan for any XPath part that may jump over a node without checking what the type is. Can also
|
|
37
|
+
// be done dynamically, by listening for the `bucket` parameter of all the dom accessors
|
|
38
|
+
const flaggedConstructs = [
|
|
39
|
+
'//',
|
|
40
|
+
'ancestor',
|
|
41
|
+
'descendant',
|
|
42
|
+
'element(',
|
|
43
|
+
'*',
|
|
44
|
+
'..',
|
|
45
|
+
'following',
|
|
46
|
+
'preceding',
|
|
47
|
+
];
|
|
48
|
+
for (const xpath of this._xpaths) {
|
|
49
|
+
if (flaggedConstructs.some(c => xpath.includes(c))) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (affectedLocalNames.some(n => xpath.includes(n))) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// We can also depend on these elements if it was used in our ancestry
|
|
57
|
+
return !!this._parentDependencies?.isInvalidatedByChildlistChanges(affectedLocalNames);
|
|
58
|
+
}
|
|
59
|
+
|
|
28
60
|
/**
|
|
29
61
|
* Add an XPath to the dependencies
|
|
30
62
|
*
|