Hylograph.HATS
- Package
- purescript-hylograph-selection
- Repository
- afcondon/purescript-hylograph-selection
HATS: Hylomorphic Abstract Tree Syntax
A principled AST for data visualization based on recursion schemes.
The core insight: a visualization is a transduction from data to DOM, composed of two orthogonal choices:
- Enumeration: How to traverse/extract elements from input (coalgebra)
- Assembly: How to structure the output DOM (algebra)
This version uses existentially-scoped data binding:
Treehas no type parameter - all trees compose freely with<>- Each
Foldbrings its own datum typeainto scope - Attributes and behaviors inside templates capture datum via closures
#Tree Source
data TreeThe HATS tree type - a specification for visual structure.
No type parameter - trees compose freely with <> regardless of
what data they bind internally.
Only three constructors:
Elem- A DOM element with attributes, children, and behaviorsMkFold- An existentially-wrapped iteration (enumerate, transform, assemble)Empty- Nothing to render
Constructors
Elem { attrs :: Array Attr, behaviors :: Array ThunkedBehavior, children :: Array Tree, elemType :: ElementType }MkFold SomeFoldEmpty
Instances
#Enumeration Source
data Enumeration aHow to extract elements from a data structure.
Constructors
FromArray (Array a)FromTree { children :: a -> Array a, includeInternal :: Boolean, order :: TraversalOrder, root :: a }WithContext (Array { datum :: a, depth :: Int, index :: Int })
#SomeFold Source
newtype SomeFoldExistentially-wrapped Fold specification.
The datum type a is scoped to the Fold - it doesn't appear in
the outer Tree type. This allows heterogeneous composition.
Uses CPS encoding since PureScript doesn't have native existentials:
SomeFold packs a FoldSpec a and can only be consumed by a
polymorphic continuation that works for any a.
#FoldSpec Source
type FoldSpec a = { assemble :: Assembly, elementType :: ElementType, enumerate :: Enumeration a, gup :: Maybe (GUPSpec a), keyFn :: a -> String, name :: String, template :: a -> Tree }Fold specification with its datum type.
The template function receives a datum and returns a Tree.
Inside the template, attributes and behaviors capture the datum
via closures - the datum is "baked in" at template evaluation time.
#mkSomeFold Source
mkSomeFold :: forall a. FoldSpec a -> SomeFoldCreate a SomeFold from a FoldSpec (existentially packs the type)
#runSomeFold Source
runSomeFold :: forall r. SomeFold -> (forall a. FoldSpec a -> r) -> rConsume a SomeFold with a polymorphic handler
#Attr Source
data AttrAttribute type - either static or a thunk (closure).
Thunked attributes capture their datum value at construction time. The interpreter just invokes the thunk to get the value.
Constructors
#ThunkedBehavior Source
data ThunkedBehaviorBehavior type with thunked handlers.
Handlers are closures that capture datum values. The interpreter invokes them without needing to know the datum type.
Constructors
ThunkedMouseEnter (Unit -> Effect Unit)ThunkedMouseLeave (Unit -> Effect Unit)ThunkedClick (Unit -> Effect Unit)ThunkedDrag DragConfigThunkedZoom ZoomConfigThunkedCoordinatedHighlight { classify :: String -> HighlightClass, group :: Maybe String, identify :: Unit -> String, tooltipContent :: Maybe (Unit -> String), tooltipTrigger :: TooltipTrigger }ThunkedCoordinatedInteraction { group :: Maybe String, identify :: Unit -> String, position :: Maybe (Unit -> { x :: Number, y :: Number }), respond :: InteractionTrigger -> InteractionState }ThunkedBrush { extent :: BoundingBox, group :: Maybe String }
#forEach Source
forEach :: forall a. String -> ElementType -> Array a -> (a -> String) -> (a -> Tree) -> TreeSimple iteration over an array (most common case)
The datum type a is inferred from items and available in
the template function. Use attr constructors that capture values:
forEach "circles" Circle points _.id \pt ->
elem Circle
[ thunkedNum "cx" pt.x
, thunkedNum "cy" pt.y
, staticNum "r" 5.0
] []
#forEachP Source
forEachP :: forall source target. Project source target => String -> ElementType -> source -> (target -> String) -> (target -> Tree) -> TreeforEach with projection - iterates over any Projectable source
The killer feature: one Map, three projections, one diagram.
mapDiagram :: Map k v -> Tree
mapDiagram m =
elem SVG [...]
[ -- Domain nodes from keys
forEachP "keys" Group (MapKeys m) show \key -> keyNode key
-- Codomain nodes from values (auto-deduplicated)
, forEachP "values" Group (MapValues m) show \value -> valueNode value
-- Arrows from entries
, forEachP "arrows" Path (MapEntries m) (\(Tuple k _) -> show k) \(Tuple k v) ->
arrow (keyPos k) (valuePos v)
]
#forEachWithGUP Source
forEachWithGUP :: forall a. String -> ElementType -> Array a -> (a -> String) -> (a -> Tree) -> GUPSpec a -> TreeIteration with GUP (enter/update/exit transitions)
#fromTree Source
fromTree :: forall a. String -> ElementType -> a -> (a -> Array a) -> (a -> String) -> TraversalOrder -> Boolean -> (a -> Tree) -> TreeUnfold a tree structure to flat output
#preserveTree Source
preserveTree :: forall a. String -> ElementType -> a -> (a -> Array a) -> (a -> String) -> (a -> Tree) -> TreeUnfold a tree structure preserving hierarchy in output
#Project Source
class Project source target | source -> target whereProject a source structure into an array of target elements.
The functional dependency source -> target ensures each source type
has a canonical projection. Use newtypes for multiple projections from
the same underlying type.
This is "finally tagless" because:
- The type class is open for extension - users can add new instances
- Multiple projections from one source use newtypes to select
- The story: "One Map, three views, one diagram"
Members
Instances
#MapEntries Source
newtype MapEntries k vProject a Map to its key-value entries
Each entry is a Tuple k v representing one mapping.
Constructors
MapEntries (Map k v)
Instances
(Ord k) => Project (MapEntries k v) (Tuple k v)
#withBehaviors Source
withBehaviors :: Array ThunkedBehavior -> Tree -> TreeAttach behaviors to an element
#thunkedStr Source
thunkedStr :: String -> String -> AttrThunked string attribute (captures a value)
Use inside templates to capture datum-derived values:
\node -> elem Text [ thunkedStr "text-anchor" node.anchor ] []
#thunkedNum Source
thunkedNum :: String -> Number -> AttrThunked numeric attribute (captures a value)
Use inside templates to capture datum-derived values:
\node -> elem Circle [ thunkedNum "cx" node.x, thunkedNum "cy" node.y ] []
#onMouseEnter Source
onMouseEnter :: Effect Unit -> ThunkedBehaviorMouse enter handler (captures handler in closure)
\node -> withBehaviors [ onMouseEnter (callbacks.onHover node.path) ] $
elem Circle [...] []
#onMouseLeave Source
onMouseLeave :: Effect Unit -> ThunkedBehaviorMouse leave handler
#onDrag Source
onDrag :: DragConfig -> ThunkedBehaviorDrag behavior
#onZoom Source
onZoom :: ZoomConfig -> ThunkedBehaviorZoom behavior
#onCoordinatedHighlight Source
onCoordinatedHighlight :: { classify :: String -> HighlightClass, group :: Maybe String, identify :: String } -> ThunkedBehaviorCoordinated highlighting behavior
When this element is hovered, ALL elements with coordinated highlighting in the same group receive CSS classes based on their relationship:
.highlight-primary- the hovered element itself.highlight-related- elements related to hovered.highlight-dimmed- unrelated elements
Use inside forEach templates to capture datum:
forEach "nodes" Circle nodes _.id \node ->
withBehaviors
[ onCoordinatedHighlight
{ identify: node.name
, classify: \hoveredId ->
if node.name == hoveredId then Primary
else if hoveredId `elem` node.connections then Related
else Dimmed
, group: Nothing -- global coordination
, tooltip: Nothing -- or Just { content: "...", showWhen: OnHover }
}
] $
elem Circle [...] []
#onCoordinatedHighlightWithTooltip Source
onCoordinatedHighlightWithTooltip :: { classify :: String -> HighlightClass, group :: Maybe String, identify :: String, tooltip :: Maybe { content :: String, showWhen :: TooltipTrigger } } -> ThunkedBehaviorCoordinated highlight with tooltip support
Same as onCoordinatedHighlight but with optional tooltip configuration.
When tooltip is provided, it will show on hover (or based on showWhen trigger).
forEach "nodes" Circle nodes _.id \node ->
withBehaviors
[ onCoordinatedHighlightWithTooltip
{ identify: node.name
, classify: \hoveredId -> if node.name == hoveredId then Primary else Dimmed
, group: Nothing
, tooltip: Just { content: node.description, showWhen: OnHover }
}
] $
elem Circle [...] []
#onCoordinatedInteraction Source
onCoordinatedInteraction :: { group :: Maybe String, identify :: String, position :: Maybe { x :: Number, y :: Number }, respond :: InteractionTrigger -> InteractionState } -> ThunkedBehaviorFull coordinated interaction behavior (supports brush, hover, focus, selection)
Unlike onCoordinatedHighlight which only handles hover,
this behavior responds to ALL interaction triggers including brush regions.
forEach "points" Circle points _.id \pt ->
withBehaviors
[ onCoordinatedInteraction
{ identify: pt.id
, respond: \trigger -> case trigger of
HoverTrigger id -> if pt.id == id then Primary else Dimmed
BrushTrigger box -> if pointInBox pt.pos box then Selected else Dimmed
ClearTrigger -> Neutral
_ -> Neutral
, position: Just pt.pos -- For automatic brush hit-testing
, group: Nothing
}
] $
elem Circle [...] []
#onBrush Source
onBrush :: { extent :: BoundingBox, group :: Maybe String } -> ThunkedBehaviorAttach a brush overlay to an element
When users brush (drag) on this element, a BrushTrigger is emitted
to all elements registered with onCoordinatedInteraction in the same group.
elem Group [ staticStr "class" "brush-overlay" ] []
# withBehaviors
[ onBrush
{ extent: { x0: 0.0, y0: 0.0, x1: 400.0, y1: 300.0 }
, group: Just "scatter-plot"
}
]
Re-exports from Hylograph.Internal.Behavior.Types
#ZoomConfig Source
newtype ZoomConfigZoom behavior configuration
The target is the selection that will be transformed when zooming.
Typically this is an inner <g> element, while the zoom behavior is
attached to the outer <svg> element.
Example:
zoomConfig = ZoomConfig
{ scaleExtent: ScaleExtent 0.5 4.0 -- 50% to 400%
, targetSelector: ".zoom-group" -- What to transform
}
Instances
#TooltipTrigger Source
data TooltipTriggerWhen to show a tooltip
OnHover: Traditional tooltip - only when mouse is directly over elementWhenPrimary: Show when this element becomes Primary (from any view's hover)WhenRelated: Show when this element becomes Related (use judiciously!)
Constructors
Instances
#HighlightClass Source
data HighlightClassClassification of an element's highlight state
When an element is hovered, ALL elements with CoordinatedHighlight behavior are classified based on their relationship to the hovered element.
Primary: The hovered element itselfRelated: Connected/related to the hovered elementUpstream: Dependencies (things this element depends on)Downstream: Dependents (things that depend on this element)Dimmed: Not related (de-emphasized)Neutral: No highlight state change (default appearance)
Constructors
Instances
#DragConfig Source
data DragConfigDrag behavior configuration
SimpleDrag: Basic dragging with transformSimulationDrag: Drag with force simulation reheat (datum IS the simulation node)SimulationDragNested: Drag where datum has a.nodefield containing the simulation node
Instances
- 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
Semigroup instance: trees combine as siblings
This enables compositional chart building:
Key feature: linksLayer <> nodesLayer works even when they bind different data types internally!