@onehat/ui 0.4.45 → 0.4.49

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.
Files changed (89) hide show
  1. package/package.json +1 -1
  2. package/src/Components/Form/Field/Date.js +13 -0
  3. package/src/Components/Grid/Grid.js +11 -11
  4. package/src/Components/Hoc/Secondary/withSecondaryEditor.js +13 -8
  5. package/src/Components/Hoc/withAlert.js +1 -0
  6. package/src/Components/Hoc/withEditor.js +12 -8
  7. package/src/Components/Hoc/withPdfButtons.js +3 -0
  8. package/src/PlatformImports/Web/Attachments.js +143 -10
  9. /package/src/{Constants → constants}/Alert.js +0 -0
  10. /package/src/{Constants → constants}/AppStates.js +0 -0
  11. /package/src/{Constants → constants}/Colors.js +0 -0
  12. /package/src/{Constants → constants}/Commands.js +0 -0
  13. /package/src/{Constants → constants}/Date.js +0 -0
  14. /package/src/{Constants → constants}/Dates.js +0 -0
  15. /package/src/{Constants → constants}/Directions.js +0 -0
  16. /package/src/{Constants → constants}/Editor.js +0 -0
  17. /package/src/{Constants → constants}/EditorModes.js +0 -0
  18. /package/src/{Constants → constants}/File.js +0 -0
  19. /package/src/{Constants → constants}/Filters.js +0 -0
  20. /package/src/{Constants → constants}/Grid.js +0 -0
  21. /package/src/{Constants → constants}/Input.js +0 -0
  22. /package/src/{Constants → constants}/MeterTypes.js +0 -0
  23. /package/src/{Constants → constants}/MimeTypes.js +0 -0
  24. /package/src/{Constants → constants}/ReportTypes.js +0 -0
  25. /package/src/{Constants → constants}/ScreenModes.js +0 -0
  26. /package/src/{Constants → constants}/Selection.js +0 -0
  27. /package/src/{Constants → constants}/Styles.js +0 -0
  28. /package/src/{Constants → constants}/Tasks.js +0 -0
  29. /package/src/{Constants → constants}/Tree.js +0 -0
  30. /package/src/{Constants → constants}/UiModes.js +0 -0
  31. /package/src/{Functions → functions}/BankersRound.js +0 -0
  32. /package/src/{Functions → functions}/Cypress/button_functions.js +0 -0
  33. /package/src/{Functions → functions}/Cypress/common_functions.js +0 -0
  34. /package/src/{Functions → functions}/Cypress/crud_functions.js +0 -0
  35. /package/src/{Functions → functions}/Cypress/dom_functions.js +0 -0
  36. /package/src/{Functions → functions}/Cypress/form_functions.js +0 -0
  37. /package/src/{Functions → functions}/Cypress/grid_functions.js +0 -0
  38. /package/src/{Functions → functions}/Cypress/navigation_functions.js +0 -0
  39. /package/src/{Functions → functions}/Cypress/tree_functions.js +0 -0
  40. /package/src/{Functions → functions}/Cypress/utilities.js +0 -0
  41. /package/src/{Functions → functions}/PlatformDetector.js +0 -0
  42. /package/src/{Functions → functions}/Timer.js +0 -0
  43. /package/src/{Functions → functions}/buildAdditionalButtons.js +0 -0
  44. /package/src/{Functions → functions}/chunkArray.js +0 -0
  45. /package/src/{Functions → functions}/colorConversions.js +0 -0
  46. /package/src/{Functions → functions}/delay.js +0 -0
  47. /package/src/{Functions → functions}/delayUntil.js +0 -0
  48. /package/src/{Functions → functions}/deleteSaved.js +0 -0
  49. /package/src/{Functions → functions}/deleteSecure.js +0 -0
  50. /package/src/{Functions → functions}/downloadInBackground.js +0 -0
  51. /package/src/{Functions → functions}/downloadWithFetch.js +0 -0
  52. /package/src/{Functions → functions}/emptyFn.js +0 -0
  53. /package/src/{Functions → functions}/getComponentFromType.js +0 -0
  54. /package/src/{Functions → functions}/getIconButtonFromConfig.js +0 -0
  55. /package/src/{Functions → functions}/getIsMobile.js +0 -0
  56. /package/src/{Functions → functions}/getNodeIcon.js +0 -0
  57. /package/src/{Functions → functions}/getPref.js +0 -0
  58. /package/src/{Functions → functions}/getReport.js +0 -0
  59. /package/src/{Functions → functions}/getSaved.js +0 -0
  60. /package/src/{Functions → functions}/getSecure.js +0 -0
  61. /package/src/{Functions → functions}/getTokenHeaders.js +0 -0
  62. /package/src/{Functions → functions}/gsToHex.js +0 -0
  63. /package/src/{Functions → functions}/ignoreWanings.js +0 -0
  64. /package/src/{Functions → functions}/inArray.js +0 -0
  65. /package/src/{Functions → functions}/isJson.js +0 -0
  66. /package/src/{Functions → functions}/isSerializable.js +0 -0
  67. /package/src/{Functions → functions}/isVideo.js +0 -0
  68. /package/src/{Functions → functions}/jsonValidator.js +0 -0
  69. /package/src/{Functions → functions}/nbToRgb.js +0 -0
  70. /package/src/{Functions → functions}/objectToClassName.js +0 -0
  71. /package/src/{Functions → functions}/parseNotification.js +0 -0
  72. /package/src/{Functions → functions}/processImage.js +0 -0
  73. /package/src/{Functions → functions}/registerComponents.js +0 -0
  74. /package/src/{Functions → functions}/registerReactNativeComponents.js +0 -0
  75. /package/src/{Functions → functions}/registerStyles.js +0 -0
  76. /package/src/{Functions → functions}/registerWebComponents.js +0 -0
  77. /package/src/{Functions → functions}/setCustomInflector.js +0 -0
  78. /package/src/{Functions → functions}/setPref.js +0 -0
  79. /package/src/{Functions → functions}/setProgress.js +0 -0
  80. /package/src/{Functions → functions}/setSaved.js +0 -0
  81. /package/src/{Functions → functions}/setSecure.js +0 -0
  82. /package/src/{Functions → functions}/setThemeOverrides.js +0 -0
  83. /package/src/{Functions → functions}/setUiSavesRepo.js +0 -0
  84. /package/src/{Functions → functions}/sleep.js +0 -0
  85. /package/src/{Functions → functions}/tailwindFunctions.js +0 -0
  86. /package/src/{Functions → functions}/testProps.js +0 -0
  87. /package/src/{Functions → functions}/trackEngagementHit.js +0 -0
  88. /package/src/{Functions → functions}/verifyCompleted.js +0 -0
  89. /package/src/{Functions → functions}/waitFor.js +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/ui",
