@jinntec/fore 3.2.0 → 3.3.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/README.md +6 -9
- package/dist/fore-dev.js +14269 -34575
- package/dist/fore.js +13418 -31471
- package/index.js +1 -0
- package/package.json +3 -2
- package/resources/fore.css +22 -0
- package/src/ForeElementMixin.js +18 -0
- package/src/actions/abstract-action.js +94 -24
- package/src/actions/fx-insert.js +2 -2
- package/src/fx-bind.js +28 -0
- package/src/fx-fore.js +151 -38
- package/src/fx-instance.js +51 -4
- package/src/fx-model.js +189 -4
- package/src/fx-submission.js +29 -0
- package/src/modelitem.js +35 -0
- package/src/ui/abstract-control.js +2 -1
- package/src/ui/fx-control.js +93 -1
- package/src/ui/fx-include.js +34 -2
- package/src/ui/fx-items.js +3 -3
- package/src/xpath-evaluation.js +43 -0
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jinntec/fore",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Fore - declarative user interfaces in plain HTML",
|
|
5
5
|
"module": "./index.js",
|
|
6
6
|
"publishConfig": {
|
|
@@ -93,7 +93,8 @@
|
|
|
93
93
|
"test:prune-snapshots": "karma start --prune-snapshots",
|
|
94
94
|
"test:bs": "karma start karma.bs.config.js --coverage",
|
|
95
95
|
"start:build": "cd dist && es-dev-server --open",
|
|
96
|
-
"build": "rimraf dist &&
|
|
96
|
+
"build": "rimraf dist && vite build --config vite.build.config.js && vite build --config vite.build-dev.config.js",
|
|
97
|
+
"build:rollup": "rimraf dist && rollup -c rollup.config.js",
|
|
97
98
|
"start": "vite --open --port 8090 --host",
|
|
98
99
|
"preversion": "npm run test",
|
|
99
100
|
"prepare": "npm run build",
|
package/resources/fore.css
CHANGED
|
@@ -66,6 +66,25 @@
|
|
|
66
66
|
display: block;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/*
|
|
70
|
+
* Native-constraint alert visibility: show fx-alert only after the user has
|
|
71
|
+
* interacted with the field (:user-invalid fires after first dirty interaction).
|
|
72
|
+
* Complements .visited[invalid] above, which covers Fore-driven (XPath) invalidity.
|
|
73
|
+
*/
|
|
74
|
+
fx-control:has(input.widget:user-invalid) fx-alert {
|
|
75
|
+
display: block;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
* Fallback for browsers without :user-invalid support.
|
|
80
|
+
* :placeholder-shown is false when a value is present, approximating "user typed".
|
|
81
|
+
*/
|
|
82
|
+
@supports not selector(input:user-invalid) {
|
|
83
|
+
fx-control[invalid]:has(input.widget:not(:placeholder-shown)) fx-alert {
|
|
84
|
+
display: block;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
69
88
|
/* case not displayed by default - if you want e.g. apply transitions you have to overwrite this rule with display='inline' or similar */
|
|
70
89
|
fx-case {
|
|
71
90
|
display: none;
|
|
@@ -73,6 +92,9 @@
|
|
|
73
92
|
fx-case.selected-case{
|
|
74
93
|
display:block;
|
|
75
94
|
}
|
|
95
|
+
fx-case.deselected-case{
|
|
96
|
+
display:none;
|
|
97
|
+
}
|
|
76
98
|
|
|
77
99
|
fx-output[readonly] img {
|
|
78
100
|
background: inherit;
|
package/src/ForeElementMixin.js
CHANGED
|
@@ -68,6 +68,24 @@ export default class ForeElementMixin extends HTMLElement {
|
|
|
68
68
|
this.ownerForm = null;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
getDebugInfo() {
|
|
72
|
+
return {
|
|
73
|
+
localName: this.localName,
|
|
74
|
+
id: this.id || null,
|
|
75
|
+
ref: this.getAttribute?.('ref') || null,
|
|
76
|
+
|
|
77
|
+
modelItemPath: this.modelItem?.path || null,
|
|
78
|
+
instanceId: this.modelItem?.instanceId || null,
|
|
79
|
+
|
|
80
|
+
value: this.modelItem?.value,
|
|
81
|
+
|
|
82
|
+
relevant: this.modelItem?.relevant,
|
|
83
|
+
readonly: this.modelItem?.readonly,
|
|
84
|
+
required: this.modelItem?.required,
|
|
85
|
+
constraint: this.modelItem?.constraint,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
71
89
|
connectedCallback() {
|
|
72
90
|
if (this.parentElement) {
|
|
73
91
|
this.dependencies.setParentDependencies(this.parentElement?.closest('[ref]')?.dependencies);
|
|
@@ -186,10 +186,69 @@ export class AbstractAction extends ForeElementMixin {
|
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
getActionDebugDetail(extra = {}) {
|
|
190
|
+
const getAttr = name => {
|
|
191
|
+
try {
|
|
192
|
+
return this.getAttribute(name) || null;
|
|
193
|
+
} catch (error) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
action: this.localName || null,
|
|
200
|
+
actionClass: this.constructor?.name || null,
|
|
201
|
+
id: this.id || null,
|
|
202
|
+
event: this.event || null,
|
|
203
|
+
ref: getAttr('ref'),
|
|
204
|
+
target: getAttr('target'),
|
|
205
|
+
origin: getAttr('origin'),
|
|
206
|
+
submission: getAttr('submission'),
|
|
207
|
+
control: getAttr('control'),
|
|
208
|
+
if: getAttr('if'),
|
|
209
|
+
while: getAttr('while'),
|
|
210
|
+
iterate: getAttr('iterate'),
|
|
211
|
+
delay: this.delay || 0,
|
|
212
|
+
needsUpdate: this.needsUpdate,
|
|
213
|
+
ownerFore: this.getOwnerFormSafe()?.id || null,
|
|
214
|
+
...extra,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
dispatchActionDebugEvent(type, extra = {}) {
|
|
219
|
+
try {
|
|
220
|
+
const target = this.isConnected ? this : (this.getOwnerFormSafe() ?? this);
|
|
221
|
+
target.dispatchEvent(
|
|
222
|
+
new CustomEvent(type, {
|
|
223
|
+
composed: true,
|
|
224
|
+
bubbles: true,
|
|
225
|
+
cancelable: false,
|
|
226
|
+
detail: this.getActionDebugDetail(extra),
|
|
227
|
+
}),
|
|
228
|
+
);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
// Debug events must never affect action execution.
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
getOwnerFormSafe() {
|
|
235
|
+
try {
|
|
236
|
+
return this.getOwnerForm?.() || null;
|
|
237
|
+
} catch (error) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
189
242
|
async performSafe() {
|
|
243
|
+
let success = false;
|
|
244
|
+
|
|
245
|
+
this.dispatchActionDebugEvent('action-start', {
|
|
246
|
+
phase: 'start',
|
|
247
|
+
});
|
|
248
|
+
|
|
190
249
|
try {
|
|
191
250
|
await this.perform();
|
|
192
|
-
|
|
251
|
+
success = true;
|
|
193
252
|
return true;
|
|
194
253
|
} catch (error) {
|
|
195
254
|
await Fore.dispatch(this, 'error', {
|
|
@@ -198,11 +257,15 @@ export class AbstractAction extends ForeElementMixin {
|
|
|
198
257
|
expr: error,
|
|
199
258
|
level: 'Error',
|
|
200
259
|
});
|
|
201
|
-
|
|
260
|
+
|
|
202
261
|
return false;
|
|
262
|
+
} finally {
|
|
263
|
+
this.dispatchActionDebugEvent('action-end', {
|
|
264
|
+
phase: 'end',
|
|
265
|
+
success,
|
|
266
|
+
});
|
|
203
267
|
}
|
|
204
268
|
}
|
|
205
|
-
|
|
206
269
|
/**
|
|
207
270
|
* executes the action.
|
|
208
271
|
*
|
|
@@ -263,8 +326,10 @@ export class AbstractAction extends ForeElementMixin {
|
|
|
263
326
|
|
|
264
327
|
// Outermost handling
|
|
265
328
|
if (FxFore.outermostHandler === null) {
|
|
329
|
+
const ownerForm = this.getOwnerFormSafe();
|
|
330
|
+
|
|
266
331
|
console.log(
|
|
267
|
-
`%coutermost Action on ${
|
|
332
|
+
`%coutermost Action on ${ownerForm?.id || ''}`,
|
|
268
333
|
'background:darkblue; color:white; padding:0.3rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;',
|
|
269
334
|
this,
|
|
270
335
|
);
|
|
@@ -319,7 +384,8 @@ export class AbstractAction extends ForeElementMixin {
|
|
|
319
384
|
if (this.delay) {
|
|
320
385
|
// Delay further execution until the delay is done
|
|
321
386
|
await wait(this.delay);
|
|
322
|
-
|
|
387
|
+
const ownerForm = this.getOwnerFormSafe();
|
|
388
|
+
if (!ownerForm || !XPathUtil.contains(ownerForm, this)) {
|
|
323
389
|
// We are no longer in the document. Stop working
|
|
324
390
|
this.actionPerformed();
|
|
325
391
|
resolveThisEvent();
|
|
@@ -336,7 +402,8 @@ export class AbstractAction extends ForeElementMixin {
|
|
|
336
402
|
// Start by waiting
|
|
337
403
|
await wait(this.delay || 0);
|
|
338
404
|
|
|
339
|
-
|
|
405
|
+
const ownerForm = this.getOwnerFormSafe();
|
|
406
|
+
if (!ownerForm || !XPathUtil.contains(ownerForm, this)) {
|
|
340
407
|
// We are no longer in the document. Stop working
|
|
341
408
|
return;
|
|
342
409
|
}
|
|
@@ -347,7 +414,7 @@ export class AbstractAction extends ForeElementMixin {
|
|
|
347
414
|
}
|
|
348
415
|
|
|
349
416
|
// Perform the action once. But quit if it failed
|
|
350
|
-
if (!this.performSafe()) {
|
|
417
|
+
if (!(await this.performSafe())) {
|
|
351
418
|
return;
|
|
352
419
|
}
|
|
353
420
|
|
|
@@ -373,7 +440,8 @@ export class AbstractAction extends ForeElementMixin {
|
|
|
373
440
|
return;
|
|
374
441
|
}
|
|
375
442
|
|
|
376
|
-
|
|
443
|
+
const ownerForm = this.getOwnerFormSafe();
|
|
444
|
+
if (!ownerForm || !XPathUtil.contains(ownerForm, this)) {
|
|
377
445
|
// We are no longer in the document. Stop working
|
|
378
446
|
return;
|
|
379
447
|
}
|
|
@@ -400,8 +468,10 @@ export class AbstractAction extends ForeElementMixin {
|
|
|
400
468
|
this.currentEvent = null;
|
|
401
469
|
this.actionPerformed();
|
|
402
470
|
if (FxFore.outermostHandler === this) {
|
|
471
|
+
const ownerForm = this.getOwnerFormSafe();
|
|
472
|
+
|
|
403
473
|
console.log(
|
|
404
|
-
`%cfinalizing outermost Action on ${
|
|
474
|
+
`%cfinalizing outermost Action on ${ownerForm?.id || ''}`,
|
|
405
475
|
'background:darkblue; color:white; padding:0.3rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;',
|
|
406
476
|
this,
|
|
407
477
|
);
|
|
@@ -430,24 +500,22 @@ export class AbstractAction extends ForeElementMixin {
|
|
|
430
500
|
* Template method to be implemented by each action that is called by execute() as part of
|
|
431
501
|
* the processing.
|
|
432
502
|
*
|
|
433
|
-
* This function should not called on any action directly - call execute() instead to ensure proper execution of 'if' and 'while'
|
|
503
|
+
* This function should not called on any action directly - call execute() instead to ensure proper execution of 'if' and 'while'.
|
|
504
|
+
*
|
|
505
|
+
* TODO Fore DevTools:
|
|
506
|
+
* Concrete actions overriding perform() should call `await super.perform()` at the start.
|
|
507
|
+
* Otherwise the debugger will not see the `execute-action` event for that action.
|
|
434
508
|
*/
|
|
435
509
|
async perform() {
|
|
436
|
-
// await Fore.dispatch(document, 'execute-action', {action:this, event:this.event});
|
|
437
|
-
|
|
438
|
-
// todo: review - this evaluation seems redundant as we already evaluated in execute
|
|
439
510
|
if (this.isBound() || this.nodeName === 'FX-ACTION') {
|
|
440
511
|
this.evalInContext();
|
|
441
512
|
}
|
|
442
513
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
detail: { action: this, event: this.event },
|
|
449
|
-
}),
|
|
450
|
-
);
|
|
514
|
+
/*
|
|
515
|
+
await Fore.dispatch(this, 'execute-action', this.getActionDebugDetail({
|
|
516
|
+
phase: 'before',
|
|
517
|
+
}));
|
|
518
|
+
*/
|
|
451
519
|
}
|
|
452
520
|
|
|
453
521
|
/**
|
|
@@ -475,7 +543,8 @@ export class AbstractAction extends ForeElementMixin {
|
|
|
475
543
|
// console.log('running update cycle for outermostHandler', this);
|
|
476
544
|
model.recalculate();
|
|
477
545
|
model.revalidate();
|
|
478
|
-
this.
|
|
546
|
+
const ownerForm = this.getOwnerFormSafe();
|
|
547
|
+
ownerForm?.refresh(false);
|
|
479
548
|
this.dispatchActionPerformed();
|
|
480
549
|
} else if (this.needsUpdate) {
|
|
481
550
|
// console.log('Update delayed!');
|
|
@@ -493,8 +562,9 @@ export class AbstractAction extends ForeElementMixin {
|
|
|
493
562
|
* @event action-performed - whenever an action has been run
|
|
494
563
|
*/
|
|
495
564
|
dispatchActionPerformed() {
|
|
496
|
-
|
|
497
|
-
|
|
565
|
+
Fore.dispatch(this, 'action-performed', this.getActionDebugDetail({
|
|
566
|
+
phase: 'after',
|
|
567
|
+
}));
|
|
498
568
|
}
|
|
499
569
|
}
|
|
500
570
|
|
package/src/actions/fx-insert.js
CHANGED
|
@@ -413,7 +413,7 @@ export class FxInsert extends AbstractAction {
|
|
|
413
413
|
xpath,
|
|
414
414
|
});
|
|
415
415
|
|
|
416
|
-
|
|
416
|
+
this.dispatchEvent(
|
|
417
417
|
new CustomEvent('index-changed', {
|
|
418
418
|
composed: true,
|
|
419
419
|
bubbles: true,
|
|
@@ -674,7 +674,7 @@ export class FxInsert extends AbstractAction {
|
|
|
674
674
|
xpath,
|
|
675
675
|
});
|
|
676
676
|
|
|
677
|
-
|
|
677
|
+
this.dispatchEvent(
|
|
678
678
|
new CustomEvent('index-changed', {
|
|
679
679
|
composed: true,
|
|
680
680
|
bubbles: true,
|
package/src/fx-bind.js
CHANGED
|
@@ -45,6 +45,34 @@ export class FxBind extends ForeElementMixin {
|
|
|
45
45
|
this.inited = false;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
getDebugInfo() {
|
|
49
|
+
return {
|
|
50
|
+
ref: this.getAttribute('ref'),
|
|
51
|
+
id: this.id || null,
|
|
52
|
+
instanceId: this.instanceId,
|
|
53
|
+
bindType: this.bindType,
|
|
54
|
+
|
|
55
|
+
calculate: this.getAttribute('calculate'),
|
|
56
|
+
readonly: this.getAttribute('readonly'),
|
|
57
|
+
required: this.getAttribute('required'),
|
|
58
|
+
relevant: this.getAttribute('relevant'),
|
|
59
|
+
constraint: this.getAttribute('constraint'),
|
|
60
|
+
type: this.getAttribute('type'),
|
|
61
|
+
|
|
62
|
+
modelItemCount: this._debugModelItemCount?.() || null,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
_debugModelItemCount() {
|
|
67
|
+
const model = this.getModel?.();
|
|
68
|
+
|
|
69
|
+
if (!model || !Array.isArray(model.modelItems)) {
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return model.modelItems.filter(item => item?.bind === this).length;
|
|
74
|
+
}
|
|
75
|
+
|
|
48
76
|
connectedCallback() {
|
|
49
77
|
// console.log('connectedCallback ', this);
|
|
50
78
|
// this.id = this.hasAttribute('id')?this.getAttribute('id'):;
|