@manusiakemos/laravel-tanstack-react 0.1.0 → 0.1.1

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 CHANGED
@@ -23,59 +23,244 @@ composer require manusiakemos/laravel-tanstack
23
23
  ## Quick start
24
24
 
25
25
  ```tsx
26
- import { useDataTable } from '@manusiakemos/laravel-tanstack-react'
27
- import { flexRender } from '@tanstack/react-table'
26
+ import {
27
+ DataTable,
28
+ DataTableFilter,
29
+ DataTablePagination,
30
+ DataTableSearch,
31
+ useDataTable,
32
+ } from '@manusiakemos/laravel-tanstack-react'
28
33
 
29
34
  interface User {
30
35
  id: number
31
36
  name: string
32
37
  email: string
38
+ status: 'active' | 'inactive'
33
39
  }
34
40
 
35
41
  export default function UsersIndex() {
36
42
  const { table, loading, meta } = useDataTable<User>({
37
43
  endpoint: '/datatable/users',
38
44
  columns: [
39
- { accessorKey: 'name', header: 'Nama' },
40
- { accessorKey: 'email', header: 'Email' },
45
+ { accessorKey: 'name', header: 'Name', enableSorting: true },
46
+ { accessorKey: 'email', header: 'Email', enableSorting: true },
47
+ {
48
+ accessorKey: 'status',
49
+ header: 'Status',
50
+ enableColumnFilter: true,
51
+ },
41
52
  ],
42
53
  })
43
54
 
44
55
  return (
45
- <table>
46
- <thead>
47
- {table.getHeaderGroups().map((hg) => (
48
- <tr key={hg.id}>
49
- {hg.headers.map((h) => (
50
- <th
51
- key={h.id}
52
- onClick={h.column.getToggleSortingHandler()}
53
- >
54
- {flexRender(h.column.columnDef.header, h.getContext())}
55
- </th>
56
- ))}
57
- </tr>
58
- ))}
59
- </thead>
60
- <tbody>
61
- {table.getRowModel().rows.map((row) => (
62
- <tr key={row.id}>
63
- {row.getVisibleCells().map((cell) => (
64
- <td key={cell.id}>
65
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
66
- </td>
67
- ))}
68
- </tr>
69
- ))}
70
- </tbody>
71
- </table>
56
+ <div className="space-y-4">
57
+ <div className="flex flex-wrap gap-3">
58
+ <DataTableSearch table={table} placeholder="Search users..." />
59
+ <DataTableFilter
60
+ table={table}
61
+ columnId="status"
62
+ options={[
63
+ { label: 'Active', value: 'active' },
64
+ { label: 'Inactive', value: 'inactive' },
65
+ ]}
66
+ placeholder="All statuses"
67
+ />
68
+ </div>
69
+
70
+ <DataTable table={table} loading={loading} />
71
+
72
+ <DataTablePagination
73
+ table={table}
74
+ meta={meta}
75
+ showPageSize
76
+ showFirstLast
77
+ />
78
+ </div>
72
79
  )
73
80
  }
74
81
  ```
75
82
 
76
83
  That's it. Pagination, sorting, filtering, and global search are all wired up. Your backend controller stays under 10 lines (see the Composer package README).
77
84
 
