@bsol-oss/react-datatable5 8.0.1 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal, Dialog, RadioGroup as RadioGroup$1, Grid, Box, Slider as Slider$1, HStack, For, Flex, Text, Tag as Tag$1, Input, useDisclosure, DialogBackdrop, CheckboxCard as CheckboxCard$1, Menu, createRecipeContext, createContext as createContext$1, Pagination, usePaginationContext, Image, Card, DataList, Checkbox as Checkbox$1, Table as Table$1, Tooltip as Tooltip$1, Icon, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, EmptyState as EmptyState$2, VStack, List, Alert, Group, InputElement, Popover, Field as Field$1, NumberInput, Accordion, Show, RadioCard, CheckboxGroup, Heading, Center } from '@chakra-ui/react';
3
3
  import { AiOutlineColumnWidth } from 'react-icons/ai';
4
4
  import * as React from 'react';
5
- import React__default, { createContext, useContext, useState, useEffect, useRef, useMemo } from 'react';
5
+ import React__default, { createContext, useContext, useState, useEffect, useRef } from 'react';
6
6
  import { MdFilterAlt, MdArrowUpward, MdArrowDownward, MdOutlineMoveDown, MdOutlineSort, MdOutlineViewColumn, MdFilterListAlt, MdPushPin, MdCancel, MdClear, MdOutlineChecklist, MdClose, MdSearch } from 'react-icons/md';
7
7
  import { LuX, LuCheck, LuChevronRight, LuChevronDown } from 'react-icons/lu';
8
8
  import Dayzed from '@bsol-oss/dayzed-react19';
@@ -3540,16 +3540,17 @@ const Field = React.forwardRef(function Field(props, ref) {
3540
3540
  });
3541
3541
 
3542
3542
  const useSchemaContext = () => {
3543
- const { schema, serverUrl, order, ignore, onSubmit, preLoadedValues, rowNumber, displayText, } = useContext(SchemaFormContext);
3543
+ const { schema, serverUrl, order, ignore, onSubmit, rowNumber, displayText, idMap, setIdMap, } = useContext(SchemaFormContext);
3544
3544
  return {
3545
3545
  schema,
3546
3546
  serverUrl,
3547
3547
  order,
3548
3548
  ignore,
3549
3549
  onSubmit,
3550
- preLoadedValues,
3551
3550
  rowNumber,
3552
3551
  displayText,
3552
+ idMap,
3553
+ setIdMap,
3553
3554
  };
3554
3555
  };
3555
3556
 
