@osdk/react-components 0.2.0 → 0.2.1-main-20260407074313

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/build/browser/action-form/FormFieldApi.js.map +1 -1
  3. package/build/browser/action-form/fields/FilePickerField.js +119 -0
  4. package/build/browser/action-form/fields/FilePickerField.js.map +1 -0
  5. package/build/browser/action-form/fields/FilePickerField.module.css +132 -0
  6. package/build/browser/action-form/fields/FilePickerField.module.css.js +11 -0
  7. package/build/browser/action-form/fields/FormFieldRenderer.js +20 -0
  8. package/build/browser/action-form/fields/FormFieldRenderer.js.map +1 -1
  9. package/build/browser/object-table/hooks/useObjectTableData.js.map +1 -1
  10. package/build/browser/styles.css +135 -0
  11. package/build/cjs/public/experimental.cjs +116 -0
  12. package/build/cjs/public/experimental.cjs.map +1 -1
  13. package/build/cjs/public/experimental.css +94 -0
  14. package/build/cjs/public/experimental.css.map +1 -1
  15. package/build/cjs/public/experimental.d.cts +12 -0
  16. package/build/esm/action-form/FormFieldApi.js.map +1 -1
  17. package/build/esm/action-form/fields/FilePickerField.js +119 -0
  18. package/build/esm/action-form/fields/FilePickerField.js.map +1 -0
  19. package/build/esm/action-form/fields/FilePickerField.module.css +132 -0
  20. package/build/esm/action-form/fields/FormFieldRenderer.js +20 -0
  21. package/build/esm/action-form/fields/FormFieldRenderer.js.map +1 -1
  22. package/build/esm/object-table/hooks/useObjectTableData.js.map +1 -1
  23. package/build/types/action-form/FormFieldApi.d.ts +12 -0
  24. package/build/types/action-form/FormFieldApi.d.ts.map +1 -1
  25. package/build/types/action-form/fields/FilePickerField.d.ts +3 -0
  26. package/build/types/action-form/fields/FilePickerField.d.ts.map +1 -0
  27. package/build/types/action-form/fields/FormFieldRenderer.d.ts.map +1 -1
  28. package/build/types/object-table/hooks/useObjectTableData.d.ts +1 -3
  29. package/build/types/object-table/hooks/useObjectTableData.d.ts.map +1 -1
  30. package/package.json +7 -7
@@ -8990,6 +8990,105 @@ function defaultItemToStringLabel(item) {
8990
8990
  return String(item);
8991
8991
  }
8992
8992
 
