@jayalfredprufrock/mobx-toolbox 0.2.3 → 0.2.4

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/dialog.mjs CHANGED
@@ -1,2 +1,128 @@
1
- import{observer as e}from"mobx-react-lite";import{createContext as t,useContext as n,useRef as r}from"react";import{makeAutoObservable as i}from"mobx";import{jsx as a}from"react/jsx-runtime";const o=t(void 0),s=()=>{let e=n(o);if(!e)throw Error(`Dialog store context not available. Are you using the <Dialogs /> component?`);return e},c=o.Provider,l=t(void 0),u=()=>{let e=n(l);if(!e)throw Error(`Dialog context not available. Are you using <DialogProvider /> ?`);return e},d=()=>n(l),f=l.Provider;var p=class{id;component;props;openedAt=0;previouslyFocusedElement=null;state;constructor(e,t){this.store=e,this.config=t,this.id=t.id??btoa(Math.random().toString()).substring(3,12),this.component=t.component,this.props=t.props,this.setState(t.initialState??`closed`),i(this,{id:!1,component:!1,props:!1,openedAt:!1,previouslyFocusedElement:!1})}close(e){this.setState(e?`closed`:`closing`)}open(e){this.setState(e?`opened`:`opening`)}setState(e){this.state!==e&&(this.state=e,e===`opened`||e===`opening`?(this.openedAt=Date.now(),e===`opening`&&(this.previouslyFocusedElement=document.activeElement instanceof HTMLElement?document.activeElement:null)):e===`closed`&&(this.previouslyFocusedElement?.focus(),this.config.removeOnClosed&&this.store.dialogs.delete(this.id),this.previouslyFocusedElement=null))}},m=class{dialogs=new Map;get openDialogs(){return[...this.dialogs.values()].filter(e=>e.state!==`closed`).sort((e,t)=>e.openedAt-t.openedAt)}get activeDialog(){return this.openDialogs.at(-1)}constructor(){i(this)}add(e){let t=new p(this,e);return this.dialogs.set(t.id,t),t}open(...e){let[t,n]=e;return this.add({component:t,props:n,removeOnClosed:!0,initialState:`opening`})}close(e){this.activeDialog?.close(e)}};const h=e=>{let t=r(e);return t.current||=new m,t.current},g=e(({store:e})=>{let t=h(e);return a(c,{value:t,children:t.openDialogs.map(e=>a(_,{dialog:e},e.id))})}),_=e(({dialog:e})=>{let{component:t,props:n}=e;return a(f,{value:e,children:a(t,{...n})})});export{p as DialogModel,f as DialogProvider,m as DialogStore,c as DialogStoreProvider,_ as MobxDialog,g as MobxDialogs,l as dialogContext,o as dialogStoreContext,u as useDialogContext,d as useDialogContextIfAvailable,s as useDialogStore,h as useDialogs};
1
+ import { observer } from "mobx-react-lite";
2
+ import { createContext, useContext, useRef } from "react";
3
+ import { makeAutoObservable } from "mobx";
4
+ import { jsx } from "react/jsx-runtime";
5
+
6
+ //#region src/dialog/dialog.context.ts
7
+ const dialogStoreContext = createContext(void 0);
8
+ const useDialogStore = () => {
9
+ const context = useContext(dialogStoreContext);
10
+ if (!context) throw new Error("Dialog store context not available. Are you using the <Dialogs /> component?");
11
+ return context;
12
+ };
13
+ const DialogStoreProvider = dialogStoreContext.Provider;
14
+ const dialogContext = createContext(void 0);
15
+ const useDialogContext = () => {
16
+ const context = useContext(dialogContext);
17
+ if (!context) throw new Error("Dialog context not available. Are you using <DialogProvider /> ?");
18
+ return context;
19
+ };
20
+ const useDialogContextIfAvailable = () => useContext(dialogContext);
21
+ const DialogProvider = dialogContext.Provider;
22
+
23
+ //#endregion
24
+ //#region src/dialog/dialog.model.ts
25
+ var DialogModel = class {
26
+ id;
27
+ component;
28
+ props;
29
+ openedAt = 0;
30
+ previouslyFocusedElement = null;
31
+ state;
32
+ constructor(store, config) {
33
+ this.store = store;
34
+ this.config = config;
35
+ this.id = config.id ?? btoa(Math.random().toString()).substring(3, 12);
36
+ this.component = config.component;
37
+ this.props = config.props;
38
+ this.setState(config.initialState ?? "closed");
39
+ makeAutoObservable(this, {
40
+ id: false,
41
+ component: false,
42
+ props: false,
43
+ openedAt: false,
44
+ previouslyFocusedElement: false
45
+ });
46
+ }
47
+ close(closeImmediately) {
48
+ this.setState(closeImmediately ? "closed" : "closing");
49
+ }
50
+ open(openImmediately) {
51
+ this.setState(openImmediately ? "opened" : "opening");
52
+ }
53
+ setState(state) {
54
+ if (this.state === state) return;
55
+ this.state = state;
56
+ if (state === "opened" || state === "opening") {
57
+ this.openedAt = Date.now();
58
+ if (state === "opening") this.previouslyFocusedElement = document.activeElement instanceof HTMLElement ? document.activeElement : null;
59
+ } else if (state === "closed") {
60
+ this.previouslyFocusedElement?.focus();
61
+ if (this.config.removeOnClosed) this.store.dialogs.delete(this.id);
62
+ this.previouslyFocusedElement = null;
63
+ }
64
+ }
65
+ };
66
+
67
+ //#endregion
68
+ //#region src/dialog/dialog.store.ts
69
+ var DialogStore = class {
70
+ dialogs = /* @__PURE__ */ new Map();
71
+ get openDialogs() {
72
+ return [...this.dialogs.values()].filter((d) => d.state !== "closed").sort((d1, d2) => d1.openedAt - d2.openedAt);
73
+ }
74
+ get activeDialog() {
75
+ return this.openDialogs.at(-1);
76
+ }
77
+ constructor() {
78
+ makeAutoObservable(this);
79
+ }
80
+ add(config) {
81
+ const dialogModel = new DialogModel(this, config);
82
+ this.dialogs.set(dialogModel.id, dialogModel);
83
+ return dialogModel;
84
+ }
85
+ open(...args) {
86
+ const [component, props] = args;
87
+ return this.add({
88
+ component,
89
+ props,
90
+ removeOnClosed: true,
91
+ initialState: "opening"
92
+ });
93
+ }
94
+ close(closeImmediately) {
95
+ this.activeDialog?.close(closeImmediately);
96
+ }
97
+ };
98
+
99
+ //#endregion
100
+ //#region src/dialog/use-dialogs.ts
101
+ const useDialogs = (store) => {
102
+ const storeRef = useRef(store);
103
+ if (!storeRef.current) storeRef.current = new DialogStore();
104
+ return storeRef.current;
105
+ };
106
+
107
+ //#endregion
108
+ //#region src/dialog/components/dialogs.tsx
109
+ const MobxDialogs = observer(({ store }) => {
110
+ const dialogStore = useDialogs(store);
111
+ return /* @__PURE__ */ jsx(DialogStoreProvider, {
112
+ value: dialogStore,
113
+ children: dialogStore.openDialogs.map((dialog) => {
114
+ return /* @__PURE__ */ jsx(MobxDialog, { dialog }, dialog.id);
115
+ })
116
+ });
117
+ });
118
+ const MobxDialog = observer(({ dialog }) => {
119
+ const { component: DialogComponent, props: dialogProps } = dialog;
120
+ return /* @__PURE__ */ jsx(DialogProvider, {
121
+ value: dialog,
122
+ children: /* @__PURE__ */ jsx(DialogComponent, { ...dialogProps })
123
+ });
124
+ });
125
+
126
+ //#endregion
127
+ export { DialogModel, DialogProvider, DialogStore, DialogStoreProvider, MobxDialog, MobxDialogs, dialogContext, dialogStoreContext, useDialogContext, useDialogContextIfAvailable, useDialogStore, useDialogs };
2
128
  //# sourceMappingURL=dialog.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.mjs","names":[],"sources":["../src/dialog/dialog.context.ts","../src/dialog/dialog.model.ts","../src/dialog/dialog.store.ts","../src/dialog/use-dialogs.ts","../src/dialog/components/dialogs.tsx"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { DialogModel } from \"./dialog.model\";\nimport type { DialogStore } from \"./dialog.store\";\n\nexport const dialogStoreContext = createContext<DialogStore | undefined>(undefined);\nexport const useDialogStore = () => {\n const context = useContext(dialogStoreContext);\n if (!context) {\n throw new Error(\"Dialog store context not available. Are you using the <Dialogs /> component?\");\n }\n return context;\n};\n\nexport const DialogStoreProvider = dialogStoreContext.Provider;\n\nexport const dialogContext = createContext<DialogModel | undefined>(undefined);\nexport const useDialogContext = () => {\n const context = useContext(dialogContext);\n if (!context) {\n throw new Error(\"Dialog context not available. Are you using <DialogProvider /> ?\");\n }\n return context;\n};\n\nexport const useDialogContextIfAvailable = () => useContext(dialogContext);\n\nexport const DialogProvider = dialogContext.Provider;\n","import { makeAutoObservable } from \"mobx\";\nimport type { DialogStore } from \"./dialog.store\";\nimport type { AnyComponent, DialogModelConfig, DialogState } from \"./dialog.types\";\n\nexport class DialogModel {\n readonly id: string;\n readonly component: AnyComponent;\n readonly props?: React.ComponentProps<AnyComponent>;\n\n openedAt = 0;\n previouslyFocusedElement: HTMLElement | null = null;\n\n state!: DialogState;\n\n constructor(\n readonly store: DialogStore,\n readonly config: DialogModelConfig,\n ) {\n this.id = config.id ?? btoa(Math.random().toString()).substring(3, 12);\n this.component = config.component;\n this.props = config.props;\n\n this.setState(config.initialState ?? \"closed\");\n\n makeAutoObservable(this, {\n id: false,\n component: false,\n props: false,\n openedAt: false,\n previouslyFocusedElement: false,\n });\n }\n\n close(closeImmediately?: boolean): void {\n this.setState(closeImmediately ? \"closed\" : \"closing\");\n }\n\n open(openImmediately?: boolean): void {\n this.setState(openImmediately ? \"opened\" : \"opening\");\n }\n\n setState(state: DialogState): void {\n if (this.state === state) return;\n\n this.state = state;\n if (state === \"opened\" || state === \"opening\") {\n this.openedAt = Date.now();\n if (state === \"opening\") {\n this.previouslyFocusedElement =\n document.activeElement instanceof HTMLElement ? document.activeElement : null;\n }\n } else if (state === \"closed\") {\n this.previouslyFocusedElement?.focus();\n if (this.config.removeOnClosed) {\n this.store.dialogs.delete(this.id);\n }\n this.previouslyFocusedElement = null;\n }\n }\n}\n","import { makeAutoObservable } from \"mobx\";\nimport { DialogModel } from \"./dialog.model\";\nimport type { AnyComponent, DialogComponentAndProps, DialogModelConfig } from \"./dialog.types\";\n\nexport class DialogStore {\n dialogs = new Map<string, DialogModel>();\n\n get openDialogs(): DialogModel[] {\n return [...this.dialogs.values()]\n .filter((d) => d.state !== \"closed\")\n .sort((d1, d2) => d1.openedAt - d2.openedAt);\n }\n\n get activeDialog(): DialogModel | undefined {\n return this.openDialogs.at(-1);\n }\n\n constructor() {\n makeAutoObservable(this);\n }\n\n add(config: DialogModelConfig): DialogModel {\n const dialogModel = new DialogModel(this, config);\n this.dialogs.set(dialogModel.id, dialogModel);\n return dialogModel;\n }\n\n open<C extends AnyComponent>(...args: DialogComponentAndProps<C>): DialogModel {\n const [component, props] = args;\n return this.add({\n component,\n props,\n removeOnClosed: true,\n initialState: \"opening\",\n });\n }\n\n close(closeImmediately?: boolean): void {\n this.activeDialog?.close(closeImmediately);\n }\n}\n","import { useRef } from \"react\";\nimport { DialogStore } from \"./dialog.store\";\n\nexport const useDialogs = (store?: DialogStore): DialogStore => {\n const storeRef = useRef<DialogStore | undefined>(store);\n if (!storeRef.current) {\n storeRef.current = new DialogStore();\n }\n return storeRef.current;\n};\n","import { observer } from \"mobx-react-lite\";\nimport { DialogProvider, DialogStoreProvider } from \"../dialog.context\";\nimport type { DialogModel } from \"../dialog.model\";\nimport type { DialogStore } from \"../dialog.store\";\nimport { useDialogs } from \"../use-dialogs\";\n\nexport interface MobxDialogsProps {\n store?: DialogStore;\n}\n\nexport const MobxDialogs = observer(({ store }: MobxDialogsProps) => {\n const dialogStore = useDialogs(store);\n\n return (\n <DialogStoreProvider value={dialogStore}>\n {dialogStore.openDialogs.map((dialog) => {\n return <MobxDialog key={dialog.id} dialog={dialog} />;\n })}\n </DialogStoreProvider>\n );\n});\n\nexport interface MobxDialogProps {\n dialog: DialogModel;\n}\n\nexport const MobxDialog = observer(({ dialog }: MobxDialogProps) => {\n const { component: DialogComponent, props: dialogProps } = dialog;\n return (\n <DialogProvider value={dialog}>\n <DialogComponent {...dialogProps} />\n </DialogProvider>\n );\n});\n"],"mappings":"+LAIA,MAAa,EAAqB,EAAuC,IAAA,GAAU,CACtE,MAAuB,CAClC,IAAM,EAAU,EAAW,EAAmB,CAC9C,GAAI,CAAC,EACH,MAAU,MAAM,+EAA+E,CAEjG,OAAO,GAGI,EAAsB,EAAmB,SAEzC,EAAgB,EAAuC,IAAA,GAAU,CACjE,MAAyB,CACpC,IAAM,EAAU,EAAW,EAAc,CACzC,GAAI,CAAC,EACH,MAAU,MAAM,mEAAmE,CAErF,OAAO,GAGI,MAAoC,EAAW,EAAc,CAE7D,EAAiB,EAAc,SCtB5C,IAAa,EAAb,KAAyB,CACvB,GACA,UACA,MAEA,SAAW,EACX,yBAA+C,KAE/C,MAEA,YACE,EACA,EACA,CAFS,KAAA,MAAA,EACA,KAAA,OAAA,EAET,KAAK,GAAK,EAAO,IAAM,KAAK,KAAK,QAAQ,CAAC,UAAU,CAAC,CAAC,UAAU,EAAG,GAAG,CACtE,KAAK,UAAY,EAAO,UACxB,KAAK,MAAQ,EAAO,MAEpB,KAAK,SAAS,EAAO,cAAgB,SAAS,CAE9C,EAAmB,KAAM,CACvB,GAAI,GACJ,UAAW,GACX,MAAO,GACP,SAAU,GACV,yBAA0B,GAC3B,CAAC,CAGJ,MAAM,EAAkC,CACtC,KAAK,SAAS,EAAmB,SAAW,UAAU,CAGxD,KAAK,EAAiC,CACpC,KAAK,SAAS,EAAkB,SAAW,UAAU,CAGvD,SAAS,EAA0B,CAC7B,KAAK,QAAU,IAEnB,KAAK,MAAQ,EACT,IAAU,UAAY,IAAU,WAClC,KAAK,SAAW,KAAK,KAAK,CACtB,IAAU,YACZ,KAAK,yBACH,SAAS,yBAAyB,YAAc,SAAS,cAAgB,OAEpE,IAAU,WACnB,KAAK,0BAA0B,OAAO,CAClC,KAAK,OAAO,gBACd,KAAK,MAAM,QAAQ,OAAO,KAAK,GAAG,CAEpC,KAAK,yBAA2B,SCpDzB,EAAb,KAAyB,CACvB,QAAU,IAAI,IAEd,IAAI,aAA6B,CAC/B,MAAO,CAAC,GAAG,KAAK,QAAQ,QAAQ,CAAC,CAC9B,OAAQ,GAAM,EAAE,QAAU,SAAS,CACnC,MAAM,EAAI,IAAO,EAAG,SAAW,EAAG,SAAS,CAGhD,IAAI,cAAwC,CAC1C,OAAO,KAAK,YAAY,GAAG,GAAG,CAGhC,aAAc,CACZ,EAAmB,KAAK,CAG1B,IAAI,EAAwC,CAC1C,IAAM,EAAc,IAAI,EAAY,KAAM,EAAO,CAEjD,OADA,KAAK,QAAQ,IAAI,EAAY,GAAI,EAAY,CACtC,EAGT,KAA6B,GAAG,EAA+C,CAC7E,GAAM,CAAC,EAAW,GAAS,EAC3B,OAAO,KAAK,IAAI,CACd,YACA,QACA,eAAgB,GAChB,aAAc,UACf,CAAC,CAGJ,MAAM,EAAkC,CACtC,KAAK,cAAc,MAAM,EAAiB,GCnC9C,MAAa,EAAc,GAAqC,CAC9D,IAAM,EAAW,EAAgC,EAAM,CAIvD,MAHA,CACE,EAAS,UAAU,IAAI,EAElB,EAAS,SCEL,EAAc,GAAU,CAAE,WAA8B,CACnE,IAAM,EAAc,EAAW,EAAM,CAErC,OACE,EAAC,EAAD,CAAqB,MAAO,WACzB,EAAY,YAAY,IAAK,GACrB,EAAC,EAAD,CAAoC,SAAU,CAA7B,EAAO,GAAsB,CACrD,CACkB,CAAA,EAExB,CAMW,EAAa,GAAU,CAAE,YAA8B,CAClE,GAAM,CAAE,UAAW,EAAiB,MAAO,GAAgB,EAC3D,OACE,EAAC,EAAD,CAAgB,MAAO,WACrB,EAAC,EAAD,CAAiB,GAAI,EAAe,CAAA,CACrB,CAAA,EAEnB"}
