Module

Hylograph.Internal.Selection.Query

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

EXPERIMENTAL: Selection Query Language

Status: Experimental - API may change

Provides CSS-style querying across named selections for dynamic selection manipulation. This module is orthogonal to the core selection API and can be used selectively.

Known Issues and Limitations

1. D3v2Selection_ Unwrapping

When using with the D3v2 interpreter, you cannot simply unsafeCoerce from Map String (D3v2Selection_ ...) to Map String (Selection ...). The newtype constructor must be properly unwrapped:

-- ❌ WRONG: unsafeCoerce doesn't properly unwrap newtype constructors
let unwrapped = unsafeCoerce selections
groupCircles <- queryAll ".group-1" unwrapped  -- Returns empty!

-- ✅ RIGHT: Pattern match to unwrap each selection
let unwrapped = map (\(D3v2Selection_ sel) -> sel) selections
groupCircles <- queryAll ".group-1" unwrapped  -- Works correctly

-- ✅ BEST: Use the wrapper function
groupCircles <- queryAllD3v2 ".group-1" selections  -- Handles unwrapping

See queryAllD3v2 in PSD3.Interpreter.D3 for the correct wrapper.

2. Phantom Type Transitions

Query functions return SEmpty selections (no bound data), but you may know that elements have data from prior joins. Currently requires unsafeCoerce to transition from SEmpty to SBoundOwns for attribute operations.

3. Controlled Coercion

The datumOut type parameter is polymorphic, allowing the caller to specify the expected datum type. This is safe only if you know what data is bound to the queried elements. Type safety is enforced by later operations.

Alternative Approaches

For simpler use cases, consider:

  • Direct DOM API: Use document.querySelectorAll from JavaScript/FFI
  • CSS selector passthrough: Store selectors as strings, apply later in a visualization builder/live-editor context
  • Document-level queries: Simpler API that searches the whole document instead of within named selections

Intended Use Cases

  • Interactive visualization builders with live-editing
  • Prototyping with CSS selectors before principled implementation
  • Dynamic selection and modification based on data properties
  • Testing and debugging selections in development
  • Meta-tree editing with selector annotations and lambda attributes

Example Usage

-- Query within a named selection
activeCircles <- queryIn "nodesGroup" "circle.active" selections

-- Query across all selections (with D3v2 interpreter)
allCircles <- queryAllD3v2 "circle" selections

-- Filter by data predicate (requires bound selection)
largeNodes <- filterByData (_.value > 100) nodeCircles

-- Traverse DOM structure
parents <- parentElements nodeCircles
allDescendants <- descendants "circle" containerGroup

-- Combine selections
allShapes <- union [circles, rects, paths]

#queryIn Source

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

Query within a specific named selection

Similar to D3's d3.select("#foo").selectAll("circle.active") Returns all matching descendants within the named selection.

Example:

activeCircles <- queryIn "nodesGroup" "circle.active" selections

#queryAll Source

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

Query across all selections in the map

Finds all elements matching selector in any named selection. Results are in document order with duplicates removed.

Example:

allCircles <- queryAll "circle" selections
allActiveElements <- queryAll ".active" selections

#queryFirst Source

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

Query for first matching element within a named selection

Like queryIn but returns only the first match.

Example:

firstActive <- queryFirst "nodesGroup" "circle.active" selections

#queryInBound Source

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

Query within a bound selection directly

Like selectAll but with clearer naming for query contexts.

Example:

circles <- queryInBound "circle" nodeGroup

#filterByClass Source

filterByClass :: forall datumOut. String -> Selection SEmpty Element datumOut -> Effect (Selection SEmpty Element datumOut)

Filter selection by CSS class

Returns only elements that have the specified class.

Example:

activeNodes <- filterByClass "active" allNodes

#filterByAttribute Source

filterByAttribute :: forall datumOut. String -> String -> Selection SEmpty Element datumOut -> Effect (Selection SEmpty Element datumOut)

Filter selection by attribute value

Returns only elements where the attribute matches the value.

Example:

visibleNodes <- filterByAttribute "data-visible" "true" allNodes

#hasClass Source

hasClass :: forall datum. String -> Selection SEmpty Element datum -> Effect Boolean

Check if selection has elements with the given class

Example:

hasActive <- hasClass "active" nodes

#hasAttribute Source

