From 0b65f7d4041cf85a3170dd1d3de5cda8417123d4 Mon Sep 17 00:00:00 2001 From: olayway Date: Tue, 17 May 2022 14:38:24 +0200 Subject: [PATCH] [site/components][f]: floating-ui tooltip on hover --- site/components/Anchor.js | 100 ++++++++++++++++++++++++++----- site/components/Content.js | 82 ------------------------- site/components/Tooltip.js | 48 +++++++-------- site/package-lock.json | 119 +++++++++++++++++++++++++++++++++++++ site/package.json | 1 + site/styles/global.css | 12 ---- site/utils/absolutePath.js | 10 ++++ 7 files changed, 236 insertions(+), 136 deletions(-) delete mode 100644 site/components/Content.js create mode 100644 site/utils/absolutePath.js diff --git a/site/components/Anchor.js b/site/components/Anchor.js index bcf3960..7a719b3 100644 --- a/site/components/Anchor.js +++ b/site/components/Anchor.js @@ -1,25 +1,93 @@ -import { useState, Fragment } from 'react'; -import { Tooltip } from './Tooltip'; +import { useRouter } from 'next/router' +import { useState, useEffect, Fragment } from 'react' +import { useFloating, useHover, useInteractions } from '@floating-ui/react-dom-interactions' +import { unified } from 'unified' +import rehypeParse from 'rehype-parse' +import find from 'unist-util-find' +import { toString } from 'hast-util-to-string' + +import { Tooltip } from './Tooltip' +import getAbsolutePath from '../utils/absolutePath' + + +// TODO cancel request on mouseleave when it hasn't been fulfilled yet export const Anchor = (props) => { - const href = props.href; + const { href } = props; + const router = useRouter(); + const [ open, setOpen ] = useState(false); + const [ loaded, setLoaded ] = useState(false); + const [ preview, setPreview ] = useState(""); + const { x, y, reference, floating, strategy, context } = useFloating({ + open, + onOpenChange: setOpen + }); + const { getReferenceProps, getFloatingProps } = useInteractions([ + useHover(context, props) + ]); + + useEffect(() => { + if (open) { + fetchPreview(); + } + }, [open]) + + const fetchPreview = async () => { + setLoaded(false); + const basePath = "http://localhost:3000"; // TODO + const currentPath = router.asPath; + const relativePath = props.href.split(".")[0]; // TBD temp remove .md + const absolutePath = getAbsolutePath({ currentPath, basePath, relativePath }); + console.log(`Fetching: ${absolutePath}`); + + const response = await fetch(absolutePath); + if (response.status !== 200) { + // TODO + console.log(`Looks like there was a problem. Status Code: ${response.status}`) + return + } + const html = await response.text(); + const hast = unified().use(rehypeParse).parse(html); + console.log(hast) + const article = find(hast, (node) => { + return node.tagName === "article" + }) + const main = find(article, (node) => { + return node.tagName === "main" + }) + const p = find(main, (node) => { + return node.tagName === "p" + }) + + setPreview(toString(p)); + setLoaded(true); + } + if ( href && - href.indexOf("http://") !== 0 && - href.indexOf("http://") !== 0 && + href.indexOf("http:") !== 0 && + href.indexOf("https:") !== 0 && href.includes(".md") ) { - return ( - - - - - - ); + return + + {open && loaded && ( + // TODO temp span + + { preview } + + )} + ; } return ; }; diff --git a/site/components/Content.js b/site/components/Content.js deleted file mode 100644 index d809bdf..0000000 --- a/site/components/Content.js +++ /dev/null @@ -1,82 +0,0 @@ -import { useState, useEffect } from 'react' -import { useRouter } from 'next/router' -import {unified} from 'unified' -import rehypeParse from 'rehype-parse' -import find from 'unist-util-find' -import {toString} from 'hast-util-to-string' - -const textStyles = (theme) => ({ - padding: '16px 22px', - fontSize: '11px', - background: theme === 'light' ? '#fff' : '#000', -}) - - -const display = (data, theme) => { - return ( -
-
{data}
- {/*
{wikiLogo(theme)}
*/} - {/* {arrow(theme)} */} -
- ) -} - -const getAbsolutePath = ({ currentPath, basePath, relativePath }) => { - console.log({ currentPath, basePath, relativePath }); - const absolutePath = currentPath.slice(1).split("/") - absolutePath.pop(); // remove current page name - absolutePath.unshift(basePath); - absolutePath.push(relativePath); - console.log(absolutePath); - return absolutePath.join("/"); -} - -export const Content = (props) => { - const [state, setState] = useState({ - data: "", - isLoaded: false, - }) - - const router = useRouter(); - - useEffect(async () => { - const basePath = "http://localhost:3000"; - const currentPath = router.asPath; - const relativePath = props.value.split(".")[0]; // temp remove .md - const absolutePath = getAbsolutePath({ currentPath, basePath, relativePath }); - console.log(absolutePath); - fetch(absolutePath).then((response) => { - if (response.status !== 200) { - console.log(`Looks like there was a problem. Status Code: ${response.status}`) - return - } - response.text().then((data) => { - const hast = unified().use(rehypeParse).parse(data); - console.log(hast) - const article = find(hast, (node) => { - return node.tagName === "article" - }) - const main = find(article, (node) => { - return node.tagName === "main" - }) - const p = find(main, (node) => { - return node.tagName === "p" - }) - setState({ - data: toString(p), - isLoaded: true, - }) - }) - }) - }, []) - - const { theme } = props - const { data, isLoaded } = state - - return isLoaded ? display(data, theme) :
-} diff --git a/site/components/Tooltip.js b/site/components/Tooltip.js index 1cccf1f..7a17402 100644 --- a/site/components/Tooltip.js +++ b/site/components/Tooltip.js @@ -1,32 +1,28 @@ -import Trigger from 'rc-trigger' -import { Content } from './Content' +const tooltipTextStyles = (theme) => ({ + padding: '16px 22px', + fontSize: '11px', + background: theme === 'light' ? '#fff' : '#000', + // color: theme === 'light' ? 'black' : 'white', + pointerEvents: 'none', + borderRadius: '4px', + position: 'absolute' +}) -const position = { - bottom: { - points: ['tc', 'bc'], - offset: [0, 10] - }, - right: { - points: ['tl', 'tc'], - offset: [20, 0] - }, - top: { - points: ['bc', 'tc'], - offset: [0, -10] - } -} +// const tooltipBoxStyles = (theme) => ({ +// color: theme === 'light' ? 'rgb(99, 98, 98)' : '#A8A8A8', +// transition: "0.1s", +// width: "50vw" +// }) export const Tooltip = (props) => { - const { children, value, theme, mouseEnterDelay = 0.5 } = props; + const { theme, content } = props; return ( - } - mouseEnterDelay={mouseEnterDelay} - prefixCls='trigger' - popupAlign={position.bottom} - > - {children} - +
+
+ { content } +
+ {/*
{wikiLogo(theme)}
*/} + {/* {arrow(theme)} */} +
) } diff --git a/site/package-lock.json b/site/package-lock.json index 0b7fa30..f359ab9 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "dependencies": { + "@floating-ui/react-dom-interactions": "^0.6.0", "@headlessui/react": "^1.4.1", "@heroicons/react": "^1.0.4", "@mdx-js/loader": "^2.0.0", @@ -337,6 +338,42 @@ "resolved": "https://registry.npmjs.org/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz", "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==" }, + "node_modules/@floating-ui/core": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.0.tgz", + "integrity": "sha512-W7+i5Suhhvv97WDTW//KqUA43f/2a4abprM1rWqtLM9lIlJ29tbFI8h232SvqunXon0WmKNEKVjbOsgBhTnbLw==" + }, + "node_modules/@floating-ui/dom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.0.tgz", + "integrity": "sha512-PS75dnMg4GdWjDFOiOs15cDzYJpukRNHqQn0ugrBlsrpk2n+y8bwZ24XrsdLSL7kxshmxxr2nTNycLnmRIvV7g==", + "dependencies": { + "@floating-ui/core": "^0.7.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-0.7.0.tgz", + "integrity": "sha512-mpYGykTqwtBYT+ZTQQ2OfZ6wXJNuUgmqqD9ooCgbMRgvul6InFOTtWYvtujps439hmOFiVPm4PoBkEEn5imidg==", + "dependencies": { + "@floating-ui/dom": "^0.5.0", + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom-interactions": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom-interactions/-/react-dom-interactions-0.6.0.tgz", + "integrity": "sha512-8XzQuQStUNztHvg+Rj6MdUjBsOKIb6Oe0eIs1w9LfssOT0ZEPPHVsCZgLiWoyDaotF6pirLGAXkOfQxPM7VBRQ==", + "dependencies": { + "@floating-ui/react-dom": "^0.7.0", + "aria-hidden": "^1.1.3", + "use-isomorphic-layout-effect": "^1.1.1" + } + }, "node_modules/@grpc/grpc-js": { "version": "1.5.9", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.5.9.tgz", @@ -1277,6 +1314,22 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/aria-hidden": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.1.3.tgz", + "integrity": "sha512-RhVWFtKH5BiGMycI72q2RAFMLQi8JP9bLuQXgR5a8Znp7P5KOIADSJeyfI8PCVxLEp067B2HbP5JIiI/PXIZeA==", + "dependencies": { + "tslib": "^1.0.0" + }, + "engines": { + "node": ">=8.5.0" + } + }, + "node_modules/aria-hidden/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/array-iterate": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-1.1.4.tgz", @@ -5850,6 +5903,19 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-subscription": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz", @@ -6291,6 +6357,38 @@ "resolved": "https://registry.npmjs.org/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz", "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==" }, + "@floating-ui/core": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.0.tgz", + "integrity": "sha512-W7+i5Suhhvv97WDTW//KqUA43f/2a4abprM1rWqtLM9lIlJ29tbFI8h232SvqunXon0WmKNEKVjbOsgBhTnbLw==" + }, + "@floating-ui/dom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.0.tgz", + "integrity": "sha512-PS75dnMg4GdWjDFOiOs15cDzYJpukRNHqQn0ugrBlsrpk2n+y8bwZ24XrsdLSL7kxshmxxr2nTNycLnmRIvV7g==", + "requires": { + "@floating-ui/core": "^0.7.0" + } + }, + "@floating-ui/react-dom": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-0.7.0.tgz", + "integrity": "sha512-mpYGykTqwtBYT+ZTQQ2OfZ6wXJNuUgmqqD9ooCgbMRgvul6InFOTtWYvtujps439hmOFiVPm4PoBkEEn5imidg==", + "requires": { + "@floating-ui/dom": "^0.5.0", + "use-isomorphic-layout-effect": "^1.1.1" + } + }, + "@floating-ui/react-dom-interactions": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom-interactions/-/react-dom-interactions-0.6.0.tgz", + "integrity": "sha512-8XzQuQStUNztHvg+Rj6MdUjBsOKIb6Oe0eIs1w9LfssOT0ZEPPHVsCZgLiWoyDaotF6pirLGAXkOfQxPM7VBRQ==", + "requires": { + "@floating-ui/react-dom": "^0.7.0", + "aria-hidden": "^1.1.3", + "use-isomorphic-layout-effect": "^1.1.1" + } + }, "@grpc/grpc-js": { "version": "1.5.9", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.5.9.tgz", @@ -6947,6 +7045,21 @@ "sprintf-js": "~1.0.2" } }, + "aria-hidden": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.1.3.tgz", + "integrity": "sha512-RhVWFtKH5BiGMycI72q2RAFMLQi8JP9bLuQXgR5a8Znp7P5KOIADSJeyfI8PCVxLEp067B2HbP5JIiI/PXIZeA==", + "requires": { + "tslib": "^1.0.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, "array-iterate": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-1.1.4.tgz", @@ -9961,6 +10074,12 @@ "unist-util-is": "^5.0.0" } }, + "use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "requires": {} + }, "use-subscription": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz", diff --git a/site/package.json b/site/package.json index a7834b0..b618c5f 100644 --- a/site/package.json +++ b/site/package.json @@ -7,6 +7,7 @@ "start": "next start" }, "dependencies": { + "@floating-ui/react-dom-interactions": "^0.6.0", "@headlessui/react": "^1.4.1", "@heroicons/react": "^1.0.4", "@mdx-js/loader": "^2.0.0", diff --git a/site/styles/global.css b/site/styles/global.css index 907ec95..0387224 100644 --- a/site/styles/global.css +++ b/site/styles/global.css @@ -27,15 +27,3 @@ body { .extra-small { font-size: 0.50rem; } - -/* TOOLTIP */ -.trigger { - position: absolute; - opacity: 1; - transition: 0.1s; - -} -.trigger-hidden { - opacity: 0; - visibility: hidden; -} diff --git a/site/utils/absolutePath.js b/site/utils/absolutePath.js new file mode 100644 index 0000000..d12126a --- /dev/null +++ b/site/utils/absolutePath.js @@ -0,0 +1,10 @@ +const absolutePath = ({ currentPath, basePath, relativePath }) => { + const absolutePath = currentPath.slice(1).split("/") + absolutePath.pop(); // remove current page name + absolutePath.unshift(basePath); + absolutePath.push(relativePath); + console.log(absolutePath); + return absolutePath.join("/"); +}; + +export default absolutePath;