Phoenix LiveView hook for brush selection interactions.
Enables rectangular or 1D brush selection on SVG elements for selecting data ranges, filtering, or zooming.
Usage
Add the hook to your app.js:
import { BrushHook } from "visualize/hooks"
let liveSocket = new LiveSocket("/live", Socket, { hooks: { BrushHook } })
Use in your LiveView template:
<svg phx-hook="BrushHook"
id="my-chart" data-brush-type="xy" data-brush-event="brush_select"><g class="chart-content">
<!-- Your chart content --></g> <g class="brush-overlay"></g> </svg>
Handle brush events in your LiveView:
def handle_event("brush_select", %{"x0" => x0, "y0" => y0, "x1" => x1, "y1" => y1}, socket) do # Filter data within selection {:noreply, assign(socket, selection: {x0, y0, x1, y1})} end def handle_event("brush_clear", _params, socket) do {:noreply, assign(socket, selection: nil)} end
The hook may also sit on a container around the chart's <svg>, as the other hooks do:
it maps the pointer through, and draws in, the first <svg> inside it.
In a sync group
A chart whose design names a sync group (interaction: %{sync: "dash-42"}, spec/14
§2.9) shares its brush (spec/10 §6.4, D-117): the chart brushed publishes the brushed x
extent in domain units on the group's bus, and every other member draws it as a band
through its own scale, clipped to its domain, at once. Only the chart brushed pushes
brush_select, once, on release, with the window as "domain" => [lo, hi] too, so one
window change is one server request:
def handle_event("brush_select", %{"domain" => [from, to]}, socket) do
{:noreply, assign(socket, window: {from, to})}
endBrush Types
xy- 2D rectangular brush (default)x- Horizontal brush onlyy- Vertical brush only
Configuration
Data attributes on the SVG element:
data-brush-type- Brush type: "xy", "x", or "y" (default: "xy")data-brush-event- Event name for selection (default: "brush_select")data-brush-clear-event- Event name for clearing (default: "brush_clear")data-brush-extent- Constrains brush to "x0,y0,x1,y1" (optional)data-brush-color- Selection rectangle fill color (default: "rgba(119, 119, 119, 0.2)")data-brush-stroke- Selection rectangle stroke color (default: "#666")
Summary
Functions
Keeps the data within the selection on every axis whose scale is given (spec/10 §6.3, D-29).
Returns the JavaScript code for the BrushHook.
Converts a pixel selection to domain values through the scales given (spec/10 §6.3, D-29).
Functions
Keeps the data within the selection on every axis whose scale is given (spec/10 §6.3, D-29).
opts takes :x_accessor with :x_scale, :y_accessor with :y_scale, or both pairs;
an axis without a scale is not tested, so an "x" brush needs no y scale. Neither scale
is an ArgumentError.
Example
filtered = Visualize.Hooks.Brush.filter_selection(
data,
selection,
x_accessor: & &1.date,
x_scale: x_scale
)
Returns the JavaScript code for the BrushHook.
Include this in your app.js or as a separate file.
Converts a pixel selection to domain values through the scales given (spec/10 §6.3, D-29).
With both :x_scale and :y_scale: %{x0:, x1:, y0:, y1:}. With one: the {start, stop}
pair of that axis in domain units, in pixel order — for a y brush under the usual flipped
range start is the larger value. Neither scale is an ArgumentError.
Example
{from, to} = Visualize.Hooks.Brush.selection_to_domain(selection, x_scale: x_scale)