@bsol-oss/react-datatable5 13.0.1-beta.6 → 13.0.1-beta.7

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
@@ -4393,39 +4393,31 @@ const FormRoot = ({ schema, idMap, setIdMap, form, translate, children, order =
4393
4393
  }, children: jsxRuntime.jsx(reactHookForm.FormProvider, { ...form, children: children }) }));
4394
4394
  };
4395
4395
 
4396
- function removeIndex(str) {
4397
- return str.replace(/\.\d+\./g, ".");
4398
- }
4399
-
4400
4396
  /**
4401
- * Custom hook for form field labels and fallback text.
4402
- * Automatically handles colLabel construction and removeIndex logic.
4403
- * Uses schema.title when available, otherwise falls back to translate function.
4397
+ * Custom hook for form field labels.
4398
+ * Automatically handles colLabel construction.
4399
+ * Uses schema.title for labels and schema.errorMessages for error messages.
4404
4400
  *
4405
4401
  * @param column - The column name
4406
4402
  * @param prefix - The prefix for the field (usually empty string or parent path)
4407
- * @param schema - Required schema object with title property
4403
+ * @param schema - Required schema object with title and errorMessages properties
4408
4404
  * @returns Object with label helper functions
4409
4405
  *
4410
4406
  * @example
4411
4407
  * ```tsx
4412
4408
  * const formI18n = useFormI18n(column, prefix, schema);
4413
4409
  *
4414
- * // Get field label (prefers schema.title)
4410
+ * // Get field label (from schema.title)
4415
4411
  * <Field label={formI18n.label()} />
4416
4412
  *
4417
- * // Get required error message
4413
+ * // Get required error message (from schema.errorMessages?.required)
4418
4414
  * <Text>{formI18n.required()}</Text>
4419
4415
  *
4420
- * // Get custom text
4421
- * <Text>{formI18n.t('add_more')}</Text>
4422
- *
4423
4416
  * // Access the raw colLabel
4424
4417
  * const colLabel = formI18n.colLabel;
4425
4418
  * ```
4426
4419
  */
4427
4420
  const useFormI18n = (column, prefix = '', schema) => {
4428
- const { translate } = useSchemaContext();
4429
4421
  const colLabel = `${prefix}${column}`;
4430
4422
  return {
4431
4423
  /**
@@ -4433,10 +4425,10 @@ const useFormI18n = (column, prefix = '', schema) => {
4433
4425
  */
4434
4426
  colLabel,
4435
4427
  /**
4436
- * Get the field label from schema title prop, or fall back to translate function
4437
- * Uses schema.title if available, otherwise: translate.t(removeIndex(`${colLabel}.field_label`))
4428
+ * Get the field label from schema title property.
4429
+ * Logs a debug message if title is missing.
4438
4430
  */
4439
- label: (options) => {
4431
+ label: () => {
4440
4432
  if (schema.title) {
4441
4433
  return schema.title;
4442
4434
  }
@@ -4452,29 +4444,36 @@ const useFormI18n = (column, prefix = '', schema) => {
4452
4444
  : undefined,
4453
4445
  },
4454
4446
  });
4455
- return translate.t(removeIndex(`${colLabel}.field_label`), options);
4447
+ // Return column name as fallback
4448
+ return column;
4456
4449
  },
4457
4450
  /**
4458
- * Get the required error message
4459
- * Equivalent to: translate.t(removeIndex(`${colLabel}.field_required`))
4460
- */
4461
- required: (options) => {
4462
- return translate.t(removeIndex(`${colLabel}.field_required`), options);
4463
- },
4464
- /**
4465
- * Get text for any custom key relative to the field
4466
- * Equivalent to: translate.t(removeIndex(`${colLabel}.${key}`))
4467
- *
4468
- * @param key - The key suffix (e.g., 'add_more', 'total', etc.)
4469
- * @param options - Optional options (e.g., defaultValue, interpolation variables)
4451
+ * Get the required error message from schema.errorMessages?.required.
4452
+ * Returns a helpful fallback message if not provided.
4470
4453
  */
4471
- t: (key, options) => {
4472
- return translate.t(removeIndex(`${colLabel}.${key}`), options);
4454
+ required: () => {
4455
+ const errorMessage = schema.errorMessages?.required;
4456
+ if (errorMessage) {
4457
+ return errorMessage;
4458
+ }
4459
+ // Debug log when error message is missing
4460
+ console.debug(`[Form Field Required] Missing error message for required field '${colLabel}'. Add errorMessages.required to schema for field '${colLabel}'.`, {
4461
+ fieldName: column,
4462
+ colLabel,
4463
+ prefix,
4464
+ schema: {
4465
+ type: schema.type,
4466
+ title: schema.title,
4467
+ required: schema.required,
4468
+ hasErrorMessages: !!schema.errorMessages,
4469
+ errorMessageKeys: schema.errorMessages
4470
+ ? Object.keys(schema.errorMessages)
4471
+ : undefined,
4472
+ },
4473
+ });
4474
+ // Return helpful fallback message
4475
+ return `Missing error message for required. Add errorMessages.required to schema for field '${colLabel}'`;
4473
4476
  },
4474
- /**
4475
- * Access to the original translate object for edge cases
4476
- */
4477
- translate,
4478
4477
  };
4479
4478
  };
