@react-fabric/data 1.0.0-beta.5 → 1.0.0-beta.52
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/{src/histogram/Histogram.module.css → css/_histogram.css} +16 -13
- package/css/styles.css +24 -0
- package/dist/cjs/checklist/CheckList.js +48 -16
- package/dist/cjs/histogram/Histogram.js +48 -10
- package/dist/cjs/hooks/useFilteredList.js +20 -0
- package/dist/cjs/hooks/usePagination.js +27 -4
- package/dist/cjs/hooks/useSelectableList.js +29 -0
- package/dist/cjs/i18n/ar.json.js +8 -1
- package/dist/cjs/i18n/en.json.js +8 -1
- package/dist/cjs/json/JsonViewer.js +67 -27
- package/dist/cjs/pagination/Pagination.js +10 -0
- package/dist/cjs/table/AddColumn.js +1 -1
- package/dist/cjs/table/BodyCell.js +38 -5
- package/dist/cjs/table/BodyRow.js +107 -0
- package/dist/cjs/table/CheckboxCell.js +1 -1
- package/dist/cjs/table/ColumnFilters.js +97 -0
- package/dist/cjs/table/HeaderCell.js +70 -37
- package/dist/cjs/table/Table.js +113 -71
- package/dist/cjs/table/types.js +29 -0
- package/dist/cjs/table/useResizer.js +4 -4
- package/dist/cjs/table/useVirtualTable.js +122 -18
- package/dist/cjs/tree/TreeNode.js +45 -25
- package/dist/cjs/tree/TreePanel.js +28 -10
- package/dist/cjs/tree/useTree.js +97 -15
- package/dist/cjs/tree/utils.js +20 -5
- package/dist/cjs/virtual/Gallery.js +84 -35
- package/dist/cjs/virtual/List.js +82 -32
- package/dist/esm/checklist/CheckList.js +50 -18
- package/dist/esm/histogram/Histogram.js +49 -11
- package/dist/esm/hooks/useFilteredList.js +20 -0
- package/dist/esm/hooks/usePagination.js +27 -4
- package/dist/esm/hooks/useSelectableList.js +29 -0
- package/dist/esm/i18n/ar.json.js +8 -2
- package/dist/esm/i18n/en.json.js +8 -2
- package/dist/esm/json/JsonViewer.js +68 -28
- package/dist/esm/pagination/Pagination.js +10 -0
- package/dist/esm/table/AddColumn.js +1 -1
- package/dist/esm/table/BodyCell.js +38 -5
- package/dist/esm/table/BodyRow.js +107 -0
- package/dist/esm/table/CheckboxCell.js +1 -1
- package/dist/esm/table/ColumnFilters.js +97 -0
- package/dist/esm/table/HeaderCell.js +71 -38
- package/dist/esm/table/Table.js +115 -73
- package/dist/esm/table/types.js +29 -0
- package/dist/esm/table/useResizer.js +4 -4
- package/dist/esm/table/useVirtualTable.js +124 -20
- package/dist/esm/tree/TreeNode.js +47 -27
- package/dist/esm/tree/TreePanel.js +28 -10
- package/dist/esm/tree/useTree.js +99 -17
- package/dist/esm/tree/utils.js +20 -5
- package/dist/esm/virtual/Gallery.js +84 -35
- package/dist/esm/virtual/List.js +82 -32
- package/dist/types/checklist/CheckList.d.ts +24 -2
- package/dist/types/histogram/Histogram.d.ts +31 -4
- package/dist/types/hooks/useFilteredList.d.ts +20 -2
- package/dist/types/hooks/usePagination.d.ts +19 -0
- package/dist/types/hooks/useSelectableList.d.ts +29 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/json/JsonViewer.d.ts +27 -2
- package/dist/types/pagination/Pagination.d.ts +10 -1
- package/dist/types/table/AddColumn.d.ts +2 -2
- package/dist/types/table/BodyCell.d.ts +4 -3
- package/dist/types/table/BodyRow.d.ts +20 -0
- package/dist/types/table/ColumnFilters.d.ts +9 -0
- package/dist/types/table/EmptyCell.d.ts +2 -2
- package/dist/types/table/HeaderCell.d.ts +2 -2
- package/dist/types/table/Table.d.ts +14 -1
- package/dist/types/table/types.d.ts +48 -7
- package/dist/types/table/useTableColumns.d.ts +6 -6
- package/dist/types/table/useVirtualTable.d.ts +23 -8
- package/dist/types/tree/TreeNode.d.ts +1 -1
- package/dist/types/tree/TreePanel.d.ts +42 -1
- package/dist/types/tree/types.d.ts +25 -5
- package/dist/types/tree/useTree.d.ts +3 -10
- package/dist/types/tree/utils.d.ts +4 -1
- package/dist/types/virtual/Gallery.d.ts +44 -4
- package/dist/types/virtual/List.d.ts +44 -4
- package/package.json +13 -10
- package/src/checklist/CheckList.tsx +103 -77
- package/src/histogram/Histogram.tsx +45 -11
- package/src/hooks/useFilteredList.tsx +19 -0
- package/src/hooks/usePagination.tsx +32 -7
- package/src/hooks/useSelectableList.ts +29 -0
- package/src/i18n/ar.json +5 -0
- package/src/i18n/en.json +5 -0
- package/src/index.ts +1 -1
- package/src/json/JsonViewer.tsx +71 -35
- package/src/pagination/Pagination.tsx +10 -0
- package/src/table/AddColumn.tsx +3 -3
- package/src/table/BodyCell.tsx +34 -5
- package/src/table/BodyRow.tsx +143 -0
- package/src/table/CheckboxCell.tsx +1 -1
- package/src/table/ColumnFilters.tsx +105 -0
- package/src/table/EmptyCell.tsx +2 -2
- package/src/table/HeaderCell.tsx +72 -14
- package/src/table/Table.tsx +177 -77
- package/src/table/types.ts +52 -6
- package/src/table/useResizer.ts +5 -3
- package/src/table/useTableColumns.ts +7 -7
- package/src/table/useVirtualTable.ts +146 -24
- package/src/tree/TreeNode.tsx +98 -56
- package/src/tree/TreePanel.tsx +64 -3
- package/src/tree/types.ts +32 -4
- package/src/tree/useTree.ts +109 -26
- package/src/tree/utils.ts +23 -8
- package/src/virtual/Gallery.tsx +96 -42
- package/src/virtual/List.tsx +82 -33
- package/dist/cjs/histogram/Histogram.module.css +0 -1
- package/dist/esm/histogram/Histogram.module.css +0 -1
|
@@ -22,12 +22,14 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import { Badge, CoreIcons, EmptyContent, Icon } from "@react-fabric/core";
|
|
25
|
-
import {
|
|
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,97 +75,121 @@ const CheckItem: FC<
|
|
|
73
75
|
selected?: number;
|
|
74
76
|
allowNegative?: boolean;
|
|
75
77
|
}
|
|
76
|
-
> =
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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="
|
|
115
|
+
data-type="multiple"
|
|
104
116
|
className="ax-checkList__checkbox"
|
|
105
|
-
|
|
106
|
-
|
|
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">
|
|
111
127
|
<Icon
|
|
112
|
-
data-type="
|
|
113
|
-
|
|
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
|
+
/>
|
|
133
|
+
<Icon
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
|
|
152
|
+
/**
|
|
153
|
+
* CheckList component to display a list of selectable items with checkboxes.
|
|
154
|
+
* It supports selection, deselection, and allows for negative selections.
|
|
155
|
+
*
|
|
156
|
+
* @param {CheckListProps} props - The properties for the CheckList component.
|
|
157
|
+
* @returns {JSX.Element} The rendered CheckList component.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```jsx
|
|
161
|
+
* <CheckList
|
|
162
|
+
* items={[
|
|
163
|
+
* { id: "1", label: "Item 1", icon: "icon1" },
|
|
164
|
+
* { id: "2", label: "Item 2", icon: "icon2", badge: { text: "New" } },
|
|
165
|
+
* ]}
|
|
166
|
+
* maxCount={5}
|
|
167
|
+
* allowNegative={true}
|
|
168
|
+
* emptyMessage="No items available"
|
|
169
|
+
* onChange={(selectedItems) => console.log(selectedItems)}
|
|
170
|
+
* onClick={(itemId, isNegative) => console.log(itemId, isNegative)}
|
|
171
|
+
* />
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
155
174
|
export const CheckList: FC<CheckListProps> = ({
|
|
156
175
|
items = [],
|
|
157
176
|
maxCount = 0,
|
|
158
177
|
allowNegative,
|
|
159
178
|
emptyMessage,
|
|
160
179
|
sortItems = true,
|
|
161
|
-
|
|
180
|
+
selected,
|
|
181
|
+
nonselected,
|
|
182
|
+
onChange,
|
|
183
|
+
onClick,
|
|
184
|
+
...aria
|
|
162
185
|
}: CheckListProps) => {
|
|
163
186
|
const { t } = useTranslation("data");
|
|
164
187
|
const { selection, toggleSelection } = useSelectableList({
|
|
165
188
|
items,
|
|
166
|
-
|
|
189
|
+
selected,
|
|
190
|
+
nonselected,
|
|
191
|
+
onChange,
|
|
192
|
+
onClick,
|
|
167
193
|
});
|
|
168
194
|
const [showMore, setShowMore] = useState(false);
|
|
169
195
|
|
|
@@ -186,7 +212,7 @@ export const CheckList: FC<CheckListProps> = ({
|
|
|
186
212
|
}, [items, selection, maxCount, showMore, sortItems]);
|
|
187
213
|
|
|
188
214
|
return (
|
|
189
|
-
<div
|
|
215
|
+
<div {...aria}>
|
|
190
216
|
{listItems.map((item, index) => (
|
|
191
217
|
<CheckItem
|
|
192
218
|
{...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
|
*/
|
|
@@ -80,14 +80,43 @@ export interface HistogramProps {
|
|
|
80
80
|
onClick?: (id: string, negative?: true) => void;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Histogram component displays a bar chart of items with counts.
|
|
85
|
+
* It allows users to visualize the distribution of counts across different items.
|
|
86
|
+
* The component supports sorting, custom colors, and click handling for each item.
|
|
87
|
+
* It also provides an empty state when there are no items to display.
|
|
88
|
+
*
|
|
89
|
+
* @param {HistogramProps} props - The properties for the Histogram component.
|
|
90
|
+
* @returns {JSX.Element} - The rendered Histogram component.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```jsx
|
|
94
|
+
* <Histogram
|
|
95
|
+
* items={[
|
|
96
|
+
* { id: "1", label: "Item 1", count: 10 },
|
|
97
|
+
* { id: "2", label: "Item 2", count: 20 },
|
|
98
|
+
* { id: "3", label: "Item 3", count: 5, disabled: true },
|
|
99
|
+
* ]}
|
|
100
|
+
* total={30}
|
|
101
|
+
* color="primary-500"
|
|
102
|
+
* sortBy="count"
|
|
103
|
+
* emptyTitle="No Data Available"
|
|
104
|
+
* emptyMessage="Please check back later."
|
|
105
|
+
* onClick={(id, negative) => console.log(`Clicked on ${id}`, negative ? "Negative" : "Positive")}
|
|
106
|
+
* />
|
|
107
|
+
* ```
|
|
108
|
+
*
|
|
109
|
+
* @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/data-histogram--docs}
|
|
110
|
+
*/
|
|
83
111
|
export const Histogram = ({
|
|
84
112
|
items = [],
|
|
85
113
|
total,
|
|
86
|
-
color = "primary",
|
|
114
|
+
color = "primary-500",
|
|
87
115
|
sortBy = "count",
|
|
88
116
|
emptyTitle,
|
|
89
117
|
emptyMessage,
|
|
90
118
|
onClick,
|
|
119
|
+
...aria
|
|
91
120
|
}: HistogramProps) => {
|
|
92
121
|
const { t } = useTranslation("data");
|
|
93
122
|
const maxTotal = useMemo(() => {
|
|
@@ -103,10 +132,11 @@ export const Histogram = ({
|
|
|
103
132
|
|
|
104
133
|
return (
|
|
105
134
|
<div
|
|
106
|
-
className={
|
|
135
|
+
className={"fabric-histogram from-0% to-100%"}
|
|
136
|
+
{...aria}
|
|
107
137
|
style={
|
|
108
138
|
{
|
|
109
|
-
"--color": `var(
|
|
139
|
+
"--color": isColor(color) ? color : `var(--color-${color})`,
|
|
110
140
|
} as AnyObject
|
|
111
141
|
}
|
|
112
142
|
>
|
|
@@ -124,15 +154,17 @@ export const Histogram = ({
|
|
|
124
154
|
data-disabled={item.count === 0 || item.disabled}
|
|
125
155
|
className={classNames(
|
|
126
156
|
"relative cursor-pointer flex flex-nowrap items-center select-none overflow-hidden min-h-6 my-px",
|
|
127
|
-
|
|
128
|
-
(item.count === 0 || item.disabled) &&
|
|
157
|
+
"fabric-histoLabel",
|
|
158
|
+
((item.total ?? item.count) === 0 || item.disabled) &&
|
|
159
|
+
"pointer-events-none",
|
|
129
160
|
)}
|
|
161
|
+
data-testid={item.id}
|
|
130
162
|
onClick={(e) => [onClick?.(item.id), e.stopPropagation()]}
|
|
131
163
|
>
|
|
132
164
|
<div
|
|
133
165
|
className={classNames(
|
|
134
|
-
"absolute z-0 inset-0 opacity-50 min-w-2 ltr:bg-gradient-to-r rtl:bg-gradient-to-l
|
|
135
|
-
|
|
166
|
+
"absolute z-0 inset-0 opacity-50 min-w-2 ltr:bg-gradient-to-r rtl:bg-gradient-to-l",
|
|
167
|
+
"fabric-histoBar",
|
|
136
168
|
)}
|
|
137
169
|
style={{
|
|
138
170
|
width: `${Math.min(item.count / maxTotal, 1) * 100}%`,
|
|
@@ -145,6 +177,7 @@ export const Histogram = ({
|
|
|
145
177
|
key="negative"
|
|
146
178
|
color="danger"
|
|
147
179
|
size="sm"
|
|
180
|
+
stopPropagation
|
|
148
181
|
icon={CoreIcons.funnelMinus}
|
|
149
182
|
aria-label="Filter Negative"
|
|
150
183
|
onClick={() => onClick?.(item.id, true)}
|
|
@@ -153,6 +186,7 @@ export const Histogram = ({
|
|
|
153
186
|
key="positive"
|
|
154
187
|
color="primary"
|
|
155
188
|
size="sm"
|
|
189
|
+
stopPropagation
|
|
156
190
|
icon={CoreIcons.funnelPlus}
|
|
157
191
|
aria-label="Filter"
|
|
158
192
|
onClick={() => onClick?.(item.id)}
|
|
@@ -164,7 +198,7 @@ export const Histogram = ({
|
|
|
164
198
|
<span
|
|
165
199
|
className={classNames(
|
|
166
200
|
"z-1 rounded-full text-xs font-medium px-2 py-1 leading-none",
|
|
167
|
-
|
|
201
|
+
"fabric-histoBadge",
|
|
168
202
|
)}
|
|
169
203
|
>
|
|
170
204
|
{Format.number(item.count)}
|
|
@@ -51,6 +51,25 @@ const filterList = (
|
|
|
51
51
|
return newList;
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Custom hook to filter a list of items based on a query.
|
|
56
|
+
* It provides a way to search through the items and returns the filtered list.
|
|
57
|
+
* The hook uses a deferred value to optimize performance during transitions.
|
|
58
|
+
* It also allows for a custom matcher function to define how items should be matched against the query.
|
|
59
|
+
*
|
|
60
|
+
* @param items - The list of items to filter.
|
|
61
|
+
* @param matcher - An optional function to match items against the query.
|
|
62
|
+
* @returns An object containing the search function, current query, filtered list, and a boolean indicating if a search is in progress.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```jsx
|
|
66
|
+
* useFilteredList(items, (item, query) => {
|
|
67
|
+
* return item.name.includes(query);
|
|
68
|
+
* });
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/data-usefilteredlist--docs}
|
|
72
|
+
*/
|
|
54
73
|
export const useFilteredList = <
|
|
55
74
|
T extends AnyObject = KeyValue,
|
|
56
75
|
Q extends AnyObject = string,
|
|
@@ -57,6 +57,25 @@ const makeRange = (start: number, end: number) => {
|
|
|
57
57
|
return Array.from({ length }, (_: AnyObject, idx: number) => idx + start);
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Custom hook to manage pagination state and logic.
|
|
62
|
+
* It calculates the total number of pages, generates page ranges,
|
|
63
|
+
* and provides a way to change the current page.
|
|
64
|
+
* It also handles the transition of page changes to optimize performance.
|
|
65
|
+
*
|
|
66
|
+
* @param options - The pagination options including total records, current page, records per page, and change handler.
|
|
67
|
+
* @returns An object containing the current page, total pages, head label, ranges for pagination pills, and a function to change the page.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```jsx
|
|
71
|
+
* const { page, totalPages, headLabel, ranges, onPageChange } = usePagination({
|
|
72
|
+
* totalRecords: 100,
|
|
73
|
+
* currentPage: 0,
|
|
74
|
+
* perPage: 10,
|
|
75
|
+
* onChange: (newPage) => console.log("Page changed to:", newPage),
|
|
76
|
+
* });
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
60
79
|
export const usePagination = ({
|
|
61
80
|
totalRecords,
|
|
62
81
|
currentPage = 0,
|
|
@@ -111,9 +130,12 @@ export const usePagination = ({
|
|
|
111
130
|
|
|
112
131
|
/** ***************** set page *******************/
|
|
113
132
|
useEffect(() => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
133
|
+
if (totalRecords > -1) {
|
|
134
|
+
const actual = Math.min(currentPage, totalPages - 1);
|
|
135
|
+
setPage(actual);
|
|
136
|
+
actual !== currentPage && onChange?.(actual);
|
|
137
|
+
}
|
|
138
|
+
}, [currentPage, totalRecords, totalPages]);
|
|
117
139
|
|
|
118
140
|
const headLabel = useMemo(
|
|
119
141
|
() => (
|
|
@@ -126,10 +148,13 @@ export const usePagination = ({
|
|
|
126
148
|
[page, perPage, totalRecords],
|
|
127
149
|
);
|
|
128
150
|
|
|
129
|
-
const onPageChange = useCallback(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
151
|
+
const onPageChange = useCallback(
|
|
152
|
+
(page: number) => {
|
|
153
|
+
setPage(page);
|
|
154
|
+
startTransition(() => onChange?.(page));
|
|
155
|
+
},
|
|
156
|
+
[onChange],
|
|
157
|
+
);
|
|
133
158
|
|
|
134
159
|
return { page, totalPages, headLabel, ranges, onPageChange };
|
|
135
160
|
};
|
|
@@ -46,6 +46,35 @@ export interface SelectableProps {
|
|
|
46
46
|
onClick?: (id: string, isNegative: boolean) => void;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Custom hook to manage selection of items in a list.
|
|
51
|
+
* It allows toggling selection state of items, handling both selected and non-selected states.
|
|
52
|
+
* The hook maintains a selection state and provides a function to toggle the selection of items.
|
|
53
|
+
* It also supports callbacks for when the selection changes or when an item is clicked.
|
|
54
|
+
*
|
|
55
|
+
* @param items - The list of items to manage selection for.
|
|
56
|
+
* @param selected - An array of item IDs that are currently selected.
|
|
57
|
+
* @param nonselected - An array of item IDs that are currently non-selected.
|
|
58
|
+
* @param onChange - Callback function that is called when the selection changes.
|
|
59
|
+
* @param onClick - Callback function that is called when an item is clicked.
|
|
60
|
+
* @returns An object containing the current selection state and a function to toggle selection of items.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```jsx
|
|
64
|
+
* const { selection, toggleSelection } = useSelectableList({
|
|
65
|
+
* items: [{ id: '1', label: 'Item 1' }, { id: '2', label: 'Item 2' }],
|
|
66
|
+
* selected: ['1'],
|
|
67
|
+
* nonselected: ['2'],
|
|
68
|
+
* onChange: (selectedIds, nonSelectedIds) => {
|
|
69
|
+
* console.log('Selected:', selectedIds);
|
|
70
|
+
* console.log('Non-selected:', nonSelectedIds);
|
|
71
|
+
* },
|
|
72
|
+
* onClick: (id, isNegative) => {
|
|
73
|
+
* console.log(`Item ${id} clicked, isNegative: ${isNegative}`);
|
|
74
|
+
* },
|
|
75
|
+
* });
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
49
78
|
export const useSelectableList = ({
|
|
50
79
|
items,
|
|
51
80
|
selected,
|
package/src/i18n/ar.json
CHANGED
package/src/i18n/en.json
CHANGED
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";
|
package/src/json/JsonViewer.tsx
CHANGED
|
@@ -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
|
-
<
|
|
220
|
-
className=
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
</
|
|
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
|
|
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
|
|
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
|
)}
|
|
@@ -336,6 +346,32 @@ const JsonEntry = ({
|
|
|
336
346
|
);
|
|
337
347
|
};
|
|
338
348
|
|
|
349
|
+
/**
|
|
350
|
+
* JsonViewer component to display JSON data in a structured format.
|
|
351
|
+
* It supports inline display, property filtering, and custom formatting.
|
|
352
|
+
* The component can handle nested objects and arrays, displaying them in a tree-like structure.
|
|
353
|
+
* It also provides options for copying values and filtering properties.
|
|
354
|
+
*
|
|
355
|
+
* @param {JsonViewProps<T>} props - The properties for the JsonViewer component.
|
|
356
|
+
* @returns {JSX.Element} The rendered JsonViewer component.
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```jsx
|
|
360
|
+
* <JsonViewer
|
|
361
|
+
* json={{ name: "John", age: 30, address: { city: "New York", zip: "10001" } }}
|
|
362
|
+
* copy={true}
|
|
363
|
+
* filters={true}
|
|
364
|
+
* propertyScheme={{ date: ["createdAt"], phone: ["contact"] }}
|
|
365
|
+
* inline={true}
|
|
366
|
+
* labelWidth="25%"
|
|
367
|
+
* formatter={(path, value) => (path === "age" ? `${value} years` : value)}
|
|
368
|
+
* labeler={(path) => path.replace(/_/g, " ").toUpperCase()}
|
|
369
|
+
* onFilter={(path, value, negate) => console.log(`Filter ${negate ? "negate" : ""} on ${path}:`, value)}
|
|
370
|
+
* />
|
|
371
|
+
* ```
|
|
372
|
+
*
|
|
373
|
+
* @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/data-jsonviewer--docs}
|
|
374
|
+
*/
|
|
339
375
|
export const JsonViewer = <T extends KeyValue = KeyValue>({
|
|
340
376
|
className,
|
|
341
377
|
json,
|
|
@@ -356,7 +392,7 @@ export const JsonViewer = <T extends KeyValue = KeyValue>({
|
|
|
356
392
|
const list = useMemo(() => reduceObject(json), [json]);
|
|
357
393
|
|
|
358
394
|
return (
|
|
359
|
-
<div {...rest} className={classNames(className, "
|
|
395
|
+
<div {...rest} className={classNames(className, "bg-alternate")}>
|
|
360
396
|
{!isEmpty(json) ? (
|
|
361
397
|
list.map((item) => (
|
|
362
398
|
<JsonEntry
|