nitro-web 0.2.3 → 0.2.4
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.
|
@@ -142,7 +142,7 @@ export const Dropdown = forwardRef(function Dropdown({
|
|
|
142
142
|
|
|
143
143
|
function onMouseDown(e: { key: string, preventDefault: Function, target: EventTarget | null }) {
|
|
144
144
|
if (e.key && e.key != 'Enter') return
|
|
145
|
-
if (e.key) e.preventDefault() //
|
|
145
|
+
if (e.key) e.preventDefault() // Enter on a <button> also fires a native click, cancel to avoid toggling twice
|
|
146
146
|
|
|
147
147
|
if (!isHoverable && !menuIsOpen && ((menuToggles || e.key) || !isActive)) {
|
|
148
148
|
if (isActive && preventCloseOnClickChild && dropdownRef.current?.contains(e.target as Node)) return // keep active
|
|
@@ -150,13 +150,20 @@ export const Dropdown = forwardRef(function Dropdown({
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
function onClick(e: React.MouseEvent) {
|
|
154
|
+
e.preventDefault()
|
|
155
|
+
e.stopPropagation()
|
|
156
|
+
}
|
|
157
|
+
|
|
153
158
|
function onItemClick(option: { onClick?: Function, preventCloseOnClick?: boolean }, e: React.MouseEvent) {
|
|
154
159
|
if (option.onClick) option.onClick(e, option)
|
|
155
160
|
if (!menuIsOpen && !option?.preventCloseOnClick) setIsActive(!isActive)
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
return (
|
|
159
|
-
<div
|
|
164
|
+
<div
|
|
165
|
+
ref={dropdownRef}
|
|
166
|
+
css={style}
|
|
160
167
|
class={
|
|
161
168
|
`relative is-${direction || dir}` + // until hovered, show the original direction to prevent scrollbars
|
|
162
169
|
(isHoverable ? ' is-hoverable' : '') +
|
|
@@ -166,28 +173,36 @@ export const Dropdown = forwardRef(function Dropdown({
|
|
|
166
173
|
' nitro-dropdown' +
|
|
167
174
|
(className ? ` ${className}` : '')
|
|
168
175
|
}
|
|
169
|
-
// When the dropdown is inside a link, prevent click propagation
|
|
170
|
-
onClick={(e) => { e.preventDefault() }}
|
|
171
|
-
// When the dropdown is inside a link, prevent auto <a> focus propagation (for the window listener)
|
|
172
|
-
onMouseDown={(e) => { e.preventDefault() }}
|
|
173
|
-
ref={dropdownRef}
|
|
174
|
-
css={style}
|
|
175
176
|
>
|
|
176
177
|
{
|
|
177
178
|
(Array.isArray(children) ? children : [children]).map((el, key) => {
|
|
178
179
|
const onKeyDown = onMouseDown
|
|
179
180
|
if (!el.type) throw new Error('Dropdown component requires a valid child element')
|
|
180
|
-
return cloneElement(el, { key, onMouseDown, onKeyDown })
|
|
181
|
+
return cloneElement(el, { key, onMouseDown, onKeyDown, onClick })
|
|
181
182
|
})
|
|
182
183
|
}
|
|
183
184
|
<ul
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
// Mousedown on non-focusable menu content now focuses the ul instead of escaping to parent link row,
|
|
186
|
+
// so the window 'focus' listener doesn't close the menu early
|
|
187
|
+
tabIndex={-1}
|
|
188
|
+
// Keep mousedown from bubbling to parent row handlers (focus containment is handled by tabIndex above)
|
|
189
|
+
onMouseDown={(e: React.MouseEvent) => e.stopPropagation()}
|
|
190
|
+
onClick={(e: React.MouseEvent) => {
|
|
191
|
+
// Keep menu clicks from reaching parent row click handlers
|
|
192
|
+
e.stopPropagation()
|
|
193
|
+
// Cancel parent <a> row navigation, unless a link INSIDE the menu was clicked
|
|
194
|
+
// (closest() doesn't stop at the ul, so check the matched link is contained within it)
|
|
195
|
+
const link = (e.target as HTMLElement).closest('a[href]')
|
|
196
|
+
if (!link || !e.currentTarget.contains(link)) e.preventDefault()
|
|
197
|
+
}}
|
|
198
|
+
style={{
|
|
199
|
+
minWidth: minWidth,
|
|
200
|
+
maxHeight: maxHeight,
|
|
187
201
|
...(maxHeight ? { overflow: 'auto' } : {}),
|
|
188
202
|
}}
|
|
189
|
-
class={
|
|
190
|
-
|
|
203
|
+
class={twMerge(
|
|
204
|
+
`${menuStyle} ${ready ? 'is-ready' : ''} absolute invisible opacity-0 select-none outline-none min-w-full z-[1] ${menuClassName||''}`
|
|
205
|
+
)}
|
|
191
206
|
>
|
|
192
207
|
{menuContent}
|
|
193
208
|
{
|
|
@@ -26,8 +26,10 @@ export type TableRow = {
|
|
|
26
26
|
export type TableProps<T> = {
|
|
27
27
|
// todo: put the classnames into a single object
|
|
28
28
|
columns: TableColumn[]
|
|
29
|
-
rows: T[]
|
|
30
29
|
generateTd: (col: TableColumn, row: T, i: number, isLast: boolean) => JSX.Element | null
|
|
30
|
+
rows: T[]
|
|
31
|
+
// Optional settings
|
|
32
|
+
addOverflowScroll?: boolean
|
|
31
33
|
generateCheckboxActions?: (selectedRowIds: string[], setSelectedRowIds: (selectedRowIds: string[]) => void) => JSX.Element | null
|
|
32
34
|
headerHeightMin?: number
|
|
33
35
|
rowHeightMin?: number
|
|
@@ -39,7 +41,10 @@ export type TableProps<T> = {
|
|
|
39
41
|
rowLink?: (row: T) => string
|
|
40
42
|
columnGap?: number
|
|
41
43
|
columnPaddingX?: number
|
|
44
|
+
checkboxSize?: number
|
|
45
|
+
// Class names
|
|
42
46
|
className?: string
|
|
47
|
+
scrollContainerClassName?: string
|
|
43
48
|
tableClassName?: string
|
|
44
49
|
rowClassName?: string
|
|
45
50
|
rowClassNameFn?: (row: T, i: number) => string
|
|
@@ -48,8 +53,8 @@ export type TableProps<T> = {
|
|
|
48
53
|
columnSelectedClassName?: string
|
|
49
54
|
columnHeaderClassName?: string
|
|
50
55
|
checkboxClassName?: string
|
|
51
|
-
checkboxSize?: number
|
|
52
56
|
loadingOverlayClassName?: string
|
|
57
|
+
// Loading
|
|
53
58
|
isLoading?:boolean
|
|
54
59
|
loadingMessage?: string
|
|
55
60
|
showLoadingInline?: boolean|JSX.Element
|
|
@@ -57,22 +62,26 @@ export type TableProps<T> = {
|
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
export function Table<T extends TableRow>({
|
|
60
|
-
rows,
|
|
61
65
|
columns: columnsProp,
|
|
62
|
-
generateTd,
|
|
66
|
+
generateTd,
|
|
67
|
+
rows,
|
|
68
|
+
// Optional settings
|
|
69
|
+
addOverflowScroll,
|
|
70
|
+
checkboxSize=16,
|
|
71
|
+
columnGap=11,
|
|
72
|
+
columnPaddingX=11,
|
|
63
73
|
generateCheckboxActions,
|
|
64
74
|
headerHeightMin=40,
|
|
65
|
-
rowHeightMin=48,
|
|
66
75
|
rowContentHeightMax,
|
|
67
|
-
rowLinesMax,
|
|
68
|
-
rowSideColor,
|
|
69
76
|
rowGap=0,
|
|
70
|
-
|
|
77
|
+
rowHeightMin=48,
|
|
78
|
+
rowLinesMax,
|
|
71
79
|
rowLink,
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
rowOnClick,
|
|
81
|
+
rowSideColor,
|
|
74
82
|
// Class names
|
|
75
83
|
className,
|
|
84
|
+
scrollContainerClassName,
|
|
76
85
|
tableClassName,
|
|
77
86
|
rowClassName,
|
|
78
87
|
rowClassNameFn,
|
|
@@ -81,7 +90,6 @@ export function Table<T extends TableRow>({
|
|
|
81
90
|
columnSelectedClassName,
|
|
82
91
|
columnHeaderClassName,
|
|
83
92
|
checkboxClassName,
|
|
84
|
-
checkboxSize=16,
|
|
85
93
|
loadingOverlayClassName,
|
|
86
94
|
// Loading
|
|
87
95
|
isLoading=false,
|
|
@@ -161,190 +169,192 @@ export function Table<T extends TableRow>({
|
|
|
161
169
|
}, [rowSideColor, columnPaddingX, columnGap])
|
|
162
170
|
|
|
163
171
|
return (
|
|
164
|
-
<div
|
|
165
|
-
style={{ marginTop: -rowGap }}
|
|
166
|
-
className={twMerge('overflow-x-auto thin-scrollbar', className)}
|
|
167
|
-
>
|
|
172
|
+
<div className={className}>
|
|
168
173
|
<div
|
|
169
|
-
style={{
|
|
170
|
-
className={twMerge('
|
|
174
|
+
style={{ marginTop: -rowGap, marginBottom: -rowGap }}
|
|
175
|
+
className={twMerge(addOverflowScroll ? 'overflow-x-auto thin-scrollbar' : '', scrollContainerClassName)}
|
|
171
176
|
>
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
{
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
177
|
+
<div
|
|
178
|
+
style={{ borderSpacing: `0 ${rowGap}px` }}
|
|
179
|
+
className={twMerge('table w-full border-separate', showLoadingOverlay ? 'relative' : '', tableClassName)}
|
|
180
|
+
>
|
|
181
|
+
{/* Thead row */}
|
|
182
|
+
<div className="table-row relative">
|
|
183
|
+
{
|
|
184
|
+
columns.map((col, j) => {
|
|
185
|
+
const disableSort = col.disableSort || selectedRowIds.length
|
|
186
|
+
const { pl, pr } = getColumnPadding(j, undefined, 'thead')
|
|
178
187
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
<div
|
|
182
|
-
key={j}
|
|
183
|
-
onClick={disableSort ? undefined : () => onSort(col)}
|
|
184
|
-
style={{ height: headerHeightMin, minWidth: col.minWidth, paddingLeft: pl, paddingRight: pr }}
|
|
185
|
-
className={twMerge(
|
|
186
|
-
_columnClassName,
|
|
187
|
-
'h-auto text-sm font-medium border-t-1',
|
|
188
|
-
disableSort ? '' : 'cursor-pointer select-none',
|
|
189
|
-
getAlignClass(col.align),
|
|
190
|
-
columnClassName,
|
|
191
|
-
columnHeaderClassName,
|
|
192
|
-
columnClassNameFn ? columnClassNameFn(col, undefined, j) : '',
|
|
193
|
-
col.className
|
|
194
|
-
)}
|
|
195
|
-
>
|
|
188
|
+
if (col.isHidden) return <Fragment key={j} />
|
|
189
|
+
return (
|
|
196
190
|
<div
|
|
197
|
-
|
|
191
|
+
key={j}
|
|
192
|
+
onClick={disableSort ? undefined : () => onSort(col)}
|
|
193
|
+
style={{ height: headerHeightMin, minWidth: col.minWidth, paddingLeft: pl, paddingRight: pr }}
|
|
198
194
|
className={twMerge(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
col.
|
|
195
|
+
_columnClassName,
|
|
196
|
+
'h-auto text-sm font-medium border-t-1',
|
|
197
|
+
disableSort ? '' : 'cursor-pointer select-none',
|
|
198
|
+
getAlignClass(col.align),
|
|
199
|
+
columnClassName,
|
|
200
|
+
columnHeaderClassName,
|
|
201
|
+
columnClassNameFn ? columnClassNameFn(col, undefined, j) : '',
|
|
202
|
+
col.className
|
|
203
203
|
)}
|
|
204
|
-
>
|
|
205
|
-
{
|
|
206
|
-
col.value == 'checkbox'
|
|
207
|
-
? <Fragment>
|
|
208
|
-
<Checkbox
|
|
209
|
-
size={checkboxSize}
|
|
210
|
-
name={`checkbox-all-${rand}`}
|
|
211
|
-
hitboxPadding={5}
|
|
212
|
-
checked={rows.length > 0 && selectedRowIds.length === rows.length}
|
|
213
|
-
className='!m-0 py-[5px]' // py-5 is required for hitbox (restricted to tabel cell height)
|
|
214
|
-
checkboxClassName={twMerge('border-foreground shadow-[0_1px_2px_0px_#0000001c]', checkboxClassName)}
|
|
215
|
-
onChange={(e) => onSelect('all', e.target.checked)}
|
|
216
|
-
/>
|
|
217
|
-
<div
|
|
218
|
-
className={`${selectedRowIds.length ? 'block' : 'hidden'} [&>*]:absolute [&>*]:inset-y-0 [&>*]:left-[35px] [&>*]:z-10 whitespace-nowrap`}
|
|
219
|
-
>
|
|
220
|
-
{generateCheckboxActions && generateCheckboxActions(selectedRowIds, setSelectedRowIds)}
|
|
221
|
-
</div>
|
|
222
|
-
</Fragment>
|
|
223
|
-
: <span className={twMerge(
|
|
224
|
-
'flex items-center gap-x-2 transition-opacity',
|
|
225
|
-
selectedRowIds.length ? 'opacity-0' : '',
|
|
226
|
-
getAlignClass(col.align, true)
|
|
227
|
-
)}>
|
|
228
|
-
<span>{col.label}</span>
|
|
229
|
-
{
|
|
230
|
-
(!col.disableSort && sortBy == col.sortByValue)
|
|
231
|
-
? (sort == '1'
|
|
232
|
-
? <ChevronDownIcon class='shrink-0 size-[16px]' />
|
|
233
|
-
: <ChevronUpIcon class='shrink-0 size-[16px]' />
|
|
234
|
-
)
|
|
235
|
-
: col.align == 'left' && <div class='size-[16px] shrink-0' /> // prevent layout shift on sort
|
|
236
|
-
}
|
|
237
|
-
</span>
|
|
238
|
-
}
|
|
239
|
-
</div>
|
|
240
|
-
</div>
|
|
241
|
-
)
|
|
242
|
-
})
|
|
243
|
-
}
|
|
244
|
-
</div>
|
|
245
|
-
{/* Tbody rows */}
|
|
246
|
-
{
|
|
247
|
-
rowsToRender.map((row: T, i: number) => {
|
|
248
|
-
const isSelected = selectedRowIds.includes(row._id || '')
|
|
249
|
-
const Element = (rowLink ? Link : 'div') as React.ElementType
|
|
250
|
-
const extraProps = rowLink ? { to: rowLink(row) } : { onClick: rowOnClick ? () => rowOnClick(row) : undefined }
|
|
251
|
-
return (
|
|
252
|
-
<Element
|
|
253
|
-
{...extraProps}
|
|
254
|
-
key={`${row._id}-${i}`}
|
|
255
|
-
id={`row-${row._id}-${i}`}
|
|
256
|
-
className={twMerge(
|
|
257
|
-
`table-row relative ${(rowOnClick || rowLink) ? 'cursor-pointer' : ''} ${isSelected ? 'is-selected' : ''}`,
|
|
258
|
-
rowClassName,
|
|
259
|
-
rowClassNameFn ? rowClassNameFn(row, i) : ''
|
|
260
|
-
)}
|
|
261
|
-
>
|
|
262
|
-
{columns.map((col, j) => {
|
|
263
|
-
const rowType = row._id ? 'row' : isLoading ? 'loading' : 'empty'
|
|
264
|
-
const { pl, pr, sideColor } = getColumnPadding(j, isLoading ? undefined : row, rowType)
|
|
265
|
-
if (col.isHidden) return <Fragment key={j} />
|
|
266
|
-
return (
|
|
204
|
+
>
|
|
267
205
|
<div
|
|
268
|
-
|
|
269
|
-
style={{ height: rowHeightMin, paddingLeft: pl, paddingRight: pr }}
|
|
206
|
+
style={{ maxHeight: rowContentHeightMax }}
|
|
270
207
|
className={twMerge(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
col.className,
|
|
276
|
-
isSelected ? `bg-gray-50 ${columnSelectedClassName||''}` : ''
|
|
208
|
+
col.value == 'checkbox' ? 'relative' : getLineClampClassName(col.rowLinesMax),
|
|
209
|
+
rowContentHeightMax ? 'overflow-hidden' : '',
|
|
210
|
+
col.overflow ? 'overflow-visible' : '',
|
|
211
|
+
col.innerClassName
|
|
277
212
|
)}
|
|
278
|
-
>
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
213
|
+
>
|
|
214
|
+
{
|
|
215
|
+
col.value == 'checkbox'
|
|
216
|
+
? <Fragment>
|
|
217
|
+
<Checkbox
|
|
218
|
+
size={checkboxSize}
|
|
219
|
+
name={`checkbox-all-${rand}`}
|
|
220
|
+
hitboxPadding={5}
|
|
221
|
+
checked={rows.length > 0 && selectedRowIds.length === rows.length}
|
|
222
|
+
className='!m-0 py-[5px]' // py-5 is required for hitbox (restricted to tabel cell height)
|
|
223
|
+
checkboxClassName={twMerge('border-foreground shadow-[0_1px_2px_0px_#0000001c]', checkboxClassName)}
|
|
224
|
+
onChange={(e) => onSelect('all', e.target.checked)}
|
|
225
|
+
/>
|
|
226
|
+
<div
|
|
227
|
+
className={`${selectedRowIds.length ? 'block' : 'hidden'} [&>*]:absolute [&>*]:inset-y-0 [&>*]:left-[35px] [&>*]:z-10 whitespace-nowrap`}
|
|
228
|
+
>
|
|
229
|
+
{generateCheckboxActions && generateCheckboxActions(selectedRowIds, setSelectedRowIds)}
|
|
230
|
+
</div>
|
|
231
|
+
</Fragment>
|
|
232
|
+
: <span className={twMerge(
|
|
233
|
+
'flex items-center gap-x-2 transition-opacity',
|
|
234
|
+
selectedRowIds.length ? 'opacity-0' : '',
|
|
235
|
+
getAlignClass(col.align, true)
|
|
236
|
+
)}>
|
|
237
|
+
<span>{col.label}</span>
|
|
238
|
+
{
|
|
239
|
+
(!col.disableSort && sortBy == col.sortByValue)
|
|
240
|
+
? (sort == '1'
|
|
241
|
+
? <ChevronDownIcon class='shrink-0 size-[16px]' />
|
|
242
|
+
: <ChevronUpIcon class='shrink-0 size-[16px]' />
|
|
243
|
+
)
|
|
244
|
+
: col.align == 'left' && <div class='size-[16px] shrink-0' /> // prevent layout shift on sort
|
|
245
|
+
}
|
|
246
|
+
</span>
|
|
247
|
+
}
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
)
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
</div>
|
|
254
|
+
{/* Tbody rows */}
|
|
255
|
+
{
|
|
256
|
+
rowsToRender.map((row: T, i: number) => {
|
|
257
|
+
const isSelected = selectedRowIds.includes(row._id || '')
|
|
258
|
+
const Element = (rowLink ? Link : 'div') as React.ElementType
|
|
259
|
+
const extraProps = rowLink ? { to: rowLink(row) } : { onClick: rowOnClick ? () => rowOnClick(row) : undefined }
|
|
260
|
+
return (
|
|
261
|
+
<Element
|
|
262
|
+
{...extraProps}
|
|
263
|
+
key={`${row._id}-${i}`}
|
|
264
|
+
id={`row-${row._id}-${i}`}
|
|
265
|
+
className={twMerge(
|
|
266
|
+
`table-row relative ${(rowOnClick || rowLink) ? 'cursor-pointer' : ''} ${isSelected ? 'is-selected' : ''}`,
|
|
267
|
+
rowClassName,
|
|
268
|
+
rowClassNameFn ? rowClassNameFn(row, i) : ''
|
|
269
|
+
)}
|
|
270
|
+
>
|
|
271
|
+
{columns.map((col, j) => {
|
|
272
|
+
const rowType = row._id ? 'row' : isLoading ? 'loading' : 'empty'
|
|
273
|
+
const { pl, pr, sideColor } = getColumnPadding(j, isLoading ? undefined : row, rowType)
|
|
274
|
+
if (col.isHidden) return <Fragment key={j} />
|
|
275
|
+
return (
|
|
276
|
+
<div
|
|
277
|
+
key={j}
|
|
278
|
+
style={{ height: rowHeightMin, paddingLeft: pl, paddingRight: pr }}
|
|
282
279
|
className={twMerge(
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
col
|
|
280
|
+
_columnClassName,
|
|
281
|
+
getAlignClass(col.align),
|
|
282
|
+
columnClassName,
|
|
283
|
+
columnClassNameFn ? columnClassNameFn(col, row, i) : '',
|
|
284
|
+
col.className,
|
|
285
|
+
isSelected ? `bg-gray-50 ${columnSelectedClassName||''}` : ''
|
|
287
286
|
)}
|
|
288
287
|
>
|
|
289
|
-
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
288
|
+
<div
|
|
289
|
+
// pl:sideColorPadding was originally here
|
|
290
|
+
style={{ maxHeight: rowContentHeightMax }}
|
|
291
|
+
className={twMerge(
|
|
292
|
+
rowContentHeightMax ? 'overflow-hidden' : '',
|
|
293
|
+
getLineClampClassName(col.rowLinesMax),
|
|
294
|
+
col.overflow ? 'overflow-visible' : '',
|
|
295
|
+
col.innerClassName
|
|
296
|
+
)}
|
|
297
|
+
>
|
|
298
|
+
{
|
|
299
|
+
// Side color
|
|
300
|
+
sideColor &&
|
|
301
|
+
<div
|
|
302
|
+
className={`absolute top-0 left-0 h-full ${sideColor?.className||''}`}
|
|
303
|
+
style={{ width: sideColor.width }}
|
|
304
|
+
/>
|
|
305
|
+
}
|
|
306
|
+
{
|
|
307
|
+
// Rows (content hidden when loading inline)
|
|
308
|
+
row._id &&
|
|
309
|
+
<div className={isLoading && showLoadingInline ? 'opacity-0 pointer-events-none' : ''}>
|
|
310
|
+
{
|
|
311
|
+
col.value == 'checkbox'
|
|
312
|
+
? <Checkbox
|
|
313
|
+
size={checkboxSize}
|
|
314
|
+
name={`checkbox-${row._id}`}
|
|
315
|
+
onChange={(e) => onSelect(row?._id || '', e.target.checked)}
|
|
316
|
+
checked={selectedRowIds.includes(row?._id || '')}
|
|
317
|
+
onClick={(e) => e.stopPropagation()}
|
|
318
|
+
hitboxPadding={5}
|
|
319
|
+
className='!m-0 py-[5px]' // py-5 is required for hitbox (restricted to tabel cell height)
|
|
320
|
+
checkboxClassName={twMerge('border-foreground shadow-[0_1px_2px_0px_#0000001c]', checkboxClassName)}
|
|
321
|
+
/>
|
|
322
|
+
: generateTd(col, row, i, i == rows.length - 1)
|
|
323
|
+
}
|
|
324
|
+
</div>
|
|
325
|
+
}
|
|
326
|
+
{
|
|
327
|
+
// Show "no records" or "loading" text in the first column
|
|
328
|
+
j == 0 && (!row._id || isLoading) &&
|
|
329
|
+
<div className={'absolute top-0 h-full flex items-center justify-center gap-3 text-sm text-gray-500'}>
|
|
330
|
+
{
|
|
331
|
+
(!row._id && !isLoading) ? (
|
|
332
|
+
'No records found.'
|
|
333
|
+
) : (!row._id && isLoading && showLoadingInline === true) ? (
|
|
334
|
+
<Fragment>
|
|
335
|
+
<Spinner className="border-gray-500" />
|
|
336
|
+
<LoadingWithDots message={loadingMessage} />
|
|
337
|
+
</Fragment>
|
|
338
|
+
) : (!row._id && isLoading && showLoadingInline) ? (
|
|
339
|
+
showLoadingInline
|
|
340
|
+
) : null
|
|
341
|
+
}
|
|
342
|
+
</div>
|
|
343
|
+
}
|
|
344
|
+
</div>
|
|
335
345
|
</div>
|
|
336
|
-
|
|
337
|
-
)
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
)
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
346
|
+
)
|
|
347
|
+
})}
|
|
348
|
+
</Element>
|
|
349
|
+
)
|
|
350
|
+
})
|
|
351
|
+
}
|
|
352
|
+
{
|
|
353
|
+
(isLoading && showLoadingOverlay === true)
|
|
354
|
+
? <LoadingOverlay className={twMerge('m-[1px]', loadingOverlayClassName)} message={loadingMessage} />
|
|
355
|
+
: (isLoading && showLoadingOverlay) ? showLoadingOverlay : null
|
|
356
|
+
}
|
|
357
|
+
</div>
|
|
348
358
|
</div>
|
|
349
359
|
</div>
|
|
350
360
|
)
|
|
@@ -220,7 +220,10 @@ export function Styleguide({ className, elements, children, currencies, groups }
|
|
|
220
220
|
case 'actions':
|
|
221
221
|
return (
|
|
222
222
|
<Dropdown
|
|
223
|
-
options={[
|
|
223
|
+
options={[
|
|
224
|
+
{ label: 'Set Status', onClick: () => { console.log('set', row._id) } },
|
|
225
|
+
{ label: 'Delete', onClick: () => { console.log('remove', row._id) } },
|
|
226
|
+
]}
|
|
224
227
|
dir={rows.slice(0, perPage).length - 3 < i ? 'top-right' : 'bottom-right'}
|
|
225
228
|
minWidth={100}
|
|
226
229
|
>
|
|
@@ -701,51 +704,64 @@ export function Styleguide({ className, elements, children, currencies, groups }
|
|
|
701
704
|
/>
|
|
702
705
|
</div>
|
|
703
706
|
<Table
|
|
704
|
-
|
|
707
|
+
className="mb-6"
|
|
705
708
|
columns={thead}
|
|
706
|
-
rowSideColor={(row) => ({ className: row?.status == 'pending' ? 'bg-yellow-400' : '', width: 5 })}
|
|
707
709
|
generateCheckboxActions={generateCheckboxActions}
|
|
708
710
|
generateTd={generateTd}
|
|
709
|
-
className
|
|
711
|
+
rowSideColor={(row) => ({ className: row?.status == 'pending' ? 'bg-yellow-400' : '', width: 5 })}
|
|
712
|
+
rows={rows.slice(0, perPage)}
|
|
710
713
|
/>
|
|
711
714
|
<Table
|
|
712
|
-
|
|
715
|
+
className="mb-6"
|
|
713
716
|
columns={thead}
|
|
714
|
-
rowSideColor={(row) => ({ className: row?.status == 'pending' ? 'bg-yellow-400' : '', width: 5 })}
|
|
715
717
|
generateCheckboxActions={generateCheckboxActions}
|
|
716
718
|
generateTd={generateTd}
|
|
717
|
-
className="mb-6"
|
|
718
719
|
isLoading={true}
|
|
720
|
+
rowSideColor={(row) => ({ className: row?.status == 'pending' ? 'bg-yellow-400' : '', width: 5 })}
|
|
721
|
+
rows={[]}
|
|
719
722
|
/>
|
|
720
723
|
<Table
|
|
721
|
-
|
|
724
|
+
className="mb-6"
|
|
722
725
|
columns={thead}
|
|
723
|
-
rowSideColor={(row) => ({ className: row?.status == 'pending' ? 'bg-yellow-400' : '', width: 5 })}
|
|
724
726
|
generateCheckboxActions={generateCheckboxActions}
|
|
725
727
|
generateTd={generateTd}
|
|
726
|
-
className="mb-6"
|
|
727
728
|
isLoading={true}
|
|
728
|
-
|
|
729
|
+
rowSideColor={(row) => ({ className: row?.status == 'pending' ? 'bg-yellow-400' : '', width: 5 })}
|
|
730
|
+
rows={[]}
|
|
729
731
|
showLoadingInline={true}
|
|
732
|
+
showLoadingOverlay={false}
|
|
730
733
|
/>
|
|
731
734
|
<Table
|
|
732
|
-
|
|
735
|
+
className="mb-6"
|
|
733
736
|
columns={thead}
|
|
734
|
-
|
|
737
|
+
generateCheckboxActions={generateCheckboxActions}
|
|
738
|
+
generateTd={generateTd}
|
|
739
|
+
rowLink={(row) => `#/table-link/${row?._id}`}
|
|
740
|
+
rowSideColor={(row) => ({ className: row?.status == 'pending' ? 'bg-yellow-400' : '', width: 5 })}
|
|
741
|
+
rows={rows.slice(0, 1).map(row => ({ ...row, _id: '1_' + row._id }))}
|
|
742
|
+
/>
|
|
743
|
+
<Table
|
|
744
|
+
className="mb-6"
|
|
745
|
+
columns={thead}
|
|
746
|
+
generateCheckboxActions={generateCheckboxActions}
|
|
747
|
+
generateTd={generateTd}
|
|
735
748
|
headerHeightMin={35}
|
|
736
749
|
rowGap={8}
|
|
737
750
|
rowHeightMin={42}
|
|
751
|
+
rowLinesMax={1}
|
|
752
|
+
rowOnClick={useCallback((row: QuoteExample) => {
|
|
753
|
+
console.log('row clicked', row?._id)
|
|
754
|
+
setStore((s) => ({ ...s, message: `Row ${row?._id} clicked` }))
|
|
755
|
+
}, [setStore])}
|
|
738
756
|
rowSideColor={(row) => ({ className: `rounded-l-xl ${statusColors(row?.status as string)}`, width: 10 })}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
className="mb-5"
|
|
743
|
-
tableClassName="rounded-3px"
|
|
744
|
-
rowClassName="[&:hover>div]:bg-gray-50"
|
|
757
|
+
rows={rows.slice(0, 2).map(row => ({ ...row, _id: '2_' + row._id }))}
|
|
758
|
+
|
|
759
|
+
checkboxClassName="rounded-[2px] shadow-none"
|
|
745
760
|
columnClassName="border-t-1 first:rounded-l-xl last:rounded-r-xl"
|
|
746
|
-
columnSelectedClassName="bg-gray-50 border-indigo-300"
|
|
747
761
|
columnHeaderClassName="text-gray-500 text-2xs uppercase border-none"
|
|
748
|
-
|
|
762
|
+
columnSelectedClassName="bg-gray-50 border-indigo-300"
|
|
763
|
+
rowClassName="[&:hover>div]:bg-gray-50"
|
|
764
|
+
tableClassName="rounded-3px"
|
|
749
765
|
/>
|
|
750
766
|
</div>
|
|
751
767
|
)}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nitro-web",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
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 🚀",
|