@planningcenter/topbar 12.3.0 → 12.4.0-qa-958.1
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 +582 -17
- package/dist/topbar.css.map +1 -1
- package/dist/topbar.js +2358 -864
- package/dist/topbar.js.map +1 -1
- package/dist/topbar.modern.js +2362 -869
- package/dist/topbar.modern.js.map +1 -1
- package/dist/topbar.module.js +2362 -869
- package/dist/topbar.module.js.map +1 -1
- package/dist/topbar.umd.js +2360 -867
- package/dist/topbar.umd.js.map +1 -1
- package/package.json +4 -3
package/dist/topbar.umd.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@planningcenter/tapestry'), require('react'), require('@planningcenter/url'), require('@planningcenter/icons/paths/general'), require('@planningcenter/jolt-client'), require('use-sync-external-store/shim'), require('react/jsx-runtime'), require('@planningcenter/
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports', '@planningcenter/tapestry', 'react', '@planningcenter/url', '@planningcenter/icons/paths/general', '@planningcenter/jolt-client', 'use-sync-external-store/shim', 'react/jsx-runtime', '@planningcenter/tapestry-react', '@planningcenter/add-ons', '@planningcenter/icons/paths/church-center', '@planningcenter/icons/paths/people', '
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.PLANNINGCENTER = global.PLANNINGCENTER || {}, global.PLANNINGCENTER.Topbar = {}), global.tapestry, global.react, global.pcoUrl, global.general, global.Jolt, global.shim, null, global.tapestryReact, global.addOns, global.churchCenter, global.people, global.
|
|
5
|
-
})(this, (function (exports, tapestry, React, pcoUrl$1, general, Jolt, shim, jsxRuntime, tapestryReact, addOns, churchCenter, people,
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@planningcenter/tapestry'), require('react'), require('@planningcenter/url'), require('@planningcenter/icons/paths/general'), require('@planningcenter/jolt-client'), require('use-sync-external-store/shim'), require('react/jsx-runtime'), require('@planningcenter/icons/paths/toolbar'), require('@planningcenter/icons/paths/logomark'), require('@planningcenter/tapestry-react'), require('@planningcenter/add-ons'), require('@planningcenter/icons/paths/church-center'), require('@planningcenter/icons/paths/people'), require('react-calendly'), require('algoliasearch'), require('react-instantsearch-core'), require('react-transition-group'), require('@planningcenter/datetime-fmt')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports', '@planningcenter/tapestry', 'react', '@planningcenter/url', '@planningcenter/icons/paths/general', '@planningcenter/jolt-client', 'use-sync-external-store/shim', 'react/jsx-runtime', '@planningcenter/icons/paths/toolbar', '@planningcenter/icons/paths/logomark', '@planningcenter/tapestry-react', '@planningcenter/add-ons', '@planningcenter/icons/paths/church-center', '@planningcenter/icons/paths/people', 'react-calendly', 'algoliasearch', 'react-instantsearch-core', 'react-transition-group', '@planningcenter/datetime-fmt'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.PLANNINGCENTER = global.PLANNINGCENTER || {}, global.PLANNINGCENTER.Topbar = {}), global.tapestry, global.react, global.pcoUrl, global.general, global.Jolt, global.shim, null, global.toolbar, global.logomark, global.tapestryReact, global.addOns, global.churchCenter, global.people, global.reactCalendly, global.algoliasearch, global.reactInstantsearchCore, global.reactTransitionGroup, global.datetimeFmt));
|
|
5
|
+
})(this, (function (exports, tapestry, React, pcoUrl$1, general, Jolt, shim, jsxRuntime, toolbar, logomarks, tapestryReact, addOns, churchCenter, people, reactCalendly, algoliasearch, reactInstantsearchCore, reactTransitionGroup, datetimeFmt) { 'use strict';
|
|
6
6
|
|
|
7
7
|
function _interopNamespaceDefault(e) {
|
|
8
8
|
var n = Object.create(null);
|
|
@@ -23,11 +23,12 @@
|
|
|
23
23
|
|
|
24
24
|
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
25
25
|
var general__namespace = /*#__PURE__*/_interopNamespaceDefault(general);
|
|
26
|
+
var toolbar__namespace = /*#__PURE__*/_interopNamespaceDefault(toolbar);
|
|
27
|
+
var logomarks__namespace = /*#__PURE__*/_interopNamespaceDefault(logomarks);
|
|
26
28
|
var churchCenter__namespace = /*#__PURE__*/_interopNamespaceDefault(churchCenter);
|
|
27
29
|
var people__namespace = /*#__PURE__*/_interopNamespaceDefault(people);
|
|
28
|
-
var toolbar__namespace = /*#__PURE__*/_interopNamespaceDefault(toolbar);
|
|
29
30
|
|
|
30
|
-
var styles$
|
|
31
|
+
var styles$s = {"accessibility-toolbar":"accessibility-toolbar-jgjUG"};
|
|
31
32
|
|
|
32
33
|
const AccessibilityToolbar = ({
|
|
33
34
|
children
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
(_a = document.getElementById("main-content")) === null || _a === void 0 ? void 0 : _a.focus();
|
|
40
41
|
};
|
|
41
42
|
return /*#__PURE__*/React.createElement("div", {
|
|
42
|
-
className: styles$
|
|
43
|
+
className: styles$s["accessibility-toolbar"]
|
|
43
44
|
}, /*#__PURE__*/React.createElement(tapestry.Link, {
|
|
44
45
|
href: "#main-content",
|
|
45
46
|
kind: "primary",
|
|
@@ -166,12 +167,14 @@
|
|
|
166
167
|
}
|
|
167
168
|
function apiRequest(fetch, url, {
|
|
168
169
|
method = "GET",
|
|
169
|
-
data = null
|
|
170
|
+
data = null,
|
|
171
|
+
headers = {}
|
|
170
172
|
} = {}) {
|
|
171
173
|
const options = {
|
|
172
174
|
headers: {
|
|
173
175
|
Accept: "application/json",
|
|
174
|
-
"Content-Type": "application/json"
|
|
176
|
+
"Content-Type": "application/json",
|
|
177
|
+
...headers
|
|
175
178
|
},
|
|
176
179
|
method,
|
|
177
180
|
redirect: "follow"
|
|
@@ -356,11 +359,11 @@
|
|
|
356
359
|
fill: fill
|
|
357
360
|
})));
|
|
358
361
|
|
|
359
|
-
var styles$
|
|
362
|
+
var styles$r = {"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"};
|
|
360
363
|
|
|
361
364
|
function AnnouncementIcon() {
|
|
362
365
|
return /*#__PURE__*/React.createElement("svg", {
|
|
363
|
-
className: styles$
|
|
366
|
+
className: styles$r["announcement-icon"],
|
|
364
367
|
width: "16",
|
|
365
368
|
height: "16",
|
|
366
369
|
viewBox: "0 0 16 16",
|
|
@@ -485,7 +488,7 @@
|
|
|
485
488
|
} = this.props;
|
|
486
489
|
const isPromo = variant === "promo";
|
|
487
490
|
return /*#__PURE__*/React.createElement("div", {
|
|
488
|
-
className: classnames(styles$
|
|
491
|
+
className: classnames(styles$r["announcement"], styles$r[`announcement--${variant !== null && variant !== void 0 ? variant : "platform"}`]),
|
|
489
492
|
"data-announcement-variant": variant !== null && variant !== void 0 ? variant : "platform",
|
|
490
493
|
style: isPromo ? {
|
|
491
494
|
backgroundImage: promoBackgroundUrl,
|
|
@@ -494,7 +497,7 @@
|
|
|
494
497
|
backgroundSize: "cover"
|
|
495
498
|
} : undefined
|
|
496
499
|
}, !isPromo && /*#__PURE__*/React.createElement(AnnouncementIcon, null), /*#__PURE__*/React.createElement("div", {
|
|
497
|
-
className: styles$
|
|
500
|
+
className: styles$r["announcement-content"],
|
|
498
501
|
style: style,
|
|
499
502
|
...platformProps
|
|
500
503
|
}));
|
|
@@ -543,7 +546,7 @@
|
|
|
543
546
|
const isPromo = variant === "promo";
|
|
544
547
|
return /*#__PURE__*/React.createElement("button", {
|
|
545
548
|
type: "button",
|
|
546
|
-
className: classnames(styles$
|
|
549
|
+
className: classnames(styles$r.dismiss, isPromo && styles$r["dismiss--promo"]),
|
|
547
550
|
...props
|
|
548
551
|
}, /*#__PURE__*/React.createElement(X, {
|
|
549
552
|
fill: isPromo ? "currentColor" : "white",
|
|
@@ -623,9 +626,9 @@
|
|
|
623
626
|
...platformProps
|
|
624
627
|
} = this.props;
|
|
625
628
|
return /*#__PURE__*/React.createElement("div", {
|
|
626
|
-
className: styles$
|
|
629
|
+
className: styles$r["announcement"]
|
|
627
630
|
}, /*#__PURE__*/React.createElement(AnnouncementIcon, null), /*#__PURE__*/React.createElement("div", {
|
|
628
|
-
className: styles$
|
|
631
|
+
className: styles$r["announcement-content"],
|
|
629
632
|
style: style,
|
|
630
633
|
...platformProps
|
|
631
634
|
}));
|
|
@@ -636,7 +639,7 @@
|
|
|
636
639
|
}) {
|
|
637
640
|
return /*#__PURE__*/React.createElement("button", {
|
|
638
641
|
type: "button",
|
|
639
|
-
className: styles$
|
|
642
|
+
className: styles$r.dismiss,
|
|
640
643
|
...props
|
|
641
644
|
}, /*#__PURE__*/React.createElement(X, {
|
|
642
645
|
fill: "white",
|
|
@@ -791,7 +794,7 @@
|
|
|
791
794
|
return matches;
|
|
792
795
|
}
|
|
793
796
|
|
|
794
|
-
const capitalize = s => s.charAt(0).toUpperCase() + s.slice(1);
|
|
797
|
+
const capitalize$1 = s => s.charAt(0).toUpperCase() + s.slice(1);
|
|
795
798
|
const RESOLVED_COLOR_MODE_ATTRIBUTE = "data-resolved-color-mode";
|
|
796
799
|
function resolveColorMode(colorMode) {
|
|
797
800
|
if (colorMode === "system") {
|
|
@@ -1758,7 +1761,7 @@
|
|
|
1758
1761
|
})) : (/*#__PURE__*/React.createElement("div", null, "appName not supported")));
|
|
1759
1762
|
}
|
|
1760
1763
|
|
|
1761
|
-
var styles$
|
|
1764
|
+
var styles$q = {"icon-slot":"icon-slot-m3ZIk","name":"name-UNqbW"};
|
|
1762
1765
|
|
|
1763
1766
|
const AppRow = ({
|
|
1764
1767
|
appName,
|
|
@@ -1767,24 +1770,24 @@
|
|
|
1767
1770
|
}) => {
|
|
1768
1771
|
if (appName === "Accounts") {
|
|
1769
1772
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
|
|
1770
|
-
className: styles$
|
|
1773
|
+
className: styles$q["icon-slot"]
|
|
1771
1774
|
}, /*#__PURE__*/React.createElement(MonoAppIcon, {
|
|
1772
1775
|
appName: "Accounts",
|
|
1773
1776
|
colors: colors,
|
|
1774
1777
|
size: iconSize
|
|
1775
1778
|
})), /*#__PURE__*/React.createElement("span", {
|
|
1776
|
-
className: styles$
|
|
1779
|
+
className: styles$q.name
|
|
1777
1780
|
}, "Account settings"));
|
|
1778
1781
|
}
|
|
1779
1782
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ColorAppIcon, {
|
|
1780
1783
|
appName: appName.replace(/[\s-]/, ""),
|
|
1781
1784
|
size: iconSize
|
|
1782
1785
|
}), /*#__PURE__*/React.createElement("span", {
|
|
1783
|
-
className: styles$
|
|
1786
|
+
className: styles$q.name
|
|
1784
1787
|
}, appName));
|
|
1785
1788
|
};
|
|
1786
1789
|
|
|
1787
|
-
var styles$
|
|
1790
|
+
var styles$p = {"button":"button-XMbeQ","label":"label-884of","chevron":"chevron-RRg9k"};
|
|
1788
1791
|
|
|
1789
1792
|
const AppsButton = /*#__PURE__*/React.forwardRef(({
|
|
1790
1793
|
appName,
|
|
@@ -1796,10 +1799,10 @@
|
|
|
1796
1799
|
...restProps,
|
|
1797
1800
|
ref: ref,
|
|
1798
1801
|
"aria-label": `App switcher: ${appName}`,
|
|
1799
|
-
className: styles$
|
|
1802
|
+
className: styles$p.button,
|
|
1800
1803
|
kind: "neutral-inline",
|
|
1801
1804
|
label: /*#__PURE__*/React.createElement("span", {
|
|
1802
|
-
className: styles$
|
|
1805
|
+
className: styles$p.label
|
|
1803
1806
|
}, isAccounts ? "Account settings" : appName),
|
|
1804
1807
|
prefix: isAccounts ? (/*#__PURE__*/React.createElement(MonoAppIcon, {
|
|
1805
1808
|
appName: "Accounts",
|
|
@@ -1810,13 +1813,13 @@
|
|
|
1810
1813
|
size: 22
|
|
1811
1814
|
})),
|
|
1812
1815
|
suffix: /*#__PURE__*/React.createElement(DisclosureChevronIcon, {
|
|
1813
|
-
className: `${styles$
|
|
1816
|
+
className: `${styles$p.chevron} symbol`
|
|
1814
1817
|
})
|
|
1815
1818
|
});
|
|
1816
1819
|
});
|
|
1817
1820
|
AppsButton.displayName = "AppsButton";
|
|
1818
1821
|
|
|
1819
|
-
var styles$
|
|
1822
|
+
var styles$o = {"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"};
|
|
1820
1823
|
|
|
1821
1824
|
const AppsMenu = ({
|
|
1822
1825
|
appName,
|
|
@@ -1837,8 +1840,8 @@
|
|
|
1837
1840
|
}, /*#__PURE__*/React.createElement("a", {
|
|
1838
1841
|
"aria-current": isCurrent ? "page" : undefined,
|
|
1839
1842
|
"aria-label": name,
|
|
1840
|
-
className: classnames(styles$
|
|
1841
|
-
[styles$
|
|
1843
|
+
className: classnames(styles$o.link, {
|
|
1844
|
+
[styles$o.current]: isCurrent
|
|
1842
1845
|
}),
|
|
1843
1846
|
"data-pendo": `app-switcher--change-app--${name}`,
|
|
1844
1847
|
"data-turbolinks": false,
|
|
@@ -1854,22 +1857,22 @@
|
|
|
1854
1857
|
colors: colors
|
|
1855
1858
|
})), /*#__PURE__*/React.createElement(tapestry.PopoverContent, {
|
|
1856
1859
|
"aria-label": "Switch app",
|
|
1857
|
-
className: styles$
|
|
1860
|
+
className: styles$o["popover-content"],
|
|
1858
1861
|
placement: "bottom start"
|
|
1859
1862
|
}, /*#__PURE__*/React.createElement("nav", {
|
|
1860
|
-
className: styles$
|
|
1863
|
+
className: styles$o.nav,
|
|
1861
1864
|
"data-pendo": "app-switcher"
|
|
1862
1865
|
}, /*#__PURE__*/React.createElement("ul", {
|
|
1863
|
-
className: styles$
|
|
1866
|
+
className: styles$o.list,
|
|
1864
1867
|
role: "list"
|
|
1865
1868
|
}, homeProduct && renderProductLink(homeProduct.name), restProducts.map(({
|
|
1866
1869
|
name
|
|
1867
1870
|
}) => renderProductLink(name)), accountsProduct && (/*#__PURE__*/React.createElement("li", {
|
|
1868
|
-
className: styles$
|
|
1871
|
+
className: styles$o["accounts-item"]
|
|
1869
1872
|
}, /*#__PURE__*/React.createElement("a", {
|
|
1870
1873
|
"aria-current": appName === accountsProduct.name ? "page" : undefined,
|
|
1871
1874
|
"aria-label": "Account settings",
|
|
1872
|
-
className: classnames(styles$
|
|
1875
|
+
className: classnames(styles$o.link, styles$o["accounts-link"]),
|
|
1873
1876
|
"data-pendo": "app-switcher--change-app--Accounts",
|
|
1874
1877
|
"data-turbolinks": false,
|
|
1875
1878
|
href: pcoUrl$1(env)("accounts") + getAppsSwitchProductPath(accountsProduct.name)
|
|
@@ -2077,7 +2080,7 @@
|
|
|
2077
2080
|
})) : (/*#__PURE__*/React.createElement("div", null, "appName not supported"));
|
|
2078
2081
|
}
|
|
2079
2082
|
|
|
2080
|
-
var styles$
|
|
2083
|
+
var styles$n = {"unbutton":"unbutton-lEbTR"};
|
|
2081
2084
|
|
|
2082
2085
|
function Unbutton({
|
|
2083
2086
|
className,
|
|
@@ -2086,7 +2089,7 @@
|
|
|
2086
2089
|
...nativeProps
|
|
2087
2090
|
}) {
|
|
2088
2091
|
return /*#__PURE__*/React.createElement("button", {
|
|
2089
|
-
className: classnames(styles$
|
|
2092
|
+
className: classnames(styles$n.unbutton, className),
|
|
2090
2093
|
style: {
|
|
2091
2094
|
...style
|
|
2092
2095
|
},
|
|
@@ -2095,7 +2098,7 @@
|
|
|
2095
2098
|
});
|
|
2096
2099
|
}
|
|
2097
2100
|
|
|
2098
|
-
var styles$
|
|
2101
|
+
var styles$m = {"button":"button-wsipc","button-accounts":"button-accounts-zQmBM","chevron":"chevron-obyBF","is-expanded":"is-expanded-PoCQ9"};
|
|
2099
2102
|
|
|
2100
2103
|
const LegacyAppsButton = ({
|
|
2101
2104
|
appName,
|
|
@@ -2110,8 +2113,8 @@
|
|
|
2110
2113
|
"aria-haspopup": "true",
|
|
2111
2114
|
"aria-label": `App switcher: ${appName}`,
|
|
2112
2115
|
onClick: onClick,
|
|
2113
|
-
className: classnames(styles$
|
|
2114
|
-
[styles$
|
|
2116
|
+
className: classnames(styles$m.button, {
|
|
2117
|
+
[styles$m["is-expanded"]]: expanded
|
|
2115
2118
|
}),
|
|
2116
2119
|
type: "button",
|
|
2117
2120
|
style: style,
|
|
@@ -2120,7 +2123,7 @@
|
|
|
2120
2123
|
appName: "Accounts",
|
|
2121
2124
|
colors: colors
|
|
2122
2125
|
}), /*#__PURE__*/React.createElement("div", {
|
|
2123
|
-
className: styles$
|
|
2126
|
+
className: styles$m["button-accounts"]
|
|
2124
2127
|
}, "Account settings"))) : (/*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ColorAppIcon, {
|
|
2125
2128
|
appName: appName.replace(/[\s-]/, "")
|
|
2126
2129
|
}), /*#__PURE__*/React.createElement(MonoAppName, {
|
|
@@ -2128,7 +2131,7 @@
|
|
|
2128
2131
|
key: "mono-app-name",
|
|
2129
2132
|
size: 24
|
|
2130
2133
|
}))), /*#__PURE__*/React.createElement(DisclosureChevronIcon, {
|
|
2131
|
-
className: styles$
|
|
2134
|
+
className: styles$m.chevron,
|
|
2132
2135
|
key: "disclosure-chevron-icon"
|
|
2133
2136
|
}));
|
|
2134
2137
|
};
|
|
@@ -2261,7 +2264,7 @@
|
|
|
2261
2264
|
}
|
|
2262
2265
|
}
|
|
2263
2266
|
|
|
2264
|
-
var styles$
|
|
2267
|
+
var styles$l = {"popup-root":"popup-root-SeL5u","popup":"popup-nQXYW","is-visible":"is-visible-3DAUP"};
|
|
2265
2268
|
|
|
2266
2269
|
const Popup = ({
|
|
2267
2270
|
className,
|
|
@@ -2269,8 +2272,8 @@
|
|
|
2269
2272
|
isVisible,
|
|
2270
2273
|
...nativeProps
|
|
2271
2274
|
}) => (/*#__PURE__*/React.createElement(C, {
|
|
2272
|
-
className: classnames(styles$
|
|
2273
|
-
[styles$
|
|
2275
|
+
className: classnames(styles$l.popup, {
|
|
2276
|
+
[styles$l["is-visible"]]: isVisible
|
|
2274
2277
|
}, className),
|
|
2275
2278
|
...nativeProps
|
|
2276
2279
|
}));
|
|
@@ -2278,11 +2281,11 @@
|
|
|
2278
2281
|
const PopupRoot = ({
|
|
2279
2282
|
...nativeProps
|
|
2280
2283
|
}) => (/*#__PURE__*/React.createElement("div", {
|
|
2281
|
-
className: styles$
|
|
2284
|
+
className: styles$l["popup-root"],
|
|
2282
2285
|
...nativeProps
|
|
2283
2286
|
}));
|
|
2284
2287
|
|
|
2285
|
-
var styles$
|
|
2288
|
+
var styles$k = {"popup":"popup-bVhSa"};
|
|
2286
2289
|
|
|
2287
2290
|
const LegacyAppsMenu = ({
|
|
2288
2291
|
appName,
|
|
@@ -2320,7 +2323,7 @@
|
|
|
2320
2323
|
cleanup: !visible,
|
|
2321
2324
|
component: Outsider,
|
|
2322
2325
|
onOutsideClick: toggle,
|
|
2323
|
-
className: styles$
|
|
2326
|
+
className: styles$k.popup,
|
|
2324
2327
|
isVisible: visible
|
|
2325
2328
|
}, /*#__PURE__*/React.createElement("menu", {
|
|
2326
2329
|
ref: menuRef,
|
|
@@ -3230,29 +3233,121 @@ true;
|
|
|
3230
3233
|
});
|
|
3231
3234
|
}, []);
|
|
3232
3235
|
|
|
3233
|
-
const
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
const
|
|
3238
|
-
function
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3236
|
+
const collections = {
|
|
3237
|
+
general: general__namespace,
|
|
3238
|
+
toolbar: toolbar__namespace
|
|
3239
|
+
};
|
|
3240
|
+
const paths = collections;
|
|
3241
|
+
function Icon({
|
|
3242
|
+
name,
|
|
3243
|
+
size = 16,
|
|
3244
|
+
style,
|
|
3245
|
+
theme,
|
|
3246
|
+
title,
|
|
3247
|
+
...props
|
|
3248
|
+
}) {
|
|
3249
|
+
const [collection, symbol] = name.split(".");
|
|
3250
|
+
return /*#__PURE__*/React.createElement("svg", {
|
|
3251
|
+
"aria-hidden": title ? undefined : true,
|
|
3252
|
+
fill: "currentColor",
|
|
3253
|
+
height: size,
|
|
3254
|
+
role: title ? "img" : undefined,
|
|
3255
|
+
style: {
|
|
3256
|
+
color: theme && `var(${theme})`,
|
|
3257
|
+
flexShrink: 0,
|
|
3258
|
+
...style
|
|
3259
|
+
},
|
|
3260
|
+
viewBox: "0 0 16 16",
|
|
3261
|
+
width: size,
|
|
3262
|
+
...props
|
|
3263
|
+
}, title && /*#__PURE__*/React.createElement("title", null, title), /*#__PURE__*/React.createElement("path", {
|
|
3264
|
+
d: paths[collection][symbol]
|
|
3265
|
+
}));
|
|
3244
3266
|
}
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3267
|
+
|
|
3268
|
+
const SEARCHABLE_PRODUCTS = [{
|
|
3269
|
+
appName: "Calendar",
|
|
3270
|
+
slug: "calendar"
|
|
3271
|
+
}, {
|
|
3272
|
+
appName: "Check-Ins",
|
|
3273
|
+
slug: "check-ins"
|
|
3274
|
+
}, {
|
|
3275
|
+
appName: "Giving",
|
|
3276
|
+
slug: "giving"
|
|
3277
|
+
}, {
|
|
3278
|
+
appName: "Groups",
|
|
3279
|
+
slug: "groups"
|
|
3280
|
+
}, {
|
|
3281
|
+
appName: "People",
|
|
3282
|
+
slug: "people"
|
|
3283
|
+
}, {
|
|
3284
|
+
appName: "Publishing",
|
|
3285
|
+
slug: "publishing"
|
|
3286
|
+
}, {
|
|
3287
|
+
appName: "Registrations",
|
|
3288
|
+
slug: "registrations"
|
|
3289
|
+
}, {
|
|
3290
|
+
appName: "Services",
|
|
3291
|
+
slug: "services"
|
|
3292
|
+
}];
|
|
3293
|
+
const KEYBOARD_SHORTCUTS = {
|
|
3294
|
+
copyLink: {
|
|
3295
|
+
key: "c",
|
|
3296
|
+
label: "Copy link",
|
|
3297
|
+
modifierKey: true
|
|
3298
|
+
},
|
|
3299
|
+
openInNewTab: {
|
|
3300
|
+
label: "Open in new tab"},
|
|
3301
|
+
openSearch: {
|
|
3302
|
+
key: "k",
|
|
3303
|
+
label: "Open search",
|
|
3304
|
+
modifierKey: true
|
|
3305
|
+
},
|
|
3306
|
+
select: {
|
|
3307
|
+
label: "Select",
|
|
3308
|
+
modifierKey: false,
|
|
3309
|
+
next: "ArrowDown",
|
|
3310
|
+
previous: "ArrowUp"
|
|
3250
3311
|
}
|
|
3251
|
-
}
|
|
3312
|
+
};
|
|
3313
|
+
|
|
3314
|
+
const isMac = () => /Mac|iPhone|iPad|iPod/.test(navigator.platform);
|
|
3315
|
+
const hasCommandKey = event => isMac() ? event.metaKey : event.ctrlKey;
|
|
3316
|
+
const matchesShortcut = (shortcut, event) => event.key.toLowerCase() === shortcut.key && !event.altKey && !event.shiftKey && hasCommandKey(event) === shortcut.modifierKey;
|
|
3317
|
+
const isSearchShortcut = event => matchesShortcut(KEYBOARD_SHORTCUTS.openSearch, event);
|
|
3318
|
+
// Products opened their own search with `/` before this one replaced it.
|
|
3319
|
+
const isLegacySearchShortcut = event => event.key === "/" && !event.altKey && !event.ctrlKey && !event.metaKey && !isTypingIn(event.target);
|
|
3320
|
+
const isCopyShortcut = event => matchesShortcut(KEYBOARD_SHORTCUTS.copyLink, event);
|
|
3321
|
+
const isTypingIn = target => target instanceof Element && target.closest('input, select, textarea, [contenteditable]:not([contenteditable="false"])') !== null;
|
|
3322
|
+
const isTypingKey = event => event.key.length === 1 && event.key !== " " && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
3323
|
+
const verticalStep = key => ({
|
|
3324
|
+
[KEYBOARD_SHORTCUTS.select.next]: 1,
|
|
3325
|
+
[KEYBOARD_SHORTCUTS.select.previous]: -1
|
|
3326
|
+
})[key];
|
|
3327
|
+
const horizontalStep = key => ({
|
|
3328
|
+
ArrowLeft: -1,
|
|
3329
|
+
ArrowRight: 1
|
|
3330
|
+
})[key];
|
|
3252
3331
|
|
|
3253
|
-
var styles$
|
|
3332
|
+
var styles$j = {"searchButton":"searchButton-fYa5-","mobile":"mobile-ksZyA"};
|
|
3254
3333
|
|
|
3255
|
-
|
|
3334
|
+
function SearchButton({
|
|
3335
|
+
mobile = false,
|
|
3336
|
+
onClick
|
|
3337
|
+
}) {
|
|
3338
|
+
return /*#__PURE__*/React.createElement("button", {
|
|
3339
|
+
"aria-keyshortcuts": mobile ? undefined : `${isMac() ? "Meta" : "Control"}+${KEYBOARD_SHORTCUTS.openSearch.key.toUpperCase()} /`,
|
|
3340
|
+
"aria-label": "Search",
|
|
3341
|
+
className: classnames(styles$j.searchButton, {
|
|
3342
|
+
[styles$j.mobile]: mobile
|
|
3343
|
+
}),
|
|
3344
|
+
onClick: onClick,
|
|
3345
|
+
type: "button"
|
|
3346
|
+
}, /*#__PURE__*/React.createElement(Icon, {
|
|
3347
|
+
name: "general.search",
|
|
3348
|
+
size: mobile ? 18 : 14
|
|
3349
|
+
}));
|
|
3350
|
+
}
|
|
3256
3351
|
|
|
3257
3352
|
// useId isn't available on React 17, which our peer range still supports.
|
|
3258
3353
|
let instanceCount$1 = 0;
|
|
@@ -3261,173 +3356,33 @@ true;
|
|
|
3261
3356
|
return id;
|
|
3262
3357
|
}
|
|
3263
3358
|
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
}
|
|
3287
|
-
|
|
3288
|
-
}
|
|
3289
|
-
};
|
|
3290
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
3291
|
-
"aria-label": "Product",
|
|
3292
|
-
onKeyDown: handleKeyDown,
|
|
3293
|
-
role: "tablist"
|
|
3294
|
-
}, products.map(product => (/*#__PURE__*/React.createElement("button", {
|
|
3295
|
-
"aria-controls": `${id}-panel`,
|
|
3296
|
-
"aria-selected": product === selected,
|
|
3297
|
-
className: styles$b.tab,
|
|
3298
|
-
id: tabId(product.slug),
|
|
3299
|
-
key: product.slug,
|
|
3300
|
-
onClick: () => onSelect(product.slug),
|
|
3301
|
-
ref: element => {
|
|
3302
|
-
if (element) {
|
|
3303
|
-
tabRefs.current.set(product.slug, element);
|
|
3304
|
-
} else {
|
|
3305
|
-
tabRefs.current.delete(product.slug);
|
|
3306
|
-
}
|
|
3307
|
-
},
|
|
3308
|
-
role: "tab",
|
|
3309
|
-
tabIndex: product === selected ? 0 : -1,
|
|
3310
|
-
type: "button"
|
|
3311
|
-
}, product.appName)))), /*#__PURE__*/React.createElement("div", {
|
|
3312
|
-
"aria-labelledby": tabId(selected.slug),
|
|
3313
|
-
id: `${id}-panel`,
|
|
3314
|
-
role: "tabpanel"
|
|
3315
|
-
}, children));
|
|
3316
|
-
}
|
|
3317
|
-
|
|
3318
|
-
var styles$a = {"result":"result-WCnxr"};
|
|
3319
|
-
|
|
3320
|
-
function SearchResult({
|
|
3321
|
-
highlighted,
|
|
3322
|
-
id,
|
|
3323
|
-
onHighlight,
|
|
3324
|
-
result
|
|
3325
|
-
}) {
|
|
3326
|
-
const {
|
|
3327
|
-
metadata,
|
|
3328
|
-
title,
|
|
3329
|
-
url
|
|
3330
|
-
} = result.attributes;
|
|
3331
|
-
const ref = React.useRef(null);
|
|
3332
|
-
React.useEffect(() => {
|
|
3333
|
-
var _a;
|
|
3334
|
-
if (highlighted) (_a = ref.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({
|
|
3335
|
-
block: "nearest"
|
|
3336
|
-
});
|
|
3337
|
-
}, [highlighted]);
|
|
3338
|
-
// Focus stays in the search input, which points at the highlighted result
|
|
3339
|
-
// with aria-activedescendant, so results are kept out of the tab order.
|
|
3340
|
-
// Mouse move rather than enter: arrowing scrolls results under a resting
|
|
3341
|
-
// pointer, and that shouldn't steal the highlight.
|
|
3342
|
-
return /*#__PURE__*/React.createElement("a", {
|
|
3343
|
-
"aria-selected": highlighted,
|
|
3344
|
-
className: styles$a.result,
|
|
3345
|
-
href: url,
|
|
3346
|
-
id: id,
|
|
3347
|
-
onMouseMove: onHighlight,
|
|
3348
|
-
ref: ref,
|
|
3349
|
-
role: "option",
|
|
3350
|
-
tabIndex: -1
|
|
3351
|
-
}, /*#__PURE__*/React.createElement("div", null, title), /*#__PURE__*/React.createElement("div", null, summarize(metadata)));
|
|
3352
|
-
}
|
|
3353
|
-
function summarize(metadata) {
|
|
3354
|
-
return Object.values(metadata !== null && metadata !== void 0 ? metadata : {}).filter(value => typeof value === "string" && value !== "").join(" · ");
|
|
3355
|
-
}
|
|
3356
|
-
|
|
3357
|
-
const resultId = (listboxId, index) => `${listboxId}-result-${index}`;
|
|
3358
|
-
function SearchResults({
|
|
3359
|
-
search,
|
|
3360
|
-
term,
|
|
3361
|
-
...navigation
|
|
3362
|
-
}) {
|
|
3363
|
-
var _a;
|
|
3364
|
-
switch (search.status) {
|
|
3365
|
-
case "idle":
|
|
3366
|
-
return null;
|
|
3367
|
-
case "loading":
|
|
3368
|
-
if ((_a = search.groups) === null || _a === void 0 ? void 0 : _a.length) {
|
|
3369
|
-
return /*#__PURE__*/React.createElement(ResultGroups, {
|
|
3370
|
-
busy: true,
|
|
3371
|
-
groups: search.groups,
|
|
3372
|
-
...navigation
|
|
3373
|
-
});
|
|
3374
|
-
}
|
|
3375
|
-
return /*#__PURE__*/React.createElement("p", {
|
|
3376
|
-
role: "status"
|
|
3377
|
-
}, "Searching\u2026");
|
|
3378
|
-
case "error":
|
|
3379
|
-
return /*#__PURE__*/React.createElement("p", {
|
|
3380
|
-
role: "alert"
|
|
3381
|
-
}, "Search isn\u2019t working right now. Try again.");
|
|
3382
|
-
case "done":
|
|
3383
|
-
if (search.groups.length === 0) {
|
|
3384
|
-
return /*#__PURE__*/React.createElement("p", {
|
|
3385
|
-
role: "status"
|
|
3386
|
-
}, "No results for \u201C", term.trim(), "\u201D");
|
|
3387
|
-
}
|
|
3388
|
-
return /*#__PURE__*/React.createElement(ResultGroups, {
|
|
3389
|
-
groups: search.groups,
|
|
3390
|
-
...navigation
|
|
3391
|
-
});
|
|
3359
|
+
// The versions home-mobile pins, so web and mobile read the same response
|
|
3360
|
+
// shapes. Some endpoints only exist from a version on: Registrations' me,
|
|
3361
|
+
// product_search_results, and pcrl_lookups need 2025-05-01.
|
|
3362
|
+
const API_VERSIONS = {
|
|
3363
|
+
calendar: "2022-07-07",
|
|
3364
|
+
"check-ins": "2025-05-28",
|
|
3365
|
+
giving: "2019-10-18",
|
|
3366
|
+
groups: "2023-07-10",
|
|
3367
|
+
home: "2018-08-01",
|
|
3368
|
+
people: "2023-03-21",
|
|
3369
|
+
publishing: "2024-03-25",
|
|
3370
|
+
registrations: "2025-05-01",
|
|
3371
|
+
services: "2018-11-01"
|
|
3372
|
+
};
|
|
3373
|
+
// For a `/~api/{product}/v2/...` URL, the header that pins its product's
|
|
3374
|
+
// version.
|
|
3375
|
+
function apiVersionHeaders(url) {
|
|
3376
|
+
const [,, product] = new URL(url).pathname.split("/");
|
|
3377
|
+
const version = API_VERSIONS[product];
|
|
3378
|
+
if (version) {
|
|
3379
|
+
return {
|
|
3380
|
+
"X-PCO-API-Version": version
|
|
3381
|
+
};
|
|
3382
|
+
} else {
|
|
3383
|
+
return {};
|
|
3392
3384
|
}
|
|
3393
3385
|
}
|
|
3394
|
-
function ResultGroups({
|
|
3395
|
-
busy = false,
|
|
3396
|
-
groups,
|
|
3397
|
-
highlightedIndex,
|
|
3398
|
-
id,
|
|
3399
|
-
label,
|
|
3400
|
-
onHighlight
|
|
3401
|
-
}) {
|
|
3402
|
-
let index = 0;
|
|
3403
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
3404
|
-
"aria-busy": busy,
|
|
3405
|
-
"aria-label": label,
|
|
3406
|
-
id: id,
|
|
3407
|
-
role: "listbox"
|
|
3408
|
-
}, groups.map(({
|
|
3409
|
-
results,
|
|
3410
|
-
type
|
|
3411
|
-
}, groupIndex) => {
|
|
3412
|
-
const headingId = `${id}-group-${groupIndex}`;
|
|
3413
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
3414
|
-
"aria-labelledby": headingId,
|
|
3415
|
-
key: type,
|
|
3416
|
-
role: "group"
|
|
3417
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
3418
|
-
id: headingId
|
|
3419
|
-
}, type), results.map(result => {
|
|
3420
|
-
const resultIndex = index++;
|
|
3421
|
-
return /*#__PURE__*/React.createElement(SearchResult, {
|
|
3422
|
-
highlighted: resultIndex === highlightedIndex,
|
|
3423
|
-
id: resultId(id, resultIndex),
|
|
3424
|
-
key: result.id,
|
|
3425
|
-
onHighlight: () => onHighlight(resultIndex),
|
|
3426
|
-
result: result
|
|
3427
|
-
});
|
|
3428
|
-
}));
|
|
3429
|
-
}));
|
|
3430
|
-
}
|
|
3431
3386
|
|
|
3432
3387
|
const CACHE_TTL_MS = 60000;
|
|
3433
3388
|
const MAX_ENTRIES$1 = 50;
|
|
@@ -3444,7 +3399,9 @@ true;
|
|
|
3444
3399
|
}
|
|
3445
3400
|
}
|
|
3446
3401
|
function fetchIntoCache(url) {
|
|
3447
|
-
const promise = apiRequest(defaultFetch, url
|
|
3402
|
+
const promise = apiRequest(defaultFetch, url, {
|
|
3403
|
+
headers: apiVersionHeaders(url)
|
|
3404
|
+
}).then(({
|
|
3448
3405
|
json,
|
|
3449
3406
|
ok
|
|
3450
3407
|
}) => {
|
|
@@ -3500,44 +3457,52 @@ true;
|
|
|
3500
3457
|
};
|
|
3501
3458
|
}
|
|
3502
3459
|
}
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
const
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
const
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
return {
|
|
3527
|
-
|
|
3528
|
-
status: "done"
|
|
3529
|
-
};
|
|
3530
|
-
} else if (request.status === "error") {
|
|
3531
|
-
return {
|
|
3532
|
-
status: "error"
|
|
3533
|
-
};
|
|
3534
|
-
} else {
|
|
3535
|
-
return {
|
|
3536
|
-
groups,
|
|
3537
|
-
status: "loading"
|
|
3460
|
+
// One request per URL, for callers that fan out to a varying number of
|
|
3461
|
+
// endpoints. Unlike `useCachedRequest`, a URL that hasn't settled yet has no
|
|
3462
|
+
// earlier data to fall back on.
|
|
3463
|
+
function useCachedRequests(urls) {
|
|
3464
|
+
const [settled, setSettled] = React.useState({});
|
|
3465
|
+
// Keyed by value because each settle re-renders the caller, and a caller
|
|
3466
|
+
// that builds a fresh `urls` array on render would otherwise loop forever.
|
|
3467
|
+
const key = urls.join("\n");
|
|
3468
|
+
React.useEffect(() => {
|
|
3469
|
+
let current = true;
|
|
3470
|
+
const settle = result => current && setSettled(prev => ({
|
|
3471
|
+
...prev,
|
|
3472
|
+
[result.url]: result
|
|
3473
|
+
}));
|
|
3474
|
+
for (const url of key.split("\n").filter(Boolean)) {
|
|
3475
|
+
cachedRequest(url).then(data => settle({
|
|
3476
|
+
data,
|
|
3477
|
+
url
|
|
3478
|
+
}), () => settle({
|
|
3479
|
+
failed: true,
|
|
3480
|
+
url
|
|
3481
|
+
}));
|
|
3482
|
+
}
|
|
3483
|
+
return () => {
|
|
3484
|
+
current = false;
|
|
3538
3485
|
};
|
|
3539
|
-
}
|
|
3486
|
+
}, [key]);
|
|
3487
|
+
return urls.map(url => {
|
|
3488
|
+
const result = settled[url];
|
|
3489
|
+
if (!result) {
|
|
3490
|
+
return {
|
|
3491
|
+
status: "loading"
|
|
3492
|
+
};
|
|
3493
|
+
} else if ("data" in result) {
|
|
3494
|
+
return {
|
|
3495
|
+
data: result.data,
|
|
3496
|
+
status: "done"
|
|
3497
|
+
};
|
|
3498
|
+
} else {
|
|
3499
|
+
return {
|
|
3500
|
+
status: "error"
|
|
3501
|
+
};
|
|
3502
|
+
}
|
|
3503
|
+
});
|
|
3540
3504
|
}
|
|
3505
|
+
|
|
3541
3506
|
function productSearchUrl(product, query) {
|
|
3542
3507
|
const params = new URLSearchParams({
|
|
3543
3508
|
per_page: "100",
|
|
@@ -3568,6 +3533,53 @@ true;
|
|
|
3568
3533
|
};
|
|
3569
3534
|
return [...groups.values()].sort((a, b) => rank(a.type) - rank(b.type));
|
|
3570
3535
|
}
|
|
3536
|
+
|
|
3537
|
+
function useDebouncedValue(value, delay) {
|
|
3538
|
+
const [debounced, setDebounced] = React.useState(value);
|
|
3539
|
+
React.useEffect(() => {
|
|
3540
|
+
const timeout = setTimeout(() => setDebounced(value), delay);
|
|
3541
|
+
return () => clearTimeout(timeout);
|
|
3542
|
+
}, [value, delay]);
|
|
3543
|
+
return debounced;
|
|
3544
|
+
}
|
|
3545
|
+
|
|
3546
|
+
const SEARCH_DEBOUNCE_MS = 300;
|
|
3547
|
+
function useProductSearch(product, term) {
|
|
3548
|
+
const query = term.trim();
|
|
3549
|
+
const debouncedQuery = useDebouncedValue(query, SEARCH_DEBOUNCE_MS);
|
|
3550
|
+
const request = useCachedRequest(debouncedQuery ? productSearchUrl(product, debouncedQuery) : null);
|
|
3551
|
+
const response = "data" in request ? request.data : undefined;
|
|
3552
|
+
const settledProduct = useSettledProduct(product, request.status);
|
|
3553
|
+
// While a request loads, the previous response stays on screen, but only
|
|
3554
|
+
// if it was for this product: People results must never show under the
|
|
3555
|
+
// Services tab.
|
|
3556
|
+
const visibleResponse = request.status === "done" || settledProduct === product ? response : undefined;
|
|
3557
|
+
const groups = React.useMemo(() => visibleResponse && groupByType(visibleResponse), [visibleResponse]);
|
|
3558
|
+
if (!query) {
|
|
3559
|
+
return {
|
|
3560
|
+
status: "idle"
|
|
3561
|
+
};
|
|
3562
|
+
} else if (query !== debouncedQuery) {
|
|
3563
|
+
return {
|
|
3564
|
+
groups,
|
|
3565
|
+
status: "loading"
|
|
3566
|
+
};
|
|
3567
|
+
} else if (request.status === "done") {
|
|
3568
|
+
return {
|
|
3569
|
+
groups: groups !== null && groups !== void 0 ? groups : [],
|
|
3570
|
+
status: "done"
|
|
3571
|
+
};
|
|
3572
|
+
} else if (request.status === "error") {
|
|
3573
|
+
return {
|
|
3574
|
+
status: "error"
|
|
3575
|
+
};
|
|
3576
|
+
} else {
|
|
3577
|
+
return {
|
|
3578
|
+
groups,
|
|
3579
|
+
status: "loading"
|
|
3580
|
+
};
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3571
3583
|
// The product the most recent finished request was for.
|
|
3572
3584
|
function useSettledProduct(product, status) {
|
|
3573
3585
|
const [settledProduct, setSettledProduct] = React.useState();
|
|
@@ -3576,29 +3588,489 @@ true;
|
|
|
3576
3588
|
}, [product, status]);
|
|
3577
3589
|
return settledProduct;
|
|
3578
3590
|
}
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3591
|
+
|
|
3592
|
+
function parseValue(element) {
|
|
3593
|
+
const datatype = element.getAttribute("datatype");
|
|
3594
|
+
switch (datatype) {
|
|
3595
|
+
case "boolean":
|
|
3596
|
+
return element.content === "true" || element.content === "1";
|
|
3597
|
+
default:
|
|
3598
|
+
return element.content;
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
function getAllPcoMeta() {
|
|
3602
|
+
const elements = document.head.querySelectorAll(`meta[property^="pco:"]`);
|
|
3603
|
+
const meta = {};
|
|
3604
|
+
elements.forEach(e => {
|
|
3605
|
+
const property = e.getAttribute("property");
|
|
3606
|
+
meta[property.substring("pco:".length)] = parseValue(e);
|
|
3607
|
+
});
|
|
3608
|
+
return meta;
|
|
3609
|
+
}
|
|
3610
|
+
// usePcoMeta only reads each tag's content. Recently Visited also needs the
|
|
3611
|
+
// pco:recently_visitable tag's `metadata` attribute, which can change while
|
|
3612
|
+
// the content stays the same, so it is tracked in its own store.
|
|
3613
|
+
function getRecentlyVisitableMetadata() {
|
|
3614
|
+
var _a, _b;
|
|
3615
|
+
return (_b = (_a = document.head.querySelector(`meta[property="pco:recently_visitable"]`)) === null || _a === void 0 ? void 0 : _a.getAttribute("metadata")) !== null && _b !== void 0 ? _b : null;
|
|
3616
|
+
}
|
|
3617
|
+
const store = createStore(getAllPcoMeta());
|
|
3618
|
+
const recentlyVisitableMetadataStore = createStore({
|
|
3619
|
+
metadata: getRecentlyVisitableMetadata()
|
|
3620
|
+
});
|
|
3621
|
+
function sync() {
|
|
3622
|
+
const nextState = getAllPcoMeta();
|
|
3623
|
+
if (!shallowCompare(store.getState(), nextState)) store.setState(nextState);
|
|
3624
|
+
const metadata = getRecentlyVisitableMetadata();
|
|
3625
|
+
if (recentlyVisitableMetadataStore.getState().metadata !== metadata) {
|
|
3626
|
+
recentlyVisitableMetadataStore.setState({
|
|
3627
|
+
metadata
|
|
3628
|
+
});
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
const observer = new MutationObserver(sync);
|
|
3632
|
+
let hasTriggeredObserve = false;
|
|
3633
|
+
function observe() {
|
|
3634
|
+
if (hasTriggeredObserve) return;
|
|
3635
|
+
hasTriggeredObserve = true;
|
|
3636
|
+
// The module-load snapshot above can predate meta tags that appear later
|
|
3637
|
+
// in <head> (e.g. pco:resource, yielded right before </head>) if this
|
|
3638
|
+
// module's own script tag isn't deferred and runs mid-parse. Those tags
|
|
3639
|
+
// never mutate afterward, so the observer alone would never catch them.
|
|
3640
|
+
// Re-read once now, since observe() is only called once a consumer is
|
|
3641
|
+
// actually mounting, which is always after the document has fully parsed.
|
|
3642
|
+
sync();
|
|
3643
|
+
observer.observe(document.head, {
|
|
3644
|
+
attributes: true,
|
|
3645
|
+
childList: true,
|
|
3646
|
+
subtree: true
|
|
3647
|
+
});
|
|
3648
|
+
}
|
|
3649
|
+
function usePcoMeta() {
|
|
3650
|
+
observe();
|
|
3651
|
+
return shim.useSyncExternalStore(store.subscribe, () => store.getState());
|
|
3652
|
+
}
|
|
3653
|
+
function useRecentlyVisitableMetadata() {
|
|
3654
|
+
observe();
|
|
3655
|
+
return shim.useSyncExternalStore(recentlyVisitableMetadataStore.subscribe, () => recentlyVisitableMetadataStore.getState().metadata);
|
|
3586
3656
|
}
|
|
3587
3657
|
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3658
|
+
// Entries match the ones home-mobile stores, so one list can sync across web
|
|
3659
|
+
// and mobile.
|
|
3660
|
+
const ENTRY_VERSION = 3;
|
|
3661
|
+
const MAX_ENTRIES = 50;
|
|
3662
|
+
const MAX_TEXT_LENGTH = 200;
|
|
3663
|
+
const PRODUCTS$1 = ["accounts", "calendar", "check-ins", "giving", "groups", "home", "login", "people", "publishing", "registrations", "services"];
|
|
3664
|
+
// Login has no pages worth returning to.
|
|
3665
|
+
const APPS = PRODUCTS$1.filter(product => product !== "login");
|
|
3666
|
+
function isId(value) {
|
|
3667
|
+
return value !== "" && Array.from(value).every(character => character >= "0" && character <= "9");
|
|
3668
|
+
}
|
|
3669
|
+
// A PCRL looks like `<product>/v2/<resource_type>/<id>`.
|
|
3670
|
+
function isPcrl(value) {
|
|
3671
|
+
const segments = value.split("/");
|
|
3672
|
+
return segments.length === 4 && PRODUCTS$1.some(product => product === segments[0]) && isId(segments[3]);
|
|
3673
|
+
}
|
|
3674
|
+
function isApp(value) {
|
|
3675
|
+
return APPS.some(app => app === value);
|
|
3676
|
+
}
|
|
3677
|
+
// Links are built by resolving the path against its app's origin, so a stored
|
|
3678
|
+
// path must not be able to resolve anywhere else (`//evil.com`, `/\evil.com`).
|
|
3679
|
+
function isPath(value) {
|
|
3680
|
+
const origin = "https://app.invalid";
|
|
3681
|
+
try {
|
|
3682
|
+
return typeof value === "string" && value.startsWith("/") && new URL(value, origin).origin === origin;
|
|
3683
|
+
} catch {
|
|
3684
|
+
return false;
|
|
3685
|
+
}
|
|
3686
|
+
}
|
|
3687
|
+
function isNonBlankString(value) {
|
|
3688
|
+
return typeof value === "string" && value.trim() !== "";
|
|
3689
|
+
}
|
|
3690
|
+
function isOptionalString(value) {
|
|
3691
|
+
return value === undefined || typeof value === "string";
|
|
3692
|
+
}
|
|
3693
|
+
function isRecord(value) {
|
|
3694
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3695
|
+
}
|
|
3696
|
+
function isPageMetadata(value) {
|
|
3697
|
+
return value === undefined || isRecord(value) && isOptionalString(value.details) && isOptionalString(value.type);
|
|
3698
|
+
}
|
|
3699
|
+
// The query string is part of the link but not of the page's identity, so
|
|
3700
|
+
// revisiting with other filters moves the existing entry instead of adding one.
|
|
3701
|
+
function entryKey(entry) {
|
|
3702
|
+
if (entry.type === "resource") {
|
|
3703
|
+
return `resource:${entry.pcrl}`;
|
|
3704
|
+
} else {
|
|
3705
|
+
return `page:${entry.app}:${entry.path.split("?")[0]}`;
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
function isEntry(value) {
|
|
3709
|
+
if (!isRecord(value) || value.version !== ENTRY_VERSION || typeof value.visitedAt !== "string") {
|
|
3710
|
+
return false;
|
|
3711
|
+
} else if (value.type === "resource") {
|
|
3712
|
+
return typeof value.pcrl === "string" && isPcrl(value.pcrl);
|
|
3713
|
+
} else if (value.type === "page") {
|
|
3714
|
+
return isApp(value.app) && isPath(value.path) && isNonBlankString(value.title) && isPageMetadata(value.metadata);
|
|
3715
|
+
} else {
|
|
3716
|
+
return false;
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
// Cuts on code points so a surrogate pair is never split.
|
|
3720
|
+
function clampText(value) {
|
|
3721
|
+
return Array.from(value).slice(0, MAX_TEXT_LENGTH).join("");
|
|
3722
|
+
}
|
|
3723
|
+
function clampMetadata({
|
|
3724
|
+
details,
|
|
3725
|
+
type
|
|
3726
|
+
}) {
|
|
3727
|
+
return {
|
|
3728
|
+
...(details && {
|
|
3729
|
+
details: clampText(details)
|
|
3730
|
+
}),
|
|
3731
|
+
...(type && {
|
|
3732
|
+
type: clampText(type)
|
|
3733
|
+
})
|
|
3734
|
+
};
|
|
3735
|
+
}
|
|
3736
|
+
function clampVisit(visit) {
|
|
3737
|
+
if (visit.type === "resource") {
|
|
3738
|
+
return visit;
|
|
3739
|
+
} else {
|
|
3740
|
+
return {
|
|
3741
|
+
...visit,
|
|
3742
|
+
...(visit.metadata && {
|
|
3743
|
+
metadata: clampMetadata(visit.metadata)
|
|
3744
|
+
}),
|
|
3745
|
+
title: clampText(visit.title.trim())
|
|
3746
|
+
};
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
function toEntry(visit, visitedAt) {
|
|
3750
|
+
return {
|
|
3751
|
+
...clampVisit(visit),
|
|
3752
|
+
version: ENTRY_VERSION,
|
|
3753
|
+
visitedAt: visitedAt.toISOString()
|
|
3754
|
+
};
|
|
3755
|
+
}
|
|
3756
|
+
// Recents are disposable: anything unexpected reads as an empty list (or, for
|
|
3757
|
+
// a single bad entry, a list without it) rather than throwing.
|
|
3758
|
+
function parseRecentlyVisited(value) {
|
|
3759
|
+
let parsed = null;
|
|
3760
|
+
try {
|
|
3761
|
+
parsed = JSON.parse(value !== null && value !== void 0 ? value : "null");
|
|
3762
|
+
} catch {
|
|
3763
|
+
parsed = null;
|
|
3764
|
+
}
|
|
3765
|
+
if (Array.isArray(parsed)) {
|
|
3766
|
+
return parsed.filter(isEntry);
|
|
3767
|
+
} else {
|
|
3768
|
+
return [];
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
function addVisit(entries, entry) {
|
|
3772
|
+
const key = entryKey(entry);
|
|
3773
|
+
return [entry, ...entries.filter(existing => entryKey(existing) !== key)].slice(0, MAX_ENTRIES);
|
|
3774
|
+
}
|
|
3775
|
+
|
|
3776
|
+
// The app whose URL, in this environment, has the current page's host.
|
|
3777
|
+
// Reversing pcoUrl handles every environment @planningcenter/url knows about,
|
|
3778
|
+
// and guarantees the stored app rebuilds a working link.
|
|
3779
|
+
function currentApp() {
|
|
3780
|
+
var _a;
|
|
3781
|
+
return (_a = APPS.find(app => new URL(pcoUrl$1.pcoUrl(app)).hostname === window.location.hostname)) !== null && _a !== void 0 ? _a : null;
|
|
3782
|
+
}
|
|
3783
|
+
function isFilled(value) {
|
|
3784
|
+
return typeof value === "string" && value !== "";
|
|
3785
|
+
}
|
|
3786
|
+
// The tag's metadata is product-supplied JSON; only its string `type` and
|
|
3787
|
+
// `details` carry over, and an entry without either has no metadata at all.
|
|
3788
|
+
function metadataFromPage(metadata) {
|
|
3789
|
+
var _a;
|
|
3790
|
+
try {
|
|
3791
|
+
const {
|
|
3792
|
+
details,
|
|
3793
|
+
type
|
|
3794
|
+
} = (_a = JSON.parse(metadata !== null && metadata !== void 0 ? metadata : "{}")) !== null && _a !== void 0 ? _a : {};
|
|
3795
|
+
if (isFilled(details) || isFilled(type)) {
|
|
3796
|
+
return {
|
|
3797
|
+
...(isFilled(details) && {
|
|
3798
|
+
details
|
|
3799
|
+
}),
|
|
3800
|
+
...(isFilled(type) && {
|
|
3801
|
+
type
|
|
3802
|
+
})
|
|
3803
|
+
};
|
|
3804
|
+
} else {
|
|
3805
|
+
return undefined;
|
|
3806
|
+
}
|
|
3807
|
+
} catch {
|
|
3808
|
+
return undefined;
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
// Staff impersonating someone browse under `/O<org id>/P<person id>/…`.
|
|
3812
|
+
function withoutImpersonation(segments) {
|
|
3813
|
+
const [org = "", person = ""] = segments;
|
|
3814
|
+
if (org.startsWith("O") && isId(org.slice(1)) && person.startsWith("P") && isId(person.slice(1))) {
|
|
3815
|
+
return segments.slice(2);
|
|
3816
|
+
} else {
|
|
3817
|
+
return segments;
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
// Normalized the way home-mobile does it, so a page visited on web and on
|
|
3821
|
+
// mobile is one entry once the list syncs.
|
|
3822
|
+
function pathFromLocation({
|
|
3823
|
+
pathname,
|
|
3824
|
+
search
|
|
3825
|
+
}) {
|
|
3826
|
+
const segments = withoutImpersonation(pathname.split("/").filter(Boolean));
|
|
3827
|
+
if (segments.length === 0) {
|
|
3828
|
+
return "/";
|
|
3829
|
+
} else {
|
|
3830
|
+
return `/${segments.join("/")}${search.length > 1 ? search : ""}`;
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
// pco:resource is the more specific signal, so it wins when both tags are
|
|
3834
|
+
// present. A resource that isn't a PCRL is ignored rather than recorded.
|
|
3835
|
+
function visitFromPage({
|
|
3836
|
+
metadata,
|
|
3837
|
+
resource,
|
|
3838
|
+
title
|
|
3839
|
+
}) {
|
|
3840
|
+
const app = currentApp();
|
|
3841
|
+
if (resource && isPcrl(resource)) {
|
|
3842
|
+
return {
|
|
3843
|
+
pcrl: resource,
|
|
3844
|
+
type: "resource"
|
|
3845
|
+
};
|
|
3846
|
+
} else if (title && title.trim() !== "" && app) {
|
|
3847
|
+
const pageMetadata = metadataFromPage(metadata);
|
|
3848
|
+
return {
|
|
3849
|
+
app,
|
|
3850
|
+
...(pageMetadata && {
|
|
3851
|
+
metadata: pageMetadata
|
|
3852
|
+
}),
|
|
3853
|
+
path: pathFromLocation(window.location),
|
|
3854
|
+
title,
|
|
3855
|
+
type: "page"
|
|
3856
|
+
};
|
|
3857
|
+
} else {
|
|
3858
|
+
return null;
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
// The entry Recently Visited would record for the current page, or null when
|
|
3862
|
+
// the page isn't eligible. Shared by the recorder and the debug panel so the
|
|
3863
|
+
// panel always shows what actually gets stored.
|
|
3864
|
+
function useCurrentVisit() {
|
|
3865
|
+
const {
|
|
3866
|
+
recently_visitable: title,
|
|
3867
|
+
resource
|
|
3868
|
+
} = usePcoMeta();
|
|
3869
|
+
const metadata = useRecentlyVisitableMetadata();
|
|
3870
|
+
return React.useMemo(() => visitFromPage({
|
|
3871
|
+
metadata,
|
|
3872
|
+
resource,
|
|
3873
|
+
title
|
|
3874
|
+
}), [metadata, resource, title]);
|
|
3875
|
+
}
|
|
3876
|
+
// Products hand the person's ID to AppsProvider, not to the topbar itself,
|
|
3877
|
+
// and several pass it as a number.
|
|
3878
|
+
function useCurrentPersonId() {
|
|
3879
|
+
const {
|
|
3880
|
+
userId
|
|
3881
|
+
} = useStore();
|
|
3882
|
+
return userId == null ? undefined : String(userId);
|
|
3883
|
+
}
|
|
3884
|
+
|
|
3885
|
+
// Per-origin localStorage is a stopgap: each product keeps its own list until
|
|
3886
|
+
// PCA-409 moves this to storage shared across product subdomains. Only this
|
|
3887
|
+
// file should need to change when it does.
|
|
3888
|
+
function storageKey({
|
|
3889
|
+
env,
|
|
3890
|
+
personId
|
|
3891
|
+
}) {
|
|
3892
|
+
return `topbar.recently_visited.${env}.${personId}`;
|
|
3893
|
+
}
|
|
3894
|
+
// localStorage doesn't notify the tab that wrote it, so readers in this tab
|
|
3895
|
+
// learn about new visits from this counter instead.
|
|
3896
|
+
const changes = createStore({
|
|
3897
|
+
version: 0
|
|
3898
|
+
});
|
|
3899
|
+
function readRecentlyVisited(scope) {
|
|
3900
|
+
try {
|
|
3901
|
+
return parseRecentlyVisited(localStorage.getItem(storageKey(scope)));
|
|
3902
|
+
} catch {
|
|
3903
|
+
return [];
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
function recordVisit(visit, scope) {
|
|
3907
|
+
const entry = toEntry(visit, new Date());
|
|
3908
|
+
if (isEntry(entry)) {
|
|
3909
|
+
try {
|
|
3910
|
+
localStorage.setItem(storageKey(scope), JSON.stringify(addVisit(readRecentlyVisited(scope), entry)));
|
|
3911
|
+
changes.setState({
|
|
3912
|
+
version: changes.getState().version + 1
|
|
3913
|
+
});
|
|
3914
|
+
} catch {
|
|
3915
|
+
// Storage can be full or blocked; recents are disposable.
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
function useRecentlyVisited({
|
|
3920
|
+
env,
|
|
3921
|
+
personId
|
|
3922
|
+
}) {
|
|
3923
|
+
const version = shim.useSyncExternalStore(changes.subscribe, () => changes.getState().version);
|
|
3924
|
+
// `version` isn't read, but listing it re-reads storage after each visit.
|
|
3925
|
+
return React.useMemo(() => personId ? readRecentlyVisited({
|
|
3926
|
+
env,
|
|
3927
|
+
personId
|
|
3928
|
+
}) : [], [env, personId, version]);
|
|
3929
|
+
}
|
|
3930
|
+
|
|
3931
|
+
const pcoUrl = pcoUrl$1;
|
|
3932
|
+
|
|
3933
|
+
// Resolves resource entries with one pcrl_lookups request per product.
|
|
3934
|
+
// pcrl_lookups reports forbidden and deleted resources the same way, so both
|
|
3935
|
+
// become `unresolved`, as does every entry in a batch whose request failed.
|
|
3936
|
+
function useHydratedRecentlyVisited(entries, env) {
|
|
3937
|
+
const batches = React.useMemo(() => batchByProduct(entries), [entries]);
|
|
3938
|
+
const urls = React.useMemo(() => batches.map(([product, pcrls]) => pcrlLookupsUrl(product, pcrls)), [batches]);
|
|
3939
|
+
const requests = useCachedRequests(urls);
|
|
3940
|
+
const lookups = new Map();
|
|
3941
|
+
batches.forEach(([, pcrls], index) => {
|
|
3942
|
+
const request = requests[index];
|
|
3943
|
+
for (const pcrl of pcrls) lookups.set(pcrl, lookupFor(pcrl, request));
|
|
3599
3944
|
});
|
|
3600
|
-
|
|
3601
|
-
|
|
3945
|
+
return entries.map(entry => {
|
|
3946
|
+
var _a;
|
|
3947
|
+
if (entry.type === "page") {
|
|
3948
|
+
return {
|
|
3949
|
+
entry,
|
|
3950
|
+
status: "page",
|
|
3951
|
+
url: pageUrl(entry, env)
|
|
3952
|
+
};
|
|
3953
|
+
} else {
|
|
3954
|
+
const lookup = (_a = lookups.get(entry.pcrl)) !== null && _a !== void 0 ? _a : {
|
|
3955
|
+
status: "unresolved"
|
|
3956
|
+
};
|
|
3957
|
+
if (lookup.status === "resolved") {
|
|
3958
|
+
return {
|
|
3959
|
+
...lookup,
|
|
3960
|
+
entry,
|
|
3961
|
+
url: lookup.result.attributes.url
|
|
3962
|
+
};
|
|
3963
|
+
} else {
|
|
3964
|
+
return {
|
|
3965
|
+
entry,
|
|
3966
|
+
status: lookup.status
|
|
3967
|
+
};
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
});
|
|
3971
|
+
}
|
|
3972
|
+
function lookupFor(pcrl, request) {
|
|
3973
|
+
if (request.status === "done") {
|
|
3974
|
+
const result = request.data.data.find(({
|
|
3975
|
+
attributes
|
|
3976
|
+
}) => attributes.pcrl === pcrl);
|
|
3977
|
+
return result ? {
|
|
3978
|
+
result,
|
|
3979
|
+
status: "resolved"
|
|
3980
|
+
} : {
|
|
3981
|
+
status: "unresolved"
|
|
3982
|
+
};
|
|
3983
|
+
} else if (request.status === "error") {
|
|
3984
|
+
return {
|
|
3985
|
+
status: "unresolved"
|
|
3986
|
+
};
|
|
3987
|
+
} else {
|
|
3988
|
+
return {
|
|
3989
|
+
status: "loading"
|
|
3990
|
+
};
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
const RECENTLY_VISITED_PAGE_SIZE = 10;
|
|
3994
|
+
// A PCRL can only be looked up through its own product's API, which may not be
|
|
3995
|
+
// the product it was visited in. Each page of entries is batched on its own,
|
|
3996
|
+
// so loading the next page leaves earlier pages' URLs, and so their cache
|
|
3997
|
+
// keys, alone. Sorting keeps them stable as a page reorders.
|
|
3998
|
+
function batchByProduct(entries) {
|
|
3999
|
+
var _a;
|
|
4000
|
+
const batches = [];
|
|
4001
|
+
for (let start = 0; start < entries.length; start += RECENTLY_VISITED_PAGE_SIZE) {
|
|
4002
|
+
const page = new Map();
|
|
4003
|
+
for (const entry of entries.slice(start, start + RECENTLY_VISITED_PAGE_SIZE)) {
|
|
4004
|
+
if (entry.type === "resource") {
|
|
4005
|
+
const [product] = entry.pcrl.split("/");
|
|
4006
|
+
page.set(product, [...((_a = page.get(product)) !== null && _a !== void 0 ? _a : []), entry.pcrl]);
|
|
4007
|
+
}
|
|
4008
|
+
}
|
|
4009
|
+
for (const [product, pcrls] of page) {
|
|
4010
|
+
batches.push([product, [...pcrls].sort()]);
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
return batches;
|
|
4014
|
+
}
|
|
4015
|
+
function pcrlLookupsUrl(product, pcrls) {
|
|
4016
|
+
const params = new URLSearchParams({
|
|
4017
|
+
"where[pcrl]": pcrls.join(",")
|
|
4018
|
+
});
|
|
4019
|
+
return `${window.location.origin}/~api/${product}/v2/pcrl_lookups?${params}`;
|
|
4020
|
+
}
|
|
4021
|
+
// Built from the app rather than the current origin, so it stays right once
|
|
4022
|
+
// PCA-409 shares one list across product subdomains.
|
|
4023
|
+
function pageUrl(entry, env) {
|
|
4024
|
+
return new URL(entry.path, pcoUrl(env)(entry.app)).href;
|
|
4025
|
+
}
|
|
4026
|
+
|
|
4027
|
+
function useRecentVisits(env) {
|
|
4028
|
+
const personId = useCurrentPersonId();
|
|
4029
|
+
const entries = useRecentlyVisited({
|
|
4030
|
+
env,
|
|
4031
|
+
personId
|
|
4032
|
+
});
|
|
4033
|
+
const [count, setCount] = React.useState(RECENTLY_VISITED_PAGE_SIZE);
|
|
4034
|
+
const shown = React.useMemo(() => entries.slice(0, count), [entries, count]);
|
|
4035
|
+
const hydrated = useHydratedRecentlyVisited(shown, env);
|
|
4036
|
+
// Resources show up once they're found. Ones that can't be found anymore,
|
|
4037
|
+
// or that the person can no longer see, are left out.
|
|
4038
|
+
const visits = hydrated.filter(isShown);
|
|
4039
|
+
const loading = hydrated.some(({
|
|
4040
|
+
status
|
|
4041
|
+
}) => status === "loading");
|
|
4042
|
+
const showMore = React.useCallback(() => setCount(current => current + RECENTLY_VISITED_PAGE_SIZE), []);
|
|
4043
|
+
return {
|
|
4044
|
+
count,
|
|
4045
|
+
hasMore: entries.length > count,
|
|
4046
|
+
loading,
|
|
4047
|
+
showMore,
|
|
4048
|
+
visits
|
|
4049
|
+
};
|
|
4050
|
+
}
|
|
4051
|
+
function isShown(visit) {
|
|
4052
|
+
return visit.status === "page" || visit.status === "resolved";
|
|
4053
|
+
}
|
|
4054
|
+
|
|
4055
|
+
function open(url, {
|
|
4056
|
+
newTab
|
|
4057
|
+
}) {
|
|
4058
|
+
if (newTab) {
|
|
4059
|
+
window.open(url, "_blank", "noopener");
|
|
4060
|
+
} else {
|
|
4061
|
+
window.location.assign(url);
|
|
4062
|
+
}
|
|
4063
|
+
}
|
|
4064
|
+
// With text selected, ⌘C should copy that text as usual, not the link.
|
|
4065
|
+
function hasSelectedText(element) {
|
|
4066
|
+
var _a, _b;
|
|
4067
|
+
const selectedInField = element instanceof HTMLInputElement && element.selectionStart !== element.selectionEnd;
|
|
4068
|
+
return selectedInField || ((_b = (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : "") !== "";
|
|
4069
|
+
}
|
|
4070
|
+
|
|
4071
|
+
const COPIED_FEEDBACK_MS = 1500;
|
|
4072
|
+
// `copiedUrl` stays set briefly after a copy so the UI can confirm it.
|
|
4073
|
+
function useCopyLink() {
|
|
3602
4074
|
const [copiedUrl, setCopiedUrl] = React.useState(null);
|
|
3603
4075
|
React.useEffect(() => {
|
|
3604
4076
|
if (copiedUrl) {
|
|
@@ -3608,201 +4080,721 @@ true;
|
|
|
3608
4080
|
return undefined;
|
|
3609
4081
|
}
|
|
3610
4082
|
}, [copiedUrl]);
|
|
3611
|
-
const
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
4083
|
+
const copy = React.useCallback(url => {
|
|
4084
|
+
navigator.clipboard.writeText(url).then(() => setCopiedUrl(url));
|
|
4085
|
+
}, []);
|
|
4086
|
+
return {
|
|
4087
|
+
copiedUrl,
|
|
4088
|
+
copy
|
|
4089
|
+
};
|
|
4090
|
+
}
|
|
4091
|
+
|
|
4092
|
+
// Remembering which list the index belongs to puts the highlight back on the
|
|
4093
|
+
// first item whenever a new list replaces it, without an effect to reset it.
|
|
4094
|
+
// A list that grows or fills in under the same `list` keeps its highlight.
|
|
4095
|
+
// Disabled, nothing is highlighted and the keys and pointer leave it alone.
|
|
4096
|
+
function useResultNavigation(items, list, enabled = true) {
|
|
4097
|
+
const [highlight, setHighlight] = React.useState({
|
|
4098
|
+
index: 0,
|
|
4099
|
+
list
|
|
4100
|
+
});
|
|
4101
|
+
const highlightedIndex = highlight.list === list ? highlight.index : 0;
|
|
4102
|
+
const highlighted = enabled ? items[highlightedIndex] : undefined;
|
|
4103
|
+
const {
|
|
4104
|
+
copiedUrl,
|
|
4105
|
+
copy
|
|
4106
|
+
} = useCopyLink();
|
|
4107
|
+
const highlightIndex = React.useCallback(index => {
|
|
4108
|
+
if (!enabled) return;
|
|
4109
|
+
setHighlight(current => current.list === list && current.index === index ? current : {
|
|
4110
|
+
index,
|
|
4111
|
+
list
|
|
4112
|
+
});
|
|
4113
|
+
}, [enabled, list]);
|
|
3615
4114
|
const handleKeyDown = event => {
|
|
4115
|
+
if (!enabled) return;
|
|
3616
4116
|
const step = verticalStep(event.key);
|
|
3617
|
-
if (step &&
|
|
4117
|
+
if (step && items.length > 0) {
|
|
3618
4118
|
event.preventDefault();
|
|
3619
|
-
highlightIndex((highlightedIndex + step +
|
|
4119
|
+
highlightIndex((highlightedIndex + step + items.length) % items.length);
|
|
3620
4120
|
} else if (event.key === "Enter" && highlighted && !event.nativeEvent.isComposing) {
|
|
3621
4121
|
event.preventDefault();
|
|
3622
|
-
open(highlighted.
|
|
4122
|
+
open(highlighted.url, {
|
|
3623
4123
|
newTab: hasCommandKey(event)
|
|
3624
4124
|
});
|
|
3625
4125
|
} else if (isCopyShortcut(event) && highlighted && !hasSelectedText(event.currentTarget)) {
|
|
3626
4126
|
event.preventDefault();
|
|
3627
|
-
|
|
3628
|
-
url
|
|
3629
|
-
} = highlighted.attributes;
|
|
3630
|
-
navigator.clipboard.writeText(url).then(() => setCopiedUrl(url));
|
|
4127
|
+
copy(highlighted.url);
|
|
3631
4128
|
}
|
|
3632
4129
|
};
|
|
3633
4130
|
return {
|
|
3634
|
-
copied: copiedUrl !== null && copiedUrl === (highlighted === null || highlighted === void 0 ? void 0 : highlighted.
|
|
4131
|
+
copied: copiedUrl !== null && copiedUrl === (highlighted === null || highlighted === void 0 ? void 0 : highlighted.url),
|
|
3635
4132
|
handleKeyDown,
|
|
3636
4133
|
highlightIndex,
|
|
3637
4134
|
highlighted,
|
|
3638
4135
|
highlightedIndex: highlighted ? highlightedIndex : null
|
|
3639
4136
|
};
|
|
3640
4137
|
}
|
|
3641
|
-
|
|
3642
|
-
|
|
4138
|
+
|
|
4139
|
+
var styles$i = {"modal":"modal-NR3TO","mobile":"mobile-4e5rC","header":"header-c3SLe","visuallyHidden":"visuallyHidden-C28ju","panel":"panel-HlF7S"};
|
|
4140
|
+
|
|
4141
|
+
function groupSections(groups = []) {
|
|
4142
|
+
return groups.map(({
|
|
4143
|
+
results,
|
|
4144
|
+
type
|
|
4145
|
+
}) => ({
|
|
4146
|
+
heading: type,
|
|
4147
|
+
items: results.map(resultItem),
|
|
4148
|
+
key: type
|
|
4149
|
+
}));
|
|
4150
|
+
}
|
|
4151
|
+
function resultItem({
|
|
4152
|
+
attributes,
|
|
4153
|
+
id
|
|
3643
4154
|
}) {
|
|
3644
|
-
|
|
3645
|
-
|
|
4155
|
+
return {
|
|
4156
|
+
category: attributes.category,
|
|
4157
|
+
imageUrl: attributes.image_url,
|
|
4158
|
+
key: id,
|
|
4159
|
+
product: attributes.product,
|
|
4160
|
+
subtitle: summarize(attributes.metadata),
|
|
4161
|
+
title: attributes.title,
|
|
4162
|
+
url: attributes.url
|
|
4163
|
+
};
|
|
4164
|
+
}
|
|
4165
|
+
function visitItem(visit) {
|
|
4166
|
+
return {
|
|
4167
|
+
key: entryKey(visit.entry),
|
|
4168
|
+
...describeVisit(visit)
|
|
4169
|
+
};
|
|
4170
|
+
}
|
|
4171
|
+
function summarize(metadata) {
|
|
4172
|
+
return Object.values(metadata !== null && metadata !== void 0 ? metadata : {}).filter(value => typeof value === "string" && value !== "").join(" · ");
|
|
4173
|
+
}
|
|
4174
|
+
// Unlike search results, recents come from every product, so each one names
|
|
4175
|
+
// its product and type.
|
|
4176
|
+
function describeVisit(visit) {
|
|
4177
|
+
var _a, _b;
|
|
4178
|
+
const subtitle = (...parts) => parts.filter(Boolean).join(" · ");
|
|
4179
|
+
if (visit.status === "page") {
|
|
4180
|
+
const {
|
|
4181
|
+
app,
|
|
4182
|
+
metadata,
|
|
4183
|
+
title
|
|
4184
|
+
} = visit.entry;
|
|
4185
|
+
return {
|
|
4186
|
+
category: "generic",
|
|
4187
|
+
imageUrl: null,
|
|
4188
|
+
product: app,
|
|
4189
|
+
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 : ""),
|
|
4190
|
+
title,
|
|
4191
|
+
url: visit.url
|
|
4192
|
+
};
|
|
3646
4193
|
} else {
|
|
3647
|
-
|
|
4194
|
+
const {
|
|
4195
|
+
category,
|
|
4196
|
+
image_url,
|
|
4197
|
+
metadata,
|
|
4198
|
+
product,
|
|
4199
|
+
title,
|
|
4200
|
+
type
|
|
4201
|
+
} = visit.result.attributes;
|
|
4202
|
+
return {
|
|
4203
|
+
category,
|
|
4204
|
+
imageUrl: image_url,
|
|
4205
|
+
product,
|
|
4206
|
+
subtitle: subtitle(productName(product), type, summarize(metadata)),
|
|
4207
|
+
title,
|
|
4208
|
+
url: visit.url
|
|
4209
|
+
};
|
|
3648
4210
|
}
|
|
3649
4211
|
}
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
return
|
|
4212
|
+
// check-ins → Check-Ins
|
|
4213
|
+
function productName(slug) {
|
|
4214
|
+
return slug.split("-").map(capitalize).join("-");
|
|
4215
|
+
}
|
|
4216
|
+
function capitalize(word) {
|
|
4217
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
3656
4218
|
}
|
|
3657
4219
|
|
|
3658
|
-
const
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
4220
|
+
const STORAGE_KEY = "topbar.product_search.last_product";
|
|
4221
|
+
function readLastProduct() {
|
|
4222
|
+
try {
|
|
4223
|
+
return localStorage.getItem(STORAGE_KEY);
|
|
4224
|
+
} catch {
|
|
4225
|
+
return null;
|
|
4226
|
+
}
|
|
4227
|
+
}
|
|
4228
|
+
function saveLastProduct(slug) {
|
|
4229
|
+
try {
|
|
4230
|
+
localStorage.setItem(STORAGE_KEY, slug);
|
|
4231
|
+
} catch {
|
|
4232
|
+
// Storage can be full or blocked; search still works without it.
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
function initialProduct(products) {
|
|
4236
|
+
var _a, _b, _c;
|
|
4237
|
+
const lastProduct = readLastProduct();
|
|
4238
|
+
const bySlug = slug => products.find(product => product.slug === slug);
|
|
4239
|
+
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];
|
|
4240
|
+
}
|
|
4241
|
+
|
|
4242
|
+
var styles$h = {"container":"container-YDdM1","hint":"hint-017Rd","keycap":"keycap-ENJM2","copied":"copied-DUFa1"};
|
|
4243
|
+
|
|
4244
|
+
const SYMBOLS = {
|
|
4245
|
+
command: {
|
|
4246
|
+
label: "⌘",
|
|
4247
|
+
paths: ["M7 9a2 2 0 1 1 2 -2v10a2 2 0 1 1 -2 -2h10a2 2 0 1 1 -2 2v-10a2 2 0 1 1 2 2h-10"]
|
|
4248
|
+
},
|
|
4249
|
+
down: {
|
|
4250
|
+
label: "↓",
|
|
4251
|
+
paths: ["M12 6l0 12", "M16 14l-4 4", "M8 14l4 4"]
|
|
4252
|
+
},
|
|
4253
|
+
return: {
|
|
4254
|
+
label: "↵",
|
|
4255
|
+
paths: ["M18 6v6a3 3 0 0 1 -3 3h-10l4 -4m0 8l-4 -4"]
|
|
4256
|
+
},
|
|
4257
|
+
up: {
|
|
4258
|
+
label: "↑",
|
|
4259
|
+
paths: ["M12 6l0 12", "M16 10l-4 -4", "M8 10l4 -4"]
|
|
4260
|
+
}
|
|
3676
4261
|
};
|
|
3677
|
-
function
|
|
3678
|
-
|
|
3679
|
-
|
|
4262
|
+
function Keycap(props) {
|
|
4263
|
+
return /*#__PURE__*/React.createElement("kbd", {
|
|
4264
|
+
className: styles$h.keycap
|
|
4265
|
+
}, "symbol" in props ? (/*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("svg", {
|
|
4266
|
+
"aria-hidden": true,
|
|
4267
|
+
fill: "none",
|
|
4268
|
+
stroke: "currentColor",
|
|
4269
|
+
strokeLinecap: "round",
|
|
4270
|
+
strokeLinejoin: "round",
|
|
4271
|
+
viewBox: "0 0 24 24"
|
|
4272
|
+
}, SYMBOLS[props.symbol].paths.map(d => (/*#__PURE__*/React.createElement("path", {
|
|
4273
|
+
d: d,
|
|
4274
|
+
key: d
|
|
4275
|
+
})))), /*#__PURE__*/React.createElement("span", {
|
|
4276
|
+
className: styles$i.visuallyHidden
|
|
4277
|
+
}, SYMBOLS[props.symbol].label))) : props.label);
|
|
4278
|
+
}
|
|
4279
|
+
function CommandKey() {
|
|
4280
|
+
return isMac() ? /*#__PURE__*/React.createElement(Keycap, {
|
|
4281
|
+
symbol: "command"
|
|
4282
|
+
}) : /*#__PURE__*/React.createElement(Keycap, {
|
|
4283
|
+
label: "Ctrl"
|
|
3680
4284
|
});
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
4285
|
+
}
|
|
4286
|
+
function EnterKey() {
|
|
4287
|
+
return isMac() ? /*#__PURE__*/React.createElement(Keycap, {
|
|
4288
|
+
symbol: "return"
|
|
4289
|
+
}) : /*#__PURE__*/React.createElement(Keycap, {
|
|
4290
|
+
label: "Enter"
|
|
3687
4291
|
});
|
|
3688
4292
|
}
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
4293
|
+
const {
|
|
4294
|
+
copyLink,
|
|
4295
|
+
openInNewTab,
|
|
4296
|
+
openSearch,
|
|
4297
|
+
select
|
|
4298
|
+
} = KEYBOARD_SHORTCUTS;
|
|
4299
|
+
function KeyboardHints({
|
|
4300
|
+
copied
|
|
4301
|
+
}) {
|
|
4302
|
+
return /*#__PURE__*/React.createElement("ul", {
|
|
4303
|
+
"aria-label": "Keyboard shortcuts",
|
|
4304
|
+
className: styles$h.container
|
|
4305
|
+
}, /*#__PURE__*/React.createElement("li", {
|
|
4306
|
+
className: styles$h.hint
|
|
4307
|
+
}, /*#__PURE__*/React.createElement(tapestry.Flex, {
|
|
4308
|
+
gap: 0.5
|
|
4309
|
+
}, select.modifierKey, /*#__PURE__*/React.createElement(Keycap, {
|
|
4310
|
+
symbol: "up"
|
|
4311
|
+
}), /*#__PURE__*/React.createElement(Keycap, {
|
|
4312
|
+
symbol: "down"
|
|
4313
|
+
})), select.label), /*#__PURE__*/React.createElement("li", {
|
|
4314
|
+
className: styles$h.hint
|
|
4315
|
+
}, /*#__PURE__*/React.createElement(tapestry.Flex, {
|
|
4316
|
+
gap: 0.5
|
|
4317
|
+
}, /*#__PURE__*/React.createElement(CommandKey, null), /*#__PURE__*/React.createElement(EnterKey, null)), openInNewTab.label), /*#__PURE__*/React.createElement("li", {
|
|
4318
|
+
className: classnames(styles$h.hint, copied && styles$h.copied)
|
|
4319
|
+
}, /*#__PURE__*/React.createElement(tapestry.Flex, {
|
|
4320
|
+
gap: 0.5
|
|
4321
|
+
}, /*#__PURE__*/React.createElement(CommandKey, null), /*#__PURE__*/React.createElement(Keycap, {
|
|
4322
|
+
label: copyLink.key.toUpperCase()
|
|
4323
|
+
})), /*#__PURE__*/React.createElement("span", {
|
|
4324
|
+
role: "status"
|
|
4325
|
+
}, copied ? "Copied!" : copyLink.label)), /*#__PURE__*/React.createElement("li", {
|
|
4326
|
+
className: styles$h.hint
|
|
4327
|
+
}, /*#__PURE__*/React.createElement(tapestry.Flex, {
|
|
4328
|
+
gap: 0.5
|
|
4329
|
+
}, /*#__PURE__*/React.createElement(CommandKey, null), /*#__PURE__*/React.createElement(Keycap, {
|
|
4330
|
+
label: openSearch.key.toUpperCase()
|
|
4331
|
+
})), openSearch.label));
|
|
4332
|
+
}
|
|
4333
|
+
|
|
4334
|
+
var styles$g = {"icon":"icon-XKLvN","squircle":"squircle-JCHr6"};
|
|
4335
|
+
|
|
4336
|
+
const PRODUCTS = {
|
|
4337
|
+
accounts: {
|
|
4338
|
+
gradient: "--t-fill-color-product-accounts-gradient-brand",
|
|
4339
|
+
path: logomarks__namespace.accounts
|
|
4340
|
+
},
|
|
4341
|
+
api: {
|
|
4342
|
+
gradient: "--t-fill-color-product-api-gradient-brand",
|
|
4343
|
+
path: logomarks__namespace.api
|
|
4344
|
+
},
|
|
4345
|
+
calendar: {
|
|
4346
|
+
gradient: "--t-fill-color-product-calendar-gradient-brand",
|
|
4347
|
+
path: logomarks__namespace.calendar
|
|
4348
|
+
},
|
|
4349
|
+
"check-ins": {
|
|
4350
|
+
gradient: "--t-fill-color-product-checkins-gradient-brand",
|
|
4351
|
+
path: logomarks__namespace.checkIns
|
|
4352
|
+
},
|
|
4353
|
+
"church-center": {
|
|
4354
|
+
gradient: "--t-fill-color-product-cc-gradient-brand",
|
|
4355
|
+
path: logomarks__namespace.churchCenter
|
|
4356
|
+
},
|
|
4357
|
+
giving: {
|
|
4358
|
+
gradient: "--t-fill-color-product-giving-gradient-brand",
|
|
4359
|
+
path: logomarks__namespace.giving
|
|
4360
|
+
},
|
|
4361
|
+
groups: {
|
|
4362
|
+
gradient: "--t-fill-color-product-groups-gradient-brand",
|
|
4363
|
+
path: logomarks__namespace.groups
|
|
4364
|
+
},
|
|
4365
|
+
home: {
|
|
4366
|
+
gradient: "--t-fill-color-product-home-gradient-brand",
|
|
4367
|
+
path: logomarks__namespace.home
|
|
4368
|
+
},
|
|
4369
|
+
"music-stand": {
|
|
4370
|
+
gradient: "--t-fill-color-product-musicstand-gradient-brand",
|
|
4371
|
+
path: logomarks__namespace.musicStand
|
|
4372
|
+
},
|
|
4373
|
+
people: {
|
|
4374
|
+
gradient: "--t-fill-color-product-people-gradient-brand",
|
|
4375
|
+
path: logomarks__namespace.people
|
|
4376
|
+
},
|
|
4377
|
+
publishing: {
|
|
4378
|
+
gradient: "--t-fill-color-product-publishing-gradient-brand",
|
|
4379
|
+
path: logomarks__namespace.publishing
|
|
4380
|
+
},
|
|
4381
|
+
registrations: {
|
|
4382
|
+
gradient: "--t-fill-color-product-registrations-gradient-brand",
|
|
4383
|
+
path: logomarks__namespace.registrations
|
|
4384
|
+
},
|
|
4385
|
+
services: {
|
|
4386
|
+
gradient: "--t-fill-color-product-services-gradient-brand",
|
|
4387
|
+
path: logomarks__namespace.services
|
|
3706
4388
|
}
|
|
4389
|
+
};
|
|
4390
|
+
const isProductIconSlug = slug => Object.prototype.hasOwnProperty.call(PRODUCTS, slug);
|
|
4391
|
+
const maskFor = path => `url("data:image/svg+xml,${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="${path}"/></svg>`)}")`;
|
|
4392
|
+
function ProductIcon({
|
|
4393
|
+
size = 16,
|
|
4394
|
+
slug
|
|
4395
|
+
}) {
|
|
4396
|
+
const {
|
|
4397
|
+
gradient,
|
|
4398
|
+
path
|
|
4399
|
+
} = PRODUCTS[slug];
|
|
4400
|
+
const mask = maskFor(path);
|
|
4401
|
+
return /*#__PURE__*/React.createElement("span", {
|
|
4402
|
+
"aria-hidden": true,
|
|
4403
|
+
className: styles$g.icon,
|
|
4404
|
+
style: {
|
|
4405
|
+
height: size,
|
|
4406
|
+
width: size
|
|
4407
|
+
}
|
|
4408
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
4409
|
+
className: styles$g.squircle,
|
|
4410
|
+
style: {
|
|
4411
|
+
WebkitMaskImage: mask,
|
|
4412
|
+
background: `var(${gradient})`,
|
|
4413
|
+
maskImage: mask
|
|
4414
|
+
}
|
|
4415
|
+
}));
|
|
3707
4416
|
}
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
4417
|
+
|
|
4418
|
+
var styles$f = {"picker":"picker-iuzzj","option":"option--7LXg"};
|
|
4419
|
+
|
|
4420
|
+
// Phones have no room for a tab per product, so they pick from a dropdown.
|
|
4421
|
+
// Complex mode renders each option's logomark, in the list and the trigger.
|
|
4422
|
+
function ProductSelect({
|
|
4423
|
+
children,
|
|
4424
|
+
onSelect,
|
|
4425
|
+
products,
|
|
4426
|
+
selected
|
|
4427
|
+
}) {
|
|
4428
|
+
const options = products.map(({
|
|
4429
|
+
appName,
|
|
3712
4430
|
slug
|
|
3713
|
-
}) =>
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
4431
|
+
}) => ({
|
|
4432
|
+
label: (/*#__PURE__*/React.createElement("span", {
|
|
4433
|
+
className: styles$f.option
|
|
4434
|
+
}, /*#__PURE__*/React.createElement(ProductIcon, {
|
|
4435
|
+
slug: slug
|
|
4436
|
+
}), appName)),
|
|
4437
|
+
textValue: appName,
|
|
4438
|
+
value: slug
|
|
4439
|
+
}));
|
|
4440
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
4441
|
+
className: styles$f.picker
|
|
4442
|
+
}, /*#__PURE__*/React.createElement(tapestry.Select, {
|
|
4443
|
+
"aria-label": "Product",
|
|
4444
|
+
complex: true,
|
|
4445
|
+
onChange: event => onSelect(event.target.value),
|
|
4446
|
+
options: options,
|
|
4447
|
+
value: selected.slug
|
|
4448
|
+
})), /*#__PURE__*/React.createElement("div", {
|
|
4449
|
+
className: styles$i.panel
|
|
4450
|
+
}, children));
|
|
4451
|
+
}
|
|
4452
|
+
|
|
4453
|
+
var styles$e = {"tablist":"tablist-7ASvc","tab":"tab-dz8ki"};
|
|
4454
|
+
|
|
4455
|
+
function ProductTabs({
|
|
4456
|
+
children,
|
|
4457
|
+
onKeyDown,
|
|
4458
|
+
onSelect,
|
|
4459
|
+
products,
|
|
4460
|
+
selected
|
|
4461
|
+
}) {
|
|
4462
|
+
const id = useInstanceId("product-tabs");
|
|
4463
|
+
const tabId = slug => `${id}-tab-${slug}`;
|
|
4464
|
+
const tabRefs = React.useRef(new Map());
|
|
4465
|
+
const handleKeyDown = event => {
|
|
4466
|
+
var _a;
|
|
4467
|
+
const offset = horizontalStep(event.key);
|
|
4468
|
+
if (offset) {
|
|
4469
|
+
event.preventDefault();
|
|
4470
|
+
const index = products.indexOf(selected);
|
|
4471
|
+
const next = products[(index + offset + products.length) % products.length];
|
|
4472
|
+
onSelect(next.slug);
|
|
4473
|
+
(_a = tabRefs.current.get(next.slug)) === null || _a === void 0 ? void 0 : _a.focus();
|
|
4474
|
+
} else {
|
|
4475
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
|
|
4476
|
+
}
|
|
4477
|
+
};
|
|
4478
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
4479
|
+
"aria-label": "Product",
|
|
4480
|
+
className: styles$e.tablist,
|
|
4481
|
+
onKeyDown: handleKeyDown,
|
|
4482
|
+
role: "tablist"
|
|
4483
|
+
}, products.map(product => (/*#__PURE__*/React.createElement("button", {
|
|
4484
|
+
"aria-controls": `${id}-panel`,
|
|
4485
|
+
"aria-selected": product === selected,
|
|
4486
|
+
className: styles$e.tab,
|
|
4487
|
+
id: tabId(product.slug),
|
|
4488
|
+
key: product.slug,
|
|
4489
|
+
onClick: () => onSelect(product.slug),
|
|
4490
|
+
ref: element => {
|
|
4491
|
+
if (element) {
|
|
4492
|
+
tabRefs.current.set(product.slug, element);
|
|
4493
|
+
} else {
|
|
4494
|
+
tabRefs.current.delete(product.slug);
|
|
3726
4495
|
}
|
|
4496
|
+
},
|
|
4497
|
+
role: "tab",
|
|
4498
|
+
tabIndex: product === selected ? 0 : -1,
|
|
4499
|
+
type: "button"
|
|
4500
|
+
}, /*#__PURE__*/React.createElement(ProductIcon, {
|
|
4501
|
+
slug: product.slug
|
|
4502
|
+
}), product === selected ? product.appName : (/*#__PURE__*/React.createElement("span", {
|
|
4503
|
+
className: styles$i.visuallyHidden
|
|
4504
|
+
}, product.appName)))))), /*#__PURE__*/React.createElement("div", {
|
|
4505
|
+
"aria-labelledby": tabId(selected.slug),
|
|
4506
|
+
className: styles$i.panel,
|
|
4507
|
+
id: `${id}-panel`,
|
|
4508
|
+
role: "tabpanel"
|
|
4509
|
+
}, children));
|
|
4510
|
+
}
|
|
4511
|
+
|
|
4512
|
+
function EndOfResultsObserver({
|
|
4513
|
+
onVisible
|
|
4514
|
+
}) {
|
|
4515
|
+
const ref = React.useRef(null);
|
|
4516
|
+
React.useEffect(() => {
|
|
4517
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
4518
|
+
if (entry.isIntersecting) onVisible();
|
|
3727
4519
|
});
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
products: settled.products,
|
|
3735
|
-
status: "done"
|
|
3736
|
-
};
|
|
3737
|
-
} else {
|
|
3738
|
-
return {
|
|
3739
|
-
status: "loading"
|
|
3740
|
-
};
|
|
3741
|
-
}
|
|
4520
|
+
if (ref.current) observer.observe(ref.current);
|
|
4521
|
+
return () => observer.disconnect();
|
|
4522
|
+
}, [onVisible]);
|
|
4523
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
4524
|
+
ref: ref
|
|
4525
|
+
});
|
|
3742
4526
|
}
|
|
3743
|
-
|
|
3744
|
-
|
|
4527
|
+
|
|
4528
|
+
const resultId = (listboxId, index) => `${listboxId}-result-${index}`;
|
|
4529
|
+
|
|
4530
|
+
var styles$d = {"result":"result-Thq2C","title":"title-KYUEn","subtitle":"subtitle-R2haq","image":"image-KVDor","avatar":"avatar-6Gh62","photo":"photo-FZR3Q","badge":"badge-trUKt"};
|
|
4531
|
+
|
|
4532
|
+
function SearchResult({
|
|
4533
|
+
category,
|
|
4534
|
+
highlighted,
|
|
4535
|
+
id,
|
|
4536
|
+
imageUrl,
|
|
4537
|
+
onHighlight,
|
|
4538
|
+
product,
|
|
4539
|
+
subtitle,
|
|
4540
|
+
title,
|
|
4541
|
+
url
|
|
4542
|
+
}) {
|
|
4543
|
+
const ref = React.useRef(null);
|
|
4544
|
+
// A hovered result is already under the pointer, and scrolling it clear of
|
|
4545
|
+
// the panel's scroll padding would slide the list out from under the mouse.
|
|
4546
|
+
const highlightedByPointer = React.useRef(false);
|
|
4547
|
+
React.useEffect(() => {
|
|
4548
|
+
var _a;
|
|
4549
|
+
if (!highlighted) return;
|
|
4550
|
+
if (highlightedByPointer.current) {
|
|
4551
|
+
highlightedByPointer.current = false;
|
|
4552
|
+
} else {
|
|
4553
|
+
(_a = ref.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({
|
|
4554
|
+
block: "nearest"
|
|
4555
|
+
});
|
|
4556
|
+
}
|
|
4557
|
+
}, [highlighted]);
|
|
4558
|
+
const highlightFromPointer = () => {
|
|
4559
|
+
if (highlighted) return;
|
|
4560
|
+
highlightedByPointer.current = true;
|
|
4561
|
+
onHighlight();
|
|
4562
|
+
};
|
|
4563
|
+
// Focus stays in the search input, which points at the highlighted result
|
|
4564
|
+
// with aria-activedescendant, so results are kept out of the tab order.
|
|
4565
|
+
// Mouse move rather than enter: arrowing scrolls results under a resting
|
|
4566
|
+
// pointer, and that shouldn't steal the highlight.
|
|
4567
|
+
return /*#__PURE__*/React.createElement("a", {
|
|
4568
|
+
"aria-selected": highlighted,
|
|
4569
|
+
className: styles$d.result,
|
|
4570
|
+
href: url,
|
|
4571
|
+
id: id,
|
|
4572
|
+
onMouseMove: highlightFromPointer,
|
|
4573
|
+
ref: ref,
|
|
4574
|
+
role: "option",
|
|
4575
|
+
tabIndex: -1
|
|
4576
|
+
}, /*#__PURE__*/React.createElement(ResultImage, {
|
|
4577
|
+
category: category,
|
|
4578
|
+
imageUrl: imageUrl,
|
|
4579
|
+
product: product
|
|
4580
|
+
}), /*#__PURE__*/React.createElement("span", {
|
|
4581
|
+
className: styles$d.title
|
|
4582
|
+
}, title), /*#__PURE__*/React.createElement("span", {
|
|
4583
|
+
className: styles$d.subtitle
|
|
4584
|
+
}, subtitle));
|
|
4585
|
+
}
|
|
4586
|
+
// A person's photo or the result's own image, badged with its product, or
|
|
4587
|
+
// just the product's logomark when there's no image.
|
|
4588
|
+
function ResultImage({
|
|
4589
|
+
category,
|
|
4590
|
+
imageUrl,
|
|
4591
|
+
product
|
|
4592
|
+
}) {
|
|
4593
|
+
const icon = size => isProductIconSlug(product) ? (/*#__PURE__*/React.createElement(ProductIcon, {
|
|
4594
|
+
size: size,
|
|
4595
|
+
slug: product
|
|
4596
|
+
})) : null;
|
|
4597
|
+
return /*#__PURE__*/React.createElement("span", {
|
|
4598
|
+
"aria-hidden": true,
|
|
4599
|
+
className: styles$d.image
|
|
4600
|
+
}, imageUrl ? (/*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("img", {
|
|
4601
|
+
alt: "",
|
|
4602
|
+
className: category === "profile" ? styles$d.avatar : styles$d.photo,
|
|
4603
|
+
loading: "lazy",
|
|
4604
|
+
src: imageUrl
|
|
4605
|
+
}), /*#__PURE__*/React.createElement("span", {
|
|
4606
|
+
className: styles$d.badge
|
|
4607
|
+
}, icon(16)))) : icon("100%"));
|
|
3745
4608
|
}
|
|
3746
4609
|
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
4610
|
+
var styles$c = {"message":"message-1-PP-","heading":"heading-LioUF"};
|
|
4611
|
+
|
|
4612
|
+
// Sections with a heading render as labelled groups; ones without render
|
|
4613
|
+
// their items straight into the listbox. Indexes run across every section.
|
|
4614
|
+
function ResultListbox({
|
|
4615
|
+
busy,
|
|
4616
|
+
highlightedIndex,
|
|
4617
|
+
id,
|
|
4618
|
+
label,
|
|
4619
|
+
labelledBy,
|
|
4620
|
+
onHighlight,
|
|
4621
|
+
sections
|
|
3754
4622
|
}) {
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
4623
|
+
let index = 0;
|
|
4624
|
+
const renderItem = item => {
|
|
4625
|
+
const itemIndex = index++;
|
|
4626
|
+
return /*#__PURE__*/React.createElement(SearchResult, {
|
|
4627
|
+
category: item.category,
|
|
4628
|
+
highlighted: itemIndex === highlightedIndex,
|
|
4629
|
+
id: resultId(id, itemIndex),
|
|
4630
|
+
imageUrl: item.imageUrl,
|
|
4631
|
+
key: item.key,
|
|
4632
|
+
onHighlight: () => onHighlight(itemIndex),
|
|
4633
|
+
product: item.product,
|
|
4634
|
+
subtitle: item.subtitle,
|
|
4635
|
+
title: item.title,
|
|
4636
|
+
url: item.url
|
|
3759
4637
|
});
|
|
4638
|
+
};
|
|
4639
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
4640
|
+
"aria-busy": busy,
|
|
4641
|
+
"aria-label": label,
|
|
4642
|
+
"aria-labelledby": labelledBy,
|
|
4643
|
+
id: id,
|
|
4644
|
+
role: "listbox"
|
|
4645
|
+
}, sections.map((section, sectionIndex) => {
|
|
4646
|
+
if (section.heading === undefined) {
|
|
4647
|
+
return /*#__PURE__*/React.createElement(React.Fragment, {
|
|
4648
|
+
key: section.key
|
|
4649
|
+
}, section.items.map(renderItem));
|
|
4650
|
+
}
|
|
4651
|
+
const headingId = `${id}-group-${sectionIndex}`;
|
|
4652
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
4653
|
+
"aria-labelledby": headingId,
|
|
4654
|
+
key: section.key,
|
|
4655
|
+
role: "group"
|
|
4656
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
4657
|
+
className: styles$c.heading,
|
|
4658
|
+
id: headingId
|
|
4659
|
+
}, section.heading), section.items.map(renderItem));
|
|
4660
|
+
}));
|
|
4661
|
+
}
|
|
4662
|
+
|
|
4663
|
+
function RecentlyVisited({
|
|
4664
|
+
count,
|
|
4665
|
+
hasMore,
|
|
4666
|
+
items,
|
|
4667
|
+
loading,
|
|
4668
|
+
showMore,
|
|
4669
|
+
...navigation
|
|
4670
|
+
}) {
|
|
4671
|
+
const headingId = `${navigation.id}-heading`;
|
|
4672
|
+
if (items.length === 0 && !hasMore && !loading) {
|
|
4673
|
+
return /*#__PURE__*/React.createElement("p", {
|
|
4674
|
+
className: styles$c.message
|
|
4675
|
+
}, "Pages you visit will show up here.");
|
|
3760
4676
|
} else {
|
|
3761
|
-
return null
|
|
4677
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
4678
|
+
className: styles$c.heading,
|
|
4679
|
+
id: headingId
|
|
4680
|
+
}, "Recently visited"), /*#__PURE__*/React.createElement(ResultListbox, {
|
|
4681
|
+
labelledBy: headingId,
|
|
4682
|
+
sections: [{
|
|
4683
|
+
items,
|
|
4684
|
+
key: "recents"
|
|
4685
|
+
}],
|
|
4686
|
+
...navigation
|
|
4687
|
+
}), hasMore && /*#__PURE__*/React.createElement(EndOfResultsObserver, {
|
|
4688
|
+
key: count,
|
|
4689
|
+
onVisible: showMore
|
|
4690
|
+
}));
|
|
3762
4691
|
}
|
|
3763
4692
|
}
|
|
3764
|
-
|
|
3765
|
-
|
|
4693
|
+
|
|
4694
|
+
var styles$b = {"header":"header-sXEyG","bordered":"bordered-mWkCy","input":"input-YQkTC","clearButton":"clearButton-9Yh9w"};
|
|
4695
|
+
|
|
4696
|
+
// A combobox: focus stays here while aria-activedescendant points screen
|
|
4697
|
+
// readers at the highlighted result in the listbox it controls.
|
|
4698
|
+
function SearchInput({
|
|
4699
|
+
bordered = false,
|
|
4700
|
+
expanded,
|
|
4701
|
+
highlightedIndex,
|
|
4702
|
+
inputRef,
|
|
4703
|
+
listboxId,
|
|
4704
|
+
onChange,
|
|
4705
|
+
onFocus,
|
|
4706
|
+
onKeyDown,
|
|
4707
|
+
value
|
|
3766
4708
|
}) {
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
}
|
|
3781
|
-
};
|
|
3782
|
-
window.addEventListener("keydown", handleKeyDown, {
|
|
3783
|
-
capture: true
|
|
3784
|
-
});
|
|
3785
|
-
return () => window.removeEventListener("keydown", handleKeyDown, {
|
|
3786
|
-
capture: true
|
|
3787
|
-
});
|
|
3788
|
-
}, []);
|
|
3789
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("button", {
|
|
3790
|
-
"aria-keyshortcuts": `${isMac() ? "Meta+K" : "Control+K"} /`,
|
|
4709
|
+
return /*#__PURE__*/React.createElement("label", {
|
|
4710
|
+
className: classnames(styles$b.header, {
|
|
4711
|
+
[styles$b.bordered]: bordered
|
|
4712
|
+
})
|
|
4713
|
+
}, /*#__PURE__*/React.createElement(Icon, {
|
|
4714
|
+
name: "general.search",
|
|
4715
|
+
size: 20,
|
|
4716
|
+
theme: "--t-icon-color-dim"
|
|
4717
|
+
}), /*#__PURE__*/React.createElement("input", {
|
|
4718
|
+
"aria-activedescendant": highlightedIndex === null ? undefined : resultId(listboxId, highlightedIndex),
|
|
4719
|
+
"aria-autocomplete": "list",
|
|
4720
|
+
"aria-controls": expanded ? listboxId : undefined,
|
|
4721
|
+
"aria-expanded": expanded,
|
|
3791
4722
|
"aria-label": "Search",
|
|
3792
|
-
|
|
4723
|
+
autoFocus: true,
|
|
4724
|
+
className: styles$b.input,
|
|
4725
|
+
onChange: event => onChange(event.target.value),
|
|
4726
|
+
onFocus: onFocus,
|
|
4727
|
+
onKeyDown: onKeyDown,
|
|
4728
|
+
placeholder: "Search",
|
|
4729
|
+
ref: inputRef,
|
|
4730
|
+
role: "combobox",
|
|
4731
|
+
type: "search",
|
|
4732
|
+
value: value
|
|
4733
|
+
}), value !== "" && (/*#__PURE__*/React.createElement("button", {
|
|
4734
|
+
"aria-label": "Clear search",
|
|
4735
|
+
className: styles$b.clearButton,
|
|
4736
|
+
onClick: () => {
|
|
4737
|
+
var _a;
|
|
4738
|
+
onChange("");
|
|
4739
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
4740
|
+
},
|
|
4741
|
+
tabIndex: -1,
|
|
3793
4742
|
type: "button"
|
|
3794
|
-
}, /*#__PURE__*/React.createElement(
|
|
3795
|
-
|
|
3796
|
-
}))
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
4743
|
+
}, /*#__PURE__*/React.createElement(Icon, {
|
|
4744
|
+
name: "general.smallX"
|
|
4745
|
+
}))));
|
|
4746
|
+
}
|
|
4747
|
+
|
|
4748
|
+
function SearchResults({
|
|
4749
|
+
label,
|
|
4750
|
+
sections,
|
|
4751
|
+
status,
|
|
4752
|
+
term,
|
|
4753
|
+
...navigation
|
|
4754
|
+
}) {
|
|
4755
|
+
switch (status) {
|
|
4756
|
+
case "idle":
|
|
4757
|
+
return null;
|
|
4758
|
+
case "loading":
|
|
4759
|
+
if (sections.length > 0) {
|
|
4760
|
+
return /*#__PURE__*/React.createElement(ResultListbox, {
|
|
4761
|
+
busy: true,
|
|
4762
|
+
label: label,
|
|
4763
|
+
sections: sections,
|
|
4764
|
+
...navigation
|
|
4765
|
+
});
|
|
4766
|
+
}
|
|
4767
|
+
return /*#__PURE__*/React.createElement("p", {
|
|
4768
|
+
className: styles$c.message,
|
|
4769
|
+
role: "status"
|
|
4770
|
+
}, "Searching\u2026");
|
|
4771
|
+
case "error":
|
|
4772
|
+
return /*#__PURE__*/React.createElement("p", {
|
|
4773
|
+
className: styles$c.message,
|
|
4774
|
+
role: "alert"
|
|
4775
|
+
}, "Search isn\u2019t working right now. Try again.");
|
|
4776
|
+
case "done":
|
|
4777
|
+
if (sections.length === 0) {
|
|
4778
|
+
return /*#__PURE__*/React.createElement("p", {
|
|
4779
|
+
className: styles$c.message,
|
|
4780
|
+
role: "status"
|
|
4781
|
+
}, "No results for \u201C", term.trim(), "\u201D");
|
|
4782
|
+
}
|
|
4783
|
+
return /*#__PURE__*/React.createElement(ResultListbox, {
|
|
4784
|
+
busy: false,
|
|
4785
|
+
label: label,
|
|
4786
|
+
sections: sections,
|
|
4787
|
+
...navigation
|
|
4788
|
+
});
|
|
4789
|
+
}
|
|
3804
4790
|
}
|
|
4791
|
+
|
|
4792
|
+
// Keys the recents list for navigation, so the highlight
|
|
4793
|
+
// stays put as more recents load and as their lookups answer.
|
|
4794
|
+
const RECENTS = "recents";
|
|
3805
4795
|
function SearchPanel({
|
|
4796
|
+
mobile = false,
|
|
4797
|
+
env,
|
|
3806
4798
|
products
|
|
3807
4799
|
}) {
|
|
3808
4800
|
var _a, _b;
|
|
@@ -3815,11 +4807,17 @@ true;
|
|
|
3815
4807
|
const hasTerm = term.trim() !== "";
|
|
3816
4808
|
const search = useProductSearch(product.slug, term);
|
|
3817
4809
|
const groups = "groups" in search ? search.groups : undefined;
|
|
3818
|
-
const
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
4810
|
+
const sections = React.useMemo(() => groupSections(groups), [groups]);
|
|
4811
|
+
const {
|
|
4812
|
+
visits,
|
|
4813
|
+
...recents
|
|
4814
|
+
} = useRecentVisits(env);
|
|
4815
|
+
const recentItems = visits.map(visitItem);
|
|
4816
|
+
const items = hasTerm ? sections.flatMap(section => section.items) : recentItems;
|
|
4817
|
+
// Touch-only devices open results by tapping them, with no highlight to
|
|
4818
|
+
// arrow through. A narrow window with a keyboard and mouse keeps both.
|
|
4819
|
+
const touchOnly = useMediaQuery("(hover: none) and (pointer: coarse)");
|
|
4820
|
+
const navigation = useResultNavigation(items, hasTerm ? sections : RECENTS, !touchOnly);
|
|
3823
4821
|
const {
|
|
3824
4822
|
highlightedIndex
|
|
3825
4823
|
} = navigation;
|
|
@@ -3842,296 +4840,273 @@ true;
|
|
|
3842
4840
|
React.useEffect(() => {
|
|
3843
4841
|
if (hasTerm) saveLastProduct(product.slug);
|
|
3844
4842
|
}, [hasTerm, product.slug]);
|
|
3845
|
-
|
|
3846
|
-
"aria-activedescendant": highlightedIndex === null ? undefined : resultId(listboxId, highlightedIndex),
|
|
3847
|
-
"aria-autocomplete": "list",
|
|
3848
|
-
"aria-controls": results.length > 0 ? listboxId : undefined,
|
|
3849
|
-
"aria-expanded": results.length > 0,
|
|
3850
|
-
"aria-label": "Search",
|
|
3851
|
-
autoFocus: true,
|
|
3852
|
-
onChange: event => setTerm(event.target.value),
|
|
3853
|
-
onFocus: () => setAnnounceHighlight(false),
|
|
3854
|
-
onKeyDown: navigation.handleKeyDown,
|
|
3855
|
-
placeholder: "Search",
|
|
3856
|
-
ref: inputRef,
|
|
3857
|
-
role: "combobox",
|
|
3858
|
-
type: "search",
|
|
3859
|
-
value: term
|
|
3860
|
-
}), /*#__PURE__*/React.createElement(ProductTabs, {
|
|
3861
|
-
onKeyDown: handleTabsKeyDown,
|
|
3862
|
-
onSelect: setSelectedSlug,
|
|
3863
|
-
products: products,
|
|
3864
|
-
selected: product
|
|
3865
|
-
}, /*#__PURE__*/React.createElement(SearchResults, {
|
|
4843
|
+
const results = hasTerm ? (/*#__PURE__*/React.createElement(SearchResults, {
|
|
3866
4844
|
highlightedIndex: highlightedIndex,
|
|
3867
4845
|
id: listboxId,
|
|
3868
4846
|
label: `${product.appName} results`,
|
|
3869
4847
|
onHighlight: navigation.highlightIndex,
|
|
3870
|
-
|
|
4848
|
+
sections: sections,
|
|
4849
|
+
status: search.status,
|
|
3871
4850
|
term: term
|
|
3872
|
-
}))
|
|
4851
|
+
})) : (/*#__PURE__*/React.createElement(RecentlyVisited, {
|
|
4852
|
+
highlightedIndex: highlightedIndex,
|
|
4853
|
+
id: listboxId,
|
|
4854
|
+
items: recentItems,
|
|
4855
|
+
onHighlight: navigation.highlightIndex,
|
|
4856
|
+
...recents
|
|
4857
|
+
}));
|
|
4858
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SearchInput, {
|
|
4859
|
+
bordered: mobile,
|
|
4860
|
+
expanded: items.length > 0,
|
|
4861
|
+
highlightedIndex: highlightedIndex,
|
|
4862
|
+
inputRef: inputRef,
|
|
4863
|
+
listboxId: listboxId,
|
|
4864
|
+
onChange: setTerm,
|
|
4865
|
+
onFocus: () => setAnnounceHighlight(false),
|
|
4866
|
+
onKeyDown: navigation.handleKeyDown,
|
|
4867
|
+
value: term
|
|
4868
|
+
}), mobile ? (/*#__PURE__*/React.createElement(ProductSelect, {
|
|
4869
|
+
onSelect: setSelectedSlug,
|
|
4870
|
+
products: products,
|
|
4871
|
+
selected: product
|
|
4872
|
+
}, results)) : (/*#__PURE__*/React.createElement(ProductTabs, {
|
|
4873
|
+
onKeyDown: handleTabsKeyDown,
|
|
4874
|
+
onSelect: setSelectedSlug,
|
|
4875
|
+
products: products,
|
|
4876
|
+
selected: product
|
|
4877
|
+
}, results)), /*#__PURE__*/React.createElement("div", {
|
|
3873
4878
|
"aria-live": "polite",
|
|
3874
|
-
className: styles$
|
|
3875
|
-
}, announceHighlight ? (_b = navigation.highlighted) === null || _b === void 0 ? void 0 : _b.
|
|
4879
|
+
className: styles$i.visuallyHidden
|
|
4880
|
+
}, announceHighlight ? (_b = navigation.highlighted) === null || _b === void 0 ? void 0 : _b.title : null), mobile ? null : /*#__PURE__*/React.createElement(KeyboardHints, {
|
|
3876
4881
|
copied: navigation.copied
|
|
3877
4882
|
}));
|
|
3878
4883
|
}
|
|
3879
|
-
function KeyboardHints({
|
|
3880
|
-
copied
|
|
3881
|
-
}) {
|
|
3882
|
-
const command = commandKeyLabel();
|
|
3883
|
-
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", {
|
|
3884
|
-
role: "status"
|
|
3885
|
-
}, copied ? "Copied!" : "Copy link"), " \u00B7 ", command, "K Open search");
|
|
3886
|
-
}
|
|
3887
|
-
function initialProduct(products) {
|
|
3888
|
-
var _a, _b, _c;
|
|
3889
|
-
const lastProduct = readLastProduct();
|
|
3890
|
-
const bySlug = slug => products.find(product => product.slug === slug);
|
|
3891
|
-
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];
|
|
3892
|
-
}
|
|
3893
4884
|
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
function getAllPcoMeta() {
|
|
3904
|
-
const elements = document.head.querySelectorAll(`meta[property^="pco:"]`);
|
|
3905
|
-
const meta = {};
|
|
3906
|
-
elements.forEach(e => {
|
|
3907
|
-
const property = e.getAttribute("property");
|
|
3908
|
-
meta[property.substring("pco:".length)] = parseValue(e);
|
|
3909
|
-
});
|
|
3910
|
-
return meta;
|
|
4885
|
+
// Products open modals from many libraries, not all of which mark themselves
|
|
4886
|
+
// with roles, so a modal is recognized by what it does: cover the page.
|
|
4887
|
+
function isModalOpen() {
|
|
4888
|
+
const {
|
|
4889
|
+
clientHeight,
|
|
4890
|
+
clientWidth
|
|
4891
|
+
} = document.documentElement;
|
|
4892
|
+
const element = document.elementFromPoint(clientWidth / 2, clientHeight / 2);
|
|
4893
|
+
return element !== null && (isInNativeDialog(element) || hasBackdrop(element, clientWidth, clientHeight));
|
|
3911
4894
|
}
|
|
3912
|
-
//
|
|
3913
|
-
//
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
var _a, _b;
|
|
3917
|
-
return (_b = (_a = document.head.querySelector(`meta[property="pco:recently_visitable"]`)) === null || _a === void 0 ? void 0 : _a.getAttribute("metadata")) !== null && _b !== void 0 ? _b : null;
|
|
4895
|
+
// A native modal's backdrop is drawn by the browser rather than being an
|
|
4896
|
+
// element; hit-testing it lands on the dialog itself.
|
|
4897
|
+
function isInNativeDialog(element) {
|
|
4898
|
+
return element.closest("dialog[open]") !== null;
|
|
3918
4899
|
}
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
function sync() {
|
|
3924
|
-
const nextState = getAllPcoMeta();
|
|
3925
|
-
if (!shallowCompare(store.getState(), nextState)) store.setState(nextState);
|
|
3926
|
-
const metadata = getRecentlyVisitableMetadata();
|
|
3927
|
-
if (recentlyVisitableMetadataStore.getState().metadata !== metadata) {
|
|
3928
|
-
recentlyVisitableMetadataStore.setState({
|
|
3929
|
-
metadata
|
|
3930
|
-
});
|
|
4900
|
+
function hasBackdrop(element, width, height) {
|
|
4901
|
+
let current = element;
|
|
4902
|
+
while (current && !isBackdrop(current, width, height)) {
|
|
4903
|
+
current = current.parentElement;
|
|
3931
4904
|
}
|
|
4905
|
+
return current !== null;
|
|
3932
4906
|
}
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
// Re-read once now, since observe() is only called once a consumer is
|
|
3943
|
-
// actually mounting, which is always after the document has fully parsed.
|
|
3944
|
-
sync();
|
|
3945
|
-
observer.observe(document.head, {
|
|
3946
|
-
attributes: true,
|
|
3947
|
-
childList: true,
|
|
3948
|
-
subtree: true
|
|
3949
|
-
});
|
|
3950
|
-
}
|
|
3951
|
-
function usePcoMeta() {
|
|
3952
|
-
observe();
|
|
3953
|
-
return shim.useSyncExternalStore(store.subscribe, () => store.getState());
|
|
4907
|
+
// Some backdrops start below the topbar rather than at the top of the screen,
|
|
4908
|
+
// so the layer only has to span the width and reach from the center to the
|
|
4909
|
+
// bottom. Its own box has to reach the center: a bar along the bottom can
|
|
4910
|
+
// hold a panel that pops up over the center, like Services' media player.
|
|
4911
|
+
// react-aria leaves a closed modal's backdrop up while it fades out, search's
|
|
4912
|
+
// own included, so one that's on its way out doesn't count.
|
|
4913
|
+
function isBackdrop(element, width, height) {
|
|
4914
|
+
const rect = element.getBoundingClientRect();
|
|
4915
|
+
return !element.matches("[data-exiting]") && getComputedStyle(element).position === "fixed" && rect.width >= width - SCROLLBAR_SLACK && rect.top <= height / 2 && rect.bottom >= height - ROUNDING_SLACK;
|
|
3954
4916
|
}
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
4917
|
+
// react-aria keeps a classic scrollbar's gutter reserved while its modals lock
|
|
4918
|
+
// scrolling, so their backdrops stop short of the right edge by its width.
|
|
4919
|
+
const SCROLLBAR_SLACK = 24;
|
|
4920
|
+
// Rects are fractional at non-100% zoom, but clientWidth and clientHeight are
|
|
4921
|
+
// rounded to whole pixels.
|
|
4922
|
+
const ROUNDING_SLACK = 1;
|
|
4923
|
+
|
|
4924
|
+
function useSearchShortcut(enabled, open, setOpen) {
|
|
4925
|
+
React.useEffect(() => {
|
|
4926
|
+
if (!enabled) return undefined;
|
|
4927
|
+
// Rich text editors like Trix bind ⌘K to "insert link"; capturing at the
|
|
4928
|
+
// window and stopping propagation keeps them from ever seeing it. Under a
|
|
4929
|
+
// product's modal, search stays shut rather than stacking a second modal,
|
|
4930
|
+
// and the keystroke goes on to the product.
|
|
4931
|
+
const handleKeyDown = event => {
|
|
4932
|
+
if (open && isSearchShortcut(event)) {
|
|
4933
|
+
event.preventDefault();
|
|
4934
|
+
event.stopPropagation();
|
|
4935
|
+
setOpen(false);
|
|
4936
|
+
} else if (!open && (isSearchShortcut(event) || isLegacySearchShortcut(event)) && !isModalOpen()) {
|
|
4937
|
+
event.preventDefault();
|
|
4938
|
+
event.stopPropagation();
|
|
4939
|
+
setOpen(true);
|
|
4940
|
+
}
|
|
4941
|
+
};
|
|
4942
|
+
window.addEventListener("keydown", handleKeyDown, {
|
|
4943
|
+
capture: true
|
|
4944
|
+
});
|
|
4945
|
+
return () => window.removeEventListener("keydown", handleKeyDown, {
|
|
4946
|
+
capture: true
|
|
4947
|
+
});
|
|
4948
|
+
}, [enabled, open, setOpen]);
|
|
3958
4949
|
}
|
|
3959
4950
|
|
|
3960
|
-
const
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
}
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
} else if (value.type === "page") {
|
|
3991
|
-
return isNonBlankString(value.product) && isNonBlankString(value.title) && isNonBlankString(value.path) && typeof value.details === "string";
|
|
3992
|
-
} else {
|
|
3993
|
-
return false;
|
|
3994
|
-
}
|
|
3995
|
-
}
|
|
3996
|
-
// Cuts on code points so a surrogate pair is never split.
|
|
3997
|
-
function clampText(value) {
|
|
3998
|
-
return Array.from(value).slice(0, MAX_TEXT_LENGTH).join("");
|
|
4951
|
+
const PERMISSION_CHECKS = {
|
|
4952
|
+
"check-ins": () => fetchMe("check-ins", "permission").then(({
|
|
4953
|
+
permission
|
|
4954
|
+
}) => permission !== null),
|
|
4955
|
+
giving: personId => {
|
|
4956
|
+
if (personId === undefined) {
|
|
4957
|
+
return Promise.reject(new Error("No person to check Giving access for"));
|
|
4958
|
+
} else {
|
|
4959
|
+
return fetchAttributes(`giving/v2/person_permissions/${personId}`, {
|
|
4960
|
+
"fields[PersonPermissions]": "can_search"
|
|
4961
|
+
}).then(({
|
|
4962
|
+
can_search
|
|
4963
|
+
}) => can_search);
|
|
4964
|
+
}
|
|
4965
|
+
},
|
|
4966
|
+
people: () => fetchMe("people", "people_permissions,site_administrator").then(({
|
|
4967
|
+
people_permissions,
|
|
4968
|
+
site_administrator
|
|
4969
|
+
}) => site_administrator || people_permissions !== null),
|
|
4970
|
+
registrations: () => fetchMe("registrations", "can_search").then(({
|
|
4971
|
+
can_search
|
|
4972
|
+
}) => can_search),
|
|
4973
|
+
services: () => fetchMe("services", "can_search").then(({
|
|
4974
|
+
can_search
|
|
4975
|
+
}) => can_search)
|
|
4976
|
+
};
|
|
4977
|
+
function fetchMe(product, fields) {
|
|
4978
|
+
return fetchAttributes(`${product}/v2/me`, {
|
|
4979
|
+
"fields[Person]": fields
|
|
4980
|
+
});
|
|
3999
4981
|
}
|
|
4000
|
-
function
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4982
|
+
function fetchAttributes(path, params) {
|
|
4983
|
+
const url = `${window.location.origin}/~api/${path}?${new URLSearchParams(params)}`;
|
|
4984
|
+
return apiRequest(defaultFetch, url, {
|
|
4985
|
+
headers: apiVersionHeaders(url)
|
|
4986
|
+
}).then(({
|
|
4987
|
+
json,
|
|
4988
|
+
ok
|
|
4989
|
+
}) => {
|
|
4990
|
+
if (!ok || json === null) throw new Error("Permission check failed");
|
|
4991
|
+
return json.data.attributes;
|
|
4992
|
+
});
|
|
4010
4993
|
}
|
|
4011
|
-
//
|
|
4012
|
-
//
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
}
|
|
4020
|
-
|
|
4021
|
-
return parsed.entries.filter(isEntry);
|
|
4994
|
+
// Permissions rarely change mid-session, so a successful check is kept until
|
|
4995
|
+
// the page reloads. A failed one is dropped so a remount tries again.
|
|
4996
|
+
const permissionCache = new Map();
|
|
4997
|
+
function canSearch(slug, personId) {
|
|
4998
|
+
const check = PERMISSION_CHECKS[slug];
|
|
4999
|
+
const cached = permissionCache.get(slug);
|
|
5000
|
+
if (!check) {
|
|
5001
|
+
return Promise.resolve(true);
|
|
5002
|
+
} else if (cached) {
|
|
5003
|
+
return cached;
|
|
4022
5004
|
} else {
|
|
4023
|
-
|
|
5005
|
+
const promise = check(personId).catch(() => {
|
|
5006
|
+
permissionCache.delete(slug);
|
|
5007
|
+
return false;
|
|
5008
|
+
});
|
|
5009
|
+
permissionCache.set(slug, promise);
|
|
5010
|
+
return promise;
|
|
4024
5011
|
}
|
|
4025
5012
|
}
|
|
4026
|
-
function addVisit(entries, visit) {
|
|
4027
|
-
const key = entryKey(visit);
|
|
4028
|
-
return [clampEntry(visit), ...entries.filter(entry => entryKey(entry) !== key)].slice(0, MAX_ENTRIES);
|
|
4029
|
-
}
|
|
4030
|
-
function serializeRecentlyVisited(entries, personId) {
|
|
4031
|
-
return JSON.stringify({
|
|
4032
|
-
entries,
|
|
4033
|
-
person: personId,
|
|
4034
|
-
v: FORMAT_VERSION
|
|
4035
|
-
});
|
|
4036
|
-
}
|
|
4037
5013
|
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
const
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
if (resource && isPcrl(resource)) {
|
|
4063
|
-
return {
|
|
4064
|
-
pcrl: resource,
|
|
4065
|
-
type: "resource"
|
|
5014
|
+
function useSearchableProducts(apps) {
|
|
5015
|
+
// Keyed by name because providers hand over a fresh `apps` array on renders
|
|
5016
|
+
// where nothing changed.
|
|
5017
|
+
const accessibleKey = inSwitcherOrder(apps).map(({
|
|
5018
|
+
slug
|
|
5019
|
+
}) => slug).join(",");
|
|
5020
|
+
const personId = useCurrentPersonId();
|
|
5021
|
+
const [settled, setSettled] = React.useState();
|
|
5022
|
+
React.useEffect(() => {
|
|
5023
|
+
let current = true;
|
|
5024
|
+
const accessible = accessibleKey.split(",").flatMap(slug => SEARCHABLE_PRODUCTS.filter(product => product.slug === slug));
|
|
5025
|
+
Promise.all(accessible.map(({
|
|
5026
|
+
slug
|
|
5027
|
+
}) => canSearch(slug, personId))).then(allowed => {
|
|
5028
|
+
if (current) {
|
|
5029
|
+
setSettled({
|
|
5030
|
+
accessibleKey,
|
|
5031
|
+
personId,
|
|
5032
|
+
products: accessible.filter((_, index) => allowed[index])
|
|
5033
|
+
});
|
|
5034
|
+
}
|
|
5035
|
+
});
|
|
5036
|
+
return () => {
|
|
5037
|
+
current = false;
|
|
4066
5038
|
};
|
|
4067
|
-
}
|
|
5039
|
+
}, [accessibleKey, personId]);
|
|
5040
|
+
if ((settled === null || settled === void 0 ? void 0 : settled.accessibleKey) === accessibleKey && settled.personId === personId) {
|
|
4068
5041
|
return {
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
product,
|
|
4072
|
-
title,
|
|
4073
|
-
type: "page"
|
|
5042
|
+
products: settled.products,
|
|
5043
|
+
status: "done"
|
|
4074
5044
|
};
|
|
4075
5045
|
} else {
|
|
4076
|
-
return
|
|
5046
|
+
return {
|
|
5047
|
+
status: "loading"
|
|
5048
|
+
};
|
|
4077
5049
|
}
|
|
4078
5050
|
}
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
// panel always shows what actually gets stored.
|
|
4082
|
-
function useCurrentVisit() {
|
|
4083
|
-
const {
|
|
4084
|
-
recently_visitable: title,
|
|
4085
|
-
resource
|
|
4086
|
-
} = usePcoMeta();
|
|
4087
|
-
const metadata = useRecentlyVisitableMetadata();
|
|
4088
|
-
return React.useMemo(() => visitFromPage({
|
|
4089
|
-
metadata,
|
|
4090
|
-
resource,
|
|
4091
|
-
title
|
|
4092
|
-
}), [metadata, resource, title]);
|
|
5051
|
+
function inSwitcherOrder(apps) {
|
|
5052
|
+
return groupApps(apps).restProducts.flatMap(app => SEARCHABLE_PRODUCTS.filter(product => product.appName === app.name));
|
|
4093
5053
|
}
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
5054
|
+
|
|
5055
|
+
/**
|
|
5056
|
+
* Writes the height of the visible part of the page to the parent of `ref`'s
|
|
5057
|
+
* element as `--visual-viewport-height`. It shrinks when an on-screen keyboard
|
|
5058
|
+
* opens; `100dvh` doesn't, since mobile Safari draws the keyboard over it.
|
|
5059
|
+
*/
|
|
5060
|
+
function useVisualViewportHeight(ref, enabled) {
|
|
5061
|
+
React.useEffect(() => {
|
|
5062
|
+
var _a;
|
|
5063
|
+
const viewport = window.visualViewport;
|
|
5064
|
+
const parent = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.parentElement;
|
|
5065
|
+
if (!enabled || !viewport || !parent) return undefined;
|
|
5066
|
+
const update = () => parent.style.setProperty("--visual-viewport-height", `${viewport.height}px`);
|
|
5067
|
+
update();
|
|
5068
|
+
viewport.addEventListener("resize", update);
|
|
5069
|
+
return () => {
|
|
5070
|
+
viewport.removeEventListener("resize", update);
|
|
5071
|
+
parent.style.removeProperty("--visual-viewport-height");
|
|
5072
|
+
};
|
|
5073
|
+
}, [enabled, ref]);
|
|
4101
5074
|
}
|
|
4102
5075
|
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
env,
|
|
4108
|
-
personId
|
|
5076
|
+
function ProductSearch({
|
|
5077
|
+
apps,
|
|
5078
|
+
mobile = false,
|
|
5079
|
+
env
|
|
4109
5080
|
}) {
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
}
|
|
5081
|
+
const searchable = useSearchableProducts(apps);
|
|
5082
|
+
const [open, setOpen] = React.useState(false);
|
|
5083
|
+
const modalRef = React.useRef(null);
|
|
5084
|
+
// Sizes Tapestry's modal frame, the dialog's parent, to fit above the keyboard.
|
|
5085
|
+
useVisualViewportHeight(modalRef, mobile && open);
|
|
5086
|
+
const available = searchable.status === "done" && searchable.products.length > 0;
|
|
5087
|
+
useSearchShortcut(available, open, setOpen);
|
|
5088
|
+
if (!available) return null;
|
|
5089
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SearchButton, {
|
|
5090
|
+
mobile: mobile,
|
|
5091
|
+
onClick: () => setOpen(true)
|
|
5092
|
+
}), /*#__PURE__*/React.createElement(tapestry.Modal, {
|
|
5093
|
+
"aria-label": "Search",
|
|
5094
|
+
className: classnames(styles$i.modal, {
|
|
5095
|
+
[styles$i.mobile]: mobile
|
|
5096
|
+
}),
|
|
5097
|
+
dismissible: true,
|
|
5098
|
+
onClose: () => setOpen(false),
|
|
5099
|
+
open: open,
|
|
5100
|
+
ref: modalRef,
|
|
5101
|
+
size: "lg"
|
|
5102
|
+
}, mobile && /*#__PURE__*/React.createElement(tapestry.ModalHeader, {
|
|
5103
|
+
className: styles$i.header,
|
|
5104
|
+
title: "Search"
|
|
5105
|
+
}), /*#__PURE__*/React.createElement(SearchPanel, {
|
|
5106
|
+
mobile: mobile,
|
|
5107
|
+
env: env,
|
|
5108
|
+
products: searchable.products
|
|
5109
|
+
})));
|
|
4135
5110
|
}
|
|
4136
5111
|
|
|
4137
5112
|
function useRecordRecentlyVisited({
|
|
@@ -4176,9 +5151,9 @@ true;
|
|
|
4176
5151
|
`), children);
|
|
4177
5152
|
}
|
|
4178
5153
|
|
|
4179
|
-
var styles$
|
|
5154
|
+
var styles$a = {"route":"route--8BWe","is-active":"is-active-FsU9Y","apps-routes-separator":"apps-routes-separator-Nlm-N"};
|
|
4180
5155
|
|
|
4181
|
-
var styles$
|
|
5156
|
+
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"};
|
|
4182
5157
|
|
|
4183
5158
|
const UserMenu = ({
|
|
4184
5159
|
appName,
|
|
@@ -4197,7 +5172,7 @@ true;
|
|
|
4197
5172
|
component: Outsider,
|
|
4198
5173
|
onOutsideClick: () => setUserMenuVisible(false),
|
|
4199
5174
|
cleanup: !userMenuVisible,
|
|
4200
|
-
className: styles$
|
|
5175
|
+
className: styles$9.popup,
|
|
4201
5176
|
isVisible: userMenuVisible
|
|
4202
5177
|
}, /*#__PURE__*/React.createElement("div", null, hasConnectedPeople ? (/*#__PURE__*/React.createElement("div", {
|
|
4203
5178
|
style: {
|
|
@@ -4341,11 +5316,11 @@ true;
|
|
|
4341
5316
|
userMenuVisible
|
|
4342
5317
|
}) => {
|
|
4343
5318
|
return /*#__PURE__*/React.createElement("div", {
|
|
4344
|
-
className: styles$
|
|
5319
|
+
className: styles$9["trigger-wrap"],
|
|
4345
5320
|
"data-pendo": "topbar-org-switcher"
|
|
4346
5321
|
}, /*#__PURE__*/React.createElement(Hoverable, {
|
|
4347
|
-
className: classnames(styles$
|
|
4348
|
-
[styles$
|
|
5322
|
+
className: classnames(styles$9["trigger"], {
|
|
5323
|
+
[styles$9["is-visible"]]: userMenuVisible
|
|
4349
5324
|
}),
|
|
4350
5325
|
component: "button",
|
|
4351
5326
|
type: "button",
|
|
@@ -4357,7 +5332,7 @@ true;
|
|
|
4357
5332
|
requestConnectedPeopleFetch();
|
|
4358
5333
|
}
|
|
4359
5334
|
}, showOrgName && (/*#__PURE__*/React.createElement("span", {
|
|
4360
|
-
className: styles$
|
|
5335
|
+
className: styles$9["trigger-orgname"]
|
|
4361
5336
|
}, orgName)), /*#__PURE__*/React.createElement(DisclosureChevronIcon, null)));
|
|
4362
5337
|
};
|
|
4363
5338
|
|
|
@@ -4417,13 +5392,14 @@ true;
|
|
|
4417
5392
|
colors: props.colors,
|
|
4418
5393
|
requestAppsFetch: props.requestAppsFetch
|
|
4419
5394
|
})), /*#__PURE__*/React.createElement("div", {
|
|
4420
|
-
className: styles$
|
|
5395
|
+
className: styles$a["apps-routes-separator"]
|
|
4421
5396
|
}), routesVisible && props.routes, /*#__PURE__*/React.createElement("div", {
|
|
4422
5397
|
style: {
|
|
4423
5398
|
margin: "auto"
|
|
4424
5399
|
}
|
|
4425
5400
|
}), !notificationsEnabled && notifications(), productSearchEnabled ? (/*#__PURE__*/React.createElement(ProductSearch, {
|
|
4426
|
-
apps: props.apps
|
|
5401
|
+
apps: props.apps,
|
|
5402
|
+
env: props.env
|
|
4427
5403
|
})) : search({
|
|
4428
5404
|
hideRoutes: () => setRoutesVisible(false),
|
|
4429
5405
|
showRoutes: () => setRoutesVisible(true)
|
|
@@ -4457,12 +5433,12 @@ true;
|
|
|
4457
5433
|
...nativeProps
|
|
4458
5434
|
}) => {
|
|
4459
5435
|
return /*#__PURE__*/React.createElement("a", {
|
|
4460
|
-
className: classnames(styles$
|
|
5436
|
+
className: classnames(styles$a["route"], className, active && styles$a["is-active"]),
|
|
4461
5437
|
...nativeProps
|
|
4462
5438
|
});
|
|
4463
5439
|
};
|
|
4464
5440
|
|
|
4465
|
-
var styles$
|
|
5441
|
+
var styles$8 = {"avatar":"avatar-tW-wn"};
|
|
4466
5442
|
|
|
4467
5443
|
function Avatar({
|
|
4468
5444
|
env,
|
|
@@ -4472,7 +5448,7 @@ true;
|
|
|
4472
5448
|
}) {
|
|
4473
5449
|
const url = incomingUrl || `${pcoUrl$1(env)("people")}/static/no_photo_thumbnail_gray.svg`;
|
|
4474
5450
|
return /*#__PURE__*/React.createElement("div", {
|
|
4475
|
-
className: styles$
|
|
5451
|
+
className: styles$8.avatar,
|
|
4476
5452
|
style: {
|
|
4477
5453
|
backgroundImage: `url(${url})`,
|
|
4478
5454
|
...style
|
|
@@ -32231,7 +33207,7 @@ true;
|
|
|
32231
33207
|
|
|
32232
33208
|
var reactDomExports = requireReactDom();
|
|
32233
33209
|
|
|
32234
|
-
var styles$
|
|
33210
|
+
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"};
|
|
32235
33211
|
|
|
32236
33212
|
const AppsSwitcher = ({
|
|
32237
33213
|
appName,
|
|
@@ -32259,8 +33235,8 @@ true;
|
|
|
32259
33235
|
}, /*#__PURE__*/React.createElement("a", {
|
|
32260
33236
|
"aria-current": name === appName ? "page" : undefined,
|
|
32261
33237
|
"aria-label": name,
|
|
32262
|
-
className: classnames(styles$
|
|
32263
|
-
[styles$
|
|
33238
|
+
className: classnames(styles$7.link, {
|
|
33239
|
+
[styles$7.current]: name === appName
|
|
32264
33240
|
}),
|
|
32265
33241
|
"data-pendo": `app-switcher--change-app--${name}`,
|
|
32266
33242
|
"data-turbolinks": false,
|
|
@@ -32272,48 +33248,48 @@ true;
|
|
|
32272
33248
|
"aria-expanded": visible,
|
|
32273
33249
|
"aria-haspopup": "true",
|
|
32274
33250
|
"aria-label": `App switcher: ${appName}`,
|
|
32275
|
-
className: styles$
|
|
33251
|
+
className: styles$7.trigger,
|
|
32276
33252
|
onClick: onTriggerClick,
|
|
32277
33253
|
type: "button"
|
|
32278
33254
|
}, /*#__PURE__*/React.createElement(ColorAppIcon, {
|
|
32279
33255
|
appName: appName.replace(/[\s-]/, "")
|
|
32280
33256
|
})), /*#__PURE__*/reactDomExports.createPortal(/*#__PURE__*/React.createElement("div", {
|
|
32281
33257
|
ref: containerRef,
|
|
32282
|
-
className: classnames(styles$
|
|
32283
|
-
[styles$
|
|
33258
|
+
className: classnames(styles$7.layer, {
|
|
33259
|
+
[styles$7["layer--visible"]]: visible
|
|
32284
33260
|
}),
|
|
32285
33261
|
onClick: toggle,
|
|
32286
33262
|
onKeyDown: handleKeyDown
|
|
32287
33263
|
}, /*#__PURE__*/React.createElement("nav", {
|
|
32288
33264
|
"aria-label": "Switch app",
|
|
32289
33265
|
"aria-modal": visible,
|
|
32290
|
-
className: styles$
|
|
33266
|
+
className: styles$7.panel,
|
|
32291
33267
|
"data-pendo": "app-switcher",
|
|
32292
33268
|
onClick: event => event.stopPropagation(),
|
|
32293
33269
|
role: "dialog"
|
|
32294
33270
|
}, /*#__PURE__*/React.createElement("div", {
|
|
32295
|
-
className: styles$
|
|
33271
|
+
className: styles$7.header
|
|
32296
33272
|
}, /*#__PURE__*/React.createElement("span", {
|
|
32297
|
-
className: styles$
|
|
33273
|
+
className: styles$7.heading
|
|
32298
33274
|
}, "Switch to"), /*#__PURE__*/React.createElement("button", {
|
|
32299
33275
|
"aria-label": "Close",
|
|
32300
|
-
className: styles$
|
|
33276
|
+
className: styles$7.close,
|
|
32301
33277
|
onClick: toggle,
|
|
32302
33278
|
type: "button"
|
|
32303
33279
|
}, /*#__PURE__*/React.createElement(X, {
|
|
32304
33280
|
"aria-hidden": "true",
|
|
32305
33281
|
fill: "var(--t-icon-color-dim)"
|
|
32306
33282
|
}))), /*#__PURE__*/React.createElement("ul", {
|
|
32307
|
-
className: styles$
|
|
33283
|
+
className: styles$7.list,
|
|
32308
33284
|
role: "list"
|
|
32309
33285
|
}, homeProduct && productLink(homeProduct.name), restProducts.map(({
|
|
32310
33286
|
name
|
|
32311
33287
|
}) => productLink(name)), accountsProduct && (/*#__PURE__*/React.createElement("li", {
|
|
32312
|
-
className: styles$
|
|
33288
|
+
className: styles$7["accounts-item"]
|
|
32313
33289
|
}, /*#__PURE__*/React.createElement("a", {
|
|
32314
33290
|
"aria-current": appName === "Accounts" ? "page" : undefined,
|
|
32315
33291
|
"aria-label": "Account settings",
|
|
32316
|
-
className: classnames(styles$
|
|
33292
|
+
className: classnames(styles$7.link, styles$7["accounts-link"]),
|
|
32317
33293
|
"data-pendo": "app-switcher--change-app--Accounts",
|
|
32318
33294
|
"data-turbolinks": false,
|
|
32319
33295
|
href: pcoUrl$1(env)("accounts") + getAppsSwitchProductPath(accountsProduct.name)
|
|
@@ -32336,14 +33312,14 @@ true;
|
|
|
32336
33312
|
method: "localStorage"
|
|
32337
33313
|
};
|
|
32338
33314
|
|
|
32339
|
-
var styles$
|
|
33315
|
+
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","actions":"actions-INQ7-","profile-button":"profile-button-a2WO2"};
|
|
32340
33316
|
|
|
32341
33317
|
function AccountSettingsLink({
|
|
32342
33318
|
env
|
|
32343
33319
|
}) {
|
|
32344
33320
|
const accountsName = "accounts";
|
|
32345
33321
|
return /*#__PURE__*/React.createElement("a", {
|
|
32346
|
-
className: classnames(styles$
|
|
33322
|
+
className: classnames(styles$6["appmenu-list-a"], styles$6["appmenu-list-a--bottom"]),
|
|
32347
33323
|
"data-pendo": `app-switcher--change-app--${accountsName}`,
|
|
32348
33324
|
"data-turbolinks": false,
|
|
32349
33325
|
href: pcoUrl$1(env)(accountsName) + getAppsSwitchProductPath(accountsName)
|
|
@@ -32366,7 +33342,7 @@ true;
|
|
|
32366
33342
|
}) => (/*#__PURE__*/React.createElement("button", {
|
|
32367
33343
|
"aria-label": "Close menu",
|
|
32368
33344
|
type: "button",
|
|
32369
|
-
className: styles$
|
|
33345
|
+
className: styles$6["appmenu-close-button"],
|
|
32370
33346
|
style: style
|
|
32371
33347
|
}, /*#__PURE__*/React.createElement(X, {
|
|
32372
33348
|
fill: color,
|
|
@@ -32427,7 +33403,7 @@ true;
|
|
|
32427
33403
|
"aria-haspopup": "true",
|
|
32428
33404
|
"aria-label": `App switcher: ${appName}`,
|
|
32429
33405
|
type: "button",
|
|
32430
|
-
className: styles$
|
|
33406
|
+
className: styles$6["appmenu-button"],
|
|
32431
33407
|
onClick: onTriggerClick
|
|
32432
33408
|
}, /*#__PURE__*/React.createElement(ColorAppIcon, {
|
|
32433
33409
|
appName: appName.replace(/[\s-]/, "")
|
|
@@ -32435,7 +33411,7 @@ true;
|
|
|
32435
33411
|
item: "Topbar:Small:Menus:Products:visibility",
|
|
32436
33412
|
render: () => (/*#__PURE__*/React.createElement("div", {
|
|
32437
33413
|
ref: appMenuRef,
|
|
32438
|
-
className: styles$
|
|
33414
|
+
className: styles$6["appmenu-layer"],
|
|
32439
33415
|
style: {
|
|
32440
33416
|
...(visible && {
|
|
32441
33417
|
display: "block"
|
|
@@ -32452,37 +33428,37 @@ true;
|
|
|
32452
33428
|
e.stopPropagation();
|
|
32453
33429
|
}
|
|
32454
33430
|
},
|
|
32455
|
-
className: styles$
|
|
33431
|
+
className: styles$6["appmenu-container"]
|
|
32456
33432
|
}, /*#__PURE__*/React.createElement("a", {
|
|
32457
33433
|
"aria-current": appName === (homeProduct === null || homeProduct === void 0 ? void 0 : homeProduct.name) ? "page" : undefined,
|
|
32458
33434
|
"aria-label": homeProduct ? homeProduct.name : appName,
|
|
32459
33435
|
"data-pendo": `app-switcher--change-app--${homeProduct === null || homeProduct === void 0 ? void 0 : homeProduct.name}`,
|
|
32460
33436
|
"data-turbolinks": false,
|
|
32461
|
-
className: classnames(styles$
|
|
33437
|
+
className: classnames(styles$6["appmenu-list-a"], styles$6["appmenu-list-a--top"]),
|
|
32462
33438
|
href: homeProduct ? pcoUrl$1(env)("accounts") + getAppsSwitchProductPath(homeProduct === null || homeProduct === void 0 ? void 0 : homeProduct.name) : undefined
|
|
32463
33439
|
}, /*#__PURE__*/React.createElement(UserMenuAppLockup, {
|
|
32464
33440
|
appName: homeProduct ? homeProduct.name : appName
|
|
32465
33441
|
})), /*#__PURE__*/React.createElement("div", {
|
|
32466
|
-
className: styles$
|
|
33442
|
+
className: styles$6["appmenu-scroll"]
|
|
32467
33443
|
}, /*#__PURE__*/React.createElement("ul", {
|
|
32468
33444
|
"data-pendo": "app-switcher",
|
|
32469
|
-
className: styles$
|
|
33445
|
+
className: styles$6["appmenu-list"]
|
|
32470
33446
|
}, appsMenuFormatter(restProducts).map(({
|
|
32471
33447
|
name
|
|
32472
33448
|
}) => (/*#__PURE__*/React.createElement("li", {
|
|
32473
33449
|
key: name,
|
|
32474
|
-
className: styles$
|
|
33450
|
+
className: styles$6["appmenu-list-li"]
|
|
32475
33451
|
}, /*#__PURE__*/React.createElement("a", {
|
|
32476
33452
|
"aria-current": name === appName ? "page" : undefined,
|
|
32477
33453
|
"aria-label": name,
|
|
32478
33454
|
"data-pendo": `app-switcher--change-app--${name}`,
|
|
32479
33455
|
"data-turbolinks": false,
|
|
32480
|
-
className: styles$
|
|
33456
|
+
className: styles$6["appmenu-list-a"],
|
|
32481
33457
|
href: pcoUrl$1(env)("accounts") + getAppsSwitchProductPath(name)
|
|
32482
33458
|
}, /*#__PURE__*/React.createElement(UserMenuAppLockup, {
|
|
32483
33459
|
appName: name
|
|
32484
33460
|
}))))), accountsProduct && (/*#__PURE__*/React.createElement("li", {
|
|
32485
|
-
className: styles$
|
|
33461
|
+
className: styles$6["appmenu-list-li"]
|
|
32486
33462
|
}, /*#__PURE__*/React.createElement(AccountSettingsLink, {
|
|
32487
33463
|
env: env
|
|
32488
33464
|
}))))))))
|
|
@@ -32546,19 +33522,25 @@ true;
|
|
|
32546
33522
|
toggle: toggleProductSwitcher,
|
|
32547
33523
|
visible: productSwitcherVisible
|
|
32548
33524
|
})), routesMenuVisible && (/*#__PURE__*/React.createElement("div", {
|
|
32549
|
-
className: styles$
|
|
33525
|
+
className: styles$6["routes"]
|
|
32550
33526
|
}, props.routes)), /*#__PURE__*/React.createElement("button", {
|
|
32551
33527
|
type: "button",
|
|
32552
|
-
className: classnames(styles$
|
|
32553
|
-
[styles$
|
|
33528
|
+
className: classnames(styles$6["current-route"], {
|
|
33529
|
+
[styles$6["is-active"]]: routesMenuVisible
|
|
32554
33530
|
}),
|
|
32555
33531
|
onClick: props.routes.length > 0 ? () => {
|
|
32556
33532
|
setProductSwitcherVisible(false);
|
|
32557
33533
|
setRoutesMenuVisible(visible => !visible);
|
|
32558
33534
|
} : null
|
|
32559
|
-
}, /*#__PURE__*/React.createElement(CurrentRouteComponent, null, capitalize(currentRouteText)), props.routes.length > 0 && /*#__PURE__*/React.createElement(DisclosureChevronIcon, null)), /*#__PURE__*/React.createElement("
|
|
33535
|
+
}, /*#__PURE__*/React.createElement(CurrentRouteComponent, null, capitalize$1(currentRouteText)), props.routes.length > 0 && /*#__PURE__*/React.createElement(DisclosureChevronIcon, null)), /*#__PURE__*/React.createElement("div", {
|
|
33536
|
+
className: styles$6["actions"]
|
|
33537
|
+
}, productSearchEnabled && (/*#__PURE__*/React.createElement(ProductSearch, {
|
|
33538
|
+
apps: props.apps,
|
|
33539
|
+
mobile: true,
|
|
33540
|
+
env: props.env
|
|
33541
|
+
})), /*#__PURE__*/React.createElement("button", {
|
|
32560
33542
|
type: "button",
|
|
32561
|
-
className: styles$
|
|
33543
|
+
className: styles$6["profile-button"],
|
|
32562
33544
|
onClick: () => {
|
|
32563
33545
|
var _a, _b;
|
|
32564
33546
|
props.requestConnectedPeopleFetch();
|
|
@@ -32567,14 +33549,14 @@ true;
|
|
|
32567
33549
|
}, /*#__PURE__*/React.createElement(Avatar, {
|
|
32568
33550
|
env: props.env,
|
|
32569
33551
|
url: props.userAvatarPath
|
|
32570
|
-
})));
|
|
33552
|
+
}))));
|
|
32571
33553
|
};
|
|
32572
33554
|
const SmallRoute = ({
|
|
32573
33555
|
active,
|
|
32574
33556
|
className,
|
|
32575
33557
|
...props
|
|
32576
33558
|
}) => (/*#__PURE__*/React.createElement("a", {
|
|
32577
|
-
className: classnames(styles$
|
|
33559
|
+
className: classnames(styles$6["route"], className, active && styles$6["is-active"]),
|
|
32578
33560
|
...props
|
|
32579
33561
|
}));
|
|
32580
33562
|
|
|
@@ -32713,7 +33695,7 @@ true;
|
|
|
32713
33695
|
id: "topbar"
|
|
32714
33696
|
};
|
|
32715
33697
|
|
|
32716
|
-
var styles$
|
|
33698
|
+
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"};
|
|
32717
33699
|
|
|
32718
33700
|
function isPresent(value) {
|
|
32719
33701
|
return value !== undefined && value.trim() !== "";
|
|
@@ -32742,15 +33724,31 @@ true;
|
|
|
32742
33724
|
value
|
|
32743
33725
|
}) {
|
|
32744
33726
|
return /*#__PURE__*/React.createElement("div", {
|
|
32745
|
-
className: styles$
|
|
33727
|
+
className: styles$5["debug-pco-resource-field"]
|
|
32746
33728
|
}, /*#__PURE__*/React.createElement("span", {
|
|
32747
|
-
className: styles$
|
|
33729
|
+
className: styles$5["debug-pco-resource-field-label"]
|
|
32748
33730
|
}, label), /*#__PURE__*/React.createElement("span", {
|
|
32749
|
-
className: styles$
|
|
33731
|
+
className: styles$5["debug-pco-resource-field-value"]
|
|
32750
33732
|
}, value));
|
|
32751
33733
|
}
|
|
33734
|
+
// Only staff ghosting or impersonating a person get pco:staff-access-level,
|
|
33735
|
+
// which keeps the panel away from regular users on staging.
|
|
32752
33736
|
function DebugPcoResource({
|
|
32753
33737
|
env
|
|
33738
|
+
}) {
|
|
33739
|
+
const {
|
|
33740
|
+
"staff-access-level": staffAccessLevel
|
|
33741
|
+
} = usePcoMeta();
|
|
33742
|
+
if (isPresent(staffAccessLevel)) {
|
|
33743
|
+
return /*#__PURE__*/React.createElement(DebugPanel, {
|
|
33744
|
+
env: env
|
|
33745
|
+
});
|
|
33746
|
+
} else {
|
|
33747
|
+
return null;
|
|
33748
|
+
}
|
|
33749
|
+
}
|
|
33750
|
+
function DebugPanel({
|
|
33751
|
+
env
|
|
32754
33752
|
}) {
|
|
32755
33753
|
const {
|
|
32756
33754
|
recently_visitable: recentlyVisitable,
|
|
@@ -32759,12 +33757,17 @@ true;
|
|
|
32759
33757
|
const [lookup, setLookup] = React.useState({
|
|
32760
33758
|
status: "idle"
|
|
32761
33759
|
});
|
|
32762
|
-
const
|
|
33760
|
+
const visit = useCurrentVisit();
|
|
33761
|
+
const personId = useCurrentPersonId();
|
|
33762
|
+
const recentlyVisited = useRecentlyVisited({
|
|
33763
|
+
env,
|
|
33764
|
+
personId
|
|
33765
|
+
});
|
|
33766
|
+
const pcrl = (visit === null || visit === void 0 ? void 0 : visit.type) === "resource" ? visit.pcrl : null;
|
|
32763
33767
|
const hasRecentlyVisitable = isPresent(recentlyVisitable);
|
|
33768
|
+
const hasInvalidResource = isPresent(resource) && !isPcrl(resource);
|
|
32764
33769
|
React.useEffect(() => {
|
|
32765
|
-
|
|
32766
|
-
// tags are present.
|
|
32767
|
-
const url = hasResource ? pcrlLookupUrl(resource, env) : null;
|
|
33770
|
+
const url = pcrl ? pcrlLookupUrl(pcrl, env) : null;
|
|
32768
33771
|
if (!url) {
|
|
32769
33772
|
setLookup({
|
|
32770
33773
|
status: "idle"
|
|
@@ -32796,37 +33799,45 @@ true;
|
|
|
32796
33799
|
return () => {
|
|
32797
33800
|
cancelled = true;
|
|
32798
33801
|
};
|
|
32799
|
-
}, [
|
|
33802
|
+
}, [pcrl, env]);
|
|
32800
33803
|
return /*#__PURE__*/React.createElement("div", {
|
|
32801
|
-
className: classnames("staff-only", styles$
|
|
33804
|
+
className: classnames("staff-only", styles$5["debug-pco-resource"])
|
|
32802
33805
|
}, /*#__PURE__*/React.createElement("div", {
|
|
32803
|
-
className: styles$
|
|
33806
|
+
className: styles$5["debug-pco-resource-header"]
|
|
32804
33807
|
}, /*#__PURE__*/React.createElement("span", {
|
|
32805
|
-
className: styles$
|
|
33808
|
+
className: styles$5["debug-pco-resource-badge"]
|
|
32806
33809
|
}, "Staff"), /*#__PURE__*/React.createElement("span", {
|
|
32807
|
-
className: styles$
|
|
33810
|
+
className: styles$5["debug-pco-resource-title"]
|
|
32808
33811
|
}, "Recently Visited debug")), /*#__PURE__*/React.createElement(Field, {
|
|
32809
33812
|
label: "pco:resource",
|
|
32810
33813
|
value: describeTag("resource", resource)
|
|
32811
33814
|
}), /*#__PURE__*/React.createElement(Field, {
|
|
32812
33815
|
label: "pco:recently_visitable",
|
|
32813
33816
|
value: describeTag("recently_visitable", recentlyVisitable)
|
|
32814
|
-
}),
|
|
32815
|
-
className: styles$
|
|
32816
|
-
}, "
|
|
32817
|
-
className: styles$
|
|
32818
|
-
}, "
|
|
32819
|
-
className: styles$
|
|
32820
|
-
}, "
|
|
32821
|
-
className: styles$
|
|
32822
|
-
},
|
|
32823
|
-
className: styles$
|
|
32824
|
-
},
|
|
32825
|
-
className: styles$
|
|
32826
|
-
},
|
|
32827
|
-
|
|
32828
|
-
|
|
32829
|
-
|
|
33817
|
+
}), hasInvalidResource && (/*#__PURE__*/React.createElement("p", {
|
|
33818
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33819
|
+
}, "pco:resource isn't a PCRL (product/v2/type/id), so Recently Visited ignores it.")), visit === null && (/*#__PURE__*/React.createElement("p", {
|
|
33820
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33821
|
+
}, "This page is not eligible for Recently Visited")), pcrl && hasRecentlyVisitable && (/*#__PURE__*/React.createElement("p", {
|
|
33822
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33823
|
+
}, "Both tags are present \u2014 pco:resource takes precedence for the Recently Visited entry.")), pcrl && lookup.status === "loading" && (/*#__PURE__*/React.createElement("p", {
|
|
33824
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33825
|
+
}, "Looking up resource\u2026")), pcrl && lookup.status === "error" && (/*#__PURE__*/React.createElement("p", {
|
|
33826
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33827
|
+
}, lookup.message)), pcrl && lookup.status === "loaded" && (/*#__PURE__*/React.createElement("pre", {
|
|
33828
|
+
className: styles$5["debug-pco-resource-json"]
|
|
33829
|
+
}, JSON.stringify(lookup.json, null, 2))), (visit === null || visit === void 0 ? void 0 : visit.type) === "page" && (/*#__PURE__*/React.createElement("p", {
|
|
33830
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33831
|
+
}, "Recently Visited title: ", visit.title)), /*#__PURE__*/React.createElement("span", {
|
|
33832
|
+
className: styles$5["debug-pco-resource-field-label"]
|
|
33833
|
+
}, "Recently Visited"), personId ? (/*#__PURE__*/React.createElement("pre", {
|
|
33834
|
+
className: styles$5["debug-pco-resource-json"]
|
|
33835
|
+
}, JSON.stringify(recentlyVisited, null, 2))) : (/*#__PURE__*/React.createElement("p", {
|
|
33836
|
+
className: styles$5["debug-pco-resource-note"]
|
|
33837
|
+
}, "No current person, so nothing is recorded.")));
|
|
33838
|
+
}
|
|
33839
|
+
|
|
33840
|
+
var styles$4 = {"drawer-wrap":"drawer-wrap-J0rSL","is-visible":"is-visible-KTME8","drawer":"drawer-j7f4l"};
|
|
32830
33841
|
|
|
32831
33842
|
function Drawer({
|
|
32832
33843
|
children,
|
|
@@ -32834,7 +33845,7 @@ true;
|
|
|
32834
33845
|
}) {
|
|
32835
33846
|
return /*#__PURE__*/React.createElement("div", {
|
|
32836
33847
|
id: id,
|
|
32837
|
-
className: styles$
|
|
33848
|
+
className: styles$4["drawer"]
|
|
32838
33849
|
}, children);
|
|
32839
33850
|
}
|
|
32840
33851
|
function DrawerWrap({
|
|
@@ -32844,7 +33855,7 @@ true;
|
|
|
32844
33855
|
...props
|
|
32845
33856
|
}) {
|
|
32846
33857
|
return /*#__PURE__*/React.createElement("div", {
|
|
32847
|
-
className: classnames(styles$
|
|
33858
|
+
className: classnames(styles$4["drawer-wrap"], className, isVisible && styles$4["is-visible"]),
|
|
32848
33859
|
...props
|
|
32849
33860
|
}, children);
|
|
32850
33861
|
}
|
|
@@ -33028,15 +34039,18 @@ true;
|
|
|
33028
34039
|
}));
|
|
33029
34040
|
};
|
|
33030
34041
|
|
|
33031
|
-
var styles$
|
|
34042
|
+
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"};
|
|
33032
34043
|
|
|
33033
34044
|
const COLOR_MODES = [{
|
|
34045
|
+
icon: "toolbar.sun",
|
|
33034
34046
|
label: "Light",
|
|
33035
34047
|
value: "light"
|
|
33036
34048
|
}, {
|
|
34049
|
+
icon: "toolbar.moon",
|
|
33037
34050
|
label: "Dark",
|
|
33038
34051
|
value: "dark"
|
|
33039
34052
|
}, {
|
|
34053
|
+
icon: "toolbar.display",
|
|
33040
34054
|
label: "System",
|
|
33041
34055
|
value: "system"
|
|
33042
34056
|
}];
|
|
@@ -33048,14 +34062,15 @@ true;
|
|
|
33048
34062
|
const updateStore = useUpdateStore();
|
|
33049
34063
|
const [counter] = React.useState(() => COLOR_MODE_INSTANCE_COUNTER++);
|
|
33050
34064
|
return /*#__PURE__*/React.createElement("fieldset", {
|
|
33051
|
-
className: styles$
|
|
34065
|
+
className: styles$3["color-mode-switcher"]
|
|
33052
34066
|
}, /*#__PURE__*/React.createElement("legend", {
|
|
33053
|
-
className: styles$
|
|
34067
|
+
className: styles$3["color-mode-switcher-label"]
|
|
33054
34068
|
}, "Appearance", " "), /*#__PURE__*/React.createElement("div", {
|
|
33055
|
-
className: styles$
|
|
34069
|
+
className: styles$3["color-mode-switcher-modes"]
|
|
33056
34070
|
}, COLOR_MODES.map(({
|
|
33057
|
-
|
|
33058
|
-
label
|
|
34071
|
+
icon,
|
|
34072
|
+
label,
|
|
34073
|
+
value
|
|
33059
34074
|
}) => (/*#__PURE__*/React.createElement("label", {
|
|
33060
34075
|
key: value
|
|
33061
34076
|
}, /*#__PURE__*/React.createElement("input", {
|
|
@@ -33072,8 +34087,9 @@ true;
|
|
|
33072
34087
|
document.documentElement.setAttribute(RESOLVED_COLOR_MODE_ATTRIBUTE, resolveColorMode(colorMode));
|
|
33073
34088
|
updateColorMode(colorMode);
|
|
33074
34089
|
}
|
|
33075
|
-
}), /*#__PURE__*/React.createElement(
|
|
33076
|
-
|
|
34090
|
+
}), /*#__PURE__*/React.createElement(Icon, {
|
|
34091
|
+
name: icon,
|
|
34092
|
+
size: 15
|
|
33077
34093
|
}), label)))));
|
|
33078
34094
|
}
|
|
33079
34095
|
function updateColorMode(mode) {
|
|
@@ -33088,28 +34104,6 @@ true;
|
|
|
33088
34104
|
method: "POST"
|
|
33089
34105
|
});
|
|
33090
34106
|
}
|
|
33091
|
-
const COLOR_MODE_ICON_PATHS = {
|
|
33092
|
-
dark: "M12.66667,9.07579 C11.90047,9.42229 11.04997,9.61514 10.15457,9.61514 C6.78392,9.61514 4.05151,6.88275 4.05151,3.51215 C4.05151,2.61663 4.24438,1.76615 4.59086,1 C2.47336,1.95762 1,4.08858 1,6.56366 C1,9.93423 3.73241,12.66663 7.103,12.66663 C9.57807,12.66663 11.70907,11.19333 12.66667,9.07579 Z",
|
|
33093
|
-
light: "M6.83333,1 L6.83333,2.16667 M6.83333,11.50003 L6.83333,12.66663 M2.16667,6.83334 L1,6.83334 M3.51657,3.51657 L2.69161,2.69161 M10.1501,3.51657 L10.9751,2.69161 M3.51657,10.15253 L2.69161,10.97743 M10.1501,10.15253 L10.9751,10.97743 M12.6667,6.83334 L11.5,6.83334 M9.75,6.83334 C9.75,8.44415 8.44415,9.75 6.83333,9.75 C5.2225,9.75 3.91667,8.44415 3.91667,6.83334 C3.91667,5.22251 5.2225,3.91667 6.83333,3.91667 C8.44415,3.91667 9.75,5.22251 9.75,6.83334 Z",
|
|
33094
|
-
system: "M4.75,12.50003 L9.41667,12.50003 M7.08334,10.16667 L7.08334,12.50003 M4.05,10.16667 L10.11665,10.16667 C11.09675,10.16667 11.58675,10.16667 11.96115,9.97592 C12.29045,9.80815 12.55815,9.54046 12.72595,9.21117 C12.91665,8.83678 12.91665,8.34678 12.91665,7.36667 L12.91665,4.8 C12.91665,3.81991 12.91665,3.32986 12.72595,2.95552 C12.55815,2.62623 12.29045,2.35852 11.96115,2.19074 C11.58675,2 11.09675,2 10.11665,2 L4.05,2 C3.06991,2 2.57986,2 2.20552,2.19074 C1.87623,2.35852 1.60852,2.62623 1.44074,2.95552 C1.25,3.32986 1.25,3.81991 1.25,4.8 L1.25,7.36667 C1.25,8.34678 1.25,8.83678 1.44074,9.21117 C1.60852,9.54046 1.87623,9.80815 2.20552,9.97592 C2.57986,10.16667 3.06991,10.16667 4.05,10.16667 Z"
|
|
33095
|
-
};
|
|
33096
|
-
function ColorModeIcon({
|
|
33097
|
-
mode
|
|
33098
|
-
}) {
|
|
33099
|
-
return /*#__PURE__*/React.createElement("svg", {
|
|
33100
|
-
width: "16",
|
|
33101
|
-
height: "16",
|
|
33102
|
-
viewBox: "0 0 14 14",
|
|
33103
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
33104
|
-
"aria-hidden": "true"
|
|
33105
|
-
}, /*#__PURE__*/React.createElement("path", {
|
|
33106
|
-
d: COLOR_MODE_ICON_PATHS[mode],
|
|
33107
|
-
strokeWidth: "1.5",
|
|
33108
|
-
fill: "none",
|
|
33109
|
-
strokeLinecap: "round",
|
|
33110
|
-
strokeLinejoin: "round"
|
|
33111
|
-
}));
|
|
33112
|
-
}
|
|
33113
34107
|
|
|
33114
34108
|
const requestClearConnectedPeopleCache = () => remove();
|
|
33115
34109
|
function UserProfile({
|
|
@@ -33172,10 +34166,10 @@ true;
|
|
|
33172
34166
|
alignment: "center",
|
|
33173
34167
|
spacing: 0.5,
|
|
33174
34168
|
width: "100%"
|
|
33175
|
-
}, /*#__PURE__*/React.createElement(
|
|
34169
|
+
}, /*#__PURE__*/React.createElement(Icon, {
|
|
33176
34170
|
name: mfaConfigured ? "general.lock" : "general.unlock",
|
|
33177
|
-
size:
|
|
33178
|
-
|
|
34171
|
+
size: 12,
|
|
34172
|
+
theme: mfaConfigured ? "--t-icon-color-status-success" : "--t-icon-color-status-error"
|
|
33179
34173
|
}), /*#__PURE__*/React.createElement(tapestryReact.Text, {
|
|
33180
34174
|
size: 4,
|
|
33181
34175
|
weight: 400,
|
|
@@ -33223,9 +34217,10 @@ true;
|
|
|
33223
34217
|
}, /*#__PURE__*/React.createElement(tapestryReact.Text, {
|
|
33224
34218
|
color: "var(--text-color-default-secondary)",
|
|
33225
34219
|
size: 3
|
|
33226
|
-
}, "Log out"), /*#__PURE__*/React.createElement(
|
|
34220
|
+
}, "Log out"), /*#__PURE__*/React.createElement(Icon, {
|
|
33227
34221
|
name: "general.intoBox",
|
|
33228
|
-
|
|
34222
|
+
size: 14,
|
|
34223
|
+
theme: "--t-icon-color-secondary"
|
|
33229
34224
|
})))), env !== "production" && /*#__PURE__*/React.createElement(DebugPcoResource, {
|
|
33230
34225
|
env: env
|
|
33231
34226
|
}), hasConnectedPeople && (/*#__PURE__*/React.createElement(tapestryReact.StackView, {
|
|
@@ -33287,26 +34282,42 @@ true;
|
|
|
33287
34282
|
|
|
33288
34283
|
var ActionType;
|
|
33289
34284
|
(function (ActionType) {
|
|
34285
|
+
ActionType["clearPending"] = "clearPending";
|
|
33290
34286
|
ActionType["close"] = "close";
|
|
33291
34287
|
ActionType["mount"] = "mount";
|
|
33292
34288
|
ActionType["open"] = "open";
|
|
34289
|
+
ActionType["setPending"] = "setPending";
|
|
33293
34290
|
ActionType["unmount"] = "unmount";
|
|
33294
34291
|
})(ActionType || (ActionType = {}));
|
|
33295
34292
|
const initialState = {
|
|
33296
34293
|
currentContent: null,
|
|
33297
|
-
open: false
|
|
34294
|
+
open: false,
|
|
34295
|
+
pending: null
|
|
33298
34296
|
};
|
|
33299
34297
|
const reducer = (state, action) => {
|
|
34298
|
+
var _a, _b;
|
|
33300
34299
|
switch (action.type) {
|
|
33301
34300
|
case ActionType.mount:
|
|
33302
34301
|
return {
|
|
33303
34302
|
...state,
|
|
33304
|
-
currentContent: action.payload.content
|
|
34303
|
+
currentContent: action.payload.content,
|
|
34304
|
+
pending: ((_a = state.pending) === null || _a === void 0 ? void 0 : _a.tool) === ((_b = action.payload.content) === null || _b === void 0 ? void 0 : _b.name) ? state.pending : null
|
|
33305
34305
|
};
|
|
33306
34306
|
case ActionType.unmount:
|
|
33307
34307
|
return {
|
|
33308
34308
|
...state,
|
|
33309
|
-
currentContent: null
|
|
34309
|
+
currentContent: null,
|
|
34310
|
+
pending: null
|
|
34311
|
+
};
|
|
34312
|
+
case ActionType.setPending:
|
|
34313
|
+
return {
|
|
34314
|
+
...state,
|
|
34315
|
+
pending: action.payload
|
|
34316
|
+
};
|
|
34317
|
+
case ActionType.clearPending:
|
|
34318
|
+
return {
|
|
34319
|
+
...state,
|
|
34320
|
+
pending: null
|
|
33310
34321
|
};
|
|
33311
34322
|
case ActionType.open:
|
|
33312
34323
|
return {
|
|
@@ -33325,13 +34336,181 @@ true;
|
|
|
33325
34336
|
...overrides
|
|
33326
34337
|
});
|
|
33327
34338
|
|
|
33328
|
-
var styles$
|
|
34339
|
+
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"};
|
|
33329
34340
|
|
|
33330
|
-
|
|
34341
|
+
// PCO Box serves TLS by default, and a bare http:// frame inside an https
|
|
34342
|
+
// product page is blocked as mixed content, so follow the page's scheme.
|
|
34343
|
+
const iframeSrc = () => `${window.location.protocol}//pco.test/iframe`;
|
|
34344
|
+
const STAFF_PINK = "var(--t-fill-color-product-staff)";
|
|
34345
|
+
// The pco.test domain is only ever served by a local PCO Box, so the tool
|
|
34346
|
+
// stays hidden in staging and production — and on Topbar's own dev server,
|
|
34347
|
+
// which runs on localhost and has no pco.test to frame.
|
|
34348
|
+
function isPcoTestHost() {
|
|
34349
|
+
const {
|
|
34350
|
+
hostname
|
|
34351
|
+
} = window.location;
|
|
34352
|
+
return hostname === "pco.test" || hostname.endsWith(".pco.test");
|
|
34353
|
+
}
|
|
34354
|
+
// Flattened from the source artwork's two outline paths — its gradients,
|
|
34355
|
+
// filters and clip paths carry nothing at icon size. Those paths are authored
|
|
34356
|
+
// on a 128px canvas, hence the transform: it fits the pair into the same ~24px
|
|
34357
|
+
// optical box the sibling toolbar icons occupy. Filled explicitly rather than
|
|
34358
|
+
// with `currentColor` so the mark stays staff pink instead of inheriting the
|
|
34359
|
+
// toolbar's text color and its hover/active states.
|
|
34360
|
+
const WRENCH = "m22.42 17.2 12.96 12.95-2.5 8.78-8.78 2.5-12.96-12.96-5.86 5.86 14.89 21.46 17.56-3.34 61.63 65.62c9.44 9.51 22.17-4.75 13.34-12.87L46.85 42.92l4.01-17.76-22.14-14.27-6.3 6.31zm81.06 91.97c1.68-1.68 4.4-1.68 6.08 0 1.68 1.68 1.68 4.4 0 6.08-1.68 1.67-4.4 1.67-6.08 0-1.68-1.68-1.68-4.4 0-6.08z";
|
|
34361
|
+
const SCREWDRIVER = "m55.8 12 .76.16c8.84-2.94 28.68 8.64 24.26 18.96-1.12 1.62-.93 4.5.42 6.31L56.43 62.25c-1.39-1.39-4.48-1.36-5.9 0L9.5 101.63c-3.74 3.59-.8 6.64.78 8.25l7.36 7.36c1.61 1.6 5.86 3.76 8.88.56l38.72-40.81c1.35-1.42 1.36-4.47 0-5.87 0 0 .01-.02 0-.02l24.91-24.9c2.63 2.37 5.11 2.57 7.47 1.93 4.55-1.25 7.09 3.21 9.81 6.46l-1.23 1.21c-1.82 1.84-1.65 4.94.39 6.98s5.15 2.21 6.97.39l8.14-8.13c1.82-1.84 1.65-4.94-.4-6.97-1.84-1.85-4.51-2.09-6.37-.77-5.58-6.26-2.65-7.2-10.01-15.35L91.13 17.86C81.2 8.12 68.3 8.13 55.8 12z";
|
|
34362
|
+
function ToolsIcon() {
|
|
34363
|
+
return /*#__PURE__*/React.createElement("svg", {
|
|
34364
|
+
"aria-hidden": "true",
|
|
34365
|
+
width: "32",
|
|
34366
|
+
height: "32",
|
|
34367
|
+
viewBox: "0 0 32 32",
|
|
34368
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
34369
|
+
}, /*#__PURE__*/React.createElement("g", {
|
|
34370
|
+
fill: STAFF_PINK,
|
|
34371
|
+
transform: "translate(2.92 2.66) scale(.204)"
|
|
34372
|
+
}, /*#__PURE__*/React.createElement("path", {
|
|
34373
|
+
d: WRENCH
|
|
34374
|
+
}), /*#__PURE__*/React.createElement("path", {
|
|
34375
|
+
d: SCREWDRIVER
|
|
34376
|
+
})));
|
|
34377
|
+
}
|
|
34378
|
+
// A cross-origin frame's own history object is unreachable (touching it throws
|
|
34379
|
+
// a SecurityError), but the frame's navigations do land in the top-level joint
|
|
34380
|
+
// session history, so the *parent's* back/forward traverse them while leaving
|
|
34381
|
+
// the host page in place. Counting frame loads tracks how deep we are, which is
|
|
34382
|
+
// what keeps a click on Back from stepping past the frame's first page and
|
|
34383
|
+
// navigating the host app instead. Loads we caused are attributed to the
|
|
34384
|
+
// traversal that requested them rather than counted as new navigations.
|
|
34385
|
+
function useFrameHistory() {
|
|
34386
|
+
const [steps, setSteps] = React.useState({
|
|
34387
|
+
back: 0,
|
|
34388
|
+
forward: 0
|
|
34389
|
+
});
|
|
34390
|
+
// Traversal has to read the current counts outside of render, so the ref is
|
|
34391
|
+
// the source of truth and state exists to paint the buttons.
|
|
34392
|
+
const stepsRef = React.useRef(steps);
|
|
34393
|
+
const pendingTraversal = React.useRef(null);
|
|
34394
|
+
const frameIsLoaded = React.useRef(false);
|
|
34395
|
+
const moveTo = React.useCallback(next => {
|
|
34396
|
+
stepsRef.current = next;
|
|
34397
|
+
setSteps(next);
|
|
34398
|
+
}, []);
|
|
34399
|
+
const onFrameLoad = React.useCallback(() => {
|
|
34400
|
+
if (!frameIsLoaded.current) {
|
|
34401
|
+
// The frame's first page is the floor, not a step.
|
|
34402
|
+
frameIsLoaded.current = true;
|
|
34403
|
+
} else {
|
|
34404
|
+
const traversal = pendingTraversal.current;
|
|
34405
|
+
pendingTraversal.current = null;
|
|
34406
|
+
const {
|
|
34407
|
+
back,
|
|
34408
|
+
forward
|
|
34409
|
+
} = stepsRef.current;
|
|
34410
|
+
if (traversal === "back") {
|
|
34411
|
+
moveTo({
|
|
34412
|
+
back: back - 1,
|
|
34413
|
+
forward: forward + 1
|
|
34414
|
+
});
|
|
34415
|
+
} else if (traversal === "forward") {
|
|
34416
|
+
moveTo({
|
|
34417
|
+
back: back + 1,
|
|
34418
|
+
forward: forward - 1
|
|
34419
|
+
});
|
|
34420
|
+
} else {
|
|
34421
|
+
moveTo({
|
|
34422
|
+
back: back + 1,
|
|
34423
|
+
forward: 0
|
|
34424
|
+
});
|
|
34425
|
+
}
|
|
34426
|
+
}
|
|
34427
|
+
}, [moveTo]);
|
|
34428
|
+
// Guarded here rather than trusting the button's disabled state: traversing
|
|
34429
|
+
// with no frame step left would navigate the host app out from under us.
|
|
34430
|
+
const traverse = React.useCallback(direction => {
|
|
34431
|
+
if (stepsRef.current[direction] > 0) {
|
|
34432
|
+
pendingTraversal.current = direction;
|
|
34433
|
+
window.history[direction]();
|
|
34434
|
+
}
|
|
34435
|
+
}, []);
|
|
34436
|
+
return {
|
|
34437
|
+
onFrameLoad,
|
|
34438
|
+
steps,
|
|
34439
|
+
traverse
|
|
34440
|
+
};
|
|
34441
|
+
}
|
|
34442
|
+
function NavButton({
|
|
34443
|
+
direction,
|
|
34444
|
+
disabled,
|
|
34445
|
+
onClick
|
|
34446
|
+
}) {
|
|
34447
|
+
return /*#__PURE__*/React.createElement(tapestryReact.Button
|
|
34448
|
+
// Tapestry's `disabled` suppresses the click but emits no ARIA state.
|
|
34449
|
+
, {
|
|
34450
|
+
"aria-disabled": disabled,
|
|
34451
|
+
disabled: disabled,
|
|
34452
|
+
hover: {
|
|
34453
|
+
backgroundColor: "var(--t-fill-color-neutral-070)"
|
|
34454
|
+
},
|
|
34455
|
+
icon: {
|
|
34456
|
+
fill: "var(--t-icon-color-dim)",
|
|
34457
|
+
name: direction === "back" ? "general.leftChevron" : "general.rightChevron",
|
|
34458
|
+
size: "md"
|
|
34459
|
+
},
|
|
34460
|
+
onClick: onClick,
|
|
34461
|
+
opacity: disabled ? 0.35 : 1,
|
|
34462
|
+
theme: false,
|
|
34463
|
+
title: direction === "back" ? "Back" : "Forward"
|
|
34464
|
+
});
|
|
34465
|
+
}
|
|
34466
|
+
function DeveloperToolsComponent({
|
|
34467
|
+
closeDrawer
|
|
34468
|
+
}) {
|
|
34469
|
+
const {
|
|
34470
|
+
onFrameLoad,
|
|
34471
|
+
steps,
|
|
34472
|
+
traverse
|
|
34473
|
+
} = useFrameHistory();
|
|
34474
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DrawerHeader, {
|
|
34475
|
+
closeDrawer: closeDrawer
|
|
34476
|
+
}, /*#__PURE__*/React.createElement(tapestryReact.StackView, {
|
|
34477
|
+
alignment: "center",
|
|
34478
|
+
axis: "horizontal",
|
|
34479
|
+
spacing: 0.25
|
|
34480
|
+
}, /*#__PURE__*/React.createElement(NavButton, {
|
|
34481
|
+
direction: "back",
|
|
34482
|
+
disabled: steps.back === 0,
|
|
34483
|
+
onClick: () => traverse("back")
|
|
34484
|
+
}), /*#__PURE__*/React.createElement(NavButton, {
|
|
34485
|
+
direction: "forward",
|
|
34486
|
+
disabled: steps.forward === 0,
|
|
34487
|
+
onClick: () => traverse("forward")
|
|
34488
|
+
}), /*#__PURE__*/React.createElement(DrawerHeading, {
|
|
34489
|
+
paddingLeft: 0.5
|
|
34490
|
+
}, "Developer Tools"))), /*#__PURE__*/React.createElement(tapestryReact.Box, {
|
|
34491
|
+
as: "iframe",
|
|
34492
|
+
border: "none",
|
|
34493
|
+
flex: "1",
|
|
34494
|
+
minHeight: 0,
|
|
34495
|
+
onLoad: onFrameLoad,
|
|
34496
|
+
src: iframeSrc(),
|
|
34497
|
+
title: "Developer Tools",
|
|
34498
|
+
width: "100%"
|
|
34499
|
+
}));
|
|
34500
|
+
}
|
|
34501
|
+
const DeveloperTools = {
|
|
34502
|
+
component: DeveloperToolsComponent,
|
|
34503
|
+
icons: {
|
|
34504
|
+
component: ToolsIcon
|
|
34505
|
+
},
|
|
34506
|
+
label: "Developer Tools",
|
|
34507
|
+
// No space: `name` becomes an element id and a pendo attribute.
|
|
34508
|
+
name: "DeveloperTools"
|
|
34509
|
+
};
|
|
33331
34510
|
|
|
33332
|
-
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"};
|
|
34511
|
+
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"};
|
|
33333
34512
|
|
|
33334
|
-
const SPARKLE_PATH = "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";
|
|
34513
|
+
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";
|
|
33335
34514
|
// Same stops as the AskAIBanner border gradient, so the two stay in sync.
|
|
33336
34515
|
const GRADIENT_STOPS = [{
|
|
33337
34516
|
color: "var(--t-fill-color-tag-teal-040)",
|
|
@@ -33380,7 +34559,7 @@ true;
|
|
|
33380
34559
|
stopColor: color
|
|
33381
34560
|
}
|
|
33382
34561
|
})))))), /*#__PURE__*/React.createElement("path", {
|
|
33383
|
-
d: SPARKLE_PATH,
|
|
34562
|
+
d: SPARKLE_PATH$1,
|
|
33384
34563
|
fill: isGradient ? `url(#${gradientId})` : "currentColor"
|
|
33385
34564
|
}));
|
|
33386
34565
|
}
|
|
@@ -33389,8 +34568,8 @@ true;
|
|
|
33389
34568
|
var _a, _b;
|
|
33390
34569
|
const isStacked = props.variant !== "inline";
|
|
33391
34570
|
return /*#__PURE__*/React.createElement("button", {
|
|
33392
|
-
className: classnames(styles.banner, {
|
|
33393
|
-
[styles.stacked]: isStacked
|
|
34571
|
+
className: classnames(styles$1.banner, {
|
|
34572
|
+
[styles$1.stacked]: isStacked
|
|
33394
34573
|
}),
|
|
33395
34574
|
onClick: () => props.onClick(),
|
|
33396
34575
|
type: "button"
|
|
@@ -33405,15 +34584,15 @@ true;
|
|
|
33405
34584
|
title
|
|
33406
34585
|
}) {
|
|
33407
34586
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
|
|
33408
|
-
className: styles.content
|
|
34587
|
+
className: styles$1.content
|
|
33409
34588
|
}, /*#__PURE__*/React.createElement(AskAIIcon, null), /*#__PURE__*/React.createElement("span", {
|
|
33410
|
-
className: styles.text
|
|
34589
|
+
className: styles$1.text
|
|
33411
34590
|
}, /*#__PURE__*/React.createElement("span", {
|
|
33412
|
-
className: styles.title
|
|
34591
|
+
className: styles$1.title
|
|
33413
34592
|
}, title), /*#__PURE__*/React.createElement("span", {
|
|
33414
|
-
className: styles.subtitle
|
|
34593
|
+
className: styles$1.subtitle
|
|
33415
34594
|
}, "Support answers tailored to you"))), /*#__PURE__*/React.createElement(tapestryReact.Icon, {
|
|
33416
|
-
className: styles.chevron,
|
|
34595
|
+
className: styles$1.chevron,
|
|
33417
34596
|
name: "general.rightChevron",
|
|
33418
34597
|
size: "16px"
|
|
33419
34598
|
}));
|
|
@@ -33423,18 +34602,18 @@ true;
|
|
|
33423
34602
|
title
|
|
33424
34603
|
}) {
|
|
33425
34604
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
|
|
33426
|
-
className: styles.inlineContent
|
|
34605
|
+
className: styles$1.inlineContent
|
|
33427
34606
|
}, /*#__PURE__*/React.createElement(SparkleIcon, {
|
|
33428
|
-
className: styles.sparkle,
|
|
34607
|
+
className: styles$1.sparkle,
|
|
33429
34608
|
fill: "currentColor"
|
|
33430
34609
|
}), /*#__PURE__*/React.createElement("span", {
|
|
33431
|
-
className: styles.inlineTitle
|
|
34610
|
+
className: styles$1.inlineTitle
|
|
33432
34611
|
}, title)), /*#__PURE__*/React.createElement("span", {
|
|
33433
|
-
className: styles.action
|
|
34612
|
+
className: styles$1.action
|
|
33434
34613
|
}, /*#__PURE__*/React.createElement("span", {
|
|
33435
|
-
className: styles.actionLabel
|
|
34614
|
+
className: styles$1.actionLabel
|
|
33436
34615
|
}, action), /*#__PURE__*/React.createElement(tapestryReact.Icon, {
|
|
33437
|
-
className: styles.actionChevron,
|
|
34616
|
+
className: styles$1.actionChevron,
|
|
33438
34617
|
name: "general.rightChevron",
|
|
33439
34618
|
size: "14px"
|
|
33440
34619
|
})));
|
|
@@ -33451,9 +34630,9 @@ true;
|
|
|
33451
34630
|
const borderA = `${uid}-border-a`;
|
|
33452
34631
|
const borderB = `${uid}-border-b`;
|
|
33453
34632
|
return /*#__PURE__*/React.createElement("span", {
|
|
33454
|
-
className: styles.icon
|
|
34633
|
+
className: styles$1.icon
|
|
33455
34634
|
}, /*#__PURE__*/React.createElement("svg", {
|
|
33456
|
-
className: styles.iconShape,
|
|
34635
|
+
className: styles$1.iconShape,
|
|
33457
34636
|
viewBox: "0 0 32 32",
|
|
33458
34637
|
fill: "none",
|
|
33459
34638
|
"aria-hidden": "true"
|
|
@@ -33624,7 +34803,7 @@ true;
|
|
|
33624
34803
|
stopColor: "#D48B91",
|
|
33625
34804
|
stopOpacity: "0"
|
|
33626
34805
|
})))), /*#__PURE__*/React.createElement("svg", {
|
|
33627
|
-
className: styles.iconSparkle,
|
|
34806
|
+
className: styles$1.iconSparkle,
|
|
33628
34807
|
viewBox: "0 0 19.4995 17.9999",
|
|
33629
34808
|
"aria-hidden": "true"
|
|
33630
34809
|
}, /*#__PURE__*/React.createElement("path", {
|
|
@@ -33633,6 +34812,23 @@ true;
|
|
|
33633
34812
|
})));
|
|
33634
34813
|
}
|
|
33635
34814
|
|
|
34815
|
+
var styles = {"button":"button-2NVI1","sparkle":"sparkle-ruJGP","label":"label-4NUGB"};
|
|
34816
|
+
|
|
34817
|
+
function AskAIButton({
|
|
34818
|
+
onClick,
|
|
34819
|
+
text = "Ask AI"
|
|
34820
|
+
}) {
|
|
34821
|
+
return /*#__PURE__*/React.createElement("button", {
|
|
34822
|
+
className: styles.button,
|
|
34823
|
+
onClick: () => onClick(),
|
|
34824
|
+
type: "button"
|
|
34825
|
+
}, /*#__PURE__*/React.createElement(SparkleIcon, {
|
|
34826
|
+
className: styles.sparkle
|
|
34827
|
+
}), /*#__PURE__*/React.createElement("span", {
|
|
34828
|
+
className: styles.label
|
|
34829
|
+
}, text));
|
|
34830
|
+
}
|
|
34831
|
+
|
|
33636
34832
|
// The article heading lives in the deepest populated hierarchy level (lvl0 is
|
|
33637
34833
|
// always "Planning Center").
|
|
33638
34834
|
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) || "";
|
|
@@ -33788,16 +34984,24 @@ true;
|
|
|
33788
34984
|
// not the path, so updating it is a same-document navigation that doesn't
|
|
33789
34985
|
// reload the iframe, but also lets the route survive a forced reload (e.g.
|
|
33790
34986
|
// Turbo re-parenting the topbar).
|
|
33791
|
-
|
|
34987
|
+
//
|
|
34988
|
+
// `panel` loads picobot as its own toolbar panel rather than inside Helpdesk.
|
|
34989
|
+
function buildPicobotSrc(origin, path, {
|
|
34990
|
+
panel = false
|
|
34991
|
+
} = {}) {
|
|
33792
34992
|
const [rawPath, queryPart] = path.replace(/^\/+/, "").split("?", 2);
|
|
33793
34993
|
const safePath = rawPath.split("/").filter(segment => segment && segment !== "." && segment !== "..").join("/");
|
|
33794
34994
|
const fragment = queryPart !== undefined ? `${safePath}?${queryPart}` : safePath;
|
|
33795
|
-
const
|
|
34995
|
+
const url = new URL("topbar", origin);
|
|
34996
|
+
if (panel) url.searchParams.set("panel", "1");
|
|
34997
|
+
const base = url.toString();
|
|
33796
34998
|
return fragment ? `${base}#${fragment}` : base;
|
|
33797
34999
|
}
|
|
33798
35000
|
|
|
33799
35001
|
const LOAD_TIMEOUT_MS = 3000;
|
|
33800
|
-
function createPicobotIframe(picobotOrigin
|
|
35002
|
+
function createPicobotIframe(picobotOrigin, {
|
|
35003
|
+
panel = false
|
|
35004
|
+
} = {}) {
|
|
33801
35005
|
return /*#__PURE__*/React.forwardRef(function PicobotIframe({
|
|
33802
35006
|
currentPath: iframePath,
|
|
33803
35007
|
onChangePath: onIframeChangePath,
|
|
@@ -33853,7 +35057,9 @@ true;
|
|
|
33853
35057
|
(_b = (_a = iframeRef.current) === null || _a === void 0 ? void 0 : _a.contentWindow) === null || _b === void 0 ? void 0 : _b.postMessage(message, picobotOrigin);
|
|
33854
35058
|
}
|
|
33855
35059
|
}), []);
|
|
33856
|
-
const iframeSrc = buildPicobotSrc(picobotOrigin, loadedPath
|
|
35060
|
+
const iframeSrc = buildPicobotSrc(picobotOrigin, loadedPath, {
|
|
35061
|
+
panel
|
|
35062
|
+
});
|
|
33857
35063
|
return /*#__PURE__*/React.createElement("iframe", {
|
|
33858
35064
|
ref: iframeRef,
|
|
33859
35065
|
src: iframeSrc,
|
|
@@ -33892,22 +35098,28 @@ true;
|
|
|
33892
35098
|
currentPath,
|
|
33893
35099
|
env,
|
|
33894
35100
|
helpdeskConfig,
|
|
33895
|
-
|
|
35101
|
+
isPicobotLoading,
|
|
35102
|
+
onChangePath,
|
|
35103
|
+
openPicobot
|
|
33896
35104
|
}) {
|
|
33897
35105
|
const PicobotIframe = React.useMemo(() => createPicobotIframe(pcoUrl(env)("picobot")), [env]);
|
|
35106
|
+
const loadingFallback = /*#__PURE__*/React.createElement(StackView$1, {
|
|
35107
|
+
flex: 1,
|
|
35108
|
+
axis: "vertical"
|
|
35109
|
+
}, /*#__PURE__*/React.createElement(DrawerHeader, {
|
|
35110
|
+
closeDrawer: closeDrawer
|
|
35111
|
+
}, /*#__PURE__*/React.createElement(StackView$1, {
|
|
35112
|
+
axis: "horizontal",
|
|
35113
|
+
spacing: 1
|
|
35114
|
+
}, /*#__PURE__*/React.createElement(DrawerHeading, null, "Help and support"), /*#__PURE__*/React.createElement(LoadingIndicator$1, null))));
|
|
35115
|
+
// The add-on reads its args once, so mounting before the gate answers
|
|
35116
|
+
// would leave out openPicobot for good.
|
|
35117
|
+
if (isPicobotLoading) return loadingFallback;
|
|
33898
35118
|
return /*#__PURE__*/React.createElement(addOns.UiExtensionFirstPartyComponent, {
|
|
33899
35119
|
grow: 1,
|
|
33900
35120
|
display: "flex",
|
|
33901
35121
|
extensionId: env === "staging" ? "42" : "11",
|
|
33902
|
-
loadingFallback:
|
|
33903
|
-
flex: 1,
|
|
33904
|
-
axis: "vertical"
|
|
33905
|
-
}, /*#__PURE__*/React.createElement(DrawerHeader, {
|
|
33906
|
-
closeDrawer: closeDrawer
|
|
33907
|
-
}, /*#__PURE__*/React.createElement(StackView$1, {
|
|
33908
|
-
axis: "horizontal",
|
|
33909
|
-
spacing: 1
|
|
33910
|
-
}, /*#__PURE__*/React.createElement(DrawerHeading, null, "Help and support"), /*#__PURE__*/React.createElement(LoadingIndicator$1, null)))),
|
|
35122
|
+
loadingFallback: loadingFallback,
|
|
33911
35123
|
additionalComponents: {
|
|
33912
35124
|
AskAIBanner: {
|
|
33913
35125
|
component: AskAIBanner,
|
|
@@ -33927,6 +35139,18 @@ true;
|
|
|
33927
35139
|
}
|
|
33928
35140
|
}
|
|
33929
35141
|
},
|
|
35142
|
+
AskAIButton: {
|
|
35143
|
+
component: AskAIButton,
|
|
35144
|
+
properties: {
|
|
35145
|
+
onClick: {
|
|
35146
|
+
event: false,
|
|
35147
|
+
type: Function
|
|
35148
|
+
},
|
|
35149
|
+
text: {
|
|
35150
|
+
type: String
|
|
35151
|
+
}
|
|
35152
|
+
}
|
|
35153
|
+
},
|
|
33930
35154
|
CalendlyWidget: {
|
|
33931
35155
|
component: CalendlyWidget,
|
|
33932
35156
|
properties: {
|
|
@@ -34005,7 +35229,11 @@ true;
|
|
|
34005
35229
|
currentPageParams: location.search,
|
|
34006
35230
|
currentPath,
|
|
34007
35231
|
helpdeskConfig,
|
|
34008
|
-
onChangePath
|
|
35232
|
+
onChangePath,
|
|
35233
|
+
// Its presence tells helpdesk that Picobot has its own panel.
|
|
35234
|
+
...(openPicobot && {
|
|
35235
|
+
openPicobot
|
|
35236
|
+
})
|
|
34009
35237
|
}
|
|
34010
35238
|
});
|
|
34011
35239
|
}
|
|
@@ -34552,6 +35780,142 @@ true;
|
|
|
34552
35780
|
name: "Notifications"
|
|
34553
35781
|
};
|
|
34554
35782
|
|
|
35783
|
+
const SPARKLE_PATH = "M8.7824 18.8791C8.88371 18.5973 9.26996 18.5973 9.37127 18.8791L9.94256 20.465C10.0222 20.6863 10.1914 20.8616 10.4064 20.9435L11.6779 21.4269L11.9484 21.5304L11.9963 21.5539C12.2205 21.6839 12.2047 22.039 11.9484 22.1369L11.6779 22.2394L10.4064 22.7248C10.1915 22.8068 10.0222 22.9812 9.94256 23.2023L9.37127 24.7893L9.34881 24.8381C9.23098 25.054 8.92273 25.0539 8.80486 24.8381L8.7824 24.7893L8.2111 23.2023C8.14141 23.0088 8.0029 22.8513 7.82536 22.76L7.74723 22.7248L6.47573 22.2394L6.20522 22.1369C5.93172 22.0325 5.93164 21.6347 6.20522 21.5304L6.47476 21.4269L7.74723 20.9435C7.96226 20.8616 8.13147 20.6863 8.2111 20.465L8.7824 18.8791ZM17.5706 8.11138C17.8241 7.40747 18.7928 7.40734 19.0462 8.11138L20.472 12.0782C20.6711 12.6318 21.0952 13.0687 21.6331 13.2735L25.4867 14.7413C26.1711 15.002 26.1711 15.9983 25.4867 16.2589L21.6331 17.7267C21.0952 17.9316 20.6711 18.3684 20.472 18.922L19.2649 22.2785L19.0462 22.8889L18.9905 23.0119C18.6956 23.5517 17.9212 23.5515 17.6262 23.0119L17.5706 22.8889L17.3508 22.2795L16.1448 18.922C15.9457 18.3684 15.5206 17.9316 14.9827 17.7267L11.1291 16.2589C10.4449 15.9982 10.4449 15.0021 11.1291 14.7413L14.9827 13.2735C15.4533 13.0943 15.8379 12.7376 16.0598 12.2804L16.1448 12.0782L17.5706 8.11138ZM18.0266 12.755C17.6351 13.8438 16.7916 14.7249 15.6946 15.1427L14.7551 15.5001L15.6946 15.8575C16.7916 16.2753 17.6351 17.1564 18.0266 18.2453L18.3079 19.0275L18.5891 18.2453C18.9806 17.1564 19.8242 16.2753 20.9212 15.8575L21.8597 15.5001L20.9212 15.1427C19.8242 14.7249 18.9806 13.8439 18.5891 12.755L18.3079 11.9718L18.0266 12.755ZM8.7824 6.21097C8.88388 5.9297 9.26982 5.92966 9.37127 6.21097L9.94256 7.7979C10.0222 8.01911 10.1915 8.19346 10.4064 8.27544L11.6779 8.75982L11.8322 9.16607L11.9484 8.86334C12.2218 8.96779 12.2218 9.36542 11.9484 9.46979L11.6779 9.57233L10.4064 10.0567C10.1914 10.1387 10.0222 10.3139 9.94256 10.5352L9.37127 12.1212C9.26996 12.4029 8.88371 12.4029 8.7824 12.1212L8.2111 10.5352C8.14139 10.3415 8.00304 10.1832 7.82536 10.0919L7.74723 10.0567L6.47573 9.57233L6.20522 9.46979C5.9486 9.37195 5.93265 9.01568 6.15737 8.8858L6.20522 8.86334L7.74723 8.27544C7.96214 8.19346 8.13149 8.0191 8.2111 7.7979L8.69451 6.45413L8.7824 6.21097ZM11.9484 8.86334L11.8322 9.16607L11.6779 8.75982L11.9484 8.86334Z";
|
|
35784
|
+
// Helpdesk reads this context off the email path to open its email form the
|
|
35785
|
+
// same way a handoff inside its own drawer does.
|
|
35786
|
+
function emailPathWithContext(context = {}) {
|
|
35787
|
+
const params = new URLSearchParams();
|
|
35788
|
+
for (const key of ["conversationId", "source", "turnIndex"]) {
|
|
35789
|
+
const value = context[key];
|
|
35790
|
+
if (value !== null && value !== undefined) params.set(key, String(value));
|
|
35791
|
+
}
|
|
35792
|
+
return `email?${params}`;
|
|
35793
|
+
}
|
|
35794
|
+
function PicobotComponent({
|
|
35795
|
+
clearPending,
|
|
35796
|
+
closeDrawer,
|
|
35797
|
+
currentPath,
|
|
35798
|
+
env,
|
|
35799
|
+
onChangePath,
|
|
35800
|
+
openTool,
|
|
35801
|
+
pending
|
|
35802
|
+
}) {
|
|
35803
|
+
const PicobotIframe = React.useMemo(() => createPicobotIframe(pcoUrl(env)("picobot"), {
|
|
35804
|
+
panel: true
|
|
35805
|
+
}), [env]);
|
|
35806
|
+
const picobotRef = React.useRef(null);
|
|
35807
|
+
const [isPicobotReady, setIsPicobotReady] = React.useState(false);
|
|
35808
|
+
// Picobot has no listener until it sends READY, so openPicobot's draft or
|
|
35809
|
+
// conversation waits for it.
|
|
35810
|
+
React.useEffect(function handOverPendingOncePicobotIsReady() {
|
|
35811
|
+
var _a, _b;
|
|
35812
|
+
if (!pending || !isPicobotReady) return;
|
|
35813
|
+
const {
|
|
35814
|
+
conversationId,
|
|
35815
|
+
draft,
|
|
35816
|
+
ticketExternalId
|
|
35817
|
+
} = pending;
|
|
35818
|
+
if (conversationId) {
|
|
35819
|
+
(_a = picobotRef.current) === null || _a === void 0 ? void 0 : _a.postMessage({
|
|
35820
|
+
payload: {
|
|
35821
|
+
conversation_id: conversationId,
|
|
35822
|
+
// Picobot asks whether this resolved the ticket, and closes it.
|
|
35823
|
+
...(ticketExternalId && {
|
|
35824
|
+
external_id: ticketExternalId,
|
|
35825
|
+
source: "ticket_submitted"
|
|
35826
|
+
})
|
|
35827
|
+
},
|
|
35828
|
+
type: "LOAD_CONVERSATION"
|
|
35829
|
+
});
|
|
35830
|
+
} else if (draft) {
|
|
35831
|
+
// Not both: Picobot cancels a conversation load when a prefill
|
|
35832
|
+
// starts a new conversation.
|
|
35833
|
+
(_b = picobotRef.current) === null || _b === void 0 ? void 0 : _b.postMessage({
|
|
35834
|
+
payload: {
|
|
35835
|
+
source: "email_form",
|
|
35836
|
+
text: draft
|
|
35837
|
+
},
|
|
35838
|
+
type: "PREFILL_COMPOSER"
|
|
35839
|
+
});
|
|
35840
|
+
}
|
|
35841
|
+
clearPending();
|
|
35842
|
+
}, [pending, isPicobotReady, clearPending]);
|
|
35843
|
+
const [iframeErrored, setIframeErrored] = React.useState(false);
|
|
35844
|
+
const [mountKey, setMountKey] = React.useState(0);
|
|
35845
|
+
const handleRetry = React.useCallback(() => {
|
|
35846
|
+
setIframeErrored(false);
|
|
35847
|
+
setIsPicobotReady(false);
|
|
35848
|
+
setMountKey(key => key + 1);
|
|
35849
|
+
}, []);
|
|
35850
|
+
const handleMessage = React.useCallback(data => {
|
|
35851
|
+
var _a;
|
|
35852
|
+
const message = data;
|
|
35853
|
+
switch (message === null || message === void 0 ? void 0 : message.type) {
|
|
35854
|
+
case "CLOSE_DRAWER":
|
|
35855
|
+
return closeDrawer();
|
|
35856
|
+
case "READY":
|
|
35857
|
+
return setIsPicobotReady(true);
|
|
35858
|
+
case "PICOBOT_SUPPORT_HANDOFF":
|
|
35859
|
+
return openTool("helpdesk", ((_a = message.payload) === null || _a === void 0 ? void 0 : _a.method) === "zoom" ? "phone" : emailPathWithContext(message.payload));
|
|
35860
|
+
case "IFRAME_LOAD_ERROR":
|
|
35861
|
+
console.warn("topbar: Picobot iframe failed to load");
|
|
35862
|
+
return setIframeErrored(true);
|
|
35863
|
+
}
|
|
35864
|
+
}, [closeDrawer, openTool]);
|
|
35865
|
+
if (iframeErrored) {
|
|
35866
|
+
return /*#__PURE__*/React.createElement(LoadError, {
|
|
35867
|
+
onContactSupport: () => openTool("helpdesk", "email"),
|
|
35868
|
+
onRetry: handleRetry
|
|
35869
|
+
});
|
|
35870
|
+
}
|
|
35871
|
+
return /*#__PURE__*/React.createElement(PicobotIframe, {
|
|
35872
|
+
key: mountKey,
|
|
35873
|
+
ref: picobotRef,
|
|
35874
|
+
currentPath: currentPath,
|
|
35875
|
+
onChangePath: onChangePath,
|
|
35876
|
+
onMessage: handleMessage
|
|
35877
|
+
});
|
|
35878
|
+
}
|
|
35879
|
+
function LoadError({
|
|
35880
|
+
onContactSupport,
|
|
35881
|
+
onRetry
|
|
35882
|
+
}) {
|
|
35883
|
+
return /*#__PURE__*/React.createElement(tapestryReact.StackView, {
|
|
35884
|
+
axis: "vertical",
|
|
35885
|
+
flex: 1,
|
|
35886
|
+
alignment: "center",
|
|
35887
|
+
distribution: "center",
|
|
35888
|
+
padding: 3,
|
|
35889
|
+
spacing: 1
|
|
35890
|
+
}, /*#__PURE__*/React.createElement(tapestryReact.Heading, {
|
|
35891
|
+
level: 2
|
|
35892
|
+
}, "Picobot is temporarily unavailable"), /*#__PURE__*/React.createElement(tapestryReact.Text, {
|
|
35893
|
+
size: 5,
|
|
35894
|
+
color: "var(--t-text-color-secondary)"
|
|
35895
|
+
}, "Try again or contact support."), /*#__PURE__*/React.createElement(tapestryReact.Box, {
|
|
35896
|
+
paddingTop: 2
|
|
35897
|
+
}, /*#__PURE__*/React.createElement(tapestryReact.Button, {
|
|
35898
|
+
onClick: onRetry,
|
|
35899
|
+
iconLeft: "general.refresh",
|
|
35900
|
+
theme: "primary",
|
|
35901
|
+
variant: "fill",
|
|
35902
|
+
title: "Retry"
|
|
35903
|
+
})), /*#__PURE__*/React.createElement(tapestryReact.Button, {
|
|
35904
|
+
onClick: onContactSupport,
|
|
35905
|
+
variant: "naked",
|
|
35906
|
+
theme: "primary",
|
|
35907
|
+
title: "Contact support"
|
|
35908
|
+
}));
|
|
35909
|
+
}
|
|
35910
|
+
const Picobot = {
|
|
35911
|
+
component: PicobotComponent,
|
|
35912
|
+
icons: {
|
|
35913
|
+
staticDotPath: SPARKLE_PATH,
|
|
35914
|
+
staticPath: SPARKLE_PATH
|
|
35915
|
+
},
|
|
35916
|
+
name: "Picobot"
|
|
35917
|
+
};
|
|
35918
|
+
|
|
34555
35919
|
const {
|
|
34556
35920
|
LoadingIndicator,
|
|
34557
35921
|
StackView
|
|
@@ -34947,6 +36311,41 @@ true;
|
|
|
34947
36311
|
};
|
|
34948
36312
|
};
|
|
34949
36313
|
|
|
36314
|
+
// Picobot's API rejects requests without a version header, and apiRequest
|
|
36315
|
+
// sets its own headers, so the version rides in on the fetch.
|
|
36316
|
+
const picobotFetch = (url, opts) => defaultFetch(url, {
|
|
36317
|
+
...opts,
|
|
36318
|
+
headers: {
|
|
36319
|
+
...(opts === null || opts === void 0 ? void 0 : opts.headers),
|
|
36320
|
+
"X-PCO-API-Version": "2026-08-11"
|
|
36321
|
+
}
|
|
36322
|
+
});
|
|
36323
|
+
// Routes wait on the gate, so a slow Picobot can't hold them up for long.
|
|
36324
|
+
const GATE_TIMEOUT_MS = 3000;
|
|
36325
|
+
const usePicobotConfig = ({
|
|
36326
|
+
isEnabled
|
|
36327
|
+
}) => {
|
|
36328
|
+
// Undefined until Picobot answers.
|
|
36329
|
+
const [panelEnabled, setPanelEnabled] = React.useState();
|
|
36330
|
+
React.useEffect(() => {
|
|
36331
|
+
if (!isEnabled) return undefined;
|
|
36332
|
+
// Whatever settles first wins, so a late answer can't reopen the gate.
|
|
36333
|
+
const settle = enabled => setPanelEnabled(current => current !== null && current !== void 0 ? current : enabled);
|
|
36334
|
+
const timeout = setTimeout(() => settle(false), GATE_TIMEOUT_MS);
|
|
36335
|
+
apiRequest(picobotFetch, `${window.location.origin}/~api/picobot/v2/me`).then(({
|
|
36336
|
+
json
|
|
36337
|
+
}) => {
|
|
36338
|
+
var _a, _b;
|
|
36339
|
+
return settle(((_b = (_a = json === null || json === void 0 ? void 0 : json.data) === null || _a === void 0 ? void 0 : _a.attributes) === null || _b === void 0 ? void 0 : _b.picobot_panel_enabled) === true);
|
|
36340
|
+
}).catch(() => settle(false)).finally(() => clearTimeout(timeout));
|
|
36341
|
+
return () => clearTimeout(timeout);
|
|
36342
|
+
}, [isEnabled]);
|
|
36343
|
+
return {
|
|
36344
|
+
displayPicobotIcon: panelEnabled === true,
|
|
36345
|
+
isLoading: isEnabled && panelEnabled === undefined
|
|
36346
|
+
};
|
|
36347
|
+
};
|
|
36348
|
+
|
|
34950
36349
|
const Profile = {
|
|
34951
36350
|
component: UserProfile,
|
|
34952
36351
|
icons: {
|
|
@@ -34957,10 +36356,16 @@ true;
|
|
|
34957
36356
|
};
|
|
34958
36357
|
|
|
34959
36358
|
const DURATION = 100;
|
|
36359
|
+
// Exempt paths match regardless of query, so `email?conversationId=…` is
|
|
36360
|
+
// exempt along with `email`.
|
|
34960
36361
|
function isToolPathExemptFromStorage(toolName, path) {
|
|
34961
36362
|
var _a, _b;
|
|
34962
36363
|
const tool = toolsRouteMap[toolName];
|
|
34963
|
-
|
|
36364
|
+
const [pathWithoutQuery] = path.split("?");
|
|
36365
|
+
return (_b = (_a = tool === null || tool === void 0 ? void 0 : tool.storageExemptPaths) === null || _a === void 0 ? void 0 : _a.includes(pathWithoutQuery)) !== null && _b !== void 0 ? _b : false;
|
|
36366
|
+
}
|
|
36367
|
+
function isConversationPath(path) {
|
|
36368
|
+
return Boolean(path === null || path === void 0 ? void 0 : path.startsWith("conversations/"));
|
|
34964
36369
|
}
|
|
34965
36370
|
function scrollToolDrawerToTop(toolName) {
|
|
34966
36371
|
var _a;
|
|
@@ -34973,6 +36378,7 @@ true;
|
|
|
34973
36378
|
Chat,
|
|
34974
36379
|
Helpdesk,
|
|
34975
36380
|
Notifications,
|
|
36381
|
+
Picobot,
|
|
34976
36382
|
Profile,
|
|
34977
36383
|
Tasks
|
|
34978
36384
|
};
|
|
@@ -35000,7 +36406,8 @@ true;
|
|
|
35000
36406
|
});
|
|
35001
36407
|
const {
|
|
35002
36408
|
currentContent,
|
|
35003
|
-
open
|
|
36409
|
+
open,
|
|
36410
|
+
pending
|
|
35004
36411
|
} = state;
|
|
35005
36412
|
const mounted = !!currentContent;
|
|
35006
36413
|
const storeData = useStore();
|
|
@@ -35013,6 +36420,15 @@ true;
|
|
|
35013
36420
|
...(env === "development" ? _storeDataOverride !== null && _storeDataOverride !== void 0 ? _storeDataOverride : {} : {})
|
|
35014
36421
|
};
|
|
35015
36422
|
const updateStore = useUpdateStore();
|
|
36423
|
+
const {
|
|
36424
|
+
displayPicobotIcon,
|
|
36425
|
+
isLoading: isPicobotGateLoading
|
|
36426
|
+
} = usePicobotConfig({
|
|
36427
|
+
isEnabled: Boolean(userId)
|
|
36428
|
+
});
|
|
36429
|
+
// Picobot is a kill-switched tool: until its gate says the panel is
|
|
36430
|
+
// enabled, its routes are held, and if it never does, they're ignored.
|
|
36431
|
+
const routableTool = React.useCallback(name => name === Picobot.name.toLowerCase() && !displayPicobotIcon ? undefined : toolsRouteMap[name], [displayPicobotIcon]);
|
|
35016
36432
|
React.useEffect(() => {
|
|
35017
36433
|
if (open) {
|
|
35018
36434
|
document.body.style.overflow = "hidden";
|
|
@@ -35100,7 +36516,9 @@ true;
|
|
|
35100
36516
|
});
|
|
35101
36517
|
}, [clearRoute]);
|
|
35102
36518
|
React.useEffect(() => {
|
|
35103
|
-
if (!toolbarFragmentRoute)
|
|
36519
|
+
if (!toolbarFragmentRoute || !routableTool(toolbarFragmentRoute.tool)) {
|
|
36520
|
+
return;
|
|
36521
|
+
}
|
|
35104
36522
|
const isExempt = isToolPathExemptFromStorage(toolbarFragmentRoute.tool, toolbarFragmentRoute.path);
|
|
35105
36523
|
if (isExempt) return;
|
|
35106
36524
|
const nextState = {
|
|
@@ -35110,12 +36528,32 @@ true;
|
|
|
35110
36528
|
if (!shallowCompare(nextState, toolStateCookie)) {
|
|
35111
36529
|
setToolStateCookie(nextState);
|
|
35112
36530
|
}
|
|
35113
|
-
}, [toolbarFragmentRoute, toolStateCookie]);
|
|
36531
|
+
}, [toolbarFragmentRoute, toolStateCookie, routableTool]);
|
|
35114
36532
|
React.useEffect(() => {
|
|
35115
|
-
|
|
35116
|
-
|
|
36533
|
+
// Helpdesk conversations may belong to Picobot, so they wait for its gate
|
|
36534
|
+
// rather than flashing open in Helpdesk first.
|
|
36535
|
+
const mayRedirectToPicobot = (toolbarFragmentRoute === null || toolbarFragmentRoute === void 0 ? void 0 : toolbarFragmentRoute.tool) === Helpdesk.name.toLowerCase() && isConversationPath(toolbarFragmentRoute.path) && (isPicobotGateLoading || displayPicobotIcon);
|
|
36536
|
+
const tool = (toolbarFragmentRoute === null || toolbarFragmentRoute === void 0 ? void 0 : toolbarFragmentRoute.tool) && !mayRedirectToPicobot && routableTool(toolbarFragmentRoute.tool);
|
|
36537
|
+
if (tool && (currentContent !== tool || !open)) {
|
|
36538
|
+
setToolAndOpenDrawer(tool);
|
|
35117
36539
|
}
|
|
35118
|
-
}, [setToolAndOpenDrawer, toolbarFragmentRoute === null || toolbarFragmentRoute === void 0 ? void 0 : toolbarFragmentRoute.tool, currentContent, open]);
|
|
36540
|
+
}, [setToolAndOpenDrawer, toolbarFragmentRoute === null || toolbarFragmentRoute === void 0 ? void 0 : toolbarFragmentRoute.tool, toolbarFragmentRoute === null || toolbarFragmentRoute === void 0 ? void 0 : toolbarFragmentRoute.path, currentContent, open, routableTool, isPicobotGateLoading, displayPicobotIcon]);
|
|
36541
|
+
const openTool = React.useCallback((name, path, pendingValue) => {
|
|
36542
|
+
const tool = routableTool(name.toLowerCase());
|
|
36543
|
+
if (!tool) return;
|
|
36544
|
+
dispatch({
|
|
36545
|
+
payload: pendingValue === undefined ? null : {
|
|
36546
|
+
tool: tool.name,
|
|
36547
|
+
value: pendingValue
|
|
36548
|
+
},
|
|
36549
|
+
type: ActionType.setPending
|
|
36550
|
+
});
|
|
36551
|
+
onChangeToolbarFragmentRoute(tool.name.toLowerCase(), path);
|
|
36552
|
+
scrollToolDrawerToTop(tool.name);
|
|
36553
|
+
}, [onChangeToolbarFragmentRoute, routableTool]);
|
|
36554
|
+
const clearPending = React.useCallback(() => dispatch({
|
|
36555
|
+
type: ActionType.clearPending
|
|
36556
|
+
}), []);
|
|
35119
36557
|
const toggleDrawer = content => {
|
|
35120
36558
|
if (!open) {
|
|
35121
36559
|
return openDrawer(content);
|
|
@@ -35132,12 +36570,52 @@ true;
|
|
|
35132
36570
|
joltPromise: storeData === null || storeData === void 0 ? void 0 : storeData.joltPromise,
|
|
35133
36571
|
userId
|
|
35134
36572
|
});
|
|
35135
|
-
|
|
36573
|
+
// Picobot conversations moved from Helpdesk to the Picobot panel, so
|
|
36574
|
+
// send old links and saved paths there once the panel is enabled.
|
|
36575
|
+
React.useEffect(() => {
|
|
36576
|
+
if (!displayPicobotIcon) return;
|
|
36577
|
+
if ((toolbarFragmentRoute === null || toolbarFragmentRoute === void 0 ? void 0 : toolbarFragmentRoute.tool) === Helpdesk.name.toLowerCase() && isConversationPath(toolbarFragmentRoute.path)) {
|
|
36578
|
+
onChangeToolbarFragmentRoute(Picobot.name.toLowerCase(), toolbarFragmentRoute.path);
|
|
36579
|
+
}
|
|
36580
|
+
}, [displayPicobotIcon, toolbarFragmentRoute, onChangeToolbarFragmentRoute]);
|
|
36581
|
+
React.useEffect(() => {
|
|
36582
|
+
if (!displayPicobotIcon) return;
|
|
36583
|
+
const {
|
|
36584
|
+
helpdesk,
|
|
36585
|
+
picobot
|
|
36586
|
+
} = toolStateCookie;
|
|
36587
|
+
if (!isConversationPath(helpdesk)) return;
|
|
36588
|
+
setToolStateCookie({
|
|
36589
|
+
...toolStateCookie,
|
|
36590
|
+
helpdesk: "",
|
|
36591
|
+
picobot: picobot || helpdesk
|
|
36592
|
+
});
|
|
36593
|
+
}, [displayPicobotIcon, toolStateCookie]);
|
|
36594
|
+
const openPicobot = React.useCallback(options => {
|
|
36595
|
+
var _a;
|
|
36596
|
+
// Moving an open Picobot off its path reloads or navigates the iframe,
|
|
36597
|
+
// which would race the options it's being handed.
|
|
36598
|
+
const path = currentContent === Picobot ? (_a = toolbarFragmentRoute === null || toolbarFragmentRoute === void 0 ? void 0 : toolbarFragmentRoute.path) !== null && _a !== void 0 ? _a : "" : "";
|
|
36599
|
+
openTool(Picobot.name, path, options);
|
|
36600
|
+
}, [openTool, currentContent, toolbarFragmentRoute === null || toolbarFragmentRoute === void 0 ? void 0 : toolbarFragmentRoute.path]);
|
|
36601
|
+
React.useEffect(() => {
|
|
36602
|
+
if (!displayPicobotIcon) return undefined;
|
|
36603
|
+
window.topbar = window.topbar || {};
|
|
36604
|
+
window.topbar.openPicobot = openPicobot;
|
|
36605
|
+
return () => {
|
|
36606
|
+
delete window.topbar.openPicobot;
|
|
36607
|
+
};
|
|
36608
|
+
}, [displayPicobotIcon, openPicobot]);
|
|
36609
|
+
const TOOLS = [Notifications, Tasks, displayChatIcon && Chat, displayPicobotIcon && Picobot, isPcoTestHost() && DeveloperTools].filter(Boolean);
|
|
35136
36610
|
const handleToolOnClick = tool => {
|
|
35137
36611
|
toggleDrawer(tool);
|
|
35138
36612
|
scrollToolDrawerToTop(tool.name);
|
|
35139
36613
|
};
|
|
35140
36614
|
const toolSpecificProps = {
|
|
36615
|
+
Helpdesk: {
|
|
36616
|
+
isPicobotLoading: isPicobotGateLoading,
|
|
36617
|
+
openPicobot: displayPicobotIcon ? openPicobot : undefined
|
|
36618
|
+
},
|
|
35141
36619
|
Profile: {
|
|
35142
36620
|
appName,
|
|
35143
36621
|
closeDrawer,
|
|
@@ -35160,6 +36638,7 @@ true;
|
|
|
35160
36638
|
const routeIsForCurrentTool = (toolbarFragmentRoute === null || toolbarFragmentRoute === void 0 ? void 0 : toolbarFragmentRoute.tool) && toolsRouteMap[toolbarFragmentRoute.tool] === currentContent;
|
|
35161
36639
|
const currentToolProps = {
|
|
35162
36640
|
appName,
|
|
36641
|
+
clearPending,
|
|
35163
36642
|
closeDrawer,
|
|
35164
36643
|
currentPath: routeIsForCurrentTool ? toolbarFragmentRoute === null || toolbarFragmentRoute === void 0 ? void 0 : toolbarFragmentRoute.path : null,
|
|
35165
36644
|
env,
|
|
@@ -35168,6 +36647,8 @@ true;
|
|
|
35168
36647
|
joltPromise,
|
|
35169
36648
|
onChangePath,
|
|
35170
36649
|
openDrawer: () => currentContent && openDrawer(currentContent),
|
|
36650
|
+
openTool,
|
|
36651
|
+
pending: (pending === null || pending === void 0 ? void 0 : pending.tool) === (currentContent === null || currentContent === void 0 ? void 0 : currentContent.name) ? pending === null || pending === void 0 ? void 0 : pending.value : undefined,
|
|
35171
36652
|
userId,
|
|
35172
36653
|
...(currentContent ? toolSpecificProps[currentContent.name] : {})
|
|
35173
36654
|
};
|
|
@@ -35178,38 +36659,37 @@ true;
|
|
|
35178
36659
|
return /*#__PURE__*/React.createElement(tapestryReact.ThemeProvider, {
|
|
35179
36660
|
theme: topbarTheme
|
|
35180
36661
|
}, /*#__PURE__*/React.createElement(addOns.UiExtensionDataContext, null, /*#__PURE__*/React.createElement("div", {
|
|
35181
|
-
className: styles$
|
|
36662
|
+
className: styles$2["toolbar"],
|
|
35182
36663
|
"data-pendo": "toolbar-profile-support-wrapper"
|
|
35183
36664
|
}, /*#__PURE__*/React.createElement(ToolbarButton, {
|
|
35184
36665
|
"aria-label": `Open ${userName}’s profile drawer`,
|
|
35185
36666
|
onClick: () => toggleDrawer(Profile),
|
|
35186
36667
|
"data-pendo": "toolbar-profile",
|
|
35187
|
-
className: styles$
|
|
36668
|
+
className: styles$2["toolbar-button--avatar"]
|
|
35188
36669
|
}, /*#__PURE__*/React.createElement(Avatar, {
|
|
35189
36670
|
env: env,
|
|
35190
36671
|
url: userAvatarPath
|
|
35191
36672
|
})), /*#__PURE__*/React.createElement(ToolbarButton, {
|
|
35192
|
-
className: styles$
|
|
36673
|
+
className: styles$2["toolbar-button--home"],
|
|
35193
36674
|
onClick: () => closeDrawer()
|
|
35194
36675
|
}, /*#__PURE__*/React.createElement(tapestryReact.Icon, {
|
|
35195
36676
|
name: "toolbar.planningCenter",
|
|
35196
36677
|
size: "24px"
|
|
35197
36678
|
})), [HelpDeskTool, ...TOOLS].map(tool => {
|
|
36679
|
+
var _a;
|
|
35198
36680
|
const current = open && currentContent === tool;
|
|
35199
36681
|
const indicator = indicatorState[tool.name];
|
|
35200
36682
|
return /*#__PURE__*/React.createElement(ToolbarButton, {
|
|
35201
36683
|
current: current,
|
|
35202
36684
|
onClick: () => handleToolOnClick(tool),
|
|
35203
36685
|
id: getDataPendoAttribute(tool.name),
|
|
35204
|
-
"aria-label": `Open ${tool.name} drawer`,
|
|
36686
|
+
"aria-label": `Open ${(_a = tool.label) !== null && _a !== void 0 ? _a : tool.name} drawer`,
|
|
35205
36687
|
"data-pendo": getDataPendoAttribute(tool.name),
|
|
35206
36688
|
key: tool.name
|
|
35207
|
-
},
|
|
35208
|
-
|
|
35209
|
-
|
|
35210
|
-
|
|
35211
|
-
dot: true
|
|
35212
|
-
})));
|
|
36689
|
+
}, /*#__PURE__*/React.createElement(ToolbarToolIcon, {
|
|
36690
|
+
icons: tool.icons,
|
|
36691
|
+
dot: !!indicator
|
|
36692
|
+
}));
|
|
35213
36693
|
})), /*#__PURE__*/React.createElement(DrawerWrap, {
|
|
35214
36694
|
isVisible: open
|
|
35215
36695
|
}, mounted && (/*#__PURE__*/React.createElement(Drawer, {
|
|
@@ -35227,12 +36707,25 @@ true;
|
|
|
35227
36707
|
}) => {
|
|
35228
36708
|
return /*#__PURE__*/React.createElement("button", {
|
|
35229
36709
|
type: "button",
|
|
35230
|
-
className: classnames(styles$
|
|
36710
|
+
className: classnames(styles$2["toolbar-button"], className, current && styles$2["is-current"]),
|
|
35231
36711
|
onClick: onClick,
|
|
35232
36712
|
"data-toolbar-link": "true",
|
|
35233
36713
|
...restProps
|
|
35234
36714
|
}, children);
|
|
35235
36715
|
};
|
|
36716
|
+
function ToolbarToolIcon({
|
|
36717
|
+
dot,
|
|
36718
|
+
icons
|
|
36719
|
+
}) {
|
|
36720
|
+
if ("component" in icons) {
|
|
36721
|
+
return /*#__PURE__*/React.createElement(icons.component, null);
|
|
36722
|
+
} else {
|
|
36723
|
+
return /*#__PURE__*/React.createElement(ToolbarButtonIcon, {
|
|
36724
|
+
path: dot ? icons.staticDotPath : icons.staticPath,
|
|
36725
|
+
dot: dot
|
|
36726
|
+
});
|
|
36727
|
+
}
|
|
36728
|
+
}
|
|
35236
36729
|
function ToolbarButtonIcon({
|
|
35237
36730
|
dot,
|
|
35238
36731
|
path
|