hasAttribute :: forall datum. String -> Selection SEmpty Element datum -> Effect Boolean

Check if selection has elements with the given attribute

Example:

hasDataId <- hasAttribute "data-id" nodes

#filterByData Source

filterByData :: forall datum. (datum -> Boolean) -> Selection SBoundOwns Element datum -> Effect (Selection SBoundOwns Element datum)

Filter a bound selection by data predicate

Returns only elements where the bound data matches the predicate. This requires a bound selection (SBoundOwns) since we need access to data.

Example:

largeNodes <- filterByData (_.value > 100) nodeCircles
activeNodes <- filterByData (_.active) nodeCircles

#findByData Source

findByData :: forall datum. (datum -> Boolean) -> Selection SBoundOwns Element datum -> Effect (Selection SBoundOwns Element datum)

Find first element where data matches predicate

Like filterByData but returns only the first match.

Example:

selectedNode <- findByData (_.id == nodeId) nodeCircles

#parentElements Source

parentElements :: forall datumOut. Selection SEmpty Element datumOut -> Effect (Selection SEmpty Element datumOut)

Get parent elements of the selection

Returns the immediate parent of each element in the selection. Duplicates are removed.

Example:

groups <- parentElements circles

#children Source

children :: forall datumOut. Selection SEmpty Element datumOut -> Effect (Selection SEmpty Element datumOut)

Get direct children of the selection

Returns all element children of each element in the selection.

Example:

childCircles <- children nodeGroups

#descendants Source

descendants :: forall datum datumOut. String -> Selection SEmpty Element datum -> Effect (Selection SEmpty Element datumOut)

Get all descendants matching selector

Like queryInBound but works with empty selections too.

Example:

allCircles <- descendants "circle" container

#siblings Source

siblings :: forall datumOut. Selection SEmpty Element datumOut -> Effect (Selection SEmpty Element datumOut)

Get sibling elements

Returns all sibling elements (excluding the elements themselves).

Example:

otherNodes <- siblings selectedNode

#ancestors Source

ancestors :: forall datumOut. Selection SEmpty Element datumOut -> Effect (Selection SEmpty Element datumOut)

Get all ancestors of the selection

Returns all ancestor elements up to the document root. Useful for finding containing groups or SVG elements.

Example:

containers <- ancestors circle

#union Source

union :: forall datumOut. Array (Selection SEmpty Element datumOut) -> Effect (Selection SEmpty Element datumOut)

Union (combine) multiple selections

Merges selections in document order with duplicates removed.

Example:

allShapes <- union [circles, rects, paths]

#intersect Source

intersect :: forall datumOut. Selection SEmpty Element datumOut -> Selection SEmpty Element datumOut -> Effect (Selection SEmpty Element datumOut)

Intersection of two selections

Returns only elements present in both selections.

Example:

activeVisible <- intersect activeNodes visibleNodes

#difference Source

difference :: forall datumOut. Selection SEmpty Element datumOut -> Selection SEmpty Element datumOut -> Effect (Selection SEmpty Element datumOut)

Difference between two selections

Returns elements in first selection that are not in second.

Example:

inactiveNodes <- difference allNodes activeNodes

#isEmpty Source

isEmpty :: forall state elem datum. Selection state elem datum -> Boolean

Check if selection is empty (has no elements)

Example:

when (isEmpty selection) $ log "No elements found"

#size Source

size :: forall state elem datum. Selection state elem datum -> Int

Get number of elements in selection

Example:

count <- size circles
log $ "Found " <> show count <> " circles"

#first Source

first :: forall datumOut. Selection SEmpty Element datumOut -> Effect (Selection SEmpty Element datumOut)

Get first element from selection

Example:

firstNode <- first allNodes

#last Source

last :: forall datumOut. Selection SEmpty Element datumOut -> Effect (Selection SEmpty Element datumOut)

Get last element from selection

Example:

lastNode <- last allNodes

#nth Source

nth :: forall datumOut. Int -> Selection SEmpty Element datumOut -> Effect (Selection SEmpty Element datumOut)

Get nth element from selection (0-indexed)

Example:

thirdNode <- nth 2 allNodes

#toArray Source

toArray :: forall state elem datum. Selection state elem datum -> Array Element

Convert selection to array of elements

Useful for manual iteration or analysis.

Example:

elements <- toArray circles
traverse_ processElement elements
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