A tour that branches
Two buttons choose the way on, each way is a short story of its own, and both hand the tour to a summary once the order is sent.
// What a branch assumes: nothing sent yet, and nothing read yet. Both are the// page's own state rather than a variable this file keeps, which is also where// the chosen path is kept.const fresh = (): void => { ;(at('[data-checked]') as HTMLInputElement).checked = false const status = at('[data-status]') status.textContent = 'Nothing sent yet.' status.dataset['sent'] = 'no'}
const intro = { id: 'intro', // Asked when `choose` advances, and asked again on every run. The page holds // which button was pressed, so nothing is written on this object and a second // run of the tour cannot inherit the first run's branch. next: () => (at('[data-panel]').dataset['path'] === 'quick' ? quick : careful), steps: [ { id: 'total', target: '[data-total]', message: 'Where every path starts. Press Next.', }, { id: 'choose', target: { elements: ['[data-careful]', '[data-quick]'], interactive: true }, // A control would be a way past the choice, so this step has none and the // buttons report instead. Which story that opens is `next`'s answer. awaits: 'path-chosen', message: 'Two ways on. Press either. The page says which was pressed and ' + 'this story says what follows it, so watch the footer: the switch ' + 'cuts rather than morphs, because two unrelated stories ' + 'interpolating into each other would be a strange thing to watch.', }, ],} satisfies LekoStory
const summary = { // Where the paths meet — DESIGN.md, **What two paths share is a story, // not a step they both point at**. It says what it needs in its own // `onEnter`. // // No `next`. The tour ends here, and running out of steps with nothing after // it is how a tour ends. id: 'summary', steps: [ { id: 'summary', target: '[data-status]', message: 'The rejoin, and a story of its own. Both branches name it in ' + '‘next’, so neither of them had to know how many steps came ' + 'before it.', }, ],} satisfies LekoStory
const careful = { id: 'careful', next: summary, onEnter: fresh, steps: [ { id: 'lines', // The box is the target, because ticking it is the work. The rows // it is a claim about get a cutout of their own rather than joining // the union, so the space between the two stays dimmed and stays // blocked. target: [{ elements: '[data-check]', interactive: true }, '[data-lines]'], message: 'careful 1/2 in the footer. A branch counts from one, because ' + 'the count belongs to the story that is running.', awaits: 'lines-checked', }, { id: 'send', target: { elements: '[data-send]', interactive: true }, message: 'Send it. That ends this branch, and the branch says what follows it.', awaits: 'order-sent', }, ],} satisfies LekoStory
const quick = { id: 'quick', next: summary, onEnter: fresh, steps: [ { id: 'send', target: { elements: '[data-send]', interactive: true }, message: 'quick 1/1. Same button, same signal, same rejoin, one step to get there.', awaits: 'order-sent', }, ],} satisfies LekoStory// The page records which way it went and says that the choice was made.// Which story that opens is `intro`'s to answer, so neither branch is named// here and a third one would be added to `next` rather than to this file's// event listeners.for (const path of ['careful', 'quick'] as const) { panel.querySelector(`[data-${path}]`)!.addEventListener('click', () => { panel.dataset['path'] = path leko.reached('path-chosen') })}
checkbox.addEventListener('change', () => { if (checkbox.checked) leko.reached('lines-checked')})
panel.querySelector('[data-send]')!.addEventListener('click', () => { status.textContent = 'Order sent.' status.dataset['sent'] = 'yes'
// One call, at the point the order goes. Both branches end on it, and // neither of them is named here. leko.reached('order-sent')})