next-helios-fe 1.0.0
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/@types/index.d.ts +2 -0
- package/dist/components/button/index.d.ts +15 -0
- package/dist/components/calendar/big-calendar/event.d.ts +7 -0
- package/dist/components/calendar/big-calendar/index.d.ts +14 -0
- package/dist/components/calendar/big-calendar/toolbar.d.ts +8 -0
- package/dist/components/calendar/calendar/index.d.ts +11 -0
- package/dist/components/chart/index.d.ts +17 -0
- package/dist/components/chip/index.d.ts +12 -0
- package/dist/components/content-container/accordion/index.d.ts +10 -0
- package/dist/components/content-container/accordion/item.d.ts +6 -0
- package/dist/components/content-container/card.d.ts +10 -0
- package/dist/components/content-container/carousel.d.ts +10 -0
- package/dist/components/content-container/drawer.d.ts +8 -0
- package/dist/components/content-container/modal/index.d.ts +21 -0
- package/dist/components/content-container/sortable/index.d.ts +14 -0
- package/dist/components/content-container/sortable/item.d.ts +8 -0
- package/dist/components/content-container/sortable/knob.d.ts +5 -0
- package/dist/components/content-container/tab/index.d.ts +17 -0
- package/dist/components/content-container/tab/window.d.ts +5 -0
- package/dist/components/data-tree/index.d.ts +6 -0
- package/dist/components/dialog/index.d.ts +16 -0
- package/dist/components/dropdown/header.d.ts +5 -0
- package/dist/components/dropdown/index.d.ts +15 -0
- package/dist/components/dropdown/item.d.ts +7 -0
- package/dist/components/form/index.d.ts +44 -0
- package/dist/components/form/input/checkbox.d.ts +9 -0
- package/dist/components/form/input/color.d.ts +10 -0
- package/dist/components/form/input/email.d.ts +9 -0
- package/dist/components/form/input/file.d.ts +14 -0
- package/dist/components/form/input/number.d.ts +9 -0
- package/dist/components/form/input/password.d.ts +9 -0
- package/dist/components/form/input/radio.d.ts +9 -0
- package/dist/components/form/input/range.d.ts +9 -0
- package/dist/components/form/input/search.d.ts +8 -0
- package/dist/components/form/input/text.d.ts +9 -0
- package/dist/components/form/input/time.d.ts +9 -0
- package/dist/components/form/other/autocomplete.d.ts +15 -0
- package/dist/components/form/other/multipleSelect.d.ts +23 -0
- package/dist/components/form/other/phoneNumber.d.ts +10 -0
- package/dist/components/form/other/pin.d.ts +10 -0
- package/dist/components/form/other/secret.d.ts +11 -0
- package/dist/components/form/other/select.d.ts +15 -0
- package/dist/components/form/other/textarea.d.ts +9 -0
- package/dist/components/index.d.ts +19 -0
- package/dist/components/map/index.d.ts +13 -0
- package/dist/components/map/marker.d.ts +8 -0
- package/dist/components/syntax-highlighter/index.d.ts +9 -0
- package/dist/components/table/action.d.ts +5 -0
- package/dist/components/table/index.d.ts +24 -0
- package/dist/components/timeline/index.d.ts +10 -0
- package/dist/components/timeline/item.d.ts +7 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.LICENSE.txt +62 -0
- package/package.json +72 -0
- package/src/components/button/index.tsx +74 -0
- package/src/components/calendar/big-calendar/event.tsx +46 -0
- package/src/components/calendar/big-calendar/index.tsx +102 -0
- package/src/components/calendar/big-calendar/toolbar.tsx +98 -0
- package/src/components/calendar/calendar/index.tsx +26 -0
- package/src/components/chart/index.tsx +121 -0
- package/src/components/chip/index.tsx +47 -0
- package/src/components/content-container/accordion/index.tsx +28 -0
- package/src/components/content-container/accordion/item.tsx +40 -0
- package/src/components/content-container/card.tsx +21 -0
- package/src/components/content-container/carousel.tsx +35 -0
- package/src/components/content-container/drawer.tsx +78 -0
- package/src/components/content-container/modal/index.tsx +127 -0
- package/src/components/content-container/sortable/index.tsx +47 -0
- package/src/components/content-container/sortable/item.tsx +20 -0
- package/src/components/content-container/sortable/knob.tsx +11 -0
- package/src/components/content-container/tab/index.tsx +94 -0
- package/src/components/content-container/tab/window.tsx +10 -0
- package/src/components/data-tree/index.tsx +60 -0
- package/src/components/dialog/index.tsx +88 -0
- package/src/components/dropdown/header.tsx +11 -0
- package/src/components/dropdown/index.tsx +69 -0
- package/src/components/dropdown/item.tsx +22 -0
- package/src/components/form/index.tsx +81 -0
- package/src/components/form/input/checkbox.tsx +49 -0
- package/src/components/form/input/color.tsx +104 -0
- package/src/components/form/input/email.tsx +40 -0
- package/src/components/form/input/file.tsx +189 -0
- package/src/components/form/input/number.tsx +93 -0
- package/src/components/form/input/password.tsx +57 -0
- package/src/components/form/input/radio.tsx +49 -0
- package/src/components/form/input/range.tsx +67 -0
- package/src/components/form/input/search.tsx +50 -0
- package/src/components/form/input/text.tsx +39 -0
- package/src/components/form/input/time.tsx +315 -0
- package/src/components/form/other/autocomplete.tsx +199 -0
- package/src/components/form/other/multipleSelect.tsx +211 -0
- package/src/components/form/other/phoneNumber.tsx +1668 -0
- package/src/components/form/other/pin.tsx +56 -0
- package/src/components/form/other/secret.tsx +74 -0
- package/src/components/form/other/select.tsx +187 -0
- package/src/components/form/other/textarea.tsx +44 -0
- package/src/components/index.ts +29 -0
- package/src/components/map/index.tsx +72 -0
- package/src/components/map/marker.tsx +40 -0
- package/src/components/syntax-highlighter/index.tsx +45 -0
- package/src/components/table/action.tsx +22 -0
- package/src/components/table/index.tsx +431 -0
- package/src/components/timeline/index.tsx +28 -0
- package/src/components/timeline/item.tsx +25 -0
- package/src/index.css +1 -0
- package/src/index.ts +3 -0
- package/src/styles/big-calendar.scss +810 -0
- package/src/styles/calendar.scss +195 -0
- package/src/styles/index.css +2 -0
- package/tsconfig.json +17 -0
- package/webpack.config.js +35 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React, { useState, useEffect, useMemo } from "react";
|
|
3
|
+
import { Icon } from "@iconify/react";
|
|
4
|
+
import { Form, Button, Dropdown } from "../../components";
|
|
5
|
+
import { exportToExcel } from "react-json-to-excel";
|
|
6
|
+
import dayjs from "dayjs";
|
|
7
|
+
import { Action, type ActionProps } from "./action";
|
|
8
|
+
|
|
9
|
+
interface TableProps {
|
|
10
|
+
title?: string;
|
|
11
|
+
header: { title: string; key: string }[];
|
|
12
|
+
data: any[];
|
|
13
|
+
options?: {
|
|
14
|
+
hideToolbar?: boolean;
|
|
15
|
+
hideNumber?: boolean;
|
|
16
|
+
checkbox?: boolean;
|
|
17
|
+
height?: "full" | "fit" | "20" | "40" | "80";
|
|
18
|
+
maxRow?: 10 | 20 | 50 | 100;
|
|
19
|
+
border?: boolean;
|
|
20
|
+
};
|
|
21
|
+
actions?: (e: any) => React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface TableComponentProps extends React.FC<TableProps> {
|
|
25
|
+
Action: React.FC<ActionProps>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const Table: TableComponentProps = ({
|
|
29
|
+
title,
|
|
30
|
+
header,
|
|
31
|
+
data,
|
|
32
|
+
options,
|
|
33
|
+
actions,
|
|
34
|
+
}) => {
|
|
35
|
+
const [search, setSearch] = useState<string>("");
|
|
36
|
+
const [filter, setFilter] = useState<any[]>([]);
|
|
37
|
+
const [maxRow, setMaxRow] = useState<number>(options?.maxRow || 10);
|
|
38
|
+
const [page, setPage] = useState<number>(1);
|
|
39
|
+
const [selected, setSelected] = useState<number[]>([]);
|
|
40
|
+
const [sortBy, setSortBy] = useState<string[]>(["", ""]);
|
|
41
|
+
const [excluded, setExcluded] = useState<string[]>([]);
|
|
42
|
+
|
|
43
|
+
const height =
|
|
44
|
+
options?.height === "fit"
|
|
45
|
+
? "h-fit"
|
|
46
|
+
: options?.height === "20"
|
|
47
|
+
? "h-20"
|
|
48
|
+
: options?.height === "40"
|
|
49
|
+
? "h-40"
|
|
50
|
+
: options?.height === "80"
|
|
51
|
+
? "h-80"
|
|
52
|
+
: "flex-1 h-full";
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
setFilter(
|
|
56
|
+
header.map((item) => {
|
|
57
|
+
return { key: item.key, value: "" };
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
}, [header]);
|
|
61
|
+
|
|
62
|
+
const filteredData = useMemo(() => {
|
|
63
|
+
return data
|
|
64
|
+
?.filter((item) => {
|
|
65
|
+
if (search === "") {
|
|
66
|
+
return item;
|
|
67
|
+
} else if (
|
|
68
|
+
header.some((headerItem) => {
|
|
69
|
+
if (headerItem.title !== header[1].title) {
|
|
70
|
+
return item[headerItem.key as keyof typeof item]
|
|
71
|
+
?.toString()
|
|
72
|
+
.toLowerCase()
|
|
73
|
+
.includes(search.toLowerCase());
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
) {
|
|
77
|
+
return item;
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
?.filter((item) => {
|
|
81
|
+
return filter.every((filterItem) => {
|
|
82
|
+
return item[filterItem.key as keyof typeof item]
|
|
83
|
+
.toString()
|
|
84
|
+
.toLowerCase()
|
|
85
|
+
.includes(filterItem.value.toLowerCase());
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}, [data, filter, search]);
|
|
89
|
+
|
|
90
|
+
const headerArr = header
|
|
91
|
+
?.filter((item) => !excluded.includes(item.key))
|
|
92
|
+
?.map((item) => {
|
|
93
|
+
return (
|
|
94
|
+
<th
|
|
95
|
+
key={item.key}
|
|
96
|
+
className="px-4 py-2 bg-secondary-bg font-medium text-left whitespace-nowrap"
|
|
97
|
+
>
|
|
98
|
+
<div className="flex flex-col">
|
|
99
|
+
<button
|
|
100
|
+
type="button"
|
|
101
|
+
className="group/header flex justify-between items-center gap-4 w-full"
|
|
102
|
+
onClick={() => {
|
|
103
|
+
setSortBy([
|
|
104
|
+
sortBy[0] !== item.key
|
|
105
|
+
? item.key
|
|
106
|
+
: sortBy[0] === item.key && sortBy[1] === "asc"
|
|
107
|
+
? item.key
|
|
108
|
+
: "",
|
|
109
|
+
sortBy[0] !== item.key
|
|
110
|
+
? "asc"
|
|
111
|
+
: sortBy[0] === item.key && sortBy[1] === "asc"
|
|
112
|
+
? "desc"
|
|
113
|
+
: "",
|
|
114
|
+
]);
|
|
115
|
+
}}
|
|
116
|
+
>
|
|
117
|
+
{item.title}
|
|
118
|
+
<Icon
|
|
119
|
+
icon={`mi:${
|
|
120
|
+
item.key === sortBy[0] && sortBy[1] === "asc"
|
|
121
|
+
? "arrow-up"
|
|
122
|
+
: item.key === sortBy[0] && sortBy[1] === "desc"
|
|
123
|
+
? "arrow-down"
|
|
124
|
+
: "sort"
|
|
125
|
+
}`}
|
|
126
|
+
className={`group-hover/header:visible ${
|
|
127
|
+
item.key === sortBy[0] ? "visible" : "invisible"
|
|
128
|
+
}`}
|
|
129
|
+
/>
|
|
130
|
+
</button>
|
|
131
|
+
<input
|
|
132
|
+
type="search"
|
|
133
|
+
className="w-full px-0 pt-0 pb-0.5 border-default border-t-0 border-b border-x-0 bg-secondary-bg text-sm font-normal placeholder:duration-300 placeholder:translate-x-0 [&::-webkit-search-cancel-button]:appearance-none focus:placeholder:translate-x-1 placeholder:text-slate-300 focus:outline-none focus:ring-0 focus:border-primary-dark disabled:bg-secondary-light disabled:text-slate-400"
|
|
134
|
+
value={
|
|
135
|
+
filter.find((filterItem) => filterItem.key === item.key)?.value
|
|
136
|
+
}
|
|
137
|
+
onChange={(e) => {
|
|
138
|
+
setFilter(
|
|
139
|
+
filter.map((filterItem) => {
|
|
140
|
+
return filterItem.key === item.key
|
|
141
|
+
? { ...filterItem, value: e.target.value }
|
|
142
|
+
: filterItem;
|
|
143
|
+
})
|
|
144
|
+
);
|
|
145
|
+
}}
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
</th>
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const dataArr = filteredData
|
|
153
|
+
?.sort((a, b) => {
|
|
154
|
+
if (sortBy[1] === "asc") {
|
|
155
|
+
return a[sortBy[0] as keyof typeof a] > b[sortBy[0] as keyof typeof b]
|
|
156
|
+
? 1
|
|
157
|
+
: -1;
|
|
158
|
+
} else if (sortBy[1] === "desc") {
|
|
159
|
+
return a[sortBy[0] as keyof typeof a] < b[sortBy[0] as keyof typeof b]
|
|
160
|
+
? 1
|
|
161
|
+
: -1;
|
|
162
|
+
} else {
|
|
163
|
+
return a.id > b.id ? 1 : -1;
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
?.slice((page - 1) * maxRow, page * maxRow)
|
|
167
|
+
?.map((item, index) => {
|
|
168
|
+
return (
|
|
169
|
+
<tr key={item.id}>
|
|
170
|
+
{options?.checkbox && (
|
|
171
|
+
<td className="sticky left-0 w-8 px-4 py-1 border-b bg-secondary-bg">
|
|
172
|
+
<Form.Checkbox
|
|
173
|
+
options={{ disableHover: true }}
|
|
174
|
+
checked={selected.includes(item.id)}
|
|
175
|
+
onChange={(e) => {
|
|
176
|
+
if (selected.includes(item.id)) {
|
|
177
|
+
setSelected(selected.filter((prev) => prev !== item.id));
|
|
178
|
+
} else {
|
|
179
|
+
setSelected([...selected, item.id]);
|
|
180
|
+
}
|
|
181
|
+
}}
|
|
182
|
+
/>
|
|
183
|
+
</td>
|
|
184
|
+
)}
|
|
185
|
+
{!options?.hideNumber && (
|
|
186
|
+
<td className="sticky left-0 px-4 py-1 border-b bg-secondary-bg text-center">
|
|
187
|
+
{(page - 1) * maxRow + index + 1}
|
|
188
|
+
</td>
|
|
189
|
+
)}
|
|
190
|
+
{header
|
|
191
|
+
?.filter((headerItem) => !excluded.includes(headerItem.key))
|
|
192
|
+
?.map((headerItem) => {
|
|
193
|
+
return (
|
|
194
|
+
<td
|
|
195
|
+
key={headerItem.key}
|
|
196
|
+
className="px-4 py-1 border-b bg-secondary-bg whitespace-nowrap"
|
|
197
|
+
>
|
|
198
|
+
{item[headerItem.key as keyof typeof item]}
|
|
199
|
+
</td>
|
|
200
|
+
);
|
|
201
|
+
})}
|
|
202
|
+
{actions && (
|
|
203
|
+
<td className="px-4 py-1 border-b bg-secondary-bg text-center">
|
|
204
|
+
{actions(item)}
|
|
205
|
+
</td>
|
|
206
|
+
)}
|
|
207
|
+
</tr>
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<div className="flex flex-col gap-6 h-full">
|
|
213
|
+
{!options?.hideToolbar && (
|
|
214
|
+
<div className="flex justify-between items-center gap-4 w-full h-fit">
|
|
215
|
+
<span className="text-lg">{title}</span>
|
|
216
|
+
<div className="flex items-center gap-4">
|
|
217
|
+
<Dropdown
|
|
218
|
+
trigger={
|
|
219
|
+
<button
|
|
220
|
+
type="button"
|
|
221
|
+
className="px-2 py-2 rounded-full hover:bg-secondary-light"
|
|
222
|
+
>
|
|
223
|
+
<Icon icon="mage:filter" className="text-xl" />
|
|
224
|
+
</button>
|
|
225
|
+
}
|
|
226
|
+
>
|
|
227
|
+
{header.map((item) => {
|
|
228
|
+
return (
|
|
229
|
+
<Dropdown.Item
|
|
230
|
+
key={item.key}
|
|
231
|
+
onClick={() => {
|
|
232
|
+
if (excluded.includes(item.key)) {
|
|
233
|
+
setExcluded(
|
|
234
|
+
excluded.filter((prev) => prev !== item.key)
|
|
235
|
+
);
|
|
236
|
+
} else {
|
|
237
|
+
setExcluded([...excluded, item.key]);
|
|
238
|
+
}
|
|
239
|
+
}}
|
|
240
|
+
>
|
|
241
|
+
<Form.Checkbox
|
|
242
|
+
options={{ disableHover: true }}
|
|
243
|
+
label={item.title}
|
|
244
|
+
checked={!excluded.includes(item.key)}
|
|
245
|
+
/>
|
|
246
|
+
</Dropdown.Item>
|
|
247
|
+
);
|
|
248
|
+
})}
|
|
249
|
+
</Dropdown>
|
|
250
|
+
<Form.Search
|
|
251
|
+
options={{ width: "fit" }}
|
|
252
|
+
placeholder="search.."
|
|
253
|
+
value={search}
|
|
254
|
+
onChange={(e) => {
|
|
255
|
+
setSearch(e.target.value);
|
|
256
|
+
}}
|
|
257
|
+
/>
|
|
258
|
+
<Button
|
|
259
|
+
options={{ variant: "primary", width: "fit" }}
|
|
260
|
+
onClick={() => {
|
|
261
|
+
exportToExcel(
|
|
262
|
+
data,
|
|
263
|
+
`${title}-data_${dayjs().format("YYYY-MM-DD_HH-mm-ss")}`
|
|
264
|
+
);
|
|
265
|
+
}}
|
|
266
|
+
>
|
|
267
|
+
Export
|
|
268
|
+
</Button>
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
)}
|
|
272
|
+
<div
|
|
273
|
+
className={`overflow-auto ${height} ${
|
|
274
|
+
options?.border && "border rounded-md"
|
|
275
|
+
}`}
|
|
276
|
+
>
|
|
277
|
+
<table className="w-full text-sm overflow-x-auto">
|
|
278
|
+
<thead className="sticky top-0 z-10 border-b">
|
|
279
|
+
<tr>
|
|
280
|
+
{options?.checkbox && (
|
|
281
|
+
<th className="sticky left-0 w-8 px-4 py-2 bg-secondary-bg">
|
|
282
|
+
<div className="flex flex-col">
|
|
283
|
+
<Form.Checkbox
|
|
284
|
+
options={{ disableHover: true }}
|
|
285
|
+
checked={selected.length === data.length}
|
|
286
|
+
onChange={(e) => {
|
|
287
|
+
if (selected.length === data.length) {
|
|
288
|
+
setSelected([]);
|
|
289
|
+
} else {
|
|
290
|
+
setSelected(data.map((item) => item.id));
|
|
291
|
+
}
|
|
292
|
+
}}
|
|
293
|
+
/>
|
|
294
|
+
<div className="invisible w-0 overflow-hidden">
|
|
295
|
+
<input
|
|
296
|
+
type="search"
|
|
297
|
+
className="w-full px-0 pt-0 pb-0.5 border-default border-t-0 border-b border-x-0 bg-secondary-bg text-sm font-normal placeholder:duration-300 placeholder:translate-x-0 [&::-webkit-search-cancel-button]:appearance-none focus:placeholder:translate-x-1 placeholder:text-slate-300 focus:outline-none focus:ring-0 focus:border-primary-dark disabled:bg-secondary-light disabled:text-slate-400"
|
|
298
|
+
/>
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
</th>
|
|
302
|
+
)}
|
|
303
|
+
{!options?.hideNumber && (
|
|
304
|
+
<th className="sticky left-0 w-min px-4 py-2 bg-secondary-bg font-medium text-center whitespace-nowrap">
|
|
305
|
+
<div className="flex flex-col">
|
|
306
|
+
<span>NO.</span>
|
|
307
|
+
<div className="invisible w-0 overflow-hidden">
|
|
308
|
+
<input
|
|
309
|
+
type="search"
|
|
310
|
+
className="w-full px-0 pt-0 pb-0.5 border-default border-t-0 border-b border-x-0 bg-secondary-bg text-sm font-normal placeholder:duration-300 placeholder:translate-x-0 [&::-webkit-search-cancel-button]:appearance-none focus:placeholder:translate-x-1 placeholder:text-slate-300 focus:outline-none focus:ring-0 focus:border-primary-dark disabled:bg-secondary-light disabled:text-slate-400"
|
|
311
|
+
/>
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
</th>
|
|
315
|
+
)}
|
|
316
|
+
{headerArr}
|
|
317
|
+
{actions && (
|
|
318
|
+
<th className="w-min px-4 py-2 bg-secondary-bg font-medium text-center">
|
|
319
|
+
<div className="flex flex-col">
|
|
320
|
+
<span>Actions</span>
|
|
321
|
+
<div className="invisible w-0 overflow-hidden">
|
|
322
|
+
<input
|
|
323
|
+
type="search"
|
|
324
|
+
className="w-full px-0 pt-0 pb-0.5 border-default border-t-0 border-b border-x-0 bg-secondary-bg text-sm font-normal placeholder:duration-300 placeholder:translate-x-0 [&::-webkit-search-cancel-button]:appearance-none focus:placeholder:translate-x-1 placeholder:text-slate-300 focus:outline-none focus:ring-0 focus:border-primary-dark disabled:bg-secondary-light disabled:text-slate-400"
|
|
325
|
+
/>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
</th>
|
|
329
|
+
)}
|
|
330
|
+
</tr>
|
|
331
|
+
</thead>
|
|
332
|
+
<tbody>{dataArr}</tbody>
|
|
333
|
+
</table>
|
|
334
|
+
</div>
|
|
335
|
+
{!options?.hideToolbar && (
|
|
336
|
+
<div className="flex justify-between items-end gap-2 w-full h-fit">
|
|
337
|
+
<div className="!text-sm">
|
|
338
|
+
<Form.Select
|
|
339
|
+
options={{
|
|
340
|
+
width: "fit",
|
|
341
|
+
height: "short",
|
|
342
|
+
}}
|
|
343
|
+
label="Data per page:"
|
|
344
|
+
menus={[
|
|
345
|
+
{ label: "10", value: "10" },
|
|
346
|
+
{ label: "20", value: "20" },
|
|
347
|
+
{ label: "50", value: "50" },
|
|
348
|
+
{ label: "100", value: "100" },
|
|
349
|
+
]}
|
|
350
|
+
value={maxRow.toString()}
|
|
351
|
+
onChange={(e) => {
|
|
352
|
+
setMaxRow(Number(e.target.value));
|
|
353
|
+
}}
|
|
354
|
+
/>
|
|
355
|
+
</div>
|
|
356
|
+
<span className="hidden md:block text-sm text-slate-400">{`Showing ${
|
|
357
|
+
(page - 1) * maxRow + 1
|
|
358
|
+
} to ${
|
|
359
|
+
page * maxRow > data.length ? data.length : page * maxRow
|
|
360
|
+
} of ${data.length} entries`}</span>
|
|
361
|
+
<div className="flex items-center gap-2 text-xs font-light">
|
|
362
|
+
<button
|
|
363
|
+
type="button"
|
|
364
|
+
className="flex justify-center items-center border rounded-md min-w-9 h-9 bg-secondary-bg hover:bg-secondary-light disabled:bg-secondary-light disabled:text-slate-400 disabled:pointer-events-none"
|
|
365
|
+
disabled={page === 1}
|
|
366
|
+
onClick={() => {
|
|
367
|
+
setPage(1);
|
|
368
|
+
}}
|
|
369
|
+
>
|
|
370
|
+
<Icon icon="gravity-ui:chevrons-left" />
|
|
371
|
+
</button>
|
|
372
|
+
<button
|
|
373
|
+
type="button"
|
|
374
|
+
className="hidden md:flex justify-center items-center border rounded-md min-w-9 h-9 bg-secondary-bg hover:bg-secondary-light disabled:bg-secondary-light disabled:text-slate-400 disabled:pointer-events-none"
|
|
375
|
+
disabled={page === 1}
|
|
376
|
+
onClick={() => {
|
|
377
|
+
setPage((prev) => prev - 1);
|
|
378
|
+
}}
|
|
379
|
+
>
|
|
380
|
+
<Icon icon="gravity-ui:chevron-left" />
|
|
381
|
+
</button>
|
|
382
|
+
<div className="flex gap-2 max-w-20 overflow-auto [&::-webkit-scrollbar]:hidden">
|
|
383
|
+
{Array.from({ length: Math.ceil(data.length / maxRow) }).map(
|
|
384
|
+
(_, index) => {
|
|
385
|
+
return (
|
|
386
|
+
<button
|
|
387
|
+
key={index}
|
|
388
|
+
type="button"
|
|
389
|
+
className={`flex justify-center items-center border rounded-md min-w-9 h-9 ${
|
|
390
|
+
page === index + 1
|
|
391
|
+
? "bg-primary-light text-primary"
|
|
392
|
+
: "bg-secondary-bg hover:bg-secondary-light"
|
|
393
|
+
}`}
|
|
394
|
+
onClick={() => {
|
|
395
|
+
setPage(index + 1);
|
|
396
|
+
}}
|
|
397
|
+
>
|
|
398
|
+
{index + 1}
|
|
399
|
+
</button>
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
)}
|
|
403
|
+
</div>
|
|
404
|
+
<button
|
|
405
|
+
type="button"
|
|
406
|
+
className="hidden md:flex justify-center items-center border rounded-md min-w-9 h-9 bg-secondary-bg hover:bg-secondary-light disabled:bg-secondary-light disabled:text-slate-400 disabled:pointer-events-none"
|
|
407
|
+
disabled={data.length <= page * maxRow}
|
|
408
|
+
onClick={() => {
|
|
409
|
+
setPage((prev) => prev + 1);
|
|
410
|
+
}}
|
|
411
|
+
>
|
|
412
|
+
<Icon icon="gravity-ui:chevron-right" />
|
|
413
|
+
</button>
|
|
414
|
+
<button
|
|
415
|
+
type="button"
|
|
416
|
+
className="flex justify-center items-center border rounded-md min-w-9 h-9 bg-secondary-bg hover:bg-secondary-light disabled:bg-secondary-light disabled:text-slate-400 disabled:pointer-events-none"
|
|
417
|
+
disabled={data.length <= page * maxRow}
|
|
418
|
+
onClick={() => {
|
|
419
|
+
setPage(Math.ceil(data.length / maxRow));
|
|
420
|
+
}}
|
|
421
|
+
>
|
|
422
|
+
<Icon icon="gravity-ui:chevrons-right" />
|
|
423
|
+
</button>
|
|
424
|
+
</div>
|
|
425
|
+
</div>
|
|
426
|
+
)}
|
|
427
|
+
</div>
|
|
428
|
+
);
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
Table.Action = Action;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Item, type ItemProps } from "./item";
|
|
4
|
+
|
|
5
|
+
interface TimelineProps {
|
|
6
|
+
children?: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface TimelineComponent extends React.FC<TimelineProps> {
|
|
10
|
+
Item: React.FC<ItemProps>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Timeline: TimelineComponent = ({ children }) => {
|
|
14
|
+
const childrenList = React.Children.toArray(children);
|
|
15
|
+
const itemList = childrenList.filter((child) => {
|
|
16
|
+
return (child as React.ReactElement).type === Timeline.Item;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="space-y-8 relative before:absolute before:inset-0 before:ml-4 before:-translate-x-px md:before:mx-auto md:before:translate-x-0 before:h-full before:w-0.5 before:bg-gradient-to-b before:from-transparent before:via-primary before:to-transparent">
|
|
21
|
+
{itemList.map((item) => {
|
|
22
|
+
return item;
|
|
23
|
+
})}
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
Timeline.Item = Item;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
export interface ItemProps {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
icon?: React.ReactNode;
|
|
7
|
+
isActive?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const Item: React.FC<ItemProps> = ({ children, icon, isActive }) => {
|
|
11
|
+
return (
|
|
12
|
+
<div
|
|
13
|
+
className={`relative flex items-center justify-between md:justify-normal md:odd:flex-row-reverse group ${
|
|
14
|
+
isActive && "is-active"
|
|
15
|
+
}`}
|
|
16
|
+
>
|
|
17
|
+
<div className="flex items-center justify-center w-8 h-8 rounded-full border border-white bg-slate-300 group-[.is-active]:bg-primary group-[.is-active]:text-white shadow shrink-0 md:order-1 md:group-odd:-translate-x-1/2 md:group-even:translate-x-1/2">
|
|
18
|
+
{icon}
|
|
19
|
+
</div>
|
|
20
|
+
<div className="w-[calc(100%-4rem)] md:w-[calc(50%-2rem)]">
|
|
21
|
+
{children}
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
};
|
package/src/index.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import "./styles/index.css";
|
package/src/index.ts
ADDED