mirror of
https://github.com/makeplane/plane.git
synced 2025-12-16 11:57:56 +01:00
[WEB-4654] refactor: replace nprogress with bprogress and clean up unused code (#7559)
* refactor: replace nprogress with bprogress and clean up unused code * chore: code refactor * chore: code refactor
This commit is contained in:
committed by
GitHub
parent
9de5b1a009
commit
736296090e
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { FC, ReactNode } from "react";
|
import { FC, ReactNode } from "react";
|
||||||
|
import { AppProgressProvider as ProgressProvider } from "@bprogress/next";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { useTheme, ThemeProvider } from "next-themes";
|
import { useTheme, ThemeProvider } from "next-themes";
|
||||||
import { SWRConfig } from "swr";
|
import { SWRConfig } from "swr";
|
||||||
@@ -10,8 +11,6 @@ import { TranslationProvider } from "@plane/i18n";
|
|||||||
import { Toast } from "@plane/ui";
|
import { Toast } from "@plane/ui";
|
||||||
//helpers
|
//helpers
|
||||||
import { resolveGeneralTheme } from "@plane/utils";
|
import { resolveGeneralTheme } from "@plane/utils";
|
||||||
// nprogress
|
|
||||||
import { AppProgressBar } from "@/lib/n-progress";
|
|
||||||
// polyfills
|
// polyfills
|
||||||
import "@/lib/polyfills";
|
import "@/lib/polyfills";
|
||||||
// mobx store provider
|
// mobx store provider
|
||||||
@@ -37,23 +36,29 @@ export const AppProvider: FC<IAppProvider> = (props) => {
|
|||||||
// themes
|
// themes
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AppProgressBar height="4px" options={{ showSpinner: false }} shallowRouting />
|
<ProgressProvider
|
||||||
<StoreProvider>
|
height="4px"
|
||||||
<ThemeProvider themes={["light", "dark", "light-contrast", "dark-contrast", "custom"]} defaultTheme="system">
|
color="rgb(var(--color-primary-100))"
|
||||||
<ToastWithTheme />
|
options={{ showSpinner: false }}
|
||||||
<TranslationProvider>
|
shallowRouting
|
||||||
<StoreWrapper>
|
>
|
||||||
<InstanceWrapper>
|
<StoreProvider>
|
||||||
<IntercomProvider>
|
<ThemeProvider themes={["light", "dark", "light-contrast", "dark-contrast", "custom"]} defaultTheme="system">
|
||||||
<PostHogProvider>
|
<ToastWithTheme />
|
||||||
<SWRConfig value={WEB_SWR_CONFIG}>{children}</SWRConfig>
|
<TranslationProvider>
|
||||||
</PostHogProvider>
|
<StoreWrapper>
|
||||||
</IntercomProvider>
|
<InstanceWrapper>
|
||||||
</InstanceWrapper>
|
<IntercomProvider>
|
||||||
</StoreWrapper>
|
<PostHogProvider>
|
||||||
</TranslationProvider>
|
<SWRConfig value={WEB_SWR_CONFIG}>{children}</SWRConfig>
|
||||||
</ThemeProvider>
|
</PostHogProvider>
|
||||||
</StoreProvider>
|
</IntercomProvider>
|
||||||
|
</InstanceWrapper>
|
||||||
|
</StoreWrapper>
|
||||||
|
</TranslationProvider>
|
||||||
|
</ThemeProvider>
|
||||||
|
</StoreProvider>
|
||||||
|
</ProgressProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ interface IListItemProps {
|
|||||||
actionItemContainerClassName?: string;
|
actionItemContainerClassName?: string;
|
||||||
isSidebarOpen?: boolean;
|
isSidebarOpen?: boolean;
|
||||||
quickActionElement?: JSX.Element;
|
quickActionElement?: JSX.Element;
|
||||||
preventDefaultNProgress?: boolean;
|
preventDefaultProgress?: boolean;
|
||||||
leftElementClassName?: string;
|
leftElementClassName?: string;
|
||||||
rightElementClassName?: string;
|
rightElementClassName?: string;
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ export const ListItem: FC<IListItemProps> = (props) => {
|
|||||||
isSidebarOpen = false,
|
isSidebarOpen = false,
|
||||||
quickActionElement,
|
quickActionElement,
|
||||||
itemClassName = "",
|
itemClassName = "",
|
||||||
preventDefaultNProgress = false,
|
preventDefaultProgress = false,
|
||||||
leftElementClassName = "",
|
leftElementClassName = "",
|
||||||
rightElementClassName = "",
|
rightElementClassName = "",
|
||||||
} = props;
|
} = props;
|
||||||
@@ -76,7 +76,7 @@ export const ListItem: FC<IListItemProps> = (props) => {
|
|||||||
target="_self"
|
target="_self"
|
||||||
onClick={handleControlLinkClick}
|
onClick={handleControlLinkClick}
|
||||||
disabled={disableLink}
|
disabled={disableLink}
|
||||||
data-prevent-nprogress={preventDefaultNProgress}
|
data-prevent-progress={preventDefaultProgress}
|
||||||
>
|
>
|
||||||
<div className={cn("flex items-center gap-4 truncate", leftElementClassName)}>
|
<div className={cn("flex items-center gap-4 truncate", leftElementClassName)}>
|
||||||
{prependTitleElement && <span className="flex items-center flex-shrink-0">{prependTitleElement}</span>}
|
{prependTitleElement && <span className="flex items-center flex-shrink-0">{prependTitleElement}</span>}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export const CyclePeekOverview: React.FC<Props> = observer((props) => {
|
|||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
const query = generateQueryParams(searchParams, ["peekCycle"]);
|
const query = generateQueryParams(searchParams, ["peekCycle"]);
|
||||||
router.push(`${pathname}?${query}`, {}, { showProgressBar: false });
|
router.push(`${pathname}?${query}`, { showProgress: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -189,9 +189,9 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
|
|||||||
|
|
||||||
const query = generateQueryParams(searchParams, ["peekCycle"]);
|
const query = generateQueryParams(searchParams, ["peekCycle"]);
|
||||||
if (searchParams.has("peekCycle") && searchParams.get("peekCycle") === cycleId) {
|
if (searchParams.has("peekCycle") && searchParams.get("peekCycle") === cycleId) {
|
||||||
router.push(`${pathname}?${query}`, {}, { showProgressBar: false });
|
router.push(`${pathname}?${query}`, { showProgress: false });
|
||||||
} else {
|
} else {
|
||||||
router.push(`${pathname}?${query && `${query}&`}peekCycle=${cycleId}`, {}, { showProgressBar: false });
|
router.push(`${pathname}?${query && `${query}&`}peekCycle=${cycleId}`, { showProgress: false });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -67,9 +67,9 @@ export const CyclesListItem: FC<TCyclesListItem> = observer((props) => {
|
|||||||
|
|
||||||
const query = generateQueryParams(searchParams, ["peekCycle"]);
|
const query = generateQueryParams(searchParams, ["peekCycle"]);
|
||||||
if (searchParams.has("peekCycle") && searchParams.get("peekCycle") === cycleId) {
|
if (searchParams.has("peekCycle") && searchParams.get("peekCycle") === cycleId) {
|
||||||
router.push(`${pathname}?${query}`, {}, { showProgressBar: false });
|
router.push(`${pathname}?${query}`, { showProgress: false });
|
||||||
} else {
|
} else {
|
||||||
router.push(`${pathname}?${query && `${query}&`}peekCycle=${cycleId}`, {}, { showProgressBar: false });
|
router.push(`${pathname}?${query && `${query}&`}peekCycle=${cycleId}`, { showProgress: false });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export const RecentIssue = observer((props: BlockProps) => {
|
|||||||
className="bg-transparent my-auto !px-2 border-none py-3"
|
className="bg-transparent my-auto !px-2 border-none py-3"
|
||||||
itemClassName="my-auto"
|
itemClassName="my-auto"
|
||||||
onItemClick={handlePeekOverview}
|
onItemClick={handlePeekOverview}
|
||||||
preventDefaultNProgress
|
preventDefaultProgress
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative" data-prevent-nprogress>
|
<div className="relative" data-prevent-progress>
|
||||||
<Link ref={parentRef} href={`/${workspaceSlug}/projects/${moduleDetails.project_id}/modules/${moduleDetails.id}`}>
|
<Link ref={parentRef} href={`/${workspaceSlug}/projects/${moduleDetails.project_id}/modules/${moduleDetails.id}`}>
|
||||||
<Card>
|
<Card>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export const ProjectCard: React.FC<Props> = observer((props) => {
|
|||||||
const MENU_ITEMS: TContextMenuItem[] = [
|
const MENU_ITEMS: TContextMenuItem[] = [
|
||||||
{
|
{
|
||||||
key: "settings",
|
key: "settings",
|
||||||
action: () => router.push(`/${workspaceSlug}/settings/projects/${project.id}`, {}, { showProgressBar: false }),
|
action: () => router.push(`/${workspaceSlug}/settings/projects/${project.id}`, { showProgress: false }),
|
||||||
title: "Settings",
|
title: "Settings",
|
||||||
icon: Settings,
|
icon: Settings,
|
||||||
shouldRender: !isArchived && (hasAdminRole || hasMemberRole),
|
shouldRender: !isArchived && (hasAdminRole || hasMemberRole),
|
||||||
@@ -200,7 +200,7 @@ export const ProjectCard: React.FC<Props> = observer((props) => {
|
|||||||
if (!isArchived) setJoinProjectModal(true);
|
if (!isArchived) setJoinProjectModal(true);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
data-prevent-nprogress={!isMemberOfProject || isArchived}
|
data-prevent-progress={!isMemberOfProject || isArchived}
|
||||||
className="flex flex-col rounded border border-custom-border-200 bg-custom-background-100"
|
className="flex flex-col rounded border border-custom-border-200 bg-custom-background-100"
|
||||||
>
|
>
|
||||||
<ContextMenu parentRef={projectCardRef} items={MENU_ITEMS} />
|
<ContextMenu parentRef={projectCardRef} items={MENU_ITEMS} />
|
||||||
@@ -232,7 +232,7 @@ export const ProjectCard: React.FC<Props> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!isArchived && (
|
{!isArchived && (
|
||||||
<div data-prevent-nprogress className="flex h-full flex-shrink-0 items-center gap-2">
|
<div data-prevent-progress className="flex h-full flex-shrink-0 items-center gap-2">
|
||||||
<button
|
<button
|
||||||
className="flex h-6 w-6 items-center justify-center rounded bg-white/10"
|
className="flex h-6 w-6 items-center justify-center rounded bg-white/10"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// router from n-progress-bar
|
// router from n-progress-bar
|
||||||
import { useRouter } from "@/lib/n-progress";
|
import { useRouter } from "@/lib/b-progress";
|
||||||
|
|
||||||
export const useAppRouter = () => useRouter();
|
export const useAppRouter = () => useRouter();
|
||||||
|
|||||||
6
apps/web/core/lib/b-progress/AppProgressBar.tsx
Normal file
6
apps/web/core/lib/b-progress/AppProgressBar.tsx
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { useRouter as useBProgressRouter } from "@bprogress/next";
|
||||||
|
|
||||||
|
export function useRouter() {
|
||||||
|
const router = useBProgressRouter();
|
||||||
|
return router;
|
||||||
|
}
|
||||||
1
apps/web/core/lib/b-progress/index.tsx
Normal file
1
apps/web/core/lib/b-progress/index.tsx
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from "./AppProgressBar";
|
||||||
@@ -1,313 +0,0 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
|
||||||
import { NavigateOptions } from "next/dist/shared/lib/app-router-context.shared-runtime";
|
|
||||||
import { usePathname, useSearchParams, useRouter as useNextRouter } from "next/navigation";
|
|
||||||
import NProgress from "nprogress";
|
|
||||||
import { getAnchorProperty, hasPreventProgressAttribute } from "./utils/getAnchorProperty";
|
|
||||||
import { isSameURL, isSameURLWithoutSearch } from "./utils/sameURL";
|
|
||||||
import { ProgressBarProps, RouterNProgressOptions } from ".";
|
|
||||||
|
|
||||||
type PushStateInput = [data: any, unused: string, url?: string | URL | null | undefined];
|
|
||||||
|
|
||||||
export const AppProgressBar = React.memo(
|
|
||||||
({
|
|
||||||
color = "rgb(var(--color-primary-100))",
|
|
||||||
height = "2px",
|
|
||||||
options,
|
|
||||||
shallowRouting = false,
|
|
||||||
disableSameURL = true,
|
|
||||||
startPosition = 0,
|
|
||||||
delay = 0,
|
|
||||||
stopDelay = 0,
|
|
||||||
style,
|
|
||||||
nonce,
|
|
||||||
targetPreprocessor,
|
|
||||||
disableAnchorClick = false,
|
|
||||||
}: ProgressBarProps) => {
|
|
||||||
const styles = (
|
|
||||||
<style nonce={nonce}>
|
|
||||||
{style ||
|
|
||||||
`
|
|
||||||
#nprogress {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nprogress .bar {
|
|
||||||
background: ${color};
|
|
||||||
|
|
||||||
position: fixed;
|
|
||||||
z-index: 1031;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: ${height};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fancy blur effect */
|
|
||||||
#nprogress .peg {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
right: 0px;
|
|
||||||
width: 100px;
|
|
||||||
height: 100%;
|
|
||||||
box-shadow: 0 0 10px ${color}, 0 0 5px ${color};
|
|
||||||
opacity: 1.0;
|
|
||||||
|
|
||||||
-webkit-transform: rotate(3deg) translate(0px, -4px);
|
|
||||||
-ms-transform: rotate(3deg) translate(0px, -4px);
|
|
||||||
transform: rotate(3deg) translate(0px, -4px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove these to get rid of the spinner */
|
|
||||||
#nprogress .spinner {
|
|
||||||
display: block;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 1031;
|
|
||||||
top: 15px;
|
|
||||||
right: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nprogress .spinner-icon {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
border: solid 2px transparent;
|
|
||||||
border-top-color: ${color};
|
|
||||||
border-left-color: ${color};
|
|
||||||
border-radius: 50%;
|
|
||||||
|
|
||||||
-webkit-animation: nprogress-spinner 400ms linear infinite;
|
|
||||||
animation: nprogress-spinner 400ms linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nprogress-custom-parent {
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nprogress-custom-parent #nprogress .spinner,
|
|
||||||
.nprogress-custom-parent #nprogress .bar {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes nprogress-spinner {
|
|
||||||
0% { -webkit-transform: rotate(0deg); }
|
|
||||||
100% { -webkit-transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
@keyframes nprogress-spinner {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
</style>
|
|
||||||
);
|
|
||||||
|
|
||||||
NProgress.configure(options || {});
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
let progressDoneTimer: NodeJS.Timeout;
|
|
||||||
|
|
||||||
const pathname = usePathname();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (progressDoneTimer) clearTimeout(progressDoneTimer);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
progressDoneTimer = setTimeout(() => {
|
|
||||||
NProgress.done();
|
|
||||||
}, stopDelay);
|
|
||||||
}, [pathname, searchParams]);
|
|
||||||
|
|
||||||
const elementsWithAttachedHandlers = useRef<(HTMLAnchorElement | SVGAElement)[]>([]);
|
|
||||||
useEffect(() => {
|
|
||||||
if (disableAnchorClick) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
let timer: NodeJS.Timeout;
|
|
||||||
|
|
||||||
const startProgress = () => {
|
|
||||||
timer = setTimeout(() => {
|
|
||||||
if (startPosition > 0) NProgress.set(startPosition);
|
|
||||||
NProgress.start();
|
|
||||||
}, delay);
|
|
||||||
};
|
|
||||||
|
|
||||||
const stopProgress = () => {
|
|
||||||
if (timer) clearTimeout(timer);
|
|
||||||
timer = setTimeout(() => {
|
|
||||||
NProgress.done();
|
|
||||||
}, stopDelay);
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const handleAnchorClick: any = (event: MouseEvent) => {
|
|
||||||
// Skip preventDefault
|
|
||||||
if (event.defaultPrevented) return;
|
|
||||||
|
|
||||||
const anchorElement = event.currentTarget as HTMLAnchorElement | SVGAElement;
|
|
||||||
const target = event.target as HTMLElement | Element;
|
|
||||||
// Check if the target or any of its parents have the attribute
|
|
||||||
const preventProgress =
|
|
||||||
hasPreventProgressAttribute(target) || anchorElement?.getAttribute("data-prevent-nprogress") === "true";
|
|
||||||
|
|
||||||
if (preventProgress) return;
|
|
||||||
|
|
||||||
const anchorTarget = getAnchorProperty(anchorElement, "target");
|
|
||||||
// Skip anchors with target="_blank"
|
|
||||||
if (anchorTarget === "_blank") return;
|
|
||||||
|
|
||||||
// Skip control/command/option/alt+click
|
|
||||||
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
||||||
|
|
||||||
const targetHref = getAnchorProperty(anchorElement, "href");
|
|
||||||
const targetUrl = targetPreprocessor ? targetPreprocessor(new URL(targetHref)) : new URL(targetHref);
|
|
||||||
const currentUrl = new URL(location.href);
|
|
||||||
|
|
||||||
if (shallowRouting && isSameURLWithoutSearch(targetUrl, currentUrl) && disableSameURL) return;
|
|
||||||
if (isSameURL(targetUrl, currentUrl) && disableSameURL) return;
|
|
||||||
|
|
||||||
startProgress();
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
const handleMutation: MutationCallback = () => {
|
|
||||||
const anchorElements = Array.from(document.querySelectorAll("a")) as (HTMLAnchorElement | SVGAElement)[];
|
|
||||||
|
|
||||||
const validAnchorElements = anchorElements.filter((anchor) => {
|
|
||||||
const href = getAnchorProperty(anchor, "href");
|
|
||||||
const isNProgressDisabled = anchor.getAttribute("data-disable-nprogress") === "true";
|
|
||||||
const isNotTelOrMailto =
|
|
||||||
href &&
|
|
||||||
!href.startsWith("tel:") &&
|
|
||||||
!href.startsWith("mailto:") &&
|
|
||||||
!href.startsWith("blob:") &&
|
|
||||||
!href.startsWith("javascript:") &&
|
|
||||||
!href.startsWith("data:") &&
|
|
||||||
!href.startsWith("vbscript:");
|
|
||||||
|
|
||||||
return !isNProgressDisabled && isNotTelOrMailto && getAnchorProperty(anchor, "target") !== "_blank";
|
|
||||||
});
|
|
||||||
|
|
||||||
validAnchorElements.forEach((anchor) => anchor.addEventListener("click", handleAnchorClick));
|
|
||||||
elementsWithAttachedHandlers.current = validAnchorElements;
|
|
||||||
};
|
|
||||||
|
|
||||||
const mutationObserver = new MutationObserver(handleMutation);
|
|
||||||
mutationObserver.observe(document, { childList: true, subtree: true });
|
|
||||||
|
|
||||||
const originalWindowHistoryPushState = window.history.pushState;
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
window.history.pushState = new Proxy(window.history.pushState, {
|
|
||||||
apply: (target, thisArg, argArray: PushStateInput) => {
|
|
||||||
stopProgress();
|
|
||||||
return target.apply(thisArg, argArray);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
mutationObserver.disconnect();
|
|
||||||
elementsWithAttachedHandlers.current.forEach((anchor) => {
|
|
||||||
anchor.removeEventListener("click", handleAnchorClick);
|
|
||||||
});
|
|
||||||
elementsWithAttachedHandlers.current = [];
|
|
||||||
window.history.pushState = originalWindowHistoryPushState;
|
|
||||||
};
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return styles;
|
|
||||||
},
|
|
||||||
(prevProps, nextProps) => {
|
|
||||||
if (nextProps?.memo === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nextProps?.shouldCompareComplexProps) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
prevProps?.color === nextProps?.color &&
|
|
||||||
prevProps?.height === nextProps?.height &&
|
|
||||||
prevProps?.shallowRouting === nextProps?.shallowRouting &&
|
|
||||||
prevProps?.startPosition === nextProps?.startPosition &&
|
|
||||||
prevProps?.delay === nextProps?.delay &&
|
|
||||||
prevProps?.disableSameURL === nextProps?.disableSameURL &&
|
|
||||||
prevProps?.stopDelay === nextProps?.stopDelay &&
|
|
||||||
prevProps?.nonce === nextProps?.nonce &&
|
|
||||||
JSON.stringify(prevProps?.options) === JSON.stringify(nextProps?.options) &&
|
|
||||||
prevProps?.style === nextProps?.style &&
|
|
||||||
prevProps.disableAnchorClick === nextProps.disableAnchorClick
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
AppProgressBar.displayName = "AppProgressBar";
|
|
||||||
|
|
||||||
export function useRouter() {
|
|
||||||
const router = useNextRouter();
|
|
||||||
|
|
||||||
const startProgress = useCallback(
|
|
||||||
(startPosition?: number) => {
|
|
||||||
if (startPosition && startPosition > 0) NProgress.set(startPosition);
|
|
||||||
NProgress.start();
|
|
||||||
},
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
[router]
|
|
||||||
);
|
|
||||||
|
|
||||||
const progress = useCallback(
|
|
||||||
(href: string, options?: NavigateOptions, NProgressOptions?: RouterNProgressOptions) => {
|
|
||||||
if (NProgressOptions?.showProgressBar === false) {
|
|
||||||
return router.push(href, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentUrl = new URL(location.href);
|
|
||||||
const targetUrl = new URL(href, location.href);
|
|
||||||
|
|
||||||
if (isSameURL(targetUrl, currentUrl) && NProgressOptions?.disableSameURL !== false)
|
|
||||||
return router.push(href, options);
|
|
||||||
|
|
||||||
startProgress(NProgressOptions?.startPosition);
|
|
||||||
},
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
[router]
|
|
||||||
);
|
|
||||||
|
|
||||||
const push = useCallback(
|
|
||||||
(href: string, options?: NavigateOptions, NProgressOptions?: RouterNProgressOptions) => {
|
|
||||||
progress(href, options, NProgressOptions);
|
|
||||||
return router.push(href, options);
|
|
||||||
},
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
[router, startProgress]
|
|
||||||
);
|
|
||||||
|
|
||||||
const replace = useCallback(
|
|
||||||
(href: string, options?: NavigateOptions, NProgressOptions?: RouterNProgressOptions) => {
|
|
||||||
progress(href, options, NProgressOptions);
|
|
||||||
return router.replace(href, options);
|
|
||||||
},
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
[router, startProgress]
|
|
||||||
);
|
|
||||||
|
|
||||||
const back = useCallback(
|
|
||||||
(NProgressOptions?: RouterNProgressOptions) => {
|
|
||||||
if (NProgressOptions?.showProgressBar === false) return router.back();
|
|
||||||
|
|
||||||
startProgress(NProgressOptions?.startPosition);
|
|
||||||
|
|
||||||
return router.back();
|
|
||||||
},
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
[router]
|
|
||||||
);
|
|
||||||
|
|
||||||
const enhancedRouter = useMemo(() => ({ ...router, push, replace, back }), [router, push, replace, back]);
|
|
||||||
|
|
||||||
return enhancedRouter;
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import { start, done } from "nprogress";
|
|
||||||
import { AppProgressBar as AppProgressBarComponent, useRouter } from "./AppProgressBar";
|
|
||||||
import withSuspense from "./withSuspense";
|
|
||||||
|
|
||||||
export interface NProgressOptions {
|
|
||||||
minimum?: number;
|
|
||||||
template?: string;
|
|
||||||
easing?: string;
|
|
||||||
speed?: number;
|
|
||||||
trickle?: boolean;
|
|
||||||
trickleSpeed?: number;
|
|
||||||
showSpinner?: boolean;
|
|
||||||
parent?: string;
|
|
||||||
positionUsing?: string;
|
|
||||||
barSelector?: string;
|
|
||||||
spinnerSelector?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param color Color of the progress bar. @default #0A2FFF
|
|
||||||
* @param height Height of the progress bar. @default 2px
|
|
||||||
* @param options NProgress options. @default undefined
|
|
||||||
* @param shallowRouting If the progress bar is not displayed when you use shallow routing - @default false
|
|
||||||
* @param startPosition The position of the progress bar at the start of the page load - @default 0
|
|
||||||
* @param delay When the page loads faster than the progress bar, it does not display - @default 0
|
|
||||||
* @param stopDelay Delay to stop the progress bar - @default 0
|
|
||||||
* @param style Custom css - @default undefined
|
|
||||||
* @param nonce Custom nonce for Content-Security-Policy directives - @default undefined
|
|
||||||
* @param shouldCompareComplexProps If you want to compare props in the React.memo return - @default false
|
|
||||||
* @param targetPreprocessor If you want to./AppProgressBaress the target URL - @default undefined
|
|
||||||
* @param disableAnchorClick Disable triggering progress bar on anchor clicks - @default false
|
|
||||||
*/
|
|
||||||
export interface ProgressBarProps {
|
|
||||||
color?: string;
|
|
||||||
height?: string;
|
|
||||||
options?: Partial<NProgressOptions>;
|
|
||||||
shallowRouting?: boolean;
|
|
||||||
disableSameURL?: boolean;
|
|
||||||
startPosition?: number;
|
|
||||||
delay?: number;
|
|
||||||
stopDelay?: number;
|
|
||||||
style?: string;
|
|
||||||
nonce?: string;
|
|
||||||
memo?: boolean;
|
|
||||||
shouldCompareComplexProps?: boolean;
|
|
||||||
targetPreprocessor?: (url: URL) => URL;
|
|
||||||
disableAnchorClick?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RouterNProgressOptions {
|
|
||||||
showProgressBar?: boolean;
|
|
||||||
startPosition?: number;
|
|
||||||
disableSameURL?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const startProgress = () => {
|
|
||||||
start();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const stopProgress = (force?: boolean) => {
|
|
||||||
done(force);
|
|
||||||
};
|
|
||||||
|
|
||||||
const AppProgressBar = withSuspense<ProgressBarProps>(AppProgressBarComponent);
|
|
||||||
export { AppProgressBar, useRouter };
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
function parsePath(path: string) {
|
|
||||||
const hashIndex = path.indexOf("#");
|
|
||||||
const queryIndex = path.indexOf("?");
|
|
||||||
const hasQuery = queryIndex > -1 && (hashIndex < 0 || queryIndex < hashIndex);
|
|
||||||
|
|
||||||
if (hasQuery || hashIndex > -1) {
|
|
||||||
return {
|
|
||||||
pathname: path.substring(0, hasQuery ? queryIndex : hashIndex),
|
|
||||||
query: hasQuery ? path.substring(queryIndex, hashIndex > -1 ? hashIndex : undefined) : "",
|
|
||||||
hash: hashIndex > -1 ? path.slice(hashIndex) : "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return { pathname: path, query: "", hash: "" };
|
|
||||||
}
|
|
||||||
|
|
||||||
function addPathPrefix(path: string, prefix?: string) {
|
|
||||||
if (!path.startsWith("/") || !prefix) {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { pathname, query, hash } = parsePath(path);
|
|
||||||
return `${prefix}${pathname}${query}${hash}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAnchorProperty<T extends HTMLAnchorElement | SVGAElement, K extends keyof T, P extends T[K]>(
|
|
||||||
a: T,
|
|
||||||
key: K
|
|
||||||
): P extends SVGAnimatedString ? string : P {
|
|
||||||
if (typeof key === "string" && key === "data-disable-nprogress") {
|
|
||||||
const dataKey = key.substring(5) as keyof DOMStringMap;
|
|
||||||
return a.dataset[dataKey] as any;
|
|
||||||
}
|
|
||||||
|
|
||||||
const prop = a[key];
|
|
||||||
|
|
||||||
if (prop instanceof SVGAnimatedString) {
|
|
||||||
const value = prop.baseVal as unknown;
|
|
||||||
|
|
||||||
if (key === "href") {
|
|
||||||
return addPathPrefix(value as string, location.origin) as any;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value as any;
|
|
||||||
}
|
|
||||||
|
|
||||||
return prop as any;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility function to check for attribute in parent elements
|
|
||||||
export const hasPreventProgressAttribute = (element: Element | null): boolean => {
|
|
||||||
while (element) {
|
|
||||||
if (element?.getAttribute("data-prevent-nprogress") === "true") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
element = element?.parentElement;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
export function isSameURL(target: URL, current: URL) {
|
|
||||||
const cleanTarget = target.protocol + "//" + target.host + target.pathname + target.search;
|
|
||||||
const cleanCurrent = current.protocol + "//" + current.host + current.pathname + current.search;
|
|
||||||
|
|
||||||
return cleanTarget === cleanCurrent;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isSameURLWithoutSearch(target: URL, current: URL) {
|
|
||||||
const cleanTarget = target.protocol + "//" + target.host + target.pathname;
|
|
||||||
const cleanCurrent = current.protocol + "//" + current.host + current.pathname;
|
|
||||||
|
|
||||||
return cleanTarget === cleanCurrent;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import React, { ComponentType, Suspense } from "react";
|
|
||||||
|
|
||||||
export default function withSuspense<P extends object>(Component: ComponentType<P>) {
|
|
||||||
return function WithSuspenseComponent(props: P) {
|
|
||||||
return (
|
|
||||||
<Suspense>
|
|
||||||
<Component {...props} />
|
|
||||||
</Suspense>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
"@atlaskit/pragmatic-drag-and-drop": "^1.1.3",
|
"@atlaskit/pragmatic-drag-and-drop": "^1.1.3",
|
||||||
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^1.3.0",
|
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^1.3.0",
|
||||||
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3",
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3",
|
||||||
|
"@bprogress/next": "^3.2.12",
|
||||||
"@headlessui/react": "^1.7.3",
|
"@headlessui/react": "^1.7.3",
|
||||||
"@intercom/messenger-js-sdk": "^0.0.12",
|
"@intercom/messenger-js-sdk": "^0.0.12",
|
||||||
"@plane/constants": "*",
|
"@plane/constants": "*",
|
||||||
@@ -47,7 +48,6 @@
|
|||||||
"mobx-utils": "^6.0.8",
|
"mobx-utils": "^6.0.8",
|
||||||
"next": "14.2.30",
|
"next": "14.2.30",
|
||||||
"next-themes": "^0.2.1",
|
"next-themes": "^0.2.1",
|
||||||
"nprogress": "^0.2.0",
|
|
||||||
"posthog-js": "^1.131.3",
|
"posthog-js": "^1.131.3",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
@@ -74,7 +74,6 @@
|
|||||||
"@types/dompurify": "^3.0.5",
|
"@types/dompurify": "^3.0.5",
|
||||||
"@types/lodash": "^4.14.202",
|
"@types/lodash": "^4.14.202",
|
||||||
"@types/node": "18.16.1",
|
"@types/node": "18.16.1",
|
||||||
"@types/nprogress": "^0.2.0",
|
|
||||||
"@types/react": "^18.3.11",
|
"@types/react": "^18.3.11",
|
||||||
"@types/react-color": "^3.0.6",
|
"@types/react-color": "^3.0.6",
|
||||||
"@types/react-dom": "^18.2.18",
|
"@types/react-dom": "^18.2.18",
|
||||||
@@ -82,4 +81,4 @@
|
|||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"typescript": "5.8.3"
|
"typescript": "5.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#nprogress {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nprogress .bar {
|
|
||||||
background: rgba(var(--color-primary-100));
|
|
||||||
|
|
||||||
position: fixed;
|
|
||||||
z-index: 1031;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 0.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* for blur effect */
|
|
||||||
#nprogress .peg {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
right: 0px;
|
|
||||||
width: 100px;
|
|
||||||
height: 100%;
|
|
||||||
box-shadow:
|
|
||||||
0 0 10px rgba(var(--color-primary-100)),
|
|
||||||
0 0 5px rgba(var(--color-primary-100));
|
|
||||||
opacity: 1;
|
|
||||||
|
|
||||||
-webkit-transform: rotate(3deg) translate(0px, -4px);
|
|
||||||
-ms-transform: rotate(3deg) translate(0px, -4px);
|
|
||||||
transform: rotate(3deg) translate(0px, -4px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nprogress-custom-parent {
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nprogress-custom-parent #nprogress .bar {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
20
yarn.lock
20
yarn.lock
@@ -256,6 +256,26 @@
|
|||||||
react-popper "^2.3.0"
|
react-popper "^2.3.0"
|
||||||
tslib "~2.5.0"
|
tslib "~2.5.0"
|
||||||
|
|
||||||
|
"@bprogress/core@^1.3.4":
|
||||||
|
version "1.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@bprogress/core/-/core-1.3.4.tgz#4d4b90300d8a3b8d56a1a9876daa3fd4243aeabb"
|
||||||
|
integrity sha512-q/AqpurI/1uJzOrQROuZWixn/+ARekh+uvJGwLCP6HQ/EqAX4SkvNf618tSBxL4NysC0MwqAppb/mRw6Tzi61w==
|
||||||
|
|
||||||
|
"@bprogress/next@^3.2.12":
|
||||||
|
version "3.2.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@bprogress/next/-/next-3.2.12.tgz#c9d4f4cfdf787b1cd481c96d24a9ac509e99db0c"
|
||||||
|
integrity sha512-/ZvNwbAd0ty9QiQwCfT2AfwWVdAaEyCPx5RUz3CfiiJS/OLBohhDz/IC/srhwK9GnXeXavvtiUrpKzN5GJDwlw==
|
||||||
|
dependencies:
|
||||||
|
"@bprogress/core" "^1.3.4"
|
||||||
|
"@bprogress/react" "^1.2.7"
|
||||||
|
|
||||||
|
"@bprogress/react@^1.2.7":
|
||||||
|
version "1.2.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@bprogress/react/-/react-1.2.7.tgz#5f4ee30bf11019b0efdf3d8045b9fcb49c9fdb2d"
|
||||||
|
integrity sha512-MqJfHW+R5CQeWqyqrLxUjdBRHk24Xl63OkBLo5DMWqUqocUikRTfCIc/jtQQbPk7BRfdr5OP3Lx7YlfQ9QOZMQ==
|
||||||
|
dependencies:
|
||||||
|
"@bprogress/core" "^1.3.4"
|
||||||
|
|
||||||
"@chromatic-com/storybook@^1.4.0":
|
"@chromatic-com/storybook@^1.4.0":
|
||||||
version "1.9.0"
|
version "1.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/@chromatic-com/storybook/-/storybook-1.9.0.tgz#d95eb3474783bcc17a830a7627c3f099c1f75ba5"
|
resolved "https://registry.yarnpkg.com/@chromatic-com/storybook/-/storybook-1.9.0.tgz#d95eb3474783bcc17a830a7627c3f099c1f75ba5"
|
||||||
|
|||||||
Reference in New Issue
Block a user