Published
Edited
Mar 25, 2022
2 forks
Importers
32 stars
Insert cell
Insert cell
Insert cell
Insert cell
viewof listItems = listInput()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
listInput({
min: 2,
max: 5,
value: ['Try', 'adding', 'an', 'item!'],
defaultValue: 'You\'re maxed out!',
})
Insert cell
Insert cell
viewof numberList = listInput({
input: v => html`<input type="range" min="0" max="100" value=${v} />`,
min: 2,
defaultValue: 0,
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
defaultInput = v => html.fragment`<input type="text" value="${v}" />`
Insert cell
listInput = ({
input = defaultInput,
min = 0,
max = 0,
value = null,
defaultValue = '',
} = {}) => {
// This is a state variable that we will change as events occur on the input
let values = value !== null ? value : new Array(Math.max(min, 1)).fill(defaultValue)
// Templates
const inputRow = (v, i) => html.fragment`<tr>
<td onchange=${e => changeValueAt(i, e.target.value)}>${input(v, i, values)}</td>
<td>
<button onclick=${e => removeRow(i)} disabled=${values.length <= min}>&times;</button>
</td>
</tr>`
const footerRow = () => html.fragment`<tr>
<td></td>
<td>
<button onclick=${addRow} disabled=${values.length >= max && max !== 0}>+</button>
</td>
</tr>`
// Rerender Helpers
const rerenderTbody = () => {
const tbody = output.querySelector('tbody')
tbody.innerHTML = ''
tbody.appendChild(html.fragment`${values.map(inputRow)}`)
}
const rerenderTfoot = () => {
const tfoot = output.querySelector('tfoot')
tfoot.innerHTML = ''
tfoot.appendChild(footerRow())
}
const rerender = () => {
rerenderTbody()
rerenderTfoot()
}

// Event Dispatcher
const dispatchInputEvent = () => {
output.value = values
output.dispatchEvent(new CustomEvent('input'))
}
// Event Handlers
const addRow = () => {
values.push(defaultValue)
rerender()
dispatchInputEvent()
}
const removeRow = removeIdx => {
values = values.filter((_, i) => i !== removeIdx)
rerender()
dispatchInputEvent()
}
const changeValueAt = (i, value) => {
values[i] = value
rerender()
dispatchInputEvent()
}
const output = html`<table style=${{ width: 'auto' }}>
<tbody>
${values.map(inputRow)}
</tbody>
<tfoot>
${footerRow()}
</tfoot>
</table>`
output.value = values
return output
}
Insert cell
Insert cell
list = arr => html`<ul>
${arr.map(v => html.fragment`<li>${v}</li>`)}
</ul>`
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more