pika-ux 1.0.1 → 1.0.2
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/package.json
CHANGED
|
@@ -21,9 +21,11 @@
|
|
|
21
21
|
|
|
22
22
|
/** When provided, this is the title of the button */
|
|
23
23
|
title?: string;
|
|
24
|
+
|
|
25
|
+
size?: 'small' | 'medium';
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
const { children, value: propValue, embedded = false, truncateAfter = 0, showTextAsLink = false, linkCallbackFn, title }: Props = $props();
|
|
28
|
+
const { children, value: propValue, embedded = false, truncateAfter = 0, showTextAsLink = false, linkCallbackFn, title, size = 'medium' }: Props = $props();
|
|
27
29
|
|
|
28
30
|
let hiddenRef = $state<HTMLElement>() as HTMLElement;
|
|
29
31
|
let value: string | undefined = $state(undefined);
|
|
@@ -118,16 +120,16 @@
|
|
|
118
120
|
|
|
119
121
|
<span class="inline-flex w-fit items-center {embedded ? '' : 'border border-gray-200 rounded-sm'}">
|
|
120
122
|
{#if showTextAsLink}
|
|
121
|
-
<Button class="p-0" variant="link" onclick={() => linkCallbackFn?.()}>{buttonTitle}</Button>
|
|
123
|
+
<Button class="p-0 {size === 'small' ? 'text-xs' : ''}" variant="link" onclick={() => linkCallbackFn?.()}>{buttonTitle}</Button>
|
|
122
124
|
{:else}
|
|
123
|
-
<span class={embedded ? '' : 'border-r border-gray-200 px-2'}>{buttonTitle}</span>
|
|
125
|
+
<span class="{embedded ? '' : 'border-r border-gray-200 px-2'} {size === 'small' ? 'text-xs' : ''}">{buttonTitle}</span>
|
|
124
126
|
{/if}
|
|
125
|
-
<span class="w-6 h-6 flex items-center justify-center">
|
|
127
|
+
<span class="{size === 'small' ? 'w-4 h-4 ml-1' : 'w-6 h-6'} flex items-center justify-center">
|
|
126
128
|
{#if showCheckmark}
|
|
127
|
-
<Check class="w-3.5 h-3.5 text-green-500" />
|
|
129
|
+
<Check class="{size === 'small' ? 'w-2.5 h-2.5' : 'w-3.5 h-3.5'} text-green-500" />
|
|
128
130
|
{:else}
|
|
129
131
|
<Button variant="ghost" class="h-full w-full min-h-0 p-0 ml-1 rounded-none hover:border-blue-100 hover:border hover:rounded-sm" onclick={copy} disabled={!value}>
|
|
130
|
-
<Copy class="w-3.5 h-3.5 text-gray-400" />
|
|
132
|
+
<Copy class="{size === 'small' ? 'w-2.5 h-2.5' : 'w-3.5 h-3.5'} text-gray-400" />
|
|
131
133
|
</Button>
|
|
132
134
|
{/if}
|
|
133
135
|
</span>
|
|
@@ -20,22 +20,48 @@ PikaTablePagination - Pagination controls for PikaTable
|
|
|
20
20
|
table: Table<TData>;
|
|
21
21
|
serverSide: ServerSideConfig;
|
|
22
22
|
showRowsPerPage?: boolean;
|
|
23
|
+
globalFilterActive?: boolean;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
let { table, serverSide, showRowsPerPage = true }: Props = $props();
|
|
26
|
+
let { table, serverSide, showRowsPerPage = true, globalFilterActive = false }: Props = $props();
|
|
26
27
|
|
|
27
28
|
// For cursor-based pagination, we can't jump to arbitrary pages
|
|
28
29
|
const isCursorBased = $derived(serverSide?.paginationMode === 'cursor');
|
|
30
|
+
|
|
31
|
+
// Calculate counts for display
|
|
32
|
+
const totalLoadedRows = $derived(table.getCoreRowModel().rows.length);
|
|
33
|
+
const visibleRows = $derived(table.getRowModel().rows.length);
|
|
34
|
+
const selectedRows = $derived(table.getFilteredSelectedRowModel().rows.length);
|
|
29
35
|
</script>
|
|
30
36
|
|
|
31
37
|
<div class="flex items-center justify-between px-2">
|
|
32
|
-
<div class="
|
|
33
|
-
{#if
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
<div class="flex-1 text-sm">
|
|
39
|
+
{#if globalFilterActive && visibleRows < totalLoadedRows}
|
|
40
|
+
<!-- Client-side filtering active -->
|
|
41
|
+
<div class="flex items-center gap-2">
|
|
42
|
+
<span class="text-sm font-medium text-blue-700">
|
|
43
|
+
Showing {visibleRows} of {totalLoadedRows} loaded results
|
|
44
|
+
</span>
|
|
45
|
+
{#if selectedRows > 0}
|
|
46
|
+
<span class="text-muted-foreground text-xs">
|
|
47
|
+
({selectedRows} selected)
|
|
48
|
+
</span>
|
|
49
|
+
{/if}
|
|
50
|
+
</div>
|
|
51
|
+
{:else if selectedRows > 0}
|
|
52
|
+
<!-- No filtering, just show selection -->
|
|
53
|
+
<span class="text-muted-foreground">
|
|
54
|
+
{#if serverSide?.tableState?.totalRecords !== undefined}
|
|
55
|
+
{selectedRows} of {serverSide.tableState.totalRecords} total row(s) selected.
|
|
56
|
+
{:else}
|
|
57
|
+
{selectedRows} of {visibleRows} row(s) selected.
|
|
58
|
+
{/if}
|
|
59
|
+
</span>
|
|
36
60
|
{:else}
|
|
37
|
-
|
|
38
|
-
|
|
61
|
+
<!-- No filtering, no selection -->
|
|
62
|
+
<span class="text-muted-foreground">
|
|
63
|
+
{totalLoadedRows} row(s) loaded
|
|
64
|
+
</span>
|
|
39
65
|
{/if}
|
|
40
66
|
</div>
|
|
41
67
|
<div class="flex items-center space-x-8">
|
|
@@ -172,7 +172,9 @@ PikaTable - A reusable table component with server-side pagination, sorting, and
|
|
|
172
172
|
return !!serverSideConfig;
|
|
173
173
|
},
|
|
174
174
|
get manualFiltering() {
|
|
175
|
-
|
|
175
|
+
// CRITICAL: If using client-side global filter, we must disable manualFiltering
|
|
176
|
+
// Otherwise TanStack skips ALL filtering logic before checking manualGlobalFilter
|
|
177
|
+
return serverSideConfig ? !serverSideConfig.clientSideGlobalFilter : false;
|
|
176
178
|
},
|
|
177
179
|
get manualPagination() {
|
|
178
180
|
return !!serverSideConfig;
|
|
@@ -183,13 +185,19 @@ PikaTable - A reusable table component with server-side pagination, sorting, and
|
|
|
183
185
|
|
|
184
186
|
const tableState = serverSideConfig?.tableState;
|
|
185
187
|
|
|
188
|
+
// For server-side pagination, use the pageSize from tableState if available
|
|
189
|
+
const effectivePageSize = tableState?.pageSize ?? pageSize;
|
|
190
|
+
|
|
186
191
|
// Calculate pageCount if totalRecords is available, regardless of pagination mode
|
|
187
|
-
if (tableState?.totalRecords !== undefined &&
|
|
188
|
-
return Math.ceil(tableState.totalRecords /
|
|
192
|
+
if (tableState?.totalRecords !== undefined && effectivePageSize > 0) {
|
|
193
|
+
return Math.ceil(tableState.totalRecords / effectivePageSize);
|
|
189
194
|
}
|
|
190
195
|
// For cursor-based pagination without totalRecords, we don't know the total
|
|
191
196
|
if (serverState.paginationMode === 'cursor') {
|
|
192
|
-
|
|
197
|
+
// For cursor pagination, determine page count based on hasNextPage
|
|
198
|
+
// If we're on page 0 and hasNextPage is false, we have 1 page
|
|
199
|
+
// If hasNextPage is true, we have at least 2 pages
|
|
200
|
+
return tableState?.hasNextPage ? pageIndex + 2 : pageIndex + 1;
|
|
193
201
|
}
|
|
194
202
|
return undefined; // Unknown page count
|
|
195
203
|
},
|
|
@@ -251,17 +259,21 @@ PikaTable - A reusable table component with server-side pagination, sorting, and
|
|
|
251
259
|
if (globalFilterProps?.showGlobalFilter) {
|
|
252
260
|
globalFilterProps.globalFilterValue = value;
|
|
253
261
|
|
|
254
|
-
//
|
|
255
|
-
if (serverSideConfig) {
|
|
262
|
+
// Only trigger server request if global filter is server-side (not client-side)
|
|
263
|
+
if (serverSideConfig && !serverSideConfig.clientSideGlobalFilter) {
|
|
256
264
|
triggerServerRequest();
|
|
257
265
|
}
|
|
266
|
+
// Otherwise, global filter will work client-side automatically via getFilteredRowModel
|
|
258
267
|
}
|
|
259
268
|
},
|
|
260
269
|
|
|
261
270
|
// === ROW MODELS ===
|
|
262
271
|
getCoreRowModel: getCoreRowModel(),
|
|
263
272
|
...(serverSideConfig
|
|
264
|
-
? {
|
|
273
|
+
? {
|
|
274
|
+
// Include filtered row model ONLY if using client-side global filter
|
|
275
|
+
...(serverSideConfig.clientSideGlobalFilter ? { getFilteredRowModel: getFilteredRowModel() } : {})
|
|
276
|
+
}
|
|
265
277
|
: {
|
|
266
278
|
getFilteredRowModel: getFilteredRowModel(),
|
|
267
279
|
getPaginationRowModel: getPaginationRowModel(),
|
|
@@ -278,7 +290,12 @@ PikaTable - A reusable table component with server-side pagination, sorting, and
|
|
|
278
290
|
</div>
|
|
279
291
|
{#if paginationPlacement === 'top' || paginationPlacement === 'both'}
|
|
280
292
|
<div class="mt-2 pb-1">
|
|
281
|
-
<TablePagination
|
|
293
|
+
<TablePagination
|
|
294
|
+
{table}
|
|
295
|
+
serverSide={serverSideConfig}
|
|
296
|
+
{showRowsPerPage}
|
|
297
|
+
globalFilterActive={!!(serverSideConfig?.clientSideGlobalFilter && globalFilterProps?.globalFilterValue)}
|
|
298
|
+
/>
|
|
282
299
|
</div>
|
|
283
300
|
{/if}
|
|
284
301
|
<div class="rounded-md border h-full flex flex-col overflow-y-auto">
|
|
@@ -315,7 +332,12 @@ PikaTable - A reusable table component with server-side pagination, sorting, and
|
|
|
315
332
|
</div>
|
|
316
333
|
{#if paginationPlacement === 'bottom' || paginationPlacement === 'both'}
|
|
317
334
|
<div class="mt-2">
|
|
318
|
-
<TablePagination
|
|
335
|
+
<TablePagination
|
|
336
|
+
{table}
|
|
337
|
+
serverSide={serverSideConfig}
|
|
338
|
+
{showRowsPerPage}
|
|
339
|
+
globalFilterActive={!!(serverSideConfig?.clientSideGlobalFilter && globalFilterProps?.globalFilterValue)}
|
|
340
|
+
/>
|
|
319
341
|
</div>
|
|
320
342
|
{/if}
|
|
321
343
|
</div>
|
|
@@ -111,6 +111,14 @@ export interface ServerSideConfig {
|
|
|
111
111
|
// === ERROR HANDLING ===
|
|
112
112
|
onError?: (error: string) => void;
|
|
113
113
|
|
|
114
|
+
// === CLIENT-SIDE FILTERING ===
|
|
115
|
+
/**
|
|
116
|
+
* When true, enables client-side global filter even in server-side mode.
|
|
117
|
+
* This allows users to quickly filter already-loaded results without triggering server requests.
|
|
118
|
+
* Column filters still work server-side, but the global filter searches loaded data only.
|
|
119
|
+
*/
|
|
120
|
+
clientSideGlobalFilter?: boolean;
|
|
121
|
+
|
|
114
122
|
// === DYNAMIC TABLE STATE ===
|
|
115
123
|
tableState: ServerSideTableState;
|
|
116
124
|
}
|