mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-12 23:36:00 +00:00
parent
ed9906ae2f
commit
c82084576a
@ -4,7 +4,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
import { memo } from 'react'
|
|
||||||
import Markdown from 'react-markdown'
|
import Markdown from 'react-markdown'
|
||||||
import remarkBreaks from 'remark-breaks'
|
import remarkBreaks from 'remark-breaks'
|
||||||
|
|
||||||
@ -26,14 +25,12 @@ export const TextView = ({
|
|||||||
text,
|
text,
|
||||||
truncate = false,
|
truncate = false,
|
||||||
rawText,
|
rawText,
|
||||||
itemTextField,
|
|
||||||
}: {
|
}: {
|
||||||
item?: Item
|
item?: Item
|
||||||
itemId?: string
|
itemId?: string
|
||||||
text?: string
|
text?: string
|
||||||
truncate?: boolean
|
truncate?: boolean
|
||||||
rawText?: string
|
rawText?: string
|
||||||
itemTextField?: string
|
|
||||||
}) => {
|
}) => {
|
||||||
if (item) {
|
if (item) {
|
||||||
text = item.text
|
text = item.text
|
||||||
@ -41,8 +38,6 @@ export const TextView = ({
|
|||||||
}
|
}
|
||||||
const tags = useTags()
|
const tags = useTags()
|
||||||
const addFilterTag = useAddFilterTag()
|
const addFilterTag = useAddFilterTag()
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const itemTextFieldDummy = itemTextField
|
|
||||||
|
|
||||||
let innerText = ''
|
let innerText = ''
|
||||||
let replacedText = ''
|
let replacedText = ''
|
||||||
@ -86,53 +81,15 @@ export const TextView = ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomH1 = ({ children }) => <h1 className='tw:text-xl tw:font-bold'>{children}</h1>
|
const HashTag = ({ children, tag, itemId }: { children: string; tag: Tag; itemId?: string }) => {
|
||||||
|
|
||||||
const CustomH2 = ({ children }) => <h2 className='tw:text-lg tw:font-bold'>{children}</h2>
|
|
||||||
|
|
||||||
const CustomH3 = ({ children }) => <h3 className='tw:text-base tw:font-bold'>{children}</h3>
|
|
||||||
|
|
||||||
const CustomH4 = ({ children }) => <h4 className='tw:text-base tw:font-bold'>{children}</h4>
|
|
||||||
|
|
||||||
const CustomH5 = ({ children }) => <h5 className='tw:text-sm tw:font-bold'>{children}</h5>
|
|
||||||
|
|
||||||
const CustomH6 = ({ children }) => <h6 className='tw:text-sm tw:font-bold'>{children}</h6>
|
|
||||||
|
|
||||||
const CustomParagraph = ({ children }) => <p className='tw:my-2!'>{children}</p>
|
|
||||||
|
|
||||||
const CustomUnorderdList = ({ children }) => (
|
|
||||||
<ul className='tw:list-disc tw:list-inside'>{children}</ul>
|
|
||||||
)
|
|
||||||
|
|
||||||
const CustomOrderdList = ({ children }) => (
|
|
||||||
<ol className='tw:list-decimal tw:list-inside'>{children}</ol>
|
|
||||||
)
|
|
||||||
|
|
||||||
const CustomHorizontalRow = ({ children }) => <hr className='tw:border-current'>{children}</hr>
|
|
||||||
// eslint-disable-next-line react/prop-types
|
|
||||||
const CustomImage = ({ alt, src, title }) => (
|
|
||||||
<img className='tw:max-w-full tw:rounded tw:shadow' src={src} alt={alt} title={title} />
|
|
||||||
)
|
|
||||||
|
|
||||||
const CustomExternalLink = ({ href, children }) => (
|
|
||||||
<a className='tw:font-bold tw:underline' href={href} target='_blank' rel='noreferrer'>
|
|
||||||
{' '}
|
|
||||||
{children}
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
|
|
||||||
const CustomHashTagLink = ({
|
|
||||||
children,
|
|
||||||
tag,
|
|
||||||
itemId,
|
|
||||||
}: {
|
|
||||||
children: string
|
|
||||||
tag: Tag
|
|
||||||
itemId?: string
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
style={{ color: tag ? tag.color : '#faa', fontWeight: 'bold', cursor: 'pointer' }}
|
className='hashtag'
|
||||||
|
style={
|
||||||
|
tag && {
|
||||||
|
color: tag.color,
|
||||||
|
}
|
||||||
|
}
|
||||||
key={tag ? tag.name + itemId : itemId}
|
key={tag ? tag.name + itemId : itemId}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@ -144,62 +101,48 @@ export const TextView = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
const Link = ({ href, children }: { href: string; children: string }) => {
|
||||||
const MemoizedVideoEmbed = memo(({ url }: { url: string }) => (
|
// Youtube
|
||||||
<iframe
|
if (href.startsWith('https://www.youtube.com/watch?v=')) {
|
||||||
className='tw:w-full'
|
const videoId = href?.split('v=')[1].split('&')[0]
|
||||||
src={url}
|
const youtubeEmbedUrl = `https://www.youtube-nocookie.com/embed/${videoId}`
|
||||||
allow='fullscreen; picture-in-picture'
|
|
||||||
allowFullScreen
|
return (
|
||||||
/>
|
<iframe src={youtubeEmbedUrl} allow='fullscreen; picture-in-picture' allowFullScreen />
|
||||||
))
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rumble
|
||||||
|
if (href.startsWith('https://rumble.com/embed/')) {
|
||||||
|
return <iframe src={href} allow='fullscreen; picture-in-picture' allowFullScreen />
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashTag
|
||||||
|
if (href.startsWith('#')) {
|
||||||
|
const tag = tags.find((t) => t.name.toLowerCase() === decodeURI(href).slice(1).toLowerCase())
|
||||||
|
if (tag)
|
||||||
|
return (
|
||||||
|
<HashTag tag={tag} itemId={itemId}>
|
||||||
|
{children}
|
||||||
|
</HashTag>
|
||||||
|
)
|
||||||
|
else return children
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default: Link
|
||||||
|
return (
|
||||||
|
<a href={href} target='_blank' rel='noreferrer'>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Markdown
|
<Markdown
|
||||||
className={'tw:text-map tw:leading-map tw:text-sm'}
|
className={'markdown tw:text-map tw:leading-map tw:text-sm'}
|
||||||
remarkPlugins={[remarkBreaks]}
|
remarkPlugins={[remarkBreaks]}
|
||||||
components={{
|
components={{
|
||||||
p: CustomParagraph,
|
a: Link,
|
||||||
a: ({ href, children }: { href: string; children: string }) => {
|
|
||||||
const isYouTubeVideo = href?.startsWith('https://www.youtube.com/watch?v=')
|
|
||||||
|
|
||||||
const isRumbleVideo = href?.startsWith('https://rumble.com/embed/')
|
|
||||||
|
|
||||||
if (isYouTubeVideo) {
|
|
||||||
const videoId = href?.split('v=')[1].split('&')[0]
|
|
||||||
const youtubeEmbedUrl = `https://www.youtube-nocookie.com/embed/${videoId}`
|
|
||||||
|
|
||||||
return <MemoizedVideoEmbed url={youtubeEmbedUrl}></MemoizedVideoEmbed>
|
|
||||||
}
|
|
||||||
if (isRumbleVideo) {
|
|
||||||
return <MemoizedVideoEmbed url={href}></MemoizedVideoEmbed>
|
|
||||||
}
|
|
||||||
|
|
||||||
if (href?.startsWith('#')) {
|
|
||||||
const tag = tags.find(
|
|
||||||
(t) => t.name.toLowerCase() === decodeURI(href).slice(1).toLowerCase(),
|
|
||||||
)
|
|
||||||
if (tag)
|
|
||||||
return (
|
|
||||||
<CustomHashTagLink tag={tag} itemId={itemId}>
|
|
||||||
{children}
|
|
||||||
</CustomHashTagLink>
|
|
||||||
)
|
|
||||||
else return children
|
|
||||||
} else {
|
|
||||||
return <CustomExternalLink href={href}>{children}</CustomExternalLink>
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ul: CustomUnorderdList,
|
|
||||||
ol: CustomOrderdList,
|
|
||||||
img: CustomImage,
|
|
||||||
hr: CustomHorizontalRow,
|
|
||||||
h1: CustomH1,
|
|
||||||
h2: CustomH2,
|
|
||||||
h3: CustomH3,
|
|
||||||
h4: CustomH4,
|
|
||||||
h5: CustomH5,
|
|
||||||
h6: CustomH6,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{replacedText}
|
{replacedText}
|
||||||
|
|||||||
63
src/assets/css/markdown.css
Normal file
63
src/assets/css/markdown.css
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
@import 'tailwindcss' prefix(tw);
|
||||||
|
|
||||||
|
@layer markdown {
|
||||||
|
.markdown h1 {
|
||||||
|
@apply tw:text-xl;
|
||||||
|
@apply tw:font-bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h2 {
|
||||||
|
@apply tw:text-lg;
|
||||||
|
@apply tw:font-bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h3, .markdown h4 {
|
||||||
|
@apply tw:text-base;
|
||||||
|
@apply tw:font-bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown h5, .markdown h6 {
|
||||||
|
@apply tw:text-sm;
|
||||||
|
@apply tw:font-bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown p {
|
||||||
|
@apply tw:my-2!;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown ul {
|
||||||
|
@apply tw:list-disc;
|
||||||
|
@apply tw:list-inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown ol {
|
||||||
|
@apply tw:list-decimal;
|
||||||
|
@apply tw:list-inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown hl {
|
||||||
|
@apply tw:border-current;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown img {
|
||||||
|
@apply tw:max-w-full;
|
||||||
|
@apply tw:rounded;
|
||||||
|
@apply tw:shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown a {
|
||||||
|
@apply tw:font-bold;
|
||||||
|
@apply tw:underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown .hashtag {
|
||||||
|
color: #faa;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown iframe {
|
||||||
|
@apply tw:w-full;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,3 +13,4 @@ import '#assets/css/misc.css'
|
|||||||
import '#assets/css/marker-icons.css'
|
import '#assets/css/marker-icons.css'
|
||||||
import '#assets/css/leaflet.css'
|
import '#assets/css/leaflet.css'
|
||||||
import '#assets/css/color-picker.css'
|
import '#assets/css/color-picker.css'
|
||||||
|
import '#assets/css/markdown.css'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user