@@ -3587,7 +3588,7 @@ const getTableData = async ({ serverUrl, in_table, searching = "", where = [], l
3587
3588
 
3588
3589
  const IdPicker = ({ column, isMultiple = false }) => {
3589
3590
  const { watch, formState: { errors }, setValue, } = useFormContext();
3590
- const { schema, serverUrl, displayText } = useSchemaContext();
3591
+ const { schema, serverUrl, displayText, idMap, setIdMap } = useSchemaContext();
3591
3592
  const { fieldRequired } = displayText;
3592
3593
  const { required } = schema;
3593
3594
  const isRequired = required?.some((columnId) => columnId === column);
@@ -3597,21 +3598,20 @@ const IdPicker = ({ column, isMultiple = false }) => {
3597
3598
  const { total, showing, typeToSearch } = displayText;
3598
3599
  const { gridColumn, gridRow, title, renderDisplay, foreign_key } = schema
3599
3600
  .properties[column];
3600
- const { table: in_table, column: column_ref, display_column } = foreign_key;
3601
+ const { table, column: column_ref, display_column, } = foreign_key;
3601
3602
  const [searchText, setSearchText] = useState();
3602
3603
  const [limit, setLimit] = useState(10);
3603
3604
  const [openSearchResult, setOpenSearchResult] = useState();
3604
3605
  const [page, setPage] = useState(0);
3605
- const [idMap, setIdMap] = useState({});
3606
3606
  const ref = useRef(null);
3607
3607
  const selectedIds = watch(column) ?? [];
3608
3608
  const query = useQuery({
3609
- queryKey: [`idpicker`, { searchText, limit, page }],
3609
+ queryKey: [`idpicker`, { column, searchText, limit, page }],
3610
3610
  queryFn: async () => {
3611
3611
  const data = await getTableData({
3612
3612
  serverUrl,
3613
3613
  searching: searchText ?? "",
3614
- in_table: in_table,
3614
+ in_table: table,
3615
3615
  limit: limit,
3616
3616
  offset: page * 10,
3617
3617
  });
@@ -3631,33 +3631,8 @@ const IdPicker = ({ column, isMultiple = false }) => {
3631
3631
  enabled: (searchText ?? "")?.length > 0,
3632
3632
  staleTime: 300000,
3633
3633
  });
3634
- useQuery({
3635
- queryKey: [`idpicker`, { selectedIds }],
3636
- queryFn: async () => {
3637
- const data = await getTableData({
3638
- serverUrl,
3639
- in_table: in_table,
3640
- limit: 1,
3641
- where: [{ id: column_ref, value: watchId }],
3642
- });
3643
- const newMap = Object.fromEntries((data ?? { data: [] }).data.map((item) => {
3644
- return [
3645
- item[column_ref],
3646
- {
3647
- ...item,
3648
- },
3649
- ];
3650
- }));
3651
- setIdMap((state) => {
3652
- return { ...state, ...newMap };
3653
- });
3654
- return data;
3655
- },
3656
- enabled: (selectedIds ?? []).length > 0,
3657
- staleTime: 300000,
3658
- });
3659
3634
  const { isLoading, isFetching, data, isPending, isError } = query;
3660
- const dataList = useMemo(() => data?.data ?? [], [data]);
3635
+ const dataList = data?.data ?? [];
3661
3636
  const count = data?.count ?? 0;
3662
3637
  const isDirty = (searchText?.length ?? 0) > 0;
3663
3638
  const onSearchChange = async (event) => {
@@ -3733,32 +3708,16 @@ const DataListItem = React.forwardRef(function DataListItem(props, ref) {
3733
3708
  return (jsxs(DataList.Item, { ref: ref, ...rest, children: [jsxs(DataList.ItemLabel, { flex: grow ? "1" : undefined, children: [label, info && jsx(InfoTip, { children: info })] }), jsx(DataList.ItemValue, { flex: grow ? "1" : undefined, children: value }), children] }));
3734
3709
  });
3735
3710
 
3736
- const IdViewer = ({ value, column }) => {
3737
- const { schema, serverUrl } = useSchemaContext();
3711
+ const IdViewer = ({ value, column, dataListItemProps, }) => {
3712
+ const { schema, idMap } = useSchemaContext();
3738
3713
  if (schema.properties == undefined) {
3739
3714
  throw new Error("schema properties when using DatePicker");
3740
3715
  }
3741
3716
  const { title, foreign_key } = schema.properties[column];
3742
3717
  if (foreign_key === undefined) {
3743
- throw new Error('foreign_key when variant is id-picker');
3718
+ throw new Error("foreign_key when variant is id-picker");
3744
3719
  }
3745
- const { table, column: foreginKeyColumn, display_column } = foreign_key;
3746
- const query = useQuery({
3747
- queryKey: [`idpicker`, table, value],
3748
- queryFn: async () => {
3749
- return await getTableData({
3750
- serverUrl,
3751
- in_table: foreginKeyColumn ?? '',
3752
- where: [
3753
- {
3754
- id: column,
3755
- value: value,
3756
- },
3757
- ],
3758
- });
3759
- },
3760
- staleTime: 10000,
3761
- });
3720
+ const { display_column } = foreign_key;
3762
3721
  const getDataListProps = (value) => {
3763
3722
  if (value == undefined || value.length <= 0) {
3764
3723
  return {
@@ -3770,7 +3729,10 @@ const IdViewer = ({ value, column }) => {
3770
3729
  value: value,
3771
3730
  };
3772
3731
  };
3773
- return (jsx(Fragment, { children: jsx(DataListItem, { label: `${title ?? snakeToLabel(column)}`, ...getDataListProps((query.data?.data[0] ?? {})[display_column]) }) }));
3732
+ if (value === undefined) {
3733
+ return jsx(Fragment, { children: "undefined" });
3734
+ }
3735
+ return (jsx(DataListItem, { label: `${title ?? snakeToLabel(column)}`, ...getDataListProps(idMap[value][display_column]), ...dataListItemProps }));
3774
3736
  };
3775
3737
 
3776
3738
  const NumberInputRoot = React.forwardRef(function NumberInput$1(props, ref) {
@@ -3928,156 +3890,71 @@ const DatePicker = ({ column }) => {
3928
3890
  } })] }) })] }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: fieldRequired ?? "The field is requried" }))] }));
3929
3891
  };
3930
3892
 
