@geotab/zenith 3.6.3 → 3.7.0-beta.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/README.md +14 -0
- package/dist/absolute/absolute.d.ts +3 -1
- package/dist/absolute/absolute.js +4 -4
- package/dist/controlledPopup/controlledPopup.d.ts +3 -1
- package/dist/controlledPopup/controlledPopup.js +2 -2
- package/dist/groupsFilterRaw/groupsFilterCurrentlySelectedState.js +8 -2
- package/dist/groupsFilterRaw/groupsFilterRaw.js +43 -9
- package/dist/groupsFilterRaw/groupsFilterTrigger.d.ts +2 -1
- package/dist/groupsFilterRaw/groupsFilterTrigger.js +9 -2
- package/dist/index.css +208 -18
- package/dist/index.d.ts +1 -1
- package/dist/list/hooks/useDragAndDrop.d.ts +3 -1
- package/dist/list/hooks/useDragAndDrop.js +11 -4
- package/dist/menu/components/menuItem.js +12 -2
- package/dist/menu/contexts/pathContext.d.ts +3 -1
- package/dist/menu/contexts/pathProvider.d.ts +1 -1
- package/dist/menu/contexts/pathProvider.js +1 -1
- package/dist/menu/controlledMenu.js +97 -29
- package/dist/nav/nav.d.ts +2 -1
- package/dist/nav/nav.js +3 -2
- package/dist/nav/navEditList/navEditList.js +2 -1
- package/dist/pillExpandable/pillContent.d.ts +2 -1
- package/dist/pillExpandable/pillContent.js +8 -3
- package/dist/pillExpandable/pillExpandable.d.ts +27 -4
- package/dist/pillExpandable/pillExpandable.js +426 -196
- package/dist/pillExpandable/pillExpandablePopoverContent.d.ts +19 -0
- package/dist/pillExpandable/pillExpandablePopoverContent.js +7 -0
- package/dist/pillExpandable/pillExpandableSimple.d.ts +5 -3
- package/dist/pillExpandable/pillExpandableSimple.js +12 -5
- package/dist/summary/summary.js +29 -1
- package/dist/summaryTile/summaryTile.d.ts +2 -1
- package/dist/summaryTile/summaryTile.js +175 -65
- package/dist/summaryTile/summaryTileTrigger.d.ts +1 -1
- package/dist/summaryTile/summaryTileTrigger.js +1 -1
- package/dist/toggleButtonRaw/toggleButtonRaw.js +2 -1
- package/dist/utils/localization/getSupportedLanguage.d.ts +2 -2
- package/dist/utils/localization/getSupportedLanguage.js +28 -8
- package/dist/utils/localization/languageContext.d.ts +1 -1
- package/esm/absolute/absolute.d.ts +3 -1
- package/esm/absolute/absolute.js +4 -4
- package/esm/controlledPopup/controlledPopup.d.ts +3 -1
- package/esm/controlledPopup/controlledPopup.js +2 -2
- package/esm/groupsFilterRaw/groupsFilterCurrentlySelectedState.js +9 -3
- package/esm/groupsFilterRaw/groupsFilterRaw.js +43 -9
- package/esm/groupsFilterRaw/groupsFilterTrigger.d.ts +2 -1
- package/esm/groupsFilterRaw/groupsFilterTrigger.js +9 -2
- package/esm/index.d.ts +1 -1
- package/esm/list/hooks/useDragAndDrop.d.ts +3 -1
- package/esm/list/hooks/useDragAndDrop.js +11 -4
- package/esm/menu/components/menuItem.js +12 -2
- package/esm/menu/contexts/pathContext.d.ts +3 -1
- package/esm/menu/contexts/pathProvider.d.ts +1 -1
- package/esm/menu/contexts/pathProvider.js +1 -1
- package/esm/menu/controlledMenu.js +98 -30
- package/esm/nav/nav.d.ts +2 -1
- package/esm/nav/nav.js +3 -2
- package/esm/nav/navEditList/navEditList.js +2 -1
- package/esm/pillExpandable/pillContent.d.ts +2 -1
- package/esm/pillExpandable/pillContent.js +8 -3
- package/esm/pillExpandable/pillExpandable.d.ts +27 -4
- package/esm/pillExpandable/pillExpandable.js +427 -197
- package/esm/pillExpandable/pillExpandablePopoverContent.d.ts +19 -0
- package/esm/pillExpandable/pillExpandablePopoverContent.js +3 -0
- package/esm/pillExpandable/pillExpandableSimple.d.ts +5 -3
- package/esm/pillExpandable/pillExpandableSimple.js +12 -5
- package/esm/summary/summary.js +29 -1
- package/esm/summaryTile/summaryTile.d.ts +2 -1
- package/esm/summaryTile/summaryTile.js +140 -43
- package/esm/summaryTile/summaryTileTrigger.d.ts +1 -1
- package/esm/summaryTile/summaryTileTrigger.js +1 -1
- package/esm/toggleButtonRaw/toggleButtonRaw.js +2 -1
- package/esm/utils/localization/getSupportedLanguage.d.ts +2 -2
- package/esm/utils/localization/getSupportedLanguage.js +28 -8
- package/esm/utils/localization/languageContext.d.ts +1 -1
- package/package.json +4 -4
|
@@ -17,9 +17,11 @@ const zen_1 = require("../../utils/zen");
|
|
|
17
17
|
* If not provided, defaults to 'zen-list-item' based classes
|
|
18
18
|
* @param onBeforeDragOver - Optional callback function called before placing the placeholder at a new position.
|
|
19
19
|
* Receives the target position and should return false to prevent the drag operation.
|
|
20
|
+
* @param itemsGap - Optional gap size in pixels between list items. Used to adjust placeholder positioning
|
|
21
|
+
* calculations during drag operations. Defaults to 0.
|
|
20
22
|
* @returns An object containing the `onDragStart` event handler to be attached to draggable elements
|
|
21
23
|
*/
|
|
22
|
-
const useDragAndDrop = (containerRef, onChangeOrder, marksClassNames, onBeforeDragOver) => {
|
|
24
|
+
const useDragAndDrop = (containerRef, onChangeOrder, marksClassNames, onBeforeDragOver, itemsGap = 0) => {
|
|
23
25
|
const dragImageRef = (0, react_1.useRef)(null);
|
|
24
26
|
const dragMoveRef = (0, react_1.useRef)(undefined);
|
|
25
27
|
const { moveScroll, cancelMoveScroll } = (0, useContainerScroll_1.useContainerScroll)();
|
|
@@ -50,8 +52,12 @@ const useDragAndDrop = (containerRef, onChangeOrder, marksClassNames, onBeforeDr
|
|
|
50
52
|
if (!containerRef.current) {
|
|
51
53
|
return;
|
|
52
54
|
}
|
|
53
|
-
const items = containerRef.current.querySelectorAll(`.${cssMarks.general}`);
|
|
54
55
|
const rect = element.getBoundingClientRect();
|
|
56
|
+
// preventing placeholder alignment when dragged element is not properly positioned yet
|
|
57
|
+
if (rect.top < 0) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const items = containerRef.current.querySelectorAll(`.${cssMarks.general}`);
|
|
55
61
|
const listRect = containerRef.current.getBoundingClientRect();
|
|
56
62
|
if (rect.top < listRect.top) {
|
|
57
63
|
if (onBeforeDragOver && !onBeforeDragOver(0)) {
|
|
@@ -79,13 +85,14 @@ const useDragAndDrop = (containerRef, onChangeOrder, marksClassNames, onBeforeDr
|
|
|
79
85
|
return;
|
|
80
86
|
}
|
|
81
87
|
const dragItemTopOffset = 4;
|
|
88
|
+
const halfGap = itemsGap / 2;
|
|
82
89
|
for (let i = 0; i < items.length; i++) {
|
|
83
90
|
const item = items[i];
|
|
84
91
|
if (item.classList.contains(cssMarks.placeholder) || item.classList.contains(cssMarks.dragging)) {
|
|
85
92
|
continue;
|
|
86
93
|
}
|
|
87
94
|
const itemRect = item.getBoundingClientRect();
|
|
88
|
-
if (item !== element && rect.top > itemRect.top + dragItemTopOffset && rect.top <= itemRect.bottom) {
|
|
95
|
+
if (item !== element && rect.top > itemRect.top + dragItemTopOffset - halfGap && rect.top <= itemRect.bottom + halfGap) {
|
|
89
96
|
if (onBeforeDragOver && !onBeforeDragOver(i + 1)) {
|
|
90
97
|
return;
|
|
91
98
|
}
|
|
@@ -101,7 +108,7 @@ const useDragAndDrop = (containerRef, onChangeOrder, marksClassNames, onBeforeDr
|
|
|
101
108
|
return;
|
|
102
109
|
}
|
|
103
110
|
}
|
|
104
|
-
}, [containerRef, getPlaceholder, removePlaceholders, onBeforeDragOver, cssMarks.general, cssMarks.placeholder, cssMarks.dragging]);
|
|
111
|
+
}, [containerRef, getPlaceholder, removePlaceholders, onBeforeDragOver, cssMarks.general, cssMarks.placeholder, cssMarks.dragging, itemsGap]);
|
|
105
112
|
const onDragStart = (0, react_1.useCallback)((e) => {
|
|
106
113
|
var _a, _b, _c, _d, _e;
|
|
107
114
|
e.preventDefault();
|
|
@@ -28,7 +28,7 @@ exports.isMenuItem = isMenuItem;
|
|
|
28
28
|
const MenuItem = ({ id, children, name, icon, disabled, onClick, link, target, rel, isMobile = false, setIsOpen, trigger, className, active, alignment }) => {
|
|
29
29
|
const aligmentContext = (0, react_1.useContext)(headerContext_1.MenuAlignmentContext);
|
|
30
30
|
const contentAlignment = alignment || aligmentContext.alignment || "right-top";
|
|
31
|
-
const { path, onOpenBranch, closeBranch } = (0, react_1.useContext)(pathContext_1.PathContext);
|
|
31
|
+
const { path, onOpenBranch, closeBranch, navigatedViaKeyboardRef } = (0, react_1.useContext)(pathContext_1.PathContext);
|
|
32
32
|
const memoizedDesktopActionOnClick = (0, react_1.useCallback)((itemId, e) => {
|
|
33
33
|
setIsOpen === null || setIsOpen === void 0 ? void 0 : setIsOpen(false);
|
|
34
34
|
onClick === null || onClick === void 0 ? void 0 : onClick(itemId, e);
|
|
@@ -87,6 +87,16 @@ const MenuItem = ({ id, children, name, icon, disabled, onClick, link, target, r
|
|
|
87
87
|
return cont;
|
|
88
88
|
}, [children, isMobile, setIsOpen, className]);
|
|
89
89
|
const isOpen = (0, react_1.useMemo)(() => path.includes(id), [path, id]);
|
|
90
|
+
// Track previous isOpen state to detect when submenu opens
|
|
91
|
+
const wasOpenRef = (0, react_1.useRef)(false);
|
|
92
|
+
const localOpenedViaKeyboardRef = (0, react_1.useRef)(false);
|
|
93
|
+
// Capture keyboard navigation state synchronously when isOpen transitions to true
|
|
94
|
+
if (isOpen && !wasOpenRef.current && navigatedViaKeyboardRef) {
|
|
95
|
+
localOpenedViaKeyboardRef.current = navigatedViaKeyboardRef.current;
|
|
96
|
+
navigatedViaKeyboardRef.current = false; // Reset for next navigation
|
|
97
|
+
}
|
|
98
|
+
wasOpenRef.current = isOpen;
|
|
99
|
+
const openedViaKeyboard = localOpenedViaKeyboardRef.current;
|
|
90
100
|
if (content.length === 0) {
|
|
91
101
|
return (0, jsx_runtime_1.jsx)(menuButton_1.MenuButton, { id: id, name: name, icon: icon, disabled: disabled, link: link, target: target, rel: rel, onClick: memoizedDesktopActionOnClick, className: className, active: active, hasChildren: false }, id);
|
|
92
102
|
}
|
|
@@ -103,7 +113,7 @@ const MenuItem = ({ id, children, name, icon, disabled, onClick, link, target, r
|
|
|
103
113
|
else {
|
|
104
114
|
popupTrigger = (0, jsx_runtime_1.jsx)(menuButton_1.MenuButton, { id: id, ref: ref, name: name, icon: icon, disabled: disabled, hasChildren: true, onClick: memoizedTriggerOnClick, active: active }, id);
|
|
105
115
|
}
|
|
106
|
-
return (0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [popupTrigger, (0, jsx_runtime_1.jsx)(controlledPopup_1.ControlledPopup, { className: (0, classNames_1.classNames)([`zen-controlled-menu-submenu--${path.length}`]), useTrapFocusWithTrigger: "on", alignment: contentAlignment, triggerRef: ref, isOpen: isOpen, onOpenChange: memoizedOnOpenChange, ariaLabel: popupTrigger.props.name, recalculateOnScroll: true, children: (0, jsx_runtime_1.jsx)("ul", { role: "menu", className: "zen-menu-item", children: content }) })] }, id);
|
|
116
|
+
return (0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [popupTrigger, (0, jsx_runtime_1.jsx)(controlledPopup_1.ControlledPopup, { className: (0, classNames_1.classNames)([`zen-controlled-menu-submenu--${path.length}`]), useTrapFocusWithTrigger: openedViaKeyboard ? "on" : "withTrigger", alignment: contentAlignment, triggerRef: ref, isOpen: isOpen, onOpenChange: memoizedOnOpenChange, ariaLabel: popupTrigger.props.name, recalculateOnScroll: true, children: (0, jsx_runtime_1.jsx)("ul", { role: "menu", className: "zen-menu-item", children: content }) })] }, id);
|
|
107
117
|
};
|
|
108
118
|
exports.MenuItem = MenuItem;
|
|
109
119
|
exports.MenuItem.displayName = "MenuItem";
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { RefObject } from "react";
|
|
2
2
|
export interface IPathContext {
|
|
3
3
|
path: string[];
|
|
4
4
|
onOpenBranch: (id: string) => void;
|
|
5
5
|
closeBranch: () => void;
|
|
6
|
+
navigatedViaKeyboardRef?: RefObject<boolean>;
|
|
7
|
+
keyboardActiveRef?: RefObject<boolean>;
|
|
6
8
|
}
|
|
7
9
|
export declare const PathContext: import("react").Context<IPathContext>;
|
|
@@ -2,5 +2,5 @@ import { PropsWithChildren } from "react";
|
|
|
2
2
|
import { IPathContext } from "./pathContext";
|
|
3
3
|
interface IPathProvider extends IPathContext, PropsWithChildren {
|
|
4
4
|
}
|
|
5
|
-
export declare const PathProvider: ({ children, path, onOpenBranch, closeBranch }: IPathProvider) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare const PathProvider: ({ children, path, onOpenBranch, closeBranch, navigatedViaKeyboardRef, keyboardActiveRef }: IPathProvider) => import("react/jsx-runtime").JSX.Element;
|
|
6
6
|
export {};
|
|
@@ -3,5 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PathProvider = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const pathContext_1 = require("./pathContext");
|
|
6
|
-
const PathProvider = ({ children, path, onOpenBranch, closeBranch }) => ((0, jsx_runtime_1.jsx)(pathContext_1.PathContext.Provider, { value: { path, onOpenBranch, closeBranch }, children: children }));
|
|
6
|
+
const PathProvider = ({ children, path, onOpenBranch, closeBranch, navigatedViaKeyboardRef, keyboardActiveRef }) => ((0, jsx_runtime_1.jsx)(pathContext_1.PathContext.Provider, { value: { path, onOpenBranch, closeBranch, navigatedViaKeyboardRef, keyboardActiveRef }, children: children }));
|
|
7
7
|
exports.PathProvider = PathProvider;
|
|
@@ -22,17 +22,57 @@ const useDeviceType_1 = require("../commonHelpers/hooks/useDeviceType");
|
|
|
22
22
|
const utils_1 = require("../commonHelpers/utils");
|
|
23
23
|
const pathProvider_1 = require("./contexts/pathProvider");
|
|
24
24
|
const menuSeparator_1 = require("./components/menuSeparator");
|
|
25
|
+
const focusableSelector_1 = require("../utils/focusableSelector");
|
|
25
26
|
const ControlledMenu = ({ children, isOpen, setIsOpen, triggerRef, ariaLabel, ariaLabelledby, id, title, className = "", listClassName = "", paddingX = 0, paddingY = 0, alignment }) => {
|
|
26
27
|
const [deviceType, setDeviceType] = (0, react_1.useState)(deviceType_1.DeviceType.Desktop);
|
|
27
28
|
const isMobile = deviceType === deviceType_1.DeviceType.Mobile;
|
|
28
29
|
const memoizedOnChange = (0, react_1.useCallback)(setIsOpen, [setIsOpen]);
|
|
29
30
|
(0, useDeviceType_1.useDeviceType)(setDeviceType);
|
|
31
|
+
const menuListRef = (0, react_1.useRef)(null);
|
|
30
32
|
const [path, setPath] = (0, react_1.useState)([]);
|
|
33
|
+
// Track if the trigger was activated via keyboard
|
|
34
|
+
const openedViaKeyboardRef = (0, react_1.useRef)(false);
|
|
35
|
+
// Track if submenu was navigated via keyboard (ArrowRight)
|
|
36
|
+
const navigatedViaKeyboardRef = (0, react_1.useRef)(false);
|
|
37
|
+
// Track if keyboard is actively being used for navigation (vs mouse/touch)
|
|
38
|
+
const keyboardActiveRef = (0, react_1.useRef)(false);
|
|
39
|
+
// Listen for keyboard activation on trigger
|
|
40
|
+
(0, react_1.useEffect)(() => {
|
|
41
|
+
const trigger = triggerRef.current;
|
|
42
|
+
if (!trigger)
|
|
43
|
+
return undefined;
|
|
44
|
+
const handleKeyDown = (e) => {
|
|
45
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
46
|
+
openedViaKeyboardRef.current = true;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const handleMouseDown = () => {
|
|
50
|
+
openedViaKeyboardRef.current = false;
|
|
51
|
+
};
|
|
52
|
+
trigger.addEventListener("keydown", handleKeyDown);
|
|
53
|
+
trigger.addEventListener("mousedown", handleMouseDown);
|
|
54
|
+
return () => {
|
|
55
|
+
trigger.removeEventListener("keydown", handleKeyDown);
|
|
56
|
+
trigger.removeEventListener("mousedown", handleMouseDown);
|
|
57
|
+
};
|
|
58
|
+
}, [triggerRef]);
|
|
31
59
|
(0, react_1.useEffect)(() => {
|
|
32
60
|
if (path.length && !isOpen) {
|
|
33
61
|
setPath([]);
|
|
34
62
|
}
|
|
35
63
|
}, [isOpen, path, setPath]);
|
|
64
|
+
// Focus the menu list container or first item when menu opens (for keyboard navigation)
|
|
65
|
+
(0, react_1.useEffect)(() => {
|
|
66
|
+
var _a;
|
|
67
|
+
if (isOpen && !isMobile && menuListRef.current) {
|
|
68
|
+
if (openedViaKeyboardRef.current) {
|
|
69
|
+
(_a = menuListRef.current.querySelector(focusableSelector_1.FOCUSABLE_SELECTOR)) === null || _a === void 0 ? void 0 : _a.focus();
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
menuListRef.current.focus();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}, [isOpen, isMobile]);
|
|
36
76
|
const onOpenBranch = (0, react_1.useCallback)((branchId) => {
|
|
37
77
|
if (!branchId) {
|
|
38
78
|
return;
|
|
@@ -110,48 +150,76 @@ const ControlledMenu = ({ children, isOpen, setIsOpen, triggerRef, ariaLabel, ar
|
|
|
110
150
|
});
|
|
111
151
|
return [cont, par];
|
|
112
152
|
}, [children, isMobile, path, setIsOpen]);
|
|
153
|
+
// Handle keyboard navigation when no menu item is focused (e.g., when menu just opened)
|
|
154
|
+
const handleUnfocusedKeyDown = (e, menuList) => {
|
|
155
|
+
var _a, _b;
|
|
156
|
+
if (e.key === "ArrowDown" || e.key === "Home") {
|
|
157
|
+
e.preventDefault();
|
|
158
|
+
(_a = (0, findFirstFocusable_1.findFirstFocusable)(menuList)) === null || _a === void 0 ? void 0 : _a.focus();
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
if (e.key === "ArrowUp" || e.key === "End") {
|
|
162
|
+
e.preventDefault();
|
|
163
|
+
(_b = (0, findLastFocusable_1.findLastFocusable)(menuList)) === null || _b === void 0 ? void 0 : _b.focus();
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
};
|
|
168
|
+
const onMouseDown = () => {
|
|
169
|
+
keyboardActiveRef.current = false;
|
|
170
|
+
};
|
|
113
171
|
const onKeyDown = (e) => {
|
|
114
172
|
var _a, _b, _c, _d;
|
|
173
|
+
keyboardActiveRef.current = true;
|
|
115
174
|
const target = e.target;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
e.preventDefault();
|
|
124
|
-
(_b = (0, findPrevFocusable_1.findPrevFocusable)(target)) === null || _b === void 0 ? void 0 : _b.focus();
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if (e.key === "Home") {
|
|
128
|
-
e.preventDefault();
|
|
129
|
-
(_c = (0, findFirstFocusable_1.findFirstFocusable)(target)) === null || _c === void 0 ? void 0 : _c.focus();
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
if (e.key === "End") {
|
|
133
|
-
e.preventDefault();
|
|
134
|
-
(_d = (0, findLastFocusable_1.findLastFocusable)(target)) === null || _d === void 0 ? void 0 : _d.focus();
|
|
135
|
-
return;
|
|
175
|
+
const currentTarget = e.currentTarget;
|
|
176
|
+
if (!(0, isButton_1.isButton)(target) && !(0, isLink_1.isLink)(target)) {
|
|
177
|
+
if (target === currentTarget) {
|
|
178
|
+
const menuList = currentTarget.querySelector("ul");
|
|
179
|
+
if (menuList) {
|
|
180
|
+
handleUnfocusedKeyDown(e, menuList);
|
|
181
|
+
}
|
|
136
182
|
}
|
|
183
|
+
return;
|
|
137
184
|
}
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
185
|
+
if (e.key === "ArrowDown") {
|
|
186
|
+
e.preventDefault();
|
|
187
|
+
(_a = (0, findNextFocusable_1.findNextFocusable)(target)) === null || _a === void 0 ? void 0 : _a.focus();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (e.key === "ArrowUp") {
|
|
191
|
+
e.preventDefault();
|
|
192
|
+
(_b = (0, findPrevFocusable_1.findPrevFocusable)(target)) === null || _b === void 0 ? void 0 : _b.focus();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (e.key === "Home") {
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
(_c = (0, findFirstFocusable_1.findFirstFocusable)(target)) === null || _c === void 0 ? void 0 : _c.focus();
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (e.key === "End") {
|
|
201
|
+
e.preventDefault();
|
|
202
|
+
(_d = (0, findLastFocusable_1.findLastFocusable)(target)) === null || _d === void 0 ? void 0 : _d.focus();
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if ((0, isButton_1.isButton)(target) && (e.key === "ArrowRight" || e.key === "Enter" || e.key === " ") && target.classList.contains("zen-menu-button__action--has-children")) {
|
|
206
|
+
e.preventDefault();
|
|
207
|
+
navigatedViaKeyboardRef.current = true;
|
|
208
|
+
target.click();
|
|
144
209
|
}
|
|
145
210
|
};
|
|
146
|
-
const renderMenuList = () => (0, jsx_runtime_1.jsx)("div", { onKeyDown: onKeyDown, className: (0, classNames_1.classNames)(["zen-action-list", className]), children: (0, jsx_runtime_1.jsxs)("ul", { role: "menu", className: (0, classNames_1.classNames)(["zen-menu-item", className, listClassName]), children: [parent ? (0, jsx_runtime_1.jsx)(menuButton_1.MenuButton, { id: "root", name: parent.props.name || "", icon: iconArrowLeft_1.IconArrowLeft, onClick: closeBranch, hasChildren: false, disabled: false }, "root") : null, content] }) });
|
|
211
|
+
const renderMenuList = () => (0, jsx_runtime_1.jsx)("div", { ref: menuListRef, tabIndex: -1, onKeyDown: onKeyDown, onMouseDown: onMouseDown, className: (0, classNames_1.classNames)(["zen-action-list", className]), children: (0, jsx_runtime_1.jsxs)("ul", { role: "menu", className: (0, classNames_1.classNames)(["zen-menu-item", className, listClassName]), children: [parent ? (0, jsx_runtime_1.jsx)(menuButton_1.MenuButton, { id: "root", name: parent.props.name || "", icon: iconArrowLeft_1.IconArrowLeft, onClick: closeBranch, hasChildren: false, disabled: false }, "root") : null, content] }) });
|
|
147
212
|
const hideMenu = (0, react_1.useCallback)(() => {
|
|
148
213
|
closeBranch();
|
|
149
214
|
setIsOpen(false);
|
|
150
215
|
}, [closeBranch, setIsOpen]);
|
|
151
216
|
if (isMobile) {
|
|
152
|
-
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(pathProvider_1.PathProvider, { path: path, onOpenBranch: onOpenBranch, closeBranch: closeBranch, children: (0, jsx_runtime_1.jsxs)(mobileSheet_1.MobileSheet, { label: title, isOpen: isOpen, triggerRef: triggerRef, onHidePanel: hideMenu, onCloseClick: hideMenu, children: [(0, jsx_runtime_1.jsx)(mobileSheet_1.MobileSheet.Title, { children: title }), (0, jsx_runtime_1.jsx)(mobileSheet_1.MobileSheet.Content, { children: renderMenuList() })] }) }) });
|
|
217
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(pathProvider_1.PathProvider, { path: path, onOpenBranch: onOpenBranch, closeBranch: closeBranch, navigatedViaKeyboardRef: navigatedViaKeyboardRef, keyboardActiveRef: keyboardActiveRef, children: (0, jsx_runtime_1.jsxs)(mobileSheet_1.MobileSheet, { label: title, isOpen: isOpen, triggerRef: triggerRef, onHidePanel: hideMenu, onCloseClick: hideMenu, children: [(0, jsx_runtime_1.jsx)(mobileSheet_1.MobileSheet.Title, { children: title }), (0, jsx_runtime_1.jsx)(mobileSheet_1.MobileSheet.Content, { children: renderMenuList() })] }) }) });
|
|
153
218
|
}
|
|
154
|
-
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(pathProvider_1.PathProvider, { path: path, onOpenBranch: onOpenBranch, closeBranch: closeBranch, children: (0, jsx_runtime_1.jsx)(controlledPopup_1.ControlledPopup, { id: id, useTrapFocusWithTrigger: "on", className: (0, classNames_1.classNames)(["zen-controlled-menu", className]), onOpenChange: memoizedOnChange, isOpen: isOpen, triggerRef: triggerRef, paddingX: paddingX, paddingY: paddingY, alignment: alignment, ariaLabelledby: ariaLabelledby, ariaLabel: ariaLabel || title, recalculateOnScroll: true,
|
|
219
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(pathProvider_1.PathProvider, { path: path, onOpenBranch: onOpenBranch, closeBranch: closeBranch, navigatedViaKeyboardRef: navigatedViaKeyboardRef, keyboardActiveRef: keyboardActiveRef, children: (0, jsx_runtime_1.jsx)(controlledPopup_1.ControlledPopup, { id: id, useTrapFocusWithTrigger: "on", className: (0, classNames_1.classNames)(["zen-controlled-menu", className]), onOpenChange: memoizedOnChange, isOpen: isOpen, triggerRef: triggerRef, paddingX: paddingX, paddingY: paddingY, alignment: alignment, ariaLabelledby: ariaLabelledby, ariaLabel: ariaLabel || title, recalculateOnScroll: true,
|
|
220
|
+
// focusOnOpen is false - ControlledMenu handles focus based on input method
|
|
221
|
+
// (keyboard vs mouse) in its own useEffect
|
|
222
|
+
focusOnOpen: false, children: renderMenuList() }) }) });
|
|
155
223
|
};
|
|
156
224
|
exports.ControlledMenu = ControlledMenu;
|
|
157
225
|
exports.ControlledMenu.Item = menuItem_1.MenuItem;
|
package/dist/nav/nav.d.ts
CHANGED
|
@@ -7,8 +7,9 @@ export interface INav extends IZenComponentProps {
|
|
|
7
7
|
onMenuVisibilityToggle?: (isOpen: boolean) => void;
|
|
8
8
|
isMobileMenuOpen?: boolean;
|
|
9
9
|
onIsMobileMenuOpenToggle?: (isOpen: boolean) => void;
|
|
10
|
+
onEditModeToggle?: (isEdit: boolean) => void;
|
|
10
11
|
}
|
|
11
12
|
/**
|
|
12
13
|
* @beta This component is not fully ready yet and may change in future releases.
|
|
13
14
|
*/
|
|
14
|
-
export declare const Nav: ({ children, className, collapsed, onCollapseToggle, onMenuVisibilityToggle, mobileBarContainer, isMobileMenuOpen, onIsMobileMenuOpenToggle }: INav) => import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare const Nav: ({ children, className, collapsed, onCollapseToggle, onMenuVisibilityToggle, mobileBarContainer, isMobileMenuOpen, onIsMobileMenuOpenToggle, onEditModeToggle }: INav) => import("react/jsx-runtime").JSX.Element;
|
package/dist/nav/nav.js
CHANGED
|
@@ -54,7 +54,7 @@ const NavContent = ({ children }) => {
|
|
|
54
54
|
/**
|
|
55
55
|
* @beta This component is not fully ready yet and may change in future releases.
|
|
56
56
|
*/
|
|
57
|
-
const Nav = ({ children, className, collapsed = false, onCollapseToggle, onMenuVisibilityToggle, mobileBarContainer, isMobileMenuOpen = false, onIsMobileMenuOpenToggle
|
|
57
|
+
const Nav = ({ children, className, collapsed = false, onCollapseToggle, onMenuVisibilityToggle, mobileBarContainer, isMobileMenuOpen = false, onIsMobileMenuOpenToggle, onEditModeToggle
|
|
58
58
|
// eslint-disable-next-line complexity
|
|
59
59
|
}) => {
|
|
60
60
|
const menuRef = (0, react_1.useRef)(null);
|
|
@@ -97,7 +97,8 @@ const Nav = ({ children, className, collapsed = false, onCollapseToggle, onMenuV
|
|
|
97
97
|
const onEditStateToggle = (0, react_1.useCallback)((isEdit) => {
|
|
98
98
|
isEdit && !isMobile && onCollapseToggle && onCollapseToggle(false);
|
|
99
99
|
editListItems && editFooter && setIsEditState(isEdit);
|
|
100
|
-
|
|
100
|
+
onEditModeToggle && onEditModeToggle(isEdit);
|
|
101
|
+
}, [onCollapseToggle, setIsEditState, editListItems, editFooter, isMobile, onEditModeToggle]);
|
|
101
102
|
(0, react_1.useEffect)(() => {
|
|
102
103
|
const wasMobile = prevIsMobileRef.current;
|
|
103
104
|
prevIsMobileRef.current = isMobile;
|
|
@@ -12,10 +12,11 @@ const marksClassNames = {
|
|
|
12
12
|
dragging: "zen-nav-edit-list__item--dragging",
|
|
13
13
|
dragged: "zen-nav-edit-list__item--dragged"
|
|
14
14
|
};
|
|
15
|
+
const EDIT_ITEM_GAP = 10;
|
|
15
16
|
const NavEditList = ({ children, className, onItemsChangeOrder, onBeforeItemDrag }) => {
|
|
16
17
|
const appsListRef = (0, react_1.useRef)(null);
|
|
17
18
|
const dragHandler = onItemsChangeOrder ? onItemsChangeOrder : () => { };
|
|
18
|
-
const { onDragStart } = (0, useDragAndDrop_1.useDragAndDrop)(appsListRef, dragHandler, marksClassNames, onBeforeItemDrag);
|
|
19
|
+
const { onDragStart } = (0, useDragAndDrop_1.useDragAndDrop)(appsListRef, dragHandler, marksClassNames, onBeforeItemDrag, EDIT_ITEM_GAP);
|
|
19
20
|
const childrenArray = (0, react_1.useMemo)(() => onItemsChangeOrder ? react_1.Children.map(children, child => (0, navEditListUtils_1.attachDndHandler)(child, onDragStart)) : children, [children, onItemsChangeOrder, onDragStart]);
|
|
20
21
|
return (0, jsx_runtime_1.jsx)("div", { className: (0, classNames_1.classNames)(["zen-nav-edit-list", className || ""]), ref: appsListRef, children: childrenArray });
|
|
21
22
|
};
|
|
@@ -7,10 +7,11 @@ interface IPillContent {
|
|
|
7
7
|
descriptionText?: TLazyContent<ReactNode>;
|
|
8
8
|
mainAction?: TLazyContent<IPillExpandableAction>;
|
|
9
9
|
secondaryAction?: TLazyContent<IPillExpandableAction>;
|
|
10
|
+
tertiaryAction?: TLazyContent<IPillExpandableAction>;
|
|
10
11
|
onActionClick: () => void;
|
|
11
12
|
isVisible?: boolean;
|
|
12
13
|
errorHandler: (err: any) => ReactNode | undefined;
|
|
13
14
|
descriptionId?: string;
|
|
14
15
|
}
|
|
15
|
-
export declare const PillContent: ({ date, count, descriptionText, mainAction, secondaryAction, onActionClick, isVisible, errorHandler, descriptionId }: IPillContent) => import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare const PillContent: ({ date, count, descriptionText, mainAction, secondaryAction, tertiaryAction, onActionClick, isVisible, errorHandler, descriptionId }: IPillContent) => import("react/jsx-runtime").JSX.Element;
|
|
16
17
|
export {};
|
|
@@ -7,7 +7,7 @@ const button_1 = require("../button/button");
|
|
|
7
7
|
const classNames_1 = require("../commonHelpers/classNames/classNames");
|
|
8
8
|
const useDriveClassName_1 = require("../utils/theme/useDriveClassName");
|
|
9
9
|
const lazyContent_1 = require("../skeleton/lazyContent");
|
|
10
|
-
const PillContent = ({ date, count, descriptionText, mainAction, secondaryAction, onActionClick, isVisible = true, errorHandler, descriptionId }) => {
|
|
10
|
+
const PillContent = ({ date, count, descriptionText, mainAction, secondaryAction, tertiaryAction, onActionClick, isVisible = true, errorHandler, descriptionId }) => {
|
|
11
11
|
const headerDriveClassName = (0, useDriveClassName_1.useDriveClassName)("zen-status-pill-popup__header-text");
|
|
12
12
|
const descriptionDriveClassName = (0, useDriveClassName_1.useDriveClassName)("zen-status-pill-popup__description");
|
|
13
13
|
const mainActionClick = (0, react_1.useCallback)((onClick) => () => {
|
|
@@ -18,12 +18,17 @@ const PillContent = ({ date, count, descriptionText, mainAction, secondaryAction
|
|
|
18
18
|
onClick && onClick();
|
|
19
19
|
onActionClick();
|
|
20
20
|
}, [onActionClick]);
|
|
21
|
+
const tertiaryActionClick = (0, react_1.useCallback)((onClick) => () => {
|
|
22
|
+
onClick && onClick();
|
|
23
|
+
onActionClick();
|
|
24
|
+
}, [onActionClick]);
|
|
21
25
|
const renderMainButton = (0, react_1.useCallback)((data) => (0, jsx_runtime_1.jsx)(button_1.Button, Object.assign({ link: data.link }, data.linkOptions, { onClick: mainActionClick(data.onClick), type: "primary", children: data.text })), [mainActionClick]);
|
|
22
|
-
const renderSecondaryButton = (0, react_1.useCallback)((data) => (0, jsx_runtime_1.jsx)(button_1.Button, Object.assign({ link: data.link }, data.linkOptions, { onClick: secondaryActionClick(data.onClick), type: "
|
|
26
|
+
const renderSecondaryButton = (0, react_1.useCallback)((data) => (0, jsx_runtime_1.jsx)(button_1.Button, Object.assign({ link: data.link }, data.linkOptions, { onClick: secondaryActionClick(data.onClick), type: "secondary", children: data.text })), [secondaryActionClick]);
|
|
27
|
+
const renderTertiaryButton = (0, react_1.useCallback)((data) => (0, jsx_runtime_1.jsx)(button_1.Button, Object.assign({ link: data.link }, data.linkOptions, { onClick: tertiaryActionClick(data.onClick), type: "tertiary", children: data.text })), [tertiaryActionClick]);
|
|
23
28
|
const renderDescription = (0, react_1.useCallback)(data => (0, jsx_runtime_1.jsx)("div", { id: descriptionId, className: (0, classNames_1.classNames)(["zen-status-pill-popup__description", descriptionDriveClassName || ""]), children: data }), [descriptionDriveClassName, descriptionId]);
|
|
24
29
|
const renderCount = (0, react_1.useCallback)((data) => data ? (0, jsx_runtime_1.jsx)("div", { className: (0, classNames_1.classNames)(["zen-status-pill-popup__header-text zen-status-pill-popup__header-text--left", headerDriveClassName || ""]), children: data }) : null, [headerDriveClassName]);
|
|
25
30
|
const renderDate = (0, react_1.useCallback)((data) => data ? (0, jsx_runtime_1.jsx)("div", { className: (0, classNames_1.classNames)(["zen-status-pill-popup__header-text", headerDriveClassName || ""]), children: data }) : null, [headerDriveClassName]);
|
|
26
31
|
return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(date || count) && isVisible && (0, jsx_runtime_1.jsxs)("div", { className: "zen-status-pill-popup__content-header", children: [date && (0, jsx_runtime_1.jsx)(lazyContent_1.LazyContent, { width: 80, type: "text", content: date, isVisible: isVisible, errorHandler: errorHandler, render: renderDate }), count && (0, jsx_runtime_1.jsx)(lazyContent_1.LazyContent, { width: 80, type: "text", content: count, isVisible: isVisible, errorHandler: errorHandler, render: renderCount })] }), descriptionText &&
|
|
27
|
-
(0, jsx_runtime_1.jsx)(lazyContent_1.LazyContent, { width: 200, type: "text", content: descriptionText, isVisible: isVisible, errorHandler: errorHandler, render: renderDescription }), (mainAction || secondaryAction) && isVisible && (0, jsx_runtime_1.jsxs)("div", { className: "zen-status-pill-popup__actions", children: [mainAction && (0, jsx_runtime_1.jsx)(lazyContent_1.LazyContent, { type: "button", content: mainAction, errorHandler: errorHandler, render: renderMainButton, isVisible: isVisible }), secondaryAction && (0, jsx_runtime_1.jsx)(lazyContent_1.LazyContent, { type: "button", content: secondaryAction, errorHandler: errorHandler, render: renderSecondaryButton, isVisible: isVisible })] })] });
|
|
32
|
+
(0, jsx_runtime_1.jsx)(lazyContent_1.LazyContent, { width: 200, type: "text", content: descriptionText, isVisible: isVisible, errorHandler: errorHandler, render: renderDescription }), (mainAction || secondaryAction || tertiaryAction) && isVisible && (0, jsx_runtime_1.jsxs)("div", { className: "zen-status-pill-popup__actions", children: [mainAction && (0, jsx_runtime_1.jsx)(lazyContent_1.LazyContent, { type: "button", content: mainAction, errorHandler: errorHandler, render: renderMainButton, isVisible: isVisible }), secondaryAction && (0, jsx_runtime_1.jsx)(lazyContent_1.LazyContent, { type: "button", content: secondaryAction, errorHandler: errorHandler, render: renderSecondaryButton, isVisible: isVisible }), tertiaryAction && (0, jsx_runtime_1.jsx)(lazyContent_1.LazyContent, { type: "button", content: tertiaryAction, errorHandler: errorHandler, render: renderTertiaryButton, isVisible: isVisible })] })] });
|
|
28
33
|
};
|
|
29
34
|
exports.PillContent = PillContent;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "./pillExpandable.less";
|
|
2
2
|
import { IZenComponentProps } from "../commonHelpers/zenComponent";
|
|
3
|
+
import { Exclusive } from "../commonHelpers/types/exclusive";
|
|
3
4
|
import React, { FC, ReactNode } from "react";
|
|
4
5
|
import { TViewMoreButton } from "./getProps";
|
|
5
6
|
import { IIcon } from "../icons/icon";
|
|
@@ -20,6 +21,7 @@ export interface IBaseData {
|
|
|
20
21
|
count?: TLazyContent<number>;
|
|
21
22
|
mainAction?: TLazyContent<IPillExpandableAction>;
|
|
22
23
|
secondaryAction?: TLazyContent<IPillExpandableAction>;
|
|
24
|
+
tertiaryAction?: TLazyContent<IPillExpandableAction>;
|
|
23
25
|
getData?: undefined;
|
|
24
26
|
}
|
|
25
27
|
export interface IPromisedBaseData {
|
|
@@ -29,16 +31,20 @@ export interface IPromisedBaseData {
|
|
|
29
31
|
count?: undefined;
|
|
30
32
|
mainAction?: undefined;
|
|
31
33
|
secondaryAction?: undefined;
|
|
34
|
+
tertiaryAction?: undefined;
|
|
32
35
|
}
|
|
33
36
|
type TProps = IBaseData | IPromisedBaseData;
|
|
34
|
-
export
|
|
37
|
+
export type TPopoverVariant = "default" | "placeholder";
|
|
38
|
+
export type TPopoverSize = "small" | "medium" | "large";
|
|
39
|
+
type TPillTypeBase = "error" | "success" | "warning" | "info" | "default";
|
|
40
|
+
export type TPillType = TPillTypeBase | "upsell";
|
|
41
|
+
/** Base props shared by all PillExpandable variants */
|
|
42
|
+
export interface IPillExpandableBase extends IZenComponentProps {
|
|
35
43
|
uniquePills?: number;
|
|
36
44
|
text: string;
|
|
37
|
-
type?: "error" | "success" | "warning" | "info" | "default";
|
|
38
45
|
viewMoreAction?: TViewMoreButton;
|
|
39
46
|
className?: string;
|
|
40
47
|
popupClassName?: string;
|
|
41
|
-
icon?: FC<IIcon> | false;
|
|
42
48
|
secondaryIcon?: FC<IIcon> | false;
|
|
43
49
|
isFlat?: boolean;
|
|
44
50
|
isLoading?: boolean;
|
|
@@ -47,8 +53,25 @@ export interface IPillExpandable extends IZenComponentProps {
|
|
|
47
53
|
isBeta?: boolean;
|
|
48
54
|
hideCounterNumber?: boolean;
|
|
49
55
|
autoExpandChildren?: boolean;
|
|
56
|
+
/** Popover size: small (220px), medium (320px), large (420px). Default: "medium" */
|
|
57
|
+
popoverSize?: TPopoverSize;
|
|
58
|
+
/** Title displayed in the popover title bar. If provided, the title bar will be shown. */
|
|
59
|
+
title?: string;
|
|
60
|
+
}
|
|
61
|
+
/** Props for standard PillExpandable (non-upsell) */
|
|
62
|
+
interface IPillExpandableStandard extends IPillExpandableBase {
|
|
63
|
+
type?: TPillTypeBase;
|
|
64
|
+
icon?: FC<IIcon> | false;
|
|
65
|
+
}
|
|
66
|
+
/** Props for upsell PillExpandable - icon and children are not allowed */
|
|
67
|
+
interface IPillExpandableUpsell extends IPillExpandableBase {
|
|
68
|
+
type: "upsell";
|
|
69
|
+
icon?: never;
|
|
70
|
+
children?: never;
|
|
50
71
|
}
|
|
72
|
+
/** Combined type using Exclusive to prevent mixing upsell with standard props */
|
|
73
|
+
export type IPillExpandable = Exclusive<IPillExpandableStandard, IPillExpandableUpsell>;
|
|
51
74
|
type TPillExpandable = IPillExpandable & TProps;
|
|
52
|
-
export declare const PillExpandable: React.MemoExoticComponent<({ children, errorHandler, getData, text, description, date, count, mainAction, secondaryAction, viewMoreAction, uniquePills, type, className, popupClassName, isLoading, isFlat, icon, secondaryIcon, isBeta, loadingWidth, hideCounterNumber, autoExpandChildren }: TPillExpandable) => import("react/jsx-runtime").JSX.Element>;
|
|
75
|
+
export declare const PillExpandable: React.MemoExoticComponent<({ children, errorHandler, getData, text, description, date, count, mainAction, secondaryAction, tertiaryAction, viewMoreAction, uniquePills, type, className, popupClassName, isLoading, isFlat, icon, secondaryIcon, isBeta, loadingWidth, hideCounterNumber, autoExpandChildren, popoverSize, title }: TPillExpandable) => import("react/jsx-runtime").JSX.Element>;
|
|
53
76
|
export declare const TRANSLATIONS: string[];
|
|
54
77
|
export {};
|