@onehat/ui 0.3.95 → 0.3.97

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.95",
3
+ "version": "0.3.97",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -332,10 +332,8 @@ export function ComboComponent(props) {
332
332
  const filterName = getFilterName();
333
333
  if (Repository.hasFilter(filterName)) {
334
334
  Repository.clearFilters(filterName);
335
- if (Repository.isRemote) {
336
- if (!this.isAutoLoad) {
337
- await Repository.reload();
338
- }
335
+ if (Repository.isRemote && !Repository.isAutoLoad) {
336
+ await Repository.reload();
339
337
  }
340
338
  }
341
339
  } else {
@@ -371,7 +369,7 @@ export function ComboComponent(props) {
371
369
  // remote
372
370
  const filterValue = _.isEmpty(value) ? null : value + '%';
373
371
  await Repository.filter(filterName, filterValue);
374
- if (!this.isAutoLoad) {
372
+ if (!Repository.isAutoLoad) {
375
373
  await Repository.reload();
376
374
  }
377
375
  } else {
@@ -554,6 +554,7 @@ function Form(props) {
554
554
  let {
555
555
  type,
556
556
  title = null,
557
+ description = null,
557
558
  selectorId,
558
559
  ...propsToPass
559
560
  } = item;
@@ -579,7 +580,13 @@ function Form(props) {
579
580
  fontWeight="bold"
580
581
  >{title}</Text>;
581
582
  }
582
- components.push(<Column key={'ancillary-' + ix} mx={2} my={5}>{title}{element}</Column>);
583
+ if (description) {
584
+ description = <Text
585
+ fontSize={styles.FORM_ANCILLARY_DESCRIPTION_FONTSIZE}
586
+ fontStyle="italic"
587
+ >{description}</Text>;
588
+ }
589
+ components.push(<Column key={'ancillary-' + ix} mx={2} my={5}>{title}{description}{element}</Column>);
583
590
  });
584
591
  }
585
592
  return components;
@@ -45,6 +45,7 @@ import withInlineEditor from '../Hoc/withInlineEditor.js';
45
45
  import getIconButtonFromConfig from '../../Functions/getIconButtonFromConfig.js';
46
46
  import testProps from '../../Functions/testProps.js';
47
47
  import nbToRgb from '../../Functions/nbToRgb.js';
48
+ import Loading from '../Messages/Loading.js';
48
49
  import GridHeaderRow from './GridHeaderRow.js';
49
50
  import GridRow, { ReorderableGridRow } from './GridRow.js';
50
51
  import IconButton from '../Buttons/IconButton.js';
@@ -56,15 +57,33 @@ import ReorderRows from '../Icons/ReorderRows.js';
56
57
  import _ from 'lodash';
57
58
 
58
59
 
59
- // Grid requires the use of HOC withSelection() whenever it's used.
60
- // The default export is *with* the HOC. A separate *raw* component is
61
- // exported which can be combined with many HOCs for various functionality.
60
+ // This fn gets called many times per component
61
+ // First call
62
+ // !isInited
63
+ // render a placeholder, to get container dimensions
64
+ // onInitialLayout()
65
+ // set initial pageSize
66
+ // setIsInited(true)
67
+ // Second call
68
+ // !isReady
69
+ // set selectorSelected
70
+ // load Repo
71
+ // Third call
72
+ // isReady
73
+ // render Grid,
74
+ // subsequent calls due to changes of selectorSelected
75
+ // re-apply selectorSelected
76
+ // subsequent calls due to changes changes in onLayout
77
+ // adjust pageSize if needed
78
+
79
+ // TODO: account for various environments (mainly for optimization):
80
+ // RN vs web
81
+ // Repository vs data
62
82
 
63
83
  function GridComponent(props) {
64
84
  const {
65
85
 
66
86
  columnsConfig = [], // json configurations for each column
67
-
68
87
  columnProps = {},
69
88
  getRowProps = (item) => {
70
89
  return {
@@ -153,7 +172,7 @@ function GridComponent(props) {
153
172
  gridRef = useRef(),
154
173
  gridContainerRef = useRef(),
155
174
  isAddingRef = useRef(),
156
- isRenderedRef = useRef(),
175
+ [isInited, setIsInited] = useState(false),
157
176
  [isReady, setIsReady] = useState(false),
158
177
  [isLoading, setIsLoading] = useState(false),
159
178
  [localColumnsConfig, setLocalColumnsConfigRaw] = useState([]),
@@ -639,56 +658,68 @@ function GridComponent(props) {
639
658
  }
640
659
  setDragRowSlot(null);
641
660
  },
661
+ calculatePageSize = (containerHeight) => {
662
+ const
663
+ headerHeight = showHeaders ? 50 : 0,
664
+ footerHeight = !disablePagination ? 50 : 0,
665
+ height = containerHeight - headerHeight - footerHeight,
666
+ rowHeight = 48,
667
+ rowsPerContainer = Math.floor(height / rowHeight);
668
+ let pageSize = rowsPerContainer;
669
+ if (showHeaders) {
670
+ pageSize--;
671
+ }
672
+ return pageSize;
673
+ },
642
674
  adjustPageSizeToHeight = (e) => {
643
- if (CURRENT_MODE === UI_MODE_REACT_NATIVE) {
675
+ if (CURRENT_MODE !== UI_MODE_WEB) { // TODO: Remove this conditional, and don't even do the double render for RN
644
676
  return;
645
677
  }
646
- let doLoad = false;
647
- if (!isRenderedRef.current) {
648
- isRenderedRef.current = true;
649
- if (loadOnRender && Repository && !Repository.isLoaded && !Repository.isLoading && !Repository.isAutoLoad) {
650
- doLoad = true; // first time in onLayout only!
651
- }
678
+ if (!Repository) {
679
+ return;
652
680
  }
653
681
 
654
- let adjustPageSizeToHeight = autoAdjustPageSizeToHeight;
655
- if (!Repository || (!_.isNil(UiGlobals.autoAdjustPageSizeToHeight) && !UiGlobals.autoAdjustPageSizeToHeight)) {
656
- adjustPageSizeToHeight = false;
682
+ let doAdjustment = autoAdjustPageSizeToHeight;
683
+ if (!_.isNil(UiGlobals.autoAdjustPageSizeToHeight) && !UiGlobals.autoAdjustPageSizeToHeight) {
684
+ // allow global override to prevent this auto adjustment
685
+ doAdjustment = false;
657
686
  }
658
- if (adjustPageSizeToHeight) {
659
- const
660
- containerHeight = e.nativeEvent.layout.height,
661
- headerHeight = showHeaders ? 50 : 0,
662
- footerHeight = !disablePagination ? 50 : 0,
663
- height = containerHeight - headerHeight - footerHeight,
664
- rowHeight = 48,
665
- rowsPerContainer = Math.floor(height / rowHeight);
666
- let pageSize = rowsPerContainer;
667
- if (showHeaders) {
668
- pageSize--;
669
- }
670
- if (pageSize !== Repository.pageSize) {
671
- Repository.setPageSize(pageSize);
687
+ if (doAdjustment) {
688
+ const containerHeight = e.nativeEvent.layout.height;
689
+ if (containerHeight > 0) {
690
+ const pageSize = calculatePageSize(containerHeight);
691
+ if (pageSize !== Repository.pageSize) {
692
+ Repository.setPageSize(pageSize);
693
+ }
672
694
  }
673
695
  }
674
- if (doLoad) {
675
- Repository.load();
676
- }
677
696
  },
678
697
  debouncedAdjustPageSizeToHeight = useCallback(_.debounce(adjustPageSizeToHeight, 200), []),
679
- onLayout = (e) => {
680
- if (!isRenderedRef.current) {
681
- // first time, call this immediately
682
- adjustPageSizeToHeight(e);
683
- } else {
684
- // debounce all subsequent calls
685
- debouncedAdjustPageSizeToHeight(e);
698
+ applySelectorSelected = () => {
699
+ if (disableSelectorSelected || !selectorId) {
700
+ return
701
+ }
702
+ let id = selectorSelected?.id;
703
+ if (_.isEmpty(selectorSelected)) {
704
+ id = noSelectorMeansNoResults ? 'NO_MATCHES' : null;
686
705
  }
706
+ Repository.filter(selectorId, id, false); // so it doesn't clear existing filters
687
707
  };
688
708
 
689
709
  useEffect(() => {
710
+ if (!isInited) {
711
+ // first call -- meant to render placeholder so we get container dimensions
712
+ if (Repository) {
713
+ if (Repository.isRemote) {
714
+ Repository.isAutoLoad = false;
715
+ }
716
+ Repository.pauseEvents();
717
+ }
718
+ return () => {};
719
+ }
690
720
 
691
- const calculateLocalColumnsConfig = () => {
721
+ // second call -- do other necessary setup
722
+ function calculateLocalColumnsConfig() {
692
723
  // convert json config into actual elements
693
724
  const localColumnsConfig = [];
694
725
  if (_.isEmpty(columnsConfig)) {
@@ -756,12 +787,11 @@ function GridComponent(props) {
756
787
  });
757
788
  }
758
789
  return localColumnsConfig;
759
- };
760
-
761
- if (!isReady) {
762
- setLocalColumnsConfig(calculateLocalColumnsConfig());
763
- setIsReady(true);
764
790
  }
791
+ setLocalColumnsConfig(calculateLocalColumnsConfig());
792
+
793
+ setIsReady(true);
794
+
765
795
  if (!Repository) {
766
796
  return () => {};
767
797
  }
@@ -791,6 +821,13 @@ function GridComponent(props) {
791
821
  Repository.on('changeSorters', onChangeSorters);
792
822
 
793
823
 
824
+ applySelectorSelected();
825
+ Repository.resumeEvents();
826
+
827
+ if (Repository.isRemote) {
828
+ Repository.load();
829
+ }
830
+
794
831
  return () => {
795
832
  Repository.off('beforeLoad', setTrue);
796
833
  Repository.off('load', setFalse);
@@ -801,21 +838,16 @@ function GridComponent(props) {
801
838
  Repository.off('changeFilters', onChangeFilters);
802
839
  Repository.off('changeSorters', onChangeSorters);
803
840
  };
804
- }, []);
841
+ }, [isInited]);
805
842
 
806
843
  useEffect(() => {
807
- if (!Repository) {
844
+ if (!Repository || !isReady) {
808
845
  return () => {};
809
846
  }
810
- if (!disableSelectorSelected && selectorId) {
811
- let id = selectorSelected?.id;
812
- if (_.isEmpty(selectorSelected)) {
813
- id = noSelectorMeansNoResults ? 'NO_MATCHES' : null;
814
- }
815
- Repository.filter(selectorId, id, false); // so it doesn't clear existing filters
816
- }
817
847
 
818
- }, [selectorId, selectorSelected]);
848
+ applySelectorSelected();
849
+
850
+ }, [selectorSelected]);
819
851
 
820
852
  if (self) {
821
853
  self.ref = containerRef;
@@ -825,7 +857,19 @@ function GridComponent(props) {
825
857
 
826
858
  const footerToolbarItemComponents = useMemo(() => getFooterToolbarItems(), [additionalToolbarButtons, isDragMode]);
827
859
 
860
+ if (!isInited) {
861
+ // first time through, render a placeholder so we can get container dimensions
862
+ return <Column
863
+ flex={1}
864
+ w="100%"
865
+ onLayout={(e) => {
866
+ adjustPageSizeToHeight(e);
867
+ setIsInited(true);
868
+ }}
869
+ />;
870
+ }
828
871
  if (!isReady) {
872
+ // second time through, render nothing, as we are still setting up the Repository
829
873
  return null;
830
874
  }
831
875
 
@@ -905,6 +949,14 @@ function GridComponent(props) {
905
949
  grid = <ScrollView flex={1} w="100%">{grid}</ScrollView>
906
950
  }
907
951
 
952
+ // placeholders in case no entities yet
953
+ if (!entities?.length) {
954
+ if (Repository?.isLoading) {
955
+ grid = <Loading isScreen={true} />;
956
+ } else {
957
+ grid = <NoRecordsFound text={noneFoundText} onRefresh={onRefresh} />;
958
+ }
959
+ }
908
960
 
909
961
  return <Column
910
962
  {...testProps('Grid')}
@@ -913,7 +965,7 @@ function GridComponent(props) {
913
965
  bg={bg}
914
966
  borderWidth={styles.GRID_BORDER_WIDTH}
915
967
  borderColor={styles.GRID_BORDER_COLOR}
916
- onLayout={onLayout}
968
+ onLayout={(e) => debouncedAdjustPageSizeToHeight(e)}
917
969
  {...sizeProps}
918
970
  >
919
971
  {topToolbar}
@@ -923,7 +975,7 @@ function GridComponent(props) {
923
975
  deselectAll();
924
976
  }
925
977
  }}>
926
- {!entities?.length ? <NoRecordsFound text={noneFoundText} onRefresh={onRefresh} /> : grid}
978
+ {grid}
927
979
  </Column>
928
980
 
929
981
  {listFooterComponent}
@@ -396,7 +396,7 @@ export default function GridHeaderRow(props) {
396
396
  {...textProps}
397
397
  >{header}</Text>
398
398
 
399
- {isSorter && <Icon key="Icon" as={isSortDirectionAsc ? SortDown : SortUp} textAlign="center" size="sm" mt={3} mr={2} color="trueGray.500" />}
399
+ {isSorter && <Icon key="Icon" as={isSortDirectionAsc ? SortUp : SortDown} textAlign="center" size="sm" mt={3} mr={2} color="trueGray.500" />}
400
400
 
401
401
  {isResizable && showDragHandles &&
402
402
  <HeaderResizeHandle
@@ -5,6 +5,7 @@ import {
5
5
  Text,
6
6
  } from 'native-base';
7
7
  import withComponent from '../Hoc/withComponent.js';
8
+ import UiGlobals from '../../UiGlobals.js';
8
9
  import IconButton from '../Buttons/IconButton';
9
10
  import FullWidth from '../Icons/FullWidth';
10
11
  import SideBySide from '../Icons/SideBySide';
@@ -23,6 +24,7 @@ function ManagerScreen(props) {
23
24
  fullModeComponent,
24
25
  id,
25
26
  } = props,
27
+ styles = UiGlobals.styles,
26
28
  [isReady, setIsReady] = useState(false),
27
29
  [mode, setModeRaw] = useState(MODE_FULL),
28
30
  setMode = (newMode) => {
@@ -61,6 +63,13 @@ function ManagerScreen(props) {
61
63
  whichComponent = sideModeComponent;
62
64
  }
63
65
 
66
+ const textProps = {};
67
+ if (styles.MANAGER_SCREEN_TITLE) {
68
+ textProps.style = {
69
+ fontFamily: styles.MANAGER_SCREEN_TITLE,
70
+ };
71
+ }
72
+
64
73
  return <Column maxHeight="100vh" overflow="hidden" flex={1} w="100%">
65
74
  <Row
66
75
  h="80px"
@@ -68,7 +77,7 @@ function ManagerScreen(props) {
68
77
  borderBottomWidth={2}
69
78
  borderBottomColor="#ccc"
70
79
  >
71
- <Text p={4} fontSize="26" fontWeight={700}>{title}</Text>
80
+ <Text p={4} fontSize="26" fontWeight={700} {...textProps}>{title}</Text>
72
81
  <IconButton
73
82
  icon={FullWidth}
74
83
  _icon={{
@@ -9,6 +9,7 @@ const defaults = {
9
9
  ATTACHMENTS_MAX_FILESIZE: 1024 * 1024 * 5, // 5MB
10
10
  FILTER_LABEL_FONTSIZE: DEFAULT_FONTSIZE,
11
11
  FORM_ANCILLARY_TITLE_FONTSIZE: 22,
12
+ FORM_ANCILLARY_DESCRIPTION_FONTSIZE: 16,
12
13
  FORM_COLOR_READOUT_FONTSIZE: DEFAULT_FONTSIZE,
13
14
  FORM_COLOR_INPUT_BG: WHITE,
14
15
  FORM_COLOR_INPUT_FOCUS_BG: FOCUS,