@gulibs/react-vtable 0.0.11 → 0.0.13

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
@@ -116,8 +116,9 @@ The table provides **default search behavior** that works automatically:
116
116
  - **Remote Data Mode** (with `request` prop): Search triggers a new data fetch with search parameters passed to the backend.
117
117
 
118
118
  The default filter function supports:
119
- - Case-insensitive string matching (contains)
120
- - Array-based multi-select filtering
119
+ - Case-insensitive string matching (contains) for **text-like** fields
120
+ - **Exact match** for **enum/filter** fields (e.g. `valueEnum`, `filters`, `valueType: 'select' | 'status'`)
121
+ - Array-based multi-select filtering (exact match for enum/filter fields, contains match for text fields)
121
122
  - Automatic handling of empty/null values
122
123
 
123
124
  ```tsx
@@ -164,14 +165,152 @@ You can provide custom `onSearch` and `onReset` callbacks to override the defaul
164
165
  dataSource={dataSource}
165
166
  rowKey="id"
166
167
  pagination={{
167
- current: 1,
168
168
  pageSize: 10,
169
- total: 100,
170
169
  showSizeChanger: true
171
170
  }}
172
171
  />
173
172
  ```
174
173
 
174
+ #### Remote Pagination (with `request`)
175
+
176
+ In **Remote Data Mode** (when you pass `request`), pagination works like this:
177
+
178
+ - **`total` controls the page count**: return `total` from `request` (recommended) and/or pass `pagination.total`.
179
+ - **Do not pass `pagination.current/pageSize` as constants** (e.g. `current: 1`), unless you intend to lock the table to that page. If you want controlled pagination, store them in state and update in `pagination.onChange`.
180
+
181
+ **Uncontrolled (recommended if you don't need to control page state):**
182
+
183
+ ```tsx
184
+ <ProTable
185
+ columns={columns}
186
+ rowKey="id"
187
+ request={async (params) => {
188
+ // params includes: current, pageSize, ...filters, ...sorter
189
+ const res = await fetchUsers(params)
190
+ return { data: res.items, total: res.total, success: true }
191
+ }}
192
+ pagination={{
193
+ pageSize: 10,
194
+ showSizeChanger: true,
195
+ showQuickJumper: true,
196
+ }}
197
+ />
198
+ ```
199
+
200
+ **Controlled (when you need to sync pagination with external state / URL):**
201
+
202
+ ```tsx
203
+ function Page() {
204
+ const [pagination, setPagination] = useState({
205
+ current: 1,
206
+ pageSize: 10,
207
+ total: 0,
208
+ })
209
+
210
+ return (
211
+ <ProTable
212
+ columns={columns}
213
+ rowKey="id"
214
+ request={async (params) => {
215
+ const res = await fetchUsers(params)
216
+ setPagination((prev) => ({ ...prev, total: res.total }))
217
+ return { data: res.items, total: res.total, success: true }
218
+ }}
219
+ pagination={{
220
+ current: pagination.current,
221
+ pageSize: pagination.pageSize,
222
+ total: pagination.total,
223
+ showSizeChanger: true,
224
+ showQuickJumper: true,
225
+ onChange: (current, pageSize) => {
226
+ setPagination((prev) => ({ ...prev, current, pageSize }))
227
+ },
228
+ }}
229
+ />
230
+ )
231
+ }
232
+ ```
233
+
234
+ #### Server Pagination (NO `request`, you fetch data yourself)
235
+
236
+ If you **do not** use `request`, but you still want **server-side pagination** (i.e. `dataSource` only contains **current page items**),
237
+ set `pagination.mode: 'server'` to prevent TanStack Table from slicing again.
238
+
239
+ ```tsx
240
+ function Page() {
241
+ const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 })
242
+ const [data, setData] = useState<any[]>([])
243
+
244
+ const fetchPage = async (current: number, pageSize: number) => {
245
+ const res = await fetchUsers({ current, pageSize })
246
+ setData(res.items)
247
+ setPagination((prev) => ({ ...prev, current, pageSize, total: res.total }))
248
+ }
249
+
250
+ useEffect(() => {
251
+ void fetchPage(pagination.current, pagination.pageSize)
252
+ }, [])
253
+
254
+ return (
255
+ <ProTable
256
+ columns={columns}
257
+ rowKey="id"
258
+ dataSource={data}
259
+ pagination={{
260
+ mode: "server",
261
+ current: pagination.current,
262
+ pageSize: pagination.pageSize,
263
+ total: pagination.total,
264
+ onChange: (current, pageSize) => void fetchPage(current, pageSize),
265
+ }}
266
+ />
267
+ )
268
+ }
269
+ ```
270
+
271
+ #### Custom Pagination Rendering (`paginationRender`)
272
+
273
+ Use `paginationRender` when you want to **render your own pagination UI** (in any layout), while still reusing ProTable’s built-in
274
+ pagination state and default Pagination component.
275
+
276
+ Notes:
277
+ - `paginationRender` receives `pagination`, `onChange`, and `defaultDom`.
278
+ - If you are in **server pagination mode** (`pagination.mode: 'server'`) you must also provide `pagination.onChange` to fetch data.
279
+
280
+ ```tsx
281
+ <ProTable
282
+ columns={columns}
283
+ rowKey="id"
284
+ dataSource={data}
285
+ pagination={{
286
+ mode: "server",
287
+ current,
288
+ pageSize,
289
+ total,
290
+ onChange: (page, pageSize) => void fetchPage(page, pageSize),
291
+ }}
292
+ paginationRender={({ pagination, onChange, defaultDom }) => (
293
+ <div className="space-y-2">
294
+ <div className="flex items-center justify-between rounded border p-2">
295
+ <div className="text-sm text-muted-foreground">
296
+ Page {pagination.current} / Size {pagination.pageSize} / Total {pagination.total}
297
+ </div>
298
+ <div className="flex gap-2">
299
+ <button onClick={() => onChange(Math.max(1, pagination.current - 1), pagination.pageSize)}>
300
+ Prev
301
+ </button>
302
+ <button onClick={() => onChange(pagination.current + 1, pagination.pageSize)}>
303
+ Next
304
+ </button>
305
+ </div>
306
+ </div>
307
+ {/* reuse built-in Pagination UI */}
308
+ {defaultDom}
309
+ </div>
310
+ )}
311
+ />
312
+ ```
313
+
175
314
  #### With Internationalization
176
315
 
177
316
  ```tsx
@@ -629,8 +768,9 @@ function App() {
629
768
  - **远程数据模式**(有 `request` 属性):搜索会触发新的数据获取,搜索参数会传递给后端。
630
769
 
631
770
  默认过滤函数支持:
632
- - 不区分大小写的字符串匹配(包含)
633
- - 基于数组的多选过滤
771
+ - **文本类字段**:不区分大小写的字符串匹配(包含)
772
+ - **枚举 / 筛选类字段**:精确匹配(例如 `valueEnum`、`filters`、`valueType: 'select' | 'status'`)
773
+ - 基于数组的多选过滤(枚举 / 筛选类字段为精确匹配;文本类字段为包含匹配)
634
774
  - 自动处理空值 /null 值
635
775
 
636
776
  ```tsx
@@ -677,14 +817,149 @@ function App() {
677
817
  dataSource={dataSource}
678
818
  rowKey="id"
679
819
  pagination={{
680
- current: 1,
681
820
  pageSize: 10,
682
- total: 100,
683
821
  showSizeChanger: true
684
822
  }}
685
823
  />
686
824
  ```
687
825
 
826
+ #### 远程分页(配合 `request`)
827
+
828
+ 在**远程数据模式**(传入 `request`)下,分页规则如下:
829
+
830
+ - **`total` 决定页码数量**:推荐让 `request` 返回 `{ total }`,也可以 / 同时传入 `pagination.total`。
831
+ - **不要把 `pagination.current/pageSize` 写死**(例如 `current: 1`),除非你就是想把表格锁死在那一页;如果需要受控分页,请把它们放到 state 里,并在 `pagination.onChange` 里更新。
832
+
833
+ **非受控(推荐:不需要外部同步分页状态时)**:
834
+
835
+ ```tsx
836
+ <ProTable
837
+ columns={columns}
838
+ rowKey="id"
839
+ request={async (params) => {
840
+ // params 包含:current, pageSize, ...filters, ...sorter
841
+ const res = await fetchUsers(params)
842
+ return { data: res.items, total: res.total, success: true }
843
+ }}
844
+ pagination={{
845
+ pageSize: 10,
846
+ showSizeChanger: true,
847
+ showQuickJumper: true,
848
+ }}
849
+ />
850
+ ```
851
+
852
+ **受控(需要把分页状态同步到外部 state / URL 时)**:
853
+
854
+ ```tsx
855
+ function Page() {
856
+ const [pagination, setPagination] = useState({
857
+ current: 1,
858
+ pageSize: 10,
859
+ total: 0,
860
+ })
861
+
862
+ return (
863
+ <ProTable
864
+ columns={columns}
865
+ rowKey="id"
866
+ request={async (params) => {
867
+ const res = await fetchUsers(params)
868
+ setPagination((prev) => ({ ...prev, total: res.total }))
869
+ return { data: res.items, total: res.total, success: true }
870
+ }}
871
+ pagination={{
872
+ current: pagination.current,
873
+ pageSize: pagination.pageSize,
874
+ total: pagination.total,
875
+ showSizeChanger: true,
876
+ showQuickJumper: true,
877
+ onChange: (current, pageSize) => {
878
+ setPagination((prev) => ({ ...prev, current, pageSize }))
879
+ },
880
+ }}
881
+ />
882
+ )
883
+ }
884
+ ```
885
+
886
+ #### 服务端分页(不传 `request`,外部自行请求)
887
+
888
+ 如果你**不使用** `request`,但希望实现**服务端分页**(也就是 `dataSource` 只提供“当前页 items”),请设置
889
+ `pagination.mode: 'server'`,避免 TanStack Table 再次 slice 导致第 2 页为空 / 数据错乱。
890
+
891
+ ```tsx
892
+ function Page() {
893
+ const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 })
894
+ const [data, setData] = useState<any[]>([])
895
+
896
+ const fetchPage = async (current: number, pageSize: number) => {
897
+ const res = await fetchUsers({ current, pageSize })
898
+ setData(res.items)
899
+ setPagination((prev) => ({ ...prev, current, pageSize, total: res.total }))
900
+ }
901
+
902
+ useEffect(() => {
903
+ void fetchPage(pagination.current, pagination.pageSize)
904
+ }, [])
905
+
906
+ return (
907
+ <ProTable
908
+ columns={columns}
909
+ rowKey="id"
910
+ dataSource={data}
911
+ pagination={{
912
+ mode: "server",
913
+ current: pagination.current,
914
+ pageSize: pagination.pageSize,
915
+ total: pagination.total,
916
+ onChange: (current, pageSize) => void fetchPage(current, pageSize),
917
+ }}
918
+ />
919
+ )
920
+ }
921
+ ```
922
+
923
+ #### 自定义分页渲染(`paginationRender`)
924
+
925
+ 当你想把分页 UI 放到任意布局 / 卡片里,并且仍复用 ProTable 内置分页状态 / 逻辑时,使用 `paginationRender`:
926
+
927
+ - `paginationRender` 会拿到 `pagination`、`onChange`、`defaultDom`
928
+ - 如果你是 **服务端分页模式**(`pagination.mode: 'server'`),务必同时提供 `pagination.onChange` 去请求数据
929
+
930
+ ```tsx
931
+ <ProTable
932
+ columns={columns}
933
+ rowKey="id"
934
+ dataSource={data}
935
+ pagination={{
936
+ mode: "server",
937
+ current,
938
+ pageSize,
939
+ total,
940
+ onChange: (page, pageSize) => void fetchPage(page, pageSize),
941
+ }}
942
+ paginationRender={({ pagination, onChange, defaultDom }) => (
943
+ <div className="space-y-2">
944
+ <div className="flex items-center justify-between rounded border p-2">
945
+ <div className="text-sm text-muted-foreground">
946
+ 自定义分页:第 {pagination.current} 页 / 每页 {pagination.pageSize} 条 / 共 {pagination.total} 条
947
+ </div>
948
+ <div className="flex gap-2">
949
+ <button onClick={() => onChange(Math.max(1, pagination.current - 1), pagination.pageSize)}>
950
+ 上一页
951
+ </button>
952
+ <button onClick={() => onChange(pagination.current + 1, pagination.pageSize)}>
953
+ 下一页
954
+ </button>
955
+ </div>
956
+ </div>
957
+ {defaultDom}
958
+ </div>
959
+ )}
960
+ />
961
+ ```
962
+
688
963
  #### 带国际化
689
964
 
690
965
  ```tsx
