@bsol-oss/react-datatable5 12.0.0-beta.75 → 12.0.0-beta.76
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 +28 -2
- package/dist/index.js +260 -33
- package/dist/index.mjs +261 -34
- package/dist/types/components/Form/SchemaFormContext.d.ts +2 -1
- package/dist/types/components/Form/components/core/FormRoot.d.ts +3 -2
- package/dist/types/components/Form/components/fields/DateRangePicker.d.ts +2 -0
- package/dist/types/components/Form/components/fields/SchemaRenderer.d.ts +1 -1
- package/dist/types/components/Form/components/types/CustomJSONSchema7.d.ts +25 -0
- package/dist/types/components/Form/utils/formatBytes.d.ts +6 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -823,6 +823,17 @@ interface EnumPickerLabels {
|
|
|
823
823
|
emptySearchResult?: string;
|
|
824
824
|
initialResults?: string;
|
|
825
825
|
}
|
|
826
|
+
interface FilePickerLabels {
|
|
827
|
+
fileDropzone?: string;
|
|
828
|
+
browseLibrary?: string;
|
|
829
|
+
dialogTitle?: string;
|
|
830
|
+
searchPlaceholder?: string;
|
|
831
|
+
loading?: string;
|
|
832
|
+
loadingFailed?: string;
|
|
833
|
+
noFilesFound?: string;
|
|
834
|
+
cancel?: string;
|
|
835
|
+
select?: string;
|
|
836
|
+
}
|
|
826
837
|
interface CustomJSONSchema7 extends JSONSchema7 {
|
|
827
838
|
gridColumn?: string;
|
|
828
839
|
gridRow?: string;
|
|
@@ -849,12 +860,26 @@ interface CustomJSONSchema7 extends JSONSchema7 {
|
|
|
849
860
|
formatOptions?: Intl.NumberFormatOptions;
|
|
850
861
|
numberStorageType?: 'string' | 'number';
|
|
851
862
|
errorMessages?: Partial<Record<ValidationErrorType | string, string>>;
|
|
863
|
+
filePicker?: FilePickerProps;
|
|
852
864
|
}
|
|
853
865
|
interface TagPickerProps {
|
|
854
866
|
column: string;
|
|
855
867
|
schema: CustomJSONSchema7;
|
|
856
868
|
prefix: string;
|
|
857
869
|
}
|
|
870
|
+
interface FilePickerMediaFile {
|
|
871
|
+
id: string;
|
|
872
|
+
name: string;
|
|
873
|
+
url?: string;
|
|
874
|
+
size?: string | number;
|
|
875
|
+
comment?: string;
|
|
876
|
+
type?: string;
|
|
877
|
+
}
|
|
878
|
+
interface FilePickerProps {
|
|
879
|
+
onFetchFiles?: (search: string) => Promise<FilePickerMediaFile[]>;
|
|
880
|
+
enableMediaLibrary?: boolean;
|
|
881
|
+
filterImageOnly?: boolean;
|
|
882
|
+
}
|
|
858
883
|
|
|
859
884
|
interface FormRootProps<TData extends FieldValues> {
|
|
860
885
|
schema: CustomJSONSchema7;
|
|
@@ -883,6 +908,7 @@ interface FormRootProps<TData extends FieldValues> {
|
|
|
883
908
|
dateTimePickerLabels?: DateTimePickerLabels;
|
|
884
909
|
idPickerLabels?: IdPickerLabels;
|
|
885
910
|
enumPickerLabels?: EnumPickerLabels;
|
|
911
|
+
filePickerLabels?: FilePickerLabels;
|
|
886
912
|
}
|
|
887
913
|
interface CustomJSONSchema7Definition extends JSONSchema7 {
|
|
888
914
|
variant: string;
|
|
@@ -899,7 +925,7 @@ declare const idPickerSanityCheck: (column: string, foreign_key?: {
|
|
|
899
925
|
column?: string | undefined;
|
|
900
926
|
display_column?: string | undefined;
|
|
901
927
|
} | undefined) => void;
|
|
902
|
-
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, }: FormRootProps<TData>) => react_jsx_runtime.JSX.Element;
|
|
928
|
+
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;
|
|
903
929
|
|
|
904
930
|
interface DefaultFormProps<TData extends FieldValues> {
|
|
905
931
|
formConfig: Omit<FormRootProps<TData>, "children">;
|
|
@@ -1071,4 +1097,4 @@ declare module "@tanstack/react-table" {
|
|
|
1071
1097
|
}
|
|
1072
1098
|
}
|
|
1073
1099
|
|
|
1074
|
-
export { type CalendarProps, CardHeader, type CardHeaderProps, type CustomJSONSchema7, type CustomJSONSchema7Definition, DataDisplay, type DataDisplayProps, type DataResponse, DataTable, type DataTableDefaultState, type DataTableProps, DataTableServer, type DataTableServerProps, type DatePickerLabels, type DatePickerProps, type DateTimePickerLabels, DefaultCardTitle, DefaultForm, type DefaultFormProps, DefaultTable, type DefaultTableProps, DensityToggleButton, type DensityToggleButtonProps, type EditFilterButtonProps, EditSortingButton, type EditSortingButtonProps, type EditViewButtonProps, EmptyState, type EmptyStateProps, type EnumPickerLabels, ErrorAlert, type ErrorAlertProps, type ErrorMessageConfig, type ErrorMessageResult, type FieldErrorConfig, FilterDialog, FormBody, FormRoot, type FormRootProps, FormTitle, type GetColumnsConfigs, type GetDateColorProps, type GetMultiDatesProps, type GetRangeDatesProps, type GetStyleProps, type GetVariantProps, GlobalFilter, type IdPickerLabels, PageSizeControl, type PageSizeControlProps, Pagination, type QueryParams, type RangeCalendarProps, type RangeDatePickerProps, RecordDisplay, type RecordDisplayProps, ReloadButton, type ReloadButtonProps, ResetFilteringButton, ResetSelectionButton, ResetSortingButton, type Result, RowCountText, Table, TableBody, type TableBodyProps, TableCardContainer, type TableCardContainerProps, TableCards, type TableCardsProps, TableComponent, TableControls, type TableControlsProps, TableDataDisplay, type TableDataDisplayProps, TableFilter, TableFilterTags, TableFooter, type TableFooterProps, TableHeader, type TableHeaderProps, type TableHeaderTexts, TableLoadingComponent, type TableLoadingComponentProps, type TableProps, type TableRendererProps, type TableRowSelectorProps, TableSelector, TableSorter, TableViewer, type TagPickerProps, TextCell, type TextCellProps, type UseDataTableProps, type UseDataTableReturn, type UseDataTableServerProps, type UseDataTableServerReturn, type UseFormProps, type ValidationErrorType, ViewDialog, buildErrorMessages, buildFieldErrors, buildRequiredErrors, convertToAjvErrorsFormat, createErrorMessage, getColumns, getMultiDates, getRangeDates, idPickerSanityCheck, useDataTable, useDataTableContext, useDataTableServer, useForm, widthSanityCheck };
|
|
1100
|
+
export { type CalendarProps, CardHeader, type CardHeaderProps, type CustomJSONSchema7, type CustomJSONSchema7Definition, DataDisplay, type DataDisplayProps, type DataResponse, DataTable, type DataTableDefaultState, type DataTableProps, DataTableServer, type DataTableServerProps, type DatePickerLabels, type DatePickerProps, type DateTimePickerLabels, DefaultCardTitle, DefaultForm, type DefaultFormProps, DefaultTable, type DefaultTableProps, DensityToggleButton, type DensityToggleButtonProps, type EditFilterButtonProps, EditSortingButton, type EditSortingButtonProps, type EditViewButtonProps, EmptyState, type EmptyStateProps, type EnumPickerLabels, ErrorAlert, type ErrorAlertProps, type ErrorMessageConfig, type ErrorMessageResult, type FieldErrorConfig, type FilePickerLabels, type FilePickerMediaFile, type FilePickerProps, FilterDialog, FormBody, FormRoot, type FormRootProps, FormTitle, type GetColumnsConfigs, type GetDateColorProps, type GetMultiDatesProps, type GetRangeDatesProps, type GetStyleProps, type GetVariantProps, GlobalFilter, type IdPickerLabels, PageSizeControl, type PageSizeControlProps, Pagination, type QueryParams, type RangeCalendarProps, type RangeDatePickerProps, RecordDisplay, type RecordDisplayProps, ReloadButton, type ReloadButtonProps, ResetFilteringButton, ResetSelectionButton, ResetSortingButton, type Result, RowCountText, Table, TableBody, type TableBodyProps, TableCardContainer, type TableCardContainerProps, TableCards, type TableCardsProps, TableComponent, TableControls, type TableControlsProps, TableDataDisplay, type TableDataDisplayProps, TableFilter, TableFilterTags, TableFooter, type TableFooterProps, TableHeader, type TableHeaderProps, type TableHeaderTexts, TableLoadingComponent, type TableLoadingComponentProps, type TableProps, type TableRendererProps, type TableRowSelectorProps, TableSelector, TableSorter, TableViewer, type TagPickerProps, TextCell, type TextCellProps, type UseDataTableProps, type UseDataTableReturn, type UseDataTableServerProps, type UseDataTableServerReturn, type UseFormProps, type ValidationErrorType, ViewDialog, buildErrorMessages, buildFieldErrors, buildRequiredErrors, convertToAjvErrorsFormat, createErrorMessage, getColumns, getMultiDates, getRangeDates, idPickerSanityCheck, useDataTable, useDataTableContext, useDataTableServer, useForm, widthSanityCheck };
|
package/dist/index.js
CHANGED
|
@@ -3908,7 +3908,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3908
3908
|
showSubmitButton: true,
|
|
3909
3909
|
showResetButton: true,
|
|
3910
3910
|
showTitle: true,
|
|
3911
|
-
}, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, }) => {
|
|
3911
|
+
}, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, }) => {
|
|
3912
3912
|
const [isSuccess, setIsSuccess] = React.useState(false);
|
|
3913
3913
|
const [isError, setIsError] = React.useState(false);
|
|
3914
3914
|
const [isSubmiting, setIsSubmiting] = React.useState(false);
|
|
@@ -3997,6 +3997,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3997
3997
|
dateTimePickerLabels,
|
|
3998
3998
|
idPickerLabels,
|
|
3999
3999
|
enumPickerLabels,
|
|
4000
|
+
filePickerLabels,
|
|
4000
4001
|
ajvResolver: ajvResolver(schema),
|
|
4001
4002
|
}, children: jsxRuntime.jsx(reactHookForm.FormProvider, { ...form, children: children }) }));
|
|
4002
4003
|
};
|
|
@@ -4370,6 +4371,92 @@ const DatePicker = ({ column, schema, prefix }) => {
|
|
|
4370
4371
|
} })] }) })] }) }));
|
|
4371
4372
|
};
|
|
4372
4373
|
|
|
4374
|
+
dayjs.extend(utc);
|
|
4375
|
+
dayjs.extend(timezone);
|
|
4376
|
+
const DateRangePicker = ({ column, schema, prefix, }) => {
|
|
4377
|
+
const { watch, formState: { errors }, setValue, } = reactHookForm.useFormContext();
|
|
4378
|
+
const { timezone, dateTimePickerLabels } = useSchemaContext();
|
|
4379
|
+
const formI18n = useFormI18n(column, prefix);
|
|
4380
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD', dateFormat = 'YYYY-MM-DD', } = schema;
|
|
4381
|
+
const isRequired = required?.some((columnId) => columnId === column);
|
|
4382
|
+
const colLabel = formI18n.colLabel;
|
|
4383
|
+
const [open, setOpen] = React.useState(false);
|
|
4384
|
+
const selectedDateRange = watch(colLabel);
|
|
4385
|
+
// Convert string[] to Date[] for the picker
|
|
4386
|
+
const selectedDates = (selectedDateRange ?? [])
|
|
4387
|
+
.map((dateStr) => {
|
|
4388
|
+
if (!dateStr)
|
|
4389
|
+
return null;
|
|
4390
|
+
const parsed = dayjs(dateStr).tz(timezone);
|
|
4391
|
+
return parsed.isValid() ? parsed.toDate() : null;
|
|
4392
|
+
})
|
|
4393
|
+
.filter((date) => date !== null);
|
|
4394
|
+
// Format display string
|
|
4395
|
+
const getDisplayText = () => {
|
|
4396
|
+
if (!selectedDateRange || selectedDateRange.length === 0) {
|
|
4397
|
+
return '';
|
|
4398
|
+
}
|
|
4399
|
+
if (selectedDateRange.length === 1) {
|
|
4400
|
+
const date = dayjs(selectedDateRange[0]).tz(timezone);
|
|
4401
|
+
return date.isValid() ? date.format(displayDateFormat) : '';
|
|
4402
|
+
}
|
|
4403
|
+
if (selectedDateRange.length === 2) {
|
|
4404
|
+
const startDate = dayjs(selectedDateRange[0]).tz(timezone);
|
|
4405
|
+
const endDate = dayjs(selectedDateRange[1]).tz(timezone);
|
|
4406
|
+
if (startDate.isValid() && endDate.isValid()) {
|
|
4407
|
+
return `${startDate.format(displayDateFormat)} - ${endDate.format(displayDateFormat)}`;
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4410
|
+
return '';
|
|
4411
|
+
};
|
|
4412
|
+
React.useEffect(() => {
|
|
4413
|
+
try {
|
|
4414
|
+
if (selectedDateRange && selectedDateRange.length > 0) {
|
|
4415
|
+
// Format dates according to dateFormat from schema
|
|
4416
|
+
const formatted = selectedDateRange
|
|
4417
|
+
.map((dateStr) => {
|
|
4418
|
+
if (!dateStr)
|
|
4419
|
+
return null;
|
|
4420
|
+
const parsed = dayjs(dateStr).tz(timezone);
|
|
4421
|
+
return parsed.isValid() ? parsed.format(dateFormat) : null;
|
|
4422
|
+
})
|
|
4423
|
+
.filter((date) => date !== null);
|
|
4424
|
+
// Update the form value only if different to avoid loops
|
|
4425
|
+
// Compare arrays element by element
|
|
4426
|
+
const needsUpdate = formatted.length !== selectedDateRange.length ||
|
|
4427
|
+
formatted.some((val, idx) => val !== selectedDateRange[idx]);
|
|
4428
|
+
if (needsUpdate && formatted.length > 0) {
|
|
4429
|
+
setValue(colLabel, formatted, {
|
|
4430
|
+
shouldValidate: true,
|
|
4431
|
+
shouldDirty: true,
|
|
4432
|
+
});
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
4435
|
+
}
|
|
4436
|
+
catch (e) {
|
|
4437
|
+
console.error(e);
|
|
4438
|
+
}
|
|
4439
|
+
}, [selectedDateRange, dateFormat, colLabel, setValue, timezone]);
|
|
4440
|
+
return (jsxRuntime.jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4441
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], 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: () => {
|
|
4442
|
+
setOpen(true);
|
|
4443
|
+
}, justifyContent: 'start', children: [jsxRuntime.jsx(md.MdDateRange, {}), getDisplayText()] }) }), jsxRuntime.jsx(PopoverContent, { minW: '600px', children: jsxRuntime.jsxs(PopoverBody, { children: [jsxRuntime.jsx(PopoverTitle, {}), jsxRuntime.jsx(RangeDatePicker, { selected: selectedDates, onDateSelected: ({ selected, selectable, date }) => {
|
|
4444
|
+
const newDates = getRangeDates({
|
|
4445
|
+
selectable,
|
|
4446
|
+
date,
|
|
4447
|
+
selectedDates,
|
|
4448
|
+
}) ?? [];
|
|
4449
|
+
// Convert Date[] to string[]
|
|
4450
|
+
const formattedDates = newDates
|
|
4451
|
+
.map((dateObj) => dayjs(dateObj).tz(timezone).format(dateFormat))
|
|
4452
|
+
.filter((dateStr) => dateStr);
|
|
4453
|
+
setValue(colLabel, formattedDates, {
|
|
4454
|
+
shouldValidate: true,
|
|
4455
|
+
shouldDirty: true,
|
|
4456
|
+
});
|
|
4457
|
+
}, monthsToDisplay: 2 })] }) })] }) }));
|
|
4458
|
+
};
|
|
4459
|
+
|
|
4373
4460
|
function filterArray(array, searchTerm) {
|
|
4374
4461
|
// Convert the search term to lower case for case-insensitive comparison
|
|
4375
4462
|
const lowerCaseSearchTerm = searchTerm.toLowerCase();
|
|
@@ -4831,24 +4918,161 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
|
|
|
4831
4918
|
return (jsxRuntime.jsxs(react.Grid, { ...getColor(isDraggedOver), ref: ref, cursor: "pointer", onClick: handleClick, borderStyle: "dashed", borderColor: "colorPalette.400", alignContent: "center", justifyContent: "center", borderWidth: 1, borderRadius: 4, ...gridProps, children: [children, !!children === false && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(react.Flex, { children: placeholder }), jsxRuntime.jsx(react.Input, { type: "file", multiple: true, style: { display: "none" }, ref: fileInput, onChange: handleChange })] }))] }));
|
|
4832
4919
|
};
|
|
4833
4920
|
|
|
4921
|
+
/**
|
|
4922
|
+
* Format bytes to human-readable string
|
|
4923
|
+
* @param bytes - The number of bytes to format
|
|
4924
|
+
* @returns Formatted string (e.g., "1.5 KB", "2.3 MB")
|
|
4925
|
+
*/
|
|
4926
|
+
function formatBytes(bytes) {
|
|
4927
|
+
if (bytes === 0)
|
|
4928
|
+
return '0 Bytes';
|
|
4929
|
+
const k = 1024;
|
|
4930
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
4931
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
4932
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
4933
|
+
}
|
|
4934
|
+
|
|
4935
|
+
function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, labels, translate, colLabel, }) {
|
|
4936
|
+
const [searchTerm, setSearchTerm] = React.useState('');
|
|
4937
|
+
const [selectedFileId, setSelectedFileId] = React.useState('');
|
|
4938
|
+
const { data: filesData, isLoading, isError, } = reactQuery.useQuery({
|
|
4939
|
+
queryKey: ['file-picker-library', searchTerm],
|
|
4940
|
+
queryFn: async () => {
|
|
4941
|
+
if (!onFetchFiles)
|
|
4942
|
+
return { data: [] };
|
|
4943
|
+
const files = await onFetchFiles(searchTerm.trim() || '');
|
|
4944
|
+
return { data: files };
|
|
4945
|
+
},
|
|
4946
|
+
enabled: open && !!onFetchFiles,
|
|
4947
|
+
});
|
|
4948
|
+
const files = (filesData?.data || []);
|
|
4949
|
+
const filteredFiles = filterImageOnly
|
|
4950
|
+
? files.filter((file) => /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name))
|
|
4951
|
+
: files;
|
|
4952
|
+
const handleSelect = () => {
|
|
4953
|
+
if (selectedFileId) {
|
|
4954
|
+
onSelect(selectedFileId);
|
|
4955
|
+
onClose();
|
|
4956
|
+
setSelectedFileId('');
|
|
4957
|
+
setSearchTerm('');
|
|
4958
|
+
}
|
|
4959
|
+
};
|
|
4960
|
+
const handleClose = () => {
|
|
4961
|
+
onClose();
|
|
4962
|
+
setSelectedFileId('');
|
|
4963
|
+
setSearchTerm('');
|
|
4964
|
+
};
|
|
4965
|
+
if (!onFetchFiles)
|
|
4966
|
+
return null;
|
|
4967
|
+
return (jsxRuntime.jsx(DialogRoot, { open: open, onOpenChange: (e) => !e.open && handleClose(), children: jsxRuntime.jsxs(DialogContent, { maxWidth: "800px", maxHeight: "90vh", children: [jsxRuntime.jsxs(DialogHeader, { children: [jsxRuntime.jsx(DialogTitle, { fontSize: "lg", fontWeight: "bold", children: title }), jsxRuntime.jsx(DialogCloseTrigger, {})] }), jsxRuntime.jsx(DialogBody, { children: jsxRuntime.jsxs(react.VStack, { align: "stretch", gap: 4, children: [jsxRuntime.jsxs(react.Box, { position: "relative", children: [jsxRuntime.jsx(react.Input, { placeholder: labels?.searchPlaceholder ??
|
|
4968
|
+
translate(removeIndex(`${colLabel}.search_placeholder`)) ??
|
|
4969
|
+
'Search files...', value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), bg: "bg.panel", border: "1px solid", borderColor: "border.default", _focus: {
|
|
4970
|
+
borderColor: 'blue.500',
|
|
4971
|
+
boxShadow: '0 0 0 1px var(--chakra-colors-blue-500)',
|
|
4972
|
+
}, pl: 10 }), jsxRuntime.jsx(react.Icon, { as: lu.LuSearch, position: "absolute", left: 3, top: "50%", transform: "translateY(-50%)", color: "fg.muted", boxSize: 4 })] }), isLoading && (jsxRuntime.jsxs(react.Box, { textAlign: "center", py: 8, children: [jsxRuntime.jsx(react.Spinner, { size: "lg", colorPalette: "blue" }), jsxRuntime.jsx(react.Text, { mt: 4, color: "fg.muted", children: labels?.loading ??
|
|
4973
|
+
translate(removeIndex(`${colLabel}.loading`)) ??
|
|
4974
|
+
'Loading files...' })] })), isError && (jsxRuntime.jsx(react.Box, { bg: "red.50", _dark: { bg: 'red.900/20' }, border: "1px solid", borderColor: "red.200", borderRadius: "md", p: 4, children: jsxRuntime.jsx(react.Text, { color: "red.600", _dark: { color: 'red.300' }, children: labels?.loadingFailed ??
|
|
4975
|
+
translate(removeIndex(`${colLabel}.error.loading_failed`)) ??
|
|
4976
|
+
'Failed to load files' }) })), !isLoading && !isError && (jsxRuntime.jsx(react.Box, { maxHeight: "400px", overflowY: "auto", children: filteredFiles.length === 0 ? (jsxRuntime.jsx(react.Box, { textAlign: "center", py: 8, children: jsxRuntime.jsx(react.Text, { color: "fg.muted", children: labels?.noFilesFound ??
|
|
4977
|
+
translate(removeIndex(`${colLabel}.no_files_found`)) ??
|
|
4978
|
+
'No files found' }) })) : (jsxRuntime.jsx(react.VStack, { align: "stretch", gap: 2, children: filteredFiles.map((file) => {
|
|
4979
|
+
const isImage = /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name);
|
|
4980
|
+
const isSelected = selectedFileId === file.id;
|
|
4981
|
+
return (jsxRuntime.jsx(react.Box, { p: 3, border: "2px solid", borderColor: isSelected ? 'blue.500' : 'border.default', borderRadius: "md", bg: isSelected ? 'blue.50' : 'bg.panel', _dark: {
|
|
4982
|
+
bg: isSelected ? 'blue.900/20' : 'bg.panel',
|
|
4983
|
+
}, cursor: "pointer", onClick: () => setSelectedFileId(file.id), _hover: {
|
|
4984
|
+
borderColor: isSelected ? 'blue.600' : 'blue.300',
|
|
4985
|
+
bg: isSelected ? 'blue.100' : 'bg.muted',
|
|
4986
|
+
}, transition: "all 0.2s", children: jsxRuntime.jsxs(react.HStack, { gap: 3, children: [jsxRuntime.jsx(react.Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, children: isImage && file.url ? (jsxRuntime.jsx(react.Image, { src: file.url, alt: file.name, boxSize: "60px", objectFit: "cover", borderRadius: "md" })) : (jsxRuntime.jsx(react.Icon, { as: lu.LuFile, boxSize: 6, color: "fg.muted" })) }), jsxRuntime.jsxs(react.VStack, { align: "start", flex: 1, gap: 1, children: [jsxRuntime.jsx(react.Text, { fontSize: "sm", fontWeight: "medium", color: "fg.default", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: file.name }), jsxRuntime.jsxs(react.HStack, { gap: 2, children: [file.size && (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: typeof file.size === 'number'
|
|
4987
|
+
? formatBytes(file.size)
|
|
4988
|
+
: file.size }) })), file.comment && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [file.size && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: "\u2022" })), jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: file.comment })] }))] })] }), isSelected && (jsxRuntime.jsx(react.Box, { width: "24px", height: "24px", borderRadius: "full", bg: "blue.500", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: jsxRuntime.jsx(react.Text, { color: "white", fontSize: "xs", fontWeight: "bold", children: "\u2713" }) }))] }) }, file.id));
|
|
4989
|
+
}) })) }))] }) }), jsxRuntime.jsx(DialogFooter, { children: jsxRuntime.jsxs(react.HStack, { gap: 3, justify: "end", children: [jsxRuntime.jsx(react.Button, { variant: "outline", onClick: handleClose, borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: labels?.cancel ??
|
|
4990
|
+
translate(removeIndex(`${colLabel}.cancel`)) ??
|
|
4991
|
+
'Cancel' }), jsxRuntime.jsx(react.Button, { colorPalette: "blue", onClick: handleSelect, disabled: !selectedFileId, children: labels?.select ??
|
|
4992
|
+
translate(removeIndex(`${colLabel}.select`)) ??
|
|
4993
|
+
'Select' })] }) })] }) }));
|
|
4994
|
+
}
|
|
4834
4995
|
const FilePicker = ({ column, schema, prefix }) => {
|
|
4835
4996
|
const { setValue, formState: { errors }, watch, } = reactHookForm.useFormContext();
|
|
4836
|
-
const {
|
|
4837
|
-
const
|
|
4997
|
+
const { filePickerLabels } = useSchemaContext();
|
|
4998
|
+
const formI18n = useFormI18n(column, prefix);
|
|
4999
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', filePicker, } = schema;
|
|
4838
5000
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4839
|
-
const
|
|
4840
|
-
const
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
5001
|
+
const currentValue = watch(column) ?? [];
|
|
5002
|
+
const currentFiles = Array.isArray(currentValue)
|
|
5003
|
+
? currentValue
|
|
5004
|
+
: [];
|
|
5005
|
+
const colLabel = formI18n.colLabel;
|
|
5006
|
+
const [dialogOpen, setDialogOpen] = React.useState(false);
|
|
5007
|
+
const { onFetchFiles, enableMediaLibrary = false, filterImageOnly = false, } = filePicker || {};
|
|
5008
|
+
const showMediaLibrary = enableMediaLibrary && !!onFetchFiles;
|
|
5009
|
+
const handleMediaLibrarySelect = (fileId) => {
|
|
5010
|
+
const newFiles = [...currentFiles, fileId];
|
|
5011
|
+
setValue(colLabel, newFiles);
|
|
5012
|
+
};
|
|
5013
|
+
const handleRemove = (index) => {
|
|
5014
|
+
const newFiles = currentFiles.filter((_, i) => i !== index);
|
|
5015
|
+
setValue(colLabel, newFiles);
|
|
5016
|
+
};
|
|
5017
|
+
const isFileObject = (value) => {
|
|
5018
|
+
return value instanceof File;
|
|
5019
|
+
};
|
|
5020
|
+
const getFileIdentifier = (file, index) => {
|
|
5021
|
+
if (isFileObject(file)) {
|
|
5022
|
+
return `${file.name}-${file.size}-${index}`;
|
|
5023
|
+
}
|
|
5024
|
+
return file;
|
|
5025
|
+
};
|
|
5026
|
+
const getFileName = (file) => {
|
|
5027
|
+
if (isFileObject(file)) {
|
|
5028
|
+
return file.name;
|
|
5029
|
+
}
|
|
5030
|
+
return typeof file === 'string' ? file : 'Unknown file';
|
|
5031
|
+
};
|
|
5032
|
+
const getFileSize = (file) => {
|
|
5033
|
+
if (isFileObject(file)) {
|
|
5034
|
+
return file.size;
|
|
5035
|
+
}
|
|
5036
|
+
return undefined;
|
|
5037
|
+
};
|
|
5038
|
+
const isImageFile = (file) => {
|
|
5039
|
+
if (isFileObject(file)) {
|
|
5040
|
+
return file.type.startsWith('image/');
|
|
5041
|
+
}
|
|
5042
|
+
if (typeof file === 'string') {
|
|
5043
|
+
return /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file);
|
|
5044
|
+
}
|
|
5045
|
+
return false;
|
|
5046
|
+
};
|
|
5047
|
+
const getImageUrl = (file) => {
|
|
5048
|
+
if (isFileObject(file)) {
|
|
5049
|
+
return URL.createObjectURL(file);
|
|
5050
|
+
}
|
|
5051
|
+
return undefined;
|
|
5052
|
+
};
|
|
5053
|
+
return (jsxRuntime.jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
5054
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [jsxRuntime.jsxs(react.VStack, { align: "stretch", gap: 2, children: [jsxRuntime.jsx(FileDropzone, { onDrop: ({ files }) => {
|
|
5055
|
+
const newFiles = files.filter(({ name }) => !currentFiles.some((cur) => {
|
|
5056
|
+
if (isFileObject(cur)) {
|
|
5057
|
+
return cur.name === name;
|
|
5058
|
+
}
|
|
5059
|
+
return false;
|
|
5060
|
+
}));
|
|
5061
|
+
setValue(colLabel, [...currentFiles, ...newFiles]);
|
|
5062
|
+
}, placeholder: filePickerLabels?.fileDropzone ?? formI18n.t('fileDropzone') }), showMediaLibrary && (jsxRuntime.jsx(react.Button, { variant: "outline", onClick: () => setDialogOpen(true), borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: filePickerLabels?.browseLibrary ??
|
|
5063
|
+
formI18n.t('browse_library') ??
|
|
5064
|
+
'Browse from Library' }))] }), showMediaLibrary && (jsxRuntime.jsx(FilePickerDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ??
|
|
5065
|
+
formI18n.t('dialog_title') ??
|
|
5066
|
+
'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, labels: filePickerLabels, translate: formI18n.t, colLabel: colLabel })), jsxRuntime.jsx(react.Flex, { flexFlow: 'column', gap: 1, children: currentFiles.map((file, index) => {
|
|
5067
|
+
const fileIdentifier = getFileIdentifier(file, index);
|
|
5068
|
+
const fileName = getFileName(file);
|
|
5069
|
+
const fileSize = getFileSize(file);
|
|
5070
|
+
const isImage = isImageFile(file);
|
|
5071
|
+
const imageUrl = getImageUrl(file);
|
|
5072
|
+
return (jsxRuntime.jsx(react.Card.Root, { variant: 'subtle', children: jsxRuntime.jsxs(react.Card.Body, { gap: "2", cursor: 'pointer', onClick: () => handleRemove(index), display: 'flex', flexFlow: 'row', alignItems: 'center', padding: '2', border: "2px solid", borderColor: "border.default", borderRadius: "md", _hover: {
|
|
5073
|
+
borderColor: 'blue.300',
|
|
5074
|
+
bg: 'bg.muted',
|
|
5075
|
+
}, transition: "all 0.2s", children: [jsxRuntime.jsx(react.Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, marginRight: "2", children: isImage && imageUrl ? (jsxRuntime.jsx(react.Image, { src: imageUrl, alt: fileName, boxSize: "60px", objectFit: "cover", borderRadius: "md" })) : (jsxRuntime.jsx(react.Icon, { as: lu.LuFile, boxSize: 6, color: "fg.muted" })) }), jsxRuntime.jsxs(react.VStack, { align: "start", flex: 1, gap: 1, children: [jsxRuntime.jsx(react.Text, { fontSize: "sm", fontWeight: "medium", color: "fg.default", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: fileName }), fileSize !== undefined && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: formatBytes(fileSize) }))] }), jsxRuntime.jsx(react.Icon, { as: ti.TiDeleteOutline, boxSize: 5, color: "fg.muted" })] }) }, fileIdentifier));
|
|
4852
5076
|
}) })] }));
|
|
4853
5077
|
};
|
|
4854
5078
|
|
|
@@ -5972,59 +6196,62 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
|
|
|
5972
6196
|
const SchemaRenderer = ({ schema, prefix, column, }) => {
|
|
5973
6197
|
const colSchema = schema;
|
|
5974
6198
|
const { type, variant, properties: innerProperties, foreign_key, format, items, } = schema;
|
|
5975
|
-
if (variant ===
|
|
6199
|
+
if (variant === 'custom-input') {
|
|
5976
6200
|
return jsxRuntime.jsx(CustomInput, { schema: colSchema, prefix, column });
|
|
5977
6201
|
}
|
|
5978
|
-
if (type ===
|
|
6202
|
+
if (type === 'string') {
|
|
5979
6203
|
if ((schema.enum ?? []).length > 0) {
|
|
5980
6204
|
return jsxRuntime.jsx(EnumPicker, { schema: colSchema, prefix, column });
|
|
5981
6205
|
}
|
|
5982
|
-
if (variant ===
|
|
6206
|
+
if (variant === 'id-picker') {
|
|
5983
6207
|
idPickerSanityCheck(column, foreign_key);
|
|
5984
6208
|
return jsxRuntime.jsx(IdPicker, { schema: colSchema, prefix, column });
|
|
5985
6209
|
}
|
|
5986
|
-
if (format ===
|
|
6210
|
+
if (format === 'date') {
|
|
5987
6211
|
return jsxRuntime.jsx(DatePicker, { schema: colSchema, prefix, column });
|
|
5988
6212
|
}
|
|
5989
|
-
if (format ===
|
|
6213
|
+
if (format === 'time') {
|
|
5990
6214
|
return jsxRuntime.jsx(TimePicker, { schema: colSchema, prefix, column });
|
|
5991
6215
|
}
|
|
5992
|
-
if (format ===
|
|
6216
|
+
if (format === 'date-time') {
|
|
5993
6217
|
return jsxRuntime.jsx(DateTimePicker, { schema: colSchema, prefix, column });
|
|
5994
6218
|
}
|
|
5995
|
-
if (variant ===
|
|
6219
|
+
if (variant === 'text-area') {
|
|
5996
6220
|
return jsxRuntime.jsx(TextAreaInput, { schema: colSchema, prefix, column });
|
|
5997
6221
|
}
|
|
5998
6222
|
return jsxRuntime.jsx(StringInputField, { schema: colSchema, prefix, column });
|
|
5999
6223
|
}
|
|
6000
|
-
if (type ===
|
|
6224
|
+
if (type === 'number' || type === 'integer') {
|
|
6001
6225
|
return jsxRuntime.jsx(NumberInputField, { schema: colSchema, prefix, column });
|
|
6002
6226
|
}
|
|
6003
|
-
if (type ===
|
|
6227
|
+
if (type === 'boolean') {
|
|
6004
6228
|
return jsxRuntime.jsx(BooleanPicker, { schema: colSchema, prefix, column });
|
|
6005
6229
|
}
|
|
6006
|
-
if (type ===
|
|
6230
|
+
if (type === 'object') {
|
|
6007
6231
|
if (innerProperties) {
|
|
6008
6232
|
return jsxRuntime.jsx(ObjectInput, { schema: colSchema, prefix, column });
|
|
6009
6233
|
}
|
|
6010
6234
|
return jsxRuntime.jsx(RecordInput$1, { schema: colSchema, prefix, column });
|
|
6011
6235
|
}
|
|
6012
|
-
if (type ===
|
|
6013
|
-
if (variant ===
|
|
6236
|
+
if (type === 'array') {
|
|
6237
|
+
if (variant === 'id-picker') {
|
|
6014
6238
|
idPickerSanityCheck(column, foreign_key);
|
|
6015
6239
|
return (jsxRuntime.jsx(IdPicker, { schema: colSchema, prefix, column, isMultiple: true }));
|
|
6016
6240
|
}
|
|
6017
|
-
if (variant ===
|
|
6241
|
+
if (variant === 'tag-picker') {
|
|
6018
6242
|
return jsxRuntime.jsx(TagPicker, { schema: colSchema, prefix, column });
|
|
6019
6243
|
}
|
|
6020
|
-
if (variant ===
|
|
6244
|
+
if (variant === 'file-picker') {
|
|
6021
6245
|
return jsxRuntime.jsx(FilePicker, { schema: colSchema, prefix, column });
|
|
6022
6246
|
}
|
|
6023
|
-
if (variant ===
|
|
6247
|
+
if (variant === 'date-range') {
|
|
6248
|
+
return jsxRuntime.jsx(DateRangePicker, { schema: colSchema, prefix, column });
|
|
6249
|
+
}
|
|
6250
|
+
if (variant === 'enum-picker') {
|
|
6024
6251
|
const { items } = colSchema;
|
|
6025
6252
|
const { enum: enumItems } = items;
|
|
6026
6253
|
const enumSchema = {
|
|
6027
|
-
type:
|
|
6254
|
+
type: 'string',
|
|
6028
6255
|
enum: enumItems,
|
|
6029
6256
|
};
|
|
6030
6257
|
return (jsxRuntime.jsx(EnumPicker, { isMultiple: true, schema: enumSchema, prefix, column }));
|
|
@@ -6034,7 +6261,7 @@ const SchemaRenderer = ({ schema, prefix, column, }) => {
|
|
|
6034
6261
|
}
|
|
6035
6262
|
return jsxRuntime.jsx(react.Text, { children: `array ${column}` });
|
|
6036
6263
|
}
|
|
6037
|
-
if (type ===
|
|
6264
|
+
if (type === 'null') {
|
|
6038
6265
|
return jsxRuntime.jsx(react.Text, { children: `null ${column}` });
|
|
6039
6266
|
}
|
|
6040
6267
|
return jsxRuntime.jsx(react.Text, { children: "missing type" });
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal,
|
|
|
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, forwardRef } from 'react';
|
|
6
|
-
import { LuX, LuCheck, LuChevronRight } from 'react-icons/lu';
|
|
6
|
+
import { LuX, LuCheck, LuChevronRight, LuFile, LuSearch } from 'react-icons/lu';
|
|
7
7
|
import { MdOutlineSort, MdFilterAlt, MdSearch, MdOutlineViewColumn, MdFilterListAlt, MdPushPin, MdCancel, MdClear, MdOutlineChecklist, MdDateRange } from 'react-icons/md';
|
|
8
8
|
import { FaUpDown, FaGripLinesVertical, FaTrash } from 'react-icons/fa6';
|
|
9
9
|
import { BiDownArrow, BiUpArrow, BiError } from 'react-icons/bi';
|
|
@@ -3888,7 +3888,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3888
3888
|
showSubmitButton: true,
|
|
3889
3889
|
showResetButton: true,
|
|
3890
3890
|
showTitle: true,
|
|
3891
|
-
}, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, }) => {
|
|
3891
|
+
}, requireConfirmation = false, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, }) => {
|
|
3892
3892
|
const [isSuccess, setIsSuccess] = useState(false);
|
|
3893
3893
|
const [isError, setIsError] = useState(false);
|
|
3894
3894
|
const [isSubmiting, setIsSubmiting] = useState(false);
|
|
@@ -3977,6 +3977,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, serverUrl, translate, childre
|
|
|
3977
3977
|
dateTimePickerLabels,
|
|
3978
3978
|
idPickerLabels,
|
|
3979
3979
|
enumPickerLabels,
|
|
3980
|
+
filePickerLabels,
|
|
3980
3981
|
ajvResolver: ajvResolver(schema),
|
|
3981
3982
|
}, children: jsx(FormProvider, { ...form, children: children }) }));
|
|
3982
3983
|
};
|
|
@@ -4350,6 +4351,92 @@ const DatePicker = ({ column, schema, prefix }) => {
|
|
|
4350
4351
|
} })] }) })] }) }));
|
|
4351
4352
|
};
|
|
4352
4353
|
|
|
4354
|
+
dayjs.extend(utc);
|
|
4355
|
+
dayjs.extend(timezone);
|
|
4356
|
+
const DateRangePicker = ({ column, schema, prefix, }) => {
|
|
4357
|
+
const { watch, formState: { errors }, setValue, } = useFormContext();
|
|
4358
|
+
const { timezone, dateTimePickerLabels } = useSchemaContext();
|
|
4359
|
+
const formI18n = useFormI18n(column, prefix);
|
|
4360
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', displayDateFormat = 'YYYY-MM-DD', dateFormat = 'YYYY-MM-DD', } = schema;
|
|
4361
|
+
const isRequired = required?.some((columnId) => columnId === column);
|
|
4362
|
+
const colLabel = formI18n.colLabel;
|
|
4363
|
+
const [open, setOpen] = useState(false);
|
|
4364
|
+
const selectedDateRange = watch(colLabel);
|
|
4365
|
+
// Convert string[] to Date[] for the picker
|
|
4366
|
+
const selectedDates = (selectedDateRange ?? [])
|
|
4367
|
+
.map((dateStr) => {
|
|
4368
|
+
if (!dateStr)
|
|
4369
|
+
return null;
|
|
4370
|
+
const parsed = dayjs(dateStr).tz(timezone);
|
|
4371
|
+
return parsed.isValid() ? parsed.toDate() : null;
|
|
4372
|
+
})
|
|
4373
|
+
.filter((date) => date !== null);
|
|
4374
|
+
// Format display string
|
|
4375
|
+
const getDisplayText = () => {
|
|
4376
|
+
if (!selectedDateRange || selectedDateRange.length === 0) {
|
|
4377
|
+
return '';
|
|
4378
|
+
}
|
|
4379
|
+
if (selectedDateRange.length === 1) {
|
|
4380
|
+
const date = dayjs(selectedDateRange[0]).tz(timezone);
|
|
4381
|
+
return date.isValid() ? date.format(displayDateFormat) : '';
|
|
4382
|
+
}
|
|
4383
|
+
if (selectedDateRange.length === 2) {
|
|
4384
|
+
const startDate = dayjs(selectedDateRange[0]).tz(timezone);
|
|
4385
|
+
const endDate = dayjs(selectedDateRange[1]).tz(timezone);
|
|
4386
|
+
if (startDate.isValid() && endDate.isValid()) {
|
|
4387
|
+
return `${startDate.format(displayDateFormat)} - ${endDate.format(displayDateFormat)}`;
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
return '';
|
|
4391
|
+
};
|
|
4392
|
+
useEffect(() => {
|
|
4393
|
+
try {
|
|
4394
|
+
if (selectedDateRange && selectedDateRange.length > 0) {
|
|
4395
|
+
// Format dates according to dateFormat from schema
|
|
4396
|
+
const formatted = selectedDateRange
|
|
4397
|
+
.map((dateStr) => {
|
|
4398
|
+
if (!dateStr)
|
|
4399
|
+
return null;
|
|
4400
|
+
const parsed = dayjs(dateStr).tz(timezone);
|
|
4401
|
+
return parsed.isValid() ? parsed.format(dateFormat) : null;
|
|
4402
|
+
})
|
|
4403
|
+
.filter((date) => date !== null);
|
|
4404
|
+
// Update the form value only if different to avoid loops
|
|
4405
|
+
// Compare arrays element by element
|
|
4406
|
+
const needsUpdate = formatted.length !== selectedDateRange.length ||
|
|
4407
|
+
formatted.some((val, idx) => val !== selectedDateRange[idx]);
|
|
4408
|
+
if (needsUpdate && formatted.length > 0) {
|
|
4409
|
+
setValue(colLabel, formatted, {
|
|
4410
|
+
shouldValidate: true,
|
|
4411
|
+
shouldDirty: true,
|
|
4412
|
+
});
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4415
|
+
}
|
|
4416
|
+
catch (e) {
|
|
4417
|
+
console.error(e);
|
|
4418
|
+
}
|
|
4419
|
+
}, [selectedDateRange, dateFormat, colLabel, setValue, timezone]);
|
|
4420
|
+
return (jsx(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
4421
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], 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: () => {
|
|
4422
|
+
setOpen(true);
|
|
4423
|
+
}, justifyContent: 'start', children: [jsx(MdDateRange, {}), getDisplayText()] }) }), jsx(PopoverContent, { minW: '600px', children: jsxs(PopoverBody, { children: [jsx(PopoverTitle, {}), jsx(RangeDatePicker, { selected: selectedDates, onDateSelected: ({ selected, selectable, date }) => {
|
|
4424
|
+
const newDates = getRangeDates({
|
|
4425
|
+
selectable,
|
|
4426
|
+
date,
|
|
4427
|
+
selectedDates,
|
|
4428
|
+
}) ?? [];
|
|
4429
|
+
// Convert Date[] to string[]
|
|
4430
|
+
const formattedDates = newDates
|
|
4431
|
+
.map((dateObj) => dayjs(dateObj).tz(timezone).format(dateFormat))
|
|
4432
|
+
.filter((dateStr) => dateStr);
|
|
4433
|
+
setValue(colLabel, formattedDates, {
|
|
4434
|
+
shouldValidate: true,
|
|
4435
|
+
shouldDirty: true,
|
|
4436
|
+
});
|
|
4437
|
+
}, monthsToDisplay: 2 })] }) })] }) }));
|
|
4438
|
+
};
|
|
4439
|
+
|
|
4353
4440
|
function filterArray(array, searchTerm) {
|
|
4354
4441
|
// Convert the search term to lower case for case-insensitive comparison
|
|
4355
4442
|
const lowerCaseSearchTerm = searchTerm.toLowerCase();
|
|
@@ -4811,24 +4898,161 @@ const FileDropzone = ({ children = undefined, gridProps = {}, onDrop = () => { }
|
|
|
4811
4898
|
return (jsxs(Grid, { ...getColor(isDraggedOver), ref: ref, cursor: "pointer", onClick: handleClick, borderStyle: "dashed", borderColor: "colorPalette.400", alignContent: "center", justifyContent: "center", borderWidth: 1, borderRadius: 4, ...gridProps, children: [children, !!children === false && (jsxs(Fragment, { children: [jsx(Flex, { children: placeholder }), jsx(Input, { type: "file", multiple: true, style: { display: "none" }, ref: fileInput, onChange: handleChange })] }))] }));
|
|
4812
4899
|
};
|
|
4813
4900
|
|
|
4901
|
+
/**
|
|
4902
|
+
* Format bytes to human-readable string
|
|
4903
|
+
* @param bytes - The number of bytes to format
|
|
4904
|
+
* @returns Formatted string (e.g., "1.5 KB", "2.3 MB")
|
|
4905
|
+
*/
|
|
4906
|
+
function formatBytes(bytes) {
|
|
4907
|
+
if (bytes === 0)
|
|
4908
|
+
return '0 Bytes';
|
|
4909
|
+
const k = 1024;
|
|
4910
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
4911
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
4912
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
4913
|
+
}
|
|
4914
|
+
|
|
4915
|
+
function FilePickerDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, labels, translate, colLabel, }) {
|
|
4916
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
4917
|
+
const [selectedFileId, setSelectedFileId] = useState('');
|
|
4918
|
+
const { data: filesData, isLoading, isError, } = useQuery({
|
|
4919
|
+
queryKey: ['file-picker-library', searchTerm],
|
|
4920
|
+
queryFn: async () => {
|
|
4921
|
+
if (!onFetchFiles)
|
|
4922
|
+
return { data: [] };
|
|
4923
|
+
const files = await onFetchFiles(searchTerm.trim() || '');
|
|
4924
|
+
return { data: files };
|
|
4925
|
+
},
|
|
4926
|
+
enabled: open && !!onFetchFiles,
|
|
4927
|
+
});
|
|
4928
|
+
const files = (filesData?.data || []);
|
|
4929
|
+
const filteredFiles = filterImageOnly
|
|
4930
|
+
? files.filter((file) => /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name))
|
|
4931
|
+
: files;
|
|
4932
|
+
const handleSelect = () => {
|
|
4933
|
+
if (selectedFileId) {
|
|
4934
|
+
onSelect(selectedFileId);
|
|
4935
|
+
onClose();
|
|
4936
|
+
setSelectedFileId('');
|
|
4937
|
+
setSearchTerm('');
|
|
4938
|
+
}
|
|
4939
|
+
};
|
|
4940
|
+
const handleClose = () => {
|
|
4941
|
+
onClose();
|
|
4942
|
+
setSelectedFileId('');
|
|
4943
|
+
setSearchTerm('');
|
|
4944
|
+
};
|
|
4945
|
+
if (!onFetchFiles)
|
|
4946
|
+
return null;
|
|
4947
|
+
return (jsx(DialogRoot, { open: open, onOpenChange: (e) => !e.open && handleClose(), children: jsxs(DialogContent, { maxWidth: "800px", maxHeight: "90vh", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { fontSize: "lg", fontWeight: "bold", children: title }), jsx(DialogCloseTrigger, {})] }), jsx(DialogBody, { children: jsxs(VStack, { align: "stretch", gap: 4, children: [jsxs(Box, { position: "relative", children: [jsx(Input, { placeholder: labels?.searchPlaceholder ??
|
|
4948
|
+
translate(removeIndex(`${colLabel}.search_placeholder`)) ??
|
|
4949
|
+
'Search files...', value: searchTerm, onChange: (e) => setSearchTerm(e.target.value), bg: "bg.panel", border: "1px solid", borderColor: "border.default", _focus: {
|
|
4950
|
+
borderColor: 'blue.500',
|
|
4951
|
+
boxShadow: '0 0 0 1px var(--chakra-colors-blue-500)',
|
|
4952
|
+
}, pl: 10 }), jsx(Icon, { as: LuSearch, position: "absolute", left: 3, top: "50%", transform: "translateY(-50%)", color: "fg.muted", boxSize: 4 })] }), isLoading && (jsxs(Box, { textAlign: "center", py: 8, children: [jsx(Spinner, { size: "lg", colorPalette: "blue" }), jsx(Text, { mt: 4, color: "fg.muted", children: labels?.loading ??
|
|
4953
|
+
translate(removeIndex(`${colLabel}.loading`)) ??
|
|
4954
|
+
'Loading files...' })] })), isError && (jsx(Box, { bg: "red.50", _dark: { bg: 'red.900/20' }, border: "1px solid", borderColor: "red.200", borderRadius: "md", p: 4, children: jsx(Text, { color: "red.600", _dark: { color: 'red.300' }, children: labels?.loadingFailed ??
|
|
4955
|
+
translate(removeIndex(`${colLabel}.error.loading_failed`)) ??
|
|
4956
|
+
'Failed to load files' }) })), !isLoading && !isError && (jsx(Box, { maxHeight: "400px", overflowY: "auto", children: filteredFiles.length === 0 ? (jsx(Box, { textAlign: "center", py: 8, children: jsx(Text, { color: "fg.muted", children: labels?.noFilesFound ??
|
|
4957
|
+
translate(removeIndex(`${colLabel}.no_files_found`)) ??
|
|
4958
|
+
'No files found' }) })) : (jsx(VStack, { align: "stretch", gap: 2, children: filteredFiles.map((file) => {
|
|
4959
|
+
const isImage = /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file.name);
|
|
4960
|
+
const isSelected = selectedFileId === file.id;
|
|
4961
|
+
return (jsx(Box, { p: 3, border: "2px solid", borderColor: isSelected ? 'blue.500' : 'border.default', borderRadius: "md", bg: isSelected ? 'blue.50' : 'bg.panel', _dark: {
|
|
4962
|
+
bg: isSelected ? 'blue.900/20' : 'bg.panel',
|
|
4963
|
+
}, cursor: "pointer", onClick: () => setSelectedFileId(file.id), _hover: {
|
|
4964
|
+
borderColor: isSelected ? 'blue.600' : 'blue.300',
|
|
4965
|
+
bg: isSelected ? 'blue.100' : 'bg.muted',
|
|
4966
|
+
}, transition: "all 0.2s", children: jsxs(HStack, { gap: 3, children: [jsx(Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, children: isImage && file.url ? (jsx(Image, { src: file.url, alt: file.name, boxSize: "60px", objectFit: "cover", borderRadius: "md" })) : (jsx(Icon, { as: LuFile, boxSize: 6, color: "fg.muted" })) }), jsxs(VStack, { align: "start", flex: 1, gap: 1, children: [jsx(Text, { fontSize: "sm", fontWeight: "medium", color: "fg.default", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: file.name }), jsxs(HStack, { gap: 2, children: [file.size && (jsx(Fragment, { children: jsx(Text, { fontSize: "xs", color: "fg.muted", children: typeof file.size === 'number'
|
|
4967
|
+
? formatBytes(file.size)
|
|
4968
|
+
: file.size }) })), file.comment && (jsxs(Fragment, { children: [file.size && (jsx(Text, { fontSize: "xs", color: "fg.muted", children: "\u2022" })), jsx(Text, { fontSize: "xs", color: "fg.muted", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: file.comment })] }))] })] }), isSelected && (jsx(Box, { width: "24px", height: "24px", borderRadius: "full", bg: "blue.500", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: jsx(Text, { color: "white", fontSize: "xs", fontWeight: "bold", children: "\u2713" }) }))] }) }, file.id));
|
|
4969
|
+
}) })) }))] }) }), jsx(DialogFooter, { children: jsxs(HStack, { gap: 3, justify: "end", children: [jsx(Button$1, { variant: "outline", onClick: handleClose, borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: labels?.cancel ??
|
|
4970
|
+
translate(removeIndex(`${colLabel}.cancel`)) ??
|
|
4971
|
+
'Cancel' }), jsx(Button$1, { colorPalette: "blue", onClick: handleSelect, disabled: !selectedFileId, children: labels?.select ??
|
|
4972
|
+
translate(removeIndex(`${colLabel}.select`)) ??
|
|
4973
|
+
'Select' })] }) })] }) }));
|
|
4974
|
+
}
|
|
4814
4975
|
const FilePicker = ({ column, schema, prefix }) => {
|
|
4815
4976
|
const { setValue, formState: { errors }, watch, } = useFormContext();
|
|
4816
|
-
const {
|
|
4817
|
-
const
|
|
4977
|
+
const { filePickerLabels } = useSchemaContext();
|
|
4978
|
+
const formI18n = useFormI18n(column, prefix);
|
|
4979
|
+
const { required, gridColumn = 'span 12', gridRow = 'span 1', filePicker, } = schema;
|
|
4818
4980
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
4819
|
-
const
|
|
4820
|
-
const
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4981
|
+
const currentValue = watch(column) ?? [];
|
|
4982
|
+
const currentFiles = Array.isArray(currentValue)
|
|
4983
|
+
? currentValue
|
|
4984
|
+
: [];
|
|
4985
|
+
const colLabel = formI18n.colLabel;
|
|
4986
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
4987
|
+
const { onFetchFiles, enableMediaLibrary = false, filterImageOnly = false, } = filePicker || {};
|
|
4988
|
+
const showMediaLibrary = enableMediaLibrary && !!onFetchFiles;
|
|
4989
|
+
const handleMediaLibrarySelect = (fileId) => {
|
|
4990
|
+
const newFiles = [...currentFiles, fileId];
|
|
4991
|
+
setValue(colLabel, newFiles);
|
|
4992
|
+
};
|
|
4993
|
+
const handleRemove = (index) => {
|
|
4994
|
+
const newFiles = currentFiles.filter((_, i) => i !== index);
|
|
4995
|
+
setValue(colLabel, newFiles);
|
|
4996
|
+
};
|
|
4997
|
+
const isFileObject = (value) => {
|
|
4998
|
+
return value instanceof File;
|
|
4999
|
+
};
|
|
5000
|
+
const getFileIdentifier = (file, index) => {
|
|
5001
|
+
if (isFileObject(file)) {
|
|
5002
|
+
return `${file.name}-${file.size}-${index}`;
|
|
5003
|
+
}
|
|
5004
|
+
return file;
|
|
5005
|
+
};
|
|
5006
|
+
const getFileName = (file) => {
|
|
5007
|
+
if (isFileObject(file)) {
|
|
5008
|
+
return file.name;
|
|
5009
|
+
}
|
|
5010
|
+
return typeof file === 'string' ? file : 'Unknown file';
|
|
5011
|
+
};
|
|
5012
|
+
const getFileSize = (file) => {
|
|
5013
|
+
if (isFileObject(file)) {
|
|
5014
|
+
return file.size;
|
|
5015
|
+
}
|
|
5016
|
+
return undefined;
|
|
5017
|
+
};
|
|
5018
|
+
const isImageFile = (file) => {
|
|
5019
|
+
if (isFileObject(file)) {
|
|
5020
|
+
return file.type.startsWith('image/');
|
|
5021
|
+
}
|
|
5022
|
+
if (typeof file === 'string') {
|
|
5023
|
+
return /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(file);
|
|
5024
|
+
}
|
|
5025
|
+
return false;
|
|
5026
|
+
};
|
|
5027
|
+
const getImageUrl = (file) => {
|
|
5028
|
+
if (isFileObject(file)) {
|
|
5029
|
+
return URL.createObjectURL(file);
|
|
5030
|
+
}
|
|
5031
|
+
return undefined;
|
|
5032
|
+
};
|
|
5033
|
+
return (jsxs(Field, { label: formI18n.label(), required: isRequired, alignItems: 'stretch', gridColumn,
|
|
5034
|
+
gridRow, errorText: errors[`${colLabel}`] ? formI18n.required() : undefined, invalid: !!errors[colLabel], children: [jsxs(VStack, { align: "stretch", gap: 2, children: [jsx(FileDropzone, { onDrop: ({ files }) => {
|
|
5035
|
+
const newFiles = files.filter(({ name }) => !currentFiles.some((cur) => {
|
|
5036
|
+
if (isFileObject(cur)) {
|
|
5037
|
+
return cur.name === name;
|
|
5038
|
+
}
|
|
5039
|
+
return false;
|
|
5040
|
+
}));
|
|
5041
|
+
setValue(colLabel, [...currentFiles, ...newFiles]);
|
|
5042
|
+
}, placeholder: filePickerLabels?.fileDropzone ?? formI18n.t('fileDropzone') }), showMediaLibrary && (jsx(Button$1, { variant: "outline", onClick: () => setDialogOpen(true), borderColor: "border.default", bg: "bg.panel", _hover: { bg: 'bg.muted' }, children: filePickerLabels?.browseLibrary ??
|
|
5043
|
+
formI18n.t('browse_library') ??
|
|
5044
|
+
'Browse from Library' }))] }), showMediaLibrary && (jsx(FilePickerDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSelect: handleMediaLibrarySelect, title: filePickerLabels?.dialogTitle ??
|
|
5045
|
+
formI18n.t('dialog_title') ??
|
|
5046
|
+
'Select File', filterImageOnly: filterImageOnly, onFetchFiles: onFetchFiles, labels: filePickerLabels, translate: formI18n.t, colLabel: colLabel })), jsx(Flex, { flexFlow: 'column', gap: 1, children: currentFiles.map((file, index) => {
|
|
5047
|
+
const fileIdentifier = getFileIdentifier(file, index);
|
|
5048
|
+
const fileName = getFileName(file);
|
|
5049
|
+
const fileSize = getFileSize(file);
|
|
5050
|
+
const isImage = isImageFile(file);
|
|
5051
|
+
const imageUrl = getImageUrl(file);
|
|
5052
|
+
return (jsx(Card.Root, { variant: 'subtle', children: jsxs(Card.Body, { gap: "2", cursor: 'pointer', onClick: () => handleRemove(index), display: 'flex', flexFlow: 'row', alignItems: 'center', padding: '2', border: "2px solid", borderColor: "border.default", borderRadius: "md", _hover: {
|
|
5053
|
+
borderColor: 'blue.300',
|
|
5054
|
+
bg: 'bg.muted',
|
|
5055
|
+
}, transition: "all 0.2s", children: [jsx(Box, { width: "60px", height: "60px", display: "flex", alignItems: "center", justifyContent: "center", bg: "bg.muted", borderRadius: "md", flexShrink: 0, marginRight: "2", children: isImage && imageUrl ? (jsx(Image, { src: imageUrl, alt: fileName, boxSize: "60px", objectFit: "cover", borderRadius: "md" })) : (jsx(Icon, { as: LuFile, boxSize: 6, color: "fg.muted" })) }), jsxs(VStack, { align: "start", flex: 1, gap: 1, children: [jsx(Text, { fontSize: "sm", fontWeight: "medium", color: "fg.default", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: fileName }), fileSize !== undefined && (jsx(Text, { fontSize: "xs", color: "fg.muted", children: formatBytes(fileSize) }))] }), jsx(Icon, { as: TiDeleteOutline, boxSize: 5, color: "fg.muted" })] }) }, fileIdentifier));
|
|
4832
5056
|
}) })] }));
|
|
4833
5057
|
};
|
|
4834
5058
|
|
|
@@ -5952,59 +6176,62 @@ const DateTimePicker = ({ column, schema, prefix, }) => {
|
|
|
5952
6176
|
const SchemaRenderer = ({ schema, prefix, column, }) => {
|
|
5953
6177
|
const colSchema = schema;
|
|
5954
6178
|
const { type, variant, properties: innerProperties, foreign_key, format, items, } = schema;
|
|
5955
|
-
if (variant ===
|
|
6179
|
+
if (variant === 'custom-input') {
|
|
5956
6180
|
return jsx(CustomInput, { schema: colSchema, prefix, column });
|
|
5957
6181
|
}
|
|
5958
|
-
if (type ===
|
|
6182
|
+
if (type === 'string') {
|
|
5959
6183
|
if ((schema.enum ?? []).length > 0) {
|
|
5960
6184
|
return jsx(EnumPicker, { schema: colSchema, prefix, column });
|
|
5961
6185
|
}
|
|
5962
|
-
if (variant ===
|
|
6186
|
+
if (variant === 'id-picker') {
|
|
5963
6187
|
idPickerSanityCheck(column, foreign_key);
|
|
5964
6188
|
return jsx(IdPicker, { schema: colSchema, prefix, column });
|
|
5965
6189
|
}
|
|
5966
|
-
if (format ===
|
|
6190
|
+
if (format === 'date') {
|
|
5967
6191
|
return jsx(DatePicker, { schema: colSchema, prefix, column });
|
|
5968
6192
|
}
|
|
5969
|
-
if (format ===
|
|
6193
|
+
if (format === 'time') {
|
|
5970
6194
|
return jsx(TimePicker, { schema: colSchema, prefix, column });
|
|
5971
6195
|
}
|
|
5972
|
-
if (format ===
|
|
6196
|
+
if (format === 'date-time') {
|
|
5973
6197
|
return jsx(DateTimePicker, { schema: colSchema, prefix, column });
|
|
5974
6198
|
}
|
|
5975
|
-
if (variant ===
|
|
6199
|
+
if (variant === 'text-area') {
|
|
5976
6200
|
return jsx(TextAreaInput, { schema: colSchema, prefix, column });
|
|
5977
6201
|
}
|
|
5978
6202
|
return jsx(StringInputField, { schema: colSchema, prefix, column });
|
|
5979
6203
|
}
|
|
5980
|
-
if (type ===
|
|
6204
|
+
if (type === 'number' || type === 'integer') {
|
|
5981
6205
|
return jsx(NumberInputField, { schema: colSchema, prefix, column });
|
|
5982
6206
|
}
|
|
5983
|
-
if (type ===
|
|
6207
|
+
if (type === 'boolean') {
|
|
5984
6208
|
return jsx(BooleanPicker, { schema: colSchema, prefix, column });
|
|
5985
6209
|
}
|
|
5986
|
-
if (type ===
|
|
6210
|
+
if (type === 'object') {
|
|
5987
6211
|
if (innerProperties) {
|
|
5988
6212
|
return jsx(ObjectInput, { schema: colSchema, prefix, column });
|
|
5989
6213
|
}
|
|
5990
6214
|
return jsx(RecordInput$1, { schema: colSchema, prefix, column });
|
|
5991
6215
|
}
|
|
5992
|
-
if (type ===
|
|
5993
|
-
if (variant ===
|
|
6216
|
+
if (type === 'array') {
|
|
6217
|
+
if (variant === 'id-picker') {
|
|
5994
6218
|
idPickerSanityCheck(column, foreign_key);
|
|
5995
6219
|
return (jsx(IdPicker, { schema: colSchema, prefix, column, isMultiple: true }));
|
|
5996
6220
|
}
|
|
5997
|
-
if (variant ===
|
|
6221
|
+
if (variant === 'tag-picker') {
|
|
5998
6222
|
return jsx(TagPicker, { schema: colSchema, prefix, column });
|
|
5999
6223
|
}
|
|
6000
|
-
if (variant ===
|
|
6224
|
+
if (variant === 'file-picker') {
|
|
6001
6225
|
return jsx(FilePicker, { schema: colSchema, prefix, column });
|
|
6002
6226
|
}
|
|
6003
|
-
if (variant ===
|
|
6227
|
+
if (variant === 'date-range') {
|
|
6228
|
+
return jsx(DateRangePicker, { schema: colSchema, prefix, column });
|
|
6229
|
+
}
|
|
6230
|
+
if (variant === 'enum-picker') {
|
|
6004
6231
|
const { items } = colSchema;
|
|
6005
6232
|
const { enum: enumItems } = items;
|
|
6006
6233
|
const enumSchema = {
|
|
6007
|
-
type:
|
|
6234
|
+
type: 'string',
|
|
6008
6235
|
enum: enumItems,
|
|
6009
6236
|
};
|
|
6010
6237
|
return (jsx(EnumPicker, { isMultiple: true, schema: enumSchema, prefix, column }));
|
|
@@ -6014,7 +6241,7 @@ const SchemaRenderer = ({ schema, prefix, column, }) => {
|
|
|
6014
6241
|
}
|
|
6015
6242
|
return jsx(Text, { children: `array ${column}` });
|
|
6016
6243
|
}
|
|
6017
|
-
if (type ===
|
|
6244
|
+
if (type === 'null') {
|
|
6018
6245
|
return jsx(Text, { children: `null ${column}` });
|
|
6019
6246
|
}
|
|
6020
6247
|
return jsx(Text, { children: "missing type" });
|
|
@@ -3,7 +3,7 @@ import { JSONSchema7 } from 'json-schema';
|
|
|
3
3
|
import { Dispatch, ReactNode, SetStateAction } from 'react';
|
|
4
4
|
import { FieldValues, Resolver } from 'react-hook-form';
|
|
5
5
|
import { UseTranslationResponse } from 'react-i18next';
|
|
6
|
-
import { DateTimePickerLabels, IdPickerLabels, EnumPickerLabels } from './components/types/CustomJSONSchema7';
|
|
6
|
+
import { DateTimePickerLabels, IdPickerLabels, EnumPickerLabels, FilePickerLabels } from './components/types/CustomJSONSchema7';
|
|
7
7
|
export interface SchemaFormContext<TData extends FieldValues> {
|
|
8
8
|
schema: JSONSchema7;
|
|
9
9
|
serverUrl: string;
|
|
@@ -43,6 +43,7 @@ export interface SchemaFormContext<TData extends FieldValues> {
|
|
|
43
43
|
dateTimePickerLabels?: DateTimePickerLabels;
|
|
44
44
|
idPickerLabels?: IdPickerLabels;
|
|
45
45
|
enumPickerLabels?: EnumPickerLabels;
|
|
46
|
+
filePickerLabels?: FilePickerLabels;
|
|
46
47
|
ajvResolver: Resolver<FieldValues>;
|
|
47
48
|
}
|
|
48
49
|
export declare const SchemaFormContext: import("react").Context<SchemaFormContext<unknown>>;
|
|
@@ -4,7 +4,7 @@ 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, DateTimePickerLabels, IdPickerLabels, EnumPickerLabels } from '../types/CustomJSONSchema7';
|
|
7
|
+
import { CustomJSONSchema7, DateTimePickerLabels, IdPickerLabels, EnumPickerLabels, FilePickerLabels } from '../types/CustomJSONSchema7';
|
|
8
8
|
export interface FormRootProps<TData extends FieldValues> {
|
|
9
9
|
schema: CustomJSONSchema7;
|
|
10
10
|
serverUrl: string;
|
|
@@ -32,6 +32,7 @@ export interface FormRootProps<TData extends FieldValues> {
|
|
|
32
32
|
dateTimePickerLabels?: DateTimePickerLabels;
|
|
33
33
|
idPickerLabels?: IdPickerLabels;
|
|
34
34
|
enumPickerLabels?: EnumPickerLabels;
|
|
35
|
+
filePickerLabels?: FilePickerLabels;
|
|
35
36
|
}
|
|
36
37
|
export interface CustomJSONSchema7Definition extends JSONSchema7 {
|
|
37
38
|
variant: string;
|
|
@@ -48,4 +49,4 @@ export declare const idPickerSanityCheck: (column: string, foreign_key?: {
|
|
|
48
49
|
column?: string | undefined;
|
|
49
50
|
display_column?: string | undefined;
|
|
50
51
|
} | undefined) => void;
|
|
51
|
-
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, }: FormRootProps<TData>) => import("react/jsx-runtime").JSX.Element;
|
|
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;
|
|
@@ -29,6 +29,17 @@ export interface EnumPickerLabels {
|
|
|
29
29
|
emptySearchResult?: string;
|
|
30
30
|
initialResults?: string;
|
|
31
31
|
}
|
|
32
|
+
export interface FilePickerLabels {
|
|
33
|
+
fileDropzone?: string;
|
|
34
|
+
browseLibrary?: string;
|
|
35
|
+
dialogTitle?: string;
|
|
36
|
+
searchPlaceholder?: string;
|
|
37
|
+
loading?: string;
|
|
38
|
+
loadingFailed?: string;
|
|
39
|
+
noFilesFound?: string;
|
|
40
|
+
cancel?: string;
|
|
41
|
+
select?: string;
|
|
42
|
+
}
|
|
32
43
|
export interface CustomJSONSchema7 extends JSONSchema7 {
|
|
33
44
|
gridColumn?: string;
|
|
34
45
|
gridRow?: string;
|
|
@@ -55,9 +66,23 @@ export interface CustomJSONSchema7 extends JSONSchema7 {
|
|
|
55
66
|
formatOptions?: Intl.NumberFormatOptions;
|
|
56
67
|
numberStorageType?: 'string' | 'number';
|
|
57
68
|
errorMessages?: Partial<Record<ValidationErrorType | string, string>>;
|
|
69
|
+
filePicker?: FilePickerProps;
|
|
58
70
|
}
|
|
59
71
|
export interface TagPickerProps {
|
|
60
72
|
column: string;
|
|
61
73
|
schema: CustomJSONSchema7;
|
|
62
74
|
prefix: string;
|
|
63
75
|
}
|
|
76
|
+
export interface FilePickerMediaFile {
|
|
77
|
+
id: string;
|
|
78
|
+
name: string;
|
|
79
|
+
url?: string;
|
|
80
|
+
size?: string | number;
|
|
81
|
+
comment?: string;
|
|
82
|
+
type?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface FilePickerProps {
|
|
85
|
+
onFetchFiles?: (search: string) => Promise<FilePickerMediaFile[]>;
|
|
86
|
+
enableMediaLibrary?: boolean;
|
|
87
|
+
filterImageOnly?: boolean;
|
|
88
|
+
}
|