@jinntec/fore 1.0.0-5 → 1.2.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.
Files changed (64) hide show
  1. package/README.md +7 -28
  2. package/dist/fore-dev.js +43 -0
  3. package/dist/fore-dev.js.map +1 -0
  4. package/dist/fore.js +37 -0
  5. package/dist/fore.js.map +1 -0
  6. package/index.js +3 -1
  7. package/package.json +39 -41
  8. package/resources/fore.css +27 -54
  9. package/src/DependencyNotifyingDomFacade.js +5 -13
  10. package/src/ForeElementMixin.js +15 -22
  11. package/src/actions/abstract-action.js +34 -10
  12. package/src/actions/fx-action.js +7 -5
  13. package/src/actions/fx-append.js +8 -17
  14. package/src/actions/fx-confirm.js +5 -3
  15. package/src/actions/fx-delete.js +6 -3
  16. package/src/actions/fx-dispatch.js +9 -8
  17. package/src/actions/fx-hide.js +9 -6
  18. package/src/actions/fx-insert.js +27 -14
  19. package/src/actions/fx-message.js +3 -1
  20. package/src/actions/fx-refresh.js +24 -1
  21. package/src/actions/fx-replace.js +74 -0
  22. package/src/actions/fx-return.js +42 -0
  23. package/src/actions/fx-send.js +3 -1
  24. package/src/actions/fx-setfocus.js +37 -0
  25. package/src/actions/fx-setvalue.js +58 -51
  26. package/src/actions/fx-show.js +12 -4
  27. package/src/actions/fx-toggle.js +13 -9
  28. package/src/actions/fx-update.js +3 -1
  29. package/src/dep_graph.js +1 -1
  30. package/src/drawdown.js +67 -82
  31. package/src/fore.js +143 -26
  32. package/src/functions/fx-function.js +17 -3
  33. package/src/fx-bind.js +40 -200
  34. package/src/fx-fore.js +598 -568
  35. package/src/fx-header.js +3 -1
  36. package/src/fx-instance.js +9 -1
  37. package/src/fx-model.js +60 -27
  38. package/src/fx-submission.js +108 -51
  39. package/src/fx-var.js +7 -4
  40. package/src/getInScopeContext.js +23 -16
  41. package/src/modelitem.js +4 -4
  42. package/src/relevance.js +64 -0
  43. package/src/ui/abstract-control.js +65 -37
  44. package/src/ui/fx-alert.js +7 -1
  45. package/src/ui/fx-case.js +4 -3
  46. package/src/ui/fx-container.js +4 -2
  47. package/src/ui/fx-control.js +315 -34
  48. package/src/ui/fx-dialog.js +50 -45
  49. package/src/ui/fx-group.js +3 -1
  50. package/src/ui/fx-hint.js +4 -1
  51. package/src/ui/fx-inspector.js +118 -17
  52. package/src/ui/fx-items.js +7 -5
  53. package/src/ui/fx-output.js +19 -6
  54. package/src/ui/fx-repeat.js +13 -26
  55. package/src/ui/fx-repeatitem.js +10 -4
  56. package/src/ui/fx-switch.js +5 -3
  57. package/src/ui/fx-trigger.js +3 -1
  58. package/src/xpath-evaluation.js +622 -553
  59. package/src/xpath-util.js +2 -6
  60. package/dist/fore-all.js +0 -140
  61. package/dist/fore-debug.js +0 -140
  62. package/src/.DS_Store +0 -0
  63. package/src/actions/.DS_Store +0 -0
  64. package/src/ui/.DS_Store +0 -0
@@ -1,7 +1,12 @@
1
1
  import XfAbstractControl from './abstract-control.js';
2
- import { evaluateXPath, evaluateXPathToString, evaluateXPathToNodes } from '../xpath-evaluation.js';
2
+ import {
3
+ evaluateXPath,
4
+ evaluateXPathToString,
5
+ evaluateXPathToFirstNode,
6
+ } from '../xpath-evaluation.js';
3
7
  import getInScopeContext from '../getInScopeContext.js';
4
8
  import { Fore } from '../fore.js';
9
+ import {ModelItem} from "../modelitem.js";
5
10
 
6
11
  const WIDGETCLASS = 'widget';
7
12
 
@@ -14,6 +19,16 @@ const WIDGETCLASS = 'widget';
14
19
  * @customElement
15
20
  * @demo demo/index.html
16
21
  */