@@ -5,7 +5,7 @@ interface LoadingStateProps {
5
5
  draggable?: boolean;
6
6
  /** 是否显示选择列 */
7
7
  rowSelection?: boolean;
8
- /** 加载行数,默认 5 */
8
+ /** 加载行数,默认 10(更贴近常用 pageSize,减少翻页加载时的高度跳动) */
9
9
  rowCount?: number;
10
10
  }
11
11
  export declare function LoadingState({ columnCount, draggable, rowSelection, rowCount, }: LoadingStateProps): import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"loading-state.d.ts","sourceRoot":"","sources":["../../lib/components/loading-state.tsx"],"names":[],"mappings":"AAGA,UAAU,iBAAiB;IACzB,SAAS;IACT,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc;IACd,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,YAAY,CAAC,EAC3B,WAAW,EACX,SAAiB,EACjB,YAAoB,EACpB,QAAY,GACb,EAAE,iBAAiB,2CAwBnB"}
1
+ {"version":3,"file":"loading-state.d.ts","sourceRoot":"","sources":["../../lib/components/loading-state.tsx"],"names":[],"mappings":"AAGA,UAAU,iBAAiB;IACzB,SAAS;IACT,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc;IACd,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,YAAY,CAAC,EAC3B,WAAW,EACX,SAAiB,EACjB,YAAoB,EACpB,QAAa,GACd,EAAE,iBAAiB,2CAwBnB"}
@@ -22,6 +22,9 @@ type ProTableActionType<T> = {
22
22
  data: T[];
23
23
  total?: number;
24
24
  };
