@onehat/ui 0.3.333 → 0.3.335

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.333",
3
+ "version": "0.3.335",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -15,7 +15,7 @@ function Editor(props) {
15
15
  onEditorDelete: onDelete,
16
16
  editorMode,
17
17
  onEditMode,
18
- verifyCanEdit,
18
+ canRecordBeEdited,
19
19
  _viewer = {},
20
20
  _form = {
21
21
  containerProps: {}
@@ -36,7 +36,7 @@ function Editor(props) {
36
36
  const propsToPass = _.omit(props, ['self', 'reference', 'parent']);
37
37
 
38
38
  let canEdit = true;
39
- if (verifyCanEdit && !verifyCanEdit(selection)) {
39
+ if (canRecordBeEdited && !canRecordBeEdited(selection)) {
40
40
  canEdit = false;
41
41
  }
42
42
 
@@ -7,6 +7,9 @@ import {
7
7
  ScrollView,
8
8
  Text,
9
9
  } from 'native-base';
10
+ import {
11
+ VIEW,
12
+ } from '../../Constants/Commands.js';
10
13
  import {
11
14
  EDITOR_TYPE__INLINE,
12
15
  EDITOR_TYPE__WINDOWED,
@@ -106,6 +109,9 @@ function Form(props) {
106
109
 
107
110
  // withData
108
111
  Repository,
112
+
113
+ // withPermissions
114
+ canUser,
109
115
 
110
116
  // withEditor
111
117
  isEditorViewOnly = false,
@@ -922,7 +928,7 @@ function Form(props) {
922
928
  >Back</Button>}
923
929
  <Text fontSize={20} ml={2} color="trueGray.500">Edit Mode</Text>
924
930
  </Row>
925
- {onViewMode && !disableView &&
931
+ {onViewMode && !disableView && (!canUser || canUser(VIEW)) &&
926
932
  <Button
927
933
  {...testProps('toViewBtn')}
928
934
  key="viewBtn"
@@ -14,6 +14,10 @@ import {
14
14
  SELECTION_MODE_SINGLE,
15
15
  SELECTION_MODE_MULTI,
16
16
  } from '../../Constants/Selection.js';
17
+ import {
18
+ EDIT,
19
+ VIEW,
20
+ } from '../../Constants/Commands.js';
17
21
  import {
18
22
  DROP_POSITION_BEFORE,
19
23
  DROP_POSITION_AFTER,
@@ -32,18 +36,19 @@ import withComponent from '../Hoc/withComponent.js';
32
36
  import withData from '../Hoc/withData.js';
33
37
  import { withDropTarget } from '../Hoc/withDnd.js';
34
38
  import withEvents from '../Hoc/withEvents.js';
35
- import withSideEditor from '../Hoc/withSideEditor.js';
36
39
  import withFilters from '../Hoc/withFilters.js';
40
+ import withInlineEditor from '../Hoc/withInlineEditor.js';
41
+ import withPermissions from '../Hoc/withPermissions.js';
37
42
  import withPresetButtons from '../Hoc/withPresetButtons.js';
38
43
  import withMultiSelection from '../Hoc/withMultiSelection.js';
39
44
  import withSelection from '../Hoc/withSelection.js';
45
+ import withSideEditor from '../Hoc/withSideEditor.js';
40
46
  import withWindowedEditor from '../Hoc/withWindowedEditor.js';
41
- import withInlineEditor from '../Hoc/withInlineEditor.js';
42
47
  import getSaved from '../../Functions/getSaved.js';
43
48
  import setSaved from '../../Functions/setSaved.js';
44
49
  import getIconButtonFromConfig from '../../Functions/getIconButtonFromConfig.js';
45
- import nbToRgb from '../../Functions/nbToRgb.js';
46
50
  import testProps from '../../Functions/testProps.js';
51
+ import nbToRgb from '../../Functions/nbToRgb.js';
47
52
  import Loading from '../Messages/Loading.js';
48
53
  import isSerializable from '../../Functions/isSerializable.js';
49
54
  import inArray from '../../Functions/inArray.js';
@@ -58,6 +63,7 @@ import Toolbar from '../Toolbar/Toolbar.js';
58
63
  import NoReorderRows from '../Icons/NoReorderRows.js';
59
64
  import ReorderRows from '../Icons/ReorderRows.js';
60
65
  import ColumnSelectorWindow from './ColumnSelectorWindow.js';
66
+ import Unauthorized from '../Messages/Unauthorized.js';
61
67
  import _ from 'lodash';
62
68
 
63
69
 
@@ -128,7 +134,7 @@ function GridComponent(props) {
128
134
  h,
129
135
  flex,
130
136
  bg = '#fff',
131
- verifyCanEdit,
137
+ canRecordBeEdited,
132
138
  alternateRowBackgrounds = true,
133
139
  alternatingInterval = 2,
134
140
  defaultRowHeight = 48,
@@ -153,6 +159,9 @@ function GridComponent(props) {
153
159
  idIx,
154
160
  displayIx,
155
161
 
162
+ // withPermissions
163
+ canUser,
164
+
156
165
  // withDnd
157
166
  isDropTarget,
158
167
  canDrop,
@@ -371,11 +380,17 @@ function GridComponent(props) {
371
380
 
372
381
  if (UiGlobals.doubleClickingGridRowOpensEditorInViewMode) { // global setting
373
382
  if (onView) {
383
+ if (canUser && !canUser(VIEW)) { // permissions
384
+ return;
385
+ }
374
386
  onView(true);
375
387
  }
376
388
  } else {
377
389
  if (onEdit) {
378
- if (verifyCanEdit && !verifyCanEdit(selection)) {
390
+ if (canUser && !canUser(EDIT)) { // permissions
391
+ return;
392
+ }
393
+ if (canRecordBeEdited && !canRecordBeEdited(selection)) { // record can be edited
379
394
  return;
380
395
  }
381
396
  onEdit();
@@ -467,49 +482,53 @@ function GridComponent(props) {
467
482
  dragSelectionRef.current = selection;
468
483
  const getSelection = () => dragSelectionRef.current;
469
484
 
470
- // assign event handlers
471
- if (canRowsReorder && isReorderMode) {
472
- WhichRow = DragSourceGridRow;
473
- rowReorderProps.isDragSource = true;
474
- rowReorderProps.dragSourceType = 'row';
475
- const dragIx = showHeaders ? index - 1 : index;
476
- rowReorderProps.dragSourceItem = {
477
- id: item.id,
478
- getSelection,
479
- onDrag: (dragState) => {
480
- onRowReorderDrag(dragState, dragIx);
481
- },
482
- };
483
- rowReorderProps.onDragEnd = onRowReorderEnd;
484
- } else {
485
- // Don't allow drag/drop from withDnd while reordering
486
- if (areRowsDragSource) {
485
+ const userHasPermissionToDrag = (!canUser || canUser(EDIT));
486
+ if (userHasPermissionToDrag) {
487
+ // assign event handlers
488
+ if (canRowsReorder && isReorderMode) {
487
489
  WhichRow = DragSourceGridRow;
488
- rowDragProps.isDragSource = true;
489
- rowDragProps.dragSourceType = rowDragSourceType;
490
- if (getRowDragSourceItem) {
491
- rowDragProps.dragSourceItem = getRowDragSourceItem(item, getSelection, rowDragSourceType);
492
- } else {
493
- rowDragProps.dragSourceItem = {
494
- id: item.id,
495
- getSelection,
496
- type: rowDragSourceType,
490
+ rowReorderProps.isDragSource = true;
491
+ rowReorderProps.dragSourceType = 'row';
492
+ const dragIx = showHeaders ? index - 1 : index;
493
+ rowReorderProps.dragSourceItem = {
494
+ id: item.id,
495
+ getSelection,
496
+ onDrag: (dragState) => {
497
+ onRowReorderDrag(dragState, dragIx);
498
+ },
499
+ };
500
+ rowReorderProps.onDragEnd = onRowReorderEnd;
501
+ } else {
502
+ // Don't allow drag/drop from withDnd while reordering
503
+ if (areRowsDragSource) {
504
+ WhichRow = DragSourceGridRow;
505
+ rowDragProps.isDragSource = true;
506
+ rowDragProps.dragSourceType = rowDragSourceType;
507
+ if (getRowDragSourceItem) {
508
+ rowDragProps.dragSourceItem = getRowDragSourceItem(item, getSelection, rowDragSourceType);
509
+ } else {
510
+ rowDragProps.dragSourceItem = {
511
+ id: item.id,
512
+ getSelection,
513
+ type: rowDragSourceType,
514
+ };
515
+ }
516
+ }
517
+ if (areRowsDropTarget) {
518
+ WhichRow = DropTargetGridRow;
519
+ rowDragProps.isDropTarget = true;
520
+ rowDragProps.dropTargetAccept = dropTargetAccept;
521
+ rowDragProps.onDrop = (droppedItem) => {
522
+ // NOTE: item is sometimes getting destroyed, but it still as the id, so you can still use it
523
+ onRowDrop(item, droppedItem); // item is what it was dropped on; droppedItem is the dragSourceItem defined above
497
524
  };
498
525
  }
499
- }
500
- if (areRowsDropTarget) {
501
- WhichRow = DropTargetGridRow;
502
- rowDragProps.isDropTarget = true;
503
- rowDragProps.dropTargetAccept = dropTargetAccept;
504
- rowDragProps.onDrop = (droppedItem) => {
505
- // NOTE: item is sometimes getting destroyed, but it still as the id, so you can still use it
506
- onRowDrop(item, droppedItem); // item is what it was dropped on; droppedItem is the dragSourceItem defined above
507
- };
508
- }
509
- if (areRowsDragSource && areRowsDropTarget) {
510
- WhichRow = DragSourceDropTargetGridRow;
526
+ if (areRowsDragSource && areRowsDropTarget) {
527
+ WhichRow = DragSourceDropTargetGridRow;
528
+ }
511
529
  }
512
530
  }
531
+
513
532
  }
514
533
  return <WhichRow
515
534
  columnsConfig={localColumnsConfig}
@@ -941,6 +960,10 @@ function GridComponent(props) {
941
960
 
942
961
  }, [selectorSelected]);
943
962
 
963
+ if (canUser && !canUser('view')) {
964
+ return <Unauthorized />;
965
+ }
966
+
944
967
  if (self) {
945
968
  self.ref = containerRef;
946
969
  self.gridRef = gridRef;
@@ -1146,16 +1169,18 @@ export const Grid = withComponent(
1146
1169
  withAlert(
1147
1170
  withEvents(
1148
1171
  withData(
1149
- withDropTarget(
1150
- withMultiSelection(
1151
- withSelection(
1152
- withFilters(
1153
- withPresetButtons(
1154
- withContextMenu(
1155
- GridComponent
1156
- )
1157
- ),
1158
- true // isGrid
1172
+ withPermissions(
1173
+ withDropTarget(
1174
+ withMultiSelection(
1175
+ withSelection(
1176
+ withFilters(
1177
+ withPresetButtons(
1178
+ withContextMenu(
1179
+ GridComponent
1180
+ )
1181
+ ),
1182
+ true // isGrid
1183
+ )
1159
1184
  )
1160
1185
  )
1161
1186
  )
@@ -1169,17 +1194,19 @@ export const SideGridEditor = withComponent(
1169
1194
  withAlert(
1170
1195
  withEvents(
1171
1196
  withData(
1172
- withDropTarget(
1173
- withMultiSelection(
1174
- withSelection(
1175
- withSideEditor(
1176
- withFilters(
1177
- withPresetButtons(
1178
- withContextMenu(
1179
- GridComponent
1180
- )
1181
- ),
1182
- true // isGrid
1197
+ withPermissions(
1198
+ withDropTarget(
1199
+ withMultiSelection(
1200
+ withSelection(
1201
+ withSideEditor(
1202
+ withFilters(
1203
+ withPresetButtons(
1204
+ withContextMenu(
1205
+ GridComponent
1206
+ )
1207
+ ),
1208
+ true // isGrid
1209
+ )
1183
1210
  )
1184
1211
  )
1185
1212
  )
@@ -1194,16 +1221,18 @@ export const WindowedGridEditor = withComponent(
1194
1221
  withAlert(
1195
1222
  withEvents(
1196
1223
  withData(
1197
- withDropTarget(
1198
- withMultiSelection(
1199
- withSelection(
1200
- withWindowedEditor(
1201
- withFilters(
1202
- withPresetButtons(
1203
- withContextMenu(
1204
- GridComponent
1205
- ),
1206
- true // isGrid
1224
+ withPermissions(
1225
+ withDropTarget(
1226
+ withMultiSelection(
1227
+ withSelection(
1228
+ withWindowedEditor(
1229
+ withFilters(
1230
+ withPresetButtons(
1231
+ withContextMenu(
1232
+ GridComponent
1233
+ ),
1234
+ true // isGrid
1235
+ )
1207
1236
  )
1208
1237
  )
1209
1238
  )
@@ -1219,17 +1248,19 @@ export const InlineGridEditor = withComponent(
1219
1248
  withAlert(
1220
1249
  withEvents(
1221
1250
  withData(
1222
- withDropTarget(
1223
- withMultiSelection(
1224
- withSelection(
1225
- withInlineEditor(
1226
- withFilters(
1227
- withPresetButtons(
1228
- withContextMenu(
1229
- GridComponent
1230
- )
1231
- ),
1232
- true // isGrid
1251
+ withPermissions(
1252
+ withDropTarget(
1253
+ withMultiSelection(
1254
+ withSelection(
1255
+ withInlineEditor(
1256
+ withFilters(
1257
+ withPresetButtons(
1258
+ withContextMenu(
1259
+ GridComponent
1260
+ )
1261
+ ),
1262
+ true // isGrid
1263
+ )
1233
1264
  )
1234
1265
  )
1235
1266
  )
@@ -1244,17 +1275,19 @@ export const InlineGridEditor = withComponent(
1244
1275
  // withAlert(
1245
1276
  // withEvents(
1246
1277
  // withData(
1247
- // withDropTarget(
1248
- // withMultiSelection(
1249
- // withSelection(
1250
- // withInlineSideEditor(
1251
- // withFilters(
1252
- // withPresetButtons(
1253
- // withContextMenu(
1254
- // GridComponent
1255
- // )
1256
- // ),
1257
- // true // isGrid
1278
+ // withPermissions(
1279
+ // withDropTarget(
1280
+ // withMultiSelection(
1281
+ // withSelection(
1282
+ // withInlineSideEditor(
1283
+ // withFilters(
1284
+ // withPresetButtons(
1285
+ // withContextMenu(
1286
+ // GridComponent
1287
+ // )
1288
+ // ),
1289
+ // true // isGrid
1290
+ // )
1258
1291
  // )
1259
1292
  // )
1260
1293
  // )
@@ -22,7 +22,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
22
22
 
23
23
  let [secondaryEditorMode, secondarySetEditorMode] = useState(EDITOR_MODE__VIEW); // Can change below, so use 'let'
24
24
  const {
25
- secondaryUserCanEdit = true,
25
+ secondaryUserCanEdit = true, // not permissions, but capability
26
26
  secondaryUserCanView = true,
27
27
  secondaryCanEditorViewOnly = false, // whether the editor can *ever* change state out of 'View' mode
28
28
  secondaryDisableAdd = false,
@@ -57,6 +57,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
57
57
  // withSecondaryData
58
58
  SecondaryRepository,
59
59
 
60
+ // withPermissions
61
+ canUser,
62
+ showPermissionsError,
63
+
60
64
  // withSecondarySelection
61
65
  secondarySelection,
62
66
  secondarySetSelection,
@@ -69,6 +73,7 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
69
73
  secondaryListeners = useRef({}),
70
74
  secondaryEditorStateRef = useRef(),
71
75
  secondaryNewEntityDisplayValueRef = useRef(),
76
+ secondaryModel = SecondaryRepository?.schema?.name,
72
77
  [secondaryCurrentRecord, secondarySetCurrentRecord] = useState(null),
73
78
  [secondaryIsAdding, setIsAdding] = useState(false),
74
79
  [secondaryIsSaving, setIsSaving] = useState(false),
@@ -109,6 +114,11 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
109
114
  return secondaryNewEntityDisplayValueRef.current;
110
115
  },
111
116
  secondaryDoAdd = async (e, values) => {
117
+ if (canUser && !canUser(ADD, secondaryModel)) {
118
+ showPermissionsError(ADD, secondaryModel);
119
+ return;
120
+ }
121
+
112
122
  let addValues = values;
113
123
 
114
124
  if (SecondaryRepository?.isLoading) {
@@ -197,6 +207,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
197
207
  secondarySetIsEditorShown(true);
198
208
  },
199
209
  secondaryDoEdit = async () => {
210
+ if (canUser && !canUser(EDIT, secondaryModel)) {
211
+ showPermissionsError(EDIT, secondaryModel);
212
+ return;
213
+ }
200
214
  if (_.isEmpty(secondarySelection) || (_.isArray(secondarySelection) && (secondarySelection.length > 1 || secondarySelection[0]?.isDestroyed))) {
201
215
  return;
202
216
  }
@@ -211,6 +225,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
211
225
  secondarySetIsEditorShown(true);
212
226
  },
213
227
  secondaryDoDelete = async (args) => {
228
+ if (canUser && !canUser(DELETE, secondaryModel)) {
229
+ showPermissionsError(DELETE, secondaryModel);
230
+ return;
231
+ }
214
232
  let cb = null;
215
233
  if (_.isFunction(args)) {
216
234
  cb = args;
@@ -263,6 +281,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
263
281
  deleteRecord(false, cb);
264
282
  },
265
283
  deleteRecord = async (moveSubtreeUp, cb) => {
284
+ if (canUser && !canUser(DELETE, secondaryModel)) {
285
+ showPermissionsError(DELETE, secondaryModel);
286
+ return;
287
+ }
266
288
  if (getListeners().onBeforeDelete) {
267
289
  const listenerResult = await getListeners().onBeforeDelete(secondarySelection);
268
290
  if (listenerResult === false) {
@@ -292,6 +314,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
292
314
  if (!secondaryUserCanView) {
293
315
  return;
294
316
  }
317
+ if (canUser && !canUser(VIEW, secondaryModel)) {
318
+ showPermissionsError(VIEW, secondaryModel);
319
+ return;
320
+ }
295
321
  if (secondarySelection.length !== 1) {
296
322
  return;
297
323
  }
@@ -307,6 +333,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
307
333
  if (!secondaryUserCanEdit || secondaryDisableDuplicate) {
308
334
  return;
309
335
  }
336
+ if (canUser && !canUser(DUPLICATE, secondaryModel)) {
337
+ showPermissionsError(DUPLICATE, secondaryModel);
338
+ return;
339
+ }
310
340
  if (secondarySelection.length !== 1) {
311
341
  return;
312
342
  }
@@ -335,6 +365,12 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
335
365
  secondaryDoEdit();
336
366
  },
337
367
  secondaryDoEditorSave = async (data, e) => {
368
+ let mode = editorMode === EDITOR_MODE__ADD ? ADD : EDIT;
369
+ if (canUser && !canUser(mode, secondaryModel)) {
370
+ showPermissionsError(mode, secondaryModel);
371
+ return;
372
+ }
373
+
338
374
  // NOTE: The Form submits onSave for both adds (when not isAutoSsave) and edits.
339
375
  const isSingle = secondarySelection.length === 1;
340
376
  let useStaged = false;
@@ -391,7 +427,11 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
391
427
  await getListeners().onAfterAdd(secondarySelection);
392
428
  }
393
429
  setIsAdding(false);
394
- secondarySetEditorMode(EDITOR_MODE__EDIT);
430
+ if (!canUser || canUser(EDIT, secondaryModel)) {
431
+ secondarySetEditorMode(EDITOR_MODE__EDIT);
432
+ } else {
433
+ secondarySetEditorMode(EDITOR_MODE__VIEW);
434
+ }
395
435
  } else if (secondaryEditorMode === EDITOR_MODE__EDIT) {
396
436
  if (getListeners().onAfterEdit) {
397
437
  await getListeners().onAfterEdit(secondarySelection);
@@ -436,6 +476,11 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
436
476
  secondarySetIsEditorShown(false);
437
477
  },
438
478
  secondaryDoEditorDelete = async () => {
479
+ if (canUser && !canUser(DELETE, secondaryModel)) {
480
+ showPermissionsError(DELETE, secondaryModel);
481
+ return;
482
+ }
483
+
439
484
  secondaryDoDelete(() => {
440
485
  secondarySetEditorMode(EDITOR_MODE__VIEW);
441
486
  secondarySetIsEditorShown(false);
@@ -470,9 +515,19 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
470
515
  return mode;
471
516
  },
472
517
  secondarySetEditMode = () => {
518
+ if (canUser && !canUser(EDIT, secondaryModel)) {
519
+ showPermissionsError(EDIT, secondaryModel);
520
+ return;
521
+ }
522
+
473
523
  secondarySetEditorMode(EDITOR_MODE__EDIT);
474
524
  },
475
525
  secondarySetViewMode = () => {
526
+ if (canUser && !canUser(VIEW, secondaryModel)) {
527
+ showPermissionsError(VIEW, secondaryModel);
528
+ return;
529
+ }
530
+
476
531
  function doIt() {
477
532
  secondarySetEditorMode(EDITOR_MODE__VIEW);
478
533
  }
@@ -38,7 +38,6 @@ export default function withData(WrappedComponent) {
38
38
  // withComponent
39
39
  self,
40
40
  } = props,
41
- propsToPass = _.omit(props, ['model']), // passing 'model' would mess things up if withData gets called twice (e.g. withData(...withData(...)) ), as we'd be trying to recreate Repository twice
42
41
  localIdIx = idIx || (fields && idField ? fields.indexOf(idField) : null),
43
42
  localDisplayIx = displayIx || (fields && displayField ? fields?.indexOf(displayField) : null),
44
43
  [LocalRepository, setLocalRepository] = useState(Repository || null), // simply pass on Repository if it's already supplied
@@ -106,11 +105,9 @@ export default function withData(WrappedComponent) {
106
105
  }
107
106
 
108
107
  return <WrappedComponent
109
- {...propsToPass}
108
+ {...props}
110
109
  disableWithData={false}
111
110
  Repository={LocalRepository}
112
- model={model}
113
- data={data}
114
111
  fields={fields}
115
112
  idField={idField}
116
113
  displayField={displayField}