diff --git a/package-lock.json b/package-lock.json index 6ef716e..e9fc868 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@fontsource/opendyslexic": "^4.5.2", "@fontsource/vollkorn": "^4.5.4", "@fontsource/zen-maru-gothic": "^4.5.5", + "@tippyjs/react": "^4.2.6", "markdown-to-jsx": "^7.1.7", "next": "^12.1.0", "react": "17.0.2", @@ -447,12 +448,34 @@ "node": ">= 8" } }, + "node_modules/@popperjs/core": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.3.tgz", + "integrity": "sha512-8U7hIl7+30XbIrJ0deQMXpXESM1L4yrt6BHok5hzcR0LivivuNkk+tHU1iRVScOwCmQcrOr6kvtIr29MNbQHqQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz", "integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==", "dev": true }, + "node_modules/@tippyjs/react": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz", + "integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==", + "dependencies": { + "tippy.js": "^6.3.1" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -3409,6 +3432,14 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "node_modules/tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "dependencies": { + "@popperjs/core": "^2.9.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3894,12 +3925,25 @@ "fastq": "^1.6.0" } }, + "@popperjs/core": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.3.tgz", + "integrity": "sha512-8U7hIl7+30XbIrJ0deQMXpXESM1L4yrt6BHok5hzcR0LivivuNkk+tHU1iRVScOwCmQcrOr6kvtIr29MNbQHqQ==" + }, "@rushstack/eslint-patch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz", "integrity": "sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A==", "dev": true }, + "@tippyjs/react": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz", + "integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==", + "requires": { + "tippy.js": "^6.3.1" + } + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -6013,6 +6057,14 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "requires": { + "@popperjs/core": "^2.9.0" + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index f46c44f..07b9196 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@fontsource/opendyslexic": "^4.5.2", "@fontsource/vollkorn": "^4.5.4", "@fontsource/zen-maru-gothic": "^4.5.5", + "@tippyjs/react": "^4.2.6", "markdown-to-jsx": "^7.1.7", "next": "^12.1.0", "react": "17.0.2", diff --git a/src/components/Chip.tsx b/src/components/Chip.tsx index 2c41af4..e3719a1 100644 --- a/src/components/Chip.tsx +++ b/src/components/Chip.tsx @@ -3,16 +3,12 @@ import { MouseEventHandler } from "react"; type ChipProps = { className?: string; children: React.ReactNode; - onMouseEnter?: MouseEventHandler; - onMouseLeave?: MouseEventHandler; }; export default function Chip(props: ChipProps): JSX.Element { return (
{props.children}
diff --git a/src/components/Chronology/ChronologyItemComponent.tsx b/src/components/Chronology/ChronologyItemComponent.tsx index 4e1bcad..81f1504 100644 --- a/src/components/Chronology/ChronologyItemComponent.tsx +++ b/src/components/Chronology/ChronologyItemComponent.tsx @@ -6,7 +6,6 @@ import { GetWebsiteInterfaceQuery, } from "graphql/operations-types"; import { getStatusDescription } from "queries/helpers"; -import { useState } from "react"; export type ChronologyItemComponentProps = { item: GetChronologyItemsQuery["chronologyItems"]["data"][number]; @@ -18,7 +17,6 @@ export default function ChronologyItemComponent( props: ChronologyItemComponentProps ): JSX.Element { const { langui } = props; - const [statusHovered, setStatusHovered] = useState(false); function generateAnchor(year: number, month: number, day: number): string { let result: string = ""; @@ -93,23 +91,12 @@ export default function ChronologyItemComponent(
{translation.status !== Enum_Componenttranslationschronologyitem_Status.Done && ( - setStatusHovered(true)} - onMouseLeave={() => setStatusHovered(false)} + - {translation.status} - -

- {getStatusDescription(translation.status, langui)} -

-
-
+ {translation.status} + )} {translation.title ?

{translation.title}

: ""}
diff --git a/src/components/PanelComponents/NavOption.tsx b/src/components/PanelComponents/NavOption.tsx index 4d9660f..457e0dc 100644 --- a/src/components/PanelComponents/NavOption.tsx +++ b/src/components/PanelComponents/NavOption.tsx @@ -1,6 +1,6 @@ import { useRouter } from "next/router"; import Link from "next/link"; -import { MouseEventHandler, useState } from "react"; +import { MouseEventHandler } from "react"; import ToolTip from "components/ToolTip"; type NavOptionProps = { @@ -23,34 +23,39 @@ export default function NavOption(props: NavOptionProps): JSX.Element { props.border ? border : "" } ${isActive ? divActive : ""}`; - const [hovered, setHovered] = useState(false); - return ( - -
props.reduced && setHovered(true)} - onMouseLeave={() => setHovered(false)} - className={`relative grid grid-flow-col grid-cols-[auto] auto-cols-fr justify-center ${ - props.icon ? "text-left" : "text-center" - } ${divCommon}`} - > - {props.icon && ( - {props.icon} - )} - - {!props.reduced && ( -
-

{props.title}

- {props.subtitle &&

{props.subtitle}

} -
- )} - - +

{props.title}

{props.subtitle &&

{props.subtitle}

} -
-
- + + } + placement="right" + className="text-left" + disabled={!props.reduced} + > + +
+ {props.icon && ( + {props.icon} + )} + + {!props.reduced && ( +
+

{props.title}

+ {props.subtitle && ( +

{props.subtitle}

+ )} +
+ )} +
+ + ); } diff --git a/src/components/Panels/MainPanel.tsx b/src/components/Panels/MainPanel.tsx index 81cb786..a705e48 100644 --- a/src/components/Panels/MainPanel.tsx +++ b/src/components/Panels/MainPanel.tsx @@ -7,6 +7,7 @@ import { GetWebsiteInterfaceQuery } from "graphql/operations-types"; import Markdown from "markdown-to-jsx"; import { useMediaDesktop } from "hooks/useMediaQuery"; import { useAppLayout } from "contexts/AppLayoutContext"; +import ToolTip from "components/ToolTip"; type MainPanelProps = { langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]; @@ -62,49 +63,74 @@ export default function MainPanel(props: MainPanelProps): JSX.Element { : "flex-row" } flex-wrap gap-2`} > - - - {router.locale && ( + + + {router.locale && ( + {"Change language"}} + placement="right" + className="text-left" + disabled={!appLayout.mainPanelReduced} + > + + )} - + + search + + + diff --git a/src/components/RecorderChip.tsx b/src/components/RecorderChip.tsx index 257d5c5..2a61174 100644 --- a/src/components/RecorderChip.tsx +++ b/src/components/RecorderChip.tsx @@ -3,7 +3,6 @@ import { GetContentTextQuery, GetWebsiteInterfaceQuery, } from "graphql/operations-types"; -import { useState } from "react"; import Img, { ImageQuality } from "./Img"; import ToolTip from "./ToolTip"; @@ -17,24 +16,9 @@ export default function RecorderChip(props: RecorderChipProps): JSX.Element { const recorder = props.recorder; const langui = props.langui; - const [hovered, setHovered] = useState(false); - return ( - setHovered(true)} - onMouseLeave={() => setHovered(false)} - > - {recorder.attributes.anonymize - ? `Recorder#${recorder.attributes.anonymous_code}` - : recorder.attributes.username} - - +
{recorder.attributes.avatar.data && ( @@ -68,7 +52,14 @@ export default function RecorderChip(props: RecorderChipProps): JSX.Element { recorder.attributes.bio[0].bio}

-
-
+ } + placement="top" + > + + {recorder.attributes.anonymize + ? `Recorder#${recorder.attributes.anonymous_code}` + : recorder.attributes.username} + + ); } diff --git a/src/components/ToolTip.tsx b/src/components/ToolTip.tsx index eeadec6..ccf0400 100644 --- a/src/components/ToolTip.tsx +++ b/src/components/ToolTip.tsx @@ -1,82 +1,17 @@ -import { useEffect, useState } from "react"; +import Tippy, { TippyProps } from "@tippyjs/react"; -type ToolTipProps = { - hovered: boolean; - children: React.ReactNode; - direction: "right" | "bottom" | "top" | "left"; - offset: string; - delayShow?: number; - maxWidth?: "max-w-[10rem]" | "max-w-xs" | "max-w-sm" | "max-w-md"; -}; +interface ToolTipProps extends TippyProps {} export default function ToolTip(props: ToolTipProps): JSX.Element { - const { children, hovered, direction, offset } = props; - let { delayShow, maxWidth } = props; - if (delayShow === undefined) delayShow = 300; - if (maxWidth === undefined) maxWidth = "max-w-sm"; + let newProps = { ...props }; - const [show, setShow] = useState(false); - - useEffect(() => { - let timeout = setTimeout(() => {}); - if (hovered) { - timeout = setTimeout(() => hovered && setShow(true), delayShow); - } else { - setShow(false); - } - return () => clearTimeout(timeout); - }, [delayShow, hovered]); - - let tooltipCSS = ""; - let transformCSS = ""; - let arrowParentCSS = ""; - let arrowCSS = ""; - - switch (direction) { - case "left": - tooltipCSS = "[justify-self:end] [align-self:center]"; - transformCSS = `translateX(-${offset})`; - arrowParentCSS = "w-4 -right-4 top-0 bottom-0"; - arrowCSS = "border-l-light"; - break; - - case "right": - tooltipCSS = "[justify-self:start] [align-self:center]"; - transformCSS = `translateX(${offset})`; - arrowParentCSS = "w-4 -left-4 top-0 bottom-0"; - arrowCSS = "border-r-light"; - break; - - case "top": - tooltipCSS = "[align-self:end]"; - transformCSS = `translateY(-${offset})`; - arrowParentCSS = "h-4 -bottom-4 left-0 right-0"; - arrowCSS = "border-t-light"; - break; - - case "bottom": - tooltipCSS = "[align-self:start]"; - transformCSS = `translateY(${offset})`; - arrowParentCSS = "h-4 -top-4 left-0 right-0"; - arrowCSS = "border-b-light"; - break; - } + // Set defaults + if (newProps.delay === undefined) newProps.delay = [150, 0]; + if (newProps.interactive === undefined) newProps.interactive = true; return ( -
-
-
-
-
{children}
-
+ +
{props.children}
+
); } diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 1d10540..b073451 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -2,7 +2,7 @@ import type { AppProps } from "next/app"; import "tailwind.css"; import "@fontsource/zen-maru-gothic/500.css"; import "@fontsource/vollkorn/700.css"; -import "@fontsource/opendyslexic/400.css" +import "@fontsource/opendyslexic/400.css"; import "@fontsource/material-icons"; import { AppContextProvider } from "contexts/AppLayoutContext"; diff --git a/src/pages/contents/[slug]/read.tsx b/src/pages/contents/[slug]/read.tsx index b45f324..774c85e 100644 --- a/src/pages/contents/[slug]/read.tsx +++ b/src/pages/contents/[slug]/read.tsx @@ -28,7 +28,6 @@ import RecorderChip from "components/RecorderChip"; import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps"; import TOC from "components/Markdown/TOC"; import ToolTip from "components/ToolTip"; -import { useState } from "react"; interface ContentReadProps extends AppStaticProps { content: GetContentTextQuery["contents"]["data"][number]["attributes"]; @@ -40,8 +39,6 @@ export default function ContentRead(props: ContentReadProps): JSX.Element { const { langui, content, languages } = props; const router = useRouter(); - const [statusHovered, setStatusHovered] = useState(false); - const subPanel = (

{langui.status}:

- setStatusHovered(true)} - onMouseLeave={() => setStatusHovered(false)} + - {content.text_set[0].status} - -

- {getStatusDescription(content.text_set[0].status, langui)} -

-
-
+ {content.text_set[0].status} +
{content.text_set[0].transcribers.data.length > 0 && ( diff --git a/src/tailwind.css b/src/tailwind.css index 7f30142..e4a8203 100644 --- a/src/tailwind.css +++ b/src/tailwind.css @@ -25,7 +25,7 @@ } mark { - @apply bg-mid px-2 + @apply bg-mid px-2; } /* SCROLLBARS STYLING */ @@ -142,3 +142,66 @@ @apply [background-image:var(--theme-texture-dots)] [background-blend-mode:var(--theme-texture-dots-blend)] bg-local bg-[length:10cm]; } } + +.tippy-box[data-animation="fade"][data-state="hidden"] { + @apply opacity-0; +} +[data-tippy-root] { + max-width: calc(100vw - 10px); +} +.tippy-box { + @apply relative bg-light drop-shadow-shade-xl rounded-lg transition-[transform,_visibility,_opacity]; +} +.tippy-box[data-placement^="top"] > .tippy-arrow { + @apply bottom-0; +} +.tippy-box[data-placement^="top"] > .tippy-arrow:before { + bottom: -7px; + left: 0; + border-width: 8px 8px 0; + border-top-color: initial; + transform-origin: center top; +} +.tippy-box[data-placement^="bottom"] > .tippy-arrow { + top: 0; +} +.tippy-box[data-placement^="bottom"] > .tippy-arrow:before { + top: -7px; + left: 0; + border-width: 0 8px 8px; + border-bottom-color: initial; + transform-origin: center bottom; +} +.tippy-box[data-placement^="left"] > .tippy-arrow { + right: 0; +} +.tippy-box[data-placement^="left"] > .tippy-arrow:before { + border-width: 8px 0 8px 8px; + border-left-color: initial; + right: -7px; + transform-origin: center left; +} +.tippy-box[data-placement^="right"] > .tippy-arrow { + left: 0; +} +.tippy-box[data-placement^="right"] > .tippy-arrow:before { + left: -7px; + border-width: 8px 8px 8px 0; + border-right-color: initial; + transform-origin: center right; +} +.tippy-box[data-inertia][data-state="visible"] { + transition-timing-function: cubic-bezier(0.54, 1.5, 0.38, 1.11); +} +.tippy-arrow { + @apply h-4 w-4 text-light; +} +.tippy-arrow:before { + content: ""; + position: absolute; + border-color: transparent; + border-style: solid; +} +.tippy-content { + @apply relative px-6 py-4 z-10; +}