@geotab/zenith 3.5.0 → 3.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/index.css +57 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -3
- package/dist/list/hooks/useDragAndDrop.d.ts +19 -1
- package/dist/list/hooks/useDragAndDrop.js +65 -26
- package/dist/list/utils/findElement.d.ts +1 -0
- package/dist/list/utils/findElement.js +13 -0
- package/dist/list/utils/findItemPosition.d.ts +1 -1
- package/dist/list/utils/findItemPosition.js +2 -2
- package/dist/list/utils/isDragNotClick.d.ts +6 -0
- package/dist/list/utils/isDragNotClick.js +9 -0
- package/dist/nav/context/nav.context.d.ts +2 -0
- package/dist/nav/context/nav.context.js +3 -1
- package/dist/nav/nav.js +20 -8
- package/dist/nav/navEditList/navEditList.d.ts +7 -0
- package/dist/nav/navEditList/navEditList.js +22 -0
- package/dist/nav/navEditList/navEditListUtils.d.ts +3 -0
- package/dist/nav/navEditList/navEditListUtils.js +14 -0
- package/dist/nav/navEditSection/navEditSection.d.ts +3 -0
- package/dist/nav/navEditSection/navEditSection.js +7 -0
- package/dist/nav/navFooter/navEditFooter/navEditFooter.d.ts +11 -0
- package/dist/nav/navFooter/navEditFooter/navEditFooter.js +110 -0
- package/dist/nav/navFooter/navFooter.d.ts +2 -7
- package/dist/nav/navFooter/navFooter.js +4 -4
- package/dist/nav/navItem/navActionItem.d.ts +14 -0
- package/dist/nav/navItem/navActionItem.js +15 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.js +1 -0
- package/esm/list/hooks/useDragAndDrop.d.ts +19 -1
- package/esm/list/hooks/useDragAndDrop.js +65 -26
- package/esm/list/utils/findElement.d.ts +1 -0
- package/esm/list/utils/findElement.js +9 -0
- package/esm/list/utils/findItemPosition.d.ts +1 -1
- package/esm/list/utils/findItemPosition.js +2 -2
- package/esm/list/utils/isDragNotClick.d.ts +6 -0
- package/esm/list/utils/isDragNotClick.js +5 -0
- package/esm/nav/context/nav.context.d.ts +2 -0
- package/esm/nav/context/nav.context.js +3 -1
- package/esm/nav/nav.js +21 -9
- package/esm/nav/navEditList/navEditList.d.ts +7 -0
- package/esm/nav/navEditList/navEditList.js +18 -0
- package/esm/nav/navEditList/navEditListUtils.d.ts +3 -0
- package/esm/nav/navEditList/navEditListUtils.js +10 -0
- package/esm/nav/navEditSection/navEditSection.d.ts +3 -0
- package/esm/nav/navEditSection/navEditSection.js +3 -0
- package/esm/nav/navFooter/navEditFooter/navEditFooter.d.ts +11 -0
- package/esm/nav/navFooter/navEditFooter/navEditFooter.js +101 -0
- package/esm/nav/navFooter/navFooter.d.ts +2 -7
- package/esm/nav/navFooter/navFooter.js +4 -4
- package/esm/nav/navItem/navActionItem.d.ts +14 -0
- package/esm/nav/navItem/navActionItem.js +11 -0
- package/package.json +4 -4
- package/dist/list/utils/findListElement.d.ts +0 -1
- package/dist/list/utils/findListElement.js +0 -13
- package/esm/list/utils/findListElement.d.ts +0 -1
- package/esm/list/utils/findListElement.js +0 -9
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
injectString
|
|
5
|
+
} = require("../../../utils/localization/translationsDictionary");
|
|
6
|
+
Object.defineProperty(exports, "__esModule", {
|
|
7
|
+
value: true
|
|
8
|
+
});
|
|
9
|
+
exports.NavEditFooter = void 0;
|
|
10
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
11
|
+
injectString("cs", "Save", "Ulo\u017Eit");
|
|
12
|
+
injectString("da-DK", "Save", "Spare");
|
|
13
|
+
injectString("de", "Save", "Speichern");
|
|
14
|
+
injectString("en", "Save", "Save");
|
|
15
|
+
injectString("es", "Save", "Guardar");
|
|
16
|
+
injectString("fi-FI", "Save", "S\xE4\xE4st\xE4\xE4");
|
|
17
|
+
injectString("fr", "Save", "Enregistrer");
|
|
18
|
+
injectString("fr-FR", "Save", "Enregistrer");
|
|
19
|
+
injectString("hu-HU", "Save", "Ment\xE9s");
|
|
20
|
+
injectString("id", "Save", "Simpan");
|
|
21
|
+
injectString("it", "Save", "Salvare");
|
|
22
|
+
injectString("ja", "Save", "\u4FDD\u5B58");
|
|
23
|
+
injectString("ko-KR", "Save", "\uC800\uC7A5");
|
|
24
|
+
injectString("ms", "Save", "Simpan");
|
|
25
|
+
injectString("nb-NO", "Save", "Lagre");
|
|
26
|
+
injectString("nl", "Save", "Opslaan");
|
|
27
|
+
injectString("pl", "Save", "Zapisz");
|
|
28
|
+
injectString("pt-BR", "Save", "Salvar");
|
|
29
|
+
injectString("pt-PT", "Save", "Guardar");
|
|
30
|
+
injectString("sk-SK", "Save", "Ulo\u017Ei\u0165");
|
|
31
|
+
injectString("sv", "Save", "Spara");
|
|
32
|
+
injectString("th", "Save", "\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01");
|
|
33
|
+
injectString("tr", "Save", "Kaydet");
|
|
34
|
+
injectString("zh-Hans", "Save", "\u4FDD\u5B58");
|
|
35
|
+
injectString("zh-TW", "Save", "\u5132\u5B58");
|
|
36
|
+
injectString("ro-RO", "Save", "Salva\u021Bi");
|
|
37
|
+
injectString("cs", "Cancel", "Zru\u0161it");
|
|
38
|
+
injectString("da-DK", "Cancel", "Annuller");
|
|
39
|
+
injectString("de", "Cancel", "Abbrechen");
|
|
40
|
+
injectString("en", "Cancel", "Cancel");
|
|
41
|
+
injectString("es", "Cancel", "Cancelar");
|
|
42
|
+
injectString("fi-FI", "Cancel", "Peruuta");
|
|
43
|
+
injectString("fr", "Cancel", "Annuler");
|
|
44
|
+
injectString("fr-FR", "Cancel", "Annuler");
|
|
45
|
+
injectString("hu-HU", "Cancel", "M\xE9gse");
|
|
46
|
+
injectString("id", "Cancel", "Batalkan");
|
|
47
|
+
injectString("it", "Cancel", "Annullare");
|
|
48
|
+
injectString("ja", "Cancel", "\u30AD\u30E3\u30F3\u30BB\u30EB");
|
|
49
|
+
injectString("ko-KR", "Cancel", "\uCDE8\uC18C");
|
|
50
|
+
injectString("ms", "Cancel", "Batal");
|
|
51
|
+
injectString("nb-NO", "Cancel", "Avbryt");
|
|
52
|
+
injectString("nl", "Cancel", "Annuleren");
|
|
53
|
+
injectString("pl", "Cancel", "Anuluj");
|
|
54
|
+
injectString("pt-BR", "Cancel", "Cancelar");
|
|
55
|
+
injectString("pt-PT", "Cancel", "Cancelar");
|
|
56
|
+
injectString("sk-SK", "Cancel", "Zru\u0161.");
|
|
57
|
+
injectString("sv", "Cancel", "Avbryt");
|
|
58
|
+
injectString("th", "Cancel", "\u0E22\u0E01\u0E40\u0E25\u0E34\u0E01");
|
|
59
|
+
injectString("tr", "Cancel", "\u0130ptal");
|
|
60
|
+
injectString("zh-Hans", "Cancel", "\u53D6\u6D88");
|
|
61
|
+
injectString("zh-TW", "Cancel", "\u53D6\u6D88");
|
|
62
|
+
injectString("ro-RO", "Cancel", "Anula\u021Bi");
|
|
63
|
+
const button_1 = require("../../../button/button");
|
|
64
|
+
const classNames_1 = require("../../../commonHelpers/classNames/classNames");
|
|
65
|
+
const useLanguage_1 = require("../../../utils/localization/useLanguage");
|
|
66
|
+
const nav_context_1 = require("../../context/nav.context");
|
|
67
|
+
const react_1 = require("react");
|
|
68
|
+
const useMobile_1 = require("../../../commonHelpers/hooks/useMobile");
|
|
69
|
+
const NavEditFooter = ({
|
|
70
|
+
className,
|
|
71
|
+
handleSaveClick,
|
|
72
|
+
handleCancelClick
|
|
73
|
+
}) => {
|
|
74
|
+
const {
|
|
75
|
+
onEditStateToggle
|
|
76
|
+
} = (0, nav_context_1.useNavContext)();
|
|
77
|
+
const isMobile = (0, useMobile_1.useMobile)();
|
|
78
|
+
const {
|
|
79
|
+
translate
|
|
80
|
+
} = (0, useLanguage_1.useLanguage)();
|
|
81
|
+
const onSaveClick = (0, react_1.useCallback)(() => {
|
|
82
|
+
onEditStateToggle === null || onEditStateToggle === void 0 ? void 0 : onEditStateToggle(false);
|
|
83
|
+
handleSaveClick();
|
|
84
|
+
}, [onEditStateToggle, handleSaveClick]);
|
|
85
|
+
const onCancelClick = (0, react_1.useCallback)(() => {
|
|
86
|
+
onEditStateToggle === null || onEditStateToggle === void 0 ? void 0 : onEditStateToggle(false);
|
|
87
|
+
handleCancelClick();
|
|
88
|
+
}, [onEditStateToggle, handleCancelClick]);
|
|
89
|
+
const navEditFooterClassNames = (0, classNames_1.classNames)(["zen-nav-edit-footer", isMobile ? "zen-nav-edit-footer--mobile" : "", className !== null && className !== void 0 ? className : ""]);
|
|
90
|
+
// needed to change buttons order between mobile and desktop modes
|
|
91
|
+
const saveButtonClassNames = (0, classNames_1.classNames)(["zen-nav-edit-footer__button", isMobile ? "zen-nav-edit-footer__button--with-margin" : ""]);
|
|
92
|
+
const saveButton = (0, jsx_runtime_1.jsx)(button_1.Button, {
|
|
93
|
+
onClick: onSaveClick,
|
|
94
|
+
className: saveButtonClassNames,
|
|
95
|
+
type: "primary",
|
|
96
|
+
children: translate("Save")
|
|
97
|
+
});
|
|
98
|
+
const cancelButton = (0, jsx_runtime_1.jsx)(button_1.Button, {
|
|
99
|
+
onClick: onCancelClick,
|
|
100
|
+
className: "zen-nav-edit-footer__button",
|
|
101
|
+
type: "secondary",
|
|
102
|
+
children: translate("Cancel")
|
|
103
|
+
});
|
|
104
|
+
return (0, jsx_runtime_1.jsxs)("div", {
|
|
105
|
+
className: navEditFooterClassNames,
|
|
106
|
+
children: [isMobile ? saveButton : cancelButton, isMobile ? cancelButton : saveButton]
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
exports.NavEditFooter = NavEditFooter;
|
|
110
|
+
exports.NavEditFooter.displayName = "NavEditFooter";
|
|
@@ -3,11 +3,6 @@ import { IZenComponentProps } from "../../commonHelpers/zenComponent";
|
|
|
3
3
|
import { FC } from "react";
|
|
4
4
|
export interface INavFooter extends IZenComponentProps {
|
|
5
5
|
}
|
|
6
|
-
export interface INavFooterBottom extends IZenComponentProps {
|
|
7
|
-
}
|
|
8
|
-
export interface INavFooterEditButton extends IZenComponentProps {
|
|
9
|
-
onClick?: () => void;
|
|
10
|
-
}
|
|
11
6
|
/**
|
|
12
7
|
* @beta This component is not fully ready yet and may change in future releases.
|
|
13
8
|
*/
|
|
@@ -16,6 +11,6 @@ export declare const NavFooter: FC<INavFooter> & {
|
|
|
16
11
|
({ children }: import("./navFooterAction/navFooterAction").INavFooterActionProps): import("react/jsx-runtime").JSX.Element | null;
|
|
17
12
|
displayName: string;
|
|
18
13
|
};
|
|
19
|
-
Bottom: FC<
|
|
20
|
-
EditButton: FC<
|
|
14
|
+
Bottom: FC<IZenComponentProps>;
|
|
15
|
+
EditButton: FC<IZenComponentProps>;
|
|
21
16
|
};
|
|
@@ -73,14 +73,14 @@ const NavFooterBottom = ({
|
|
|
73
73
|
};
|
|
74
74
|
NavFooterBottom.displayName = "NavFooterBottom";
|
|
75
75
|
const NavFooterEditButton = ({
|
|
76
|
-
className
|
|
77
|
-
onClick
|
|
76
|
+
className
|
|
78
77
|
}) => {
|
|
79
78
|
const {
|
|
80
79
|
translate
|
|
81
80
|
} = (0, useLanguage_1.useLanguage)();
|
|
82
81
|
const {
|
|
83
|
-
collapsed
|
|
82
|
+
collapsed,
|
|
83
|
+
onEditStateToggle
|
|
84
84
|
} = (0, nav_context_1.useNavContext)();
|
|
85
85
|
const isMobile = (0, useMobile_1.useMobile)();
|
|
86
86
|
return (0, jsx_runtime_1.jsx)(navItem_1.NavItem, {
|
|
@@ -88,7 +88,7 @@ const NavFooterEditButton = ({
|
|
|
88
88
|
title: translate("Edit Navigation"),
|
|
89
89
|
collapsed: !isMobile,
|
|
90
90
|
primaryIcon: iconEdit_1.IconEdit,
|
|
91
|
-
onClick: () =>
|
|
91
|
+
onClick: () => onEditStateToggle === null || onEditStateToggle === void 0 ? void 0 : onEditStateToggle(true),
|
|
92
92
|
menuAlignment: "right-bottom",
|
|
93
93
|
tooltipAlignment: collapsed ? "right" : "top"
|
|
94
94
|
}, "edit-button");
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FC, MouseEvent, ReactNode } from "react";
|
|
2
|
+
import { IZenComponentProps } from "../../commonHelpers/zenComponent";
|
|
3
|
+
import { IIcon } from "../../icons/icon";
|
|
4
|
+
import "./navItem.less";
|
|
5
|
+
export interface INavActionItem extends IZenComponentProps {
|
|
6
|
+
title: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
icon?: FC<IIcon>;
|
|
9
|
+
startNode?: ReactNode;
|
|
10
|
+
endButtonIcon?: FC<IIcon>;
|
|
11
|
+
endButtonTitle?: string;
|
|
12
|
+
onEndButtonClick?: (e: MouseEvent) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare const NavActionItem: FC<INavActionItem>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NavActionItem = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const classNames_1 = require("../../commonHelpers/classNames/classNames");
|
|
6
|
+
const NavActionItem = ({ title, id, icon, className, startNode, endButtonIcon, endButtonTitle, onEndButtonClick }) => {
|
|
7
|
+
const PrimaryIconComponent = typeof icon === "function" ? icon : null;
|
|
8
|
+
const EndButtonIconComponent = typeof endButtonIcon === "function" ? endButtonIcon : null;
|
|
9
|
+
const itemClasses = (0, classNames_1.classNames)(["zen-nav-item", className || ""]);
|
|
10
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: itemClasses, id: id, children: [(0, jsx_runtime_1.jsx)("div", { className: "zen-nav-item__main", children: (0, jsx_runtime_1.jsxs)("div", { className: "zen-nav-item__content-left", children: [startNode ? (0, jsx_runtime_1.jsx)("span", { className: "zen-nav-item__icon", children: startNode }) : null, PrimaryIconComponent ? (0, jsx_runtime_1.jsx)(PrimaryIconComponent, { size: "huge" }) : null, (0, jsx_runtime_1.jsx)("span", { className: "zen-nav-item__title", children: (0, jsx_runtime_1.jsx)("span", { className: "zen-nav-item__title-text", children: title }) })] }) }), EndButtonIconComponent && endButtonTitle && onEndButtonClick
|
|
11
|
+
? (0, jsx_runtime_1.jsx)("button", { type: "button", className: "zen-nav-item__action", "aria-label": endButtonTitle, title: endButtonTitle, onClick: onEndButtonClick, children: (0, jsx_runtime_1.jsx)(EndButtonIconComponent, { size: "huge" }) })
|
|
12
|
+
: null] }));
|
|
13
|
+
};
|
|
14
|
+
exports.NavActionItem = NavActionItem;
|
|
15
|
+
exports.NavActionItem.displayName = "NavActionItem";
|
package/esm/index.d.ts
CHANGED
|
@@ -697,6 +697,7 @@ export { type IItemData, ItemData } from "./list/itemData/itemData";
|
|
|
697
697
|
export { type IList, List } from "./list/list";
|
|
698
698
|
export { type IListItem, ListItem } from "./list/listItem/listItem";
|
|
699
699
|
export { changeOrder } from "./list/utils/changeOrder";
|
|
700
|
+
export { isDragNotClick } from "./list/utils/isDragNotClick";
|
|
700
701
|
export { type IControlledMenu, ControlledMenu } from "./menu/controlledMenu";
|
|
701
702
|
export { type IMenu, Menu } from "./menu/menu";
|
|
702
703
|
export { findFirstFocusable } from "./menu/utils/findFirstFocusable";
|
package/esm/index.js
CHANGED
|
@@ -684,6 +684,7 @@ export { ItemData } from "./list/itemData/itemData";
|
|
|
684
684
|
export { List } from "./list/list";
|
|
685
685
|
export { ListItem } from "./list/listItem/listItem";
|
|
686
686
|
export { changeOrder } from "./list/utils/changeOrder";
|
|
687
|
+
export { isDragNotClick } from "./list/utils/isDragNotClick";
|
|
687
688
|
export { ControlledMenu } from "./menu/controlledMenu";
|
|
688
689
|
export { Menu } from "./menu/menu";
|
|
689
690
|
export { findFirstFocusable } from "./menu/utils/findFirstFocusable";
|
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import { RefObject } from "react";
|
|
2
|
-
|
|
2
|
+
interface IMarkerClasses {
|
|
3
|
+
general: string;
|
|
4
|
+
placeholder: string;
|
|
5
|
+
dragging: string;
|
|
6
|
+
dragged?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A custom React hook that provides drag-and-drop functionality for reorderable list items.
|
|
10
|
+
* @param containerRef - A React ref pointing to the container element that holds the draggable items
|
|
11
|
+
* @param onChangeOrder - Callback function invoked when an item is dropped at a new position.
|
|
12
|
+
* Receives the start position and end position of the dragged item
|
|
13
|
+
* @param marksClassNames - Optional custom CSS class names for marking different states of list items.
|
|
14
|
+
* If not provided, defaults to 'zen-list-item' based classes
|
|
15
|
+
* @param onBeforeDragOver - Optional callback function called before placing the placeholder at a new position.
|
|
16
|
+
* Receives the target position and should return false to prevent the drag operation.
|
|
17
|
+
* @returns An object containing the `onDragStart` event handler to be attached to draggable elements
|
|
18
|
+
*/
|
|
19
|
+
export declare const useDragAndDrop: (containerRef: RefObject<HTMLElement | null>, onChangeOrder: (startPosition: number, endPosition: number) => void, marksClassNames?: IMarkerClasses, onBeforeDragOver?: (endPosition: number) => boolean) => {
|
|
3
20
|
onDragStart: (e: React.PointerEvent<HTMLElement>) => void;
|
|
4
21
|
};
|
|
22
|
+
export {};
|
|
@@ -1,32 +1,54 @@
|
|
|
1
1
|
import { useCallback, useRef } from "react";
|
|
2
2
|
import { getScrollableParent } from "../../utils/getScrollableParent";
|
|
3
|
-
import {
|
|
3
|
+
import { findElement } from "../utils/findElement";
|
|
4
4
|
import { useContainerScroll } from "./useContainerScroll";
|
|
5
5
|
import { findItemPosition } from "../utils/findItemPosition";
|
|
6
|
-
|
|
6
|
+
import { isDragNotClick } from "../utils/isDragNotClick";
|
|
7
|
+
/**
|
|
8
|
+
* A custom React hook that provides drag-and-drop functionality for reorderable list items.
|
|
9
|
+
* @param containerRef - A React ref pointing to the container element that holds the draggable items
|
|
10
|
+
* @param onChangeOrder - Callback function invoked when an item is dropped at a new position.
|
|
11
|
+
* Receives the start position and end position of the dragged item
|
|
12
|
+
* @param marksClassNames - Optional custom CSS class names for marking different states of list items.
|
|
13
|
+
* If not provided, defaults to 'zen-list-item' based classes
|
|
14
|
+
* @param onBeforeDragOver - Optional callback function called before placing the placeholder at a new position.
|
|
15
|
+
* Receives the target position and should return false to prevent the drag operation.
|
|
16
|
+
* @returns An object containing the `onDragStart` event handler to be attached to draggable elements
|
|
17
|
+
*/
|
|
18
|
+
export const useDragAndDrop = (containerRef, onChangeOrder, marksClassNames, onBeforeDragOver) => {
|
|
7
19
|
const dragImageRef = useRef(null);
|
|
8
20
|
const dragMoveRef = useRef(undefined);
|
|
9
21
|
const { moveScroll, cancelMoveScroll } = useContainerScroll();
|
|
22
|
+
const cssMarks = marksClassNames
|
|
23
|
+
? marksClassNames
|
|
24
|
+
: {
|
|
25
|
+
general: "zen-list-item",
|
|
26
|
+
placeholder: "zen-list-item--placeholder",
|
|
27
|
+
dragging: "zen-list-item--dragging"
|
|
28
|
+
};
|
|
10
29
|
const getPlaceholder = useCallback((height) => {
|
|
11
30
|
const placeholder = document.createElement("li");
|
|
12
|
-
placeholder.className =
|
|
31
|
+
placeholder.className = `${cssMarks.general} ${cssMarks.placeholder}`;
|
|
13
32
|
placeholder.style.height = height + "px";
|
|
14
33
|
return placeholder;
|
|
15
|
-
}, []);
|
|
34
|
+
}, [cssMarks.general, cssMarks.placeholder]);
|
|
16
35
|
const removePlaceholders = useCallback((element) => {
|
|
17
|
-
const placeholders = element.querySelectorAll(
|
|
36
|
+
const placeholders = element.querySelectorAll(`.${cssMarks.placeholder}`);
|
|
18
37
|
for (const placeholder of placeholders) {
|
|
19
38
|
placeholder.remove();
|
|
20
39
|
}
|
|
21
|
-
}, []);
|
|
40
|
+
}, [cssMarks.placeholder]);
|
|
22
41
|
const setPlaceholder = useCallback((element) => {
|
|
23
42
|
if (!containerRef.current) {
|
|
24
43
|
return;
|
|
25
44
|
}
|
|
26
|
-
const items = containerRef.current.querySelectorAll(
|
|
45
|
+
const items = containerRef.current.querySelectorAll(`.${cssMarks.general}`);
|
|
27
46
|
const rect = element.getBoundingClientRect();
|
|
28
47
|
const listRect = containerRef.current.getBoundingClientRect();
|
|
29
48
|
if (rect.top < listRect.top) {
|
|
49
|
+
if (onBeforeDragOver && !onBeforeDragOver(0)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
30
52
|
// If the dragged element is above the list, insert placeholder at the top
|
|
31
53
|
const placeholder = getPlaceholder(rect.height);
|
|
32
54
|
removePlaceholders(containerRef.current);
|
|
@@ -40,18 +62,25 @@ export const useDragAndDrop = (containerRef, onChangeOrder) => {
|
|
|
40
62
|
return;
|
|
41
63
|
}
|
|
42
64
|
if (rect.top > listRect.bottom) {
|
|
65
|
+
if (onBeforeDragOver && !onBeforeDragOver(items.length)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
43
68
|
const placeholder = getPlaceholder(rect.height);
|
|
44
69
|
removePlaceholders(containerRef.current);
|
|
45
70
|
containerRef.current.appendChild(placeholder);
|
|
46
71
|
return;
|
|
47
72
|
}
|
|
48
73
|
const dragItemTopOffset = 4;
|
|
49
|
-
for (
|
|
50
|
-
|
|
74
|
+
for (let i = 0; i < items.length; i++) {
|
|
75
|
+
const item = items[i];
|
|
76
|
+
if (item.classList.contains(cssMarks.placeholder) || item.classList.contains(cssMarks.dragging)) {
|
|
51
77
|
continue;
|
|
52
78
|
}
|
|
53
79
|
const itemRect = item.getBoundingClientRect();
|
|
54
80
|
if (item !== element && rect.top > itemRect.top + dragItemTopOffset && rect.top <= itemRect.bottom) {
|
|
81
|
+
if (onBeforeDragOver && !onBeforeDragOver(i + 1)) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
55
84
|
const placeholder = getPlaceholder(rect.height);
|
|
56
85
|
removePlaceholders(containerRef.current);
|
|
57
86
|
const next = item.nextSibling;
|
|
@@ -64,19 +93,24 @@ export const useDragAndDrop = (containerRef, onChangeOrder) => {
|
|
|
64
93
|
return;
|
|
65
94
|
}
|
|
66
95
|
}
|
|
67
|
-
}, [containerRef, getPlaceholder, removePlaceholders]);
|
|
96
|
+
}, [containerRef, getPlaceholder, removePlaceholders, onBeforeDragOver, cssMarks.general, cssMarks.placeholder, cssMarks.dragging]);
|
|
68
97
|
const onDragStart = useCallback((e) => {
|
|
69
|
-
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
const listItemElement = findElement(e.currentTarget, cssMarks.general);
|
|
70
100
|
if (!listItemElement) {
|
|
71
101
|
return;
|
|
72
102
|
}
|
|
73
|
-
const startPosition = findItemPosition(listItemElement);
|
|
103
|
+
const startPosition = findItemPosition(listItemElement, cssMarks.dragging);
|
|
74
104
|
const rect = listItemElement.getBoundingClientRect();
|
|
75
105
|
const offset = {
|
|
76
106
|
x: e.clientX - rect.left,
|
|
77
107
|
y: e.clientY - rect.top
|
|
78
108
|
};
|
|
109
|
+
const initialCoords = { x: e.clientX, y: e.clientY };
|
|
79
110
|
const div = listItemElement.cloneNode(true);
|
|
111
|
+
if (cssMarks.dragged) {
|
|
112
|
+
div.classList.add(cssMarks.dragged);
|
|
113
|
+
}
|
|
80
114
|
document.body.appendChild(div);
|
|
81
115
|
dragImageRef.current = div;
|
|
82
116
|
div.style.position = "absolute";
|
|
@@ -84,16 +118,25 @@ export const useDragAndDrop = (containerRef, onChangeOrder) => {
|
|
|
84
118
|
div.style.top = (e.clientY - offset.y) + "px";
|
|
85
119
|
div.style.width = listItemElement.offsetWidth + "px";
|
|
86
120
|
div.style.height = listItemElement.offsetHeight + "px";
|
|
121
|
+
let isDragStarted = false;
|
|
87
122
|
dragMoveRef.current = (moveEvent) => {
|
|
88
123
|
if (!dragImageRef.current) {
|
|
89
124
|
return;
|
|
90
125
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
126
|
+
if (isDragStarted || isDragNotClick(initialCoords, { x: moveEvent.clientX, y: moveEvent.clientY })) {
|
|
127
|
+
if (!isDragStarted) {
|
|
128
|
+
isDragStarted = true;
|
|
129
|
+
listItemElement.classList.add(cssMarks.dragging);
|
|
130
|
+
const scrollableParent = getScrollableParent(containerRef.current);
|
|
131
|
+
moveScroll((scrollableParent || containerRef.current), dragImageRef.current, () => setPlaceholder(dragImageRef.current));
|
|
132
|
+
}
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
134
|
+
const pageScroll = window ? window.scrollY : 0;
|
|
135
|
+
dragImageRef.current.style.left = (moveEvent.clientX - offset.x) + "px";
|
|
136
|
+
dragImageRef.current.style.top = (pageScroll + moveEvent.clientY - offset.y) + "px";
|
|
137
|
+
setPlaceholder(dragImageRef.current);
|
|
138
|
+
moveEvent.preventDefault();
|
|
139
|
+
}
|
|
97
140
|
};
|
|
98
141
|
const onDragEnd = () => {
|
|
99
142
|
var _a;
|
|
@@ -104,9 +147,9 @@ export const useDragAndDrop = (containerRef, onChangeOrder) => {
|
|
|
104
147
|
if (dragMoveRef.current) {
|
|
105
148
|
document.body.removeEventListener("pointermove", dragMoveRef.current, false);
|
|
106
149
|
}
|
|
107
|
-
const placeholderElement = (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(
|
|
108
|
-
const endPosition = placeholderElement ? findItemPosition(placeholderElement) : null;
|
|
109
|
-
listItemElement.classList.remove(
|
|
150
|
+
const placeholderElement = (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(`.${cssMarks.placeholder}`);
|
|
151
|
+
const endPosition = placeholderElement ? findItemPosition(placeholderElement, cssMarks.dragging) : null;
|
|
152
|
+
listItemElement.classList.remove(cssMarks.dragging);
|
|
110
153
|
if (containerRef.current) {
|
|
111
154
|
removePlaceholders(containerRef.current);
|
|
112
155
|
}
|
|
@@ -121,10 +164,6 @@ export const useDragAndDrop = (containerRef, onChangeOrder) => {
|
|
|
121
164
|
document.body.addEventListener("pointermove", dragMoveRef.current, false);
|
|
122
165
|
document.body.addEventListener("pointerup", onDragEnd, false);
|
|
123
166
|
document.addEventListener("mouseleave", onDragEnd);
|
|
124
|
-
|
|
125
|
-
setPlaceholder(dragImageRef.current);
|
|
126
|
-
const scrollableParent = getScrollableParent(containerRef.current);
|
|
127
|
-
moveScroll((scrollableParent || containerRef.current), dragImageRef.current, () => setPlaceholder(dragImageRef.current));
|
|
128
|
-
}, [cancelMoveScroll, containerRef, moveScroll, onChangeOrder, removePlaceholders, setPlaceholder]);
|
|
167
|
+
}, [cancelMoveScroll, containerRef, moveScroll, onChangeOrder, removePlaceholders, setPlaceholder, cssMarks.general, cssMarks.placeholder, cssMarks.dragging, cssMarks.dragged]);
|
|
129
168
|
return { onDragStart };
|
|
130
169
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const findElement: (element: HTMLElement, className: string) => HTMLElement | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const findItemPosition: (element: HTMLElement) => number;
|
|
1
|
+
export declare const findItemPosition: (element: HTMLElement, draggingClass?: string) => number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const findItemPosition = (element) => {
|
|
1
|
+
export const findItemPosition = (element, draggingClass = "zen-list-item--dragging") => {
|
|
2
2
|
const parent = element.parentElement;
|
|
3
3
|
if (!parent) {
|
|
4
4
|
return -1;
|
|
@@ -6,7 +6,7 @@ export const findItemPosition = (element) => {
|
|
|
6
6
|
let ignoreOriginalElement = false;
|
|
7
7
|
for (let i = 0; i < parent.children.length; i++) {
|
|
8
8
|
const currentChild = parent.children[i];
|
|
9
|
-
ignoreOriginalElement = ignoreOriginalElement || currentChild.classList.contains(
|
|
9
|
+
ignoreOriginalElement = ignoreOriginalElement || currentChild.classList.contains(draggingClass);
|
|
10
10
|
if (currentChild === element) {
|
|
11
11
|
return i - (ignoreOriginalElement ? 1 : 0);
|
|
12
12
|
}
|
|
@@ -7,6 +7,8 @@ export interface INavContext {
|
|
|
7
7
|
searchOpen?: boolean;
|
|
8
8
|
onSearchToggle?: (isOpen: boolean) => void;
|
|
9
9
|
onNavigate?: () => void;
|
|
10
|
+
isEditState?: boolean;
|
|
11
|
+
onEditStateToggle?: (isEdit: boolean) => void;
|
|
10
12
|
}
|
|
11
13
|
/**
|
|
12
14
|
* @beta This component is not fully ready yet and may change in future releases.
|
|
@@ -9,7 +9,9 @@ export const NavContext = createContext({
|
|
|
9
9
|
onSearch: undefined,
|
|
10
10
|
searchOpen: false,
|
|
11
11
|
onSearchToggle: undefined,
|
|
12
|
-
onNavigate: undefined
|
|
12
|
+
onNavigate: undefined,
|
|
13
|
+
isEditState: false,
|
|
14
|
+
onEditStateToggle: undefined
|
|
13
15
|
});
|
|
14
16
|
/**
|
|
15
17
|
* @beta This component is not fully ready yet and may change in future releases.
|
package/esm/nav/nav.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { classNames } from "../commonHelpers/classNames/classNames";
|
|
3
|
-
import { NavContext } from "./context/nav.context";
|
|
3
|
+
import { NavContext, useNavContext } from "./context/nav.context";
|
|
4
4
|
import { Children, cloneElement, Fragment, useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
5
5
|
import { NavDivider } from "./navDivider/navDivider";
|
|
6
6
|
import { detectOverlayScrollbar, filterPrimaryNavItems, collectNavItems } from "./utils/navUtils";
|
|
@@ -10,6 +10,7 @@ import { NavMobileBar } from "./navMobileBar/navMobileBar";
|
|
|
10
10
|
import { useMobile } from "../commonHelpers/hooks/useMobile";
|
|
11
11
|
import { FOCUSABLE_SELECTOR } from "../utils/focusableSelector";
|
|
12
12
|
const NavContent = ({ children }) => {
|
|
13
|
+
const { isEditState } = useNavContext();
|
|
13
14
|
const contentRef = useRef(null);
|
|
14
15
|
const [hasScrollbar, setHasScrollbar] = useState(false);
|
|
15
16
|
const [showTopShadow, setShowTopShadow] = useState(false);
|
|
@@ -40,8 +41,8 @@ const NavContent = ({ children }) => {
|
|
|
40
41
|
}, [handleResize]);
|
|
41
42
|
return _jsx("div", { ref: contentRef, onScroll: handleScroll, className: classNames([
|
|
42
43
|
"zen-nav__content",
|
|
43
|
-
showTopShadow ? "zen-nav__content--show-top-shadow" : "",
|
|
44
|
-
showBottomShadow ? "zen-nav__content--show-bottom-shadow" : "",
|
|
44
|
+
showTopShadow && !isEditState ? "zen-nav__content--show-top-shadow" : "",
|
|
45
|
+
showBottomShadow && !isEditState ? "zen-nav__content--show-bottom-shadow" : "",
|
|
45
46
|
hasScrollbar ? "zen-nav__content--has-scrollbar" : "",
|
|
46
47
|
hasOverlayScrollbar ? "zen-nav__content--overlay-scrollbar" : ""
|
|
47
48
|
]), children: _jsx("div", { className: "zen-nav__content-inner", children: children }) });
|
|
@@ -53,6 +54,7 @@ export const Nav = ({ children, className, collapsed = false, onCollapseToggle,
|
|
|
53
54
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
|
54
55
|
const menuRef = useRef(null);
|
|
55
56
|
const [isSearchOpen, setIsSearchOpen] = useState(false);
|
|
57
|
+
const [isEditState, setIsEditState] = useState(false);
|
|
56
58
|
const [searchTerm, setSearchTerm] = useState("");
|
|
57
59
|
const isMobile = useMobile();
|
|
58
60
|
const prevIsMobileRef = useRef(false);
|
|
@@ -68,16 +70,18 @@ export const Nav = ({ children, className, collapsed = false, onCollapseToggle,
|
|
|
68
70
|
}
|
|
69
71
|
}, [isMobile, isMobileMenuOpen]);
|
|
70
72
|
const childrenArray = Children.toArray(children);
|
|
71
|
-
const header = childrenArray.find(child => { var _a; return ((_a = child.type) === null || _a === void 0 ? void 0 : _a.displayName) === "NavHeader"; });
|
|
73
|
+
const header = isEditState ? undefined : childrenArray.find(child => { var _a; return ((_a = child.type) === null || _a === void 0 ? void 0 : _a.displayName) === "NavHeader"; });
|
|
72
74
|
const sections = childrenArray.filter(child => { var _a; return ((_a = child.type) === null || _a === void 0 ? void 0 : _a.displayName) === "NavSection"; });
|
|
73
75
|
const footer = childrenArray.find(child => { var _a; return ((_a = child.type) === null || _a === void 0 ? void 0 : _a.displayName) === "NavFooter"; });
|
|
76
|
+
const editFooter = childrenArray.find(child => { var _a; return ((_a = child.type) === null || _a === void 0 ? void 0 : _a.displayName) === "NavEditFooter"; });
|
|
74
77
|
const secondarySections = sections.map(section => filterPrimaryNavItems(section, true)).filter(section => !!section && Children.count(section.props.children) > 0);
|
|
75
78
|
const primarySections = sections.map(section => filterPrimaryNavItems(section, false)).filter(section => !!section && Children.count(section.props.children) > 0);
|
|
76
79
|
const sectionsToRender = isMobile ? secondarySections : sections;
|
|
77
80
|
const secondaryFooter = footer && filterPrimaryNavItems(footer, true);
|
|
78
81
|
const primaryFooter = footer && filterPrimaryNavItems(footer, false);
|
|
79
|
-
const footerToRender = isMobile ? secondaryFooter : footer;
|
|
82
|
+
const footerToRender = isEditState ? editFooter : (isMobile ? secondaryFooter : footer);
|
|
80
83
|
const primaryItems = [...primarySections, ...(primaryFooter ? [primaryFooter] : [])].flatMap(e => collectNavItems(e));
|
|
84
|
+
const editListItems = childrenArray.find(child => { var _a; return ((_a = child.type) === null || _a === void 0 ? void 0 : _a.name) === "NavEditSection"; });
|
|
81
85
|
const onSearchOpen = useCallback((isOpen) => {
|
|
82
86
|
isOpen && onCollapseToggle && onCollapseToggle(false);
|
|
83
87
|
setIsSearchOpen(isOpen || isMobile);
|
|
@@ -85,6 +89,10 @@ export const Nav = ({ children, className, collapsed = false, onCollapseToggle,
|
|
|
85
89
|
const onMobileMenuToggle = useCallback(() => {
|
|
86
90
|
setIsMobileMenuOpen(!isMobileMenuOpen);
|
|
87
91
|
}, [isMobileMenuOpen, setIsMobileMenuOpen]);
|
|
92
|
+
const onEditStateToggle = useCallback((isEdit) => {
|
|
93
|
+
isEdit && onCollapseToggle && onCollapseToggle(false);
|
|
94
|
+
editListItems && editFooter && setIsEditState(isEdit);
|
|
95
|
+
}, [onCollapseToggle, setIsEditState, editListItems, editFooter]);
|
|
88
96
|
useEffect(() => {
|
|
89
97
|
const wasMobile = prevIsMobileRef.current;
|
|
90
98
|
prevIsMobileRef.current = isMobile;
|
|
@@ -119,13 +127,17 @@ export const Nav = ({ children, className, collapsed = false, onCollapseToggle,
|
|
|
119
127
|
collapsed,
|
|
120
128
|
onCollapseToggle,
|
|
121
129
|
searchOpen: isSearchOpen,
|
|
130
|
+
isEditState: isEditState,
|
|
131
|
+
onEditStateToggle,
|
|
122
132
|
onSearchToggle: onSearchOpen,
|
|
123
133
|
searchTerm,
|
|
124
134
|
onSearch: setSearchTerm,
|
|
125
135
|
onNavigate: () => setIsMobileMenuOpen(false)
|
|
126
136
|
}, children: [(!isMobile || isMobileMenuOpen)
|
|
127
|
-
? _jsxs("div", { ref: menuRef, className: classNames(["zen-nav", isMobile ? "zen-nav--mobile" : "", collapsed ? "zen-nav--collapsed" : "", className || ""]), children: [header ? cloneElement(header, Object.assign(Object.assign({}, header.props), { className: classNames(["zen-nav__header", header.props.className || ""]) })) : null, _jsx(NavContent, { children:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
137
|
+
? _jsxs("div", { ref: menuRef, className: classNames(["zen-nav", isMobile ? "zen-nav--mobile" : "", collapsed ? "zen-nav--collapsed" : "", className || ""]), children: [header ? cloneElement(header, Object.assign(Object.assign({}, header.props), { className: classNames(["zen-nav__header", header.props.className || ""]) })) : null, _jsx(NavContent, { children: isEditState
|
|
138
|
+
? editListItems
|
|
139
|
+
: sectionsToRender.map((section, index) => (_jsxs(Fragment, { children: [cloneElement(section, {
|
|
140
|
+
className: classNames(["zen-nav__section", section.props.className || ""])
|
|
141
|
+
}), isMobile || index < sectionsToRender.length - 1 ? _jsx(NavDivider, {}) : null] }, index))) }), footerToRender] })
|
|
142
|
+
: null, mobileBarContainer && isMobile && createPortal(_jsx(NavMobileBar, { onMenuToggle: onMobileMenuToggle, isMenuOpen: isMobileMenuOpen, children: editListItems ? editListItems : primaryItems }), mobileBarContainer)] });
|
|
131
143
|
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import "./navEditList.less";
|
|
2
|
+
import { IZenComponentProps } from "../../commonHelpers/zenComponent";
|
|
3
|
+
export interface INavEditList extends IZenComponentProps {
|
|
4
|
+
onItemsChangeOrder?: (startPosition: number, endPosition: number) => void;
|
|
5
|
+
onBeforeItemDrag?: (position: number) => boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare const NavEditList: ({ children, className, onItemsChangeOrder, onBeforeItemDrag }: INavEditList) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Children, useMemo, useRef } from "react";
|
|
3
|
+
import { useDragAndDrop } from "../../list/hooks/useDragAndDrop";
|
|
4
|
+
import { classNames } from "../../commonHelpers/classNames/classNames";
|
|
5
|
+
import { attachDndHandler } from "./navEditListUtils";
|
|
6
|
+
const marksClassNames = {
|
|
7
|
+
general: "zen-nav-edit-list__item",
|
|
8
|
+
placeholder: "zen-nav-edit-list__item--placeholder",
|
|
9
|
+
dragging: "zen-nav-edit-list__item--dragging",
|
|
10
|
+
dragged: "zen-nav-edit-list__item--dragged"
|
|
11
|
+
};
|
|
12
|
+
export const NavEditList = ({ children, className, onItemsChangeOrder, onBeforeItemDrag }) => {
|
|
13
|
+
const appsListRef = useRef(null);
|
|
14
|
+
const dragHandler = onItemsChangeOrder ? onItemsChangeOrder : () => { };
|
|
15
|
+
const { onDragStart } = useDragAndDrop(appsListRef, dragHandler, marksClassNames, onBeforeItemDrag);
|
|
16
|
+
const childrenArray = useMemo(() => onItemsChangeOrder ? Children.map(children, child => attachDndHandler(child, onDragStart)) : children, [children, onItemsChangeOrder, onDragStart]);
|
|
17
|
+
return _jsx("div", { className: classNames(["zen-nav-edit-list", className || ""]), ref: appsListRef, children: childrenArray });
|
|
18
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { PointerEvent, ReactElement } from "react";
|
|
2
|
+
import { INavActionItem } from "../navItem/navActionItem";
|
|
3
|
+
export declare const attachDndHandler: (item: ReactElement<INavActionItem>, onPointerDown: (e: PointerEvent<HTMLElement>) => void) => ReactElement<INavActionItem, string | import("react").JSXElementConstructor<any>>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cloneElement } from "react";
|
|
3
|
+
import { NavActionItem } from "../navItem/navActionItem";
|
|
4
|
+
import { IconGrab } from "../../icons/iconGrab";
|
|
5
|
+
export const attachDndHandler = (item, onPointerDown) => {
|
|
6
|
+
if (item.type === NavActionItem) {
|
|
7
|
+
return cloneElement(item, Object.assign(Object.assign({}, item.props), { startNode: _jsx("div", { className: "zen-nav-item__dnd", onPointerDown: onPointerDown, children: _jsx(IconGrab, { size: "large" }) }) }));
|
|
8
|
+
}
|
|
9
|
+
return item;
|
|
10
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { classNames } from "../../commonHelpers/classNames/classNames";
|
|
3
|
+
export const NavEditSection = ({ children, className }) => _jsx("div", { className: classNames(["zen-nav-edit-section", className || ""]), children: children });
|