@osdk/react-components 0.2.0-beta.16 → 0.2.0-beta.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +91 -3
- package/build/browser/action-form/ActionForm.js +109 -0
- package/build/browser/action-form/ActionForm.js.map +1 -0
- package/build/browser/action-form/ActionFormApi.js.map +1 -1
- package/build/browser/action-form/BaseForm.js +74 -0
- package/build/browser/action-form/BaseForm.js.map +1 -0
- package/build/browser/action-form/FormFieldApi.js.map +1 -1
- package/build/browser/action-form/fields/FieldBridge.js +45 -0
- package/build/browser/action-form/fields/FieldBridge.js.map +1 -0
- package/build/browser/action-form/fields/FormFieldRenderer.js +68 -0
- package/build/browser/action-form/fields/FormFieldRenderer.js.map +1 -0
- package/build/browser/action-form/fields/TextInputField.js +38 -0
- package/build/browser/action-form/fields/TextInputField.js.map +1 -0
- package/build/browser/action-form/utils/coerceFieldValue.js +93 -0
- package/build/browser/action-form/utils/coerceFieldValue.js.map +1 -0
- package/build/browser/action-form/utils/getDefaultFieldComponent.js +56 -0
- package/build/browser/action-form/utils/getDefaultFieldComponent.js.map +1 -0
- package/build/browser/action-form/utils/getDefaultFieldDefinitions.js +33 -0
- package/build/browser/action-form/utils/getDefaultFieldDefinitions.js.map +1 -0
- package/build/browser/base-components/tooltip/Tooltip.js +91 -0
- package/build/browser/base-components/tooltip/Tooltip.js.map +1 -0
- package/build/browser/base-components/tooltip/Tooltip.module.css +70 -0
- package/build/browser/base-components/tooltip/Tooltip.module.css.js +11 -0
- package/build/browser/base-components/tooltip/index.js +18 -0
- package/build/browser/base-components/tooltip/index.js.map +1 -0
- package/build/browser/filter-list/base/BaseFilterList.js +23 -11
- package/build/browser/filter-list/base/BaseFilterList.js.map +1 -1
- package/build/browser/filter-list/base/FilterIcons.js +48 -13
- package/build/browser/filter-list/base/FilterIcons.js.map +1 -1
- package/build/browser/filter-list/base/FilterList.module.css +57 -13
- package/build/browser/filter-list/base/FilterList.module.css.js +5 -2
- package/build/browser/filter-list/base/FilterListHeader.js +10 -15
- package/build/browser/filter-list/base/FilterListHeader.js.map +1 -1
- package/build/browser/filter-list/base/FilterListHeader.module.css +13 -41
- package/build/browser/filter-list/base/FilterListHeader.module.css.js +1 -4
- package/build/browser/filter-list/hooks/useFilterListState.js +25 -7
- package/build/browser/filter-list/hooks/useFilterListState.js.map +1 -1
- package/build/browser/filter-list/utils/assertUnreachable.js +1 -8
- package/build/browser/filter-list/utils/assertUnreachable.js.map +1 -1
- package/build/browser/filter-list/utils/filterStateToWhereClause.js +121 -91
- package/build/browser/filter-list/utils/filterStateToWhereClause.js.map +1 -1
- package/build/browser/object-table/DefaultCellRenderer.js +6 -1
- package/build/browser/object-table/DefaultCellRenderer.js.map +1 -1
- package/build/browser/object-table/EditableCell.js +76 -9
- package/build/browser/object-table/EditableCell.js.map +1 -1
- package/build/browser/object-table/EditableCell.module.css +47 -8
- package/build/browser/object-table/EditableCell.module.css.js +5 -1
- package/build/browser/object-table/MultiColumnSortDialog.js +4 -1
- package/build/browser/object-table/MultiColumnSortDialog.js.map +1 -1
- package/build/browser/object-table/ObjectTable.js +4 -1
- package/build/browser/object-table/ObjectTable.js.map +1 -1
- package/build/browser/object-table/ObjectTableApi.js.map +1 -1
- package/build/browser/object-table/SelectionCells.js +1 -0
- package/build/browser/object-table/SelectionCells.js.map +1 -1
- package/build/browser/object-table/Table.js +2 -2
- package/build/browser/object-table/Table.js.map +1 -1
- package/build/browser/object-table/TableBody.js +1 -1
- package/build/browser/object-table/TableBody.js.map +1 -1
- package/build/browser/object-table/TableEditContainer.js +27 -16
- package/build/browser/object-table/TableEditContainer.js.map +1 -1
- package/build/browser/object-table/TableEditContainer.module.css +27 -0
- package/build/browser/object-table/TableEditContainer.module.css.js +5 -1
- package/build/browser/object-table/TableHeader.js +4 -1
- package/build/browser/object-table/TableHeader.js.map +1 -1
- package/build/browser/object-table/TableHeader.module.css +2 -1
- package/build/browser/object-table/TableHeader.module.css.js +1 -0
- package/build/browser/object-table/TableRow.js +2 -2
- package/build/browser/object-table/TableRow.js.map +1 -1
- package/build/browser/object-table/hooks/useColumnDefs.js +2 -1
- package/build/browser/object-table/hooks/useColumnDefs.js.map +1 -1
- package/build/browser/object-table/hooks/useEditableTable.js +21 -2
- package/build/browser/object-table/hooks/useEditableTable.js.map +1 -1
- package/build/browser/object-table/utils/types.js.map +1 -1
- package/build/browser/shared/assertUnreachable.js +25 -0
- package/build/browser/shared/assertUnreachable.js.map +1 -0
- package/build/browser/shared/typedMemo.js +25 -0
- package/build/browser/shared/typedMemo.js.map +1 -0
- package/build/browser/styles.css +219 -63
- package/build/cjs/public/experimental.cjs +1212 -966
- package/build/cjs/public/experimental.cjs.map +1 -1
- package/build/cjs/public/experimental.css +167 -55
- package/build/cjs/public/experimental.css.map +1 -1
- package/build/cjs/public/experimental.d.cts +18 -4
- package/build/esm/action-form/ActionForm.js +109 -0
- package/build/esm/action-form/ActionForm.js.map +1 -0
- package/build/esm/action-form/ActionFormApi.js.map +1 -1
- package/build/esm/action-form/BaseForm.js +74 -0
- package/build/esm/action-form/BaseForm.js.map +1 -0
- package/build/esm/action-form/FormFieldApi.js.map +1 -1
- package/build/esm/action-form/fields/FieldBridge.js +45 -0
- package/build/esm/action-form/fields/FieldBridge.js.map +1 -0
- package/build/esm/action-form/fields/FormFieldRenderer.js +68 -0
- package/build/esm/action-form/fields/FormFieldRenderer.js.map +1 -0
- package/build/esm/action-form/fields/TextInputField.js +38 -0
- package/build/esm/action-form/fields/TextInputField.js.map +1 -0
- package/build/esm/action-form/utils/coerceFieldValue.js +93 -0
- package/build/esm/action-form/utils/coerceFieldValue.js.map +1 -0
- package/build/esm/action-form/utils/getDefaultFieldComponent.js +56 -0
- package/build/esm/action-form/utils/getDefaultFieldComponent.js.map +1 -0
- package/build/esm/action-form/utils/getDefaultFieldDefinitions.js +33 -0
- package/build/esm/action-form/utils/getDefaultFieldDefinitions.js.map +1 -0
- package/build/esm/base-components/tooltip/Tooltip.js +91 -0
- package/build/esm/base-components/tooltip/Tooltip.js.map +1 -0
- package/build/esm/base-components/tooltip/Tooltip.module.css +70 -0
- package/build/esm/base-components/tooltip/index.js +18 -0
- package/build/esm/base-components/tooltip/index.js.map +1 -0
- package/build/esm/filter-list/base/BaseFilterList.js +23 -11
- package/build/esm/filter-list/base/BaseFilterList.js.map +1 -1
- package/build/esm/filter-list/base/FilterIcons.js +48 -13
- package/build/esm/filter-list/base/FilterIcons.js.map +1 -1
- package/build/esm/filter-list/base/FilterList.module.css +57 -13
- package/build/esm/filter-list/base/FilterListHeader.js +10 -15
- package/build/esm/filter-list/base/FilterListHeader.js.map +1 -1
- package/build/esm/filter-list/base/FilterListHeader.module.css +13 -41
- package/build/esm/filter-list/hooks/useFilterListState.js +25 -7
- package/build/esm/filter-list/hooks/useFilterListState.js.map +1 -1
- package/build/esm/filter-list/utils/assertUnreachable.js +1 -8
- package/build/esm/filter-list/utils/assertUnreachable.js.map +1 -1
- package/build/esm/filter-list/utils/filterStateToWhereClause.js +121 -91
- package/build/esm/filter-list/utils/filterStateToWhereClause.js.map +1 -1
- package/build/esm/object-table/DefaultCellRenderer.js +6 -1
- package/build/esm/object-table/DefaultCellRenderer.js.map +1 -1
- package/build/esm/object-table/EditableCell.js +76 -9
- package/build/esm/object-table/EditableCell.js.map +1 -1
- package/build/esm/object-table/EditableCell.module.css +47 -8
- package/build/esm/object-table/MultiColumnSortDialog.js +4 -1
- package/build/esm/object-table/MultiColumnSortDialog.js.map +1 -1
- package/build/esm/object-table/ObjectTable.js +4 -1
- package/build/esm/object-table/ObjectTable.js.map +1 -1
- package/build/esm/object-table/ObjectTableApi.js.map +1 -1
- package/build/esm/object-table/SelectionCells.js +1 -0
- package/build/esm/object-table/SelectionCells.js.map +1 -1
- package/build/esm/object-table/Table.js +2 -2
- package/build/esm/object-table/Table.js.map +1 -1
- package/build/esm/object-table/TableBody.js +1 -1
- package/build/esm/object-table/TableBody.js.map +1 -1
- package/build/esm/object-table/TableEditContainer.js +27 -16
- package/build/esm/object-table/TableEditContainer.js.map +1 -1
- package/build/esm/object-table/TableEditContainer.module.css +27 -0
- package/build/esm/object-table/TableHeader.js +4 -1
- package/build/esm/object-table/TableHeader.js.map +1 -1
- package/build/esm/object-table/TableHeader.module.css +2 -1
- package/build/esm/object-table/TableRow.js +2 -2
- package/build/esm/object-table/TableRow.js.map +1 -1
- package/build/esm/object-table/hooks/useColumnDefs.js +2 -1
- package/build/esm/object-table/hooks/useColumnDefs.js.map +1 -1
- package/build/esm/object-table/hooks/useEditableTable.js +21 -2
- package/build/esm/object-table/hooks/useEditableTable.js.map +1 -1
- package/build/esm/object-table/utils/types.js.map +1 -1
- package/build/esm/shared/assertUnreachable.js +25 -0
- package/build/esm/shared/assertUnreachable.js.map +1 -0
- package/build/esm/shared/typedMemo.js +25 -0
- package/build/esm/shared/typedMemo.js.map +1 -0
- package/build/types/action-form/ActionForm.d.ts +4 -0
- package/build/types/action-form/ActionForm.d.ts.map +1 -0
- package/build/types/action-form/ActionFormApi.d.ts +48 -32
- package/build/types/action-form/ActionFormApi.d.ts.map +1 -1
- package/build/types/action-form/BaseForm.d.ts +3 -0
- package/build/types/action-form/BaseForm.d.ts.map +1 -0
- package/build/types/action-form/FormFieldApi.d.ts +58 -18
- package/build/types/action-form/FormFieldApi.d.ts.map +1 -1
- package/build/types/action-form/fields/FieldBridge.d.ts +9 -0
- package/build/types/action-form/fields/FieldBridge.d.ts.map +1 -0
- package/build/types/action-form/fields/FormFieldRenderer.d.ts +8 -0
- package/build/types/action-form/fields/FormFieldRenderer.d.ts.map +1 -0
- package/build/types/action-form/fields/TextInputField.d.ts +3 -0
- package/build/types/action-form/fields/TextInputField.d.ts.map +1 -0
- package/build/types/action-form/utils/coerceFieldValue.d.ts +10 -0
- package/build/types/action-form/utils/coerceFieldValue.d.ts.map +1 -0
- package/build/types/action-form/utils/getDefaultFieldComponent.d.ts +6 -0
- package/build/types/action-form/utils/getDefaultFieldComponent.d.ts.map +1 -0
- package/build/types/action-form/utils/getDefaultFieldDefinitions.d.ts +6 -0
- package/build/types/action-form/utils/getDefaultFieldDefinitions.d.ts.map +1 -0
- package/build/types/base-components/tooltip/Tooltip.d.ts +31 -0
- package/build/types/base-components/tooltip/Tooltip.d.ts.map +1 -0
- package/build/types/base-components/tooltip/index.d.ts +1 -0
- package/build/types/base-components/tooltip/index.d.ts.map +1 -0
- package/build/types/filter-list/base/BaseFilterList.d.ts.map +1 -1
- package/build/types/filter-list/base/FilterIcons.d.ts +7 -0
- package/build/types/filter-list/base/FilterIcons.d.ts.map +1 -1
- package/build/types/filter-list/base/FilterListHeader.d.ts.map +1 -1
- package/build/types/filter-list/hooks/useFilterListState.d.ts.map +1 -1
- package/build/types/filter-list/utils/assertUnreachable.d.ts +1 -6
- package/build/types/filter-list/utils/assertUnreachable.d.ts.map +1 -1
- package/build/types/filter-list/utils/filterStateToWhereClause.d.ts +5 -1
- package/build/types/filter-list/utils/filterStateToWhereClause.d.ts.map +1 -1
- package/build/types/object-table/EditableCell.d.ts +8 -2
- package/build/types/object-table/EditableCell.d.ts.map +1 -1
- package/build/types/object-table/ObjectTableApi.d.ts +7 -0
- package/build/types/object-table/ObjectTableApi.d.ts.map +1 -1
- package/build/types/object-table/Table.d.ts +4 -0
- package/build/types/object-table/Table.d.ts.map +1 -1
- package/build/types/object-table/TableEditContainer.d.ts +1 -1
- package/build/types/object-table/TableEditContainer.d.ts.map +1 -1
- package/build/types/object-table/TableHeader.d.ts.map +1 -1
- package/build/types/object-table/TableRow.d.ts +2 -2
- package/build/types/object-table/TableRow.d.ts.map +1 -1
- package/build/types/object-table/hooks/useEditableTable.d.ts.map +1 -1
- package/build/types/object-table/utils/types.d.ts +7 -4
- package/build/types/object-table/utils/types.d.ts.map +1 -1
- package/build/types/shared/assertUnreachable.d.ts +6 -0
- package/build/types/shared/assertUnreachable.d.ts.map +1 -0
- package/build/types/shared/typedMemo.d.ts +6 -0
- package/build/types/shared/typedMemo.d.ts.map +1 -0
- package/package.json +6 -5
|
@@ -513,11 +513,11 @@ interface CellIdentifier {
|
|
|
513
513
|
columnId: string;
|
|
514
514
|
}
|
|
515
515
|
interface CellEditInfo<TData extends RowData = unknown, CellValue = unknown> extends CellIdentifier {
|
|
516
|
-
newValue: CellValue;
|
|
517
|
-
oldValue: CellValue;
|
|
516
|
+
newValue: CellValue | null;
|
|
517
|
+
oldValue: CellValue | null;
|
|
518
518
|
originalRowData: TData;
|
|
519
519
|
}
|
|
520
|
-
type
|
|
520
|
+
type EditModeState = {
|
|
521
521
|
type: "always";
|
|
522
522
|
isActive: true;
|
|
523
523
|
} | {
|
|
@@ -530,7 +530,10 @@ interface EditableConfig<TData extends RowData = unknown, CellValue = unknown> {
|
|
|
530
530
|
onCellEdit: (cellId: string, info: CellEditInfo<TData, CellValue>) => void;
|
|
531
531
|
onSubmitEdits?: () => Promise<boolean>;
|
|
532
532
|
clearEdits: () => void;
|
|
533
|
-
|
|
533
|
+
editModeState: EditModeState;
|
|
534
|
+
onCellValidationError: (cellId: string, error: string) => void;
|
|
535
|
+
validationErrors: Map<string, string>;
|
|
536
|
+
clearCellValidationError: (cellId: string) => void;
|
|
534
537
|
}
|
|
535
538
|
|
|
536
539
|
type ColumnDefinition<Q extends ObjectOrInterfaceDefinition, RDPs extends Record<string, SimplePropertyDef> = Record<string, never>, FunctionColumns extends Record<string, QueryDefinition<{}>> = Record<string, never>> = {
|
|
@@ -550,6 +553,13 @@ type ColumnDefinition<Q extends ObjectOrInterfaceDefinition, RDPs extends Record
|
|
|
550
553
|
orderable?: boolean;
|
|
551
554
|
filterable?: boolean;
|
|
552
555
|
editable?: boolean;
|
|
556
|
+
/**
|
|
557
|
+
* Additional function to validate the cell value during edit
|
|
558
|
+
*
|
|
559
|
+
* @param value the current cell value
|
|
560
|
+
* @returns a promise that resolves to an error message string if validation fails, or undefined if validation succeeds
|
|
561
|
+
*/
|
|
562
|
+
validateEdit?: (value: unknown) => Promise<string | undefined>;
|
|
553
563
|
renderCell?: (object: Osdk.Instance<Q, "$allBaseProperties", PropertyKeys<Q>, RDPs>, locator: ColumnDefinitionLocator<Q, RDPs, FunctionColumns>) => React.ReactNode;
|
|
554
564
|
/**
|
|
555
565
|
* If provided, this will be used in the column header.
|
|
@@ -812,11 +822,15 @@ declare module "@tanstack/react-table" {
|
|
|
812
822
|
isVisible?: boolean;
|
|
813
823
|
editable?: boolean;
|
|
814
824
|
dataType?: string;
|
|
825
|
+
validateEdit?: (value: unknown) => Promise<string | undefined>;
|
|
815
826
|
}
|
|
816
827
|
interface TableMeta<TData extends RowData = unknown> {
|
|
817
828
|
onCellEdit?: (cellId: string, info: CellEditInfo<TData, unknown>) => void;
|
|
829
|
+
onCellValidationError?: (cellId: string, error: string) => void;
|
|
830
|
+
clearCellValidationError?: (cellId: string) => void;
|
|
818
831
|
cellEdits?: Record<string, CellEditInfo<TData, unknown>>;
|
|
819
832
|
isInEditMode?: boolean;
|
|
833
|
+
validationErrors?: Map<string, string>;
|
|
820
834
|
}
|
|
821
835
|
}
|
|
822
836
|
interface BaseTableProps<TData extends RowData> {
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
2
|
+
/*
|
|
3
|
+
* Copyright 2025 Palantir Technologies, Inc. All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { useOsdkMetadata } from "@osdk/react";
|
|
19
|
+
import { useOsdkAction } from "@osdk/react/experimental";
|
|
20
|
+
import React, { useCallback, useEffect, useMemo } from "react";
|
|
21
|
+
import { typedReactMemo } from "../shared/typedMemo.js";
|
|
22
|
+
import { BaseForm } from "./BaseForm.js";
|
|
23
|
+
import { coerceFieldValue } from "./utils/coerceFieldValue.js";
|
|
24
|
+
import { getDefaultFieldDefinitions } from "./utils/getDefaultFieldDefinitions.js";
|
|
25
|
+
const EMPTY_FIELD_DEFINITIONS = [];
|
|
26
|
+
export const ActionForm = typedReactMemo(function ({
|
|
27
|
+
actionDefinition,
|
|
28
|
+
formTitle,
|
|
29
|
+
formFieldDefinitions,
|
|
30
|
+
formState: controlledFormState,
|
|
31
|
+
onFormStateChange,
|
|
32
|
+
isSubmitDisabled,
|
|
33
|
+
onSubmit,
|
|
34
|
+
onValidationResponse: _onValidationResponse,
|
|
35
|
+
onSuccess,
|
|
36
|
+
onError
|
|
37
|
+
}) {
|
|
38
|
+
const {
|
|
39
|
+
applyAction: osdkApplyAction,
|
|
40
|
+
isPending
|
|
41
|
+
} = useOsdkAction(actionDefinition);
|
|
42
|
+
const {
|
|
43
|
+
metadata,
|
|
44
|
+
loading: metadataLoading,
|
|
45
|
+
error: metadataError
|
|
46
|
+
} = useOsdkMetadata(actionDefinition);
|
|
47
|
+
useEffect(function () {
|
|
48
|
+
if (metadataError != null) {
|
|
49
|
+
onError?.({
|
|
50
|
+
type: "unknown",
|
|
51
|
+
error: metadataError
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}, [metadataError, onError]);
|
|
55
|
+
const parameters = metadata?.parameters;
|
|
56
|
+
const resolvedFieldDefinitions = useMemo(() => formFieldDefinitions ?? (metadata != null ? getDefaultFieldDefinitions(metadata) : EMPTY_FIELD_DEFINITIONS), [formFieldDefinitions, metadata]);
|
|
57
|
+
const rendererFieldDefinitions = useMemo(() => resolvedFieldDefinitions.map(def => ({
|
|
58
|
+
...def,
|
|
59
|
+
fieldKey: String(def.fieldKey),
|
|
60
|
+
fieldType: parameters?.[String(def.fieldKey)]?.type,
|
|
61
|
+
defaultValue: def.defaultValue
|
|
62
|
+
})), [resolvedFieldDefinitions, parameters]);
|
|
63
|
+
const coerceFormState = useCallback(rawState => {
|
|
64
|
+
const coerced = {};
|
|
65
|
+
for (const [key, value] of Object.entries(rawState)) {
|
|
66
|
+
coerced[key] = coerceFieldValue(parameters?.[key]?.type, value);
|
|
67
|
+
}
|
|
68
|
+
return coerced;
|
|
69
|
+
}, [parameters]);
|
|
70
|
+
const handleSubmit = useCallback(async rawFormState => {
|
|
71
|
+
const formState = coerceFormState(rawFormState);
|
|
72
|
+
try {
|
|
73
|
+
if (onSubmit != null) {
|
|
74
|
+
await onSubmit(formState, osdkApplyAction);
|
|
75
|
+
} else {
|
|
76
|
+
const result = await osdkApplyAction(formState);
|
|
77
|
+
onSuccess?.(result);
|
|
78
|
+
}
|
|
79
|
+
} catch (e) {
|
|
80
|
+
onError?.({
|
|
81
|
+
type: "submission",
|
|
82
|
+
error: e
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}, [coerceFormState, onSubmit, osdkApplyAction, onSuccess, onError]);
|
|
86
|
+
const handleFieldValueChange = useCallback((fieldKey, value) => {
|
|
87
|
+
onFormStateChange?.(prev => ({
|
|
88
|
+
...prev,
|
|
89
|
+
[fieldKey]: value
|
|
90
|
+
}));
|
|
91
|
+
}, [onFormStateChange]);
|
|
92
|
+
const resolvedTitle = formTitle ?? metadata?.displayName ?? actionDefinition.apiName;
|
|
93
|
+
const commonProps = {
|
|
94
|
+
formTitle: resolvedTitle,
|
|
95
|
+
fieldDefinitions: rendererFieldDefinitions,
|
|
96
|
+
onSubmit: handleSubmit,
|
|
97
|
+
isSubmitDisabled,
|
|
98
|
+
isPending,
|
|
99
|
+
isLoading: metadataLoading
|
|
100
|
+
};
|
|
101
|
+
if (controlledFormState != null) {
|
|
102
|
+
return /*#__PURE__*/React.createElement(BaseForm, _extends({}, commonProps, {
|
|
103
|
+
formState: controlledFormState,
|
|
104
|
+
onFieldValueChange: handleFieldValueChange
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
return /*#__PURE__*/React.createElement(BaseForm, commonProps);
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=ActionForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionForm.js","names":["useOsdkMetadata","useOsdkAction","React","useCallback","useEffect","useMemo","typedReactMemo","BaseForm","coerceFieldValue","getDefaultFieldDefinitions","EMPTY_FIELD_DEFINITIONS","ActionForm","actionDefinition","formTitle","formFieldDefinitions","formState","controlledFormState","onFormStateChange","isSubmitDisabled","onSubmit","onValidationResponse","_onValidationResponse","onSuccess","onError","applyAction","osdkApplyAction","isPending","metadata","loading","metadataLoading","error","metadataError","type","parameters","resolvedFieldDefinitions","rendererFieldDefinitions","map","def","fieldKey","String","fieldType","defaultValue","coerceFormState","rawState","coerced","key","value","Object","entries","handleSubmit","rawFormState","result","e","handleFieldValueChange","prev","resolvedTitle","displayName","apiName","commonProps","fieldDefinitions","isLoading","createElement","_extends","onFieldValueChange"],"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 { useOsdkMetadata } from \"@osdk/react\";\nimport { useOsdkAction } from \"@osdk/react/experimental\";\nimport React, { useCallback, useEffect, useMemo } from \"react\";\nimport { typedReactMemo } from \"../shared/typedMemo.js\";\nimport type { ActionFormProps, FormState } 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: readonly [] = [];\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 resolvedFieldDefinitions = useMemo(\n () =>\n formFieldDefinitions\n ?? (metadata != null\n ? getDefaultFieldDefinitions<Q>(metadata)\n : EMPTY_FIELD_DEFINITIONS),\n [formFieldDefinitions, metadata],\n );\n\n const rendererFieldDefinitions: ReadonlyArray<RendererFieldDefinition> =\n useMemo(\n () =>\n resolvedFieldDefinitions.map((def) => ({\n ...def,\n fieldKey: String(def.fieldKey),\n fieldType: parameters?.[String(def.fieldKey)]?.type,\n defaultValue: def.defaultValue,\n })),\n [resolvedFieldDefinitions, parameters],\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 fieldDefinitions: rendererFieldDefinitions,\n onSubmit: handleSubmit,\n isSubmitDisabled,\n isPending,\n isLoading: metadataLoading,\n };\n\n if (isControlled) {\n return (\n <BaseForm\n {...commonProps}\n formState={controlledFormState}\n onFieldValueChange={handleFieldValueChange}\n />\n );\n }\n\n return <BaseForm {...commonProps} />;\n});\n"],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA,SAASA,eAAe,QAAQ,aAAa;AAC7C,SAASC,aAAa,QAAQ,0BAA0B;AACxD,OAAOC,KAAK,IAAIC,WAAW,EAAEC,SAAS,EAAEC,OAAO,QAAQ,OAAO;AAC9D,SAASC,cAAc,QAAQ,wBAAwB;AAEvD,SAASC,QAAQ,QAAQ,eAAe;AAExC,SAASC,gBAAgB,QAAQ,6BAA6B;AAC9D,SAASC,0BAA0B,QAAQ,uCAAuC;AAElF,MAAMC,uBAAoC,GAAG,EAAE;AAE/C,OAAO,MAAMC,UAEU,GAAGL,cAAc,CAAC,UAEvC;EACAM,gBAAgB;EAChBC,SAAS;EACTC,oBAAoB;EACpBC,SAAS,EAAEC,mBAAmB;EAC9BC,iBAAiB;EACjBC,gBAAgB;EAChBC,QAAQ;EACRC,oBAAoB,EAAEC,qBAAqB;EAC3CC,SAAS;EACTC;AACkB,CAAC,EAAsB;EACzC,MAAM;IAAEC,WAAW,EAAEC,eAAe;IAAEC;EAAU,CAAC,GAAGzB,aAAa,CAC/DW,gBACF,CAAC;EACD,MAAM;IACJe,QAAQ;IACRC,OAAO,EAAEC,eAAe;IACxBC,KAAK,EAAEC;EACT,CAAC,GAAG/B,eAAe,CAACY,gBAAgB,CAAC;EAErCR,SAAS,CACP,YAA6B;IAC3B,IAAI2B,aAAa,IAAI,IAAI,EAAE;MACzBR,OAAO,GAAG;QAAES,IAAI,EAAE,SAAS;QAAEF,KAAK,EAAEC;MAAc,CAAC,CAAC;IACtD;EACF,CAAC,EACD,CAACA,aAAa,EAAER,OAAO,CACzB,CAAC;EAED,MAAMU,UAAU,GAAGN,QAAQ,EAAEM,UAAU;EAEvC,MAAMC,wBAAwB,GAAG7B,OAAO,CACtC,MACES,oBAAoB,KACda,QAAQ,IAAI,IAAI,GAChBlB,0BAA0B,CAAIkB,QAAQ,CAAC,GACvCjB,uBAAuB,CAAC,EAChC,CAACI,oBAAoB,EAAEa,QAAQ,CACjC,CAAC;EAED,MAAMQ,wBAAgE,GACpE9B,OAAO,CACL,MACE6B,wBAAwB,CAACE,GAAG,CAAEC,GAAG,KAAM;IACrC,GAAGA,GAAG;IACNC,QAAQ,EAAEC,MAAM,CAACF,GAAG,CAACC,QAAQ,CAAC;IAC9BE,SAAS,EAAEP,UAAU,GAAGM,MAAM,CAACF,GAAG,CAACC,QAAQ,CAAC,CAAC,EAAEN,IAAI;IACnDS,YAAY,EAAEJ,GAAG,CAACI;EACpB,CAAC,CAAC,CAAC,EACL,CAACP,wBAAwB,EAAED,UAAU,CACvC,CAAC;EAEH,MAAMS,eAAe,GAAGvC,WAAW,CAChCwC,QAAiC,IAA8B;IAC9D,MAAMC,OAAgC,GAAG,CAAC,CAAC;IAC3C,KAAK,MAAM,CAACC,GAAG,EAAEC,KAAK,CAAC,IAAIC,MAAM,CAACC,OAAO,CAACL,QAAQ,CAAC,EAAE;MACnDC,OAAO,CAACC,GAAG,CAAC,GAAGrC,gBAAgB,CAACyB,UAAU,GAAGY,GAAG,CAAC,EAAEb,IAAI,EAAEc,KAAK,CAAC;IACjE;IACA,OAAOF,OAAO;EAChB,CAAC,EACD,CAACX,UAAU,CACb,CAAC;EAED,MAAMgB,YAAY,GAAG9C,WAAW,CAC9B,MAAO+C,YAAqC,IAAK;IAC/C,MAAMnC,SAAS,GAAG2B,eAAe,CAACQ,YAAY,CAAiB;IAC/D,IAAI;MACF,IAAI/B,QAAQ,IAAI,IAAI,EAAE;QACpB,MAAMA,QAAQ,CAACJ,SAAS,EAAEU,eAAe,CAAC;MAC5C,CAAC,MAAM;QACL,MAAM0B,MAAM,GAAG,MAAM1B,eAAe,CAACV,SAAS,CAAC;QAC/CO,SAAS,GAAG6B,MAAM,CAAC;MACrB;IACF,CAAC,CAAC,OAAOC,CAAC,EAAE;MACV7B,OAAO,GAAG;QAAES,IAAI,EAAE,YAAY;QAAEF,KAAK,EAAEsB;MAAE,CAAC,CAAC;IAC7C;EACF,CAAC,EACD,CAACV,eAAe,EAAEvB,QAAQ,EAAEM,eAAe,EAAEH,SAAS,EAAEC,OAAO,CACjE,CAAC;EAED,MAAM8B,sBAAsB,GAAGlD,WAAW,CACxC,CAACmC,QAAgB,EAAEQ,KAAc,KAAK;IACpC7B,iBAAiB,GACdqC,IAAI,KACF;MACC,GAAGA,IAAI;MACP,CAAChB,QAAQ,GAAGQ;IACd,CAAC,CACL,CAAC;EACH,CAAC,EACD,CAAC7B,iBAAiB,CACpB,CAAC;EAED,MAAMsC,aAAa,GAAG1C,SAAS,IAAIc,QAAQ,EAAE6B,WAAW,IACnD5C,gBAAgB,CAAC6C,OAAO;EAI7B,MAAMC,WAAW,GAAG;IAClB7C,SAAS,EAAE0C,aAAa;IACxBI,gBAAgB,EAAExB,wBAAwB;IAC1ChB,QAAQ,EAAE8B,YAAY;IACtB/B,gBAAgB;IAChBQ,SAAS;IACTkC,SAAS,EAAE/B;EACb,CAAC;EAED,IAXqBb,mBAAmB,IAAI,IAAI,EAW9B;IAChB,oBACEd,KAAA,CAAA2D,aAAA,CAACtD,QAAQ,EAAAuD,QAAA,KACHJ,WAAW;MACf3C,SAAS,EAAEC,mBAAoB;MAC/B+C,kBAAkB,EAAEV;IAAuB,EAC5C,CAAC;EAEN;EAEA,oBAAOnD,KAAA,CAAA2D,aAAA,CAACtD,QAAQ,EAAKmD,WAAc,CAAC;AACtC,CAAC,CAAC","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
|
|
1
|
+
{"version":3,"file":"ActionFormApi.js","names":[],"sources":["ActionFormApi.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n ActionDefinition,\n ActionEditResponse,\n ActionValidationResponse,\n} from \"@osdk/api\";\nimport type { ActionValidationError } from \"@osdk/client\";\nimport type {\n ActionParameters,\n FieldKey,\n FieldValueType,\n FormFieldDefinition,\n RendererFieldDefinition,\n} from \"./FormFieldApi.js\";\n\n/**\n * Props for the ActionForm component.\n *\n * A discriminated union ensures that controlled mode (formState provided)\n * always requires onFormStateChange, and uncontrolled mode makes `onFormStateChange` optional\n */\nexport type ActionFormProps<Q extends ActionDefinition<unknown>> =\n | (ActionFormConfigProps<Q> & {\n formState: FormState<Q>;\n onFormStateChange: (\n updater: (prevState: FormState<Q>) => FormState<Q>,\n ) => void;\n })\n | (ActionFormConfigProps<Q> & {\n formState?: undefined;\n onFormStateChange?: (\n updater: (prevState: FormState<Q>) => FormState<Q>,\n ) => void;\n });\n\ninterface ActionFormConfigProps<Q extends ActionDefinition<unknown>>\n extends Pick<BaseFormProps, \"formTitle\" | \"isSubmitDisabled\">\n{\n actionDefinition: Q;\n\n /**\n * If not supplied, field definitions are constructed from `ActionParameters`.\n */\n formFieldDefinitions?: ReadonlyArray<FormFieldDefinition<Q>>;\n\n /**\n * If supplied, this will override the default submit action\n * By default, the action's applyAction will be called\n *\n * @param formState all field values when onSubmit is called\n * @param applyAction the function to execute the action\n * @returns a promise of the submission response\n */\n onSubmit?: (\n formState: FormState<Q>,\n applyAction: (\n args: ActionParameters<Q>,\n ) => Promise<ActionEditResponse | undefined>,\n ) => Promise<unknown> | void;\n\n /**\n * Called when the validation response is returned from a validateOnly submission\n *\n * @param results the validation response\n */\n onValidationResponse?: (results: ActionValidationResponse) => void;\n\n /**\n * Called when the action is successfully executed from a non-validateOnly submission\n *\n * @param results the submission response\n */\n onSuccess?: (results: ActionEditResponse | undefined) => void;\n\n /**\n * Called when there is an error in form submission\n *\n * @param error the error that occurred\n */\n onError?: (error: FormError) => void;\n}\n\n/**\n * Form values mapping parameter names to their values\n */\nexport type FormState<Q extends ActionDefinition<unknown>> = {\n [K in FieldKey<Q>]?: FieldValueType<Q, K>;\n};\n\n/**\n * Form error discriminated union\n */\nexport type FormError =\n | { type: \"validation\"; error: ActionValidationError }\n | { type: \"submission\"; error: unknown }\n | { type: \"unknown\"; error: unknown };\n\n/**\n * Props for the `BaseForm` component, which renders a form without\n * OSDK data fetching.\n *\n * Uses a discriminated union so that controlled mode (`formState` provided)\n * always requires `onFieldValueChange`, and uncontrolled mode omits both.\n * `onSubmit` receives the current form state so callers can access values\n * even in uncontrolled mode.\n */\nexport type BaseFormProps =\n & BaseFormCommonProps\n & (\n | {\n formState: Record<string, unknown>;\n onFieldValueChange: (fieldKey: string, value: unknown) => void;\n }\n | {\n formState?: undefined;\n onFieldValueChange?: (fieldKey: string, value: unknown) => void;\n }\n );\n\ninterface BaseFormCommonProps {\n formTitle?: string;\n fieldDefinitions: ReadonlyArray<RendererFieldDefinition>;\n onSubmit: (formState: Record<string, unknown>) => void;\n isSubmitDisabled?: boolean;\n isPending?: boolean;\n isLoading?: boolean;\n}\n"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import React, { memo, useCallback, useMemo } from "react";
|
|
18
|
+
import { useForm } from "react-hook-form";
|
|
19
|
+
import { FieldBridge } from "./fields/FieldBridge.js";
|
|
20
|
+
export const BaseForm = /*#__PURE__*/memo(function ({
|
|
21
|
+
formTitle,
|
|
22
|
+
fieldDefinitions,
|
|
23
|
+
formState: controlledFormState,
|
|
24
|
+
onFieldValueChange,
|
|
25
|
+
onSubmit,
|
|
26
|
+
isSubmitDisabled = false,
|
|
27
|
+
isPending = false,
|
|
28
|
+
isLoading = false
|
|
29
|
+
}) {
|
|
30
|
+
const defaultValues = useMemo(() => buildDefaultValues(fieldDefinitions), [fieldDefinitions]);
|
|
31
|
+
const {
|
|
32
|
+
control,
|
|
33
|
+
handleSubmit: rhfHandleSubmit
|
|
34
|
+
} = useForm({
|
|
35
|
+
...(controlledFormState != null ? {
|
|
36
|
+
values: controlledFormState
|
|
37
|
+
} : {
|
|
38
|
+
defaultValues
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
const onFormSubmit = useCallback(rhfValues => {
|
|
42
|
+
// In controlled mode, always submit the controlled state, not RHF's
|
|
43
|
+
// internal state. Between a user keystroke and the parent re-rendering,
|
|
44
|
+
// RHF's store may hold the user-typed value rather than the parent's
|
|
45
|
+
// value. Using controlledFormState directly preserves the existing
|
|
46
|
+
// guarantee that controlled mode submits the parent's state.
|
|
47
|
+
onSubmit(controlledFormState ?? rhfValues);
|
|
48
|
+
}, [onSubmit, controlledFormState]);
|
|
49
|
+
return /*#__PURE__*/React.createElement("form", {
|
|
50
|
+
onSubmit: rhfHandleSubmit(onFormSubmit),
|
|
51
|
+
"data-testid": "action-form"
|
|
52
|
+
}, formTitle != null && /*#__PURE__*/React.createElement("h2", {
|
|
53
|
+
"data-testid": "form-title"
|
|
54
|
+
}, formTitle), isLoading && fieldDefinitions.length === 0 && /*#__PURE__*/React.createElement("div", {
|
|
55
|
+
"data-testid": "form-loading"
|
|
56
|
+
}, "Loading form fields..."), fieldDefinitions.map(fieldDef => /*#__PURE__*/React.createElement(FieldBridge, {
|
|
57
|
+
key: fieldDef.fieldKey,
|
|
58
|
+
fieldDef: fieldDef,
|
|
59
|
+
control: control,
|
|
60
|
+
onExternalChange: onFieldValueChange
|
|
61
|
+
})), /*#__PURE__*/React.createElement("button", {
|
|
62
|
+
type: "submit",
|
|
63
|
+
disabled: isSubmitDisabled || isPending,
|
|
64
|
+
"data-testid": "submit-button"
|
|
65
|
+
}, isPending ? "Submitting..." : "Submit"));
|
|
66
|
+
});
|
|
67
|
+
function buildDefaultValues(fieldDefinitions) {
|
|
68
|
+
const values = {};
|
|
69
|
+
for (const def of fieldDefinitions) {
|
|
70
|
+
values[def.fieldKey] = def.defaultValue;
|
|
71
|
+
}
|
|
72
|
+
return values;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=BaseForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseForm.js","names":["React","memo","useCallback","useMemo","useForm","FieldBridge","BaseForm","formTitle","fieldDefinitions","formState","controlledFormState","onFieldValueChange","onSubmit","isSubmitDisabled","isPending","isLoading","defaultValues","buildDefaultValues","control","handleSubmit","rhfHandleSubmit","values","onFormSubmit","rhfValues","createElement","length","map","fieldDef","key","fieldKey","onExternalChange","type","disabled","def","defaultValue"],"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 React, { memo, useCallback, useMemo } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport type { BaseFormProps } from \"./ActionFormApi.js\";\nimport { FieldBridge } from \"./fields/FieldBridge.js\";\nimport type { RendererFieldDefinition } from \"./FormFieldApi.js\";\n\nexport const BaseForm: React.FC<BaseFormProps> = memo(function BaseFormFn({\n formTitle,\n fieldDefinitions,\n formState: controlledFormState,\n onFieldValueChange,\n onSubmit,\n isSubmitDisabled = false,\n isPending = false,\n isLoading = false,\n}: BaseFormProps): React.ReactElement {\n const isControlled = controlledFormState != null;\n\n const defaultValues = useMemo(\n () => buildDefaultValues(fieldDefinitions),\n [fieldDefinitions],\n );\n\n const { control, handleSubmit: rhfHandleSubmit } = useForm<\n Record<string, unknown>\n >({\n ...(isControlled ? { values: controlledFormState } : { defaultValues }),\n });\n\n const onFormSubmit = useCallback(\n (rhfValues: Record<string, unknown>) => {\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 onSubmit(controlledFormState ?? rhfValues);\n },\n [onSubmit, controlledFormState],\n );\n\n return (\n <form onSubmit={rhfHandleSubmit(onFormSubmit)} data-testid=\"action-form\">\n {formTitle != null && <h2 data-testid=\"form-title\">{formTitle}</h2>}\n {isLoading && fieldDefinitions.length === 0 && (\n <div data-testid=\"form-loading\">Loading form fields...</div>\n )}\n {fieldDefinitions.map((fieldDef) => (\n <FieldBridge\n key={fieldDef.fieldKey}\n fieldDef={fieldDef}\n control={control}\n onExternalChange={onFieldValueChange}\n />\n ))}\n <button\n type=\"submit\"\n disabled={isSubmitDisabled || isPending}\n data-testid=\"submit-button\"\n >\n {isPending ? \"Submitting...\" : \"Submit\"}\n </button>\n </form>\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 values[def.fieldKey] = def.defaultValue;\n }\n return values;\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAOA,KAAK,IAAIC,IAAI,EAAEC,WAAW,EAAEC,OAAO,QAAQ,OAAO;AACzD,SAASC,OAAO,QAAQ,iBAAiB;AAEzC,SAASC,WAAW,QAAQ,yBAAyB;AAGrD,OAAO,MAAMC,QAAiC,gBAAGL,IAAI,CAAC,UAAoB;EACxEM,SAAS;EACTC,gBAAgB;EAChBC,SAAS,EAAEC,mBAAmB;EAC9BC,kBAAkB;EAClBC,QAAQ;EACRC,gBAAgB,GAAG,KAAK;EACxBC,SAAS,GAAG,KAAK;EACjBC,SAAS,GAAG;AACC,CAAC,EAAsB;EAGpC,MAAMC,aAAa,GAAGb,OAAO,CAC3B,MAAMc,kBAAkB,CAACT,gBAAgB,CAAC,EAC1C,CAACA,gBAAgB,CACnB,CAAC;EAED,MAAM;IAAEU,OAAO;IAAEC,YAAY,EAAEC;EAAgB,CAAC,GAAGhB,OAAO,CAExD;IACA,IAVmBM,mBAAmB,IAAI,IAAI,GAU3B;MAAEW,MAAM,EAAEX;IAAoB,CAAC,GAAG;MAAEM;IAAc,CAAC;EACxE,CAAC,CAAC;EAEF,MAAMM,YAAY,GAAGpB,WAAW,CAC7BqB,SAAkC,IAAK;IACtC;IACA;IACA;IACA;IACA;IACAX,QAAQ,CAACF,mBAAmB,IAAIa,SAAS,CAAC;EAC5C,CAAC,EACD,CAACX,QAAQ,EAAEF,mBAAmB,CAChC,CAAC;EAED,oBACEV,KAAA,CAAAwB,aAAA;IAAMZ,QAAQ,EAAEQ,eAAe,CAACE,YAAY,CAAE;IAAC,eAAY;EAAa,GACrEf,SAAS,IAAI,IAAI,iBAAIP,KAAA,CAAAwB,aAAA;IAAI,eAAY;EAAY,GAAEjB,SAAc,CAAC,EAClEQ,SAAS,IAAIP,gBAAgB,CAACiB,MAAM,KAAK,CAAC,iBACzCzB,KAAA,CAAAwB,aAAA;IAAK,eAAY;EAAc,GAAC,wBAA2B,CAC5D,EACAhB,gBAAgB,CAACkB,GAAG,CAAEC,QAAQ,iBAC7B3B,KAAA,CAAAwB,aAAA,CAACnB,WAAW;IACVuB,GAAG,EAAED,QAAQ,CAACE,QAAS;IACvBF,QAAQ,EAAEA,QAAS;IACnBT,OAAO,EAAEA,OAAQ;IACjBY,gBAAgB,EAAEnB;EAAmB,CACtC,CACF,CAAC,eACFX,KAAA,CAAAwB,aAAA;IACEO,IAAI,EAAC,QAAQ;IACbC,QAAQ,EAAEnB,gBAAgB,IAAIC,SAAU;IACxC,eAAY;EAAe,GAE1BA,SAAS,GAAG,eAAe,GAAG,QACzB,CACJ,CAAC;AAEX,CAAC,CAAC;AAEF,SAASG,kBAAkBA,CACzBT,gBAAwD,EAC/B;EACzB,MAAMa,MAA+B,GAAG,CAAC,CAAC;EAC1C,KAAK,MAAMY,GAAG,IAAIzB,gBAAgB,EAAE;IAClCa,MAAM,CAACY,GAAG,CAACJ,QAAQ,CAAC,GAAGI,GAAG,CAACC,YAAY;EACzC;EACA,OAAOb,MAAM;AACf","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormFieldApi.js","names":[],"sources":["FormFieldApi.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 CompileTimeMetadata,\n ObjectSet,\n ObjectTypeDefinition,\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 V extends FieldValueType<Q, K> = FieldValueType<Q, K>,\n C extends ValidFormFieldForPropertyType<V> = ValidFormFieldForPropertyType<V>,\n> {\n /**\n * The field's unique key\n */\n fieldKey: K;\n\n /**\n * Display label for the field\n * If not provided, the form field will not show any label.\n */\n label?: string;\n\n /**\n * Current value of the field\n * If provided, the field operates in controlled mode\n */\n value?: V;\n\n /**\n * Default value of the field\n */\n defaultValue?: V;\n\n /**\n * The form field component type to render\n */\n fieldComponent: C;\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 * The placement of helper text depends on the value of helperTextPlacement prop\n */\n helperText?: string;\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 return a custom error message if validation failed\n *\n * @param validationRule the validation rule that failed with the error message\n * @returns the error message to display\n */\n onValidationError?: (error: ValidationError) => string;\n\n /**\n * Additional function to validate the field\n *\n * @param value the current field value\n * @returns a boolean promise indicating whether the value is valid\n */\n validate?: (value: V) => Promise<boolean>;\n\n /**\n * The component props for the form field\n * Excludes runtime props (key, onChange) which are managed by ActionForm\n */\n fieldComponentProps?: Omit<FormFieldPropsByType[C], \"key\" | \"onChange\">;\n}\n\ntype ValidationError = { type: ValidationRule; error: string };\n\ntype ValidationRule =\n | \"required\"\n | \"min\"\n | \"max\"\n | \"minLength\"\n | \"maxLength\"\n | \"pattern\"\n | \"validate\";\n\n/**\n * Maps field types to their corresponding props\n */\nexport interface FormFieldPropsByType {\n DATETIME_PICKER: DatetimePickerFieldProps;\n DROPDOWN: DropdownFieldProps<unknown>;\n FILE_PICKER: FilePickerProps;\n NUMBER_INPUT: NumberInputFieldProps;\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 */\nexport interface DatetimePickerFieldProps extends BaseFormFieldProps<Date> {\n fieldComponent: \"DATETIME_PICKER\";\n\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 * Function to format the date string\n */\n formatDate?: (date: Date) => string;\n}\n\n/**\n * Dropdown field props with selectable options\n */\nexport interface DropdownFieldProps<V, IsMulti extends boolean = false>\n extends BaseFormFieldProps<IsMulti extends true ? V[] : V>\n{\n fieldComponent: \"DROPDOWN\";\n\n /**\n * Available options for the dropdown\n */\n options: Option<V>[];\n\n /**\n * Whether the dropdown allows searching/filtering\n */\n isSearchable?: boolean;\n\n /**\n * Whether multiple values can be selected\n */\n isMulti?: boolean;\n}\n\nexport interface FilePickerProps extends BaseFormFieldProps<File | File[]> {\n fieldComponent: \"FILE_PICKER\";\n\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/**\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 fieldComponent: \"TEXT_AREA\";\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 fieldComponent: \"TEXT_INPUT\";\n}\n\n/**\n * Number input field props\n */\nexport interface NumberInputFieldProps extends\n BaseFormFieldProps<number>,\n Pick<\n React.InputHTMLAttributes<HTMLInputElement>,\n /**\n * If provided, this will be added to the field validation\n */\n | \"min\"\n /**\n * If provided, this will be added to the field validation\n */\n | \"max\"\n | \"step\"\n >\n{\n fieldComponent: \"NUMBER_INPUT\";\n}\n\n/**\n * Radio buttons field props\n */\nexport interface RadioButtonsFieldProps<V> extends BaseFormFieldProps<V> {\n fieldComponent: \"RADIO_BUTTONS\";\n\n /**\n * Available options for radio buttons\n */\n options: Option<V>[];\n}\n\n/**\n * Option interface for dropdown and radio 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 BaseFormFieldProps<ObjectSet<T>>\n{\n fieldComponent: \"OBJECT_SET\";\n}\n\n/**\n * Custom field props for user-defined renderers\n */\nexport interface CustomFieldProps<V> extends BaseFormFieldProps<V> {\n fieldComponent: \"CUSTOM\";\n\n /**\n * Custom renderer function\n */\n customRenderer: (props: BaseFormFieldProps<V>) => React.ReactNode;\n}\n\nexport interface BaseFormFieldProps<V> {\n fieldComponent: FieldComponent;\n\n /**\n * Called when the field value changed.\n *\n * ActionForm internally wraps this to pass the key to onFieldValueChanged:\n * ```\n * <DropdownField\n * {...fieldDef}\n * onChange={(value) => onFieldValueChanged(fieldDef.key, value)}\n * />\n * ```\n *\n * @param value The new value of the form field\n */\n onChange?: (value?: V) => 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 */\nexport type FieldValueType<\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 | \"DATETIME_PICKER\"\n | \"DROPDOWN\"\n | \"FILE_PICKER\"\n | \"NUMBER_INPUT\"\n | \"RADIO_BUTTONS\"\n | \"OBJECT_SET\"\n | \"TEXT_AREA\"\n | \"TEXT_INPUT\"\n | \"CUSTOM\";\n\n/**\n * Gets valid form field types for a given property type\n */\nexport type ValidFormFieldForPropertyType<P extends FieldValueType> = P extends\n \"objectSet\" ? \"OBJECT_SET\"\n : P extends \"object\" ? \"DROPDOWN\"\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":"","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"FormFieldApi.js","names":[],"sources":["FormFieldApi.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 ActionMetadata,\n ActionParam,\n CompileTimeMetadata,\n DataValueClientToWire,\n ObjectSet,\n ObjectTypeDefinition,\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 * If not provided, the form field will not show any label.\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 * The placement of helper text depends on the value of helperTextPlacement prop\n */\n helperText?: string;\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 return a custom error message if validation failed\n *\n * @param validationRule the validation rule that failed with the error message\n * @returns the error message to display\n */\n onValidationError?: (error: ValidationError) => string;\n\n /**\n * Additional function to validate the field\n *\n * @param value the current field value\n * @returns a boolean promise indicating whether the value is valid\n */\n validate?: (value: FieldValueType<Q, K>) => Promise<boolean>;\n\n /**\n * The component props for the form field\n * Excludes runtime props (key, onChange) which are managed by ActionForm\n */\n fieldComponentProps?: Omit<\n FormFieldPropsByType[\n ValidFormFieldForPropertyType<FieldDescriptorType<Q, K>>\n ],\n \"key\" | \"onChange\"\n >;\n}\n\ntype ValidationError = { type: ValidationRule; error: string };\n\ntype ValidationRule =\n | \"required\"\n | \"min\"\n | \"max\"\n | \"minLength\"\n | \"maxLength\"\n | \"pattern\"\n | \"validate\";\n\n/**\n * Maps field types to their corresponding props\n */\nexport interface FormFieldPropsByType {\n DATETIME_PICKER: DatetimePickerFieldProps;\n DROPDOWN: DropdownFieldProps<unknown>;\n FILE_PICKER: FilePickerProps;\n NUMBER_INPUT: NumberInputFieldProps;\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 */\nexport interface DatetimePickerFieldProps extends BaseFormFieldProps<Date> {\n fieldComponent: \"DATETIME_PICKER\";\n\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 * Function to format the date string\n */\n formatDate?: (date: Date) => string;\n}\n\n/**\n * Dropdown field props with selectable options\n */\nexport interface DropdownFieldProps<V, IsMulti extends boolean = false>\n extends BaseFormFieldProps<IsMulti extends true ? V[] : V>\n{\n fieldComponent: \"DROPDOWN\";\n\n /**\n * Available options for the dropdown\n */\n options: Option<V>[];\n\n /**\n * Whether the dropdown allows searching/filtering\n */\n isSearchable?: boolean;\n\n /**\n * Whether multiple values can be selected\n */\n isMulti?: boolean;\n}\n\nexport interface FilePickerProps extends BaseFormFieldProps<File | File[]> {\n fieldComponent: \"FILE_PICKER\";\n\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/**\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 fieldComponent: \"TEXT_AREA\";\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 fieldComponent: \"TEXT_INPUT\";\n placeholder?: string;\n}\n\n/**\n * Number input field props\n */\nexport interface NumberInputFieldProps extends\n BaseFormFieldProps<number>,\n Pick<\n React.InputHTMLAttributes<HTMLInputElement>,\n /**\n * If provided, this will be added to the field validation\n */\n | \"min\"\n /**\n * If provided, this will be added to the field validation\n */\n | \"max\"\n | \"step\"\n >\n{\n fieldComponent: \"NUMBER_INPUT\";\n}\n\n/**\n * Radio buttons field props\n */\nexport interface RadioButtonsFieldProps<V> extends BaseFormFieldProps<V> {\n fieldComponent: \"RADIO_BUTTONS\";\n\n /**\n * Available options for radio buttons\n */\n options: Option<V>[];\n}\n\n/**\n * Option interface for dropdown and radio 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 BaseFormFieldProps<ObjectSet<T>>\n{\n fieldComponent: \"OBJECT_SET\";\n}\n\n/**\n * Custom field props for user-defined renderers\n */\nexport interface CustomFieldProps<V> extends BaseFormFieldProps<V> {\n fieldComponent: \"CUSTOM\";\n\n /**\n * Custom renderer function\n */\n customRenderer: (props: BaseFormFieldProps<V>) => React.ReactNode;\n}\n\nexport interface BaseFormFieldProps<V> {\n /**\n * The value of the form field\n */\n value: V;\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) => 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\n ActionMetadata.DataType.ObjectSet<infer T> ? ActionParam.ObjectSetType<T>\n : ActionParameters<Q>[K][\"type\"] extends\n ActionMetadata.DataType.Struct<infer T> ? 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 | \"DATETIME_PICKER\"\n | \"DROPDOWN\"\n | \"FILE_PICKER\"\n | \"NUMBER_INPUT\"\n | \"RADIO_BUTTONS\"\n | \"OBJECT_SET\"\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 * 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 */\nexport interface RendererFieldDefinition {\n fieldKey: string;\n fieldComponent: FieldComponent;\n fieldType?: FieldType;\n label?: string;\n defaultValue?: unknown;\n isRequired?: boolean;\n placeholder?: string;\n helperText?: string;\n helperTextPlacement?: \"bottom\" | \"tooltip\";\n fieldComponentProps?: Record<string, unknown>;\n}\n\n/**\n * Gets valid form field types for a given property type\n */\nexport type ValidFormFieldForPropertyType<P extends FieldDescriptorType> =\n P extends \"objectSet\" ? \"OBJECT_SET\"\n : P extends \"object\" ? \"DROPDOWN\"\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":"","ignoreList":[]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import React, { memo, useCallback } from "react";
|
|
18
|
+
import { useController } from "react-hook-form";
|
|
19
|
+
import { FormFieldRenderer } from "./FormFieldRenderer.js";
|
|
20
|
+
export const FieldBridge = /*#__PURE__*/memo(function ({
|
|
21
|
+
fieldDef,
|
|
22
|
+
control,
|
|
23
|
+
onExternalChange
|
|
24
|
+
}) {
|
|
25
|
+
const {
|
|
26
|
+
field: {
|
|
27
|
+
onChange,
|
|
28
|
+
value
|
|
29
|
+
}
|
|
30
|
+
} = useController({
|
|
31
|
+
name: fieldDef.fieldKey,
|
|
32
|
+
control,
|
|
33
|
+
defaultValue: fieldDef.defaultValue
|
|
34
|
+
});
|
|
35
|
+
const handleChange = useCallback(newValue => {
|
|
36
|
+
onChange(newValue);
|
|
37
|
+
onExternalChange?.(fieldDef.fieldKey, newValue);
|
|
38
|
+
}, [onChange, onExternalChange, fieldDef.fieldKey]);
|
|
39
|
+
return /*#__PURE__*/React.createElement(FormFieldRenderer, {
|
|
40
|
+
value: value,
|
|
41
|
+
fieldDefinition: fieldDef,
|
|
42
|
+
onFieldValueChange: handleChange
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=FieldBridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldBridge.js","names":["React","memo","useCallback","useController","FormFieldRenderer","FieldBridge","fieldDef","control","onExternalChange","field","onChange","value","name","fieldKey","defaultValue","handleChange","newValue","createElement","fieldDefinition","onFieldValueChange"],"sources":["FieldBridge.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 React, { memo, useCallback } from \"react\";\nimport type { Control } from \"react-hook-form\";\nimport { useController } from \"react-hook-form\";\nimport type { RendererFieldDefinition } from \"../FormFieldApi.js\";\nimport { FormFieldRenderer } from \"./FormFieldRenderer.js\";\n\nexport interface FieldBridgeProps {\n fieldDef: RendererFieldDefinition;\n control: Control<Record<string, unknown>>;\n onExternalChange?: (fieldKey: string, value: unknown) => void;\n}\nexport const FieldBridge: React.FC<FieldBridgeProps> = memo(\n function FieldBridgeFn({\n fieldDef,\n control,\n onExternalChange,\n }: FieldBridgeProps): React.ReactElement {\n const {\n field: { onChange, value },\n } = useController({\n name: fieldDef.fieldKey,\n control,\n defaultValue: fieldDef.defaultValue,\n });\n\n const handleChange = useCallback(\n (newValue: unknown) => {\n onChange(newValue);\n onExternalChange?.(fieldDef.fieldKey, newValue);\n },\n [onChange, onExternalChange, fieldDef.fieldKey],\n );\n\n return (\n <FormFieldRenderer\n value={value}\n fieldDefinition={fieldDef}\n onFieldValueChange={handleChange}\n />\n );\n },\n);\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAOA,KAAK,IAAIC,IAAI,EAAEC,WAAW,QAAQ,OAAO;AAEhD,SAASC,aAAa,QAAQ,iBAAiB;AAE/C,SAASC,iBAAiB,QAAQ,wBAAwB;AAO1D,OAAO,MAAMC,WAAuC,gBAAGJ,IAAI,CACzD,UAAuB;EACrBK,QAAQ;EACRC,OAAO;EACPC;AACgB,CAAC,EAAsB;EACvC,MAAM;IACJC,KAAK,EAAE;MAAEC,QAAQ;MAAEC;IAAM;EAC3B,CAAC,GAAGR,aAAa,CAAC;IAChBS,IAAI,EAAEN,QAAQ,CAACO,QAAQ;IACvBN,OAAO;IACPO,YAAY,EAAER,QAAQ,CAACQ;EACzB,CAAC,CAAC;EAEF,MAAMC,YAAY,GAAGb,WAAW,CAC7Bc,QAAiB,IAAK;IACrBN,QAAQ,CAACM,QAAQ,CAAC;IAClBR,gBAAgB,GAAGF,QAAQ,CAACO,QAAQ,EAAEG,QAAQ,CAAC;EACjD,CAAC,EACD,CAACN,QAAQ,EAAEF,gBAAgB,EAAEF,QAAQ,CAACO,QAAQ,CAChD,CAAC;EAED,oBACEb,KAAA,CAAAiB,aAAA,CAACb,iBAAiB;IAChBO,KAAK,EAAEA,KAAM;IACbO,eAAe,EAAEZ,QAAS;IAC1Ba,kBAAkB,EAAEJ;EAAa,CAClC,CAAC;AAEN,CACF,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import React, { memo } from "react";
|
|
18
|
+
import { TextInputField } from "./TextInputField.js";
|
|
19
|
+
export const FormFieldRenderer = /*#__PURE__*/memo(function ({
|
|
20
|
+
fieldDefinition,
|
|
21
|
+
value,
|
|
22
|
+
onFieldValueChange
|
|
23
|
+
}) {
|
|
24
|
+
const {
|
|
25
|
+
label,
|
|
26
|
+
isRequired,
|
|
27
|
+
helperText,
|
|
28
|
+
helperTextPlacement
|
|
29
|
+
} = fieldDefinition;
|
|
30
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
31
|
+
"data-testid": `form-field-${fieldDefinition.fieldKey}`
|
|
32
|
+
}, label != null && /*#__PURE__*/React.createElement("label", null, label, isRequired === true && /*#__PURE__*/React.createElement("span", {
|
|
33
|
+
"aria-label": "required"
|
|
34
|
+
}, "*")), renderFieldComponent(fieldDefinition, value, onFieldValueChange), helperText != null && helperTextPlacement !== "tooltip" && /*#__PURE__*/React.createElement("div", {
|
|
35
|
+
"data-testid": "helper-text"
|
|
36
|
+
}, helperText));
|
|
37
|
+
});
|
|
38
|
+
function renderFieldComponent(fieldDefinition, value, onChange) {
|
|
39
|
+
switch (fieldDefinition.fieldComponent) {
|
|
40
|
+
case "TEXT_INPUT":
|
|
41
|
+
// TODO: Render a <textarea> for TEXT_AREA instead of falling through
|
|
42
|
+
case "TEXT_AREA":
|
|
43
|
+
return /*#__PURE__*/React.createElement(TextInputField, {
|
|
44
|
+
fieldComponent: "TEXT_INPUT"
|
|
45
|
+
// TODO: Use coerceFieldValue
|
|
46
|
+
,
|
|
47
|
+
value: value != null ? String(value) : "",
|
|
48
|
+
onChange: onChange,
|
|
49
|
+
placeholder: fieldDefinition.placeholder
|
|
50
|
+
});
|
|
51
|
+
case "NUMBER_INPUT":
|
|
52
|
+
case "DROPDOWN":
|
|
53
|
+
case "RADIO_BUTTONS":
|
|
54
|
+
case "DATETIME_PICKER":
|
|
55
|
+
case "FILE_PICKER":
|
|
56
|
+
case "OBJECT_SET":
|
|
57
|
+
case "CUSTOM":
|
|
58
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
59
|
+
"data-testid": "unsupported-field"
|
|
60
|
+
}, "Unsupported field type: ", fieldDefinition.fieldComponent);
|
|
61
|
+
default:
|
|
62
|
+
return assertUnreachableFieldComponent(fieldDefinition.fieldComponent);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function assertUnreachableFieldComponent(value) {
|
|
66
|
+
throw new Error(`Unhandled field component: ${String(value)}`);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=FormFieldRenderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormFieldRenderer.js","names":["React","memo","TextInputField","FormFieldRenderer","fieldDefinition","value","onFieldValueChange","label","isRequired","helperText","helperTextPlacement","createElement","fieldKey","renderFieldComponent","onChange","fieldComponent","String","placeholder","assertUnreachableFieldComponent","Error"],"sources":["FormFieldRenderer.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 React, { memo } from \"react\";\nimport type { RendererFieldDefinition } from \"../FormFieldApi.js\";\nimport { TextInputField } from \"./TextInputField.js\";\n\nexport interface FormFieldRendererProps {\n fieldDefinition: RendererFieldDefinition;\n value: unknown;\n onFieldValueChange: (value: unknown) => void;\n}\n\nexport const FormFieldRenderer: React.FC<FormFieldRendererProps> = memo(\n function FormFieldRendererFn({\n fieldDefinition,\n value,\n onFieldValueChange,\n }: FormFieldRendererProps): React.ReactElement {\n const { label, isRequired, helperText, helperTextPlacement } =\n fieldDefinition;\n\n return (\n <div data-testid={`form-field-${fieldDefinition.fieldKey}`}>\n {label != null && (\n <label>\n {label}\n {isRequired === true && <span aria-label=\"required\">*</span>}\n </label>\n )}\n {renderFieldComponent(fieldDefinition, value, onFieldValueChange)}\n {helperText != null && helperTextPlacement !== \"tooltip\" && (\n <div data-testid=\"helper-text\">{helperText}</div>\n )}\n </div>\n );\n },\n);\n\nfunction renderFieldComponent(\n fieldDefinition: RendererFieldDefinition,\n value: unknown,\n onChange: (value: unknown) => void,\n): React.ReactElement {\n switch (fieldDefinition.fieldComponent) {\n case \"TEXT_INPUT\":\n // TODO: Render a <textarea> for TEXT_AREA instead of falling through\n case \"TEXT_AREA\":\n return (\n <TextInputField\n fieldComponent=\"TEXT_INPUT\"\n // TODO: Use coerceFieldValue\n value={value != null ? String(value) : \"\"}\n onChange={onChange}\n placeholder={fieldDefinition.placeholder}\n />\n );\n case \"NUMBER_INPUT\":\n case \"DROPDOWN\":\n case \"RADIO_BUTTONS\":\n case \"DATETIME_PICKER\":\n case \"FILE_PICKER\":\n case \"OBJECT_SET\":\n case \"CUSTOM\":\n return (\n <div data-testid=\"unsupported-field\">\n Unsupported field type: {fieldDefinition.fieldComponent}\n </div>\n );\n default:\n return assertUnreachableFieldComponent(fieldDefinition.fieldComponent);\n }\n}\n\nfunction assertUnreachableFieldComponent(value: never): never {\n throw new Error(`Unhandled field component: ${String(value)}`);\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAOA,KAAK,IAAIC,IAAI,QAAQ,OAAO;AAEnC,SAASC,cAAc,QAAQ,qBAAqB;AAQpD,OAAO,MAAMC,iBAAmD,gBAAGF,IAAI,CACrE,UAA6B;EAC3BG,eAAe;EACfC,KAAK;EACLC;AACsB,CAAC,EAAsB;EAC7C,MAAM;IAAEC,KAAK;IAAEC,UAAU;IAAEC,UAAU;IAAEC;EAAoB,CAAC,GAC1DN,eAAe;EAEjB,oBACEJ,KAAA,CAAAW,aAAA;IAAK,eAAa,cAAcP,eAAe,CAACQ,QAAQ;EAAG,GACxDL,KAAK,IAAI,IAAI,iBACZP,KAAA,CAAAW,aAAA,gBACGJ,KAAK,EACLC,UAAU,KAAK,IAAI,iBAAIR,KAAA,CAAAW,aAAA;IAAM,cAAW;EAAU,GAAC,GAAO,CACtD,CACR,EACAE,oBAAoB,CAACT,eAAe,EAAEC,KAAK,EAAEC,kBAAkB,CAAC,EAChEG,UAAU,IAAI,IAAI,IAAIC,mBAAmB,KAAK,SAAS,iBACtDV,KAAA,CAAAW,aAAA;IAAK,eAAY;EAAa,GAAEF,UAAgB,CAE/C,CAAC;AAEV,CACF,CAAC;AAED,SAASI,oBAAoBA,CAC3BT,eAAwC,EACxCC,KAAc,EACdS,QAAkC,EACd;EACpB,QAAQV,eAAe,CAACW,cAAc;IACpC,KAAK,YAAY;IACjB;IACA,KAAK,WAAW;MACd,oBACEf,KAAA,CAAAW,aAAA,CAACT,cAAc;QACba,cAAc,EAAC;QACf;QAAA;QACAV,KAAK,EAAEA,KAAK,IAAI,IAAI,GAAGW,MAAM,CAACX,KAAK,CAAC,GAAG,EAAG;QAC1CS,QAAQ,EAAEA,QAAS;QACnBG,WAAW,EAAEb,eAAe,CAACa;MAAY,CAC1C,CAAC;IAEN,KAAK,cAAc;IACnB,KAAK,UAAU;IACf,KAAK,eAAe;IACpB,KAAK,iBAAiB;IACtB,KAAK,aAAa;IAClB,KAAK,YAAY;IACjB,KAAK,QAAQ;MACX,oBACEjB,KAAA,CAAAW,aAAA;QAAK,eAAY;MAAmB,GAAC,0BACX,EAACP,eAAe,CAACW,cACtC,CAAC;IAEV;MACE,OAAOG,+BAA+B,CAACd,eAAe,CAACW,cAAc,CAAC;EAC1E;AACF;AAEA,SAASG,+BAA+BA,CAACb,KAAY,EAAS;EAC5D,MAAM,IAAIc,KAAK,CAAC,8BAA8BH,MAAM,CAACX,KAAK,CAAC,EAAE,CAAC;AAChE","ignoreList":[]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import React, { useCallback } from "react";
|
|
18
|
+
export function TextInputField({
|
|
19
|
+
value,
|
|
20
|
+
onChange,
|
|
21
|
+
placeholder,
|
|
22
|
+
minLength,
|
|
23
|
+
maxLength
|
|
24
|
+
}) {
|
|
25
|
+
const handleChange = useCallback(e => {
|
|
26
|
+
onChange?.(e.target.value);
|
|
27
|
+
}, [onChange]);
|
|
28
|
+
return /*#__PURE__*/React.createElement("input", {
|
|
29
|
+
type: "text",
|
|
30
|
+
value: value,
|
|
31
|
+
onChange: handleChange,
|
|
32
|
+
placeholder: placeholder,
|
|
33
|
+
minLength: minLength,
|
|
34
|
+
maxLength: maxLength,
|
|
35
|
+
"data-testid": "text-input-field"
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=TextInputField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextInputField.js","names":["React","useCallback","TextInputField","value","onChange","placeholder","minLength","maxLength","handleChange","e","target","createElement","type"],"sources":["TextInputField.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 React, { useCallback } from \"react\";\nimport type { TextInputFieldProps } from \"../FormFieldApi.js\";\n\nexport function TextInputField({\n value,\n onChange,\n placeholder,\n minLength,\n maxLength,\n}: TextInputFieldProps): React.ReactElement {\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n onChange?.(e.target.value);\n },\n [onChange],\n );\n\n return (\n <input\n type=\"text\"\n value={value}\n onChange={handleChange}\n placeholder={placeholder}\n minLength={minLength}\n maxLength={maxLength}\n data-testid=\"text-input-field\"\n />\n );\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAOA,KAAK,IAAIC,WAAW,QAAQ,OAAO;AAG1C,OAAO,SAASC,cAAcA,CAAC;EAC7BC,KAAK;EACLC,QAAQ;EACRC,WAAW;EACXC,SAAS;EACTC;AACmB,CAAC,EAAsB;EAC1C,MAAMC,YAAY,GAAGP,WAAW,CAC7BQ,CAAsC,IAAK;IAC1CL,QAAQ,GAAGK,CAAC,CAACC,MAAM,CAACP,KAAK,CAAC;EAC5B,CAAC,EACD,CAACC,QAAQ,CACX,CAAC;EAED,oBACEJ,KAAA,CAAAW,aAAA;IACEC,IAAI,EAAC,MAAM;IACXT,KAAK,EAAEA,KAAM;IACbC,QAAQ,EAAEI,YAAa;IACvBH,WAAW,EAAEA,WAAY;IACzBC,SAAS,EAAEA,SAAU;IACrBC,SAAS,EAAEA,SAAU;IACrB,eAAY;EAAkB,CAC/B,CAAC;AAEN","ignoreList":[]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Coerces a raw form field value to match the expected type for the given
|
|
19
|
+
* field type. Returns `undefined` when coercion fails
|
|
20
|
+
* (e.g. non-numeric string for a number field).
|
|
21
|
+
*
|
|
22
|
+
* Complex types (object, objectSet, struct, interface) are passed through
|
|
23
|
+
* unchanged since they cannot be meaningfully coerced.
|
|
24
|
+
*/
|
|
25
|
+
export function coerceFieldValue(parameterType, rawValue) {
|
|
26
|
+
if (rawValue == null) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
if (parameterType == null) {
|
|
30
|
+
return rawValue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// TODO: Handle complex object types later
|
|
34
|
+
if (typeof parameterType === "object") {
|
|
35
|
+
return rawValue;
|
|
36
|
+
}
|
|
37
|
+
switch (parameterType) {
|
|
38
|
+
case "string":
|
|
39
|
+
case "marking":
|
|
40
|
+
case "geohash":
|
|
41
|
+
case "geoshape":
|
|
42
|
+
case "objectType":
|
|
43
|
+
return typeof rawValue === "string" ? rawValue : String(rawValue);
|
|
44
|
+
case "integer":
|
|
45
|
+
case "long":
|
|
46
|
+
{
|
|
47
|
+
const maybeNumber = extractNumber(rawValue);
|
|
48
|
+
return maybeNumber != null ? Math.trunc(maybeNumber) : undefined;
|
|
49
|
+
}
|
|
50
|
+
case "double":
|
|
51
|
+
{
|
|
52
|
+
return extractNumber(rawValue);
|
|
53
|
+
}
|
|
54
|
+
case "boolean":
|
|
55
|
+
{
|
|
56
|
+
if (typeof rawValue === "boolean") {
|
|
57
|
+
return rawValue;
|
|
58
|
+
}
|
|
59
|
+
if (rawValue === "true") return true;
|
|
60
|
+
if (rawValue === "false") return false;
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
case "datetime":
|
|
64
|
+
case "timestamp":
|
|
65
|
+
{
|
|
66
|
+
if (typeof rawValue === "string") {
|
|
67
|
+
return Number.isNaN(Date.parse(rawValue)) ? undefined : rawValue;
|
|
68
|
+
}
|
|
69
|
+
if (rawValue instanceof Date) {
|
|
70
|
+
return rawValue.toISOString();
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
case "attachment":
|
|
75
|
+
case "mediaReference":
|
|
76
|
+
return rawValue;
|
|
77
|
+
default:
|
|
78
|
+
return rawValue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function extractNumber(rawValue) {
|
|
82
|
+
if (typeof rawValue === "number") {
|
|
83
|
+
return rawValue;
|
|
84
|
+
}
|
|
85
|
+
if (typeof rawValue === "string") {
|
|
86
|
+
const trimmed = rawValue.trim();
|
|
87
|
+
if (trimmed === "") return undefined;
|
|
88
|
+
const parsed = Number(trimmed);
|
|
89
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=coerceFieldValue.js.map
|