cross-state 0.9.0 → 0.9.2

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.
@@ -1,6 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const immerMethods = require("../immerMethods.cjs");
4
- require("immer");
5
- exports.immerMethods = immerMethods.immerMethods;
3
+ const immer = require("immer");
4
+ function update(...args) {
5
+ if (args.length === 1) {
6
+ this.set(
7
+ (value) => immer.produce(value, (draft) => {
8
+ args[0](draft);
9
+ })
10
+ );
11
+ } else {
12
+ this.set(
13
+ args[0],
14
+ (value) => immer.produce(value, (draft) => {
15
+ args[1](draft);
16
+ })
17
+ );
18
+ }
19
+ }
20
+ const immerMethods = {
21
+ update
22
+ };
23
+ exports.immerMethods = immerMethods;
6
24
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../src/immer/immerMethods.ts"],"sourcesContent":["import type { Draft } from 'immer';\nimport { produce } from 'immer';\nimport type { Store } from '@core/store';\nimport { type Path, type Value } from '@lib/path';\n\ntype Recipe<T> = (draft: Draft<T>) => void;\n\nfunction update<T>(this: Store<T>, recipe: Recipe<T>): void;\n\nfunction update<T, P extends Path<T>>(this: Store<T>, path: P, recipe: Recipe<Value<T, P>>): void;\n\nfunction update<T, P extends Path<T>>(\n this: Store<T>,\n ...args: [recipe: Recipe<T>] | [P: P, recipe: Recipe<Value<T, P>>]\n) {\n if (args.length === 1) {\n this.set((value) =>\n produce(value, (draft) => {\n args[0](draft);\n }),\n );\n } else {\n this.set(args[0], (value) =>\n produce(value, (draft) => {\n args[1](draft);\n }),\n );\n }\n}\n\nexport const immerMethods = {\n update,\n};\n"],"names":["produce"],"mappings":";;;AAWA,SAAS,UAEJ,MACH;AACI,MAAA,KAAK,WAAW,GAAG;AAChB,SAAA;AAAA,MAAI,CAAC,UACRA,MAAAA,QAAQ,OAAO,CAAC,UAAU;AACnB,aAAA,CAAC,EAAE,KAAK;AAAA,MAAA,CACd;AAAA,IAAA;AAAA,EACH,OACK;AACA,SAAA;AAAA,MAAI,KAAK,CAAC;AAAA,MAAG,CAAC,UACjBA,MAAAA,QAAQ,OAAO,CAAC,UAAU;AACnB,aAAA,CAAC,EAAE,KAAK;AAAA,MAAA,CACd;AAAA,IAAA;AAAA,EAEL;AACF;AAEO,MAAM,eAAe;AAAA,EAC1B;AACF;;"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
- const immerMethods = require("../immerMethods.cjs");
2
+ const immer_index = require("./index.cjs");
3
3
  const store = require("../store.cjs");
4
4
  require("immer");
5
- Object.assign(store.Store.prototype, immerMethods.immerMethods);
5
+ Object.assign(store.Store.prototype, immer_index.immerMethods);
6
6
  //# sourceMappingURL=register.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"register.cjs","sources":["../../../src/immer/register.ts"],"sourcesContent":["import { immerMethods } from './immerMethods';\nimport { Store } from '@core';\n\ntype ImmerMethods = typeof immerMethods;\n\ndeclare module '@core' {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n interface Store<T> extends ImmerMethods {}\n}\n\nObject.assign(Store.prototype, immerMethods);\n"],"names":["Store","immerMethods"],"mappings":";;;;AAUA,OAAO,OAAOA,MAAAA,MAAM,WAAWC,yBAAY;"}
1
+ {"version":3,"file":"register.cjs","sources":["../../../src/immer/register.ts"],"sourcesContent":["import { immerMethods } from './immerMethods';\nimport { Store } from '@core';\n\ntype ImmerMethods = typeof immerMethods;\n\ndeclare module '@core' {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n interface Store<T> extends ImmerMethods {}\n}\n\nObject.assign(Store.prototype, immerMethods);\n"],"names":["Store","immerMethods"],"mappings":";;;;AAUA,OAAO,OAAOA,MAAAA,MAAM,WAAWC,wBAAY;"}
@@ -1,10 +1,80 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const jsxRuntime = require("react/jsx-runtime");
4
- const require$$0 = require("react");
5
3
  const useCache = require("../useCache.cjs");
4
+ const require$$0 = require("react");
6
5
  const store = require("../store.cjs");
7
6
  const scope = require("../scope.cjs");
