Allow middle click to open new tab
This commit is contained in:
parent
c2c434eb80
commit
82c605086b
|
@ -24,5 +24,14 @@ module.exports = {
|
||||||
hreflang: "ja",
|
hreflang: "ja",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
exclude: ["/en/*", "/fr/*", "/ja/*", "/es/*", "/pt-br/*", "/404", "/500", "/dev/*"],
|
exclude: [
|
||||||
|
"/en/*",
|
||||||
|
"/fr/*",
|
||||||
|
"/ja/*",
|
||||||
|
"/es/*",
|
||||||
|
"/pt-br/*",
|
||||||
|
"/404",
|
||||||
|
"/500",
|
||||||
|
"/dev/*",
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
|
||||||
<link rel="manifest" href="/site.webmanifest" />
|
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#9c6644" />
|
|
||||||
<meta name="apple-mobile-web-app-title" content="Accord's Library" />
|
|
||||||
<meta name="application-name" content="Accord's Library" />
|
|
||||||
<meta name="msapplication-TileColor" content="#feecd6" />
|
|
||||||
<meta name="msapplication-TileImage" content="/mstile-144x144.png" />
|
|
||||||
<meta name="theme-color" content="#feecd6" />
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Link from "next/link";
|
import { Link } from "components/Inputs/Link";
|
||||||
import { DatePickerFragment } from "graphql/generated";
|
import { DatePickerFragment } from "graphql/generated";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
|
|
||||||
|
@ -20,17 +20,14 @@ export const ChroniclePreview = ({
|
||||||
title,
|
title,
|
||||||
isActive,
|
isActive,
|
||||||
}: Props): JSX.Element => (
|
}: Props): JSX.Element => (
|
||||||
<Link href={url}>
|
<Link
|
||||||
<div
|
href={url}
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
`flex w-full cursor-pointer gap-4 rounded-2xl py-4 px-5
|
`flex w-full cursor-pointer gap-4 rounded-2xl py-4 px-5
|
||||||
text-left align-top outline outline-2 outline-offset-[-2px] outline-mid transition-all
|
text-left align-top outline outline-2 outline-offset-[-2px] outline-mid transition-all
|
||||||
hover:bg-mid hover:shadow-inner-sm hover:shadow-shade
|
hover:bg-mid hover:shadow-inner-sm hover:shadow-shade
|
||||||
hover:outline-[transparent] hover:active:shadow-inner hover:active:shadow-shade`,
|
hover:outline-[transparent] hover:active:shadow-inner hover:active:shadow-shade`,
|
||||||
cIf(
|
cIf(isActive, "bg-mid shadow-inner-sm shadow-shade outline-[transparent]")
|
||||||
isActive,
|
|
||||||
"bg-mid shadow-inner-sm shadow-shade outline-[transparent]"
|
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
|
@ -40,7 +37,6 @@ export const ChroniclePreview = ({
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-lg leading-tight">{title}</p>
|
<p className="text-lg leading-tight">{title}</p>
|
||||||
</div>
|
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import React, { MouseEventHandler } from "react";
|
import React, { MouseEventHandler } from "react";
|
||||||
|
import { Link } from "./Link";
|
||||||
import { Ico, Icon } from "components/Ico";
|
import { Ico, Icon } from "components/Ico";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { ConditionalWrapper, Wrapper } from "helpers/component";
|
import { ConditionalWrapper, Wrapper } from "helpers/component";
|
||||||
|
@ -17,8 +17,7 @@ interface Props {
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
icon?: Icon;
|
icon?: Icon;
|
||||||
text?: string | null | undefined;
|
text?: string | null | undefined;
|
||||||
locale?: string;
|
alwaysNewTab?: boolean;
|
||||||
target?: "_blank";
|
|
||||||
onClick?: MouseEventHandler<HTMLDivElement>;
|
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||||
draggable?: boolean;
|
draggable?: boolean;
|
||||||
badgeNumber?: number;
|
badgeNumber?: number;
|
||||||
|
@ -35,30 +34,17 @@ export const Button = ({
|
||||||
className,
|
className,
|
||||||
icon,
|
icon,
|
||||||
text,
|
text,
|
||||||
target,
|
|
||||||
href,
|
href,
|
||||||
locale,
|
alwaysNewTab = false,
|
||||||
badgeNumber,
|
badgeNumber,
|
||||||
size = "normal",
|
size = "normal",
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => (
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ConditionalWrapper
|
<ConditionalWrapper
|
||||||
isWrapping={isDefined(target)}
|
isWrapping={isDefinedAndNotEmpty(href)}
|
||||||
wrapperProps={{ href: href }}
|
wrapperProps={{ href: href ?? "", alwaysNewTab }}
|
||||||
wrapper={LinkWrapper}
|
wrapper={LinkWrapper}
|
||||||
>
|
>
|
||||||
<div
|
<div className="relative">
|
||||||
className="relative"
|
|
||||||
onClick={() => {
|
|
||||||
if (!isDefined(target) && (isDefined(href) || isDefined(locale))) {
|
|
||||||
router.push(href ?? router.asPath, href, {
|
|
||||||
locale: locale,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
draggable={draggable}
|
draggable={draggable}
|
||||||
id={id}
|
id={id}
|
||||||
|
@ -98,7 +84,6 @@ export const Button = ({
|
||||||
</div>
|
</div>
|
||||||
</ConditionalWrapper>
|
</ConditionalWrapper>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭──────────────────────╮
|
* ╭──────────────────────╮
|
||||||
|
@ -106,11 +91,16 @@ export const Button = ({
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface LinkWrapperProps {
|
interface LinkWrapperProps {
|
||||||
href?: string;
|
href: string;
|
||||||
|
alwaysNewTab: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LinkWrapper = ({ children, href }: LinkWrapperProps & Wrapper) => (
|
const LinkWrapper = ({
|
||||||
<a href={href} target="_blank" rel="noreferrer">
|
children,
|
||||||
|
alwaysNewTab,
|
||||||
|
href,
|
||||||
|
}: LinkWrapperProps & Wrapper) => (
|
||||||
|
<Link href={href} alwaysNewTab={alwaysNewTab}>
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
import router from "next/router";
|
||||||
|
import { MouseEventHandler, useState } from "react";
|
||||||
|
import { isDefined } from "helpers/others";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
href: string;
|
||||||
|
className?: string;
|
||||||
|
allowNewTab?: boolean;
|
||||||
|
alwaysNewTab?: boolean;
|
||||||
|
children: React.ReactNode;
|
||||||
|
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Link = ({
|
||||||
|
href,
|
||||||
|
allowNewTab = true,
|
||||||
|
alwaysNewTab = false,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
}: Props): JSX.Element => {
|
||||||
|
const [isValidClick, setIsValidClick] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={className}
|
||||||
|
onMouseLeave={() => setIsValidClick(false)}
|
||||||
|
onContextMenu={(event) => event.preventDefault()}
|
||||||
|
onMouseDown={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setIsValidClick(true);
|
||||||
|
}}
|
||||||
|
onMouseUp={(event) => {
|
||||||
|
if (isDefined(onClick)) {
|
||||||
|
onClick(event);
|
||||||
|
} else if (isValidClick && href) {
|
||||||
|
if (event.button !== MouseButton.Right) {
|
||||||
|
if (alwaysNewTab) {
|
||||||
|
window.open(href, "_blank");
|
||||||
|
} else if (event.button === MouseButton.Left) {
|
||||||
|
if (href.startsWith("#")) {
|
||||||
|
router.replace(href);
|
||||||
|
} else {
|
||||||
|
router.push(href);
|
||||||
|
}
|
||||||
|
} else if (allowNewTab) {
|
||||||
|
window.open(href, "_blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MouseButton {
|
||||||
|
Left = 0,
|
||||||
|
Middle = 1,
|
||||||
|
Right = 2,
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import { Ico, Icon } from "components/Ico";
|
||||||
import { ToolTip } from "components/ToolTip";
|
import { ToolTip } from "components/ToolTip";
|
||||||
import { cJoin, cIf } from "helpers/className";
|
import { cJoin, cIf } from "helpers/className";
|
||||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||||
|
import { Link } from "components/Inputs/Link";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -51,17 +52,9 @@ export const NavOption = ({
|
||||||
className="text-left"
|
className="text-left"
|
||||||
disabled={!reduced}
|
disabled={!reduced}
|
||||||
>
|
>
|
||||||
<div
|
<Link
|
||||||
onClick={(event) => {
|
href={url}
|
||||||
if (onClick) onClick(event);
|
onClick={onClick}
|
||||||
if (url) {
|
|
||||||
if (url.startsWith("#")) {
|
|
||||||
router.replace(url);
|
|
||||||
} else {
|
|
||||||
router.push(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
`relative grid w-full cursor-pointer auto-cols-fr grid-flow-col grid-cols-[auto]
|
`relative grid w-full cursor-pointer auto-cols-fr grid-flow-col grid-cols-[auto]
|
||||||
justify-center gap-x-5 rounded-2xl p-4 transition-all hover:bg-mid hover:shadow-inner-sm
|
justify-center gap-x-5 rounded-2xl p-4 transition-all hover:bg-mid hover:shadow-inner-sm
|
||||||
|
@ -84,7 +77,7 @@ export const NavOption = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Link>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import Markdown from "markdown-to-jsx";
|
import Markdown from "markdown-to-jsx";
|
||||||
import Link from "next/link";
|
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
import { NavOption } from "components/PanelComponents/NavOption";
|
import { NavOption } from "components/PanelComponents/NavOption";
|
||||||
|
@ -11,6 +10,7 @@ import { useMediaDesktop } from "hooks/useMediaQuery";
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||||
|
import { Link } from "components/Inputs/Link";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -54,7 +54,7 @@ export const MainPanel = ({ langui }: Props): JSX.Element => {
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className="grid place-items-center">
|
<div className="grid place-items-center">
|
||||||
<Link href="/" passHref>
|
<Link href="/" className="flex w-full justify-center">
|
||||||
<div
|
<div
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
`mb-4 aspect-square cursor-pointer bg-black transition-colors
|
`mb-4 aspect-square cursor-pointer bg-black transition-colors
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Dispatch, SetStateAction, useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import Hotkeys from "react-hot-keys";
|
import Hotkeys from "react-hot-keys";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import Link from "next/link";
|
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { Chip } from "./Chip";
|
import { Chip } from "./Chip";
|
||||||
import { Ico, Icon } from "./Ico";
|
import { Ico, Icon } from "./Ico";
|
||||||
import { Img } from "./Img";
|
import { Img } from "./Img";
|
||||||
|
import { Link } from "./Inputs/Link";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||||
import {
|
import {
|
||||||
DatePickerFragment,
|
DatePickerFragment,
|
||||||
|
@ -131,8 +131,8 @@ export const PreviewCard = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={href} passHref>
|
<Link
|
||||||
<div
|
href={href}
|
||||||
className="group grid cursor-pointer items-end text-left transition-transform
|
className="group grid cursor-pointer items-end text-left transition-transform
|
||||||
drop-shadow-shade-xl hover:scale-[1.02]"
|
drop-shadow-shade-xl hover:scale-[1.02]"
|
||||||
>
|
>
|
||||||
|
@ -293,7 +293,6 @@ export const PreviewCard = ({
|
||||||
|
|
||||||
{infoAppend}
|
{infoAppend}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Link from "next/link";
|
|
||||||
import { Chip } from "./Chip";
|
import { Chip } from "./Chip";
|
||||||
import { Img } from "./Img";
|
import { Img } from "./Img";
|
||||||
|
import { Link } from "./Inputs/Link";
|
||||||
import { UploadImageFragment } from "graphql/generated";
|
import { UploadImageFragment } from "graphql/generated";
|
||||||
import { ImageQuality } from "helpers/img";
|
import { ImageQuality } from "helpers/img";
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@ export const PreviewLine = ({
|
||||||
bottomChips,
|
bottomChips,
|
||||||
thumbnailAspectRatio,
|
thumbnailAspectRatio,
|
||||||
}: Props): JSX.Element => (
|
}: Props): JSX.Element => (
|
||||||
<Link href={href} passHref>
|
<Link
|
||||||
<div
|
href={href}
|
||||||
className="flex h-36 w-full cursor-pointer flex-row place-items-center gap-4 overflow-hidden
|
className="flex h-36 w-full cursor-pointer flex-row place-items-center gap-4 overflow-hidden
|
||||||
rounded-md bg-light pr-4 transition-transform drop-shadow-shade-xl hover:scale-[1.02]"
|
rounded-md bg-light pr-4 transition-transform drop-shadow-shade-xl hover:scale-[1.02]"
|
||||||
>
|
>
|
||||||
|
@ -59,9 +59,7 @@ export const PreviewLine = ({
|
||||||
<div className="my-1 flex flex-col">
|
<div className="my-1 flex flex-col">
|
||||||
{pre_title && <p className="mb-1 leading-none">{pre_title}</p>}
|
{pre_title && <p className="mb-1 leading-none">{pre_title}</p>}
|
||||||
{title && (
|
{title && (
|
||||||
<p className="font-headers text-lg font-bold leading-none">
|
<p className="font-headers text-lg font-bold leading-none">{title}</p>
|
||||||
{title}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
{subtitle && <p className="leading-none">{subtitle}</p>}
|
{subtitle && <p className="leading-none">{subtitle}</p>}
|
||||||
</div>
|
</div>
|
||||||
|
@ -73,6 +71,5 @@ export const PreviewLine = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import Link from "next/link";
|
|
||||||
import { Chip } from "components/Chip";
|
import { Chip } from "components/Chip";
|
||||||
import { ToolTip } from "components/ToolTip";
|
import { ToolTip } from "components/ToolTip";
|
||||||
import { AppStaticProps } from "graphql/getAppStaticProps";
|
import { AppStaticProps } from "graphql/getAppStaticProps";
|
||||||
|
@ -87,12 +86,10 @@ const DefinitionCard = ({
|
||||||
<p>{selectedTranslation?.definition}</p>
|
<p>{selectedTranslation?.definition}</p>
|
||||||
|
|
||||||
{source?.url && source.name && (
|
{source?.url && source.name && (
|
||||||
<Link href={source.url}>
|
|
||||||
<div className="mt-3 flex place-items-center gap-2 mobile:flex-col mobile:text-center">
|
<div className="mt-3 flex place-items-center gap-2 mobile:flex-col mobile:text-center">
|
||||||
<p>{langui.source}: </p>
|
<p>{langui.source}: </p>
|
||||||
<Button size="small" text={source.name} />
|
<Button href={source.url} size="small" text={source.name} />
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -53,15 +53,15 @@ const CheckupContents = ({ contents, ...otherProps }: Props): JSX.Element => {
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
href={line.frontendUrl}
|
href={line.frontendUrl}
|
||||||
target="_blank"
|
|
||||||
className="w-4 text-xs"
|
className="w-4 text-xs"
|
||||||
text="F"
|
text="F"
|
||||||
|
alwaysNewTab
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
href={line.backendUrl}
|
href={line.backendUrl}
|
||||||
target="_blank"
|
|
||||||
className="w-4 text-xs"
|
className="w-4 text-xs"
|
||||||
text="B"
|
text="B"
|
||||||
|
alwaysNewTab
|
||||||
/>
|
/>
|
||||||
<p>{line.subitems.join(" -> ")}</p>
|
<p>{line.subitems.join(" -> ")}</p>
|
||||||
<p>{line.name}</p>
|
<p>{line.name}</p>
|
||||||
|
|
|
@ -58,15 +58,15 @@ const CheckupLibraryItems = ({
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
href={line.frontendUrl}
|
href={line.frontendUrl}
|
||||||
target="_blank"
|
|
||||||
className="w-4 text-xs"
|
className="w-4 text-xs"
|
||||||
text="F"
|
text="F"
|
||||||
|
alwaysNewTab
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
href={line.backendUrl}
|
href={line.backendUrl}
|
||||||
target="_blank"
|
|
||||||
className="w-4 text-xs"
|
className="w-4 text-xs"
|
||||||
text="B"
|
text="B"
|
||||||
|
alwaysNewTab
|
||||||
/>
|
/>
|
||||||
<p>{line.subitems.join(" -> ")}</p>
|
<p>{line.subitems.join(" -> ")}</p>
|
||||||
<p>{line.name}</p>
|
<p>{line.name}</p>
|
||||||
|
|
|
@ -230,8 +230,8 @@ const LibrarySlug = ({
|
||||||
<Fragment key={index}>
|
<Fragment key={index}>
|
||||||
<Button
|
<Button
|
||||||
href={url.url}
|
href={url.url}
|
||||||
target={"_blank"}
|
|
||||||
text={prettyURL(url.url)}
|
text={prettyURL(url.url)}
|
||||||
|
alwaysNewTab
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue