@planningcenter/topbar 11.1.1 → 11.2.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/topbar.js +101 -1
- package/dist/topbar.js.map +1 -1
- package/dist/topbar.modern.js +101 -1
- package/dist/topbar.modern.js.map +1 -1
- package/dist/topbar.module.js +101 -1
- package/dist/topbar.module.js.map +1 -1
- package/dist/topbar.umd.js +101 -1
- package/dist/topbar.umd.js.map +1 -1
- package/package.json +11 -3
package/dist/topbar.modern.js
CHANGED
|
@@ -1901,6 +1901,9 @@ const AppsButton = ({
|
|
|
1901
1901
|
...nativeProps
|
|
1902
1902
|
}) => {
|
|
1903
1903
|
return /*#__PURE__*/React__default.createElement(Unbutton, {
|
|
1904
|
+
"aria-expanded": expanded,
|
|
1905
|
+
"aria-haspopup": "true",
|
|
1906
|
+
"aria-label": `App switcher: ${appName}`,
|
|
1904
1907
|
onClick: onClick,
|
|
1905
1908
|
className: classnames(styles$9.button, {
|
|
1906
1909
|
[styles$9["is-expanded"]]: expanded
|
|
@@ -1935,6 +1938,72 @@ function getAppsSwitchProductPath(productName) {
|
|
|
1935
1938
|
return `${basePath}?${params.toString()}`;
|
|
1936
1939
|
}
|
|
1937
1940
|
|
|
1941
|
+
// Currently scoped to links and buttons since the app switcher only contains
|
|
1942
|
+
// those elements. If this hook is reused in containers with form elements,
|
|
1943
|
+
// expand to include: input, select, textarea, [tabindex]:not([tabindex="-1"])
|
|
1944
|
+
const FOCUSABLE_SELECTOR = "a[href], button:not([disabled])";
|
|
1945
|
+
/**
|
|
1946
|
+
* Manages focus trapping within a container element.
|
|
1947
|
+
*
|
|
1948
|
+
* When `active` becomes true:
|
|
1949
|
+
* - Stores the currently focused element
|
|
1950
|
+
* - Moves focus to the first focusable child
|
|
1951
|
+
*
|
|
1952
|
+
* When `active` becomes false:
|
|
1953
|
+
* - Returns focus to the previously focused element
|
|
1954
|
+
*
|
|
1955
|
+
* While active:
|
|
1956
|
+
* - Tab/Shift+Tab wraps within the container
|
|
1957
|
+
* - Escape calls `onClose`
|
|
1958
|
+
*
|
|
1959
|
+
* Note: focus trapping uses scoped querySelector on the container ref,
|
|
1960
|
+
* which is the same approach used by libraries like focus-trap-react.
|
|
1961
|
+
*/
|
|
1962
|
+
function useFocusTrap({
|
|
1963
|
+
active,
|
|
1964
|
+
onClose
|
|
1965
|
+
}) {
|
|
1966
|
+
const containerRef = React__default.useRef(null);
|
|
1967
|
+
const previousFocusRef = React__default.useRef(null);
|
|
1968
|
+
React__default.useLayoutEffect(() => {
|
|
1969
|
+
var _a, _b, _c;
|
|
1970
|
+
if (active) {
|
|
1971
|
+
previousFocusRef.current = document.activeElement;
|
|
1972
|
+
(_b = (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(FOCUSABLE_SELECTOR)) === null || _b === void 0 ? void 0 : _b.focus();
|
|
1973
|
+
} else {
|
|
1974
|
+
(_c = previousFocusRef.current) === null || _c === void 0 ? void 0 : _c.focus();
|
|
1975
|
+
previousFocusRef.current = null;
|
|
1976
|
+
}
|
|
1977
|
+
}, [active]);
|
|
1978
|
+
const handleKeyDown = React__default.useCallback(e => {
|
|
1979
|
+
var _a;
|
|
1980
|
+
if (!active) {
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1983
|
+
if (e.key === "Escape") {
|
|
1984
|
+
onClose();
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1987
|
+
if (e.key === "Tab") {
|
|
1988
|
+
const focusable = (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll(FOCUSABLE_SELECTOR);
|
|
1989
|
+
if (!focusable || focusable.length === 0) return;
|
|
1990
|
+
const first = focusable[0];
|
|
1991
|
+
const last = focusable[focusable.length - 1];
|
|
1992
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
1993
|
+
e.preventDefault();
|
|
1994
|
+
last.focus();
|
|
1995
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
1996
|
+
e.preventDefault();
|
|
1997
|
+
first.focus();
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
}, [active, onClose]);
|
|
2001
|
+
return {
|
|
2002
|
+
containerRef,
|
|
2003
|
+
handleKeyDown
|
|
2004
|
+
};
|
|
2005
|
+
}
|
|
2006
|
+
|
|
1938
2007
|
const fontFamily = {
|
|
1939
2008
|
fontFamily: `inherit`
|
|
1940
2009
|
};
|
|
@@ -2036,6 +2105,13 @@ const AppsMenu = ({
|
|
|
2036
2105
|
const restProducts = [...apps].filter(({
|
|
2037
2106
|
name
|
|
2038
2107
|
}) => name !== "Home" && name !== "Accounts").sort((a, b) => a.name.localeCompare(b.name));
|
|
2108
|
+
const {
|
|
2109
|
+
containerRef: menuRef,
|
|
2110
|
+
handleKeyDown
|
|
2111
|
+
} = useFocusTrap({
|
|
2112
|
+
active: visible,
|
|
2113
|
+
onClose: toggle
|
|
2114
|
+
});
|
|
2039
2115
|
const listItemProps = {
|
|
2040
2116
|
...fontFamily,
|
|
2041
2117
|
alignItems: "center",
|
|
@@ -2052,12 +2128,16 @@ const AppsMenu = ({
|
|
|
2052
2128
|
className: styles$8.popup,
|
|
2053
2129
|
isVisible: visible
|
|
2054
2130
|
}, /*#__PURE__*/React__default.createElement("menu", {
|
|
2131
|
+
ref: menuRef,
|
|
2055
2132
|
"data-pendo": "app-switcher",
|
|
2133
|
+
onKeyDown: handleKeyDown,
|
|
2056
2134
|
style: {
|
|
2057
2135
|
margin: 0,
|
|
2058
2136
|
padding: 0
|
|
2059
2137
|
}
|
|
2060
2138
|
}, homeProduct && (/*#__PURE__*/React__default.createElement(HoverableListItem, {
|
|
2139
|
+
"aria-current": appName === homeProduct.name ? "page" : undefined,
|
|
2140
|
+
"aria-label": homeProduct.name,
|
|
2061
2141
|
component: "a",
|
|
2062
2142
|
"data-pendo": `app-switcher--change-app--${homeProduct.name}`,
|
|
2063
2143
|
"data-turbolinks": false,
|
|
@@ -2082,6 +2162,8 @@ const AppsMenu = ({
|
|
|
2082
2162
|
const isFirst = i === 0;
|
|
2083
2163
|
const isLast = i === restProducts.length - 1;
|
|
2084
2164
|
return /*#__PURE__*/React__default.createElement(HoverableListItem, {
|
|
2165
|
+
"aria-current": name === appName ? "page" : undefined,
|
|
2166
|
+
"aria-label": name,
|
|
2085
2167
|
component: "a",
|
|
2086
2168
|
"data-pendo": `app-switcher--change-app--${name}`,
|
|
2087
2169
|
"data-turbolinks": false,
|
|
@@ -2105,6 +2187,8 @@ const AppsMenu = ({
|
|
|
2105
2187
|
size: 18
|
|
2106
2188
|
}));
|
|
2107
2189
|
}), accountsProduct && (/*#__PURE__*/React__default.createElement(HoverableListItem, {
|
|
2190
|
+
"aria-current": appName === accountsProduct.name ? "page" : undefined,
|
|
2191
|
+
"aria-label": accountsProduct.name,
|
|
2108
2192
|
component: "a",
|
|
2109
2193
|
"data-pendo": `app-switcher--change-app--${accountsProduct.name}`,
|
|
2110
2194
|
"data-turbolinks": false,
|
|
@@ -2604,6 +2688,13 @@ const SmallTopbar = ({
|
|
|
2604
2688
|
const restProducts = [...props.apps].filter(({
|
|
2605
2689
|
name
|
|
2606
2690
|
}) => name !== "Home" && name !== "Accounts");
|
|
2691
|
+
const {
|
|
2692
|
+
containerRef: appMenuRef,
|
|
2693
|
+
handleKeyDown: handleAppMenuKeyDown
|
|
2694
|
+
} = useFocusTrap({
|
|
2695
|
+
active: productSwitcherVisible,
|
|
2696
|
+
onClose: () => setProductSwitcherVisible(false)
|
|
2697
|
+
});
|
|
2607
2698
|
React__default.useEffect(() => {
|
|
2608
2699
|
setRoutesMenuVisible(false);
|
|
2609
2700
|
}, [activeRoute && ((_b = (_a = activeRoute[0]) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b.href)]);
|
|
@@ -2612,6 +2703,9 @@ const SmallTopbar = ({
|
|
|
2612
2703
|
...props.style
|
|
2613
2704
|
}
|
|
2614
2705
|
}, /*#__PURE__*/React__default.createElement("button", {
|
|
2706
|
+
"aria-expanded": productSwitcherVisible,
|
|
2707
|
+
"aria-haspopup": "true",
|
|
2708
|
+
"aria-label": `App switcher: ${props.appName}`,
|
|
2615
2709
|
type: "button",
|
|
2616
2710
|
className: styles$3["appmenu-button"],
|
|
2617
2711
|
onClick: () => {
|
|
@@ -2627,6 +2721,7 @@ const SmallTopbar = ({
|
|
|
2627
2721
|
}, props.routes)), /*#__PURE__*/React__default.createElement(ClientStorage, {
|
|
2628
2722
|
item: "Topbar:Small:Menus:Products:visibility",
|
|
2629
2723
|
render: () => (/*#__PURE__*/React__default.createElement("div", {
|
|
2724
|
+
ref: appMenuRef,
|
|
2630
2725
|
className: styles$3["appmenu-layer"],
|
|
2631
2726
|
style: {
|
|
2632
2727
|
...(productSwitcherVisible && {
|
|
@@ -2635,7 +2730,8 @@ const SmallTopbar = ({
|
|
|
2635
2730
|
},
|
|
2636
2731
|
onClick: () => {
|
|
2637
2732
|
setProductSwitcherVisible(visible => !visible);
|
|
2638
|
-
}
|
|
2733
|
+
},
|
|
2734
|
+
onKeyDown: handleAppMenuKeyDown
|
|
2639
2735
|
}, /*#__PURE__*/React__default.createElement(CloseMenuButton, null), /*#__PURE__*/React__default.createElement("div", {
|
|
2640
2736
|
onClick: e => {
|
|
2641
2737
|
const {
|
|
@@ -2647,6 +2743,8 @@ const SmallTopbar = ({
|
|
|
2647
2743
|
},
|
|
2648
2744
|
className: styles$3["appmenu-container"]
|
|
2649
2745
|
}, /*#__PURE__*/React__default.createElement("a", {
|
|
2746
|
+
"aria-current": props.appName === (homeProduct === null || homeProduct === void 0 ? void 0 : homeProduct.name) ? "page" : undefined,
|
|
2747
|
+
"aria-label": homeProduct ? homeProduct.name : props.appName,
|
|
2650
2748
|
"data-pendo": `app-switcher--change-app--${homeProduct === null || homeProduct === void 0 ? void 0 : homeProduct.name}`,
|
|
2651
2749
|
"data-turbolinks": false,
|
|
2652
2750
|
className: classnames(styles$3["appmenu-list-a"], styles$3["appmenu-list-a--top"]),
|
|
@@ -2664,6 +2762,8 @@ const SmallTopbar = ({
|
|
|
2664
2762
|
key: name,
|
|
2665
2763
|
className: styles$3["appmenu-list-li"]
|
|
2666
2764
|
}, /*#__PURE__*/React__default.createElement("a", {
|
|
2765
|
+
"aria-current": name === props.appName ? "page" : undefined,
|
|
2766
|
+
"aria-label": name,
|
|
2667
2767
|
"data-pendo": `app-switcher--change-app--${name}`,
|
|
2668
2768
|
"data-turbolinks": false,
|
|
2669
2769
|
className: styles$3["appmenu-list-a"],
|