@hitachivantara/uikit-cli 6.0.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.
Files changed (109) hide show
  1. package/README.md +46 -0
  2. package/package.json +68 -0
  3. package/src/app-shell.js +106 -0
  4. package/src/baselines/app-shell/vite/_gitignore +30 -0
  5. package/src/baselines/app-shell/vite/_oxlintrc.json +5 -0
  6. package/src/baselines/app-shell/vite/_package.json +55 -0
  7. package/src/baselines/app-shell/vite/public/locales/en/example.json +8 -0
  8. package/src/baselines/app-shell/vite/src/lib/data/config.ts +15 -0
  9. package/src/baselines/app-shell/vite/src/lib/i18n.ts +44 -0
  10. package/src/baselines/app-shell/vite/src/pages/Example/index.tsx +25 -0
  11. package/src/baselines/app-shell/vite/src/providers/Provider.tsx +31 -0
  12. package/src/baselines/app-shell/vite/src/tests/mocks.ts +1 -0
  13. package/src/baselines/app-shell/vite/src/tests/providers.tsx +13 -0
  14. package/src/baselines/app-shell/vite/src/tests/setupTests.ts +24 -0
  15. package/src/baselines/app-shell/vite/src/types/theme.d.ts +8 -0
  16. package/src/baselines/app-shell/vite/src/types/vite-env.d.ts +1 -0
  17. package/src/baselines/app-shell/vite/tsconfig.json +10 -0
  18. package/src/baselines/app-shell/vite/tsconfig.node.json +9 -0
  19. package/src/baselines/app-shell/vite/uno.config.ts +6 -0
  20. package/src/baselines/app-shell/vite/vite.config.ts +45 -0
  21. package/src/baselines/vite/_gitignore +30 -0
  22. package/src/baselines/vite/_oxlintrc.json +5 -0
  23. package/src/baselines/vite/_package.json +53 -0
  24. package/src/baselines/vite/index.html +18 -0
  25. package/src/baselines/vite/public/favicon.ico +0 -0
  26. package/src/baselines/vite/public/locales/en/common.json +16 -0
  27. package/src/baselines/vite/public/locales/en/home.json +4 -0
  28. package/src/baselines/vite/public/logo192.png +0 -0
  29. package/src/baselines/vite/src/App.tsx +31 -0
  30. package/src/baselines/vite/src/assets/HitachiLogo.tsx +27 -0
  31. package/src/baselines/vite/src/components/common/Loading/Loading.test.tsx +18 -0
  32. package/src/baselines/vite/src/components/common/Loading/Loading.tsx +15 -0
  33. package/src/baselines/vite/src/components/common/Loading/index.ts +1 -0
  34. package/src/baselines/vite/src/context/NavigationContext.tsx +67 -0
  35. package/src/baselines/vite/src/lib/i18n.ts +29 -0
  36. package/src/baselines/vite/src/main.tsx +12 -0
  37. package/src/baselines/vite/src/pages/Home/index.tsx +13 -0
  38. package/src/baselines/vite/src/pages/NotFound/NotFound.tsx +39 -0
  39. package/src/baselines/vite/src/pages/NotFound/index.tsx +1 -0
  40. package/src/baselines/vite/src/pages/layout/navigation.tsx +82 -0
  41. package/src/baselines/vite/src/routes.tsx +14 -0
  42. package/src/baselines/vite/src/tests/mocks.ts +1 -0
  43. package/src/baselines/vite/src/tests/providers.tsx +13 -0
  44. package/src/baselines/vite/src/tests/setupTests.ts +24 -0
  45. package/src/baselines/vite/src/types/theme.d.ts +8 -0
  46. package/src/baselines/vite/src/vite-env.d.ts +1 -0
  47. package/src/baselines/vite/tsconfig.json +10 -0
  48. package/src/baselines/vite/tsconfig.node.json +9 -0
  49. package/src/baselines/vite/uno.config.ts +6 -0
  50. package/src/baselines/vite/vite.config.ts +31 -0
  51. package/src/contents.js +63 -0
  52. package/src/create.js +172 -0
  53. package/src/index.js +22 -0
  54. package/src/navigation.js +21 -0
  55. package/src/package.js +37 -0
  56. package/src/plop-templates/README.md.hbs +10 -0
  57. package/src/plop-templates/app-shell/app-shell.config.ts.hbs +54 -0
  58. package/src/plop-templates/app-shell/index.html.hbs +15 -0
  59. package/src/plopfile.js +61 -0
  60. package/src/templates/AssetInventory/CardView.tsx +167 -0
  61. package/src/templates/AssetInventory/ListView.tsx +56 -0
  62. package/src/templates/AssetInventory/data.tsx +255 -0
  63. package/src/templates/AssetInventory/index.tsx +198 -0
  64. package/src/templates/AssetInventory/usePaginationData.ts +158 -0
  65. package/src/templates/Canvas/Context.tsx +49 -0
  66. package/src/templates/Canvas/ListView.tsx +189 -0
  67. package/src/templates/Canvas/Node.tsx +203 -0
  68. package/src/templates/Canvas/Sidebar.tsx +51 -0
  69. package/src/templates/Canvas/StatusEdge.tsx +75 -0
  70. package/src/templates/Canvas/StickyNode.tsx +475 -0
  71. package/src/templates/Canvas/Table.tsx +202 -0
  72. package/src/templates/Canvas/TreeView.tsx +211 -0
  73. package/src/templates/Canvas/dependencies.json +7 -0
  74. package/src/templates/Canvas/index.tsx +363 -0
  75. package/src/templates/Canvas/styles.tsx +41 -0
  76. package/src/templates/Canvas/utils.tsx +70 -0
  77. package/src/templates/Dashboard/GridPanel.tsx +33 -0
  78. package/src/templates/Dashboard/Kpi.tsx +107 -0
  79. package/src/templates/Dashboard/Map.styles.ts +681 -0
  80. package/src/templates/Dashboard/Map.tsx +71 -0
  81. package/src/templates/Dashboard/data.ts +67 -0
  82. package/src/templates/Dashboard/dependencies.json +11 -0
  83. package/src/templates/Dashboard/index.tsx +173 -0
  84. package/src/templates/DetailsView/KPIs.tsx +70 -0
  85. package/src/templates/DetailsView/MetadataItem.tsx +35 -0
  86. package/src/templates/DetailsView/Properties.tsx +127 -0
  87. package/src/templates/DetailsView/Table.tsx +104 -0
  88. package/src/templates/DetailsView/data.ts +67 -0
  89. package/src/templates/DetailsView/index.tsx +102 -0
  90. package/src/templates/DetailsView/usePaginationData.ts +155 -0
  91. package/src/templates/DetailsView/utils.ts +51 -0
  92. package/src/templates/Form/index.tsx +107 -0
  93. package/src/templates/KanbanBoard/ColumnContainer.tsx +89 -0
  94. package/src/templates/KanbanBoard/TaskCard.tsx +130 -0
  95. package/src/templates/KanbanBoard/data.tsx +140 -0
  96. package/src/templates/KanbanBoard/dependencies.json +6 -0
  97. package/src/templates/KanbanBoard/index.tsx +179 -0
  98. package/src/templates/KanbanBoard/styles.tsx +76 -0
  99. package/src/templates/KanbanBoard/types.ts +21 -0
  100. package/src/templates/ListView/Indicator.tsx +42 -0
  101. package/src/templates/ListView/Kpi.tsx +120 -0
  102. package/src/templates/ListView/Table.tsx +55 -0
  103. package/src/templates/ListView/data.tsx +179 -0
  104. package/src/templates/ListView/dependencies.json +5 -0
  105. package/src/templates/ListView/index.tsx +245 -0
  106. package/src/templates/ListView/usePaginationData.ts +158 -0
  107. package/src/templates/Welcome/index.tsx +101 -0
  108. package/src/templates/package.json +30 -0
  109. package/src/utils.js +37 -0
