sv5ui 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -11
- package/dist/Checkbox/Checkbox.svelte +2 -11
- package/dist/CheckboxGroup/CheckboxGroup.svelte +2 -11
- package/dist/Collapsible/Collapsible.svelte +69 -0
- package/dist/Collapsible/Collapsible.svelte.d.ts +6 -0
- package/dist/Collapsible/CollapsibleTestWrapper.svelte +17 -0
- package/dist/Collapsible/CollapsibleTestWrapper.svelte.d.ts +4 -0
- package/dist/Collapsible/collapsible.types.d.ts +75 -0
- package/dist/Collapsible/collapsible.types.js +1 -0
- package/dist/Collapsible/collapsible.variants.d.ts +53 -0
- package/dist/Collapsible/collapsible.variants.js +21 -0
- package/dist/Collapsible/index.d.ts +2 -0
- package/dist/Collapsible/index.js +1 -0
- package/dist/Command/Command.svelte +183 -0
- package/dist/Command/Command.svelte.d.ts +6 -0
- package/dist/Command/CommandTestWrapper.svelte +13 -0
- package/dist/Command/CommandTestWrapper.svelte.d.ts +4 -0
- package/dist/Command/command.types.d.ts +98 -0
- package/dist/Command/command.types.js +1 -0
- package/dist/Command/command.variants.d.ts +226 -0
- package/dist/Command/command.variants.js +86 -0
- package/dist/Command/index.d.ts +2 -0
- package/dist/Command/index.js +1 -0
- package/dist/FormField/FormField.svelte +2 -6
- package/dist/Input/Input.svelte +2 -10
- package/dist/PinInput/PinInput.svelte +2 -11
- package/dist/RadioGroup/RadioGroup.svelte +2 -11
- package/dist/Select/Select.svelte +2 -10
- package/dist/Select/select.variants.js +1 -1
- package/dist/SelectMenu/SelectMenu.svelte +2 -10
- package/dist/SelectMenu/select-menu.variants.js +1 -1
- package/dist/Slider/Slider.svelte +2 -11
- package/dist/Switch/Switch.svelte +2 -11
- package/dist/Table/Table.svelte +752 -0
- package/dist/Table/Table.svelte.d.ts +26 -0
- package/dist/Table/index.d.ts +2 -0
- package/dist/Table/index.js +1 -0
- package/dist/Table/table.types.d.ts +199 -0
- package/dist/Table/table.types.js +1 -0
- package/dist/Table/table.utils.d.ts +51 -0
- package/dist/Table/table.utils.js +166 -0
- package/dist/Table/table.variants.d.ts +205 -0
- package/dist/Table/table.variants.js +126 -0
- package/dist/Textarea/Textarea.svelte +2 -10
- package/dist/Toast/Toaster.svelte +618 -0
- package/dist/Toast/Toaster.svelte.d.ts +5 -0
- package/dist/Toast/index.d.ts +4 -0
- package/dist/Toast/index.js +2 -0
- package/dist/Toast/toast.d.ts +38 -0
- package/dist/Toast/toast.js +73 -0
- package/dist/Toast/toast.types.d.ts +19 -0
- package/dist/Toast/toast.types.js +1 -0
- package/dist/Toast/toast.variants.d.ts +7 -0
- package/dist/Toast/toast.variants.js +5 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +5 -1
- package/dist/hooks/index.d.ts +14 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/useClickOutside.svelte.d.ts +31 -0
- package/dist/hooks/useClickOutside.svelte.js +37 -0
- package/dist/hooks/useClipboard.svelte.d.ts +30 -0
- package/dist/hooks/useClipboard.svelte.js +45 -0
- package/dist/hooks/useDebounce.svelte.d.ts +36 -0
- package/dist/hooks/useDebounce.svelte.js +56 -0
- package/dist/hooks/useEscapeKeydown.svelte.d.ts +31 -0
- package/dist/hooks/useEscapeKeydown.svelte.js +37 -0
- package/dist/hooks/useFormField.svelte.d.ts +21 -0
- package/dist/hooks/useFormField.svelte.js +17 -0
- package/dist/hooks/useInfiniteScroll.svelte.d.ts +57 -0
- package/dist/hooks/useInfiniteScroll.svelte.js +69 -0
- package/dist/hooks/useMediaQuery.svelte.d.ts +31 -0
- package/dist/hooks/useMediaQuery.svelte.js +38 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -0
- package/dist/theme.css +36 -0
- package/package.json +2 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TableProps } from './table.types.js';
|
|
2
|
+
export type Props = TableProps;
|
|
3
|
+
declare function $$render<T extends Record<string, any>>(): {
|
|
4
|
+
props: TableProps<T>;
|
|
5
|
+
exports: {};
|
|
6
|
+
bindings: "ref" | "page" | "sorting" | "globalFilter" | "columnFilters" | "selectedRows" | "columnVisibility" | "columnPinning" | "pinnedRows" | "expandedRows" | "columnSizing";
|
|
7
|
+
slots: {};
|
|
8
|
+
events: {};
|
|
9
|
+
};
|
|
10
|
+
declare class __sveltets_Render<T extends Record<string, any>> {
|
|
11
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
12
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
13
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
14
|
+
bindings(): "ref" | "page" | "sorting" | "globalFilter" | "columnFilters" | "selectedRows" | "columnVisibility" | "columnPinning" | "pinnedRows" | "expandedRows" | "columnSizing";
|
|
15
|
+
exports(): {};
|
|
16
|
+
}
|
|
17
|
+
interface $$IsomorphicComponent {
|
|
18
|
+
new <T extends Record<string, any>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
19
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
20
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
21
|
+
<T extends Record<string, any>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
22
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
23
|
+
}
|
|
24
|
+
declare const Table: $$IsomorphicComponent;
|
|
25
|
+
type Table<T extends Record<string, any>> = InstanceType<typeof Table<T>>;
|
|
26
|
+
export default Table;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Table } from './Table.svelte';
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import type { ClassNameValue } from 'tailwind-merge';
|
|
4
|
+
import type { TableVariantProps, TableSlots } from './table.variants.js';
|
|
5
|
+
export interface TableColumn<T> {
|
|
6
|
+
/** Data accessor key. */
|
|
7
|
+
key: keyof T & string;
|
|
8
|
+
/** Header label text. Defaults to capitalized `key`. */
|
|
9
|
+
label?: string;
|
|
10
|
+
/** Enable sorting on this column. @default false */
|
|
11
|
+
sortable?: boolean;
|
|
12
|
+
/** Custom sort comparator. Receives two rows, returns -1/0/1. */
|
|
13
|
+
sortFn?: (a: T, b: T) => number;
|
|
14
|
+
/** Enable column-level filtering. @default false */
|
|
15
|
+
filterable?: boolean;
|
|
16
|
+
/** Custom filter function. */
|
|
17
|
+
filterFn?: (row: T, filterValue: string) => boolean;
|
|
18
|
+
/** Pin this column to left or right. */
|
|
19
|
+
pinned?: 'left' | 'right';
|
|
20
|
+
/** Enable drag-resize on this column. @default false */
|
|
21
|
+
resizable?: boolean;
|
|
22
|
+
/** Initial column width in px. */
|
|
23
|
+
width?: number;
|
|
24
|
+
/** Minimum column width in px. @default 50 */
|
|
25
|
+
minWidth?: number;
|
|
26
|
+
/** Maximum column width in px. */
|
|
27
|
+
maxWidth?: number;
|
|
28
|
+
/** Text alignment. @default 'left' */
|
|
29
|
+
align?: 'left' | 'center' | 'right';
|
|
30
|
+
/** Header cell colspan. */
|
|
31
|
+
colspan?: number;
|
|
32
|
+
/** Header cell rowspan. */
|
|
33
|
+
rowspan?: number;
|
|
34
|
+
/** Data cell colspan. */
|
|
35
|
+
cellColspan?: number;
|
|
36
|
+
/** Data cell rowspan. */
|
|
37
|
+
cellRowspan?: number;
|
|
38
|
+
/** Column visibility. @default true */
|
|
39
|
+
visible?: boolean;
|
|
40
|
+
/** Additional CSS class for this column's header cell. */
|
|
41
|
+
headerClass?: ClassNameValue;
|
|
42
|
+
/** Additional CSS class for this column's data cell. */
|
|
43
|
+
cellClass?: ClassNameValue;
|
|
44
|
+
/** Custom header snippet for this column. */
|
|
45
|
+
header?: Snippet<[TableHeaderSlotProps<T>]>;
|
|
46
|
+
/** Custom cell snippet for this column. */
|
|
47
|
+
cell?: Snippet<[TableCellSlotProps<T>]>;
|
|
48
|
+
/** Custom footer snippet for this column. */
|
|
49
|
+
footer?: Snippet<[TableFooterSlotProps<T>]>;
|
|
50
|
+
}
|
|
51
|
+
export interface TableHeaderSlotProps<T> {
|
|
52
|
+
column: TableColumn<T>;
|
|
53
|
+
columnIndex: number;
|
|
54
|
+
sortDirection: 'asc' | 'desc' | null;
|
|
55
|
+
toggleSort: () => void;
|
|
56
|
+
}
|
|
57
|
+
export interface TableCellSlotProps<T> {
|
|
58
|
+
row: T;
|
|
59
|
+
column: TableColumn<T>;
|
|
60
|
+
rowIndex: number;
|
|
61
|
+
columnIndex: number;
|
|
62
|
+
value: unknown;
|
|
63
|
+
}
|
|
64
|
+
export interface TableFooterSlotProps<T> {
|
|
65
|
+
column: TableColumn<T>;
|
|
66
|
+
columnIndex: number;
|
|
67
|
+
rows: T[];
|
|
68
|
+
}
|
|
69
|
+
export interface TableRowSlotProps<T> {
|
|
70
|
+
row: T;
|
|
71
|
+
rowIndex: number;
|
|
72
|
+
selected: boolean;
|
|
73
|
+
expanded: boolean;
|
|
74
|
+
toggleExpand: () => void;
|
|
75
|
+
toggleSelect: () => void;
|
|
76
|
+
}
|
|
77
|
+
export type SortDirection = 'asc' | 'desc';
|
|
78
|
+
export interface SortItem {
|
|
79
|
+
key: string;
|
|
80
|
+
direction: SortDirection;
|
|
81
|
+
}
|
|
82
|
+
export type SortState = SortItem[];
|
|
83
|
+
export interface TableProps<T extends Record<string, any> = Record<string, any>> extends Omit<HTMLAttributes<HTMLDivElement>, 'class'> {
|
|
84
|
+
/** Bindable reference to the root wrapper DOM element. */
|
|
85
|
+
ref?: HTMLElement | null;
|
|
86
|
+
/**
|
|
87
|
+
* The HTML element the root should render as.
|
|
88
|
+
* @default 'div'
|
|
89
|
+
*/
|
|
90
|
+
as?: 'div' | 'section' | 'article' | 'aside' | 'main';
|
|
91
|
+
/** Array of row data objects. @default [] */
|
|
92
|
+
data?: T[];
|
|
93
|
+
/** Column definitions. Auto-generated from data keys if omitted. */
|
|
94
|
+
columns?: TableColumn<T>[];
|
|
95
|
+
/** Unique row identifier key. Falls back to row index. */
|
|
96
|
+
rowKey?: keyof T & string;
|
|
97
|
+
/** Table caption text (rendered as sr-only for accessibility). */
|
|
98
|
+
caption?: string;
|
|
99
|
+
/** Current sort state. Use `bind:sorting` for two-way binding. */
|
|
100
|
+
sorting?: SortState;
|
|
101
|
+
/** Allow multi-column sorting via shift+click. @default false */
|
|
102
|
+
multiSort?: boolean;
|
|
103
|
+
/** Disable client-side sorting (for server-side). @default false */
|
|
104
|
+
manualSorting?: boolean;
|
|
105
|
+
/** Callback fired when sort state changes. */
|
|
106
|
+
onSortingChange?: (sorting: SortState) => void;
|
|
107
|
+
/** Global filter search string. Use `bind:globalFilter`. */
|
|
108
|
+
globalFilter?: string;
|
|
109
|
+
/** Limit global filter to specific column keys. Defaults to all string columns. */
|
|
110
|
+
globalFilterKeys?: (keyof T & string)[];
|
|
111
|
+
/** Disable client-side filtering. @default false */
|
|
112
|
+
manualFiltering?: boolean;
|
|
113
|
+
/** Callback fired when global filter changes. */
|
|
114
|
+
onGlobalFilterChange?: (value: string) => void;
|
|
115
|
+
/** Per-column filter values. Use `bind:columnFilters`. */
|
|
116
|
+
columnFilters?: Record<string, string>;
|
|
117
|
+
/** Callback fired when column filters change. */
|
|
118
|
+
onColumnFiltersChange?: (filters: Record<string, string>) => void;
|
|
119
|
+
/** Current page index (0-based). Use `bind:page`. */
|
|
120
|
+
page?: number;
|
|
121
|
+
/** Rows per page. @default 10 */
|
|
122
|
+
pageSize?: number;
|
|
123
|
+
/** Disable client-side pagination. @default false */
|
|
124
|
+
manualPagination?: boolean;
|
|
125
|
+
/** Total row count (required for manualPagination). */
|
|
126
|
+
total?: number;
|
|
127
|
+
/** Callback fired when page changes. */
|
|
128
|
+
onPageChange?: (page: number) => void;
|
|
129
|
+
/** Selection mode. undefined = disabled. */
|
|
130
|
+
selection?: 'single' | 'multiple';
|
|
131
|
+
/** Currently selected rows. Use `bind:selectedRows`. */
|
|
132
|
+
selectedRows?: T[];
|
|
133
|
+
/** Callback fired when selection changes. */
|
|
134
|
+
onSelectionChange?: (rows: T[]) => void;
|
|
135
|
+
/** Column visibility map. Use `bind:columnVisibility`. */
|
|
136
|
+
columnVisibility?: Record<string, boolean>;
|
|
137
|
+
/** Callback fired when visibility changes. */
|
|
138
|
+
onColumnVisibilityChange?: (visibility: Record<string, boolean>) => void;
|
|
139
|
+
/** Column pinning configuration. Use `bind:columnPinning`. */
|
|
140
|
+
columnPinning?: {
|
|
141
|
+
left?: string[];
|
|
142
|
+
right?: string[];
|
|
143
|
+
};
|
|
144
|
+
/** Row keys to pin at the top of the table. Use `bind:pinnedRows`. */
|
|
145
|
+
pinnedRows?: (string | number)[];
|
|
146
|
+
/** Callback fired when pinned rows change. */
|
|
147
|
+
onPinnedRowsChange?: (keys: (string | number)[]) => void;
|
|
148
|
+
/** Expanded row keys. Use `bind:expandedRows`. */
|
|
149
|
+
expandedRows?: (string | number)[];
|
|
150
|
+
/** Callback fired when expanded rows change. */
|
|
151
|
+
onExpandedChange?: (keys: (string | number)[]) => void;
|
|
152
|
+
/** Column width overrides (key -> px). Use `bind:columnSizing`. */
|
|
153
|
+
columnSizing?: Record<string, number>;
|
|
154
|
+
/** Callback fired when column sizing changes. */
|
|
155
|
+
onColumnSizingChange?: (sizing: Record<string, number>) => void;
|
|
156
|
+
/** Show loading progress bar. @default false */
|
|
157
|
+
loading?: boolean;
|
|
158
|
+
/** Loading bar color. @default 'primary' */
|
|
159
|
+
loadingColor?: NonNullable<TableVariantProps['loadingColor']>;
|
|
160
|
+
/** Loading bar animation. @default 'carousel' */
|
|
161
|
+
loadingAnimation?: NonNullable<TableVariantProps['loadingAnimation']>;
|
|
162
|
+
/** Text shown when data is empty. @default 'No data.' */
|
|
163
|
+
empty?: string;
|
|
164
|
+
/** Alternate row background colors. @default false */
|
|
165
|
+
striped?: boolean;
|
|
166
|
+
/** Show hover effect on interactive rows. @default true */
|
|
167
|
+
hoverable?: boolean;
|
|
168
|
+
/** Sticky header/footer. @default false */
|
|
169
|
+
sticky?: boolean | 'header' | 'footer';
|
|
170
|
+
/** Callback fired when a row is clicked. */
|
|
171
|
+
onRowClick?: (row: T, index: number, event: MouseEvent) => void;
|
|
172
|
+
/** Callback fired when pointer enters/leaves a row. Passes `null` on leave. */
|
|
173
|
+
onRowHover?: (row: T | null, index: number, event: PointerEvent) => void;
|
|
174
|
+
/** Callback fired on row right-click. */
|
|
175
|
+
onRowContextmenu?: (row: T, index: number, event: MouseEvent) => void;
|
|
176
|
+
/** Additional CSS classes for the root element. */
|
|
177
|
+
class?: ClassNameValue;
|
|
178
|
+
/** Override styles for specific table slots. */
|
|
179
|
+
ui?: Partial<Record<TableSlots, ClassNameValue>>;
|
|
180
|
+
/** Custom caption snippet. */
|
|
181
|
+
captionSlot?: Snippet;
|
|
182
|
+
/** Custom empty state snippet. */
|
|
183
|
+
emptySlot?: Snippet;
|
|
184
|
+
/** Custom loading state snippet. */
|
|
185
|
+
loadingSlot?: Snippet;
|
|
186
|
+
/** Custom expanded row content. */
|
|
187
|
+
expandedSlot?: Snippet<[{
|
|
188
|
+
row: T;
|
|
189
|
+
rowIndex: number;
|
|
190
|
+
}]>;
|
|
191
|
+
/** Custom content rendered before data rows in tbody. */
|
|
192
|
+
bodyTopSlot?: Snippet;
|
|
193
|
+
/** Custom content rendered after data rows in tbody. */
|
|
194
|
+
bodyBottomSlot?: Snippet;
|
|
195
|
+
/** Global header override snippet. */
|
|
196
|
+
headerSlot?: Snippet<[TableHeaderSlotProps<T>]>;
|
|
197
|
+
/** Global cell override snippet. */
|
|
198
|
+
cellSlot?: Snippet<[TableCellSlotProps<T>]>;
|
|
199
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { TableColumn, SortState } from './table.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Auto-generate columns from the first data row's keys.
|
|
4
|
+
*/
|
|
5
|
+
export declare function autoGenerateColumns<T extends Record<string, any>>(data: T[]): TableColumn<T>[];
|
|
6
|
+
/**
|
|
7
|
+
* Resolve a row's unique key.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getRowKey<T extends Record<string, any>>(row: T, rowKey: (keyof T & string) | undefined, index: number): string | number;
|
|
10
|
+
/**
|
|
11
|
+
* Sort data by the given sort state, supporting multi-column sort.
|
|
12
|
+
*/
|
|
13
|
+
export declare function sortData<T extends Record<string, any>>(data: T[], sorting: SortState, columns: TableColumn<T>[]): T[];
|
|
14
|
+
/**
|
|
15
|
+
* Filter data by global filter string across specified keys.
|
|
16
|
+
*/
|
|
17
|
+
export declare function filterByGlobal<T extends Record<string, any>>(data: T[], globalFilter: string, filterKeys?: (keyof T & string)[]): T[];
|
|
18
|
+
/**
|
|
19
|
+
* Filter data by per-column filter values.
|
|
20
|
+
*/
|
|
21
|
+
export declare function filterByColumns<T extends Record<string, any>>(data: T[], columnFilters: Record<string, string>, columns: TableColumn<T>[]): T[];
|
|
22
|
+
/**
|
|
23
|
+
* Paginate data — returns a slice for the given page.
|
|
24
|
+
*/
|
|
25
|
+
export declare function paginateData<T>(data: T[], page: number, pageSize: number): T[];
|
|
26
|
+
/**
|
|
27
|
+
* Compute total pages.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getTotalPages(totalRows: number, pageSize: number): number;
|
|
30
|
+
/**
|
|
31
|
+
* Order columns: left-pinned first, unpinned in original order, right-pinned last.
|
|
32
|
+
* Also filter by visibility.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveVisibleColumns<T extends Record<string, any>>(columns: TableColumn<T>[], columnVisibility?: Record<string, boolean>, columnPinning?: {
|
|
35
|
+
left?: string[];
|
|
36
|
+
right?: string[];
|
|
37
|
+
}): TableColumn<T>[];
|
|
38
|
+
/**
|
|
39
|
+
* Compute cumulative left/right offsets for pinned columns.
|
|
40
|
+
*/
|
|
41
|
+
export declare function computePinOffsets<T extends Record<string, any>>(columns: TableColumn<T>[], columnSizing: Record<string, number>, columnPinning?: {
|
|
42
|
+
left?: string[];
|
|
43
|
+
right?: string[];
|
|
44
|
+
}): Map<string, {
|
|
45
|
+
side: 'left' | 'right';
|
|
46
|
+
offset: number;
|
|
47
|
+
}>;
|
|
48
|
+
/**
|
|
49
|
+
* Format cell value for display — handles null/undefined/empty string.
|
|
50
|
+
*/
|
|
51
|
+
export declare function formatCellValue(value: unknown): string;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-generate columns from the first data row's keys.
|
|
3
|
+
*/
|
|
4
|
+
export function autoGenerateColumns(data) {
|
|
5
|
+
if (data.length === 0)
|
|
6
|
+
return [];
|
|
7
|
+
return Object.keys(data[0]).map((key) => ({
|
|
8
|
+
key: key,
|
|
9
|
+
label: key.charAt(0).toUpperCase() + key.slice(1)
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a row's unique key.
|
|
14
|
+
*/
|
|
15
|
+
export function getRowKey(row, rowKey, index) {
|
|
16
|
+
if (rowKey && row[rowKey] !== undefined)
|
|
17
|
+
return row[rowKey];
|
|
18
|
+
return index;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Default sort comparator — handles string, number, boolean, null/undefined.
|
|
22
|
+
*/
|
|
23
|
+
function defaultCompare(a, b) {
|
|
24
|
+
if (a === b)
|
|
25
|
+
return 0;
|
|
26
|
+
if (a === null || a === undefined)
|
|
27
|
+
return 1;
|
|
28
|
+
if (b === null || b === undefined)
|
|
29
|
+
return -1;
|
|
30
|
+
if (typeof a === 'number' && typeof b === 'number')
|
|
31
|
+
return a - b;
|
|
32
|
+
if (typeof a === 'boolean' && typeof b === 'boolean')
|
|
33
|
+
return a === b ? 0 : a ? -1 : 1;
|
|
34
|
+
return String(a).localeCompare(String(b));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Sort data by the given sort state, supporting multi-column sort.
|
|
38
|
+
*/
|
|
39
|
+
export function sortData(data, sorting, columns) {
|
|
40
|
+
if (sorting.length === 0)
|
|
41
|
+
return data;
|
|
42
|
+
const columnMap = new Map(columns.map((c) => [c.key, c]));
|
|
43
|
+
const sorted = [...data];
|
|
44
|
+
sorted.sort((a, b) => {
|
|
45
|
+
for (const { key, direction } of sorting) {
|
|
46
|
+
const col = columnMap.get(key);
|
|
47
|
+
const multiplier = direction === 'desc' ? -1 : 1;
|
|
48
|
+
let result;
|
|
49
|
+
if (col?.sortFn) {
|
|
50
|
+
result = col.sortFn(a, b);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
result = defaultCompare(a[key], b[key]);
|
|
54
|
+
}
|
|
55
|
+
if (result !== 0)
|
|
56
|
+
return result * multiplier;
|
|
57
|
+
}
|
|
58
|
+
return 0;
|
|
59
|
+
});
|
|
60
|
+
return sorted;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Filter data by global filter string across specified keys.
|
|
64
|
+
*/
|
|
65
|
+
export function filterByGlobal(data, globalFilter, filterKeys) {
|
|
66
|
+
if (!globalFilter)
|
|
67
|
+
return data;
|
|
68
|
+
const search = globalFilter.toLowerCase();
|
|
69
|
+
return data.filter((row) => {
|
|
70
|
+
const keys = filterKeys ?? Object.keys(row);
|
|
71
|
+
return keys.some((key) => {
|
|
72
|
+
const val = row[key];
|
|
73
|
+
if (val === null || val === undefined)
|
|
74
|
+
return false;
|
|
75
|
+
return String(val).toLowerCase().includes(search);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Filter data by per-column filter values.
|
|
81
|
+
*/
|
|
82
|
+
export function filterByColumns(data, columnFilters, columns) {
|
|
83
|
+
const activeFilters = Object.entries(columnFilters).filter(([, v]) => v !== '');
|
|
84
|
+
if (activeFilters.length === 0)
|
|
85
|
+
return data;
|
|
86
|
+
const columnMap = new Map(columns.map((c) => [c.key, c]));
|
|
87
|
+
return data.filter((row) => activeFilters.every(([key, filterValue]) => {
|
|
88
|
+
const col = columnMap.get(key);
|
|
89
|
+
if (col?.filterFn)
|
|
90
|
+
return col.filterFn(row, filterValue);
|
|
91
|
+
const val = row[key];
|
|
92
|
+
if (val === null || val === undefined)
|
|
93
|
+
return false;
|
|
94
|
+
return String(val).toLowerCase().includes(filterValue.toLowerCase());
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Paginate data — returns a slice for the given page.
|
|
99
|
+
*/
|
|
100
|
+
export function paginateData(data, page, pageSize) {
|
|
101
|
+
const start = page * pageSize;
|
|
102
|
+
return data.slice(start, start + pageSize);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Compute total pages.
|
|
106
|
+
*/
|
|
107
|
+
export function getTotalPages(totalRows, pageSize) {
|
|
108
|
+
return Math.max(1, Math.ceil(totalRows / pageSize));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Order columns: left-pinned first, unpinned in original order, right-pinned last.
|
|
112
|
+
* Also filter by visibility.
|
|
113
|
+
*/
|
|
114
|
+
export function resolveVisibleColumns(columns, columnVisibility, columnPinning) {
|
|
115
|
+
const visible = columns.filter((col) => {
|
|
116
|
+
if (col.visible === false)
|
|
117
|
+
return false;
|
|
118
|
+
if (columnVisibility && columnVisibility[col.key] === false)
|
|
119
|
+
return false;
|
|
120
|
+
return true;
|
|
121
|
+
});
|
|
122
|
+
if (!columnPinning)
|
|
123
|
+
return visible;
|
|
124
|
+
const leftKeys = new Set(columnPinning.left ?? []);
|
|
125
|
+
const rightKeys = new Set(columnPinning.right ?? []);
|
|
126
|
+
const left = visible.filter((c) => leftKeys.has(c.key));
|
|
127
|
+
const center = visible.filter((c) => !leftKeys.has(c.key) && !rightKeys.has(c.key));
|
|
128
|
+
const right = visible.filter((c) => rightKeys.has(c.key));
|
|
129
|
+
return [...left, ...center, ...right];
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Compute cumulative left/right offsets for pinned columns.
|
|
133
|
+
*/
|
|
134
|
+
export function computePinOffsets(columns, columnSizing, columnPinning) {
|
|
135
|
+
const offsets = new Map();
|
|
136
|
+
if (!columnPinning)
|
|
137
|
+
return offsets;
|
|
138
|
+
const leftKeys = columnPinning.left ?? [];
|
|
139
|
+
const rightKeys = columnPinning.right ?? [];
|
|
140
|
+
let leftOffset = 0;
|
|
141
|
+
for (const key of leftKeys) {
|
|
142
|
+
const col = columns.find((c) => c.key === key);
|
|
143
|
+
if (!col)
|
|
144
|
+
continue;
|
|
145
|
+
offsets.set(key, { side: 'left', offset: leftOffset });
|
|
146
|
+
leftOffset += columnSizing[key] ?? col.width ?? 150;
|
|
147
|
+
}
|
|
148
|
+
let rightOffset = 0;
|
|
149
|
+
for (let i = rightKeys.length - 1; i >= 0; i--) {
|
|
150
|
+
const key = rightKeys[i];
|
|
151
|
+
const col = columns.find((c) => c.key === key);
|
|
152
|
+
if (!col)
|
|
153
|
+
continue;
|
|
154
|
+
offsets.set(key, { side: 'right', offset: rightOffset });
|
|
155
|
+
rightOffset += columnSizing[key] ?? col.width ?? 150;
|
|
156
|
+
}
|
|
157
|
+
return offsets;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Format cell value for display — handles null/undefined/empty string.
|
|
161
|
+
*/
|
|
162
|
+
export function formatCellValue(value) {
|
|
163
|
+
if (value === null || value === undefined || value === '')
|
|
164
|
+
return '\u00A0';
|
|
165
|
+
return String(value);
|
|
166
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { type VariantProps } from 'tailwind-variants';
|
|
2
|
+
export declare const tableVariants: import("tailwind-variants").TVReturnType<{
|
|
3
|
+
pinned: {
|
|
4
|
+
true: {
|
|
5
|
+
th: string;
|
|
6
|
+
td: string;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
hoverable: {
|
|
10
|
+
true: {
|
|
11
|
+
tbody: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
sticky: {
|
|
15
|
+
true: {
|
|
16
|
+
thead: string;
|
|
17
|
+
tfoot: string;
|
|
18
|
+
};
|
|
19
|
+
header: {
|
|
20
|
+
thead: string;
|
|
21
|
+
};
|
|
22
|
+
footer: {
|
|
23
|
+
tfoot: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
striped: {
|
|
27
|
+
true: {
|
|
28
|
+
tbody: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
loading: {
|
|
32
|
+
true: {
|
|
33
|
+
thead: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
loadingColor: {
|
|
37
|
+
primary: string;
|
|
38
|
+
secondary: string;
|
|
39
|
+
tertiary: string;
|
|
40
|
+
success: string;
|
|
41
|
+
warning: string;
|
|
42
|
+
error: string;
|
|
43
|
+
info: string;
|
|
44
|
+
surface: string;
|
|
45
|
+
};
|
|
46
|
+
loadingAnimation: {
|
|
47
|
+
carousel: string;
|
|
48
|
+
'carousel-inverse': string;
|
|
49
|
+
swing: string;
|
|
50
|
+
elastic: string;
|
|
51
|
+
};
|
|
52
|
+
}, {
|
|
53
|
+
root: string;
|
|
54
|
+
base: string;
|
|
55
|
+
caption: string;
|
|
56
|
+
thead: string;
|
|
57
|
+
tbody: string[];
|
|
58
|
+
tfoot: string;
|
|
59
|
+
tr: string;
|
|
60
|
+
th: string[];
|
|
61
|
+
td: string[];
|
|
62
|
+
separator: string;
|
|
63
|
+
empty: string;
|
|
64
|
+
loading: string;
|
|
65
|
+
}, undefined, {
|
|
66
|
+
pinned: {
|
|
67
|
+
true: {
|
|
68
|
+
th: string;
|
|
69
|
+
td: string;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
hoverable: {
|
|
73
|
+
true: {
|
|
74
|
+
tbody: string;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
sticky: {
|
|
78
|
+
true: {
|
|
79
|
+
thead: string;
|
|
80
|
+
tfoot: string;
|
|
81
|
+
};
|
|
82
|
+
header: {
|
|
83
|
+
thead: string;
|
|
84
|
+
};
|
|
85
|
+
footer: {
|
|
86
|
+
tfoot: string;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
striped: {
|
|
90
|
+
true: {
|
|
91
|
+
tbody: string;
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
loading: {
|
|
95
|
+
true: {
|
|
96
|
+
thead: string;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
loadingColor: {
|
|
100
|
+
primary: string;
|
|
101
|
+
secondary: string;
|
|
102
|
+
tertiary: string;
|
|
103
|
+
success: string;
|
|
104
|
+
warning: string;
|
|
105
|
+
error: string;
|
|
106
|
+
info: string;
|
|
107
|
+
surface: string;
|
|
108
|
+
};
|
|
109
|
+
loadingAnimation: {
|
|
110
|
+
carousel: string;
|
|
111
|
+
'carousel-inverse': string;
|
|
112
|
+
swing: string;
|
|
113
|
+
elastic: string;
|
|
114
|
+
};
|
|
115
|
+
}, {
|
|
116
|
+
root: string;
|
|
117
|
+
base: string;
|
|
118
|
+
caption: string;
|
|
119
|
+
thead: string;
|
|
120
|
+
tbody: string[];
|
|
121
|
+
tfoot: string;
|
|
122
|
+
tr: string;
|
|
123
|
+
th: string[];
|
|
124
|
+
td: string[];
|
|
125
|
+
separator: string;
|
|
126
|
+
empty: string;
|
|
127
|
+
loading: string;
|
|
128
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
129
|
+
pinned: {
|
|
130
|
+
true: {
|
|
131
|
+
th: string;
|
|
132
|
+
td: string;
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
hoverable: {
|
|
136
|
+
true: {
|
|
137
|
+
tbody: string;
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
sticky: {
|
|
141
|
+
true: {
|
|
142
|
+
thead: string;
|
|
143
|
+
tfoot: string;
|
|
144
|
+
};
|
|
145
|
+
header: {
|
|
146
|
+
thead: string;
|
|
147
|
+
};
|
|
148
|
+
footer: {
|
|
149
|
+
tfoot: string;
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
striped: {
|
|
153
|
+
true: {
|
|
154
|
+
tbody: string;
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
loading: {
|
|
158
|
+
true: {
|
|
159
|
+
thead: string;
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
loadingColor: {
|
|
163
|
+
primary: string;
|
|
164
|
+
secondary: string;
|
|
165
|
+
tertiary: string;
|
|
166
|
+
success: string;
|
|
167
|
+
warning: string;
|
|
168
|
+
error: string;
|
|
169
|
+
info: string;
|
|
170
|
+
surface: string;
|
|
171
|
+
};
|
|
172
|
+
loadingAnimation: {
|
|
173
|
+
carousel: string;
|
|
174
|
+
'carousel-inverse': string;
|
|
175
|
+
swing: string;
|
|
176
|
+
elastic: string;
|
|
177
|
+
};
|
|
178
|
+
}, {
|
|
179
|
+
root: string;
|
|
180
|
+
base: string;
|
|
181
|
+
caption: string;
|
|
182
|
+
thead: string;
|
|
183
|
+
tbody: string[];
|
|
184
|
+
tfoot: string;
|
|
185
|
+
tr: string;
|
|
186
|
+
th: string[];
|
|
187
|
+
td: string[];
|
|
188
|
+
separator: string;
|
|
189
|
+
empty: string;
|
|
190
|
+
loading: string;
|
|
191
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
192
|
+
export type TableVariantProps = VariantProps<typeof tableVariants>;
|
|
193
|
+
export type TableSlots = keyof ReturnType<typeof tableVariants>;
|
|
194
|
+
export declare const tableDefaults: {
|
|
195
|
+
defaultVariants: {
|
|
196
|
+
hoverable: true;
|
|
197
|
+
loadingColor: "primary";
|
|
198
|
+
loadingAnimation: "carousel";
|
|
199
|
+
loading?: boolean | undefined;
|
|
200
|
+
sticky?: boolean | "footer" | "header" | undefined;
|
|
201
|
+
pinned?: boolean | undefined;
|
|
202
|
+
striped?: boolean | undefined;
|
|
203
|
+
};
|
|
204
|
+
slots: Partial<Record<TableSlots, string>>;
|
|
205
|
+
};
|