8993
+ // src/action-form/fields/FilePickerField.module.css
8994
+ var FilePickerField_default = {};
8995
+
8996
+ // src/action-form/fields/FilePickerField.tsx
8997
+ var FilePickerField = /* @__PURE__ */ React76.memo(function FilePickerFieldFn({
8998
+ id,
8999
+ value,
9000
+ onChange,
9001
+ isMulti,
9002
+ accept,
9003
+ // TODO: implement maxSize validation in a follow-up
9004
+ maxSize: _maxSize,
9005
+ text = "No file chosen",
9006
+ buttonText = "Browse"
9007
+ }) {
9008
+ const inputRef = React76.useRef(null);
9009
+ const openFileDialog = React76.useCallback(() => {
9010
+ inputRef.current?.click();
9011
+ }, []);
9012
+ const handleInputChange = React76.useCallback((event) => {
9013
+ const files = event.target.files;
9014
+ if (files == null || files.length === 0) {
9015
+ onChange?.(null);
9016
+ return;
9017
+ }
9018
+ if (isMulti) {
9019
+ onChange?.(Array.from(files));
9020
+ } else {
9021
+ onChange?.(files[0] ?? null);
9022
+ }
9023
+ }, [onChange, isMulti]);
9024
+ const handleClear = React76.useCallback((event) => {
9025
+ event.stopPropagation();
9026
+ event.preventDefault();
9027
+ onChange?.(null);
9028
+ if (inputRef.current != null) {
9029
+ inputRef.current.value = "";
9030
+ }
9031
+ }, [onChange]);
9032
+ const handleKeyDown = React76.useCallback((event) => {
9033
+ if (event.key === "Enter" || event.key === " ") {
9034
+ event.preventDefault();
9035
+ openFileDialog();
9036
+ }
9037
+ }, [openFileDialog]);
9038
+ const displayText = getDisplayText(value);
9039
+ const hasValue = displayText != null;
9040
+ const acceptString = normalizeAccept(accept);
9041
+ return (
9042
+ // The entire component is a single tab stop (tabIndex={0}).
9043
+ // Text and Browse are <span>s (not buttons) so they don't create
9044
+ // extra tab stops — clicks on them bubble up to the container's onClick.
9045
+ // The clear button is the only inner interactive element and gets its
9046
+ // own tab stop so keyboard users can clear the selection.
9047
+ /* @__PURE__ */ React76__default.default.createElement("div", {
9048
+ id,
9049
+ className: FilePickerField_default.osdkFilePickerTrigger,
9050
+ tabIndex: 0,
9051
+ role: "button",
9052
+ onClick: openFileDialog,
9053
+ onKeyDown: handleKeyDown
9054
+ }, /* @__PURE__ */ React76__default.default.createElement("input", {
9055
+ ref: inputRef,
9056
+ type: "file",
9057
+ className: FilePickerField_default.osdkFilePickerHiddenInput,
9058
+ multiple: isMulti,
9059
+ accept: acceptString,
9060
+ onChange: handleInputChange,
9061
+ "aria-hidden": "true",
9062
+ tabIndex: -1
9063
+ }), /* @__PURE__ */ React76__default.default.createElement("span", {
9064
+ className: classnames14__default.default(FilePickerField_default.osdkFilePickerText, !hasValue && FilePickerField_default.osdkFilePickerPlaceholder)
9065
+ }, displayText ?? text), hasValue && // stopPropagation + preventDefault prevent the click from
9066
+ // bubbling to the container's onClick which opens the file dialog.
9067
+ /* @__PURE__ */ React76__default.default.createElement(button.Button, {
9068
+ className: FilePickerField_default.osdkFilePickerClear,
9069
+ onClick: handleClear,
9070
+ "aria-label": "Clear selection"
9071
+ }, /* @__PURE__ */ React76__default.default.createElement(icons.Cross, null)), /* @__PURE__ */ React76__default.default.createElement("span", {
9072
+ className: FilePickerField_default.osdkFilePickerBrowse
9073
+ }, buttonText))
9074
+ );
9075
+ });
9076
+ function normalizeAccept(accept) {
9077
+ if (accept == null) {
9078
+ return void 0;
9079
+ }
9080
+ return Array.isArray(accept) ? accept.join(",") : accept;
9081
+ }
9082
+ function getDisplayText(value) {
9083
+ if (value == null) {
9084
+ return void 0;
9085
+ }
9086
+ if (Array.isArray(value)) {
9087
+ return value.map((f) => f.name).join(", ");
9088
+ }
9089
+ return value.name;
9090
+ }
9091
+
8993
9092
  // src/action-form/fields/NumberInputField.module.css
8994
9093
  var NumberInputField_default = {};
8995
9094
 
@@ -9266,12 +9365,29 @@ function renderFieldComponent(fieldDefinition, value, onChange) {
9266
9365
  placeholder: fieldDefinition.placeholder
9267
9366
  }, fieldDefinition.fieldComponentProps));
9268
9367
  case "FILE_PICKER":
9368
+ return /* @__PURE__ */ React76__default.default.createElement(FilePickerField, _extends13({
9369
+ id: fieldDefinition.fieldKey,
9370
+ value: coerceToFileValue(value),
9371
+ onChange
9372
+ }, fieldDefinition.fieldComponentProps));
9269
9373
  case "OBJECT_SET":
9270
9374
  return /* @__PURE__ */ React76__default.default.createElement("div", null, "Unsupported field type: ", fieldDefinition.fieldComponent);
9271
9375
  default:
9272
9376
  return assertUnreachableFieldComponent(fieldDefinition);
9273
9377
  }
9274
9378
  }
9379
+ function isFileArray(value) {
9380
+ return value.every((v) => v instanceof File);
9381
+ }
9382
+ function coerceToFileValue(value) {
9383
+ if (value instanceof File) {
9384
+ return value;
9385
+ }
9386
+ if (Array.isArray(value) && isFileArray(value)) {
9387
+ return value;
9388
+ }
9389
+ return null;
9390
+ }
9275
9391
  function assertUnreachableFieldComponent(value) {
9276
9392
  throw new Error(`Unhandled field component: ${String(value)}`);
9277
9393
  }