1
+ {"version":3,"file":"dialog.mjs","names":[],"sources":["../src/dialog/dialog.context.ts","../src/dialog/dialog.model.ts","../src/dialog/dialog.store.ts","../src/dialog/use-dialogs.ts","../src/dialog/components/dialogs.tsx"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { DialogModel } from \"./dialog.model\";\nimport type { DialogStore } from \"./dialog.store\";\n\nexport const dialogStoreContext = createContext<DialogStore | undefined>(undefined);\nexport const useDialogStore = () => {\n const context = useContext(dialogStoreContext);\n if (!context) {\n throw new Error(\"Dialog store context not available. Are you using the <Dialogs /> component?\");\n }\n return context;\n};\n\nexport const DialogStoreProvider = dialogStoreContext.Provider;\n\nexport const dialogContext = createContext<DialogModel | undefined>(undefined);\nexport const useDialogContext = () => {\n const context = useContext(dialogContext);\n if (!context) {\n throw new Error(\"Dialog context not available. Are you using <DialogProvider /> ?\");\n }\n return context;\n};\n\nexport const useDialogContextIfAvailable = () => useContext(dialogContext);\n\nexport const DialogProvider = dialogContext.Provider;\n","import { makeAutoObservable } from \"mobx\";\nimport type { DialogStore } from \"./dialog.store\";\nimport type { AnyComponent, DialogModelConfig, DialogState } from \"./dialog.types\";\n\nexport class DialogModel {\n readonly id: string;\n readonly component: AnyComponent;\n readonly props?: React.ComponentProps<AnyComponent>;\n\n openedAt = 0;\n previouslyFocusedElement: HTMLElement | null = null;\n\n state!: DialogState;\n\n constructor(\n readonly store: DialogStore,\n readonly config: DialogModelConfig,\n ) {\n this.id = config.id ?? btoa(Math.random().toString()).substring(3, 12);\n this.component = config.component;\n this.props = config.props;\n\n this.setState(config.initialState ?? \"closed\");\n\n makeAutoObservable(this, {\n id: false,\n component: false,\n props: false,\n openedAt: false,\n previouslyFocusedElement: false,\n });\n }\n\n close(closeImmediately?: boolean): void {\n this.setState(closeImmediately ? \"closed\" : \"closing\");\n }\n\n open(openImmediately?: boolean): void {\n this.setState(openImmediately ? \"opened\" : \"opening\");\n }\n\n setState(state: DialogState): void {\n if (this.state === state) return;\n\n this.state = state;\n if (state === \"opened\" || state === \"opening\") {\n this.openedAt = Date.now();\n if (state === \"opening\") {\n this.previouslyFocusedElement =\n document.activeElement instanceof HTMLElement ? document.activeElement : null;\n }\n } else if (state === \"closed\") {\n this.previouslyFocusedElement?.focus();\n if (this.config.removeOnClosed) {\n this.store.dialogs.delete(this.id);\n }\n this.previouslyFocusedElement = null;\n }\n }\n}\n","import { makeAutoObservable } from \"mobx\";\nimport { DialogModel } from \"./dialog.model\";\nimport type { AnyComponent, DialogComponentAndProps, DialogModelConfig } from \"./dialog.types\";\n\nexport class DialogStore {\n dialogs = new Map<string, DialogModel>();\n\n get openDialogs(): DialogModel[] {\n return [...this.dialogs.values()]\n .filter((d) => d.state !== \"closed\")\n .sort((d1, d2) => d1.openedAt - d2.openedAt);\n }\n\n get activeDialog(): DialogModel | undefined {\n return this.openDialogs.at(-1);\n }\n\n constructor() {\n makeAutoObservable(this);\n }\n\n add(config: DialogModelConfig): DialogModel {\n const dialogModel = new DialogModel(this, config);\n this.dialogs.set(dialogModel.id, dialogModel);\n return dialogModel;\n }\n\n open<C extends AnyComponent>(...args: DialogComponentAndProps<C>): DialogModel {\n const [component, props] = args;\n return this.add({\n component,\n props,\n removeOnClosed: true,\n initialState: \"opening\",\n });\n }\n\n close(closeImmediately?: boolean): void {\n this.activeDialog?.close(closeImmediately);\n }\n}\n","import { useRef } from \"react\";\nimport { DialogStore } from \"./dialog.store\";\n\nexport const useDialogs = (store?: DialogStore): DialogStore => {\n const storeRef = useRef<DialogStore | undefined>(store);\n if (!storeRef.current) {\n storeRef.current = new DialogStore();\n }\n return storeRef.current;\n};\n","import { observer } from \"mobx-react-lite\";\nimport { DialogProvider, DialogStoreProvider } from \"../dialog.context\";\nimport type { DialogModel } from \"../dialog.model\";\nimport type { DialogStore } from \"../dialog.store\";\nimport { useDialogs } from \"../use-dialogs\";\n\nexport interface MobxDialogsProps {\n store?: DialogStore;\n}\n\nexport const MobxDialogs = observer(({ store }: MobxDialogsProps) => {\n const dialogStore = useDialogs(store);\n\n return (\n <DialogStoreProvider value={dialogStore}>\n {dialogStore.openDialogs.map((dialog) => {\n return <MobxDialog key={dialog.id} dialog={dialog} />;\n })}\n </DialogStoreProvider>\n );\n});\n\nexport interface MobxDialogProps {\n dialog: DialogModel;\n}\n\nexport const MobxDialog = observer(({ dialog }: MobxDialogProps) => {\n const { component: DialogComponent, props: dialogProps } = dialog;\n return (\n <DialogProvider value={dialog}>\n <DialogComponent {...dialogProps} />\n </DialogProvider>\n );\n});\n"],"mappings":"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"}
package/dist/form.mjs CHANGED
@@ -1,2 +1,173 @@
1
- import{createContext as e,forwardRef as t,useContext as n,useRef as r}from"react";import{makeAutoObservable as i,toJS as a}from"mobx";import{jsx as o}from"react/jsx-runtime";import s from"typebox/format";import{Type as c}from"typebox";import l from"typebox/schema";import u from"typebox/value";const d=e(void 0),f=()=>{let e=n(d);if(!e)throw Error(`Form context not available. Make sure you are within the <Form> component or providing the form context manually.`);return e},p=d.Provider,m=()=>n(d),h=t(function({form:e,children:t,...n},r){return o(p,{value:e,children:o(`form`,{noValidate:!0,...n,ref:r,...e.props(),children:t})})});var g=class{name;schema;config;validator;value;touched=!1;get valid(){return this.value===void 0&&c.IsOptional(this.schema)?!0:this.validator.Check(this.value)}get errorMessage(){if(this.valid||!this.touched)return``;let[e,t]=this.validator.Errors(this.value);return t.at(0)?.message??``}constructor(e){i(this,{name:!1,schema:!1,config:!1,validator:!1}),this.config=e,this.name=e.name,this.schema=e.schema,this.validator=l.Compile(this.schema),this.setValue(e.initialValue)}setValue(e){this.value=u.Convert(this.schema,e)}setTouched(e){this.touched=e}reset(){this.setTouched(!1),this.setValue(this.config.initialValue)}props(){return{name:this.name,onChange:e=>this.setValue(e),value:this.value,onBlur:()=>{this.setTouched(!0)}}}toJSON(){return a(this.value)}};s.Set(`password`,()=>!0),s.Set(`phone`,()=>!0);var _=class{fields;config;schema;submitting=!1;submitted=!1;submitError;constructor(e,t){this.schema=e,this.config=t,i(this,{fields:!1,schema:!1,config:!1}),this.fields=Object.entries(e.properties).reduce((e,[n,r])=>(e[n]=new g({name:n,schema:r,initialValue:t?.initialValues?.[n]}),e),{})}get valid(){return Object.values(this.fields).every(e=>e.valid)}props(){return{onSubmit:e=>{e.preventDefault(),this.setSubmitError(void 0),this.validate()&&(this.setSubmitting(!0),this.config.handleSubmit(this.toJSON()).then(e=>(this.setSubmitted(!0),e)).catch(e=>{this.setSubmitError(e)}).finally(()=>{this.setSubmitting(!1)}))}}}setSubmitError(e){this.submitError=e}setSubmitting(e){this.submitting=e}setSubmitted(e){this.submitted=e}reset(){this.setSubmitError(void 0);for(let e of Object.values(this.fields))e.reset()}validate(){for(let e of Object.values(this.fields))e.setTouched(!0);return this.valid}toJSON(){return Object.values(this.fields).reduce((e,t)=>(e[t.name]=t.value,e),{})}};const v=(e,t)=>{let n=r(void 0);return n.current?Object.assign(n.current.config,t):n.current=new _(e,t),n.current};export{g as FormFieldModel,_ as FormModel,p as FormProvider,h as MobxForm,d as formContext,v as useForm,f as useFormContext,m as useFormContextIfAvailable};
1
+ import { createContext, forwardRef, useContext, useRef } from "react";
2
+ import { makeAutoObservable, toJS } from "mobx";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import Format from "typebox/format";
5
+ import { Type } from "typebox";
6
+ import Schema, { Validator } from "typebox/schema";
7
+ import Value from "typebox/value";
8
+
9
+ //#region src/form/form.context.ts
10
+ const formContext = createContext(void 0);
11
+ const useFormContext = () => {
12
+ const context = useContext(formContext);
13
+ if (!context) throw new Error("Form context not available. Make sure you are within the <Form> component or providing the form context manually.");
14
+ return context;
15
+ };
16
+ const FormProvider = formContext.Provider;
17
+ const useFormContextIfAvailable = () => useContext(formContext);
18
+
19
+ //#endregion
20
+ //#region src/form/components/form.tsx
21
+ const MobxForm = forwardRef(function MobxForm({ form, children, ...formProps }, ref) {
22
+ return /* @__PURE__ */ jsx(FormProvider, {
23
+ value: form,
24
+ children: /* @__PURE__ */ jsx("form", {
25
+ noValidate: true,
26
+ ...formProps,
27
+ ref,
28
+ ...form.props(),
29
+ children
30
+ })
31
+ });
32
+ });
33
+
34
+ //#endregion
35
+ //#region src/form/form-field.model.ts
36
+ var FormFieldModel = class {
37
+ name;
38
+ schema;
39
+ config;
40
+ validator;
41
+ value;
42
+ touched = false;
43
+ get valid() {
44
+ if (this.value === void 0 && Type.IsOptional(this.schema)) return true;
45
+ return this.validator.Check(this.value);
46
+ }
47
+ get errorMessage() {
48
+ if (this.valid || !this.touched) return "";
49
+ const [_, errors] = this.validator.Errors(this.value);
50
+ return errors.at(0)?.message ?? "";
51
+ }
52
+ constructor(config) {
53
+ makeAutoObservable(this, {
54
+ name: false,
55
+ schema: false,
56
+ config: false,
57
+ validator: false
58
+ });
59
+ this.config = config;
60
+ this.name = config.name;
61
+ this.schema = config.schema;
62
+ this.validator = Schema.Compile(this.schema);
63
+ this.setValue(config.initialValue);
64
+ }
65
+ setValue(value) {
66
+ this.value = Value.Convert(this.schema, value);
67
+ }
68
+ setTouched(touched) {
69
+ this.touched = touched;
70
+ }
71
+ reset() {
72
+ this.setTouched(false);
73
+ this.setValue(this.config.initialValue);
74
+ }
75
+ props() {
76
+ return {
77
+ name: this.name,
78
+ onChange: (v) => this.setValue(v),
79
+ value: this.value,
80
+ onBlur: () => {
81
+ this.setTouched(true);
82
+ }
83
+ };
84
+ }
85
+ toJSON() {
86
+ return toJS(this.value);
87
+ }
88
+ };
89
+
90
+ //#endregion
91
+ //#region src/form/form.model.ts
92
+ Format.Set("password", () => true);
93
+ Format.Set("phone", () => true);
94
+ var FormModel = class {
95
+ fields;
96
+ config;
97
+ schema;
98
+ submitting = false;
99
+ submitted = false;
100
+ submitError;
101
+ constructor(schema, config) {
102
+ this.schema = schema;
103
+ this.config = config;
104
+ makeAutoObservable(this, {
105
+ fields: false,
106
+ schema: false,
107
+ config: false
108
+ });
109
+ this.fields = Object.entries(schema.properties).reduce((fields, [fieldName, fieldSchema]) => {
110
+ fields[fieldName] = new FormFieldModel({
111
+ name: fieldName,
112
+ schema: fieldSchema,
113
+ initialValue: config?.initialValues?.[fieldName]
114
+ });
115
+ return fields;
116
+ }, {});
117
+ }
118
+ get valid() {
119
+ return Object.values(this.fields).every((field) => field.valid);
120
+ }
121
+ props() {
122
+ return { onSubmit: (e) => {
123
+ e.preventDefault();
124
+ this.setSubmitError(void 0);
125
+ if (!this.validate()) return;
126
+ this.setSubmitting(true);
127
+ this.config.handleSubmit(this.toJSON()).then((resp) => {
128
+ this.setSubmitted(true);
129
+ return resp;
130
+ }).catch((e) => {
131
+ this.setSubmitError(e);
132
+ }).finally(() => {
133
+ this.setSubmitting(false);
134
+ });
135
+ } };
136
+ }
137
+ setSubmitError(error) {
138
+ this.submitError = error;
139
+ }
140
+ setSubmitting(submitting) {
141
+ this.submitting = submitting;
142
+ }
143
+ setSubmitted(submitted) {
144
+ this.submitted = submitted;
145
+ }
146
+ reset() {
147
+ this.setSubmitError(void 0);
148
+ for (const field of Object.values(this.fields)) field.reset();
149
+ }
150
+ validate() {
151
+ for (const field of Object.values(this.fields)) field.setTouched(true);
152
+ return this.valid;
153
+ }
154
+ toJSON() {
155
+ return Object.values(this.fields).reduce((fields, field) => {
156
+ fields[field.name] = field.value;
157
+ return fields;
158
+ }, {});
159
+ }
160
+ };
161
+
162
+ //#endregion
163
+ //#region src/form/use-form.ts
164
+ const useForm = (schema, config) => {
165
+ const formRef = useRef(void 0);
166
+ if (formRef.current) Object.assign(formRef.current.config, config);
167
+ else formRef.current = new FormModel(schema, config);
168
+ return formRef.current;
169
+ };
170
+
171
+ //#endregion
172
+ export { FormFieldModel, FormModel, FormProvider, MobxForm, formContext, useForm, useFormContext, useFormContextIfAvailable };
2
173
  //# sourceMappingURL=form.mjs.map
