Foro: botón «Wowhead» en la barra de TinyMCE
En el editor de crear/responder/editar aparece un botón «Wowhead» que abre un diálogo (tipo: ítem/hechizo/misión/PNJ/logro/objeto, ID y texto opcional) e inserta el enlace de wowhead ya listo: con su href a la rama /wotlk/ en el subdominio del idioma de la web y su data-wowhead, para que salga el tooltip, el color por calidad y el icono (lo pinta el script global tooltips.js). Reutiliza wowheadUrl/wowheadData de lib/wowhead (mismo formato que los enlaces de wowhead del resto del sitio). Si no se pone texto, se usa `<tipo>=<id>`. El ID se limpia a dígitos. El HTML resultante lo sigue saneando el servidor. Verificado: el registro del botón, los textos ES/EN del diálogo y el toolbar con «wowhead» quedan compilados en el bundle; la página del editor responde. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { Editor } from '@tinymce/tinymce-react'
|
import { Editor } from '@tinymce/tinymce-react'
|
||||||
|
import { useLocale } from 'next-intl'
|
||||||
|
import { wowheadUrl, wowheadData, type WowheadType } from '@/lib/wowhead'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Editor TinyMCE del foro (community, self-hosted desde /tinymce — ver
|
* Editor TinyMCE del foro (community, self-hosted desde /tinymce — ver
|
||||||
@@ -12,7 +14,25 @@ import { Editor } from '@tinymce/tinymce-react'
|
|||||||
* `licenseKey: 'gpl'` deja claro que es la edición community (GPL), no la de nube.
|
* `licenseKey: 'gpl'` deja claro que es la edición community (GPL), no la de nube.
|
||||||
* El HTML que produce se sanea SIEMPRE en el servidor con nh3 (lib/forum-sanitize),
|
* El HTML que produce se sanea SIEMPRE en el servidor con nh3 (lib/forum-sanitize),
|
||||||
* nunca se confía en el cliente.
|
* nunca se confía en el cliente.
|
||||||
|
*
|
||||||
|
* Botón «Wowhead»: abre un diálogo (tipo + ID + texto) e inserta un enlace de
|
||||||
|
* wowhead con su data-wowhead en el idioma de la web, para que salga el tooltip,
|
||||||
|
* el color por calidad y el icono (lo pinta el script global tooltips.js).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const WOWHEAD_TYPES: { value: WowheadType; es: string; en: string }[] = [
|
||||||
|
{ value: 'item', es: 'Ítem', en: 'Item' },
|
||||||
|
{ value: 'spell', es: 'Hechizo', en: 'Spell' },
|
||||||
|
{ value: 'quest', es: 'Misión', en: 'Quest' },
|
||||||
|
{ value: 'npc', es: 'PNJ', en: 'NPC' },
|
||||||
|
{ value: 'achievement', es: 'Logro', en: 'Achievement' },
|
||||||
|
{ value: 'object', es: 'Objeto', en: 'Object' },
|
||||||
|
]
|
||||||
|
|
||||||
|
function esc(s: string): string {
|
||||||
|
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
|
||||||
|
}
|
||||||
|
|
||||||
export function ForumEditor({
|
export function ForumEditor({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
@@ -22,6 +42,18 @@ export function ForumEditor({
|
|||||||
onChange: (html: string) => void
|
onChange: (html: string) => void
|
||||||
height?: number
|
height?: number
|
||||||
}) {
|
}) {
|
||||||
|
const locale = useLocale()
|
||||||
|
const es = locale !== 'en'
|
||||||
|
const L = {
|
||||||
|
title: 'Wowhead',
|
||||||
|
tooltip: es ? 'Insertar enlace de Wowhead' : 'Insert a Wowhead link',
|
||||||
|
type: es ? 'Tipo' : 'Type',
|
||||||
|
id: 'ID',
|
||||||
|
text: es ? 'Texto (opcional)' : 'Text (optional)',
|
||||||
|
insert: es ? 'Insertar' : 'Insert',
|
||||||
|
cancel: es ? 'Cancelar' : 'Cancel',
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Editor
|
<Editor
|
||||||
tinymceScriptSrc="/tinymce/tinymce.min.js"
|
tinymceScriptSrc="/tinymce/tinymce.min.js"
|
||||||
@@ -40,8 +72,53 @@ export function ForumEditor({
|
|||||||
plugins:
|
plugins:
|
||||||
'preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount emoticons',
|
'preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount emoticons',
|
||||||
toolbar:
|
toolbar:
|
||||||
'bold italic strikethrough forecolor backcolor emoticons | link image media | alignleft aligncenter alignright alignjustify | numlist bullist outdent indent | removeformat',
|
'bold italic strikethrough forecolor backcolor emoticons | link wowhead image media | alignleft aligncenter alignright alignjustify | numlist bullist outdent indent | removeformat',
|
||||||
image_advtab: true,
|
image_advtab: true,
|
||||||
|
setup: (editor) => {
|
||||||
|
editor.ui.registry.addButton('wowhead', {
|
||||||
|
text: 'Wowhead',
|
||||||
|
tooltip: L.tooltip,
|
||||||
|
onAction: () => {
|
||||||
|
editor.windowManager.open({
|
||||||
|
title: L.title,
|
||||||
|
body: {
|
||||||
|
type: 'panel',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'listbox',
|
||||||
|
name: 'wtype',
|
||||||
|
label: L.type,
|
||||||
|
items: WOWHEAD_TYPES.map((t) => ({ value: t.value, text: es ? t.es : t.en })),
|
||||||
|
},
|
||||||
|
{ type: 'input', name: 'wid', label: L.id },
|
||||||
|
{ type: 'input', name: 'wtext', label: L.text },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
initialData: { wtype: 'item', wid: '', wtext: '' },
|
||||||
|
buttons: [
|
||||||
|
{ type: 'cancel', text: L.cancel },
|
||||||
|
{ type: 'submit', text: L.insert, primary: true },
|
||||||
|
],
|
||||||
|
onSubmit: (api) => {
|
||||||
|
const data = api.getData() as { wtype: WowheadType; wid: string; wtext: string }
|
||||||
|
const id = String(data.wid).trim().replace(/\D/g, '')
|
||||||
|
if (!id) {
|
||||||
|
api.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const type = (data.wtype || 'item') as WowheadType
|
||||||
|
const label = data.wtext.trim() || `${type}=${id}`
|
||||||
|
const href = wowheadUrl(type, id, locale)
|
||||||
|
const wh = wowheadData(type, id, locale)
|
||||||
|
editor.insertContent(
|
||||||
|
`<a href="${href}" data-wowhead="${esc(wh)}" target="_blank" rel="noopener noreferrer">${esc(label)}</a> `,
|
||||||
|
)
|
||||||
|
api.close()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user