-
-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathworkshop.js
More file actions
120 lines (116 loc) · 5.38 KB
/
Copy pathworkshop.js
File metadata and controls
120 lines (116 loc) · 5.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// The production page is the source of every live preview. Nothing is written back to it.
const variants = [
['round-bottom', 'Original', 'Below players', 'round', false],
['round-middle', 'Rounded pills', 'Between waveforms', 'round', true],
['soft-bottom', 'Soft pointers', 'Below players', 'soft', false],
['soft-middle', 'Soft pointers', 'Between waveforms', 'soft', true],
['chevron-bottom', 'Chevrons', 'Below players', 'chevron', false],
['chevron-middle', 'Chevrons', 'Between waveforms', 'chevron', true]
]
const grid = document.querySelector('#variants'), status = document.querySelector('#workshop-status')
const namespace = 'http://www.w3.org/2000/svg'
function outline(width, shape) {
const end = width - .5, shoulder = width - 12
return shape === 'soft'
? `M15 .5H${shoulder - 2}Q${shoulder} .5 ${shoulder + 2} 3L${end - 1} 13Q${end + .3} 15 ${end - 1} 17L${shoulder + 2} 27Q${shoulder} 29.5 ${shoulder - 2} 29.5H15A14.5 14.5 0 0 1 15 .5Z`
: `M.75 .5H${shoulder}L${end} 15L${shoulder} 29.5H.75L12 15Z`
}
function connect(frame, shape) {
const doc = frame.contentDocument, win = frame.contentWindow, demo = doc.querySelector('.demo')
let pending, reveal
function refresh() {
pending = null
if (shape !== 'round') for (const pill of doc.querySelectorAll('.chain .pill')) {
let svg = pill.querySelector('.crumb-outline')
if (!svg) {
svg = doc.createElementNS(namespace, 'svg')
svg.classList.add('crumb-outline')
svg.setAttribute('aria-hidden', 'true')
svg.append(doc.createElementNS(namespace, 'path'))
pill.prepend(svg)
}
const width = pill.offsetWidth
svg.setAttribute('viewBox', `0 0 ${width} 30`)
svg.firstElementChild.setAttribute('d', outline(width, shape))
}
// Include open popovers so the iframe never clips a connected menu.
let height = demo.offsetHeight + 80, menuTop = Infinity, menuBottom = 0
for (const menu of doc.querySelectorAll('[popover]:popover-open')) {
const panel = menu.querySelector('.pill-body, .add-body')
const top = menu.getBoundingClientRect().top + win.scrollY
menuTop = Math.min(menuTop, top)
menuBottom = Math.max(menuBottom, top + panel.offsetTop + panel.scrollHeight + 2)
height = Math.max(height, menuBottom + 24)
}
frame.parentElement.style.setProperty('--player-height', demo.offsetHeight + 'px')
frame.style.height = Math.ceil(height) + 'px'
if (reveal && menuBottom) {
const top = frame.getBoundingClientRect().top
const shift = Math.min(Math.max(0, top + menuBottom - innerHeight + 24), Math.max(0, top + menuTop - 24))
if (shift) scrollBy({ top: Math.ceil(shift), behavior: 'instant' })
}
reveal = false
}
const schedule = () => { if (!pending) pending = requestAnimationFrame(refresh) }
new ResizeObserver(schedule).observe(demo)
new MutationObserver(schedule).observe(doc.querySelector('.chain'), { childList: true, subtree: true, characterData: true })
const menus = new MutationObserver(() => { reveal = true; schedule() })
for (const menu of doc.querySelectorAll('[popover]')) {
menus.observe(menu, { childList: true, subtree: true, characterData: true })
menu.addEventListener('toggle', event => { reveal = event.newState === 'open'; schedule() })
}
win.addEventListener('resize', schedule)
doc.fonts.ready.then(schedule)
refresh()
frame.dataset.ready = 'true'
}
try {
const response = await fetch('index.html')
if (!response.ok) throw Error('Page unavailable')
const source = await response.text()
for (const [id, name, position, shape, middle] of variants) {
const card = document.createElement('section')
card.className = 'variant'
card.id = id
const heading = document.createElement('div')
heading.className = 'variant-heading'
const title = document.createElement('h2'), placement = document.createElement('span')
title.textContent = name
placement.textContent = position
heading.append(title, placement)
const frame = document.createElement('iframe')
frame.title = `${name} — ${position.toLowerCase()}`
frame.loading = 'lazy'
const doc = new DOMParser().parseFromString(source, 'text/html')
doc.documentElement.classList.add('workshop-preview')
doc.documentElement.dataset.shape = shape
const base = doc.createElement('base'), css = doc.createElement('link')
base.href = new URL('index.html', location.href).href
css.rel = 'stylesheet'
css.href = 'workshop.css'
doc.head.prepend(base)
doc.head.append(css)
if (middle) {
const pipeline = doc.createElement('div')
pipeline.className = 'pipeline'
const edited = doc.querySelector('.edited-row')
edited.before(pipeline)
pipeline.append(doc.querySelector('.chain'), doc.querySelector('.tool.add'))
}
frame.addEventListener('load', () => connect(frame, shape), { once: true })
frame.srcdoc = '<!doctype html>\n' + doc.documentElement.outerHTML
const surface = document.createElement('div')
surface.className = 'variant-player'
surface.append(frame)
card.append(heading, surface)
grid.append(card)
}
status.textContent = ''
} catch {
status.textContent = 'Could not load the players. Reload to try again.'
} finally {
grid.setAttribute('aria-busy', 'false')
}
document.querySelector('.preview-width').addEventListener('change', event => {
document.body.dataset.width = event.target.value
})