@sesionario/blocks 1.40.0 → 1.40.1-rc.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.
|
@@ -25,8 +25,59 @@ var NAV_PROGRESS_CSS = `
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
`;
|
|
28
|
+
var startListeners = /* @__PURE__ */ new Set();
|
|
29
|
+
var navigationSuppressed = false;
|
|
30
|
+
var suppressTimer = 0;
|
|
31
|
+
function normalizeSearch(search) {
|
|
32
|
+
const params = new URLSearchParams(search);
|
|
33
|
+
const entries = [...params.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
34
|
+
const sorted = new URLSearchParams();
|
|
35
|
+
for (const [key, value] of entries) {
|
|
36
|
+
sorted.append(key, value);
|
|
37
|
+
}
|
|
38
|
+
return sorted.toString();
|
|
39
|
+
}
|
|
28
40
|
function routeKey(pathname, search) {
|
|
29
|
-
return `${pathname}?${search}`;
|
|
41
|
+
return `${pathname}?${normalizeSearch(search)}`;
|
|
42
|
+
}
|
|
43
|
+
function routeKeyFromHref(href) {
|
|
44
|
+
try {
|
|
45
|
+
const url = new URL(href, window.location.origin);
|
|
46
|
+
return routeKey(url.pathname, url.search.replace(/^\?/, ""));
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function isShown(element) {
|
|
52
|
+
if (!(element instanceof HTMLElement)) return false;
|
|
53
|
+
if (element.hidden) return false;
|
|
54
|
+
const display = element.style.display || getComputedStyle(element).display;
|
|
55
|
+
if (display === "none") return false;
|
|
56
|
+
const visibility = element.style.visibility || getComputedStyle(element).visibility;
|
|
57
|
+
if (visibility === "hidden") return false;
|
|
58
|
+
if (display === "contents") {
|
|
59
|
+
return Array.from(element.children).some((child) => isShown(child));
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
function routeLoadingIsActive() {
|
|
64
|
+
for (const node of document.querySelectorAll(ROUTE_LOADING_SELECTOR)) {
|
|
65
|
+
if (isShown(node)) return true;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
function suppressNavigationProgress() {
|
|
70
|
+
navigationSuppressed = true;
|
|
71
|
+
window.clearTimeout(suppressTimer);
|
|
72
|
+
suppressTimer = window.setTimeout(() => {
|
|
73
|
+
navigationSuppressed = false;
|
|
74
|
+
}, 0);
|
|
75
|
+
}
|
|
76
|
+
function signalNavigationStart(href) {
|
|
77
|
+
if (navigationSuppressed) return;
|
|
78
|
+
for (const listener of startListeners) {
|
|
79
|
+
listener(href);
|
|
80
|
+
}
|
|
30
81
|
}
|
|
31
82
|
function isInternalNavigationClick(event) {
|
|
32
83
|
if (event.button !== 0) return false;
|
|
@@ -61,6 +112,7 @@ function NavigationProgress({
|
|
|
61
112
|
forceVisible = false
|
|
62
113
|
}) {
|
|
63
114
|
const [visible, setVisible] = react.useState(forceVisible);
|
|
115
|
+
const [runId, setRunId] = react.useState(0);
|
|
64
116
|
const startedAt = react.useRef(0);
|
|
65
117
|
const startedRoute = react.useRef(null);
|
|
66
118
|
const committedRoute = react.useRef(null);
|
|
@@ -70,38 +122,60 @@ function NavigationProgress({
|
|
|
70
122
|
react.useEffect(() => {
|
|
71
123
|
if (forceVisible) return;
|
|
72
124
|
function begin(route) {
|
|
125
|
+
if (navigationSuppressed) return;
|
|
73
126
|
startedAt.current = Date.now();
|
|
74
127
|
startedRoute.current = route;
|
|
128
|
+
setRunId((id) => id + 1);
|
|
75
129
|
setVisible(true);
|
|
76
130
|
}
|
|
77
131
|
function onClick(event) {
|
|
78
132
|
if (!isInternalNavigationClick(event)) return;
|
|
79
|
-
begin(
|
|
80
|
-
routeKey(
|
|
81
|
-
window.location.pathname,
|
|
82
|
-
window.location.search.replace(/^\?/, "")
|
|
83
|
-
)
|
|
84
|
-
);
|
|
133
|
+
begin(committedRoute.current ?? routeKey(pathname, search));
|
|
85
134
|
}
|
|
86
135
|
function onPopState() {
|
|
136
|
+
const landed = routeKey(
|
|
137
|
+
window.location.pathname,
|
|
138
|
+
window.location.search.replace(/^\?/, "")
|
|
139
|
+
);
|
|
140
|
+
if (landed === (committedRoute.current ?? "")) return;
|
|
87
141
|
begin(committedRoute.current ?? "");
|
|
88
142
|
}
|
|
143
|
+
function onStart(href) {
|
|
144
|
+
if (href) {
|
|
145
|
+
const target = routeKeyFromHref(href);
|
|
146
|
+
if (target !== null && target === committedRoute.current) return;
|
|
147
|
+
}
|
|
148
|
+
begin(committedRoute.current ?? routeKey(pathname, search));
|
|
149
|
+
}
|
|
150
|
+
startListeners.add(onStart);
|
|
89
151
|
document.addEventListener("click", onClick, true);
|
|
90
152
|
window.addEventListener("popstate", onPopState);
|
|
91
153
|
return () => {
|
|
154
|
+
startListeners.delete(onStart);
|
|
92
155
|
document.removeEventListener("click", onClick, true);
|
|
93
156
|
window.removeEventListener("popstate", onPopState);
|
|
94
157
|
};
|
|
95
|
-
}, [forceVisible]);
|
|
158
|
+
}, [forceVisible, pathname, search]);
|
|
159
|
+
react.useEffect(() => {
|
|
160
|
+
if (forceVisible || !visible) return;
|
|
161
|
+
const safety = window.setTimeout(() => setVisible(false), SAFETY_MS);
|
|
162
|
+
return () => window.clearTimeout(safety);
|
|
163
|
+
}, [forceVisible, runId, visible]);
|
|
96
164
|
react.useEffect(() => {
|
|
97
165
|
if (forceVisible || !visible) return;
|
|
98
166
|
const started = startedRoute.current;
|
|
99
|
-
if (!started
|
|
167
|
+
if (!started) return;
|
|
100
168
|
let hideTimer = 0;
|
|
101
169
|
let stopped = false;
|
|
170
|
+
let sawLoading = false;
|
|
102
171
|
function hide() {
|
|
103
172
|
if (stopped) return;
|
|
104
|
-
if (
|
|
173
|
+
if (routeLoadingIsActive()) {
|
|
174
|
+
sawLoading = true;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const routeChanged = routeKey(pathname, search) !== started;
|
|
178
|
+
if (!routeChanged && !sawLoading) return;
|
|
105
179
|
stopped = true;
|
|
106
180
|
const wait = Math.max(
|
|
107
181
|
0,
|
|
@@ -111,12 +185,15 @@ function NavigationProgress({
|
|
|
111
185
|
}
|
|
112
186
|
hide();
|
|
113
187
|
const observer = new MutationObserver(hide);
|
|
114
|
-
observer.observe(document.body, {
|
|
115
|
-
|
|
188
|
+
observer.observe(document.body, {
|
|
189
|
+
childList: true,
|
|
190
|
+
subtree: true,
|
|
191
|
+
attributes: true,
|
|
192
|
+
attributeFilter: ["style", "hidden", "class"]
|
|
193
|
+
});
|
|
116
194
|
return () => {
|
|
117
195
|
stopped = true;
|
|
118
196
|
window.clearTimeout(hideTimer);
|
|
119
|
-
window.clearTimeout(safety);
|
|
120
197
|
observer.disconnect();
|
|
121
198
|
};
|
|
122
199
|
}, [forceVisible, pathname, search, visible]);
|
|
@@ -136,5 +213,7 @@ function NavigationProgress({
|
|
|
136
213
|
}
|
|
137
214
|
|
|
138
215
|
exports.NavigationProgress = NavigationProgress;
|
|
216
|
+
exports.signalNavigationStart = signalNavigationStart;
|
|
217
|
+
exports.suppressNavigationProgress = suppressNavigationProgress;
|
|
139
218
|
//# sourceMappingURL=navigation-progress.cjs.map
|
|
140
219
|
//# sourceMappingURL=navigation-progress.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/NavigationProgress.tsx"],"names":["useState","useRef","useEffect","jsxs","Fragment","jsx"],"mappings":"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","file":"navigation-progress.cjs","sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\nconst MIN_VISIBLE_MS = 200;\nconst SAFETY_MS = 10_000;\n\nexport const ROUTE_LOADING_SELECTOR = \"[data-route-loading]\";\n\n/** Indeterminate bar motion. Inlined so apps do not copy keyframes into their CSS. */\nexport const NAV_PROGRESS_CSS = `\n@keyframes nav-progress-indeterminate {\n\t0% { transform: translateX(-100%); }\n\t100% { transform: translateX(400%); }\n}\n.nav-progress-indeterminate {\n\twidth: 33%;\n\theight: 100%;\n\tanimation: nav-progress-indeterminate 1.1s ease-in-out infinite;\n}\n@media (prefers-reduced-motion: reduce) {\n\t.nav-progress-indeterminate {\n\t\tanimation: none;\n\t\twidth: 100%;\n\t\ttransform: none;\n\t}\n}\n`;\n\nfunction routeKey(pathname: string, search: string): string {\n\treturn `${pathname}?${search}`;\n}\n\nexport function isInternalNavigationClick(event: MouseEvent): boolean {\n\tif (event.button !== 0) return false;\n\tif (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n\t\treturn false;\n\t}\n\tconst target = event.target;\n\tif (!(target instanceof Element)) return false;\n\tconst anchor = target.closest(\"a\");\n\tif (!(anchor instanceof HTMLAnchorElement)) return false;\n\tif (anchor.target && anchor.target !== \"_self\") return false;\n\tif (anchor.hasAttribute(\"download\")) return false;\n\tconst raw = anchor.getAttribute(\"href\");\n\tif (\n\t\t!raw ||\n\t\traw.startsWith(\"#\") ||\n\t\traw.startsWith(\"mailto:\") ||\n\t\traw.startsWith(\"tel:\")\n\t) {\n\t\treturn false;\n\t}\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(anchor.href, window.location.href);\n\t} catch {\n\t\treturn false;\n\t}\n\tif (url.origin !== window.location.origin) return false;\n\tif (\n\t\turl.pathname === window.location.pathname &&\n\t\turl.search === window.location.search\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nexport type NavigationProgressProps = {\n\t/** App-supplied pathname. Blocks does not import `next/navigation`. */\n\tpathname: string;\n\t/** Query string without the leading `?`. */\n\tsearch?: string;\n\t/** Storybook / tests. Skips click listeners and stays visible. */\n\tforceVisible?: boolean;\n};\n\n/**\n * Indeterminate bar fixed to the top of the viewport. Apps pass the current\n * route from their router and wrap `useSearchParams` in `<Suspense>`.\n * The bar starts on a same-origin link click or back/forward, and stays up\n * while a `[data-route-loading]` node (see `RouteLoading`) is mounted.\n */\nexport function NavigationProgress({\n\tpathname,\n\tsearch = \"\",\n\tforceVisible = false,\n}: NavigationProgressProps) {\n\tconst [visible, setVisible] = useState(forceVisible);\n\tconst startedAt = useRef(0);\n\tconst startedRoute = useRef<string | null>(null);\n\tconst committedRoute = useRef<string | null>(null);\n\n\tuseEffect(() => {\n\t\tcommittedRoute.current = routeKey(pathname, search);\n\t}, [pathname, search]);\n\n\tuseEffect(() => {\n\t\tif (forceVisible) return;\n\t\tfunction begin(route: string) {\n\t\t\tstartedAt.current = Date.now();\n\t\t\tstartedRoute.current = route;\n\t\t\tsetVisible(true);\n\t\t}\n\t\tfunction onClick(event: MouseEvent) {\n\t\t\tif (!isInternalNavigationClick(event)) return;\n\t\t\t// Capture runs before Next's <Link> handler, which may push history\n\t\t\t// before a bubble listener would see the click.\n\t\t\tbegin(\n\t\t\t\trouteKey(\n\t\t\t\t\twindow.location.pathname,\n\t\t\t\t\twindow.location.search.replace(/^\\?/, \"\"),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tfunction onPopState() {\n\t\t\t// The URL has already changed. Keep the route React last committed\n\t\t\t// so the bar can end once the destination commits.\n\t\t\tbegin(committedRoute.current ?? \"\");\n\t\t}\n\t\tdocument.addEventListener(\"click\", onClick, true);\n\t\twindow.addEventListener(\"popstate\", onPopState);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"click\", onClick, true);\n\t\t\twindow.removeEventListener(\"popstate\", onPopState);\n\t\t};\n\t}, [forceVisible]);\n\n\tuseEffect(() => {\n\t\tif (forceVisible || !visible) return;\n\t\tconst started = startedRoute.current;\n\t\tif (!started || routeKey(pathname, search) === started) return;\n\n\t\tlet hideTimer = 0;\n\t\tlet stopped = false;\n\t\tfunction hide() {\n\t\t\tif (stopped) return;\n\t\t\tif (document.querySelector(ROUTE_LOADING_SELECTOR)) return;\n\t\t\tstopped = true;\n\t\t\tconst wait = Math.max(\n\t\t\t\t0,\n\t\t\t\tMIN_VISIBLE_MS - (Date.now() - startedAt.current),\n\t\t\t);\n\t\t\thideTimer = window.setTimeout(() => setVisible(false), wait);\n\t\t}\n\t\thide();\n\t\tconst observer = new MutationObserver(hide);\n\t\tobserver.observe(document.body, { childList: true, subtree: true });\n\t\tconst safety = window.setTimeout(() => setVisible(false), SAFETY_MS);\n\t\treturn () => {\n\t\t\tstopped = true;\n\t\t\twindow.clearTimeout(hideTimer);\n\t\t\twindow.clearTimeout(safety);\n\t\t\tobserver.disconnect();\n\t\t};\n\t}, [forceVisible, pathname, search, visible]);\n\n\tif (!visible) return null;\n\n\treturn (\n\t\t<>\n\t\t\t<style dangerouslySetInnerHTML={{ __html: NAV_PROGRESS_CSS }} />\n\t\t\t<div\n\t\t\t\trole=\"progressbar\"\n\t\t\t\taria-label=\"Cargando\"\n\t\t\t\tclassName=\"pointer-events-none fixed inset-x-0 top-0 z-[60] h-0.5 overflow-hidden bg-primary/20\"\n\t\t\t>\n\t\t\t\t<div className=\"nav-progress-indeterminate bg-primary\" />\n\t\t\t</div>\n\t\t</>\n\t);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/NavigationProgress.tsx"],"names":["useState","useRef","useEffect","jsxs","Fragment","jsx"],"mappings":"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","file":"navigation-progress.cjs","sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\nconst MIN_VISIBLE_MS = 200;\nconst SAFETY_MS = 10_000;\n\nexport const ROUTE_LOADING_SELECTOR = \"[data-route-loading]\";\n\n/** Indeterminate bar motion. Inlined so apps do not copy keyframes into their CSS. */\nexport const NAV_PROGRESS_CSS = `\n@keyframes nav-progress-indeterminate {\n\t0% { transform: translateX(-100%); }\n\t100% { transform: translateX(400%); }\n}\n.nav-progress-indeterminate {\n\twidth: 33%;\n\theight: 100%;\n\tanimation: nav-progress-indeterminate 1.1s ease-in-out infinite;\n}\n@media (prefers-reduced-motion: reduce) {\n\t.nav-progress-indeterminate {\n\t\tanimation: none;\n\t\twidth: 100%;\n\t\ttransform: none;\n\t}\n}\n`;\n\ntype StartListener = (href: string | undefined) => void;\n\nconst startListeners = new Set<StartListener>();\nlet navigationSuppressed = false;\nlet suppressTimer = 0;\n\nfunction normalizeSearch(search: string): string {\n\tconst params = new URLSearchParams(search);\n\tconst entries = [...params.entries()].sort(([a], [b]) => a.localeCompare(b));\n\tconst sorted = new URLSearchParams();\n\tfor (const [key, value] of entries) {\n\t\tsorted.append(key, value);\n\t}\n\treturn sorted.toString();\n}\n\nfunction routeKey(pathname: string, search: string): string {\n\treturn `${pathname}?${normalizeSearch(search)}`;\n}\n\nfunction routeKeyFromHref(href: string): string | null {\n\ttry {\n\t\tconst url = new URL(href, window.location.origin);\n\t\treturn routeKey(url.pathname, url.search.replace(/^\\?/, \"\"));\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction isShown(element: Element): boolean {\n\tif (!(element instanceof HTMLElement)) return false;\n\tif (element.hidden) return false;\n\tconst display = element.style.display || getComputedStyle(element).display;\n\tif (display === \"none\") return false;\n\tconst visibility =\n\t\telement.style.visibility || getComputedStyle(element).visibility;\n\tif (visibility === \"hidden\") return false;\n\tif (display === \"contents\") {\n\t\treturn Array.from(element.children).some((child) => isShown(child));\n\t}\n\treturn true;\n}\n\n/** A route skeleton counts only while it is actually on screen. */\nexport function routeLoadingIsActive(): boolean {\n\tfor (const node of document.querySelectorAll(ROUTE_LOADING_SELECTOR)) {\n\t\tif (isShown(node)) return true;\n\t}\n\treturn false;\n}\n\n/**\n * Ignore navigation-start signals for the rest of this turn. Used by the\n * fullscreen post viewer, whose permalink `pushState` / `history.back()` is\n * not a page load. An in-flight bar is left alone.\n */\nexport function suppressNavigationProgress(): void {\n\tnavigationSuppressed = true;\n\twindow.clearTimeout(suppressTimer);\n\tsuppressTimer = window.setTimeout(() => {\n\t\tnavigationSuppressed = false;\n\t}, 0);\n}\n\n/**\n * Start the top bar for a navigation that did not come from a link click.\n * Records the route React has committed, not `window.location` (already the\n * destination on back/forward). No-ops when `href` is that same route, or\n * when {@link suppressNavigationProgress} is set.\n */\nexport function signalNavigationStart(href?: string): void {\n\tif (navigationSuppressed) return;\n\tfor (const listener of startListeners) {\n\t\tlistener(href);\n\t}\n}\n\nexport function isInternalNavigationClick(event: MouseEvent): boolean {\n\tif (event.button !== 0) return false;\n\tif (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n\t\treturn false;\n\t}\n\tconst target = event.target;\n\tif (!(target instanceof Element)) return false;\n\tconst anchor = target.closest(\"a\");\n\tif (!(anchor instanceof HTMLAnchorElement)) return false;\n\tif (anchor.target && anchor.target !== \"_self\") return false;\n\tif (anchor.hasAttribute(\"download\")) return false;\n\tconst raw = anchor.getAttribute(\"href\");\n\tif (\n\t\t!raw ||\n\t\traw.startsWith(\"#\") ||\n\t\traw.startsWith(\"mailto:\") ||\n\t\traw.startsWith(\"tel:\")\n\t) {\n\t\treturn false;\n\t}\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(anchor.href, window.location.href);\n\t} catch {\n\t\treturn false;\n\t}\n\tif (url.origin !== window.location.origin) return false;\n\tif (\n\t\turl.pathname === window.location.pathname &&\n\t\turl.search === window.location.search\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nexport type NavigationProgressProps = {\n\t/** App-supplied pathname. Blocks does not import `next/navigation`. */\n\tpathname: string;\n\t/** Query string without the leading `?`. */\n\tsearch?: string;\n\t/** Storybook / tests. Skips click listeners and stays visible. */\n\tforceVisible?: boolean;\n};\n\n/**\n * Indeterminate bar fixed to the top of the viewport. Apps pass the current\n * route from their router and wrap `useSearchParams` in `<Suspense>`.\n * The bar starts on a same-origin link click, back/forward, or\n * {@link signalNavigationStart}, and stays up while a visible\n * `[data-route-loading]` node (see `RouteLoading`) is on screen.\n */\nexport function NavigationProgress({\n\tpathname,\n\tsearch = \"\",\n\tforceVisible = false,\n}: NavigationProgressProps) {\n\tconst [visible, setVisible] = useState(forceVisible);\n\tconst [runId, setRunId] = useState(0);\n\tconst startedAt = useRef(0);\n\tconst startedRoute = useRef<string | null>(null);\n\tconst committedRoute = useRef<string | null>(null);\n\n\tuseEffect(() => {\n\t\tcommittedRoute.current = routeKey(pathname, search);\n\t}, [pathname, search]);\n\n\tuseEffect(() => {\n\t\tif (forceVisible) return;\n\t\tfunction begin(route: string) {\n\t\t\tif (navigationSuppressed) return;\n\t\t\tstartedAt.current = Date.now();\n\t\t\tstartedRoute.current = route;\n\t\t\tsetRunId((id) => id + 1);\n\t\t\tsetVisible(true);\n\t\t}\n\t\tfunction onClick(event: MouseEvent) {\n\t\t\tif (!isInternalNavigationClick(event)) return;\n\t\t\t// Capture runs before Next's <Link> handler, which may push history\n\t\t\t// before a bubble listener would see the click.\n\t\t\tbegin(committedRoute.current ?? routeKey(pathname, search));\n\t\t}\n\t\tfunction onPopState() {\n\t\t\t// The URL has already changed. Skip when it still matches the route\n\t\t\t// React committed (post-viewer history.back). Otherwise keep that\n\t\t\t// committed route so the bar can end once the destination commits.\n\t\t\tconst landed = routeKey(\n\t\t\t\twindow.location.pathname,\n\t\t\t\twindow.location.search.replace(/^\\?/, \"\"),\n\t\t\t);\n\t\t\tif (landed === (committedRoute.current ?? \"\")) return;\n\t\t\tbegin(committedRoute.current ?? \"\");\n\t\t}\n\t\tfunction onStart(href: string | undefined) {\n\t\t\tif (href) {\n\t\t\t\tconst target = routeKeyFromHref(href);\n\t\t\t\tif (target !== null && target === committedRoute.current) return;\n\t\t\t}\n\t\t\tbegin(committedRoute.current ?? routeKey(pathname, search));\n\t\t}\n\t\tstartListeners.add(onStart);\n\t\tdocument.addEventListener(\"click\", onClick, true);\n\t\twindow.addEventListener(\"popstate\", onPopState);\n\t\treturn () => {\n\t\t\tstartListeners.delete(onStart);\n\t\t\tdocument.removeEventListener(\"click\", onClick, true);\n\t\t\twindow.removeEventListener(\"popstate\", onPopState);\n\t\t};\n\t}, [forceVisible, pathname, search]);\n\n\tuseEffect(() => {\n\t\tif (forceVisible || !visible) return;\n\t\tconst safety = window.setTimeout(() => setVisible(false), SAFETY_MS);\n\t\treturn () => window.clearTimeout(safety);\n\t}, [forceVisible, runId, visible]);\n\n\tuseEffect(() => {\n\t\tif (forceVisible || !visible) return;\n\t\tconst started = startedRoute.current;\n\t\tif (!started) return;\n\n\t\tlet hideTimer = 0;\n\t\tlet stopped = false;\n\t\tlet sawLoading = false;\n\t\tfunction hide() {\n\t\t\tif (stopped) return;\n\t\t\tif (routeLoadingIsActive()) {\n\t\t\t\tsawLoading = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst routeChanged = routeKey(pathname, search) !== started;\n\t\t\tif (!routeChanged && !sawLoading) return;\n\t\t\tstopped = true;\n\t\t\tconst wait = Math.max(\n\t\t\t\t0,\n\t\t\t\tMIN_VISIBLE_MS - (Date.now() - startedAt.current),\n\t\t\t);\n\t\t\thideTimer = window.setTimeout(() => setVisible(false), wait);\n\t\t}\n\t\thide();\n\t\tconst observer = new MutationObserver(hide);\n\t\tobserver.observe(document.body, {\n\t\t\tchildList: true,\n\t\t\tsubtree: true,\n\t\t\tattributes: true,\n\t\t\tattributeFilter: [\"style\", \"hidden\", \"class\"],\n\t\t});\n\t\treturn () => {\n\t\t\tstopped = true;\n\t\t\twindow.clearTimeout(hideTimer);\n\t\t\tobserver.disconnect();\n\t\t};\n\t}, [forceVisible, pathname, search, visible]);\n\n\tif (!visible) return null;\n\n\treturn (\n\t\t<>\n\t\t\t<style dangerouslySetInnerHTML={{ __html: NAV_PROGRESS_CSS }} />\n\t\t\t<div\n\t\t\t\trole=\"progressbar\"\n\t\t\t\taria-label=\"Cargando\"\n\t\t\t\tclassName=\"pointer-events-none fixed inset-x-0 top-0 z-[60] h-0.5 overflow-hidden bg-primary/20\"\n\t\t\t>\n\t\t\t\t<div className=\"nav-progress-indeterminate bg-primary\" />\n\t\t\t</div>\n\t\t</>\n\t);\n}\n"]}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Ignore navigation-start signals for the rest of this turn. Used by the
|
|
5
|
+
* fullscreen post viewer, whose permalink `pushState` / `history.back()` is
|
|
6
|
+
* not a page load. An in-flight bar is left alone.
|
|
7
|
+
*/
|
|
8
|
+
declare function suppressNavigationProgress(): void;
|
|
9
|
+
/**
|
|
10
|
+
* Start the top bar for a navigation that did not come from a link click.
|
|
11
|
+
* Records the route React has committed, not `window.location` (already the
|
|
12
|
+
* destination on back/forward). No-ops when `href` is that same route, or
|
|
13
|
+
* when {@link suppressNavigationProgress} is set.
|
|
14
|
+
*/
|
|
15
|
+
declare function signalNavigationStart(href?: string): void;
|
|
3
16
|
type NavigationProgressProps = {
|
|
4
17
|
/** App-supplied pathname. Blocks does not import `next/navigation`. */
|
|
5
18
|
pathname: string;
|
|
@@ -11,9 +24,10 @@ type NavigationProgressProps = {
|
|
|
11
24
|
/**
|
|
12
25
|
* Indeterminate bar fixed to the top of the viewport. Apps pass the current
|
|
13
26
|
* route from their router and wrap `useSearchParams` in `<Suspense>`.
|
|
14
|
-
* The bar starts on a same-origin link click
|
|
15
|
-
*
|
|
27
|
+
* The bar starts on a same-origin link click, back/forward, or
|
|
28
|
+
* {@link signalNavigationStart}, and stays up while a visible
|
|
29
|
+
* `[data-route-loading]` node (see `RouteLoading`) is on screen.
|
|
16
30
|
*/
|
|
17
31
|
declare function NavigationProgress({ pathname, search, forceVisible, }: NavigationProgressProps): React.JSX.Element | null;
|
|
18
32
|
|
|
19
|
-
export { NavigationProgress, type NavigationProgressProps };
|
|
33
|
+
export { NavigationProgress, type NavigationProgressProps, signalNavigationStart, suppressNavigationProgress };
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Ignore navigation-start signals for the rest of this turn. Used by the
|
|
5
|
+
* fullscreen post viewer, whose permalink `pushState` / `history.back()` is
|
|
6
|
+
* not a page load. An in-flight bar is left alone.
|
|
7
|
+
*/
|
|
8
|
+
declare function suppressNavigationProgress(): void;
|
|
9
|
+
/**
|
|
10
|
+
* Start the top bar for a navigation that did not come from a link click.
|
|
11
|
+
* Records the route React has committed, not `window.location` (already the
|
|
12
|
+
* destination on back/forward). No-ops when `href` is that same route, or
|
|
13
|
+
* when {@link suppressNavigationProgress} is set.
|
|
14
|
+
*/
|
|
15
|
+
declare function signalNavigationStart(href?: string): void;
|
|
3
16
|
type NavigationProgressProps = {
|
|
4
17
|
/** App-supplied pathname. Blocks does not import `next/navigation`. */
|
|
5
18
|
pathname: string;
|
|
@@ -11,9 +24,10 @@ type NavigationProgressProps = {
|
|
|
11
24
|
/**
|
|
12
25
|
* Indeterminate bar fixed to the top of the viewport. Apps pass the current
|
|
13
26
|
* route from their router and wrap `useSearchParams` in `<Suspense>`.
|
|
14
|
-
* The bar starts on a same-origin link click
|
|
15
|
-
*
|
|
27
|
+
* The bar starts on a same-origin link click, back/forward, or
|
|
28
|
+
* {@link signalNavigationStart}, and stays up while a visible
|
|
29
|
+
* `[data-route-loading]` node (see `RouteLoading`) is on screen.
|
|
16
30
|
*/
|
|
17
31
|
declare function NavigationProgress({ pathname, search, forceVisible, }: NavigationProgressProps): React.JSX.Element | null;
|
|
18
32
|
|
|
19
|
-
export { NavigationProgress, type NavigationProgressProps };
|
|
33
|
+
export { NavigationProgress, type NavigationProgressProps, signalNavigationStart, suppressNavigationProgress };
|
|
@@ -23,8 +23,59 @@ var NAV_PROGRESS_CSS = `
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
`;
|
|
26
|
+
var startListeners = /* @__PURE__ */ new Set();
|
|
27
|
+
var navigationSuppressed = false;
|
|
28
|
+
var suppressTimer = 0;
|
|
29
|
+
function normalizeSearch(search) {
|
|
30
|
+
const params = new URLSearchParams(search);
|
|
31
|
+
const entries = [...params.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
32
|
+
const sorted = new URLSearchParams();
|
|
33
|
+
for (const [key, value] of entries) {
|
|
34
|
+
sorted.append(key, value);
|
|
35
|
+
}
|
|
36
|
+
return sorted.toString();
|
|
37
|
+
}
|
|
26
38
|
function routeKey(pathname, search) {
|
|
27
|
-
return `${pathname}?${search}`;
|
|
39
|
+
return `${pathname}?${normalizeSearch(search)}`;
|
|
40
|
+
}
|
|
41
|
+
function routeKeyFromHref(href) {
|
|
42
|
+
try {
|
|
43
|
+
const url = new URL(href, window.location.origin);
|
|
44
|
+
return routeKey(url.pathname, url.search.replace(/^\?/, ""));
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function isShown(element) {
|
|
50
|
+
if (!(element instanceof HTMLElement)) return false;
|
|
51
|
+
if (element.hidden) return false;
|
|
52
|
+
const display = element.style.display || getComputedStyle(element).display;
|
|
53
|
+
if (display === "none") return false;
|
|
54
|
+
const visibility = element.style.visibility || getComputedStyle(element).visibility;
|
|
55
|
+
if (visibility === "hidden") return false;
|
|
56
|
+
if (display === "contents") {
|
|
57
|
+
return Array.from(element.children).some((child) => isShown(child));
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
function routeLoadingIsActive() {
|
|
62
|
+
for (const node of document.querySelectorAll(ROUTE_LOADING_SELECTOR)) {
|
|
63
|
+
if (isShown(node)) return true;
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
function suppressNavigationProgress() {
|
|
68
|
+
navigationSuppressed = true;
|
|
69
|
+
window.clearTimeout(suppressTimer);
|
|
70
|
+
suppressTimer = window.setTimeout(() => {
|
|
71
|
+
navigationSuppressed = false;
|
|
72
|
+
}, 0);
|
|
73
|
+
}
|
|
74
|
+
function signalNavigationStart(href) {
|
|
75
|
+
if (navigationSuppressed) return;
|
|
76
|
+
for (const listener of startListeners) {
|
|
77
|
+
listener(href);
|
|
78
|
+
}
|
|
28
79
|
}
|
|
29
80
|
function isInternalNavigationClick(event) {
|
|
30
81
|
if (event.button !== 0) return false;
|
|
@@ -59,6 +110,7 @@ function NavigationProgress({
|
|
|
59
110
|
forceVisible = false
|
|
60
111
|
}) {
|
|
61
112
|
const [visible, setVisible] = useState(forceVisible);
|
|
113
|
+
const [runId, setRunId] = useState(0);
|
|
62
114
|
const startedAt = useRef(0);
|
|
63
115
|
const startedRoute = useRef(null);
|
|
64
116
|
const committedRoute = useRef(null);
|
|
@@ -68,38 +120,60 @@ function NavigationProgress({
|
|
|
68
120
|
useEffect(() => {
|
|
69
121
|
if (forceVisible) return;
|
|
70
122
|
function begin(route) {
|
|
123
|
+
if (navigationSuppressed) return;
|
|
71
124
|
startedAt.current = Date.now();
|
|
72
125
|
startedRoute.current = route;
|
|
126
|
+
setRunId((id) => id + 1);
|
|
73
127
|
setVisible(true);
|
|
74
128
|
}
|
|
75
129
|
function onClick(event) {
|
|
76
130
|
if (!isInternalNavigationClick(event)) return;
|
|
77
|
-
begin(
|
|
78
|
-
routeKey(
|
|
79
|
-
window.location.pathname,
|
|
80
|
-
window.location.search.replace(/^\?/, "")
|
|
81
|
-
)
|
|
82
|
-
);
|
|
131
|
+
begin(committedRoute.current ?? routeKey(pathname, search));
|
|
83
132
|
}
|
|
84
133
|
function onPopState() {
|
|
134
|
+
const landed = routeKey(
|
|
135
|
+
window.location.pathname,
|
|
136
|
+
window.location.search.replace(/^\?/, "")
|
|
137
|
+
);
|
|
138
|
+
if (landed === (committedRoute.current ?? "")) return;
|
|
85
139
|
begin(committedRoute.current ?? "");
|
|
86
140
|
}
|
|
141
|
+
function onStart(href) {
|
|
142
|
+
if (href) {
|
|
143
|
+
const target = routeKeyFromHref(href);
|
|
144
|
+
if (target !== null && target === committedRoute.current) return;
|
|
145
|
+
}
|
|
146
|
+
begin(committedRoute.current ?? routeKey(pathname, search));
|
|
147
|
+
}
|
|
148
|
+
startListeners.add(onStart);
|
|
87
149
|
document.addEventListener("click", onClick, true);
|
|
88
150
|
window.addEventListener("popstate", onPopState);
|
|
89
151
|
return () => {
|
|
152
|
+
startListeners.delete(onStart);
|
|
90
153
|
document.removeEventListener("click", onClick, true);
|
|
91
154
|
window.removeEventListener("popstate", onPopState);
|
|
92
155
|
};
|
|
93
|
-
}, [forceVisible]);
|
|
156
|
+
}, [forceVisible, pathname, search]);
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
if (forceVisible || !visible) return;
|
|
159
|
+
const safety = window.setTimeout(() => setVisible(false), SAFETY_MS);
|
|
160
|
+
return () => window.clearTimeout(safety);
|
|
161
|
+
}, [forceVisible, runId, visible]);
|
|
94
162
|
useEffect(() => {
|
|
95
163
|
if (forceVisible || !visible) return;
|
|
96
164
|
const started = startedRoute.current;
|
|
97
|
-
if (!started
|
|
165
|
+
if (!started) return;
|
|
98
166
|
let hideTimer = 0;
|
|
99
167
|
let stopped = false;
|
|
168
|
+
let sawLoading = false;
|
|
100
169
|
function hide() {
|
|
101
170
|
if (stopped) return;
|
|
102
|
-
if (
|
|
171
|
+
if (routeLoadingIsActive()) {
|
|
172
|
+
sawLoading = true;
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const routeChanged = routeKey(pathname, search) !== started;
|
|
176
|
+
if (!routeChanged && !sawLoading) return;
|
|
103
177
|
stopped = true;
|
|
104
178
|
const wait = Math.max(
|
|
105
179
|
0,
|
|
@@ -109,12 +183,15 @@ function NavigationProgress({
|
|
|
109
183
|
}
|
|
110
184
|
hide();
|
|
111
185
|
const observer = new MutationObserver(hide);
|
|
112
|
-
observer.observe(document.body, {
|
|
113
|
-
|
|
186
|
+
observer.observe(document.body, {
|
|
187
|
+
childList: true,
|
|
188
|
+
subtree: true,
|
|
189
|
+
attributes: true,
|
|
190
|
+
attributeFilter: ["style", "hidden", "class"]
|
|
191
|
+
});
|
|
114
192
|
return () => {
|
|
115
193
|
stopped = true;
|
|
116
194
|
window.clearTimeout(hideTimer);
|
|
117
|
-
window.clearTimeout(safety);
|
|
118
195
|
observer.disconnect();
|
|
119
196
|
};
|
|
120
197
|
}, [forceVisible, pathname, search, visible]);
|
|
@@ -133,6 +210,6 @@ function NavigationProgress({
|
|
|
133
210
|
] });
|
|
134
211
|
}
|
|
135
212
|
|
|
136
|
-
export { NavigationProgress };
|
|
213
|
+
export { NavigationProgress, signalNavigationStart, suppressNavigationProgress };
|
|
137
214
|
//# sourceMappingURL=navigation-progress.js.map
|
|
138
215
|
//# sourceMappingURL=navigation-progress.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/NavigationProgress.tsx"],"names":[],"mappings":"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","file":"navigation-progress.js","sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\nconst MIN_VISIBLE_MS = 200;\nconst SAFETY_MS = 10_000;\n\nexport const ROUTE_LOADING_SELECTOR = \"[data-route-loading]\";\n\n/** Indeterminate bar motion. Inlined so apps do not copy keyframes into their CSS. */\nexport const NAV_PROGRESS_CSS = `\n@keyframes nav-progress-indeterminate {\n\t0% { transform: translateX(-100%); }\n\t100% { transform: translateX(400%); }\n}\n.nav-progress-indeterminate {\n\twidth: 33%;\n\theight: 100%;\n\tanimation: nav-progress-indeterminate 1.1s ease-in-out infinite;\n}\n@media (prefers-reduced-motion: reduce) {\n\t.nav-progress-indeterminate {\n\t\tanimation: none;\n\t\twidth: 100%;\n\t\ttransform: none;\n\t}\n}\n`;\n\nfunction routeKey(pathname: string, search: string): string {\n\treturn `${pathname}?${search}`;\n}\n\nexport function isInternalNavigationClick(event: MouseEvent): boolean {\n\tif (event.button !== 0) return false;\n\tif (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n\t\treturn false;\n\t}\n\tconst target = event.target;\n\tif (!(target instanceof Element)) return false;\n\tconst anchor = target.closest(\"a\");\n\tif (!(anchor instanceof HTMLAnchorElement)) return false;\n\tif (anchor.target && anchor.target !== \"_self\") return false;\n\tif (anchor.hasAttribute(\"download\")) return false;\n\tconst raw = anchor.getAttribute(\"href\");\n\tif (\n\t\t!raw ||\n\t\traw.startsWith(\"#\") ||\n\t\traw.startsWith(\"mailto:\") ||\n\t\traw.startsWith(\"tel:\")\n\t) {\n\t\treturn false;\n\t}\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(anchor.href, window.location.href);\n\t} catch {\n\t\treturn false;\n\t}\n\tif (url.origin !== window.location.origin) return false;\n\tif (\n\t\turl.pathname === window.location.pathname &&\n\t\turl.search === window.location.search\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nexport type NavigationProgressProps = {\n\t/** App-supplied pathname. Blocks does not import `next/navigation`. */\n\tpathname: string;\n\t/** Query string without the leading `?`. */\n\tsearch?: string;\n\t/** Storybook / tests. Skips click listeners and stays visible. */\n\tforceVisible?: boolean;\n};\n\n/**\n * Indeterminate bar fixed to the top of the viewport. Apps pass the current\n * route from their router and wrap `useSearchParams` in `<Suspense>`.\n * The bar starts on a same-origin link click or back/forward, and stays up\n * while a `[data-route-loading]` node (see `RouteLoading`) is mounted.\n */\nexport function NavigationProgress({\n\tpathname,\n\tsearch = \"\",\n\tforceVisible = false,\n}: NavigationProgressProps) {\n\tconst [visible, setVisible] = useState(forceVisible);\n\tconst startedAt = useRef(0);\n\tconst startedRoute = useRef<string | null>(null);\n\tconst committedRoute = useRef<string | null>(null);\n\n\tuseEffect(() => {\n\t\tcommittedRoute.current = routeKey(pathname, search);\n\t}, [pathname, search]);\n\n\tuseEffect(() => {\n\t\tif (forceVisible) return;\n\t\tfunction begin(route: string) {\n\t\t\tstartedAt.current = Date.now();\n\t\t\tstartedRoute.current = route;\n\t\t\tsetVisible(true);\n\t\t}\n\t\tfunction onClick(event: MouseEvent) {\n\t\t\tif (!isInternalNavigationClick(event)) return;\n\t\t\t// Capture runs before Next's <Link> handler, which may push history\n\t\t\t// before a bubble listener would see the click.\n\t\t\tbegin(\n\t\t\t\trouteKey(\n\t\t\t\t\twindow.location.pathname,\n\t\t\t\t\twindow.location.search.replace(/^\\?/, \"\"),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tfunction onPopState() {\n\t\t\t// The URL has already changed. Keep the route React last committed\n\t\t\t// so the bar can end once the destination commits.\n\t\t\tbegin(committedRoute.current ?? \"\");\n\t\t}\n\t\tdocument.addEventListener(\"click\", onClick, true);\n\t\twindow.addEventListener(\"popstate\", onPopState);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"click\", onClick, true);\n\t\t\twindow.removeEventListener(\"popstate\", onPopState);\n\t\t};\n\t}, [forceVisible]);\n\n\tuseEffect(() => {\n\t\tif (forceVisible || !visible) return;\n\t\tconst started = startedRoute.current;\n\t\tif (!started || routeKey(pathname, search) === started) return;\n\n\t\tlet hideTimer = 0;\n\t\tlet stopped = false;\n\t\tfunction hide() {\n\t\t\tif (stopped) return;\n\t\t\tif (document.querySelector(ROUTE_LOADING_SELECTOR)) return;\n\t\t\tstopped = true;\n\t\t\tconst wait = Math.max(\n\t\t\t\t0,\n\t\t\t\tMIN_VISIBLE_MS - (Date.now() - startedAt.current),\n\t\t\t);\n\t\t\thideTimer = window.setTimeout(() => setVisible(false), wait);\n\t\t}\n\t\thide();\n\t\tconst observer = new MutationObserver(hide);\n\t\tobserver.observe(document.body, { childList: true, subtree: true });\n\t\tconst safety = window.setTimeout(() => setVisible(false), SAFETY_MS);\n\t\treturn () => {\n\t\t\tstopped = true;\n\t\t\twindow.clearTimeout(hideTimer);\n\t\t\twindow.clearTimeout(safety);\n\t\t\tobserver.disconnect();\n\t\t};\n\t}, [forceVisible, pathname, search, visible]);\n\n\tif (!visible) return null;\n\n\treturn (\n\t\t<>\n\t\t\t<style dangerouslySetInnerHTML={{ __html: NAV_PROGRESS_CSS }} />\n\t\t\t<div\n\t\t\t\trole=\"progressbar\"\n\t\t\t\taria-label=\"Cargando\"\n\t\t\t\tclassName=\"pointer-events-none fixed inset-x-0 top-0 z-[60] h-0.5 overflow-hidden bg-primary/20\"\n\t\t\t>\n\t\t\t\t<div className=\"nav-progress-indeterminate bg-primary\" />\n\t\t\t</div>\n\t\t</>\n\t);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/NavigationProgress.tsx"],"names":[],"mappings":"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","file":"navigation-progress.js","sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\nconst MIN_VISIBLE_MS = 200;\nconst SAFETY_MS = 10_000;\n\nexport const ROUTE_LOADING_SELECTOR = \"[data-route-loading]\";\n\n/** Indeterminate bar motion. Inlined so apps do not copy keyframes into their CSS. */\nexport const NAV_PROGRESS_CSS = `\n@keyframes nav-progress-indeterminate {\n\t0% { transform: translateX(-100%); }\n\t100% { transform: translateX(400%); }\n}\n.nav-progress-indeterminate {\n\twidth: 33%;\n\theight: 100%;\n\tanimation: nav-progress-indeterminate 1.1s ease-in-out infinite;\n}\n@media (prefers-reduced-motion: reduce) {\n\t.nav-progress-indeterminate {\n\t\tanimation: none;\n\t\twidth: 100%;\n\t\ttransform: none;\n\t}\n}\n`;\n\ntype StartListener = (href: string | undefined) => void;\n\nconst startListeners = new Set<StartListener>();\nlet navigationSuppressed = false;\nlet suppressTimer = 0;\n\nfunction normalizeSearch(search: string): string {\n\tconst params = new URLSearchParams(search);\n\tconst entries = [...params.entries()].sort(([a], [b]) => a.localeCompare(b));\n\tconst sorted = new URLSearchParams();\n\tfor (const [key, value] of entries) {\n\t\tsorted.append(key, value);\n\t}\n\treturn sorted.toString();\n}\n\nfunction routeKey(pathname: string, search: string): string {\n\treturn `${pathname}?${normalizeSearch(search)}`;\n}\n\nfunction routeKeyFromHref(href: string): string | null {\n\ttry {\n\t\tconst url = new URL(href, window.location.origin);\n\t\treturn routeKey(url.pathname, url.search.replace(/^\\?/, \"\"));\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction isShown(element: Element): boolean {\n\tif (!(element instanceof HTMLElement)) return false;\n\tif (element.hidden) return false;\n\tconst display = element.style.display || getComputedStyle(element).display;\n\tif (display === \"none\") return false;\n\tconst visibility =\n\t\telement.style.visibility || getComputedStyle(element).visibility;\n\tif (visibility === \"hidden\") return false;\n\tif (display === \"contents\") {\n\t\treturn Array.from(element.children).some((child) => isShown(child));\n\t}\n\treturn true;\n}\n\n/** A route skeleton counts only while it is actually on screen. */\nexport function routeLoadingIsActive(): boolean {\n\tfor (const node of document.querySelectorAll(ROUTE_LOADING_SELECTOR)) {\n\t\tif (isShown(node)) return true;\n\t}\n\treturn false;\n}\n\n/**\n * Ignore navigation-start signals for the rest of this turn. Used by the\n * fullscreen post viewer, whose permalink `pushState` / `history.back()` is\n * not a page load. An in-flight bar is left alone.\n */\nexport function suppressNavigationProgress(): void {\n\tnavigationSuppressed = true;\n\twindow.clearTimeout(suppressTimer);\n\tsuppressTimer = window.setTimeout(() => {\n\t\tnavigationSuppressed = false;\n\t}, 0);\n}\n\n/**\n * Start the top bar for a navigation that did not come from a link click.\n * Records the route React has committed, not `window.location` (already the\n * destination on back/forward). No-ops when `href` is that same route, or\n * when {@link suppressNavigationProgress} is set.\n */\nexport function signalNavigationStart(href?: string): void {\n\tif (navigationSuppressed) return;\n\tfor (const listener of startListeners) {\n\t\tlistener(href);\n\t}\n}\n\nexport function isInternalNavigationClick(event: MouseEvent): boolean {\n\tif (event.button !== 0) return false;\n\tif (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n\t\treturn false;\n\t}\n\tconst target = event.target;\n\tif (!(target instanceof Element)) return false;\n\tconst anchor = target.closest(\"a\");\n\tif (!(anchor instanceof HTMLAnchorElement)) return false;\n\tif (anchor.target && anchor.target !== \"_self\") return false;\n\tif (anchor.hasAttribute(\"download\")) return false;\n\tconst raw = anchor.getAttribute(\"href\");\n\tif (\n\t\t!raw ||\n\t\traw.startsWith(\"#\") ||\n\t\traw.startsWith(\"mailto:\") ||\n\t\traw.startsWith(\"tel:\")\n\t) {\n\t\treturn false;\n\t}\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(anchor.href, window.location.href);\n\t} catch {\n\t\treturn false;\n\t}\n\tif (url.origin !== window.location.origin) return false;\n\tif (\n\t\turl.pathname === window.location.pathname &&\n\t\turl.search === window.location.search\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nexport type NavigationProgressProps = {\n\t/** App-supplied pathname. Blocks does not import `next/navigation`. */\n\tpathname: string;\n\t/** Query string without the leading `?`. */\n\tsearch?: string;\n\t/** Storybook / tests. Skips click listeners and stays visible. */\n\tforceVisible?: boolean;\n};\n\n/**\n * Indeterminate bar fixed to the top of the viewport. Apps pass the current\n * route from their router and wrap `useSearchParams` in `<Suspense>`.\n * The bar starts on a same-origin link click, back/forward, or\n * {@link signalNavigationStart}, and stays up while a visible\n * `[data-route-loading]` node (see `RouteLoading`) is on screen.\n */\nexport function NavigationProgress({\n\tpathname,\n\tsearch = \"\",\n\tforceVisible = false,\n}: NavigationProgressProps) {\n\tconst [visible, setVisible] = useState(forceVisible);\n\tconst [runId, setRunId] = useState(0);\n\tconst startedAt = useRef(0);\n\tconst startedRoute = useRef<string | null>(null);\n\tconst committedRoute = useRef<string | null>(null);\n\n\tuseEffect(() => {\n\t\tcommittedRoute.current = routeKey(pathname, search);\n\t}, [pathname, search]);\n\n\tuseEffect(() => {\n\t\tif (forceVisible) return;\n\t\tfunction begin(route: string) {\n\t\t\tif (navigationSuppressed) return;\n\t\t\tstartedAt.current = Date.now();\n\t\t\tstartedRoute.current = route;\n\t\t\tsetRunId((id) => id + 1);\n\t\t\tsetVisible(true);\n\t\t}\n\t\tfunction onClick(event: MouseEvent) {\n\t\t\tif (!isInternalNavigationClick(event)) return;\n\t\t\t// Capture runs before Next's <Link> handler, which may push history\n\t\t\t// before a bubble listener would see the click.\n\t\t\tbegin(committedRoute.current ?? routeKey(pathname, search));\n\t\t}\n\t\tfunction onPopState() {\n\t\t\t// The URL has already changed. Skip when it still matches the route\n\t\t\t// React committed (post-viewer history.back). Otherwise keep that\n\t\t\t// committed route so the bar can end once the destination commits.\n\t\t\tconst landed = routeKey(\n\t\t\t\twindow.location.pathname,\n\t\t\t\twindow.location.search.replace(/^\\?/, \"\"),\n\t\t\t);\n\t\t\tif (landed === (committedRoute.current ?? \"\")) return;\n\t\t\tbegin(committedRoute.current ?? \"\");\n\t\t}\n\t\tfunction onStart(href: string | undefined) {\n\t\t\tif (href) {\n\t\t\t\tconst target = routeKeyFromHref(href);\n\t\t\t\tif (target !== null && target === committedRoute.current) return;\n\t\t\t}\n\t\t\tbegin(committedRoute.current ?? routeKey(pathname, search));\n\t\t}\n\t\tstartListeners.add(onStart);\n\t\tdocument.addEventListener(\"click\", onClick, true);\n\t\twindow.addEventListener(\"popstate\", onPopState);\n\t\treturn () => {\n\t\t\tstartListeners.delete(onStart);\n\t\t\tdocument.removeEventListener(\"click\", onClick, true);\n\t\t\twindow.removeEventListener(\"popstate\", onPopState);\n\t\t};\n\t}, [forceVisible, pathname, search]);\n\n\tuseEffect(() => {\n\t\tif (forceVisible || !visible) return;\n\t\tconst safety = window.setTimeout(() => setVisible(false), SAFETY_MS);\n\t\treturn () => window.clearTimeout(safety);\n\t}, [forceVisible, runId, visible]);\n\n\tuseEffect(() => {\n\t\tif (forceVisible || !visible) return;\n\t\tconst started = startedRoute.current;\n\t\tif (!started) return;\n\n\t\tlet hideTimer = 0;\n\t\tlet stopped = false;\n\t\tlet sawLoading = false;\n\t\tfunction hide() {\n\t\t\tif (stopped) return;\n\t\t\tif (routeLoadingIsActive()) {\n\t\t\t\tsawLoading = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst routeChanged = routeKey(pathname, search) !== started;\n\t\t\tif (!routeChanged && !sawLoading) return;\n\t\t\tstopped = true;\n\t\t\tconst wait = Math.max(\n\t\t\t\t0,\n\t\t\t\tMIN_VISIBLE_MS - (Date.now() - startedAt.current),\n\t\t\t);\n\t\t\thideTimer = window.setTimeout(() => setVisible(false), wait);\n\t\t}\n\t\thide();\n\t\tconst observer = new MutationObserver(hide);\n\t\tobserver.observe(document.body, {\n\t\t\tchildList: true,\n\t\t\tsubtree: true,\n\t\t\tattributes: true,\n\t\t\tattributeFilter: [\"style\", \"hidden\", \"class\"],\n\t\t});\n\t\treturn () => {\n\t\t\tstopped = true;\n\t\t\twindow.clearTimeout(hideTimer);\n\t\t\tobserver.disconnect();\n\t\t};\n\t}, [forceVisible, pathname, search, visible]);\n\n\tif (!visible) return null;\n\n\treturn (\n\t\t<>\n\t\t\t<style dangerouslySetInnerHTML={{ __html: NAV_PROGRESS_CSS }} />\n\t\t\t<div\n\t\t\t\trole=\"progressbar\"\n\t\t\t\taria-label=\"Cargando\"\n\t\t\t\tclassName=\"pointer-events-none fixed inset-x-0 top-0 z-[60] h-0.5 overflow-hidden bg-primary/20\"\n\t\t\t>\n\t\t\t\t<div className=\"nav-progress-indeterminate bg-primary\" />\n\t\t\t</div>\n\t\t</>\n\t);\n}\n"]}
|