import { useMemo } from 'preact/hooks'
import { useVersion } from '../contexts/Version.jsx'
import { useAsync } from '../hooks/useAsync.js'
import { getLanguage, replaceTranslation } from '../services/Resources.js'
interface StyleData {
color?: string,
bold?: boolean,
italic?: boolean,
underlined?: boolean,
strikethrough?: boolean,
}
interface PartData extends StyleData {
text?: string,
translate?: string,
fallback?: string,
with?: string[],
}
interface Props {
component: unknown,
base?: StyleData,
shadow?: boolean,
}
export function TextComponent({ component, base = { color: 'white' }, shadow = true }: Props) {
const { version } = useVersion()
const state = JSON.stringify(component)
const parts = useMemo(() => {
const parts: PartData[] = []
visitComponent(component, el => parts.push(inherit(el, base)))
return parts
}, [state, base])
const { value: language } = useAsync(() => getLanguage(version), [version])
return
{shadow &&
{parts.map(p => )}
}
{parts.map(p => )}
}
function visitComponent(component: unknown, consumer: (c: PartData) => void) {
if (typeof component === 'string' || typeof component === 'number') {
consumer({ text: component.toString() })
} else if (Array.isArray(component)) {
const base = component[0] ?? {}
visitComponent(base, consumer)
for (const c of component.slice(1)) {
visitComponent(c, d => consumer(inherit(d, base)))
}
} else if (typeof component === 'object' && component !== null) {
if ('text' in component) {
consumer(component)
} else if ('translate' in component) {
consumer(component)
} else if ('score' in component) {
consumer({ ...component, text: '123' })
} else if ('selector' in component) {
consumer({ ...component, text: 'Steve' })
} else if ('keybind' in component) {
consumer({ ...component, text: (component as any).keybind })
} else if ('nbt' in component) {
consumer({ ...component, text: (component as any).nbt })
}
if ('extra' in component) {
for (const e of (component as any).extra) {
visitComponent(e, c => consumer(inherit(c, component)))
}
}
}
}
function inherit(component: object, base: PartData) {
return {
color: base.color,
bold: base.bold,
italic: base.italic,
underlined: base.underlined,
strikethrough: base.strikethrough,
...component,
}
}
const TextColors = {
black: ['#000', '#000'],
dark_blue: ['#00A', '#00002A'],
dark_green: ['#0A0', '#002A00'],
dark_aqua: ['#0AA', '#002A2A'],
dark_red: ['#A00', '#2A0000'],
dark_purple: ['#A0A', '#2A002A'],
gold: ['#FA0', '#2A2A00'],
gray: ['#AAA', '#2A2A2A'],
dark_gray: ['#555', '#151515'],
blue: ['#55F', '#15153F'],
green: ['#5F5', '#153F15'],
aqua: ['#5FF', '#153F3F'],
red: ['#F55', '#3F1515'],
light_purple: ['#F5F', '#3F153F'],
yellow: ['#FF5', '#3F3F15'],
white: ['#FFF', '#3F3F3F'],
}
type TextColorKey = keyof typeof TextColors
const TextColorKeys = Object.keys(TextColors)
function TextPart({ part, shadow, lang }: { part: PartData, shadow?: boolean, lang: Record }) {
if (part.translate) {
const str = resolveTranslate(part.translate, part.fallback, part.with, lang)
return {str}
}
return {part.text}
}
function resolveTranslate(translate: string, fallback: string | undefined, with_: any[] | undefined, lang: Record): string {
const str = lang[translate] ?? fallback
if (typeof str !== 'string') return translate
const params = with_?.map((c): string => {
if (typeof c === 'string' || typeof c === 'number') return `${c}`
if (c.text) return c.text
if (c.translate) return resolveTranslate(c.translate, c.fallback, c.with, lang)
return ''
})
return replaceTranslation(str, params)
}
function createStyle(style: StyleData, shadow?: boolean) {
return {
color: style.color && (TextColorKeys.includes(style.color)
? TextColors[style.color as TextColorKey][shadow ? 1 : 0]
: shadow ? 'transparent' : style.color),
fontWeight: (style.bold === true) ? 'bold' : undefined,
fontStyle: (style.italic === true) ? 'italic' : undefined,
textDecoration: (style.underlined === true)
? (style.strikethrough === true) ? 'underline line-through' : 'underline'
: (style.strikethrough === true) ? 'line-through' : undefined,
}
}