4480
4479
 
@@ -5090,21 +5089,37 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
5090
5089
  const watchEnum = watch(colLabel);
5091
5090
  const watchEnums = (watch(colLabel) ?? []);
5092
5091
  const dataList = schema.enum ?? [];
5092
+ // Helper function to render enum value
5093
+ // If renderDisplay is provided, use it; otherwise show the enum string value directly
5094
+ const renderEnumValue = (value) => {
5095
+ if (renderDisplay) {
5096
+ return renderDisplay(value);
5097
+ }
5098
+ // If no renderDisplay provided, show the enum string value directly
5099
+ return value;
5100
+ };
5101
+ // Debug log when renderDisplay is missing
5102
+ if (!renderDisplay) {
5103
+ console.debug(`[EnumPicker] Missing renderDisplay for field '${colLabel}'. Add renderDisplay function to schema for field '${colLabel}' to provide custom UI rendering. Currently showing enum string values directly.`, {
5104
+ fieldName: column,
5105
+ colLabel,
5106
+ prefix,
5107
+ enumValues: dataList,
5108
+ });
5109
+ }
5093
5110
  // Current value for combobox (array format)
5094
5111
  const currentValue = isMultiple
5095
5112
  ? watchEnums.filter((val) => val != null && val !== '')
5096
5113
  : watchEnum
5097
5114
  ? [watchEnum]
5098
5115
  : [];
5099
- // Transform enum data for combobox collection
5100
5116
  const comboboxItems = React.useMemo(() => {
5101
5117
  return dataList.map((item) => ({
5102
- label: !!renderDisplay === true
5103
- ? String(renderDisplay(item))
5104
- : formI18n.t(item),
5118
+ label: item, // Internal: used for search/filtering only
5105
5119
  value: item,
5120
+ raw: item, // Passed to renderEnumValue for UI rendering
5106
5121
  }));
5107
- }, [dataList, renderDisplay, formI18n]);
5122
+ }, [dataList]);
5108
5123
  // Use filter hook for combobox
5109
5124
  const { contains } = react.useFilter({ sensitivity: 'base' });
5110
5125
  // Create collection for combobox
@@ -5134,9 +5149,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
5134
5149
  setValue(colLabel, details.value);
5135
5150
  }
