@osdk/react-components 0.10.1-main-64aa85f53477a30d1b51ed7c35f2cdae6f595256 → 0.11.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.
Files changed (110) hide show
  1. package/CHANGELOG.md +2 -9
  2. package/build/browser/action-form/ActionForm.js +4 -2
  3. package/build/browser/action-form/ActionForm.js.map +1 -1
  4. package/build/browser/action-form/ActionFormApi.js.map +1 -1
  5. package/build/browser/action-form/BaseForm.js +6 -3
  6. package/build/browser/action-form/BaseForm.js.map +1 -1
  7. package/build/browser/action-form/FormFieldApi.js.map +1 -1
  8. package/build/browser/action-form/fields/DatePickerCommon.module.css +10 -0
  9. package/build/browser/action-form/fields/DatePickerCommon.module.css.js +2 -0
  10. package/build/browser/action-form/fields/DateRangeInputField.js +52 -16
  11. package/build/browser/action-form/fields/DateRangeInputField.js.map +1 -1
  12. package/build/browser/action-form/fields/DatetimePickerField.js +14 -3
  13. package/build/browser/action-form/fields/DatetimePickerField.js.map +1 -1
  14. package/build/browser/action-form/fields/DropdownField.js +24 -6
  15. package/build/browser/action-form/fields/DropdownField.js.map +1 -1
  16. package/build/browser/action-form/fields/DropdownField.module.css +29 -0
  17. package/build/browser/action-form/fields/DropdownField.module.css.js +7 -0
  18. package/build/browser/action-form/fields/FieldBridge.js +4 -2
  19. package/build/browser/action-form/fields/FieldBridge.js.map +1 -1
  20. package/build/browser/action-form/fields/FormFieldRenderer.js +21 -7
  21. package/build/browser/action-form/fields/FormFieldRenderer.js.map +1 -1
  22. package/build/browser/action-form/fields/ObjectSelectField.js +3 -1
  23. package/build/browser/action-form/fields/ObjectSelectField.js.map +1 -1
  24. package/build/browser/action-form/fields/PortalDismissLayer.js +37 -0
  25. package/build/browser/action-form/fields/PortalDismissLayer.js.map +1 -0
  26. package/build/browser/action-form/fields/PortalDismissLayer.module.css +28 -0
  27. package/build/browser/action-form/fields/PortalDismissLayer.module.css.js +6 -0
  28. package/build/browser/base-components/combobox/Combobox.js +1 -0
  29. package/build/browser/base-components/combobox/Combobox.js.map +1 -1
  30. package/build/browser/public/experimental/action-form.js.map +1 -1
  31. package/build/browser/styles.css +73 -0
  32. package/build/browser/util/UserAgent.js +1 -1
  33. package/build/browser/util/UserAgent.js.map +1 -1
  34. package/build/cjs/{chunk-AUGVJ3V7.cjs → chunk-45AHFC66.cjs} +4 -4
  35. package/build/cjs/{chunk-AUGVJ3V7.cjs.map → chunk-45AHFC66.cjs.map} +1 -1
  36. package/build/cjs/{chunk-YQQQ5C5D.cjs → chunk-6DM3IVYI.cjs} +111 -62
  37. package/build/cjs/chunk-6DM3IVYI.cjs.map +1 -0
  38. package/build/cjs/{chunk-XBXC6VAS.cjs → chunk-DX7LM5UP.cjs} +3 -2
  39. package/build/cjs/chunk-DX7LM5UP.cjs.map +1 -0
  40. package/build/cjs/{chunk-TRV7RMK3.cjs → chunk-UD5GEWHY.cjs} +47 -47
  41. package/build/cjs/{chunk-TRV7RMK3.cjs.map → chunk-UD5GEWHY.cjs.map} +1 -1
  42. package/build/cjs/{chunk-MVNWOKH2.cjs → chunk-V6OLMEB3.cjs} +162 -115
  43. package/build/cjs/chunk-V6OLMEB3.cjs.map +1 -0
  44. package/build/cjs/{chunk-QLAU3JVG.cjs → chunk-VVR27JI3.cjs} +3 -3
  45. package/build/cjs/chunk-VVR27JI3.cjs.map +1 -0
  46. package/build/cjs/{chunk-CUFSYOCB.cjs → chunk-X5SMPJYR.cjs} +9 -9
  47. package/build/cjs/{chunk-CUFSYOCB.cjs.map → chunk-X5SMPJYR.cjs.map} +1 -1
  48. package/build/cjs/public/experimental/action-form.cjs +6 -6
  49. package/build/cjs/public/experimental/action-form.css +25 -0
  50. package/build/cjs/public/experimental/action-form.css.map +1 -1
  51. package/build/cjs/public/experimental/action-form.d.cts +27 -2
  52. package/build/cjs/public/experimental/filter-list.cjs +7 -7
  53. package/build/cjs/public/experimental/object-table.cjs +10 -10
  54. package/build/cjs/public/experimental/object-table.css +25 -0
  55. package/build/cjs/public/experimental/object-table.css.map +1 -1
  56. package/build/cjs/public/experimental/pdf-viewer.cjs +24 -24
  57. package/build/cjs/public/experimental.cjs +41 -41
  58. package/build/cjs/public/experimental.css +25 -0
  59. package/build/cjs/public/experimental.css.map +1 -1
  60. package/build/cjs/public/experimental.d.cts +1 -1
  61. package/build/esm/action-form/ActionForm.js +4 -2
  62. package/build/esm/action-form/ActionForm.js.map +1 -1
  63. package/build/esm/action-form/ActionFormApi.js.map +1 -1
  64. package/build/esm/action-form/BaseForm.js +6 -3
  65. package/build/esm/action-form/BaseForm.js.map +1 -1
  66. package/build/esm/action-form/FormFieldApi.js.map +1 -1
  67. package/build/esm/action-form/fields/DatePickerCommon.module.css +10 -0
  68. package/build/esm/action-form/fields/DateRangeInputField.js +52 -16
  69. package/build/esm/action-form/fields/DateRangeInputField.js.map +1 -1
  70. package/build/esm/action-form/fields/DatetimePickerField.js +14 -3
  71. package/build/esm/action-form/fields/DatetimePickerField.js.map +1 -1
  72. package/build/esm/action-form/fields/DropdownField.js +24 -6
  73. package/build/esm/action-form/fields/DropdownField.js.map +1 -1
  74. package/build/esm/action-form/fields/DropdownField.module.css +29 -0
  75. package/build/esm/action-form/fields/FieldBridge.js +4 -2
  76. package/build/esm/action-form/fields/FieldBridge.js.map +1 -1
  77. package/build/esm/action-form/fields/FormFieldRenderer.js +21 -7
  78. package/build/esm/action-form/fields/FormFieldRenderer.js.map +1 -1
  79. package/build/esm/action-form/fields/ObjectSelectField.js +3 -1
  80. package/build/esm/action-form/fields/ObjectSelectField.js.map +1 -1
  81. package/build/esm/action-form/fields/PortalDismissLayer.js +37 -0
  82. package/build/esm/action-form/fields/PortalDismissLayer.js.map +1 -0
  83. package/build/esm/action-form/fields/PortalDismissLayer.module.css +28 -0
  84. package/build/esm/base-components/combobox/Combobox.js +1 -0
  85. package/build/esm/base-components/combobox/Combobox.js.map +1 -1
  86. package/build/esm/public/experimental/action-form.js.map +1 -1
  87. package/build/esm/util/UserAgent.js +1 -1
  88. package/build/esm/util/UserAgent.js.map +1 -1
  89. package/build/types/action-form/ActionFormApi.d.ts +8 -2
  90. package/build/types/action-form/ActionFormApi.d.ts.map +1 -1
  91. package/build/types/action-form/FormFieldApi.d.ts +19 -0
  92. package/build/types/action-form/FormFieldApi.d.ts.map +1 -1
  93. package/build/types/action-form/fields/DateRangeInputField.d.ts.map +1 -1
  94. package/build/types/action-form/fields/DatetimePickerField.d.ts.map +1 -1
  95. package/build/types/action-form/fields/DropdownField.d.ts.map +1 -1
  96. package/build/types/action-form/fields/FieldBridge.d.ts +2 -1
  97. package/build/types/action-form/fields/FieldBridge.d.ts.map +1 -1
  98. package/build/types/action-form/fields/FormFieldRenderer.d.ts +2 -1
  99. package/build/types/action-form/fields/FormFieldRenderer.d.ts.map +1 -1
  100. package/build/types/action-form/fields/PortalDismissLayer.d.ts +7 -0
  101. package/build/types/action-form/fields/PortalDismissLayer.d.ts.map +1 -0
  102. package/build/types/base-components/combobox/Combobox.d.ts +1 -0
  103. package/build/types/base-components/combobox/Combobox.d.ts.map +1 -1
  104. package/build/types/public/experimental/action-form.d.ts +1 -1
  105. package/build/types/public/experimental/action-form.d.ts.map +1 -1
  106. package/package.json +7 -7
  107. package/build/cjs/chunk-MVNWOKH2.cjs.map +0 -1
  108. package/build/cjs/chunk-QLAU3JVG.cjs.map +0 -1
  109. package/build/cjs/chunk-XBXC6VAS.cjs.map +0 -1
  110. package/build/cjs/chunk-YQQQ5C5D.cjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @osdk/react-components
