slate/components/core/MarkdownFrame.js

188 lines
4.5 KiB
JavaScript
Raw Normal View History

2021-02-16 21:03:59 +03:00
import "isomorphic-fetch";
2021-02-18 10:30:25 +03:00
import * as React from "react";
2021-02-18 20:39:44 +03:00
import * as Strings from "~/common/strings";
2021-02-18 10:30:25 +03:00
import { css } from "@emotion/react";
2021-02-16 21:03:59 +03:00
import { Markdown } from "~/components/system/components/Markdown";
2021-07-07 23:50:57 +03:00
import { H1, H2, H3, H4, P1, UL, OL, LI, A } from "~/components/system/components/Typography";
2021-02-16 21:03:59 +03:00
2021-02-18 20:39:44 +03:00
const STYLES_ASSET = (theme) => css`
padding: 120px calc(32px + 16px + 8px);
position: relative;
width: 100%;
height: 100%;
overflow-y: scroll;
will-change: transform;
2021-07-07 22:58:14 +03:00
color: ${theme.darkmode ? theme.system.grayLight6 : theme.system.black};
background-color: ${theme.darkmode ? theme.system.black : theme.system.grayLight6};
2021-02-18 20:39:44 +03:00
`;
2021-02-16 21:03:59 +03:00
const STYLES_BODY = css`
width: 100%;
2021-02-18 10:30:25 +03:00
// 687px to ensure we have maximum 70ch per line
2021-02-16 21:03:59 +03:00
max-width: 687px;
margin: 0 auto;
& > *:first-child {
margin-top: 0;
}
p,
ul,
ol,
code,
pre,
div {
margin-top: 24px;
}
h1,
h2,
h3,
h4 {
margin-top: 48px;
margin-bottom: 24px;
}
img,
video {
padding: 16px 0px;
}
h1 + * {
margin-top: 0px;
}
h2 + * {
margin-top: 0px;
}
h3 + * {
margin-top: 0px;
}
h4 + * {
margin-top: 0px;
}
`;
const STYLES_IMG = css`
width: auto;
max-width: 100%;
`;
2021-02-18 20:39:44 +03:00
const STYLES_META = (theme) => css`
max-width: 687px;
margin: 0 auto;
font-size: 12px;
2021-07-07 22:24:01 +03:00
color: ${theme.darkmode ? theme.semantic.textGray : theme.system.grayDark3};
2021-02-18 20:39:44 +03:00
letter-spacing: 0.2px;
align-items: center;
margin-bottom: 12px;
`;
2021-06-09 01:53:30 +03:00
const STYLES_DIVIDER = (theme) => css`
2021-02-18 20:39:44 +03:00
position: sticky;
// Note(Amine): asset padding
top: -120px;
left: 0;
width: 100%;
height: 1px;
max-width: 687px;
margin: 0 auto;
margin-bottom: 58px;
2021-07-07 21:56:04 +03:00
background-color: ${theme.system.grayLight2};
2021-02-18 20:39:44 +03:00
transition: height 0.3s;
`;
const STYLE_PROGRESS = (theme) => css`
width: 80%;
height: 100%;
background-color: ${theme.darkmode ? "#fff" : "#000"};
transition: opacity 0.3s;
`;
const STYLES_INTENT = (theme) => css`
width: 100%;
height: 8px;
background: linear-gradient(
180deg,
2021-07-07 22:58:14 +03:00
${theme.darkmode ? theme.system.black : theme.system.grayLight6} 0%,
2021-02-18 20:39:44 +03:00
${theme.darkmode ? "rgba(12, 12, 12, 0)" : "rgba(241, 240, 242, 0)"} 100%
);
`;
export default function MarkdownFrame({ url, date }) {
2021-02-16 21:03:59 +03:00
const [content, setContent] = React.useState("");
React.useEffect(() => {
fetch(url).then(async (res) => {
const content = await res.text();
setContent(content);
});
}, []);
2021-02-18 20:39:44 +03:00
const ref = React.useRef();
2021-02-22 19:15:26 +03:00
const meterRef = React.useRef();
const { handleScrollAnimation, opacity, extendScroll } = useScrollPosition({
ref,
childRef: meterRef,
});
2021-02-18 20:39:44 +03:00
const readTime = Math.round(content.split(" ").length / 150);
2021-02-16 21:03:59 +03:00
const remarkReactComponents = {
2021-07-07 23:50:57 +03:00
p: (props) => <P1 {...props} />,
2021-02-16 21:03:59 +03:00
h1: (props) => <H1 {...props} />,
h2: (props) => <H2 {...props} />,
h3: (props) => <H3 {...props} />,
h4: (props) => <H4 {...props} />,
h5: (props) => <H4 {...props} />,
h6: (props) => <H4 {...props} />,
ol: OL,
ul: UL,
li: LI,
2021-07-07 23:50:57 +03:00
a: (props) => <A {...props} dark={true} target="_blank" />,
2021-02-16 21:03:59 +03:00
img: (props) => <img css={STYLES_IMG} {...props} />,
};
return (
<div
css={STYLES_ASSET}
onClick={(e) => {
e.stopPropagation();
}}
2021-02-18 20:39:44 +03:00
onScroll={handleScrollAnimation}
ref={ref}
2021-02-16 21:03:59 +03:00
>
2021-02-18 20:39:44 +03:00
<div>
<div css={STYLES_META}>
<span>{Strings.toDate(date)}</span> / <span>{readTime} min read</span>
</div>
2021-06-09 01:53:30 +03:00
<div css={STYLES_DIVIDER} style={{ height: extendScroll ? "4px" : "1px" }}>
2021-02-22 19:15:26 +03:00
<div css={STYLE_PROGRESS} ref={meterRef} style={{ opacity: opacity }} />
2021-02-18 20:39:44 +03:00
<div css={STYLES_INTENT} />
</div>
<article css={STYLES_BODY}>
<Markdown md={content} css={STYLES_BODY} options={{ remarkReactComponents }} />
</article>
</div>
2021-02-16 21:03:59 +03:00
</div>
);
}
2021-02-18 20:39:44 +03:00
2021-02-22 19:15:26 +03:00
const useScrollPosition = ({ ref, childRef }) => {
const [extendScroll, setExtendScroll] = React.useState(false);
const [opacity, setOpacity] = React.useState(0);
2021-02-18 20:39:44 +03:00
const handleScrollAnimation = () => {
const percentage =
(100 * ref.current.scrollTop) / (ref.current.scrollHeight - ref.current.clientHeight);
2021-02-22 19:15:26 +03:00
// Updating width without updating state to improve performance
childRef.current.style.width = `${percentage}%`;
if (percentage > 15 && !extendScroll) setExtendScroll(true);
if (percentage < 15 && extendScroll) setExtendScroll(false);
if (percentage < 15 && opacity === 1) setOpacity(0);
if (percentage > 15 && opacity === 0) setOpacity(1);
2021-02-18 20:39:44 +03:00
};
2021-02-22 19:15:26 +03:00
return { opacity, extendScroll, handleScrollAnimation };
2021-02-18 20:39:44 +03:00
};