@bsol-oss/react-datatable5 12.0.0-beta.85 → 12.0.0-beta.87
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
|
@@ -853,6 +853,7 @@ interface FormRootProps<TData extends FieldValues> {
|
|
|
853
853
|
idPickerLabels?: IdPickerLabels;
|
|
854
854
|
enumPickerLabels?: EnumPickerLabels;
|
|
855
855
|
filePickerLabels?: FilePickerLabels;
|
|
856
|
+
comboboxInDialog?: boolean;
|
|
856
857
|
}
|
|
857
858
|
interface CustomJSONSchema7Definition extends JSONSchema7 {
|
|
858
859
|
variant: string;
|
|
@@ -869,7 +870,7 @@ declare const idPickerSanityCheck: (column: string, foreign_key?: {
|
|
|
869
870
|
column?: string | undefined;
|
|
870
871
|
display_column?: string | undefined;
|
|
871
872
|
} | undefined) => void;
|
|
872
|
-
declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, serverUrl, translate, children, order, ignore, include, onSubmit, rowNumber, requestOptions, getUpdatedData, customErrorRenderer, customSuccessRenderer, displayConfig, requireConfirmation, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, }: FormRootProps<TData>) => react_jsx_runtime.JSX.Element;
|
|
873
|
+
declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, serverUrl, translate, children, order, ignore, include, onSubmit, rowNumber, requestOptions, getUpdatedData, customErrorRenderer, customSuccessRenderer, displayConfig, requireConfirmation, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, comboboxInDialog, }: FormRootProps<TData>) => react_jsx_runtime.JSX.Element;
|
|
873
874
|
|
|
874
875
|
interface DefaultFormProps<TData extends FieldValues> {
|
|
875
876
|
formConfig: Omit<FormRootProps<TData>, "children">;
|
package/dist/index.js
CHANGED
|
@@ -3627,7 +3627,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3627
3627
|
showSubmitButton: true,
|
|
3628
3628
|
showResetButton: true,
|
|
3629
3629
|
showTitle: true,
|
|
3630
|
-
}, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, }) => {
|
|
3630
|
+
}, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, comboboxInDialog = false, }) => {
|
|
3631
3631
|
const [isSuccess, setIsSuccess] = React.useState(false);
|
|
3632
3632
|
const [isError, setIsError] = React.useState(false);
|
|
3633
3633
|
const [isSubmiting, setIsSubmiting] = React.useState(false);
|
|
@@ -3718,6 +3718,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3718
3718
|
enumPickerLabels,
|
|
3719
3719
|
filePickerLabels,
|
|
3720
3720
|
ajvResolver: ajvResolver(schema),
|
|
3721
|
+
comboboxInDialog,
|
|
3721
3722
|
}, children: jsxRuntime.jsx(reactHookForm.FormProvider, { ...form, children: children }) }));
|
|
3722
3723
|
};
|
|
3723
3724
|
|
|
@@ -4178,7 +4179,7 @@ const DateRangePicker = ({ column, schema, prefix, }) => {
|
|
|
4178
4179
|
|
|
4179
4180
|
const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLimit = false, }) => {
|
|
4180
4181
|
const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
|
|
4181
|
-
const { enumPickerLabels } = useSchemaContext();
|
|
4182
|
+
const { enumPickerLabels, comboboxInDialog } = useSchemaContext();
|
|
4182
4183
|
const formI18n = useFormI18n(column, prefix);
|
|
4183
4184
|
const { required, variant } = schema;
|
|
4184
4185
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
@@ -4247,10 +4248,13 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
4247
4248
|
}, children: !!renderDisplay === true
|
|
4248
4249
|
? renderDisplay(enumValue)
|
|
4249
4250
|
: formI18n.t(enumValue) }, enumValue));
|
|
4250
|
-
}) })), jsxRuntime.jsxs(react.Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%",
|
|
4251
|
+
}) })), jsxRuntime.jsxs(react.Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%", positioning: comboboxInDialog
|
|
4252
|
+
? { strategy: 'fixed', hideWhenDetached: true }
|
|
4253
|
+
: undefined, children: [jsxRuntime.jsxs(react.Combobox.Control, { children: [jsxRuntime.jsx(react.Combobox.Input, { placeholder: enumPickerLabels?.typeToSearch ?? formI18n.t('type_to_search') }), jsxRuntime.jsxs(react.Combobox.IndicatorGroup, { children: [!isMultiple && currentValue.length > 0 && (jsxRuntime.jsx(react.Combobox.ClearTrigger, { onClick: () => {
|
|
4251
4254
|
setValue(colLabel, '');
|
|
4252
|
-
} })), jsxRuntime.jsx(react.Combobox.Trigger, {})] })] }),
|
|
4253
|
-
|
|
4255
|
+
} })), jsxRuntime.jsx(react.Combobox.Trigger, {})] })] }), comboboxInDialog ? (jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsxs(react.Combobox.Content, { children: [showTotalAndLimit && (jsxRuntime.jsx(react.Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${collection.items.length}` })), collection.items.length === 0 ? (jsxRuntime.jsx(react.Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ??
|
|
4256
|
+
formI18n.t('empty_search_result') })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: collection.items.map((item, index) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsx(react.Combobox.ItemText, { children: item.label }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) })) : (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsxs(react.Combobox.Content, { children: [showTotalAndLimit && (jsxRuntime.jsx(react.Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${collection.items.length}` })), collection.items.length === 0 ? (jsxRuntime.jsx(react.Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ??
|
|
4257
|
+
formI18n.t('empty_search_result') })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: collection.items.map((item, index) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsx(react.Combobox.ItemText, { children: item.label }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) }) }))] })] }));
|
|
4254
4258
|
};
|
|
4255
4259
|
|
|
4256
4260
|
function isEnteringWindow(_ref) {
|
|
@@ -5039,12 +5043,13 @@ const getTableData = async ({ serverUrl, in_table, searching = "", where = [], l
|
|
|
5039
5043
|
|
|
5040
5044
|
const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
5041
5045
|
const { watch, getValues, formState: { errors }, setValue, } = reactHookForm.useFormContext();
|
|
5042
|
-
const { serverUrl, idMap, setIdMap, schema: parentSchema, idPickerLabels, } = useSchemaContext();
|
|
5046
|
+
const { serverUrl, idMap, setIdMap, schema: parentSchema, idPickerLabels, comboboxInDialog, } = useSchemaContext();
|
|
5043
5047
|
const formI18n = useFormI18n(column, prefix);
|
|
5044
5048
|
const { required, gridColumn = 'span 12', gridRow = 'span 1', renderDisplay, foreign_key, } = schema;
|
|
5045
5049
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
5046
5050
|
const { table, column: column_ref, display_column, customQueryFn, } = foreign_key;
|
|
5047
5051
|
const [searchText, setSearchText] = React.useState('');
|
|
5052
|
+
const [debouncedSearchText, setDebouncedSearchText] = React.useState('');
|
|
5048
5053
|
const [limit] = React.useState(50); // Increased limit for combobox
|
|
5049
5054
|
const colLabel = formI18n.colLabel;
|
|
5050
5055
|
const watchedValue = watch(colLabel);
|
|
@@ -5070,13 +5075,20 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5070
5075
|
: currentId
|
|
5071
5076
|
? [currentId]
|
|
5072
5077
|
: [];
|
|
5078
|
+
// Debounce search text to avoid too many API calls
|
|
5079
|
+
React.useEffect(() => {
|
|
5080
|
+
const timer = setTimeout(() => {
|
|
5081
|
+
setDebouncedSearchText(searchText);
|
|
5082
|
+
}, 300);
|
|
5083
|
+
return () => clearTimeout(timer);
|
|
5084
|
+
}, [searchText]);
|
|
5073
5085
|
// Query for search results (async loading)
|
|
5074
5086
|
const query = reactQuery.useQuery({
|
|
5075
|
-
queryKey: [`idpicker`, { column, searchText, limit }],
|
|
5087
|
+
queryKey: [`idpicker`, { column, searchText: debouncedSearchText, limit }],
|
|
5076
5088
|
queryFn: async () => {
|
|
5077
5089
|
if (customQueryFn) {
|
|
5078
5090
|
const { data, idMap } = await customQueryFn({
|
|
5079
|
-
searching:
|
|
5091
|
+
searching: debouncedSearchText ?? '',
|
|
5080
5092
|
limit: limit,
|
|
5081
5093
|
offset: 0,
|
|
5082
5094
|
});
|
|
@@ -5087,7 +5099,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5087
5099
|
}
|
|
5088
5100
|
const data = await getTableData({
|
|
5089
5101
|
serverUrl,
|
|
5090
|
-
searching:
|
|
5102
|
+
searching: debouncedSearchText ?? '',
|
|
5091
5103
|
in_table: table,
|
|
5092
5104
|
limit: limit,
|
|
5093
5105
|
offset: 0,
|
|
@@ -5109,6 +5121,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5109
5121
|
staleTime: 300000,
|
|
5110
5122
|
});
|
|
5111
5123
|
// Query for currently selected items (to display them properly)
|
|
5124
|
+
// This query is needed for side effects (populating idMap) even though the result isn't directly used
|
|
5112
5125
|
reactQuery.useQuery({
|
|
5113
5126
|
queryKey: [
|
|
5114
5127
|
`idpicker-default`,
|
|
@@ -5163,6 +5176,8 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5163
5176
|
});
|
|
5164
5177
|
const { isLoading, isFetching, data, isPending, isError } = query;
|
|
5165
5178
|
const dataList = data?.data ?? [];
|
|
5179
|
+
// Check if we're currently searching (user typed but debounce hasn't fired yet)
|
|
5180
|
+
const isSearching = searchText !== debouncedSearchText;
|
|
5166
5181
|
// Transform data for combobox collection
|
|
5167
5182
|
const comboboxItems = React.useMemo(() => {
|
|
5168
5183
|
return dataList.map((item) => ({
|
|
@@ -5196,25 +5211,19 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5196
5211
|
setValue(colLabel, details.value[0] || '');
|
|
5197
5212
|
}
|
|
5198
5213
|
};
|
|
5199
|
-
// Debounce search to avoid too many API calls and update collection after data loads
|
|
5200
|
-
React.useEffect(() => {
|
|
5201
|
-
const timer = setTimeout(() => {
|
|
5202
|
-
if (searchText !== undefined) {
|
|
5203
|
-
query.refetch();
|
|
5204
|
-
}
|
|
5205
|
-
}, 300);
|
|
5206
|
-
return () => clearTimeout(timer);
|
|
5207
|
-
}, [searchText, query]);
|
|
5208
5214
|
// Update collection and filter when data changes
|
|
5209
5215
|
React.useEffect(() => {
|
|
5210
|
-
if (dataList.length > 0) {
|
|
5216
|
+
if (dataList.length > 0 && comboboxItems.length > 0) {
|
|
5211
5217
|
set(comboboxItems);
|
|
5212
|
-
// Apply filter to the collection
|
|
5218
|
+
// Apply filter to the collection using the immediate searchText for UI responsiveness
|
|
5213
5219
|
if (searchText) {
|
|
5214
5220
|
filter(searchText);
|
|
5215
5221
|
}
|
|
5216
5222
|
}
|
|
5217
|
-
|
|
5223
|
+
// Only depend on dataList and searchText, not comboboxItems (which is derived from dataList)
|
|
5224
|
+
// set and filter are stable functions from useListCollection
|
|
5225
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
5226
|
+
}, [dataList, searchText]);
|
|
5218
5227
|
return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
5219
5228
|
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && currentValue.length > 0 && (jsxRuntime.jsx(react.Flex, { flexFlow: 'wrap', gap: 1, mb: 2, children: currentValue.map((id) => {
|
|
5220
5229
|
const item = idMap[id];
|
|
@@ -5227,14 +5236,23 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5227
5236
|
}, children: !!renderDisplay === true
|
|
5228
5237
|
? renderDisplay(item)
|
|
5229
5238
|
: item[display_column] }, id));
|
|
5230
|
-
}) })), jsxRuntime.jsxs(react.Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%",
|
|
5239
|
+
}) })), jsxRuntime.jsxs(react.Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%", positioning: comboboxInDialog
|
|
5240
|
+
? { strategy: 'fixed', hideWhenDetached: true }
|
|
5241
|
+
: undefined, children: [jsxRuntime.jsxs(react.Combobox.Control, { children: [jsxRuntime.jsx(react.Combobox.Input, { placeholder: idPickerLabels?.typeToSearch ?? formI18n.t('type_to_search') }), jsxRuntime.jsxs(react.Combobox.IndicatorGroup, { children: [(isFetching || isLoading || isPending) && jsxRuntime.jsx(react.Spinner, { size: "xs" }), isError && (jsxRuntime.jsx(react.Icon, { color: "fg.error", children: jsxRuntime.jsx(bi.BiError, {}) })), !isMultiple && currentValue.length > 0 && (jsxRuntime.jsx(react.Combobox.ClearTrigger, { onClick: () => {
|
|
5231
5242
|
setValue(colLabel, '');
|
|
5232
|
-
} })), jsxRuntime.jsx(react.Combobox.Trigger, {})] })] }),
|
|
5233
|
-
|
|
5243
|
+
} })), jsxRuntime.jsx(react.Combobox.Trigger, {})] })] }), comboboxInDialog ? (jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsx(react.Combobox.Content, { children: isError ? (jsxRuntime.jsx(react.Text, { p: 2, color: "fg.error", fontSize: "sm", children: formI18n.t('loading_failed') })) : isFetching || isLoading || isPending || isSearching ? (
|
|
5244
|
+
// Show skeleton items to prevent UI shift
|
|
5245
|
+
jsxRuntime.jsx(jsxRuntime.Fragment, { children: Array.from({ length: 5 }).map((_, index) => (jsxRuntime.jsx(react.Flex, { p: 2, align: "center", gap: 2, children: jsxRuntime.jsx(react.Skeleton, { height: "20px", flex: "1" }) }, `skeleton-${index}`))) })) : collection.items.length === 0 ? (jsxRuntime.jsx(react.Combobox.Empty, { children: searchText
|
|
5246
|
+
? idPickerLabels?.emptySearchResult ??
|
|
5247
|
+
formI18n.t('empty_search_result')
|
|
5248
|
+
: idPickerLabels?.initialResults ??
|
|
5249
|
+
formI18n.t('initial_results') })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: collection.items.map((item, index) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsx(react.Combobox.ItemText, { children: item.label }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) })) : (jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(react.Combobox.Positioner, { children: jsxRuntime.jsx(react.Combobox.Content, { children: isError ? (jsxRuntime.jsx(react.Text, { p: 2, color: "fg.error", fontSize: "sm", children: formI18n.t('loading_failed') })) : isFetching || isLoading || isPending || isSearching ? (
|
|
5250
|
+
// Show skeleton items to prevent UI shift
|
|
5251
|
+
jsxRuntime.jsx(jsxRuntime.Fragment, { children: Array.from({ length: 5 }).map((_, index) => (jsxRuntime.jsx(react.Flex, { p: 2, align: "center", gap: 2, children: jsxRuntime.jsx(react.Skeleton, { height: "20px", flex: "1" }) }, `skeleton-${index}`))) })) : collection.items.length === 0 ? (jsxRuntime.jsx(react.Combobox.Empty, { children: searchText
|
|
5234
5252
|
? idPickerLabels?.emptySearchResult ??
|
|
5235
5253
|
formI18n.t('empty_search_result')
|
|
5236
5254
|
: idPickerLabels?.initialResults ??
|
|
5237
|
-
formI18n.t('initial_results') })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: collection.items.map((item) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsx(react.Combobox.ItemText, { children: item.label }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value))) })) }) }) })] })] }));
|
|
5255
|
+
formI18n.t('initial_results') })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: collection.items.map((item, index) => (jsxRuntime.jsxs(react.Combobox.Item, { item: item, children: [jsxRuntime.jsx(react.Combobox.ItemText, { children: item.label }), jsxRuntime.jsx(react.Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) }) }))] })] }));
|
|
5238
5256
|
};
|
|
5239
5257
|
|
|
5240
5258
|
const NumberInputRoot = React__namespace.forwardRef(function NumberInput(props, ref) {
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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, Tooltip as Tooltip$1, Group, InputElement, Icon, EmptyState as EmptyState$2, VStack, List, Table as Table$1, Checkbox as Checkbox$1, Card, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Image, Alert, Field as Field$1, Popover, useFilter, useListCollection, Combobox, Tabs, NumberInput, Show, RadioCard, CheckboxGroup, Center, Heading
|
|
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, Tooltip as Tooltip$1, Group, InputElement, Icon, EmptyState as EmptyState$2, VStack, List, Table as Table$1, Checkbox as Checkbox$1, Card, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Image, Alert, Field as Field$1, Popover, useFilter, useListCollection, Combobox, Tabs, Skeleton, NumberInput, Show, RadioCard, CheckboxGroup, 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, useMemo, forwardRef } from 'react';
|
|
@@ -3607,7 +3607,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3607
3607
|
showSubmitButton: true,
|
|
3608
3608
|
showResetButton: true,
|
|
3609
3609
|
showTitle: true,
|
|
3610
|
-
}, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, }) => {
|
|
3610
|
+
}, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, comboboxInDialog = false, }) => {
|
|
3611
3611
|
const [isSuccess, setIsSuccess] = useState(false);
|
|
3612
3612
|
const [isError, setIsError] = useState(false);
|
|
3613
3613
|
const [isSubmiting, setIsSubmiting] = useState(false);
|
|
@@ -3698,6 +3698,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3698
3698
|
enumPickerLabels,
|
|
3699
3699
|
filePickerLabels,
|
|
3700
3700
|
ajvResolver: ajvResolver(schema),
|
|
3701
|
+
comboboxInDialog,
|
|
3701
3702
|
}, children: jsx(FormProvider, { ...form, children: children }) }));
|
|
3702
3703
|
};
|
|
3703
3704
|
|
|
@@ -4158,7 +4159,7 @@ const DateRangePicker = ({ column, schema, prefix, }) => {
|
|
|
4158
4159
|
|
|
4159
4160
|
const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLimit = false, }) => {
|
|
4160
4161
|
const { watch, formState: { errors }, setValue, } = useFormContext();
|
|
4161
|
-
const { enumPickerLabels } = useSchemaContext();
|
|
4162
|
+
const { enumPickerLabels, comboboxInDialog } = useSchemaContext();
|
|
4162
4163
|
const formI18n = useFormI18n(column, prefix);
|
|
4163
4164
|
const { required, variant } = schema;
|
|
4164
4165
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
@@ -4227,10 +4228,13 @@ const EnumPicker = ({ column, isMultiple = false, schema, prefix, showTotalAndLi
|
|
|
4227
4228
|
}, children: !!renderDisplay === true
|
|
4228
4229
|
? renderDisplay(enumValue)
|
|
4229
4230
|
: formI18n.t(enumValue) }, enumValue));
|
|
4230
|
-
}) })), jsxs(Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%",
|
|
4231
|
+
}) })), jsxs(Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%", positioning: comboboxInDialog
|
|
4232
|
+
? { strategy: 'fixed', hideWhenDetached: true }
|
|
4233
|
+
: undefined, children: [jsxs(Combobox.Control, { children: [jsx(Combobox.Input, { placeholder: enumPickerLabels?.typeToSearch ?? formI18n.t('type_to_search') }), jsxs(Combobox.IndicatorGroup, { children: [!isMultiple && currentValue.length > 0 && (jsx(Combobox.ClearTrigger, { onClick: () => {
|
|
4231
4234
|
setValue(colLabel, '');
|
|
4232
|
-
} })), jsx(Combobox.Trigger, {})] })] }),
|
|
4233
|
-
|
|
4235
|
+
} })), jsx(Combobox.Trigger, {})] })] }), comboboxInDialog ? (jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [showTotalAndLimit && (jsx(Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${collection.items.length}` })), collection.items.length === 0 ? (jsx(Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ??
|
|
4236
|
+
formI18n.t('empty_search_result') })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) })) : (jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsxs(Combobox.Content, { children: [showTotalAndLimit && (jsx(Text, { p: 2, fontSize: "sm", color: "fg.muted", children: `${enumPickerLabels?.total ?? formI18n.t('total')}: ${collection.items.length}` })), collection.items.length === 0 ? (jsx(Combobox.Empty, { children: enumPickerLabels?.emptySearchResult ??
|
|
4237
|
+
formI18n.t('empty_search_result') })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) }))] }) }) }))] })] }));
|
|
4234
4238
|
};
|
|
4235
4239
|
|
|
4236
4240
|
function isEnteringWindow(_ref) {
|
|
@@ -5019,12 +5023,13 @@ const getTableData = async ({ serverUrl, in_table, searching = "", where = [], l
|
|
|
5019
5023
|
|
|
5020
5024
|
const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
5021
5025
|
const { watch, getValues, formState: { errors }, setValue, } = useFormContext();
|
|
5022
|
-
const { serverUrl, idMap, setIdMap, schema: parentSchema, idPickerLabels, } = useSchemaContext();
|
|
5026
|
+
const { serverUrl, idMap, setIdMap, schema: parentSchema, idPickerLabels, comboboxInDialog, } = useSchemaContext();
|
|
5023
5027
|
const formI18n = useFormI18n(column, prefix);
|
|
5024
5028
|
const { required, gridColumn = 'span 12', gridRow = 'span 1', renderDisplay, foreign_key, } = schema;
|
|
5025
5029
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
5026
5030
|
const { table, column: column_ref, display_column, customQueryFn, } = foreign_key;
|
|
5027
5031
|
const [searchText, setSearchText] = useState('');
|
|
5032
|
+
const [debouncedSearchText, setDebouncedSearchText] = useState('');
|
|
5028
5033
|
const [limit] = useState(50); // Increased limit for combobox
|
|
5029
5034
|
const colLabel = formI18n.colLabel;
|
|
5030
5035
|
const watchedValue = watch(colLabel);
|
|
@@ -5050,13 +5055,20 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5050
5055
|
: currentId
|
|
5051
5056
|
? [currentId]
|
|
5052
5057
|
: [];
|
|
5058
|
+
// Debounce search text to avoid too many API calls
|
|
5059
|
+
useEffect(() => {
|
|
5060
|
+
const timer = setTimeout(() => {
|
|
5061
|
+
setDebouncedSearchText(searchText);
|
|
5062
|
+
}, 300);
|
|
5063
|
+
return () => clearTimeout(timer);
|
|
5064
|
+
}, [searchText]);
|
|
5053
5065
|
// Query for search results (async loading)
|
|
5054
5066
|
const query = useQuery({
|
|
5055
|
-
queryKey: [`idpicker`, { column, searchText, limit }],
|
|
5067
|
+
queryKey: [`idpicker`, { column, searchText: debouncedSearchText, limit }],
|
|
5056
5068
|
queryFn: async () => {
|
|
5057
5069
|
if (customQueryFn) {
|
|
5058
5070
|
const { data, idMap } = await customQueryFn({
|
|
5059
|
-
searching:
|
|
5071
|
+
searching: debouncedSearchText ?? '',
|
|
5060
5072
|
limit: limit,
|
|
5061
5073
|
offset: 0,
|
|
5062
5074
|
});
|
|
@@ -5067,7 +5079,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5067
5079
|
}
|
|
5068
5080
|
const data = await getTableData({
|
|
5069
5081
|
serverUrl,
|
|
5070
|
-
searching:
|
|
5082
|
+
searching: debouncedSearchText ?? '',
|
|
5071
5083
|
in_table: table,
|
|
5072
5084
|
limit: limit,
|
|
5073
5085
|
offset: 0,
|
|
@@ -5089,6 +5101,7 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5089
5101
|
staleTime: 300000,
|
|
5090
5102
|
});
|
|
5091
5103
|
// Query for currently selected items (to display them properly)
|
|
5104
|
+
// This query is needed for side effects (populating idMap) even though the result isn't directly used
|
|
5092
5105
|
useQuery({
|
|
5093
5106
|
queryKey: [
|
|
5094
5107
|
`idpicker-default`,
|
|
@@ -5143,6 +5156,8 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5143
5156
|
});
|
|
5144
5157
|
const { isLoading, isFetching, data, isPending, isError } = query;
|
|
5145
5158
|
const dataList = data?.data ?? [];
|
|
5159
|
+
// Check if we're currently searching (user typed but debounce hasn't fired yet)
|
|
5160
|
+
const isSearching = searchText !== debouncedSearchText;
|
|
5146
5161
|
// Transform data for combobox collection
|
|
5147
5162
|
const comboboxItems = useMemo(() => {
|
|
5148
5163
|
return dataList.map((item) => ({
|
|
@@ -5176,25 +5191,19 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5176
5191
|
setValue(colLabel, details.value[0] || '');
|
|
5177
5192
|
}
|
|
5178
5193
|
};
|
|
5179
|
-
// Debounce search to avoid too many API calls and update collection after data loads
|
|
5180
|
-
useEffect(() => {
|
|
5181
|
-
const timer = setTimeout(() => {
|
|
5182
|
-
if (searchText !== undefined) {
|
|
5183
|
-
query.refetch();
|
|
5184
|
-
}
|
|
5185
|
-
}, 300);
|
|
5186
|
-
return () => clearTimeout(timer);
|
|
5187
|
-
}, [searchText, query]);
|
|
5188
5194
|
// Update collection and filter when data changes
|
|
5189
5195
|
useEffect(() => {
|
|
5190
|
-
if (dataList.length > 0) {
|
|
5196
|
+
if (dataList.length > 0 && comboboxItems.length > 0) {
|
|
5191
5197
|
set(comboboxItems);
|
|
5192
|
-
// Apply filter to the collection
|
|
5198
|
+
// Apply filter to the collection using the immediate searchText for UI responsiveness
|
|
5193
5199
|
if (searchText) {
|
|
5194
5200
|
filter(searchText);
|
|
5195
5201
|
}
|
|
5196
5202
|
}
|
|
5197
|
-
|
|
5203
|
+
// Only depend on dataList and searchText, not comboboxItems (which is derived from dataList)
|
|
5204
|
+
// set and filter are stable functions from useListCollection
|
|
5205
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
5206
|
+
}, [dataList, searchText]);
|
|
5198
5207
|
return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
5199
5208
|
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [isMultiple && currentValue.length > 0 && (jsx(Flex, { flexFlow: 'wrap', gap: 1, mb: 2, children: currentValue.map((id) => {
|
|
5200
5209
|
const item = idMap[id];
|
|
@@ -5207,14 +5216,23 @@ const IdPicker = ({ column, schema, prefix, isMultiple = false, }) => {
|
|
|
5207
5216
|
}, children: !!renderDisplay === true
|
|
5208
5217
|
? renderDisplay(item)
|
|
5209
5218
|
: item[display_column] }, id));
|
|
5210
|
-
}) })), jsxs(Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%",
|
|
5219
|
+
}) })), jsxs(Combobox.Root, { collection: collection, value: currentValue, onValueChange: handleValueChange, onInputValueChange: handleInputValueChange, multiple: isMultiple, closeOnSelect: !isMultiple, openOnClick: true, invalid: !!errors[colLabel], width: "100%", positioning: comboboxInDialog
|
|
5220
|
+
? { strategy: 'fixed', hideWhenDetached: true }
|
|
5221
|
+
: undefined, children: [jsxs(Combobox.Control, { children: [jsx(Combobox.Input, { placeholder: idPickerLabels?.typeToSearch ?? formI18n.t('type_to_search') }), jsxs(Combobox.IndicatorGroup, { children: [(isFetching || isLoading || isPending) && jsx(Spinner, { size: "xs" }), isError && (jsx(Icon, { color: "fg.error", children: jsx(BiError, {}) })), !isMultiple && currentValue.length > 0 && (jsx(Combobox.ClearTrigger, { onClick: () => {
|
|
5211
5222
|
setValue(colLabel, '');
|
|
5212
|
-
} })), jsx(Combobox.Trigger, {})] })] }),
|
|
5213
|
-
|
|
5223
|
+
} })), jsx(Combobox.Trigger, {})] })] }), comboboxInDialog ? (jsx(Combobox.Positioner, { children: jsx(Combobox.Content, { children: isError ? (jsx(Text, { p: 2, color: "fg.error", fontSize: "sm", children: formI18n.t('loading_failed') })) : isFetching || isLoading || isPending || isSearching ? (
|
|
5224
|
+
// Show skeleton items to prevent UI shift
|
|
5225
|
+
jsx(Fragment, { children: Array.from({ length: 5 }).map((_, index) => (jsx(Flex, { p: 2, align: "center", gap: 2, children: jsx(Skeleton, { height: "20px", flex: "1" }) }, `skeleton-${index}`))) })) : collection.items.length === 0 ? (jsx(Combobox.Empty, { children: searchText
|
|
5226
|
+
? idPickerLabels?.emptySearchResult ??
|
|
5227
|
+
formI18n.t('empty_search_result')
|
|
5228
|
+
: idPickerLabels?.initialResults ??
|
|
5229
|
+
formI18n.t('initial_results') })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) })) : (jsx(Portal, { children: jsx(Combobox.Positioner, { children: jsx(Combobox.Content, { children: isError ? (jsx(Text, { p: 2, color: "fg.error", fontSize: "sm", children: formI18n.t('loading_failed') })) : isFetching || isLoading || isPending || isSearching ? (
|
|
5230
|
+
// Show skeleton items to prevent UI shift
|
|
5231
|
+
jsx(Fragment, { children: Array.from({ length: 5 }).map((_, index) => (jsx(Flex, { p: 2, align: "center", gap: 2, children: jsx(Skeleton, { height: "20px", flex: "1" }) }, `skeleton-${index}`))) })) : collection.items.length === 0 ? (jsx(Combobox.Empty, { children: searchText
|
|
5214
5232
|
? idPickerLabels?.emptySearchResult ??
|
|
5215
5233
|
formI18n.t('empty_search_result')
|
|
5216
5234
|
: idPickerLabels?.initialResults ??
|
|
5217
|
-
formI18n.t('initial_results') })) : (jsx(Fragment, { children: collection.items.map((item) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value))) })) }) }) })] })] }));
|
|
5235
|
+
formI18n.t('initial_results') })) : (jsx(Fragment, { children: collection.items.map((item, index) => (jsxs(Combobox.Item, { item: item, children: [jsx(Combobox.ItemText, { children: item.label }), jsx(Combobox.ItemIndicator, {})] }, item.value ?? `item-${index}`))) })) }) }) }))] })] }));
|
|
5218
5236
|
};
|
|
5219
5237
|
|
|
5220
5238
|
const NumberInputRoot = React.forwardRef(function NumberInput$1(props, ref) {
|
|
@@ -45,5 +45,6 @@ export interface SchemaFormContext<TData extends FieldValues> {
|
|
|
45
45
|
enumPickerLabels?: EnumPickerLabels;
|
|
46
46
|
filePickerLabels?: FilePickerLabels;
|
|
47
47
|
ajvResolver: Resolver<FieldValues>;
|
|
48
|
+
comboboxInDialog?: boolean;
|
|
48
49
|
}
|
|
49
50
|
export declare const SchemaFormContext: import("react").Context<SchemaFormContext<unknown>>;
|
|
@@ -33,6 +33,7 @@ export interface FormRootProps<TData extends FieldValues> {
|
|
|
33
33
|
idPickerLabels?: IdPickerLabels;
|
|
34
34
|
enumPickerLabels?: EnumPickerLabels;
|
|
35
35
|
filePickerLabels?: FilePickerLabels;
|
|
36
|
+
comboboxInDialog?: boolean;
|
|
36
37
|
}
|
|
37
38
|
export interface CustomJSONSchema7Definition extends JSONSchema7 {
|
|
38
39
|
variant: string;
|
|
@@ -49,4 +50,4 @@ export declare const idPickerSanityCheck: (column: string, foreign_key?: {
|
|
|
49
50
|
column?: string | undefined;
|
|
50
51
|
display_column?: string | undefined;
|
|
51
52
|
} | undefined) => void;
|
|
52
|
-
export declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, serverUrl, translate, children, order, ignore, include, onSubmit, rowNumber, requestOptions, getUpdatedData, customErrorRenderer, customSuccessRenderer, displayConfig, requireConfirmation, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, }: FormRootProps<TData>) => import("react/jsx-runtime").JSX.Element;
|
|
53
|
+
export declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, serverUrl, translate, children, order, ignore, include, onSubmit, rowNumber, requestOptions, getUpdatedData, customErrorRenderer, customSuccessRenderer, displayConfig, requireConfirmation, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, comboboxInDialog, }: FormRootProps<TData>) => import("react/jsx-runtime").JSX.Element;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bsol-oss/react-datatable5",
|
|
3
|
-
"version": "12.0.0-beta.
|
|
3
|
+
"version": "12.0.0-beta.87",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -62,10 +62,10 @@
|
|
|
62
62
|
"@rollup/plugin-typescript": "^11.1.6",
|
|
63
63
|
"@storybook/addon-essentials": "^8.4.7",
|
|
64
64
|
"@storybook/addon-interactions": "^8.4.7",
|
|
65
|
-
"@storybook/addon-onboarding": "^
|
|
65
|
+
"@storybook/addon-onboarding": "^10.0.7",
|
|
66
66
|
"@storybook/blocks": "^8.4.7",
|
|
67
|
-
"@storybook/react": "^
|
|
68
|
-
"@storybook/react-vite": "^
|
|
67
|
+
"@storybook/react": "^10.0.7",
|
|
68
|
+
"@storybook/react-vite": "^10.0.7",
|
|
69
69
|
"@storybook/test": "^8.4.7",
|
|
70
70
|
"@types/ajv-errors": "^2.0.0",
|
|
71
71
|
"@types/json-schema": "^7.0.15",
|
|
@@ -81,12 +81,12 @@
|
|
|
81
81
|
"eslint": "^8.57.0",
|
|
82
82
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
83
83
|
"eslint-plugin-react-refresh": "^0.4.6",
|
|
84
|
-
"eslint-plugin-storybook": "^0.
|
|
84
|
+
"eslint-plugin-storybook": "^10.0.7",
|
|
85
85
|
"husky": "^9.1.7",
|
|
86
86
|
"lint-staged": "^16.2.5",
|
|
87
87
|
"prettier": "3.2.5",
|
|
88
88
|
"rollup-plugin-dts": "^6.1.0",
|
|
89
|
-
"storybook": "^
|
|
89
|
+
"storybook": "^10.0.7",
|
|
90
90
|
"typescript": "^5.2.2",
|
|
91
91
|
"vite": "^5.2.0"
|
|
92
92
|
},
|