renderInput = (s, {label = 'input'} = {}) => {
const {classNames} = useScope()
const lines = s.trim().split(/(?<=\n)/)
const renderToken = (token, classNames = '') => htl.html.fragment`<span class="${classNames}">${token}</span>`
const renderLine = (line) => {
const tokens = line.split(/(\s+)/)
const spans = tokens.map(token => {
if (!token.trim()) {
if (token === '\n') {
return renderToken(token.replace(/\s/g, '↵'), classNames.whitespace)
} else {
return renderToken(token.replace(/\s/g, '•'), classNames.whitespace)
}
} else {
return renderToken(token)
}
})
return htl.html.fragment`<code class="${classNames.line}">${spans}</code>`
}
const element = htl.html`
<style>
.${classNames.pre} {
counter-reset: line;
display: flex;
flex-direction: column;
border: 1px solid #333340;
background-color: #10101a;
color: #cccccc;
overflow: hidden;
}
.${classNames.pre} .${classNames.scroll} {
padding: 0.5em;
display: flex;
flex-direction: column;
max-height: 12em;
overflow: auto;
}
.${classNames.pre} .${classNames.header} {
color: white;
width: calc(100% + 1em);
margin: 0 -0.5em;
border-bottom: 1px solid #cccccc;
}
.${classNames.pre} h5 {
color: white;
padding: 0.5em 1em;
}
.${classNames.pre} .${classNames.line} {
counter-increment: line;
}
.${classNames.pre} .${classNames.line}:before {
display: inline-block;
content: '';
width: 3em;
user-select: none;
opacity: 0.33;
}
.${classNames.pre} .${classNames.line}:before {
content: counter(line);
}
.${classNames.pre} .${classNames.whitespace} {
opacity: 0.33;
}
</style>
<pre class="${classNames.pre}">
<div class="${classNames.header}"><h5>${label} (${s.length} characters, ${lines.length} lines)</h5></div>
<div class="${classNames.scroll}">
${lines.map(line => renderLine(line))}
</div>
</pre>`
element.value = s
return element
}