@doyourjob/gravity-ui-page-constructor-addons 2.1.46 → 2.1.48
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 +12 -0
- package/build/cjs/components/HeaderStripe/HeaderStripe.css +10 -0
- package/build/cjs/components/HeaderStripe/HeaderStripe.js +18 -7
- package/build/cjs/components/Navigation/containers/Footer/GroupLinks/GroupLinks.js +3 -4
- package/build/cjs/components/NewHeader/NewHeader.css +5 -0
- package/build/cjs/components/NewHeader/NewHeader.js +3 -2
- package/build/cjs/components/NewHeader/components/NHLoginButton/NHLoginButton.css +18 -0
- package/build/cjs/components/NewHeader/components/NHLoginButton/NHLoginButton.js +18 -6
- package/build/cjs/components/NewHeader/components/NHMobileNavigation/NHMobileNavigation.css +10 -0
- package/build/cjs/components/NewHeader/components/NHMobileNavigation/NHMobileNavigation.js +8 -3
- package/build/cjs/components/NewHeader/components/NHMobileNavigationItem/NHMobileNavigationItem.css +5 -0
- package/build/cjs/components/NewHeader/components/NHMobileNavigationItem/NHMobileNavigationItem.js +2 -1
- package/build/cjs/components/NewHeader/components/NHMobileNavigationPopup/NHMobileNavigationPopup.d.ts +2 -1
- package/build/cjs/components/NewHeader/components/NHMobileNavigationPopup/NHMobileNavigationPopup.js +2 -2
- package/build/cjs/components/NewHeader/components/NHNavigation/NHNavigation.d.ts +4 -1
- package/build/cjs/components/NewHeader/components/NHNavigation/NHNavigation.js +72 -9
- package/build/cjs/components/NewHeader/components/NHNavigationItem/NHNavigationItem.css +5 -0
- package/build/cjs/components/NewHeader/components/NHNavigationItem/NHNavigationItem.d.ts +3 -0
- package/build/cjs/components/NewHeader/components/NHNavigationItem/NHNavigationItem.js +12 -3
- package/build/cjs/components/NewHeader/components/NHNavigationPopup/NHNavigationPopup.css +13 -2
- package/build/cjs/components/NewHeader/components/NHNavigationPopup/NHNavigationPopup.js +58 -6
- package/build/cjs/components/NewHeader/components/NHNavigationPopup/keyboardNavigation.d.ts +4 -0
- package/build/cjs/components/NewHeader/components/NHNavigationPopup/keyboardNavigation.js +43 -0
- package/build/cjs/components/NewHeader/components/NHPopupItem/NHPopupItem.css +5 -0
- package/build/esm/components/HeaderStripe/HeaderStripe.css +10 -0
- package/build/esm/components/HeaderStripe/HeaderStripe.js +18 -7
- package/build/esm/components/Navigation/containers/Footer/GroupLinks/GroupLinks.js +3 -4
- package/build/esm/components/NewHeader/NewHeader.css +5 -0
- package/build/esm/components/NewHeader/NewHeader.js +3 -2
- package/build/esm/components/NewHeader/components/NHLoginButton/NHLoginButton.css +18 -0
- package/build/esm/components/NewHeader/components/NHLoginButton/NHLoginButton.js +19 -7
- package/build/esm/components/NewHeader/components/NHMobileNavigation/NHMobileNavigation.css +10 -0
- package/build/esm/components/NewHeader/components/NHMobileNavigation/NHMobileNavigation.js +9 -4
- package/build/esm/components/NewHeader/components/NHMobileNavigationItem/NHMobileNavigationItem.css +5 -0
- package/build/esm/components/NewHeader/components/NHMobileNavigationItem/NHMobileNavigationItem.js +2 -1
- package/build/esm/components/NewHeader/components/NHMobileNavigationPopup/NHMobileNavigationPopup.d.ts +2 -1
- package/build/esm/components/NewHeader/components/NHMobileNavigationPopup/NHMobileNavigationPopup.js +2 -2
- package/build/esm/components/NewHeader/components/NHNavigation/NHNavigation.d.ts +4 -1
- package/build/esm/components/NewHeader/components/NHNavigation/NHNavigation.js +72 -9
- package/build/esm/components/NewHeader/components/NHNavigationItem/NHNavigationItem.css +5 -0
- package/build/esm/components/NewHeader/components/NHNavigationItem/NHNavigationItem.d.ts +3 -0
- package/build/esm/components/NewHeader/components/NHNavigationItem/NHNavigationItem.js +12 -3
- package/build/esm/components/NewHeader/components/NHNavigationPopup/NHNavigationPopup.css +13 -2
- package/build/esm/components/NewHeader/components/NHNavigationPopup/NHNavigationPopup.js +58 -6
- package/build/esm/components/NewHeader/components/NHNavigationPopup/keyboardNavigation.d.ts +4 -0
- package/build/esm/components/NewHeader/components/NHNavigationPopup/keyboardNavigation.js +38 -0
- package/build/esm/components/NewHeader/components/NHPopupItem/NHPopupItem.css +5 -0
- package/package.json +2 -1
|
@@ -6,6 +6,9 @@ interface NavigationItemOwnProps {
|
|
|
6
6
|
item: NHNavigationItemData;
|
|
7
7
|
isActive: boolean;
|
|
8
8
|
handleActiveTab: (currentIndex: number) => void;
|
|
9
|
+
handleFocusTab: (currentIndex: number) => void;
|
|
10
|
+
handleFocusTabPopup: (currentIndex: number) => void;
|
|
11
|
+
handleToggleTab: (currentIndex: number) => void;
|
|
9
12
|
children?: ReactNode;
|
|
10
13
|
tooltipId?: string;
|
|
11
14
|
}
|
|
@@ -9,21 +9,30 @@ import { ChevronDown } from '../ChevronDown';
|
|
|
9
9
|
import { ChevronUp } from '../ChevronUp';
|
|
10
10
|
import './NHNavigationItem.css';
|
|
11
11
|
const b = block('nh-navigation-item');
|
|
12
|
-
export const NHNavigationItem = ({ item, isActive, handleActiveTab, index, children, tooltipId, }) => {
|
|
12
|
+
export const NHNavigationItem = ({ item, isActive, handleActiveTab, handleFocusTab, handleFocusTabPopup, handleToggleTab, index, children, tooltipId, }) => {
|
|
13
13
|
var _a, _b, _c, _d;
|
|
14
14
|
const setupRouteChangeHandler = useContext(RouteChangeHandlerContext);
|
|
15
15
|
const handleMouseEnter = useCallback(() => handleActiveTab(index), [handleActiveTab, index]);
|
|
16
16
|
const handleMouseLeave = useCallback(() => handleActiveTab(NO_MENU_TAB_SELECTED), [handleActiveTab]);
|
|
17
|
+
const handleToggle = useCallback(() => handleToggleTab(index), [handleToggleTab, index]);
|
|
18
|
+
const handlePopupFocus = useCallback(() => handleFocusTab(index), [handleFocusTab, index]);
|
|
19
|
+
const handleLinkFocus = useCallback(() => handleFocusTab(NO_MENU_TAB_SELECTED), [handleFocusTab]);
|
|
20
|
+
const handleKeyDown = useCallback((event) => {
|
|
21
|
+
if (event.key === 'ArrowDown') {
|
|
22
|
+
event.preventDefault();
|
|
23
|
+
handleFocusTabPopup(index);
|
|
24
|
+
}
|
|
25
|
+
}, [handleFocusTabPopup, index]);
|
|
17
26
|
useEffect(() => setupRouteChangeHandler === null || setupRouteChangeHandler === void 0 ? void 0 : setupRouteChangeHandler(() => {
|
|
18
27
|
handleMouseLeave();
|
|
19
28
|
}), [handleMouseLeave, setupRouteChangeHandler]);
|
|
20
29
|
if (item.type === NHNavigationItemType.Link) {
|
|
21
30
|
return (React.createElement("li", { className: b({ disable: !((_a = item.data) === null || _a === void 0 ? void 0 : _a.url) }), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave },
|
|
22
|
-
React.createElement("a", Object.assign({ className: b('text', { active: isActive }), href: (_b = item.data) === null || _b === void 0 ? void 0 : _b.url }, getLinkProps(((_c = item.data) === null || _c === void 0 ? void 0 : _c.url) || '', undefined, (_d = item.data) === null || _d === void 0 ? void 0 : _d.target)), item.title),
|
|
31
|
+
React.createElement("a", Object.assign({ className: b('text', { active: isActive }), href: (_b = item.data) === null || _b === void 0 ? void 0 : _b.url, onFocus: handleLinkFocus }, getLinkProps(((_c = item.data) === null || _c === void 0 ? void 0 : _c.url) || '', undefined, (_d = item.data) === null || _d === void 0 ? void 0 : _d.target)), item.title),
|
|
23
32
|
children));
|
|
24
33
|
}
|
|
25
34
|
return (React.createElement("li", { className: b({}), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave },
|
|
26
|
-
React.createElement("button", { className: b('text', { active: isActive, cursor: 'default' }), onClick:
|
|
35
|
+
React.createElement("button", { className: b('text', { active: isActive, cursor: 'default' }), type: "button", onClick: handleToggle, onFocus: handlePopupFocus, onKeyDown: handleKeyDown, "aria-expanded": isActive, "aria-controls": tooltipId },
|
|
27
36
|
item.title,
|
|
28
37
|
React.createElement(Icon, { className: b('icon'), data: isActive ? ChevronUp : ChevronDown, size: 16 })),
|
|
29
38
|
children));
|
|
@@ -8,6 +8,17 @@
|
|
|
8
8
|
z-index: 10;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
.pc-addons-nh-navigation-popup:focus
|
|
12
|
-
|
|
11
|
+
.pc-addons-nh-navigation-popup a:focus-visible, .pc-addons-nh-navigation-popup a[data-keyboard-focus=true],
|
|
12
|
+
.pc-addons-nh-navigation-popup button:focus-visible,
|
|
13
|
+
.pc-addons-nh-navigation-popup button[data-keyboard-focus=true],
|
|
14
|
+
.pc-addons-nh-navigation-popup input:focus-visible,
|
|
15
|
+
.pc-addons-nh-navigation-popup input[data-keyboard-focus=true],
|
|
16
|
+
.pc-addons-nh-navigation-popup select:focus-visible,
|
|
17
|
+
.pc-addons-nh-navigation-popup select[data-keyboard-focus=true],
|
|
18
|
+
.pc-addons-nh-navigation-popup textarea:focus-visible,
|
|
19
|
+
.pc-addons-nh-navigation-popup textarea[data-keyboard-focus=true],
|
|
20
|
+
.pc-addons-nh-navigation-popup [tabindex]:not([tabindex="-1"]):focus-visible,
|
|
21
|
+
.pc-addons-nh-navigation-popup [tabindex]:not([tabindex="-1"])[data-keyboard-focus=true] {
|
|
22
|
+
outline: 2px solid var(--g-color-line-focus);
|
|
23
|
+
outline-offset: 4px;
|
|
13
24
|
}
|
|
@@ -1,14 +1,66 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useRef } from 'react';
|
|
2
2
|
import ReactDOM from 'react-dom';
|
|
3
|
-
import { FocusTrap } from '../../../../utils/FocusTrap';
|
|
4
3
|
import { block } from '../../../../utils/cn';
|
|
4
|
+
import { focusFromKeyboard, getFocusableElements } from './keyboardNavigation';
|
|
5
5
|
import './NHNavigationPopup.css';
|
|
6
6
|
const b = block('nh-navigation-popup');
|
|
7
|
+
const nextKeys = new Set(['ArrowDown', 'ArrowRight']);
|
|
8
|
+
const previousKeys = new Set(['ArrowUp', 'ArrowLeft']);
|
|
9
|
+
const getNextIndex = (currentIndex, offset, elementsCount) => {
|
|
10
|
+
if (currentIndex === -1) {
|
|
11
|
+
return offset > 0 ? 0 : elementsCount - 1;
|
|
12
|
+
}
|
|
13
|
+
return (currentIndex + offset + elementsCount) % elementsCount;
|
|
14
|
+
};
|
|
7
15
|
export const NHNavigationPopup = ({ headerRef, children, id }) => {
|
|
16
|
+
const popupRef = useRef(null);
|
|
17
|
+
const focusRelativeElement = useCallback((offset) => {
|
|
18
|
+
const popup = popupRef.current;
|
|
19
|
+
const focusableElements = getFocusableElements(popup);
|
|
20
|
+
if (!popup || !focusableElements.length) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const activeElement = popup.ownerDocument.activeElement;
|
|
24
|
+
const currentIndex = activeElement ? focusableElements.indexOf(activeElement) : -1;
|
|
25
|
+
const nextIndex = getNextIndex(currentIndex, offset, focusableElements.length);
|
|
26
|
+
focusFromKeyboard(focusableElements[nextIndex], popup);
|
|
27
|
+
}, []);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const popup = popupRef.current;
|
|
30
|
+
if (!popup) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const handleKeyDown = (event) => {
|
|
34
|
+
if (event.key === 'Tab') {
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
focusRelativeElement(event.shiftKey ? -1 : 1);
|
|
37
|
+
}
|
|
38
|
+
else if (nextKeys.has(event.key)) {
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
focusRelativeElement(1);
|
|
41
|
+
}
|
|
42
|
+
else if (previousKeys.has(event.key)) {
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
focusRelativeElement(-1);
|
|
45
|
+
}
|
|
46
|
+
else if (event.key === 'Home') {
|
|
47
|
+
event.preventDefault();
|
|
48
|
+
const popupElement = popupRef.current;
|
|
49
|
+
focusFromKeyboard(getFocusableElements(popupElement)[0], popupElement);
|
|
50
|
+
}
|
|
51
|
+
else if (event.key === 'End') {
|
|
52
|
+
event.preventDefault();
|
|
53
|
+
const popupElement = popupRef.current;
|
|
54
|
+
const focusableElements = getFocusableElements(popupElement);
|
|
55
|
+
focusFromKeyboard(focusableElements[focusableElements.length - 1], popupElement);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
popup.addEventListener('keydown', handleKeyDown);
|
|
59
|
+
return () => {
|
|
60
|
+
popup.removeEventListener('keydown', handleKeyDown);
|
|
61
|
+
};
|
|
62
|
+
}, [focusRelativeElement]);
|
|
8
63
|
return (headerRef === null || headerRef === void 0 ? void 0 : headerRef.current)
|
|
9
|
-
? ReactDOM.createPortal(React.createElement(
|
|
10
|
-
React.createElement("div", {
|
|
11
|
-
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
12
|
-
tabIndex: 0, className: b(), id: id }, children)), headerRef.current)
|
|
64
|
+
? ReactDOM.createPortal(React.createElement("div", { className: b(), id: id, ref: popupRef }, children), headerRef.current)
|
|
13
65
|
: null;
|
|
14
66
|
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const keyboardFocusAttribute = "data-keyboard-focus";
|
|
2
|
+
export declare const focusableElementSelector: string;
|
|
3
|
+
export declare const getFocusableElements: (root?: HTMLElement | null) => HTMLElement[];
|
|
4
|
+
export declare const focusFromKeyboard: (element?: HTMLElement | null, root?: ParentNode | null) => void;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const keyboardFocusAttribute = 'data-keyboard-focus';
|
|
2
|
+
export const focusableElementSelector = [
|
|
3
|
+
'a[href]',
|
|
4
|
+
'button:not([disabled])',
|
|
5
|
+
'input:not([disabled])',
|
|
6
|
+
'select:not([disabled])',
|
|
7
|
+
'textarea:not([disabled])',
|
|
8
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
9
|
+
].join(',');
|
|
10
|
+
const keyboardFocusSelector = `[${keyboardFocusAttribute}='true']`;
|
|
11
|
+
const blurHandlers = new WeakMap();
|
|
12
|
+
const clearKeyboardFocus = (root) => {
|
|
13
|
+
root.querySelectorAll(keyboardFocusSelector).forEach((element) => {
|
|
14
|
+
const blurHandler = blurHandlers.get(element);
|
|
15
|
+
if (blurHandler) {
|
|
16
|
+
element.removeEventListener('blur', blurHandler);
|
|
17
|
+
blurHandlers.delete(element);
|
|
18
|
+
}
|
|
19
|
+
element.removeAttribute(keyboardFocusAttribute);
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
export const getFocusableElements = (root) => Array.from((root === null || root === void 0 ? void 0 : root.querySelectorAll(focusableElementSelector)) || []);
|
|
23
|
+
export const focusFromKeyboard = (element, root) => {
|
|
24
|
+
if (!element) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const focusRoot = root || element.ownerDocument;
|
|
28
|
+
const handleBlur = () => {
|
|
29
|
+
element.removeAttribute(keyboardFocusAttribute);
|
|
30
|
+
element.removeEventListener('blur', handleBlur);
|
|
31
|
+
blurHandlers.delete(element);
|
|
32
|
+
};
|
|
33
|
+
clearKeyboardFocus(focusRoot);
|
|
34
|
+
element.setAttribute(keyboardFocusAttribute, 'true');
|
|
35
|
+
element.addEventListener('blur', handleBlur);
|
|
36
|
+
blurHandlers.set(element, handleBlur);
|
|
37
|
+
element.focus({ preventScroll: true });
|
|
38
|
+
};
|
|
@@ -57,6 +57,11 @@ unpredictable css rules order in build */
|
|
|
57
57
|
opacity: 1;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
.pc-addons-nh-navigation-popup-item:focus, .pc-addons-nh-navigation-popup-item:focus-visible, .pc-addons-nh-navigation-popup-item[data-keyboard-focus=true] {
|
|
61
|
+
outline: 2px solid var(--g-color-line-focus);
|
|
62
|
+
outline-offset: 4px;
|
|
63
|
+
}
|
|
64
|
+
|
|
60
65
|
.pc-addons-nh-navigation-popup-item_disable {
|
|
61
66
|
pointer-events: none;
|
|
62
67
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doyourjob/gravity-ui-page-constructor-addons",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.48",
|
|
4
4
|
"description": "Components and plugins for @doyourjob/gravity-ui-page-constructor",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"*.scss"
|
|
43
43
|
],
|
|
44
44
|
"scripts": {
|
|
45
|
+
"bump-patch-release": "node ./scripts/bump-patch.mjs",
|
|
45
46
|
"deps:install": "npm ci",
|
|
46
47
|
"deps:truncate": "npm prune --production",
|
|
47
48
|
"lint:fix": "run-s lint:js:fix lint:styles:fix lint:prettier:fix typecheck",
|