package/dist/form.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"form.mjs","names":[],"sources":["../src/form/form.context.ts","../src/form/components/form.tsx","../src/form/form-field.model.ts","../src/form/form.model.ts","../src/form/use-form.ts"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { FormModel } from \"./form.model\";\n\nexport const formContext = createContext<FormModel | undefined>(undefined);\nexport const useFormContext = () => {\n const context = useContext(formContext);\n if (!context) {\n throw new Error(\n \"Form context not available. Make sure you are within the <Form> component or providing the form context manually.\",\n );\n }\n return context;\n};\n\nexport const FormProvider = formContext.Provider;\n\nexport const useFormContextIfAvailable = () => useContext(formContext);\n","import { forwardRef } from \"react\";\nimport { FormProvider } from \"../form.context\";\nimport type { FormModel } from \"../form.model\";\n\nexport interface MobxFormProps extends Omit<\n React.HTMLProps<HTMLFormElement>,\n \"form\" | \"action\" | \"method\"\n> {\n form: FormModel<any>;\n}\n\nexport const MobxForm = forwardRef(function MobxForm(\n { form, children, ...formProps }: MobxFormProps,\n ref,\n) {\n return (\n <FormProvider value={form}>\n <form noValidate {...formProps} ref={ref} {...form.props()}>\n {children}\n </form>\n </FormProvider>\n );\n});\n","import Schema, { Validator } from \"typebox/schema\";\nimport Value from \"typebox/value\";\nimport { type Static, type TSchema, Type } from \"typebox\";\nimport { makeAutoObservable, toJS } from \"mobx\";\nimport type { FormFieldConfig } from \"./form.types\";\n\n// TODO: should infer required prop\n// TODO: should also allow for \"id\", defaulting to name\n// TODO: probably shouldn't assume value is of correct type, maybe unknown?\n\nexport class FormFieldModel<T extends TSchema = TSchema> {\n readonly name: string;\n readonly schema: T;\n readonly config: FormFieldConfig<T>;\n readonly validator: Validator<T>;\n\n value: Static<T> | undefined;\n touched = false;\n\n get valid(): boolean {\n if (this.value === undefined && Type.IsOptional(this.schema)) return true;\n return this.validator.Check(this.value);\n }\n\n get errorMessage(): string {\n if (this.valid || !this.touched) return \"\";\n // TODO: revisit this, now that .Errors returns success/fail\n const [_, errors] = this.validator.Errors(this.value);\n return errors.at(0)?.message ?? \"\";\n }\n\n constructor(config: FormFieldConfig<T>) {\n makeAutoObservable(this, {\n name: false,\n schema: false,\n config: false,\n validator: false,\n });\n\n this.config = config;\n this.name = config.name;\n this.schema = config.schema;\n this.validator = Schema.Compile(this.schema);\n this.setValue(config.initialValue);\n }\n\n setValue(value?: Static<T>) {\n // TODO: does this still make sense? If value is invalid,\n // then type will be wrong...revisit this\n this.value = Value.Convert(this.schema, value) as Static<T>;\n }\n\n setTouched(touched: boolean) {\n this.touched = touched;\n }\n\n reset() {\n this.setTouched(false);\n this.setValue(this.config.initialValue);\n }\n\n // TODO: this any type is dangerous, need to figure out a good way\n // to make this work OTTB for most form controls, while allowing\n // some kind of escape hatch for special cases\n props(): any {\n return {\n name: this.name,\n onChange: (v?: Static<T>) => this.setValue(v),\n value: this.value,\n onBlur: () => {\n this.setTouched(true);\n },\n };\n }\n\n toJSON(): Static<T> | undefined {\n return toJS(this.value);\n }\n}\n","import Format from \"typebox/format\";\nimport { type Static, type TObject, type TSchema } from \"typebox\";\nimport { makeAutoObservable } from \"mobx\";\nimport type { FormConfig, FormFields } from \"./form.types\";\nimport { FormFieldModel } from \"./form-field.model\";\n\n// should these be here?\nFormat.Set(\"password\", () => true);\nFormat.Set(\"phone\", () => true);\n\n/*\nSetErrorFunction((param) => {\n const { schema, value } = param;\n\n const message =\n typeof schema.errorMessage === \"function\" ? schema.errorMessage(param) : schema.errorMessage;\n\n if (message !== undefined) return message;\n\n if ((value === undefined || value === \"\") && !TypeGuard.IsOptional(schema)) {\n return \"This field is required.\";\n }\n\n if (Type.IsString(schema)) {\n if (Schema.IsFormat(schema) && schema.format === \"email\")\n return \"Please enter a valid email address.\";\n }\n\n return DefaultErrorFunction(param);\n});\n*/\n\n// consider using enumerable to allow spreading of\n// field model instead of calling props() unless we need\n// to pass data to props...\n\n// TODO:\n// handleSubmit should catch a special MobxFormError\n// that can set field-level errors from an API response\n\nexport class FormModel<T extends TObject = TObject> {\n readonly fields: FormFields<T>;\n readonly config: FormConfig<T>;\n readonly schema: T;\n\n // TODO: maybe refactor into a \"state\" property?\n submitting = false;\n submitted = false;\n\n // TODO: what should this type be? should it be generic?\n submitError: any;\n\n constructor(schema: T, config: FormConfig<T>) {\n this.schema = schema;\n this.config = config;\n\n makeAutoObservable(this, { fields: false, schema: false, config: false });\n\n this.fields = Object.entries(schema.properties).reduce(\n (fields, [fieldName, fieldSchema]) => {\n fields[fieldName] = new FormFieldModel({\n name: fieldName,\n schema: fieldSchema,\n initialValue: config?.initialValues?.[fieldName],\n });\n return fields;\n },\n {} as Record<string, FormFieldModel<TSchema>>,\n ) as FormFields<T>;\n }\n\n get valid(): boolean {\n return Object.values(this.fields).every((field) => field.valid);\n }\n\n props(): any {\n return {\n onSubmit: (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n\n this.setSubmitError(undefined);\n\n if (!this.validate()) {\n return;\n }\n\n this.setSubmitting(true);\n this.config\n .handleSubmit(this.toJSON() as Static<T>)\n .then((resp) => {\n this.setSubmitted(true);\n return resp;\n })\n .catch((e) => {\n this.setSubmitError(e);\n })\n .finally(() => {\n this.setSubmitting(false);\n });\n },\n };\n }\n\n setSubmitError(error: any): void {\n this.submitError = error;\n }\n\n protected setSubmitting(submitting: boolean): void {\n this.submitting = submitting;\n }\n\n protected setSubmitted(submitted: boolean): void {\n this.submitted = submitted;\n }\n\n reset(): void {\n this.setSubmitError(undefined);\n for (const field of Object.values(this.fields)) {\n field.reset();\n }\n }\n\n validate(): boolean {\n for (const field of Object.values(this.fields)) {\n field.setTouched(true);\n }\n return this.valid;\n }\n\n toJSON(): Partial<Static<T>> {\n return Object.values(this.fields).reduce(\n (fields, field) => {\n fields[field.name] = field.value;\n return fields;\n },\n {} as Partial<Static<T>>,\n );\n }\n}\n","import type { TObject } from \"typebox\";\nimport { useRef } from \"react\";\nimport { FormModel } from \"./form.model\";\nimport type { FormConfig } from \"./form.types\";\n\nexport const useForm = <T extends TObject = TObject>(\n schema: T,\n config: FormConfig<T>,\n): FormModel<T> => {\n const formRef = useRef<FormModel<T>>(undefined);\n if (formRef.current) {\n Object.assign(formRef.current.config, config);\n } else {\n formRef.current = new FormModel<T>(schema, config);\n }\n\n return formRef.current;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"form.mjs","names":[],"sources":["../src/form/form.context.ts","../src/form/components/form.tsx","../src/form/form-field.model.ts","../src/form/form.model.ts","../src/form/use-form.ts"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { FormModel } from \"./form.model\";\n\nexport const formContext = createContext<FormModel | undefined>(undefined);\nexport const useFormContext = () => {\n const context = useContext(formContext);\n if (!context) {\n throw new Error(\n \"Form context not available. Make sure you are within the <Form> component or providing the form context manually.\",\n );\n }\n return context;\n};\n\nexport const FormProvider = formContext.Provider;\n\nexport const useFormContextIfAvailable = () => useContext(formContext);\n","import { forwardRef } from \"react\";\nimport { FormProvider } from \"../form.context\";\nimport type { FormModel } from \"../form.model\";\n\nexport interface MobxFormProps extends Omit<\n React.HTMLProps<HTMLFormElement>,\n \"form\" | \"action\" | \"method\"\n> {\n form: FormModel<any>;\n}\n\nexport const MobxForm = forwardRef(function MobxForm(\n { form, children, ...formProps }: MobxFormProps,\n ref,\n) {\n return (\n <FormProvider value={form}>\n <form noValidate {...formProps} ref={ref} {...form.props()}>\n {children}\n </form>\n </FormProvider>\n );\n});\n","import Schema, { Validator } from \"typebox/schema\";\nimport Value from \"typebox/value\";\nimport { type Static, type TSchema, Type } from \"typebox\";\nimport { makeAutoObservable, toJS } from \"mobx\";\nimport type { FormFieldConfig } from \"./form.types\";\n\n// TODO: should infer required prop\n// TODO: should also allow for \"id\", defaulting to name\n// TODO: probably shouldn't assume value is of correct type, maybe unknown?\n\nexport class FormFieldModel<T extends TSchema = TSchema> {\n readonly name: string;\n readonly schema: T;\n readonly config: FormFieldConfig<T>;\n readonly validator: Validator<T>;\n\n value: Static<T> | undefined;\n touched = false;\n\n get valid(): boolean {\n if (this.value === undefined && Type.IsOptional(this.schema)) return true;\n return this.validator.Check(this.value);\n }\n\n get errorMessage(): string {\n if (this.valid || !this.touched) return \"\";\n // TODO: revisit this, now that .Errors returns success/fail\n const [_, errors] = this.validator.Errors(this.value);\n return errors.at(0)?.message ?? \"\";\n }\n\n constructor(config: FormFieldConfig<T>) {\n makeAutoObservable(this, {\n name: false,\n schema: false,\n config: false,\n validator: false,\n });\n\n this.config = config;\n this.name = config.name;\n this.schema = config.schema;\n this.validator = Schema.Compile(this.schema);\n this.setValue(config.initialValue);\n }\n\n setValue(value?: Static<T>) {\n // TODO: does this still make sense? If value is invalid,\n // then type will be wrong...revisit this\n this.value = Value.Convert(this.schema, value) as Static<T>;\n }\n\n setTouched(touched: boolean) {\n this.touched = touched;\n }\n\n reset() {\n this.setTouched(false);\n this.setValue(this.config.initialValue);\n }\n\n // TODO: this any type is dangerous, need to figure out a good way\n // to make this work OTTB for most form controls, while allowing\n // some kind of escape hatch for special cases\n props(): any {\n return {\n name: this.name,\n onChange: (v?: Static<T>) => this.setValue(v),\n value: this.value,\n onBlur: () => {\n this.setTouched(true);\n },\n };\n }\n\n toJSON(): Static<T> | undefined {\n return toJS(this.value);\n }\n}\n","import Format from \"typebox/format\";\nimport { type Static, type TObject, type TSchema } from \"typebox\";\nimport { makeAutoObservable } from \"mobx\";\nimport type { FormConfig, FormFields } from \"./form.types\";\nimport { FormFieldModel } from \"./form-field.model\";\n\n// should these be here?\nFormat.Set(\"password\", () => true);\nFormat.Set(\"phone\", () => true);\n\n/*\nSetErrorFunction((param) => {\n const { schema, value } = param;\n\n const message =\n typeof schema.errorMessage === \"function\" ? schema.errorMessage(param) : schema.errorMessage;\n\n if (message !== undefined) return message;\n\n if ((value === undefined || value === \"\") && !TypeGuard.IsOptional(schema)) {\n return \"This field is required.\";\n }\n\n if (Type.IsString(schema)) {\n if (Schema.IsFormat(schema) && schema.format === \"email\")\n return \"Please enter a valid email address.\";\n }\n\n return DefaultErrorFunction(param);\n});\n*/\n\n// consider using enumerable to allow spreading of\n// field model instead of calling props() unless we need\n// to pass data to props...\n\n// TODO:\n// handleSubmit should catch a special MobxFormError\n// that can set field-level errors from an API response\n\nexport class FormModel<T extends TObject = TObject> {\n readonly fields: FormFields<T>;\n readonly config: FormConfig<T>;\n readonly schema: T;\n\n // TODO: maybe refactor into a \"state\" property?\n submitting = false;\n submitted = false;\n\n // TODO: what should this type be? should it be generic?\n submitError: any;\n\n constructor(schema: T, config: FormConfig<T>) {\n this.schema = schema;\n this.config = config;\n\n makeAutoObservable(this, { fields: false, schema: false, config: false });\n\n this.fields = Object.entries(schema.properties).reduce(\n (fields, [fieldName, fieldSchema]) => {\n fields[fieldName] = new FormFieldModel({\n name: fieldName,\n schema: fieldSchema,\n initialValue: config?.initialValues?.[fieldName],\n });\n return fields;\n },\n {} as Record<string, FormFieldModel<TSchema>>,\n ) as FormFields<T>;\n }\n\n get valid(): boolean {\n return Object.values(this.fields).every((field) => field.valid);\n }\n\n props(): any {\n return {\n onSubmit: (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n\n this.setSubmitError(undefined);\n\n if (!this.validate()) {\n return;\n }\n\n this.setSubmitting(true);\n this.config\n .handleSubmit(this.toJSON() as Static<T>)\n .then((resp) => {\n this.setSubmitted(true);\n return resp;\n })\n .catch((e) => {\n this.setSubmitError(e);\n })\n .finally(() => {\n this.setSubmitting(false);\n });\n },\n };\n }\n\n setSubmitError(error: any): void {\n this.submitError = error;\n }\n\n protected setSubmitting(submitting: boolean): void {\n this.submitting = submitting;\n }\n\n protected setSubmitted(submitted: boolean): void {\n this.submitted = submitted;\n }\n\n reset(): void {\n this.setSubmitError(undefined);\n for (const field of Object.values(this.fields)) {\n field.reset();\n }\n }\n\n validate(): boolean {\n for (const field of Object.values(this.fields)) {\n field.setTouched(true);\n }\n return this.valid;\n }\n\n toJSON(): Partial<Static<T>> {\n return Object.values(this.fields).reduce(\n (fields, field) => {\n fields[field.name] = field.value;\n return fields;\n },\n {} as Partial<Static<T>>,\n );\n }\n}\n","import type { TObject } from \"typebox\";\nimport { useRef } from \"react\";\nimport { FormModel } from \"./form.model\";\nimport type { FormConfig } from \"./form.types\";\n\nexport const useForm = <T extends TObject = TObject>(\n schema: T,\n config: FormConfig<T>,\n): FormModel<T> => {\n const formRef = useRef<FormModel<T>>(undefined);\n if (formRef.current) {\n Object.assign(formRef.current.config, config);\n } else {\n formRef.current = new FormModel<T>(schema, config);\n }\n\n return formRef.current;\n};\n"],"mappings":"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"}
@@ -1,2 +1,152 @@
1
- import{useCallback as e,useEffect as t,useLayoutEffect as n,useRef as r,useState as i}from"react";const a=(n,a=[],o)=>{let{debounceType:s=`leading`,debounceMs:c=650}=o??{},l=r(void 0),u=r(void 0),[d,f]=i(o?.initialValue?{loading:!1,value:o?.initialValue}:{loading:!0}),p=e((...e)=>{window.clearTimeout(l.current),u.current?.abort();let t=new AbortController;return u.current=t,d?.loading||f(e=>({...e,loading:!0})),new Promise((r,i)=>{let a=s===`leading`&&!l.current?0:c;l.current=window.setTimeout(()=>{n(t.signal,...e).then(e=>{t.signal.aborted||f({value:e,loading:!1}),r(e)},e=>{t.signal.aborted||f({error:e,loading:!1}),i(e)}).finally(()=>{l.current=void 0})},a)})},a);return t(()=>()=>u.current?.abort(),[]),{...d,run:p}},o=(e,n=[],i)=>{let o=r(!0),{runImmediately:s=!0,...c}=i??{},l=a(e,n,c);return t(()=>{let e=o.current;o.current=!1,!(!s&&e)&&l.run()},[l.run]),l};function s(n,i,a){let o=r({...a,timeout:null,mounted:!1,leadingTriggered:a?.leading??!1});return t(()=>{let e=o.current;return e.mounted=!0,e.leadingTriggered=!1,()=>{e.mounted=!1,window.clearTimeout(e.timeout)}},[]),e((...e)=>{let t=o.current;window.clearTimeout(t.timeout),!t.leadingTriggered&&t.leading?(t.leadingTriggered=!0,n(...e),t.timeout=window.setTimeout(()=>{t.leadingTriggered=!0},t.delayMs??1600)):t.timeout=window.setTimeout(()=>{t.mounted&&(n(...e),t.leadingTriggered=!1)},t.delayMs??1600)},i)}function c(e,n,r){return t(s(e,n,{leading:!0,...r}),n)}const l=e=>t(e,[]),u=()=>{let n=r(!1),i=e(()=>n.current,[]);return t(()=>(n.current=!0,()=>{n.current=!1}),[]),i},d=(t,i)=>{let a=r(i),o=e(()=>{t.current&&a.current(t.current.clientWidth,t.current.clientHeight)},[t]);n(()=>{let e=t.current;if(e)if(o(),typeof ResizeObserver==`function`){let t=new ResizeObserver(()=>o());return t.observe(e),()=>t.disconnect()}else return window.addEventListener(`resize`,o),()=>window.window.removeEventListener(`resize`,o)},[t,o])};export{o as useAsync,a as useAsyncFn,s as useDebouncedCallback,c as useDebouncedEffect,l as useMountEffect,u as useMountedState,d as useResizeObserver};
1
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
2
+
3
+ //#region src/react-util/useAsyncFn.ts
4
+ const useAsyncFn = (fn, deps = [], options) => {
5
+ const { debounceType = "leading", debounceMs = 650 } = options ?? {};
6
+ const timeoutRef = useRef(void 0);
7
+ const abortControllerRef = useRef(void 0);
8
+ const [state, setState] = useState(options?.initialValue ? {
9
+ loading: false,
10
+ value: options?.initialValue
11
+ } : { loading: true });
12
+ const run = useCallback((...args) => {
13
+ window.clearTimeout(timeoutRef.current);
14
+ abortControllerRef.current?.abort();
15
+ const abortController = new AbortController();
16
+ abortControllerRef.current = abortController;
17
+ if (!state?.loading) setState((prevState) => ({
18
+ ...prevState,
19
+ loading: true
20
+ }));
21
+ return new Promise((resolve, reject) => {
22
+ const timeoutMs = debounceType === "leading" && !timeoutRef.current ? 0 : debounceMs;
23
+ timeoutRef.current = window.setTimeout(() => {
24
+ fn(abortController.signal, ...args).then((value) => {
25
+ if (!abortController.signal.aborted) setState({
26
+ value,
27
+ loading: false
28
+ });
29
+ resolve(value);
30
+ }, (error) => {
31
+ if (!abortController.signal.aborted) setState({
32
+ error,
33
+ loading: false
34
+ });
35
+ reject(error);
36
+ }).finally(() => {
37
+ timeoutRef.current = void 0;
38
+ });
39
+ }, timeoutMs);
40
+ });
41
+ }, deps);
42
+ useEffect(() => {
43
+ return () => abortControllerRef.current?.abort();
44
+ }, []);
45
+ return {
46
+ ...state,
47
+ run
48
+ };
49
+ };
50
+
51
+ //#endregion
52
+ //#region src/react-util/useAsync.ts
53
+ const useAsync = (fn, deps = [], options) => {
54
+ const firstRun = useRef(true);
55
+ const { runImmediately = true, ...useAsyncFnOptions } = options ?? {};
56
+ const asyncFn = useAsyncFn(fn, deps, useAsyncFnOptions);
57
+ useEffect(() => {
58
+ const isFirstRun = firstRun.current;
59
+ firstRun.current = false;
60
+ if (!runImmediately && isFirstRun) return;
61
+ asyncFn.run();
62
+ }, [asyncFn.run]);
63
+ return asyncFn;
64
+ };
65
+
66
+ //#endregion
67
+ //#region src/react-util/useDebouncedCallback.tsx
68
+ function useDebouncedCallback(callback, deps, options) {
69
+ const ref = useRef({
70
+ ...options,
71
+ timeout: null,
72
+ mounted: false,
73
+ leadingTriggered: options?.leading ?? false
74
+ });
75
+ useEffect(() => {
76
+ const currentRef = ref.current;
77
+ currentRef.mounted = true;
78
+ currentRef.leadingTriggered = false;
79
+ return () => {
80
+ currentRef.mounted = false;
81
+ window.clearTimeout(currentRef.timeout);
82
+ };
83
+ }, []);
84
+ return useCallback((...params) => {
85
+ const currentRef = ref.current;
86
+ window.clearTimeout(currentRef.timeout);
87
+ if (!currentRef.leadingTriggered && currentRef.leading) {
88
+ currentRef.leadingTriggered = true;
89
+ callback(...params);
90
+ currentRef.timeout = window.setTimeout(() => {
91
+ currentRef.leadingTriggered = true;
92
+ }, currentRef.delayMs ?? 1600);
93
+ } else currentRef.timeout = window.setTimeout(() => {
94
+ if (!currentRef.mounted) return;
95
+ callback(...params);
96
+ currentRef.leadingTriggered = false;
97
+ }, currentRef.delayMs ?? 1600);
98
+ }, deps);
99
+ }
100
+
101
+ //#endregion
102
+ //#region src/react-util/useDebouncedEffect.tsx
103
+ function useDebouncedEffect(callback, deps, options) {
104
+ return useEffect(useDebouncedCallback(callback, deps, {
105
+ leading: true,
106
+ ...options
107
+ }), deps);
108
+ }
109
+
110
+ //#endregion
111
+ //#region src/react-util/useMountEffect.tsx
112
+ const useMountEffect = (effectFn) => useEffect(effectFn, []);
113
+
114
+ //#endregion
115
+ //#region src/react-util/useMountedState.ts
116
+ const useMountedState = () => {
117
+ const mountedRef = useRef(false);
118
+ const get = useCallback(() => mountedRef.current, []);
119
+ useEffect(() => {
120
+ mountedRef.current = true;
121
+ return () => {
122
+ mountedRef.current = false;
123
+ };
124
+ }, []);
125
+ return get;
126
+ };
127
+
128
+ //#endregion
129
+ //#region src/react-util/useResizeObserver.tsx
130
+ const useResizeObserver = (ref, onResize) => {
131
+ const onResizeRef = useRef(onResize);
132
+ const handleResize = useCallback(() => {
133
+ if (ref.current) onResizeRef.current(ref.current.clientWidth, ref.current.clientHeight);
134
+ }, [ref]);
135
+ useLayoutEffect(() => {
136
+ const el = ref.current;
137
+ if (!el) return;
138
+ handleResize();
139
+ if (typeof ResizeObserver === "function") {
140
+ const resizeObserver = new ResizeObserver(() => handleResize());
141
+ resizeObserver.observe(el);
142
+ return () => resizeObserver.disconnect();
143
+ } else {
144
+ window.addEventListener("resize", handleResize);
145
+ return () => window.window.removeEventListener("resize", handleResize);
146
+ }
147
+ }, [ref, handleResize]);
148
+ };
149
+
150
+ //#endregion
151
+ export { useAsync, useAsyncFn, useDebouncedCallback, useDebouncedEffect, useMountEffect, useMountedState, useResizeObserver };
2
152
  //# sourceMappingURL=react-util.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"react-util.mjs","names":[],"sources":["../src/react-util/useAsyncFn.ts","../src/react-util/useAsync.ts","../src/react-util/useDebouncedCallback.tsx","../src/react-util/useDebouncedEffect.tsx","../src/react-util/useMountEffect.tsx","../src/react-util/useMountedState.ts","../src/react-util/useResizeObserver.tsx"],"sourcesContent":["import { type DependencyList, useCallback, useEffect, useRef, useState } from \"react\";\n\nexport interface UseAsyncFnOptions<F extends UseAsyncFn> {\n initialValue?: InferValue<F>;\n debounceMs?: number;\n debounceType?: \"leading\" | \"trailing\";\n}\n\nexport type UseAsyncFn = (signal: AbortSignal, ...args: any[]) => Promise<any>;\nexport type UseAsyncFnRun<Value, Args> = Args extends unknown[]\n ? (...args: Args) => Promise<Value>\n : () => Promise<Value>;\n\nexport interface UseAsyncFnStateBase<Value, Args> {\n run: UseAsyncFnRun<Value, Args>;\n}\n\nexport interface UseAsyncFnStateLoading<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n loading: true;\n error?: Error | undefined;\n value?: Value;\n}\n\nexport interface UseAsyncFnStateError<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n loading: false;\n error: Error;\n value?: undefined;\n}\n\nexport interface UseAsyncFnStateResolved<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n loading: false;\n error?: undefined;\n value: Value;\n}\n\ntype InferValue<F extends UseAsyncFn> = Awaited<ReturnType<F>>;\ntype InferArgs<F extends UseAsyncFn> = Parameters<F> extends [any, ...infer Rest] ? Rest : [];\n\nexport type UseAsyncFnState<Value, Args> =\n | UseAsyncFnStateLoading<Value, Args>\n | UseAsyncFnStateError<Value, Args>\n | UseAsyncFnStateResolved<Value, Args>;\n\nexport const useAsyncFn = <F extends UseAsyncFn, Value = InferValue<F>, Args = InferArgs<F>>(\n fn: F,\n deps: DependencyList = [],\n options?: UseAsyncFnOptions<F>,\n): UseAsyncFnState<Value, Args> => {\n const { debounceType = \"leading\", debounceMs = 650 } = options ?? {};\n\n const timeoutRef = useRef<number | undefined>(undefined);\n const abortControllerRef = useRef<AbortController | undefined>(undefined);\n\n const [state, setState] = useState<Omit<UseAsyncFnState<Value, Args>, \"run\">>(\n options?.initialValue ? { loading: false, value: options?.initialValue } : { loading: true },\n );\n\n const run = useCallback((...args: Parameters<F>) => {\n window.clearTimeout(timeoutRef.current);\n\n abortControllerRef.current?.abort();\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n if (!state?.loading) {\n setState((prevState) => ({ ...prevState, loading: true }));\n }\n\n return new Promise((resolve, reject) => {\n const timeoutMs = debounceType === \"leading\" && !timeoutRef.current ? 0 : debounceMs;\n\n timeoutRef.current = window.setTimeout(() => {\n fn(abortController.signal, ...args)\n .then(\n (value) => {\n if (!abortController.signal.aborted) {\n setState({ value, loading: false });\n }\n resolve(value);\n },\n (error) => {\n if (!abortController.signal.aborted) {\n setState({ error, loading: false });\n }\n reject(error);\n },\n )\n .finally(() => {\n timeoutRef.current = undefined;\n });\n }, timeoutMs);\n });\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: deps are controlled by caller\n }, deps);\n\n useEffect(() => {\n return () => abortControllerRef.current?.abort();\n }, []);\n\n return { ...state, run } as UseAsyncFnState<Value, Args>;\n};\n","import { type DependencyList, useEffect, useRef } from \"react\";\nimport { type UseAsyncFn, type UseAsyncFnOptions, useAsyncFn } from \"./useAsyncFn\";\n\nexport const useAsync = <F extends UseAsyncFn>(\n fn: F,\n deps: DependencyList = [],\n options?: UseAsyncFnOptions<F> & { runImmediately?: boolean },\n) => {\n const firstRun = useRef(true);\n\n const { runImmediately = true, ...useAsyncFnOptions } = options ?? {};\n\n const asyncFn = useAsyncFn(fn, deps, useAsyncFnOptions);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: option changes should not trigger effect\n useEffect(() => {\n const isFirstRun = firstRun.current;\n firstRun.current = false;\n\n if (!runImmediately && isFirstRun) return;\n\n // TODO: how should errors be handled? this isn't great...\n void asyncFn.run();\n }, [asyncFn.run]);\n\n return asyncFn;\n};\n","import { type DependencyList, useCallback, useEffect, useRef } from \"react\";\n\nexport interface UseDebouncedCallbackOptions {\n leading?: boolean;\n delayMs?: number;\n}\n\nexport function useDebouncedCallback<T extends (...args: any) => any>(\n callback: T,\n deps: DependencyList,\n options?: UseDebouncedCallbackOptions,\n) {\n const ref = useRef<\n UseDebouncedCallbackOptions & {\n timeout: number | null;\n mounted: boolean;\n leadingTriggered: boolean;\n }\n >({\n ...options,\n timeout: null,\n mounted: false,\n leadingTriggered: options?.leading ?? false,\n });\n\n useEffect(() => {\n const currentRef = ref.current;\n currentRef.mounted = true;\n currentRef.leadingTriggered = false;\n return () => {\n currentRef.mounted = false;\n window.clearTimeout(currentRef.timeout!);\n };\n }, []);\n\n return useCallback((...params: Parameters<T>) => {\n const currentRef = ref.current;\n window.clearTimeout(currentRef.timeout!);\n if (!currentRef.leadingTriggered && currentRef.leading) {\n currentRef.leadingTriggered = true;\n callback(...params);\n currentRef.timeout = window.setTimeout(() => {\n currentRef.leadingTriggered = true;\n }, currentRef.delayMs ?? 1600);\n } else {\n currentRef.timeout = window.setTimeout(() => {\n if (!currentRef.mounted) return;\n callback(...params);\n currentRef.leadingTriggered = false;\n }, currentRef.delayMs ?? 1600);\n }\n // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>\n }, deps) as T;\n}\n","import { type DependencyList, type EffectCallback, useEffect } from \"react\";\nimport { type UseDebouncedCallbackOptions, useDebouncedCallback } from \"./useDebouncedCallback\";\n\nexport function useDebouncedEffect(\n callback: EffectCallback,\n deps: DependencyList,\n options?: UseDebouncedCallbackOptions,\n) {\n const debouncedCallback = useDebouncedCallback(callback, deps, { leading: true, ...options });\n // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>\n return useEffect(debouncedCallback, deps);\n}\n","import { useEffect } from \"react\";\n\nexport const useMountEffect = (effectFn: React.EffectCallback): void => useEffect(effectFn, []);\n","import { useCallback, useEffect, useRef } from \"react\";\n\nexport const useMountedState = (): (() => boolean) => {\n const mountedRef = useRef<boolean>(false);\n const get = useCallback(() => mountedRef.current, []);\n\n useEffect(() => {\n mountedRef.current = true;\n\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n return get;\n};\n","import { useCallback, useLayoutEffect, useRef } from \"react\";\n\nexport const useResizeObserver = (\n ref: React.MutableRefObject<HTMLElement | null>,\n onResize: (width: number, height: number) => void,\n): void => {\n const onResizeRef = useRef(onResize);\n const handleResize = useCallback(() => {\n if (ref.current) {\n onResizeRef.current(ref.current.clientWidth, ref.current.clientHeight);\n }\n }, [ref]);\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n handleResize();\n\n if (typeof ResizeObserver === \"function\") {\n const resizeObserver = new ResizeObserver(() => handleResize());\n\n resizeObserver.observe(el);\n\n return () => resizeObserver.disconnect();\n } else {\n window.addEventListener(\"resize\", handleResize);\n\n return () => window.window.removeEventListener(\"resize\", handleResize);\n }\n }, [ref, handleResize]);\n};\n"],"mappings":"kGA2CA,MAAa,GACX,EACA,EAAuB,EAAE,CACzB,IACiC,CACjC,GAAM,CAAE,eAAe,UAAW,aAAa,KAAQ,GAAW,EAAE,CAE9D,EAAa,EAA2B,IAAA,GAAU,CAClD,EAAqB,EAAoC,IAAA,GAAU,CAEnE,CAAC,EAAO,GAAY,EACxB,GAAS,aAAe,CAAE,QAAS,GAAO,MAAO,GAAS,aAAc,CAAG,CAAE,QAAS,GAAM,CAC7F,CAEK,EAAM,GAAa,GAAG,IAAwB,CAClD,OAAO,aAAa,EAAW,QAAQ,CAEvC,EAAmB,SAAS,OAAO,CACnC,IAAM,EAAkB,IAAI,gBAO5B,MANA,GAAmB,QAAU,EAExB,GAAO,SACV,EAAU,IAAe,CAAE,GAAG,EAAW,QAAS,GAAM,EAAE,CAGrD,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,EAAY,IAAiB,WAAa,CAAC,EAAW,QAAU,EAAI,EAE1E,EAAW,QAAU,OAAO,eAAiB,CAC3C,EAAG,EAAgB,OAAQ,GAAG,EAAK,CAChC,KACE,GAAU,CACJ,EAAgB,OAAO,SAC1B,EAAS,CAAE,QAAO,QAAS,GAAO,CAAC,CAErC,EAAQ,EAAM,EAEf,GAAU,CACJ,EAAgB,OAAO,SAC1B,EAAS,CAAE,QAAO,QAAS,GAAO,CAAC,CAErC,EAAO,EAAM,EAEhB,CACA,YAAc,CACb,EAAW,QAAU,IAAA,IACrB,EACH,EAAU,EACb,EAGD,EAAK,CAMR,OAJA,UACe,EAAmB,SAAS,OAAO,CAC/C,EAAE,CAAC,CAEC,CAAE,GAAG,EAAO,MAAK,ECjGb,GACX,EACA,EAAuB,EAAE,CACzB,IACG,CACH,IAAM,EAAW,EAAO,GAAK,CAEvB,CAAE,iBAAiB,GAAM,GAAG,GAAsB,GAAW,EAAE,CAE/D,EAAU,EAAW,EAAI,EAAM,EAAkB,CAavD,OAVA,MAAgB,CACd,IAAM,EAAa,EAAS,QAC5B,EAAS,QAAU,GAEf,GAAC,GAAkB,IAGlB,EAAQ,KAAK,EACjB,CAAC,EAAQ,IAAI,CAAC,CAEV,GClBT,SAAgB,EACd,EACA,EACA,EACA,CACA,IAAM,EAAM,EAMV,CACA,GAAG,EACH,QAAS,KACT,QAAS,GACT,iBAAkB,GAAS,SAAW,GACvC,CAAC,CAYF,OAVA,MAAgB,CACd,IAAM,EAAa,EAAI,QAGvB,MAFA,GAAW,QAAU,GACrB,EAAW,iBAAmB,OACjB,CACX,EAAW,QAAU,GACrB,OAAO,aAAa,EAAW,QAAS,GAEzC,EAAE,CAAC,CAEC,GAAa,GAAG,IAA0B,CAC/C,IAAM,EAAa,EAAI,QACvB,OAAO,aAAa,EAAW,QAAS,CACpC,CAAC,EAAW,kBAAoB,EAAW,SAC7C,EAAW,iBAAmB,GAC9B,EAAS,GAAG,EAAO,CACnB,EAAW,QAAU,OAAO,eAAiB,CAC3C,EAAW,iBAAmB,IAC7B,EAAW,SAAW,KAAK,EAE9B,EAAW,QAAU,OAAO,eAAiB,CACtC,EAAW,UAChB,EAAS,GAAG,EAAO,CACnB,EAAW,iBAAmB,KAC7B,EAAW,SAAW,KAAK,EAG/B,EAAK,CCjDV,SAAgB,EACd,EACA,EACA,EACA,CAGA,OAAO,EAFmB,EAAqB,EAAU,EAAM,CAAE,QAAS,GAAM,GAAG,EAAS,CAAC,CAEzD,EAAK,CCR3C,MAAa,EAAkB,GAAyC,EAAU,EAAU,EAAE,CAAC,CCAlF,MAAyC,CACpD,IAAM,EAAa,EAAgB,GAAM,CACnC,EAAM,MAAkB,EAAW,QAAS,EAAE,CAAC,CAUrD,OARA,OACE,EAAW,QAAU,OAER,CACX,EAAW,QAAU,KAEtB,EAAE,CAAC,CAEC,GCZI,GACX,EACA,IACS,CACT,IAAM,EAAc,EAAO,EAAS,CAC9B,EAAe,MAAkB,CACjC,EAAI,SACN,EAAY,QAAQ,EAAI,QAAQ,YAAa,EAAI,QAAQ,aAAa,EAEvE,CAAC,EAAI,CAAC,CAET,MAAsB,CACpB,IAAM,EAAK,EAAI,QACV,KAML,GAFA,GAAc,CAEV,OAAO,gBAAmB,WAAY,CACxC,IAAM,EAAiB,IAAI,mBAAqB,GAAc,CAAC,CAI/D,OAFA,EAAe,QAAQ,EAAG,KAEb,EAAe,YAAY,MAIxC,OAFA,OAAO,iBAAiB,SAAU,EAAa,KAElC,OAAO,OAAO,oBAAoB,SAAU,EAAa,EAEvE,CAAC,EAAK,EAAa,CAAC"}
