Files
NightSpire/web-next/components/RichTextArea.tsx
T
Inna fa7897c195 Prefijos del tema nw- y uw- -> ns- (carpetas, ficheros, clases y rutas)
Renombrado completo de los prefijos heredados: 19 carpetas, 11 ficheros y 318
referencias en 35 ficheros de código, más las clases y las rutas url() de dentro
del CSS del tema. Se usa git mv para conservar el historial.

También fuera del código, que un sed no ve:
- BD: votesite.image_url (4 filas) apuntaba a /nw-themes/...
- Ficheros con la marca vieja en el NOMBRE: novawow-maintenance.webp ->
  nightspire-maintenance.webp (lo usa la página de mantenimiento) y
  store_novawow_response.js.

⚠ Alias en Caddy /nw-themes/* -> /ns-themes/*: los correos ENVIADOS antes del
rebranding llevan esas rutas escritas y están en las bandejas de los usuarios.
Sin el alias, sus imágenes se romperían. Verificado que las rutas viejas siguen
sirviendo 200.

Nota: ns-js/ y ns-js-handlers/ son CÓDIGO MUERTO (manejadores jQuery del portal
Django, que ya se borró). Se midió en el navegador: la web no pide ni un solo JS
del tema. Se renombran igualmente por consistencia, pero son candidatos a
borrarse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 15:50:43 +00:00

85 lines
2.8 KiB
TypeScript

'use client'
import { useRef } from 'react'
import { useTranslations } from 'next-intl'
/**
* Editor ligero: barra de formato que envuelve la selección del textarea en HTML
* (que luego se sanea en el servidor con cleanPostHtml). Sin dependencias pesadas.
*/
export function RichTextArea({
value,
onChange,
placeholder,
rows = 4,
}: {
value: string
onChange: (v: string) => void
placeholder?: string
rows?: number
}) {
const t = useTranslations('Editor')
const ref = useRef<HTMLTextAreaElement>(null)
function surround(before: string, after: string, placeholderText = '') {
const el = ref.current
if (!el) return
const start = el.selectionStart
const end = el.selectionEnd
const selected = value.slice(start, end) || placeholderText
const next = value.slice(0, start) + before + selected + after + value.slice(end)
onChange(next)
// Reposiciona el cursor dentro del texto insertado tras el re-render.
requestAnimationFrame(() => {
el.focus()
const pos = start + before.length
el.setSelectionRange(pos, pos + selected.length)
})
}
function insertLink() {
const url = prompt(t('linkPrompt'), 'https://')
if (!url) return
surround(`<a href="${url.replace(/"/g, '&quot;')}" target="_blank">`, '</a>', t('linkText'))
}
return (
<div>
<div className="lefted">
<button type="button" onClick={() => surround('<strong>', '</strong>')} className="ns-tool-btn" title={t('bold')}>
<b>B</b>
</button>{' '}
<button type="button" onClick={() => surround('<em>', '</em>')} className="ns-tool-btn" title={t('italic')}>
<i>I</i>
</button>{' '}
<button type="button" onClick={() => surround('<u>', '</u>')} className="ns-tool-btn" title={t('underline')}>
<u>U</u>
</button>{' '}
<button type="button" onClick={() => surround('<s>', '</s>')} className="ns-tool-btn" title={t('strike')}>
<s>S</s>
</button>{' '}
<button type="button" onClick={insertLink} className="ns-tool-btn" title={t('link')}>
🔗
</button>{' '}
<button type="button" onClick={() => surround('<blockquote>', '</blockquote>', t('quoteText'))} className="ns-tool-btn" title={t('quote')}>
</button>{' '}
<button type="button" onClick={() => surround('<ul>\n<li>', '</li>\n</ul>', t('listItem'))} className="ns-tool-btn" title={t('list')}>
</button>{' '}
<button type="button" onClick={() => surround('<code>', '</code>')} className="ns-tool-btn" title={t('code')}>
{'</>'}
</button>
</div>
<br />
<textarea
ref={ref}
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
rows={rows}
/>
</div>
)
}