@bsol-oss/react-datatable5 12.0.0-beta.71 → 12.0.0-beta.72

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.mjs CHANGED
@@ -3657,8 +3657,8 @@ const AccordionItem = Accordion.Item;
3657
3657
  //@ts-expect-error TODO: find appropriate type
3658
3658
  const SchemaFormContext = createContext({
3659
3659
  schema: {},
3660
- serverUrl: "",
3661
- requestUrl: "",
3660
+ serverUrl: '',
3661
+ requestUrl: '',
3662
3662
  order: [],
3663
3663
  ignore: [],
3664
3664
  include: [],
@@ -3714,11 +3714,11 @@ const idPickerSanityCheck = (column, foreign_key) => {
3714
3714
  throw new Error(`The key column does not exist in properties of column ${column} when using id-picker.`);
3715
3715
  }
3716
3716
  };
3717
- const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, children, order = [], ignore = [], include = [], onSubmit = undefined, rowNumber = undefined, requestOptions = {}, getUpdatedData = () => { }, customErrorRenderer, displayConfig = {
3717
+ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, children, order = [], ignore = [], include = [], onSubmit = undefined, rowNumber = undefined, requestOptions = {}, getUpdatedData = () => { }, customErrorRenderer, customSuccessRenderer, displayConfig = {
3718
3718
  showSubmitButton: true,
3719
3719
  showResetButton: true,
3720
3720
  showTitle: true,
3721
- }, }) => {
3721
+ }, dateTimePickerLabels, idPickerLabels, }) => {
3722
3722
  const [isSuccess, setIsSuccess] = useState(false);
3723
3723
  const [isError, setIsError] = useState(false);
3724
3724
  const [isSubmiting, setIsSubmiting] = useState(false);
@@ -3752,7 +3752,10 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
3752
3752
  setError,
3753
3753
  getUpdatedData,
3754
3754
  customErrorRenderer,
3755
+ customSuccessRenderer,
3755
3756
  displayConfig,
3757
+ dateTimePickerLabels,
3758
+ idPickerLabels,
3756
3759
  }, children: jsx(FormProvider, { ...form, children: children }) }));
3757
3760
  };
3758
3761
 