3931
- const ObjectInput = ({ column }) => {
3932
- const { formState: { errors }, setValue, getValues, } = useFormContext();
3893
+ function filterArray(array, searchTerm) {
3894
+ // Convert the search term to lower case for case-insensitive comparison
3895
+ const lowerCaseSearchTerm = searchTerm.toLowerCase();
3896
+ // Use the filter method to return an array of matching items
3897
+ return array.filter((item) => {
3898
+ // Convert each item to a string and check if it includes the search term
3899
+ return item.toString().toLowerCase().includes(lowerCaseSearchTerm);
3900
+ });
3901
+ }
3902
+
3903
+ const EnumPicker = ({ column, isMultiple = false }) => {
3904
+ const { watch, formState: { errors }, setValue, } = useFormContext();
3933
3905
  const { schema, displayText } = useSchemaContext();
3934
- const { addNew, fieldRequired, save } = displayText;
3906
+ const { fieldRequired, total, showing, typeToSearch } = displayText;
3935
3907
  const { required } = schema;
3936
3908
  const isRequired = required?.some((columnId) => columnId === column);
3937
- const entries = Object.entries(getValues(column) ?? {});
3938
- const [showNewEntries, setShowNewEntries] = useState(false);
3939
- const [newKey, setNewKey] = useState();
3940
- const [newValue, setNewValue] = useState();
3941
3909
  if (schema.properties == undefined) {
3942
3910
  throw new Error("schema properties when using DatePicker");
3943
3911
  }
3944
- const { gridColumn, gridRow, title } = schema.properties[column];
3945
- return (jsxs(Field, { label: `${title ?? snakeToLabel(column)}`, required: isRequired, alignItems: "stretch", gridColumn, gridRow, children: [entries.map(([key, value]) => {
3946
- return (jsxs(Grid, { templateColumns: "1fr 1fr auto", gap: 1, children: [jsx(Input, { value: key, onChange: (e) => {
3947
- const filtered = entries.filter(([target]) => {
3948
- return target !== key;
3949
- });
3950
- setValue(column, Object.fromEntries([...filtered, [e.target.value, value]]));
3951
- }, autoComplete: "off" }), jsx(Input, { value: value, onChange: (e) => {
3952
- setValue(column, {
3953
- ...getValues(column),
3954
- [key]: e.target.value,
3955
- });
3956
- }, autoComplete: "off" }), jsx(IconButton, { variant: "ghost", onClick: () => {
3957
- const filtered = entries.filter(([target]) => {
3958
- return target !== key;
3959
- });
3960
- setValue(column, Object.fromEntries([...filtered]));
3961
- }, children: jsx(CgClose, {}) })] }));
3962
- }), jsx(Show, { when: showNewEntries, children: jsxs(Card.Root, { children: [jsx(Card.Body, { gap: "2", children: jsxs(Grid, { templateColumns: "1fr 1fr auto", gap: 1, children: [jsx(Input, { value: newKey, onChange: (e) => {
3963
- setNewKey(e.target.value);
3964
- }, autoComplete: "off" }), jsx(Input, { value: newValue, onChange: (e) => {
3965
- setNewValue(e.target.value);
3966
- }, autoComplete: "off" })] }) }), jsxs(Card.Footer, { justifyContent: "flex-end", children: [jsx(IconButton, { variant: "subtle", onClick: () => {
3967
- setShowNewEntries(false);
3968
- setNewKey(undefined);
3969
- setNewValue(undefined);
3970
- }, children: jsx(CgClose, {}) }), jsx(Button, { onClick: () => {
3971
- if (!!newKey === false) {
3972
- setShowNewEntries(false);
3973
- setNewKey(undefined);
3974
- setNewValue(undefined);
3975
- return;
3976
- }
3977
- setValue(column, Object.fromEntries([...entries, [newKey, newValue]]));
3978
- setShowNewEntries(false);
3979
- setNewKey(undefined);
3980
- setNewValue(undefined);
3981
- }, children: save ?? "Save" })] })] }) }), jsx(Button, { onClick: () => {
3982
- setShowNewEntries(true);
3983
- setNewKey(undefined);
3984
- setNewValue(undefined);
3985
- }, children: addNew ?? "Add New" }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: fieldRequired ?? "The field is requried" }))] }));
3986
- };
3987
-
3988
- const RadioCardItem = React.forwardRef(function RadioCardItem(props, ref) {
3989
- const { inputProps, label, description, addon, icon, indicator = jsx(RadioCard.ItemIndicator, {}), indicatorPlacement = "end", ...rest } = props;
3990
- const hasContent = label || description || icon;
3991
- const ContentWrapper = indicator ? RadioCard.ItemContent : React.Fragment;
3992
- return (jsxs(RadioCard.Item, { ...rest, children: [jsx(RadioCard.ItemHiddenInput, { ref: ref, ...inputProps }), jsxs(RadioCard.ItemControl, { children: [indicatorPlacement === "start" && indicator, hasContent && (jsxs(ContentWrapper, { children: [icon, label && jsx(RadioCard.ItemText, { children: label }), description && (jsx(RadioCard.ItemDescription, { children: description })), indicatorPlacement === "inside" && indicator] })), indicatorPlacement === "end" && indicator] }), addon && jsx(RadioCard.ItemAddon, { children: addon })] }));
3993
- });
3994
- const RadioCardRoot = RadioCard.Root;
3995
- RadioCard.Label;
3996
- RadioCard.ItemIndicator;
3997
-
3998
- const TagPicker = ({ column }) => {
3999
- const { watch, formState: { errors }, setValue, } = useFormContext();
4000
- const { schema, serverUrl } = useSchemaContext();
4001
- if (schema.properties == undefined) {
4002
- throw new Error("schema properties undefined when using DatePicker");
4003
- }
4004
- const { gridColumn, gridRow, in_table, object_id_column } = schema.properties[column];
4005
- if (in_table === undefined) {
4006
- throw new Error("in_table is undefined when using TagPicker");
4007
- }
4008
- if (object_id_column === undefined) {
4009
- throw new Error("object_id_column is undefined when using TagPicker");
4010
- }
4011
- const query = useQuery({
4012
- queryKey: [`tagpicker`, in_table],
4013
- queryFn: async () => {
4014
- return await getTableData({
4015
- serverUrl,
4016
- in_table: "tables_tags_view",
4017
- where: [
4018
- {
4019
- id: "table_name",
4020
- value: [in_table],
4021
- },
4022
- ],
4023
- limit: 100,
4024
- });
4025
- },
4026
- staleTime: 10000,
4027
- });
4028
- const object_id = watch(object_id_column);
4029
- const existingTagsQuery = useQuery({
4030
- queryKey: [`existing`, in_table, object_id_column, object_id],
4031
- queryFn: async () => {
4032
- return await getTableData({
4033
- serverUrl,
4034
- in_table: in_table,
4035
- where: [
4036
- {
4037
- id: object_id_column,
4038
- value: object_id[0],
4039
- },
4040
- ],
4041
- limit: 100,
4042
- });
4043
- },
4044
- enabled: object_id != undefined,
4045
- staleTime: 10000,
4046
- });
4047
- const { isLoading, isFetching, data, isPending, isError } = query;
4048
- const dataList = data?.data ?? [];
4049
- const existingTagList = existingTagsQuery.data?.data ?? [];
4050
- if (!!object_id === false) {
4051
- return jsx(Fragment, {});
4052
- }
4053
- return (jsxs(Flex, { flexFlow: "column", gap: 4, gridColumn,
4054
- gridRow, children: [isFetching && jsx(Fragment, { children: "isFetching" }), isLoading && jsx(Fragment, { children: "isLoading" }), isPending && jsx(Fragment, { children: "isPending" }), isError && jsx(Fragment, { children: "isError" }), dataList.map(({ parent_tag_name, all_tags, is_mutually_exclusive }) => {
4055
- return (jsxs(Flex, { flexFlow: "column", gap: 2, children: [jsx(Text, { children: parent_tag_name }), is_mutually_exclusive && (jsx(RadioCardRoot, { defaultValue: "next", variant: "surface", onValueChange: (tagIds) => {
4056
- const existedTags = Object.values(all_tags)
4057
- .filter(({ id }) => {
4058
- return existingTagList.some(({ tag_id }) => tag_id === id);
4059
- })
4060
- .map(({ id }) => {
4061
- return id;
4062
- });
4063
- setValue(`${column}.${parent_tag_name}.current`, [
4064
- tagIds.value,
4065
- ]);
4066
- setValue(`${column}.${parent_tag_name}.old`, existedTags);
4067
- }, children: jsx(Flex, { flexFlow: "wrap", gap: 2, children: Object.entries(all_tags).map(([tagName, { id }]) => {
4068
- if (existingTagList.some(({ tag_id }) => tag_id === id)) {
4069
- return (jsx(RadioCardItem, { label: tagName, value: id, flex: "0 0 0%", disabled: true }, `${tagName}-${id}`));
4070
- }
4071
- return (jsx(RadioCardItem, { label: tagName, value: id, flex: "0 0 0%", colorPalette: "blue" }, `${tagName}-${id}`));
4072
- }) }) })), !is_mutually_exclusive && (jsx(CheckboxGroup, { onValueChange: (tagIds) => {
4073
- setValue(`${column}.${parent_tag_name}.current`, tagIds);
4074
- }, children: jsx(Flex, { flexFlow: "wrap", gap: 2, children: Object.entries(all_tags).map(([tagName, { id }]) => {
4075
- if (existingTagList.some(({ tag_id }) => tag_id === id)) {
4076
- return (jsx(CheckboxCard, { label: tagName, value: id, flex: "0 0 0%", disabled: true, colorPalette: "blue" }, `${tagName}-${id}`));
4077
- }
4078
- return (jsx(CheckboxCard, { label: tagName, value: id, flex: "0 0 0%" }, `${tagName}-${id}`));
4079
- }) }) }))] }, `tag-${parent_tag_name}`));
4080
- }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: (errors[`${column}`]?.message ?? "No error message") }))] }));
3912
+ const { gridColumn, gridRow, title, renderDisplay } = schema.properties[column];
3913
+ const [searchText, setSearchText] = useState();
3914
+ const [limit, setLimit] = useState(10);
3915
+ const [openSearchResult, setOpenSearchResult] = useState();
3916
+ const ref = useRef(null);
3917
+ const watchEnum = watch(column);
3918
+ const watchEnums = (watch(column) ?? []);
3919
+ const properties = (schema.properties[column] ?? {});
3920
+ const dataList = properties.enum ?? [];
3921
+ const count = properties.enum?.length ?? 0;
3922
+ const isDirty = (searchText?.length ?? 0) > 0;
3923
+ const onSearchChange = async (event) => {
3924
+ setSearchText(event.target.value);
3925
+ setLimit(10);
3926
+ };
3927
+ return (jsxs(Field, { label: `${title ?? snakeToLabel(column)}`, required: isRequired, alignItems: "stretch", gridColumn,
3928
+ gridRow, children: [isMultiple && (jsxs(Flex, { flexFlow: "wrap", gap: 1, children: [watchEnums.map((enumValue) => {
3929
+ const item = enumValue;
3930
+ if (item === undefined) {
3931
+ return jsx(Fragment, { children: "undefined" });
3932
+ }
3933
+ return (jsx(Tag, { closable: true, onClick: () => {
3934
+ setSelectedEnums((state) => state.filter((id) => id != item));
3935
+ setValue(column, watchEnums.filter((id) => id != item));
3936
+ }, children: !!renderDisplay === true ? renderDisplay(item) : item }));
3937
+ }), jsx(Tag, { cursor: "pointer", onClick: () => {
3938
+ setOpenSearchResult(true);
3939
+ }, children: "Add" })] })), !isMultiple && (jsx(Button, { variant: "outline", onClick: () => {
3940
+ setOpenSearchResult(true);
3941
+ }, children: 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: typeToSearch ?? "Type to search", onChange: (event) => {
3942
+ onSearchChange(event);
3943
+ setOpenSearchResult(true);
3944
+ }, autoComplete: "off", ref: ref }), jsx(PopoverTitle, {}), jsx(Text, { children: `${total ?? "Total"}: ${count}, ${showing ?? "Showing"} ${limit}` }), jsxs(Grid, { gridTemplateColumns: "repeat(auto-fit, minmax(15rem, 1fr))", overflow: "auto", maxHeight: "50vh", children: [jsx(Flex, { flexFlow: "column wrap", children: filterArray(dataList, searchText ?? "").map((item) => {
3945
+ const selected = isMultiple
3946
+ ? watchEnums.some((enumValue) => item === enumValue)
3947
+ : watchEnum == item;
3948
+ return (jsx(Box, { cursor: "pointer", onClick: () => {
3949
+ if (!isMultiple) {
3950
+ setOpenSearchResult(false);
3951
+ setValue(column, item);
3952
+ return;
3953
+ }
3954
+ const newSet = new Set([...(watchEnums ?? []), item]);
3955
+ setValue(column, [...newSet]);
3956
+ }, ...(selected ? { color: "gray.400/50" } : {}), children: !!renderDisplay === true ? renderDisplay(item) : item }, `${column}-${item}`));
3957
+ }) }), isDirty && (jsxs(Fragment, { children: [dataList.length <= 0 && jsx(Fragment, { children: "Empty Search Result" }), " "] }))] })] }) })] }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: fieldRequired ?? "The field is requried" }))] }));
4081
3958
  };
