@onehat/ui 0.2.42 → 0.2.44

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.2.42",
3
+ "version": "0.2.44",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -0,0 +1,58 @@
1
+ import {
2
+ EDITOR_MODE__VIEW,
3
+ EDITOR_MODE__ADD,
4
+ EDITOR_MODE__EDIT,
5
+ } from '../../Constants/Editor.js';
6
+ import _ from 'lodash';
7
+
8
+
9
+ export default function Editor(props) {
10
+ const {
11
+ Form,
12
+ Viewer,
13
+ isEditorViewOnly: isViewOnly,
14
+ onEditorCancel: onCancel,
15
+ onEditorSave: onSave,
16
+ onEditorClose: onClose,
17
+ editorMode,
18
+ setEditorMode,
19
+
20
+ // withData
21
+ Repository,
22
+
23
+ // withSelection
24
+ selection,
25
+
26
+ } = props,
27
+ onEditMode = () => {
28
+ setEditorMode(EDITOR_MODE__EDIT);
29
+ },
30
+ onBack = () => {
31
+ setEditorMode(EDITOR_MODE__VIEW);
32
+ };
33
+
34
+ if (_.isEmpty(selection)) {
35
+ return null;
36
+ }
37
+
38
+ if (Repository.isRemotePhantomMode && selection.length === 1 && editorMode === EDITOR_MODE__VIEW) {
39
+ return <Viewer
40
+ record={selection[0]}
41
+ Repository={Repository}
42
+ onEditMode={isViewOnly ? null : onEditMode}
43
+ {...props}
44
+ />;
45
+ }
46
+
47
+ // NOTE: Ideally, this form should use multiple columns when screen is wide enough,
48
+ // and only show in one column when it's not.
49
+
50
+ return <Form
51
+ record={selection}
52
+ onBack={onBack}
53
+ onCancel={onCancel}
54
+ onSave={onSave}
55
+ onClose={onClose}
56
+ {...props}
57
+ />;
58
+ }
@@ -14,7 +14,7 @@ function InputElement(props) {
14
14
  let {
15
15
  value,
16
16
  setValue,
17
- autoSubmit = false, // automatically setValue after user stops typing for autoSubmitDelay
17
+ autoSubmit = true, // automatically setValue after user stops typing for autoSubmitDelay
18
18
  autoSubmitDelay = AUTO_SUBMIT_DELAY,
19
19
  maxLength,
20
20
  onKeyPress,
@@ -83,7 +83,7 @@ function Form(props) {
83
83
  onSave = onEditorSave,
84
84
  onClose,
85
85
 
86
- // withSelection
86
+ // DataMgt
87
87
  selectorId,
88
88
  selectorSelected,
89
89
 
@@ -82,6 +82,7 @@ export function Grid(props) {
82
82
  hideNavColumn = true,
83
83
  noneFoundText,
84
84
  disableLoadingIndicator = false,
85
+ disableSelectorSelected = false,
85
86
  showRowExpander = false,
86
87
  rowExpanderTpl = '',
87
88
  showHeaders = true,
@@ -129,10 +130,9 @@ export function Grid(props) {
129
130
  isInSelection,
130
131
  noSelectorMeansNoResults = false,
131
132
 
132
- // selectorSelected
133
+ // DataMgt
133
134
  selectorId,
134
135
  selectorSelected,
135
- disableSelectorSelected = false,
136
136
 
137
137
  // withInlineEditor
138
138
  inlineEditorRef,
@@ -751,11 +751,11 @@ export function Grid(props) {
751
751
  return () => {};
752
752
  }
753
753
  if (!disableSelectorSelected) {
754
- let matches = selectorSelected?.[0]?.id;
754
+ let id = selectorSelected?.id;
755
755
  if (_.isEmpty(selectorSelected)) {
756
- matches = noSelectorMeansNoResults ? 'NO_MATCHES' : null;
756
+ id = noSelectorMeansNoResults ? 'NO_MATCHES' : null;
757
757
  }
758
- Repository.filter(selectorId, matches, false); // so it doesn't clear existing filters
758
+ Repository.filter(selectorId, id, false); // so it doesn't clear existing filters
759
759
  }
760
760
 
761
761
  }, [selectorId, selectorSelected]);
@@ -26,7 +26,7 @@ export default function GridRow(props) {
26
26
  } = props,
27
27
  styles = UiGlobals.styles,
28
28
  isPhantom = item.isPhantom,
29
- hash = item.hash || item;
29
+ hash = item?.hash || item;
30
30
 
31
31
  return useMemo(() => {
32
32
  const renderColumns = (item) => {
@@ -27,6 +27,10 @@ export default function withEditor(WrappedComponent) {
27
27
  },
28
28
  record,
29
29
 
30
+ // DataMgt
31
+ selectorId,
32
+ selectorSelected,
33
+
30
34
  // withData
31
35
  Repository,
32
36
 
@@ -47,7 +51,13 @@ export default function withEditor(WrappedComponent) {
47
51
  }
48
52
  const
49
53
  defaultValues = Repository.getSchema().model.defaultValues,
50
- entity = await Repository.add(defaultValues, false, true, true);
54
+ addValues = _.clone(defaultValues);
55
+
56
+ if (selectorId && !_.isEmpty(selectorSelected)) {
57
+ addValues[selectorId] = selectorSelected.id;
58
+ }
59
+
60
+ const entity = await Repository.add(addValues, false, true, true);
51
61
  setSelection([entity]);
52
62
  setIsEditorViewOnly(false);
53
63
  setEditorMode(EDITOR_MODE__ADD);
@@ -118,11 +128,15 @@ export default function withEditor(WrappedComponent) {
118
128
  } else if (selection.length > 1) {
119
129
  // Edit multiple entities
120
130
 
121
-
122
- debugger;
123
-
124
-
125
-
131
+ // Loop through all entities and change fields that are not null
132
+ const propertyNames = Object.getOwnPropertyNames(data);
133
+ _.each(propertyNames, (propertyName) => {
134
+ if (!_.isNil(data[propertyName])) {
135
+ _.each(what, (rec) => {
136
+ rec[propertyName] = data[propertyName]
137
+ });
138
+ }
139
+ });
126
140
  }
127
141
  await Repository.save();
128
142
  setIsEditorShown(false);
@@ -148,7 +162,7 @@ export default function withEditor(WrappedComponent) {
148
162
  // For multiple entities selected, change it to edit multiple mode
149
163
  mode = EDITOR_MODE__EDIT;
150
164
  }
151
- } else if (selection.length === 1 && selection.isPhantom) {
165
+ } else if (selection.length === 1 && selection[0].isPhantom) {
152
166
  if (!disableAdd) {
153
167
  // When a phantom entity is selected, change it to add mode.
154
168
  mode = EDITOR_MODE__ADD;
@@ -33,12 +33,6 @@ export default function withPresetButtons(WrappedComponent) {
33
33
  } = props,
34
34
  {
35
35
  // for local use
36
- selection,
37
- onAdd,
38
- onEdit,
39
- onDelete,
40
- onView,
41
- onDuplicate,
42
36
  useEditor = true,
43
37
  disableAdd = false,
44
38
  disableEdit = false,
@@ -51,6 +45,19 @@ export default function withPresetButtons(WrappedComponent) {
51
45
  // withEditor
52
46
  userCanEdit = true,
53
47
  userCanView = true,
48
+ onAdd,
49
+ onEdit,
50
+ onDelete,
51
+ onView,
52
+ onDuplicate,
53
+
54
+ // withSelection
55
+ selection,
56
+ setSelection,
57
+
58
+ // DataMgt
59
+ selectorId,
60
+ selectorSelected,
54
61
  } = props,
55
62
  [isReady, setIsReady] = useState(false),
56
63
  [localContextMenuItems, setLocalContextMenuItems] = useState([]),
@@ -119,29 +126,59 @@ export default function withPresetButtons(WrappedComponent) {
119
126
  text = 'Edit';
120
127
  handler = onEdit;
121
128
  icon = <Edit />;
129
+ if (selectorId && !selectorSelected) {
130
+ isDisabled = true;
131
+ }
132
+ if (_.isEmpty(selection)) {
133
+ isDisabled = true;
134
+ }
122
135
  break;
123
136
  case 'delete':
124
137
  text = 'Delete';
125
138
  handler = onDelete;
126
139
  icon = <Trash />;
140
+ if (selectorId && !selectorSelected) {
141
+ isDisabled = true;
142
+ }
143
+ if (_.isEmpty(selection) || selection.length > 1) {
144
+ isDisabled = true;
145
+ }
127
146
  break;
128
147
  case 'view':
129
148
  text = 'View';
130
149
  handler = onView;
131
150
  icon = <Eye />;
132
151
  isDisabled = !selection.length || selection.length !== 1;
152
+ if (selectorId && !selectorSelected) {
153
+ isDisabled = true;
154
+ }
155
+ if (_.isEmpty(selection) || selection.length > 1) {
156
+ isDisabled = true;
157
+ }
133
158
  break;
134
159
  case 'copy':
135
160
  text = 'Copy to Clipboard';
136
161
  handler = onCopyToClipboard;
137
162
  icon = <Clipboard />;
138
163
  isDisabled = !selection.length;
164
+ if (selectorId && !selectorSelected) {
165
+ isDisabled = true;
166
+ }
167
+ if (_.isEmpty(selection)) {
168
+ isDisabled = true;
169
+ }
139
170
  break;
140
171
  case 'duplicate':
141
172
  text = 'Duplicate';
142
173
  handler = onDuplicate;
143
174
  icon = <Duplicate />;
144
175
  isDisabled = !selection.length || selection.length !== 1;
176
+ if (selectorId && !selectorSelected) {
177
+ isDisabled = true;
178
+ }
179
+ if (_.isEmpty(selection) || selection.length > 1) {
180
+ isDisabled = true;
181
+ }
145
182
  break;
146
183
  // case 'print':
147
184
  // text = 'Print';
@@ -210,7 +247,7 @@ export default function withPresetButtons(WrappedComponent) {
210
247
  if (!isReady) {
211
248
  setIsReady(true);
212
249
  }
213
- }, [selection, localColumnsConfig]);
250
+ }, [selection, selectorSelected, localColumnsConfig]);
214
251
 
215
252
  if (!isReady) {
216
253
  return null;
@@ -229,6 +266,7 @@ export default function withPresetButtons(WrappedComponent) {
229
266
  if (additionalToolbarButtons) {
230
267
  additionalToolbarButtonsToPass.concat(additionalToolbarButtons);
231
268
  }
269
+
232
270
  return <WrappedComponent
233
271
  {...propsToPass}
234
272
  contextMenuItems={contextMenuItemsToPass}
@@ -44,8 +44,8 @@ export default function DataMgt(props) {
44
44
  [isWestCollapsed, setIsWestCollapsed] = useState(westStartsCollapsed),
45
45
  [isEastCollapsed, setIsEastCollapsed] = useState(eastStartsCollapsed),
46
46
  [isFullscreen, setIsFullscreen] = useState(false),
47
- [westSelected, setWestSelectedRaw] = useState([]),
48
- [centerSelected, setCenterSelected] = useState([]),
47
+ [westSelected, setWestSelectedRaw] = useState(),
48
+ [centerSelected, setCenterSelected] = useState(),
49
49
  setWestSelected = (selected) => {
50
50
  setWestSelectedRaw(selected);
51
51
  setCenterSelected(); // clear selection in center
@@ -156,7 +156,7 @@ export default function DataMgt(props) {
156
156
  autoLoad={!showSelector}
157
157
  uniqueRepository={true}
158
158
  selectorId={showSelector ? westSelector_id : null}
159
- selectorSelected={westSelected}
159
+ selectorSelected={westSelected?.[0]}
160
160
  noSelectorMeansNoResults={centerNoSelectorMeansNoResults}
161
161
  onChangeSelection={setCenterSelected}
162
162
  onEvent={onEvent}
@@ -167,7 +167,7 @@ export default function DataMgt(props) {
167
167
  isFullscreen,
168
168
  showSelector,
169
169
  westSelected,
170
- westSelected?.hash,
170
+ westSelected?.[0]?.hash,
171
171
  centerNoSelectorMeansNoResults,
172
172
  // {...centerProps}
173
173
  ])
@@ -191,7 +191,7 @@ export default function DataMgt(props) {
191
191
  controlledByCenter = typeof associatedPanel.props.controlledByCenter === 'undefined' ? true : associatedPanel.props.controlledByCenter,
192
192
  thisAssociatedPanelProps = {
193
193
  selectorId: controlledByCenter ? centerSelector_id : westSelector_id,
194
- selectorSelected: controlledByCenter ? centerSelected : westSelected,
194
+ selectorSelected: controlledByCenter ? centerSelected?.[0] : westSelected?.[0],
195
195
  ...associatedPanel.props,
196
196
  };
197
197
  return React.cloneElement(associatedPanel, { key: ix, reference: 'associatedPanel' + ix, ...allAssociatedPanelProps, ...thisAssociatedPanelProps, });
@@ -0,0 +1,18 @@
1
+ import useWindowSize from './useWindowSize.js';
2
+
3
+ // This hook takes the submitted window size and adjusts it
4
+ // to fit the actual screen size
5
+
6
+ export default function(width, height, percentage = 0.9) {
7
+
8
+ const windowSize = useWindowSize();
9
+
10
+ if (width > windowSize.width) {
11
+ width = windowSize.width * percentage;
12
+ }
13
+ if (height > windowSize.height) {
14
+ height = windowSize.height * percentage;
15
+ }
16
+
17
+ return [ width, height, ];
18
+ }
@@ -0,0 +1,25 @@
1
+ // from https://designcode.io/react-hooks-usewindowsize-hook
2
+
3
+ import { useLayoutEffect, useState } from 'react';
4
+
5
+ // For web only!
6
+ export default function useWindowSize() {
7
+ const [windowSize, setWindowSize] = useState({ width: 0, height: 0 });
8
+
9
+ const handleSize = () => {
10
+ setWindowSize({
11
+ width: window.innerWidth,
12
+ height: window.innerHeight
13
+ });
14
+ };
15
+
16
+ useLayoutEffect(() => {
17
+ handleSize();
18
+
19
+ window.addEventListener('resize', handleSize);
20
+
21
+ return () => window.removeEventListener('resize', handleSize);
22
+ }, []);
23
+
24
+ return windowSize;
25
+ };