5136
5151
  }, children: jsxRuntime.jsx(react.HStack, { gap: "6", children: dataList.map((item) => {
5137
- 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: !!renderDisplay === true
5138
- ? renderDisplay(item)
5139
- : formI18n.t(item) })] }, `${colLabel}-${item}`));
5152
+ 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}`));
5140
5153
  }) }) }) }));
5141
5154
  }
5142
5155
  return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
@@ -5147,16 +5160,12 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
5147
5160
  return (jsxRuntime.jsx(Tag, { size: "lg", closable: true, onClick: () => {
5148
5161
  const newValue = currentValue.filter((val) => val !== enumValue);
5149
5162
  setValue(colLabel, newValue);
5150
- }, children: !!renderDisplay === true
5151
- ? renderDisplay(enumValue)
5152
- : formI18n.t(enumValue) }, enumValue));
5163
+ }, children: renderEnumValue(enumValue) }, enumValue));
5153
5164
  }) })), jsxRuntime.jsxs(react.Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%", positioning: insideDialog
5154
5165
  ? { strategy: 'fixed', hideWhenDetached: true }
5155
- : undefined, children: [jsxRuntime.jsxs(react.Combobox.Control, { children: [jsxRuntime.jsx(react.Combobox.Input, { placeholder: enumPickerLabels?.typeToSearch ?? formI18n.t('type_to_search') }), jsxRuntime.jsxs(react.Combobox.IndicatorGroup, { children: [!isMultiple && currentValue.length > 0 && (jsxRuntime.jsx(react.Combobox.ClearTrigger, { onClick: () => {
5166
+ : undefined, children: [jsxRuntime.jsxs(react.Combobox.Control, { children: [jsxRuntime.jsx(react.Combobox.Input, { placeholder: enumPickerLabels?.typeToSearch ?? 'Type to search' }), jsxRuntime.jsxs(react.Combobox.IndicatorGroup, { children: [!isMultiple && currentValue.length > 0 && (jsxRuntime.jsx(react.Combobox.ClearTrigger, { onClick: () => {
5156
5167
  setValue(colLabel, '');
5157
- } })), jsxRuntime.jsx(react.Combobox.Trigger, {})] })] }), insideDialog ? (jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsxs(react.Combobox.Content, { children: [showTotalAndLimit && (jsxRuntime.jsx(react.Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${collection.items.length}` })), collection.items.length === 0 ? (jsxRuntime.jsx(react.Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ??
5158
- formI18n.t('empty_search_result') })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: collection.items.map((item, index) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsx(react.Combobox.ItemText, { children: item.label }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) })) : (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsxs(react.Combobox.Content, { children: [showTotalAndLimit && (jsxRuntime.jsx(react.Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${collection.items.length}` })), collection.items.length === 0 ? (jsxRuntime.jsx(react.Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ??
5159
- formI18n.t('empty_search_result') })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: collection.items.map((item, index) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsx(react.Combobox.ItemText, { children: item.label }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) }) }))] })] }));
5168
+ } })), jsxRuntime.jsx(react.Combobox.Trigger, {})] })] }), insideDialog ? (jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsxs(react.Combobox.Content, { children: [showTotalAndLimit && (jsxRuntime.jsx(react.Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? 'Total'}: ${collection.items.length}` })), collection.items.length === 0 ? (jsxRuntime.jsx(react.Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ?? 'No results found' })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: collection.items.map((item, index) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsx(react.Combobox.ItemText, { children: renderEnumValue(item.raw) }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) })) : (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsxs(react.Combobox.Content, { children: [showTotalAndLimit && (jsxRuntime.jsx(react.Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? 'Total'}: ${collection.items.length}` })), collection.items.length === 0 ? (jsxRuntime.jsx(react.Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ?? 'No results found' })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: collection.items.map((item, index) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsx(react.Combobox.ItemText, { children: renderEnumValue(item.raw) }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) }) }))] })] }));
5160
5169
  };
5161
5170
 
5162
5171
  function isEnteringWindow(_ref) {
@@ -5771,7 +5780,7 @@ const FilePicker = ({ column, schema, prefix }) => {
5771
5780
  const newFiles = files.filter(({ name }) => !currentFiles.some((cur) => cur.name === name));
5772
5781
  setValue(colLabel, [...currentFiles, ...newFiles]);
5773
5782
  }
5774
- }, placeholder: filePickerLabels?.fileDropzone ?? formI18n.t('fileDropzone') }) }), jsxRuntime.jsx(react.Flex, { flexFlow: 'column', gap: 1, children: currentFiles.map((file, index) => {
5783
+ }, placeholder: filePickerLabels?.fileDropzone ?? 'Drop files here' }) }), jsxRuntime.jsx(react.Flex, { flexFlow: 'column', gap: 1, children: currentFiles.map((file, index) => {
5775
5784
  const fileIdentifier = getFileIdentifier(file, index);
5776
5785
  const fileName = getFileName(file);
5777
5786
  const fileSize = getFileSize(file);
@@ -5882,9 +5891,7 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
5882
5891
  }
5883
5892
  };
5884
5893
  return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5885
- gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], 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 ??
5886
- formI18n.t('browse_library') ??
5887
- '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) => {
5894
+ gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], 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) => {
5888
5895
  const file = fileMap.get(fileId);
5889
5896
  const isImage = file
5890
5897
  ? /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name)
@@ -8186,7 +8193,7 @@ const BooleanViewer = ({ schema, column, prefix, }) => {
8186
8193
  const value = watch(colLabel);
8187
8194
  const formI18n = useFormI18n(column, prefix, schema);
8188
8195
  return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
8189
- gridRow, children: [jsxRuntime.jsx(react.Text, { children: value ? formI18n.t('true') : formI18n.t('false') }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: 'red.400', children: formI18n.required() }))] }));
8196
+ gridRow, children: [jsxRuntime.jsx(react.Text, { children: value ? 'True' : 'False' }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: 'red.400', children: formI18n.required() }))] }));
8190
8197
  };
8191
8198
 
8192
8199
  const CustomViewer = ({ column, schema, prefix }) => {
@@ -8225,16 +8232,15 @@ const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
8225
8232
  const colLabel = formI18n.colLabel;
8226
8233
  const watchEnum = watch(colLabel);
8227
8234
  const watchEnums = (watch(colLabel) ?? []);
8235
+ const renderDisplayFunction = renderDisplay || defaultRenderDisplay;
8228
8236
  return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
8229
8237
  gridRow, children: [isMultiple && (jsxRuntime.jsx(react.Flex, { flexFlow: 'wrap', gap: 1, children: watchEnums.map((enumValue) => {
8230
8238
  const item = enumValue;
8231
8239
  if (item === undefined) {
8232
8240
  return jsxRuntime.jsx(jsxRuntime.Fragment, { children: "undefined" });
8233
8241
  }
8234
- return (jsxRuntime.jsx(Tag, { size: "lg", children: !!renderDisplay === true
8235
- ? renderDisplay(item)
8236
- : formI18n.t(item) }, item));
8237
- }) })), !isMultiple && jsxRuntime.jsx(react.Text, { children: formI18n.t(watchEnum) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: 'red.400', children: formI18n.required() }))] }));
8242
+ return (jsxRuntime.jsx(Tag, { size: "lg", children: renderDisplayFunction(item) }, item));
8243
+ }) })), !isMultiple && jsxRuntime.jsx(react.Text, { children: renderDisplayFunction(watchEnum) }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: 'red.400', children: formI18n.required() }))] }));
8238
8244
  };
8239
8245
 
8240
8246
  const FileViewer = ({ column, schema, prefix }) => {
package/dist/index.mjs CHANGED
@@ -4373,39 +4373,31 @@ const FormRoot = ({ schema, idMap, setIdMap, form, translate, children, order =
4373
4373
  }, children: jsx(FormProvider, { ...form, children: children }) }));
4374
4374
  };
4375
4375
 
4376
- function removeIndex(str) {
4377
- return str.replace(/\.\d+\./g, ".");
4378
- }
4379
-
4380
4376
  /**
4381
- * Custom hook for form field labels and fallback text.
4382
- * Automatically handles colLabel construction and removeIndex logic.
4383
- * Uses schema.title when available, otherwise falls back to translate function.
4377
+ * Custom hook for form field labels.
4378
+ * Automatically handles colLabel construction.
4379
+ * Uses schema.title for labels and schema.errorMessages for error messages.
4384
4380
  *
4385
4381
  * @param column - The column name
4386
4382
  * @param prefix - The prefix for the field (usually empty string or parent path)
4387
- * @param schema - Required schema object with title property
4383
+ * @param schema - Required schema object with title and errorMessages properties
4388
4384
  * @returns Object with label helper functions
4389
4385
  *
4390
4386
  * @example
4391
4387
  * ```tsx
4392
4388
  * const formI18n = useFormI18n(column, prefix, schema);
4393
4389
  *
4394
- * // Get field label (prefers schema.title)
4390
+ * // Get field label (from schema.title)
4395
4391
  * <Field label={formI18n.label()} />
4396
4392
  *
4397
- * // Get required error message
4393
+ * // Get required error message (from schema.errorMessages?.required)
4398
4394
  * <Text>{formI18n.required()}</Text>
4399
4395
  *
4400
- * // Get custom text
4401
- * <Text>{formI18n.t('add_more')}</Text>
4402
- *
4403
4396
  * // Access the raw colLabel
4404
4397
  * const colLabel = formI18n.colLabel;
4405
4398
  * ```
4406
4399
  */
4407
4400
  const useFormI18n = (column, prefix = '', schema) => {
4408
- const { translate } = useSchemaContext();
4409
4401
  const colLabel = `${prefix}${column}`;
4410
4402
  return {
4411
4403
  /**
@@ -4413,10 +4405,10 @@ const useFormI18n = (column, prefix = '', schema) => {
4413
4405
  */
4414
4406
  colLabel,
4415
4407
  /**
4416
- * Get the field label from schema title prop, or fall back to translate function
4417
- * Uses schema.title if available, otherwise: translate.t(removeIndex(`${colLabel}.field_label`))
4408
+ * Get the field label from schema title property.
4409
+ * Logs a debug message if title is missing.
4418
4410
  */
4419
- label: (options) => {
4411
+ label: () => {
4420
4412
  if (schema.title) {
4421
4413
  return schema.title;
4422
4414
  }
@@ -4432,29 +4424,36 @@ const useFormI18n = (column, prefix = '', schema) => {
4432
4424
  : undefined,
4433
4425
  },
4434
4426
  });
4435
- return translate.t(removeIndex(`${colLabel}.field_label`), options);
4427
+ // Return column name as fallback
4428
+ return column;
4436
4429
  },
4437
4430
  /**
4438
- * Get the required error message
4439
- * Equivalent to: translate.t(removeIndex(`${colLabel}.field_required`))
4440
- */
4441
- required: (options) => {
4442
- return translate.t(removeIndex(`${colLabel}.field_required`), options);
4443
- },
4444
- /**
4445
- * Get text for any custom key relative to the field
4446
- * Equivalent to: translate.t(removeIndex(`${colLabel}.${key}`))
4447
- *
4448
- * @param key - The key suffix (e.g., 'add_more', 'total', etc.)
4449
- * @param options - Optional options (e.g., defaultValue, interpolation variables)
4431
+ * Get the required error message from schema.errorMessages?.required.
4432
+ * Returns a helpful fallback message if not provided.
4450
4433
  */
4451
- t: (key, options) => {
4452
- return translate.t(removeIndex(`${colLabel}.${key}`), options);
4434
+ required: () => {
4435
+ const errorMessage = schema.errorMessages?.required;
4436
+ if (errorMessage) {
4437
+ return errorMessage;
4438
+ }
4439
+ // Debug log when error message is missing
4440
+ console.debug(`[Form Field Required] Missing error message for required field '${colLabel}'. Add errorMessages.required to schema for field '${colLabel}'.`, {
4441
+ fieldName: column,
4442
+ colLabel,
4443
+ prefix,
4444
+ schema: {
4445
+ type: schema.type,
4446
+ title: schema.title,
4447
+ required: schema.required,
4448
+ hasErrorMessages: !!schema.errorMessages,
4449
+ errorMessageKeys: schema.errorMessages
4450
+ ? Object.keys(schema.errorMessages)
4451
+ : undefined,
4452
+ },
4453
+ });
4454
+ // Return helpful fallback message
4455
+ return `Missing error message for required. Add errorMessages.required to schema for field '${colLabel}'`;
4453
4456
  },
4454
- /**
4455
- * Access to the original translate object for edge cases
4456
- */
4457
- translate,
4458
4457
  };
4459
4458
  };
4460
4459
 
@@ -5070,21 +5069,37 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
5070
5069
  const watchEnum = watch(colLabel);
5071
5070
  const watchEnums = (watch(colLabel) ?? []);
5072
5071
  const dataList = schema.enum ?? [];
5072
+ // Helper function to render enum value
5073
+ // If renderDisplay is provided, use it; otherwise show the enum string value directly
5074
+ const renderEnumValue = (value) => {
5075
+ if (renderDisplay) {
5076
+ return renderDisplay(value);
5077
+ }
5078
+ // If no renderDisplay provided, show the enum string value directly
5079
+ return value;
5080
+ };
5081
+ // Debug log when renderDisplay is missing
5082
+ if (!renderDisplay) {
5083
+ console.debug(`[EnumPicker] Missing renderDisplay for field '${colLabel}'. Add renderDisplay function to schema for field '${colLabel}' to provide custom UI rendering. Currently showing enum string values directly.`, {
5084
+ fieldName: column,
5085
+ colLabel,
5086
+ prefix,
5087
+ enumValues: dataList,
5088
+ });
5089
+ }
5073
5090
  // Current value for combobox (array format)
5074
5091
  const currentValue = isMultiple
5075
5092
  ? watchEnums.filter((val) => val != null && val !== '')
5076
5093
  : watchEnum
5077
5094
  ? [watchEnum]
5078
5095
  : [];
5079
- // Transform enum data for combobox collection
5080
5096
  const comboboxItems = useMemo(() => {
5081
5097
  return dataList.map((item) => ({
5082
- label: !!renderDisplay === true
5083
- ? String(renderDisplay(item))
5084
- : formI18n.t(item),
5098
+ label: item, // Internal: used for search/filtering only
5085
5099
  value: item,
5100
+ raw: item, // Passed to renderEnumValue for UI rendering
5086
5101
  }));
5087
- }, [dataList, renderDisplay, formI18n]);
5102
+ }, [dataList]);
5088
5103
  // Use filter hook for combobox
5089
5104
  const { contains } = useFilter({ sensitivity: 'base' });
5090
5105
  // Create collection for combobox
@@ -5114,9 +5129,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
5114
5129
  setValue(colLabel, details.value);
5115
5130
  }
5116
5131
  }, children: jsx(HStack, { gap: "6", children: dataList.map((item) => {
5117
- return (jsxs(RadioGroup$1.Item, { value: item, children: [jsx(RadioGroup$1.ItemHiddenInput, {}), jsx(RadioGroup$1.ItemIndicator, {}), jsx(RadioGroup$1.ItemText, { children: !!renderDisplay === true
5118
- ? renderDisplay(item)
5119
- : formI18n.t(item) })] }, `${colLabel}-${item}`));
5132
+ 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}`));
5120
5133
  }) }) }) }));