4082
3959
 
4083
3960
  function isEnteringWindow(_ref) {
@@ -4455,71 +4332,156 @@ const FilePicker = ({ column }) => {
4455
4332
  }) }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: fieldRequired ?? "The field is requried" }))] }));
4456
4333
  };
4457
4334
 
4458
- function filterArray(array, searchTerm) {
4459
- // Convert the search term to lower case for case-insensitive comparison
4460
- const lowerCaseSearchTerm = searchTerm.toLowerCase();
4461
- // Use the filter method to return an array of matching items
4462
- return array.filter((item) => {
4463
- // Convert each item to a string and check if it includes the search term
4464
- return item.toString().toLowerCase().includes(lowerCaseSearchTerm);
4465
- });
4466
- }
4467
-
4468
- const EnumPicker = ({ column, isMultiple = false }) => {
4469
- const { watch, formState: { errors }, setValue, } = useFormContext();
4335
+ const ObjectInput = ({ column }) => {
4336
+ const { formState: { errors }, setValue, getValues, } = useFormContext();
4470
4337
  const { schema, displayText } = useSchemaContext();
4471
- const { fieldRequired, total, showing, typeToSearch } = displayText;
4338
+ const { addNew, fieldRequired, save } = displayText;
4472
4339
  const { required } = schema;
4473
4340
  const isRequired = required?.some((columnId) => columnId === column);
4341
+ const entries = Object.entries(getValues(column) ?? {});
4342
+ const [showNewEntries, setShowNewEntries] = useState(false);
4343
+ const [newKey, setNewKey] = useState();
4344
+ const [newValue, setNewValue] = useState();
4474
4345
  if (schema.properties == undefined) {
4475
4346
  throw new Error("schema properties when using DatePicker");
4476
4347
  }
4477
- const { gridColumn, gridRow, title, renderDisplay } = schema.properties[column];
4478
- const [searchText, setSearchText] = useState();
4479
- const [limit, setLimit] = useState(10);
4480
- const [openSearchResult, setOpenSearchResult] = useState();
4481
- const ref = useRef(null);
4482
- const watchEnum = watch(column);
4483
- const watchEnums = (watch(column) ?? []);
4484
- const properties = (schema.properties[column] ?? {});
4485
- const dataList = properties.enum ?? [];
4486
- const count = properties.enum?.length ?? 0;
4487
- const isDirty = (searchText?.length ?? 0) > 0;
4488
- const onSearchChange = async (event) => {
4489
- setSearchText(event.target.value);
4490
- setLimit(10);
4491
- };
4492
- return (jsxs(Field, { label: `${title ?? snakeToLabel(column)}`, required: isRequired, alignItems: "stretch", gridColumn,
4493
- gridRow, children: [isMultiple && (jsxs(Flex, { flexFlow: "wrap", gap: 1, children: [watchEnums.map((enumValue) => {
4494
- const item = enumValue;
4495
- if (item === undefined) {
4496
- return jsx(Fragment, { children: "undefined" });
4497
- }
4498
- return (jsx(Tag, { closable: true, onClick: () => {
4499
- setSelectedEnums((state) => state.filter((id) => id != item));
4500
- setValue(column, watchEnums.filter((id) => id != item));
4501
- }, children: !!renderDisplay === true ? renderDisplay(item) : item }));
4502
- }), jsx(Tag, { cursor: "pointer", onClick: () => {
4503
- setOpenSearchResult(true);
4504
- }, children: "Add" })] })), !isMultiple && (jsx(Button, { variant: "outline", onClick: () => {
4505
- setOpenSearchResult(true);
4506
- }, children: 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: typeToSearch ?? "Type to search", onChange: (event) => {
4507
- onSearchChange(event);
4508
- setOpenSearchResult(true);
4509
- }, autoComplete: "off", ref: ref }), jsx(PopoverTitle, {}), jsx(Text, { children: `${total ?? "Total"}: ${count}, ${showing ?? "Showing"} ${limit}` }), jsxs(Grid, { gridTemplateColumns: "repeat(auto-fit, minmax(15rem, 1fr))", overflow: "auto", maxHeight: "50vh", children: [jsx(Flex, { flexFlow: "column wrap", children: filterArray(dataList, searchText ?? "").map((item) => {
4510
- const selected = isMultiple
4511
- ? watchEnums.some((enumValue) => item === enumValue)
4512
- : watchEnum == item;
4513
- return (jsx(Box, { cursor: "pointer", onClick: () => {
4514
- if (!isMultiple) {
4515
- setOpenSearchResult(false);
4516
- setValue(column, item);
4517
- return;
4518
- }
4519
- const newSet = new Set([...(watchEnums ?? []), item]);
4520
- setValue(column, [...newSet]);
4521
- }, ...(selected ? { color: "gray.400/50" } : {}), children: !!renderDisplay === true ? renderDisplay(item) : item }, `${column}-${item}`));
4522
- }) }), isDirty && (jsxs(Fragment, { children: [dataList.length <= 0 && jsx(Fragment, { children: "Empty Search Result" }), " "] }))] })] }) })] }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: fieldRequired ?? "The field is requried" }))] }));
4348
+ const { gridColumn, gridRow, title } = schema.properties[column];
4349
+ return (jsxs(Field, { label: `${title ?? snakeToLabel(column)}`, required: isRequired, alignItems: "stretch", gridColumn, gridRow, children: [entries.map(([key, value]) => {
4350
+ return (jsxs(Grid, { templateColumns: "1fr 1fr auto", gap: 1, children: [jsx(Input, { value: key, onChange: (e) => {
4351
+ const filtered = entries.filter(([target]) => {
4352
+ return target !== key;
4353
+ });
4354
+ setValue(column, Object.fromEntries([...filtered, [e.target.value, value]]));
4355
+ }, autoComplete: "off" }), jsx(Input, { value: value, onChange: (e) => {
4356
+ setValue(column, {
4357
+ ...getValues(column),
4358
+ [key]: e.target.value,
4359
+ });
4360
+ }, autoComplete: "off" }), jsx(IconButton, { variant: "ghost", onClick: () => {
4361
+ const filtered = entries.filter(([target]) => {
4362
+ return target !== key;
4363
+ });
4364
+ setValue(column, Object.fromEntries([...filtered]));
4365
+ }, children: jsx(CgClose, {}) })] }));
4366
+ }), jsx(Show, { when: showNewEntries, children: jsxs(Card.Root, { children: [jsx(Card.Body, { gap: "2", children: jsxs(Grid, { templateColumns: "1fr 1fr auto", gap: 1, children: [jsx(Input, { value: newKey, onChange: (e) => {
4367
+ setNewKey(e.target.value);
4368
+ }, autoComplete: "off" }), jsx(Input, { value: newValue, onChange: (e) => {
4369
+ setNewValue(e.target.value);
4370
+ }, autoComplete: "off" })] }) }), jsxs(Card.Footer, { justifyContent: "flex-end", children: [jsx(IconButton, { variant: "subtle", onClick: () => {
4371
+ setShowNewEntries(false);
4372
+ setNewKey(undefined);
4373
+ setNewValue(undefined);
4374
+ }, children: jsx(CgClose, {}) }), jsx(Button, { onClick: () => {
4375
+ if (!!newKey === false) {
4376
+ setShowNewEntries(false);
4377
+ setNewKey(undefined);
4378
+ setNewValue(undefined);
4379
+ return;
4380
+ }
4381
+ setValue(column, Object.fromEntries([...entries, [newKey, newValue]]));
4382
+ setShowNewEntries(false);
4383
+ setNewKey(undefined);
4384
+ setNewValue(undefined);
4385
+ }, children: save ?? "Save" })] })] }) }), jsx(Button, { onClick: () => {
4386
+ setShowNewEntries(true);
4387
+ setNewKey(undefined);
4388
+ setNewValue(undefined);
4389
+ }, children: addNew ?? "Add New" }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: fieldRequired ?? "The field is requried" }))] }));
4390
+ };
4391
+
4392
+ const RadioCardItem = React.forwardRef(function RadioCardItem(props, ref) {
4393
+ const { inputProps, label, description, addon, icon, indicator = jsx(RadioCard.ItemIndicator, {}), indicatorPlacement = "end", ...rest } = props;
4394
+ const hasContent = label || description || icon;
4395
+ const ContentWrapper = indicator ? RadioCard.ItemContent : React.Fragment;
4396
+ return (jsxs(RadioCard.Item, { ...rest, children: [jsx(RadioCard.ItemHiddenInput, { ref: ref, ...inputProps }), jsxs(RadioCard.ItemControl, { children: [indicatorPlacement === "start" && indicator, hasContent && (jsxs(ContentWrapper, { children: [icon, label && jsx(RadioCard.ItemText, { children: label }), description && (jsx(RadioCard.ItemDescription, { children: description })), indicatorPlacement === "inside" && indicator] })), indicatorPlacement === "end" && indicator] }), addon && jsx(RadioCard.ItemAddon, { children: addon })] }));
4397
+ });
4398
+ const RadioCardRoot = RadioCard.Root;
4399
+ RadioCard.Label;
4400
+ RadioCard.ItemIndicator;
4401
+
4402
+ const TagPicker = ({ column }) => {
4403
+ const { watch, formState: { errors }, setValue, } = useFormContext();
4404
+ const { schema, serverUrl } = useSchemaContext();
4405
+ if (schema.properties == undefined) {
4406
+ throw new Error("schema properties undefined when using DatePicker");
4407
+ }
4408
+ const { gridColumn, gridRow, in_table, object_id_column } = schema.properties[column];
4409
+ if (in_table === undefined) {
4410
+ throw new Error("in_table is undefined when using TagPicker");
4411
+ }
4412
+ if (object_id_column === undefined) {
4413
+ throw new Error("object_id_column is undefined when using TagPicker");
4414
+ }
4415
+ const query = useQuery({
4416
+ queryKey: [`tagpicker`, in_table],
4417
+ queryFn: async () => {
4418
+ return await getTableData({
4419
+ serverUrl,
4420
+ in_table: "tables_tags_view",
4421
+ where: [
4422
+ {
4423
+ id: "table_name",
4424
+ value: [in_table],
4425
+ },
4426
+ ],
4427
+ limit: 100,
4428
+ });
4429
+ },
4430
+ staleTime: 10000,
4431
+ });
4432
+ const object_id = watch(object_id_column);
4433
+ const existingTagsQuery = useQuery({
4434
+ queryKey: [`existing`, in_table, object_id_column, object_id],
4435
+ queryFn: async () => {
4436
+ return await getTableData({
4437
+ serverUrl,
4438
+ in_table: in_table,
4439
+ where: [
4440
+ {
4441
+ id: object_id_column,
4442
+ value: object_id[0],
4443
+ },
4444
+ ],
4445
+ limit: 100,
4446
+ });
4447
+ },
4448
+ enabled: object_id != undefined,
4449
+ staleTime: 10000,
4450
+ });
4451
+ const { isLoading, isFetching, data, isPending, isError } = query;
4452
+ const dataList = data?.data ?? [];
4453
+ const existingTagList = existingTagsQuery.data?.data ?? [];
4454
+ if (!!object_id === false) {
4455
+ return jsx(Fragment, {});
4456
+ }
4457
+ return (jsxs(Flex, { flexFlow: "column", gap: 4, gridColumn,
4458
+ gridRow, children: [isFetching && jsx(Fragment, { children: "isFetching" }), isLoading && jsx(Fragment, { children: "isLoading" }), isPending && jsx(Fragment, { children: "isPending" }), isError && jsx(Fragment, { children: "isError" }), dataList.map(({ parent_tag_name, all_tags, is_mutually_exclusive }) => {
4459
+ return (jsxs(Flex, { flexFlow: "column", gap: 2, children: [jsx(Text, { children: parent_tag_name }), is_mutually_exclusive && (jsx(RadioCardRoot, { defaultValue: "next", variant: "surface", onValueChange: (tagIds) => {
4460
+ const existedTags = Object.values(all_tags)
4461
+ .filter(({ id }) => {
4462
+ return existingTagList.some(({ tag_id }) => tag_id === id);
4463
+ })
4464
+ .map(({ id }) => {
4465
+ return id;
4466
+ });
4467
+ setValue(`${column}.${parent_tag_name}.current`, [
4468
+ tagIds.value,
4469
+ ]);
4470
+ setValue(`${column}.${parent_tag_name}.old`, existedTags);
4471
+ }, children: jsx(Flex, { flexFlow: "wrap", gap: 2, children: Object.entries(all_tags).map(([tagName, { id }]) => {
4472
+ if (existingTagList.some(({ tag_id }) => tag_id === id)) {
4473
+ return (jsx(RadioCardItem, { label: tagName, value: id, flex: "0 0 0%", disabled: true }, `${tagName}-${id}`));
4474
+ }
4475
+ return (jsx(RadioCardItem, { label: tagName, value: id, flex: "0 0 0%", colorPalette: "blue" }, `${tagName}-${id}`));
4476
+ }) }) })), !is_mutually_exclusive && (jsx(CheckboxGroup, { onValueChange: (tagIds) => {
4477
+ setValue(`${column}.${parent_tag_name}.current`, tagIds);
4478
+ }, children: jsx(Flex, { flexFlow: "wrap", gap: 2, children: Object.entries(all_tags).map(([tagName, { id }]) => {
4479
+ if (existingTagList.some(({ tag_id }) => tag_id === id)) {
4480
+ return (jsx(CheckboxCard, { label: tagName, value: id, flex: "0 0 0%", disabled: true, colorPalette: "blue" }, `${tagName}-${id}`));
4481
+ }
4482
+ return (jsx(CheckboxCard, { label: tagName, value: id, flex: "0 0 0%" }, `${tagName}-${id}`));
4483
+ }) }) }))] }, `tag-${parent_tag_name}`));
4484
+ }), errors[`${column}`] && (jsx(Text, { color: "red.400", children: (errors[`${column}`]?.message ?? "No error message") }))] }));
4523
4485
  };