7
+ const jsxRuntime = require("react/jsx-runtime");
8
+ function FormError({ name }) {
9
+ const { errors, isDirty } = this.useField(name);
10
+ return isDirty ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: errors.join(", ") }) : null;
11
+ }
12
+ function FormInput({
13
+ id,
14
+ name,
15
+ component = "input",
16
+ commitOnBlur,
17
+ commitDebounce,
18
+ inputFilter,
19
+ serialize = (x) => x,
20
+ deserialize = (x) => x,
21
+ ...restProps
22
+ }) {
23
+ const { value, setValue, errors } = this.useField(name);
24
+ const errorString = require$$0.useMemo(() => errors.join("\n"), [errors]);
25
+ const [localValue, setLocalValue] = require$$0.useState();
26
+ const _id = require$$0.useMemo(
27
+ () => id || `f${Math.random().toString(36).slice(2, 15)}${Math.random().toString(36).slice(2, 15)}`,
28
+ [id]
29
+ );
30
+ require$$0.useEffect(() => {
31
+ if (localValue === void 0 || !commitDebounce) {
32
+ return;
33
+ }
34
+ const timeout = setTimeout(() => {
35
+ setValue(deserialize(localValue));
36
+ setLocalValue(void 0);
37
+ }, commitDebounce);
38
+ return () => clearTimeout(timeout);
39
+ }, [localValue, commitDebounce]);
40
+ require$$0.useEffect(() => {
41
+ const element = document.querySelector(
42
+ [`#${_id} input`, `#${_id} select`, `#${_id} textarea`, `#${_id}`].join(",")
43
+ );
44
+ if (!(element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement)) {
45
+ return;
46
+ }
47
+ element.setCustomValidity(errorString);
48
+ }, [_id, errorString]);
49
+ const props = {
50
+ ...restProps,
51
+ id: _id,
52
+ name,
53
+ value: localValue ?? serialize(value),
54
+ onChange: (event, ...moreArgs) => {
55
+ var _a;
56
+ const value2 = typeof event === "object" && event !== null && "target" in event ? event.target.value : event;
57
+ if (inputFilter && !inputFilter(value2)) {
58
+ return;
59
+ }
60
+ if (commitOnBlur || commitDebounce) {
61
+ setLocalValue(value2);
62
+ } else {
63
+ setValue(deserialize(value2));
64
+ }
65
+ (_a = restProps.onChange) == null ? void 0 : _a.call(restProps, event, ...moreArgs);
66
+ },
67
+ onBlur(...args) {
68
+ var _a;
69
+ if (localValue !== void 0) {
70
+ setValue(deserialize(localValue));
71
+ setLocalValue(void 0);
72
+ }
73
+ (_a = restProps.onBlur) == null ? void 0 : _a.call(restProps, ...args);
74
+ }
75
+ };
76
+ return require$$0.createElement(component, props);
77
+ }
8
78
  function wildcardMatch(s, w) {
9
79
  if (typeof s === "string") {
10
80
  s = store.castArrayPath(s);
@@ -32,6 +102,21 @@ function getWildCardMatches(object, path) {
32
102
  }
33
103
  return matches;
34
104
  }
105
+ function FormContainer({ form, children }) {
106
+ const { validate } = form.useForm();
107
+ return /* @__PURE__ */ jsxRuntime.jsx(
108
+ "form",
109
+ {
110
+ noValidate: true,
111
+ onSubmit: (event) => {
112
+ event.preventDefault();
113
+ validate();
114
+ event.currentTarget.reportValidity();
115
+ },
116
+ children
117
+ }
118
+ );
119
+ }
35
120
  class Form {
36
121
  constructor(options) {
37
122
  this.options = options;
@@ -41,31 +126,43 @@ class Form {
41
126
  });
42
127
  this.state = new scope.Scope({});
43
128
  this.Provider = this.Provider.bind(this);
129
+ this.useForm = this.useForm.bind(this);
130
+ this.useField = this.useField.bind(this);
131
+ this.useHasChanges = this.useHasChanges.bind(this);
132
+ this.useIsValid = this.useIsValid.bind(this);
133
+ this.Input = this.Input.bind(this);
134
+ this.Error = this.Error.bind(this);
44
135
  }
45
136
  Provider({
46
137
  children,
47
138
  original,
48
- defaultValue = this.options.defaultValue,
49
- validations = this.options.validations
139
+ defaultValue,
140
+ validations
50
141
  }) {
51
142
  const value = require$$0.useMemo(
52
- () => ({ original, options: { defaultValue, validations } }),
143
+ () => ({
144
+ original,
145
+ options: {
146
+ defaultValue: { ...this.options.defaultValue, ...defaultValue },
147
+ validations: { ...this.options.validations, ...validations }
148
+ }
149
+ }),
53
150
  [original, defaultValue, validations]
54
151
  );
55
- return /* @__PURE__ */ jsxRuntime.jsx(this.context.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx(this.state.Provider, { children }) });
152
+ return /* @__PURE__ */ jsxRuntime.jsx(this.context.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx(useCache.ScopeProvider, { scope: this.state, children: /* @__PURE__ */ jsxRuntime.jsx(FormContainer, { form: this, children }) }) });
56
153
  }
