@griddo/ax 12.1.0 → 12.2.0-rc.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/__tests__/components/ContextMenu/ContextMenu.test.tsx +381 -0
- package/src/__tests__/components/FloatingMenu/FloatingMenu.test.tsx +3 -3
- package/src/__tests__/components/FloatingPanel/FloatingPanel.test.tsx +2 -2
- package/src/__tests__/components/SideModal/SideModal.test.tsx +2 -2
- package/src/__tests__/hooks/modals.test.tsx +86 -1
- package/src/components/ActionMenu/index.tsx +26 -11
- package/src/components/ActionMenu/style.tsx +15 -8
- package/src/components/ContextMenu/index.tsx +102 -0
- package/src/components/ContextMenu/style.tsx +15 -0
- package/src/components/Fields/ComponentContainer/index.tsx +9 -2
- package/src/components/FloatingMenu/index.tsx +1 -1
- package/src/components/FloatingMenu/style.tsx +1 -1
- package/src/components/index.tsx +4 -1
- package/src/hooks/index.tsx +2 -1
- package/src/hooks/modals.tsx +51 -4
- package/src/modules/Analytics/DimensionItem/index.tsx +5 -3
- package/src/modules/Analytics/GroupItem/index.tsx +5 -3
- package/src/modules/Categories/CategoriesList/CategoryItem/index.tsx +5 -2
- package/src/modules/Content/PageItem/index.tsx +15 -3
- package/src/modules/FileDrive/FolderItem/index.tsx +5 -3
- package/src/modules/FileDrive/GridItem/index.tsx +5 -3
- package/src/modules/FileDrive/ListItem/index.tsx +5 -3
- package/src/modules/Forms/FormCategoriesList/CategoryItem/index.tsx +5 -2
- package/src/modules/Forms/FormList/FormItem/index.tsx +5 -2
- package/src/modules/MediaGallery/FolderItem/index.tsx +5 -3
- package/src/modules/MediaGallery/GridItem/index.tsx +5 -3
- package/src/modules/MediaGallery/ListItem/index.tsx +5 -3
- package/src/modules/Navigation/Defaults/Item/index.tsx +5 -3
- package/src/modules/Navigation/Menus/List/Table/Item/index.tsx +5 -2
- package/src/modules/Redirects/RedirectItem/index.tsx +5 -3
- package/src/modules/Settings/Integrations/IntegrationForm/VariableItem/index.tsx +5 -3
- package/src/modules/Settings/Integrations/IntegrationItem/CopyModal/index.tsx +1 -0
- package/src/modules/Settings/Integrations/IntegrationItem/index.tsx +5 -3
- package/src/modules/Settings/Languages/Table/Item/index.tsx +5 -3
- package/src/modules/Sites/SitesList/GridView/GridSiteItem/index.tsx +5 -2
- package/src/modules/Sites/SitesList/ListView/ListSiteItem/index.tsx +5 -2
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +5 -2
- package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +5 -3
- package/src/modules/Users/UserList/UserItem/index.tsx +5 -3
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { ActionMenuContent } from "@ax/components";
|
|
4
|
+
import { useHandleClickOutside } from "@ax/hooks";
|
|
5
|
+
import type { IActionMenuOption } from "@ax/types";
|
|
6
|
+
|
|
7
|
+
import * as S from "./style";
|
|
8
|
+
|
|
9
|
+
const ContextMenu = (props: IContextMenuProps): JSX.Element | null => {
|
|
10
|
+
const { position, options, onClose } = props;
|
|
11
|
+
const menuRef = useRef<HTMLDivElement>(null);
|
|
12
|
+
const [adjustedPosition, setAdjustedPosition] = useState<{ x: number; y: number } | null>(null);
|
|
13
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
14
|
+
|
|
15
|
+
// Maneja los clicks fuera del menú para cerrarlo
|
|
16
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
17
|
+
// No cerrar si el click fue dentro del menú
|
|
18
|
+
if (menuRef.current?.contains(event.target as Node)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
event.stopPropagation();
|
|
23
|
+
onClose?.();
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
useHandleClickOutside(!!position, handleClickOutside);
|
|
27
|
+
|
|
28
|
+
useLayoutEffect(() => {
|
|
29
|
+
if (!position || !menuRef.current) {
|
|
30
|
+
setAdjustedPosition(null);
|
|
31
|
+
setIsVisible(false);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const calculatePosition = () => {
|
|
36
|
+
const menuRect = menuRef.current?.getBoundingClientRect();
|
|
37
|
+
if (!menuRect || menuRect.width === 0 || menuRect.height === 0) return;
|
|
38
|
+
|
|
39
|
+
const viewportWidth = window.innerWidth;
|
|
40
|
+
const viewportHeight = window.innerHeight;
|
|
41
|
+
const padding = 8;
|
|
42
|
+
|
|
43
|
+
let x = position.x;
|
|
44
|
+
let y = position.y;
|
|
45
|
+
|
|
46
|
+
// Ajustar si se sale por la derecha
|
|
47
|
+
if (x + menuRect.width > viewportWidth - padding) {
|
|
48
|
+
x = viewportWidth - menuRect.width - padding;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Ajustar si se sale por abajo
|
|
52
|
+
if (y + menuRect.height > viewportHeight - padding) {
|
|
53
|
+
y = viewportHeight - menuRect.height - padding;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Asegurar que no esté fuera de los bordes izquierdo y superior
|
|
57
|
+
x = Math.max(padding, x);
|
|
58
|
+
y = Math.max(padding, y);
|
|
59
|
+
|
|
60
|
+
setAdjustedPosition({ x, y });
|
|
61
|
+
setIsVisible(true);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
calculatePosition();
|
|
65
|
+
}, [position]);
|
|
66
|
+
|
|
67
|
+
if (!position) return null;
|
|
68
|
+
|
|
69
|
+
const handleActionClick = (action: () => void) => {
|
|
70
|
+
return () => {
|
|
71
|
+
action();
|
|
72
|
+
onClose?.();
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const wrappedOptions = options.map((option) => {
|
|
77
|
+
if (!option) return null;
|
|
78
|
+
return {
|
|
79
|
+
...option,
|
|
80
|
+
action: handleActionClick(option.action),
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<S.ContextMenuContainer
|
|
86
|
+
ref={menuRef}
|
|
87
|
+
x={adjustedPosition?.x ?? 0}
|
|
88
|
+
y={adjustedPosition?.y ?? 0}
|
|
89
|
+
isVisible={isVisible}
|
|
90
|
+
>
|
|
91
|
+
<ActionMenuContent options={wrappedOptions} />
|
|
92
|
+
</S.ContextMenuContainer>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
interface IContextMenuProps {
|
|
97
|
+
position: { x: number; y: number } | null;
|
|
98
|
+
options: (IActionMenuOption | undefined | null)[];
|
|
99
|
+
onClose?: () => void;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export default ContextMenu;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
|
|
3
|
+
const ContextMenuContainer = styled.div<{ x: number; y: number; isVisible: boolean }>`
|
|
4
|
+
position: fixed;
|
|
5
|
+
top: ${(p) => p.y}px;
|
|
6
|
+
left: ${(p) => p.x}px;
|
|
7
|
+
z-index: 1000;
|
|
8
|
+
box-shadow: ${(p) => p.theme.shadow.shadowL};
|
|
9
|
+
visibility: ${(p) => (p.isVisible ? "visible" : "hidden")};
|
|
10
|
+
opacity: ${(p) => (p.isVisible ? 1 : 0)};
|
|
11
|
+
transition: opacity 0.1s ease-in-out;
|
|
12
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export { ContextMenuContainer };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type React from "react";
|
|
2
2
|
|
|
3
|
-
import { CheckField, Icon, SideModal } from "@ax/components";
|
|
3
|
+
import { CheckField, ContextMenu, Icon, SideModal } from "@ax/components";
|
|
4
4
|
import { getDisplayName, isEmptyContainer, trimText } from "@ax/helpers";
|
|
5
|
-
import { useModal } from "@ax/hooks";
|
|
5
|
+
import { useContextMenu, useModal } from "@ax/hooks";
|
|
6
6
|
import type { ICheck, IComponent, ModuleCategoryInfo } from "@ax/types";
|
|
7
7
|
|
|
8
8
|
import { useSortable } from "@dnd-kit/sortable";
|
|
@@ -48,6 +48,7 @@ const ComponentContainer = (props: IComponentContainerProps): JSX.Element => {
|
|
|
48
48
|
actions || {};
|
|
49
49
|
|
|
50
50
|
const { isOpen, toggleModal } = useModal();
|
|
51
|
+
const { contextMenu, handleContextMenu, closeContextMenu } = useContextMenu();
|
|
51
52
|
|
|
52
53
|
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({
|
|
53
54
|
id: editorID,
|
|
@@ -180,6 +181,7 @@ const ComponentContainer = (props: IComponentContainerProps): JSX.Element => {
|
|
|
180
181
|
disabled={disabled}
|
|
181
182
|
className={`editorId-${editorID} ${className}`}
|
|
182
183
|
onClick={handleClick}
|
|
184
|
+
onContextMenu={handleContextMenu}
|
|
183
185
|
isActive={isActive}
|
|
184
186
|
cssTransform={transform}
|
|
185
187
|
transition={transition}
|
|
@@ -231,6 +233,11 @@ const ComponentContainer = (props: IComponentContainerProps): JSX.Element => {
|
|
|
231
233
|
theme={theme}
|
|
232
234
|
/>
|
|
233
235
|
)}
|
|
236
|
+
<ContextMenu
|
|
237
|
+
position={contextMenu}
|
|
238
|
+
options={isArray ? actionArrayMenuOptions : actionMenuOptions}
|
|
239
|
+
onClose={closeContextMenu}
|
|
240
|
+
/>
|
|
234
241
|
</>
|
|
235
242
|
);
|
|
236
243
|
};
|
|
@@ -41,7 +41,7 @@ const Menu = styled.div<{ hasMargin: boolean; width?: number }>`
|
|
|
41
41
|
min-width: ${(p) => `calc(${p.theme.spacing.m} * 7)`};
|
|
42
42
|
background: ${(p) => p.theme.color.uiBackground02};
|
|
43
43
|
box-shadow: ${(p) => p.theme.shadow.shadowL};
|
|
44
|
-
border-radius: ${(p) => p.theme.
|
|
44
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
45
45
|
`;
|
|
46
46
|
|
|
47
47
|
export { Wrapper, ButtonWrapper, MenuWrapper, Menu };
|
package/src/components/index.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import ActionMenu from "./ActionMenu";
|
|
1
|
+
import ActionMenu, { ActionMenuContent } from "./ActionMenu";
|
|
2
2
|
import Avatar from "./Avatar";
|
|
3
3
|
import BackFolder from "./BackFolder";
|
|
4
4
|
import Breadcrumb from "./Breadcrumb";
|
|
@@ -10,6 +10,7 @@ import CategoryCell from "./CategoryCell";
|
|
|
10
10
|
import Circle from "./Circle";
|
|
11
11
|
import ContentItem from "./ContentItem";
|
|
12
12
|
import ConfigPanel from "./ConfigPanel";
|
|
13
|
+
import ContextMenu from "./ContextMenu";
|
|
13
14
|
import DragAndDrop from "./DragAndDrop";
|
|
14
15
|
import ElementsTooltip from "./ElementsTooltip";
|
|
15
16
|
import EmptyState from "./EmptyState";
|
|
@@ -133,6 +134,7 @@ import UserRolesAndSites from "./UserRolesAndSites";
|
|
|
133
134
|
|
|
134
135
|
export {
|
|
135
136
|
ActionMenu,
|
|
137
|
+
ActionMenuContent,
|
|
136
138
|
AIReferenceField,
|
|
137
139
|
ArrayFieldGroup,
|
|
138
140
|
AsyncCheckGroup,
|
|
@@ -156,6 +158,7 @@ export {
|
|
|
156
158
|
ContentItem,
|
|
157
159
|
ConditionalField,
|
|
158
160
|
ConfigPanel,
|
|
161
|
+
ContextMenu,
|
|
159
162
|
CustomizeFilters,
|
|
160
163
|
DateField,
|
|
161
164
|
DateFilter,
|
package/src/hooks/index.tsx
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "./forms";
|
|
11
11
|
import { useOnMessageReceivedFromIframe, useOnMessageReceivedFromOutside } from "./iframe";
|
|
12
12
|
import { useURLSearchParam } from "./location";
|
|
13
|
-
import { useHandleClickOutside, useModal, useModals, useToast } from "./modals";
|
|
13
|
+
import { useContextMenu, useHandleClickOutside, useModal, useModals, useToast } from "./modals";
|
|
14
14
|
import { useNetworkStatus } from "./network";
|
|
15
15
|
import { useResizable } from "./resize";
|
|
16
16
|
import { useGlobalPermission, usePermission, usePermissions, usePermissionsForPage, usePermissionsForSite } from "./users";
|
|
@@ -20,6 +20,7 @@ export {
|
|
|
20
20
|
useAdaptiveText,
|
|
21
21
|
useBulkSelection,
|
|
22
22
|
useCategoryColors,
|
|
23
|
+
useContextMenu,
|
|
23
24
|
useDebounce,
|
|
24
25
|
useDebouncedCallback,
|
|
25
26
|
useEmptyState,
|
package/src/hooks/modals.tsx
CHANGED
|
@@ -84,13 +84,13 @@ const useModals = <T extends string>(modalKeys: readonly T[], bodyBlock = true)
|
|
|
84
84
|
const useHandleClickOutside = (isOpen: boolean, handleClickOutside: (e: MouseEvent) => void) => {
|
|
85
85
|
useEffect(() => {
|
|
86
86
|
if (isOpen) {
|
|
87
|
-
document.addEventListener("
|
|
87
|
+
document.addEventListener("click", handleClickOutside, true);
|
|
88
88
|
} else {
|
|
89
|
-
document.removeEventListener("
|
|
89
|
+
document.removeEventListener("click", handleClickOutside, true);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
return () => {
|
|
93
|
-
document.removeEventListener("
|
|
93
|
+
document.removeEventListener("click", handleClickOutside, true);
|
|
94
94
|
};
|
|
95
95
|
}, [isOpen, handleClickOutside]);
|
|
96
96
|
};
|
|
@@ -126,4 +126,51 @@ const useToast = () => {
|
|
|
126
126
|
return { isVisible, setIsVisible, toggleToast, state };
|
|
127
127
|
};
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
const useContextMenu = () => {
|
|
130
|
+
const [contextMenu, setContextMenu] = useState<IContextMenuPosition | null>(null);
|
|
131
|
+
|
|
132
|
+
const closeContextMenu = useCallback(() => {
|
|
133
|
+
setContextMenu(null);
|
|
134
|
+
}, []);
|
|
135
|
+
|
|
136
|
+
const handleContextMenu = (e: React.MouseEvent) => {
|
|
137
|
+
e.preventDefault();
|
|
138
|
+
setContextMenu({ x: e.clientX, y: e.clientY });
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Registra este context menu como el activo y cierra el anterior si existe
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (contextMenu) {
|
|
144
|
+
const unregister = registerContextMenu(closeContextMenu);
|
|
145
|
+
return unregister;
|
|
146
|
+
}
|
|
147
|
+
}, [contextMenu, closeContextMenu]);
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
contextMenu,
|
|
151
|
+
handleContextMenu,
|
|
152
|
+
closeContextMenu,
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
let activeContextMenuClose: (() => void) | null = null;
|
|
157
|
+
|
|
158
|
+
const registerContextMenu = (onClose: () => void) => {
|
|
159
|
+
if (activeContextMenuClose) {
|
|
160
|
+
activeContextMenuClose();
|
|
161
|
+
}
|
|
162
|
+
activeContextMenuClose = onClose;
|
|
163
|
+
|
|
164
|
+
return () => {
|
|
165
|
+
if (activeContextMenuClose === onClose) {
|
|
166
|
+
activeContextMenuClose = null;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
interface IContextMenuPosition {
|
|
172
|
+
x: number;
|
|
173
|
+
y: number;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export { useModal, useModals, useHandleClickOutside, useToast, useContextMenu };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Modal } from "@ax/components";
|
|
1
|
+
import { Modal, ContextMenu } from "@ax/components";
|
|
2
2
|
import { splitAndJoin, trimText } from "@ax/helpers";
|
|
3
|
-
import { useModal } from "@ax/hooks";
|
|
3
|
+
import { useModal, useContextMenu } from "@ax/hooks";
|
|
4
4
|
import type { IDimension } from "@ax/types";
|
|
5
5
|
|
|
6
6
|
import DimensionPanel from "../DimensionPanel";
|
|
@@ -12,6 +12,7 @@ const Item = (props: IProps): JSX.Element => {
|
|
|
12
12
|
|
|
13
13
|
const { isOpen, toggleModal } = useModal();
|
|
14
14
|
const { isOpen: isRemoveOpen, toggleModal: toggleRemoveModal } = useModal();
|
|
15
|
+
const { contextMenu, handleContextMenu, closeContextMenu } = useContextMenu();
|
|
15
16
|
|
|
16
17
|
const handleClick = () => toggleModal();
|
|
17
18
|
|
|
@@ -36,7 +37,7 @@ const Item = (props: IProps): JSX.Element => {
|
|
|
36
37
|
|
|
37
38
|
return (
|
|
38
39
|
<>
|
|
39
|
-
<S.Component onClick={handleClick}>
|
|
40
|
+
<S.Component onClick={handleClick} onContextMenu={handleContextMenu}>
|
|
40
41
|
<S.Name>{item.name}</S.Name>
|
|
41
42
|
<S.Values>{trimText(valuesText, 70)}</S.Values>
|
|
42
43
|
<S.StyledActionMenu icon="more" options={dimensionOptions} tooltip="Dimension actions" />
|
|
@@ -59,6 +60,7 @@ const Item = (props: IProps): JSX.Element => {
|
|
|
59
60
|
</p>
|
|
60
61
|
</S.ModalContent>
|
|
61
62
|
</Modal>
|
|
63
|
+
<ContextMenu position={contextMenu} options={dimensionOptions} onClose={closeContextMenu} />
|
|
62
64
|
</>
|
|
63
65
|
);
|
|
64
66
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Modal } from "@ax/components";
|
|
1
|
+
import { Modal, ContextMenu } from "@ax/components";
|
|
2
2
|
import { splitAndJoin } from "@ax/helpers";
|
|
3
|
-
import { useModal } from "@ax/hooks";
|
|
3
|
+
import { useModal, useContextMenu } from "@ax/hooks";
|
|
4
4
|
import type { IDimension, IDimensionsGroup } from "@ax/types";
|
|
5
5
|
|
|
6
6
|
import GroupPanel from "../GroupPanel";
|
|
@@ -12,6 +12,7 @@ const Item = (props: IProps): JSX.Element => {
|
|
|
12
12
|
|
|
13
13
|
const { isOpen, toggleModal } = useModal();
|
|
14
14
|
const { isOpen: isRemoveOpen, toggleModal: toggleRemoveModal } = useModal();
|
|
15
|
+
const { contextMenu, handleContextMenu, closeContextMenu } = useContextMenu();
|
|
15
16
|
|
|
16
17
|
const handleClick = () => toggleModal();
|
|
17
18
|
|
|
@@ -36,7 +37,7 @@ const Item = (props: IProps): JSX.Element => {
|
|
|
36
37
|
|
|
37
38
|
return (
|
|
38
39
|
<>
|
|
39
|
-
<S.Component onClick={handleClick}>
|
|
40
|
+
<S.Component onClick={handleClick} onContextMenu={handleContextMenu}>
|
|
40
41
|
<S.Name>{item.name}</S.Name>
|
|
41
42
|
<S.ComponentInfo>
|
|
42
43
|
<S.Values>
|
|
@@ -70,6 +71,7 @@ const Item = (props: IProps): JSX.Element => {
|
|
|
70
71
|
</p>
|
|
71
72
|
</S.ModalContent>
|
|
72
73
|
</Modal>
|
|
74
|
+
<ContextMenu position={contextMenu} options={groupOptions} onClose={closeContextMenu} />
|
|
73
75
|
</>
|
|
74
76
|
);
|
|
75
77
|
};
|
|
@@ -3,10 +3,10 @@ import { useState } from "react";
|
|
|
3
3
|
import { connect } from "react-redux";
|
|
4
4
|
|
|
5
5
|
import { structuredData } from "@ax/api";
|
|
6
|
-
import { CheckField, Flag, FloatingMenu, Icon, LanguageMenu, Tag, TruncatedTooltip } from "@ax/components";
|
|
6
|
+
import { CheckField, ContextMenu, Flag, FloatingMenu, Icon, LanguageMenu, Tag, TruncatedTooltip } from "@ax/components";
|
|
7
7
|
import { structuredDataActions } from "@ax/containers/StructuredData";
|
|
8
8
|
import { isReqOk } from "@ax/helpers";
|
|
9
|
-
import { useModals, usePermissions } from "@ax/hooks";
|
|
9
|
+
import { useContextMenu, useModals, usePermissions } from "@ax/hooks";
|
|
10
10
|
import type { ICategoryGroup, ICheck, IDataLanguage, ILanguage, IRootState, IStructuredDataCategory } from "@ax/types";
|
|
11
11
|
|
|
12
12
|
import { type AnimateLayoutChanges, useSortable } from "@dnd-kit/sortable";
|
|
@@ -39,6 +39,7 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
|
|
|
39
39
|
} = props;
|
|
40
40
|
|
|
41
41
|
const { isOpen, toggleModal } = useModals(["panel", "delete", "group"]);
|
|
42
|
+
const { contextMenu, handleContextMenu, closeContextMenu } = useContextMenu();
|
|
42
43
|
const [deleteGroupCategories, setDeleteGroupCategories] = useState(false);
|
|
43
44
|
const [translation, setTranslation] = useState<{
|
|
44
45
|
lang: { locale: string; id: number };
|
|
@@ -190,6 +191,7 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
|
|
|
190
191
|
<S.CategoryRow
|
|
191
192
|
selected={isSelected}
|
|
192
193
|
onClick={handleClick}
|
|
194
|
+
onContextMenu={handleContextMenu}
|
|
193
195
|
ref={setDraggableNodeRef}
|
|
194
196
|
cssTransform={transform}
|
|
195
197
|
transition={transition}
|
|
@@ -273,6 +275,7 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
|
|
|
273
275
|
isLoading={isSaving}
|
|
274
276
|
/>
|
|
275
277
|
)}
|
|
278
|
+
<ContextMenu position={contextMenu} options={menuOptions} onClose={closeContextMenu} />
|
|
276
279
|
</>
|
|
277
280
|
);
|
|
278
281
|
};
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
CheckField,
|
|
6
|
+
ContextMenu,
|
|
7
|
+
Flag,
|
|
8
|
+
FloatingMenu,
|
|
9
|
+
Icon,
|
|
10
|
+
LanguageMenu,
|
|
11
|
+
Tag,
|
|
12
|
+
Tooltip,
|
|
13
|
+
TruncatedTooltip,
|
|
14
|
+
} from "@ax/components";
|
|
5
15
|
import { appActions } from "@ax/containers/App";
|
|
6
16
|
import { pageEditorActions } from "@ax/containers/PageEditor";
|
|
7
17
|
import { type ISetCurrentPageIDAction, pageStatus } from "@ax/containers/PageEditor/interfaces";
|
|
@@ -12,7 +22,7 @@ import {
|
|
|
12
22
|
getTemplateDisplayName,
|
|
13
23
|
slugify,
|
|
14
24
|
} from "@ax/helpers";
|
|
15
|
-
import { useModals, usePermissionsForPage } from "@ax/hooks";
|
|
25
|
+
import { useContextMenu, useModals, usePermissionsForPage } from "@ax/hooks";
|
|
16
26
|
import type {
|
|
17
27
|
ICheck,
|
|
18
28
|
IColumn,
|
|
@@ -94,6 +104,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
94
104
|
const [site, setSite] = useState(null);
|
|
95
105
|
const [modalState, setModalState] = useState(initValue);
|
|
96
106
|
const [deleteAllVersions, setDeleteAllVersions] = useState(false);
|
|
107
|
+
const { contextMenu, handleContextMenu, closeContextMenu } = useContextMenu();
|
|
97
108
|
const { toggleModal, isOpen } = useModals(["duplicate", "remove", "unpublish", "delete", "copy"]);
|
|
98
109
|
|
|
99
110
|
const isAllowedTo = usePermissionsForPage(
|
|
@@ -465,7 +476,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
465
476
|
|
|
466
477
|
return (
|
|
467
478
|
<>
|
|
468
|
-
<S.PageRow role="rowgroup" selected={isSelected} global={isGlobal}>
|
|
479
|
+
<S.PageRow role="rowgroup" selected={isSelected} global={isGlobal} onContextMenu={handleContextMenu}>
|
|
469
480
|
<S.CheckCell role="cell">
|
|
470
481
|
<CheckField name="check" value={page.id} checked={isSelected || hoverCheck} onChange={handleOnChange} />
|
|
471
482
|
</S.CheckCell>
|
|
@@ -555,6 +566,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
555
566
|
onDelete={handleDeleteItem}
|
|
556
567
|
{...{ isTranslated, deleteAllVersions, setDeleteAllVersions, title: page.title, isDeleting: isSaving }}
|
|
557
568
|
/>
|
|
569
|
+
<ContextMenu position={contextMenu} options={menuOptions} onClose={closeContextMenu} />
|
|
558
570
|
</>
|
|
559
571
|
);
|
|
560
572
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import { Icon, Tooltip } from "@ax/components";
|
|
4
|
+
import { ContextMenu, Icon, Tooltip } from "@ax/components";
|
|
5
5
|
import { fileDriveActions } from "@ax/containers/FileDrive";
|
|
6
6
|
import { trimText } from "@ax/helpers";
|
|
7
|
-
import { useModals } from "@ax/hooks";
|
|
7
|
+
import { useContextMenu, useModals } from "@ax/hooks";
|
|
8
8
|
import type { IActionMenuOption, IFolder, IRootState } from "@ax/types";
|
|
9
9
|
|
|
10
10
|
import { DeleteFolderModal, MoveItemModal, RenameFolderModal } from "../atoms";
|
|
@@ -28,6 +28,7 @@ const FolderItem = (props: IProps) => {
|
|
|
28
28
|
const initState = { folderName, parentId };
|
|
29
29
|
const [folderForm, setFolderForm] = useState(initState);
|
|
30
30
|
const { isOpen, toggleModal } = useModals(["delete", "rename", "move"]);
|
|
31
|
+
const { contextMenu, handleContextMenu, closeContextMenu } = useContextMenu();
|
|
31
32
|
|
|
32
33
|
const setName = (name: string) => setFolderForm({ ...folderForm, folderName: name });
|
|
33
34
|
const setParent = (folderID: number) => setFolderForm({ ...folderForm, parentId: folderID });
|
|
@@ -102,7 +103,7 @@ const FolderItem = (props: IProps) => {
|
|
|
102
103
|
|
|
103
104
|
return (
|
|
104
105
|
<>
|
|
105
|
-
<S.Wrapper onClick={handleClick}>
|
|
106
|
+
<S.Wrapper onClick={handleClick} onContextMenu={handleContextMenu}>
|
|
106
107
|
<S.IconWrapper>
|
|
107
108
|
<Icon name="project" size="24" />
|
|
108
109
|
</S.IconWrapper>
|
|
@@ -141,6 +142,7 @@ const FolderItem = (props: IProps) => {
|
|
|
141
142
|
isMoving={isSaving}
|
|
142
143
|
/>
|
|
143
144
|
)}
|
|
145
|
+
<ContextMenu position={contextMenu} options={menuOptions} onClose={closeContextMenu} />
|
|
144
146
|
</>
|
|
145
147
|
);
|
|
146
148
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type React from "react";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
|
|
4
|
-
import { CheckField, Tag, Tooltip } from "@ax/components";
|
|
4
|
+
import { CheckField, ContextMenu, Tag, Tooltip } from "@ax/components";
|
|
5
5
|
import { formatBytes, getFileIcon, getFormattedDateWithTimezone, trimText } from "@ax/helpers";
|
|
6
|
-
import { useModals } from "@ax/hooks";
|
|
6
|
+
import { useContextMenu, useModals } from "@ax/hooks";
|
|
7
7
|
import type { IActionMenuOption, ICheck, IFile } from "@ax/types";
|
|
8
8
|
|
|
9
9
|
import { DeleteFileModal, MoveItemModal } from "../atoms";
|
|
@@ -31,6 +31,7 @@ const GridItem = (props: IProps) => {
|
|
|
31
31
|
const [selectedFolder, setSelectedFolder] = useState<number>(currentFolderID || 0);
|
|
32
32
|
const [isDeleting, setIsDeleting] = useState(false);
|
|
33
33
|
const { isOpen, toggleModal } = useModals(["delete", "move"]);
|
|
34
|
+
const { contextMenu, handleContextMenu, closeContextMenu } = useContextMenu();
|
|
34
35
|
|
|
35
36
|
const handleChange = (value: ICheck) => onChange(value);
|
|
36
37
|
const handleClick = () => onClick(file);
|
|
@@ -117,7 +118,7 @@ const GridItem = (props: IProps) => {
|
|
|
117
118
|
|
|
118
119
|
return (
|
|
119
120
|
<>
|
|
120
|
-
<S.Wrapper onClick={handleClick} isTall={isSearching}>
|
|
121
|
+
<S.Wrapper onClick={handleClick} onContextMenu={handleContextMenu} isTall={isSearching}>
|
|
121
122
|
<S.Header>
|
|
122
123
|
<S.CheckWrapper onClick={handleCheckClick}>
|
|
123
124
|
<CheckField name="check" value={id} checked={isSelected} onChange={handleChange} />
|
|
@@ -158,6 +159,7 @@ const GridItem = (props: IProps) => {
|
|
|
158
159
|
isMoving={isSaving}
|
|
159
160
|
/>
|
|
160
161
|
)}
|
|
162
|
+
<ContextMenu position={contextMenu} options={menuOptions} onClose={closeContextMenu} />
|
|
161
163
|
</>
|
|
162
164
|
);
|
|
163
165
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
|
|
3
|
-
import { CheckField, ElementsTooltip, Tag, Tooltip } from "@ax/components";
|
|
3
|
+
import { CheckField, ContextMenu, ElementsTooltip, Tag, Tooltip } from "@ax/components";
|
|
4
4
|
import { formatBytes, getFileIcon, getFormattedDateWithTimezone, trimText } from "@ax/helpers";
|
|
5
|
-
import { useModals } from "@ax/hooks";
|
|
5
|
+
import { useContextMenu, useModals } from "@ax/hooks";
|
|
6
6
|
import type { IActionMenuOption, ICheck, IFile } from "@ax/types";
|
|
7
7
|
|
|
8
8
|
import { DeleteFileModal, MoveItemModal } from "../atoms";
|
|
@@ -30,6 +30,7 @@ const ListItem = (props: IProps) => {
|
|
|
30
30
|
const [selectedFolder, setSelectedFolder] = useState<number>(currentFolderID || 0);
|
|
31
31
|
const [isDeleting, setIsDeleting] = useState(false);
|
|
32
32
|
const { isOpen, toggleModal } = useModals(["delete", "move"]);
|
|
33
|
+
const { contextMenu, handleContextMenu, closeContextMenu } = useContextMenu();
|
|
33
34
|
|
|
34
35
|
const handleChange = (value: ICheck) => onChange(value);
|
|
35
36
|
const handleClick = () => onClick(file);
|
|
@@ -109,7 +110,7 @@ const ListItem = (props: IProps) => {
|
|
|
109
110
|
|
|
110
111
|
return (
|
|
111
112
|
<>
|
|
112
|
-
<S.ItemRow role="rowgroup" selected={isSelected}>
|
|
113
|
+
<S.ItemRow role="rowgroup" selected={isSelected} onContextMenu={handleContextMenu}>
|
|
113
114
|
<S.CheckCell role="cell">
|
|
114
115
|
<CheckField name="check" value={id} checked={isSelected || hoverCheck} onChange={handleChange} />
|
|
115
116
|
</S.CheckCell>
|
|
@@ -170,6 +171,7 @@ const ListItem = (props: IProps) => {
|
|
|
170
171
|
isMoving={isSaving}
|
|
171
172
|
/>
|
|
172
173
|
)}
|
|
174
|
+
<ContextMenu position={contextMenu} options={menuOptions} onClose={closeContextMenu} />
|
|
173
175
|
</>
|
|
174
176
|
);
|
|
175
177
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type React from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import { CheckField, Icon, TruncatedTooltip } from "@ax/components";
|
|
4
|
+
import { CheckField, ContextMenu, Icon, TruncatedTooltip } from "@ax/components";
|
|
5
5
|
import { formsActions } from "@ax/containers/Forms";
|
|
6
|
-
import { useModals, usePermissions } from "@ax/hooks";
|
|
6
|
+
import { useContextMenu, useModals, usePermissions } from "@ax/hooks";
|
|
7
7
|
import type { FormCategory, ICheck, IRootState } from "@ax/types";
|
|
8
8
|
|
|
9
9
|
import { useSortable } from "@dnd-kit/sortable";
|
|
@@ -27,6 +27,7 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
|
|
|
27
27
|
} = props;
|
|
28
28
|
|
|
29
29
|
const { isOpen, toggleModal } = useModals(["edit", "delete"]);
|
|
30
|
+
const { contextMenu, handleContextMenu, closeContextMenu } = useContextMenu();
|
|
30
31
|
|
|
31
32
|
const isAllowedTo = usePermissions({
|
|
32
33
|
editSiteCategory: "forms.editFormsCategories",
|
|
@@ -78,6 +79,7 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
|
|
|
78
79
|
<S.CategoryRow
|
|
79
80
|
selected={isSelected}
|
|
80
81
|
onClick={handleClick}
|
|
82
|
+
onContextMenu={handleContextMenu}
|
|
81
83
|
ref={setNodeRef}
|
|
82
84
|
cssTransform={transform}
|
|
83
85
|
transition={transition}
|
|
@@ -121,6 +123,7 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
|
|
|
121
123
|
onDelete={removeCategory}
|
|
122
124
|
isDeleting={isSaving}
|
|
123
125
|
/>
|
|
126
|
+
<ContextMenu position={contextMenu} options={menuOptions} onClose={closeContextMenu} />
|
|
124
127
|
</>
|
|
125
128
|
);
|
|
126
129
|
};
|
|
@@ -5,6 +5,7 @@ import { connect } from "react-redux";
|
|
|
5
5
|
import {
|
|
6
6
|
CategoryCell,
|
|
7
7
|
CheckField,
|
|
8
|
+
ContextMenu,
|
|
8
9
|
Flag,
|
|
9
10
|
FloatingMenu,
|
|
10
11
|
Icon,
|
|
@@ -16,7 +17,7 @@ import { appActions } from "@ax/containers/App";
|
|
|
16
17
|
import { formsActions } from "@ax/containers/Forms";
|
|
17
18
|
import { findFieldsErrors } from "@ax/forms";
|
|
18
19
|
import { findObjectValue, getHumanLastModifiedDate } from "@ax/helpers";
|
|
19
|
-
import { useModals, usePermissions } from "@ax/hooks";
|
|
20
|
+
import { useContextMenu, useModals, usePermissions } from "@ax/hooks";
|
|
20
21
|
import type {
|
|
21
22
|
FormContent,
|
|
22
23
|
FormLanguage,
|
|
@@ -74,6 +75,7 @@ const FormItem = (props: IFormItemProps): JSX.Element => {
|
|
|
74
75
|
const [duplicateTitle, setDuplicateTitle] = useState("");
|
|
75
76
|
const [site, setSite] = useState<number | null>(null);
|
|
76
77
|
const { isOpen, toggleModal } = useModals(["delete", "unpublish", "duplicate", "copy", "use"]);
|
|
78
|
+
const { contextMenu, handleContextMenu, closeContextMenu } = useContextMenu();
|
|
77
79
|
|
|
78
80
|
const isAllowedTo = usePermissions({
|
|
79
81
|
createForms: isSiteView ? "forms.createForms" : "global.forms.createForms",
|
|
@@ -260,7 +262,7 @@ const FormItem = (props: IFormItemProps): JSX.Element => {
|
|
|
260
262
|
|
|
261
263
|
return (
|
|
262
264
|
<>
|
|
263
|
-
<S.FormRow role="rowgroup" selected={isSelected} onClick={handleClick} clickable={isAllowedTo.editForms}>
|
|
265
|
+
<S.FormRow role="rowgroup" selected={isSelected} onClick={handleClick} onContextMenu={handleContextMenu} clickable={isAllowedTo.editForms}>
|
|
264
266
|
<S.CheckCell role="cell" onClick={handleCheckClick}>
|
|
265
267
|
<CheckField name={`form-${id}`} value={id} checked={isSelected || hoverCheck} onChange={handleOnChange} />
|
|
266
268
|
</S.CheckCell>
|
|
@@ -322,6 +324,7 @@ const FormItem = (props: IFormItemProps): JSX.Element => {
|
|
|
322
324
|
formInUse={formInUse}
|
|
323
325
|
/>
|
|
324
326
|
)}
|
|
327
|
+
<ContextMenu position={contextMenu} options={menuOptions} onClose={closeContextMenu} />
|
|
325
328
|
</>
|
|
326
329
|
);
|
|
327
330
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import { Icon, Tooltip } from "@ax/components";
|
|
4
|
+
import { ContextMenu, Icon, Tooltip } from "@ax/components";
|
|
5
5
|
import { galleryActions } from "@ax/containers/Gallery";
|
|
6
6
|
import { trimText } from "@ax/helpers";
|
|
7
|
-
import { useModals } from "@ax/hooks";
|
|
7
|
+
import { useContextMenu, useModals } from "@ax/hooks";
|
|
8
8
|
import type { IActionMenuOption, IFolder, IGetFolderParams, IRootState } from "@ax/types";
|
|
9
9
|
|
|
10
10
|
import { DeleteFolderModal, MoveItemModal, RenameFolderModal } from "../atoms";
|
|
@@ -29,6 +29,7 @@ const FolderItem = (props: IProps) => {
|
|
|
29
29
|
const initState = { folderName, parentId };
|
|
30
30
|
const [folderForm, setFolderForm] = useState(initState);
|
|
31
31
|
const { isOpen, toggleModal } = useModals(["delete", "rename", "move"]);
|
|
32
|
+
const { contextMenu, handleContextMenu, closeContextMenu } = useContextMenu();
|
|
32
33
|
|
|
33
34
|
const setName = (name: string) => setFolderForm({ ...folderForm, folderName: name });
|
|
34
35
|
const setParent = (folderID: number) => setFolderForm({ ...folderForm, parentId: folderID });
|
|
@@ -104,7 +105,7 @@ const FolderItem = (props: IProps) => {
|
|
|
104
105
|
|
|
105
106
|
return (
|
|
106
107
|
<>
|
|
107
|
-
<S.Wrapper onClick={handleClick}>
|
|
108
|
+
<S.Wrapper onClick={handleClick} onContextMenu={handleContextMenu}>
|
|
108
109
|
<S.IconWrapper>
|
|
109
110
|
<Icon name="project" size="24" />
|
|
110
111
|
</S.IconWrapper>
|
|
@@ -143,6 +144,7 @@ const FolderItem = (props: IProps) => {
|
|
|
143
144
|
isMoving={isSaving}
|
|
144
145
|
/>
|
|
145
146
|
)}
|
|
147
|
+
<ContextMenu position={contextMenu} options={menuOptions} onClose={closeContextMenu} />
|
|
146
148
|
</>
|
|
147
149
|
);
|
|
148
150
|
};
|