@@ -4595,12 +4598,12 @@ const getTableData = async ({ serverUrl, in_table, searching = "", where = [], l
4595
4598
 
4596
4599
  const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
4597
4600
  const { watch, formState: { errors }, setValue, } = useFormContext();
4598
- const { serverUrl, idMap, setIdMap, schema: parentSchema, } = useSchemaContext();
4601
+ const { serverUrl, idMap, setIdMap, schema: parentSchema, idPickerLabels, } = useSchemaContext();
4599
4602
  const formI18n = useFormI18n(column, prefix);
4600
- const { required, gridColumn = "span 12", gridRow = "span 1", renderDisplay, foreign_key, } = schema;
4603
+ const { required, gridColumn = 'span 12', gridRow = 'span 1', renderDisplay, foreign_key, } = schema;
4601
4604
  const isRequired = required?.some((columnId) => columnId === column);
4602
4605
  const { table, column: column_ref, display_column, customQueryFn, } = foreign_key;
4603
- const [searchText, setSearchText] = useState("");
4606
+ const [searchText, setSearchText] = useState('');
4604
4607
  const [limit, setLimit] = useState(10);
4605
4608
  const [openSearchResult, setOpenSearchResult] = useState();
4606
4609
  const [page, setPage] = useState(0);
@@ -4614,7 +4617,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
4614
4617
  queryFn: async () => {
4615
4618
  if (customQueryFn) {
4616
4619
  const { data, idMap } = await customQueryFn({
4617
- searching: searchText ?? "",
4620
+ searching: searchText ?? '',
4618
4621
  limit: limit,
4619
4622
  offset: page * limit,
4620
4623
  });
@@ -4625,7 +4628,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
4625
4628
  }
4626
4629
  const data = await getTableData({
4627
4630
  serverUrl,
4628
- searching: searchText ?? "",
4631
+ searching: searchText ?? '',
4629
4632
  in_table: table,
4630
4633
  limit: limit,
4631
4634
  offset: page * limit,
@@ -4655,7 +4658,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
4655
4658
  queryFn: async () => {
4656
4659
  if (customQueryFn) {
4657
4660
  const { data, idMap } = await customQueryFn({
4658
- searching: watchIds.join(","),
4661
+ searching: watchIds.join(','),
4659
4662
  limit: isMultiple ? watchIds.length : 1,
4660
4663
  offset: 0,
4661
4664
  });
@@ -4667,7 +4670,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
4667
4670
  if (!watchId && (!watchIds || watchIds.length === 0)) {
4668
4671
  return { data: [] };
4669
4672
  }
4670
- const searchValue = isMultiple ? watchIds.join(",") : watchId;
4673
+ const searchValue = isMultiple ? watchIds.join(',') : watchId;
4671
4674
  const data = await getTableData({
4672
4675
  serverUrl,
4673
4676
  searching: searchValue,
@@ -4704,7 +4707,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
4704
4707
  useEffect(() => {
4705
4708
  if (openSearchResult) {
4706
4709
  // Reset search text when opening the popover
4707
- setSearchText("");
4710
+ setSearchText('');
4708
4711
  // Reset page to first page
4709
4712
  setPage(0);
4710
4713
  // Fetch initial data
@@ -4730,44 +4733,44 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
4730
4733
  const count = data?.count ?? 0;
4731
4734
  const getPickedValue = () => {
4732
4735
  if (Object.keys(idMap).length <= 0) {
4733
- return "";
4736
+ return '';
4734
4737
  }
4735
4738
  const record = idMap[watchId];
4736
4739
  if (record === undefined) {
4737
- return "";
4740
+ return '';
4738
4741
  }
4739
4742
  if (!!renderDisplay === true) {
4740
4743
  return renderDisplay(record);
4741
4744
  }
4742
4745
  return record[display_column];
4743
4746
  };
4744
- return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: "stretch", gridColumn,
4745
- gridRow, children: [isMultiple && (jsxs(Flex, { flexFlow: "wrap", gap: 1, children: [watchIds.map((id) => {
4747
+ return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
4748
+ gridRow, children: [isMultiple && (jsxs(Flex, { flexFlow: 'wrap', gap: 1, children: [watchIds.map((id) => {
4746
4749
  const item = idMap[id];
4747
4750
  if (item === undefined) {
4748
- return (jsx(Text, { children: formI18n.t('undefined') }, id));
4751
+ return (jsx(Text, { children: idPickerLabels?.undefined ?? formI18n.t('undefined') }, id));
4749
4752
  }
4750
4753
  return (jsx(Tag, { closable: true, onClick: () => {
4751
4754
  setValue(colLabel, watchIds.filter((itemId) => itemId !== item[column_ref]));
4752
4755
  }, children: !!renderDisplay === true
4753
4756
  ? renderDisplay(item)
4754
4757
  : item[display_column] }, id));
4755
- }), jsx(Tag, { cursor: "pointer", onClick: () => {
4758
+ }), jsx(Tag, { cursor: 'pointer', onClick: () => {
4756
4759
  setOpenSearchResult(true);
4757
- }, children: formI18n.t('add_more') })] })), !isMultiple && (jsx(Button, { variant: "outline", onClick: () => {
4760
+ }, children: idPickerLabels?.addMore ?? formI18n.t('add_more') })] })), !isMultiple && (jsx(Button, { variant: 'outline', onClick: () => {
4758
4761
  setOpenSearchResult(true);
4759
- }, justifyContent: "start", children: queryDefault.isLoading ? jsx(Spinner, { size: "sm" }) : getPickedValue() })), jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: "bottom-start", strategy: "fixed" }, children: [jsx(PopoverTrigger, {}), jsx(PopoverContent, { portalled: false, children: jsxs(PopoverBody, { display: "grid", gap: 1, children: [jsx(Input, { placeholder: formI18n.t('type_to_search'), onChange: onSearchChange, autoComplete: "off", ref: ref, value: searchText }), jsx(PopoverTitle, {}), openSearchResult && (jsxs(Fragment, { children: [(isFetching || isLoading || isPending) && jsx(Spinner, {}), isError && (jsx(Icon, { color: "red.400", children: jsx(BiError, {}) })), jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [jsxs(Flex, { alignItems: "center", gap: "2", children: [jsx(InfoTip, { children: `${formI18n.t('total')} ${count}, ${formI18n.t('showing')} ${limit} ${formI18n.t('per_page', { defaultValue: 'per page' })}` }), jsxs(Text, { fontSize: "sm", fontWeight: "bold", children: [count, jsxs(Text, { as: "span", fontSize: "xs", ml: "1", color: "gray.500", children: ["/", " ", count > 0
4762
+ }, justifyContent: 'start', children: queryDefault.isLoading ? jsx(Spinner, { size: "sm" }) : getPickedValue() })), jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: 'bottom-start', strategy: 'fixed' }, children: [jsx(PopoverTrigger, {}), jsx(PopoverContent, { portalled: false, children: jsxs(PopoverBody, { display: 'grid', gap: 1, children: [jsx(Input, { placeholder: idPickerLabels?.typeToSearch ?? formI18n.t('type_to_search'), onChange: onSearchChange, autoComplete: "off", ref: ref, value: searchText }), jsx(PopoverTitle, {}), openSearchResult && (jsxs(Fragment, { children: [(isFetching || isLoading || isPending) && jsx(Spinner, {}), isError && (jsx(Icon, { color: 'red.400', children: jsx(BiError, {}) })), jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [jsxs(Flex, { alignItems: "center", gap: "2", children: [jsx(InfoTip, { children: `${idPickerLabels?.total ?? formI18n.t('total')} ${count}, ${idPickerLabels?.showing ?? formI18n.t('showing')} ${limit} ${idPickerLabels?.perPage ?? formI18n.t('per_page', { defaultValue: 'per page' })}` }), jsxs(Text, { fontSize: "sm", fontWeight: "bold", children: [count, jsxs(Text, { as: "span", fontSize: "xs", ml: "1", color: "gray.500", children: ["/", ' ', count > 0
4760
4763
  ? `${page * limit + 1}-${Math.min((page + 1) * limit, count)}`
4761
- : "0"] })] })] }), jsx(Box, { children: jsxs("select", { value: limit, onChange: handleLimitChange, style: {
4762
- padding: "4px 8px",
4763
- borderRadius: "4px",
4764
- border: "1px solid #ccc",
4765
- fontSize: "14px",
4766
- }, children: [jsx("option", { value: "5", children: "5" }), jsx("option", { value: "10", children: "10" }), jsx("option", { value: "20", children: "20" }), jsx("option", { value: "30", children: "30" })] }) })] }), jsx(Grid, { overflowY: "auto", children: dataList.length > 0 ? (jsx(Flex, { flexFlow: "column wrap", gap: 1, children: dataList.map((item) => {
4764
+ : '0'] })] })] }), jsx(Box, { children: jsxs("select", { value: limit, onChange: handleLimitChange, style: {
4765
+ padding: '4px 8px',
4766
+ borderRadius: '4px',
4767
+ border: '1px solid #ccc',
4768
+ fontSize: '14px',
4769
+ }, children: [jsx("option", { value: "5", children: "5" }), jsx("option", { value: "10", children: "10" }), jsx("option", { value: "20", children: "20" }), jsx("option", { value: "30", children: "30" })] }) })] }), jsx(Grid, { overflowY: 'auto', children: dataList.length > 0 ? (jsx(Flex, { flexFlow: 'column wrap', gap: 1, children: dataList.map((item) => {
4767
4770
  const selected = isMultiple
4768
4771
  ? watchIds.some((id) => item[column_ref] === id)
4769
4772
  : watchId === item[column_ref];
4770
- return (jsx(Box, { cursor: "pointer", onClick: () => {
4773
+ return (jsx(Box, { cursor: 'pointer', onClick: () => {
4771
4774
  if (!isMultiple) {
4772
4775
  setOpenSearchResult(false);
4773
4776
  setValue(colLabel, item[column_ref]);
@@ -4783,15 +4786,17 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
4783
4786
  setValue(colLabel, [...newSet]);
4784
4787
  }, opacity: 0.7, _hover: { opacity: 1 }, ...(selected
4785
4788
  ? {
4786
- color: "colorPalette.400/50",
4787
- fontWeight: "bold",
4789
+ color: 'colorPalette.400/50',
4790
+ fontWeight: 'bold',
4788
4791
  }
4789
4792
  : {}), children: !!renderDisplay === true
4790
4793
  ? renderDisplay(item)
4791
4794
  : item[display_column] }, item[column_ref]));
4792
4795
  }) })) : (jsx(Text, { children: searchText
4793
- ? formI18n.t('empty_search_result')
4794
- : formI18n.t('initial_results') })) }), jsx(PaginationRoot, { justifySelf: "center", count: count, pageSize: limit, defaultPage: 1, page: page + 1, onPageChange: (e) => setPage(e.page - 1), children: jsxs(HStack, { gap: "4", children: [jsx(PaginationPrevTrigger, {}), count > 0 && jsx(PaginationPageText, {}), jsx(PaginationNextTrigger, {})] }) })] }))] }) })] }), errors[`${colLabel}`] && (jsx(Text, { color: "red.400", children: formI18n.required() }))] }));
4796
+ ? idPickerLabels?.emptySearchResult ??
4797
+ formI18n.t('empty_search_result')
4798
+ : idPickerLabels?.initialResults ??
4799
+ formI18n.t('initial_results') })) }), jsx(PaginationRoot, { justifySelf: 'center', count: count, pageSize: limit, defaultPage: 1, page: page + 1, onPageChange: (e) => setPage(e.page - 1), children: jsxs(HStack, { gap: "4", children: [jsx(PaginationPrevTrigger, {}), count > 0 && jsx(PaginationPageText, {}), jsx(PaginationNextTrigger, {})] }) })] }))] }) })] }), errors[`${colLabel}`] && (jsx(Text, { color: 'red.400', children: formI18n.required() }))] }));
4795
4800
  };
4796
4801
 
4797
4802
  const NumberInputRoot = React.forwardRef(function NumberInput$1(props, ref) {
@@ -5478,7 +5483,7 @@ dayjs.extend(utc);
5478
5483
  dayjs.extend(timezone);
5479
5484
  const DateTimePicker = ({ column, schema, prefix, }) => {
5480
5485
  const { watch, formState: { errors }, setValue, } = useFormContext();
5481
- const { timezone } = useSchemaContext();
5486
+ const { timezone, dateTimePickerLabels } = useSchemaContext();
5482
5487
  const formI18n = useFormI18n(column, prefix);
5483
5488
  const { required, gridColumn = "span 12", gridRow = "span 1", displayDateFormat = "YYYY-MM-DD HH:mm:ss",
5484
5489
  // with timezone
@@ -5519,7 +5524,7 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
5519
5524
  }, justifyContent: "start", children: [jsx(MdDateRange, {}), selectedDate !== undefined ? `${displayDate}` : ""] }) }), jsx(PopoverContent, { minW: "450px", children: jsxs(PopoverBody, { children: [jsx(PopoverTitle, {}), jsx(DateTimePicker$1, { value: selectedDate, onChange: (date) => {
5520
5525
  setValue(colLabel, dayjs(date).tz(timezone).format(dateFormat));
5521
5526
  }, timezone: timezone, labels: {
5522
- monthNamesShort: [
5527
+ monthNamesShort: dateTimePickerLabels?.monthNamesShort ?? [
5523
5528
  formI18n.translate.t(`common.month_1`, { defaultValue: "January" }),
5524
5529
  formI18n.translate.t(`common.month_2`, { defaultValue: "February" }),
5525
5530
  formI18n.translate.t(`common.month_3`, { defaultValue: "March" }),
@@ -5533,7 +5538,7 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
5533
5538
  formI18n.translate.t(`common.month_11`, { defaultValue: "November" }),
5534
5539
  formI18n.translate.t(`common.month_12`, { defaultValue: "December" }),
5535
5540
  ],
5536
- weekdayNamesShort: [
5541
+ weekdayNamesShort: dateTimePickerLabels?.weekdayNamesShort ?? [
5537
5542
  formI18n.translate.t(`common.weekday_1`, { defaultValue: "Sun" }),
5538
5543
  formI18n.translate.t(`common.weekday_2`, { defaultValue: "Mon" }),
5539
5544
  formI18n.translate.t(`common.weekday_3`, { defaultValue: "Tue" }),
@@ -5544,10 +5549,10 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
5544
5549
  formI18n.translate.t(`common.weekday_6`, { defaultValue: "Fri" }),
5545
5550
  formI18n.translate.t(`common.weekday_7`, { defaultValue: "Sat" }),
5546
5551
  ],
5547
- backButtonLabel: formI18n.translate.t(`common.back_button`, {
5552
+ backButtonLabel: dateTimePickerLabels?.backButtonLabel ?? formI18n.translate.t(`common.back_button`, {
5548
5553
  defaultValue: "Back",
5549
5554
  }),
5550
- forwardButtonLabel: formI18n.translate.t(`common.forward_button`, {
5555
+ forwardButtonLabel: dateTimePickerLabels?.forwardButtonLabel ?? formI18n.translate.t(`common.forward_button`, {
5551
5556
  defaultValue: "Forward",
5552
5557
  }),
5553
5558
  } })] }) })] }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: formI18n.required() }))] }));
