@bsol-oss/react-datatable5 12.0.0-beta.21 → 12.0.0-beta.22

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.d.ts CHANGED
@@ -10,10 +10,10 @@ import { UseTranslationResponse } from 'react-i18next';
10
10
  import { RankingInfo } from '@tanstack/match-sorter-utils';
11
11
  import { UseQueryResult } from '@tanstack/react-query';
12
12
  import { JSONSchema7 } from 'json-schema';
13
- import { ForeignKeyProps } from '@/components/Form/components/fields/StringInputField';
13
+ import { ForeignKeyProps as ForeignKeyProps$1 } from '@/components/Form/components/fields/StringInputField';
14
14
  import { AxiosRequestConfig } from 'axios';
15
15
  import * as react_hook_form from 'react-hook-form';
16
- import { FieldValues, UseFormReturn, SubmitHandler } from 'react-hook-form';
16
+ import { UseFormReturn, FieldValues, SubmitHandler } from 'react-hook-form';
17
17
  import { RenderProps, Props } from '@bsol-oss/dayzed-react19';
18
18
 
19
19
  interface DensityToggleButtonProps {
@@ -445,8 +445,34 @@ declare const FilterOptions: ({ column }: FilterOptionsProps) => react_jsx_runti
445
445
 
446
446
  declare const GlobalFilter: () => react_jsx_runtime.JSX.Element;
447
447
 
448
+ interface ForeignKeyProps {
449
+ column: string;
450
+ table: string;
451
+ display_column: string;
452
+ }
453
+
454
+ interface CustomJSONSchema7 extends JSONSchema7 {
455
+ gridColumn?: string;
456
+ gridRow?: string;
457
+ foreign_key?: ForeignKeyProps;
458
+ variant?: string;
459
+ renderDisplay?: (item: unknown) => ReactNode;
460
+ inputRender?: (props: {
461
+ column: string;
462
+ schema: CustomJSONSchema7;
463
+ prefix: string;
464
+ formContext: UseFormReturn;
465
+ }) => ReactNode;
466
+ inputViewerRender?: (props: {
467
+ column: string;
468
+ schema: CustomJSONSchema7;
469
+ prefix: string;
470
+ formContext: UseFormReturn;
471
+ }) => ReactNode;
472
+ }
473
+
448
474
  interface FormRootProps<TData extends FieldValues> {
449
- schema: JSONSchema7;
475
+ schema: CustomJSONSchema7;
450
476
  serverUrl: string;
451
477
  requestUrl?: string;
452
478
  idMap: Record<string, object>;
@@ -469,7 +495,7 @@ interface CustomJSONSchema7Definition extends JSONSchema7 {
469
495
  display_column: string;
470
496
  gridColumn: string;
471
497
  gridRow: string;
472
- foreign_key: ForeignKeyProps;
498
+ foreign_key: ForeignKeyProps$1;
473
499
  children: ReactNode;
474
500
  }
475
501
  declare const idPickerSanityCheck: (column: string, foreign_key?: {
package/dist/index.js CHANGED
@@ -3658,6 +3658,18 @@ const BooleanPicker = ({ schema, column, prefix }) => {
3658
3658
  } }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
3659
3659
  };
3660
3660
 
3661
+ const CustomInput = ({ column, schema, prefix }) => {
3662
+ const formContext = reactHookForm.useFormContext();
3663
+ const { inputRender } = schema;
3664
+ return (inputRender &&
3665
+ inputRender({
3666
+ column,
3667
+ schema,
3668
+ prefix,
3669
+ formContext,
3670
+ }));
3671
+ };
3672
+
3661
3673
  const monthNamesShort = [
3662
3674
  "Jan",
3663
3675
  "Feb",
@@ -3755,9 +3767,9 @@ const DatePicker = ({ column, schema, prefix }) => {
3755
3767
  const selectedDate = watch(colLabel);
3756
3768
  const formatedDate = dayjs(selectedDate).format("YYYY-MM-DD");
3757
3769
  return (jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
3758
- gridRow, children: [jsxRuntime.jsxs(PopoverRoot, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: jsxRuntime.jsx(Button, { size: "sm", variant: "outline", onClick: () => {
3770
+ gridRow, children: [jsxRuntime.jsxs(PopoverRoot, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: jsxRuntime.jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
3759
3771
  setOpen(true);
3760
- }, children: selectedDate !== undefined ? `${formatedDate}` : "" }) }), jsxRuntime.jsx(PopoverContent, { children: jsxRuntime.jsxs(PopoverBody, { children: [jsxRuntime.jsx(PopoverTitle, {}), jsxRuntime.jsx(DatePicker$1
3772
+ }, justifyContent: "start", children: [jsxRuntime.jsx(md.MdDateRange, {}), selectedDate !== undefined ? `${formatedDate}` : ""] }) }), jsxRuntime.jsx(PopoverContent, { children: jsxRuntime.jsxs(PopoverBody, { children: [jsxRuntime.jsx(PopoverTitle, {}), jsxRuntime.jsx(DatePicker$1
3761
3773
  // @ts-expect-error TODO: find appropriate types
3762
3774
  , {
3763
3775
  // @ts-expect-error TODO: find appropriate types
@@ -3782,7 +3794,7 @@ function filterArray(array, searchTerm) {
3782
3794
  const EnumPicker = ({ column, isMultiple = false, schema, prefix, }) => {
3783
3795
  const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
3784
3796
  const { translate } = useSchemaContext();
3785
- const { required } = schema;
3797
+ const { required, variant } = schema;
3786
3798
  const isRequired = required?.some((columnId) => columnId === column);
3787
3799
  const { gridColumn, gridRow, renderDisplay } = schema;
3788
3800
  const [searchText, setSearchText] = React.useState();
@@ -3799,6 +3811,22 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, }) => {
3799
3811
  setSearchText(event.target.value);
3800
3812
  setLimit(10);
3801
3813
  };
3814
+ if (variant === "radio") {
3815
+ return (jsxRuntime.jsx(Field, { label: `${translate.t(removeIndex(`${column}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
3816
+ gridRow, children: jsxRuntime.jsx(react.RadioGroup.Root, { defaultValue: "1", children: jsxRuntime.jsx(react.HStack, { gap: "6", children: filterArray(dataList, searchText ?? "").map((item) => {
3817
+ return (jsxRuntime.jsxs(react.RadioGroup.Item, { onClick: () => {
3818
+ if (!isMultiple) {
3819
+ setOpenSearchResult(false);
3820
+ setValue(colLabel, item);
3821
+ return;
3822
+ }
3823
+ const newSet = new Set([...(watchEnums ?? []), item]);
3824
+ setValue(colLabel, [...newSet]);
3825
+ }, value: item, children: [jsxRuntime.jsx(react.RadioGroup.ItemHiddenInput, {}), jsxRuntime.jsx(react.RadioGroup.ItemIndicator, {}), jsxRuntime.jsx(react.RadioGroup.ItemText, { children: !!renderDisplay === true
3826
+ ? renderDisplay(item)
3827
+ : translate.t(removeIndex(`${colLabel}.${item}`)) })] }, `${colLabel}-${item}`));
3828
+ }) }) }) }));
3829
+ }
3802
3830
  return (jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${column}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
3803
3831
  gridRow, children: [isMultiple && (jsxRuntime.jsxs(react.Flex, { flexFlow: "wrap", gap: 1, children: [watchEnums.map((enumValue) => {
3804
3832
  const item = enumValue;
@@ -3814,7 +3842,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, }) => {
3814
3842
  setOpenSearchResult(true);
3815
3843
  }, children: translate.t(removeIndex(`${colLabel}.add_more`)) })] })), !isMultiple && (jsxRuntime.jsx(Button, { variant: "outline", onClick: () => {
3816
3844
  setOpenSearchResult(true);
3817
- }, children: watchEnum === undefined
3845
+ }, justifyContent: "start", children: watchEnum === undefined
3818
3846
  ? ""
3819
3847
  : translate.t(removeIndex(`${colLabel}.${watchEnum}`)) })), jsxRuntime.jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: "bottom-start" }, children: [jsxRuntime.jsx(PopoverTrigger, {}), jsxRuntime.jsx(PopoverContent, { children: jsxRuntime.jsxs(PopoverBody, { display: "grid", gap: 1, children: [jsxRuntime.jsx(react.Input, { placeholder: translate.t(`${column}.type_to_search`), onChange: (event) => {
3820
3848
  onSearchChange(event);
@@ -4204,7 +4232,7 @@ const FilePicker = ({ column, schema, prefix }) => {
4204
4232
  setValue(column, currentFiles.filter(({ name }) => {
4205
4233
  return name !== file.name;
4206
4234
  }));
4207
- }, display: "flex", flexFlow: "row", alignItems: "center", padding: "2", children: [jsxRuntime.jsx(react.Box, { children: file.name }), jsxRuntime.jsx(ti.TiDeleteOutline, {})] }) }, file.name));
4235
+ }, display: "flex", flexFlow: "row", alignItems: "center", padding: "2", children: [file.type.startsWith("image/") && (jsxRuntime.jsx(react.Image, { src: URL.createObjectURL(file), alt: file.name, boxSize: "50px", objectFit: "cover", borderRadius: "md", marginRight: "2" })), jsxRuntime.jsx(react.Box, { children: file.name }), jsxRuntime.jsx(ti.TiDeleteOutline, {})] }) }, file.name));
4208
4236
  }) }), errors[`${colLabel}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
4209
4237
  };
4210
4238
 
@@ -4337,7 +4365,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
4337
4365
  setOpenSearchResult(true);
4338
4366
  }, children: translate.t(removeIndex(`${colLabel}.add_more`)) })] })), !isMultiple && (jsxRuntime.jsx(Button, { variant: "outline", onClick: () => {
4339
4367
  setOpenSearchResult(true);
4340
- }, children: getPickedValue() })), jsxRuntime.jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: "bottom-start", strategy: "fixed" }, children: [jsxRuntime.jsx(PopoverTrigger, {}), jsxRuntime.jsx(PopoverContent, { children: jsxRuntime.jsxs(PopoverBody, { display: "grid", gap: 1, children: [jsxRuntime.jsx(react.Input, { placeholder: translate.t(removeIndex(`${colLabel}.typeToSearch`)), onChange: (event) => {
4368
+ }, justifyContent: "start", children: getPickedValue() })), jsxRuntime.jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: "bottom-start", strategy: "fixed" }, children: [jsxRuntime.jsx(PopoverTrigger, {}), jsxRuntime.jsx(PopoverContent, { children: jsxRuntime.jsxs(PopoverBody, { display: "grid", gap: 1, children: [jsxRuntime.jsx(react.Input, { placeholder: translate.t(removeIndex(`${colLabel}.typeToSearch`)), onChange: (event) => {
4341
4369
  onSearchChange(event);
4342
4370
  setOpenSearchResult(true);
4343
4371
  }, autoComplete: "off", ref: ref }), jsxRuntime.jsx(PopoverTitle, {}), (searchText?.length ?? 0) > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [isFetching && jsxRuntime.jsx(jsxRuntime.Fragment, { children: "isFetching" }), isLoading && jsxRuntime.jsx(jsxRuntime.Fragment, { children: "isLoading" }), isPending && jsxRuntime.jsx(jsxRuntime.Fragment, { children: "isPending" }), (isFetching || isLoading || isPending) && jsxRuntime.jsx(react.Spinner, {}), isError && (jsxRuntime.jsx(react.Icon, { color: "red.400", children: jsxRuntime.jsx(bi.BiError, {}) })), jsxRuntime.jsx(react.Text, { justifySelf: "center", children: `${translate.t(removeIndex(`${colLabel}.total`))} ${count}, ${translate.t(removeIndex(`${colLabel}.showing`))} ${limit}` }), jsxRuntime.jsxs(react.Grid, { gridTemplateColumns: "repeat(auto-fit, minmax(15rem, 1fr))", overflow: "auto", maxHeight: "50vh", children: [jsxRuntime.jsx(react.Flex, { flexFlow: "column wrap", children:
@@ -4357,7 +4385,9 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
4357
4385
  item[column_ref],
4358
4386
  ]);
4359
4387
  setValue(colLabel, [...newSet]);
4360
- }, opacity: 0.7, _hover: { opacity: 1 }, ...(selected ? { color: "colorPalette.400/50" } : {}), children: !!renderDisplay === true
4388
+ }, opacity: 0.7, _hover: { opacity: 1 }, ...(selected
4389
+ ? { color: "colorPalette.400/50" }
4390
+ : {}), children: !!renderDisplay === true
4361
4391
  ? renderDisplay(item)
4362
4392
  : item[display_column] }, item[column_ref]));
4363
4393
  }) }), isDirty && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: dataList.length <= 0 && (jsxRuntime.jsx(react.Text, { children: translate.t(removeIndex(`${colLabel}.empty_search_result`)) })) }))] }), jsxRuntime.jsx(PaginationRoot, { justifySelf: "center", count: count, pageSize: 10, defaultPage: 1, page: page + 1, onPageChange: (e) => setPage(e.page - 1), children: jsxRuntime.jsxs(react.HStack, { gap: "4", children: [jsxRuntime.jsx(PaginationPrevTrigger, {}), count > 0 && jsxRuntime.jsx(PaginationPageText, {}), jsxRuntime.jsx(PaginationNextTrigger, {})] }) })] }))] }) })] }), errors[`${colLabel}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
@@ -4557,9 +4587,115 @@ const TagPicker = ({ column, schema, prefix }) => {
4557
4587
  }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: (errors[`${column}`]?.message ?? "No error message") }))] }));
4558
4588
  };