5121
5134
  }
5122
5135
  return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
@@ -5127,16 +5140,12 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
5127
5140
  return (jsx(Tag, { size: "lg", closable: true, onClick: () => {
5128
5141
  const newValue = currentValue.filter((val) => val !== enumValue);
5129
5142
  setValue(colLabel, newValue);
5130
- }, children: !!renderDisplay === true
5131
- ? renderDisplay(enumValue)
5132
- : formI18n.t(enumValue) }, enumValue));
5143
+ }, children: renderEnumValue(enumValue) }, enumValue));
5133
5144
  }) })), jsxs(Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%", positioning: insideDialog
5134
5145
  ? { strategy: 'fixed', hideWhenDetached: true }
5135
- : undefined, children: [jsxs(Combobox.Control, { children: [jsx(Combobox.Input, { placeholder: enumPickerLabels?.typeToSearch ?? formI18n.t('type_to_search') }), jsxs(Combobox.IndicatorGroup, { children: [!isMultiple && currentValue.length > 0 && (jsx(Combobox.ClearTrigger, { onClick: () => {
5146
+ : undefined, children: [jsxs(Combobox.Control, { children: [jsx(Combobox.Input, { placeholder: enumPickerLabels?.typeToSearch ?? 'Type to search' }), jsxs(Combobox.IndicatorGroup, { children: [!isMultiple && currentValue.length > 0 && (jsx(Combobox.ClearTrigger, { onClick: () => {
5136
5147
  setValue(colLabel, '');
5137
- } })), jsx(Combobox.Trigger, {})] })] }), insideDialog ? (jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [showTotalAndLimit && (jsx(Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${collection.items.length}` })), collection.items.length === 0 ? (jsx(Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ??
5138
- formI18n.t('empty_search_result') })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) })) : (jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [showTotalAndLimit && (jsx(Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${collection.items.length}` })), collection.items.length === 0 ? (jsx(Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ??
5139
- formI18n.t('empty_search_result') })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) }) }))] })] }));
5148
+ } })), jsx(Combobox.Trigger, {})] })] }), insideDialog ? (jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [showTotalAndLimit && (jsx(Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? 'Total'}: ${collection.items.length}` })), collection.items.length === 0 ? (jsx(Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ?? 'No results found' })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: renderEnumValue(item.raw) }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) })) : (jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [showTotalAndLimit && (jsx(Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? 'Total'}: ${collection.items.length}` })), collection.items.length === 0 ? (jsx(Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ?? 'No results found' })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: renderEnumValue(item.raw) }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) }) }))] })] }));
5140
5149
  };
