nitro-web 0.0.144 → 0.0.145
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.
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { JSX, useState, useCallback, Fragment } from 'react'
|
|
1
|
+
import { JSX, useState, useCallback, Fragment, useMemo, useEffect } from 'react'
|
|
2
2
|
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react'
|
|
3
3
|
import { Checkbox, queryObject, queryString, twMerge } from 'nitro-web'
|
|
4
|
+
import { useLocation, useNavigate } from 'react-router-dom'
|
|
4
5
|
|
|
5
6
|
export type TableRowType = 'row' | 'loading' | 'empty' | 'thead'
|
|
6
7
|
|
|
@@ -78,6 +79,11 @@ export function Table<T extends TableRow>({
|
|
|
78
79
|
'first:border-l last:border-r border-t-0 box-border'
|
|
79
80
|
const [rand] = useState(() => new Date().getTime() + Math.random())
|
|
80
81
|
|
|
82
|
+
const rowsToRender = useMemo(() => {
|
|
83
|
+
// 1) Only show the first row when loading (content hidden), 2) an empty row when there are no records, or all rows
|
|
84
|
+
return rows.length > 0 ? (isLoading ? rows.slice(0, 1) : rows) : [{ _id: '' }] as unknown as T[]
|
|
85
|
+
}, [rows, isLoading])
|
|
86
|
+
|
|
81
87
|
const columns = useMemo(() => {
|
|
82
88
|
const checkboxCol: TableColumn = { value: 'checkbox', label: '', disableSort: true }
|
|
83
89
|
const cols = (generateCheckboxActions ? [checkboxCol, ...columnsProp] : columnsProp).map((col, _i) => ({
|
|
@@ -92,7 +98,7 @@ export function Table<T extends TableRow>({
|
|
|
92
98
|
|
|
93
99
|
const onSelect = useCallback((idOrAll: string, checked: boolean) => {
|
|
94
100
|
setSelectedRowIds((o) => {
|
|
95
|
-
if (idOrAll == 'all' && checked) return (rows ?? []).map(row => row
|
|
101
|
+
if (idOrAll == 'all' && checked) return (rows ?? []).map(row => row._id || '')
|
|
96
102
|
else if (idOrAll == 'all' && !checked) return []
|
|
97
103
|
else if (o.includes(idOrAll) && !checked) return o.filter(id => id != idOrAll)
|
|
98
104
|
else if (!o.includes(idOrAll) && checked) return [...o, idOrAll]
|
|
@@ -106,7 +112,7 @@ export function Table<T extends TableRow>({
|
|
|
106
112
|
}, [])
|
|
107
113
|
|
|
108
114
|
// Reset selected rows when the location changes, or the number of rows changed (e.g. when a row is removed)
|
|
109
|
-
useEffect(() => setSelectedRowIds([]), [location.key, (rows ?? []).map(row => row
|
|
115
|
+
useEffect(() => setSelectedRowIds([]), [location.key, (rows ?? []).map(row => row._id || '').join(',')])
|
|
110
116
|
|
|
111
117
|
// --- Sorting ---
|
|
112
118
|
|
|
@@ -124,8 +130,8 @@ export function Table<T extends TableRow>({
|
|
|
124
130
|
navigate(location.pathname + queryStr, { replace: true })
|
|
125
131
|
}, [location.pathname, query, sort, sortBy])
|
|
126
132
|
|
|
127
|
-
const getColumnPadding = useCallback((j: number, row: T|undefined,
|
|
128
|
-
const sideColor = j == 0 && rowSideColor ? rowSideColor(row,
|
|
133
|
+
const getColumnPadding = useCallback((j: number, row: T|undefined, rowType: TableRowType) => {
|
|
134
|
+
const sideColor = j == 0 && rowSideColor ? rowSideColor(row, rowType) : undefined
|
|
129
135
|
const sideColorPadding = sideColor /*&& rows.length > 0*/ ? sideColor.width + 5 : 0
|
|
130
136
|
const pl = sideColorPadding + (j == 0 ? columnPaddingX : columnGap)
|
|
131
137
|
const pr = j == columns.length - 1 ? columnPaddingX : columnGap
|
|
@@ -214,8 +220,8 @@ export function Table<T extends TableRow>({
|
|
|
214
220
|
</div>
|
|
215
221
|
{/* Tbody rows */}
|
|
216
222
|
{
|
|
217
|
-
|
|
218
|
-
const isSelected = selectedRowIds.includes(row
|
|
223
|
+
rowsToRender.map((row: T, i: number) => {
|
|
224
|
+
const isSelected = selectedRowIds.includes(row._id || '')
|
|
219
225
|
return (
|
|
220
226
|
<div
|
|
221
227
|
key={`${row._id}-${i}`}
|
|
@@ -227,7 +233,8 @@ export function Table<T extends TableRow>({
|
|
|
227
233
|
>
|
|
228
234
|
{
|
|
229
235
|
columns.map((col, j) => {
|
|
230
|
-
const
|
|
236
|
+
const rowType = row._id ? 'row' : isLoading ? 'loading' : 'empty'
|
|
237
|
+
const { pl, pr, sideColor } = getColumnPadding(j, isLoading ? undefined : row, rowType)
|
|
231
238
|
if (col.isHidden) return <Fragment key={j} />
|
|
232
239
|
return (
|
|
233
240
|
<div
|
|
@@ -260,18 +267,31 @@ export function Table<T extends TableRow>({
|
|
|
260
267
|
/>
|
|
261
268
|
}
|
|
262
269
|
{
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
270
|
+
// Rows (content hidden when loading)
|
|
271
|
+
row._id &&
|
|
272
|
+
<div className={isLoading ? 'opacity-0 pointer-events-none' : ''}>
|
|
273
|
+
{
|
|
274
|
+
col.value == 'checkbox'
|
|
275
|
+
? <Checkbox
|
|
276
|
+
size={checkboxSize}
|
|
277
|
+
name={`checkbox-${row._id}`}
|
|
278
|
+
onChange={(e) => onSelect(row?._id || '', e.target.checked)}
|
|
279
|
+
checked={selectedRowIds.includes(row?._id || '')}
|
|
280
|
+
onClick={(e) => e.stopPropagation()}
|
|
281
|
+
hitboxPadding={5}
|
|
282
|
+
className='!m-0 py-[5px]' // py-5 is required for hitbox (restricted to tabel cell height)
|
|
283
|
+
checkboxClassName={twMerge('border-foreground shadow-[0_1px_2px_0px_#0000001c]', checkboxClassName)}
|
|
284
|
+
/>
|
|
285
|
+
: generateTd(col, row, i, i == rows.length - 1)
|
|
286
|
+
}
|
|
287
|
+
</div>
|
|
288
|
+
}
|
|
289
|
+
{
|
|
290
|
+
// Show "loading" or "no records" text in the first column
|
|
291
|
+
j == 0 && (isLoading || !row._id) &&
|
|
292
|
+
<div className={'absolute top-0 h-full flex items-center justify-center text-sm text-gray-500'}>
|
|
293
|
+
{ isLoading ? <>Loading<span className="relative ml-[2px] loading-dots" /></> : 'No records found.' }
|
|
294
|
+
</div>
|
|
275
295
|
}
|
|
276
296
|
</div>
|
|
277
297
|
</div>
|
|
@@ -282,39 +302,6 @@ export function Table<T extends TableRow>({
|
|
|
282
302
|
)
|
|
283
303
|
})
|
|
284
304
|
}
|
|
285
|
-
{
|
|
286
|
-
(isLoading || rows.length == 0) &&
|
|
287
|
-
<div className='table-row relative'>
|
|
288
|
-
{
|
|
289
|
-
columns.map((col, j) => {
|
|
290
|
-
const { pl, pr, sideColor } = getColumnPadding(j, undefined, isLoading ? 'loading' : 'empty')
|
|
291
|
-
return (
|
|
292
|
-
<div
|
|
293
|
-
key={j}
|
|
294
|
-
style={{ height: rowHeightMin, paddingLeft: pl, paddingRight: pr }}
|
|
295
|
-
className={twMerge(_columnClassName, columnClassName, col.className)}
|
|
296
|
-
>
|
|
297
|
-
{
|
|
298
|
-
sideColor &&
|
|
299
|
-
<div
|
|
300
|
-
className={`absolute top-0 left-0 h-full ${sideColor?.className||''}`}
|
|
301
|
-
style={{ width: sideColor.width }}
|
|
302
|
-
/>
|
|
303
|
-
}
|
|
304
|
-
<div
|
|
305
|
-
className={twMerge(
|
|
306
|
-
'absolute top-0 h-full flex items-center justify-center text-sm text-gray-500',
|
|
307
|
-
col.innerClassName
|
|
308
|
-
)}
|
|
309
|
-
>
|
|
310
|
-
{ j == 0 && (isLoading ? <>Loading<span className="relative ml-[2px] loading-dots" /></> : 'No records found.') }
|
|
311
|
-
</div>
|
|
312
|
-
</div>
|
|
313
|
-
)
|
|
314
|
-
})
|
|
315
|
-
}
|
|
316
|
-
</div>
|
|
317
|
-
}
|
|
318
305
|
</div>
|
|
319
306
|
</div>
|
|
320
307
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nitro-web",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.145",
|
|
4
4
|
"repository": "github:boycce/nitro-web",
|
|
5
5
|
"homepage": "https://boycce.github.io/nitro-web/",
|
|
6
6
|
"description": "Nitro is a battle-tested, modular base project to turbocharge your projects, styled using Tailwind 🚀",
|