@jinntec/fore 2.0.0 → 2.1.1
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 +2 -2
- package/dist/fore-dev.js.map +1 -1
- package/dist/fore.js +2 -2
- package/dist/fore.js.map +1 -1
- package/index.js +3 -0
- package/package.json +1 -1
- package/src/actions/abstract-action.js +100 -66
- package/src/actions/fx-confirm.js +3 -3
- package/src/actions/fx-construct-done.js +28 -0
- package/src/actions/fx-insert.js +53 -51
- package/src/actions/fx-setattribute.js +68 -0
- package/src/fore.js +3 -1
- package/src/fx-bind.js +1 -0
- package/src/fx-fore.js +48 -1
- package/src/fx-model.js +6 -3
- package/src/fx-submission.js +24 -4
- package/src/fx-var.js +7 -2
- package/src/getInScopeContext.js +11 -1
- package/src/json-util.js +27 -0
- package/src/relevance.js +18 -0
- package/src/ui/abstract-control.js +2 -0
- package/src/ui/fx-droptarget.js +9 -0
- package/src/ui/fx-repeat-attributes.js +4 -2
- package/src/ui/fx-repeat.js +366 -356
- package/src/ui/fx-repeatitem.js +78 -86
- package/src/ui/fx-trigger.js +3 -2
- package/src/withDraggability.js +218 -0
- package/src/xpath-evaluation.js +140 -1
package/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import './src/ui/fx-case.js';
|
|
|
22
22
|
import './src/ui/fx-inspector.js';
|
|
23
23
|
import './src/ui/fx-dialog.js';
|
|
24
24
|
import './src/ui/fx-items.js';
|
|
25
|
+
import './src/ui/fx-droptarget.js';
|
|
25
26
|
|
|
26
27
|
import './src/tools/fx-action-log.js';
|
|
27
28
|
import './src/tools/fx-log-settings.js';
|
|
@@ -57,6 +58,8 @@ import './src/actions/fx-reset.js';
|
|
|
57
58
|
import './src/actions/fx-load.js';
|
|
58
59
|
import './src/actions/fx-toggleboolean.js';
|
|
59
60
|
import './src/actions/fx-call.js';
|
|
61
|
+
import './src/actions/fx-setattribute.js';
|
|
62
|
+
import './src/actions/fx-construct-done.js';
|
|
60
63
|
|
|
61
64
|
import './src/functions/fx-function.js';
|
|
62
65
|
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {foreElementMixin} from '../ForeElementMixin.js';
|
|
2
|
-
import {evaluateXPathToBoolean, resolveId} from '../xpath-evaluation.js';
|
|
2
|
+
import {evaluateXPathToBoolean, resolveId, evaluateXPath} from '../xpath-evaluation.js';
|
|
3
3
|
import getInScopeContext from '../getInScopeContext.js';
|
|
4
4
|
import {Fore} from '../fore.js';
|
|
5
5
|
import {FxFore} from '../fx-fore.js';
|
|
@@ -56,6 +56,18 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
56
56
|
ifExpr: {
|
|
57
57
|
type: String,
|
|
58
58
|
},
|
|
59
|
+
/**
|
|
60
|
+
* The iterate attribute can be added to any XForms action. It contains an expression
|
|
61
|
+
* that is evaluated once using the in-scope evaluation context before the action is
|
|
62
|
+
* executed, which will result in a sequence of items. The action will be executed with
|
|
63
|
+
* each item in the sequence as its context. This context replaces the default in scope
|
|
64
|
+
* evaluation context.
|
|
65
|
+
*
|
|
66
|
+
* The interaction with `@while` and `@if` is undefined.
|
|
67
|
+
*/
|
|
68
|
+
iterateExpr: {
|
|
69
|
+
type: String
|
|
70
|
+
},
|
|
59
71
|
/**
|
|
60
72
|
* whether nor not an action needs to run the update cycle
|
|
61
73
|
*/
|
|
@@ -88,8 +100,8 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
88
100
|
type: String,
|
|
89
101
|
},
|
|
90
102
|
/**
|
|
91
|
-
* boolean XPath expression. If true loop will be executed. If an ifExpr is present this
|
|
92
|
-
* to actually run the action.
|
|
103
|
+
* boolean XPath expression. If true loop will be executed. If an ifExpr is present this
|
|
104
|
+
* also needs to be true to actually run the action.
|
|
93
105
|
*/
|
|
94
106
|
whileExpr: {
|
|
95
107
|
type: String,
|
|
@@ -103,6 +115,9 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
103
115
|
this.needsUpdate = false;
|
|
104
116
|
}
|
|
105
117
|
|
|
118
|
+
disconnectedCallback() {
|
|
119
|
+
}
|
|
120
|
+
|
|
106
121
|
connectedCallback() {
|
|
107
122
|
this.style.display = 'none';
|
|
108
123
|
this.propagate = this.hasAttribute('propagate') ? this.getAttribute('propagate') : 'continue';
|
|
@@ -132,6 +147,7 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
132
147
|
this.ifExpr = this.hasAttribute('if') ? this.getAttribute('if') : null;
|
|
133
148
|
this.whileExpr = this.hasAttribute('while') ? this.getAttribute('while') : null;
|
|
134
149
|
this.delay = this.hasAttribute('delay') ? Number(this.getAttribute('delay')) : 0;
|
|
150
|
+
this.iterateExpr = this.hasAttribute('iterate') ? this.getAttribute('iterate') : null;
|
|
135
151
|
|
|
136
152
|
this._addUpdateListener();
|
|
137
153
|
|
|
@@ -186,8 +202,6 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
186
202
|
*/
|
|
187
203
|
async execute(e) {
|
|
188
204
|
console.log(this, this.event);
|
|
189
|
-
// console.log('execute', this.event);
|
|
190
|
-
|
|
191
205
|
|
|
192
206
|
if (e && e.target.nodeType !== Node.DOCUMENT_NODE && e.target !== window ){
|
|
193
207
|
/*
|
|
@@ -219,19 +233,8 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
219
233
|
);
|
|
220
234
|
}
|
|
221
235
|
|
|
222
|
-
|
|
223
|
-
// console.log('executing e', e);
|
|
224
|
-
// console.log('executing e phase', e.eventPhase);
|
|
236
|
+
// Outermost handling
|
|
225
237
|
if (FxFore.outermostHandler === null) {
|
|
226
|
-
// console.time('outermostHandler');
|
|
227
|
-
/*
|
|
228
|
-
console.info(
|
|
229
|
-
`%coutermost Action `,
|
|
230
|
-
'background:#e65100; color:white; padding:0.3rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;',
|
|
231
|
-
this,
|
|
232
|
-
);
|
|
233
|
-
*/
|
|
234
|
-
// console.log('starting outermost handler',this);
|
|
235
238
|
FxFore.outermostHandler = this;
|
|
236
239
|
this.dispatchEvent(new CustomEvent('outermost-action-start', {
|
|
237
240
|
composed: true,
|
|
@@ -240,16 +243,6 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
240
243
|
}));
|
|
241
244
|
}
|
|
242
245
|
|
|
243
|
-
/*
|
|
244
|
-
if (FxFore.outermostHandler !== this) {
|
|
245
|
-
console.info(
|
|
246
|
-
`%cAction `,
|
|
247
|
-
'background:orange; color:white; padding:0.3rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;',
|
|
248
|
-
this,
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
*/
|
|
252
|
-
// console.log('>>> outermostHandler', FxFore.outermostHandler);
|
|
253
246
|
|
|
254
247
|
if (e) {
|
|
255
248
|
this.currentEvent = e;
|
|
@@ -265,51 +258,26 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
265
258
|
this.nodeset = this.targetElement.nodeset;
|
|
266
259
|
}
|
|
267
260
|
|
|
268
|
-
|
|
261
|
+
// Order of application between if / while and iterate is undefined. See
|
|
262
|
+
// https://www.w3.org/MarkUp/Forms/wiki/@iterate
|
|
263
|
+
if (this.iterateExpr) {
|
|
264
|
+
// Same as whileExpr, let it go update UI afterwards
|
|
265
|
+
await this.handleIterateExpr();
|
|
266
|
+
this._finalizePerform(resolveThisEvent);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Check if 'if' condition is true - otherwise exist right away
|
|
269
271
|
if (this.ifExpr && !evaluateXPathToBoolean(this.ifExpr, getInScopeContext(this), this)) {
|
|
270
272
|
this._finalizePerform(resolveThisEvent);
|
|
271
273
|
return;
|
|
272
274
|
}
|
|
273
275
|
|
|
274
|
-
// console.log('performing action', this);
|
|
275
|
-
|
|
276
276
|
if (this.whileExpr) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (!XPathUtil.contains(this.getOwnerForm(), this)) {
|
|
283
|
-
// We are no longer in the document. Stop working
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (!evaluateXPathToBoolean(this.whileExpr, getInScopeContext(this), this)) {
|
|
288
|
-
// Done with iterating
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Perform the action once. But quit if it errored
|
|
293
|
-
if (!this.performSafe()) {
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Go for one more iteration
|
|
298
|
-
if (this.delay) {
|
|
299
|
-
// If we have a delay, fire and forget this.
|
|
300
|
-
// Otherwise, if we have no delay, keep waiting for all iterations to be done.
|
|
301
|
-
// The while is then uninterruptable and immediate
|
|
302
|
-
loop();
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
await loop();
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
// After loop is done call actionPerformed to update the model and UI
|
|
309
|
-
await loop();
|
|
310
|
-
this._finalizePerform(resolveThisEvent);
|
|
311
|
-
|
|
312
|
-
return;
|
|
277
|
+
// After loop is done call actionPerformed to update the model and UI
|
|
278
|
+
await this.handleWhileExpr();
|
|
279
|
+
this._finalizePerform(resolveThisEvent);
|
|
280
|
+
return;
|
|
313
281
|
}
|
|
314
282
|
|
|
315
283
|
if (this.delay) {
|
|
@@ -327,6 +295,72 @@ export class AbstractAction extends foreElementMixin(HTMLElement) {
|
|
|
327
295
|
this._finalizePerform(resolveThisEvent);
|
|
328
296
|
}
|
|
329
297
|
|
|
298
|
+
async handleWhileExpr () {
|
|
299
|
+
// While: while the condition is true, delay a bit and execute the action
|
|
300
|
+
// Start by waiting
|
|
301
|
+
await wait(this.delay || 0);
|
|
302
|
+
|
|
303
|
+
if (!XPathUtil.contains(this.getOwnerForm(), this)) {
|
|
304
|
+
// We are no longer in the document. Stop working
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (!evaluateXPathToBoolean(this.whileExpr, getInScopeContext(this), this)) {
|
|
309
|
+
// Done with iterating
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Perform the action once. But quit if it failed
|
|
314
|
+
if (!this.performSafe()) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Go for one more iteration
|
|
319
|
+
if (this.delay) {
|
|
320
|
+
// If we have a delay, fire and forget this.
|
|
321
|
+
// Otherwise, if we have no delay, keep waiting for all iterations to be done.
|
|
322
|
+
// The while is then uninterruptable and immediate
|
|
323
|
+
|
|
324
|
+
this.handleWhileExpr();
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
await this.handleWhileExpr();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async handleIterateExpr () {
|
|
333
|
+
try {
|
|
334
|
+
// Iterate: get the context sequence and perform the action once per item.
|
|
335
|
+
const contextSequence = evaluateXPath(this.iterateExpr, getInScopeContext(this), this);
|
|
336
|
+
|
|
337
|
+
if (contextSequence.length === 0) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (!XPathUtil.contains(this.getOwnerForm(), this)) {
|
|
342
|
+
// We are no longer in the document. Stop working
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
for (const item of contextSequence) {
|
|
347
|
+
if (this.delay) {
|
|
348
|
+
await wait(this.delay || 0);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// This will be picked up in `getInscopeContext`
|
|
352
|
+
this.currentContext = item;
|
|
353
|
+
|
|
354
|
+
// Perform the action once. But quit if it failed
|
|
355
|
+
if (!await this.performSafe()) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
} finally {
|
|
360
|
+
this.currentContext = null;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
330
364
|
_finalizePerform(resolveThisEvent) {
|
|
331
365
|
this.currentEvent = null;
|
|
332
366
|
this.actionPerformed();
|
|
@@ -18,15 +18,15 @@ export class FxConfirm extends FxAction {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
connectedCallback() {
|
|
21
|
-
if(super.connectedCallback){
|
|
22
|
-
|
|
21
|
+
if (super.connectedCallback){
|
|
22
|
+
super.connectedCallback();
|
|
23
23
|
}
|
|
24
24
|
this.message = this.hasAttribute('message') ? this.getAttribute('message') : null;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
async perform() {
|
|
28
28
|
if (window.confirm(this.message)) {
|
|
29
|
-
super.perform();
|
|
29
|
+
await super.perform();
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AbstractAction } from './abstract-action.js';
|
|
2
|
+
import {FxAction} from "./fx-action.js";
|
|
3
|
+
import {Fore} from "../fore.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `fx-action`
|
|
7
|
+
* an action to wrap other actions and defers the update cycle until the end of the block.
|
|
8
|
+
*
|
|
9
|
+
* @customElement
|
|
10
|
+
* @demo demo/index.html
|
|
11
|
+
*/
|
|
12
|
+
export class FxConstructDone extends FxAction {
|
|
13
|
+
connectedCallback() {
|
|
14
|
+
// eslint-disable-next-line wc/guard-super-call
|
|
15
|
+
super.connectedCallback();
|
|
16
|
+
console.log('parentNode', this.parentNode);
|
|
17
|
+
if(this.parentNode.nodeName !== 'FX-MODEL'){
|
|
18
|
+
Fore.dispatch(this, 'error', {message:'parent is not a model'})
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
this.parentNode.addEventListener('model-construct-done', e => {
|
|
22
|
+
super.perform();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (!customElements.get('fx-construct-done')) {
|
|
27
|
+
window.customElements.define('fx-construct-done', FxConstructDone);
|
|
28
|
+
}
|
package/src/actions/fx-insert.js
CHANGED
|
@@ -15,23 +15,23 @@ import {Fore} from '../fore.js';
|
|
|
15
15
|
* @customElement
|
|
16
16
|
*/
|
|
17
17
|
export class FxInsert extends AbstractAction {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
18
|
+
static get properties() {
|
|
19
|
+
return {
|
|
20
|
+
...super.properties,
|
|
21
|
+
at: {
|
|
22
|
+
type: Number,
|
|
23
|
+
},
|
|
24
|
+
position: {
|
|
25
|
+
type: Number,
|
|
26
|
+
},
|
|
27
|
+
origin: {
|
|
28
|
+
type: Object,
|
|
29
|
+
},
|
|
30
|
+
keepValues: {
|
|
31
|
+
type: Boolean,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
35
|
|
|
36
36
|
constructor() {
|
|
37
37
|
super();
|
|
@@ -127,26 +127,23 @@ export class FxInsert extends AbstractAction {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
async perform() {
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
/*
|
|
135
|
-
todo: !!! calling super here does not correctly give the nodeset - it's likely still a bug in ForeElementMixin !!!
|
|
136
|
-
// super.perform();
|
|
137
|
-
console.log('this.nodeset', this.nodeset);
|
|
138
|
-
*/
|
|
139
|
-
|
|
130
|
+
// We have a few terms here: `inScope` is the 'current item' we have. It is the item we're
|
|
131
|
+
// copying and inserting elsewhere. If we have a `ref`, one of the nodes returned will
|
|
132
|
+
// become the sibling of this copy. The `context` is the new parent of the copied
|
|
133
|
+
// element. It's usually better to add a `context` because that deals with empty elements.
|
|
140
134
|
let inscope;
|
|
135
|
+
let context;
|
|
136
|
+
let targetSequence = [];
|
|
137
|
+
const inscopeContext = getInScopeContext(this);
|
|
138
|
+
|
|
141
139
|
// ### 'context' attribute takes precedence over 'ref'
|
|
142
|
-
let targetSequence;
|
|
143
140
|
if (this.hasAttribute('context')) {
|
|
144
|
-
|
|
145
|
-
targetSequence = evaluateXPathToNodes(
|
|
141
|
+
[context] = evaluateXPathToNodes(
|
|
146
142
|
this.getAttribute('context'),
|
|
147
|
-
|
|
143
|
+
inscopeContext,
|
|
148
144
|
this.getOwnerForm(),
|
|
149
145
|
);
|
|
146
|
+
inscope = inscopeContext;
|
|
150
147
|
}
|
|
151
148
|
|
|
152
149
|
if (this.hasAttribute('ref')) {
|
|
@@ -163,14 +160,19 @@ export class FxInsert extends AbstractAction {
|
|
|
163
160
|
let insertLocationNode;
|
|
164
161
|
let index;
|
|
165
162
|
|
|
166
|
-
// const idx = this._getInsertIndex(inscope, targetSequence);
|
|
167
|
-
// console.log('insert index', idx);
|
|
168
|
-
|
|
169
163
|
// if the targetSequence is empty but we got an originSequence use inscope as context and ignore 'at' and 'position'
|
|
170
164
|
if (targetSequence.length === 0) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
165
|
+
if (context) {
|
|
166
|
+
insertLocationNode = context;
|
|
167
|
+
context.appendChild(originSequenceClone);
|
|
168
|
+
index = 1;
|
|
169
|
+
} else {
|
|
170
|
+
// No context. We can insert into the `inscope`.
|
|
171
|
+
insertLocationNode = inscope;
|
|
172
|
+
inscope.appendChild(originSequenceClone);
|
|
173
|
+
index = 1;
|
|
174
|
+
|
|
175
|
+
}
|
|
174
176
|
} else {
|
|
175
177
|
/* ### insert at position given by 'at' or use the last item in the targetSequence ### */
|
|
176
178
|
if (this.hasAttribute('at')) {
|
|
@@ -221,31 +223,31 @@ export class FxInsert extends AbstractAction {
|
|
|
221
223
|
}
|
|
222
224
|
}
|
|
223
225
|
}
|
|
224
|
-
|
|
226
|
+
// instance('default')/items/item[index()]
|
|
225
227
|
|
|
226
228
|
// console.log('insert context item ', insertLocationNode);
|
|
227
229
|
// console.log('parent ', insertLocationNode.parentNode);
|
|
228
230
|
// console.log('instance ', this.getModel().getDefaultContext());
|
|
229
231
|
// Fore.dispatch()
|
|
230
232
|
|
|
231
|
-
|
|
233
|
+
// const instanceId = XPathUtil.resolveInstance(this, this.getAttribute('context'));
|
|
232
234
|
const instanceId = XPathUtil.resolveInstance(this, this.ref);
|
|
233
235
|
const inst = this.getModel().getInstance(instanceId);
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
236
|
+
// console.log('<<<<<<< resolved instance', inst);
|
|
237
|
+
// Note: the parent to insert under is always the parent of the inserted node. The 'context' is not always the parent if the sequence is empty, or the position is different
|
|
238
|
+
// const xpath = XPathUtil.getPath(originSequenceClone.parentNode, instanceId);
|
|
237
239
|
const xpath = XPathUtil.getPath(insertLocationNode.parentNode, instanceId);
|
|
238
240
|
|
|
239
241
|
|
|
240
242
|
|
|
241
243
|
const path = Fore.getDomNodeIndexString(originSequenceClone);
|
|
242
244
|
this.dispatchEvent(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
245
|
+
new CustomEvent('execute-action', {
|
|
246
|
+
composed: true,
|
|
247
|
+
bubbles: true,
|
|
248
|
+
cancelable:true,
|
|
249
|
+
detail: { action: this, event:this.event, path },
|
|
250
|
+
}),
|
|
249
251
|
);
|
|
250
252
|
|
|
251
253
|
Fore.dispatch(inst,'insert',{
|
|
@@ -267,9 +269,9 @@ export class FxInsert extends AbstractAction {
|
|
|
267
269
|
}),
|
|
268
270
|
);
|
|
269
271
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
272
|
+
this.needsUpdate = true;
|
|
273
|
+
console.log('Changed!', xpath)
|
|
274
|
+
return [xpath];
|
|
273
275
|
}
|
|
274
276
|
|
|
275
277
|
// eslint-disable-next-line class-methods-use-this
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// import { FxAction } from './fx-action.js';
|
|
2
|
+
import '../fx-model.js';
|
|
3
|
+
import {AbstractAction} from './abstract-action.js';
|
|
4
|
+
import {evaluateXPathToString} from '../xpath-evaluation.js';
|
|
5
|
+
import {Fore} from '../fore.js';
|
|
6
|
+
import getInScopeContext from "../getInScopeContext";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* `fx-setattribute` allows to create and set an attribute value in the data.
|
|
10
|
+
*
|
|
11
|
+
* @customElement
|
|
12
|
+
*/
|
|
13
|
+
export default class FxSetattribute extends AbstractAction {
|
|
14
|
+
static get properties() {
|
|
15
|
+
return {
|
|
16
|
+
...super.properties,
|
|
17
|
+
ref: {
|
|
18
|
+
type: String,
|
|
19
|
+
},
|
|
20
|
+
attrName: {
|
|
21
|
+
type: String,
|
|
22
|
+
},
|
|
23
|
+
attrValue:{
|
|
24
|
+
type: String
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
constructor() {
|
|
30
|
+
super();
|
|
31
|
+
this.ref = '';
|
|
32
|
+
this.attrName = '';
|
|
33
|
+
this.attrValue = '';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
connectedCallback() {
|
|
37
|
+
if (super.connectedCallback) {
|
|
38
|
+
super.connectedCallback();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (this.hasAttribute('ref')) {
|
|
42
|
+
this.ref = this.getAttribute('ref');
|
|
43
|
+
} else {
|
|
44
|
+
throw new Error('fx-setvalue must specify a "ref" attribute');
|
|
45
|
+
}
|
|
46
|
+
this.attrName = this.hasAttribute('name') ? this.getAttribute('name') : null;
|
|
47
|
+
this.attrValue = this.hasAttribute('value') ? this.getAttribute('value') : '';
|
|
48
|
+
if(!this.attrName){
|
|
49
|
+
Fore.dispatch('this', 'error',{message:'name or value not specified'});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async perform() {
|
|
54
|
+
super.perform();
|
|
55
|
+
const mi = this.getModelItem();
|
|
56
|
+
if(mi.node.nodeType !== Node.ELEMENT_NODE){
|
|
57
|
+
Fore.dispatch('this', 'error',{message:'referenced item is not an element'});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
mi.node.setAttribute(this.attrName, this.attrValue);
|
|
61
|
+
this.needsUpdate = true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!customElements.get('fx-setattribute')) {
|
|
67
|
+
window.customElements.define('fx-setattribute', FxSetattribute);
|
|
68
|
+
}
|
package/src/fore.js
CHANGED
|
@@ -18,7 +18,7 @@ export class Fore {
|
|
|
18
18
|
|
|
19
19
|
static TYPE_DEFAULT = 'xs:string';
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
22
|
* Loads and return a piece of HTML
|
|
23
23
|
* @param url - the Url to load from
|
|
24
24
|
* @returns {Promise<string>}
|
|
@@ -412,6 +412,7 @@ export class Fore {
|
|
|
412
412
|
return 'done';
|
|
413
413
|
}
|
|
414
414
|
|
|
415
|
+
/*
|
|
415
416
|
static evaluateAttributeTemplateExpression(expr, node) {
|
|
416
417
|
const matches = expr.match(/{[^}]*}/g);
|
|
417
418
|
if (matches) {
|
|
@@ -427,6 +428,7 @@ export class Fore {
|
|
|
427
428
|
}
|
|
428
429
|
return expr;
|
|
429
430
|
}
|
|
431
|
+
*/
|
|
430
432
|
|
|
431
433
|
static fadeInElement(element) {
|
|
432
434
|
const duration = 600;
|
package/src/fx-bind.js
CHANGED
package/src/fx-fore.js
CHANGED
|
@@ -26,6 +26,7 @@ import {FxRepeatAttributes} from './ui/fx-repeat-attributes.js';
|
|
|
26
26
|
*/
|
|
27
27
|
export class FxFore extends HTMLElement {
|
|
28
28
|
static outermostHandler = null;
|
|
29
|
+
static draggedItem = null;
|
|
29
30
|
|
|
30
31
|
static get properties() {
|
|
31
32
|
return {
|
|
@@ -103,6 +104,8 @@ export class FxFore extends HTMLElement {
|
|
|
103
104
|
// updates are included in that one
|
|
104
105
|
this.outermostHandler = null;
|
|
105
106
|
|
|
107
|
+
this.copiedElements = new WeakSet();
|
|
108
|
+
|
|
106
109
|
const style = `
|
|
107
110
|
:host {
|
|
108
111
|
display: block;
|
|
@@ -179,7 +182,6 @@ export class FxFore extends HTMLElement {
|
|
|
179
182
|
`;
|
|
180
183
|
|
|
181
184
|
const html = `
|
|
182
|
-
<noscript>This page uses Web Components and needs JavaScript to be enabled..</noscript>
|
|
183
185
|
<!-- <slot name="errors"></slot> -->
|
|
184
186
|
<jinn-toast id="message" gravity="bottom" position="left"></jinn-toast>
|
|
185
187
|
<jinn-toast id="sticky" gravity="bottom" position="left" duration="-1" close="true" data-class="sticky-message"></jinn-toast>
|
|
@@ -390,6 +392,7 @@ export class FxFore extends HTMLElement {
|
|
|
390
392
|
}
|
|
391
393
|
|
|
392
394
|
disconnectedCallback() {
|
|
395
|
+
this.removeEventListener('dragstart', this.dragstart);
|
|
393
396
|
/*
|
|
394
397
|
this.removeEventListener('model-construct-done', this._handleModelConstructDone);
|
|
395
398
|
this.removeEventListener('message', this._displayMessage);
|
|
@@ -530,6 +533,7 @@ export class FxFore extends HTMLElement {
|
|
|
530
533
|
this._updateTemplateExpressions();
|
|
531
534
|
this._scanForNewTemplateExpressionsNextRefresh = false; // reset
|
|
532
535
|
}
|
|
536
|
+
|
|
533
537
|
this._processTemplateExpressions();
|
|
534
538
|
|
|
535
539
|
// console.log('### <<<<< dispatching refresh-done - end of UI update cycle >>>>>');
|
|
@@ -931,8 +935,51 @@ export class FxFore extends HTMLElement {
|
|
|
931
935
|
Fore.dispatch(this, 'ready', {});
|
|
932
936
|
// console.log('dataChanged', FxModel.dataChanged);
|
|
933
937
|
console.timeEnd('init');
|
|
938
|
+
|
|
939
|
+
this.addEventListener('dragstart', this._handleDragStart);
|
|
940
|
+
// this.addEventListener('dragend', this._handleDragEnd);
|
|
941
|
+
this.handleDrop = event => this._handleDrop(event);
|
|
942
|
+
this.ownerDocument.body.addEventListener('drop', this.handleDrop);
|
|
943
|
+
this.ownerDocument.body.addEventListener('dragover', e=>{
|
|
944
|
+
e.preventDefault();
|
|
945
|
+
e.stopPropagation();
|
|
946
|
+
e.dataTransfer.dropEffect = "move";
|
|
947
|
+
});
|
|
934
948
|
}
|
|
935
949
|
|
|
950
|
+
_handleDragStart (event) {
|
|
951
|
+
const draggedItem = event.target.closest('[draggable="true"]');
|
|
952
|
+
this.originalDraggedItem = draggedItem;
|
|
953
|
+
console.log('DRAG START', this);
|
|
954
|
+
if (draggedItem.getAttribute('drop-action') === 'copy') {
|
|
955
|
+
event.dataTransfer.dropEffect = 'copy';
|
|
956
|
+
event.dataTransfer.effectAllowed = 'copy';
|
|
957
|
+
this.draggedItem = draggedItem.cloneNode(true);
|
|
958
|
+
this.draggedItem.setAttribute('drop-action', 'move');
|
|
959
|
+
this.copiedElements.add(this.draggedItem);
|
|
960
|
+
} else {
|
|
961
|
+
event.dataTransfer.dropEffect = 'move';
|
|
962
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
963
|
+
this.draggedItem = draggedItem;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
_handleDrop (event) {
|
|
968
|
+
console.log('DROP ON BODY', this)
|
|
969
|
+
if (!this.draggedItem) {
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
// A drop on 'body' should be a removal.
|
|
973
|
+
if (event.dataTransfer.dropEffect === 'none') {
|
|
974
|
+
if (this.copiedElements.has(this.originalDraggedItem)) {
|
|
975
|
+
this.originalDraggedItem.remove();
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
this.originalDraggedItem = null
|
|
979
|
+
this.draggedItem = null;
|
|
980
|
+
event.stopPropagation();
|
|
981
|
+
}
|
|
982
|
+
|
|
936
983
|
registerLazyElement(element) {
|
|
937
984
|
if (this.intersectionObserver) {
|
|
938
985
|
// console.log('registerLazyElement',element);
|