Skip to content

Commit

Permalink
Refactor: add a utility to create DOM elements
Browse files Browse the repository at this point in the history
  • Loading branch information
katspaugh committed Dec 27, 2023
1 parent 41e64cc commit 4a33f16
Show file tree
Hide file tree
Showing 11 changed files with 341 additions and 187 deletions.
73 changes: 73 additions & 0 deletions cypress/e2e/envelope.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const id = '#waveform'

describe('WaveSurfer Envelope plugin tests', () => {
it('should render an envelope', () => {
cy.visit('cypress/e2e/index.html')
cy.window().then((win) => {
return new Promise((resolve) => {
win.wavesurfer = win.WaveSurfer.create({
container: id,
height: 200,
url: '../../examples/audio/demo.wav',
plugins: [
win.Envelope.create({
volume: 0.8,
lineColor: 'rgba(255, 0, 0, 0.5)',
lineWidth: 4,
dragPointSize: 20,
dragLine: false,
dragPointFill: 'rgba(0, 255, 255, 0.8)',
dragPointStroke: 'rgba(0, 0, 0, 0.5)',

points: [
{ time: 11.2, volume: 0.5 },
{ time: 15.5, volume: 0.8 },
],
}),
],
})

win.wavesurfer.once('ready', () => {
cy.get(id).matchImageSnapshot('envelope-basic')
resolve()
})
})
})
})

it('should render an envelope and add a point', () => {
cy.visit('cypress/e2e/index.html')
cy.window().then((win) => {
return new Promise((resolve) => {
const envelopePlugin = win.Envelope.create({
volume: 0.5,
lineColor: 'rgba(255, 0, 0, 0.5)',
lineWidth: 10,
dragPointSize: 12,
dragLine: true,
dragPointFill: 'rgba(0, 255, 255, 0.8)',
dragPointStroke: 'rgba(0, 0, 0, 0.5)',

points: [
{ time: 12.2, volume: 0.4 },
{ time: 16.5, volume: 0.9 },
],
})

win.wavesurfer = win.WaveSurfer.create({
container: id,
height: 200,
url: '../../examples/audio/demo.wav',
plugins: [envelopePlugin],
})

envelopePlugin.addPoint({ id: 'new-point', time: 10.1, volume: 0.6 })

win.wavesurfer.once('ready', () => {
cy.get(id).matchImageSnapshot('envelope-add-point')
resolve()
})
})
})
})
})
2 changes: 2 additions & 0 deletions cypress/e2e/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
import Regions from '../../dist/plugins/regions.js'
import Timeline from '../../dist/plugins/timeline.js'
import Spectrogram from '../../dist/plugins/spectrogram.js'
import Envelope from '../../dist/plugins/envelope.js'

window.WaveSurfer = WaveSurfer
window.Regions = Regions
window.Timeline = Timeline
window.Spectrogram = Spectrogram
window.Envelope = Envelope
</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions src/dom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
type TreeNode = { [key: string]: string | number | boolean | CSSStyleDeclaration | TreeNode | Node } & {
xmlns?: string
style?: Partial<CSSStyleDeclaration>
textContent?: string | Node
children?: TreeNode
}

function renderNode(tagName: string, content: TreeNode): HTMLElement | SVGElement {
const element = content.xmlns
? (document.createElementNS(content.xmlns, tagName) as SVGElement)
: (document.createElement(tagName) as HTMLElement)

for (const [key, value] of Object.entries(content)) {
if (key === 'children') {
for (const [key, value] of Object.entries(content)) {
if (typeof value === 'string') {
element.appendChild(document.createTextNode(value))
} else {
element.appendChild(renderNode(key, value as TreeNode))
}
}
} else if (key === 'style') {
Object.assign((element as HTMLElement).style, value)
} else if (key === 'textContent') {
element.textContent = value as string
} else {
element.setAttribute(key, value.toString())
}
}

return element
}

function render(tagName: string, content: TreeNode & { xmlns: string }, container?: Node): SVGElement
function render(tagName: string, content?: TreeNode, container?: Node): HTMLElement
function render(tagName: string, content?: TreeNode, container?: Node): HTMLElement | SVGElement {
const el = renderNode(tagName, content || {})
container?.appendChild(el)
return el
}

export default render
87 changes: 56 additions & 31 deletions src/plugins/envelope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import BasePlugin, { type BasePluginEvents } from '../base-plugin.js'
import { makeDraggable } from '../draggable.js'
import EventEmitter from '../event-emitter.js'
import render from '../dom.js'

