neo.mjs 6.5.10 → 6.6.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.
@@ -1,5 +1,5 @@
1
- import Panel from '../container/Panel.mjs';
2
1
  import NeoArray from '../util/Array.mjs';
2
+ import Panel from '../container/Panel.mjs';
3
3
  import Toolbar from './header/Toolbar.mjs';
4
4
  import VDomUtil from '../util/VDom.mjs';
5
5
 
@@ -45,6 +45,10 @@ class Base extends Panel {
45
45
  * @member {Boolean} autoRender=true
46
46
  */
47
47
  autoRender: true,
48
+ /**
49
+ * @member {Boolean} autoShow=true
50
+ */
51
+ autoShow: true,
48
52
  /**
49
53
  * @member {String[]} baseCls=['neo-dialog','neo-panel','neo-container']
50
54
  * @protected
@@ -79,6 +83,10 @@ class Base extends Panel {
79
83
  * @member {Object} dragZoneConfig=null
80
84
  */
81
85
  dragZoneConfig: null,
86
+ /**
87
+ * @member {Boolean} floating=true
88
+ */
89
+ floating: true,
82
90
  /**
83
91
  * @member {Object} headerConfig=null
84
92
  */
@@ -104,6 +112,10 @@ class Base extends Panel {
104
112
  * @member {String} minimizeCls='far fa-window-minimize'
105
113
  */
106
114
  minimizeCls: 'far fa-window-minimize',
115
+ /**
116
+ * @member {Boolean} modal_=false
117
+ */
118
+ modal_: false,
107
119
  /**
108
120
  * @member {Boolean} resizable_=true
109
121
  */
@@ -115,14 +127,7 @@ class Base extends Panel {
115
127
  /**
116
128
  * @member {String|null} title_=null
117
129
  */
118
- title_: null,
119
- /**
120
- * @member {Object} _vdom
121
- */
122
- _vdom:
123
- {cls: ['neo-dialog-wrapper'], cn: [
124
- {cn: []}
125
- ]}
130
+ title_: null
126
131
  }
127
132
 
128
133
  /**
@@ -133,11 +138,9 @@ class Base extends Panel {
133
138
 
134
139
  let me = this;
135
140
 
136
- me.vdom.id = me.getWrapperId();
137
-
138
141
  me.createHeader();
139
142
 
140
- me.animateTargetId && me.animateShow();
143
+ me.autoShow && me.show()
141
144
  }
142
145
 
143
146
  /**
@@ -148,7 +151,7 @@ class Base extends Panel {
148
151
  */
149
152
  afterSetAnimateTargetId(value, oldValue) {
150
153
  this.autoMount = !value;
151
- this.autoRender = !value;
154
+ this.autoRender = !value
152
155
  }
153
156
 
154
157
  /**
@@ -162,14 +165,14 @@ class Base extends Panel {
162
165
  resizable = me.getPlugin({flag: 'resizable'});
163
166
 
164
167
  if (me.dragZone) {
165
- me.dragZone.appName = value;
168
+ me.dragZone.appName = value
166
169
  }
167
170
 
168
171
  if (resizable) {
169
- resizable.appName = value;
172
+ resizable.appName = value
170
173
  }
171
174
 
172
- super.afterSetAppName(value, oldValue);
175
+ super.afterSetAppName(value, oldValue)
173
176
  }
174
177
 
175
178
  /**
@@ -186,7 +189,7 @@ class Base extends Panel {
186
189
  if (oldValue !== undefined && me.headerToolbar) {
187
190
  cls = me.headerToolbar.cls;
188
191
  NeoArray[value ? 'add' : 'remove'](cls, 'neo-draggable');
189
- me.headerToolbar.cls = cls;
192
+ me.headerToolbar.cls = cls
190
193
  }
191
194
 
192
195
  value && import('../draggable/DragZone.mjs').then(module => {
@@ -201,13 +204,13 @@ class Base extends Panel {
201
204
  if (me.dragZoneConfig?.alwaysFireDragMove) {
202
205
  domListeners.push(
203
206
  {'drag:move': me.onDragMove, scope: me, delegate: '.neo-header-toolbar'}
204
- );
207
+ )
205
208
  }
206
209
 
207
210
  me.domListeners = domListeners;
208
- me.dragListenersAdded = true;
211
+ me.dragListenersAdded = true
209
212
  }
210
- });
213
+ })
211
214
  }
212
215
 
213
216
  /**
@@ -217,29 +220,29 @@ class Base extends Panel {
217
220
  * @protected
218
221
  */
219
222
  afterSetMaximized(value, oldValue) {
220
- let me = this,
221
- cls = me.vdom.cls; // todo: using wrapperCls
223
+ let me = this,
224
+ cls = me.vdom.cls; // todo: using wrapperCls
222
225
 
223
- NeoArray[value ? 'add' : 'remove'](cls, 'neo-maximized');
226
+ NeoArray.toggle(cls, 'neo-maximized', value);
224
227
  me.update();
225
228
  }
226
229
 
227
230
  /**
228
- * Triggered after the mounted config got changed
231
+ * Triggered after the modal config got changed
229
232
  * @param {Boolean} value
230
233
  * @param {Boolean} oldValue
231
234
  * @protected
232
235
  */
233
- afterSetMounted(value, oldValue) {
234
- super.afterSetMounted(value, oldValue);
236
+ afterSetModal(value, oldValue) {
237
+ const
238
+ me = this,
239
+ { cls } = me.vdom;
235
240
 
236
- let me = this;
241
+ NeoArray.toggle(cls, 'neo-modal', value);
242
+ me.update();
237
243
 
238
- if (value && me.animateTargetId) {
239
- Neo.applyDeltas(me.appName, {
240
- action: 'removeNode',
241
- id : me.getAnimateTargetId()
242
- })
244
+ if (me.rendered) {
245
+ me.syncModalMask()
243
246
  }
244
247
  }
245
248
 
@@ -263,9 +266,9 @@ class Base extends Panel {
263
266
  ...me.resizablePluginConfig
264
267
  });
265
268
 
266
- me.plugins = plugins;
269
+ me.plugins = plugins
267
270
  }
268
- });
271
+ })
269
272
  }