25
+ } | {
26
+ type: 'SET_TOTAL';
27
+ payload: number;
25
28
  } | {
26
29
  type: 'SET_PAGINATION';
27
30
  payload: {
@@ -1 +1 @@
1
- {"version":3,"file":"use-pro-table.d.ts","sourceRoot":"","sources":["../../lib/hooks/use-pro-table.ts"],"names":[],"mappings":"AAaA,OAAO,EAAiE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAA;AAC/F,OAAO,KAAK,EACR,cAAc,EACd,aAAa,EAEhB,MAAM,SAAS,CAAA;AAGhB,UAAU,aAAa,CAAC,CAAC;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC3B,eAAe,EAAE,GAAG,EAAE,CAAA;IACtB,YAAY,EAAE,CAAC,EAAE,CAAA;IACjB,UAAU,EAAE,GAAG,GAAG,IAAI,CAAA;IACtB,aAAa,EAAE,CAAC,GAAG,IAAI,CAAA;CAC1B;AAED,KAAK,kBAAkB,CAAC,CAAC,IACnB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE;QAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC5E;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE;QAAE,IAAI,EAAE,GAAG,EAAE,CAAC;QAAC,IAAI,EAAE,CAAC,EAAE,CAAA;KAAE,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE;QAAE,GAAG,EAAE,GAAG,CAAC;QAAC,MAAM,EAAE,CAAC,CAAA;KAAE,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,CAAC,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAoEvB,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrD,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC;;;;;;0CAyS8B,MAAM,YAAY,MAAM;+BAUnC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;;;EAyHhE"}
1
+ {"version":3,"file":"use-pro-table.d.ts","sourceRoot":"","sources":["../../lib/hooks/use-pro-table.ts"],"names":[],"mappings":"AAaA,OAAO,EAAiE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAA;AAC/F,OAAO,KAAK,EACR,cAAc,EACd,aAAa,EAEhB,MAAM,SAAS,CAAA;AAGhB,UAAU,aAAa,CAAC,CAAC;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC3B,eAAe,EAAE,GAAG,EAAE,CAAA;IACtB,YAAY,EAAE,CAAC,EAAE,CAAA;IACjB,UAAU,EAAE,GAAG,GAAG,IAAI,CAAA;IACtB,aAAa,EAAE,CAAC,GAAG,IAAI,CAAA;CAC1B;AAED,KAAK,kBAAkB,CAAC,CAAC,IACnB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE;QAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAC5E;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE;QAAE,IAAI,EAAE,GAAG,EAAE,CAAC;QAAC,IAAI,EAAE,CAAC,EAAE,CAAA;KAAE,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE;QAAE,GAAG,EAAE,GAAG,CAAC;QAAC,MAAM,EAAE,CAAC,CAAA;KAAE,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,CAAC,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAyEvB,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrD,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC;;;;;;0CA6W8B,MAAM,YAAY,MAAM;+BAUnC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;;;EA2HhE"}