@griddo/ax 1.65.27 → 1.66.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/package.json +2 -2
- package/src/GlobalStore.tsx +4 -3
- package/src/api/structuredData.tsx +15 -1
- package/src/components/Browser/index.tsx +13 -16
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/Field/index.tsx +12 -0
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/TemplateManager/index.tsx +11 -0
- package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +13 -0
- package/src/components/ConfigPanel/Form/index.tsx +3 -1
- package/src/components/ConfigPanel/index.tsx +19 -5
- package/src/components/ConfigPanel/style.tsx +5 -0
- package/src/components/FieldContainer/index.tsx +4 -0
- package/src/components/Fields/ComponentArray/MixableComponentArray/PasteModuleButton/index.tsx +49 -0
- package/src/components/Fields/ComponentArray/MixableComponentArray/index.tsx +51 -11
- package/src/components/Fields/ComponentArray/MixableComponentArray/style.tsx +14 -1
- package/src/components/Fields/ComponentContainer/index.tsx +24 -5
- package/src/components/Fields/ImageField/index.tsx +18 -5
- package/src/components/Image/index.tsx +25 -0
- package/src/components/Notification/index.tsx +3 -1
- package/src/components/index.tsx +2 -0
- package/src/containers/Navigation/Defaults/actions.tsx +27 -9
- package/src/containers/PageEditor/actions.tsx +104 -5
- package/src/containers/PageEditor/constants.tsx +2 -0
- package/src/containers/PageEditor/interfaces.tsx +12 -0
- package/src/containers/PageEditor/reducer.tsx +8 -0
- package/src/containers/PageEditor/utils.tsx +2 -2
- package/src/helpers/index.tsx +6 -0
- package/src/helpers/schemas.tsx +36 -7
- package/src/modules/App/Routing/index.tsx +1 -1
- package/src/modules/Content/OptionTable/index.tsx +44 -43
- package/src/modules/Content/OptionTable/store.tsx +1 -1
- package/src/modules/Content/OptionTable/style.tsx +27 -12
- package/src/modules/Content/PageItem/index.tsx +14 -4
- package/src/modules/Content/atoms.tsx +19 -2
- package/src/modules/Content/index.tsx +37 -14
- package/src/modules/Content/utils.tsx +27 -12
- package/src/modules/GlobalEditor/Editor/index.tsx +12 -1
- package/src/modules/GlobalEditor/index.tsx +20 -2
- package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +13 -0
- package/src/modules/Navigation/Defaults/atoms.tsx +28 -0
- package/src/modules/Navigation/Defaults/index.tsx +30 -4
- package/src/modules/Navigation/Defaults/style.tsx +32 -1
- package/src/modules/PageEditor/Editor/index.tsx +16 -1
- package/src/modules/PageEditor/index.tsx +14 -1
- package/src/modules/PublicPreview/index.tsx +15 -18
- package/src/modules/Settings/Globals/NavigationModules/SideModal/SideModalOption/index.tsx +35 -0
- package/src/modules/Settings/Globals/NavigationModules/SideModal/SideModalOption/style.tsx +22 -0
- package/src/modules/Settings/Globals/NavigationModules/SideModal/index.tsx +111 -0
- package/src/modules/Settings/Globals/NavigationModules/SideModal/style.tsx +64 -0
- package/src/modules/Settings/Globals/NavigationModules/index.tsx +89 -0
- package/src/modules/Settings/Globals/NavigationModules/style.tsx +36 -0
- package/src/modules/Settings/Globals/index.tsx +38 -1
- package/src/modules/Sites/SitesList/SiteItem/index.tsx +7 -5
- package/src/modules/StructuredData/StructuredDataList/OptionTable/index.tsx +14 -3
- package/src/modules/StructuredData/StructuredDataList/OptionTable/style.tsx +11 -2
- package/src/modules/StructuredData/StructuredDataList/atoms.tsx +19 -2
- package/src/modules/StructuredData/StructuredDataList/index.tsx +4 -13
- package/src/types/index.tsx +11 -0
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.66.0",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
|
|
7
7
|
"Carlos Torres <carlos.torres@secuoyas.com>",
|
|
@@ -221,5 +221,5 @@
|
|
|
221
221
|
"publishConfig": {
|
|
222
222
|
"access": "public"
|
|
223
223
|
},
|
|
224
|
-
"gitHead": "
|
|
224
|
+
"gitHead": "48f0e2027ddcce4188e6bed411ecaab485ddb7e5"
|
|
225
225
|
}
|
package/src/GlobalStore.tsx
CHANGED
|
@@ -19,6 +19,7 @@ import { galleryReducer, galleryInitialState } from "./containers/Gallery/reduce
|
|
|
19
19
|
import { domainsReducer, domainsInitialState } from "./containers/Domains/reducer";
|
|
20
20
|
import { redirectsReducer, redirectsInitialState } from "./containers/Redirects/reducer";
|
|
21
21
|
import { analyticsReducer, analyticsInitialState } from "./containers/Analytics/reducer";
|
|
22
|
+
import { LOGOUT } from "./containers/App/constants";
|
|
22
23
|
|
|
23
24
|
import { IRootState } from "@ax/types";
|
|
24
25
|
|
|
@@ -54,10 +55,10 @@ export class GlobalStore {
|
|
|
54
55
|
});
|
|
55
56
|
|
|
56
57
|
const rootReducer = (state: IRootState | undefined, action: any) => {
|
|
57
|
-
if (action.type ===
|
|
58
|
+
if (action.type === LOGOUT) {
|
|
58
59
|
state = {
|
|
59
|
-
router:
|
|
60
|
-
app: appInitialState,
|
|
60
|
+
router: state?.router,
|
|
61
|
+
app: state?.app || appInitialState,
|
|
61
62
|
sites: sitesInitialState,
|
|
62
63
|
pageEditor: pageEditorInitialState,
|
|
63
64
|
menu: menuInitialState,
|
|
@@ -34,6 +34,11 @@ const SERVICES: { [key: string]: IServiceConfig } = {
|
|
|
34
34
|
endpoint: "/structured_data_content/",
|
|
35
35
|
method: "DELETE",
|
|
36
36
|
},
|
|
37
|
+
GET_DATA_CONTENT_BULK: {
|
|
38
|
+
...template,
|
|
39
|
+
endpoint: "/structured_data_content/bulk/",
|
|
40
|
+
method: "GET",
|
|
41
|
+
},
|
|
37
42
|
DELETE_DATA_CONTENT_BULK: {
|
|
38
43
|
...template,
|
|
39
44
|
endpoint: "/structured_data_content/bulk",
|
|
@@ -73,7 +78,7 @@ const SERVICES: { [key: string]: IServiceConfig } = {
|
|
|
73
78
|
...template,
|
|
74
79
|
endpoint: ["/site/", "/structured_data"],
|
|
75
80
|
method: "GET",
|
|
76
|
-
}
|
|
81
|
+
},
|
|
77
82
|
};
|
|
78
83
|
|
|
79
84
|
const getData = (token: string | null, siteID?: number | null) => {
|
|
@@ -145,6 +150,14 @@ const deleteDataContent = async (dataContentId: number) => {
|
|
|
145
150
|
return sendRequest(SERVICES.DELETE_DATA_CONTENT);
|
|
146
151
|
};
|
|
147
152
|
|
|
153
|
+
const getDataContentBulk = (dataContentIDs: number[], siteID?: number) => {
|
|
154
|
+
const { host, endpoint } = SERVICES.GET_DATA_CONTENT_BULK;
|
|
155
|
+
const url = siteID ? `${host}/site/${siteID}${endpoint}` : `${host}${endpoint}`;
|
|
156
|
+
const ids = dataContentIDs.join(",");
|
|
157
|
+
SERVICES.GET_DATA_CONTENT_BULK.dynamicUrl = `${url}${ids}`;
|
|
158
|
+
return sendRequest(SERVICES.GET_DATA_CONTENT_BULK);
|
|
159
|
+
};
|
|
160
|
+
|
|
148
161
|
const deleteDataContentBulk = (dataContentID: number[]) => {
|
|
149
162
|
return sendRequest(SERVICES.DELETE_DATA_CONTENT_BULK, { ids: dataContentID });
|
|
150
163
|
};
|
|
@@ -226,4 +239,5 @@ export default {
|
|
|
226
239
|
setDataStatus,
|
|
227
240
|
setDataStatusBulk,
|
|
228
241
|
getContentTypes,
|
|
242
|
+
getDataContentBulk,
|
|
229
243
|
};
|
|
@@ -3,7 +3,7 @@ import { FrameContextConsumer } from "react-frame-component";
|
|
|
3
3
|
import { StyleSheetManager } from "styled-components";
|
|
4
4
|
|
|
5
5
|
import * as components from "components";
|
|
6
|
-
import {
|
|
6
|
+
import { SiteProvider, builderSSR, ssrHelpers } from "components";
|
|
7
7
|
import { Preview } from "@griddo/core";
|
|
8
8
|
import { findByEditorID } from "@ax/forms";
|
|
9
9
|
import { copyTextToClipboard } from "@ax/helpers";
|
|
@@ -33,7 +33,6 @@ const Browser = (props: IBrowserProps): JSX.Element => {
|
|
|
33
33
|
|
|
34
34
|
const API_URL = process.env.REACT_APP_API_ENDPOINT;
|
|
35
35
|
const PUBLIC_API_URL = process.env.REACT_APP_PUBLIC_API_ENDPOINT;
|
|
36
|
-
const { SiteProvider, AnimationProvider } = providers;
|
|
37
36
|
|
|
38
37
|
const [resolution, setResolution] = useState("desktop");
|
|
39
38
|
const { isVisible, toggleToast, setIsVisible } = useToast();
|
|
@@ -111,20 +110,18 @@ const Browser = (props: IBrowserProps): JSX.Element => {
|
|
|
111
110
|
publicApiUrl={PUBLIC_API_URL}
|
|
112
111
|
siteId={siteID}
|
|
113
112
|
>
|
|
114
|
-
<
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
</S.Wrapper>
|
|
127
|
-
</AnimationProvider>
|
|
113
|
+
<S.Wrapper ref={(ref: any) => ((window as any).browserRef = ref)}>
|
|
114
|
+
<Preview
|
|
115
|
+
isPage={isPage}
|
|
116
|
+
apiUrl={API_URL}
|
|
117
|
+
library={components}
|
|
118
|
+
content={content}
|
|
119
|
+
header={header}
|
|
120
|
+
footer={footer}
|
|
121
|
+
languageId={content.language}
|
|
122
|
+
pageLanguages={content.pageLanguages}
|
|
123
|
+
/>
|
|
124
|
+
</S.Wrapper>
|
|
128
125
|
</SiteProvider>
|
|
129
126
|
);
|
|
130
127
|
|
|
@@ -25,6 +25,10 @@ const Field = (props: IProps): JSX.Element => {
|
|
|
25
25
|
deleteError,
|
|
26
26
|
errors,
|
|
27
27
|
theme,
|
|
28
|
+
moduleCopy,
|
|
29
|
+
availableDataPacks,
|
|
30
|
+
template,
|
|
31
|
+
setHistoryPush,
|
|
28
32
|
} = props;
|
|
29
33
|
|
|
30
34
|
const isGroup = field.type === "FieldGroup";
|
|
@@ -72,6 +76,10 @@ const Field = (props: IProps): JSX.Element => {
|
|
|
72
76
|
error={error}
|
|
73
77
|
deleteError={deleteError}
|
|
74
78
|
theme={theme}
|
|
79
|
+
moduleCopy={moduleCopy}
|
|
80
|
+
availableDataPacks={availableDataPacks}
|
|
81
|
+
template={template}
|
|
82
|
+
setHistoryPush={setHistoryPush}
|
|
75
83
|
/>
|
|
76
84
|
);
|
|
77
85
|
};
|
|
@@ -97,6 +105,10 @@ interface IProps {
|
|
|
97
105
|
errors: IErrorItem[];
|
|
98
106
|
collapsed?: boolean;
|
|
99
107
|
theme: string;
|
|
108
|
+
moduleCopy: { date: string; element: Record<string, unknown> } | null;
|
|
109
|
+
availableDataPacks: Record<string, any>[];
|
|
110
|
+
template: any;
|
|
111
|
+
setHistoryPush?: (path: string, isEditor: boolean) => void;
|
|
100
112
|
}
|
|
101
113
|
|
|
102
114
|
export default Field;
|
package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/TemplateManager/index.tsx
CHANGED
|
@@ -25,6 +25,9 @@ export const TemplateManager = (props: IProps): JSX.Element => {
|
|
|
25
25
|
deleteError,
|
|
26
26
|
errors,
|
|
27
27
|
theme,
|
|
28
|
+
moduleCopy,
|
|
29
|
+
availableDataPacks,
|
|
30
|
+
setHistoryPush,
|
|
28
31
|
} = props;
|
|
29
32
|
|
|
30
33
|
const isConfig = selectedTab === "config";
|
|
@@ -92,6 +95,10 @@ export const TemplateManager = (props: IProps): JSX.Element => {
|
|
|
92
95
|
deleteError={deleteError}
|
|
93
96
|
errors={errors}
|
|
94
97
|
theme={theme}
|
|
98
|
+
moduleCopy={moduleCopy}
|
|
99
|
+
availableDataPacks={availableDataPacks}
|
|
100
|
+
template={template}
|
|
101
|
+
setHistoryPush={setHistoryPush}
|
|
95
102
|
/>
|
|
96
103
|
);
|
|
97
104
|
})}
|
|
@@ -116,11 +123,15 @@ interface IProps {
|
|
|
116
123
|
deleteError(error: IErrorItem): void;
|
|
117
124
|
errors: IErrorItem[];
|
|
118
125
|
theme: string;
|
|
126
|
+
moduleCopy: { date: string; element: Record<string, unknown> } | null;
|
|
127
|
+
availableDataPacks: Record<string, any>[];
|
|
128
|
+
setHistoryPush?: (path: string, isEditor: boolean) => void;
|
|
119
129
|
}
|
|
120
130
|
|
|
121
131
|
const mapStateToProps = (state: IRootState) => ({
|
|
122
132
|
activatedPacks: state.dataPacks.activated,
|
|
123
133
|
activatedModules: state.dataPacks.modules,
|
|
134
|
+
availableDataPacks: state.dataPacks.available,
|
|
124
135
|
});
|
|
125
136
|
|
|
126
137
|
export default connect(mapStateToProps)(TemplateManager);
|
|
@@ -34,6 +34,10 @@ const PageConnectedField = (props: any) => {
|
|
|
34
34
|
deleteError,
|
|
35
35
|
isGlobal,
|
|
36
36
|
theme,
|
|
37
|
+
moduleCopy,
|
|
38
|
+
availableDataPacks,
|
|
39
|
+
template,
|
|
40
|
+
setHistoryPush,
|
|
37
41
|
} = props;
|
|
38
42
|
|
|
39
43
|
const isTemplate = field.type === "template";
|
|
@@ -155,6 +159,8 @@ const PageConnectedField = (props: any) => {
|
|
|
155
159
|
deleteError={deleteError}
|
|
156
160
|
errors={errors}
|
|
157
161
|
theme={theme}
|
|
162
|
+
moduleCopy={moduleCopy}
|
|
163
|
+
setHistoryPush={setHistoryPush}
|
|
158
164
|
/>
|
|
159
165
|
);
|
|
160
166
|
}
|
|
@@ -179,6 +185,10 @@ const PageConnectedField = (props: any) => {
|
|
|
179
185
|
deleteError={deleteError}
|
|
180
186
|
errors={errors}
|
|
181
187
|
theme={theme}
|
|
188
|
+
moduleCopy={moduleCopy}
|
|
189
|
+
availableDataPacks={availableDataPacks}
|
|
190
|
+
template={template}
|
|
191
|
+
setHistoryPush={setHistoryPush}
|
|
182
192
|
/>
|
|
183
193
|
);
|
|
184
194
|
};
|
|
@@ -193,6 +203,9 @@ const mapStateToProps = (state: IRootState) => ({
|
|
|
193
203
|
templateConfig: state.pageEditor.templateConfig,
|
|
194
204
|
activatedModules: state.dataPacks.modules,
|
|
195
205
|
errors: state.pageEditor.errors,
|
|
206
|
+
moduleCopy: state.pageEditor.moduleCopy,
|
|
207
|
+
availableDataPacks: state.dataPacks.available,
|
|
208
|
+
template: state.pageEditor.template,
|
|
196
209
|
});
|
|
197
210
|
|
|
198
211
|
const mapDispatchToProps = {
|
|
@@ -6,7 +6,7 @@ import { Tabs } from "@ax/components";
|
|
|
6
6
|
import ConnectedField from "./ConnectedField";
|
|
7
7
|
|
|
8
8
|
export const Form = (props: IProps): JSX.Element => {
|
|
9
|
-
const { schema, selectedTab, setSelectedTab, actions, isPage, isGlobal, theme } = props;
|
|
9
|
+
const { schema, selectedTab, setSelectedTab, actions, isPage, isGlobal, theme, setHistoryPush } = props;
|
|
10
10
|
const tabContent = schema.configTabs.find((tab: ISchemaTab) => tab.title === selectedTab);
|
|
11
11
|
const setTab = (tab: string) => setSelectedTab(tab);
|
|
12
12
|
|
|
@@ -29,6 +29,7 @@ export const Form = (props: IProps): JSX.Element => {
|
|
|
29
29
|
isPage={isPage}
|
|
30
30
|
isGlobal={isGlobal}
|
|
31
31
|
theme={theme}
|
|
32
|
+
setHistoryPush={setHistoryPush}
|
|
32
33
|
/>
|
|
33
34
|
);
|
|
34
35
|
};
|
|
@@ -69,6 +70,7 @@ interface IProps {
|
|
|
69
70
|
isPage: boolean;
|
|
70
71
|
isGlobal?: boolean;
|
|
71
72
|
theme: string;
|
|
73
|
+
setHistoryPush?: (path: string, isEditor: boolean) => void;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
export default Form;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useEffect, useRef } from "react";
|
|
2
2
|
import { isEmptyObj } from "@ax/helpers";
|
|
3
3
|
|
|
4
4
|
import { Loading } from "@ax/components";
|
|
@@ -10,7 +10,9 @@ import NavigationForm from "./NavigationForm";
|
|
|
10
10
|
import GlobalPageForm from "./GlobalPageForm";
|
|
11
11
|
import PreviewForm from "./PreviewForm";
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
import * as S from "./style";
|
|
14
|
+
|
|
15
|
+
const navigationModulesTypes = ["header", "footer"];
|
|
14
16
|
|
|
15
17
|
const ConfigPanel = (props: IStateProps): JSX.Element => {
|
|
16
18
|
const {
|
|
@@ -31,13 +33,23 @@ const ConfigPanel = (props: IStateProps): JSX.Element => {
|
|
|
31
33
|
isReadOnly,
|
|
32
34
|
userEditing,
|
|
33
35
|
theme,
|
|
36
|
+
lastElementAddedId,
|
|
34
37
|
} = props;
|
|
35
38
|
|
|
39
|
+
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (lastElementAddedId && wrapperRef.current) {
|
|
43
|
+
const element = document.querySelector(`.editorId-${lastElementAddedId}`);
|
|
44
|
+
element && element.scrollIntoView();
|
|
45
|
+
}
|
|
46
|
+
}, [lastElementAddedId]);
|
|
47
|
+
|
|
36
48
|
if (isLoading || isEmptyObj(schema)) {
|
|
37
49
|
return <Loading />;
|
|
38
50
|
}
|
|
39
51
|
|
|
40
|
-
const showNavigationForm =
|
|
52
|
+
const showNavigationForm = navigationModulesTypes.includes(schema.type) && isPage;
|
|
41
53
|
const isGlobalPageNotEditable = isGlobal && isPage && !isEditable;
|
|
42
54
|
|
|
43
55
|
const getForm = () => {
|
|
@@ -67,13 +79,14 @@ const ConfigPanel = (props: IStateProps): JSX.Element => {
|
|
|
67
79
|
isPage={isPage}
|
|
68
80
|
isGlobal={isGlobal}
|
|
69
81
|
theme={theme}
|
|
82
|
+
setHistoryPush={setHistoryPush}
|
|
70
83
|
/>
|
|
71
84
|
);
|
|
72
85
|
}
|
|
73
86
|
};
|
|
74
87
|
|
|
75
88
|
return (
|
|
76
|
-
|
|
89
|
+
<S.Wrapper ref={wrapperRef}>
|
|
77
90
|
<Header
|
|
78
91
|
schema={schema}
|
|
79
92
|
actions={actions}
|
|
@@ -83,7 +96,7 @@ const ConfigPanel = (props: IStateProps): JSX.Element => {
|
|
|
83
96
|
setSelectedContent={setSelectedContent}
|
|
84
97
|
/>
|
|
85
98
|
{getForm()}
|
|
86
|
-
|
|
99
|
+
</S.Wrapper>
|
|
87
100
|
);
|
|
88
101
|
};
|
|
89
102
|
|
|
@@ -105,6 +118,7 @@ interface IStateProps {
|
|
|
105
118
|
isReadOnly?: boolean;
|
|
106
119
|
userEditing?: IUserEditing | null;
|
|
107
120
|
theme: string;
|
|
121
|
+
lastElementAddedId?: null | number;
|
|
108
122
|
}
|
|
109
123
|
|
|
110
124
|
export default ConfigPanel;
|
|
@@ -77,4 +77,8 @@ interface IProps {
|
|
|
77
77
|
error?: any;
|
|
78
78
|
deleteError?(error: any): void;
|
|
79
79
|
theme: string;
|
|
80
|
+
moduleCopy?: { date: string; element: Record<string, unknown> } | null;
|
|
81
|
+
availableDataPacks?: Record<string, unknown>[];
|
|
82
|
+
template?: any;
|
|
83
|
+
setHistoryPush?: (path: string, isEditor: boolean) => void;
|
|
80
84
|
}
|
package/src/components/Fields/ComponentArray/MixableComponentArray/PasteModuleButton/index.tsx
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React, { memo } from "react";
|
|
2
|
+
|
|
3
|
+
import { useToast } from "@ax/hooks";
|
|
4
|
+
import { Tooltip, IconAction, Toast } from "@ax/components";
|
|
5
|
+
import { INotification } from "@ax/types";
|
|
6
|
+
|
|
7
|
+
const PasteModuleButton = (props: IProps): JSX.Element => {
|
|
8
|
+
const { pasteModule, setNotification, setHistoryPush, editorID, isModuleCopyUnavailable } = props;
|
|
9
|
+
|
|
10
|
+
const { isVisible, toggleToast, setIsVisible } = useToast();
|
|
11
|
+
|
|
12
|
+
const handlePasteModule = async () => {
|
|
13
|
+
if (!isModuleCopyUnavailable) {
|
|
14
|
+
const pasteResult = await pasteModule(editorID);
|
|
15
|
+
if (pasteResult.error) {
|
|
16
|
+
const { type, text } = pasteResult.error;
|
|
17
|
+
setNotification && setNotification({ type, text });
|
|
18
|
+
}
|
|
19
|
+
toggleToast();
|
|
20
|
+
} else {
|
|
21
|
+
const notification: INotification = {
|
|
22
|
+
type: "error",
|
|
23
|
+
text: "You are trying to paste a module that is part of a disabled content type package. To copy it, you must first activate it.",
|
|
24
|
+
btnText: "Activate package",
|
|
25
|
+
onClick: () => setHistoryPush && setHistoryPush("/sites/settings/content-types", false),
|
|
26
|
+
};
|
|
27
|
+
setNotification && setNotification(notification);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
<Tooltip content="Paste from clipboard">
|
|
34
|
+
<IconAction icon="paste" onClick={handlePasteModule} />
|
|
35
|
+
</Tooltip>
|
|
36
|
+
{isVisible && <Toast message="Module pasted from clipboard" setIsVisible={setIsVisible} />}
|
|
37
|
+
</>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
interface IProps {
|
|
42
|
+
pasteModule: (editorID: number) => Promise<{ error?: INotification }>;
|
|
43
|
+
setNotification?: (notification: INotification) => void;
|
|
44
|
+
setHistoryPush?: (path: string, isEditor: boolean) => void;
|
|
45
|
+
editorID: number;
|
|
46
|
+
isModuleCopyUnavailable: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default memo(PasteModuleButton);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import differenceInSeconds from 'date-fns/differenceInSeconds';
|
|
2
3
|
|
|
3
4
|
import { IModule } from "@ax/types";
|
|
4
5
|
import { ComponentContainer } from "@ax/components";
|
|
5
6
|
|
|
6
|
-
import AddItemButton from "./AddItemButton";
|
|
7
7
|
import { getComponentProps, containerToComponentArray, getTypefromKey } from "../helpers";
|
|
8
|
+
import AddItemButton from "./AddItemButton";
|
|
9
|
+
import PasteModuleButton from "./PasteModuleButton";
|
|
8
10
|
|
|
9
11
|
import * as S from "./style";
|
|
10
12
|
|
|
@@ -25,8 +27,22 @@ const MixableComponentArray = (props: IMixableComponentArrayProps): JSX.Element
|
|
|
25
27
|
field,
|
|
26
28
|
mandatory,
|
|
27
29
|
theme,
|
|
30
|
+
moduleCopy,
|
|
31
|
+
availableDataPacks,
|
|
32
|
+
template,
|
|
33
|
+
setHistoryPush,
|
|
28
34
|
} = props;
|
|
29
35
|
|
|
36
|
+
const moduleCopyComponent = moduleCopy?.element.component;
|
|
37
|
+
const availableDataPackModule = availableDataPacks?.reduce((prev: any, curr: any) => {
|
|
38
|
+
const packModule = curr.modules.find((module: any) => module.id === moduleCopyComponent);
|
|
39
|
+
return prev || packModule;
|
|
40
|
+
}, null);
|
|
41
|
+
const isModuleCopyUnavailable =
|
|
42
|
+
availableDataPackModule &&
|
|
43
|
+
!whiteList.includes(moduleCopyComponent) &&
|
|
44
|
+
!!availableDataPackModule?.sectionList[template.component]?.includes(field.key);
|
|
45
|
+
|
|
30
46
|
const type = getTypefromKey(objKey);
|
|
31
47
|
const { contentType = type } = field;
|
|
32
48
|
|
|
@@ -54,7 +70,16 @@ const MixableComponentArray = (props: IMixableComponentArrayProps): JSX.Element
|
|
|
54
70
|
|
|
55
71
|
const handleAdd = isModuleArr ? handleAddModule : handleAddComponent;
|
|
56
72
|
|
|
57
|
-
const showAddItemButton = !maxItems || fixedValue.length < maxItems;
|
|
73
|
+
const showAddItemButton = (!maxItems || fixedValue.length < maxItems) && !disabled;
|
|
74
|
+
|
|
75
|
+
const timeSinceModuleCopy = !!moduleCopy && differenceInSeconds(new Date(), new Date(moduleCopy.date));
|
|
76
|
+
const eightHoursInSeconds = 8 * 60 * 60;
|
|
77
|
+
const showPasteModuleButton =
|
|
78
|
+
showAddItemButton &&
|
|
79
|
+
isModuleArr &&
|
|
80
|
+
!!moduleCopy &&
|
|
81
|
+
timeSinceModuleCopy < eightHoursInSeconds &&
|
|
82
|
+
(whiteList.includes(moduleCopyComponent) || isModuleCopyUnavailable);
|
|
58
83
|
|
|
59
84
|
const Asterisk = () => (mandatory ? <S.Asterisk>*</S.Asterisk> : null);
|
|
60
85
|
|
|
@@ -65,15 +90,26 @@ const MixableComponentArray = (props: IMixableComponentArrayProps): JSX.Element
|
|
|
65
90
|
</S.Title>
|
|
66
91
|
<S.ItemRow>
|
|
67
92
|
<S.Subtitle>{fixedValue && fixedValue.length} items</S.Subtitle>
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
93
|
+
<S.ActionsWrapper>
|
|
94
|
+
{showPasteModuleButton && (
|
|
95
|
+
<PasteModuleButton
|
|
96
|
+
editorID={editorID}
|
|
97
|
+
isModuleCopyUnavailable={isModuleCopyUnavailable}
|
|
98
|
+
pasteModule={actions.pasteModuleAction}
|
|
99
|
+
setNotification={actions.setNotificationAction}
|
|
100
|
+
setHistoryPush={setHistoryPush}
|
|
101
|
+
/>
|
|
102
|
+
)}
|
|
103
|
+
{showAddItemButton && (
|
|
104
|
+
<AddItemButton
|
|
105
|
+
whiteList={whiteList}
|
|
106
|
+
categories={categories}
|
|
107
|
+
handleClick={handleAdd}
|
|
108
|
+
isModuleArr={isModuleArr}
|
|
109
|
+
theme={theme}
|
|
110
|
+
/>
|
|
111
|
+
)}
|
|
112
|
+
</S.ActionsWrapper>
|
|
77
113
|
</S.ItemRow>
|
|
78
114
|
{fixedValue &&
|
|
79
115
|
fixedValue.map((element: any, i: number) => {
|
|
@@ -125,6 +161,10 @@ export interface IMixableComponentArrayProps {
|
|
|
125
161
|
field: any;
|
|
126
162
|
mandatory?: boolean;
|
|
127
163
|
theme: string;
|
|
164
|
+
moduleCopy: { date: string; element: Record<string, any> } | null;
|
|
165
|
+
availableDataPacks: Record<string, any>[];
|
|
166
|
+
template: any;
|
|
167
|
+
setHistoryPush?: (path: string, isEditor: boolean) => void;
|
|
128
168
|
}
|
|
129
169
|
|
|
130
170
|
export default MixableComponentArray;
|
|
@@ -52,4 +52,17 @@ const Asterisk = styled.span`
|
|
|
52
52
|
color: ${(p) => p.theme.color.error};
|
|
53
53
|
`;
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
const ActionsWrapper = styled.div`
|
|
56
|
+
display: flex;
|
|
57
|
+
gap: ${(p) => p.theme.spacing.xxs};
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
Wrapper,
|
|
62
|
+
Title,
|
|
63
|
+
Component,
|
|
64
|
+
ItemRow,
|
|
65
|
+
Subtitle,
|
|
66
|
+
Asterisk,
|
|
67
|
+
ActionsWrapper
|
|
68
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import { useModal } from "@ax/hooks";
|
|
3
|
+
import { useModal, useToast } from "@ax/hooks";
|
|
4
4
|
import { isEmptyContainer, getDisplayName, trimText } from "@ax/helpers";
|
|
5
|
-
import { Icon, SideModal } from "@ax/components";
|
|
5
|
+
import { Icon, SideModal, Toast } from "@ax/components";
|
|
6
6
|
import EmptyContainer from "./EmptyContainer";
|
|
7
7
|
import { ArrayContainerButtons, ContainerButtons } from "./atoms";
|
|
8
8
|
|
|
@@ -27,17 +27,21 @@ const ComponentContainer = (props: IComponentContainerProps): JSX.Element => {
|
|
|
27
27
|
disabled,
|
|
28
28
|
canDuplicate,
|
|
29
29
|
parentKey,
|
|
30
|
-
theme
|
|
30
|
+
theme,
|
|
31
31
|
} = props;
|
|
32
32
|
|
|
33
|
+
const { isVisible, toggleToast, setIsVisible } = useToast();
|
|
34
|
+
|
|
33
35
|
let deleteModuleAction: any;
|
|
34
36
|
let moveModuleAction: any;
|
|
35
37
|
let duplicateModuleAction: any;
|
|
38
|
+
let copyModuleAction: any;
|
|
36
39
|
|
|
37
40
|
if (actions) {
|
|
38
41
|
deleteModuleAction = actions.deleteModuleAction;
|
|
39
42
|
moveModuleAction = actions.moveModuleAction;
|
|
40
43
|
duplicateModuleAction = actions.duplicateModuleAction;
|
|
44
|
+
copyModuleAction = actions.copyModuleAction;
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
const whiteListFirstItem: any = whiteList && whiteList[0];
|
|
@@ -69,6 +73,16 @@ const ComponentContainer = (props: IComponentContainerProps): JSX.Element => {
|
|
|
69
73
|
|
|
70
74
|
const removeItem = () => deleteModuleAction(editorID, parentKey);
|
|
71
75
|
const duplicateItem = () => parentKey && duplicateModuleAction(editorID, parentKey);
|
|
76
|
+
const copyItem = () => {
|
|
77
|
+
const isCopied = copyModuleAction(editorID);
|
|
78
|
+
isCopied && toggleToast();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const copyOpt = {
|
|
82
|
+
label: "copy",
|
|
83
|
+
icon: "duplicate",
|
|
84
|
+
action: copyItem,
|
|
85
|
+
};
|
|
72
86
|
|
|
73
87
|
const duplicateOpt = {
|
|
74
88
|
label: "duplicate",
|
|
@@ -82,7 +96,11 @@ const ComponentContainer = (props: IComponentContainerProps): JSX.Element => {
|
|
|
82
96
|
action: removeItem,
|
|
83
97
|
};
|
|
84
98
|
|
|
85
|
-
const actionArrayMenuOptions =
|
|
99
|
+
const actionArrayMenuOptions = [
|
|
100
|
+
copyOpt,
|
|
101
|
+
...(canDuplicate ? [duplicateOpt] : []),
|
|
102
|
+
deleteOpt
|
|
103
|
+
];
|
|
86
104
|
|
|
87
105
|
const actionMenuOptions = [
|
|
88
106
|
{
|
|
@@ -130,7 +148,7 @@ const ComponentContainer = (props: IComponentContainerProps): JSX.Element => {
|
|
|
130
148
|
/>
|
|
131
149
|
) : (
|
|
132
150
|
<>
|
|
133
|
-
<S.Component isArray={isArray} onClick={handleClick} disabled={disabled}>
|
|
151
|
+
<S.Component isArray={isArray} onClick={handleClick} disabled={disabled} className={`editorId-${editorID}`}>
|
|
134
152
|
{containerInfo && !disabled && (
|
|
135
153
|
<S.IconWrapper>
|
|
136
154
|
<Icon name={containerText} />
|
|
@@ -160,6 +178,7 @@ const ComponentContainer = (props: IComponentContainerProps): JSX.Element => {
|
|
|
160
178
|
theme={theme}
|
|
161
179
|
/>
|
|
162
180
|
)}
|
|
181
|
+
{isVisible && <Toast message="1 module copied to clipboard" setIsVisible={setIsVisible} />}
|
|
163
182
|
</>
|
|
164
183
|
);
|
|
165
184
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { memo, useEffect, useState } from "react";
|
|
2
|
-
import { Icon, IconAction, Gallery, Modal } from "@ax/components";
|
|
2
|
+
import { Icon, IconAction, Gallery, Modal, Image } from "@ax/components";
|
|
3
3
|
|
|
4
4
|
import { formatBytes, getFormattedDateWithTimezone } from "@ax/helpers";
|
|
5
5
|
import { IImage, ISite } from "@ax/types";
|
|
@@ -8,8 +8,18 @@ import { useModal } from "@ax/hooks";
|
|
|
8
8
|
import * as S from "./style";
|
|
9
9
|
|
|
10
10
|
const ImageField = (props: IImageFieldProps) => {
|
|
11
|
-
const {
|
|
12
|
-
|
|
11
|
+
const {
|
|
12
|
+
value,
|
|
13
|
+
error,
|
|
14
|
+
onChange,
|
|
15
|
+
selectedContent,
|
|
16
|
+
disabled,
|
|
17
|
+
handleValidation,
|
|
18
|
+
validators,
|
|
19
|
+
site,
|
|
20
|
+
setIsGalleryOpened,
|
|
21
|
+
cropPreview = false,
|
|
22
|
+
} = props;
|
|
13
23
|
|
|
14
24
|
const isLinkableImage = selectedContent && selectedContent.component === "LinkableImage";
|
|
15
25
|
const hasImage = value && Object.prototype.hasOwnProperty.call(value, "url");
|
|
@@ -54,6 +64,8 @@ const ImageField = (props: IImageFieldProps) => {
|
|
|
54
64
|
}
|
|
55
65
|
};
|
|
56
66
|
|
|
67
|
+
const previewHeight = cropPreview ? { height: 190 } : {};
|
|
68
|
+
|
|
57
69
|
return (
|
|
58
70
|
<>
|
|
59
71
|
<S.FieldWrapper error={error} preview={!!previewSrc} disabled={disabled} onClick={handleClick}>
|
|
@@ -73,7 +85,7 @@ const ImageField = (props: IImageFieldProps) => {
|
|
|
73
85
|
</S.ImageDataWrapper>
|
|
74
86
|
)}
|
|
75
87
|
<S.Preview preview={!!previewSrc}>
|
|
76
|
-
<
|
|
88
|
+
{previewSrc && <Image url={previewSrc} width={320} {...previewHeight} />}
|
|
77
89
|
<S.PreviewActions>
|
|
78
90
|
<IconAction icon="change" onClick={handleChange} />
|
|
79
91
|
<IconAction icon="delete" onClick={handleDelete} />
|
|
@@ -96,6 +108,7 @@ interface IImageFieldProps {
|
|
|
96
108
|
handleValidation?: (value: string, validators?: Record<string, unknown>) => void;
|
|
97
109
|
validators?: Record<string, unknown>;
|
|
98
110
|
site: ISite;
|
|
111
|
+
cropPreview?: boolean;
|
|
99
112
|
}
|
|
100
113
|
|
|
101
|
-
export default memo(ImageField);
|
|
114
|
+
export default memo(ImageField);
|