Module

Hylograph.Internal.Selection.Operations

Package
purescript-hylograph-selection
Repository
afcondon/purescript-hylograph-selection

Internal: Implementation of selection operations using web-dom.

Provides the concrete implementations for SelectionM operations:

  • Selecting: select, selectAll, selectElement
  • Appending: append, appendChild, appendChildInheriting
  • Data binding: joinData, joinDataWithKey, renderData
  • Attributes: setAttrs, setAttrsExit
  • Lifecycle: remove, clear, merge
  • HATS rendering: renderTree, renderTreeKeyed

Uses web-dom for DOM manipulation, not D3's selection API.

Internal module - use Hylograph.Selection for the public API.

SAFETY: Phantom Type Guards for unsafePartial

This module uses unsafePartial extensively to pattern-match on Selection constructors. These are safe because the phantom type parameter (SEmpty, SPending, SBoundOwns, SBoundInherits, SExiting) is set at construction time and enforced by the type system:

  • SEmpty guarantees EmptySelection constructor
  • SPending guarantees PendingSelection constructor
  • SBoundOwns guarantees BoundSelection with owned data
  • SBoundInherits guarantees BoundSelection with inherited data
  • SExiting guarantees ExitingSelection constructor

The phantom types make illegal states unrepresentable at compile time. A function taking Selection SPending elem datum can only receive a PendingSelection, so the partial match is actually total for that type.

Similarly, Array.unsafeIndex is used where indices come from internal bookkeeping (e.g., join algorithms) that maintains valid bounds.

#select Source

select :: forall m datum. MonadEffect m => String -> m (Selection SEmpty Element datum)

Select a single element matching the CSS selector

Returns an empty selection (no data bound). The datum type is polymorphic and will be inferred from usage. This is typically the starting point for data binding.

Example:

svg <- select "svg"
circles <- renderData Circle [1, 2, 3] "circle" svg ...

#selectElement Source

selectElement :: forall m datum. MonadEffect m => Element -> m (Selection SEmpty Element datum)

Select from a DOM element directly

This is useful for framework integration (React, Vue, etc.) where you have a reference to a DOM element rather than a CSS selector.

Example:

selectElement element >>= renderTree myVisualization

#selectAll Source

selectAll :: forall state parent parentDatum datum m. MonadEffect m => String -> Selection state parent parentDatum -> m (Selection SEmpty Element datum)

Select all elements matching the CSS selector within a parent selection

Returns an empty selection (no data bound yet). The datum type is polymorphic and will be inferred from usage. Use this for nested selections.

Example:

svg <- select "svg"
groups <- selectAll "g" svg

#selectAllWithData Source

selectAllWithData :: forall state parent parentDatum datum m. MonadEffect m => String -> Selection state parent parentDatum -> m (Selection SBoundOwns Element datum)

Select all elements matching selector and extract their bound data

Use this when selecting child elements that have inherited data from their parent. This is necessary when you want to use the selection with transitions that need access to the bound data (like withTransitionStaggered).

Example:

-- After creating nodes with appendChildInheriting
groups <- selectSimulationGroups
circles <- selectAllWithData "circle" groups.nodes
withTransitionStaggered config delayFn circles [fill colorByDepth]

#selectChildInheriting Source

selectChildInheriting :: forall parent datum m. MonadEffect m => String -> Selection SBoundOwns parent datum -> m (Selection SBoundOwns Element datum)

Select child elements, inheriting parent's data

Like D3's selection.select(): for each parent element, selects the first child matching the selector and copies the parent's __data__ to the child.

Use when you have groups with bound data and want to update their children.

Example:

groups <- selectAllWithData ".treemap-package" container
circles <- selectChildInheriting "circle" groups
setAttrs [ fill (colorByData _.topoLayer) ] circles

#append Source

append :: forall parent datum m. MonadEffect m => ElementType -> Array (Attribute datum) -> Selection SPending parent datum -> m (Selection SBoundOwns Element datum)

Append new elements to a pending (enter) selection

