@bsol-oss/react-datatable5 13.0.1 → 13.0.2

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/dist/index.js CHANGED
@@ -3917,6 +3917,10 @@ const useFormLabel = (column, prefix = '', schema) => {
3917
3917
  // Return column name as fallback
3918
3918
  return column;
3919
3919
  },
3920
+ /**
3921
+ * Helper text from JSON Schema `description` (shown below the control).
3922
+ */
3923
+ helperText: schema.description,
3920
3924
  };
3921
3925
  };
3922
3926
 
@@ -3997,7 +4001,7 @@ const BooleanPicker = ({ schema, column, prefix }) => {
3997
4001
  const value = watch(colLabel);
3998
4002
  const formI18n = useFormLabel(column, prefix, schema);
3999
4003
  const fieldError = getNestedError(errors, colLabel);
4000
- return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4004
+ return (jsxRuntime.jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
4001
4005
  gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: jsxRuntime.jsx(CheckboxCard, { checked: value, variant: 'surface', onChange: () => {
4002
4006
  setValue(colLabel, !value);
4003
4007
  } }) }));
@@ -4118,7 +4122,7 @@ const DatePicker = ({ column, schema, prefix }) => {
4118
4122
  console.error(e);
4119
4123
  }
4120
4124
  }, [selectedDate, dateFormat, colLabel, setValue, timezone]);
