effector-final-form
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -1,2 +0,1 @@ | ||
import type { Domain } from 'effector'; | ||
import type { FormApi as FFFormApi } from 'final-form'; | ||
@@ -7,6 +6,5 @@ import type { createFields } from './createFields'; | ||
declare const createApi: <FormValues, T extends FormSubscription>(config: { | ||
domain: Domain; | ||
finalForm: FFFormApi<FormValues, Partial<FormValues>>; | ||
fieldsApi: { | ||
update: import("effector").Event<void>; | ||
update: import("effector").Event<import("final-form").FieldState<FormValues[keyof FormValues]>>; | ||
}; | ||
@@ -13,0 +11,0 @@ formStateApi: { |
@@ -1,10 +0,8 @@ | ||
import type { Domain } from 'effector'; | ||
import type { FormApi as FFFormApi } from 'final-form'; | ||
import type { FieldState as FFFieldState, FormApi as FFFormApi } from 'final-form'; | ||
declare const createFields: <FormValues>(config: { | ||
domain: Domain; | ||
finalForm: FFFormApi<FormValues, Partial<FormValues>>; | ||
}) => { | ||
$fields: import("effector").Store<{ [T in keyof FormValues]: import("final-form").FieldState<FormValues[keyof FormValues]>; }>; | ||
$fields: import("effector").Store<{ [T in keyof FormValues]: FFFieldState<FormValues[keyof FormValues]>; }>; | ||
fieldsApi: { | ||
update: import("effector").Event<void>; | ||
update: import("effector").Event<FFFieldState<FormValues[keyof FormValues]>>; | ||
}; | ||
@@ -11,0 +9,0 @@ $registeredFields: import("effector").Store<[] | (keyof FormValues)[]>; |
@@ -1,6 +0,4 @@ | ||
import type { Domain } from 'effector'; | ||
import type { FormApi as FFFormApi, FormState as FFFormState } from 'final-form'; | ||
import type { FormSubscription } from './types'; | ||
declare const createFormState: <FormValues, T extends FormSubscription>(config: { | ||
domain: Domain; | ||
finalForm: FFFormApi<FormValues, Partial<FormValues>>; | ||
@@ -7,0 +5,0 @@ subscribeOn: T; |
import type { Config as FFConfig } from 'final-form'; | ||
import type { FormSubscription } from './types'; | ||
export declare const createForm: <FormValues, T extends FormSubscription>(config: Omit<FFConfig<FormValues, Partial<FormValues>>, "mutators" | "debug"> & { | ||
declare const createForm: <FormValues, T extends FormSubscription>(config: Omit<FFConfig<FormValues, Partial<FormValues>>, "debug"> & { | ||
subscribeOn: T; | ||
@@ -23,2 +23,5 @@ }) => { | ||
api: { | ||
reValidateFx: import("effector").Effect<void, void, Error>; | ||
setValidationFn: (fn: (params: FormValues) => import("final-form").ValidationErrors | Promise<import("final-form").ValidationErrors>) => void; | ||
setSubmitFn: (fn: (params: FormValues) => void | import("final-form").SubmissionErrors | Promise<void | import("final-form").SubmissionErrors>) => void; | ||
blurFx: import("effector").Effect<keyof FormValues, void, Error>; | ||
@@ -44,4 +47,4 @@ changeFx: import("effector").Effect<{ | ||
$fields: import("effector").Store<{ [T_1 in keyof FormValues]: import("final-form").FieldState<FormValues[keyof FormValues]>; }>; | ||
domain: import("effector").Domain; | ||
$registeredFields: import("effector").Store<[] | (keyof FormValues)[]>; | ||
}; | ||
export { createForm }; |
@@ -1,80 +0,157 @@ | ||
import { sample as u, createDomain as g } from "effector"; | ||
import { fieldSubscriptionItems as b, formSubscriptionItems as S, createForm as y } from "final-form"; | ||
const m = (s) => s == null, V = (s, e) => ( | ||
import { createEffect as a, createEvent as g, createStore as O, sample as j } from "effector"; | ||
import { fieldSubscriptionItems as E, formSubscriptionItems as P, createForm as h } from "final-form"; | ||
function S(t) { | ||
if (t === null) | ||
return "Null"; | ||
if (t === void 0) | ||
return "Undefined"; | ||
if (Number.isNaN(t)) | ||
return "NaN"; | ||
const e = Object.prototype.toString.call(t).slice(8, -1); | ||
return e === "AsyncFunction" ? "Promise" : e; | ||
} | ||
const b = (t) => { | ||
const e = t.__proto__.toString(); | ||
return ["Error", "TypeError"].includes(e) ? [e, t.message] : []; | ||
}, V = (t) => t.toDateString ? [!0, t.getTime()] : [!1], y = (t) => t.constructor !== RegExp ? [!1] : [!0, t.toString()], F = (t, e) => { | ||
const r = S(t); | ||
if (r !== S(e)) | ||
return !1; | ||
if (r === "Function") | ||
return t.name === void 0 ? !1 : t.name === e.name; | ||
if (["NaN", "Undefined", "Null"].includes(r)) | ||
return !0; | ||
if (r === "Number") | ||
return Object.is(-0, t) !== Object.is(-0, e) ? !1 : t - e === 0; | ||
if (["String", "Boolean"].includes(r)) | ||
return t.toString() === e.toString(); | ||
if (r === "Array") { | ||
const o = Array.from(t), u = Array.from(e); | ||
return o.toString() !== u.toString() ? !1 : o.every((f, p) => f === u[p] || F(f, u[p])); | ||
} | ||
const s = y(t), n = y(e); | ||
if (s[0]) | ||
return n[0] ? s[1] === n[1] : !1; | ||
if (n[0]) | ||
return !1; | ||
const c = V(t), i = V(e); | ||
if (c[0]) | ||
return i[0] ? c[1] === i[1] : !1; | ||
if (i[0]) | ||
return !1; | ||
const l = b(t), d = b(e); | ||
if (l[0]) | ||
return d[0] ? l[0] === d[0] && l[1] === d[1] : !1; | ||
if (r === "Object") { | ||
const o = Object.keys(t); | ||
return o.length !== Object.keys(e).length ? !1 : o.every((u) => { | ||
const f = t[u], p = e[u]; | ||
return f === p || F(f, p); | ||
}); | ||
} | ||
return !1; | ||
}, _ = (t) => t == null, x = (t, e) => ( | ||
// @ts-expect-error | ||
m(e) ? {} : s.reduce( | ||
(t, i) => Object.prototype.hasOwnProperty.call(e, i) ? { ...t, [i]: e[i] } : t, | ||
_(e) ? {} : t.reduce( | ||
(r, s) => Object.prototype.hasOwnProperty.call(e, s) ? { ...r, [s]: e[s] } : r, | ||
{} | ||
) | ||
), F = (s, e) => s.reduce((t, i) => ({ ...t, [i]: e.includes(i) }), {}), O = (s) => { | ||
const { domain: e, finalForm: t, fieldsApi: i, formStateApi: r } = s, o = ({ name: c, value: f }) => t.change(c, f), d = ({ | ||
name: c, | ||
subscribeOn: f, | ||
config: p | ||
), v = (t, e) => t.reduce((r, s) => ({ ...r, [s]: e.includes(s) }), {}), A = (t, e) => !F(t, e), $ = (t) => { | ||
const { finalForm: e, fieldsApi: r, formStateApi: s } = t, n = ({ name: o, value: u }) => e.change(o, u), c = ({ | ||
name: o, | ||
subscribeOn: u, | ||
config: f | ||
}) => { | ||
t.registerField(c, () => { | ||
}, F(b, f), p); | ||
}, n = () => { | ||
t.pauseValidation(), r.setValidationPaused(!0); | ||
e.registerField( | ||
o, | ||
r.update, | ||
v(E, [...u, "value"]), | ||
f | ||
); | ||
}, i = () => { | ||
e.pauseValidation(), s.setValidationPaused(!0); | ||
}, l = () => { | ||
t.resumeValidation(), r.setValidationPaused(!1); | ||
}, a = { | ||
blurFx: e.effect(t.blur), | ||
changeFx: e.effect(o), | ||
focusFx: e.effect(t.focus), | ||
initialize: e.effect(t.initialize), | ||
pauseValidation: e.effect(n), | ||
registerField: e.effect(d), | ||
reset: e.effect(t.reset), | ||
resetFieldState: e.effect(t.resetFieldState), | ||
restart: e.effect(t.restart), | ||
resumeValidation: e.effect(l), | ||
submitFx: e.effect(t.submit) | ||
e.resumeValidation(), s.setValidationPaused(!1); | ||
}; | ||
return u({ | ||
clock: [ | ||
a.blurFx.finally, | ||
a.changeFx.finally, | ||
a.focusFx.finally, | ||
a.initialize.finally, | ||
a.registerField.finally, | ||
a.reset.finally, | ||
a.resetFieldState.finally, | ||
a.restart.finally, | ||
a.submitFx.finally | ||
], | ||
target: i.update | ||
}), a; | ||
}, x = (s) => { | ||
const { domain: e, finalForm: t } = s, i = () => t.getRegisteredFields().reduce( | ||
(n, l) => Object.assign({}, n, { | ||
[l]: t.getFieldState(l) | ||
}), | ||
{} | ||
), r = { | ||
update: e.event() | ||
}, o = e.store(i()), d = o.map((n) => Object.keys(n)); | ||
return u({ clock: r.update, fn: i, target: o }), { $fields: o, fieldsApi: r, $registeredFields: d }; | ||
}, A = (s) => { | ||
const { domain: e, finalForm: t, subscribeOn: i } = s, r = V([...i, "isValidationPaused"], { | ||
...t.getState(), | ||
return { | ||
blurFx: a(e.blur), | ||
changeFx: a(n), | ||
focusFx: a(e.focus), | ||
initialize: a(e.initialize), | ||
pauseValidation: a(i), | ||
registerField: a(c), | ||
reset: a(e.reset), | ||
resetFieldState: a(e.resetFieldState), | ||
restart: a(e.restart), | ||
resumeValidation: a(l), | ||
submitFx: a(e.submit) | ||
}; | ||
}, k = (t) => { | ||
const e = { | ||
update: g() | ||
}, r = O( | ||
// @ts-expect-error | ||
{}, | ||
{ updateFilter: A } | ||
), s = r.map((n) => Object.keys(n)); | ||
return j({ | ||
clock: e.update, | ||
source: r, | ||
fn: (n, c) => ({ ...n, [c.name]: c }), | ||
target: r | ||
}), { $fields: r, fieldsApi: e, $registeredFields: s }; | ||
}, z = (t) => { | ||
const { finalForm: e, subscribeOn: r } = t, s = x([...r, "isValidationPaused"], { | ||
...e.getState(), | ||
isValidationPaused: !1 | ||
}), o = { | ||
update: e.event(), | ||
setValidationPaused: e.event() | ||
}, d = e.store(r).on(o.update, (n, l) => Object.assign({}, n, l)).on(o.setValidationPaused, (n, l) => Object.assign({}, n, { isValidationPaused: l })); | ||
return t.subscribe((n) => { | ||
const l = i.reduce((a, c) => m(n[c]) ? { ...a, [c]: null } : a, n); | ||
o.update(l); | ||
}, F(S, i)), { $formState: d, formStateApi: o }; | ||
}, h = (s) => { | ||
const { subscribeOn: e, ...t } = s, i = y(t), r = g(), { $fields: o, $registeredFields: d, fieldsApi: n } = x({ domain: r, finalForm: i }), { $formState: l, formStateApi: a } = A({ | ||
domain: r, | ||
finalForm: i, | ||
}), n = { | ||
update: g(), | ||
setValidationPaused: g() | ||
}, c = O(s, { | ||
updateFilter: A | ||
}).on(n.update, (i, l) => Object.assign({}, i, l)).on(n.setValidationPaused, (i, l) => Object.assign({}, i, { isValidationPaused: l })); | ||
return e.subscribe((i) => { | ||
const l = r.reduce((d, o) => _(i[o]) ? { ...d, [o]: null } : d, i); | ||
n.update(l); | ||
}, v(P, r)), { $formState: c, formStateApi: n }; | ||
}, H = () => { | ||
}, w = (t) => { | ||
const { subscribeOn: e, ...r } = t, s = a(r.validate ?? H), n = a(r.onSubmit), c = h({ | ||
...r, | ||
validate: s, | ||
onSubmit: async (m) => { | ||
try { | ||
return await n(m); | ||
} catch (N) { | ||
return N; | ||
} | ||
}, | ||
mutators: { | ||
__update__: () => { | ||
} | ||
} | ||
}), i = a(() => { | ||
c.mutators.__update__(); | ||
}), { $fields: l, $registeredFields: d, fieldsApi: o } = k(), { $formState: u, formStateApi: f } = z({ | ||
finalForm: c, | ||
subscribeOn: e | ||
}), c = O({ domain: r, finalForm: i, fieldsApi: n, formStateApi: a }); | ||
return { $formState: l, api: c, $fields: o, domain: r, $registeredFields: d }; | ||
}), p = $({ finalForm: c, fieldsApi: o, formStateApi: f }); | ||
return i(), { | ||
$formState: u, | ||
api: { | ||
...p, | ||
reValidateFx: i, | ||
setValidationFn: (m) => { | ||
s.use(m), i(); | ||
}, | ||
setSubmitFn: (m) => { | ||
n.use(m); | ||
} | ||
}, | ||
$fields: l, | ||
$registeredFields: d | ||
}; | ||
}; | ||
export { | ||
h as createForm | ||
w as createForm | ||
}; |
{ | ||
"name": "effector-final-form", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "☄️ Effector bindings for Final Form", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -33,10 +33,18 @@ # effector-final-form | ||
## Differences from Final Form | ||
[Documentation link](https://binjospookie.github.io/effector-final-form/docs/differences) | ||
## Limitations | ||
[Documentation link](https://binjospookie.github.io/effector-final-form/docs/limitations) | ||
## Base example | ||
```ts | ||
import { allSettled, fork } from 'effector'; | ||
import { createForm } from 'effector-final-form'; | ||
import { vi } from 'vitest'; | ||
import waitForExpect from 'wait-for-expect'; | ||
vi.useFakeTimers(); | ||
// import { createForm } from 'effector-final-form'; | ||
import { createForm } from '../../index'; | ||
@@ -47,3 +55,3 @@ const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); | ||
test('', async () => { | ||
const { $formState, $fields, domain, api } = createForm({ | ||
const { $formState, $fields, api } = createForm({ | ||
onSubmit: async (f) => { | ||
@@ -58,40 +66,42 @@ await sleep(1000); | ||
}); | ||
const scope = fork(domain); | ||
await allSettled(api.registerField, { | ||
scope, | ||
params: { | ||
name: 'firstName', | ||
subscribeOn: ['value', 'error', 'initial'], | ||
}, | ||
await api.registerField({ | ||
name: 'firstName', | ||
subscribeOn: ['value', 'error', 'initial'], | ||
}); | ||
{ | ||
await allSettled(api.changeFx, { scope, params: { name: 'firstName', value: '' } }); | ||
expect(scope.getState($fields).firstName.error).toBe('Can not be empty'); | ||
expect(scope.getState($fields).firstName.initial).toBe(''); | ||
expect(scope.getState($fields).firstName.value).toBe(''); | ||
api.changeFx({ name: 'firstName', value: '' }); | ||
expect(scope.getState($formState).errors).toStrictEqual({ firstName: 'Can not be empty' }); | ||
expect(scope.getState($formState).values).toStrictEqual({ firstName: '' }); | ||
expect(scope.getState($formState).submitting).toBe(false); | ||
expect(scope.getState($formState).submitSucceeded).toBe(false); | ||
expect(scope.getState($formState).submitFailed).toBe(false); | ||
expect(scope.getState($formState).submitErrors).toBe(null); | ||
} | ||
await waitForExpect(() => { | ||
expect($fields.getState().firstName.error).toBe('Can not be empty'); | ||
}); | ||
expect($fields.getState().firstName.initial).toBe(''); | ||
expect($fields.getState().firstName.value).toBe(''); | ||
expect($formState.getState().errors).toStrictEqual({ firstName: 'Can not be empty' }); | ||
expect($formState.getState().values).toStrictEqual({ firstName: '' }); | ||
expect($formState.getState().submitting).toBe(false); | ||
expect($formState.getState().submitSucceeded).toBe(false); | ||
expect($formState.getState().submitFailed).toBe(false); | ||
expect($formState.getState().submitErrors).toBe(null); | ||
{ | ||
await allSettled(api.changeFx, { scope, params: { name: 'firstName', value: 'Incorrect' } }); | ||
expect(scope.getState($fields).firstName.error).toBe(undefined); | ||
expect(scope.getState($fields).firstName.value).toBe('Incorrect'); | ||
expect(scope.getState($formState).errors).toStrictEqual({}); | ||
await api.changeFx({ name: 'firstName', value: 'Incorrect' }); | ||
await waitForExpect(() => { | ||
expect($fields.getState().firstName.error).toBe(undefined); | ||
expect($fields.getState().firstName.value).toBe('Incorrect'); | ||
expect($formState.getState().errors).toStrictEqual({}); | ||
}); | ||
} | ||
{ | ||
const submitPromise = allSettled(api.submitFx, { scope }); | ||
vi.runOnlyPendingTimers(); | ||
const submitPromise = api.submitFx(); | ||
expect(scope.getState($formState).submitting).toBe(true); | ||
expect($formState.getState().submitting).toBe(true); | ||
vi.useFakeTimers(); | ||
vi.runOnlyPendingTimers(); | ||
await submitPromise; | ||
vi.useRealTimers(); | ||
@@ -105,15 +115,22 @@ expect($formState.getState().submitting).toBe(false); | ||
{ | ||
await allSettled(api.changeFx, { scope, params: { name: 'firstName', value: 'John' } }); | ||
expect(scope.getState($fields).firstName.error).toBe(undefined); | ||
expect(scope.getState($fields).firstName.value).toBe('John'); | ||
expect(scope.getState($formState).errors).toStrictEqual({}); | ||
await api.changeFx({ name: 'firstName', value: 'John' }); | ||
const submitPromise = allSettled(api.submitFx, { scope }); | ||
vi.runOnlyPendingTimers(); | ||
expect($fields.getState().firstName.error).toBe(undefined); | ||
expect($fields.getState().firstName.value).toBe('John'); | ||
expect($formState.getState().errors).toStrictEqual({}); | ||
expect(scope.getState($formState).submitting).toBe(true); | ||
const submitPromise = api.submitFx(); | ||
await waitForExpect(() => { | ||
expect($formState.getState().submitting).toBe(true); | ||
}); | ||
vi.useFakeTimers(); | ||
vi.runOnlyPendingTimers(); | ||
await submitPromise; | ||
vi.useRealTimers(); | ||
expect($formState.getState().submitting).toBe(false); | ||
await waitForExpect(() => { | ||
expect($formState.getState().submitting).toBe(false); | ||
}); | ||
expect($formState.getState().submitSucceeded).toBe(true); | ||
@@ -124,2 +141,3 @@ expect($formState.getState().submitFailed).toBe(false); | ||
}); | ||
}); | ||
``` | ||
@@ -126,0 +144,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
23080
13
344
143