@react-fabric/data 1.0.0-beta.8 → 1.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.
- package/{src/histogram/Histogram.module.css → css/_histogram.css} +16 -13
- package/css/styles.css +24 -0
- package/dist/cjs/checklist/CheckList.js +59 -33
- package/dist/cjs/histogram/Histogram.js +41 -26
- package/dist/cjs/hooks/useFilteredList.js +100 -17
- package/dist/cjs/hooks/usePagination.js +29 -8
- package/dist/cjs/hooks/useSelectableList.js +30 -5
- package/dist/cjs/i18n/{ar.json.js → ar/data.json.js} +8 -1
- package/dist/cjs/i18n/{en.json.js → en/data.json.js} +8 -1
- package/dist/cjs/i18n/index.js +4 -4
- package/dist/cjs/index.js +30 -0
- package/dist/cjs/json/JsonViewer.js +80 -43
- package/dist/cjs/pagination/Pagination.js +24 -15
- package/dist/cjs/table/AddColumn.js +5 -5
- package/dist/cjs/table/BodyCell.js +20 -13
- package/dist/cjs/table/BodyRow.js +41 -28
- package/dist/cjs/table/CheckboxCell.js +10 -9
- package/dist/cjs/table/ColumnFilters.js +100 -0
- package/dist/cjs/table/EmptyCell.js +4 -4
- package/dist/cjs/table/HeaderCell.js +79 -53
- package/dist/cjs/table/HtmlTable.js +84 -0
- package/dist/cjs/table/{Context.js → Provider.js} +8 -10
- package/dist/cjs/table/Table.js +99 -33
- package/dist/cjs/{tree/types.js → table/context.js} +9 -5
- package/dist/cjs/table/types.js +29 -0
- package/dist/cjs/table/useResizer.js +10 -10
- package/dist/cjs/table/useTableColumns.js +4 -4
- package/dist/cjs/table/useVirtualTable.js +129 -22
- package/dist/cjs/tree/TreeNode.js +66 -46
- package/dist/cjs/tree/TreePanel.js +60 -24
- package/dist/cjs/tree/useTree.js +212 -47
- package/dist/cjs/tree/utils.js +63 -34
- package/dist/cjs/utils/getColor.js +58 -0
- package/dist/cjs/virtual/Gallery.js +119 -61
- package/dist/cjs/virtual/List.js +116 -55
- package/dist/cjs/virtual/Scroller.js +16 -8
- package/dist/esm/checklist/CheckList.js +61 -35
- package/dist/esm/histogram/Histogram.js +43 -28
- package/dist/esm/hooks/useFilteredList.js +101 -18
- package/dist/esm/hooks/usePagination.js +29 -8
- package/dist/esm/hooks/useSelectableList.js +30 -5
- package/dist/esm/i18n/{ar.json.js → ar/data.json.js} +8 -2
- package/dist/esm/i18n/{en.json.js → en/data.json.js} +8 -2
- package/dist/esm/i18n/index.js +2 -2
- package/dist/esm/index.js +31 -0
- package/dist/esm/json/JsonViewer.js +82 -45
- package/dist/esm/pagination/Pagination.js +25 -16
- package/dist/esm/table/AddColumn.js +6 -6
- package/dist/esm/table/BodyCell.js +21 -14
- package/dist/esm/table/BodyRow.js +41 -28
- package/dist/esm/table/CheckboxCell.js +11 -10
- package/dist/esm/table/ColumnFilters.js +100 -0
- package/dist/esm/table/EmptyCell.js +4 -4
- package/dist/esm/table/HeaderCell.js +80 -54
- package/dist/esm/table/HtmlTable.js +84 -0
- package/dist/esm/table/{Context.js → Provider.js} +11 -12
- package/dist/esm/table/Table.js +100 -34
- package/dist/esm/{tree/types.js → table/context.js} +8 -4
- package/dist/esm/table/types.js +29 -0
- package/dist/esm/table/useResizer.js +10 -10
- package/dist/esm/table/useTableColumns.js +4 -4
- package/dist/esm/table/useVirtualTable.js +131 -24
- package/dist/esm/tree/TreeNode.js +69 -49
- package/dist/esm/tree/TreePanel.js +62 -26
- package/dist/esm/tree/useTree.js +215 -50
- package/dist/esm/tree/utils.js +64 -35
- package/dist/esm/utils/getColor.js +58 -0
- package/dist/esm/virtual/Gallery.js +121 -63
- package/dist/esm/virtual/List.js +118 -57
- package/dist/esm/virtual/Scroller.js +17 -9
- package/dist/types/checklist/CheckList.d.ts +25 -6
- package/dist/types/histogram/Histogram.d.ts +28 -6
- package/dist/types/hooks/useFilteredList.d.ts +62 -5
- package/dist/types/hooks/usePagination.d.ts +18 -2
- package/dist/types/hooks/useSelectableList.d.ts +24 -1
- package/dist/types/index.d.ts +27 -0
- package/dist/types/json/JsonViewer.d.ts +23 -3
- package/dist/types/pagination/Pagination.d.ts +8 -2
- package/dist/types/table/AddColumn.d.ts +2 -2
- package/dist/types/table/BodyCell.d.ts +2 -2
- package/dist/types/table/BodyRow.d.ts +9 -9
- package/dist/types/table/CheckboxCell.d.ts +3 -3
- package/dist/types/table/ColumnFilters.d.ts +12 -0
- package/dist/types/table/EmptyCell.d.ts +1 -1
- package/dist/types/table/HeaderCell.d.ts +1 -1
- package/dist/types/table/HtmlTable.d.ts +21 -0
- package/dist/types/table/Provider.d.ts +2 -0
- package/dist/types/table/Table.d.ts +10 -1
- package/dist/types/table/context.d.ts +7 -0
- package/dist/types/table/types.d.ts +52 -10
- package/dist/types/table/useResizer.d.ts +1 -1
- package/dist/types/table/useTableColumns.d.ts +1 -1
- package/dist/types/table/useVirtualTable.d.ts +31 -12
- package/dist/types/tree/TreeNode.d.ts +1 -1
- package/dist/types/tree/TreePanel.d.ts +36 -1
- package/dist/types/tree/types.d.ts +75 -28
- package/dist/types/tree/useTree.d.ts +8 -12
- package/dist/types/tree/utils.d.ts +15 -8
- package/dist/types/utils/getColor.d.ts +43 -0
- package/dist/types/virtual/Gallery.d.ts +37 -6
- package/dist/types/virtual/List.d.ts +38 -6
- package/dist/types/virtual/Scroller.d.ts +1 -1
- package/package.json +17 -19
- package/src/checklist/CheckList.tsx +68 -71
- package/src/histogram/Histogram.tsx +47 -48
- package/src/hooks/useFilteredList.tsx +111 -28
- package/src/hooks/usePagination.tsx +39 -39
- package/src/hooks/useSelectableList.ts +45 -33
- package/src/i18n/{ar.json → ar/data.json} +5 -0
- package/src/i18n/{en.json → en/data.json} +5 -0
- package/src/i18n/index.ts +2 -2
- package/src/index.ts +23 -17
- package/src/json/JsonViewer.tsx +89 -120
- package/src/pagination/Pagination.tsx +104 -105
- package/src/table/AddColumn.tsx +6 -15
- package/src/table/BodyCell.tsx +27 -36
- package/src/table/BodyRow.tsx +36 -59
- package/src/table/CheckboxCell.tsx +12 -15
- package/src/table/ColumnFilters.tsx +93 -0
- package/src/table/EmptyCell.tsx +5 -18
- package/src/table/HeaderCell.tsx +72 -53
- package/src/table/HtmlTable.tsx +106 -0
- package/src/table/{Context.tsx → Provider.tsx} +11 -28
- package/src/table/Table.tsx +98 -83
- package/src/table/context.ts +33 -0
- package/src/table/types.ts +53 -27
- package/src/table/useResizer.ts +5 -9
- package/src/table/useTableColumns.ts +4 -6
- package/src/table/useVirtualTable.ts +136 -37
- package/src/tree/TreeNode.tsx +91 -86
- package/src/tree/TreePanel.tsx +87 -51
- package/src/tree/types.ts +85 -46
- package/src/tree/useTree.ts +210 -65
- package/src/tree/utils.ts +81 -62
- package/src/utils/getColor.ts +55 -0
- package/src/virtual/Gallery.tsx +129 -120
- package/src/virtual/List.tsx +124 -91
- package/src/virtual/Scroller.tsx +25 -9
- package/dist/cjs/histogram/Histogram.module.css +0 -1
- package/dist/esm/histogram/Histogram.module.css +0 -1
- package/dist/types/table/Context.d.ts +0 -8
|
@@ -21,32 +21,19 @@
|
|
|
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 { Badge,
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
} from "@react-fabric/core/dist/types/types";
|
|
29
|
-
import { isString } from "@react-fabric/utilities";
|
|
30
|
-
import {
|
|
31
|
-
Fragment,
|
|
32
|
-
isValidElement,
|
|
33
|
-
useEffect,
|
|
34
|
-
useMemo,
|
|
35
|
-
useState,
|
|
36
|
-
type FC,
|
|
37
|
-
} from "react";
|
|
24
|
+
import { Badge, EmptyContent, Icon } from "@react-fabric/core";
|
|
25
|
+
import type { BadgeType } from "@react-fabric/core/dist/types/components/badge/Badge";
|
|
26
|
+
import { type TestProps } from "@react-fabric/core/dist/types/types";
|
|
27
|
+
import { Fragment, useEffect, useMemo, useState } from "react";
|
|
38
28
|
import { useTranslation } from "react-i18next";
|
|
39
|
-
import {
|
|
40
|
-
useSelectableList,
|
|
41
|
-
type SelectableProps,
|
|
42
|
-
} from "../hooks/useSelectableList";
|
|
29
|
+
import { useSelectableList, type SelectableProps } from "../hooks/useSelectableList";
|
|
43
30
|
|
|
44
|
-
export interface CheckListItem
|
|
31
|
+
export interface CheckListItem {
|
|
45
32
|
id: string;
|
|
46
33
|
label: string;
|
|
47
|
-
icon?: string;
|
|
34
|
+
icon?: string | React.ReactElement;
|
|
48
35
|
badge?: BadgeType;
|
|
49
|
-
|
|
36
|
+
disabled?: boolean;
|
|
50
37
|
}
|
|
51
38
|
|
|
52
39
|
export interface CheckListProps extends SelectableProps, TestProps {
|
|
@@ -70,43 +57,34 @@ export interface CheckListProps extends SelectableProps, TestProps {
|
|
|
70
57
|
sortItems?: boolean;
|
|
71
58
|
}
|
|
72
59
|
|
|
73
|
-
|
|
74
|
-
Omit<CheckListItem, "selected"> & {
|
|
75
|
-
selected?: number;
|
|
76
|
-
allowNegative?: boolean;
|
|
77
|
-
}
|
|
78
|
-
> = ({
|
|
60
|
+
function CheckItem({
|
|
79
61
|
id,
|
|
80
62
|
allowNegative,
|
|
81
63
|
selected,
|
|
82
64
|
icon,
|
|
83
65
|
label,
|
|
84
|
-
|
|
66
|
+
disabled,
|
|
85
67
|
onClick,
|
|
86
68
|
badge,
|
|
87
|
-
...aria
|
|
88
69
|
}: Omit<CheckListItem, "selected"> & {
|
|
70
|
+
onClick: (id: string, isNegative: boolean) => void;
|
|
89
71
|
selected?: number;
|
|
90
72
|
allowNegative?: boolean;
|
|
91
|
-
})
|
|
73
|
+
}) {
|
|
92
74
|
return (
|
|
93
75
|
<div
|
|
94
76
|
role="none"
|
|
95
77
|
className="ax-checkList__item"
|
|
96
|
-
data-disabled={
|
|
78
|
+
data-disabled={disabled}
|
|
97
79
|
onClick={() => onClick(id, false)}
|
|
98
|
-
onContextMenu={(e) => [
|
|
99
|
-
allowNegative && onClick(id, true),
|
|
100
|
-
e.preventDefault(),
|
|
101
|
-
]}
|
|
102
|
-
{...aria}
|
|
80
|
+
onContextMenu={(e) => [allowNegative && onClick(id, true), e.preventDefault()]}
|
|
103
81
|
>
|
|
104
82
|
{!allowNegative && (
|
|
105
83
|
<Icon
|
|
106
84
|
data-type="checkbox"
|
|
107
85
|
className="ax-checkList__checkbox"
|
|
108
86
|
data-selected={selected === 1}
|
|
109
|
-
icon={selected ?
|
|
87
|
+
icon={selected ? "icon-[mdi--checkbox-marked]" : "icon-[mdi--checkbox-blank-outline]"}
|
|
110
88
|
/>
|
|
111
89
|
)}
|
|
112
90
|
{allowNegative && (
|
|
@@ -116,40 +94,66 @@ const CheckItem: FC<
|
|
|
116
94
|
className="ax-checkList__checkbox"
|
|
117
95
|
icon={
|
|
118
96
|
selected === 1
|
|
119
|
-
?
|
|
97
|
+
? "icon-[mdi--plus-box-outline]"
|
|
120
98
|
: selected === -1
|
|
121
|
-
?
|
|
122
|
-
:
|
|
99
|
+
? "icon-[mdi--minus-box-outline]"
|
|
100
|
+
: "icon-[mdi--checkbox-blank-outline]"
|
|
123
101
|
}
|
|
124
102
|
data-selected={selected}
|
|
125
103
|
/>
|
|
126
104
|
<div className="ax-checkList__checkbox">
|
|
127
|
-
<
|
|
128
|
-
|
|
105
|
+
<button
|
|
106
|
+
type="button"
|
|
107
|
+
className="size-fit leading-0 cursor-pointer"
|
|
129
108
|
onClick={(e) => [(onClick(id, false), e.stopPropagation())]}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
109
|
+
>
|
|
110
|
+
<Icon
|
|
111
|
+
data-type="positive"
|
|
112
|
+
icon={selected === 1 ? "icon-[mdi--plus-box]" : "icon-[mdi--plus-box-outline]"}
|
|
113
|
+
data-selected={selected === 1}
|
|
114
|
+
/>
|
|
115
|
+
</button>
|
|
116
|
+
<button
|
|
117
|
+
type="button"
|
|
118
|
+
className="size-fit leading-0 cursor-pointer"
|
|
135
119
|
onClick={(e) => [(onClick(id, true), e.stopPropagation())]}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
120
|
+
>
|
|
121
|
+
<Icon
|
|
122
|
+
data-type="negative"
|
|
123
|
+
icon={selected === -1 ? "icon-[mdi--minus-box]" : "icon-[mdi--minus-box-outline]"}
|
|
124
|
+
data-selected={selected === -1}
|
|
125
|
+
/>
|
|
126
|
+
</button>
|
|
141
127
|
</div>
|
|
142
128
|
</Fragment>
|
|
143
129
|
)}
|
|
144
|
-
{
|
|
145
|
-
{isString(icon) && <Icon icon={icon} />}
|
|
130
|
+
{icon && <Icon icon={icon} />}
|
|
146
131
|
<label>{label}</label>
|
|
147
|
-
<Badge {...badge} />
|
|
132
|
+
{badge && <Badge {...(badge as KeyValue)} />}
|
|
148
133
|
</div>
|
|
149
134
|
);
|
|
150
|
-
}
|
|
135
|
+
}
|
|
151
136
|
|
|
152
|
-
|
|
137
|
+
/**
|
|
138
|
+
* CheckList component to display a list of selectable items with checkboxes.
|
|
139
|
+
* It supports selection, deselection, and allows for negative selections.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```jsx
|
|
143
|
+
* <CheckList
|
|
144
|
+
* items={[
|
|
145
|
+
* { id: "1", label: "Item 1", icon: "icon1" },
|
|
146
|
+
* { id: "2", label: "Item 2", icon: "icon2", badge: { text: "New" } },
|
|
147
|
+
* ]}
|
|
148
|
+
* maxCount={5}
|
|
149
|
+
* allowNegative={true}
|
|
150
|
+
* emptyMessage="No items available"
|
|
151
|
+
* onChange={(selectedItems) => console.log(selectedItems)}
|
|
152
|
+
* onClick={(itemId, isNegative) => console.log(itemId, isNegative)}
|
|
153
|
+
* />
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export function CheckList({
|
|
153
157
|
items = [],
|
|
154
158
|
maxCount = 0,
|
|
155
159
|
allowNegative,
|
|
@@ -160,7 +164,7 @@ export const CheckList: FC<CheckListProps> = ({
|
|
|
160
164
|
onChange,
|
|
161
165
|
onClick,
|
|
162
166
|
...aria
|
|
163
|
-
}: CheckListProps)
|
|
167
|
+
}: CheckListProps) {
|
|
164
168
|
const { t } = useTranslation("data");
|
|
165
169
|
const { selection, toggleSelection } = useSelectableList({
|
|
166
170
|
items,
|
|
@@ -172,6 +176,7 @@ export const CheckList: FC<CheckListProps> = ({
|
|
|
172
176
|
const [showMore, setShowMore] = useState(false);
|
|
173
177
|
|
|
174
178
|
useEffect(() => {
|
|
179
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
175
180
|
setShowMore(false);
|
|
176
181
|
}, [items]);
|
|
177
182
|
|
|
@@ -193,8 +198,9 @@ export const CheckList: FC<CheckListProps> = ({
|
|
|
193
198
|
<div {...aria}>
|
|
194
199
|
{listItems.map((item, index) => (
|
|
195
200
|
<CheckItem
|
|
196
|
-
|
|
201
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key
|
|
197
202
|
key={index}
|
|
203
|
+
{...item}
|
|
198
204
|
onClick={toggleSelection}
|
|
199
205
|
allowNegative={allowNegative}
|
|
200
206
|
selected={selection[item.id]}
|
|
@@ -202,21 +208,12 @@ export const CheckList: FC<CheckListProps> = ({
|
|
|
202
208
|
))}
|
|
203
209
|
{maxCount > 0 && items.length > maxCount && (
|
|
204
210
|
<div className="ax-moreLink">
|
|
205
|
-
<span
|
|
206
|
-
role="none"
|
|
207
|
-
className="link"
|
|
208
|
-
onClick={() => setShowMore(!showMore)}
|
|
209
|
-
>
|
|
211
|
+
<span role="none" className="link" onClick={() => setShowMore(!showMore)}>
|
|
210
212
|
...{t(`core:action.${showMore ? "less" : "more"}`)}
|
|
211
213
|
</span>
|
|
212
214
|
</div>
|
|
213
215
|
)}
|
|
214
|
-
{listItems.length === 0 && (
|
|
215
|
-
<EmptyContent
|
|
216
|
-
className="text-sm"
|
|
217
|
-
message={emptyMessage ?? t("checkList.empty")}
|
|
218
|
-
/>
|
|
219
|
-
)}
|
|
216
|
+
{listItems.length === 0 && <EmptyContent className="text-sm" message={emptyMessage ?? t("checkList.empty")} />}
|
|
220
217
|
</div>
|
|
221
218
|
);
|
|
222
|
-
}
|
|
219
|
+
}
|
|
@@ -21,23 +21,12 @@
|
|
|
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 {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
CoreIcons,
|
|
28
|
-
EmptyContent,
|
|
29
|
-
Icon,
|
|
30
|
-
} from "@react-fabric/core";
|
|
31
|
-
import {
|
|
32
|
-
type ColorType,
|
|
33
|
-
type PaletteType,
|
|
34
|
-
type TestProps,
|
|
35
|
-
} from "@react-fabric/core/dist/types/types";
|
|
36
|
-
import { compareValues, Format } from "@react-fabric/utilities";
|
|
37
|
-
import classNames from "classnames";
|
|
24
|
+
import { ActionLabel, Button, EmptyContent, Icon } from "@react-fabric/core";
|
|
25
|
+
import type { CustomColors, TestProps } from "@react-fabric/core/dist/types/types";
|
|
26
|
+
import { cn, compareValues, Format } from "@react-fabric/utilities";
|
|
38
27
|
import { useMemo } from "react";
|
|
39
28
|
import { useTranslation } from "react-i18next";
|
|
40
|
-
import
|
|
29
|
+
import { getColor } from "../utils/getColor";
|
|
41
30
|
|
|
42
31
|
const emptyIcon =
|
|
43
32
|
"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";
|
|
@@ -62,7 +51,7 @@ export interface HistogramProps extends TestProps {
|
|
|
62
51
|
/**
|
|
63
52
|
* histogram bar color
|
|
64
53
|
*/
|
|
65
|
-
color?:
|
|
54
|
+
color?: CustomColors;
|
|
66
55
|
/**
|
|
67
56
|
* sorted list
|
|
68
57
|
*/
|
|
@@ -70,27 +59,49 @@ export interface HistogramProps extends TestProps {
|
|
|
70
59
|
/**
|
|
71
60
|
* title when histo list is empty
|
|
72
61
|
*/
|
|
73
|
-
emptyTitle?: string |
|
|
62
|
+
emptyTitle?: string | React.ReactElement;
|
|
74
63
|
/**
|
|
75
64
|
* message when histo list is empty
|
|
76
65
|
*/
|
|
77
|
-
emptyMessage?: string |
|
|
66
|
+
emptyMessage?: string | React.ReactElement;
|
|
78
67
|
/**
|
|
79
68
|
* handle histo click
|
|
80
69
|
*/
|
|
81
70
|
onClick?: (id: string, negative?: true) => void;
|
|
82
71
|
}
|
|
83
72
|
|
|
84
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Histogram component displays a bar chart of items with counts.
|
|
75
|
+
* It allows users to visualize the distribution of counts across different items.
|
|
76
|
+
* The component supports sorting, custom colors, and click handling for each item.
|
|
77
|
+
* It also provides an empty state when there are no items to display.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```jsx
|
|
81
|
+
* <Histogram
|
|
82
|
+
* items={[
|
|
83
|
+
* { id: "1", label: "Item 1", count: 10 },
|
|
84
|
+
* { id: "2", label: "Item 2", count: 20 },
|
|
85
|
+
* { id: "3", label: "Item 3", count: 5, disabled: true },
|
|
86
|
+
* ]}
|
|
87
|
+
* total={30}
|
|
88
|
+
* color="primary-500"
|
|
89
|
+
* sortBy="count"
|
|
90
|
+
* emptyTitle="No Data Available"
|
|
91
|
+
* emptyMessage="Please check back later."
|
|
92
|
+
* onClick={(id, negative) => console.log(`Clicked on ${id}`, negative ? "Negative" : "Positive")}
|
|
93
|
+
* />
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export function Histogram({
|
|
85
97
|
items = [],
|
|
86
98
|
total,
|
|
87
|
-
color = "primary",
|
|
99
|
+
color = "primary-500",
|
|
88
100
|
sortBy = "count",
|
|
89
101
|
emptyTitle,
|
|
90
102
|
emptyMessage,
|
|
91
103
|
onClick,
|
|
92
|
-
|
|
93
|
-
}: HistogramProps) => {
|
|
104
|
+
}: HistogramProps) {
|
|
94
105
|
const { t } = useTranslation("data");
|
|
95
106
|
const maxTotal = useMemo(() => {
|
|
96
107
|
if (total) return total;
|
|
@@ -98,19 +109,16 @@ export const Histogram = ({
|
|
|
98
109
|
}, [total, items]);
|
|
99
110
|
|
|
100
111
|
const sorted = useMemo(() => {
|
|
101
|
-
return items.sort(
|
|
102
|
-
compareValues(sortBy === "label" ? "asc" : "desc", sortBy),
|
|
103
|
-
);
|
|
112
|
+
return items.sort(compareValues(sortBy === "label" ? "asc" : "desc", sortBy));
|
|
104
113
|
}, [items, sortBy]);
|
|
105
114
|
|
|
106
115
|
return (
|
|
107
116
|
<div
|
|
108
|
-
className={
|
|
109
|
-
{...aria}
|
|
117
|
+
className={"fabric-histogram from-0% to-100%"}
|
|
110
118
|
style={
|
|
111
119
|
{
|
|
112
|
-
"--color":
|
|
113
|
-
} as
|
|
120
|
+
"--color": getColor(color),
|
|
121
|
+
} as React.CSSProperties
|
|
114
122
|
}
|
|
115
123
|
>
|
|
116
124
|
{sorted.length === 0 && (
|
|
@@ -125,20 +133,16 @@ export const Histogram = ({
|
|
|
125
133
|
role="none"
|
|
126
134
|
key={item.id}
|
|
127
135
|
data-disabled={item.count === 0 || item.disabled}
|
|
128
|
-
className={
|
|
136
|
+
className={cn(
|
|
129
137
|
"relative cursor-pointer flex flex-nowrap items-center select-none overflow-hidden min-h-6 my-px",
|
|
130
|
-
|
|
131
|
-
((item.total ?? item.count) === 0 || item.disabled) &&
|
|
132
|
-
"pointer-events-none",
|
|
138
|
+
"fabric-histoLabel",
|
|
139
|
+
((item.total ?? item.count) === 0 || item.disabled) && "pointer-events-none",
|
|
133
140
|
)}
|
|
134
141
|
data-testid={item.id}
|
|
135
142
|
onClick={(e) => [onClick?.(item.id), e.stopPropagation()]}
|
|
136
143
|
>
|
|
137
144
|
<div
|
|
138
|
-
className={
|
|
139
|
-
"absolute z-0 inset-0 opacity-50 min-w-2 ltr:bg-gradient-to-r rtl:bg-gradient-to-l from-0% to-100%",
|
|
140
|
-
classes.histoBar,
|
|
141
|
-
)}
|
|
145
|
+
className={cn("absolute z-0 inset-0 opacity-50 min-w-2 ltr:bg-linear-to-r rtl:bg-linear-to-l", "fabric-histoBar")}
|
|
142
146
|
style={{
|
|
143
147
|
width: `${Math.min(item.count / maxTotal, 1) * 100}%`,
|
|
144
148
|
}}
|
|
@@ -150,32 +154,27 @@ export const Histogram = ({
|
|
|
150
154
|
key="negative"
|
|
151
155
|
color="danger"
|
|
152
156
|
size="sm"
|
|
153
|
-
icon=
|
|
157
|
+
icon="icon-[mdi--filter-minus]"
|
|
154
158
|
aria-label="Filter Negative"
|
|
155
|
-
onClick={() => onClick?.(item.id, true)}
|
|
159
|
+
onClick={(e) => (e?.stopPropagation(), onClick?.(item.id, true))}
|
|
156
160
|
/>,
|
|
157
161
|
<Button
|
|
158
162
|
key="positive"
|
|
159
163
|
color="primary"
|
|
160
164
|
size="sm"
|
|
161
|
-
icon=
|
|
165
|
+
icon="icon-[mdi--filter-plus]"
|
|
162
166
|
aria-label="Filter"
|
|
163
|
-
onClick={() => onClick?.(item.id)}
|
|
167
|
+
onClick={(e) => (e?.stopPropagation(), onClick?.(item.id))}
|
|
164
168
|
/>,
|
|
165
169
|
]}
|
|
166
170
|
>
|
|
167
171
|
{item.label}
|
|
168
172
|
</ActionLabel>
|
|
169
|
-
<span
|
|
170
|
-
className={classNames(
|
|
171
|
-
"z-1 rounded-full text-xs font-medium px-2 py-1 leading-none",
|
|
172
|
-
classes.histoBadge,
|
|
173
|
-
)}
|
|
174
|
-
>
|
|
173
|
+
<span className={cn("z-1 rounded-full text-xs font-medium px-2 py-1 leading-none", "fabric-histoBadge")}>
|
|
175
174
|
{Format.number(item.count)}
|
|
176
175
|
</span>
|
|
177
176
|
</div>
|
|
178
177
|
))}
|
|
179
178
|
</div>
|
|
180
179
|
);
|
|
181
|
-
}
|
|
180
|
+
}
|
|
@@ -21,26 +21,28 @@
|
|
|
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 { isString, matchString } from "@react-fabric/utilities";
|
|
25
|
-
import {
|
|
26
|
-
useCallback,
|
|
27
|
-
useDeferredValue,
|
|
28
|
-
useEffect,
|
|
29
|
-
useState,
|
|
30
|
-
useTransition,
|
|
31
|
-
} from "react";
|
|
24
|
+
import { isArray, isString, matchString } from "@react-fabric/utilities";
|
|
25
|
+
import { useCallback, useDeferredValue, useEffect, useState, useTransition } from "react";
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
items:
|
|
35
|
-
query:
|
|
36
|
-
|
|
37
|
-
) =>
|
|
38
|
-
|
|
27
|
+
function filterList<T extends AnyObject = KeyValue>(
|
|
28
|
+
items: Array<T & { label?: string }>,
|
|
29
|
+
query: string | string[] = "",
|
|
30
|
+
matchAll: boolean = true,
|
|
31
|
+
matcher?: (option: T, query: string | string[]) => boolean,
|
|
32
|
+
) {
|
|
33
|
+
const newList: T[] = [];
|
|
39
34
|
items.forEach((item) => {
|
|
40
35
|
if (matcher) {
|
|
41
|
-
|
|
36
|
+
if (matcher?.(item, query)) return newList.push(item);
|
|
42
37
|
}
|
|
43
|
-
if (query &&
|
|
38
|
+
if (query && isArray(query) && query.length > 0) {
|
|
39
|
+
if (matchAll && query.every((q) => matchString(item.label ?? item.toString(), q))) {
|
|
40
|
+
return newList.push(item);
|
|
41
|
+
}
|
|
42
|
+
if (!matchAll && query.some((q) => matchString(item.label ?? item.toString(), q))) {
|
|
43
|
+
return newList.push(item);
|
|
44
|
+
}
|
|
45
|
+
} else if (query && isString(query)) {
|
|
44
46
|
if (matchString(item.label ?? item.toString(), query)) {
|
|
45
47
|
return newList.push(item);
|
|
46
48
|
}
|
|
@@ -49,30 +51,111 @@ const filterList = (
|
|
|
49
51
|
}
|
|
50
52
|
});
|
|
51
53
|
return newList;
|
|
52
|
-
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface RetType<T, Q> {
|
|
57
|
+
onSearch: (query?: Q) => void;
|
|
58
|
+
query: Q;
|
|
59
|
+
filteredList: T[];
|
|
60
|
+
isSearching: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Custom hook to filter a list of items based on a query.
|
|
65
|
+
* It provides a way to search through the items and returns the filtered list.
|
|
66
|
+
* The hook uses a deferred value to optimize performance during transitions.
|
|
67
|
+
* It also allows for a custom matcher function to define how items should be matched against the query.
|
|
68
|
+
*
|
|
69
|
+
* @param items - The list of items to filter.
|
|
70
|
+
* @param matchAll - Match all phrases in the query list against the items.
|
|
71
|
+
* @returns An object containing the search function, current query, filtered list, and a boolean indicating if a search is in progress.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```jsx
|
|
75
|
+
* useFilteredList(items, (item, query) => {
|
|
76
|
+
* return item.name.includes(query);
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @see {@link https://adarshpastakia.github.io/react-fabric/?path=/docs/data-usefilteredlist--docs}
|
|
81
|
+
*/
|
|
82
|
+
export function useFilteredList<T extends AnyObject = KeyValue, Q extends AnyObject = string>(
|
|
83
|
+
items: T[],
|
|
84
|
+
matchAll?: boolean,
|
|
85
|
+
): RetType<T, Q>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Custom hook to filter a list of items based on a query.
|
|
89
|
+
* It provides a way to search through the items and returns the filtered list.
|
|
90
|
+
* The hook uses a deferred value to optimize performance during transitions.
|
|
91
|
+
* It also allows for a custom matcher function to define how items should be matched against the query.
|
|
92
|
+
*
|
|
93
|
+
* @param items - The list of items to filter.
|
|
94
|
+
* @param matcher - An optional function to match items against the query.
|
|
95
|
+
* @returns An object containing the search function, current query, filtered list, and a boolean indicating if a search is in progress.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```jsx
|
|
99
|
+
* useFilteredList(items, (item, query) => {
|
|
100
|
+
* return item.name.includes(query);
|
|
101
|
+
* });
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export function useFilteredList<T extends AnyObject = KeyValue, Q extends AnyObject = string>(
|
|
105
|
+
items: T[],
|
|
106
|
+
matcher?: (item: T, query: Q) => boolean,
|
|
107
|
+
): RetType<T, Q>;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Custom hook to filter a list of items based on a query.
|
|
111
|
+
* It provides a way to search through the items and returns the filtered list.
|
|
112
|
+
* The hook uses a deferred value to optimize performance during transitions.
|
|
113
|
+
* It also allows for a custom matcher function to define how items should be matched against the query.
|
|
114
|
+
*
|
|
115
|
+
* @param items - The list of items to filter.
|
|
116
|
+
* @param matchAll - Match all phrases in the query list against the items.
|
|
117
|
+
* @param matcher - An optional function to match items against the query.
|
|
118
|
+
* @returns An object containing the search function, current query, filtered list, and a boolean indicating if a search is in progress.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```jsx
|
|
122
|
+
* useFilteredList(items, (item, query) => {
|
|
123
|
+
* return item.name.includes(query);
|
|
124
|
+
* });
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export function useFilteredList<T extends AnyObject = KeyValue, Q extends AnyObject = string>(
|
|
128
|
+
items: T[],
|
|
129
|
+
matchAll?: boolean,
|
|
130
|
+
matcher?: (item: T, query: Q) => boolean,
|
|
131
|
+
): RetType<T, Q>;
|
|
53
132
|
|
|
54
|
-
export
|
|
55
|
-
T extends AnyObject = KeyValue,
|
|
56
|
-
Q extends AnyObject = string,
|
|
57
|
-
>(
|
|
133
|
+
export function useFilteredList<T extends AnyObject = KeyValue, Q extends AnyObject = string>(
|
|
58
134
|
items: T[],
|
|
135
|
+
matchAll?: boolean | ((item: T, query: Q) => boolean),
|
|
59
136
|
matcher?: (item: T, query: Q) => boolean,
|
|
60
|
-
)
|
|
137
|
+
) {
|
|
61
138
|
const [query, setQuery] = useState<Q>();
|
|
62
139
|
const _items = useDeferredValue(items ?? []);
|
|
63
|
-
const [filteredList, setFilteredList] = useState<T[]>([]);
|
|
140
|
+
const [filteredList, setFilteredList] = useState<T[]>(() => []);
|
|
64
141
|
const [isSearching, startTransition] = useTransition();
|
|
65
142
|
|
|
143
|
+
const matchingAll = typeof matchAll === "boolean" ? matchAll : true;
|
|
144
|
+
const matcherFn = typeof matchAll === "function" ? matchAll : matcher;
|
|
145
|
+
|
|
66
146
|
const filterItems = useCallback(
|
|
67
|
-
(items: T[], query?: Q) =>
|
|
68
|
-
|
|
147
|
+
(items: T[], query?: Q, matchingAll?: boolean) =>
|
|
148
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
149
|
+
startTransition(() => setFilteredList(filterList(items as never, query as never, matchingAll, matcherFn as never))),
|
|
150
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
69
151
|
[],
|
|
70
152
|
);
|
|
71
153
|
|
|
72
154
|
useEffect(() => {
|
|
73
|
-
if (query) filterItems(_items, query);
|
|
155
|
+
if (query) filterItems(_items, query, matchingAll);
|
|
156
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
74
157
|
else setFilteredList(items);
|
|
75
|
-
}, [query, _items]);
|
|
158
|
+
}, [query, matchingAll, _items, filterItems, items]);
|
|
76
159
|
|
|
77
160
|
return { onSearch: setQuery, query, filteredList, isSearching };
|
|
78
|
-
}
|
|
161
|
+
}
|