mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(pdf): dress the layout switch like the rest of the app
The preview shipped a bare checkbox, which is not what a toggle looks like anywhere else in TREK. The overlay is imperative DOM, so the switch from Settings/ToggleSwitch cannot be rendered into it — as static markup it would carry no behaviour — but its geometry and its tokens can be, and the control now reads as the same one.
This commit is contained in:
@@ -929,7 +929,8 @@ describe('page breaks between days (#1292)', () => {
|
||||
})
|
||||
|
||||
const toggle = () =>
|
||||
document.querySelector<HTMLInputElement>('#pdf-daybreak-toggle input')
|
||||
document.querySelector<HTMLButtonElement>('#pdf-daybreak-toggle')
|
||||
const isOn = () => toggle()!.getAttribute('aria-checked') === 'true'
|
||||
|
||||
it('FE-PDF-BREAK-001: days break onto their own page by default', async () => {
|
||||
await downloadTripPDF(twoDays)
|
||||
@@ -937,7 +938,17 @@ describe('page breaks between days (#1292)', () => {
|
||||
|
||||
expect(html).toContain('day-section day-break')
|
||||
expect(html).not.toContain('class="pdf-flow"')
|
||||
expect(toggle()!.checked).toBe(true)
|
||||
expect(isOn()).toBe(true)
|
||||
})
|
||||
|
||||
it('FE-PDF-BREAK-001b: the control is the switch from the rest of the app, not a checkbox', async () => {
|
||||
await downloadTripPDF(twoDays)
|
||||
const el = toggle()!
|
||||
|
||||
expect(el.tagName).toBe('BUTTON')
|
||||
expect(el.getAttribute('role')).toBe('switch')
|
||||
expect(el.style.background).toContain('--accent')
|
||||
expect(document.querySelector('#pdf-daybreak-toggle input')).toBeNull()
|
||||
})
|
||||
|
||||
it('FE-PDF-BREAK-002: the remembered flowing layout comes back on the next export', async () => {
|
||||
@@ -948,7 +959,9 @@ describe('page breaks between days (#1292)', () => {
|
||||
// The class stays on the day; the body is what decides whether it breaks.
|
||||
expect(html).toContain('day-section day-break')
|
||||
expect(html).toContain('<body class="pdf-flow">')
|
||||
expect(toggle()!.checked).toBe(false)
|
||||
expect(isOn()).toBe(false)
|
||||
// Off uses the neutral track, so the two states are told apart by more than the knob.
|
||||
expect(toggle()!.style.background).toContain('--border-primary')
|
||||
})
|
||||
|
||||
it('FE-PDF-BREAK-003: the first day never carries a break of its own', async () => {
|
||||
@@ -961,10 +974,9 @@ describe('page breaks between days (#1292)', () => {
|
||||
|
||||
it('FE-PDF-BREAK-004: turning the breaks off is remembered and shown at once', async () => {
|
||||
await downloadTripPDF(twoDays)
|
||||
const box = toggle()!
|
||||
box.checked = false
|
||||
box.dispatchEvent(new Event('change'))
|
||||
toggle()!.click()
|
||||
|
||||
expect(isOn()).toBe(false)
|
||||
expect(localStorage.getItem('trek_pdf_page_break_per_day')).toBe('0')
|
||||
const body = getIframe()!.contentDocument?.body
|
||||
if (body) expect(body.classList.contains('pdf-flow')).toBe(true)
|
||||
@@ -973,10 +985,9 @@ describe('page breaks between days (#1292)', () => {
|
||||
it('FE-PDF-BREAK-005: turning them back on is remembered too', async () => {
|
||||
localStorage.setItem('trek_pdf_page_break_per_day', '0')
|
||||
await downloadTripPDF(twoDays)
|
||||
const box = toggle()!
|
||||
box.checked = true
|
||||
box.dispatchEvent(new Event('change'))
|
||||
toggle()!.click()
|
||||
|
||||
expect(isOn()).toBe(true)
|
||||
expect(localStorage.getItem('trek_pdf_page_break_per_day')).toBe('1')
|
||||
const body = getIframe()!.contentDocument?.body
|
||||
if (body) expect(body.classList.contains('pdf-flow')).toBe(false)
|
||||
@@ -991,8 +1002,6 @@ describe('page breaks between days (#1292)', () => {
|
||||
expect(html).toContain('day-section day-break')
|
||||
expect(html).not.toContain('class="pdf-flow"')
|
||||
|
||||
const box = toggle()!
|
||||
box.checked = false
|
||||
expect(() => box.dispatchEvent(new Event('change'))).not.toThrow()
|
||||
expect(() => toggle()!.click()).not.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -32,6 +32,14 @@ function rememberPageBreakPerDay(on: boolean): void {
|
||||
}
|
||||
}
|
||||
|
||||
/* The overlay is imperative DOM, so the switch from Settings/ToggleSwitch is
|
||||
rebuilt here rather than rendered — as static markup it would carry no
|
||||
behaviour. Same geometry and the same tokens, so it reads as the same control. */
|
||||
const TOGGLE_TRACK = 'position:relative;width:44px;height:24px;min-width:44px;flex-shrink:0;border-radius:12px;border:none;padding:0;cursor:pointer;transition:background 0.2s;'
|
||||
const TOGGLE_KNOB = 'position:absolute;top:2px;width:20px;height:20px;border-radius:50%;background:white;transition:left 0.2s;box-shadow:0 1px 3px rgba(0,0,0,0.2);'
|
||||
const trackColour = (on: boolean) => (on ? 'var(--accent, #111827)' : 'var(--border-primary, #d1d5db)')
|
||||
const knobOffset = (on: boolean) => (on ? '22px' : '2px')
|
||||
|
||||
function renderLucideIcon(icon:LucideIcon, props = {}) {
|
||||
if (!_renderToStaticMarkup) return ''
|
||||
return _renderToStaticMarkup(
|
||||
@@ -699,10 +707,10 @@ ${pluginSectionsHtml}
|
||||
header.innerHTML = `
|
||||
<span style="font-size:13px;font-weight:600;color:var(--text-primary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escHtml(trip?.title || tr('pdf.travelPlan'))}</span>
|
||||
<div style="display:flex;align-items:center;gap:8px">
|
||||
<label id="pdf-daybreak-toggle" style="display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-muted);cursor:pointer;user-select:none">
|
||||
<input type="checkbox"${breaksPerDay ? ' checked' : ''} style="margin:0;cursor:pointer">
|
||||
${escHtml(tr('pdf.pageBreakPerDay'))}
|
||||
</label>
|
||||
<label for="pdf-daybreak-toggle" style="font-size:12px;color:var(--text-muted);cursor:pointer;user-select:none">${escHtml(tr('pdf.pageBreakPerDay'))}</label>
|
||||
<button id="pdf-daybreak-toggle" type="button" role="switch" aria-checked="${breaksPerDay}" aria-label="${escHtml(tr('pdf.pageBreakPerDay'))}" style="${TOGGLE_TRACK} background:${trackColour(breaksPerDay)}">
|
||||
<span style="${TOGGLE_KNOB} left:${knobOffset(breaksPerDay)}"></span>
|
||||
</button>
|
||||
<button id="pdf-print-btn" style="display:flex;align-items:center;gap:5px;font-size:12px;font-weight:500;color:var(--text-muted);background:none;border:none;cursor:pointer;padding:4px 8px;border-radius:6px;font-family:inherit">${tr('pdf.saveAsPdf')}</button>
|
||||
<button id="pdf-close-btn" style="background:none;border:none;cursor:pointer;color:var(--text-faint);display:flex;padding:4px;border-radius:6px">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||||
@@ -729,9 +737,14 @@ ${pluginSectionsHtml}
|
||||
|
||||
// The two layouts differ by one class, so switching is instant in the preview
|
||||
// and there is no need to re-fetch photos or rebuild the document.
|
||||
const dayBreakBox = header.querySelector<HTMLInputElement>('#pdf-daybreak-toggle input')
|
||||
if (dayBreakBox) dayBreakBox.onchange = () => {
|
||||
rememberPageBreakPerDay(dayBreakBox.checked)
|
||||
iframe.contentDocument?.body.classList.toggle('pdf-flow', !dayBreakBox.checked)
|
||||
const dayBreakSwitch = header.querySelector<HTMLButtonElement>('#pdf-daybreak-toggle')
|
||||
if (dayBreakSwitch) dayBreakSwitch.onclick = () => {
|
||||
const on = dayBreakSwitch.getAttribute('aria-checked') !== 'true'
|
||||
dayBreakSwitch.setAttribute('aria-checked', String(on))
|
||||
dayBreakSwitch.style.background = trackColour(on)
|
||||
const knob = dayBreakSwitch.firstElementChild as HTMLElement | null
|
||||
if (knob) knob.style.left = knobOffset(on)
|
||||
rememberPageBreakPerDay(on)
|
||||
iframe.contentDocument?.body.classList.toggle('pdf-flow', !on)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user