4121
- return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4125
+ return (jsxRuntime.jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
4122
4126
  gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: [jsxRuntime.jsx("input", { type: "hidden", name: colLabel, value: selectedDate ?? '', readOnly: true, "aria-hidden": true }), jsxRuntime.jsx(DatePickerInput, { value: selectedDate, onChange: (d) => {
4123
4127
  setValue(colLabel, d, {
4124
4128
  shouldValidate: true,
@@ -4215,7 +4219,7 @@ const DateRangePicker = ({ column, schema, prefix, }) => {
4215
4219
  console.error(e);
4216
4220
  }
4217
4221
  }, [selectedDateRange, dateFormat, colLabel, setValue, timezone]);
4218
- return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4222
+ return (jsxRuntime.jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
4219
4223
  gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: jsxRuntime.jsxs(react.Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: jsxRuntime.jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
4220
4224
  setOpen(true);
4221
4225
  }, justifyContent: 'start', children: [jsxRuntime.jsx(md.MdDateRange, {}), getDisplayText()] }) }), insideDialog ? (jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { width: "fit-content", minW: "50rem", minH: "25rem", children: jsxRuntime.jsx(react.Popover.Body, { children: jsxRuntime.jsx(RangeDatePicker, { selected: selectedDates, timezone: timezone, onDateSelected: ({ selected }) => {
@@ -4346,7 +4350,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4346
4350
  }
4347
4351
  };
4348
4352
  if (variant === 'radio') {
4349
- return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4353
+ return (jsxRuntime.jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
4350
4354
  gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: jsxRuntime.jsx(react.RadioGroup.Root, { value: !isMultiple ? watchEnum : undefined, onValueChange: (details) => {
4351
4355
  if (!isMultiple) {
4352
4356
  setValue(colLabel, details.value);
@@ -4355,7 +4359,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4355
4359
  return (jsxRuntime.jsxs(react.RadioGroup.Item, { value: item, children: [jsxRuntime.jsx(react.RadioGroup.ItemHiddenInput, {}), jsxRuntime.jsx(react.RadioGroup.ItemIndicator, {}), jsxRuntime.jsx(react.RadioGroup.ItemText, { children: renderEnumValue(item) })] }, `${colLabel}-${item}`));
4356
4360
  }) }) }) }));
4357
4361
  }
4358
- return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4362
+ return (jsxRuntime.jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
4359
4363
  gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: [isMultiple && currentValue.length > 0 && (jsxRuntime.jsx(react.Flex, { flexFlow: 'wrap', gap: 1, mb: 2, children: currentValue.map((enumValue) => {
4360
4364
  if (!enumValue) {
4361
4365
  return null;
@@ -4967,7 +4971,7 @@ const FilePicker = ({ column, schema, prefix }) => {
4967
4971
  const getImageUrl = (file) => {
4968
4972
  return URL.createObjectURL(file);
4969
4973
  };
4970
- return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4974
+ return (jsxRuntime.jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
4971
4975
  gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: [jsxRuntime.jsx(react.VStack, { align: "stretch", gap: 2, children: jsxRuntime.jsx(FileDropzone, { onDrop: ({ files }) => {
4972
4976
  // file-picker variant: Store File objects directly (no ID conversion)
4973
4977
  if (isSingleSelect) {
@@ -5066,7 +5070,7 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
5066
5070
  // eslint-disable-next-line react-hooks/exhaustive-deps
5067
5071
  }, [currentFileIds.join(',')]);
5068
5072
  if (!onFetchFiles) {
5069
- return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5073
+ return (jsxRuntime.jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
5070
5074
  gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: jsxRuntime.jsx(react.Text, { color: "fg.muted", children: "Media library browser requires onFetchFiles" }) }));
5071
5075
  }
5072
5076
  const handleImageError = (fileIdentifier) => {
@@ -5092,7 +5096,7 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
5092
5096
  setValue(colLabel, newFileIds);
5093
5097
  }
5094
5098
  };
5095
- return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5099
+ return (jsxRuntime.jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
5096
5100
  gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: [jsxRuntime.jsx(react.VStack, { align: "stretch", gap: 2, children: jsxRuntime.jsx(react.Button, { variant: "outline", onClick: () => setDialogOpen(true), borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: filePickerLabels?.browseLibrary ?? 'Browse from Library' }) }), jsxRuntime.jsx(MediaBrowserDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ?? formI18n.label() ?? 'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, onUploadFile: onUploadFile, enableUpload: enableUpload, labels: filePickerLabels, colLabel: colLabel }), jsxRuntime.jsx(react.Flex, { flexFlow: 'column', gap: 1, children: currentFileIds.map((fileId, index) => {
5097
5101
  const file = fileMap.get(fileId);
5098
5102
  const isImage = file
@@ -5505,7 +5509,7 @@ const IdPickerSingle = ({ column, schema, prefix, }) => {
5505
5509
  ? renderDisplayFunction(selectedItem)
5506
5510
  : null;
5507
5511
  const fieldError = getNestedError(errors, colLabel);
5508
- return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5512
+ return (jsxRuntime.jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
5509
5513
  gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: jsxRuntime.jsxs(react.Combobox.RootProvider, { value: combobox, width: "100%", children: [jsxRuntime.jsx(react.Show, { when: selectedId && selectedRendered, children: jsxRuntime.jsxs(react.HStack, { justifyContent: 'space-between', children: [jsxRuntime.jsx(react.Box, { children: selectedRendered }), currentValue.length > 0 && (jsxRuntime.jsx(react.Button, { variant: "ghost", size: "sm", onClick: () => {
5510
5514
  setValue(colLabel, '');
5511
5515
  }, children: jsxRuntime.jsx(react.Icon, { children: jsxRuntime.jsx(bi.BiX, {}) }) }))] }) }), jsxRuntime.jsx(react.Show, { when: !selectedId || !selectedRendered, children: jsxRuntime.jsxs(react.Combobox.Control, { position: "relative", children: [jsxRuntime.jsx(react.Combobox.Input, { placeholder: idPickerLabels?.typeToSearch ?? 'Type to search' }), jsxRuntime.jsxs(react.Combobox.IndicatorGroup, { children: [(isFetching || isLoading || isPending) && jsxRuntime.jsx(react.Spinner, { size: "xs" }), isError && (jsxRuntime.jsx(react.Icon, { color: "fg.error", children: jsxRuntime.jsx(bi.BiError, {}) })), jsxRuntime.jsx(react.Combobox.Trigger, {})] })] }) }), insideDialog ? (jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsx(react.Combobox.Content, { children: isError ? (jsxRuntime.jsx(react.Text, { p: 2, color: "fg.error", fontSize: "sm", children: idPickerLabels?.emptySearchResult ?? 'Loading failed' })) : isFetching || isLoading || isPending || isSearching ? (
@@ -5540,7 +5544,7 @@ const IdPickerMultiple = ({ column, schema, prefix, }) => {
5540
5544
  // Use renderDisplay from hook (which comes from schema) or fallback to default
5541
5545
  const renderDisplayFunction = renderDisplayFn || defaultRenderDisplay;
5542
5546
  const fieldError = getNestedError(errors, colLabel);
5543
- return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5547
+ return (jsxRuntime.jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
5544
5548
  gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: [currentValue.length > 0 && (jsxRuntime.jsx(react.Flex, { flexFlow: 'wrap', gap: 1, mb: 2, children: currentValue.map((id) => {
5545
5549
  const item = idMap[id];
5546
5550
  // Show loading skeleton while fetching initial values
@@ -5586,7 +5590,7 @@ const NumberInputField = ({ schema, column, prefix, }) => {
5586
5590
  const stringValue = value !== undefined && value !== null && value !== ''
5587
5591
  ? String(value)
5588
5592
  : undefined;
5589
- return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, gridColumn, gridRow, errorText: fieldError, invalid: !!fieldError, children: jsxRuntime.jsxs(react.NumberInput.Root, { value: stringValue, onValueChange: (details) => {
5593
+ return (jsxRuntime.jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, gridColumn, gridRow, errorText: fieldError, invalid: !!fieldError, children: jsxRuntime.jsxs(react.NumberInput.Root, { value: stringValue, onValueChange: (details) => {
5590
5594
  // Store as string or number based on configuration, default to number
5591
5595
  // Handle empty values properly - if value is empty string, store undefined
5592
5596
  if (details.value === '' || details.value === undefined) {
@@ -5631,7 +5635,7 @@ const RecordInput = ({ column, schema, prefix }) => {
5631
5635
  const [newValue, setNewValue] = React.useState();
5632
5636
  const formI18n = useFormLabel(column, prefix, schema);
5633
5637
  const fieldError = errors[column]?.message;
5634
- return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn, gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: [entries.map(([key, value]) => {
5638
+ return (jsxRuntime.jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn, gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: [entries.map(([key, value]) => {
5635
5639
  return (jsxRuntime.jsxs(react.Grid, { templateColumns: '1fr 1fr auto', gap: 1, children: [jsxRuntime.jsx(react.Input, { value: key, onChange: (e) => {
5636
5640
  const filtered = entries.filter(([target]) => {
5637
5641
  return target !== key;
@@ -5681,7 +5685,7 @@ const StringInputField = ({ column, schema, prefix, }) => {
5681
5685
  const colLabel = `${prefix}${column}`;
5682
5686
  const fieldError = getNestedError(errors, colLabel);
5683
5687
  const formI18n = useFormLabel(column, prefix, schema);
5684
- return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, gridColumn: gridColumn, gridRow: gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: jsxRuntime.jsx(react.Input, { ...register(`${colLabel}`, { required: isRequired }), autoComplete: "off" }) }) }));
5688
+ return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, gridColumn: gridColumn, gridRow: gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: jsxRuntime.jsx(react.Input, { ...register(`${colLabel}`, { required: isRequired }), autoComplete: "off" }) }) }));
5685
5689
  };
5686
5690
 
5687
5691
  const Textarea = React__namespace.forwardRef(function Textarea({ value, onChange, ...props }, ref) {
@@ -5701,7 +5705,7 @@ const TextAreaInput = ({ column, schema, prefix, }) => {
5701
5705
  const fieldError = getNestedError(errors, colLabel);
5702
5706
  const formI18n = useFormLabel(column, prefix, schema);
5703
5707
  const watchValue = watch(colLabel);
5704
- return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, gridColumn: gridColumn ?? 'span 4', gridRow: gridRow ?? 'span 1', display: "grid", errorText: fieldError, invalid: !!fieldError, children: jsxRuntime.jsx(Textarea, { value: watchValue, onChange: (value) => setValue(colLabel, value) }) }) }));
5708
+ return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, gridColumn: gridColumn ?? 'span 4', gridRow: gridRow ?? 'span 1', display: "grid", errorText: fieldError, invalid: !!fieldError, children: jsxRuntime.jsx(Textarea, { value: watchValue, onChange: (value) => setValue(colLabel, value) }) }) }));
5705
5709
  };
5706
5710
 
5707
5711
  dayjs.extend(utc);
@@ -5849,7 +5853,7 @@ const TimePicker = ({ column, schema, prefix }) => {
5849
5853
  const timeString = getTimeString(newHour, newMinute, newMeridiem);
5850
5854
  setValue(colLabel, timeString, { shouldValidate: true, shouldDirty: true });
5851
5855
  };
5852
- return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5856
+ return (jsxRuntime.jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
5853
5857
  gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: jsxRuntime.jsxs(react.Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsxRuntime.jsx(react.Popover.Trigger, { asChild: true, children: jsxRuntime.jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
5854
5858
  setOpen(true);
5855
5859
  }, justifyContent: 'start', children: [jsxRuntime.jsx(io.IoMdClock, {}), value ? displayedTime : ''] }) }), insideDialog ? (jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { maxH: "70vh", overflowY: "auto", children: jsxRuntime.jsx(react.Popover.Body, { overflow: "visible", children: jsxRuntime.jsx(TimePicker$1, { hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, startTime: startTime, selectedDate: selectedDate, timezone: timezone, portalled: false, labels: timePickerLabels }) }) }) })) : (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { children: jsxRuntime.jsx(react.Popover.Body, { children: jsxRuntime.jsx(TimePicker$1, { format: "12h", hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, startTime: startTime, selectedDate: selectedDate, timezone: timezone, portalled: false, labels: timePickerLabels }) }) }) }) }))] }) }));
@@ -5972,7 +5976,7 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
5972
5976
  }
5973
5977
  }, timezone: timezone, labels: dateTimePickerLabelsConfig, timePickerLabels: timePickerLabels, portalled: !insideDialog, showQuickActions: dateTimePicker?.showQuickActions ?? false, quickActionLabels: dateTimePickerLabels?.quickActionLabels ??
5974
5978
  dateTimePicker?.quickActionLabels, showTimezoneSelector: dateTimePicker?.showTimezoneSelector ?? false }));
5975
- return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5979
+ return (jsxRuntime.jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
5976
5980
  gridRow, errorText: jsxRuntime.jsx(jsxRuntime.Fragment, { children: fieldError }), invalid: !!fieldError, children: [jsxRuntime.jsx("input", { type: "hidden", name: colLabel, value: selectedDate ?? '', readOnly: true, "aria-hidden": true }), dateTimePickerContent] }));
5977
5981
  };
5978
5982
 
package/dist/index.mjs CHANGED
@@ -3897,6 +3897,10 @@ const useFormLabel = (column, prefix = '', schema) => {
3897
3897
  // Return column name as fallback
3898
3898
  return column;
3899
3899
  },
3900
+ /**
3901
+ * Helper text from JSON Schema `description` (shown below the control).
3902
+ */
3903
+ helperText: schema.description,
3900
3904
  };
3901
3905
  };
3902
3906
 
@@ -3977,7 +3981,7 @@ const BooleanPicker = ({ schema, column, prefix }) => {
3977
3981
  const value = watch(colLabel);
3978
3982
  const formI18n = useFormLabel(column, prefix, schema);
3979
3983
  const fieldError = getNestedError(errors, colLabel);
3980
- return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
3984
+ return (jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
3981
3985
  gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: jsx(CheckboxCard, { checked: value, variant: 'surface', onChange: () => {
3982
3986
  setValue(colLabel, !value);
3983
3987
  } }) }));
@@ -4098,7 +4102,7 @@ const DatePicker = ({ column, schema, prefix }) => {
4098
4102
  console.error(e);
4099
4103
  }
4100
4104
  }, [selectedDate, dateFormat, colLabel, setValue, timezone]);
4101
- return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4105
+ return (jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
4102
4106
  gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: [jsx("input", { type: "hidden", name: colLabel, value: selectedDate ?? '', readOnly: true, "aria-hidden": true }), jsx(DatePickerInput, { value: selectedDate, onChange: (d) => {
4103
4107
  setValue(colLabel, d, {
4104
4108
  shouldValidate: true,
@@ -4195,7 +4199,7 @@ const DateRangePicker = ({ column, schema, prefix, }) => {
4195
4199
  console.error(e);
4196
4200
  }
4197
4201
  }, [selectedDateRange, dateFormat, colLabel, setValue, timezone]);
4198
- return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4202
+ return (jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
4199
4203
  gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: jsxs(Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
4200
4204
  setOpen(true);
4201
4205
  }, justifyContent: 'start', children: [jsx(MdDateRange, {}), getDisplayText()] }) }), insideDialog ? (jsx(Popover.Positioner, { children: jsx(Popover.Content, { width: "fit-content", minW: "50rem", minH: "25rem", children: jsx(Popover.Body, { children: jsx(RangeDatePicker, { selected: selectedDates, timezone: timezone, onDateSelected: ({ selected }) => {
@@ -4326,7 +4330,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4326
4330
  }
4327
4331
  };
4328
4332
  if (variant === 'radio') {
4329
- return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4333
+ return (jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
4330
4334
  gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: jsx(RadioGroup$1.Root, { value: !isMultiple ? watchEnum : undefined, onValueChange: (details) => {
4331
4335
  if (!isMultiple) {
4332
4336
  setValue(colLabel, details.value);
@@ -4335,7 +4339,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
4335
4339
  return (jsxs(RadioGroup$1.Item, { value: item, children: [jsx(RadioGroup$1.ItemHiddenInput, {}), jsx(RadioGroup$1.ItemIndicator, {}), jsx(RadioGroup$1.ItemText, { children: renderEnumValue(item) })] }, `${colLabel}-${item}`));
4336
4340
  }) }) }) }));
4337
4341
  }
4338
- return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4342
+ return (jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
4339
4343
  gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: [isMultiple && currentValue.length > 0 && (jsx(Flex, { flexFlow: 'wrap', gap: 1, mb: 2, children: currentValue.map((enumValue) => {
4340
4344
  if (!enumValue) {
4341
4345
  return null;
@@ -4947,7 +4951,7 @@ const FilePicker = ({ column, schema, prefix }) => {
4947
4951
  const getImageUrl = (file) => {
4948
4952
  return URL.createObjectURL(file);
4949
4953
  };
4950
- return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4954
+ return (jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
4951
4955
  gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: [jsx(VStack, { align: "stretch", gap: 2, children: jsx(FileDropzone, { onDrop: ({ files }) => {
4952
4956
  // file-picker variant: Store File objects directly (no ID conversion)
4953
4957
  if (isSingleSelect) {
@@ -5046,7 +5050,7 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
5046
5050
  // eslint-disable-next-line react-hooks/exhaustive-deps
5047
5051
  }, [currentFileIds.join(',')]);
5048
5052
  if (!onFetchFiles) {
5049
- return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5053
+ return (jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
5050
5054
  gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: jsx(Text, { color: "fg.muted", children: "Media library browser requires onFetchFiles" }) }));
5051
5055
  }
5052
5056
  const handleImageError = (fileIdentifier) => {
@@ -5072,7 +5076,7 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
5072
5076
  setValue(colLabel, newFileIds);
5073
5077
  }
5074
5078
  };
5075
- return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5079
+ return (jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
5076
5080
  gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: [jsx(VStack, { align: "stretch", gap: 2, children: jsx(Button$1, { variant: "outline", onClick: () => setDialogOpen(true), borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: filePickerLabels?.browseLibrary ?? 'Browse from Library' }) }), jsx(MediaBrowserDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ?? formI18n.label() ?? 'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, onUploadFile: onUploadFile, enableUpload: enableUpload, labels: filePickerLabels, colLabel: colLabel }), jsx(Flex, { flexFlow: 'column', gap: 1, children: currentFileIds.map((fileId, index) => {
5077
5081
  const file = fileMap.get(fileId);
5078
5082
  const isImage = file
@@ -5485,7 +5489,7 @@ const IdPickerSingle = ({ column, schema, prefix, }) => {
5485
5489
  ? renderDisplayFunction(selectedItem)
5486
5490
  : null;
5487
5491
  const fieldError = getNestedError(errors, colLabel);
5488
- return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5492
+ return (jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
5489
5493
  gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: jsxs(Combobox.RootProvider, { value: combobox, width: "100%", children: [jsx(Show, { when: selectedId && selectedRendered, children: jsxs(HStack, { justifyContent: 'space-between', children: [jsx(Box, { children: selectedRendered }), currentValue.length > 0 && (jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => {
5490
5494
  setValue(colLabel, '');
5491
5495
  }, children: jsx(Icon, { children: jsx(BiX, {}) }) }))] }) }), jsx(Show, { when: !selectedId || !selectedRendered, children: jsxs(Combobox.Control, { position: "relative", children: [jsx(Combobox.Input, { placeholder: idPickerLabels?.typeToSearch ?? 'Type to search' }), jsxs(Combobox.IndicatorGroup, { children: [(isFetching || isLoading || isPending) && jsx(Spinner, { size: "xs" }), isError && (jsx(Icon, { color: "fg.error", children: jsx(BiError, {}) })), jsx(Combobox.Trigger, {})] })] }) }), insideDialog ? (jsx(Combobox.Positioner, { children: jsx(Combobox.Content, { children: isError ? (jsx(Text, { p: 2, color: "fg.error", fontSize: "sm", children: idPickerLabels?.emptySearchResult ?? 'Loading failed' })) : isFetching || isLoading || isPending || isSearching ? (
@@ -5520,7 +5524,7 @@ const IdPickerMultiple = ({ column, schema, prefix, }) => {
5520
5524
  // Use renderDisplay from hook (which comes from schema) or fallback to default
5521
5525
  const renderDisplayFunction = renderDisplayFn || defaultRenderDisplay;
5522
5526
  const fieldError = getNestedError(errors, colLabel);
5523
- return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5527
+ return (jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
5524
5528
  gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: [currentValue.length > 0 && (jsx(Flex, { flexFlow: 'wrap', gap: 1, mb: 2, children: currentValue.map((id) => {
5525
5529
  const item = idMap[id];
5526
5530
  // Show loading skeleton while fetching initial values
@@ -5566,7 +5570,7 @@ const NumberInputField = ({ schema, column, prefix, }) => {
5566
5570
  const stringValue = value !== undefined && value !== null && value !== ''
5567
5571
  ? String(value)
5568
5572
  : undefined;
5569
- return (jsx(Field, { label: formI18n.label(), required: isRequired, gridColumn, gridRow, errorText: fieldError, invalid: !!fieldError, children: jsxs(NumberInput.Root, { value: stringValue, onValueChange: (details) => {
5573
+ return (jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, gridColumn, gridRow, errorText: fieldError, invalid: !!fieldError, children: jsxs(NumberInput.Root, { value: stringValue, onValueChange: (details) => {
5570
5574
  // Store as string or number based on configuration, default to number
5571
5575
  // Handle empty values properly - if value is empty string, store undefined
5572
5576
  if (details.value === '' || details.value === undefined) {
@@ -5611,7 +5615,7 @@ const RecordInput = ({ column, schema, prefix }) => {
5611
5615
  const [newValue, setNewValue] = useState();
5612
5616
  const formI18n = useFormLabel(column, prefix, schema);
5613
5617
  const fieldError = errors[column]?.message;
5614
- return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn, gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: [entries.map(([key, value]) => {
5618
+ return (jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn, gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: [entries.map(([key, value]) => {
5615
5619
  return (jsxs(Grid, { templateColumns: '1fr 1fr auto', gap: 1, children: [jsx(Input, { value: key, onChange: (e) => {
5616
5620
  const filtered = entries.filter(([target]) => {
5617
5621
  return target !== key;
@@ -5661,7 +5665,7 @@ const StringInputField = ({ column, schema, prefix, }) => {
5661
5665
  const colLabel = `${prefix}${column}`;
5662
5666
  const fieldError = getNestedError(errors, colLabel);
5663
5667
  const formI18n = useFormLabel(column, prefix, schema);
5664
- return (jsx(Fragment, { children: jsx(Field, { label: formI18n.label(), required: isRequired, gridColumn: gridColumn, gridRow: gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: jsx(Input, { ...register(`${colLabel}`, { required: isRequired }), autoComplete: "off" }) }) }));
5668
+ return (jsx(Fragment, { children: jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, gridColumn: gridColumn, gridRow: gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: jsx(Input, { ...register(`${colLabel}`, { required: isRequired }), autoComplete: "off" }) }) }));
5665
5669
  };
5666
5670
 
5667
5671
  const Textarea = React.forwardRef(function Textarea({ value, onChange, ...props }, ref) {
@@ -5681,7 +5685,7 @@ const TextAreaInput = ({ column, schema, prefix, }) => {
5681
5685
  const fieldError = getNestedError(errors, colLabel);
5682
5686
  const formI18n = useFormLabel(column, prefix, schema);
5683
5687
  const watchValue = watch(colLabel);
5684
- return (jsx(Fragment, { children: jsx(Field, { label: formI18n.label(), required: isRequired, gridColumn: gridColumn ?? 'span 4', gridRow: gridRow ?? 'span 1', display: "grid", errorText: fieldError, invalid: !!fieldError, children: jsx(Textarea, { value: watchValue, onChange: (value) => setValue(colLabel, value) }) }) }));
5688
+ return (jsx(Fragment, { children: jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, gridColumn: gridColumn ?? 'span 4', gridRow: gridRow ?? 'span 1', display: "grid", errorText: fieldError, invalid: !!fieldError, children: jsx(Textarea, { value: watchValue, onChange: (value) => setValue(colLabel, value) }) }) }));
5685
5689
  };
5686
5690
 
5687
5691
  dayjs.extend(utc);
@@ -5829,7 +5833,7 @@ const TimePicker = ({ column, schema, prefix }) => {
5829
5833
  const timeString = getTimeString(newHour, newMinute, newMeridiem);
5830
5834
  setValue(colLabel, timeString, { shouldValidate: true, shouldDirty: true });
5831
5835
  };
5832
- return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5836
+ return (jsx(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
5833
5837
  gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: jsxs(Popover.Root, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsx(Popover.Trigger, { asChild: true, children: jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
5834
5838
  setOpen(true);
5835
5839
  }, justifyContent: 'start', children: [jsx(IoMdClock, {}), value ? displayedTime : ''] }) }), insideDialog ? (jsx(Popover.Positioner, { children: jsx(Popover.Content, { maxH: "70vh", overflowY: "auto", children: jsx(Popover.Body, { overflow: "visible", children: jsx(TimePicker$1, { hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, startTime: startTime, selectedDate: selectedDate, timezone: timezone, portalled: false, labels: timePickerLabels }) }) }) })) : (jsx(Portal, { children: jsx(Popover.Positioner, { children: jsx(Popover.Content, { children: jsx(Popover.Body, { children: jsx(TimePicker$1, { format: "12h", hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, startTime: startTime, selectedDate: selectedDate, timezone: timezone, portalled: false, labels: timePickerLabels }) }) }) }) }))] }) }));
@@ -5952,7 +5956,7 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
5952
5956
  }
5953
5957
  }, timezone: timezone, labels: dateTimePickerLabelsConfig, timePickerLabels: timePickerLabels, portalled: !insideDialog, showQuickActions: dateTimePicker?.showQuickActions ?? false, quickActionLabels: dateTimePickerLabels?.quickActionLabels ??
5954
5958
  dateTimePicker?.quickActionLabels, showTimezoneSelector: dateTimePicker?.showTimezoneSelector ?? false }));
5955
- return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5959
+ return (jsxs(Field, { label: formI18n.label(), helperText: formI18n.helperText, required: isRequired, alignItems: 'stretch', gridColumn,
5956
5960
  gridRow, errorText: jsx(Fragment, { children: fieldError }), invalid: !!fieldError, children: [jsx("input", { type: "hidden", name: colLabel, value: selectedDate ?? '', readOnly: true, "aria-hidden": true }), dateTimePickerContent] }));
5957
5961
  };
5958
5962
 
@@ -9,4 +9,8 @@ export declare const useFormLabel: (column: string, prefix: string | undefined,
9
9
  * Logs a debug message if title is missing.
10
10
  */
11
11
  label: () => string;
12
+ /**
13
+ * Helper text from JSON Schema `description` (shown below the control).
14
+ */
15
+ helperText: string | undefined;
12
16
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsol-oss/react-datatable5",
3
- "version": "13.0.1",
3
+ "version": "13.0.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",