@kizmann/nano-ui 0.9.0 → 0.9.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kizmann/nano-ui",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "license": "MIT",
5
5
  "private": false,
6
6
  "author": "Eduard Kizmann <kizmann@protonmail.ch>",
@@ -49,6 +49,10 @@
49
49
  font-weight: bold;
50
50
  }
51
51
 
52
+ .n-popover-option {
53
+ width: 100%;
54
+ }
55
+
52
56
  .n-popover-option.n-disabled {
53
57
  cursor: not-allowed;
54
58
  opacity: 0.7;
@@ -277,10 +277,20 @@ export default {
277
277
  let innerHeight = this.$refs.content
278
278
  .scrollHeight || 0;
279
279
 
280
+ let virtualHeight = 0;
281
+
282
+ Dom.find(this.$refs.content).childs().each((el) => {
283
+ virtualHeight += Dom.find(el).height() || 0;
284
+ });
285
+
280
286
  if ( this.native && ! this.allowNative ) {
281
287
  innerHeight -= 16;
282
288
  }
283
289
 
290
+ if ( virtualHeight > innerHeight ) {
291
+ innerHeight = virtualHeight;
292
+ }
293
+
284
294
  // if ( offsetHeight === 0 && this.overflowX ) {
285
295
  // innerHeight -= 15;
286
296
  // }
@@ -363,10 +373,20 @@ export default {
363
373
  let innerWidth = this.$refs.content
364
374
  .scrollWidth || 0;
365
375
 
376
+ let virtualWidth = 0;
377
+
378
+ Dom.find(this.$refs.content).childs().each((el) => {
379
+ virtualWidth += Dom.find(el).width() || 0;
380
+ });
381
+
366
382
  if ( this.native && ! this.allowNative ) {
367
383
  innerWidth -= 16;
368
384
  }
369
385
 
386
+ if ( virtualWidth > innerWidth ) {
387
+ innerWidth = virtualWidth;
388
+ }
389
+
370
390
  // if ( offsetWidth === 0 && this.overflowY ) {
371
391
  // innerWidth -= 15;
372
392
  // }
@@ -437,7 +457,7 @@ export default {
437
457
  scroll.left = this.$refs.content.scrollLeft;
438
458
  }
439
459
 
440
- let vbarTop= Math.ceil((this.outerHeight / this.innerHeight) *
460
+ let vbarTop = Math.ceil((this.outerHeight / this.innerHeight) *
441
461
  scroll.top * this.heightRatio) || 0;
442
462
 
443
463
  if ( ! this.vbarTop || vbarTop !== this.vbarTop ) {
@@ -1,4 +1,4 @@
1
- import { Str, Arr, Obj, Any, Locale, Dom } from "@kizmann/pico-js";
1
+ import { Str, Arr, Obj, Any, Locale, Dom, Num } from "@kizmann/pico-js";
2
2
 
3
3
  export default {
4
4
 
@@ -36,6 +36,14 @@ export default {
36
36
  type: [String]
37
37
  },
38
38
 
39
+ lazy: {
40
+ default()
41
+ {
42
+ return false;
43
+ },
44
+ type: [Boolean]
45
+ },
46
+
39
47
  size: {
40
48
  default()
41
49
  {
@@ -160,7 +168,8 @@ export default {
160
168
 
161
169
  computed: {
162
170
 
163
- deepDisabled() {
171
+ deepDisabled()
172
+ {
164
173
  return this.NFormItem ? this.NFormItem.disabled(this.disabled) :
165
174
  this.disabled;
166
175
  },
@@ -193,12 +202,30 @@ export default {
193
202
  tempClear: this.clearValue,
194
203
  focus: false,
195
204
  search: '',
196
- index: -1,
205
+ index: - 1,
197
206
  elements: [],
198
207
  searched: []
199
208
  };
200
209
  },
201
210
 
211
+
212
+ beforeMount()
213
+ {
214
+ if ( this.lazy ) {
215
+ this.generateOptions();
216
+ }
217
+
218
+ if ( this.multiple && !Any.isArray(this.tempValue) ) {
219
+ this.tempValue = [];
220
+ }
221
+
222
+ if ( this.multiple && !Any.isArray(this.clearValue) ) {
223
+ this.tempClear = [];
224
+ }
225
+
226
+ this.searchOptions();
227
+ },
228
+
202
229
  provide()
203
230
  {
204
231
  return {
@@ -210,11 +237,11 @@ export default {
210
237
 
211
238
  modelValue(value)
212
239
  {
213
- if ( ! this.multiple && Any.isArray(value) ) {
240
+ if ( !this.multiple && Any.isArray(value) ) {
214
241
  value = null;
215
242
  }
216
243
 
217
- if ( this.multiple && ! Any.isArray(value) ) {
244
+ if ( this.multiple && !Any.isArray(value) ) {
218
245
  value = [];
219
246
  }
220
247
 
@@ -244,25 +271,44 @@ export default {
244
271
 
245
272
  this.focusInput();
246
273
 
247
- this.$emit('update:modelValue',
274
+ this.$emit('update:modelValue',
248
275
  this.tempValue = Arr.clone(this.tempClear));
249
276
  },
250
277
 
278
+ generateOptions()
279
+ {
280
+ this.elements = Arr.each(this.options, (value, index) => {
281
+
282
+ let data = {
283
+ $value: value, $index: index
284
+ };
285
+
286
+ let option = {
287
+ label: Obj.get(data, this.optionsLabel),
288
+ value: Obj.get(data, this.optionsValue)
289
+ };
290
+
291
+ return Obj.assign(option, {
292
+ tempLabel: option.label, tempValue: option.value
293
+ });
294
+ });
295
+ },
296
+
251
297
  addOption(option)
252
298
  {
253
- Arr.add(this.elements, option,
299
+ Arr.add(this.elements, option,
254
300
  { tempValue: option.tempValue });
255
301
  },
256
302
 
257
303
  removeOption(option)
258
304
  {
259
- Arr.remove(this.elements,
305
+ Arr.remove(this.elements,
260
306
  { tempValue: option.tempValue });
261
307
  },
262
308
 
263
309
  resetInput()
264
310
  {
265
- this.index = -1;
311
+ this.index = - 1;
266
312
  this.search = '';
267
313
  },
268
314
 
@@ -277,10 +323,10 @@ export default {
277
323
 
278
324
  onFocusInput()
279
325
  {
280
- if ( ! this.focus ) {
326
+ if ( !this.focus ) {
281
327
  this.$refs.popover.open();
282
328
  }
283
-
329
+
284
330
  clearInterval(this.refresh);
285
331
  },
286
332
 
@@ -291,7 +337,7 @@ export default {
291
337
 
292
338
  onKeydownInput(event)
293
339
  {
294
- if ( ! this.focus ) {
340
+ if ( !this.focus ) {
295
341
  return this.onFocusInput();
296
342
  }
297
343
 
@@ -310,7 +356,7 @@ export default {
310
356
 
311
357
  searchOptions()
312
358
  {
313
- this.index = -1;
359
+ this.index = - 1;
314
360
 
315
361
  if ( Any.isEmpty(this.search) ) {
316
362
  return this.searched = this.elements;
@@ -331,6 +377,10 @@ export default {
331
377
  return;
332
378
  }
333
379
 
380
+ if ( event && event.which !== 1 ) {
381
+ return;
382
+ }
383
+
334
384
  if ( event ) {
335
385
  event.preventDefault();
336
386
  }
@@ -341,7 +391,7 @@ export default {
341
391
  this.focusInput();
342
392
  }
343
393
 
344
- if ( ! this.multiple ) {
394
+ if ( !this.multiple ) {
345
395
  tempValue = value;
346
396
  }
347
397
 
@@ -353,11 +403,11 @@ export default {
353
403
 
354
404
  let denyUpdate = this.tempValue === tempValue;
355
405
 
356
- if ( this.multiple && ! Any.isArray(this.modelValue) ) {
406
+ if ( this.multiple && !Any.isArray(this.modelValue) ) {
357
407
  denyUpdate = false;
358
408
  }
359
409
 
360
- if ( ! this.multiple && Any.isArray(this.modelValue) ) {
410
+ if ( !this.multiple && Any.isArray(this.modelValue) ) {
361
411
  denyUpdate = false;
362
412
  }
363
413
 
@@ -371,14 +421,14 @@ export default {
371
421
 
372
422
  getOptionLabel(value)
373
423
  {
374
- let option = Arr.find(this.elements,
424
+ let option = Arr.find(this.elements,
375
425
  { tempValue: value });
376
426
 
377
- if ( ! option && this.allowCreate ) {
427
+ if ( !option && this.allowCreate ) {
378
428
  return value;
379
429
  }
380
430
 
381
- if ( ! option && ! this.allowCreate ) {
431
+ if ( !option && !this.allowCreate ) {
382
432
  return this.trans(this.undefinedText);
383
433
  }
384
434
 
@@ -416,15 +466,15 @@ export default {
416
466
  if ( this.allowCreate && this.search ) {
417
467
  return this.createOption();
418
468
  }
419
-
420
- let selected = Arr.get(this.searched,
469
+
470
+ let selected = Arr.get(this.searched,
421
471
  this.index);
422
472
 
423
473
  if ( this.searched.length === 1 ) {
424
474
  selected = Arr.first(this.searched);
425
475
  }
426
476
 
427
- if ( ! selected || selected.disabled ) {
477
+ if ( !selected || selected.disabled ) {
428
478
  return;
429
479
  }
430
480
 
@@ -440,24 +490,29 @@ export default {
440
490
 
441
491
  scrollToCurrent()
442
492
  {
443
- if ( ! this.focus ) {
493
+ if ( !this.focus ) {
444
494
  return;
445
495
  }
446
496
 
447
- let selected = Arr.get(this.searched,
497
+ let selected = Arr.get(this.searched,
448
498
  this.index);
449
499
 
450
- if ( ! selected || ! this.$refs.scrollbar ) {
500
+ if ( !selected ) {
451
501
  return;
452
502
  }
453
503
 
454
- this.$refs.scrollbar.scrollIntoView(
455
- `[data-option="${selected._.uid}"]`);
504
+ if ( this.$refs.scrollbar ) {
505
+ this.$refs.scrollbar.scrollIntoView(`[data-option="${selected._.uid}"]`);
506
+ }
507
+
508
+ if ( this.$refs.virtualbar ) {
509
+ this.$refs.virtualbar.scrollToIndex(this.index);
510
+ }
456
511
  },
457
512
 
458
513
  scrollToClosest()
459
514
  {
460
- if ( ! this.focus ) {
515
+ if ( !this.focus ) {
461
516
  return;
462
517
  }
463
518
 
@@ -467,47 +522,40 @@ export default {
467
522
  value = Arr.first(this.tempValue);
468
523
  }
469
524
 
470
- if ( ! value ) {
525
+ if ( !value ) {
471
526
  return;
472
527
  }
473
528
 
474
- let target = Arr.find(this.elements, {
529
+ let index = Arr.findIndex(this.elements, {
475
530
  tempValue: value
476
- });
531
+ });
477
532
 
478
- if ( ! target ) {
533
+ if ( !index ) {
479
534
  return;
480
535
  }
481
536
 
482
- this.$refs.scrollbar.scrollIntoView(
483
- `[data-option="${target._.uid}"]`, 150);
484
- }
485
-
486
- },
537
+ if ( this.$refs.virtualbar ) {
538
+ this.$refs.virtualbar.scrollToIndex(index, 250);
539
+ }
487
540
 
488
- beforeMount()
489
- {
490
- if ( this.multiple && ! Any.isArray(this.tempValue) ) {
491
- this.tempValue = [];
492
- }
541
+ let select = `[data-option="${Obj.get(this.elements[index], '_.uid', 0)}"]`;
493
542
 
494
- if ( this.multiple && ! Any.isArray(this.clearValue) ) {
495
- this.tempClear = [];
543
+ if ( this.$refs.scrollbar ) {
544
+ this.$refs.scrollbar.scrollIntoView(select, 250);
545
+ }
496
546
  }
497
547
 
498
- this.searchOptions();
499
548
  },
500
549
 
501
-
502
550
  renderLabelClear()
503
551
  {
504
- if ( ! this.clearable || Any.isEmpty(this.tempValue) ) {
552
+ if ( !this.clearable || Any.isEmpty(this.tempValue) ) {
505
553
  return null;
506
554
  }
507
555
 
508
556
  let props = {};
509
557
 
510
- if ( ! this.deepDisabled ) {
558
+ if ( !this.deepDisabled ) {
511
559
  props.onMousedown = this.clear;
512
560
  }
513
561
 
@@ -537,17 +585,17 @@ export default {
537
585
  class: nano.Icons.times,
538
586
  };
539
587
 
540
- if ( ! this.deepDisabled ) {
588
+ if ( !this.deepDisabled ) {
541
589
  props.onMousedown = (event) => this.toggleOption(value, event);
542
590
  }
543
591
 
544
592
  let labelHtml = (
545
- <span>{ this.getOptionLabel(value) }</span>
593
+ <span>{this.getOptionLabel(value)}</span>
546
594
  );
547
595
 
548
596
  return (
549
597
  <div class={classList}>
550
- { [labelHtml, <i {...props}></i>] }
598
+ {[labelHtml, <i {...props}></i>]}
551
599
  </div>
552
600
  );
553
601
  },
@@ -556,7 +604,7 @@ export default {
556
604
  {
557
605
  let first = Arr.first(this.tempValue);
558
606
 
559
- if ( ! first ) {
607
+ if ( !first ) {
560
608
  return null;
561
609
  }
562
610
 
@@ -570,7 +618,7 @@ export default {
570
618
 
571
619
  let collapseHtml = (
572
620
  <div class="n-select__item">
573
- <span>{ this.choice(this.collapseText, count) }</span>
621
+ <span>{this.choice(this.collapseText, count)}</span>
574
622
  </div>
575
623
  );
576
624
 
@@ -581,7 +629,7 @@ export default {
581
629
 
582
630
  renderLabelItems()
583
631
  {
584
- if ( ! Any.isArray(this.tempValue) ) {
632
+ if ( !Any.isArray(this.tempValue) ) {
585
633
  return null;
586
634
  }
587
635
 
@@ -597,7 +645,7 @@ export default {
597
645
  renderMultiple()
598
646
  {
599
647
  let isEmptyValue = Any.isEmpty(this.tempValue) &&
600
- ! Any.isNumber(this.tempValue);
648
+ !Any.isNumber(this.tempValue);
601
649
 
602
650
  let props = {
603
651
  value: this.search,
@@ -608,11 +656,11 @@ export default {
608
656
  onKeydown: this.onKeydownInput
609
657
  };
610
658
 
611
- if ( ! this.focus ) {
659
+ if ( !this.focus ) {
612
660
  props.value = null;
613
661
  }
614
662
 
615
- if ( ! isEmptyValue ) {
663
+ if ( !isEmptyValue ) {
616
664
  props.placeholder = null;
617
665
  }
618
666
 
@@ -623,12 +671,12 @@ export default {
623
671
  );
624
672
 
625
673
  return [
626
- this.ctor('renderLabelClear')(),
674
+ this.ctor('renderLabelClear')(),
627
675
  (
628
676
  <div class="n-select__items">
629
- { [this.ctor('renderLabelItems')(), inputHtml] }
677
+ {[this.ctor('renderLabelItems')(), inputHtml]}
630
678
  </div>
631
- ),
679
+ ),
632
680
  this.ctor('renderLabelAngle')()
633
681
  ];
634
682
  },
@@ -636,11 +684,11 @@ export default {
636
684
  renderSingle()
637
685
  {
638
686
  let isEmptyValue = Any.isEmpty(this.tempValue) &&
639
- ! Any.isNumber(this.tempValue);
687
+ !Any.isNumber(this.tempValue);
640
688
 
641
689
  let modelLabel = this.getOptionLabel(
642
690
  this.tempValue);
643
-
691
+
644
692
  if ( isEmptyValue ) {
645
693
  modelLabel = null;
646
694
  }
@@ -654,25 +702,25 @@ export default {
654
702
  onKeydown: this.onKeydownInput
655
703
  };
656
704
 
657
- if ( ! this.search && this.custom ) {
705
+ if ( !this.search && this.custom ) {
658
706
  props.value = this.tempValue;
659
707
  }
660
708
 
661
- if ( ! this.focus ) {
709
+ if ( !this.focus ) {
662
710
  props.value = modelLabel;
663
711
  }
664
712
 
665
- if ( ! isEmptyValue ) {
713
+ if ( !isEmptyValue ) {
666
714
  props.placeholder = modelLabel;
667
715
  }
668
716
 
669
717
  return [
670
- this.ctor('renderLabelClear')(),
718
+ this.ctor('renderLabelClear')(),
671
719
  (
672
720
  <div class="n-select__input">
673
721
  <input ref="input" {...props} />
674
722
  </div>
675
- ),
723
+ ),
676
724
  this.ctor('renderLabelAngle')()
677
725
  ];
678
726
  },
@@ -694,7 +742,7 @@ export default {
694
742
  }
695
743
 
696
744
  return (
697
- <div class={classList}>{ displayHtml() }</div>
745
+ <div class={classList}>{displayHtml()}</div>
698
746
  );
699
747
  },
700
748
 
@@ -702,14 +750,18 @@ export default {
702
750
  {
703
751
  let emptyHtml = (
704
752
  <div class="n-select__empty">
705
- <NEmptyIcon>{ this.trans(this.emptyText) }</NEmptyIcon>
753
+ <NEmptyIcon>{this.trans(this.emptyText)}</NEmptyIcon>
706
754
  </div>
707
755
  );
708
-
709
- if ( ! this.searched.length ) {
756
+
757
+ if ( !this.searched.length ) {
710
758
  return emptyHtml;
711
759
  }
712
760
 
761
+ if ( this.lazy ) {
762
+ return this.ctor('renderLazyItems')();
763
+ }
764
+
713
765
  let options = Obj.each(this.searched, (option, index) => {
714
766
  return option.ctor('renderOption')(index);
715
767
  });
@@ -720,17 +772,82 @@ export default {
720
772
 
721
773
  return (
722
774
  <NScrollbar ref="scrollbar" class="n-select__body" {...props}>
723
- { Obj.values(options) }
775
+ {Obj.values(options)}
724
776
  </NScrollbar>
725
777
  );
726
778
  },
727
779
 
780
+ renderLazyOption(value, index)
781
+ {
782
+ let classList = [];
783
+
784
+ let isMultipleActive = this.multiple &&
785
+ Arr.has(this.tempValue, value.value);
786
+
787
+ if ( isMultipleActive ) {
788
+ classList.push('n-active');
789
+ }
790
+
791
+ let isSingleActive = ! this.multiple &&
792
+ this.tempValue === value.value;
793
+
794
+ if ( isSingleActive ) {
795
+ classList.push('n-active');
796
+ }
797
+
798
+ if ( this.index === Num.int(index) ) {
799
+ classList.push('n-focus');
800
+ }
801
+
802
+ let props = {
803
+ 'type': this.type,
804
+ 'clickClose': ! this.multiple,
805
+ };
806
+
807
+ props['onMousedown'] = (e) => {
808
+ this.toggleOption(value.value, e);
809
+ };
810
+
811
+ if ( isSingleActive || isMultipleActive ) {
812
+ props.icon = nano.Icons.checked;
813
+ }
814
+
815
+ return (
816
+ <NPopoverOption class={classList} {...props}>
817
+ {value.label}
818
+ </NPopoverOption>
819
+ );
820
+ },
821
+
822
+ renderLazyItems()
823
+ {
824
+ let props = {
825
+ items: this.searched
826
+ };
827
+
828
+ props.renderNode = ({ value, index }) => {
829
+ return this.ctor('renderLazyOption')(value, index);
830
+ };
831
+
832
+ return (
833
+ <NVirtualscroller ref="virtualbar" class="n-select__body n-virtual" {...props} />
834
+ );
835
+ },
836
+
728
837
  renderPopover()
729
838
  {
839
+ let classList = [
840
+ 'n-popover--select'
841
+ ];
842
+
843
+ if ( this.lazy ) {
844
+ classList.push('n-virtual');
845
+ }
846
+
730
847
  let props = {
731
- class: 'n-popover--select',
848
+ class: classList,
732
849
  trigger: 'click',
733
- width: -1,
850
+ width: - 1,
734
851
  listen: true,
735
852
  size: this.size,
736
853
  scrollClose: true,
@@ -740,13 +857,17 @@ export default {
740
857
 
741
858
  return (
742
859
  <NPopover ref="popover" vModel={this.focus} {...props}>
743
- { { raw: this.ctor('renderItems') } }
860
+ {{ raw: this.ctor('renderItems') }}
744
861
  </NPopover>
745
862
  );
746
863
  },
747
864
 
748
865
  renderOptions()
749
866
  {
867
+ if ( this.lazy ) {
868
+ return null;
869
+ }
870
+
750
871
  if ( Any.isEmpty(this.options) ) {
751
872
  return this.$slots.default && this.$slots.default();
752
873
  }
@@ -773,7 +894,7 @@ export default {
773
894
  ];
774
895
 
775
896
  let isEmptyValue = Any.isEmpty(this.tempValue) &&
776
- ! Any.isNumber(this.tempValue);
897
+ !Any.isNumber(this.tempValue);
777
898
 
778
899
  if ( isEmptyValue ) {
779
900
  classList.push('n-empty');
@@ -793,9 +914,9 @@ export default {
793
914
 
794
915
  return (
795
916
  <div class={classList} onClick={this.focusInput}>
796
- { this.ctor('renderDisplay')() }
797
- { this.ctor('renderPopover')() }
798
- { this.ctor('renderOptions')() }
917
+ {this.ctor('renderDisplay')()}
918
+ {this.ctor('renderPopover')()}
919
+ {this.ctor('renderOptions')()}
799
920
  </div>
800
921
  );
801
922
  }
@@ -117,10 +117,18 @@
117
117
  max-height: 210px;
118
118
  }
119
119
 
120
+ .n-select__body.n-virtual {
121
+ min-height: 210px;
122
+ }
123
+
120
124
  .n-select__empty {
121
125
  text-align: center;
122
126
  }
123
127
 
128
+ .n-popover--select.n-virtual {
129
+ overflow: hidden;
130
+ }
131
+
124
132
  @each $suffix, $values in $form {
125
133
 
126
134
  $-select-font: map-get($values, 'font');