@the-portland-company/shell 0.99.1 → 0.100.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/CHANGELOG.md +9 -0
- package/dist/app-chrome.cjs +78 -8
- package/dist/app-chrome.cjs.map +1 -1
- package/dist/app-chrome.d.cts +1 -1
- package/dist/app-chrome.d.ts +1 -1
- package/dist/app-chrome.js +2 -2
- package/dist/{chunk-YMCWLO2L.js → chunk-2JH5AWUG.js} +74 -4
- package/dist/chunk-2JH5AWUG.js.map +1 -0
- package/dist/{index-Di9AyVsU.d.ts → index-2RNBy1XE.d.ts} +16 -1
- package/dist/{index-DhrhpBVm.d.cts → index-B57_u6pS.d.cts} +16 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/native.cjs +73 -0
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +1 -1
- package/dist/native.d.ts +1 -1
- package/dist/native.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-YMCWLO2L.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## 0.100.0
|
|
2
|
+
|
|
3
|
+
New pages open at the top (Ben 2026-11-08, clicked "Save as written" and "Go, all Modes" in the scroll-to-top session box; Reference/UI-Platform → URL state → "Scroll position"). Ben: "when clicking from one tab to another … the next page starts out slightly scrolled down. I need every new page to start fully scrolled at the top."
|
|
4
|
+
|
|
5
|
+
- **Cause.** Next's App Router only scrolls when the new page's top edge is off screen, so a page scrolled down a little kept that offset. And most pages and tabs switch by query parameter (`?page=…&tab=…`) on the same path, which nothing reset.
|
|
6
|
+
- **Fix.** `NavAppShell` (so every app on `PolitogyAppFrame`) now scrolls the window to the top, instantly, when the URL moves to a new page: a different path, or a different `mode`, `page` or `tab`. Changing only `section`, `org_id` or a feature parameter (filters, pagination) keeps the place; filling a missing default is not a new page; Back / Forward keep the browser's own position; a `#anchor` URL lands on its anchor. Uses the Navigation API where present, with a `popstate` + render-time check as the fallback, and survives apps that remount their chrome on each page (Tribes, Surveys).
|
|
7
|
+
- New exports on `./native`: `useScrollToTopOnNewPage`, `isNewPage`, `pageIdentityOf`, type `PageIdentity`.
|
|
8
|
+
- Tests: `__tests__/scrollToTop.test.tsx` (new).
|
|
9
|
+
|
|
1
10
|
## 0.99.1
|
|
2
11
|
|
|
3
12
|
The user card says "Account Manager" for an Account Manager (bug seen live 2026-11-07: benedtl@hotmail.com, `account_manager` in "TEST Campaign A (link test)", a Linked Accounts roster membership, was shown as "Member").
|
package/dist/app-chrome.cjs
CHANGED
|
@@ -422,6 +422,75 @@ function SpeculationRulesScript({ items }) {
|
|
|
422
422
|
}, [items, origin]);
|
|
423
423
|
return null;
|
|
424
424
|
}
|
|
425
|
+
var PAGE_KEYS = ["mode", "page", "tab"];
|
|
426
|
+
function pageIdentityOf(pathname, search) {
|
|
427
|
+
const params = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
|
|
428
|
+
const path = pathname.length > 1 ? pathname.replace(/\/+$/, "") : pathname;
|
|
429
|
+
return {
|
|
430
|
+
pathname: path || "/",
|
|
431
|
+
mode: params.get("mode") || null,
|
|
432
|
+
page: params.get("page") || null,
|
|
433
|
+
tab: params.get("tab") || null
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
function isNewPage(prev, next) {
|
|
437
|
+
if (prev.pathname !== next.pathname) return true;
|
|
438
|
+
return PAGE_KEYS.some((k) => prev[k] !== null && next[k] !== prev[k]);
|
|
439
|
+
}
|
|
440
|
+
function scrollWindowToTop() {
|
|
441
|
+
if (window.scrollX === 0 && window.scrollY === 0) return;
|
|
442
|
+
try {
|
|
443
|
+
window.scrollTo({ top: 0, left: 0, behavior: "instant" });
|
|
444
|
+
} catch {
|
|
445
|
+
window.scrollTo(0, 0);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
var lastIdentity = null;
|
|
449
|
+
var listeners = 0;
|
|
450
|
+
var detach = null;
|
|
451
|
+
function currentIdentity() {
|
|
452
|
+
return pageIdentityOf(window.location.pathname, window.location.search);
|
|
453
|
+
}
|
|
454
|
+
function check(traverse = false) {
|
|
455
|
+
const next = currentIdentity();
|
|
456
|
+
const prev = lastIdentity;
|
|
457
|
+
lastIdentity = next;
|
|
458
|
+
if (!prev || traverse) return;
|
|
459
|
+
if (!isNewPage(prev, next)) return;
|
|
460
|
+
if (window.location.hash) return;
|
|
461
|
+
scrollWindowToTop();
|
|
462
|
+
}
|
|
463
|
+
function attach() {
|
|
464
|
+
const nav = window.navigation;
|
|
465
|
+
if (nav && typeof nav.addEventListener === "function") {
|
|
466
|
+
const onEntryChange = (e) => {
|
|
467
|
+
check(e.navigationType === "traverse");
|
|
468
|
+
};
|
|
469
|
+
nav.addEventListener("currententrychange", onEntryChange);
|
|
470
|
+
return () => nav.removeEventListener("currententrychange", onEntryChange);
|
|
471
|
+
}
|
|
472
|
+
const onPopState = () => check(true);
|
|
473
|
+
window.addEventListener("popstate", onPopState);
|
|
474
|
+
return () => window.removeEventListener("popstate", onPopState);
|
|
475
|
+
}
|
|
476
|
+
var useIsoLayoutEffect = typeof window === "undefined" ? react.useEffect : react.useLayoutEffect;
|
|
477
|
+
function useScrollToTopOnNewPage() {
|
|
478
|
+
react.useEffect(() => {
|
|
479
|
+
if (listeners === 0) detach = attach();
|
|
480
|
+
listeners += 1;
|
|
481
|
+
return () => {
|
|
482
|
+
listeners -= 1;
|
|
483
|
+
if (listeners === 0) {
|
|
484
|
+
detach?.();
|
|
485
|
+
detach = null;
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
}, []);
|
|
489
|
+
useIsoLayoutEffect(() => {
|
|
490
|
+
if (lastIdentity) check();
|
|
491
|
+
else lastIdentity = currentIdentity();
|
|
492
|
+
});
|
|
493
|
+
}
|
|
425
494
|
|
|
426
495
|
// src/native/credits.ts
|
|
427
496
|
var CREDIT_TYPE_ORDER = [
|
|
@@ -3463,6 +3532,7 @@ function NavAppShell({
|
|
|
3463
3532
|
children
|
|
3464
3533
|
}) {
|
|
3465
3534
|
useCloseNavDetailsOnOutsideClick();
|
|
3535
|
+
useScrollToTopOnNewPage();
|
|
3466
3536
|
if (contentWrapper === "bare") {
|
|
3467
3537
|
warnDeprecated(
|
|
3468
3538
|
"contentWrapper",
|
|
@@ -3673,7 +3743,7 @@ function watchLogout(onLogout, options = {}) {
|
|
|
3673
3743
|
cleanup();
|
|
3674
3744
|
onLogout();
|
|
3675
3745
|
};
|
|
3676
|
-
const
|
|
3746
|
+
const check2 = () => {
|
|
3677
3747
|
const at = readLogoutCookie();
|
|
3678
3748
|
if (at !== null) fire(at);
|
|
3679
3749
|
};
|
|
@@ -3687,15 +3757,15 @@ function watchLogout(onLogout, options = {}) {
|
|
|
3687
3757
|
} catch {
|
|
3688
3758
|
channel = null;
|
|
3689
3759
|
}
|
|
3690
|
-
const timer = window.setInterval(
|
|
3691
|
-
window.addEventListener("visibilitychange",
|
|
3692
|
-
window.addEventListener("focus",
|
|
3693
|
-
window.addEventListener("pageshow",
|
|
3760
|
+
const timer = window.setInterval(check2, intervalMs);
|
|
3761
|
+
window.addEventListener("visibilitychange", check2);
|
|
3762
|
+
window.addEventListener("focus", check2);
|
|
3763
|
+
window.addEventListener("pageshow", check2);
|
|
3694
3764
|
function cleanup() {
|
|
3695
3765
|
window.clearInterval(timer);
|
|
3696
|
-
window.removeEventListener("visibilitychange",
|
|
3697
|
-
window.removeEventListener("focus",
|
|
3698
|
-
window.removeEventListener("pageshow",
|
|
3766
|
+
window.removeEventListener("visibilitychange", check2);
|
|
3767
|
+
window.removeEventListener("focus", check2);
|
|
3768
|
+
window.removeEventListener("pageshow", check2);
|
|
3699
3769
|
try {
|
|
3700
3770
|
channel?.close();
|
|
3701
3771
|
} catch {
|