@onehat/ui 0.3.59 → 0.3.60

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": "@onehat/ui",
3
- "version": "0.3.59",
3
+ "version": "0.3.60",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  import React, { useState, useEffect, useRef, } from 'react';
2
2
  import {
3
- Box,
3
+ Modal,
4
4
  Popover,
5
5
  Pressable,
6
6
  Row,
@@ -8,6 +8,7 @@ import {
8
8
  Tooltip,
9
9
  } from 'native-base';
10
10
  import {
11
+ UI_MODE_REACT_NATIVE,
11
12
  UI_MODE_WEB,
12
13
  } from '../../../../Constants/UiModes.js';
13
14
  import UiGlobals from '../../../../UiGlobals.js';
@@ -60,7 +61,6 @@ export function ComboComponent(props) {
60
61
  triggerRef = useRef(),
61
62
  menuRef = useRef(),
62
63
  displayValueRef = useRef(null),
63
- savedSearch = useRef(null),
64
64
  typingTimeout = useRef(),
65
65
  [isMenuShown, setIsMenuShown] = useState(false),
66
66
  [isRendered, setIsRendered] = useState(false),
@@ -117,12 +117,6 @@ export function ComboComponent(props) {
117
117
  toggleMenu = () => {
118
118
  setIsMenuShown(!isMenuShown);
119
119
  },
120
- getSavedSearch = () => {
121
- return savedSearch.current;
122
- },
123
- setSavedSearch = (val) => {
124
- savedSearch.current = val;
125
- },
126
120
  resetInputTextValue = () => {
127
121
  setTextInputValue(getDisplayValue());
128
122
  },
@@ -186,9 +180,7 @@ export function ComboComponent(props) {
186
180
  }
187
181
 
188
182
  if (_.isEmpty(value)) {
189
- // text input is cleared
190
- hideMenu();
191
- return;
183
+ setValue(null);
192
184
  }
193
185
 
194
186
  setTextInputValue(value);
@@ -200,7 +192,9 @@ export function ComboComponent(props) {
200
192
  }, 300);
201
193
  },
202
194
  onInputFocus = (e) => {
203
- inputRef.current.select();
195
+ if (inputRef.current.select) {
196
+ inputRef.current.select();
197
+ }
204
198
  },
205
199
  onInputBlur = (e) => {
206
200
  if (isEventStillInComponent(e)) {
@@ -278,8 +272,6 @@ export function ComboComponent(props) {
278
272
  } else {
279
273
  throw Error('Not yet implemented');
280
274
  }
281
-
282
- setSavedSearch(null);
283
275
 
284
276
  } else {
285
277
  // throw Error('Not yet implemented');
@@ -331,7 +323,6 @@ export function ComboComponent(props) {
331
323
  Repository.filter(filter);
332
324
  }
333
325
 
334
- setSavedSearch(value);
335
326
  setNewEntityDisplayValue(value); // capture the search query so we can tell Grid what to use for a new entity's displayValue
336
327
 
337
328
  } else {
@@ -435,105 +426,47 @@ export function ComboComponent(props) {
435
426
  self.clear = onClearBtn;
436
427
  }
437
428
 
438
- const refProps = {};
439
- if (tooltipRef) {
440
- refProps.ref = tooltipRef;
429
+ let xButton = null,
430
+ inputAndTrigger = null,
431
+ grid = null,
432
+ dropdownMenu = null,
433
+ assembledComponents = null;
434
+
435
+ if (showXButton && !_.isNil(value)) {
436
+ xButton = <IconButton
437
+ _icon={{
438
+ as: Xmark,
439
+ color: 'trueGray.600',
440
+ size: 'sm',
441
+ }}
442
+ isDisabled={isDisabled}
443
+ onPress={onClearBtn}
444
+ h="100%"
445
+ bg={styles.FORM_COMBO_TRIGGER_BG}
446
+ _hover={{
447
+ bg: styles.FORM_COMBO_TRIGGER_HOVER_BG,
448
+ }}
449
+ />;
441
450
  }
442
451
 
443
- const gridProps = _.pick(props, [
444
- 'Editor',
445
- 'model',
446
- 'Repository',
447
- 'data',
448
- 'idIx',
449
- 'displayIx',
450
- 'value',
451
- 'disableView',
452
- 'disableCopy',
453
- 'disableDuplicate',
454
- 'disablePrint',
455
- ]);
456
-
457
- const WhichGrid = isEditor ? WindowedGridEditor : Grid;
458
-
459
- let comboComponent = <Row {...refProps} justifyContent="center" alignItems="center" h={styles.FORM_COMBO_HEIGHT} flex={1} onLayout={() => setIsRendered(true)}>
460
- {showXButton && !_.isNil(value) &&
461
- <IconButton
462
- _icon={{
463
- as: Xmark,
464
- color: 'trueGray.600',
465
- size: 'sm',
466
- }}
467
- isDisabled={isDisabled}
468
- onPress={onClearBtn}
469
- h="100%"
470
- bg={styles.FORM_COMBO_TRIGGER_BG}
471
- _hover={{
472
- bg: styles.FORM_COMBO_TRIGGER_HOVER_BG,
473
- }}
474
- />}
475
- {disableDirectEntry ?
476
- <Pressable
477
- onPress={toggleMenu}
478
- flex={1}
479
- h="100%"
480
- >
481
- <Text
482
- ref={inputRef}
483
- onLayout={(e) => {
484
- // On web, this is not needed, but on RN it might be, so leave it in for now
485
- const {
486
- height,
487
- width,
488
- top,
489
- left,
490
- } = e.nativeEvent.layout;
491
- setWidth(width);
492
- setTop(top + height);
493
- setLeft(left);
494
- }}
495
- flex={1}
496
- h="100%"
497
- numberOfLines={1}
498
- ellipsizeMode="head"
499
- m={0}
500
- p={2}
501
- borderWidth={1}
502
- borderColor="trueGray.400"
503
- borderTopRightRadius={0}
504
- borderBottomRightRadius={0}
505
- fontSize={styles.FORM_COMBO_INPUT_FONTSIZE}
506
- bg={styles.FORM_COMBO_INPUT_BG}
507
- _focus={{
508
- bg: styles.FORM_COMBO_INPUT_FOCUS_BG,
509
- }}
510
- >{textInputValue}</Text>
511
- </Pressable> :
512
- <Input
452
+ if (UiGlobals.mode === UI_MODE_WEB) {
453
+ inputAndTrigger = <>
454
+ {disableDirectEntry ?
455
+ <Pressable
456
+ onPress={toggleMenu}
457
+ flex={1}
458
+ h="100%"
459
+ >
460
+ <Text
513
461
  ref={inputRef}
514
- value={textInputValue}
515
- autoSubmit={true}
516
- isDisabled={isDisabled}
517
- onChangeValue={onInputChangeText}
518
- onKeyPress={onInputKeyPress}
519
- onFocus={onInputFocus}
520
- onBlur={onInputBlur}
521
- onLayout={(e) => {
522
- // On web, this is not needed, but on RN it might be, so leave it in for now
523
- const {
524
- height,
525
- width,
526
- top,
527
- left,
528
- } = e.nativeEvent.layout;
529
- setWidth(width);
530
- setTop(top + height);
531
- setLeft(left);
532
- }}
533
462
  flex={1}
534
463
  h="100%"
464
+ numberOfLines={1}
465
+ ellipsizeMode="head"
535
466
  m={0}
536
- autoSubmitDelay={0}
467
+ p={2}
468
+ borderWidth={1}
469
+ borderColor="trueGray.400"
537
470
  borderTopRightRadius={0}
538
471
  borderBottomRightRadius={0}
539
472
  fontSize={styles.FORM_COMBO_INPUT_FONTSIZE}
@@ -541,171 +474,360 @@ export function ComboComponent(props) {
541
474
  _focus={{
542
475
  bg: styles.FORM_COMBO_INPUT_FOCUS_BG,
543
476
  }}
544
- {..._input}
545
- />}
546
- <IconButton
547
- ref={triggerRef}
548
- _icon={{
549
- as: CaretDown,
550
- color: 'primary.800',
551
- size: 'sm',
552
- }}
477
+ >{textInputValue}</Text>
478
+ </Pressable> :
479
+ <Input
480
+ ref={inputRef}
481
+ value={textInputValue}
482
+ autoSubmit={true}
553
483
  isDisabled={isDisabled}
554
- onPress={onTriggerPress}
555
- onBlur={onTriggerBlur}
484
+ onChangeValue={onInputChangeText}
485
+ onKeyPress={onInputKeyPress}
486
+ onFocus={onInputFocus}
487
+ onBlur={onInputBlur}
488
+ onLayout={(e) => {
489
+ const {
490
+ height,
491
+ width,
492
+ } = e.nativeEvent.layout;
493
+ setWidth(Math.round(width));
494
+ setTop(Math.round(height));
495
+ }}
496
+ flex={1}
556
497
  h="100%"
557
- borderWidth={1}
558
- borderColor="#bbb"
559
- borderLeftWidth={0}
560
- borderLeftRadius={0}
561
- borderRightRadius="md"
562
- bg={styles.FORM_COMBO_TRIGGER_BG}
563
- _hover={{
564
- bg: styles.FORM_COMBO_TRIGGER_HOVER_BG,
498
+ m={0}
499
+ autoSubmitDelay={0}
500
+ borderTopRightRadius={0}
501
+ borderBottomRightRadius={0}
502
+ fontSize={styles.FORM_COMBO_INPUT_FONTSIZE}
503
+ bg={styles.FORM_COMBO_INPUT_BG}
504
+ _focus={{
505
+ bg: styles.FORM_COMBO_INPUT_FOCUS_BG,
565
506
  }}
566
- />
567
- {additionalButtons}
568
- <Popover
569
- isOpen={isMenuShown}
570
- onClose={() => {
571
- hideMenu();
507
+ {..._input}
508
+ />}
509
+ <IconButton
510
+ ref={triggerRef}
511
+ _icon={{
512
+ as: CaretDown,
513
+ color: 'primary.800',
514
+ size: 'sm',
515
+ }}
516
+ isDisabled={isDisabled}
517
+ onPress={onTriggerPress}
518
+ onBlur={onTriggerBlur}
519
+ h="100%"
520
+ borderWidth={1}
521
+ borderColor="#bbb"
522
+ borderLeftWidth={0}
523
+ borderLeftRadius={0}
524
+ borderRightRadius="md"
525
+ bg={styles.FORM_COMBO_TRIGGER_BG}
526
+ _hover={{
527
+ bg: styles.FORM_COMBO_TRIGGER_HOVER_BG,
528
+ }}
529
+ />
530
+ </>;
531
+ }
532
+
533
+ if (UiGlobals.mode === UI_MODE_REACT_NATIVE) {
534
+ // This input and trigger are for show
535
+ // The just show the current value and open the menu
536
+ inputAndTrigger = <>
537
+ <Pressable
538
+ onPress={showMenu}
539
+ flex={1}
540
+ >
541
+ <Text
542
+ flex={1}
543
+ h="100%"
544
+ numberOfLines={1}
545
+ ellipsizeMode="head"
546
+ m={0}
547
+ p={2}
548
+ borderWidth={1}
549
+ borderColor="trueGray.400"
550
+ borderTopRightRadius={0}
551
+ borderBottomRightRadius={0}
552
+ fontSize={styles.FORM_COMBO_INPUT_FONTSIZE}
553
+ bg={styles.FORM_COMBO_INPUT_BG}
554
+ _focus={{
555
+ bg: styles.FORM_COMBO_INPUT_FOCUS_BG,
572
556
  }}
573
- trigger={emptyFn}
574
- trapFocus={false}
575
- placement={'auto'}
576
- // _fade={{
577
- // entryDuration: 0, // Doesn't work, as Popover doesn't have animation controls like Modal does. See node_modules/native-base/src/components/composites/Popover/Popover.tsx line 99 (vs .../composites/Modal/Modal.tsx line 113 which has ..._fade) I added a feature request to NativeBase https://github.com/GeekyAnts/NativeBase/issues/5651
578
- // }}
579
- {...props}
557
+ >{textInputValue}</Text>
558
+ </Pressable>
559
+ <IconButton
560
+ ref={triggerRef}
561
+ _icon={{
562
+ as: CaretDown,
563
+ color: 'primary.800',
564
+ size: 'sm',
565
+ }}
566
+ isDisabled={isDisabled}
567
+ onPress={onTriggerPress}
568
+ h="100%"
569
+ borderWidth={1}
570
+ borderColor="#bbb"
571
+ borderLeftWidth={0}
572
+ borderLeftRadius={0}
573
+ borderRightRadius="md"
574
+ bg={styles.FORM_COMBO_TRIGGER_BG}
575
+ _hover={{
576
+ bg: styles.FORM_COMBO_TRIGGER_HOVER_BG,
577
+ }}
578
+ />
579
+ </>;
580
+ }
581
+
582
+ if (isMenuShown) {
583
+ const gridProps = _.pick(props, [
584
+ 'Editor',
585
+ 'model',
586
+ 'Repository',
587
+ 'data',
588
+ 'idIx',
589
+ 'displayIx',
590
+ 'value',
591
+ 'disableView',
592
+ 'disableCopy',
593
+ 'disableDuplicate',
594
+ 'disablePrint',
595
+ ]);
596
+ const WhichGrid = isEditor ? WindowedGridEditor : Grid;
597
+ grid = <WhichGrid
598
+ showHeaders={false}
599
+ showHovers={true}
600
+ shadow={1}
601
+ getRowProps={() => {
602
+ return {
603
+ borderBottomWidth: 1,
604
+ borderBottomColor: 'trueGray.300',
605
+ py: 1,
606
+ pl: 4,
607
+ pr: 2,
608
+ w: '100%',
609
+ };
610
+ }}
611
+ autoAdjustPageSizeToHeight={false}
612
+ {...gridProps}
613
+ reference="dropdownGrid"
614
+ parent={self}
615
+ h={UiGlobals.mode === UI_MODE_WEB ? styles.FORM_COMBO_MENU_HEIGHT + 'px' : null}
616
+ newEntityDisplayValue={newEntityDisplayValue}
617
+ disablePresetButtons={!isEditor}
618
+ onChangeSelection={(selection) => {
619
+
620
+ if (Repository && selection[0]?.isPhantom) {
621
+ // do nothing
622
+ return;
623
+ }
624
+
625
+ setGridSelection(selection);
626
+
627
+ if (Repository) {
628
+
629
+ // When we first open the menu, we try to match the selection to the value, ignore this
630
+ if (selection[0]?.displayValue === getDisplayValue()) {
631
+ return;
632
+ }
633
+
634
+ // when user selected the record matching the current value, kill search mode
635
+ if (selection[0]?.id === value) {
636
+ setIsSearchMode(false);
637
+ resetInputTextValue();
638
+ if (hideMenuOnSelection) {
639
+ hideMenu();
640
+ }
641
+ return;
642
+ }
643
+
644
+ setValue(selection[0] ? selection[0].id : null);
645
+
646
+ } else {
647
+
648
+ // When we first open the menu, we try to match the selection to the value, ignore this
649
+ if (selection[0] && selection[0][displayIx] === getDisplayValue()) {
650
+ return;
651
+ }
652
+
653
+ // when user selected the record matching the current value, kill search mode
654
+ if (selection[0] && selection[0][idIx] === value) {
655
+ setIsSearchMode(false);
656
+ resetInputTextValue();
657
+ if (hideMenuOnSelection) {
658
+ hideMenu();
659
+ }
660
+ return;
661
+ }
662
+
663
+ setValue(selection[0] ? selection[0][idIx] : null);
664
+
665
+ }
666
+
667
+ if (_.isEmpty(selection)) {
668
+ return;
669
+ }
670
+
671
+ if (hideMenuOnSelection && !isEditor) {
672
+ hideMenu();
673
+ }
674
+
675
+ }}
676
+ onSave={(selection) => {
677
+ const entity = selection[0];
678
+ if (entity?.id !== value) {
679
+ // Either a phantom record was just solidified into a real record, or a new (non-phantom) record was added.
680
+ // Select it and set the value of the combo.
681
+ setGridSelection([entity]);
682
+ const id = entity.id;
683
+ setValue(id);
684
+ }
685
+ }}
686
+ onRowPress={(item, e) => {
687
+ if (onRowPress) {
688
+ onRowPress(item, e);
689
+ return;
690
+ }
691
+ const id = Repository ? item.id : item[idIx];
692
+ if (id === value) {
693
+ hideMenu();
694
+ onInputFocus();
695
+ }
696
+ }}
697
+ />;
698
+ if (UiGlobals.mode === UI_MODE_WEB) {
699
+ dropdownMenu = <Popover
700
+ isOpen={isMenuShown}
701
+ onClose={() => {
702
+ hideMenu();
703
+ }}
704
+ trigger={emptyFn}
705
+ trapFocus={false}
706
+ placement={'auto'}
707
+ // _fade={{
708
+ // entryDuration: 0, // Doesn't work, as Popover doesn't have animation controls like Modal does. See node_modules/native-base/src/components/composites/Popover/Popover.tsx line 99 (vs .../composites/Modal/Modal.tsx line 113 which has ..._fade) I added a feature request to NativeBase https://github.com/GeekyAnts/NativeBase/issues/5651
709
+ // }}
710
+ {...props}
711
+ >
712
+ <Popover.Content
713
+ position="absolute"
714
+ top={top + 'px'}
715
+ left={left + 'px'}
716
+ w={width + 'px'}
717
+ minWidth={menuMinWidth}
718
+ overflow="auto"
719
+ bg="#fff"
720
+
721
+ h={200}
580
722
  >
581
- <Popover.Content
582
- position="absolute"
583
- top={top + 'px'}
584
- left={left + 'px'}
585
- w={width + 'px'}
586
- minWidth={menuMinWidth}
587
- overflow="auto"
588
- bg="#fff"
723
+ <Popover.Body
724
+ ref={menuRef}
725
+ borderWidth={1}
726
+ borderColor='trueGray.400'
727
+ borderTopWidth={0}
728
+ p={0}
589
729
  >
590
- <Popover.Body
591
- ref={menuRef}
592
- borderWidth={1}
593
- borderColor='trueGray.400'
594
- borderTopWidth={0}
595
- p={0}
596
- >
597
- <WhichGrid
598
- showHeaders={false}
599
- showHovers={true}
600
- shadow={1}
601
- getRowProps={() => {
602
- return {
603
- borderBottomWidth: 1,
604
- borderBottomColor: 'trueGray.300',
605
- py: 1,
606
- pl: 4,
607
- pr: 2,
608
- w: '100%',
609
- };
610
- }}
611
- autoAdjustPageSizeToHeight={false}
612
- {...gridProps}
613
- reference="dropdownGrid"
614
- parent={self}
615
- h={styles.FORM_COMBO_MENU_HEIGHT + 'px'}
616
- newEntityDisplayValue={newEntityDisplayValue}
617
- disablePresetButtons={!isEditor}
618
- onChangeSelection={(selection) => {
619
-
620
- if (Repository && selection[0]?.isPhantom) {
621
- // do nothing
622
- return;
623
- }
624
-
625
- setGridSelection(selection);
626
-
627
- if (Repository) {
628
-
629
- // When we first open the menu, we try to match the selection to the value, ignore this
630
- if (selection[0]?.displayValue === getDisplayValue()) {
631
- return;
632
- }
633
-
634
- // when user selected the record matching the current value, kill search mode
635
- if (selection[0]?.id === value) {
636
- setIsSearchMode(false);
637
- resetInputTextValue();
638
- if (hideMenuOnSelection) {
639
- hideMenu();
640
- }
641
- return;
642
- }
643
-
644
- setValue(selection[0] ? selection[0].id : null);
645
-
646
- } else {
647
-
648
- // When we first open the menu, we try to match the selection to the value, ignore this
649
- if (selection[0] && selection[0][displayIx] === getDisplayValue()) {
650
- return;
651
- }
652
-
653
- // when user selected the record matching the current value, kill search mode
654
- if (selection[0] && selection[0][idIx] === value) {
655
- setIsSearchMode(false);
656
- resetInputTextValue();
657
- if (hideMenuOnSelection) {
658
- hideMenu();
659
- }
660
- return;
661
- }
662
-
663
- setValue(selection[0] ? selection[0][idIx] : null);
664
-
665
- }
666
-
667
- if (_.isEmpty(selection)) {
668
- return;
669
- }
670
-
671
- if (hideMenuOnSelection && !isEditor) {
672
- hideMenu();
673
- }
674
-
675
- }}
676
- onSave={(selection) => {
677
- const entity = selection[0];
678
- if (entity?.id !== value) {
679
- // Either a phantom record was just solidified into a real record, or a new (non-phantom) record was added.
680
- // Select it and set the value of the combo.
681
- setGridSelection([entity]);
682
- const id = entity.id;
683
- setValue(id);
684
- }
685
- }}
686
- onRowPress={(item, e) => {
687
- if (onRowPress) {
688
- onRowPress(item, e);
689
- return;
690
- }
691
- const id = Repository ? item.id : item[idIx];
692
- if (id === value) {
693
- hideMenu();
694
- onInputFocus();
695
- }
696
- }}
697
- />
698
- </Popover.Body>
699
- </Popover.Content>
700
- </Popover>
701
- </Row>;
730
+ {grid}
731
+ </Popover.Body>
732
+ </Popover.Content>
733
+ </Popover>;
734
+ }
735
+ if (UiGlobals.mode === UI_MODE_REACT_NATIVE) {
736
+ const inputAndTriggerClone = // for RN, this is the actual input and trigger, as we need them to appear up above in the modal
737
+ <Row h={10}>
738
+ {disableDirectEntry ?
739
+ <Text
740
+ ref={inputRef}
741
+ flex={1}
742
+ h="100%"
743
+ numberOfLines={1}
744
+ ellipsizeMode="head"
745
+ m={0}
746
+ p={2}
747
+ borderWidth={1}
748
+ borderColor="trueGray.400"
749
+ borderTopRightRadius={0}
750
+ borderBottomRightRadius={0}
751
+ fontSize={styles.FORM_COMBO_INPUT_FONTSIZE}
752
+ bg={styles.FORM_COMBO_INPUT_BG}
753
+ _focus={{
754
+ bg: styles.FORM_COMBO_INPUT_FOCUS_BG,
755
+ }}
756
+ >{textInputValue}</Text> :
757
+ <Input
758
+ ref={inputRef}
759
+ value={textInputValue}
760
+ autoSubmit={true}
761
+ isDisabled={isDisabled}
762
+ onChangeValue={onInputChangeText}
763
+ onKeyPress={onInputKeyPress}
764
+ onFocus={onInputFocus}
765
+ onBlur={onInputBlur}
766
+ flex={1}
767
+ h="100%"
768
+ m={0}
769
+ autoSubmitDelay={0}
770
+ borderTopRightRadius={0}
771
+ borderBottomRightRadius={0}
772
+ fontSize={styles.FORM_COMBO_INPUT_FONTSIZE}
773
+ bg={styles.FORM_COMBO_INPUT_BG}
774
+ _focus={{
775
+ bg: styles.FORM_COMBO_INPUT_FOCUS_BG,
776
+ }}
777
+ {..._input}
778
+ />}
779
+ <IconButton
780
+ _icon={{
781
+ as: CaretDown,
782
+ color: 'primary.800',
783
+ size: 'sm',
784
+ }}
785
+ isDisabled={isDisabled}
786
+ onPress={() => hideMenu()}
787
+ h="100%"
788
+ borderWidth={1}
789
+ borderColor="#bbb"
790
+ borderLeftWidth={0}
791
+ borderLeftRadius={0}
792
+ borderRightRadius="md"
793
+ bg={styles.FORM_COMBO_TRIGGER_BG}
794
+ _hover={{
795
+ bg: styles.FORM_COMBO_TRIGGER_HOVER_BG,
796
+ }}
797
+ />
798
+ </Row>;
799
+ dropdownMenu = <Modal
800
+ isOpen={true}
801
+ onClose={() => setIsMenuShown(false)}
802
+ top="30%"
803
+ w="100%"
804
+ h={400}
805
+ px={5}
806
+ >
807
+ {inputAndTriggerClone}
808
+ {grid}
809
+ </Modal>;
810
+ }
811
+ }
812
+
813
+ const refProps = {};
814
+ if (tooltipRef) {
815
+ refProps.ref = tooltipRef;
816
+ }
817
+ assembledComponents = <Row {...refProps} justifyContent="center" alignItems="center" h={styles.FORM_COMBO_HEIGHT} flex={1} onLayout={() => setIsRendered(true)}>
818
+ {xButton}
819
+ {inputAndTrigger}
820
+ {additionalButtons}
821
+ {dropdownMenu}
822
+ </Row>;
823
+
702
824
  if (tooltip) {
703
- comboComponent = <Tooltip label={tooltip} placement={tooltipPlacement}>
704
- {comboComponent}
825
+ assembledComponents = <Tooltip label={tooltip} placement={tooltipPlacement}>
826
+ {assembledComponents}
705
827
  </Tooltip>;
706
828
  }
707
829
 
708
- return comboComponent;
830
+ return assembledComponents;
709
831
  }
710
832
 
711
833
  export const Combo = withComponent(
@@ -71,6 +71,7 @@ function Form(props) {
71
71
  footerProps = {},
72
72
  buttonGroupProps = {}, // buttons in footer
73
73
  checkIsEditingDisabled = true,
74
+ disableLabels = false,
74
75
  onBack,
75
76
  onReset,
76
77
  onViewMode,
@@ -295,6 +296,9 @@ function Form(props) {
295
296
  if (isHidden) {
296
297
  return null;
297
298
  }
299
+ if (type === 'DisplayField') {
300
+ isEditable = false;
301
+ }
298
302
  const propertyDef = name && Repository?.getSchema().getPropertyDefinition(name);
299
303
  if (!useAdditionalEditButtons) {
300
304
  item = _.omit(item, 'additionalEditButtons');
@@ -330,13 +334,13 @@ function Form(props) {
330
334
  editorTypeProps.autoLoad = true;
331
335
  }
332
336
  }
333
- if (isCombo) {
337
+ if (isCombo && _.isNil(propsToPass.showXButton)) {
334
338
  editorTypeProps.showXButton = true;
335
339
  }
336
340
  const Element = getComponentFromType(type);
337
341
  let children;
338
-
339
- if (inArray(type, ['Column', 'FieldSet'])) {
342
+
343
+ if (inArray(type, ['Column', 'Row', 'FieldSet'])) {
340
344
  if (_.isEmpty(items)) {
341
345
  return null;
342
346
  }
@@ -357,6 +361,9 @@ function Form(props) {
357
361
  }
358
362
  propsToPass.pl = 3;
359
363
  }
364
+ if (type === 'Row') {
365
+ propsToPass.w = '100%';
366
+ }
360
367
  const itemDefaults = item.defaults;
361
368
  children = _.map(items, (item, ix) => {
362
369
  return buildFromItem(item, ix, itemDefaults);
@@ -376,7 +383,7 @@ function Form(props) {
376
383
  reference={name}
377
384
  {...propsToPass}
378
385
  />;
379
- if (label) {
386
+ if (!disableLabels && label) {
380
387
  const labelProps = {};
381
388
  if (defaults?.labelWidth) {
382
389
  labelProps.w = defaults.labelWidth;
@@ -483,19 +490,24 @@ function Form(props) {
483
490
  </Row>;
484
491
  }
485
492
 
486
- if (label && editorType !== EDITOR_TYPE__INLINE) {
493
+ let requiredIndicator = null;
494
+ if (propertyDef?.validator?.spec && !propertyDef.validator.spec.optional) {
495
+ requiredIndicator = <Text color="#f00" fontSize="30px" pr={1}>*</Text>;
496
+ }
497
+ if (!disableLabels && label && editorType !== EDITOR_TYPE__INLINE) {
487
498
  const labelProps = {};
488
499
  if (defaults?.labelWidth) {
489
500
  labelProps.w = defaults.labelWidth;
490
501
  }
491
- let requiredIndicator = null;
492
- if (propertyDef?.validator?.spec && !propertyDef.validator.spec.optional) {
493
- requiredIndicator = <Text color="#f00" pr={1}>*</Text>;
494
- }
495
502
  element = <Row w="100%" py={1}>
496
503
  <Label {...labelProps}>{requiredIndicator}{label}</Label>
497
504
  {element}
498
505
  </Row>;
506
+ } else if (disableLabels && requiredIndicator) {
507
+ element = <Row w="100%" py={1}>
508
+ {requiredIndicator}
509
+ {element}
510
+ </Row>;
499
511
  }
500
512
 
501
513
  const dirtyIcon = isDirty ? <Icon as={Pencil} size="2xs" color="trueGray.300" position="absolute" top="2px" left="2px" /> : null;
@@ -740,8 +752,7 @@ function Form(props) {
740
752
  }
741
753
 
742
754
  return <Column {...sizeProps} onLayout={onLayoutDecorated} ref={formRef}>
743
- {containerWidth && <>
744
-
755
+ {!!containerWidth && <>
745
756
  {editorType === EDITOR_TYPE__INLINE &&
746
757
  <ScrollView
747
758
  horizontal={true}
@@ -5,6 +5,7 @@ import {
5
5
  Pressable,
6
6
  Icon,
7
7
  Row,
8
+ ScrollView,
8
9
  Text,
9
10
  } from 'native-base';
10
11
  import {
@@ -97,7 +98,7 @@ function GridComponent(props) {
97
98
  additionalToolbarButtons = [],
98
99
  h,
99
100
  flex,
100
- bg,
101
+ bg = '#fff',
101
102
 
102
103
  // withComponent
103
104
  self,
@@ -177,8 +178,8 @@ function GridComponent(props) {
177
178
  }
178
179
  const
179
180
  {
180
- shiftKey,
181
- metaKey,
181
+ shiftKey = false,
182
+ metaKey = false,
182
183
  } = e;
183
184
  let allowToggle = allowToggleSelection;
184
185
  if (metaKey) {
@@ -277,24 +278,31 @@ function GridComponent(props) {
277
278
  if (isHeaderRow || isDragMode) {
278
279
  return
279
280
  }
280
- switch (e.detail) {
281
- case 1: // single click
282
- onRowClick(item, e); // sets selection
283
- if (onEditorRowClick) {
284
- onEditorRowClick(item, index, e);
285
- }
286
- break;
287
- case 2: // double click
288
- if (!isSelected) { // If a row was already selected when double-clicked, the first click will deselect it,
289
- onRowClick(item, e); // so reselect it
290
- }
291
- if (onEdit) {
292
- onEdit();
293
- }
294
- break;
295
- case 3: // triple click
296
- break;
297
- default:
281
+ if (CURRENT_MODE === UI_MODE_WEB) {
282
+ switch (e.detail) {
283
+ case 1: // single click
284
+ onRowClick(item, e); // sets selection
285
+ if (onEditorRowClick) {
286
+ onEditorRowClick(item, index, e);
287
+ }
288
+ break;
289
+ case 2: // double click
290
+ if (!isSelected) { // If a row was already selected when double-clicked, the first click will deselect it,
291
+ onRowClick(item, e); // so reselect it
292
+ }
293
+ if (onEdit) {
294
+ onEdit();
295
+ }
296
+ break;
297
+ case 3: // triple click
298
+ break;
299
+ default:
300
+ }
301
+ } else if (CURRENT_MODE === UI_MODE_REACT_NATIVE) {
302
+ onRowClick(item, e); // sets selection
303
+ if (onEditorRowClick) {
304
+ onEditorRowClick(item, index, e);
305
+ }
298
306
  }
299
307
  }}
300
308
  onLongPress={(e) => {
@@ -877,43 +885,40 @@ function GridComponent(props) {
877
885
  deselectAll();
878
886
  }
879
887
  }}>
880
- {!entities?.length ? <NoRecordsFound text={noneFoundText} onRefresh={onRefresh} /> :
881
- <FlatList
882
- ref={gridRef}
883
- // ListHeaderComponent={listHeaderComponent}
884
- // ListFooterComponent={listFooterComponent}
885
- scrollEnabled={true}
886
- nestedScrollEnabled={true}
887
- contentContainerStyle={{
888
- overflow: 'auto',
889
- borderWidth: isDragMode ? styles.REORDER_BORDER_WIDTH : 0,
890
- borderColor: isDragMode ? styles.REORDER_BORDER_COLOR : null,
891
- borderStyle: styles.REORDER_BORDER_STYLE,
892
- flex: 1,
893
- }}
894
- refreshing={isLoading}
895
- onRefresh={pullToRefresh ? onRefresh : null}
896
- progressViewOffset={100}
897
- data={rowData}
898
- keyExtractor={(item) => {
899
- let id;
900
- if (item.id) {
901
- id = item.id;
902
- } else if (fields) {
903
- id = item[idIx];
904
- }
905
- return String(id);
906
- }}
907
- // getItemLayout={(data, index) => ( // an optional optimization that allows skipping the measurement of dynamic content if you know the size (height or width) of items ahead of time. getItemLayout is efficient if you have fixed size items
908
- // {length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
909
- // )}
910
- // numColumns={1}
911
- initialNumToRender={initialNumToRender}
912
- initialScrollIndex={0}
913
- renderItem={renderRow}
914
- bg="trueGray.100"
915
- {...flatListProps}
916
- />}
888
+ {!entities?.length ?
889
+ <NoRecordsFound text={noneFoundText} onRefresh={onRefresh} /> :
890
+ <ScrollView flex={1} w="100%">
891
+ <FlatList
892
+ ref={gridRef}
893
+ scrollEnabled={false}
894
+ nestedScrollEnabled={true}
895
+ contentContainerStyle={{
896
+ overflow: 'auto',
897
+ borderWidth: isDragMode ? styles.REORDER_BORDER_WIDTH : 0,
898
+ borderColor: isDragMode ? styles.REORDER_BORDER_COLOR : null,
899
+ borderStyle: styles.REORDER_BORDER_STYLE,
900
+ flex: 1,
901
+ }}
902
+ refreshing={isLoading}
903
+ onRefresh={pullToRefresh ? onRefresh : null}
904
+ progressViewOffset={100}
905
+ data={rowData}
906
+ keyExtractor={(item) => {
907
+ let id;
908
+ if (item.id) {
909
+ id = item.id;
910
+ } else if (fields) {
911
+ id = item[idIx];
912
+ }
913
+ return String(id);
914
+ }}
915
+ initialNumToRender={initialNumToRender}
916
+ initialScrollIndex={0}
917
+ renderItem={renderRow}
918
+ bg="trueGray.100"
919
+ {...flatListProps}
920
+ />
921
+ </ScrollView>}
917
922
  </Column>
918
923
 
919
924
  {listFooterComponent}
@@ -146,11 +146,13 @@ export default function GridRow(props) {
146
146
  if (_.isFunction(value)) {
147
147
  return value(key);
148
148
  }
149
-
149
+ const elementProps = {};
150
+ if (UiGlobals.mode === UI_MODE_WEB) {
151
+ elementProps.textOverflow = 'ellipsis';
152
+ }
150
153
  return <Text
151
154
  key={key}
152
155
  overflow="hidden"
153
- textOverflow="ellipsis"
154
156
  alignSelf="center"
155
157
  style={{
156
158
  userSelect: 'none',
@@ -160,6 +162,7 @@ export default function GridRow(props) {
160
162
  py={styles.GRID_CELL_PY}
161
163
  numberOfLines={1}
162
164
  ellipsizeMode="head"
165
+ {...elementProps}
163
166
  {...propsToPass}
164
167
  >{value}</Text>;
165
168
  });
@@ -26,6 +26,8 @@ export default function NoRecordsFound(props) {
26
26
  style: {
27
27
  fontSize: 16,
28
28
  },
29
+ color: 'trueGray.400',
30
+ mr: 1,
29
31
  }}
30
32
  onPress={onRefresh}
31
33
  variant="ghost"
@@ -10,6 +10,7 @@ import {
10
10
  } from 'native-base';
11
11
  import {
12
12
  ALERT_MODE_OK,
13
+ ALERT_MODE_YES,
13
14
  ALERT_MODE_YES_NO,
14
15
  ALERT_MODE_CUSTOM,
15
16
  } from '../../Constants/Alert.js';
@@ -70,6 +71,7 @@ export default function withAlert(WrappedComponent) {
70
71
  setMessage(message);
71
72
  setIncludeCancel(includeCancel);
72
73
  setYesCallback(() => callback);
74
+ setNoCallback(null);
73
75
  showAlert();
74
76
  },
75
77
  onCancel = () => {
@@ -127,6 +129,15 @@ export default function withAlert(WrappedComponent) {
127
129
  color="#fff"
128
130
  >OK</Button>);
129
131
  break;
132
+ case ALERT_MODE_YES:
133
+ buttons.push(<Button
134
+ key="yesBtn"
135
+ ref={autoFocusRef}
136
+ onPress={onYes}
137
+ color="#fff"
138
+ colorScheme="danger"
139
+ >Yes</Button>);
140
+ break;
130
141
  case ALERT_MODE_YES_NO:
131
142
  buttons.push(<Button
132
143
  key="noBtn"
@@ -1,3 +1,4 @@
1
1
  export const ALERT_MODE_OK = 'ALERT_MODE_OK';
2
+ export const ALERT_MODE_YES = 'ALERT_MODE_YES';
2
3
  export const ALERT_MODE_YES_NO = 'ALERT_MODE_YES_NO';
3
4
  export const ALERT_MODE_CUSTOM = 'ALERT_MODE_CUSTOM';