4524
4486
 
4525
4487
  const idPickerSanityCheck = (column, foreign_key) => {
@@ -4538,7 +4500,7 @@ const idPickerSanityCheck = (column, foreign_key) => {
4538
4500
  }
4539
4501
  };
4540
4502
  const FormInternal = () => {
4541
- const { schema, serverUrl, displayText, order, ignore, onSubmit, rowNumber } = useSchemaContext();
4503
+ const { schema, serverUrl, displayText, order, ignore, onSubmit, rowNumber, idMap, } = useSchemaContext();
4542
4504
  const { title, submit, empty, cancel, submitSuccess, submitAgain, confirm } = displayText;
4543
4505
  const methods = useFormContext();
4544
4506
  const [isSuccess, setIsSuccess] = useState(false);
@@ -4645,8 +4607,7 @@ const FormInternal = () => {
4645
4607
  if (variant === "id-picker") {
4646
4608
  idPickerSanityCheck(column, foreign_key);
4647
4609
  return (jsx(IdViewer, { value: (validatedData ?? {})[column], column,
4648
- gridColumn,
4649
- gridRow }, `form-${key}`));
4610
+ dataListItemProps: { gridColumn, gridRow } }, `form-${key}`));
4650
4611
  }
4651
4612
  if (variant === "date-picker") {
4652
4613
  const value = (validatedData ?? {})[column];
@@ -4684,6 +4645,22 @@ const FormInternal = () => {
4684
4645
  });
