@osdk/react-components 0.10.0-main-5dc557e9e321d23e8e150e8015c41ebe3b6f387c → 0.10.0
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/AGENTS.md +6 -6
- package/CHANGELOG.md +8 -7
- package/README.md +4 -4
- package/build/browser/action-form/ActionForm.js +7 -3
- package/build/browser/action-form/ActionForm.js.map +1 -1
- package/build/browser/action-form/ActionFormApi.js.map +1 -1
- package/build/browser/action-form/BaseForm.js +58 -15
- package/build/browser/action-form/BaseForm.js.map +1 -1
- package/build/browser/action-form/FormSection.js +92 -0
- package/build/browser/action-form/FormSection.js.map +1 -0
- package/build/browser/action-form/FormSection.module.css +144 -0
- package/build/browser/action-form/FormSection.module.css.js +20 -0
- package/build/browser/action-form/fields/ObjectSelectField.js +1 -2
- package/build/browser/action-form/fields/ObjectSelectField.js.map +1 -1
- package/build/browser/action-form/fields/ObjectSetField.js +1 -2
- package/build/browser/action-form/fields/ObjectSetField.js.map +1 -1
- package/build/browser/filter-list/hooks/usePropertyAggregation.js +1 -1
- package/build/browser/filter-list/hooks/usePropertyAggregation.js.map +1 -1
- package/build/browser/filter-list/inputs/DateRangeFilterInput.js +1 -1
- package/build/browser/filter-list/inputs/DateRangeFilterInput.js.map +1 -1
- package/build/browser/filter-list/inputs/LinkedPropertyInput.js +1 -1
- package/build/browser/filter-list/inputs/LinkedPropertyInput.js.map +1 -1
- package/build/browser/filter-list/inputs/NumberRangeFilterInput.js +1 -1
- package/build/browser/filter-list/inputs/NumberRangeFilterInput.js.map +1 -1
- package/build/browser/object-table/ObjectTableApi.js.map +1 -1
- package/build/browser/object-table/hooks/useFunctionColumnsData.js +2 -2
- package/build/browser/object-table/hooks/useFunctionColumnsData.js.map +1 -1
- package/build/browser/object-table/hooks/useObjectTableData.js +1 -1
- package/build/browser/object-table/hooks/useObjectTableData.js.map +1 -1
- package/build/browser/public/experimental/action-form.js.map +1 -1
- package/build/browser/styles.css +205 -0
- package/build/browser/tokens/component-tokens/form-section.css +58 -0
- package/build/browser/tokens.css +1 -0
- package/build/browser/util/UserAgent.js +1 -1
- package/build/browser/util/UserAgent.js.map +1 -1
- package/build/browser/util/withOsdkMetrics.js +1 -1
- package/build/browser/util/withOsdkMetrics.js.map +1 -1
- package/build/cjs/{chunk-YUN7IILR.cjs → chunk-5DE6S6TS.cjs} +5 -5
- package/build/cjs/chunk-5DE6S6TS.cjs.map +1 -0
- package/build/cjs/{chunk-FVA2OU7H.cjs → chunk-NFFWKDF6.cjs} +12 -13
- package/build/cjs/chunk-NFFWKDF6.cjs.map +1 -0
- package/build/cjs/{chunk-RVARLNVS.cjs → chunk-PHMG5MOF.cjs} +4 -4
- package/build/cjs/{chunk-RVARLNVS.cjs.map → chunk-PHMG5MOF.cjs.map} +1 -1
- package/build/cjs/{chunk-HV6Q2FFI.cjs → chunk-QQXIHLYS.cjs} +8 -9
- package/build/cjs/chunk-QQXIHLYS.cjs.map +1 -0
- package/build/cjs/{chunk-4OIHG235.cjs → chunk-Y6KKCW7D.cjs} +336 -220
- package/build/cjs/chunk-Y6KKCW7D.cjs.map +1 -0
- package/build/cjs/public/experimental/action-form.cjs +4 -4
- package/build/cjs/public/experimental/action-form.css +102 -0
- package/build/cjs/public/experimental/action-form.css.map +1 -1
- package/build/cjs/public/experimental/action-form.d.cts +35 -2
- package/build/cjs/public/experimental/filter-list.cjs +6 -6
- package/build/cjs/public/experimental/object-table.cjs +8 -8
- package/build/cjs/public/experimental/object-table.d.cts +1 -1
- package/build/cjs/public/experimental/pdf-viewer.cjs +24 -24
- package/build/cjs/public/experimental.cjs +39 -39
- package/build/cjs/public/experimental.css +102 -0
- package/build/cjs/public/experimental.css.map +1 -1
- package/build/cjs/public/experimental.d.cts +2 -2
- package/build/esm/action-form/ActionForm.js +7 -3
- package/build/esm/action-form/ActionForm.js.map +1 -1
- package/build/esm/action-form/ActionFormApi.js.map +1 -1
- package/build/esm/action-form/BaseForm.js +58 -15
- package/build/esm/action-form/BaseForm.js.map +1 -1
- package/build/esm/action-form/FormSection.js +92 -0
- package/build/esm/action-form/FormSection.js.map +1 -0
- package/build/esm/action-form/FormSection.module.css +144 -0
- package/build/esm/action-form/fields/ObjectSelectField.js +1 -2
- package/build/esm/action-form/fields/ObjectSelectField.js.map +1 -1
- package/build/esm/action-form/fields/ObjectSetField.js +1 -2
- package/build/esm/action-form/fields/ObjectSetField.js.map +1 -1
- package/build/esm/filter-list/hooks/usePropertyAggregation.js +1 -1
- package/build/esm/filter-list/hooks/usePropertyAggregation.js.map +1 -1
- package/build/esm/filter-list/inputs/DateRangeFilterInput.js +1 -1
- package/build/esm/filter-list/inputs/DateRangeFilterInput.js.map +1 -1
- package/build/esm/filter-list/inputs/LinkedPropertyInput.js +1 -1
- package/build/esm/filter-list/inputs/LinkedPropertyInput.js.map +1 -1
- package/build/esm/filter-list/inputs/NumberRangeFilterInput.js +1 -1
- package/build/esm/filter-list/inputs/NumberRangeFilterInput.js.map +1 -1
- package/build/esm/object-table/ObjectTableApi.js.map +1 -1
- package/build/esm/object-table/hooks/useFunctionColumnsData.js +2 -2
- package/build/esm/object-table/hooks/useFunctionColumnsData.js.map +1 -1
- package/build/esm/object-table/hooks/useObjectTableData.js +1 -1
- package/build/esm/object-table/hooks/useObjectTableData.js.map +1 -1
- package/build/esm/public/experimental/action-form.js.map +1 -1
- package/build/esm/tokens/component-tokens/form-section.css +58 -0
- package/build/esm/tokens.css +1 -0
- package/build/esm/util/UserAgent.js +1 -1
- package/build/esm/util/UserAgent.js.map +1 -1
- package/build/esm/util/withOsdkMetrics.js +1 -1
- package/build/esm/util/withOsdkMetrics.js.map +1 -1
- package/build/types/action-form/ActionForm.d.ts.map +1 -1
- package/build/types/action-form/ActionFormApi.d.ts +34 -1
- package/build/types/action-form/ActionFormApi.d.ts.map +1 -1
- package/build/types/action-form/BaseForm.d.ts.map +1 -1
- package/build/types/action-form/FormSection.d.ts +8 -0
- package/build/types/action-form/FormSection.d.ts.map +1 -0
- package/build/types/action-form/fields/ObjectSelectField.d.ts.map +1 -1
- package/build/types/action-form/fields/ObjectSetField.d.ts.map +1 -1
- package/build/types/object-table/ObjectTableApi.d.ts +1 -1
- package/build/types/object-table/ObjectTableApi.d.ts.map +1 -1
- package/build/types/object-table/hooks/useObjectTableData.d.ts +1 -1
- package/build/types/object-table/hooks/useObjectTableData.d.ts.map +1 -1
- package/build/types/public/experimental/action-form.d.ts +1 -1
- package/build/types/public/experimental/action-form.d.ts.map +1 -1
- package/docs/FilterList.md +1 -1
- package/docs/ObjectTable.md +1 -1
- package/docs/Prerequisites.md +4 -4
- package/package.json +8 -8
- package/build/cjs/chunk-4OIHG235.cjs.map +0 -1
- package/build/cjs/chunk-FVA2OU7H.cjs.map +0 -1
- package/build/cjs/chunk-HV6Q2FFI.cjs.map +0 -1
- package/build/cjs/chunk-YUN7IILR.cjs.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -27,12 +27,12 @@ See `@osdk/react`'s `AGENTS.md` for optional peers (`@osdk/foundry.admin`, `@osd
|
|
|
27
27
|
|
|
28
28
|
## Install-time errors
|
|
29
29
|
|
|
30
|
-
| Error
|
|
31
|
-
|
|
|
32
|
-
| `"<name>" is not exported by @osdk/client/.../unstable-do-not-use.js
|
|
33
|
-
| `"<name>" is not exported by @osdk/react/...`
|
|
34
|
-
| `Rollup failed to resolve import "@osdk/foundry.admin"` (or `@osdk/foundry.core`)
|
|
35
|
-
| pnpm/npm peer warning about `@osdk/client` or `@osdk/react` range
|
|
30
|
+
| Error | Cause | Fix |
|
|
31
|
+
| ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
32
|
+
| `"<name>" is not exported by @osdk/client/.../observable.js` (or `@osdk/client/.../unstable-do-not-use.js`, or `@osdk/api/...`) | `@osdk/client` or `@osdk/api` or `@osdk/react` version mismatches what `@osdk/react-components` was built against | Do NOT delete the import or downgrade silently. Follow the CHANGELOG recipe in `## Installing` and pin all three peers to the exact versions listed. |
|
|
33
|
+
| `"<name>" is not exported by @osdk/react/...` | `@osdk/react` version mismatches what `@osdk/react-components` was built against | Do NOT delete the import or downgrade silently. Follow the CHANGELOG recipe in `## Installing` and pin `@osdk/react` to the exact version listed. |
|
|
34
|
+
| `Rollup failed to resolve import "@osdk/foundry.admin"` (or `@osdk/foundry.core`) | Transitive import from `@osdk/react/platform-apis` without the optional peers | Install `@osdk/foundry.admin` + `@osdk/foundry.core`, OR avoid surfaces that use the admin hooks. |
|
|
35
|
+
| pnpm/npm peer warning about `@osdk/client` or `@osdk/react` range | Declared peer ranges are broad; prerelease coupling is tighter | Follow the CHANGELOG recipe; pin to exact versions. |
|
|
36
36
|
|
|
37
37
|
## Components
|
|
38
38
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
# @osdk/react-components
|
|
2
2
|
|
|
3
|
-
## 0.10.0
|
|
3
|
+
## 0.10.0
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
7
|
+
- b355bc3: Add CONTRIBUTING.md for @osdk/react and @osdk/react-components
|
|
8
|
+
- 9b45e7b: Add form section support to BaseForm with collapsible groups, multi-column grid, and custom submit button
|
|
7
9
|
- 3a4528c: Add ObjectSelect field for selecting object instances in action forms
|
|
10
|
+
- 5dc557e: Add helperText tooltip using Popover and widen type to React.ReactNode
|
|
8
11
|
|
|
9
12
|
### Patch Changes
|
|
10
13
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- Updated dependencies [eb36e21]
|
|
14
|
+
- Updated dependencies [f747fa3]
|
|
15
|
+
- Updated dependencies [d892397]
|
|
14
16
|
- Updated dependencies [c5a6047]
|
|
15
17
|
- Updated dependencies [45be476]
|
|
16
18
|
- Updated dependencies [b355bc3]
|
|
17
|
-
|
|
18
|
-
- @osdk/react@0.17.0
|
|
19
|
-
- @osdk/api@2.14.0-main-5dc557e9e321d23e8e150e8015c41ebe3b6f387c
|
|
19
|
+
- Updated dependencies [20e9678]
|
|
20
|
+
- @osdk/react@0.17.0
|
|
20
21
|
|
|
21
22
|
## 0.9.0
|
|
22
23
|
|
package/README.md
CHANGED
|
@@ -48,22 +48,22 @@ npm install react react-dom classnames @osdk/react @osdk/client @osdk/api
|
|
|
48
48
|
**Prerequisites:**
|
|
49
49
|
|
|
50
50
|
- A configured OSDK client
|
|
51
|
-
- An
|
|
51
|
+
- An OsdkProvider wrapping your application
|
|
52
52
|
|
|
53
53
|
## Setup
|
|
54
54
|
|
|
55
55
|
### App Setup
|
|
56
56
|
|
|
57
|
-
**REQUIRED:** Wrap app with
|
|
57
|
+
**REQUIRED:** Wrap app with OsdkProvider:
|
|
58
58
|
|
|
59
59
|
```tsx
|
|
60
60
|
import { createClient } from "@osdk/client";
|
|
61
|
-
import {
|
|
61
|
+
import { OsdkProvider } from "@osdk/react";
|
|
62
62
|
|
|
63
63
|
const client = createClient(/* config */);
|
|
64
64
|
|
|
65
65
|
function App() {
|
|
66
|
-
return <
|
|
66
|
+
return <OsdkProvider client={client}>{/* components */}</OsdkProvider>;
|
|
67
67
|
}
|
|
68
68
|
```
|
|
69
69
|
|
|
@@ -15,14 +15,14 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { useOsdkMetadata } from "@osdk/react";
|
|
19
|
-
import { useOsdkAction } from "@osdk/react/experimental";
|
|
18
|
+
import { useOsdkAction, useOsdkMetadata } from "@osdk/react";
|
|
20
19
|
import React, { useCallback, useEffect, useMemo } from "react";
|
|
21
20
|
import { typedReactMemo } from "../shared/typedMemo.js";
|
|
22
21
|
import { BaseForm } from "./BaseForm.js";
|
|
23
22
|
import { coerceFieldValue } from "./utils/coerceFieldValue.js";
|
|
24
23
|
import { getDefaultFieldDefinitions } from "./utils/getDefaultFieldDefinitions.js";
|
|
25
24
|
const EMPTY_FIELD_DEFINITIONS = [];
|
|
25
|
+
const EMPTY_FORM_CONTENT = [];
|
|
26
26
|
export const ActionForm = typedReactMemo(function ({
|
|
27
27
|
actionDefinition,
|
|
28
28
|
formTitle,
|
|
@@ -68,6 +68,10 @@ export const ActionForm = typedReactMemo(function ({
|
|
|
68
68
|
}));
|
|
69
69
|
}, [formFieldDefinitions, parameters]);
|
|
70
70
|
const rendererFieldDefinitions = useMemo(() => customFieldDefinitions ?? (metadata != null ? getDefaultFieldDefinitions(metadata) : EMPTY_FIELD_DEFINITIONS), [customFieldDefinitions, metadata]);
|
|
71
|
+
const formContent = useMemo(() => rendererFieldDefinitions.length === 0 ? EMPTY_FORM_CONTENT : rendererFieldDefinitions.map(def => ({
|
|
72
|
+
type: "field",
|
|
73
|
+
definition: def
|
|
74
|
+
})), [rendererFieldDefinitions]);
|
|
71
75
|
const coerceFormState = useCallback(rawState => {
|
|
72
76
|
const coerced = {};
|
|
73
77
|
for (const [key, value] of Object.entries(rawState)) {
|
|
@@ -100,7 +104,7 @@ export const ActionForm = typedReactMemo(function ({
|
|
|
100
104
|
const resolvedTitle = formTitle ?? metadata?.displayName ?? actionDefinition.apiName;
|
|
101
105
|
const commonProps = {
|
|
102
106
|
formTitle: resolvedTitle,
|
|
103
|
-
|
|
107
|
+
formContent,
|
|
104
108
|
onSubmit: handleSubmit,
|
|
105
109
|
isSubmitDisabled,
|
|
106
110
|
isPending,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionForm.js","names":["
|
|
1
|
+
{"version":3,"file":"ActionForm.js","names":["useOsdkAction","useOsdkMetadata","React","useCallback","useEffect","useMemo","typedReactMemo","BaseForm","coerceFieldValue","getDefaultFieldDefinitions","EMPTY_FIELD_DEFINITIONS","EMPTY_FORM_CONTENT","ActionForm","actionDefinition","formTitle","formFieldDefinitions","formState","controlledFormState","onFormStateChange","isSubmitDisabled","onSubmit","onValidationResponse","_onValidationResponse","onSuccess","onError","applyAction","osdkApplyAction","isPending","metadata","loading","metadataLoading","error","metadataError","type","parameters","customFieldDefinitions","map","def","fieldKey","String","fieldType","defaultValue","rendererFieldDefinitions","formContent","length","definition","coerceFormState","rawState","coerced","key","value","Object","entries","handleSubmit","rawFormState","result","e","handleFieldValueChange","prev","resolvedTitle","displayName","apiName","commonProps","isLoading","onFieldValueChange","createElement","_extends"],"sources":["ActionForm.tsx"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ActionDefinition } from \"@osdk/api\";\nimport { useOsdkAction, useOsdkMetadata } from \"@osdk/react\";\nimport React, { useCallback, useEffect, useMemo } from \"react\";\nimport { typedReactMemo } from \"../shared/typedMemo.js\";\nimport type {\n ActionFormProps,\n FormContentItem,\n FormState,\n} from \"./ActionFormApi.js\";\nimport { BaseForm } from \"./BaseForm.js\";\nimport type { RendererFieldDefinition } from \"./FormFieldApi.js\";\nimport { coerceFieldValue } from \"./utils/coerceFieldValue.js\";\nimport { getDefaultFieldDefinitions } from \"./utils/getDefaultFieldDefinitions.js\";\n\nconst EMPTY_FIELD_DEFINITIONS: ReadonlyArray<RendererFieldDefinition> = [];\nconst EMPTY_FORM_CONTENT: ReadonlyArray<FormContentItem> = [];\n\nexport const ActionForm: <Q extends ActionDefinition<unknown>>(\n props: ActionFormProps<Q>,\n) => React.ReactElement = typedReactMemo(function ActionFormFn<\n Q extends ActionDefinition<unknown>,\n>({\n actionDefinition,\n formTitle,\n formFieldDefinitions,\n formState: controlledFormState,\n onFormStateChange,\n isSubmitDisabled,\n onSubmit,\n onValidationResponse: _onValidationResponse,\n onSuccess,\n onError,\n}: ActionFormProps<Q>): React.ReactElement {\n const { applyAction: osdkApplyAction, isPending } = useOsdkAction(\n actionDefinition,\n );\n const {\n metadata,\n loading: metadataLoading,\n error: metadataError,\n } = useOsdkMetadata(actionDefinition);\n\n useEffect(\n function saveMetadataError() {\n if (metadataError != null) {\n onError?.({ type: \"unknown\", error: metadataError });\n }\n },\n [metadataError, onError],\n );\n\n const parameters = metadata?.parameters;\n\n const customFieldDefinitions: ReadonlyArray<RendererFieldDefinition> | null =\n useMemo(() => {\n if (formFieldDefinitions == null) {\n return null;\n }\n // RendererFieldDefinition is a discriminated union keyed by fieldComponent.\n // TypeScript can't verify that the spread preserves the fieldComponent ↔\n // fieldComponentProps pairing, but FormFieldDefinition guarantees it.\n return formFieldDefinitions.map(\n (def) =>\n ({\n ...def,\n fieldKey: String(def.fieldKey),\n fieldType: parameters?.[String(def.fieldKey)]?.type,\n defaultValue: def.defaultValue,\n }) as RendererFieldDefinition,\n );\n }, [formFieldDefinitions, parameters]);\n\n const rendererFieldDefinitions = useMemo(\n () =>\n customFieldDefinitions\n ?? (metadata != null\n ? getDefaultFieldDefinitions(metadata)\n : EMPTY_FIELD_DEFINITIONS),\n [customFieldDefinitions, metadata],\n );\n\n const formContent = useMemo(\n (): ReadonlyArray<FormContentItem> =>\n rendererFieldDefinitions.length === 0\n ? EMPTY_FORM_CONTENT\n : rendererFieldDefinitions.map(\n (def): FormContentItem => ({ type: \"field\", definition: def }),\n ),\n [rendererFieldDefinitions],\n );\n\n const coerceFormState = useCallback(\n (rawState: Record<string, unknown>): Record<string, unknown> => {\n const coerced: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(rawState)) {\n coerced[key] = coerceFieldValue(parameters?.[key]?.type, value);\n }\n return coerced;\n },\n [parameters],\n );\n\n const handleSubmit = useCallback(\n async (rawFormState: Record<string, unknown>) => {\n const formState = coerceFormState(rawFormState) as FormState<Q>;\n try {\n if (onSubmit != null) {\n await onSubmit(formState, osdkApplyAction);\n } else {\n const result = await osdkApplyAction(formState);\n onSuccess?.(result);\n }\n } catch (e) {\n onError?.({ type: \"submission\", error: e });\n }\n },\n [coerceFormState, onSubmit, osdkApplyAction, onSuccess, onError],\n );\n\n const handleFieldValueChange = useCallback(\n (fieldKey: string, value: unknown) => {\n onFormStateChange?.(\n (prev) =>\n ({\n ...prev,\n [fieldKey]: value,\n }) as FormState<Q>,\n );\n },\n [onFormStateChange],\n );\n\n const resolvedTitle = formTitle ?? metadata?.displayName\n ?? actionDefinition.apiName;\n\n const isControlled = controlledFormState != null;\n\n const commonProps = {\n formTitle: resolvedTitle,\n formContent,\n onSubmit: handleSubmit,\n isSubmitDisabled,\n isPending,\n isLoading: metadataLoading,\n onFieldValueChange: handleFieldValueChange,\n };\n\n if (!isControlled) {\n return <BaseForm {...commonProps} />;\n }\n\n return <BaseForm {...commonProps} formState={controlledFormState} />;\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionFormApi.js","names":[],"sources":["ActionFormApi.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n ActionDefinition,\n ActionEditResponse,\n ActionValidationResponse,\n} from \"@osdk/api\";\nimport type { ActionValidationError } from \"@osdk/client\";\nimport type {\n ActionParameters,\n FieldKey,\n FieldValueType,\n FormFieldDefinition,\n RendererFieldDefinition,\n} from \"./FormFieldApi.js\";\n\n/**\n * Props for the ActionForm component.\n *\n * A discriminated union ensures that controlled mode (formState provided)\n * always requires onFormStateChange, and uncontrolled mode makes `onFormStateChange` optional\n */\nexport type ActionFormProps<Q extends ActionDefinition<unknown>> =\n | (ActionFormConfigProps<Q> & {\n formState: FormState<Q>;\n onFormStateChange: (\n updater: (prevState: FormState<Q>) => FormState<Q>,\n ) => void;\n })\n | (ActionFormConfigProps<Q> & {\n formState?: undefined;\n onFormStateChange?: (\n updater: (prevState: FormState<Q>) => FormState<Q>,\n ) => void;\n });\n\ninterface ActionFormConfigProps<Q extends ActionDefinition<unknown>>\n extends Pick<BaseFormProps, \"formTitle\" | \"isSubmitDisabled\">\n{\n actionDefinition: Q;\n\n /**\n * If not supplied, field definitions are constructed from `ActionParameters`.\n */\n formFieldDefinitions?: ReadonlyArray<FormFieldDefinition<Q>>;\n\n /**\n * If supplied, this will override the default submit action\n * By default, the action's applyAction will be called\n *\n * @param formState all field values when onSubmit is called\n * @param applyAction the function to execute the action\n * @returns a promise of the submission response\n */\n onSubmit?: (\n formState: FormState<Q>,\n applyAction: (\n args: ActionParameters<Q>,\n ) => Promise<ActionEditResponse | undefined>,\n ) => Promise<unknown> | void;\n\n /**\n * Called when the validation response is returned from a validateOnly submission\n *\n * @param results the validation response\n */\n onValidationResponse?: (results: ActionValidationResponse) => void;\n\n /**\n * Called when the action is successfully executed from a non-validateOnly submission\n *\n * @param results the submission response\n */\n onSuccess?: (results: ActionEditResponse | undefined) => void;\n\n /**\n * Called when there is an error in form submission\n *\n * @param error the error that occurred\n */\n onError?: (error: FormError) => void;\n}\n\n/**\n * Form values mapping parameter names to their values\n */\nexport type FormState<Q extends ActionDefinition<unknown>> = {\n [K in FieldKey<Q>]?: FieldValueType<Q, K>;\n};\n\n/**\n * Form error discriminated union\n */\nexport type FormError =\n | { type: \"validation\"; error: ActionValidationError }\n | { type: \"submission\"; error: unknown }\n | { type: \"unknown\"; error: unknown };\n\n/**\n * Props for the `BaseForm` component, which renders a form without\n * OSDK data fetching.\n *\n * Uses a discriminated union so that controlled mode (`formState` provided)\n * always requires `onFieldValueChange`, and uncontrolled mode omits both.\n * `onSubmit` receives the current form state so callers can access values\n * even in uncontrolled mode.\n */\nexport type BaseFormProps =\n & BaseFormCommonProps\n & (\n | {\n formState: Record<string, unknown>;\n onFieldValueChange: (fieldKey: string, value: unknown) => void;\n }\n | {\n formState?: undefined;\n onFieldValueChange?: (fieldKey: string, value: unknown) => void;\n }\n );\n\ninterface BaseFormCommonProps {\n formTitle?: string;\n
|
|
1
|
+
{"version":3,"file":"ActionFormApi.js","names":[],"sources":["ActionFormApi.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n ActionDefinition,\n ActionEditResponse,\n ActionValidationResponse,\n} from \"@osdk/api\";\nimport type { ActionValidationError } from \"@osdk/client\";\nimport type {\n ActionParameters,\n FieldKey,\n FieldValueType,\n FormFieldDefinition,\n RendererFieldDefinition,\n} from \"./FormFieldApi.js\";\n\n/**\n * Props for the ActionForm component.\n *\n * A discriminated union ensures that controlled mode (formState provided)\n * always requires onFormStateChange, and uncontrolled mode makes `onFormStateChange` optional\n */\nexport type ActionFormProps<Q extends ActionDefinition<unknown>> =\n | (ActionFormConfigProps<Q> & {\n formState: FormState<Q>;\n onFormStateChange: (\n updater: (prevState: FormState<Q>) => FormState<Q>,\n ) => void;\n })\n | (ActionFormConfigProps<Q> & {\n formState?: undefined;\n onFormStateChange?: (\n updater: (prevState: FormState<Q>) => FormState<Q>,\n ) => void;\n });\n\ninterface ActionFormConfigProps<Q extends ActionDefinition<unknown>>\n extends Pick<BaseFormProps, \"formTitle\" | \"isSubmitDisabled\">\n{\n actionDefinition: Q;\n\n /**\n * If not supplied, field definitions are constructed from `ActionParameters`.\n */\n formFieldDefinitions?: ReadonlyArray<FormFieldDefinition<Q>>;\n\n /**\n * If supplied, this will override the default submit action\n * By default, the action's applyAction will be called\n *\n * @param formState all field values when onSubmit is called\n * @param applyAction the function to execute the action\n * @returns a promise of the submission response\n */\n onSubmit?: (\n formState: FormState<Q>,\n applyAction: (\n args: ActionParameters<Q>,\n ) => Promise<ActionEditResponse | undefined>,\n ) => Promise<unknown> | void;\n\n /**\n * Called when the validation response is returned from a validateOnly submission\n *\n * @param results the validation response\n */\n onValidationResponse?: (results: ActionValidationResponse) => void;\n\n /**\n * Called when the action is successfully executed from a non-validateOnly submission\n *\n * @param results the submission response\n */\n onSuccess?: (results: ActionEditResponse | undefined) => void;\n\n /**\n * Called when there is an error in form submission\n *\n * @param error the error that occurred\n */\n onError?: (error: FormError) => void;\n}\n\n/**\n * Form values mapping parameter names to their values\n */\nexport type FormState<Q extends ActionDefinition<unknown>> = {\n [K in FieldKey<Q>]?: FieldValueType<Q, K>;\n};\n\n/**\n * Form error discriminated union\n */\nexport type FormError =\n | { type: \"validation\"; error: ActionValidationError }\n | { type: \"submission\"; error: unknown }\n | { type: \"unknown\"; error: unknown };\n\n/**\n * A single item in the form content array — either a standalone field\n * or a section that groups multiple fields.\n */\nexport type FormContentItem =\n | { type: \"field\"; definition: RendererFieldDefinition }\n | { type: \"section\"; key: string; definition: FormSectionDefinition };\n\n/**\n * Configuration for a form section — a visual group of fields with\n * optional title bar, collapse behavior, and multi-column layout.\n */\nexport interface FormSectionDefinition {\n title: string;\n description?: string;\n fields: ReadonlyArray<RendererFieldDefinition>;\n /** Whether the section starts collapsed. Default `false`. */\n collapsedByDefault?: boolean;\n /** Whether to show the title bar. Default `true`. */\n showTitleBar?: boolean;\n /** Number of columns for fields. Default `1`. */\n columnCount?: 1 | 2;\n /** Visual style. `\"box\"` = bordered card, `\"minimal\"` = heading only. Default `\"box\"`. */\n style?: \"box\" | \"minimal\";\n}\n\n/**\n * Props for the `BaseForm` component, which renders a form without\n * OSDK data fetching.\n *\n * Uses a discriminated union so that controlled mode (`formState` provided)\n * always requires `onFieldValueChange`, and uncontrolled mode omits both.\n * `onSubmit` receives the current form state so callers can access values\n * even in uncontrolled mode.\n */\nexport type BaseFormProps =\n & BaseFormCommonProps\n & (\n | {\n formState: Record<string, unknown>;\n onFieldValueChange: (fieldKey: string, value: unknown) => void;\n }\n | {\n formState?: undefined;\n onFieldValueChange?: (fieldKey: string, value: unknown) => void;\n }\n );\n\ninterface BaseFormCommonProps {\n formTitle?: string;\n formContent: ReadonlyArray<FormContentItem>;\n onSubmit: (formState: Record<string, unknown>) => Promise<void> | void;\n isSubmitDisabled?: boolean;\n isPending?: boolean;\n isLoading?: boolean;\n className?: string;\n /** Label for the submit button. Default `\"Submit\"`. */\n submitButtonText?: string;\n /** Visual variant of the submit button. Default `\"primary\"`. */\n submitButtonVariant?: \"primary\" | \"secondary\";\n}\n"],"mappings":"","ignoreList":[]}
|
|
@@ -25,18 +25,22 @@ import { useAsyncAction } from "../shared/hooks/useAsyncAction.js";
|
|
|
25
25
|
import styles from "./BaseForm.module.css.js";
|
|
26
26
|
import { FieldBridge } from "./fields/FieldBridge.js";
|
|
27
27
|
import { FormHeader } from "./FormHeader.js";
|
|
28
|
+
import { FormSection } from "./FormSection.js";
|
|
28
29
|
export const BaseForm = /*#__PURE__*/memo(function ({
|
|
29
30
|
formTitle,
|
|
30
|
-
|
|
31
|
+
formContent,
|
|
31
32
|
formState: controlledFormState,
|
|
32
33
|
onFieldValueChange,
|
|
33
34
|
onSubmit,
|
|
34
35
|
isSubmitDisabled = false,
|
|
35
36
|
isPending = false,
|
|
36
37
|
isLoading = false,
|
|
37
|
-
className
|
|
38
|
+
className,
|
|
39
|
+
submitButtonText = "Submit",
|
|
40
|
+
submitButtonVariant = "primary"
|
|
38
41
|
}) {
|
|
39
|
-
const
|
|
42
|
+
const allFieldDefinitions = useMemo(() => flattenFieldDefinitions(formContent), [formContent]);
|
|
43
|
+
const defaultValues = useMemo(() => buildDefaultValues(allFieldDefinitions), [allFieldDefinitions]);
|
|
40
44
|
const {
|
|
41
45
|
control,
|
|
42
46
|
trigger,
|
|
@@ -84,7 +88,7 @@ export const BaseForm = /*#__PURE__*/memo(function ({
|
|
|
84
88
|
clearError();
|
|
85
89
|
onFieldValueChange?.(fieldKey, value);
|
|
86
90
|
}, [clearError, onFieldValueChange]);
|
|
87
|
-
const labelByFieldKey = useMemo(() => new Map(
|
|
91
|
+
const labelByFieldKey = useMemo(() => new Map(allFieldDefinitions.map(d => [d.fieldKey, d.label])), [allFieldDefinitions]);
|
|
88
92
|
|
|
89
93
|
// RHF reuses the same errors object reference across renders so we cannot memoize errorEntries
|
|
90
94
|
const errorEntries = Object.entries(errors).map(([key, entry]) => ({
|
|
@@ -98,18 +102,33 @@ export const BaseForm = /*#__PURE__*/memo(function ({
|
|
|
98
102
|
onSubmit: handleFormSubmit
|
|
99
103
|
}, formTitle != null && /*#__PURE__*/React.createElement(FormHeader, {
|
|
100
104
|
title: formTitle
|
|
101
|
-
}), isLoading &&
|
|
105
|
+
}), isLoading && allFieldDefinitions.length === 0 && /*#__PURE__*/React.createElement("div", {
|
|
102
106
|
role: "status",
|
|
103
107
|
"aria-label": "Loading form fields",
|
|
104
108
|
className: styles.osdkFormFields
|
|
105
109
|
}, FORM_SKELETON), /*#__PURE__*/React.createElement("div", {
|
|
106
110
|
className: styles.osdkFormFields
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
}, formContent.map(item => {
|
|
112
|
+
if (item.type === "field") {
|
|
113
|
+
return /*#__PURE__*/React.createElement(FieldBridge, {
|
|
114
|
+
key: item.definition.fieldKey,
|
|
115
|
+
fieldDef: item.definition,
|
|
116
|
+
control: control,
|
|
117
|
+
onExternalChange: handleFieldChange
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
const sectionErrorCount = item.definition.fields.reduce((count, field) => count + (errors[field.fieldKey] != null ? 1 : 0), 0);
|
|
121
|
+
return /*#__PURE__*/React.createElement(FormSection, {
|
|
122
|
+
key: item.key,
|
|
123
|
+
definition: item.definition,
|
|
124
|
+
errorCount: sectionErrorCount
|
|
125
|
+
}, item.definition.fields.map(fieldDef => /*#__PURE__*/React.createElement(FieldBridge, {
|
|
126
|
+
key: fieldDef.fieldKey,
|
|
127
|
+
fieldDef: fieldDef,
|
|
128
|
+
control: control,
|
|
129
|
+
onExternalChange: handleFieldChange
|
|
130
|
+
})));
|
|
131
|
+
})), /*#__PURE__*/React.createElement("div", {
|
|
113
132
|
className: styles.osdkFormFooter
|
|
114
133
|
}, /*#__PURE__*/React.createElement(ErrorIndicator, {
|
|
115
134
|
errorEntries: errorEntries
|
|
@@ -118,9 +137,31 @@ export const BaseForm = /*#__PURE__*/memo(function ({
|
|
|
118
137
|
}, /*#__PURE__*/React.createElement(SubmitButton, {
|
|
119
138
|
isPending: isPending || isSubmitting,
|
|
120
139
|
isSubmitDisabled: isSubmitDisabled || hasAttemptedSubmit && areErrorsPresent,
|
|
121
|
-
errorMessage: buttonErrorMessage
|
|
140
|
+
errorMessage: buttonErrorMessage,
|
|
141
|
+
buttonText: submitButtonText,
|
|
142
|
+
buttonVariant: submitButtonVariant
|
|
122
143
|
}))));
|
|
123
144
|
});
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Extracts all RendererFieldDefinitions from formContent, flattening
|
|
148
|
+
* section fields into a single array. RHF sees a flat field namespace
|
|
149
|
+
* regardless of visual grouping, so this is used to build default values
|
|
150
|
+
* and the field-key-to-label map for error display.
|
|
151
|
+
*/
|
|
152
|
+
function flattenFieldDefinitions(formContent) {
|
|
153
|
+
const result = [];
|
|
154
|
+
for (const item of formContent) {
|
|
155
|
+
if (item.type === "field") {
|
|
156
|
+
result.push(item.definition);
|
|
157
|
+
} else {
|
|
158
|
+
for (const fieldDef of item.definition.fields) {
|
|
159
|
+
result.push(fieldDef);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
124
165
|
const SKELETON_FIELD_COUNT = 3;
|
|
125
166
|
|
|
126
167
|
// Mimics the label + input layout of real form fields.
|
|
@@ -147,12 +188,14 @@ function buildDefaultValues(fieldDefinitions) {
|
|
|
147
188
|
const SubmitButton = /*#__PURE__*/memo(function ({
|
|
148
189
|
isPending,
|
|
149
190
|
isSubmitDisabled,
|
|
150
|
-
errorMessage
|
|
191
|
+
errorMessage,
|
|
192
|
+
buttonText,
|
|
193
|
+
buttonVariant
|
|
151
194
|
}) {
|
|
152
|
-
const buttonLabel = isPending ? "Submitting\u2026" :
|
|
195
|
+
const buttonLabel = isPending ? "Submitting\u2026" : buttonText;
|
|
153
196
|
const button = /*#__PURE__*/React.createElement(ActionButton, {
|
|
154
197
|
type: "submit",
|
|
155
|
-
variant:
|
|
198
|
+
variant: buttonVariant,
|
|
156
199
|
disabled: isSubmitDisabled || isPending
|
|
157
200
|
}, buttonLabel);
|
|
158
201
|
if (errorMessage == null) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseForm.js","names":["Error","ErrorIcon","classNames","React","memo","useCallback","useMemo","useState","useForm","ActionButton","SkeletonBar","Tooltip","useAsyncAction","styles","FieldBridge","FormHeader","BaseForm","formTitle","fieldDefinitions","formState","controlledFormState","onFieldValueChange","onSubmit","isSubmitDisabled","isPending","isLoading","className","defaultValues","buildDefaultValues","control","trigger","getValues","errors","mode","values","hasAttemptedSubmit","setHasAttemptedSubmit","isSubmitting","error","submissionError","execute","executeSubmit","clearError","submissionErrorMessage","message","undefined","handleFormSubmit","e","preventDefault","isValid","handleFieldChange","fieldKey","value","labelByFieldKey","Map","map","d","label","errorEntries","Object","entries","key","entry","get","areErrorsPresent","length","buttonErrorMessage","createElement","osdkForm","title","role","osdkFormFields","FORM_SKELETON","fieldDef","onExternalChange","osdkFormFooter","ErrorIndicator","osdkFormSubmitButton","SubmitButton","errorMessage","SKELETON_FIELD_COUNT","Array","from","_","i","osdkFormSkeletonField","osdkFormSkeletonLabel","osdkFormSkeletonInput","def","props","fieldComponentProps","defaultValue","buttonLabel","button","type","variant","disabled","Root","defaultOpen","Trigger","render","osdkTooltipTriggerWrapper","Portal","Positioner","Popup","Arrow","count","osdkFormErrorIndicator","size","osdkFormErrorList"],"sources":["BaseForm.tsx"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Error as ErrorIcon } from \"@blueprintjs/icons\";\nimport classNames from \"classnames\";\nimport React, { memo, useCallback, useMemo, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { ActionButton } from \"../base-components/action-button/ActionButton.js\";\nimport { SkeletonBar } from \"../base-components/skeleton/SkeletonBar.js\";\nimport { Tooltip } from \"../base-components/tooltip/Tooltip.js\";\nimport { useAsyncAction } from \"../shared/hooks/useAsyncAction.js\";\nimport type { BaseFormProps } from \"./ActionFormApi.js\";\nimport styles from \"./BaseForm.module.css\";\nimport { FieldBridge } from \"./fields/FieldBridge.js\";\nimport type { RendererFieldDefinition } from \"./FormFieldApi.js\";\nimport { FormHeader } from \"./FormHeader.js\";\n\nexport const BaseForm: React.FC<BaseFormProps> = memo(function BaseFormFn({\n formTitle,\n fieldDefinitions,\n formState: controlledFormState,\n onFieldValueChange,\n onSubmit,\n isSubmitDisabled = false,\n isPending = false,\n isLoading = false,\n className,\n}: BaseFormProps): React.ReactElement {\n const isControlled = controlledFormState != null;\n\n const defaultValues = useMemo(\n () => buildDefaultValues(fieldDefinitions),\n [fieldDefinitions],\n );\n\n const {\n control,\n trigger,\n getValues,\n formState: { errors },\n } = useForm<Record<string, unknown>>({\n // Validate on blur first, then revalidate on change after the first\n // error. This gives the user a chance to finish typing before seeing\n // errors, while staying responsive once an error is surfaced.\n mode: \"onTouched\",\n ...(isControlled ? { values: controlledFormState } : { defaultValues }),\n });\n\n const [hasAttemptedSubmit, setHasAttemptedSubmit] = useState(false);\n\n const {\n isPending: isSubmitting,\n error: submissionError,\n execute: executeSubmit,\n clearError,\n } = useAsyncAction(onSubmit);\n const submissionErrorMessage = submissionError != null\n ? submissionError instanceof Error\n ? submissionError.message\n // TODO: provide better error message\n : \"Submission failed\"\n : undefined;\n\n const handleFormSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n setHasAttemptedSubmit(true);\n\n const isValid = await trigger();\n if (!isValid) {\n return;\n }\n\n // In controlled mode, always submit the controlled state, not RHF's\n // internal state. Between a user keystroke and the parent re-rendering,\n // RHF's store may hold the user-typed value rather than the parent's\n // value. Using controlledFormState directly preserves the existing\n // guarantee that controlled mode submits the parent's state.\n await executeSubmit(controlledFormState ?? getValues());\n },\n [trigger, executeSubmit, controlledFormState, getValues],\n );\n\n const handleFieldChange = useCallback(\n (fieldKey: string, value: unknown) => {\n clearError();\n onFieldValueChange?.(fieldKey, value);\n },\n [clearError, onFieldValueChange],\n );\n\n const isFormPending = isPending || isSubmitting;\n\n const labelByFieldKey = useMemo(\n () => new Map(fieldDefinitions.map((d) => [d.fieldKey, d.label])),\n [fieldDefinitions],\n );\n\n // RHF reuses the same errors object reference across renders so we cannot memoize errorEntries\n const errorEntries = Object.entries(errors).map(([key, entry]) => ({\n label: labelByFieldKey.get(key) ?? key,\n message: entry?.message ?? \"Invalid\",\n }));\n const areErrorsPresent = errorEntries.length > 0;\n const buttonErrorMessage = areErrorsPresent\n ? \"Some fields are invalid\"\n : submissionErrorMessage;\n\n return (\n <form\n className={classNames(styles.osdkForm, className)}\n onSubmit={handleFormSubmit}\n >\n {formTitle != null && <FormHeader title={formTitle} />}\n {isLoading && fieldDefinitions.length === 0 && (\n <div\n role=\"status\"\n aria-label=\"Loading form fields\"\n className={styles.osdkFormFields}\n >\n {FORM_SKELETON}\n </div>\n )}\n <div className={styles.osdkFormFields}>\n {fieldDefinitions.map((fieldDef) => (\n <FieldBridge\n key={fieldDef.fieldKey}\n fieldDef={fieldDef}\n control={control}\n onExternalChange={handleFieldChange}\n />\n ))}\n </div>\n <div className={styles.osdkFormFooter}>\n <ErrorIndicator errorEntries={errorEntries} />\n <div className={styles.osdkFormSubmitButton}>\n <SubmitButton\n isPending={isFormPending}\n isSubmitDisabled={isSubmitDisabled\n || (hasAttemptedSubmit && areErrorsPresent)}\n errorMessage={buttonErrorMessage}\n />\n </div>\n </div>\n </form>\n );\n});\n\nconst SKELETON_FIELD_COUNT = 3;\n\n// Mimics the label + input layout of real form fields.\nconst FORM_SKELETON = Array.from(\n { length: SKELETON_FIELD_COUNT },\n (_, i) => (\n <div key={i} className={styles.osdkFormSkeletonField}>\n <SkeletonBar className={styles.osdkFormSkeletonLabel} />\n <SkeletonBar className={styles.osdkFormSkeletonInput} />\n </div>\n ),\n);\n\nfunction buildDefaultValues(\n fieldDefinitions: ReadonlyArray<RendererFieldDefinition>,\n): Record<string, unknown> {\n const values: Record<string, unknown> = {};\n for (const def of fieldDefinitions) {\n const props: Record<string, unknown> = def.fieldComponentProps;\n if (\"defaultValue\" in props) {\n values[def.fieldKey] = props.defaultValue;\n }\n }\n return values;\n}\n\ninterface ErrorEntry {\n label: string;\n message: string;\n}\n\ninterface SubmitButtonProps {\n isPending: boolean;\n isSubmitDisabled: boolean;\n errorMessage: string | undefined;\n}\n\nconst SubmitButton = memo(function SubmitButtonFn({\n isPending,\n isSubmitDisabled,\n errorMessage,\n}: SubmitButtonProps): React.ReactElement {\n const buttonLabel = isPending ? \"Submitting\\u2026\" : \"Submit\";\n const button = (\n <ActionButton\n type=\"submit\"\n variant=\"primary\"\n disabled={isSubmitDisabled || isPending}\n >\n {buttonLabel}\n </ActionButton>\n );\n\n if (errorMessage == null) {\n return button;\n }\n\n return (\n <Tooltip.Root defaultOpen={true}>\n <Tooltip.Trigger\n render={<span className={styles.osdkTooltipTriggerWrapper} />}\n >\n {button}\n </Tooltip.Trigger>\n <Tooltip.Portal>\n <Tooltip.Positioner>\n <Tooltip.Popup>\n <Tooltip.Arrow />\n {errorMessage}\n </Tooltip.Popup>\n </Tooltip.Positioner>\n </Tooltip.Portal>\n </Tooltip.Root>\n );\n});\n\ninterface ErrorIndicatorProps {\n errorEntries: ReadonlyArray<ErrorEntry>;\n}\n\n// memo omitted: errorEntries is always a new array (RHF reuses the same errors ref)\nfunction ErrorIndicator({\n errorEntries,\n}: ErrorIndicatorProps): React.ReactElement | null {\n if (errorEntries.length === 0) {\n return null;\n }\n\n const count = errorEntries.length;\n\n return (\n <Tooltip.Root>\n <Tooltip.Trigger>\n <span className={styles.osdkFormErrorIndicator}>\n <ErrorIcon size={14} />\n {count === 1 ? \"1 issue\" : `${count} issues`}\n </span>\n </Tooltip.Trigger>\n <Tooltip.Portal>\n <Tooltip.Positioner>\n <Tooltip.Popup>\n <Tooltip.Arrow />\n <ul className={styles.osdkFormErrorList}>\n {errorEntries.map((entry) => (\n <li key={entry.label}>\n <strong>{entry.label}:</strong> {entry.message}\n </li>\n ))}\n </ul>\n </Tooltip.Popup>\n </Tooltip.Positioner>\n </Tooltip.Portal>\n </Tooltip.Root>\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"BaseForm.js","names":["Error","ErrorIcon","classNames","React","memo","useCallback","useMemo","useState","useForm","ActionButton","SkeletonBar","Tooltip","useAsyncAction","styles","FieldBridge","FormHeader","FormSection","BaseForm","formTitle","formContent","formState","controlledFormState","onFieldValueChange","onSubmit","isSubmitDisabled","isPending","isLoading","className","submitButtonText","submitButtonVariant","allFieldDefinitions","flattenFieldDefinitions","defaultValues","buildDefaultValues","control","trigger","getValues","errors","mode","values","hasAttemptedSubmit","setHasAttemptedSubmit","isSubmitting","error","submissionError","execute","executeSubmit","clearError","submissionErrorMessage","message","undefined","handleFormSubmit","e","preventDefault","isValid","handleFieldChange","fieldKey","value","labelByFieldKey","Map","map","d","label","errorEntries","Object","entries","key","entry","get","areErrorsPresent","length","buttonErrorMessage","createElement","osdkForm","title","role","osdkFormFields","FORM_SKELETON","item","type","definition","fieldDef","onExternalChange","sectionErrorCount","fields","reduce","count","field","errorCount","osdkFormFooter","ErrorIndicator","osdkFormSubmitButton","SubmitButton","errorMessage","buttonText","buttonVariant","result","push","SKELETON_FIELD_COUNT","Array","from","_","i","osdkFormSkeletonField","osdkFormSkeletonLabel","osdkFormSkeletonInput","fieldDefinitions","def","props","fieldComponentProps","defaultValue","buttonLabel","button","variant","disabled","Root","defaultOpen","Trigger","render","osdkTooltipTriggerWrapper","Portal","Positioner","Popup","Arrow","osdkFormErrorIndicator","size","osdkFormErrorList"],"sources":["BaseForm.tsx"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Error as ErrorIcon } from \"@blueprintjs/icons\";\nimport classNames from \"classnames\";\nimport React, { memo, useCallback, useMemo, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { ActionButton } from \"../base-components/action-button/ActionButton.js\";\nimport { SkeletonBar } from \"../base-components/skeleton/SkeletonBar.js\";\nimport { Tooltip } from \"../base-components/tooltip/Tooltip.js\";\nimport { useAsyncAction } from \"../shared/hooks/useAsyncAction.js\";\nimport type { BaseFormProps, FormContentItem } from \"./ActionFormApi.js\";\nimport styles from \"./BaseForm.module.css\";\nimport { FieldBridge } from \"./fields/FieldBridge.js\";\nimport type { RendererFieldDefinition } from \"./FormFieldApi.js\";\nimport { FormHeader } from \"./FormHeader.js\";\nimport { FormSection } from \"./FormSection.js\";\n\nexport const BaseForm: React.FC<BaseFormProps> = memo(function BaseFormFn({\n formTitle,\n formContent,\n formState: controlledFormState,\n onFieldValueChange,\n onSubmit,\n isSubmitDisabled = false,\n isPending = false,\n isLoading = false,\n className,\n submitButtonText = \"Submit\",\n submitButtonVariant = \"primary\",\n}: BaseFormProps): React.ReactElement {\n const isControlled = controlledFormState != null;\n\n const allFieldDefinitions = useMemo(\n () => flattenFieldDefinitions(formContent),\n [formContent],\n );\n\n const defaultValues = useMemo(\n () => buildDefaultValues(allFieldDefinitions),\n [allFieldDefinitions],\n );\n\n const {\n control,\n trigger,\n getValues,\n formState: { errors },\n } = useForm<Record<string, unknown>>({\n // Validate on blur first, then revalidate on change after the first\n // error. This gives the user a chance to finish typing before seeing\n // errors, while staying responsive once an error is surfaced.\n mode: \"onTouched\",\n ...(isControlled ? { values: controlledFormState } : { defaultValues }),\n });\n\n const [hasAttemptedSubmit, setHasAttemptedSubmit] = useState(false);\n\n const {\n isPending: isSubmitting,\n error: submissionError,\n execute: executeSubmit,\n clearError,\n } = useAsyncAction(onSubmit);\n const submissionErrorMessage = submissionError != null\n ? submissionError instanceof Error\n ? submissionError.message\n // TODO: provide better error message\n : \"Submission failed\"\n : undefined;\n\n const handleFormSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n setHasAttemptedSubmit(true);\n\n const isValid = await trigger();\n if (!isValid) {\n return;\n }\n\n // In controlled mode, always submit the controlled state, not RHF's\n // internal state. Between a user keystroke and the parent re-rendering,\n // RHF's store may hold the user-typed value rather than the parent's\n // value. Using controlledFormState directly preserves the existing\n // guarantee that controlled mode submits the parent's state.\n await executeSubmit(controlledFormState ?? getValues());\n },\n [trigger, executeSubmit, controlledFormState, getValues],\n );\n\n const handleFieldChange = useCallback(\n (fieldKey: string, value: unknown) => {\n clearError();\n onFieldValueChange?.(fieldKey, value);\n },\n [clearError, onFieldValueChange],\n );\n\n const isFormPending = isPending || isSubmitting;\n\n const labelByFieldKey = useMemo(\n () => new Map(allFieldDefinitions.map((d) => [d.fieldKey, d.label])),\n [allFieldDefinitions],\n );\n\n // RHF reuses the same errors object reference across renders so we cannot memoize errorEntries\n const errorEntries = Object.entries(errors).map(([key, entry]) => ({\n label: labelByFieldKey.get(key) ?? key,\n message: entry?.message ?? \"Invalid\",\n }));\n const areErrorsPresent = errorEntries.length > 0;\n const buttonErrorMessage = areErrorsPresent\n ? \"Some fields are invalid\"\n : submissionErrorMessage;\n\n return (\n <form\n className={classNames(styles.osdkForm, className)}\n onSubmit={handleFormSubmit}\n >\n {formTitle != null && <FormHeader title={formTitle} />}\n {isLoading && allFieldDefinitions.length === 0 && (\n <div\n role=\"status\"\n aria-label=\"Loading form fields\"\n className={styles.osdkFormFields}\n >\n {FORM_SKELETON}\n </div>\n )}\n <div className={styles.osdkFormFields}>\n {formContent.map((item) => {\n if (item.type === \"field\") {\n return (\n <FieldBridge\n key={item.definition.fieldKey}\n fieldDef={item.definition}\n control={control}\n onExternalChange={handleFieldChange}\n />\n );\n }\n const sectionErrorCount = item.definition.fields.reduce(\n (count, field) => count + (errors[field.fieldKey] != null ? 1 : 0),\n 0,\n );\n return (\n <FormSection\n key={item.key}\n definition={item.definition}\n errorCount={sectionErrorCount}\n >\n {item.definition.fields.map((fieldDef) => (\n <FieldBridge\n key={fieldDef.fieldKey}\n fieldDef={fieldDef}\n control={control}\n onExternalChange={handleFieldChange}\n />\n ))}\n </FormSection>\n );\n })}\n </div>\n <div className={styles.osdkFormFooter}>\n <ErrorIndicator errorEntries={errorEntries} />\n <div className={styles.osdkFormSubmitButton}>\n <SubmitButton\n isPending={isFormPending}\n isSubmitDisabled={isSubmitDisabled\n || (hasAttemptedSubmit && areErrorsPresent)}\n errorMessage={buttonErrorMessage}\n buttonText={submitButtonText}\n buttonVariant={submitButtonVariant}\n />\n </div>\n </div>\n </form>\n );\n});\n\n/**\n * Extracts all RendererFieldDefinitions from formContent, flattening\n * section fields into a single array. RHF sees a flat field namespace\n * regardless of visual grouping, so this is used to build default values\n * and the field-key-to-label map for error display.\n */\nfunction flattenFieldDefinitions(\n formContent: ReadonlyArray<FormContentItem>,\n): ReadonlyArray<RendererFieldDefinition> {\n const result: RendererFieldDefinition[] = [];\n for (const item of formContent) {\n if (item.type === \"field\") {\n result.push(item.definition);\n } else {\n for (const fieldDef of item.definition.fields) {\n result.push(fieldDef);\n }\n }\n }\n return result;\n}\n\nconst SKELETON_FIELD_COUNT = 3;\n\n// Mimics the label + input layout of real form fields.\nconst FORM_SKELETON = Array.from(\n { length: SKELETON_FIELD_COUNT },\n (_, i) => (\n <div key={i} className={styles.osdkFormSkeletonField}>\n <SkeletonBar className={styles.osdkFormSkeletonLabel} />\n <SkeletonBar className={styles.osdkFormSkeletonInput} />\n </div>\n ),\n);\n\nfunction buildDefaultValues(\n fieldDefinitions: ReadonlyArray<RendererFieldDefinition>,\n): Record<string, unknown> {\n const values: Record<string, unknown> = {};\n for (const def of fieldDefinitions) {\n const props: Record<string, unknown> = def.fieldComponentProps;\n if (\"defaultValue\" in props) {\n values[def.fieldKey] = props.defaultValue;\n }\n }\n return values;\n}\n\ninterface ErrorEntry {\n label: string;\n message: string;\n}\n\ninterface SubmitButtonProps {\n isPending: boolean;\n isSubmitDisabled: boolean;\n errorMessage: string | undefined;\n buttonText: string;\n buttonVariant: \"primary\" | \"secondary\";\n}\n\nconst SubmitButton = memo(function SubmitButtonFn({\n isPending,\n isSubmitDisabled,\n errorMessage,\n buttonText,\n buttonVariant,\n}: SubmitButtonProps): React.ReactElement {\n const buttonLabel = isPending ? \"Submitting\\u2026\" : buttonText;\n const button = (\n <ActionButton\n type=\"submit\"\n variant={buttonVariant}\n disabled={isSubmitDisabled || isPending}\n >\n {buttonLabel}\n </ActionButton>\n );\n\n if (errorMessage == null) {\n return button;\n }\n\n return (\n <Tooltip.Root defaultOpen={true}>\n <Tooltip.Trigger\n render={<span className={styles.osdkTooltipTriggerWrapper} />}\n >\n {button}\n </Tooltip.Trigger>\n <Tooltip.Portal>\n <Tooltip.Positioner>\n <Tooltip.Popup>\n <Tooltip.Arrow />\n {errorMessage}\n </Tooltip.Popup>\n </Tooltip.Positioner>\n </Tooltip.Portal>\n </Tooltip.Root>\n );\n});\n\ninterface ErrorIndicatorProps {\n errorEntries: ReadonlyArray<ErrorEntry>;\n}\n\n// memo omitted: errorEntries is always a new array (RHF reuses the same errors ref)\nfunction ErrorIndicator({\n errorEntries,\n}: ErrorIndicatorProps): React.ReactElement | null {\n if (errorEntries.length === 0) {\n return null;\n }\n\n const count = errorEntries.length;\n\n return (\n <Tooltip.Root>\n <Tooltip.Trigger>\n <span className={styles.osdkFormErrorIndicator}>\n <ErrorIcon size={14} />\n {count === 1 ? \"1 issue\" : `${count} issues`}\n </span>\n </Tooltip.Trigger>\n <Tooltip.Portal>\n <Tooltip.Positioner>\n <Tooltip.Popup>\n <Tooltip.Arrow />\n <ul className={styles.osdkFormErrorList}>\n {errorEntries.map((entry) => (\n <li key={entry.label}>\n <strong>{entry.label}:</strong> {entry.message}\n </li>\n ))}\n </ul>\n </Tooltip.Popup>\n </Tooltip.Positioner>\n </Tooltip.Portal>\n </Tooltip.Root>\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { Collapsible } from "@base-ui/react/collapsible";
|
|
18
|
+
import { CaretDown } from "@blueprintjs/icons";
|
|
19
|
+
import classNames from "classnames";
|
|
20
|
+
import React, { memo } from "react";
|
|
21
|
+
import styles from "./FormSection.module.css.js";
|
|
22
|
+
export const FormSection = /*#__PURE__*/memo(function ({
|
|
23
|
+
definition,
|
|
24
|
+
errorCount,
|
|
25
|
+
children
|
|
26
|
+
}) {
|
|
27
|
+
const {
|
|
28
|
+
title,
|
|
29
|
+
description,
|
|
30
|
+
collapsedByDefault = false,
|
|
31
|
+
showTitleBar = true,
|
|
32
|
+
columnCount = 1,
|
|
33
|
+
style = "box"
|
|
34
|
+
} = definition;
|
|
35
|
+
if (style === "minimal") {
|
|
36
|
+
return /*#__PURE__*/React.createElement(MinimalSection, {
|
|
37
|
+
title: title,
|
|
38
|
+
description: description
|
|
39
|
+
}, children);
|
|
40
|
+
}
|
|
41
|
+
const contentClassName = classNames(columnCount === 2 ? styles.osdkFormSectionGrid : styles.osdkFormSectionContent, styles.osdkFormSectionDivider);
|
|
42
|
+
if (!showTitleBar) {
|
|
43
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
44
|
+
className: styles.osdkFormSectionBox
|
|
45
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
46
|
+
className: contentClassName
|
|
47
|
+
}, children));
|
|
48
|
+
}
|
|
49
|
+
return /*#__PURE__*/React.createElement(Collapsible.Root, {
|
|
50
|
+
// Inverted: Base UI uses "open" semantics, our API uses "collapsed" semantics
|
|
51
|
+
defaultOpen: !collapsedByDefault,
|
|
52
|
+
className: styles.osdkFormSectionBox
|
|
53
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
54
|
+
className: styles.osdkFormSectionHeader
|
|
55
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
56
|
+
className: styles.osdkFormSectionTitleArea
|
|
57
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
58
|
+
className: styles.osdkFormSectionTitle
|
|
59
|
+
}, title), description != null && /*#__PURE__*/React.createElement("span", {
|
|
60
|
+
className: styles.osdkFormSectionDescription
|
|
61
|
+
}, description)), /*#__PURE__*/React.createElement(Collapsible.Trigger, {
|
|
62
|
+
className: styles.osdkFormSectionTrigger,
|
|
63
|
+
"aria-label": title
|
|
64
|
+
}, errorCount > 0 && /*#__PURE__*/React.createElement("span", {
|
|
65
|
+
className: styles.osdkFormSectionErrorBadge,
|
|
66
|
+
role: "status"
|
|
67
|
+
}, errorCount === 1 ? "1 error" : `${errorCount} errors`), /*#__PURE__*/React.createElement("span", {
|
|
68
|
+
className: styles.osdkFormSectionChevron
|
|
69
|
+
}, /*#__PURE__*/React.createElement(CaretDown, {
|
|
70
|
+
size: 16
|
|
71
|
+
})))), /*#__PURE__*/React.createElement(Collapsible.Panel, {
|
|
72
|
+
keepMounted: true
|
|
73
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
74
|
+
className: contentClassName
|
|
75
|
+
}, children)));
|
|
76
|
+
});
|
|
77
|
+
const MinimalSection = /*#__PURE__*/memo(function ({
|
|
78
|
+
title,
|
|
79
|
+
description,
|
|
80
|
+
children
|
|
81
|
+
}) {
|
|
82
|
+
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
|
83
|
+
className: styles.osdkFormSectionMinimalHeader
|
|
84
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
85
|
+
className: styles.osdkFormSectionMinimalTitle
|
|
86
|
+
}, title), description != null && /*#__PURE__*/React.createElement("div", {
|
|
87
|
+
className: styles.osdkFormSectionMinimalDescription
|
|
88
|
+
}, description)), /*#__PURE__*/React.createElement("div", {
|
|
89
|
+
className: styles.osdkFormSectionMinimalContent
|
|
90
|
+
}, children));
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=FormSection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormSection.js","names":["Collapsible","CaretDown","classNames","React","memo","styles","FormSection","definition","errorCount","children","title","description","collapsedByDefault","showTitleBar","columnCount","style","createElement","MinimalSection","contentClassName","osdkFormSectionGrid","osdkFormSectionContent","osdkFormSectionDivider","className","osdkFormSectionBox","Root","defaultOpen","osdkFormSectionHeader","osdkFormSectionTitleArea","osdkFormSectionTitle","osdkFormSectionDescription","Trigger","osdkFormSectionTrigger","osdkFormSectionErrorBadge","role","osdkFormSectionChevron","size","Panel","keepMounted","osdkFormSectionMinimalHeader","osdkFormSectionMinimalTitle","osdkFormSectionMinimalDescription","osdkFormSectionMinimalContent"],"sources":["FormSection.tsx"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Collapsible } from \"@base-ui/react/collapsible\";\nimport { CaretDown } from \"@blueprintjs/icons\";\nimport classNames from \"classnames\";\nimport React, { memo } from \"react\";\nimport type { FormSectionDefinition } from \"./ActionFormApi.js\";\nimport styles from \"./FormSection.module.css\";\n\nexport interface FormSectionProps {\n definition: FormSectionDefinition;\n errorCount: number;\n children: React.ReactNode;\n}\n\nexport const FormSection: React.NamedExoticComponent<FormSectionProps> = memo(\n function FormSectionFn({\n definition,\n errorCount,\n children,\n }: FormSectionProps): React.ReactElement {\n const {\n title,\n description,\n collapsedByDefault = false,\n showTitleBar = true,\n columnCount = 1,\n style = \"box\",\n } = definition;\n\n const isMinimal = style === \"minimal\";\n\n if (isMinimal) {\n return (\n <MinimalSection title={title} description={description}>\n {children}\n </MinimalSection>\n );\n }\n\n const contentClassName = classNames(\n columnCount === 2\n ? styles.osdkFormSectionGrid\n : styles.osdkFormSectionContent,\n styles.osdkFormSectionDivider,\n );\n\n if (!showTitleBar) {\n return (\n <div className={styles.osdkFormSectionBox}>\n <div className={contentClassName}>{children}</div>\n </div>\n );\n }\n\n return (\n <Collapsible.Root\n // Inverted: Base UI uses \"open\" semantics, our API uses \"collapsed\" semantics\n defaultOpen={!collapsedByDefault}\n className={styles.osdkFormSectionBox}\n >\n <div className={styles.osdkFormSectionHeader}>\n <div className={styles.osdkFormSectionTitleArea}>\n <span className={styles.osdkFormSectionTitle}>{title}</span>\n {description != null && (\n <span className={styles.osdkFormSectionDescription}>\n {description}\n </span>\n )}\n </div>\n <Collapsible.Trigger\n className={styles.osdkFormSectionTrigger}\n aria-label={title}\n >\n {errorCount > 0 && (\n <span className={styles.osdkFormSectionErrorBadge} role=\"status\">\n {errorCount === 1 ? \"1 error\" : `${errorCount} errors`}\n </span>\n )}\n <span className={styles.osdkFormSectionChevron}>\n <CaretDown size={16} />\n </span>\n </Collapsible.Trigger>\n </div>\n {/* keepMounted: RHF needs fields in the DOM even when collapsed for validation */}\n <Collapsible.Panel keepMounted={true}>\n <div className={contentClassName}>{children}</div>\n </Collapsible.Panel>\n </Collapsible.Root>\n );\n },\n);\n\ninterface MinimalSectionProps {\n title: string;\n description: string | undefined;\n children: React.ReactNode;\n}\n\nconst MinimalSection = memo(function MinimalSectionFn({\n title,\n description,\n children,\n}: MinimalSectionProps): React.ReactElement {\n return (\n <div>\n <div className={styles.osdkFormSectionMinimalHeader}>\n <div className={styles.osdkFormSectionMinimalTitle}>{title}</div>\n {description != null && (\n <div className={styles.osdkFormSectionMinimalDescription}>\n {description}\n </div>\n )}\n </div>\n <div className={styles.osdkFormSectionMinimalContent}>{children}</div>\n </div>\n );\n});\n"],"mappings":"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","ignoreList":[]}
|