nitro-web 0.2.2 → 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.
|
@@ -72,7 +72,9 @@ export const Dropdown = forwardRef(function Dropdown({
|
|
|
72
72
|
const pageClick = (event: MouseEvent | FocusEvent) => {
|
|
73
73
|
try {
|
|
74
74
|
// If the active element exists and is clicked outside of the dropdown, toggle the dropdown
|
|
75
|
-
if (dropdownRef.current !== null && !dropdownRef.current.contains(event.target as Node))
|
|
75
|
+
if (dropdownRef.current !== null && !dropdownRef.current.contains(event.target as Node)) {
|
|
76
|
+
setIsActive(!isActive)
|
|
77
|
+
}
|
|
76
78
|
} catch (_e) {
|
|
77
79
|
// Errors throw for contains() when the user clicks off the webpage when open
|
|
78
80
|
setIsActive(!isActive)
|
|
@@ -140,7 +142,7 @@ export const Dropdown = forwardRef(function Dropdown({
|
|
|
140
142
|
|
|
141
143
|
function onMouseDown(e: { key: string, preventDefault: Function, target: EventTarget | null }) {
|
|
142
144
|
if (e.key && e.key != 'Enter') return
|
|
143
|
-
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
|
|
144
146
|
|
|
145
147
|
if (!isHoverable && !menuIsOpen && ((menuToggles || e.key) || !isActive)) {
|
|
146
148
|
if (isActive && preventCloseOnClickChild && dropdownRef.current?.contains(e.target as Node)) return // keep active
|
|
@@ -148,13 +150,20 @@ export const Dropdown = forwardRef(function Dropdown({
|
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
function onClick(
|
|
153
|
+
function onClick(e: React.MouseEvent) {
|
|
154
|
+
e.preventDefault()
|
|
155
|
+
e.stopPropagation()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function onItemClick(option: { onClick?: Function, preventCloseOnClick?: boolean }, e: React.MouseEvent) {
|
|
152
159
|
if (option.onClick) option.onClick(e, option)
|
|
153
160
|
if (!menuIsOpen && !option?.preventCloseOnClick) setIsActive(!isActive)
|
|
154
161
|
}
|
|
155
162
|
|
|
156
163
|
return (
|
|
157
|
-
<div
|
|
164
|
+
<div
|
|
165
|
+
ref={dropdownRef}
|
|
166
|
+
css={style}
|
|
158
167
|
class={
|
|
159
168
|
`relative is-${direction || dir}` + // until hovered, show the original direction to prevent scrollbars
|
|
160
169
|
(isHoverable ? ' is-hoverable' : '') +
|
|
@@ -164,25 +173,36 @@ export const Dropdown = forwardRef(function Dropdown({
|
|
|
164
173
|
' nitro-dropdown' +
|
|
165
174
|
(className ? ` ${className}` : '')
|
|
166
175
|
}
|
|
167
|
-
onClick={(e) => { e.stopPropagation(); e.preventDefault() }} // required for dropdowns inside row links
|
|
168
|
-
ref={dropdownRef}
|
|
169
|
-
css={style}
|
|
170
176
|
>
|
|
171
177
|
{
|
|
172
178
|
(Array.isArray(children) ? children : [children]).map((el, key) => {
|
|
173
179
|
const onKeyDown = onMouseDown
|
|
174
180
|
if (!el.type) throw new Error('Dropdown component requires a valid child element')
|
|
175
|
-
return cloneElement(el, { key, onMouseDown, onKeyDown })
|
|
181
|
+
return cloneElement(el, { key, onMouseDown, onKeyDown, onClick })
|
|
176
182
|
})
|
|
177
183
|
}
|
|
178
184
|
<ul
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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,
|
|
182
201
|
...(maxHeight ? { overflow: 'auto' } : {}),
|
|
183
202
|
}}
|
|
184
|
-
class={
|
|
185
|
-
|
|
203
|
+
class={twMerge(
|
|
204
|
+
`${menuStyle} ${ready ? 'is-ready' : ''} absolute invisible opacity-0 select-none outline-none min-w-full z-[1] ${menuClassName||''}`
|
|
205
|
+
)}
|
|
186
206
|
>
|
|
187
207
|
{menuContent}
|
|
188
208
|
{
|
|
@@ -192,7 +212,7 @@ export const Dropdown = forwardRef(function Dropdown({
|
|
|
192
212
|
<li
|
|
193
213
|
key={i}
|
|
194
214
|
className={twMerge(`${optionStyle} ${option.className} ${menuOptionClassName}`)}
|
|
195
|
-
onClick={(e: React.MouseEvent) =>
|
|
215
|
+
onClick={(e: React.MouseEvent) => onItemClick(option, e)}
|
|
196
216
|
>
|
|
197
217
|
{ !!option.iconLeft && option.iconLeft }
|
|
198
218
|
<span class="flex-auto">{option.label}</span>
|
|
@@ -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 🚀",
|