@carefully-built/crud 0.1.2 → 0.1.3

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/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { ActionHandlers, ActionType, Column, FilterConfig, ResponsiveSheetClassNames, SortState } from "@carefully-built/ui";
1
+ import { ActionHandlers, ActionType, Column, FilterConfig, ResponsiveSheetClassNames, SheetOutsideInteractionGuard, SortState } from "@carefully-built/ui";
2
2
  import { ReactNode } from "react";
3
3
 
4
4
  //#region src/pagination.d.ts
@@ -18,6 +18,7 @@ interface CrudFilterDefinition<TItem> {
18
18
  readonly key: Extract<keyof TItem, string>;
19
19
  readonly config: FilterConfig;
20
20
  readonly allowAll?: boolean;
21
+ readonly allOptionLabel?: string;
21
22
  readonly clearable?: boolean;
22
23
  }
23
24
  interface UseCrudTableStateOptions<TItem> {
@@ -125,6 +126,7 @@ interface CrudResourceSheetProps {
125
126
  readonly confirmLoading?: boolean;
126
127
  readonly confirmCloseWhenDirty?: boolean;
127
128
  readonly width?: number;
129
+ readonly outsideInteractionGuard?: SheetOutsideInteractionGuard;
128
130
  readonly className?: string;
129
131
  readonly contentClassName?: string;
130
132
  readonly footerClassName?: string;
@@ -135,6 +137,7 @@ declare function CrudResourceSheet({
135
137
  formId,
136
138
  onConfirm,
137
139
  confirmCloseWhenDirty: _confirmCloseWhenDirty,
140
+ outsideInteractionGuard,
138
141
  ...sheetProps
139
142
  }: CrudResourceSheetProps): React.ReactElement;
140
143
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/pagination.ts","../src/types.ts","../src/crud-data-table.tsx","../src/crud-list-table.tsx","../src/crud-resource-sheet.tsx","../src/crud-table-view.tsx","../src/use-crud-table-state.ts","../src/use-url-string-filters.ts","../src/use-url-pagination.ts"],"sourcesContent":[],"mappings":";;;;UAAiB,mBAAA;;;;EAAA,SAAA,QAAA,EAAA,MAAmB;;;;ACKpC;;;KAAY,cAAA;ADLK,UCOA,oBDPmB,CAAA,KAAA,CAAA,CAAA;gBCQpB,cAAc;mBACX;;EAJP,SAAA,SAAc,CAAA,EAAA,OAAA;AAE1B;AAC8B,UAMb,wBANa,CAAA,KAAA,CAAA,CAAA;EAAd,SAAA,IAAA,EAAA,SAOU,KAPV,EAAA;EACG,SAAA,OAAA,EAAA,SAOU,MAPV,CAOiB,KAPjB,CAAA,EAAA;EAAY,SAAA,YAAA,CAAA,EAAA,SAQI,OARJ,CAAA,MAQkB,KARlB,EAAA,MAAA,CAAA,EAAA;EAKd,SAAA,OAAA,CAAA,EAAA,SAIa,oBAJW,CAIU,KAJV,CAAA,EAAA;EACf,SAAA,QAAA,CAAA,EAAA,MAAA;EACU,SAAA,gBAAA,CAAA,EAIN,SAJM;;AACa,UAMhC,cANgC,CAAA,KAAA,CAAA,CAAA;EAAd,SAAA,YAAA,EAOV,KAPU,EAAA;EACgB,SAAA,UAAA,EAO5B,KAP4B,EAAA;EAArB,SAAA,aAAA,EAQJ,KARI,EAAA;EAEA,SAAA,MAAA,EAAA,MAAA;EAAS,SAAA,SAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAGtB,SAAA,OAAA,EAMG,MANW,CAAA,MAAA,EAAA,MAAA,CAAA;EACN,SAAA,SAAA,EAAA,CAAA,GAAA,EAAA,MAAA,EAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EACF,SAAA,QAAA,EAAA,GAAA,GAAA,IAAA;EACG,SAAA,yBAAA,EAAA,CAAA,WAAA,EAOT,MAPS,CAAA,MAAA,EAAA,MAAA,CAAA,EAAA,GAAA,MAAA;EAGN,SAAA,SAAA,EAAA,OAAA;EAIH,SAAA,UAAA,EAAA,OAAA;EAIM,SAAA,UAAA,EAAA,cAAA;EACD,SAAA,SAAA,EAAA,SAAA;EACW,SAAA,YAAA,EAAA,CAAA,KAAA,EAAA,SAAA,EAAA,GAAA,IAAA;EACV,SAAA,UAAA,EAAA,mBAAA;;AAGN,UAAA,kBAAkB,CAAA,KAAA,CAAA,CAAA;EACF,SAAA,KAAA,EAAf,cAAe,CAAA,KAAA,CAAA;EAAf,SAAA,OAAA,EAAA,SACW,MADX,CACkB,KADlB,CAAA,EAAA;EACkB,SAAA,SAAA,EAAA,OAAA;EAAP,SAAA,iBAAA,CAAA,EAAA,MAAA;EAGsB,SAAA,OAAA,CAAA,EAAA,SAArB,oBAAqB,CAAA,KAAA,CAAA,EAAA;EAArB,SAAA,OAAA,CAAA,EAAA,SACA,UADA,EAAA;EACA,SAAA,cAAA,CAAA,EACF,cADE,CACa,KADb,CAAA;EACa,SAAA,aAAA,CAAA,EAAA,CAAA,IAAA,EACT,KADS,EAAA,GACC,SADD;EAAf,SAAA,aAAA,CAAA,EAAA,MAAA;EACM,SAAA,mBAAA,CAAA,EAED,SAFC;EAAU,SAAA,gBAAA,CAAA,EAGd,SAHc;EAEX,SAAA,SAAA,CAAA,EAAA,CAAA,IAAA,EAEH,KAFG,EAAA,GAAA,MAAA,GAAA,MAAA;EACH,SAAA,UAAA,CAAA,EAAA,CAAA,IAAA,EAEC,KAFD,EAAA,GAAA,IAAA;EACA,SAAA,gBAAA,CAAA,EAAA,CAAA,IAAA,EAEO,KAFP,EAAA,GAEiB,SAFjB;EACC,SAAA,YAAA,CAAA,EAAA,OAAA;EACM,SAAA,UAAA,CAAA,EAAA,OAAA;EAAU,SAAA,SAAA,CAAA,EAAA,MAAA;;AAM9B,UAAA,kBAAkB,CAAA,KAAA,CAAA,CAAA;EACT,SAAA,IAAA,EAAA,SAAA,KAAA,EAAA;EACU,SAAA,OAAA,EAAA,SAAP,MAAO,CAAA,KAAA,CAAA,EAAA;EAAP,SAAA,SAAA,EAAA,OAAA;EAEC,SAAA,OAAA,CAAA,EAAA,SAAA,UAAA,EAAA;EACa,SAAA,cAAA,CAAA,EAAf,cAAe,CAAA,KAAA,CAAA;EAAf,SAAA,aAAA,CAAA,EAAA,CAAA,IAAA,EACM,KADN,EAAA,GACgB,SADhB;EACM,SAAA,aAAA,CAAA,EAAA,MAAA;EAAU,SAAA,aAAA,CAAA,EAEjB,SAFiB;EAEjB,SAAA,SAAA,CAAA,EAAA,CAAA,IAAA,EACG,KADH,EAAA,GAAA,MAAA,GAAA,MAAA;EACG,SAAA,UAAA,CAAA,EAAA,CAAA,IAAA,EACC,KADD,EAAA,GAAA,IAAA;EACC,SAAA,gBAAA,CAAA,EAAA,CAAA,IAAA,EACM,KADN,EAAA,GACgB,SADhB;EACM,SAAA,YAAA,CAAA,EAAA,OAAA;EAAU,SAAA,UAAA,CAAA,EAAA,OAAA;EAIxB,SAAA,SAAA,CAAA,EAAA,MAAA;EACW,SAAA,SAAA,CAAA,EADX,SACW;EACV,SAAA,YAAA,CAAA,EAAA,CAAA,KAAA,EADU,SACV,EAAA,GAAA,IAAA;EAAmB,SAAA,UAAA,CAAA,EAAnB,mBAAmB;;;;iBCxE3B;;;;;;;;;;;;;;;;;;GAkBb,mBAAmB,SAAS,KAAA,CAAM;;;KCrBzB,2CAA2C,mBAAmB;iBAE1D,2CACP,mBAAmB,SACzB,KAAA,CAAM;;;UCFQ,sBAAA;;EJPA,SAAA,YAAA,EAAmB,CAAA,IAAA,EAAA,OAAA,EAAA,GAAA,IAAA;kBIUlB;qBACG;;EHNT,SAAA,WAAc,CAAA,EGQD,SHRC;EAET,SAAA,QAAA,CAAA,EAAA,GAAoB,GAAA,IAAA;EACP,SAAA,WAAA,CAAA,EGOL,SHPK;EAAd,SAAA,SAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EACG,SAAA,YAAA,CAAA,EGQO,SHRP;EAAY,SAAA,eAAA,CAAA,EAAA,OAAA;EAKd,SAAA,cAAA,CAAA,EAAA,OAAwB;EACf,SAAA,qBAAA,CAAA,EAAA,OAAA;EACU,SAAA,KAAA,CAAA,EAAA,MAAA;EAAP,SAAA,SAAA,CAAA,EAAA,MAAA;EACoB,SAAA,gBAAA,CAAA,EAAA,MAAA;EAAd,SAAA,eAAA,CAAA,EAAA,MAAA;EACgB,SAAA,OAAA,CAAA,EGO9B,yBHP8B;;AAErB,iBGQd,iBAAA,CHRc;EAAA,QAAA;EAAA,MAAA;EAAA,SAAA;EAAA,qBAAA,EGYL,sBHZK;EAAA,GAAA;AAAA,CAAA,EGc3B,sBHd2B,CAAA,EGcF,KAAA,CAAM,YHdJ;;;iBIdd;;;;;;;;;;;;;;;;;;GAkBb,mBAAmB,SAAS,KAAA,CAAM;;;iBC2BrB;;;;WAIL;;;GAGR,yBAAyB,SAAS,eAAe;;;UCrDnC;gBACD;;;EPNC,SAAA,UAAA,CAAA,EAAmB,MAAA;;KOY/B,oDAAoD,0DAC9B,wBAAwB,6BNRnD;AAEiB,UMSA,qBNToB,CAAA,qBAAA,SMUL,yBNVK,EAAA,CAAA,CAAA;EACP,SAAA,MAAA,EMWX,qBNXW,CMWW,YNXX,CAAA;EAAd,SAAA,QAAA,EAAA,CAAA,GAAA,EMYW,YNZX,CAAA,MAAA,CAAA,CAAA,KAAA,CAAA,EAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EACG,SAAA,KAAA,EAAA,GAAA,GAAA,IAAA;EAAY,SAAA,cAAA,EAAA,CAAA,WAAA,EMcd,MNdc,CAAA,MAAA,EAAA,MAAA,CAAA,EAAA,GMexB,qBNfwB,CMeF,YNfE,CAAA;AAK/B;AAC0B,iBMkCV,mBNlCU,CAAA,2BAAA,SMmCY,yBNnCZ,EAAA,CAAA,CAAA,WAAA,EMoCX,YNpCW,CAAA,EMoCI,qBNpCJ,CMoC0B,YNpC1B,CAAA;;;UORT,uBAAA;;;ERPA,SAAA,SAAA,CAAA,EAAA,MAAmB;;UQanB,kBAAA,SAA2B;;EPRhC,SAAA,WAAc,EAAA,OAAA;EAET,SAAA,QAAA,EAAA,CAAA,IAAoB,EAAA,MAAA,EAAA,GAAA,IAAA;EACP,SAAA,QAAA,EAAA,GAAA,GAAA,IAAA;EAAd,SAAA,QAAA,EAAA,GAAA,GAAA,IAAA;EACG,SAAA,SAAA,EAAA,GAAA,GAAA,IAAA;EAAY,SAAA,QAAA,EAAA,GAAA,GAAA,IAAA;EAKd,SAAA,QAAA,EAAA,CAAA,CAAA,CAAA,CAAA,IAAwB,EAAA,SOOD,CPPC,EAAA,EAAA,GOOO,CPPP,EAAA;;AAEL,iBOQpB,gBAAA,CPRoB;EAAA,UAAA;EAAA,QAAA;EAAA;AAAA,CAAA,EOYjC,uBPZiC,CAAA,EOYP,kBPZO"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/pagination.ts","../src/types.ts","../src/crud-data-table.tsx","../src/crud-list-table.tsx","../src/crud-resource-sheet.tsx","../src/crud-table-view.tsx","../src/use-crud-table-state.ts","../src/use-url-string-filters.ts","../src/use-url-pagination.ts"],"sourcesContent":[],"mappings":";;;;UAAiB,mBAAA;;;;EAAA,SAAA,QAAA,EAAA,MAAmB;;;;ACKpC;;;KAAY,cAAA;ADLK,UCOA,oBDPmB,CAAA,KAAA,CAAA,CAAA;gBCQpB,cAAc;mBACX;;EAJP,SAAA,cAAc,CAAA,EAAA,MAAA;EAET,SAAA,SAAA,CAAA,EAAA,OAAoB;;AACrB,UAOC,wBAPD,CAAA,KAAA,CAAA,CAAA;EACG,SAAA,IAAA,EAAA,SAOO,KAPP,EAAA;EAAY,SAAA,OAAA,EAAA,SAQF,MARE,CAQK,KARL,CAAA,EAAA;EAMd,SAAA,YAAA,CAAA,EAAA,SAGkB,OAHM,CAAA,MAGQ,KAHR,EAAA,MAAA,CAAA,EAAA;EACf,SAAA,OAAA,CAAA,EAAA,SAGI,oBAHJ,CAGyB,KAHzB,CAAA,EAAA;EACU,SAAA,QAAA,CAAA,EAAA,MAAA;EAAP,SAAA,gBAAA,CAAA,EAIC,SAJD;;AACM,UAMlB,cANkB,CAAA,KAAA,CAAA,CAAA;EACgB,SAAA,YAAA,EAM1B,KAN0B,EAAA;EAArB,SAAA,UAAA,EAOP,KAPO,EAAA;EAEA,SAAA,aAAA,EAMJ,KANI,EAAA;EAAS,SAAA,MAAA,EAAA,MAAA;EAGtB,SAAA,SAAc,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EACN,SAAA,OAAA,EAKL,MALK,CAAA,MAAA,EAAA,MAAA,CAAA;EACF,SAAA,SAAA,EAAA,CAAA,GAAA,EAAA,MAAA,EAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EACG,SAAA,QAAA,EAAA,GAAA,GAAA,IAAA;EAGN,SAAA,yBAAA,EAAA,CAAA,WAAA,EAIH,MAJG,CAAA,MAAA,EAAA,MAAA,CAAA,EAAA,GAAA,MAAA;EAIH,SAAA,SAAA,EAAA,OAAA;EAIM,SAAA,UAAA,EAAA,OAAA;EACD,SAAA,UAAA,EADC,cACD;EACW,SAAA,SAAA,EADX,SACW;EACV,SAAA,YAAA,EAAA,CAAA,KAAA,EADU,SACV,EAAA,GAAA,IAAA;EAAmB,SAAA,UAAA,EAAnB,mBAAmB;AAG1C;AACiC,UADhB,kBACgB,CAAA,KAAA,CAAA,CAAA;EAAf,SAAA,KAAA,EAAA,cAAA,CAAe,KAAf,CAAA;EACkB,SAAA,OAAA,EAAA,SAAP,MAAO,CAAA,KAAA,CAAA,EAAA;EAAP,SAAA,SAAA,EAAA,OAAA;EAGsB,SAAA,iBAAA,CAAA,EAAA,MAAA;EAArB,SAAA,OAAA,CAAA,EAAA,SAAA,oBAAA,CAAqB,KAArB,CAAA,EAAA;EACA,SAAA,OAAA,CAAA,EAAA,SAAA,UAAA,EAAA;EACa,SAAA,cAAA,CAAA,EAAf,cAAe,CAAA,KAAA,CAAA;EAAf,SAAA,aAAA,CAAA,EAAA,CAAA,IAAA,EACM,KADN,EAAA,GACgB,SADhB;EACM,SAAA,aAAA,CAAA,EAAA,MAAA;EAAU,SAAA,mBAAA,CAAA,EAEX,SAFW;EAEX,SAAA,gBAAA,CAAA,EACH,SADG;EACH,SAAA,SAAA,CAAA,EAAA,CAAA,IAAA,EACA,KADA,EAAA,GAAA,MAAA,GAAA,MAAA;EACA,SAAA,UAAA,CAAA,EAAA,CAAA,IAAA,EACC,KADD,EAAA,GAAA,IAAA;EACC,SAAA,gBAAA,CAAA,EAAA,CAAA,IAAA,EACM,KADN,EAAA,GACgB,SADhB;EACM,SAAA,YAAA,CAAA,EAAA,OAAA;EAAU,SAAA,UAAA,CAAA,EAAA,OAAA;EAAS,SAAA,SAAA,CAAA,EAAA,MAAA;AAMxD;AAC0B,UADT,kBACS,CAAA,KAAA,CAAA,CAAA;EACU,SAAA,IAAA,EAAA,SADV,KACU,EAAA;EAAP,SAAA,OAAA,EAAA,SAAA,MAAA,CAAO,KAAP,CAAA,EAAA;EAEC,SAAA,SAAA,EAAA,OAAA;EACa,SAAA,OAAA,CAAA,EAAA,SADb,UACa,EAAA;EAAf,SAAA,cAAA,CAAA,EAAA,cAAA,CAAe,KAAf,CAAA;EACM,SAAA,aAAA,CAAA,EAAA,CAAA,IAAA,EAAA,KAAA,EAAA,GAAU,SAAV;EAAU,SAAA,aAAA,CAAA,EAAA,MAAA;EAEjB,SAAA,aAAA,CAAA,EAAA,SAAA;EACG,SAAA,SAAA,CAAA,EAAA,CAAA,IAAA,EAAA,KAAA,EAAA,GAAA,MAAA,GAAA,MAAA;EACC,SAAA,UAAA,CAAA,EAAA,CAAA,IAAA,EAAA,KAAA,EAAA,GAAA,IAAA;EACM,SAAA,gBAAA,CAAA,EAAA,CAAA,IAAA,EAAA,KAAA,EAAA,GAAU,SAAV;EAAU,SAAA,YAAA,CAAA,EAAA,OAAA;EAIxB,SAAA,UAAA,CAAA,EAAA,OAAA;EACW,SAAA,SAAA,CAAA,EAAA,MAAA;EACV,SAAA,SAAA,CAAA,EAFD,SAEC;EAAmB,SAAA,YAAA,CAAA,EAAA,CAAA,KAAA,EADT,SACS,EAAA,GAAA,IAAA;wBAAnB;;;;iBCzER;;;;;;;;;;;;;;;;;;GAkBb,mBAAmB,SAAS,KAAA,CAAM;;;KCrBzB,2CAA2C,mBAAmB;iBAE1D,2CACP,mBAAmB,SACzB,KAAA,CAAM;;;UCFQ,sBAAA;;EJPA,SAAA,YAAA,EAAmB,CAAA,IAAA,EAAA,OAAA,EAAA,GAAA,IAAA;kBIUlB;qBACG;;EHNT,SAAA,WAAc,CAAA,EGQD,SHRC;EAET,SAAA,QAAA,CAAA,EAAA,GAAoB,GAAA,IAAA;EACP,SAAA,WAAA,CAAA,EGOL,SHPK;EAAd,SAAA,SAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EACG,SAAA,YAAA,CAAA,EGQO,SHRP;EAAY,SAAA,eAAA,CAAA,EAAA,OAAA;EAMd,SAAA,cAAA,CAAA,EAAA,OAAwB;EACf,SAAA,qBAAA,CAAA,EAAA,OAAA;EACU,SAAA,KAAA,CAAA,EAAA,MAAA;EAAP,SAAA,uBAAA,CAAA,EGKQ,4BHLR;EACoB,SAAA,SAAA,CAAA,EAAA,MAAA;EAAd,SAAA,gBAAA,CAAA,EAAA,MAAA;EACgB,SAAA,eAAA,CAAA,EAAA,MAAA;EAArB,SAAA,OAAA,CAAA,EGOT,yBHPS;;AAES,iBGQvB,iBAAA,CHRuB;EAAA,QAAA;EAAA,MAAA;EAAA,SAAA;EAAA,qBAAA,EGYd,sBHZc;EAAA,uBAAA;EAAA,GAAA;AAAA,CAAA,EGepC,sBHfoC,CAAA,EGeX,KAAA,CAAM,YHfK;;;iBIfvB;;;;;;;;;;;;;;;;;;GAkBb,mBAAmB,SAAS,KAAA,CAAM;;;iBC4BrB;;;;WAIL;;;GAGR,yBAAyB,SAAS,eAAe;;;UCtDnC;gBACD;;;EPNC,SAAA,UAAA,CAAA,EAAmB,MAAA;;KOY/B,oDAAoD,0DAC9B,wBAAwB,6BNRnD;AAEiB,UMSA,qBNToB,CAAA,qBAAA,SMUL,yBNVK,EAAA,CAAA,CAAA;EACP,SAAA,MAAA,EMWX,qBNXW,CMWW,YNXX,CAAA;EAAd,SAAA,QAAA,EAAA,CAAA,GAAA,EMYW,YNZX,CAAA,MAAA,CAAA,CAAA,KAAA,CAAA,EAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EACG,SAAA,KAAA,EAAA,GAAA,GAAA,IAAA;EAAY,SAAA,cAAA,EAAA,CAAA,WAAA,EMcd,MNdc,CAAA,MAAA,EAAA,MAAA,CAAA,EAAA,GMexB,qBNfwB,CMeF,YNfE,CAAA;AAM/B;AAC0B,iBMiCV,mBNjCU,CAAA,2BAAA,SMkCY,yBNlCZ,EAAA,CAAA,CAAA,WAAA,EMmCX,YNnCW,CAAA,EMmCI,qBNnCJ,CMmC0B,YNnC1B,CAAA;;;UOTT,uBAAA;;;ERPA,SAAA,SAAA,CAAA,EAAA,MAAmB;;UQanB,kBAAA,SAA2B;;EPRhC,SAAA,WAAc,EAAA,OAAA;EAET,SAAA,QAAA,EAAA,CAAA,IAAoB,EAAA,MAAA,EAAA,GAAA,IAAA;EACP,SAAA,QAAA,EAAA,GAAA,GAAA,IAAA;EAAd,SAAA,QAAA,EAAA,GAAA,GAAA,IAAA;EACG,SAAA,SAAA,EAAA,GAAA,GAAA,IAAA;EAAY,SAAA,QAAA,EAAA,GAAA,GAAA,IAAA;EAMd,SAAA,QAAA,EAAA,CAAA,CAAA,CAAA,CAAA,IAAwB,EAAA,SOMD,CPNC,EAAA,EAAA,GOMO,CPNP,EAAA;;AAEL,iBOOpB,gBAAA,CPPoB;EAAA,UAAA;EAAA,QAAA;EAAA;AAAA,CAAA,EOWjC,uBPXiC,CAAA,EOWP,kBPXO"}
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ResponsiveSheet, SmartTable, TableToolbar, useTableSorting } from "@carefully-built/ui";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { useCallback, useMemo, useState } from "react";
3
+ import { useCallback, useMemo } from "react";
4
4
  import { parseAsInteger, parseAsString, useQueryState, useQueryStates } from "nuqs";