This materializes the data into DOM elements. Returns a bound selection that can be further modified.

Example:

enterSelection <- append Circle
  [ fill "green"
  , radius 10.0
  , cx (\d -> d.x)
  ]
  pendingSelection

#appendChild Source

appendChild :: forall parent datum datumOut m. MonadEffect m => ElementType -> Array (Attribute datumOut) -> Selection SEmpty parent datum -> m (Selection SEmpty Element datumOut)

Append a single child element to a parent selection

Creates one new element and appends it to each parent in the selection. Returns an empty selection of the newly created element(s).

This is different from append which creates elements for each datum in a pending (enter) selection. appendChild is for structural elements like creating an SVG container.

Example:

container <- select "#viz"
svg <- appendChild SVG [width 400.0, height 150.0] container
circles <- renderData Circle [1, 2, 3] "circle" svg ...

#appendChildInheriting Source

appendChildInheriting :: forall parent datum m. MonadEffect m => ElementType -> Array (Attribute datum) -> Selection SBoundOwns parent datum -> m (Selection SBoundInherits Element datum)

Append child elements to a data-bound selection, inheriting parent's data

This is the key function for creating nested SVG structures where children need access to their parent's bound data. The children don't own the data binding - they inherit it from their parent.

Semantics: Each parent element gets one child. The child inherits the parent's data by copying the data reference (a performance optimization vs. traversing the DOM tree on every attribute access).

