@onehat/ui 0.4.69 → 0.4.70

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.4.69",
3
+ "version": "0.4.70",
4
4
  "description": "Base UI for OneHat apps",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -61,6 +61,7 @@ export function JsonElement(props) {
61
61
  onEdit={(obj) => {
62
62
  setValue(JSON.stringify(obj.updated_src));
63
63
  }}
64
+ collapseStringsAfterLength={500}
64
65
  {...propsToPass}
65
66
  />
66
67
  </HStack>;
@@ -1358,7 +1358,7 @@ function Form(props) {
1358
1358
  icon={getEditorMode() === EDITOR_MODE__ADD ? Plus : FloppyDiskRegular}
1359
1359
  isDisabled={isSaveDisabled}
1360
1360
  className="text-white"
1361
- text={getEditorMode() === EDITOR_MODE__ADD ? 'Add' : 'Save'}
1361
+ text={(getEditorMode() === EDITOR_MODE__ADD ? 'Add' : 'Save') + (props.record?.length > 1 ? ` (${props.record.length})` : '')}
1362
1362
  />}
1363
1363
 
1364
1364
  {showSubmitBtn &&
@@ -194,6 +194,7 @@ function GridComponent(props) {
194
194
  onView,
195
195
  onContextMenu,
196
196
  isAdding,
197
+ isEditorViewOnly,
197
198
 
198
199
  // withData
199
200
  Repository,
@@ -432,7 +433,7 @@ function GridComponent(props) {
432
433
  } else {
433
434
  let canDoEdit = false,
434
435
  canDoView = false;
435
- if (onEdit && canUser && canUser(EDIT) && (!canRecordBeEdited || canRecordBeEdited(selection)) && !props.disableEdit) {
436
+ if (onEdit && canUser && canUser(EDIT) && (!canRecordBeEdited || canRecordBeEdited(selection)) && !props.disableEdit && !isEditorViewOnly) {
436
437
  canDoEdit = true;
437
438
  } else
438
439
  if (onView && canUser && canUser(VIEW) && !props.disableView) {
@@ -1,9 +1,10 @@
1
- import { useMemo, useEffect, } from 'react';
1
+ import { useMemo, useState, useEffect, } from 'react';
2
2
  import {
3
3
  Box,
4
4
  HStack,
5
5
  HStackNative,
6
6
  Icon,
7
+ Text,
7
8
  TextNative,
8
9
  } from '@project-components/Gluestack';
9
10
  import {
@@ -16,9 +17,11 @@ import getComponentFromType from '../../Functions/getComponentFromType.js';
16
17
  import UiGlobals from '../../UiGlobals.js';
17
18
  import { withDragSource, withDropTarget } from '../Hoc/withDnd.js';
18
19
  import testProps from '../../Functions/testProps.js';
20
+ import Loading from '../Messages/Loading.js';
19
21
  import AngleRight from '../Icons/AngleRight.js';
20
22
  import RowDragHandle from './RowDragHandle.js';
21
23
  import RowSelectHandle from './RowSelectHandle.js';
24
+ import useAsyncRenderers from '../../Hooks/useAsyncRenderers.js';
22
25
  import _ from 'lodash';
23
26
 
24
27
  // Conditional import for web only
@@ -64,7 +67,11 @@ function GridRow(props) {
64
67
  dropTargetRef,
65
68
  ...propsToPass
66
69
  } = props,
67
- styles = UiGlobals.styles;
70
+ styles = UiGlobals.styles,
71
+ {
72
+ results: asyncResults,
73
+ loading: asyncLoading,
74
+ } = useAsyncRenderers(columnsConfig, item);
68
75
 
69
76
  if (item.isDestroyed) {
70
77
  return null;
@@ -159,21 +166,25 @@ function GridRow(props) {
159
166
  }
160
167
  if (_.isPlainObject(config)) {
161
168
  if (config.renderer) {
162
- const extraProps = _.omit(config, [
163
- 'header',
164
- 'fieldName',
165
- 'type',
166
- 'isEditable',
167
- 'editor',
168
- 'format',
169
- 'renderer',
170
- 'isReorderable',
171
- 'isResizable',
172
- 'isSortable',
173
- 'w',
174
- 'flex',
175
- 'isOver',
176
- ]);
169
+ const
170
+ asyncResult = asyncResults.get(key),
171
+ isLoading = asyncLoading.has(key),
172
+ extraProps = _.omit(config, [
173
+ 'header',
174
+ 'fieldName',
175
+ 'type',
176
+ 'isEditable',
177
+ 'editor',
178
+ 'format',
179
+ 'renderer',
180
+ 'isAsync',
181
+ 'isReorderable',
182
+ 'isResizable',
183
+ 'isSortable',
184
+ 'w',
185
+ 'flex',
186
+ 'isOver',
187
+ ]);
177
188
 
178
189
  if (!extraProps._web) {
179
190
  extraProps._web = {};
@@ -185,6 +196,31 @@ function GridRow(props) {
185
196
  // userSelect: 'none',
186
197
  };
187
198
 
199
+ let content = null;
200
+ if (config.isAsync) {
201
+ // Async renderer
202
+ if (isLoading) {
203
+ content = <Loading />;
204
+ } else if (asyncResult) {
205
+ if (asyncResult.error) {
206
+ content = <Text>Render Error: {asyncResult.error.message || String(asyncResult.error)}</Text>;
207
+ } else {
208
+ content = asyncResult.result;
209
+ }
210
+ }
211
+ } else {
212
+ // Synchronous renderer
213
+ try {
214
+ const result = config.renderer(item);
215
+ if (result && typeof result.then === 'function') {
216
+ content = <Text>Error: Async renderer not properly configured</Text>;
217
+ } else {
218
+ content = result;
219
+ }
220
+ } catch (error) {
221
+ content = <Text>Render Error: {error}</Text>;
222
+ }
223
+ }
188
224
  return <HStackNative
189
225
  key={key}
190
226
  {...testProps('rendererCol-' + key)}
@@ -192,7 +228,7 @@ function GridRow(props) {
192
228
  {...propsToPass}
193
229
  {...extraProps}
194
230
  style={colStyle}
195
- >{config.renderer(item)}</HStackNative>;
231
+ >{content}</HStackNative>;
196
232
  }
197
233
  if (config.fieldName) {
198
234
 
@@ -390,6 +426,8 @@ function GridRow(props) {
390
426
  >{rowContents}</HStackNative>;
391
427
  }, [
392
428
  columnsConfig,
429
+ asyncResults,
430
+ asyncLoading,
393
431
  columnProps,
394
432
  fields,
395
433
  rowProps,
@@ -0,0 +1,53 @@
1
+ import { useState, useEffect, } from 'react';
2
+
3
+ export default function useAsyncRenderers(columnsConfig, item) {
4
+ const
5
+ [results, setResults] = useState(new Map()),
6
+ [loading, setLoading] = useState(new Set());
7
+
8
+ useEffect(() => {
9
+ const asyncConfigs = columnsConfig.filter(config =>
10
+ config.renderer && typeof config.renderer === 'function'
11
+ );
12
+
13
+ if (asyncConfigs.length === 0) {
14
+ setResults(new Map());
15
+ setLoading(new Set());
16
+ return;
17
+ }
18
+
19
+ const newLoading = new Set();
20
+ asyncConfigs.forEach((config, index) => {
21
+ newLoading.add(index);
22
+ });
23
+ setLoading(newLoading);
24
+
25
+ const promises = asyncConfigs.map(async (config, configIndex) => {
26
+ try {
27
+ const result = await config.renderer(item);
28
+ return { configIndex, result, error: null };
29
+ } catch (error) {
30
+ return { configIndex, result: null, error };
31
+ }
32
+ });
33
+
34
+ Promise.allSettled(promises).then(settled => {
35
+ const
36
+ newResults = new Map(),
37
+ newLoading = new Set();
38
+
39
+ settled.forEach((outcome, index) => {
40
+ if (outcome.status === 'fulfilled') {
41
+ const { configIndex, result, error } = outcome.value;
42
+ newResults.set(configIndex, { result, error });
43
+ }
44
+ });
45
+
46
+ setResults(newResults);
47
+ setLoading(newLoading);
48
+ });
49
+
50
+ }, [columnsConfig, item]);
51
+
52
+ return { results, loading };
53
+ }