@react-fabric/data 1.0.0-beta.5 → 1.0.0-beta.51

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 (88) hide show
  1. package/{src/histogram/Histogram.module.css → css/_histogram.css} +16 -13
  2. package/css/styles.css +24 -0
  3. package/dist/cjs/checklist/CheckList.js +25 -16
  4. package/dist/cjs/histogram/Histogram.js +20 -10
  5. package/dist/cjs/hooks/usePagination.js +7 -4
  6. package/dist/cjs/json/JsonViewer.js +40 -27
  7. package/dist/cjs/table/AddColumn.js +1 -1
  8. package/dist/cjs/table/BodyCell.js +38 -5
  9. package/dist/cjs/table/BodyRow.js +107 -0
  10. package/dist/cjs/table/CheckboxCell.js +1 -1
  11. package/dist/cjs/table/ColumnFilters.js +97 -0
  12. package/dist/cjs/table/HeaderCell.js +70 -37
  13. package/dist/cjs/table/Table.js +113 -71
  14. package/dist/cjs/table/useResizer.js +4 -4
  15. package/dist/cjs/table/useVirtualTable.js +122 -18
  16. package/dist/cjs/tree/TreeNode.js +45 -25
  17. package/dist/cjs/tree/TreePanel.js +28 -10
  18. package/dist/cjs/tree/useTree.js +97 -15
  19. package/dist/cjs/tree/utils.js +20 -5
  20. package/dist/cjs/virtual/Gallery.js +51 -35
  21. package/dist/cjs/virtual/List.js +44 -31
  22. package/dist/esm/checklist/CheckList.js +27 -18
  23. package/dist/esm/histogram/Histogram.js +21 -11
  24. package/dist/esm/hooks/usePagination.js +7 -4
  25. package/dist/esm/json/JsonViewer.js +41 -28
  26. package/dist/esm/table/AddColumn.js +1 -1
  27. package/dist/esm/table/BodyCell.js +38 -5
  28. package/dist/esm/table/BodyRow.js +107 -0
  29. package/dist/esm/table/CheckboxCell.js +1 -1
  30. package/dist/esm/table/ColumnFilters.js +97 -0
  31. package/dist/esm/table/HeaderCell.js +71 -38
  32. package/dist/esm/table/Table.js +115 -73
  33. package/dist/esm/table/useResizer.js +4 -4
  34. package/dist/esm/table/useVirtualTable.js +124 -20
  35. package/dist/esm/tree/TreeNode.js +47 -27
  36. package/dist/esm/tree/TreePanel.js +28 -10
  37. package/dist/esm/tree/useTree.js +99 -17
  38. package/dist/esm/tree/utils.js +20 -5
  39. package/dist/esm/virtual/Gallery.js +51 -35
  40. package/dist/esm/virtual/List.js +44 -31
  41. package/dist/types/checklist/CheckList.d.ts +2 -2
  42. package/dist/types/histogram/Histogram.d.ts +3 -4
  43. package/dist/types/index.d.ts +1 -1
  44. package/dist/types/json/JsonViewer.d.ts +0 -1
  45. package/dist/types/table/AddColumn.d.ts +2 -2
  46. package/dist/types/table/BodyCell.d.ts +4 -3
  47. package/dist/types/table/BodyRow.d.ts +20 -0
  48. package/dist/types/table/ColumnFilters.d.ts +9 -0
  49. package/dist/types/table/EmptyCell.d.ts +2 -2
  50. package/dist/types/table/HeaderCell.d.ts +2 -2
  51. package/dist/types/table/Table.d.ts +1 -1
  52. package/dist/types/table/types.d.ts +20 -7
  53. package/dist/types/table/useTableColumns.d.ts +6 -6
  54. package/dist/types/table/useVirtualTable.d.ts +23 -7
  55. package/dist/types/tree/TreeNode.d.ts +1 -1
  56. package/dist/types/tree/TreePanel.d.ts +1 -1
  57. package/dist/types/tree/types.d.ts +25 -5
  58. package/dist/types/tree/useTree.d.ts +2 -9
  59. package/dist/types/tree/utils.d.ts +4 -1
  60. package/dist/types/virtual/Gallery.d.ts +12 -4
  61. package/dist/types/virtual/List.d.ts +8 -4
  62. package/package.json +13 -10
  63. package/src/checklist/CheckList.tsx +81 -77
  64. package/src/histogram/Histogram.tsx +17 -11
  65. package/src/hooks/usePagination.tsx +13 -7
  66. package/src/index.ts +1 -1
  67. package/src/json/JsonViewer.tsx +45 -35
  68. package/src/table/AddColumn.tsx +3 -3
  69. package/src/table/BodyCell.tsx +34 -5
  70. package/src/table/BodyRow.tsx +143 -0
  71. package/src/table/CheckboxCell.tsx +1 -1
  72. package/src/table/ColumnFilters.tsx +105 -0
  73. package/src/table/EmptyCell.tsx +2 -2
  74. package/src/table/HeaderCell.tsx +72 -14
  75. package/src/table/Table.tsx +164 -77
  76. package/src/table/types.ts +24 -6
  77. package/src/table/useResizer.ts +5 -3
  78. package/src/table/useTableColumns.ts +7 -7
  79. package/src/table/useVirtualTable.ts +146 -24
  80. package/src/tree/TreeNode.tsx +98 -56
  81. package/src/tree/TreePanel.tsx +23 -3
  82. package/src/tree/types.ts +32 -4
  83. package/src/tree/useTree.ts +109 -26
  84. package/src/tree/utils.ts +23 -8
  85. package/src/virtual/Gallery.tsx +63 -42
  86. package/src/virtual/List.tsx +44 -32
  87. package/dist/cjs/histogram/Histogram.module.css +0 -1
  88. package/dist/esm/histogram/Histogram.module.css +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-fabric/data",
