@griddo/ax 11.4.23 → 11.4.24
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 +4 -4
- package/src/__tests__/components/Fields/ComponentContainer/ComponentContainer.test.tsx +10 -52
- package/src/components/ActionMenu/index.tsx +3 -2
- package/src/components/Fields/CheckField/style.tsx +16 -16
- package/src/components/Fields/ComponentArray/MixableComponentArray/index.tsx +65 -49
- package/src/components/Fields/ComponentArray/SameComponentArray/index.tsx +89 -69
- package/src/components/Fields/ComponentContainer/index.tsx +11 -11
- package/src/components/Fields/ComponentContainer/style.tsx +12 -4
- package/src/components/Fields/ReferenceField/ItemList/Item/index.tsx +19 -9
- package/src/components/Fields/ReferenceField/ItemList/Item/style.tsx +24 -13
- package/src/components/Fields/ReferenceField/ItemList/index.tsx +59 -41
- package/src/containers/StructuredData/actions.tsx +1 -1
- package/src/modules/Categories/CategoriesList/CategoryItem/index.tsx +71 -43
- package/src/modules/Categories/CategoriesList/CategoryItem/style.tsx +9 -1
- package/src/modules/Categories/CategoriesList/CategoryNav/index.tsx +0 -1
- package/src/modules/Categories/CategoriesList/CategoryPanel/index.tsx +1 -0
- package/src/modules/Categories/CategoriesList/helpers.tsx +135 -94
- package/src/modules/Categories/CategoriesList/index.tsx +153 -157
- package/src/modules/Categories/CategoriesList/style.tsx +0 -3
- package/src/modules/FileDrive/FolderTree/MenuItem/index.tsx +83 -0
- package/src/modules/FileDrive/FolderTree/MenuItem/style.tsx +69 -0
- package/src/modules/FileDrive/FolderTree/MenuList/index.tsx +26 -0
- package/src/modules/FileDrive/FolderTree/index.tsx +12 -58
- package/src/modules/FileDrive/FolderTree/style.tsx +6 -27
- package/src/modules/Forms/FormCategoriesList/CategoryItem/index.tsx +11 -17
- package/src/modules/Forms/FormCategoriesList/CategoryItem/style.tsx +4 -1
- package/src/modules/Forms/FormCategoriesList/index.tsx +68 -53
- package/src/modules/Navigation/Menus/List/Table/Item/index.tsx +45 -16
- package/src/modules/Navigation/Menus/List/Table/Item/style.tsx +8 -3
- package/src/modules/Navigation/Menus/List/Table/helpers.tsx +132 -74
- package/src/modules/Navigation/Menus/List/Table/index.tsx +119 -86
- package/src/modules/Settings/Integrations/IntegrationItem/index.tsx +11 -11
- package/src/modules/Settings/Integrations/IntegrationItem/style.tsx +9 -1
- package/src/modules/Settings/Integrations/index.tsx +59 -56
- package/src/types/index.tsx +4 -5
- package/src/modules/FileDrive/FolderTree/utils.tsx +0 -91
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import styled from "styled-components";
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const TreeWrapper = styled.div`
|
|
4
|
+
ul:not(:first-child) {
|
|
5
|
+
padding-left: ${(p) => p.theme.spacing.s};
|
|
6
|
+
}
|
|
7
|
+
`;
|
|
4
8
|
|
|
5
9
|
const NewIconWrapper = styled.div`
|
|
6
10
|
width: 16px;
|
|
@@ -28,37 +32,12 @@ const Item = styled.div<{ selected: boolean }>`
|
|
|
28
32
|
}
|
|
29
33
|
`;
|
|
30
34
|
|
|
31
|
-
const Name = styled.div`
|
|
32
|
-
${(p) => p.theme.textStyle.uiS};
|
|
33
|
-
color: ${(p) => p.theme.color.textMediumEmphasis};
|
|
34
|
-
margin-left: ${(p) => p.theme.spacing.xs};
|
|
35
|
-
white-space: nowrap;
|
|
36
|
-
`;
|
|
37
|
-
|
|
38
35
|
const RootName = styled.div`
|
|
39
36
|
${(p) => p.theme.textStyle.uiM};
|
|
40
37
|
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
41
38
|
white-space: nowrap;
|
|
42
39
|
`;
|
|
43
40
|
|
|
44
|
-
const IconWrapper = styled.div`
|
|
45
|
-
width: 16px;
|
|
46
|
-
height: 16px;
|
|
47
|
-
svg {
|
|
48
|
-
path {
|
|
49
|
-
fill: ${(p) => p.theme.color.iconMediumEmphasis};
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
`;
|
|
53
|
-
|
|
54
|
-
const ArrowWrapper = styled.div`
|
|
55
|
-
width: 16px;
|
|
56
|
-
height: 16px;
|
|
57
|
-
margin-right: ${(p) => p.theme.spacing.xxs};
|
|
58
|
-
white-space: nowrap;
|
|
59
|
-
flex-shrink: 0;
|
|
60
|
-
`;
|
|
61
|
-
|
|
62
41
|
const Title = styled.div`
|
|
63
42
|
${(p) => p.theme.textStyle.uiM};
|
|
64
43
|
color: ${(p) => p.theme.color.textMediumEmphasis};
|
|
@@ -66,4 +45,4 @@ const Title = styled.div`
|
|
|
66
45
|
margin-bottom: ${(p) => p.theme.spacing.xs};
|
|
67
46
|
`;
|
|
68
47
|
|
|
69
|
-
export {
|
|
48
|
+
export { TreeWrapper, Item, RootName, Title, NewIconWrapper };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
|
-
import {
|
|
3
|
+
import { useSortable } from "@dnd-kit/sortable";
|
|
4
4
|
|
|
5
5
|
import { formsActions } from "@ax/containers/Forms";
|
|
6
6
|
import { useModal, usePermission } from "@ax/hooks";
|
|
@@ -12,17 +12,7 @@ import { DeleteModal } from "../atoms";
|
|
|
12
12
|
import * as S from "./style";
|
|
13
13
|
|
|
14
14
|
const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
|
|
15
|
-
const {
|
|
16
|
-
category,
|
|
17
|
-
isSelected,
|
|
18
|
-
hoverCheck,
|
|
19
|
-
isDragging,
|
|
20
|
-
provided,
|
|
21
|
-
isSiteView,
|
|
22
|
-
deleteFormCategory,
|
|
23
|
-
onChange,
|
|
24
|
-
toggleToast,
|
|
25
|
-
} = props;
|
|
15
|
+
const { category, isSelected, hoverCheck, isDragging, isSiteView, deleteFormCategory, onChange, toggleToast } = props;
|
|
26
16
|
|
|
27
17
|
const { isOpen, toggleModal } = useModal();
|
|
28
18
|
const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
|
|
@@ -34,6 +24,10 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
|
|
|
34
24
|
const isAllowedToEditFormCategory = usePermission(editFormCategoryPermission);
|
|
35
25
|
const isAllowedToDeleteFormCategory = usePermission(deleteFormCategoryPermission);
|
|
36
26
|
|
|
27
|
+
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({
|
|
28
|
+
id: category.id,
|
|
29
|
+
});
|
|
30
|
+
|
|
37
31
|
const { content } = category;
|
|
38
32
|
|
|
39
33
|
const handleClick = () => !isDragging && isAllowedToEditFormCategory && toggleModal();
|
|
@@ -75,12 +69,13 @@ const CategoryItem = (props: ICategoryItemProps): JSX.Element => {
|
|
|
75
69
|
return (
|
|
76
70
|
<>
|
|
77
71
|
<S.CategoryRow
|
|
78
|
-
role="rowgroup"
|
|
79
72
|
selected={isSelected}
|
|
80
73
|
onClick={handleClick}
|
|
81
|
-
ref={
|
|
82
|
-
{
|
|
83
|
-
{
|
|
74
|
+
ref={setNodeRef}
|
|
75
|
+
cssTransform={transform}
|
|
76
|
+
transition={transition}
|
|
77
|
+
{...listeners}
|
|
78
|
+
{...attributes}
|
|
84
79
|
>
|
|
85
80
|
<S.HandleCell>
|
|
86
81
|
<S.IconWrapperDrag role="cell">
|
|
@@ -128,7 +123,6 @@ interface IProps {
|
|
|
128
123
|
toggleToast(state: any): void;
|
|
129
124
|
hoverCheck?: boolean;
|
|
130
125
|
isDragging: boolean;
|
|
131
|
-
provided: DraggableProvided;
|
|
132
126
|
isSiteView: boolean;
|
|
133
127
|
}
|
|
134
128
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import styled from "styled-components";
|
|
2
|
+
import { CSS, Transform } from "@dnd-kit/utilities";
|
|
2
3
|
|
|
3
4
|
import { Cell, Row } from "@ax/components/TableList/TableItem/style";
|
|
4
5
|
import { ActionMenu } from "@ax/components";
|
|
@@ -41,7 +42,9 @@ const StyledActionMenu = styled(ActionMenu)`
|
|
|
41
42
|
margin-left: auto;
|
|
42
43
|
`;
|
|
43
44
|
|
|
44
|
-
const CategoryRow = styled(Row)
|
|
45
|
+
const CategoryRow = styled(Row)<{ cssTransform: Transform | null; transition?: string }>`
|
|
46
|
+
transform: ${(p) => CSS.Transform.toString(p.cssTransform)};
|
|
47
|
+
transition: ${(p) => p.transition};
|
|
45
48
|
&:hover {
|
|
46
49
|
${StyledActionMenu} {
|
|
47
50
|
opacity: 1;
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import React, { useEffect, useState, useRef } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
import { useParams } from "react-router-dom";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
closestCenter,
|
|
6
|
+
DndContext,
|
|
7
|
+
DragEndEvent,
|
|
8
|
+
DragStartEvent,
|
|
9
|
+
KeyboardSensor,
|
|
10
|
+
PointerSensor,
|
|
11
|
+
useSensor,
|
|
12
|
+
useSensors,
|
|
13
|
+
} from "@dnd-kit/core";
|
|
14
|
+
import { SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
5
15
|
|
|
6
16
|
import { formsActions } from "@ax/containers/Forms";
|
|
7
17
|
import { IRootState, FormCategory, FormCategoriesOrderParams } from "@ax/types";
|
|
@@ -48,6 +58,17 @@ const FormCategoriesList = (props: IProps): JSX.Element => {
|
|
|
48
58
|
const isAllowedToCreateFormCategory = usePermission(createFormCategoryPermission);
|
|
49
59
|
const isAllowedToDeleteFormCategory = usePermission(deleteFormCategoryPermission);
|
|
50
60
|
|
|
61
|
+
const sensors = useSensors(
|
|
62
|
+
useSensor(PointerSensor, {
|
|
63
|
+
activationConstraint: {
|
|
64
|
+
distance: 5,
|
|
65
|
+
},
|
|
66
|
+
}),
|
|
67
|
+
useSensor(KeyboardSensor, {
|
|
68
|
+
coordinateGetter: sortableKeyboardCoordinates,
|
|
69
|
+
})
|
|
70
|
+
);
|
|
71
|
+
|
|
51
72
|
const catIds = categories.map((category) => category.id);
|
|
52
73
|
const totalItems = categories.length;
|
|
53
74
|
|
|
@@ -116,55 +137,33 @@ const FormCategoriesList = (props: IProps): JSX.Element => {
|
|
|
116
137
|
/>
|
|
117
138
|
);
|
|
118
139
|
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<Draggable draggableId={`${category.id}`} index={i} key={category.id}>
|
|
126
|
-
{(provided) => (
|
|
127
|
-
<CategoryItem
|
|
128
|
-
category={category}
|
|
129
|
-
key={category.id}
|
|
130
|
-
isSelected={isItemSelected}
|
|
131
|
-
onChange={addToBulkSelection}
|
|
132
|
-
toggleToast={toggleToast}
|
|
133
|
-
hoverCheck={checkState.hoverCheck}
|
|
134
|
-
isDragging={isDragging}
|
|
135
|
-
provided={provided}
|
|
136
|
-
isSiteView={isSiteView}
|
|
137
|
-
/>
|
|
138
|
-
)}
|
|
139
|
-
</Draggable>
|
|
140
|
-
);
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
const onDragEnd = async (result: DropResult) => {
|
|
145
|
-
if (!result.destination || result.destination.index === result.source.index) {
|
|
146
|
-
setDraggingId(null);
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
140
|
+
const handleDragStart = (event: DragStartEvent) => {
|
|
141
|
+
const { active } = event;
|
|
142
|
+
setDraggingId(active.id as number);
|
|
143
|
+
};
|
|
149
144
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
: 1;
|
|
145
|
+
const handleOnDragEnd = async (event: DragEndEvent) => {
|
|
146
|
+
const { active, over } = event;
|
|
153
147
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
position: newPosition,
|
|
158
|
-
relatedSite: currentSiteID,
|
|
159
|
-
};
|
|
148
|
+
if (over && active.id !== over.id) {
|
|
149
|
+
const oldIndex = categories.map((e) => e.id).indexOf(active.id as number);
|
|
150
|
+
const newIndex = categories.map((e) => e.id).indexOf(over.id as number);
|
|
160
151
|
|
|
161
|
-
|
|
152
|
+
const newPosition = newIndex ? categories[newIndex - (newIndex > oldIndex ? 0 : 1)].position + 1 : 1;
|
|
153
|
+
|
|
154
|
+
const params: FormCategoriesOrderParams = {
|
|
155
|
+
id: active.id as number,
|
|
156
|
+
categoryType: category,
|
|
157
|
+
position: newPosition,
|
|
158
|
+
relatedSite: currentSiteID,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
await orderFormCategory(params);
|
|
162
|
+
}
|
|
162
163
|
|
|
163
164
|
setDraggingId(null);
|
|
164
165
|
};
|
|
165
166
|
|
|
166
|
-
const onBeforeCapture = (start: BeforeCapture) => setDraggingId(parseInt(start.draggableId));
|
|
167
|
-
|
|
168
167
|
const isEmpty = totalItems === 0;
|
|
169
168
|
const emptyProps = {
|
|
170
169
|
message: isAllowedToCreateFormCategory
|
|
@@ -213,16 +212,32 @@ const FormCategoriesList = (props: IProps): JSX.Element => {
|
|
|
213
212
|
<S.Notification>
|
|
214
213
|
Reorder your category list by <strong>drag & drop</strong>.
|
|
215
214
|
</S.Notification>
|
|
216
|
-
<
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
|
|
215
|
+
<DndContext
|
|
216
|
+
sensors={sensors}
|
|
217
|
+
collisionDetection={closestCenter}
|
|
218
|
+
onDragEnd={handleOnDragEnd}
|
|
219
|
+
onDragStart={handleDragStart}
|
|
220
|
+
>
|
|
221
|
+
<SortableContext items={catIds} strategy={verticalListSortingStrategy}>
|
|
222
|
+
{categories.map((category: FormCategory) => {
|
|
223
|
+
const isItemSelected = isSelected(category.id);
|
|
224
|
+
const isDragging = !!draggingId && draggingId !== category.id;
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<CategoryItem
|
|
228
|
+
category={category}
|
|
229
|
+
key={category.id}
|
|
230
|
+
isSelected={isItemSelected}
|
|
231
|
+
onChange={addToBulkSelection}
|
|
232
|
+
toggleToast={toggleToast}
|
|
233
|
+
hoverCheck={checkState.hoverCheck}
|
|
234
|
+
isDragging={isDragging}
|
|
235
|
+
isSiteView={isSiteView}
|
|
236
|
+
/>
|
|
237
|
+
);
|
|
238
|
+
})}
|
|
239
|
+
</SortableContext>
|
|
240
|
+
</DndContext>
|
|
226
241
|
</>
|
|
227
242
|
)}
|
|
228
243
|
</TableList>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
|
+
import { AnimateLayoutChanges, useSortable } from "@dnd-kit/sortable";
|
|
3
4
|
|
|
4
5
|
import { menuActions } from "@ax/containers/Navigation";
|
|
5
6
|
import { useModal, usePermission } from "@ax/hooks";
|
|
@@ -12,12 +13,6 @@ import { RemoveModal } from "./atoms";
|
|
|
12
13
|
|
|
13
14
|
import * as S from "./style";
|
|
14
15
|
|
|
15
|
-
const DragButton = ({ dragHandleProps }: any) => (
|
|
16
|
-
<S.IconWrapperDrag {...dragHandleProps}>
|
|
17
|
-
<Icon name="drag" size="16" />
|
|
18
|
-
</S.IconWrapperDrag>
|
|
19
|
-
);
|
|
20
|
-
|
|
21
16
|
const Item = (props: IItemProps): JSX.Element => {
|
|
22
17
|
const {
|
|
23
18
|
item,
|
|
@@ -25,11 +20,12 @@ const Item = (props: IItemProps): JSX.Element => {
|
|
|
25
20
|
resetItemValues,
|
|
26
21
|
setItemValue,
|
|
27
22
|
updateFormValue,
|
|
28
|
-
icon,
|
|
29
|
-
dragHandleProps,
|
|
30
23
|
depth,
|
|
31
|
-
|
|
24
|
+
indentationWidth,
|
|
25
|
+
collapsed,
|
|
26
|
+
onCollapse,
|
|
32
27
|
} = props;
|
|
28
|
+
|
|
33
29
|
const deleteItem = (item: IMenuItem, deleteChildren?: boolean) => deleteMenuItem(item.id, deleteChildren);
|
|
34
30
|
const hasChildren = !!item.children.length;
|
|
35
31
|
|
|
@@ -50,6 +46,17 @@ const Item = (props: IItemProps): JSX.Element => {
|
|
|
50
46
|
const [isOpenedSecond, setIsOpenedSecond] = useState(false);
|
|
51
47
|
const { isOpen: isConfigOpen, toggleModal: toggleConfigModal } = useModal(false);
|
|
52
48
|
|
|
49
|
+
const animateLayoutChanges: AnimateLayoutChanges = ({ isSorting, wasDragging }) =>
|
|
50
|
+
isSorting || wasDragging ? false : true;
|
|
51
|
+
|
|
52
|
+
const { attributes, isDragging, listeners, setDraggableNodeRef, setDroppableNodeRef, transform, transition } =
|
|
53
|
+
useSortable({
|
|
54
|
+
id: item.id,
|
|
55
|
+
animateLayoutChanges,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const handleIconClick = (e: React.MouseEvent<HTMLDivElement>) => e.stopPropagation();
|
|
59
|
+
|
|
53
60
|
const toggleSecondaryPanel = () => setIsOpenedSecond(!isOpenedSecond);
|
|
54
61
|
const resetValues = () => resetItemValues();
|
|
55
62
|
|
|
@@ -84,10 +91,24 @@ const Item = (props: IItemProps): JSX.Element => {
|
|
|
84
91
|
|
|
85
92
|
return (
|
|
86
93
|
<>
|
|
87
|
-
<S.Container
|
|
88
|
-
|
|
94
|
+
<S.Container
|
|
95
|
+
disabled={!isAllowedToMangageMenus}
|
|
96
|
+
identationWidth={isDragging ? indentationWidth : indentationWidth * depth}
|
|
97
|
+
ref={setDroppableNodeRef}
|
|
98
|
+
>
|
|
99
|
+
<S.Component
|
|
100
|
+
onClick={openModal}
|
|
101
|
+
isGroup={isGroupingElement}
|
|
102
|
+
ref={setDraggableNodeRef}
|
|
103
|
+
cssTransform={transform}
|
|
104
|
+
transition={transition}
|
|
105
|
+
{...listeners}
|
|
106
|
+
{...attributes}
|
|
107
|
+
>
|
|
89
108
|
<S.FlexWrapper>
|
|
90
|
-
<
|
|
109
|
+
<S.IconWrapperDrag>
|
|
110
|
+
<Icon name="drag" size="16" />
|
|
111
|
+
</S.IconWrapperDrag>
|
|
91
112
|
{isGroupingElement && (
|
|
92
113
|
<S.GroupIcon>
|
|
93
114
|
<Icon name="unlink" size="16" />
|
|
@@ -96,7 +117,15 @@ const Item = (props: IItemProps): JSX.Element => {
|
|
|
96
117
|
{item.label}
|
|
97
118
|
</S.FlexWrapper>
|
|
98
119
|
<S.FlexWrapper>
|
|
99
|
-
|
|
120
|
+
{onCollapse && (
|
|
121
|
+
<S.IconWrapper onClick={handleIconClick}>
|
|
122
|
+
{collapsed ? (
|
|
123
|
+
<IconAction icon="UpArrow" size="m" onClick={onCollapse} />
|
|
124
|
+
) : (
|
|
125
|
+
<IconAction icon="DownArrow" size="m" onClick={onCollapse} />
|
|
126
|
+
)}
|
|
127
|
+
</S.IconWrapper>
|
|
128
|
+
)}
|
|
100
129
|
{depth === 1 && hasChildren && isAllowedToMangageMenus && (
|
|
101
130
|
<S.IconWrapper>
|
|
102
131
|
<Tooltip content="Options view">
|
|
@@ -125,10 +154,10 @@ const Item = (props: IItemProps): JSX.Element => {
|
|
|
125
154
|
|
|
126
155
|
interface IProps {
|
|
127
156
|
item: IMenuItem;
|
|
128
|
-
icon: JSX.Element;
|
|
129
|
-
dragHandleProps: any;
|
|
130
157
|
depth: number;
|
|
131
|
-
|
|
158
|
+
indentationWidth: number;
|
|
159
|
+
collapsed: boolean;
|
|
160
|
+
onCollapse?: () => void;
|
|
132
161
|
}
|
|
133
162
|
|
|
134
163
|
interface IDispatchProps {
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import styled from "styled-components";
|
|
2
|
+
import { CSS, Transform } from "@dnd-kit/utilities";
|
|
3
|
+
|
|
2
4
|
import { ActionMenu } from "@ax/components";
|
|
3
5
|
|
|
4
|
-
const Container = styled.div<{ disabled: boolean }>`
|
|
6
|
+
const Container = styled.div<{ disabled: boolean; identationWidth: number }>`
|
|
5
7
|
display: flex;
|
|
6
8
|
align-items: center;
|
|
7
9
|
width: 100%;
|
|
8
10
|
pointer-events: ${(p) => (p.disabled ? "none" : "auto")};
|
|
11
|
+
padding-left: ${(p) => `${p.identationWidth}px`};
|
|
9
12
|
`;
|
|
10
13
|
|
|
11
14
|
const IconWrapper = styled.div`
|
|
@@ -27,7 +30,8 @@ const StyledActionMenu = styled(ActionMenu)`
|
|
|
27
30
|
opacity: 0;
|
|
28
31
|
`;
|
|
29
32
|
|
|
30
|
-
const Component = styled.span<{ isGroup: boolean }>`
|
|
33
|
+
const Component = styled.span<{ isGroup: boolean; cssTransform: Transform | null; transition?: string }>`
|
|
34
|
+
${(p) => p.theme.textStyle.fieldLabel};
|
|
31
35
|
width: 100%;
|
|
32
36
|
display: flex;
|
|
33
37
|
align-items: center;
|
|
@@ -42,9 +46,10 @@ const Component = styled.span<{ isGroup: boolean }>`
|
|
|
42
46
|
padding: 0 ${(p) => p.theme.spacing.s};
|
|
43
47
|
border-radius: ${(p) => p.theme.radii.s};
|
|
44
48
|
box-shadow: ${(p) => p.theme.shadow.shadowS};
|
|
45
|
-
${(p) => p.theme.textStyle.fieldLabel};
|
|
46
49
|
text-align: left;
|
|
47
50
|
cursor: pointer;
|
|
51
|
+
transform: ${(p) => CSS.Transform.toString(p.cssTransform)};
|
|
52
|
+
transition: ${(p) => p.transition};
|
|
48
53
|
|
|
49
54
|
&::after {
|
|
50
55
|
content: "";
|
|
@@ -1,92 +1,150 @@
|
|
|
1
|
-
import { ItemId, TreeData, TreeItem } from "@atlaskit/tree";
|
|
2
|
-
import { isEmptyObj } from "@ax/helpers";
|
|
3
1
|
import { IMenuItem } from "@ax/types";
|
|
2
|
+
import type { UniqueIdentifier } from "@dnd-kit/core";
|
|
3
|
+
import { arrayMove } from "@dnd-kit/sortable";
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
id: "root",
|
|
11
|
-
children: [],
|
|
12
|
-
hasChildren: true,
|
|
13
|
-
isExpanded: true,
|
|
14
|
-
isChildrenLoading: false,
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const previousMenu: IMenuItem[] = [];
|
|
20
|
-
if (!isEmptyObj(tree.items)) {
|
|
21
|
-
previousMenu.push(...formatMenu(tree));
|
|
22
|
-
}
|
|
5
|
+
const flatten = (items: TreeItem[], parentId: UniqueIdentifier | null = null, depth = 0): FlattenedItem[] => {
|
|
6
|
+
return items.reduce<FlattenedItem[]>((acc, item, index) => {
|
|
7
|
+
return [...acc, { ...item, parentId, depth, index }, ...flatten(item.children || [], item.id, depth + 1)];
|
|
8
|
+
}, []);
|
|
9
|
+
};
|
|
23
10
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const getElement = (item: IMenuItem, previousMenu: IMenuItem) => {
|
|
30
|
-
normalized.items[item.id] = {
|
|
31
|
-
...item,
|
|
32
|
-
children: [],
|
|
33
|
-
isExpanded: isItemExpanded(previousMenu),
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
if (item.children && item.children.length > 0) {
|
|
37
|
-
item.children.forEach((child: IMenuItem, index: number) => {
|
|
38
|
-
normalized.items[item.id].children.push(child.id);
|
|
39
|
-
getElement(child, previousMenu?.children[index]);
|
|
40
|
-
});
|
|
41
|
-
normalized.items[item.id].hasChildren = true;
|
|
42
|
-
} else {
|
|
43
|
-
normalized.items[item.id].hasChildren = false;
|
|
44
|
-
}
|
|
45
|
-
};
|
|
11
|
+
const flattenTree = (items: TreeItem[]): FlattenedItem[] => {
|
|
12
|
+
return flatten(items);
|
|
13
|
+
};
|
|
46
14
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
});
|
|
15
|
+
const buildTree = (flattenedItems: FlattenedItem[]): TreeItem[] => {
|
|
16
|
+
const root = { id: 0, children: [] };
|
|
17
|
+
const nodes: Record<string, Partial<TreeItem>> = { [root.id]: root };
|
|
18
|
+
const items = flattenedItems.map((item) => ({ ...item, children: [] })) as FlattenedItem[];
|
|
51
19
|
|
|
52
|
-
|
|
53
|
-
};
|
|
20
|
+
for (const item of items) {
|
|
21
|
+
const { id, children } = item;
|
|
22
|
+
const parentId = item.parentId ?? root.id;
|
|
23
|
+
const parent = nodes[parentId] ?? findItem(items, parentId);
|
|
54
24
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const elements: TreeItem[] = [];
|
|
58
|
-
for (const item of Object.values(items)) {
|
|
59
|
-
elements.push(item);
|
|
25
|
+
nodes[id] = { id, children };
|
|
26
|
+
parent.children && parent.children.push(item);
|
|
60
27
|
}
|
|
61
|
-
return elements;
|
|
62
|
-
};
|
|
63
28
|
|
|
64
|
-
|
|
65
|
-
const element = elements.find((element: IMenuItem) => element.id === child);
|
|
66
|
-
const children = element?.children.map((child: ItemId) => getChild(child, elements));
|
|
67
|
-
return { ...element, children };
|
|
29
|
+
return root.children;
|
|
68
30
|
};
|
|
69
31
|
|
|
70
|
-
const
|
|
71
|
-
|
|
32
|
+
const findItem = (items: FlattenedItem[], itemId: UniqueIdentifier) => {
|
|
33
|
+
return items.find(({ id }) => id === itemId);
|
|
34
|
+
};
|
|
72
35
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
36
|
+
function getDragDepth(offset: number, indentationWidth: number) {
|
|
37
|
+
return Math.round(offset / indentationWidth);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getProjection(
|
|
41
|
+
items: FlattenedItem[],
|
|
42
|
+
activeId: UniqueIdentifier,
|
|
43
|
+
overId: UniqueIdentifier,
|
|
44
|
+
dragOffset: number,
|
|
45
|
+
indentationWidth: number
|
|
46
|
+
) {
|
|
47
|
+
const overItemIndex = items.findIndex(({ id }) => id === overId);
|
|
48
|
+
const activeItemIndex = items.findIndex(({ id }) => id === activeId);
|
|
49
|
+
const activeItem = items[activeItemIndex];
|
|
50
|
+
const newItems = arrayMove(items, activeItemIndex, overItemIndex);
|
|
51
|
+
const previousItem = newItems[overItemIndex - 1];
|
|
52
|
+
const nextItem = newItems[overItemIndex + 1];
|
|
53
|
+
const dragDepth = getDragDepth(dragOffset, indentationWidth);
|
|
54
|
+
const projectedDepth = activeItem.depth + dragDepth;
|
|
55
|
+
const maxDepth = getMaxDepth({
|
|
56
|
+
previousItem,
|
|
76
57
|
});
|
|
58
|
+
const minDepth = getMinDepth({ nextItem });
|
|
59
|
+
let depth = projectedDepth;
|
|
77
60
|
|
|
78
|
-
|
|
79
|
-
|
|
61
|
+
if (projectedDepth >= maxDepth) {
|
|
62
|
+
depth = maxDepth;
|
|
63
|
+
} else if (projectedDepth < minDepth) {
|
|
64
|
+
depth = minDepth;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { depth, maxDepth, minDepth, parentId: getParentId() };
|
|
68
|
+
|
|
69
|
+
function getParentId() {
|
|
70
|
+
if (depth === 0 || !previousItem) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (depth === previousItem.depth) {
|
|
75
|
+
return previousItem.parentId;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (depth > previousItem.depth) {
|
|
79
|
+
return previousItem.id;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const newParent = newItems
|
|
83
|
+
.slice(0, overItemIndex)
|
|
84
|
+
.reverse()
|
|
85
|
+
.find((item) => item.depth === depth)?.parentId;
|
|
80
86
|
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
return newParent ?? null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getMinDepth({ nextItem }: { nextItem: FlattenedItem }) {
|
|
92
|
+
if (nextItem) {
|
|
93
|
+
return nextItem.depth;
|
|
94
|
+
}
|
|
83
95
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function getMaxDepth({ previousItem }: { previousItem: FlattenedItem }) {
|
|
100
|
+
if (previousItem) {
|
|
101
|
+
return previousItem.depth + 1;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const removeChildrenOf = (items: FlattenedItem[], ids: UniqueIdentifier[]) => {
|
|
108
|
+
const excludeParentIds = [...ids];
|
|
109
|
+
|
|
110
|
+
return items.filter((item) => {
|
|
111
|
+
if (item.parentId && excludeParentIds.includes(item.parentId)) {
|
|
112
|
+
if (item.children && item.children.length) {
|
|
113
|
+
excludeParentIds.push(item.id);
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
88
117
|
|
|
89
|
-
|
|
118
|
+
return true;
|
|
119
|
+
});
|
|
90
120
|
};
|
|
91
121
|
|
|
92
|
-
export
|
|
122
|
+
export function setProperty<T extends keyof TreeItem>(
|
|
123
|
+
items: TreeItem[],
|
|
124
|
+
id: UniqueIdentifier,
|
|
125
|
+
property: T,
|
|
126
|
+
setter: (value: TreeItem[T]) => TreeItem[T]
|
|
127
|
+
) {
|
|
128
|
+
for (const item of items) {
|
|
129
|
+
if (item.id === id) {
|
|
130
|
+
item[property] = setter(item[property]);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (item.children && item.children.length) {
|
|
135
|
+
item.children = setProperty(item.children || [], id, property, setter);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return [...items];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface FlattenedItem extends IMenuItem {
|
|
143
|
+
parentId: UniqueIdentifier | null;
|
|
144
|
+
depth: number;
|
|
145
|
+
index: number;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export type TreeItem = IMenuItem;
|
|
149
|
+
|
|
150
|
+
export { flattenTree, buildTree, removeChildrenOf };
|