2
2
 
3
- ## 0.10.1-main-64aa85f53477a30d1b51ed7c35f2cdae6f595256
3
+ ## 0.11.0
4
4
 
5
- ### Patch Changes
5
+ ### Minor Changes
6
6
 
7
7
  - 82c7210: update @osdk/react peer dependency range to ^2.8.0 to track its alignment with the @osdk/client fixed group
8
8
  - 203331e: GA: promote modern hooks from `@osdk/react/experimental` to the main entry, rename `@osdk/react/experimental/admin` → `@osdk/react/platform-apis`, consolidate to a single `OsdkProvider`. Promote `ObservableClient` and supporting types out of `@osdk/client/unstable-do-not-use` to a new stable `@osdk/client/observable` entry so the GA hooks no longer depend on a "do not use" entry point. The previous import paths and symbol names are kept as `@deprecated` shims so 0.x consumers can upgrade without code changes.
@@ -54,13 +54,6 @@
54
54
  - `import { createObservableClient, ObservableClient, ... } from "@osdk/client/unstable-do-not-use"` → `import { ... } from "@osdk/client/observable"`
55
55
  - the symbols moved: `createObservableClient`, `ObservableClient`, `CacheEntry`, `CacheSnapshot`, `CanonicalizedOptions`, `CanonicalizeOptionsInput`, `Observer`, `ObserveLinks`, `ObserveAggregationArgs`, `ObserveFunctionCallbackArgs`, `ObserveFunctionOptions`, `ObserveObjectCallbackArgs`, `ObserveObjectsCallbackArgs`, `ObserveObjectSetArgs`, `Unsubscribable`, `ActionSignatureFromDef`, `QueryParameterType`, `QueryReturnType`
56
56
 
57
- - Updated dependencies [3889ff8]
58
- - Updated dependencies [7a92156]
59
- - Updated dependencies [203331e]
60
- - @osdk/react@2.15.0-main-64aa85f53477a30d1b51ed7c35f2cdae6f595256
61
- - @osdk/client@2.15.0-main-64aa85f53477a30d1b51ed7c35f2cdae6f595256
62
- - @osdk/api@2.15.0-main-64aa85f53477a30d1b51ed7c35f2cdae6f595256
63
-
64
57
  ## 0.10.0
65
58
 
66
59
  ### Minor Changes