Type signature documents the data flow:

  • Parent must be SBoundOwns (owns the data to inherit from)
  • Child is SBoundInherits (borrows parent's data)
  • Both parent and child have same datum type

Example:

-- Create groups with data
groups <- append Group [] enterSelection  -- SBoundOwns

-- Add circles that inherit group's data
circles <- appendChildInheriting Circle [radius 5.0] groups  -- SBoundInherits

-- Add text that also inherits group's data
labels <- appendChildInheriting Text [textContent _.name] groups  -- SBoundInherits

#setAttrs Source

setAttrs :: forall datum m. MonadEffect m => Array (Attribute datum) -> Selection SBoundOwns Element datum -> m (Selection SBoundOwns Element datum)

Set attributes on a bound selection

Updates existing elements with new attribute values. This is used for the "update" part of enter-update-exit.

Example:

updated <- setAttrs
  [ fill "orange"
  , cx (\d -> d.x)
  ]
  boundSelection

#setAttrsExit Source

setAttrsExit :: forall datum m. MonadEffect m => Array (Attribute datum) -> Selection SExiting Element datum -> m (Selection SExiting Element datum)

Set attributes on an exiting selection

Similar to setAttrs but for selections in the exit phase. Useful for styling elements before they are removed.

Example:

setAttrsExit [fill "brown", class_ "exiting"] exitSelection

#remove Source

remove :: forall datum m. MonadEffect m => Selection SExiting Element datum -> m Unit

Remove elements from an exit selection

Removes the elements from the DOM. This is the final step for exiting data.

Example:

remove exitSelection

#clear Source

clear :: forall m. MonadEffect m => String -> m Unit

Clear all children from an element

Selects the element and removes all its children. Useful for clearing a container before rendering new content.

Example:

clear "#viz"
svg <- appendChild SVG [...] container

#syncDOMToData Source

syncDOMToData :: forall m. MonadEffect m => String -> m Unit

Sync DOM transform positions back to data.x and data.y

Reads the current transform attribute from each element matching the selector and updates the bound data's x/y coordinates. Essential for transitioning from CSS animations to force simulation - ensures simulation sees current positions.

Example:

-- After tree reveal animation completes:
syncDOMToData "g.nodes > g"  -- Sync group positions to node data
start  -- Simulation continues from current positions

#merge Source

merge :: forall datum m. MonadEffect m => Selection SBoundOwns Element datum -> Selection SBoundOwns Element datum -> m (Selection SBoundOwns Element datum)

Merge two bound selections

Follows D3 semantics: concatenates in document order. Useful for combining enter and update selections.

Example:

allCircles <- merge enterSelection updateSelection

#joinData Source

joinData :: forall f parent parentDatum datum m. MonadEffect m => Foldable f => Ord datum => f datum -> String -> Selection SEmpty parent parentDatum -> m (JoinResult Selection parent datum)

Low-level data join for power users

Explicitly returns enter, update, and exit selections. Users must handle each set manually.

Example:

JoinResult { enter, update, exit } <- joinData [1, 2, 3] "circle" svg
enterEls <- append Circle [...] enter
updateEls <- setAttrs [...] update
remove exit

#joinDataWithKey Source

joinDataWithKey :: forall f parent parentDatum datum key m. MonadEffect m => Foldable f => Eq key => f datum -> (datum -> key) -> String -> Selection SEmpty parent parentDatum -> m (JoinResult Selection parent datum)

Low-level data join with custom key function

Like joinData, but uses a key function to extract comparable keys instead of requiring Ord on the data itself.

This is essential for data types that don't have lawful Ord instances (e.g., opaque foreign types like D3Link_Swizzled).

Example:

JoinResult { enter, update, exit } <- joinDataWithKey links (\l -> l.id) "line" svg
enterEls <- append Line [...] enter
updateEls <- setAttrs [...] update
remove exit

#renderData Source

renderData :: forall f parent datum m. MonadEffect m => Foldable f => Ord datum => ElementType -> f datum -> String -> Selection SEmpty parent datum -> Maybe (datum -> Array (Attribute datum)) -> Maybe (datum -> Array (Attribute datum)) -> Maybe (datum -> Array (Attribute datum)) -> m (Selection SBoundOwns Element datum)

High-level data rendering for most users

Manages the entire enter-update-exit cycle automatically. Users provide Maybe callbacks for each phase.

This is the recommended API for 90% of use cases.

Example:

circles <- renderData Circle [1, 2, 3] "circle" svg
  (Just \d -> [fill "green", cx (\_ -> d * 100.0)])  -- Enter
  (Just \d -> [fill "orange"])                        -- Update
  Nothing                                             -- Exit (just remove)

#appendData Source

appendData :: forall f parent parentDatum datum m. MonadEffect m => Foldable f => ElementType -> f datum -> Array (Attribute datum) -> Selection SEmpty parent parentDatum -> m (Selection SBoundOwns Element datum)

Simple data append for initial renders

A simplified variant of renderData for when you just want to create elements from data without worrying about enter/update/exit cycles.

This is perfect for initial renders where you know there are no existing elements to update or remove.

Example:

svg <- select "svg"
circles <- appendData Circle [1, 2, 3]
  [radius 10.0, fill "steelblue", cx (\d _ -> d * 100.0)]
  svg

#on Source

on :: forall state elem datum. Behavior datum -> Selection state elem datum -> Effect (Selection state elem datum)

Attach a behavior (zoom, drag, etc.) to a selection

Works with any selection type - extracts elements and applies D3 behavior. Returns the selection unchanged to allow chaining.

Example:

svg <- appendChild SVG [...] container
zoomGroup <- appendChild Group [...] svg
_ <- on (Drag defaultDrag) zoomGroup
_ <- on (Zoom $ defaultZoom (ScaleExtent 0.5 4.0) ".zoom-group") svg

#renderTree Source

renderTree :: forall parent parentDatum datum. Ord datum => Selection SEmpty parent parentDatum -> Tree datum -> Effect (Map String (Selection SBoundOwns Element datum))

Render a declarative tree structure

Walks the tree, creates DOM elements, and returns a map of named selections. This is the core implementation of the declarative API.

#renderTreeKeyed Source

renderTreeKeyed :: forall parent parentDatum datum. Selection SEmpty parent parentDatum -> Tree datum -> Effect (Map String (Selection SBoundOwns Element datum))

Render a tree using key functions from UpdateJoin instead of Ord constraint

This is for cases where the datum type doesn't have Ord, but UpdateJoin provides a keyFn for identity matching. The keyFn in the AST handles keying.

Note: Uses unsafeCoerce to bypass the Ord constraint. This is safe when the tree uses UpdateJoin with a keyFn, as that's what determines identity.

#reselect Source

reselect :: forall datum datumOut. String -> Map String (Selection SBoundOwns Element datum) -> Effect (Selection SEmpty Element datumOut)

Extract a named selection from a renderTree result and convert to SEmpty

This is useful for the two-tree pattern where you need to render different datum types in sequence:

axesSelections <- renderTree container axesTree
chartGroup <- reselect "chartGroup" axesSelections
barsSelections <- renderTree chartGroup barsTree

If the named selection is not found, returns an empty selection.

#createElementWithNS Source

createElementWithNS :: ElementType -> Document -> Effect Element

Create an element with the appropriate namespace Uses the element's rendering context to determine namespace

#getDocument Source

getDocument :: forall parent datum. SelectionImpl parent datum -> Effect Document
Modules
Data.DependencyGraph
Hylograph.AST
Hylograph.Axis.Axis
Hylograph.Brush
Hylograph.Brush.FFI
Hylograph.Brush.Types
Hylograph.Classify
Hylograph.Data.Graph
Hylograph.Data.Graph.Algorithms
Hylograph.Data.Node
Hylograph.Data.Tree
Hylograph.Expr.Animation
Hylograph.Expr.Attr
Hylograph.Expr.Datum
Hylograph.Expr.Expr
Hylograph.Expr.Friendly
Hylograph.Expr.Integration
Hylograph.Expr.Interpreter.CodeGen
Hylograph.Expr.Interpreter.Eval
Hylograph.Expr.Interpreter.Meta
Hylograph.Expr.Interpreter.PureSVG
Hylograph.Expr.Interpreter.SVG
Hylograph.Expr.Path
Hylograph.Expr.Path.Generators
Hylograph.Expr.Sugar
Hylograph.Expr.Units
Hylograph.HATS
Hylograph.HATS.Friendly
Hylograph.HATS.InterpreterTick
Hylograph.HATS.Transitions
Hylograph.Interaction.Brush
Hylograph.Interaction.Coordinated
Hylograph.Interaction.Pointer
Hylograph.Interaction.Zoom
Hylograph.Internal.Attribute
Hylograph.Internal.Behavior.FFI
Hylograph.Internal.Behavior.Types
Hylograph.Internal.Capabilities.Selection
Hylograph.Internal.Capabilities.Transition
Hylograph.Internal.FFI
Hylograph.Internal.Selection.Join
Hylograph.Internal.Selection.Operations
Hylograph.Internal.Selection.Operations.Conversions
Hylograph.Internal.Selection.Operations.Helpers
Hylograph.Internal.Selection.Operations.Selection
Hylograph.Internal.Selection.Query
Hylograph.Internal.Selection.Types
Hylograph.Internal.Transition.FFI
Hylograph.Internal.Transition.Manager
Hylograph.Internal.Transition.Scene
Hylograph.Internal.Transition.Types
Hylograph.Internal.Types
Hylograph.Interpreter.D3
Hylograph.Interpreter.English
Hylograph.Interpreter.Mermaid
Hylograph.Interpreter.MetaAST
Hylograph.Interpreter.SemiQuine
Hylograph.Interpreter.SemiQuine.TreeToCode
Hylograph.Interpreter.SemiQuine.Types
Hylograph.Render
Hylograph.Scale
Hylograph.Scale.FP
Hylograph.Shape.Arc
Hylograph.Shape.Pie
Hylograph.Shape.Polygon
Hylograph.Tooltip
Hylograph.Transform
Hylograph.TreeDSL
Hylograph.TreeDSL.ShapeTree
Hylograph.Unified
Hylograph.Unified.Attribute
Hylograph.Unified.DataDSL
Hylograph.Unified.Display
Hylograph.Unified.Examples
Hylograph.Unified.Join
Hylograph.Unified.Sugar