3
- "version": "0.4.45",
3
+ "version": "0.4.49",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -17,6 +17,7 @@ import {
17
17
  UI_MODE_NATIVE,
18
18
  UI_MODE_WEB,
19
19
  } from '../../../Constants/UiModes.js';
20
+ import Button from '../../Buttons/Button.js';
20
21
  import UiGlobals from '../../../UiGlobals.js';
21
22
  import Formatters from '@onehat/data/src/Util/Formatters.js';
22
23
  import Parsers from '@onehat/data/src/Util/Parsers.js';
@@ -219,6 +220,9 @@ export const DateElement = forwardRef((props, ref) => {
219
220
  setBothValues(value);
220
221
  setTextInputValue(value);
221
222
  }
223
+ },
224
+ onToday = () => {
225
+ onPickerChange(moment());
222
226
  };
223
227
 
224
228
  useEffect(() => {
@@ -460,6 +464,15 @@ export const DateElement = forwardRef((props, ref) => {
460
464
  timeFormat={mode === TIME || mode === DATETIME ? 'HH:mm:ss' : false}
461
465
  onChange={onPickerChange}
462
466
  />
467
+ <Button
468
+ {...testProps('todayBtn')}
469
+ key="todayBtn"
470
+ onPress={onToday}
471
+ className={`
472
+ mt-2
473
+ `}
474
+ text="Today"
475
+ />
463
476
  </PopoverBody>
464
477
  </PopoverContent>
465
478
  </Popover>;
@@ -422,18 +422,18 @@ function GridComponent(props) {
422
422
  onView(!props.isEditorViewOnly);
423
423
  }
424
424
  } else {
425
- if (onEdit) {
426
- if (canUser && !canUser(EDIT)) { // permissions
427
- return;
428
- }
429
- if (canRecordBeEdited && !canRecordBeEdited(selection)) { // record can be edited
430
- return;
431
- }
425
+ let canDoEdit = false,
426
+ canDoView = false;
427
+ if (onEdit && canUser && canUser(EDIT) && canRecordBeEdited && canRecordBeEdited(selection)) {
428
+ canDoEdit = true;
429
+ }
430
+ if (onView && canUser && canUser(VIEW)) {
431
+ canDoView = true;
432
+ }
433
+
434
+ if (canDoEdit) {
432
435
  onEdit();
433
- } else if (onView) {
434
- if (canUser && !canUser(VIEW)) { // permissions
435
- return;
436
- }
436
+ } else if (canDoView) {
437
437
  onView();
438
438
  }
439
439
  }
@@ -465,13 +465,15 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
465
465
  }
466
466
 
467
467
  setIsSaving(true);
468
- let success;
469
- try {
470
- await SecondaryRepository.save(null, useStaged);
471
- success = true;
472
- } catch (e) {
473
- success = e;
474
- }
468
+ let success = true;
469
+ const tempListener = (msg, data) => {
470
+ success = { msg, data };
471
+ };
472
+
473
+ SecondaryRepository.on('error', tempListener); // add a temporary listener for the error event
474
+ await SecondaryRepository.save(null, useStaged);
475
+ SecondaryRepository.off('error', tempListener); // remove the temporary listener
476
+
475
477
  setIsSaving(false);
476
478
 
477
479
  if (_.isBoolean(success) && success) {
@@ -499,7 +501,10 @@ export default function withSecondaryEditor(WrappedComponent, isTree = false) {
499
501
  secondaryOnSave(secondarySelection);
500
502
  }
501
503
  }
502
- // setIsEditorShown(false);
504
+ if (secondaryEditorType === EDITOR_TYPE__INLINE) {
505
+ secondarySetIsEditorShown(false);
506
+ }
507
+
503
508
  }
504
509
 
505
510
  return success;
@@ -62,6 +62,7 @@ function withAlert(WrappedComponent) {
62
62
  text-${color}
63
63
  text-[18px]
64
64
  flex-none
65
+ mr-2
65
66
  `}>{message}</Text>
66
67
  </Box>
67
68
  </HStack>;
@@ -500,13 +500,15 @@ export default function withEditor(WrappedComponent, isTree = false) {
500
500
  }
501
501
 
502
502
  setIsSaving(true);
503
- let success;
504
- try {
505
- await Repository.save(null, useStaged);
506
- success = true;
507
- } catch (e) {
508
- success = e;
509
- }
503
+ let success = true;
504
+ const tempListener = (msg, data) => {
505
+ success = { msg, data };
506
+ };
507
+
508
+ Repository.on('error', tempListener); // add a temporary listener for the error event
509
+ await Repository.save(null, useStaged);
510
+ Repository.off('error', tempListener); // remove the temporary listener
511
+
510
512
  setIsSaving(false);
511
513
 
512
514
  if (_.isBoolean(success) && success) {
@@ -534,7 +536,9 @@ export default function withEditor(WrappedComponent, isTree = false) {
534
536
  onSave(selection);
535
537
  }
536
538
  }
537
- // setIsEditorShown(false);
539
+ if (editorType === EDITOR_TYPE__INLINE) {
540
+ setIsEditorShown(false);
541
+ }
538
542
  }
539
543
 
540
544
  return success;
@@ -92,6 +92,9 @@ export default function withPdfButtons(WrappedComponent) {
92
92
  }),
93
93
  items = [];
94
94
  _.each(ancillaryItemsClone, (ancillaryItem) => { // clone, as we don't want to alter the item by reference
95
+ if (ancillaryItem.skipInPdfButtons) {
96
+ return;
97
+ }
95
98
  let name;
96
99
  if (ancillaryItem.pdfModel) {
97
100
  name = ancillaryItem.pdfModel;
@@ -21,12 +21,17 @@ import {
21
21
  import { Avatar, Dropzone, FileMosaic, FileCard, FileInputButton, } from "@files-ui/react";
22
22
  import inArray from '../../Functions/inArray.js';
23
23
  import IconButton from '../../Components/Buttons/IconButton.js';
24
- import Xmark from '../../Components/Icons/Xmark.js'
24
+ import Xmark from '../../Components/Icons/Xmark.js';
25
+ import Eye from '../../Components/Icons/Eye.js';
26
+ import ChevronLeft from '../../Components/Icons/ChevronLeft.js';
27
+ import ChevronRight from '../../Components/Icons/ChevronRight.js';
25
28
  import withAlert from '../../Components/Hoc/withAlert.js';
26
29
  import withComponent from '../../Components/Hoc/withComponent.js';
27
30
  import withData from '../../Components/Hoc/withData.js';
31
+ import CenterBox from '../../Components/Layout/CenterBox.js';
28
32
  import downloadInBackground from '../../Functions/downloadInBackground.js';
29
33
  import downloadWithFetch from '../../Functions/downloadWithFetch.js';
34
+ import useForceUpdate from '../../Hooks/useForceUpdate.js';
30
35
  import _ from 'lodash';
31
36
 
32
37
  const
@@ -35,16 +40,18 @@ const
35
40
  isPwa = !!window?.navigator?.standalone;
36
41
 
37
42
  function FileCardCustom(props) {
38
- const
39
- {
43
+ const {
40
44
  id,
41
45
  name: filename,
42
46
  type: mimetype,
43
47
  onDelete,
48
+ onSee,
44
49
  downloadUrl,
45
50
  uploadStatus,
46
51
  } = props,
47
- isDownloading = uploadStatus && inArray(uploadStatus, ['preparing', 'uploading', 'success']);
52
+ isDownloading = uploadStatus && inArray(uploadStatus, ['preparing', 'uploading', 'success']),
53
+ isPdf = mimetype === 'application/pdf';
54
+
48
55
  return <Pressable
49
56
  onPress={() => {
50
57
  downloadInBackground(downloadUrl);
@@ -52,6 +59,7 @@ function FileCardCustom(props) {
52
59
  className="px-3 py-1 items-center flex-row rounded-[5px] border border-primary.700"
53
60
  >
54
61
  {isDownloading && <Spinner className="mr-2" />}
62
+ {onSee && isPdf && <IconButton mr={1} icon={Eye} onPress={() => onSee(null, id)} />}
55
63
  <Text>{filename}</Text>
56
64
  {onDelete && <IconButton ml={1} icon={Xmark} onPress={() => onDelete(id)} />}
57
65
  </Pressable>;
@@ -81,7 +89,7 @@ function AttachmentsElement(props) {
81
89
  expandedMax = EXPANDED_MAX,
82
90
  collapsedMax = COLLAPSED_MAX,
83
91
  autoUpload = true,
84
- onBeforeDropzoneChange,
92
+ onAfterDropzoneChange, // fn, should return true if it mutated the files array
85
93
 
86
94
  // withComponent
87
95
  self,
@@ -94,6 +102,8 @@ function AttachmentsElement(props) {
94
102
  Repository,
95
103
 
96
104
  // withAlert
105
+ showModal,
106
+ updateModalBody,
97
107
  alert,
98
108
  confirm,
99
109
 
@@ -102,6 +112,7 @@ function AttachmentsElement(props) {
102
112
  model = _.isArray(selectorSelected) && selectorSelected[0] ? selectorSelected[0].repository?.name : selectorSelected?.repository?.name,
103
113
  modelidCalc = _.isArray(selectorSelected) ? _.map(selectorSelected, (entity) => entity[selectorSelectedField]) : selectorSelected?.[selectorSelectedField],
104
114
  modelid = useRef(modelidCalc),
115
+ forceUpdate = useForceUpdate(),
105
116
  [isReady, setIsReady] = useState(false),
106
117
  [isUploading, setIsUploading] = useState(false),
107
118
  [showAll, setShowAll] = useState(false),
@@ -135,7 +146,7 @@ function AttachmentsElement(props) {
135
146
  toggleShowAll = () => {
136
147
  setShowAll(!showAll);
137
148
  },
138
- onDropzoneChange = (files) => {
149
+ onDropzoneChange = async (files) => {
139
150
  if (!files.length) {
140
151
  alert('No files accepted. Perhaps they were too large or the wrong file type?');
141
152
  return;
@@ -148,8 +159,11 @@ function AttachmentsElement(props) {
148
159
  ...extraUploadData,
149
160
  };
150
161
  });
151
- if (onBeforeDropzoneChange) {
152
- onBeforeDropzoneChange(files);
162
+ if (onAfterDropzoneChange) {
163
+ const isChanged = await onAfterDropzoneChange(files);
164
+ if (isChanged) {
165
+ forceUpdate();
166
+ }
153
167
  }
154
168
  },
155
169
  onUploadStart = (files) => {
@@ -199,9 +213,120 @@ function AttachmentsElement(props) {
199
213
  downloadInBackground(url);
200
214
  }
201
215
  },
216
+ buildModalBody = (url, id) => {
217
+ // This method was abstracted out so showModal/onPrev/onNext can all use it.
218
+ // url comes from FileMosaic, which passes in imageUrl,
219
+ // whereas FileCardCustom passes in id.
220
+
221
+ function findFile(url, id) {
222
+ if (id) {
223
+ return _.find(files, { id });
224
+ }
225
+ return _.find(files, (file) => file.imageUrl === url);
226
+ }
227
+ function findPrevFile(url, id) {
228
+ const
229
+ currentFile = findFile(url, id),
230
+ currentIx = _.findIndex(files, currentFile);
231
+ if (currentIx > 0) {
232
+ return files[currentIx - 1];
233
+ }
234
+ return null;
235
+ }
236
+ function findNextFile(url, id) {
237
+ const
238
+ currentFile = findFile(url, id),
239
+ currentIx = _.findIndex(files, currentFile);
240
+ if (currentIx < files.length - 1) {
241
+ return files[currentIx + 1];
242
+ }
243
+ return null;
244
+ }
245
+
246
+ const
247
+ prevFile = findPrevFile(url, id),
248
+ isPrevDisabled = !prevFile,
249
+ nextFile = findNextFile(url, id),
250
+ isNextDisabled = !nextFile,
251
+ onPrev = () => {
252
+ const { imageUrl, id } = prevFile;
253
+ updateModalBody(buildModalBody(imageUrl, id));
254
+ },
255
+ onNext = () => {
256
+ const { imageUrl, id } = nextFile;
257
+ updateModalBody(buildModalBody(imageUrl, id));
258
+ };
259
+
260
+ let isPdf = false,
261
+ body = null;
262
+
263
+ if (id) {
264
+ const file = _.find(files, { id });
265
+ url = file.imageUrl;
266
+ isPdf = true;
267
+ } else if (url?.match(/\.pdf$/)) {
268
+ isPdf = true;
269
+ }
270
+
271
+ if (isPdf) {
272
+ body = <iframe
273
+ src={url}
274
+ className="w-full h-full"
275
+ />;
276
+ } else {
277
+ body = <CenterBox className="w-full h-full">
278
+ <img src={url} />
279
+ </CenterBox>;
280
+ }
281
+ return <HStack
282
+ className="w-full h-full"
283
+ >
284
+ <IconButton
285
+ onPress={onPrev}
286
+ className="Lightbox-prevBtn h-full w-[50px]"
287
+ icon={ChevronLeft}
288
+ isDisabled={isPrevDisabled}
289
+ />
290
+ {body}
291
+ <IconButton
292
+ onPress={onNext}
293
+ className="Lightbox-prevBtn h-full w-[50px]"
294
+ icon={ChevronRight}
295
+ isDisabled={isNextDisabled}
296
+ />
297
+ </HStack>;
298
+ },
299
+ onViewLightbox = (url, id) => {
300
+ if (!url && !id) {
301
+ alert('Cannot view lightbox until image is uploaded.');
302
+ return;
303
+ }
304
+ showModal({
305
+ title: 'Lightbox',
306
+ body: buildModalBody(url, id),
307
+ canClose: true,
308
+ includeCancel: true,
309
+ w: 1920,
310
+ h: 1080,
311
+ });
312
+ },
202
313
  doDelete = (id) => {
203
- Repository.deleteById(id);
204
- Repository.save();
314
+ const file = Repository.getById(id);
315
+ if (file) {
316
+ // if the file exists in the repository, delete it there
317
+ Repository.deleteById(id);
318
+ Repository.save();
319
+
320
+ } else {
321
+ // simply remove it from the files array
322
+ const newFiles = [];
323
+ _.each(files, (file) => {
324
+ if (file.id !== id) {
325
+ newFiles.push(file);
326
+ }
327
+ });
328
+ setFiles(newFiles);
329
+ }
205
330
  };
206
331
 
207
332
  if (!_.isEqual(modelidCalc, modelid.current)) {
@@ -296,6 +421,12 @@ function AttachmentsElement(props) {
296
421
  <HStack className="AttachmentsElement-HStack flex-wrap">
297
422
  {files.length === 0 && <Text className="text-grey-600 italic">No files</Text>}
298
423
  {files.map((file) => {
424
+ let seeProps = {};
425
+ if (file.type && (file.type.match(/^image\//) || file.type === 'application/pdf')) {
426
+ seeProps = {
427
+ onSee: onViewLightbox,
428
+ };
429
+ }
299
430
  return <Box
300
431
  key={file.id}
301
432
  className="mr-2"
@@ -306,12 +437,14 @@ function AttachmentsElement(props) {
306
437
  backgroundBlurImage={false}
307
438
  onDownload={onDownload}
308
439
  {..._fileMosaic}
440
+ {...seeProps}
309
441
  />}
310
442
  {!useFileMosaic &&
311
443
  <FileCardCustom
312
444
  {...file}
313
445
  backgroundBlurImage={false}
314
446
  {..._fileMosaic}
447
+ {...seeProps}
315
448
  />}
316
449
  </Box>;
317
450
  })}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes