Components that display via DOM but expose other values

Here‘s a function for showing a color picker:

colorPicker = () => html`<input type="color">`

I can easily use it in a notebook as a kind of component:

viewof myColor = colorPicker()

And then reference the color in another cell like this:

html`<p style="color: ${myColor}; font-family: Avenir; font-size: 30px;">Sample Text</p>`

Pretty cool! (live version is here: https://beta.observablehq.com/@halffullheart/test

I know it seems small, but having to put viewof in there (or use Generators.input() in the referencing cells) is frustrating. A custom component like this is meant to always be used this way where it displays one thing but exposes another value.

What I’m looking for is a way to package up what viewof is doing into the component so that wherever it is used, I only have to give it a name: aColor = colorPicker().

1 Like

I assume you know you can omit the first cell…

     viewof myColor = html`<input type="color">`

There’s no way to encapsulate viewof because viewof is part of the name of the thing you’re defining. It is interface, not implementation.

You can encapsulate the implementation of input components that are compatible with viewof (and by extension Generators.input), as in Jeremy’s Grand Input Bazaar, but there’s no way to force the referencing cell to become a viewof cell.

The way to think of viewof foo is that, in addition to the viewof foo that displays the input element, it creates a second hidden cell foo that exposes the current value of the input element to the rest of the notebook.

If we didn’t have viewof, the equivalent long form of defining an input and its current value would be as two cells:

colorView = html`<input type="color">`
colorValue = Generators.input(colorView)

Now other cells may reference colorValue and they will see the current color shown in colorView; those referencing wills will re-run whenever the reader interacts with the colorView input. Although rarely needed, you can also reference colorView in another cell, which is an HTMLInputElement. (And if a cell references colorView, it isn’t re-evaluated on interaction with the color input; it’s only re-evaluated if the color input is redefined.)

colorView.type // "color"

With viewof, all you have to say is:

viewof color = html`<input type="color">`

And the result is that viewof color is equivalent to the previous colorView, and color is equivalent to colorValue. You can also reference viewof color, which will likewise be an HTMLInputElement.

viewof color.type // "color"

Live code here:

https://beta.observablehq.com/@mbostock/a-brief-introduction-to-viewof

Yes. This was a simplified example. My intention is to create reusable components that are more complex and use them multiple times in a document.