4685
4646
  return (jsx(DataListItem, { gridColumn: gridColumn ?? "span 4", gridRow: gridRow ?? "span 4", label: `${snakeToLabel(column)}`, ...getDataListProps(JSON.stringify(fileNames)) }, `form-${key}`));
4686
4647
  }
4648
+ if (variant === "id-picker") {
4649
+ const value = (validatedData ?? {})[column];
4650
+ if (schema.properties == undefined) {
4651
+ throw new Error("schema properties when using DatePicker");
4652
+ }
4653
+ const { foreign_key } = schema.properties[column];
4654
+ if (foreign_key === undefined) {
4655
+ throw new Error("foreign_key when variant is id-picker");
4656
+ }
4657
+ const { display_column } = foreign_key;
4658
+ const mapped = value.map((item) => {
4659
+ return idMap[item][display_column];
4660
+ });
4661
+ return (jsxs(Grid, { flexFlow: "column", gridColumn,
4662
+ gridRow, children: [jsx(Text, { children: snakeToLabel(column) }), jsx(RecordDisplay, { object: mapped })] }, `form-${key}`));
4663
+ }
4687
4664
  const objectString = JSON.stringify((validatedData ?? {})[column]);
4688
4665
  return (jsx(DataListItem, { gridColumn: gridColumn ?? "span 4", gridRow: gridRow ?? "span 4", label: `${snakeToLabel(column)}`, ...getDataListProps(objectString) }, `form-${key}`));