57
154
  useForm() {
58
155
  const { original, options } = require$$0.useContext(this.context);
59
156
  const state = useCache.useScope(this.state);
60
- return require$$0.useMemo(
61
- () => ({
157
+ return require$$0.useMemo(() => {
158
+ const instance = {
62
159
  original,
63
160
  draft: state.map(
64
161
  (state2) => state2.draft ?? original ?? options.defaultValue,
65
162
  (draft) => (state2) => ({ ...state2, draft })
66
163
  ),
67
- getField(path) {
68
- const { draft } = this;
164
+ getField: (path) => {
165
+ const { draft } = instance;
69
166
  return {
70
167
  get originalValue() {
71
168
  return original !== void 0 ? store.get(original, path) : void 0;
@@ -77,31 +174,33 @@ class Form {
77
174
  draft.set(path, update);
78
175
  },
79
176
  get isDirty() {
80
- return state.get().hasTriggeredValidations || !store.deepEqual(this.originalValue, this.value);
177
+ const comparisonValue = this.originalValue ?? store.get(options.defaultValue, path);
178
+ return state.get().hasTriggeredValidations || !store.deepEqual(comparisonValue, this.value);
81
179
  },
82
- get error() {
180
+ get errors() {
83
181
  const blocks = Object.entries(
84
182
  options.validations ?? {}
85
183
  ).filter(([key]) => wildcardMatch(path, key)).map(([, value2]) => value2);
86
184
  const value = this.value;
87
185
  const draftValue = draft.get();
186
+ const errors = [];
88
187
  for (const block of blocks ?? []) {
89
188
  for (const [validationName, validate] of Object.entries(block)) {
90
189
  if (!validate(value, { draft: draftValue, original, field: path })) {
91
- return validationName;
190
+ errors.push(validationName);
92
191
  }
93
192
  }
94
193
  }
95
- return void 0;
194
+ return errors;
96
195
  }
97
196
  };
98
197
  },
99
- hasChanges() {
198
+ get hasChanges() {
100
199
  const { draft } = state.get();
101
- return !!draft && !store.deepEqual(draft, original);
200
+ return !!draft && !store.deepEqual(draft, original ?? options.defaultValue);
102
201
  },
103
- getErrors() {
104
- const draft = this.draft.get();
202
+ get errors() {
203
+ const draft = instance.draft.get();
105
204
  const errors = /* @__PURE__ */ new Set();
106
205
  for (const [path, block] of Object.entries(options.validations ?? {})) {
107
206
  for (const [validationName, validate] of Object.entries(
@@ -116,19 +215,19 @@ class Form {
116
215
  }
117
216
  return [...errors];
118
217
  },
119
- isValid() {
120
- return this.getErrors().length === 0;
218
+ get isValid() {
219
+ return instance.errors.length === 0;
121
220
  },
122
- validate() {
221
+ validate: () => {
123
222
  state.set("hasTriggeredValidations", true);
124
- return this.isValid();
223
+ return instance.isValid;
125
224
  },
126
225
  reset() {
127
226
  state.set("draft", void 0);
128
227
  }
129
- }),
130
- [original, options, state]
131
- );
228
+ };
229
+ return instance;
230
+ }, [original, options, state]);
132
231
  }
133
232
  useField(path, useStoreOptions) {
134
233
  const form = this.useForm();
@@ -145,11 +244,17 @@ class Form {
145
244
  }
146
245
  useHasChanges() {
147
246
  const form = this.useForm();
148
- return useCache.useStore(form.draft.map(() => form.hasChanges()));
247
+ return useCache.useStore(form.draft.map(() => form.hasChanges));
149
248
  }
150
249
  useIsValid() {
151
250
  const form = this.useForm();
152
- return useCache.useStore(form.draft.map(() => form.isValid()));
251
+ return useCache.useStore(form.draft.map(() => form.isValid));
252
+ }
253
+ Input(props) {
254
+ return Reflect.apply(FormInput, this, [props]);
255
+ }
256
+ Error({ name }) {
257
+ return Reflect.apply(FormError, this, [{ name }]);
153
258
  }
154
259
  }
155
260
  function createForm(options) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../src/lib/wildcardMatch.ts","../../../src/react/form.tsx","../../../src/react/read.ts","../../../src/react/useDecoupledState.ts"],"sourcesContent":["import { type KeyType } from './path';\nimport { castArrayPath } from './propAccess';\n\nexport function wildcardMatch(s: KeyType[] | string, w: KeyType[] | string): boolean {\n if (typeof s === 'string') {\n s = castArrayPath(s);\n }\n\n if (typeof w === 'string') {\n w = castArrayPath(w);\n }\n\n return s.length === w.length && s.every((s, i) => w[i] === '*' || s === w[i]);\n}\n\nexport function getWildCardMatches(object: any, path: KeyType[] | string): Record<KeyType, any> {\n const matches: Record<KeyType, any> = {};\n const [first, ...rest] = castArrayPath(path);\n\n if (first === undefined) {\n return object;\n }\n\n if (!(object instanceof Object)) {\n return {};\n }\n\n if (first === '*') {\n for (const [key, value] of Object.entries(object)) {\n matches[key] = getWildCardMatches(value, rest);\n }\n } else {\n matches[first] = getWildCardMatches(object[first], rest);\n }\n\n return matches;\n}\n","/* eslint-disable react-hooks/rules-of-hooks */\n\nimport { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport { useScope } from './scope';\nimport { useStore, type UseStoreOptions } from './useStore';\nimport { Scope } from '@core';\nimport { deepEqual } from '@lib/equals';\nimport {\n type PathAsString,\n type Value,\n type WildcardMatch,\n type WildcardPathAsString,\n type WildcardValue,\n} from '@lib/path';\nimport { get } from '@lib/propAccess';\nimport { getWildCardMatches, wildcardMatch } from '@lib/wildcardMatch';\n\nexport interface FormOptions<\n TDraft,\n TOriginal,\n TValidations extends Validations<TDraft, TOriginal>,\n> {\n defaultValue: TDraft;\n validations?: TValidations;\n}\n\nexport type Validation<TValue, TDraft, TOriginal> = (\n value: TValue,\n context: { draft: TDraft; original: TOriginal; field: PathAsString<TDraft> },\n) => boolean;\n\nexport type Validations<TDraft, TOriginal> = {\n [P in WildcardPathAsString<TDraft>]?: Record<\n string,\n Validation<WildcardValue<TDraft, P>, TDraft, TOriginal>\n >;\n};\n\nexport interface Field<\n TDraft,\n TOriginal,\n TPath extends PathAsString<TDraft>,\n TValidations extends Validations<TDraft, TOriginal>,\n> {\n originalValue: Value<TOriginal, TPath> | undefined;\n value: Value<TDraft, TPath>;\n setValue: (\n value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>),\n ) => void;\n isDirty: boolean;\n error?: keyof {\n [P in keyof TValidations as WildcardMatch<TPath, P> extends true\n ? keyof TValidations[P]\n : never]: 1;\n };\n}\n\nexport class Form<\n TDraft,\n TOriginal extends TDraft = TDraft,\n TValidations extends Validations<TDraft, TOriginal> = {},\n> {\n private context = createContext({\n original: undefined as TOriginal | undefined,\n options: this.options,\n });\n\n private state = new Scope<{\n draft?: TDraft;\n hasTriggeredValidations?: boolean;\n }>({});\n\n constructor(public readonly options: FormOptions<TDraft, TOriginal, TValidations>) {\n this.Provider = this.Provider.bind(this);\n }\n\n Provider({\n children,\n original,\n defaultValue = this.options.defaultValue,\n validations = this.options.validations,\n }: { children?: ReactNode; original?: TOriginal } & Partial<\n FormOptions<TDraft, TOriginal, TValidations>\n >) {\n const value = useMemo(\n () => ({ original, options: { defaultValue, validations } }),\n [original, defaultValue, validations],\n );\n\n return (\n <this.context.Provider value={value}>\n <this.state.Provider>{children}</this.state.Provider>\n </this.context.Provider>\n );\n }\n\n useForm() {\n const { original, options } = useContext(this.context);\n const state = useScope(this.state);\n\n return useMemo(\n () => ({\n original,\n\n draft: state.map(\n (state) => state.draft ?? original ?? options.defaultValue,\n (draft) => (state) => ({ ...state, draft }),\n ),\n\n getField<TPath extends PathAsString<TDraft>>(\n path: TPath,\n ): Field<TDraft, TOriginal, TPath, TValidations> {\n const { draft } = this;\n\n return {\n get originalValue() {\n return original !== undefined ? get(original as any, path as any) : undefined;\n },\n\n get value() {\n return get(draft.get(), path);\n },\n\n setValue(update) {\n draft.set(path, update);\n },\n\n get isDirty() {\n return (\n state.get().hasTriggeredValidations || !deepEqual(this.originalValue, this.value)\n );\n },\n\n get error() {\n const blocks: Record<string, Validation<any, any, any>>[] = Object.entries(\n options.validations ?? {},\n )\n .filter(([key]) => wildcardMatch(path, key))\n .map(([, value]) => value);\n\n const value = this.value;\n const draftValue = draft.get();\n\n for (const block of blocks ?? []) {\n for (const [validationName, validate] of Object.entries(block)) {\n if (!validate(value, { draft: draftValue, original, field: path })) {\n return validationName as any;\n }\n }\n }\n\n return undefined;\n },\n };\n },\n\n hasChanges() {\n const { draft } = state.get();\n return !!draft && !deepEqual(draft, original);\n },\n\n getErrors(): (keyof {\n [Field in PathAsString<TDraft> as keyof {\n [Pattern in keyof TValidations as WildcardMatch<Field, Pattern> extends true\n ? `${Field}.${keyof TValidations[Pattern] & string}`\n : never]: 1;\n }]: 1;\n })[] {\n const draft = this.draft.get();\n const errors = new Set<string>();\n\n for (const [path, block] of Object.entries(options.validations ?? {})) {\n for (const [validationName, validate] of Object.entries(\n block as Record<string, Validation<any, any, any>>,\n )) {\n for (const [field, value] of Object.entries(getWildCardMatches(draft, path))) {\n if (!validate(value, { draft, original, field })) {\n errors.add(`${field}.${validationName}`);\n }\n }\n }\n }\n\n return [...errors] as any;\n },\n\n isValid() {\n return this.getErrors().length === 0;\n },\n\n validate() {\n state.set('hasTriggeredValidations', true);\n return this.isValid();\n },\n\n reset() {\n state.set('draft', undefined);\n },\n }),\n [original, options, state],\n );\n }\n\n useField<TPath extends PathAsString<TDraft>>(path: TPath, useStoreOptions?: UseStoreOptions) {\n const form = this.useForm();\n const state = useScope(this.state);\n\n useStore(\n form.draft.map((draft) => get(draft, path)),\n useStoreOptions,\n );\n\n useStore(\n state.map((state) => state.hasTriggeredValidations),\n useStoreOptions,\n );\n\n return form.getField(path);\n }\n\n useHasChanges() {\n const form = this.useForm();\n\n return useStore(form.draft.map(() => form.hasChanges()));\n }\n\n useIsValid() {\n const form = this.useForm();\n\n return useStore(form.draft.map(() => form.isValid()));\n }\n}\n\nexport function createForm<\n TDraft,\n TOriginal extends TDraft = TDraft,\n TValidations extends Validations<TDraft, TOriginal> = {},\n>(options: FormOptions<TDraft, TOriginal, TValidations>) {\n return new Form(options);\n}\n\n/* eslint-enable react-hooks/rules-of-hooks */\n","import { useCache } from './useCache';\nimport type { UseStoreOptions } from './useStore';\nimport type { Cache } from '@core';\n\nexport function read<T>(cache: Cache<T>, options?: UseStoreOptions): T {\n const { status, value, error } = useCache(cache, options);\n\n if (status === 'value') {\n return value;\n }\n\n if (status === 'error') {\n throw error;\n }\n\n throw cache.state.once((state) => state.status !== 'pending');\n}\n","import { startTransition, useEffect, useMemo, useRef, useState } from 'react';\nimport { type Duration } from '@core';\nimport { debounce } from '@lib/debounce';\nimport { hash } from '@lib/hash';\nimport { throttle } from '@lib/throttle';\n\nexport interface UseDecoupledStateOptions<T> {\n debounce?: Duration;\n throttle?: Duration;\n onCommit?: (value: T) => void;\n}\n\nexport function useDecoupledState<T>(\n value: T,\n onChange: (value: T) => void,\n options: UseDecoupledStateOptions<T> = {},\n): [state: T, setState: (value: T) => void] {\n const [dirty, setDirty] = useState<{ v: T }>();\n const ref = useRef({ onChange, onCommit: options.onCommit });\n\n useEffect(() => {\n ref.current = { onChange, onCommit: options.onCommit };\n }, [onChange]);\n\n const update = useMemo(() => {\n const { onChange, onCommit } = ref.current;\n\n const update = (value: T) => {\n onChange(value);\n setDirty(undefined);\n onCommit?.(value);\n };\n\n let delayedUpdate: (value: T) => void;\n\n if (options.debounce) {\n delayedUpdate = debounce(update, options.debounce);\n } else if (options.throttle) {\n delayedUpdate = throttle(update, options.throttle);\n } else {\n delayedUpdate = (value) => startTransition(() => update(value));\n }\n\n return (value: T) => {\n setDirty({ v: value });\n delayedUpdate(value);\n };\n }, [hash([options.debounce, options.throttle])]);\n\n return [dirty ? dirty.v : value, update];\n}\n"],"names":["castArrayPath","s","createContext","Scope","useMemo","jsx","useContext","useScope","state","get","deepEqual","value","useStore","useCache","useState","useRef","useEffect","onChange","update","debounce","throttle","startTransition","hash"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../src/react/form/formError.tsx","../../../src/react/form/formInput.tsx","../../../src/lib/wildcardMatch.ts","../../../src/react/form/form.tsx","../../../src/react/read.ts","../../../src/react/useDecoupledState.ts"],"sourcesContent":["import { type Form } from './form';\nimport { type PathAsString } from '@lib/path';\n\nexport type FormErrorProps<TDraft, TPath extends PathAsString<TDraft>> = {\n name: TPath;\n};\n\nexport function FormError<TDraft, TPath extends PathAsString<TDraft>>(\n this: Form<TDraft, any>,\n { name }: FormErrorProps<TDraft, TPath>,\n) {\n const { errors, isDirty } = this.useField(name);\n\n return isDirty ? <>{errors.join(', ')}</> : null;\n}\n","import {\n createElement,\n useEffect,\n useMemo,\n useState,\n type ComponentPropsWithoutRef,\n type ElementType,\n type HTMLProps,\n} from 'react';\nimport { type Form } from './form';\nimport { type PathAsString } from '@index';\nimport { type Value } from '@lib/path';\n\nexport type FormInputComponent<T> = ElementType<{\n id: string;\n value: T;\n onChange: (event: { target: { value: T } } | T | undefined, ...args: any[]) => void;\n onBlur: (...args: any[]) => void;\n}>;\n\ntype InputValue<T extends FormInputComponent<any>> = ComponentPropsWithoutRef<T> extends {\n value: infer U;\n}\n ? U\n : ComponentPropsWithoutRef<T> extends {\n value?: infer U;\n }\n ? U | undefined\n : never;\n\ntype InputChangeValue<T extends FormInputComponent<any>> = ComponentPropsWithoutRef<T> extends {\n onChange?: (update: infer U) => void;\n}\n ? U extends { target: { value: infer V } }\n ? V\n : U\n : never;\n\nexport type FormInputProps<\n TDraft,\n TPath extends PathAsString<TDraft>,\n TComponent extends FormInputComponent<any>,\n> = {\n name: TPath;\n commitOnBlur?: boolean;\n commitDebounce?: number;\n inputFilter?: (value: InputChangeValue<TComponent>) => boolean;\n onChange?: ComponentPropsWithoutRef<TComponent>['onChange'];\n onBlur?: ComponentPropsWithoutRef<TComponent>['onBlur'];\n} & (TComponent extends 'input' | ((props: HTMLProps<HTMLInputElement>) => JSX.Element)\n ? { component?: TComponent }\n : { component: TComponent }) &\n Omit<\n ComponentPropsWithoutRef<TComponent>,\n | 'form'\n | 'name'\n | 'component'\n | 'commitOnBlur'\n | 'commitDebounce'\n | 'value'\n | 'onChange'\n | 'onBlur'\n > &\n (Value<TDraft, TPath> extends InputValue<TComponent>\n ? { serialize?: (value: Value<TDraft, TPath>) => InputValue<TComponent> }\n : { serialize: (value: Value<TDraft, TPath>) => InputValue<TComponent> }) &\n (InputChangeValue<TComponent> extends Value<TDraft, TPath>\n ? { deserialize?: (value: InputChangeValue<TComponent>) => Value<TDraft, TPath> }\n : { deserialize: (value: InputChangeValue<TComponent>) => Value<TDraft, TPath> });\n\nexport function FormInput<\n TDraft,\n TPath extends PathAsString<TDraft>,\n TComponent extends FormInputComponent<any>,\n>(\n this: Form<TDraft, any>,\n {\n id,\n name,\n component = 'input' as TComponent,\n commitOnBlur,\n commitDebounce,\n inputFilter,\n serialize = (x) => x as InputValue<TComponent>,\n deserialize = (x) => x as Value<TDraft, TPath>,\n ...restProps\n }: FormInputProps<TDraft, TPath, TComponent>,\n): JSX.Element {\n type T = InputChangeValue<TComponent>;\n\n const { value, setValue, errors } = this.useField(name);\n const errorString = useMemo(() => errors.join('\\n'), [errors]);\n const [localValue, setLocalValue] = useState<T>();\n const _id = useMemo(\n () =>\n id || `f${Math.random().toString(36).slice(2, 15)}${Math.random().toString(36).slice(2, 15)}`,\n\n [id],\n );\n\n useEffect(() => {\n if (localValue === undefined || !commitDebounce) {\n return;\n }\n\n const timeout = setTimeout(() => {\n setValue(deserialize(localValue));\n setLocalValue(undefined);\n }, commitDebounce);\n\n return () => clearTimeout(timeout);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [localValue, commitDebounce]);\n\n useEffect(() => {\n const element = document.querySelector(\n [`#${_id} input`, `#${_id} select`, `#${_id} textarea`, `#${_id}`].join(','),\n );\n\n if (\n !(\n element instanceof HTMLInputElement ||\n element instanceof HTMLSelectElement ||\n element instanceof HTMLTextAreaElement\n )\n ) {\n return;\n }\n\n element.setCustomValidity(errorString);\n }, [_id, errorString]);\n\n const props = {\n ...restProps,\n id: _id,\n name,\n value: localValue ?? serialize(value),\n onChange: (event: { target: { value: T } } | T, ...moreArgs: any[]) => {\n const value =\n typeof event === 'object' && event !== null && 'target' in event\n ? event.target.value\n : event;\n\n if (inputFilter && !inputFilter(value)) {\n return;\n }\n\n if (commitOnBlur || commitDebounce) {\n setLocalValue(value);\n } else {\n setValue(deserialize(value));\n }\n\n restProps.onChange?.(event, ...moreArgs);\n },\n onBlur(...args: any[]) {\n if (localValue !== undefined) {\n setValue(deserialize(localValue));\n setLocalValue(undefined);\n }\n\n restProps.onBlur?.(...(args as [any]));\n },\n };\n\n return createElement(component, props as any);\n}\n","import { type KeyType } from './path';\nimport { castArrayPath } from './propAccess';\n\nexport function wildcardMatch(s: KeyType[] | string, w: KeyType[] | string): boolean {\n if (typeof s === 'string') {\n s = castArrayPath(s);\n }\n\n if (typeof w === 'string') {\n w = castArrayPath(w);\n }\n\n return s.length === w.length && s.every((s, i) => w[i] === '*' || s === w[i]);\n}\n\nexport function getWildCardMatches(object: any, path: KeyType[] | string): Record<KeyType, any> {\n const matches: Record<KeyType, any> = {};\n const [first, ...rest] = castArrayPath(path);\n\n if (first === undefined) {\n return object;\n }\n\n if (!(object instanceof Object)) {\n return {};\n }\n\n if (first === '*') {\n for (const [key, value] of Object.entries(object)) {\n matches[key] = getWildCardMatches(value, rest);\n }\n } else {\n matches[first] = getWildCardMatches(object[first], rest);\n }\n\n return matches;\n}\n","/* eslint-disable react-hooks/rules-of-hooks */\n\nimport {\n createContext,\n useContext,\n useMemo,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from 'react';\nimport { ScopeProvider, useScope } from '../scope';\nimport { useStore, type UseStoreOptions } from '../useStore';\nimport { FormError, type FormErrorProps } from './formError';\nimport { FormInput, type FormInputComponent, type FormInputProps } from './formInput';\nimport { Scope } from '@core';\nimport { deepEqual } from '@lib/equals';\nimport {\n type PathAsString,\n type Value,\n type WildcardPathAsString,\n type WildcardValue,\n} from '@lib/path';\nimport { get } from '@lib/propAccess';\nimport { getWildCardMatches, wildcardMatch } from '@lib/wildcardMatch';\n\n/// /////////////////////////////////////////////////////////////////////////////\n// Form types\n/// /////////////////////////////////////////////////////////////////////////////\n\nexport interface FormOptions<TDraft, TOriginal> {\n defaultValue: TDraft;\n validations?: Validations<TDraft, TOriginal>;\n}\n\nexport type Validations<TDraft, TOriginal> = {\n [P in WildcardPathAsString<TDraft>]?: Record<\n string,\n Validation<WildcardValue<TDraft, P>, TDraft, TOriginal>\n >;\n};\n\nexport type Validation<TValue, TDraft, TOriginal> = (\n value: TValue,\n context: { draft: TDraft; original: TOriginal; field: PathAsString<TDraft> },\n) => boolean;\n\nexport interface Field<TDraft, TOriginal, TPath extends PathAsString<TDraft>> {\n originalValue: Value<TOriginal, TPath> | undefined;\n value: Value<TDraft, TPath>;\n setValue: (\n value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>),\n ) => void;\n isDirty: boolean;\n errors: string[];\n}\n\n/// /////////////////////////////////////////////////////////////////////////////\n// Implementation\n/// /////////////////////////////////////////////////////////////////////////////\n\nfunction FormContainer({ form, children }: { form: Form<any, any>; children?: ReactNode }) {\n const { validate } = form.useForm();\n\n return (\n <form\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n\n validate();\n event.currentTarget.reportValidity();\n }}\n >\n {children}\n </form>\n );\n}\n\nexport class Form<TDraft, TOriginal extends TDraft = TDraft> {\n private context = createContext({\n original: undefined as TOriginal | undefined,\n options: this.options,\n });\n\n private state = new Scope<{\n draft?: TDraft;\n hasTriggeredValidations?: boolean;\n }>({});\n\n constructor(public readonly options: FormOptions<TDraft, TOriginal>) {\n this.Provider = this.Provider.bind(this);\n this.useForm = this.useForm.bind(this);\n this.useField = this.useField.bind(this);\n this.useHasChanges = this.useHasChanges.bind(this);\n this.useIsValid = this.useIsValid.bind(this);\n this.Input = this.Input.bind(this);\n this.Error = this.Error.bind(this);\n }\n\n Provider({\n children,\n original,\n defaultValue,\n validations,\n }: { children?: ReactNode; original?: TOriginal } & Partial<FormOptions<TDraft, TOriginal>>) {\n const value = useMemo(\n () => ({\n original,\n options: {\n defaultValue: { ...this.options.defaultValue, ...defaultValue },\n validations: { ...this.options.validations, ...validations } as Validations<\n TDraft,\n TOriginal\n >,\n },\n }),\n [original, defaultValue, validations],\n );\n\n return (\n <this.context.Provider value={value}>\n <ScopeProvider scope={this.state}>\n <FormContainer form={this}>{children}</FormContainer>\n </ScopeProvider>\n </this.context.Provider>\n );\n }\n\n useForm() {\n const { original, options } = useContext(this.context);\n const state = useScope(this.state);\n\n return useMemo(() => {\n const instance = {\n original,\n\n draft: state.map(\n (state) => state.draft ?? original ?? options.defaultValue,\n (draft) => (state) => ({ ...state, draft }),\n ),\n\n getField: <TPath extends PathAsString<TDraft>>(\n path: TPath,\n ): Field<TDraft, TOriginal, TPath> => {\n const { draft } = instance;\n\n return {\n get originalValue() {\n return original !== undefined ? get(original as any, path as any) : undefined;\n },\n\n get value() {\n return get(draft.get(), path);\n },\n\n setValue(update) {\n draft.set(path, update);\n },\n\n get isDirty() {\n const comparisonValue = this.originalValue ?? get(options.defaultValue, path);\n\n return state.get().hasTriggeredValidations || !deepEqual(comparisonValue, this.value);\n },\n\n get errors() {\n const blocks: Record<string, Validation<any, any, any>>[] = Object.entries(\n options.validations ?? {},\n )\n .filter(([key]) => wildcardMatch(path, key))\n .map(([, value]) => value);\n\n const value = this.value;\n const draftValue = draft.get();\n const errors: string[] = [];\n\n for (const block of blocks ?? []) {\n for (const [validationName, validate] of Object.entries(block)) {\n if (!validate(value, { draft: draftValue, original, field: path })) {\n errors.push(validationName);\n }\n }\n }\n\n return errors;\n },\n };\n },\n\n get hasChanges() {\n const { draft } = state.get();\n return !!draft && !deepEqual(draft, original ?? options.defaultValue);\n },\n\n get errors(): string[] {\n const draft = instance.draft.get();\n const errors = new Set<string>();\n\n for (const [path, block] of Object.entries(options.validations ?? {})) {\n for (const [validationName, validate] of Object.entries(\n block as Record<string, Validation<any, any, any>>,\n )) {\n for (const [field, value] of Object.entries(getWildCardMatches(draft, path))) {\n if (!validate(value, { draft, original, field })) {\n errors.add(`${field}.${validationName}`);\n }\n }\n }\n }\n\n return [...errors] as any;\n },\n\n get isValid() {\n return instance.errors.length === 0;\n },\n\n validate: () => {\n state.set('hasTriggeredValidations', true);\n return instance.isValid;\n },\n\n reset() {\n state.set('draft', undefined);\n },\n };\n\n return instance;\n }, [original, options, state]);\n }\n\n useField<TPath extends PathAsString<TDraft>>(path: TPath, useStoreOptions?: UseStoreOptions) {\n const form = this.useForm();\n const state = useScope(this.state);\n\n useStore(\n form.draft.map((draft) => get(draft, path)),\n useStoreOptions,\n );\n\n useStore(\n state.map((state) => state.hasTriggeredValidations),\n useStoreOptions,\n );\n\n return form.getField(path);\n }\n\n useHasChanges() {\n const form = this.useForm();\n\n return useStore(form.draft.map(() => form.hasChanges));\n }\n\n useIsValid() {\n const form = this.useForm();\n\n return useStore(form.draft.map(() => form.isValid));\n }\n\n Input<\n TPath extends PathAsString<TDraft>,\n TComponent extends FormInputComponent<any> = (\n props: ComponentPropsWithoutRef<'input'>,\n ) => JSX.Element,\n >(props: FormInputProps<TDraft, TPath, TComponent>): JSX.Element {\n return Reflect.apply(FormInput, this, [props]);\n }\n\n Error<TPath extends PathAsString<TDraft>>({ name }: FormErrorProps<TDraft, TPath>) {\n return Reflect.apply(FormError, this, [{ name }]);\n }\n}\n\nexport function createForm<TDraft, TOriginal extends TDraft = TDraft>(\n options: FormOptions<TDraft, TOriginal>,\n) {\n return new Form(options);\n}\n\n/* eslint-enable react-hooks/rules-of-hooks */\n","import { useCache } from './useCache';\nimport type { UseStoreOptions } from './useStore';\nimport type { Cache } from '@core';\n\nexport function read<T>(cache: Cache<T>, options?: UseStoreOptions): T {\n const { status, value, error } = useCache(cache, options);\n\n if (status === 'value') {\n return value;\n }\n\n if (status === 'error') {\n throw error;\n }\n\n throw cache.state.once((state) => state.status !== 'pending');\n}\n","import { startTransition, useEffect, useMemo, useRef, useState } from 'react';\nimport { type Duration } from '@core';\nimport { debounce } from '@lib/debounce';\nimport { hash } from '@lib/hash';\nimport { throttle } from '@lib/throttle';\n\nexport interface UseDecoupledStateOptions<T> {\n debounce?: Duration;\n throttle?: Duration;\n onCommit?: (value: T) => void;\n}\n\nexport function useDecoupledState<T>(\n value: T,\n onChange: (value: T) => void,\n options: UseDecoupledStateOptions<T> = {},\n): [state: T, setState: (value: T) => void] {\n const [dirty, setDirty] = useState<{ v: T }>();\n const ref = useRef({ onChange, onCommit: options.onCommit });\n\n useEffect(() => {\n ref.current = { onChange, onCommit: options.onCommit };\n }, [onChange]);\n\n const update = useMemo(() => {\n const { onChange, onCommit } = ref.current;\n\n const update = (value: T) => {\n onChange(value);\n setDirty(undefined);\n onCommit?.(value);\n };\n\n let delayedUpdate: (value: T) => void;\n\n if (options.debounce) {\n delayedUpdate = debounce(update, options.debounce);\n } else if (options.throttle) {\n delayedUpdate = throttle(update, options.throttle);\n } else {\n delayedUpdate = (value) => startTransition(() => update(value));\n }\n\n return (value: T) => {\n setDirty({ v: value });\n delayedUpdate(value);\n };\n }, [hash([options.debounce, options.throttle])]);\n\n return [dirty ? dirty.v : value, update];\n}\n"],"names":["jsx","Fragment","useMemo","useState","useEffect","value","createElement","castArrayPath","s","createContext","Scope","ScopeProvider","useContext","useScope","state","get","deepEqual","useStore","useCache","useRef","onChange","update","debounce","throttle","startTransition","hash"],"mappings":"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"}
@@ -3,25 +3,9 @@ const jsxRuntime = require("react/jsx-runtime");
3
3
  const require$$0 = require("react");
4
4
  const store = require("./store.cjs");
5
5
  const scope = require("./scope.cjs");
6
- var withSelectorExports = {};
7
- var withSelector = {
8
- get exports() {
9
- return withSelectorExports;
10
- },
11
- set exports(v) {
12
- withSelectorExports = v;
13
- }
14
- };
6
+ var withSelector = { exports: {} };
15
7
  var withSelector_production_min = {};
16
- var shimExports = {};
17
- var shim = {
18
- get exports() {
19
- return shimExports;
20
- },
21
- set exports(v) {
22
- shimExports = v;
23
- }
24
- };
8
+ var shim = { exports: {} };
25
9
  var useSyncExternalStoreShim_production_min = {};
26
10
  /**
27
11
  * @license React
@@ -209,16 +193,14 @@ function requireUseSyncExternalStoreShim_development() {
209
193
  var hasRequiredShim;
210
194
  function requireShim() {
211
195
  if (hasRequiredShim)
212
- return shimExports;
196
+ return shim.exports;
213
197
  hasRequiredShim = 1;
214
- (function(module2) {
215
- if (process.env.NODE_ENV === "production") {
216
- module2.exports = requireUseSyncExternalStoreShim_production_min();
217
- } else {
218
- module2.exports = requireUseSyncExternalStoreShim_development();
219
- }
220
- })(shim);
221
- return shimExports;
198
+ if (process.env.NODE_ENV === "production") {
199
+ shim.exports = requireUseSyncExternalStoreShim_production_min();
200
+ } else {
201
+ shim.exports = requireUseSyncExternalStoreShim_development();
202
+ }
203
+ return shim.exports;
222
204
  }
223
205
  /**
224
206
  * @license React
@@ -384,13 +366,12 @@ function requireWithSelector_development() {
384
366
  }
385
367
  return withSelector_development;
386
368
  }
387
- (function(module2) {
388
- if (process.env.NODE_ENV === "production") {
389
- module2.exports = requireWithSelector_production_min();
390
- } else {
391
- module2.exports = requireWithSelector_development();
392
- }
393
- })(withSelector);
369
+ if (process.env.NODE_ENV === "production") {
370
+ withSelector.exports = requireWithSelector_production_min();
371
+ } else {
372
+ withSelector.exports = requireWithSelector_development();
373
+ }
374
+ var withSelectorExports = withSelector.exports;
394
375
  const unwrapProxySymbol = /* @__PURE__ */ Symbol("unwrapProxy");
395
376
  function isPlainObject(value) {
396
377
  return typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype;