@picked/react 0.0.50 → 0.0.51-next.1

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,16 +1,28 @@
1
- import { ValidationError } from '@picked/client';
2
- interface GetFormOptions {
3
- id: string;
1
+ import { FormInstance as CoreFormInstance, ErrorCallback, SubmitCallback, SuccessCallback } from '@picked/client/form';
2
+ interface UseFormOptions {
3
+ formId: string;
4
4
  title: string;
5
- onSuccess?: () => void | Promise<void>;
6
- onError?: (errors: ValidationError[]) => void | Promise<void>;
5
+ fieldIds: string[];
7
6
  }
8
- export declare function useForm(options: GetFormOptions): {
9
- form: {
10
- onsubmit: (event: SubmitEvent & {
11
- currentTarget: EventTarget & HTMLFormElement;
12
- }) => Promise<void>;
7
+ /**
8
+ * React FormInstance — identical to core but callback registration returns void
9
+ * (cleanup is handled automatically via useEffect, not by the consumer).
10
+ */
11
+ interface FormInstance<TFieldIds extends string = string> extends Omit<CoreFormInstance<TFieldIds>, 'form'> {
12
+ form: Omit<CoreFormInstance<TFieldIds>['form'], 'onBeforeSubmit' | 'onSuccess' | 'onError'> & {
13
+ onBeforeSubmit: (callback: SubmitCallback<TFieldIds>) => void;
14
+ onSuccess: (callback: SuccessCallback<TFieldIds>) => void;
15
+ onError: (callback: ErrorCallback) => void;
13
16
  };
14
- };
17
+ }
18
+ /**
19
+ * React hook wrapping the framework-agnostic createForm from @picked/client/form.
20
+ *
21
+ * - Injects client and experienceInstance from React context
22
+ * - Collects unsubscribe functions from callback registration and cleans them
23
+ * up on unmount via a single useEffect
24
+ * - Consumer DX: `form.onSuccess(cb)` — no hooks or lifecycle awareness needed
25
+ */
26
+ export declare function useForm({ formId, title, fieldIds }: UseFormOptions): FormInstance;
15
27
  export {};
16
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/form/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAQxE,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9D;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,cAAc;;0BAKd,WAAW,GAAG;YAAE,aAAa,EAAE,WAAW,GAAG,eAAe,CAAA;SAAE;;EA+G9F"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/form/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,YAAY,IAAI,gBAAgB,EACrC,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAA;AAG5B,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;GAGG;AACH,UAAU,YAAY,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,IAAI,CACpE,gBAAgB,CAAC,SAAS,CAAC,EAC3B,MAAM,CACP;IACC,IAAI,EAAE,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,WAAW,GAAG,SAAS,CAAC,GAAG;QAC5F,cAAc,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,SAAS,CAAC,KAAK,IAAI,CAAA;QAC7D,SAAS,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,SAAS,CAAC,KAAK,IAAI,CAAA;QACzD,OAAO,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAA;KAC3C,CAAA;CACF;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,cAAc,GAAG,YAAY,CAwCjF"}
@@ -1 +1 @@
1
- import{useExperience as e,usePicked as t}from"../hooks/client.js";import"../hooks/index.js";function n(n){let r=t(),i=e(),{id:a,title:o}=n;async function s(e){e.preventDefault(),r.eventBus({type:`form_submit`,form:{id:a,title:o},experience:i});let t=new FormData(e.currentTarget),s={data:{},push(e){let t=e.questionId,n=this.data[t];n&&Array.isArray(n)&&Array.isArray(e.value)?Array.isArray(n)&&Array.isArray(e.value)&&(typeof n[0]==`string`?this.data[t]=[...n,...e.value].map(String):typeof n[0]==`number`?this.data[t]=[...n,...e.value].map(Number):typeof n[0]==`boolean`?this.data[t]=[...n,...e.value].map(e=>!!e):this.data[t]=e.value):this.data[t]=e.value}};for(let[e,n]of t.entries()){let t=r.fields.getById(e).getState();if(!t)continue;let i=t.id;switch(t.type){case`text`:s.push({questionId:i,value:t.multiple?[String(n)]:String(n)});break;case`number`:s.push({questionId:i,value:t.multiple?[Number(n)]:Number(n)});break;case`boolean`:s.push({questionId:i,value:t.multiple?[n===`true`]:n===`true`});break}}let{errors:c}=await r.responses.commit(s.data);if(c.length>0){r.eventBus({type:`form_submit_error`,form:{id:a,title:o},exception:{reason:c},experience:i}),c.forEach(e=>{r.eventBus({type:`field_validation_error`,exception:{reason:[e]},experience:i})}),await n.onError?.(c);return}r.eventBus({type:`form_submit_success`,form:{id:a,title:o},experience:i}),await n.onSuccess?.()}return{form:{onsubmit:s}}}export{n as useForm};
1
+ import{useExperience as e,usePicked as t}from"../hooks/client.js";import"../hooks/index.js";import{useEffect as n,useMemo as r,useRef as i}from"react";import{createStore as a}from"effector";import{createForm as o}from"@picked/client/form";function s({formId:s,title:c,fieldIds:l}){let u=t(),d=e(),f=r(()=>a(c),[c]),p=r(()=>a(l),[l]),m=r(()=>o({client:u,experienceInstance:d},s,f,p),[u,d,s,f,p]),h=i([]);return n(()=>()=>{h.current.forEach(e=>e()),h.current=[]},[]),{form:r(()=>({...m.form,onBeforeSubmit:e=>{h.current.push(m.form.onBeforeSubmit(e))},onSuccess:e=>{h.current.push(m.form.onSuccess(e))},onError:e=>{h.current.push(m.form.onError(e))}}),[m]),fields:m.fields}}export{s as useForm};
@@ -1,5 +1,5 @@
1
1
  export { usePicked, useExperience, getPicked, getExperience } from './client';
2
- export { useStep, useSetStepContext } from './step';
2
+ export { useStep, useSetStepContext, type StepContext } from './step';
3
3
  export { useSessionStorage, useLocalStorage } from './storage';
4
4
  export * from './field';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7E,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC9D,cAAc,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7E,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAA;AACrE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC9D,cAAc,SAAS,CAAA"}
@@ -1,26 +1,31 @@
1
- import { Field, Response } from '@picked/client';
2
- type FieldWithValue = Field & {
3
- value: Response | undefined;
4
- };
5
- type StepWithLabel = {
6
- id: string;
7
- label: string;
8
- };
1
+ import { EventCallable, Store } from 'effector';
2
+ import { ExperienceStep, ValidationError } from '@picked/client';
3
+ import { FormField, ErrorCallback, SubmitCallback, SubmitEventWithCurrentTarget, SuccessCallback } from '@picked/client/form';
9
4
  export interface StepContext {
10
- step: StepWithLabel;
11
- fields: FieldWithValue[];
12
- isActiveStep?: boolean;
5
+ step: Store<ExperienceStep>;
6
+ /** Reactive store of fields with current values, errors, touched state, and event handlers */
7
+ fields: Store<FormField[]>;
8
+ isActive: boolean;
9
+ /** @deprecated Use `isActive` instead */
10
+ isActiveStep: boolean;
11
+ form: {
12
+ /** HTML-safe attributes to spread onto a <form> element */
13
+ props: {
14
+ onsubmit: EventCallable<SubmitEventWithCurrentTarget>;
15
+ };
16
+ /** Flat list of all current validation errors */
17
+ errors: Store<ValidationError[]>;
18
+ /** Whether the form is currently submitting */
19
+ submitting: Store<boolean>;
20
+ /** Register a callback to transform data before submission. Cleanup is automatic. */
21
+ onBeforeSubmit: (callback: SubmitCallback) => void;
22
+ /** Register a callback to run on successful submission. Cleanup is automatic. */
23
+ onSuccess: (callback: SuccessCallback) => void;
24
+ /** Register a callback to run on validation errors. Cleanup is automatic. */
25
+ onError: (callback: ErrorCallback) => void;
26
+ };
13
27
  }
14
28
  export declare const StepContextProvider: import('react').Context<StepContext | null>;
15
- export declare function useStep(): {
16
- step: StepWithLabel;
17
- fields: FieldWithValue[];
18
- isActiveStep: boolean;
19
- };
20
- export declare function useSetStepContext(stepId: string): {
21
- step: StepWithLabel;
22
- fields: FieldWithValue[];
23
- isActiveStep: boolean;
24
- };
25
- export {};
29
+ export declare function useStep(): StepContext;
30
+ export declare function useSetStepContext(stepId: string): StepContext;
26
31
  //# sourceMappingURL=step.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"step.d.ts","sourceRoot":"","sources":["../../src/hooks/step.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAoC,MAAM,gBAAgB,CAAA;AAMvF,KAAK,cAAc,GAAG,KAAK,GAAG;IAAE,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAA;CAAE,CAAA;AAC7D,KAAK,aAAa,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAOlD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,aAAa,CAAA;IACnB,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,eAAO,MAAM,mBAAmB,6CAA0C,CAAA;AAE1E,wBAAgB,OAAO;;;;EAWtB;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM;;;;EA+F/C"}
1
+ {"version":3,"file":"step.d.ts","sourceRoot":"","sources":["../../src/hooks/step.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACrE,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,4BAA4B,EACjC,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAA;AAI5B,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,KAAK,CAAC,cAAc,CAAC,CAAA;IAC3B,8FAA8F;IAC9F,MAAM,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAA;IAC1B,QAAQ,EAAE,OAAO,CAAA;IACjB,yCAAyC;IACzC,YAAY,EAAE,OAAO,CAAA;IACrB,IAAI,EAAE;QACJ,2DAA2D;QAC3D,KAAK,EAAE;YACL,QAAQ,EAAE,aAAa,CAAC,4BAA4B,CAAC,CAAA;SACtD,CAAA;QACD,iDAAiD;QACjD,MAAM,EAAE,KAAK,CAAC,eAAe,EAAE,CAAC,CAAA;QAChC,+CAA+C;QAC/C,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;QAC1B,qFAAqF;QACrF,cAAc,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAA;QAClD,iFAAiF;QACjF,SAAS,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,IAAI,CAAA;QAC9C,6EAA6E;QAC7E,OAAO,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAA;KAC3C,CAAA;CACF;AAED,eAAO,MAAM,mBAAmB,6CAA0C,CAAA;AAE1E,wBAAgB,OAAO,IAAI,WAAW,CAMrC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAiE7D"}
@@ -1 +1 @@
1
- import{useExperience as e,usePicked as t}from"./client.js";import{useStepRouter as n}from"../router/context.js";import{createContext as r,useContext as i,useMemo as a}from"react";import{attach as o,combine as s,createEffect as c,createStore as l,sample as u}from"effector";import{evaluateRuleLogic as d}from"@picked/client";import{useUnit as f}from"effector-react";var p=r(null);function m(){let e=i(p);if(!e)throw Error(`useStep must be used within a StepContextProvider`);return{step:e.step,fields:e.fields,isActiveStep:e.isActiveStep??!1}}function h(r){let i=e(),p=t(),{activeStep:m}=n(),h=a(()=>m?.id===r,[m?.id,r]),{stepContext:g}=a(()=>{let e=o({source:s({steps:i.treeStore.map(e=>e.steps),fields:p.fields.listStore,responses:p.responses.listStore}),effect:c(async({steps:e,fields:t,responses:n})=>{let i=e.find(e=>e.id===r);if(!i)throw Error(`Step with id "${r}" not found in the experience tree.`);let a=t.filter(e=>i.fieldIds.includes(e.id)),o=[];for(let e of a)e.condition?await d(p,e.condition)&&o.push(e):o.push(e);let s=o.map(e=>{let t=n.find(t=>t.fieldId===e.id)?.value;return{...e,value:t}});return{step:{id:i.id,label:i.label},fields:s}})}),t=l({step:{id:r,label:``},fields:[]});return t.on(e.doneData,(e,t)=>t),u({clock:[p.fields.listStore,p.responses.listStore],target:e}),e(),{stepContextFx:e,stepContext:t}},[r,p,i]),_=f(g);return{step:_?.step,fields:_?.fields||[],isActiveStep:h}}export{p as StepContextProvider,h as useSetStepContext,m as useStep};
1
+ import{useExperience as e,usePicked as t}from"./client.js";import{useStepRouter as n}from"../router/context.js";import{createContext as r,useContext as i,useEffect as a,useMemo as o,useRef as s}from"react";import{createForm as c}from"@picked/client/form";var l=r(null);function u(){let e=i(l);if(!e)throw Error(`useStep must be used within a StepContextProvider`);return e}function d(r){let i=e(),l=t(),{activeStep:u}=n(),d=o(()=>u?.id===r,[u?.id,r]),f=o(()=>i.treeStore.map(e=>{let t=e.steps.find(e=>e.id===r);if(!t)throw Error(`Step with id "${r}" not found in the experience tree.`);return t}),[i,r]),p=o(()=>f.map(e=>e.label),[f]),m=o(()=>f.map(e=>e.fieldIds),[f]),h=o(()=>c({client:l,experienceInstance:i},r,p,m),[l,i,r,p,m]),g=s([]);a(()=>()=>{g.current.forEach(e=>e()),g.current=[]},[]);let _=o(()=>({...h.form,onBeforeSubmit:e=>{g.current.push(h.form.onBeforeSubmit(e))},onSuccess:e=>{g.current.push(h.form.onSuccess(e))},onError:e=>{g.current.push(h.form.onError(e))}}),[h]);return{step:f,fields:h.fields,isActive:d,isActiveStep:d,form:_}}export{l as StepContextProvider,d as useSetStepContext,u as useStep};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@picked/react",
3
- "version": "0.0.50",
3
+ "version": "0.0.51-next.1",
4
4
  "devDependencies": {
5
5
  "@picked/client": "workspace:*",
6
6
  "@repo/eslint-config": "workspace:*",
@@ -68,7 +68,7 @@
68
68
  },
69
69
  "module": "./dist/index.js",
70
70
  "peerDependencies": {
71
- "@picked/client": "0.0.50",
71
+ "@picked/client": "0.0.51-next.1",
72
72
  "effector": "^23.3.0",
73
73
  "effector-react": "^23.3.0",
74
74
  "react": "19.2.2",
@@ -88,7 +88,7 @@
88
88
  "lint:report": "eslint . -o './.reports/eslint.json' -f json || [ -f './.reports/eslint.json' ]",
89
89
  "size": "size-limit",
90
90
  "test": "vitest run",
91
- "test:coverage": "vitest run --coverage",
91
+ "test:coverage": "../../scripts/vitest-coverage.sh",
92
92
  "test:packaged-types": "attw --pack",
93
93
  "test:watch": "vitest",
94
94
  "type-check": "tsgo -p . --noEmit"