@pipe0/react 1.0.13 → 1.0.14
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/CHANGELOG.md +10 -0
- package/dist/components/compound/pipe-form/root.mjs +9 -3
- package/dist/components/compound/pipe-form/root.mjs.map +1 -1
- package/dist/components/compound/search-form/root.mjs +9 -3
- package/dist/components/compound/search-form/root.mjs.map +1 -1
- package/dist/hooks/use-pipe-form.d.mts.map +1 -1
- package/dist/hooks/use-pipe-form.mjs +18 -8
- package/dist/hooks/use-pipe-form.mjs.map +1 -1
- package/dist/hooks/use-search-form.d.mts.map +1 -1
- package/dist/hooks/use-search-form.mjs +14 -4
- package/dist/hooks/use-search-form.mjs.map +1 -1
- package/dist/hooks/use-sheet-effect-form.d.mts +0 -10
- package/dist/hooks/use-sheet-effect-form.d.mts.map +1 -1
- package/dist/hooks/use-sheet-effect-form.mjs +18 -8
- package/dist/hooks/use-sheet-effect-form.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @pipe0/elements-react
|
|
2
2
|
|
|
3
|
+
## 1.0.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1d9fc69: Add surfe
|
|
8
|
+
- 5b12ec2: update llm pricing
|
|
9
|
+
- Updated dependencies [1d9fc69]
|
|
10
|
+
- Updated dependencies [5b12ec2]
|
|
11
|
+
- @pipe0/base@1.0.14
|
|
12
|
+
|
|
3
13
|
## 1.0.13
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
|
@@ -74,10 +74,16 @@ function PipeFormShell({ children, className, onSubmit }) {
|
|
|
74
74
|
setPortalContainer(node);
|
|
75
75
|
}, []);
|
|
76
76
|
const body = children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(PipeFormContent, {}), /* @__PURE__ */ jsx(PipeFormSubmitButton, {})] });
|
|
77
|
+
const submit = context.form.handleSubmit(async (values) => {
|
|
78
|
+
await onSubmit(values, { form: context.form });
|
|
79
|
+
});
|
|
80
|
+
const handleFormSubmit = (event) => {
|
|
81
|
+
if (event && event.target !== event.currentTarget) return;
|
|
82
|
+
event?.stopPropagation();
|
|
83
|
+
return submit(event);
|
|
84
|
+
};
|
|
77
85
|
return /* @__PURE__ */ jsx(DefaultForm, {
|
|
78
|
-
onSubmit:
|
|
79
|
-
await onSubmit(values, { form: context.form });
|
|
80
|
-
}),
|
|
86
|
+
onSubmit: handleFormSubmit,
|
|
81
87
|
ref: portalRef,
|
|
82
88
|
"data-p0": "form",
|
|
83
89
|
"data-p0-pipe": context.id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root.mjs","names":[],"sources":["../../../../src/components/compound/pipe-form/root.tsx"],"sourcesContent":["import {\n type FormResolvers,\n type PipeId,\n type PipePayload,\n type ValidationContext,\n validatePipesOrError,\n} from \"@pipe0/base\";\nimport { type ReactNode, useCallback, useState } from \"react\";\nimport { FormProvider, FormRoot } from \"../../../context/form-provider.js\";\nimport type { PipeFormContext as PipeFormContextValue } from \"../../../context/pipe-form-context.js\";\nimport { PipeFormContext, usePipeFormContext } from \"../../../context/pipe-form-context.js\";\nimport { PortalContainerContext } from \"../../../context/portal-container-context.js\";\nimport { usePipeForm } from \"../../../hooks/use-pipe-form.js\";\nimport type { FieldAdapterMap, FormClassNames, FormComponents } from \"../../../types/adapters.js\";\nimport type { GroupMap, PathMap, SectionMap } from \"../../../types/form-customization.js\";\nimport type { FormHandle } from \"../../../types/form-handle.js\";\nimport { DefaultForm } from \"../../defaults/layout/form.js\";\nimport { PipeFormContent } from \"./content.js\";\nimport { PipeFormSubmitButton } from \"./submit-button.js\";\n\nexport type PipeFormSubmitHandler = (\n payload: PipePayload,\n ctx: { form: FormHandle<PipePayload> },\n) => void | Promise<void>;\n\ninterface RenderingProps {\n classNames?: FormClassNames;\n adapters?: FieldAdapterMap;\n /** Whole-component overrides (e.g. `alert`). Forwarded to `FormProvider`. */\n components?: FormComponents;\n className?: string;\n}\n\nexport interface PipeFormInstanceProps extends RenderingProps {\n context: PipeFormContextValue;\n onSubmit: PipeFormSubmitHandler;\n children?: ReactNode;\n}\n\nexport interface PipeFormZeroConfigProps extends RenderingProps {\n pipeId: PipeId;\n publicKey: string;\n onSubmit: PipeFormSubmitHandler;\n defaultValues?: PipePayload;\n validationContext?: ValidationContext;\n resolvers?: FormResolvers;\n /**\n * Hide / relabel / reorder sections by section key. `null` hides a section.\n * Forwarded to the internal `usePipeForm` call. For `<PipeForm context={...}>`,\n * pass these to `usePipeForm` directly instead.\n */\n sectionMap?: SectionMap;\n groupMap?: GroupMap;\n pathMap?: PathMap;\n /**\n * Form-level scope tags. Bundled into every `resolvers.getSecrets` call so\n * the backend can return only the secrets allowed in the declared scopes.\n */\n scopes?: string[];\n /**\n * Current team context. Bundled into every `resolvers.getSecrets` call so\n * the backend can restrict team-level secrets to this team only.\n */\n teamId?: string;\n children?: ReactNode;\n}\n\nexport type PipeFormProps = PipeFormInstanceProps | PipeFormZeroConfigProps;\n\n/**\n * Compound root for pipe forms.\n *\n * Two modes:\n * - Compound: `<PipeForm context={ctx} onSubmit={...}>...</PipeForm>` — pass the context from `usePipeForm`.\n * Visibility customization (`sectionMap`/`groupMap`/`pathMap`) lives on the hook in this mode.\n * - Zero-config: `<PipeForm pipeId=\"...\" publicKey=\"...\" onSubmit={...} />` — fresh hook created\n * internally; visibility maps may be passed as props and forward to that hook.\n */\nexport function PipeForm(props: PipeFormProps) {\n if (\"context\" in props) {\n return <PipeFormWithContext {...props} />;\n }\n return <PipeFormZeroConfig {...props} />;\n}\n\nfunction PipeFormZeroConfig({\n pipeId,\n publicKey,\n defaultValues,\n onSubmit,\n resolvers,\n scopes,\n teamId,\n sectionMap,\n groupMap,\n pathMap,\n validationContext = validatePipesOrError({\n config: { environment: \"production\" },\n pipes: [],\n input: [],\n }),\n children,\n classNames,\n adapters,\n components,\n className,\n}: PipeFormZeroConfigProps) {\n const context = usePipeForm({\n pipeId,\n publicKey,\n defaultValues,\n resolvers,\n scopes,\n teamId,\n validationContext,\n sectionMap,\n groupMap,\n pathMap,\n });\n\n return (\n <PipeFormWithContext\n context={context}\n onSubmit={onSubmit}\n className={className}\n classNames={classNames}\n adapters={adapters}\n components={components}\n >\n {children}\n </PipeFormWithContext>\n );\n}\n\nfunction PipeFormWithContext({\n context,\n onSubmit,\n children,\n className,\n classNames,\n adapters,\n components,\n}: PipeFormInstanceProps) {\n const hasLocalOverrides = classNames || adapters || components;\n\n const inner = (\n <PipeFormContext.Provider value={context}>\n <PipeFormShell className={className} onSubmit={onSubmit}>\n {children}\n </PipeFormShell>\n </PipeFormContext.Provider>\n );\n\n if (hasLocalOverrides) {\n return (\n <FormRoot>\n <FormProvider classNames={classNames} adapters={adapters} components={components}>\n {inner}\n </FormProvider>\n </FormRoot>\n );\n }\n\n return <FormRoot>{inner}</FormRoot>;\n}\n\nfunction PipeFormShell({\n children,\n className,\n onSubmit,\n}: {\n children?: ReactNode;\n className?: string;\n onSubmit: PipeFormSubmitHandler;\n}) {\n const context = usePipeFormContext();\n\n const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);\n const portalRef = useCallback((node: HTMLElement | null) => {\n setPortalContainer(node);\n }, []);\n\n const body = children ?? (\n <>\n <PipeFormContent />\n <PipeFormSubmitButton />\n </>\n );\n\n const
|
|
1
|
+
{"version":3,"file":"root.mjs","names":[],"sources":["../../../../src/components/compound/pipe-form/root.tsx"],"sourcesContent":["import {\n type FormResolvers,\n type PipeId,\n type PipePayload,\n type ValidationContext,\n validatePipesOrError,\n} from \"@pipe0/base\";\nimport { type BaseSyntheticEvent, type ReactNode, useCallback, useState } from \"react\";\nimport { FormProvider, FormRoot } from \"../../../context/form-provider.js\";\nimport type { PipeFormContext as PipeFormContextValue } from \"../../../context/pipe-form-context.js\";\nimport { PipeFormContext, usePipeFormContext } from \"../../../context/pipe-form-context.js\";\nimport { PortalContainerContext } from \"../../../context/portal-container-context.js\";\nimport { usePipeForm } from \"../../../hooks/use-pipe-form.js\";\nimport type { FieldAdapterMap, FormClassNames, FormComponents } from \"../../../types/adapters.js\";\nimport type { GroupMap, PathMap, SectionMap } from \"../../../types/form-customization.js\";\nimport type { FormHandle } from \"../../../types/form-handle.js\";\nimport { DefaultForm } from \"../../defaults/layout/form.js\";\nimport { PipeFormContent } from \"./content.js\";\nimport { PipeFormSubmitButton } from \"./submit-button.js\";\n\nexport type PipeFormSubmitHandler = (\n payload: PipePayload,\n ctx: { form: FormHandle<PipePayload> },\n) => void | Promise<void>;\n\ninterface RenderingProps {\n classNames?: FormClassNames;\n adapters?: FieldAdapterMap;\n /** Whole-component overrides (e.g. `alert`). Forwarded to `FormProvider`. */\n components?: FormComponents;\n className?: string;\n}\n\nexport interface PipeFormInstanceProps extends RenderingProps {\n context: PipeFormContextValue;\n onSubmit: PipeFormSubmitHandler;\n children?: ReactNode;\n}\n\nexport interface PipeFormZeroConfigProps extends RenderingProps {\n pipeId: PipeId;\n publicKey: string;\n onSubmit: PipeFormSubmitHandler;\n defaultValues?: PipePayload;\n validationContext?: ValidationContext;\n resolvers?: FormResolvers;\n /**\n * Hide / relabel / reorder sections by section key. `null` hides a section.\n * Forwarded to the internal `usePipeForm` call. For `<PipeForm context={...}>`,\n * pass these to `usePipeForm` directly instead.\n */\n sectionMap?: SectionMap;\n groupMap?: GroupMap;\n pathMap?: PathMap;\n /**\n * Form-level scope tags. Bundled into every `resolvers.getSecrets` call so\n * the backend can return only the secrets allowed in the declared scopes.\n */\n scopes?: string[];\n /**\n * Current team context. Bundled into every `resolvers.getSecrets` call so\n * the backend can restrict team-level secrets to this team only.\n */\n teamId?: string;\n children?: ReactNode;\n}\n\nexport type PipeFormProps = PipeFormInstanceProps | PipeFormZeroConfigProps;\n\n/**\n * Compound root for pipe forms.\n *\n * Two modes:\n * - Compound: `<PipeForm context={ctx} onSubmit={...}>...</PipeForm>` — pass the context from `usePipeForm`.\n * Visibility customization (`sectionMap`/`groupMap`/`pathMap`) lives on the hook in this mode.\n * - Zero-config: `<PipeForm pipeId=\"...\" publicKey=\"...\" onSubmit={...} />` — fresh hook created\n * internally; visibility maps may be passed as props and forward to that hook.\n */\nexport function PipeForm(props: PipeFormProps) {\n if (\"context\" in props) {\n return <PipeFormWithContext {...props} />;\n }\n return <PipeFormZeroConfig {...props} />;\n}\n\nfunction PipeFormZeroConfig({\n pipeId,\n publicKey,\n defaultValues,\n onSubmit,\n resolvers,\n scopes,\n teamId,\n sectionMap,\n groupMap,\n pathMap,\n validationContext = validatePipesOrError({\n config: { environment: \"production\" },\n pipes: [],\n input: [],\n }),\n children,\n classNames,\n adapters,\n components,\n className,\n}: PipeFormZeroConfigProps) {\n const context = usePipeForm({\n pipeId,\n publicKey,\n defaultValues,\n resolvers,\n scopes,\n teamId,\n validationContext,\n sectionMap,\n groupMap,\n pathMap,\n });\n\n return (\n <PipeFormWithContext\n context={context}\n onSubmit={onSubmit}\n className={className}\n classNames={classNames}\n adapters={adapters}\n components={components}\n >\n {children}\n </PipeFormWithContext>\n );\n}\n\nfunction PipeFormWithContext({\n context,\n onSubmit,\n children,\n className,\n classNames,\n adapters,\n components,\n}: PipeFormInstanceProps) {\n const hasLocalOverrides = classNames || adapters || components;\n\n const inner = (\n <PipeFormContext.Provider value={context}>\n <PipeFormShell className={className} onSubmit={onSubmit}>\n {children}\n </PipeFormShell>\n </PipeFormContext.Provider>\n );\n\n if (hasLocalOverrides) {\n return (\n <FormRoot>\n <FormProvider classNames={classNames} adapters={adapters} components={components}>\n {inner}\n </FormProvider>\n </FormRoot>\n );\n }\n\n return <FormRoot>{inner}</FormRoot>;\n}\n\nfunction PipeFormShell({\n children,\n className,\n onSubmit,\n}: {\n children?: ReactNode;\n className?: string;\n onSubmit: PipeFormSubmitHandler;\n}) {\n const context = usePipeFormContext();\n\n const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);\n const portalRef = useCallback((node: HTMLElement | null) => {\n setPortalContainer(node);\n }, []);\n\n const body = children ?? (\n <>\n <PipeFormContent />\n <PipeFormSubmitButton />\n </>\n );\n\n const submit = context.form.handleSubmit(async (values) => {\n await onSubmit(values, { form: context.form });\n });\n\n // A field adapter may render a form of its own (the `search_payload_input`\n // opens a whole search config form in a portalled sheet). React propagates\n // that nested submit up this tree, and treating it as OUR submit would save\n // this form's values a beat before the adapter commits its result — a silent\n // save of stale config. Only ever handle a submit fired by this form element,\n // and stop it here so no ancestor form makes the same mistake.\n const handleFormSubmit = (event?: BaseSyntheticEvent) => {\n if (event && event.target !== event.currentTarget) return;\n event?.stopPropagation();\n return submit(event);\n };\n\n return (\n <DefaultForm\n onSubmit={handleFormSubmit}\n ref={portalRef}\n data-p0=\"form\"\n data-p0-pipe={context.id}\n className={className}\n >\n <PortalContainerContext.Provider value={portalContainer}>\n {body}\n </PortalContainerContext.Provider>\n </DefaultForm>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA8EA,SAAgB,SAAS,OAAsB;AAC7C,KAAI,aAAa,MACf,QAAO,oBAAC,qBAAD,EAAqB,GAAI,OAAS;AAE3C,QAAO,oBAAC,oBAAD,EAAoB,GAAI,OAAS;;AAG1C,SAAS,mBAAmB,EAC1B,QACA,WACA,eACA,UACA,WACA,QACA,QACA,YACA,UACA,SACA,oBAAoB,qBAAqB;CACvC,QAAQ,EAAE,aAAa,cAAc;CACrC,OAAO,EAAE;CACT,OAAO,EAAE;CACV,CAAC,EACF,UACA,YACA,UACA,YACA,aAC0B;AAc1B,QACE,oBAAC,qBAAD;EACE,SAfY,YAAY;GAC1B;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EAKY;EACC;EACC;EACF;EACE;EAEX;EACmB;;AAI1B,SAAS,oBAAoB,EAC3B,SACA,UACA,UACA,WACA,YACA,UACA,cACwB;CACxB,MAAM,oBAAoB,cAAc,YAAY;CAEpD,MAAM,QACJ,oBAAC,gBAAgB,UAAjB;EAA0B,OAAO;YAC/B,oBAAC,eAAD;GAA0B;GAAqB;GAC5C;GACa;EACS;AAG7B,KAAI,kBACF,QACE,oBAAC,UAAD,YACE,oBAAC,cAAD;EAA0B;EAAsB;EAAsB;YACnE;EACY,GACN;AAIf,QAAO,oBAAC,UAAD,YAAW,OAAiB;;AAGrC,SAAS,cAAc,EACrB,UACA,WACA,YAKC;CACD,MAAM,UAAU,oBAAoB;CAEpC,MAAM,CAAC,iBAAiB,sBAAsB,SAA6B,KAAK;CAChF,MAAM,YAAY,aAAa,SAA6B;AAC1D,qBAAmB,KAAK;IACvB,EAAE,CAAC;CAEN,MAAM,OAAO,YACX,8CACE,oBAAC,iBAAD,EAAmB,GACnB,oBAAC,sBAAD,EAAwB,EACvB;CAGL,MAAM,SAAS,QAAQ,KAAK,aAAa,OAAO,WAAW;AACzD,QAAM,SAAS,QAAQ,EAAE,MAAM,QAAQ,MAAM,CAAC;GAC9C;CAQF,MAAM,oBAAoB,UAA+B;AACvD,MAAI,SAAS,MAAM,WAAW,MAAM,cAAe;AACnD,SAAO,iBAAiB;AACxB,SAAO,OAAO,MAAM;;AAGtB,QACE,oBAAC,aAAD;EACE,UAAU;EACV,KAAK;EACL,WAAQ;EACR,gBAAc,QAAQ;EACX;YAEX,oBAAC,uBAAuB,UAAxB;GAAiC,OAAO;aACrC;GAC+B;EACtB"}
|
|
@@ -66,10 +66,16 @@ function SearchFormShell({ children, className, onSubmit }) {
|
|
|
66
66
|
setPortalContainer(node);
|
|
67
67
|
}, []);
|
|
68
68
|
const body = children ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(SearchFormContent, {}), /* @__PURE__ */ jsx(SearchFormSubmitButton, {})] });
|
|
69
|
+
const submit = context.form.handleSubmit(async (values) => {
|
|
70
|
+
await onSubmit(values, { form: context.form });
|
|
71
|
+
});
|
|
72
|
+
const handleFormSubmit = (event) => {
|
|
73
|
+
if (event && event.target !== event.currentTarget) return;
|
|
74
|
+
event?.stopPropagation();
|
|
75
|
+
return submit(event);
|
|
76
|
+
};
|
|
69
77
|
return /* @__PURE__ */ jsx(DefaultForm, {
|
|
70
|
-
onSubmit:
|
|
71
|
-
await onSubmit(values, { form: context.form });
|
|
72
|
-
}),
|
|
78
|
+
onSubmit: handleFormSubmit,
|
|
73
79
|
ref: portalRef,
|
|
74
80
|
"data-p0": "form",
|
|
75
81
|
"data-p0-search": context.id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root.mjs","names":[],"sources":["../../../../src/components/compound/search-form/root.tsx"],"sourcesContent":["import type { FormResolvers, PipesEnvironment, SearchId, SearchPayload } from \"@pipe0/base\";\nimport { type ReactNode, useCallback, useState } from \"react\";\nimport { FormProvider, FormRoot } from \"../../../context/form-provider.js\";\nimport { PortalContainerContext } from \"../../../context/portal-container-context.js\";\nimport type { SearchFormContext as SearchFormContextValue } from \"../../../context/search-form-context.js\";\nimport { SearchFormContext, useSearchFormContext } from \"../../../context/search-form-context.js\";\nimport { useSearchForm } from \"../../../hooks/use-search-form.js\";\nimport type { FieldAdapterMap, FormClassNames, FormComponents } from \"../../../types/adapters.js\";\nimport type { GroupMap, PathMap, SectionMap } from \"../../../types/form-customization.js\";\nimport type { FormHandle } from \"../../../types/form-handle.js\";\nimport { DefaultForm } from \"../../defaults/layout/form.js\";\nimport { SearchFormContent } from \"./content.js\";\nimport { SearchFormSubmitButton } from \"./submit-button.js\";\n\nexport type SearchFormSubmitHandler = (\n payload: SearchPayload,\n ctx: { form: FormHandle<SearchPayload> },\n) => void | Promise<void>;\n\ninterface RenderingProps {\n classNames?: FormClassNames;\n adapters?: FieldAdapterMap;\n /** Whole-component overrides (e.g. `alert`). Forwarded to `FormProvider`. */\n components?: FormComponents;\n className?: string;\n}\n\nexport interface SearchFormInstanceProps extends RenderingProps {\n context: SearchFormContextValue;\n onSubmit: SearchFormSubmitHandler;\n children?: ReactNode;\n}\n\nexport interface SearchFormZeroConfigProps extends RenderingProps {\n searchId: SearchId;\n publicKey: string;\n onSubmit: SearchFormSubmitHandler;\n defaultValues?: SearchPayload;\n resolvers?: FormResolvers;\n environment?: PipesEnvironment;\n /**\n * Hide / relabel / reorder sections by section key. `null` hides a section.\n * Forwarded to the internal `useSearchForm` call. For `<SearchForm context={...}>`,\n * pass these to `useSearchForm` directly instead.\n */\n sectionMap?: SectionMap;\n groupMap?: GroupMap;\n pathMap?: PathMap;\n children?: ReactNode;\n}\n\nexport type SearchFormProps = SearchFormInstanceProps | SearchFormZeroConfigProps;\n\n/**\n * Compound root for search forms.\n *\n * - Compound: `<SearchForm context={ctx} onSubmit={...}>...</SearchForm>` — pass the context from\n * `useSearchForm`. Visibility customization lives on the hook in this mode.\n * - Zero-config: `<SearchForm searchId=\"...\" publicKey=\"...\" onSubmit={...} />` — fresh hook\n * created internally; visibility maps may be passed as props and forward to that hook.\n */\nexport function SearchForm(props: SearchFormProps) {\n if (\"context\" in props) {\n return <SearchFormWithContext {...props} />;\n }\n return <SearchFormZeroConfig {...props} />;\n}\n\nfunction SearchFormZeroConfig({\n searchId,\n publicKey,\n defaultValues,\n onSubmit,\n resolvers,\n environment,\n sectionMap,\n groupMap,\n pathMap,\n children,\n classNames,\n adapters,\n components,\n className,\n}: SearchFormZeroConfigProps) {\n const context = useSearchForm({\n searchId,\n publicKey,\n defaultValues,\n resolvers,\n environment,\n sectionMap,\n groupMap,\n pathMap,\n });\n\n return (\n <SearchFormWithContext\n context={context}\n onSubmit={onSubmit}\n className={className}\n classNames={classNames}\n adapters={adapters}\n components={components}\n >\n {children}\n </SearchFormWithContext>\n );\n}\n\nfunction SearchFormWithContext({\n context,\n onSubmit,\n children,\n className,\n classNames,\n adapters,\n components,\n}: SearchFormInstanceProps) {\n const hasLocalOverrides = classNames || adapters || components;\n\n const inner = (\n <SearchFormContext.Provider value={context}>\n <SearchFormShell className={className} onSubmit={onSubmit}>\n {children}\n </SearchFormShell>\n </SearchFormContext.Provider>\n );\n\n if (hasLocalOverrides) {\n return (\n <FormRoot>\n <FormProvider classNames={classNames} adapters={adapters} components={components}>\n {inner}\n </FormProvider>\n </FormRoot>\n );\n }\n\n return <FormRoot>{inner}</FormRoot>;\n}\n\nfunction SearchFormShell({\n children,\n className,\n onSubmit,\n}: {\n children?: ReactNode;\n className?: string;\n onSubmit: SearchFormSubmitHandler;\n}) {\n const context = useSearchFormContext();\n\n const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);\n const portalRef = useCallback((node: HTMLElement | null) => {\n setPortalContainer(node);\n }, []);\n\n const body = children ?? (\n <>\n <SearchFormContent />\n <SearchFormSubmitButton />\n </>\n );\n\n const
|
|
1
|
+
{"version":3,"file":"root.mjs","names":[],"sources":["../../../../src/components/compound/search-form/root.tsx"],"sourcesContent":["import type { FormResolvers, PipesEnvironment, SearchId, SearchPayload } from \"@pipe0/base\";\nimport { type BaseSyntheticEvent, type ReactNode, useCallback, useState } from \"react\";\nimport { FormProvider, FormRoot } from \"../../../context/form-provider.js\";\nimport { PortalContainerContext } from \"../../../context/portal-container-context.js\";\nimport type { SearchFormContext as SearchFormContextValue } from \"../../../context/search-form-context.js\";\nimport { SearchFormContext, useSearchFormContext } from \"../../../context/search-form-context.js\";\nimport { useSearchForm } from \"../../../hooks/use-search-form.js\";\nimport type { FieldAdapterMap, FormClassNames, FormComponents } from \"../../../types/adapters.js\";\nimport type { GroupMap, PathMap, SectionMap } from \"../../../types/form-customization.js\";\nimport type { FormHandle } from \"../../../types/form-handle.js\";\nimport { DefaultForm } from \"../../defaults/layout/form.js\";\nimport { SearchFormContent } from \"./content.js\";\nimport { SearchFormSubmitButton } from \"./submit-button.js\";\n\nexport type SearchFormSubmitHandler = (\n payload: SearchPayload,\n ctx: { form: FormHandle<SearchPayload> },\n) => void | Promise<void>;\n\ninterface RenderingProps {\n classNames?: FormClassNames;\n adapters?: FieldAdapterMap;\n /** Whole-component overrides (e.g. `alert`). Forwarded to `FormProvider`. */\n components?: FormComponents;\n className?: string;\n}\n\nexport interface SearchFormInstanceProps extends RenderingProps {\n context: SearchFormContextValue;\n onSubmit: SearchFormSubmitHandler;\n children?: ReactNode;\n}\n\nexport interface SearchFormZeroConfigProps extends RenderingProps {\n searchId: SearchId;\n publicKey: string;\n onSubmit: SearchFormSubmitHandler;\n defaultValues?: SearchPayload;\n resolvers?: FormResolvers;\n environment?: PipesEnvironment;\n /**\n * Hide / relabel / reorder sections by section key. `null` hides a section.\n * Forwarded to the internal `useSearchForm` call. For `<SearchForm context={...}>`,\n * pass these to `useSearchForm` directly instead.\n */\n sectionMap?: SectionMap;\n groupMap?: GroupMap;\n pathMap?: PathMap;\n children?: ReactNode;\n}\n\nexport type SearchFormProps = SearchFormInstanceProps | SearchFormZeroConfigProps;\n\n/**\n * Compound root for search forms.\n *\n * - Compound: `<SearchForm context={ctx} onSubmit={...}>...</SearchForm>` — pass the context from\n * `useSearchForm`. Visibility customization lives on the hook in this mode.\n * - Zero-config: `<SearchForm searchId=\"...\" publicKey=\"...\" onSubmit={...} />` — fresh hook\n * created internally; visibility maps may be passed as props and forward to that hook.\n */\nexport function SearchForm(props: SearchFormProps) {\n if (\"context\" in props) {\n return <SearchFormWithContext {...props} />;\n }\n return <SearchFormZeroConfig {...props} />;\n}\n\nfunction SearchFormZeroConfig({\n searchId,\n publicKey,\n defaultValues,\n onSubmit,\n resolvers,\n environment,\n sectionMap,\n groupMap,\n pathMap,\n children,\n classNames,\n adapters,\n components,\n className,\n}: SearchFormZeroConfigProps) {\n const context = useSearchForm({\n searchId,\n publicKey,\n defaultValues,\n resolvers,\n environment,\n sectionMap,\n groupMap,\n pathMap,\n });\n\n return (\n <SearchFormWithContext\n context={context}\n onSubmit={onSubmit}\n className={className}\n classNames={classNames}\n adapters={adapters}\n components={components}\n >\n {children}\n </SearchFormWithContext>\n );\n}\n\nfunction SearchFormWithContext({\n context,\n onSubmit,\n children,\n className,\n classNames,\n adapters,\n components,\n}: SearchFormInstanceProps) {\n const hasLocalOverrides = classNames || adapters || components;\n\n const inner = (\n <SearchFormContext.Provider value={context}>\n <SearchFormShell className={className} onSubmit={onSubmit}>\n {children}\n </SearchFormShell>\n </SearchFormContext.Provider>\n );\n\n if (hasLocalOverrides) {\n return (\n <FormRoot>\n <FormProvider classNames={classNames} adapters={adapters} components={components}>\n {inner}\n </FormProvider>\n </FormRoot>\n );\n }\n\n return <FormRoot>{inner}</FormRoot>;\n}\n\nfunction SearchFormShell({\n children,\n className,\n onSubmit,\n}: {\n children?: ReactNode;\n className?: string;\n onSubmit: SearchFormSubmitHandler;\n}) {\n const context = useSearchFormContext();\n\n const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);\n const portalRef = useCallback((node: HTMLElement | null) => {\n setPortalContainer(node);\n }, []);\n\n const body = children ?? (\n <>\n <SearchFormContent />\n <SearchFormSubmitButton />\n </>\n );\n\n const submit = context.form.handleSubmit(async (values) => {\n await onSubmit(values, { form: context.form });\n });\n\n // Nested forms: see the note in `PipeFormShell`. A search form is itself the\n // form most often nested inside another one (the effect form's\n // `search_payload_input`), so stopping its submit here is what keeps the\n // outer form from saving stale values on this form's Save.\n const handleFormSubmit = (event?: BaseSyntheticEvent) => {\n if (event && event.target !== event.currentTarget) return;\n event?.stopPropagation();\n return submit(event);\n };\n\n return (\n <DefaultForm\n onSubmit={handleFormSubmit}\n ref={portalRef}\n data-p0=\"form\"\n data-p0-search={context.id}\n className={className}\n >\n <PortalContainerContext.Provider value={portalContainer}>\n {body}\n </PortalContainerContext.Provider>\n </DefaultForm>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6DA,SAAgB,WAAW,OAAwB;AACjD,KAAI,aAAa,MACf,QAAO,oBAAC,uBAAD,EAAuB,GAAI,OAAS;AAE7C,QAAO,oBAAC,sBAAD,EAAsB,GAAI,OAAS;;AAG5C,SAAS,qBAAqB,EAC5B,UACA,WACA,eACA,UACA,WACA,aACA,YACA,UACA,SACA,UACA,YACA,UACA,YACA,aAC4B;AAY5B,QACE,oBAAC,uBAAD;EACE,SAbY,cAAc;GAC5B;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EAKY;EACC;EACC;EACF;EACE;EAEX;EACqB;;AAI5B,SAAS,sBAAsB,EAC7B,SACA,UACA,UACA,WACA,YACA,UACA,cAC0B;CAC1B,MAAM,oBAAoB,cAAc,YAAY;CAEpD,MAAM,QACJ,oBAAC,kBAAkB,UAAnB;EAA4B,OAAO;YACjC,oBAAC,iBAAD;GAA4B;GAAqB;GAC9C;GACe;EACS;AAG/B,KAAI,kBACF,QACE,oBAAC,UAAD,YACE,oBAAC,cAAD;EAA0B;EAAsB;EAAsB;YACnE;EACY,GACN;AAIf,QAAO,oBAAC,UAAD,YAAW,OAAiB;;AAGrC,SAAS,gBAAgB,EACvB,UACA,WACA,YAKC;CACD,MAAM,UAAU,sBAAsB;CAEtC,MAAM,CAAC,iBAAiB,sBAAsB,SAA6B,KAAK;CAChF,MAAM,YAAY,aAAa,SAA6B;AAC1D,qBAAmB,KAAK;IACvB,EAAE,CAAC;CAEN,MAAM,OAAO,YACX,8CACE,oBAAC,mBAAD,EAAqB,GACrB,oBAAC,wBAAD,EAA0B,EACzB;CAGL,MAAM,SAAS,QAAQ,KAAK,aAAa,OAAO,WAAW;AACzD,QAAM,SAAS,QAAQ,EAAE,MAAM,QAAQ,MAAM,CAAC;GAC9C;CAMF,MAAM,oBAAoB,UAA+B;AACvD,MAAI,SAAS,MAAM,WAAW,MAAM,cAAe;AACnD,SAAO,iBAAiB;AACxB,SAAO,OAAO,MAAM;;AAGtB,QACE,oBAAC,aAAD;EACE,UAAU;EACV,KAAK;EACL,WAAQ;EACR,kBAAgB,QAAQ;EACb;YAEX,oBAAC,uBAAuB,UAAxB;GAAiC,OAAO;aACrC;GAC+B;EACtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-pipe-form.d.mts","names":[],"sources":["../../src/hooks/use-pipe-form.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"use-pipe-form.d.mts","names":[],"sources":["../../src/hooks/use-pipe-form.ts"],"mappings":";;;;;;UAmBiB,kBAAA;EACf,MAAA,EAAQ,MAAA;EACR,SAAA;EACA,aAAA,GAAgB,WAAA;EAChB,SAAA,GAAY,aAAA;EACZ,eAAA;IAAoB,SAAA;IAAmB,QAAA;EAAA;EACvC,iBAAA,GAAoB,iBAAA;EACpB,KAAA,GAAQ,SAAA;EAUE;;;;;EAJV,UAAA,GAAa,UAAA;EAVb;EAYA,QAAA,GAAW,QAAA;EAXX;EAaA,OAAA,GAAU,OAAA;EAZV;;;;EAiBA,MAAA;EAfA;;;;EAoBA,MAAA;AAAA;AAAA,iBAec,WAAA,CAAY,OAAA,EAAS,kBAAA,GAAqB,eAAA"}
|
|
@@ -1,20 +1,35 @@
|
|
|
1
1
|
import { asFormHandle } from "../utils/internal-form.mjs";
|
|
2
2
|
import { useFormCore } from "./use-form-core.mjs";
|
|
3
3
|
import { useMemo, useState } from "react";
|
|
4
|
-
import { getPipeDefaultPayload, getPipePayloadFormConfig, getPipePayloadSchema } from "@pipe0/base";
|
|
4
|
+
import { configDefaults, getPipeDefaultPayload, getPipePayloadFormConfig, getPipePayloadSchema } from "@pipe0/base";
|
|
5
5
|
|
|
6
6
|
//#region src/hooks/use-pipe-form.ts
|
|
7
|
+
/** Catalog `config` defaults under the payload's own values. Mirrors `ProcessedPipePayloadSchema`. */
|
|
8
|
+
function withConfigDefaults(pipeId, payload) {
|
|
9
|
+
const defaultPayload = getPipeDefaultPayload(pipeId);
|
|
10
|
+
if (!defaultPayload) return payload;
|
|
11
|
+
return {
|
|
12
|
+
...payload,
|
|
13
|
+
config: configDefaults(payload.config ?? {}, { ...defaultPayload.config })
|
|
14
|
+
};
|
|
15
|
+
}
|
|
7
16
|
function usePipeForm(options) {
|
|
8
17
|
const { pipeId, publicKey, defaultValues, resolvers, validationContext, store: defaultFormStore, sectionMap, groupMap, pathMap, scopes, teamId } = options;
|
|
9
18
|
const schema = useMemo(() => getPipePayloadSchema(pipeId), [pipeId]);
|
|
10
19
|
const [store, setStore] = useState(defaultFormStore ?? { field_options: {} });
|
|
11
20
|
const hasGetConnections = resolvers?.getConnections != null;
|
|
21
|
+
const customizationKey = JSON.stringify({
|
|
22
|
+
sectionMap,
|
|
23
|
+
groupMap,
|
|
24
|
+
pathMap,
|
|
25
|
+
hasGetConnections
|
|
26
|
+
});
|
|
12
27
|
const core = useFormCore({
|
|
13
28
|
pipeOrSearchId: pipeId,
|
|
14
29
|
kind: "pipe",
|
|
15
30
|
schema,
|
|
16
31
|
publicKey,
|
|
17
|
-
defaultValues,
|
|
32
|
+
defaultValues: useMemo(() => defaultValues ? withConfigDefaults(pipeId, defaultValues) : defaultValues, [pipeId, defaultValues]),
|
|
18
33
|
store,
|
|
19
34
|
setStore,
|
|
20
35
|
formConfig: useMemo(() => {
|
|
@@ -38,12 +53,7 @@ function usePipeForm(options) {
|
|
|
38
53
|
pipeId,
|
|
39
54
|
validationContext,
|
|
40
55
|
store,
|
|
41
|
-
|
|
42
|
-
sectionMap,
|
|
43
|
-
groupMap,
|
|
44
|
-
pathMap,
|
|
45
|
-
hasGetConnections
|
|
46
|
-
})
|
|
56
|
+
customizationKey
|
|
47
57
|
]),
|
|
48
58
|
resolvers,
|
|
49
59
|
environment: validationContext?.environment,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-pipe-form.mjs","names":[],"sources":["../../src/hooks/use-pipe-form.ts"],"sourcesContent":["import {\n type FormResolvers,\n type FormStore,\n getPipeDefaultPayload,\n getPipePayloadFormConfig,\n getPipePayloadSchema,\n type PipeId,\n type PipePayload,\n type ValidationContext,\n} from \"@pipe0/base\";\nimport { useMemo, useState } from \"react\";\nimport type { PipeFormContext } from \"../context/pipe-form-context.js\";\nimport type { GroupMap, PathMap, SectionMap } from \"../types/form-customization.js\";\nimport { asFormHandle } from \"../utils/internal-form.js\";\nimport { type ResourceStatus, useFormCore } from \"./use-form-core.js\";\n\nexport type { PipeFormContext, ResourceStatus };\n\nexport interface UsePipeFormOptions {\n pipeId: PipeId;\n publicKey: string;\n defaultValues?: PipePayload;\n resolvers?: FormResolvers;\n userConnections?: { public_id: string; provider: string }[];\n validationContext?: ValidationContext;\n store?: FormStore;\n /**\n * Hide / relabel / reorder sections by section key. `null` hides a section.\n * Note: when `resolvers.getConnections` is omitted, the `connector` section\n * is hidden automatically — there's no value to render without a resolver.\n */\n sectionMap?: SectionMap;\n /** Hide / relabel / reorder groups by group path. `null` hides a group. */\n groupMap?: GroupMap;\n /** Hide / relabel fields by field path. `null` hides a field. */\n pathMap?: PathMap;\n /**\n * Form-level scope tags. Bundled into every `resolvers.getSecrets` call so\n * the backend can return only the secrets allowed in the declared scopes.\n */\n scopes?: string[];\n /**\n * Current team context. Bundled into every `resolvers.getSecrets` call so\n * the backend can restrict team-level secrets to this team only.\n */\n teamId?: string;\n}\n\nexport function usePipeForm(options: UsePipeFormOptions): PipeFormContext {\n const {\n pipeId,\n publicKey,\n defaultValues,\n resolvers,\n validationContext,\n store: defaultFormStore,\n sectionMap,\n groupMap,\n pathMap,\n scopes,\n teamId,\n } = options;\n\n const schema = useMemo(() => getPipePayloadSchema(pipeId), [pipeId]);\n\n const [store, setStore] = useState<FormStore>(defaultFormStore ?? { field_options: {} });\n\n // Customization is applied while the form config is built (in `@pipe0/base`'s\n // `extractFormMetadata`), so `core.sections` is already filtered/relabeled.\n // Connector auto-hides without a `getConnections` resolver — it's a *group*\n // (groupPath \"connector\"), so hide it via `groupMap`.\n const hasGetConnections = resolvers?.getConnections != null;\n // Stable content key: callers pass fresh inline customization objects every\n // render, so key on contents to avoid re-walking the schema needlessly.\n const customizationKey = JSON.stringify({ sectionMap, groupMap, pathMap, hasGetConnections });\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: customization inputs + resolver flag are tracked by value via `customizationKey` to keep the config stable across inline-object renders.\n const formConfig = useMemo(() => {\n const defaultPayload = getPipeDefaultPayload(pipeId);\n if (!defaultPayload) throw new Error(\"No default payload\");\n\n return getPipePayloadFormConfig({\n pipePayload: defaultPayload,\n store,\n validationContext,\n formResolvers: resolvers,\n customization: {\n sectionMap,\n groupMap: hasGetConnections ? groupMap : { ...groupMap, connector: null },\n pathMap,\n },\n });\n }, [pipeId, validationContext, store, customizationKey]);\n\n const core = useFormCore<PipePayload>({\n pipeOrSearchId: pipeId,\n kind: \"pipe\",\n schema,\n publicKey,\n defaultValues,\n store,\n setStore,\n formConfig,\n resolvers,\n environment: validationContext?.environment,\n scopes,\n teamId,\n });\n\n // `core.sections` is already customized (hidden/relabeled) by the metadata\n // extractor, so the hook exposes it directly.\n const sections = core.sections;\n\n const fields = useMemo(\n () => sections.flatMap((s) => s.groups.flatMap((g) => g.fields)),\n [sections],\n );\n\n const fieldPaths = useMemo(\n () => new Set(sections.flatMap((s) => s.groups.flatMap((g) => g.fields.map((f) => f.path)))),\n [sections],\n );\n\n return useMemo<PipeFormContext>(\n () => ({\n id: pipeId,\n publicKey,\n form: asFormHandle(core.form),\n sections,\n fields,\n fieldPaths,\n connectionsStatus: core.connectionsStatus,\n fieldLoadStates: core.fieldLoadStates,\n fieldLoaderErrors: core.fieldLoaderErrors,\n loadingFieldLoaders: core.loadingFieldLoaders,\n hasFieldLoaderError: core.hasFieldLoaderError,\n isFieldLoaderLoading: core.isFieldLoaderLoading,\n resolvers,\n store,\n reset: core.reset,\n }),\n [\n pipeId,\n publicKey,\n core.form,\n sections,\n fields,\n fieldPaths,\n core.connectionsStatus,\n core.fieldLoadStates,\n core.fieldLoaderErrors,\n core.loadingFieldLoaders,\n core.hasFieldLoaderError,\n core.isFieldLoaderLoading,\n resolvers,\n store,\n core.reset,\n ],\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-pipe-form.mjs","names":[],"sources":["../../src/hooks/use-pipe-form.ts"],"sourcesContent":["import {\n configDefaults,\n type FormResolvers,\n type FormStore,\n getPipeDefaultPayload,\n getPipePayloadFormConfig,\n getPipePayloadSchema,\n type PipeId,\n type PipePayload,\n type ValidationContext,\n} from \"@pipe0/base\";\nimport { useMemo, useState } from \"react\";\nimport type { PipeFormContext } from \"../context/pipe-form-context.js\";\nimport type { GroupMap, PathMap, SectionMap } from \"../types/form-customization.js\";\nimport { asFormHandle } from \"../utils/internal-form.js\";\nimport { type ResourceStatus, useFormCore } from \"./use-form-core.js\";\n\nexport type { PipeFormContext, ResourceStatus };\n\nexport interface UsePipeFormOptions {\n pipeId: PipeId;\n publicKey: string;\n defaultValues?: PipePayload;\n resolvers?: FormResolvers;\n userConnections?: { public_id: string; provider: string }[];\n validationContext?: ValidationContext;\n store?: FormStore;\n /**\n * Hide / relabel / reorder sections by section key. `null` hides a section.\n * Note: when `resolvers.getConnections` is omitted, the `connector` section\n * is hidden automatically — there's no value to render without a resolver.\n */\n sectionMap?: SectionMap;\n /** Hide / relabel / reorder groups by group path. `null` hides a group. */\n groupMap?: GroupMap;\n /** Hide / relabel fields by field path. `null` hides a field. */\n pathMap?: PathMap;\n /**\n * Form-level scope tags. Bundled into every `resolvers.getSecrets` call so\n * the backend can return only the secrets allowed in the declared scopes.\n */\n scopes?: string[];\n /**\n * Current team context. Bundled into every `resolvers.getSecrets` call so\n * the backend can restrict team-level secrets to this team only.\n */\n teamId?: string;\n}\n\n/** Catalog `config` defaults under the payload's own values. Mirrors `ProcessedPipePayloadSchema`. */\nfunction withConfigDefaults(pipeId: PipeId, payload: PipePayload): PipePayload {\n const defaultPayload = getPipeDefaultPayload(pipeId);\n if (!defaultPayload) return payload;\n return {\n ...payload,\n config: configDefaults((payload as { config?: Record<string, unknown> }).config ?? {}, {\n ...(defaultPayload.config as Record<string, unknown>),\n }),\n } as PipePayload;\n}\n\nexport function usePipeForm(options: UsePipeFormOptions): PipeFormContext {\n const {\n pipeId,\n publicKey,\n defaultValues,\n resolvers,\n validationContext,\n store: defaultFormStore,\n sectionMap,\n groupMap,\n pathMap,\n scopes,\n teamId,\n } = options;\n\n const schema = useMemo(() => getPipePayloadSchema(pipeId), [pipeId]);\n\n const [store, setStore] = useState<FormStore>(defaultFormStore ?? { field_options: {} });\n\n // Customization is applied while the form config is built (in `@pipe0/base`'s\n // `extractFormMetadata`), so `core.sections` is already filtered/relabeled.\n // Connector auto-hides without a `getConnections` resolver — it's a *group*\n // (groupPath \"connector\"), so hide it via `groupMap`.\n const hasGetConnections = resolvers?.getConnections != null;\n // Stable content key: callers pass fresh inline customization objects every\n // render, so key on contents to avoid re-walking the schema needlessly.\n const customizationKey = JSON.stringify({ sectionMap, groupMap, pathMap, hasGetConnections });\n\n // Catalog defaults under the stored values — see the note in `useSearchForm`.\n const seedValues = useMemo(\n () => (defaultValues ? withConfigDefaults(pipeId, defaultValues) : defaultValues),\n [pipeId, defaultValues],\n );\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: customization inputs + resolver flag are tracked by value via `customizationKey` to keep the config stable across inline-object renders.\n const formConfig = useMemo(() => {\n const defaultPayload = getPipeDefaultPayload(pipeId);\n if (!defaultPayload) throw new Error(\"No default payload\");\n\n return getPipePayloadFormConfig({\n pipePayload: defaultPayload,\n store,\n validationContext,\n formResolvers: resolvers,\n customization: {\n sectionMap,\n groupMap: hasGetConnections ? groupMap : { ...groupMap, connector: null },\n pathMap,\n },\n });\n }, [pipeId, validationContext, store, customizationKey]);\n\n const core = useFormCore<PipePayload>({\n pipeOrSearchId: pipeId,\n kind: \"pipe\",\n schema,\n publicKey,\n defaultValues: seedValues,\n store,\n setStore,\n formConfig,\n resolvers,\n environment: validationContext?.environment,\n scopes,\n teamId,\n });\n\n // `core.sections` is already customized (hidden/relabeled) by the metadata\n // extractor, so the hook exposes it directly.\n const sections = core.sections;\n\n const fields = useMemo(\n () => sections.flatMap((s) => s.groups.flatMap((g) => g.fields)),\n [sections],\n );\n\n const fieldPaths = useMemo(\n () => new Set(sections.flatMap((s) => s.groups.flatMap((g) => g.fields.map((f) => f.path)))),\n [sections],\n );\n\n return useMemo<PipeFormContext>(\n () => ({\n id: pipeId,\n publicKey,\n form: asFormHandle(core.form),\n sections,\n fields,\n fieldPaths,\n connectionsStatus: core.connectionsStatus,\n fieldLoadStates: core.fieldLoadStates,\n fieldLoaderErrors: core.fieldLoaderErrors,\n loadingFieldLoaders: core.loadingFieldLoaders,\n hasFieldLoaderError: core.hasFieldLoaderError,\n isFieldLoaderLoading: core.isFieldLoaderLoading,\n resolvers,\n store,\n reset: core.reset,\n }),\n [\n pipeId,\n publicKey,\n core.form,\n sections,\n fields,\n fieldPaths,\n core.connectionsStatus,\n core.fieldLoadStates,\n core.fieldLoaderErrors,\n core.loadingFieldLoaders,\n core.hasFieldLoaderError,\n core.isFieldLoaderLoading,\n resolvers,\n store,\n core.reset,\n ],\n );\n}\n"],"mappings":";;;;;;;AAkDA,SAAS,mBAAmB,QAAgB,SAAmC;CAC7E,MAAM,iBAAiB,sBAAsB,OAAO;AACpD,KAAI,CAAC,eAAgB,QAAO;AAC5B,QAAO;EACL,GAAG;EACH,QAAQ,eAAgB,QAAiD,UAAU,EAAE,EAAE,EACrF,GAAI,eAAe,QACpB,CAAC;EACH;;AAGH,SAAgB,YAAY,SAA8C;CACxE,MAAM,EACJ,QACA,WACA,eACA,WACA,mBACA,OAAO,kBACP,YACA,UACA,SACA,QACA,WACE;CAEJ,MAAM,SAAS,cAAc,qBAAqB,OAAO,EAAE,CAAC,OAAO,CAAC;CAEpE,MAAM,CAAC,OAAO,YAAY,SAAoB,oBAAoB,EAAE,eAAe,EAAE,EAAE,CAAC;CAMxF,MAAM,oBAAoB,WAAW,kBAAkB;CAGvD,MAAM,mBAAmB,KAAK,UAAU;EAAE;EAAY;EAAU;EAAS;EAAmB,CAAC;CA0B7F,MAAM,OAAO,YAAyB;EACpC,gBAAgB;EAChB,MAAM;EACN;EACA;EACA,eA5BiB,cACV,gBAAgB,mBAAmB,QAAQ,cAAc,GAAG,eACnE,CAAC,QAAQ,cAAc,CACxB;EA0BC;EACA;EACA,YAzBiB,cAAc;GAC/B,MAAM,iBAAiB,sBAAsB,OAAO;AACpD,OAAI,CAAC,eAAgB,OAAM,IAAI,MAAM,qBAAqB;AAE1D,UAAO,yBAAyB;IAC9B,aAAa;IACb;IACA;IACA,eAAe;IACf,eAAe;KACb;KACA,UAAU,oBAAoB,WAAW;MAAE,GAAG;MAAU,WAAW;MAAM;KACzE;KACD;IACF,CAAC;KACD;GAAC;GAAQ;GAAmB;GAAO;GAAiB,CAAC;EAWtD;EACA,aAAa,mBAAmB;EAChC;EACA;EACD,CAAC;CAIF,MAAM,WAAW,KAAK;CAEtB,MAAM,SAAS,cACP,SAAS,SAAS,MAAM,EAAE,OAAO,SAAS,MAAM,EAAE,OAAO,CAAC,EAChE,CAAC,SAAS,CACX;CAED,MAAM,aAAa,cACX,IAAI,IAAI,SAAS,SAAS,MAAM,EAAE,OAAO,SAAS,MAAM,EAAE,OAAO,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAC5F,CAAC,SAAS,CACX;AAED,QAAO,eACE;EACL,IAAI;EACJ;EACA,MAAM,aAAa,KAAK,KAAK;EAC7B;EACA;EACA;EACA,mBAAmB,KAAK;EACxB,iBAAiB,KAAK;EACtB,mBAAmB,KAAK;EACxB,qBAAqB,KAAK;EAC1B,qBAAqB,KAAK;EAC1B,sBAAsB,KAAK;EAC3B;EACA;EACA,OAAO,KAAK;EACb,GACD;EACE;EACA;EACA,KAAK;EACL;EACA;EACA;EACA,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL;EACA;EACA,KAAK;EACN,CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-search-form.d.mts","names":[],"sources":["../../src/hooks/use-search-form.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"use-search-form.d.mts","names":[],"sources":["../../src/hooks/use-search-form.ts"],"mappings":";;;;;;UAmBiB,oBAAA;EACf,QAAA,EAAU,QAAA;EACV,SAAA;EACA,aAAA,GAAgB,aAAA;EAChB,SAAA,GAAY,aAAA;EACZ,WAAA,GAAc,gBAAA;EACd,KAAA,GAAQ,SAAA;EADM;;;;;EAOd,UAAA,GAAa,UAAA;EAII;EAFjB,QAAA,GAAW,QAAA;EAbD;EAeV,OAAA,GAAU,OAAA;AAAA;AAAA,iBAeI,aAAA,CAAc,OAAA,EAAS,oBAAA,GAAuB,iBAAA"}
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { asFormHandle } from "../utils/internal-form.mjs";
|
|
2
2
|
import { useFormCore } from "./use-form-core.mjs";
|
|
3
3
|
import { useMemo, useState } from "react";
|
|
4
|
-
import { getSearchPayloadFormConfig, getSearchPayloadSchema } from "@pipe0/base";
|
|
4
|
+
import { configDefaults, getSearchDefaultPayload, getSearchPayloadFormConfig, getSearchPayloadSchema } from "@pipe0/base";
|
|
5
5
|
|
|
6
6
|
//#region src/hooks/use-search-form.ts
|
|
7
|
+
/** Catalog `config` defaults under the payload's own values. Mirrors `ProcessedSearchPayloadSchema`. */
|
|
8
|
+
function withConfigDefaults(searchId, payload) {
|
|
9
|
+
const defaultPayload = getSearchDefaultPayload(searchId);
|
|
10
|
+
if (!defaultPayload) return payload;
|
|
11
|
+
return {
|
|
12
|
+
...payload,
|
|
13
|
+
config: configDefaults(payload.config ?? {}, { ...defaultPayload.config })
|
|
14
|
+
};
|
|
15
|
+
}
|
|
7
16
|
function useSearchForm(options) {
|
|
8
17
|
const { searchId, publicKey, defaultValues, resolvers, environment, store: defaultStore, sectionMap, groupMap, pathMap } = options;
|
|
9
18
|
const schema = useMemo(() => getSearchPayloadSchema(searchId), [searchId]);
|
|
19
|
+
const seedValues = useMemo(() => defaultValues ? withConfigDefaults(searchId, defaultValues) : defaultValues, [searchId, defaultValues]);
|
|
10
20
|
const [store, setStore] = useState(defaultStore ?? { field_options: {} });
|
|
11
21
|
const hasGetConnections = resolvers?.getConnections != null;
|
|
12
22
|
const core = useFormCore({
|
|
@@ -14,12 +24,12 @@ function useSearchForm(options) {
|
|
|
14
24
|
kind: "search",
|
|
15
25
|
schema,
|
|
16
26
|
publicKey,
|
|
17
|
-
defaultValues,
|
|
27
|
+
defaultValues: seedValues,
|
|
18
28
|
store,
|
|
19
29
|
setStore,
|
|
20
30
|
formConfig: useMemo(() => {
|
|
21
31
|
return getSearchPayloadFormConfig({
|
|
22
|
-
searchPayload:
|
|
32
|
+
searchPayload: seedValues ?? { search_id: searchId },
|
|
23
33
|
formContext: store,
|
|
24
34
|
customization: {
|
|
25
35
|
sectionMap,
|
|
@@ -32,7 +42,7 @@ function useSearchForm(options) {
|
|
|
32
42
|
});
|
|
33
43
|
}, [
|
|
34
44
|
searchId,
|
|
35
|
-
|
|
45
|
+
seedValues,
|
|
36
46
|
store,
|
|
37
47
|
JSON.stringify({
|
|
38
48
|
sectionMap,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-search-form.mjs","names":[],"sources":["../../src/hooks/use-search-form.ts"],"sourcesContent":["import {\n type FormResolvers,\n type FormStore,\n getSearchPayloadFormConfig,\n getSearchPayloadSchema,\n type PipesEnvironment,\n type SearchId,\n type SearchPayload,\n} from \"@pipe0/base\";\nimport { useMemo, useState } from \"react\";\nimport type { SearchFormContext } from \"../context/search-form-context.js\";\nimport type { GroupMap, PathMap, SectionMap } from \"../types/form-customization.js\";\nimport { asFormHandle } from \"../utils/internal-form.js\";\nimport { type ResourceStatus, useFormCore } from \"./use-form-core.js\";\n\nexport type { ResourceStatus, SearchFormContext };\n\nexport interface UseSearchFormOptions {\n searchId: SearchId;\n publicKey: string;\n defaultValues?: SearchPayload;\n resolvers?: FormResolvers;\n environment?: PipesEnvironment;\n store?: FormStore;\n /**\n * Hide / relabel / reorder sections by section key. `null` hides a section.\n * Note: when `resolvers.getConnections` is omitted, the `connector` section\n * is hidden automatically — there's no value to render without a resolver.\n */\n sectionMap?: SectionMap;\n /** Hide / relabel / reorder groups by group path. `null` hides a group. */\n groupMap?: GroupMap;\n /** Hide / relabel fields by field path. `null` hides a field. */\n pathMap?: PathMap;\n}\n\nexport function useSearchForm(options: UseSearchFormOptions): SearchFormContext {\n const {\n searchId,\n publicKey,\n defaultValues,\n resolvers,\n environment,\n store: defaultStore,\n sectionMap,\n groupMap,\n pathMap,\n } = options;\n\n const schema = useMemo(() => getSearchPayloadSchema(searchId), [searchId]);\n\n const [store, setStore] = useState<FormStore>(defaultStore ?? { field_options: {} });\n\n // Connector auto-hides without a `getConnections` resolver — it's a *group*\n // (groupPath \"connector\"), so hide it via `groupMap`. Customization is applied\n // while the form config is built, so `core.sections` is already filtered.\n const hasGetConnections = resolvers?.getConnections != null;\n const customizationKey = JSON.stringify({ sectionMap, groupMap, pathMap, hasGetConnections });\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: customization inputs + resolver flag are tracked by value via `customizationKey` to keep the config stable across inline-object renders.\n const formConfig = useMemo(() => {\n return getSearchPayloadFormConfig({\n searchPayload:
|
|
1
|
+
{"version":3,"file":"use-search-form.mjs","names":[],"sources":["../../src/hooks/use-search-form.ts"],"sourcesContent":["import {\n configDefaults,\n type FormResolvers,\n type FormStore,\n getSearchDefaultPayload,\n getSearchPayloadFormConfig,\n getSearchPayloadSchema,\n type PipesEnvironment,\n type SearchId,\n type SearchPayload,\n} from \"@pipe0/base\";\nimport { useMemo, useState } from \"react\";\nimport type { SearchFormContext } from \"../context/search-form-context.js\";\nimport type { GroupMap, PathMap, SectionMap } from \"../types/form-customization.js\";\nimport { asFormHandle } from \"../utils/internal-form.js\";\nimport { type ResourceStatus, useFormCore } from \"./use-form-core.js\";\n\nexport type { ResourceStatus, SearchFormContext };\n\nexport interface UseSearchFormOptions {\n searchId: SearchId;\n publicKey: string;\n defaultValues?: SearchPayload;\n resolvers?: FormResolvers;\n environment?: PipesEnvironment;\n store?: FormStore;\n /**\n * Hide / relabel / reorder sections by section key. `null` hides a section.\n * Note: when `resolvers.getConnections` is omitted, the `connector` section\n * is hidden automatically — there's no value to render without a resolver.\n */\n sectionMap?: SectionMap;\n /** Hide / relabel / reorder groups by group path. `null` hides a group. */\n groupMap?: GroupMap;\n /** Hide / relabel fields by field path. `null` hides a field. */\n pathMap?: PathMap;\n}\n\n/** Catalog `config` defaults under the payload's own values. Mirrors `ProcessedSearchPayloadSchema`. */\nfunction withConfigDefaults(searchId: SearchId, payload: SearchPayload): SearchPayload {\n const defaultPayload = getSearchDefaultPayload(searchId);\n if (!defaultPayload) return payload;\n return {\n ...payload,\n config: configDefaults((payload as { config?: Record<string, unknown> }).config ?? {}, {\n ...(defaultPayload.config as Record<string, unknown>),\n }),\n } as SearchPayload;\n}\n\nexport function useSearchForm(options: UseSearchFormOptions): SearchFormContext {\n const {\n searchId,\n publicKey,\n defaultValues,\n resolvers,\n environment,\n store: defaultStore,\n sectionMap,\n groupMap,\n pathMap,\n } = options;\n\n const schema = useMemo(() => getSearchPayloadSchema(searchId), [searchId]);\n\n // Seed the form the way every other consumer of a stored payload reads one:\n // catalog defaults under the caller's values. A payload authored elsewhere\n // (the agent, an older build) legitimately omits keys it never set, and\n // `ProcessedSearchPayloadSchema` fills those in before anything validates or\n // runs it. Seeding the raw payload instead would leave those fields\n // `undefined` and the form would report the search as invalid — a config it\n // cannot save even though the server accepts it. `config` only, exactly what\n // the `Processed*` preprocess merges.\n const seedValues = useMemo(\n () => (defaultValues ? withConfigDefaults(searchId, defaultValues) : defaultValues),\n [searchId, defaultValues],\n );\n\n const [store, setStore] = useState<FormStore>(defaultStore ?? { field_options: {} });\n\n // Connector auto-hides without a `getConnections` resolver — it's a *group*\n // (groupPath \"connector\"), so hide it via `groupMap`. Customization is applied\n // while the form config is built, so `core.sections` is already filtered.\n const hasGetConnections = resolvers?.getConnections != null;\n const customizationKey = JSON.stringify({ sectionMap, groupMap, pathMap, hasGetConnections });\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: customization inputs + resolver flag are tracked by value via `customizationKey` to keep the config stable across inline-object renders.\n const formConfig = useMemo(() => {\n return getSearchPayloadFormConfig({\n searchPayload: seedValues ?? ({ search_id: searchId } as SearchPayload),\n formContext: store,\n customization: {\n sectionMap,\n groupMap: hasGetConnections ? groupMap : { ...groupMap, connector: null },\n pathMap,\n },\n });\n }, [searchId, seedValues, store, customizationKey]);\n\n const core = useFormCore<SearchPayload>({\n pipeOrSearchId: searchId,\n kind: \"search\",\n schema,\n publicKey,\n defaultValues: seedValues,\n store,\n setStore,\n formConfig,\n resolvers,\n environment,\n });\n\n const sections = core.sections;\n\n const fields = useMemo(\n () => sections.flatMap((s) => s.groups.flatMap((g) => g.fields)),\n [sections],\n );\n\n const fieldPaths = useMemo(\n () => new Set(sections.flatMap((s) => s.groups.flatMap((g) => g.fields.map((f) => f.path)))),\n [sections],\n );\n\n return useMemo<SearchFormContext>(\n () => ({\n id: searchId,\n publicKey,\n form: asFormHandle(core.form),\n sections,\n fields,\n fieldPaths,\n connectionsStatus: core.connectionsStatus,\n fieldLoadStates: core.fieldLoadStates,\n fieldLoaderErrors: core.fieldLoaderErrors,\n loadingFieldLoaders: core.loadingFieldLoaders,\n hasFieldLoaderError: core.hasFieldLoaderError,\n isFieldLoaderLoading: core.isFieldLoaderLoading,\n resolvers,\n store,\n reset: core.reset,\n }),\n [\n searchId,\n publicKey,\n core.form,\n sections,\n fields,\n fieldPaths,\n core.connectionsStatus,\n core.fieldLoadStates,\n core.fieldLoaderErrors,\n core.loadingFieldLoaders,\n core.hasFieldLoaderError,\n core.isFieldLoaderLoading,\n resolvers,\n store,\n core.reset,\n ],\n );\n}\n"],"mappings":";;;;;;;AAuCA,SAAS,mBAAmB,UAAoB,SAAuC;CACrF,MAAM,iBAAiB,wBAAwB,SAAS;AACxD,KAAI,CAAC,eAAgB,QAAO;AAC5B,QAAO;EACL,GAAG;EACH,QAAQ,eAAgB,QAAiD,UAAU,EAAE,EAAE,EACrF,GAAI,eAAe,QACpB,CAAC;EACH;;AAGH,SAAgB,cAAc,SAAkD;CAC9E,MAAM,EACJ,UACA,WACA,eACA,WACA,aACA,OAAO,cACP,YACA,UACA,YACE;CAEJ,MAAM,SAAS,cAAc,uBAAuB,SAAS,EAAE,CAAC,SAAS,CAAC;CAU1E,MAAM,aAAa,cACV,gBAAgB,mBAAmB,UAAU,cAAc,GAAG,eACrE,CAAC,UAAU,cAAc,CAC1B;CAED,MAAM,CAAC,OAAO,YAAY,SAAoB,gBAAgB,EAAE,eAAe,EAAE,EAAE,CAAC;CAKpF,MAAM,oBAAoB,WAAW,kBAAkB;CAgBvD,MAAM,OAAO,YAA2B;EACtC,gBAAgB;EAChB,MAAM;EACN;EACA;EACA,eAAe;EACf;EACA;EACA,YApBiB,cAAc;AAC/B,UAAO,2BAA2B;IAChC,eAAe,cAAe,EAAE,WAAW,UAAU;IACrD,aAAa;IACb,eAAe;KACb;KACA,UAAU,oBAAoB,WAAW;MAAE,GAAG;MAAU,WAAW;MAAM;KACzE;KACD;IACF,CAAC;KACD;GAAC;GAAU;GAAY;GAbD,KAAK,UAAU;IAAE;IAAY;IAAU;IAAS;IAAmB,CAAC;GAa3C,CAAC;EAWjD;EACA;EACD,CAAC;CAEF,MAAM,WAAW,KAAK;CAEtB,MAAM,SAAS,cACP,SAAS,SAAS,MAAM,EAAE,OAAO,SAAS,MAAM,EAAE,OAAO,CAAC,EAChE,CAAC,SAAS,CACX;CAED,MAAM,aAAa,cACX,IAAI,IAAI,SAAS,SAAS,MAAM,EAAE,OAAO,SAAS,MAAM,EAAE,OAAO,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAC5F,CAAC,SAAS,CACX;AAED,QAAO,eACE;EACL,IAAI;EACJ;EACA,MAAM,aAAa,KAAK,KAAK;EAC7B;EACA;EACA;EACA,mBAAmB,KAAK;EACxB,iBAAiB,KAAK;EACtB,mBAAmB,KAAK;EACxB,qBAAqB,KAAK;EAC1B,qBAAqB,KAAK;EAC1B,sBAAsB,KAAK;EAC3B;EACA;EACA,OAAO,KAAK;EACb,GACD;EACE;EACA;EACA,KAAK;EACL;EACA;EACA;EACA,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL;EACA;EACA,KAAK;EACN,CACF"}
|
|
@@ -19,16 +19,6 @@ interface UseSheetEffectFormOptions {
|
|
|
19
19
|
scopes?: string[];
|
|
20
20
|
teamId?: string;
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Effect analog of `usePipeForm` — drives a form for a sheet-effect payload
|
|
24
|
-
* (e.g. `sheet:rows:dedupe@1`) from `getSheetEffectPayloadFormConfig`.
|
|
25
|
-
*
|
|
26
|
-
* Returns a `PipeFormContext`-shaped object so the existing `<PipeForm>`
|
|
27
|
-
* compound components render it unchanged (the context interface is
|
|
28
|
-
* structural; only the `id`/payload generics differ). A dedicated
|
|
29
|
-
* `SheetEffectForm` compound family can be split out once effect forms need
|
|
30
|
-
* to diverge visually.
|
|
31
|
-
*/
|
|
32
22
|
declare function useSheetEffectForm(options: UseSheetEffectFormOptions): PipeFormContext;
|
|
33
23
|
//#endregion
|
|
34
24
|
export { UseSheetEffectFormOptions, useSheetEffectForm };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-sheet-effect-form.d.mts","names":[],"sources":["../../src/hooks/use-sheet-effect-form.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"use-sheet-effect-form.d.mts","names":[],"sources":["../../src/hooks/use-sheet-effect-form.ts"],"mappings":";;;;;UAiBiB,yBAAA;EACf,QAAA,EAAU,aAAA;EACV,SAAA;EACA,aAAA,GAAgB,kBAAA;EAChB,SAAA,GAAY,aAAA;EACZ,iBAAA,GAAoB,iBAAA;EACpB,KAAA,GAAQ,SAAA;EAFI;EAIZ,UAAA,GAAa,UAAA;EAFL;EAIR,QAAA,GAAW,QAAA;EAAA;EAEX,OAAA,GAAU,OAAA;EACV,MAAA;EACA,MAAA;AAAA;AAAA,iBA4Bc,kBAAA,CAAmB,OAAA,EAAS,yBAAA,GAA4B,eAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { asFormHandle } from "../utils/internal-form.mjs";
|
|
2
2
|
import { useFormCore } from "./use-form-core.mjs";
|
|
3
3
|
import { useMemo, useState } from "react";
|
|
4
|
-
import { getSheetEffectDefaultPayload, getSheetEffectPayloadFormConfig, getSheetEffectPayloadSchema } from "@pipe0/base";
|
|
4
|
+
import { configDefaults, getSheetEffectDefaultPayload, getSheetEffectPayloadFormConfig, getSheetEffectPayloadSchema } from "@pipe0/base";
|
|
5
5
|
|
|
6
6
|
//#region src/hooks/use-sheet-effect-form.ts
|
|
7
7
|
/**
|
|
@@ -14,17 +14,32 @@ import { getSheetEffectDefaultPayload, getSheetEffectPayloadFormConfig, getSheet
|
|
|
14
14
|
* `SheetEffectForm` compound family can be split out once effect forms need
|
|
15
15
|
* to diverge visually.
|
|
16
16
|
*/
|
|
17
|
+
/** Catalog `config` defaults under the payload's own values. Mirrors `ProcessedSheetEffectPayloadSchema`. */
|
|
18
|
+
function withConfigDefaults(effectId, payload) {
|
|
19
|
+
const defaultPayload = getSheetEffectDefaultPayload(effectId);
|
|
20
|
+
if (!defaultPayload) return payload;
|
|
21
|
+
return {
|
|
22
|
+
...payload,
|
|
23
|
+
config: configDefaults(payload.config ?? {}, { ...defaultPayload.config })
|
|
24
|
+
};
|
|
25
|
+
}
|
|
17
26
|
function useSheetEffectForm(options) {
|
|
18
27
|
const { effectId, publicKey, defaultValues, resolvers, validationContext, store: defaultFormStore, sectionMap, groupMap, pathMap, scopes, teamId } = options;
|
|
19
28
|
const schema = useMemo(() => getSheetEffectPayloadSchema(effectId), [effectId]);
|
|
20
29
|
const [store, setStore] = useState(defaultFormStore ?? { field_options: {} });
|
|
21
30
|
const hasGetConnections = resolvers?.getConnections != null;
|
|
31
|
+
const customizationKey = JSON.stringify({
|
|
32
|
+
sectionMap,
|
|
33
|
+
groupMap,
|
|
34
|
+
pathMap,
|
|
35
|
+
hasGetConnections
|
|
36
|
+
});
|
|
22
37
|
const core = useFormCore({
|
|
23
38
|
pipeOrSearchId: effectId,
|
|
24
39
|
kind: "effect",
|
|
25
40
|
schema,
|
|
26
41
|
publicKey,
|
|
27
|
-
defaultValues,
|
|
42
|
+
defaultValues: useMemo(() => defaultValues ? withConfigDefaults(effectId, defaultValues) : defaultValues, [effectId, defaultValues]),
|
|
28
43
|
store,
|
|
29
44
|
setStore,
|
|
30
45
|
formConfig: useMemo(() => {
|
|
@@ -48,12 +63,7 @@ function useSheetEffectForm(options) {
|
|
|
48
63
|
effectId,
|
|
49
64
|
validationContext,
|
|
50
65
|
store,
|
|
51
|
-
|
|
52
|
-
sectionMap,
|
|
53
|
-
groupMap,
|
|
54
|
-
pathMap,
|
|
55
|
-
hasGetConnections
|
|
56
|
-
})
|
|
66
|
+
customizationKey
|
|
57
67
|
]),
|
|
58
68
|
resolvers,
|
|
59
69
|
environment: validationContext?.environment,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-sheet-effect-form.mjs","names":[],"sources":["../../src/hooks/use-sheet-effect-form.ts"],"sourcesContent":["import {\n type FormResolvers,\n type FormStore,\n getSheetEffectDefaultPayload,\n getSheetEffectPayloadFormConfig,\n getSheetEffectPayloadSchema,\n type SheetEffectId,\n type SheetEffectPayload,\n type ValidationContext,\n} from \"@pipe0/base\";\nimport { useMemo, useState } from \"react\";\nimport type { PipeFormContext } from \"../context/pipe-form-context.js\";\nimport type { GroupMap, PathMap, SectionMap } from \"../types/form-customization.js\";\nimport { asFormHandle } from \"../utils/internal-form.js\";\nimport { useFormCore } from \"./use-form-core.js\";\n\nexport interface UseSheetEffectFormOptions {\n effectId: SheetEffectId;\n publicKey: string;\n defaultValues?: SheetEffectPayload;\n resolvers?: FormResolvers;\n validationContext?: ValidationContext;\n store?: FormStore;\n /** Hide / relabel / reorder sections by section key. `null` hides a section. */\n sectionMap?: SectionMap;\n /** Hide / relabel / reorder groups by group path. `null` hides a group. */\n groupMap?: GroupMap;\n /** Hide / relabel fields by field path. `null` hides a field. */\n pathMap?: PathMap;\n scopes?: string[];\n teamId?: string;\n}\n\n/**\n * Effect analog of `usePipeForm` — drives a form for a sheet-effect payload\n * (e.g. `sheet:rows:dedupe@1`) from `getSheetEffectPayloadFormConfig`.\n *\n * Returns a `PipeFormContext`-shaped object so the existing `<PipeForm>`\n * compound components render it unchanged (the context interface is\n * structural; only the `id`/payload generics differ). A dedicated\n * `SheetEffectForm` compound family can be split out once effect forms need\n * to diverge visually.\n */\nexport function useSheetEffectForm(options: UseSheetEffectFormOptions): PipeFormContext {\n const {\n effectId,\n publicKey,\n defaultValues,\n resolvers,\n validationContext,\n store: defaultFormStore,\n sectionMap,\n groupMap,\n pathMap,\n scopes,\n teamId,\n } = options;\n\n const schema = useMemo(() => getSheetEffectPayloadSchema(effectId), [effectId]);\n\n const [store, setStore] = useState<FormStore>(defaultFormStore ?? { field_options: {} });\n\n // Connector auto-hides without a `getConnections` resolver — it's a *group*\n // (groupPath \"connector\"), so hide it via `groupMap`. Mirrors `usePipeForm`.\n const hasGetConnections = resolvers?.getConnections != null;\n const customizationKey = JSON.stringify({ sectionMap, groupMap, pathMap, hasGetConnections });\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: customization inputs + resolver flag are tracked by value via `customizationKey` to keep the config stable across inline-object renders.\n const formConfig = useMemo(() => {\n const defaultPayload = getSheetEffectDefaultPayload(effectId);\n if (!defaultPayload) throw new Error(\"No default payload\");\n\n return getSheetEffectPayloadFormConfig({\n effectPayload: defaultPayload as SheetEffectPayload,\n store,\n validationContext,\n formResolvers: resolvers,\n customization: {\n sectionMap,\n groupMap: hasGetConnections ? groupMap : { ...groupMap, connector: null },\n pathMap,\n },\n });\n }, [effectId, validationContext, store, customizationKey]);\n\n const core = useFormCore<SheetEffectPayload>({\n pipeOrSearchId: effectId,\n kind: \"effect\",\n schema,\n publicKey,\n defaultValues,\n store,\n setStore,\n formConfig,\n resolvers,\n environment: validationContext?.environment,\n scopes,\n teamId,\n });\n\n const sections = core.sections;\n\n const fields = useMemo(\n () => sections.flatMap((s) => s.groups.flatMap((g) => g.fields)),\n [sections],\n );\n\n const fieldPaths = useMemo(\n () => new Set(sections.flatMap((s) => s.groups.flatMap((g) => g.fields.map((f) => f.path)))),\n [sections],\n );\n\n return useMemo<PipeFormContext>(\n () => ({\n // Structural reuse of PipeFormContext — see the function docblock.\n id: effectId as unknown as PipeFormContext[\"id\"],\n publicKey,\n form: asFormHandle(core.form) as unknown as PipeFormContext[\"form\"],\n sections,\n fields,\n fieldPaths,\n connectionsStatus: core.connectionsStatus,\n fieldLoadStates: core.fieldLoadStates,\n fieldLoaderErrors: core.fieldLoaderErrors,\n loadingFieldLoaders: core.loadingFieldLoaders,\n hasFieldLoaderError: core.hasFieldLoaderError,\n isFieldLoaderLoading: core.isFieldLoaderLoading,\n resolvers,\n store,\n reset: core.reset as unknown as PipeFormContext[\"reset\"],\n }),\n [\n effectId,\n publicKey,\n core.form,\n sections,\n fields,\n fieldPaths,\n core.connectionsStatus,\n core.fieldLoadStates,\n core.fieldLoaderErrors,\n core.loadingFieldLoaders,\n core.hasFieldLoaderError,\n core.isFieldLoaderLoading,\n resolvers,\n store,\n core.reset,\n ],\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-sheet-effect-form.mjs","names":[],"sources":["../../src/hooks/use-sheet-effect-form.ts"],"sourcesContent":["import {\n configDefaults,\n type FormResolvers,\n type FormStore,\n getSheetEffectDefaultPayload,\n getSheetEffectPayloadFormConfig,\n getSheetEffectPayloadSchema,\n type SheetEffectId,\n type SheetEffectPayload,\n type ValidationContext,\n} from \"@pipe0/base\";\nimport { useMemo, useState } from \"react\";\nimport type { PipeFormContext } from \"../context/pipe-form-context.js\";\nimport type { GroupMap, PathMap, SectionMap } from \"../types/form-customization.js\";\nimport { asFormHandle } from \"../utils/internal-form.js\";\nimport { useFormCore } from \"./use-form-core.js\";\n\nexport interface UseSheetEffectFormOptions {\n effectId: SheetEffectId;\n publicKey: string;\n defaultValues?: SheetEffectPayload;\n resolvers?: FormResolvers;\n validationContext?: ValidationContext;\n store?: FormStore;\n /** Hide / relabel / reorder sections by section key. `null` hides a section. */\n sectionMap?: SectionMap;\n /** Hide / relabel / reorder groups by group path. `null` hides a group. */\n groupMap?: GroupMap;\n /** Hide / relabel fields by field path. `null` hides a field. */\n pathMap?: PathMap;\n scopes?: string[];\n teamId?: string;\n}\n\n/**\n * Effect analog of `usePipeForm` — drives a form for a sheet-effect payload\n * (e.g. `sheet:rows:dedupe@1`) from `getSheetEffectPayloadFormConfig`.\n *\n * Returns a `PipeFormContext`-shaped object so the existing `<PipeForm>`\n * compound components render it unchanged (the context interface is\n * structural; only the `id`/payload generics differ). A dedicated\n * `SheetEffectForm` compound family can be split out once effect forms need\n * to diverge visually.\n */\n/** Catalog `config` defaults under the payload's own values. Mirrors `ProcessedSheetEffectPayloadSchema`. */\nfunction withConfigDefaults(\n effectId: SheetEffectId,\n payload: SheetEffectPayload,\n): SheetEffectPayload {\n const defaultPayload = getSheetEffectDefaultPayload(effectId);\n if (!defaultPayload) return payload;\n return {\n ...payload,\n config: configDefaults((payload as { config?: Record<string, unknown> }).config ?? {}, {\n ...(defaultPayload.config as Record<string, unknown>),\n }),\n } as SheetEffectPayload;\n}\n\nexport function useSheetEffectForm(options: UseSheetEffectFormOptions): PipeFormContext {\n const {\n effectId,\n publicKey,\n defaultValues,\n resolvers,\n validationContext,\n store: defaultFormStore,\n sectionMap,\n groupMap,\n pathMap,\n scopes,\n teamId,\n } = options;\n\n const schema = useMemo(() => getSheetEffectPayloadSchema(effectId), [effectId]);\n\n const [store, setStore] = useState<FormStore>(defaultFormStore ?? { field_options: {} });\n\n // Connector auto-hides without a `getConnections` resolver — it's a *group*\n // (groupPath \"connector\"), so hide it via `groupMap`. Mirrors `usePipeForm`.\n const hasGetConnections = resolvers?.getConnections != null;\n const customizationKey = JSON.stringify({ sectionMap, groupMap, pathMap, hasGetConnections });\n\n // Catalog defaults under the stored values — see the note in `useSearchForm`.\n const seedValues = useMemo(\n () => (defaultValues ? withConfigDefaults(effectId, defaultValues) : defaultValues),\n [effectId, defaultValues],\n );\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: customization inputs + resolver flag are tracked by value via `customizationKey` to keep the config stable across inline-object renders.\n const formConfig = useMemo(() => {\n const defaultPayload = getSheetEffectDefaultPayload(effectId);\n if (!defaultPayload) throw new Error(\"No default payload\");\n\n return getSheetEffectPayloadFormConfig({\n effectPayload: defaultPayload as SheetEffectPayload,\n store,\n validationContext,\n formResolvers: resolvers,\n customization: {\n sectionMap,\n groupMap: hasGetConnections ? groupMap : { ...groupMap, connector: null },\n pathMap,\n },\n });\n }, [effectId, validationContext, store, customizationKey]);\n\n const core = useFormCore<SheetEffectPayload>({\n pipeOrSearchId: effectId,\n kind: \"effect\",\n schema,\n publicKey,\n defaultValues: seedValues,\n store,\n setStore,\n formConfig,\n resolvers,\n environment: validationContext?.environment,\n scopes,\n teamId,\n });\n\n const sections = core.sections;\n\n const fields = useMemo(\n () => sections.flatMap((s) => s.groups.flatMap((g) => g.fields)),\n [sections],\n );\n\n const fieldPaths = useMemo(\n () => new Set(sections.flatMap((s) => s.groups.flatMap((g) => g.fields.map((f) => f.path)))),\n [sections],\n );\n\n return useMemo<PipeFormContext>(\n () => ({\n // Structural reuse of PipeFormContext — see the function docblock.\n id: effectId as unknown as PipeFormContext[\"id\"],\n publicKey,\n form: asFormHandle(core.form) as unknown as PipeFormContext[\"form\"],\n sections,\n fields,\n fieldPaths,\n connectionsStatus: core.connectionsStatus,\n fieldLoadStates: core.fieldLoadStates,\n fieldLoaderErrors: core.fieldLoaderErrors,\n loadingFieldLoaders: core.loadingFieldLoaders,\n hasFieldLoaderError: core.hasFieldLoaderError,\n isFieldLoaderLoading: core.isFieldLoaderLoading,\n resolvers,\n store,\n reset: core.reset as unknown as PipeFormContext[\"reset\"],\n }),\n [\n effectId,\n publicKey,\n core.form,\n sections,\n fields,\n fieldPaths,\n core.connectionsStatus,\n core.fieldLoadStates,\n core.fieldLoaderErrors,\n core.loadingFieldLoaders,\n core.hasFieldLoaderError,\n core.isFieldLoaderLoading,\n resolvers,\n store,\n core.reset,\n ],\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA6CA,SAAS,mBACP,UACA,SACoB;CACpB,MAAM,iBAAiB,6BAA6B,SAAS;AAC7D,KAAI,CAAC,eAAgB,QAAO;AAC5B,QAAO;EACL,GAAG;EACH,QAAQ,eAAgB,QAAiD,UAAU,EAAE,EAAE,EACrF,GAAI,eAAe,QACpB,CAAC;EACH;;AAGH,SAAgB,mBAAmB,SAAqD;CACtF,MAAM,EACJ,UACA,WACA,eACA,WACA,mBACA,OAAO,kBACP,YACA,UACA,SACA,QACA,WACE;CAEJ,MAAM,SAAS,cAAc,4BAA4B,SAAS,EAAE,CAAC,SAAS,CAAC;CAE/E,MAAM,CAAC,OAAO,YAAY,SAAoB,oBAAoB,EAAE,eAAe,EAAE,EAAE,CAAC;CAIxF,MAAM,oBAAoB,WAAW,kBAAkB;CACvD,MAAM,mBAAmB,KAAK,UAAU;EAAE;EAAY;EAAU;EAAS;EAAmB,CAAC;CA0B7F,MAAM,OAAO,YAAgC;EAC3C,gBAAgB;EAChB,MAAM;EACN;EACA;EACA,eA5BiB,cACV,gBAAgB,mBAAmB,UAAU,cAAc,GAAG,eACrE,CAAC,UAAU,cAAc,CAC1B;EA0BC;EACA;EACA,YAzBiB,cAAc;GAC/B,MAAM,iBAAiB,6BAA6B,SAAS;AAC7D,OAAI,CAAC,eAAgB,OAAM,IAAI,MAAM,qBAAqB;AAE1D,UAAO,gCAAgC;IACrC,eAAe;IACf;IACA;IACA,eAAe;IACf,eAAe;KACb;KACA,UAAU,oBAAoB,WAAW;MAAE,GAAG;MAAU,WAAW;MAAM;KACzE;KACD;IACF,CAAC;KACD;GAAC;GAAU;GAAmB;GAAO;GAAiB,CAAC;EAWxD;EACA,aAAa,mBAAmB;EAChC;EACA;EACD,CAAC;CAEF,MAAM,WAAW,KAAK;CAEtB,MAAM,SAAS,cACP,SAAS,SAAS,MAAM,EAAE,OAAO,SAAS,MAAM,EAAE,OAAO,CAAC,EAChE,CAAC,SAAS,CACX;CAED,MAAM,aAAa,cACX,IAAI,IAAI,SAAS,SAAS,MAAM,EAAE,OAAO,SAAS,MAAM,EAAE,OAAO,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAC5F,CAAC,SAAS,CACX;AAED,QAAO,eACE;EAEL,IAAI;EACJ;EACA,MAAM,aAAa,KAAK,KAAK;EAC7B;EACA;EACA;EACA,mBAAmB,KAAK;EACxB,iBAAiB,KAAK;EACtB,mBAAmB,KAAK;EACxB,qBAAqB,KAAK;EAC1B,qBAAqB,KAAK;EAC1B,sBAAsB,KAAK;EAC3B;EACA;EACA,OAAO,KAAK;EACb,GACD;EACE;EACA;EACA,KAAK;EACL;EACA;EACA;EACA,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL;EACA;EACA,KAAK;EACN,CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipe0/react",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "React component library for building forms and catalogs powered by pipe0 pipes and searches.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "pipe0",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"react-hook-form": "^7.62.0",
|
|
85
85
|
"swr": "^2.4.1",
|
|
86
86
|
"tailwind-merge": "^3.3.1",
|
|
87
|
-
"@pipe0/base": "1.0.
|
|
87
|
+
"@pipe0/base": "1.0.14"
|
|
88
88
|
},
|
|
89
89
|
"devDependencies": {
|
|
90
90
|
"@base-ui/react": "^1.6.0",
|