5
5
 
6
6
  //#region src/crud-data-table.tsx
@@ -34,7 +34,7 @@ function CrudListTable(props) {
34
34
 
35
35
  //#endregion
36
36
  //#region src/crud-resource-sheet.tsx
37
- function CrudResourceSheet({ children, formId, onConfirm, confirmCloseWhenDirty: _confirmCloseWhenDirty, ...sheetProps }) {
37
+ function CrudResourceSheet({ children, formId, onConfirm, confirmCloseWhenDirty: _confirmCloseWhenDirty, outsideInteractionGuard, ...sheetProps }) {
38
38
  const submitForm = () => {
39
39
  const form = formId ? document.getElementById(formId) : null;
40
40
  if (form instanceof HTMLFormElement) {
@@ -45,6 +45,7 @@ function CrudResourceSheet({ children, formId, onConfirm, confirmCloseWhenDirty:
45
45
  };
46
46
  return /* @__PURE__ */ jsx(ResponsiveSheet, {
47
47
  ...sheetProps,
48
+ outsideInteractionGuard,
48
49
  onConfirm: formId || onConfirm ? submitForm : void 0,
49
50
  children
50
51
  });
@@ -69,6 +70,7 @@ function CrudTableView({ state, columns, isLoading, searchPlaceholder = "Search.
69
70
  state.setFilter(filter.key, value);
70
71
  },
71
72
  allowAll: filter.allowAll,
73
+ allOptionLabel: filter.allOptionLabel,
72
74
  clearable: filter.clearable
73
75
  })),
74
76
  onClearAll: state.clearAll,
@@ -110,13 +112,104 @@ function matchesCrudSearch(searchText, query) {
110
112
  }
111
113
 
112
114
  //#endregion
113
- //#region src/pagination.ts
114
- function getValidPage(page, totalPages) {
115
- return Math.min(Math.max(1, page), totalPages);
116
- }
117
- function paginateCrudData(data, currentPage, pageSize) {
115
+ //#region src/use-url-pagination.ts
116
+ function useUrlPagination({ totalItems, pageSize = 20, pageParam = "page" }) {
117
+ const [page, setPage] = useQueryState(pageParam, parseAsInteger.withDefault(1));
118
+ const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
119
+ const currentPage = Math.min(Math.max(1, page), totalPages);
118
120
  const startIndex = (currentPage - 1) * pageSize;
119
- return data.slice(startIndex, startIndex + pageSize);
121
+ const endIndex = Math.min(startIndex + pageSize, totalItems);
122
+ const hasPrevPage = currentPage > 1;
123
+ const hasNextPage = currentPage < totalPages;
124
+ const goToPage = useCallback((newPage) => {
125
+ const validPage = Math.min(Math.max(1, newPage), totalPages);
126
+ setPage(validPage === 1 ? null : validPage);
127
+ }, [setPage, totalPages]);
128
+ const nextPage = useCallback(() => {
129
+ if (hasNextPage) goToPage(currentPage + 1);
130
+ }, [
131
+ currentPage,
132
+ goToPage,
133
+ hasNextPage
134
+ ]);
135
+ const prevPage = useCallback(() => {
136
+ if (hasPrevPage) goToPage(currentPage - 1);
137
+ }, [
138
+ currentPage,
139
+ goToPage,
140
+ hasPrevPage
141
+ ]);
142
+ const firstPage = useCallback(() => {
143
+ goToPage(1);
144
+ }, [goToPage]);
145
+ const lastPage = useCallback(() => {
146
+ goToPage(totalPages);
147
+ }, [goToPage, totalPages]);
148
+ const paginate = useCallback((data) => data.slice(startIndex, endIndex), [endIndex, startIndex]);
149
+ return useMemo(() => ({
150
+ currentPage,
151
+ pageSize,
152
+ totalPages,
153
+ totalItems,
154
+ startIndex,
155
+ endIndex,
156
+ hasPrevPage,
157
+ hasNextPage,
158
+ goToPage,
159
+ nextPage,
160
+ prevPage,
161
+ firstPage,
162
+ lastPage,
163
+ paginate,
164
+ onPageChange: goToPage
165
+ }), [
166
+ currentPage,
167
+ endIndex,
168
+ firstPage,
169
+ goToPage,
170
+ hasNextPage,
171
+ hasPrevPage,
172
+ lastPage,
173
+ nextPage,
174
+ pageSize,
175
+ paginate,
176
+ prevPage,
177
+ startIndex,
178
+ totalItems,
179
+ totalPages
180
+ ]);
181
+ }
182
+
183
+ //#endregion
184
+ //#region src/use-url-string-filters.ts
185
+ function buildParserMap(definitions) {
186
+ return Object.fromEntries(definitions.map((definition) => [definition.param ?? definition.key, parseAsString.withDefault(definition.defaultValue ?? "all")]));
187
+ }
188
+ function normalizeFilterValue(value, definition) {
189
+ if (value === (definition.clearValue ?? definition.defaultValue ?? "all")) return null;
190
+ return value.length > 0 ? value : null;
191
+ }
192
+ function useUrlStringFilters(definitions) {
193
+ const [params, setParams] = useQueryStates(useMemo(() => buildParserMap(definitions), [definitions]));
194
+ const values = useMemo(() => Object.fromEntries(definitions.map((definition) => {
195
+ const param = definition.param ?? definition.key;
196
+ return [definition.key, params[param] ?? definition.defaultValue ?? "all"];
197
+ })), [definitions, params]);
198
+ return {
199
+ values,
200
+ setValue: useCallback((key, value) => {
201
+ const definition = definitions.find((item) => item.key === key);
202
+ if (!definition) return;
203
+ setParams({ [definition.param ?? definition.key]: normalizeFilterValue(value, definition) });
204
+ }, [definitions, setParams]),
205
+ clear: useCallback(() => {
206
+ setParams(Object.fromEntries(definitions.map((definition) => [definition.param ?? definition.key, null])));
207
+ }, [definitions, setParams]),
208
+ getDraftValues: useCallback((draftValues) => ({
209
+ ...values,
210
+ ...draftValues
211
+ }), [values])
212
+ };
120
213
  }
121
214
 
122
215
  //#endregion
@@ -139,45 +232,49 @@ function filterCrudData(data, search, filters, searchFields) {
139
232
  });
140
233
  }
141
234
  function useCrudTableState({ data, columns, searchFields = [], filters: filterDefinitions = [], pageSize = 20, initialSortState = null }) {
142
- const [search, setSearch] = useState("");
143
- const [filters, setFilters] = useState(() => buildInitialFilters(filterDefinitions));
144
- const [currentPage, setCurrentPage] = useState(1);
235
+ const urlFilters = useUrlStringFilters(useMemo(() => [{
236
+ key: "search",
237
+ defaultValue: "",
238
+ clearValue: ""
239
+ }, ...filterDefinitions.map((filter) => ({
240
+ key: filter.key,
241
+ defaultValue: "all",
242
+ clearValue: "all"
243
+ }))], [filterDefinitions]));
244
+ const search = urlFilters.values.search ?? "";
245
+ const filters = useMemo(() => ({
246
+ ...buildInitialFilters(filterDefinitions),
247
+ ...Object.fromEntries(filterDefinitions.map((filter) => [filter.key, urlFilters.values[filter.key] ?? "all"]))
248
+ }), [filterDefinitions, urlFilters.values]);
145
249
  const filteredData = useMemo(() => filterCrudData(data, search, filters, searchFields), [
146
250
  data,
147
251
  filters,
148
252
  search,
149
253
  searchFields
150
254
  ]);
255
+ const pagination = useUrlPagination({
256
+ totalItems: filteredData.length,
257
+ pageSize
258
+ });
259
+ const setCurrentPage = pagination.onPageChange;
151
260
  const { sortedData, sortState, setSortState } = useTableSorting({
152
261
  data: filteredData,
153
262
  columns,
154
263
  initialSortState
155
264
  });
156
- const totalPages = Math.max(1, Math.ceil(filteredData.length / pageSize));
157
- const validCurrentPage = getValidPage(currentPage, totalPages);
158
- const startIndex = (validCurrentPage - 1) * pageSize;
159
- const endIndex = Math.min(startIndex + pageSize, filteredData.length);
160
- const paginatedData = useMemo(() => paginateCrudData(sortedData, validCurrentPage, pageSize), [
161
- pageSize,
162
- sortedData,
163
- validCurrentPage
164
- ]);
265
+ const paginatedData = useMemo(() => pagination.paginate(sortedData), [pagination, sortedData]);
165
266
  const setFilter = useCallback((key, value) => {
166
- setFilters((currentFilters) => ({
167
- ...currentFilters,
168
- [key]: value
169
- }));
267
+ urlFilters.setValue(key, value);
170
268
  setCurrentPage(1);
171
- }, []);
269
+ }, [setCurrentPage, urlFilters]);
172
270
  const updateSearch = useCallback((value) => {
173
- setSearch(value);
271
+ urlFilters.setValue("search", value);
174
272
  setCurrentPage(1);
175
- }, []);
273
+ }, [setCurrentPage, urlFilters]);
176
274
  const clearAll = useCallback(() => {
177
- setSearch("");
178
- setFilters(buildInitialFilters(filterDefinitions));
275
+ urlFilters.clear();
179
276
  setCurrentPage(1);
180
- }, [filterDefinitions]);
277
+ }, [setCurrentPage, urlFilters]);
181
278
  const getDraftFilterResultCount = useCallback((draftValues) => filterCrudData(data, search, {
182
279
  ...filters,
183
280
  ...draftValues
@@ -204,119 +301,10 @@ function useCrudTableState({ data, columns, searchFields = [], filters: filterDe
204
301
  emptyState: hasSearch || hasFilters ? "no-results" : "initial",
205
302
  sortState,
206
303
  setSortState,
207
- pagination: {
208
- currentPage: validCurrentPage,
209
- totalPages,
210
- totalItems: filteredData.length,
211
- pageSize,
212
- startIndex,
213
- endIndex,
214
- onPageChange: setCurrentPage
215
- }
216
- };
217
- }
218
-
219
- //#endregion
220
- //#region src/use-url-string-filters.ts
221
- function buildParserMap(definitions) {
222
- return Object.fromEntries(definitions.map((definition) => [definition.param ?? definition.key, parseAsString.withDefault(definition.defaultValue ?? "all")]));
223
- }
224
- function normalizeFilterValue(value, definition) {
225
- if (value === (definition.clearValue ?? definition.defaultValue ?? "all")) return null;
226
- return value.length > 0 ? value : null;
227
- }
228
- function useUrlStringFilters(definitions) {
229
- const [params, setParams] = useQueryStates(useMemo(() => buildParserMap(definitions), [definitions]));
230
- const values = useMemo(() => Object.fromEntries(definitions.map((definition) => {
231
- const param = definition.param ?? definition.key;
232
- return [definition.key, params[param] ?? definition.defaultValue ?? "all"];
233
- })), [definitions, params]);
234
- return {
235
- values,
236
- setValue: useCallback((key, value) => {
237
- const definition = definitions.find((item) => item.key === key);
238
- if (!definition) return;
239
- setParams({ [definition.param ?? definition.key]: normalizeFilterValue(value, definition) });
240
- }, [definitions, setParams]),
241
- clear: useCallback(() => {
242
- setParams(Object.fromEntries(definitions.map((definition) => [definition.param ?? definition.key, null])));
243
- }, [definitions, setParams]),
244
- getDraftValues: useCallback((draftValues) => ({
245
- ...values,
246
- ...draftValues
247
- }), [values])
304
+ pagination
248
305
  };
249
306
  }
250
307
 
251
- //#endregion
252
- //#region src/use-url-pagination.ts
253
- function useUrlPagination({ totalItems, pageSize = 20, pageParam = "page" }) {
254
- const [page, setPage] = useQueryState(pageParam, parseAsInteger.withDefault(1));
255
- const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
256
- const currentPage = Math.min(Math.max(1, page), totalPages);
257
- const startIndex = (currentPage - 1) * pageSize;
258
- const endIndex = Math.min(startIndex + pageSize, totalItems);
259
- const hasPrevPage = currentPage > 1;
260
- const hasNextPage = currentPage < totalPages;
261
- const goToPage = useCallback((newPage) => {
262
- const validPage = Math.min(Math.max(1, newPage), totalPages);
263
- setPage(validPage === 1 ? null : validPage);
264
- }, [setPage, totalPages]);
265
- const nextPage = useCallback(() => {
266
- if (hasNextPage) goToPage(currentPage + 1);
267
- }, [
268
- currentPage,
269
- goToPage,
270
- hasNextPage
271
- ]);
272
- const prevPage = useCallback(() => {
273
- if (hasPrevPage) goToPage(currentPage - 1);
274
- }, [
275
- currentPage,
276
- goToPage,
277
- hasPrevPage
278
- ]);
279
- const firstPage = useCallback(() => {
280
- goToPage(1);
281
- }, [goToPage]);
282
- const lastPage = useCallback(() => {
283
- goToPage(totalPages);
284
- }, [goToPage, totalPages]);
285
- const paginate = useCallback((data) => data.slice(startIndex, endIndex), [endIndex, startIndex]);
286
- return useMemo(() => ({
287
- currentPage,
288
- pageSize,
289
- totalPages,
290
- totalItems,
291
- startIndex,
292
- endIndex,
293
- hasPrevPage,
294
- hasNextPage,
295
- goToPage,
296
- nextPage,
297
- prevPage,
298
- firstPage,
299
- lastPage,
300
- paginate,
301
- onPageChange: goToPage
302
- }), [
303
- currentPage,
304
- endIndex,
305
- firstPage,
306
- goToPage,
307
- hasNextPage,
308
- hasPrevPage,
309
- lastPage,
310
- nextPage,
311
- pageSize,
312
- paginate,
313
- prevPage,
314
- startIndex,
315
- totalItems,
316
- totalPages
317
- ]);
318
- }
319
-
320
308
  //#endregion
321
309
  export { CrudDataTable, CrudListTable, CrudResourceSheet, CrudTableView, useCrudTableState, useUrlPagination, useUrlStringFilters };
322
310
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/crud-data-table.tsx","../src/crud-list-table.tsx","../src/crud-resource-sheet.tsx","../src/crud-table-view.tsx","../src/search.ts","../src/pagination.ts","../src/use-crud-table-state.ts","../src/use-url-string-filters.ts","../src/use-url-pagination.ts"],"sourcesContent":["\"use client\";\n\nimport { SmartTable } from \"@carefully-built/ui\";\n\nimport type {\n CrudDataTableProps,\n} from \"./types\";\n\nexport function CrudDataTable<TItem extends object>({\n actions,\n actionHandlers,\n columns,\n data,\n fullHeight = true,\n getRowKey,\n isLoading,\n maxHeight,\n noDataContent,\n noDataMessage,\n onRowClick,\n pagination,\n renderActions,\n renderMobileCard,\n sortState,\n stickyHeader = true,\n onSortChange,\n}: CrudDataTableProps<TItem>): React.ReactElement {\n return (\n <SmartTable\n data={[...data]}\n columns={[...columns]}\n isLoading={isLoading}\n actions={actions ? [...actions] : undefined}\n actionHandlers={actionHandlers}\n renderActions={renderActions}\n noDataMessage={noDataMessage}\n noDataContent={noDataContent}\n getRowKey={getRowKey}\n onRowClick={onRowClick}\n renderMobileCard={renderMobileCard}\n stickyHeader={stickyHeader}\n fullHeight={fullHeight}\n maxHeight={maxHeight}\n sortState={sortState}\n onSortChange={onSortChange}\n pagination={pagination}\n />\n );\n}\n","\"use client\";\n\nimport { CrudDataTable } from \"./crud-data-table\";\nimport type { CrudDataTableProps } from \"./types\";\n\nexport type CrudListTableProps<TItem extends object> = CrudDataTableProps<TItem>;\n\nexport function CrudListTable<TItem extends object>(\n props: CrudListTableProps<TItem>,\n): React.ReactElement {\n return <CrudDataTable {...props} />;\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { ResponsiveSheet } from \"@carefully-built/ui\";\nimport type { ResponsiveSheetClassNames } from \"@carefully-built/ui\";\n\nexport interface CrudResourceSheetProps {\n readonly open: boolean;\n readonly onOpenChange: (open: boolean) => void;\n readonly title: ReactNode;\n readonly children: ReactNode;\n readonly formId?: string;\n readonly description?: ReactNode;\n readonly onCancel?: () => void;\n readonly cancelLabel?: ReactNode;\n readonly onConfirm?: () => void;\n readonly confirmLabel?: ReactNode;\n readonly confirmDisabled?: boolean;\n readonly confirmLoading?: boolean;\n readonly confirmCloseWhenDirty?: boolean;\n readonly width?: number;\n readonly className?: string;\n readonly contentClassName?: string;\n readonly footerClassName?: string;\n readonly classes?: ResponsiveSheetClassNames;\n}\n\nexport function CrudResourceSheet({\n children,\n formId,\n onConfirm,\n confirmCloseWhenDirty: _confirmCloseWhenDirty,\n ...sheetProps\n}: CrudResourceSheetProps): React.ReactElement {\n const submitForm = (): void => {\n const form = formId ? document.getElementById(formId) : null;\n\n if (form instanceof HTMLFormElement) {\n form.requestSubmit();\n return;\n }\n\n onConfirm?.();\n };\n\n return (\n <ResponsiveSheet\n {...sheetProps}\n onConfirm={formId || onConfirm ? submitForm : undefined}\n >\n {children}\n </ResponsiveSheet>\n );\n}\n","\"use client\";\n\nimport type { CrudTableViewProps } from \"./types\";\n\nimport { SmartTable, TableToolbar } from \"@carefully-built/ui\";\n\nexport function CrudTableView<TItem extends object>({\n state,\n columns,\n isLoading,\n searchPlaceholder = \"Search...\",\n filters = [],\n actions,\n actionHandlers,\n renderActions,\n noDataMessage,\n initialEmptyContent,\n noResultsContent,\n getRowKey,\n onRowClick,\n renderMobileCard,\n stickyHeader = true,\n fullHeight = true,\n maxHeight,\n}: CrudTableViewProps<TItem>): React.ReactElement {\n const emptyContent =\n state.emptyState === \"no-results\" ? noResultsContent : initialEmptyContent;\n\n return (\n <>\n <div className=\"shrink-0\">\n <TableToolbar\n search={{\n value: state.search,\n onChange: state.setSearch,\n placeholder: searchPlaceholder,\n }}\n filters={filters.map((filter) => ({\n config: filter.config,\n value: state.filters[filter.key] ?? \"all\",\n onChange: (value) => {\n state.setFilter(filter.key, value);\n },\n allowAll: filter.allowAll,\n clearable: filter.clearable,\n }))}\n onClearAll={state.clearAll}\n getDraftResultCount={state.getDraftFilterResultCount}\n />\n </div>\n\n <SmartTable\n data={state.paginatedData}\n columns={[...columns]}\n isLoading={isLoading}\n actions={actions ? [...actions] : undefined}\n actionHandlers={actionHandlers}\n renderActions={renderActions}\n noDataMessage={noDataMessage}\n noDataContent={emptyContent}\n getRowKey={getRowKey}\n onRowClick={onRowClick}\n renderMobileCard={renderMobileCard}\n stickyHeader={stickyHeader}\n fullHeight={fullHeight}\n maxHeight={maxHeight}\n sortState={state.sortState}\n onSortChange={state.setSortState}\n pagination={state.pagination}\n />\n </>\n );\n}\n","function normalizeSearchText(value: string): string {\n return value\n .toLocaleLowerCase()\n .normalize(\"NFD\")\n .replace(/\\p{Diacritic}/gu, \"\")\n .trim();\n}\n\nexport function buildCrudSearchText(values: readonly unknown[]): string {\n return normalizeSearchText(\n values\n .filter(\n (value): value is string | number =>\n typeof value === \"string\" || typeof value === \"number\",\n )\n .map(String)\n .join(\" \"),\n );\n}\n\nexport function matchesCrudSearch(searchText: string, query: string): boolean {\n const normalizedQuery = normalizeSearchText(query);\n\n if (!normalizedQuery) {\n return true;\n }\n\n return normalizedQuery\n .split(/\\s+/)\n .every((token) => searchText.includes(token));\n}\n","export interface CrudPaginationState {\n readonly currentPage: number;\n readonly totalPages: number;\n readonly totalItems: number;\n readonly pageSize: number;\n readonly startIndex: number;\n readonly endIndex: number;\n readonly onPageChange: (page: number) => void;\n}\n\nexport function getValidPage(page: number, totalPages: number): number {\n return Math.min(Math.max(1, page), totalPages);\n}\n\nexport function paginateCrudData<T>(\n data: readonly T[],\n currentPage: number,\n pageSize: number,\n): T[] {\n const startIndex = (currentPage - 1) * pageSize;\n return data.slice(startIndex, startIndex + pageSize);\n}\n","\"use client\";\n\nimport { useCallback, useMemo, useState } from \"react\";\n\nimport type { UseCrudTableStateOptions, CrudTableState } from \"./types\";\n\nimport { useTableSorting } from \"@carefully-built/ui\";\n\nimport { buildCrudSearchText, matchesCrudSearch } from \"./search\";\nimport { getValidPage, paginateCrudData } from \"./pagination\";\n\nfunction buildInitialFilters<TItem>(\n filters: readonly { readonly key: Extract<keyof TItem, string> }[],\n): Record<string, string> {\n return Object.fromEntries(filters.map((filter) => [filter.key, \"all\"]));\n}\n\nfunction itemMatchesFilters<TItem extends object>(\n item: TItem,\n filters: Record<string, string>,\n): boolean {\n const record = item as Record<string, unknown>;\n\n return Object.entries(filters).every(([key, filterValue]) => {\n if (!filterValue || filterValue === \"all\") {\n return true;\n }\n\n return record[key] === filterValue;\n });\n}\n\nfunction filterCrudData<TItem extends object>(\n data: readonly TItem[],\n search: string,\n filters: Record<string, string>,\n searchFields: readonly Extract<keyof TItem, string>[],\n): TItem[] {\n return data.filter((item) => {\n if (!itemMatchesFilters(item, filters)) {\n return false;\n }\n\n const record = item as Record<string, unknown>;\n const searchText = buildCrudSearchText(\n searchFields.map((field) => record[field]),\n );\n return matchesCrudSearch(searchText, search);\n });\n}\n\nexport function useCrudTableState<TItem extends object>({\n data,\n columns,\n searchFields = [],\n filters: filterDefinitions = [],\n pageSize = 20,\n initialSortState = null,\n}: UseCrudTableStateOptions<TItem>): CrudTableState<TItem> {\n const [search, setSearch] = useState(\"\");\n const [filters, setFilters] = useState<Record<string, string>>(() =>\n buildInitialFilters(filterDefinitions),\n );\n const [currentPage, setCurrentPage] = useState(1);\n\n const filteredData = useMemo(\n () => filterCrudData(data, search, filters, searchFields),\n [data, filters, search, searchFields],\n );\n const { sortedData, sortState, setSortState } = useTableSorting({\n data: filteredData,\n columns,\n initialSortState,\n });\n\n const totalPages = Math.max(1, Math.ceil(filteredData.length / pageSize));\n const validCurrentPage = getValidPage(currentPage, totalPages);\n const startIndex = (validCurrentPage - 1) * pageSize;\n const endIndex = Math.min(startIndex + pageSize, filteredData.length);\n const paginatedData = useMemo(\n () => paginateCrudData(sortedData, validCurrentPage, pageSize),\n [pageSize, sortedData, validCurrentPage],\n );\n\n const setFilter = useCallback((key: string, value: string) => {\n setFilters((currentFilters) => ({\n ...currentFilters,\n [key]: value,\n }));\n setCurrentPage(1);\n }, []);\n\n const updateSearch = useCallback((value: string) => {\n setSearch(value);\n setCurrentPage(1);\n }, []);\n\n const clearAll = useCallback(() => {\n setSearch(\"\");\n setFilters(buildInitialFilters(filterDefinitions));\n setCurrentPage(1);\n }, [filterDefinitions]);\n\n const getDraftFilterResultCount = useCallback(\n (draftValues: Record<string, string>) =>\n filterCrudData(\n data,\n search,\n {\n ...filters,\n ...draftValues,\n },\n searchFields,\n ).length,\n [data, filters, search, searchFields],\n );\n\n const hasSearch = search.trim().length > 0;\n const hasFilters = Object.values(filters).some(\n (value) => value && value !== \"all\",\n );\n\n return {\n filteredData,\n sortedData,\n paginatedData,\n search,\n setSearch: updateSearch,\n filters,\n setFilter,\n clearAll,\n getDraftFilterResultCount,\n hasSearch,\n hasFilters,\n emptyState: hasSearch || hasFilters ? \"no-results\" : \"initial\",\n sortState,\n setSortState,\n pagination: {\n currentPage: validCurrentPage,\n totalPages,\n totalItems: filteredData.length,\n pageSize,\n startIndex,\n endIndex,\n onPageChange: setCurrentPage,\n },\n };\n}\n","\"use client\";\n\nimport { parseAsString, useQueryStates } from \"nuqs\";\nimport { useCallback, useMemo } from \"react\";\n\nexport interface UrlStringFilterDefinition<TKey extends string = string> {\n readonly key: TKey;\n readonly param?: string;\n readonly defaultValue?: string;\n readonly clearValue?: string;\n}\n\ntype UrlStringFilterValues<TDefinitions extends readonly UrlStringFilterDefinition[]> = {\n readonly [TDefinition in TDefinitions[number] as TDefinition[\"key\"]]: string;\n};\n\nexport interface UrlStringFiltersState<\n TDefinitions extends readonly UrlStringFilterDefinition[],\n> {\n readonly values: UrlStringFilterValues<TDefinitions>;\n readonly setValue: (key: TDefinitions[number][\"key\"], value: string) => void;\n readonly clear: () => void;\n readonly getDraftValues: (\n draftValues: Record<string, string>,\n ) => UrlStringFilterValues<TDefinitions>;\n}\n\nfunction buildParserMap(definitions: readonly UrlStringFilterDefinition[]) {\n return Object.fromEntries(\n definitions.map((definition) => [\n definition.param ?? definition.key,\n parseAsString.withDefault(definition.defaultValue ?? \"all\"),\n ]),\n );\n}\n\nfunction normalizeFilterValue(\n value: string,\n definition: UrlStringFilterDefinition,\n): string | null {\n const clearValue = definition.clearValue ?? definition.defaultValue ?? \"all\";\n\n if (value === clearValue) {\n return null;\n }\n\n return value.length > 0 ? value : null;\n}\n\nexport function useUrlStringFilters<\n const TDefinitions extends readonly UrlStringFilterDefinition[],\n>(definitions: TDefinitions): UrlStringFiltersState<TDefinitions> {\n const parserMap = useMemo(() => buildParserMap(definitions), [definitions]);\n const [params, setParams] = useQueryStates(parserMap);\n\n const values = useMemo(\n () =>\n Object.fromEntries(\n definitions.map((definition) => {\n const param = definition.param ?? definition.key;\n return [definition.key, params[param] ?? definition.defaultValue ?? \"all\"];\n }),\n ) as UrlStringFilterValues<TDefinitions>,\n [definitions, params],\n );\n\n const setValue = useCallback(\n (key: TDefinitions[number][\"key\"], value: string) => {\n const definition = definitions.find((item) => item.key === key);\n\n if (!definition) {\n return;\n }\n\n void setParams({\n [definition.param ?? definition.key]: normalizeFilterValue(value, definition),\n });\n },\n [definitions, setParams],\n );\n\n const clear = useCallback(() => {\n void setParams(\n Object.fromEntries(\n definitions.map((definition) => [definition.param ?? definition.key, null]),\n ),\n );\n }, [definitions, setParams]);\n\n const getDraftValues = useCallback(\n (draftValues: Record<string, string>) =>\n ({\n ...values,\n ...draftValues,\n }) as UrlStringFilterValues<TDefinitions>,\n [values],\n );\n\n return {\n values,\n setValue,\n clear,\n getDraftValues,\n };\n}\n","\"use client\";\n\nimport { parseAsInteger, useQueryState } from \"nuqs\";\nimport { useCallback, useMemo } from \"react\";\n\nimport type { CrudPaginationState } from \"./pagination\";\n\nexport interface UseUrlPaginationOptions {\n readonly totalItems: number;\n readonly pageSize?: number;\n readonly pageParam?: string;\n}\n\nexport interface UrlPaginationState extends CrudPaginationState {\n readonly hasPrevPage: boolean;\n readonly hasNextPage: boolean;\n readonly goToPage: (page: number) => void;\n readonly nextPage: () => void;\n readonly prevPage: () => void;\n readonly firstPage: () => void;\n readonly lastPage: () => void;\n readonly paginate: <T>(data: readonly T[]) => T[];\n}\n\nexport function useUrlPagination({\n totalItems,\n pageSize = 20,\n pageParam = \"page\",\n}: UseUrlPaginationOptions): UrlPaginationState {\n const [page, setPage] = useQueryState(\n pageParam,\n parseAsInteger.withDefault(1),\n );\n\n const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));\n const currentPage = Math.min(Math.max(1, page), totalPages);\n const startIndex = (currentPage - 1) * pageSize;\n const endIndex = Math.min(startIndex + pageSize, totalItems);\n const hasPrevPage = currentPage > 1;\n const hasNextPage = currentPage < totalPages;\n\n const goToPage = useCallback(\n (newPage: number) => {\n const validPage = Math.min(Math.max(1, newPage), totalPages);\n void setPage(validPage === 1 ? null : validPage);\n },\n [setPage, totalPages],\n );\n\n const nextPage = useCallback(() => {\n if (hasNextPage) {\n goToPage(currentPage + 1);\n }\n }, [currentPage, goToPage, hasNextPage]);\n\n const prevPage = useCallback(() => {\n if (hasPrevPage) {\n goToPage(currentPage - 1);\n }\n }, [currentPage, goToPage, hasPrevPage]);\n\n const firstPage = useCallback(() => {\n goToPage(1);\n }, [goToPage]);\n\n const lastPage = useCallback(() => {\n goToPage(totalPages);\n }, [goToPage, totalPages]);\n\n const paginate = useCallback(\n <T,>(data: readonly T[]): T[] => data.slice(startIndex, endIndex),\n [endIndex, startIndex],\n );\n\n return useMemo(\n () => ({\n currentPage,\n pageSize,\n totalPages,\n totalItems,\n startIndex,\n endIndex,\n hasPrevPage,\n hasNextPage,\n goToPage,\n nextPage,\n prevPage,\n firstPage,\n lastPage,\n paginate,\n onPageChange: goToPage,\n }),\n [\n currentPage,\n endIndex,\n firstPage,\n goToPage,\n hasNextPage,\n hasPrevPage,\n lastPage,\n nextPage,\n pageSize,\n paginate,\n prevPage,\n startIndex,\n totalItems,\n totalPages,\n ],\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/crud-data-table.tsx","../src/crud-list-table.tsx","../src/crud-resource-sheet.tsx","../src/crud-table-view.tsx","../src/search.ts","../src/use-url-pagination.ts","../src/use-url-string-filters.ts","../src/use-crud-table-state.ts"],"sourcesContent":["\"use client\";\n\nimport { SmartTable } from \"@carefully-built/ui\";\n\nimport type {\n CrudDataTableProps,\n} from \"./types\";\n\nexport function CrudDataTable<TItem extends object>({\n actions,\n actionHandlers,\n columns,\n data,\n fullHeight = true,\n getRowKey,\n isLoading,\n maxHeight,\n noDataContent,\n noDataMessage,\n onRowClick,\n pagination,\n renderActions,\n renderMobileCard,\n sortState,\n stickyHeader = true,\n onSortChange,\n}: CrudDataTableProps<TItem>): React.ReactElement {\n return (\n <SmartTable\n data={[...data]}\n columns={[...columns]}\n isLoading={isLoading}\n actions={actions ? [...actions] : undefined}\n actionHandlers={actionHandlers}\n renderActions={renderActions}\n noDataMessage={noDataMessage}\n noDataContent={noDataContent}\n getRowKey={getRowKey}\n onRowClick={onRowClick}\n renderMobileCard={renderMobileCard}\n stickyHeader={stickyHeader}\n fullHeight={fullHeight}\n maxHeight={maxHeight}\n sortState={sortState}\n onSortChange={onSortChange}\n pagination={pagination}\n />\n );\n}\n","\"use client\";\n\nimport { CrudDataTable } from \"./crud-data-table\";\nimport type { CrudDataTableProps } from \"./types\";\n\nexport type CrudListTableProps<TItem extends object> = CrudDataTableProps<TItem>;\n\nexport function CrudListTable<TItem extends object>(\n props: CrudListTableProps<TItem>,\n): React.ReactElement {\n return <CrudDataTable {...props} />;\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { ResponsiveSheet } from \"@carefully-built/ui\";\nimport type { ResponsiveSheetClassNames, SheetOutsideInteractionGuard } from \"@carefully-built/ui\";\n\nexport interface CrudResourceSheetProps {\n readonly open: boolean;\n readonly onOpenChange: (open: boolean) => void;\n readonly title: ReactNode;\n readonly children: ReactNode;\n readonly formId?: string;\n readonly description?: ReactNode;\n readonly onCancel?: () => void;\n readonly cancelLabel?: ReactNode;\n readonly onConfirm?: () => void;\n readonly confirmLabel?: ReactNode;\n readonly confirmDisabled?: boolean;\n readonly confirmLoading?: boolean;\n readonly confirmCloseWhenDirty?: boolean;\n readonly width?: number;\n readonly outsideInteractionGuard?: SheetOutsideInteractionGuard;\n readonly className?: string;\n readonly contentClassName?: string;\n readonly footerClassName?: string;\n readonly classes?: ResponsiveSheetClassNames;\n}\n\nexport function CrudResourceSheet({\n children,\n formId,\n onConfirm,\n confirmCloseWhenDirty: _confirmCloseWhenDirty,\n outsideInteractionGuard,\n ...sheetProps\n}: CrudResourceSheetProps): React.ReactElement {\n const submitForm = (): void => {\n const form = formId ? document.getElementById(formId) : null;\n\n if (form instanceof HTMLFormElement) {\n form.requestSubmit();\n return;\n }\n\n onConfirm?.();\n };\n\n return (\n <ResponsiveSheet\n {...sheetProps}\n outsideInteractionGuard={outsideInteractionGuard}\n onConfirm={formId || onConfirm ? submitForm : undefined}\n >\n {children}\n </ResponsiveSheet>\n );\n}\n","\"use client\";\n\nimport type { CrudTableViewProps } from \"./types\";\n\nimport { SmartTable, TableToolbar } from \"@carefully-built/ui\";\n\nexport function CrudTableView<TItem extends object>({\n state,\n columns,\n isLoading,\n searchPlaceholder = \"Search...\",\n filters = [],\n actions,\n actionHandlers,\n renderActions,\n noDataMessage,\n initialEmptyContent,\n noResultsContent,\n getRowKey,\n onRowClick,\n renderMobileCard,\n stickyHeader = true,\n fullHeight = true,\n maxHeight,\n}: CrudTableViewProps<TItem>): React.ReactElement {\n const emptyContent =\n state.emptyState === \"no-results\" ? noResultsContent : initialEmptyContent;\n\n return (\n <>\n <div className=\"shrink-0\">\n <TableToolbar\n search={{\n value: state.search,\n onChange: state.setSearch,\n placeholder: searchPlaceholder,\n }}\n filters={filters.map((filter) => ({\n config: filter.config,\n value: state.filters[filter.key] ?? \"all\",\n onChange: (value) => {\n state.setFilter(filter.key, value);\n },\n allowAll: filter.allowAll,\n allOptionLabel: filter.allOptionLabel,\n clearable: filter.clearable,\n }))}\n onClearAll={state.clearAll}\n getDraftResultCount={state.getDraftFilterResultCount}\n />\n </div>\n\n <SmartTable\n data={state.paginatedData}\n columns={[...columns]}\n isLoading={isLoading}\n actions={actions ? [...actions] : undefined}\n actionHandlers={actionHandlers}\n renderActions={renderActions}\n noDataMessage={noDataMessage}\n noDataContent={emptyContent}\n getRowKey={getRowKey}\n onRowClick={onRowClick}\n renderMobileCard={renderMobileCard}\n stickyHeader={stickyHeader}\n fullHeight={fullHeight}\n maxHeight={maxHeight}\n sortState={state.sortState}\n onSortChange={state.setSortState}\n pagination={state.pagination}\n />\n </>\n );\n}\n","function normalizeSearchText(value: string): string {\n return value\n .toLocaleLowerCase()\n .normalize(\"NFD\")\n .replace(/\\p{Diacritic}/gu, \"\")\n .trim();\n}\n\nexport function buildCrudSearchText(values: readonly unknown[]): string {\n return normalizeSearchText(\n values\n .filter(\n (value): value is string | number =>\n typeof value === \"string\" || typeof value === \"number\",\n )\n .map(String)\n .join(\" \"),\n );\n}\n\nexport function matchesCrudSearch(searchText: string, query: string): boolean {\n const normalizedQuery = normalizeSearchText(query);\n\n if (!normalizedQuery) {\n return true;\n }\n\n return normalizedQuery\n .split(/\\s+/)\n .every((token) => searchText.includes(token));\n}\n","\"use client\";\n\nimport { parseAsInteger, useQueryState } from \"nuqs\";\nimport { useCallback, useMemo } from \"react\";\n\nimport type { CrudPaginationState } from \"./pagination\";\n\nexport interface UseUrlPaginationOptions {\n readonly totalItems: number;\n readonly pageSize?: number;\n readonly pageParam?: string;\n}\n\nexport interface UrlPaginationState extends CrudPaginationState {\n readonly hasPrevPage: boolean;\n readonly hasNextPage: boolean;\n readonly goToPage: (page: number) => void;\n readonly nextPage: () => void;\n readonly prevPage: () => void;\n readonly firstPage: () => void;\n readonly lastPage: () => void;\n readonly paginate: <T>(data: readonly T[]) => T[];\n}\n\nexport function useUrlPagination({\n totalItems,\n pageSize = 20,\n pageParam = \"page\",\n}: UseUrlPaginationOptions): UrlPaginationState {\n const [page, setPage] = useQueryState(\n pageParam,\n parseAsInteger.withDefault(1),\n );\n\n const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));\n const currentPage = Math.min(Math.max(1, page), totalPages);\n const startIndex = (currentPage - 1) * pageSize;\n const endIndex = Math.min(startIndex + pageSize, totalItems);\n const hasPrevPage = currentPage > 1;\n const hasNextPage = currentPage < totalPages;\n\n const goToPage = useCallback(\n (newPage: number) => {\n const validPage = Math.min(Math.max(1, newPage), totalPages);\n void setPage(validPage === 1 ? null : validPage);\n },\n [setPage, totalPages],\n );\n\n const nextPage = useCallback(() => {\n if (hasNextPage) {\n goToPage(currentPage + 1);\n }\n }, [currentPage, goToPage, hasNextPage]);\n\n const prevPage = useCallback(() => {\n if (hasPrevPage) {\n goToPage(currentPage - 1);\n }\n }, [currentPage, goToPage, hasPrevPage]);\n\n const firstPage = useCallback(() => {\n goToPage(1);\n }, [goToPage]);\n\n const lastPage = useCallback(() => {\n goToPage(totalPages);\n }, [goToPage, totalPages]);\n\n const paginate = useCallback(\n <T,>(data: readonly T[]): T[] => data.slice(startIndex, endIndex),\n [endIndex, startIndex],\n );\n\n return useMemo(\n () => ({\n currentPage,\n pageSize,\n totalPages,\n totalItems,\n startIndex,\n endIndex,\n hasPrevPage,\n hasNextPage,\n goToPage,\n nextPage,\n prevPage,\n firstPage,\n lastPage,\n paginate,\n onPageChange: goToPage,\n }),\n [\n currentPage,\n endIndex,\n firstPage,\n goToPage,\n hasNextPage,\n hasPrevPage,\n lastPage,\n nextPage,\n pageSize,\n paginate,\n prevPage,\n startIndex,\n totalItems,\n totalPages,\n ],\n );\n}\n","\"use client\";\n\nimport { parseAsString, useQueryStates } from \"nuqs\";\nimport { useCallback, useMemo } from \"react\";\n\nexport interface UrlStringFilterDefinition<TKey extends string = string> {\n readonly key: TKey;\n readonly param?: string;\n readonly defaultValue?: string;\n readonly clearValue?: string;\n}\n\ntype UrlStringFilterValues<TDefinitions extends readonly UrlStringFilterDefinition[]> = {\n readonly [TDefinition in TDefinitions[number] as TDefinition[\"key\"]]: string;\n};\n\nexport interface UrlStringFiltersState<\n TDefinitions extends readonly UrlStringFilterDefinition[],\n> {\n readonly values: UrlStringFilterValues<TDefinitions>;\n readonly setValue: (key: TDefinitions[number][\"key\"], value: string) => void;\n readonly clear: () => void;\n readonly getDraftValues: (\n draftValues: Record<string, string>,\n ) => UrlStringFilterValues<TDefinitions>;\n}\n\nfunction buildParserMap(definitions: readonly UrlStringFilterDefinition[]) {\n return Object.fromEntries(\n definitions.map((definition) => [\n definition.param ?? definition.key,\n parseAsString.withDefault(definition.defaultValue ?? \"all\"),\n ]),\n );\n}\n\nfunction normalizeFilterValue(\n value: string,\n definition: UrlStringFilterDefinition,\n): string | null {\n const clearValue = definition.clearValue ?? definition.defaultValue ?? \"all\";\n\n if (value === clearValue) {\n return null;\n }\n\n return value.length > 0 ? value : null;\n}\n\nexport function useUrlStringFilters<\n const TDefinitions extends readonly UrlStringFilterDefinition[],\n>(definitions: TDefinitions): UrlStringFiltersState<TDefinitions> {\n const parserMap = useMemo(() => buildParserMap(definitions), [definitions]);\n const [params, setParams] = useQueryStates(parserMap);\n\n const values = useMemo(\n () =>\n Object.fromEntries(\n definitions.map((definition) => {\n const param = definition.param ?? definition.key;\n return [definition.key, params[param] ?? definition.defaultValue ?? \"all\"];\n }),\n ) as UrlStringFilterValues<TDefinitions>,\n [definitions, params],\n );\n\n const setValue = useCallback(\n (key: TDefinitions[number][\"key\"], value: string) => {\n const definition = definitions.find((item) => item.key === key);\n\n if (!definition) {\n return;\n }\n\n void setParams({\n [definition.param ?? definition.key]: normalizeFilterValue(value, definition),\n });\n },\n [definitions, setParams],\n );\n\n const clear = useCallback(() => {\n void setParams(\n Object.fromEntries(\n definitions.map((definition) => [definition.param ?? definition.key, null]),\n ),\n );\n }, [definitions, setParams]);\n\n const getDraftValues = useCallback(\n (draftValues: Record<string, string>) =>\n ({\n ...values,\n ...draftValues,\n }) as UrlStringFilterValues<TDefinitions>,\n [values],\n );\n\n return {\n values,\n setValue,\n clear,\n getDraftValues,\n };\n}\n","\"use client\";\n\nimport { useCallback, useMemo } from \"react\";\n\nimport type { UseCrudTableStateOptions, CrudTableState } from \"./types\";\n\nimport { useTableSorting } from \"@carefully-built/ui\";\n\nimport { buildCrudSearchText, matchesCrudSearch } from \"./search\";\nimport { useUrlPagination } from \"./use-url-pagination\";\nimport { useUrlStringFilters, type UrlStringFilterDefinition } from \"./use-url-string-filters\";\n\nfunction buildInitialFilters<TItem>(\n filters: readonly { readonly key: Extract<keyof TItem, string> }[],\n): Record<string, string> {\n return Object.fromEntries(filters.map((filter) => [filter.key, \"all\"]));\n}\n\nfunction itemMatchesFilters<TItem extends object>(\n item: TItem,\n filters: Record<string, string>,\n): boolean {\n const record = item as Record<string, unknown>;\n\n return Object.entries(filters).every(([key, filterValue]) => {\n if (!filterValue || filterValue === \"all\") {\n return true;\n }\n\n return record[key] === filterValue;\n });\n}\n\nfunction filterCrudData<TItem extends object>(\n data: readonly TItem[],\n search: string,\n filters: Record<string, string>,\n searchFields: readonly Extract<keyof TItem, string>[],\n): TItem[] {\n return data.filter((item) => {\n if (!itemMatchesFilters(item, filters)) {\n return false;\n }\n\n const record = item as Record<string, unknown>;\n const searchText = buildCrudSearchText(\n searchFields.map((field) => record[field]),\n );\n return matchesCrudSearch(searchText, search);\n });\n}\n\nexport function useCrudTableState<TItem extends object>({\n data,\n columns,\n searchFields = [],\n filters: filterDefinitions = [],\n pageSize = 20,\n initialSortState = null,\n}: UseCrudTableStateOptions<TItem>): CrudTableState<TItem> {\n const urlFilterDefinitions = useMemo(\n () =>\n [\n { key: \"search\", defaultValue: \"\", clearValue: \"\" },\n ...filterDefinitions.map((filter) => ({\n key: filter.key,\n defaultValue: \"all\",\n clearValue: \"all\",\n })),\n ] satisfies readonly UrlStringFilterDefinition[],\n [filterDefinitions],\n );\n const urlFilters = useUrlStringFilters(urlFilterDefinitions);\n const search = urlFilters.values.search ?? \"\";\n const filters = useMemo(\n () => ({\n ...buildInitialFilters(filterDefinitions),\n ...Object.fromEntries(\n filterDefinitions.map((filter) => [filter.key, urlFilters.values[filter.key] ?? \"all\"]),\n ),\n }),\n [filterDefinitions, urlFilters.values],\n );\n\n const filteredData = useMemo(\n () => filterCrudData(data, search, filters, searchFields),\n [data, filters, search, searchFields],\n );\n const pagination = useUrlPagination({\n totalItems: filteredData.length,\n pageSize,\n });\n const setCurrentPage = pagination.onPageChange;\n const { sortedData, sortState, setSortState } = useTableSorting({\n data: filteredData,\n columns,\n initialSortState,\n });\n\n const paginatedData = useMemo(\n () => pagination.paginate(sortedData),\n [pagination, sortedData],\n );\n\n const setFilter = useCallback((key: string, value: string) => {\n urlFilters.setValue(key, value);\n void setCurrentPage(1);\n }, [setCurrentPage, urlFilters]);\n\n const updateSearch = useCallback((value: string) => {\n urlFilters.setValue(\"search\", value);\n void setCurrentPage(1);\n }, [setCurrentPage, urlFilters]);\n\n const clearAll = useCallback(() => {\n urlFilters.clear();\n void setCurrentPage(1);\n }, [setCurrentPage, urlFilters]);\n\n const getDraftFilterResultCount = useCallback(\n (draftValues: Record<string, string>) =>\n filterCrudData(\n data,\n search,\n {\n ...filters,\n ...draftValues,\n },\n searchFields,\n ).length,\n [data, filters, search, searchFields],\n );\n\n const hasSearch = search.trim().length > 0;\n const hasFilters = Object.values(filters).some(\n (value) => value && value !== \"all\",\n );\n\n return {\n filteredData,\n sortedData,\n paginatedData,\n search,\n setSearch: updateSearch,\n filters,\n setFilter,\n clearAll,\n getDraftFilterResultCount,\n hasSearch,\n hasFilters,\n emptyState: hasSearch || hasFilters ? \"no-results\" : \"initial\",\n sortState,\n setSortState,\n pagination,\n };\n}\n"],"mappings":";;;;;;AAQA,SAAgB,cAAoC,EAClD,SACA,gBACA,SACA,MACA,aAAa,MACb,WACA,WACA,WACA,eACA,eACA,YACA,YACA,eACA,kBACA,WACA,eAAe,MACf,gBACgD;AAChD,QACE,oBAAC;EACC,MAAM,CAAC,GAAG,KAAK;EACf,SAAS,CAAC,GAAG,QAAQ;EACV;EACX,SAAS,UAAU,CAAC,GAAG,QAAQ,GAAG;EAClB;EACD;EACA;EACA;EACJ;EACC;EACM;EACJ;EACF;EACD;EACA;EACG;EACF;GACZ;;;;;ACvCN,SAAgB,cACd,OACoB;AACpB,QAAO,oBAAC,iBAAc,GAAI,QAAS;;;;;ACmBrC,SAAgB,kBAAkB,EAChC,UACA,QACA,WACA,uBAAuB,wBACvB,yBACA,GAAG,cAC0C;CAC7C,MAAM,mBAAyB;EAC7B,MAAM,OAAO,SAAS,SAAS,eAAe,OAAO,GAAG;AAExD,MAAI,gBAAgB,iBAAiB;AACnC,QAAK,eAAe;AACpB;;AAGF,eAAa;;AAGf,QACE,oBAAC;EACC,GAAI;EACqB;EACzB,WAAW,UAAU,YAAY,aAAa;EAE7C;GACe;;;;;ACjDtB,SAAgB,cAAoC,EAClD,OACA,SACA,WACA,oBAAoB,aACpB,UAAU,EAAE,EACZ,SACA,gBACA,eACA,eACA,qBACA,kBACA,WACA,YACA,kBACA,eAAe,MACf,aAAa,MACb,aACgD;CAChD,MAAM,eACJ,MAAM,eAAe,eAAe,mBAAmB;AAEzD,QACE,4CACE,oBAAC;EAAI,WAAU;YACb,oBAAC;GACC,QAAQ;IACN,OAAO,MAAM;IACb,UAAU,MAAM;IAChB,aAAa;IACd;GACD,SAAS,QAAQ,KAAK,YAAY;IAChC,QAAQ,OAAO;IACf,OAAO,MAAM,QAAQ,OAAO,QAAQ;IACpC,WAAW,UAAU;AACnB,WAAM,UAAU,OAAO,KAAK,MAAM;;IAEpC,UAAU,OAAO;IACjB,gBAAgB,OAAO;IACvB,WAAW,OAAO;IACnB,EAAE;GACH,YAAY,MAAM;GAClB,qBAAqB,MAAM;IAC3B;GACE,EAEN,oBAAC;EACC,MAAM,MAAM;EACZ,SAAS,CAAC,GAAG,QAAQ;EACV;EACX,SAAS,UAAU,CAAC,GAAG,QAAQ,GAAG;EAClB;EACD;EACA;EACf,eAAe;EACJ;EACC;EACM;EACJ;EACF;EACD;EACX,WAAW,MAAM;EACjB,cAAc,MAAM;EACpB,YAAY,MAAM;GAClB,IACD;;;;;ACvEP,SAAS,oBAAoB,OAAuB;AAClD,QAAO,MACJ,mBAAmB,CACnB,UAAU,MAAM,CAChB,QAAQ,mBAAmB,GAAG,CAC9B,MAAM;;AAGX,SAAgB,oBAAoB,QAAoC;AACtE,QAAO,oBACL,OACG,QACE,UACC,OAAO,UAAU,YAAY,OAAO,UAAU,SACjD,CACA,IAAI,OAAO,CACX,KAAK,IAAI,CACb;;AAGH,SAAgB,kBAAkB,YAAoB,OAAwB;CAC5E,MAAM,kBAAkB,oBAAoB,MAAM;AAElD,KAAI,CAAC,gBACH,QAAO;AAGT,QAAO,gBACJ,MAAM,MAAM,CACZ,OAAO,UAAU,WAAW,SAAS,MAAM,CAAC;;;;;ACLjD,SAAgB,iBAAiB,EAC/B,YACA,WAAW,IACX,YAAY,UACkC;CAC9C,MAAM,CAAC,MAAM,WAAW,cACtB,WACA,eAAe,YAAY,EAAE,CAC9B;CAED,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,KAAK,aAAa,SAAS,CAAC;CAChE,MAAM,cAAc,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,EAAE,WAAW;CAC3D,MAAM,cAAc,cAAc,KAAK;CACvC,MAAM,WAAW,KAAK,IAAI,aAAa,UAAU,WAAW;CAC5D,MAAM,cAAc,cAAc;CAClC,MAAM,cAAc,cAAc;CAElC,MAAM,WAAW,aACd,YAAoB;EACnB,MAAM,YAAY,KAAK,IAAI,KAAK,IAAI,GAAG,QAAQ,EAAE,WAAW;AAC5D,EAAK,QAAQ,cAAc,IAAI,OAAO,UAAU;IAElD,CAAC,SAAS,WAAW,CACtB;CAED,MAAM,WAAW,kBAAkB;AACjC,MAAI,YACF,UAAS,cAAc,EAAE;IAE1B;EAAC;EAAa;EAAU;EAAY,CAAC;CAExC,MAAM,WAAW,kBAAkB;AACjC,MAAI,YACF,UAAS,cAAc,EAAE;IAE1B;EAAC;EAAa;EAAU;EAAY,CAAC;CAExC,MAAM,YAAY,kBAAkB;AAClC,WAAS,EAAE;IACV,CAAC,SAAS,CAAC;CAEd,MAAM,WAAW,kBAAkB;AACjC,WAAS,WAAW;IACnB,CAAC,UAAU,WAAW,CAAC;CAE1B,MAAM,WAAW,aACV,SAA4B,KAAK,MAAM,YAAY,SAAS,EACjE,CAAC,UAAU,WAAW,CACvB;AAED,QAAO,eACE;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,cAAc;EACf,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;;;;;ACjFH,SAAS,eAAe,aAAmD;AACzE,QAAO,OAAO,YACZ,YAAY,KAAK,eAAe,CAC9B,WAAW,SAAS,WAAW,KAC/B,cAAc,YAAY,WAAW,gBAAgB,MAAM,CAC5D,CAAC,CACH;;AAGH,SAAS,qBACP,OACA,YACe;AAGf,KAAI,WAFe,WAAW,cAAc,WAAW,gBAAgB,OAGrE,QAAO;AAGT,QAAO,MAAM,SAAS,IAAI,QAAQ;;AAGpC,SAAgB,oBAEd,aAAgE;CAEhE,MAAM,CAAC,QAAQ,aAAa,eADV,cAAc,eAAe,YAAY,EAAE,CAAC,YAAY,CAAC,CACtB;CAErD,MAAM,SAAS,cAEX,OAAO,YACL,YAAY,KAAK,eAAe;EAC9B,MAAM,QAAQ,WAAW,SAAS,WAAW;AAC7C,SAAO,CAAC,WAAW,KAAK,OAAO,UAAU,WAAW,gBAAgB,MAAM;GAC1E,CACH,EACH,CAAC,aAAa,OAAO,CACtB;AAkCD,QAAO;EACL;EACA,UAlCe,aACd,KAAkC,UAAkB;GACnD,MAAM,aAAa,YAAY,MAAM,SAAS,KAAK,QAAQ,IAAI;AAE/D,OAAI,CAAC,WACH;AAGF,GAAK,UAAU,GACZ,WAAW,SAAS,WAAW,MAAM,qBAAqB,OAAO,WAAW,EAC9E,CAAC;KAEJ,CAAC,aAAa,UAAU,CACzB;EAsBC,OApBY,kBAAkB;AAC9B,GAAK,UACH,OAAO,YACL,YAAY,KAAK,eAAe,CAAC,WAAW,SAAS,WAAW,KAAK,KAAK,CAAC,CAC5E,CACF;KACA,CAAC,aAAa,UAAU,CAAC;EAe1B,gBAbqB,aACpB,iBACE;GACC,GAAG;GACH,GAAG;GACJ,GACH,CAAC,OAAO,CACT;EAOA;;;;;AC3FH,SAAS,oBACP,SACwB;AACxB,QAAO,OAAO,YAAY,QAAQ,KAAK,WAAW,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC;;AAGzE,SAAS,mBACP,MACA,SACS;CACT,MAAM,SAAS;AAEf,QAAO,OAAO,QAAQ,QAAQ,CAAC,OAAO,CAAC,KAAK,iBAAiB;AAC3D,MAAI,CAAC,eAAe,gBAAgB,MAClC,QAAO;AAGT,SAAO,OAAO,SAAS;GACvB;;AAGJ,SAAS,eACP,MACA,QACA,SACA,cACS;AACT,QAAO,KAAK,QAAQ,SAAS;AAC3B,MAAI,CAAC,mBAAmB,MAAM,QAAQ,CACpC,QAAO;EAGT,MAAM,SAAS;AAIf,SAAO,kBAHY,oBACjB,aAAa,KAAK,UAAU,OAAO,OAAO,CAC3C,EACoC,OAAO;GAC5C;;AAGJ,SAAgB,kBAAwC,EACtD,MACA,SACA,eAAe,EAAE,EACjB,SAAS,oBAAoB,EAAE,EAC/B,WAAW,IACX,mBAAmB,QACsC;CAazD,MAAM,aAAa,oBAZU,cAEzB,CACE;EAAE,KAAK;EAAU,cAAc;EAAI,YAAY;EAAI,EACnD,GAAG,kBAAkB,KAAK,YAAY;EACpC,KAAK,OAAO;EACZ,cAAc;EACd,YAAY;EACb,EAAE,CACJ,EACH,CAAC,kBAAkB,CACpB,CAC2D;CAC5D,MAAM,SAAS,WAAW,OAAO,UAAU;CAC3C,MAAM,UAAU,eACP;EACL,GAAG,oBAAoB,kBAAkB;EACzC,GAAG,OAAO,YACR,kBAAkB,KAAK,WAAW,CAAC,OAAO,KAAK,WAAW,OAAO,OAAO,QAAQ,MAAM,CAAC,CACxF;EACF,GACD,CAAC,mBAAmB,WAAW,OAAO,CACvC;CAED,MAAM,eAAe,cACb,eAAe,MAAM,QAAQ,SAAS,aAAa,EACzD;EAAC;EAAM;EAAS;EAAQ;EAAa,CACtC;CACD,MAAM,aAAa,iBAAiB;EAClC,YAAY,aAAa;EACzB;EACD,CAAC;CACF,MAAM,iBAAiB,WAAW;CAClC,MAAM,EAAE,YAAY,WAAW,iBAAiB,gBAAgB;EAC9D,MAAM;EACN;EACA;EACD,CAAC;CAEF,MAAM,gBAAgB,cACd,WAAW,SAAS,WAAW,EACrC,CAAC,YAAY,WAAW,CACzB;CAED,MAAM,YAAY,aAAa,KAAa,UAAkB;AAC5D,aAAW,SAAS,KAAK,MAAM;AAC/B,EAAK,eAAe,EAAE;IACrB,CAAC,gBAAgB,WAAW,CAAC;CAEhC,MAAM,eAAe,aAAa,UAAkB;AAClD,aAAW,SAAS,UAAU,MAAM;AACpC,EAAK,eAAe,EAAE;IACrB,CAAC,gBAAgB,WAAW,CAAC;CAEhC,MAAM,WAAW,kBAAkB;AACjC,aAAW,OAAO;AAClB,EAAK,eAAe,EAAE;IACrB,CAAC,gBAAgB,WAAW,CAAC;CAEhC,MAAM,4BAA4B,aAC/B,gBACC,eACE,MACA,QACA;EACE,GAAG;EACH,GAAG;EACJ,EACD,aACD,CAAC,QACJ;EAAC;EAAM;EAAS;EAAQ;EAAa,CACtC;CAED,MAAM,YAAY,OAAO,MAAM,CAAC,SAAS;CACzC,MAAM,aAAa,OAAO,OAAO,QAAQ,CAAC,MACvC,UAAU,SAAS,UAAU,MAC/B;AAED,QAAO;EACL;EACA;EACA;EACA;EACA,WAAW;EACX;EACA;EACA;EACA;EACA;EACA;EACA,YAAY,aAAa,aAAa,eAAe;EACrD;EACA;EACA;EACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carefully-built/crud",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Config-driven CRUD table and form helpers for Carefully Built SaaS apps.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -45,7 +45,7 @@
45
45
  "access": "public"
46
46
  },
47
47
  "peerDependencies": {
48
- "@carefully-built/ui": ">=0.1.4",
48
+ "@carefully-built/ui": ">=0.1.15",
49
49
  "nuqs": ">=2.0.0",
50
50
  "react": ">=18.2.0 || >=19.0.0",
51
51
  "react-dom": ">=18.2.0 || >=19.0.0"