diff --git a/src/lib/cardbuilder/builder.js b/src/lib/cardbuilder/builder.js index faf0a04d..e6ed6f16 100644 --- a/src/lib/cardbuilder/builder.js +++ b/src/lib/cardbuilder/builder.js @@ -240,7 +240,7 @@ export function mountCardBuilder({ form, data }) { wasSet.wmode = wset; wasSet.hmode = hset; el('eb-px').hidden = !widthOpen; - el('eb-px-w').hidden = el('eb-widths').hidden = !wset; + el('eb-px-w').hidden = !wset; el('eb-px-h').hidden = !hset; el('eb-shapes').hidden = !(wset && hset); el('eb-w-shown').textContent = wset && !widthOpen ? `${s.width}px` : 'set'; @@ -582,7 +582,7 @@ export function mountCardBuilder({ form, data }) { } // The surface the preview's modules are given. - const b = { form, el, items, space, areasOn, st, trackBetween, trackName, showTracks, tune, render, setRadio, sideKind, showGap, stackedIn, moveButton }; + const b = { limits, form, el, items, space, areasOn, st, trackBetween, trackName, showTracks, tune, render, setRadio, sideKind, showGap, stackedIn, moveButton }; tuneAll.addEventListener('click', () => tune(tuneAll.textContent === 'hide all' ? [] : tuneRows.map((r) => r.dataset.tune))); // The PNG or PDF, drawn here from the preview when asked for. diff --git a/src/lib/cardbuilder/overlay.js b/src/lib/cardbuilder/overlay.js index bf5cf8dd..9cd725f7 100644 --- a/src/lib/cardbuilder/overlay.js +++ b/src/lib/cardbuilder/overlay.js @@ -56,6 +56,40 @@ export function attachGrip(frame, b) { }); } +/** A card of fixed width or height has a grip on its right or bottom edge: + * dragging it sizes that dimension live, and on release writes the px box. */ +export function attachSize(frame, b) { + const { form, render, limits } = b; + const doc = frame.contentDocument, card = doc.querySelector('.card'); + if (!card) return; + const { fixedMin, fixedMax, fixedMinHeight } = limits; + for (const [axis, mode, box, lo] of [['w', 'wmode', 'px', fixedMin], ['h', 'hmode', 'hpx', fixedMinHeight]]) { + if (form.elements[mode].value !== 'px') continue; + const g = doc.createElement('span'); + g.className = 'eb-size-grip'; + g.dataset.axis = axis; + g.title = `Drag to set the card’s ${axis === 'w' ? 'width' : 'height'}`; + card.append(g); + g.addEventListener('pointerdown', (e) => { + e.preventDefault(); + e.stopPropagation(); + g.setPointerCapture(e.pointerId); + const r = card.getBoundingClientRect(), x0 = e.clientX, y0 = e.clientY; + let v = null; + const move = (ev) => { + v = clampInt(axis === 'w' ? r.width + ev.clientX - x0 : r.height + ev.clientY - y0, lo, fixedMax); + if (axis === 'w') frame.style.width = `${v}px`; else card.style.height = `${v}px`; + }; + const up = () => { + if (v == null) return; + form.elements[box].value = v; + render(); + }; + drag(g, move, up); + }); + } +} + /** In the preview, a ⊕ between each two slots that show — the first track * between them, where parts left out leave several — to add space there, * and a hatched band over each track that has some. */ diff --git a/src/lib/cardbuilder/preview.js b/src/lib/cardbuilder/preview.js index 0ad32fca..58c0c1b3 100644 --- a/src/lib/cardbuilder/preview.js +++ b/src/lib/cardbuilder/preview.js @@ -12,7 +12,7 @@ import { FIGURE_SLOTS } from '../cardname.js'; import { inBox } from './model.js'; import { cellGrid, partZones, venueDrops } from './geometry.js'; import { drag, shownIn } from './dom.js'; -import { attachGrip, attachPlus } from './overlay.js'; +import { attachGrip, attachPlus, attachSize } from './overlay.js'; // The preview's hitboxes: each part of the card, outlined, brings up what // tunes it — first match wins, so a link button beats the box it sits in, @@ -58,6 +58,7 @@ export function attachPreview(frame, b) { doc.documentElement.classList.add('eb-hits'); attachGrip(frame, b); attachPlus(frame, b); + attachSize(frame, b); // A part is what it is; what is left — the card's own room, the box a // buttons' box fills its side with, or a title's box past its words — is the // area under the pointer, where there is one, not the part that happens @@ -72,7 +73,7 @@ export function attachPreview(frame, b) { return [...r.getClientRects()].some((b) => x >= b.left - 2 && x <= b.right + 2 && y >= b.top - 2 && y <= b.bottom + 2); }; const find = (t, x, y) => { - if (t.closest?.('.eb-plus, .eb-band, .eb-area-grip, .eb-areabtn')) return null; + if (t.closest?.('.eb-plus, .eb-band, .eb-area-grip, .eb-size-grip, .eb-areabtn')) return null; for (const [q, keys] of HITS) { const el = t.closest?.(q); if (!el) continue; diff --git a/src/pages/tools/card.astro b/src/pages/tools/card.astro index fe45ea95..41971532 100644 --- a/src/pages/tools/card.astro +++ b/src/pages/tools/card.astro @@ -102,7 +102,7 @@ const data = {