import type { Editor } from '@tiptap/core'; import { useEditorState } from '@tiptap/react'; import { useTranslation } from 'react-i18next'; import type { SectionStyleOption } from '../plugins/use-pond-plugins'; /** The option's label in the UI language, falling back through English to the * raw id (a manifest always carries de and en, so the id is a last resort). */ function optionLabel(option: SectionStyleOption, language: string): string { const base = language.split('-')[0] ?? language; return option.title[base] ?? option.title.en ?? option.styleId; } /** * Toolbar control for styled sections (issue #75): a picker over the section * styles the pond's active `section_style` plugins declare. Selecting a style * wraps the selection (or restyles the surrounding section); the dedicated * button unwraps. Hidden entirely when no style plugin is active — the * feature only exists where an admin turned a plugin on. */ export function SectionStyleMenu({ editor, options, }: { editor: Editor; options: SectionStyleOption[]; }): React.JSX.Element | null { const { t, i18n } = useTranslation('editor'); const active = useEditorState({ editor, selector: ({ editor: e }) => { if (!e.isActive('section')) return null; const attrs = e.getAttributes('section'); return { pluginId: String(attrs.pluginId ?? ''), styleId: String(attrs.styleId ?? '') }; }, }); if (options.length === 0) return null; const activeKey = active ? `${active.pluginId}/${active.styleId}` : ''; function applyStyle(key: string): void { const [pluginId, styleId] = key.split('/'); if (!pluginId || !styleId) return; const chain = editor.chain().focus(); if (active) { chain.updateAttributes('section', { pluginId, styleId }).run(); } else { chain.wrapInSection({ pluginId, styleId }).run(); } } return (