3
- "version": "1.0.0-beta.5",
3
+ "version": "1.0.0-beta.51",
4
4
  "description": "RUF: Data package",
5
5
  "author": "Adarsh Pastakia <adarshpastakia@outlook.com>",
6
6
  "license": "MIT",
@@ -11,9 +11,11 @@
11
11
  "types": "dist/types/index.d.ts",
12
12
  "directories": {
13
13
  "src": "src",
14
+ "styles": "css",
14
15
  "test": "tests"
15
16
  },
16
17
  "files": [
18
+ "css",
17
19
  "src",
18
20
  "dist"
19
21
  ],
@@ -33,23 +35,24 @@
33
35
  "watch": "npm-run-all -p watch:rollup watch:dts"
34
36
  },
35
37
  "dependencies": {
36
- "@react-fabric/core": "^1.0.0-beta.5",
37
- "@react-fabric/form": "^1.0.0-beta.5",
38
- "@react-fabric/utilities": "^1.0.0-beta.5",
39
- "@tanstack/react-virtual": "^3.8.6",
38
+ "@react-fabric/core": "^1.0.0-beta.51",
39
+ "@react-fabric/date": "^1.0.0-beta.51",
40
+ "@react-fabric/form": "^1.0.0-beta.51",
41
+ "@react-fabric/utilities": "^1.0.0-beta.50",
42
+ "@tanstack/react-virtual": "^3.13.9",
40
43
  "classnames": "^2.5.1",
41
44
  "color": "^4.2.3",
42
45
  "memoize-one": "^6.0.0"
43
46
  },
44
47
  "peerDependencies": {
45
- "i18next": "^23.15.1",
46
- "react": ">=18.0.0 <20.0.0",
47
- "react-dom": ">=18.0.0 <20.0.0",
48
- "react-i18next": "^15.0.2"
48
+ "i18next": "^25.2.1",
49
+ "react": "^19.0.0",
50
+ "react-dom": "^19.0.0",
51
+ "react-i18next": "^15.5.2"
49
52
  },
50
53
  "engines": {
51
54
  "node": ">=18.12.0",
52
55
  "npm": ">=8.19.0"
53
56
  },
54
- "gitHead": "7d4a7ed1ea9890652826bef58d01c31280d761c8"
57
+ "gitHead": "a1b04de4663045f82992bff2aef8db26608eac74"
55
58
  }
@@ -22,12 +22,14 @@
22
22
  */
23
23
 
24
24
  import { Badge, CoreIcons, EmptyContent, Icon } from "@react-fabric/core";
25
- import { type BadgeType } from "@react-fabric/core/dist/types/types";
25
+ import {
26
+ type BadgeType,
27
+ type TestProps,
28
+ } from "@react-fabric/core/dist/types/types";
26
29
  import { isString } from "@react-fabric/utilities";