@@ -6073,7 +6078,7 @@ const SubmitButton = () => {
6073
6078
  };
6074
6079
 
6075
6080
  const FormBody = () => {
6076
- const { schema, requestUrl, order, ignore, include, onSubmit, translate, requestOptions, isSuccess, setIsSuccess, isError, setIsError, isSubmiting, setIsSubmiting, isConfirming, setIsConfirming, validatedData, setValidatedData, error, setError, getUpdatedData, customErrorRenderer, displayConfig, } = useSchemaContext();
6081
+ const { schema, requestUrl, order, ignore, include, onSubmit, translate, requestOptions, isSuccess, setIsSuccess, isError, setIsError, isSubmiting, setIsSubmiting, isConfirming, setIsConfirming, validatedData, setValidatedData, error, setError, getUpdatedData, customErrorRenderer, customSuccessRenderer, displayConfig, } = useSchemaContext();
6077
6082
  const { showSubmitButton, showResetButton } = displayConfig;
6078
6083
  const methods = useFormContext();
6079
6084
  const { properties } = schema;
@@ -6170,15 +6175,19 @@ const FormBody = () => {
6170
6175
  include,
6171
6176
  });
6172
6177
  if (isSuccess) {
6173
- return (jsxs(Flex, { flexFlow: "column", gap: "2", children: [jsxs(Alert.Root, { status: "success", children: [jsx(Alert.Indicator, {}), jsx(Alert.Content, { children: jsx(Alert.Title, { children: translate.t("submit_success") }) })] }), jsx(Flex, { justifyContent: "end", children: jsx(Button$1, { onClick: async () => {
6174
- setIsError(false);
6175
- setIsSubmiting(false);
6176
- setIsSuccess(false);
6177
- setIsConfirming(false);
6178
- setValidatedData(undefined);
6179
- const data = await getUpdatedData();
6180
- methods.reset(data);
6181
- }, formNoValidate: true, children: translate.t("submit_again") }) })] }));
6178
+ const resetHandler = async () => {
6179
+ setIsError(false);
6180
+ setIsSubmiting(false);
6181
+ setIsSuccess(false);
6182
+ setIsConfirming(false);
6183
+ setValidatedData(undefined);
6184
+ const data = await getUpdatedData();
6185
+ methods.reset(data);
6186
+ };
6187
+ if (customSuccessRenderer) {
6188
+ return customSuccessRenderer(resetHandler);
6189
+ }
6190
+ return (jsxs(Flex, { flexFlow: "column", gap: "2", children: [jsxs(Alert.Root, { status: "success", children: [jsx(Alert.Indicator, {}), jsx(Alert.Content, { children: jsx(Alert.Title, { children: translate.t("submit_success") }) })] }), jsx(Flex, { justifyContent: "end", children: jsx(Button$1, { onClick: resetHandler, formNoValidate: true, children: translate.t("submit_again") }) })] }));
6182
6191
  }
