Class and Style Binding
Previously in the Getting Started tour was an introduction to state management. With basic state management comes great power, and one of those useful powers is binding complex changes to CSS classes and styles for our elements.
Class Binding
A very common need in any HTML application is to dynamically change
CSS classes to represent changes in state. classBind
binds an
Observable<boolean>
toggling whether that class is applied
based on the value observed. (It binds classList.add
and
classList.remove
.) One very simple example would be a button that
toggles a "highlight" effect by toggling a .highlight
CSS class:
import {
type ComponentContext,
type ObservableEvent,
butterfly,
jsx,
} from 'butterfloat'
interface HighlightProps {}
interface HighlightEvents {
toggleHighlight: ObservableEvent<MouseEvent>
}
function Highlight(
props: HighlightProps,
{ bindEffect, events }: ComponentContext<HighlightEvents>,
) {
const { toggleHighlight } = events
const [highlight, setHighlighted] = butterfly(false)
bindEffect(toggleHighlight, () =>
setHighlighted((highlighted) => !highlighted),
)
return (
<div classBind={{ highlight }}>
Click the button to toggle the highlight on this{' '}
<button type="button" events={{ click: toggleHighlight }}>
Toggle Highlight
</button>
</div>
)
}
Style Binding
Sometimes even when you try your best to follow good CSS practices
and reflect all of your dynamic changes as CSS classes to toggle you
still find a need for dynamically adjusting individual "inline"
styles. styleBind
lets you bind observables to individual style
properties on the element.
An example using a terrible random traffic light (maybe it is representing a child playing "Red Light, Green Light"):
import { jsx } from 'butterfloat'
import { interval, map, shareReplay } from 'rxjs'
const colors = ['red', 'yellow', 'green']
function TrafficLight() {
const color = interval(5_000 /* ms */).pipe(
map(() => colors[Math.round(colors.length * Math.random())]),
shareReplay(1),
)
const lightName = color.pipe(map((color) => `${color} light`))
return (
<div
styleBind={{ backgroundColor: color }}
bind={{ innerText: lightName }}
/>
)
}
Next Steps
As components grow more complicated they may need to handle Component Children and Dynamic Children Binding.