@@ -33,7 +33,8 @@ export const ActionForm = typedReactMemo(function ({
33
33
  onSubmit,
34
34
  onValidationResponse: _onValidationResponse,
35
35
  onSuccess,
36
- onError
36
+ onError,
37
+ portalContainer
37
38
  }) {
38
39
  const {
39
40
  applyAction: osdkApplyAction,
@@ -109,7 +110,8 @@ export const ActionForm = typedReactMemo(function ({
109
110
  isSubmitDisabled,
110
111
  isPending,
111
112
  isLoading: metadataLoading,
112
- onFieldValueChange: handleFieldValueChange
113
+ onFieldValueChange: handleFieldValueChange,
114
+ portalContainer
113
115
  };
114
116
  if (!(controlledFormState != null)) {
115
117
  return /*#__PURE__*/React.createElement(BaseForm, commonProps);
@@ -1 +1 @@
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
+ {"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","portalContainer","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 portalContainer,\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 portalContainer,\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 * 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":[]}
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 PortalContainer,\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\n Pick<\n BaseFormProps,\n \"formTitle\" | \"isSubmitDisabled\" | \"portalContainer\"\n >\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 * Element that receives popover/dropdown portals for fields rendered by this\n * form. Use this when rendering inside modal dialogs so popups stay in the\n * dialog's stacking and focus context.\n */\n portalContainer?: PortalContainer;\n}\n"],"mappings":"","ignoreList":[]}
@@ -37,7 +37,8 @@ export const BaseForm = /*#__PURE__*/memo(function ({
37
37
  isLoading = false,
38
38
  className,
39
39
  submitButtonText = "Submit",
40
- submitButtonVariant = "primary"
40
+ submitButtonVariant = "primary",
41
+ portalContainer
41
42
  }) {
42
43
  const allFieldDefinitions = useMemo(() => flattenFieldDefinitions(formContent), [formContent]);
43
44
  const defaultValues = useMemo(() => buildDefaultValues(allFieldDefinitions), [allFieldDefinitions]);
@@ -114,7 +115,8 @@ export const BaseForm = /*#__PURE__*/memo(function ({
114
115
  key: item.definition.fieldKey,
115
116
  fieldDef: item.definition,
116
117
  control: control,
117
- onExternalChange: handleFieldChange
118
+ onExternalChange: handleFieldChange,
119
+ portalContainer: portalContainer
118
120
  });
119
121
  }
120
122
  const sectionErrorCount = item.definition.fields.reduce((count, field) => count + (errors[field.fieldKey] != null ? 1 : 0), 0);
@@ -126,7 +128,8 @@ export const BaseForm = /*#__PURE__*/memo(function ({
126
128
  key: fieldDef.fieldKey,
127
129
  fieldDef: fieldDef,
128
130
  control: control,
129
- onExternalChange: handleFieldChange
131
+ onExternalChange: handleFieldChange,
132
+ portalContainer: portalContainer
130
133
  })));
131
134
  })), /*#__PURE__*/React.createElement("div", {
132
135
  className: styles.osdkFormFooter
@@ -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","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":[]}
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","portalContainer","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 portalContainer,\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 portalContainer={portalContainer}\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 portalContainer={portalContainer}\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":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"FormFieldApi.js","names":["EMPTY_RANGE"],"sources":["FormFieldApi.ts"],"sourcesContent":["/*\n * Copyright 2026 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 ActionMetadata,\n ActionParam,\n CompileTimeMetadata,\n DataValueClientToWire,\n ObjectSet,\n ObjectTypeDefinition,\n Osdk,\n} from \"@osdk/api\";\nimport type React from \"react\";\n\n/**\n * A form field definition specifies configuration for a single field\n */\nexport interface FormFieldDefinition<\n Q extends ActionDefinition<unknown>,\n K extends FieldKey<Q> = FieldKey<Q>,\n> {\n /**\n * The field's unique key\n */\n fieldKey: K;\n\n /**\n * Display label for the field\n */\n label: string;\n\n /**\n * Default value of the field\n */\n defaultValue?: FieldValueType<Q, K>;\n\n /**\n * The form field component type to render\n */\n fieldComponent: ValidFormFieldForPropertyType<FieldDescriptorType<Q, K>>;\n\n /**\n * Whether the field is required\n */\n isRequired?: boolean;\n\n /**\n * Placeholder text\n */\n placeholder?: string;\n\n /**\n * Additional information to display on this field.\n * Accepts plain text or rich content (e.g. JSX with links or formatting).\n * Rendered as a tooltip icon next to the label by default, or below the\n * label when helperTextPlacement is \"bottom\".\n */\n helperText?: React.ReactNode;\n\n /**\n * The placement of the helper text either below the field or in a tooltip\n *\n * @default \"tooltip\"\n */\n helperTextPlacement?: \"bottom\" | \"tooltip\";\n\n /**\n * Whether the field is disabled\n */\n isDisabled?: boolean;\n\n /**\n * A callback to customize error messages when a built-in validation rule fails.\n * Receives a discriminated union with the constraint data (e.g., the min value\n * that was exceeded) so the message can reference the threshold.\n *\n * Return a string to override the default message, or `undefined` to keep it.\n */\n onValidationError?: (error: ValidationError) => string | undefined;\n\n /**\n * Additional function to validate the field.\n *\n * Return `undefined` if valid, or an error message string if invalid.\n */\n validate?: (value: FieldValueType<Q, K>) => Promise<string | undefined>;\n\n /**\n * The component props for the form field.\n * Excludes runtime props (value, onChange) which are managed by ActionForm.\n */\n fieldComponentProps: Omit<\n FormFieldPropsByType[\n ValidFormFieldForPropertyType<\n FieldDescriptorType<Q, K>\n >\n ],\n FormManagedProps<\n ValidFormFieldForPropertyType<FieldDescriptorType<Q, K>>\n >\n >;\n}\n\n/**\n * A discriminated union describing which validation rule failed and the\n * constraint data the user needs to build a meaningful error message.\n */\nexport type ValidationError =\n | { type: \"required\" }\n | { type: \"min\"; min: number | Date }\n | { type: \"max\"; max: number | Date }\n | { type: \"minLength\"; minLength: number }\n | { type: \"maxLength\"; maxLength: number }\n | { type: \"maxSize\"; maxSize: number }\n | { type: \"validate\"; message: string };\n\n/**\n * Maps field types to their corresponding props\n */\nexport interface FormFieldPropsByType {\n DATE_RANGE_INPUT: DateRangeInputFieldProps;\n DATETIME_PICKER: DatetimePickerFieldProps;\n DROPDOWN: DropdownFieldProps<unknown, boolean>;\n FILE_PICKER: FilePickerProps;\n NUMBER_INPUT: NumberInputFieldProps;\n OBJECT_SELECT: ObjectSelectFieldProps<ObjectTypeDefinition>;\n OBJECT_SET: ObjectSetFieldProps<ObjectTypeDefinition>;\n RADIO_BUTTONS: RadioButtonsFieldProps<unknown>;\n TEXT_AREA: TextAreaFieldProps;\n TEXT_INPUT: TextInputFieldProps;\n CUSTOM: CustomFieldProps<unknown>;\n}\n\n/**\n * Datetime picker field props.\n *\n * When `formatDate` is omitted, ISO-like format is used (YYYY-MM-DD / YYYY-MM-DD HH:mm).\n */\nexport interface DatetimePickerFieldProps extends BaseFormFieldProps<Date> {\n /**\n * The earliest date the user can select.\n * If provided, this will be added to the field validation.\n */\n min?: Date;\n\n /**\n * The latest date the user can select.\n * If provided, this will be added to the field validation.\n */\n max?: Date;\n\n /**\n * Whether to show time picker.\n */\n showTime?: boolean;\n\n /**\n * Whether to close the popover after selecting a date.\n * @default true when `showTime` is false, false when `showTime` is true\n */\n closeOnSelection?: boolean;\n\n /**\n * Placeholder text shown when no value is selected.\n */\n placeholder?: string;\n\n /**\n * Formats a Date for display in the input field when not editing.\n * When typing, the input shows the parsable format (YYYY-MM-DD or YYYY-MM-DD HH:mm).\n * Provide a matching `parseDate` if using a custom format.\n */\n formatDate?: (date: Date) => string;\n\n /**\n * Parses a user-typed string back into a Date.\n * Must be the inverse of `formatDate` — if `formatDate(d)` produces string `s`,\n * then `parseDate(s)` must return an equivalent Date.\n * When omitted, defaults to parsing \"YYYY-MM-DD\" (date-only) or \"YYYY-MM-DD HH:mm\" (with time).\n */\n parseDate?: (text: string) => Date | undefined;\n\n /**\n * Ref forwarded to the portal container element.\n * Used to track portaled content for click-outside detection.\n */\n portalRef?: React.Ref<HTMLDivElement>;\n}\n\n/**\n * A date range represented as a start/end tuple.\n * Either element may be `null` when the range is partially selected.\n */\nexport type DateRange = readonly [Date | null, Date | null];\n\n/** Default empty range — both bounds are null. */\nexport const EMPTY_RANGE: DateRange = [null, null];\n\n/**\n * Date range input field props.\n *\n * Renders two text inputs (start / end) with a shared calendar popover\n * that supports range selection.\n */\nexport interface DateRangeInputFieldProps\n extends BaseFormFieldProps<DateRange>\n{\n /** The earliest selectable date. */\n min?: Date;\n\n /** The latest selectable date. */\n max?: Date;\n\n /** Whether to show time pickers for both dates. */\n showTime?: boolean;\n\n /** Placeholder text for the start date input. */\n placeholderStart?: string;\n\n /** Placeholder text for the end date input. */\n placeholderEnd?: string;\n\n /** Whether to allow start and end on the same day. @default true */\n allowSingleDayRange?: boolean;\n\n /** Formats a Date for display. Defaults to \"YYYY-MM-DD\". */\n formatDate?: (date: Date) => string;\n\n /** Parses a user-typed string back into a Date. */\n parseDate?: (text: string) => Date | undefined;\n}\n\n/**\n * Dropdown field props with selectable items\n */\nexport interface DropdownFieldProps<V, Multiple extends boolean = false>\n extends BaseFormFieldProps<Multiple extends true ? V[] : V>\n{\n /**\n * Available items for the dropdown\n */\n items: V[];\n\n /**\n * Converts an item to a display string. Defaults to `String()`.\n */\n itemToStringLabel?: (item: V) => string;\n\n /**\n * Returns a unique string key for a list item. Used as the React `key`.\n * Falls back to the item's index when not provided.\n */\n itemToKey?: (item: V) => string;\n\n /**\n * Custom equality check for item values. Defaults to `Object.is`.\n * Required when items are objects to ensure correct selection matching.\n */\n isItemEqual?: (a: V, b: V) => boolean;\n\n /**\n * Whether the dropdown allows searching/filtering.\n * When true, renders a Combobox with a search input.\n * When false (default), renders a Select dropdown.\n */\n isSearchable?: boolean;\n\n /**\n * Placeholder text shown when no value is selected.\n */\n placeholder?: string;\n\n /**\n * Whether multiple values can be selected\n */\n isMultiple?: Multiple;\n\n /**\n * Ref forwarded to the portal container element.\n * Used to track portaled content for click-outside detection.\n */\n portalRef?: React.Ref<HTMLDivElement>;\n\n /**\n * Controlled search input value. Must be provided together with `onQueryChange`.\n */\n query?: string;\n\n /**\n * Callback when the search input value changes.\n * Can be used standalone as an event listener or together with `query`\n * for fully controlled search state.\n */\n onQueryChange?: (query: string) => void;\n\n /**\n * When true, disables the combobox's built-in client-side filtering.\n * Use when items are already filtered server-side (e.g. via `onQueryChange`).\n *\n * @default false\n */\n disableClientSideFiltering?: boolean;\n\n /**\n * Status message rendered below the search input and above the item list\n * inside the popup. Use for loading/error/empty messages.\n */\n popupStatus?: React.ReactNode;\n\n /**\n * A React node to render after the item list.\n * Use for infinite scroll sentinels, \"load more\" buttons, etc.\n */\n trailingItem?: React.ReactNode;\n}\n\nexport interface FilePickerProps extends BaseFormFieldProps<File | File[]> {\n /**\n * Whether multiple files can be selected\n */\n isMulti?: boolean;\n\n /**\n * Accepted file types (e.g., \"image/*\", \".pdf\")\n */\n accept?: string | string[];\n\n /**\n * Maximum file size in bytes\n */\n maxSize?: number;\n\n /**\n * The text displayed when no file is selected.\n *\n * @default \"No file chosen\"\n */\n text?: string;\n\n /**\n * The text displayed on the browse button.\n *\n * @default \"Browse\"\n */\n buttonText?: string;\n}\n\n/**\n * Text area field props\n */\nexport interface TextAreaFieldProps extends\n BaseFormFieldProps<string>,\n Pick<\n React.TextareaHTMLAttributes<HTMLTextAreaElement>,\n | \"rows\"\n | \"wrap\"\n /**\n * If provided, this will be added to the field validation\n */\n | \"minLength\"\n /**\n * If provided, this will be added to the field validation\n */\n | \"maxLength\"\n >\n{\n placeholder?: string;\n}\n\nexport interface TextInputFieldProps extends\n BaseFormFieldProps<string>,\n Pick<\n React.InputHTMLAttributes<HTMLInputElement>,\n /**\n * If provided, this will be added to the field validation\n */\n | \"minLength\"\n /**\n * If provided, this will be added to the field validation\n */\n | \"maxLength\"\n >\n{\n placeholder?: string;\n}\n\n/**\n * Number input field props\n */\nexport interface NumberInputFieldProps extends BaseFormFieldProps<number> {\n /**\n * Minimum allowed value.\n */\n min?: number;\n\n /**\n * Maximum allowed value.\n */\n max?: number;\n\n /**\n * Step increment for the input. Used by the stepper buttons and ArrowUp/ArrowDown keyboard stepping.\n *\n * @default 1\n */\n step?: number;\n\n /**\n * Placeholder text shown when the input is empty.\n */\n placeholder?: string;\n}\n\n/**\n * Radio buttons field props\n */\nexport interface RadioButtonsFieldProps<V> extends BaseFormFieldProps<V> {\n /**\n * Available options for radio buttons.\n *\n * Values are compared by reference equality (`===`). When options contain\n * non-primitive values, pass the same object references for `value` and\n * the corresponding option entry.\n */\n options: Option<V>[];\n\n /**\n * Controls the layout direction of the radio buttons.\n *\n * - `\"vertical\"` (default): options are stacked in a column\n * - `\"horizontal\"`: options are laid out in a row, wrapping when needed\n */\n orientation?: \"horizontal\" | \"vertical\";\n}\n\n/**\n * Option interface for radio button options\n */\nexport interface Option<V> {\n label: string;\n value: V;\n}\n\n/**\n * Object set field displays the summary of the count of the given object set\n */\nexport interface ObjectSetFieldProps<T extends ObjectTypeDefinition>\n extends Pick<BaseFormFieldProps<ObjectSet<T>>, \"id\" | \"value\">\n{\n /**\n * Message displayed when no object set is provided.\n *\n * @default \"Object set is not defined\"\n */\n emptyMessage?: string;\n}\n\n/**\n * Object select field props for selecting object instances from the ontology.\n * Used for action parameters that accept a single object or multiple objects.\n *\n * Extends DropdownFieldProps with props that ObjectSelectField\n * manages internally (items, search, filtering) omitted from the public surface.\n */\nexport interface ObjectSelectFieldProps<\n Q extends ObjectTypeDefinition = ObjectTypeDefinition,\n> extends\n Omit<\n DropdownFieldProps<Osdk.Instance<Q>>,\n | \"items\"\n | \"itemToStringLabel\"\n | \"itemToKey\"\n | \"isItemEqual\"\n | \"isSearchable\"\n | \"query\"\n | \"onQueryChange\"\n | \"disableClientSideFiltering\"\n | \"renderItemList\"\n >\n{\n /**\n * The object type definition to search within.\n */\n objectType: Q;\n}\n\n/**\n * Custom field props for user-defined renderers\n */\nexport interface CustomFieldProps<V> extends BaseFormFieldProps<V> {\n /**\n * Custom renderer function\n */\n customRenderer: (props: BaseFormFieldProps<V>) => React.ReactNode;\n}\n\nexport interface BaseFormFieldProps<V> {\n /**\n * The HTML `id` attribute for the field input element.\n * Used for `<label htmlFor>` association.\n */\n id?: string;\n\n /**\n * The validation error message for this field, if any.\n * When set, the field should display a visual error state.\n */\n error?: string;\n\n /**\n * The value of the form field\n */\n value: V | null;\n\n /**\n * The default value of the form field.\n */\n defaultValue?: V;\n\n /**\n * Called when the field value changes.\n *\n * ActionForm internally wraps this to pass the key to `onFieldValueChange`:\n * ```\n * <DropdownField\n * {...fieldDef}\n * onChange={(value) => onFieldValueChange(fieldDef.key, value)}\n * />\n * ```\n *\n * @param value The new value of the form field\n */\n onChange?: (value: V | null) => void;\n}\n\nexport type FieldKey<Q extends ActionDefinition<unknown>> =\n keyof ActionParameters<Q>;\n\n/**\n * Extracts parameters from an ActionDefinition\n */\nexport type ActionParameters<Q extends ActionDefinition<unknown>> =\n CompileTimeMetadata<Q>[\"parameters\"];\n\n/**\n * Extracts the value type for a specific parameter\n *\n * TODO: Re-use `BaseType`\n */\nexport type FieldValueType<\n Q extends ActionDefinition<unknown>,\n K extends keyof ActionParameters<Q> = keyof ActionParameters<Q>,\n> = ActionParameters<Q>[K][\"type\"] extends\n ActionMetadata.DataType.Object<infer T> ? ActionParam.ObjectType<T>\n : ActionParameters<Q>[K][\"type\"] extends ActionMetadata.DataType.ObjectSet<\n infer T\n > ? ActionParam.ObjectSetType<T>\n : ActionParameters<Q>[K][\"type\"] extends ActionMetadata.DataType.Struct<\n infer T\n > ? ActionParam.StructType<T>\n : ActionParameters<Q>[K][\"type\"] extends keyof DataValueClientToWire\n ? DataValueClientToWire[ActionParameters<Q>[K][\"type\"]]\n : never;\n\n/**\n * Extracts the parameter type descriptor for a specific action parameter.\n */\nexport type FieldDescriptorType<\n Q extends ActionDefinition<unknown> = ActionDefinition<unknown>,\n K extends keyof ActionParameters<Q> = keyof ActionParameters<Q>,\n> = ActionParameters<Q>[K][\"type\"];\n\n/**\n * Available form field component types\n */\nexport type FieldComponent =\n | \"DATE_RANGE_INPUT\"\n | \"DATETIME_PICKER\"\n | \"DROPDOWN\"\n | \"FILE_PICKER\"\n | \"NUMBER_INPUT\"\n | \"OBJECT_SELECT\"\n | \"OBJECT_SET\"\n | \"RADIO_BUTTONS\"\n | \"TEXT_AREA\"\n | \"TEXT_INPUT\"\n | \"CUSTOM\";\n\n/**\n * Describes the data type of a form field, independent of OSDK.\n * Mirrors ActionMetadata.DataType to keep the rendering layer OSDK-agnostic.\n */\nexport type FieldType =\n | \"boolean\"\n | \"string\"\n | \"integer\"\n | \"long\"\n | \"double\"\n | \"datetime\"\n | \"timestamp\"\n | \"attachment\"\n | \"marking\"\n | \"mediaReference\"\n | \"objectType\"\n | \"geoshape\"\n | \"geohash\"\n | { type: \"object\"; object: string }\n | { type: \"objectSet\"; objectSet: string }\n | { type: \"interface\"; interface: string }\n | { type: \"struct\"; struct: Record<string, string> };\n\n/**\n * Props managed by form state infrastructure (FieldBridge / RHF).\n * Fields with onChange participate in form state → value and onChange are managed\n * externally. Read-only fields (no onChange, e.g. ObjectSetField) keep value in\n * fieldComponentProps so it bypasses form state cloning.\n */\ntype FormManagedProps<K extends FieldComponent> = \"onChange\" extends\n keyof FormFieldPropsByType[K] ? \"value\" | \"onChange\"\n : \"onChange\";\n\n/**\n * An OSDK-agnostic field definition used by BaseForm and FormFieldRenderer.\n * Contains only the information needed to render a single field — no generics,\n * no compile-time parameter constraints.\n *\n * Implemented as a distributed mapped type: switching on `fieldComponent`\n * narrows `fieldComponentProps` to the correct props type automatically.\n */\nexport type RendererFieldDefinition = {\n [K in FieldComponent]: {\n fieldKey: string;\n fieldComponent: K;\n fieldType?: FieldType;\n label: string;\n isRequired?: boolean;\n placeholder?: string;\n helperText?: React.ReactNode;\n helperTextPlacement?: \"bottom\" | \"tooltip\";\n validate?: (value: unknown) => Promise<string | undefined>;\n onValidationError?: (error: ValidationError) => string | undefined;\n fieldComponentProps: Omit<FormFieldPropsByType[K], FormManagedProps<K>>;\n };\n}[FieldComponent];\n\n/**\n * Gets valid form field types for a given property type\n */\nexport type ValidFormFieldForPropertyType<P extends FieldDescriptorType> =\n P extends { type: \"objectSet\" } ? \"OBJECT_SET\"\n : P extends { type: \"object\" } ? \"OBJECT_SELECT\"\n : P extends \"mediaReference\" | \"attachment\" ? \"FILE_PICKER\"\n : P extends \"boolean\" ? \"RADIO_BUTTONS\" | \"DROPDOWN\"\n : P extends \"string\" ? \"TEXT_INPUT\" | \"TEXT_AREA\"\n : P extends \"datetime\" | \"timestamp\" ? \"DATETIME_PICKER\"\n : P extends\n | \"double\"\n | \"integer\"\n | \"long\"\n | \"float\"\n | \"short\"\n | \"byte\"\n | \"decimal\" ? \"NUMBER_INPUT\"\n : never;\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAcA;AACA;AACA;;AAuFA;AACA;AACA;AACA;;AAUA;AACA;AACA;;AAeA;AACA;AACA;AACA;AACA;;AAoDA;AACA;AACA;AACA;;AAGA;AACA,OAAO,MAAMA,WAAsB,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC;;AAElD;AACA;AACA;AACA;AACA;AACA;;AA6BA;AACA;AACA;;AAiHA;AACA;AACA;;AAqCA;AACA;AACA;;AAyBA;AACA;AACA;;AAoBA;AACA;AACA;;AAMA;AACA;AACA;;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;;AAuBA;AACA;AACA;;AAkDA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACA;;AAgBA;AACA;AACA;;AAMA;AACA;AACA;;AAcA;AACA;AACA;AACA;;AAoBA;AACA;AACA;AACA;AACA;AACA;;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAiBA;AACA;AACA","ignoreList":[]}
1
+ {"version":3,"file":"FormFieldApi.js","names":["EMPTY_RANGE"],"sources":["FormFieldApi.ts"],"sourcesContent":["/*\n * Copyright 2026 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 ActionMetadata,\n ActionParam,\n CompileTimeMetadata,\n DataValueClientToWire,\n ObjectSet,\n ObjectTypeDefinition,\n Osdk,\n} from \"@osdk/api\";\nimport type React from \"react\";\n\nexport type PortalContainer =\n | HTMLElement\n | ShadowRoot\n | null\n | React.RefObject<HTMLElement | ShadowRoot | null>;\n\n/**\n * A form field definition specifies configuration for a single field\n */\nexport interface FormFieldDefinition<\n Q extends ActionDefinition<unknown>,\n K extends FieldKey<Q> = FieldKey<Q>,\n> {\n /**\n * The field's unique key\n */\n fieldKey: K;\n\n /**\n * Display label for the field\n */\n label: string;\n\n /**\n * Default value of the field\n */\n defaultValue?: FieldValueType<Q, K>;\n\n /**\n * The form field component type to render\n */\n fieldComponent: ValidFormFieldForPropertyType<FieldDescriptorType<Q, K>>;\n\n /**\n * Whether the field is required\n */\n isRequired?: boolean;\n\n /**\n * Placeholder text\n */\n placeholder?: string;\n\n /**\n * Additional information to display on this field.\n * Accepts plain text or rich content (e.g. JSX with links or formatting).\n * Rendered as a tooltip icon next to the label by default, or below the\n * label when helperTextPlacement is \"bottom\".\n */\n helperText?: React.ReactNode;\n\n /**\n * The placement of the helper text either below the field or in a tooltip\n *\n * @default \"tooltip\"\n */\n helperTextPlacement?: \"bottom\" | \"tooltip\";\n\n /**\n * Whether the field is disabled\n */\n isDisabled?: boolean;\n\n /**\n * A callback to customize error messages when a built-in validation rule fails.\n * Receives a discriminated union with the constraint data (e.g., the min value\n * that was exceeded) so the message can reference the threshold.\n *\n * Return a string to override the default message, or `undefined` to keep it.\n */\n onValidationError?: (error: ValidationError) => string | undefined;\n\n /**\n * Additional function to validate the field.\n *\n * Return `undefined` if valid, or an error message string if invalid.\n */\n validate?: (value: FieldValueType<Q, K>) => Promise<string | undefined>;\n\n /**\n * The component props for the form field.\n * Excludes runtime props (value, onChange) which are managed by ActionForm.\n */\n fieldComponentProps: Omit<\n FormFieldPropsByType[\n ValidFormFieldForPropertyType<\n FieldDescriptorType<Q, K>\n >\n ],\n FormManagedProps<\n ValidFormFieldForPropertyType<FieldDescriptorType<Q, K>>\n >\n >;\n}\n\n/**\n * A discriminated union describing which validation rule failed and the\n * constraint data the user needs to build a meaningful error message.\n */\nexport type ValidationError =\n | { type: \"required\" }\n | { type: \"min\"; min: number | Date }\n | { type: \"max\"; max: number | Date }\n | { type: \"minLength\"; minLength: number }\n | { type: \"maxLength\"; maxLength: number }\n | { type: \"maxSize\"; maxSize: number }\n | { type: \"validate\"; message: string };\n\n/**\n * Maps field types to their corresponding props\n */\nexport interface FormFieldPropsByType {\n DATE_RANGE_INPUT: DateRangeInputFieldProps;\n DATETIME_PICKER: DatetimePickerFieldProps;\n DROPDOWN: DropdownFieldProps<unknown, boolean>;\n FILE_PICKER: FilePickerProps;\n NUMBER_INPUT: NumberInputFieldProps;\n OBJECT_SELECT: ObjectSelectFieldProps<ObjectTypeDefinition>;\n OBJECT_SET: ObjectSetFieldProps<ObjectTypeDefinition>;\n RADIO_BUTTONS: RadioButtonsFieldProps<unknown>;\n TEXT_AREA: TextAreaFieldProps;\n TEXT_INPUT: TextInputFieldProps;\n CUSTOM: CustomFieldProps<unknown>;\n}\n\n/**\n * Datetime picker field props.\n *\n * When `formatDate` is omitted, ISO-like format is used (YYYY-MM-DD / YYYY-MM-DD HH:mm).\n */\nexport interface DatetimePickerFieldProps extends BaseFormFieldProps<Date> {\n /**\n * The earliest date the user can select.\n * If provided, this will be added to the field validation.\n */\n min?: Date;\n\n /**\n * The latest date the user can select.\n * If provided, this will be added to the field validation.\n */\n max?: Date;\n\n /**\n * Whether to show time picker.\n */\n showTime?: boolean;\n\n /**\n * Whether to close the popover after selecting a date.\n * @default true when `showTime` is false, false when `showTime` is true\n */\n closeOnSelection?: boolean;\n\n /**\n * Placeholder text shown when no value is selected.\n */\n placeholder?: string;\n\n /**\n * Formats a Date for display in the input field when not editing.\n * When typing, the input shows the parsable format (YYYY-MM-DD or YYYY-MM-DD HH:mm).\n * Provide a matching `parseDate` if using a custom format.\n */\n formatDate?: (date: Date) => string;\n\n /**\n * Parses a user-typed string back into a Date.\n * Must be the inverse of `formatDate` — if `formatDate(d)` produces string `s`,\n * then `parseDate(s)` must return an equivalent Date.\n * When omitted, defaults to parsing \"YYYY-MM-DD\" (date-only) or \"YYYY-MM-DD HH:mm\" (with time).\n */\n parseDate?: (text: string) => Date | undefined;\n\n /**\n * Ref forwarded to the portal container element.\n * Used to track portaled content for click-outside detection.\n */\n portalRef?: React.Ref<HTMLDivElement>;\n\n /**\n * Element that receives the date picker portal. Use this when rendering\n * inside modal dialogs so popovers stay in the dialog's stacking and focus\n * context instead of being appended directly to document.body.\n */\n portalContainer?: PortalContainer;\n}\n\n/**\n * A date range represented as a start/end tuple.\n * Either element may be `null` when the range is partially selected.\n */\nexport type DateRange = readonly [Date | null, Date | null];\n\n/** Default empty range — both bounds are null. */\nexport const EMPTY_RANGE: DateRange = [null, null];\n\n/**\n * Date range input field props.\n *\n * Renders two text inputs (start / end) with a shared calendar popover\n * that supports range selection.\n */\nexport interface DateRangeInputFieldProps\n extends BaseFormFieldProps<DateRange>\n{\n /** The earliest selectable date. */\n min?: Date;\n\n /** The latest selectable date. */\n max?: Date;\n\n /** Whether to show time pickers for both dates. */\n showTime?: boolean;\n\n /** Placeholder text for the start date input. */\n placeholderStart?: string;\n\n /** Placeholder text for the end date input. */\n placeholderEnd?: string;\n\n /** Whether to allow start and end on the same day. @default true */\n allowSingleDayRange?: boolean;\n\n /** Formats a Date for display. Defaults to \"YYYY-MM-DD\". */\n formatDate?: (date: Date) => string;\n\n /** Parses a user-typed string back into a Date. */\n parseDate?: (text: string) => Date | undefined;\n\n /**\n * Element that receives the date range picker portal. Use this when rendering\n * inside modal dialogs so popovers stay in the dialog's stacking and focus\n * context instead of being appended directly to document.body.\n */\n portalContainer?: PortalContainer;\n}\n\n/**\n * Dropdown field props with selectable items\n */\nexport interface DropdownFieldProps<V, Multiple extends boolean = false>\n extends BaseFormFieldProps<Multiple extends true ? V[] : V>\n{\n /**\n * Available items for the dropdown\n */\n items: V[];\n\n /**\n * Converts an item to a display string. Defaults to `String()`.\n */\n itemToStringLabel?: (item: V) => string;\n\n /**\n * Returns a unique string key for a list item. Used as the React `key`.\n * Falls back to the item's index when not provided.\n */\n itemToKey?: (item: V) => string;\n\n /**\n * Custom equality check for item values. Defaults to `Object.is`.\n * Required when items are objects to ensure correct selection matching.\n */\n isItemEqual?: (a: V, b: V) => boolean;\n\n /**\n * Whether the dropdown allows searching/filtering.\n * When true, renders a Combobox with a search input.\n * When false (default), renders a Select dropdown.\n */\n isSearchable?: boolean;\n\n /**\n * Placeholder text shown when no value is selected.\n */\n placeholder?: string;\n\n /**\n * Whether multiple values can be selected\n */\n isMultiple?: Multiple;\n\n /**\n * Ref forwarded to the portal container element.\n * Used to track portaled content for click-outside detection.\n */\n portalRef?: React.Ref<HTMLDivElement>;\n\n /**\n * Element that receives the dropdown portal. Use this when rendering inside\n * modal dialogs so popups stay in the dialog's stacking and focus context\n * instead of being appended directly to document.body.\n */\n portalContainer?: PortalContainer;\n\n /**\n * Controlled search input value. Must be provided together with `onQueryChange`.\n */\n query?: string;\n\n /**\n * Callback when the search input value changes.\n * Can be used standalone as an event listener or together with `query`\n * for fully controlled search state.\n */\n onQueryChange?: (query: string) => void;\n\n /**\n * When true, disables the combobox's built-in client-side filtering.\n * Use when items are already filtered server-side (e.g. via `onQueryChange`).\n *\n * @default false\n */\n disableClientSideFiltering?: boolean;\n\n /**\n * Status message rendered below the search input and above the item list\n * inside the popup. Use for loading/error/empty messages.\n */\n popupStatus?: React.ReactNode;\n\n /**\n * A React node to render after the item list.\n * Use for infinite scroll sentinels, \"load more\" buttons, etc.\n */\n trailingItem?: React.ReactNode;\n}\n\nexport interface FilePickerProps extends BaseFormFieldProps<File | File[]> {\n /**\n * Whether multiple files can be selected\n */\n isMulti?: boolean;\n\n /**\n * Accepted file types (e.g., \"image/*\", \".pdf\")\n */\n accept?: string | string[];\n\n /**\n * Maximum file size in bytes\n */\n maxSize?: number;\n\n /**\n * The text displayed when no file is selected.\n *\n * @default \"No file chosen\"\n */\n text?: string;\n\n /**\n * The text displayed on the browse button.\n *\n * @default \"Browse\"\n */\n buttonText?: string;\n}\n\n/**\n * Text area field props\n */\nexport interface TextAreaFieldProps extends\n BaseFormFieldProps<string>,\n Pick<\n React.TextareaHTMLAttributes<HTMLTextAreaElement>,\n | \"rows\"\n | \"wrap\"\n /**\n * If provided, this will be added to the field validation\n */\n | \"minLength\"\n /**\n * If provided, this will be added to the field validation\n */\n | \"maxLength\"\n >\n{\n placeholder?: string;\n}\n\nexport interface TextInputFieldProps extends\n BaseFormFieldProps<string>,\n Pick<\n React.InputHTMLAttributes<HTMLInputElement>,\n /**\n * If provided, this will be added to the field validation\n */\n | \"minLength\"\n /**\n * If provided, this will be added to the field validation\n */\n | \"maxLength\"\n >\n{\n placeholder?: string;\n}\n\n/**\n * Number input field props\n */\nexport interface NumberInputFieldProps extends BaseFormFieldProps<number> {\n /**\n * Minimum allowed value.\n */\n min?: number;\n\n /**\n * Maximum allowed value.\n */\n max?: number;\n\n /**\n * Step increment for the input. Used by the stepper buttons and ArrowUp/ArrowDown keyboard stepping.\n *\n * @default 1\n */\n step?: number;\n\n /**\n * Placeholder text shown when the input is empty.\n */\n placeholder?: string;\n}\n\n/**\n * Radio buttons field props\n */\nexport interface RadioButtonsFieldProps<V> extends BaseFormFieldProps<V> {\n /**\n * Available options for radio buttons.\n *\n * Values are compared by reference equality (`===`). When options contain\n * non-primitive values, pass the same object references for `value` and\n * the corresponding option entry.\n */\n options: Option<V>[];\n\n /**\n * Controls the layout direction of the radio buttons.\n *\n * - `\"vertical\"` (default): options are stacked in a column\n * - `\"horizontal\"`: options are laid out in a row, wrapping when needed\n */\n orientation?: \"horizontal\" | \"vertical\";\n}\n\n/**\n * Option interface for radio button options\n */\nexport interface Option<V> {\n label: string;\n value: V;\n}\n\n/**\n * Object set field displays the summary of the count of the given object set\n */\nexport interface ObjectSetFieldProps<T extends ObjectTypeDefinition>\n extends Pick<BaseFormFieldProps<ObjectSet<T>>, \"id\" | \"value\">\n{\n /**\n * Message displayed when no object set is provided.\n *\n * @default \"Object set is not defined\"\n */\n emptyMessage?: string;\n}\n\n/**\n * Object select field props for selecting object instances from the ontology.\n * Used for action parameters that accept a single object or multiple objects.\n *\n * Extends DropdownFieldProps with props that ObjectSelectField\n * manages internally (items, search, filtering) omitted from the public surface.\n */\nexport interface ObjectSelectFieldProps<\n Q extends ObjectTypeDefinition = ObjectTypeDefinition,\n> extends\n Omit<\n DropdownFieldProps<Osdk.Instance<Q>>,\n | \"items\"\n | \"itemToStringLabel\"\n | \"itemToKey\"\n | \"isItemEqual\"\n | \"isSearchable\"\n | \"query\"\n | \"onQueryChange\"\n | \"disableClientSideFiltering\"\n | \"renderItemList\"\n >\n{\n /**\n * The object type definition to search within.\n */\n objectType: Q;\n}\n\n/**\n * Custom field props for user-defined renderers\n */\nexport interface CustomFieldProps<V> extends BaseFormFieldProps<V> {\n /**\n * Custom renderer function\n */\n customRenderer: (props: BaseFormFieldProps<V>) => React.ReactNode;\n}\n\nexport interface BaseFormFieldProps<V> {\n /**\n * The HTML `id` attribute for the field input element.\n * Used for `<label htmlFor>` association.\n */\n id?: string;\n\n /**\n * The validation error message for this field, if any.\n * When set, the field should display a visual error state.\n */\n error?: string;\n\n /**\n * The value of the form field\n */\n value: V | null;\n\n /**\n * The default value of the form field.\n */\n defaultValue?: V;\n\n /**\n * Called when the field value changes.\n *\n * ActionForm internally wraps this to pass the key to `onFieldValueChange`:\n * ```\n * <DropdownField\n * {...fieldDef}\n * onChange={(value) => onFieldValueChange(fieldDef.key, value)}\n * />\n * ```\n *\n * @param value The new value of the form field\n */\n onChange?: (value: V | null) => void;\n}\n\nexport type FieldKey<Q extends ActionDefinition<unknown>> =\n keyof ActionParameters<Q>;\n\n/**\n * Extracts parameters from an ActionDefinition\n */\nexport type ActionParameters<Q extends ActionDefinition<unknown>> =\n CompileTimeMetadata<Q>[\"parameters\"];\n\n/**\n * Extracts the value type for a specific parameter\n *\n * TODO: Re-use `BaseType`\n */\nexport type FieldValueType<\n Q extends ActionDefinition<unknown>,\n K extends keyof ActionParameters<Q> = keyof ActionParameters<Q>,\n> = ActionParameters<Q>[K][\"type\"] extends\n ActionMetadata.DataType.Object<infer T> ? ActionParam.ObjectType<T>\n : ActionParameters<Q>[K][\"type\"] extends ActionMetadata.DataType.ObjectSet<\n infer T\n > ? ActionParam.ObjectSetType<T>\n : ActionParameters<Q>[K][\"type\"] extends ActionMetadata.DataType.Struct<\n infer T\n > ? ActionParam.StructType<T>\n : ActionParameters<Q>[K][\"type\"] extends keyof DataValueClientToWire\n ? DataValueClientToWire[ActionParameters<Q>[K][\"type\"]]\n : never;\n\n/**\n * Extracts the parameter type descriptor for a specific action parameter.\n */\nexport type FieldDescriptorType<\n Q extends ActionDefinition<unknown> = ActionDefinition<unknown>,\n K extends keyof ActionParameters<Q> = keyof ActionParameters<Q>,\n> = ActionParameters<Q>[K][\"type\"];\n\n/**\n * Available form field component types\n */\nexport type FieldComponent =\n | \"DATE_RANGE_INPUT\"\n | \"DATETIME_PICKER\"\n | \"DROPDOWN\"\n | \"FILE_PICKER\"\n | \"NUMBER_INPUT\"\n | \"OBJECT_SELECT\"\n | \"OBJECT_SET\"\n | \"RADIO_BUTTONS\"\n | \"TEXT_AREA\"\n | \"TEXT_INPUT\"\n | \"CUSTOM\";\n\n/**\n * Describes the data type of a form field, independent of OSDK.\n * Mirrors ActionMetadata.DataType to keep the rendering layer OSDK-agnostic.\n */\nexport type FieldType =\n | \"boolean\"\n | \"string\"\n | \"integer\"\n | \"long\"\n | \"double\"\n | \"datetime\"\n | \"timestamp\"\n | \"attachment\"\n | \"marking\"\n | \"mediaReference\"\n | \"objectType\"\n | \"geoshape\"\n | \"geohash\"\n | { type: \"object\"; object: string }\n | { type: \"objectSet\"; objectSet: string }\n | { type: \"interface\"; interface: string }\n | { type: \"struct\"; struct: Record<string, string> };\n\n/**\n * Props managed by form state infrastructure (FieldBridge / RHF).\n * Fields with onChange participate in form state → value and onChange are managed\n * externally. Read-only fields (no onChange, e.g. ObjectSetField) keep value in\n * fieldComponentProps so it bypasses form state cloning.\n */\ntype FormManagedProps<K extends FieldComponent> = \"onChange\" extends\n keyof FormFieldPropsByType[K] ? \"value\" | \"onChange\"\n : \"onChange\";\n\n/**\n * An OSDK-agnostic field definition used by BaseForm and FormFieldRenderer.\n * Contains only the information needed to render a single field — no generics,\n * no compile-time parameter constraints.\n *\n * Implemented as a distributed mapped type: switching on `fieldComponent`\n * narrows `fieldComponentProps` to the correct props type automatically.\n */\nexport type RendererFieldDefinition = {\n [K in FieldComponent]: {\n fieldKey: string;\n fieldComponent: K;\n fieldType?: FieldType;\n label: string;\n isRequired?: boolean;\n placeholder?: string;\n helperText?: React.ReactNode;\n helperTextPlacement?: \"bottom\" | \"tooltip\";\n validate?: (value: unknown) => Promise<string | undefined>;\n onValidationError?: (error: ValidationError) => string | undefined;\n fieldComponentProps: Omit<FormFieldPropsByType[K], FormManagedProps<K>>;\n };\n}[FieldComponent];\n\n/**\n * Gets valid form field types for a given property type\n */\nexport type ValidFormFieldForPropertyType<P extends FieldDescriptorType> =\n P extends { type: \"objectSet\" } ? \"OBJECT_SET\"\n : P extends { type: \"object\" } ? \"OBJECT_SELECT\"\n : P extends \"mediaReference\" | \"attachment\" ? \"FILE_PICKER\"\n : P extends \"boolean\" ? \"RADIO_BUTTONS\" | \"DROPDOWN\"\n : P extends \"string\" ? \"TEXT_INPUT\" | \"TEXT_AREA\"\n : P extends \"datetime\" | \"timestamp\" ? \"DATETIME_PICKER\"\n : P extends\n | \"double\"\n | \"integer\"\n | \"long\"\n | \"float\"\n | \"short\"\n | \"byte\"\n | \"decimal\" ? \"NUMBER_INPUT\"\n : never;\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAoBA;AACA;AACA;;AAuFA;AACA;AACA;AACA;;AAUA;AACA;AACA;;AAeA;AACA;AACA;AACA;AACA;;AA2DA;AACA;AACA;AACA;;AAGA;AACA,OAAO,MAAMA,WAAsB,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC;;AAElD;AACA;AACA;AACA;AACA;AACA;;AAoCA;AACA;AACA;;AAwHA;AACA;AACA;;AAqCA;AACA;AACA;;AAyBA;AACA;AACA;;AAoBA;AACA;AACA;;AAMA;AACA;AACA;;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;;AAuBA;AACA;AACA;;AAkDA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACA;;AAgBA;AACA;AACA;;AAMA;AACA;AACA;;AAcA;AACA;AACA;AACA;;AAoBA;AACA;AACA;AACA;AACA;AACA;;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAiBA;AACA;AACA","ignoreList":[]}
@@ -25,7 +25,17 @@
25
25
  }
26
26
  }
27
27
 
28
+ .osdkDatePickerDismissLayer {
29
+ --osdk-portal-dismiss-layer-z-index: var(--osdk-datetime-popover-z-index);
30
+ }
31
+
28
32
  /* Popover container */
33
+ .osdkDatePickerPositioner {
34
+ /* Match the dismiss layer's dialog stack level so the later positioner stays
35
+ clickable above it without escalating z-index values. */
36
+ z-index: var(--osdk-datetime-popover-z-index);
37
+ }
38
+
29
39
  .osdkDatePickerPopover {
30
40
  background: var(--osdk-datetime-popover-bg);
31
41
  padding: var(--osdk-datetime-popover-padding);
@@ -1,6 +1,8 @@
1
1
  // CSS Module proxy for DatePickerCommon.module.css
2
2
  const styles = {
3
3
  "osdkDatePickerInput": "DatePickerCommon-module__osdkDatePickerInput___wO8wXY1o",
4
+ "osdkDatePickerDismissLayer": "DatePickerCommon-module__osdkDatePickerDismissLayer___cgn-HDOP",
5
+ "osdkDatePickerPositioner": "DatePickerCommon-module__osdkDatePickerPositioner___-SNEfLrh",
4
6
  "osdkDatePickerPopover": "DatePickerCommon-module__osdkDatePickerPopover___naKd1wD6",
5
7
  "osdkDatePickerFocusBoundary": "DatePickerCommon-module__osdkDatePickerFocusBoundary___bqSPC7u5",
6
8
  "osdkDatePickerInputWrapper": "DatePickerCommon-module__osdkDatePickerInputWrapper___6-YO4VL8",
@@ -25,6 +25,7 @@ import { stopPropagation } from "./calendarShared.js";
25
25
  import commonStyles from "./DatePickerCommon.module.css.js";
26
26
  import styles from "./DateRangeInputField.module.css.js";
27
27
  import { LazyDateRangeCalendar } from "./LazyDateRangeCalendar.js";
28
+ import { PortalDismissLayer } from "./PortalDismissLayer.js";
28
29
  import { TimePicker } from "./TimePicker.js";
29
30
  import { useDateEditState } from "./useDateEditState.js";
30
31
  // Shared props for both start/end inputs. role="combobox" because each input
@@ -48,7 +49,8 @@ export const DateRangeInputField = /*#__PURE__*/React.memo(function ({
48
49
  allowSingleDayRange = true,
49
50
  showTime = false,
50
51
  formatDate,
51
- parseDate
52
+ parseDate,
53
+ portalContainer
52
54
  }) {
53
55
  const shouldCloseOnSelection = !showTime;
54
56
  const popoverId = useId();
@@ -56,6 +58,10 @@ export const DateRangeInputField = /*#__PURE__*/React.memo(function ({
56
58
  const endInputRef = useRef(null);
57
59
  const popoverRef = useRef(null);
58
60
  const [isOpen, setIsOpen] = useState(false);
61
+ // When focus returns to an input after Tab exits the popover boundary, the
62
+ // next input focus should not reopen the calendar before native Tab can
63
+ // continue to the following form field.
64
+ const skipReopenRef = useRef(false);
59
65
  // Tracks which input (start/end) owns the shared calendar popover.
60
66
  // Used to restore focus to the correct input when Tab-cycling through
61
67
  // focus boundaries and when the calendar selects a range endpoint.
@@ -135,16 +141,36 @@ export const DateRangeInputField = /*#__PURE__*/React.memo(function ({
135
141
 
136
142
  // --- Focus handlers ---
137
143
 
138
- const handleStartFocus = useCallback(() => {
139
- beginStartEditing();
140
- setActiveBoundary("start");
144
+ const getActiveInputRef = useCallback(() => activeBoundary === "start" ? startInputRef : endInputRef, [activeBoundary]);
145
+ const beginEditing = useCallback(boundary => {
146
+ if (boundary === "start") {
147
+ beginStartEditing();
148
+ } else {
149
+ beginEndEditing();
150
+ }
151
+ setActiveBoundary(boundary);
152
+ }, [beginStartEditing, beginEndEditing]);
153
+ const handleInputFocus = useCallback(boundary => {
154
+ beginEditing(boundary);
155
+ if (skipReopenRef.current) {
156
+ skipReopenRef.current = false;
157
+ return;
158
+ }
141
159
  setIsOpen(true);
142
- }, [beginStartEditing]);
160
+ }, [beginEditing]);
161
+ const handleStartFocus = useCallback(() => {
162
+ handleInputFocus("start");
163
+ }, [handleInputFocus]);
143
164
  const handleEndFocus = useCallback(() => {
144
- beginEndEditing();
145
- setActiveBoundary("end");
146
- setIsOpen(true);
147
- }, [beginEndEditing]);
165
+ handleInputFocus("end");
166
+ }, [handleInputFocus]);
167
+ const closePopoverForBoundaryExit = useCallback(() => {
168
+ skipReopenRef.current = true;
169
+ setIsOpen(false);
170
+ stopStartEditing();
171
+ stopEndEditing();
172
+ getActiveInputRef().current?.focus();
173
+ }, [getActiveInputRef, stopStartEditing, stopEndEditing]);
148
174
 
149
175
  // --- Blur handlers ---
150
176
 
@@ -282,25 +308,25 @@ export const DateRangeInputField = /*#__PURE__*/React.memo(function ({
282
308
  // Start boundary (top): Shift+Tab past the first calendar element redirects
283
309
  // focus to whichever input is currently active.
284
310
  const handleStartFocusBoundary = useCallback(() => {
285
- const activeRef = activeBoundary === "start" ? startInputRef : endInputRef;
286
- activeRef.current?.focus();
287
- }, [activeBoundary]);
311
+ getActiveInputRef().current?.focus();
312
+ }, [getActiveInputRef]);
288
313
 
289
314
  // End boundary (bottom): Two cases —
290
315
  // (1) Tab past the last calendar element (focus came from inside the popover)
291
- // → close the popover and blur both inputs.
316
+ // → close the popover and return focus to the active input so the next
317
+ // native Tab continues to the next form field.
292
318
  // (2) Focus entered from outside the popover (e.g. reverse Tab from the page)
293
319
  // → redirect to the last interactive element inside the popover.
294
320
  const handleEndFocusBoundary = useCallback(e => {
295
321
  const related = e.relatedTarget ?? document.activeElement;
296
322
  if (popoverRef.current?.contains(related)) {
297
- closePopover();
323
+ closePopoverForBoundaryExit();
298
324
  } else {
299
325
  const buttons = popoverRef.current?.querySelectorAll("button, select");
300
326
  const lastButton = buttons?.[buttons.length - 1];
301
327
  lastButton?.focus();
302
328
  }
303
- }, [closePopover]);
329
+ }, [closePopoverForBoundaryExit]);
304
330
 
305
331
  // --- Calendar selected range ---
306
332
 
@@ -326,6 +352,10 @@ export const DateRangeInputField = /*#__PURE__*/React.memo(function ({
326
352
  return /*#__PURE__*/React.createElement(Popover.Root, {
327
353
  open: isOpen,
328
354
  onOpenChange: handleOpenChange
355
+ // Uses pointer-down outside dismissal so the click that opens the picker
356
+ // is not reinterpreted after the portal dismiss layer appears.
357
+ ,
358
+ modal: "trap-focus"
329
359
  }, /*#__PURE__*/React.createElement(Popover.Trigger, {
330
360
  nativeButton: false,
331
361
  render: /*#__PURE__*/React.createElement("div", {
@@ -359,7 +389,13 @@ export const DateRangeInputField = /*#__PURE__*/React.memo(function ({
359
389
  "aria-expanded": isOpen && activeBoundary === "end",
360
390
  "aria-label": "End date",
361
391
  "aria-invalid": endInvalid || undefined
362
- }, sharedInputProps)))), /*#__PURE__*/React.createElement(Popover.Portal, null, /*#__PURE__*/React.createElement(Popover.Positioner, {
392
+ }, sharedInputProps)))), /*#__PURE__*/React.createElement(Popover.Portal, {
393
+ container: portalContainer
394
+ }, /*#__PURE__*/React.createElement(PortalDismissLayer, {
395
+ className: commonStyles.osdkDatePickerDismissLayer,
396
+ onDismiss: closePopover
397
+ }), /*#__PURE__*/React.createElement(Popover.Positioner, {
398
+ className: commonStyles.osdkDatePickerPositioner,
363
399
  sideOffset: 4
364
400
  }, /*#__PURE__*/React.createElement(Popover.Popup, {
365
401
  ref: popoverRef,