Sheet: large portrait slot, writable notes in summary and lobby
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJhXuLD2Z6hv3Wqeq9UL9K
This commit is contained in:
+36
-15
@@ -31,6 +31,8 @@ export function printSheet(root, title) {
|
||||
const win = window.open('', '_blank')
|
||||
if (!win) { window.alert('Please allow pop-ups for this site to print the sheet.'); return }
|
||||
const clone = root.cloneNode(true)
|
||||
const src = root.querySelectorAll('textarea'), dst = clone.querySelectorAll('textarea')
|
||||
dst.forEach((t, i) => { t.textContent = src[i].value }) // typed text does not survive cloning on its own
|
||||
clone.style.zoom = ''
|
||||
clone.classList.remove('dark') // printed pages are always light
|
||||
const head = [...document.querySelectorAll('link[rel=stylesheet], style')].map((n) => n.outerHTML).join('')
|
||||
@@ -47,6 +49,17 @@ export function printSheet(root, title) {
|
||||
links.forEach((l) => { l.addEventListener('load', () => { if (--pending === 0) setTimeout(go, 150) }); l.addEventListener('error', () => { if (--pending === 0) go() }) })
|
||||
}
|
||||
|
||||
// Ruled note area. With onChange (own sheet in the summary or the lobby) it can be typed into, otherwise it just shows the text.
|
||||
function Notes({ title, value, onChange }) {
|
||||
return (
|
||||
<div className="psec pnotes"><h3>{title}</h3>
|
||||
{onChange
|
||||
? <textarea className="plines pwrite" value={value || ''} maxLength={5000} onChange={(e) => onChange(e.target.value)} placeholder="Click here to write …" aria-label={title} />
|
||||
: <div className="plines pwrite">{value}</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Scales the A4 pages down when the space is narrower than the paper
|
||||
function Fit({ children, rootRef, dark }) {
|
||||
const wrap = useRef(null)
|
||||
@@ -68,7 +81,7 @@ const Pips = ({ n, locked }) => (
|
||||
)
|
||||
|
||||
// The character sheet as A4 pages (page 1: attributes and skills, page 2: talents, mutations, equipment, notes).
|
||||
export default function PaperSheet({ data, baseRules, tools, bare, onRoll }) {
|
||||
export default function PaperSheet({ data, baseRules, tools, bare, onRoll, onNotes }) {
|
||||
const ch = useMemo(() => normalize(data, baseRules), [data, baseRules])
|
||||
const rules = useMemo(() => effectiveRules(baseRules, ch), [baseRules, ch])
|
||||
const c = useMemo(() => compute(ch, rules), [ch, rules])
|
||||
@@ -108,18 +121,26 @@ export default function PaperSheet({ data, baseRules, tools, bare, onRoll }) {
|
||||
)}
|
||||
<Fit rootRef={root} dark={dark}>
|
||||
<section className="paper">
|
||||
<header className="phead">
|
||||
<div className="pname">{name}</div>
|
||||
{ch.portrait && <img className="pportrait" src={ch.portrait} alt="" />}
|
||||
<div className="pbrand">Arcane Wasteland<span>Character sheet</span></div>
|
||||
</header>
|
||||
<div className="pfields">
|
||||
<Field label="Player">{ch.player}</Field>
|
||||
<Field label="Concept">{ch.concept}</Field>
|
||||
<Field label="Class">{cls && [cls.name, sub?.name].filter(Boolean).join(' / ')}</Field>
|
||||
<Field label="Profession">{prof?.name}</Field>
|
||||
<Field label="Rules">{preset}</Field>
|
||||
</div>
|
||||
{(() => {
|
||||
const head = (
|
||||
<header className="phead">
|
||||
<div className="pname">{name}</div>
|
||||
<div className="pbrand">Arcane Wasteland<span>Character sheet</span></div>
|
||||
</header>
|
||||
)
|
||||
const fields = (
|
||||
<div className="pfields">
|
||||
<Field label="Player">{ch.player}</Field>
|
||||
<Field label="Concept">{ch.concept}</Field>
|
||||
<Field label="Class">{cls && [cls.name, sub?.name].filter(Boolean).join(' / ')}</Field>
|
||||
<Field label="Profession">{prof?.name}</Field>
|
||||
<Field label="Rules">{preset}</Field>
|
||||
</div>
|
||||
)
|
||||
return ch.portrait
|
||||
? <div className="pidentity"><img className="pportrait" src={ch.portrait} alt="" /><div className="pidright">{head}{fields}</div></div>
|
||||
: <>{head}{fields}</>
|
||||
})()}
|
||||
|
||||
<div className="pattrs">
|
||||
{rules.attributes.map((a) => (
|
||||
@@ -165,7 +186,7 @@ export default function PaperSheet({ data, baseRules, tools, bare, onRoll }) {
|
||||
{data.loot && <><h3>Loot</h3><p className="pre">{data.loot}</p></>}
|
||||
</div>
|
||||
)}
|
||||
<div className="psec pnotes"><h3>Conditions & notes</h3><div className="plines" /></div>
|
||||
<Notes title="Conditions & notes" value={ch.conditions} onChange={onNotes && ((v) => onNotes({ conditions: v }))} />
|
||||
<Foot n={1} />
|
||||
</section>
|
||||
|
||||
@@ -208,7 +229,7 @@ export default function PaperSheet({ data, baseRules, tools, bare, onRoll }) {
|
||||
|
||||
{ch.background && <div className="psec"><h3>Background</h3><p className="pbg">{ch.background}</p></div>}
|
||||
|
||||
<div className="psec pnotes"><h3>Notes</h3><div className="plines" /></div>
|
||||
<Notes title="Notes" value={ch.notes} onChange={onNotes && ((v) => onNotes({ notes: v }))} />
|
||||
<Foot n={2} />
|
||||
</section>
|
||||
</Fit>
|
||||
|
||||
Reference in New Issue
Block a user