@griddo/ax 11.6.1 → 11.6.2
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 -3
- package/src/components/Browser/index.tsx +2 -1
- package/src/components/Browser/style.tsx +2 -1
- package/src/components/Fields/ComponentArray/MixableComponentArray/AddItemButton/index.tsx +1 -1
- package/src/components/Fields/ComponentArray/MixableComponentArray/index.tsx +12 -5
- package/src/components/Fields/FormContainer/FormModal/index.tsx +11 -23
- package/src/components/Fields/FormContainer/FormModal/style.tsx +2 -0
- package/src/components/SearchField/index.tsx +1 -0
- package/src/components/SideModal/SideModalOption/index.tsx +2 -3
- package/src/components/SideModal/SideModalOption/style.tsx +2 -2
- package/src/components/SideModal/index.tsx +7 -37
- package/src/containers/Forms/actions.tsx +7 -3
- package/src/containers/Forms/reducer.tsx +1 -1
- package/src/modules/Forms/FormEditor/Editor/SideModal/SectionOption/index.tsx +23 -0
- package/src/modules/Forms/FormEditor/Editor/SideModal/SectionOption/style.tsx +20 -0
- package/src/modules/Forms/FormEditor/Editor/SideModal/SideModalOption/index.tsx +34 -0
- package/src/modules/Forms/FormEditor/Editor/SideModal/SideModalOption/style.tsx +29 -0
- package/src/modules/Forms/FormEditor/Editor/SideModal/index.tsx +118 -0
- package/src/modules/Forms/FormEditor/Editor/SideModal/style.tsx +116 -0
- package/src/modules/Forms/FormEditor/Editor/index.tsx +53 -3
- package/src/modules/Forms/FormEditor/Editor/style.tsx +9 -0
- package/src/modules/Forms/FormEditor/PageBrowser/style.tsx +0 -1
- package/src/modules/MediaGallery/index.tsx +6 -0
- package/src/types/index.tsx +1 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddo/ax",
|
|
3
3
|
"description": "Griddo Author Experience",
|
|
4
|
-
"version": "11.6.
|
|
4
|
+
"version": "11.6.2",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
|
|
7
7
|
"Diego M. Béjar <diego.bejar@secuoyas.com>",
|
|
@@ -99,7 +99,6 @@
|
|
|
99
99
|
"jsdom-global": "3.0.2",
|
|
100
100
|
"lodash.isequal": "4.5.0",
|
|
101
101
|
"markdown-draft-js": "2.4.0",
|
|
102
|
-
"masonic": "4.1.0",
|
|
103
102
|
"mini-css-extract-plugin": "0.11.3",
|
|
104
103
|
"optimize-css-assets-webpack-plugin": "6.0.1",
|
|
105
104
|
"pkg-dir": "5.0.0",
|
|
@@ -224,5 +223,5 @@
|
|
|
224
223
|
"publishConfig": {
|
|
225
224
|
"access": "public"
|
|
226
225
|
},
|
|
227
|
-
"gitHead": "
|
|
226
|
+
"gitHead": "ce1be2946a2f6320ce6b82d38a3cdd6277b13000"
|
|
228
227
|
}
|
|
@@ -35,6 +35,7 @@ const Browser = (props: IBrowserProps): JSX.Element => {
|
|
|
35
35
|
const domain = window.location.origin;
|
|
36
36
|
const urlPreview = `${domain}/editor/page-preview?preview=${!!isPreview}&disabled=${!!disabled}&type=${editorType}`;
|
|
37
37
|
const isPageEditor = editorType === "page";
|
|
38
|
+
const isFormEditor = editorType === "form";
|
|
38
39
|
|
|
39
40
|
const [resolution, setResolution] = useState("desktop");
|
|
40
41
|
const { isVisible, toggleToast, setIsVisible } = useToast();
|
|
@@ -146,7 +147,7 @@ const Browser = (props: IBrowserProps): JSX.Element => {
|
|
|
146
147
|
</S.NavBar>
|
|
147
148
|
)}
|
|
148
149
|
{showIframe ? (
|
|
149
|
-
<S.FrameWrapper hasBorder={isPageEditor} data-testid="navbar-iframe-wrapper">
|
|
150
|
+
<S.FrameWrapper hasBorder={isPageEditor} isFormEditor={isFormEditor} data-testid="navbar-iframe-wrapper">
|
|
150
151
|
<iframe
|
|
151
152
|
title="Preview"
|
|
152
153
|
width={getWidth(resolution)}
|
|
@@ -46,13 +46,14 @@ const IconWrapper = styled.div<{ active?: boolean }>`
|
|
|
46
46
|
}
|
|
47
47
|
`;
|
|
48
48
|
|
|
49
|
-
const FrameWrapper = styled.div<{ hasBorder: boolean }>`
|
|
49
|
+
const FrameWrapper = styled.div<{ hasBorder: boolean; isFormEditor: boolean }>`
|
|
50
50
|
border-left: ${(p) => (p.hasBorder ? `1px solid ${p.theme.color.uiLine}` : "none")};
|
|
51
51
|
border-right: 1px solid ${(p) => p.theme.color.uiLine};
|
|
52
52
|
border-bottom: ${(p) => (p.hasBorder ? `1px solid ${p.theme.color.uiLine}` : "none")};
|
|
53
53
|
display: flex;
|
|
54
54
|
justify-content: center;
|
|
55
55
|
height: 100%;
|
|
56
|
+
padding: ${(p) => (p.isFormEditor ? p.theme.spacing.m : "0")};
|
|
56
57
|
`;
|
|
57
58
|
|
|
58
59
|
const Wrapper = styled.div`
|
|
@@ -3,7 +3,7 @@ import { IconAction, SideModal, Tooltip } from "@ax/components";
|
|
|
3
3
|
|
|
4
4
|
const AddItemButton = (props: IProps) => {
|
|
5
5
|
const { handleClick, whiteList, categories, theme, isOpen, contentType, toggleModal } = props;
|
|
6
|
-
const addAction = whiteList.length <= 1 ? () => handleClick(whiteList[0]) : toggleModal;
|
|
6
|
+
const addAction = whiteList.length <= 1 && contentType !== "fields" ? () => handleClick(whiteList[0]) : toggleModal;
|
|
7
7
|
|
|
8
8
|
const tooltip = contentType === "fields" ? "Add field" : contentType === "module" ? "Add module" : "Add component";
|
|
9
9
|
|
|
@@ -54,6 +54,7 @@ const MixableComponentArray = (props: IMixableComponentArrayProps): JSX.Element
|
|
|
54
54
|
setNotificationAction,
|
|
55
55
|
copyModuleAction,
|
|
56
56
|
duplicateModuleAction,
|
|
57
|
+
setSelectedFormFieldAction,
|
|
57
58
|
} = actions || {};
|
|
58
59
|
|
|
59
60
|
// fix for old not array values
|
|
@@ -76,7 +77,7 @@ const MixableComponentArray = (props: IMixableComponentArrayProps): JSX.Element
|
|
|
76
77
|
const isModuleArr = contentType === "modules" || contentType === "fields";
|
|
77
78
|
const isFormArr = contentType === "fields";
|
|
78
79
|
|
|
79
|
-
const { isOpen, toggleModal } = useModal(
|
|
80
|
+
const { isOpen, toggleModal } = useModal(false, false);
|
|
80
81
|
const [isBulkOpen, setIsBulkOpen] = useState(false);
|
|
81
82
|
const [draggingId, setDraggingId] = useState<number | null>(null);
|
|
82
83
|
const { isVisible, toggleToast, setIsVisible, state: toastState } = useToast();
|
|
@@ -123,10 +124,15 @@ const MixableComponentArray = (props: IMixableComponentArrayProps): JSX.Element
|
|
|
123
124
|
|
|
124
125
|
const selectItems = () => (checkState.isAllSelected ? resetBulkSelection() : selectAllItems());
|
|
125
126
|
|
|
126
|
-
const
|
|
127
|
+
const handleToggleModal = () =>
|
|
128
|
+
isFormArr && setSelectedFormFieldAction ? setSelectedFormFieldAction(field) : toggleModal();
|
|
129
|
+
|
|
130
|
+
const isAbletoAdd = (!maxItems || fixedValue.length < maxItems) && !disabled && whiteList.length > 0;
|
|
131
|
+
|
|
132
|
+
const showAddItemButton = isAbletoAdd && (!isFormArr || (isFormArr && !!setSelectedFormFieldAction));
|
|
127
133
|
|
|
128
134
|
const showPasteModuleButton =
|
|
129
|
-
|
|
135
|
+
isAbletoAdd &&
|
|
130
136
|
(isModuleArr || objKey === "componentModules") &&
|
|
131
137
|
(modulesToPaste.length > 0 || unavailableModules.length > 0);
|
|
132
138
|
|
|
@@ -167,7 +173,7 @@ const MixableComponentArray = (props: IMixableComponentArrayProps): JSX.Element
|
|
|
167
173
|
actions={actions}
|
|
168
174
|
selectedContent={selectedContent}
|
|
169
175
|
disabled={disabled || isModuleDisabled}
|
|
170
|
-
canDuplicate={
|
|
176
|
+
canDuplicate={isAbletoAdd && !isModuleDeactivated}
|
|
171
177
|
canDelete={canDelete}
|
|
172
178
|
parentKey={objKey}
|
|
173
179
|
theme={theme}
|
|
@@ -293,7 +299,7 @@ const MixableComponentArray = (props: IMixableComponentArrayProps): JSX.Element
|
|
|
293
299
|
{showAddItemButton && !disabled && (
|
|
294
300
|
<AddItemButton
|
|
295
301
|
isOpen={isOpen}
|
|
296
|
-
toggleModal={
|
|
302
|
+
toggleModal={handleToggleModal}
|
|
297
303
|
whiteList={whiteList}
|
|
298
304
|
categories={categories}
|
|
299
305
|
handleClick={handleAdd}
|
|
@@ -351,6 +357,7 @@ export interface IMixableComponentArrayProps {
|
|
|
351
357
|
pasteModuleAction: (editorID: number, key: string, modulesToPaste: IModule[]) => Promise<{ error?: INotification }>;
|
|
352
358
|
setNotificationAction: (notification: INotification) => void;
|
|
353
359
|
replaceModuleAction: (module: any, parent: any, objKey: string) => void;
|
|
360
|
+
setSelectedFormFieldAction: (field: ISchemaField | null) => void;
|
|
354
361
|
};
|
|
355
362
|
categories?: any[];
|
|
356
363
|
disabled?: boolean;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import React, { Dispatch, SetStateAction, useCallback, useEffect, useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
|
-
import { Masonry, RenderComponentProps } from "masonic";
|
|
4
|
-
import { v4 as uuidv4 } from "uuid";
|
|
5
3
|
|
|
6
4
|
import { FormContent, GetFormsParams, IModal, IQueryValue, IRootState, ISite } from "@ax/types";
|
|
7
5
|
import {
|
|
@@ -110,19 +108,6 @@ const FormModal = (props: IFormModal): JSX.Element => {
|
|
|
110
108
|
</S.Filters>
|
|
111
109
|
);
|
|
112
110
|
|
|
113
|
-
const MasonryCard = (props: RenderComponentProps<FormContent>) => {
|
|
114
|
-
const { data: form } = props;
|
|
115
|
-
const handleClick = () => setSelectedForm(form);
|
|
116
|
-
return (
|
|
117
|
-
<S.FormWrapper key={form.id}>
|
|
118
|
-
<S.FormItem onClick={handleClick} isSelected={form.id === selectedForm?.id}>
|
|
119
|
-
<img src={form.thumbnail} alt={form.title} title={form.title} />
|
|
120
|
-
</S.FormItem>
|
|
121
|
-
<S.FormTitle>{form.title}</S.FormTitle>
|
|
122
|
-
</S.FormWrapper>
|
|
123
|
-
);
|
|
124
|
-
};
|
|
125
|
-
|
|
126
111
|
const emptySearchStateProps = {
|
|
127
112
|
icon: "search",
|
|
128
113
|
title: "Oh! No Results Found",
|
|
@@ -174,14 +159,17 @@ const FormModal = (props: IFormModal): JSX.Element => {
|
|
|
174
159
|
</S.EmptyWrapper>
|
|
175
160
|
) : (
|
|
176
161
|
<S.FormsGrid>
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
162
|
+
{forms.map((form) => {
|
|
163
|
+
const handleClick = () => setSelectedForm(form);
|
|
164
|
+
return (
|
|
165
|
+
<S.FormWrapper key={form.id}>
|
|
166
|
+
<S.FormItem onClick={handleClick} isSelected={form.id === selectedForm?.id}>
|
|
167
|
+
<img src={form.thumbnail} alt={form.title} title={form.title} />
|
|
168
|
+
</S.FormItem>
|
|
169
|
+
<S.FormTitle>{form.title}</S.FormTitle>
|
|
170
|
+
</S.FormWrapper>
|
|
171
|
+
);
|
|
172
|
+
})}
|
|
185
173
|
</S.FormsGrid>
|
|
186
174
|
)}
|
|
187
175
|
</S.ModalContent>
|
|
@@ -69,12 +69,14 @@ const TabsWrapper = styled.div`
|
|
|
69
69
|
|
|
70
70
|
const FormsGrid = styled.div`
|
|
71
71
|
padding-bottom: ${(p) => p.theme.spacing.l};
|
|
72
|
+
columns: 300px;
|
|
72
73
|
`;
|
|
73
74
|
|
|
74
75
|
const FormWrapper = styled.div`
|
|
75
76
|
display: flex;
|
|
76
77
|
flex-direction: column;
|
|
77
78
|
padding-bottom: ${(p) => p.theme.spacing.s};
|
|
79
|
+
break-inside: avoid-column;
|
|
78
80
|
`;
|
|
79
81
|
|
|
80
82
|
const FormItem = styled.div<{ isSelected: boolean }>`
|
|
@@ -100,6 +100,7 @@ const SearchField = (props: ISearchFieldProps): JSX.Element => {
|
|
|
100
100
|
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
101
101
|
autoFocus={focus && showField}
|
|
102
102
|
inputSize={size}
|
|
103
|
+
name="searchInput"
|
|
103
104
|
/>
|
|
104
105
|
{inputValue.trim() !== "" && searchOnEnter && <S.HelpText>Press ENTER</S.HelpText>}
|
|
105
106
|
{closeOnInactive || inputValue.length > 0 ? (
|
|
@@ -12,7 +12,7 @@ const getThumbnailData = (option: any, theme: string) => {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
const SideModalOption = (props: IProps) => {
|
|
15
|
-
const { option, handleClick, theme
|
|
15
|
+
const { option, handleClick, theme } = props;
|
|
16
16
|
|
|
17
17
|
const isNavigationDefault = ["header", "footer"].includes(option.type);
|
|
18
18
|
|
|
@@ -36,7 +36,7 @@ const SideModalOption = (props: IProps) => {
|
|
|
36
36
|
) : null;
|
|
37
37
|
|
|
38
38
|
return (
|
|
39
|
-
<S.Item onClick={setOption}
|
|
39
|
+
<S.Item onClick={setOption} data-testid="side-modal-option">
|
|
40
40
|
<S.Thumbnail data-testid="side-modal-option-img" {...thumbnailProps} />
|
|
41
41
|
{label}
|
|
42
42
|
{defaultTag}
|
|
@@ -49,7 +49,6 @@ interface IProps {
|
|
|
49
49
|
handleClick?: (option: any) => void;
|
|
50
50
|
children: string;
|
|
51
51
|
theme: string;
|
|
52
|
-
smallMargin: boolean;
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
export default memo(SideModalOption);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import styled from "styled-components";
|
|
2
2
|
|
|
3
|
-
const Item = styled.li
|
|
3
|
+
const Item = styled.li`
|
|
4
4
|
cursor: pointer;
|
|
5
5
|
padding: ${(p) => p.theme.spacing.xs};
|
|
6
6
|
padding-bottom: ${(p) => p.theme.spacing.s};
|
|
7
|
-
margin-bottom: ${(p) =>
|
|
7
|
+
margin-bottom: ${(p) => p.theme.spacing.s};
|
|
8
8
|
box-shadow: ${(p) => p.theme.shadow.shadowS};
|
|
9
9
|
border-radius: ${(p) => p.theme.radii.s};
|
|
10
10
|
${(p) => p.theme.textStyle.uiS};
|
|
@@ -4,7 +4,7 @@ import { createPortal } from "react-dom";
|
|
|
4
4
|
import { useHandleClickOutside } from "@ax/hooks";
|
|
5
5
|
import { getDisplayName, filterByCategory } from "@ax/helpers";
|
|
6
6
|
import SideModalOption from "@ax/components/SideModal/SideModalOption";
|
|
7
|
-
import { MenuItem, SearchField, IconAction
|
|
7
|
+
import { MenuItem, SearchField, IconAction } from "@ax/components";
|
|
8
8
|
import { ModuleCategoryInfo } from "@ax/types";
|
|
9
9
|
|
|
10
10
|
import * as S from "./style";
|
|
@@ -25,7 +25,6 @@ const SideModal = (props: ISideModalProps): JSX.Element | null => {
|
|
|
25
25
|
|
|
26
26
|
const componentList: any = [];
|
|
27
27
|
const hasCategories = categories && categories.length > 1;
|
|
28
|
-
const hasFieldsStyle = optionsType === "fields";
|
|
29
28
|
|
|
30
29
|
const optionList = componentOptions ? componentList : whiteList;
|
|
31
30
|
|
|
@@ -63,7 +62,7 @@ const SideModal = (props: ISideModalProps): JSX.Element | null => {
|
|
|
63
62
|
if (onClick) {
|
|
64
63
|
onClick(moduleType);
|
|
65
64
|
}
|
|
66
|
-
|
|
65
|
+
handleCloseModal();
|
|
67
66
|
};
|
|
68
67
|
|
|
69
68
|
const handleCloseModal = () => {
|
|
@@ -72,7 +71,7 @@ const SideModal = (props: ISideModalProps): JSX.Element | null => {
|
|
|
72
71
|
};
|
|
73
72
|
|
|
74
73
|
const handleClickOutside = (e: any) => {
|
|
75
|
-
if (node.current?.contains(e.target)
|
|
74
|
+
if (node.current?.contains(e.target)) {
|
|
76
75
|
return;
|
|
77
76
|
}
|
|
78
77
|
|
|
@@ -137,13 +136,7 @@ const SideModal = (props: ISideModalProps): JSX.Element | null => {
|
|
|
137
136
|
if (!name.includes(search)) return null;
|
|
138
137
|
}
|
|
139
138
|
return (
|
|
140
|
-
<SideModalOption
|
|
141
|
-
option={option}
|
|
142
|
-
handleClick={handleClick}
|
|
143
|
-
key={`${option}${i}`}
|
|
144
|
-
theme={theme}
|
|
145
|
-
smallMargin={hasFieldsStyle}
|
|
146
|
-
>
|
|
139
|
+
<SideModalOption option={option} handleClick={handleClick} key={`${option}${i}`} theme={theme}>
|
|
147
140
|
{displayName}
|
|
148
141
|
</SideModalOption>
|
|
149
142
|
);
|
|
@@ -151,23 +144,12 @@ const SideModal = (props: ISideModalProps): JSX.Element | null => {
|
|
|
151
144
|
|
|
152
145
|
return createPortal(
|
|
153
146
|
<>
|
|
154
|
-
<S.Wrapper
|
|
155
|
-
ref={node}
|
|
156
|
-
optionsType={optionsType}
|
|
157
|
-
isOpen={isOpen}
|
|
158
|
-
data-testid="side-modal"
|
|
159
|
-
className={hasFieldsStyle ? "form-fields" : ""}
|
|
160
|
-
>
|
|
147
|
+
<S.Wrapper ref={node} optionsType={optionsType} isOpen={isOpen} data-testid="side-modal">
|
|
161
148
|
<S.Header>
|
|
162
|
-
|
|
149
|
+
<S.Title data-testid="side-modal-title">{optionsType}</S.Title>
|
|
163
150
|
{showSearch && optionsType !== "components" && (
|
|
164
151
|
<S.SearchWrapper>
|
|
165
|
-
<SearchField
|
|
166
|
-
onChange={setSearchQuery}
|
|
167
|
-
closeOnInactive={!hasFieldsStyle}
|
|
168
|
-
small={!filters}
|
|
169
|
-
placeholder={hasFieldsStyle ? "Search field" : ""}
|
|
170
|
-
/>
|
|
152
|
+
<SearchField onChange={setSearchQuery} closeOnInactive={true} small={!filters} value={searchQuery} />
|
|
171
153
|
</S.SearchWrapper>
|
|
172
154
|
)}
|
|
173
155
|
{!showSearch && (
|
|
@@ -177,11 +159,6 @@ const SideModal = (props: ISideModalProps): JSX.Element | null => {
|
|
|
177
159
|
)}
|
|
178
160
|
</S.Header>
|
|
179
161
|
<S.ColumnsWrapper>
|
|
180
|
-
{hasFieldsStyle && (
|
|
181
|
-
<S.FloatingButtonWrapper>
|
|
182
|
-
<FloatingButton icon="leftArrow" size="S" onClick={handleCloseModal} />
|
|
183
|
-
</S.FloatingButtonWrapper>
|
|
184
|
-
)}
|
|
185
162
|
{(filters || featuredFilters) && (
|
|
186
163
|
<S.Content>
|
|
187
164
|
{featuredFilters && <S.FeaturedWrapper>{featuredFilters}</S.FeaturedWrapper>}
|
|
@@ -191,13 +168,6 @@ const SideModal = (props: ISideModalProps): JSX.Element | null => {
|
|
|
191
168
|
<S.Content>{filteredOptions}</S.Content>
|
|
192
169
|
</S.ColumnsWrapper>
|
|
193
170
|
</S.Wrapper>
|
|
194
|
-
{hasFieldsStyle && (
|
|
195
|
-
<S.ClosedWrapper isOpen={!isOpen}>
|
|
196
|
-
<S.ClosedFloatingButtonWrapper>
|
|
197
|
-
<FloatingButton text="fields" icon="rightArrow" size="S" onClick={toggleModal} />
|
|
198
|
-
</S.ClosedFloatingButtonWrapper>
|
|
199
|
-
</S.ClosedWrapper>
|
|
200
|
-
)}
|
|
201
171
|
</>,
|
|
202
172
|
document.body
|
|
203
173
|
);
|
|
@@ -597,9 +597,13 @@ function moveModule(
|
|
|
597
597
|
if (!formContent) return;
|
|
598
598
|
|
|
599
599
|
const templateContent = formContent.template;
|
|
600
|
+
const isRoot = selectedContent.component === "FormPage";
|
|
601
|
+
|
|
602
|
+
const contentElements = isRoot ? [...selectedContent.template[key]] : [...selectedContent[key]];
|
|
603
|
+
const editorID = isRoot ? templateContent.editorID : selectedContent.editorID;
|
|
604
|
+
|
|
605
|
+
const { element: selectedModule } = findByEditorID(templateContent, editorID);
|
|
600
606
|
|
|
601
|
-
const contentElements = [...(selectedContent as any).template[key]];
|
|
602
|
-
const { element: selectedModule } = findByEditorID(templateContent, templateContent.editorID);
|
|
603
607
|
selectedModule[key] = moveElement(elementID, contentElements, newIndex);
|
|
604
608
|
|
|
605
609
|
const updatedContent = {
|
|
@@ -610,7 +614,7 @@ function moveModule(
|
|
|
610
614
|
};
|
|
611
615
|
|
|
612
616
|
dispatch(setFormContent(updatedContent));
|
|
613
|
-
dispatch(setSelectedFormContent(updatedContent));
|
|
617
|
+
dispatch(setSelectedFormContent(isRoot ? updatedContent : selectedModule));
|
|
614
618
|
};
|
|
615
619
|
}
|
|
616
620
|
|
|
@@ -25,7 +25,7 @@ export interface IFormsState {
|
|
|
25
25
|
tab: string;
|
|
26
26
|
currentFormID: number | null;
|
|
27
27
|
currentFormStatus: string | null;
|
|
28
|
-
selectedContent: Record<string,
|
|
28
|
+
selectedContent: Record<string, any>;
|
|
29
29
|
selectedEditorID: number;
|
|
30
30
|
selectedParent: Record<string, unknown> | null;
|
|
31
31
|
breadcrumb: IBreadcrumbItem[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React, { memo } from "react";
|
|
2
|
+
import { ISchemaField } from "@ax/types";
|
|
3
|
+
|
|
4
|
+
import * as S from "./style";
|
|
5
|
+
|
|
6
|
+
const SectionOption = (props: IProps) => {
|
|
7
|
+
const { option, handleClick } = props;
|
|
8
|
+
|
|
9
|
+
const setOption = () => handleClick(option);
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<S.Item onClick={setOption} data-testid="section-option">
|
|
13
|
+
Fields for {option.title}
|
|
14
|
+
</S.Item>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
interface IProps {
|
|
19
|
+
option: ISchemaField;
|
|
20
|
+
handleClick: (option: ISchemaField) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default memo(SectionOption);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
|
|
3
|
+
const Item = styled.li`
|
|
4
|
+
${(p) => p.theme.textStyle.uiS};
|
|
5
|
+
cursor: pointer;
|
|
6
|
+
padding: ${(p) => p.theme.spacing.s};
|
|
7
|
+
padding-bottom: ${(p) => p.theme.spacing.s};
|
|
8
|
+
margin-bottom: ${(p) => p.theme.spacing.xs};
|
|
9
|
+
box-shadow: ${(p) => p.theme.shadow.shadowS};
|
|
10
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
11
|
+
background-color: ${(p) => p.theme.color.interactiveBackground};
|
|
12
|
+
&:hover {
|
|
13
|
+
background: ${(p) => p.theme.color.overlayHoverPrimary};
|
|
14
|
+
}
|
|
15
|
+
&:focus {
|
|
16
|
+
background-color: ${(p) => p.theme.color.overlayFocusPrimary};
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
export { Item };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React, { memo } from "react";
|
|
2
|
+
|
|
3
|
+
import { getDisplayName, getThumbnailProps, filterImageText } from "@ax/helpers";
|
|
4
|
+
|
|
5
|
+
import * as S from "./style";
|
|
6
|
+
|
|
7
|
+
const getThumbnailData = (option: string, theme: string) => {
|
|
8
|
+
const optionText = filterImageText(option);
|
|
9
|
+
return getThumbnailProps(optionText, false, theme);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const SideModalOption = (props: IProps) => {
|
|
13
|
+
const { option, handleClick, theme } = props;
|
|
14
|
+
const thumbnailProps = getThumbnailData(option, theme);
|
|
15
|
+
const label = getDisplayName(option);
|
|
16
|
+
|
|
17
|
+
const setOption = () => handleClick(option);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<S.Item onClick={setOption} data-testid="side-modal-option">
|
|
21
|
+
<S.Thumbnail data-testid="side-modal-option-img" {...thumbnailProps} />
|
|
22
|
+
{label}
|
|
23
|
+
</S.Item>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
interface IProps {
|
|
28
|
+
option: string;
|
|
29
|
+
handleClick: (option: string) => void;
|
|
30
|
+
children: string;
|
|
31
|
+
theme: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default memo(SideModalOption);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
|
|
3
|
+
const Item = styled.li`
|
|
4
|
+
cursor: pointer;
|
|
5
|
+
padding: ${(p) => p.theme.spacing.xs};
|
|
6
|
+
padding-bottom: ${(p) => p.theme.spacing.s};
|
|
7
|
+
margin-bottom: ${(p) => p.theme.spacing.xs};
|
|
8
|
+
box-shadow: ${(p) => p.theme.shadow.shadowS};
|
|
9
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
10
|
+
${(p) => p.theme.textStyle.uiS};
|
|
11
|
+
background-color: ${(p) => p.theme.color.interactiveBackground};
|
|
12
|
+
&:hover {
|
|
13
|
+
background: ${(p) => p.theme.color.overlayHoverPrimary};
|
|
14
|
+
}
|
|
15
|
+
&:focus {
|
|
16
|
+
background-color: ${(p) => p.theme.color.overlayFocusPrimary};
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const Thumbnail = styled.img`
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
padding-bottom: ${(p) => p.theme.spacing.s};
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
const TagWrapper = styled.div`
|
|
26
|
+
margin-top: ${(p) => p.theme.spacing.xs};
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
export { Item, Thumbnail, TagWrapper };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { getDisplayName } from "@ax/helpers";
|
|
4
|
+
import { SearchField, FloatingButton, NoteField } from "@ax/components";
|
|
5
|
+
import { ISchemaField } from "@ax/types";
|
|
6
|
+
|
|
7
|
+
import SideModalOption from "./SideModalOption";
|
|
8
|
+
import SectionOption from "./SectionOption";
|
|
9
|
+
|
|
10
|
+
import * as S from "./style";
|
|
11
|
+
|
|
12
|
+
const SideModal = (props: ISideModalProps): JSX.Element | null => {
|
|
13
|
+
const { onClick, theme, fieldArrays, selectedField, selectField } = props;
|
|
14
|
+
|
|
15
|
+
const isMultiArray = fieldArrays.length > 1;
|
|
16
|
+
|
|
17
|
+
const [isOpen, setIsOpen] = useState(true);
|
|
18
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
19
|
+
|
|
20
|
+
const handleClick = (option: string) => onClick(option);
|
|
21
|
+
|
|
22
|
+
const handleSectionClick = (option: ISchemaField) => selectField(option);
|
|
23
|
+
const handleBackClick = () => selectField(null);
|
|
24
|
+
|
|
25
|
+
const toggleOpen = () => {
|
|
26
|
+
setSearchQuery("");
|
|
27
|
+
setIsOpen((state) => !state);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const filteredOptions = selectedField?.whiteList?.map((option: string, i: number) => {
|
|
31
|
+
const displayName = getDisplayName(option);
|
|
32
|
+
if (searchQuery.length > 0) {
|
|
33
|
+
const name = displayName.toLowerCase();
|
|
34
|
+
const search = searchQuery.toLocaleLowerCase();
|
|
35
|
+
if (!name.includes(search)) return <></>;
|
|
36
|
+
}
|
|
37
|
+
return (
|
|
38
|
+
<SideModalOption option={option} handleClick={handleClick} key={`${option}${i}`} theme={theme}>
|
|
39
|
+
{displayName}
|
|
40
|
+
</SideModalOption>
|
|
41
|
+
);
|
|
42
|
+
}) || <></>;
|
|
43
|
+
|
|
44
|
+
const sectionOptions = fieldArrays.map((option) => (
|
|
45
|
+
<SectionOption option={option} handleClick={handleSectionClick} key={option.key} />
|
|
46
|
+
));
|
|
47
|
+
|
|
48
|
+
const noteText = {
|
|
49
|
+
text: (
|
|
50
|
+
<>
|
|
51
|
+
Select <strong>which section</strong> you want to add the fields to.
|
|
52
|
+
</>
|
|
53
|
+
),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<>
|
|
58
|
+
<S.Wrapper isOpen={isOpen} data-testid="side-modal" className="form-fields">
|
|
59
|
+
{isMultiArray && !selectedField ? (
|
|
60
|
+
<>
|
|
61
|
+
<S.Header>
|
|
62
|
+
<NoteField value={noteText} />
|
|
63
|
+
</S.Header>
|
|
64
|
+
<S.ColumnsWrapper>
|
|
65
|
+
<S.FloatingButtonWrapper>
|
|
66
|
+
{isOpen && <FloatingButton icon="leftArrow" size="S" onClick={toggleOpen} />}
|
|
67
|
+
</S.FloatingButtonWrapper>
|
|
68
|
+
<S.Content>{sectionOptions}</S.Content>
|
|
69
|
+
</S.ColumnsWrapper>
|
|
70
|
+
</>
|
|
71
|
+
) : (
|
|
72
|
+
<>
|
|
73
|
+
<S.Header>
|
|
74
|
+
{isMultiArray && selectedField && (
|
|
75
|
+
<S.BreadCrumb>
|
|
76
|
+
<S.BreadCrumbItem isLastItem={false} onClick={handleBackClick}>
|
|
77
|
+
Back
|
|
78
|
+
</S.BreadCrumbItem>
|
|
79
|
+
/<S.BreadCrumbItem isLastItem={true}>{selectedField.title}</S.BreadCrumbItem>
|
|
80
|
+
</S.BreadCrumb>
|
|
81
|
+
)}
|
|
82
|
+
<S.SearchWrapper>
|
|
83
|
+
<SearchField
|
|
84
|
+
onChange={setSearchQuery}
|
|
85
|
+
value={searchQuery}
|
|
86
|
+
closeOnInactive={false}
|
|
87
|
+
small={true}
|
|
88
|
+
placeholder="Search field"
|
|
89
|
+
/>
|
|
90
|
+
</S.SearchWrapper>
|
|
91
|
+
</S.Header>
|
|
92
|
+
<S.ColumnsWrapper>
|
|
93
|
+
<S.FloatingButtonWrapper>
|
|
94
|
+
{isOpen && <FloatingButton icon="leftArrow" size="S" onClick={toggleOpen} />}
|
|
95
|
+
</S.FloatingButtonWrapper>
|
|
96
|
+
<S.Content>{filteredOptions}</S.Content>
|
|
97
|
+
</S.ColumnsWrapper>
|
|
98
|
+
</>
|
|
99
|
+
)}
|
|
100
|
+
</S.Wrapper>
|
|
101
|
+
<S.ClosedWrapper isOpen={!isOpen}>
|
|
102
|
+
<S.ClosedFloatingButtonWrapper>
|
|
103
|
+
{!isOpen && <FloatingButton text="fields" icon="rightArrow" size="S" onClick={toggleOpen} />}
|
|
104
|
+
</S.ClosedFloatingButtonWrapper>
|
|
105
|
+
</S.ClosedWrapper>
|
|
106
|
+
</>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export interface ISideModalProps {
|
|
111
|
+
onClick: (option: string) => void;
|
|
112
|
+
theme: string;
|
|
113
|
+
fieldArrays: ISchemaField[];
|
|
114
|
+
selectedField: ISchemaField | null;
|
|
115
|
+
selectField: (field: ISchemaField | null) => void;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export default SideModal;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
|
|
3
|
+
const Header = styled.div`
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
width: 100%;
|
|
7
|
+
padding: ${(p) => `${p.theme.spacing.s} ${p.theme.spacing.s} 0`};
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
const Content = styled.div`
|
|
11
|
+
list-style: none;
|
|
12
|
+
padding: ${(p) => p.theme.spacing.s};
|
|
13
|
+
height: ${(p) => `calc(100vh - ${p.theme.spacing.xl})`};
|
|
14
|
+
width: ${(p) => `calc(${p.theme.spacing.xl} * 3)`};
|
|
15
|
+
overflow: auto;
|
|
16
|
+
border-right: 1px solid ${(p) => p.theme.colors.uiLine};
|
|
17
|
+
&:last-child {
|
|
18
|
+
border-right: 0;
|
|
19
|
+
width: ${(p) => `calc(${p.theme.spacing.xl} * 4)`};
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
const Wrapper = styled.div<{ isOpen: boolean }>`
|
|
24
|
+
position: relative;
|
|
25
|
+
height: 100%;
|
|
26
|
+
background: ${(p) => p.theme.colors.uiBackground01};
|
|
27
|
+
box-shadow: ${(p) => p.theme.shadow.leftPanel};
|
|
28
|
+
margin-left: ${(p) => (p.isOpen ? "0" : "-256px")};
|
|
29
|
+
opacity: ${(p) => (p.isOpen ? "1" : "0")};
|
|
30
|
+
z-index: 1000;
|
|
31
|
+
width: 256px;
|
|
32
|
+
flex-shrink: 0;
|
|
33
|
+
transition: ${(p) =>
|
|
34
|
+
!p.isOpen
|
|
35
|
+
? "margin-left 0.5s ease-in-out 0s, opacity 0.1s ease 0.5s"
|
|
36
|
+
: "opacity 0.1s ease, margin-left 0.5s ease-in-out"};
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const ColumnsWrapper = styled.div`
|
|
40
|
+
display: flex;
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
const SearchWrapper = styled.div`
|
|
44
|
+
width: 100%;
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
const FloatingButtonWrapper = styled.div`
|
|
48
|
+
position: absolute;
|
|
49
|
+
right: -16px;
|
|
50
|
+
top: 60px;
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const ClosedWrapper = styled.div<{ isOpen: boolean }>`
|
|
54
|
+
position: relative;
|
|
55
|
+
background-color: ${(p) => p.theme.colors.uiBackground02};
|
|
56
|
+
width: ${(p) => p.theme.spacing.xs};
|
|
57
|
+
border-right: ${(p) => `2px solid ${p.theme.colors.interactive01}`};
|
|
58
|
+
height: 100vh;
|
|
59
|
+
margin-left: ${(p) => (p.isOpen ? "0" : "-100px")};
|
|
60
|
+
display: ${(p) => (p.isOpen ? "flex" : "none")};
|
|
61
|
+
transition: display 0.1s ease 1s;
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
const ClosedFloatingButtonWrapper = styled.div`
|
|
65
|
+
position: absolute;
|
|
66
|
+
right: -77px;
|
|
67
|
+
top: 56px;
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
const BreadCrumb = styled.div`
|
|
71
|
+
${(p) => p.theme.textStyle.uiS};
|
|
72
|
+
color: ${(p) => p.theme.color.textMediumEmphasis};
|
|
73
|
+
margin-bottom: ${(p) => p.theme.spacing.s};
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
const BreadCrumbItem = styled.button<{ isLastItem: boolean }>`
|
|
77
|
+
border: none;
|
|
78
|
+
${(p) => p.theme.textStyle.uiS};
|
|
79
|
+
color: ${(p) => (p.isLastItem ? p.theme.color.textMediumEmphasis : p.theme.color.interactive01)};
|
|
80
|
+
background: transparent;
|
|
81
|
+
padding: 0 ${(p) => p.theme.spacing.xxs};
|
|
82
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
83
|
+
cursor: ${(p) => (p.isLastItem ? "default" : "pointer")};
|
|
84
|
+
border: 1px solid transparent;
|
|
85
|
+
|
|
86
|
+
&:active,
|
|
87
|
+
&:focus {
|
|
88
|
+
outline: none;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
&:focus {
|
|
92
|
+
border: ${(p) => (p.isLastItem ? "none" : `1px solid ${p.theme.color.interactive01}`)};
|
|
93
|
+
background: ${(p) => (p.isLastItem ? "none" : p.theme.color.overlayFocusPrimary)};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
&:active {
|
|
97
|
+
background: ${(p) => (p.isLastItem ? "none" : p.theme.color.overlayPressedPrimary)};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
&:hover {
|
|
101
|
+
background: ${(p) => (p.isLastItem ? "none" : p.theme.color.overlayHoverPrimary)};
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
|
|
105
|
+
export {
|
|
106
|
+
Wrapper,
|
|
107
|
+
Content,
|
|
108
|
+
Header,
|
|
109
|
+
ColumnsWrapper,
|
|
110
|
+
SearchWrapper,
|
|
111
|
+
FloatingButtonWrapper,
|
|
112
|
+
ClosedWrapper,
|
|
113
|
+
ClosedFloatingButtonWrapper,
|
|
114
|
+
BreadCrumb,
|
|
115
|
+
BreadCrumbItem,
|
|
116
|
+
};
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
import { ResizePanel } from "@ax/components";
|
|
4
|
-
import { IBreadcrumbItem, IErrorItem, IModule, INotification, IRootState, ISchema } from "@ax/types";
|
|
4
|
+
import { IBreadcrumbItem, IErrorItem, IModule, INotification, IRootState, ISchema, ISchemaField } from "@ax/types";
|
|
5
5
|
import { formsActions } from "@ax/containers/Forms";
|
|
6
|
+
import { getFormTemplate } from "@ax/helpers";
|
|
6
7
|
import PageBrowser from "../PageBrowser";
|
|
7
8
|
import FormConfigPanel from "./FormConfigPanel";
|
|
9
|
+
import SideModal from "./SideModal";
|
|
10
|
+
|
|
11
|
+
import * as S from "./style";
|
|
8
12
|
|
|
9
13
|
const Editor = (props: IProps) => {
|
|
10
14
|
const {
|
|
@@ -17,6 +21,7 @@ const Editor = (props: IProps) => {
|
|
|
17
21
|
breadcrumb,
|
|
18
22
|
selectedParent,
|
|
19
23
|
errors,
|
|
24
|
+
template,
|
|
20
25
|
setErrors,
|
|
21
26
|
setNotification,
|
|
22
27
|
setSelectedTab,
|
|
@@ -29,6 +34,35 @@ const Editor = (props: IProps) => {
|
|
|
29
34
|
pasteModule,
|
|
30
35
|
} = props;
|
|
31
36
|
|
|
37
|
+
const [selectedField, setSelectedField] = useState<ISchemaField | null>(null);
|
|
38
|
+
const [fieldArrays, setFieldArrays] = useState<ISchemaField[]>([]);
|
|
39
|
+
|
|
40
|
+
const hasFieldArrays = !!fieldArrays && fieldArrays.length > 0;
|
|
41
|
+
const isMultiArray = fieldArrays.length > 1;
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const getFormFieldArrays = (schema: ISchema | Record<string, never>) => {
|
|
45
|
+
let content: ISchemaField[] = [];
|
|
46
|
+
if (schema.component === "FormPage") {
|
|
47
|
+
const templateSchema = getFormTemplate(template);
|
|
48
|
+
content = templateSchema?.content || [];
|
|
49
|
+
} else {
|
|
50
|
+
content = schema.configTabs[0].fields;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return content.filter((field: ISchemaField) => field.type === "FormFieldArray");
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const fields = getFormFieldArrays(schema);
|
|
57
|
+
const initState = fields.length === 1 ? fields[0] : null;
|
|
58
|
+
setFieldArrays(fields);
|
|
59
|
+
setSelectedField(initState);
|
|
60
|
+
}, [schema, template]);
|
|
61
|
+
|
|
62
|
+
const handleClick = (fieldType: string) => !!selectedField && addModule(fieldType, selectedField.key);
|
|
63
|
+
|
|
64
|
+
const handleSetSelectedField = (field: ISchemaField | null) => setSelectedField(field);
|
|
65
|
+
|
|
32
66
|
const actions = {
|
|
33
67
|
setNotificationAction: setNotification,
|
|
34
68
|
addModuleAction: addModule,
|
|
@@ -37,11 +71,25 @@ const Editor = (props: IProps) => {
|
|
|
37
71
|
moveModuleAction: moveModule,
|
|
38
72
|
copyModuleAction: copyModule,
|
|
39
73
|
pasteModuleAction: pasteModule,
|
|
74
|
+
setSelectedFormFieldAction: isMultiArray ? handleSetSelectedField : undefined,
|
|
40
75
|
};
|
|
41
76
|
|
|
42
77
|
return (
|
|
43
78
|
<ResizePanel
|
|
44
|
-
leftPanel={
|
|
79
|
+
leftPanel={
|
|
80
|
+
<S.EditorWrapper>
|
|
81
|
+
{hasFieldArrays && (
|
|
82
|
+
<SideModal
|
|
83
|
+
onClick={handleClick}
|
|
84
|
+
theme={theme}
|
|
85
|
+
fieldArrays={fieldArrays}
|
|
86
|
+
selectedField={selectedField}
|
|
87
|
+
selectField={handleSetSelectedField}
|
|
88
|
+
/>
|
|
89
|
+
)}
|
|
90
|
+
<PageBrowser isReadOnly={false} theme={theme} browserRef={browserRef} />
|
|
91
|
+
</S.EditorWrapper>
|
|
92
|
+
}
|
|
45
93
|
rightPanel={
|
|
46
94
|
<FormConfigPanel
|
|
47
95
|
schema={schema}
|
|
@@ -69,6 +117,7 @@ interface IEditorStateProps {
|
|
|
69
117
|
schema: ISchema | Record<string, never>;
|
|
70
118
|
breadcrumb: IBreadcrumbItem[];
|
|
71
119
|
selectedParent: Record<string, unknown> | null;
|
|
120
|
+
template: string;
|
|
72
121
|
}
|
|
73
122
|
|
|
74
123
|
interface IPageBrowserDispatchProps {
|
|
@@ -96,6 +145,7 @@ const mapStateToProps = (state: IRootState): IEditorStateProps => ({
|
|
|
96
145
|
schema: state.forms.schema,
|
|
97
146
|
breadcrumb: state.forms.breadcrumb,
|
|
98
147
|
selectedParent: state.forms.selectedParent,
|
|
148
|
+
template: state.forms.template,
|
|
99
149
|
});
|
|
100
150
|
|
|
101
151
|
const mapDispatchToProps = {
|
|
@@ -75,6 +75,7 @@ const MediaGallery = (props: IProps) => {
|
|
|
75
75
|
displayMode,
|
|
76
76
|
selectedTab,
|
|
77
77
|
foldersTree,
|
|
78
|
+
isUploading,
|
|
78
79
|
} = props;
|
|
79
80
|
|
|
80
81
|
const initState: IImageFolder = { images: { items: [], totalItems: 0, currentPage: 1 }, folders: [] };
|
|
@@ -271,6 +272,7 @@ const MediaGallery = (props: IProps) => {
|
|
|
271
272
|
|
|
272
273
|
const handleBackClick = () => {
|
|
273
274
|
const parentID = breadcrumb.length >= 2 ? breadcrumb[breadcrumb.length - 2].id : null;
|
|
275
|
+
resetBulkSelection();
|
|
274
276
|
setPage(firstPage);
|
|
275
277
|
updateCurrentFolder(parentID);
|
|
276
278
|
};
|
|
@@ -440,6 +442,7 @@ const MediaGallery = (props: IProps) => {
|
|
|
440
442
|
|
|
441
443
|
const handleUpdateCurrentFolder = (folderID: number | null) => {
|
|
442
444
|
setSearchQuery("");
|
|
445
|
+
resetBulkSelection();
|
|
443
446
|
setPage(firstPage);
|
|
444
447
|
updateCurrentFolder(folderID);
|
|
445
448
|
};
|
|
@@ -585,6 +588,7 @@ const MediaGallery = (props: IProps) => {
|
|
|
585
588
|
? {
|
|
586
589
|
label: "Upload",
|
|
587
590
|
action: () => toggleUploadModal(),
|
|
591
|
+
disabled: isUploading,
|
|
588
592
|
}
|
|
589
593
|
: undefined;
|
|
590
594
|
|
|
@@ -862,6 +866,7 @@ interface IProps {
|
|
|
862
866
|
displayMode: "grid" | "list";
|
|
863
867
|
selectedTab: "site" | "global";
|
|
864
868
|
foldersTree: IFolderTree[];
|
|
869
|
+
isUploading: boolean;
|
|
865
870
|
getFolderContent(params: IGetFolderParams): Promise<void>;
|
|
866
871
|
getFoldersTree(siteID: number | "global"): Promise<void>;
|
|
867
872
|
updateCurrentFolder(folderID: number | null): void;
|
|
@@ -926,6 +931,7 @@ const mapStateToProps = (state: IRootState) => ({
|
|
|
926
931
|
displayMode: state.gallery.displayMode,
|
|
927
932
|
selectedTab: state.gallery.selectedTab,
|
|
928
933
|
foldersTree: state.gallery.foldersTree,
|
|
934
|
+
isUploading: state.gallery.isUploading,
|
|
929
935
|
});
|
|
930
936
|
|
|
931
937
|
export default connect(mapStateToProps, mapDispatchToProps)(MediaGallery);
|
package/src/types/index.tsx
CHANGED