@planningcenter/topbar 12.4.0-rc.0 → 12.4.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.css +78 -12
- package/dist/topbar.css.map +1 -1
- package/dist/topbar.js +1082 -853
- package/dist/topbar.js.map +1 -1
- package/dist/topbar.modern.js +1082 -853
- package/dist/topbar.modern.js.map +1 -1
- package/dist/topbar.module.js +1082 -853
- package/dist/topbar.module.js.map +1 -1
- package/dist/topbar.umd.js +1082 -853
- package/dist/topbar.umd.js.map +1 -1
- package/package.json +2 -3
package/dist/topbar.modern.js
CHANGED
|
@@ -17,7 +17,7 @@ import { useSearchBox, useHits, InstantSearch, Configure } from 'react-instantse
|
|
|
17
17
|
import { TransitionGroup, Transition } from 'react-transition-group';
|
|
18
18
|
import datetimeFmt from '@planningcenter/datetime-fmt';
|
|
19
19
|
|
|
20
|
-
var styles$
|
|
20
|
+
var styles$m = {"accessibility-toolbar":"accessibility-toolbar-jgjUG"};
|
|
21
21
|
|
|
22
22
|
const AccessibilityToolbar = ({
|
|
23
23
|
children
|
|
@@ -29,7 +29,7 @@ const AccessibilityToolbar = ({
|
|
|
29
29
|
(_a = document.getElementById("main-content")) === null || _a === void 0 ? void 0 : _a.focus();
|
|
30
30
|
};
|
|
31
31
|
return /*#__PURE__*/React__default.createElement("div", {
|
|
32
|
-
className: styles$
|
|
32
|
+
className: styles$m["accessibility-toolbar"]
|
|
33
33
|
}, /*#__PURE__*/React__default.createElement(Link, {
|
|
34
34
|
href: "#main-content",
|
|
35
35
|
kind: "primary",
|
|
@@ -348,11 +348,11 @@ const X = ({
|
|
|
348
348
|
fill: fill
|
|
349
349
|
})));
|
|
350
350
|
|
|
351
|
-
var styles$
|
|
351
|
+
var styles$l = {"announcement":"announcement-pI5tM","announcement--platform":"announcement--platform-EcUn2","announcement--promo":"announcement--promo-FarUk","announcement-icon":"announcement-icon-Ov39-","announcement-content":"announcement-content-q2ad6","dismiss":"dismiss-wUAoU","dismiss--promo":"dismiss--promo-7zvO4"};
|
|
352
352
|
|
|
353
353
|
function AnnouncementIcon() {
|
|
354
354
|
return /*#__PURE__*/React__default.createElement("svg", {
|
|
355
|
-
className: styles$
|
|
355
|
+
className: styles$l["announcement-icon"],
|
|
356
356
|
width: "16",
|
|
357
357
|
height: "16",
|
|
358
358
|
viewBox: "0 0 16 16",
|
|
@@ -477,7 +477,7 @@ let StyledAnnouncement$1 = class StyledAnnouncement extends React__default.Compo
|
|
|
477
477
|
} = this.props;
|
|
478
478
|
const isPromo = variant === "promo";
|
|
479
479
|
return /*#__PURE__*/React__default.createElement("div", {
|
|
480
|
-
className: classnames(styles$
|
|
480
|
+
className: classnames(styles$l["announcement"], styles$l[`announcement--${variant !== null && variant !== void 0 ? variant : "platform"}`]),
|
|
481
481
|
"data-announcement-variant": variant !== null && variant !== void 0 ? variant : "platform",
|
|
482
482
|
style: isPromo ? {
|
|
483
483
|
backgroundImage: promoBackgroundUrl,
|
|
@@ -486,7 +486,7 @@ let StyledAnnouncement$1 = class StyledAnnouncement extends React__default.Compo
|
|
|
486
486
|
backgroundSize: "cover"
|
|
487
487
|
} : undefined
|
|
488
488
|
}, !isPromo && /*#__PURE__*/React__default.createElement(AnnouncementIcon, null), /*#__PURE__*/React__default.createElement("div", {
|
|
489
|
-
className: styles$
|
|
489
|
+
className: styles$l["announcement-content"],
|
|
490
490
|
style: style,
|
|
491
491
|
...platformProps
|
|
492
492
|
}));
|
|
@@ -535,7 +535,7 @@ function StyledDismissButton$1({
|
|
|
535
535
|
const isPromo = variant === "promo";
|
|
536
536
|
return /*#__PURE__*/React__default.createElement("button", {
|
|
537
537
|
type: "button",
|
|
538
|
-
className: classnames(styles$
|
|
538
|
+
className: classnames(styles$l.dismiss, isPromo && styles$l["dismiss--promo"]),
|
|
539
539
|
...props
|
|
540
540
|
}, /*#__PURE__*/React__default.createElement(X, {
|
|
541
541
|
fill: isPromo ? "currentColor" : "white",
|
|
@@ -615,9 +615,9 @@ class StyledAnnouncement extends React__default.Component {
|
|
|
615
615
|
...platformProps
|
|
616
616
|
} = this.props;
|
|
617
617
|
return /*#__PURE__*/React__default.createElement("div", {
|
|
618
|
-
className: styles$
|
|
618
|
+
className: styles$l["announcement"]
|
|
619
619
|
}, /*#__PURE__*/React__default.createElement(AnnouncementIcon, null), /*#__PURE__*/React__default.createElement("div", {
|
|
620
|
-
className: styles$
|
|
620
|
+
className: styles$l["announcement-content"],
|
|
621
621
|
style: style,
|
|
622
622
|
...platformProps
|
|
623
623
|
}));
|
|
@@ -628,7 +628,7 @@ function StyledDismissButton({
|
|
|
628
628
|
}) {
|
|
629
629
|
return /*#__PURE__*/React__default.createElement("button", {
|
|
630
630
|
type: "button",
|
|
631
|
-
className: styles$
|
|
631
|
+
className: styles$l.dismiss,
|
|
632
632
|
...props
|
|
633
633
|
}, /*#__PURE__*/React__default.createElement(X, {
|
|
634
634
|
fill: "white",
|
|
@@ -1750,7 +1750,7 @@ function MonoAppIcon({
|
|
|
1750
1750
|
})) : (/*#__PURE__*/React__default.createElement("div", null, "appName not supported")));
|
|
1751
1751
|
}
|
|
1752
1752
|
|
|
1753
|
-
var styles$
|
|
1753
|
+
var styles$k = {"icon-slot":"icon-slot-m3ZIk","name":"name-UNqbW"};
|
|
1754
1754
|
|
|
1755
1755
|
const AppRow = ({
|
|
1756
1756
|
appName,
|
|
@@ -1759,24 +1759,24 @@ const AppRow = ({
|
|
|
1759
1759
|
}) => {
|
|
1760
1760
|
if (appName === "Accounts") {
|
|
1761
1761
|
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("span", {
|
|
1762
|
-
className: styles$
|
|
1762
|
+
className: styles$k["icon-slot"]
|
|
1763
1763
|
}, /*#__PURE__*/React__default.createElement(MonoAppIcon, {
|
|
1764
1764
|
appName: "Accounts",
|
|
1765
1765
|
colors: colors,
|
|
1766
1766
|
size: iconSize
|
|
1767
1767
|
})), /*#__PURE__*/React__default.createElement("span", {
|
|
1768
|
-
className: styles$
|
|
1768
|
+
className: styles$k.name
|
|
1769
1769
|
}, "Account settings"));
|
|
1770
1770
|
}
|
|
1771
1771
|
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(ColorAppIcon, {
|
|
1772
1772
|
appName: appName.replace(/[\s-]/, ""),
|
|
1773
1773
|
size: iconSize
|
|
1774
1774
|
}), /*#__PURE__*/React__default.createElement("span", {
|
|
1775
|
-
className: styles$
|
|
1775
|
+
className: styles$k.name
|
|
1776
1776
|
}, appName));
|
|
1777
1777
|
};
|
|
1778
1778
|
|
|
1779
|
-
var styles$
|
|
1779
|
+
var styles$j = {"button":"button-XMbeQ","label":"label-884of","chevron":"chevron-RRg9k"};
|
|
1780
1780
|
|
|
1781
1781
|
const AppsButton = /*#__PURE__*/React__default.forwardRef(({
|
|
1782
1782
|
appName,
|
|
@@ -1788,10 +1788,10 @@ const AppsButton = /*#__PURE__*/React__default.forwardRef(({
|
|
|
1788
1788
|
...restProps,
|
|
1789
1789
|
ref: ref,
|
|
1790
1790
|
"aria-label": `App switcher: ${appName}`,
|
|
1791
|
-
className: styles$
|
|
1791
|
+
className: styles$j.button,
|
|
1792
1792
|
kind: "neutral-inline",
|
|
1793
1793
|
label: /*#__PURE__*/React__default.createElement("span", {
|
|
1794
|
-
className: styles$
|
|
1794
|
+
className: styles$j.label
|
|
1795
1795
|
}, isAccounts ? "Account settings" : appName),
|
|
1796
1796
|
prefix: isAccounts ? (/*#__PURE__*/React__default.createElement(MonoAppIcon, {
|
|
1797
1797
|
appName: "Accounts",
|
|
@@ -1802,13 +1802,13 @@ const AppsButton = /*#__PURE__*/React__default.forwardRef(({
|
|
|
1802
1802
|
size: 22
|
|
1803
1803
|
})),
|
|
1804
1804
|
suffix: /*#__PURE__*/React__default.createElement(DisclosureChevronIcon, {
|
|
1805
|
-
className: `${styles$
|
|
1805
|
+
className: `${styles$j.chevron} symbol`
|
|
1806
1806
|
})
|
|
1807
1807
|
});
|
|
1808
1808
|
});
|
|
1809
1809
|
AppsButton.displayName = "AppsButton";
|
|
1810
1810
|
|
|
1811
|
-
var styles$
|
|
1811
|
+
var styles$i = {"nav":"nav-y-6ML","popover-content":"popover-content-jOAAY","list":"list-jUxvp","link":"link-LMp2h","current":"current-4U4VL","accounts-item":"accounts-item-erJW0","accounts-link":"accounts-link-4jwPW"};
|
|
1812
1812
|
|
|
1813
1813
|
const AppsMenu = ({
|
|
1814
1814
|
appName,
|
|
@@ -1829,8 +1829,8 @@ const AppsMenu = ({
|
|
|
1829
1829
|
}, /*#__PURE__*/React__default.createElement("a", {
|
|
1830
1830
|
"aria-current": isCurrent ? "page" : undefined,
|
|
1831
1831
|
"aria-label": name,
|
|
1832
|
-
className: classnames(styles$
|
|
1833
|
-
[styles$
|
|
1832
|
+
className: classnames(styles$i.link, {
|
|
1833
|
+
[styles$i.current]: isCurrent
|
|
1834
1834
|
}),
|
|
1835
1835
|
"data-pendo": `app-switcher--change-app--${name}`,
|
|
1836
1836
|
"data-turbolinks": false,
|
|
@@ -1846,22 +1846,22 @@ const AppsMenu = ({
|
|
|
1846
1846
|
colors: colors
|
|
1847
1847
|
})), /*#__PURE__*/React__default.createElement(PopoverContent, {
|
|
1848
1848
|
"aria-label": "Switch app",
|
|
1849
|
-
className: styles$
|
|
1849
|
+
className: styles$i["popover-content"],
|
|
1850
1850
|
placement: "bottom start"
|
|
1851
1851
|
}, /*#__PURE__*/React__default.createElement("nav", {
|
|
1852
|
-
className: styles$
|
|
1852
|
+
className: styles$i.nav,
|
|
1853
1853
|
"data-pendo": "app-switcher"
|
|
1854
1854
|
}, /*#__PURE__*/React__default.createElement("ul", {
|
|
1855
|
-
className: styles$
|
|
1855
|
+
className: styles$i.list,
|
|
1856
1856
|
role: "list"
|
|
1857
1857
|
}, homeProduct && renderProductLink(homeProduct.name), restProducts.map(({
|
|
1858
1858
|
name
|
|
1859
1859
|
}) => renderProductLink(name)), accountsProduct && (/*#__PURE__*/React__default.createElement("li", {
|
|
1860
|
-
className: styles$
|
|
1860
|
+
className: styles$i["accounts-item"]
|
|
1861
1861
|
}, /*#__PURE__*/React__default.createElement("a", {
|
|
1862
1862
|
"aria-current": appName === accountsProduct.name ? "page" : undefined,
|
|
1863
1863
|
"aria-label": "Account settings",
|
|
1864
|
-
className: classnames(styles$
|
|
1864
|
+
className: classnames(styles$i.link, styles$i["accounts-link"]),
|
|
1865
1865
|
"data-pendo": "app-switcher--change-app--Accounts",
|
|
1866
1866
|
"data-turbolinks": false,
|
|
1867
1867
|
href: pcoUrl$1(env)("accounts") + getAppsSwitchProductPath(accountsProduct.name)
|
|
@@ -2069,7 +2069,7 @@ function MonoAppName({
|
|
|
2069
2069
|
})) : (/*#__PURE__*/React__default.createElement("div", null, "appName not supported"));
|
|
2070
2070
|
}
|
|
2071
2071
|
|
|
2072
|
-
var styles$
|
|
2072
|
+
var styles$h = {"unbutton":"unbutton-lEbTR"};
|
|
2073
2073
|
|
|
2074
2074
|
function Unbutton({
|
|
2075
2075
|
className,
|
|
@@ -2078,7 +2078,7 @@ function Unbutton({
|
|
|
2078
2078
|
...nativeProps
|
|
2079
2079
|
}) {
|
|
2080
2080
|
return /*#__PURE__*/React__default.createElement("button", {
|
|
2081
|
-
className: classnames(styles$
|
|
2081
|
+
className: classnames(styles$h.unbutton, className),
|
|
2082
2082
|
style: {
|
|
2083
2083
|
...style
|
|
2084
2084
|
},
|
|
@@ -2087,7 +2087,7 @@ function Unbutton({
|
|
|
2087
2087
|
});
|
|
2088
2088
|
}
|
|
2089
2089
|
|
|
2090
|
-
var styles$
|
|
2090
|
+
var styles$g = {"button":"button-wsipc","button-accounts":"button-accounts-zQmBM","chevron":"chevron-obyBF","is-expanded":"is-expanded-PoCQ9"};
|
|
2091
2091
|
|
|
2092
2092
|
const LegacyAppsButton = ({
|
|
2093
2093
|
appName,
|
|
@@ -2102,8 +2102,8 @@ const LegacyAppsButton = ({
|
|
|
2102
2102
|
"aria-haspopup": "true",
|
|
2103
2103
|
"aria-label": `App switcher: ${appName}`,
|
|
2104
2104
|
onClick: onClick,
|
|
2105
|
-
className: classnames(styles$
|
|
2106
|
-
[styles$
|
|
2105
|
+
className: classnames(styles$g.button, {
|
|
2106
|
+
[styles$g["is-expanded"]]: expanded
|
|
2107
2107
|
}),
|
|
2108
2108
|
type: "button",
|
|
2109
2109
|
style: style,
|
|
@@ -2112,7 +2112,7 @@ const LegacyAppsButton = ({
|
|
|
2112
2112
|
appName: "Accounts",
|
|
2113
2113
|
colors: colors
|
|
2114
2114
|
}), /*#__PURE__*/React__default.createElement("div", {
|
|
2115
|
-
className: styles$
|
|
2115
|
+
className: styles$g["button-accounts"]
|
|
2116
2116
|
}, "Account settings"))) : (/*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(ColorAppIcon, {
|
|
2117
2117
|
appName: appName.replace(/[\s-]/, "")
|
|
2118
2118
|
}), /*#__PURE__*/React__default.createElement(MonoAppName, {
|
|
@@ -2120,7 +2120,7 @@ const LegacyAppsButton = ({
|
|
|
2120
2120
|
key: "mono-app-name",
|
|
2121
2121
|
size: 24
|
|
2122
2122
|
}))), /*#__PURE__*/React__default.createElement(DisclosureChevronIcon, {
|
|
2123
|
-
className: styles$
|
|
2123
|
+
className: styles$g.chevron,
|
|
2124
2124
|
key: "disclosure-chevron-icon"
|
|
2125
2125
|
}));
|
|
2126
2126
|
};
|
|
@@ -2253,7 +2253,7 @@ class Outsider extends React__default.Component {
|
|
|
2253
2253
|
}
|
|
2254
2254
|
}
|
|
2255
2255
|
|
|
2256
|
-
var styles$
|
|
2256
|
+
var styles$f = {"popup-root":"popup-root-SeL5u","popup":"popup-nQXYW","is-visible":"is-visible-3DAUP"};
|
|
2257
2257
|
|
|
2258
2258
|
const Popup = ({
|
|
2259
2259
|
className,
|
|
@@ -2261,8 +2261,8 @@ const Popup = ({
|
|
|
2261
2261
|
isVisible,
|
|
2262
2262
|
...nativeProps
|
|
2263
2263
|
}) => (/*#__PURE__*/React__default.createElement(C, {
|
|
2264
|
-
className: classnames(styles$
|
|
2265
|
-
[styles$
|
|
2264
|
+
className: classnames(styles$f.popup, {
|
|
2265
|
+
[styles$f["is-visible"]]: isVisible
|
|
2266
2266
|
}, className),
|
|
2267
2267
|
...nativeProps
|
|
2268
2268
|
}));
|
|
@@ -2270,11 +2270,11 @@ const Popup = ({
|
|
|
2270
2270
|
const PopupRoot = ({
|
|
2271
2271
|
...nativeProps
|
|
2272
2272
|
}) => (/*#__PURE__*/React__default.createElement("div", {
|
|
2273
|
-
className: styles$
|
|
2273
|
+
className: styles$f["popup-root"],
|
|
2274
2274
|
...nativeProps
|
|
2275
2275
|
}));
|
|
2276
2276
|
|
|
2277
|
-
var styles$
|
|
2277
|
+
var styles$e = {"popup":"popup-bVhSa"};
|
|
2278
2278
|
|
|
2279
2279
|
const LegacyAppsMenu = ({
|
|
2280
2280
|
appName,
|
|
@@ -2312,7 +2312,7 @@ const LegacyAppsMenu = ({
|
|
|
2312
2312
|
cleanup: !visible,
|
|
2313
2313
|
component: Outsider,
|
|
2314
2314
|
onOutsideClick: toggle,
|
|
2315
|
-
className: styles$
|
|
2315
|
+
className: styles$e.popup,
|
|
2316
2316
|
isVisible: visible
|
|
2317
2317
|
}, /*#__PURE__*/React__default.createElement("menu", {
|
|
2318
2318
|
ref: menuRef,
|
|
@@ -3225,27 +3225,34 @@ const useUpdateStore = () => useCallback(updates => {
|
|
|
3225
3225
|
const isMac = () => /Mac|iPhone|iPad|iPod/.test(navigator.platform);
|
|
3226
3226
|
const hasCommandKey = event => isMac() ? event.metaKey : event.ctrlKey;
|
|
3227
3227
|
const commandKeyLabel = () => isMac() ? "⌘" : "Ctrl+";
|
|
3228
|
+
const isSearchShortcut = event => event.key.toLowerCase() === "k" && !event.altKey && !event.shiftKey && hasCommandKey(event);
|
|
3229
|
+
// Products opened their own search with `/` before this one replaced it.
|
|
3230
|
+
const isLegacySearchShortcut = event => event.key === "/" && !event.altKey && !event.ctrlKey && !event.metaKey && !isTypingIn(event.target);
|
|
3231
|
+
const isCopyShortcut = event => event.key.toLowerCase() === "c" && !event.altKey && !event.shiftKey && hasCommandKey(event);
|
|
3232
|
+
const isTypingIn = target => target instanceof Element && target.closest('input, select, textarea, [contenteditable]:not([contenteditable="false"])') !== null;
|
|
3233
|
+
const isTypingKey = event => event.key.length === 1 && event.key !== " " && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
3234
|
+
const verticalStep = key => ({
|
|
3235
|
+
ArrowDown: 1,
|
|
3236
|
+
ArrowUp: -1
|
|
3237
|
+
})[key];
|
|
3238
|
+
const horizontalStep = key => ({
|
|
3239
|
+
ArrowLeft: -1,
|
|
3240
|
+
ArrowRight: 1
|
|
3241
|
+
})[key];
|
|
3228
3242
|
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
} catch {
|
|
3241
|
-
// Storage can be full or blocked; search still works without it.
|
|
3242
|
-
}
|
|
3243
|
+
function SearchButton({
|
|
3244
|
+
onClick
|
|
3245
|
+
}) {
|
|
3246
|
+
return /*#__PURE__*/React__default.createElement("button", {
|
|
3247
|
+
"aria-keyshortcuts": `${isMac() ? "Meta+K" : "Control+K"} /`,
|
|
3248
|
+
"aria-label": "Search",
|
|
3249
|
+
onClick: onClick,
|
|
3250
|
+
type: "button"
|
|
3251
|
+
}, /*#__PURE__*/React__default.createElement(Spyglass, {
|
|
3252
|
+
fill: "currentColor"
|
|
3253
|
+
}));
|
|
3243
3254
|
}
|
|
3244
3255
|
|
|
3245
|
-
var styles$c = {"visuallyHidden":"visuallyHidden-C28ju"};
|
|
3246
|
-
|
|
3247
|
-
var styles$b = {"tab":"tab-lQLDx"};
|
|
3248
|
-
|
|
3249
3256
|
// useId isn't available on React 17, which our peer range still supports.
|
|
3250
3257
|
let instanceCount$1 = 0;
|
|
3251
3258
|
function useInstanceId(prefix) {
|
|
@@ -3253,58 +3260,237 @@ function useInstanceId(prefix) {
|
|
|
3253
3260
|
return id;
|
|
3254
3261
|
}
|
|
3255
3262
|
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3263
|
+
// The versions home-mobile pins, so web and mobile read the same response
|
|
3264
|
+
// shapes. Some endpoints only exist from a version on: Registrations' me,
|
|
3265
|
+
// product_search_results, and pcrl_lookups need 2025-05-01.
|
|
3266
|
+
const API_VERSIONS = {
|
|
3267
|
+
calendar: "2022-07-07",
|
|
3268
|
+
"check-ins": "2025-05-28",
|
|
3269
|
+
giving: "2019-10-18",
|
|
3270
|
+
groups: "2023-07-10",
|
|
3271
|
+
home: "2018-08-01",
|
|
3272
|
+
people: "2023-03-21",
|
|
3273
|
+
publishing: "2024-03-25",
|
|
3274
|
+
registrations: "2025-05-01",
|
|
3275
|
+
services: "2018-11-01"
|
|
3276
|
+
};
|
|
3277
|
+
// For a `/~api/{product}/v2/...` URL, the header that pins its product's
|
|
3278
|
+
// version.
|
|
3279
|
+
function apiVersionHeaders(url) {
|
|
3280
|
+
const [,, product] = new URL(url).pathname.split("/");
|
|
3281
|
+
const version = API_VERSIONS[product];
|
|
3282
|
+
if (version) {
|
|
3283
|
+
return {
|
|
3284
|
+
"X-PCO-API-Version": version
|
|
3285
|
+
};
|
|
3286
|
+
} else {
|
|
3287
|
+
return {};
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
const CACHE_TTL_MS = 60000;
|
|
3292
|
+
const MAX_ENTRIES$1 = 50;
|
|
3293
|
+
const cache = new Map();
|
|
3294
|
+
function cachedRequest(url) {
|
|
3295
|
+
const cached = cache.get(url);
|
|
3296
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
3297
|
+
// Re-inserting keeps the Map in recently-used order for eviction.
|
|
3298
|
+
cache.delete(url);
|
|
3299
|
+
cache.set(url, cached);
|
|
3300
|
+
return cached.promise;
|
|
3301
|
+
} else {
|
|
3302
|
+
return fetchIntoCache(url);
|
|
3303
|
+
}
|
|
3304
|
+
}
|
|
3305
|
+
function fetchIntoCache(url) {
|
|
3306
|
+
const promise = apiRequest(defaultFetch, url, {
|
|
3307
|
+
headers: apiVersionHeaders(url)
|
|
3308
|
+
}).then(({
|
|
3309
|
+
json,
|
|
3310
|
+
ok
|
|
3311
|
+
}) => {
|
|
3312
|
+
if (!ok || json === null) throw new Error(`Request failed: ${url}`);
|
|
3313
|
+
return json;
|
|
3314
|
+
});
|
|
3315
|
+
// In-flight entries never expire, so every caller shares the one fetch.
|
|
3316
|
+
const entry = {
|
|
3317
|
+
expiresAt: Infinity,
|
|
3318
|
+
promise
|
|
3319
|
+
};
|
|
3320
|
+
cache.set(url, entry);
|
|
3321
|
+
promise.then(() => entry.expiresAt = Date.now() + CACHE_TTL_MS, () => cache.get(url) === entry && cache.delete(url));
|
|
3322
|
+
if (cache.size > MAX_ENTRIES$1) cache.delete(cache.keys().next().value);
|
|
3323
|
+
return promise;
|
|
3324
|
+
}
|
|
3325
|
+
function useCachedRequest(url) {
|
|
3326
|
+
const [settled, setSettled] = React__default.useState();
|
|
3327
|
+
React__default.useEffect(() => {
|
|
3328
|
+
if (url) {
|
|
3329
|
+
let current = true;
|
|
3330
|
+
cachedRequest(url).then(data => current && setSettled({
|
|
3331
|
+
data,
|
|
3332
|
+
url
|
|
3333
|
+
}), () => current && setSettled({
|
|
3334
|
+
failed: true,
|
|
3335
|
+
url
|
|
3336
|
+
}));
|
|
3337
|
+
return () => {
|
|
3338
|
+
current = false;
|
|
3339
|
+
};
|
|
3278
3340
|
} else {
|
|
3279
|
-
|
|
3341
|
+
return undefined;
|
|
3342
|
+
}
|
|
3343
|
+
}, [url]);
|
|
3344
|
+
if (!url) {
|
|
3345
|
+
return {
|
|
3346
|
+
status: "idle"
|
|
3347
|
+
};
|
|
3348
|
+
} else if ((settled === null || settled === void 0 ? void 0 : settled.url) !== url) {
|
|
3349
|
+
return {
|
|
3350
|
+
data: settled && "data" in settled ? settled.data : undefined,
|
|
3351
|
+
status: "loading"
|
|
3352
|
+
};
|
|
3353
|
+
} else if ("data" in settled) {
|
|
3354
|
+
return {
|
|
3355
|
+
data: settled.data,
|
|
3356
|
+
status: "done"
|
|
3357
|
+
};
|
|
3358
|
+
} else {
|
|
3359
|
+
return {
|
|
3360
|
+
status: "error"
|
|
3361
|
+
};
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
// One request per URL, for callers that fan out to a varying number of
|
|
3365
|
+
// endpoints. Unlike `useCachedRequest`, a URL that hasn't settled yet has no
|
|
3366
|
+
// earlier data to fall back on.
|
|
3367
|
+
function useCachedRequests(urls) {
|
|
3368
|
+
const [settled, setSettled] = React__default.useState({});
|
|
3369
|
+
// Keyed by value because each settle re-renders the caller, and a caller
|
|
3370
|
+
// that builds a fresh `urls` array on render would otherwise loop forever.
|
|
3371
|
+
const key = urls.join("\n");
|
|
3372
|
+
React__default.useEffect(() => {
|
|
3373
|
+
let current = true;
|
|
3374
|
+
const settle = result => current && setSettled(prev => ({
|
|
3375
|
+
...prev,
|
|
3376
|
+
[result.url]: result
|
|
3377
|
+
}));
|
|
3378
|
+
for (const url of key.split("\n").filter(Boolean)) {
|
|
3379
|
+
cachedRequest(url).then(data => settle({
|
|
3380
|
+
data,
|
|
3381
|
+
url
|
|
3382
|
+
}), () => settle({
|
|
3383
|
+
failed: true,
|
|
3384
|
+
url
|
|
3385
|
+
}));
|
|
3280
3386
|
}
|
|
3387
|
+
return () => {
|
|
3388
|
+
current = false;
|
|
3389
|
+
};
|
|
3390
|
+
}, [key]);
|
|
3391
|
+
return urls.map(url => {
|
|
3392
|
+
const result = settled[url];
|
|
3393
|
+
if (!result) {
|
|
3394
|
+
return {
|
|
3395
|
+
status: "loading"
|
|
3396
|
+
};
|
|
3397
|
+
} else if ("data" in result) {
|
|
3398
|
+
return {
|
|
3399
|
+
data: result.data,
|
|
3400
|
+
status: "done"
|
|
3401
|
+
};
|
|
3402
|
+
} else {
|
|
3403
|
+
return {
|
|
3404
|
+
status: "error"
|
|
3405
|
+
};
|
|
3406
|
+
}
|
|
3407
|
+
});
|
|
3408
|
+
}
|
|
3409
|
+
|
|
3410
|
+
function productSearchUrl(product, query) {
|
|
3411
|
+
const params = new URLSearchParams({
|
|
3412
|
+
per_page: "100",
|
|
3413
|
+
"where[term]": query
|
|
3414
|
+
});
|
|
3415
|
+
return `${window.location.origin}/~api/${product}/v2/product_search_results?${params}`;
|
|
3416
|
+
}
|
|
3417
|
+
function groupByType({
|
|
3418
|
+
data,
|
|
3419
|
+
meta
|
|
3420
|
+
}) {
|
|
3421
|
+
var _a, _b;
|
|
3422
|
+
const typeOrder = (_a = meta === null || meta === void 0 ? void 0 : meta.type_order) !== null && _a !== void 0 ? _a : [];
|
|
3423
|
+
const groups = new Map();
|
|
3424
|
+
for (const result of data) {
|
|
3425
|
+
const {
|
|
3426
|
+
type
|
|
3427
|
+
} = result.attributes;
|
|
3428
|
+
if (!groups.has(type)) groups.set(type, {
|
|
3429
|
+
results: [],
|
|
3430
|
+
type
|
|
3431
|
+
});
|
|
3432
|
+
(_b = groups.get(type)) === null || _b === void 0 ? void 0 : _b.results.push(result);
|
|
3433
|
+
}
|
|
3434
|
+
const rank = type => {
|
|
3435
|
+
const index = typeOrder.indexOf(type);
|
|
3436
|
+
return index === -1 ? typeOrder.length : index;
|
|
3281
3437
|
};
|
|
3282
|
-
return
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3438
|
+
return [...groups.values()].sort((a, b) => rank(a.type) - rank(b.type));
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3441
|
+
function useDebouncedValue(value, delay) {
|
|
3442
|
+
const [debounced, setDebounced] = React__default.useState(value);
|
|
3443
|
+
React__default.useEffect(() => {
|
|
3444
|
+
const timeout = setTimeout(() => setDebounced(value), delay);
|
|
3445
|
+
return () => clearTimeout(timeout);
|
|
3446
|
+
}, [value, delay]);
|
|
3447
|
+
return debounced;
|
|
3448
|
+
}
|
|
3449
|
+
|
|
3450
|
+
const SEARCH_DEBOUNCE_MS = 300;
|
|
3451
|
+
function useProductSearch(product, term) {
|
|
3452
|
+
const query = term.trim();
|
|
3453
|
+
const debouncedQuery = useDebouncedValue(query, SEARCH_DEBOUNCE_MS);
|
|
3454
|
+
const request = useCachedRequest(debouncedQuery ? productSearchUrl(product, debouncedQuery) : null);
|
|
3455
|
+
const response = "data" in request ? request.data : undefined;
|
|
3456
|
+
const settledProduct = useSettledProduct(product, request.status);
|
|
3457
|
+
// While a request loads, the previous response stays on screen, but only
|
|
3458
|
+
// if it was for this product: People results must never show under the
|
|
3459
|
+
// Services tab.
|
|
3460
|
+
const visibleResponse = request.status === "done" || settledProduct === product ? response : undefined;
|
|
3461
|
+
const groups = React__default.useMemo(() => visibleResponse && groupByType(visibleResponse), [visibleResponse]);
|
|
3462
|
+
if (!query) {
|
|
3463
|
+
return {
|
|
3464
|
+
status: "idle"
|
|
3465
|
+
};
|
|
3466
|
+
} else if (query !== debouncedQuery) {
|
|
3467
|
+
return {
|
|
3468
|
+
groups,
|
|
3469
|
+
status: "loading"
|
|
3470
|
+
};
|
|
3471
|
+
} else if (request.status === "done") {
|
|
3472
|
+
return {
|
|
3473
|
+
groups: groups !== null && groups !== void 0 ? groups : [],
|
|
3474
|
+
status: "done"
|
|
3475
|
+
};
|
|
3476
|
+
} else if (request.status === "error") {
|
|
3477
|
+
return {
|
|
3478
|
+
status: "error"
|
|
3479
|
+
};
|
|
3480
|
+
} else {
|
|
3481
|
+
return {
|
|
3482
|
+
groups,
|
|
3483
|
+
status: "loading"
|
|
3484
|
+
};
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
// The product the most recent finished request was for.
|
|
3488
|
+
function useSettledProduct(product, status) {
|
|
3489
|
+
const [settledProduct, setSettledProduct] = React__default.useState();
|
|
3490
|
+
React__default.useEffect(() => {
|
|
3491
|
+
if (status === "done") setSettledProduct(product);
|
|
3492
|
+
}, [product, status]);
|
|
3493
|
+
return settledProduct;
|
|
3308
3494
|
}
|
|
3309
3495
|
|
|
3310
3496
|
function parseValue(element) {
|
|
@@ -3373,10 +3559,14 @@ function useRecentlyVisitableMetadata() {
|
|
|
3373
3559
|
return useSyncExternalStore(recentlyVisitableMetadataStore.subscribe, () => recentlyVisitableMetadataStore.getState().metadata);
|
|
3374
3560
|
}
|
|
3375
3561
|
|
|
3376
|
-
|
|
3377
|
-
|
|
3562
|
+
// Entries match the ones home-mobile stores, so one list can sync across web
|
|
3563
|
+
// and mobile.
|
|
3564
|
+
const ENTRY_VERSION = 3;
|
|
3565
|
+
const MAX_ENTRIES = 50;
|
|
3378
3566
|
const MAX_TEXT_LENGTH = 200;
|
|
3379
3567
|
const PRODUCTS = ["accounts", "calendar", "check-ins", "giving", "groups", "home", "login", "people", "publishing", "registrations", "services"];
|
|
3568
|
+
// Login has no pages worth returning to.
|
|
3569
|
+
const APPS = PRODUCTS.filter(product => product !== "login");
|
|
3380
3570
|
function isId(value) {
|
|
3381
3571
|
return value !== "" && Array.from(value).every(character => character >= "0" && character <= "9");
|
|
3382
3572
|
}
|
|
@@ -3385,26 +3575,47 @@ function isPcrl(value) {
|
|
|
3385
3575
|
const segments = value.split("/");
|
|
3386
3576
|
return segments.length === 4 && PRODUCTS.some(product => product === segments[0]) && isId(segments[3]);
|
|
3387
3577
|
}
|
|
3578
|
+
function isApp(value) {
|
|
3579
|
+
return APPS.some(app => app === value);
|
|
3580
|
+
}
|
|
3581
|
+
// Links are built by resolving the path against its app's origin, so a stored
|
|
3582
|
+
// path must not be able to resolve anywhere else (`//evil.com`, `/\evil.com`).
|
|
3583
|
+
function isPath(value) {
|
|
3584
|
+
const origin = "https://app.invalid";
|
|
3585
|
+
try {
|
|
3586
|
+
return typeof value === "string" && value.startsWith("/") && new URL(value, origin).origin === origin;
|
|
3587
|
+
} catch {
|
|
3588
|
+
return false;
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3388
3591
|
function isNonBlankString(value) {
|
|
3389
3592
|
return typeof value === "string" && value.trim() !== "";
|
|
3390
3593
|
}
|
|
3594
|
+
function isOptionalString(value) {
|
|
3595
|
+
return value === undefined || typeof value === "string";
|
|
3596
|
+
}
|
|
3391
3597
|
function isRecord(value) {
|
|
3392
3598
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3393
3599
|
}
|
|
3600
|
+
function isPageMetadata(value) {
|
|
3601
|
+
return value === undefined || isRecord(value) && isOptionalString(value.details) && isOptionalString(value.type);
|
|
3602
|
+
}
|
|
3603
|
+
// The query string is part of the link but not of the page's identity, so
|
|
3604
|
+
// revisiting with other filters moves the existing entry instead of adding one.
|
|
3394
3605
|
function entryKey(entry) {
|
|
3395
3606
|
if (entry.type === "resource") {
|
|
3396
3607
|
return `resource:${entry.pcrl}`;
|
|
3397
3608
|
} else {
|
|
3398
|
-
return `page:${entry.
|
|
3609
|
+
return `page:${entry.app}:${entry.path.split("?")[0]}`;
|
|
3399
3610
|
}
|
|
3400
3611
|
}
|
|
3401
3612
|
function isEntry(value) {
|
|
3402
|
-
if (!isRecord(value)) {
|
|
3613
|
+
if (!isRecord(value) || value.version !== ENTRY_VERSION || typeof value.visitedAt !== "string") {
|
|
3403
3614
|
return false;
|
|
3404
3615
|
} else if (value.type === "resource") {
|
|
3405
3616
|
return typeof value.pcrl === "string" && isPcrl(value.pcrl);
|
|
3406
3617
|
} else if (value.type === "page") {
|
|
3407
|
-
return
|
|
3618
|
+
return isApp(value.app) && isPath(value.path) && isNonBlankString(value.title) && isPageMetadata(value.metadata);
|
|
3408
3619
|
} else {
|
|
3409
3620
|
return false;
|
|
3410
3621
|
}
|
|
@@ -3413,58 +3624,114 @@ function isEntry(value) {
|
|
|
3413
3624
|
function clampText(value) {
|
|
3414
3625
|
return Array.from(value).slice(0, MAX_TEXT_LENGTH).join("");
|
|
3415
3626
|
}
|
|
3416
|
-
function
|
|
3417
|
-
|
|
3418
|
-
|
|
3627
|
+
function clampMetadata({
|
|
3628
|
+
details,
|
|
3629
|
+
type
|
|
3630
|
+
}) {
|
|
3631
|
+
return {
|
|
3632
|
+
...(details && {
|
|
3633
|
+
details: clampText(details)
|
|
3634
|
+
}),
|
|
3635
|
+
...(type && {
|
|
3636
|
+
type: clampText(type)
|
|
3637
|
+
})
|
|
3638
|
+
};
|
|
3639
|
+
}
|
|
3640
|
+
function clampVisit(visit) {
|
|
3641
|
+
if (visit.type === "resource") {
|
|
3642
|
+
return visit;
|
|
3419
3643
|
} else {
|
|
3420
3644
|
return {
|
|
3421
|
-
...
|
|
3422
|
-
|
|
3423
|
-
|
|
3645
|
+
...visit,
|
|
3646
|
+
...(visit.metadata && {
|
|
3647
|
+
metadata: clampMetadata(visit.metadata)
|
|
3648
|
+
}),
|
|
3649
|
+
title: clampText(visit.title.trim())
|
|
3424
3650
|
};
|
|
3425
3651
|
}
|
|
3426
3652
|
}
|
|
3653
|
+
function toEntry(visit, visitedAt) {
|
|
3654
|
+
return {
|
|
3655
|
+
...clampVisit(visit),
|
|
3656
|
+
version: ENTRY_VERSION,
|
|
3657
|
+
visitedAt: visitedAt.toISOString()
|
|
3658
|
+
};
|
|
3659
|
+
}
|
|
3427
3660
|
// Recents are disposable: anything unexpected reads as an empty list (or, for
|
|
3428
3661
|
// a single bad entry, a list without it) rather than throwing.
|
|
3429
|
-
function parseRecentlyVisited(value
|
|
3662
|
+
function parseRecentlyVisited(value) {
|
|
3430
3663
|
let parsed = null;
|
|
3431
3664
|
try {
|
|
3432
3665
|
parsed = JSON.parse(value !== null && value !== void 0 ? value : "null");
|
|
3433
3666
|
} catch {
|
|
3434
3667
|
parsed = null;
|
|
3435
3668
|
}
|
|
3436
|
-
if (
|
|
3437
|
-
return parsed.
|
|
3669
|
+
if (Array.isArray(parsed)) {
|
|
3670
|
+
return parsed.filter(isEntry);
|
|
3438
3671
|
} else {
|
|
3439
3672
|
return [];
|
|
3440
3673
|
}
|
|
3441
3674
|
}
|
|
3442
|
-
function addVisit(entries,
|
|
3443
|
-
const key = entryKey(
|
|
3444
|
-
return [
|
|
3445
|
-
}
|
|
3446
|
-
function serializeRecentlyVisited(entries, personId) {
|
|
3447
|
-
return JSON.stringify({
|
|
3448
|
-
entries,
|
|
3449
|
-
person: personId,
|
|
3450
|
-
v: FORMAT_VERSION
|
|
3451
|
-
});
|
|
3675
|
+
function addVisit(entries, entry) {
|
|
3676
|
+
const key = entryKey(entry);
|
|
3677
|
+
return [entry, ...entries.filter(existing => entryKey(existing) !== key)].slice(0, MAX_ENTRIES);
|
|
3452
3678
|
}
|
|
3453
3679
|
|
|
3454
|
-
// The
|
|
3680
|
+
// The app whose URL, in this environment, has the current page's host.
|
|
3455
3681
|
// Reversing pcoUrl handles every environment @planningcenter/url knows about,
|
|
3456
|
-
// and guarantees the stored
|
|
3457
|
-
function
|
|
3682
|
+
// and guarantees the stored app rebuilds a working link.
|
|
3683
|
+
function currentApp() {
|
|
3458
3684
|
var _a;
|
|
3459
|
-
return (_a =
|
|
3685
|
+
return (_a = APPS.find(app => new URL(pcoUrl$2(app)).hostname === window.location.hostname)) !== null && _a !== void 0 ? _a : null;
|
|
3460
3686
|
}
|
|
3461
|
-
function
|
|
3687
|
+
function isFilled(value) {
|
|
3688
|
+
return typeof value === "string" && value !== "";
|
|
3689
|
+
}
|
|
3690
|
+
// The tag's metadata is product-supplied JSON; only its string `type` and
|
|
3691
|
+
// `details` carry over, and an entry without either has no metadata at all.
|
|
3692
|
+
function metadataFromPage(metadata) {
|
|
3462
3693
|
var _a;
|
|
3463
3694
|
try {
|
|
3464
|
-
const
|
|
3465
|
-
|
|
3695
|
+
const {
|
|
3696
|
+
details,
|
|
3697
|
+
type
|
|
3698
|
+
} = (_a = JSON.parse(metadata !== null && metadata !== void 0 ? metadata : "{}")) !== null && _a !== void 0 ? _a : {};
|
|
3699
|
+
if (isFilled(details) || isFilled(type)) {
|
|
3700
|
+
return {
|
|
3701
|
+
...(isFilled(details) && {
|
|
3702
|
+
details
|
|
3703
|
+
}),
|
|
3704
|
+
...(isFilled(type) && {
|
|
3705
|
+
type
|
|
3706
|
+
})
|
|
3707
|
+
};
|
|
3708
|
+
} else {
|
|
3709
|
+
return undefined;
|
|
3710
|
+
}
|
|
3466
3711
|
} catch {
|
|
3467
|
-
return
|
|
3712
|
+
return undefined;
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
// Staff impersonating someone browse under `/O<org id>/P<person id>/…`.
|
|
3716
|
+
function withoutImpersonation(segments) {
|
|
3717
|
+
const [org = "", person = ""] = segments;
|
|
3718
|
+
if (org.startsWith("O") && isId(org.slice(1)) && person.startsWith("P") && isId(person.slice(1))) {
|
|
3719
|
+
return segments.slice(2);
|
|
3720
|
+
} else {
|
|
3721
|
+
return segments;
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
// Normalized the way home-mobile does it, so a page visited on web and on
|
|
3725
|
+
// mobile is one entry once the list syncs.
|
|
3726
|
+
function pathFromLocation({
|
|
3727
|
+
pathname,
|
|
3728
|
+
search
|
|
3729
|
+
}) {
|
|
3730
|
+
const segments = withoutImpersonation(pathname.split("/").filter(Boolean));
|
|
3731
|
+
if (segments.length === 0) {
|
|
3732
|
+
return "/";
|
|
3733
|
+
} else {
|
|
3734
|
+
return `/${segments.join("/")}${search.length > 1 ? search : ""}`;
|
|
3468
3735
|
}
|
|
3469
3736
|
}
|
|
3470
3737
|
// pco:resource is the more specific signal, so it wins when both tags are
|
|
@@ -3474,17 +3741,20 @@ function visitFromPage({
|
|
|
3474
3741
|
resource,
|
|
3475
3742
|
title
|
|
3476
3743
|
}) {
|
|
3477
|
-
const
|
|
3744
|
+
const app = currentApp();
|
|
3478
3745
|
if (resource && isPcrl(resource)) {
|
|
3479
3746
|
return {
|
|
3480
3747
|
pcrl: resource,
|
|
3481
3748
|
type: "resource"
|
|
3482
3749
|
};
|
|
3483
|
-
} else if (title && title.trim() !== "" &&
|
|
3750
|
+
} else if (title && title.trim() !== "" && app) {
|
|
3751
|
+
const pageMetadata = metadataFromPage(metadata);
|
|
3484
3752
|
return {
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3753
|
+
app,
|
|
3754
|
+
...(pageMetadata && {
|
|
3755
|
+
metadata: pageMetadata
|
|
3756
|
+
}),
|
|
3757
|
+
path: pathFromLocation(window.location),
|
|
3488
3758
|
title,
|
|
3489
3759
|
type: "page"
|
|
3490
3760
|
};
|
|
@@ -3532,15 +3802,16 @@ const changes = createStore({
|
|
|
3532
3802
|
});
|
|
3533
3803
|
function readRecentlyVisited(scope) {
|
|
3534
3804
|
try {
|
|
3535
|
-
return parseRecentlyVisited(localStorage.getItem(storageKey(scope))
|
|
3805
|
+
return parseRecentlyVisited(localStorage.getItem(storageKey(scope)));
|
|
3536
3806
|
} catch {
|
|
3537
3807
|
return [];
|
|
3538
3808
|
}
|
|
3539
3809
|
}
|
|
3540
3810
|
function recordVisit(visit, scope) {
|
|
3541
|
-
|
|
3811
|
+
const entry = toEntry(visit, new Date());
|
|
3812
|
+
if (isEntry(entry)) {
|
|
3542
3813
|
try {
|
|
3543
|
-
localStorage.setItem(storageKey(scope),
|
|
3814
|
+
localStorage.setItem(storageKey(scope), JSON.stringify(addVisit(readRecentlyVisited(scope), entry)));
|
|
3544
3815
|
changes.setState({
|
|
3545
3816
|
version: changes.getState().version + 1
|
|
3546
3817
|
});
|
|
@@ -3561,153 +3832,6 @@ function useRecentlyVisited({
|
|
|
3561
3832
|
}) : [], [env, personId, version]);
|
|
3562
3833
|
}
|
|
3563
3834
|
|
|
3564
|
-
// The versions home-mobile pins, so web and mobile read the same response
|
|
3565
|
-
// shapes. Some endpoints only exist from a version on: Registrations' me,
|
|
3566
|
-
// product_search_results, and pcrl_lookups need 2025-05-01.
|
|
3567
|
-
const API_VERSIONS = {
|
|
3568
|
-
calendar: "2022-07-07",
|
|
3569
|
-
"check-ins": "2025-05-28",
|
|
3570
|
-
giving: "2019-10-18",
|
|
3571
|
-
groups: "2023-07-10",
|
|
3572
|
-
home: "2018-08-01",
|
|
3573
|
-
people: "2023-03-21",
|
|
3574
|
-
publishing: "2024-03-25",
|
|
3575
|
-
registrations: "2025-05-01",
|
|
3576
|
-
services: "2018-11-01"
|
|
3577
|
-
};
|
|
3578
|
-
// For a `/~api/{product}/v2/...` URL, the header that pins its product's
|
|
3579
|
-
// version.
|
|
3580
|
-
function apiVersionHeaders(url) {
|
|
3581
|
-
const [,, product] = new URL(url).pathname.split("/");
|
|
3582
|
-
const version = API_VERSIONS[product];
|
|
3583
|
-
if (version) {
|
|
3584
|
-
return {
|
|
3585
|
-
"X-PCO-API-Version": version
|
|
3586
|
-
};
|
|
3587
|
-
} else {
|
|
3588
|
-
return {};
|
|
3589
|
-
}
|
|
3590
|
-
}
|
|
3591
|
-
|
|
3592
|
-
const CACHE_TTL_MS = 60000;
|
|
3593
|
-
const MAX_ENTRIES = 50;
|
|
3594
|
-
const cache = new Map();
|
|
3595
|
-
function cachedRequest(url) {
|
|
3596
|
-
const cached = cache.get(url);
|
|
3597
|
-
if (cached && cached.expiresAt > Date.now()) {
|
|
3598
|
-
// Re-inserting keeps the Map in recently-used order for eviction.
|
|
3599
|
-
cache.delete(url);
|
|
3600
|
-
cache.set(url, cached);
|
|
3601
|
-
return cached.promise;
|
|
3602
|
-
} else {
|
|
3603
|
-
return fetchIntoCache(url);
|
|
3604
|
-
}
|
|
3605
|
-
}
|
|
3606
|
-
function fetchIntoCache(url) {
|
|
3607
|
-
const promise = apiRequest(defaultFetch, url, {
|
|
3608
|
-
headers: apiVersionHeaders(url)
|
|
3609
|
-
}).then(({
|
|
3610
|
-
json,
|
|
3611
|
-
ok
|
|
3612
|
-
}) => {
|
|
3613
|
-
if (!ok || json === null) throw new Error(`Request failed: ${url}`);
|
|
3614
|
-
return json;
|
|
3615
|
-
});
|
|
3616
|
-
// In-flight entries never expire, so every caller shares the one fetch.
|
|
3617
|
-
const entry = {
|
|
3618
|
-
expiresAt: Infinity,
|
|
3619
|
-
promise
|
|
3620
|
-
};
|
|
3621
|
-
cache.set(url, entry);
|
|
3622
|
-
promise.then(() => entry.expiresAt = Date.now() + CACHE_TTL_MS, () => cache.get(url) === entry && cache.delete(url));
|
|
3623
|
-
if (cache.size > MAX_ENTRIES) cache.delete(cache.keys().next().value);
|
|
3624
|
-
return promise;
|
|
3625
|
-
}
|
|
3626
|
-
function useCachedRequest(url) {
|
|
3627
|
-
const [settled, setSettled] = React__default.useState();
|
|
3628
|
-
React__default.useEffect(() => {
|
|
3629
|
-
if (url) {
|
|
3630
|
-
let current = true;
|
|
3631
|
-
cachedRequest(url).then(data => current && setSettled({
|
|
3632
|
-
data,
|
|
3633
|
-
url
|
|
3634
|
-
}), () => current && setSettled({
|
|
3635
|
-
failed: true,
|
|
3636
|
-
url
|
|
3637
|
-
}));
|
|
3638
|
-
return () => {
|
|
3639
|
-
current = false;
|
|
3640
|
-
};
|
|
3641
|
-
} else {
|
|
3642
|
-
return undefined;
|
|
3643
|
-
}
|
|
3644
|
-
}, [url]);
|
|
3645
|
-
if (!url) {
|
|
3646
|
-
return {
|
|
3647
|
-
status: "idle"
|
|
3648
|
-
};
|
|
3649
|
-
} else if ((settled === null || settled === void 0 ? void 0 : settled.url) !== url) {
|
|
3650
|
-
return {
|
|
3651
|
-
data: settled && "data" in settled ? settled.data : undefined,
|
|
3652
|
-
status: "loading"
|
|
3653
|
-
};
|
|
3654
|
-
} else if ("data" in settled) {
|
|
3655
|
-
return {
|
|
3656
|
-
data: settled.data,
|
|
3657
|
-
status: "done"
|
|
3658
|
-
};
|
|
3659
|
-
} else {
|
|
3660
|
-
return {
|
|
3661
|
-
status: "error"
|
|
3662
|
-
};
|
|
3663
|
-
}
|
|
3664
|
-
}
|
|
3665
|
-
// One request per URL, for callers that fan out to a varying number of
|
|
3666
|
-
// endpoints. Unlike `useCachedRequest`, a URL that hasn't settled yet has no
|
|
3667
|
-
// earlier data to fall back on.
|
|
3668
|
-
function useCachedRequests(urls) {
|
|
3669
|
-
const [settled, setSettled] = React__default.useState({});
|
|
3670
|
-
// Keyed by value because each settle re-renders the caller, and a caller
|
|
3671
|
-
// that builds a fresh `urls` array on render would otherwise loop forever.
|
|
3672
|
-
const key = urls.join("\n");
|
|
3673
|
-
React__default.useEffect(() => {
|
|
3674
|
-
let current = true;
|
|
3675
|
-
const settle = result => current && setSettled(prev => ({
|
|
3676
|
-
...prev,
|
|
3677
|
-
[result.url]: result
|
|
3678
|
-
}));
|
|
3679
|
-
for (const url of key.split("\n").filter(Boolean)) {
|
|
3680
|
-
cachedRequest(url).then(data => settle({
|
|
3681
|
-
data,
|
|
3682
|
-
url
|
|
3683
|
-
}), () => settle({
|
|
3684
|
-
failed: true,
|
|
3685
|
-
url
|
|
3686
|
-
}));
|
|
3687
|
-
}
|
|
3688
|
-
return () => {
|
|
3689
|
-
current = false;
|
|
3690
|
-
};
|
|
3691
|
-
}, [key]);
|
|
3692
|
-
return urls.map(url => {
|
|
3693
|
-
const result = settled[url];
|
|
3694
|
-
if (!result) {
|
|
3695
|
-
return {
|
|
3696
|
-
status: "loading"
|
|
3697
|
-
};
|
|
3698
|
-
} else if ("data" in result) {
|
|
3699
|
-
return {
|
|
3700
|
-
data: result.data,
|
|
3701
|
-
status: "done"
|
|
3702
|
-
};
|
|
3703
|
-
} else {
|
|
3704
|
-
return {
|
|
3705
|
-
status: "error"
|
|
3706
|
-
};
|
|
3707
|
-
}
|
|
3708
|
-
});
|
|
3709
|
-
}
|
|
3710
|
-
|
|
3711
3835
|
const pcoUrl = pcoUrl$1;
|
|
3712
3836
|
|
|
3713
3837
|
// Resolves resource entries with one pcrl_lookups request per product.
|
|
@@ -3798,13 +3922,290 @@ function pcrlLookupsUrl(product, pcrls) {
|
|
|
3798
3922
|
});
|
|
3799
3923
|
return `${window.location.origin}/~api/${product}/v2/pcrl_lookups?${params}`;
|
|
3800
3924
|
}
|
|
3801
|
-
// Built from the
|
|
3802
|
-
//
|
|
3925
|
+
// Built from the app rather than the current origin, so it stays right once
|
|
3926
|
+
// PCA-409 shares one list across product subdomains.
|
|
3803
3927
|
function pageUrl(entry, env) {
|
|
3804
|
-
return
|
|
3928
|
+
return new URL(entry.path, pcoUrl(env)(entry.app)).href;
|
|
3929
|
+
}
|
|
3930
|
+
|
|
3931
|
+
function useRecentVisits(env) {
|
|
3932
|
+
const personId = useCurrentPersonId();
|
|
3933
|
+
const entries = useRecentlyVisited({
|
|
3934
|
+
env,
|
|
3935
|
+
personId
|
|
3936
|
+
});
|
|
3937
|
+
const [count, setCount] = React__default.useState(RECENTLY_VISITED_PAGE_SIZE);
|
|
3938
|
+
const shown = React__default.useMemo(() => entries.slice(0, count), [entries, count]);
|
|
3939
|
+
const hydrated = useHydratedRecentlyVisited(shown, env);
|
|
3940
|
+
// Resources show up once they're found. Ones that can't be found anymore,
|
|
3941
|
+
// or that the person can no longer see, are left out.
|
|
3942
|
+
const visits = hydrated.filter(isShown);
|
|
3943
|
+
const loading = hydrated.some(({
|
|
3944
|
+
status
|
|
3945
|
+
}) => status === "loading");
|
|
3946
|
+
const showMore = React__default.useCallback(() => setCount(current => current + RECENTLY_VISITED_PAGE_SIZE), []);
|
|
3947
|
+
return {
|
|
3948
|
+
count,
|
|
3949
|
+
hasMore: entries.length > count,
|
|
3950
|
+
loading,
|
|
3951
|
+
showMore,
|
|
3952
|
+
visits
|
|
3953
|
+
};
|
|
3954
|
+
}
|
|
3955
|
+
function isShown(visit) {
|
|
3956
|
+
return visit.status === "page" || visit.status === "resolved";
|
|
3957
|
+
}
|
|
3958
|
+
|
|
3959
|
+
function open(url, {
|
|
3960
|
+
newTab
|
|
3961
|
+
}) {
|
|
3962
|
+
if (newTab) {
|
|
3963
|
+
window.open(url, "_blank", "noopener");
|
|
3964
|
+
} else {
|
|
3965
|
+
window.location.assign(url);
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
// With text selected, ⌘C should copy that text as usual, not the link.
|
|
3969
|
+
function hasSelectedText(element) {
|
|
3970
|
+
var _a, _b;
|
|
3971
|
+
const selectedInField = element instanceof HTMLInputElement && element.selectionStart !== element.selectionEnd;
|
|
3972
|
+
return selectedInField || ((_b = (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : "") !== "";
|
|
3973
|
+
}
|
|
3974
|
+
|
|
3975
|
+
const COPIED_FEEDBACK_MS = 1500;
|
|
3976
|
+
// `copiedUrl` stays set briefly after a copy so the UI can confirm it.
|
|
3977
|
+
function useCopyLink() {
|
|
3978
|
+
const [copiedUrl, setCopiedUrl] = React__default.useState(null);
|
|
3979
|
+
React__default.useEffect(() => {
|
|
3980
|
+
if (copiedUrl) {
|
|
3981
|
+
const timeout = setTimeout(() => setCopiedUrl(null), COPIED_FEEDBACK_MS);
|
|
3982
|
+
return () => clearTimeout(timeout);
|
|
3983
|
+
} else {
|
|
3984
|
+
return undefined;
|
|
3985
|
+
}
|
|
3986
|
+
}, [copiedUrl]);
|
|
3987
|
+
const copy = React__default.useCallback(url => {
|
|
3988
|
+
navigator.clipboard.writeText(url).then(() => setCopiedUrl(url));
|
|
3989
|
+
}, []);
|
|
3990
|
+
return {
|
|
3991
|
+
copiedUrl,
|
|
3992
|
+
copy
|
|
3993
|
+
};
|
|
3994
|
+
}
|
|
3995
|
+
|
|
3996
|
+
// Remembering which list the index belongs to puts the highlight back on the
|
|
3997
|
+
// first item whenever a new list replaces it, without an effect to reset it.
|
|
3998
|
+
// A list that grows or fills in under the same `list` keeps its highlight.
|
|
3999
|
+
function useResultNavigation(items, list) {
|
|
4000
|
+
const [highlight, setHighlight] = React__default.useState({
|
|
4001
|
+
index: 0,
|
|
4002
|
+
list
|
|
4003
|
+
});
|
|
4004
|
+
const highlightedIndex = highlight.list === list ? highlight.index : 0;
|
|
4005
|
+
const highlighted = items[highlightedIndex];
|
|
4006
|
+
const {
|
|
4007
|
+
copiedUrl,
|
|
4008
|
+
copy
|
|
4009
|
+
} = useCopyLink();
|
|
4010
|
+
const highlightIndex = React__default.useCallback(index => setHighlight(current => current.list === list && current.index === index ? current : {
|
|
4011
|
+
index,
|
|
4012
|
+
list
|
|
4013
|
+
}), [list]);
|
|
4014
|
+
const handleKeyDown = event => {
|
|
4015
|
+
const step = verticalStep(event.key);
|
|
4016
|
+
if (step && items.length > 0) {
|
|
4017
|
+
event.preventDefault();
|
|
4018
|
+
highlightIndex((highlightedIndex + step + items.length) % items.length);
|
|
4019
|
+
} else if (event.key === "Enter" && highlighted && !event.nativeEvent.isComposing) {
|
|
4020
|
+
event.preventDefault();
|
|
4021
|
+
open(highlighted.url, {
|
|
4022
|
+
newTab: hasCommandKey(event)
|
|
4023
|
+
});
|
|
4024
|
+
} else if (isCopyShortcut(event) && highlighted && !hasSelectedText(event.currentTarget)) {
|
|
4025
|
+
event.preventDefault();
|
|
4026
|
+
copy(highlighted.url);
|
|
4027
|
+
}
|
|
4028
|
+
};
|
|
4029
|
+
return {
|
|
4030
|
+
copied: copiedUrl !== null && copiedUrl === (highlighted === null || highlighted === void 0 ? void 0 : highlighted.url),
|
|
4031
|
+
handleKeyDown,
|
|
4032
|
+
highlightIndex,
|
|
4033
|
+
highlighted,
|
|
4034
|
+
highlightedIndex: highlighted ? highlightedIndex : null
|
|
4035
|
+
};
|
|
4036
|
+
}
|
|
4037
|
+
|
|
4038
|
+
function groupSections(groups = []) {
|
|
4039
|
+
return groups.map(({
|
|
4040
|
+
results,
|
|
4041
|
+
type
|
|
4042
|
+
}) => ({
|
|
4043
|
+
heading: type,
|
|
4044
|
+
items: results.map(resultItem),
|
|
4045
|
+
key: type
|
|
4046
|
+
}));
|
|
4047
|
+
}
|
|
4048
|
+
function resultItem({
|
|
4049
|
+
attributes,
|
|
4050
|
+
id
|
|
4051
|
+
}) {
|
|
4052
|
+
return {
|
|
4053
|
+
key: id,
|
|
4054
|
+
subtitle: summarize(attributes.metadata),
|
|
4055
|
+
title: attributes.title,
|
|
4056
|
+
url: attributes.url
|
|
4057
|
+
};
|
|
4058
|
+
}
|
|
4059
|
+
function visitItem(visit) {
|
|
4060
|
+
return {
|
|
4061
|
+
key: entryKey(visit.entry),
|
|
4062
|
+
...describeVisit(visit)
|
|
4063
|
+
};
|
|
4064
|
+
}
|
|
4065
|
+
function summarize(metadata) {
|
|
4066
|
+
return Object.values(metadata !== null && metadata !== void 0 ? metadata : {}).filter(value => typeof value === "string" && value !== "").join(" · ");
|
|
4067
|
+
}
|
|
4068
|
+
// Unlike search results, recents come from every product, so each one names
|
|
4069
|
+
// its product and type.
|
|
4070
|
+
function describeVisit(visit) {
|
|
4071
|
+
var _a, _b;
|
|
4072
|
+
const subtitle = (...parts) => parts.filter(Boolean).join(" · ");
|
|
4073
|
+
if (visit.status === "page") {
|
|
4074
|
+
const {
|
|
4075
|
+
app,
|
|
4076
|
+
metadata,
|
|
4077
|
+
title
|
|
4078
|
+
} = visit.entry;
|
|
4079
|
+
return {
|
|
4080
|
+
subtitle: subtitle(productName(app), (_a = metadata === null || metadata === void 0 ? void 0 : metadata.type) !== null && _a !== void 0 ? _a : "", (_b = metadata === null || metadata === void 0 ? void 0 : metadata.details) !== null && _b !== void 0 ? _b : ""),
|
|
4081
|
+
title,
|
|
4082
|
+
url: visit.url
|
|
4083
|
+
};
|
|
4084
|
+
} else {
|
|
4085
|
+
const {
|
|
4086
|
+
metadata,
|
|
4087
|
+
product,
|
|
4088
|
+
title,
|
|
4089
|
+
type
|
|
4090
|
+
} = visit.result.attributes;
|
|
4091
|
+
return {
|
|
4092
|
+
subtitle: subtitle(productName(product), type, summarize(metadata)),
|
|
4093
|
+
title,
|
|
4094
|
+
url: visit.url
|
|
4095
|
+
};
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
// check-ins → Check-Ins
|
|
4099
|
+
function productName(slug) {
|
|
4100
|
+
return slug.split("-").map(capitalize).join("-");
|
|
4101
|
+
}
|
|
4102
|
+
function capitalize(word) {
|
|
4103
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
4104
|
+
}
|
|
4105
|
+
|
|
4106
|
+
const STORAGE_KEY = "topbar.product_search.last_product";
|
|
4107
|
+
function readLastProduct() {
|
|
4108
|
+
try {
|
|
4109
|
+
return localStorage.getItem(STORAGE_KEY);
|
|
4110
|
+
} catch {
|
|
4111
|
+
return null;
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
function saveLastProduct(slug) {
|
|
4115
|
+
try {
|
|
4116
|
+
localStorage.setItem(STORAGE_KEY, slug);
|
|
4117
|
+
} catch {
|
|
4118
|
+
// Storage can be full or blocked; search still works without it.
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
function initialProduct(products) {
|
|
4122
|
+
var _a, _b, _c;
|
|
4123
|
+
const lastProduct = readLastProduct();
|
|
4124
|
+
const bySlug = slug => products.find(product => product.slug === slug);
|
|
4125
|
+
return (_c = (_b = (_a = bySlug(lastProduct)) !== null && _a !== void 0 ? _a : bySlug("people")) !== null && _b !== void 0 ? _b : bySlug("services")) !== null && _c !== void 0 ? _c : products[0];
|
|
3805
4126
|
}
|
|
3806
4127
|
|
|
3807
|
-
|
|
4128
|
+
function KeyboardHints({
|
|
4129
|
+
copied
|
|
4130
|
+
}) {
|
|
4131
|
+
const command = commandKeyLabel();
|
|
4132
|
+
return /*#__PURE__*/React__default.createElement("p", null, "\u2191\u2193 Select \u00B7 \u21B5 Open \u00B7 ", command, "\u21B5 Open in new tab \u00B7 ", command, "C", " ", /*#__PURE__*/React__default.createElement("span", {
|
|
4133
|
+
role: "status"
|
|
4134
|
+
}, copied ? "Copied!" : "Copy link"), " \u00B7 ", command, "K Open search");
|
|
4135
|
+
}
|
|
4136
|
+
|
|
4137
|
+
var styles$d = {"tab":"tab-dz8ki"};
|
|
4138
|
+
|
|
4139
|
+
function ProductTabs({
|
|
4140
|
+
children,
|
|
4141
|
+
onKeyDown,
|
|
4142
|
+
onSelect,
|
|
4143
|
+
products,
|
|
4144
|
+
selected
|
|
4145
|
+
}) {
|
|
4146
|
+
const id = useInstanceId("product-tabs");
|
|
4147
|
+
const tabId = slug => `${id}-tab-${slug}`;
|
|
4148
|
+
const tabRefs = React__default.useRef(new Map());
|
|
4149
|
+
const handleKeyDown = event => {
|
|
4150
|
+
var _a;
|
|
4151
|
+
const offset = horizontalStep(event.key);
|
|
4152
|
+
if (offset) {
|
|
4153
|
+
event.preventDefault();
|
|
4154
|
+
const index = products.indexOf(selected);
|
|
4155
|
+
const next = products[(index + offset + products.length) % products.length];
|
|
4156
|
+
onSelect(next.slug);
|
|
4157
|
+
(_a = tabRefs.current.get(next.slug)) === null || _a === void 0 ? void 0 : _a.focus();
|
|
4158
|
+
} else {
|
|
4159
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
|
|
4160
|
+
}
|
|
4161
|
+
};
|
|
4162
|
+
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("div", {
|
|
4163
|
+
"aria-label": "Product",
|
|
4164
|
+
onKeyDown: handleKeyDown,
|
|
4165
|
+
role: "tablist"
|
|
4166
|
+
}, products.map(product => (/*#__PURE__*/React__default.createElement("button", {
|
|
4167
|
+
"aria-controls": `${id}-panel`,
|
|
4168
|
+
"aria-selected": product === selected,
|
|
4169
|
+
className: styles$d.tab,
|
|
4170
|
+
id: tabId(product.slug),
|
|
4171
|
+
key: product.slug,
|
|
4172
|
+
onClick: () => onSelect(product.slug),
|
|
4173
|
+
ref: element => {
|
|
4174
|
+
if (element) {
|
|
4175
|
+
tabRefs.current.set(product.slug, element);
|
|
4176
|
+
} else {
|
|
4177
|
+
tabRefs.current.delete(product.slug);
|
|
4178
|
+
}
|
|
4179
|
+
},
|
|
4180
|
+
role: "tab",
|
|
4181
|
+
tabIndex: product === selected ? 0 : -1,
|
|
4182
|
+
type: "button"
|
|
4183
|
+
}, product.appName)))), /*#__PURE__*/React__default.createElement("div", {
|
|
4184
|
+
"aria-labelledby": tabId(selected.slug),
|
|
4185
|
+
id: `${id}-panel`,
|
|
4186
|
+
role: "tabpanel"
|
|
4187
|
+
}, children));
|
|
4188
|
+
}
|
|
4189
|
+
|
|
4190
|
+
function EndOfResultsObserver({
|
|
4191
|
+
onVisible
|
|
4192
|
+
}) {
|
|
4193
|
+
const ref = React__default.useRef(null);
|
|
4194
|
+
React__default.useEffect(() => {
|
|
4195
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
4196
|
+
if (entry.isIntersecting) onVisible();
|
|
4197
|
+
});
|
|
4198
|
+
if (ref.current) observer.observe(ref.current);
|
|
4199
|
+
return () => observer.disconnect();
|
|
4200
|
+
}, [onVisible]);
|
|
4201
|
+
return /*#__PURE__*/React__default.createElement("div", {
|
|
4202
|
+
ref: ref
|
|
4203
|
+
});
|
|
4204
|
+
}
|
|
4205
|
+
|
|
4206
|
+
const resultId = (listboxId, index) => `${listboxId}-result-${index}`;
|
|
4207
|
+
|
|
4208
|
+
var styles$c = {"result":"result-Thq2C"};
|
|
3808
4209
|
|
|
3809
4210
|
function SearchResult({
|
|
3810
4211
|
highlighted,
|
|
@@ -3827,7 +4228,7 @@ function SearchResult({
|
|
|
3827
4228
|
// pointer, and that shouldn't steal the highlight.
|
|
3828
4229
|
return /*#__PURE__*/React__default.createElement("a", {
|
|
3829
4230
|
"aria-selected": highlighted,
|
|
3830
|
-
className: styles$
|
|
4231
|
+
className: styles$c.result,
|
|
3831
4232
|
href: url,
|
|
3832
4233
|
id: id,
|
|
3833
4234
|
onMouseMove: onHighlight,
|
|
@@ -3837,21 +4238,129 @@ function SearchResult({
|
|
|
3837
4238
|
}, /*#__PURE__*/React__default.createElement("div", null, title), /*#__PURE__*/React__default.createElement("div", null, subtitle));
|
|
3838
4239
|
}
|
|
3839
4240
|
|
|
3840
|
-
|
|
4241
|
+
// Sections with a heading render as labelled groups; ones without render
|
|
4242
|
+
// their items straight into the listbox. Indexes run across every section.
|
|
4243
|
+
function ResultListbox({
|
|
4244
|
+
busy,
|
|
4245
|
+
highlightedIndex,
|
|
4246
|
+
id,
|
|
4247
|
+
label,
|
|
4248
|
+
labelledBy,
|
|
4249
|
+
onHighlight,
|
|
4250
|
+
sections
|
|
4251
|
+
}) {
|
|
4252
|
+
let index = 0;
|
|
4253
|
+
const renderItem = item => {
|
|
4254
|
+
const itemIndex = index++;
|
|
4255
|
+
return /*#__PURE__*/React__default.createElement(SearchResult, {
|
|
4256
|
+
highlighted: itemIndex === highlightedIndex,
|
|
4257
|
+
id: resultId(id, itemIndex),
|
|
4258
|
+
key: item.key,
|
|
4259
|
+
onHighlight: () => onHighlight(itemIndex),
|
|
4260
|
+
subtitle: item.subtitle,
|
|
4261
|
+
title: item.title,
|
|
4262
|
+
url: item.url
|
|
4263
|
+
});
|
|
4264
|
+
};
|
|
4265
|
+
return /*#__PURE__*/React__default.createElement("div", {
|
|
4266
|
+
"aria-busy": busy,
|
|
4267
|
+
"aria-label": label,
|
|
4268
|
+
"aria-labelledby": labelledBy,
|
|
4269
|
+
id: id,
|
|
4270
|
+
role: "listbox"
|
|
4271
|
+
}, sections.map((section, sectionIndex) => {
|
|
4272
|
+
if (section.heading === undefined) {
|
|
4273
|
+
return /*#__PURE__*/React__default.createElement(React__default.Fragment, {
|
|
4274
|
+
key: section.key
|
|
4275
|
+
}, section.items.map(renderItem));
|
|
4276
|
+
}
|
|
4277
|
+
const headingId = `${id}-group-${sectionIndex}`;
|
|
4278
|
+
return /*#__PURE__*/React__default.createElement("div", {
|
|
4279
|
+
"aria-labelledby": headingId,
|
|
4280
|
+
key: section.key,
|
|
4281
|
+
role: "group"
|
|
4282
|
+
}, /*#__PURE__*/React__default.createElement("div", {
|
|
4283
|
+
id: headingId
|
|
4284
|
+
}, section.heading), section.items.map(renderItem));
|
|
4285
|
+
}));
|
|
4286
|
+
}
|
|
4287
|
+
|
|
4288
|
+
function RecentlyVisited({
|
|
4289
|
+
count,
|
|
4290
|
+
hasMore,
|
|
4291
|
+
items,
|
|
4292
|
+
loading,
|
|
4293
|
+
showMore,
|
|
4294
|
+
...navigation
|
|
4295
|
+
}) {
|
|
4296
|
+
const headingId = `${navigation.id}-heading`;
|
|
4297
|
+
if (items.length === 0 && !hasMore && !loading) {
|
|
4298
|
+
return /*#__PURE__*/React__default.createElement("p", null, "Pages you visit will show up here.");
|
|
4299
|
+
} else {
|
|
4300
|
+
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("div", {
|
|
4301
|
+
id: headingId
|
|
4302
|
+
}, "Recently visited"), /*#__PURE__*/React__default.createElement(ResultListbox, {
|
|
4303
|
+
labelledBy: headingId,
|
|
4304
|
+
sections: [{
|
|
4305
|
+
items,
|
|
4306
|
+
key: "recents"
|
|
4307
|
+
}],
|
|
4308
|
+
...navigation
|
|
4309
|
+
}), hasMore && /*#__PURE__*/React__default.createElement(EndOfResultsObserver, {
|
|
4310
|
+
key: count,
|
|
4311
|
+
onVisible: showMore
|
|
4312
|
+
}));
|
|
4313
|
+
}
|
|
4314
|
+
}
|
|
4315
|
+
|
|
4316
|
+
// A combobox: focus stays here while aria-activedescendant points screen
|
|
4317
|
+
// readers at the highlighted result in the listbox it controls.
|
|
4318
|
+
function SearchInput({
|
|
4319
|
+
expanded,
|
|
4320
|
+
highlightedIndex,
|
|
4321
|
+
inputRef,
|
|
4322
|
+
listboxId,
|
|
4323
|
+
onChange,
|
|
4324
|
+
onFocus,
|
|
4325
|
+
onKeyDown,
|
|
4326
|
+
value
|
|
4327
|
+
}) {
|
|
4328
|
+
return /*#__PURE__*/React__default.createElement("input", {
|
|
4329
|
+
"aria-activedescendant": highlightedIndex === null ? undefined : resultId(listboxId, highlightedIndex),
|
|
4330
|
+
"aria-autocomplete": "list",
|
|
4331
|
+
"aria-controls": expanded ? listboxId : undefined,
|
|
4332
|
+
"aria-expanded": expanded,
|
|
4333
|
+
"aria-label": "Search",
|
|
4334
|
+
autoFocus: true,
|
|
4335
|
+
onChange: event => onChange(event.target.value),
|
|
4336
|
+
onFocus: onFocus,
|
|
4337
|
+
onKeyDown: onKeyDown,
|
|
4338
|
+
placeholder: "Search",
|
|
4339
|
+
ref: inputRef,
|
|
4340
|
+
role: "combobox",
|
|
4341
|
+
type: "search",
|
|
4342
|
+
value: value
|
|
4343
|
+
});
|
|
4344
|
+
}
|
|
4345
|
+
|
|
4346
|
+
var styles$b = {"visuallyHidden":"visuallyHidden-S9VG5"};
|
|
4347
|
+
|
|
3841
4348
|
function SearchResults({
|
|
3842
|
-
|
|
4349
|
+
label,
|
|
4350
|
+
sections,
|
|
4351
|
+
status,
|
|
3843
4352
|
term,
|
|
3844
4353
|
...navigation
|
|
3845
4354
|
}) {
|
|
3846
|
-
|
|
3847
|
-
switch (search.status) {
|
|
4355
|
+
switch (status) {
|
|
3848
4356
|
case "idle":
|
|
3849
4357
|
return null;
|
|
3850
4358
|
case "loading":
|
|
3851
|
-
if (
|
|
3852
|
-
return /*#__PURE__*/React__default.createElement(
|
|
4359
|
+
if (sections.length > 0) {
|
|
4360
|
+
return /*#__PURE__*/React__default.createElement(ResultListbox, {
|
|
3853
4361
|
busy: true,
|
|
3854
|
-
|
|
4362
|
+
label: label,
|
|
4363
|
+
sections: sections,
|
|
3855
4364
|
...navigation
|
|
3856
4365
|
});
|
|
3857
4366
|
}
|
|
@@ -3863,324 +4372,167 @@ function SearchResults({
|
|
|
3863
4372
|
role: "alert"
|
|
3864
4373
|
}, "Search isn\u2019t working right now. Try again.");
|
|
3865
4374
|
case "done":
|
|
3866
|
-
if (
|
|
4375
|
+
if (sections.length === 0) {
|
|
3867
4376
|
return /*#__PURE__*/React__default.createElement("p", {
|
|
3868
4377
|
role: "status"
|
|
3869
4378
|
}, "No results for \u201C", term.trim(), "\u201D");
|
|
3870
4379
|
}
|
|
3871
|
-
return /*#__PURE__*/React__default.createElement(
|
|
3872
|
-
|
|
4380
|
+
return /*#__PURE__*/React__default.createElement(ResultListbox, {
|
|
4381
|
+
busy: false,
|
|
4382
|
+
label: label,
|
|
4383
|
+
sections: sections,
|
|
3873
4384
|
...navigation
|
|
3874
4385
|
});
|
|
3875
4386
|
}
|
|
3876
4387
|
}
|
|
3877
|
-
function ResultGroups({
|
|
3878
|
-
busy = false,
|
|
3879
|
-
groups,
|
|
3880
|
-
highlightedIndex,
|
|
3881
|
-
id,
|
|
3882
|
-
label,
|
|
3883
|
-
onHighlight
|
|
3884
|
-
}) {
|
|
3885
|
-
let index = 0;
|
|
3886
|
-
return /*#__PURE__*/React__default.createElement("div", {
|
|
3887
|
-
"aria-busy": busy,
|
|
3888
|
-
"aria-label": label,
|
|
3889
|
-
id: id,
|
|
3890
|
-
role: "listbox"
|
|
3891
|
-
}, groups.map(({
|
|
3892
|
-
results,
|
|
3893
|
-
type
|
|
3894
|
-
}, groupIndex) => {
|
|
3895
|
-
const headingId = `${id}-group-${groupIndex}`;
|
|
3896
|
-
return /*#__PURE__*/React__default.createElement("div", {
|
|
3897
|
-
"aria-labelledby": headingId,
|
|
3898
|
-
key: type,
|
|
3899
|
-
role: "group"
|
|
3900
|
-
}, /*#__PURE__*/React__default.createElement("div", {
|
|
3901
|
-
id: headingId
|
|
3902
|
-
}, type), results.map(result => {
|
|
3903
|
-
const resultIndex = index++;
|
|
3904
|
-
return /*#__PURE__*/React__default.createElement(SearchResult, {
|
|
3905
|
-
highlighted: resultIndex === highlightedIndex,
|
|
3906
|
-
id: resultId(id, resultIndex),
|
|
3907
|
-
key: result.id,
|
|
3908
|
-
onHighlight: () => onHighlight(resultIndex),
|
|
3909
|
-
subtitle: summarize(result.attributes.metadata),
|
|
3910
|
-
title: result.attributes.title,
|
|
3911
|
-
url: result.attributes.url
|
|
3912
|
-
});
|
|
3913
|
-
}));
|
|
3914
|
-
}));
|
|
3915
|
-
}
|
|
3916
|
-
function summarize(metadata) {
|
|
3917
|
-
return Object.values(metadata !== null && metadata !== void 0 ? metadata : {}).filter(value => typeof value === "string" && value !== "").join(" · ");
|
|
3918
|
-
}
|
|
3919
4388
|
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
const [count, setCount] = React__default.useState(RECENTLY_VISITED_PAGE_SIZE);
|
|
3927
|
-
const shown = React__default.useMemo(() => entries.slice(0, count), [entries, count]);
|
|
3928
|
-
const hydrated = useHydratedRecentlyVisited(shown, env);
|
|
3929
|
-
// Resources show up once they're found. Ones that can't be found anymore,
|
|
3930
|
-
// or that the person can no longer see, are left out.
|
|
3931
|
-
const visits = hydrated.filter(isShown);
|
|
3932
|
-
const loading = hydrated.some(({
|
|
3933
|
-
status
|
|
3934
|
-
}) => status === "loading");
|
|
3935
|
-
const showMore = React__default.useCallback(() => setCount(current => current + RECENTLY_VISITED_PAGE_SIZE), []);
|
|
3936
|
-
return {
|
|
3937
|
-
count,
|
|
3938
|
-
hasMore: entries.length > count,
|
|
3939
|
-
loading,
|
|
3940
|
-
showMore,
|
|
3941
|
-
visits
|
|
3942
|
-
};
|
|
3943
|
-
}
|
|
3944
|
-
function isShown(visit) {
|
|
3945
|
-
return visit.status === "page" || visit.status === "resolved";
|
|
3946
|
-
}
|
|
3947
|
-
function RecentlyVisited({
|
|
3948
|
-
count,
|
|
3949
|
-
hasMore,
|
|
3950
|
-
highlightedIndex,
|
|
3951
|
-
id,
|
|
3952
|
-
loading,
|
|
3953
|
-
onHighlight,
|
|
3954
|
-
showMore,
|
|
3955
|
-
visits
|
|
3956
|
-
}) {
|
|
3957
|
-
const headingId = `${id}-heading`;
|
|
3958
|
-
if (visits.length === 0 && !hasMore && !loading) {
|
|
3959
|
-
return /*#__PURE__*/React__default.createElement("p", null, "Pages you visit will show up here.");
|
|
3960
|
-
} else {
|
|
3961
|
-
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("div", {
|
|
3962
|
-
id: headingId
|
|
3963
|
-
}, "Recently visited"), /*#__PURE__*/React__default.createElement("div", {
|
|
3964
|
-
"aria-labelledby": headingId,
|
|
3965
|
-
id: id,
|
|
3966
|
-
role: "listbox"
|
|
3967
|
-
}, visits.map((visit, index) => (/*#__PURE__*/React__default.createElement(SearchResult, {
|
|
3968
|
-
highlighted: index === highlightedIndex,
|
|
3969
|
-
id: resultId(id, index),
|
|
3970
|
-
key: entryKey(visit.entry),
|
|
3971
|
-
onHighlight: () => onHighlight(index),
|
|
3972
|
-
...describeVisit(visit)
|
|
3973
|
-
})))), hasMore && /*#__PURE__*/React__default.createElement(ShowMoreWhenVisible, {
|
|
3974
|
-
key: count,
|
|
3975
|
-
onVisible: showMore
|
|
3976
|
-
}));
|
|
3977
|
-
}
|
|
3978
|
-
}
|
|
3979
|
-
function ShowMoreWhenVisible({
|
|
3980
|
-
onVisible
|
|
4389
|
+
// Keys the recents list for navigation, so the highlight
|
|
4390
|
+
// stays put as more recents load and as their lookups answer.
|
|
4391
|
+
const RECENTS = "recents";
|
|
4392
|
+
function SearchPanel({
|
|
4393
|
+
env,
|
|
4394
|
+
products
|
|
3981
4395
|
}) {
|
|
3982
|
-
|
|
4396
|
+
var _a, _b;
|
|
4397
|
+
const listboxId = useInstanceId("product-search-results");
|
|
4398
|
+
const [term, setTerm] = React__default.useState("");
|
|
4399
|
+
const [selectedSlug, setSelectedSlug] = React__default.useState(() => initialProduct(products).slug);
|
|
4400
|
+
const product = (_a = products.find(({
|
|
4401
|
+
slug
|
|
4402
|
+
}) => slug === selectedSlug)) !== null && _a !== void 0 ? _a : products[0];
|
|
4403
|
+
const hasTerm = term.trim() !== "";
|
|
4404
|
+
const search = useProductSearch(product.slug, term);
|
|
4405
|
+
const groups = "groups" in search ? search.groups : undefined;
|
|
4406
|
+
const sections = React__default.useMemo(() => groupSections(groups), [groups]);
|
|
4407
|
+
const {
|
|
4408
|
+
visits,
|
|
4409
|
+
...recents
|
|
4410
|
+
} = useRecentVisits(env);
|
|
4411
|
+
const recentItems = visits.map(visitItem);
|
|
4412
|
+
const items = hasTerm ? sections.flatMap(section => section.items) : recentItems;
|
|
4413
|
+
const navigation = useResultNavigation(items, hasTerm ? sections : RECENTS);
|
|
4414
|
+
const {
|
|
4415
|
+
highlightedIndex
|
|
4416
|
+
} = navigation;
|
|
4417
|
+
const inputRef = React__default.useRef(null);
|
|
4418
|
+
const [announceHighlight, setAnnounceHighlight] = React__default.useState(false);
|
|
4419
|
+
// The tabs keep focus while arrowing through results, but only the focused
|
|
4420
|
+
// input can point screen readers at the highlight, so from the tabs it's
|
|
4421
|
+
// announced instead. Typing goes back to the input, keystroke and all.
|
|
4422
|
+
const handleTabsKeyDown = event => {
|
|
4423
|
+
var _a;
|
|
4424
|
+
if (isTypingKey(event)) {
|
|
4425
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
4426
|
+
} else if (verticalStep(event.key)) {
|
|
4427
|
+
setAnnounceHighlight(true);
|
|
4428
|
+
navigation.handleKeyDown(event);
|
|
4429
|
+
} else {
|
|
4430
|
+
navigation.handleKeyDown(event);
|
|
4431
|
+
}
|
|
4432
|
+
};
|
|
3983
4433
|
React__default.useEffect(() => {
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
}
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
};
|
|
4021
|
-
}
|
|
4022
|
-
}
|
|
4023
|
-
// check-ins → Check-Ins
|
|
4024
|
-
function productName(slug) {
|
|
4025
|
-
return slug.split("-").map(capitalize).join("-");
|
|
4026
|
-
}
|
|
4027
|
-
function capitalize(word) {
|
|
4028
|
-
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
4434
|
+
if (hasTerm) saveLastProduct(product.slug);
|
|
4435
|
+
}, [hasTerm, product.slug]);
|
|
4436
|
+
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(SearchInput, {
|
|
4437
|
+
expanded: items.length > 0,
|
|
4438
|
+
highlightedIndex: highlightedIndex,
|
|
4439
|
+
inputRef: inputRef,
|
|
4440
|
+
listboxId: listboxId,
|
|
4441
|
+
onChange: setTerm,
|
|
4442
|
+
onFocus: () => setAnnounceHighlight(false),
|
|
4443
|
+
onKeyDown: navigation.handleKeyDown,
|
|
4444
|
+
value: term
|
|
4445
|
+
}), /*#__PURE__*/React__default.createElement(ProductTabs, {
|
|
4446
|
+
onKeyDown: handleTabsKeyDown,
|
|
4447
|
+
onSelect: setSelectedSlug,
|
|
4448
|
+
products: products,
|
|
4449
|
+
selected: product
|
|
4450
|
+
}, hasTerm ? (/*#__PURE__*/React__default.createElement(SearchResults, {
|
|
4451
|
+
highlightedIndex: highlightedIndex,
|
|
4452
|
+
id: listboxId,
|
|
4453
|
+
label: `${product.appName} results`,
|
|
4454
|
+
onHighlight: navigation.highlightIndex,
|
|
4455
|
+
sections: sections,
|
|
4456
|
+
status: search.status,
|
|
4457
|
+
term: term
|
|
4458
|
+
})) : (/*#__PURE__*/React__default.createElement(RecentlyVisited, {
|
|
4459
|
+
highlightedIndex: highlightedIndex,
|
|
4460
|
+
id: listboxId,
|
|
4461
|
+
items: recentItems,
|
|
4462
|
+
onHighlight: navigation.highlightIndex,
|
|
4463
|
+
...recents
|
|
4464
|
+
}))), /*#__PURE__*/React__default.createElement("div", {
|
|
4465
|
+
"aria-live": "polite",
|
|
4466
|
+
className: styles$b.visuallyHidden
|
|
4467
|
+
}, announceHighlight ? (_b = navigation.highlighted) === null || _b === void 0 ? void 0 : _b.title : null), /*#__PURE__*/React__default.createElement(KeyboardHints, {
|
|
4468
|
+
copied: navigation.copied
|
|
4469
|
+
}));
|
|
4029
4470
|
}
|
|
4030
4471
|
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
const
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
// Services tab.
|
|
4041
|
-
const visibleResponse = request.status === "done" || settledProduct === product ? response : undefined;
|
|
4042
|
-
const groups = React__default.useMemo(() => visibleResponse && groupByType(visibleResponse), [visibleResponse]);
|
|
4043
|
-
if (!query) {
|
|
4044
|
-
return {
|
|
4045
|
-
status: "idle"
|
|
4046
|
-
};
|
|
4047
|
-
} else if (query !== debouncedQuery) {
|
|
4048
|
-
return {
|
|
4049
|
-
groups,
|
|
4050
|
-
status: "loading"
|
|
4051
|
-
};
|
|
4052
|
-
} else if (request.status === "done") {
|
|
4053
|
-
return {
|
|
4054
|
-
groups: groups !== null && groups !== void 0 ? groups : [],
|
|
4055
|
-
status: "done"
|
|
4056
|
-
};
|
|
4057
|
-
} else if (request.status === "error") {
|
|
4058
|
-
return {
|
|
4059
|
-
status: "error"
|
|
4060
|
-
};
|
|
4061
|
-
} else {
|
|
4062
|
-
return {
|
|
4063
|
-
groups,
|
|
4064
|
-
status: "loading"
|
|
4065
|
-
};
|
|
4066
|
-
}
|
|
4472
|
+
// Products open modals from many libraries, not all of which mark themselves
|
|
4473
|
+
// with roles, so a modal is recognized by what it does: cover the page.
|
|
4474
|
+
function isModalOpen() {
|
|
4475
|
+
const {
|
|
4476
|
+
clientHeight,
|
|
4477
|
+
clientWidth
|
|
4478
|
+
} = document.documentElement;
|
|
4479
|
+
const element = document.elementFromPoint(clientWidth / 2, clientHeight / 2);
|
|
4480
|
+
return element !== null && (isInNativeDialog(element) || hasBackdrop(element, clientWidth, clientHeight));
|
|
4067
4481
|
}
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
});
|
|
4073
|
-
return `${window.location.origin}/~api/${product}/v2/product_search_results?${params}`;
|
|
4482
|
+
// A native modal's backdrop is drawn by the browser rather than being an
|
|
4483
|
+
// element; hit-testing it lands on the dialog itself.
|
|
4484
|
+
function isInNativeDialog(element) {
|
|
4485
|
+
return element.closest("dialog[open]") !== null;
|
|
4074
4486
|
}
|
|
4075
|
-
function
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
var _a, _b;
|
|
4080
|
-
const typeOrder = (_a = meta === null || meta === void 0 ? void 0 : meta.type_order) !== null && _a !== void 0 ? _a : [];
|
|
4081
|
-
const groups = new Map();
|
|
4082
|
-
for (const result of data) {
|
|
4083
|
-
const {
|
|
4084
|
-
type
|
|
4085
|
-
} = result.attributes;
|
|
4086
|
-
if (!groups.has(type)) groups.set(type, {
|
|
4087
|
-
results: [],
|
|
4088
|
-
type
|
|
4089
|
-
});
|
|
4090
|
-
(_b = groups.get(type)) === null || _b === void 0 ? void 0 : _b.results.push(result);
|
|
4487
|
+
function hasBackdrop(element, width, height) {
|
|
4488
|
+
let current = element;
|
|
4489
|
+
while (current && !isBackdrop(current, width, height)) {
|
|
4490
|
+
current = current.parentElement;
|
|
4091
4491
|
}
|
|
4092
|
-
|
|
4093
|
-
const index = typeOrder.indexOf(type);
|
|
4094
|
-
return index === -1 ? typeOrder.length : index;
|
|
4095
|
-
};
|
|
4096
|
-
return [...groups.values()].sort((a, b) => rank(a.type) - rank(b.type));
|
|
4492
|
+
return current !== null;
|
|
4097
4493
|
}
|
|
4098
|
-
//
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
const [debounced, setDebounced] = React__default.useState(value);
|
|
4108
|
-
React__default.useEffect(() => {
|
|
4109
|
-
const timeout = setTimeout(() => setDebounced(value), delay);
|
|
4110
|
-
return () => clearTimeout(timeout);
|
|
4111
|
-
}, [value, delay]);
|
|
4112
|
-
return debounced;
|
|
4494
|
+
// Some backdrops start below the topbar rather than at the top of the screen,
|
|
4495
|
+
// so the layer only has to span the width and reach from the center to the
|
|
4496
|
+
// bottom. Its own box has to reach the center: a bar along the bottom can
|
|
4497
|
+
// hold a panel that pops up over the center, like Services' media player.
|
|
4498
|
+
// react-aria leaves a closed modal's backdrop up while it fades out, search's
|
|
4499
|
+
// own included, so one that's on its way out doesn't count.
|
|
4500
|
+
function isBackdrop(element, width, height) {
|
|
4501
|
+
const rect = element.getBoundingClientRect();
|
|
4502
|
+
return !element.matches("[data-exiting]") && getComputedStyle(element).position === "fixed" && rect.width >= width - SCROLLBAR_SLACK && rect.top <= height / 2 && rect.bottom >= height - ROUNDING_SLACK;
|
|
4113
4503
|
}
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
const
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
// A list that grows or fills in under the same `list` keeps its highlight.
|
|
4123
|
-
function useResultNavigation(options, list) {
|
|
4124
|
-
const [highlight, setHighlight] = React__default.useState({
|
|
4125
|
-
index: 0,
|
|
4126
|
-
list
|
|
4127
|
-
});
|
|
4128
|
-
const highlightedIndex = highlight.list === list ? highlight.index : 0;
|
|
4129
|
-
const highlighted = options[highlightedIndex];
|
|
4130
|
-
const [copiedUrl, setCopiedUrl] = React__default.useState(null);
|
|
4504
|
+
// react-aria keeps a classic scrollbar's gutter reserved while its modals lock
|
|
4505
|
+
// scrolling, so their backdrops stop short of the right edge by its width.
|
|
4506
|
+
const SCROLLBAR_SLACK = 24;
|
|
4507
|
+
// Rects are fractional at non-100% zoom, but clientWidth and clientHeight are
|
|
4508
|
+
// rounded to whole pixels.
|
|
4509
|
+
const ROUNDING_SLACK = 1;
|
|
4510
|
+
|
|
4511
|
+
function useSearchShortcut(enabled, open, setOpen) {
|
|
4131
4512
|
React__default.useEffect(() => {
|
|
4132
|
-
if (
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
}
|
|
4154
|
-
|
|
4155
|
-
const {
|
|
4156
|
-
url
|
|
4157
|
-
} = highlighted;
|
|
4158
|
-
navigator.clipboard.writeText(url).then(() => setCopiedUrl(url));
|
|
4159
|
-
}
|
|
4160
|
-
};
|
|
4161
|
-
return {
|
|
4162
|
-
copied: copiedUrl !== null && copiedUrl === (highlighted === null || highlighted === void 0 ? void 0 : highlighted.url),
|
|
4163
|
-
handleKeyDown,
|
|
4164
|
-
highlightIndex,
|
|
4165
|
-
highlighted,
|
|
4166
|
-
highlightedIndex: highlighted ? highlightedIndex : null
|
|
4167
|
-
};
|
|
4168
|
-
}
|
|
4169
|
-
function open(url, {
|
|
4170
|
-
newTab
|
|
4171
|
-
}) {
|
|
4172
|
-
if (newTab) {
|
|
4173
|
-
window.open(url, "_blank", "noopener");
|
|
4174
|
-
} else {
|
|
4175
|
-
window.location.assign(url);
|
|
4176
|
-
}
|
|
4177
|
-
}
|
|
4178
|
-
const isCopyShortcut = event => event.key.toLowerCase() === "c" && !event.altKey && !event.shiftKey && hasCommandKey(event);
|
|
4179
|
-
// With text selected, ⌘C should copy that text as usual, not the link.
|
|
4180
|
-
function hasSelectedText(element) {
|
|
4181
|
-
var _a, _b;
|
|
4182
|
-
const selectedInField = element instanceof HTMLInputElement && element.selectionStart !== element.selectionEnd;
|
|
4183
|
-
return selectedInField || ((_b = (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : "") !== "";
|
|
4513
|
+
if (!enabled) return undefined;
|
|
4514
|
+
// Rich text editors like Trix bind ⌘K to "insert link"; capturing at the
|
|
4515
|
+
// window and stopping propagation keeps them from ever seeing it. Under a
|
|
4516
|
+
// product's modal, search stays shut rather than stacking a second modal,
|
|
4517
|
+
// and the keystroke goes on to the product.
|
|
4518
|
+
const handleKeyDown = event => {
|
|
4519
|
+
if (open && isSearchShortcut(event)) {
|
|
4520
|
+
event.preventDefault();
|
|
4521
|
+
event.stopPropagation();
|
|
4522
|
+
setOpen(false);
|
|
4523
|
+
} else if (!open && (isSearchShortcut(event) || isLegacySearchShortcut(event)) && !isModalOpen()) {
|
|
4524
|
+
event.preventDefault();
|
|
4525
|
+
event.stopPropagation();
|
|
4526
|
+
setOpen(true);
|
|
4527
|
+
}
|
|
4528
|
+
};
|
|
4529
|
+
window.addEventListener("keydown", handleKeyDown, {
|
|
4530
|
+
capture: true
|
|
4531
|
+
});
|
|
4532
|
+
return () => window.removeEventListener("keydown", handleKeyDown, {
|
|
4533
|
+
capture: true
|
|
4534
|
+
});
|
|
4535
|
+
}, [enabled, open, setOpen]);
|
|
4184
4536
|
}
|
|
4185
4537
|
|
|
4186
4538
|
const SEARCHABLE_PRODUCTS = [{
|
|
@@ -4208,6 +4560,7 @@ const SEARCHABLE_PRODUCTS = [{
|
|
|
4208
4560
|
appName: "Services",
|
|
4209
4561
|
slug: "services"
|
|
4210
4562
|
}];
|
|
4563
|
+
|
|
4211
4564
|
const PERMISSION_CHECKS = {
|
|
4212
4565
|
"check-ins": () => fetchMe("check-ins", "permission").then(({
|
|
4213
4566
|
permission
|
|
@@ -4270,6 +4623,7 @@ function canSearch(slug, personId) {
|
|
|
4270
4623
|
return promise;
|
|
4271
4624
|
}
|
|
4272
4625
|
}
|
|
4626
|
+
|
|
4273
4627
|
function useSearchableProducts(apps) {
|
|
4274
4628
|
// Keyed by name because providers hand over a fresh `apps` array on renders
|
|
4275
4629
|
// where nothing changed.
|
|
@@ -4311,181 +4665,27 @@ function inSwitcherOrder(apps) {
|
|
|
4311
4665
|
return groupApps(apps).restProducts.flatMap(app => SEARCHABLE_PRODUCTS.filter(product => product.appName === app.name));
|
|
4312
4666
|
}
|
|
4313
4667
|
|
|
4314
|
-
const isSearchShortcut = event => event.key.toLowerCase() === "k" && !event.altKey && !event.shiftKey && hasCommandKey(event);
|
|
4315
|
-
// Products opened their own search with `/` before this one replaced it.
|
|
4316
|
-
const isLegacySearchShortcut = event => event.key === "/" && !event.altKey && !event.ctrlKey && !event.metaKey && !isTypingIn(event.target);
|
|
4317
|
-
const isTypingKey = event => event.key.length === 1 && event.key !== " " && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
4318
|
-
const isTypingIn = target => target instanceof Element && target.closest('input, select, textarea, [contenteditable]:not([contenteditable="false"])') !== null;
|
|
4319
4668
|
function ProductSearch({
|
|
4320
4669
|
apps,
|
|
4321
4670
|
env
|
|
4322
4671
|
}) {
|
|
4323
4672
|
const searchable = useSearchableProducts(apps);
|
|
4324
|
-
if (searchable.status === "done" && searchable.products.length > 0) {
|
|
4325
|
-
return /*#__PURE__*/React__default.createElement(SearchButtonAndDialog, {
|
|
4326
|
-
env: env,
|
|
4327
|
-
products: searchable.products
|
|
4328
|
-
});
|
|
4329
|
-
} else {
|
|
4330
|
-
return null;
|
|
4331
|
-
}
|
|
4332
|
-
}
|
|
4333
|
-
function SearchButtonAndDialog({
|
|
4334
|
-
env,
|
|
4335
|
-
products
|
|
4336
|
-
}) {
|
|
4337
4673
|
const [open, setOpen] = React__default.useState(false);
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
event.stopPropagation();
|
|
4345
|
-
setOpen(wasOpen => !wasOpen);
|
|
4346
|
-
} else if (isLegacySearchShortcut(event)) {
|
|
4347
|
-
event.preventDefault();
|
|
4348
|
-
event.stopPropagation();
|
|
4349
|
-
setOpen(true);
|
|
4350
|
-
}
|
|
4351
|
-
};
|
|
4352
|
-
window.addEventListener("keydown", handleKeyDown, {
|
|
4353
|
-
capture: true
|
|
4354
|
-
});
|
|
4355
|
-
return () => window.removeEventListener("keydown", handleKeyDown, {
|
|
4356
|
-
capture: true
|
|
4357
|
-
});
|
|
4358
|
-
}, []);
|
|
4359
|
-
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("button", {
|
|
4360
|
-
"aria-keyshortcuts": `${isMac() ? "Meta+K" : "Control+K"} /`,
|
|
4361
|
-
"aria-label": "Search",
|
|
4362
|
-
onClick: () => setOpen(true),
|
|
4363
|
-
type: "button"
|
|
4364
|
-
}, /*#__PURE__*/React__default.createElement(Spyglass, {
|
|
4365
|
-
fill: "currentColor"
|
|
4366
|
-
})), /*#__PURE__*/React__default.createElement(Modal, {
|
|
4674
|
+
const available = searchable.status === "done" && searchable.products.length > 0;
|
|
4675
|
+
useSearchShortcut(available, open, setOpen);
|
|
4676
|
+
if (!available) return null;
|
|
4677
|
+
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(SearchButton, {
|
|
4678
|
+
onClick: () => setOpen(true)
|
|
4679
|
+
}), /*#__PURE__*/React__default.createElement(Modal, {
|
|
4367
4680
|
"aria-label": "Search",
|
|
4368
4681
|
dismissible: true,
|
|
4369
4682
|
onClose: () => setOpen(false),
|
|
4370
4683
|
open: open
|
|
4371
4684
|
}, /*#__PURE__*/React__default.createElement(SearchPanel, {
|
|
4372
4685
|
env: env,
|
|
4373
|
-
products: products
|
|
4686
|
+
products: searchable.products
|
|
4374
4687
|
})));
|
|
4375
4688
|
}
|
|
4376
|
-
// Keys the recents list for navigation, so the highlight stays put as more
|
|
4377
|
-
// recents load and as their lookups answer.
|
|
4378
|
-
const RECENTS = "recents";
|
|
4379
|
-
function SearchPanel({
|
|
4380
|
-
env,
|
|
4381
|
-
products
|
|
4382
|
-
}) {
|
|
4383
|
-
var _a, _b;
|
|
4384
|
-
const listboxId = useInstanceId("product-search-results");
|
|
4385
|
-
const [term, setTerm] = React__default.useState("");
|
|
4386
|
-
const [selectedSlug, setSelectedSlug] = React__default.useState(() => initialProduct(products).slug);
|
|
4387
|
-
const product = (_a = products.find(({
|
|
4388
|
-
slug
|
|
4389
|
-
}) => slug === selectedSlug)) !== null && _a !== void 0 ? _a : products[0];
|
|
4390
|
-
const hasTerm = term.trim() !== "";
|
|
4391
|
-
const search = useProductSearch(product.slug, term);
|
|
4392
|
-
const groups = "groups" in search ? search.groups : undefined;
|
|
4393
|
-
const results = React__default.useMemo(() => {
|
|
4394
|
-
var _a;
|
|
4395
|
-
return (_a = groups === null || groups === void 0 ? void 0 : groups.flatMap(group => group.results)) !== null && _a !== void 0 ? _a : [];
|
|
4396
|
-
}, [groups]);
|
|
4397
|
-
const recents = useRecentVisits(env);
|
|
4398
|
-
const options = hasTerm ? results.map(({
|
|
4399
|
-
attributes
|
|
4400
|
-
}) => ({
|
|
4401
|
-
label: attributes.title,
|
|
4402
|
-
url: attributes.url
|
|
4403
|
-
})) : recents.visits.map(visit => {
|
|
4404
|
-
const {
|
|
4405
|
-
title,
|
|
4406
|
-
url
|
|
4407
|
-
} = describeVisit(visit);
|
|
4408
|
-
return {
|
|
4409
|
-
label: title,
|
|
4410
|
-
url
|
|
4411
|
-
};
|
|
4412
|
-
});
|
|
4413
|
-
const navigation = useResultNavigation(options, hasTerm ? results : RECENTS);
|
|
4414
|
-
const {
|
|
4415
|
-
highlightedIndex
|
|
4416
|
-
} = navigation;
|
|
4417
|
-
const inputRef = React__default.useRef(null);
|
|
4418
|
-
const [announceHighlight, setAnnounceHighlight] = React__default.useState(false);
|
|
4419
|
-
// The tabs keep focus while arrowing through results, but only the focused
|
|
4420
|
-
// input can point screen readers at the highlight, so from the tabs it's
|
|
4421
|
-
// announced instead. Typing goes back to the input, keystroke and all.
|
|
4422
|
-
const handleTabsKeyDown = event => {
|
|
4423
|
-
var _a;
|
|
4424
|
-
if (isTypingKey(event)) {
|
|
4425
|
-
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
4426
|
-
} else if (verticalStep(event.key)) {
|
|
4427
|
-
setAnnounceHighlight(true);
|
|
4428
|
-
navigation.handleKeyDown(event);
|
|
4429
|
-
} else {
|
|
4430
|
-
navigation.handleKeyDown(event);
|
|
4431
|
-
}
|
|
4432
|
-
};
|
|
4433
|
-
React__default.useEffect(() => {
|
|
4434
|
-
if (hasTerm) saveLastProduct(product.slug);
|
|
4435
|
-
}, [hasTerm, product.slug]);
|
|
4436
|
-
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("input", {
|
|
4437
|
-
"aria-activedescendant": highlightedIndex === null ? undefined : resultId(listboxId, highlightedIndex),
|
|
4438
|
-
"aria-autocomplete": "list",
|
|
4439
|
-
"aria-controls": options.length > 0 ? listboxId : undefined,
|
|
4440
|
-
"aria-expanded": options.length > 0,
|
|
4441
|
-
"aria-label": "Search",
|
|
4442
|
-
autoFocus: true,
|
|
4443
|
-
onChange: event => setTerm(event.target.value),
|
|
4444
|
-
onFocus: () => setAnnounceHighlight(false),
|
|
4445
|
-
onKeyDown: navigation.handleKeyDown,
|
|
4446
|
-
placeholder: "Search",
|
|
4447
|
-
ref: inputRef,
|
|
4448
|
-
role: "combobox",
|
|
4449
|
-
type: "search",
|
|
4450
|
-
value: term
|
|
4451
|
-
}), /*#__PURE__*/React__default.createElement(ProductTabs, {
|
|
4452
|
-
onKeyDown: handleTabsKeyDown,
|
|
4453
|
-
onSelect: setSelectedSlug,
|
|
4454
|
-
products: products,
|
|
4455
|
-
selected: product
|
|
4456
|
-
}, hasTerm ? (/*#__PURE__*/React__default.createElement(SearchResults, {
|
|
4457
|
-
highlightedIndex: highlightedIndex,
|
|
4458
|
-
id: listboxId,
|
|
4459
|
-
label: `${product.appName} results`,
|
|
4460
|
-
onHighlight: navigation.highlightIndex,
|
|
4461
|
-
search: search,
|
|
4462
|
-
term: term
|
|
4463
|
-
})) : (/*#__PURE__*/React__default.createElement(RecentlyVisited, {
|
|
4464
|
-
highlightedIndex: highlightedIndex,
|
|
4465
|
-
id: listboxId,
|
|
4466
|
-
onHighlight: navigation.highlightIndex,
|
|
4467
|
-
...recents
|
|
4468
|
-
}))), /*#__PURE__*/React__default.createElement("div", {
|
|
4469
|
-
"aria-live": "polite",
|
|
4470
|
-
className: styles$c.visuallyHidden
|
|
4471
|
-
}, announceHighlight ? (_b = navigation.highlighted) === null || _b === void 0 ? void 0 : _b.label : null), /*#__PURE__*/React__default.createElement(KeyboardHints, {
|
|
4472
|
-
copied: navigation.copied
|
|
4473
|
-
}));
|
|
4474
|
-
}
|
|
4475
|
-
function KeyboardHints({
|
|
4476
|
-
copied
|
|
4477
|
-
}) {
|
|
4478
|
-
const command = commandKeyLabel();
|
|
4479
|
-
return /*#__PURE__*/React__default.createElement("p", null, "\u2191\u2193 Select \u00B7 \u21B5 Open \u00B7 ", command, "\u21B5 Open in new tab \u00B7 ", command, "C", " ", /*#__PURE__*/React__default.createElement("span", {
|
|
4480
|
-
role: "status"
|
|
4481
|
-
}, copied ? "Copied!" : "Copy link"), " \u00B7 ", command, "K Open search");
|
|
4482
|
-
}
|
|
4483
|
-
function initialProduct(products) {
|
|
4484
|
-
var _a, _b, _c;
|
|
4485
|
-
const lastProduct = readLastProduct();
|
|
4486
|
-
const bySlug = slug => products.find(product => product.slug === slug);
|
|
4487
|
-
return (_c = (_b = (_a = bySlug(lastProduct)) !== null && _a !== void 0 ? _a : bySlug("people")) !== null && _b !== void 0 ? _b : bySlug("services")) !== null && _c !== void 0 ? _c : products[0];
|
|
4488
|
-
}
|
|
4489
4689
|
|
|
4490
4690
|
function useRecordRecentlyVisited({
|
|
4491
4691
|
enabled,
|
|
@@ -4529,9 +4729,9 @@ function StyledRoot({
|
|
|
4529
4729
|
`), children);
|
|
4530
4730
|
}
|
|
4531
4731
|
|
|
4532
|
-
var styles$
|
|
4732
|
+
var styles$a = {"route":"route--8BWe","is-active":"is-active-FsU9Y","apps-routes-separator":"apps-routes-separator-Nlm-N"};
|
|
4533
4733
|
|
|
4534
|
-
var styles$
|
|
4734
|
+
var styles$9 = {"popup":"popup-mDNKn","trigger-wrap":"trigger-wrap-Jd-KR","trigger":"trigger-WgRT5","is-visible":"is-visible-GZC0-","trigger-orgname":"trigger-orgname-TDHCc"};
|
|
4535
4735
|
|
|
4536
4736
|
const UserMenu = ({
|
|
4537
4737
|
appName,
|
|
@@ -4550,7 +4750,7 @@ const UserMenu = ({
|
|
|
4550
4750
|
component: Outsider,
|
|
4551
4751
|
onOutsideClick: () => setUserMenuVisible(false),
|
|
4552
4752
|
cleanup: !userMenuVisible,
|
|
4553
|
-
className: styles$
|
|
4753
|
+
className: styles$9.popup,
|
|
4554
4754
|
isVisible: userMenuVisible
|
|
4555
4755
|
}, /*#__PURE__*/React__default.createElement("div", null, hasConnectedPeople ? (/*#__PURE__*/React__default.createElement("div", {
|
|
4556
4756
|
style: {
|
|
@@ -4694,11 +4894,11 @@ const UserMenuTrigger = ({
|
|
|
4694
4894
|
userMenuVisible
|
|
4695
4895
|
}) => {
|
|
4696
4896
|
return /*#__PURE__*/React__default.createElement("div", {
|
|
4697
|
-
className: styles$
|
|
4897
|
+
className: styles$9["trigger-wrap"],
|
|
4698
4898
|
"data-pendo": "topbar-org-switcher"
|
|
4699
4899
|
}, /*#__PURE__*/React__default.createElement(Hoverable, {
|
|
4700
|
-
className: classnames(styles$
|
|
4701
|
-
[styles$
|
|
4900
|
+
className: classnames(styles$9["trigger"], {
|
|
4901
|
+
[styles$9["is-visible"]]: userMenuVisible
|
|
4702
4902
|
}),
|
|
4703
4903
|
component: "button",
|
|
4704
4904
|
type: "button",
|
|
@@ -4710,7 +4910,7 @@ const UserMenuTrigger = ({
|
|
|
4710
4910
|
requestConnectedPeopleFetch();
|
|
4711
4911
|
}
|
|
4712
4912
|
}, showOrgName && (/*#__PURE__*/React__default.createElement("span", {
|
|
4713
|
-
className: styles$
|
|
4913
|
+
className: styles$9["trigger-orgname"]
|
|
4714
4914
|
}, orgName)), /*#__PURE__*/React__default.createElement(DisclosureChevronIcon, null)));
|
|
4715
4915
|
};
|
|
4716
4916
|
|
|
@@ -4770,7 +4970,7 @@ const NotSmallTopbar = ({
|
|
|
4770
4970
|
colors: props.colors,
|
|
4771
4971
|
requestAppsFetch: props.requestAppsFetch
|
|
4772
4972
|
})), /*#__PURE__*/React__default.createElement("div", {
|
|
4773
|
-
className: styles$
|
|
4973
|
+
className: styles$a["apps-routes-separator"]
|
|
4774
4974
|
}), routesVisible && props.routes, /*#__PURE__*/React__default.createElement("div", {
|
|
4775
4975
|
style: {
|
|
4776
4976
|
margin: "auto"
|
|
@@ -4811,12 +5011,12 @@ const NotSmallRoute = ({
|
|
|
4811
5011
|
...nativeProps
|
|
4812
5012
|
}) => {
|
|
4813
5013
|
return /*#__PURE__*/React__default.createElement("a", {
|
|
4814
|
-
className: classnames(styles$
|
|
5014
|
+
className: classnames(styles$a["route"], className, active && styles$a["is-active"]),
|
|
4815
5015
|
...nativeProps
|
|
4816
5016
|
});
|
|
4817
5017
|
};
|
|
4818
5018
|
|
|
4819
|
-
var styles$
|
|
5019
|
+
var styles$8 = {"avatar":"avatar-tW-wn"};
|
|
4820
5020
|
|
|
4821
5021
|
function Avatar({
|
|
4822
5022
|
env,
|
|
@@ -4826,7 +5026,7 @@ function Avatar({
|
|
|
4826
5026
|
}) {
|
|
4827
5027
|
const url = incomingUrl || `${pcoUrl$1(env)("people")}/static/no_photo_thumbnail_gray.svg`;
|
|
4828
5028
|
return /*#__PURE__*/React__default.createElement("div", {
|
|
4829
|
-
className: styles$
|
|
5029
|
+
className: styles$8.avatar,
|
|
4830
5030
|
style: {
|
|
4831
5031
|
backgroundImage: `url(${url})`,
|
|
4832
5032
|
...style
|
|
@@ -32585,7 +32785,7 @@ function requireReactDom () {
|
|
|
32585
32785
|
|
|
32586
32786
|
var reactDomExports = requireReactDom();
|
|
32587
32787
|
|
|
32588
|
-
var styles$
|
|
32788
|
+
var styles$7 = {"trigger":"trigger-Th-Up","layer":"layer-ck6BD","layer--visible":"layer--visible-4E2p1","panel":"panel-vobWE","header":"header-xndfZ","heading":"heading-uXyQf","close":"close-pK0Lk","list":"list-zWvrn","link":"link-Kf3qj","current":"current-g3Ey-","accounts-item":"accounts-item-eQqDD","accounts-link":"accounts-link-SeIbD"};
|
|
32589
32789
|
|
|
32590
32790
|
const AppsSwitcher = ({
|
|
32591
32791
|
appName,
|
|
@@ -32613,8 +32813,8 @@ const AppsSwitcher = ({
|
|
|
32613
32813
|
}, /*#__PURE__*/React__default.createElement("a", {
|
|
32614
32814
|
"aria-current": name === appName ? "page" : undefined,
|
|
32615
32815
|
"aria-label": name,
|
|
32616
|
-
className: classnames(styles$
|
|
32617
|
-
[styles$
|
|
32816
|
+
className: classnames(styles$7.link, {
|
|
32817
|
+
[styles$7.current]: name === appName
|
|
32618
32818
|
}),
|
|
32619
32819
|
"data-pendo": `app-switcher--change-app--${name}`,
|
|
32620
32820
|
"data-turbolinks": false,
|
|
@@ -32626,48 +32826,48 @@ const AppsSwitcher = ({
|
|
|
32626
32826
|
"aria-expanded": visible,
|
|
32627
32827
|
"aria-haspopup": "true",
|
|
32628
32828
|
"aria-label": `App switcher: ${appName}`,
|
|
32629
|
-
className: styles$
|
|
32829
|
+
className: styles$7.trigger,
|
|
32630
32830
|
onClick: onTriggerClick,
|
|
32631
32831
|
type: "button"
|
|
32632
32832
|
}, /*#__PURE__*/React__default.createElement(ColorAppIcon, {
|
|
32633
32833
|
appName: appName.replace(/[\s-]/, "")
|
|
32634
32834
|
})), /*#__PURE__*/reactDomExports.createPortal(/*#__PURE__*/React__default.createElement("div", {
|
|
32635
32835
|
ref: containerRef,
|
|
32636
|
-
className: classnames(styles$
|
|
32637
|
-
[styles$
|
|
32836
|
+
className: classnames(styles$7.layer, {
|
|
32837
|
+
[styles$7["layer--visible"]]: visible
|
|
32638
32838
|
}),
|
|
32639
32839
|
onClick: toggle,
|
|
32640
32840
|
onKeyDown: handleKeyDown
|
|
32641
32841
|
}, /*#__PURE__*/React__default.createElement("nav", {
|
|
32642
32842
|
"aria-label": "Switch app",
|
|
32643
32843
|
"aria-modal": visible,
|
|
32644
|
-
className: styles$
|
|
32844
|
+
className: styles$7.panel,
|
|
32645
32845
|
"data-pendo": "app-switcher",
|
|
32646
32846
|
onClick: event => event.stopPropagation(),
|
|
32647
32847
|
role: "dialog"
|
|
32648
32848
|
}, /*#__PURE__*/React__default.createElement("div", {
|
|
32649
|
-
className: styles$
|
|
32849
|
+
className: styles$7.header
|
|
32650
32850
|
}, /*#__PURE__*/React__default.createElement("span", {
|
|
32651
|
-
className: styles$
|
|
32851
|
+
className: styles$7.heading
|
|
32652
32852
|
}, "Switch to"), /*#__PURE__*/React__default.createElement("button", {
|
|
32653
32853
|
"aria-label": "Close",
|
|
32654
|
-
className: styles$
|
|
32854
|
+
className: styles$7.close,
|
|
32655
32855
|
onClick: toggle,
|
|
32656
32856
|
type: "button"
|
|
32657
32857
|
}, /*#__PURE__*/React__default.createElement(X, {
|
|
32658
32858
|
"aria-hidden": "true",
|
|
32659
32859
|
fill: "var(--t-icon-color-dim)"
|
|
32660
32860
|
}))), /*#__PURE__*/React__default.createElement("ul", {
|
|
32661
|
-
className: styles$
|
|
32861
|
+
className: styles$7.list,
|
|
32662
32862
|
role: "list"
|
|
32663
32863
|
}, homeProduct && productLink(homeProduct.name), restProducts.map(({
|
|
32664
32864
|
name
|
|
32665
32865
|
}) => productLink(name)), accountsProduct && (/*#__PURE__*/React__default.createElement("li", {
|
|
32666
|
-
className: styles$
|
|
32866
|
+
className: styles$7["accounts-item"]
|
|
32667
32867
|
}, /*#__PURE__*/React__default.createElement("a", {
|
|
32668
32868
|
"aria-current": appName === "Accounts" ? "page" : undefined,
|
|
32669
32869
|
"aria-label": "Account settings",
|
|
32670
|
-
className: classnames(styles$
|
|
32870
|
+
className: classnames(styles$7.link, styles$7["accounts-link"]),
|
|
32671
32871
|
"data-pendo": "app-switcher--change-app--Accounts",
|
|
32672
32872
|
"data-turbolinks": false,
|
|
32673
32873
|
href: pcoUrl$1(env)("accounts") + getAppsSwitchProductPath(accountsProduct.name)
|
|
@@ -32690,14 +32890,14 @@ ClientStorage.defaultProps = {
|
|
|
32690
32890
|
method: "localStorage"
|
|
32691
32891
|
};
|
|
32692
32892
|
|
|
32693
|
-
var styles$
|
|
32893
|
+
var styles$6 = {"appmenu-button":"appmenu-button-b0UvT","appmenu-layer":"appmenu-layer-0IQpN","appmenu-container":"appmenu-container-gackd","appmenu-scroll":"appmenu-scroll-mPSx0","appmenu-list":"appmenu-list-h7clX","appmenu-list-a":"appmenu-list-a-rGIQN","appmenu-list-a--top":"appmenu-list-a--top-RT-2F","appmenu-list-a--bottom":"appmenu-list-a--bottom-YDeBC","appmenu-close-button":"appmenu-close-button-vv9bV","current-route":"current-route-wpJgz","is-active":"is-active-wy8Bg","routes":"routes-Nmt1S","route":"route-kkknQ","profile-button":"profile-button-a2WO2"};
|
|
32694
32894
|
|
|
32695
32895
|
function AccountSettingsLink({
|
|
32696
32896
|
env
|
|
32697
32897
|
}) {
|
|
32698
32898
|
const accountsName = "accounts";
|
|
32699
32899
|
return /*#__PURE__*/React__default.createElement("a", {
|
|
32700
|
-
className: classnames(styles$
|
|
32900
|
+
className: classnames(styles$6["appmenu-list-a"], styles$6["appmenu-list-a--bottom"]),
|
|
32701
32901
|
"data-pendo": `app-switcher--change-app--${accountsName}`,
|
|
32702
32902
|
"data-turbolinks": false,
|
|
32703
32903
|
href: pcoUrl$1(env)(accountsName) + getAppsSwitchProductPath(accountsName)
|
|
@@ -32720,7 +32920,7 @@ const CloseMenuButton = ({
|
|
|
32720
32920
|
}) => (/*#__PURE__*/React__default.createElement("button", {
|
|
32721
32921
|
"aria-label": "Close menu",
|
|
32722
32922
|
type: "button",
|
|
32723
|
-
className: styles$
|
|
32923
|
+
className: styles$6["appmenu-close-button"],
|
|
32724
32924
|
style: style
|
|
32725
32925
|
}, /*#__PURE__*/React__default.createElement(X, {
|
|
32726
32926
|
fill: color,
|
|
@@ -32781,7 +32981,7 @@ const LegacyAppsSwitcher = ({
|
|
|
32781
32981
|
"aria-haspopup": "true",
|
|
32782
32982
|
"aria-label": `App switcher: ${appName}`,
|
|
32783
32983
|
type: "button",
|
|
32784
|
-
className: styles$
|
|
32984
|
+
className: styles$6["appmenu-button"],
|
|
32785
32985
|
onClick: onTriggerClick
|
|
32786
32986
|
}, /*#__PURE__*/React__default.createElement(ColorAppIcon, {
|
|
32787
32987
|
appName: appName.replace(/[\s-]/, "")
|
|
@@ -32789,7 +32989,7 @@ const LegacyAppsSwitcher = ({
|
|
|
32789
32989
|
item: "Topbar:Small:Menus:Products:visibility",
|
|
32790
32990
|
render: () => (/*#__PURE__*/React__default.createElement("div", {
|
|
32791
32991
|
ref: appMenuRef,
|
|
32792
|
-
className: styles$
|
|
32992
|
+
className: styles$6["appmenu-layer"],
|
|
32793
32993
|
style: {
|
|
32794
32994
|
...(visible && {
|
|
32795
32995
|
display: "block"
|
|
@@ -32806,37 +33006,37 @@ const LegacyAppsSwitcher = ({
|
|
|
32806
33006
|
e.stopPropagation();
|
|
32807
33007
|
}
|
|
32808
33008
|
},
|
|
32809
|
-
className: styles$
|
|
33009
|
+
className: styles$6["appmenu-container"]
|
|
32810
33010
|
}, /*#__PURE__*/React__default.createElement("a", {
|
|
32811
33011
|
"aria-current": appName === (homeProduct === null || homeProduct === void 0 ? void 0 : homeProduct.name) ? "page" : undefined,
|
|
32812
33012
|
"aria-label": homeProduct ? homeProduct.name : appName,
|
|
32813
33013
|
"data-pendo": `app-switcher--change-app--${homeProduct === null || homeProduct === void 0 ? void 0 : homeProduct.name}`,
|
|
32814
33014
|
"data-turbolinks": false,
|
|
32815
|
-
className: classnames(styles$
|
|
33015
|
+
className: classnames(styles$6["appmenu-list-a"], styles$6["appmenu-list-a--top"]),
|
|
32816
33016
|
href: homeProduct ? pcoUrl$1(env)("accounts") + getAppsSwitchProductPath(homeProduct === null || homeProduct === void 0 ? void 0 : homeProduct.name) : undefined
|
|
32817
33017
|
}, /*#__PURE__*/React__default.createElement(UserMenuAppLockup, {
|
|
32818
33018
|
appName: homeProduct ? homeProduct.name : appName
|
|
32819
33019
|
})), /*#__PURE__*/React__default.createElement("div", {
|
|
32820
|
-
className: styles$
|
|
33020
|
+
className: styles$6["appmenu-scroll"]
|
|
32821
33021
|
}, /*#__PURE__*/React__default.createElement("ul", {
|
|
32822
33022
|
"data-pendo": "app-switcher",
|
|
32823
|
-
className: styles$
|
|
33023
|
+
className: styles$6["appmenu-list"]
|
|
32824
33024
|
}, appsMenuFormatter(restProducts).map(({
|
|
32825
33025
|
name
|
|
32826
33026
|
}) => (/*#__PURE__*/React__default.createElement("li", {
|
|
32827
33027
|
key: name,
|
|
32828
|
-
className: styles$
|
|
33028
|
+
className: styles$6["appmenu-list-li"]
|
|
32829
33029
|
}, /*#__PURE__*/React__default.createElement("a", {
|
|
32830
33030
|
"aria-current": name === appName ? "page" : undefined,
|
|
32831
33031
|
"aria-label": name,
|
|
32832
33032
|
"data-pendo": `app-switcher--change-app--${name}`,
|
|
32833
33033
|
"data-turbolinks": false,
|
|
32834
|
-
className: styles$
|
|
33034
|
+
className: styles$6["appmenu-list-a"],
|
|
32835
33035
|
href: pcoUrl$1(env)("accounts") + getAppsSwitchProductPath(name)
|
|
32836
33036
|
}, /*#__PURE__*/React__default.createElement(UserMenuAppLockup, {
|
|
32837
33037
|
appName: name
|
|
32838
33038
|
}))))), accountsProduct && (/*#__PURE__*/React__default.createElement("li", {
|
|
32839
|
-
className: styles$
|
|
33039
|
+
className: styles$6["appmenu-list-li"]
|
|
32840
33040
|
}, /*#__PURE__*/React__default.createElement(AccountSettingsLink, {
|
|
32841
33041
|
env: env
|
|
32842
33042
|
}))))))))
|
|
@@ -32900,11 +33100,11 @@ const SmallTopbar = ({
|
|
|
32900
33100
|
toggle: toggleProductSwitcher,
|
|
32901
33101
|
visible: productSwitcherVisible
|
|
32902
33102
|
})), routesMenuVisible && (/*#__PURE__*/React__default.createElement("div", {
|
|
32903
|
-
className: styles$
|
|
33103
|
+
className: styles$6["routes"]
|
|
32904
33104
|
}, props.routes)), /*#__PURE__*/React__default.createElement("button", {
|
|
32905
33105
|
type: "button",
|
|
32906
|
-
className: classnames(styles$
|
|
32907
|
-
[styles$
|
|
33106
|
+
className: classnames(styles$6["current-route"], {
|
|
33107
|
+
[styles$6["is-active"]]: routesMenuVisible
|
|
32908
33108
|
}),
|
|
32909
33109
|
onClick: props.routes.length > 0 ? () => {
|
|
32910
33110
|
setProductSwitcherVisible(false);
|
|
@@ -32912,7 +33112,7 @@ const SmallTopbar = ({
|
|
|
32912
33112
|
} : null
|
|
32913
33113
|
}, /*#__PURE__*/React__default.createElement(CurrentRouteComponent, null, capitalize$1(currentRouteText)), props.routes.length > 0 && /*#__PURE__*/React__default.createElement(DisclosureChevronIcon, null)), /*#__PURE__*/React__default.createElement("button", {
|
|
32914
33114
|
type: "button",
|
|
32915
|
-
className: styles$
|
|
33115
|
+
className: styles$6["profile-button"],
|
|
32916
33116
|
onClick: () => {
|
|
32917
33117
|
var _a, _b;
|
|
32918
33118
|
props.requestConnectedPeopleFetch();
|
|
@@ -32928,7 +33128,7 @@ const SmallRoute = ({
|
|
|
32928
33128
|
className,
|
|
32929
33129
|
...props
|
|
32930
33130
|
}) => (/*#__PURE__*/React__default.createElement("a", {
|
|
32931
|
-
className: classnames(styles$
|
|
33131
|
+
className: classnames(styles$6["route"], className, active && styles$6["is-active"]),
|
|
32932
33132
|
...props
|
|
32933
33133
|
}));
|
|
32934
33134
|
|
|
@@ -33067,7 +33267,7 @@ const topbarTheme = {
|
|
|
33067
33267
|
id: "topbar"
|
|
33068
33268
|
};
|
|
33069
33269
|
|
|
33070
|
-
var styles$
|
|
33270
|
+
var styles$5 = {"debug-pco-resource":"debug-pco-resource-sLteM","debug-pco-resource-header":"debug-pco-resource-header-5fEbe","debug-pco-resource-badge":"debug-pco-resource-badge-J9v1F","debug-pco-resource-title":"debug-pco-resource-title-volWV","debug-pco-resource-field":"debug-pco-resource-field-7Gitp","debug-pco-resource-field-label":"debug-pco-resource-field-label--f1xX","debug-pco-resource-field-value":"debug-pco-resource-field-value-YoX-C","debug-pco-resource-note":"debug-pco-resource-note-N5xyf","debug-pco-resource-json":"debug-pco-resource-json-Rx-JI"};
|
|
33071
33271
|
|
|
33072
33272
|
function isPresent(value) {
|
|
33073
33273
|
return value !== undefined && value.trim() !== "";
|
|
@@ -33096,11 +33296,11 @@ function Field({
|
|
|
33096
33296
|
value
|
|
33097
33297
|
}) {
|
|
33098
33298
|
return /*#__PURE__*/React__default.createElement("div", {
|
|
33099
|
-
className: styles$
|
|
33299
|
+
className: styles$5["debug-pco-resource-field"]
|
|
33100
33300
|
}, /*#__PURE__*/React__default.createElement("span", {
|
|
33101
|
-
className: styles$
|
|
33301
|
+
className: styles$5["debug-pco-resource-field-label"]
|
|
33102
33302
|
}, label), /*#__PURE__*/React__default.createElement("span", {
|
|
33103
|
-
className: styles$
|
|
33303
|
+
className: styles$5["debug-pco-resource-field-value"]
|
|
33104
33304
|
}, value));
|
|
33105
33305
|
}
|
|
33106
33306
|
// Only staff ghosting or impersonating a person get pco:staff-access-level,
|
|
@@ -33173,13 +33373,13 @@ function DebugPanel({
|
|
|
33173
33373
|
};
|
|
33174
33374
|
}, [pcrl, env]);
|
|
33175
33375
|
return /*#__PURE__*/React__default.createElement("div", {
|
|
33176
|
-
className: classnames("staff-only", styles$
|
|
33376
|
+
className: classnames("staff-only", styles$5["debug-pco-resource"])
|
|
33177
33377
|
}, /*#__PURE__*/React__default.createElement("div", {
|
|
33178
|
-
className: styles$
|
|
33378
|
+
className: styles$5["debug-pco-resource-header"]
|
|
33179
33379
|
}, /*#__PURE__*/React__default.createElement("span", {
|
|
33180
|
-
className: styles$
|
|
33380
|
+
className: styles$5["debug-pco-resource-badge"]
|
|
33181
33381
|
}, "Staff"), /*#__PURE__*/React__default.createElement("span", {
|
|
33182
|
-
className: styles$
|
|
33382
|
+
className: styles$5["debug-pco-resource-title"]
|
|
33183
33383
|
}, "Recently Visited debug")), /*#__PURE__*/React__default.createElement(Field, {
|
|
33184
33384
|
label: "pco:resource",
|
|
33185
33385
|
value: describeTag("resource", resource)
|
|
@@ -33187,29 +33387,29 @@ function DebugPanel({
|
|
|
33187
33387
|
label: "pco:recently_visitable",
|
|
33188
33388
|
value: describeTag("recently_visitable", recentlyVisitable)
|
|
33189
33389
|
}), hasInvalidResource && (/*#__PURE__*/React__default.createElement("p", {
|
|
33190
|
-
className: styles$
|
|
33390
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33191
33391
|
}, "pco:resource isn't a PCRL (product/v2/type/id), so Recently Visited ignores it.")), visit === null && (/*#__PURE__*/React__default.createElement("p", {
|
|
33192
|
-
className: styles$
|
|
33392
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33193
33393
|
}, "This page is not eligible for Recently Visited")), pcrl && hasRecentlyVisitable && (/*#__PURE__*/React__default.createElement("p", {
|
|
33194
|
-
className: styles$
|
|
33394
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33195
33395
|
}, "Both tags are present \u2014 pco:resource takes precedence for the Recently Visited entry.")), pcrl && lookup.status === "loading" && (/*#__PURE__*/React__default.createElement("p", {
|
|
33196
|
-
className: styles$
|
|
33396
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33197
33397
|
}, "Looking up resource\u2026")), pcrl && lookup.status === "error" && (/*#__PURE__*/React__default.createElement("p", {
|
|
33198
|
-
className: styles$
|
|
33398
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33199
33399
|
}, lookup.message)), pcrl && lookup.status === "loaded" && (/*#__PURE__*/React__default.createElement("pre", {
|
|
33200
|
-
className: styles$
|
|
33400
|
+
className: styles$5["debug-pco-resource-json"]
|
|
33201
33401
|
}, JSON.stringify(lookup.json, null, 2))), (visit === null || visit === void 0 ? void 0 : visit.type) === "page" && (/*#__PURE__*/React__default.createElement("p", {
|
|
33202
|
-
className: styles$
|
|
33402
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33203
33403
|
}, "Recently Visited title: ", visit.title)), /*#__PURE__*/React__default.createElement("span", {
|
|
33204
|
-
className: styles$
|
|
33404
|
+
className: styles$5["debug-pco-resource-field-label"]
|
|
33205
33405
|
}, "Recently Visited"), personId ? (/*#__PURE__*/React__default.createElement("pre", {
|
|
33206
|
-
className: styles$
|
|
33406
|
+
className: styles$5["debug-pco-resource-json"]
|
|
33207
33407
|
}, JSON.stringify(recentlyVisited, null, 2))) : (/*#__PURE__*/React__default.createElement("p", {
|
|
33208
|
-
className: styles$
|
|
33408
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33209
33409
|
}, "No current person, so nothing is recorded.")));
|
|
33210
33410
|
}
|
|
33211
33411
|
|
|
33212
|
-
var styles$
|
|
33412
|
+
var styles$4 = {"drawer-wrap":"drawer-wrap-J0rSL","is-visible":"is-visible-KTME8","drawer":"drawer-j7f4l"};
|
|
33213
33413
|
|
|
33214
33414
|
function Drawer({
|
|
33215
33415
|
children,
|
|
@@ -33217,7 +33417,7 @@ function Drawer({
|
|
|
33217
33417
|
}) {
|
|
33218
33418
|
return /*#__PURE__*/React__default.createElement("div", {
|
|
33219
33419
|
id: id,
|
|
33220
|
-
className: styles$
|
|
33420
|
+
className: styles$4["drawer"]
|
|
33221
33421
|
}, children);
|
|
33222
33422
|
}
|
|
33223
33423
|
function DrawerWrap({
|
|
@@ -33227,7 +33427,7 @@ function DrawerWrap({
|
|
|
33227
33427
|
...props
|
|
33228
33428
|
}) {
|
|
33229
33429
|
return /*#__PURE__*/React__default.createElement("div", {
|
|
33230
|
-
className: classnames(styles$
|
|
33430
|
+
className: classnames(styles$4["drawer-wrap"], className, isVisible && styles$4["is-visible"]),
|
|
33231
33431
|
...props
|
|
33232
33432
|
}, children);
|
|
33233
33433
|
}
|
|
@@ -33443,7 +33643,7 @@ function Icon({
|
|
|
33443
33643
|
}));
|
|
33444
33644
|
}
|
|
33445
33645
|
|
|
33446
|
-
var styles$
|
|
33646
|
+
var styles$3 = {"color-mode-switcher":"color-mode-switcher-JpczO","color-mode-switcher-label":"color-mode-switcher-label-3jPM2","color-mode-switcher-modes":"color-mode-switcher-modes-u-bic"};
|
|
33447
33647
|
|
|
33448
33648
|
const COLOR_MODES = [{
|
|
33449
33649
|
icon: "toolbar.sun",
|
|
@@ -33466,11 +33666,11 @@ function ColorModeSwitcher() {
|
|
|
33466
33666
|
const updateStore = useUpdateStore();
|
|
33467
33667
|
const [counter] = useState(() => COLOR_MODE_INSTANCE_COUNTER++);
|
|
33468
33668
|
return /*#__PURE__*/React__default.createElement("fieldset", {
|
|
33469
|
-
className: styles$
|
|
33669
|
+
className: styles$3["color-mode-switcher"]
|
|
33470
33670
|
}, /*#__PURE__*/React__default.createElement("legend", {
|
|
33471
|
-
className: styles$
|
|
33671
|
+
className: styles$3["color-mode-switcher-label"]
|
|
33472
33672
|
}, "Appearance", " "), /*#__PURE__*/React__default.createElement("div", {
|
|
33473
|
-
className: styles$
|
|
33673
|
+
className: styles$3["color-mode-switcher-modes"]
|
|
33474
33674
|
}, COLOR_MODES.map(({
|
|
33475
33675
|
icon,
|
|
33476
33676
|
label,
|
|
@@ -33740,7 +33940,7 @@ const useToolbarReducer = overrides => React.useReducer(reducer, {
|
|
|
33740
33940
|
...overrides
|
|
33741
33941
|
});
|
|
33742
33942
|
|
|
33743
|
-
var styles$
|
|
33943
|
+
var styles$2 = {"toolbar":"toolbar-KHJR5","toolbar-button":"toolbar-button-Ye4h5","is-current":"is-current-DZK47","toolbar-button--avatar":"toolbar-button--avatar-5FUik","toolbar-button--home":"toolbar-button--home-Pthd0"};
|
|
33744
33944
|
|
|
33745
33945
|
// PCO Box serves TLS by default, and a bare http:// frame inside an https
|
|
33746
33946
|
// product page is blocked as mixed content, so follow the page's scheme.
|
|
@@ -33912,7 +34112,7 @@ const DeveloperTools = {
|
|
|
33912
34112
|
name: "DeveloperTools"
|
|
33913
34113
|
};
|
|
33914
34114
|
|
|
33915
|
-
var styles = {"banner":"banner-YfQ37","stacked":"stacked-EwLRh","content":"content-lNjWf","icon":"icon-abz7q","iconShape":"iconShape-6uyot","iconSparkle":"iconSparkle-t0OzK","text":"text--Vlhl","title":"title-zOEOR","subtitle":"subtitle-ufLnk","chevron":"chevron-oOUA-","inlineContent":"inlineContent-h5sVl","sparkle":"sparkle-uuMBp","inlineTitle":"inlineTitle-ru2Rk","action":"action-fSY3n","actionLabel":"actionLabel-MRBjb","actionChevron":"actionChevron-DghlN"};
|
|
34115
|
+
var styles$1 = {"banner":"banner-YfQ37","stacked":"stacked-EwLRh","content":"content-lNjWf","icon":"icon-abz7q","iconShape":"iconShape-6uyot","iconSparkle":"iconSparkle-t0OzK","text":"text--Vlhl","title":"title-zOEOR","subtitle":"subtitle-ufLnk","chevron":"chevron-oOUA-","inlineContent":"inlineContent-h5sVl","sparkle":"sparkle-uuMBp","inlineTitle":"inlineTitle-ru2Rk","action":"action-fSY3n","actionLabel":"actionLabel-MRBjb","actionChevron":"actionChevron-DghlN"};
|
|
33916
34116
|
|
|
33917
34117
|
const SPARKLE_PATH$1 = "M3.809 10.1332C3.87511 9.95596 4.12579 9.95588 4.19181 10.1332L4.63712 11.3363C4.64169 11.3487 4.65116 11.359 4.66349 11.3637L5.86661 11.809C6.04452 11.8749 6.04455 12.126 5.86661 12.1918L4.66349 12.6371C4.65134 12.6417 4.64173 12.6513 4.63712 12.6635L4.19181 13.8666C4.12597 14.0445 3.87486 14.0445 3.809 13.8666L3.36368 12.6635C3.35905 12.6512 3.3487 12.6417 3.33634 12.6371L2.13321 12.1918C1.95588 12.1258 1.95596 11.8751 2.13321 11.809L3.33634 11.3637C3.34877 11.3591 3.35908 11.3488 3.36368 11.3363L3.809 10.1332ZM8.52091 3.33341C8.68571 2.88899 9.31518 2.88891 9.47989 3.33341L10.5922 6.34122C10.6037 6.37226 10.6285 6.39712 10.6596 6.4086L13.6664 7.52091C14.1113 7.68553 14.1113 8.31528 13.6664 8.47989L10.6596 9.5922C10.6285 9.6037 10.6037 9.6285 10.5922 9.65958L9.47989 12.6664C9.31528 13.1113 8.68553 13.1113 8.52091 12.6664L7.4086 9.65958C7.39712 9.62853 7.37226 9.60371 7.34122 9.5922L4.33341 8.47989C3.88891 8.31518 3.88899 7.68571 4.33341 7.52091L7.34122 6.4086C7.3723 6.3971 7.3971 6.3723 7.4086 6.34122L8.52091 3.33341ZM3.809 2.13321C3.87511 1.95596 4.12579 1.95588 4.19181 2.13321L4.63712 3.33634C4.64169 3.3487 4.65116 3.35905 4.66349 3.36368L5.86661 3.809C6.04452 3.87486 6.04455 4.12597 5.86661 4.19181L4.66349 4.63712C4.65134 4.64173 4.64173 4.65134 4.63712 4.66349L4.19181 5.86661C4.12597 6.04455 3.87486 6.04452 3.809 5.86661L3.36368 4.66349C3.35905 4.65116 3.3487 4.64169 3.33634 4.63712L2.13321 4.19181C1.95588 4.12579 1.95596 3.87511 2.13321 3.809L3.33634 3.36368C3.34877 3.35908 3.35908 3.34877 3.36368 3.33634L3.809 2.13321Z";
|
|
33918
34118
|
// Same stops as the AskAIBanner border gradient, so the two stay in sync.
|
|
@@ -33972,8 +34172,8 @@ function AskAIBanner(props) {
|
|
|
33972
34172
|
var _a, _b;
|
|
33973
34173
|
const isStacked = props.variant !== "inline";
|
|
33974
34174
|
return /*#__PURE__*/React__default.createElement("button", {
|
|
33975
|
-
className: classnames(styles.banner, {
|
|
33976
|
-
[styles.stacked]: isStacked
|
|
34175
|
+
className: classnames(styles$1.banner, {
|
|
34176
|
+
[styles$1.stacked]: isStacked
|
|
33977
34177
|
}),
|
|
33978
34178
|
onClick: () => props.onClick(),
|
|
33979
34179
|
type: "button"
|
|
@@ -33988,15 +34188,15 @@ function StackedContent({
|
|
|
33988
34188
|
title
|
|
33989
34189
|
}) {
|
|
33990
34190
|
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("span", {
|
|
33991
|
-
className: styles.content
|
|
34191
|
+
className: styles$1.content
|
|
33992
34192
|
}, /*#__PURE__*/React__default.createElement(AskAIIcon, null), /*#__PURE__*/React__default.createElement("span", {
|
|
33993
|
-
className: styles.text
|
|
34193
|
+
className: styles$1.text
|
|
33994
34194
|
}, /*#__PURE__*/React__default.createElement("span", {
|
|
33995
|
-
className: styles.title
|
|
34195
|
+
className: styles$1.title
|
|
33996
34196
|
}, title), /*#__PURE__*/React__default.createElement("span", {
|
|
33997
|
-
className: styles.subtitle
|
|
34197
|
+
className: styles$1.subtitle
|
|
33998
34198
|
}, "Support answers tailored to you"))), /*#__PURE__*/React__default.createElement(Icon$1, {
|
|
33999
|
-
className: styles.chevron,
|
|
34199
|
+
className: styles$1.chevron,
|
|
34000
34200
|
name: "general.rightChevron",
|
|
34001
34201
|
size: "16px"
|
|
34002
34202
|
}));
|
|
@@ -34006,18 +34206,18 @@ function InlineContent({
|
|
|
34006
34206
|
title
|
|
34007
34207
|
}) {
|
|
34008
34208
|
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("span", {
|
|
34009
|
-
className: styles.inlineContent
|
|
34209
|
+
className: styles$1.inlineContent
|
|
34010
34210
|
}, /*#__PURE__*/React__default.createElement(SparkleIcon, {
|
|
34011
|
-
className: styles.sparkle,
|
|
34211
|
+
className: styles$1.sparkle,
|
|
34012
34212
|
fill: "currentColor"
|
|
34013
34213
|
}), /*#__PURE__*/React__default.createElement("span", {
|
|
34014
|
-
className: styles.inlineTitle
|
|
34214
|
+
className: styles$1.inlineTitle
|
|
34015
34215
|
}, title)), /*#__PURE__*/React__default.createElement("span", {
|
|
34016
|
-
className: styles.action
|
|
34216
|
+
className: styles$1.action
|
|
34017
34217
|
}, /*#__PURE__*/React__default.createElement("span", {
|
|
34018
|
-
className: styles.actionLabel
|
|
34218
|
+
className: styles$1.actionLabel
|
|
34019
34219
|
}, action), /*#__PURE__*/React__default.createElement(Icon$1, {
|
|
34020
|
-
className: styles.actionChevron,
|
|
34220
|
+
className: styles$1.actionChevron,
|
|
34021
34221
|
name: "general.rightChevron",
|
|
34022
34222
|
size: "14px"
|
|
34023
34223
|
})));
|
|
@@ -34034,9 +34234,9 @@ function AskAIIcon() {
|
|
|
34034
34234
|
const borderA = `${uid}-border-a`;
|
|
34035
34235
|
const borderB = `${uid}-border-b`;
|
|
34036
34236
|
return /*#__PURE__*/React__default.createElement("span", {
|
|
34037
|
-
className: styles.icon
|
|
34237
|
+
className: styles$1.icon
|
|
34038
34238
|
}, /*#__PURE__*/React__default.createElement("svg", {
|
|
34039
|
-
className: styles.iconShape,
|
|
34239
|
+
className: styles$1.iconShape,
|
|
34040
34240
|
viewBox: "0 0 32 32",
|
|
34041
34241
|
fill: "none",
|
|
34042
34242
|
"aria-hidden": "true"
|
|
@@ -34207,7 +34407,7 @@ function AskAIIcon() {
|
|
|
34207
34407
|
stopColor: "#D48B91",
|
|
34208
34408
|
stopOpacity: "0"
|
|
34209
34409
|
})))), /*#__PURE__*/React__default.createElement("svg", {
|
|
34210
|
-
className: styles.iconSparkle,
|
|
34410
|
+
className: styles$1.iconSparkle,
|
|
34211
34411
|
viewBox: "0 0 19.4995 17.9999",
|
|
34212
34412
|
"aria-hidden": "true"
|
|
34213
34413
|
}, /*#__PURE__*/React__default.createElement("path", {
|
|
@@ -34216,6 +34416,23 @@ function AskAIIcon() {
|
|
|
34216
34416
|
})));
|
|
34217
34417
|
}
|
|
34218
34418
|
|
|
34419
|
+
var styles = {"button":"button-2NVI1","sparkle":"sparkle-ruJGP","label":"label-4NUGB"};
|
|
34420
|
+
|
|
34421
|
+
function AskAIButton({
|
|
34422
|
+
onClick,
|
|
34423
|
+
text = "Ask AI"
|
|
34424
|
+
}) {
|
|
34425
|
+
return /*#__PURE__*/React__default.createElement("button", {
|
|
34426
|
+
className: styles.button,
|
|
34427
|
+
onClick: () => onClick(),
|
|
34428
|
+
type: "button"
|
|
34429
|
+
}, /*#__PURE__*/React__default.createElement(SparkleIcon, {
|
|
34430
|
+
className: styles.sparkle
|
|
34431
|
+
}), /*#__PURE__*/React__default.createElement("span", {
|
|
34432
|
+
className: styles.label
|
|
34433
|
+
}, text));
|
|
34434
|
+
}
|
|
34435
|
+
|
|
34219
34436
|
// The article heading lives in the deepest populated hierarchy level (lvl0 is
|
|
34220
34437
|
// always "Planning Center").
|
|
34221
34438
|
const hitTitle = hierarchy => (hierarchy === null || hierarchy === void 0 ? void 0 : hierarchy.lvl6) || (hierarchy === null || hierarchy === void 0 ? void 0 : hierarchy.lvl5) || (hierarchy === null || hierarchy === void 0 ? void 0 : hierarchy.lvl4) || (hierarchy === null || hierarchy === void 0 ? void 0 : hierarchy.lvl3) || (hierarchy === null || hierarchy === void 0 ? void 0 : hierarchy.lvl2) || (hierarchy === null || hierarchy === void 0 ? void 0 : hierarchy.lvl1) || (hierarchy === null || hierarchy === void 0 ? void 0 : hierarchy.lvl0) || "";
|
|
@@ -34526,6 +34743,18 @@ function HelpdeskComponent({
|
|
|
34526
34743
|
}
|
|
34527
34744
|
}
|
|
34528
34745
|
},
|
|
34746
|
+
AskAIButton: {
|
|
34747
|
+
component: AskAIButton,
|
|
34748
|
+
properties: {
|
|
34749
|
+
onClick: {
|
|
34750
|
+
event: false,
|
|
34751
|
+
type: Function
|
|
34752
|
+
},
|
|
34753
|
+
text: {
|
|
34754
|
+
type: String
|
|
34755
|
+
}
|
|
34756
|
+
}
|
|
34757
|
+
},
|
|
34529
34758
|
CalendlyWidget: {
|
|
34530
34759
|
component: CalendlyWidget,
|
|
34531
34760
|
properties: {
|
|
@@ -36034,18 +36263,18 @@ function Toolbar({
|
|
|
36034
36263
|
return /*#__PURE__*/React__default.createElement(ThemeProvider, {
|
|
36035
36264
|
theme: topbarTheme
|
|
36036
36265
|
}, /*#__PURE__*/React__default.createElement(UiExtensionDataContext, null, /*#__PURE__*/React__default.createElement("div", {
|
|
36037
|
-
className: styles$
|
|
36266
|
+
className: styles$2["toolbar"],
|
|
36038
36267
|
"data-pendo": "toolbar-profile-support-wrapper"
|
|
36039
36268
|
}, /*#__PURE__*/React__default.createElement(ToolbarButton, {
|
|
36040
36269
|
"aria-label": `Open ${userName}’s profile drawer`,
|
|
36041
36270
|
onClick: () => toggleDrawer(Profile),
|
|
36042
36271
|
"data-pendo": "toolbar-profile",
|
|
36043
|
-
className: styles$
|
|
36272
|
+
className: styles$2["toolbar-button--avatar"]
|
|
36044
36273
|
}, /*#__PURE__*/React__default.createElement(Avatar, {
|
|
36045
36274
|
env: env,
|
|
36046
36275
|
url: userAvatarPath
|
|
36047
36276
|
})), /*#__PURE__*/React__default.createElement(ToolbarButton, {
|
|
36048
|
-
className: styles$
|
|
36277
|
+
className: styles$2["toolbar-button--home"],
|
|
36049
36278
|
onClick: () => closeDrawer()
|
|
36050
36279
|
}, /*#__PURE__*/React__default.createElement(Icon$1, {
|
|
36051
36280
|
name: "toolbar.planningCenter",
|
|
@@ -36082,7 +36311,7 @@ const ToolbarButton = ({
|
|
|
36082
36311
|
}) => {
|
|
36083
36312
|
return /*#__PURE__*/React__default.createElement("button", {
|
|
36084
36313
|
type: "button",
|
|
36085
|
-
className: classnames(styles$
|
|
36314
|
+
className: classnames(styles$2["toolbar-button"], className, current && styles$2["is-current"]),
|
|
36086
36315
|
onClick: onClick,
|
|
36087
36316
|
"data-toolbar-link": "true",
|
|
36088
36317
|
...restProps
|