antd-crud-table 0.7.0 → 0.8.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/README.md +37 -0
- package/dist/CrudImportModal.d.ts +31 -0
- package/dist/CrudTable-BfVtTGYV.cjs +3 -0
- package/dist/CrudTable-jy8Db0uQ.js +1072 -0
- package/dist/CrudTable.cjs +1 -1
- package/dist/CrudTable.d.ts +10 -0
- package/dist/CrudTable.js +1 -1
- package/dist/CrudTableLazy.cjs +1 -1
- package/dist/CrudTableLazy.js +1 -1
- package/dist/core/RestDataSource.d.ts +97 -8
- package/dist/core/index.d.ts +1 -1
- package/dist/core/types.d.ts +8 -0
- package/dist/{en_US-DGCMDOdX.js → en_US-DghHhwRq.js} +19 -0
- package/dist/en_US-Q8nDeJhg.cjs +1 -0
- package/dist/fields/registry.d.ts +12 -0
- package/dist/hooks/useCrudTable.d.ts +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +50 -7
- package/dist/locale/types.d.ts +38 -0
- package/dist/{useCrudTable-DxjnXVlQ.js → useCrudTable-BVXxakfl.js} +84 -47
- package/dist/useCrudTable-Ex2Imgll.cjs +1 -0
- package/dist/useCrudTable.cjs +1 -1
- package/dist/useCrudTable.js +1 -1
- package/dist/useLocalStorageCrud.cjs +1 -1
- package/dist/useLocalStorageCrud.js +1 -1
- package/dist/utils/importData.d.ts +130 -0
- package/dist/utils/importSpreadsheetML.d.ts +8 -0
- package/dist/utils/importXlsx.d.ts +7 -0
- package/package.json +6 -1
- package/dist/CrudTable-Btf58zof.js +0 -610
- package/dist/CrudTable-DRN70sET.cjs +0 -1
- package/dist/en_US-D3Kl6g3n.cjs +0 -1
- package/dist/useCrudTable-40jpmCkr.cjs +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as e } from "./en_US-
|
|
1
|
+
import { t as e } from "./en_US-DghHhwRq.js";
|
|
2
2
|
import { message as t } from "antd";
|
|
3
3
|
import { useCallback as n, useMemo as r, useRef as i, useState as a } from "react";
|
|
4
4
|
//#region lib/core/identity.ts
|
|
@@ -138,56 +138,64 @@ var o = () => {
|
|
|
138
138
|
updatedAt: n
|
|
139
139
|
} : { updatedAt: n };
|
|
140
140
|
}
|
|
141
|
-
}, v = {
|
|
141
|
+
}, v = (e, t) => {
|
|
142
|
+
let n = t.toString();
|
|
143
|
+
return n === "" ? e : `${e}${e.includes("?") ? "&" : "?"}${n}`;
|
|
144
|
+
}, y = {
|
|
142
145
|
list: "/list",
|
|
143
146
|
create: "/create",
|
|
144
147
|
update: "/update",
|
|
145
148
|
remove: "/delete"
|
|
146
|
-
},
|
|
149
|
+
}, b = {
|
|
147
150
|
page: "current",
|
|
148
151
|
pageSize: "pageSize",
|
|
149
152
|
sortBy: "sortBy",
|
|
150
153
|
sortOrder: "sortOrder"
|
|
151
|
-
},
|
|
154
|
+
}, x = {
|
|
152
155
|
create: "POST",
|
|
153
156
|
update: "PUT",
|
|
154
157
|
remove: "DELETE"
|
|
155
|
-
},
|
|
158
|
+
}, S = class extends Error {
|
|
156
159
|
status;
|
|
157
160
|
body;
|
|
158
161
|
constructor(e, t, n) {
|
|
159
162
|
super(`Request to ${n} failed with status ${e}`), this.name = "RestError", this.status = e, this.body = t;
|
|
160
163
|
}
|
|
161
|
-
},
|
|
164
|
+
}, C = class {
|
|
162
165
|
baseUrl;
|
|
163
166
|
endpoints;
|
|
164
167
|
paramNames;
|
|
165
168
|
methods;
|
|
166
169
|
headers;
|
|
170
|
+
query;
|
|
171
|
+
init;
|
|
172
|
+
configureRequest;
|
|
167
173
|
serializeRequest;
|
|
168
174
|
parseResponse;
|
|
175
|
+
parseRecord;
|
|
169
176
|
fetchImpl;
|
|
170
177
|
constructor(e = {}) {
|
|
171
178
|
this.baseUrl = e.baseUrl ?? "", this.endpoints = {
|
|
172
|
-
...
|
|
179
|
+
...y,
|
|
173
180
|
...e.endpoints
|
|
174
181
|
}, this.paramNames = {
|
|
175
|
-
...
|
|
182
|
+
...b,
|
|
176
183
|
...e.paramNames
|
|
177
184
|
}, this.methods = {
|
|
178
|
-
...
|
|
185
|
+
...x,
|
|
179
186
|
...e.methods
|
|
180
|
-
}, this.headers = e.headers ?? {}, this.serializeRequest = e.serializeRequest ?? ((e) => e), this.parseResponse = e.parseResponse, this.fetchImpl = e.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
187
|
+
}, this.headers = e.headers ?? {}, this.query = e.query ?? {}, this.init = e.init ?? {}, this.configureRequest = e.configureRequest, this.serializeRequest = e.serializeRequest ?? ((e) => e), this.parseResponse = e.parseResponse, this.parseRecord = e.parseRecord, this.fetchImpl = e.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
181
188
|
}
|
|
182
189
|
serializeSort(e, t) {
|
|
183
190
|
let n = e?.[0];
|
|
184
191
|
n && (t.set(this.paramNames.sortBy, String(n.field)), t.set(this.paramNames.sortOrder, n.direction));
|
|
185
192
|
}
|
|
193
|
+
serializeFilters(e, t) {
|
|
194
|
+
for (let [n, r] of Object.entries(e ?? {})) r != null && r !== "" && t.set(n, String(r));
|
|
195
|
+
}
|
|
186
196
|
buildListUrl(e) {
|
|
187
197
|
let t = new URLSearchParams();
|
|
188
|
-
t.set(this.paramNames.page, String(e.page)), t.set(this.paramNames.pageSize, String(e.pageSize)), this.serializeSort(e.sort, t);
|
|
189
|
-
for (let [n, r] of Object.entries(e.filters ?? {})) r != null && r !== "" && t.set(n, String(r));
|
|
190
|
-
return `${this.endpoints.list}?${t.toString()}`;
|
|
198
|
+
return t.set(this.paramNames.page, String(e.page)), t.set(this.paramNames.pageSize, String(e.pageSize)), this.serializeSort(e.sort, t), this.serializeFilters(e.filters, t), v(this.endpoints.list, t);
|
|
191
199
|
}
|
|
192
200
|
buildRecordPath(e, t) {
|
|
193
201
|
return `${e}/${encodeURIComponent(String(t))}`;
|
|
@@ -210,44 +218,73 @@ var o = () => {
|
|
|
210
218
|
}
|
|
211
219
|
throw Error("Unrecognised list response. Provide `parseResponse` to map it to { items, total }.");
|
|
212
220
|
}
|
|
213
|
-
async request(e, t = {}) {
|
|
214
|
-
let
|
|
215
|
-
|
|
221
|
+
async request(e, t, n = {}) {
|
|
222
|
+
let r = await this.configureRequest?.(t) ?? {}, i = new URLSearchParams();
|
|
223
|
+
for (let [e, t] of Object.entries({
|
|
224
|
+
...this.query,
|
|
225
|
+
...r.query
|
|
226
|
+
})) i.set(e, String(t));
|
|
227
|
+
let a = v(`${this.baseUrl}${e}`, i), o = await this.fetchImpl(a, {
|
|
228
|
+
...this.init,
|
|
229
|
+
...r.init,
|
|
230
|
+
...n,
|
|
231
|
+
method: r.method ?? n.method,
|
|
216
232
|
headers: {
|
|
217
233
|
"Content-Type": "application/json",
|
|
218
234
|
...this.headers,
|
|
219
|
-
...
|
|
235
|
+
...r.headers,
|
|
236
|
+
...n.headers
|
|
220
237
|
}
|
|
221
238
|
});
|
|
222
|
-
if (!
|
|
223
|
-
if (
|
|
224
|
-
let
|
|
225
|
-
return
|
|
239
|
+
if (!o.ok) throw new S(o.status, await o.text().catch(() => ""), a);
|
|
240
|
+
if (o.status === 204) return;
|
|
241
|
+
let s = await o.text();
|
|
242
|
+
return s === "" ? void 0 : JSON.parse(s);
|
|
243
|
+
}
|
|
244
|
+
toRecord(e, t) {
|
|
245
|
+
return this.parseRecord ? this.parseRecord(e, t) : e;
|
|
226
246
|
}
|
|
227
247
|
async list(e) {
|
|
228
|
-
|
|
248
|
+
let t = {
|
|
249
|
+
operation: "list",
|
|
250
|
+
query: e
|
|
251
|
+
};
|
|
252
|
+
return this.toPage(await this.request(this.buildListUrl(e), t));
|
|
229
253
|
}
|
|
230
254
|
async create(e) {
|
|
231
|
-
|
|
255
|
+
let t = {
|
|
256
|
+
operation: "create",
|
|
257
|
+
draft: e
|
|
258
|
+
}, n = await this.request(this.endpoints.create, t, {
|
|
232
259
|
method: this.methods.create,
|
|
233
|
-
body: JSON.stringify(this.serializeRequest(e))
|
|
260
|
+
body: JSON.stringify(this.serializeRequest(e, t))
|
|
234
261
|
});
|
|
262
|
+
return this.toRecord(n, t);
|
|
235
263
|
}
|
|
236
264
|
async update(e, t) {
|
|
237
|
-
|
|
265
|
+
let n = {
|
|
266
|
+
operation: "update",
|
|
267
|
+
id: e,
|
|
268
|
+
draft: t
|
|
269
|
+
}, r = await this.request(this.buildRecordPath(this.endpoints.update, e), n, {
|
|
238
270
|
method: this.methods.update,
|
|
239
|
-
body: JSON.stringify(this.serializeRequest(t))
|
|
271
|
+
body: JSON.stringify(this.serializeRequest(t, n))
|
|
240
272
|
});
|
|
273
|
+
return this.toRecord(r, n);
|
|
241
274
|
}
|
|
242
275
|
async remove(e) {
|
|
243
|
-
|
|
276
|
+
let t = {
|
|
277
|
+
operation: "remove",
|
|
278
|
+
id: e
|
|
279
|
+
};
|
|
280
|
+
await this.request(this.buildRecordPath(this.endpoints.remove, e), t, { method: this.methods.remove });
|
|
244
281
|
}
|
|
245
|
-
},
|
|
282
|
+
}, w = class extends Error {
|
|
246
283
|
operation;
|
|
247
284
|
constructor(e) {
|
|
248
285
|
super(`This table's data source does not support "${e}". Provide an \`${e}\` operation to enable it.`), this.name = "UnsupportedOperationError", this.operation = e;
|
|
249
286
|
}
|
|
250
|
-
},
|
|
287
|
+
}, T = class {
|
|
251
288
|
operations;
|
|
252
289
|
constructor(e) {
|
|
253
290
|
this.operations = e;
|
|
@@ -256,40 +293,40 @@ var o = () => {
|
|
|
256
293
|
return typeof this.operations[e] == "function";
|
|
257
294
|
}
|
|
258
295
|
async list(e) {
|
|
259
|
-
if (!this.operations.list) throw new
|
|
296
|
+
if (!this.operations.list) throw new w("list");
|
|
260
297
|
return this.operations.list(e);
|
|
261
298
|
}
|
|
262
299
|
async listAll(e) {
|
|
263
300
|
if (this.operations.listAll) return this.operations.listAll(e);
|
|
264
|
-
throw new
|
|
301
|
+
throw new w("listAll");
|
|
265
302
|
}
|
|
266
303
|
async create(e) {
|
|
267
|
-
if (!this.operations.create) throw new
|
|
304
|
+
if (!this.operations.create) throw new w("create");
|
|
268
305
|
return this.operations.create(e);
|
|
269
306
|
}
|
|
270
307
|
async update(e, t) {
|
|
271
|
-
if (!this.operations.update) throw new
|
|
308
|
+
if (!this.operations.update) throw new w("update");
|
|
272
309
|
return this.operations.update(e, t);
|
|
273
310
|
}
|
|
274
311
|
async remove(e) {
|
|
275
|
-
if (!this.operations.remove) throw new
|
|
312
|
+
if (!this.operations.remove) throw new w("remove");
|
|
276
313
|
return this.operations.remove(e);
|
|
277
314
|
}
|
|
278
|
-
},
|
|
315
|
+
}, E = (e) => e instanceof Error ? e : Error(typeof e == "string" ? e : "Unknown error", { cause: e }), D = (e, t) => {
|
|
279
316
|
if ("dataSource" in t) return t.dataSource;
|
|
280
|
-
if ("operations" in t) return new
|
|
317
|
+
if ("operations" in t) return new T(t.operations);
|
|
281
318
|
if ("storageKey" in t) return new _(t.storageKey, e, t.initialData ?? [], t.generateId);
|
|
282
319
|
if ("staticData" in t) return new g(t.staticData, e, t.generateId);
|
|
283
|
-
if ("api" in t) return new
|
|
320
|
+
if ("api" in t) return new C(t.api);
|
|
284
321
|
throw Error("useCrudTable: supply exactly one of `staticData`, `api`, `storageKey`, `operations` or `dataSource`.");
|
|
285
|
-
},
|
|
322
|
+
}, O = (o, s) => {
|
|
286
323
|
let c = i(void 0), { defaultPageSize: l = 10, optimisticUpdates: u = !1, refreshAfterMutation: d = !0, notifications: f = !0 } = s, p = r(() => s.locale ? {
|
|
287
324
|
...e,
|
|
288
325
|
...s.locale
|
|
289
326
|
} : e, [s.locale]), m = i(s);
|
|
290
327
|
m.current = s;
|
|
291
328
|
let h = i(null);
|
|
292
|
-
h.current === null && (h.current =
|
|
329
|
+
h.current === null && (h.current = D(o, s));
|
|
293
330
|
let g = h.current, [_, v] = a({
|
|
294
331
|
loading: !1,
|
|
295
332
|
error: null,
|
|
@@ -319,7 +356,7 @@ var o = () => {
|
|
|
319
356
|
loading: !1
|
|
320
357
|
}));
|
|
321
358
|
} catch (e) {
|
|
322
|
-
let t =
|
|
359
|
+
let t = E(e);
|
|
323
360
|
v((e) => ({
|
|
324
361
|
...e,
|
|
325
362
|
loading: !1,
|
|
@@ -348,7 +385,7 @@ var o = () => {
|
|
|
348
385
|
u,
|
|
349
386
|
d,
|
|
350
387
|
C
|
|
351
|
-
]),
|
|
388
|
+
]), T = n(async (e) => {
|
|
352
389
|
v((e) => ({
|
|
353
390
|
...e,
|
|
354
391
|
loading: !0
|
|
@@ -365,7 +402,7 @@ var o = () => {
|
|
|
365
402
|
return v((e) => ({
|
|
366
403
|
...e,
|
|
367
404
|
loading: !1
|
|
368
|
-
})), x("create",
|
|
405
|
+
})), x("create", E(e)), null;
|
|
369
406
|
}
|
|
370
407
|
}, [
|
|
371
408
|
g,
|
|
@@ -389,7 +426,7 @@ var o = () => {
|
|
|
389
426
|
return v((e) => ({
|
|
390
427
|
...e,
|
|
391
428
|
loading: !1
|
|
392
|
-
})), x("update",
|
|
429
|
+
})), x("update", E(e)), null;
|
|
393
430
|
}
|
|
394
431
|
}, [
|
|
395
432
|
g,
|
|
@@ -414,7 +451,7 @@ var o = () => {
|
|
|
414
451
|
return v((e) => ({
|
|
415
452
|
...e,
|
|
416
453
|
loading: !1
|
|
417
|
-
})), x("delete",
|
|
454
|
+
})), x("delete", E(e)), !1;
|
|
418
455
|
}
|
|
419
456
|
}, [
|
|
420
457
|
g,
|
|
@@ -437,7 +474,7 @@ var o = () => {
|
|
|
437
474
|
}, []);
|
|
438
475
|
return r(() => ({
|
|
439
476
|
refresh: C,
|
|
440
|
-
create:
|
|
477
|
+
create: T,
|
|
441
478
|
update: O,
|
|
442
479
|
remove: k,
|
|
443
480
|
dataSource: g,
|
|
@@ -447,7 +484,7 @@ var o = () => {
|
|
|
447
484
|
actionRef: c
|
|
448
485
|
}), [
|
|
449
486
|
C,
|
|
450
|
-
|
|
487
|
+
T,
|
|
451
488
|
O,
|
|
452
489
|
k,
|
|
453
490
|
g,
|
|
@@ -457,4 +494,4 @@ var o = () => {
|
|
|
457
494
|
]);
|
|
458
495
|
};
|
|
459
496
|
//#endregion
|
|
460
|
-
export {
|
|
497
|
+
export { C as a, g as c, p as d, m as f, o as h, w as i, h as l, c as m, O as n, S as o, f as p, T as r, _ as s, E as t, d as u };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=require("./en_US-Q8nDeJhg.cjs");let t=require("antd"),n=require("react");var r=()=>{let e=globalThis.crypto===void 0?void 0:globalThis.crypto;if(e&&typeof e.randomUUID==`function`)return e.randomUUID();let t=new Uint8Array(16);if(e&&typeof e.getRandomValues==`function`)e.getRandomValues(t);else for(let e=0;e<t.length;e+=1)t[e]=Math.floor(Math.random()*256);t[6]=t[6]&15|64,t[8]=t[8]&63|128;let n=Array.from(t,e=>e.toString(16).padStart(2,`0`)).join(``);return`${n.slice(0,8)}-${n.slice(8,12)}-${n.slice(12,16)}-${n.slice(16,20)}-${n.slice(20)}`},i=/^\d+$/,a=e=>t=>{let n=t.map(t=>t[e]).filter(e=>e!=null);if(n.length===0)return 1;if(n.every(e=>typeof e==`number`)){let e=Math.max(...n.filter(Number.isFinite));return(Number.isFinite(e)?e:0)+1}if(n.every(e=>typeof e==`string`)){let e=n;if(e.every(e=>i.test(e))){let t=Math.max(...e.map(Number));return String(t+1)}return r()}throw Error(`Cannot derive the next id for "${String(e)}": existing keys are neither all numbers nor all strings. Supply an explicit \`generateId\` to the data source.`)},o=(e,t)=>typeof e==`number`&&typeof t==`number`?e-t:typeof e==`boolean`&&typeof t==`boolean`?Number(e)-Number(t):e instanceof Date&&t instanceof Date?e.getTime()-t.getTime():String(e).localeCompare(String(t),void 0,{numeric:!0}),s=(e,t)=>e!=null&&String(e).toLowerCase().includes(String(t).toLowerCase()),c=(e,t)=>{if(!t)return e;let n=Object.entries(t).filter(([,e])=>e!=null&&e!==``);return n.length===0?e:e.filter(e=>n.every(([t,n])=>s(e[t],n)))},l=(e,t)=>!t||t.length===0?e:[...e].sort((e,n)=>{for(let{field:r,direction:i}of t){let t=e[r],a=n[r],s=t==null,c=a==null;if(s&&c)continue;if(s)return 1;if(c)return-1;let l=o(t,a);if(l!==0)return i===`ascend`?l:-l}return 0}),u=(e,t,n)=>{let r=Math.max(0,(t-1)*n);return e.slice(r,r+n)},d=(e,t)=>{let n=l(c(e,t.filters),t.sort);return{items:u(n,t.page,t.pageSize),total:n.length}},f=class{key;generateId;constructor(e,t){this.key=e,this.generateId=t??a(e)}decorate(e,t){return{}}indexOf(e){return this.read().findIndex(t=>t[this.key]===e)}async list(e){return d(this.read(),e)}async listAll(e){return l(c(this.read(),e.filters),e.sort)}async create(e){let t=this.read(),n={[this.key]:this.generateId(t),...e,...this.decorate(e,`create`)};return this.write([...t,n]),n}async update(e,t){let n=this.read(),r=this.indexOf(e);if(r===-1)throw Error(`No record with ${String(this.key)} "${String(e)}"`);let i={...n[r],...t,...this.decorate(t,`update`),[this.key]:e},a=[...n];return a[r]=i,this.write(a),i}async remove(e){let t=this.read();if(this.indexOf(e)===-1)throw Error(`No record with ${String(this.key)} "${String(e)}"`);this.write(t.filter(t=>t[this.key]!==e))}},p=class extends f{records;constructor(e,t,n){super(t,n),this.records=[...e]}read(){return this.records}write(e){this.records=e}},m=class extends f{storageKey;seed;constructor(e,t,n=[],r){super(t,r),this.storageKey=e,this.seed=n}read(){let e;try{e=localStorage.getItem(this.storageKey)}catch{return this.seed}if(e===null)return this.seed;try{let t=JSON.parse(e);return Array.isArray(t)?t:this.seed}catch{return this.seed}}write(e){try{localStorage.setItem(this.storageKey,JSON.stringify(e))}catch(e){throw Error(`Failed to persist to localStorage key "${this.storageKey}": ${e instanceof Error?e.message:String(e)}`)}}decorate(e,t){let n=new Date().toISOString();return t===`create`?{createdAt:n,updatedAt:n}:{updatedAt:n}}},h=(e,t)=>{let n=t.toString();return n===``?e:`${e}${e.includes(`?`)?`&`:`?`}${n}`},g={list:`/list`,create:`/create`,update:`/update`,remove:`/delete`},_={page:`current`,pageSize:`pageSize`,sortBy:`sortBy`,sortOrder:`sortOrder`},v={create:`POST`,update:`PUT`,remove:`DELETE`},y=class extends Error{status;body;constructor(e,t,n){super(`Request to ${n} failed with status ${e}`),this.name=`RestError`,this.status=e,this.body=t}},b=class{baseUrl;endpoints;paramNames;methods;headers;query;init;configureRequest;serializeRequest;parseResponse;parseRecord;fetchImpl;constructor(e={}){this.baseUrl=e.baseUrl??``,this.endpoints={...g,...e.endpoints},this.paramNames={..._,...e.paramNames},this.methods={...v,...e.methods},this.headers=e.headers??{},this.query=e.query??{},this.init=e.init??{},this.configureRequest=e.configureRequest,this.serializeRequest=e.serializeRequest??(e=>e),this.parseResponse=e.parseResponse,this.parseRecord=e.parseRecord,this.fetchImpl=e.fetchImpl??globalThis.fetch.bind(globalThis)}serializeSort(e,t){let n=e?.[0];n&&(t.set(this.paramNames.sortBy,String(n.field)),t.set(this.paramNames.sortOrder,n.direction))}serializeFilters(e,t){for(let[n,r]of Object.entries(e??{}))r!=null&&r!==``&&t.set(n,String(r))}buildListUrl(e){let t=new URLSearchParams;return t.set(this.paramNames.page,String(e.page)),t.set(this.paramNames.pageSize,String(e.pageSize)),this.serializeSort(e.sort,t),this.serializeFilters(e.filters,t),h(this.endpoints.list,t)}buildRecordPath(e,t){return`${e}/${encodeURIComponent(String(t))}`}toPage(e){if(this.parseResponse)return this.parseResponse(e);if(Array.isArray(e))return{items:e,total:e.length};if(typeof e==`object`&&e){let t=e;if(Array.isArray(t.data)){let e=t.data;return{items:e,total:typeof t.total==`number`?t.total:e.length}}}throw Error("Unrecognised list response. Provide `parseResponse` to map it to { items, total }.")}async request(e,t,n={}){let r=await this.configureRequest?.(t)??{},i=new URLSearchParams;for(let[e,t]of Object.entries({...this.query,...r.query}))i.set(e,String(t));let a=h(`${this.baseUrl}${e}`,i),o=await this.fetchImpl(a,{...this.init,...r.init,...n,method:r.method??n.method,headers:{"Content-Type":`application/json`,...this.headers,...r.headers,...n.headers}});if(!o.ok)throw new y(o.status,await o.text().catch(()=>``),a);if(o.status===204)return;let s=await o.text();return s===``?void 0:JSON.parse(s)}toRecord(e,t){return this.parseRecord?this.parseRecord(e,t):e}async list(e){let t={operation:`list`,query:e};return this.toPage(await this.request(this.buildListUrl(e),t))}async create(e){let t={operation:`create`,draft:e},n=await this.request(this.endpoints.create,t,{method:this.methods.create,body:JSON.stringify(this.serializeRequest(e,t))});return this.toRecord(n,t)}async update(e,t){let n={operation:`update`,id:e,draft:t},r=await this.request(this.buildRecordPath(this.endpoints.update,e),n,{method:this.methods.update,body:JSON.stringify(this.serializeRequest(t,n))});return this.toRecord(r,n)}async remove(e){let t={operation:`remove`,id:e};await this.request(this.buildRecordPath(this.endpoints.remove,e),t,{method:this.methods.remove})}},x=class extends Error{operation;constructor(e){super(`This table's data source does not support "${e}". Provide an \`${e}\` operation to enable it.`),this.name=`UnsupportedOperationError`,this.operation=e}},S=class{operations;constructor(e){this.operations=e}supports(e){return typeof this.operations[e]==`function`}async list(e){if(!this.operations.list)throw new x(`list`);return this.operations.list(e)}async listAll(e){if(this.operations.listAll)return this.operations.listAll(e);throw new x(`listAll`)}async create(e){if(!this.operations.create)throw new x(`create`);return this.operations.create(e)}async update(e,t){if(!this.operations.update)throw new x(`update`);return this.operations.update(e,t)}async remove(e){if(!this.operations.remove)throw new x(`remove`);return this.operations.remove(e)}},C=e=>e instanceof Error?e:Error(typeof e==`string`?e:`Unknown error`,{cause:e}),w=(e,t)=>{if(`dataSource`in t)return t.dataSource;if(`operations`in t)return new S(t.operations);if(`storageKey`in t)return new m(t.storageKey,e,t.initialData??[],t.generateId);if(`staticData`in t)return new p(t.staticData,e,t.generateId);if(`api`in t)return new b(t.api);throw Error("useCrudTable: supply exactly one of `staticData`, `api`, `storageKey`, `operations` or `dataSource`.")},T=(r,i)=>{let a=(0,n.useRef)(void 0),{defaultPageSize:o=10,optimisticUpdates:s=!1,refreshAfterMutation:c=!0,notifications:l=!0}=i,u=(0,n.useMemo)(()=>i.locale?{...e.t,...i.locale}:e.t,[i.locale]),d=(0,n.useRef)(i);d.current=i;let f=(0,n.useRef)(null);f.current===null&&(f.current=w(r,i));let p=f.current,[m,h]=(0,n.useState)({loading:!1,error:null,data:[],total:0,page:1,pageSize:o}),{page:g,pageSize:_}=m,v=(0,n.useCallback)((e,n)=>{d.current.onError?.(e,n),l&&t.message.error(n.message)},[l]),y=(0,n.useCallback)((e,n,r)=>{d.current.onSuccess?.(e,n),l&&t.message.success(r)},[l]),b=(0,n.useCallback)(async()=>{h(e=>({...e,loading:!0,error:null}));try{let e={page:g,pageSize:_},t=await p.list(e);h(e=>({...e,data:t.items,total:t.total,loading:!1}))}catch(e){let t=C(e);h(e=>({...e,loading:!1,error:t})),v(`list`,t)}},[p,g,_,v]),x=(0,n.useCallback)(async e=>{if(s){h(e);return}if(c){await b();return}h(e=>({...e,loading:!1}))},[s,c,b]),S=(0,n.useCallback)(async e=>{h(e=>({...e,loading:!0}));try{let t=await p.create(e);return await x(e=>({...e,data:[...e.data,t],total:e.total+1,loading:!1})),y(`create`,t,u.createSuccess),t}catch(e){return h(e=>({...e,loading:!1})),v(`create`,C(e)),null}},[p,x,v,y,u]),T=(0,n.useCallback)(async(e,t)=>{h(e=>({...e,loading:!0}));try{let n=await p.update(e,t);return await x(t=>({...t,data:t.data.map(t=>t[r]===e?n:t),loading:!1})),y(`update`,n,u.updateSuccess),n}catch(e){return h(e=>({...e,loading:!1})),v(`update`,C(e)),null}},[p,x,v,r,y,u]),E=(0,n.useCallback)(async e=>{h(e=>({...e,loading:!0}));try{return await p.remove(e),await x(t=>({...t,data:t.data.filter(t=>t[r]!==e),total:Math.max(0,t.total-1),loading:!1})),y(`delete`,e,u.deleteSuccess),!0}catch(e){return h(e=>({...e,loading:!1})),v(`delete`,C(e)),!1}},[p,x,v,r,y,u]),D=(0,n.useCallback)(e=>{h(t=>({...t,page:e}))},[]),O=(0,n.useCallback)(e=>{h(t=>({...t,pageSize:e,page:1}))},[]);return(0,n.useMemo)(()=>({refresh:b,create:S,update:T,remove:E,dataSource:p,setPage:D,setPageSize:O,state:m,actionRef:a}),[b,S,T,E,p,D,O,m])};Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,"h",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return c}});
|
package/dist/useCrudTable.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./useCrudTable-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./useCrudTable-Ex2Imgll.cjs");exports.toError=e.t,exports.useCrudTable=e.n;
|
package/dist/useCrudTable.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as e, t } from "./useCrudTable-
|
|
1
|
+
import { n as e, t } from "./useCrudTable-BVXxakfl.js";
|
|
2
2
|
export { t as toError, e as useCrudTable };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./useCrudTable-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./useCrudTable-Ex2Imgll.cjs");let t=require("react");var n=(n,r,i=[],a)=>{let o=e.n(n,{...a,storageKey:r,initialData:i}),{refresh:s}=o;return(0,t.useEffect)(()=>{s()},[]),o};exports.useLocalStorageCrud=n;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { FormRule } from 'antd';
|
|
2
|
+
import { FieldType } from '../fields/registry';
|
|
3
|
+
import { EnumOption } from '../fields/types';
|
|
4
|
+
/** File formats the importer can read. */
|
|
5
|
+
export type ImportFormat = 'csv' | 'xls' | 'xlsx';
|
|
6
|
+
/**
|
|
7
|
+
* The column information the importer needs: how to match a header, how to
|
|
8
|
+
* coerce a cell, and how to validate the result.
|
|
9
|
+
*/
|
|
10
|
+
export interface ImportColumn {
|
|
11
|
+
/** Header text to match against, and the form label used in error messages. */
|
|
12
|
+
title: string;
|
|
13
|
+
/** The record property this column writes. */
|
|
14
|
+
dataIndex: string;
|
|
15
|
+
/** Drives value coercion (numbers, dates, enums, ...). */
|
|
16
|
+
fieldType?: FieldType;
|
|
17
|
+
/** Labels for `enum` columns, so an import accepts the exported label too. */
|
|
18
|
+
enumOptions?: Record<string, EnumOption>;
|
|
19
|
+
/** Resolved validation rules (registry rules + the column's own), as the form uses. */
|
|
20
|
+
rules?: FormRule[];
|
|
21
|
+
}
|
|
22
|
+
/** A file parsed into a header row and the data rows beneath it. */
|
|
23
|
+
export interface ParsedTable {
|
|
24
|
+
/** The first row, treated as headers. */
|
|
25
|
+
headers: string[];
|
|
26
|
+
/** Every subsequent row, as raw strings. */
|
|
27
|
+
rows: string[][];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Header-to-column mapping: for each header position, the `dataIndex` it feeds,
|
|
31
|
+
* or `null` to ignore that column.
|
|
32
|
+
*/
|
|
33
|
+
export type ColumnMapping = (string | null)[];
|
|
34
|
+
/** One data row prepared for creation. */
|
|
35
|
+
export interface PreparedRow {
|
|
36
|
+
/** 1-based row number in the file body (excludes the header), for reporting. */
|
|
37
|
+
rowNumber: number;
|
|
38
|
+
/** The coerced record draft. */
|
|
39
|
+
record: Record<string, unknown>;
|
|
40
|
+
/** Per-field validation errors; empty when the row is valid. */
|
|
41
|
+
errors: {
|
|
42
|
+
field: string;
|
|
43
|
+
message: string;
|
|
44
|
+
}[];
|
|
45
|
+
}
|
|
46
|
+
/** The outcome of creating one prepared row. */
|
|
47
|
+
export interface ImportRowOutcome {
|
|
48
|
+
rowNumber: number;
|
|
49
|
+
ok: boolean;
|
|
50
|
+
/** Failure reason: a validation summary or the create error message. */
|
|
51
|
+
error?: string;
|
|
52
|
+
}
|
|
53
|
+
/** The result of an import run. */
|
|
54
|
+
export interface ImportResult {
|
|
55
|
+
/** Rows created successfully. */
|
|
56
|
+
created: number;
|
|
57
|
+
/** Rows that failed validation. */
|
|
58
|
+
invalid: number;
|
|
59
|
+
/** Rows that failed to create despite being valid. */
|
|
60
|
+
failed: number;
|
|
61
|
+
/** Per-row outcomes, in file order. */
|
|
62
|
+
outcomes: ImportRowOutcome[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Parse CSV text into rows of string fields. Handles quoted fields, escaped
|
|
66
|
+
* quotes (`""`), embedded commas and newlines, `\r\n`/`\n` endings and a
|
|
67
|
+
* leading UTF-8 BOM.
|
|
68
|
+
*/
|
|
69
|
+
export declare const parseCsv: (text: string, delimiter?: string) => string[][];
|
|
70
|
+
/** Split a grid into a header row plus data rows. */
|
|
71
|
+
export declare const toParsedTable: (grid: string[][]) => ParsedTable;
|
|
72
|
+
/** A parser turns raw file bytes/text into a {@link ParsedTable}. */
|
|
73
|
+
export type FileParser = (input: string | ArrayBuffer | Uint8Array) => ParsedTable | Promise<ParsedTable>;
|
|
74
|
+
/**
|
|
75
|
+
* Registered parsers per format. CSV is built in; `xls` and `xlsx` are
|
|
76
|
+
* registered by their own modules so each format ships independently.
|
|
77
|
+
*/
|
|
78
|
+
export declare const fileParsers: Partial<Record<ImportFormat, FileParser>>;
|
|
79
|
+
/** Register a parser for a format (used by the xls/xlsx additions). */
|
|
80
|
+
export declare const registerFileParser: (format: ImportFormat, parser: FileParser) => void;
|
|
81
|
+
/** Decode raw bytes as UTF-8 text. */
|
|
82
|
+
export declare const decodeText: (input: ArrayBuffer | Uint8Array) => string;
|
|
83
|
+
/** Infer the import format from a filename extension. */
|
|
84
|
+
export declare const detectFormat: (filename: string) => ImportFormat | undefined;
|
|
85
|
+
/** Parse a file's contents into a {@link ParsedTable} using the registered parser. */
|
|
86
|
+
export declare const parseTable: (input: string | ArrayBuffer | Uint8Array, format: ImportFormat) => Promise<ParsedTable>;
|
|
87
|
+
/**
|
|
88
|
+
* Suggest a header-to-column mapping by matching header text against each
|
|
89
|
+
* column's title (then its `dataIndex`), case- and punctuation-insensitively.
|
|
90
|
+
* Password columns are never targeted. Unmatched headers map to `null`.
|
|
91
|
+
*/
|
|
92
|
+
export declare const autoMapColumns: (headers: string[], columns: ImportColumn[]) => ColumnMapping;
|
|
93
|
+
/**
|
|
94
|
+
* Coerce one raw cell into the record shape for a column. Throws when the
|
|
95
|
+
* field's own conversion throws (e.g. malformed JSON), which the caller records
|
|
96
|
+
* as a per-row error.
|
|
97
|
+
*/
|
|
98
|
+
export declare const coerceValue: (raw: string, column: ImportColumn) => unknown;
|
|
99
|
+
/**
|
|
100
|
+
* Evaluate the resolved rules for one value, returning the first failure
|
|
101
|
+
* message or `null`. Supports the rule shapes the registry and `formConfig`
|
|
102
|
+
* produce: `required`, `type: 'email' | 'url'`, `pattern`, `len`/`min`/`max`,
|
|
103
|
+
* and custom async `validator`s (which is how the `json` type validates).
|
|
104
|
+
*/
|
|
105
|
+
export declare const evaluateRules: (rules: FormRule[] | undefined, value: unknown, label: string) => Promise<string | null>;
|
|
106
|
+
/**
|
|
107
|
+
* Turn parsed rows into validated record drafts using the given mapping.
|
|
108
|
+
* Password columns are excluded even if mapped. Cells that coerce to blank are
|
|
109
|
+
* omitted from the draft so `required` rules see a missing value.
|
|
110
|
+
*/
|
|
111
|
+
export declare const prepareRows: (table: ParsedTable, mapping: ColumnMapping, columns: ImportColumn[]) => Promise<PreparedRow[]>;
|
|
112
|
+
/** How the importer creates rows: one at a time, or all at once server-side. */
|
|
113
|
+
export interface ImportSink {
|
|
114
|
+
/** Create one record. */
|
|
115
|
+
create: (draft: Record<string, unknown>) => Promise<unknown>;
|
|
116
|
+
/** Optional batch create for sources that can do it server-side. */
|
|
117
|
+
createMany?: (drafts: Record<string, unknown>[]) => Promise<unknown>;
|
|
118
|
+
}
|
|
119
|
+
/** Options for {@link runImport}. */
|
|
120
|
+
export interface RunImportOptions {
|
|
121
|
+
/** Max simultaneous single-row creates. Defaults to 5. */
|
|
122
|
+
concurrency?: number;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create the valid prepared rows, reporting each row's outcome. Invalid rows are
|
|
126
|
+
* never sent. Uses `createMany` when the sink offers it; otherwise creates with
|
|
127
|
+
* a bounded pool so a large file does not fire thousands of simultaneous
|
|
128
|
+
* requests. Partial success is honoured: a failing row does not abort the rest.
|
|
129
|
+
*/
|
|
130
|
+
export declare const runImport: (prepared: PreparedRow[], sink: ImportSink, options?: RunImportOptions) => Promise<ImportResult>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ParsedTable } from './importData';
|
|
2
|
+
/**
|
|
3
|
+
* Parse SpreadsheetML text into a grid of string cells (first Worksheet only).
|
|
4
|
+
* `ss:Index` (1-based) on a row or cell is honoured so sparse files stay aligned.
|
|
5
|
+
*/
|
|
6
|
+
export declare const parseSpreadsheetML: (xml: string) => string[][];
|
|
7
|
+
/** Parse SpreadsheetML content (text or bytes) into a {@link ParsedTable}. */
|
|
8
|
+
export declare const parseSpreadsheetMLTable: (input: string | ArrayBuffer | Uint8Array) => ParsedTable;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ParsedTable } from './importData';
|
|
2
|
+
/**
|
|
3
|
+
* Parse an `.xlsx` workbook's first sheet into a {@link ParsedTable}.
|
|
4
|
+
* Accepts the raw bytes (`ArrayBuffer`/`Uint8Array`) the import dialog reads
|
|
5
|
+
* from the chosen file.
|
|
6
|
+
*/
|
|
7
|
+
export declare const parseXlsx: (input: string | ArrayBuffer | Uint8Array) => Promise<ParsedTable>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "antd-crud-table",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/maifeeulasad/antd-crud-table",
|
|
7
7
|
"repository": {
|
|
@@ -100,6 +100,9 @@
|
|
|
100
100
|
"sideEffects": [
|
|
101
101
|
"*.css"
|
|
102
102
|
],
|
|
103
|
+
"dependencies": {
|
|
104
|
+
"to-spreadsheet": "^1.4.0"
|
|
105
|
+
},
|
|
103
106
|
"scripts": {
|
|
104
107
|
"dev": "vite",
|
|
105
108
|
"build:demo": "tsc -b && vite build",
|
|
@@ -114,6 +117,8 @@
|
|
|
114
117
|
"test": "vitest run",
|
|
115
118
|
"test:watch": "vitest",
|
|
116
119
|
"test:coverage": "vitest run --coverage",
|
|
120
|
+
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
121
|
+
"backend": "node --experimental-strip-types backend/index.ts",
|
|
117
122
|
"preview": "vite preview"
|
|
118
123
|
}
|
|
119
124
|
}
|