@@ -0,0 +1,475 @@
1
+ import { useCallback, useMemo, useRef, useState } from "react";
2
+ import { css, cx } from "@emotion/css";
3
+ import Popover from "@mui/material/Popover";
4
+ import {
5
+ NodeProps,
6
+ NodeResizer,
7
+ NodeToolbar,
8
+ Position,
9
+ useReactFlow,
10
+ } from "reactflow";
11
+ import {
12
+ HvButton,
13
+ HvCheckBox,
14
+ HvColorAny,
15
+ HvColorPicker,
16
+ HvDialogContent,
17
+ HvDialogTitle,
18
+ HvIconButton,
19
+ HvListItem,
20
+ HvMultiButton,
21
+ HvSelectionList,
22
+ theme,
23
+ } from "@hitachivantara/uikit-react-core";
24
+ import {
25
+ ActualSize,
26
+ Bold,
27
+ Delete,
28
+ FontSize,
29
+ Fullscreen,
30
+ Italic,
31
+ Palette,
32
+ } from "@hitachivantara/uikit-react-icons";
33
+
34
+ export type StickyNodeData =
35
+ | undefined
36
+ | {
37
+ title?: string;
38
+ backgroundColor?: HvColorAny;
39
+ borderColor?: HvColorAny;
40
+ textColor?: HvColorAny;
41
+ hasShadow?: boolean;
42
+ bold?: boolean;
43
+ italic?: boolean;
44
+ fontSize?: number;
45
+ expanded?: boolean;
46
+ visible?: boolean;
47
+ onDelete?: () => void;
48
+ };
49
+
50
+ const defaultData: StickyNodeData = {
51
+ title: "Sticky Note",
52
+ backgroundColor: theme.colors.warningSubtle,
53
+ borderColor: theme.colors.warningSubtle,
54
+ textColor: theme.colors.text,
55
+ hasShadow: true,
56
+ bold: false,
57
+ italic: false,
58
+ fontSize: 14,
59
+ expanded: true,
60
+ visible: true,
61
+ };
62
+
63
+ const classes = {
64
+ root: css({
65
+ width: "100%",
66
+ height: "100%",
67
+ position: "relative",
68
+ display: "flex",
69
+ outline: "none",
70
+ }),
71
+ nodeToolbar: css({
72
+ backgroundColor: "transparent",
73
+ borderRadius: theme.radii.full,
74
+ top: 10,
75
+ }),
76
+ buttonsContainer: css({
77
+ padding: theme.space.xs,
78
+ }),
79
+ textAreaRoot: css({
80
+ flex: 1,
81
+ width: "100%",
82
+ border: "none",
83
+ background: "transparent",
84
+ outline: "none",
85
+ }),
86
+ textAreaInput: css({
87
+ resize: "none",
88
+ height: "100%",
89
+ width: "100%",
90
+ padding: theme.space.xs,
91
+ "&:focus-visible": {
92
+ outline: "none",
93
+ },
94
+ paddingRight: 32,
95
+ }),
96
+ textAreaInputFolded: css({
97
+ resize: "none",
98
+ width: "100%",
99
+ padding: 0,
100
+ border: "none",
101
+ overflow: "hidden",
102
+ minHeight: "1.5rem",
103
+ height: "auto",
104
+ }),
105
+ colorConfig: css({
106
+ display: "flex",
107
+ flexDirection: "column",
108
+ gap: theme.space.sm,
109
+ }),
110
+ folded: css({
111
+ width: 34,
112
+ height: 34,
113
+ }),
114
+ expandButton: css({
115
+ position: "absolute",
116
+ top: theme.space.xxs,
117
+ right: theme.space.xxs,
118
+ }),
119
+ fontSizes: css({
120
+ maxHeight: 160,
121
+ overflowY: "auto",
122
+ padding: theme.space.xs,
123
+ }),
124
+ };
125
+
126
+ const colorsToConfig = ["textColor", "backgroundColor", "borderColor"];
127
+ const fontSizes = [10, 11, 12, 14, 16, 20, 24, 32, 36, 40, 48, 64, 96, 128];
128
+
129
+ export const StickyNode = ({
130
+ id,
131
+ selected,
132
+ data = {},
133
+ }: NodeProps<StickyNodeData>) => {
134
+ const mergedData = useMemo(() => ({ ...defaultData, ...data }), [data]);
135
+
136
+ const [text, setText] = useState("");
137
+ const { setNodes } = useReactFlow();
138
+
139
+ const [editing, setEditing] = useState(false);
140
+
141
+ const [toolbarVisible, setToolbarVisible] = useState(false);
142
+ const [colorsConfigOpen, setColorsConfigOpen] = useState(false);
143
+ const [fontSizeConfigOpen, setFontSizeConfigOpen] = useState(false);
144
+
145
+ const [fontSize, setFontSize] = useState(mergedData.fontSize ?? 14);
146
+
147
+ const colorConfigBtnRef = useRef<HTMLButtonElement>(null);
148
+ const fontSizeConfigBtnRef = useRef<HTMLButtonElement>(null);
149
+
150
+ const handleToggleBold = useCallback(() => {
151
+ setNodes((nds) =>
152
+ nds.map((node) => {
153
+ if (node.id === id) {
154
+ node.data = {
155
+ ...node.data,
156
+ bold: !node.data?.bold,
157
+ };
158
+ }
159
+ return node;
160
+ }),
161
+ );
162
+ }, [setNodes, id]);
163
+
164
+ const handleToggleItalic = useCallback(() => {
165
+ setNodes((nds) =>
166
+ nds.map((node) => {
167
+ if (node.id === id) {
168
+ node.data = {
169
+ ...node.data,
170
+ italic: !node.data?.italic,
171
+ };
172
+ }
173
+ return node;
174
+ }),
175
+ );
176
+ }, [setNodes, id]);
177
+
178
+ const handleChangeFontSize = useCallback(
179
+ (size: number) => {
180
+ setFontSize(size);
181
+ setNodes((nds) =>
182
+ nds.map((node) => {
183
+ if (node.id === id) {
184
+ node.data = {
185
+ ...node.data,
186
+ fontSize: size,
187
+ };
188
+ }
189
+ return node;
190
+ }),
191
+ );
192
+ setFontSizeConfigOpen(false);
193
+ },
194
+ [setNodes, id],
195
+ );
196
+
197
+ const handleToggleExpand = useCallback(() => {
198
+ setNodes((nds) =>
199
+ nds.map((node) => {
200
+ if (node.id === id) {
201
+ node.data = {
202
+ ...node.data,
203
+ expanded: !node.data?.expanded,
204
+ };
205
+ }
206
+ return node;
207
+ }),
208
+ );
209
+ }, [setNodes, id]);
210
+
211
+ const handleResetColors = useCallback(() => {
212
+ setNodes((nds) =>
213
+ nds.map((node) => {
214
+ if (node.id === id) {
215
+ node.data = {
216
+ ...node.data,
217
+ backgroundColor: defaultData.backgroundColor,
218
+ borderColor: defaultData.borderColor,
219
+ textColor: defaultData.textColor,
220
+ hasShadow: defaultData.hasShadow,
221
+ };
222
+ }
223
+ return node;
224
+ }),
225
+ );
226
+ }, [setNodes, id]);
227
+
228
+ const handleDeleteSticky = useCallback(() => {
229
+ setNodes((nds) => nds.filter((node) => node.id !== id));
230
+ mergedData.onDelete?.();
231
+ }, [mergedData, setNodes, id]);
232
+
233
+ if (!mergedData.visible) return null;
234
+
235
+ return (
236
+ <>
237
+ <Popover
238
+ open={colorsConfigOpen}
239
+ anchorEl={colorConfigBtnRef.current}
240
+ onClose={() => {
241
+ setColorsConfigOpen(false);
242
+ setEditing(false);
243
+ }}
244
+ anchorOrigin={{
245
+ vertical: "bottom",
246
+ horizontal: "left",
247
+ }}
248
+ transformOrigin={{
249
+ vertical: "top",
250
+ horizontal: "left",
251
+ }}
252
+ >
253
+ <HvDialogTitle>Customize Colors</HvDialogTitle>
254
+ <HvDialogContent>
255
+ <div className={classes.colorConfig}>
256
+ {colorsToConfig.map((c) => (
257
+ <HvColorPicker
258
+ key={c}
259
+ label={`${c.charAt(0).toUpperCase() + c.slice(1).replace("Color", " Color")}`}
260
+ value={mergedData[c as keyof StickyNodeData] ?? ""}
261
+ onChange={(color) => {
262
+ setNodes((nds) =>
263
+ nds.map((node) => {
264
+ if (node.id === id) {
265
+ node.data = {
266
+ ...node.data,
267
+ [c]: color,
268
+ };
269
+ }
270
+ return node;
271
+ }),
272
+ );
273
+ }}
274
+ />
275
+ ))}
276
+ <HvCheckBox
277
+ label="Drop Shadow"
278
+ defaultChecked={mergedData.hasShadow}
279
+ onChange={(_, checked) => {
280
+ setNodes((nds) =>
281
+ nds.map((node) => {
282
+ if (node.id === id) {
283
+ node.data = {
284
+ ...node.data,
285
+ hasShadow: checked,
286
+ };
287
+ }
288
+ return node;
289
+ }),
290
+ );
291
+ }}
292
+ />
293
+ <HvButton variant="secondarySubtle" onClick={handleResetColors}>
294
+ Reset to defaults
295
+ </HvButton>
296
+ </div>
297
+ </HvDialogContent>
298
+ </Popover>
299
+ <Popover
300
+ open={fontSizeConfigOpen}
301
+ anchorEl={fontSizeConfigBtnRef.current}
302
+ onClose={() => {
303
+ setFontSizeConfigOpen(false);
304
+ setEditing(false);
305
+ }}
306
+ anchorOrigin={{
307
+ vertical: "bottom",
308
+ horizontal: "right",
309
+ }}
310
+ transformOrigin={{
311
+ vertical: "top",
312
+ horizontal: "right",
313
+ }}
314
+ >
315
+ <HvSelectionList
316
+ className={classes.fontSizes}
317
+ value={fontSize}
318
+ onChange={(evt, newValue) => {
319
+ handleChangeFontSize(newValue);
320
+ }}
321
+ >
322
+ {fontSizes.map((size) => (
323
+ <HvListItem value={size} key={size}>
324
+ {size}
325
+ </HvListItem>
326
+ ))}
327
+ </HvSelectionList>
328
+ </Popover>
329
+ <div
330
+ className={cx(
331
+ classes.root,
332
+ "nowheel",
333
+ !mergedData.expanded && classes.folded,
334
+ )}
335
+ style={{
336
+ backgroundColor:
337
+ mergedData.backgroundColor ?? theme.colors.bgContainer,
338
+ boxShadow: mergedData.hasShadow
339
+ ? "0 8px 12px rgba(65,65,65,0.25)"
340
+ : "none",
341
+ border: mergedData.borderColor
342
+ ? `1px solid ${mergedData.borderColor}`
343
+ : "none",
344
+ fontSize: `${mergedData.fontSize ?? 14}px`,
345
+ lineHeight: `${mergedData.fontSize ?? 14}px`,
346
+ }}
347
+ >
348
+ {mergedData.expanded && (
349
+ <>
350
+ <NodeResizer
351
+ isVisible={selected}
352
+ minWidth={100}
353
+ minHeight={75}
354
+ lineStyle={{
355
+ color: theme.colors.primary,
356
+ borderStyle: "solid",
357
+ }}
358
+ handleStyle={{
359
+ width: 6,
360
+ height: 6,
361
+ border: `1px solid ${theme.colors.primary}`,
362
+ backgroundColor: mergedData.backgroundColor ?? "transparent",
363
+ borderRadius: theme.radii.full,
364
+ }}
365
+ />
366
+ <NodeToolbar
367
+ isVisible={editing || toolbarVisible || selected}
368
+ position={Position.Top}
369
+ className={classes.nodeToolbar}
370
+ onMouseEnter={() => setToolbarVisible(true)}
371
+ onMouseLeave={() => setToolbarVisible(false)}
372
+ >
373
+ <div className={classes.buttonsContainer}>
374
+ <HvMultiButton>
375
+ <HvIconButton
376
+ aria-label="Font Size"
377
+ title="Font Size"
378
+ ref={fontSizeConfigBtnRef}
379
+ onClick={() => {
380
+ setEditing(true);
381
+ setFontSizeConfigOpen(true);
382
+ }}
383
+ >
384
+ <FontSize />
385
+ </HvIconButton>
386
+ <HvIconButton
387
+ aria-label="Bold"
388
+ title="Bold"
389
+ selected={mergedData.bold}
390
+ onClick={handleToggleBold}
391
+ >
392
+ <Bold />
393
+ </HvIconButton>
394
+ <HvIconButton
395
+ aria-label="Italic"
396
+ title="Italic"
397
+ selected={mergedData.italic}
398
+ onClick={handleToggleItalic}
399
+ >
400
+ <Italic />
401
+ </HvIconButton>
402
+ <HvIconButton
403
+ aria-label="Customize Colors"
404
+ title="Customize Colors"
405
+ ref={colorConfigBtnRef}
406
+ onClick={() => {
407
+ setEditing(true);
408
+ setColorsConfigOpen(true);
409
+ }}
410
+ >
411
+ <Palette />
412
+ </HvIconButton>
413
+ <HvIconButton
414
+ aria-label="Delete"
415
+ title="Delete"
416
+ onClick={handleDeleteSticky}
417
+ >
418
+ <Delete />
419
+ </HvIconButton>
420
+ </HvMultiButton>
421
+ </div>
422
+ </NodeToolbar>
423
+ <div
424
+ onMouseEnter={() => setToolbarVisible(true)}
425
+ onMouseLeave={() => setToolbarVisible(false)}
426
+ className={classes.textAreaRoot}
427
+ >
428
+ <HvIconButton
429
+ className={classes.expandButton}
430
+ title="Fold"
431
+ onClick={handleToggleExpand}
432
+ >
433
+ <ActualSize />
434
+ </HvIconButton>
435
+ <textarea
436
+ id={`sticky-textarea-${id}`}
437
+ value={text || ""}
438
+ onChange={(e) => setText(e.target.value)}
439
+ className={classes.textAreaInput}
440
+ placeholder="Type here..."
441
+ style={{
442
+ color: mergedData.textColor ?? theme.colors.text,
443
+ fontWeight: mergedData.bold ? "bold" : "normal",
444
+ fontStyle: mergedData.italic ? "italic" : "normal",
445
+ fontSize: mergedData.fontSize ?? "14px",
446
+ }}
447
+ onFocus={() => setEditing(true)}
448
+ onBlur={() => {
449
+ setEditing(false);
450
+ setColorsConfigOpen(false);
451
+ }}
452
+ />
453
+ </div>
454
+ </>
455
+ )}
456
+ {!mergedData.expanded && (
457
+ <>
458
+ <HvIconButton
459
+ title={
460
+ <span
461
+ style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}
462
+ >
463
+ {text}
464
+ </span>
465
+ }
466
+ onClick={handleToggleExpand}
467
+ >
468
+ <Fullscreen />
469
+ </HvIconButton>
470
+ </>
471
+ )}
472
+ </div>
473
+ </>
474
+ );
475
+ };
@@ -0,0 +1,202 @@
1
+ import { useState } from "react";
2
+ import useSWR from "swr";
3
+ import {
4
+ HvLoadingContainer,
5
+ HvPagination,
6
+ HvTable,
7
+ HvTableBody,
8
+ HvTableCell,
9
+ HvTableColumnConfig,
10
+ HvTableContainer,
11
+ HvTableHead,
12
+ HvTableHeader,
13
+ HvTableRow,
14
+ HvTableSection,
15
+ HvTypography,
16
+ useHvData,
17
+ useHvPagination,
18
+ useHvSortBy,
19
+ } from "@hitachivantara/uikit-react-core";
20
+
21
+ import { delay } from "./utils";
22
+
23
+ const EmptyRow = () => (
24
+ <HvTableRow>
25
+ <HvTableCell colSpan={100} />
26
+ </HvTableRow>
27
+ );
28
+
29
+ interface Data {
30
+ column1: string;
31
+ column2: string;
32
+ column3: string;
33
+ column4: string;
34
+ column5: string;
35
+ column6: string;
36
+ }
37
+
38
+ const columns: HvTableColumnConfig<Data, string>[] = [
39
+ { Header: "Column 1", accessor: "column1" },
40
+ { Header: "Column 2", accessor: "column2" },
41
+ { Header: "Column 3", accessor: "column3" },
42
+ { Header: "Column 4", accessor: "column4" },
43
+ { Header: "Column 5", accessor: "column5" },
44
+ { Header: "Column 6", accessor: "column6" },
45
+ ];
46
+
47
+ const randomData = [
48
+ "potato",
49
+ "carrot",
50
+ "apple",
51
+ "grape",
52
+ "banana",
53
+ "orange",
54
+ "pineapple",
55
+ "blueberry",
56
+ "tomato",
57
+ "avocado",
58
+ "peach",
59
+ "watermelon",
60
+ "cherry",
61
+ "raspberry",
62
+ ];
63
+
64
+ // Simulating data fetching in backend
65
+ const fetchData = async (
66
+ limit: number,
67
+ ): Promise<{ data: Data[]; total: number; pageCount: number }> => {
68
+ await delay(600);
69
+ const total = 30;
70
+ return {
71
+ data: Array.from({ length: limit }).map(() => {
72
+ const random = Math.floor(Math.random() * randomData.length);
73
+ return {
74
+ column1: randomData[random],
75
+ column2: randomData[random],
76
+ column3: randomData[random],
77
+ column4: randomData[random],
78
+ column5: randomData[random],
79
+ column6: randomData[random],
80
+ };
81
+ }),
82
+ total,
83
+ pageCount: Math.ceil(total / limit),
84
+ };
85
+ };
86
+
87
+ const useServerData = (id: string, page = 0, skip = 0, limit = 5) => {
88
+ return useSWR(
89
+ `/data/${id}?page=${page}&skip=${skip}&limit=${limit}`,
90
+ async () => fetchData(limit),
91
+ );
92
+ };
93
+
94
+ interface DataTableProps {
95
+ id: string;
96
+ }
97
+
98
+ export const DataTable = ({ id }: DataTableProps) => {
99
+ const [pageSize, setPageSize] = useState(5);
100
+ const [page, setPage] = useState(0);
101
+
102
+ const { data, isLoading } = useServerData(
103
+ id,
104
+ page,
105
+ page * pageSize,
106
+ pageSize,
107
+ );
108
+
109
+ const {
110
+ getTableProps,
111
+ getTableBodyProps,
112
+ getHvPaginationProps,
113
+ prepareRow,
114
+ headerGroups,
115
+ page: pageData,
116
+ } = useHvData<Data, string>(
117
+ {
118
+ columns,
119
+ data: data?.data,
120
+ manualPagination: true,
121
+ initialState: { pageSize, pageIndex: page },
122
+ pageCount: data?.pageCount,
123
+ stateReducer: (newState, action) => {
124
+ switch (action.type) {
125
+ // Triggers a data fetch
126
+ case "init":
127
+ case "gotoPage":
128
+ case "setPageSize":
129
+ setPage(newState.pageIndex ?? 0);
130
+ setPageSize(newState.pageSize ?? 5);
131
+ break;
132
+ default:
133
+ break;
134
+ }
135
+ return newState;
136
+ },
137
+ },
138
+ useHvSortBy,
139
+ useHvPagination,
140
+ );
141
+
142
+ const renderTableRow = (i: number) => {
143
+ const row = pageData[i];
144
+
145
+ if (!row) return <EmptyRow key={i} />;
146
+
147
+ prepareRow(row);
148
+
149
+ return (
150
+ <HvTableRow {...row.getRowProps()} key={row.getRowProps().key}>
151
+ {row.cells.map((cell) => (
152
+ <HvTableCell {...cell.getCellProps()} key={cell.getCellProps().key}>
153
+ {cell.render("Cell")}
154
+ </HvTableCell>
155
+ ))}
156
+ </HvTableRow>
157
+ );
158
+ };
159
+
160
+ return (
161
+ <HvTableSection
162
+ raisedHeader
163
+ title={<HvTypography variant="label">Preview Data</HvTypography>}
164
+ >
165
+ <HvLoadingContainer hidden={!isLoading}>
166
+ <HvTableContainer tabIndex={0}>
167
+ <HvTable {...getTableProps()}>
168
+ <HvTableHead>
169
+ {headerGroups.map((headerGroup) => (
170
+ <HvTableRow
171
+ {...headerGroup.getHeaderGroupProps()}
172
+ key={headerGroup.getHeaderGroupProps().key}
173
+ >
174
+ {headerGroup.headers.map((col) => (
175
+ <HvTableHeader
176
+ {...col.getHeaderProps()}
177
+ key={col.getHeaderProps().key}
178
+ >
179
+ {col.render("Header")}
180
+ </HvTableHeader>
181
+ ))}
182
+ </HvTableRow>
183
+ ))}
184
+ </HvTableHead>
185
+ <HvTableBody {...getTableBodyProps()}>
186
+ {pageSize && [...Array(pageSize).keys()].map(renderTableRow)}
187
+ </HvTableBody>
188
+ </HvTable>
189
+ </HvTableContainer>
190
+ </HvLoadingContainer>
191
+ {pageData.length > 0 ? (
192
+ <HvPagination
193
+ {...getHvPaginationProps?.()}
194
+ labels={{
195
+ pageSizePrev: "",
196
+ pageSizeEntryName: `of ${data?.total}`,
197
+ }}
198
+ />
199
+ ) : undefined}
200
+ </HvTableSection>
201
+ );
202
+ };