TextPreview component init

This commit is contained in:
Anton Tranelis 2025-07-04 07:52:56 +02:00
parent 130169ec8e
commit d26ae3f428
11 changed files with 70 additions and 46 deletions

View File

@ -15,7 +15,7 @@ import {
PopupView,
PopupButton,
StartEndView,
TextView,
TextPreview,
PopupForm,
PopupStartEndInput,
PopupTextAreaInput,
@ -114,7 +114,7 @@ function MapContainer({ layers, map }: { layers: LayerProps[]; map: any }) {
{layer.itemType.show_profile_button && (
<PopupButton url={'/item'} parameterField={'id'} text={'Profile'} />
)}
{layer.itemType.show_text && <TextView truncate></TextView>}
{layer.itemType.show_text && <TextPreview></TextPreview>}
</PopupView>
<PopupForm>
{layer.itemType.show_name_input && (

View File

@ -6,6 +6,7 @@ import {
PopupCheckboxInput as PlainPopupCheckboxInput,
PopupTextAreaInput as PlainPopupTextAreaInput,
PopupStartEndInput as PlainPopupStartEndInput,
TextPreview as PlainTextPreview,
} from '#components/Map/Subcomponents/ItemPopupComponents'
import { templateify } from './templateify'
@ -14,6 +15,7 @@ export { PopupForm } from './PopupForm'
export { PopupView } from './PopupView'
export const TextView = templateify(PlainTextView)
export const TextPreview = templateify(PlainTextPreview)
export const StartEndView = templateify(PlainStartEndView)
export const PopupTextInput = templateify(PlainPopupTextInput)
export const PopupButton = templateify(PlainPopupButton)

View File

@ -0,0 +1,47 @@
import { useAddFilterTag } from '#components/Map/hooks/useFilter'
import { useTags } from '#components/Map/hooks/useTags'
import { decodeTag } from '#utils/FormatTags'
import type { Item } from '#types/Item'
import type { Tag } from '#types/Tag'
const MAX_CHARS = 100
/**
* @category Map
*/
export const TextPreview = ({ item }: { item: Item }) => {
const tags = useTags()
if (!item.text) return ''
const s = item.text
.replace(/`([^`]+)`/g, '$1')
.replace(/<\/?[^>]+>/g, '') // übrige HTML
.replace(/!\[.*?\]\(.*?\)/g, '') // Remove images
.replace(/(`{1,3})(.*?)\1/g, '$2') // Remove inline code
.replace(/(\*{1,2}|_{1,2})(.*?)\1/g, '$2') // Remove bold and italic
.replace(/(#+)\s+(.*)/g, '$2') // Remove headers
.replace(/>\s+(.*)/g, '$1') // Remove blockquotes
.replace(/^\s*\n/gm, '\n') // Preserve empty lines
.replace(/(\r\n|\n|\r)/gm, '\n') // Preserve line breaks
return s
}
const HashTag = ({ children, tag, itemId }: { children: string; tag: Tag; itemId?: string }) => {
const addFilterTag = useAddFilterTag()
return (
<a
className='hashtag'
style={{ color: tag.color }}
key={`${tag.name}-${itemId ?? ''}`}
onClick={(e) => {
e.stopPropagation()
addFilterTag(tag)
}}
>
{decodeTag(children)}
</a>
)
}

View File

@ -6,32 +6,16 @@ import type { Item } from '#types/Item'
/**
* @category Map
*/
export const TextView = ({
item,
text,
truncate = false,
rawText,
}: {
item?: Item
text?: string
truncate?: boolean
rawText?: string
}) => {
if (item) {
text = item.text
}
export const TextView = ({ item, rawText }: { item?: Item; rawText?: string }) => {
let innerText = ''
let replacedText = ''
if (rawText) {
innerText = replacedText = rawText
} else if (text) {
innerText = text
} else if (item) {
innerText = item.text ?? ''
}
if (innerText && truncate) innerText = truncateMarkdown(innerText, 100)
if (innerText) replacedText = fixUrls(innerText)
if (replacedText) {
@ -42,7 +26,3 @@ export const TextView = ({
return <RichTextEditor defaultValue={replacedText} readOnly={true} />
}
export function truncateMarkdown(markdown: string, limit: number): string {
return markdown.slice(0, limit)
}

View File

@ -5,3 +5,4 @@ export { PopupCheckboxInput } from './PopupCheckboxInput'
export { TextView } from './TextView'
export { StartEndView } from './StartEndView'
export { PopupButton } from './PopupButton'
export { TextPreview } from './TextPreview'

View File

@ -18,8 +18,8 @@ import { usePopupForm } from '#components/Map/hooks/usePopupForm'
import { useSetSelectPosition } from '#components/Map/hooks/useSelectPosition'
import { timeAgo } from '#utils/TimeAgo'
import { TextPreview } from './ItemPopupComponents'
import { HeaderView } from './ItemPopupComponents/HeaderView'
import { TextView } from './ItemPopupComponents/TextView'
import type { Item } from '#types/Item'
@ -101,7 +101,7 @@ export const ItemViewPopup = forwardRef((props: ItemViewPopupProps, ref: any) =>
loading={loading}
/>
<div className='tw:overflow-hidden tw:max-h-64 fade'>
{props.children ?? <TextView text={props.item.text} />}
{props.children ?? <TextPreview item={props.item} />}
</div>
<div className='tw:flex tw:-mb-1 tw:flex-row tw:mr-2 tw:mt-1'>
{infoExpanded ? (

View File

@ -5,7 +5,7 @@ import type { Item } from '#types/Item'
export const SimpleView = ({ item }: { item: Item }) => {
return (
<div className='tw:mt-8 tw:h-full tw:overflow-y-auto fade tw:px-6'>
<TextView text={item.text} />
<TextView rawText={item.text} />
</div>
)
}

View File

@ -10,7 +10,7 @@ import { useNavigate } from 'react-router-dom'
import { RichTextEditor } from '#components/Input/RichTextEditor/RichTextEditor'
import { useUpdateItem } from '#components/Map/hooks/useItems'
import { PopupStartEndInput, TextView } from '#components/Map/Subcomponents/ItemPopupComponents'
import { PopupStartEndInput, TextPreview } from '#components/Map/Subcomponents/ItemPopupComponents'
import { ActionButton } from '#components/Profile/Subcomponents/ActionsButton'
import { LinkedItemsHeaderView } from '#components/Profile/Subcomponents/LinkedItemsHeaderView'
import { TagsWidget } from '#components/Profile/Subcomponents/TagsWidget'
@ -142,7 +142,7 @@ export const TabsForm = ({
loading={loading}
/>
<div className='tw:overflow-y-auto tw:overflow-x-hidden tw:max-h-64 fade'>
<TextView truncate />
<TextPreview item={i} />
</div>
</div>
))}

View File

@ -12,7 +12,11 @@ import { Link, useNavigate } from 'react-router-dom'
import { useAppState } from '#components/AppShell/hooks/useAppState'
import { useAddFilterTag } from '#components/Map/hooks/useFilter'
import { useItems } from '#components/Map/hooks/useItems'
import { StartEndView, TextView } from '#components/Map/Subcomponents/ItemPopupComponents'
import {
StartEndView,
TextPreview,
TextView,
} from '#components/Map/Subcomponents/ItemPopupComponents'
import { ActionButton } from '#components/Profile/Subcomponents/ActionsButton'
import { LinkedItemsHeaderView } from '#components/Profile/Subcomponents/LinkedItemsHeaderView'
import { TagView } from '#components/Templates/TagView'
@ -104,9 +108,9 @@ export const TabsView = ({
<StartEndView item={item}></StartEndView>
</div>
)}
<TextView text={item.text} />
<TextView rawText={item.text} />
<div className='tw:h-4'></div>
<TextView text={item.contact} />
<TextView rawText={item.contact} />
</div>
{item.layer?.itemType.questlog && (
<>
@ -275,7 +279,7 @@ export const TabsView = ({
loading={loading}
/>
<div className='tw:overflow-y-auto tw:overflow-x-hidden tw:max-h-64 fade'>
<TextView truncate text={i.text} />
<TextPreview item={i} />
</div>
</div>
))}

View File

@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom'
import { useSetSelectPosition } from '#components/Map/hooks/useSelectPosition'
import useWindowDimensions from '#components/Map/hooks/useWindowDimension'
import { StartEndView, TextView } from '#components/Map/Subcomponents/ItemPopupComponents'
import { StartEndView, TextPreview } from '#components/Map/Subcomponents/ItemPopupComponents'
import { HeaderView } from '#components/Map/Subcomponents/ItemPopupComponents/HeaderView'
import { DateUserInfo } from './DateUserInfo'
@ -51,7 +51,7 @@ export const ItemCard = ({
></HeaderView>
<div className='tw:overflow-y-auto tw:overflow-x-hidden tw:max-h-64 fade'>
{i.layer?.itemType.show_start_end && <StartEndView item={i}></StartEndView>}
{i.layer?.itemType.show_text && <TextView truncate text={i.text} />}
{i.layer?.itemType.show_text && <TextPreview item={i} />}
</div>
<DateUserInfo item={i}></DateUserInfo>
</div>

10
package-lock.json generated
View File

@ -1,10 +0,0 @@
{
"name": "utopia-map",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "utopia-map"
}
}
}