5141
5150
 
5142
5151
  function isEnteringWindow(_ref) {
@@ -5751,7 +5760,7 @@ const FilePicker = ({ column, schema, prefix }) => {
5751
5760
  const newFiles = files.filter(({ name }) => !currentFiles.some((cur) => cur.name === name));
5752
5761
  setValue(colLabel, [...currentFiles, ...newFiles]);
5753
5762
  }
5754
- }, placeholder: filePickerLabels?.fileDropzone ?? formI18n.t('fileDropzone') }) }), jsx(Flex, { flexFlow: 'column', gap: 1, children: currentFiles.map((file, index) => {
5763
+ }, placeholder: filePickerLabels?.fileDropzone ?? 'Drop files here' }) }), jsx(Flex, { flexFlow: 'column', gap: 1, children: currentFiles.map((file, index) => {
5755
5764
  const fileIdentifier = getFileIdentifier(file, index);
5756
5765
  const fileName = getFileName(file);
5757
5766
  const fileSize = getFileSize(file);
@@ -5862,9 +5871,7 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
5862
5871
  }
5863
5872
  };
5864
5873
  return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
5865
- gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], 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 ??
5866
- formI18n.t('browse_library') ??
5867
- '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) => {
5874
+ gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], 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) => {
5868
5875
  const file = fileMap.get(fileId);
5869
5876
  const isImage = file
5870
5877
  ? /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name)