22
+
23
+ function debounce(func, timeout = 300) {
24
+ let timer;
25
+ return (...args) => {
26
+ clearTimeout(timer);
27
+ timer = setTimeout(() => {
28
+ func.apply(this, args);
29
+ }, timeout);
30
+ };
31
+ }
17
32
  export default class FxControl extends XfAbstractControl {
18
33
  constructor() {
19
34
  super();
@@ -22,6 +37,12 @@ export default class FxControl extends XfAbstractControl {
22
37
  }
23
38
 
24
39
  connectedCallback() {
40
+ this.initial = this.hasAttribute('initial') ? this.getAttribute('initial') : null;
41
+ this.url = this.hasAttribute('url') ? this.getAttribute('url') : null;
42
+ this.loaded = false;
43
+ this.initialNode = null;
44
+ this.debounceDelay = this.hasAttribute('debounce') ? this.getAttribute('debounce') : null;
45
+
25
46
  this.updateEvent = this.hasAttribute('update-event')
26
47
  ? this.getAttribute('update-event')
27
48
  : 'blur';
@@ -33,21 +54,6 @@ export default class FxControl extends XfAbstractControl {
33
54
  }
34
55
  `;
35
56
 
36
- /*
37
- const controlHtml = `
38
- <slot></slot>
39
- <fx-setvalue id="setvalue" ref="${this.ref}"></fx-setvalue>
40
- `;
41
- */
42
-
43
- /*
44
- this.shadowRoot.innerHTML = `
45
- <style>
46
- ${style}
47
- </style>
48
- ${controlHtml}
49
- `
50
- */
51
57
  this.shadowRoot.innerHTML = `
52
58
  <style>
53
59
  ${style}
@@ -57,7 +63,15 @@ export default class FxControl extends XfAbstractControl {
57
63
 
58
64
  this.widget = this.getWidget();
59
65
  // console.log('widget ', this.widget);
66
+ let listenOn = this.widget // default: usually listening on widget
60
67
 
68
+ if(this.hasAttribute('listen-on')){
69
+ const q = this.getAttribute('listen-on');
70
+ const target = this.querySelector(q);
71
+ if(target){
72
+ listenOn = target;
73
+ }
74
+ }
61
75
  // ### convenience marker event
62
76
  if (this.updateEvent === 'enter') {
63
77
  this.widget.addEventListener('keyup', event => {
@@ -69,32 +83,124 @@ export default class FxControl extends XfAbstractControl {
69
83
  });
70
84
  this.updateEvent = 'blur'; // needs to be registered too
71
85
  }
72
- this.widget.addEventListener(this.updateEvent, () => {
73
- // console.log('eventlistener ', this.updateEvent);
74
- this.setValue(this.widget[this.valueProp]);
86
+ if (this.debounceDelay) {
87
+ listenOn.addEventListener(
88
+ this.updateEvent,
89
+ debounce(() => {
90
+ console.log('eventlistener ', this.updateEvent);
91
+ this.setValue(this.widget[this.valueProp]);
92
+ }, this.debounceDelay),
93
+ );
94
+ } else {
95
+ listenOn.addEventListener(this.updateEvent, () => {
96
+ console.log('eventlistener ', this.updateEvent);
97
+ this.setValue(this.widget[this.valueProp]);
98
+ });
99
+ }
100
+
101
+ this.addEventListener('return', e => {
102
+ console.log('catched return action on ', this);
103
+ console.log('return detail', e.detail);
104
+
105
+ console.log('return triggered on ', this);
106
+ console.log('this.ref', this.ref);
107
+ console.log('current outer instance', this.getInstance());
108
+
109
+ console.log(
110
+ '???? why ???? current nodeset should point to the node of the outer control',
111
+ e.currentTarget.nodeset,
112
+ );
113
+ console.log(
114
+ '???? why ???? current nodeset should point to the node of the outer control',
115
+ this.nodeset,
116
+ );
117
+ const newNodes = e.detail.nodeset;
118
+ console.log('new nodeset', newNodes);
119
+ console.log('currentTarget', e.currentTarget);
120
+ console.log('target', e.target);
121
+
122
+ e.stopPropagation();
123
+
124
+ this._replaceNode(newNodes);
75
125
  });
76
126
 
77
- const slot = this.shadowRoot.querySelector('slot');
78
127
  this.template = this.querySelector('template');
79
128
  // console.log('template',this.template);
80
129
  }
81
130
 
131
+ _debounce(func, timeout = 300) {
132
+ let timer;
133
+ return (...args) => {
134
+ const context = this;
135
+ clearTimeout(timer);
136
+ timer = setTimeout(() => {
137
+ func.apply(context, args);
138
+ }, timeout);
139
+ };
140
+ }
141
+
142
+ /**
143
+ * updates the model with a new value by executing it's `<fx-setvalue>` action.
144
+ *
145
+ * In case the `as='node'` is given the bound node is replaced with the widgets' value with is
146
+ * expected to be a node again.
147
+ *
148
+ * @param val the new value to be set
149
+ */
82
150
  setValue(val) {
83
151
  const modelitem = this.getModelItem();
152
+
153
+ if (modelitem?.readonly){
154
+ console.warn('attempt to change readonly node', modelitem);
155
+ return; // do nothing when modelItem is readonly
156
+ }
157
+
158
+ if (this.getAttribute('as') === 'node') {
159
+ const widgetValue = this.getWidget().value;
160
+ const replace = this.shadowRoot.getElementById('replace');
161
+ replace.replace(this.nodeset, this.getWidget().value);
162
+ if (modelitem && widgetValue && widgetValue !== modelitem.value) {
163
+ modelitem.value = widgetValue;
164
+ replace.actionPerformed();
165
+ }
166
+ return;
167
+ }
84
168
  const setval = this.shadowRoot.getElementById('setvalue');
85
169
  setval.setValue(modelitem, val);
170
+
171
+ if (this.modelItem instanceof ModelItem && !this.modelItem?.boundControls.includes(this)) {
172
+ this.modelItem.boundControls.push(this);
173
+ }
174
+
86
175
  setval.actionPerformed();
87
176
  }
88
177
 
178
+ _replaceNode(node) {
179
+ // Note: clone the node while replacing to prevent the instances to leak through
180
+ this.modelItem.node.replaceWith(node.cloneNode(true));
181
+ this.getOwnerForm().refresh();
182
+ }
183
+
89
184
  renderHTML(ref) {
90
185
  return `
91
186
  ${this.label ? `${this.label}` : ''}
92
187
  <slot></slot>
93
- <fx-setvalue id="setvalue" ref="${ref}"></fx-setvalue>
188
+ ${
189
+ this.hasAttribute('as') && this.getAttribute('as') === 'node'
190
+ ? `<fx-replace id="replace" ref=".">`
191
+ : `<fx-setvalue id="setvalue" ref="${ref}"></fx-setvalue>`
192
+ }
193
+
94
194
  `;
95
195
  }
96
196
 
97
197
  /**
198
+ * The widget is the actual control being used in the UI e.g. a native input control or any
199
+ * other component that presents a control that can be interacted with.
200
+ *
201
+ * This function returns the widget by querying the children of this control for an element
202
+ * with `class="widget"`. If that cannot be found it searches for an native `input` of any type.
203
+ * If either cannot be found a `<input type="text">` is created.
98
204
  *
99
205
  * @returns {HTMLElement|*}
100
206
  */
@@ -114,37 +220,201 @@ export default class FxControl extends XfAbstractControl {
114
220
  return widget;
115
221
  }
116
222
 
117
- // todo: check again
223
+ /**
224
+ * updates the widget from the modelItem value. During refresh the a control
225
+ * evaluates it's binding expression to determine the bound node. The bound node corresponds
226
+ * to a modelItem which acts a the state object of a node. The modelItem determines the value
227
+ * and the state of the node and set the `value` property of this class.
228
+ *
229
+ * @returns {Promise<void>}
230
+ */
118
231
  async updateWidgetValue() {
119
232
  // this.widget[this.valueProp] = this.value;
233
+
234
+ let { widget } = this;
235
+ if (!widget) {
236
+ widget = this;
237
+ }
238
+ // ### value is bound to checkbox
120
239
  if (this.valueProp === 'checked') {
121
240
  if (this.value === 'true') {
122
- this.widget.checked = true;
241
+ widget.checked = true;
123
242
  } else {
124
- this.widget.checked = false;
243
+ widget.checked = false;
125
244
  }
126
- } else {
127
- let { widget } = this;
128
- if (!widget) {
129
- widget = this;
245
+ return;
246
+ }
247
+
248
+ if (this.hasAttribute('as')) {
249
+ const as = this.getAttribute('as');
250
+
251
+ // ### when there's an `as=text` attribute serialize nodeset to prettified string
252
+ if (as === 'text') {
253
+ const serializer = new XMLSerializer();
254
+ const pretty = Fore.prettifyXml(serializer.serializeToString(this.nodeset));
255
+ widget.value = pretty;
256
+ }
257
+ if (as === 'node' && this.nodeset !== widget.value) {
258
+ // const oldVal = this.nodeset.innerHTML;
259
+ const oldVal = this.nodeset;
260
+ if (widget.value) {
261
+ if (oldVal !== this.widget.value) {
262
+ console.log('changed');
263
+ widget.value = this.nodeset.cloneNode(true);
264
+ return;
265
+ }
266
+ }
267
+
268
+ widget.value = this.nodeset.cloneNode(true);
269
+ // todo: should be more like below but that can cause infinite loop when controll trigger update event due to calling a setter for property
270
+ // widget[this.valueProp] = this.nodeset.cloneNode(true);
271
+ console.log('passed value to widget', widget.value);
272
+ }
273
+
274
+ return;
275
+ }
276
+
277
+ // ### when there's a url Fore is used as widget and will be loaded from external file
278
+ if (this.url && !this.loaded) {
279
+ // ### evaluate initial data if necessary
280
+ if (this.initial) {
281
+ this.initialNode = evaluateXPathToFirstNode(this.initial, this.nodeset, this);
282
+ console.log('initialNodes', this.initialNode);
130
283
  }
284
+
285
+ // ### load the markup from Url
286
+ await this._loadForeFromUrl();
287
+ this.loaded = true;
288
+
289
+ // ### replace default instance of embedded Fore with initial nodes
290
+ // const innerInstance = this.querySelector('fx-instance');
291
+ // console.log('innerInstance',innerInstance);
292
+ return;
293
+ }
294
+
295
+ /*
296
+ if(this.url && !this.loaded){
297
+ this._loadForeFromUrl();
298
+ this.loaded=true;
299
+ return;
300
+ }
301
+ */
302
+ if (widget.value !== this.value) {
131
303
  widget.value = this.value;
132
304
  }
133
305
  }
134
306
 
307
+ /**
308
+ * loads an external Fore from an HTML file given by `url` attribute.
309
+ *
310
+ * Will look for the `<fx-fore>` element within the returned HTML file and return that element.
311
+ *
312
+ * If that cannot be found an error is dispatched.
313
+ *
314
+ * todo: dispatch link error
315
+ * @private
316
+ */
317
+ async _loadForeFromUrl() {
318
+ console.log('########## loading Fore from ', this.src, '##########');
319
+ try {
320
+ const response = await fetch(this.url, {
321
+ method: 'GET',
322
+ mode: 'cors',
323
+ credentials: 'include',
324
+ headers: {
325
+ 'Content-Type': 'text/html',
326
+ },
327
+ });
328
+ const responseContentType = response.headers.get('content-type').toLowerCase();
329
+ console.log('********** responseContentType *********', responseContentType);
330
+ let data;
331
+ if (responseContentType.startsWith('text/html')) {
332
+ data = await response.text().then(result =>
333
+ // console.log('xml ********', result);
334
+ new DOMParser().parseFromString(result, 'text/html'),
335
+ );
336
+ } else {
337
+ data = 'done';
338
+ }
339
+ // const theFore = fxEvaluateXPathToFirstNode('//fx-fore', data.firstElementChild);
340
+ const theFore = data.querySelector('fx-fore');
341
+ // console.log('thefore', theFore)
342
+ theFore.classList.add('widget'); // is the new widget
343
+ const dummy = this.querySelector('input');
344
+ if (this.hasAttribute('shadow')) {
345
+ dummy.parentNode.removeChild(dummy);
346
+ this.shadowRoot.appendChild(theFore);
347
+ } else {
348
+ dummy.replaceWith(theFore);
349
+ }
350
+
351
+ console.log(`########## loaded fore as component ##### ${this.url}`);
352
+ theFore.addEventListener(
353
+ 'model-construct-done',
354
+ e => {
355
+ console.log('subcomponent ready', e.target);
356
+ const defaultInst = theFore.querySelector('fx-instance');
357
+ console.log('defaultInst', defaultInst);
358
+ if(this.initialNode){
359
+ const doc = new DOMParser().parseFromString('<data></data>', 'application/xml');
360
+ // Note: Clone the input to prevent the inner fore from editing the outer node
361
+ doc.firstElementChild.appendChild(this.initialNode.cloneNode(true));
362
+ // defaultinst.setInstanceData(this.initialNode);
363
+ defaultInst.setInstanceData(doc);
364
+ }
365
+ console.log('new data', defaultInst.getInstanceData());
366
+ // theFore.getModel().modelConstruct();
367
+ theFore.getModel().updateModel();
368
+ theFore.refresh();
369
+ },
370
+ { once: true },
371
+ );
372
+
373
+ if (!theFore) {
374
+ this.dispatchEvent(
375
+ new CustomEvent('error', {
376
+ detail: {
377
+ message: `Fore element not found in '${this.src}'. Maybe wrapped within 'template' element?`,
378
+ },
379
+ }),
380
+ );
381
+ }
382
+ console.log('loaded');
383
+ this.dispatchEvent(new CustomEvent('loaded', { detail: { fore: theFore } }));
384
+ } catch (error) {
385
+ console.log('error', error);
386
+ this.getOwnerForm().dispatchEvent(
387
+ new CustomEvent('error', { detail: { message: `${this.url} not found` } }),
388
+ );
389
+ }
390
+ }
391
+
135
392
  getTemplate() {
136
393
  return this.querySelector('template');
137
394
  }
138
395
 
139
396
  async refresh(force) {
140
397
  // console.log('fx-control refresh', this);
141
- super.refresh();
398
+ super.refresh(force);
142
399
  // console.log('refresh template', this.template);
143
400
  // const {widget} = this;
144
401
 
145
402
  // ### if we find a ref on control we have a 'select' control of some kind
146
403
  const widget = this.getWidget();
147
- if (widget.hasAttribute('ref')) {
404
+ this._handleBoundWidget(widget);
405
+ Fore.refreshChildren(this, force);
406
+ }
407
+
408
+ /**
409
+ * If the widget itself has a `ref` it binds to another nodeset to provide some
410
+ * dynamic items to be created from a template usually. Examples are dynamic select option lists
411
+ * or a set of checkboxes.
412
+ *
413
+ * @param widget the widget to handle
414
+ * @private
415
+ */
416
+ _handleBoundWidget(widget) {
417
+ if (widget && widget.hasAttribute('ref')) {
148
418
  // ### eval nodeset for list control
149
419
  const ref = widget.getAttribute('ref');
150
420
  /*
@@ -158,6 +428,9 @@ export default class FxControl extends XfAbstractControl {
158
428
  // const nodeset = evaluateXPathToNodes(ref, inscope, this);
159
429
  const nodeset = evaluateXPath(ref, inscope, this);
160
430
 
431
+ // ### bail out when nodeset is array and empty
432
+ if (Array.isArray(nodeset) && nodeset.length === 0) return;
433
+
161
434
  // ### clear items
162
435
  const { children } = widget;
163
436
  Array.from(children).forEach(child => {
@@ -168,6 +441,14 @@ export default class FxControl extends XfAbstractControl {
168
441
 
169
442
  // ### build the items
170
443
  if (this.template) {
444
+ if(this.widget.nodeName === "SELECT" &&
445
+ this.widget.hasAttribute('selection') &&
446
+ this.widget.getAttribute('selection') === 'open'){
447
+ const firstTemplateChild=this.template.firstElementChild;
448
+ const option = document.createElement('option');
449
+ this.widget.insertBefore(option,firstTemplateChild);
450
+ }
451
+
171
452
  if (nodeset.length) {
172
453
  // console.log('nodeset', nodeset);
173
454
  Array.from(nodeset).forEach(node => {
@@ -184,7 +465,6 @@ export default class FxControl extends XfAbstractControl {
184
465
  }
185
466
  }
186
467
  }
187
- Fore.refreshChildren(this, force);
188
468
  }
189
469
 
190
470
  updateEntry(newEntry, node) {
@@ -194,7 +474,7 @@ export default class FxControl extends XfAbstractControl {
194
474
  const valueAttribute = this._getValueAttribute(newEntry);
195
475
  const valueExpr = valueAttribute.value;
196
476
  const cutted = valueExpr.substring(1, valueExpr.length - 1);
197
- const evaluated = evaluateXPath(cutted, node, newEntry);
477
+ const evaluated = evaluateXPathToString(cutted, node, newEntry);
198
478
  valueAttribute.value = evaluated;
199
479
 
200
480
  if (this.value === evaluated) {
@@ -230,5 +510,6 @@ export default class FxControl extends XfAbstractControl {
230
510
  return result;
231
511
  }
232
512
  }
233
-
234
- window.customElements.define('fx-control', FxControl);
513
+ if (!customElements.get('fx-control')) {
514
+ window.customElements.define('fx-control', FxControl);
515
+ }
@@ -1,19 +1,19 @@
1
- import {Fore} from '../fore.js';
2
- export class FxDialog extends HTMLElement {
1
+ import { Fore } from '../fore.js';
3
2
 
4
- static get properties() {
5
- return {
6
- id: String
7
- };
8
- }
3
+ export class FxDialog extends HTMLElement {
4
+ static get properties() {
5
+ return {
6
+ id: String,
7
+ };
8
+ }
9
9
 
10
- constructor() {
11
- super();
12
- this.attachShadow({mode: 'open'});
13
- }
10
+ constructor() {
11
+ super();
12
+ this.attachShadow({ mode: 'open' });
13
+ }
14
14
 
15
- connectedCallback() {
16
- const style = `
15
+ connectedCallback() {
16
+ const style = `
17
17
  :host {
18
18
  display:none;
19
19
  height: 100vh;
@@ -28,50 +28,55 @@ export class FxDialog extends HTMLElement {
28
28
 
29
29
  `;
30
30
 
31
- this.shadowRoot.innerHTML = this.render(style);
32
- this.id = this.getAttribute('id');
31
+ this.shadowRoot.innerHTML = this.render(style);
32
+ this.id = this.getAttribute('id');
33
33
 
34
- // const dialog = document.getElementById(this.id);
34
+ // const dialog = document.getElementById(this.id);
35
35
 
36
- const closeBtn = this.querySelector('.close-dialog');
37
- if (closeBtn) {
38
- closeBtn.addEventListener('click', (e) => {
39
- document.getElementById(this.id).classList.remove('show');
40
- });
41
- }
36
+ const closeBtn = this.querySelector('.close-dialog');
37
+ if (closeBtn) {
38
+ closeBtn.addEventListener('click', () => {
39
+ this.classList.remove('show');
40
+ });
41
+ }
42
42
 
43
- this.addEventListener('transitionend',()=>{
44
- console.log('transitionend');
45
- // this.style.display = 'none';
46
- });
43
+ this.addEventListener('transitionend', () => {
44
+ console.log('transitionend');
45
+ // this.style.display = 'none';
46
+ });
47
47
 
48
- this.focus();
49
- }
48
+ this.focus();
49
+ }
50
50
 
51
- render(styles) {
52
- return `
51
+ render(styles) {
52
+ return `
53
53
  <style>
54
54
  ${styles}
55
55
  </style>
56
56
  <slot></slot>
57
57
  `;
58
- }
58
+ }
59
59
 
60
- open(){
61
- window.addEventListener('keyup', (e) => {
62
- if (e.key === "Escape") {
63
- this.hide();
64
- }
65
- },{once:true});
60
+ open() {
61
+ window.addEventListener(
62
+ 'keyup',
63
+ e => {
64
+ if (e.key === 'Escape') {
65
+ this.hide();
66
+ }
67
+ },
68
+ { once: true },
69
+ );
66
70
 
67
- this.classList.add('show');
68
- }
69
-
70
- async hide(){
71
- await Fore.fadeOutElement(this,400);
72
- this.classList.remove('show');
73
- }
71
+ this.classList.add('show');
72
+ }
74
73
 
74
+ async hide() {
75
+ await Fore.fadeOutElement(this, 400);
76
+ this.classList.remove('show');
77
+ }
75
78
  }
76
79
 
77
- customElements.define('fx-dialog', FxDialog);
80
+ if (!customElements.get('fx-dialog')) {
81
+ customElements.define('fx-dialog', FxDialog);
82
+ }
@@ -86,4 +86,6 @@ class FxGroup extends FxContainer {
86
86
  }
87
87
  }
88
88
 
89
- window.customElements.define('fx-group', FxGroup);
89
+ if (!customElements.get('fx-group')) {
90
+ window.customElements.define('fx-group', FxGroup);
91
+ }
package/src/ui/fx-hint.js CHANGED
@@ -45,4 +45,7 @@ export class FxHint extends XfAbstractControl {
45
45
  }
46
46
  */
47
47
  }
48
- customElements.define('fx-hint', FxHint);
48
+
49
+ if (!customElements.get('fx-hint')) {
50
+ customElements.define('fx-hint', FxHint);
51
+ }