From 525cbc033440681c59a6c233b965c93336202ed3 Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 20 Jun 2023 22:08:33 +0800 Subject: [PATCH] feat: markdown extra component Signed-off-by: Innei --- src/app/notes/[id]/page.module.css | 2 +- src/components/layout/header/Header.tsx | 6 +- .../header/internal/HeaderWithShadow.tsx | 19 ++++ .../ui/link-card/LinkCard.module.css | 14 ++- src/components/ui/link-card/LinkCard.tsx | 10 +- src/components/ui/markdown/Markdown.tsx | 75 ++++++++++++++- .../ui/markdown/renderers/link.module.css | 13 +++ src/components/ui/markdown/renderers/link.tsx | 92 +++++++++++++++++++ 8 files changed, 212 insertions(+), 19 deletions(-) create mode 100644 src/components/layout/header/internal/HeaderWithShadow.tsx create mode 100644 src/components/ui/markdown/renderers/link.module.css create mode 100644 src/components/ui/markdown/renderers/link.tsx diff --git a/src/app/notes/[id]/page.module.css b/src/app/notes/[id]/page.module.css index 2b0162a7be..bdd0af5a68 100644 --- a/src/app/notes/[id]/page.module.css +++ b/src/app/notes/[id]/page.module.css @@ -4,7 +4,7 @@ .paragraph .indent { border-bottom: 1px solid; - @apply border-accent/20 dark:border-accent/40; + @apply border-accent/20 dark:border-accent/30; } blockquote { diff --git a/src/components/layout/header/Header.tsx b/src/components/layout/header/Header.tsx index 2870d1566f..cf6dfb5a7e 100644 --- a/src/components/layout/header/Header.tsx +++ b/src/components/layout/header/Header.tsx @@ -15,6 +15,7 @@ import { HeaderContent } from './internal/HeaderContent' import { HeaderDataConfigureProvider } from './internal/HeaderDataConfigureProvider' import { HeaderDrawerButton } from './internal/HeaderDrawerButton' import { HeaderMeta } from './internal/HeaderMeta' +import { HeaderWithShadow } from './internal/HeaderWithShadow' import { SiteOwnerAvatar } from './internal/SiteOwnerAvatar' import { UserAuth } from './internal/UserAuth' @@ -25,10 +26,9 @@ export const Header = () => { ) } - const MemoedHeader = memo(() => { return ( -
+
{
-
+ ) }) diff --git a/src/components/layout/header/internal/HeaderWithShadow.tsx b/src/components/layout/header/internal/HeaderWithShadow.tsx new file mode 100644 index 0000000000..cd0095a314 --- /dev/null +++ b/src/components/layout/header/internal/HeaderWithShadow.tsx @@ -0,0 +1,19 @@ +'use client' + +import clsx from 'clsx' + +import { usePageScrollLocationSelector } from '~/providers/root/page-scroll-info-provider' + +export const HeaderWithShadow: Component = ({ children }) => { + const showShadow = usePageScrollLocationSelector((y) => y > 100) + return ( +
+ {children} +
+ ) +} diff --git a/src/components/ui/link-card/LinkCard.module.css b/src/components/ui/link-card/LinkCard.module.css index 9c4767af73..49aa43a091 100644 --- a/src/components/ui/link-card/LinkCard.module.css +++ b/src/components/ui/link-card/LinkCard.module.css @@ -11,16 +11,14 @@ overflow: hidden; margin: 16px auto; padding: 12px 18px; - - @apply cursor-pointer font-sans; - - background-color: #f3f3f380; backdrop-filter: blur(20px) saturate(180%); transition: background-color 0.2s ease-in-out; -} + border: 0; -:global([data-theme='dark']) .card-grid { - background-color: #28282880; + @apply cursor-pointer font-sans; + @apply [&_*]:!not-italic; + @apply border border-slate-100 bg-gray-100/80 dark:border-neutral-700 dark:bg-neutral-800; + @apply transition-colors duration-200; } .contents { @@ -55,7 +53,7 @@ .image { @apply aspect-square flex-shrink-0 bg-cover bg-center bg-no-repeat; - @apply bg-gray-600 dark:bg-gray-50; + @apply bg-gray-50 dark:bg-neutral-700; height: 60px; width: 60px; diff --git a/src/components/ui/link-card/LinkCard.tsx b/src/components/ui/link-card/LinkCard.tsx index 84cbb3304f..479cbb3f61 100644 --- a/src/components/ui/link-card/LinkCard.tsx +++ b/src/components/ui/link-card/LinkCard.tsx @@ -163,12 +163,12 @@ export const LinkCard: FC = (props) => { className, )} > -
-
{cardInfo?.title}
-
{cardInfo?.desc}
-
+ + {cardInfo?.title} + {cardInfo?.desc} + {(loading || cardInfo?.image) && ( -
= }, }, + link: { + react(node, output, state) { + const { target, title } = node + return ( + + {output(node.content, state!)} + + ) + }, + }, + + footnoteReference: { + react(node, output, state) { + const { footnoteMap, target, content } = node + const footnote = footnoteMap.get(content) + const linkCardId = (() => { + try { + const thisUrl = new URL(footnote?.footnote?.replace(': ', '')) + const isCurrentHost = + thisUrl.hostname === window.location.hostname + + if (!isCurrentHost && !isDev) { + return undefined + } + const pathname = thisUrl.pathname + return pathname.slice(1) + } catch { + return undefined + } + })() + + return ( + + { + e.preventDefault() + + springScrollToElement( + document.getElementById(content)!, + + -window.innerHeight / 2, + ) + }} + > + ^{content} + + {linkCardId && } + + ) + }, + }, + // codeBlock: { + // react(node, output, state) { + // return ( + // + // ) + // }, + // }, + list: { react(node, output, state) { const Tag = node.ordered ? 'ol' : 'ul' diff --git a/src/components/ui/markdown/renderers/link.module.css b/src/components/ui/markdown/renderers/link.module.css new file mode 100644 index 0000000000..6dd053fdd6 --- /dev/null +++ b/src/components/ui/markdown/renderers/link.module.css @@ -0,0 +1,13 @@ +.anchor { + @apply relative inline cursor-alias overflow-hidden text-accent; +} + +.anchor::after { + @apply absolute -bottom-0.5 left-1/2 w-0 -translate-x-1/2 border-b-[1px] border-current text-center transition-[width] duration-500 ease-in-out; + + content: ''; +} + +.anchor:hover::after { + width: 100%; +} diff --git a/src/components/ui/markdown/renderers/link.tsx b/src/components/ui/markdown/renderers/link.tsx new file mode 100644 index 0000000000..e7b73c42de --- /dev/null +++ b/src/components/ui/markdown/renderers/link.tsx @@ -0,0 +1,92 @@ +import { memo, useCallback } from 'react' +import Router from 'next/router' +import type { FC } from 'react' + +import { FloatPopover } from '../../float-popover' +import styles from './link.module.css' + +const ExtendIcon = () => ( + + + + +) +export const MLink: FC<{ + href: string + title?: string + children?: JSX.Element | JSX.Element[] +}> = memo((props) => { + const handleRedirect = useCallback( + (e: React.MouseEvent) => { + const href = props.href + const locateUrl = new URL(location.href) + + const toUrlParser = new URL(href) + + if ( + toUrlParser.host === locateUrl.host || + (process.env.NODE_ENV === 'development' && + toUrlParser.host === 'innei.ren') + ) { + e.preventDefault() + const pathArr = toUrlParser.pathname.split('/').filter(Boolean) + const headPath = pathArr[0] + + switch (headPath) { + case 'posts': + case 'notes': + case 'category': { + Router.push(toUrlParser.pathname) + break + } + default: { + window.open(toUrlParser.pathname) + } + } + } + }, + [props.href], + ) + + return ( + ( + <> + + {props.children} + + + {ExtendIcon} + + )} + > + {props.href} + + ) +})