4559
4589
 
4590
+ function TimePicker$1({ hour, setHour, minute, setMinute, meridiem, setMeridiem, meridiemLabel = {
4591
+ am: "am",
4592
+ pm: "pm",
4593
+ }, onChange = () => { }, }) {
4594
+ const hours = Array.from({ length: 12 }, (_, i) => {
4595
+ const hour = i + 1;
4596
+ return hour.toString().padStart(2, "0");
4597
+ });
4598
+ const minutes = Array.from({ length: 60 }, (_, i) => {
4599
+ return i.toString().padStart(2, "0");
4600
+ });
4601
+ const hoursCollection = react.createListCollection({
4602
+ items: hours.map((hour) => ({
4603
+ value: hour,
4604
+ label: hour,
4605
+ })),
4606
+ });
4607
+ const minutesCollection = react.createListCollection({
4608
+ items: minutes.map((hour) => ({
4609
+ value: hour,
4610
+ label: hour,
4611
+ })),
4612
+ });
4613
+ const meridiemsCollection = react.createListCollection({
4614
+ items: ["am", "pm"].map((hour) => ({
4615
+ value: hour,
4616
+ label: meridiemLabel[hour] ?? hour,
4617
+ })),
4618
+ });
4619
+ return (jsxRuntime.jsxs(react.Grid, { templateColumns: "auto auto", gap: "4", children: [jsxRuntime.jsxs(react.Flex, { justifyContent: "center", alignItems: "center", gap: "2", children: [jsxRuntime.jsxs(react.Select.Root, { value: [`${hour.toString().padStart(2, "0")}`], onValueChange: (e) => {
4620
+ setHour(parseInt(e.value[0]));
4621
+ onChange({ hour: parseInt(e.value[0]), minute, meridiem });
4622
+ }, collection: hoursCollection, positioning: { sameWidth: true, placement: "bottom" }, children: [jsxRuntime.jsx(react.Select.HiddenSelect, {}), jsxRuntime.jsx(react.Select.Control, { children: jsxRuntime.jsx(react.Select.Trigger, { children: jsxRuntime.jsx(react.Select.ValueText, { placeholder: "Hour" }) }) }), jsxRuntime.jsx(react.Select.Positioner, { children: jsxRuntime.jsx(react.Select.Content, { width: "full", children: hoursCollection.items.map(({ value: hour }) => (jsxRuntime.jsxs(react.Select.Item, { item: hour, children: [hour, jsxRuntime.jsx(react.Select.ItemIndicator, {})] }, hour))) }) })] }), jsxRuntime.jsx(react.Text, { children: ":" }), jsxRuntime.jsxs(react.Select.Root, { value: [`${minute.toString().padStart(2, "0")}`], onValueChange: (e) => {
4623
+ setMinute(parseInt(e.value[0]));
4624
+ onChange({ hour, minute: parseInt(e.value[0]), meridiem });
4625
+ }, collection: minutesCollection, positioning: { sameWidth: true, placement: "bottom" }, children: [jsxRuntime.jsx(react.Select.HiddenSelect, {}), jsxRuntime.jsx(react.Select.Control, { children: jsxRuntime.jsx(react.Select.Trigger, { children: jsxRuntime.jsx(react.Select.ValueText, { placeholder: "Minute" }) }) }), jsxRuntime.jsx(react.Select.Positioner, { children: jsxRuntime.jsx(react.Select.Content, { width: "full", children: minutes.map((minute) => (jsxRuntime.jsxs(react.Select.Item, { item: minute, children: [minute, jsxRuntime.jsx(react.Select.ItemIndicator, {})] }, minute))) }) })] })] }), jsxRuntime.jsxs(react.Select.Root, { value: [meridiem], onValueChange: (e) => {
4626
+ setMeridiem(e.value[0]);
4627
+ onChange({ hour, minute, meridiem: e.value[0] });
4628
+ }, collection: meridiemsCollection, positioning: { sameWidth: true, placement: "bottom" }, children: [jsxRuntime.jsx(react.Select.HiddenSelect, {}), jsxRuntime.jsx(react.Select.Control, { children: jsxRuntime.jsx(react.Select.Trigger, { children: jsxRuntime.jsx(react.Select.ValueText, { placeholder: "am/pm" }) }) }), jsxRuntime.jsx(react.Select.Positioner, { children: jsxRuntime.jsx(react.Select.Content, { width: "full", children: meridiemsCollection.items.map(({ value: hour, label }) => (jsxRuntime.jsxs(react.Select.Item, { item: hour, children: [label, jsxRuntime.jsx(react.Select.ItemIndicator, {})] }, hour))) }) })] })] }));
4629
+ }
4630
+
4631
+ const TimePicker = ({ column, schema, prefix }) => {
4632
+ const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
4633
+ const { translate } = useSchemaContext();
4634
+ const { required, gridColumn, gridRow } = schema;
4635
+ const isRequired = required?.some((columnId) => columnId === column);
4636
+ const colLabel = `${prefix}${column}`;
4637
+ const [open, setOpen] = React.useState(false);
4638
+ const value = watch(colLabel);
4639
+ const formatedTime = dayjs(value).format("hh:mm A");
4640
+ // Parse the initial time parts from the ISO time string (HH:mm:ss)
4641
+ const parseTime = (isoTime) => {
4642
+ if (!isoTime)
4643
+ return { hour: 12, minute: 0, meridiem: "am" };
4644
+ const parsed = dayjs(isoTime);
4645
+ if (!parsed.isValid())
4646
+ return { hour: 12, minute: 0, meridiem: "am" };
4647
+ let hour = parsed.hour();
4648
+ const minute = parsed.minute();
4649
+ const meridiem = hour >= 12 ? "pm" : "am";
4650
+ if (hour === 0)
4651
+ hour = 12;
4652
+ else if (hour > 12)
4653
+ hour -= 12;
4654
+ return { hour, minute, meridiem };
4655
+ };
4656
+ const initialTime = parseTime(value);
4657
+ const [hour, setHour] = React.useState(initialTime.hour);
4658
+ const [minute, setMinute] = React.useState(initialTime.minute);
4659
+ const [meridiem, setMeridiem] = React.useState(initialTime.meridiem);
4660
+ React.useEffect(() => {
4661
+ const { hour, minute, meridiem } = parseTime(value);
4662
+ setHour(hour);
4663
+ setMinute(minute);
4664
+ setMeridiem(meridiem);
4665
+ }, [value]);
4666
+ // Convert hour, minute, meridiem to 24-hour ISO time string
4667
+ const toIsoTime = (hour, minute, meridiem) => {
4668
+ let h = hour;
4669
+ if (meridiem === "am" && hour === 12)
4670
+ h = 0;
4671
+ else if (meridiem === "pm" && hour < 12)
4672
+ h = hour + 12;
4673
+ return dayjs().hour(h).minute(minute).second(0).toISOString();
4674
+ };
4675
+ // Handle changes to time parts
4676
+ const handleTimeChange = ({ hour: newHour, minute: newMinute, meridiem: newMeridiem, }) => {
4677
+ setHour(newHour);
4678
+ setMinute(newMinute);
4679
+ setMeridiem(newMeridiem);
4680
+ const isoTime = toIsoTime(newHour, newMinute, newMeridiem);
4681
+ setValue(colLabel, isoTime, { shouldValidate: true, shouldDirty: true });
4682
+ };
4683
+ const containerRef = React.useRef(null);
4684
+ return (jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
4685
+ gridRow, 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: () => {
4686
+ setOpen(true);
4687
+ }, justifyContent: "start", children: [jsxRuntime.jsx(io.IoMdClock, {}), value !== undefined ? `${formatedTime}` : ""] }) }), jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Popover.Positioner, { children: jsxRuntime.jsx(react.Popover.Content, { ref: containerRef, children: jsxRuntime.jsx(react.Popover.Body, { children: jsxRuntime.jsx(TimePicker$1, { hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, meridiemLabel: {
4688
+ am: translate.t(removeIndex(`${colLabel}.am`)),
4689
+ pm: translate.t(removeIndex(`${colLabel}.pm`)),
4690
+ } }) }) }) }) })] }), errors[`${column}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
4691
+ };
4692
+
4560
4693
  const SchemaRenderer = ({ schema, prefix, column, }) => {
4561
4694
  const colSchema = schema;
4562
4695
  const { type, variant, properties: innerProperties, foreign_key, items, } = schema;
4696
+ if (variant === "custom-input") {
4697
+ return jsxRuntime.jsx(CustomInput, { schema: colSchema, prefix, column });
4698
+ }
4563
4699
  if (type === "string") {
4564
4700
  if ((schema.enum ?? []).length > 0) {
4565
4701
  return jsxRuntime.jsx(EnumPicker, { schema: colSchema, prefix, column });
@@ -4571,6 +4707,9 @@ const SchemaRenderer = ({ schema, prefix, column, }) => {
4571
4707
  if (variant === "date-picker") {
4572
4708
  return jsxRuntime.jsx(DatePicker, { schema: colSchema, prefix, column });
4573
4709
  }
4710
+ if (variant === "time-picker") {
4711
+ return jsxRuntime.jsx(TimePicker, { schema: colSchema, prefix, column });
4712
+ }
4574
4713
  return jsxRuntime.jsx(StringInputField, { schema: colSchema, prefix, column });
4575
4714
  }
4576
4715
  if (type === "number" || type === "integer") {
@@ -4674,22 +4813,15 @@ const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
4674
4813
  };
4675
4814
 
4676
4815
  const FileViewer = ({ column, schema, prefix }) => {
4677
- const { setValue, formState: { errors }, watch, } = reactHookForm.useFormContext();
4816
+ const { watch } = reactHookForm.useFormContext();
4678
4817
  const { translate } = useSchemaContext();
4679
4818
  const { required, gridColumn, gridRow } = schema;
4680
4819
  const isRequired = required?.some((columnId) => columnId === column);
4681
4820
  const currentFiles = (watch(column) ?? []);
4682
4821
  const colLabel = `${prefix}${column}`;
4683
- return (jsxRuntime.jsxs(Field, { label: `${translate.t(`${colLabel}.field_label`)}`, required: isRequired, gridColumn: gridColumn ?? "span 4", gridRow: gridRow ?? "span 1", display: "grid", gridTemplateRows: "auto 1fr auto", alignItems: "stretch", children: [jsxRuntime.jsx(FileDropzone, { onDrop: ({ files }) => {
4684
- const newFiles = files.filter(({ name }) => !currentFiles.some((cur) => cur.name === name));
4685
- setValue(colLabel, [...currentFiles, ...newFiles]);
4686
- }, placeholder: translate.t(`${colLabel}.fileDropzone`) }), jsxRuntime.jsx(react.Flex, { flexFlow: "column", gap: 1, children: currentFiles.map((file) => {
4687
- return (jsxRuntime.jsx(react.Card.Root, { variant: "subtle", children: jsxRuntime.jsxs(react.Card.Body, { gap: "2", cursor: "pointer", onClick: () => {
4688
- setValue(column, currentFiles.filter(({ name }) => {
4689
- return name !== file.name;
4690
- }));
4691
- }, display: "flex", flexFlow: "row", alignItems: "center", padding: "2", children: [jsxRuntime.jsx(react.Box, { children: file.name }), jsxRuntime.jsx(ti.TiDeleteOutline, {})] }) }, file.name));
4692
- }) }), errors[`${colLabel}`] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
4822
+ return (jsxRuntime.jsx(Field, { label: `${translate.t(`${colLabel}.field_label`)}`, required: isRequired, gridColumn: gridColumn ?? "span 4", gridRow: gridRow ?? "span 1", display: "grid", gridTemplateRows: "auto 1fr auto", alignItems: "stretch", children: jsxRuntime.jsx(react.Flex, { flexFlow: "column", gap: 1, children: currentFiles.map((file) => {
4823
+ return (jsxRuntime.jsx(react.Card.Root, { variant: "subtle", children: jsxRuntime.jsxs(react.Card.Body, { gap: "2", display: "flex", flexFlow: "row", alignItems: "center", padding: "2", children: [file.type.startsWith("image/") && (jsxRuntime.jsx(react.Image, { src: URL.createObjectURL(file), alt: file.name, boxSize: "50px", objectFit: "cover", borderRadius: "md", marginRight: "2" })), jsxRuntime.jsx(react.Box, { children: file.name })] }) }, file.name));
4824
+ }) }) }));
4693
4825
  };
4694
4826
 
4695
4827
  const IdViewer = ({ column, schema, prefix, isMultiple = false, }) => {
@@ -4898,9 +5030,24 @@ const StringViewer = ({ column, schema, prefix, }) => {
4898
5030
  return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn: gridColumn ?? "span 4", gridRow: gridRow ?? "span 1", children: [jsxRuntime.jsx(react.Text, { children: value }), errors[colLabel] && (jsxRuntime.jsx(react.Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }) }));
4899
5031
  };
4900
5032
 
5033
+ const CustomViewer = ({ column, schema, prefix }) => {
5034
+ const formContext = reactHookForm.useFormContext();
5035
+ const { inputViewerRender } = schema;
5036
+ return (inputViewerRender &&
5037
+ inputViewerRender({
5038
+ column,
5039
+ schema,
5040
+ prefix,
5041
+ formContext,
5042
+ }));
5043
+ };
5044
+
4901
5045
  const SchemaViewer = ({ schema, prefix, column, }) => {
4902
5046
  const colSchema = schema;
4903
5047
  const { type, variant, properties: innerProperties, foreign_key, items, } = schema;
5048
+ if (variant === "custom-input") {
5049
+ return jsxRuntime.jsx(CustomViewer, { schema: colSchema, prefix, column });
5050
+ }
4904
5051
  if (type === "string") {
4905
5052
  if ((schema.enum ?? []).length > 0) {
4906
5053
  return jsxRuntime.jsx(EnumViewer, { schema: colSchema, prefix, column });
package/dist/index.mjs CHANGED
@@ -1,16 +1,16 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal, Dialog, Flex, Text, useDisclosure, DialogBackdrop, RadioGroup as RadioGroup$1, Grid, Box, Slider as Slider$1, HStack, For, Tag as Tag$1, Input, Menu, createRecipeContext, createContext as createContext$1, Pagination as Pagination$1, usePaginationContext, CheckboxCard as CheckboxCard$1, Image, EmptyState as EmptyState$2, VStack, Alert, Card, Tooltip as Tooltip$1, Group, InputElement, Icon, List, Table as Table$1, Checkbox as Checkbox$1, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Accordion, Field as Field$1, Popover, NumberInput, Show, RadioCard, CheckboxGroup, Center, Heading } from '@chakra-ui/react';
2
+ import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal, Dialog, Flex, Text, useDisclosure, DialogBackdrop, RadioGroup as RadioGroup$1, Grid, Box, Slider as Slider$1, HStack, For, Tag as Tag$1, Input, Menu, createRecipeContext, createContext as createContext$1, Pagination as Pagination$1, usePaginationContext, CheckboxCard as CheckboxCard$1, Image, EmptyState as EmptyState$2, VStack, Alert, Card, Tooltip as Tooltip$1, Group, InputElement, Icon, List, Table as Table$1, Checkbox as Checkbox$1, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Accordion, Field as Field$1, Popover, NumberInput, Show, RadioCard, CheckboxGroup, createListCollection, Select, Center, Heading } from '@chakra-ui/react';
3
3
  import { AiOutlineColumnWidth } from 'react-icons/ai';
4
4
  import * as React from 'react';
5
5
  import React__default, { createContext, useContext, useState, useEffect, useRef } from 'react';
6
6
  import { LuX, LuCheck, LuChevronRight, LuChevronDown } from 'react-icons/lu';
7
- import { MdOutlineSort, MdFilterAlt, MdSearch, MdClose, MdOutlineViewColumn, MdFilterListAlt, MdPushPin, MdCancel, MdClear, MdOutlineChecklist } from 'react-icons/md';
7
+ import { MdOutlineSort, MdFilterAlt, MdSearch, MdClose, MdOutlineViewColumn, MdFilterListAlt, MdPushPin, MdCancel, MdClear, MdOutlineChecklist, MdDateRange } from 'react-icons/md';
8
8
  import { FaUpDown, FaGripLinesVertical } from 'react-icons/fa6';
9
9
  import { BiDownArrow, BiUpArrow, BiError } from 'react-icons/bi';
10
10
  import { CgClose } from 'react-icons/cg';
11
11
  import Dayzed from '@bsol-oss/dayzed-react19';
12
12
  import { HiMiniEllipsisHorizontal, HiChevronLeft, HiChevronRight } from 'react-icons/hi2';
13
- import { IoMdEye, IoMdCheckbox } from 'react-icons/io';
13
+ import { IoMdEye, IoMdCheckbox, IoMdClock } from 'react-icons/io';
14
14
  import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
15
15
  import { bind, bindAll } from 'bind-event-listener';
16
16
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
@@ -3638,6 +3638,18 @@ const BooleanPicker = ({ schema, column, prefix }) => {
3638
3638
  } }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
3639
3639
  };
3640
3640
 
3641
+ const CustomInput = ({ column, schema, prefix }) => {
3642
+ const formContext = useFormContext();
3643
+ const { inputRender } = schema;
3644
+ return (inputRender &&
3645
+ inputRender({
3646
+ column,
3647
+ schema,
3648
+ prefix,
3649
+ formContext,
3650
+ }));
3651
+ };
3652
+
3641
3653
  const monthNamesShort = [
3642
3654
  "Jan",
3643
3655
  "Feb",
@@ -3735,9 +3747,9 @@ const DatePicker = ({ column, schema, prefix }) => {
3735
3747
  const selectedDate = watch(colLabel);
3736
3748
  const formatedDate = dayjs(selectedDate).format("YYYY-MM-DD");
3737
3749
  return (jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
3738
- gridRow, children: [jsxs(PopoverRoot, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsx(PopoverTrigger, { asChild: true, children: jsx(Button, { size: "sm", variant: "outline", onClick: () => {
3750
+ gridRow, children: [jsxs(PopoverRoot, { open: open, onOpenChange: (e) => setOpen(e.open), closeOnInteractOutside: true, children: [jsx(PopoverTrigger, { asChild: true, children: jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
3739
3751
  setOpen(true);
3740
- }, children: selectedDate !== undefined ? `${formatedDate}` : "" }) }), jsx(PopoverContent, { children: jsxs(PopoverBody, { children: [jsx(PopoverTitle, {}), jsx(DatePicker$1
3752
+ }, justifyContent: "start", children: [jsx(MdDateRange, {}), selectedDate !== undefined ? `${formatedDate}` : ""] }) }), jsx(PopoverContent, { children: jsxs(PopoverBody, { children: [jsx(PopoverTitle, {}), jsx(DatePicker$1
3741
3753
  // @ts-expect-error TODO: find appropriate types
3742
3754
  , {
3743
3755
  // @ts-expect-error TODO: find appropriate types
@@ -3762,7 +3774,7 @@ function filterArray(array, searchTerm) {
3762
3774
  const EnumPicker = ({ column, isMultiple = false, schema, prefix, }) => {
3763
3775
  const { watch, formState: { errors }, setValue, } = useFormContext();
3764
3776
  const { translate } = useSchemaContext();
3765
- const { required } = schema;
3777
+ const { required, variant } = schema;
3766
3778
  const isRequired = required?.some((columnId) => columnId === column);
3767
3779
  const { gridColumn, gridRow, renderDisplay } = schema;
3768
3780
  const [searchText, setSearchText] = useState();
@@ -3779,6 +3791,22 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, }) => {
3779
3791
  setSearchText(event.target.value);
3780
3792
  setLimit(10);
3781
3793
  };
3794
+ if (variant === "radio") {
3795
+ return (jsx(Field, { label: `${translate.t(removeIndex(`${column}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
3796
+ gridRow, children: jsx(RadioGroup$1.Root, { defaultValue: "1", children: jsx(HStack, { gap: "6", children: filterArray(dataList, searchText ?? "").map((item) => {
3797
+ return (jsxs(RadioGroup$1.Item, { onClick: () => {
3798
+ if (!isMultiple) {
3799
+ setOpenSearchResult(false);
3800
+ setValue(colLabel, item);
3801
+ return;
3802
+ }
3803
+ const newSet = new Set([...(watchEnums ?? []), item]);
3804
+ setValue(colLabel, [...newSet]);
3805
+ }, value: item, children: [jsx(RadioGroup$1.ItemHiddenInput, {}), jsx(RadioGroup$1.ItemIndicator, {}), jsx(RadioGroup$1.ItemText, { children: !!renderDisplay === true
3806
+ ? renderDisplay(item)
3807
+ : translate.t(removeIndex(`${colLabel}.${item}`)) })] }, `${colLabel}-${item}`));
3808
+ }) }) }) }));
3809
+ }
3782
3810
  return (jsxs(Field, { label: `${translate.t(removeIndex(`${column}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
3783
3811
  gridRow, children: [isMultiple && (jsxs(Flex, { flexFlow: "wrap", gap: 1, children: [watchEnums.map((enumValue) => {
3784
3812
  const item = enumValue;
@@ -3794,7 +3822,7 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, }) => {
3794
3822
  setOpenSearchResult(true);
3795
3823
  }, children: translate.t(removeIndex(`${colLabel}.add_more`)) })] })), !isMultiple && (jsx(Button, { variant: "outline", onClick: () => {
3796
3824
  setOpenSearchResult(true);
3797
- }, children: watchEnum === undefined
3825
+ }, justifyContent: "start", children: watchEnum === undefined
3798
3826
  ? ""
3799
3827
  : translate.t(removeIndex(`${colLabel}.${watchEnum}`)) })), jsxs(PopoverRoot, { open: openSearchResult, onOpenChange: (e) => setOpenSearchResult(e.open), closeOnInteractOutside: true, initialFocusEl: () => ref.current, positioning: { placement: "bottom-start" }, children: [jsx(PopoverTrigger, {}), jsx(PopoverContent, { children: jsxs(PopoverBody, { display: "grid", gap: 1, children: [jsx(Input, { placeholder: translate.t(`${column}.type_to_search`), onChange: (event) => {
3800
3828
  onSearchChange(event);
@@ -4184,7 +4212,7 @@ const FilePicker = ({ column, schema, prefix }) => {
4184
4212
  setValue(column, currentFiles.filter(({ name }) => {
4185
4213
  return name !== file.name;
4186
4214
  }));
4187
- }, display: "flex", flexFlow: "row", alignItems: "center", padding: "2", children: [jsx(Box, { children: file.name }), jsx(TiDeleteOutline, {})] }) }, file.name));
4215
+ }, display: "flex", flexFlow: "row", alignItems: "center", padding: "2", children: [file.type.startsWith("image/") && (jsx(Image, { src: URL.createObjectURL(file), alt: file.name, boxSize: "50px", objectFit: "cover", borderRadius: "md", marginRight: "2" })), jsx(Box, { children: file.name }), jsx(TiDeleteOutline, {})] }) }, file.name));
4188
4216
  }) }), errors[`${colLabel}`] && (jsx(Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
4189
4217
  };
4190
4218
 
@@ -4317,7 +4345,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
4317
4345
  setOpenSearchResult(true);
4318
4346
  }, children: translate.t(removeIndex(`${colLabel}.add_more`)) })] })), !isMultiple && (jsx(Button, { variant: "outline", onClick: () => {
4319
4347
  setOpenSearchResult(true);
4320
- }, children: 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, { children: jsxs(PopoverBody, { display: "grid", gap: 1, children: [jsx(Input, { placeholder: translate.t(removeIndex(`${colLabel}.typeToSearch`)), onChange: (event) => {
4348
+ }, justifyContent: "start", children: 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, { children: jsxs(PopoverBody, { display: "grid", gap: 1, children: [jsx(Input, { placeholder: translate.t(removeIndex(`${colLabel}.typeToSearch`)), onChange: (event) => {
4321
4349
  onSearchChange(event);
4322
4350
  setOpenSearchResult(true);
4323
4351
  }, autoComplete: "off", ref: ref }), jsx(PopoverTitle, {}), (searchText?.length ?? 0) > 0 && (jsxs(Fragment, { children: [isFetching && jsx(Fragment, { children: "isFetching" }), isLoading && jsx(Fragment, { children: "isLoading" }), isPending && jsx(Fragment, { children: "isPending" }), (isFetching || isLoading || isPending) && jsx(Spinner, {}), isError && (jsx(Icon, { color: "red.400", children: jsx(BiError, {}) })), jsx(Text, { justifySelf: "center", children: `${translate.t(removeIndex(`${colLabel}.total`))} ${count}, ${translate.t(removeIndex(`${colLabel}.showing`))} ${limit}` }), jsxs(Grid, { gridTemplateColumns: "repeat(auto-fit, minmax(15rem, 1fr))", overflow: "auto", maxHeight: "50vh", children: [jsx(Flex, { flexFlow: "column wrap", children:
@@ -4337,7 +4365,9 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
4337
4365
  item[column_ref],
4338
4366
  ]);
4339
4367
  setValue(colLabel, [...newSet]);
4340
- }, opacity: 0.7, _hover: { opacity: 1 }, ...(selected ? { color: "colorPalette.400/50" } : {}), children: !!renderDisplay === true
4368
+ }, opacity: 0.7, _hover: { opacity: 1 }, ...(selected
4369
+ ? { color: "colorPalette.400/50" }
4370
+ : {}), children: !!renderDisplay === true
4341
4371
  ? renderDisplay(item)
4342
4372
  : item[display_column] }, item[column_ref]));
4343
4373
  }) }), isDirty && (jsx(Fragment, { children: dataList.length <= 0 && (jsx(Text, { children: translate.t(removeIndex(`${colLabel}.empty_search_result`)) })) }))] }), jsx(PaginationRoot, { justifySelf: "center", count: count, pageSize: 10, 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: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
@@ -4537,9 +4567,115 @@ const TagPicker = ({ column, schema, prefix }) => {
4537
4567
  }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: (errors[`${column}`]?.message ?? "No error message") }))] }));
4538
4568
  };
4539
4569
 
4570
+ function TimePicker$1({ hour, setHour, minute, setMinute, meridiem, setMeridiem, meridiemLabel = {
4571
+ am: "am",
4572
+ pm: "pm",
4573
+ }, onChange = () => { }, }) {
4574
+ const hours = Array.from({ length: 12 }, (_, i) => {
4575
+ const hour = i + 1;
4576
+ return hour.toString().padStart(2, "0");
4577
+ });
4578
+ const minutes = Array.from({ length: 60 }, (_, i) => {
4579
+ return i.toString().padStart(2, "0");
4580
+ });
4581
+ const hoursCollection = createListCollection({
4582
+ items: hours.map((hour) => ({
4583
+ value: hour,
4584
+ label: hour,
4585
+ })),
4586
+ });
4587
+ const minutesCollection = createListCollection({
4588
+ items: minutes.map((hour) => ({
4589
+ value: hour,
4590
+ label: hour,
4591
+ })),
4592
+ });
4593
+ const meridiemsCollection = createListCollection({
4594
+ items: ["am", "pm"].map((hour) => ({
4595
+ value: hour,
4596
+ label: meridiemLabel[hour] ?? hour,
4597
+ })),
4598
+ });
4599
+ return (jsxs(Grid, { templateColumns: "auto auto", gap: "4", children: [jsxs(Flex, { justifyContent: "center", alignItems: "center", gap: "2", children: [jsxs(Select.Root, { value: [`${hour.toString().padStart(2, "0")}`], onValueChange: (e) => {
4600
+ setHour(parseInt(e.value[0]));
4601
+ onChange({ hour: parseInt(e.value[0]), minute, meridiem });
4602
+ }, collection: hoursCollection, positioning: { sameWidth: true, placement: "bottom" }, children: [jsx(Select.HiddenSelect, {}), jsx(Select.Control, { children: jsx(Select.Trigger, { children: jsx(Select.ValueText, { placeholder: "Hour" }) }) }), jsx(Select.Positioner, { children: jsx(Select.Content, { width: "full", children: hoursCollection.items.map(({ value: hour }) => (jsxs(Select.Item, { item: hour, children: [hour, jsx(Select.ItemIndicator, {})] }, hour))) }) })] }), jsx(Text, { children: ":" }), jsxs(Select.Root, { value: [`${minute.toString().padStart(2, "0")}`], onValueChange: (e) => {
4603
+ setMinute(parseInt(e.value[0]));
4604
+ onChange({ hour, minute: parseInt(e.value[0]), meridiem });
4605
+ }, collection: minutesCollection, positioning: { sameWidth: true, placement: "bottom" }, children: [jsx(Select.HiddenSelect, {}), jsx(Select.Control, { children: jsx(Select.Trigger, { children: jsx(Select.ValueText, { placeholder: "Minute" }) }) }), jsx(Select.Positioner, { children: jsx(Select.Content, { width: "full", children: minutes.map((minute) => (jsxs(Select.Item, { item: minute, children: [minute, jsx(Select.ItemIndicator, {})] }, minute))) }) })] })] }), jsxs(Select.Root, { value: [meridiem], onValueChange: (e) => {
4606
+ setMeridiem(e.value[0]);
4607
+ onChange({ hour, minute, meridiem: e.value[0] });
4608
+ }, collection: meridiemsCollection, positioning: { sameWidth: true, placement: "bottom" }, children: [jsx(Select.HiddenSelect, {}), jsx(Select.Control, { children: jsx(Select.Trigger, { children: jsx(Select.ValueText, { placeholder: "am/pm" }) }) }), jsx(Select.Positioner, { children: jsx(Select.Content, { width: "full", children: meridiemsCollection.items.map(({ value: hour, label }) => (jsxs(Select.Item, { item: hour, children: [label, jsx(Select.ItemIndicator, {})] }, hour))) }) })] })] }));
4609
+ }
4610
+
4611
+ const TimePicker = ({ column, schema, prefix }) => {
4612
+ const { watch, formState: { errors }, setValue, } = useFormContext();
4613
+ const { translate } = useSchemaContext();
4614
+ const { required, gridColumn, gridRow } = schema;
4615
+ const isRequired = required?.some((columnId) => columnId === column);
4616
+ const colLabel = `${prefix}${column}`;
4617
+ const [open, setOpen] = useState(false);
4618
+ const value = watch(colLabel);
4619
+ const formatedTime = dayjs(value).format("hh:mm A");
4620
+ // Parse the initial time parts from the ISO time string (HH:mm:ss)
4621
+ const parseTime = (isoTime) => {
4622
+ if (!isoTime)
4623
+ return { hour: 12, minute: 0, meridiem: "am" };
4624
+ const parsed = dayjs(isoTime);
4625
+ if (!parsed.isValid())
4626
+ return { hour: 12, minute: 0, meridiem: "am" };
4627
+ let hour = parsed.hour();
4628
+ const minute = parsed.minute();
4629
+ const meridiem = hour >= 12 ? "pm" : "am";
4630
+ if (hour === 0)
4631
+ hour = 12;
4632
+ else if (hour > 12)
4633
+ hour -= 12;
4634
+ return { hour, minute, meridiem };
4635
+ };
4636
+ const initialTime = parseTime(value);
4637
+ const [hour, setHour] = useState(initialTime.hour);
4638
+ const [minute, setMinute] = useState(initialTime.minute);
4639
+ const [meridiem, setMeridiem] = useState(initialTime.meridiem);
4640
+ useEffect(() => {
4641
+ const { hour, minute, meridiem } = parseTime(value);
4642
+ setHour(hour);
4643
+ setMinute(minute);
4644
+ setMeridiem(meridiem);
4645
+ }, [value]);
4646
+ // Convert hour, minute, meridiem to 24-hour ISO time string
4647
+ const toIsoTime = (hour, minute, meridiem) => {
4648
+ let h = hour;
4649
+ if (meridiem === "am" && hour === 12)
4650
+ h = 0;
4651
+ else if (meridiem === "pm" && hour < 12)
4652
+ h = hour + 12;
4653
+ return dayjs().hour(h).minute(minute).second(0).toISOString();
4654
+ };
4655
+ // Handle changes to time parts
4656
+ const handleTimeChange = ({ hour: newHour, minute: newMinute, meridiem: newMeridiem, }) => {
4657
+ setHour(newHour);
4658
+ setMinute(newMinute);
4659
+ setMeridiem(newMeridiem);
4660
+ const isoTime = toIsoTime(newHour, newMinute, newMeridiem);
4661
+ setValue(colLabel, isoTime, { shouldValidate: true, shouldDirty: true });
4662
+ };
4663
+ const containerRef = useRef(null);
4664
+ return (jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, alignItems: "stretch", gridColumn,
4665
+ gridRow, 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: () => {
4666
+ setOpen(true);
4667
+ }, justifyContent: "start", children: [jsx(IoMdClock, {}), value !== undefined ? `${formatedTime}` : ""] }) }), jsx(Portal, { children: jsx(Popover.Positioner, { children: jsx(Popover.Content, { ref: containerRef, children: jsx(Popover.Body, { children: jsx(TimePicker$1, { hour: hour, setHour: setHour, minute: minute, setMinute: setMinute, meridiem: meridiem, setMeridiem: setMeridiem, onChange: handleTimeChange, meridiemLabel: {
4668
+ am: translate.t(removeIndex(`${colLabel}.am`)),
4669
+ pm: translate.t(removeIndex(`${colLabel}.pm`)),
4670
+ } }) }) }) }) })] }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
4671
+ };
4672
+
4540
4673
  const SchemaRenderer = ({ schema, prefix, column, }) => {
4541
4674
  const colSchema = schema;
4542
4675
  const { type, variant, properties: innerProperties, foreign_key, items, } = schema;
4676
+ if (variant === "custom-input") {
4677
+ return jsx(CustomInput, { schema: colSchema, prefix, column });
4678
+ }
4543
4679
  if (type === "string") {
4544
4680
  if ((schema.enum ?? []).length > 0) {
4545
4681
  return jsx(EnumPicker, { schema: colSchema, prefix, column });
@@ -4551,6 +4687,9 @@ const SchemaRenderer = ({ schema, prefix, column, }) => {
4551
4687
  if (variant === "date-picker") {
4552
4688
  return jsx(DatePicker, { schema: colSchema, prefix, column });
4553
4689
  }
4690
+ if (variant === "time-picker") {
4691
+ return jsx(TimePicker, { schema: colSchema, prefix, column });
4692
+ }
4554
4693
  return jsx(StringInputField, { schema: colSchema, prefix, column });
4555
4694
  }
4556
4695
  if (type === "number" || type === "integer") {
@@ -4654,22 +4793,15 @@ const EnumViewer = ({ column, isMultiple = false, schema, prefix, }) => {
4654
4793
  };
4655
4794
 
4656
4795
  const FileViewer = ({ column, schema, prefix }) => {
4657
- const { setValue, formState: { errors }, watch, } = useFormContext();
4796
+ const { watch } = useFormContext();
4658
4797
  const { translate } = useSchemaContext();
4659
4798
  const { required, gridColumn, gridRow } = schema;
4660
4799
  const isRequired = required?.some((columnId) => columnId === column);
4661
4800
  const currentFiles = (watch(column) ?? []);
4662
4801
  const colLabel = `${prefix}${column}`;
4663
- return (jsxs(Field, { label: `${translate.t(`${colLabel}.field_label`)}`, required: isRequired, gridColumn: gridColumn ?? "span 4", gridRow: gridRow ?? "span 1", display: "grid", gridTemplateRows: "auto 1fr auto", alignItems: "stretch", children: [jsx(FileDropzone, { onDrop: ({ files }) => {
4664
- const newFiles = files.filter(({ name }) => !currentFiles.some((cur) => cur.name === name));
4665
- setValue(colLabel, [...currentFiles, ...newFiles]);
4666
- }, placeholder: translate.t(`${colLabel}.fileDropzone`) }), jsx(Flex, { flexFlow: "column", gap: 1, children: currentFiles.map((file) => {
4667
- return (jsx(Card.Root, { variant: "subtle", children: jsxs(Card.Body, { gap: "2", cursor: "pointer", onClick: () => {
4668
- setValue(column, currentFiles.filter(({ name }) => {
4669
- return name !== file.name;
4670
- }));
4671
- }, display: "flex", flexFlow: "row", alignItems: "center", padding: "2", children: [jsx(Box, { children: file.name }), jsx(TiDeleteOutline, {})] }) }, file.name));
4672
- }) }), errors[`${colLabel}`] && (jsx(Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }));
4802
+ return (jsx(Field, { label: `${translate.t(`${colLabel}.field_label`)}`, required: isRequired, gridColumn: gridColumn ?? "span 4", gridRow: gridRow ?? "span 1", display: "grid", gridTemplateRows: "auto 1fr auto", alignItems: "stretch", children: jsx(Flex, { flexFlow: "column", gap: 1, children: currentFiles.map((file) => {
4803
+ return (jsx(Card.Root, { variant: "subtle", children: jsxs(Card.Body, { gap: "2", display: "flex", flexFlow: "row", alignItems: "center", padding: "2", children: [file.type.startsWith("image/") && (jsx(Image, { src: URL.createObjectURL(file), alt: file.name, boxSize: "50px", objectFit: "cover", borderRadius: "md", marginRight: "2" })), jsx(Box, { children: file.name })] }) }, file.name));
4804
+ }) }) }));
4673
4805
  };
4674
4806
 
4675
4807
  const IdViewer = ({ column, schema, prefix, isMultiple = false, }) => {
@@ -4878,9 +5010,24 @@ const StringViewer = ({ column, schema, prefix, }) => {
4878
5010
  return (jsx(Fragment, { children: jsxs(Field, { label: `${translate.t(removeIndex(`${colLabel}.field_label`))}`, required: isRequired, gridColumn: gridColumn ?? "span 4", gridRow: gridRow ?? "span 1", children: [jsx(Text, { children: value }), errors[colLabel] && (jsx(Text, { color: "red.400", children: translate.t(removeIndex(`${colLabel}.field_required`)) }))] }) }));
4879
5011
  };
4880
5012
 
5013
+ const CustomViewer = ({ column, schema, prefix }) => {
5014
+ const formContext = useFormContext();
5015
+ const { inputViewerRender } = schema;
5016
+ return (inputViewerRender &&
5017
+ inputViewerRender({
5018
+ column,
5019
+ schema,
5020
+ prefix,
5021
+ formContext,
5022
+ }));
5023
+ };
5024
+
4881
5025
  const SchemaViewer = ({ schema, prefix, column, }) => {
4882
5026
  const colSchema = schema;
4883
5027
  const { type, variant, properties: innerProperties, foreign_key, items, } = schema;
5028
+ if (variant === "custom-input") {
5029
+ return jsx(CustomViewer, { schema: colSchema, prefix, column });
5030
+ }
4884
5031
  if (type === "string") {
4885
5032
  if ((schema.enum ?? []).length > 0) {
4886
5033
  return jsx(EnumViewer, { schema: colSchema, prefix, column });
@@ -4,8 +4,9 @@ import { JSONSchema7 } from "json-schema";
4
4
  import { Dispatch, ReactNode, SetStateAction } from "react";
5
5
  import { FieldValues, SubmitHandler, UseFormReturn } from "react-hook-form";
6
6
  import { UseTranslationResponse } from "react-i18next";
7
+ import { CustomJSONSchema7 } from "../types/CustomJSONSchema7";
7
8
  export interface FormRootProps<TData extends FieldValues> {
8
- schema: JSONSchema7;
9
+ schema: CustomJSONSchema7;
9
10
  serverUrl: string;
10
11
  requestUrl?: string;
11
12
  idMap: Record<string, object>;
@@ -0,0 +1,8 @@
1
+ /// <reference types="react" />
2
+ import { CustomJSONSchema7 } from "../types/CustomJSONSchema7";
3
+ export interface DatePickerProps {
4
+ column: string;
5
+ schema: CustomJSONSchema7;
6
+ prefix: string;
7
+ }
8
+ export declare const CustomInput: ({ column, schema, prefix }: DatePickerProps) => import("react").ReactNode;
@@ -0,0 +1,7 @@
1
+ import { CustomJSONSchema7 } from "../types/CustomJSONSchema7";
2
+ export interface DatePickerProps {
3
+ column: string;
4
+ schema: CustomJSONSchema7;
5
+ prefix: string;
6
+ }
7
+ export declare const TimePicker: ({ column, schema, prefix }: DatePickerProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,12 +1,25 @@
1
1
  import { JSONSchema7 } from "json-schema";
2
2
  import { ReactNode } from "react";
3
3
  import { ForeignKeyProps } from "../fields/StringInputField";
4
+ import { UseFormReturn } from "react-hook-form";
4
5
  export interface CustomJSONSchema7 extends JSONSchema7 {
5
6
  gridColumn?: string;
6
7
  gridRow?: string;
7
8
  foreign_key?: ForeignKeyProps;
8
9
  variant?: string;
9
- renderDisplay: (item: unknown) => ReactNode;
10
+ renderDisplay?: (item: unknown) => ReactNode;
11
+ inputRender?: (props: {
12
+ column: string;
13
+ schema: CustomJSONSchema7;
14
+ prefix: string;
15
+ formContext: UseFormReturn;
16
+ }) => ReactNode;
17
+ inputViewerRender?: (props: {
18
+ column: string;
19
+ schema: CustomJSONSchema7;
20
+ prefix: string;
21
+ formContext: UseFormReturn;
22
+ }) => ReactNode;
10
23
  }
11
24
  export interface TagPickerProps {
12
25
  column: string;
@@ -0,0 +1,8 @@
1
+ /// <reference types="react" />
2
+ import { CustomJSONSchema7 } from "../types/CustomJSONSchema7";
3
+ export interface DatePickerProps {
4
+ column: string;
5
+ schema: CustomJSONSchema7;
6
+ prefix: string;
7
+ }
8
+ export declare const CustomViewer: ({ column, schema, prefix }: DatePickerProps) => import("react").ReactNode;
@@ -0,0 +1,19 @@
1
+ interface TimePickerProps {
2
+ hour: number;
3
+ setHour: (hour: number) => void;
4
+ minute: number;
5
+ setMinute: (minute: number) => void;
6
+ meridiem: "am" | "pm";
7
+ setMeridiem: (meridiem: "am" | "pm") => void;
8
+ onChange?: (newValue: {
9
+ hour: number;
10
+ minute: number;
11
+ meridiem: "am" | "pm";
12
+ }) => void;
13
+ meridiemLabel?: {
14
+ am: string;
15
+ pm: string;
16
+ };
17
+ }
18
+ export declare function TimePicker({ hour, setHour, minute, setMinute, meridiem, setMeridiem, meridiemLabel, onChange, }: TimePickerProps): import("react/jsx-runtime").JSX.Element;
19
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsol-oss/react-datatable5",
3
- "version": "12.0.0-beta.21",
3
+ "version": "12.0.0-beta.22",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",