@etsoo/materialui 1.4.99 → 1.5.1

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.
@@ -1,20 +1,8 @@
1
- import {
2
- GridColumnRenderProps,
3
- GridDataType,
4
- ScrollRestoration
5
- } from "@etsoo/react";
1
+ import { ScrollRestoration } from "@etsoo/react";
6
2
  import { DataTypes, Utils } from "@etsoo/shared";
7
- import {
8
- Breakpoint,
9
- Grid2,
10
- Grid2Props,
11
- LinearProgress,
12
- Stack,
13
- Typography
14
- } from "@mui/material";
3
+ import { LinearProgress, Stack } from "@mui/material";
15
4
  import React from "react";
16
5
  import { Labels } from "../app/Labels";
17
- import { GridDataFormat } from "../GridDataFormat";
18
6
  import { MUGlobal } from "../MUGlobal";
19
7
  import { PullToRefreshUI } from "../PullToRefreshUI";
20
8
  import { CommonPage, CommonPageProps } from "./CommonPage";
@@ -22,163 +10,14 @@ import type { OperationMessageHandlerAll } from "../messages/OperationMessageHan
22
10
  import { MessageUtils } from "../messages/MessageUtils";
23
11
  import type { RefreshHandler } from "../messages/RefreshHandler";
24
12
  import { OperationMessageContainer } from "../messages/OperationMessageContainer";
25
- import { ReactAppType, useRequiredAppContext } from "../app/ReactApp";
26
- import { useCurrentBreakpoint } from "../useCurrentBreakpoint";
27
-
28
- /**
29
- * View page item size
30
- */
31
- export type ViewPageItemSize = Record<Breakpoint, number | undefined>;
32
-
33
- const breakpoints: Breakpoint[] = ["xs", "sm", "md", "lg", "xl"];
34
-
35
- /**
36
- * View page grid item size
37
- */
38
- export namespace ViewPageSize {
39
- export const large: ViewPageItemSize = {
40
- xs: 12,
41
- sm: 12,
42
- md: 9,
43
- lg: 6,
44
- xl: 4
45
- };
46
- export const medium: ViewPageItemSize = {
47
- xs: 12,
48
- sm: 12,
49
- md: 6,
50
- lg: 4,
51
- xl: 3
52
- };
53
- export const line: ViewPageItemSize = {
54
- xs: 12,
55
- sm: 12,
56
- md: 12,
57
- lg: 12,
58
- xl: 12
59
- };
60
- export const small: ViewPageItemSize = { xs: 6, sm: 6, md: 4, lg: 3, xl: 2 };
61
- export const smallLine: ViewPageItemSize = {
62
- xs: 12,
63
- sm: 6,
64
- md: 4,
65
- lg: 3,
66
- xl: 2
67
- };
68
- export function matchSize(size: ViewPageItemSize) {
69
- return Object.fromEntries(
70
- Object.entries(size).map(([key, value]) => [
71
- key,
72
- value == null ? undefined : value === 12 ? 12 : 12 - value
73
- ])
74
- );
75
- }
76
- }
77
-
78
- /**
79
- * View page grid item properties
80
- */
81
- export type ViewPageGridItemProps = Grid2Props & {
82
- data: React.ReactNode;
83
- label?: React.ReactNode;
84
- singleRow?: ViewPageRowType;
85
- };
86
-
87
- /**
88
- * View page grid item
89
- * @param props Props
90
- * @returns Result
91
- */
92
- export function ViewPageGridItem(props: ViewPageGridItemProps) {
93
- // Destruct
94
- const { data, label, singleRow, ...gridProps } = props;
95
-
96
- // Default options
97
- let options = {};
98
- if (gridProps.size == null) {
99
- options = getResp(singleRow ?? "small");
100
- } else if (singleRow != null) {
101
- options = getResp(singleRow ?? "small");
102
- }
103
-
104
- // Layout
105
- return (
106
- <Grid2 {...gridProps} {...options}>
107
- {label != null && (
108
- <Typography variant="caption" component="div">
109
- {label}:
110
- </Typography>
111
- )}
112
- {typeof data === "object" ? (
113
- data
114
- ) : (
115
- <Typography variant="subtitle2">{data}</Typography>
116
- )}
117
- </Grid2>
118
- );
119
- }
120
-
121
- /**
122
- * View page row width type
123
- */
124
- export type ViewPageRowType =
125
- | boolean
126
- | "default"
127
- | "small"
128
- | "medium"
129
- | "large"
130
- | ViewPageItemSize;
131
-
132
- /**
133
- * View page display field
134
- */
135
- export interface ViewPageField<T extends object> extends Grid2Props {
136
- /**
137
- * Data field
138
- */
139
- data: (string & keyof T) | ((item: T) => React.ReactNode);
140
-
141
- /**
142
- * Data type
143
- */
144
- dataType?: GridDataType;
145
-
146
- /**
147
- * Label field
148
- */
149
- label?: string | ((item: T) => React.ReactNode);
150
-
151
- /**
152
- * Display as single row
153
- */
154
- singleRow?: ViewPageRowType;
155
-
156
- /**
157
- * Render props
158
- */
159
- renderProps?: GridColumnRenderProps;
160
- }
161
-
162
- type ViewPageFieldTypeNarrow<T extends object> =
163
- | (string & keyof T)
164
- | [string & keyof T, GridDataType, GridColumnRenderProps?, ViewPageRowType?]
165
- | ViewPageField<T>;
166
-
167
- /**
168
- * View page field type
169
- */
170
- export type ViewPageFieldType<T extends object> =
171
- | ViewPageFieldTypeNarrow<T>
172
- | ((
173
- data: T,
174
- refresh: () => Promise<void>
175
- ) => React.ReactNode | [React.ReactNode, ViewPageItemSize]);
13
+ import { ViewContainer, ViewContainerProps } from "../ViewContainer";
176
14
 
