@kizmann/nano-ui 0.9.0 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
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');