@griddo/ax 1.55.13 → 1.56.1
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/package.json +2 -2
- package/src/GlobalStore.tsx +3 -0
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +6 -0
- package/src/components/Fields/Wysiwyg/config.tsx +0 -1
- package/src/components/Fields/Wysiwyg/index.tsx +16 -5
- package/src/components/Gallery/GalleryPanel/DetailPanel/index.tsx +110 -99
- package/src/components/Gallery/GalleryPanel/GalleryDragAndDrop/index.tsx +75 -55
- package/src/components/Gallery/GalleryPanel/index.tsx +14 -8
- package/src/components/Gallery/index.tsx +113 -151
- package/src/components/Gallery/style.tsx +40 -10
- package/src/components/MainWrapper/AppBar/index.tsx +1 -0
- package/src/components/Toast/index.tsx +15 -9
- package/src/components/Toast/style.tsx +2 -2
- package/src/containers/Gallery/actions.tsx +171 -0
- package/src/containers/Gallery/constants.tsx +18 -0
- package/src/containers/Gallery/index.tsx +7 -0
- package/src/containers/Gallery/interfaces.tsx +41 -0
- package/src/containers/Gallery/reducer.tsx +78 -0
- package/src/containers/PageEditor/actions.tsx +15 -5
- package/src/containers/StructuredData/actions.tsx +4 -21
- package/src/forms/fields.tsx +19 -6
- package/src/guards/error/index.tsx +3 -1
- package/src/modules/Content/HeaderMenus/Live/index.tsx +6 -5
- package/src/modules/Sites/SitesList/index.tsx +1 -1
- package/src/modules/StructuredData/Form/ConnectedField/index.tsx +1 -1
- package/src/modules/StructuredData/Form/index.tsx +10 -4
- package/src/modules/StructuredData/StructuredDataList/HeaderMenus/Live/index.tsx +1 -1
- package/src/types/index.tsx +20 -0
- package/src/components/Gallery/store.tsx +0 -186
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddo/ax",
|
|
3
3
|
"description": "Griddo Author Experience",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.56.1",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
|
|
7
7
|
"Carlos Torres <carlos.torres@secuoyas.com>",
|
|
@@ -233,5 +233,5 @@
|
|
|
233
233
|
"publishConfig": {
|
|
234
234
|
"access": "public"
|
|
235
235
|
},
|
|
236
|
-
"gitHead": "
|
|
236
|
+
"gitHead": "337c1eda056013bf9d886bb9fb95ddb20c9ae07c"
|
|
237
237
|
}
|
package/src/GlobalStore.tsx
CHANGED
|
@@ -16,6 +16,7 @@ import { languagesReducer, languagesInitialState } from "./containers/Settings/L
|
|
|
16
16
|
import { dataPacksReducer, dataPacksInitialState } from "./containers/Settings/DataPacks/reducer";
|
|
17
17
|
import { socialReducer, socialInitialState } from "./containers/Settings/Social/reducer";
|
|
18
18
|
import { usersReducer, usersInitialState } from "./containers/Users/reducer";
|
|
19
|
+
import { galleryReducer, galleryInitialState } from "./containers/Gallery/reducer";
|
|
19
20
|
|
|
20
21
|
import { IRootState } from "@ax/types";
|
|
21
22
|
|
|
@@ -44,6 +45,7 @@ export class GlobalStore {
|
|
|
44
45
|
dataPacks: dataPacksReducer as Reducer<any, Action<any>>,
|
|
45
46
|
social: socialReducer as Reducer<any, Action<any>>,
|
|
46
47
|
users: usersReducer as Reducer<any, Action<any>>,
|
|
48
|
+
gallery: galleryReducer as Reducer<any, Action<any>>,
|
|
47
49
|
});
|
|
48
50
|
|
|
49
51
|
const rootReducer = (state: IRootState | undefined, action: any) => {
|
|
@@ -60,6 +62,7 @@ export class GlobalStore {
|
|
|
60
62
|
dataPacks: dataPacksInitialState,
|
|
61
63
|
social: socialInitialState,
|
|
62
64
|
users: usersInitialState,
|
|
65
|
+
gallery: galleryInitialState,
|
|
63
66
|
};
|
|
64
67
|
}
|
|
65
68
|
|
|
@@ -111,6 +111,12 @@ const PageConnectedField = (props: any) => {
|
|
|
111
111
|
updateEditorContent(selectedEditorID, "metaTitle", value);
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
+
|
|
115
|
+
const isComponentImage = ["LinkableImage", "Image"].includes(selectedContent.component) && key === "file";
|
|
116
|
+
if (isComponentImage) {
|
|
117
|
+
updateEditorContent(selectedEditorID, "title", value.title);
|
|
118
|
+
updateEditorContent(selectedEditorID, "alt", value.alt);
|
|
119
|
+
}
|
|
114
120
|
};
|
|
115
121
|
|
|
116
122
|
if (isOverride) {
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
2
3
|
import FroalaEditor from "react-froala-wysiwyg";
|
|
3
4
|
import { decodeEntities } from "@ax/helpers";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { IImage, ISite } from "@ax/types";
|
|
6
|
+
import { galleryActions } from "@ax/containers/Gallery";
|
|
6
7
|
|
|
7
8
|
import { wysiwygConfig, buttons, buttonsFull } from "./config";
|
|
8
9
|
import "./vendors";
|
|
9
10
|
import * as S from "./style";
|
|
10
11
|
|
|
11
|
-
const Wysiwyg = (props:
|
|
12
|
-
const { value, error, onChange, placeholder, full = true, disabled, handleValidation, site } = props;
|
|
12
|
+
const Wysiwyg = (props: IProps): JSX.Element => {
|
|
13
|
+
const { value, error, onChange, placeholder, full = true, disabled, handleValidation, site, uploadImage } = props;
|
|
13
14
|
|
|
14
15
|
const imageSite = site ? site.id : "global"
|
|
15
16
|
|
|
@@ -71,4 +72,14 @@ interface IWysiwygProps {
|
|
|
71
72
|
site: ISite;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
interface IDispatchProps {
|
|
76
|
+
uploadImage: (imageFile: File, site: number | string) => Promise<IImage>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const mapDispatchToProps = {
|
|
80
|
+
uploadImage: galleryActions.uploadImage,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
type IProps = IWysiwygProps & IDispatchProps;
|
|
84
|
+
|
|
85
|
+
export default connect(null, mapDispatchToProps)(Wysiwyg);
|
|
@@ -1,114 +1,106 @@
|
|
|
1
|
-
import React, { memo,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import React, { memo, useEffect, useState } from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
3
|
+
import { IImage, IRootState, IImageForm } from "@ax/types";
|
|
4
|
+
import { Button, CheckField, FieldsBehavior, Toast } from "@ax/components";
|
|
4
5
|
import { formatBytes, getFormattedDateWithTimezone } from "@ax/helpers";
|
|
5
|
-
import {
|
|
6
|
-
reducer,
|
|
7
|
-
initialState,
|
|
8
|
-
setInitForm,
|
|
9
|
-
setFormField,
|
|
10
|
-
saveImageData,
|
|
11
|
-
deleteImageData,
|
|
12
|
-
uploadImage,
|
|
13
|
-
} from "../../store";
|
|
14
6
|
|
|
7
|
+
import { galleryActions } from "@ax/containers/Gallery";
|
|
8
|
+
import { IIsSaving } from "@ax/containers/Gallery/reducer";
|
|
15
9
|
import * as S from "./style";
|
|
16
10
|
|
|
17
11
|
const GalleryDetailPanel = (props: IProps) => {
|
|
18
|
-
const {
|
|
12
|
+
const {
|
|
13
|
+
imageSelected,
|
|
14
|
+
isImageSelected,
|
|
15
|
+
deleteImage,
|
|
16
|
+
isGlobalTab,
|
|
17
|
+
updateImage,
|
|
18
|
+
refreshImages,
|
|
19
|
+
isSaving,
|
|
20
|
+
setImage,
|
|
21
|
+
uploadImage,
|
|
22
|
+
} = props;
|
|
23
|
+
|
|
24
|
+
const initialState: IImageForm = {
|
|
25
|
+
id: undefined,
|
|
26
|
+
alt: "",
|
|
27
|
+
title: "",
|
|
28
|
+
description: "",
|
|
29
|
+
tags: [],
|
|
30
|
+
};
|
|
19
31
|
|
|
20
|
-
const [
|
|
21
|
-
const [isSaving, setIsSaving] = useState({ save: false, saveAdd: false, delete: false });
|
|
32
|
+
const [imageForm, setImageForm] = useState(initialState);
|
|
22
33
|
const [addToGlobal, setAddToGlobal] = useState({ value: "addToGlobal", isChecked: false });
|
|
34
|
+
const [deletedToast, setDeletedToast] = useState(false);
|
|
35
|
+
|
|
36
|
+
const setInitForm = (imageSelected: IImage) => {
|
|
37
|
+
const form = {
|
|
38
|
+
id: imageSelected.id,
|
|
39
|
+
title: imageSelected.title ? imageSelected.title : "",
|
|
40
|
+
alt: imageSelected.alt ? imageSelected.alt : "",
|
|
41
|
+
description: imageSelected.description ? imageSelected.description : "",
|
|
42
|
+
tags: imageSelected.tags ? imageSelected.tags : [],
|
|
43
|
+
};
|
|
44
|
+
setImageForm(form);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const updateFormField = (field: string, value: string) => setImageForm((state) => ({ ...state, [field]: value }));
|
|
23
48
|
|
|
24
49
|
useEffect(() => {
|
|
25
|
-
if (imageSelected && imageSelected.id !==
|
|
26
|
-
|
|
27
|
-
setInitForm({
|
|
28
|
-
id: imageSelected.id,
|
|
29
|
-
title: imageSelected.title,
|
|
30
|
-
alt: imageSelected.alt,
|
|
31
|
-
description: imageSelected.description,
|
|
32
|
-
tags: imageSelected.tags,
|
|
33
|
-
})
|
|
34
|
-
);
|
|
50
|
+
if (imageSelected && imageSelected.id !== imageForm.id) {
|
|
51
|
+
setInitForm(imageSelected);
|
|
35
52
|
}
|
|
36
53
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
37
54
|
}, [imageSelected]);
|
|
38
55
|
|
|
39
|
-
const
|
|
40
|
-
|
|
56
|
+
const handleTitle = (newValue: string) => {
|
|
57
|
+
updateFormField("title", newValue);
|
|
41
58
|
};
|
|
42
59
|
|
|
43
|
-
const
|
|
44
|
-
|
|
60
|
+
const handleAlt = (newValue: string) => {
|
|
61
|
+
updateFormField("alt", newValue);
|
|
45
62
|
};
|
|
46
63
|
|
|
47
|
-
const
|
|
48
|
-
|
|
64
|
+
const handleDescription = (newValue: string) => {
|
|
65
|
+
updateFormField("description", newValue);
|
|
49
66
|
};
|
|
50
67
|
|
|
51
|
-
const
|
|
52
|
-
|
|
68
|
+
const handleTags = (newValue: string) => {
|
|
69
|
+
updateFormField("tags", newValue);
|
|
53
70
|
};
|
|
54
71
|
|
|
55
72
|
const handleSetAsGlobal = async () => {
|
|
56
73
|
const saveAsGlobal = !isGlobalTab && addToGlobal.isChecked && imageSelected?.file;
|
|
57
|
-
const image = saveAsGlobal && imageSelected && await uploadImage(imageSelected.file, "global");
|
|
58
|
-
if (image) await
|
|
74
|
+
const image = saveAsGlobal && imageSelected && (await uploadImage(imageSelected.file, "global"));
|
|
75
|
+
if (image && image.id) await updateImage(image.id, imageForm);
|
|
59
76
|
setAddToGlobal({ value: "addToGlobal", isChecked: false });
|
|
60
77
|
};
|
|
61
78
|
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const value = await saveImageData(state.imageForm.id, state.imageForm);
|
|
68
|
-
if (value) {
|
|
69
|
-
updateImage(value);
|
|
70
|
-
}
|
|
71
|
-
setIsSaving({ ...isSaving, saveAdd: false });
|
|
72
|
-
setImage();
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.log(error);
|
|
75
|
-
}
|
|
79
|
+
const handleSave = async () => {
|
|
80
|
+
if (imageForm.id) {
|
|
81
|
+
await updateImage(imageForm.id, imageForm);
|
|
82
|
+
await handleSetAsGlobal();
|
|
83
|
+
refreshImages();
|
|
76
84
|
}
|
|
77
85
|
};
|
|
78
86
|
|
|
79
|
-
const
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const value = await saveImageData(state.imageForm.id, state.imageForm);
|
|
85
|
-
if (value) {
|
|
86
|
-
updateImage(value);
|
|
87
|
-
}
|
|
88
|
-
setIsSaving({ ...isSaving, save: false });
|
|
89
|
-
} catch (error) {
|
|
90
|
-
console.log(error);
|
|
91
|
-
}
|
|
87
|
+
const handleSaveAndAdd = async () => {
|
|
88
|
+
if (imageForm.id) {
|
|
89
|
+
await updateImage(imageForm.id, imageForm, true);
|
|
90
|
+
handleSetAsGlobal();
|
|
91
|
+
setImage(imageForm);
|
|
92
92
|
}
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (value && state.imageForm.id) {
|
|
101
|
-
deleteImage(state.imageForm.id);
|
|
102
|
-
}
|
|
103
|
-
setIsSaving({ ...isSaving, delete: false });
|
|
104
|
-
})
|
|
105
|
-
.catch((error) => {
|
|
106
|
-
console.log(error);
|
|
107
|
-
});
|
|
95
|
+
const handleDelete = async () => {
|
|
96
|
+
if (imageForm.id) {
|
|
97
|
+
const deleted = await deleteImage(imageForm.id);
|
|
98
|
+
if (deleted) setDeletedToast(true);
|
|
99
|
+
refreshImages();
|
|
108
100
|
}
|
|
109
101
|
};
|
|
110
102
|
|
|
111
|
-
const
|
|
103
|
+
const handleOnClickUrl = () => {
|
|
112
104
|
if (imageSelected) {
|
|
113
105
|
const win = window.open(imageSelected.url, "_blank");
|
|
114
106
|
if (win) {
|
|
@@ -137,14 +129,14 @@ const GalleryDetailPanel = (props: IProps) => {
|
|
|
137
129
|
|
|
138
130
|
const canSetAsGlobal = !isGlobalTab && imageSelected?.file;
|
|
139
131
|
|
|
140
|
-
const
|
|
132
|
+
const renderImageForm = !imageSelected ? null : (
|
|
141
133
|
<S.PanelForm>
|
|
142
134
|
{canSetAsGlobal && <AddToGlobal />}
|
|
143
135
|
<S.DateWrapper>Uploaded: {getFormattedDateWithTimezone(imageSelected.published, "d MMM Y")}</S.DateWrapper>
|
|
144
136
|
<S.ImageName>{imageSelected.name}</S.ImageName>
|
|
145
137
|
<S.ImageInfoWrapper>
|
|
146
138
|
<S.ImageWrapper>
|
|
147
|
-
<img src={imageSelected.url} alt={
|
|
139
|
+
<img src={imageSelected.url} alt={imageForm.alt} />
|
|
148
140
|
</S.ImageWrapper>
|
|
149
141
|
<S.ImageData>
|
|
150
142
|
<S.ImageSize>{formatBytes(imageSelected.size)}</S.ImageSize>
|
|
@@ -159,42 +151,42 @@ const GalleryDetailPanel = (props: IProps) => {
|
|
|
159
151
|
fieldType="TextField"
|
|
160
152
|
readonly={true}
|
|
161
153
|
icon="openOutside"
|
|
162
|
-
onClickIcon={
|
|
154
|
+
onClickIcon={handleOnClickUrl}
|
|
163
155
|
/>
|
|
164
156
|
<FieldsBehavior
|
|
165
|
-
title="
|
|
166
|
-
name="
|
|
167
|
-
value={
|
|
157
|
+
title="Title"
|
|
158
|
+
name="title"
|
|
159
|
+
value={imageForm.title}
|
|
168
160
|
fieldType="TextField"
|
|
169
|
-
onChange={
|
|
161
|
+
onChange={handleTitle}
|
|
170
162
|
/>
|
|
171
163
|
<FieldsBehavior
|
|
172
|
-
title="
|
|
173
|
-
name="
|
|
174
|
-
value={
|
|
164
|
+
title="Alternative text"
|
|
165
|
+
name="alt"
|
|
166
|
+
value={imageForm.alt}
|
|
175
167
|
fieldType="TextField"
|
|
176
|
-
onChange={
|
|
168
|
+
onChange={handleAlt}
|
|
177
169
|
/>
|
|
178
170
|
<FieldsBehavior
|
|
179
171
|
title="Description"
|
|
180
172
|
name="description"
|
|
181
|
-
value={
|
|
173
|
+
value={imageForm.description}
|
|
182
174
|
fieldType="TextArea"
|
|
183
|
-
onChange={
|
|
175
|
+
onChange={handleDescription}
|
|
184
176
|
/>
|
|
185
|
-
<FieldsBehavior title="Tags" value={
|
|
177
|
+
<FieldsBehavior title="Tags" value={imageForm.tags} fieldType="TagField" onChange={handleTags} />
|
|
186
178
|
</S.FormWrapper>
|
|
187
179
|
</S.PanelForm>
|
|
188
180
|
);
|
|
189
181
|
|
|
190
182
|
return (
|
|
191
183
|
<S.DetailPanelWrapper hidden={!isImageSelected}>
|
|
192
|
-
{
|
|
184
|
+
{renderImageForm}
|
|
193
185
|
<S.PanelActions>
|
|
194
186
|
<Button
|
|
195
187
|
type="button"
|
|
196
188
|
buttonStyle="text"
|
|
197
|
-
onClick={
|
|
189
|
+
onClick={handleDelete}
|
|
198
190
|
disabled={isSaving.save || isSaving.saveAdd || isSaving.delete}
|
|
199
191
|
>
|
|
200
192
|
{isSaving.delete ? `Deleting` : `Delete`}
|
|
@@ -202,30 +194,49 @@ const GalleryDetailPanel = (props: IProps) => {
|
|
|
202
194
|
<Button
|
|
203
195
|
type="button"
|
|
204
196
|
buttonStyle="line"
|
|
205
|
-
onClick={
|
|
197
|
+
onClick={handleSave}
|
|
206
198
|
disabled={isSaving.save || isSaving.saveAdd || isSaving.delete}
|
|
207
199
|
>
|
|
208
200
|
{isSaving.save ? `Saving` : `Save`}
|
|
209
201
|
</Button>
|
|
210
202
|
<Button
|
|
211
203
|
type="button"
|
|
212
|
-
onClick={
|
|
204
|
+
onClick={handleSaveAndAdd}
|
|
213
205
|
disabled={isSaving.save || isSaving.saveAdd || isSaving.delete}
|
|
214
206
|
>
|
|
215
207
|
{isSaving.saveAdd ? `Saving` : `Save & Add`}
|
|
216
208
|
</Button>
|
|
217
209
|
</S.PanelActions>
|
|
210
|
+
{deletedToast && <Toast message="1 image deleted" setIsVisible={setDeletedToast} />}
|
|
218
211
|
</S.DetailPanelWrapper>
|
|
219
212
|
);
|
|
220
213
|
};
|
|
221
214
|
|
|
222
|
-
interface
|
|
223
|
-
setImage: () => void;
|
|
224
|
-
updateImage: (image: IImage) => void;
|
|
225
|
-
deleteImage: (imageID: number) => void;
|
|
215
|
+
interface IDetailPanelProps {
|
|
216
|
+
setImage: (imageData: any) => void;
|
|
226
217
|
imageSelected: IImage | null;
|
|
227
218
|
isImageSelected: boolean;
|
|
228
219
|
isGlobalTab: boolean;
|
|
220
|
+
refreshImages: () => void;
|
|
221
|
+
isSaving: IIsSaving;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const mapStateToProps = (state: IRootState) => ({
|
|
225
|
+
isSaving: state.gallery.isSaving,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
interface IDispatchProps {
|
|
229
|
+
updateImage: (imageID: number, imageData: IImageForm, add?: boolean) => Promise<void>;
|
|
230
|
+
deleteImage: (imageID: number) => Promise<boolean>;
|
|
231
|
+
uploadImage: (imageFile: File, site: number | string) => Promise<IImage>;
|
|
229
232
|
}
|
|
230
233
|
|
|
231
|
-
|
|
234
|
+
const mapDispatchToProps = {
|
|
235
|
+
updateImage: galleryActions.updateImage,
|
|
236
|
+
deleteImage: galleryActions.deleteImage,
|
|
237
|
+
uploadImage: galleryActions.uploadImage,
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
type IProps = IDetailPanelProps & IDispatchProps;
|
|
241
|
+
|
|
242
|
+
export default connect(mapStateToProps, mapDispatchToProps)(memo(GalleryDetailPanel));
|
|
@@ -1,43 +1,46 @@
|
|
|
1
|
-
import React, { memo,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
initialState,
|
|
7
|
-
setDropDepth,
|
|
8
|
-
setInDropZone,
|
|
9
|
-
setIsUploading,
|
|
10
|
-
setUploadSuccess,
|
|
11
|
-
setUploadError,
|
|
12
|
-
uploadImage,
|
|
13
|
-
} from "../../store";
|
|
1
|
+
import React, { memo, useRef, useState } from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
3
|
+
|
|
4
|
+
import { galleryActions } from "@ax/containers/Gallery";
|
|
5
|
+
import { IGetSiteImages, IImage, IRootState } from "@ax/types";
|
|
14
6
|
import { Icon, DragAndDrop } from "@ax/components";
|
|
15
7
|
|
|
16
8
|
import * as S from "./style";
|
|
17
9
|
|
|
18
10
|
const GalleryDragAndDrop = (props: IProps) => {
|
|
19
|
-
const { isImageSelected,
|
|
20
|
-
|
|
11
|
+
const { isImageSelected,
|
|
12
|
+
validFormats,
|
|
13
|
+
site,
|
|
14
|
+
allowUpload,
|
|
15
|
+
refreshImages,
|
|
16
|
+
uploadError,
|
|
17
|
+
isUploading,
|
|
18
|
+
isSuccess,
|
|
19
|
+
isError,
|
|
20
|
+
errorMsg,
|
|
21
|
+
uploadImage,
|
|
22
|
+
selectImage,
|
|
23
|
+
} = props;
|
|
21
24
|
const validExtensions = validFormats.map((format) => `.${format}`).join(",");
|
|
22
25
|
|
|
23
|
-
const filesInputRef = useRef<
|
|
24
|
-
const filesButtonRef = useRef<
|
|
25
|
-
|
|
26
|
-
const [
|
|
26
|
+
const filesInputRef = useRef<HTMLInputElement>(null);
|
|
27
|
+
const filesButtonRef = useRef<HTMLButtonElement>(null);
|
|
28
|
+
const [inDropZone, setInDropZone] = useState(false);
|
|
29
|
+
const [dropDepth, setDropDepth] = useState(0);
|
|
27
30
|
|
|
28
31
|
const handleDragEnter = () => {
|
|
29
|
-
|
|
32
|
+
setDropDepth((depth) => depth + 1);
|
|
30
33
|
};
|
|
31
34
|
|
|
32
35
|
const handleDragLeave = () => {
|
|
33
|
-
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
+
setDropDepth((depth) => depth - 1);
|
|
37
|
+
if (dropDepth > 1) return;
|
|
38
|
+
setInDropZone(false);
|
|
36
39
|
};
|
|
37
40
|
|
|
38
41
|
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
|
|
39
42
|
e.dataTransfer.dropEffect = "copy";
|
|
40
|
-
|
|
43
|
+
setInDropZone(true);
|
|
41
44
|
};
|
|
42
45
|
|
|
43
46
|
const checkType = (type: string) => {
|
|
@@ -51,7 +54,7 @@ const GalleryDragAndDrop = (props: IProps) => {
|
|
|
51
54
|
const files = Array.from(e.dataTransfer.files);
|
|
52
55
|
e.dataTransfer.clearData();
|
|
53
56
|
await handleUploadFile(files);
|
|
54
|
-
|
|
57
|
+
setDropDepth(0);
|
|
55
58
|
};
|
|
56
59
|
|
|
57
60
|
const handleFilesUpload = (e: any) => {
|
|
@@ -61,23 +64,15 @@ const GalleryDragAndDrop = (props: IProps) => {
|
|
|
61
64
|
|
|
62
65
|
const handleUploadFile = async (files: Array<any>) => {
|
|
63
66
|
if (files[0]) {
|
|
64
|
-
dispatch(setIsUploading());
|
|
65
|
-
|
|
66
67
|
if (!checkType(files[0].type)) {
|
|
67
|
-
|
|
68
|
+
uploadError("Invalid format");
|
|
68
69
|
} else {
|
|
69
70
|
try {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
addImage(value);
|
|
75
|
-
dispatch(setInDropZone(false));
|
|
76
|
-
} else {
|
|
77
|
-
dispatch(setUploadError("Error uploading image"));
|
|
78
|
-
}
|
|
71
|
+
const newImage = await uploadImage(files[0], site);
|
|
72
|
+
setInDropZone(false);
|
|
73
|
+
await refreshImages();
|
|
74
|
+
selectImage(newImage);
|
|
79
75
|
} catch (error) {
|
|
80
|
-
dispatch(setUploadError("Error uploading image"));
|
|
81
76
|
console.log(error);
|
|
82
77
|
}
|
|
83
78
|
}
|
|
@@ -85,16 +80,14 @@ const GalleryDragAndDrop = (props: IProps) => {
|
|
|
85
80
|
};
|
|
86
81
|
|
|
87
82
|
const handleTryAgain = () => {
|
|
88
|
-
|
|
83
|
+
setInDropZone(false);
|
|
89
84
|
};
|
|
90
85
|
|
|
91
86
|
const handleFileClick = () => {
|
|
92
|
-
|
|
93
|
-
filesInputRef.current.click();
|
|
94
|
-
}
|
|
87
|
+
filesInputRef && filesInputRef.current && filesInputRef.current.click();
|
|
95
88
|
};
|
|
96
89
|
|
|
97
|
-
const errorWrapper =
|
|
90
|
+
const errorWrapper = errorMsg ? <S.ErrorMsg>{errorMsg}</S.ErrorMsg> : null;
|
|
98
91
|
|
|
99
92
|
const renderDragAndDrop = () => (
|
|
100
93
|
<DragAndDrop
|
|
@@ -135,7 +128,7 @@ const GalleryDragAndDrop = (props: IProps) => {
|
|
|
135
128
|
<S.DragIcon>
|
|
136
129
|
<Icon name="image" size="36" />
|
|
137
130
|
</S.DragIcon>
|
|
138
|
-
<S.DragTitle>Select
|
|
131
|
+
<S.DragTitle>Select an image to see details</S.DragTitle>
|
|
139
132
|
</S.StatusWrapper>
|
|
140
133
|
|
|
141
134
|
);
|
|
@@ -143,18 +136,18 @@ const GalleryDragAndDrop = (props: IProps) => {
|
|
|
143
136
|
return (
|
|
144
137
|
<S.Wrapper hidden={isImageSelected}>
|
|
145
138
|
<S.DragAndDropWrapper
|
|
146
|
-
inDropZone={
|
|
147
|
-
uploading={
|
|
148
|
-
success={
|
|
149
|
-
error={
|
|
139
|
+
inDropZone={inDropZone}
|
|
140
|
+
uploading={isUploading}
|
|
141
|
+
success={isSuccess}
|
|
142
|
+
error={isError}
|
|
150
143
|
>
|
|
151
144
|
{allowUpload ? renderDragAndDrop() : renderPlaceholder()}
|
|
152
145
|
</S.DragAndDropWrapper>
|
|
153
146
|
<S.UploadingWrapper
|
|
154
|
-
inDropZone={
|
|
155
|
-
uploading={
|
|
156
|
-
success={
|
|
157
|
-
error={
|
|
147
|
+
inDropZone={inDropZone}
|
|
148
|
+
uploading={isUploading}
|
|
149
|
+
success={isSuccess}
|
|
150
|
+
error={isError}
|
|
158
151
|
>
|
|
159
152
|
<S.StatusWrapper>
|
|
160
153
|
<S.UploadingStatus>
|
|
@@ -185,12 +178,39 @@ const GalleryDragAndDrop = (props: IProps) => {
|
|
|
185
178
|
);
|
|
186
179
|
};
|
|
187
180
|
|
|
188
|
-
interface
|
|
181
|
+
interface IGalleryDragAndDropProps {
|
|
189
182
|
isImageSelected: boolean;
|
|
190
183
|
validFormats: string[];
|
|
191
|
-
addImage: (image: IImage) => void;
|
|
192
184
|
site: number | string;
|
|
193
185
|
allowUpload: boolean;
|
|
186
|
+
refreshImages: () => Promise<void>;
|
|
187
|
+
isUploading: boolean,
|
|
188
|
+
isSuccess: boolean,
|
|
189
|
+
isError: boolean,
|
|
190
|
+
errorMsg: string;
|
|
194
191
|
}
|
|
195
192
|
|
|
196
|
-
|
|
193
|
+
const mapStateToProps = (state: IRootState) => ({
|
|
194
|
+
isUploading: state.gallery.isUploading,
|
|
195
|
+
isSuccess: state.gallery.isSuccess,
|
|
196
|
+
isError: state.gallery.isError,
|
|
197
|
+
errorMsg: state.gallery.errorMsg,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
interface IDispatchProps {
|
|
201
|
+
getSiteImages(params: IGetSiteImages): Promise<void>;
|
|
202
|
+
selectImage(item: IImage): void;
|
|
203
|
+
uploadError: (error: string) => Promise<void>;
|
|
204
|
+
uploadImage: (imageFile: File, site: number | string) => Promise<IImage>;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const mapDispatchToProps = {
|
|
208
|
+
getSiteImages: galleryActions.getSiteImages,
|
|
209
|
+
selectImage: galleryActions.selectImage,
|
|
210
|
+
uploadError: galleryActions.uploadError,
|
|
211
|
+
uploadImage: galleryActions.uploadImage,
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
type IProps = IGalleryDragAndDropProps & IDispatchProps;
|
|
215
|
+
|
|
216
|
+
export default connect(mapStateToProps, mapDispatchToProps)(memo(GalleryDragAndDrop));
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
import React, { memo } from "react";
|
|
2
|
+
|
|
2
3
|
import { IImage } from "@ax/types";
|
|
4
|
+
|
|
3
5
|
import GalleryDragAndDrop from "./GalleryDragAndDrop";
|
|
4
6
|
import DetailPanel from "./DetailPanel";
|
|
5
|
-
|
|
6
7
|
import * as S from "./style";
|
|
7
8
|
|
|
8
9
|
const GalleryPanel = (props: IGalleryPanelProps) => {
|
|
9
|
-
const { isImageSelected, imageSelected,
|
|
10
|
+
const { isImageSelected, imageSelected, validFormats, setImage, isGlobalTab, site, selectedTab, refreshImages } =
|
|
11
|
+
props;
|
|
10
12
|
const allowUpload = !isGlobalTab || !selectedTab;
|
|
11
13
|
|
|
12
14
|
return (
|
|
13
15
|
<S.GalleryPanel>
|
|
14
|
-
<GalleryDragAndDrop
|
|
16
|
+
<GalleryDragAndDrop
|
|
17
|
+
isImageSelected={isImageSelected}
|
|
18
|
+
validFormats={validFormats}
|
|
19
|
+
site={site}
|
|
20
|
+
allowUpload={allowUpload}
|
|
21
|
+
refreshImages={refreshImages}
|
|
22
|
+
/>
|
|
15
23
|
<DetailPanel
|
|
16
24
|
imageSelected={imageSelected}
|
|
17
25
|
isImageSelected={isImageSelected}
|
|
18
26
|
setImage={setImage}
|
|
19
|
-
updateImage={updateItems}
|
|
20
|
-
deleteImage={updateItems}
|
|
21
27
|
isGlobalTab={isGlobalTab}
|
|
28
|
+
refreshImages={refreshImages}
|
|
22
29
|
/>
|
|
23
30
|
</S.GalleryPanel>
|
|
24
31
|
);
|
|
@@ -28,12 +35,11 @@ interface IGalleryPanelProps {
|
|
|
28
35
|
isImageSelected: boolean;
|
|
29
36
|
imageSelected: IImage | null;
|
|
30
37
|
validFormats: string[];
|
|
31
|
-
|
|
32
|
-
setImage: () => void;
|
|
33
|
-
addImage: (image: IImage) => void;
|
|
38
|
+
setImage: (imageData: any) => void;
|
|
34
39
|
isGlobalTab: boolean;
|
|
35
40
|
site: number | string;
|
|
36
41
|
selectedTab: string;
|
|
42
|
+
refreshImages: () => Promise<void>;
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
export default memo(GalleryPanel);
|