@planningcenter/topbar 12.4.0-rc.0 → 12.4.0-rc.2
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 +79 -12
- package/dist/topbar.css.map +1 -1
- package/dist/topbar.js +1091 -854
- package/dist/topbar.js.map +1 -1
- package/dist/topbar.modern.js +1091 -854
- package/dist/topbar.modern.js.map +1 -1
- package/dist/topbar.module.js +1091 -854
- package/dist/topbar.module.js.map +1 -1
- package/dist/topbar.umd.js +1091 -854
- package/dist/topbar.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/topbar.js
CHANGED
|
@@ -41,7 +41,7 @@ var churchCenter__namespace = /*#__PURE__*/_interopNamespaceDefault(churchCenter
|
|
|
41
41
|
var people__namespace = /*#__PURE__*/_interopNamespaceDefault(people);
|
|
42
42
|
var toolbar__namespace = /*#__PURE__*/_interopNamespaceDefault(toolbar);
|
|
43
43
|
|
|
44
|
-
var styles$
|
|
44
|
+
var styles$m = {"accessibility-toolbar":"accessibility-toolbar-jgjUG"};
|
|
45
45
|
|
|
46
46
|
const AccessibilityToolbar = ({
|
|
47
47
|
children
|
|
@@ -53,7 +53,7 @@ const AccessibilityToolbar = ({
|
|
|
53
53
|
(_a = document.getElementById("main-content")) === null || _a === void 0 ? void 0 : _a.focus();
|
|
54
54
|
};
|
|
55
55
|
return /*#__PURE__*/React.createElement("div", {
|
|
56
|
-
className: styles$
|
|
56
|
+
className: styles$m["accessibility-toolbar"]
|
|
57
57
|
}, /*#__PURE__*/React.createElement(tapestry.Link, {
|
|
58
58
|
href: "#main-content",
|
|
59
59
|
kind: "primary",
|
|
@@ -372,11 +372,11 @@ const X = ({
|
|
|
372
372
|
fill: fill
|
|
373
373
|
})));
|
|
374
374
|
|
|
375
|
-
var styles$
|
|
375
|
+
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"};
|
|
376
376
|
|
|
377
377
|
function AnnouncementIcon() {
|
|
378
378
|
return /*#__PURE__*/React.createElement("svg", {
|
|
379
|
-
className: styles$
|
|
379
|
+
className: styles$l["announcement-icon"],
|
|
380
380
|
width: "16",
|
|
381
381
|
height: "16",
|
|
382
382
|
viewBox: "0 0 16 16",
|
|
@@ -501,7 +501,7 @@ let StyledAnnouncement$1 = class StyledAnnouncement extends React.Component {
|
|
|
501
501
|
} = this.props;
|
|
502
502
|
const isPromo = variant === "promo";
|
|
503
503
|
return /*#__PURE__*/React.createElement("div", {
|
|
504
|
-
className: classnames(styles$
|
|
504
|
+
className: classnames(styles$l["announcement"], styles$l[`announcement--${variant !== null && variant !== void 0 ? variant : "platform"}`]),
|
|
505
505
|
"data-announcement-variant": variant !== null && variant !== void 0 ? variant : "platform",
|
|
506
506
|
style: isPromo ? {
|
|
507
507
|
backgroundImage: promoBackgroundUrl,
|
|
@@ -510,7 +510,7 @@ let StyledAnnouncement$1 = class StyledAnnouncement extends React.Component {
|
|
|
510
510
|
backgroundSize: "cover"
|
|
511
511
|
} : undefined
|
|
512
512
|
}, !isPromo && /*#__PURE__*/React.createElement(AnnouncementIcon, null), /*#__PURE__*/React.createElement("div", {
|
|
513
|
-
className: styles$
|
|
513
|
+
className: styles$l["announcement-content"],
|
|
514
514
|
style: style,
|
|
515
515
|
...platformProps
|
|
516
516
|
}));
|
|
@@ -559,7 +559,7 @@ function StyledDismissButton$1({
|
|
|
559
559
|
const isPromo = variant === "promo";
|
|
560
560
|
return /*#__PURE__*/React.createElement("button", {
|
|
561
561
|
type: "button",
|
|
562
|
-
className: classnames(styles$
|
|
562
|
+
className: classnames(styles$l.dismiss, isPromo && styles$l["dismiss--promo"]),
|
|
563
563
|
...props
|
|
564
564
|
}, /*#__PURE__*/React.createElement(X, {
|
|
565
565
|
fill: isPromo ? "currentColor" : "white",
|
|
@@ -639,9 +639,9 @@ class StyledAnnouncement extends React.Component {
|
|
|
639
639
|
...platformProps
|
|
640
640
|
} = this.props;
|
|
641
641
|
return /*#__PURE__*/React.createElement("div", {
|
|
642
|
-
className: styles$
|
|
642
|
+
className: styles$l["announcement"]
|
|
643
643
|
}, /*#__PURE__*/React.createElement(AnnouncementIcon, null), /*#__PURE__*/React.createElement("div", {
|
|
644
|
-
className: styles$
|
|
644
|
+
className: styles$l["announcement-content"],
|
|
645
645
|
style: style,
|
|
646
646
|
...platformProps
|
|
647
647
|
}));
|
|
@@ -652,7 +652,7 @@ function StyledDismissButton({
|
|
|
652
652
|
}) {
|
|
653
653
|
return /*#__PURE__*/React.createElement("button", {
|
|
654
654
|
type: "button",
|
|
655
|
-
className: styles$
|
|
655
|
+
className: styles$l.dismiss,
|
|
656
656
|
...props
|
|
657
657
|
}, /*#__PURE__*/React.createElement(X, {
|
|
658
658
|
fill: "white",
|
|
@@ -1774,7 +1774,7 @@ function MonoAppIcon({
|
|
|
1774
1774
|
})) : (/*#__PURE__*/React.createElement("div", null, "appName not supported")));
|
|
1775
1775
|
}
|
|
1776
1776
|
|
|
1777
|
-
var styles$
|
|
1777
|
+
var styles$k = {"icon-slot":"icon-slot-m3ZIk","name":"name-UNqbW"};
|
|
1778
1778
|
|
|
1779
1779
|
const AppRow = ({
|
|
1780
1780
|
appName,
|
|
@@ -1783,24 +1783,24 @@ const AppRow = ({
|
|
|
1783
1783
|
}) => {
|
|
1784
1784
|
if (appName === "Accounts") {
|
|
1785
1785
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
|
|
1786
|
-
className: styles$
|
|
1786
|
+
className: styles$k["icon-slot"]
|
|
1787
1787
|
}, /*#__PURE__*/React.createElement(MonoAppIcon, {
|
|
1788
1788
|
appName: "Accounts",
|
|
1789
1789
|
colors: colors,
|
|
1790
1790
|
size: iconSize
|
|
1791
1791
|
})), /*#__PURE__*/React.createElement("span", {
|
|
1792
|
-
className: styles$
|
|
1792
|
+
className: styles$k.name
|
|
1793
1793
|
}, "Account settings"));
|
|
1794
1794
|
}
|
|
1795
1795
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ColorAppIcon, {
|
|
1796
1796
|
appName: appName.replace(/[\s-]/, ""),
|
|
1797
1797
|
size: iconSize
|
|
1798
1798
|
}), /*#__PURE__*/React.createElement("span", {
|
|
1799
|
-
className: styles$
|
|
1799
|
+
className: styles$k.name
|
|
1800
1800
|
}, appName));
|
|
1801
1801
|
};
|
|
1802
1802
|
|
|
1803
|
-
var styles$
|
|
1803
|
+
var styles$j = {"button":"button-XMbeQ","label":"label-884of","chevron":"chevron-RRg9k"};
|
|
1804
1804
|
|
|
1805
1805
|
const AppsButton = /*#__PURE__*/React.forwardRef(({
|
|
1806
1806
|
appName,
|
|
@@ -1812,10 +1812,10 @@ const AppsButton = /*#__PURE__*/React.forwardRef(({
|
|
|
1812
1812
|
...restProps,
|
|
1813
1813
|
ref: ref,
|
|
1814
1814
|
"aria-label": `App switcher: ${appName}`,
|
|
1815
|
-
className: styles$
|
|
1815
|
+
className: styles$j.button,
|
|
1816
1816
|
kind: "neutral-inline",
|
|
1817
1817
|
label: /*#__PURE__*/React.createElement("span", {
|
|
1818
|
-
className: styles$
|
|
1818
|
+
className: styles$j.label
|
|
1819
1819
|
}, isAccounts ? "Account settings" : appName),
|
|
1820
1820
|
prefix: isAccounts ? (/*#__PURE__*/React.createElement(MonoAppIcon, {
|
|
1821
1821
|
appName: "Accounts",
|
|
@@ -1826,13 +1826,13 @@ const AppsButton = /*#__PURE__*/React.forwardRef(({
|
|
|
1826
1826
|
size: 22
|
|
1827
1827
|
})),
|
|
1828
1828
|
suffix: /*#__PURE__*/React.createElement(DisclosureChevronIcon, {
|
|
1829
|
-
className: `${styles$
|
|
1829
|
+
className: `${styles$j.chevron} symbol`
|
|
1830
1830
|
})
|
|
1831
1831
|
});
|
|
1832
1832
|
});
|
|
1833
1833
|
AppsButton.displayName = "AppsButton";
|
|
1834
1834
|
|
|
1835
|
-
var styles$
|
|
1835
|
+
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"};
|
|
1836
1836
|
|
|
1837
1837
|
const AppsMenu = ({
|
|
1838
1838
|
appName,
|
|
@@ -1853,8 +1853,8 @@ const AppsMenu = ({
|
|
|
1853
1853
|
}, /*#__PURE__*/React.createElement("a", {
|
|
1854
1854
|
"aria-current": isCurrent ? "page" : undefined,
|
|
1855
1855
|
"aria-label": name,
|
|
1856
|
-
className: classnames(styles$
|
|
1857
|
-
[styles$
|
|
1856
|
+
className: classnames(styles$i.link, {
|
|
1857
|
+
[styles$i.current]: isCurrent
|
|
1858
1858
|
}),
|
|
1859
1859
|
"data-pendo": `app-switcher--change-app--${name}`,
|
|
1860
1860
|
"data-turbolinks": false,
|
|
@@ -1870,22 +1870,22 @@ const AppsMenu = ({
|
|
|
1870
1870
|
colors: colors
|
|
1871
1871
|
})), /*#__PURE__*/React.createElement(tapestry.PopoverContent, {
|
|
1872
1872
|
"aria-label": "Switch app",
|
|
1873
|
-
className: styles$
|
|
1873
|
+
className: styles$i["popover-content"],
|
|
1874
1874
|
placement: "bottom start"
|
|
1875
1875
|
}, /*#__PURE__*/React.createElement("nav", {
|
|
1876
|
-
className: styles$
|
|
1876
|
+
className: styles$i.nav,
|
|
1877
1877
|
"data-pendo": "app-switcher"
|
|
1878
1878
|
}, /*#__PURE__*/React.createElement("ul", {
|
|
1879
|
-
className: styles$
|
|
1879
|
+
className: styles$i.list,
|
|
1880
1880
|
role: "list"
|
|
1881
1881
|
}, homeProduct && renderProductLink(homeProduct.name), restProducts.map(({
|
|
1882
1882
|
name
|
|
1883
1883
|
}) => renderProductLink(name)), accountsProduct && (/*#__PURE__*/React.createElement("li", {
|
|
1884
|
-
className: styles$
|
|
1884
|
+
className: styles$i["accounts-item"]
|
|
1885
1885
|
}, /*#__PURE__*/React.createElement("a", {
|
|
1886
1886
|
"aria-current": appName === accountsProduct.name ? "page" : undefined,
|
|
1887
1887
|
"aria-label": "Account settings",
|
|
1888
|
-
className: classnames(styles$
|
|
1888
|
+
className: classnames(styles$i.link, styles$i["accounts-link"]),
|
|
1889
1889
|
"data-pendo": "app-switcher--change-app--Accounts",
|
|
1890
1890
|
"data-turbolinks": false,
|
|
1891
1891
|
href: pcoUrl$1(env)("accounts") + getAppsSwitchProductPath(accountsProduct.name)
|
|
@@ -2093,7 +2093,7 @@ function MonoAppName({
|
|
|
2093
2093
|
})) : (/*#__PURE__*/React.createElement("div", null, "appName not supported"));
|
|
2094
2094
|
}
|
|
2095
2095
|
|
|
2096
|
-
var styles$
|
|
2096
|
+
var styles$h = {"unbutton":"unbutton-lEbTR"};
|
|
2097
2097
|
|
|
2098
2098
|
function Unbutton({
|
|
2099
2099
|
className,
|
|
@@ -2102,7 +2102,7 @@ function Unbutton({
|
|
|
2102
2102
|
...nativeProps
|
|
2103
2103
|
}) {
|
|
2104
2104
|
return /*#__PURE__*/React.createElement("button", {
|
|
2105
|
-
className: classnames(styles$
|
|
2105
|
+
className: classnames(styles$h.unbutton, className),
|
|
2106
2106
|
style: {
|
|
2107
2107
|
...style
|
|
2108
2108
|
},
|
|
@@ -2111,7 +2111,7 @@ function Unbutton({
|
|
|
2111
2111
|
});
|
|
2112
2112
|
}
|
|
2113
2113
|
|
|
2114
|
-
var styles$
|
|
2114
|
+
var styles$g = {"button":"button-wsipc","button-accounts":"button-accounts-zQmBM","chevron":"chevron-obyBF","is-expanded":"is-expanded-PoCQ9"};
|
|
2115
2115
|
|
|
2116
2116
|
const LegacyAppsButton = ({
|
|
2117
2117
|
appName,
|
|
@@ -2126,8 +2126,8 @@ const LegacyAppsButton = ({
|
|
|
2126
2126
|
"aria-haspopup": "true",
|
|
2127
2127
|
"aria-label": `App switcher: ${appName}`,
|
|
2128
2128
|
onClick: onClick,
|
|
2129
|
-
className: classnames(styles$
|
|
2130
|
-
[styles$
|
|
2129
|
+
className: classnames(styles$g.button, {
|
|
2130
|
+
[styles$g["is-expanded"]]: expanded
|
|
2131
2131
|
}),
|
|
2132
2132
|
type: "button",
|
|
2133
2133
|
style: style,
|
|
@@ -2136,7 +2136,7 @@ const LegacyAppsButton = ({
|
|
|
2136
2136
|
appName: "Accounts",
|
|
2137
2137
|
colors: colors
|
|
2138
2138
|
}), /*#__PURE__*/React.createElement("div", {
|
|
2139
|
-
className: styles$
|
|
2139
|
+
className: styles$g["button-accounts"]
|
|
2140
2140
|
}, "Account settings"))) : (/*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ColorAppIcon, {
|
|
2141
2141
|
appName: appName.replace(/[\s-]/, "")
|
|
2142
2142
|
}), /*#__PURE__*/React.createElement(MonoAppName, {
|
|
@@ -2144,7 +2144,7 @@ const LegacyAppsButton = ({
|
|
|
2144
2144
|
key: "mono-app-name",
|
|
2145
2145
|
size: 24
|
|
2146
2146
|
}))), /*#__PURE__*/React.createElement(DisclosureChevronIcon, {
|
|
2147
|
-
className: styles$
|
|
2147
|
+
className: styles$g.chevron,
|
|
2148
2148
|
key: "disclosure-chevron-icon"
|
|
2149
2149
|
}));
|
|
2150
2150
|
};
|
|
@@ -2277,7 +2277,7 @@ class Outsider extends React.Component {
|
|
|
2277
2277
|
}
|
|
2278
2278
|
}
|
|
2279
2279
|
|
|
2280
|
-
var styles$
|
|
2280
|
+
var styles$f = {"popup-root":"popup-root-SeL5u","popup":"popup-nQXYW","is-visible":"is-visible-3DAUP"};
|
|
2281
2281
|
|
|
2282
2282
|
const Popup = ({
|
|
2283
2283
|
className,
|
|
@@ -2285,8 +2285,8 @@ const Popup = ({
|
|
|
2285
2285
|
isVisible,
|
|
2286
2286
|
...nativeProps
|
|
2287
2287
|
}) => (/*#__PURE__*/React.createElement(C, {
|
|
2288
|
-
className: classnames(styles$
|
|
2289
|
-
[styles$
|
|
2288
|
+
className: classnames(styles$f.popup, {
|
|
2289
|
+
[styles$f["is-visible"]]: isVisible
|
|
2290
2290
|
}, className),
|
|
2291
2291
|
...nativeProps
|
|
2292
2292
|
}));
|
|
@@ -2294,11 +2294,11 @@ const Popup = ({
|
|
|
2294
2294
|
const PopupRoot = ({
|
|
2295
2295
|
...nativeProps
|
|
2296
2296
|
}) => (/*#__PURE__*/React.createElement("div", {
|
|
2297
|
-
className: styles$
|
|
2297
|
+
className: styles$f["popup-root"],
|
|
2298
2298
|
...nativeProps
|
|
2299
2299
|
}));
|
|
2300
2300
|
|
|
2301
|
-
var styles$
|
|
2301
|
+
var styles$e = {"popup":"popup-bVhSa"};
|
|
2302
2302
|
|
|
2303
2303
|
const LegacyAppsMenu = ({
|
|
2304
2304
|
appName,
|
|
@@ -2336,7 +2336,7 @@ const LegacyAppsMenu = ({
|
|
|
2336
2336
|
cleanup: !visible,
|
|
2337
2337
|
component: Outsider,
|
|
2338
2338
|
onOutsideClick: toggle,
|
|
2339
|
-
className: styles$
|
|
2339
|
+
className: styles$e.popup,
|
|
2340
2340
|
isVisible: visible
|
|
2341
2341
|
}, /*#__PURE__*/React.createElement("menu", {
|
|
2342
2342
|
ref: menuRef,
|
|
@@ -3249,27 +3249,34 @@ const useUpdateStore = () => React.useCallback(updates => {
|
|
|
3249
3249
|
const isMac = () => /Mac|iPhone|iPad|iPod/.test(navigator.platform);
|
|
3250
3250
|
const hasCommandKey = event => isMac() ? event.metaKey : event.ctrlKey;
|
|
3251
3251
|
const commandKeyLabel = () => isMac() ? "⌘" : "Ctrl+";
|
|
3252
|
+
const isSearchShortcut = event => event.key.toLowerCase() === "k" && !event.altKey && !event.shiftKey && hasCommandKey(event);
|
|
3253
|
+
// Products opened their own search with `/` before this one replaced it.
|
|
3254
|
+
const isLegacySearchShortcut = event => event.key === "/" && !event.altKey && !event.ctrlKey && !event.metaKey && !isTypingIn(event.target);
|
|
3255
|
+
const isCopyShortcut = event => event.key.toLowerCase() === "c" && !event.altKey && !event.shiftKey && hasCommandKey(event);
|
|
3256
|
+
const isTypingIn = target => target instanceof Element && target.closest('input, select, textarea, [contenteditable]:not([contenteditable="false"])') !== null;
|
|
3257
|
+
const isTypingKey = event => event.key.length === 1 && event.key !== " " && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
3258
|
+
const verticalStep = key => ({
|
|
3259
|
+
ArrowDown: 1,
|
|
3260
|
+
ArrowUp: -1
|
|
3261
|
+
})[key];
|
|
3262
|
+
const horizontalStep = key => ({
|
|
3263
|
+
ArrowLeft: -1,
|
|
3264
|
+
ArrowRight: 1
|
|
3265
|
+
})[key];
|
|
3252
3266
|
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
} catch {
|
|
3265
|
-
// Storage can be full or blocked; search still works without it.
|
|
3266
|
-
}
|
|
3267
|
+
function SearchButton({
|
|
3268
|
+
onClick
|
|
3269
|
+
}) {
|
|
3270
|
+
return /*#__PURE__*/React.createElement("button", {
|
|
3271
|
+
"aria-keyshortcuts": `${isMac() ? "Meta+K" : "Control+K"} /`,
|
|
3272
|
+
"aria-label": "Search",
|
|
3273
|
+
onClick: onClick,
|
|
3274
|
+
type: "button"
|
|
3275
|
+
}, /*#__PURE__*/React.createElement(Spyglass, {
|
|
3276
|
+
fill: "currentColor"
|
|
3277
|
+
}));
|
|
3267
3278
|
}
|
|
3268
3279
|
|
|
3269
|
-
var styles$c = {"visuallyHidden":"visuallyHidden-C28ju"};
|
|
3270
|
-
|
|
3271
|
-
var styles$b = {"tab":"tab-lQLDx"};
|
|
3272
|
-
|
|
3273
3280
|
// useId isn't available on React 17, which our peer range still supports.
|
|
3274
3281
|
let instanceCount$1 = 0;
|
|
3275
3282
|
function useInstanceId(prefix) {
|
|
@@ -3277,58 +3284,237 @@ function useInstanceId(prefix) {
|
|
|
3277
3284
|
return id;
|
|
3278
3285
|
}
|
|
3279
3286
|
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3287
|
+
// The versions home-mobile pins, so web and mobile read the same response
|
|
3288
|
+
// shapes. Some endpoints only exist from a version on: Registrations' me,
|
|
3289
|
+
// product_search_results, and pcrl_lookups need 2025-05-01.
|
|
3290
|
+
const API_VERSIONS = {
|
|
3291
|
+
calendar: "2022-07-07",
|
|
3292
|
+
"check-ins": "2025-05-28",
|
|
3293
|
+
giving: "2019-10-18",
|
|
3294
|
+
groups: "2023-07-10",
|
|
3295
|
+
home: "2018-08-01",
|
|
3296
|
+
people: "2023-03-21",
|
|
3297
|
+
publishing: "2024-03-25",
|
|
3298
|
+
registrations: "2025-05-01",
|
|
3299
|
+
services: "2018-11-01"
|
|
3300
|
+
};
|
|
3301
|
+
// For a `/~api/{product}/v2/...` URL, the header that pins its product's
|
|
3302
|
+
// version.
|
|
3303
|
+
function apiVersionHeaders(url) {
|
|
3304
|
+
const [,, product] = new URL(url).pathname.split("/");
|
|
3305
|
+
const version = API_VERSIONS[product];
|
|
3306
|
+
if (version) {
|
|
3307
|
+
return {
|
|
3308
|
+
"X-PCO-API-Version": version
|
|
3309
|
+
};
|
|
3310
|
+
} else {
|
|
3311
|
+
return {};
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3315
|
+
const CACHE_TTL_MS = 60000;
|
|
3316
|
+
const MAX_ENTRIES$1 = 50;
|
|
3317
|
+
const cache = new Map();
|
|
3318
|
+
function cachedRequest(url) {
|
|
3319
|
+
const cached = cache.get(url);
|
|
3320
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
3321
|
+
// Re-inserting keeps the Map in recently-used order for eviction.
|
|
3322
|
+
cache.delete(url);
|
|
3323
|
+
cache.set(url, cached);
|
|
3324
|
+
return cached.promise;
|
|
3325
|
+
} else {
|
|
3326
|
+
return fetchIntoCache(url);
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
function fetchIntoCache(url) {
|
|
3330
|
+
const promise = apiRequest(defaultFetch, url, {
|
|
3331
|
+
headers: apiVersionHeaders(url)
|
|
3332
|
+
}).then(({
|
|
3333
|
+
json,
|
|
3334
|
+
ok
|
|
3335
|
+
}) => {
|
|
3336
|
+
if (!ok || json === null) throw new Error(`Request failed: ${url}`);
|
|
3337
|
+
return json;
|
|
3338
|
+
});
|
|
3339
|
+
// In-flight entries never expire, so every caller shares the one fetch.
|
|
3340
|
+
const entry = {
|
|
3341
|
+
expiresAt: Infinity,
|
|
3342
|
+
promise
|
|
3343
|
+
};
|
|
3344
|
+
cache.set(url, entry);
|
|
3345
|
+
promise.then(() => entry.expiresAt = Date.now() + CACHE_TTL_MS, () => cache.get(url) === entry && cache.delete(url));
|
|
3346
|
+
if (cache.size > MAX_ENTRIES$1) cache.delete(cache.keys().next().value);
|
|
3347
|
+
return promise;
|
|
3348
|
+
}
|
|
3349
|
+
function useCachedRequest(url) {
|
|
3350
|
+
const [settled, setSettled] = React.useState();
|
|
3351
|
+
React.useEffect(() => {
|
|
3352
|
+
if (url) {
|
|
3353
|
+
let current = true;
|
|
3354
|
+
cachedRequest(url).then(data => current && setSettled({
|
|
3355
|
+
data,
|
|
3356
|
+
url
|
|
3357
|
+
}), () => current && setSettled({
|
|
3358
|
+
failed: true,
|
|
3359
|
+
url
|
|
3360
|
+
}));
|
|
3361
|
+
return () => {
|
|
3362
|
+
current = false;
|
|
3363
|
+
};
|
|
3302
3364
|
} else {
|
|
3303
|
-
|
|
3365
|
+
return undefined;
|
|
3366
|
+
}
|
|
3367
|
+
}, [url]);
|
|
3368
|
+
if (!url) {
|
|
3369
|
+
return {
|
|
3370
|
+
status: "idle"
|
|
3371
|
+
};
|
|
3372
|
+
} else if ((settled === null || settled === void 0 ? void 0 : settled.url) !== url) {
|
|
3373
|
+
return {
|
|
3374
|
+
data: settled && "data" in settled ? settled.data : undefined,
|
|
3375
|
+
status: "loading"
|
|
3376
|
+
};
|
|
3377
|
+
} else if ("data" in settled) {
|
|
3378
|
+
return {
|
|
3379
|
+
data: settled.data,
|
|
3380
|
+
status: "done"
|
|
3381
|
+
};
|
|
3382
|
+
} else {
|
|
3383
|
+
return {
|
|
3384
|
+
status: "error"
|
|
3385
|
+
};
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
// One request per URL, for callers that fan out to a varying number of
|
|
3389
|
+
// endpoints. Unlike `useCachedRequest`, a URL that hasn't settled yet has no
|
|
3390
|
+
// earlier data to fall back on.
|
|
3391
|
+
function useCachedRequests(urls) {
|
|
3392
|
+
const [settled, setSettled] = React.useState({});
|
|
3393
|
+
// Keyed by value because each settle re-renders the caller, and a caller
|
|
3394
|
+
// that builds a fresh `urls` array on render would otherwise loop forever.
|
|
3395
|
+
const key = urls.join("\n");
|
|
3396
|
+
React.useEffect(() => {
|
|
3397
|
+
let current = true;
|
|
3398
|
+
const settle = result => current && setSettled(prev => ({
|
|
3399
|
+
...prev,
|
|
3400
|
+
[result.url]: result
|
|
3401
|
+
}));
|
|
3402
|
+
for (const url of key.split("\n").filter(Boolean)) {
|
|
3403
|
+
cachedRequest(url).then(data => settle({
|
|
3404
|
+
data,
|
|
3405
|
+
url
|
|
3406
|
+
}), () => settle({
|
|
3407
|
+
failed: true,
|
|
3408
|
+
url
|
|
3409
|
+
}));
|
|
3304
3410
|
}
|
|
3411
|
+
return () => {
|
|
3412
|
+
current = false;
|
|
3413
|
+
};
|
|
3414
|
+
}, [key]);
|
|
3415
|
+
return urls.map(url => {
|
|
3416
|
+
const result = settled[url];
|
|
3417
|
+
if (!result) {
|
|
3418
|
+
return {
|
|
3419
|
+
status: "loading"
|
|
3420
|
+
};
|
|
3421
|
+
} else if ("data" in result) {
|
|
3422
|
+
return {
|
|
3423
|
+
data: result.data,
|
|
3424
|
+
status: "done"
|
|
3425
|
+
};
|
|
3426
|
+
} else {
|
|
3427
|
+
return {
|
|
3428
|
+
status: "error"
|
|
3429
|
+
};
|
|
3430
|
+
}
|
|
3431
|
+
});
|
|
3432
|
+
}
|
|
3433
|
+
|
|
3434
|
+
function productSearchUrl(product, query) {
|
|
3435
|
+
const params = new URLSearchParams({
|
|
3436
|
+
per_page: "100",
|
|
3437
|
+
"where[term]": query
|
|
3438
|
+
});
|
|
3439
|
+
return `${window.location.origin}/~api/${product}/v2/product_search_results?${params}`;
|
|
3440
|
+
}
|
|
3441
|
+
function groupByType({
|
|
3442
|
+
data,
|
|
3443
|
+
meta
|
|
3444
|
+
}) {
|
|
3445
|
+
var _a, _b;
|
|
3446
|
+
const typeOrder = (_a = meta === null || meta === void 0 ? void 0 : meta.type_order) !== null && _a !== void 0 ? _a : [];
|
|
3447
|
+
const groups = new Map();
|
|
3448
|
+
for (const result of data) {
|
|
3449
|
+
const {
|
|
3450
|
+
type
|
|
3451
|
+
} = result.attributes;
|
|
3452
|
+
if (!groups.has(type)) groups.set(type, {
|
|
3453
|
+
results: [],
|
|
3454
|
+
type
|
|
3455
|
+
});
|
|
3456
|
+
(_b = groups.get(type)) === null || _b === void 0 ? void 0 : _b.results.push(result);
|
|
3457
|
+
}
|
|
3458
|
+
const rank = type => {
|
|
3459
|
+
const index = typeOrder.indexOf(type);
|
|
3460
|
+
return index === -1 ? typeOrder.length : index;
|
|
3305
3461
|
};
|
|
3306
|
-
return
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3462
|
+
return [...groups.values()].sort((a, b) => rank(a.type) - rank(b.type));
|
|
3463
|
+
}
|
|
3464
|
+
|
|
3465
|
+
function useDebouncedValue(value, delay) {
|
|
3466
|
+
const [debounced, setDebounced] = React.useState(value);
|
|
3467
|
+
React.useEffect(() => {
|
|
3468
|
+
const timeout = setTimeout(() => setDebounced(value), delay);
|
|
3469
|
+
return () => clearTimeout(timeout);
|
|
3470
|
+
}, [value, delay]);
|
|
3471
|
+
return debounced;
|
|
3472
|
+
}
|
|
3473
|
+
|
|
3474
|
+
const SEARCH_DEBOUNCE_MS = 300;
|
|
3475
|
+
function useProductSearch(product, term) {
|
|
3476
|
+
const query = term.trim();
|
|
3477
|
+
const debouncedQuery = useDebouncedValue(query, SEARCH_DEBOUNCE_MS);
|
|
3478
|
+
const request = useCachedRequest(debouncedQuery ? productSearchUrl(product, debouncedQuery) : null);
|
|
3479
|
+
const response = "data" in request ? request.data : undefined;
|
|
3480
|
+
const settledProduct = useSettledProduct(product, request.status);
|
|
3481
|
+
// While a request loads, the previous response stays on screen, but only
|
|
3482
|
+
// if it was for this product: People results must never show under the
|
|
3483
|
+
// Services tab.
|
|
3484
|
+
const visibleResponse = request.status === "done" || settledProduct === product ? response : undefined;
|
|
3485
|
+
const groups = React.useMemo(() => visibleResponse && groupByType(visibleResponse), [visibleResponse]);
|
|
3486
|
+
if (!query) {
|
|
3487
|
+
return {
|
|
3488
|
+
status: "idle"
|
|
3489
|
+
};
|
|
3490
|
+
} else if (query !== debouncedQuery) {
|
|
3491
|
+
return {
|
|
3492
|
+
groups,
|
|
3493
|
+
status: "loading"
|
|
3494
|
+
};
|
|
3495
|
+
} else if (request.status === "done") {
|
|
3496
|
+
return {
|
|
3497
|
+
groups: groups !== null && groups !== void 0 ? groups : [],
|
|
3498
|
+
status: "done"
|
|
3499
|
+
};
|
|
3500
|
+
} else if (request.status === "error") {
|
|
3501
|
+
return {
|
|
3502
|
+
status: "error"
|
|
3503
|
+
};
|
|
3504
|
+
} else {
|
|
3505
|
+
return {
|
|
3506
|
+
groups,
|
|
3507
|
+
status: "loading"
|
|
3508
|
+
};
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
// The product the most recent finished request was for.
|
|
3512
|
+
function useSettledProduct(product, status) {
|
|
3513
|
+
const [settledProduct, setSettledProduct] = React.useState();
|
|
3514
|
+
React.useEffect(() => {
|
|
3515
|
+
if (status === "done") setSettledProduct(product);
|
|
3516
|
+
}, [product, status]);
|
|
3517
|
+
return settledProduct;
|
|
3332
3518
|
}
|
|
3333
3519
|
|
|
3334
3520
|
function parseValue(element) {
|
|
@@ -3397,10 +3583,14 @@ function useRecentlyVisitableMetadata() {
|
|
|
3397
3583
|
return shim.useSyncExternalStore(recentlyVisitableMetadataStore.subscribe, () => recentlyVisitableMetadataStore.getState().metadata);
|
|
3398
3584
|
}
|
|
3399
3585
|
|
|
3400
|
-
|
|
3401
|
-
|
|
3586
|
+
// Entries match the ones home-mobile stores, so one list can sync across web
|
|
3587
|
+
// and mobile.
|
|
3588
|
+
const ENTRY_VERSION = 3;
|
|
3589
|
+
const MAX_ENTRIES = 50;
|
|
3402
3590
|
const MAX_TEXT_LENGTH = 200;
|
|
3403
3591
|
const PRODUCTS = ["accounts", "calendar", "check-ins", "giving", "groups", "home", "login", "people", "publishing", "registrations", "services"];
|
|
3592
|
+
// Login has no pages worth returning to.
|
|
3593
|
+
const APPS = PRODUCTS.filter(product => product !== "login");
|
|
3404
3594
|
function isId(value) {
|
|
3405
3595
|
return value !== "" && Array.from(value).every(character => character >= "0" && character <= "9");
|
|
3406
3596
|
}
|
|
@@ -3409,26 +3599,47 @@ function isPcrl(value) {
|
|
|
3409
3599
|
const segments = value.split("/");
|
|
3410
3600
|
return segments.length === 4 && PRODUCTS.some(product => product === segments[0]) && isId(segments[3]);
|
|
3411
3601
|
}
|
|
3602
|
+
function isApp(value) {
|
|
3603
|
+
return APPS.some(app => app === value);
|
|
3604
|
+
}
|
|
3605
|
+
// Links are built by resolving the path against its app's origin, so a stored
|
|
3606
|
+
// path must not be able to resolve anywhere else (`//evil.com`, `/\evil.com`).
|
|
3607
|
+
function isPath(value) {
|
|
3608
|
+
const origin = "https://app.invalid";
|
|
3609
|
+
try {
|
|
3610
|
+
return typeof value === "string" && value.startsWith("/") && new URL(value, origin).origin === origin;
|
|
3611
|
+
} catch {
|
|
3612
|
+
return false;
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3412
3615
|
function isNonBlankString(value) {
|
|
3413
3616
|
return typeof value === "string" && value.trim() !== "";
|
|
3414
3617
|
}
|
|
3618
|
+
function isOptionalString(value) {
|
|
3619
|
+
return value === undefined || typeof value === "string";
|
|
3620
|
+
}
|
|
3415
3621
|
function isRecord(value) {
|
|
3416
3622
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3417
3623
|
}
|
|
3624
|
+
function isPageMetadata(value) {
|
|
3625
|
+
return value === undefined || isRecord(value) && isOptionalString(value.details) && isOptionalString(value.type);
|
|
3626
|
+
}
|
|
3627
|
+
// The query string is part of the link but not of the page's identity, so
|
|
3628
|
+
// revisiting with other filters moves the existing entry instead of adding one.
|
|
3418
3629
|
function entryKey(entry) {
|
|
3419
3630
|
if (entry.type === "resource") {
|
|
3420
3631
|
return `resource:${entry.pcrl}`;
|
|
3421
3632
|
} else {
|
|
3422
|
-
return `page:${entry.
|
|
3633
|
+
return `page:${entry.app}:${entry.path.split("?")[0]}`;
|
|
3423
3634
|
}
|
|
3424
3635
|
}
|
|
3425
3636
|
function isEntry(value) {
|
|
3426
|
-
if (!isRecord(value)) {
|
|
3637
|
+
if (!isRecord(value) || value.version !== ENTRY_VERSION || typeof value.visitedAt !== "string") {
|
|
3427
3638
|
return false;
|
|
3428
3639
|
} else if (value.type === "resource") {
|
|
3429
3640
|
return typeof value.pcrl === "string" && isPcrl(value.pcrl);
|
|
3430
3641
|
} else if (value.type === "page") {
|
|
3431
|
-
return
|
|
3642
|
+
return isApp(value.app) && isPath(value.path) && isNonBlankString(value.title) && isPageMetadata(value.metadata);
|
|
3432
3643
|
} else {
|
|
3433
3644
|
return false;
|
|
3434
3645
|
}
|
|
@@ -3437,58 +3648,114 @@ function isEntry(value) {
|
|
|
3437
3648
|
function clampText(value) {
|
|
3438
3649
|
return Array.from(value).slice(0, MAX_TEXT_LENGTH).join("");
|
|
3439
3650
|
}
|
|
3440
|
-
function
|
|
3441
|
-
|
|
3442
|
-
|
|
3651
|
+
function clampMetadata({
|
|
3652
|
+
details,
|
|
3653
|
+
type
|
|
3654
|
+
}) {
|
|
3655
|
+
return {
|
|
3656
|
+
...(details && {
|
|
3657
|
+
details: clampText(details)
|
|
3658
|
+
}),
|
|
3659
|
+
...(type && {
|
|
3660
|
+
type: clampText(type)
|
|
3661
|
+
})
|
|
3662
|
+
};
|
|
3663
|
+
}
|
|
3664
|
+
function clampVisit(visit) {
|
|
3665
|
+
if (visit.type === "resource") {
|
|
3666
|
+
return visit;
|
|
3443
3667
|
} else {
|
|
3444
3668
|
return {
|
|
3445
|
-
...
|
|
3446
|
-
|
|
3447
|
-
|
|
3669
|
+
...visit,
|
|
3670
|
+
...(visit.metadata && {
|
|
3671
|
+
metadata: clampMetadata(visit.metadata)
|
|
3672
|
+
}),
|
|
3673
|
+
title: clampText(visit.title.trim())
|
|
3448
3674
|
};
|
|
3449
3675
|
}
|
|
3450
3676
|
}
|
|
3677
|
+
function toEntry(visit, visitedAt) {
|
|
3678
|
+
return {
|
|
3679
|
+
...clampVisit(visit),
|
|
3680
|
+
version: ENTRY_VERSION,
|
|
3681
|
+
visitedAt: visitedAt.toISOString()
|
|
3682
|
+
};
|
|
3683
|
+
}
|
|
3451
3684
|
// Recents are disposable: anything unexpected reads as an empty list (or, for
|
|
3452
3685
|
// a single bad entry, a list without it) rather than throwing.
|
|
3453
|
-
function parseRecentlyVisited(value
|
|
3686
|
+
function parseRecentlyVisited(value) {
|
|
3454
3687
|
let parsed = null;
|
|
3455
3688
|
try {
|
|
3456
3689
|
parsed = JSON.parse(value !== null && value !== void 0 ? value : "null");
|
|
3457
3690
|
} catch {
|
|
3458
3691
|
parsed = null;
|
|
3459
3692
|
}
|
|
3460
|
-
if (
|
|
3461
|
-
return parsed.
|
|
3693
|
+
if (Array.isArray(parsed)) {
|
|
3694
|
+
return parsed.filter(isEntry);
|
|
3462
3695
|
} else {
|
|
3463
3696
|
return [];
|
|
3464
3697
|
}
|
|
3465
3698
|
}
|
|
3466
|
-
function addVisit(entries,
|
|
3467
|
-
const key = entryKey(
|
|
3468
|
-
return [
|
|
3469
|
-
}
|
|
3470
|
-
function serializeRecentlyVisited(entries, personId) {
|
|
3471
|
-
return JSON.stringify({
|
|
3472
|
-
entries,
|
|
3473
|
-
person: personId,
|
|
3474
|
-
v: FORMAT_VERSION
|
|
3475
|
-
});
|
|
3699
|
+
function addVisit(entries, entry) {
|
|
3700
|
+
const key = entryKey(entry);
|
|
3701
|
+
return [entry, ...entries.filter(existing => entryKey(existing) !== key)].slice(0, MAX_ENTRIES);
|
|
3476
3702
|
}
|
|
3477
3703
|
|
|
3478
|
-
// The
|
|
3704
|
+
// The app whose URL, in this environment, has the current page's host.
|
|
3479
3705
|
// Reversing pcoUrl handles every environment @planningcenter/url knows about,
|
|
3480
|
-
// and guarantees the stored
|
|
3481
|
-
function
|
|
3706
|
+
// and guarantees the stored app rebuilds a working link.
|
|
3707
|
+
function currentApp() {
|
|
3482
3708
|
var _a;
|
|
3483
|
-
return (_a =
|
|
3709
|
+
return (_a = APPS.find(app => new URL(pcoUrl$1.pcoUrl(app)).hostname === window.location.hostname)) !== null && _a !== void 0 ? _a : null;
|
|
3484
3710
|
}
|
|
3485
|
-
function
|
|
3711
|
+
function isFilled(value) {
|
|
3712
|
+
return typeof value === "string" && value !== "";
|
|
3713
|
+
}
|
|
3714
|
+
// The tag's metadata is product-supplied JSON; only its string `type` and
|
|
3715
|
+
// `details` carry over, and an entry without either has no metadata at all.
|
|
3716
|
+
function metadataFromPage(metadata) {
|
|
3486
3717
|
var _a;
|
|
3487
3718
|
try {
|
|
3488
|
-
const
|
|
3489
|
-
|
|
3719
|
+
const {
|
|
3720
|
+
details,
|
|
3721
|
+
type
|
|
3722
|
+
} = (_a = JSON.parse(metadata !== null && metadata !== void 0 ? metadata : "{}")) !== null && _a !== void 0 ? _a : {};
|
|
3723
|
+
if (isFilled(details) || isFilled(type)) {
|
|
3724
|
+
return {
|
|
3725
|
+
...(isFilled(details) && {
|
|
3726
|
+
details
|
|
3727
|
+
}),
|
|
3728
|
+
...(isFilled(type) && {
|
|
3729
|
+
type
|
|
3730
|
+
})
|
|
3731
|
+
};
|
|
3732
|
+
} else {
|
|
3733
|
+
return undefined;
|
|
3734
|
+
}
|
|
3490
3735
|
} catch {
|
|
3491
|
-
return
|
|
3736
|
+
return undefined;
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
// Staff impersonating someone browse under `/O<org id>/P<person id>/…`.
|
|
3740
|
+
function withoutImpersonation(segments) {
|
|
3741
|
+
const [org = "", person = ""] = segments;
|
|
3742
|
+
if (org.startsWith("O") && isId(org.slice(1)) && person.startsWith("P") && isId(person.slice(1))) {
|
|
3743
|
+
return segments.slice(2);
|
|
3744
|
+
} else {
|
|
3745
|
+
return segments;
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
// Normalized the way home-mobile does it, so a page visited on web and on
|
|
3749
|
+
// mobile is one entry once the list syncs.
|
|
3750
|
+
function pathFromLocation({
|
|
3751
|
+
pathname,
|
|
3752
|
+
search
|
|
3753
|
+
}) {
|
|
3754
|
+
const segments = withoutImpersonation(pathname.split("/").filter(Boolean));
|
|
3755
|
+
if (segments.length === 0) {
|
|
3756
|
+
return "/";
|
|
3757
|
+
} else {
|
|
3758
|
+
return `/${segments.join("/")}${search.length > 1 ? search : ""}`;
|
|
3492
3759
|
}
|
|
3493
3760
|
}
|
|
3494
3761
|
// pco:resource is the more specific signal, so it wins when both tags are
|
|
@@ -3498,17 +3765,20 @@ function visitFromPage({
|
|
|
3498
3765
|
resource,
|
|
3499
3766
|
title
|
|
3500
3767
|
}) {
|
|
3501
|
-
const
|
|
3768
|
+
const app = currentApp();
|
|
3502
3769
|
if (resource && isPcrl(resource)) {
|
|
3503
3770
|
return {
|
|
3504
3771
|
pcrl: resource,
|
|
3505
3772
|
type: "resource"
|
|
3506
3773
|
};
|
|
3507
|
-
} else if (title && title.trim() !== "" &&
|
|
3774
|
+
} else if (title && title.trim() !== "" && app) {
|
|
3775
|
+
const pageMetadata = metadataFromPage(metadata);
|
|
3508
3776
|
return {
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3777
|
+
app,
|
|
3778
|
+
...(pageMetadata && {
|
|
3779
|
+
metadata: pageMetadata
|
|
3780
|
+
}),
|
|
3781
|
+
path: pathFromLocation(window.location),
|
|
3512
3782
|
title,
|
|
3513
3783
|
type: "page"
|
|
3514
3784
|
};
|
|
@@ -3556,15 +3826,16 @@ const changes = createStore({
|
|
|
3556
3826
|
});
|
|
3557
3827
|
function readRecentlyVisited(scope) {
|
|
3558
3828
|
try {
|
|
3559
|
-
return parseRecentlyVisited(localStorage.getItem(storageKey(scope))
|
|
3829
|
+
return parseRecentlyVisited(localStorage.getItem(storageKey(scope)));
|
|
3560
3830
|
} catch {
|
|
3561
3831
|
return [];
|
|
3562
3832
|
}
|
|
3563
3833
|
}
|
|
3564
3834
|
function recordVisit(visit, scope) {
|
|
3565
|
-
|
|
3835
|
+
const entry = toEntry(visit, new Date());
|
|
3836
|
+
if (isEntry(entry)) {
|
|
3566
3837
|
try {
|
|
3567
|
-
localStorage.setItem(storageKey(scope),
|
|
3838
|
+
localStorage.setItem(storageKey(scope), JSON.stringify(addVisit(readRecentlyVisited(scope), entry)));
|
|
3568
3839
|
changes.setState({
|
|
3569
3840
|
version: changes.getState().version + 1
|
|
3570
3841
|
});
|
|
@@ -3585,153 +3856,6 @@ function useRecentlyVisited({
|
|
|
3585
3856
|
}) : [], [env, personId, version]);
|
|
3586
3857
|
}
|
|
3587
3858
|
|
|
3588
|
-
// The versions home-mobile pins, so web and mobile read the same response
|
|
3589
|
-
// shapes. Some endpoints only exist from a version on: Registrations' me,
|
|
3590
|
-
// product_search_results, and pcrl_lookups need 2025-05-01.
|
|
3591
|
-
const API_VERSIONS = {
|
|
3592
|
-
calendar: "2022-07-07",
|
|
3593
|
-
"check-ins": "2025-05-28",
|
|
3594
|
-
giving: "2019-10-18",
|
|
3595
|
-
groups: "2023-07-10",
|
|
3596
|
-
home: "2018-08-01",
|
|
3597
|
-
people: "2023-03-21",
|
|
3598
|
-
publishing: "2024-03-25",
|
|
3599
|
-
registrations: "2025-05-01",
|
|
3600
|
-
services: "2018-11-01"
|
|
3601
|
-
};
|
|
3602
|
-
// For a `/~api/{product}/v2/...` URL, the header that pins its product's
|
|
3603
|
-
// version.
|
|
3604
|
-
function apiVersionHeaders(url) {
|
|
3605
|
-
const [,, product] = new URL(url).pathname.split("/");
|
|
3606
|
-
const version = API_VERSIONS[product];
|
|
3607
|
-
if (version) {
|
|
3608
|
-
return {
|
|
3609
|
-
"X-PCO-API-Version": version
|
|
3610
|
-
};
|
|
3611
|
-
} else {
|
|
3612
|
-
return {};
|
|
3613
|
-
}
|
|
3614
|
-
}
|
|
3615
|
-
|
|
3616
|
-
const CACHE_TTL_MS = 60000;
|
|
3617
|
-
const MAX_ENTRIES = 50;
|
|
3618
|
-
const cache = new Map();
|
|
3619
|
-
function cachedRequest(url) {
|
|
3620
|
-
const cached = cache.get(url);
|
|
3621
|
-
if (cached && cached.expiresAt > Date.now()) {
|
|
3622
|
-
// Re-inserting keeps the Map in recently-used order for eviction.
|
|
3623
|
-
cache.delete(url);
|
|
3624
|
-
cache.set(url, cached);
|
|
3625
|
-
return cached.promise;
|
|
3626
|
-
} else {
|
|
3627
|
-
return fetchIntoCache(url);
|
|
3628
|
-
}
|
|
3629
|
-
}
|
|
3630
|
-
function fetchIntoCache(url) {
|
|
3631
|
-
const promise = apiRequest(defaultFetch, url, {
|
|
3632
|
-
headers: apiVersionHeaders(url)
|
|
3633
|
-
}).then(({
|
|
3634
|
-
json,
|
|
3635
|
-
ok
|
|
3636
|
-
}) => {
|
|
3637
|
-
if (!ok || json === null) throw new Error(`Request failed: ${url}`);
|
|
3638
|
-
return json;
|
|
3639
|
-
});
|
|
3640
|
-
// In-flight entries never expire, so every caller shares the one fetch.
|
|
3641
|
-
const entry = {
|
|
3642
|
-
expiresAt: Infinity,
|
|
3643
|
-
promise
|
|
3644
|
-
};
|
|
3645
|
-
cache.set(url, entry);
|
|
3646
|
-
promise.then(() => entry.expiresAt = Date.now() + CACHE_TTL_MS, () => cache.get(url) === entry && cache.delete(url));
|
|
3647
|
-
if (cache.size > MAX_ENTRIES) cache.delete(cache.keys().next().value);
|
|
3648
|
-
return promise;
|
|
3649
|
-
}
|
|
3650
|
-
function useCachedRequest(url) {
|
|
3651
|
-
const [settled, setSettled] = React.useState();
|
|
3652
|
-
React.useEffect(() => {
|
|
3653
|
-
if (url) {
|
|
3654
|
-
let current = true;
|
|
3655
|
-
cachedRequest(url).then(data => current && setSettled({
|
|
3656
|
-
data,
|
|
3657
|
-
url
|
|
3658
|
-
}), () => current && setSettled({
|
|
3659
|
-
failed: true,
|
|
3660
|
-
url
|
|
3661
|
-
}));
|
|
3662
|
-
return () => {
|
|
3663
|
-
current = false;
|
|
3664
|
-
};
|
|
3665
|
-
} else {
|
|
3666
|
-
return undefined;
|
|
3667
|
-
}
|
|
3668
|
-
}, [url]);
|
|
3669
|
-
if (!url) {
|
|
3670
|
-
return {
|
|
3671
|
-
status: "idle"
|
|
3672
|
-
};
|
|
3673
|
-
} else if ((settled === null || settled === void 0 ? void 0 : settled.url) !== url) {
|
|
3674
|
-
return {
|
|
3675
|
-
data: settled && "data" in settled ? settled.data : undefined,
|
|
3676
|
-
status: "loading"
|
|
3677
|
-
};
|
|
3678
|
-
} else if ("data" in settled) {
|
|
3679
|
-
return {
|
|
3680
|
-
data: settled.data,
|
|
3681
|
-
status: "done"
|
|
3682
|
-
};
|
|
3683
|
-
} else {
|
|
3684
|
-
return {
|
|
3685
|
-
status: "error"
|
|
3686
|
-
};
|
|
3687
|
-
}
|
|
3688
|
-
}
|
|
3689
|
-
// One request per URL, for callers that fan out to a varying number of
|
|
3690
|
-
// endpoints. Unlike `useCachedRequest`, a URL that hasn't settled yet has no
|
|
3691
|
-
// earlier data to fall back on.
|
|
3692
|
-
function useCachedRequests(urls) {
|
|
3693
|
-
const [settled, setSettled] = React.useState({});
|
|
3694
|
-
// Keyed by value because each settle re-renders the caller, and a caller
|
|
3695
|
-
// that builds a fresh `urls` array on render would otherwise loop forever.
|
|
3696
|
-
const key = urls.join("\n");
|
|
3697
|
-
React.useEffect(() => {
|
|
3698
|
-
let current = true;
|
|
3699
|
-
const settle = result => current && setSettled(prev => ({
|
|
3700
|
-
...prev,
|
|
3701
|
-
[result.url]: result
|
|
3702
|
-
}));
|
|
3703
|
-
for (const url of key.split("\n").filter(Boolean)) {
|
|
3704
|
-
cachedRequest(url).then(data => settle({
|
|
3705
|
-
data,
|
|
3706
|
-
url
|
|
3707
|
-
}), () => settle({
|
|
3708
|
-
failed: true,
|
|
3709
|
-
url
|
|
3710
|
-
}));
|
|
3711
|
-
}
|
|
3712
|
-
return () => {
|
|
3713
|
-
current = false;
|
|
3714
|
-
};
|
|
3715
|
-
}, [key]);
|
|
3716
|
-
return urls.map(url => {
|
|
3717
|
-
const result = settled[url];
|
|
3718
|
-
if (!result) {
|
|
3719
|
-
return {
|
|
3720
|
-
status: "loading"
|
|
3721
|
-
};
|
|
3722
|
-
} else if ("data" in result) {
|
|
3723
|
-
return {
|
|
3724
|
-
data: result.data,
|
|
3725
|
-
status: "done"
|
|
3726
|
-
};
|
|
3727
|
-
} else {
|
|
3728
|
-
return {
|
|
3729
|
-
status: "error"
|
|
3730
|
-
};
|
|
3731
|
-
}
|
|
3732
|
-
});
|
|
3733
|
-
}
|
|
3734
|
-
|
|
3735
3859
|
const pcoUrl = pcoUrl$1;
|
|
3736
3860
|
|
|
3737
3861
|
// Resolves resource entries with one pcrl_lookups request per product.
|
|
@@ -3822,13 +3946,290 @@ function pcrlLookupsUrl(product, pcrls) {
|
|
|
3822
3946
|
});
|
|
3823
3947
|
return `${window.location.origin}/~api/${product}/v2/pcrl_lookups?${params}`;
|
|
3824
3948
|
}
|
|
3825
|
-
// Built from the
|
|
3826
|
-
//
|
|
3949
|
+
// Built from the app rather than the current origin, so it stays right once
|
|
3950
|
+
// PCA-409 shares one list across product subdomains.
|
|
3827
3951
|
function pageUrl(entry, env) {
|
|
3828
|
-
return
|
|
3952
|
+
return new URL(entry.path, pcoUrl(env)(entry.app)).href;
|
|
3953
|
+
}
|
|
3954
|
+
|
|
3955
|
+
function useRecentVisits(env) {
|
|
3956
|
+
const personId = useCurrentPersonId();
|
|
3957
|
+
const entries = useRecentlyVisited({
|
|
3958
|
+
env,
|
|
3959
|
+
personId
|
|
3960
|
+
});
|
|
3961
|
+
const [count, setCount] = React.useState(RECENTLY_VISITED_PAGE_SIZE);
|
|
3962
|
+
const shown = React.useMemo(() => entries.slice(0, count), [entries, count]);
|
|
3963
|
+
const hydrated = useHydratedRecentlyVisited(shown, env);
|
|
3964
|
+
// Resources show up once they're found. Ones that can't be found anymore,
|
|
3965
|
+
// or that the person can no longer see, are left out.
|
|
3966
|
+
const visits = hydrated.filter(isShown);
|
|
3967
|
+
const loading = hydrated.some(({
|
|
3968
|
+
status
|
|
3969
|
+
}) => status === "loading");
|
|
3970
|
+
const showMore = React.useCallback(() => setCount(current => current + RECENTLY_VISITED_PAGE_SIZE), []);
|
|
3971
|
+
return {
|
|
3972
|
+
count,
|
|
3973
|
+
hasMore: entries.length > count,
|
|
3974
|
+
loading,
|
|
3975
|
+
showMore,
|
|
3976
|
+
visits
|
|
3977
|
+
};
|
|
3978
|
+
}
|
|
3979
|
+
function isShown(visit) {
|
|
3980
|
+
return visit.status === "page" || visit.status === "resolved";
|
|
3981
|
+
}
|
|
3982
|
+
|
|
3983
|
+
function open(url, {
|
|
3984
|
+
newTab
|
|
3985
|
+
}) {
|
|
3986
|
+
if (newTab) {
|
|
3987
|
+
window.open(url, "_blank", "noopener");
|
|
3988
|
+
} else {
|
|
3989
|
+
window.location.assign(url);
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
// With text selected, ⌘C should copy that text as usual, not the link.
|
|
3993
|
+
function hasSelectedText(element) {
|
|
3994
|
+
var _a, _b;
|
|
3995
|
+
const selectedInField = element instanceof HTMLInputElement && element.selectionStart !== element.selectionEnd;
|
|
3996
|
+
return selectedInField || ((_b = (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : "") !== "";
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3999
|
+
const COPIED_FEEDBACK_MS = 1500;
|
|
4000
|
+
// `copiedUrl` stays set briefly after a copy so the UI can confirm it.
|
|
4001
|
+
function useCopyLink() {
|
|
4002
|
+
const [copiedUrl, setCopiedUrl] = React.useState(null);
|
|
4003
|
+
React.useEffect(() => {
|
|
4004
|
+
if (copiedUrl) {
|
|
4005
|
+
const timeout = setTimeout(() => setCopiedUrl(null), COPIED_FEEDBACK_MS);
|
|
4006
|
+
return () => clearTimeout(timeout);
|
|
4007
|
+
} else {
|
|
4008
|
+
return undefined;
|
|
4009
|
+
}
|
|
4010
|
+
}, [copiedUrl]);
|
|
4011
|
+
const copy = React.useCallback(url => {
|
|
4012
|
+
navigator.clipboard.writeText(url).then(() => setCopiedUrl(url));
|
|
4013
|
+
}, []);
|
|
4014
|
+
return {
|
|
4015
|
+
copiedUrl,
|
|
4016
|
+
copy
|
|
4017
|
+
};
|
|
4018
|
+
}
|
|
4019
|
+
|
|
4020
|
+
// Remembering which list the index belongs to puts the highlight back on the
|
|
4021
|
+
// first item whenever a new list replaces it, without an effect to reset it.
|
|
4022
|
+
// A list that grows or fills in under the same `list` keeps its highlight.
|
|
4023
|
+
function useResultNavigation(items, list) {
|
|
4024
|
+
const [highlight, setHighlight] = React.useState({
|
|
4025
|
+
index: 0,
|
|
4026
|
+
list
|
|
4027
|
+
});
|
|
4028
|
+
const highlightedIndex = highlight.list === list ? highlight.index : 0;
|
|
4029
|
+
const highlighted = items[highlightedIndex];
|
|
4030
|
+
const {
|
|
4031
|
+
copiedUrl,
|
|
4032
|
+
copy
|
|
4033
|
+
} = useCopyLink();
|
|
4034
|
+
const highlightIndex = React.useCallback(index => setHighlight(current => current.list === list && current.index === index ? current : {
|
|
4035
|
+
index,
|
|
4036
|
+
list
|
|
4037
|
+
}), [list]);
|
|
4038
|
+
const handleKeyDown = event => {
|
|
4039
|
+
const step = verticalStep(event.key);
|
|
4040
|
+
if (step && items.length > 0) {
|
|
4041
|
+
event.preventDefault();
|
|
4042
|
+
highlightIndex((highlightedIndex + step + items.length) % items.length);
|
|
4043
|
+
} else if (event.key === "Enter" && highlighted && !event.nativeEvent.isComposing) {
|
|
4044
|
+
event.preventDefault();
|
|
4045
|
+
open(highlighted.url, {
|
|
4046
|
+
newTab: hasCommandKey(event)
|
|
4047
|
+
});
|
|
4048
|
+
} else if (isCopyShortcut(event) && highlighted && !hasSelectedText(event.currentTarget)) {
|
|
4049
|
+
event.preventDefault();
|
|
4050
|
+
copy(highlighted.url);
|
|
4051
|
+
}
|
|
4052
|
+
};
|
|
4053
|
+
return {
|
|
4054
|
+
copied: copiedUrl !== null && copiedUrl === (highlighted === null || highlighted === void 0 ? void 0 : highlighted.url),
|
|
4055
|
+
handleKeyDown,
|
|
4056
|
+
highlightIndex,
|
|
4057
|
+
highlighted,
|
|
4058
|
+
highlightedIndex: highlighted ? highlightedIndex : null
|
|
4059
|
+
};
|
|
4060
|
+
}
|
|
4061
|
+
|
|
4062
|
+
function groupSections(groups = []) {
|
|
4063
|
+
return groups.map(({
|
|
4064
|
+
results,
|
|
4065
|
+
type
|
|
4066
|
+
}) => ({
|
|
4067
|
+
heading: type,
|
|
4068
|
+
items: results.map(resultItem),
|
|
4069
|
+
key: type
|
|
4070
|
+
}));
|
|
4071
|
+
}
|
|
4072
|
+
function resultItem({
|
|
4073
|
+
attributes,
|
|
4074
|
+
id
|
|
4075
|
+
}) {
|
|
4076
|
+
return {
|
|
4077
|
+
key: id,
|
|
4078
|
+
subtitle: summarize(attributes.metadata),
|
|
4079
|
+
title: attributes.title,
|
|
4080
|
+
url: attributes.url
|
|
4081
|
+
};
|
|
4082
|
+
}
|
|
4083
|
+
function visitItem(visit) {
|
|
4084
|
+
return {
|
|
4085
|
+
key: entryKey(visit.entry),
|
|
4086
|
+
...describeVisit(visit)
|
|
4087
|
+
};
|
|
4088
|
+
}
|
|
4089
|
+
function summarize(metadata) {
|
|
4090
|
+
return Object.values(metadata !== null && metadata !== void 0 ? metadata : {}).filter(value => typeof value === "string" && value !== "").join(" · ");
|
|
4091
|
+
}
|
|
4092
|
+
// Unlike search results, recents come from every product, so each one names
|
|
4093
|
+
// its product and type.
|
|
4094
|
+
function describeVisit(visit) {
|
|
4095
|
+
var _a, _b;
|
|
4096
|
+
const subtitle = (...parts) => parts.filter(Boolean).join(" · ");
|
|
4097
|
+
if (visit.status === "page") {
|
|
4098
|
+
const {
|
|
4099
|
+
app,
|
|
4100
|
+
metadata,
|
|
4101
|
+
title
|
|
4102
|
+
} = visit.entry;
|
|
4103
|
+
return {
|
|
4104
|
+
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 : ""),
|
|
4105
|
+
title,
|
|
4106
|
+
url: visit.url
|
|
4107
|
+
};
|
|
4108
|
+
} else {
|
|
4109
|
+
const {
|
|
4110
|
+
metadata,
|
|
4111
|
+
product,
|
|
4112
|
+
title,
|
|
4113
|
+
type
|
|
4114
|
+
} = visit.result.attributes;
|
|
4115
|
+
return {
|
|
4116
|
+
subtitle: subtitle(productName(product), type, summarize(metadata)),
|
|
4117
|
+
title,
|
|
4118
|
+
url: visit.url
|
|
4119
|
+
};
|
|
4120
|
+
}
|
|
4121
|
+
}
|
|
4122
|
+
// check-ins → Check-Ins
|
|
4123
|
+
function productName(slug) {
|
|
4124
|
+
return slug.split("-").map(capitalize).join("-");
|
|
4125
|
+
}
|
|
4126
|
+
function capitalize(word) {
|
|
4127
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
4128
|
+
}
|
|
4129
|
+
|
|
4130
|
+
const STORAGE_KEY = "topbar.product_search.last_product";
|
|
4131
|
+
function readLastProduct() {
|
|
4132
|
+
try {
|
|
4133
|
+
return localStorage.getItem(STORAGE_KEY);
|
|
4134
|
+
} catch {
|
|
4135
|
+
return null;
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
function saveLastProduct(slug) {
|
|
4139
|
+
try {
|
|
4140
|
+
localStorage.setItem(STORAGE_KEY, slug);
|
|
4141
|
+
} catch {
|
|
4142
|
+
// Storage can be full or blocked; search still works without it.
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
function initialProduct(products) {
|
|
4146
|
+
var _a, _b, _c;
|
|
4147
|
+
const lastProduct = readLastProduct();
|
|
4148
|
+
const bySlug = slug => products.find(product => product.slug === slug);
|
|
4149
|
+
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];
|
|
3829
4150
|
}
|
|
3830
4151
|
|
|
3831
|
-
|
|
4152
|
+
function KeyboardHints({
|
|
4153
|
+
copied
|
|
4154
|
+
}) {
|
|
4155
|
+
const command = commandKeyLabel();
|
|
4156
|
+
return /*#__PURE__*/React.createElement("p", null, "\u2191\u2193 Select \u00B7 \u21B5 Open \u00B7 ", command, "\u21B5 Open in new tab \u00B7 ", command, "C", " ", /*#__PURE__*/React.createElement("span", {
|
|
4157
|
+
role: "status"
|
|
4158
|
+
}, copied ? "Copied!" : "Copy link"), " \u00B7 ", command, "K Open search");
|
|
4159
|
+
}
|
|
4160
|
+
|
|
4161
|
+
var styles$d = {"tab":"tab-dz8ki"};
|
|
4162
|
+
|
|
4163
|
+
function ProductTabs({
|
|
4164
|
+
children,
|
|
4165
|
+
onKeyDown,
|
|
4166
|
+
onSelect,
|
|
4167
|
+
products,
|
|
4168
|
+
selected
|
|
4169
|
+
}) {
|
|
4170
|
+
const id = useInstanceId("product-tabs");
|
|
4171
|
+
const tabId = slug => `${id}-tab-${slug}`;
|
|
4172
|
+
const tabRefs = React.useRef(new Map());
|
|
4173
|
+
const handleKeyDown = event => {
|
|
4174
|
+
var _a;
|
|
4175
|
+
const offset = horizontalStep(event.key);
|
|
4176
|
+
if (offset) {
|
|
4177
|
+
event.preventDefault();
|
|
4178
|
+
const index = products.indexOf(selected);
|
|
4179
|
+
const next = products[(index + offset + products.length) % products.length];
|
|
4180
|
+
onSelect(next.slug);
|
|
4181
|
+
(_a = tabRefs.current.get(next.slug)) === null || _a === void 0 ? void 0 : _a.focus();
|
|
4182
|
+
} else {
|
|
4183
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
|
|
4184
|
+
}
|
|
4185
|
+
};
|
|
4186
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
4187
|
+
"aria-label": "Product",
|
|
4188
|
+
onKeyDown: handleKeyDown,
|
|
4189
|
+
role: "tablist"
|
|
4190
|
+
}, products.map(product => (/*#__PURE__*/React.createElement("button", {
|
|
4191
|
+
"aria-controls": `${id}-panel`,
|
|
4192
|
+
"aria-selected": product === selected,
|
|
4193
|
+
className: styles$d.tab,
|
|
4194
|
+
id: tabId(product.slug),
|
|
4195
|
+
key: product.slug,
|
|
4196
|
+
onClick: () => onSelect(product.slug),
|
|
4197
|
+
ref: element => {
|
|
4198
|
+
if (element) {
|
|
4199
|
+
tabRefs.current.set(product.slug, element);
|
|
4200
|
+
} else {
|
|
4201
|
+
tabRefs.current.delete(product.slug);
|
|
4202
|
+
}
|
|
4203
|
+
},
|
|
4204
|
+
role: "tab",
|
|
4205
|
+
tabIndex: product === selected ? 0 : -1,
|
|
4206
|
+
type: "button"
|
|
4207
|
+
}, product.appName)))), /*#__PURE__*/React.createElement("div", {
|
|
4208
|
+
"aria-labelledby": tabId(selected.slug),
|
|
4209
|
+
id: `${id}-panel`,
|
|
4210
|
+
role: "tabpanel"
|
|
4211
|
+
}, children));
|
|
4212
|
+
}
|
|
4213
|
+
|
|
4214
|
+
function EndOfResultsObserver({
|
|
4215
|
+
onVisible
|
|
4216
|
+
}) {
|
|
4217
|
+
const ref = React.useRef(null);
|
|
4218
|
+
React.useEffect(() => {
|
|
4219
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
4220
|
+
if (entry.isIntersecting) onVisible();
|
|
4221
|
+
});
|
|
4222
|
+
if (ref.current) observer.observe(ref.current);
|
|
4223
|
+
return () => observer.disconnect();
|
|
4224
|
+
}, [onVisible]);
|
|
4225
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
4226
|
+
ref: ref
|
|
4227
|
+
});
|
|
4228
|
+
}
|
|
4229
|
+
|
|
4230
|
+
const resultId = (listboxId, index) => `${listboxId}-result-${index}`;
|
|
4231
|
+
|
|
4232
|
+
var styles$c = {"result":"result-Thq2C"};
|
|
3832
4233
|
|
|
3833
4234
|
function SearchResult({
|
|
3834
4235
|
highlighted,
|
|
@@ -3851,7 +4252,7 @@ function SearchResult({
|
|
|
3851
4252
|
// pointer, and that shouldn't steal the highlight.
|
|
3852
4253
|
return /*#__PURE__*/React.createElement("a", {
|
|
3853
4254
|
"aria-selected": highlighted,
|
|
3854
|
-
className: styles$
|
|
4255
|
+
className: styles$c.result,
|
|
3855
4256
|
href: url,
|
|
3856
4257
|
id: id,
|
|
3857
4258
|
onMouseMove: onHighlight,
|
|
@@ -3861,21 +4262,129 @@ function SearchResult({
|
|
|
3861
4262
|
}, /*#__PURE__*/React.createElement("div", null, title), /*#__PURE__*/React.createElement("div", null, subtitle));
|
|
3862
4263
|
}
|
|
3863
4264
|
|
|
3864
|
-
|
|
4265
|
+
// Sections with a heading render as labelled groups; ones without render
|
|
4266
|
+
// their items straight into the listbox. Indexes run across every section.
|
|
4267
|
+
function ResultListbox({
|
|
4268
|
+
busy,
|
|
4269
|
+
highlightedIndex,
|
|
4270
|
+
id,
|
|
4271
|
+
label,
|
|
4272
|
+
labelledBy,
|
|
4273
|
+
onHighlight,
|
|
4274
|
+
sections
|
|
4275
|
+
}) {
|
|
4276
|
+
let index = 0;
|
|
4277
|
+
const renderItem = item => {
|
|
4278
|
+
const itemIndex = index++;
|
|
4279
|
+
return /*#__PURE__*/React.createElement(SearchResult, {
|
|
4280
|
+
highlighted: itemIndex === highlightedIndex,
|
|
4281
|
+
id: resultId(id, itemIndex),
|
|
4282
|
+
key: item.key,
|
|
4283
|
+
onHighlight: () => onHighlight(itemIndex),
|
|
4284
|
+
subtitle: item.subtitle,
|
|
4285
|
+
title: item.title,
|
|
4286
|
+
url: item.url
|
|
4287
|
+
});
|
|
4288
|
+
};
|
|
4289
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
4290
|
+
"aria-busy": busy,
|
|
4291
|
+
"aria-label": label,
|
|
4292
|
+
"aria-labelledby": labelledBy,
|
|
4293
|
+
id: id,
|
|
4294
|
+
role: "listbox"
|
|
4295
|
+
}, sections.map((section, sectionIndex) => {
|
|
4296
|
+
if (section.heading === undefined) {
|
|
4297
|
+
return /*#__PURE__*/React.createElement(React.Fragment, {
|
|
4298
|
+
key: section.key
|
|
4299
|
+
}, section.items.map(renderItem));
|
|
4300
|
+
}
|
|
4301
|
+
const headingId = `${id}-group-${sectionIndex}`;
|
|
4302
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
4303
|
+
"aria-labelledby": headingId,
|
|
4304
|
+
key: section.key,
|
|
4305
|
+
role: "group"
|
|
4306
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
4307
|
+
id: headingId
|
|
4308
|
+
}, section.heading), section.items.map(renderItem));
|
|
4309
|
+
}));
|
|
4310
|
+
}
|
|
4311
|
+
|
|
4312
|
+
function RecentlyVisited({
|
|
4313
|
+
count,
|
|
4314
|
+
hasMore,
|
|
4315
|
+
items,
|
|
4316
|
+
loading,
|
|
4317
|
+
showMore,
|
|
4318
|
+
...navigation
|
|
4319
|
+
}) {
|
|
4320
|
+
const headingId = `${navigation.id}-heading`;
|
|
4321
|
+
if (items.length === 0 && !hasMore && !loading) {
|
|
4322
|
+
return /*#__PURE__*/React.createElement("p", null, "Pages you visit will show up here.");
|
|
4323
|
+
} else {
|
|
4324
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
4325
|
+
id: headingId
|
|
4326
|
+
}, "Recently visited"), /*#__PURE__*/React.createElement(ResultListbox, {
|
|
4327
|
+
labelledBy: headingId,
|
|
4328
|
+
sections: [{
|
|
4329
|
+
items,
|
|
4330
|
+
key: "recents"
|
|
4331
|
+
}],
|
|
4332
|
+
...navigation
|
|
4333
|
+
}), hasMore && /*#__PURE__*/React.createElement(EndOfResultsObserver, {
|
|
4334
|
+
key: count,
|
|
4335
|
+
onVisible: showMore
|
|
4336
|
+
}));
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
|
|
4340
|
+
// A combobox: focus stays here while aria-activedescendant points screen
|
|
4341
|
+
// readers at the highlighted result in the listbox it controls.
|
|
4342
|
+
function SearchInput({
|
|
4343
|
+
expanded,
|
|
4344
|
+
highlightedIndex,
|
|
4345
|
+
inputRef,
|
|
4346
|
+
listboxId,
|
|
4347
|
+
onChange,
|
|
4348
|
+
onFocus,
|
|
4349
|
+
onKeyDown,
|
|
4350
|
+
value
|
|
4351
|
+
}) {
|
|
4352
|
+
return /*#__PURE__*/React.createElement("input", {
|
|
4353
|
+
"aria-activedescendant": highlightedIndex === null ? undefined : resultId(listboxId, highlightedIndex),
|
|
4354
|
+
"aria-autocomplete": "list",
|
|
4355
|
+
"aria-controls": expanded ? listboxId : undefined,
|
|
4356
|
+
"aria-expanded": expanded,
|
|
4357
|
+
"aria-label": "Search",
|
|
4358
|
+
autoFocus: true,
|
|
4359
|
+
onChange: event => onChange(event.target.value),
|
|
4360
|
+
onFocus: onFocus,
|
|
4361
|
+
onKeyDown: onKeyDown,
|
|
4362
|
+
placeholder: "Search",
|
|
4363
|
+
ref: inputRef,
|
|
4364
|
+
role: "combobox",
|
|
4365
|
+
type: "search",
|
|
4366
|
+
value: value
|
|
4367
|
+
});
|
|
4368
|
+
}
|
|
4369
|
+
|
|
4370
|
+
var styles$b = {"visuallyHidden":"visuallyHidden-S9VG5"};
|
|
4371
|
+
|
|
3865
4372
|
function SearchResults({
|
|
3866
|
-
|
|
4373
|
+
label,
|
|
4374
|
+
sections,
|
|
4375
|
+
status,
|
|
3867
4376
|
term,
|
|
3868
4377
|
...navigation
|
|
3869
4378
|
}) {
|
|
3870
|
-
|
|
3871
|
-
switch (search.status) {
|
|
4379
|
+
switch (status) {
|
|
3872
4380
|
case "idle":
|
|
3873
4381
|
return null;
|
|
3874
4382
|
case "loading":
|
|
3875
|
-
if (
|
|
3876
|
-
return /*#__PURE__*/React.createElement(
|
|
4383
|
+
if (sections.length > 0) {
|
|
4384
|
+
return /*#__PURE__*/React.createElement(ResultListbox, {
|
|
3877
4385
|
busy: true,
|
|
3878
|
-
|
|
4386
|
+
label: label,
|
|
4387
|
+
sections: sections,
|
|
3879
4388
|
...navigation
|
|
3880
4389
|
});
|
|
3881
4390
|
}
|
|
@@ -3887,324 +4396,167 @@ function SearchResults({
|
|
|
3887
4396
|
role: "alert"
|
|
3888
4397
|
}, "Search isn\u2019t working right now. Try again.");
|
|
3889
4398
|
case "done":
|
|
3890
|
-
if (
|
|
4399
|
+
if (sections.length === 0) {
|
|
3891
4400
|
return /*#__PURE__*/React.createElement("p", {
|
|
3892
4401
|
role: "status"
|
|
3893
4402
|
}, "No results for \u201C", term.trim(), "\u201D");
|
|
3894
4403
|
}
|
|
3895
|
-
return /*#__PURE__*/React.createElement(
|
|
3896
|
-
|
|
4404
|
+
return /*#__PURE__*/React.createElement(ResultListbox, {
|
|
4405
|
+
busy: false,
|
|
4406
|
+
label: label,
|
|
4407
|
+
sections: sections,
|
|
3897
4408
|
...navigation
|
|
3898
4409
|
});
|
|
3899
4410
|
}
|
|
3900
4411
|
}
|
|
3901
|
-
function ResultGroups({
|
|
3902
|
-
busy = false,
|
|
3903
|
-
groups,
|
|
3904
|
-
highlightedIndex,
|
|
3905
|
-
id,
|
|
3906
|
-
label,
|
|
3907
|
-
onHighlight
|
|
3908
|
-
}) {
|
|
3909
|
-
let index = 0;
|
|
3910
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
3911
|
-
"aria-busy": busy,
|
|
3912
|
-
"aria-label": label,
|
|
3913
|
-
id: id,
|
|
3914
|
-
role: "listbox"
|
|
3915
|
-
}, groups.map(({
|
|
3916
|
-
results,
|
|
3917
|
-
type
|
|
3918
|
-
}, groupIndex) => {
|
|
3919
|
-
const headingId = `${id}-group-${groupIndex}`;
|
|
3920
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
3921
|
-
"aria-labelledby": headingId,
|
|
3922
|
-
key: type,
|
|
3923
|
-
role: "group"
|
|
3924
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
3925
|
-
id: headingId
|
|
3926
|
-
}, type), results.map(result => {
|
|
3927
|
-
const resultIndex = index++;
|
|
3928
|
-
return /*#__PURE__*/React.createElement(SearchResult, {
|
|
3929
|
-
highlighted: resultIndex === highlightedIndex,
|
|
3930
|
-
id: resultId(id, resultIndex),
|
|
3931
|
-
key: result.id,
|
|
3932
|
-
onHighlight: () => onHighlight(resultIndex),
|
|
3933
|
-
subtitle: summarize(result.attributes.metadata),
|
|
3934
|
-
title: result.attributes.title,
|
|
3935
|
-
url: result.attributes.url
|
|
3936
|
-
});
|
|
3937
|
-
}));
|
|
3938
|
-
}));
|
|
3939
|
-
}
|
|
3940
|
-
function summarize(metadata) {
|
|
3941
|
-
return Object.values(metadata !== null && metadata !== void 0 ? metadata : {}).filter(value => typeof value === "string" && value !== "").join(" · ");
|
|
3942
|
-
}
|
|
3943
4412
|
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
const [count, setCount] = React.useState(RECENTLY_VISITED_PAGE_SIZE);
|
|
3951
|
-
const shown = React.useMemo(() => entries.slice(0, count), [entries, count]);
|
|
3952
|
-
const hydrated = useHydratedRecentlyVisited(shown, env);
|
|
3953
|
-
// Resources show up once they're found. Ones that can't be found anymore,
|
|
3954
|
-
// or that the person can no longer see, are left out.
|
|
3955
|
-
const visits = hydrated.filter(isShown);
|
|
3956
|
-
const loading = hydrated.some(({
|
|
3957
|
-
status
|
|
3958
|
-
}) => status === "loading");
|
|
3959
|
-
const showMore = React.useCallback(() => setCount(current => current + RECENTLY_VISITED_PAGE_SIZE), []);
|
|
3960
|
-
return {
|
|
3961
|
-
count,
|
|
3962
|
-
hasMore: entries.length > count,
|
|
3963
|
-
loading,
|
|
3964
|
-
showMore,
|
|
3965
|
-
visits
|
|
3966
|
-
};
|
|
3967
|
-
}
|
|
3968
|
-
function isShown(visit) {
|
|
3969
|
-
return visit.status === "page" || visit.status === "resolved";
|
|
3970
|
-
}
|
|
3971
|
-
function RecentlyVisited({
|
|
3972
|
-
count,
|
|
3973
|
-
hasMore,
|
|
3974
|
-
highlightedIndex,
|
|
3975
|
-
id,
|
|
3976
|
-
loading,
|
|
3977
|
-
onHighlight,
|
|
3978
|
-
showMore,
|
|
3979
|
-
visits
|
|
3980
|
-
}) {
|
|
3981
|
-
const headingId = `${id}-heading`;
|
|
3982
|
-
if (visits.length === 0 && !hasMore && !loading) {
|
|
3983
|
-
return /*#__PURE__*/React.createElement("p", null, "Pages you visit will show up here.");
|
|
3984
|
-
} else {
|
|
3985
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
3986
|
-
id: headingId
|
|
3987
|
-
}, "Recently visited"), /*#__PURE__*/React.createElement("div", {
|
|
3988
|
-
"aria-labelledby": headingId,
|
|
3989
|
-
id: id,
|
|
3990
|
-
role: "listbox"
|
|
3991
|
-
}, visits.map((visit, index) => (/*#__PURE__*/React.createElement(SearchResult, {
|
|
3992
|
-
highlighted: index === highlightedIndex,
|
|
3993
|
-
id: resultId(id, index),
|
|
3994
|
-
key: entryKey(visit.entry),
|
|
3995
|
-
onHighlight: () => onHighlight(index),
|
|
3996
|
-
...describeVisit(visit)
|
|
3997
|
-
})))), hasMore && /*#__PURE__*/React.createElement(ShowMoreWhenVisible, {
|
|
3998
|
-
key: count,
|
|
3999
|
-
onVisible: showMore
|
|
4000
|
-
}));
|
|
4001
|
-
}
|
|
4002
|
-
}
|
|
4003
|
-
function ShowMoreWhenVisible({
|
|
4004
|
-
onVisible
|
|
4413
|
+
// Keys the recents list for navigation, so the highlight
|
|
4414
|
+
// stays put as more recents load and as their lookups answer.
|
|
4415
|
+
const RECENTS = "recents";
|
|
4416
|
+
function SearchPanel({
|
|
4417
|
+
env,
|
|
4418
|
+
products
|
|
4005
4419
|
}) {
|
|
4006
|
-
|
|
4420
|
+
var _a, _b;
|
|
4421
|
+
const listboxId = useInstanceId("product-search-results");
|
|
4422
|
+
const [term, setTerm] = React.useState("");
|
|
4423
|
+
const [selectedSlug, setSelectedSlug] = React.useState(() => initialProduct(products).slug);
|
|
4424
|
+
const product = (_a = products.find(({
|
|
4425
|
+
slug
|
|
4426
|
+
}) => slug === selectedSlug)) !== null && _a !== void 0 ? _a : products[0];
|
|
4427
|
+
const hasTerm = term.trim() !== "";
|
|
4428
|
+
const search = useProductSearch(product.slug, term);
|
|
4429
|
+
const groups = "groups" in search ? search.groups : undefined;
|
|
4430
|
+
const sections = React.useMemo(() => groupSections(groups), [groups]);
|
|
4431
|
+
const {
|
|
4432
|
+
visits,
|
|
4433
|
+
...recents
|
|
4434
|
+
} = useRecentVisits(env);
|
|
4435
|
+
const recentItems = visits.map(visitItem);
|
|
4436
|
+
const items = hasTerm ? sections.flatMap(section => section.items) : recentItems;
|
|
4437
|
+
const navigation = useResultNavigation(items, hasTerm ? sections : RECENTS);
|
|
4438
|
+
const {
|
|
4439
|
+
highlightedIndex
|
|
4440
|
+
} = navigation;
|
|
4441
|
+
const inputRef = React.useRef(null);
|
|
4442
|
+
const [announceHighlight, setAnnounceHighlight] = React.useState(false);
|
|
4443
|
+
// The tabs keep focus while arrowing through results, but only the focused
|
|
4444
|
+
// input can point screen readers at the highlight, so from the tabs it's
|
|
4445
|
+
// announced instead. Typing goes back to the input, keystroke and all.
|
|
4446
|
+
const handleTabsKeyDown = event => {
|
|
4447
|
+
var _a;
|
|
4448
|
+
if (isTypingKey(event)) {
|
|
4449
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
4450
|
+
} else if (verticalStep(event.key)) {
|
|
4451
|
+
setAnnounceHighlight(true);
|
|
4452
|
+
navigation.handleKeyDown(event);
|
|
4453
|
+
} else {
|
|
4454
|
+
navigation.handleKeyDown(event);
|
|
4455
|
+
}
|
|
4456
|
+
};
|
|
4007
4457
|
React.useEffect(() => {
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
}
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
};
|
|
4045
|
-
}
|
|
4046
|
-
}
|
|
4047
|
-
// check-ins → Check-Ins
|
|
4048
|
-
function productName(slug) {
|
|
4049
|
-
return slug.split("-").map(capitalize).join("-");
|
|
4050
|
-
}
|
|
4051
|
-
function capitalize(word) {
|
|
4052
|
-
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
4458
|
+
if (hasTerm) saveLastProduct(product.slug);
|
|
4459
|
+
}, [hasTerm, product.slug]);
|
|
4460
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SearchInput, {
|
|
4461
|
+
expanded: items.length > 0,
|
|
4462
|
+
highlightedIndex: highlightedIndex,
|
|
4463
|
+
inputRef: inputRef,
|
|
4464
|
+
listboxId: listboxId,
|
|
4465
|
+
onChange: setTerm,
|
|
4466
|
+
onFocus: () => setAnnounceHighlight(false),
|
|
4467
|
+
onKeyDown: navigation.handleKeyDown,
|
|
4468
|
+
value: term
|
|
4469
|
+
}), /*#__PURE__*/React.createElement(ProductTabs, {
|
|
4470
|
+
onKeyDown: handleTabsKeyDown,
|
|
4471
|
+
onSelect: setSelectedSlug,
|
|
4472
|
+
products: products,
|
|
4473
|
+
selected: product
|
|
4474
|
+
}, hasTerm ? (/*#__PURE__*/React.createElement(SearchResults, {
|
|
4475
|
+
highlightedIndex: highlightedIndex,
|
|
4476
|
+
id: listboxId,
|
|
4477
|
+
label: `${product.appName} results`,
|
|
4478
|
+
onHighlight: navigation.highlightIndex,
|
|
4479
|
+
sections: sections,
|
|
4480
|
+
status: search.status,
|
|
4481
|
+
term: term
|
|
4482
|
+
})) : (/*#__PURE__*/React.createElement(RecentlyVisited, {
|
|
4483
|
+
highlightedIndex: highlightedIndex,
|
|
4484
|
+
id: listboxId,
|
|
4485
|
+
items: recentItems,
|
|
4486
|
+
onHighlight: navigation.highlightIndex,
|
|
4487
|
+
...recents
|
|
4488
|
+
}))), /*#__PURE__*/React.createElement("div", {
|
|
4489
|
+
"aria-live": "polite",
|
|
4490
|
+
className: styles$b.visuallyHidden
|
|
4491
|
+
}, announceHighlight ? (_b = navigation.highlighted) === null || _b === void 0 ? void 0 : _b.title : null), /*#__PURE__*/React.createElement(KeyboardHints, {
|
|
4492
|
+
copied: navigation.copied
|
|
4493
|
+
}));
|
|
4053
4494
|
}
|
|
4054
4495
|
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
const
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
// Services tab.
|
|
4065
|
-
const visibleResponse = request.status === "done" || settledProduct === product ? response : undefined;
|
|
4066
|
-
const groups = React.useMemo(() => visibleResponse && groupByType(visibleResponse), [visibleResponse]);
|
|
4067
|
-
if (!query) {
|
|
4068
|
-
return {
|
|
4069
|
-
status: "idle"
|
|
4070
|
-
};
|
|
4071
|
-
} else if (query !== debouncedQuery) {
|
|
4072
|
-
return {
|
|
4073
|
-
groups,
|
|
4074
|
-
status: "loading"
|
|
4075
|
-
};
|
|
4076
|
-
} else if (request.status === "done") {
|
|
4077
|
-
return {
|
|
4078
|
-
groups: groups !== null && groups !== void 0 ? groups : [],
|
|
4079
|
-
status: "done"
|
|
4080
|
-
};
|
|
4081
|
-
} else if (request.status === "error") {
|
|
4082
|
-
return {
|
|
4083
|
-
status: "error"
|
|
4084
|
-
};
|
|
4085
|
-
} else {
|
|
4086
|
-
return {
|
|
4087
|
-
groups,
|
|
4088
|
-
status: "loading"
|
|
4089
|
-
};
|
|
4090
|
-
}
|
|
4496
|
+
// Products open modals from many libraries, not all of which mark themselves
|
|
4497
|
+
// with roles, so a modal is recognized by what it does: cover the page.
|
|
4498
|
+
function isModalOpen() {
|
|
4499
|
+
const {
|
|
4500
|
+
clientHeight,
|
|
4501
|
+
clientWidth
|
|
4502
|
+
} = document.documentElement;
|
|
4503
|
+
const element = document.elementFromPoint(clientWidth / 2, clientHeight / 2);
|
|
4504
|
+
return element !== null && (isInNativeDialog(element) || hasBackdrop(element, clientWidth, clientHeight));
|
|
4091
4505
|
}
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
});
|
|
4097
|
-
return `${window.location.origin}/~api/${product}/v2/product_search_results?${params}`;
|
|
4506
|
+
// A native modal's backdrop is drawn by the browser rather than being an
|
|
4507
|
+
// element; hit-testing it lands on the dialog itself.
|
|
4508
|
+
function isInNativeDialog(element) {
|
|
4509
|
+
return element.closest("dialog[open]") !== null;
|
|
4098
4510
|
}
|
|
4099
|
-
function
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
var _a, _b;
|
|
4104
|
-
const typeOrder = (_a = meta === null || meta === void 0 ? void 0 : meta.type_order) !== null && _a !== void 0 ? _a : [];
|
|
4105
|
-
const groups = new Map();
|
|
4106
|
-
for (const result of data) {
|
|
4107
|
-
const {
|
|
4108
|
-
type
|
|
4109
|
-
} = result.attributes;
|
|
4110
|
-
if (!groups.has(type)) groups.set(type, {
|
|
4111
|
-
results: [],
|
|
4112
|
-
type
|
|
4113
|
-
});
|
|
4114
|
-
(_b = groups.get(type)) === null || _b === void 0 ? void 0 : _b.results.push(result);
|
|
4511
|
+
function hasBackdrop(element, width, height) {
|
|
4512
|
+
let current = element;
|
|
4513
|
+
while (current && !isBackdrop(current, width, height)) {
|
|
4514
|
+
current = current.parentElement;
|
|
4115
4515
|
}
|
|
4116
|
-
|
|
4117
|
-
const index = typeOrder.indexOf(type);
|
|
4118
|
-
return index === -1 ? typeOrder.length : index;
|
|
4119
|
-
};
|
|
4120
|
-
return [...groups.values()].sort((a, b) => rank(a.type) - rank(b.type));
|
|
4516
|
+
return current !== null;
|
|
4121
4517
|
}
|
|
4122
|
-
//
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4518
|
+
// Some backdrops start below the topbar rather than at the top of the screen,
|
|
4519
|
+
// so the layer only has to span the width and reach from the center to the
|
|
4520
|
+
// bottom. Its own box has to reach the center: a bar along the bottom can
|
|
4521
|
+
// hold a panel that pops up over the center, like Services' media player.
|
|
4522
|
+
// react-aria leaves a closed modal's backdrop up while it fades out, search's
|
|
4523
|
+
// own included, so one that's on its way out doesn't count.
|
|
4524
|
+
function isBackdrop(element, width, height) {
|
|
4525
|
+
const rect = element.getBoundingClientRect();
|
|
4526
|
+
return !element.matches("[data-exiting]") && getComputedStyle(element).position === "fixed" && rect.width >= width - SCROLLBAR_SLACK && rect.top <= height / 2 && rect.bottom >= height - ROUNDING_SLACK;
|
|
4129
4527
|
}
|
|
4130
|
-
|
|
4131
|
-
|
|
4528
|
+
// react-aria keeps a classic scrollbar's gutter reserved while its modals lock
|
|
4529
|
+
// scrolling, so their backdrops stop short of the right edge by its width.
|
|
4530
|
+
const SCROLLBAR_SLACK = 24;
|
|
4531
|
+
// Rects are fractional at non-100% zoom, but clientWidth and clientHeight are
|
|
4532
|
+
// rounded to whole pixels.
|
|
4533
|
+
const ROUNDING_SLACK = 1;
|
|
4534
|
+
|
|
4535
|
+
function useSearchShortcut(enabled, open, setOpen) {
|
|
4132
4536
|
React.useEffect(() => {
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
})
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
if (copiedUrl) {
|
|
4157
|
-
const timeout = setTimeout(() => setCopiedUrl(null), COPIED_FEEDBACK_MS);
|
|
4158
|
-
return () => clearTimeout(timeout);
|
|
4159
|
-
} else {
|
|
4160
|
-
return undefined;
|
|
4161
|
-
}
|
|
4162
|
-
}, [copiedUrl]);
|
|
4163
|
-
const highlightIndex = React.useCallback(index => setHighlight(current => current.list === list && current.index === index ? current : {
|
|
4164
|
-
index,
|
|
4165
|
-
list
|
|
4166
|
-
}), [list]);
|
|
4167
|
-
const handleKeyDown = event => {
|
|
4168
|
-
const step = verticalStep(event.key);
|
|
4169
|
-
if (step && options.length > 0) {
|
|
4170
|
-
event.preventDefault();
|
|
4171
|
-
highlightIndex((highlightedIndex + step + options.length) % options.length);
|
|
4172
|
-
} else if (event.key === "Enter" && highlighted && !event.nativeEvent.isComposing) {
|
|
4173
|
-
event.preventDefault();
|
|
4174
|
-
open(highlighted.url, {
|
|
4175
|
-
newTab: hasCommandKey(event)
|
|
4176
|
-
});
|
|
4177
|
-
} else if (isCopyShortcut(event) && highlighted && !hasSelectedText(event.currentTarget)) {
|
|
4178
|
-
event.preventDefault();
|
|
4179
|
-
const {
|
|
4180
|
-
url
|
|
4181
|
-
} = highlighted;
|
|
4182
|
-
navigator.clipboard.writeText(url).then(() => setCopiedUrl(url));
|
|
4183
|
-
}
|
|
4184
|
-
};
|
|
4185
|
-
return {
|
|
4186
|
-
copied: copiedUrl !== null && copiedUrl === (highlighted === null || highlighted === void 0 ? void 0 : highlighted.url),
|
|
4187
|
-
handleKeyDown,
|
|
4188
|
-
highlightIndex,
|
|
4189
|
-
highlighted,
|
|
4190
|
-
highlightedIndex: highlighted ? highlightedIndex : null
|
|
4191
|
-
};
|
|
4192
|
-
}
|
|
4193
|
-
function open(url, {
|
|
4194
|
-
newTab
|
|
4195
|
-
}) {
|
|
4196
|
-
if (newTab) {
|
|
4197
|
-
window.open(url, "_blank", "noopener");
|
|
4198
|
-
} else {
|
|
4199
|
-
window.location.assign(url);
|
|
4200
|
-
}
|
|
4201
|
-
}
|
|
4202
|
-
const isCopyShortcut = event => event.key.toLowerCase() === "c" && !event.altKey && !event.shiftKey && hasCommandKey(event);
|
|
4203
|
-
// With text selected, ⌘C should copy that text as usual, not the link.
|
|
4204
|
-
function hasSelectedText(element) {
|
|
4205
|
-
var _a, _b;
|
|
4206
|
-
const selectedInField = element instanceof HTMLInputElement && element.selectionStart !== element.selectionEnd;
|
|
4207
|
-
return selectedInField || ((_b = (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : "") !== "";
|
|
4537
|
+
if (!enabled) return undefined;
|
|
4538
|
+
// Rich text editors like Trix bind ⌘K to "insert link"; capturing at the
|
|
4539
|
+
// window and stopping propagation keeps them from ever seeing it. Under a
|
|
4540
|
+
// product's modal, search stays shut rather than stacking a second modal,
|
|
4541
|
+
// and the keystroke goes on to the product.
|
|
4542
|
+
const handleKeyDown = event => {
|
|
4543
|
+
if (open && isSearchShortcut(event)) {
|
|
4544
|
+
event.preventDefault();
|
|
4545
|
+
event.stopPropagation();
|
|
4546
|
+
setOpen(false);
|
|
4547
|
+
} else if (!open && (isSearchShortcut(event) || isLegacySearchShortcut(event)) && !isModalOpen()) {
|
|
4548
|
+
event.preventDefault();
|
|
4549
|
+
event.stopPropagation();
|
|
4550
|
+
setOpen(true);
|
|
4551
|
+
}
|
|
4552
|
+
};
|
|
4553
|
+
window.addEventListener("keydown", handleKeyDown, {
|
|
4554
|
+
capture: true
|
|
4555
|
+
});
|
|
4556
|
+
return () => window.removeEventListener("keydown", handleKeyDown, {
|
|
4557
|
+
capture: true
|
|
4558
|
+
});
|
|
4559
|
+
}, [enabled, open, setOpen]);
|
|
4208
4560
|
}
|
|
4209
4561
|
|
|
4210
4562
|
const SEARCHABLE_PRODUCTS = [{
|
|
@@ -4232,6 +4584,7 @@ const SEARCHABLE_PRODUCTS = [{
|
|
|
4232
4584
|
appName: "Services",
|
|
4233
4585
|
slug: "services"
|
|
4234
4586
|
}];
|
|
4587
|
+
|
|
4235
4588
|
const PERMISSION_CHECKS = {
|
|
4236
4589
|
"check-ins": () => fetchMe("check-ins", "permission").then(({
|
|
4237
4590
|
permission
|
|
@@ -4294,6 +4647,7 @@ function canSearch(slug, personId) {
|
|
|
4294
4647
|
return promise;
|
|
4295
4648
|
}
|
|
4296
4649
|
}
|
|
4650
|
+
|
|
4297
4651
|
function useSearchableProducts(apps) {
|
|
4298
4652
|
// Keyed by name because providers hand over a fresh `apps` array on renders
|
|
4299
4653
|
// where nothing changed.
|
|
@@ -4335,181 +4689,27 @@ function inSwitcherOrder(apps) {
|
|
|
4335
4689
|
return groupApps(apps).restProducts.flatMap(app => SEARCHABLE_PRODUCTS.filter(product => product.appName === app.name));
|
|
4336
4690
|
}
|
|
4337
4691
|
|
|
4338
|
-
const isSearchShortcut = event => event.key.toLowerCase() === "k" && !event.altKey && !event.shiftKey && hasCommandKey(event);
|
|
4339
|
-
// Products opened their own search with `/` before this one replaced it.
|
|
4340
|
-
const isLegacySearchShortcut = event => event.key === "/" && !event.altKey && !event.ctrlKey && !event.metaKey && !isTypingIn(event.target);
|
|
4341
|
-
const isTypingKey = event => event.key.length === 1 && event.key !== " " && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
4342
|
-
const isTypingIn = target => target instanceof Element && target.closest('input, select, textarea, [contenteditable]:not([contenteditable="false"])') !== null;
|
|
4343
4692
|
function ProductSearch({
|
|
4344
4693
|
apps,
|
|
4345
4694
|
env
|
|
4346
4695
|
}) {
|
|
4347
4696
|
const searchable = useSearchableProducts(apps);
|
|
4348
|
-
if (searchable.status === "done" && searchable.products.length > 0) {
|
|
4349
|
-
return /*#__PURE__*/React.createElement(SearchButtonAndDialog, {
|
|
4350
|
-
env: env,
|
|
4351
|
-
products: searchable.products
|
|
4352
|
-
});
|
|
4353
|
-
} else {
|
|
4354
|
-
return null;
|
|
4355
|
-
}
|
|
4356
|
-
}
|
|
4357
|
-
function SearchButtonAndDialog({
|
|
4358
|
-
env,
|
|
4359
|
-
products
|
|
4360
|
-
}) {
|
|
4361
4697
|
const [open, setOpen] = React.useState(false);
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
event.stopPropagation();
|
|
4369
|
-
setOpen(wasOpen => !wasOpen);
|
|
4370
|
-
} else if (isLegacySearchShortcut(event)) {
|
|
4371
|
-
event.preventDefault();
|
|
4372
|
-
event.stopPropagation();
|
|
4373
|
-
setOpen(true);
|
|
4374
|
-
}
|
|
4375
|
-
};
|
|
4376
|
-
window.addEventListener("keydown", handleKeyDown, {
|
|
4377
|
-
capture: true
|
|
4378
|
-
});
|
|
4379
|
-
return () => window.removeEventListener("keydown", handleKeyDown, {
|
|
4380
|
-
capture: true
|
|
4381
|
-
});
|
|
4382
|
-
}, []);
|
|
4383
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("button", {
|
|
4384
|
-
"aria-keyshortcuts": `${isMac() ? "Meta+K" : "Control+K"} /`,
|
|
4385
|
-
"aria-label": "Search",
|
|
4386
|
-
onClick: () => setOpen(true),
|
|
4387
|
-
type: "button"
|
|
4388
|
-
}, /*#__PURE__*/React.createElement(Spyglass, {
|
|
4389
|
-
fill: "currentColor"
|
|
4390
|
-
})), /*#__PURE__*/React.createElement(tapestry.Modal, {
|
|
4698
|
+
const available = searchable.status === "done" && searchable.products.length > 0;
|
|
4699
|
+
useSearchShortcut(available, open, setOpen);
|
|
4700
|
+
if (!available) return null;
|
|
4701
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SearchButton, {
|
|
4702
|
+
onClick: () => setOpen(true)
|
|
4703
|
+
}), /*#__PURE__*/React.createElement(tapestry.Modal, {
|
|
4391
4704
|
"aria-label": "Search",
|
|
4392
4705
|
dismissible: true,
|
|
4393
4706
|
onClose: () => setOpen(false),
|
|
4394
4707
|
open: open
|
|
4395
4708
|
}, /*#__PURE__*/React.createElement(SearchPanel, {
|
|
4396
4709
|
env: env,
|
|
4397
|
-
products: products
|
|
4710
|
+
products: searchable.products
|
|
4398
4711
|
})));
|
|
4399
4712
|
}
|
|
4400
|
-
// Keys the recents list for navigation, so the highlight stays put as more
|
|
4401
|
-
// recents load and as their lookups answer.
|
|
4402
|
-
const RECENTS = "recents";
|
|
4403
|
-
function SearchPanel({
|
|
4404
|
-
env,
|
|
4405
|
-
products
|
|
4406
|
-
}) {
|
|
4407
|
-
var _a, _b;
|
|
4408
|
-
const listboxId = useInstanceId("product-search-results");
|
|
4409
|
-
const [term, setTerm] = React.useState("");
|
|
4410
|
-
const [selectedSlug, setSelectedSlug] = React.useState(() => initialProduct(products).slug);
|
|
4411
|
-
const product = (_a = products.find(({
|
|
4412
|
-
slug
|
|
4413
|
-
}) => slug === selectedSlug)) !== null && _a !== void 0 ? _a : products[0];
|
|
4414
|
-
const hasTerm = term.trim() !== "";
|
|
4415
|
-
const search = useProductSearch(product.slug, term);
|
|
4416
|
-
const groups = "groups" in search ? search.groups : undefined;
|
|
4417
|
-
const results = React.useMemo(() => {
|
|
4418
|
-
var _a;
|
|
4419
|
-
return (_a = groups === null || groups === void 0 ? void 0 : groups.flatMap(group => group.results)) !== null && _a !== void 0 ? _a : [];
|
|
4420
|
-
}, [groups]);
|
|
4421
|
-
const recents = useRecentVisits(env);
|
|
4422
|
-
const options = hasTerm ? results.map(({
|
|
4423
|
-
attributes
|
|
4424
|
-
}) => ({
|
|
4425
|
-
label: attributes.title,
|
|
4426
|
-
url: attributes.url
|
|
4427
|
-
})) : recents.visits.map(visit => {
|
|
4428
|
-
const {
|
|
4429
|
-
title,
|
|
4430
|
-
url
|
|
4431
|
-
} = describeVisit(visit);
|
|
4432
|
-
return {
|
|
4433
|
-
label: title,
|
|
4434
|
-
url
|
|
4435
|
-
};
|
|
4436
|
-
});
|
|
4437
|
-
const navigation = useResultNavigation(options, hasTerm ? results : RECENTS);
|
|
4438
|
-
const {
|
|
4439
|
-
highlightedIndex
|
|
4440
|
-
} = navigation;
|
|
4441
|
-
const inputRef = React.useRef(null);
|
|
4442
|
-
const [announceHighlight, setAnnounceHighlight] = React.useState(false);
|
|
4443
|
-
// The tabs keep focus while arrowing through results, but only the focused
|
|
4444
|
-
// input can point screen readers at the highlight, so from the tabs it's
|
|
4445
|
-
// announced instead. Typing goes back to the input, keystroke and all.
|
|
4446
|
-
const handleTabsKeyDown = event => {
|
|
4447
|
-
var _a;
|
|
4448
|
-
if (isTypingKey(event)) {
|
|
4449
|
-
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
4450
|
-
} else if (verticalStep(event.key)) {
|
|
4451
|
-
setAnnounceHighlight(true);
|
|
4452
|
-
navigation.handleKeyDown(event);
|
|
4453
|
-
} else {
|
|
4454
|
-
navigation.handleKeyDown(event);
|
|
4455
|
-
}
|
|
4456
|
-
};
|
|
4457
|
-
React.useEffect(() => {
|
|
4458
|
-
if (hasTerm) saveLastProduct(product.slug);
|
|
4459
|
-
}, [hasTerm, product.slug]);
|
|
4460
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("input", {
|
|
4461
|
-
"aria-activedescendant": highlightedIndex === null ? undefined : resultId(listboxId, highlightedIndex),
|
|
4462
|
-
"aria-autocomplete": "list",
|
|
4463
|
-
"aria-controls": options.length > 0 ? listboxId : undefined,
|
|
4464
|
-
"aria-expanded": options.length > 0,
|
|
4465
|
-
"aria-label": "Search",
|
|
4466
|
-
autoFocus: true,
|
|
4467
|
-
onChange: event => setTerm(event.target.value),
|
|
4468
|
-
onFocus: () => setAnnounceHighlight(false),
|
|
4469
|
-
onKeyDown: navigation.handleKeyDown,
|
|
4470
|
-
placeholder: "Search",
|
|
4471
|
-
ref: inputRef,
|
|
4472
|
-
role: "combobox",
|
|
4473
|
-
type: "search",
|
|
4474
|
-
value: term
|
|
4475
|
-
}), /*#__PURE__*/React.createElement(ProductTabs, {
|
|
4476
|
-
onKeyDown: handleTabsKeyDown,
|
|
4477
|
-
onSelect: setSelectedSlug,
|
|
4478
|
-
products: products,
|
|
4479
|
-
selected: product
|
|
4480
|
-
}, hasTerm ? (/*#__PURE__*/React.createElement(SearchResults, {
|
|
4481
|
-
highlightedIndex: highlightedIndex,
|
|
4482
|
-
id: listboxId,
|
|
4483
|
-
label: `${product.appName} results`,
|
|
4484
|
-
onHighlight: navigation.highlightIndex,
|
|
4485
|
-
search: search,
|
|
4486
|
-
term: term
|
|
4487
|
-
})) : (/*#__PURE__*/React.createElement(RecentlyVisited, {
|
|
4488
|
-
highlightedIndex: highlightedIndex,
|
|
4489
|
-
id: listboxId,
|
|
4490
|
-
onHighlight: navigation.highlightIndex,
|
|
4491
|
-
...recents
|
|
4492
|
-
}))), /*#__PURE__*/React.createElement("div", {
|
|
4493
|
-
"aria-live": "polite",
|
|
4494
|
-
className: styles$c.visuallyHidden
|
|
4495
|
-
}, announceHighlight ? (_b = navigation.highlighted) === null || _b === void 0 ? void 0 : _b.label : null), /*#__PURE__*/React.createElement(KeyboardHints, {
|
|
4496
|
-
copied: navigation.copied
|
|
4497
|
-
}));
|
|
4498
|
-
}
|
|
4499
|
-
function KeyboardHints({
|
|
4500
|
-
copied
|
|
4501
|
-
}) {
|
|
4502
|
-
const command = commandKeyLabel();
|
|
4503
|
-
return /*#__PURE__*/React.createElement("p", null, "\u2191\u2193 Select \u00B7 \u21B5 Open \u00B7 ", command, "\u21B5 Open in new tab \u00B7 ", command, "C", " ", /*#__PURE__*/React.createElement("span", {
|
|
4504
|
-
role: "status"
|
|
4505
|
-
}, copied ? "Copied!" : "Copy link"), " \u00B7 ", command, "K Open search");
|
|
4506
|
-
}
|
|
4507
|
-
function initialProduct(products) {
|
|
4508
|
-
var _a, _b, _c;
|
|
4509
|
-
const lastProduct = readLastProduct();
|
|
4510
|
-
const bySlug = slug => products.find(product => product.slug === slug);
|
|
4511
|
-
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];
|
|
4512
|
-
}
|
|
4513
4713
|
|
|
4514
4714
|
function useRecordRecentlyVisited({
|
|
4515
4715
|
enabled,
|
|
@@ -4553,9 +4753,9 @@ function StyledRoot({
|
|
|
4553
4753
|
`), children);
|
|
4554
4754
|
}
|
|
4555
4755
|
|
|
4556
|
-
var styles$
|
|
4756
|
+
var styles$a = {"route":"route--8BWe","is-active":"is-active-FsU9Y","apps-routes-separator":"apps-routes-separator-Nlm-N"};
|
|
4557
4757
|
|
|
4558
|
-
var styles$
|
|
4758
|
+
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"};
|
|
4559
4759
|
|
|
4560
4760
|
const UserMenu = ({
|
|
4561
4761
|
appName,
|
|
@@ -4574,7 +4774,7 @@ const UserMenu = ({
|
|
|
4574
4774
|
component: Outsider,
|
|
4575
4775
|
onOutsideClick: () => setUserMenuVisible(false),
|
|
4576
4776
|
cleanup: !userMenuVisible,
|
|
4577
|
-
className: styles$
|
|
4777
|
+
className: styles$9.popup,
|
|
4578
4778
|
isVisible: userMenuVisible
|
|
4579
4779
|
}, /*#__PURE__*/React.createElement("div", null, hasConnectedPeople ? (/*#__PURE__*/React.createElement("div", {
|
|
4580
4780
|
style: {
|
|
@@ -4718,11 +4918,11 @@ const UserMenuTrigger = ({
|
|
|
4718
4918
|
userMenuVisible
|
|
4719
4919
|
}) => {
|
|
4720
4920
|
return /*#__PURE__*/React.createElement("div", {
|
|
4721
|
-
className: styles$
|
|
4921
|
+
className: styles$9["trigger-wrap"],
|
|
4722
4922
|
"data-pendo": "topbar-org-switcher"
|
|
4723
4923
|
}, /*#__PURE__*/React.createElement(Hoverable, {
|
|
4724
|
-
className: classnames(styles$
|
|
4725
|
-
[styles$
|
|
4924
|
+
className: classnames(styles$9["trigger"], {
|
|
4925
|
+
[styles$9["is-visible"]]: userMenuVisible
|
|
4726
4926
|
}),
|
|
4727
4927
|
component: "button",
|
|
4728
4928
|
type: "button",
|
|
@@ -4734,7 +4934,7 @@ const UserMenuTrigger = ({
|
|
|
4734
4934
|
requestConnectedPeopleFetch();
|
|
4735
4935
|
}
|
|
4736
4936
|
}, showOrgName && (/*#__PURE__*/React.createElement("span", {
|
|
4737
|
-
className: styles$
|
|
4937
|
+
className: styles$9["trigger-orgname"]
|
|
4738
4938
|
}, orgName)), /*#__PURE__*/React.createElement(DisclosureChevronIcon, null)));
|
|
4739
4939
|
};
|
|
4740
4940
|
|
|
@@ -4794,7 +4994,7 @@ const NotSmallTopbar = ({
|
|
|
4794
4994
|
colors: props.colors,
|
|
4795
4995
|
requestAppsFetch: props.requestAppsFetch
|
|
4796
4996
|
})), /*#__PURE__*/React.createElement("div", {
|
|
4797
|
-
className: styles$
|
|
4997
|
+
className: styles$a["apps-routes-separator"]
|
|
4798
4998
|
}), routesVisible && props.routes, /*#__PURE__*/React.createElement("div", {
|
|
4799
4999
|
style: {
|
|
4800
5000
|
margin: "auto"
|
|
@@ -4835,12 +5035,12 @@ const NotSmallRoute = ({
|
|
|
4835
5035
|
...nativeProps
|
|
4836
5036
|
}) => {
|
|
4837
5037
|
return /*#__PURE__*/React.createElement("a", {
|
|
4838
|
-
className: classnames(styles$
|
|
5038
|
+
className: classnames(styles$a["route"], className, active && styles$a["is-active"]),
|
|
4839
5039
|
...nativeProps
|
|
4840
5040
|
});
|
|
4841
5041
|
};
|
|
4842
5042
|
|
|
4843
|
-
var styles$
|
|
5043
|
+
var styles$8 = {"avatar":"avatar-tW-wn"};
|
|
4844
5044
|
|
|
4845
5045
|
function Avatar({
|
|
4846
5046
|
env,
|
|
@@ -4850,7 +5050,7 @@ function Avatar({
|
|
|
4850
5050
|
}) {
|
|
4851
5051
|
const url = incomingUrl || `${pcoUrl$1(env)("people")}/static/no_photo_thumbnail_gray.svg`;
|
|
4852
5052
|
return /*#__PURE__*/React.createElement("div", {
|
|
4853
|
-
className: styles$
|
|
5053
|
+
className: styles$8.avatar,
|
|
4854
5054
|
style: {
|
|
4855
5055
|
backgroundImage: `url(${url})`,
|
|
4856
5056
|
...style
|
|
@@ -32609,7 +32809,7 @@ function requireReactDom () {
|
|
|
32609
32809
|
|
|
32610
32810
|
var reactDomExports = requireReactDom();
|
|
32611
32811
|
|
|
32612
|
-
var styles$
|
|
32812
|
+
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"};
|
|
32613
32813
|
|
|
32614
32814
|
const AppsSwitcher = ({
|
|
32615
32815
|
appName,
|
|
@@ -32637,8 +32837,8 @@ const AppsSwitcher = ({
|
|
|
32637
32837
|
}, /*#__PURE__*/React.createElement("a", {
|
|
32638
32838
|
"aria-current": name === appName ? "page" : undefined,
|
|
32639
32839
|
"aria-label": name,
|
|
32640
|
-
className: classnames(styles$
|
|
32641
|
-
[styles$
|
|
32840
|
+
className: classnames(styles$7.link, {
|
|
32841
|
+
[styles$7.current]: name === appName
|
|
32642
32842
|
}),
|
|
32643
32843
|
"data-pendo": `app-switcher--change-app--${name}`,
|
|
32644
32844
|
"data-turbolinks": false,
|
|
@@ -32650,48 +32850,48 @@ const AppsSwitcher = ({
|
|
|
32650
32850
|
"aria-expanded": visible,
|
|
32651
32851
|
"aria-haspopup": "true",
|
|
32652
32852
|
"aria-label": `App switcher: ${appName}`,
|
|
32653
|
-
className: styles$
|
|
32853
|
+
className: styles$7.trigger,
|
|
32654
32854
|
onClick: onTriggerClick,
|
|
32655
32855
|
type: "button"
|
|
32656
32856
|
}, /*#__PURE__*/React.createElement(ColorAppIcon, {
|
|
32657
32857
|
appName: appName.replace(/[\s-]/, "")
|
|
32658
32858
|
})), /*#__PURE__*/reactDomExports.createPortal(/*#__PURE__*/React.createElement("div", {
|
|
32659
32859
|
ref: containerRef,
|
|
32660
|
-
className: classnames(styles$
|
|
32661
|
-
[styles$
|
|
32860
|
+
className: classnames(styles$7.layer, {
|
|
32861
|
+
[styles$7["layer--visible"]]: visible
|
|
32662
32862
|
}),
|
|
32663
32863
|
onClick: toggle,
|
|
32664
32864
|
onKeyDown: handleKeyDown
|
|
32665
32865
|
}, /*#__PURE__*/React.createElement("nav", {
|
|
32666
32866
|
"aria-label": "Switch app",
|
|
32667
32867
|
"aria-modal": visible,
|
|
32668
|
-
className: styles$
|
|
32868
|
+
className: styles$7.panel,
|
|
32669
32869
|
"data-pendo": "app-switcher",
|
|
32670
32870
|
onClick: event => event.stopPropagation(),
|
|
32671
32871
|
role: "dialog"
|
|
32672
32872
|
}, /*#__PURE__*/React.createElement("div", {
|
|
32673
|
-
className: styles$
|
|
32873
|
+
className: styles$7.header
|
|
32674
32874
|
}, /*#__PURE__*/React.createElement("span", {
|
|
32675
|
-
className: styles$
|
|
32875
|
+
className: styles$7.heading
|
|
32676
32876
|
}, "Switch to"), /*#__PURE__*/React.createElement("button", {
|
|
32677
32877
|
"aria-label": "Close",
|
|
32678
|
-
className: styles$
|
|
32878
|
+
className: styles$7.close,
|
|
32679
32879
|
onClick: toggle,
|
|
32680
32880
|
type: "button"
|
|
32681
32881
|
}, /*#__PURE__*/React.createElement(X, {
|
|
32682
32882
|
"aria-hidden": "true",
|
|
32683
32883
|
fill: "var(--t-icon-color-dim)"
|
|
32684
32884
|
}))), /*#__PURE__*/React.createElement("ul", {
|
|
32685
|
-
className: styles$
|
|
32885
|
+
className: styles$7.list,
|
|
32686
32886
|
role: "list"
|
|
32687
32887
|
}, homeProduct && productLink(homeProduct.name), restProducts.map(({
|
|
32688
32888
|
name
|
|
32689
32889
|
}) => productLink(name)), accountsProduct && (/*#__PURE__*/React.createElement("li", {
|
|
32690
|
-
className: styles$
|
|
32890
|
+
className: styles$7["accounts-item"]
|
|
32691
32891
|
}, /*#__PURE__*/React.createElement("a", {
|
|
32692
32892
|
"aria-current": appName === "Accounts" ? "page" : undefined,
|
|
32693
32893
|
"aria-label": "Account settings",
|
|
32694
|
-
className: classnames(styles$
|
|
32894
|
+
className: classnames(styles$7.link, styles$7["accounts-link"]),
|
|
32695
32895
|
"data-pendo": "app-switcher--change-app--Accounts",
|
|
32696
32896
|
"data-turbolinks": false,
|
|
32697
32897
|
href: pcoUrl$1(env)("accounts") + getAppsSwitchProductPath(accountsProduct.name)
|
|
@@ -32714,14 +32914,14 @@ ClientStorage.defaultProps = {
|
|
|
32714
32914
|
method: "localStorage"
|
|
32715
32915
|
};
|
|
32716
32916
|
|
|
32717
|
-
var styles$
|
|
32917
|
+
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"};
|
|
32718
32918
|
|
|
32719
32919
|
function AccountSettingsLink({
|
|
32720
32920
|
env
|
|
32721
32921
|
}) {
|
|
32722
32922
|
const accountsName = "accounts";
|
|
32723
32923
|
return /*#__PURE__*/React.createElement("a", {
|
|
32724
|
-
className: classnames(styles$
|
|
32924
|
+
className: classnames(styles$6["appmenu-list-a"], styles$6["appmenu-list-a--bottom"]),
|
|
32725
32925
|
"data-pendo": `app-switcher--change-app--${accountsName}`,
|
|
32726
32926
|
"data-turbolinks": false,
|
|
32727
32927
|
href: pcoUrl$1(env)(accountsName) + getAppsSwitchProductPath(accountsName)
|
|
@@ -32744,7 +32944,7 @@ const CloseMenuButton = ({
|
|
|
32744
32944
|
}) => (/*#__PURE__*/React.createElement("button", {
|
|
32745
32945
|
"aria-label": "Close menu",
|
|
32746
32946
|
type: "button",
|
|
32747
|
-
className: styles$
|
|
32947
|
+
className: styles$6["appmenu-close-button"],
|
|
32748
32948
|
style: style
|
|
32749
32949
|
}, /*#__PURE__*/React.createElement(X, {
|
|
32750
32950
|
fill: color,
|
|
@@ -32805,7 +33005,7 @@ const LegacyAppsSwitcher = ({
|
|
|
32805
33005
|
"aria-haspopup": "true",
|
|
32806
33006
|
"aria-label": `App switcher: ${appName}`,
|
|
32807
33007
|
type: "button",
|
|
32808
|
-
className: styles$
|
|
33008
|
+
className: styles$6["appmenu-button"],
|
|
32809
33009
|
onClick: onTriggerClick
|
|
32810
33010
|
}, /*#__PURE__*/React.createElement(ColorAppIcon, {
|
|
32811
33011
|
appName: appName.replace(/[\s-]/, "")
|
|
@@ -32813,7 +33013,7 @@ const LegacyAppsSwitcher = ({
|
|
|
32813
33013
|
item: "Topbar:Small:Menus:Products:visibility",
|
|
32814
33014
|
render: () => (/*#__PURE__*/React.createElement("div", {
|
|
32815
33015
|
ref: appMenuRef,
|
|
32816
|
-
className: styles$
|
|
33016
|
+
className: styles$6["appmenu-layer"],
|
|
32817
33017
|
style: {
|
|
32818
33018
|
...(visible && {
|
|
32819
33019
|
display: "block"
|
|
@@ -32830,37 +33030,37 @@ const LegacyAppsSwitcher = ({
|
|
|
32830
33030
|
e.stopPropagation();
|
|
32831
33031
|
}
|
|
32832
33032
|
},
|
|
32833
|
-
className: styles$
|
|
33033
|
+
className: styles$6["appmenu-container"]
|
|
32834
33034
|
}, /*#__PURE__*/React.createElement("a", {
|
|
32835
33035
|
"aria-current": appName === (homeProduct === null || homeProduct === void 0 ? void 0 : homeProduct.name) ? "page" : undefined,
|
|
32836
33036
|
"aria-label": homeProduct ? homeProduct.name : appName,
|
|
32837
33037
|
"data-pendo": `app-switcher--change-app--${homeProduct === null || homeProduct === void 0 ? void 0 : homeProduct.name}`,
|
|
32838
33038
|
"data-turbolinks": false,
|
|
32839
|
-
className: classnames(styles$
|
|
33039
|
+
className: classnames(styles$6["appmenu-list-a"], styles$6["appmenu-list-a--top"]),
|
|
32840
33040
|
href: homeProduct ? pcoUrl$1(env)("accounts") + getAppsSwitchProductPath(homeProduct === null || homeProduct === void 0 ? void 0 : homeProduct.name) : undefined
|
|
32841
33041
|
}, /*#__PURE__*/React.createElement(UserMenuAppLockup, {
|
|
32842
33042
|
appName: homeProduct ? homeProduct.name : appName
|
|
32843
33043
|
})), /*#__PURE__*/React.createElement("div", {
|
|
32844
|
-
className: styles$
|
|
33044
|
+
className: styles$6["appmenu-scroll"]
|
|
32845
33045
|
}, /*#__PURE__*/React.createElement("ul", {
|
|
32846
33046
|
"data-pendo": "app-switcher",
|
|
32847
|
-
className: styles$
|
|
33047
|
+
className: styles$6["appmenu-list"]
|
|
32848
33048
|
}, appsMenuFormatter(restProducts).map(({
|
|
32849
33049
|
name
|
|
32850
33050
|
}) => (/*#__PURE__*/React.createElement("li", {
|
|
32851
33051
|
key: name,
|
|
32852
|
-
className: styles$
|
|
33052
|
+
className: styles$6["appmenu-list-li"]
|
|
32853
33053
|
}, /*#__PURE__*/React.createElement("a", {
|
|
32854
33054
|
"aria-current": name === appName ? "page" : undefined,
|
|
32855
33055
|
"aria-label": name,
|
|
32856
33056
|
"data-pendo": `app-switcher--change-app--${name}`,
|
|
32857
33057
|
"data-turbolinks": false,
|
|
32858
|
-
className: styles$
|
|
33058
|
+
className: styles$6["appmenu-list-a"],
|
|
32859
33059
|
href: pcoUrl$1(env)("accounts") + getAppsSwitchProductPath(name)
|
|
32860
33060
|
}, /*#__PURE__*/React.createElement(UserMenuAppLockup, {
|
|
32861
33061
|
appName: name
|
|
32862
33062
|
}))))), accountsProduct && (/*#__PURE__*/React.createElement("li", {
|
|
32863
|
-
className: styles$
|
|
33063
|
+
className: styles$6["appmenu-list-li"]
|
|
32864
33064
|
}, /*#__PURE__*/React.createElement(AccountSettingsLink, {
|
|
32865
33065
|
env: env
|
|
32866
33066
|
}))))))))
|
|
@@ -32924,11 +33124,11 @@ const SmallTopbar = ({
|
|
|
32924
33124
|
toggle: toggleProductSwitcher,
|
|
32925
33125
|
visible: productSwitcherVisible
|
|
32926
33126
|
})), routesMenuVisible && (/*#__PURE__*/React.createElement("div", {
|
|
32927
|
-
className: styles$
|
|
33127
|
+
className: styles$6["routes"]
|
|
32928
33128
|
}, props.routes)), /*#__PURE__*/React.createElement("button", {
|
|
32929
33129
|
type: "button",
|
|
32930
|
-
className: classnames(styles$
|
|
32931
|
-
[styles$
|
|
33130
|
+
className: classnames(styles$6["current-route"], {
|
|
33131
|
+
[styles$6["is-active"]]: routesMenuVisible
|
|
32932
33132
|
}),
|
|
32933
33133
|
onClick: props.routes.length > 0 ? () => {
|
|
32934
33134
|
setProductSwitcherVisible(false);
|
|
@@ -32936,7 +33136,7 @@ const SmallTopbar = ({
|
|
|
32936
33136
|
} : null
|
|
32937
33137
|
}, /*#__PURE__*/React.createElement(CurrentRouteComponent, null, capitalize$1(currentRouteText)), props.routes.length > 0 && /*#__PURE__*/React.createElement(DisclosureChevronIcon, null)), /*#__PURE__*/React.createElement("button", {
|
|
32938
33138
|
type: "button",
|
|
32939
|
-
className: styles$
|
|
33139
|
+
className: styles$6["profile-button"],
|
|
32940
33140
|
onClick: () => {
|
|
32941
33141
|
var _a, _b;
|
|
32942
33142
|
props.requestConnectedPeopleFetch();
|
|
@@ -32952,7 +33152,7 @@ const SmallRoute = ({
|
|
|
32952
33152
|
className,
|
|
32953
33153
|
...props
|
|
32954
33154
|
}) => (/*#__PURE__*/React.createElement("a", {
|
|
32955
|
-
className: classnames(styles$
|
|
33155
|
+
className: classnames(styles$6["route"], className, active && styles$6["is-active"]),
|
|
32956
33156
|
...props
|
|
32957
33157
|
}));
|
|
32958
33158
|
|
|
@@ -33091,7 +33291,7 @@ const topbarTheme = {
|
|
|
33091
33291
|
id: "topbar"
|
|
33092
33292
|
};
|
|
33093
33293
|
|
|
33094
|
-
var styles$
|
|
33294
|
+
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"};
|
|
33095
33295
|
|
|
33096
33296
|
function isPresent(value) {
|
|
33097
33297
|
return value !== undefined && value.trim() !== "";
|
|
@@ -33120,11 +33320,11 @@ function Field({
|
|
|
33120
33320
|
value
|
|
33121
33321
|
}) {
|
|
33122
33322
|
return /*#__PURE__*/React.createElement("div", {
|
|
33123
|
-
className: styles$
|
|
33323
|
+
className: styles$5["debug-pco-resource-field"]
|
|
33124
33324
|
}, /*#__PURE__*/React.createElement("span", {
|
|
33125
|
-
className: styles$
|
|
33325
|
+
className: styles$5["debug-pco-resource-field-label"]
|
|
33126
33326
|
}, label), /*#__PURE__*/React.createElement("span", {
|
|
33127
|
-
className: styles$
|
|
33327
|
+
className: styles$5["debug-pco-resource-field-value"]
|
|
33128
33328
|
}, value));
|
|
33129
33329
|
}
|
|
33130
33330
|
// Only staff ghosting or impersonating a person get pco:staff-access-level,
|
|
@@ -33197,13 +33397,13 @@ function DebugPanel({
|
|
|
33197
33397
|
};
|
|
33198
33398
|
}, [pcrl, env]);
|
|
33199
33399
|
return /*#__PURE__*/React.createElement("div", {
|
|
33200
|
-
className: classnames("staff-only", styles$
|
|
33400
|
+
className: classnames("staff-only", styles$5["debug-pco-resource"])
|
|
33201
33401
|
}, /*#__PURE__*/React.createElement("div", {
|
|
33202
|
-
className: styles$
|
|
33402
|
+
className: styles$5["debug-pco-resource-header"]
|
|
33203
33403
|
}, /*#__PURE__*/React.createElement("span", {
|
|
33204
|
-
className: styles$
|
|
33404
|
+
className: styles$5["debug-pco-resource-badge"]
|
|
33205
33405
|
}, "Staff"), /*#__PURE__*/React.createElement("span", {
|
|
33206
|
-
className: styles$
|
|
33406
|
+
className: styles$5["debug-pco-resource-title"]
|
|
33207
33407
|
}, "Recently Visited debug")), /*#__PURE__*/React.createElement(Field, {
|
|
33208
33408
|
label: "pco:resource",
|
|
33209
33409
|
value: describeTag("resource", resource)
|
|
@@ -33211,29 +33411,29 @@ function DebugPanel({
|
|
|
33211
33411
|
label: "pco:recently_visitable",
|
|
33212
33412
|
value: describeTag("recently_visitable", recentlyVisitable)
|
|
33213
33413
|
}), hasInvalidResource && (/*#__PURE__*/React.createElement("p", {
|
|
33214
|
-
className: styles$
|
|
33414
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33215
33415
|
}, "pco:resource isn't a PCRL (product/v2/type/id), so Recently Visited ignores it.")), visit === null && (/*#__PURE__*/React.createElement("p", {
|
|
33216
|
-
className: styles$
|
|
33416
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33217
33417
|
}, "This page is not eligible for Recently Visited")), pcrl && hasRecentlyVisitable && (/*#__PURE__*/React.createElement("p", {
|
|
33218
|
-
className: styles$
|
|
33418
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33219
33419
|
}, "Both tags are present \u2014 pco:resource takes precedence for the Recently Visited entry.")), pcrl && lookup.status === "loading" && (/*#__PURE__*/React.createElement("p", {
|
|
33220
|
-
className: styles$
|
|
33420
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33221
33421
|
}, "Looking up resource\u2026")), pcrl && lookup.status === "error" && (/*#__PURE__*/React.createElement("p", {
|
|
33222
|
-
className: styles$
|
|
33422
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33223
33423
|
}, lookup.message)), pcrl && lookup.status === "loaded" && (/*#__PURE__*/React.createElement("pre", {
|
|
33224
|
-
className: styles$
|
|
33424
|
+
className: styles$5["debug-pco-resource-json"]
|
|
33225
33425
|
}, JSON.stringify(lookup.json, null, 2))), (visit === null || visit === void 0 ? void 0 : visit.type) === "page" && (/*#__PURE__*/React.createElement("p", {
|
|
33226
|
-
className: styles$
|
|
33426
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33227
33427
|
}, "Recently Visited title: ", visit.title)), /*#__PURE__*/React.createElement("span", {
|
|
33228
|
-
className: styles$
|
|
33428
|
+
className: styles$5["debug-pco-resource-field-label"]
|
|
33229
33429
|
}, "Recently Visited"), personId ? (/*#__PURE__*/React.createElement("pre", {
|
|
33230
|
-
className: styles$
|
|
33430
|
+
className: styles$5["debug-pco-resource-json"]
|
|
33231
33431
|
}, JSON.stringify(recentlyVisited, null, 2))) : (/*#__PURE__*/React.createElement("p", {
|
|
33232
|
-
className: styles$
|
|
33432
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33233
33433
|
}, "No current person, so nothing is recorded.")));
|
|
33234
33434
|
}
|
|
33235
33435
|
|
|
33236
|
-
var styles$
|
|
33436
|
+
var styles$4 = {"drawer-wrap":"drawer-wrap-J0rSL","is-visible":"is-visible-KTME8","drawer":"drawer-j7f4l"};
|
|
33237
33437
|
|
|
33238
33438
|
function Drawer({
|
|
33239
33439
|
children,
|
|
@@ -33241,7 +33441,7 @@ function Drawer({
|
|
|
33241
33441
|
}) {
|
|
33242
33442
|
return /*#__PURE__*/React.createElement("div", {
|
|
33243
33443
|
id: id,
|
|
33244
|
-
className: styles$
|
|
33444
|
+
className: styles$4["drawer"]
|
|
33245
33445
|
}, children);
|
|
33246
33446
|
}
|
|
33247
33447
|
function DrawerWrap({
|
|
@@ -33251,7 +33451,7 @@ function DrawerWrap({
|
|
|
33251
33451
|
...props
|
|
33252
33452
|
}) {
|
|
33253
33453
|
return /*#__PURE__*/React.createElement("div", {
|
|
33254
|
-
className: classnames(styles$
|
|
33454
|
+
className: classnames(styles$4["drawer-wrap"], className, isVisible && styles$4["is-visible"]),
|
|
33255
33455
|
...props
|
|
33256
33456
|
}, children);
|
|
33257
33457
|
}
|
|
@@ -33467,7 +33667,7 @@ function Icon({
|
|
|
33467
33667
|
}));
|
|
33468
33668
|
}
|
|
33469
33669
|
|
|
33470
|
-
var styles$
|
|
33670
|
+
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"};
|
|
33471
33671
|
|
|
33472
33672
|
const COLOR_MODES = [{
|
|
33473
33673
|
icon: "toolbar.sun",
|
|
@@ -33490,11 +33690,11 @@ function ColorModeSwitcher() {
|
|
|
33490
33690
|
const updateStore = useUpdateStore();
|
|
33491
33691
|
const [counter] = React.useState(() => COLOR_MODE_INSTANCE_COUNTER++);
|
|
33492
33692
|
return /*#__PURE__*/React.createElement("fieldset", {
|
|
33493
|
-
className: styles$
|
|
33693
|
+
className: styles$3["color-mode-switcher"]
|
|
33494
33694
|
}, /*#__PURE__*/React.createElement("legend", {
|
|
33495
|
-
className: styles$
|
|
33695
|
+
className: styles$3["color-mode-switcher-label"]
|
|
33496
33696
|
}, "Appearance", " "), /*#__PURE__*/React.createElement("div", {
|
|
33497
|
-
className: styles$
|
|
33697
|
+
className: styles$3["color-mode-switcher-modes"]
|
|
33498
33698
|
}, COLOR_MODES.map(({
|
|
33499
33699
|
icon,
|
|
33500
33700
|
label,
|
|
@@ -33764,7 +33964,7 @@ const useToolbarReducer = overrides => React__namespace.useReducer(reducer, {
|
|
|
33764
33964
|
...overrides
|
|
33765
33965
|
});
|
|
33766
33966
|
|
|
33767
|
-
var styles$
|
|
33967
|
+
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"};
|
|
33768
33968
|
|
|
33769
33969
|
// PCO Box serves TLS by default, and a bare http:// frame inside an https
|
|
33770
33970
|
// product page is blocked as mixed content, so follow the page's scheme.
|
|
@@ -33936,7 +34136,7 @@ const DeveloperTools = {
|
|
|
33936
34136
|
name: "DeveloperTools"
|
|
33937
34137
|
};
|
|
33938
34138
|
|
|
33939
|
-
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"};
|
|
34139
|
+
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"};
|
|
33940
34140
|
|
|
33941
34141
|
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";
|
|
33942
34142
|
// Same stops as the AskAIBanner border gradient, so the two stay in sync.
|
|
@@ -33996,8 +34196,8 @@ function AskAIBanner(props) {
|
|
|
33996
34196
|
var _a, _b;
|
|
33997
34197
|
const isStacked = props.variant !== "inline";
|
|
33998
34198
|
return /*#__PURE__*/React.createElement("button", {
|
|
33999
|
-
className: classnames(styles.banner, {
|
|
34000
|
-
[styles.stacked]: isStacked
|
|
34199
|
+
className: classnames(styles$1.banner, {
|
|
34200
|
+
[styles$1.stacked]: isStacked
|
|
34001
34201
|
}),
|
|
34002
34202
|
onClick: () => props.onClick(),
|
|
34003
34203
|
type: "button"
|
|
@@ -34012,15 +34212,15 @@ function StackedContent({
|
|
|
34012
34212
|
title
|
|
34013
34213
|
}) {
|
|
34014
34214
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
|
|
34015
|
-
className: styles.content
|
|
34215
|
+
className: styles$1.content
|
|
34016
34216
|
}, /*#__PURE__*/React.createElement(AskAIIcon, null), /*#__PURE__*/React.createElement("span", {
|
|
34017
|
-
className: styles.text
|
|
34217
|
+
className: styles$1.text
|
|
34018
34218
|
}, /*#__PURE__*/React.createElement("span", {
|
|
34019
|
-
className: styles.title
|
|
34219
|
+
className: styles$1.title
|
|
34020
34220
|
}, title), /*#__PURE__*/React.createElement("span", {
|
|
34021
|
-
className: styles.subtitle
|
|
34221
|
+
className: styles$1.subtitle
|
|
34022
34222
|
}, "Support answers tailored to you"))), /*#__PURE__*/React.createElement(tapestryReact.Icon, {
|
|
34023
|
-
className: styles.chevron,
|
|
34223
|
+
className: styles$1.chevron,
|
|
34024
34224
|
name: "general.rightChevron",
|
|
34025
34225
|
size: "16px"
|
|
34026
34226
|
}));
|
|
@@ -34030,18 +34230,18 @@ function InlineContent({
|
|
|
34030
34230
|
title
|
|
34031
34231
|
}) {
|
|
34032
34232
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
|
|
34033
|
-
className: styles.inlineContent
|
|
34233
|
+
className: styles$1.inlineContent
|
|
34034
34234
|
}, /*#__PURE__*/React.createElement(SparkleIcon, {
|
|
34035
|
-
className: styles.sparkle,
|
|
34235
|
+
className: styles$1.sparkle,
|
|
34036
34236
|
fill: "currentColor"
|
|
34037
34237
|
}), /*#__PURE__*/React.createElement("span", {
|
|
34038
|
-
className: styles.inlineTitle
|
|
34238
|
+
className: styles$1.inlineTitle
|
|
34039
34239
|
}, title)), /*#__PURE__*/React.createElement("span", {
|
|
34040
|
-
className: styles.action
|
|
34240
|
+
className: styles$1.action
|
|
34041
34241
|
}, /*#__PURE__*/React.createElement("span", {
|
|
34042
|
-
className: styles.actionLabel
|
|
34242
|
+
className: styles$1.actionLabel
|
|
34043
34243
|
}, action), /*#__PURE__*/React.createElement(tapestryReact.Icon, {
|
|
34044
|
-
className: styles.actionChevron,
|
|
34244
|
+
className: styles$1.actionChevron,
|
|
34045
34245
|
name: "general.rightChevron",
|
|
34046
34246
|
size: "14px"
|
|
34047
34247
|
})));
|
|
@@ -34058,9 +34258,9 @@ function AskAIIcon() {
|
|
|
34058
34258
|
const borderA = `${uid}-border-a`;
|
|
34059
34259
|
const borderB = `${uid}-border-b`;
|
|
34060
34260
|
return /*#__PURE__*/React.createElement("span", {
|
|
34061
|
-
className: styles.icon
|
|
34261
|
+
className: styles$1.icon
|
|
34062
34262
|
}, /*#__PURE__*/React.createElement("svg", {
|
|
34063
|
-
className: styles.iconShape,
|
|
34263
|
+
className: styles$1.iconShape,
|
|
34064
34264
|
viewBox: "0 0 32 32",
|
|
34065
34265
|
fill: "none",
|
|
34066
34266
|
"aria-hidden": "true"
|
|
@@ -34231,7 +34431,7 @@ function AskAIIcon() {
|
|
|
34231
34431
|
stopColor: "#D48B91",
|
|
34232
34432
|
stopOpacity: "0"
|
|
34233
34433
|
})))), /*#__PURE__*/React.createElement("svg", {
|
|
34234
|
-
className: styles.iconSparkle,
|
|
34434
|
+
className: styles$1.iconSparkle,
|
|
34235
34435
|
viewBox: "0 0 19.4995 17.9999",
|
|
34236
34436
|
"aria-hidden": "true"
|
|
34237
34437
|
}, /*#__PURE__*/React.createElement("path", {
|
|
@@ -34240,6 +34440,24 @@ function AskAIIcon() {
|
|
|
34240
34440
|
})));
|
|
34241
34441
|
}
|
|
34242
34442
|
|
|
34443
|
+
var styles = {"button":"button-2NVI1","sparkle":"sparkle-ruJGP","label":"label-4NUGB"};
|
|
34444
|
+
|
|
34445
|
+
function AskAIButton({
|
|
34446
|
+
onClick,
|
|
34447
|
+
text = "Ask AI"
|
|
34448
|
+
}) {
|
|
34449
|
+
return /*#__PURE__*/React.createElement("button", {
|
|
34450
|
+
className: styles.button,
|
|
34451
|
+
onClick: () => onClick(),
|
|
34452
|
+
type: "button"
|
|
34453
|
+
}, /*#__PURE__*/React.createElement(SparkleIcon, {
|
|
34454
|
+
className: styles.sparkle,
|
|
34455
|
+
fill: "currentColor"
|
|
34456
|
+
}), /*#__PURE__*/React.createElement("span", {
|
|
34457
|
+
className: styles.label
|
|
34458
|
+
}, text));
|
|
34459
|
+
}
|
|
34460
|
+
|
|
34243
34461
|
// The article heading lives in the deepest populated hierarchy level (lvl0 is
|
|
34244
34462
|
// always "Planning Center").
|
|
34245
34463
|
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) || "";
|
|
@@ -34423,6 +34641,9 @@ function createPicobotIframe(picobotOrigin, {
|
|
|
34423
34641
|
// Contact support) and picobot's own NEW_ROUTE_RENDERED reports—so it
|
|
34424
34642
|
// always reflects the live conversation. See buildPicobotSrc.
|
|
34425
34643
|
const [loadedPath, setLoadedPath] = React.useState(iframePath || "");
|
|
34644
|
+
// Hidden until picobot paints so its blank first frame doesn't flash
|
|
34645
|
+
// white against the drawer in dark mode.
|
|
34646
|
+
const [isReady, setIsReady] = React.useState(false);
|
|
34426
34647
|
const hasReportedErrorRef = React.useRef(false);
|
|
34427
34648
|
const loadTimeoutRef = React.useRef();
|
|
34428
34649
|
const reportLoadError = React.useCallback(() => {
|
|
@@ -34450,7 +34671,10 @@ function createPicobotIframe(picobotOrigin, {
|
|
|
34450
34671
|
setLoadedPath(reportedPath);
|
|
34451
34672
|
onIframeChangePath(reportedPath);
|
|
34452
34673
|
}
|
|
34453
|
-
if (((_c = e.data) === null || _c === void 0 ? void 0 : _c.type) === "READY")
|
|
34674
|
+
if (((_c = e.data) === null || _c === void 0 ? void 0 : _c.type) === "READY") {
|
|
34675
|
+
clearTimeout(loadTimeoutRef.current);
|
|
34676
|
+
setIsReady(true);
|
|
34677
|
+
}
|
|
34454
34678
|
if (onMessage) onMessage(e.data);
|
|
34455
34679
|
}
|
|
34456
34680
|
window.addEventListener("message", handleGuestMessage);
|
|
@@ -34479,6 +34703,7 @@ function createPicobotIframe(picobotOrigin, {
|
|
|
34479
34703
|
border: 0,
|
|
34480
34704
|
flex: 1,
|
|
34481
34705
|
height: "100%",
|
|
34706
|
+
visibility: isReady ? undefined : "hidden",
|
|
34482
34707
|
width: "100%"
|
|
34483
34708
|
},
|
|
34484
34709
|
title: "Picobot"
|
|
@@ -34550,6 +34775,18 @@ function HelpdeskComponent({
|
|
|
34550
34775
|
}
|
|
34551
34776
|
}
|
|
34552
34777
|
},
|
|
34778
|
+
AskAIButton: {
|
|
34779
|
+
component: AskAIButton,
|
|
34780
|
+
properties: {
|
|
34781
|
+
onClick: {
|
|
34782
|
+
event: false,
|
|
34783
|
+
type: Function
|
|
34784
|
+
},
|
|
34785
|
+
text: {
|
|
34786
|
+
type: String
|
|
34787
|
+
}
|
|
34788
|
+
}
|
|
34789
|
+
},
|
|
34553
34790
|
CalendlyWidget: {
|
|
34554
34791
|
component: CalendlyWidget,
|
|
34555
34792
|
properties: {
|
|
@@ -36058,18 +36295,18 @@ function Toolbar({
|
|
|
36058
36295
|
return /*#__PURE__*/React.createElement(tapestryReact.ThemeProvider, {
|
|
36059
36296
|
theme: topbarTheme
|
|
36060
36297
|
}, /*#__PURE__*/React.createElement(addOns.UiExtensionDataContext, null, /*#__PURE__*/React.createElement("div", {
|
|
36061
|
-
className: styles$
|
|
36298
|
+
className: styles$2["toolbar"],
|
|
36062
36299
|
"data-pendo": "toolbar-profile-support-wrapper"
|
|
36063
36300
|
}, /*#__PURE__*/React.createElement(ToolbarButton, {
|
|
36064
36301
|
"aria-label": `Open ${userName}’s profile drawer`,
|
|
36065
36302
|
onClick: () => toggleDrawer(Profile),
|
|
36066
36303
|
"data-pendo": "toolbar-profile",
|
|
36067
|
-
className: styles$
|
|
36304
|
+
className: styles$2["toolbar-button--avatar"]
|
|
36068
36305
|
}, /*#__PURE__*/React.createElement(Avatar, {
|
|
36069
36306
|
env: env,
|
|
36070
36307
|
url: userAvatarPath
|
|
36071
36308
|
})), /*#__PURE__*/React.createElement(ToolbarButton, {
|
|
36072
|
-
className: styles$
|
|
36309
|
+
className: styles$2["toolbar-button--home"],
|
|
36073
36310
|
onClick: () => closeDrawer()
|
|
36074
36311
|
}, /*#__PURE__*/React.createElement(tapestryReact.Icon, {
|
|
36075
36312
|
name: "toolbar.planningCenter",
|
|
@@ -36106,7 +36343,7 @@ const ToolbarButton = ({
|
|
|
36106
36343
|
}) => {
|
|
36107
36344
|
return /*#__PURE__*/React.createElement("button", {
|
|
36108
36345
|
type: "button",
|
|
36109
|
-
className: classnames(styles$
|
|
36346
|
+
className: classnames(styles$2["toolbar-button"], className, current && styles$2["is-current"]),
|
|
36110
36347
|
onClick: onClick,
|
|
36111
36348
|
"data-toolbar-link": "true",
|
|
36112
36349
|
...restProps
|