@@ -8166,7 +8173,7 @@ const BooleanViewer = ({ schema, column, prefix, }) => {
8166
8173
  const value = watch(colLabel);
8167
8174
  const formI18n = useFormI18n(column, prefix, schema);
8168
8175
  return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
8169
- gridRow, children: [jsx(Text, { children: value ? formI18n.t('true') : formI18n.t('false') }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
8176
+ gridRow, children: [jsx(Text, { children: value ? 'True' : 'False' }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
8170
8177
  };
8171
8178
 
8172
8179
  const CustomViewer = ({ column, schema, prefix }) => {
@@ -8205,16 +8212,15 @@ const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
8205
8212
  const colLabel = formI18n.colLabel;
8206
8213
  const watchEnum = watch(colLabel);
8207
8214
  const watchEnums = (watch(colLabel) ?? []);
8215
+ const renderDisplayFunction = renderDisplay || defaultRenderDisplay;
8208
8216
  return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
8209
8217
  gridRow, children: [isMultiple && (jsx(Flex, { flexFlow: 'wrap', gap: 1, children: watchEnums.map((enumValue) => {
8210
8218
  const item = enumValue;
8211
8219
  if (item === undefined) {
8212
8220
  return jsx(Fragment, { children: "undefined" });
8213
8221
  }
8214
- return (jsx(Tag, { size: "lg", children: !!renderDisplay === true
8215
- ? renderDisplay(item)
8216
- : formI18n.t(item) }, item));
8217
- }) })), !isMultiple && jsx(Text, { children: formI18n.t(watchEnum) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
8222
+ return (jsx(Tag, { size: "lg", children: renderDisplayFunction(item) }, item));
8223
+ }) })), !isMultiple && jsx(Text, { children: renderDisplayFunction(watchEnum) }), errors[`${column}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
8218
8224
  };
8219
8225
 
8220
8226
  const FileViewer = ({ column, schema, prefix }) => {
@@ -1,57 +1,41 @@
1
+ import { CustomJSONSchema7 } from '../components/types/CustomJSONSchema7';
1
2
  /**
2
- * Custom hook for form field labels and fallback text.
3
- * Automatically handles colLabel construction and removeIndex logic.
4
- * Uses schema.title when available, otherwise falls back to translate function.
3
+ * Custom hook for form field labels.
4
+ * Automatically handles colLabel construction.
5
+ * Uses schema.title for labels and schema.errorMessages for error messages.
5
6
  *
6
7
  * @param column - The column name
7
8
  * @param prefix - The prefix for the field (usually empty string or parent path)
8
- * @param schema - Required schema object with title property
9
+ * @param schema - Required schema object with title and errorMessages properties
9
10
  * @returns Object with label helper functions
10
11
  *
11
12
  * @example
12
13
  * ```tsx
13
14
  * const formI18n = useFormI18n(column, prefix, schema);
14
15
  *
15
- * // Get field label (prefers schema.title)
16
+ * // Get field label (from schema.title)
16
17
  * <Field label={formI18n.label()} />
17
18
  *
18
- * // Get required error message
19
+ * // Get required error message (from schema.errorMessages?.required)
19
20
  * <Text>{formI18n.required()}</Text>
20
21
  *
21
- * // Get custom text
22
- * <Text>{formI18n.t('add_more')}</Text>
23
- *
24
22
  * // Access the raw colLabel
25
23
  * const colLabel = formI18n.colLabel;
26
24
  * ```
27
25
  */
28
- export declare const useFormI18n: (column: string, prefix: string | undefined, schema: {
29
- title?: string;
30
- }) => {
26
+ export declare const useFormI18n: (column: string, prefix: string | undefined, schema: CustomJSONSchema7) => {
31
27
  /**
32
28
  * The constructed column label (prefix + column)
33
29
  */
34
30
  colLabel: string;
35
31
  /**
36
- * Get the field label from schema title prop, or fall back to translate function
37
- * Uses schema.title if available, otherwise: translate.t(removeIndex(`${colLabel}.field_label`))
38
- */
39
- label: (options?: any) => string;
40
- /**
41
- * Get the required error message
42
- * Equivalent to: translate.t(removeIndex(`${colLabel}.field_required`))
43
- */
44
- required: (options?: any) => string;
45
- /**
46
- * Get text for any custom key relative to the field
47
- * Equivalent to: translate.t(removeIndex(`${colLabel}.${key}`))
48
- *
49
- * @param key - The key suffix (e.g., 'add_more', 'total', etc.)
50
- * @param options - Optional options (e.g., defaultValue, interpolation variables)
32
+ * Get the field label from schema title property.
33
+ * Logs a debug message if title is missing.
51
34
  */
52
- t: (key: string, options?: any) => string;
35
+ label: () => string;
53
36
  /**
54
- * Access to the original translate object for edge cases
37
+ * Get the required error message from schema.errorMessages?.required.
38
+ * Returns a helpful fallback message if not provided.
55
39
  */
56
- translate: import("../useForm").Translate;
40
+ required: () => string;
57
41
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsol-oss/react-datatable5",
3
- "version": "13.0.1-beta.6",
3
+ "version": "13.0.1-beta.7",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",