@firecms/core 3.1.0-canary.9e89e98 → 3.1.0
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/components/EntityCollectionTable/internal/popup_field/useDraggable.d.ts +2 -2
- package/dist/components/ErrorBoundary.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableHeader.d.ts +1 -1
- package/dist/form/components/ErrorFocus.d.ts +1 -1
- package/dist/index.es.js +118 -54
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +118 -54
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useRestoreScroll.d.ts +1 -1
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/plugins.d.ts +16 -0
- package/dist/util/entities.d.ts +1 -1
- package/dist/util/resolutions.d.ts +2 -2
- package/package.json +9 -9
- package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +11 -11
- package/src/components/EntityCollectionView/EntityBoardCard.tsx +1 -1
- package/src/components/EntityCollectionView/EntityCard.tsx +4 -0
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +23 -3
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +32 -3
- package/src/components/VirtualTable/VirtualTable.tsx +116 -113
- package/src/components/VirtualTable/VirtualTableHeader.tsx +42 -42
- package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +1 -1
- package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +3 -3
- package/src/core/DefaultAppBar.tsx +1 -1
- package/src/core/EntitySidePanel.tsx +28 -26
- package/src/core/field_configs.tsx +14 -9
- package/src/form/EntityForm.tsx +69 -60
- package/src/form/PropertyFieldBinding.tsx +3 -3
- package/src/form/components/ErrorFocus.tsx +3 -3
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +1 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +83 -83
- package/src/hooks/useBuildNavigationController.tsx +4 -4
- package/src/hooks/useValidateAuthenticator.tsx +1 -1
- package/src/internal/useBuildDataSource.ts +1 -2
- package/src/preview/PropertyPreview.tsx +1 -0
- package/src/types/analytics.ts +10 -0
- package/src/types/plugins.tsx +18 -0
- package/src/util/entities.ts +1 -1
- package/src/util/join_collections.ts +10 -8
- package/src/util/previews.ts +2 -2
- package/src/util/property_utils.tsx +1 -1
- package/src/util/resolutions.ts +5 -3
package/src/form/EntityForm.tsx
CHANGED
|
@@ -181,30 +181,30 @@ export function getChanges<T extends object>(source: Partial<T>, comparison: Par
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
export function EntityForm<M extends Record<string, any>>({
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
184
|
+
path,
|
|
185
|
+
fullIdPath,
|
|
186
|
+
entityId: entityIdProp,
|
|
187
|
+
collection,
|
|
188
|
+
onValuesModified,
|
|
189
|
+
onIdChange,
|
|
190
|
+
onSaved,
|
|
191
|
+
entity,
|
|
192
|
+
initialDirtyValues,
|
|
193
|
+
onFormContextReady,
|
|
194
|
+
forceActionsAtTheBottom,
|
|
195
|
+
initialStatus,
|
|
196
|
+
className,
|
|
197
|
+
onStatusChange,
|
|
198
|
+
onEntityChange,
|
|
199
|
+
openEntityMode = "full_screen",
|
|
200
|
+
formex: formexProp,
|
|
201
|
+
disabled: disabledProp,
|
|
202
|
+
Builder,
|
|
203
|
+
EntityFormActionsComponent = EntityFormActions,
|
|
204
|
+
showDefaultActions = true,
|
|
205
|
+
showEntityPath = true,
|
|
206
|
+
children
|
|
207
|
+
}: EntityFormProps<M>) {
|
|
208
208
|
|
|
209
209
|
if (collection.customId && collection.formAutoSave) {
|
|
210
210
|
console.warn(`The collection ${collection.path} has customId and formAutoSave enabled. This is not supported and formAutoSave will be ignored`);
|
|
@@ -455,12 +455,12 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
455
455
|
}, [entityId, path, snackbarController]);
|
|
456
456
|
|
|
457
457
|
const saveEntity = ({
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
458
|
+
values,
|
|
459
|
+
previousValues,
|
|
460
|
+
entityId,
|
|
461
|
+
collection,
|
|
462
|
+
path
|
|
463
|
+
}: {
|
|
464
464
|
collection: EntityCollection<M>,
|
|
465
465
|
path: string,
|
|
466
466
|
entityId: string | undefined,
|
|
@@ -493,13 +493,13 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
493
493
|
};
|
|
494
494
|
|
|
495
495
|
const onSaveEntityRequest = async ({
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
496
|
+
collection,
|
|
497
|
+
path,
|
|
498
|
+
entityId,
|
|
499
|
+
values,
|
|
500
|
+
previousValues,
|
|
501
|
+
autoSave
|
|
502
|
+
}: EntityFormSaveParams<M>): Promise<void> => {
|
|
503
503
|
if (!status)
|
|
504
504
|
return;
|
|
505
505
|
if (autoSave) {
|
|
@@ -567,6 +567,7 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
567
567
|
}, [snackbarController]);
|
|
568
568
|
|
|
569
569
|
const pluginActions: React.ReactNode[] = [];
|
|
570
|
+
const pluginBeforeTitle: React.ReactNode[] = [];
|
|
570
571
|
const plugins = customizationController.plugins;
|
|
571
572
|
|
|
572
573
|
const actionsDisabled = disabled || formex.isSubmitting || (status === "existing" && !formex.dirty) || Boolean(disabledProp);
|
|
@@ -590,6 +591,12 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
590
591
|
key={`actions_${plugin.key}`} {...actionProps} />
|
|
591
592
|
: null
|
|
592
593
|
)).filter(Boolean));
|
|
594
|
+
pluginBeforeTitle.push(...plugins.map((plugin) => (
|
|
595
|
+
plugin.form?.BeforeTitle
|
|
596
|
+
? <plugin.form.BeforeTitle
|
|
597
|
+
key={`before_title_${plugin.key}`} {...actionProps} />
|
|
598
|
+
: null
|
|
599
|
+
)).filter(Boolean));
|
|
593
600
|
}
|
|
594
601
|
|
|
595
602
|
const titlePropertyKey = getEntityTitlePropertyKey(resolvedCollection, customizationController.propertyConfigs);
|
|
@@ -631,17 +638,17 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
631
638
|
const modified = formex.dirty;
|
|
632
639
|
|
|
633
640
|
const uniqueFieldValidator: CustomFieldValidator = useCallback(({
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
641
|
+
name,
|
|
642
|
+
value
|
|
643
|
+
}) => dataSource.checkUniqueField(path, name, value, entityId, collection),
|
|
637
644
|
[dataSource, path, entityId]);
|
|
638
645
|
|
|
639
646
|
const validationSchema = useMemo(() => entityId
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
647
|
+
? getYupEntitySchema(
|
|
648
|
+
entityId,
|
|
649
|
+
resolvedCollection.properties,
|
|
650
|
+
uniqueFieldValidator)
|
|
651
|
+
: undefined,
|
|
645
652
|
[entityId, resolvedCollection.properties, uniqueFieldValidator]);
|
|
646
653
|
|
|
647
654
|
useOnAutoSave(autoSave, formex, lastSavedValues, save);
|
|
@@ -699,8 +706,8 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
699
706
|
|
|
700
707
|
return (
|
|
701
708
|
<FormEntry propertyKey={key}
|
|
702
|
-
|
|
703
|
-
|
|
709
|
+
widthPercentage={widthPercentage}
|
|
710
|
+
key={`field_${key}`}>
|
|
704
711
|
<PropertyFieldBinding {...cmsFormFieldProps} />
|
|
705
712
|
</FormEntry>
|
|
706
713
|
);
|
|
@@ -713,7 +720,7 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
713
720
|
throw new Error("When using additional fields you need to provide a Builder or a value");
|
|
714
721
|
}
|
|
715
722
|
const child = Builder
|
|
716
|
-
? <Builder entity={entity} context={context}/>
|
|
723
|
+
? <Builder entity={entity} context={context} />
|
|
717
724
|
: <div className={"w-full"}>
|
|
718
725
|
{additionalField.value?.({
|
|
719
726
|
entity,
|
|
@@ -725,9 +732,9 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
725
732
|
<div key={`additional_${key}`} className={"w-full"}>
|
|
726
733
|
<LabelWithIconAndTooltip
|
|
727
734
|
propertyKey={key}
|
|
728
|
-
icon={<NotesIcon size={"small"}/>}
|
|
735
|
+
icon={<NotesIcon size={"small"} />}
|
|
729
736
|
title={additionalField.name}
|
|
730
|
-
className={"text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>
|
|
737
|
+
className={"text-text-secondary dark:text-text-secondary-dark ml-3.5"} />
|
|
731
738
|
<div
|
|
732
739
|
className={cls(paperMixin, "w-full min-h-14 p-4 md:p-6 overflow-x-scroll no-scrollbar")}>
|
|
733
740
|
<ErrorBoundary>
|
|
@@ -749,6 +756,8 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
749
756
|
|
|
750
757
|
const formView = <ErrorBoundary>
|
|
751
758
|
<>
|
|
759
|
+
{pluginBeforeTitle}
|
|
760
|
+
|
|
752
761
|
{!Builder && <div className={"w-full py-2 flex flex-col items-start my-4 lg:my-6"}>
|
|
753
762
|
<Typography
|
|
754
763
|
className={"my-4 flex-grow line-clamp-1 " + (collection.hideIdFromForm ? "mb-6" : "")}
|
|
@@ -777,22 +786,22 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
777
786
|
|
|
778
787
|
{!Builder && !collection.hideIdFromForm &&
|
|
779
788
|
<CustomIdField customId={collection.customId}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
789
|
+
entityId={entityId}
|
|
790
|
+
status={status}
|
|
791
|
+
onChange={setEntityId}
|
|
792
|
+
error={entityIdError}
|
|
793
|
+
loading={customIdLoading}
|
|
794
|
+
entity={entity} />
|
|
786
795
|
}
|
|
787
796
|
|
|
788
797
|
{entityId && formContext && <>
|
|
789
798
|
<div className="mt-12 flex flex-col gap-8" ref={formRef}>
|
|
790
799
|
{formFields()}
|
|
791
|
-
<ErrorFocus containerRef={formRef}/>
|
|
800
|
+
<ErrorFocus containerRef={formRef} />
|
|
792
801
|
</div>
|
|
793
802
|
</>}
|
|
794
803
|
|
|
795
|
-
{forceActionsAtTheBottom && <div className="h-16"/>}
|
|
804
|
+
{forceActionsAtTheBottom && <div className="h-16" />}
|
|
796
805
|
</>
|
|
797
806
|
</ErrorBoundary>;
|
|
798
807
|
|
|
@@ -852,12 +861,12 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
852
861
|
{formex.dirty
|
|
853
862
|
? <Tooltip title={"This form has been modified"}>
|
|
854
863
|
<Chip size={"small"} className={"py-1"} colorScheme={"orangeDarker"}>
|
|
855
|
-
<EditIcon size={"smallest"}/>
|
|
864
|
+
<EditIcon size={"smallest"} />
|
|
856
865
|
</Chip>
|
|
857
866
|
</Tooltip>
|
|
858
867
|
: <Tooltip title={"The current form is in sync with the database"}>
|
|
859
868
|
<Chip size={"small"} className={"py-1"}>
|
|
860
|
-
<CheckIcon size={"smallest"}/>
|
|
869
|
+
<CheckIcon size={"smallest"} />
|
|
861
870
|
</Chip>
|
|
862
871
|
</Tooltip>}
|
|
863
872
|
</div>
|
|
@@ -137,7 +137,7 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, M extends Rec
|
|
|
137
137
|
}
|
|
138
138
|
const configProperty = resolveProperty({
|
|
139
139
|
propertyKey,
|
|
140
|
-
propertyOrBuilder: propertyConfig.property,
|
|
140
|
+
propertyOrBuilder: propertyConfig.property as any,
|
|
141
141
|
values: fieldProps.form.values,
|
|
142
142
|
path: context.path,
|
|
143
143
|
entityId: context.entityId,
|
|
@@ -145,7 +145,7 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, M extends Rec
|
|
|
145
145
|
index,
|
|
146
146
|
authController
|
|
147
147
|
});
|
|
148
|
-
Component = configProperty
|
|
148
|
+
Component = configProperty?.Field as ComponentType<FieldProps<T>> | undefined;
|
|
149
149
|
}
|
|
150
150
|
if (!Component) {
|
|
151
151
|
console.warn(`No field component found for property ${propertyKey}`);
|
|
@@ -302,7 +302,7 @@ const shouldPropertyReRender = (property: PropertyOrBuilder | ResolvedProperty,
|
|
|
302
302
|
if (plugins?.some((plugin) => plugin.form?.fieldBuilder)) {
|
|
303
303
|
return true;
|
|
304
304
|
}
|
|
305
|
-
if (isPropertyBuilder(property)) {
|
|
305
|
+
if (isPropertyBuilder(property as any)) {
|
|
306
306
|
return true;
|
|
307
307
|
}
|
|
308
308
|
const defAProperty = property as Property | ResolvedProperty;
|
|
@@ -2,9 +2,9 @@ import React, { useEffect, useRef } from "react";
|
|
|
2
2
|
import { useFormex } from "@firecms/formex";
|
|
3
3
|
|
|
4
4
|
export const ErrorFocus = ({ containerRef }:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
{
|
|
6
|
+
containerRef?: React.RefObject<HTMLDivElement | null>
|
|
7
|
+
}) => {
|
|
8
8
|
const {
|
|
9
9
|
isValidating,
|
|
10
10
|
errors,
|
|
@@ -65,7 +65,7 @@ export function MarkdownEditorFieldBinding({
|
|
|
65
65
|
}, [value]);
|
|
66
66
|
|
|
67
67
|
const resolvedProperty = resolveProperty({
|
|
68
|
-
propertyOrBuilder: property as PropertyOrBuilder
|
|
68
|
+
propertyOrBuilder: property as PropertyOrBuilder<string>,
|
|
69
69
|
values: entityValues,
|
|
70
70
|
authController
|
|
71
71
|
}) as ResolvedStringProperty | ResolvedArrayProperty<string[]>;
|
|
@@ -52,18 +52,18 @@ const rejectDropClasses = "transition-colors duration-200 ease-[cubic-bezier(0,0
|
|
|
52
52
|
type StorageUploadFieldProps = FieldProps<string | string[]>;
|
|
53
53
|
|
|
54
54
|
export function StorageUploadFieldBinding({
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
55
|
+
propertyKey,
|
|
56
|
+
value,
|
|
57
|
+
setValue,
|
|
58
|
+
error,
|
|
59
|
+
showError,
|
|
60
|
+
autoFocus,
|
|
61
|
+
minimalistView,
|
|
62
|
+
property,
|
|
63
|
+
includeDescription,
|
|
64
|
+
context,
|
|
65
|
+
isSubmitting,
|
|
66
|
+
}: StorageUploadFieldProps) {
|
|
67
67
|
|
|
68
68
|
const authController = useAuthController();
|
|
69
69
|
|
|
@@ -100,7 +100,7 @@ export function StorageUploadFieldBinding({
|
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
const resolvedProperty = resolveProperty({
|
|
103
|
-
propertyOrBuilder: property as PropertyOrBuilder
|
|
103
|
+
propertyOrBuilder: property as PropertyOrBuilder<string>,
|
|
104
104
|
authController
|
|
105
105
|
}) as ResolvedStringProperty | ResolvedArrayProperty<string[]>;
|
|
106
106
|
|
|
@@ -114,7 +114,7 @@ export function StorageUploadFieldBinding({
|
|
|
114
114
|
icon={getIconForProperty(property, "small")}
|
|
115
115
|
required={property.validation?.required}
|
|
116
116
|
title={property.name}
|
|
117
|
-
className={"h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>}
|
|
117
|
+
className={"h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5"} />}
|
|
118
118
|
|
|
119
119
|
<StorageUpload
|
|
120
120
|
value={internalValue}
|
|
@@ -128,13 +128,13 @@ export function StorageUploadFieldBinding({
|
|
|
128
128
|
onFileUploadComplete={onFileUploadComplete}
|
|
129
129
|
storagePathBuilder={storagePathBuilder}
|
|
130
130
|
storage={storage}
|
|
131
|
-
multipleFilesSupported={multipleFilesSupported}/>
|
|
131
|
+
multipleFilesSupported={multipleFilesSupported} />
|
|
132
132
|
|
|
133
133
|
<FieldHelperText includeDescription={includeDescription}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
showError={showError}
|
|
135
|
+
error={error}
|
|
136
|
+
disabled={disabled}
|
|
137
|
+
property={property} />
|
|
138
138
|
|
|
139
139
|
</>
|
|
140
140
|
);
|
|
@@ -154,15 +154,15 @@ interface SortableStorageItemProps {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
function SortableStorageItem({
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
157
|
+
id,
|
|
158
|
+
entry,
|
|
159
|
+
property,
|
|
160
|
+
metadata,
|
|
161
|
+
storagePathBuilder,
|
|
162
|
+
onFileUploadComplete,
|
|
163
|
+
onClear,
|
|
164
|
+
disabled,
|
|
165
|
+
}: SortableStorageItemProps) {
|
|
166
166
|
|
|
167
167
|
const {
|
|
168
168
|
attributes,
|
|
@@ -201,7 +201,7 @@ function SortableStorageItem({
|
|
|
201
201
|
disabled={disabled}
|
|
202
202
|
value={entry.storagePathOrDownloadUrl}
|
|
203
203
|
onRemove={() => onClear(entry.storagePathOrDownloadUrl!)}
|
|
204
|
-
size={entry.size}/>
|
|
204
|
+
size={entry.size} />
|
|
205
205
|
);
|
|
206
206
|
} else if (entry.file) {
|
|
207
207
|
child = (
|
|
@@ -231,21 +231,21 @@ function SortableStorageItem({
|
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
function FileDropComponent({
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
234
|
+
storage,
|
|
235
|
+
disabled,
|
|
236
|
+
onFilesAdded,
|
|
237
|
+
multipleFilesSupported,
|
|
238
|
+
autoFocus,
|
|
239
|
+
internalValue,
|
|
240
|
+
property,
|
|
241
|
+
onClear,
|
|
242
|
+
metadata,
|
|
243
|
+
storagePathBuilder,
|
|
244
|
+
onFileUploadComplete,
|
|
245
|
+
name,
|
|
246
|
+
helpText,
|
|
247
|
+
isDndItemDragging
|
|
248
|
+
}: {
|
|
249
249
|
storage: StorageConfig,
|
|
250
250
|
disabled: boolean,
|
|
251
251
|
onFilesAdded: (acceptedFiles: File[]) => Promise<void>,
|
|
@@ -271,33 +271,33 @@ function FileDropComponent({
|
|
|
271
271
|
isDragAccept,
|
|
272
272
|
isDragReject
|
|
273
273
|
} = useDropzone({
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
274
|
+
accept: storage.acceptedFiles ? storage.acceptedFiles.reduce((acc, ext) => ({
|
|
275
|
+
...acc,
|
|
276
|
+
[ext]: []
|
|
277
|
+
}), {}) : undefined,
|
|
278
|
+
disabled: disabled || isDndItemDragging,
|
|
279
|
+
noDragEventsBubbling: true,
|
|
280
|
+
maxSize: storage.maxSize,
|
|
281
|
+
onDrop: onFilesAdded,
|
|
282
|
+
onDropRejected: (fileRejections) => {
|
|
283
|
+
for (const fileRejection of fileRejections) {
|
|
284
|
+
for (const error of fileRejection.errors) {
|
|
285
|
+
console.error("Error uploading file: ", error);
|
|
286
|
+
if (error.code === "file-too-large") {
|
|
287
|
+
snackbarContext.open({
|
|
288
|
+
type: "error",
|
|
289
|
+
message: `Error uploading file: File is larger than ${storage.maxSize} bytes`
|
|
290
|
+
});
|
|
291
|
+
} else if (error.code === "file-invalid-type") {
|
|
292
|
+
snackbarContext.open({
|
|
293
|
+
type: "error",
|
|
294
|
+
message: "Error uploading file: File type is not supported"
|
|
295
|
+
});
|
|
297
296
|
}
|
|
298
297
|
}
|
|
299
298
|
}
|
|
300
299
|
}
|
|
300
|
+
}
|
|
301
301
|
);
|
|
302
302
|
|
|
303
303
|
return (
|
|
@@ -349,8 +349,8 @@ function FileDropComponent({
|
|
|
349
349
|
<div
|
|
350
350
|
className="flex-grow min-h-[38px] box-border m-2 text-center">
|
|
351
351
|
<Typography align={"center"}
|
|
352
|
-
|
|
353
|
-
|
|
352
|
+
variant={"label"}
|
|
353
|
+
className={disabled ? "text-surface-accent-600 dark:text-surface-accent-500" : ""}>
|
|
354
354
|
{helpText}
|
|
355
355
|
</Typography>
|
|
356
356
|
</div>
|
|
@@ -374,19 +374,19 @@ export interface StorageUploadProps {
|
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
export function StorageUpload({
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
377
|
+
property,
|
|
378
|
+
name,
|
|
379
|
+
value, // This is internalValue from useStorageUploadController
|
|
380
|
+
setInternalValue,
|
|
381
|
+
onChange,
|
|
382
|
+
multipleFilesSupported,
|
|
383
|
+
onFileUploadComplete,
|
|
384
|
+
disabled,
|
|
385
|
+
onFilesAdded,
|
|
386
|
+
autoFocus,
|
|
387
|
+
storage,
|
|
388
|
+
storagePathBuilder,
|
|
389
|
+
}: StorageUploadProps) {
|
|
390
390
|
|
|
391
391
|
if (multipleFilesSupported) {
|
|
392
392
|
const arrayProperty = property as ResolvedArrayProperty<string[]>;
|
|
@@ -500,6 +500,6 @@ export function StorageUpload({
|
|
|
500
500
|
);
|
|
501
501
|
} else {
|
|
502
502
|
// For single file, no D&D context is needed
|
|
503
|
-
return <FileDropComponent {...fileDropProps} isDndItemDragging={false}/>;
|
|
503
|
+
return <FileDropComponent {...fileDropProps} isDndItemDragging={false} />;
|
|
504
504
|
}
|
|
505
505
|
}
|
|
@@ -122,10 +122,10 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
|
|
|
122
122
|
|
|
123
123
|
const navigate = useNavigate();
|
|
124
124
|
|
|
125
|
-
const collectionsRef = useRef<EntityCollection[] | undefined>();
|
|
126
|
-
const viewsRef = useRef<CMSView[] | undefined>();
|
|
127
|
-
const adminViewsRef = useRef<CMSView[] | undefined>();
|
|
128
|
-
const navigationEntriesOrderRef = useRef<string[] | undefined>();
|
|
125
|
+
const collectionsRef = useRef<EntityCollection[] | undefined>(undefined);
|
|
126
|
+
const viewsRef = useRef<CMSView[] | undefined>(undefined);
|
|
127
|
+
const adminViewsRef = useRef<CMSView[] | undefined>(undefined);
|
|
128
|
+
const navigationEntriesOrderRef = useRef<string[] | undefined>(undefined);
|
|
129
129
|
|
|
130
130
|
const [initialised, setInitialised] = useState<boolean>(false);
|
|
131
131
|
|
|
@@ -51,7 +51,7 @@ export function useValidateAuthenticator<USER extends User = any>
|
|
|
51
51
|
* We use this ref to check the authentication only if the user has
|
|
52
52
|
* changed.
|
|
53
53
|
*/
|
|
54
|
-
const checkedUserRef = useRef<User | undefined>();
|
|
54
|
+
const checkedUserRef = useRef<User | undefined>(undefined);
|
|
55
55
|
|
|
56
56
|
const checkAuthentication = useCallback(async () => {
|
|
57
57
|
|
|
@@ -238,8 +238,7 @@ export function useBuildDataSource({
|
|
|
238
238
|
const orderProperty = collection?.orderProperty;
|
|
239
239
|
if (orderProperty && (status === "new" || status === "copy")) {
|
|
240
240
|
const orderProp = properties?.[orderProperty as keyof M];
|
|
241
|
-
|
|
242
|
-
if (orderProp?.disabled === true) {
|
|
241
|
+
if (orderProp) {
|
|
243
242
|
const currentValue = updatedValues[orderProperty as keyof M];
|
|
244
243
|
if (currentValue === undefined || currentValue === null) {
|
|
245
244
|
try {
|
|
@@ -186,6 +186,7 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
|
|
|
186
186
|
if (typeof value === "object") {
|
|
187
187
|
content =
|
|
188
188
|
<MapPropertyPreview {...props}
|
|
189
|
+
value={value as Record<string, CMSType>}
|
|
189
190
|
property={property as ResolvedMapProperty} />;
|
|
190
191
|
} else {
|
|
191
192
|
content = buildWrongValueType(propertyKey, property.dataType, value);
|
package/src/types/analytics.ts
CHANGED
|
@@ -34,5 +34,15 @@ export type CMSAnalyticsEvent =
|
|
|
34
34
|
|
|
35
35
|
| "collection_inline_editing"
|
|
36
36
|
|
|
37
|
+
| "view_mode_changed"
|
|
38
|
+
|
|
39
|
+
| "kanban_card_moved"
|
|
40
|
+
| "kanban_column_reorder"
|
|
41
|
+
| "kanban_property_changed"
|
|
42
|
+
| "kanban_new_entity_in_column"
|
|
43
|
+
| "kanban_backfill_order"
|
|
44
|
+
|
|
45
|
+
| "card_view_entity_click"
|
|
46
|
+
|
|
37
47
|
| "unmapped_event"
|
|
38
48
|
;
|
package/src/types/plugins.tsx
CHANGED
|
@@ -112,6 +112,19 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
|
|
|
112
112
|
|
|
113
113
|
collectionView?: {
|
|
114
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Custom component to render when a collection loading error occurs.
|
|
117
|
+
* If provided, this replaces the default error view in all collection view modes
|
|
118
|
+
* (table, card, kanban).
|
|
119
|
+
* Return `null` from the component to fall back to the default error view.
|
|
120
|
+
*/
|
|
121
|
+
CollectionError?: React.ComponentType<{
|
|
122
|
+
path: string;
|
|
123
|
+
collection: EC;
|
|
124
|
+
parentCollectionIds?: string[];
|
|
125
|
+
error: Error;
|
|
126
|
+
}>;
|
|
127
|
+
|
|
115
128
|
/**
|
|
116
129
|
* Use this component to add custom actions to the entity collections
|
|
117
130
|
* toolbar.
|
|
@@ -229,6 +242,11 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
|
|
|
229
242
|
*/
|
|
230
243
|
ActionsTop?: React.ComponentType<PluginFormActionProps<any, EC>>;
|
|
231
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Add custom content above the entity title in the form view
|
|
247
|
+
*/
|
|
248
|
+
BeforeTitle?: React.ComponentType<PluginFormActionProps<any, EC>>;
|
|
249
|
+
|
|
232
250
|
fieldBuilder?: <T extends CMSType = CMSType>(props: PluginFieldBuilderParams<T, any, EC>) => React.ComponentType<FieldProps<T>> | null;
|
|
233
251
|
|
|
234
252
|
fieldBuilderEnabled?: <T extends CMSType = CMSType>(props: PluginFieldBuilderParams<T>) => boolean;
|
package/src/util/entities.ts
CHANGED
|
@@ -33,7 +33,7 @@ export function isHidden(property: Property | ResolvedProperty): boolean {
|
|
|
33
33
|
return typeof property.disabled === "object" && Boolean(property.disabled.hidden);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export function isPropertyBuilder<T extends CMSType, M extends Record<string, any
|
|
36
|
+
export function isPropertyBuilder<T extends CMSType = CMSType, M extends Record<string, any> = any>(propertyOrBuilder?: PropertyOrBuilder<T, M> | Property | ResolvedProperty): propertyOrBuilder is PropertyBuilder<T, M> {
|
|
37
37
|
return typeof propertyOrBuilder === "function";
|
|
38
38
|
}
|
|
39
39
|
|