6183
6192
  if (isConfirming) {
6184
6193
  return (jsxs(Flex, { flexFlow: "column", gap: "2", children: [jsx(Grid, { gap: 4, gridTemplateColumns: "repeat(12, 1fr)", gridTemplateRows: "repeat(12, max-content)", autoFlow: "row", children: ordered.map((column) => {
@@ -6216,12 +6225,12 @@ const DefaultForm = ({ formConfig, }) => {
6216
6225
  return (jsx(FormRoot, { ...formConfig, children: jsxs(Grid, { gap: "2", children: [showTitle && jsx(FormTitle, {}), jsx(FormBody, {})] }) }));
6217
6226
  };
6218
6227
 
6219
- const useForm = ({ preLoadedValues, keyPrefix }) => {
6228
+ const useForm = ({ preLoadedValues, keyPrefix, namespace }) => {
6220
6229
  const form = useForm$1({
6221
6230
  values: preLoadedValues,
6222
6231
  });
6223
6232
  const [idMap, setIdMap] = useState({});
6224
- const translate = useTranslation("", { keyPrefix });
6233
+ const translate = useTranslation(namespace || "", { keyPrefix });
6225
6234
  return {
6226
6235
  form,
6227
6236
  idMap,
@@ -6230,6 +6239,207 @@ const useForm = ({ preLoadedValues, keyPrefix }) => {
6230
6239
  };
6231
6240
  };
6232
6241
 
6242
+ /**
6243
+ * Type definitions for error message configuration
6244
+ */
6245
+ /**
6246
+ * Schema-level error message builder
6247
+ *
6248
+ * Builds a complete errorMessage object compatible with ajv-errors plugin.
6249
+ * Supports both i18n translation keys and plain string messages.
6250
+ *
6251
+ * @param config - Error message configuration
6252
+ * @returns Complete errorMessage object for JSON Schema
6253
+ *
6254
+ * @example
6255
+ * ```typescript
6256
+ * // Simple required field errors
6257
+ * const errorMessage = buildErrorMessages({
6258
+ * required: {
6259
+ * username: "Username is required",
6260
+ * email: "user.email.field_required" // i18n key
6261
+ * }
6262
+ * });
6263
+ *
6264
+ * // With validation rules
6265
+ * const errorMessage = buildErrorMessages({
6266
+ * required: {
6267
+ * password: "Password is required"
6268
+ * },
6269
+ * properties: {
6270
+ * password: {
6271
+ * minLength: "Password must be at least 8 characters",
6272
+ * pattern: "Password must contain letters and numbers"
6273
+ * },
6274
+ * age: {
6275
+ * minimum: "Must be 18 or older",
6276
+ * maximum: "Must be under 120"
6277
+ * }
6278
+ * }
6279
+ * });
6280
+ *
6281
+ * // With global fallbacks
6282
+ * const errorMessage = buildErrorMessages({
6283
+ * required: {
6284
+ * email: "Email is required"
6285
+ * },
6286
+ * minLength: "This field is too short", // applies to all fields
6287
+ * minimum: "Value is too small"
6288
+ * });
6289
+ * ```
6290
+ */
6291
+ const buildErrorMessages = (config) => {
6292
+ const result = {};
6293
+ // Add required field errors
6294
+ if (config.required && Object.keys(config.required).length > 0) {
6295
+ result.required = config.required;
6296
+ }
6297
+ // Add field-specific validation errors
6298
+ if (config.properties && Object.keys(config.properties).length > 0) {
6299
+ result.properties = config.properties;
6300
+ }
6301
+ // Add global fallback error messages
6302
+ const globalKeys = [
6303
+ "minLength",
6304
+ "maxLength",
6305
+ "pattern",
6306
+ "minimum",
6307
+ "maximum",
6308
+ "multipleOf",
6309
+ "format",
6310
+ "type",
6311
+ "enum",
6312
+ ];
6313
+ globalKeys.forEach((key) => {
6314
+ if (config[key]) {
6315
+ result[key] = config[key];
6316
+ }
6317
+ });
6318
+ return result;
6319
+ };
6320
+ /**
6321
+ * Helper function to build required field errors
6322
+ *
6323
+ * Simplifies creating required field error messages, especially useful
6324
+ * for generating i18n translation keys following a pattern.
6325
+ *
6326
+ * @param fields - Array of required field names
6327
+ * @param messageOrGenerator - Either a string template or function to generate messages
6328
+ * @returns Required field error configuration
6329
+ *
6330
+ * @example
6331
+ * ```typescript
6332
+ * // Plain string messages
6333
+ * const required = buildRequiredErrors(
6334
+ * ["username", "email", "password"],
6335
+ * (field) => `${field} is required`
6336
+ * );
6337
+ * // Result: { username: "username is required", email: "email is required", ... }
6338
+ *
6339
+ * // i18n translation keys
6340
+ * const required = buildRequiredErrors(
6341
+ * ["username", "email"],
6342
+ * (field) => `user.${field}.field_required`
6343
+ * );
6344
+ * // Result: { username: "user.username.field_required", email: "user.email.field_required" }
6345
+ *
6346
+ * // Same message for all fields
6347
+ * const required = buildRequiredErrors(
6348
+ * ["username", "email"],
6349
+ * "This field is required"
6350
+ * );
6351
+ * // Result: { username: "This field is required", email: "This field is required" }
6352
+ *
6353
+ * // With keyPrefix for i18n
6354
+ * const required = buildRequiredErrors(
6355
+ * ["username", "email"],
6356
+ * (field) => `${field}.field_required`,
6357
+ * "user"
6358
+ * );
6359
+ * // Result: { username: "user.username.field_required", email: "user.email.field_required" }
6360
+ * ```
6361
+ */
6362
+ const buildRequiredErrors = (fields, messageOrGenerator, keyPrefix = "") => {
6363
+ const result = {};
6364
+ fields.forEach((field) => {
6365
+ if (typeof messageOrGenerator === "function") {
6366
+ const message = messageOrGenerator(field);
6367
+ result[field] = keyPrefix ? `${keyPrefix}.${message}` : message;
6368
+ }
6369
+ else {
6370
+ result[field] = messageOrGenerator;
6371
+ }
6372
+ });
6373
+ return result;
6374
+ };
6375
+ /**
6376
+ * Helper function to build field-specific validation errors
6377
+ *
6378
+ * Creates property-specific error messages for multiple fields at once.
6379
+ *
6380
+ * @param config - Maps field names to their validation error configurations
6381
+ * @returns Properties error configuration
6382
+ *
6383
+ * @example
6384
+ * ```typescript
6385
+ * const properties = buildFieldErrors({
6386
+ * username: {
6387
+ * minLength: "Username must be at least 3 characters",
6388
+ * pattern: "Username can only contain letters and numbers"
6389
+ * },
6390
+ * age: {
6391
+ * minimum: "Must be 18 or older",
6392
+ * maximum: "Must be under 120"
6393
+ * },
6394
+ * email: {
6395
+ * format: "Please enter a valid email address"
6396
+ * }
6397
+ * });
6398
+ * ```
6399
+ */
6400
+ const buildFieldErrors = (config) => {
6401
+ return config;
6402
+ };
6403
+ /**
6404
+ * Helper function to create a complete error message configuration in one call
6405
+ *
6406
+ * Convenient wrapper that combines required and validation errors.
6407
+ *
6408
+ * @param required - Required field error messages
6409
+ * @param properties - Field-specific validation error messages
6410
+ * @param globalFallbacks - Global fallback error messages
6411
+ * @returns Complete error message configuration
6412
+ *
6413
+ * @example
6414
+ * ```typescript
6415
+ * const errorMessage = createErrorMessage(
6416
+ * {
6417
+ * username: "Username is required",
6418
+ * email: "Email is required"
6419
+ * },
6420
+ * {
6421
+ * username: {
6422
+ * minLength: "Username must be at least 3 characters"
6423
+ * },
6424
+ * email: {
6425
+ * format: "Please enter a valid email"
6426
+ * }
6427
+ * },
6428
+ * {
6429
+ * minLength: "This field is too short",
6430
+ * format: "Invalid format"
6431
+ * }
6432
+ * );
6433
+ * ```
6434
+ */
6435
+ const createErrorMessage = (required, properties, globalFallbacks) => {
6436
+ return buildErrorMessages({
6437
+ required,
6438
+ properties,
6439
+ ...globalFallbacks,
6440
+ });
6441
+ };
6442
+
6233
6443
  const getMultiDates = ({ selected, selectedDate, selectedDates, selectable, }) => {
6234
6444
  if (!selectable) {
6235
6445
  return [...selectedDates];
@@ -6246,4 +6456,4 @@ const getMultiDates = ({ selected, selectedDate, selectedDates, selectable, }) =
6246
6456
  }
6247
6457
  };
6248
6458
 
6249
- export { CardHeader, DataDisplay, DataTable, DataTableServer, DefaultCardTitle, DefaultForm, DefaultTable, DensityToggleButton, EditSortingButton, EmptyState$1 as EmptyState, ErrorAlert, FilterDialog, FormBody, FormRoot, FormTitle, GlobalFilter, PageSizeControl, Pagination, RecordDisplay, ReloadButton, ResetFilteringButton, ResetSelectionButton, ResetSortingButton, RowCountText, Table, TableBody, TableCardContainer, TableCards, TableComponent, TableControls, TableDataDisplay, TableFilter, TableFilterTags, TableFooter, TableHeader, TableLoadingComponent, TableSelector, TableSorter, TableViewer, TextCell, ViewDialog, getColumns, getMultiDates, getRangeDates, idPickerSanityCheck, useDataTable, useDataTableContext, useDataTableServer, useForm, widthSanityCheck };
6459
+ export { CardHeader, DataDisplay, DataTable, DataTableServer, DefaultCardTitle, DefaultForm, DefaultTable, DensityToggleButton, EditSortingButton, EmptyState$1 as EmptyState, ErrorAlert, FilterDialog, FormBody, FormRoot, FormTitle, GlobalFilter, PageSizeControl, Pagination, RecordDisplay, ReloadButton, ResetFilteringButton, ResetSelectionButton, ResetSortingButton, RowCountText, Table, TableBody, TableCardContainer, TableCards, TableComponent, TableControls, TableDataDisplay, TableFilter, TableFilterTags, TableFooter, TableHeader, TableLoadingComponent, TableSelector, TableSorter, TableViewer, TextCell, ViewDialog, buildErrorMessages, buildFieldErrors, buildRequiredErrors, createErrorMessage, getColumns, getMultiDates, getRangeDates, idPickerSanityCheck, useDataTable, useDataTableContext, useDataTableServer, useForm, widthSanityCheck };
@@ -1,8 +1,9 @@
1
- import { AxiosRequestConfig } from "axios";
2
- import { JSONSchema7 } from "json-schema";
3
- import { Dispatch, ReactNode, SetStateAction } from "react";
4
- import { FieldValues } from "react-hook-form";
5
- import { UseTranslationResponse } from "react-i18next";
1
+ import { AxiosRequestConfig } from 'axios';
2
+ import { JSONSchema7 } from 'json-schema';
3
+ import { Dispatch, ReactNode, SetStateAction } from 'react';
4
+ import { FieldValues } from 'react-hook-form';
5
+ import { UseTranslationResponse } from 'react-i18next';
6
+ import { DateTimePickerLabels, IdPickerLabels } from './components/types/CustomJSONSchema7';
6
7
  export interface SchemaFormContext<TData extends FieldValues> {
7
8
  schema: JSONSchema7;
8
9
  serverUrl: string;
@@ -30,11 +31,14 @@ export interface SchemaFormContext<TData extends FieldValues> {
30
31
  setError: Dispatch<SetStateAction<unknown>>;
31
32
  getUpdatedData: () => TData | Promise<TData>;
32
33
  customErrorRenderer?: (error: unknown) => ReactNode;
34
+ customSuccessRenderer?: (resetHandler: () => void | Promise<void>) => ReactNode;
33
35
  timezone?: string;
34
36
  displayConfig: {
35
37
  showSubmitButton?: boolean;
36
38
  showResetButton?: boolean;
37
39
  showTitle?: boolean;
38
40
  };
41
+ dateTimePickerLabels?: DateTimePickerLabels;
42
+ idPickerLabels?: IdPickerLabels;
39
43
  }
40
44
  export declare const SchemaFormContext: import("react").Context<SchemaFormContext<unknown>>;
@@ -1 +1,2 @@
1
- export declare const FormBody: <TData extends object>() => import("react/jsx-runtime").JSX.Element;
1
+ /// <reference types="react" />
2
+ export declare const FormBody: <TData extends object>() => string | number | bigint | boolean | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
@@ -1,10 +1,10 @@
1
- import { ForeignKeyProps } from "@/components/Form/components/fields/StringInputField";
2
- import { AxiosRequestConfig } from "axios";
3
- import { JSONSchema7 } from "json-schema";
4
- import { Dispatch, ReactNode, SetStateAction } from "react";
5
- import { FieldValues, SubmitHandler, UseFormReturn } from "react-hook-form";
6
- import { UseTranslationResponse } from "react-i18next";
7
- import { CustomJSONSchema7 } from "../types/CustomJSONSchema7";
1
+ import { ForeignKeyProps } from '@/components/Form/components/fields/StringInputField';
2
+ import { AxiosRequestConfig } from 'axios';
3
+ import { JSONSchema7 } from 'json-schema';
4
+ import { Dispatch, ReactNode, SetStateAction } from 'react';
5
+ import { FieldValues, SubmitHandler, UseFormReturn } from 'react-hook-form';
6
+ import { UseTranslationResponse } from 'react-i18next';
7
+ import { CustomJSONSchema7, DateTimePickerLabels, IdPickerLabels } from '../types/CustomJSONSchema7';
8
8
  export interface FormRootProps<TData extends FieldValues> {
9
9
  schema: CustomJSONSchema7;
10
10
  serverUrl: string;
@@ -22,11 +22,14 @@ export interface FormRootProps<TData extends FieldValues> {
22
22
  requestOptions?: AxiosRequestConfig;
23
23
  getUpdatedData?: () => TData | Promise<TData> | void;
24
24
  customErrorRenderer?: (error: unknown) => ReactNode;
25
+ customSuccessRenderer?: (resetHandler: () => void | Promise<void>) => ReactNode;
25
26
  displayConfig?: {
26
27
  showSubmitButton?: boolean;
27
28
  showResetButton?: boolean;
28
29
  showTitle?: boolean;
29
30
  };
31
+ dateTimePickerLabels?: DateTimePickerLabels;
32
+ idPickerLabels?: IdPickerLabels;
30
33
  }
31
34
  export interface CustomJSONSchema7Definition extends JSONSchema7 {
32
35
  variant: string;
@@ -43,4 +46,4 @@ export declare const idPickerSanityCheck: (column: string, foreign_key?: {
43
46
  column?: string | undefined;
44
47
  display_column?: string | undefined;
45
48
  } | undefined) => void;
46
- export declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, serverUrl, translate, children, order, ignore, include, onSubmit, rowNumber, requestOptions, getUpdatedData, customErrorRenderer, displayConfig, }: FormRootProps<TData>) => import("react/jsx-runtime").JSX.Element;
49
+ export declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, serverUrl, translate, children, order, ignore, include, onSubmit, rowNumber, requestOptions, getUpdatedData, customErrorRenderer, customSuccessRenderer, displayConfig, dateTimePickerLabels, idPickerLabels, }: FormRootProps<TData>) => import("react/jsx-runtime").JSX.Element;
@@ -1,4 +1,4 @@
1
- import { CustomJSONSchema7 } from "../types/CustomJSONSchema7";
1
+ import { CustomJSONSchema7 } from '../types/CustomJSONSchema7';
2
2
  export interface IdPickerProps {
3
3
  column: string;
4
4
  schema: CustomJSONSchema7;
@@ -1,7 +1,23 @@
1
- import { JSONSchema7 } from "json-schema";
2
- import { ReactNode } from "react";
3
- import { ForeignKeyProps } from "../fields/StringInputField";
4
- import { UseFormReturn } from "react-hook-form";
1
+ import { JSONSchema7 } from 'json-schema';
2
+ import { ReactNode } from 'react';
3
+ import { ForeignKeyProps } from '../fields/StringInputField';
4
+ import { UseFormReturn } from 'react-hook-form';
5
+ export interface DateTimePickerLabels {
6
+ monthNamesShort?: string[];
7
+ weekdayNamesShort?: string[];
8
+ backButtonLabel?: string;
9
+ forwardButtonLabel?: string;
10
+ }
11
+ export interface IdPickerLabels {
12
+ undefined?: string;
13
+ addMore?: string;
14
+ typeToSearch?: string;
15
+ total?: string;
16
+ showing?: string;
17
+ perPage?: string;
18
+ emptySearchResult?: string;
19
+ initialResults?: string;
20
+ }
5
21
  export interface CustomJSONSchema7 extends JSONSchema7 {
6
22
  gridColumn?: string;
7
23
  gridRow?: string;
@@ -3,10 +3,11 @@ import { FieldValues } from "react-hook-form";
3
3
  export interface UseFormProps {
4
4
  preLoadedValues?: FieldValues | undefined;
5
5
  keyPrefix?: string;
6
+ namespace?: string;
6
7
  }
7
- export declare const useForm: ({ preLoadedValues, keyPrefix }: UseFormProps) => {
8
+ export declare const useForm: ({ preLoadedValues, keyPrefix, namespace }: UseFormProps) => {
8
9
  form: import("react-hook-form").UseFormReturn<FieldValues, any, undefined>;
9
10
  idMap: Record<string, object>;
10
11
  setIdMap: import("react").Dispatch<import("react").SetStateAction<Record<string, object>>>;
11
- translate: import("react-i18next").UseTranslationResponse<"", string>;
12
+ translate: import("react-i18next").UseTranslationResponse<string, string>;
12
13
  };