@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.umd.js
CHANGED
|
@@ -1912,6 +1912,9 @@
|
|
|
1912
1912
|
...nativeProps
|
|
1913
1913
|
}) => {
|
|
1914
1914
|
return /*#__PURE__*/React.createElement(Unbutton, {
|
|
1915
|
+
"aria-expanded": expanded,
|
|
1916
|
+
"aria-haspopup": "true",
|
|
1917
|
+
"aria-label": `App switcher: ${appName}`,
|
|
1915
1918
|
onClick: onClick,
|
|
1916
1919
|
className: classnames(styles$9.button, {
|
|
1917
1920
|
[styles$9["is-expanded"]]: expanded
|
|
@@ -1946,6 +1949,72 @@
|
|
|
1946
1949
|
return `${basePath}?${params.toString()}`;
|
|
1947
1950
|
}
|
|
1948
1951
|
|
|
1952
|
+
// Currently scoped to links and buttons since the app switcher only contains
|
|
1953
|
+
// those elements. If this hook is reused in containers with form elements,
|
|
1954
|
+
// expand to include: input, select, textarea, [tabindex]:not([tabindex="-1"])
|
|
1955
|
+
const FOCUSABLE_SELECTOR = "a[href], button:not([disabled])";
|
|
1956
|
+
/**
|
|
1957
|
+
* Manages focus trapping within a container element.
|
|
1958
|
+
*
|
|
1959
|
+
* When `active` becomes true:
|
|
1960
|
+
* - Stores the currently focused element
|
|
1961
|
+
* - Moves focus to the first focusable child
|
|
1962
|
+
*
|
|
1963
|
+
* When `active` becomes false:
|
|
1964
|
+
* - Returns focus to the previously focused element
|
|
1965
|
+
*
|
|
1966
|
+
* While active:
|
|
1967
|
+
* - Tab/Shift+Tab wraps within the container
|
|
1968
|
+
* - Escape calls `onClose`
|
|
1969
|
+
*
|
|
1970
|
+
* Note: focus trapping uses scoped querySelector on the container ref,
|
|
1971
|
+
* which is the same approach used by libraries like focus-trap-react.
|
|
1972
|
+
*/
|
|
1973
|
+
function useFocusTrap({
|
|
1974
|
+
active,
|
|
1975
|
+
onClose
|
|
1976
|
+
}) {
|
|
1977
|
+
const containerRef = React.useRef(null);
|
|
1978
|
+
const previousFocusRef = React.useRef(null);
|
|
1979
|
+
React.useLayoutEffect(() => {
|
|
1980
|
+
var _a, _b, _c;
|
|
1981
|
+
if (active) {
|
|
1982
|
+
previousFocusRef.current = document.activeElement;
|
|
1983
|
+
(_b = (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(FOCUSABLE_SELECTOR)) === null || _b === void 0 ? void 0 : _b.focus();
|
|
1984
|
+
} else {
|
|
1985
|
+
(_c = previousFocusRef.current) === null || _c === void 0 ? void 0 : _c.focus();
|
|
1986
|
+
previousFocusRef.current = null;
|
|
1987
|
+
}
|
|
1988
|
+
}, [active]);
|
|
1989
|
+
const handleKeyDown = React.useCallback(e => {
|
|
1990
|
+
var _a;
|
|
1991
|
+
if (!active) {
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
if (e.key === "Escape") {
|
|
1995
|
+
onClose();
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1998
|
+
if (e.key === "Tab") {
|
|
1999
|
+
const focusable = (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll(FOCUSABLE_SELECTOR);
|
|
2000
|
+
if (!focusable || focusable.length === 0) return;
|
|
2001
|
+
const first = focusable[0];
|
|
2002
|
+
const last = focusable[focusable.length - 1];
|
|
2003
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
2004
|
+
e.preventDefault();
|
|
2005
|
+
last.focus();
|
|
2006
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
2007
|
+
e.preventDefault();
|
|
2008
|
+
first.focus();
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
}, [active, onClose]);
|
|
2012
|
+
return {
|
|
2013
|
+
containerRef,
|
|
2014
|
+
handleKeyDown
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
|
|
1949
2018
|
const fontFamily = {
|
|
1950
2019
|
fontFamily: `inherit`
|
|
1951
2020
|
};
|
|
@@ -2047,6 +2116,13 @@
|
|
|
2047
2116
|
const restProducts = [...apps].filter(({
|
|
2048
2117
|
name
|
|
2049
2118
|
}) => name !== "Home" && name !== "Accounts").sort((a, b) => a.name.localeCompare(b.name));
|
|
2119
|
+
const {
|
|
2120
|
+
containerRef: menuRef,
|
|
2121
|
+
handleKeyDown
|
|
2122
|
+
} = useFocusTrap({
|
|
2123
|
+
active: visible,
|
|
2124
|
+
onClose: toggle
|
|
2125
|
+
});
|
|
2050
2126
|
const listItemProps = {
|
|
2051
2127
|
...fontFamily,
|
|
2052
2128
|
alignItems: "center",
|
|
@@ -2063,12 +2139,16 @@
|
|
|
2063
2139
|
className: styles$8.popup,
|
|
2064
2140
|
isVisible: visible
|
|
2065
2141
|
}, /*#__PURE__*/React.createElement("menu", {
|
|
2142
|
+
ref: menuRef,
|
|
2066
2143
|
"data-pendo": "app-switcher",
|
|
2144
|
+
onKeyDown: handleKeyDown,
|
|
2067
2145
|
style: {
|
|
2068
2146
|
margin: 0,
|
|
2069
2147
|
padding: 0
|
|
2070
2148
|
}
|
|
2071
2149
|
}, homeProduct && (/*#__PURE__*/React.createElement(HoverableListItem, {
|
|
2150
|
+
"aria-current": appName === homeProduct.name ? "page" : undefined,
|
|
2151
|
+
"aria-label": homeProduct.name,
|
|
2072
2152
|
component: "a",
|
|
2073
2153
|
"data-pendo": `app-switcher--change-app--${homeProduct.name}`,
|
|
2074
2154
|
"data-turbolinks": false,
|
|
@@ -2093,6 +2173,8 @@
|
|
|
2093
2173
|
const isFirst = i === 0;
|
|
2094
2174
|
const isLast = i === restProducts.length - 1;
|
|
2095
2175
|
return /*#__PURE__*/React.createElement(HoverableListItem, {
|
|
2176
|
+
"aria-current": name === appName ? "page" : undefined,
|
|
2177
|
+
"aria-label": name,
|
|
2096
2178
|
component: "a",
|
|
2097
2179
|
"data-pendo": `app-switcher--change-app--${name}`,
|
|
2098
2180
|
"data-turbolinks": false,
|
|
@@ -2116,6 +2198,8 @@
|
|
|
2116
2198
|
size: 18
|
|
2117
2199
|
}));
|
|
2118
2200
|
}), accountsProduct && (/*#__PURE__*/React.createElement(HoverableListItem, {
|
|
2201
|
+
"aria-current": appName === accountsProduct.name ? "page" : undefined,
|
|
2202
|
+
"aria-label": accountsProduct.name,
|
|
2119
2203
|
component: "a",
|
|
2120
2204
|
"data-pendo": `app-switcher--change-app--${accountsProduct.name}`,
|
|
2121
2205
|
"data-turbolinks": false,
|
|
@@ -2615,6 +2699,13 @@
|
|
|
2615
2699
|
const restProducts = [...props.apps].filter(({
|
|
2616
2700
|
name
|
|
2617
2701
|
}) => name !== "Home" && name !== "Accounts");
|
|
2702
|
+
const {
|
|
2703
|
+
containerRef: appMenuRef,
|
|
2704
|
+
handleKeyDown: handleAppMenuKeyDown
|
|
2705
|
+
} = useFocusTrap({
|
|
2706
|
+
active: productSwitcherVisible,
|
|
2707
|
+
onClose: () => setProductSwitcherVisible(false)
|
|
2708
|
+
});
|
|
2618
2709
|
React.useEffect(() => {
|
|
2619
2710
|
setRoutesMenuVisible(false);
|
|
2620
2711
|
}, [activeRoute && ((_b = (_a = activeRoute[0]) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b.href)]);
|
|
@@ -2623,6 +2714,9 @@
|
|
|
2623
2714
|
...props.style
|
|
2624
2715
|
}
|
|
2625
2716
|
}, /*#__PURE__*/React.createElement("button", {
|
|
2717
|
+
"aria-expanded": productSwitcherVisible,
|
|
2718
|
+
"aria-haspopup": "true",
|
|
2719
|
+
"aria-label": `App switcher: ${props.appName}`,
|
|
2626
2720
|
type: "button",
|
|
2627
2721
|
className: styles$3["appmenu-button"],
|
|
2628
2722
|
onClick: () => {
|
|
@@ -2638,6 +2732,7 @@
|
|
|
2638
2732
|
}, props.routes)), /*#__PURE__*/React.createElement(ClientStorage, {
|
|
2639
2733
|
item: "Topbar:Small:Menus:Products:visibility",
|
|
2640
2734
|
render: () => (/*#__PURE__*/React.createElement("div", {
|
|
2735
|
+
ref: appMenuRef,
|
|
2641
2736
|
className: styles$3["appmenu-layer"],
|
|
2642
2737
|
style: {
|
|
2643
2738
|
...(productSwitcherVisible && {
|
|
@@ -2646,7 +2741,8 @@
|
|
|
2646
2741
|
},
|
|
2647
2742
|
onClick: () => {
|
|
2648
2743
|
setProductSwitcherVisible(visible => !visible);
|
|
2649
|
-
}
|
|
2744
|
+
},
|
|
2745
|
+
onKeyDown: handleAppMenuKeyDown
|
|
2650
2746
|
}, /*#__PURE__*/React.createElement(CloseMenuButton, null), /*#__PURE__*/React.createElement("div", {
|
|
2651
2747
|
onClick: e => {
|
|
2652
2748
|
const {
|
|
@@ -2658,6 +2754,8 @@
|
|
|
2658
2754
|
},
|
|
2659
2755
|
className: styles$3["appmenu-container"]
|
|
2660
2756
|
}, /*#__PURE__*/React.createElement("a", {
|
|
2757
|
+
"aria-current": props.appName === (homeProduct === null || homeProduct === void 0 ? void 0 : homeProduct.name) ? "page" : undefined,
|
|
2758
|
+
"aria-label": homeProduct ? homeProduct.name : props.appName,
|
|
2661
2759
|
"data-pendo": `app-switcher--change-app--${homeProduct === null || homeProduct === void 0 ? void 0 : homeProduct.name}`,
|
|
2662
2760
|
"data-turbolinks": false,
|
|
2663
2761
|
className: classnames(styles$3["appmenu-list-a"], styles$3["appmenu-list-a--top"]),
|
|
@@ -2675,6 +2773,8 @@
|
|
|
2675
2773
|
key: name,
|
|
2676
2774
|
className: styles$3["appmenu-list-li"]
|
|
2677
2775
|
}, /*#__PURE__*/React.createElement("a", {
|
|
2776
|
+
"aria-current": name === props.appName ? "page" : undefined,
|
|
2777
|
+
"aria-label": name,
|
|
2678
2778
|
"data-pendo": `app-switcher--change-app--${name}`,
|
|
2679
2779
|
"data-turbolinks": false,
|
|
2680
2780
|
className: styles$3["appmenu-list-a"],
|