From 41b3e174ea0bcdd62039e12b8fdb42f8b763fffd Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 26 Jul 2024 14:26:37 +0800 Subject: [PATCH] feat: update feed content render Signed-off-by: Innei --- icons/mgc/arrow_right_up_cute_re.svg | 1 + src/renderer/src/components/ui/link/index.tsx | 41 ++++++++++++-- src/renderer/src/components/ui/media.tsx | 53 +++++++++++-------- src/renderer/src/lib/parse-html.ts | 30 ++++++----- src/renderer/src/styles/tailwind-extend.css | 32 +++++++++++ 5 files changed, 118 insertions(+), 39 deletions(-) create mode 100644 icons/mgc/arrow_right_up_cute_re.svg diff --git a/icons/mgc/arrow_right_up_cute_re.svg b/icons/mgc/arrow_right_up_cute_re.svg new file mode 100644 index 000000000..187aecd3e --- /dev/null +++ b/icons/mgc/arrow_right_up_cute_re.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/renderer/src/components/ui/link/index.tsx b/src/renderer/src/components/ui/link/index.tsx index 7a9d27708..867f90032 100644 --- a/src/renderer/src/components/ui/link/index.tsx +++ b/src/renderer/src/components/ui/link/index.tsx @@ -1,4 +1,9 @@ -import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip" +import { + Tooltip, + TooltipContent, + TooltipPortal, + TooltipTrigger, +} from "../tooltip" interface LinkProps { href: string @@ -10,10 +15,36 @@ interface LinkProps { export const LinkWithTooltip = (props: LinkProps) => ( - {props.children} + + {props.children} + - - {props.href} - + + + {props.href} + + + +) + +export const MarkdownLink = (props: LinkProps) => ( + + + + {props.children} + + {typeof props.children === "string" && } + + + + + {props.href} + + ) diff --git a/src/renderer/src/components/ui/media.tsx b/src/renderer/src/components/ui/media.tsx index 049bea017..fb624f78d 100644 --- a/src/renderer/src/components/ui/media.tsx +++ b/src/renderer/src/components/ui/media.tsx @@ -10,32 +10,39 @@ import { useEventCallback } from "usehooks-ts" import { usePreviewMedia } from "./media/hooks" const failedList = new Set() -export type ImageProps = - | (ImgHTMLAttributes & { - proxy?: { - width: number - height: number - } - disableContextMenu?: boolean - popper?: boolean - type: "photo" - previewImageUrl?: string - }) - | (VideoHTMLAttributes & { - proxy?: { - width: number - height: number - } - disableContextMenu?: boolean - popper?: boolean - type: "video" - previewImageUrl?: string - }) -const MediaImpl: FC = ({ + +type BaseProps = { + mediaContainerClassName?: string +} +export type MediaProps = BaseProps & + ( + | (ImgHTMLAttributes & { + proxy?: { + width: number + height: number + } + disableContextMenu?: boolean + popper?: boolean + type: "photo" + previewImageUrl?: string + }) + | (VideoHTMLAttributes & { + proxy?: { + width: number + height: number + } + disableContextMenu?: boolean + popper?: boolean + type: "video" + previewImageUrl?: string + }) + ) +const MediaImpl: FC = ({ className, proxy, disableContextMenu, popper = false, + mediaContainerClassName, ...props }) => { const { src, style, type, previewImageUrl, ...rest } = props @@ -89,6 +96,7 @@ const MediaImpl: FC = ({ !(props.width || props.height) && "size-full", "bg-stone-100 object-cover", popper && "cursor-zoom-in", + mediaContainerClassName, )} src={imgSrc} onClick={handleClick} @@ -136,6 +144,7 @@ const MediaImpl: FC = ({ hidden && "hidden", !(props.width || props.height) && "size-full", "relative bg-stone-100 object-cover", + mediaContainerClassName, )} onClick={handleClick} > diff --git a/src/renderer/src/lib/parse-html.ts b/src/renderer/src/lib/parse-html.ts index d20c87326..645b68097 100644 --- a/src/renderer/src/lib/parse-html.ts +++ b/src/renderer/src/lib/parse-html.ts @@ -1,6 +1,6 @@ import { Checkbox } from "@renderer/components/ui/checkbox" import { ShikiHighLighter } from "@renderer/components/ui/code-highlighter" -import { LinkWithTooltip } from "@renderer/components/ui/link" +import { MarkdownLink } from "@renderer/components/ui/link" import { Media } from "@renderer/components/ui/media" import { toJsxRuntime } from "hast-util-to-jsx-runtime" import { createElement } from "react" @@ -28,10 +28,7 @@ export const parseHtml = async ( ...defaultSchema, tagNames: renderInlineStyle ? defaultSchema.tagNames : - [ - ...defaultSchema.tagNames!, - "video", - ], + [...defaultSchema.tagNames!, "video"], attributes: { ...defaultSchema.attributes, @@ -58,7 +55,7 @@ export const parseHtml = async ( passNode: true, components: { a: ({ node, ...props }) => - createElement(LinkWithTooltip, { ...props } as any), + createElement(MarkdownLink, { ...props } as any), img: ({ node, ...props }) => { if (node?.properties.inline) { return createElement("img", { @@ -66,9 +63,17 @@ export const parseHtml = async ( style: { maxWidth: "100%", display: "inline" }, }) } - return createElement(Media, { ...props, popper: true, type: "photo" }) + return createElement(Media, { + ...props, + mediaContainerClassName: "w-full rounded", + className: "flex justify-center", + + popper: true, + type: "photo", + }) }, - video: ({ node, ...props }) => createElement(Media, { ...props, popper: true, type: "video" }), + video: ({ node, ...props }) => + createElement(Media, { ...props, popper: true, type: "video" }), p: ({ node, ...props }) => { if (node?.children) { for (const item of node.children) { @@ -79,10 +84,11 @@ export const parseHtml = async ( } return createElement("p", undefined, props.children) }, - hr: ({ node, ...props }) => createElement("hr", { - ...props, - className: tw`scale-x-50`, - }), + hr: ({ node, ...props }) => + createElement("hr", { + ...props, + className: tw`scale-x-50`, + }), input: ({ node, ...props }) => { if (props.type === "checkbox") { return createElement(Checkbox, { diff --git a/src/renderer/src/styles/tailwind-extend.css b/src/renderer/src/styles/tailwind-extend.css index cc1791e10..b75af3dff 100644 --- a/src/renderer/src/styles/tailwind-extend.css +++ b/src/renderer/src/styles/tailwind-extend.css @@ -229,3 +229,35 @@ rgba(0, 0, 0, 0.067) 0px 2px 5px, rgba(0, 0, 0, 0.067) 0px 1px 1px; } } + +/* Link */ +@layer components { + .follow-link--underline { + color: currentColor; + background-image: linear-gradient( + theme(colors.theme.accent.DEFAULT), + theme(colors.theme.accent.DEFAULT) + ); + background-size: 0% 1.5px; + background-repeat: no-repeat; + /* NOTE: this won't work with background images */ + + transition: all 500ms ease; + + @apply border-0 no-underline; + + background-position: left 1em; + + &:hover { + background-size: 100% 1.5px; + text-shadow: 0.05em 0 theme(colors.background), + -0.05em 0 theme(colors.background); + + transition: all 250ms ease; + } + + &.no-shadow { + text-shadow: none; + } + } +}