@eml-payments/ui-kit 1.8.13 → 1.8.15
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/dist/index.css +2 -2
- package/dist/index.d.cts +488 -0
- package/dist/index.d.ts +488 -0
- package/dist/src/components/Alert/Alert.types.d.ts +1 -1
- package/dist/src/components/Alert/AlertContainer.stories.js +8 -16
- package/dist/src/components/ButtonGroup/ButtonGroup.types.d.ts +3 -3
- package/dist/src/components/Calendar/Calendar.d.ts +1 -1
- package/dist/src/components/Checkbox/Checkbox.js +1 -1
- package/dist/src/components/Checkbox/Checkbox.stories.js +6 -8
- package/dist/src/components/Counter/Counter.d.ts +1 -1
- package/dist/src/components/Counter/Counter.js +9 -1
- package/dist/src/components/DatePicker/DatePicker.js +168 -13
- package/dist/src/components/Dropdown/Dropdown.d.ts +2 -2
- package/dist/src/components/Filters/Filters.js +1 -1
- package/dist/src/components/Pills/Pills.stories.d.ts +1 -1
- package/dist/src/components/Pills/Pills.stories.js +1 -1
- package/dist/src/components/Skeleton/Skeleton.d.ts +1 -1
- package/dist/src/components/Skeleton/Skeleton.js +1 -1
- package/dist/src/components/Spinner/Spinner.d.ts +1 -1
- package/dist/src/components/Stepper/useStepper.js +1 -1
- package/dist/src/components/Switch/Switch.js +1 -1
- package/dist/src/components/Switch/Switch.stories.js +6 -8
- package/dist/src/components/Table/BaseTable/index.d.ts +1 -0
- package/dist/src/components/Table/BaseTable/index.js +1 -0
- package/dist/src/components/Table/Pagination/Pagination.types.d.ts +2 -2
- package/dist/src/components/Table/Pagination/PaginationControls.d.ts +3 -0
- package/dist/src/components/Table/Pagination/PaginationControls.js +22 -0
- package/dist/src/components/Table/Pagination/PaginationControls.types.d.ts +24 -0
- package/dist/src/components/Table/Pagination/PaginationControls.types.js +1 -0
- package/dist/src/components/Table/Table.d.ts +4 -0
- package/dist/src/components/Table/Table.js +93 -0
- package/dist/src/components/Table/Table.stories.d.ts +31 -0
- package/dist/src/components/Table/Table.stories.js +479 -0
- package/dist/src/components/Table/hooks/useInfiniteScrolling.d.ts +29 -0
- package/dist/src/components/Table/hooks/useInfiniteScrolling.js +96 -0
- package/dist/src/components/Table/hooks/usePaginationController.d.ts +16 -0
- package/dist/src/components/Table/hooks/usePaginationController.js +30 -0
- package/dist/src/components/Table/hooks/useTableController.d.ts +26 -0
- package/dist/src/components/Table/hooks/useTableController.js +146 -0
- package/dist/src/context/UIKitProvider.js +2 -3
- package/dist/src/stories/Page.js +1 -1
- package/package.json +1 -5
- package/dist/src/assets/index.d.ts +0 -2
- package/dist/src/assets/index.js +0 -3
- package/dist/src/assets/index.ts +0 -3
|
@@ -5,6 +5,74 @@ import { dayjs } from '../../utils';
|
|
|
5
5
|
import { Calendar } from '../Calendar';
|
|
6
6
|
import { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } from '../Popover';
|
|
7
7
|
import { Input } from '../Input';
|
|
8
|
+
/**
|
|
9
|
+
* Derive the fixed-slot mask from the date format. The empty template is the
|
|
10
|
+
* format string itself, so an empty slot displays its own format character
|
|
11
|
+
* (e.g. clearing the month of `28/01/1996` shows `28/MM/1996`).
|
|
12
|
+
* `slotPositions` holds the string index of every digit slot; separators live
|
|
13
|
+
* at all other indices and are immutable. Format characters are letters and
|
|
14
|
+
* typed content is always digits, so filled and empty slots never collide.
|
|
15
|
+
*/
|
|
16
|
+
const buildMaskMeta = (format) => {
|
|
17
|
+
const slotPositions = [];
|
|
18
|
+
for (let i = 0; i < format.length; i++) {
|
|
19
|
+
if (/[DMY]/i.test(format[i])) {
|
|
20
|
+
slotPositions.push(i);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return { template: format, slotPositions };
|
|
24
|
+
};
|
|
25
|
+
const findSlotIndexAtOrAfter = (slotPositions, position) => slotPositions.findIndex((pos) => pos >= position);
|
|
26
|
+
const findSlotIndexBefore = (slotPositions, position) => {
|
|
27
|
+
for (let i = slotPositions.length - 1; i >= 0; i--) {
|
|
28
|
+
if (slotPositions[i] < position) {
|
|
29
|
+
return i;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return -1;
|
|
33
|
+
};
|
|
34
|
+
/** Reset every slot inside [from, to) back to its format character. */
|
|
35
|
+
const clearSlotsInRange = (displayChars, slotPositions, template, from, to) => {
|
|
36
|
+
for (const pos of slotPositions) {
|
|
37
|
+
if (pos >= from && pos < to) {
|
|
38
|
+
displayChars[pos] = template[pos];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
/** Fill the slot at/after the caret with a digit. Returns the new caret, or null for a no-op. */
|
|
43
|
+
const applyDigitKey = (displayChars, slotPositions, template, digit, start, end) => {
|
|
44
|
+
var _a;
|
|
45
|
+
const hadSelection = end > start;
|
|
46
|
+
if (hadSelection) {
|
|
47
|
+
clearSlotsInRange(displayChars, slotPositions, template, start, end);
|
|
48
|
+
}
|
|
49
|
+
const slotIndex = findSlotIndexAtOrAfter(slotPositions, start);
|
|
50
|
+
// Caret past the last slot: only flush if a selection was cleared.
|
|
51
|
+
if (slotIndex === -1) {
|
|
52
|
+
return hadSelection ? start : null;
|
|
53
|
+
}
|
|
54
|
+
displayChars[slotPositions[slotIndex]] = digit;
|
|
55
|
+
// Advance past the filled slot, skipping any separator.
|
|
56
|
+
return (_a = slotPositions[slotIndex + 1]) !== null && _a !== void 0 ? _a : slotPositions[slotIndex] + 1;
|
|
57
|
+
};
|
|
58
|
+
/** Clear slots for Backspace/Delete. Returns the new caret, or null for a no-op. */
|
|
59
|
+
const applyDeletionKey = (displayChars, slotPositions, template, isBackspace, start, end) => {
|
|
60
|
+
// A selection clears exactly the covered slots.
|
|
61
|
+
if (end > start) {
|
|
62
|
+
clearSlotsInRange(displayChars, slotPositions, template, start, end);
|
|
63
|
+
return start;
|
|
64
|
+
}
|
|
65
|
+
const slotIndex = isBackspace
|
|
66
|
+
? findSlotIndexBefore(slotPositions, start)
|
|
67
|
+
: findSlotIndexAtOrAfter(slotPositions, start);
|
|
68
|
+
if (slotIndex === -1) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const pos = slotPositions[slotIndex];
|
|
72
|
+
displayChars[pos] = template[pos];
|
|
73
|
+
// Backspace lands on the cleared slot; forward Delete keeps the caret put.
|
|
74
|
+
return isBackspace ? pos : start;
|
|
75
|
+
};
|
|
8
76
|
const formatDate = (date, format) => {
|
|
9
77
|
return date ? dayjs(date).format(format) : '';
|
|
10
78
|
};
|
|
@@ -33,9 +101,10 @@ const normalizeRangeSelection = (currentRange, nextRange) => {
|
|
|
33
101
|
}
|
|
34
102
|
return nextRange;
|
|
35
103
|
};
|
|
36
|
-
export const DatePicker = ({ mode = 'single', placeholder
|
|
104
|
+
export const DatePicker = ({ mode = 'single', placeholder, dateFormat = 'DD/MM/YYYY', rangeDateFormat, currentDate, minDate, maxDate, error, onChangeDate, ...inputProps }) => {
|
|
37
105
|
const isRangeMode = mode === 'range';
|
|
38
106
|
const effectiveRangeDateFormat = rangeDateFormat || dateFormat;
|
|
107
|
+
const { template, slotPositions } = buildMaskMeta(dateFormat);
|
|
39
108
|
const computeDisplayValue = (date) => {
|
|
40
109
|
if (isRangeMode) {
|
|
41
110
|
return formatRangeDisplay(date, effectiveRangeDateFormat);
|
|
@@ -80,20 +149,25 @@ export const DatePicker = ({ mode = 'single', placeholder = 'DD/MM/YYYY', dateFo
|
|
|
80
149
|
setValue(formatDate(selected, dateFormat));
|
|
81
150
|
setOpen(false);
|
|
82
151
|
};
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
152
|
+
/**
|
|
153
|
+
* Validate the current display. Only a fully filled mask is parsed; a
|
|
154
|
+
* partially filled one (still showing format characters) clears the date
|
|
155
|
+
* silently so the user can keep editing.
|
|
156
|
+
*/
|
|
157
|
+
const commitDisplay = (display, showErrorWhenComplete) => {
|
|
158
|
+
const isComplete = display.length === template.length && slotPositions.every((pos) => /\d/.test(display[pos]));
|
|
159
|
+
if (!isComplete) {
|
|
160
|
+
setDate(undefined);
|
|
161
|
+
setDefaultError('');
|
|
91
162
|
return;
|
|
92
163
|
}
|
|
93
|
-
const parsedDate = dayjs(
|
|
94
|
-
onChangeDate === null || onChangeDate === void 0 ? void 0 : onChangeDate(parsedDate.toDate());
|
|
164
|
+
const parsedDate = dayjs(display, dateFormat, true);
|
|
95
165
|
if (!parsedDate.isValid()) {
|
|
96
|
-
|
|
166
|
+
setDate(undefined);
|
|
167
|
+
setDefaultError(showErrorWhenComplete ? 'Invalid date format' : '');
|
|
168
|
+
if (showErrorWhenComplete) {
|
|
169
|
+
onChangeDate === null || onChangeDate === void 0 ? void 0 : onChangeDate(undefined);
|
|
170
|
+
}
|
|
97
171
|
return;
|
|
98
172
|
}
|
|
99
173
|
const isWithinRange = (!minDate || parsedDate.isSameOrAfter(dayjs(minDate), 'day')) &&
|
|
@@ -102,8 +176,89 @@ export const DatePicker = ({ mode = 'single', placeholder = 'DD/MM/YYYY', dateFo
|
|
|
102
176
|
onUpdateDate(parsedDate.toDate());
|
|
103
177
|
}
|
|
104
178
|
else {
|
|
179
|
+
setDate(undefined);
|
|
180
|
+
onChangeDate === null || onChangeDate === void 0 ? void 0 : onChangeDate(undefined);
|
|
105
181
|
setDefaultError('Date is out of range');
|
|
106
182
|
}
|
|
107
183
|
};
|
|
108
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Write the new display and caret to the DOM synchronously, then sync
|
|
186
|
+
* React state. Because the DOM already matches the upcoming state when
|
|
187
|
+
* React commits, React never rewrites the input and the caret is never
|
|
188
|
+
* reset — regardless of typing speed or render bailouts.
|
|
189
|
+
*/
|
|
190
|
+
const flushDisplay = (input, displayChars, caret, showErrorWhenComplete) => {
|
|
191
|
+
// All slots back to their format characters: collapse to an empty
|
|
192
|
+
// field so the regular input placeholder shows instead.
|
|
193
|
+
const isEmpty = slotPositions.every((pos) => displayChars[pos] === template[pos]);
|
|
194
|
+
if (isEmpty) {
|
|
195
|
+
input.value = '';
|
|
196
|
+
input.setSelectionRange(0, 0);
|
|
197
|
+
setValue('');
|
|
198
|
+
onUpdateDate(undefined);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const nextDisplay = displayChars.join('');
|
|
202
|
+
input.value = nextDisplay;
|
|
203
|
+
input.setSelectionRange(caret, caret);
|
|
204
|
+
setValue(nextDisplay);
|
|
205
|
+
commitDisplay(nextDisplay, showErrorWhenComplete);
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* Fixed-slot (overtype) editing: every digit lives in its own slot and
|
|
209
|
+
* separators are immutable. Typing fills the slot at/after the caret,
|
|
210
|
+
* Backspace/Delete restore a slot to its format character, so removing
|
|
211
|
+
* the month from `28/01/1996` yields `28/MM/1996` and typing `0` into an
|
|
212
|
+
* empty field yields `0D/MM/YYYY`.
|
|
213
|
+
*/
|
|
214
|
+
const handleKeyDown = (e) => {
|
|
215
|
+
var _a, _b;
|
|
216
|
+
// Range mode is read-only; let shortcuts (Ctrl/Cmd+A/C/V/X…) through untouched.
|
|
217
|
+
if (isRangeMode || e.ctrlKey || e.metaKey) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const isDigit = /^\d$/.test(e.key);
|
|
221
|
+
const isDeletionKey = e.key === 'Backspace' || e.key === 'Delete';
|
|
222
|
+
if (!isDigit && !isDeletionKey) {
|
|
223
|
+
// Block other printable characters; navigation keys pass through.
|
|
224
|
+
if (e.key.length === 1) {
|
|
225
|
+
e.preventDefault();
|
|
226
|
+
}
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
e.preventDefault();
|
|
230
|
+
const input = e.currentTarget;
|
|
231
|
+
const displayChars = (value || template).split('');
|
|
232
|
+
const start = (_a = input.selectionStart) !== null && _a !== void 0 ? _a : 0;
|
|
233
|
+
const end = (_b = input.selectionEnd) !== null && _b !== void 0 ? _b : start;
|
|
234
|
+
const caret = isDigit
|
|
235
|
+
? applyDigitKey(displayChars, slotPositions, template, e.key, start, end)
|
|
236
|
+
: applyDeletionKey(displayChars, slotPositions, template, e.key === 'Backspace', start, end);
|
|
237
|
+
if (caret === null) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
flushDisplay(input, displayChars, caret, isDigit);
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Fallback for edits that bypass keydown (paste, drag-and-drop, autofill,
|
|
244
|
+
* some mobile keyboards): extract the digits and refill the slots from the
|
|
245
|
+
* start.
|
|
246
|
+
*/
|
|
247
|
+
const handleChangeInputValue = (e) => {
|
|
248
|
+
// In range mode, input is read-only and changes should be made via calendar
|
|
249
|
+
if (isRangeMode) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const input = e.target;
|
|
253
|
+
const digits = input.value.replace(/\D/g, '').slice(0, slotPositions.length);
|
|
254
|
+
const displayChars = template.split('');
|
|
255
|
+
slotPositions.forEach((pos, i) => {
|
|
256
|
+
if (digits[i] !== undefined) {
|
|
257
|
+
displayChars[pos] = digits[i];
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
const caret = digits.length < slotPositions.length ? slotPositions[digits.length] : template.length;
|
|
261
|
+
flushDisplay(input, displayChars, caret, true);
|
|
262
|
+
};
|
|
263
|
+
return (_jsx("div", { className: "relative", children: _jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverAnchor, { children: _jsx(Input, { type: "text", value: value, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : dateFormat, error: error !== null && error !== void 0 ? error : defaultError, readOnly: isRangeMode, onKeyDown: handleKeyDown, onChange: handleChangeInputValue, endAdornment: _jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "p-1.5 hover:bg-gray-100 rounded-full transition-colors cursor-pointer flex items-center justify-center", "aria-label": "Open calendar", children: _jsx(CalendarIcon, { size: 16, className: "text-gray-500" }) }) }), ...inputProps }) }), _jsx(PopoverContent, { className: "w-auto p-0", align: "center", sideOffset: 0, children: isRangeMode ? (_jsx(Calendar, { mode: "range", captionLayout: "dropdown", selected: date, startMonth: minDate, endMonth: maxDate, onSelect: handleChangeRangeDate, numberOfMonths: 2 })) : (_jsx(Calendar, { mode: "single", captionLayout: "dropdown", selected: date, startMonth: minDate, endMonth: maxDate, onSelect: handleChangeSingleDate, numberOfMonths: 1 })) })] }) }));
|
|
109
264
|
};
|
|
@@ -5,10 +5,10 @@ declare function DropdownMenuPortal({ ...props }: Readonly<React.ComponentProps<
|
|
|
5
5
|
declare function DropdownMenuTrigger({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>): import("react/jsx-runtime").JSX.Element;
|
|
6
6
|
declare function DropdownMenuContent({ className, sideOffset, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>): import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
declare function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>): import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
declare function DropdownMenuItem({ className, inset, variant, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
8
|
+
declare function DropdownMenuItem({ className, inset, variant, ...props }: Readonly<React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
9
9
|
inset?: boolean;
|
|
10
10
|
variant?: 'default' | 'destructive';
|
|
11
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
}>): import("react/jsx-runtime").JSX.Element;
|
|
12
12
|
declare function DropdownMenuCheckboxItem({ className, children, checked, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>): import("react/jsx-runtime").JSX.Element;
|
|
13
13
|
declare function DropdownMenuRadioGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>): import("react/jsx-runtime").JSX.Element;
|
|
14
14
|
declare function DropdownMenuRadioItem({ className, children, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -66,6 +66,6 @@ export const Filters = forwardRef(({ filters, defaultValues, onChange, onReset,
|
|
|
66
66
|
const currentValue = values[key];
|
|
67
67
|
return JSON.stringify(defaultValue) !== JSON.stringify(currentValue);
|
|
68
68
|
});
|
|
69
|
-
return (_jsxs("div", { className: "flex flex-wrap items-center gap-3 h-full w-full", children: [filters.map((filterConfig) => (_jsx("div", { className: "flex items-center
|
|
69
|
+
return (_jsxs("div", { className: "flex flex-wrap items-center gap-3 h-full w-full", children: [filters.map((filterConfig) => (_jsx("div", { className: "flex items-center h-10", children: _jsx(Controller, { name: filterConfig.name, control: control, render: ({ field }) => renderFilterComponent(filterConfig, field) }) }, filterConfig.name))), showResetButton && (_jsx("div", { className: "flex items-center h-10 ml-3", children: _jsx(Button, { ...resetButtonProps, variant: "secondaryOutlined", className: cn('min-w-[80px]', resetButtonProps === null || resetButtonProps === void 0 ? void 0 : resetButtonProps.className), onClick: handleResetFilters, disabled: isResetDisabled, children: resetButtonText }) }))] }));
|
|
70
70
|
});
|
|
71
71
|
Filters.displayName = 'Filters';
|
|
@@ -9,6 +9,6 @@ export declare const Outlined: Story;
|
|
|
9
9
|
export declare const Disabled: Story;
|
|
10
10
|
export declare const Secondary: Story;
|
|
11
11
|
export declare const Warning: Story;
|
|
12
|
-
export declare const
|
|
12
|
+
export declare const ErrorPill: Story;
|
|
13
13
|
export declare const Info: Story;
|
|
14
14
|
export declare const Success: Story;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { SkeletonProps } from './Skeleton.types';
|
|
2
|
-
export declare function Skeleton({ variant, width, height, animation, className, style }: SkeletonProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function Skeleton({ variant, width, height, animation, className, style, }: Readonly<SkeletonProps>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from '../../lib/utils';
|
|
3
|
-
export function Skeleton({ variant = 'text', width, height, animation = 'pulse', className, style }) {
|
|
3
|
+
export function Skeleton({ variant = 'text', width, height, animation = 'pulse', className, style, }) {
|
|
4
4
|
const baseClasses = 'bg-gray-200 dark:bg-gray-700';
|
|
5
5
|
const animationClasses = {
|
|
6
6
|
pulse: 'animate-pulse',
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { SpinnerProps } from './Spinner.types';
|
|
2
|
-
export declare function Spinner({ size, color, className, 'aria-label': ariaLabel, }: SpinnerProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function Spinner({ size, color, className, 'aria-label': ariaLabel, }: Readonly<SpinnerProps>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
export function useStepper(steps, initialStep) {
|
|
3
3
|
const [currentStep, setCurrentStep] = useState(initialStep !== null && initialStep !== void 0 ? initialStep : steps[0]);
|
|
4
|
-
const currentIndex = steps.
|
|
4
|
+
const currentIndex = steps.indexOf(currentStep);
|
|
5
5
|
const isFirst = currentIndex === 0;
|
|
6
6
|
const isLast = currentIndex === steps.length - 1;
|
|
7
7
|
const goNext = () => {
|
|
@@ -16,7 +16,7 @@ const Switch = React.forwardRef(({ className, label, labelPosition = 'left', id,
|
|
|
16
16
|
const ariaLabel = label || id || props['aria-label'];
|
|
17
17
|
return (_jsxs("div", { className: wrapperClass, children: [label && (_jsx(Label, { htmlFor: id, className: cn('text-sm font-medium cursor-pointer', {
|
|
18
18
|
'cursor-not-allowed opacity-70': props.disabled,
|
|
19
|
-
}), children: label })), _jsx(SwitchPrimitives.Root, { ref: ref, id: id, "aria-label":
|
|
19
|
+
}), children: label })), _jsx(SwitchPrimitives.Root, { ref: ref, id: id, "aria-label": label ? undefined : ariaLabel, title: label ? undefined : ariaLabel, className: cn('peer inline-flex h-[22px] w-[40px] shrink-0 cursor-pointer items-center rounded-full border-[3px] border-black transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 bg-transparent', className), ...props, children: _jsx(SwitchPrimitives.Thumb, { className: cn('pointer-events-none block h-[12px] w-[12px] rounded-full shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-[21px] data-[state=checked]:bg-(--uikit-primary) data-[state=unchecked]:translate-x-[2px] data-[state=unchecked]:bg-black') }) })] }));
|
|
20
20
|
});
|
|
21
21
|
Switch.displayName = SwitchPrimitives.Root.displayName;
|
|
22
22
|
export { Switch };
|
|
@@ -28,14 +28,15 @@ export const Disabled = {
|
|
|
28
28
|
id: 'disabled-switch',
|
|
29
29
|
},
|
|
30
30
|
};
|
|
31
|
+
function ControlledSwitchStory(args) {
|
|
32
|
+
const [checked, setChecked] = useState(args.checked);
|
|
33
|
+
return _jsx(Switch, { ...args, checked: checked, onCheckedChange: setChecked });
|
|
34
|
+
}
|
|
31
35
|
export const ControlledSwitch = {
|
|
32
36
|
args: {
|
|
33
37
|
id: 'controlled-switch',
|
|
34
38
|
},
|
|
35
|
-
render: (args) => {
|
|
36
|
-
const [checked, setChecked] = useState(args.checked);
|
|
37
|
-
return _jsx(Switch, { ...args, checked: checked, onCheckedChange: setChecked });
|
|
38
|
-
},
|
|
39
|
+
render: (args) => _jsx(ControlledSwitchStory, { ...args }),
|
|
39
40
|
};
|
|
40
41
|
export const WithLabel = {
|
|
41
42
|
args: {
|
|
@@ -55,8 +56,5 @@ export const ControlledAndLabel = {
|
|
|
55
56
|
label: 'Controlled Switch',
|
|
56
57
|
id: 'controlled-switch-label',
|
|
57
58
|
},
|
|
58
|
-
render: (args) => {
|
|
59
|
-
const [checked, setChecked] = useState(args.checked);
|
|
60
|
-
return _jsx(Switch, { ...args, checked: checked, onCheckedChange: setChecked });
|
|
61
|
-
},
|
|
59
|
+
render: (args) => _jsx(ControlledSwitchStory, { ...args }),
|
|
62
60
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BaseTable } from './BaseTable';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BaseTable } from './BaseTable';
|
|
@@ -17,6 +17,6 @@ export type PageSizeSelectorProps = {
|
|
|
17
17
|
onChange: (val: string) => void;
|
|
18
18
|
options?: string[];
|
|
19
19
|
label?: string;
|
|
20
|
-
className?: SelectWrapperProps['className']
|
|
21
|
-
size?: SelectWrapperProps['size']
|
|
20
|
+
className?: NonNullable<SelectWrapperProps['className']>;
|
|
21
|
+
size?: NonNullable<SelectWrapperProps['size']>;
|
|
22
22
|
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { PageSizeSelectorProps, PaginationControlsProps } from './PaginationControls.types';
|
|
2
|
+
export declare const PageSizeSelector: ({ id, value, onChange, options, label, className, size, }: PageSizeSelectorProps) => import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export declare const PaginationFooter: <T>({ table, tableId, paginationMode, onPageSizeChange, totalServerRows, onNextPage, onPrevPage, }: PaginationControlsProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SelectWrapper } from '../../SelectWrapper';
|
|
3
|
+
import { cn } from '../../../lib/utils';
|
|
4
|
+
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
|
|
5
|
+
import { Button } from '../../Button';
|
|
6
|
+
export const PageSizeSelector = ({ id, value, onChange, options = ['5', '10', '20', '50', '100'], label = 'Rows per page:', className, size = 'default', }) => {
|
|
7
|
+
return (_jsxs("div", { className: cn('flex items-center gap-2 text-xs', className), id: `page-size-select-${id}`, children: [_jsx("label", { htmlFor: `page-size-select-${id}`, children: label }), _jsx("div", { className: "w-fit", children: _jsx(SelectWrapper, { size: size, value: value, onChange: onChange, options: options.map((option) => ({
|
|
8
|
+
label: option,
|
|
9
|
+
value: option,
|
|
10
|
+
})), className: "bg-transparent shadow-none border-none gap-1" }) })] }));
|
|
11
|
+
};
|
|
12
|
+
export const PaginationFooter = ({ table, tableId, paginationMode = 'client', onPageSizeChange, totalServerRows, onNextPage, onPrevPage, }) => {
|
|
13
|
+
const { pageIndex, pageSize } = table.getState().pagination;
|
|
14
|
+
const totalRows = paginationMode === 'server' ? (totalServerRows !== null && totalServerRows !== void 0 ? totalServerRows : 0) : table.getFilteredRowModel().rows.length;
|
|
15
|
+
const startRow = pageIndex * pageSize + 1;
|
|
16
|
+
const endRow = Math.min((pageIndex + 1) * pageSize, totalRows);
|
|
17
|
+
return (_jsxs("div", { className: "flex justify-end items-center gap-6 px-4 py-2 text-xs text-(--uikit-textSecondary) w-full", children: [_jsx(PageSizeSelector, { id: tableId, value: String(pageSize), onChange: (val) => {
|
|
18
|
+
const newSize = Number(val);
|
|
19
|
+
table.setPageSize(newSize);
|
|
20
|
+
onPageSizeChange === null || onPageSizeChange === void 0 ? void 0 : onPageSizeChange(newSize);
|
|
21
|
+
}, size: "small", label: "Rows per page" }), totalRows > 0 && _jsx("div", { id: `page-indicator-${tableId}`, children: `${startRow} - ${endRow} of ${totalRows}` }), totalRows > 0 && (_jsxs("div", { className: "flex gap-2", id: `pagination-controls-${tableId}`, children: [_jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Previous page", className: cn({ '!bg-transparent': !table.getCanPreviousPage() }), onClick: onPrevPage, disabled: !table.getCanPreviousPage(), children: _jsx(FaChevronLeft, { className: "!w-3 !h-3" }) }), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Next page", onClick: onNextPage, className: cn({ '!bg-transparent': !table.getCanNextPage() }), disabled: !table.getCanNextPage(), children: _jsx(FaChevronRight, { className: "w-3! h-3!" }) })] }))] }));
|
|
22
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Table } from '@tanstack/react-table';
|
|
2
|
+
import type { SelectWrapperProps } from '../../SelectWrapper';
|
|
3
|
+
import { PaginationMode } from '../Table.types';
|
|
4
|
+
export interface PaginationControlsProps<T> {
|
|
5
|
+
tableId: string;
|
|
6
|
+
table: Table<T>;
|
|
7
|
+
paginationMode?: PaginationMode;
|
|
8
|
+
totalServerRows?: number;
|
|
9
|
+
pageSizeOptions?: string[];
|
|
10
|
+
onPageSizeChange?: (size: number) => void;
|
|
11
|
+
onNextPage: () => void;
|
|
12
|
+
onPrevPage: () => void;
|
|
13
|
+
canNextPage: boolean;
|
|
14
|
+
canPrevPage: boolean;
|
|
15
|
+
}
|
|
16
|
+
export type PageSizeSelectorProps = {
|
|
17
|
+
id: string;
|
|
18
|
+
value: string;
|
|
19
|
+
onChange: (val: string) => void;
|
|
20
|
+
options?: string[];
|
|
21
|
+
label?: string;
|
|
22
|
+
className?: SelectWrapperProps['className'];
|
|
23
|
+
size?: SelectWrapperProps['size'];
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { flexRender } from '@tanstack/react-table';
|
|
3
|
+
import { classNames } from '../../utils/classNames';
|
|
4
|
+
import { PaginationFooter } from './Pagination/PaginationControls';
|
|
5
|
+
import { useTableController } from './hooks/useTableController';
|
|
6
|
+
import { FaChevronDown, FaChevronUp } from 'react-icons/fa';
|
|
7
|
+
import { DropdownWrapper } from '../DropdownWrapper';
|
|
8
|
+
import { FiMoreHorizontal } from 'react-icons/fi';
|
|
9
|
+
import { Button } from '../Button';
|
|
10
|
+
export function Table(props) {
|
|
11
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
12
|
+
const { table, setPageSize, showHeader, height, virtualizationEnabled, rowVirtualizer, hasNextPage, parentScrollRef, loaderRef, infiniteScroll, ...pagination } = useTableController(props);
|
|
13
|
+
let tableContent;
|
|
14
|
+
const noResultsMessage = props.isSearchActive
|
|
15
|
+
? ((_a = props.noSearchResultsMessage) !== null && _a !== void 0 ? _a : 'No results meet your search criteria')
|
|
16
|
+
: ((_b = props.noRowsMessage) !== null && _b !== void 0 ? _b : 'No rows to display');
|
|
17
|
+
if (props.isLoading && props.showSkeletonRows) {
|
|
18
|
+
tableContent = Array.from({ length: 3 }).map(() => (_jsx("tr", { className: "border-t animate-pulse", children: table.getVisibleFlatColumns().map((__c) => (_jsx("td", { className: "px-4 py-2", children: _jsx("div", { className: "h-4 w-3/4 bg-(--uikit-tertiary) rounded" }) }, `skeleton-cell-${__c.id}`))) }, "skeleton-row")));
|
|
19
|
+
}
|
|
20
|
+
else if (!virtualizationEnabled &&
|
|
21
|
+
table.getRowModel().rows.length === 0 &&
|
|
22
|
+
!props.isLoading &&
|
|
23
|
+
(!(infiniteScroll === null || infiniteScroll === void 0 ? void 0 : infiniteScroll.enabled) || !hasNextPage)) {
|
|
24
|
+
tableContent = (_jsx("tr", { children: _jsx("td", { colSpan: table.getVisibleFlatColumns().length + (props.tableActionsDropdown ? 1 : 0), className: "px-4 py-8 text-center text-muted-foreground", children: noResultsMessage }) }));
|
|
25
|
+
}
|
|
26
|
+
else if (!virtualizationEnabled) {
|
|
27
|
+
const renderRow = (row) => {
|
|
28
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
29
|
+
const rows = [];
|
|
30
|
+
const isRowClickable = (_a = props.isRowClickable) === null || _a === void 0 ? void 0 : _a.call(props, row.original);
|
|
31
|
+
if (row.getIsGrouped() && row.groupingColumnId) {
|
|
32
|
+
const column = table.getColumn(row.groupingColumnId);
|
|
33
|
+
const bgColor = (_c = (_b = column === null || column === void 0 ? void 0 : column.columnDef.meta) === null || _b === void 0 ? void 0 : _b.bgColor) !== null && _c !== void 0 ? _c : 'transparent';
|
|
34
|
+
rows.push(_jsx("tr", { className: "border-none", children: _jsx("td", { colSpan: table.getVisibleLeafColumns().length, className: "p-0", children: _jsx("div", { className: "px-4 py-4 text-sm text-muted-foreground", style: { backgroundColor: bgColor }, children: row.getGroupingValue(row.groupingColumnId) }) }) }, `group-${row.id}`));
|
|
35
|
+
row.subRows.forEach((subRow) => {
|
|
36
|
+
rows.push(...renderRow(subRow));
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
rows.push(_jsxs("tr", { className: classNames('border-t', isRowClickable && 'cursor-pointer', row.getIsSelected() && 'bg-(--uikit-primary-10)'), onClick: () => { var _a; return isRowClickable && ((_a = props.onRowClick) === null || _a === void 0 ? void 0 : _a.call(props, row.original)); }, children: [row.getVisibleCells().map((cell) => {
|
|
41
|
+
const width = cell.column.getSize();
|
|
42
|
+
return (_jsx("td", { style: { width }, className: cell.column.id === 'select' ? 'p-1' : 'p-4', children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id));
|
|
43
|
+
}), props.tableActionsDropdown && (_jsx("td", { className: "w-[40px] px-2 py-2 text-right", children: _jsx(DropdownWrapper, { structure: (_d = props.tableActionsDropdown.structure) !== null && _d !== void 0 ? _d : 'default', options: props.tableActionsDropdown.getOptions(row.original), menuAlignment: props.tableActionsDropdown.menuAlignment, isTriggerElementDisabled: typeof props.tableActionsDropdown.isDisabled === 'function'
|
|
44
|
+
? props.tableActionsDropdown.isDisabled(row.original)
|
|
45
|
+
: props.tableActionsDropdown.isDisabled, triggerElement: (_g = (_f = (_e = props.tableActionsDropdown).renderCustomTrigger) === null || _f === void 0 ? void 0 : _f.call(_e, row.original)) !== null && _g !== void 0 ? _g : (_jsx(Button, { "aria-label": "Open actions menu", title: "Open actions menu", size: "icon", variant: "ghost", disabled: typeof props.tableActionsDropdown.isDisabled === 'function'
|
|
46
|
+
? props.tableActionsDropdown.isDisabled(row.original)
|
|
47
|
+
: ((_h = props.tableActionsDropdown.isDisabled) !== null && _h !== void 0 ? _h : false), className: "p-1 focus-visible:outline-hidden focus-visible:ring-0", children: _jsx(FiMoreHorizontal, { size: 16 }) })) }) }))] }, row.original[((_j = props.rowIdKey) !== null && _j !== void 0 ? _j : 'id')]));
|
|
48
|
+
}
|
|
49
|
+
return rows;
|
|
50
|
+
};
|
|
51
|
+
tableContent = table.getRowModel().rows.flatMap(renderRow);
|
|
52
|
+
}
|
|
53
|
+
const virtualItems = virtualizationEnabled && rowVirtualizer ? rowVirtualizer.getVirtualItems() : [];
|
|
54
|
+
const colSpanFull = table.getVisibleFlatColumns().length + (props.tableActionsDropdown ? 1 : 0);
|
|
55
|
+
const paddingTop = virtualItems.length ? virtualItems[0].start : 0;
|
|
56
|
+
const lastVirtual = virtualItems[virtualItems.length - 1];
|
|
57
|
+
const totalSize = (_d = (_c = rowVirtualizer === null || rowVirtualizer === void 0 ? void 0 : rowVirtualizer.getTotalSize) === null || _c === void 0 ? void 0 : _c.call(rowVirtualizer)) !== null && _d !== void 0 ? _d : 0;
|
|
58
|
+
const end = lastVirtual ? lastVirtual.end : 0;
|
|
59
|
+
const paddingBottom = Math.max(totalSize - end, 0);
|
|
60
|
+
const tableRows = table.getRowModel().rows;
|
|
61
|
+
const isTrulyEmpty = tableRows.length === 0 && !props.isLoading && !hasNextPage;
|
|
62
|
+
return (_jsxs("div", { ref: parentScrollRef, style: { height }, className: "relative w-full overflow-auto", children: [props.isLoading && _jsx("div", { className: "mui-loader mt-4" }), _jsx("div", { className: "rounded-t-(--uikit-radius) overflow-y-hidden overflow-x-auto", children: _jsxs("table", { className: classNames('min-w-full text-sm table-fixed bg-[#ffffff]', props.className), role: "table", id: props.id, children: [showHeader && (_jsx("thead", { className: "bg-(--uikit-tertiary)", children: table.getHeaderGroups().map((headerGroup) => (_jsxs("tr", { className: "p-4", children: [headerGroup.headers.map((header) => {
|
|
63
|
+
return (_jsx("th", { scope: "col", style: { width: header.getSize() }, className: classNames('select-none text-[14px] font-bold ', header.id === 'select' ? 'p-0' : ' p-4 text-left cursor-pointer'), onClick: !(infiniteScroll === null || infiniteScroll === void 0 ? void 0 : infiniteScroll.enabled) && header.column.getCanSort()
|
|
64
|
+
? header.column.getToggleSortingHandler()
|
|
65
|
+
: undefined, children: _jsxs("div", { className: classNames('w-full h-full', header.id === 'select'
|
|
66
|
+
? 'flex justify-center items-center'
|
|
67
|
+
: 'flex items-center gap-2'), children: [flexRender(header.column.columnDef.header, header.getContext()), !(infiniteScroll === null || infiniteScroll === void 0 ? void 0 : infiniteScroll.enabled) && header.column.getCanSort() && (_jsx("span", { className: "text-xs", children: {
|
|
68
|
+
asc: _jsx(FaChevronUp, {}),
|
|
69
|
+
desc: _jsx(FaChevronDown, {}),
|
|
70
|
+
}[header.column.getIsSorted()] || null }))] }) }, header.id));
|
|
71
|
+
}), props.tableActionsDropdown && _jsx("th", { className: "w-[40px] px-2 py-2 text-right" })] }, headerGroup.id))) })), _jsx("tbody", { children: virtualizationEnabled && rowVirtualizer ? (_jsxs(_Fragment, { children: [_jsx("tr", { style: { height: `${paddingTop}px` }, children: _jsx("td", { colSpan: colSpanFull }) }), isTrulyEmpty && (_jsx("tr", { children: _jsx("td", { colSpan: colSpanFull, className: "px-4 py-8 text-center text-muted-foreground", children: noResultsMessage }) })), virtualItems.map((vi) => {
|
|
72
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
73
|
+
const isLoaderRow = vi.index >= tableRows.length;
|
|
74
|
+
if (isLoaderRow) {
|
|
75
|
+
return (_jsx("tr", { className: "border-t", children: _jsx("td", { colSpan: colSpanFull, className: "px-4 py-2", children: hasNextPage
|
|
76
|
+
? ((_a = infiniteScroll === null || infiniteScroll === void 0 ? void 0 : infiniteScroll.loadingMoreMessage) !== null && _a !== void 0 ? _a : 'Loading more...')
|
|
77
|
+
: ((_b = infiniteScroll === null || infiniteScroll === void 0 ? void 0 : infiniteScroll.noMoreDataMessage) !== null && _b !== void 0 ? _b : 'Nothing more to load') }) }, `loader-${vi.key}`));
|
|
78
|
+
}
|
|
79
|
+
const row = tableRows[vi.index];
|
|
80
|
+
const isRowClickable = (_c = props.isRowClickable) === null || _c === void 0 ? void 0 : _c.call(props, row.original);
|
|
81
|
+
return (_jsxs("tr", { className: classNames('border-t', isRowClickable && 'cursor-pointer', (row === null || row === void 0 ? void 0 : row.getIsSelected()) && 'bg-(--uikit-primary-10)'), onClick: () => { var _a; return isRowClickable && ((_a = props.onRowClick) === null || _a === void 0 ? void 0 : _a.call(props, row.original)); }, children: [row === null || row === void 0 ? void 0 : row.getVisibleCells().map((cell) => {
|
|
82
|
+
const width = cell.column.getSize();
|
|
83
|
+
return (_jsx("td", { style: { width }, className: cell.column.id === 'select' ? 'p-1' : 'p-4', children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id));
|
|
84
|
+
}), props.tableActionsDropdown && (_jsx("td", { className: "w-[40px] px-2 py-2 text-right", children: _jsx(DropdownWrapper, { structure: (_d = props.tableActionsDropdown.structure) !== null && _d !== void 0 ? _d : 'default', options: props.tableActionsDropdown.getOptions(row.original), menuAlignment: props.tableActionsDropdown.menuAlignment, isTriggerElementDisabled: typeof props.tableActionsDropdown.isDisabled === 'function'
|
|
85
|
+
? props.tableActionsDropdown.isDisabled(row.original)
|
|
86
|
+
: props.tableActionsDropdown.isDisabled, triggerElement: (_g = (_f = (_e = props.tableActionsDropdown).renderCustomTrigger) === null || _f === void 0 ? void 0 : _f.call(_e, row.original)) !== null && _g !== void 0 ? _g : (_jsx(Button, { "aria-label": "Open actions menu", title: "Open actions menu", size: "icon", variant: "ghost", disabled: typeof props.tableActionsDropdown.isDisabled ===
|
|
87
|
+
'function'
|
|
88
|
+
? props.tableActionsDropdown.isDisabled(row.original)
|
|
89
|
+
: ((_h = props.tableActionsDropdown.isDisabled) !== null && _h !== void 0 ? _h : false), className: "p-1 focus-visible:outline-hidden focus-visible:ring-0", children: _jsx(FiMoreHorizontal, { size: 16 }) })) }) }))] }, row.original[((_j = props.rowIdKey) !== null && _j !== void 0 ? _j : 'id')]));
|
|
90
|
+
}), _jsx("tr", { style: { height: `${paddingBottom}px` }, children: _jsx("td", { colSpan: colSpanFull }) })] })) : (_jsxs(_Fragment, { children: [tableContent, (infiniteScroll === null || infiniteScroll === void 0 ? void 0 : infiniteScroll.enabled) && !isTrulyEmpty && (_jsx("tr", { ref: loaderRef, className: "border-t", children: _jsx("td", { colSpan: colSpanFull, className: "px-4 py-2", children: props.isLoading || hasNextPage
|
|
91
|
+
? ((_e = infiniteScroll.loadingMoreMessage) !== null && _e !== void 0 ? _e : 'Loading more...')
|
|
92
|
+
: ((_f = infiniteScroll.noMoreDataMessage) !== null && _f !== void 0 ? _f : 'Nothing more to load') }) }))] })) })] }) }), !((_g = props.infiniteScroll) === null || _g === void 0 ? void 0 : _g.enabled) && (_jsx(PaginationFooter, { tableId: props.id, table: table, paginationMode: props.paginationMode, totalServerRows: props.totalServerRows, onPageSizeChange: setPageSize, ...pagination }))] }));
|
|
93
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Table } from './Table';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
declare const meta: Meta<typeof Table>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof Table>;
|
|
6
|
+
export declare const Basic: Story;
|
|
7
|
+
export declare const NoRows: Story;
|
|
8
|
+
export declare const LoadingWithSkeleton: Story;
|
|
9
|
+
export declare const ClientPagination: Story;
|
|
10
|
+
export declare const ServerSidePagination: Story;
|
|
11
|
+
export declare const WithCheckboxSelection: Story;
|
|
12
|
+
export declare const WithDisabledSelection: Story;
|
|
13
|
+
export declare const WithSingleRowSelection: Story;
|
|
14
|
+
export declare const WithDefaultRowsSelected: Story;
|
|
15
|
+
export declare const FlexColumnWidths: Story;
|
|
16
|
+
export declare const WithControlledSorting: Story;
|
|
17
|
+
export declare const WithRowActionsDropdown: Story;
|
|
18
|
+
export declare const WithPartiallyDisabledRowActionsDropdown: Story;
|
|
19
|
+
export declare const WithDisabledRowActionsDropdownItem: Story;
|
|
20
|
+
export declare const WithRowClickHandler: Story;
|
|
21
|
+
export declare const WithHiddenHeader: Story;
|
|
22
|
+
export declare const GroupedByDate: Story;
|
|
23
|
+
export declare const WithSearch: Story;
|
|
24
|
+
export declare const ServerSideSearch: Story;
|
|
25
|
+
export declare const ClientPaginationWithQueryParams: StoryObj;
|
|
26
|
+
export declare const ServerPaginationWithQueryParams: StoryObj;
|
|
27
|
+
export declare const TwoTablesWithQueryParams: StoryObj;
|
|
28
|
+
export declare const InfiniteScrollVirtualized: StoryObj;
|
|
29
|
+
export declare const InfiniteScrollNonVirtual: StoryObj;
|
|
30
|
+
export declare const NoRowsInfiniteScrollNonVirtual: StoryObj;
|
|
31
|
+
export declare const NoRowsInfiniteScrollVirtualized: StoryObj;
|