27
30
  import {
28
31
  Fragment,
29
32
  isValidElement,
30
- memo,
31
33
  useEffect,
32
34
  useMemo,
33
35
  useState,
@@ -47,7 +49,7 @@ export interface CheckListItem extends KeyValue {
47
49
  isDisabled?: boolean;
48
50
  }
49
51
 
50
- export interface CheckListProps extends SelectableProps {
52
+ export interface CheckListProps extends SelectableProps, TestProps {
51
53
  /**
52
54
  * list items
53
55
  */
@@ -73,84 +75,79 @@ const CheckItem: FC<
73
75
  selected?: number;
74
76
  allowNegative?: boolean;
75
77
  }
76
- > = memo(
77
- ({
78
- id,
79
- allowNegative,
80
- selected,
81
- icon,
82
- label,
83
- isDisabled,
84
- onClick,
85
- badge,
86
- }: Omit<CheckListItem, "selected"> & {
87
- selected?: number;
88
- allowNegative?: boolean;
89
- }) => {
90
- return (
91
- <div
92
- role="none"
93
- className="ax-checkList__item"
94
- data-disabled={isDisabled}
95
- onClick={() => onClick(id, false)}
96
- onContextMenu={(e) => [
97
- allowNegative && onClick(id, true),
98
- e.preventDefault(),
99
- ]}
100
- >
101
- {!allowNegative && (
78
+ > = ({
79
+ id,
80
+ allowNegative,
81
+ selected,
82
+ icon,
83
+ label,
84
+ isDisabled,
85
+ onClick,
86
+ badge,
87
+ ...aria
88
+ }: Omit<CheckListItem, "selected"> & {
89
+ selected?: number;
90
+ allowNegative?: boolean;
91
+ }) => {
92
+ return (
93
+ <div
94
+ role="none"
95
+ className="ax-checkList__item"
96
+ data-disabled={isDisabled}
97
+ onClick={() => onClick(id, false)}
98
+ onContextMenu={(e) => [
99
+ allowNegative && onClick(id, true),
100
+ e.preventDefault(),
101
+ ]}
102
+ {...aria}
103
+ >
104
+ {!allowNegative && (
105
+ <Icon
106
+ data-type="checkbox"
107
+ className="ax-checkList__checkbox"
108
+ data-selected={selected === 1}
109
+ icon={selected ? CoreIcons.checkboxInt : CoreIcons.checkboxOff}
110
+ />
111
+ )}
112
+ {allowNegative && (
113
+ <Fragment>
102
114
  <Icon
103
- data-type="checkbox"
115
+ data-type="multiple"
104
116
  className="ax-checkList__checkbox"
105
- data-selected={selected === 1}
106
- icon={selected ? CoreIcons.checkboxInt : CoreIcons.checkboxOff}
117
+ icon={
118
+ selected === 1
119
+ ? CoreIcons.expand
120
+ : selected === -1
121
+ ? CoreIcons.collapse
122
+ : CoreIcons.checkboxOff
123
+ }
124
+ data-selected={selected}
107
125
  />
108
- )}
109
- {allowNegative && (
110
- <Fragment>
126
+ <div className="ax-checkList__checkbox">
127
+ <Icon
128
+ data-type="positive"
129
+ onClick={(e) => [(onClick(id, false), e.stopPropagation())]}
130
+ icon={selected === 1 ? CoreIcons.expandActive : CoreIcons.expand}
131
+ data-selected={selected === 1}
132
+ />
111
133
  <Icon
112
- data-type="multiple"
113
- className="ax-checkList__checkbox"
134
+ data-type="negative"
135
+ onClick={(e) => [(onClick(id, true), e.stopPropagation())]}
114
136
  icon={
115
- selected === 1
116
- ? CoreIcons.expand
117
- : selected === -1
118
- ? CoreIcons.collapse
119
- : CoreIcons.checkboxOff
137
+ selected === -1 ? CoreIcons.collapseActive : CoreIcons.collapse
120
138
  }
121
- data-selected={selected}
139
+ data-selected={selected === -1}
122
140
  />
123
- <div className="ax-checkList__checkbox">
124
- <Icon
125
- data-type="positive"
126
- onClick={(e) => [(onClick(id, false), e.stopPropagation())]}
127
- icon={
128
- selected === 1 ? CoreIcons.expandActive : CoreIcons.expand
129
- }
130
- data-selected={selected === 1}
131
- />
132
- <Icon
133
- data-type="negative"
134
- onClick={(e) => [(onClick(id, true), e.stopPropagation())]}
135
- icon={
136
- selected === -1
137
- ? CoreIcons.collapseActive
138
- : CoreIcons.collapse
139
- }
140
- data-selected={selected === -1}
141
- />
142
- </div>
143
- </Fragment>
144
- )}
145
- {isValidElement(icon) && icon}
146
- {isString(icon) && <Icon icon={icon} />}
147
- <label>{label}</label>
148
- <Badge {...badge} />
149
- </div>
150
- );
151
- },
152
- );
153
- CheckItem.displayName = "CheckItem";
141
+ </div>
142
+ </Fragment>
143
+ )}
144
+ {isValidElement(icon) && icon}
145
+ {isString(icon) && <Icon icon={icon} />}
146
+ <label>{label}</label>
147
+ <Badge {...badge} />
148
+ </div>
149
+ );
150
+ };
154
151
 
155
152
  export const CheckList: FC<CheckListProps> = ({
156
153
  items = [],
@@ -158,12 +155,19 @@ export const CheckList: FC<CheckListProps> = ({
158
155
  allowNegative,
159
156
  emptyMessage,
160
157
  sortItems = true,
161
- ...props
158
+ selected,
159
+ nonselected,
160
+ onChange,
161
+ onClick,
162
+ ...aria
162
163
  }: CheckListProps) => {
163
164
  const { t } = useTranslation("data");
164
165
  const { selection, toggleSelection } = useSelectableList({
165
166
  items,
166
- ...props,
167
+ selected,
168
+ nonselected,
169
+ onChange,
170
+ onClick,
167
171
  });
168
172
  const [showMore, setShowMore] = useState(false);
169
173
 
@@ -186,7 +190,7 @@ export const CheckList: FC<CheckListProps> = ({
186
190
  }, [items, selection, maxCount, showMore, sortItems]);
187
191
 
188
192
  return (
189
- <div className="ax-checkList">
193
+ <div {...aria}>
190
194
  {listItems.map((item, index) => (
191
195
  <CheckItem
192
196
  {...item}
@@ -31,12 +31,12 @@ import {
31
31
  import {
32
32
  type ColorType,
33
33
  type PaletteType,
34
+ type TestProps,
34
35
  } from "@react-fabric/core/dist/types/types";
35
- import { compareValues, Format } from "@react-fabric/utilities";
36
+ import { compareValues, Format, isColor } from "@react-fabric/utilities";
36
37
  import classNames from "classnames";
37
38
  import { useMemo } from "react";
38
39
  import { useTranslation } from "react-i18next";
39
- import classes from "./Histogram.module.css";
40
40
 
41
41
  const emptyIcon =
42
42
  "M20.84 22.73L11.11 13H3V11H9.11L6.11 8H3V6H4.11L1.11 3L2.39 1.73L22.11 21.46L20.84 22.73M15 11H14.2L15 11.8V11M21 8V6H9.2L11.2 8H21M3 18H9V16H3V18Z";
@@ -49,7 +49,7 @@ interface HistogramItem {
49
49
  disabled?: boolean;
50
50
  }
51
51
 
52
- export interface HistogramProps {
52
+ export interface HistogramProps extends TestProps {
53
53
  /**
54
54
  * histogram items list
55
55
  */
@@ -83,11 +83,12 @@ export interface HistogramProps {
83
83
  export const Histogram = ({
84
84
  items = [],
85
85
  total,
86
- color = "primary",
86
+ color = "primary-500",
87
87
  sortBy = "count",
88
88
  emptyTitle,
89
89
  emptyMessage,
90
90
  onClick,
91
+ ...aria
91
92
  }: HistogramProps) => {
92
93
  const { t } = useTranslation("data");
93
94
  const maxTotal = useMemo(() => {
@@ -103,10 +104,11 @@ export const Histogram = ({
103
104
 
104
105
  return (
105
106
  <div
106
- className={classes.histogram}
107
+ className={"fabric-histogram from-0% to-100%"}
108
+ {...aria}
107
109
  style={
108
110
  {
109
- "--color": `var(--${color})`,
111
+ "--color": isColor(color) ? color : `var(--color-${color})`,
110
112
  } as AnyObject
111
113
  }
112
114
  >
@@ -124,15 +126,17 @@ export const Histogram = ({
124
126
  data-disabled={item.count === 0 || item.disabled}
125
127
  className={classNames(
126
128
  "relative cursor-pointer flex flex-nowrap items-center select-none overflow-hidden min-h-6 my-px",
127
- classes.histoLabel,
128
- (item.count === 0 || item.disabled) && "pointer-events-none",
129
+ "fabric-histoLabel",
130
+ ((item.total ?? item.count) === 0 || item.disabled) &&
131
+ "pointer-events-none",
129
132
  )}
133
+ data-testid={item.id}
130
134
  onClick={(e) => [onClick?.(item.id), e.stopPropagation()]}
131
135
  >
132
136
  <div
133
137
  className={classNames(
134
- "absolute z-0 inset-0 opacity-50 min-w-2 ltr:bg-gradient-to-r rtl:bg-gradient-to-l from-0% to-100%",
135
- classes.histoBar,
138
+ "absolute z-0 inset-0 opacity-50 min-w-2 ltr:bg-gradient-to-r rtl:bg-gradient-to-l",
139
+ "fabric-histoBar",
136
140
  )}
137
141
  style={{
138
142
  width: `${Math.min(item.count / maxTotal, 1) * 100}%`,
@@ -145,6 +149,7 @@ export const Histogram = ({
145
149
  key="negative"
146
150
  color="danger"
147
151
  size="sm"
152
+ stopPropagation
148
153
  icon={CoreIcons.funnelMinus}
149
154
  aria-label="Filter Negative"
150
155
  onClick={() => onClick?.(item.id, true)}
@@ -153,6 +158,7 @@ export const Histogram = ({
153
158
  key="positive"
154
159
  color="primary"
155
160
  size="sm"
161
+ stopPropagation
156
162
  icon={CoreIcons.funnelPlus}
157
163
  aria-label="Filter"
158
164
  onClick={() => onClick?.(item.id)}
@@ -164,7 +170,7 @@ export const Histogram = ({
164
170
  <span
165
171
  className={classNames(
166
172
  "z-1 rounded-full text-xs font-medium px-2 py-1 leading-none",
167
- classes.histoBadge,
173
+ "fabric-histoBadge",
168
174
  )}
169
175
  >
170
176
  {Format.number(item.count)}
@@ -111,9 +111,12 @@ export const usePagination = ({
111
111
 
112
112
  /** ***************** set page *******************/
113
113
  useEffect(() => {
114
- setPage(Math.min(currentPage, totalPages - 1));
115
- onChange?.(Math.min(currentPage, totalPages - 1));
116
- }, [currentPage, totalPages]);
114
+ if (totalRecords > -1) {
115
+ const actual = Math.min(currentPage, totalPages - 1);
116
+ setPage(actual);
117
+ actual !== currentPage && onChange?.(actual);
118
+ }
119
+ }, [currentPage, totalRecords, totalPages]);
117
120
 
118
121
  const headLabel = useMemo(
119
122
  () => (
@@ -126,10 +129,13 @@ export const usePagination = ({
126
129
  [page, perPage, totalRecords],
127
130
  );
128
131
 
129
- const onPageChange = useCallback((page: number) => {
130
- setPage(page);
131
- startTransition(() => onChange?.(page));
132
- }, []);
132
+ const onPageChange = useCallback(
133
+ (page: number) => {
134
+ setPage(page);
135
+ startTransition(() => onChange?.(page));
136
+ },
137
+ [onChange],
138
+ );
133
139
 
134
140
  return { page, totalPages, headLabel, ranges, onPageChange };
135
141
  };
package/src/index.ts CHANGED
@@ -28,7 +28,7 @@ export { Histogram } from "./histogram/Histogram";
28
28
  export { JsonViewer } from "./json/JsonViewer";
29
29
  export { Pagination } from "./pagination/Pagination";
30
30
  export { Table } from "./table/Table";
31
- export { type TableRef } from "./table/types";
31
+ export { type TableColumn, type TableRef } from "./table/types";
32
32
  export { TreePanel } from "./tree/TreePanel";
33
33
  export { type TreeNodeType as TreeNode } from "./tree/types";
34
34
  export { VirtualGallery, type VirtualGalleryRef } from "./virtual/Gallery";
@@ -22,7 +22,6 @@
22
22
  */
23
23
 
24
24
  import {
25
- ActionLabel,
26
25
  Button,
27
26
  Copy,
28
27
  CoreIcons,
@@ -181,6 +180,8 @@ const JsonEntry = ({
181
180
  ret = actualValue ? t("json.true") : t("json.false");
182
181
  if (ret?.length > 128) {
183
182
  ret = <Text clamp={3}>{ret}</Text>;
183
+ } else {
184
+ ret = <div className="break-words">{ret}</div>;
184
185
  }
185
186
 
186
187
  return (
@@ -216,37 +217,36 @@ const JsonEntry = ({
216
217
 
217
218
  const labelDisplay = useMemo(
218
219
  () => (
219
- <ActionLabel
220
- className="text-dimmed text-sm px-2 flex-1 truncate"
221
- actions={
222
- !canExpand && canFilter
223
- ? [
224
- <Button
225
- key="negative"
226
- color="danger"
227
- size="sm"
228
- variant="link"
229
- icon={CoreIcons.funnelMinus}
230
- aria-label="Filter Negative"
231
- onClick={() =>
232
- onFilter?.(pathWithoutOrdinal, actualValue, true)
233
- }
234
- />,
235
- <Button
236
- key="positive"
237
- color="primary"
238
- size="sm"
239
- variant="link"
240
- icon={CoreIcons.funnelPlus}
241
- aria-label="Filter"
242
- onClick={() => onFilter?.(pathWithoutOrdinal, actualValue)}
243
- />,
244
- ]
245
- : []
246
- }
220
+ <div
221
+ className={classNames(
222
+ "text-dimmed text-sm px-2 flex-1 group",
223
+ inline ? "truncate" : "break-words",
224
+ )}
247
225
  >
226
+ {!canExpand && canFilter && (
227
+ <div className="hidden pe-1 group-hover:inline-block">
228
+ <Button
229
+ key="negative"
230
+ color="danger"
231
+ size="xs"
232
+ variant="link"
233
+ icon={CoreIcons.funnelMinus}
234
+ aria-label="Filter Negative"
235
+ onClick={() => onFilter?.(pathWithoutOrdinal, actualValue, true)}
236
+ />
237
+ <Button
238
+ key="positive"
239
+ color="primary"
240
+ size="xs"
241
+ variant="link"
242
+ icon={CoreIcons.funnelPlus}
243
+ aria-label="Filter"
244
+ onClick={() => onFilter?.(pathWithoutOrdinal, actualValue)}
245
+ />
246
+ </div>
247
+ )}
248
248
  {labeler?.(path) ?? field}
249
- </ActionLabel>
249
+ </div>
250
250
  ),
251
251
  [field, labeler, pathWithoutOrdinal, actualValue, canExpand, canFilter],
252
252
  );
@@ -283,9 +283,14 @@ const JsonEntry = ({
283
283
  {labelDisplay}
284
284
  </div>
285
285
  {display && (
286
- <div className={classNames("px-2", inline && "flex-1")}>
286
+ <div
287
+ className={classNames(
288
+ "px-2 flex flex-nowrap overflow-hidden",
289
+ inline && "flex-1",
290
+ )}
291
+ >
287
292
  {canCopy && <Copy size="xs" text={actualValue} />}
288
- {display}
293
+ <div className="flex-1">{display}</div>
289
294
  </div>
290
295
  )}
291
296
  </div>
@@ -309,9 +314,14 @@ const JsonEntry = ({
309
314
  >
310
315
  {labelDisplay}
311
316
  </div>
312
- <div className={classNames("px-2", inline && "flex-1")}>
317
+ <div
318
+ className={classNames(
319
+ "px-2 flex flex-nowrap overflow-hidden",
320
+ inline && "flex-1",
321
+ )}
322
+ >
313
323
  {canCopy && <Copy size="xs" text={value} />}
314
- {display}
324
+ <div className="flex-1">{display}</div>
315
325
  </div>
316
326
  </div>
317
327
  )}
@@ -356,7 +366,7 @@ export const JsonViewer = <T extends KeyValue = KeyValue>({
356
366
  const list = useMemo(() => reduceObject(json), [json]);
357
367
 
358
368
  return (
359
- <div {...rest} className={classNames(className, "p-4 bg-alternate")}>
369
+ <div {...rest} className={classNames(className, "bg-alternate")}>
360
370
  {!isEmpty(json) ? (
361
371
  list.map((item) => (
362
372
  <JsonEntry
@@ -22,17 +22,17 @@
22
22
  */
23
23
 
24
24
  import { CoreIcons, Dropdown, Icon, Menu, MenuItem } from "@react-fabric/core";
25
- import { type ColumnType } from "./types";
25
+ import { type TableColumn } from "./types";
26
26
 
27
27
  export const AddColumn = ({
28
28
  columns,
29
29
  onClick,
30
30
  }: {
31
- columns: ColumnType[];
31
+ columns: TableColumn[];
32
32
  onClick: (id: string) => void;
33
33
  }) => {
34
34
  return (
35
- <div className="group font-medium w-6 flex flex-nowrap text-start items-end">
35
+ <div className="group font-medium w-6 flex flex-nowrap text-start items-center">
36
36
  <Dropdown placement="bottom-end">
37
37
  <Icon
38
38
  icon={CoreIcons.insert}
@@ -21,6 +21,8 @@
21
21
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  */
23
23
 
24
+ import { DateDisplay } from "@react-fabric/date";
25
+ import { Format, getByPath, isTrue } from "@react-fabric/utilities";
24
26
  import classNames from "classnames";
25
27
  import { useMemo } from "react";
26
28
  import { useTableContext } from "./Context";
@@ -28,15 +30,17 @@ import {
28
30
  COL_DEFAULT_WIDTH,
29
31
  COL_MAX_WIDTH,
30
32
  COL_MIN_WIDTH,
31
- type ColumnType,
33
+ type TableColumn,
32
34
  } from "./types";
33
35
 
34
36
  export const BodyCell = ({
35
37
  column,
36
38
  item,
39
+ index,
37
40
  }: {
38
- column: ColumnType;
41
+ column: TableColumn;
39
42
  item: KeyValue;
43
+ index: number;
40
44
  }) => {
41
45
  const { widths } = useTableContext();
42
46
 
@@ -45,9 +49,32 @@ export const BodyCell = ({
45
49
  [widths, column],
46
50
  );
47
51
 
52
+ const content = useMemo(() => {
53
+ const value = getByPath(item, column.id.toString());
54
+ if (column.renderer != null) {
55
+ return column.renderer(value, item, index);
56
+ }
57
+ if (column.dataType === "boolean") {
58
+ const map = column.valueMap ?? { true: "Yes", false: "no" };
59
+ return map[`${isTrue(value) ? "true" : "false"}`];
60
+ }
61
+ if (column.valueMap != null) {
62
+ if (column.dataType === "string" && value in column.valueMap) {
63
+ return column.valueMap[value];
64
+ }
65
+ }
66
+ if (["date", "time", "datetime"].includes(column.dataType ?? "")) {
67
+ return <DateDisplay date={value} format={column.format} />;
68
+ }
69
+ if (column.dataType === "number") {
70
+ return Format.number(value, column.format);
71
+ }
72
+ return value ?? "";
73
+ }, [item, column]);
74
+
48
75
  return (
49
76
  <div
50
- className="overflow-hidden bg-inherit start-0"
77
+ className="overflow-hidden bg-inherit start-0 border-e group/table-col"
51
78
  style={{
52
79
  width: width ?? COL_DEFAULT_WIDTH,
53
80
  minWidth: column.minWidth ?? COL_MIN_WIDTH,
@@ -56,12 +83,14 @@ export const BodyCell = ({
56
83
  >
57
84
  <div
58
85
  className={classNames(
59
- "px-2 py-1 border-e truncate",
86
+ "px-2 py-1",
87
+ column.className,
88
+ column.truncate && "truncate",
60
89
  column.align === "center" && "text-center",
61
90
  column.align === "end" && "text-end",
62
91
  )}
63
92
  >
64
- {column.renderer?.(item[column.id], item) ?? item[column.id]}
93
+ {content}
65
94
  </div>
66
95
  </div>
67
96
  );