@kikiloaw/simple-table 1.1.3 → 1.1.8
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 +5 -10
- package/package.json +1 -1
- package/src/SimpleTable.vue +85 -61
- package/src/components/table/TableHead.vue +1 -1
- package/src/components/table/TableRow.vue +1 -1
package/README.md
CHANGED
|
@@ -30,14 +30,8 @@
|
|
|
30
30
|
npm install @kikiloaw/simple-table
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
### Option 2: Local Copy
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
cp -r path/to/SimpleTable /your-project/src/components/
|
|
37
|
-
```
|
|
38
33
|
|
|
39
34
|
---
|
|
40
|
-
|
|
41
35
|
## 🚀 Quick Start
|
|
42
36
|
|
|
43
37
|
### 1. Import the Component
|
|
@@ -422,10 +416,10 @@ SimpleTable supports three data modes:
|
|
|
422
416
|
|
|
423
417
|
### Protocol Formats
|
|
424
418
|
|
|
425
|
-
| Protocol | When to Use | Backend
|
|
426
|
-
|
|
427
|
-
| **`laravel`** (default) |
|
|
428
|
-
| **`datatables`** |
|
|
419
|
+
| Protocol | When to Use | Backend Compatibility |
|
|
420
|
+
|----------|-------------|-----------------------|
|
|
421
|
+
| **`laravel`** (default) | **Recommended for 99% of cases.** | Standard Laravel pagination, Resource Collections, and custom JSON responses (including simple DataTables-like backends). |
|
|
422
|
+
| **`datatables`** | Rare. | Only use if your backend **strictly requires** `draw`, `start`, and `length` request parameters and fails with standard `page`/`per_page` params. |
|
|
429
423
|
|
|
430
424
|
---
|
|
431
425
|
|
|
@@ -464,6 +458,7 @@ SimpleTable supports three data modes:
|
|
|
464
458
|
| `oddRowColor` | String | `'bg-white'` | Tailwind class for odd rows |
|
|
465
459
|
| `evenRowColor` | String | `'bg-stone-100'` | Tailwind class for even rows |
|
|
466
460
|
| `hoverColor` | String | `'hover:bg-stone-200'` | Tailwind class for row hover |
|
|
461
|
+
| `paginationColor` | String | `'#2563eb'` | Hex color for active pagination button |
|
|
467
462
|
|
|
468
463
|
---
|
|
469
464
|
|
package/package.json
CHANGED
package/src/SimpleTable.vue
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
import { useDebounceFn } from '@vueuse/core'
|
|
15
|
+
import { useDebounceFn, useWindowSize } from '@vueuse/core'
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Props definition
|
|
@@ -49,6 +49,7 @@ interface Props {
|
|
|
49
49
|
oddRowColor?: string // Tailwind color class, e.g. 'bg-white'
|
|
50
50
|
evenRowColor?: string // Tailwind color class, e.g. 'bg-gray-50'
|
|
51
51
|
hoverColor?: string // Tailwind color class for hover, e.g. 'hover:bg-gray-100'. If passed, we'll try to apply group-hover for fixed cols.
|
|
52
|
+
paginationColor?: string // Hex color for active pagination button (default: #2563eb)
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -225,13 +226,24 @@ const serverMeta = computed(() => {
|
|
|
225
226
|
const d = internalData.value as any
|
|
226
227
|
// Handle standard Laravel Paginator or Resource Collection
|
|
227
228
|
const meta = d.meta || d
|
|
229
|
+
|
|
230
|
+
// Polyfill missing 'from' and 'to' if server response (like DataTables custom backend) omits them
|
|
231
|
+
const page = meta.current_page ?? 1
|
|
232
|
+
const pPage = meta.per_page ?? currentPerPage.value
|
|
233
|
+
const total = meta.total ?? 0
|
|
234
|
+
const dataCount = Array.isArray(d.data) ? d.data.length : 0
|
|
235
|
+
|
|
236
|
+
// Calculate fallback values
|
|
237
|
+
const calculatedFrom = total === 0 ? 0 : ((page - 1) * pPage) + 1
|
|
238
|
+
const calculatedTo = total === 0 ? 0 : Math.min(calculatedFrom + dataCount - 1, total)
|
|
239
|
+
|
|
228
240
|
return {
|
|
229
|
-
current_page:
|
|
241
|
+
current_page: page,
|
|
230
242
|
last_page: meta.last_page ?? 1,
|
|
231
|
-
per_page:
|
|
232
|
-
from: meta.from ??
|
|
233
|
-
to: meta.to ??
|
|
234
|
-
total:
|
|
243
|
+
per_page: pPage,
|
|
244
|
+
from: meta.from ?? calculatedFrom,
|
|
245
|
+
to: meta.to ?? calculatedTo,
|
|
246
|
+
total: total,
|
|
235
247
|
links: meta.links ?? []
|
|
236
248
|
}
|
|
237
249
|
})
|
|
@@ -325,9 +337,16 @@ const paginationMeta = computed(() => {
|
|
|
325
337
|
|
|
326
338
|
// -- Computed: Page Numbers for Pagination --
|
|
327
339
|
const pageNumbers = computed(() => {
|
|
328
|
-
const
|
|
329
|
-
const
|
|
330
|
-
const
|
|
340
|
+
const { width } = useWindowSize()
|
|
341
|
+
const isMobile = width.value < 640
|
|
342
|
+
const isExtraSmall = width.value < 550
|
|
343
|
+
|
|
344
|
+
const current = Number(isServerSide.value ? (serverMeta.value?.current_page || 1) : currentPage.value)
|
|
345
|
+
const total = Number(totalPages.value)
|
|
346
|
+
// Show fewer surrounding pages on mobile to prevent overflow
|
|
347
|
+
// delta = 0 means only current page (plus first/last)
|
|
348
|
+
const delta = isExtraSmall ? 0 : (isMobile ? 1 : 2)
|
|
349
|
+
|
|
331
350
|
const pages: (number | string)[] = []
|
|
332
351
|
|
|
333
352
|
// Always show first page
|
|
@@ -803,48 +822,52 @@ function getCellStyle(col: any, index: number, totalCols: number) {
|
|
|
803
822
|
<template>
|
|
804
823
|
<div class="space-y-4">
|
|
805
824
|
<!-- Toolbar -->
|
|
806
|
-
<div v-if="searchable" class="flex flex-
|
|
807
|
-
|
|
808
|
-
<!--
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
<div class="
|
|
813
|
-
<
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
v-for="pageSize in normalizedPageSizes"
|
|
820
|
-
:key="pageSize.value"
|
|
821
|
-
:value="pageSize.value"
|
|
825
|
+
<div v-if="searchable" class="flex flex-wrap items-center justify-between gap-4 w-full">
|
|
826
|
+
|
|
827
|
+
<!-- Left Group: Rows + Search -->
|
|
828
|
+
<div class="flex flex-wrap items-center gap-2 w-full sm:w-auto flex-1">
|
|
829
|
+
|
|
830
|
+
<!-- Rows per page -->
|
|
831
|
+
<div class="flex items-center gap-2 shrink-0">
|
|
832
|
+
<span class="text-sm text-gray-500 whitespace-nowrap">Rows</span>
|
|
833
|
+
<div class="relative h-10 w-[70px]">
|
|
834
|
+
<select
|
|
835
|
+
:value="currentPerPage"
|
|
836
|
+
@change="(e: any) => handlePageSizeChange(e.target.value)"
|
|
837
|
+
class="h-full w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm ring-offset-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 cursor-pointer"
|
|
822
838
|
>
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
839
|
+
<option
|
|
840
|
+
v-for="pageSize in normalizedPageSizes"
|
|
841
|
+
:key="pageSize.value"
|
|
842
|
+
:value="pageSize.value"
|
|
843
|
+
>
|
|
844
|
+
{{ pageSize.label }}
|
|
845
|
+
</option>
|
|
846
|
+
</select>
|
|
847
|
+
</div>
|
|
848
|
+
</div>
|
|
849
|
+
|
|
850
|
+
<!-- Search Input -->
|
|
851
|
+
<div class="relative flex-1 min-w-[200px]">
|
|
852
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-500 pointer-events-none"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
853
|
+
<input
|
|
854
|
+
v-model="searchQuery"
|
|
855
|
+
type="text"
|
|
856
|
+
style="padding-left: 2.5rem !important"
|
|
857
|
+
placeholder="Search..."
|
|
858
|
+
class="flex h-10 w-full rounded-md border border-gray-300 bg-white !pr-3 !pl-10 py-2 text-sm ring-offset-white placeholder:text-gray-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
859
|
+
/>
|
|
860
|
+
</div>
|
|
840
861
|
</div>
|
|
841
|
-
|
|
862
|
+
|
|
863
|
+
<!-- Actions Slot -->
|
|
864
|
+
<div class="flex items-center gap-2 shrink-0 ml-auto sm:ml-0">
|
|
842
865
|
<slot name="actions" :rows="tableData" :columns="columns" />
|
|
843
866
|
</div>
|
|
844
867
|
</div>
|
|
845
868
|
|
|
846
869
|
<!-- Table -->
|
|
847
|
-
<div class="border bg-
|
|
870
|
+
<div class="border bg-white relative">
|
|
848
871
|
<div class="overflow-x-auto">
|
|
849
872
|
<!-- We add min-w-full to Table to ensure it stretches -->
|
|
850
873
|
<Table class="min-w-full table-auto">
|
|
@@ -860,7 +883,7 @@ function getCellStyle(col: any, index: number, totalCols: number) {
|
|
|
860
883
|
>
|
|
861
884
|
<div
|
|
862
885
|
v-if="col.sortable"
|
|
863
|
-
class="flex items-center space-x-2 cursor-pointer select-none hover:text-
|
|
886
|
+
class="flex items-center space-x-2 cursor-pointer select-none hover:text-gray-900 w-full"
|
|
864
887
|
:class="getHeaderJustifyClass(col)"
|
|
865
888
|
@click="handleSort(col)"
|
|
866
889
|
>
|
|
@@ -876,7 +899,7 @@ function getCellStyle(col: any, index: number, totalCols: number) {
|
|
|
876
899
|
<TableRow>
|
|
877
900
|
<TableCell :colspan="columns.length" class="h-24 text-center">
|
|
878
901
|
<div class="flex items-center justify-center">
|
|
879
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-6 w-6 animate-spin text-
|
|
902
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-6 w-6 animate-spin text-gray-500"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
|
|
880
903
|
</div>
|
|
881
904
|
</TableCell>
|
|
882
905
|
</TableRow>
|
|
@@ -938,22 +961,22 @@ function getCellStyle(col: any, index: number, totalCols: number) {
|
|
|
938
961
|
</div>
|
|
939
962
|
|
|
940
963
|
<!-- Loading Overlay -->
|
|
941
|
-
<div v-if="isLoading && tableData.length > 0" class="absolute inset-0 bg-
|
|
942
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-8 w-8 animate-spin text-
|
|
964
|
+
<div v-if="isLoading && tableData.length > 0" class="absolute inset-0 bg-white/50 flex items-center justify-center z-[60]">
|
|
965
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-8 w-8 animate-spin text-blue-600"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
|
|
943
966
|
</div>
|
|
944
967
|
</div>
|
|
945
968
|
|
|
946
969
|
<!-- Pagination -->
|
|
947
|
-
<div class="flex items-center justify-between px-2">
|
|
948
|
-
<div class="text-sm text-
|
|
970
|
+
<div class="flex items-center justify-between flex-wrap gap-4 px-2 py-2">
|
|
971
|
+
<div class="text-sm text-gray-500">
|
|
949
972
|
Showing {{ paginationMeta.from }} to {{ paginationMeta.to }} of {{ paginationMeta.total }} results
|
|
950
973
|
</div>
|
|
951
974
|
<div class="flex items-center space-x-1">
|
|
952
975
|
<!-- Previous Button -->
|
|
953
976
|
<button
|
|
954
|
-
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-
|
|
955
|
-
:disabled="(isServerSide ? serverMeta?.current_page === 1 : currentPage === 1)"
|
|
956
|
-
@click="handlePageChange(isServerSide ? (serverMeta?.current_page || 1) - 1 : currentPage - 1)"
|
|
977
|
+
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-gray-300 bg-white hover:bg-gray-100 hover:text-gray-900 h-9 px-2 sm:px-3"
|
|
978
|
+
:disabled="(isServerSide ? Number(serverMeta?.current_page) === 1 : currentPage === 1)"
|
|
979
|
+
@click="handlePageChange(isServerSide ? (Number(serverMeta?.current_page || 1)) - 1 : currentPage - 1)"
|
|
957
980
|
>
|
|
958
981
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4"><path d="m15 18-6-6 6-6"/></svg>
|
|
959
982
|
<span class="ml-1 hidden sm:inline">Previous</span>
|
|
@@ -964,7 +987,7 @@ function getCellStyle(col: any, index: number, totalCols: number) {
|
|
|
964
987
|
<!-- Ellipsis -->
|
|
965
988
|
<span
|
|
966
989
|
v-if="page === '...'"
|
|
967
|
-
class="inline-flex items-center justify-center h-9 px-3 text-sm text-
|
|
990
|
+
class="inline-flex items-center justify-center h-9 px-2 sm:px-3 text-sm text-gray-500"
|
|
968
991
|
>
|
|
969
992
|
...
|
|
970
993
|
</span>
|
|
@@ -972,12 +995,13 @@ function getCellStyle(col: any, index: number, totalCols: number) {
|
|
|
972
995
|
<!-- Page Number Button -->
|
|
973
996
|
<button
|
|
974
997
|
v-else
|
|
975
|
-
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-
|
|
998
|
+
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border h-9 min-w-[36px] px-2 sm:px-3"
|
|
976
999
|
:class="[
|
|
977
|
-
(isServerSide ? serverMeta?.current_page === page : currentPage === page)
|
|
978
|
-
? '
|
|
979
|
-
: 'border-
|
|
1000
|
+
(isServerSide ? Number(serverMeta?.current_page) === page : currentPage === page)
|
|
1001
|
+
? 'hover:bg-blue-700'
|
|
1002
|
+
: 'border-gray-300 bg-white hover:bg-gray-100 hover:text-gray-900'
|
|
980
1003
|
]"
|
|
1004
|
+
:style="(isServerSide ? Number(serverMeta?.current_page) === page : currentPage === page) ? `background-color: ${props.paginationColor || '#2563eb'} !important; color: white !important; border-color: ${props.paginationColor || '#2563eb'} !important;` : ''"
|
|
981
1005
|
@click="handlePageChange(page as number)"
|
|
982
1006
|
>
|
|
983
1007
|
{{ page }}
|
|
@@ -986,9 +1010,9 @@ function getCellStyle(col: any, index: number, totalCols: number) {
|
|
|
986
1010
|
|
|
987
1011
|
<!-- Next Button -->
|
|
988
1012
|
<button
|
|
989
|
-
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-
|
|
990
|
-
:disabled="(isServerSide ? serverMeta?.current_page === serverMeta?.last_page : currentPage === totalPages)"
|
|
991
|
-
@click="handlePageChange(isServerSide ? (serverMeta?.current_page || 1) + 1 : currentPage + 1)"
|
|
1013
|
+
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-gray-300 bg-white hover:bg-gray-100 hover:text-gray-900 h-9 px-2 sm:px-3"
|
|
1014
|
+
:disabled="(isServerSide ? Number(serverMeta?.current_page) === Number(serverMeta?.last_page) : currentPage === totalPages)"
|
|
1015
|
+
@click="handlePageChange(isServerSide ? (Number(serverMeta?.current_page || 1)) + 1 : currentPage + 1)"
|
|
992
1016
|
>
|
|
993
1017
|
<span class="mr-1 hidden sm:inline">Next</span>
|
|
994
1018
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4"><path d="m9 18 6-6-6-6"/></svg>
|
|
@@ -21,7 +21,7 @@ const delegatedProps = computed(() => {
|
|
|
21
21
|
cn(
|
|
22
22
|
props.height || 'h-[38px]',
|
|
23
23
|
props.padding || 'px-2.5',
|
|
24
|
-
'border-b border-stone-300 align-middle font-bold text-
|
|
24
|
+
'border-b border-stone-300 align-middle font-bold text-gray-500 [&:has([role=checkbox])]:pr-0',
|
|
25
25
|
props.class,
|
|
26
26
|
)
|
|
27
27
|
"
|