feat: update feed content render
Signed-off-by: Innei <i@innei.in>
This commit is contained in:
parent
cdeb419294
commit
41b3e174ea
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="#fff" fill-opacity=".01" d="M24 0v24H0V0z"/><path stroke="#10161F" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16.946 7.054 6.339 17.661M9.409 6.59a15.963 15.963 0 0 1 6.796-.249c.571.1.857.149 1.08.373.225.224.274.51.374 1.08a15.963 15.963 0 0 1-.25 6.796"/></svg>
|
||||
|
After Width: | Height: | Size: 376 B |
|
|
@ -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) => (
|
||||
<Tooltip delayDuration={0}>
|
||||
<TooltipTrigger asChild>
|
||||
<a href={props.href} title={props.title} target={props.target}>{props.children}</a>
|
||||
<a href={props.href} title={props.title} target={props.target}>
|
||||
{props.children}
|
||||
</a>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent align="start" side="bottom">
|
||||
{props.href}
|
||||
</TooltipContent>
|
||||
<TooltipPortal>
|
||||
<TooltipContent align="start" className="break-all" side="bottom">
|
||||
{props.href}
|
||||
</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</Tooltip>
|
||||
)
|
||||
|
||||
export const MarkdownLink = (props: LinkProps) => (
|
||||
<Tooltip delayDuration={0}>
|
||||
<TooltipTrigger asChild>
|
||||
<a
|
||||
className="follow-link--underline font-semibold text-foreground no-underline"
|
||||
href={props.href}
|
||||
title={props.title}
|
||||
target="_blank"
|
||||
>
|
||||
{props.children}
|
||||
|
||||
{typeof props.children === "string" && <i className="i-mgc-arrow-right-up-cute-re size-[0.9em] translate-y-[2px] opacity-70" />}
|
||||
</a>
|
||||
</TooltipTrigger>
|
||||
<TooltipPortal>
|
||||
<TooltipContent align="start" className="break-all" side="bottom">
|
||||
{props.href}
|
||||
</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</Tooltip>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,32 +10,39 @@ import { useEventCallback } from "usehooks-ts"
|
|||
import { usePreviewMedia } from "./media/hooks"
|
||||
|
||||
const failedList = new Set<string | undefined>()
|
||||
export type ImageProps =
|
||||
| (ImgHTMLAttributes<HTMLImageElement> & {
|
||||
proxy?: {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
disableContextMenu?: boolean
|
||||
popper?: boolean
|
||||
type: "photo"
|
||||
previewImageUrl?: string
|
||||
})
|
||||
| (VideoHTMLAttributes<HTMLVideoElement> & {
|
||||
proxy?: {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
disableContextMenu?: boolean
|
||||
popper?: boolean
|
||||
type: "video"
|
||||
previewImageUrl?: string
|
||||
})
|
||||
const MediaImpl: FC<ImageProps> = ({
|
||||
|
||||
type BaseProps = {
|
||||
mediaContainerClassName?: string
|
||||
}
|
||||
export type MediaProps = BaseProps &
|
||||
(
|
||||
| (ImgHTMLAttributes<HTMLImageElement> & {
|
||||
proxy?: {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
disableContextMenu?: boolean
|
||||
popper?: boolean
|
||||
type: "photo"
|
||||
previewImageUrl?: string
|
||||
})
|
||||
| (VideoHTMLAttributes<HTMLVideoElement> & {
|
||||
proxy?: {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
disableContextMenu?: boolean
|
||||
popper?: boolean
|
||||
type: "video"
|
||||
previewImageUrl?: string
|
||||
})
|
||||
)
|
||||
const MediaImpl: FC<MediaProps> = ({
|
||||
className,
|
||||
proxy,
|
||||
disableContextMenu,
|
||||
popper = false,
|
||||
mediaContainerClassName,
|
||||
...props
|
||||
}) => {
|
||||
const { src, style, type, previewImageUrl, ...rest } = props
|
||||
|
|
@ -89,6 +96,7 @@ const MediaImpl: FC<ImageProps> = ({
|
|||
!(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<ImageProps> = ({
|
|||
hidden && "hidden",
|
||||
!(props.width || props.height) && "size-full",
|
||||
"relative bg-stone-100 object-cover",
|
||||
mediaContainerClassName,
|
||||
)}
|
||||
onClick={handleClick}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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, {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue