@firecms/core 3.0.0-beta.7 → 3.0.0-beta.8
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/app/AppBar.d.ts +12 -0
- package/dist/app/Drawer.d.ts +17 -0
- package/dist/app/Scaffold.d.ts +30 -0
- package/dist/app/index.d.ts +4 -0
- package/dist/app/useApp.d.ts +16 -0
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +4 -0
- package/dist/components/index.d.ts +1 -1
- package/dist/{components/FireCMSAppBar.d.ts → core/DefaultAppBar.d.ts} +2 -7
- package/dist/core/DefaultDrawer.d.ts +19 -0
- package/dist/core/EntityEditView.d.ts +17 -3
- package/dist/core/NavigationRoutes.d.ts +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/form/PropertiesForm.d.ts +8 -0
- package/dist/form/components/FieldHelperText.d.ts +3 -3
- package/dist/form/components/StorageItemPreview.d.ts +4 -4
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +2 -4
- package/dist/form/index.d.ts +0 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +5595 -5574
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5 -5
- package/dist/index.umd.js.map +1 -1
- package/dist/preview/PropertyPreviewProps.d.ts +5 -0
- package/dist/preview/components/StorageThumbnail.d.ts +2 -1
- package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
- package/dist/types/auth.d.ts +1 -1
- package/dist/types/collections.d.ts +14 -0
- package/dist/types/datasource.d.ts +18 -8
- package/dist/types/fields.d.ts +31 -30
- package/dist/types/plugins.d.ts +11 -3
- package/dist/types/properties.d.ts +7 -2
- package/dist/util/icons.d.ts +2 -2
- package/dist/util/storage.d.ts +23 -2
- package/dist/util/useStorageUploadController.d.ts +1 -1
- package/package.json +13 -22
- package/src/app/AppBar.tsx +18 -0
- package/src/app/Drawer.tsx +25 -0
- package/src/{core → app}/Scaffold.tsx +61 -107
- package/src/app/index.ts +4 -0
- package/src/app/useApp.tsx +32 -0
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +7 -9
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +2 -1
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +7 -4
- package/src/components/VirtualTable/VirtualTable.tsx +3 -1
- package/src/components/VirtualTable/VirtualTableProps.tsx +5 -0
- package/src/components/common/useTableSearchHelper.ts +52 -12
- package/src/components/index.tsx +1 -1
- package/src/contexts/DialogsProvider.tsx +2 -2
- package/src/{components/FireCMSAppBar.tsx → core/DefaultAppBar.tsx} +26 -28
- package/src/core/DefaultDrawer.tsx +177 -0
- package/src/core/EntityEditView.tsx +669 -129
- package/src/core/EntitySidePanel.tsx +0 -1
- package/src/core/NavigationRoutes.tsx +3 -3
- package/src/core/index.tsx +2 -2
- package/src/form/PropertiesForm.tsx +81 -0
- package/src/form/PropertyFieldBinding.tsx +29 -5
- package/src/form/components/FieldHelperText.tsx +3 -3
- package/src/form/components/StorageItemPreview.tsx +19 -10
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +8 -5
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +5 -4
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +40 -35
- package/src/form/field_bindings/MapFieldBinding.tsx +11 -3
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +0 -7
- package/src/form/field_bindings/RepeatFieldBinding.tsx +10 -7
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +6 -35
- package/src/form/field_bindings/SwitchFieldBinding.tsx +1 -1
- package/src/form/index.tsx +4 -4
- package/src/form/validation.ts +1 -17
- package/src/index.ts +1 -0
- package/src/internal/useBuildDataSource.ts +12 -0
- package/src/preview/PropertyPreview.tsx +5 -2
- package/src/preview/PropertyPreviewProps.tsx +6 -0
- package/src/preview/components/ImagePreview.tsx +21 -33
- package/src/preview/components/ReferencePreview.tsx +2 -1
- package/src/preview/components/StorageThumbnail.tsx +5 -1
- package/src/preview/components/UrlComponentPreview.tsx +44 -11
- package/src/types/auth.tsx +1 -1
- package/src/types/collections.ts +14 -0
- package/src/types/customization_controller.tsx +0 -1
- package/src/types/datasource.ts +20 -16
- package/src/types/fields.tsx +33 -33
- package/src/types/plugins.tsx +12 -3
- package/src/types/properties.ts +7 -2
- package/src/util/entities.ts +4 -3
- package/src/util/objects.ts +17 -7
- package/src/util/permissions.ts +1 -0
- package/src/util/storage.ts +75 -21
- package/src/util/useStorageUploadController.tsx +21 -3
- package/dist/core/Drawer.d.ts +0 -16
- package/dist/core/Scaffold.d.ts +0 -51
- package/dist/form/EntityForm.d.ts +0 -77
- package/src/core/Drawer.tsx +0 -139
- package/src/form/EntityForm.tsx +0 -736
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import React, { useCallback } from "react";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
ArrayProperty,
|
|
5
|
-
Entity,
|
|
6
|
-
EntityCollection,
|
|
7
|
-
FieldProps,
|
|
8
|
-
ResolvedArrayProperty,
|
|
9
|
-
ResolvedStringProperty,
|
|
10
|
-
StorageConfig
|
|
11
|
-
} from "../../types";
|
|
3
|
+
import { ArrayProperty, FieldProps, ResolvedArrayProperty, ResolvedStringProperty, StorageConfig } from "../../types";
|
|
12
4
|
import { useDropzone } from "react-dropzone";
|
|
13
5
|
import { PreviewSize } from "../../preview";
|
|
14
6
|
import { FieldHelperText, LabelWithIcon } from "../components";
|
|
@@ -31,7 +23,7 @@ import { useClearRestoreValue } from "../useClearRestoreValue";
|
|
|
31
23
|
|
|
32
24
|
const dropZoneClasses = "box-border relative pt-[2px] items-center border border-transparent min-h-[254px] outline-none rounded-md duration-200 ease-[cubic-bezier(0.4,0,0.2,1)] focus:border-primary-solid";
|
|
33
25
|
const disabledClasses = "border-dotted-gray"
|
|
34
|
-
const nonActiveDropClasses =
|
|
26
|
+
const nonActiveDropClasses = fieldBackgroundHoverMixin
|
|
35
27
|
const activeDropClasses = "pt-0 border-2 border-solid"
|
|
36
28
|
const acceptDropClasses = "transition-colors duration-200 ease-[cubic-bezier(0,0,0.2,1)] border-2 border-solid border-green-500"
|
|
37
29
|
const rejectDropClasses = "transition-colors duration-200 ease-[cubic-bezier(0,0,0.2,1)] border-2 border-solid border-red-500"
|
|
@@ -81,7 +73,7 @@ export function StorageUploadFieldBinding({
|
|
|
81
73
|
propertyKey,
|
|
82
74
|
value,
|
|
83
75
|
storageSource,
|
|
84
|
-
disabled,
|
|
76
|
+
disabled: disabled ?? false,
|
|
85
77
|
onChange: setValue
|
|
86
78
|
});
|
|
87
79
|
|
|
@@ -91,12 +83,6 @@ export function StorageUploadFieldBinding({
|
|
|
91
83
|
setValue
|
|
92
84
|
});
|
|
93
85
|
|
|
94
|
-
const entity: Entity<any> = {
|
|
95
|
-
id: context.entityId,
|
|
96
|
-
values: context.values,
|
|
97
|
-
path: context.path
|
|
98
|
-
};
|
|
99
|
-
|
|
100
86
|
return (
|
|
101
87
|
|
|
102
88
|
<>
|
|
@@ -109,15 +95,13 @@ export function StorageUploadFieldBinding({
|
|
|
109
95
|
|
|
110
96
|
<StorageUpload
|
|
111
97
|
value={internalValue}
|
|
112
|
-
collection={context.collection}
|
|
113
98
|
name={propertyKey}
|
|
114
|
-
disabled={disabled}
|
|
115
|
-
autoFocus={autoFocus}
|
|
99
|
+
disabled={disabled ?? false}
|
|
100
|
+
autoFocus={autoFocus ?? false}
|
|
116
101
|
property={property}
|
|
117
102
|
onChange={setValue}
|
|
118
103
|
setInternalValue={setInternalValue}
|
|
119
104
|
onFilesAdded={onFilesAdded}
|
|
120
|
-
entity={entity}
|
|
121
105
|
onFileUploadComplete={onFileUploadComplete}
|
|
122
106
|
storagePathBuilder={storagePathBuilder}
|
|
123
107
|
storage={storage}
|
|
@@ -135,7 +119,6 @@ export function StorageUploadFieldBinding({
|
|
|
135
119
|
|
|
136
120
|
function FileDropComponent({
|
|
137
121
|
storage,
|
|
138
|
-
collection,
|
|
139
122
|
disabled,
|
|
140
123
|
isDraggingOver,
|
|
141
124
|
onFilesAdded,
|
|
@@ -144,7 +127,6 @@ function FileDropComponent({
|
|
|
144
127
|
autoFocus,
|
|
145
128
|
internalValue,
|
|
146
129
|
property,
|
|
147
|
-
entity,
|
|
148
130
|
onClear,
|
|
149
131
|
metadata,
|
|
150
132
|
storagePathBuilder,
|
|
@@ -154,7 +136,6 @@ function FileDropComponent({
|
|
|
154
136
|
helpText
|
|
155
137
|
}: {
|
|
156
138
|
storage: StorageConfig,
|
|
157
|
-
collection: EntityCollection,
|
|
158
139
|
disabled: boolean,
|
|
159
140
|
isDraggingOver: boolean,
|
|
160
141
|
droppableProvided: any,
|
|
@@ -165,7 +146,6 @@ function FileDropComponent({
|
|
|
165
146
|
property: ResolvedStringProperty,
|
|
166
147
|
onClear: (clearedStoragePathOrDownloadUrl: string) => void,
|
|
167
148
|
metadata: any,
|
|
168
|
-
entity: Entity<any>;
|
|
169
149
|
storagePathBuilder: (file: File) => string,
|
|
170
150
|
onFileUploadComplete: (uploadedPath: string, entry: StorageFieldItem, fileMetadata?: any) => Promise<void>,
|
|
171
151
|
size: PreviewSize,
|
|
@@ -235,11 +215,9 @@ function FileDropComponent({
|
|
|
235
215
|
if (entry.storagePathOrDownloadUrl) {
|
|
236
216
|
child = (
|
|
237
217
|
<StorageItemPreview
|
|
238
|
-
collection={collection}
|
|
239
218
|
name={`storage_preview_${entry.storagePathOrDownloadUrl}`}
|
|
240
219
|
property={property}
|
|
241
220
|
disabled={disabled}
|
|
242
|
-
entity={entity}
|
|
243
221
|
value={entry.storagePathOrDownloadUrl}
|
|
244
222
|
onRemove={onClear}
|
|
245
223
|
size={entry.size}/>
|
|
@@ -299,7 +277,6 @@ function FileDropComponent({
|
|
|
299
277
|
|
|
300
278
|
export interface StorageUploadProps {
|
|
301
279
|
value: StorageFieldItem[];
|
|
302
|
-
collection: EntityCollection;
|
|
303
280
|
setInternalValue: (v: StorageFieldItem[]) => void;
|
|
304
281
|
name: string;
|
|
305
282
|
property: ResolvedStringProperty | ResolvedArrayProperty<string[]>;
|
|
@@ -307,7 +284,6 @@ export interface StorageUploadProps {
|
|
|
307
284
|
multipleFilesSupported: boolean;
|
|
308
285
|
autoFocus: boolean;
|
|
309
286
|
disabled: boolean;
|
|
310
|
-
entity: Entity<any>;
|
|
311
287
|
storage: StorageConfig;
|
|
312
288
|
onFilesAdded: (acceptedFiles: File[]) => void;
|
|
313
289
|
storagePathBuilder: (file: File) => string;
|
|
@@ -315,7 +291,6 @@ export interface StorageUploadProps {
|
|
|
315
291
|
}
|
|
316
292
|
|
|
317
293
|
export function StorageUpload({
|
|
318
|
-
collection,
|
|
319
294
|
property,
|
|
320
295
|
name,
|
|
321
296
|
value,
|
|
@@ -327,7 +302,6 @@ export function StorageUpload({
|
|
|
327
302
|
onFilesAdded,
|
|
328
303
|
autoFocus,
|
|
329
304
|
storage,
|
|
330
|
-
entity,
|
|
331
305
|
storagePathBuilder,
|
|
332
306
|
}: StorageUploadProps) {
|
|
333
307
|
|
|
@@ -408,11 +382,10 @@ export function StorageUpload({
|
|
|
408
382
|
className="rounded"
|
|
409
383
|
>
|
|
410
384
|
<StorageItemPreview
|
|
411
|
-
collection={collection}
|
|
412
385
|
name={`storage_preview_${entry.storagePathOrDownloadUrl}`}
|
|
386
|
+
placeholder={true}
|
|
413
387
|
property={renderProperty}
|
|
414
388
|
disabled={true}
|
|
415
|
-
entity={entity}
|
|
416
389
|
value={entry.storagePathOrDownloadUrl as string}
|
|
417
390
|
onRemove={onClear}
|
|
418
391
|
size={entry.size}/>
|
|
@@ -422,7 +395,6 @@ export function StorageUpload({
|
|
|
422
395
|
>
|
|
423
396
|
{(provided, snapshot) => {
|
|
424
397
|
return <FileDropComponent storage={storage}
|
|
425
|
-
collection={collection}
|
|
426
398
|
disabled={disabled}
|
|
427
399
|
isDraggingOver={snapshot.isDraggingOver}
|
|
428
400
|
droppableProvided={provided}
|
|
@@ -431,7 +403,6 @@ export function StorageUpload({
|
|
|
431
403
|
autoFocus={autoFocus}
|
|
432
404
|
internalValue={value}
|
|
433
405
|
property={renderProperty}
|
|
434
|
-
entity={entity}
|
|
435
406
|
onClear={onClear}
|
|
436
407
|
metadata={metadata}
|
|
437
408
|
storagePathBuilder={storagePathBuilder}
|
|
@@ -46,7 +46,7 @@ export const SwitchFieldBinding = React.forwardRef(function SwitchFieldBinding({
|
|
|
46
46
|
title={property.name}/>}
|
|
47
47
|
disabled={disabled}
|
|
48
48
|
autoFocus={autoFocus}
|
|
49
|
-
size={"
|
|
49
|
+
size={"large"}
|
|
50
50
|
/>
|
|
51
51
|
|
|
52
52
|
<FieldHelperText includeDescription={includeDescription}
|
package/src/form/index.tsx
CHANGED
|
@@ -34,10 +34,10 @@ export {
|
|
|
34
34
|
|
|
35
35
|
export * from "./components";
|
|
36
36
|
|
|
37
|
-
export type { EntityFormProps } from "./EntityForm";
|
|
38
|
-
export {
|
|
39
|
-
|
|
40
|
-
} from "./EntityForm";
|
|
37
|
+
// export type { EntityFormProps } from "./EntityForm";
|
|
38
|
+
// export {
|
|
39
|
+
// EntityForm
|
|
40
|
+
// } from "./EntityForm";
|
|
41
41
|
|
|
42
42
|
export { PropertyFieldBinding } from "./PropertyFieldBinding";
|
|
43
43
|
export * from "./useClearRestoreValue";
|
package/src/form/validation.ts
CHANGED
|
@@ -112,23 +112,7 @@ export function getYupMapObjectSchema({
|
|
|
112
112
|
if (validation?.required) {
|
|
113
113
|
return shape.required(validation?.requiredMessage ? validation.requiredMessage : "Required").nullable(true);
|
|
114
114
|
}
|
|
115
|
-
return shape.nullable(true);
|
|
116
|
-
// const object: ObjectSchema<any> = yup.object().shape(objectSchema);
|
|
117
|
-
// return validation?.required
|
|
118
|
-
// ? object.required(validation?.requiredMessage ? validation.requiredMessage : "Required").nullable(true)
|
|
119
|
-
// : yup.object().optional().default(undefined).notRequired().nullable(true).test(
|
|
120
|
-
// "empty-check",
|
|
121
|
-
// "Optional map can be empty",
|
|
122
|
-
// (o: any, testContext: any) => {
|
|
123
|
-
// try {
|
|
124
|
-
// if (!o || Object.keys(o).length === 0) return true;
|
|
125
|
-
// return object.validateSync(o);
|
|
126
|
-
// } catch (e) {
|
|
127
|
-
// testContext.createError(e);
|
|
128
|
-
// console.error(e);
|
|
129
|
-
// return false;
|
|
130
|
-
// }
|
|
131
|
-
// });
|
|
115
|
+
return yup.object().shape(shape.fields).default(undefined).notRequired().nullable(true);
|
|
132
116
|
}
|
|
133
117
|
|
|
134
118
|
function getYupStringSchema({
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
FetchCollectionProps,
|
|
10
10
|
FetchEntityProps,
|
|
11
11
|
FilterValues,
|
|
12
|
+
FireCMSContext,
|
|
12
13
|
ListenCollectionProps,
|
|
13
14
|
ListenEntityProps,
|
|
14
15
|
NavigationController,
|
|
@@ -313,6 +314,17 @@ export function useBuildDataSource({
|
|
|
313
314
|
)
|
|
314
315
|
}, [delegate.isFilterCombinationValid]),
|
|
315
316
|
|
|
317
|
+
initTextSearch: useCallback(async (props: {
|
|
318
|
+
context: FireCMSContext,
|
|
319
|
+
path: string,
|
|
320
|
+
collection: EntityCollection,
|
|
321
|
+
parentCollectionIds?: string[]
|
|
322
|
+
}): Promise<boolean> => {
|
|
323
|
+
if (!delegate.initTextSearch)
|
|
324
|
+
return false;
|
|
325
|
+
return delegate.initTextSearch(props)
|
|
326
|
+
}, [delegate.initTextSearch]),
|
|
327
|
+
|
|
316
328
|
};
|
|
317
329
|
|
|
318
330
|
}
|
|
@@ -47,7 +47,7 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
|
|
|
47
47
|
size,
|
|
48
48
|
height,
|
|
49
49
|
width,
|
|
50
|
-
|
|
50
|
+
interactive
|
|
51
51
|
} = props;
|
|
52
52
|
|
|
53
53
|
const property = resolveProperty({
|
|
@@ -84,12 +84,15 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
|
|
|
84
84
|
content =
|
|
85
85
|
<UrlComponentPreview size={props.size}
|
|
86
86
|
url={value}
|
|
87
|
+
interactive={interactive}
|
|
87
88
|
previewType={stringProperty.url}/>;
|
|
88
89
|
} else if (stringProperty.storage) {
|
|
90
|
+
const filePath = stringProperty.storage.previewUrl ? stringProperty.storage.previewUrl(value) : value;
|
|
89
91
|
content = <StorageThumbnail
|
|
92
|
+
interactive={interactive}
|
|
90
93
|
storeUrl={property.storage?.storeUrl ?? false}
|
|
91
94
|
size={props.size}
|
|
92
|
-
storagePathOrDownloadUrl={
|
|
95
|
+
storagePathOrDownloadUrl={filePath}/>;
|
|
93
96
|
} else if (stringProperty.markdown) {
|
|
94
97
|
content = <Markdown source={value} size={"small"}/>;
|
|
95
98
|
} else {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { CSSProperties, useMemo
|
|
1
|
+
import React, { CSSProperties, useMemo } from "react";
|
|
2
2
|
|
|
3
3
|
import { getThumbnailMeasure } from "../util";
|
|
4
4
|
import { PreviewSize } from "../PropertyPreviewProps";
|
|
@@ -20,8 +20,6 @@ export function ImagePreview({
|
|
|
20
20
|
url
|
|
21
21
|
}: ImagePreviewProps) {
|
|
22
22
|
|
|
23
|
-
const [onHover, setOnHover] = useState(false);
|
|
24
|
-
|
|
25
23
|
const imageSize = useMemo(() => getThumbnailMeasure(size), [size]);
|
|
26
24
|
|
|
27
25
|
if (size === "tiny") {
|
|
@@ -47,60 +45,50 @@ export function ImagePreview({
|
|
|
47
45
|
|
|
48
46
|
return (
|
|
49
47
|
<div
|
|
50
|
-
className="relative flex items-center justify-center max-w-full max-h-full"
|
|
48
|
+
className="relative flex items-center justify-center max-w-full max-h-full group"
|
|
51
49
|
style={{
|
|
52
50
|
width: imageSize,
|
|
53
51
|
height: imageSize
|
|
54
52
|
}}
|
|
55
|
-
key={"image_preview_" + url}
|
|
56
|
-
onMouseEnter={() => setOnHover(true)}
|
|
57
|
-
onMouseMove={() => setOnHover(true)}
|
|
58
|
-
onMouseLeave={() => setOnHover(false)}>
|
|
53
|
+
key={"image_preview_" + url}>
|
|
59
54
|
|
|
60
55
|
<img src={url}
|
|
61
56
|
className={"rounded-md"}
|
|
62
57
|
style={imageStyle}/>
|
|
63
58
|
|
|
64
|
-
{onHover && <>
|
|
65
59
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
</IconButton>
|
|
80
|
-
</div>
|
|
60
|
+
<div className={"flex flex-row gap-2 absolute bottom-[-4px] right-[-4px] invisible group-hover:visible"}>
|
|
61
|
+
{navigator && <Tooltip title="Copy url to clipboard" side={"bottom"}>
|
|
62
|
+
<IconButton
|
|
63
|
+
variant={"filled"}
|
|
64
|
+
size={"small"}
|
|
65
|
+
onClick={(e) => {
|
|
66
|
+
e.stopPropagation();
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
return navigator.clipboard.writeText(url);
|
|
69
|
+
}}>
|
|
70
|
+
<ContentCopyIcon className={"text-gray-700 dark:text-gray-300"}
|
|
71
|
+
size={"small"}/>
|
|
72
|
+
</IconButton>
|
|
81
73
|
</Tooltip>}
|
|
82
74
|
|
|
83
|
-
<Tooltip title="Open image in new tab">
|
|
75
|
+
<Tooltip title="Open image in new tab" side={"bottom"}>
|
|
84
76
|
<IconButton
|
|
77
|
+
className="invisible group-hover:visible"
|
|
85
78
|
variant={"filled"}
|
|
86
79
|
component={"a" as React.ElementType}
|
|
87
|
-
style={{
|
|
88
|
-
position: "absolute",
|
|
89
|
-
bottom: -4,
|
|
90
|
-
right: -4
|
|
91
|
-
}}
|
|
92
80
|
href={url}
|
|
93
81
|
rel="noopener noreferrer"
|
|
94
82
|
target="_blank"
|
|
95
83
|
size={"small"}
|
|
96
84
|
onClick={(e: any) => e.stopPropagation()}
|
|
97
85
|
>
|
|
98
|
-
<OpenInNewIcon className={"text-gray-
|
|
86
|
+
<OpenInNewIcon className={"text-gray-700 dark:text-gray-300"}
|
|
99
87
|
size={"small"}/>
|
|
100
88
|
</IconButton>
|
|
101
89
|
</Tooltip>
|
|
102
|
-
|
|
103
|
-
|
|
90
|
+
</div>
|
|
91
|
+
|
|
104
92
|
</div>
|
|
105
93
|
);
|
|
106
94
|
}
|
|
@@ -41,7 +41,8 @@ function areEqual(prevProps: ReferencePreviewProps, nextProps: ReferencePreviewP
|
|
|
41
41
|
prevProps.hover === nextProps.hover &&
|
|
42
42
|
prevProps.reference?.id === nextProps.reference?.id &&
|
|
43
43
|
prevProps.reference?.path === nextProps.reference?.path &&
|
|
44
|
-
prevProps.includeEntityLink === nextProps.includeEntityLink
|
|
44
|
+
prevProps.includeEntityLink === nextProps.includeEntityLink &&
|
|
45
|
+
prevProps.onClick === nextProps.onClick
|
|
45
46
|
;
|
|
46
47
|
}
|
|
47
48
|
|
|
@@ -11,6 +11,7 @@ type StorageThumbnailProps = {
|
|
|
11
11
|
storagePathOrDownloadUrl: string;
|
|
12
12
|
storeUrl: boolean;
|
|
13
13
|
size: PreviewSize;
|
|
14
|
+
interactive?: boolean;
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -21,13 +22,15 @@ export const StorageThumbnail = React.memo<StorageThumbnailProps>(StorageThumbna
|
|
|
21
22
|
function areEqual(prevProps: StorageThumbnailProps, nextProps: StorageThumbnailProps) {
|
|
22
23
|
return prevProps.size === nextProps.size &&
|
|
23
24
|
prevProps.storagePathOrDownloadUrl === nextProps.storagePathOrDownloadUrl &&
|
|
24
|
-
prevProps.storeUrl === nextProps.storeUrl
|
|
25
|
+
prevProps.storeUrl === nextProps.storeUrl&&
|
|
26
|
+
prevProps.interactive === nextProps.interactive;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
const URL_CACHE: Record<string, DownloadConfig> = {};
|
|
28
30
|
|
|
29
31
|
export function StorageThumbnailInternal({
|
|
30
32
|
storeUrl,
|
|
33
|
+
interactive,
|
|
31
34
|
storagePathOrDownloadUrl,
|
|
32
35
|
size
|
|
33
36
|
}: StorageThumbnailProps) {
|
|
@@ -68,6 +71,7 @@ export function StorageThumbnailInternal({
|
|
|
68
71
|
return downloadConfig?.url
|
|
69
72
|
? <UrlComponentPreview previewType={previewType}
|
|
70
73
|
url={downloadConfig.url}
|
|
74
|
+
interactive={interactive}
|
|
71
75
|
size={size}
|
|
72
76
|
hint={storagePathOrDownloadUrl}/>
|
|
73
77
|
: renderSkeletonImageThumbnail(size);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
2
|
|
|
3
3
|
import { ImagePreview } from "./ImagePreview";
|
|
4
4
|
import { getThumbnailMeasure } from "../util";
|
|
5
5
|
import { PreviewType } from "../../types";
|
|
6
6
|
import { PreviewSize } from "../PropertyPreviewProps";
|
|
7
|
-
import { DescriptionIcon, OpenInNewIcon, Tooltip, Typography } from "@firecms/ui";
|
|
7
|
+
import { cls, DescriptionIcon, OpenInNewIcon, Tooltip, Typography } from "@firecms/ui";
|
|
8
8
|
import { EmptyValue } from "./EmptyValue";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -14,12 +14,15 @@ export function UrlComponentPreview({
|
|
|
14
14
|
url,
|
|
15
15
|
previewType,
|
|
16
16
|
size,
|
|
17
|
-
hint
|
|
17
|
+
hint,
|
|
18
|
+
interactive = true
|
|
18
19
|
}: {
|
|
19
20
|
url: string,
|
|
20
21
|
previewType?: PreviewType,
|
|
21
22
|
size: PreviewSize,
|
|
22
|
-
hint?: string
|
|
23
|
+
hint?: string,
|
|
24
|
+
// for video controls
|
|
25
|
+
interactive?: boolean
|
|
23
26
|
}): React.ReactElement {
|
|
24
27
|
|
|
25
28
|
if (!previewType) {
|
|
@@ -43,17 +46,13 @@ export function UrlComponentPreview({
|
|
|
43
46
|
size={size}/>;
|
|
44
47
|
} else if (previewType === "audio") {
|
|
45
48
|
return <audio controls
|
|
49
|
+
className={"max-w-100%"}
|
|
46
50
|
src={url}>
|
|
47
51
|
Your browser does not support the
|
|
48
52
|
<code>audio</code> element.
|
|
49
53
|
</audio>;
|
|
50
54
|
} else if (previewType === "video") {
|
|
51
|
-
return <
|
|
52
|
-
className={`max-w-${size === "small" ? "sm" : "md"}`}
|
|
53
|
-
controls
|
|
54
|
-
>
|
|
55
|
-
<source src={url}/>
|
|
56
|
-
</video>;
|
|
55
|
+
return <VideoPreview size={size} src={url} interactive={interactive}/>;
|
|
57
56
|
} else {
|
|
58
57
|
return (
|
|
59
58
|
<a
|
|
@@ -66,7 +65,7 @@ export function UrlComponentPreview({
|
|
|
66
65
|
width: getThumbnailMeasure(size),
|
|
67
66
|
height: getThumbnailMeasure(size)
|
|
68
67
|
}}>
|
|
69
|
-
<DescriptionIcon className="
|
|
68
|
+
<DescriptionIcon className="text-gray-700 dark:text-gray-300"/>
|
|
70
69
|
{hint &&
|
|
71
70
|
<Tooltip title={hint}>
|
|
72
71
|
<Typography
|
|
@@ -77,3 +76,37 @@ export function UrlComponentPreview({
|
|
|
77
76
|
);
|
|
78
77
|
}
|
|
79
78
|
}
|
|
79
|
+
|
|
80
|
+
function VideoPreview({
|
|
81
|
+
size,
|
|
82
|
+
src,
|
|
83
|
+
interactive
|
|
84
|
+
}: { size: PreviewSize, src: string, interactive: boolean }) {
|
|
85
|
+
|
|
86
|
+
const imageSize = useMemo(() => {
|
|
87
|
+
if (size === "tiny")
|
|
88
|
+
return "140px";
|
|
89
|
+
else if (size === "small")
|
|
90
|
+
return "240px";
|
|
91
|
+
else if (size === "medium")
|
|
92
|
+
return "100%";
|
|
93
|
+
else throw new Error("Invalid size");
|
|
94
|
+
}, [size]);
|
|
95
|
+
|
|
96
|
+
const videoProps = {
|
|
97
|
+
controls: interactive
|
|
98
|
+
};
|
|
99
|
+
return <video
|
|
100
|
+
style={{
|
|
101
|
+
position: "relative",
|
|
102
|
+
objectFit: "cover",
|
|
103
|
+
width: imageSize,
|
|
104
|
+
minWidth: "140px",
|
|
105
|
+
// height: imageSize,
|
|
106
|
+
maxHeight: "100%"
|
|
107
|
+
}}
|
|
108
|
+
{...videoProps}
|
|
109
|
+
className={cls("max-w-100% rounded", { "pointer-events-none": !interactive })}>
|
|
110
|
+
<source src={src}/>
|
|
111
|
+
</video>;
|
|
112
|
+
}
|
package/src/types/auth.tsx
CHANGED
package/src/types/collections.ts
CHANGED
|
@@ -477,8 +477,22 @@ export interface AdditionalFieldDelegate<M extends Record<string, any> = any,
|
|
|
477
477
|
*/
|
|
478
478
|
export type EntityCustomView<M extends Record<string, any> = any> =
|
|
479
479
|
{
|
|
480
|
+
/**
|
|
481
|
+
* Key of this custom view.
|
|
482
|
+
*/
|
|
480
483
|
key: string,
|
|
484
|
+
/**
|
|
485
|
+
* Name of this custom view.
|
|
486
|
+
*/
|
|
481
487
|
name: string,
|
|
488
|
+
/**
|
|
489
|
+
* If set to true, the actions of the entity will be included in the
|
|
490
|
+
* bottom of the panel (save buttons, delete buttons, etc.)
|
|
491
|
+
*/
|
|
492
|
+
includeActions?: boolean;
|
|
493
|
+
/**
|
|
494
|
+
* Builder for rendering the custom view
|
|
495
|
+
*/
|
|
482
496
|
Builder?: React.ComponentType<EntityCustomViewParams<M>>;
|
|
483
497
|
}
|
|
484
498
|
|
package/src/types/datasource.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Entity, EntityReference, EntityStatus, EntityValues, GeoPoint } from "./entities";
|
|
2
2
|
import { EntityCollection, FilterValues } from "./collections";
|
|
3
3
|
import { ResolvedEntityCollection } from "./resolved_entities";
|
|
4
|
+
import { FireCMSContext } from "./firecms_context";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @group Datasource
|
|
@@ -217,6 +218,18 @@ export interface DataSource {
|
|
|
217
218
|
*/
|
|
218
219
|
isFilterCombinationValid?(props: FilterCombinationValidProps): boolean;
|
|
219
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Called when the user clicks on the search bar in a collection view.
|
|
223
|
+
* Useful for initializing a text search index.
|
|
224
|
+
* @param props
|
|
225
|
+
*/
|
|
226
|
+
initTextSearch?: (props: {
|
|
227
|
+
context: FireCMSContext,
|
|
228
|
+
path: string,
|
|
229
|
+
collection: EntityCollection,
|
|
230
|
+
parentCollectionIds?: string[]
|
|
231
|
+
}) => Promise<boolean>;
|
|
232
|
+
|
|
220
233
|
}
|
|
221
234
|
|
|
222
235
|
export type FilterCombinationValidProps = {
|
|
@@ -363,30 +376,21 @@ export interface DataSourceDelegate {
|
|
|
363
376
|
*/
|
|
364
377
|
isFilterCombinationValid?(props: Omit<FilterCombinationValidProps, "collection">): boolean;
|
|
365
378
|
|
|
366
|
-
/**
|
|
367
|
-
* Convert a FireCMS reference to a reference that can be used by the datasource
|
|
368
|
-
* @param reference
|
|
369
|
-
*/
|
|
370
|
-
// buildReference: (reference: EntityReference) => any,
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Convert a FireCMS GeoPoint to a GeoPoint that can be used by the datasource
|
|
374
|
-
* @param geoPoint
|
|
375
|
-
*/
|
|
376
|
-
// buildGeoPoint: (geoPoint: GeoPoint) => any,
|
|
377
|
-
|
|
378
379
|
/**
|
|
379
380
|
* Get the object to generate the current time in the datasource
|
|
380
381
|
*/
|
|
381
382
|
currentTime(): any;
|
|
382
383
|
|
|
383
|
-
// buildDate: (date: Date) => any;
|
|
384
|
-
|
|
385
|
-
// buildDeleteFieldValue: () => any;
|
|
386
|
-
|
|
387
384
|
delegateToCMSModel: (data: any) => any;
|
|
388
385
|
|
|
389
386
|
cmsToDelegateModel: (data: any) => any;
|
|
390
387
|
|
|
391
388
|
setDateToMidnight: (input?: any) => any;
|
|
389
|
+
|
|
390
|
+
initTextSearch?: (props: {
|
|
391
|
+
context: FireCMSContext,
|
|
392
|
+
path: string,
|
|
393
|
+
collection: EntityCollection,
|
|
394
|
+
parentCollectionIds?: string[]
|
|
395
|
+
}) => Promise<boolean>;
|
|
392
396
|
}
|