270
273
 
271
274
  /**
@@ -276,7 +279,7 @@ class Base extends Panel {
276
279
  */
277
280
  afterSetTitle(value, oldValue) {
278
281
  if (this.headerToolbar) {
279
- this.headerToolbar.title = value;
282
+ this.headerToolbar.title = value
280
283
  }
281
284
  }
282
285
 
@@ -286,79 +289,90 @@ class Base extends Panel {
286
289
  async animateHide() {
287
290
  let me = this,
288
291
  appName = me.appName,
289
- id = me.getAnimateTargetId(),
290
- rects = await me.getDomRect([me.id, me.animateTargetId]);
291
-
292
- await Neo.currentWorker.promiseMessage('main', {
293
- action : 'mountDom',
294
- appName,
295
- html : `<div id="${id}" class="neo-animate-dialog neo-hide" style="height:${rects[0].height}px;left:${rects[0].left}px;top:${rects[0].top}px;width:${rects[0].width}px;"></div>`,
296
- parentId: 'document.body'
292
+ { id } = me,
293
+ rects = await me.getDomRect([id, me.animateTargetId]);
294
+
295
+ await Neo.applyDeltas(appName, {
296
+ id,
297
+ style: {
298
+ height : `${rects[0].height}px`,
299
+ left : `${rects[0].left }px`,
300
+ top : `${rects[0].top }px`,
301
+ transform: 'none',
302
+ width : `${rects[0].width }px`
303
+ }
297
304
  });
298
305
 
299
- me.closeOrHide(false);
300
-
301
306
  await me.timeout(30);
302
307
 
303
- await Neo.currentWorker.promiseMessage('main', {
304
- action: 'updateDom',
305
- appName,
306
- deltas: [{
307
- id,
308
- style: {
309
- height: `${rects[1].height}px`,
310
- left : `${rects[1].left }px`,
311
- top : `${rects[1].top }px`,
312
- width : `${rects[1].width }px`
313
- }
314
- }]
308
+ await Neo.applyDeltas(appName, {
309
+ id,
310
+ cls: {
311
+ add: ['animated-hiding-showing']
312
+ },
313
+ style: {
314
+ height: `${rects[1].height}px`,
315
+ left : `${rects[1].left }px`,
316
+ top : `${rects[1].top }px`,
317
+ width : `${rects[1].width }px`
318
+ }
315
319
  });
316
320
 
317
321
  await me.timeout(250);
318
322
 
319
- await Neo.currentWorker.promiseMessage('main', {
320
- action: 'updateDom',
321
- appName,
322
- deltas: [{action: 'removeNode', id}]
323
- });
323
+ me.closeOrHide(false);
324
+
325
+ await Neo.applyDeltas(appName, [
326
+ {id, cls: {remove: ['animated-hiding-showing']}},
327
+ {id, action: 'removeNode'}
328
+ ])
324
329
  }
325
330
 
326
331
  /**
327
332
  *
328
333
  */
329
334
  async animateShow() {
330
- let me = this,
331
- appName = me.appName,
332
- id = me.getAnimateTargetId(),
333
- wrapperStyle = me.wrapperStyle,
334
- rect = await me.getDomRect(me.animateTargetId);
335
-
336
- await Neo.currentWorker.promiseMessage('main', {
337
- action : 'mountDom',
338
- appName,
339
- html : `<div id="${id}" class="neo-animate-dialog" style="height:${rect.height}px;left:${rect.left}px;top:${rect.top}px;width:${rect.width}px;"></div>`,
340
- parentId: 'document.body'
335
+ let me = this,
336
+ appName = me.appName,
337
+ { id, style } = me,
338
+ rect = await me.getDomRect(me.animateTargetId);
339
+
340
+ await me.render(true);
341
+
342
+ // Move to cover the animation target
343
+ await Neo.applyDeltas(appName, {
344
+ id,
345
+ style : {
346
+ height: `${rect.height}px`,
347
+ left : `${rect.left }px`,
348
+ top : `${rect.top }px`,
349
+ width : `${rect.width }px`
350
+ }
341
351
  });
342
352
 
343
- await me.timeout(30);
344
-
345
- await Neo.currentWorker.promiseMessage('main', {
346
- action: 'updateDom',
347
- appName,
348
- deltas: [{
349
- id,
350
- style: {
351
- height : wrapperStyle?.height || '50%',
352
- left : wrapperStyle?.left || '50%',
353
- top : wrapperStyle?.top || '50%',
354
- transform: wrapperStyle?.transform || 'translate(-50%, -50%)',
355
- width : wrapperStyle?.width || '50%'
356
- }
357
- }]
353
+ // Wait for the element to achieve its initial rectangle
354
+ await me.timeout(50);
355
+
356
+ // Expand to final state
357
+ await Neo.applyDeltas(appName, {
358
+ id,
359
+ cls: {
360
+ add: ['animated-hiding-showing']
361
+ },
362
+ style: {
363
+ height : style?.height || '',
364
+ left : style?.left || '50%',
365
+ top : style?.top || '50%',
366
+ transform: style?.transform || 'translate(-50%, -50%)',
367
+ width : style?.width || '50%'
368
+ }
358
369
  });
359
370
 
360
371
  await me.timeout(200);
361
372
 
373
+ // Remove the animation class
374
+ await Neo.applyDeltas(appName, {id, cls: {remove: ['animated-hiding-showing']}});
375
+
362
376
  me.show(false)
363
377
  }
364
378
 
@@ -369,28 +383,34 @@ class Base extends Panel {
369
383
  * @protected
370
384
  */
371
385
  beforeSetCloseAction(value, oldValue) {
372
- return this.beforeSetEnumValue(value, oldValue, 'closeAction');
386
+ return this.beforeSetEnumValue(value, oldValue, 'closeAction')
373
387
  }
374
388
 
375
389
  /**
376
- * @param {Boolean} [animate=!!this.animateTargetId]
390
+ * @param {Boolean} animate=!!this.animateTargetId
377
391
  */
378
392
  close(animate=!!this.animateTargetId) {
379
393
  let me = this;
380
394
 
381
395
  if (animate) {
382
- me.animateHide();
396
+ me.animateHide()
383
397
  } else {
384
398
  me.fire('close');
385
- me.destroy(true);
399
+ me.destroy(true)
386
400
  }
387
401
  }
388
402
 
389
403
  /**
390
- * @param {Boolean} [animate=!!this.animateTargetId]
404
+ * @param {Boolean} animate=!!this.animateTargetId
391
405
  */
392
- closeOrHide(animate=!!this.animateTargetId) {
393
- this[this.closeAction](animate);
406
+ async closeOrHide(animate=!!this.animateTargetId) {
407
+ const
408
+ me = this,
409
+ { id } = me;
410
+
411
+ me[me.closeAction](animate);
412
+ await me.timeout(30);
413
+ me.syncModalMask(id)
394
414
  }
395
415
 
396
416
  /**
@@ -417,7 +437,7 @@ class Base extends Panel {
417
437
 
418
438
  headers.unshift(me.headerToolbar);
419
439
 
420
- me.headers = headers;
440
+ me.headers = headers
421
441
  }
422
442
 
423
443
  /**
@@ -444,7 +464,7 @@ class Base extends Panel {
444
464
  * @returns {String}
445
465
  */
446
466
  getAnimateTargetId() {
447
- return this.id + '-animate';
467
+ return this.id + '-animate'
448
468
  }
449
469
 
450
470
  /**
@@ -452,56 +472,32 @@ class Base extends Panel {
452
472
  * @returns {String}
453
473
  */
454
474
  getHeaderToolbarId() {
455
- return this.id + '-header-toolbar';
475
+ return this.id + '-header-toolbar'
456
476
  }
457
477
 
458
478
  /**
459
479
  * @returns {Object} vdom
460
480
  */
461
481
  getProxyVdom() {
462
- let vdom = VDomUtil.clone(this.vdom);
463
-
464
- // this call expects a fixed dialog structure
465
- // todo: a panel content container could get a flag which we can query for instead
466
- vdom.cn[0].cn[1].cn = [];
467
-
468
- return vdom;
469
- }
470
-
471
- /**
472
- * @returns {Object} The new vdom root
473
- */
474
- getVdomRoot() {
475
- return this.vdom.cn[0];
476
- }
477
-
478
- /**
479
- * @returns {Object} The new vnode root
480
- */
481
- getVnodeRoot() {
482
- return this.vnode.childNodes[0];
483
- }
484
-
485
- /**
486
- * Returns the id of the header toolbar
487
- * @returns {String}
488
- */
489
- getWrapperId() {
490
- return this.id + '-wrapper';
482
+ return VDomUtil.clone(this.vdom)
491
483
  }
492
484
 
493
485
  /**
494
- * @param {Boolean} [animate=!!this.animateTargetId]
486
+ * @param {Boolean} animate=!!this.animateTargetId
495
487
  */
496
- hide(animate=!!this.animateTargetId) {
488
+ async hide(animate=!!this.animateTargetId) {
497
489
  let me = this;
498
490
 
499
491
  if (animate) {
500
- me.animateHide();
492
+ me.animateHide()
501
493
  } else {
502
494
  me.unmount();
503
- me.fire('hide');
495
+ me.fire('hide')
504
496
  }
497
+
498
+ await me.timeout(30);
499
+
500
+ me.syncModalMask()
505
501
  }
506
502
 
507
503
  /**
@@ -512,7 +508,7 @@ class Base extends Panel {
512
508
 
513
509
  data.component.iconCls = me.maximized ? me.maximizeCls : me.minimizeCls;
514
510
 
515
- me.maximized = !me.maximized;
511
+ me.maximized = !me.maximized
516
512
  }
517
513
 
518
514
  /**
@@ -525,7 +521,7 @@ class Base extends Panel {
525
521
 
526
522
  me.headerToolbar = me.down({
527
523
  id: me.getHeaderToolbarId()
528
- });
524
+ })
529
525
  }
530
526
 
531
527
  /**
@@ -533,13 +529,13 @@ class Base extends Panel {
533
529
  */
534
530
  onDragEnd(data) {
535
531
  let me = this,
536
- initialTransitionProperty, wrapperStyle;
532
+ initialTransitionProperty, style;
537
533
 
538
534
  if (!me.maximized) {
539
535
  me.getDomRect(me.dragZone.dragProxy.id).then(rect => {
540
- wrapperStyle = me.wrapperStyle;
536
+ style = me.style;
541
537
 
542
- Object.assign(wrapperStyle, {
538
+ Object.assign(style, {
543
539
  height : `${rect.height}px`,
544
540
  left : `${rect.left}px`,
545
541
  opacity : 1,
@@ -549,27 +545,27 @@ class Base extends Panel {
549
545
  });
550
546
 
551
547
  if (!me.animateOnDragEnd) {
552
- initialTransitionProperty = wrapperStyle.transitionProperty || null;
548
+ initialTransitionProperty = style.transitionProperty || null;
553
549
 
554
- wrapperStyle.transitionProperty = 'none';
550
+ style.transitionProperty = 'none';
555
551
 
556
552
  setTimeout(() => {
557
- wrapperStyle = me.wrapperStyle;
553
+ style = me.style;
558
554
 
559
- wrapperStyle.transitionProperty = initialTransitionProperty;
555
+ style.transitionProperty = initialTransitionProperty;
560
556
 
561
- me.wrapperStyle = wrapperStyle;
562
- }, 50);
557
+ me.style = style
558
+ }, 50)
563
559
  }
564
560
 
565
- me.wrapperStyle = wrapperStyle;
561
+ me.style = style;
566
562
 
567
563
  me.dragZone.dragEnd(data);
568
564
 
569
565
  // we need a reset, otherwise we do not get a change event for the next onDragStart() call
570
566
  me.dragZone.boundaryContainerId = null;
571
- me.isDragging = false;
572
- });
567
+ me.isDragging = false
568
+ })
573
569
  }
574
570
  }
575
571
 
@@ -578,25 +574,20 @@ class Base extends Panel {
578
574
  * @param data
579
575
  */
580
576
  onDragMove(data) {
581
- this.dragZone.dragMove(data);
577
+ this.dragZone.dragMove(data)
582
578
  }
583
579
 
584
580
  /**
585
581
  * @param data
586
582
  */
587
583
  onDragStart(data) {
588
- let me = this,
589
- wrapperStyle = me.wrapperStyle || {},
590
- resizablePlugin;
584
+ let me = this,
585
+ style = me.style || {};
591
586
 
592
587
  if (!me.maximized) {
593
588
  me.isDragging = true;
594
589
 
595
- resizablePlugin = me.getPlugin({flag: 'resizable'});
596
-
597
- if (resizablePlugin) {
598
- resizablePlugin.removeAllNodes();
599
- }
590
+ me.getPlugin({flag: 'resizable'})?.removeAllNodes();
600
591
 
601
592
  if (!me.dragZone) {
602
593
  me.dragZone = Neo.create({
@@ -614,21 +605,21 @@ class Base extends Panel {
614
605
  me.fire('dragZoneCreated', {
615
606
  dragZone: me.dragZone,
616
607
  id : me.id
617
- });
608
+ })
618
609
  } else {
619
- me.dragZone.boundaryContainerId = me.boundaryContainerId;
610
+ me.dragZone.boundaryContainerId = me.boundaryContainerId
620
611
  }
621
612
 
622
613
  me.dragZone.dragStart(data);
623
614
 
624
- wrapperStyle.opacity = 0.7;
615
+ style.opacity = 0.7;
625
616
 
626
- me.wrapperStyle = wrapperStyle;
617
+ me.style = style
627
618
  }
628
619
  }
629
620
 
630
621
  /**
631
- * @param {Boolean} [animate=!!this.animateTargetId]
622
+ * @param {Boolean} animate=!!this.animateTargetId
632
623
  */
633
624
  show(animate=!!this.animateTargetId) {
634
625
  let me = this;
@@ -636,9 +627,22 @@ class Base extends Panel {
636
627
  if (animate) {
637
628
  me.animateShow();
638
629
  } else {
639
- me.render(true);
640
- me.fire('show');
630
+ if (!me.rendered) {
631
+ me.render(true)
632
+ }
633
+
634
+ me.fire('show')
641
635
  }
636
+
637
+ me.syncModalMask()
638
+ }
639
+
640
+ /**
641
+ * @param {String} id=this.id
642
+ */
643
+ syncModalMask(id=this.id) {
644
+ // This should sync the visibility and position of the modal mask element.
645
+ Neo.main.DomAccess.syncModalMask({ id, modal: this.modal });
642
646
  }
643
647
  }
644
648
 
@@ -459,6 +459,10 @@ class FileUpload extends Base {
459
459
  me.update();
460
460
  me.state = 'uploading';
461
461
 
462
+ // This means no progress as opposed to zero, but still during a currently successful ongoing upload.
463
+ // When it is NaN, the error display does not attempt to show progress.
464
+ me.progress = NaN;
465
+
462
466
  fileData.append("file", file);
463
467
 
464
468
  // React to upload state changes
@@ -517,28 +521,38 @@ class FileUpload extends Base {
517
521
 
518
522
  me.xhr = null;
519
523
 
520
- if (loaded !== 0) {
521
- const response = JSON.parse(xhr.response);
522
-
523
- if (response.success) {
524
- me.documentId = response[me.documentIdParameter];
525
-
526
- // The status check phase is optional.
527
- // If no URL specified, the file is taken to be downloadable.
528
- if (me.documentStatusUrl) {
529
- me.state = 'processing';
530
-
531
- // Start polling the server to see when the scan has a result;
532
- me.checkDocumentStatus();
524
+ // Successful network request.
525
+ // Check the resulting JSON packet for details and any error.
526
+ if (String(xhr.status).startsWith('2')) {
527
+ if (loaded !== 0) {
528
+ const response = JSON.parse(xhr.response);
529
+
530
+ if (response.success) {
531
+ me.documentId = response[me.documentIdParameter];
532
+
533
+ // The status check phase is optional.
534
+ // If no URL specified, the file is taken to be downloadable.
535
+ if (me.documentStatusUrl) {
536
+ me.state = 'processing';
537
+
538
+ // Start polling the server to see when the scan has a result;
539
+ me.checkDocumentStatus();
540
+ }
541
+ else {
542
+ me.state = 'downloadable';
543
+ }
533
544
  }
534
545
  else {
535
- me.state = 'downloadable';
546
+ me.error = response.message;
547
+ me.state = 'upload-failed';
536
548
  }
537
549
  }
538
- else {
539
- me.error = response.message;
540
- me.state = 'upload-failed';
541
- }
550
+ }
551
+ // Failed network request
552
+ else {
553
+ me.progress = NaN;
554
+ me.error = `HTTP status : ${xhr.statusText}`;
555
+ me.state = 'upload-failed';
542
556
  }
543
557
  }
544
558
 
@@ -693,7 +707,7 @@ class FileUpload extends Base {
693
707
  anchor.href = '';
694
708
  break;
695
709
  case 'upload-failed':
696
- status.innerHTML = `${me.uploadFailed}... (${Math.round(me.progress * 100)}%)`;
710
+ status.innerHTML = `${me.uploadFailed}${isNaN(me.progress) ? '' : `... (${Math.round(me.progress * 100)}%)`}`;
697
711
  break;
698
712
  case 'processing':
699
713
  status.innerHTML = `${me.scanning}... (${me.formatSize(me.uploadSize)})`;
@@ -806,9 +820,9 @@ class FileUpload extends Base {
806
820
  if (bytes) {
807
821
  const
808
822
  sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'],
809
- i = Math.min(parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString(), 10), sizes.length - 1);
823
+ i = Math.min(parseInt(Math.floor(Math.log(bytes) / Math.log(1000)).toString(), 10), sizes.length - 1);
810
824
 
811
- return `${(bytes / (1024 ** i)).toFixed(i ? 1 : 0)}${separator}${sizes[i]}${postFix}`;
825
+ return `${(bytes / (1000 ** i)).toFixed(i ? 1 : 0)}${separator}${sizes[i]}${postFix}`;
812
826
  }
813
827
  return 'n/a';
814
828
  }