@bsol-oss/react-datatable5 13.0.1-beta.37 → 13.0.1-beta.39
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 +4 -12
- package/dist/index.js +79 -155
- package/dist/index.mjs +81 -156
- package/dist/types/components/Form/SchemaFormContext.d.ts +0 -5
- package/dist/types/components/Form/components/MediaLibraryBrowser.d.ts +1 -1
- package/dist/types/components/Form/components/core/DefaultForm.d.ts +3 -4
- package/dist/types/components/Form/components/core/FormRoot.d.ts +1 -6
- package/dist/types/index.d.ts +0 -1
- package/package.json +1 -1
- package/dist/types/components/Form/components/core/FormTitle.d.ts +0 -1
- package/dist/types/components/Form/components/core/SubmitButton.d.ts +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -700,11 +700,6 @@ interface FormRootProps<TData extends FieldValues> {
|
|
|
700
700
|
setIdMap: Dispatch<SetStateAction<Record<string, unknown>>>;
|
|
701
701
|
form: UseFormReturn<TData, any, TData>;
|
|
702
702
|
children: ReactNode;
|
|
703
|
-
displayConfig?: {
|
|
704
|
-
showSubmitButton?: boolean;
|
|
705
|
-
showResetButton?: boolean;
|
|
706
|
-
showTitle?: boolean;
|
|
707
|
-
};
|
|
708
703
|
dateTimePickerLabels?: DateTimePickerLabels;
|
|
709
704
|
idPickerLabels?: IdPickerLabels;
|
|
710
705
|
enumPickerLabels?: EnumPickerLabels;
|
|
@@ -720,16 +715,13 @@ interface CustomJSONSchema7Definition extends JSONSchema7 {
|
|
|
720
715
|
customQueryFn: any;
|
|
721
716
|
children: ReactNode;
|
|
722
717
|
}
|
|
723
|
-
declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, children,
|
|
718
|
+
declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, children, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, formButtonLabels, timePickerLabels, insideDialog, }: FormRootProps<TData>) => react_jsx_runtime.JSX.Element;
|
|
724
719
|
|
|
725
720
|
interface DefaultFormProps<TData extends FieldValues> {
|
|
726
|
-
formConfig: Omit<FormRootProps<TData>,
|
|
727
|
-
showTitle?: boolean;
|
|
721
|
+
formConfig: Omit<FormRootProps<TData>, 'children'>;
|
|
728
722
|
}
|
|
729
723
|
declare const DefaultForm: <TData extends FieldValues>({ formConfig, }: DefaultFormProps<TData>) => react_jsx_runtime.JSX.Element;
|
|
730
724
|
|
|
731
|
-
declare const FormTitle: () => react_jsx_runtime.JSX.Element;
|
|
732
|
-
|
|
733
725
|
declare const FormBody: () => react_jsx_runtime.JSX.Element;
|
|
734
726
|
|
|
735
727
|
type MediaLibraryBrowserPropsBase = {
|
|
@@ -751,7 +743,7 @@ type MediaLibraryBrowserPropsMultiple = MediaLibraryBrowserPropsBase & {
|
|
|
751
743
|
onSelectedFileChange?: (files: FilePickerMediaFile[]) => void;
|
|
752
744
|
};
|
|
753
745
|
type MediaLibraryBrowserProps = MediaLibraryBrowserPropsSingle | MediaLibraryBrowserPropsMultiple;
|
|
754
|
-
declare const MediaLibraryBrowser: ({ onFetchFiles, filterImageOnly, labels, enabled, multiple, onFileSelect, selectedFile: controlledSelectedFile, onSelectedFileChange, }: MediaLibraryBrowserProps) => react_jsx_runtime.JSX.Element
|
|
746
|
+
declare const MediaLibraryBrowser: ({ onFetchFiles, filterImageOnly, labels, enabled, multiple, onFileSelect, selectedFile: controlledSelectedFile, onSelectedFileChange, }: MediaLibraryBrowserProps) => react_jsx_runtime.JSX.Element;
|
|
755
747
|
|
|
756
748
|
interface UseFormProps<T> {
|
|
757
749
|
preLoadedValues?: T | undefined;
|
|
@@ -1477,4 +1469,4 @@ declare module '@tanstack/react-table' {
|
|
|
1477
1469
|
}
|
|
1478
1470
|
}
|
|
1479
1471
|
|
|
1480
|
-
export { CalendarDisplay, type CalendarDisplayProps, type CalendarEvent, type CalendarProps, CardHeader, type CardHeaderProps, type CustomJSONSchema7, type CustomJSONSchema7Definition, type CustomQueryFn, type CustomQueryFnParams, type CustomQueryFnResponse, DataDisplay, type DataDisplayProps, type DataResponse, DataTable, type DataTableDefaultState, type DataTableProps, DataTableServer, type DataTableServerProps, DatePickerContext, DatePickerInput, type DatePickerInputProps, type DatePickerLabels, type DatePickerProps, type DateTimePickerLabels, DefaultCardTitle, DefaultForm, type DefaultFormProps, DefaultTable, type DefaultTableProps, DefaultTableServer, type DefaultTableServerProps, DensityToggleButton, type DensityToggleButtonProps, type EditFilterButtonProps, EditSortingButton, type EditSortingButtonProps, type EditViewButtonProps, EmptyState, type EmptyStateProps, type EnumPickerLabels, ErrorAlert, type ErrorAlertProps, type FilePickerLabels, type FilePickerMediaFile, type FilePickerProps, FilterDialog, FormBody, type FormButtonLabels, FormRoot, type FormRootProps,
|
|
1472
|
+
export { CalendarDisplay, type CalendarDisplayProps, type CalendarEvent, type CalendarProps, CardHeader, type CardHeaderProps, type CustomJSONSchema7, type CustomJSONSchema7Definition, type CustomQueryFn, type CustomQueryFnParams, type CustomQueryFnResponse, DataDisplay, type DataDisplayProps, type DataResponse, DataTable, type DataTableDefaultState, type DataTableProps, DataTableServer, type DataTableServerProps, DatePickerContext, DatePickerInput, type DatePickerInputProps, type DatePickerLabels, type DatePickerProps, type DateTimePickerLabels, DefaultCardTitle, DefaultForm, type DefaultFormProps, DefaultTable, type DefaultTableProps, DefaultTableServer, type DefaultTableServerProps, DensityToggleButton, type DensityToggleButtonProps, type EditFilterButtonProps, EditSortingButton, type EditSortingButtonProps, type EditViewButtonProps, EmptyState, type EmptyStateProps, type EnumPickerLabels, ErrorAlert, type ErrorAlertProps, type FilePickerLabels, type FilePickerMediaFile, type FilePickerProps, FilterDialog, FormBody, type FormButtonLabels, FormRoot, type FormRootProps, type GetDateColorProps, type GetMultiDatesProps, type GetRangeDatesProps, type GetStyleProps, type GetVariantProps, GlobalFilter, type IdPickerLabels, type LoadInitialValuesParams, type LoadInitialValuesResult, MediaLibraryBrowser, type MediaLibraryBrowserProps, PageSizeControl, type PageSizeControlProps, Pagination, type QueryParams, type RangeCalendarProps, type RangeDatePickerLabels, type RangeDatePickerProps, RecordDisplay, type RecordDisplayProps, ReloadButton, type ReloadButtonProps, ResetFilteringButton, ResetSelectionButton, ResetSortingButton, type Result, RowCountText, SelectAllRowsToggle, type SelectAllRowsToggleProps, Table, TableBody, type TableBodyProps, TableCardContainer, type TableCardContainerProps, TableCards, type TableCardsProps, TableComponent, TableControls, type TableControlsProps, TableDataDisplay, type TableDataDisplayProps, TableFilter, TableFilterTags, type TableFilterTagsProps, TableFooter, type TableFooterProps, TableHeader, type TableHeaderProps, type TableHeaderTexts, TableLoadingComponent, type TableLoadingComponentProps, type TableProps, type TableRendererProps, type TableRowSelectorProps, TableSelector, TableSorter, TableViewer, TextCell, type TextCellProps, type TimePickerLabels, TimeRangeZoom, type TimeRangeZoomLabels, type TimeRangeZoomProps, TimeViewportBlock, type TimeViewportBlockItem, type TimeViewportBlockProps, type TimeViewportBlockRenderArgs, TimeViewportBlocks, type TimeViewportBlocksProps, TimeViewportGrid, type TimeViewportGridProps, TimeViewportHeader, type TimeViewportHeaderProps, type TimeViewportHeaderTick, TimeViewportMarkerLine, type TimeViewportMarkerLineProps, TimeViewportRoot, type TimeViewportRootProps, type TimeViewportTrackRenderArgs, type UseDataTableProps, type UseDataTableReturn, type UseDataTableServerProps, type UseDataTableServerReturn, type UseFormProps, type UseTimeRangeZoomResult, type UseTimeViewportBlockGeometryResult, type UseTimeViewportDerivedResult, type UseTimeViewportTicksResult, type ValidationErrorType, ViewDialog, type ViewableTimeRange, defaultRenderDisplay, getMultiDates, getRangeDates, useDataTable, useDataTableContext, useDataTableServer, useForm, useTimeRangeZoom, useTimeViewport, useTimeViewportBlockGeometry, useTimeViewportHeader, useTimeViewportTicks };
|
package/dist/index.js
CHANGED
|
@@ -4190,11 +4190,6 @@ const SchemaFormContext = React.createContext({
|
|
|
4190
4190
|
schema: {},
|
|
4191
4191
|
onSubmit: async () => { },
|
|
4192
4192
|
timezone: 'Asia/Hong_Kong',
|
|
4193
|
-
displayConfig: {
|
|
4194
|
-
showSubmitButton: false,
|
|
4195
|
-
showResetButton: false,
|
|
4196
|
-
showTitle: true,
|
|
4197
|
-
},
|
|
4198
4193
|
});
|
|
4199
4194
|
|
|
4200
4195
|
const useSchemaContext = () => {
|
|
@@ -5386,108 +5381,81 @@ function formatBytes(bytes) {
|
|
|
5386
5381
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
5387
5382
|
}
|
|
5388
5383
|
|
|
5384
|
+
const IMAGE_EXT = /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i;
|
|
5385
|
+
function filterImageFiles(files) {
|
|
5386
|
+
return files.filter((f) => IMAGE_EXT.test(f.name));
|
|
5387
|
+
}
|
|
5389
5388
|
const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, enabled = true, multiple = false, onFileSelect, selectedFile: controlledSelectedFile, onSelectedFileChange, }) => {
|
|
5390
|
-
const [
|
|
5391
|
-
const
|
|
5392
|
-
|
|
5393
|
-
// Use controlled or internal state for selectedFile
|
|
5394
|
-
const selectedFile = controlledSelectedFile ?? internalSelectedFile;
|
|
5395
|
-
const setSelectedFile = onSelectedFileChange ?? setInternalSelectedFile;
|
|
5396
|
-
const { data: filesData, isLoading, isError, } = reactQuery.useQuery({
|
|
5397
|
-
queryKey: ['file-picker-library', searchTerm],
|
|
5389
|
+
const [search, setSearch] = React.useState('');
|
|
5390
|
+
const query = reactQuery.useQuery({
|
|
5391
|
+
queryKey: ['media-library', search, filterImageOnly],
|
|
5398
5392
|
queryFn: async () => {
|
|
5399
5393
|
if (!onFetchFiles)
|
|
5400
|
-
return
|
|
5401
|
-
const
|
|
5402
|
-
return
|
|
5394
|
+
return [];
|
|
5395
|
+
const list = await onFetchFiles(search);
|
|
5396
|
+
return filterImageOnly ? filterImageFiles(list) : list;
|
|
5403
5397
|
},
|
|
5404
5398
|
enabled: enabled && !!onFetchFiles,
|
|
5405
5399
|
});
|
|
5406
|
-
const files = (
|
|
5407
|
-
const
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
if (multiple
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
else {
|
|
5423
|
-
const newFile = selectedFile === file ? undefined : file;
|
|
5424
|
-
setSelectedFile(newFile);
|
|
5425
|
-
if (onFileSelect && newFile) {
|
|
5426
|
-
onFileSelect(newFile);
|
|
5427
|
-
}
|
|
5400
|
+
const files = React.useMemo(() => (query.data ?? []), [query.data]);
|
|
5401
|
+
const selectedIds = React.useMemo(() => {
|
|
5402
|
+
if (multiple && Array.isArray(controlledSelectedFile)) {
|
|
5403
|
+
return new Set(controlledSelectedFile.map((f) => f.id));
|
|
5404
|
+
}
|
|
5405
|
+
if (!multiple &&
|
|
5406
|
+
controlledSelectedFile &&
|
|
5407
|
+
!Array.isArray(controlledSelectedFile)) {
|
|
5408
|
+
return new Set([controlledSelectedFile.id]);
|
|
5409
|
+
}
|
|
5410
|
+
return new Set();
|
|
5411
|
+
}, [multiple, controlledSelectedFile]);
|
|
5412
|
+
const handleSingleSelect = (file) => {
|
|
5413
|
+
if (!multiple) {
|
|
5414
|
+
onSelectedFileChange?.(file);
|
|
5415
|
+
onFileSelect?.(file);
|
|
5428
5416
|
}
|
|
5429
5417
|
};
|
|
5430
|
-
const
|
|
5431
|
-
|
|
5418
|
+
const handleMultipleToggle = (file, checked) => {
|
|
5419
|
+
const current = multiple && Array.isArray(controlledSelectedFile)
|
|
5420
|
+
? [...controlledSelectedFile]
|
|
5421
|
+
: [];
|
|
5422
|
+
const next = checked
|
|
5423
|
+
? [...current, file]
|
|
5424
|
+
: current.filter((f) => f.id !== file.id);
|
|
5425
|
+
onSelectedFileChange?.(next);
|
|
5426
|
+
onFileSelect?.(next);
|
|
5432
5427
|
};
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
:
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
? {
|
|
5465
|
-
base: 'colorPalette.50',
|
|
5466
|
-
_dark: 'colorPalette.900/20',
|
|
5467
|
-
}
|
|
5468
|
-
: 'bg.panel', colorPalette: "blue", cursor: "pointer", onClick: () => handleFileClick(file), _hover: {
|
|
5469
|
-
borderColor: isSelected
|
|
5470
|
-
? {
|
|
5471
|
-
base: 'colorPalette.600',
|
|
5472
|
-
_dark: 'colorPalette.400',
|
|
5473
|
-
}
|
|
5474
|
-
: {
|
|
5475
|
-
base: 'colorPalette.300',
|
|
5476
|
-
_dark: 'colorPalette.400',
|
|
5477
|
-
},
|
|
5478
|
-
bg: isSelected
|
|
5479
|
-
? {
|
|
5480
|
-
base: 'colorPalette.100',
|
|
5481
|
-
_dark: 'colorPalette.800/30',
|
|
5482
|
-
}
|
|
5483
|
-
: 'bg.muted',
|
|
5484
|
-
}, 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 && !imageFailed ? (jsxRuntime.jsx(react.Image, { src: file.url, alt: file.name, boxSize: "60px", objectFit: "cover", borderRadius: "md", onError: () => handleImageError(file.id) })) : isImage && (imageFailed || !file.url) ? (jsxRuntime.jsx(react.Icon, { as: lu.LuImage, boxSize: 6, color: "fg.muted" })) : (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'
|
|
5485
|
-
? formatBytes(file.size)
|
|
5486
|
-
: 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: {
|
|
5487
|
-
base: 'colorPalette.500',
|
|
5488
|
-
_dark: 'colorPalette.400',
|
|
5489
|
-
}, colorPalette: "blue", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: jsxRuntime.jsx(react.Text, { color: "white", fontSize: "xs", fontWeight: "bold", children: "\u2713" }) }))] }) }, file.id));
|
|
5490
|
-
}) })) }))] }));
|
|
5428
|
+
const isLoading = query.isPending;
|
|
5429
|
+
const isError = query.isError;
|
|
5430
|
+
const searchPlaceholder = labels?.searchPlaceholder ?? 'Search files...';
|
|
5431
|
+
const loadingText = labels?.loading ?? 'Loading...';
|
|
5432
|
+
const errorText = labels?.loadingFailed ?? 'Failed to load files';
|
|
5433
|
+
const emptyText = labels?.noFilesFound ?? 'No files found';
|
|
5434
|
+
return (jsxRuntime.jsxs(react.VStack, { align: "stretch", gap: 4, children: [jsxRuntime.jsx(InputGroup, { startElement: jsxRuntime.jsx(react.Icon, { as: lu.LuSearch, color: "fg.muted" }), children: jsxRuntime.jsx(react.Input, { placeholder: searchPlaceholder, value: search, onChange: (e) => setSearch(e.target.value), bg: "bg.panel", borderColor: "border.default" }) }), isLoading && (jsxRuntime.jsxs(react.HStack, { gap: 2, py: 6, justify: "center", children: [jsxRuntime.jsx(react.Spinner, { size: "sm", colorPalette: "blue" }), jsxRuntime.jsx(react.Text, { fontSize: "sm", color: "fg.muted", children: loadingText })] })), isError && (jsxRuntime.jsx(react.Box, { py: 4, px: 3, borderRadius: "md", bg: { base: 'red.50', _dark: 'red.900/20' }, borderWidth: "1px", borderColor: { base: 'red.200', _dark: 'red.800' }, children: jsxRuntime.jsx(react.Text, { fontSize: "sm", color: { base: 'red.600', _dark: 'red.300' }, children: errorText }) })), !isLoading && !isError && files.length === 0 && (jsxRuntime.jsx(react.Box, { py: 6, textAlign: "center", children: jsxRuntime.jsx(react.Text, { fontSize: "sm", color: "fg.muted", children: emptyText }) })), !isLoading && !isError && files.length > 0 && (jsxRuntime.jsx(react.SimpleGrid, { columns: { base: 2, sm: 3, md: 4 }, gap: 3, children: files.map((file) => {
|
|
5435
|
+
const isImage = IMAGE_EXT.test(file.name);
|
|
5436
|
+
const isSelected = selectedIds.has(file.id);
|
|
5437
|
+
const fileSize = typeof file.size === 'number'
|
|
5438
|
+
? formatBytes(file.size)
|
|
5439
|
+
: file.size ?? null;
|
|
5440
|
+
if (multiple) {
|
|
5441
|
+
return (jsxRuntime.jsxs(CheckboxCard, { checked: isSelected, onCheckedChange: (e) => handleMultipleToggle(file, e.checked === true), variant: "outline", borderColor: "border.default", _hover: { borderColor: 'border.emphasized', bg: 'bg.muted' }, cursor: "pointer", children: [jsxRuntime.jsx(react.Box, { width: "100%", aspectRatio: 1, bg: "bg.muted", borderRadius: "md", overflow: "hidden", mb: 2, display: "flex", alignItems: "center", justifyContent: "center", children: isImage && file.url ? (jsxRuntime.jsx(react.Image, { src: file.url, alt: file.name, width: "100%", height: "100%", objectFit: "cover" })) : isImage ? (jsxRuntime.jsx(react.Icon, { as: lu.LuImage, boxSize: 8, color: "fg.muted" })) : (jsxRuntime.jsx(react.Icon, { as: lu.LuFile, boxSize: 8, color: "fg.muted" })) }), jsxRuntime.jsx(react.Text, { fontSize: "xs", fontWeight: "medium", color: "fg.default", lineClamp: 2, children: file.name }), fileSize && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: fileSize }))] }, file.id));
|
|
5442
|
+
}
|
|
5443
|
+
return (jsxRuntime.jsxs(react.Box, { role: "button", tabIndex: 0, onClick: () => handleSingleSelect(file), onKeyDown: (e) => {
|
|
5444
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
5445
|
+
e.preventDefault();
|
|
5446
|
+
handleSingleSelect(file);
|
|
5447
|
+
}
|
|
5448
|
+
}, padding: 3, borderRadius: "md", borderWidth: "2px", borderColor: isSelected ? 'colorPalette.500' : 'border.default', bg: isSelected
|
|
5449
|
+
? { base: 'colorPalette.50', _dark: 'colorPalette.900/20' }
|
|
5450
|
+
: 'bg.panel', _hover: {
|
|
5451
|
+
borderColor: isSelected
|
|
5452
|
+
? 'colorPalette.500'
|
|
5453
|
+
: 'border.emphasized',
|
|
5454
|
+
bg: isSelected
|
|
5455
|
+
? { base: 'colorPalette.50', _dark: 'colorPalette.900/20' }
|
|
5456
|
+
: 'bg.muted',
|
|
5457
|
+
}, cursor: "pointer", transition: "all 0.2s", children: [jsxRuntime.jsx(react.Box, { width: "100%", aspectRatio: 1, bg: "bg.muted", borderRadius: "md", overflow: "hidden", mb: 2, display: "flex", alignItems: "center", justifyContent: "center", children: isImage && file.url ? (jsxRuntime.jsx(react.Image, { src: file.url, alt: file.name, width: "100%", height: "100%", objectFit: "cover" })) : isImage ? (jsxRuntime.jsx(react.Icon, { as: lu.LuImage, boxSize: 8, color: "fg.muted" })) : (jsxRuntime.jsx(react.Icon, { as: lu.LuFile, boxSize: 8, color: "fg.muted" })) }), jsxRuntime.jsx(react.Text, { fontSize: "xs", fontWeight: "medium", color: "fg.default", lineClamp: 2, children: file.name }), fileSize && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: fileSize }))] }, file.id));
|
|
5458
|
+
}) }))] }));
|
|
5491
5459
|
};
|
|
5492
5460
|
|
|
5493
5461
|
function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, onUploadFile, enableUpload = false, labels, }) {
|
|
@@ -5655,7 +5623,8 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
|
|
|
5655
5623
|
const { required, gridColumn = 'span 12', gridRow = 'span 1', filePicker, type, } = schema;
|
|
5656
5624
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
5657
5625
|
const isSingleSelect = type === 'string';
|
|
5658
|
-
const
|
|
5626
|
+
const colLabel = formI18n.colLabel;
|
|
5627
|
+
const currentValue = watch(colLabel) ?? (isSingleSelect ? '' : []);
|
|
5659
5628
|
// Handle string IDs only
|
|
5660
5629
|
const currentFileIds = isSingleSelect
|
|
5661
5630
|
? currentValue
|
|
@@ -5664,7 +5633,6 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
|
|
|
5664
5633
|
: Array.isArray(currentValue)
|
|
5665
5634
|
? currentValue
|
|
5666
5635
|
: [];
|
|
5667
|
-
const colLabel = formI18n.colLabel;
|
|
5668
5636
|
const fieldError = getNestedError(errors, colLabel);
|
|
5669
5637
|
const [dialogOpen, setDialogOpen] = React.useState(false);
|
|
5670
5638
|
const [failedImageIds, setFailedImageIds] = React.useState(new Set());
|
|
@@ -5752,7 +5720,7 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
|
|
|
5752
5720
|
: /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(fileId);
|
|
5753
5721
|
const imageFailed = failedImageIds.has(fileId);
|
|
5754
5722
|
const displayName = file?.name ?? fileId;
|
|
5755
|
-
return (jsxRuntime.jsx(react.Card.Root, { variant: 'subtle',
|
|
5723
|
+
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: {
|
|
5756
5724
|
borderColor: 'colorPalette.300',
|
|
5757
5725
|
bg: 'bg.muted',
|
|
5758
5726
|
}, 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", overflow: "hidden", children: isImage && file?.url && !imageFailed ? (jsxRuntime.jsx(react.Image, { src: file.url, alt: displayName, boxSize: "60px", objectFit: "cover", onError: () => handleImageError(fileId) })) : isImage && !imageFailed ? (jsxRuntime.jsx(react.Icon, { as: lu.LuImage, boxSize: 6, color: "fg.muted" })) : (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: displayName }), file?.size && (jsxRuntime.jsx(react.Text, { fontSize: "xs", color: "fg.muted", children: typeof file.size === 'number'
|
|
@@ -7848,66 +7816,24 @@ const ColumnRenderer = ({ column, properties, prefix, parentRequired, }) => {
|
|
|
7848
7816
|
return jsxRuntime.jsx(SchemaRenderer, { schema: schemaWithRequired, prefix, column });
|
|
7849
7817
|
};
|
|
7850
7818
|
|
|
7851
|
-
const SubmitButton = () => {
|
|
7852
|
-
const { onSubmit, formButtonLabels } = useSchemaContext();
|
|
7853
|
-
const methods = reactHookForm.useFormContext();
|
|
7854
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7855
|
-
const onValid = async (data) => {
|
|
7856
|
-
// Validation is handled by react-hook-form
|
|
7857
|
-
// This function will only be called if validation passes
|
|
7858
|
-
if (onSubmit) {
|
|
7859
|
-
await onSubmit(data);
|
|
7860
|
-
}
|
|
7861
|
-
};
|
|
7862
|
-
return (jsxRuntime.jsx(react.Button, { onClick: () => {
|
|
7863
|
-
methods.handleSubmit(onValid)();
|
|
7864
|
-
}, formNoValidate: true, children: formButtonLabels?.submit ?? 'Submit' }));
|
|
7865
|
-
};
|
|
7866
|
-
|
|
7867
7819
|
const FormBody = () => {
|
|
7868
|
-
const { schema
|
|
7869
|
-
const { showSubmitButton, showResetButton } = displayConfig;
|
|
7870
|
-
const methods = reactHookForm.useFormContext();
|
|
7871
|
-
console.log('errors', methods.formState.errors);
|
|
7820
|
+
const { schema } = useSchemaContext();
|
|
7872
7821
|
const { properties } = schema;
|
|
7873
7822
|
const ordered = Object.keys(properties);
|
|
7874
|
-
return (jsxRuntime.
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
methods.reset();
|
|
7882
|
-
}, variant: 'subtle', children: formButtonLabels?.reset ?? 'Reset' })), showSubmitButton && jsxRuntime.jsx(SubmitButton, {})] }))] }));
|
|
7883
|
-
};
|
|
7884
|
-
|
|
7885
|
-
const FormTitle = () => {
|
|
7886
|
-
const { schema } = useSchemaContext();
|
|
7887
|
-
// Debug log when form title is missing
|
|
7888
|
-
if (!schema.title) {
|
|
7889
|
-
console.debug('[Form Title] Missing title in root schema. Add title property to schema.', {
|
|
7890
|
-
schema: {
|
|
7891
|
-
type: schema.type,
|
|
7892
|
-
properties: schema.properties
|
|
7893
|
-
? Object.keys(schema.properties)
|
|
7894
|
-
: undefined,
|
|
7895
|
-
},
|
|
7896
|
-
});
|
|
7897
|
-
}
|
|
7898
|
-
return jsxRuntime.jsx(react.Heading, { children: schema.title ?? 'Form' });
|
|
7823
|
+
return (jsxRuntime.jsx(react.Flex, { flexFlow: 'column', gap: "2", children: jsxRuntime.jsx(react.Grid, { gap: "4", gridTemplateColumns: 'repeat(12, 1fr)', autoFlow: 'row', children: ordered.map((column) => {
|
|
7824
|
+
if (!properties) {
|
|
7825
|
+
console.error('properties is undefined when using FormBody', schema);
|
|
7826
|
+
return null;
|
|
7827
|
+
}
|
|
7828
|
+
return (jsxRuntime.jsx(ColumnRenderer, { properties: properties, prefix: ``, parentRequired: schema.required, column }, `form-input-${column}`));
|
|
7829
|
+
}) }) }));
|
|
7899
7830
|
};
|
|
7900
7831
|
|
|
7901
|
-
const FormRoot = ({ schema, idMap, setIdMap, form, children,
|
|
7902
|
-
showSubmitButton: false,
|
|
7903
|
-
showResetButton: false,
|
|
7904
|
-
showTitle: true,
|
|
7905
|
-
}, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, formButtonLabels, timePickerLabels, insideDialog = false, }) => {
|
|
7832
|
+
const FormRoot = ({ schema, idMap, setIdMap, form, children, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, formButtonLabels, timePickerLabels, insideDialog = false, }) => {
|
|
7906
7833
|
return (jsxRuntime.jsx(SchemaFormContext.Provider, { value: {
|
|
7907
7834
|
schema,
|
|
7908
7835
|
idMap,
|
|
7909
7836
|
setIdMap,
|
|
7910
|
-
displayConfig,
|
|
7911
7837
|
dateTimePickerLabels,
|
|
7912
7838
|
idPickerLabels,
|
|
7913
7839
|
enumPickerLabels,
|
|
@@ -7919,8 +7845,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, children, displayConfig = {
|
|
|
7919
7845
|
};
|
|
7920
7846
|
|
|
7921
7847
|
const DefaultForm = ({ formConfig, }) => {
|
|
7922
|
-
|
|
7923
|
-
return (jsxRuntime.jsx(FormRoot, { ...formConfig, children: jsxRuntime.jsxs(react.Grid, { gap: "2", children: [showTitle && jsxRuntime.jsx(FormTitle, {}), jsxRuntime.jsx(FormBody, {})] }) }));
|
|
7848
|
+
return (jsxRuntime.jsx(FormRoot, { ...formConfig, children: jsxRuntime.jsx(react.Grid, { gap: "2", children: jsxRuntime.jsx(FormBody, {}) }) }));
|
|
7924
7849
|
};
|
|
7925
7850
|
|
|
7926
7851
|
function useForm({ preLoadedValues, schema, }) {
|
|
@@ -9500,7 +9425,6 @@ exports.ErrorAlert = ErrorAlert;
|
|
|
9500
9425
|
exports.FilterDialog = FilterDialog;
|
|
9501
9426
|
exports.FormBody = FormBody;
|
|
9502
9427
|
exports.FormRoot = FormRoot;
|
|
9503
|
-
exports.FormTitle = FormTitle;
|
|
9504
9428
|
exports.GlobalFilter = GlobalFilter;
|
|
9505
9429
|
exports.MediaLibraryBrowser = MediaLibraryBrowser;
|
|
9506
9430
|
exports.PageSizeControl = PageSizeControl;
|
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, CheckboxCard as CheckboxCard$1, Input, Menu, createRecipeContext, createContext as createContext$1, Pagination as Pagination$1, usePaginationContext, Tooltip as Tooltip$1, Group, InputElement, Tag as Tag$1, Checkbox as Checkbox$1, Icon, VStack, Heading, EmptyState as EmptyState$2, List, Table as Table$1, Card, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Clipboard, Badge, Link, Image, Alert, Field as Field$1, Popover, useFilter, useListCollection, Combobox, Tabs, useCombobox, Show, Skeleton, NumberInput, Textarea as Textarea$1, InputGroup as InputGroup$1, Select, Stack } from '@chakra-ui/react';
|
|
2
|
+
import { Button as Button$1, AbsoluteCenter, Spinner, Span, IconButton, Portal, Dialog, Flex, Text, useDisclosure, DialogBackdrop, RadioGroup as RadioGroup$1, Grid, Box, Slider as Slider$1, HStack, For, CheckboxCard as CheckboxCard$1, Input, Menu, createRecipeContext, createContext as createContext$1, Pagination as Pagination$1, usePaginationContext, Tooltip as Tooltip$1, Group, InputElement, Tag as Tag$1, Checkbox as Checkbox$1, Icon, VStack, Heading, EmptyState as EmptyState$2, List, Table as Table$1, Card, MenuRoot as MenuRoot$1, MenuTrigger as MenuTrigger$1, Clipboard, Badge, Link, Image, Alert, Field as Field$1, Popover, useFilter, useListCollection, Combobox, SimpleGrid, Tabs, useCombobox, Show, Skeleton, NumberInput, Textarea as Textarea$1, InputGroup as InputGroup$1, Select, Stack } from '@chakra-ui/react';
|
|
3
3
|
import { AiOutlineColumnWidth } from 'react-icons/ai';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { createContext, useContext, useState, useMemo, useCallback, useEffect, useRef } from 'react';
|
|
@@ -4170,11 +4170,6 @@ const SchemaFormContext = createContext({
|
|
|
4170
4170
|
schema: {},
|
|
4171
4171
|
onSubmit: async () => { },
|
|
4172
4172
|
timezone: 'Asia/Hong_Kong',
|
|
4173
|
-
displayConfig: {
|
|
4174
|
-
showSubmitButton: false,
|
|
4175
|
-
showResetButton: false,
|
|
4176
|
-
showTitle: true,
|
|
4177
|
-
},
|
|
4178
4173
|
});
|
|
4179
4174
|
|
|
4180
4175
|
const useSchemaContext = () => {
|
|
@@ -5366,108 +5361,81 @@ function formatBytes(bytes) {
|
|
|
5366
5361
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
5367
5362
|
}
|
|
5368
5363
|
|
|
5364
|
+
const IMAGE_EXT = /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i;
|
|
5365
|
+
function filterImageFiles(files) {
|
|
5366
|
+
return files.filter((f) => IMAGE_EXT.test(f.name));
|
|
5367
|
+
}
|
|
5369
5368
|
const MediaLibraryBrowser = ({ onFetchFiles, filterImageOnly = false, labels, enabled = true, multiple = false, onFileSelect, selectedFile: controlledSelectedFile, onSelectedFileChange, }) => {
|
|
5370
|
-
const [
|
|
5371
|
-
const
|
|
5372
|
-
|
|
5373
|
-
// Use controlled or internal state for selectedFile
|
|
5374
|
-
const selectedFile = controlledSelectedFile ?? internalSelectedFile;
|
|
5375
|
-
const setSelectedFile = onSelectedFileChange ?? setInternalSelectedFile;
|
|
5376
|
-
const { data: filesData, isLoading, isError, } = useQuery({
|
|
5377
|
-
queryKey: ['file-picker-library', searchTerm],
|
|
5369
|
+
const [search, setSearch] = useState('');
|
|
5370
|
+
const query = useQuery({
|
|
5371
|
+
queryKey: ['media-library', search, filterImageOnly],
|
|
5378
5372
|
queryFn: async () => {
|
|
5379
5373
|
if (!onFetchFiles)
|
|
5380
|
-
return
|
|
5381
|
-
const
|
|
5382
|
-
return
|
|
5374
|
+
return [];
|
|
5375
|
+
const list = await onFetchFiles(search);
|
|
5376
|
+
return filterImageOnly ? filterImageFiles(list) : list;
|
|
5383
5377
|
},
|
|
5384
5378
|
enabled: enabled && !!onFetchFiles,
|
|
5385
5379
|
});
|
|
5386
|
-
const files = (
|
|
5387
|
-
const
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
if (multiple
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
else {
|
|
5403
|
-
const newFile = selectedFile === file ? undefined : file;
|
|
5404
|
-
setSelectedFile(newFile);
|
|
5405
|
-
if (onFileSelect && newFile) {
|
|
5406
|
-
onFileSelect(newFile);
|
|
5407
|
-
}
|
|
5380
|
+
const files = useMemo(() => (query.data ?? []), [query.data]);
|
|
5381
|
+
const selectedIds = useMemo(() => {
|
|
5382
|
+
if (multiple && Array.isArray(controlledSelectedFile)) {
|
|
5383
|
+
return new Set(controlledSelectedFile.map((f) => f.id));
|
|
5384
|
+
}
|
|
5385
|
+
if (!multiple &&
|
|
5386
|
+
controlledSelectedFile &&
|
|
5387
|
+
!Array.isArray(controlledSelectedFile)) {
|
|
5388
|
+
return new Set([controlledSelectedFile.id]);
|
|
5389
|
+
}
|
|
5390
|
+
return new Set();
|
|
5391
|
+
}, [multiple, controlledSelectedFile]);
|
|
5392
|
+
const handleSingleSelect = (file) => {
|
|
5393
|
+
if (!multiple) {
|
|
5394
|
+
onSelectedFileChange?.(file);
|
|
5395
|
+
onFileSelect?.(file);
|
|
5408
5396
|
}
|
|
5409
5397
|
};
|
|
5410
|
-
const
|
|
5411
|
-
|
|
5398
|
+
const handleMultipleToggle = (file, checked) => {
|
|
5399
|
+
const current = multiple && Array.isArray(controlledSelectedFile)
|
|
5400
|
+
? [...controlledSelectedFile]
|
|
5401
|
+
: [];
|
|
5402
|
+
const next = checked
|
|
5403
|
+
? [...current, file]
|
|
5404
|
+
: current.filter((f) => f.id !== file.id);
|
|
5405
|
+
onSelectedFileChange?.(next);
|
|
5406
|
+
onFileSelect?.(next);
|
|
5412
5407
|
};
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
:
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
? {
|
|
5445
|
-
base: 'colorPalette.50',
|
|
5446
|
-
_dark: 'colorPalette.900/20',
|
|
5447
|
-
}
|
|
5448
|
-
: 'bg.panel', colorPalette: "blue", cursor: "pointer", onClick: () => handleFileClick(file), _hover: {
|
|
5449
|
-
borderColor: isSelected
|
|
5450
|
-
? {
|
|
5451
|
-
base: 'colorPalette.600',
|
|
5452
|
-
_dark: 'colorPalette.400',
|
|
5453
|
-
}
|
|
5454
|
-
: {
|
|
5455
|
-
base: 'colorPalette.300',
|
|
5456
|
-
_dark: 'colorPalette.400',
|
|
5457
|
-
},
|
|
5458
|
-
bg: isSelected
|
|
5459
|
-
? {
|
|
5460
|
-
base: 'colorPalette.100',
|
|
5461
|
-
_dark: 'colorPalette.800/30',
|
|
5462
|
-
}
|
|
5463
|
-
: 'bg.muted',
|
|
5464
|
-
}, 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 && !imageFailed ? (jsx(Image, { src: file.url, alt: file.name, boxSize: "60px", objectFit: "cover", borderRadius: "md", onError: () => handleImageError(file.id) })) : isImage && (imageFailed || !file.url) ? (jsx(Icon, { as: LuImage, boxSize: 6, color: "fg.muted" })) : (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'
|
|
5465
|
-
? formatBytes(file.size)
|
|
5466
|
-
: 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: {
|
|
5467
|
-
base: 'colorPalette.500',
|
|
5468
|
-
_dark: 'colorPalette.400',
|
|
5469
|
-
}, colorPalette: "blue", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, children: jsx(Text, { color: "white", fontSize: "xs", fontWeight: "bold", children: "\u2713" }) }))] }) }, file.id));
|
|
5470
|
-
}) })) }))] }));
|
|
5408
|
+
const isLoading = query.isPending;
|
|
5409
|
+
const isError = query.isError;
|
|
5410
|
+
const searchPlaceholder = labels?.searchPlaceholder ?? 'Search files...';
|
|
5411
|
+
const loadingText = labels?.loading ?? 'Loading...';
|
|
5412
|
+
const errorText = labels?.loadingFailed ?? 'Failed to load files';
|
|
5413
|
+
const emptyText = labels?.noFilesFound ?? 'No files found';
|
|
5414
|
+
return (jsxs(VStack, { align: "stretch", gap: 4, children: [jsx(InputGroup, { startElement: jsx(Icon, { as: LuSearch, color: "fg.muted" }), children: jsx(Input, { placeholder: searchPlaceholder, value: search, onChange: (e) => setSearch(e.target.value), bg: "bg.panel", borderColor: "border.default" }) }), isLoading && (jsxs(HStack, { gap: 2, py: 6, justify: "center", children: [jsx(Spinner, { size: "sm", colorPalette: "blue" }), jsx(Text, { fontSize: "sm", color: "fg.muted", children: loadingText })] })), isError && (jsx(Box, { py: 4, px: 3, borderRadius: "md", bg: { base: 'red.50', _dark: 'red.900/20' }, borderWidth: "1px", borderColor: { base: 'red.200', _dark: 'red.800' }, children: jsx(Text, { fontSize: "sm", color: { base: 'red.600', _dark: 'red.300' }, children: errorText }) })), !isLoading && !isError && files.length === 0 && (jsx(Box, { py: 6, textAlign: "center", children: jsx(Text, { fontSize: "sm", color: "fg.muted", children: emptyText }) })), !isLoading && !isError && files.length > 0 && (jsx(SimpleGrid, { columns: { base: 2, sm: 3, md: 4 }, gap: 3, children: files.map((file) => {
|
|
5415
|
+
const isImage = IMAGE_EXT.test(file.name);
|
|
5416
|
+
const isSelected = selectedIds.has(file.id);
|
|
5417
|
+
const fileSize = typeof file.size === 'number'
|
|
5418
|
+
? formatBytes(file.size)
|
|
5419
|
+
: file.size ?? null;
|
|
5420
|
+
if (multiple) {
|
|
5421
|
+
return (jsxs(CheckboxCard, { checked: isSelected, onCheckedChange: (e) => handleMultipleToggle(file, e.checked === true), variant: "outline", borderColor: "border.default", _hover: { borderColor: 'border.emphasized', bg: 'bg.muted' }, cursor: "pointer", children: [jsx(Box, { width: "100%", aspectRatio: 1, bg: "bg.muted", borderRadius: "md", overflow: "hidden", mb: 2, display: "flex", alignItems: "center", justifyContent: "center", children: isImage && file.url ? (jsx(Image, { src: file.url, alt: file.name, width: "100%", height: "100%", objectFit: "cover" })) : isImage ? (jsx(Icon, { as: LuImage, boxSize: 8, color: "fg.muted" })) : (jsx(Icon, { as: LuFile, boxSize: 8, color: "fg.muted" })) }), jsx(Text, { fontSize: "xs", fontWeight: "medium", color: "fg.default", lineClamp: 2, children: file.name }), fileSize && (jsx(Text, { fontSize: "xs", color: "fg.muted", children: fileSize }))] }, file.id));
|
|
5422
|
+
}
|
|
5423
|
+
return (jsxs(Box, { role: "button", tabIndex: 0, onClick: () => handleSingleSelect(file), onKeyDown: (e) => {
|
|
5424
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
5425
|
+
e.preventDefault();
|
|
5426
|
+
handleSingleSelect(file);
|
|
5427
|
+
}
|
|
5428
|
+
}, padding: 3, borderRadius: "md", borderWidth: "2px", borderColor: isSelected ? 'colorPalette.500' : 'border.default', bg: isSelected
|
|
5429
|
+
? { base: 'colorPalette.50', _dark: 'colorPalette.900/20' }
|
|
5430
|
+
: 'bg.panel', _hover: {
|
|
5431
|
+
borderColor: isSelected
|
|
5432
|
+
? 'colorPalette.500'
|
|
5433
|
+
: 'border.emphasized',
|
|
5434
|
+
bg: isSelected
|
|
5435
|
+
? { base: 'colorPalette.50', _dark: 'colorPalette.900/20' }
|
|
5436
|
+
: 'bg.muted',
|
|
5437
|
+
}, cursor: "pointer", transition: "all 0.2s", children: [jsx(Box, { width: "100%", aspectRatio: 1, bg: "bg.muted", borderRadius: "md", overflow: "hidden", mb: 2, display: "flex", alignItems: "center", justifyContent: "center", children: isImage && file.url ? (jsx(Image, { src: file.url, alt: file.name, width: "100%", height: "100%", objectFit: "cover" })) : isImage ? (jsx(Icon, { as: LuImage, boxSize: 8, color: "fg.muted" })) : (jsx(Icon, { as: LuFile, boxSize: 8, color: "fg.muted" })) }), jsx(Text, { fontSize: "xs", fontWeight: "medium", color: "fg.default", lineClamp: 2, children: file.name }), fileSize && (jsx(Text, { fontSize: "xs", color: "fg.muted", children: fileSize }))] }, file.id));
|
|
5438
|
+
}) }))] }));
|
|
5471
5439
|
};
|
|
5472
5440
|
|
|
5473
5441
|
function MediaBrowserDialog({ open, onClose, onSelect, title, filterImageOnly = false, onFetchFiles, onUploadFile, enableUpload = false, labels, }) {
|
|
@@ -5635,7 +5603,8 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
|
|
|
5635
5603
|
const { required, gridColumn = 'span 12', gridRow = 'span 1', filePicker, type, } = schema;
|
|
5636
5604
|
const isRequired = required?.some((columnId) => columnId === column);
|
|
5637
5605
|
const isSingleSelect = type === 'string';
|
|
5638
|
-
const
|
|
5606
|
+
const colLabel = formI18n.colLabel;
|
|
5607
|
+
const currentValue = watch(colLabel) ?? (isSingleSelect ? '' : []);
|
|
5639
5608
|
// Handle string IDs only
|
|
5640
5609
|
const currentFileIds = isSingleSelect
|
|
5641
5610
|
? currentValue
|
|
@@ -5644,7 +5613,6 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
|
|
|
5644
5613
|
: Array.isArray(currentValue)
|
|
5645
5614
|
? currentValue
|
|
5646
5615
|
: [];
|
|
5647
|
-
const colLabel = formI18n.colLabel;
|
|
5648
5616
|
const fieldError = getNestedError(errors, colLabel);
|
|
5649
5617
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
5650
5618
|
const [failedImageIds, setFailedImageIds] = useState(new Set());
|
|
@@ -5732,7 +5700,7 @@ const FormMediaLibraryBrowser = ({ column, schema, prefix, }) => {
|
|
|
5732
5700
|
: /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(fileId);
|
|
5733
5701
|
const imageFailed = failedImageIds.has(fileId);
|
|
5734
5702
|
const displayName = file?.name ?? fileId;
|
|
5735
|
-
return (jsx(Card.Root, { variant: 'subtle',
|
|
5703
|
+
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: {
|
|
5736
5704
|
borderColor: 'colorPalette.300',
|
|
5737
5705
|
bg: 'bg.muted',
|
|
5738
5706
|
}, 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", overflow: "hidden", children: isImage && file?.url && !imageFailed ? (jsx(Image, { src: file.url, alt: displayName, boxSize: "60px", objectFit: "cover", onError: () => handleImageError(fileId) })) : isImage && !imageFailed ? (jsx(Icon, { as: LuImage, boxSize: 6, color: "fg.muted" })) : (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: displayName }), file?.size && (jsx(Text, { fontSize: "xs", color: "fg.muted", children: typeof file.size === 'number'
|
|
@@ -7828,66 +7796,24 @@ const ColumnRenderer = ({ column, properties, prefix, parentRequired, }) => {
|
|
|
7828
7796
|
return jsx(SchemaRenderer, { schema: schemaWithRequired, prefix, column });
|
|
7829
7797
|
};
|
|
7830
7798
|
|
|
7831
|
-
const SubmitButton = () => {
|
|
7832
|
-
const { onSubmit, formButtonLabels } = useSchemaContext();
|
|
7833
|
-
const methods = useFormContext();
|
|
7834
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7835
|
-
const onValid = async (data) => {
|
|
7836
|
-
// Validation is handled by react-hook-form
|
|
7837
|
-
// This function will only be called if validation passes
|
|
7838
|
-
if (onSubmit) {
|
|
7839
|
-
await onSubmit(data);
|
|
7840
|
-
}
|
|
7841
|
-
};
|
|
7842
|
-
return (jsx(Button$1, { onClick: () => {
|
|
7843
|
-
methods.handleSubmit(onValid)();
|
|
7844
|
-
}, formNoValidate: true, children: formButtonLabels?.submit ?? 'Submit' }));
|
|
7845
|
-
};
|
|
7846
|
-
|
|
7847
7799
|
const FormBody = () => {
|
|
7848
|
-
const { schema
|
|
7849
|
-
const { showSubmitButton, showResetButton } = displayConfig;
|
|
7850
|
-
const methods = useFormContext();
|
|
7851
|
-
console.log('errors', methods.formState.errors);
|
|
7800
|
+
const { schema } = useSchemaContext();
|
|
7852
7801
|
const { properties } = schema;
|
|
7853
7802
|
const ordered = Object.keys(properties);
|
|
7854
|
-
return (
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
methods.reset();
|
|
7862
|
-
}, variant: 'subtle', children: formButtonLabels?.reset ?? 'Reset' })), showSubmitButton && jsx(SubmitButton, {})] }))] }));
|
|
7863
|
-
};
|
|
7864
|
-
|
|
7865
|
-
const FormTitle = () => {
|
|
7866
|
-
const { schema } = useSchemaContext();
|
|
7867
|
-
// Debug log when form title is missing
|
|
7868
|
-
if (!schema.title) {
|
|
7869
|
-
console.debug('[Form Title] Missing title in root schema. Add title property to schema.', {
|
|
7870
|
-
schema: {
|
|
7871
|
-
type: schema.type,
|
|
7872
|
-
properties: schema.properties
|
|
7873
|
-
? Object.keys(schema.properties)
|
|
7874
|
-
: undefined,
|
|
7875
|
-
},
|
|
7876
|
-
});
|
|
7877
|
-
}
|
|
7878
|
-
return jsx(Heading, { children: schema.title ?? 'Form' });
|
|
7803
|
+
return (jsx(Flex, { flexFlow: 'column', gap: "2", children: jsx(Grid, { gap: "4", gridTemplateColumns: 'repeat(12, 1fr)', autoFlow: 'row', children: ordered.map((column) => {
|
|
7804
|
+
if (!properties) {
|
|
7805
|
+
console.error('properties is undefined when using FormBody', schema);
|
|
7806
|
+
return null;
|
|
7807
|
+
}
|
|
7808
|
+
return (jsx(ColumnRenderer, { properties: properties, prefix: ``, parentRequired: schema.required, column }, `form-input-${column}`));
|
|
7809
|
+
}) }) }));
|
|
7879
7810
|
};
|
|
7880
7811
|
|
|
7881
|
-
const FormRoot = ({ schema, idMap, setIdMap, form, children,
|
|
7882
|
-
showSubmitButton: false,
|
|
7883
|
-
showResetButton: false,
|
|
7884
|
-
showTitle: true,
|
|
7885
|
-
}, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, formButtonLabels, timePickerLabels, insideDialog = false, }) => {
|
|
7812
|
+
const FormRoot = ({ schema, idMap, setIdMap, form, children, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, formButtonLabels, timePickerLabels, insideDialog = false, }) => {
|
|
7886
7813
|
return (jsx(SchemaFormContext.Provider, { value: {
|
|
7887
7814
|
schema,
|
|
7888
7815
|
idMap,
|
|
7889
7816
|
setIdMap,
|
|
7890
|
-
displayConfig,
|
|
7891
7817
|
dateTimePickerLabels,
|
|
7892
7818
|
idPickerLabels,
|
|
7893
7819
|
enumPickerLabels,
|
|
@@ -7899,8 +7825,7 @@ const FormRoot = ({ schema, idMap, setIdMap, form, children, displayConfig = {
|
|
|
7899
7825
|
};
|
|
7900
7826
|
|
|
7901
7827
|
const DefaultForm = ({ formConfig, }) => {
|
|
7902
|
-
|
|
7903
|
-
return (jsx(FormRoot, { ...formConfig, children: jsxs(Grid, { gap: "2", children: [showTitle && jsx(FormTitle, {}), jsx(FormBody, {})] }) }));
|
|
7828
|
+
return (jsx(FormRoot, { ...formConfig, children: jsx(Grid, { gap: "2", children: jsx(FormBody, {}) }) }));
|
|
7904
7829
|
};
|
|
7905
7830
|
|
|
7906
7831
|
function useForm({ preLoadedValues, schema, }) {
|
|
@@ -9462,4 +9387,4 @@ function DataTableServer({ columns, enableRowSelection = true, enableMultiRowSel
|
|
|
9462
9387
|
}, children: jsx(DataTableServerContext.Provider, { value: { url: url ?? '', query }, children: children }) }));
|
|
9463
9388
|
}
|
|
9464
9389
|
|
|
9465
|
-
export { CalendarDisplay, CardHeader, DataDisplay, DataTable, DataTableServer, DatePickerContext, DatePickerInput, DefaultCardTitle, DefaultForm, DefaultTable, DefaultTableServer, DensityToggleButton, EditSortingButton, EmptyState, ErrorAlert, FilterDialog, FormBody, FormRoot,
|
|
9390
|
+
export { CalendarDisplay, CardHeader, DataDisplay, DataTable, DataTableServer, DatePickerContext, DatePickerInput, DefaultCardTitle, DefaultForm, DefaultTable, DefaultTableServer, DensityToggleButton, EditSortingButton, EmptyState, ErrorAlert, FilterDialog, FormBody, FormRoot, GlobalFilter, MediaLibraryBrowser, PageSizeControl, Pagination, RecordDisplay, ReloadButton, ResetFilteringButton, ResetSelectionButton, ResetSortingButton, RowCountText, SelectAllRowsToggle, Table, TableBody, TableCardContainer, TableCards, TableComponent, TableControls, TableDataDisplay, TableFilter, TableFilterTags, TableFooter, TableHeader, TableLoadingComponent, TableSelector, TableSorter, TableViewer, TextCell, TimeRangeZoom, TimeViewportBlock, TimeViewportBlocks, TimeViewportGrid, TimeViewportHeader, TimeViewportMarkerLine, TimeViewportRoot, ViewDialog, defaultRenderDisplay, getMultiDates, getRangeDates, useDataTable, useDataTableContext, useDataTableServer, useForm, useTimeRangeZoom, useTimeViewport, useTimeViewportBlockGeometry, useTimeViewportHeader, useTimeViewportTicks };
|
|
@@ -7,11 +7,6 @@ export interface SchemaFormContext<TData extends FieldValues> {
|
|
|
7
7
|
idMap: Record<string, unknown>;
|
|
8
8
|
setIdMap: Dispatch<SetStateAction<Record<string, unknown>>>;
|
|
9
9
|
timezone?: string;
|
|
10
|
-
displayConfig: {
|
|
11
|
-
showSubmitButton?: boolean;
|
|
12
|
-
showResetButton?: boolean;
|
|
13
|
-
showTitle?: boolean;
|
|
14
|
-
};
|
|
15
10
|
dateTimePickerLabels?: DateTimePickerLabels;
|
|
16
11
|
idPickerLabels?: IdPickerLabels;
|
|
17
12
|
enumPickerLabels?: EnumPickerLabels;
|
|
@@ -18,5 +18,5 @@ type MediaLibraryBrowserPropsMultiple = MediaLibraryBrowserPropsBase & {
|
|
|
18
18
|
onSelectedFileChange?: (files: FilePickerMediaFile[]) => void;
|
|
19
19
|
};
|
|
20
20
|
export type MediaLibraryBrowserProps = MediaLibraryBrowserPropsSingle | MediaLibraryBrowserPropsMultiple;
|
|
21
|
-
export declare const MediaLibraryBrowser: ({ onFetchFiles, filterImageOnly, labels, enabled, multiple, onFileSelect, selectedFile: controlledSelectedFile, onSelectedFileChange, }: MediaLibraryBrowserProps) => import("react/jsx-runtime").JSX.Element
|
|
21
|
+
export declare const MediaLibraryBrowser: ({ onFetchFiles, filterImageOnly, labels, enabled, multiple, onFileSelect, selectedFile: controlledSelectedFile, onSelectedFileChange, }: MediaLibraryBrowserProps) => import("react/jsx-runtime").JSX.Element;
|
|
22
22
|
export {};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { FormRootProps } from
|
|
2
|
-
import { FieldValues } from
|
|
1
|
+
import { FormRootProps } from './FormRoot';
|
|
2
|
+
import { FieldValues } from 'react-hook-form';
|
|
3
3
|
export interface DefaultFormProps<TData extends FieldValues> {
|
|
4
|
-
formConfig: Omit<FormRootProps<TData>,
|
|
5
|
-
showTitle?: boolean;
|
|
4
|
+
formConfig: Omit<FormRootProps<TData>, 'children'>;
|
|
6
5
|
}
|
|
7
6
|
export declare const DefaultForm: <TData extends FieldValues>({ formConfig, }: DefaultFormProps<TData>) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -33,11 +33,6 @@ export interface FormRootProps<TData extends FieldValues> {
|
|
|
33
33
|
setIdMap: Dispatch<SetStateAction<Record<string, unknown>>>;
|
|
34
34
|
form: UseFormReturn<TData, any, TData>;
|
|
35
35
|
children: ReactNode;
|
|
36
|
-
displayConfig?: {
|
|
37
|
-
showSubmitButton?: boolean;
|
|
38
|
-
showResetButton?: boolean;
|
|
39
|
-
showTitle?: boolean;
|
|
40
|
-
};
|
|
41
36
|
dateTimePickerLabels?: DateTimePickerLabels;
|
|
42
37
|
idPickerLabels?: IdPickerLabels;
|
|
43
38
|
enumPickerLabels?: EnumPickerLabels;
|
|
@@ -53,4 +48,4 @@ export interface CustomJSONSchema7Definition extends JSONSchema7 {
|
|
|
53
48
|
customQueryFn: any;
|
|
54
49
|
children: ReactNode;
|
|
55
50
|
}
|
|
56
|
-
export declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, children,
|
|
51
|
+
export declare const FormRoot: <TData extends FieldValues>({ schema, idMap, setIdMap, form, children, dateTimePickerLabels, idPickerLabels, enumPickerLabels, filePickerLabels, formButtonLabels, timePickerLabels, insideDialog, }: FormRootProps<TData>) => import("react/jsx-runtime").JSX.Element;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -125,7 +125,6 @@ export * from './components/DataTable/useDataTableServer';
|
|
|
125
125
|
export * from './components/DataTable/context/useDataTableContext';
|
|
126
126
|
export * from './components/Form/components/core/DefaultForm';
|
|
127
127
|
export * from './components/Form/components/core/FormRoot';
|
|
128
|
-
export * from './components/Form/components/core/FormTitle';
|
|
129
128
|
export * from './components/Form/components/core/FormBody';
|
|
130
129
|
export * from './components/Form/components/types/CustomJSONSchema7';
|
|
131
130
|
export * from './components/Form/components/MediaLibraryBrowser';
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const FormTitle: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const SubmitButton: () => import("react/jsx-runtime").JSX.Element;
|