1
+ {"version":3,"file":"react-util.mjs","names":[],"sources":["../src/react-util/useAsyncFn.ts","../src/react-util/useAsync.ts","../src/react-util/useDebouncedCallback.tsx","../src/react-util/useDebouncedEffect.tsx","../src/react-util/useMountEffect.tsx","../src/react-util/useMountedState.ts","../src/react-util/useResizeObserver.tsx"],"sourcesContent":["import { type DependencyList, useCallback, useEffect, useRef, useState } from \"react\";\n\nexport interface UseAsyncFnOptions<F extends UseAsyncFn> {\n initialValue?: InferValue<F>;\n debounceMs?: number;\n debounceType?: \"leading\" | \"trailing\";\n}\n\nexport type UseAsyncFn = (signal: AbortSignal, ...args: any[]) => Promise<any>;\nexport type UseAsyncFnRun<Value, Args> = Args extends unknown[]\n ? (...args: Args) => Promise<Value>\n : () => Promise<Value>;\n\nexport interface UseAsyncFnStateBase<Value, Args> {\n run: UseAsyncFnRun<Value, Args>;\n}\n\nexport interface UseAsyncFnStateLoading<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n loading: true;\n error?: Error | undefined;\n value?: Value;\n}\n\nexport interface UseAsyncFnStateError<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n loading: false;\n error: Error;\n value?: undefined;\n}\n\nexport interface UseAsyncFnStateResolved<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n loading: false;\n error?: undefined;\n value: Value;\n}\n\ntype InferValue<F extends UseAsyncFn> = Awaited<ReturnType<F>>;\ntype InferArgs<F extends UseAsyncFn> = Parameters<F> extends [any, ...infer Rest] ? Rest : [];\n\nexport type UseAsyncFnState<Value, Args> =\n | UseAsyncFnStateLoading<Value, Args>\n | UseAsyncFnStateError<Value, Args>\n | UseAsyncFnStateResolved<Value, Args>;\n\nexport const useAsyncFn = <F extends UseAsyncFn, Value = InferValue<F>, Args = InferArgs<F>>(\n fn: F,\n deps: DependencyList = [],\n options?: UseAsyncFnOptions<F>,\n): UseAsyncFnState<Value, Args> => {\n const { debounceType = \"leading\", debounceMs = 650 } = options ?? {};\n\n const timeoutRef = useRef<number | undefined>(undefined);\n const abortControllerRef = useRef<AbortController | undefined>(undefined);\n\n const [state, setState] = useState<Omit<UseAsyncFnState<Value, Args>, \"run\">>(\n options?.initialValue ? { loading: false, value: options?.initialValue } : { loading: true },\n );\n\n const run = useCallback((...args: Parameters<F>) => {\n window.clearTimeout(timeoutRef.current);\n\n abortControllerRef.current?.abort();\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n if (!state?.loading) {\n setState((prevState) => ({ ...prevState, loading: true }));\n }\n\n return new Promise((resolve, reject) => {\n const timeoutMs = debounceType === \"leading\" && !timeoutRef.current ? 0 : debounceMs;\n\n timeoutRef.current = window.setTimeout(() => {\n fn(abortController.signal, ...args)\n .then(\n (value) => {\n if (!abortController.signal.aborted) {\n setState({ value, loading: false });\n }\n resolve(value);\n },\n (error) => {\n if (!abortController.signal.aborted) {\n setState({ error, loading: false });\n }\n reject(error);\n },\n )\n .finally(() => {\n timeoutRef.current = undefined;\n });\n }, timeoutMs);\n });\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: deps are controlled by caller\n }, deps);\n\n useEffect(() => {\n return () => abortControllerRef.current?.abort();\n }, []);\n\n return { ...state, run } as UseAsyncFnState<Value, Args>;\n};\n","import { type DependencyList, useEffect, useRef } from \"react\";\nimport { type UseAsyncFn, type UseAsyncFnOptions, useAsyncFn } from \"./useAsyncFn\";\n\nexport const useAsync = <F extends UseAsyncFn>(\n fn: F,\n deps: DependencyList = [],\n options?: UseAsyncFnOptions<F> & { runImmediately?: boolean },\n) => {\n const firstRun = useRef(true);\n\n const { runImmediately = true, ...useAsyncFnOptions } = options ?? {};\n\n const asyncFn = useAsyncFn(fn, deps, useAsyncFnOptions);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: option changes should not trigger effect\n useEffect(() => {\n const isFirstRun = firstRun.current;\n firstRun.current = false;\n\n if (!runImmediately && isFirstRun) return;\n\n // TODO: how should errors be handled? this isn't great...\n void asyncFn.run();\n }, [asyncFn.run]);\n\n return asyncFn;\n};\n","import { type DependencyList, useCallback, useEffect, useRef } from \"react\";\n\nexport interface UseDebouncedCallbackOptions {\n leading?: boolean;\n delayMs?: number;\n}\n\nexport function useDebouncedCallback<T extends (...args: any) => any>(\n callback: T,\n deps: DependencyList,\n options?: UseDebouncedCallbackOptions,\n) {\n const ref = useRef<\n UseDebouncedCallbackOptions & {\n timeout: number | null;\n mounted: boolean;\n leadingTriggered: boolean;\n }\n >({\n ...options,\n timeout: null,\n mounted: false,\n leadingTriggered: options?.leading ?? false,\n });\n\n useEffect(() => {\n const currentRef = ref.current;\n currentRef.mounted = true;\n currentRef.leadingTriggered = false;\n return () => {\n currentRef.mounted = false;\n window.clearTimeout(currentRef.timeout!);\n };\n }, []);\n\n return useCallback((...params: Parameters<T>) => {\n const currentRef = ref.current;\n window.clearTimeout(currentRef.timeout!);\n if (!currentRef.leadingTriggered && currentRef.leading) {\n currentRef.leadingTriggered = true;\n callback(...params);\n currentRef.timeout = window.setTimeout(() => {\n currentRef.leadingTriggered = true;\n }, currentRef.delayMs ?? 1600);\n } else {\n currentRef.timeout = window.setTimeout(() => {\n if (!currentRef.mounted) return;\n callback(...params);\n currentRef.leadingTriggered = false;\n }, currentRef.delayMs ?? 1600);\n }\n // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>\n }, deps) as T;\n}\n","import { type DependencyList, type EffectCallback, useEffect } from \"react\";\nimport { type UseDebouncedCallbackOptions, useDebouncedCallback } from \"./useDebouncedCallback\";\n\nexport function useDebouncedEffect(\n callback: EffectCallback,\n deps: DependencyList,\n options?: UseDebouncedCallbackOptions,\n) {\n const debouncedCallback = useDebouncedCallback(callback, deps, { leading: true, ...options });\n // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>\n return useEffect(debouncedCallback, deps);\n}\n","import { useEffect } from \"react\";\n\nexport const useMountEffect = (effectFn: React.EffectCallback): void => useEffect(effectFn, []);\n","import { useCallback, useEffect, useRef } from \"react\";\n\nexport const useMountedState = (): (() => boolean) => {\n const mountedRef = useRef<boolean>(false);\n const get = useCallback(() => mountedRef.current, []);\n\n useEffect(() => {\n mountedRef.current = true;\n\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n return get;\n};\n","import { useCallback, useLayoutEffect, useRef } from \"react\";\n\nexport const useResizeObserver = (\n ref: React.MutableRefObject<HTMLElement | null>,\n onResize: (width: number, height: number) => void,\n): void => {\n const onResizeRef = useRef(onResize);\n const handleResize = useCallback(() => {\n if (ref.current) {\n onResizeRef.current(ref.current.clientWidth, ref.current.clientHeight);\n }\n }, [ref]);\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n handleResize();\n\n if (typeof ResizeObserver === \"function\") {\n const resizeObserver = new ResizeObserver(() => handleResize());\n\n resizeObserver.observe(el);\n\n return () => resizeObserver.disconnect();\n } else {\n window.addEventListener(\"resize\", handleResize);\n\n return () => window.window.removeEventListener(\"resize\", handleResize);\n }\n }, [ref, handleResize]);\n};\n"],"mappings":"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"}
package/dist/router.d.mts CHANGED
@@ -11,10 +11,10 @@ interface OutletConfig {
11
11
  type RouteSegmentState = "preloading" | "loading" | "error" | "ready";
12
12
  declare class Outlet {
13
13
  readonly config: OutletConfig;
14
- accessor state: RouteSegmentState;
15
- accessor promise: Promise<unknown> | undefined;
16
- accessor data: unknown;
17
- accessor component: Component | undefined;
14
+ state: RouteSegmentState;
15
+ promise: Promise<unknown> | undefined;
16
+ data: unknown;
17
+ component: Component | undefined;
18
18
  get Component(): Component | undefined;
19
19
  constructor(config: OutletConfig);
20
20
  load(route: Route): Promise<void>;