react-column-drag-resize-table 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 react-column-drag-resize-table contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,274 @@
1
+ # react-column-drag-resize-table
2
+
3
+ A highly customizable React data table component for admin dashboards and internal tools. Features include draggable column reordering, resizable columns, built-in text filtering, pagination, optional persisted layouts, and easy theming via CSS variables. Optimized for usability and simple integration with a modern, Material-inspired design.
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
6
+ [![React](https://img.shields.io/badge/React-%3E%3D16.8-61dafb?logo=react&logoColor=white)](https://react.dev/)
7
+
8
+ ![react-column-drag-resize-table demo](https://raw.githubusercontent.com/ArmineInants/react-data-table/master/docs/Recording_table.gif)
9
+
10
+ **Live demo:** [react-data-table-topaz.vercel.app](https://react-data-table-topaz.vercel.app/)
11
+
12
+ ---
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install react-column-drag-resize-table
18
+ ```
19
+
20
+ Peers: **React** and **React DOM** ≥ **16.8**.
21
+
22
+ > If this name is unavailable on npm, publish as a [scoped package](https://docs.npmjs.com/about/scopes) (for example `@your-org/react-column-drag-resize-table`).
23
+
24
+ ---
25
+
26
+ ## How to use
27
+
28
+ Import **`DataTable`** and the stylesheet once (for layout and theme tokens):
29
+
30
+ ```js
31
+ import { DataTable } from 'react-column-drag-resize-table';
32
+ import 'react-column-drag-resize-table/styles.css';
33
+ ```
34
+
35
+ Pass **`columns`** (definitions) and **`rows`** (your data). Turn on **`enableFiltering`** / **`enablePagination`** when you need those toolbars. For controlled filter or page state, pair the value props with their **`on*Change`** handlers.
36
+
37
+ ---
38
+
39
+ ## Short example
40
+
41
+ ```jsx
42
+ import { DataTable } from 'react-column-drag-resize-table';
43
+ import 'react-column-drag-resize-table/styles.css';
44
+
45
+ const columns = [
46
+ { id: 'id', title: 'ID', accessor: 'id' },
47
+ { id: 'name', title: 'Name', accessor: 'name' }
48
+ ];
49
+
50
+ const rows = [
51
+ { id: 1, name: 'Ada Lovelace' },
52
+ { id: 2, name: 'Alan Turing' }
53
+ ];
54
+
55
+ export function Example() {
56
+ return <DataTable columns={columns} rows={rows} />;
57
+ }
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Advanced example
63
+
64
+ Filtering, pagination, and a **`renderSummary`** line using the built-in filter bar:
65
+
66
+ ```jsx
67
+ import { useMemo } from 'react';
68
+ import { DataTable } from 'react-column-drag-resize-table';
69
+ import 'react-column-drag-resize-table/styles.css';
70
+
71
+ const rows = [
72
+ { id: 1, user_name: 'alice', amount: 100, status: 'ok' },
73
+ { id: 2, user_name: 'bob', amount: 200, status: 'pending' }
74
+ ];
75
+
76
+ export function AdvancedExample() {
77
+ const columns = useMemo(
78
+ () => [
79
+ { id: 'id', title: 'ID', accessor: 'id' },
80
+ { id: 'user_name', title: 'User', accessor: 'user_name' },
81
+ { id: 'amount', title: 'Amount', accessor: 'amount' },
82
+ { id: 'status', title: 'Status', accessor: 'status' }
83
+ ],
84
+ []
85
+ );
86
+
87
+ return (
88
+ <DataTable
89
+ columns={columns}
90
+ rows={rows}
91
+ enableFiltering
92
+ filterFields={[
93
+ { field: 'user_name', label: 'Username', placeholder: 'e.g. alice' },
94
+ { field: 'status', label: 'Status', placeholder: 'e.g. ok' },
95
+ ]}
96
+ enablePagination
97
+ defaultPageSize={10}
98
+ pageSizeOptions={[5, 10, 20]}
99
+ renderSummary={({ filteredCount, totalRows, currentPage, pageSize, totalPages }) => (
100
+ <div className="summary-line grey lighten-3">
101
+ <span>
102
+ Rows: {filteredCount} of {totalRows} · Page {currentPage}/{totalPages} · {pageSize} per page
103
+ </span>
104
+ </div>
105
+ )}
106
+ />
107
+ );
108
+ }
109
+ ```
110
+
111
+ ---
112
+
113
+ ## `DataTable` props
114
+
115
+ | Property | Required | Type | Default | Possible values / notes | Description |
116
+ |----------|----------|------|---------|-------------------------|-------------|
117
+ | `columns` | **yes** | `Column<T>[]` | — | See [Column](#column) | Column definitions (`id`, `title`, cell content). |
118
+ | `rows` | **yes** | `T[]` | — | — | Row data; each row is a record (often with `id`). |
119
+ | `getRowId` | no | `(row: T, index: number) => string \| number` | `row.id` if present, else `index` | — | Stable id for React keys and layout storage keys. |
120
+ | `loading` | no | `boolean` | `false` | `true` / `false` | Shows a loading state instead of the table body. |
121
+ | `emptyMessage` | no | `string` | `'No rows to display.'` | — | Shown when there are no rows (after filter). |
122
+ | `summary` | no | `ReactNode` | — | — | Static summary node above the table (if you do not use `renderSummary`). |
123
+ | `renderSummary` | no | `(ctx: DataTableSummaryContext) => ReactNode` | — | See [DataTableSummaryContext](#datatablesummarycontext) | Summary slot with counts and pagination info. |
124
+ | `className` | no | `string` | `''` | — | Class on the outer wrapper. |
125
+ | `tableClassName` | no | `string` | `'highlight'` | — | Class on the `<table>` (e.g. zebra striping). |
126
+ | `columnOrder` | no | `string[]` | internal order | Array of column `id`s | **Controlled:** current column order. Omit for internal state. |
127
+ | `onColumnOrderChange` | no | `(order: string[]) => void` | — | — | Fires when the user reorders columns. If omitted while `columnOrder` is also omitted, reorder changes are persisted to `localStorage` (see `layoutStorageKey`). |
128
+ | `enableColumnReorder` | no | `boolean` | `true` | `true` / `false` | Allow drag-and-drop column reorder. |
129
+ | `columnWidths` | no | `Record<string, number>` | internal widths | Pixel numbers per column `id` | **Controlled:** column widths. |
130
+ | `onColumnWidthsChange` | no | `(widths: Record<string, number>) => void` | — | — | Fires when the user resizes a column. If omitted while `columnWidths` is undefined, width changes are persisted to `localStorage` (see `layoutStorageKey`). |
131
+ | `enableColumnResize` | no | `boolean` | `true` | `true` / `false` | Allow drag resize on column edges. |
132
+ | `minColumnWidth` | no | `number` | `64` | ≥ `0` | Minimum width when resizing (px). |
133
+ | `layoutStorageKey` | no | `string` | — | — | Optional id segment for `localStorage` keys. When omitted, a key is derived from the column `id`s. Order and widths persist under `react-column-drag-resize-table:v1:*` when the matching prop is uncontrolled and its `on*Change` handler is omitted. |
134
+ | `enableFiltering` | no | `boolean` | `false` | `true` / `false` | Show the filter toolbar and apply [filter rules](#filter-value-rules) to `rows`. |
135
+ | `filterFields` | no | `FilterField[]` | `[]` | See [FilterField](#filterfield) | Fields listed here get text inputs; empty string means “no filter” for that key. |
136
+ | `filters` | no | `Record<string, unknown>` | — | — | **Controlled:** current filter object. Pair with `onFiltersChange`. |
137
+ | `onFiltersChange` | no | `(filters: Record<string, unknown>) => void` | — | — | Called when filters change (built-in UI or your controlled updates). |
138
+ | `defaultFilters` | no | `Record<string, unknown>` | — | — | Initial filters when uncontrolled (`filters` omitted). |
139
+ | `enablePagination` | no | `boolean` | `false` | `true` / `false` | Paginate **after** filtering. |
140
+ | `defaultPageSize` | no | `number` | `10` | Positive integer | Initial page size when `pageSize` is uncontrolled. |
141
+ | `pageSizeOptions` | no | `number[]` | `[5, 10, 20, 50]` | Positive integers | Options in the page-size `<select>`. |
142
+ | `currentPage` | no | `number` | internal | ≥ `1` | **Controlled:** current page (1-based). |
143
+ | `onPageChange` | no | `(page: number) => void` | — | — | Fires when the user changes page. |
144
+ | `pageSize` | no | `number` | internal | Positive integer | **Controlled:** rows per page. |
145
+ | `onPageSizeChange` | no | `(size: number) => void` | — | — | Fires when the user changes page size. |
146
+ | `filterBarClassName` | no | `string` | `''` | — | Extra class on the filter toolbar row. |
147
+ | `paginationClassName` | no | `string` | `''` | — | Extra class on the pagination row. |
148
+
149
+ ### `Column`
150
+
151
+ Used for each entry in **`columns`**.
152
+
153
+ | Property | Required | Type | Default | Possible values / notes | Description |
154
+ |----------|----------|------|---------|-------------------------|-------------|
155
+ | `id` | **yes** | `string` | — | Unique among columns | Stable id (order, resize, filters, storage). |
156
+ | `title` | **yes** | `string` | — | — | Header label. |
157
+ | `accessor` | no | `keyof T \| ((row: T) => ReactNode)` | — | — | Key on the row, or function returning cell content. If omitted, falls back to `row[id]`. |
158
+ | `render` | no | `(row: T) => ReactNode` | — | — | If set, used for the cell instead of `accessor`. |
159
+
160
+ ### `FilterField`
161
+
162
+ Used for each entry in **`filterFields`** when the built-in filter bar is enabled.
163
+
164
+ | Property | Required | Type | Default | Possible values / notes | Description |
165
+ |----------|----------|------|---------|-------------------------|-------------|
166
+ | `field` | **yes** | `string` | — | Must match a key you filter on | Filter object key (e.g. `user_name`). |
167
+ | `label` | **yes** | `string` | — | — | Label for the input. |
168
+ | `placeholder` | no | `string` | — | — | Input placeholder. |
169
+
170
+ ### `DataTableSummaryContext`
171
+
172
+ Argument to **`renderSummary`** when that prop is provided.
173
+
174
+ | Property | Type | Description |
175
+ |----------|------|-------------|
176
+ | `totalRows` | `number` | Number of rows in **`rows`** before filtering. |
177
+ | `filteredCount` | `number` | Number of rows after filtering. |
178
+ | `currentPage` | `number` | Current page (1-based). |
179
+ | `pageSize` | `number` | Current page size. |
180
+ | `totalPages` | `number` | Total page count after filtering. |
181
+
182
+ ### Filter value rules
183
+
184
+ When **`enableFiltering`** is on, each key in the active filter object is applied to **`rows`** as follows:
185
+
186
+ | Key pattern | Effect |
187
+ |-------------|--------|
188
+ | `name_from` / `name_to` | Numeric range on field **`name`** |
189
+ | `name_datefrom` / `name_dateto` | Date range on field **`name`** |
190
+ | Other keys | `row[key] == filter[key]` (equality); empty values are ignored |
191
+
192
+ You can drive the same keys from **`filterFields`** (one input per `field`) or from **`filters`** / **`onFiltersChange`** with your own UI.
193
+
194
+ ---
195
+
196
+ ## Styling
197
+
198
+ ```css
199
+
200
+ Below are the CSS variables used for DataTable styling.
201
+ To customize the appearance, place overrides in the `:root` of your stylesheet:
202
+
203
+ ```css
204
+ :root {
205
+ /* Borders */
206
+ --rdt-border: #c5cae9;
207
+ --rdt-border-divider: #e0e0e0;
208
+ --rdt-border-cell: #f0f0f0;
209
+
210
+ /* Accent & interactive */
211
+ --rdt-accent: #3949ab;
212
+ --rdt-accent-soft: #e8eaf6;
213
+ --rdt-accent-muted: #5c6bc0;
214
+ /* Primary blue used for pagination, spinner, links */
215
+ --rdt-accent-blue: #1976d2;
216
+ --rdt-accent-blue-soft: #e3f2fd;
217
+ --rdt-pagination-active-bg: var(--rdt-accent-blue);
218
+ --rdt-pagination-hover-bg: var(--rdt-accent-blue-soft);
219
+ --rdt-spinner-track: var(--rdt-accent-blue-soft);
220
+ --rdt-spinner-thumb: var(--rdt-accent-blue);
221
+ --rdt-resize-hover: rgba(57, 73, 171, 0.35);
222
+ --rdt-resize-handle-mid: rgba(25, 118, 210, 0.12);
223
+ --rdt-resize-handle-end: rgba(25, 118, 210, 0.22);
224
+ --rdt-resize-handle-inset: rgba(25, 118, 210, 0.12);
225
+ --rdt-focus-ring: rgba(57, 73, 171, 0.2);
226
+ --rdt-focus-outline-contrast: #ffffff;
227
+
228
+ /* Surfaces */
229
+ --rdt-surface: #ffffff;
230
+ --rdt-surface-elevated: #f5f5f5;
231
+ --rdt-surface-muted: #eeeeee;
232
+ --rdt-surface-toolbar-start: #ffffff;
233
+ --rdt-surface-toolbar-end: #f5f5f5;
234
+ --rdt-surface-hints-start: #fafbff;
235
+ --rdt-surface-hints-end: #eef0fb;
236
+ --rdt-surface-header-start: #f5f5f5;
237
+ --rdt-surface-header-end: #eeeeee;
238
+ --rdt-surface-summary: #eeeeee;
239
+ --rdt-surface-row-hover: #f5f5f5;
240
+
241
+ /* Text */
242
+ --rdt-text: #424242;
243
+ --rdt-text-strong: #263238;
244
+ --rdt-text-muted: #757575;
245
+ --rdt-text-label: #546e7a;
246
+ --rdt-text-hint-drag: #283593;
247
+ --rdt-text-hint-resize: #1565c0;
248
+
249
+ /* Form controls */
250
+ --rdt-input-border: #b0bec5;
251
+ --rdt-input-border-hover: #78909c;
252
+
253
+ /* Pagination */
254
+ --rdt-pagination-active-fg: #ffffff;
255
+ --rdt-pagination-disabled-opacity: 0.45;
256
+
257
+ /* Misc */
258
+ --rdt-hint-icon-opacity: 0.85;
259
+ --rdt-drag-grip-opacity: 0.85;
260
+ }
261
+ ```
262
+
263
+ ## Development
264
+
265
+ ```bash
266
+ npm install && npm run dev # demo: example/
267
+ npm run build # dist/ + types
268
+ ```
269
+
270
+ ---
271
+
272
+ ## License
273
+
274
+ MIT — [LICENSE](./LICENSE)
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),d=require("react");function pt(s,a){if(!a||typeof a!="object")return s;let n=s;for(const c of Object.keys(a)){const x=/(.*)_from$/.exec(c),f=/(.*)_to$/.exec(c),j=/(.*)_datefrom$/.exec(c),g=/(.*)_dateto$/.exec(c);if(x){const l=x[1];if(a[c]===""||a[c]==null)continue;n=n.filter(p=>{const o=p[l];return parseFloat(o)>=parseFloat(a[c])})}else if(f){const l=f[1];if(a[c]===""||a[c]==null)continue;n=n.filter(p=>{const o=p[l];return parseFloat(o)<=parseFloat(a[c])})}else if(j){const l=j[1];if(!a[c])continue;n=n.filter(p=>{const o=p[l];return o==null?!1:String(o).slice(0,10)>=a[c]})}else if(g){const l=g[1];if(!a[c])continue;n=n.filter(p=>{const o=p[l];if(o==null)return!1;const m=String(o).slice(0,10);return a[c]>=m})}else{if(a[c]===""||a[c]==null)continue;n=n.filter(l=>l[c]==a[c])}}return n}function Bt({currentPage:s,totalPages:a,onPageChange:n,className:c="",getPageHref:x}){if(a<=1)return null;const f=[];for(let l=1;l<=a;l+=1)f.push(l);const j=l=>{l<1||l>a||l===s||n(l)},g=({page:l,children:p})=>{const o=l===s;return x?e.jsx("li",{className:o?"active":"waves-effect",children:e.jsx("a",{href:x(l),onClick:m=>{m.preventDefault(),j(l)},children:p})},l):e.jsx("li",{className:o?"active":"waves-effect",onClick:()=>j(l),onKeyDown:m=>{(m.key==="Enter"||m.key===" ")&&(m.preventDefault(),j(l))},role:"button",tabIndex:0,children:e.jsx("a",{href:"#",children:p})},l)};return e.jsx("div",{className:`pagination react-data-table-pagination ${c}`.trim(),children:e.jsxs("ul",{className:"pagination right",children:[s===1?e.jsx("li",{className:"disabled",children:e.jsx("span",{className:"react-data-table-pagination__nav","aria-hidden":!0,children:"‹"})}):e.jsx("li",{className:"waves-effect",children:e.jsx("a",{href:"#",onClick:l=>{l.preventDefault(),j(s-1)},"aria-label":"Previous page",children:"‹"})}),f.map(l=>e.jsx(g,{page:l,children:l},l)),s===a?e.jsx("li",{className:"disabled",children:e.jsx("span",{className:"react-data-table-pagination__nav","aria-hidden":!0,children:"›"})}):e.jsx("li",{className:"waves-effect",children:e.jsx("a",{href:"#",onClick:l=>{l.preventDefault(),j(s+1)},"aria-label":"Next page",children:"›"})})]})})}const Rt="react-column-drag-resize-table:v1:order:",Jt="react-column-drag-resize-table:v1:widths:";function K(s,a){const n=Array.isArray(s)?s:[],c=new Set(a),x=[];for(const f of n)c.has(f)&&x.push(f);for(const f of a)x.includes(f)||x.push(f);return x}function qt(s){if(!s||typeof localStorage>"u")return null;try{const a=localStorage.getItem(s);if(!a)return null;const n=JSON.parse(a);return Array.isArray(n)?n:null}catch{return null}}function Ht(s,a){if(!(!s||typeof localStorage>"u"))try{localStorage.setItem(s,JSON.stringify(a))}catch{}}function Gt(s){if(!s||typeof localStorage>"u")return null;try{const a=localStorage.getItem(s);if(!a)return null;const n=JSON.parse(a);return n&&typeof n=="object"&&!Array.isArray(n)?n:null}catch{return null}}function Ut(s,a){if(!(!s||typeof localStorage>"u"))try{localStorage.setItem(s,JSON.stringify(a))}catch{}}function Qt(s,a){return s&&s.id!=null?String(s.id):String(a)}function Yt(s,a){return s.render?s.render(a):typeof s.accessor=="function"?s.accessor(a):s.accessor!=null?a[s.accessor]:a[s.id]}function Zt(s,a,n,c){const x=a!=null,f=d.useMemo(()=>(s||[]).map(_=>_.id),[s]),[j,g]=d.useState(null),l=d.useRef(c);d.useEffect(()=>{if(x)return;const _=l.current!==c;l.current=c,g(h=>{const b=c?qt(c):null;return K(h===null||_?b??f:h,f)})},[f,x,c]);const p=d.useMemo(()=>{const _=x?a:j||f,h=new Map((s||[]).map(v=>[v.id,v])),b=new Set,L=[];for(const v of _)h.has(v)&&!b.has(v)&&(L.push(v),b.add(v));for(const v of f)b.has(v)||L.push(v);return L},[s,a,j,f,x]),o=d.useMemo(()=>{const _=new Map((s||[]).map(h=>[h.id,h]));return p.map(h=>_.get(h)).filter(Boolean)},[s,p]),m=d.useCallback(_=>{const h=K(_,f);x||(g(h),c&&Ht(c,h)),n==null||n(h)},[x,n,c,f]);return{columns:o,orderedIds:p,setOrder:m}}function Vt({columns:s,rows:a,getRowId:n=Qt,loading:c=!1,emptyMessage:x="No rows to display.",summary:f,className:j="",tableClassName:g="highlight",columnOrder:l,onColumnOrderChange:p,enableColumnReorder:o=!0,columnWidths:m,onColumnWidthsChange:_,enableColumnResize:h=!0,minColumnWidth:b=64,layoutStorageKey:L,enableFiltering:v=!1,filterFields:W=[],filters:P,onFiltersChange:B,defaultFilters:mt,enablePagination:N=!1,defaultPageSize:bt=10,pageSizeOptions:R=[5,10,20,50],currentPage:C,onPageChange:S,pageSize:tt,onPageSizeChange:J,filterBarClassName:et="",paginationClassName:jt="",renderSummary:at}){const z=d.useId(),G=P!==void 0,[vt,Nt]=d.useState(()=>({...mt??{}})),w=C!==void 0,U=tt!==void 0,[yt,q]=d.useState(1),[St,gt]=d.useState(bt),I=G?P:vt,M=U?tt:St,st=d.useMemo(()=>(s||[]).map(t=>t.id),[s]),rt=d.useMemo(()=>L??[...st].sort().join("|"),[L,st]),wt=p==null&&l==null?`${Rt}${rt}`:null,k=_==null&&m===void 0?`${Jt}${rt}`:null,E=m!==void 0,[Dt,lt]=d.useState({}),F=E?m:Dt;d.useEffect(()=>{if(E||!k)return;const t=Gt(k);t&&lt(t)},[E,k]);const Q=d.useCallback((t,r)=>{E?_==null||_({...m,[t]:r}):lt(u=>{const i={...u,[t]:r};return _==null||_(i),k&&Ut(k,i),i})},[E,m,_,k]),Y=d.useRef(null),{columns:Z,orderedIds:ct,setOrder:it}=Zt(s,l,p,wt),T=d.useMemo(()=>v?pt(a,I):a,[a,v,I]),Mt=a.length,It=T.length,D=d.useMemo(()=>N?Math.max(1,Math.ceil(T.length/M)):1,[N,T.length,M]),nt=w?C:yt,H=d.useMemo(()=>N?Math.min(Math.max(1,nt),D):1,[N,nt,D]);d.useEffect(()=>{!N||w||q(t=>t>D?D:t)},[D,N,w]);const dt=d.useMemo(()=>{if(!N)return T;const t=(H-1)*M;return T.slice(t,t+M)},[N,T,H,M]),$t=d.useCallback(t=>{const r=Math.min(Math.max(1,t),D);w?S==null||S(r):q(r)},[D,w,S]),Lt=d.useCallback((t,r)=>{const u=typeof r=="string"?r.trim():r,i={...I};u===""||u==null?delete i[t]:i[t]=u,G?B==null||B(i):Nt(i),N&&(w?S==null||S(1):q(1))},[I,G,B,N,w,S]),ot=d.useCallback(t=>{const r=Number(t.target.value);Number.isNaN(r)||r<1||(U?J==null||J(r):gt(r),w?S==null||S(1):q(1))},[U,J,w,S]),kt=d.useCallback(t=>{var u;const r=(u=t.currentTarget)==null?void 0:u.id;r&&o&&(t.dataTransfer.setData("text",r),t.dataTransfer.effectAllowed="move")},[o]),Tt=d.useCallback(t=>{var xt,_t;if(!o)return;const r=t.dataTransfer.getData("text"),u=(_t=(xt=t.target).closest)==null?void 0:_t.call(xt,"th"),i=u==null?void 0:u.id;if(!i||!r||i===r)return;const y=[...ct],A=y.indexOf(r),O=y.indexOf(i);if(A<0||O<0)return;const $=u.getBoundingClientRect(),V=t.clientX>=$.x+parseFloat($.width)/2,X=y.filter(Xt=>Xt!==r);let ht=X.indexOf(i);V&&(ht+=1),X.splice(ht,0,r),it(X),t.dataTransfer.clearData()},[ct,o,it]),At=d.useCallback((t,r)=>{if(!h)return;r.preventDefault(),r.stopPropagation();const u=r.currentTarget.closest("th"),i=F[t]??(u?u.getBoundingClientRect().width:b);Y.current={colId:t,startX:r.clientX,startW:i},document.body.classList.add("react-data-table--resizing");const y=O=>{const $=Y.current;if(!$)return;const V=O.clientX-$.startX,X=Math.max(b,$.startW+V);Q($.colId,X)},A=()=>{Y.current=null,document.body.classList.remove("react-data-table--resizing"),document.removeEventListener("mousemove",y),document.removeEventListener("mouseup",A)};document.addEventListener("mousemove",y),document.addEventListener("mouseup",A)},[h,F,b,Q]);if(c)return e.jsx("div",{className:`table react-data-table react-data-table--loading ${j}`.trim(),children:e.jsx("div",{className:"react-data-table__spinner","aria-busy":"true","aria-label":"Loading"})});if(!s||s.length===0)return null;const zt=o||h,ut=v&&W.length>0,ft=N&&Array.isArray(R)&&R.length>0,Et=at?at({totalRows:Mt,filteredCount:It,currentPage:H,pageSize:M,totalPages:D}):f,Ft=Z.map(t=>{const r=F[t.id],u=r!=null?{width:r,minWidth:b,maxWidth:r}:{minWidth:b};return e.jsxs("th",{id:t.id,className:["react-data-table__th",o&&"react-data-table__th--draggable"].filter(Boolean).join(" "),style:u,draggable:o?"true":"false",onDrop:Tt,onDragStart:kt,onDragOver:i=>i.preventDefault(),title:o&&h?"Drag to reorder · Drag the right edge to resize":o?"Drag to reorder columns":h?"Drag the right edge to resize":void 0,children:[e.jsxs("div",{className:"react-data-table__th-main",children:[o?e.jsx("span",{className:"react-data-table__drag-grip","aria-hidden":"true",title:"Drag to reorder"}):null,e.jsx("span",{className:"react-data-table__th-title",children:t.title})]}),h?e.jsx("span",{className:"react-data-table__resize-handle",role:"separator","aria-orientation":"vertical",tabIndex:0,"aria-label":"Resize column",title:"Drag to resize",onMouseDown:i=>At(t.id,i),onKeyDown:i=>{var y;if(i.key==="ArrowLeft"||i.key==="ArrowRight"){i.preventDefault();const A=i.key==="ArrowLeft"?-8:8,O=F[t.id]??((y=i.currentTarget.closest("th"))==null?void 0:y.getBoundingClientRect().width)??b;Q(t.id,Math.max(b,O+A))}}}):null]},t.id)}),Ot=dt.length>0?dt.map((t,r)=>e.jsx("tr",{children:Z.map(u=>{const i=F[u.id],y=i!=null?{width:i,minWidth:b,maxWidth:i}:{minWidth:b};return e.jsx("td",{style:y,children:Yt(u,t)},u.id)})},n(t,r))):[e.jsx("tr",{children:e.jsx("td",{colSpan:Z.length,className:"react-data-table__empty",children:x})},"__empty")];return e.jsxs("div",{className:`table react-data-table ${j}`.trim(),children:[ut?e.jsxs("div",{className:`react-data-table__filter-bar ${et}`.trim(),role:"search",children:[W.map(t=>{const r=t.field,u=`${z}-filter-${r}`,i=I[r]!=null&&I[r]!==""?String(I[r]):"";return e.jsxs("div",{className:"react-data-table__filter-field",children:[e.jsx("label",{className:"react-data-table__filter-label",htmlFor:u,children:t.label}),e.jsx("input",{id:u,className:"react-data-table__filter-input",type:"text",placeholder:t.placeholder??"",value:i,onChange:y=>Lt(r,y.target.value),autoComplete:"off"})]},r)}),ft?e.jsxs("div",{className:"react-data-table__filter-field react-data-table__filter-field--page-size",children:[e.jsx("label",{className:"react-data-table__filter-label",htmlFor:`${z}-page-size`,children:"Rows per page"}),e.jsx("select",{id:`${z}-page-size`,className:"react-data-table__filter-select",value:M,onChange:ot,children:R.map(t=>e.jsx("option",{value:t,children:t},t))})]}):null]}):null,!ut&&N&&ft?e.jsx("div",{className:`react-data-table__toolbar react-data-table__toolbar--pagination-only ${et}`.trim(),children:e.jsxs("div",{className:"react-data-table__filter-field react-data-table__filter-field--page-size",children:[e.jsx("label",{className:"react-data-table__filter-label",htmlFor:`${z}-page-size-solo`,children:"Rows per page"}),e.jsx("select",{id:`${z}-page-size-solo`,className:"react-data-table__filter-select",value:M,onChange:ot,children:R.map(t=>e.jsx("option",{value:t,children:t},t))})]})}):null,zt?e.jsxs("div",{className:"react-data-table__hints",role:"note",children:[o?e.jsxs("span",{className:"react-data-table__hint react-data-table__hint--drag",children:[e.jsx("span",{className:"react-data-table__hint-icon react-data-table__hint-icon--grip","aria-hidden":"true"}),"Drag column headers to reorder"]}):null,h?e.jsxs("span",{className:"react-data-table__hint react-data-table__hint--resize",children:[e.jsx("span",{className:"react-data-table__hint-icon react-data-table__hint-icon--arrows","aria-hidden":"true"}),"Drag the right edge of a header to resize"]}):null]}):null,e.jsx("div",{className:"react-data-table__scroll",children:e.jsxs("table",{className:g,style:{tableLayout:"fixed"},children:[e.jsx("thead",{className:"grey lighten-3",children:e.jsx("tr",{children:Ft})}),e.jsx("tbody",{children:Ot})]})}),Et,N?e.jsx(Bt,{currentPage:H,totalPages:D,onPageChange:$t,className:jt}):null]})}exports.DataTable=Vt;exports.filterRows=pt;
@@ -0,0 +1,66 @@
1
+ import type { ReactElement, ReactNode } from 'react';
2
+
3
+ export interface Column<T = Record<string, unknown>> {
4
+ id: string;
5
+ title: string;
6
+ accessor?: keyof T | ((row: T) => ReactNode);
7
+ render?: (row: T) => ReactNode;
8
+ }
9
+
10
+ export interface FilterField {
11
+ field: string;
12
+ label: string;
13
+ placeholder?: string;
14
+ }
15
+
16
+ export interface DataTableSummaryContext {
17
+ totalRows: number;
18
+ filteredCount: number;
19
+ currentPage: number;
20
+ pageSize: number;
21
+ totalPages: number;
22
+ }
23
+
24
+ export interface DataTableProps<T = Record<string, unknown>> {
25
+ columns: Column<T>[];
26
+ rows: T[];
27
+ getRowId?: (row: T, index: number) => string | number;
28
+ loading?: boolean;
29
+ emptyMessage?: string;
30
+ summary?: ReactNode;
31
+ renderSummary?: (ctx: DataTableSummaryContext) => ReactNode;
32
+ className?: string;
33
+ tableClassName?: string;
34
+ columnOrder?: string[];
35
+ onColumnOrderChange?: (order: string[]) => void;
36
+ enableColumnReorder?: boolean;
37
+ columnWidths?: Record<string, number>;
38
+ onColumnWidthsChange?: (widths: Record<string, number>) => void;
39
+ enableColumnResize?: boolean;
40
+ minColumnWidth?: number;
41
+ /** When set, used with `react-column-drag-resize-table:v1:*` keys in localStorage if column callbacks are omitted. */
42
+ layoutStorageKey?: string;
43
+ enableFiltering?: boolean;
44
+ filterFields?: FilterField[];
45
+ filters?: Record<string, unknown>;
46
+ onFiltersChange?: (filters: Record<string, unknown>) => void;
47
+ defaultFilters?: Record<string, unknown>;
48
+ enablePagination?: boolean;
49
+ defaultPageSize?: number;
50
+ pageSizeOptions?: number[];
51
+ currentPage?: number;
52
+ onPageChange?: (page: number) => void;
53
+ pageSize?: number;
54
+ onPageSizeChange?: (size: number) => void;
55
+ filterBarClassName?: string;
56
+ paginationClassName?: string;
57
+ }
58
+
59
+ export function DataTable<T = Record<string, unknown>>(
60
+ props: DataTableProps<T>
61
+ ): ReactElement | null;
62
+
63
+ export function filterRows<T extends Record<string, unknown>>(
64
+ rows: T[],
65
+ filters: Record<string, unknown>
66
+ ): T[];
package/dist/index.js ADDED
@@ -0,0 +1,476 @@
1
+ import { jsx as c, jsxs as w } from "react/jsx-runtime";
2
+ import { useId as qt, useState as R, useMemo as $, useEffect as tt, useCallback as L, useRef as yt } from "react";
3
+ function Gt(a, e) {
4
+ if (!e || typeof e != "object")
5
+ return a;
6
+ let n = a;
7
+ for (const l of Object.keys(e)) {
8
+ const h = /(.*)_from$/.exec(l), f = /(.*)_to$/.exec(l), b = /(.*)_datefrom$/.exec(l), x = /(.*)_dateto$/.exec(l);
9
+ if (h) {
10
+ const s = h[1];
11
+ if (e[l] === "" || e[l] == null) continue;
12
+ n = n.filter((p) => {
13
+ const d = p[s];
14
+ return parseFloat(d) >= parseFloat(e[l]);
15
+ });
16
+ } else if (f) {
17
+ const s = f[1];
18
+ if (e[l] === "" || e[l] == null) continue;
19
+ n = n.filter((p) => {
20
+ const d = p[s];
21
+ return parseFloat(d) <= parseFloat(e[l]);
22
+ });
23
+ } else if (b) {
24
+ const s = b[1];
25
+ if (!e[l]) continue;
26
+ n = n.filter((p) => {
27
+ const d = p[s];
28
+ return d == null ? !1 : String(d).slice(0, 10) >= e[l];
29
+ });
30
+ } else if (x) {
31
+ const s = x[1];
32
+ if (!e[l]) continue;
33
+ n = n.filter((p) => {
34
+ const d = p[s];
35
+ if (d == null) return !1;
36
+ const m = String(d).slice(0, 10);
37
+ return e[l] >= m;
38
+ });
39
+ } else {
40
+ if (e[l] === "" || e[l] == null) continue;
41
+ n = n.filter((s) => s[l] == e[l]);
42
+ }
43
+ }
44
+ return n;
45
+ }
46
+ function Ut({
47
+ currentPage: a,
48
+ totalPages: e,
49
+ onPageChange: n,
50
+ className: l = "",
51
+ getPageHref: h
52
+ }) {
53
+ if (e <= 1)
54
+ return null;
55
+ const f = [];
56
+ for (let s = 1; s <= e; s += 1)
57
+ f.push(s);
58
+ const b = (s) => {
59
+ s < 1 || s > e || s === a || n(s);
60
+ }, x = ({ page: s, children: p }) => {
61
+ const d = s === a;
62
+ return h ? /* @__PURE__ */ c("li", { className: d ? "active" : "waves-effect", children: /* @__PURE__ */ c("a", { href: h(s), onClick: (m) => {
63
+ m.preventDefault(), b(s);
64
+ }, children: p }) }, s) : /* @__PURE__ */ c(
65
+ "li",
66
+ {
67
+ className: d ? "active" : "waves-effect",
68
+ onClick: () => b(s),
69
+ onKeyDown: (m) => {
70
+ (m.key === "Enter" || m.key === " ") && (m.preventDefault(), b(s));
71
+ },
72
+ role: "button",
73
+ tabIndex: 0,
74
+ children: /* @__PURE__ */ c("a", { href: "#", children: p })
75
+ },
76
+ s
77
+ );
78
+ };
79
+ return /* @__PURE__ */ c("div", { className: `pagination react-data-table-pagination ${l}`.trim(), children: /* @__PURE__ */ w("ul", { className: "pagination right", children: [
80
+ a === 1 ? /* @__PURE__ */ c("li", { className: "disabled", children: /* @__PURE__ */ c("span", { className: "react-data-table-pagination__nav", "aria-hidden": !0, children: "‹" }) }) : /* @__PURE__ */ c("li", { className: "waves-effect", children: /* @__PURE__ */ c(
81
+ "a",
82
+ {
83
+ href: "#",
84
+ onClick: (s) => {
85
+ s.preventDefault(), b(a - 1);
86
+ },
87
+ "aria-label": "Previous page",
88
+ children: "‹"
89
+ }
90
+ ) }),
91
+ f.map((s) => /* @__PURE__ */ c(x, { page: s, children: s }, s)),
92
+ a === e ? /* @__PURE__ */ c("li", { className: "disabled", children: /* @__PURE__ */ c("span", { className: "react-data-table-pagination__nav", "aria-hidden": !0, children: "›" }) }) : /* @__PURE__ */ c("li", { className: "waves-effect", children: /* @__PURE__ */ c(
93
+ "a",
94
+ {
95
+ href: "#",
96
+ onClick: (s) => {
97
+ s.preventDefault(), b(a + 1);
98
+ },
99
+ "aria-label": "Next page",
100
+ children: "›"
101
+ }
102
+ ) })
103
+ ] }) });
104
+ }
105
+ const Qt = "react-column-drag-resize-table:v1:order:", Yt = "react-column-drag-resize-table:v1:widths:";
106
+ function C(a, e) {
107
+ const n = Array.isArray(a) ? a : [], l = new Set(e), h = [];
108
+ for (const f of n)
109
+ l.has(f) && h.push(f);
110
+ for (const f of e)
111
+ h.includes(f) || h.push(f);
112
+ return h;
113
+ }
114
+ function Zt(a) {
115
+ if (!a || typeof localStorage > "u") return null;
116
+ try {
117
+ const e = localStorage.getItem(a);
118
+ if (!e) return null;
119
+ const n = JSON.parse(e);
120
+ return Array.isArray(n) ? n : null;
121
+ } catch {
122
+ return null;
123
+ }
124
+ }
125
+ function Vt(a, e) {
126
+ if (!(!a || typeof localStorage > "u"))
127
+ try {
128
+ localStorage.setItem(a, JSON.stringify(e));
129
+ } catch {
130
+ }
131
+ }
132
+ function Kt(a) {
133
+ if (!a || typeof localStorage > "u") return null;
134
+ try {
135
+ const e = localStorage.getItem(a);
136
+ if (!e) return null;
137
+ const n = JSON.parse(e);
138
+ return n && typeof n == "object" && !Array.isArray(n) ? n : null;
139
+ } catch {
140
+ return null;
141
+ }
142
+ }
143
+ function Wt(a, e) {
144
+ if (!(!a || typeof localStorage > "u"))
145
+ try {
146
+ localStorage.setItem(a, JSON.stringify(e));
147
+ } catch {
148
+ }
149
+ }
150
+ function Pt(a, e) {
151
+ return a && a.id != null ? String(a.id) : String(e);
152
+ }
153
+ function Ct(a, e) {
154
+ return a.render ? a.render(e) : typeof a.accessor == "function" ? a.accessor(e) : a.accessor != null ? e[a.accessor] : e[a.id];
155
+ }
156
+ function te(a, e, n, l) {
157
+ const h = e != null, f = $(
158
+ () => (a || []).map((_) => _.id),
159
+ [a]
160
+ ), [b, x] = R(null), s = yt(l);
161
+ tt(() => {
162
+ if (h) return;
163
+ const _ = s.current !== l;
164
+ s.current = l, x((u) => {
165
+ const v = l ? Zt(l) : null;
166
+ return C(u === null || _ ? v ?? f : u, f);
167
+ });
168
+ }, [f, h, l]);
169
+ const p = $(() => {
170
+ const _ = h ? e : b || f, u = new Map((a || []).map((N) => [N.id, N])), v = /* @__PURE__ */ new Set(), z = [];
171
+ for (const N of _)
172
+ u.has(N) && !v.has(N) && (z.push(N), v.add(N));
173
+ for (const N of f)
174
+ v.has(N) || z.push(N);
175
+ return z;
176
+ }, [a, e, b, f, h]), d = $(() => {
177
+ const _ = new Map((a || []).map((u) => [u.id, u]));
178
+ return p.map((u) => _.get(u)).filter(Boolean);
179
+ }, [a, p]), m = L(
180
+ (_) => {
181
+ const u = C(_, f);
182
+ h || (x(u), l && Vt(l, u)), n == null || n(u);
183
+ },
184
+ [h, n, l, f]
185
+ );
186
+ return { columns: d, orderedIds: p, setOrder: m };
187
+ }
188
+ function se({
189
+ columns: a,
190
+ rows: e,
191
+ getRowId: n = Pt,
192
+ loading: l = !1,
193
+ emptyMessage: h = "No rows to display.",
194
+ summary: f,
195
+ className: b = "",
196
+ tableClassName: x = "highlight",
197
+ columnOrder: s,
198
+ onColumnOrderChange: p,
199
+ enableColumnReorder: d = !0,
200
+ columnWidths: m,
201
+ onColumnWidthsChange: _,
202
+ enableColumnResize: u = !0,
203
+ minColumnWidth: v = 64,
204
+ layoutStorageKey: z,
205
+ enableFiltering: N = !1,
206
+ filterFields: et = [],
207
+ filters: at,
208
+ onFiltersChange: H,
209
+ defaultFilters: St,
210
+ enablePagination: y = !1,
211
+ defaultPageSize: gt = 10,
212
+ pageSizeOptions: q = [5, 10, 20, 50],
213
+ currentPage: rt,
214
+ onPageChange: g,
215
+ pageSize: st,
216
+ onPageSizeChange: G,
217
+ filterBarClassName: ct = "",
218
+ paginationClassName: wt = "",
219
+ renderSummary: lt
220
+ }) {
221
+ const O = qt(), Y = at !== void 0, [xt, Dt] = R(() => ({
222
+ ...St ?? {}
223
+ })), D = rt !== void 0, Z = st !== void 0, [It, U] = R(1), [$t, Mt] = R(gt), A = Y ? at : xt, M = Z ? st : $t, it = $(
224
+ () => (a || []).map((t) => t.id),
225
+ [a]
226
+ ), nt = $(
227
+ () => z ?? [...it].sort().join("|"),
228
+ [z, it]
229
+ ), Lt = p == null && s == null ? `${Qt}${nt}` : null, F = _ == null && m === void 0 ? `${Yt}${nt}` : null, j = m !== void 0, [At, dt] = R({}), X = j ? m : At;
230
+ tt(() => {
231
+ if (j || !F) return;
232
+ const t = Kt(F);
233
+ t && dt(t);
234
+ }, [j, F]);
235
+ const V = L(
236
+ (t, r) => {
237
+ j ? _ == null || _({ ...m, [t]: r }) : dt((o) => {
238
+ const i = { ...o, [t]: r };
239
+ return _ == null || _(i), F && Wt(F, i), i;
240
+ });
241
+ },
242
+ [j, m, _, F]
243
+ ), K = yt(null), { columns: W, orderedIds: ot, setOrder: ft } = te(
244
+ a,
245
+ s,
246
+ p,
247
+ Lt
248
+ ), E = $(() => N ? Gt(e, A) : e, [e, N, A]), Tt = e.length, zt = E.length, I = $(() => y ? Math.max(1, Math.ceil(E.length / M)) : 1, [y, E.length, M]), ut = D ? rt : It, Q = $(() => y ? Math.min(Math.max(1, ut), I) : 1, [y, ut, I]);
249
+ tt(() => {
250
+ !y || D || U((t) => t > I ? I : t);
251
+ }, [I, y, D]);
252
+ const ht = $(() => {
253
+ if (!y) return E;
254
+ const t = (Q - 1) * M;
255
+ return E.slice(t, t + M);
256
+ }, [y, E, Q, M]), Ft = L(
257
+ (t) => {
258
+ const r = Math.min(Math.max(1, t), I);
259
+ D ? g == null || g(r) : U(r);
260
+ },
261
+ [I, D, g]
262
+ ), Et = L(
263
+ (t, r) => {
264
+ const o = typeof r == "string" ? r.trim() : r, i = { ...A };
265
+ o === "" || o == null ? delete i[t] : i[t] = o, Y ? H == null || H(i) : Dt(i), y && (D ? g == null || g(1) : U(1));
266
+ },
267
+ [
268
+ A,
269
+ Y,
270
+ H,
271
+ y,
272
+ D,
273
+ g
274
+ ]
275
+ ), _t = L(
276
+ (t) => {
277
+ const r = Number(t.target.value);
278
+ Number.isNaN(r) || r < 1 || (Z ? G == null || G(r) : Mt(r), D ? g == null || g(1) : U(1));
279
+ },
280
+ [Z, G, D, g]
281
+ ), kt = L(
282
+ (t) => {
283
+ var o;
284
+ const r = (o = t.currentTarget) == null ? void 0 : o.id;
285
+ r && d && (t.dataTransfer.setData("text", r), t.dataTransfer.effectAllowed = "move");
286
+ },
287
+ [d]
288
+ ), Ot = L(
289
+ (t) => {
290
+ var bt, Nt;
291
+ if (!d) return;
292
+ const r = t.dataTransfer.getData("text"), o = (Nt = (bt = t.target).closest) == null ? void 0 : Nt.call(bt, "th"), i = o == null ? void 0 : o.id;
293
+ if (!i || !r || i === r) return;
294
+ const S = [...ot], k = S.indexOf(r), B = S.indexOf(i);
295
+ if (k < 0 || B < 0) return;
296
+ const T = o.getBoundingClientRect(), P = t.clientX >= T.x + parseFloat(T.width) / 2, J = S.filter((Ht) => Ht !== r);
297
+ let vt = J.indexOf(i);
298
+ P && (vt += 1), J.splice(vt, 0, r), ft(J), t.dataTransfer.clearData();
299
+ },
300
+ [ot, d, ft]
301
+ ), jt = L(
302
+ (t, r) => {
303
+ if (!u) return;
304
+ r.preventDefault(), r.stopPropagation();
305
+ const o = r.currentTarget.closest("th"), i = X[t] ?? (o ? o.getBoundingClientRect().width : v);
306
+ K.current = { colId: t, startX: r.clientX, startW: i }, document.body.classList.add("react-data-table--resizing");
307
+ const S = (B) => {
308
+ const T = K.current;
309
+ if (!T) return;
310
+ const P = B.clientX - T.startX, J = Math.max(v, T.startW + P);
311
+ V(T.colId, J);
312
+ }, k = () => {
313
+ K.current = null, document.body.classList.remove("react-data-table--resizing"), document.removeEventListener("mousemove", S), document.removeEventListener("mouseup", k);
314
+ };
315
+ document.addEventListener("mousemove", S), document.addEventListener("mouseup", k);
316
+ },
317
+ [u, X, v, V]
318
+ );
319
+ if (l)
320
+ return /* @__PURE__ */ c("div", { className: `table react-data-table react-data-table--loading ${b}`.trim(), children: /* @__PURE__ */ c("div", { className: "react-data-table__spinner", "aria-busy": "true", "aria-label": "Loading" }) });
321
+ if (!a || a.length === 0)
322
+ return null;
323
+ const Xt = d || u, pt = N && et.length > 0, mt = y && Array.isArray(q) && q.length > 0, Bt = lt ? lt({
324
+ totalRows: Tt,
325
+ filteredCount: zt,
326
+ currentPage: Q,
327
+ pageSize: M,
328
+ totalPages: I
329
+ }) : f, Jt = W.map((t) => {
330
+ const r = X[t.id], o = r != null ? { width: r, minWidth: v, maxWidth: r } : { minWidth: v };
331
+ return /* @__PURE__ */ w(
332
+ "th",
333
+ {
334
+ id: t.id,
335
+ className: [
336
+ "react-data-table__th",
337
+ d && "react-data-table__th--draggable"
338
+ ].filter(Boolean).join(" "),
339
+ style: o,
340
+ draggable: d ? "true" : "false",
341
+ onDrop: Ot,
342
+ onDragStart: kt,
343
+ onDragOver: (i) => i.preventDefault(),
344
+ title: d && u ? "Drag to reorder · Drag the right edge to resize" : d ? "Drag to reorder columns" : u ? "Drag the right edge to resize" : void 0,
345
+ children: [
346
+ /* @__PURE__ */ w("div", { className: "react-data-table__th-main", children: [
347
+ d ? /* @__PURE__ */ c(
348
+ "span",
349
+ {
350
+ className: "react-data-table__drag-grip",
351
+ "aria-hidden": "true",
352
+ title: "Drag to reorder"
353
+ }
354
+ ) : null,
355
+ /* @__PURE__ */ c("span", { className: "react-data-table__th-title", children: t.title })
356
+ ] }),
357
+ u ? /* @__PURE__ */ c(
358
+ "span",
359
+ {
360
+ className: "react-data-table__resize-handle",
361
+ role: "separator",
362
+ "aria-orientation": "vertical",
363
+ tabIndex: 0,
364
+ "aria-label": "Resize column",
365
+ title: "Drag to resize",
366
+ onMouseDown: (i) => jt(t.id, i),
367
+ onKeyDown: (i) => {
368
+ var S;
369
+ if (i.key === "ArrowLeft" || i.key === "ArrowRight") {
370
+ i.preventDefault();
371
+ const k = i.key === "ArrowLeft" ? -8 : 8, B = X[t.id] ?? ((S = i.currentTarget.closest("th")) == null ? void 0 : S.getBoundingClientRect().width) ?? v;
372
+ V(t.id, Math.max(v, B + k));
373
+ }
374
+ }
375
+ }
376
+ ) : null
377
+ ]
378
+ },
379
+ t.id
380
+ );
381
+ }), Rt = ht.length > 0 ? ht.map((t, r) => /* @__PURE__ */ c("tr", { children: W.map((o) => {
382
+ const i = X[o.id], S = i != null ? { width: i, minWidth: v, maxWidth: i } : { minWidth: v };
383
+ return /* @__PURE__ */ c("td", { style: S, children: Ct(o, t) }, o.id);
384
+ }) }, n(t, r))) : [
385
+ /* @__PURE__ */ c("tr", { children: /* @__PURE__ */ c("td", { colSpan: W.length, className: "react-data-table__empty", children: h }) }, "__empty")
386
+ ];
387
+ return /* @__PURE__ */ w("div", { className: `table react-data-table ${b}`.trim(), children: [
388
+ pt ? /* @__PURE__ */ w(
389
+ "div",
390
+ {
391
+ className: `react-data-table__filter-bar ${ct}`.trim(),
392
+ role: "search",
393
+ children: [
394
+ et.map((t) => {
395
+ const r = t.field, o = `${O}-filter-${r}`, i = A[r] != null && A[r] !== "" ? String(A[r]) : "";
396
+ return /* @__PURE__ */ w("div", { className: "react-data-table__filter-field", children: [
397
+ /* @__PURE__ */ c("label", { className: "react-data-table__filter-label", htmlFor: o, children: t.label }),
398
+ /* @__PURE__ */ c(
399
+ "input",
400
+ {
401
+ id: o,
402
+ className: "react-data-table__filter-input",
403
+ type: "text",
404
+ placeholder: t.placeholder ?? "",
405
+ value: i,
406
+ onChange: (S) => Et(r, S.target.value),
407
+ autoComplete: "off"
408
+ }
409
+ )
410
+ ] }, r);
411
+ }),
412
+ mt ? /* @__PURE__ */ w("div", { className: "react-data-table__filter-field react-data-table__filter-field--page-size", children: [
413
+ /* @__PURE__ */ c("label", { className: "react-data-table__filter-label", htmlFor: `${O}-page-size`, children: "Rows per page" }),
414
+ /* @__PURE__ */ c(
415
+ "select",
416
+ {
417
+ id: `${O}-page-size`,
418
+ className: "react-data-table__filter-select",
419
+ value: M,
420
+ onChange: _t,
421
+ children: q.map((t) => /* @__PURE__ */ c("option", { value: t, children: t }, t))
422
+ }
423
+ )
424
+ ] }) : null
425
+ ]
426
+ }
427
+ ) : null,
428
+ !pt && y && mt ? /* @__PURE__ */ c(
429
+ "div",
430
+ {
431
+ className: `react-data-table__toolbar react-data-table__toolbar--pagination-only ${ct}`.trim(),
432
+ children: /* @__PURE__ */ w("div", { className: "react-data-table__filter-field react-data-table__filter-field--page-size", children: [
433
+ /* @__PURE__ */ c("label", { className: "react-data-table__filter-label", htmlFor: `${O}-page-size-solo`, children: "Rows per page" }),
434
+ /* @__PURE__ */ c(
435
+ "select",
436
+ {
437
+ id: `${O}-page-size-solo`,
438
+ className: "react-data-table__filter-select",
439
+ value: M,
440
+ onChange: _t,
441
+ children: q.map((t) => /* @__PURE__ */ c("option", { value: t, children: t }, t))
442
+ }
443
+ )
444
+ ] })
445
+ }
446
+ ) : null,
447
+ Xt ? /* @__PURE__ */ w("div", { className: "react-data-table__hints", role: "note", children: [
448
+ d ? /* @__PURE__ */ w("span", { className: "react-data-table__hint react-data-table__hint--drag", children: [
449
+ /* @__PURE__ */ c("span", { className: "react-data-table__hint-icon react-data-table__hint-icon--grip", "aria-hidden": "true" }),
450
+ "Drag column headers to reorder"
451
+ ] }) : null,
452
+ u ? /* @__PURE__ */ w("span", { className: "react-data-table__hint react-data-table__hint--resize", children: [
453
+ /* @__PURE__ */ c("span", { className: "react-data-table__hint-icon react-data-table__hint-icon--arrows", "aria-hidden": "true" }),
454
+ "Drag the right edge of a header to resize"
455
+ ] }) : null
456
+ ] }) : null,
457
+ /* @__PURE__ */ c("div", { className: "react-data-table__scroll", children: /* @__PURE__ */ w("table", { className: x, style: { tableLayout: "fixed" }, children: [
458
+ /* @__PURE__ */ c("thead", { className: "grey lighten-3", children: /* @__PURE__ */ c("tr", { children: Jt }) }),
459
+ /* @__PURE__ */ c("tbody", { children: Rt })
460
+ ] }) }),
461
+ Bt,
462
+ y ? /* @__PURE__ */ c(
463
+ Ut,
464
+ {
465
+ currentPage: Q,
466
+ totalPages: I,
467
+ onPageChange: Ft,
468
+ className: wt
469
+ }
470
+ ) : null
471
+ ] });
472
+ }
473
+ export {
474
+ se as DataTable,
475
+ Gt as filterRows
476
+ };
@@ -0,0 +1 @@
1
+ :root{--rdt-border: #c5cae9;--rdt-border-divider: #e0e0e0;--rdt-border-cell: #f0f0f0;--rdt-accent: #3949ab;--rdt-accent-soft: #e8eaf6;--rdt-accent-muted: #5c6bc0;--rdt-accent-blue: #1976d2;--rdt-accent-blue-soft: #e3f2fd;--rdt-pagination-active-bg: var(--rdt-accent-blue);--rdt-pagination-hover-bg: var(--rdt-accent-blue-soft);--rdt-spinner-track: var(--rdt-accent-blue-soft);--rdt-spinner-thumb: var(--rdt-accent-blue);--rdt-resize-hover: rgba(57, 73, 171, .35);--rdt-resize-handle-mid: rgba(25, 118, 210, .12);--rdt-resize-handle-end: rgba(25, 118, 210, .22);--rdt-resize-handle-inset: rgba(25, 118, 210, .12);--rdt-focus-ring: rgba(57, 73, 171, .2);--rdt-focus-outline-contrast: #ffffff;--rdt-surface: #ffffff;--rdt-surface-elevated: #f5f5f5;--rdt-surface-muted: #eeeeee;--rdt-surface-toolbar-start: #ffffff;--rdt-surface-toolbar-end: #f5f5f5;--rdt-surface-hints-start: #fafbff;--rdt-surface-hints-end: #eef0fb;--rdt-surface-header-start: #f5f5f5;--rdt-surface-header-end: #eeeeee;--rdt-surface-summary: #eeeeee;--rdt-surface-row-hover: #f5f5f5;--rdt-text: #424242;--rdt-text-strong: #263238;--rdt-text-muted: #757575;--rdt-text-label: #546e7a;--rdt-text-hint-drag: #283593;--rdt-text-hint-resize: #1565c0;--rdt-input-border: #b0bec5;--rdt-input-border-hover: #78909c;--rdt-pagination-active-fg: #ffffff;--rdt-pagination-disabled-opacity: .45;--rdt-hint-icon-opacity: .85;--rdt-drag-grip-opacity: .85}.react-data-table{color:var(--rdt-text)}.react-data-table__filter-bar,.react-data-table__toolbar{display:flex;flex-wrap:wrap;align-items:flex-end;gap:12px 24px;padding:10px 12px;border:1px solid var(--rdt-border);border-bottom:none;border-radius:6px 6px 0 0;background:linear-gradient(180deg,var(--rdt-surface-toolbar-start) 0%,var(--rdt-surface-toolbar-end) 100%)}.react-data-table__toolbar--pagination-only{justify-content:flex-end}.react-data-table__filter-field{display:flex;flex-direction:column;gap:4px;min-width:140px}.react-data-table__filter-field--page-size{margin-left:auto;min-width:0}.react-data-table__filter-label{font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--rdt-text-label)}.react-data-table__filter-input,.react-data-table__filter-select{font:inherit;font-size:13px;padding:6px 10px;border:1px solid var(--rdt-input-border);border-radius:4px;background:var(--rdt-surface);color:var(--rdt-text-strong);min-width:0;max-width:100%}.react-data-table__filter-input:hover,.react-data-table__filter-select:hover{border-color:var(--rdt-input-border-hover)}.react-data-table__filter-input:focus,.react-data-table__filter-select:focus{outline:none;border-color:var(--rdt-accent);box-shadow:0 0 0 2px var(--rdt-focus-ring)}.react-data-table__filter-bar+.react-data-table__hints{border-top:none;border-radius:0}.react-data-table__toolbar--pagination-only+.react-data-table__hints{border-top:none;border-radius:0}.react-data-table__filter-bar+.react-data-table__scroll,.react-data-table__toolbar--pagination-only+.react-data-table__scroll{border-top:none;border-radius:0 0 6px 6px}.react-data-table__hints{display:flex;flex-wrap:wrap;align-items:center;gap:.6rem 1.25rem;font-size:12px;line-height:1.35;color:var(--rdt-text);border:1px solid var(--rdt-border);border-bottom:none;border-radius:6px 6px 0 0;padding:8px 12px;background:linear-gradient(180deg,var(--rdt-surface-hints-start) 0%,var(--rdt-surface-hints-end) 100%)}.react-data-table__hint{display:inline-flex;align-items:center;gap:.45rem}.react-data-table__hint--drag{color:var(--rdt-text-hint-drag)}.react-data-table__hint--resize{color:var(--rdt-text-hint-resize)}.react-data-table__hint-icon{flex-shrink:0;width:1.1rem;height:1rem;border-radius:3px}.react-data-table__hint-icon--grip{background-image:radial-gradient(currentColor 1.2px,transparent 1.3px);background-size:5px 5px;background-position:0 0;opacity:var(--rdt-hint-icon-opacity)}.react-data-table__hint-icon--arrows{background:var(--rdt-accent-soft);border:1px solid var(--rdt-border);position:relative}.react-data-table__hint-icon--arrows:before,.react-data-table__hint-icon--arrows:after{content:"";position:absolute;top:50%;width:0;height:0;margin-top:-3px;border-style:solid}.react-data-table__hint-icon--arrows:before{left:2px;border-width:3px 4px 3px 0;border-color:transparent transparent transparent var(--rdt-accent)}.react-data-table__hint-icon--arrows:after{right:2px;border-width:3px 0 3px 4px;border-color:transparent var(--rdt-accent) transparent transparent}.react-data-table__scroll{overflow-x:auto;border:1px solid var(--rdt-border);border-radius:6px}.react-data-table__hints+.react-data-table__scroll{border-top:none;border-radius:0 0 6px 6px}.react-data-table table{width:100%;border-collapse:separate;border-spacing:0}.react-data-table__th{position:relative;text-align:left;padding:0;vertical-align:middle;border-right:1px solid var(--rdt-border-divider);background:linear-gradient(180deg,var(--rdt-surface-header-start) 0%,var(--rdt-surface-header-end) 100%);-webkit-user-select:none;user-select:none}.react-data-table__th:last-child{border-right:none}.react-data-table__th--draggable{cursor:grab}.react-data-table__th--draggable:active{cursor:grabbing}.react-data-table__th-main{display:flex;align-items:center;gap:.4rem;min-height:2.5rem;padding:8px 14px 8px 12px}.react-data-table__drag-grip{flex-shrink:0;width:10px;height:18px;border-radius:3px;cursor:grab;background-image:radial-gradient(var(--rdt-accent-muted) 1.2px,transparent 1.3px);background-size:4px 4px;background-position:0 0;opacity:var(--rdt-drag-grip-opacity)}.react-data-table__th--draggable:active .react-data-table__drag-grip{cursor:grabbing}.react-data-table__th-title{flex:1;min-width:0;font-weight:600;font-size:12px;letter-spacing:.02em}.react-data-table__resize-handle{position:absolute;top:0;right:0;width:10px;height:100%;margin:0;padding:0;cursor:col-resize;z-index:2;border-radius:0 0 2px;background:linear-gradient(90deg,transparent 0%,transparent 35%,var(--rdt-resize-handle-mid) 50%,var(--rdt-resize-handle-end) 100%);box-shadow:inset 1px 0 0 var(--rdt-resize-handle-inset)}.react-data-table__resize-handle:hover,.react-data-table__resize-handle:focus-visible{outline:none;background:linear-gradient(90deg,transparent 0%,var(--rdt-resize-hover) 100%);box-shadow:inset 2px 0 0 var(--rdt-accent)}.react-data-table__resize-handle:focus-visible{box-shadow:inset 2px 0 0 var(--rdt-accent),0 0 0 2px var(--rdt-focus-outline-contrast),0 0 0 4px var(--rdt-accent)}body.react-data-table--resizing{cursor:col-resize!important;-webkit-user-select:none!important;user-select:none!important}.react-data-table tbody td{padding:8px 12px;border-bottom:1px solid var(--rdt-border-divider);border-right:1px solid var(--rdt-border-cell);overflow:hidden;text-overflow:ellipsis;word-break:break-word}.react-data-table tbody td:last-child{border-right:none}.react-data-table__empty{text-align:center;color:var(--rdt-text-muted);padding:24px!important}.react-data-table--loading{min-height:120px;display:flex;align-items:center;justify-content:center}.react-data-table__spinner{width:40px;height:40px;border:3px solid var(--rdt-spinner-track);border-top-color:var(--rdt-spinner-thumb);border-radius:50%;animation:react-data-table-spin .8s linear infinite}@keyframes react-data-table-spin{to{transform:rotate(360deg)}}.summary-line{padding:16px 20px;display:flex;flex-direction:row;align-items:center;flex-wrap:wrap;gap:8px 40px;background:var(--rdt-surface-summary)}.summary-line .bold{font-weight:700}.react-data-table-pagination ul.pagination{list-style:none;display:flex;flex-wrap:wrap;gap:4px;margin:12px 0 0;padding:0;justify-content:flex-end;align-items:center}.react-data-table-pagination ul.pagination li{display:inline-block;min-width:32px;text-align:center}.react-data-table-pagination ul.pagination li a,.react-data-table-pagination ul.pagination span{display:inline-block;padding:6px 10px;text-decoration:none;color:var(--rdt-text);border-radius:2px}.react-data-table-pagination ul.pagination li.active a{background:var(--rdt-pagination-active-bg);color:var(--rdt-pagination-active-fg)}.react-data-table-pagination ul.pagination li.waves-effect a:hover{background:var(--rdt-pagination-hover-bg)}.react-data-table-pagination ul.pagination li.disabled{opacity:var(--rdt-pagination-disabled-opacity);pointer-events:none}thead.grey.lighten-3{background-color:var(--rdt-surface-muted)}table.highlight tbody tr:hover{background-color:var(--rdt-surface-row-hover)}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "react-column-drag-resize-table",
3
+ "version": "0.1.0",
4
+ "description": "React data table component: draggable column reorder, resizable columns, text filters, pagination, optional localStorage column layout, CSS variables theming. For admin dashboards and internal tools.",
5
+ "author": "Armine Inants",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.cjs"
16
+ },
17
+ "./styles.css": "./dist/react-column-drag-resize-table.css"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "README.md"
22
+ ],
23
+ "sideEffects": [
24
+ "**/*.css"
25
+ ],
26
+ "keywords": [
27
+ "react",
28
+ "react-component",
29
+ "table",
30
+ "data-table",
31
+ "datatable",
32
+ "data-grid",
33
+ "columns",
34
+ "resize",
35
+ "reorder",
36
+ "drag",
37
+ "draggable",
38
+ "column-resize",
39
+ "admin",
40
+ "dashboard",
41
+ "pagination",
42
+ "filter",
43
+ "filtering",
44
+ "ui",
45
+ "theme",
46
+ "css-variables"
47
+ ],
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/ArmineInants/react-data-table.git"
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/ArmineInants/react-data-table/issues"
54
+ },
55
+ "homepage": "https://react-data-table-topaz.vercel.app/",
56
+ "peerDependencies": {
57
+ "react": ">=16.8.0",
58
+ "react-dom": ">=16.8.0"
59
+ },
60
+ "scripts": {
61
+ "dev": "vite --config vite.demo.config.js",
62
+ "build:demo": "vite build --config vite.demo.config.js",
63
+ "build": "vite build && npm run copy:types",
64
+ "copy:types": "node -e \"require('fs').copyFileSync('src/index.d.ts','dist/index.d.ts')\"",
65
+ "prepublishOnly": "npm run build",
66
+ "preview": "vite preview --config vite.demo.config.js"
67
+ },
68
+ "devDependencies": {
69
+ "@vitejs/plugin-react": "^4.3.4",
70
+ "react": "^18.3.1",
71
+ "react-dom": "^18.3.1",
72
+ "vite": "^5.4.11"
73
+ }
74
+ }