export type EnvelopePoint = {
id?: string
Expand Down Expand Up @@ -65,26 +66,46 @@ class Polyline extends EventEmitter<{
const height = wrapper.clientHeight

// SVG element
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
svg.setAttribute('width', '100%')
svg.setAttribute('height', '100%')
svg.setAttribute('viewBox', `0 0 ${width} ${height}`)
svg.setAttribute('preserveAspectRatio', 'none')
svg.setAttribute('style', 'position: absolute; left: 0; top: 0; z-index: 4;')
svg.setAttribute('part', 'envelope')
const svg = render(
'svg',
{
xmlns: 'http://www.w3.org/2000/svg',
width: '100%',
height: '100%',
viewBox: `0 0 ${width} ${height}`,
preserveAspectRatio: 'none',
style: {
position: 'absolute',
left: '0',
top: '0',
zIndex: '4',
},
part: 'envelope',
},
wrapper,
) as SVGSVGElement

this.svg = svg

// A polyline representing the envelope
const polyline = document.createElementNS('http://www.w3.org/2000/svg', 'polyline')
polyline.setAttribute('points', `0,${height} ${width},${height}`)
polyline.setAttribute('stroke', options.lineColor)
polyline.setAttribute('stroke-width', options.lineWidth)
polyline.setAttribute('fill', 'none')
polyline.setAttribute('part', 'polyline')
polyline.setAttribute('style', options.dragLine ? 'cursor: row-resize; pointer-events: stroke;' : '')
svg.appendChild(polyline)

wrapper.appendChild(svg)
const polyline = render(
'polyline',
{
xmlns: 'http://www.w3.org/2000/svg',
points: `0,${height} ${width},${height}`,
stroke: options.lineColor,
'stroke-width': options.lineWidth,
fill: 'none',
part: 'polyline',
style: options.dragLine
? {
cursor: 'row-resize',
pointerEvents: 'stroke',
}
: {},
},
svg,
) as SVGPolylineElement

// Make the polyline draggable along the Y axis
if (options.dragLine) {
Expand Down Expand Up @@ -150,22 +171,26 @@ class Polyline extends EventEmitter<{
}

private createCircle(x: number, y: number) {
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse')
const size = this.options.dragPointSize
const radius = size / 2
circle.setAttribute('rx', radius.toString())
circle.setAttribute('ry', radius.toString())
circle.setAttribute('fill', this.options.dragPointFill)
if (this.options.dragPointStroke) {
circle.setAttribute('stroke', this.options.dragPointStroke)
circle.setAttribute('stroke-width', '2')
}
circle.setAttribute('style', 'cursor: grab; pointer-events: all;')
circle.setAttribute('part', 'envelope-circle')
circle.setAttribute('cx', x.toString())
circle.setAttribute('cy', y.toString())
this.svg.appendChild(circle)
return circle
return render(
'ellipse',
{
xmlns: 'http://www.w3.org/2000/svg',
cx: x.toString(),
cy: y.toString(),
r: radius.toString(),
fill: this.options.dragPointFill,
stroke: this.options.dragPointStroke,
'stroke-width': '2',
style: {
cursor: 'grab',
pointerEvents: 'all',
},
part: 'envelope-circle',
},
this.svg,
) as SVGEllipseElement
}

removePolyPoint(point: EnvelopePoint) {
Expand Down
8 changes: 3 additions & 5 deletions src/plugins/hover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

import BasePlugin, { type BasePluginEvents } from '../base-plugin.js'
import render from '../dom.js'

export type HoverPluginOptions = {
lineColor?: string
Expand Down Expand Up @@ -32,9 +33,8 @@ class HoverPlugin extends BasePlugin<HoverPluginEvents, HoverPluginOptions> {
this.options = Object.assign({}, defaultOptions, options)

// Create the plugin elements
this.wrapper = document.createElement('div')
this.label = document.createElement('span')
this.wrapper.appendChild(this.label)
this.wrapper = render('div', { part: 'hover' })
this.label = render('span', { part: 'hover-label' }, this.wrapper)
}

public static create(options?: HoverPluginOptions) {
Expand All @@ -56,7 +56,6 @@ class HoverPlugin extends BasePlugin<HoverPluginEvents, HoverPluginOptions> {
const lineColor = this.options.lineColor || wsOptions.cursorColor || wsOptions.progressColor

// Vertical line
this.wrapper.setAttribute('part', 'hover')
Object.assign(this.wrapper.style, {
position: 'absolute',
zIndex: 10,
Expand All @@ -70,7 +69,6 @@ class HoverPlugin extends BasePlugin<HoverPluginEvents, HoverPluginOptions> {
})

// Timestamp label
this.label.setAttribute('part', 'hover-label')
Object.assign(this.label.style, {
display: 'block',
backgroundColor: this.options.labelBackground,
Expand Down
33 changes: 22 additions & 11 deletions src/plugins/minimap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import BasePlugin, { type BasePluginEvents } from '../base-plugin.js'
import WaveSurfer, { type WaveSurferOptions } from '../wavesurfer.js'
import render from '../dom.js'

export type MinimapPluginOptions = {
overlayColor?: string
Expand Down Expand Up @@ -62,21 +63,31 @@ class MinimapPlugin extends BasePlugin<MinimapPluginEvents, MinimapPluginOptions
}

private initMinimapWrapper(): HTMLElement {
const div = document.createElement('div')
div.style.position = 'relative'
div.setAttribute('part', 'minimap')
return div
return render('div', {
part: 'minimap',
style: {
position: 'relative',
},
})
}

private initOverlay(): HTMLElement {
const div = document.createElement('div')
div.setAttribute(
'style',
'position: absolute; z-index: 2; left: 0; top: 0; bottom: 0; transition: left 100ms ease-out; pointer-events: none;',
return render(
'div',
{
style: {
position: 'absolute',
zIndex: '2',
left: '0',
top: '0',
bottom: '0',
transition: 'left 100ms ease-out',
pointerEvents: 'none',
backgroundColor: this.options.overlayColor,
},
},
this.minimapWrapper,
)
div.style.backgroundColor = this.options.overlayColor
this.minimapWrapper.appendChild(div)
return div
}

private initMinimap() {
Expand Down
Loading

0 comments on commit 4a33f16

Please sign in to comment.