4689
4666
  }
@@ -4710,8 +4687,9 @@ const FormInternal = () => {
4710
4687
  return jsx(Fragment, {});
4711
4688
  }
4712
4689
  //@ts-expect-error TODO: add more fields to support form-creation
4713
- const { type, variant, in_table, column_ref, foreign_key } = values;
4690
+ const { type, variant, foreign_key } = values;
4714
4691
  if (type === "string") {
4692
+ // @ts-expect-error enum should exists
4715
4693
  if ((values.enum ?? []).length > 0) {
4716
4694
  return jsx(EnumPicker, { column: key }, `form-${key}`);
4717
4695
  }
@@ -4757,6 +4735,7 @@ const FormInternal = () => {
4757
4735
  const Form = ({ schema, serverUrl, order = [], ignore = [], onSubmit = undefined, preLoadedValues = {}, rowNumber = undefined, displayText = {}, }) => {
4758
4736
  const queryClient = new QueryClient();
4759
4737
  const methods = useForm({ values: preLoadedValues });
4738
+ const [idMap, setIdMap] = useState({});
4760
4739
  const { properties } = schema;
4761
4740
  idListSanityCheck("order", order, properties);
4762
4741
  idListSanityCheck("ignore", ignore, properties);
@@ -4770,6 +4749,8 @@ const Form = ({ schema, serverUrl, order = [], ignore = [], onSubmit = undefined
4770
4749
  // @ts-expect-error TODO: find appropriate types
4771
4750
  onSubmit,
4772
4751
  rowNumber,
4752
+ idMap,
4753
+ setIdMap,
4773
4754
  }, children: jsx(FormProvider, { ...methods, children: jsx(FormInternal, {}) }) }) }));
4774
4755
  };
4775
4756