@osdk/react-components 0.3.0 → 0.4.0-main-90a01d202dfa1e497125bd6cfcdc8175278fb7ec
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 +14 -0
- package/build/browser/action-form/ActionFormApi.js.map +1 -1
- package/build/browser/action-form/BaseForm.js +100 -12
- package/build/browser/action-form/BaseForm.js.map +1 -1
- package/build/browser/action-form/BaseForm.module.css +21 -1
- package/build/browser/action-form/BaseForm.module.css.js +4 -1
- package/build/browser/action-form/FormField.js +3 -1
- package/build/browser/action-form/FormField.js.map +1 -1
- package/build/browser/action-form/FormFieldApi.js.map +1 -1
- package/build/browser/action-form/fields/BaseInput.module.css +4 -0
- package/build/browser/action-form/fields/DatetimePickerField.js +3 -1
- package/build/browser/action-form/fields/DatetimePickerField.js.map +1 -1
- package/build/browser/action-form/fields/DatetimePickerField.module.css +4 -0
- package/build/browser/action-form/fields/FieldBridge.js +31 -4
- package/build/browser/action-form/fields/FieldBridge.js.map +1 -1
- package/build/browser/action-form/fields/FilePickerField.js +6 -2
- package/build/browser/action-form/fields/FilePickerField.js.map +1 -1
- package/build/browser/action-form/fields/FilePickerField.module.css +4 -0
- package/build/browser/action-form/fields/FormFieldRenderer.js +24 -12
- package/build/browser/action-form/fields/FormFieldRenderer.js.map +1 -1
- package/build/browser/action-form/fields/NumberInputField.js +12 -8
- package/build/browser/action-form/fields/NumberInputField.js.map +1 -1
- package/build/browser/action-form/fields/NumberInputField.module.css +4 -0
- package/build/browser/action-form/fields/TextAreaField.js +3 -1
- package/build/browser/action-form/fields/TextAreaField.js.map +1 -1
- package/build/browser/action-form/fields/TextInputField.js +3 -1
- package/build/browser/action-form/fields/TextInputField.js.map +1 -1
- package/build/browser/action-form/utils/extractValidationRules.js +186 -0
- package/build/browser/action-form/utils/extractValidationRules.js.map +1 -0
- package/build/browser/public/experimental.js.map +1 -1
- package/build/browser/shared/hooks/useAsyncAction.js +56 -0
- package/build/browser/shared/hooks/useAsyncAction.js.map +1 -0
- package/build/browser/shared/hooks/useIsMounted.js +32 -0
- package/build/browser/shared/hooks/useIsMounted.js.map +1 -0
- package/build/browser/styles.css +37 -1
- package/build/cjs/public/experimental.cjs +361 -37
- package/build/cjs/public/experimental.cjs.map +1 -1
- package/build/cjs/public/experimental.css +30 -1
- package/build/cjs/public/experimental.css.map +1 -1
- package/build/cjs/public/experimental.d.cts +40 -13
- package/build/esm/action-form/ActionFormApi.js.map +1 -1
- package/build/esm/action-form/BaseForm.js +100 -12
- package/build/esm/action-form/BaseForm.js.map +1 -1
- package/build/esm/action-form/BaseForm.module.css +21 -1
- package/build/esm/action-form/FormField.js +3 -1
- package/build/esm/action-form/FormField.js.map +1 -1
- package/build/esm/action-form/FormFieldApi.js.map +1 -1
- package/build/esm/action-form/fields/BaseInput.module.css +4 -0
- package/build/esm/action-form/fields/DatetimePickerField.js +3 -1
- package/build/esm/action-form/fields/DatetimePickerField.js.map +1 -1
- package/build/esm/action-form/fields/DatetimePickerField.module.css +4 -0
- package/build/esm/action-form/fields/FieldBridge.js +31 -4
- package/build/esm/action-form/fields/FieldBridge.js.map +1 -1
- package/build/esm/action-form/fields/FilePickerField.js +6 -2
- package/build/esm/action-form/fields/FilePickerField.js.map +1 -1
- package/build/esm/action-form/fields/FilePickerField.module.css +4 -0
- package/build/esm/action-form/fields/FormFieldRenderer.js +24 -12
- package/build/esm/action-form/fields/FormFieldRenderer.js.map +1 -1
- package/build/esm/action-form/fields/NumberInputField.js +12 -8
- package/build/esm/action-form/fields/NumberInputField.js.map +1 -1
- package/build/esm/action-form/fields/NumberInputField.module.css +4 -0
- package/build/esm/action-form/fields/TextAreaField.js +3 -1
- package/build/esm/action-form/fields/TextAreaField.js.map +1 -1
- package/build/esm/action-form/fields/TextInputField.js +3 -1
- package/build/esm/action-form/fields/TextInputField.js.map +1 -1
- package/build/esm/action-form/utils/extractValidationRules.js +186 -0
- package/build/esm/action-form/utils/extractValidationRules.js.map +1 -0
- package/build/esm/public/experimental.js.map +1 -1
- package/build/esm/shared/hooks/useAsyncAction.js +56 -0
- package/build/esm/shared/hooks/useAsyncAction.js.map +1 -0
- package/build/esm/shared/hooks/useIsMounted.js +32 -0
- package/build/esm/shared/hooks/useIsMounted.js.map +1 -0
- package/build/types/action-form/ActionFormApi.d.ts +1 -1
- package/build/types/action-form/ActionFormApi.d.ts.map +1 -1
- package/build/types/action-form/BaseForm.d.ts.map +1 -1
- package/build/types/action-form/FormField.d.ts +1 -0
- package/build/types/action-form/FormField.d.ts.map +1 -1
- package/build/types/action-form/FormFieldApi.d.ts +39 -12
- package/build/types/action-form/FormFieldApi.d.ts.map +1 -1
- package/build/types/action-form/fields/DatetimePickerField.d.ts +1 -1
- package/build/types/action-form/fields/DatetimePickerField.d.ts.map +1 -1
- package/build/types/action-form/fields/FieldBridge.d.ts.map +1 -1
- package/build/types/action-form/fields/FormFieldRenderer.d.ts +2 -0
- package/build/types/action-form/fields/FormFieldRenderer.d.ts.map +1 -1
- package/build/types/action-form/fields/NumberInputField.d.ts +1 -1
- package/build/types/action-form/fields/NumberInputField.d.ts.map +1 -1
- package/build/types/action-form/fields/TextAreaField.d.ts +1 -1
- package/build/types/action-form/fields/TextAreaField.d.ts.map +1 -1
- package/build/types/action-form/fields/TextInputField.d.ts +1 -1
- package/build/types/action-form/fields/TextInputField.d.ts.map +1 -1
- package/build/types/action-form/utils/extractValidationRules.d.ts +13 -0
- package/build/types/action-form/utils/extractValidationRules.d.ts.map +1 -0
- package/build/types/public/experimental.d.ts +1 -1
- package/build/types/public/experimental.d.ts.map +1 -1
- package/build/types/shared/hooks/useAsyncAction.d.ts +16 -0
- package/build/types/shared/hooks/useAsyncAction.d.ts.map +1 -0
- package/build/types/shared/hooks/useIsMounted.d.ts +5 -0
- package/build/types/shared/hooks/useIsMounted.d.ts.map +1 -0
- package/package.json +8 -8
|
@@ -8801,10 +8801,206 @@ function PdfViewer({
|
|
|
8801
8801
|
}, pdfViewerProps));
|
|
8802
8802
|
}
|
|
8803
8803
|
var typedReactMemo = React75__default.default.memo;
|
|
8804
|
+
function useIsMounted() {
|
|
8805
|
+
const isMountedRef = React75.useRef(true);
|
|
8806
|
+
React75.useEffect(function trackMountedState() {
|
|
8807
|
+
return () => {
|
|
8808
|
+
isMountedRef.current = false;
|
|
8809
|
+
};
|
|
8810
|
+
}, []);
|
|
8811
|
+
return isMountedRef;
|
|
8812
|
+
}
|
|
8813
|
+
|
|
8814
|
+
// src/shared/hooks/useAsyncAction.ts
|
|
8815
|
+
function useAsyncAction(action) {
|
|
8816
|
+
const [isPending, setIsPending] = React75.useState(false);
|
|
8817
|
+
const [error, setError] = React75.useState(void 0);
|
|
8818
|
+
const isMountedRef = useIsMounted();
|
|
8819
|
+
const execute = React75.useCallback(async (...args) => {
|
|
8820
|
+
setError(void 0);
|
|
8821
|
+
setIsPending(true);
|
|
8822
|
+
try {
|
|
8823
|
+
await action(...args);
|
|
8824
|
+
} catch (err) {
|
|
8825
|
+
if (isMountedRef.current) {
|
|
8826
|
+
setError(err);
|
|
8827
|
+
}
|
|
8828
|
+
} finally {
|
|
8829
|
+
if (isMountedRef.current) {
|
|
8830
|
+
setIsPending(false);
|
|
8831
|
+
}
|
|
8832
|
+
}
|
|
8833
|
+
}, [action, isMountedRef]);
|
|
8834
|
+
const clearError = React75.useCallback(() => {
|
|
8835
|
+
setError(void 0);
|
|
8836
|
+
}, []);
|
|
8837
|
+
return {
|
|
8838
|
+
isPending,
|
|
8839
|
+
error,
|
|
8840
|
+
execute,
|
|
8841
|
+
clearError
|
|
8842
|
+
};
|
|
8843
|
+
}
|
|
8804
8844
|
|
|
8805
8845
|
// src/action-form/BaseForm.module.css
|
|
8806
8846
|
var BaseForm_default = {};
|
|
8807
8847
|
|
|
8848
|
+
// src/action-form/utils/extractValidationRules.ts
|
|
8849
|
+
function extractValidationRules(fieldDef) {
|
|
8850
|
+
const rules = {};
|
|
8851
|
+
if (fieldDef.isRequired) {
|
|
8852
|
+
rules.required = getMessage(fieldDef, {
|
|
8853
|
+
type: "required"
|
|
8854
|
+
});
|
|
8855
|
+
}
|
|
8856
|
+
const validateFns = {};
|
|
8857
|
+
switch (fieldDef.fieldComponent) {
|
|
8858
|
+
case "NUMBER_INPUT": {
|
|
8859
|
+
const {
|
|
8860
|
+
min,
|
|
8861
|
+
max
|
|
8862
|
+
} = fieldDef.fieldComponentProps;
|
|
8863
|
+
if (min != null) {
|
|
8864
|
+
const msg = getMessage(fieldDef, {
|
|
8865
|
+
type: "min",
|
|
8866
|
+
min
|
|
8867
|
+
});
|
|
8868
|
+
validateFns.min = (value) => typeof value === "number" && value < min ? msg : true;
|
|
8869
|
+
}
|
|
8870
|
+
if (max != null) {
|
|
8871
|
+
const msg = getMessage(fieldDef, {
|
|
8872
|
+
type: "max",
|
|
8873
|
+
max
|
|
8874
|
+
});
|
|
8875
|
+
validateFns.max = (value) => typeof value === "number" && value > max ? msg : true;
|
|
8876
|
+
}
|
|
8877
|
+
break;
|
|
8878
|
+
}
|
|
8879
|
+
case "TEXT_INPUT":
|
|
8880
|
+
case "TEXT_AREA": {
|
|
8881
|
+
const {
|
|
8882
|
+
minLength,
|
|
8883
|
+
maxLength
|
|
8884
|
+
} = fieldDef.fieldComponentProps;
|
|
8885
|
+
if (minLength != null) {
|
|
8886
|
+
rules.minLength = {
|
|
8887
|
+
value: minLength,
|
|
8888
|
+
message: getMessage(fieldDef, {
|
|
8889
|
+
type: "minLength",
|
|
8890
|
+
minLength
|
|
8891
|
+
})
|
|
8892
|
+
};
|
|
8893
|
+
}
|
|
8894
|
+
if (maxLength != null) {
|
|
8895
|
+
rules.maxLength = {
|
|
8896
|
+
value: maxLength,
|
|
8897
|
+
message: getMessage(fieldDef, {
|
|
8898
|
+
type: "maxLength",
|
|
8899
|
+
maxLength
|
|
8900
|
+
})
|
|
8901
|
+
};
|
|
8902
|
+
}
|
|
8903
|
+
break;
|
|
8904
|
+
}
|
|
8905
|
+
case "DATETIME_PICKER": {
|
|
8906
|
+
const {
|
|
8907
|
+
min,
|
|
8908
|
+
max
|
|
8909
|
+
} = fieldDef.fieldComponentProps;
|
|
8910
|
+
if (min != null) {
|
|
8911
|
+
const msg = getMessage(fieldDef, {
|
|
8912
|
+
type: "min",
|
|
8913
|
+
min
|
|
8914
|
+
});
|
|
8915
|
+
validateFns.min = (value) => value instanceof Date && value.getTime() < min.getTime() ? msg : true;
|
|
8916
|
+
}
|
|
8917
|
+
if (max != null) {
|
|
8918
|
+
const msg = getMessage(fieldDef, {
|
|
8919
|
+
type: "max",
|
|
8920
|
+
max
|
|
8921
|
+
});
|
|
8922
|
+
validateFns.max = (value) => value instanceof Date && value.getTime() > max.getTime() ? msg : true;
|
|
8923
|
+
}
|
|
8924
|
+
break;
|
|
8925
|
+
}
|
|
8926
|
+
case "FILE_PICKER": {
|
|
8927
|
+
const {
|
|
8928
|
+
maxSize
|
|
8929
|
+
} = fieldDef.fieldComponentProps;
|
|
8930
|
+
if (maxSize != null) {
|
|
8931
|
+
const msg = getMessage(fieldDef, {
|
|
8932
|
+
type: "maxSize",
|
|
8933
|
+
maxSize
|
|
8934
|
+
});
|
|
8935
|
+
validateFns.maxSize = (value) => {
|
|
8936
|
+
if (value instanceof File) {
|
|
8937
|
+
return value.size > maxSize ? msg : true;
|
|
8938
|
+
}
|
|
8939
|
+
if (Array.isArray(value)) {
|
|
8940
|
+
const oversized = value.some((f) => f instanceof File && f.size > maxSize);
|
|
8941
|
+
return oversized ? msg : true;
|
|
8942
|
+
}
|
|
8943
|
+
return true;
|
|
8944
|
+
};
|
|
8945
|
+
}
|
|
8946
|
+
break;
|
|
8947
|
+
}
|
|
8948
|
+
}
|
|
8949
|
+
if (fieldDef.validate != null) {
|
|
8950
|
+
const userValidate = fieldDef.validate;
|
|
8951
|
+
validateFns.custom = async (value) => {
|
|
8952
|
+
const result = await userValidate(value);
|
|
8953
|
+
if (result == null) {
|
|
8954
|
+
return true;
|
|
8955
|
+
}
|
|
8956
|
+
return getMessage(fieldDef, {
|
|
8957
|
+
type: "validate",
|
|
8958
|
+
message: result
|
|
8959
|
+
});
|
|
8960
|
+
};
|
|
8961
|
+
}
|
|
8962
|
+
if (Object.keys(validateFns).length > 0) {
|
|
8963
|
+
rules.validate = validateFns;
|
|
8964
|
+
}
|
|
8965
|
+
return rules;
|
|
8966
|
+
}
|
|
8967
|
+
function getMessage(fieldDef, error) {
|
|
8968
|
+
return fieldDef.onValidationError?.(error) ?? getDefaultMessage(error);
|
|
8969
|
+
}
|
|
8970
|
+
function getDefaultMessage(error) {
|
|
8971
|
+
switch (error.type) {
|
|
8972
|
+
case "required":
|
|
8973
|
+
return "This field is required";
|
|
8974
|
+
case "min":
|
|
8975
|
+
return `Must be at least ${formatConstraint(error.min)}`;
|
|
8976
|
+
case "max":
|
|
8977
|
+
return `Must be at most ${formatConstraint(error.max)}`;
|
|
8978
|
+
case "minLength":
|
|
8979
|
+
return `Must be at least ${error.minLength} characters`;
|
|
8980
|
+
case "maxLength":
|
|
8981
|
+
return `Must be at most ${error.maxLength} characters`;
|
|
8982
|
+
case "maxSize":
|
|
8983
|
+
return `File must be smaller than ${formatBytes(error.maxSize)}`;
|
|
8984
|
+
case "validate":
|
|
8985
|
+
return error.message;
|
|
8986
|
+
}
|
|
8987
|
+
}
|
|
8988
|
+
function formatConstraint(value) {
|
|
8989
|
+
if (value instanceof Date) {
|
|
8990
|
+
return value.toLocaleDateString();
|
|
8991
|
+
}
|
|
8992
|
+
return String(value);
|
|
8993
|
+
}
|
|
8994
|
+
function formatBytes(bytes) {
|
|
8995
|
+
if (bytes < 1024) {
|
|
8996
|
+
return `${bytes} B`;
|
|
8997
|
+
}
|
|
8998
|
+
if (bytes < 1024 * 1024) {
|
|
8999
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
9000
|
+
}
|
|
9001
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
9002
|
+
}
|
|
9003
|
+
|
|
8808
9004
|
// src/action-form/FormField.module.css
|
|
8809
9005
|
var FormField_default = {};
|
|
8810
9006
|
|
|
@@ -8815,10 +9011,12 @@ var FormField = /* @__PURE__ */ React75.memo(function FormFieldFn({
|
|
|
8815
9011
|
isRequired,
|
|
8816
9012
|
helperText,
|
|
8817
9013
|
error,
|
|
9014
|
+
onBlur,
|
|
8818
9015
|
children
|
|
8819
9016
|
}) {
|
|
8820
9017
|
return /* @__PURE__ */ React75__default.default.createElement("div", {
|
|
8821
|
-
className: FormField_default.osdkFormField
|
|
9018
|
+
className: FormField_default.osdkFormField,
|
|
9019
|
+
onBlur
|
|
8822
9020
|
}, label != null && /* @__PURE__ */ React75__default.default.createElement("label", {
|
|
8823
9021
|
className: FormField_default.osdkFormFieldLabel,
|
|
8824
9022
|
htmlFor: fieldKey
|
|
@@ -8859,6 +9057,7 @@ function DatetimePickerField({
|
|
|
8859
9057
|
id,
|
|
8860
9058
|
value,
|
|
8861
9059
|
onChange,
|
|
9060
|
+
error,
|
|
8862
9061
|
min,
|
|
8863
9062
|
max,
|
|
8864
9063
|
placeholder,
|
|
@@ -8910,7 +9109,8 @@ function DatetimePickerField({
|
|
|
8910
9109
|
}, /* @__PURE__ */ React75__default.default.createElement(popover.Popover.Trigger, {
|
|
8911
9110
|
id,
|
|
8912
9111
|
className: classnames13__default.default(DatetimePickerField_default.triggerButton, !hasValue && DatetimePickerField_default.triggerButtonPlaceholder),
|
|
8913
|
-
"aria-label": hasValue ? void 0 : "Select date"
|
|
9112
|
+
"aria-label": hasValue ? void 0 : "Select date",
|
|
9113
|
+
"aria-invalid": error != null || void 0
|
|
8914
9114
|
}, displayText), /* @__PURE__ */ React75__default.default.createElement(popover.Popover.Portal, null, /* @__PURE__ */ React75__default.default.createElement(popover.Popover.Positioner, {
|
|
8915
9115
|
sideOffset: 4
|
|
8916
9116
|
}, /* @__PURE__ */ React75__default.default.createElement(popover.Popover.Popup, {
|
|
@@ -9099,9 +9299,12 @@ var FilePickerField = /* @__PURE__ */ React75.memo(function FilePickerFieldFn({
|
|
|
9099
9299
|
id,
|
|
9100
9300
|
value,
|
|
9101
9301
|
onChange,
|
|
9302
|
+
error,
|
|
9102
9303
|
isMulti,
|
|
9103
9304
|
accept,
|
|
9104
|
-
//
|
|
9305
|
+
// maxSize is enforced by form-level validation (extractValidationRules),
|
|
9306
|
+
// not here. Silently dropping oversized files would leave the user with
|
|
9307
|
+
// no indication of why their selection disappeared.
|
|
9105
9308
|
maxSize: _maxSize,
|
|
9106
9309
|
text = "No file chosen",
|
|
9107
9310
|
buttonText = "Browse"
|
|
@@ -9152,7 +9355,8 @@ var FilePickerField = /* @__PURE__ */ React75.memo(function FilePickerFieldFn({
|
|
|
9152
9355
|
role: "button",
|
|
9153
9356
|
"aria-label": "Choose file",
|
|
9154
9357
|
onClick: openFileDialog,
|
|
9155
|
-
onKeyDown: handleKeyDown
|
|
9358
|
+
onKeyDown: handleKeyDown,
|
|
9359
|
+
"aria-invalid": error != null || void 0
|
|
9156
9360
|
}, /* @__PURE__ */ React75__default.default.createElement("input", {
|
|
9157
9361
|
ref: inputRef,
|
|
9158
9362
|
type: "file",
|
|
@@ -9202,9 +9406,10 @@ function NumberInputField({
|
|
|
9202
9406
|
id,
|
|
9203
9407
|
value,
|
|
9204
9408
|
onChange,
|
|
9409
|
+
error,
|
|
9205
9410
|
placeholder,
|
|
9206
|
-
min
|
|
9207
|
-
max
|
|
9411
|
+
min,
|
|
9412
|
+
max,
|
|
9208
9413
|
step
|
|
9209
9414
|
}) {
|
|
9210
9415
|
const [displayValue, setDisplayValue] = React75.useState(() => formatNumberForDisplay(value));
|
|
@@ -9226,11 +9431,11 @@ function NumberInputField({
|
|
|
9226
9431
|
const applyStep = React75.useCallback((direction) => {
|
|
9227
9432
|
const current = parseNumericValue(displayValue) ?? 0;
|
|
9228
9433
|
const delta = direction * (step ?? DEFAULT_STEP);
|
|
9229
|
-
const next = current + delta;
|
|
9434
|
+
const next = clamp(current + delta, min, max);
|
|
9230
9435
|
const formatted = formatNumberForDisplay(next);
|
|
9231
9436
|
setDisplayValue(formatted);
|
|
9232
9437
|
onChange?.(next);
|
|
9233
|
-
}, [displayValue, onChange, step]);
|
|
9438
|
+
}, [displayValue, onChange, step, min, max]);
|
|
9234
9439
|
const handleKeyDown = React75.useCallback((e) => {
|
|
9235
9440
|
if (e.key !== "ArrowUp" && e.key !== "ArrowDown") {
|
|
9236
9441
|
return;
|
|
@@ -9245,7 +9450,8 @@ function NumberInputField({
|
|
|
9245
9450
|
applyStep(-1);
|
|
9246
9451
|
}, [applyStep]);
|
|
9247
9452
|
return /* @__PURE__ */ React75__default.default.createElement("div", {
|
|
9248
|
-
className: NumberInputField_default.osdkNumberInputWrapper
|
|
9453
|
+
className: NumberInputField_default.osdkNumberInputWrapper,
|
|
9454
|
+
"aria-invalid": error != null || void 0
|
|
9249
9455
|
}, /* @__PURE__ */ React75__default.default.createElement(input.Input, {
|
|
9250
9456
|
id,
|
|
9251
9457
|
className: NumberInputField_default.osdkNumberInputField,
|
|
@@ -9286,6 +9492,11 @@ function parseNumericValue(text) {
|
|
|
9286
9492
|
function formatNumberForDisplay(value) {
|
|
9287
9493
|
return value != null ? String(value) : "";
|
|
9288
9494
|
}
|
|
9495
|
+
function clamp(value, min, max) {
|
|
9496
|
+
if (min != null && value < min) return min;
|
|
9497
|
+
if (max != null && value > max) return max;
|
|
9498
|
+
return value;
|
|
9499
|
+
}
|
|
9289
9500
|
var BlueprintIcon = /* @__PURE__ */ React75__default.default.memo(function BlueprintIconFn({
|
|
9290
9501
|
icon,
|
|
9291
9502
|
size
|
|
@@ -9472,6 +9683,7 @@ function TextAreaField({
|
|
|
9472
9683
|
id,
|
|
9473
9684
|
value,
|
|
9474
9685
|
onChange,
|
|
9686
|
+
error,
|
|
9475
9687
|
placeholder,
|
|
9476
9688
|
rows,
|
|
9477
9689
|
wrap,
|
|
@@ -9491,13 +9703,15 @@ function TextAreaField({
|
|
|
9491
9703
|
placeholder,
|
|
9492
9704
|
minLength,
|
|
9493
9705
|
maxLength,
|
|
9494
|
-
render: renderTextarea
|
|
9706
|
+
render: renderTextarea,
|
|
9707
|
+
"aria-invalid": error != null || void 0
|
|
9495
9708
|
});
|
|
9496
9709
|
}
|
|
9497
9710
|
function TextInputField({
|
|
9498
9711
|
id,
|
|
9499
9712
|
value,
|
|
9500
9713
|
onChange,
|
|
9714
|
+
error,
|
|
9501
9715
|
placeholder,
|
|
9502
9716
|
minLength,
|
|
9503
9717
|
maxLength
|
|
@@ -9510,7 +9724,8 @@ function TextInputField({
|
|
|
9510
9724
|
onValueChange: onChange,
|
|
9511
9725
|
placeholder,
|
|
9512
9726
|
minLength,
|
|
9513
|
-
maxLength
|
|
9727
|
+
maxLength,
|
|
9728
|
+
"aria-invalid": error != null || void 0
|
|
9514
9729
|
});
|
|
9515
9730
|
}
|
|
9516
9731
|
|
|
@@ -9527,7 +9742,9 @@ function _extends13() {
|
|
|
9527
9742
|
var FormFieldRenderer = /* @__PURE__ */ React75.memo(function FormFieldRendererFn({
|
|
9528
9743
|
fieldDefinition,
|
|
9529
9744
|
value,
|
|
9530
|
-
onFieldValueChange
|
|
9745
|
+
onFieldValueChange,
|
|
9746
|
+
onBlur,
|
|
9747
|
+
error
|
|
9531
9748
|
}) {
|
|
9532
9749
|
const {
|
|
9533
9750
|
label,
|
|
@@ -9539,30 +9756,35 @@ var FormFieldRenderer = /* @__PURE__ */ React75.memo(function FormFieldRendererF
|
|
|
9539
9756
|
label,
|
|
9540
9757
|
isRequired,
|
|
9541
9758
|
fieldKey: fieldDefinition.fieldKey,
|
|
9542
|
-
helperText: helperTextPlacement !== "tooltip" ? helperText : void 0
|
|
9543
|
-
|
|
9759
|
+
helperText: helperTextPlacement !== "tooltip" ? helperText : void 0,
|
|
9760
|
+
error,
|
|
9761
|
+
onBlur
|
|
9762
|
+
}, renderFieldComponent(fieldDefinition, value, onFieldValueChange, error));
|
|
9544
9763
|
});
|
|
9545
|
-
function renderFieldComponent(fieldDefinition, value, onChange) {
|
|
9764
|
+
function renderFieldComponent(fieldDefinition, value, onChange, error) {
|
|
9546
9765
|
switch (fieldDefinition.fieldComponent) {
|
|
9547
9766
|
case "TEXT_INPUT":
|
|
9548
9767
|
return /* @__PURE__ */ React75__default.default.createElement(TextInputField, _extends13({
|
|
9549
9768
|
id: fieldDefinition.fieldKey,
|
|
9550
9769
|
value: value != null ? String(value) : "",
|
|
9551
9770
|
onChange,
|
|
9552
|
-
placeholder: fieldDefinition.placeholder
|
|
9771
|
+
placeholder: fieldDefinition.placeholder,
|
|
9772
|
+
error
|
|
9553
9773
|
}, fieldDefinition.fieldComponentProps));
|
|
9554
9774
|
case "TEXT_AREA":
|
|
9555
9775
|
return /* @__PURE__ */ React75__default.default.createElement(TextAreaField, _extends13({
|
|
9556
9776
|
id: fieldDefinition.fieldKey,
|
|
9557
9777
|
value: value != null ? String(value) : "",
|
|
9558
9778
|
onChange,
|
|
9559
|
-
placeholder: fieldDefinition.placeholder
|
|
9779
|
+
placeholder: fieldDefinition.placeholder,
|
|
9780
|
+
error
|
|
9560
9781
|
}, fieldDefinition.fieldComponentProps));
|
|
9561
9782
|
case "DROPDOWN": {
|
|
9562
9783
|
return /* @__PURE__ */ React75__default.default.createElement(DropdownField, _extends13({
|
|
9563
9784
|
value,
|
|
9564
9785
|
onChange,
|
|
9565
|
-
placeholder: fieldDefinition.placeholder
|
|
9786
|
+
placeholder: fieldDefinition.placeholder,
|
|
9787
|
+
error
|
|
9566
9788
|
}, fieldDefinition.fieldComponentProps));
|
|
9567
9789
|
}
|
|
9568
9790
|
case "DATETIME_PICKER":
|
|
@@ -9570,32 +9792,37 @@ function renderFieldComponent(fieldDefinition, value, onChange) {
|
|
|
9570
9792
|
id: fieldDefinition.fieldKey,
|
|
9571
9793
|
placeholder: fieldDefinition.placeholder,
|
|
9572
9794
|
value: value instanceof Date ? value : null,
|
|
9573
|
-
onChange
|
|
9795
|
+
onChange,
|
|
9796
|
+
error
|
|
9574
9797
|
}, fieldDefinition.fieldComponentProps));
|
|
9575
9798
|
case "RADIO_BUTTONS":
|
|
9576
9799
|
return /* @__PURE__ */ React75__default.default.createElement(RadioButtonsField, _extends13({
|
|
9577
9800
|
id: fieldDefinition.fieldKey,
|
|
9578
9801
|
value,
|
|
9579
|
-
onChange
|
|
9802
|
+
onChange,
|
|
9803
|
+
error
|
|
9580
9804
|
}, fieldDefinition.fieldComponentProps));
|
|
9581
9805
|
case "CUSTOM":
|
|
9582
9806
|
return /* @__PURE__ */ React75__default.default.createElement(CustomField, _extends13({
|
|
9583
9807
|
id: fieldDefinition.fieldKey,
|
|
9584
9808
|
value,
|
|
9585
|
-
onChange
|
|
9809
|
+
onChange,
|
|
9810
|
+
error
|
|
9586
9811
|
}, fieldDefinition.fieldComponentProps));
|
|
9587
9812
|
case "NUMBER_INPUT":
|
|
9588
9813
|
return /* @__PURE__ */ React75__default.default.createElement(NumberInputField, _extends13({
|
|
9589
9814
|
id: fieldDefinition.fieldKey,
|
|
9590
9815
|
value: typeof value === "number" ? value : null,
|
|
9591
9816
|
onChange,
|
|
9592
|
-
placeholder: fieldDefinition.placeholder
|
|
9817
|
+
placeholder: fieldDefinition.placeholder,
|
|
9818
|
+
error
|
|
9593
9819
|
}, fieldDefinition.fieldComponentProps));
|
|
9594
9820
|
case "FILE_PICKER":
|
|
9595
9821
|
return /* @__PURE__ */ React75__default.default.createElement(FilePickerField, _extends13({
|
|
9596
9822
|
id: fieldDefinition.fieldKey,
|
|
9597
9823
|
value: coerceToFileValue(value),
|
|
9598
|
-
onChange
|
|
9824
|
+
onChange,
|
|
9825
|
+
error
|
|
9599
9826
|
}, fieldDefinition.fieldComponentProps));
|
|
9600
9827
|
case "OBJECT_SET":
|
|
9601
9828
|
return /* @__PURE__ */ React75__default.default.createElement(ObjectSetField, _extends13({
|
|
@@ -9622,28 +9849,47 @@ function assertUnreachableFieldComponent(value) {
|
|
|
9622
9849
|
}
|
|
9623
9850
|
|
|
9624
9851
|
// src/action-form/fields/FieldBridge.tsx
|
|
9852
|
+
var SELECT_LIKE_FIELDS = /* @__PURE__ */ new Set(["RADIO_BUTTONS", "DROPDOWN"]);
|
|
9625
9853
|
var FieldBridge = /* @__PURE__ */ React75.memo(function FieldBridgeFn({
|
|
9626
9854
|
fieldDef,
|
|
9627
9855
|
control,
|
|
9628
9856
|
onExternalChange
|
|
9629
9857
|
}) {
|
|
9858
|
+
const rules = React75.useMemo(() => extractValidationRules(fieldDef), [fieldDef]);
|
|
9630
9859
|
const {
|
|
9631
9860
|
field: {
|
|
9632
9861
|
onChange,
|
|
9862
|
+
onBlur,
|
|
9633
9863
|
value
|
|
9864
|
+
},
|
|
9865
|
+
fieldState: {
|
|
9866
|
+
error: fieldError
|
|
9634
9867
|
}
|
|
9635
9868
|
} = reactHookForm.useController({
|
|
9636
9869
|
name: fieldDef.fieldKey,
|
|
9637
|
-
control
|
|
9870
|
+
control,
|
|
9871
|
+
rules
|
|
9638
9872
|
});
|
|
9873
|
+
const isSelectLike = SELECT_LIKE_FIELDS.has(fieldDef.fieldComponent);
|
|
9639
9874
|
const handleChange = React75.useCallback((newValue) => {
|
|
9640
9875
|
onChange(newValue);
|
|
9641
9876
|
onExternalChange?.(fieldDef.fieldKey, newValue);
|
|
9642
|
-
|
|
9877
|
+
if (isSelectLike) {
|
|
9878
|
+
onBlur();
|
|
9879
|
+
}
|
|
9880
|
+
}, [onChange, onBlur, onExternalChange, fieldDef.fieldKey, isSelectLike]);
|
|
9881
|
+
const handleBlur = React75.useCallback((e) => {
|
|
9882
|
+
if (e.currentTarget.contains(e.relatedTarget)) {
|
|
9883
|
+
return;
|
|
9884
|
+
}
|
|
9885
|
+
onBlur();
|
|
9886
|
+
}, [onBlur]);
|
|
9643
9887
|
return /* @__PURE__ */ React75__default.default.createElement(FormFieldRenderer, {
|
|
9644
9888
|
value,
|
|
9645
9889
|
fieldDefinition: fieldDef,
|
|
9646
|
-
onFieldValueChange: handleChange
|
|
9890
|
+
onFieldValueChange: handleChange,
|
|
9891
|
+
onBlur: handleBlur,
|
|
9892
|
+
error: fieldError?.message
|
|
9647
9893
|
});
|
|
9648
9894
|
});
|
|
9649
9895
|
|
|
@@ -9660,6 +9906,7 @@ var FormHeader = /* @__PURE__ */ React75.memo(function FormHeaderFn({
|
|
|
9660
9906
|
});
|
|
9661
9907
|
|
|
9662
9908
|
// src/action-form/BaseForm.tsx
|
|
9909
|
+
var TOOLTIP_TRIGGER_DELAY_MS = 200;
|
|
9663
9910
|
var BaseForm = /* @__PURE__ */ React75.memo(function BaseFormFn({
|
|
9664
9911
|
formTitle,
|
|
9665
9912
|
fieldDefinitions,
|
|
@@ -9675,20 +9922,54 @@ var BaseForm = /* @__PURE__ */ React75.memo(function BaseFormFn({
|
|
|
9675
9922
|
const defaultValues = React75.useMemo(() => buildDefaultValues(fieldDefinitions), [fieldDefinitions]);
|
|
9676
9923
|
const {
|
|
9677
9924
|
control,
|
|
9678
|
-
|
|
9925
|
+
trigger,
|
|
9926
|
+
getValues,
|
|
9927
|
+
formState: {
|
|
9928
|
+
errors
|
|
9929
|
+
}
|
|
9679
9930
|
} = reactHookForm.useForm({
|
|
9931
|
+
// Validate on blur first, then revalidate on change after the first
|
|
9932
|
+
// error. This gives the user a chance to finish typing before seeing
|
|
9933
|
+
// errors, while staying responsive once an error is surfaced.
|
|
9934
|
+
mode: "onTouched",
|
|
9680
9935
|
...isControlled ? {
|
|
9681
9936
|
values: controlledFormState
|
|
9682
9937
|
} : {
|
|
9683
9938
|
defaultValues
|
|
9684
9939
|
}
|
|
9685
9940
|
});
|
|
9686
|
-
const
|
|
9687
|
-
|
|
9688
|
-
|
|
9941
|
+
const [hasAttemptedSubmit, setHasAttemptedSubmit] = React75.useState(false);
|
|
9942
|
+
const {
|
|
9943
|
+
isPending: isSubmitting,
|
|
9944
|
+
error: submissionError,
|
|
9945
|
+
execute: executeSubmit,
|
|
9946
|
+
clearError
|
|
9947
|
+
} = useAsyncAction(onSubmit);
|
|
9948
|
+
const submissionErrorMessage = submissionError != null ? submissionError instanceof Error ? submissionError.message : "Submission failed" : void 0;
|
|
9949
|
+
const handleFormSubmit = React75.useCallback(async (e) => {
|
|
9950
|
+
e.preventDefault();
|
|
9951
|
+
setHasAttemptedSubmit(true);
|
|
9952
|
+
const isValid = await trigger();
|
|
9953
|
+
if (!isValid) {
|
|
9954
|
+
return;
|
|
9955
|
+
}
|
|
9956
|
+
await executeSubmit(controlledFormState ?? getValues());
|
|
9957
|
+
}, [trigger, executeSubmit, controlledFormState, getValues]);
|
|
9958
|
+
const handleFieldChange = React75.useCallback((fieldKey, value) => {
|
|
9959
|
+
clearError();
|
|
9960
|
+
onFieldValueChange?.(fieldKey, value);
|
|
9961
|
+
}, [clearError, onFieldValueChange]);
|
|
9962
|
+
const isFormPending = isPending || isSubmitting;
|
|
9963
|
+
const labelByFieldKey = React75.useMemo(() => new Map(fieldDefinitions.map((d) => [d.fieldKey, d.label])), [fieldDefinitions]);
|
|
9964
|
+
const errorEntries = Object.entries(errors).map(([key, entry]) => ({
|
|
9965
|
+
label: labelByFieldKey.get(key) ?? key,
|
|
9966
|
+
message: entry?.message ?? "Invalid"
|
|
9967
|
+
}));
|
|
9968
|
+
const areErrorsPresent = errorEntries.length > 0;
|
|
9969
|
+
const buttonErrorMessage = areErrorsPresent ? "Some fields are invalid" : submissionErrorMessage;
|
|
9689
9970
|
return /* @__PURE__ */ React75__default.default.createElement("form", {
|
|
9690
9971
|
className: classnames13__default.default(BaseForm_default.osdkForm, className),
|
|
9691
|
-
onSubmit:
|
|
9972
|
+
onSubmit: handleFormSubmit
|
|
9692
9973
|
}, formTitle != null && /* @__PURE__ */ React75__default.default.createElement(FormHeader, {
|
|
9693
9974
|
title: formTitle
|
|
9694
9975
|
}), isLoading && fieldDefinitions.length === 0 && /* @__PURE__ */ React75__default.default.createElement("div", {
|
|
@@ -9699,14 +9980,18 @@ var BaseForm = /* @__PURE__ */ React75.memo(function BaseFormFn({
|
|
|
9699
9980
|
key: fieldDef.fieldKey,
|
|
9700
9981
|
fieldDef,
|
|
9701
9982
|
control,
|
|
9702
|
-
onExternalChange:
|
|
9983
|
+
onExternalChange: handleFieldChange
|
|
9703
9984
|
}))), /* @__PURE__ */ React75__default.default.createElement("div", {
|
|
9704
9985
|
className: BaseForm_default.osdkFormFooter
|
|
9705
|
-
}, /* @__PURE__ */ React75__default.default.createElement(
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
},
|
|
9986
|
+
}, /* @__PURE__ */ React75__default.default.createElement(ErrorIndicator, {
|
|
9987
|
+
errorEntries
|
|
9988
|
+
}), /* @__PURE__ */ React75__default.default.createElement("div", {
|
|
9989
|
+
className: BaseForm_default.osdkFormSubmitButton
|
|
9990
|
+
}, /* @__PURE__ */ React75__default.default.createElement(SubmitButton, {
|
|
9991
|
+
isPending: isFormPending,
|
|
9992
|
+
isSubmitDisabled: isSubmitDisabled || hasAttemptedSubmit && areErrorsPresent,
|
|
9993
|
+
errorMessage: buttonErrorMessage
|
|
9994
|
+
}))));
|
|
9710
9995
|
});
|
|
9711
9996
|
function buildDefaultValues(fieldDefinitions) {
|
|
9712
9997
|
const values = {};
|
|
@@ -9718,6 +10003,45 @@ function buildDefaultValues(fieldDefinitions) {
|
|
|
9718
10003
|
}
|
|
9719
10004
|
return values;
|
|
9720
10005
|
}
|
|
10006
|
+
var SubmitButton = /* @__PURE__ */ React75.memo(function SubmitButtonFn({
|
|
10007
|
+
isPending,
|
|
10008
|
+
isSubmitDisabled,
|
|
10009
|
+
errorMessage
|
|
10010
|
+
}) {
|
|
10011
|
+
const buttonLabel = isPending ? "Submitting\u2026" : "Submit";
|
|
10012
|
+
const button = /* @__PURE__ */ React75__default.default.createElement(chunkNEWX2ZXY_cjs.ActionButton, {
|
|
10013
|
+
type: "submit",
|
|
10014
|
+
variant: "primary",
|
|
10015
|
+
disabled: isSubmitDisabled || isPending
|
|
10016
|
+
}, buttonLabel);
|
|
10017
|
+
if (errorMessage == null) {
|
|
10018
|
+
return button;
|
|
10019
|
+
}
|
|
10020
|
+
return /* @__PURE__ */ React75__default.default.createElement(chunkNEWX2ZXY_cjs.Tooltip.Root, {
|
|
10021
|
+
defaultOpen: true
|
|
10022
|
+
}, /* @__PURE__ */ React75__default.default.createElement(chunkNEWX2ZXY_cjs.Tooltip.Trigger, {
|
|
10023
|
+
delay: TOOLTIP_TRIGGER_DELAY_MS
|
|
10024
|
+
}, button), /* @__PURE__ */ React75__default.default.createElement(chunkNEWX2ZXY_cjs.Tooltip.Portal, null, /* @__PURE__ */ React75__default.default.createElement(chunkNEWX2ZXY_cjs.Tooltip.Positioner, null, /* @__PURE__ */ React75__default.default.createElement(chunkNEWX2ZXY_cjs.Tooltip.Popup, null, /* @__PURE__ */ React75__default.default.createElement(chunkNEWX2ZXY_cjs.Tooltip.Arrow, null), errorMessage))));
|
|
10025
|
+
});
|
|
10026
|
+
function ErrorIndicator({
|
|
10027
|
+
errorEntries
|
|
10028
|
+
}) {
|
|
10029
|
+
if (errorEntries.length === 0) {
|
|
10030
|
+
return null;
|
|
10031
|
+
}
|
|
10032
|
+
const count = errorEntries.length;
|
|
10033
|
+
return /* @__PURE__ */ React75__default.default.createElement(chunkNEWX2ZXY_cjs.Tooltip.Root, null, /* @__PURE__ */ React75__default.default.createElement(chunkNEWX2ZXY_cjs.Tooltip.Trigger, {
|
|
10034
|
+
delay: TOOLTIP_TRIGGER_DELAY_MS
|
|
10035
|
+
}, /* @__PURE__ */ React75__default.default.createElement("span", {
|
|
10036
|
+
className: BaseForm_default.osdkFormErrorIndicator
|
|
10037
|
+
}, /* @__PURE__ */ React75__default.default.createElement(icons.Error, {
|
|
10038
|
+
size: 14
|
|
10039
|
+
}), count === 1 ? "1 issue" : `${count} issues`)), /* @__PURE__ */ React75__default.default.createElement(chunkNEWX2ZXY_cjs.Tooltip.Portal, null, /* @__PURE__ */ React75__default.default.createElement(chunkNEWX2ZXY_cjs.Tooltip.Positioner, null, /* @__PURE__ */ React75__default.default.createElement(chunkNEWX2ZXY_cjs.Tooltip.Popup, null, /* @__PURE__ */ React75__default.default.createElement(chunkNEWX2ZXY_cjs.Tooltip.Arrow, null), /* @__PURE__ */ React75__default.default.createElement("ul", {
|
|
10040
|
+
className: BaseForm_default.osdkFormErrorList
|
|
10041
|
+
}, errorEntries.map((entry) => /* @__PURE__ */ React75__default.default.createElement("li", {
|
|
10042
|
+
key: entry.label
|
|
10043
|
+
}, /* @__PURE__ */ React75__default.default.createElement("strong", null, entry.label, ":"), " ", entry.message)))))));
|
|
10044
|
+
}
|
|
9721
10045
|
|
|
9722
10046
|
// src/action-form/utils/coerceFieldValue.ts
|
|
9723
10047
|
function coerceFieldValue(parameterType, rawValue) {
|