@planningcenter/topbar 12.5.0-rc.3 → 12.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/topbar.js +69 -18
- package/dist/topbar.js.map +1 -1
- package/dist/topbar.modern.js +69 -18
- package/dist/topbar.modern.js.map +1 -1
- package/dist/topbar.module.js +69 -18
- package/dist/topbar.module.js.map +1 -1
- package/dist/topbar.umd.js +69 -18
- package/dist/topbar.umd.js.map +1 -1
- package/package.json +3 -4
package/dist/topbar.js
CHANGED
|
@@ -3362,6 +3362,25 @@ function SearchButton({
|
|
|
3362
3362
|
}));
|
|
3363
3363
|
}
|
|
3364
3364
|
|
|
3365
|
+
// With the search's own debounce, about 1.4 seconds after the last keystroke,
|
|
3366
|
+
// as in GOV.UK's accessible-autocomplete.
|
|
3367
|
+
const ANNOUNCEMENT_DELAY_MS = 1100;
|
|
3368
|
+
// Screen readers echo typing themselves, as each person has set them to, so
|
|
3369
|
+
// the message waits until typing settles instead of talking over it. Waiting
|
|
3370
|
+
// also lets VoiceOver finish what focus had to say, since it often skips a
|
|
3371
|
+
// change made as focus moves, as when a tab with cached results is selected,
|
|
3372
|
+
// and only the tab someone stops on when arrowing through them is announced.
|
|
3373
|
+
// The region empties at once, so a message that comes back is read again.
|
|
3374
|
+
function useAnnouncement(message) {
|
|
3375
|
+
const [announcement, setAnnouncement] = React.useState(null);
|
|
3376
|
+
React.useEffect(() => {
|
|
3377
|
+
setAnnouncement(null);
|
|
3378
|
+
const timeout = setTimeout(() => setAnnouncement(message), ANNOUNCEMENT_DELAY_MS);
|
|
3379
|
+
return () => clearTimeout(timeout);
|
|
3380
|
+
}, [message]);
|
|
3381
|
+
return announcement;
|
|
3382
|
+
}
|
|
3383
|
+
|
|
3365
3384
|
// useId isn't available on React 17, which our peer range still supports.
|
|
3366
3385
|
let instanceCount$1 = 0;
|
|
3367
3386
|
function useInstanceId(prefix) {
|
|
@@ -4111,7 +4130,18 @@ function subject(token) {
|
|
|
4111
4130
|
return undefined;
|
|
4112
4131
|
}
|
|
4113
4132
|
}
|
|
4114
|
-
|
|
4133
|
+
const minting = new Map();
|
|
4134
|
+
function mintToken(personId) {
|
|
4135
|
+
const running = minting.get(personId);
|
|
4136
|
+
if (running) {
|
|
4137
|
+
return running;
|
|
4138
|
+
} else {
|
|
4139
|
+
const run = mint(personId).finally(() => minting.delete(personId));
|
|
4140
|
+
minting.set(personId, run);
|
|
4141
|
+
return run;
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
async function mint(personId) {
|
|
4115
4145
|
const {
|
|
4116
4146
|
json,
|
|
4117
4147
|
ok,
|
|
@@ -4135,6 +4165,10 @@ async function mintToken(personId) {
|
|
|
4135
4165
|
}
|
|
4136
4166
|
return token;
|
|
4137
4167
|
}
|
|
4168
|
+
function currentToken(personId) {
|
|
4169
|
+
var _a;
|
|
4170
|
+
return (_a = cachedToken(personId)) !== null && _a !== void 0 ? _a : mintToken(personId).catch(() => null);
|
|
4171
|
+
}
|
|
4138
4172
|
async function request(personId, key, init = {}, isRetry = false) {
|
|
4139
4173
|
var _a;
|
|
4140
4174
|
const token = isRetry ? await mintToken(personId) : (_a = cachedToken(personId)) !== null && _a !== void 0 ? _a : await mintToken(personId);
|
|
@@ -4170,8 +4204,8 @@ function ifMatch(version) {
|
|
|
4170
4204
|
// retrying when another tab or device wrote in between. `change` returns null
|
|
4171
4205
|
// when there's nothing to write. A read-only token (staff impersonating
|
|
4172
4206
|
// someone) only reads, so nothing is written into the person's history.
|
|
4173
|
-
// Resolves to the value now stored,
|
|
4174
|
-
// reached.
|
|
4207
|
+
// Resolves to the value now stored, with whether the token was read-only, or
|
|
4208
|
+
// null when the service couldn't be reached.
|
|
4175
4209
|
async function updateStoredValue(personId, key, change, attemptsLeft = MAX_ATTEMPTS) {
|
|
4176
4210
|
const read = await request(personId, key);
|
|
4177
4211
|
if (read === null || read === void 0 ? void 0 : read.response.ok) {
|
|
@@ -4179,9 +4213,15 @@ async function updateStoredValue(personId, key, change, attemptsLeft = MAX_ATTEM
|
|
|
4179
4213
|
value,
|
|
4180
4214
|
version
|
|
4181
4215
|
} = await read.response.json();
|
|
4182
|
-
const
|
|
4216
|
+
const {
|
|
4217
|
+
readOnly
|
|
4218
|
+
} = read.token;
|
|
4219
|
+
const next = readOnly ? null : change(value);
|
|
4183
4220
|
if (next === null) {
|
|
4184
|
-
return
|
|
4221
|
+
return {
|
|
4222
|
+
readOnly,
|
|
4223
|
+
value: Array.isArray(value) ? value : []
|
|
4224
|
+
};
|
|
4185
4225
|
} else {
|
|
4186
4226
|
const write = await request(personId, key, {
|
|
4187
4227
|
body: JSON.stringify({
|
|
@@ -4194,7 +4234,10 @@ async function updateStoredValue(personId, key, change, attemptsLeft = MAX_ATTEM
|
|
|
4194
4234
|
method: "PUT"
|
|
4195
4235
|
});
|
|
4196
4236
|
if (write === null || write === void 0 ? void 0 : write.response.ok) {
|
|
4197
|
-
return
|
|
4237
|
+
return {
|
|
4238
|
+
readOnly,
|
|
4239
|
+
value: next
|
|
4240
|
+
};
|
|
4198
4241
|
} else if ((write === null || write === void 0 ? void 0 : write.response.status) === 409 && attemptsLeft > 1) {
|
|
4199
4242
|
return updateStoredValue(personId, key, change, attemptsLeft - 1);
|
|
4200
4243
|
} else {
|
|
@@ -4246,7 +4289,8 @@ async function syncOnce(scope) {
|
|
|
4246
4289
|
return isSameList(mergeEntries(shared, local), shared) ? null : mergeStored(stored, local);
|
|
4247
4290
|
});
|
|
4248
4291
|
if (synced) {
|
|
4249
|
-
|
|
4292
|
+
const shared = entriesFrom(synced.value);
|
|
4293
|
+
writeRecentlyVisited(scope, synced.readOnly ? shared : mergeEntries(shared, readRecentlyVisited(scope)));
|
|
4250
4294
|
}
|
|
4251
4295
|
} catch {
|
|
4252
4296
|
// The service is best-effort; the local copy stands on its own.
|
|
@@ -4277,11 +4321,23 @@ function sync(scope, followUp) {
|
|
|
4277
4321
|
function syncRecentlyVisited(scope) {
|
|
4278
4322
|
return sync(scope, false);
|
|
4279
4323
|
}
|
|
4324
|
+
function record(entry, scope, token) {
|
|
4325
|
+
if (!(token === null || token === void 0 ? void 0 : token.readOnly)) {
|
|
4326
|
+
writeRecentlyVisited(scope, addVisit(readRecentlyVisited(scope), entry));
|
|
4327
|
+
if (token) sync(scope, true);
|
|
4328
|
+
}
|
|
4329
|
+
}
|
|
4280
4330
|
function recordVisit(visit, scope) {
|
|
4281
4331
|
const entry = toEntry(visit, new Date());
|
|
4282
|
-
if (isEntry(entry)) {
|
|
4283
|
-
|
|
4284
|
-
|
|
4332
|
+
if (!isEntry(entry)) {
|
|
4333
|
+
return Promise.resolve();
|
|
4334
|
+
}
|
|
4335
|
+
const token = isUserStoreSyncUnavailable() ? null : currentToken(scope.personId);
|
|
4336
|
+
if (token instanceof Promise) {
|
|
4337
|
+
return token.then(minted => record(entry, scope, minted));
|
|
4338
|
+
} else {
|
|
4339
|
+
record(entry, scope, token);
|
|
4340
|
+
return Promise.resolve();
|
|
4285
4341
|
}
|
|
4286
4342
|
}
|
|
4287
4343
|
function useRecentlyVisited({
|
|
@@ -4962,7 +5018,6 @@ function ProductTabs({
|
|
|
4962
5018
|
role: "tablist"
|
|
4963
5019
|
}, products.map(product => (/*#__PURE__*/React.createElement(Tab, {
|
|
4964
5020
|
"aria-controls": `${id}-panel`,
|
|
4965
|
-
"aria-describedby": `${tabId(product.slug)}-description`,
|
|
4966
5021
|
"aria-selected": product === selected,
|
|
4967
5022
|
id: tabId(product.slug),
|
|
4968
5023
|
key: product.slug,
|
|
@@ -4980,11 +5035,6 @@ function ProductTabs({
|
|
|
4980
5035
|
}), showAllNames || product === selected ? product.appName : (/*#__PURE__*/React.createElement("span", {
|
|
4981
5036
|
className: styles$k.visuallyHidden
|
|
4982
5037
|
}, product.appName)))))), /*#__PURE__*/React.createElement("div", {
|
|
4983
|
-
className: styles$k.visuallyHidden
|
|
4984
|
-
}, products.map(product => (/*#__PURE__*/React.createElement("span", {
|
|
4985
|
-
id: `${tabId(product.slug)}-description`,
|
|
4986
|
-
key: product.slug
|
|
4987
|
-
}, "Planning Center ", product.appName)))), /*#__PURE__*/React.createElement("div", {
|
|
4988
5038
|
"aria-labelledby": tabId(selected.slug),
|
|
4989
5039
|
className: styles$k.panel,
|
|
4990
5040
|
id: `${id}-panel`,
|
|
@@ -6079,9 +6129,9 @@ function SearchResults({
|
|
|
6079
6129
|
});
|
|
6080
6130
|
case "done":
|
|
6081
6131
|
if (sections.length === 0) {
|
|
6132
|
+
// The panel's result count announces this, so it isn't read twice.
|
|
6082
6133
|
return /*#__PURE__*/React.createElement(BlankState$1, {
|
|
6083
6134
|
image: /*#__PURE__*/React.createElement(BlankStateNoResults, null),
|
|
6084
|
-
role: "status",
|
|
6085
6135
|
subtitle: "Clear search and try again.",
|
|
6086
6136
|
title: `No results for “${term.trim()}”`
|
|
6087
6137
|
});
|
|
@@ -6135,6 +6185,7 @@ function SearchPanel({
|
|
|
6135
6185
|
highlightedIndex
|
|
6136
6186
|
} = navigation;
|
|
6137
6187
|
const inputRef = React.useRef(null);
|
|
6188
|
+
const resultCount = useAnnouncement(hasTerm && search.status === "done" ? `${items.length === 0 ? "No results" : `${items.length} ${items.length === 1 ? "result" : "results"}`} for “${term.trim()}” in ${product.appName}` : null);
|
|
6138
6189
|
const [announceHighlight, setAnnounceHighlight] = React.useState(false);
|
|
6139
6190
|
// The tabs keep focus while arrowing through results, but only the focused
|
|
6140
6191
|
// input can point screen readers at the highlight, so from the tabs it's
|
|
@@ -6193,7 +6244,7 @@ function SearchPanel({
|
|
|
6193
6244
|
"aria-live": "polite",
|
|
6194
6245
|
className: styles$k.visuallyHidden,
|
|
6195
6246
|
"data-testid": "result-count"
|
|
6196
|
-
},
|
|
6247
|
+
}, resultCount), mobile ? null : /*#__PURE__*/React.createElement(KeyboardHints, {
|
|
6197
6248
|
copied: navigation.copied
|
|
6198
6249
|
}));
|
|
6199
6250
|
}
|