Files
TREK/client/src/components/Studio/StudioSavedLayouts.tsx
T
Maurice f956ab07f3 fix(studio): tidy the head row, the layouts tab and the theme panel
Every piece of the head row is one raised tile of one height: the format
picker moved beside the tools, the assistant and help stand alone, the
close button is square like the rest, and the beta mark is gone. The
layouts tab lays the book's own and the built-in layouts out in sections
and in the same grid as the designed pages. In the theme panel the accent
dots sit in the sheet's corner, the three accents share one name, a font
keeps its name ahead of its kind, a doubled rule is gone, and the
inspector's empty hint is shorter.
2026-10-24 13:54:49 +02:00

85 lines
3.5 KiB
TypeScript

import type { BookPageSetup } from '@trek/shared'
import {
MAX_BOOK_LAYOUTS, bookLayoutPreview as layoutPreview, bookLayoutRole as layoutRole, nextBookLayoutName as nextLayoutName,
} from '@trek/shared'
import { BookmarkPlus, Trash2 } from 'lucide-react'
import { useStudioStore } from '../../store/studioStore'
import { Tooltip } from '../shared/Tooltip'
import { SpreadView } from './SpreadView'
/**
* The book's own layouts, above the built-in ones (#2316).
*
* One button keeps the page on screen as a layout; each card lays the page on
* screen out that way, keeping its pictures and words. The cards draw the
* layout with the same renderer as the page, so what is kept is what is shown.
*/
export function StudioSavedLayouts({
page, width, pxPerMm, t,
}: { page: BookPageSetup; width: number; pxPerMm: number; t: (k: string) => string }) {
const doc = useStudioStore(s => s.doc)
const active = useStudioStore(s => s.activeSpread)
const saveLayout = useStudioStore(s => s.saveLayout)
const removeLayout = useStudioStore(s => s.removeLayout)
const applyLayout = useStudioStore(s => s.applyLayout)
const spread = doc?.spreads[active]
if (!doc || !spread) return null
const all = doc.layouts ?? []
const role = layoutRole(spread)
const fitting = all.filter(l => l.role === role)
const full = all.length >= MAX_BOOK_LAYOUTS
const canSave = spread.elements.length > 0 && !full
const single = role !== 'inner'
const wMm = page.pageWidth * (single ? 1 : 2)
const scale = width / (wMm * pxPerMm)
return (
<div className="st-section st-saved-layouts">
<div className="st-section-label">{t('journey.studio.myLayouts')}</div>
{fitting.length === 0 && <p className="st-hint">{t('journey.studio.myLayoutsEmpty')}</p>}
<div className={`st-thumbs st-tpl-grid ${single ? 'is-pages' : 'is-spreads'}`}>
{fitting.map(layout => (
<div className="st-thumb-row" key={layout.id}>
<button type="button" className="st-thumb" onClick={() => applyLayout(active, layout.id)}>
<div className="st-thumb-sheet" style={{ width, height: page.pageHeight * pxPerMm * scale }}>
<div
style={{
position: 'absolute', left: 0, top: 0,
width: `${wMm}mm`, height: `${page.pageHeight}mm`,
transform: `scale(${scale})`, transformOrigin: 'top left',
}}
>
<SpreadView spread={layoutPreview(layout, page)} page={page} />
</div>
</div>
<span className="st-thumb-label">{layout.name}</span>
</button>
<div className="st-thumb-actions">
<Tooltip label={t('journey.studio.deleteLayout')}>
<button type="button"
className="is-danger"
onClick={() => removeLayout(layout.id)}
aria-label={t('journey.studio.deleteLayout')}
>
<Trash2 size={12} />
</button>
</Tooltip>
</div>
</div>
))}
</div>
<Tooltip label={t(full ? 'journey.studio.saveLayoutFull' : 'journey.studio.saveLayoutHint')}>
<button type="button"
className="st-add-page"
disabled={!canSave}
onClick={() => saveLayout(active, nextLayoutName(all, t('journey.studio.layoutName')))}
>
<BookmarkPlus size={14} />
{t('journey.studio.saveLayout')}
</button>
</Tooltip>
</div>
)
}