78
- See the [full Inertia example](./examples/inertia-users-index.tsx) for pagination UI, loading states, and global search input.
85
+ > **Styling.** Components ship pre-styled with [shadcn/ui](https://ui.shadcn.com)-style Tailwind classes (Button, Input, Select, Table primitives are bundled). Make sure Tailwind is configured in your app with the standard shadcn theme tokens (`--background`, `--foreground`, `--primary`, `--border`, `--ring`, `--muted-foreground`, etc.). Every component accepts `className`, fine-grained `classNames`, and a full `render` prop so you can swap in your own UI.
86
+
87
+ See the [full Inertia example](./examples/inertia-users-index.tsx) for pagination, filters, and search wiring.
88
+
89
+ ## Components
90
+
91
+ All four components below are **fully customizable via props** — you can override class names, labels, sub-element styles, or replace the entire UI via the `render` prop while keeping the table wiring intact.
92
+
93
+ ### `<DataTable />`
94
+
95
+ Shadcn-styled table with built-in sort indicators, empty state, and loading state.
96
+
97
+ ```tsx
98
+ <DataTable
99
+ table={table}
100
+ loading={loading}
101
+ emptyMessage="No users yet."
102
+ loadingMessage="Fetching..."
103
+ onRowClick={(user) => navigate(`/users/${user.id}`)}
104
+ className="rounded-lg"
105
+ classNames={{ row: 'hover:bg-muted/30', cell: 'py-3' }}
106
+ />
107
+ ```
108
+
109
+ ### `<DataTableSearch />`
110
+
111
+ Global search. Debounced by default; pass `debounce={false}` to switch to **manual mode with a Search button** (input + button, submits on click or Enter).
112
+
113
+ ```tsx
114
+ {/* Debounced (default, 300ms) */}
115
+ <DataTableSearch table={table} placeholder="Search..." />
116
+
117
+ {/* Custom debounce delay */}
118
+ <DataTableSearch table={table} debounce={500} />
119
+
120
+ {/* Manual submit — Search button */}
121
+ <DataTableSearch table={table} debounce={false} placeholder="Search..." />
122
+
123
+ {/* Fully custom UI */}
124
+ <DataTableSearch
125
+ table={table}
126
+ render={({ value, setValue, submit }) => (
127
+ <MyCombobox value={value} onChange={setValue} onSubmit={submit} />
128
+ )}
129
+ />
130
+ ```
131
+
132
+ | Prop | Type | Default | Description |
133
+ |---|---|---|---|
134
+ | `debounce` | `boolean \| number` | `true` | `true` → 300ms; number → custom ms; `false` → manual + Search button. |
135
+ | `placeholder` | `string` | `'Search...'` | Placeholder text. |
136
+ | `submitLabel` | `ReactNode` | Search icon + "Search" | Button label when `debounce={false}`. |
137
+ | `className` | `string` | — | Root wrapper class. |
138
+ | `inputClassName` | `string` | — | Class merged into the Input. |
139
+ | `buttonClassName` | `string` | — | Class merged into the submit Button. |
140
+ | `onSearch` | `(value: string) => void` | — | Fired when the search is committed. |
141
+ | `render` | `(props) => ReactNode` | — | Full UI override. |
142
+
143
+ ### `<DataTablePagination />`
144
+
145
+ Prev/next (and optional first/last) buttons, page-size selector, row-count summary.
146
+
147
+ ```tsx
148
+ <DataTablePagination
149
+ table={table}
150
+ meta={meta}
151
+ showPageSize
152
+ showFirstLast
153
+ pageSizeOptions={[10, 25, 50, 100]}
154
+ labels={{ previous: 'Prev', next: 'Next', page: 'Hal' }}
155
+ classNames={{ info: 'text-xs', button: 'rounded-full' }}
156
+ />
157
+ ```
158
+
159
+ Use the `render` prop to take over rendering entirely while keeping the navigation handlers wired up.
160
+
161
+ ### `<DataTableFilter />`
162
+
163
+ Per-column filter. Four built-in modes:
164
+
165
+ ```tsx
166
+ {/* Single select */}
167
+ <DataTableFilter
168
+ table={table}
169
+ columnId="status"
170
+ options={[
171
+ { label: 'Active', value: 'active' },
172
+ { label: 'Inactive', value: 'inactive' },
173
+ ]}
174
+ placeholder="All statuses"
175
+ />
176
+
177
+ {/* Multi-select (sends ?filter[status][]=… arrays) */}
178
+ <DataTableFilter
179
+ table={table}
180
+ columnId="role"
181
+ type="multiselect"
182
+ options={[
183
+ { label: 'Admin', value: 'admin' },
184
+ { label: 'Editor', value: 'editor' },
185
+ ]}
186
+ />
187
+
188
+ {/* Text input */}
189
+ <DataTableFilter
190
+ table={table}
191
+ columnId="name"
192
+ type="input"
193
+ placeholder="Filter name"
194
+ />
195
+
196
+ {/* Fully custom — popover, combobox, date picker, anything */}
197
+ <DataTableFilter
198
+ table={table}
199
+ columnId="status"
200
+ render={({ value, setValue }) => (
201
+ <MyFancyFilter value={value} onChange={setValue} />
202
+ )}
203
+ />
204
+ ```
205
+
206
+ ### `<DataTableSplitLayout />`
207
+
208
+ Predefined "split toolbar" layout that composes `DataTableSearch`, `DataTableFilter`s, `DataTable`, and `DataTablePagination` for you — search left, filters/actions right, table in the middle, pagination info left + controls right.
209
+
210
+ ```tsx
211
+ import {
212
+ Button,
213
+ DataTableFilter,
214
+ DataTableSplitLayout,
215
+ useDataTable,
216
+ } from '@manusiakemos/laravel-tanstack-react'
217
+
218
+ const { table, loading, meta } = useDataTable<User>({ /* ... */ })
219
+
220
+ return (
221
+ <DataTableSplitLayout
222
+ table={table}
223
+ meta={meta}
224
+ loading={loading}
225
+ searchProps={{ placeholder: 'Search users...' }}
226
+ filters={
227
+ <>
228
+ <DataTableFilter table={table} columnId="status" options={statusOpts} />
229
+ <DataTableFilter table={table} columnId="role" type="multiselect" options={roleOpts} />
230
+ </>
231
+ }
232
+ actions={<Button>+ Add user</Button>}
233
+ paginationProps={{ showPageSize: true, showFirstLast: true }}
234
+ tableProps={{ emptyMessage: 'No users found.' }}
235
+ />
236
+ )
237
+ ```
238
+
239
+ | Prop | Type | Description |
240
+ |---|---|---|
241
+ | `table` | `Table<TData>` | Required. From `useDataTable`. |
242
+ | `meta` | `DataTableMeta \| null` | For the row-count summary. |
243
+ | `loading` | `boolean` | Forwarded to `<DataTable />`. |
244
+ | `search` | `ReactNode` | Override the entire search slot. |
245
+ | `searchProps` | `Partial<DataTableSearchProps>` | Forwarded to the auto-rendered search. |
246
+ | `filters` | `ReactNode` | Right-toolbar slot (typically one or more `<DataTableFilter />`s). |
247
+ | `actions` | `ReactNode` | Far-right slot for page-level actions (Add button, export, …). |
248
+ | `tableProps` | `Partial<DataTableProps>` | Forwarded to `<DataTable />`. |
249
+ | `paginationProps` | `Partial<DataTablePaginationProps>` | Forwarded to `<DataTablePagination />`. |
250
+ | `className` | `string` | Root wrapper class. |
251
+ | `classNames` | `{ root, toolbar, toolbarLeft, toolbarRight, body, footer }` | Fine-grained class names. |
252
+
253
+ ### Shadcn primitives
254
+
255
+ The bundled primitives are re-exported so you can compose your own UI without pulling in a separate copy:
256
+
257
+ ```ts
258
+ import {
259
+ Button, Input, Select,
260
+ Table, TableHeader, TableBody, TableRow, TableHead, TableCell,
261
+ cn,
262
+ } from '@manusiakemos/laravel-tanstack-react'
263
+ ```
79
264
 
80
265
  ## API
81
266
 
@@ -178,7 +363,7 @@ export default function Index() {
178
363
  const { table } = useDataTable<User>({
179
364
  endpoint: '/datatable/users',
180
365
  columns: [
181
- { accessorKey: 'name', header: 'Nama' },
366
+ { accessorKey: 'name', header: 'Name' },
182
367
  {
183
368
  accessorKey: 'status',
184
369
  header: 'Status',
@@ -242,6 +427,72 @@ See the [backend package docs](https://github.com/manusiakemos/laravel-tanstack)
242
427
  - [ ] Optimistic row updates
243
428
  - [ ] Built-in CSV export trigger
244
429
 
430
+ ## Contributing
431
+
432
+ Contributions are welcome — bug fixes, new features, docs, and tests.
433
+
434
+ ### Language policy
435
+
436
+ **All contributions must be in English.** This applies to:
437
+
438
+ - Source code: identifiers (variables, functions, types, files), string literals, JSX text, UI copy, placeholders, error messages, and log output.
439
+ - Comments and JSDoc.
440
+ - Documentation: README, examples, code samples, and any other Markdown.
441
+ - Commit messages, PR titles, and PR descriptions.
442
+ - Issue reports and discussion replies.
443
+
444
+ This keeps the package usable by the broadest possible audience and consistent for future maintainers. If you need locale-specific copy in your own app, override it on the consumer side (e.g. translate column headers in your call to `useDataTable`); do not localize the package itself.
445
+
446
+ ### Getting set up
447
+
448
+ ```bash
449
+ git clone https://github.com/manusiakemos/laravel-tanstack-react.git
450
+ cd laravel-tanstack-react
451
+ npm install
452
+ ```
453
+
454
+ Node 18+ is required.
455
+
456
+ ### Workflow
457
+
458
+ 1. Fork the repo and create a topic branch off `main`:
459
+ ```bash
460
+ git checkout -b fix/short-description
461
+ ```
462
+ 2. Make your change. Keep the public API surface small and the diff focused.
463
+ 3. Add or update tests for any behavior change. The suite lives under `tests/` and runs against `happy-dom`.
464
+ 4. Run the full check locally before opening a PR:
465
+ ```bash
466
+ npm run lint
467
+ npm run format
468
+ npm run typecheck
469
+ npm run test
470
+ npm run build
471
+ ```
472
+ 5. Update `CHANGELOG.md` under `## [Unreleased]` describing the change (Added / Changed / Fixed / Removed).
473
+ 6. Open a pull request against `main`. Include:
474
+ - what changed and why
475
+ - any breaking-change notes (call them out explicitly)
476
+ - a code sample if you added a new option or hook
477
+
478
+ ### Commit style
479
+
480
+ Short, imperative, present tense. Prefix with a conventional scope when useful:
481
+
482
+ ```
483
+ feat: add `extraHeaders` option to default fetcher
484
+ fix: cancel in-flight request when endpoint changes
485
+ docs: clarify Sanctum CSRF requirement
486
+ ```
487
+
488
+ ### Reporting bugs
489
+
490
+ Open an issue at [github.com/manusiakemos/laravel-tanstack-react/issues](https://github.com/manusiakemos/laravel-tanstack-react/issues) with:
491
+
492
+ - a minimal reproduction (CodeSandbox or repo)
493
+ - the version of this package, `@tanstack/react-table`, and React you're on
494
+ - the actual vs. expected behavior
495
+
245
496
  ## License
246
497
 
247
498
  MIT.