gbs-add-block 0.0.39 → 0.0.41
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/README.md +3 -3
- package/index.js +149 -137
- package/package.json +1 -1
- package/source/components/datepicker/index.tsx +1 -1
- package/source/components/input/index.tsx +1 -1
- package/source/components/materialInput/index.tsx +3 -3
- package/source/components/modal/index.tsx +114 -70
- package/source/components/modal/types.ts +5 -3
- package/source/components/multiselect/index.tsx +55 -111
- package/source/components/select/Select.tsx +176 -0
- package/source/components/select/types.ts +1 -1
- package/source/components/spinner/index.tsx +1 -1
- package/source/components/toast/Toast.tsx +3 -3
- package/source/components/toast/ToastContext.tsx +43 -0
- package/source/components/toast/index.tsx +2 -3
- package/source/components/toast/useToast.ts +2 -2
- package/source/globalStyle.ts +4 -0
- package/source/hooks/SelectionHooks/type.ts +4 -0
- package/source/hooks/SelectionHooks/useClickOutside.ts +22 -0
- package/source/hooks/SelectionHooks/useMultiSelectState.ts +64 -0
- package/source/hooks/SelectionHooks/useSelectData.ts +58 -0
- package/source/hooks/SelectionHooks/useSelectState.ts +36 -0
- package/source/components/select/index.tsx +0 -233
- package/source/components/toast/toastAtom.ts +0 -23
|
@@ -5,20 +5,20 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
9
|
-
forwardRef,
|
|
10
|
-
memo,
|
|
8
|
+
import {
|
|
11
9
|
useCallback,
|
|
10
|
+
forwardRef,
|
|
11
|
+
useRef,
|
|
12
12
|
useEffect,
|
|
13
|
+
memo,
|
|
13
14
|
useImperativeHandle,
|
|
14
|
-
useRef,
|
|
15
|
-
useState,
|
|
16
15
|
} from "react";
|
|
16
|
+
import { useClickOutside } from "../hooks/SelectionHooks/useClickOutside";
|
|
17
|
+
import type { MultiSelectHandle, MultiSelectProps } from "./types";
|
|
18
|
+
import { useMultiSelectState } from "../hooks/SelectionHooks/useMultiSelectState";
|
|
17
19
|
import Icon from "../icon/Icon";
|
|
18
20
|
import { check, search, upDown, x } from "../icon/iconPaths";
|
|
19
|
-
import {
|
|
20
|
-
import type { ItemsProps, MultiSelectHandle, MultiSelectProps } from "./types";
|
|
21
|
-
import { primary } from "../globalStyle";
|
|
21
|
+
import { iconClass, popUp, primary } from "../globalStyle";
|
|
22
22
|
|
|
23
23
|
const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
|
|
24
24
|
(props, ref) => {
|
|
@@ -34,70 +34,32 @@ const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
|
|
|
34
34
|
error,
|
|
35
35
|
} = props;
|
|
36
36
|
|
|
37
|
-
const [showPopover, setShowPopover] = useState(false);
|
|
38
|
-
const [workingDataSource, setWorkingDataSource] = useState<ItemsProps[]>(
|
|
39
|
-
[]
|
|
40
|
-
);
|
|
41
|
-
const [filteredItems, setFilteredItems] = useState<ItemsProps[]>([]);
|
|
42
|
-
const [selected, setSelected] = useState<string[]>(selectedItems);
|
|
43
|
-
const [searchTerm, setSearchTerm] = useState("");
|
|
44
37
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
45
38
|
const selectRef = useRef<HTMLDivElement>(null);
|
|
46
39
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
const {
|
|
41
|
+
showPopover,
|
|
42
|
+
setShowPopover,
|
|
43
|
+
workingDataSource,
|
|
44
|
+
filteredItems,
|
|
45
|
+
selected,
|
|
46
|
+
searchTerm,
|
|
47
|
+
setSearchTerm,
|
|
48
|
+
handleSelect,
|
|
49
|
+
togglePopover,
|
|
50
|
+
clearSelected,
|
|
51
|
+
getSelectItems,
|
|
52
|
+
} = useMultiSelectState(items, selectedItems, lazy, onSelect);
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
handleDataSource();
|
|
53
|
-
}, [items]);
|
|
54
|
+
useClickOutside(selectRef, () => setShowPopover(false));
|
|
54
55
|
|
|
55
56
|
useEffect(() => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
selectRef.current &&
|
|
59
|
-
!selectRef.current.contains(event.target as Node)
|
|
60
|
-
) {
|
|
61
|
-
setShowPopover(false);
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
65
|
-
return () =>
|
|
66
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
67
|
-
}, []);
|
|
68
|
-
|
|
69
|
-
const getSelectItems = async (itemsApi: string) => {
|
|
70
|
-
const itemsData = await getSourceData(itemsApi);
|
|
71
|
-
setWorkingDataSource(itemsData.sourcedata);
|
|
72
|
-
setFilteredItems(itemsData.sourcedata);
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const handleDataSource = async () => {
|
|
76
|
-
if (Array.isArray(items)) {
|
|
77
|
-
setWorkingDataSource(items);
|
|
78
|
-
setFilteredItems(items);
|
|
79
|
-
} else if (typeof items === "string") {
|
|
80
|
-
await getSelectItems(items);
|
|
57
|
+
if (showPopover) {
|
|
58
|
+
inputRef.current?.focus();
|
|
81
59
|
}
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const togglePopover = (e: React.MouseEvent) => {
|
|
85
|
-
e.stopPropagation();
|
|
86
|
-
setShowPopover(!showPopover);
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const handleSelect = useCallback(
|
|
90
|
-
(value: string) => {
|
|
91
|
-
const newSelected = selected.includes(value)
|
|
92
|
-
? selected.filter((item) => item !== value)
|
|
93
|
-
: [...selected, value];
|
|
94
|
-
setSelected(newSelected);
|
|
95
|
-
onSelect?.(newSelected);
|
|
96
|
-
},
|
|
97
|
-
[selected, onSelect]
|
|
98
|
-
);
|
|
60
|
+
}, [showPopover]);
|
|
99
61
|
|
|
100
|
-
const getSelectedDisplay = () => {
|
|
62
|
+
const getSelectedDisplay = useCallback(() => {
|
|
101
63
|
if (selected.length === 0) return placeholder;
|
|
102
64
|
const displayedItems = selected
|
|
103
65
|
.slice(0, 3)
|
|
@@ -110,37 +72,29 @@ const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
|
|
|
110
72
|
display += `, +${selected.length - 3} more`;
|
|
111
73
|
}
|
|
112
74
|
return display;
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
workingDataSource,
|
|
137
|
-
items,
|
|
138
|
-
clearSelected,
|
|
139
|
-
togglePopover,
|
|
140
|
-
getSelectItems,
|
|
141
|
-
selectedDisplay: getSelectedDisplay(),
|
|
142
|
-
selected,
|
|
143
|
-
}));
|
|
75
|
+
}, [selected, workingDataSource, placeholder, truncate]);
|
|
76
|
+
|
|
77
|
+
useImperativeHandle(
|
|
78
|
+
ref,
|
|
79
|
+
() => ({
|
|
80
|
+
workingDataSource,
|
|
81
|
+
items,
|
|
82
|
+
clearSelected,
|
|
83
|
+
togglePopover,
|
|
84
|
+
getSelectItems,
|
|
85
|
+
selectedDisplay: getSelectedDisplay(),
|
|
86
|
+
selected,
|
|
87
|
+
}),
|
|
88
|
+
[
|
|
89
|
+
workingDataSource,
|
|
90
|
+
items,
|
|
91
|
+
clearSelected,
|
|
92
|
+
togglePopover,
|
|
93
|
+
getSelectItems,
|
|
94
|
+
getSelectedDisplay,
|
|
95
|
+
selected,
|
|
96
|
+
]
|
|
97
|
+
);
|
|
144
98
|
|
|
145
99
|
return (
|
|
146
100
|
<div className="relative w-full" ref={selectRef}>
|
|
@@ -163,23 +117,16 @@ const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
|
|
|
163
117
|
onClick={clearSelected}
|
|
164
118
|
type="button"
|
|
165
119
|
>
|
|
166
|
-
<Icon
|
|
167
|
-
elements={x}
|
|
168
|
-
svgClass="h-4 w-4 stroke-gray-500 fill-none dark:stroke-white"
|
|
169
|
-
/>
|
|
120
|
+
<Icon elements={x} svgClass={iconClass["grey-common"]} />
|
|
170
121
|
</button>
|
|
171
122
|
)}
|
|
172
123
|
<button onClick={togglePopover} type="button">
|
|
173
|
-
<Icon
|
|
174
|
-
elements={upDown}
|
|
175
|
-
svgClass="h-4 w-4 stroke-gray-500 fill-none dark:stroke-white"
|
|
176
|
-
/>
|
|
124
|
+
<Icon elements={upDown} svgClass={iconClass["grey-common"]} />
|
|
177
125
|
</button>
|
|
178
126
|
</div>
|
|
179
127
|
</div>
|
|
180
|
-
<p className={primary["error-primary"]}>{error
|
|
128
|
+
{error && <p className={primary["error-primary"]}>{error}</p>}
|
|
181
129
|
|
|
182
|
-
{/* Hidden input to integrate with the form */}
|
|
183
130
|
<input
|
|
184
131
|
type="hidden"
|
|
185
132
|
name={name}
|
|
@@ -188,13 +135,10 @@ const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
|
|
|
188
135
|
/>
|
|
189
136
|
|
|
190
137
|
{showPopover && (
|
|
191
|
-
<div className="
|
|
138
|
+
<div className={popUp["pop-up-style"]}>
|
|
192
139
|
{showSearch && (
|
|
193
140
|
<div className="flex p-2 gap-1 items-center sticky top-0 bg-white border-b dark:bg-black">
|
|
194
|
-
<Icon
|
|
195
|
-
elements={search}
|
|
196
|
-
svgClass="stroke-gray-500 fill-none dark:stroke-white"
|
|
197
|
-
/>
|
|
141
|
+
<Icon elements={search} svgClass={iconClass["grey-common"]} />
|
|
198
142
|
<input
|
|
199
143
|
autoComplete="off"
|
|
200
144
|
type="text"
|
|
@@ -204,7 +148,7 @@ const MultiSelect = forwardRef<MultiSelectHandle, MultiSelectProps>(
|
|
|
204
148
|
className="w-full outline-none dark:bg-black"
|
|
205
149
|
ref={inputRef}
|
|
206
150
|
value={searchTerm}
|
|
207
|
-
onChange={
|
|
151
|
+
onChange={(e) => setSearchTerm(e.target.value)}
|
|
208
152
|
/>
|
|
209
153
|
</div>
|
|
210
154
|
)}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Grampro Business Services and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {
|
|
9
|
+
forwardRef,
|
|
10
|
+
memo,
|
|
11
|
+
useCallback,
|
|
12
|
+
useEffect,
|
|
13
|
+
useImperativeHandle,
|
|
14
|
+
useRef,
|
|
15
|
+
} from "react";
|
|
16
|
+
import Icon from "../icon/Icon";
|
|
17
|
+
import { check, search, upDown, x } from "../icon/iconPaths";
|
|
18
|
+
import type { SelectHandle, SelectProps } from "./types";
|
|
19
|
+
import { selectStyle } from "./style";
|
|
20
|
+
import { iconClass, popUp, primary } from "../globalStyle";
|
|
21
|
+
import { useSelectState } from "../hooks/SelectionHooks/useSelectState";
|
|
22
|
+
import { useSelectData } from "../hooks/SelectionHooks/useSelectData";
|
|
23
|
+
import { useClickOutside } from "../hooks/SelectionHooks/useClickOutside";
|
|
24
|
+
|
|
25
|
+
const Select = forwardRef<SelectHandle, SelectProps>((props, ref) => {
|
|
26
|
+
const {
|
|
27
|
+
id = "",
|
|
28
|
+
name = "",
|
|
29
|
+
placeholder = "Select an Item...",
|
|
30
|
+
items,
|
|
31
|
+
lazy = false,
|
|
32
|
+
showSearch = true,
|
|
33
|
+
onSelect,
|
|
34
|
+
selectedItem: initialSelectedItem,
|
|
35
|
+
error = undefined,
|
|
36
|
+
onFiltering,
|
|
37
|
+
} = props;
|
|
38
|
+
|
|
39
|
+
const {
|
|
40
|
+
showPopover,
|
|
41
|
+
setShowPopover,
|
|
42
|
+
workingDataSource,
|
|
43
|
+
setWorkingDataSource,
|
|
44
|
+
searchTerm,
|
|
45
|
+
setSearchTerm,
|
|
46
|
+
selectedItem,
|
|
47
|
+
setSelectedItem,
|
|
48
|
+
togglePopover,
|
|
49
|
+
clearSelected,
|
|
50
|
+
} = useSelectState(initialSelectedItem);
|
|
51
|
+
|
|
52
|
+
const { getSelectItems, selectedDisplay, filteredItems } = useSelectData(
|
|
53
|
+
items,
|
|
54
|
+
setWorkingDataSource,
|
|
55
|
+
workingDataSource,
|
|
56
|
+
searchTerm,
|
|
57
|
+
lazy,
|
|
58
|
+
selectedItem
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
62
|
+
const selectRef = useRef<HTMLDivElement>(null);
|
|
63
|
+
|
|
64
|
+
useClickOutside(selectRef, () => setShowPopover(false));
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (showPopover) {
|
|
68
|
+
inputRef.current?.focus();
|
|
69
|
+
}
|
|
70
|
+
}, [showPopover]);
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
setSelectedItem(initialSelectedItem);
|
|
74
|
+
}, [initialSelectedItem, setSelectedItem]);
|
|
75
|
+
|
|
76
|
+
const handleSelect = useCallback(
|
|
77
|
+
(value: string) => {
|
|
78
|
+
setSelectedItem(value);
|
|
79
|
+
setShowPopover(false);
|
|
80
|
+
setSearchTerm("");
|
|
81
|
+
if (onSelect) onSelect(value);
|
|
82
|
+
},
|
|
83
|
+
[onSelect, setSelectedItem, setShowPopover, setSearchTerm]
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const handleClear = () => {
|
|
87
|
+
clearSelected();
|
|
88
|
+
if (onSelect) onSelect(undefined);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
useImperativeHandle(ref, () => ({
|
|
92
|
+
workingDataSource,
|
|
93
|
+
items,
|
|
94
|
+
clearSelected,
|
|
95
|
+
togglePopover,
|
|
96
|
+
getSelectItems,
|
|
97
|
+
selectedDisplay,
|
|
98
|
+
selected: selectedItem,
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<div className="relative w-full" ref={selectRef} id={id}>
|
|
103
|
+
<div className="w-full relative">
|
|
104
|
+
<button
|
|
105
|
+
className={`${error ? primary["error-border"] : "border"} ${
|
|
106
|
+
selectStyle["select-button"]
|
|
107
|
+
}`}
|
|
108
|
+
onClick={togglePopover}
|
|
109
|
+
type="button"
|
|
110
|
+
>
|
|
111
|
+
{selectedDisplay || placeholder}
|
|
112
|
+
<Icon elements={upDown} svgClass={iconClass["grey-common"]} />
|
|
113
|
+
</button>
|
|
114
|
+
{selectedDisplay && (
|
|
115
|
+
<button
|
|
116
|
+
className={selectStyle["selectedDisplay-Button"]}
|
|
117
|
+
onClick={handleClear}
|
|
118
|
+
>
|
|
119
|
+
<Icon elements={x} svgClass={iconClass["grey-common"]} />
|
|
120
|
+
</button>
|
|
121
|
+
)}
|
|
122
|
+
{error && <p className={primary["error-primary"]}>{error}</p>}
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<input type="hidden" name={name} value={selectedItem || ""} readOnly />
|
|
126
|
+
|
|
127
|
+
{showPopover && (
|
|
128
|
+
<div className={popUp["pop-up-style"]}>
|
|
129
|
+
{showSearch && (
|
|
130
|
+
<div className={selectStyle["input-parent"]}>
|
|
131
|
+
<Icon elements={search} svgClass={iconClass["grey-common"]} />
|
|
132
|
+
<input
|
|
133
|
+
autoComplete="off"
|
|
134
|
+
type="text"
|
|
135
|
+
name="search"
|
|
136
|
+
id="search"
|
|
137
|
+
placeholder="Search a value"
|
|
138
|
+
className="w-full outline-none dark:bg-black"
|
|
139
|
+
ref={inputRef}
|
|
140
|
+
value={searchTerm}
|
|
141
|
+
onChange={(e) => {
|
|
142
|
+
setSearchTerm(e.target.value);
|
|
143
|
+
if (onFiltering) onFiltering(e.target.value);
|
|
144
|
+
}}
|
|
145
|
+
/>
|
|
146
|
+
</div>
|
|
147
|
+
)}
|
|
148
|
+
{filteredItems.length > 0 ? (
|
|
149
|
+
filteredItems.map(({ value, label }) => (
|
|
150
|
+
<button
|
|
151
|
+
key={value}
|
|
152
|
+
className={selectStyle["filter-button"]}
|
|
153
|
+
onClick={() => handleSelect(value)}
|
|
154
|
+
>
|
|
155
|
+
<Icon
|
|
156
|
+
elements={check}
|
|
157
|
+
svgClass={`h-4 w-4 fill-none ${
|
|
158
|
+
selectedItem === value ? "stroke-gray-500" : ""
|
|
159
|
+
}`}
|
|
160
|
+
/>
|
|
161
|
+
{label}
|
|
162
|
+
</button>
|
|
163
|
+
))
|
|
164
|
+
) : (
|
|
165
|
+
<div className="text-sm text-center">No Data Found</div>
|
|
166
|
+
)}
|
|
167
|
+
</div>
|
|
168
|
+
)}
|
|
169
|
+
</div>
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
Select.displayName = "Select";
|
|
174
|
+
|
|
175
|
+
const MemoizedSelect = memo(Select);
|
|
176
|
+
export { MemoizedSelect as Select };
|
|
@@ -11,7 +11,7 @@ export type SelectProps = {
|
|
|
11
11
|
items?: ItemsProps[] | string;
|
|
12
12
|
lazy?: boolean;
|
|
13
13
|
showSearch?: boolean;
|
|
14
|
-
onSelect?: (selectedItem: string) => void;
|
|
14
|
+
onSelect?: (selectedItem: string | undefined) => void;
|
|
15
15
|
selectedItem?: string;
|
|
16
16
|
onFiltering?: (searchTerm: string) => void;
|
|
17
17
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Toast as ToastType } from "./types";
|
|
3
|
-
import {
|
|
3
|
+
import { useToastContext } from "./ToastContext";
|
|
4
4
|
import Icon from "../icon/Icon";
|
|
5
5
|
import { circleCheck, info, x, xCircle } from "../icon/iconPaths";
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ interface ToastProps {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const Toast: React.FC<ToastProps> = ({ toast }) => {
|
|
12
|
-
const { dismissToast } =
|
|
12
|
+
const { dismissToast } = useToastContext();
|
|
13
13
|
const icons = {
|
|
14
14
|
success: (
|
|
15
15
|
<Icon
|
|
@@ -33,7 +33,7 @@ const Toast: React.FC<ToastProps> = ({ toast }) => {
|
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
35
|
<div
|
|
36
|
-
className={`bg-white px-2 py-3 rounded-lg text-sm flex justify-between w-
|
|
36
|
+
className={`bg-white px-2 py-3 rounded-lg text-sm flex justify-between w-full m-2 border shadow-lg ${
|
|
37
37
|
toast.type === "success"
|
|
38
38
|
? "text-green-500"
|
|
39
39
|
: toast.type === "error"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState } from "react";
|
|
2
|
+
import { Toast } from "./types";
|
|
3
|
+
|
|
4
|
+
interface ToastContextProps {
|
|
5
|
+
toasts: Toast[];
|
|
6
|
+
addToast: (toast: Omit<Toast, "id">) => void;
|
|
7
|
+
dismissToast: (id: number) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const ToastContext = createContext<ToastContextProps | undefined>(undefined);
|
|
11
|
+
|
|
12
|
+
export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
13
|
+
children,
|
|
14
|
+
}) => {
|
|
15
|
+
const [toasts, setToasts] = useState<Toast[]>([]);
|
|
16
|
+
|
|
17
|
+
const addToast = (toast: Omit<Toast, "id">) => {
|
|
18
|
+
const id = Math.floor(Math.random() * 10000);
|
|
19
|
+
const newToast = { ...toast, id };
|
|
20
|
+
setToasts((prev) => [newToast, ...prev]);
|
|
21
|
+
if (toast.timeout) {
|
|
22
|
+
setTimeout(() => dismissToast(id), toast.timeout);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const dismissToast = (id: number) => {
|
|
27
|
+
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<ToastContext.Provider value={{ toasts, addToast, dismissToast }}>
|
|
32
|
+
{children}
|
|
33
|
+
</ToastContext.Provider>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const useToastContext = (): ToastContextProps => {
|
|
38
|
+
const context = useContext(ToastContext);
|
|
39
|
+
if (!context) {
|
|
40
|
+
throw new Error("useToastContext must be used within a ToastProvider");
|
|
41
|
+
}
|
|
42
|
+
return context;
|
|
43
|
+
};
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
"use client";
|
|
9
8
|
import React from "react";
|
|
10
|
-
import {
|
|
9
|
+
import { useToastContext } from "./ToastContext";
|
|
11
10
|
import Toast from "./Toast";
|
|
12
11
|
import { ToastPosition } from "./types";
|
|
13
12
|
|
|
@@ -23,7 +22,7 @@ const positionClasses: Record<ToastPosition, string> = {
|
|
|
23
22
|
};
|
|
24
23
|
|
|
25
24
|
export const Toasts: React.FC<ToastsProps> = ({ position = "top-right" }) => {
|
|
26
|
-
const { toasts } =
|
|
25
|
+
const { toasts } = useToastContext();
|
|
27
26
|
|
|
28
27
|
return (
|
|
29
28
|
<section
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ToastOptions } from "./types";
|
|
2
|
-
import {
|
|
2
|
+
import { useToastContext } from "./ToastContext";
|
|
3
3
|
|
|
4
4
|
const useToast = () => {
|
|
5
|
-
const { addToast, dismissToast } =
|
|
5
|
+
const { addToast, dismissToast } = useToastContext();
|
|
6
6
|
|
|
7
7
|
return {
|
|
8
8
|
addToast: ({
|
package/source/globalStyle.ts
CHANGED
|
@@ -7,3 +7,7 @@ export const popUp = {
|
|
|
7
7
|
"pop-up-style":
|
|
8
8
|
"w-full absolute overflow-y-auto border px-2 rounded-lg mt-[1px] scrollbar bg-white z-50 scrollbar h-auto dark:bg-black dark:text-white animate-fade-down animate-once animate-duration-200",
|
|
9
9
|
};
|
|
10
|
+
|
|
11
|
+
export const iconClass = {
|
|
12
|
+
"grey-common": "h-4 w-4 stroke-gray-500 fill-none dark:stroke-white",
|
|
13
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useCallback, useEffect } from "react";
|
|
2
|
+
|
|
3
|
+
export const useClickOutside = <T extends HTMLElement>(
|
|
4
|
+
ref: React.RefObject<T | null>,
|
|
5
|
+
callback: () => void
|
|
6
|
+
) => {
|
|
7
|
+
const handleClickOutside = useCallback(
|
|
8
|
+
(event: MouseEvent) => {
|
|
9
|
+
if (ref.current && !ref.current.contains(event.target as Node)) {
|
|
10
|
+
callback();
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
[callback, ref]
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
18
|
+
return () => {
|
|
19
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
20
|
+
};
|
|
21
|
+
}, [handleClickOutside]);
|
|
22
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useState, useCallback } from "react";
|
|
2
|
+
import { useSelectState } from "./useSelectState";
|
|
3
|
+
import { useSelectData } from "./useSelectData";
|
|
4
|
+
import type { ItemsProps } from "./type";
|
|
5
|
+
|
|
6
|
+
export const useMultiSelectState = (
|
|
7
|
+
items: ItemsProps[] | string | undefined,
|
|
8
|
+
selectedItems: string[] = [],
|
|
9
|
+
lazy = false,
|
|
10
|
+
onSelect?: (selectedItems: string[]) => void
|
|
11
|
+
) => {
|
|
12
|
+
const [selected, setSelected] = useState<string[]>(selectedItems);
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
showPopover,
|
|
16
|
+
setShowPopover,
|
|
17
|
+
workingDataSource,
|
|
18
|
+
setWorkingDataSource,
|
|
19
|
+
searchTerm,
|
|
20
|
+
setSearchTerm,
|
|
21
|
+
togglePopover,
|
|
22
|
+
} = useSelectState();
|
|
23
|
+
|
|
24
|
+
const { filteredItems, getSelectItems } = useSelectData(
|
|
25
|
+
items,
|
|
26
|
+
setWorkingDataSource,
|
|
27
|
+
workingDataSource,
|
|
28
|
+
searchTerm,
|
|
29
|
+
lazy,
|
|
30
|
+
undefined
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const handleSelect = useCallback(
|
|
34
|
+
(value: string) => {
|
|
35
|
+
const newSelected = selected.includes(value)
|
|
36
|
+
? selected.filter((item) => item !== value)
|
|
37
|
+
: [...selected, value];
|
|
38
|
+
setSelected(newSelected);
|
|
39
|
+
onSelect?.(newSelected);
|
|
40
|
+
},
|
|
41
|
+
[selected, onSelect]
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const clearSelected = useCallback(() => {
|
|
45
|
+
setSelected([]);
|
|
46
|
+
setShowPopover(false);
|
|
47
|
+
setSearchTerm("");
|
|
48
|
+
onSelect?.([]);
|
|
49
|
+
}, [setShowPopover, setSearchTerm, onSelect]);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
showPopover,
|
|
53
|
+
setShowPopover,
|
|
54
|
+
workingDataSource,
|
|
55
|
+
filteredItems,
|
|
56
|
+
selected,
|
|
57
|
+
searchTerm,
|
|
58
|
+
setSearchTerm,
|
|
59
|
+
handleSelect,
|
|
60
|
+
togglePopover,
|
|
61
|
+
clearSelected,
|
|
62
|
+
getSelectItems,
|
|
63
|
+
};
|
|
64
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo } from "react";
|
|
2
|
+
import { getSourceData } from "../../utils";
|
|
3
|
+
import type { ItemsProps } from "./type";
|
|
4
|
+
|
|
5
|
+
export const useSelectData = (
|
|
6
|
+
items: ItemsProps[] | string | undefined,
|
|
7
|
+
setWorkingDataSource: (data: ItemsProps[]) => void,
|
|
8
|
+
workingDataSource: ItemsProps[],
|
|
9
|
+
searchTerm: string,
|
|
10
|
+
lazy: boolean,
|
|
11
|
+
selectedItem?: string
|
|
12
|
+
) => {
|
|
13
|
+
const getSelectItems = useCallback(
|
|
14
|
+
async (itemsApi: string) => {
|
|
15
|
+
const itemsData = await getSourceData(itemsApi);
|
|
16
|
+
setWorkingDataSource(itemsData.sourcedata);
|
|
17
|
+
},
|
|
18
|
+
[setWorkingDataSource]
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!items) {
|
|
23
|
+
setWorkingDataSource([]);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (Array.isArray(items)) {
|
|
28
|
+
setWorkingDataSource(items);
|
|
29
|
+
} else if (typeof items === "string") {
|
|
30
|
+
getSelectItems(items);
|
|
31
|
+
}
|
|
32
|
+
}, [items, getSelectItems, setWorkingDataSource]);
|
|
33
|
+
|
|
34
|
+
const selectedDisplay = useMemo(() => {
|
|
35
|
+
if (selectedItem && workingDataSource.length > 0) {
|
|
36
|
+
const selected = workingDataSource.find(
|
|
37
|
+
(item) => item.value === selectedItem
|
|
38
|
+
);
|
|
39
|
+
return selected ? selected.label : "";
|
|
40
|
+
}
|
|
41
|
+
return "";
|
|
42
|
+
}, [selectedItem, workingDataSource]);
|
|
43
|
+
|
|
44
|
+
const filteredItems = useMemo(() => {
|
|
45
|
+
if (!searchTerm || lazy) return workingDataSource;
|
|
46
|
+
return workingDataSource.filter((item) =>
|
|
47
|
+
Object.values(item).some((val) =>
|
|
48
|
+
val.toString().toLowerCase().includes(searchTerm.toLowerCase())
|
|
49
|
+
)
|
|
50
|
+
);
|
|
51
|
+
}, [workingDataSource, searchTerm, lazy]);
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
getSelectItems,
|
|
55
|
+
selectedDisplay,
|
|
56
|
+
filteredItems,
|
|
57
|
+
};
|
|
58
|
+
};
|