177
15
  /**
178
16
  * View page props
179
17
  */
180
18
  export interface ViewPageProps<T extends DataTypes.StringRecord>
181
- extends Omit<CommonPageProps, "children"> {
19
+ extends Omit<CommonPageProps, "children">,
20
+ Omit<ViewContainerProps<T>, "data"> {
182
21
  /**
183
22
  * Actions
184
23
  */
@@ -198,11 +37,6 @@ export interface ViewPageProps<T extends DataTypes.StringRecord>
198
37
  | React.ReactNode
199
38
  | ((data: T, refresh: () => PromiseLike<void>) => React.ReactNode);
200
39
 
201
- /**
202
- * Fields to display
203
- */
204
- fields: ViewPageFieldType<T>[];
205
-
206
40
  /**
207
41
  * Load data
208
42
  */
@@ -213,21 +47,11 @@ export interface ViewPageProps<T extends DataTypes.StringRecord>
213
47
  */
214
48
  pullToRefresh?: boolean;
215
49
 
216
- /**
217
- * Grid spacing
218
- */
219
- spacing?: Record<string, string | number>;
220
-
221
50
  /**
222
51
  * Support refresh
223
52
  */
224
53
  supportRefresh?: boolean;
225
54
 
226
- /**
227
- * Grid container reference
228
- */
229
- gridRef?: React.Ref<HTMLDivElement>;
230
-
231
55
  /**
232
56
  * Operation message handler
233
57
  */
@@ -241,123 +65,6 @@ export interface ViewPageProps<T extends DataTypes.StringRecord>
241
65
  * @returns
242
66
  */
243
67
  titleBar?: (data: T) => React.ReactNode;
244
-
245
- /**
246
- * Left container
247
- */
248
- leftContainer?: (data: T) => React.ReactNode;
249
-
250
- /**
251
- * Left container height in lines
252
- */
253
- leftContainerLines?: number;
254
-
255
- /**
256
- * Left container properties
257
- */
258
- leftContainerProps?: Omit<Grid2Props, "size"> & { size?: ViewPageItemSize };
259
- }
260
-
261
- function formatItemData(
262
- app: ReactAppType,
263
- fieldData: unknown
264
- ): string | undefined {
265
- if (fieldData == null) return undefined;
266
- if (typeof fieldData === "string") return fieldData;
267
- if (fieldData instanceof Date) return app.formatDate(fieldData, "d");
268
- return `${fieldData}`;
269
- }
270
-
271
- function getResp(singleRow: ViewPageRowType) {
272
- const size =
273
- typeof singleRow === "object"
274
- ? singleRow
275
- : singleRow === "medium"
276
- ? ViewPageSize.medium
277
- : singleRow === "large"
278
- ? ViewPageSize.large
279
- : singleRow === true
280
- ? ViewPageSize.line
281
- : singleRow === false
282
- ? ViewPageSize.smallLine
283
- : ViewPageSize.small;
284
- return size;
285
- }
286
-
287
- function getItemField<T extends object>(
288
- app: ReactAppType,
289
- field: ViewPageFieldTypeNarrow<T>,
290
- data: T
291
- ): [React.ReactNode, React.ReactNode, Grid2Props, ViewPageItemSize] {
292
- // Item data and label
293
- let itemData: React.ReactNode,
294
- itemLabel: React.ReactNode,
295
- gridProps: Grid2Props = {},
296
- size: ViewPageItemSize;
297
-
298
- if (Array.isArray(field)) {
299
- const [fieldData, fieldType, renderProps, singleRow = "small"] = field;
300
- itemData = GridDataFormat(data[fieldData], fieldType, renderProps);
301
- itemLabel = app.get<string>(fieldData) ?? fieldData;
302
- size = getResp(singleRow);
303
- gridProps = { size };
304
- } else if (typeof field === "object") {
305
- // Destruct
306
- const {
307
- data: fieldData,
308
- dataType,
309
- label: fieldLabel,
310
- renderProps,
311
- singleRow = "default",
312
- ...rest
313
- } = field;
314
-
315
- // Size
316
- size = getResp(singleRow);
317
-
318
- gridProps = {
319
- ...rest,
320
- size
321
- };
322
-
323
- // Field data
324
- if (typeof fieldData === "function") itemData = fieldData(data);
325
- else if (dataType == null) itemData = formatItemData(app, data[fieldData]);
326
- else itemData = GridDataFormat(data[fieldData], dataType, renderProps);
327
-
328
- // Field label
329
- itemLabel =
330
- fieldLabel === ""
331
- ? undefined
332
- : fieldLabel == null && typeof fieldData === "string"
333
- ? app.get<string>(fieldData) ?? fieldData
334
- : typeof fieldLabel === "function"
335
- ? fieldLabel(data)
336
- : fieldLabel != null
337
- ? app.get<string>(fieldLabel) ?? fieldLabel
338
- : undefined;
339
- } else {
340
- // Single field format
341
- itemData = formatItemData(app, data[field]);
342
- itemLabel = app.get<string>(field) ?? field;
343
- size = ViewPageSize.small;
344
- gridProps = { size };
345
- }
346
-
347
- return [itemData, itemLabel, gridProps, size];
348
- }
349
-
350
- function getItemSize(bp: Breakpoint, size: ViewPageItemSize) {
351
- const v = size[bp];
352
- if (v != null) return v;
353
-
354
- const index = breakpoints.indexOf(bp);
355
- for (let i = index; i >= 0; i--) {
356
- const v = size[breakpoints[i]];
357
- if (v != null) return v;
358
- }
359
-
360
- return 12;
361
68
  }
362
69
 
363
70
  /**
@@ -367,9 +74,6 @@ function getItemSize(bp: Breakpoint, size: ViewPageItemSize) {
367
74
  export function ViewPage<T extends DataTypes.StringRecord>(
368
75
  props: ViewPageProps<T>
369
76
  ) {
370
- // Global app
371
- const app = useRequiredAppContext();
372
-
373
77
  // Destruct
374
78
  const {
375
79
  actions,
@@ -377,7 +81,7 @@ export function ViewPage<T extends DataTypes.StringRecord>(
377
81
  fields,
378
82
  loadData,
379
83
  paddings = MUGlobal.pagePaddings,
380
- spacing = MUGlobal.half(MUGlobal.pagePaddings),
84
+ spacing,
381
85
  actionPaddings = MUGlobal.pagePaddings,
382
86
  supportRefresh = true,
383
87
  fabColumnDirection = true,
@@ -388,14 +92,11 @@ export function ViewPage<T extends DataTypes.StringRecord>(
388
92
  operationMessageHandler,
389
93
  titleBar,
390
94
  leftContainer,
391
- leftContainerLines = 3,
392
- leftContainerProps = {},
95
+ leftContainerLines,
96
+ leftContainerProps,
393
97
  ...rest
394
98
  } = props;
395
99
 
396
- // Current breakpoint
397
- const bp = useCurrentBreakpoint();
398
-
399
100
  // Data
400
101
  const [data, setData] = React.useState<T>();
401
102
 
@@ -405,10 +106,6 @@ export function ViewPage<T extends DataTypes.StringRecord>(
405
106
  // Container
406
107
  const pullContainer = "#page-container";
407
108
 
408
- // Left container
409
- const { size = ViewPageSize.smallLine, ...leftContainerPropsRest } =
410
- leftContainerProps;
411
-
412
109
  // Load data
413
110
  const refresh = React.useCallback(async () => {
414
111
  const result = await loadData();
@@ -416,76 +113,6 @@ export function ViewPage<T extends DataTypes.StringRecord>(
416
113
  setData(result);
417
114
  }, [loadData]);
418
115
 
419
- // Create fields
420
- const fieldIndexRef = React.useRef(0);
421
- const createFields = React.useCallback(
422
- (data: T, maxItems: number = 0) => {
423
- let validItems = 0;
424
- const items: React.ReactNode[] = [];
425
- let i: number = fieldIndexRef.current;
426
- for (; i < fields.length; i++) {
427
- const field = fields[i];
428
- let oneSize: ViewPageItemSize;
429
- let oneItem: React.ReactNode;
430
- if (typeof field === "function") {
431
- // Most flexible way, do whatever you want
432
- const createdResult = field(data, refresh);
433
- if (createdResult == null || createdResult === "") continue;
434
- if (Array.isArray(createdResult)) {
435
- const [created, size] = createdResult;
436
- oneSize = size;
437
- oneItem = created;
438
- } else {
439
- oneSize = ViewPageSize.line;
440
- oneItem = createdResult;
441
- }
442
- } else {
443
- const [itemData, itemLabel, gridProps, size] = getItemField(
444
- app,
445
- field,
446
- data
447
- );
448
-
449
- // Some callback function may return '' instead of undefined
450
- if (itemData == null || itemData === "") continue;
451
-
452
- oneSize = size;
453
- oneItem = (
454
- <ViewPageGridItem
455
- {...gridProps}
456
- key={i}
457
- data={itemData}
458
- label={itemLabel}
459
- />
460
- );
461
- }
462
-
463
- // Max lines
464
- if (maxItems > 0) {
465
- const itemSize = getItemSize(bp, oneSize);
466
- if (maxItems < validItems + itemSize) {
467
- fieldIndexRef.current = i;
468
- break;
469
- } else {
470
- items.push(oneItem);
471
- validItems += itemSize;
472
- }
473
- } else {
474
- items.push(oneItem);
475
- }
476
- }
477
-
478
- if (maxItems === 0) {
479
- fieldIndexRef.current = 0;
480
- } else {
481
- fieldIndexRef.current = i;
482
- }
483
-
484
- return items;
485
- },
486
- [app, refresh, fields, data, bp]
487
- );
488
-
489
116
  React.useEffect(() => {
490
117
  const refreshHandler: RefreshHandler = async () => {
491
118
  await refresh();
@@ -497,18 +124,12 @@ export function ViewPage<T extends DataTypes.StringRecord>(
497
124
  };
498
125
  }, [refresh]);
499
126
 
500
- let leftResult: React.ReactNode;
501
-
502
127
  return (
503
128
  <CommonPage
504
129
  paddings={paddings}
505
130
  onRefresh={supportRefresh ? refresh : undefined}
506
131
  onUpdate={supportRefresh ? undefined : refresh}
507
- sx={{
508
- ".MuiTypography-subtitle2": {
509
- fontWeight: "bold"
510
- }
511
- }}
132
+ className="ET-ViewPage"
512
133
  {...rest}
513
134
  scrollContainer={globalThis}
514
135
  fabColumnDirection={fabColumnDirection}
@@ -533,39 +154,16 @@ export function ViewPage<T extends DataTypes.StringRecord>(
533
154
  />
534
155
  )}
535
156
  {titleBar && titleBar(data)}
536
- <Grid2
537
- container
538
- justifyContent="left"
539
- className="ET-ViewPage"
540
- ref={gridRef}
157
+ <ViewContainer
158
+ data={data}
159
+ fields={fields}
160
+ gridRef={gridRef}
161
+ leftContainer={leftContainer}
162
+ leftContainerLines={leftContainerLines}
163
+ leftContainerProps={leftContainerProps}
164
+ refresh={refresh}
541
165
  spacing={spacing}
542
- >
543
- {leftContainer && (leftResult = leftContainer(data)) != null && (
544
- <React.Fragment>
545
- <Grid2
546
- container
547
- className="ET-ViewPage-LeftContainer"
548
- spacing={spacing}
549
- size={size}
550
- {...leftContainerPropsRest}
551
- >
552
- {leftResult}
553
- </Grid2>
554
- <Grid2
555
- container
556
- className="ET-ViewPage-LeftOthers"
557
- spacing={spacing}
558
- size={ViewPageSize.matchSize(size)}
559
- >
560
- {createFields(
561
- data,
562
- leftContainerLines * (12 - getItemSize(bp, size))
563
- )}
564
- </Grid2>
565
- </React.Fragment>
566
- )}
567
- {createFields(data)}
568
- </Grid2>
166
+ />
569
167
  {actions !== null && (
570
168
  <Stack
571
169
  className="ET-ViewPage-Actions"