[site/components][f]: floating-ui tooltip on hover
This commit is contained in:
parent
1ba2193870
commit
0b65f7d404
|
|
@ -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 (
|
||||
<Fragment key={href}>
|
||||
<Tooltip
|
||||
theme="light"
|
||||
mouseEnterDelay="0.5"
|
||||
value={href}
|
||||
>
|
||||
<a {...props} />
|
||||
</Tooltip>
|
||||
</Fragment>
|
||||
);
|
||||
return <Fragment>
|
||||
<a {...props} {...getReferenceProps({ref: reference})} />
|
||||
{open && loaded && (
|
||||
// TODO temp span
|
||||
<span
|
||||
{...getFloatingProps({
|
||||
ref: floating,
|
||||
className: "tooltip",
|
||||
style: {
|
||||
position: strategy,
|
||||
left: x ?? '',
|
||||
top: y ?? '',
|
||||
},
|
||||
})}
|
||||
>
|
||||
{ preview }
|
||||
</span>
|
||||
)}
|
||||
</Fragment>;
|
||||
}
|
||||
return <a {...props} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div
|
||||
style={{
|
||||
color: theme === 'light' ? 'rgb(99, 98, 98)' : '#A8A8A8',
|
||||
}}
|
||||
>
|
||||
<div style={textStyles(theme)}>{data}</div>
|
||||
{/* <div style={footer(theme)}>{wikiLogo(theme)}</div> */}
|
||||
{/* {arrow(theme)} */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
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) : <div />
|
||||
}
|
||||
|
|
@ -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 (
|
||||
<Trigger
|
||||
action={['hover']}
|
||||
popup={<Content theme={theme} value={value} />}
|
||||
mouseEnterDelay={mouseEnterDelay}
|
||||
prefixCls='trigger'
|
||||
popupAlign={position.bottom}
|
||||
>
|
||||
{children}
|
||||
</Trigger>
|
||||
<div>
|
||||
<div style={ tooltipTextStyles(theme) }>
|
||||
{ content }
|
||||
</div>
|
||||
{/* <div style={footer(theme)}>{wikiLogo(theme)}</div> */}
|
||||
{/* {arrow(theme)} */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
Loading…
Reference in New Issue