Last active
          October 31, 2025 12:20 
        
      - 
      
- 
        Save Giammaria/e196425aedf7c45325136179268cbdff to your computer and use it in GitHub Desktop. 
    20251008_hierarchical_gantt_v_v2.6
  
        
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | { | |
| "$schema": "https://vega.github.io/schema/vega/v6.json", | |
| "bounds": "flush", | |
| "autosize": {"resize": true, "type": "pad", "contains": "padding"}, | |
| "background": "#fff", | |
| "signals": [ | |
| {"name": "isPowerBIVisual", "value": false}, | |
| {"name": "desiredHeight", "update": "400"}, | |
| {"name": "desiredWidth", "update": "1300"}, | |
| { | |
| "name": "configColumn", | |
| "description": "configurations for the columns that appear to the left of the Gantt", | |
| "init": "{xOffset: 5, innerPadding: 25, values: {fontWeight: 400, fontSize: 12}}" | |
| }, | |
| { | |
| "name": "configGantt", | |
| "description": "configurations for the Gantt", | |
| "update": "{x: ganttDimensions.width, childRect: {cornerRadius: 1, percentOfRowHeight: 0.5}, parentRect: {percentOfRowHeight: 0.25}, label: {xOffset: 7.5, font: 'Segoe UI', fontSize: 11, align: 'left', fontWeight: 400, fill: '#999'}, todayLine: {label: {text: 'Today'}, stroke: '#000', strokeWidth: 0.5, strokeDash: [3,5], strokeDashOffset: 4 }}" | |
| }, | |
| {"name": "configBorders", "init": "{'stroke': '#ccc', strokeWidth: 1.5}"}, | |
| { | |
| "name": "configGanttGridlines", | |
| "init": "{'topAxis': {'stroke': '#666', strokeWidth: 1}, secondaryAxis: {'stroke': '#ddd', strokeWidth: 1.5}}" | |
| }, | |
| { | |
| "name": "configAnimationDuration", | |
| "init": "{granularityReset: 750, granularitySliderClick: 250, anchorReset: 750, anchorMapClick: 250, showDetails: 750, nodeExpandCollapse: 500}" | |
| }, | |
| { | |
| "name": "configIncludedLeftHandColumns", | |
| "description": "array of column titles to be included. 'name' must be included. 'startDate', 'endDate', 'duration', and 'progress' are all 'optional'", | |
| "value": ["name", "startDate", "endDate", "test", "duration", "progress"] | |
| }, | |
| { | |
| "name": "configButtons", | |
| "description": "configurations for the button controls", | |
| "init": "{padding: 5, yOffset: 5, label: {text: 'Expand/Collapse All', font: 'Segoe UI', fontSize: 12, fill: '#888', fontStyle: 'regular', dy: 15}, outerStroke: '#767', fill: '#EEE', hoverFill: 'steelblue'}" | |
| }, | |
| { | |
| "name": "showDetailsConfig", | |
| "description": "configurations for the show details toggle control", | |
| "update": "{enabled: true, initialValue: true, xOffset: 40, track: {height: 7.5, width: 25, cornerRadius: 5, fill: '#EEE', stroke: '#777', strokeWidth: 1}, handle: {stroke: '#777', strokeWidth: 1, fill: '#fff'}, label: {text: 'Show Details', font: 'Segoe UI', fontSize: 12, fill: '#888', fontStyle: 'regular', dx: 10}, on: {fill: '#d1e0ec', fillOpacity: 1, stroke: '#777', strokeWidth: 1}, tooltip: {text: 'Show/hide detail columns'}}" | |
| }, | |
| { | |
| "name": "configTogglepanAndZoomMode", | |
| "description": "configurations for the mouse wheel granularity control", | |
| "update": "{enabled: true, initialValue: false, xOffset: 0, track: {height: 7.5, width: 25, cornerRadius: 5, fill: '#d1e0ec', stroke: '#777', strokeWidth: 1}, handle: {stroke: '#777', strokeWidth: 1, fill: '#fff'}, label: {text: 'Mouse wheel granularity', font: 'Segoe UI', fontSize: 12, fill: '#888', fontStyle: 'regular', dx: 5}, on: {fill: '#d1e0ec', fillOpacity: 1, stroke: '#777', strokeWidth: 1}, tooltip: {object: {title: 'Mouse Wheel Scroll vs. Zoom', '•      ': 'Click here to toggle between vertical scrolling and date granularity zooming.'}}}" | |
| }, | |
| { | |
| "name": "configDateStepSlider", | |
| "description": "configurations for the date granularity slider control", | |
| "update": "{enabled: true, innerPadding: 12.5, track: {height: 7.5, width: 130, cornerRadius: 5, fill: '#EEE', stroke: '#777', strokeWidth: 1}, handle: {outerStroke: '#777', outerStrokeWidth: 1, outerfill: '#fff', innerStroke: '#777', innerStrokeWidth: 1, innerFill: '#666'}, label: {text: 'Date Granularity', font: 'Segoe UI', fontSize: 10, fill: '#666', fontStyle: 'regular', dy: 10}, progress: {fill: '#d1e0ec', fillOpacity: 1}, tooltip: {text: 'Adjust date granularity'}, reset: {iconPath: 'M75 75L41 41C25.9 25.9 0 36.6 0 57.9L0 168c0 13.3 10.7 24 24 24l110.1 0c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24l0 104c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65 0-94.1c0-13.3-10.7-24-24-24z', tooltipText: 'Reset granularity', fill:'#888', hoverFill: '#333'}}" | |
| }, | |
| { | |
| "name": "configRow", | |
| "description": "configurations for the rows", | |
| "init": "{rowHeight: 25, levelIndentWidth: 15, defaultFill: '#40407d'}" | |
| }, | |
| { | |
| "name": "configIncludeRoot", | |
| "description": "boolean to indicate whether the root node should be visible", | |
| "init": "false" | |
| }, | |
| { | |
| "name": "configInitialDepth", | |
| "description": "the number of levels deep to start with", | |
| "value": 1 | |
| }, | |
| { | |
| "name": "configVerticalScrollbar", | |
| "description": "configurations for the vertical scroll bar", | |
| "update": "{enabled: actualHeight>adjustedHeight ,innerPadding: 10, track: {width: 10, height: extent([actualHeight, adjustedHeight])[0], fill: '#F3F3F3'}, handle: {height: max((adjustedHeight/actualHeight)*extent([actualHeight, adjustedHeight])[0], 30), fill: '#ddd', hover: {fill: '#888'}}}" | |
| }, | |
| { | |
| "name": "resetLevel", | |
| "description": "the number of levels deep to revert to when collapsing all nodes", | |
| "init": "configInitialDepth", | |
| "on": [] | |
| }, | |
| { | |
| "name": "expandAllClicked", | |
| "description": "boolean indicating whether expandAll was the last element clicked", | |
| "init": "false", | |
| "on": [ | |
| { | |
| "events": "@expand-all-collapse-all-button-background:click{0,50}", | |
| "update": "isAnimating ? expandAllClicked : !isValid(datum.datum) ? expandAllClicked : datum.datum.name === 'expandAll' ? true : false" | |
| }, | |
| { | |
| "events": "@gantt-row-clickable-rect:mousemove, @column-row-background-rect:mousemove", | |
| "update": "!isAnimating ? false : expandAllClicked" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "collapseAllClicked", | |
| "description": "boolean indicating whether collapseAll was the last element clicked", | |
| "init": "false", | |
| "on": [ | |
| { | |
| "events": "@expand-all-collapse-all-button-background:click", | |
| "update": "isAnimating ? collapseAllClicked : !isValid(datum.datum) ? collapseAllClicked : datum.datum.name === 'collapseAll' ? true : false" | |
| }, | |
| { | |
| "events": "@gantt-row-clickable-rect:mousemove, @column-row-background-rect:mousemove", | |
| "update": "!isAnimating ? false : collapseAllClicked" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "prevInteractionType", | |
| "update": "length(interactionTypeHistory) === 0 ? null : slice(interactionTypeHistory,-1)[0]" | |
| }, | |
| { | |
| "name": "preLastClickedNodeTargets", | |
| "on": [ | |
| { | |
| "events": "@gantt-row-clickable-rect:pointerdown, @column-row-background-rect:pointerdown", | |
| "update": "rowAnimation.t === 1 ? data('hierarchy-visible-target') : preLastClickedNodeTargets" | |
| }, | |
| { | |
| "events": "@expand-all-collapse-all-button-background:mousein{0, 0}", | |
| "update": "!isAnimating ? null : preLastClickedNodeTargets" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "lastClickedNode", | |
| "description": "node in the hierarchy with children that was last expanded or collapsed", | |
| "init": "null", | |
| "on": [ | |
| { | |
| "events": "@gantt-row-clickable-rect:pointerup, @column-row-background-rect:pointerup", | |
| "update": "!isAnimating && isValid(datum) && datum.hasChildren ? {timestamp: now(), datum: datum, state: datum.isExpanded} : lastClickedNode" | |
| }, | |
| { | |
| "events": "@expand-all-collapse-all-button-background:mousemove{0, 0}", | |
| "update": "!isAnimating ? null : lastClickedNode" | |
| }, | |
| { | |
| "events": "@gantt-row-clickable-rect:pointerdown, @column-row-background-rect:pointerdown", | |
| "update": "!isAnimating && isValid(datum) && datum.hasChildren ? null : lastClickedNode" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "mouseoverNodeDatum", | |
| "description": "the datum associated with the node that is currently being moused over", | |
| "init": "null", | |
| "on": [ | |
| { | |
| "events": "@gantt-row-clickable-rect:mouseover, @gantt-row-milestone-clickable-rect:mouseover, @column-row-background-rect: mouseover", | |
| "update": "isValid(datum) ? datum : null" | |
| }, | |
| { | |
| "events": "@gantt-row-clickable-rect:mouseout, @gantt-row-milestone-clickable-rect:mouseout, @column-row-background-rect:mouseout", | |
| "update": "null" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "interactionTypeHistory", | |
| "init": "[]", | |
| "on": [ | |
| { | |
| "events": "@gantt-row-clickable-rect:pointerdown, @column-row-background-rect:pointerdown", | |
| "update": "!datum.hasChildren ? interactionTypeHistory : length(interactionTypeHistory) === 0 ? ['nodeClick'] : split('nodeClick,' + join(interactionTypeHistory), ',',2)" | |
| }, | |
| { | |
| "events": "@expand-all-collapse-all-button-background:pointerdown", | |
| "update": "datum.datum.name === 'collapseAll' ? length(interactionTypeHistory) === 0 ? ['collapseAll'] : split(('collapseAll,'+join(interactionTypeHistory)),',',2) : length(interactionTypeHistory) === 0 ? ['expandAll'] : split(('expandAll,')+join(interactionTypeHistory),',',2)" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "isInitial", | |
| "description": "boolean indicating if this is considered the initial expand/collapse state", | |
| "init": "true", | |
| "on": [ | |
| { | |
| "events": {"type": "timer"}, | |
| "update": "!isAnimating ? false : isInitial" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "expandCollapseCount", | |
| "value": 0, | |
| "on": [ | |
| { | |
| "events": "@expand-all-collapse-all-button-background:click", | |
| "update": "isAnimating ? expandCollapseCount : expandCollapseCount+(rowAnimationTEased === 1 ? 1 : 0)" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "expandCollapseButtonDatum", | |
| "description": "the datum associated with the expand/collapse button that is currently being interacted with", | |
| "value": null, | |
| "on": [ | |
| { | |
| "events": "@expand-all-collapse-all-button-background:click", | |
| "update": "!isAnimating ? datum.datum : expandCollapseButtonDatum" | |
| }, | |
| { | |
| "events": "@expand-all-collapse-all-button-background:mouseout", | |
| "update": "!isAnimating ? null : expandCollapseButtonDatum" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "xDomainZoomPercentage", | |
| "value": 0.5, | |
| "on": [ | |
| { | |
| "events": { | |
| "markname": "granularity-slider-interactive-rect", | |
| "source": "scope", | |
| "type": "pointermove", | |
| "consume": true, | |
| "throttle": 80, | |
| "between": [{"type": "pointerdown"}, {"type": "pointerup"}] | |
| }, | |
| "update": "ganttHorizontalScrollMapMouseDown ? xDomainZoomPercentage : clamp(x('granularity-slider-interactive-rect')/configDateStepSlider.track.width,0,1)" | |
| }, | |
| { | |
| "events": {"signal": "granularityAnimation"}, | |
| "update": "granularityAnimation.active ? zoomStart + (zoomTarget - zoomStart) * granularityAnimation.tEased : xDomainZoomPercentage" | |
| }, | |
| { | |
| "events": {"signal": "wheel"}, | |
| "update": "ctrlKey || panAndZoomMode ? clamp(xDomainZoomPercentage * pow(wheel, pow(span(xDomain)/span(maxXDomain),0.9)), 0.005, 1) : xDomainZoomPercentage" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "panning", | |
| "init": "false", | |
| "on": [ | |
| { | |
| "events": "@rect-gantt-background:pointerdown", | |
| "update": "panAndZoomMode" | |
| }, | |
| {"events": "@rect-gantt-background:pointerup", "update": "false"} | |
| ] | |
| }, | |
| { | |
| "name": "panStartX", | |
| "on": [ | |
| { | |
| "events": { | |
| "type": "pointerdown", | |
| "source": "scope", | |
| "markname": "rect-gantt-background" | |
| }, | |
| "update": "x(group())" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "panStartAnchor", | |
| "on": [ | |
| { | |
| "events": { | |
| "type": "pointerdown", | |
| "source": "scope", | |
| "markname": "rect-gantt-background" | |
| }, | |
| "update": "xDomainAnchorPercentage" | |
| } | |
| ] | |
| }, | |
| {"name": "anchorMin", "update": "span(xDomain) / (2 * span(maxXDomain))"}, | |
| {"name": "anchorMax", "update": "1 - anchorMin"}, | |
| { | |
| "name": "xDomainAnchorPercentage", | |
| "description": "Tracks the current horizontal scroll percentage. Updates on: Pointer drag events; Clicking the horizontal scroll map; Mouse wheel scrolling (if Shift is pressed); Arrow key presses (ArrowLeft or ArrowRight); Reset animation triggers.", | |
| "value": 0.5, | |
| "on": [ | |
| { | |
| "events": { | |
| "type": "pointermove", | |
| "source": "scope", | |
| "consume": true, | |
| "markname": "rect-gantt-background", | |
| "throttle": 50, | |
| "between": [{"type": "pointerdown"}, {"type": "pointerup"}] | |
| }, | |
| "update": "panAndZoomMode ? clamp(panStartAnchor + (x(group()) - panStartX) / max(ganttDimensions.width,1), anchorMin, anchorMax) : xDomainAnchorPercentage" | |
| }, | |
| { | |
| "events": { | |
| "type": "pointermove", | |
| "source": "scope", | |
| "consume": true, | |
| "markname": "gantt-horizontal-scroll-map-interactive-rect", | |
| "throttle": 80, | |
| "between": [{"type": "pointerdown"}, {"type": "pointerup"}] | |
| }, | |
| "update": "granularitySliderMouseDown ? xDomainAnchorPercentage : clamp(((x(group())-ganttPosition.x)/ganttDimensions.width), span(xDomain)/2/span(maxXDomain)/2,1-(span(xDomain)/2/span(maxXDomain)/2))" | |
| }, | |
| { | |
| "events": {"signal": "anchorAnimation"}, | |
| "update": "anchorAnimation.active ? anchorStart + (anchorTarget - anchorStart) * anchorAnimation.tEased : xDomainAnchorPercentage" | |
| }, | |
| { | |
| "events": {"signal": "wheel"}, | |
| "update": "shiftKey ? clamp(xDomainAnchorPercentage * pow(wheel, pow(span(xDomain)/span(maxXDomain),0.9)), 0.1, 1) : xDomainAnchorPercentage" | |
| }, | |
| { | |
| "events": "window:keydown[event.key === 'ArrowLeft' || event.key === 'ArrowRight']", | |
| "update": "clamp(xDomainAnchorPercentage + 0.005 * (event.key === 'ArrowLeft' ? -1 : 1), span(xDomain)/2/span(maxXDomain)/2,1-(span(xDomain)/2/span(maxXDomain)/2))" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "verticalScrollbarMouseDown", | |
| "description": "A boolean indicating whether the vertical scrollbar is being clicked. Initialized to false. Updates to true when the vertical-scrollbar-group mark is clicked (if panAndZoomMode is off and the scrollbar is enabled). Resets to false on pointer release or when the cursor moves out of the scrollbar.", | |
| "value": false, | |
| "on": [ | |
| { | |
| "events": "@vertical-scrollbar-group:pointerdown", | |
| "update": "!panAndZoomMode && configVerticalScrollbar.enabled" | |
| }, | |
| { | |
| "events": { | |
| "type": "mouseout", | |
| "scope": "view", | |
| "markname": "vertical-scrollbar-group", | |
| "filter": ["!event.pointerdown"] | |
| }, | |
| "update": "false" | |
| }, | |
| { | |
| "events": { | |
| "type": "mouseover", | |
| "scope": "scope", | |
| "markname": "vertical-scrollbar-group", | |
| "filter": ["event.pointerdown"] | |
| }, | |
| "update": "!panAndZoomMode" | |
| }, | |
| {"events": {"type": "pointerup"}, "update": "false"} | |
| ] | |
| }, | |
| { | |
| "name": "verticalScrollbarMouseOver", | |
| "description": "A boolean indicating whether the vertical scrollbar is being hovered over. Initialized to false. Updates to true when hovered and resets to false when the cursor leaves.", | |
| "value": false, | |
| "on": [ | |
| { | |
| "events": "@vertical-scrollbar-group:mouseover", | |
| "update": "!panAndZoomMode && configVerticalScrollbar.enabled" | |
| }, | |
| {"events": "@vertical-scrollbar-group:mouseout", "update": "false"} | |
| ] | |
| }, | |
| { | |
| "name": "verticalScrollPercentage", | |
| "description": "Tracks the current vertical scroll position as a percentage. Updates on: Mouse wheel events (if panAndZoomMode is off); Arrow key presses (ArrowUp or ArrowDown); Dragging interactions within rect-gantt-background; Clicking the vertical scrollbar; When the scrollbar is disabled, resets to 0.", | |
| "value": 0, | |
| "on": [ | |
| { | |
| "events": { | |
| "type": "wheel", | |
| "consume": true, | |
| "force": true, | |
| "source": "view", | |
| "filter": ["!event.ctrlKey", "!event.shiftKey"] | |
| }, | |
| "update": "panAndZoomMode ? verticalScrollPercentage : clamp(verticalScrollPercentage - (-event.deltaY * pow(4, event.deltaMode) * 0.0015 * adjustedHeight / actualHeight), 0, (actualHeight-adjustedHeight)/actualHeight)" | |
| }, | |
| { | |
| "events": "window:keydown[event.key === 'ArrowUp' || event.key === 'ArrowDown']{0,0}", | |
| "update": "clamp(verticalScrollPercentage + verticalScrollIncrement * (event.key === 'ArrowDown' ? 1 : -1), 0, (actualHeight-adjustedHeight)/actualHeight)" | |
| }, | |
| { | |
| "events": { | |
| "type": "pointermove", | |
| "source": "scope", | |
| "consume": true, | |
| "markname": "rect-gantt-background", | |
| "throttle": 100, | |
| "between": [{"type": "pointerdown"}, {"type": "pointerup"}] | |
| }, | |
| "update": "panAndZoomMode && isValid(yPanStart) && isValid(y(group())) ? clamp(verticalScrollPercentage + (yPanPrevious + (invert('scaleScrollHandleY', y(group()) + range('scaleScrollHandleY')[0]) - yPanStart) - verticalScrollPercentage) * 0.35, 0, (actualHeight - adjustedHeight) / actualHeight) : verticalScrollPercentage" | |
| }, | |
| { | |
| "events": { | |
| "type": "pointermove", | |
| "source": "scope", | |
| "markname": "vertical-scrollbar-group", | |
| "throttle": 50, | |
| "between": [{"type": "pointerdown"}, {"type": "pointerup"}] | |
| }, | |
| "update": "panAndZoomMode ? verticalScrollPercentage : !configVerticalScrollbar.enabled ? 0 : invert('scaleScrollHandleY', y(group()))" | |
| }, | |
| { | |
| "events": {"signal": "!configVerticalScrollbar.enabled"}, | |
| "update": "0" | |
| }, | |
| { | |
| "events": {"signal": "verticalScrollPercentage"}, | |
| "update": "isFinite(verticalScrollPercentage) ? verticalScrollPercentage : 0" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "verticalScrollIncrement", | |
| "description": "Defines the vertical scroll step size as 1% of the scrollable height. Updates dynamically based on actualHeight and adjustedHeight.", | |
| "update": "0.1 * (actualHeight-adjustedHeight)/actualHeight" | |
| }, | |
| { | |
| "name": "yPanStart", | |
| "description": "Stores the initial vertical pan position when clicking the background. Updates when clicking rect-gantt-background, clamping the value within scrollable limits.", | |
| "value": null, | |
| "on": [ | |
| { | |
| "events": { | |
| "type": "pointerdown", | |
| "source": "scope", | |
| "markname": "rect-gantt-background", | |
| "throttle": 20 | |
| }, | |
| "update": "clamp(invert('scaleScrollHandleY', y(group()) + range('scaleScrollHandleY')[0]), 0, (actualHeight-adjustedHeight)/actualHeight)" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "yPanPrevious", | |
| "description": "Stores the previous vertical scroll percentage. Updates when clicking rect-gantt-background.", | |
| "value": null, | |
| "on": [ | |
| { | |
| "events": { | |
| "type": "pointerdown", | |
| "source": "scope", | |
| "markname": "rect-gantt-background" | |
| }, | |
| "update": "verticalScrollPercentage" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "verticalScrollVelocity", | |
| "description": "Computes the vertical scroll velocity based on pointer movement between pointerdown and pointerup. Uses an easing formula to maintain smooth scrolling.", | |
| "value": 0, | |
| "on": [ | |
| { | |
| "events": { | |
| "type": "pointermove", | |
| "source": "scope", | |
| "consume": true, | |
| "markname": "rect-gantt-background", | |
| "between": [{"type": "pointerdown"}, {"type": "pointerup"}] | |
| }, | |
| "update": "(clamp(yPanPrevious + ((invert('scaleScrollHandleY', y(group())) - yPanStart)), 0, (actualHeight-adjustedHeight)/actualHeight) - verticalScrollPercentage) * 0.2 + verticalScrollVelocity * 0.8" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "ganttHorizontalScrollMapMouseDown", | |
| "description": "A boolean indicating whether the horizontal scroll map is being clicked. Updates on pointer down and resets on pointer up.", | |
| "value": false, | |
| "on": [ | |
| { | |
| "events": "@gantt-horizontal-scroll-map-interactive-rect:pointerdown", | |
| "update": "true" | |
| }, | |
| {"events": {"type": "pointerup"}, "update": "false"} | |
| ] | |
| }, | |
| { | |
| "name": "horizontalScrollMapHeight", | |
| "description": "Defines the height of the horizontal scroll map.", | |
| "value": 10 | |
| }, | |
| { | |
| "name": "xDomainInitialWithoutPadding", | |
| "init": "[data('dataset-extent')[0].minStart, data('dataset-extent')[0].maxEnd]" | |
| }, | |
| { | |
| "name": "xDomainInitial", | |
| "update": "[ xDomainInitialWithoutPadding[0] - max(xInitPadPerSide*span(xDomainInitialWithoutPadding), xInitPadMinMs), xDomainInitialWithoutPadding[1] + max(xInitPadPerSide*span(xDomainInitialWithoutPadding), xInitPadMinMs) ]" | |
| }, | |
| {"name": "xInitPadPerSide", "value": 0.05}, | |
| {"name": "xInitPadMinMs", "value": 86400000}, | |
| { | |
| "name": "xDomain", | |
| "update": "[scale('xZoomScale0', xDomainZoomPercentage), scale('xZoomScale1', xDomainZoomPercentage)]" | |
| }, | |
| { | |
| "name": "xDomainAnchorInitial", | |
| "init": "xDomainInitial[0]+(span(xDomainInitial)/2)" | |
| }, | |
| { | |
| "name": "xDomainAnchor", | |
| "init": "xDomainAnchorInitial", | |
| "on": [ | |
| { | |
| "events": {"signal": "xDomainAnchorPercentage"}, | |
| "update": "scale('xAnchorScale', xDomainAnchorPercentage)" | |
| } | |
| ] | |
| }, | |
| { | |
| "update": "[xDomainInitial[0]-span(xDomainInitial)/2, xDomainInitial[1]+span(xDomainInitial)/2]", | |
| "name": "maxXDomain" | |
| }, | |
| { | |
| "name": "minXDomain", | |
| "update": "[xDomainAnchor-msPerDay/12, xDomainAnchor+msPerDay/12]" | |
| }, | |
| {"name": "smallestAllowableXWidth", "value": 20}, | |
| { | |
| "name": "granularitySliderMouseDown", | |
| "value": false, | |
| "on": [ | |
| { | |
| "events": "@granularity-slider-interactive-rect:pointerdown", | |
| "update": "true" | |
| }, | |
| { | |
| "events": "@granularity-slider-interactive-rect:pointerup", | |
| "update": "false" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "showDetailsClick", | |
| "init": "{start: now(), end: now(), t: 1, tEased: 1}", | |
| "on": [ | |
| { | |
| "events": [{"signal": "showDetails"}], | |
| "update": "showDetailsClick.t === 1 ? {start: now(), end: now()+ configAnimationDuration.showDetails, t:0, tEased: 0} : showDetailsClick" | |
| }, | |
| { | |
| "events": {"signal": "timer"}, | |
| "update": "showDetailsClick.t < 1 ? {start: showDetailsClick.start, end: showDetailsClick.end, t:(now()-showDetailsClick.start)/(showDetailsClick.end-showDetailsClick.start), tEased: clamp(showDetailsClick.t < 0.5 ? 4 * pow(showDetailsClick.t, 3) : 1 - pow(-2 * showDetailsClick.t + 2, 3) / 2, 0,1)} : {start: showDetailsClick.start, end: showDetailsClick.end, t: 1, tEased: 1}" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "ganttPosition", | |
| "init": "{x: data('column-data')[0].totalWidth, y:0, dy: 19}", | |
| "on": [ | |
| { | |
| "events": {"signal": "showDetailsClick.tEased"}, | |
| "update": "{x: ganttPosition.x + (data('column-data')[0].totalWidth - ganttPosition.x) * showDetailsClick.tEased, y: ganttPosition.y, dy: ganttPosition.dy}" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "ganttDimensions", | |
| "update": "{width: width-ganttPosition.x-2 - (actualHeight > adjustedHeight ? configVerticalScrollbar.track.width : 0), height: adjustedHeight}" | |
| }, | |
| { | |
| "name": "xCurrentTopLevelUnit", | |
| "description": "Determines the time unit displayed", | |
| "update": "data('ganttTimeSeriesConfigurations')[0].tickCount" | |
| }, | |
| { | |
| "name": "xCurrentSecondaryUnit", | |
| "description": "Determines the time unit displayed", | |
| "update": "data('ganttTimeSeriesConfigurations')[0].secondaryTickCount" | |
| }, | |
| { | |
| "name": "xCurrentSecondaryBand", | |
| "update": "scale('xScaleGanttTimeSeries', xDomain[0]+(msPerDay*365/([1,12,365,8760][indexof(['year', 'month', 'day', 'hour'], xCurrentSecondaryUnit)])))" | |
| }, | |
| { | |
| "name": "wheel", | |
| "value": 1, | |
| "on": [ | |
| { | |
| "events": "wheel!", | |
| "force": true, | |
| "update": "pow(1.001, -event.deltaY * pow(16, event.deltaMode))" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "timer", | |
| "init": "now()", | |
| "on": [{"events": {"type": "timer", "throttle": 50}, "update": "now()"}] | |
| }, | |
| { | |
| "name": "zoomStart", | |
| "value": 0.5, | |
| "on": [ | |
| { | |
| "events": "@granularity-slider-interactive-rect:click", | |
| "update": "xDomainZoomPercentage" | |
| }, | |
| { | |
| "events": "@granularity-reset-interactive-rect:click", | |
| "update": "xDomainZoomPercentage" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "zoomTarget", | |
| "value": 0.5, | |
| "on": [ | |
| { | |
| "events": "@granularity-slider-interactive-rect:click", | |
| "update": "clamp(x('granularity-slider-interactive-rect')/configDateStepSlider.track.width,0,1)" | |
| }, | |
| {"events": "@granularity-reset-interactive-rect:click", "update": "0.5"} | |
| ] | |
| }, | |
| { | |
| "name": "granularityAnimation", | |
| "init": "{start: now(), end: now(), active: false, t: 1, tEased: 1, markName: null}", | |
| "on": [ | |
| { | |
| "events": "@granularity-reset-interactive-rect:click", | |
| "update": "{start: now(), end: now()+configAnimationDuration.granularityReset, active: true, t: 0, tEased: 0, markName: 'granularity-reset-interactive-rect'}" | |
| }, | |
| { | |
| "events": "@granularity-slider-interactive-rect:click", | |
| "update": "{start: now(), end: now()+configAnimationDuration.granularitySliderClick, active: true, t: 0, tEased: 0, markName: 'granularity-slider-interactive-rect'}" | |
| }, | |
| { | |
| "events": {"signal": "timer"}, | |
| "update": "now() > granularityAnimation.end ? {start: granularityAnimation.start, end: granularityAnimation.start, active: false, t: 1, tEased: 1, markName: null} : {start: granularityAnimation.start, end: granularityAnimation.end, active: true, t: (now()-granularityAnimation.start)/(granularityAnimation.end-granularityAnimation.start), tEased: granularityAnimation.t < 0.5 ? 4 * pow(granularityAnimation.t, 3) : 1 - pow(-2 * granularityAnimation.t + 2, 3) / 2, markName: granularityAnimation.markName}" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "anchorStart", | |
| "value": 0.5, | |
| "on": [ | |
| { | |
| "events": "@gantt-horizontal-scroll-map-interactive-rect:click", | |
| "update": "xDomainAnchorPercentage" | |
| }, | |
| { | |
| "events": "@granularity-reset-interactive-rect:click", | |
| "update": "xDomainAnchorPercentage" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "anchorTarget", | |
| "value": 0.5, | |
| "on": [ | |
| { | |
| "events": "@gantt-horizontal-scroll-map-interactive-rect:click", | |
| "update": "clamp(((x(group())-ganttPosition.x)/ganttDimensions.width), span(xDomain)/2/span(maxXDomain)/2,1-(span(xDomain)/2/span(maxXDomain)/2))" | |
| }, | |
| {"events": "@granularity-reset-interactive-rect:click", "update": "0.5"} | |
| ] | |
| }, | |
| { | |
| "name": "anchorAnimation", | |
| "init": "{start: now(), end: now(), active: false, t: 1, tEased: 1, markName: null}", | |
| "on": [ | |
| { | |
| "events": "@granularity-reset-interactive-rect:click", | |
| "update": "{start: now(), end: now()+configAnimationDuration.anchorReset, active: true, t: 0, tEased: 0, markName: 'granularity-reset-interactive-rect'}" | |
| }, | |
| { | |
| "events": "@gantt-horizontal-scroll-map-interactive-rect:click", | |
| "update": "{start: now(), end: now()+configAnimationDuration.anchorMapClick, active: true, t: 0, tEased: 0, markName: 'gantt-horizontal-scroll-map-interactive-rect'}" | |
| }, | |
| { | |
| "events": {"signal": "timer"}, | |
| "update": "now() > anchorAnimation.end ? {start: anchorAnimation.start, end: anchorAnimation.start, active: false, t: 1, tEased: 1, markName: null} : {start: anchorAnimation.start, end: anchorAnimation.end, active: true, t: (now()-anchorAnimation.start)/(anchorAnimation.end-anchorAnimation.start), tEased: anchorAnimation.t < 0.5 ? 4 * pow(anchorAnimation.t, 3) : 1 - pow(-2 * anchorAnimation.t + 2, 3) / 2, markName: anchorAnimation.markName}" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "showDetails", | |
| "description": "the currently toggled value for the 'show details' control", | |
| "init": "showDetailsConfig.initialValue", | |
| "on": [ | |
| { | |
| "events": "@show-details-interactive-rect:click{750}", | |
| "update": "!showDetails" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "panAndZoomMode", | |
| "description": "A boolean indicating whether the visualization is in pan-and-zoom mode. Initialized from configTogglepanAndZoomMode.initialValue. Updates when the pan-and-zoom-mode-interactive-rect is clicked (toggling its value) or when the space bar is pressed.", | |
| "init": "configTogglepanAndZoomMode.initialValue", | |
| "on": [ | |
| { | |
| "events": "@pan-and-zoom-mode-interactive-rect:click", | |
| "update": "!panAndZoomMode" | |
| }, | |
| { | |
| "events": "window:keydown[event.keyCode === 32]{0, 100}", | |
| "update": "panAndZoomMode ? false : true" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "ctrlKey", | |
| "value": false, | |
| "on": [ | |
| {"events": "window:keydown[event.ctrlKey]", "update": "true"}, | |
| {"events": "window:keyup", "update": "false"} | |
| ] | |
| }, | |
| { | |
| "name": "shiftKey", | |
| "value": false, | |
| "on": [ | |
| {"events": "window:keydown[event.shiftKey]", "update": "true"}, | |
| {"events": "window:keyup", "update": "false"} | |
| ] | |
| }, | |
| { | |
| "name": "currentMaxIndentWidth", | |
| "description": "the current maximum indent width for all visible nodes", | |
| "update": "(extent(pluck(data('hierarchy-initial'), 'level'))[1]-1)*configRow.levelIndentWidth" | |
| }, | |
| { | |
| "name": "scrollY", | |
| "update": "actualHeight > adjustedHeight ? clamp(-verticalScrollPercentage*actualHeight, -(actualHeight-adjustedHeight), 2*ganttPosition.dy) : 0" | |
| }, | |
| {"name": "msPerDay", "value": 86400000}, | |
| { | |
| "name": "actualHeight", | |
| "description": "Computes the total height based on the number of all time-series blocks times configRow.rowHeight. Updates dynamically when the dataset changes", | |
| "update": "data('height-animation')[0]['animatedHeight']" | |
| }, | |
| { | |
| "name": "adjustedHeight", | |
| "description": "he initial height of the visualization, set to 300. Rows that go beyond this height will require scrolling/panning", | |
| "update": "min((isPowerBIVisual ? (containerSize()[1] || windowSize()[1]) : desiredHeight), actualHeight)" | |
| }, | |
| { | |
| "name": "height", | |
| "update": "min(data('height-animation')[0].animatedHeight, adjustedHeight)" | |
| }, | |
| { | |
| "name": "width", | |
| "update": "isPowerBIVisual ? containerSize()[0] || windowSize()[0] : desiredWidth" | |
| }, | |
| {"name": "initialTimestamp", "init": "now()"}, | |
| { | |
| "name": "animateCount", | |
| "init": "0", | |
| "on": [ | |
| { | |
| "events": {"signal": "lastClickedNode"}, | |
| "update": "isAnimating || !isValid(lastClickedNode) || !lastClickedNode.datum.hasChildren ? animateCount : animateCount + 1" | |
| }, | |
| { | |
| "events": {"signal": "expandCollapseButtonDatum"}, | |
| "update": "isAnimating || !isValid(expandCollapseButtonDatum) ? animateCount : animateCount + 1" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "isAnimating", | |
| "init": "false", | |
| "on": [ | |
| { | |
| "events": {"type": "timer"}, | |
| "update": "!isValid(data('hierarchy-animation-bounds')) ? false : !(timer > data('hierarchy-animation-bounds')[0].end+500)" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "animStartTick", | |
| "init": "0", | |
| "on": [ | |
| { | |
| "events": "@gantt-row-clickable-rect:pointerup, @column-row-background-rect:pointerup", | |
| "update": "!isAnimating && datum.hasChildren ? timer : animStartTick" | |
| }, | |
| { | |
| "events": "@expand-all-collapse-all-button-background:pointerdown{0,0}", | |
| "update": "rowAnimationTEased === 1 ? timer : animStartTick" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "rowAnimation", | |
| "update": "{active: (timer < (animStartTick + configAnimationDuration.nodeExpandCollapse)), t: clamp((timer - animStartTick) / configAnimationDuration.nodeExpandCollapse, 0, 1)}" | |
| }, | |
| { | |
| "name": "rowAnimationTEased", | |
| "update": "rowAnimation.t < 0.5 ? 4*pow(rowAnimation.t,3) : 1 - pow(-2*rowAnimation.t + 2, 3)/2" | |
| } | |
| ], | |
| "marks": [ | |
| { | |
| "name": "group-everything", | |
| "type": "group", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "width": {"signal": "width"}, | |
| "height": {"signal": "height"}, | |
| "fill": {"signal": "background"} | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "group-header", | |
| "type": "group", | |
| "interactive": false, | |
| "encode": {"enter": {"y": {"value": -65}}}, | |
| "signals": [ | |
| { | |
| "name": "panAndZoomModeClick", | |
| "init": "{start: now(), end: now(), t: 1, tEased: 1}", | |
| "on": [ | |
| { | |
| "events": "@pan-and-zoom-mode-interactive-rect:click", | |
| "update": "{start: now(), end: now()+250, t:0, tEased: 0}" | |
| }, | |
| { | |
| "events": {"signal": "timer"}, | |
| "update": "panAndZoomModeClick.t < 1 ? {start: panAndZoomModeClick.start, end: panAndZoomModeClick.end, t:(now()-panAndZoomModeClick.start)/(panAndZoomModeClick.end-panAndZoomModeClick.start), tEased: clamp(panAndZoomModeClick.t < 0.5 ? 4 * pow(panAndZoomModeClick.t, 3) : 1 - pow(-2 * panAndZoomModeClick.t + 2, 3) / 2, 0,1)} : {start: panAndZoomModeClick.start, end: panAndZoomModeClick.end, t: 1, tEased: 1}" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "showDetailsClick", | |
| "init": "{start: now(), end: now(), t: 1, tEased: 1}", | |
| "on": [ | |
| { | |
| "events": [{"signal": "showDetails"}], | |
| "update": "{start: now(), end: now()+250, t:0, tEased: 0}" | |
| }, | |
| { | |
| "events": {"signal": "timer"}, | |
| "update": "showDetailsClick.t < 1 ? {start: showDetailsClick.start, end: showDetailsClick.end, t:(now()-showDetailsClick.start)/(showDetailsClick.end-showDetailsClick.start), tEased: clamp(showDetailsClick.t < 0.5 ? 4 * pow(showDetailsClick.t, 3) : 1 - pow(-2 * showDetailsClick.t + 2, 3) / 2, 0,1)} : {start: showDetailsClick.start, end: showDetailsClick.end, t: 1, tEased: 1}" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "showDetailsStart", | |
| "init": "!showDetails ? (showDetailsConfig.track.width - showDetailsConfig.track.height*0.9) : (showDetailsConfig.track.height*0.9)", | |
| "on": [ | |
| { | |
| "events": "@show-details-interactive-rect:click", | |
| "update": "!showDetails ? (showDetailsConfig.track.width - showDetailsConfig.track.height*0.9) : (showDetailsConfig.track.height*0.9)" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "showDetailsTarget", | |
| "init": "showDetails ? (showDetailsConfig.track.width - showDetailsConfig.track.height*0.9) : (showDetailsConfig.track.height*0.9)", | |
| "on": [ | |
| { | |
| "events": "@show-details-interactive-rect:click", | |
| "update": "showDetails ? (showDetailsConfig.track.width - showDetailsConfig.track.height*0.9) : (showDetailsConfig.track.height*0.9)" | |
| } | |
| ] | |
| } | |
| ], | |
| "marks": [ | |
| { | |
| "name": "group-expand-all-collapse-all-buttons", | |
| "description": "the group of marks that make up the expand/collapse buttons", | |
| "type": "group", | |
| "interactive": false, | |
| "marks": [ | |
| { | |
| "name": "label-expand-collapse-text", | |
| "description": "the title for the control", | |
| "type": "text", | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "x": { | |
| "signal": "data('expandAllCollapseAllConfig')[0]['rowNumber']*(12000*data('expandAllCollapseAllConfig')[0]['size'])" | |
| }, | |
| "text": {"signal": "configButtons.label.text || ''"}, | |
| "baseline": {"value": "top"}, | |
| "font": {"signal": "configButtons.label.font"}, | |
| "fontSize": {"signal": "configButtons.label.fontSize"}, | |
| "fontStyle": {"signal": "configButtons.label.fontStyle"}, | |
| "align": {"value": "left"}, | |
| "fill": {"signal": "configButtons.label.fill"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "expand-all-collapse-all-dummy-button-labels", | |
| "description": "icon path marks that will be used by other button marks for reactive geometry", | |
| "type": "symbol", | |
| "from": {"data": "expandAllCollapseAllConfig"}, | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "x": { | |
| "signal": "configButtons.padding+(datum.rowNumber+1)*(12000*datum.size)", | |
| "offset": { | |
| "signal": "data('label-expand-collapse-text')[0].bounds.x2" | |
| } | |
| }, | |
| "y": { | |
| "signal": "-1", | |
| "offset": {"signal": "datum.rowNumber === 1 ? 0 : -0.6"} | |
| }, | |
| "size": {"field": "size"}, | |
| "shape": {"field": "path"}, | |
| "fill": {"value": "#999"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "expand-all-collapse-all-button-background", | |
| "description": "the background for the expand/collapse buttons", | |
| "type": "rect", | |
| "from": { | |
| "data": "expand-all-collapse-all-dummy-button-labels" | |
| }, | |
| "interactive": true, | |
| "encode": { | |
| "enter": { | |
| "x": {"signal": "datum.bounds.x1-configButtons.padding"}, | |
| "x2": {"signal": "datum.bounds.x2+configButtons.padding"}, | |
| "y": {"signal": "datum.bounds.y1-configButtons.padding"}, | |
| "y2": {"signal": "datum.bounds.y2+configButtons.padding"}, | |
| "cornerRadiusTopLeft": { | |
| "signal": "datum.datum.rowNumber == 0 ? 5 : 0" | |
| }, | |
| "cornerRadiusBottomLeft": { | |
| "signal": "datum.datum.rowNumber == 0 ? 5 : 0" | |
| }, | |
| "cornerRadiusTopRight": { | |
| "signal": "datum.datum.rowNumber == 1 ? 5 : 0" | |
| }, | |
| "cornerRadiusBottomRight": { | |
| "signal": "datum.datum.rowNumber == 1 ? 5 : 0" | |
| }, | |
| "fillOpacity": {"value": 1}, | |
| "stroke": {"signal": "configButtons.outerStroke"}, | |
| "fill": {"signal": "background || '#fff'"}, | |
| "opacity": {"value": 1}, | |
| "cursor": {"value": "pointer"}, | |
| "tooltip": {"signal": "datum.datum.tooltip"} | |
| }, | |
| "update": { | |
| "stroke": {"signal": "configButtons.outerStroke"}, | |
| "strokeOpacity": {"value": 0.65} | |
| }, | |
| "hover": {"strokeOpacity": {"value": 1}} | |
| } | |
| }, | |
| { | |
| "name": "expand-all-collapse-all-buttons", | |
| "description": "the symbols that appear on the button faces", | |
| "type": "rect", | |
| "from": { | |
| "data": "expand-all-collapse-all-dummy-button-labels" | |
| }, | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "x": {"signal": "datum.bounds.x1-configButtons.padding"}, | |
| "x2": {"signal": "datum.bounds.x2+configButtons.padding"}, | |
| "y": {"signal": "datum.bounds.y1-configButtons.padding"}, | |
| "y2": {"signal": "datum.bounds.y2+configButtons.padding"}, | |
| "cornerRadiusTopLeft": { | |
| "signal": "datum.datum.rowNumber == 0 ? 5 : 0" | |
| }, | |
| "cornerRadiusBottomLeft": { | |
| "signal": "datum.datum.rowNumber == 0 ? 5 : 0" | |
| }, | |
| "cornerRadiusTopRight": { | |
| "signal": "datum.datum.rowNumber == 1 ? 5 : 0" | |
| }, | |
| "cornerRadiusBottomRight": { | |
| "signal": "datum.datum.rowNumber == 1 ? 5 : 0" | |
| }, | |
| "fillOpacity": {"value": 0.25}, | |
| "cursor": {"value": "pointer"}, | |
| "tooltip": {"signal": "datum.datum.tooltip"} | |
| }, | |
| "update": { | |
| "fill": { | |
| "signal": "isValid(expandCollapseButtonDatum) && expandCollapseButtonDatum.rowNumber === datum.datum.rowNumber ? configButtons.hoverFill : configButtons.fill" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "expand-all-collapse-all-button-labels", | |
| "description": "the actual button face symbols", | |
| "type": "symbol", | |
| "from": { | |
| "data": "expand-all-collapse-all-dummy-button-labels" | |
| }, | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "x": {"signal": "datum.x"}, | |
| "y": {"signal": "datum.y"}, | |
| "size": {"field": "size"}, | |
| "shape": {"signal": "datum.datum.path"}, | |
| "fill": {"field": "fill"}, | |
| "cursor": {"value": "pointer"}, | |
| "tooltip": {"signal": "datum.datum.tooltip"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "expand-all-collapse-all-clickStopper", | |
| "description": "prevents repetitive expand all / collapse all clicks", | |
| "type": "rect", | |
| "from": {"data": "expand-all-collapse-all-button-background"}, | |
| "interactive": true, | |
| "encode": { | |
| "update": { | |
| "tooltip": {"signal": "datum.datum.datum.tooltip"}, | |
| "x": {"signal": "datum.bounds.x1"}, | |
| "width": { | |
| "signal": "!isValid(interactionTypeHistory[0]) || datum.datum.datum.name !== interactionTypeHistory[0] ? 0 : datum.bounds.x2-datum.bounds.x1" | |
| }, | |
| "y": {"signal": "datum.bounds.y1"}, | |
| "y2": {"signal": "datum.bounds.y2"}, | |
| "cornerRadiusTopLeft": {"field": "cornerRadiusTopLeft"}, | |
| "cornerRadiusBottomLeft": { | |
| "field": "cornerRadiusBottomLeft" | |
| }, | |
| "cornerRadiusTopRight": {"field": "cornerRadiusTopRight"}, | |
| "cornerRadiusBottomRight": { | |
| "field": "cornerRadiusBottomRight" | |
| }, | |
| "fill": {"signal": "'steelblue'"}, | |
| "fillOpacity": {"value": 0.25} | |
| } | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "group-show-details", | |
| "type": "group", | |
| "from": {"data": "group-expand-all-collapse-all-buttons"}, | |
| "interactive": false, | |
| "clip": false, | |
| "encode": { | |
| "enter": { | |
| "x": {"signal": "datum.bounds.x2+showDetailsConfig.xOffset"}, | |
| "y": { | |
| "signal": "datum.bounds.y1+(datum.bounds.y2-datum.bounds.y1)/2" | |
| } | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "show-details-label", | |
| "description": "the title for the toggle control", | |
| "type": "text", | |
| "interactive": false, | |
| "encode": { | |
| "enter": {"x": {"signal": "0"}}, | |
| "update": { | |
| "text": {"signal": "showDetailsConfig.label.text"}, | |
| "baseline": {"value": "middle"}, | |
| "font": {"signal": "showDetailsConfig.label.font"}, | |
| "fontSize": { | |
| "signal": "showDetailsConfig.label.fontSize" | |
| }, | |
| "fontStyle": { | |
| "signal": "showDetailsConfig.label.fontStyle" | |
| }, | |
| "align": {"value": "left"}, | |
| "fill": {"signal": "showDetailsConfig.label.fill"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "show-details-track-rect", | |
| "description": "the track for the toggle control", | |
| "type": "rect", | |
| "from": {"data": "show-details-label"}, | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "x": { | |
| "signal": "datum.bounds.x1 + datum.bounds.x2", | |
| "offset": {"signal": "showDetailsConfig.label.dx"} | |
| }, | |
| "y": {"signal": "-(datum.bounds.y2-datum.bounds.y1)/3"} | |
| }, | |
| "update": { | |
| "height": {"signal": "showDetailsConfig.track.height"}, | |
| "width": {"signal": "showDetailsConfig.track.width"}, | |
| "cornerRadius": { | |
| "signal": "showDetailsConfig.track.cornerRadius" | |
| }, | |
| "fill": {"signal": "showDetailsConfig.track.fill"}, | |
| "stroke": {"signal": "showDetailsConfig.track.stroke"}, | |
| "strokeWidth": { | |
| "signal": "showDetailsConfig.track.strokeWidth" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "show-details-track-rect-on", | |
| "description": "the track for the toggle control", | |
| "type": "rect", | |
| "from": {"data": "show-details-track-rect"}, | |
| "interactive": false, | |
| "encode": { | |
| "enter": {"x": {"field": "x"}, "y": {"field": "y"}}, | |
| "update": { | |
| "height": {"field": "height"}, | |
| "width": {"field": "width"}, | |
| "cornerRadius": {"field": "cornerRadius"}, | |
| "fill": {"signal": "showDetailsConfig.on.fill"}, | |
| "stroke": {"field": "stroke"}, | |
| "strokeWidth": {"field": "strokeWidth"}, | |
| "fillOpacity": { | |
| "signal": "showDetails ? showDetailsClick.tEased : 1-showDetailsClick.tEased" | |
| }, | |
| "strokeOpacity": { | |
| "signal": "showDetails ? showDetailsClick.tEased : 1-showDetailsClick.tEased" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "group-show-details-toggle-outer-arc", | |
| "type": "group", | |
| "from": {"data": "show-details-track-rect"}, | |
| "interactive": false, | |
| "transform": [ | |
| { | |
| "type": "formula", | |
| "expr": "showDetailsClick.tEased", | |
| "as": "tEased" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "showDetailsStart + (showDetailsTarget - showDetailsStart) * showDetailsClick.tEased", | |
| "as": "x" | |
| } | |
| ], | |
| "encode": { | |
| "enter": { | |
| "x": {"signal": "datum.bounds.x1"}, | |
| "y": { | |
| "signal": "datum.bounds.y1+showDetailsConfig.track.height/2" | |
| } | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "show-details-toggle-outer-arc", | |
| "description": "the circle mark that serves as the the 'toggle handle'", | |
| "from": {"data": "show-details-track-rect"}, | |
| "type": "arc", | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "innerRadius": {"value": 0}, | |
| "outerRadius": { | |
| "signal": "showDetailsConfig.track.height*0.9" | |
| }, | |
| "startAngle": {"signal": "0"}, | |
| "endAngle": {"signal": "2*PI"}, | |
| "stroke": { | |
| "signal": "showDetailsConfig.handle.stroke || '#BBB'" | |
| }, | |
| "strokeWidth": { | |
| "signal": "showDetailsConfig.handle.strokeWidth" | |
| }, | |
| "fill": { | |
| "signal": "showDetailsConfig.handle.fill || '#fff'" | |
| } | |
| }, | |
| "update": {"x": {"signal": "datum.x"}} | |
| } | |
| }, | |
| { | |
| "name": "show-details-interactive-rect", | |
| "type": "rect", | |
| "from": {"data": "show-details-track-rect"}, | |
| "interactive": true, | |
| "encode": { | |
| "update": { | |
| "y": { | |
| "signal": "datum.bounds.y1-(showDetailsConfig.track.height*0.5)" | |
| }, | |
| "y2": { | |
| "signal": "datum.bounds.y2+showDetailsConfig.track.height*0.5" | |
| }, | |
| "x": {"signal": "-(datum.bounds.x2-datum.bounds.x1)"}, | |
| "x2": {"signal": "datum.bounds.x2"}, | |
| "fill": {"value": "transparent"}, | |
| "tooltip": { | |
| "signal": "showDetailsConfig.tooltip.text" | |
| }, | |
| "cursor": {"value": "pointer"} | |
| } | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "group-pan-and-zoom-mode", | |
| "description": "group for the marks that make up the toggle control to hide/show details", | |
| "type": "group", | |
| "interactive": false, | |
| "clip": false, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "ganttPosition.x+ganttDimensions.width"} | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "pan-and-zoom-mode-text-zoom", | |
| "description": "the title for the toggle control", | |
| "type": "text", | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "y": { | |
| "signal": "configTogglepanAndZoomMode.track.height/2" | |
| } | |
| }, | |
| "update": { | |
| "x": {"signal": "0"}, | |
| "text": {"signal": "'Pan & Zoom Mode'"}, | |
| "fontWeight": { | |
| "signal": "panAndZoomMode ? 'bold' : 'regular'" | |
| }, | |
| "baseline": {"value": "top"}, | |
| "font": { | |
| "signal": "configTogglepanAndZoomMode.label.font" | |
| }, | |
| "fontSize": { | |
| "signal": "configTogglepanAndZoomMode.label.fontSize" | |
| }, | |
| "fontStyle": { | |
| "signal": "configTogglepanAndZoomMode.label.fontStyle" | |
| }, | |
| "align": {"value": "right"}, | |
| "fill": { | |
| "signal": "configTogglepanAndZoomMode.label.fill" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "pan-and-zoom-mode-track-rect", | |
| "description": "the track for the toggle control", | |
| "type": "rect", | |
| "from": {"data": "pan-and-zoom-mode-text-zoom"}, | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "x": { | |
| "signal": "-(datum.bounds.x2-datum.bounds.x1)-configTogglepanAndZoomMode.track.width*2", | |
| "offset": { | |
| "signal": "-configTogglepanAndZoomMode.label.dx/2" | |
| } | |
| } | |
| }, | |
| "update": { | |
| "y": {"signal": "(datum.bounds.y2-datum.bounds.y1)/2"}, | |
| "height": { | |
| "signal": "configTogglepanAndZoomMode.track.height" | |
| }, | |
| "width": { | |
| "signal": "configTogglepanAndZoomMode.track.width" | |
| }, | |
| "cornerRadius": { | |
| "signal": "configTogglepanAndZoomMode.track.cornerRadius" | |
| }, | |
| "fill": { | |
| "signal": "panAndZoomMode ? configTogglepanAndZoomMode.on.fill : configTogglepanAndZoomMode.track.fill" | |
| }, | |
| "stroke": { | |
| "signal": "configTogglepanAndZoomMode.track.stroke" | |
| }, | |
| "strokeWidth": { | |
| "signal": "configTogglepanAndZoomMode.track.strokeWidth" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "group-pan-and-zoom-mode-toggle-outer-arc", | |
| "type": "group", | |
| "from": {"data": "pan-and-zoom-mode-track-rect"}, | |
| "interactive": false, | |
| "transform": [ | |
| { | |
| "type": "formula", | |
| "expr": "panAndZoomModeClick.tEased", | |
| "as": "tEased" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "configTogglepanAndZoomMode.track.height*0.9", | |
| "as": "scrollX" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "configTogglepanAndZoomMode.track.width - configTogglepanAndZoomMode.track.height*0.9", | |
| "as": "panX" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "panAndZoomMode ? ((datum.panX-datum.scrollX)*datum.tEased+datum.scrollX) : (-(datum.panX-datum.scrollX)*datum.tEased+datum.panX)", | |
| "as": "x" | |
| } | |
| ], | |
| "encode": { | |
| "enter": { | |
| "x": {"signal": "datum.bounds.x1"}, | |
| "y": { | |
| "signal": "datum.bounds.y1+configTogglepanAndZoomMode.track.height/2" | |
| } | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "pan-and-zoom-mode-toggle-outer-arc", | |
| "description": "the circle mark that serves as the the 'toggle handle'", | |
| "from": {"data": "pan-and-zoom-mode-track-rect"}, | |
| "type": "arc", | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "innerRadius": {"value": 0}, | |
| "outerRadius": { | |
| "signal": "configTogglepanAndZoomMode.track.height*0.9" | |
| }, | |
| "startAngle": {"signal": "0"}, | |
| "endAngle": {"signal": "2*PI"}, | |
| "stroke": { | |
| "signal": "configTogglepanAndZoomMode.handle.stroke || '#BBB'" | |
| }, | |
| "strokeWidth": { | |
| "signal": "configTogglepanAndZoomMode.handle.strokeWidth" | |
| }, | |
| "fill": { | |
| "signal": "configTogglepanAndZoomMode.handle.fill || '#fff'" | |
| } | |
| }, | |
| "update": {"x": {"signal": "datum.x"}} | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "pan-and-zoom-mode-toggle-text-scroll", | |
| "description": "the title for the toggle control", | |
| "type": "text", | |
| "from": {"data": "pan-and-zoom-mode-track-rect"}, | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "x": {"signal": "datum.bounds.x1"}, | |
| "dx": {"signal": "-configTogglepanAndZoomMode.label.dx"}, | |
| "y": { | |
| "signal": "datum.bounds.y1+(datum.bounds.y2-datum.bounds.y1)/2" | |
| } | |
| }, | |
| "update": { | |
| "text": {"signal": "'Scroll Mode'"}, | |
| "fontWeight": { | |
| "signal": "panAndZoomMode ? 'regular' : 'bold'" | |
| }, | |
| "baseline": {"value": "middle"}, | |
| "font": { | |
| "signal": "configTogglepanAndZoomMode.label.font" | |
| }, | |
| "fontSize": { | |
| "signal": "configTogglepanAndZoomMode.label.fontSize" | |
| }, | |
| "fontStyle": { | |
| "signal": "configTogglepanAndZoomMode.label.fontStyle" | |
| }, | |
| "align": {"value": "right"}, | |
| "fill": { | |
| "signal": "configTogglepanAndZoomMode.label.fill" | |
| } | |
| } | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "pan-and-zoom-mode-interactive-rect", | |
| "type": "rect", | |
| "from": {"data": "group-pan-and-zoom-mode"}, | |
| "interactive": true, | |
| "encode": { | |
| "enter": { | |
| "y": {"signal": "datum.bounds.y1"}, | |
| "y2": {"signal": "datum.bounds.y2"}, | |
| "x": {"signal": "datum.bounds.x1-5"}, | |
| "x2": {"signal": "datum.bounds.x2"}, | |
| "fill": {"value": "#fff"}, | |
| "fillOpacity": {"value": 0.15}, | |
| "tooltip": { | |
| "signal": "configTogglepanAndZoomMode.tooltip.object" | |
| }, | |
| "cursor": {"value": "pointer"} | |
| }, | |
| "hover": {"fill": {"value": "transparent"}} | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "column-group", | |
| "type": "group", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "0"}, | |
| "y": {"signal": "0"}, | |
| "x2": {"signal": "ganttPosition.x"}, | |
| "height": {"signal": "(ganttDimensions.height)"} | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "column-header-group", | |
| "type": "group", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "configRow.levelIndentWidth"}, | |
| "y": {"signal": "-ganttPosition.dy"}, | |
| "height": {"signal": "ganttPosition.dy"}, | |
| "width": {"signal": "ganttPosition.x"}, | |
| "clip": {"value": true} | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "column-header-label-group", | |
| "type": "group", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "0"}, | |
| "y": {"signal": "1.5"}, | |
| "height": {"signal": "ganttPosition.dy"}, | |
| "width": { | |
| "signal": "ganttPosition.x-configRow.levelIndentWidth" | |
| }, | |
| "clip": {"value": true} | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "column-labels", | |
| "description": "column header text labels", | |
| "type": "text", | |
| "from": {"data": "column-data"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "text": {"field": "label"}, | |
| "x": { | |
| "signal": "datum.align === 'left' ? datum.x : datum.align === 'right' ? datum.x2 : datum.x + (datum.x2-datum.x)/2" | |
| }, | |
| "dx": { | |
| "signal": "datum.align === 'left' ? 1 : datum.align === 'right' ? -3 : 0" | |
| }, | |
| "y": {"signal": "ganttPosition.dy/2"}, | |
| "baseline": {"value": "middle"}, | |
| "align": {"field": "align"}, | |
| "fontSize": {"value": 14}, | |
| "font": {"value": "Segoe UI"}, | |
| "fontWeight": {"value": "600"} | |
| } | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "static-column-row-group", | |
| "type": "group", | |
| "clip": true, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "0"}, | |
| "y": {"signal": "0"}, | |
| "x2": {"signal": "ganttPosition.x"}, | |
| "height": {"signal": "ganttDimensions.height+6"}, | |
| "clip": {"value": true} | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "scrollable-column-row-group", | |
| "type": "group", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "0"}, | |
| "y": {"signal": "scrollY"}, | |
| "x2": {"signal": "ganttPosition.x"}, | |
| "height": {"signal": "ganttDimensions.height"} | |
| } | |
| }, | |
| "data": [ | |
| { | |
| "name": "column-row-data", | |
| "source": "hierarchy-animation", | |
| "transform": [ | |
| { | |
| "type": "formula", | |
| "expr": "!isValid(datum.sourceValues) || !datum.hasChildren ? 0 : datum.sourceValues.isExpanded === 1 ? 90 : 0", | |
| "as": "sourceIndicatorAngle" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "!isValid(datum.targetValues) || !datum.hasChildren ? 0 : datum.targetValues.isExpanded === 1 ? 90 : 0", | |
| "as": "targetIndicatorAngle" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "lerp([datum.sourceIndicatorAngle, datum.targetIndicatorAngle], rowAnimationTEased)", | |
| "as": "indicatorAngle" | |
| } | |
| ] | |
| } | |
| ], | |
| "marks": [ | |
| { | |
| "name": "column-row-background-rect", | |
| "type": "rect", | |
| "from": {"data": "column-row-data"}, | |
| "interactive": {"signal": "rowAnimationTEased === 1"}, | |
| "encode": { | |
| "update": { | |
| "x": {"value": 0}, | |
| "x2": {"signal": "ganttDimensions.width"}, | |
| "y": {"field": "y1"}, | |
| "y2": {"field": "y2"}, | |
| "fill": { | |
| "signal": "isValid(mouseoverNodeDatum) && mouseoverNodeDatum.id === datum.id ? 'rgba(237, 246, 252, 0.5)' : background" | |
| }, | |
| "fillOpacity": {"field": "opacity"}, | |
| "cursor": { | |
| "signal": "datum.hasChildren ? 'pointer' : 'default'" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "column-row-divider-rect", | |
| "type": "rect", | |
| "from": {"data": "column-row-data"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": { | |
| "signal": "configRow.levelIndentWidth*((datum.level)+0.15)" | |
| }, | |
| "x2": {"signal": "ganttDimensions.width"}, | |
| "y": {"field": "y2", "offset": -0.2}, | |
| "y2": {"field": "y2", "offset": 0.2}, | |
| "fill": {"value": "#D8D8DA"}, | |
| "fillOpacity": {"field": "opacity"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "expand-collapse-indicator", | |
| "description": "the arrow indicator for parent nodes that indicate whether the current state of the node is expanded (arrow pointing down) or collapsed (arrow pointing to the right).", | |
| "type": "text", | |
| "from": {"data": "column-row-data"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "text": {"signal": "!datum.hasChildren ? '' : '➤'"}, | |
| "angle": {"field": "indicatorAngle"}, | |
| "x": { | |
| "signal": "datum.hasChildren ? configRow.levelIndentWidth*((datum.level)-0.4) : null" | |
| }, | |
| "y": {"signal": "datum.y1+configRow.rowHeight/2"}, | |
| "fontSize": {"signal": "12"}, | |
| "fill": {"field": "color"}, | |
| "fillOpacity": { | |
| "signal": "isValid(mouseoverNodeDatum) && mouseoverNodeDatum.id === datum.id ? 1 : 0.5" | |
| }, | |
| "align": {"value": "center"}, | |
| "baseline": {"value": "middle"}, | |
| "opacity": {"field": "fullOpacity"}, | |
| "fontWeight": { | |
| "signal": "isValid(mouseoverNodeDatum) && mouseoverNodeDatum.id === datum.id ? 800 : 400" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "row-column-group", | |
| "type": "group", | |
| "from": {"data": "column-data"}, | |
| "interactive": false, | |
| "data": [ | |
| { | |
| "name": "row-text-data", | |
| "source": "hierarchy-animation", | |
| "transform": [ | |
| { | |
| "type": "filter", | |
| "expr": "parent.type === 'text' || parent.type === 'date'" | |
| }, | |
| { | |
| "type": "lookup", | |
| "from": "dataset-formatted", | |
| "key": "id", | |
| "fields": ["id"], | |
| "as": ["originalValues"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "isValid(datum.originalValues) && isValid(parent.field) ? datum.originalValues[parent.field] : null", | |
| "as": "value" | |
| }, | |
| {"type": "filter", "expr": "isValid(datum.value)"}, | |
| { | |
| "type": "formula", | |
| "expr": "isValid(parent.type) && parent.type === 'date' && isValid(parent.format) ? timeFormat(datum.value, parent.format) : datum.value", | |
| "as": "value" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "isValid(parent.type) && parent.type === 'text' && isValid(parent.format) ? format(datum.value, parent.format) : datum.value", | |
| "as": "value" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "parent.index", | |
| "as": "columnIndex" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "parent.align", | |
| "as": "columnAlign" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "parent.x+(datum.columnAlign === 'left' ? 1 : 0)", | |
| "as": "columnX" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "parent.x2+(datum.columnAlign === 'right' ? -3 : 0 )", | |
| "as": "columnX2" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "parent.boldValue", | |
| "as": "columnBoldValue" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "row-percentage-data", | |
| "source": "hierarchy-animation", | |
| "transform": [ | |
| { | |
| "type": "filter", | |
| "expr": "parent.type === 'percentage'" | |
| }, | |
| { | |
| "type": "lookup", | |
| "from": "dataset-formatted", | |
| "key": "id", | |
| "fields": ["id"], | |
| "as": ["originalValues"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "isValid(datum.originalValues) && isValid(parent.field) ? datum.originalValues[parent.field] : null", | |
| "as": "value" | |
| }, | |
| {"type": "filter", "expr": "isValid(datum.value)"}, | |
| { | |
| "type": "formula", | |
| "expr": "isValid(parent.type) && parent.type === 'percentage' && isValid(parent.format) ? format(datum.value, parent.format) : datum.value", | |
| "as": "value" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "parent.index", | |
| "as": "columnIndex" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "parent.align", | |
| "as": "columnAlign" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "parent.x+(datum.columnAlign === 'left' ? 1 : 0)", | |
| "as": "columnX" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "parent.x2+(datum.columnAlign === 'right' ? -3 : 0 )", | |
| "as": "columnX2" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "parent.boldValue", | |
| "as": "columnBoldValue" | |
| } | |
| ] | |
| } | |
| ], | |
| "encode": { | |
| "update": { | |
| "x": { | |
| "field": "x", | |
| "offset": {"signal": "configRow.levelIndentWidth"} | |
| }, | |
| "x2": { | |
| "field": "x2", | |
| "offset": {"signal": "configRow.levelIndentWidth"} | |
| }, | |
| "y": {"signal": "0"}, | |
| "height": {"signal": "ganttDimensions.height"} | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "column-text-cell-group", | |
| "type": "group", | |
| "interactive": false, | |
| "from": {"data": "row-text-data"}, | |
| "encode": { | |
| "update": { | |
| "x": { | |
| "signal": "parent.index === 1 ? configRow.levelIndentWidth*((datum.level)-0.8) : 0" | |
| }, | |
| "width": { | |
| "signal": "datum.columnX2-datum.columnX-(parent.index === 1 ? configRow.levelIndentWidth*((datum.level)-0.8) : 0)" | |
| }, | |
| "y": {"field": "y1"}, | |
| "y2": {"field": "y2"}, | |
| "fill": {"value": "transparent"}, | |
| "clip": {"value": true} | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "text-value", | |
| "type": "text", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "text": {"signal": "parent.value"}, | |
| "x": { | |
| "signal": "parent.columnAlign === 'right' ? parent.columnX2-parent.columnX : parent.columnAlign === 'center' ? (parent.columnX2-parent.columnX)/2 : 0" | |
| }, | |
| "y": {"signal": "configRow.rowHeight/2"}, | |
| "baseline": {"value": "middle"}, | |
| "opacity": {"signal": "parent.fullOpacity"}, | |
| "align": {"signal": "parent.columnAlign"}, | |
| "fontSize": { | |
| "signal": "configColumn.values.fontSize" | |
| }, | |
| "fill": { | |
| "signal": "isValid(mouseoverNodeDatum) && mouseoverNodeDatum.id === datum.id ? '#000' : '#666'" | |
| }, | |
| "fontWeight": { | |
| "signal": "isValid(mouseoverNodeDatum) && mouseoverNodeDatum.id === parent.id || parent.columnBoldValue ? 700 : configColumn.fontWeight" | |
| } | |
| } | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "column-percentage-cell-group", | |
| "type": "group", | |
| "interactive": false, | |
| "from": {"data": "row-percentage-data"}, | |
| "encode": { | |
| "update": { | |
| "x": { | |
| "signal": "parent.index === 1 ? configRow.levelIndentWidth*((datum.level)-0.8) : 0" | |
| }, | |
| "width": { | |
| "signal": "datum.columnX2-datum.columnX-(parent.index === 1 ? configRow.levelIndentWidth*((datum.level)-0.8) : 0)" | |
| }, | |
| "y": {"field": "y1"}, | |
| "y2": {"field": "y2"}, | |
| "fill": {"value": "transparent"}, | |
| "clip": {"value": true} | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "percentage-container", | |
| "type": "rect", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": { | |
| "signal": "(parent.columnX2-parent.columnX)*0.15" | |
| }, | |
| "width": { | |
| "signal": "(parent.columnX2-parent.columnX)*0.7" | |
| }, | |
| "y": {"signal": "configRow.rowHeight*0.175"}, | |
| "y2": {"signal": "configRow.rowHeight*0.825"}, | |
| "cornerRadius": { | |
| "signal": "(configRow.rowHeight)*0.1" | |
| }, | |
| "opacity": {"signal": "parent.fullOpacity"}, | |
| "stroke": {"signal": "parent.color"}, | |
| "strokeOpacity": { | |
| "signal": "isValid(mouseoverNodeDatum) && mouseoverNodeDatum.id === parent.id ? 0.85 : 0.65" | |
| }, | |
| "strokeWidth": {"value": 1.5} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "percentage-bar", | |
| "type": "rect", | |
| "from": {"data": "percentage-container"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "datum.bounds.x1"}, | |
| "width": { | |
| "signal": "(datum.bounds.x2-datum.bounds.x1)*parent.decimalPercentComplete" | |
| }, | |
| "y": { | |
| "signal": "datum.bounds.y1+datum.strokeWidth" | |
| }, | |
| "y2": { | |
| "signal": "datum.bounds.y2-datum.strokeWidth" | |
| }, | |
| "fill": {"field": "stroke"}, | |
| "cornerRadius": {"field": "cornerRadius"}, | |
| "fillOpacity": { | |
| "signal": "datum.strokeOpacity-0.25" | |
| }, | |
| "opacity": {"field": "opacity"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "percentage-text", | |
| "type": "text", | |
| "from": {"data": "percentage-container"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "text": {"signal": "parent.value"}, | |
| "x": { | |
| "signal": "datum.bounds.x1+(datum.bounds.x2-datum.bounds.x1)*0.1" | |
| }, | |
| "y": { | |
| "signal": "datum.bounds.y1+(datum.bounds.y2-datum.bounds.y1)/2" | |
| }, | |
| "fill": { | |
| "signal": "isValid(mouseoverNodeDatum) && mouseoverNodeDatum.id === datum.id ? '#000' : '#666'" | |
| }, | |
| "fontWeight": { | |
| "signal": "isValid(mouseoverNodeDatum) && mouseoverNodeDatum.id === parent.id || parent.columnBoldValue ? 700 : configColumn.fontWeight" | |
| }, | |
| "align": {"value": "left"}, | |
| "baseline": {"value": "middle"}, | |
| "fillOpacity": { | |
| "signal": "parent.fullOpacity" | |
| }, | |
| "opacity": {"field": "opacity"} | |
| } | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "column-click-stopper-rect", | |
| "type": "rect", | |
| "zindex": 9999, | |
| "interactive": {"signal": "isAnimating"}, | |
| "encode": { | |
| "update": { | |
| "x": {"value": 0}, | |
| "y": {"value": 0}, | |
| "width": { | |
| "signal": "isAnimating ? ganttPosition.x : 0" | |
| }, | |
| "height": { | |
| "signal": "ganttPosition.dy + ganttDimensions.height-scrollY" | |
| }, | |
| "cursor": {"value": "default"}, | |
| "fill": {"value": "transparent"} | |
| } | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "gantt-group", | |
| "type": "group", | |
| "interactive": false, | |
| "encode": { | |
| "enter": {"y": {"signal": "-ganttPosition.dy"}}, | |
| "update": { | |
| "x": {"signal": "ganttPosition.x"}, | |
| "width": {"signal": "ganttDimensions.width"}, | |
| "height": { | |
| "signal": "ganttDimensions.height+ganttPosition.dy+horizontalScrollMapHeight*2 + configDateStepSlider.track.height*3" | |
| }, | |
| "fill": {"signal": "background"} | |
| } | |
| }, | |
| "signals": [ | |
| { | |
| "name": "ganttHorizontalScrollMapMouseOver", | |
| "description": "A boolean indicating whether the horizontal scroll map is being hovered over. Updates when hovered and resets when the cursor leaves.", | |
| "value": false, | |
| "on": [ | |
| { | |
| "events": "@gantt-horizontal-scroll-map-interactive-rect:mouseover", | |
| "update": "true" | |
| }, | |
| { | |
| "events": "@gantt-horizontal-scroll-map-interactive-rect:mouseout", | |
| "update": "false" | |
| } | |
| ] | |
| } | |
| ], | |
| "marks": [ | |
| { | |
| "name": "axes-group", | |
| "type": "group", | |
| "encode": { | |
| "update": { | |
| "y": {"signal": "-ganttPosition.dy"}, | |
| "width": {"signal": "ganttDimensions.width"}, | |
| "height": { | |
| "signal": "ganttPosition.dy*2+ganttDimensions.height" | |
| }, | |
| "clip": {"value": true} | |
| } | |
| }, | |
| "data": [ | |
| { | |
| "name": "weekend-dates", | |
| "values": [{}], | |
| "transform": [ | |
| { | |
| "type": "formula", | |
| "expr": "(scale('xScaleGanttTimeSeries', xDomain[0] + msPerDay) - scale('xScaleGanttTimeSeries', xDomain[0]) >= 5) ? timeSequence('day', xDomain[0], xDomain[1]) : []", | |
| "as": "date" | |
| }, | |
| {"type": "flatten", "fields": ["date"]}, | |
| { | |
| "type": "filter", | |
| "expr": "indexof([0,6], day(datum.date)) >= 0" | |
| } | |
| ] | |
| } | |
| ], | |
| "marks": [ | |
| { | |
| "name": "weekendRects", | |
| "type": "rect", | |
| "from": {"data": "weekend-dates"}, | |
| "encode": { | |
| "update": { | |
| "x": {"scale": "xScaleGanttTimeSeries", "field": "date"}, | |
| "x2": { | |
| "scale": "xScaleGanttTimeSeries", | |
| "signal": "+datum.date+msPerDay-1" | |
| }, | |
| "y": {"signal": "2*ganttPosition.dy"}, | |
| "height": {"signal": "ganttDimensions.height"}, | |
| "fill": {"value": "#dce3e8"}, | |
| "fillOpacity": {"value": 0.1} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "secondary-axis-group", | |
| "type": "group", | |
| "encode": { | |
| "update": { | |
| "y": {"signal": "ganttPosition.dy"}, | |
| "width": {"signal": "ganttDimensions.width"}, | |
| "height": {"signal": "adjustedHeight + ganttPosition.dy"} | |
| } | |
| }, | |
| "data": [ | |
| { | |
| "name": "ganttAxisSecondaryLevelLabels", | |
| "values": [{}], | |
| "transform": [ | |
| { | |
| "type": "formula", | |
| "expr": "xCurrentSecondaryUnit === 'hour' ? 'hours' : xCurrentSecondaryUnit", | |
| "as": "secondaryUnit" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "['year', 'month', 'day', 'hours'][indexof(['year', 'month', 'day', 'hours'], datum.secondaryUnit)]", | |
| "as": "intervalUnit" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "timeSequence(datum.intervalUnit, xDomain[0]-(msPerDay*365/([1,12,365,8760][indexof(['year', 'month', 'day', 'hour'], xCurrentSecondaryUnit)])), xDomain[1]+(msPerDay*365/([1,12,365,8760][indexof(['year', 'month', 'day', 'hour'], xCurrentSecondaryUnit)])))", | |
| "as": "startDate" | |
| }, | |
| {"type": "flatten", "fields": ["startDate"]}, | |
| { | |
| "type": "formula", | |
| "expr": "datum.startDate", | |
| "as": "date" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "+datum.startDate", | |
| "as": "startDate" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "timeFormat(datum.startDate, datum.secondaryUnit === 'year' ? '%Y' : datum.secondaryUnit === 'month' ? '%b-%Y' : datum.secondaryUnit === 'day' ? '%d-%b-%Y' : '%d-%b-%Y %H')", | |
| "as": "groupBy" | |
| }, | |
| { | |
| "type": "aggregate", | |
| "ops": ["min", "max"], | |
| "fields": ["startDate", "startDate"], | |
| "groupby": ["secondaryUnit", "groupBy"], | |
| "as": ["startDate", "endDate"] | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["row_number"], | |
| "sort": {"field": "startDate", "order": "ascending"}, | |
| "as": ["index"] | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["lead"], | |
| "fields": ["startDate"], | |
| "sort": {"field": "startDate", "order": "ascending"}, | |
| "as": ["leadStartDate"] | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["row_number"], | |
| "sort": {"field": "startDate", "order": "descending"}, | |
| "as": ["descendingIndex"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(datum.startDate === datum.endDate && isValid(datum.leadStartDate) ? datum.leadStartDate : datum.descendingIndex === 1 ? xDomain[1] : datum.endDate)-1", | |
| "as": "endDate" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(datum.startDate + (datum.endDate-datum.startDate)/2)", | |
| "as": "axisDate" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(scale('xScaleGanttTimeSeries', datum.startDate) + scale('xScaleGanttTimeSeries', datum.endDate)) / 2", | |
| "as": "axisX" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "ganttAxisSecondaryGridlines", | |
| "values": [{}], | |
| "transform": [ | |
| { | |
| "type": "formula", | |
| "expr": "xCurrentSecondaryUnit === 'hour' ? 'hours' : xCurrentSecondaryUnit", | |
| "as": "secondaryUnit" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "['year', 'month', 'day', 'hours'][indexof(['year', 'month', 'day', 'hours'], datum.secondaryUnit)]", | |
| "as": "intervalUnit" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "timeSequence(datum.intervalUnit, xDomain[0]-(msPerDay*365/([1,12,365,8760][indexof(['year', 'month', 'day', 'hour'], xCurrentSecondaryUnit)])), xDomain[1]+(msPerDay*365/([1,12,365,8760][indexof(['year', 'month', 'day', 'hour'], xCurrentSecondaryUnit)])))", | |
| "as": "startDate" | |
| }, | |
| {"type": "flatten", "fields": ["startDate"]}, | |
| { | |
| "type": "formula", | |
| "expr": "datum.startDate", | |
| "as": "axisDate" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "scale('xScaleGanttTimeSeries', datum.axisDate)", | |
| "as": "axisX" | |
| } | |
| ] | |
| } | |
| ], | |
| "marks": [ | |
| { | |
| "name": "secondary-axis-level-dummy-axis-labels", | |
| "type": "text", | |
| "from": {"data": "ganttAxisSecondaryLevelLabels"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"field": "axisX"}, | |
| "y": {"signal": "ganttPosition.dy*0.535"}, | |
| "text": { | |
| "signal": "timeFormat(datum.startDate, (xCurrentSecondaryUnit === 'month' && xCurrentSecondaryBand > 65 ? '%B' : data('ganttTimeSeriesConfigurations')[0].secondaryFormat))" | |
| }, | |
| "fontWeight": {"value": "500"}, | |
| "align": {"value": "center"}, | |
| "baseline": {"value": "middle"}, | |
| "fontSize": {"value": 12}, | |
| "fill": {"value": "#444"}, | |
| "opacity": {"value": 0} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "secondary-level-axis-labels", | |
| "type": "text", | |
| "from": { | |
| "data": "secondary-axis-level-dummy-axis-labels" | |
| }, | |
| "interactive": true, | |
| "encode": { | |
| "update": { | |
| "x": {"field": "x"}, | |
| "y": {"field": "y"}, | |
| "text": { | |
| "signal": "(datum.bounds.x1 > 2.5 && datum.bounds.x2 < ganttDimensions.width-2.5) ? datum.text : null" | |
| }, | |
| "fontWeight": {"field": "fontWeight"}, | |
| "align": {"field": "align"}, | |
| "baseline": {"field": "baseline"}, | |
| "fontSize": {"field": "fontSize"}, | |
| "fill": {"value": "#444"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "secondary-grid-lines", | |
| "type": "rect", | |
| "from": {"data": "ganttAxisSecondaryGridlines"}, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "datum.axisX-0.5"}, | |
| "width": { | |
| "signal": "configGanttGridlines.secondaryAxis.strokeWidth" | |
| }, | |
| "height": { | |
| "signal": "ganttDimensions.height+ganttPosition.dy" | |
| }, | |
| "fill": { | |
| "signal": "configGanttGridlines.secondaryAxis.stroke" | |
| }, | |
| "fillOpacity": { | |
| "signal": "max(1-(length(data('secondary-axis-level-dummy-axis-labels'))/60), 0.25)" | |
| } | |
| } | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "top-level-axis-group", | |
| "type": "group", | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "0"}, | |
| "y": {"signal": "0"}, | |
| "width": { | |
| "signal": "!isValid(xCurrentTopLevelUnit) ? 0 : ganttDimensions.width" | |
| }, | |
| "height": { | |
| "signal": "ganttPosition.dy*2+ganttDimensions.height" | |
| }, | |
| "strokeWidth": {"value": 1} | |
| } | |
| }, | |
| "data": [ | |
| { | |
| "name": "ganttAxisTopLevelLabels", | |
| "values": [{}], | |
| "transform": [ | |
| { | |
| "type": "formula", | |
| "expr": "xCurrentTopLevelUnit === 'hour' ? 'hours' : xCurrentTopLevelUnit", | |
| "as": "topLevelUnit" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "xCurrentSecondaryUnit === 'hour' ? 'hours' : xCurrentSecondaryUnit", | |
| "as": "secondaryUnit" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "['day', 'hours', 'minutes', 'seconds'][indexof(['year', 'month', 'day', 'hours'], datum.secondaryUnit)]", | |
| "as": "intervalUnit" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "timeSequence(datum.intervalUnit, xDomain[0], xDomain[1])", | |
| "as": "startDate" | |
| }, | |
| {"type": "flatten", "fields": ["startDate"]}, | |
| { | |
| "type": "formula", | |
| "expr": "datum.startDate", | |
| "as": "date" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["row_number"], | |
| "sort": {"field": "startDate", "order": "ascending"}, | |
| "as": ["index"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "+datum.startDate", | |
| "as": "startDate" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "timeFormat(datum.startDate, datum.topLevelUnit === 'year' ? '%Y' : datum.topLevelUnit === 'month' ? '%b-%Y' : datum.topLevelUnit === 'day' ? '%d-%b-%Y' : '%d-%b-%Y %H')", | |
| "as": "groupBy" | |
| }, | |
| { | |
| "type": "aggregate", | |
| "ops": ["min", "max"], | |
| "fields": ["startDate", "startDate"], | |
| "groupby": ["topLevelUnit", "groupBy"], | |
| "as": ["startDate", "endDate"] | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["rank"], | |
| "sort": {"field": "startDate", "order": "ascending"}, | |
| "groupby": ["groupBy"], | |
| "as": ["rankWithinGroup"] | |
| }, | |
| { | |
| "type": "filter", | |
| "expr": "datum.rankWithinGroup === 1" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["row_number"], | |
| "sort": {"field": "startDate", "order": "ascending"}, | |
| "as": ["index"] | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["lead"], | |
| "fields": ["startDate"], | |
| "sort": {"field": "startDate", "order": "ascending"}, | |
| "as": ["leadStartDate"] | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["row_number"], | |
| "sort": {"field": "startDate", "order": "descending"}, | |
| "as": ["descendingIndex"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(datum.startDate === datum.endDate && isValid(datum.leadStartDate) ? datum.leadStartDate : datum.descendingIndex === 1 ? xDomain[1] : datum.endDate)-1", | |
| "as": "endDate" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(datum.startDate + (datum.endDate-datum.startDate)/2)", | |
| "as": "axisDate" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(scale('xScaleGanttTimeSeries', datum.startDate) + scale('xScaleGanttTimeSeries', datum.endDate)) / 2", | |
| "as": "axisX" | |
| }, | |
| { | |
| "type": "filter", | |
| "expr": "isValid(datum.topLevelUnit)" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "ganttAxisTopGridlines", | |
| "values": [{}], | |
| "transform": [ | |
| { | |
| "type": "filter", | |
| "expr": "isValid(xCurrentTopLevelUnit)" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "xCurrentTopLevelUnit === 'hour' ? 'hours' : xCurrentTopLevelUnit", | |
| "as": "topLevelUnit" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "['year', 'month', 'day', 'hours'][indexof(['year', 'month', 'day', 'hours'], datum.topLevelUnit)]", | |
| "as": "intervalUnit" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "timeSequence(datum.intervalUnit, xDomain[0]-(msPerDay*365/([1,12,365,8760][indexof(['year', 'month', 'day', 'hour'], xCurrentTopLevelUnit)])), xDomain[1]+(msPerDay*365/([1,12,365,8760][indexof(['year', 'month', 'day', 'hour'], xCurrentTopLevelUnit)])))", | |
| "as": "startDate" | |
| }, | |
| {"type": "flatten", "fields": ["startDate"]}, | |
| { | |
| "type": "formula", | |
| "expr": "datum.startDate", | |
| "as": "axisDate" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "scale('xScaleGanttTimeSeries', datum.axisDate)", | |
| "as": "axisX" | |
| } | |
| ] | |
| } | |
| ], | |
| "marks": [ | |
| { | |
| "name": "top-level-dummy-axis-labels", | |
| "type": "text", | |
| "from": {"data": "ganttAxisTopLevelLabels"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"field": "axisX"}, | |
| "y": {"signal": "ganttPosition.dy/2"}, | |
| "dy": {"signal": "1.5"}, | |
| "text": { | |
| "signal": "timeFormat(datum.startDate, data('ganttTimeSeriesConfigurations')[0].format)" | |
| }, | |
| "fontWeight": {"value": "600"}, | |
| "align": {"value": "center"}, | |
| "baseline": {"value": "middle"}, | |
| "fontSize": {"value": 13}, | |
| "fill": {"value": "#666"}, | |
| "opacity": {"value": 0} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "top-level-axis-labels", | |
| "type": "text", | |
| "from": {"data": "top-level-dummy-axis-labels"}, | |
| "interactive": true, | |
| "encode": { | |
| "update": { | |
| "x": {"field": "x"}, | |
| "y": {"field": "y"}, | |
| "dy": {"field": "dy"}, | |
| "text": { | |
| "signal": "(datum.bounds.x1 > 2.5 && datum.bounds.x2 < ganttDimensions.width-2.5) ? datum.text : null" | |
| }, | |
| "fontWeight": {"field": "fontWeight"}, | |
| "align": {"field": "align"}, | |
| "baseline": {"field": "baseline"}, | |
| "fill": {"field": "fill"}, | |
| "fontSize": {"field": "fontSize"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "top-grid-lines", | |
| "type": "rect", | |
| "from": {"data": "ganttAxisTopGridlines"}, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "datum.axisX-0.5"}, | |
| "width": { | |
| "signal": "inrange(datum.axisX, [-1.5,1.5]) || inrange(datum.axisX, [ganttDimensions.width-1.5, ganttDimensions.width+1.5]) ? 0 : configGanttGridlines.topAxis.strokeWidth" | |
| }, | |
| "height": { | |
| "signal": "ganttDimensions.height+ganttPosition.dy*2" | |
| }, | |
| "fill": { | |
| "signal": "configGanttGridlines.topAxis.stroke" | |
| } | |
| } | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "gantt-canvas-static-group", | |
| "type": "group", | |
| "clip": true, | |
| "encode": { | |
| "update": { | |
| "y": {"signal": "ganttPosition.dy"}, | |
| "width": {"signal": "ganttDimensions.width"}, | |
| "height": {"signal": "ganttDimensions.height"}, | |
| "clip": {"value": true} | |
| } | |
| }, | |
| "signals": [{"name": "today", "update": "now()"}], | |
| "marks": [ | |
| { | |
| "name": "rect-gantt-background", | |
| "type": "rect", | |
| "encode": { | |
| "update": { | |
| "width": {"signal": "ganttDimensions.width"}, | |
| "height": {"signal": "adjustedHeight"}, | |
| "fill": {"value": "transparent"}, | |
| "cursor": { | |
| "signal": "!panAndZoomMode ? 'default' : panning ? 'grabbing' : 'grab'" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-canvas-group", | |
| "type": "group", | |
| "encode": {"update": {"y": {"signal": "scrollY"}}}, | |
| "data": [ | |
| { | |
| "name": "gantt-rect-data", | |
| "source": "hierarchy-animation", | |
| "transform": [ | |
| { | |
| "type": "filter", | |
| "expr": "toNumber(datum.endDate) >= xDomain[0] && toNumber(datum.startDate) <= xDomain[1]" | |
| }, | |
| { | |
| "type": "filter", | |
| "expr": "datum.startDate !== datum.endDate" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "gantt-milestone-data", | |
| "source": "hierarchy-animation", | |
| "transform": [ | |
| { | |
| "type": "filter", | |
| "expr": "toNumber(datum.endDate) >= xDomain[0] && toNumber(datum.startDate) <= xDomain[1]" | |
| }, | |
| { | |
| "type": "filter", | |
| "expr": "datum.startDate === datum.endDate" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "gantt-label-data", | |
| "source": "hierarchy-animation", | |
| "transform": [ | |
| { | |
| "type": "filter", | |
| "expr": "toNumber(datum.endDate) >= xDomain[0] && toNumber(datum.startDate) <= xDomain[1]" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.index === 1 || datum.index === 2", | |
| "as": "isATopRow" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "scale('xScaleGanttTimeSeries', today)", | |
| "as": "xToday" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "abs(datum.xToday-datum.x1)", | |
| "as": "startTodayProximity" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "abs(datum.xToday-datum.x2)", | |
| "as": "endTodayProximity" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "ganttDimensions.width - datum.x2", | |
| "as": "ganttWidthProximity" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "isValid(datum.label) ? length(datum.label)*configGantt.label.fontSize/1.9 : 0", | |
| "as": "approxLabelWidth" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.ganttWidthProximity < datum.approxLabelWidth || (indexof([1,2], datum.index) >= 0 && datum.endTodayProximity < datum.approxLabelWidth) ? 'start' : 'end'", | |
| "as": "anchor" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.anchor === 'start' ? 'right' : 'left'", | |
| "as": "align" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.anchor === 'start' ? datum.x1-configRow.rowHeight/2 : datum.x2+configRow.rowHeight/2", | |
| "as": "x" | |
| } | |
| ] | |
| } | |
| ], | |
| "marks": [ | |
| { | |
| "name": "today-line-group", | |
| "type": "group", | |
| "marks": [ | |
| { | |
| "name": "today-line-rect", | |
| "description": "the vertical dashed line that indicates today's date in the gantt", | |
| "type": "rect", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": { | |
| "signal": "scale('xScaleGanttTimeSeries', today)" | |
| }, | |
| "x2": { | |
| "signal": "scale('xScaleGanttTimeSeries', today)" | |
| }, | |
| "y": {"signal": "0"}, | |
| "y2": { | |
| "signal": "length(data('hierarchy-initial'))*configRow.rowHeight" | |
| }, | |
| "fill": {"value": "transparent"}, | |
| "stroke": { | |
| "signal": "configGantt.todayLine.stroke" | |
| }, | |
| "strokeWidth": { | |
| "signal": "configGantt.todayLine.strokeWidth" | |
| }, | |
| "strokeDash": { | |
| "signal": "configGantt.todayLine.strokeDash" | |
| }, | |
| "strokeDashOffset": { | |
| "signal": "configGantt.todayLine.strokeDashOffset" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "today-line-text-background", | |
| "type": "text", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "text": { | |
| "signal": "datum.labelOpacity === 0 ? '' : 'Today'" | |
| }, | |
| "x": { | |
| "signal": "scale('xScaleGanttTimeSeries', today)" | |
| }, | |
| "dx": {"signal": "5"}, | |
| "y": {"signal": "-scrollY"}, | |
| "dy": {"signal": "-2.5"}, | |
| "angle": {"value": 90}, | |
| "baseline": {"value": "bottom"}, | |
| "font": {"signal": "configGantt.label.font"}, | |
| "fontSize": { | |
| "signal": "configGantt.label.fontSize" | |
| }, | |
| "fontWeight": { | |
| "signal": "configGantt.label.fontWeight" | |
| }, | |
| "fill": {"signal": "background"}, | |
| "stroke": {"signal": "background"}, | |
| "strokeWidth": {"value": 4}, | |
| "opacity": { | |
| "signal": "!isValid(data('hierarchy-animation')[0]) || !isValid(data('hierarchy-animation')[0].x1) || !isValid(data('hierarchy-animation')[1]) || !isValid(data('hierarchy-animation')[1].x1) ? 0 : !inrange(scale('xScaleGanttTimeSeries', today), [data('hierarchy-animation')[0].x1-10, data('hierarchy-animation')[0].x2+10]) && !inrange(scale('xScaleGanttTimeSeries', today), [data('hierarchy-animation')[1].x1-10, data('hierarchy-animation')[1].x2+10]) ? 1 : 0" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "today-line-text", | |
| "description": "the text mark for the today line", | |
| "type": "text", | |
| "from": {"data": "today-line-text-background"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "text": {"field": "text"}, | |
| "x": {"field": "x"}, | |
| "dx": {"field": "dx"}, | |
| "y": {"field": "y"}, | |
| "dy": {"field": "dy"}, | |
| "angle": {"field": "angle"}, | |
| "baseline": {"field": "baseline"}, | |
| "font": {"field": "font"}, | |
| "fontSize": {"field": "fontSize"}, | |
| "fontWeight": {"field": "fontWeight"}, | |
| "fill": {"signal": "configGantt.label.fill"}, | |
| "opacity": {"field": "opacity"} | |
| } | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "dependency-links-group", | |
| "description": "group that makes up the dependency marks in the Gantt (i.e. arrowed lines)", | |
| "type": "group", | |
| "interactive": false, | |
| "from": { | |
| "facet": { | |
| "name": "dependency-links-facet", | |
| "data": "dependency-links", | |
| "groupby": [ | |
| "sourceId", | |
| "targetId", | |
| "targetTargetValues" | |
| ] | |
| } | |
| }, | |
| "data": [ | |
| { | |
| "name": "transformed-dependency-links-facet", | |
| "source": "dependency-links-facet", | |
| "transform": [ | |
| { | |
| "type": "formula", | |
| "expr": "isValid(mouseoverNodeDatum) && (datum.sourceId === mouseoverNodeDatum.id || datum.targetId === mouseoverNodeDatum.id)", | |
| "as": "hasMouseOver" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.hasMouseOver && (datum.sourceId === mouseoverNodeDatum.id || datum.targetId === mouseoverNodeDatum.id)", | |
| "as": "isMousedOver" | |
| } | |
| ] | |
| } | |
| ], | |
| "marks": [ | |
| { | |
| "name": "dependency-links-line", | |
| "description": "the dependency line mark", | |
| "type": "line", | |
| "from": { | |
| "data": "transformed-dependency-links-facet" | |
| }, | |
| "encode": { | |
| "update": { | |
| "x": { | |
| "signal": "!isValid(datum) || !isValid(datum.xy) ? 0 : datum.xy.x" | |
| }, | |
| "y": { | |
| "signal": "!isValid(datum) || !isValid(datum.xy) ? 0 : datum.xy.y" | |
| }, | |
| "stroke": { | |
| "signal": "datum.isMousedOver ? '#111' : '#666'" | |
| }, | |
| "opacity": {"field": "opacity"}, | |
| "strokeWidth": { | |
| "signal": "datum.isMousedOver ? 2.5 : 1.5" | |
| }, | |
| "interpolate": {"value": "linear"}, | |
| "strokeJoin": {"value": "bevel"}, | |
| "strokeCap": {"value": "round"}, | |
| "defined": {"value": true} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "dependency-links-arrow", | |
| "description": "the dependency arrow mark", | |
| "type": "text", | |
| "from": { | |
| "data": "transformed-dependency-links-facet" | |
| }, | |
| "encode": { | |
| "update": { | |
| "x": { | |
| "signal": "!isValid(datum) || !isValid(datum.xy) ? 0 : datum.xy.x", | |
| "offset": { | |
| "signal": "datum.targetTargetValues.startDate === datum.targetTargetValues.endDate ? -2.5 : 1" | |
| } | |
| }, | |
| "y": { | |
| "signal": "!isValid(datum) || !isValid(datum.xy) ? 0 : datum.xy.y" | |
| }, | |
| "dy": {"value": 0.999}, | |
| "text": { | |
| "signal": "!isValid(datum) || !isValid(datum.sort) ? null : datum.sort === 6 ? '⮞' : null" | |
| }, | |
| "fillOpacity": { | |
| "signal": "datum.hasMouseOver ? datum.isMousedOver ? 1 : 0 : 1" | |
| }, | |
| "opacity": {"field": "opacity"}, | |
| "fontSize": {"value": 14}, | |
| "align": {"value": "right"}, | |
| "baseline": {"value": "middle"}, | |
| "fill": { | |
| "signal": "datum.hasMouseOver ? datum.isMousedOver ? '#111' : '#666' : '#666'" | |
| } | |
| } | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "gantt-row-clickable-rect", | |
| "description": "the invisible rect used for interactions for each row that spans across the visual horizontally", | |
| "type": "rect", | |
| "from": {"data": "gantt-rect-data"}, | |
| "interactive": { | |
| "signal": "rowAnimationTEased === 1 && !panning" | |
| }, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "panAndZoomMode ? datum.x1 : 0"}, | |
| "x2": { | |
| "signal": "panAndZoomMode ? datum.x2 : ganttDimensions.width" | |
| }, | |
| "y": {"field": "y1"}, | |
| "y2": {"field": "y2"}, | |
| "fill": {"value": "transparent"}, | |
| "zindex": { | |
| "signal": "datum.opacity === 0 ? -9999 : 1" | |
| }, | |
| "cursor": { | |
| "signal": "!isAnimating && datum.hasChildren ? 'pointer' : 'default'" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-row-highlight-rect", | |
| "type": "rect", | |
| "from": {"data": "hierarchy-animation"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "0"}, | |
| "x2": {"signal": "ganttDimensions.width"}, | |
| "y": {"field": "y1"}, | |
| "y2": {"field": "y2"}, | |
| "fill": {"value": "rgba(237, 246, 252, 0.5)"}, | |
| "opacity": { | |
| "signal": "isValid(mouseoverNodeDatum) && mouseoverNodeDatum.id === datum.id ? datum.opacity : 0" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-row-milestone-clickable-rect", | |
| "type": "rect", | |
| "from": {"data": "gantt-milestone-data"}, | |
| "interactive": true, | |
| "encode": { | |
| "update": { | |
| "x": { | |
| "signal": "panAndZoomMode ? datum.x1 : 0", | |
| "offset": {"signal": "-configRow.rowHeight"} | |
| }, | |
| "x2": { | |
| "signal": "panAndZoomMode ? datum.x2 : ganttDimensions.width", | |
| "offset": {"signal": "configRow.rowHeight"} | |
| }, | |
| "y": {"field": "y1"}, | |
| "y2": {"field": "y2"}, | |
| "fill": {"value": "transparent"}, | |
| "zindex": { | |
| "signal": "datum.opacity === 0 ? -9999 : 1" | |
| }, | |
| "cursor": { | |
| "signal": "!isAnimating && datum.hasChildren ? 'pointer' : 'default'" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-click-stopper-rect", | |
| "type": "rect", | |
| "zindex": 9999, | |
| "interactive": {"signal": "isAnimating"}, | |
| "encode": { | |
| "update": { | |
| "x": {"value": 0}, | |
| "y": {"value": 0}, | |
| "width": {"signal": "isAnimating ? width : 0"}, | |
| "height": {"signal": "isAnimating ? height : 0"}, | |
| "cursor": {"value": "default"}, | |
| "fill": {"value": "transparent"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-row-rect-background", | |
| "description": "the invisible rect used for interactions for each row that spans across the visual horizontally", | |
| "type": "rect", | |
| "from": {"data": "gantt-rect-data"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"field": "x1"}, | |
| "x2": {"field": "x2"}, | |
| "y": {"field": "y1WithPadding"}, | |
| "y2": {"field": "y2WithPaddingParent"}, | |
| "fill": {"signal": "background"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-row-milestone-background", | |
| "type": "symbol", | |
| "from": {"data": "gantt-milestone-data"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"field": "x1", "offset": {"signal": "0"}}, | |
| "x2": {"field": "x2"}, | |
| "y": { | |
| "field": "y1WithPadding", | |
| "offset": {"signal": "+configRow.rowHeight*0.15"} | |
| }, | |
| "y2": {"field": "y2WithPaddingParent"}, | |
| "size": { | |
| "signal": "!inrange(datum.endDate,xDomain) ? 0 : 8*configRow.rowHeight" | |
| }, | |
| "shape": {"value": "diamond"}, | |
| "fill": {"signal": "background"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-row-rect-container", | |
| "type": "rect", | |
| "from": {"data": "gantt-rect-data"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"field": "x1"}, | |
| "x2": {"field": "x2"}, | |
| "y": {"field": "y1WithPadding"}, | |
| "y2": {"field": "y2WithPaddingParent"}, | |
| "fill": {"field": "color"}, | |
| "fillOpacity": {"field": "opacity"}, | |
| "stroke": {"field": "color"}, | |
| "strokeWidth": {"value": 0.5}, | |
| "strokeOpacity": { | |
| "signal": "datum.hasChildren ? datum.fullOpacity : 0" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-row-milestone-symbol", | |
| "type": "symbol", | |
| "from": {"data": "gantt-milestone-data"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"field": "x1", "offset": {"signal": "0"}}, | |
| "x2": {"field": "x2"}, | |
| "y": { | |
| "field": "y1WithPadding", | |
| "offset": {"signal": "+configRow.rowHeight*0.15"} | |
| }, | |
| "y2": {"field": "y2WithPaddingParent"}, | |
| "size": { | |
| "signal": "!inrange(datum.endDate,xDomain) ? 0 : 8*configRow.rowHeight" | |
| }, | |
| "shape": {"value": "diamond"}, | |
| "fill": {"field": "color"}, | |
| "opacity": { | |
| "signal": "(datum.decimalPercentComplete === 1 ? 1.5 : 1) * datum.opacity" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-row-rect-progress", | |
| "type": "rect", | |
| "from": {"data": "gantt-rect-data"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"field": "x1"}, | |
| "x2": {"field": "x2Progress"}, | |
| "y": {"field": "y1WithPadding"}, | |
| "y2": {"field": "y2WithPaddingParent"}, | |
| "fill": {"field": "color"}, | |
| "opacity": {"field": "opacity"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-row-rect-parent-left-border", | |
| "type": "rect", | |
| "from": {"data": "gantt-rect-data"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"field": "x1", "offset": -0.5}, | |
| "width": { | |
| "signal": "inrange(datum.startDate,xDomain) && datum.hasChildren ? 2 : 0" | |
| }, | |
| "y": {"field": "y1WithPadding"}, | |
| "y2": {"field": "y2WithPadding"}, | |
| "fill": {"field": "color"}, | |
| "opacity": {"field": "fullOpacity"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-row-rect-parent-right-border", | |
| "type": "rect", | |
| "from": {"data": "gantt-rect-data"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"field": "x2", "offset": -0.5}, | |
| "width": { | |
| "signal": "inrange(datum.endDate,xDomain) && datum.hasChildren ? 2 : 0" | |
| }, | |
| "y": {"field": "y1WithPadding"}, | |
| "y2": {"field": "y2WithPadding"}, | |
| "fill": {"field": "color"}, | |
| "opacity": {"field": "fullOpacity"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-row-label-background", | |
| "type": "text", | |
| "from": {"data": "gantt-label-data"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "text": {"field": "label"}, | |
| "baseline": {"value": "middle"}, | |
| "align": {"field": "align"}, | |
| "x": {"field": "x"}, | |
| "y": { | |
| "field": "y1", | |
| "offset": {"signal": "configRow.rowHeight/2"} | |
| }, | |
| "fill": { | |
| "signal": "isValid(mouseoverNodeDatum) && mouseoverNodeDatum.id === datum.id ? 'rgba(237, 246, 252, 0.5)' : background" | |
| }, | |
| "stroke": { | |
| "signal": "isValid(mouseoverNodeDatum) && mouseoverNodeDatum.id === datum.id ? 'rgba(237, 246, 252, 0.5)' : background" | |
| }, | |
| "strokeWidth": {"value": 2}, | |
| "opacity": {"signal": "datum.fullOpacity"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-row-label", | |
| "type": "text", | |
| "from": {"data": "gantt-row-label-background"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "text": {"field": "text"}, | |
| "baseline": {"field": "baseline"}, | |
| "align": {"field": "align"}, | |
| "x": {"field": "x"}, | |
| "y": {"field": "y"}, | |
| "fill": { | |
| "signal": "isValid(mouseoverNodeDatum) && mouseoverNodeDatum.id === datum.datum.id ? '#333' : '#777'" | |
| }, | |
| "fontWeight": { | |
| "signal": "isValid(mouseoverNodeDatum) && mouseoverNodeDatum.id === datum.datum.id ? 600 : 400" | |
| }, | |
| "opacity": {"signal": "datum.datum.fullOpacity"} | |
| } | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "group-gantt-horizontal-scroll-map", | |
| "type": "group", | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "0"}, | |
| "y": { | |
| "signal": "ganttPosition.y+ganttPosition.dy+ganttDimensions.height+7.5" | |
| } | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "track", | |
| "type": "rect", | |
| "encode": { | |
| "update": { | |
| "width": {"signal": "ganttDimensions.width"}, | |
| "y": {"signal": "horizontalScrollMapHeight/2"}, | |
| "height": {"value": 0.3}, | |
| "fill": {"value": "#999"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "rect-domain-max-brush", | |
| "type": "rect", | |
| "encode": { | |
| "update": { | |
| "x": { | |
| "signal": "scale('xScaleGanttTimeSeriesMax', xDomain[0])", | |
| "offset": { | |
| "signal": "(ganttHorizontalScrollMapMouseDown ? 0 : 0) -0.5" | |
| } | |
| }, | |
| "x2": { | |
| "signal": "scale('xScaleGanttTimeSeriesMax', xDomain[1])", | |
| "offset": { | |
| "signal": "(ganttHorizontalScrollMapMouseDown ? 0 : 0) + 0.5" | |
| } | |
| }, | |
| "height": {"signal": "horizontalScrollMapHeight"}, | |
| "fill": { | |
| "signal": "ganttHorizontalScrollMapMouseOver ? '#f3f7fa' : '#fff'" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "time-series-rect", | |
| "type": "rect", | |
| "encode": { | |
| "update": { | |
| "x": { | |
| "signal": "scale('xScaleGanttTimeSeriesMax', xDomainInitial[0])" | |
| }, | |
| "x2": { | |
| "signal": "scale('xScaleGanttTimeSeriesMax', xDomainInitial[1])" | |
| }, | |
| "y": {"signal": "horizontalScrollMapHeight/2"}, | |
| "strokeWidth": {"signal": "horizontalScrollMapHeight/4"}, | |
| "stroke": {"value": "#CCC"}, | |
| "fill": {"value": "transparent"}, | |
| "cursor": { | |
| "signal": "(ganttHorizontalScrollMapMouseOver || ganttHorizontalScrollMapMouseDown) && scale('xScaleGanttTimeSeries', xDomain[0]) !== range('xScaleGanttTimeSeries')[0] ? 'pointer' : 'default'" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "rect-domain-min-tick-tick", | |
| "from": {"data": "rect-domain-max-brush"}, | |
| "type": "text", | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "datum.bounds.x1-2"}, | |
| "y": { | |
| "signal": "datum.bounds.y1+(datum.bounds.y2-datum.bounds.y1)/2" | |
| }, | |
| "text": {"signal": "'|'"}, | |
| "baseline": {"value": "middle"}, | |
| "fill": {"signal": "'#999'"}, | |
| "fontWeight": {"value": "600"}, | |
| "fontSize": {"value": 12} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "rect-domain-max-tick", | |
| "from": {"data": "rect-domain-max-brush"}, | |
| "type": "text", | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "datum.bounds.x2-1"}, | |
| "y": { | |
| "signal": "datum.bounds.y1+(datum.bounds.y2-datum.bounds.y1)/2" | |
| }, | |
| "text": {"signal": "'|'"}, | |
| "baseline": {"value": "middle"}, | |
| "fill": {"signal": "'#999'"}, | |
| "fontWeight": {"value": "600"}, | |
| "fontSize": {"value": 12} | |
| } | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "group-date-granularity", | |
| "description": "the group of marks that makes up the slider control", | |
| "type": "group", | |
| "from": {"data": "gantt-canvas-static-group"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "ganttDimensions.width"}, | |
| "y": { | |
| "signal": "datum.bounds.y2+configDateStepSlider.track.height*2+10" | |
| }, | |
| "height": {"signal": "configDateStepSlider.track.height*2"} | |
| } | |
| }, | |
| "signals": [ | |
| { | |
| "name": "granularityResetMouseover", | |
| "description": "A boolean indicating whether the reset granularity button is being hovered over.", | |
| "value": false, | |
| "on": [ | |
| { | |
| "events": "@granularity-reset-interactive-rect:mouseover", | |
| "update": "true" | |
| }, | |
| { | |
| "events": "@granularity-reset-interactive-rect:mouseout", | |
| "update": "false" | |
| } | |
| ] | |
| } | |
| ], | |
| "marks": [ | |
| { | |
| "name": "group-granularity-reset", | |
| "type": "group", | |
| "marks": [ | |
| { | |
| "name": "text-date-granularity-reset-icon", | |
| "type": "symbol", | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "-15"}, | |
| "y": {"signal": "0"}, | |
| "shape": { | |
| "value": "'M75 75L41 41C25.9 25.9 0 36.6 0 57.9L0 168c0 13.3 10.7 24 24 24l110.1 0c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24l0 104c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65 0-94.1c0-13.3-10.7-24-24-24z'" | |
| }, | |
| "size": {"signal": "0.0025"}, | |
| "fill": { | |
| "signal": "granularityResetMouseover ? '#333' : '#888'" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "text-date-granularity-reset-label", | |
| "type": "text", | |
| "from": {"data": "text-date-granularity-reset-icon"}, | |
| "encode": { | |
| "enter": { | |
| "x": { | |
| "signal": "datum.bounds.x1-configDateStepSlider.innerPadding/2" | |
| } | |
| }, | |
| "update": { | |
| "y": { | |
| "signal": "(datum.bounds.y2-datum.bounds.y1)/2+1" | |
| }, | |
| "align": {"value": "right"}, | |
| "baseline": {"value": "middle"}, | |
| "text": {"signal": "'Reset'"}, | |
| "fontSize": { | |
| "signal": "configDateStepSlider.label.fontSize" | |
| }, | |
| "fill": { | |
| "signal": "granularityResetMouseover ? '#222' : '#666'" | |
| } | |
| } | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "group-date-granularity-slider", | |
| "type": "group", | |
| "marks": [ | |
| { | |
| "name": "text-date-granularity-track-rect", | |
| "from": {"data": "group-granularity-reset"}, | |
| "description": "the track for the slider control", | |
| "type": "rect", | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "x": { | |
| "signal": "datum.bounds.x1-configDateStepSlider.innerPadding-configDateStepSlider.track.width" | |
| } | |
| }, | |
| "update": { | |
| "y": { | |
| "signal": "(datum.bounds.y2-datum.bounds.y1)/2-configDateStepSlider.track.height/4" | |
| }, | |
| "height": { | |
| "signal": "configDateStepSlider.track.height" | |
| }, | |
| "width": { | |
| "signal": "configDateStepSlider.track.width" | |
| }, | |
| "cornerRadius": { | |
| "signal": "configDateStepSlider.track.cornerRadius" | |
| }, | |
| "fill": {"signal": "configDateStepSlider.track.fill"}, | |
| "stroke": { | |
| "signal": "configDateStepSlider.track.stroke" | |
| }, | |
| "strokeWidth": { | |
| "signal": "configDateStepSlider.track.strokeWidth" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "slider-percentage-rect", | |
| "description": "the rect that indicates the slider percentage", | |
| "type": "rect", | |
| "from": {"data": "text-date-granularity-track-rect"}, | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "height": { | |
| "signal": "configDateStepSlider.track.height" | |
| }, | |
| "x": {"signal": "datum.bounds.x1"}, | |
| "y": {"signal": "datum.bounds.y1+1"}, | |
| "width": { | |
| "signal": "xDomainZoomPercentage*configDateStepSlider.track.width" | |
| }, | |
| "cornerRadius": { | |
| "signal": "configDateStepSlider.track.cornerRadius" | |
| }, | |
| "fill": { | |
| "signal": "configDateStepSlider.progress.fill" | |
| }, | |
| "fillOpacity": { | |
| "signal": "configDateStepSlider.progress.fillOpacity" | |
| }, | |
| "stroke": { | |
| "signal": "configDateStepSlider.track.stroke" | |
| }, | |
| "strokeWidth": { | |
| "signal": "configDateStepSlider.track.strokeWidth" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "handles-outer-arc", | |
| "description": "the outer circle mark that serves as the the 'slider handle'", | |
| "from": {"data": "slider-percentage-rect"}, | |
| "type": "arc", | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "y": { | |
| "signal": "datum.bounds.y1+(configDateStepSlider.track.height)/2" | |
| }, | |
| "innerRadius": {"value": 0}, | |
| "outerRadius": { | |
| "signal": "configDateStepSlider.track.height*0.9" | |
| }, | |
| "startAngle": {"signal": "0"}, | |
| "endAngle": {"signal": "2*PI"}, | |
| "stroke": { | |
| "signal": "configDateStepSlider.handle.outerStroke || '#BBB'" | |
| }, | |
| "strokeWidth": { | |
| "signal": "configDateStepSlider.handle.outerStrokeWidth" | |
| }, | |
| "fill": { | |
| "signal": "configDateStepSlider.handle.outerFill || '#fff'" | |
| } | |
| }, | |
| "update": {"x": {"signal": "datum.bounds.x2"}} | |
| } | |
| }, | |
| { | |
| "name": "handles-inner-arc", | |
| "description": "the inner circle mark that serves as the the 'slider handle'", | |
| "type": "arc", | |
| "from": {"data": "handles-outer-arc"}, | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "y": {"signal": "datum.y"}, | |
| "innerRadius": {"value": 0}, | |
| "outerRadius": {"signal": "1"}, | |
| "startAngle": {"signal": "0"}, | |
| "endAngle": {"signal": "2*PI"}, | |
| "stroke": { | |
| "signal": "configDateStepSlider.handle.innerStroke" | |
| }, | |
| "fill": { | |
| "signal": "configDateStepSlider.handle.innerFill || '#999'" | |
| } | |
| }, | |
| "update": {"x": {"signal": "datum.x"}} | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "text-date-unit-label", | |
| "type": "text", | |
| "from": {"data": "group-date-granularity-slider"}, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "-ganttDimensions.width"}, | |
| "baseline": {"value": "middle"}, | |
| "text": { | |
| "signal": "(!isValid(xCurrentSecondaryUnit) ? 'Year' : upper(slice(xCurrentSecondaryUnit, 0,1)) + slice(xCurrentSecondaryUnit, -length(xCurrentSecondaryUnit)+1)) + ' View'" | |
| }, | |
| "fill": {"value": "#666"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "label-date-granularity-slider-text", | |
| "description": "the title for the slider control", | |
| "from": {"data": "group-date-granularity-slider"}, | |
| "type": "text", | |
| "interactive": false, | |
| "encode": { | |
| "enter": { | |
| "x": {"signal": "datum.bounds.x1"}, | |
| "dx": { | |
| "signal": "-configDateStepSlider.track.cornerRadius*2" | |
| } | |
| }, | |
| "update": { | |
| "y": { | |
| "signal": "datum.bounds.y1+(datum.bounds.y2-datum.bounds.y1)/2" | |
| }, | |
| "text": { | |
| "signal": "configDateStepSlider.label.text || ''" | |
| }, | |
| "baseline": {"value": "middle"}, | |
| "font": {"signal": "configDateStepSlider.label.font"}, | |
| "fontSize": { | |
| "signal": "configDateStepSlider.label.fontSize" | |
| }, | |
| "fontStyle": { | |
| "signal": "configDateStepSlider.label.fontStyle" | |
| }, | |
| "align": {"value": "right"}, | |
| "fill": {"signal": "configDateStepSlider.label.fill"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "granularity-reset-interactive-rect", | |
| "type": "rect", | |
| "from": {"data": "group-granularity-reset"}, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "datum.bounds.x1-5"}, | |
| "x2": {"signal": "datum.bounds.x2+5"}, | |
| "y": {"signal": "datum.bounds.y1-5"}, | |
| "y2": {"signal": "datum.bounds.y2+5"}, | |
| "fillOpacity": {"value": 0}, | |
| "cursor": { | |
| "signal": "granularityResetMouseover ? 'pointer' : 'default'" | |
| }, | |
| "tooltip": { | |
| "signal": "configDateStepSlider.reset.tooltipText" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "granularity-slider-interactive-rect", | |
| "type": "rect", | |
| "from": {"data": "group-date-granularity-slider"}, | |
| "encode": { | |
| "update": { | |
| "x": {"signal": "datum.bounds.x1"}, | |
| "x2": {"signal": "datum.bounds.x2"}, | |
| "y": {"signal": "datum.bounds.y1-5"}, | |
| "y2": {"signal": "datum.bounds.y2+5"}, | |
| "fillOpacity": {"value": 0}, | |
| "cursor": {"value": "pointer"}, | |
| "tooltip": { | |
| "signal": "granularitySliderMouseDown ? '' : 'Reset date granularity'" | |
| } | |
| } | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "gantt-horizontal-scroll-map-interactive-rect", | |
| "from": {"data": "group-date-granularity"}, | |
| "type": "rect", | |
| "encode": { | |
| "update": { | |
| "tooltip": { | |
| "signal": "ganttHorizontalScrollMapMouseDown ? '' : 'Horizontal scroll'" | |
| }, | |
| "x": { | |
| "signal": "ganttHorizontalScrollMapMouseDown ? -ganttPosition.x : 0" | |
| }, | |
| "y": { | |
| "signal": "ganttHorizontalScrollMapMouseDown ? -ganttPosition.y-ganttPosition.dy : datum.bounds.y1 - configDateStepSlider.track.height*3" | |
| }, | |
| "x2": {"signal": "panAndZoomMode ? 0 : datum.bounds.x2"}, | |
| "height": { | |
| "signal": "(datum.bounds.y2-datum.bounds.y1) + (ganttHorizontalScrollMapMouseDown ? ganttDimensions.height + ganttPosition.dy + (datum.bounds.y2-datum.bounds.y1) +configDateStepSlider.track.height : 0)" | |
| }, | |
| "fillOpacity": {"value": 0}, | |
| "cursor": { | |
| "signal": "ganttHorizontalScrollMapMouseOver ? 'pointer' : 'default'" | |
| } | |
| } | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "vertical-scrollbar-group", | |
| "description": "the group of marks that make up the vertical scrollbar", | |
| "type": "group", | |
| "interactive": true, | |
| "encode": { | |
| "update": { | |
| "y": { | |
| "signal": "verticalScrollbarMouseDown ? -2*ganttPosition.dy: 0" | |
| }, | |
| "x": { | |
| "signal": "verticalScrollbarMouseDown ? 0 : ganttPosition.x+ganttDimensions.width" | |
| }, | |
| "width": { | |
| "signal": "actualHeight > adjustedHeight && configVerticalScrollbar.enabled ? verticalScrollbarMouseDown ? ganttPosition.x+ganttDimensions.width+configVerticalScrollbar.track.width : configVerticalScrollbar.track.width : 0" | |
| }, | |
| "height": { | |
| "signal": "actualHeight > adjustedHeight && configVerticalScrollbar.enabled ? ((verticalScrollbarMouseDown ? 4*ganttPosition.dy : 0) + ganttDimensions.height) : 0" | |
| }, | |
| "fill": {"value": "transparent"}, | |
| "cursor": {"signal": "panAndZoomMode ? 'default' : 'pointer'"}, | |
| "zindex": {"value": 999} | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "rect_verticalScrollbar_track", | |
| "description": "the track for the scrollbar", | |
| "type": "rect", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "y": { | |
| "signal": "verticalScrollbarMouseDown ? 2*ganttPosition.dy: 0" | |
| }, | |
| "x": { | |
| "signal": "verticalScrollbarMouseDown ? ganttPosition.x+ganttDimensions.width : 0" | |
| }, | |
| "width": { | |
| "signal": "configVerticalScrollbar.enabled ? configVerticalScrollbar.track.width : 0" | |
| }, | |
| "height": { | |
| "signal": "configVerticalScrollbar.enabled ? configVerticalScrollbar.track.height : 0" | |
| }, | |
| "fill": {"signal": "configVerticalScrollbar.track.fill"}, | |
| "fillOpacity": {"signal": "panAndZoomMode ? 0.15 : 1"}, | |
| "stroke": {"signal": "configBorders.stroke"}, | |
| "strokeWidth": {"signal": "configBorders.strokeWidth"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "rect_verticalScrollbar_handle", | |
| "description": "the handle for the scrollbar", | |
| "type": "rect", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": { | |
| "signal": "verticalScrollbarMouseDown ? ganttPosition.x+ganttDimensions.width : 0" | |
| }, | |
| "width": { | |
| "signal": "configVerticalScrollbar.enabled ? configVerticalScrollbar.track.width : 0" | |
| }, | |
| "y": { | |
| "signal": "scale('scaleScrollHandleY', verticalScrollPercentage)-configVerticalScrollbar.handle.height" | |
| }, | |
| "dy": { | |
| "signal": "verticalScrollbarMouseDown ? 2*ganttPosition.dy: 0" | |
| }, | |
| "y2": { | |
| "signal": "scale('scaleScrollHandleY', verticalScrollPercentage)" | |
| }, | |
| "fill": { | |
| "signal": "verticalScrollbarMouseOver || verticalScrollbarMouseDown ? configVerticalScrollbar.handle.hover.fill : configVerticalScrollbar.handle.fill" | |
| }, | |
| "fillOpacity": {"signal": "panAndZoomMode ? 0.15 : 1"} | |
| } | |
| } | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "group-stroke-marks", | |
| "type": "group", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "width": {"signal": "ganttPosition.x+ganttDimensions.width"}, | |
| "y": {"signal": "-ganttPosition.dy*2"}, | |
| "height": {"signal": "ganttDimensions.height+ganttPosition.dy*2"} | |
| } | |
| }, | |
| "marks": [ | |
| { | |
| "name": "outer-rect", | |
| "interactive": false, | |
| "type": "rect", | |
| "encode": { | |
| "update": { | |
| "width": {"signal": "ganttPosition.x+ganttDimensions.width"}, | |
| "y": {"signal": "ganttPosition.dy*2"}, | |
| "height": {"signal": "ganttDimensions.height"}, | |
| "stroke": {"signal": "configBorders.stroke"}, | |
| "strokeWidth": {"signal": "configBorders.strokeWidth"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "column-header-left-stroke", | |
| "type": "rect", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": {"value": -0.5}, | |
| "y": {"signal": "ganttPosition.dy"}, | |
| "width": {"signal": "configBorders.strokeWidth"}, | |
| "height": {"signal": "ganttPosition.dy"}, | |
| "fill": {"signal": "configBorders.stroke"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "column-gantt-divider-rect", | |
| "type": "rect", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "x": { | |
| "signal": "ganttPosition.x-configBorders.strokeWidth/2" | |
| }, | |
| "y": { | |
| "signal": "isValid(xCurrentTopLevelUnit) ? 0 : ganttPosition.dy" | |
| }, | |
| "width": {"signal": "configBorders.strokeWidth"}, | |
| "height": { | |
| "signal": "((isValid(xCurrentTopLevelUnit) ? 2 : 1) * ganttPosition.dy)+ganttDimensions.height" | |
| }, | |
| "fill": {"signal": "configBorders.stroke"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-end-top-secondary-axis-border", | |
| "type": "rect", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "y": { | |
| "signal": "ganttPosition.dy-configBorders.strokeWidth/2" | |
| }, | |
| "width": {"signal": "ganttPosition.x+ganttDimensions.width"}, | |
| "height": {"signal": "configBorders.strokeWidth"}, | |
| "fill": {"signal": "configBorders.stroke"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-end-top-level-border", | |
| "type": "rect", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "y": {"signal": "-configBorders.strokeWidth/2"}, | |
| "x": {"signal": "ganttPosition.x"}, | |
| "width": {"signal": "ganttDimensions.width"}, | |
| "height": { | |
| "signal": "isValid(xCurrentTopLevelUnit) ? configBorders.strokeWidth : 0" | |
| }, | |
| "fill": {"signal": "configBorders.stroke"} | |
| } | |
| } | |
| }, | |
| { | |
| "name": "gantt-end-top-level-tick", | |
| "type": "rect", | |
| "interactive": false, | |
| "encode": { | |
| "update": { | |
| "y": { | |
| "signal": "isValid(xCurrentTopLevelUnit) ? 0 : ganttPosition.dy" | |
| }, | |
| "x": { | |
| "signal": "ganttPosition.x+ganttDimensions.width-configBorders.strokeWidth/2" | |
| }, | |
| "width": {"signal": " configBorders.strokeWidth"}, | |
| "height": { | |
| "signal": "ganttPosition.dy*(isValid(xCurrentTopLevelUnit) ? 2 : 1)" | |
| }, | |
| "fill": {"signal": "configBorders.stroke"} | |
| } | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| ], | |
| "scales": [ | |
| { | |
| "name": "xScaleGanttTimeSeries", | |
| "type": "time", | |
| "clamp": true, | |
| "domain": {"signal": "xDomain"}, | |
| "range": {"signal": "[0, ganttDimensions.width]"} | |
| }, | |
| { | |
| "name": "xScaleGanttTimeSeriesMax", | |
| "type": "time", | |
| "clamp": true, | |
| "domain": {"signal": "maxXDomain"}, | |
| "range": {"signal": "[1, ganttDimensions.width-2]"} | |
| }, | |
| { | |
| "name": "xZoomScale0", | |
| "type": "linear", | |
| "domain": {"signal": "[0,1]"}, | |
| "range": {"signal": "[maxXDomain[0], minXDomain[0]]"}, | |
| "clamp": true | |
| }, | |
| { | |
| "name": "xZoomScale1", | |
| "type": "linear", | |
| "domain": {"signal": "[0,1]"}, | |
| "range": {"signal": "[maxXDomain[1], minXDomain[1]]"}, | |
| "clamp": true | |
| }, | |
| { | |
| "name": "xAnchorScale", | |
| "type": "linear", | |
| "domain": {"signal": "[0,1]"}, | |
| "range": { | |
| "signal": "[maxXDomain[0]-span(xDomain)/2, maxXDomain[1]+span(xDomain)/2]" | |
| }, | |
| "clamp": true | |
| }, | |
| { | |
| "name": "scaleScrollHandleY", | |
| "type": "linear", | |
| "domain": [0, {"signal": "(actualHeight-adjustedHeight)/actualHeight"}], | |
| "range": { | |
| "signal": "[(verticalScrollbarMouseDown ? 2*ganttPosition.dy: 0)+configVerticalScrollbar.handle.height, (verticalScrollbarMouseDown ? 2*ganttPosition.dy: 0)+configVerticalScrollbar.track.height]" | |
| }, | |
| "clamp": true | |
| } | |
| ], | |
| "data": [ | |
| { | |
| "name": "dataset", | |
| "url": "https://raw.githubusercontent.com/Giammaria/PublicFiles/refs/heads/master/data/20251027_hierarchical_gantt_dataset.json?version=6", | |
| "format": { | |
| "parse": { | |
| "id": "number", | |
| "parentId": "number", | |
| "name": "string", | |
| "startDate": "date", | |
| "endDate": "date", | |
| "decimalPercentComplete": "number", | |
| "dependencyId": "string", | |
| "color": "string" | |
| } | |
| }, | |
| "transform": [] | |
| }, | |
| { | |
| "name": "dataset-formatted", | |
| "source": "dataset", | |
| "transform": [ | |
| { | |
| "type": "formula", | |
| "expr": "timeFormat(datum.startDate, '%Y-%m-%dT%H:%M:%S.%LZ')", | |
| "as": "startDateFormatted" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "timeFormat(datum.endDate, '%Y-%m-%dT%H:%M:%S.%LZ')", | |
| "as": "endDateFormatted" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "isValid(datum.color) ? datum.color : configRow.defaultFill", | |
| "as": "color" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "toDate(timeFormat(datum.startDate, '%Y-%m-%d'))", | |
| "as": "startDate" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "toDate(timeFormat(datum.endDate, '%Y-%m-%d'))", | |
| "as": "endDate" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "!isValid(datum['startDate']) ? null : datum['endDate']", | |
| "as": "endDate" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.dependencyId && datum.dependencyId!=='null' ? split(replace(datum.dependencyId,' ',''), ',') : []", | |
| "as": "dependencies" | |
| }, | |
| {"type": "stratify", "key": "id", "parentKey": "parentId"}, | |
| {"type": "window", "ops": ["row_number"], "as": ["sort"]}, | |
| {"type": "formula", "expr": "resetLevel", "as": "resetLevel"}, | |
| { | |
| "type": "formula", | |
| "expr": "{id: datum.id, parentId: datum.parentId}", | |
| "as": "idObj" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "dataset-extent", | |
| "source": "dataset-formatted", | |
| "transform": [ | |
| { | |
| "type": "aggregate", | |
| "fields": ["startDate", "endDate"], | |
| "ops": ["min", "max"], | |
| "as": ["minStart", "maxEnd"] | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "hierarchy-initial", | |
| "source": "dataset-formatted", | |
| "transform": [ | |
| { | |
| "type": "filter", | |
| "expr": "configIncludeRoot ? true : (isValid(datum['parentId']))" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "length(treeAncestors('dataset-formatted', datum['id']))-(configIncludeRoot ? 0 : 1)", | |
| "as": "level" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "indexof(pluck(data('dataset-formatted'), 'parentId'), datum['id'])>=0", | |
| "as": "hasChildren" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "slice(pluck(treeAncestors('dataset-formatted', datum['id']), 'id'), 1)", | |
| "as": "ancestorIds" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "pluck(data('dataset-formatted'), 'idObj')", | |
| "as": "immediateChildrenIds" | |
| }, | |
| { | |
| "type": "flatten", | |
| "fields": ["immediateChildrenIds"], | |
| "as": ["immediateChildrenIds"] | |
| }, | |
| { | |
| "type": "filter", | |
| "expr": "datum.id === datum.immediateChildrenIds.id || datum.id === datum.immediateChildrenIds.parentId" | |
| }, | |
| { | |
| "type": "aggregate", | |
| "ops": ["values"], | |
| "fields": ["idObs"], | |
| "groupby": [ | |
| "id", | |
| "parentId", | |
| "name", | |
| "startDate", | |
| "endDate", | |
| "level", | |
| "sort", | |
| "hasChildren", | |
| "ancestorIds" | |
| ], | |
| "as": ["immediateChildrenIds"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "pluck(slice(pluck(datum.immediateChildrenIds, 'immediateChildrenIds'), 1), 'id')", | |
| "as": "immediateChildrenIds" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.level === configInitialDepth ? datum.id : slice(datum.ancestorIds,-2)[configInitialDepth-1]", | |
| "as": "firstAncestorId" | |
| }, | |
| {"type": "formula", "expr": "null", "as": "isExpanded"}, | |
| { | |
| "type": "formula", | |
| "expr": "!datum.hasChildren ? null : isInitial ? datum.level < configInitialDepth ? 1 : 0 : datum.isExpanded", | |
| "as": "isExpanded" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "!datum.hasChildren ? null : interactionTypeHistory[0] === 'expandAll' ? 1 : interactionTypeHistory[0] === 'collapseAll' ? 0 : datum.isExpanded", | |
| "as": "isExpanded" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "!datum.hasChildren ? null : isValid(lastClickedNode) && lastClickedNode.datum.id === datum.id ? lastClickedNode.datum.isExpanded === 0 ? 1 : 0 : datum.isExpanded", | |
| "as": "isExpanded" | |
| }, | |
| {"type": "formula", "expr": "interactionTypeHistory[0]", "as": "type"}, | |
| { | |
| "type": "project", | |
| "fields": [ | |
| "sort", | |
| "id", | |
| "parentId", | |
| "name", | |
| "startDate", | |
| "endDate", | |
| "level", | |
| "ancestorIds", | |
| "dependencyIds", | |
| "hasChildren", | |
| "firstAncestorId", | |
| "immediateChildrenIds", | |
| "isExpanded", | |
| "type" | |
| ] | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "hierarchy-current-parent-collapsed-candidates", | |
| "source": "hierarchy-initial", | |
| "transform": [ | |
| {"type": "filter", "expr": "datum.isExpanded === 0"}, | |
| {"type": "filter", "expr": "datum.hasChildren"}, | |
| {"type": "project", "fields": ["id", "name"]} | |
| ] | |
| }, | |
| { | |
| "name": "hierarchy-current-parent-expanded-candidates", | |
| "source": "hierarchy-initial", | |
| "transform": [ | |
| {"type": "filter", "expr": "datum.isExpanded === 1"}, | |
| {"type": "filter", "expr": "datum.hasChildren"}, | |
| {"type": "project", "fields": ["id", "name"]} | |
| ] | |
| }, | |
| { | |
| "name": "hierarchy-visible", | |
| "source": "hierarchy-initial", | |
| "transform": [ | |
| { | |
| "type": "lookup", | |
| "from": "hierarchy-initial", | |
| "key": "parentId", | |
| "fields": ["id"], | |
| "values": ["isExpanded"], | |
| "as": ["parentIsExpanded"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "pluck(data('hierarchy-current-parent-collapsed-candidates'), 'id')", | |
| "as": "collapsedIds" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "pluck(data('hierarchy-current-parent-expanded-candidates'), 'id')", | |
| "as": "expandedIds" | |
| }, | |
| { | |
| "type": "filter", | |
| "expr": "!test('(\\\\b\\\\d+\\\\b).*\\\\b\\\\1\\\\b', (join(datum.ancestorIds, ',')+','+join(datum.collapsedIds, ','))) || (datum.parentIsExpanded && indexof(datum.expandedIds , datum.parentId) >= 0)" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "resetLevel >= datum.level || indexof(pluck(data('hierarchy-current-parent-expanded-candidates'), 'id'), datum.parentId) >= 0", | |
| "as": "isVisible" | |
| }, | |
| {"type": "filter", "expr": "datum.isVisible"}, | |
| { | |
| "type": "collect", | |
| "sort": {"field": ["sort"], "order": ["ascending"]} | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["row_number"], | |
| "as": ["index"], | |
| "sort": {"field": ["sort"], "order": ["ascending"]} | |
| }, | |
| { | |
| "type": "joinaggregate", | |
| "ops": ["min"], | |
| "fields": ["index"], | |
| "as": ["minIndex"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.index-datum.minIndex", | |
| "as": "index" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(datum.index)*configRow.rowHeight", | |
| "as": "y1" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "isInitial && datum.level <= configInitialDepth ? 1 : null", | |
| "as": "opacity" | |
| }, | |
| { | |
| "type": "project", | |
| "fields": [ | |
| "index", | |
| "sort", | |
| "id", | |
| "parentId", | |
| "name", | |
| "startDate", | |
| "endDate", | |
| "level", | |
| "ancestorIds", | |
| "dependencyIds", | |
| "hasChildren", | |
| "isVisible", | |
| "firstAncestorId", | |
| "immediateChildrenIds", | |
| "isExpanded", | |
| "y1", | |
| "opacity", | |
| "type" | |
| ] | |
| }, | |
| { | |
| "type": "filter", | |
| "expr": "collapseAllClicked ? datum.level <= configInitialDepth : true" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "hierarchy-last-visible-target", | |
| "on": [ | |
| { | |
| "trigger": "isValid(preLastClickedNodeTargets) && isValid(preLastClickedNodeTargets[0])", | |
| "remove": false, | |
| "insert": "{data: preLastClickedNodeTargets, timestamp: now(), source: 'hierarchy-visible-target'}" | |
| }, | |
| { | |
| "trigger": "timer < (initialTimestamp+500)", | |
| "remove": false, | |
| "insert": "{data: data('hierarchy-initial'), timestamp: now(), source: 'hierarchy-initial'}" | |
| } | |
| ], | |
| "transform": [ | |
| { | |
| "type": "formula", | |
| "expr": "datum.timestamp || now()", | |
| "as": "timestamp" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.data || data('hierarchy-initial')", | |
| "as": "data" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["row_number"], | |
| "sort": {"field": "timestamp", "order": "descending"}, | |
| "as": ["index"] | |
| }, | |
| { | |
| "type": "joinaggregate", | |
| "ops": ["max"], | |
| "fields": ["index"], | |
| "as": ["count"] | |
| }, | |
| {"type": "formula", "expr": "max(datum.count, 2)", "as": "count"}, | |
| {"type": "filter", "expr": "datum.index === 1"}, | |
| {"type": "formula", "expr": "datum.data[0].type", "as": "type"}, | |
| {"type": "flatten", "fields": ["data"]}, | |
| {"type": "formula", "expr": "datum.id || datum.data.id", "as": "id"}, | |
| { | |
| "type": "formula", | |
| "expr": "datum.parentId || datum.data.parentId", | |
| "as": "parentId" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.name || datum.data.name", | |
| "as": "name" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.sort || datum.data.sort", | |
| "as": "sort" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.ancestorIds || datum.data.ancestorIds", | |
| "as": "ancestorIds" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.firstAncestorId || datum.data.firstAncestorId", | |
| "as": "firstAncestorId" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.startDate || datum.data.startDate", | |
| "as": "startDate" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.endDate || datum.data.endDate", | |
| "as": "endDate" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.hasChildren || datum.data.hasChildren", | |
| "as": "hasChildren" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.level || datum.data.level", | |
| "as": "level" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.immediateChildrenIds || datum.data.immediateChildrenIds", | |
| "as": "immediateChildrenIds" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.isExpanded || datum.data.isExpanded", | |
| "as": "isExpanded" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.isVisible || datum.data.isVisible || (datum.source === 'hierarchy-initial' && (resetLevel >= datum.level)) || false", | |
| "as": "isVisible" | |
| }, | |
| {"type": "formula", "expr": "datum.isVisible ? 1 : 0", "as": "opacity"}, | |
| { | |
| "type": "formula", | |
| "expr": "datum.level <= configInitialDepth", | |
| "as": "visibleAtInitialization" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["row_number"], | |
| "groupby": ["visibleAtInitialization"], | |
| "as": ["initialIndex"], | |
| "sort": {"field": ["sort"], "order": ["ascending"]} | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(datum.initialIndex-1) * configRow.rowHeight", | |
| "as": "yInitial" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.visibleAtInitialization ? datum.yInitial : 0", | |
| "as": "yInitial" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.data.y1 || datum.yInitial", | |
| "as": "y1" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["max"], | |
| "fields": ["y1"], | |
| "frame": [null, 0], | |
| "sort": {"field": "sort"}, | |
| "as": ["y1"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.animateCount || datum.data.animateCount", | |
| "as": "animateCount" | |
| }, | |
| { | |
| "type": "project", | |
| "fields": [ | |
| "animateCount", | |
| "timestamp", | |
| "id", | |
| "parentId", | |
| "name", | |
| "sort", | |
| "ancestorIds", | |
| "firstAncestorId", | |
| "startDate", | |
| "endDate", | |
| "hasChildren", | |
| "level", | |
| "immediateChildrenIds", | |
| "isVisible", | |
| "isExpanded", | |
| "source", | |
| "y1", | |
| "opacity", | |
| "type" | |
| ] | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "hierarchy-visible-source", | |
| "on": [ | |
| { | |
| "trigger": "data('hierarchy-last-visible-target')", | |
| "remove": true, | |
| "insert": "data('hierarchy-last-visible-target')" | |
| } | |
| ], | |
| "transform": [] | |
| }, | |
| { | |
| "name": "height", | |
| "source": "hierarchy-visible", | |
| "transform": [ | |
| {"type": "aggregate", "ops": ["count"], "as": ["height"]}, | |
| { | |
| "type": "formula", | |
| "expr": "(datum.height)*configRow.rowHeight", | |
| "as": "height" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "height-animation", | |
| "values": [{"height": null, "timestamp": null}], | |
| "on": [ | |
| { | |
| "trigger": "animStartTick", | |
| "insert": "{height: data('height')[0].height, timestamp: now()}", | |
| "remove": false | |
| } | |
| ], | |
| "transform": [ | |
| { | |
| "type": "formula", | |
| "expr": "datum.height || data('height')[0].height", | |
| "as": "height" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["row_number"], | |
| "sort": {"field": "timestamp", "order": "descending"}, | |
| "as": ["index"] | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["lead"], | |
| "fields": ["height"], | |
| "sort": {"field": "index"}, | |
| "as": ["previousHeight"] | |
| }, | |
| {"type": "filter", "expr": "(datum.height !== datum.previousHeight)"}, | |
| { | |
| "type": "formula", | |
| "expr": "datum.previousHeight || datum.height", | |
| "as": "previousHeight" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["row_number"], | |
| "sort": {"field": "index", "order": "ascending"}, | |
| "as": ["index"] | |
| }, | |
| {"type": "filter", "expr": "datum.index === 1"}, | |
| {"type": "formula", "expr": "datum.timestamp || now()", "as": "start"}, | |
| { | |
| "type": "formula", | |
| "expr": "datum.start + configAnimationDuration.nodeExpandCollapse", | |
| "as": "end" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "timer <= datum.end ? clamp((timer-datum.start)/(datum.end-datum.start), 0,1) : 1", | |
| "as": "t" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.t < 0.5 ? 4 * pow(datum.t, 3) : 1 - pow(-2 * datum.t + 2, 3) / 2", | |
| "as": "tEased" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.previousHeight+(datum.height-datum.previousHeight)*datum.tEased", | |
| "as": "animatedHeight" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "hierarchy-visible-target", | |
| "source": "hierarchy-initial", | |
| "transform": [ | |
| { | |
| "type": "lookup", | |
| "from": "hierarchy-visible", | |
| "key": "id", | |
| "fields": ["id"], | |
| "as": ["visibleValues"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "isValid(datum.visibleValues)", | |
| "as": "isVisible" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["dense_rank"], | |
| "groupby": ["isVisible"], | |
| "as": ["index"], | |
| "sort": {"field": ["sort"], "order": ["ascending"]} | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "!datum.isVisible ? null : datum.visibleValues.y1", | |
| "as": "y1" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["max"], | |
| "fields": ["y1"], | |
| "frame": [null, 0], | |
| "sort": {"field": "sort"}, | |
| "as": ["y1"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(datum.level <= configInitialDepth || datum.isVisible) ? 1 : 0", | |
| "as": "opacity" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["dense_rank"], | |
| "as": ["index"], | |
| "sort": {"field": ["sort"], "order": ["ascending"]} | |
| }, | |
| {"type": "formula", "expr": "animateCount", "as": "animateCount"}, | |
| { | |
| "type": "project", | |
| "fields": [ | |
| "animateCount", | |
| "index", | |
| "id", | |
| "parentId", | |
| "name", | |
| "sort", | |
| "isVisible", | |
| "firstAncestorId", | |
| "collapseAllY1", | |
| "startDate", | |
| "endDate", | |
| "hasChildren", | |
| "level", | |
| "immediateChildrenIds", | |
| "ancestorIds", | |
| "isExpanded", | |
| "opacity", | |
| "y1", | |
| "type" | |
| ] | |
| }, | |
| {"type": "collect", "sort": {"field": "index"}} | |
| ] | |
| }, | |
| { | |
| "name": "hierarchy-pre-animation", | |
| "on": [ | |
| { | |
| "trigger": "animateCount", | |
| "insert": "data('hierarchy-initial')", | |
| "remove": true | |
| }, | |
| { | |
| "trigger": "timer < (initialTimestamp+500)", | |
| "remove": true, | |
| "insert": "data('hierarchy-initial')" | |
| } | |
| ], | |
| "transform": [ | |
| { | |
| "type": "lookup", | |
| "key": "id", | |
| "fields": ["id"], | |
| "from": "hierarchy-visible-source", | |
| "as": ["sourceValues"] | |
| }, | |
| { | |
| "type": "lookup", | |
| "key": "id", | |
| "fields": ["id"], | |
| "from": "hierarchy-visible-target", | |
| "as": ["targetValues"] | |
| }, | |
| { | |
| "type": "filter", | |
| "expr": "isValid(datum.sourceValues) || isValid(datum.targetValues)" | |
| }, | |
| { | |
| "type": "formula", | |
| "as": "sourceOpacity", | |
| "expr": "isValid(datum.sourceValues) ? datum.sourceValues.opacity : 0" | |
| }, | |
| { | |
| "type": "formula", | |
| "as": "targetOpacity", | |
| "expr": "isValid(datum.targetValues) ? datum.targetValues.opacity : 0" | |
| }, | |
| { | |
| "type": "formula", | |
| "as": "sourceY1", | |
| "expr": "isValid(datum.sourceValues) ? datum.sourceValues.y1 : (isValid(datum.targetValues) ? datum.targetValues.collapseAllY1 : null)" | |
| }, | |
| { | |
| "type": "formula", | |
| "as": "targetY1", | |
| "expr": "isValid(datum.targetValues) ? datum.targetValues.y1 : (isValid(datum.sourceValues) ? datum.sourceValues.collapseAllY1 : null)" | |
| }, | |
| { | |
| "type": "filter", | |
| "expr": "isValid(datum.sourceY1) && isValid(datum.targetY1)" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.targetValues.isExpanded", | |
| "as": "isExpanded" | |
| }, | |
| { | |
| "type": "lookup", | |
| "from": "dataset-formatted", | |
| "key": "id", | |
| "fields": ["id"], | |
| "values": [ | |
| "decimalPercentComplete", | |
| "dependencyId", | |
| "color", | |
| "dependencies" | |
| ] | |
| }, | |
| {"type": "collect", "sort": {"field": "sort"}} | |
| ] | |
| }, | |
| { | |
| "name": "hierarchy-animation-bounds", | |
| "values": [{"start": 0, "end": -1}], | |
| "on": [ | |
| { | |
| "trigger": "animStartTick", | |
| "insert": "{start: animStartTick, end: animStartTick+configAnimationDuration.nodeExpandCollapse}", | |
| "remove": true | |
| } | |
| ], | |
| "transform": [] | |
| }, | |
| { | |
| "name": "hierarchy-animation", | |
| "on": [ | |
| { | |
| "trigger": "animateCount", | |
| "remove": true, | |
| "insert": "data('hierarchy-pre-animation')" | |
| }, | |
| { | |
| "trigger": "timer < (initialTimestamp+500)", | |
| "remove": true, | |
| "insert": "isValid(data('hierarchy-pre-animation')[0]) ? data('hierarchy-pre-animation') : []" | |
| } | |
| ], | |
| "transform": [ | |
| { | |
| "type": "lookup", | |
| "from": "dataset-formatted", | |
| "key": "id", | |
| "fields": ["id"], | |
| "values": [ | |
| "decimalPercentComplete", | |
| "dependencyId", | |
| "color", | |
| "dependencies", | |
| "label" | |
| ] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "scale('xScaleGanttTimeSeries', datum.startDate)", | |
| "as": "x1" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "scale('xScaleGanttTimeSeries', datum.endDate)", | |
| "as": "x2" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "scale('xScaleGanttTimeSeries', span([datum.startDate, datum.endDate])*datum.decimalPercentComplete+datum.startDate)", | |
| "as": "x2Progress" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "lerp([datum.sourceY1, datum.targetY1], rowAnimationTEased)", | |
| "as": "y1" | |
| }, | |
| {"type": "formula", "expr": "datum.y1+configRow.rowHeight", "as": "y2"}, | |
| {"type": "formula", "expr": "scrollY+datum.y2", "as": "bufferHeight"}, | |
| { | |
| "type": "filter", | |
| "expr": "datum.bufferHeight <= (adjustedHeight+configRow.rowHeight) && (datum.bufferHeight) >= 0" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.y1+configRow.rowHeight*(datum.hasChildren ? 0.225 : 0.325)", | |
| "as": "y1WithPadding" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.y2-configRow.rowHeight*(datum.hasChildren ? 0.225 : 0.325)", | |
| "as": "y2WithPadding" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.y2WithPadding-(configRow.rowHeight*0.225)*rowAnimationTEased", | |
| "as": "y2WithPaddingParentExpanding" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.y2WithPadding-(configRow.rowHeight*0.225)", | |
| "as": "y2WithPaddingParentExpanded" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.y2WithPadding-(configRow.rowHeight*0.225)*(1-rowAnimationTEased)", | |
| "as": "y2WithPaddingParentCollapsing" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.y2WithPadding-(configRow.rowHeight*0.225)", | |
| "as": "y2WithPaddingParentCollapsed" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "!isValid(datum.isExpanded) || !isValid(datum.sourceValues.isExpanded) ? datum.y2WithPadding : datum.isExpanded === 1 ? datum.sourceValues.isExpanded === 0 ? datum.y2WithPaddingParentExpanding : datum.y2WithPaddingParentExpanded : datum.sourceValues.isExpanded === 1 ? datum.isExpanded === 0 ? datum.y2WithPaddingParentCollapsing : datum.y2WithPaddingParentCollapsed : datum.y2WithPadding", | |
| "as": "y2WithPaddingParent" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "lerp([datum.sourceOpacity, datum.targetOpacity], rowAnimationTEased)*0.35", | |
| "as": "opacity" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "lerp([datum.sourceOpacity, datum.targetOpacity], rowAnimationTEased)", | |
| "as": "fullOpacity" | |
| }, | |
| {"type": "filter", "expr": "datum.opacity>0"}, | |
| { | |
| "type": "window", | |
| "ops": ["row_number"], | |
| "sort": {"field": "sort"}, | |
| "as": ["index"] | |
| }, | |
| {"type": "collect", "sort": {"field": "sort"}} | |
| ] | |
| }, | |
| { | |
| "name": "dependency-links", | |
| "source": "hierarchy-pre-animation", | |
| "transform": [ | |
| { | |
| "type": "filter", | |
| "expr": "datum.targetOpacity === 1 && isValid(datum.dependencyId)" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "split(datum.dependencyId, ',')", | |
| "as": "dependencyId" | |
| }, | |
| {"type": "flatten", "fields": ["dependencyId"]}, | |
| { | |
| "type": "project", | |
| "fields": ["dependencyId", "id"], | |
| "as": ["sourceId", "targetId"] | |
| }, | |
| { | |
| "type": "filter", | |
| "expr": "isValid(datum.sourceId) && isValid(datum.targetId)" | |
| }, | |
| {"type": "formula", "expr": "+datum.sourceId", "as": "sourceId"}, | |
| { | |
| "type": "lookup", | |
| "key": "id", | |
| "fields": ["sourceId"], | |
| "from": "hierarchy-visible-target", | |
| "values": ["endDate", "index"], | |
| "as": ["sourceEndDate", "sourceRowNumber"] | |
| }, | |
| { | |
| "type": "lookup", | |
| "key": "id", | |
| "fields": ["targetId"], | |
| "from": "hierarchy-visible-target", | |
| "values": ["startDate", "index"], | |
| "as": ["targetStartDate", "targetRowNumber"] | |
| }, | |
| { | |
| "type": "lookup", | |
| "key": "id", | |
| "fields": ["sourceId"], | |
| "from": "hierarchy-pre-animation", | |
| "values": ["sourceValues", "targetValues"], | |
| "as": ["sourceSourceValues", "sourceTargetValues"] | |
| }, | |
| { | |
| "type": "lookup", | |
| "key": "id", | |
| "fields": ["targetId"], | |
| "from": "hierarchy-pre-animation", | |
| "values": ["sourceValues", "targetValues"], | |
| "as": ["targetSourceValues", "targetTargetValues"] | |
| }, | |
| { | |
| "type": "filter", | |
| "expr": "isValid(datum.sourceEndDate) && isValid(datum.sourceRowNumber) && isValid(datum.targetStartDate) && isValid(datum.targetRowNumber) && isValid(datum.sourceSourceValues) && isValid(datum.sourceTargetValues) && isValid(datum.targetSourceValues) && isValid(datum.targetTargetValues)" | |
| }, | |
| { | |
| "type": "formula", | |
| "as": "animationBounds", | |
| "expr": "length(data('hierarchy-animation-bounds')) > 0 ? data('hierarchy-animation-bounds')[0] : {start: animStartTick, end: animStartTick+configAnimationDuration.nodeExpandCollapse}" | |
| }, | |
| { | |
| "type": "formula", | |
| "as": "t", | |
| "expr": "(datum.animationBounds.end <= datum.animationBounds.start) ? 0 : clamp((timer - datum.animationBounds.start) / (datum.animationBounds.end - datum.animationBounds.start), 0, 1)" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.t < 0.5 ? 4*pow(datum.t,3) : 1 - pow(-2*datum.t + 2, 3)/2", | |
| "as": "tEased" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "lerp([datum.sourceSourceValues.y1, datum.sourceTargetValues.y1], datum.tEased)+configRow.rowHeight/2", | |
| "as": "sourceY" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "lerp([datum.targetSourceValues.y1, datum.targetTargetValues.y1], datum.tEased)+configRow.rowHeight/2", | |
| "as": "targetY" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(scrollY+datum.sourceY)", | |
| "as": "sourceBufferHeight" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(scrollY+datum.targetY)", | |
| "as": "targetBufferHeight" | |
| }, | |
| { | |
| "type": "filter", | |
| "expr": "(datum.sourceBufferHeight <= (adjustedHeight+configRow.rowHeight) && (datum.sourceBufferHeight) >= 0) || (datum.targetBufferHeight <= (adjustedHeight+configRow.rowHeight) && (datum.targetBufferHeight) >= 0)" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "lerp([datum.targetSourceValues.opacity || 0, datum.targetTargetValues.opacity || 0], datum.tEased)*0.35", | |
| "as": "opacity" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "+format(scale('xScaleGanttTimeSeries', datum.sourceEndDate), '.4f')+5", | |
| "as": "sourceX" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "+format(scale('xScaleGanttTimeSeries', datum.targetStartDate), '.4f')-2.5", | |
| "as": "targetX" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "{x: datum.sourceX, y: datum.sourceY}", | |
| "as": "source" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "{x: datum.targetX, y: datum.targetY}", | |
| "as": "target" | |
| }, | |
| {"type": "fold", "fields": ["source", "target"], "as": ["key", "xy"]}, | |
| { | |
| "type": "formula", | |
| "expr": "datum.sourceId+'-'+datum.targetId", | |
| "as": "sourceTargetCompId" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["dense_rank"], | |
| "sort": {"field": "sourceTargetCompId"}, | |
| "as": ["linkId"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(datum.targetY-datum.sourceY)/2", | |
| "as": "halfHeight" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.targetRowNumber-datum.sourceRowNumber", | |
| "as": "rowSeparationCount" | |
| }, | |
| {"type": "formula", "expr": "[1,2,3]", "as": "sort"}, | |
| {"type": "flatten", "fields": ["sort"]}, | |
| { | |
| "type": "formula", | |
| "expr": "datum.sort+(datum.key === 'source' ? 0 : 3)", | |
| "as": "sort" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.sort === 2 ? {x: datum.xy.x+15, y:datum.xy.y} : datum.xy", | |
| "as": "xy" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.sort === 3 ? {x:datum.xy.x+15, y:datum.xy.y+datum.halfHeight} : datum.xy", | |
| "as": "xy" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.sort === 4 ? {x:datum.xy.x-15, y:datum.xy.y-datum.halfHeight} : datum.xy", | |
| "as": "xy" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.sort === 5 ? {x:datum.xy.x-15, y:datum.xy.y} : datum.xy", | |
| "as": "xy" | |
| }, | |
| { | |
| "type": "filter", | |
| "expr": "inrange(invert('xScaleGanttTimeSeries', datum.xy.x), xDomain)" | |
| }, | |
| {"type": "filter", "expr": "datum.xy.x <= ganttDimensions.width"}, | |
| { | |
| "type": "joinaggregate", | |
| "ops": ["count"], | |
| "fields": ["linkId"], | |
| "groupby": ["linkId"], | |
| "as": ["segmentCount"] | |
| }, | |
| {"type": "filter", "expr": "datum.segmentCount === 6"} | |
| ] | |
| }, | |
| { | |
| "name": "ganttTimeSeriesConfigurations", | |
| "values": [{"timeSeriesWidths": 1}], | |
| "transform": [ | |
| { | |
| "type": "formula", | |
| "expr": "{year: span(xDomain)/msPerDay/365, month: span(xDomain)/msPerDay/12, day: span(xDomain)/msPerDay, hour: span(xDomain)/msPerDay*24}", | |
| "as": "timeSeriesWidths" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "{year: ceil(datum.timeSeriesWidths.year*smallestAllowableXWidth), month: ceil(datum.timeSeriesWidths.month*smallestAllowableXWidth), day: ceil(datum.timeSeriesWidths.day*smallestAllowableXWidth), hour: ceil(datum.timeSeriesWidths.hour*smallestAllowableXWidth*2)}", | |
| "as": "timeSeriesWidths" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.timeSeriesWidths.hour <= ganttDimensions.width ? 'hour' : datum.timeSeriesWidths.day <= ganttDimensions.width ? 'day' : datum.timeSeriesWidths.month <= ganttDimensions.width ? 'month' : 'year'", | |
| "as": "secondaryTickCount" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "['%Y', '%b', '%d', '%I %p'][indexof(['year', 'month', 'day', 'hour'], datum.secondaryTickCount)]", | |
| "as": "secondaryFormat" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "['year', 'month', 'day', 'hour'][indexof(['year', 'month', 'day', 'hour'], datum.secondaryTickCount)-1]", | |
| "as": "tickCount" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "['%Y', '%b %Y', '%d-%b-%y', '%I %p'][indexof(['year', 'month', 'day', 'hour'], datum.secondaryTickCount)-1]", | |
| "as": "format" | |
| } | |
| ] | |
| }, | |
| { | |
| "name": "expandAllCollapseAllConfig", | |
| "values": [ | |
| { | |
| "rowNumber": 0, | |
| "name": "expandAll", | |
| "path": "M342.6 534.6C330.1 547.1 309.8 547.1 297.3 534.6L137.3 374.6C124.8 362.1 124.8 341.8 137.3 329.3C149.8 316.8 170.1 316.8 182.6 329.3L320 466.7L457.4 329.4C469.9 316.9 490.2 316.9 502.7 329.4C515.2 341.9 515.2 362.2 502.7 374.7L342.7 534.7zM502.6 182.6L342.6 342.6C330.1 355.1 309.8 355.1 297.3 342.6L137.3 182.6C124.8 170.1 124.8 149.8 137.3 137.3C149.8 124.8 170.1 124.8 182.6 137.3L320 274.7L457.4 137.4C469.9 124.9 490.2 124.9 502.7 137.4C515.2 149.9 515.2 170.2 502.7 182.7z", | |
| "size": 0.00155, | |
| "fontWeight": 400, | |
| "tooltip": "Expand all" | |
| }, | |
| { | |
| "rowNumber": 1, | |
| "name": "collapseAll", | |
| "path": "M342.6 105.4C330.1 92.9 309.8 92.9 297.3 105.4L137.3 265.4C124.8 277.9 124.8 298.2 137.3 310.7C149.8 323.2 170.1 323.2 182.6 310.7L320 173.3L457.4 310.6C469.9 323.1 490.2 323.1 502.7 310.6C515.2 298.1 515.2 277.8 502.7 265.3L342.7 105.3zM502.6 457.4L342.6 297.4C330.1 284.9 309.8 284.9 297.3 297.4L137.3 457.4C124.8 469.9 124.8 490.2 137.3 502.7C149.8 515.2 170.1 515.2 182.6 502.7L320 365.3L457.4 502.6C469.9 515.1 490.2 515.1 502.7 502.6C515.2 490.1 515.2 469.8 502.7 457.3z", | |
| "size": 0.00155, | |
| "tooltip": "Collapse all" | |
| } | |
| ] | |
| }, | |
| { | |
| "transform": [ | |
| {"type": "formula", "expr": "datum.name || null", "as": "name"}, | |
| {"type": "formula", "expr": "datum.field || null", "as": "field"}, | |
| {"type": "formula", "expr": "datum.type || 'text'", "as": "type"}, | |
| {"type": "formula", "expr": "datum.format || null", "as": "format"}, | |
| {"type": "formula", "expr": "datum.label || null", "as": "label"}, | |
| {"type": "formula", "expr": "datum.align || 'left'", "as": "align"}, | |
| { | |
| "type": "formula", | |
| "expr": "datum.allowableWidth || 0", | |
| "as": "allowableWidth" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.alwaysShow || false", | |
| "as": "alwaysShow" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.boldValue || false", | |
| "as": "boldValue" | |
| }, | |
| {"type": "window", "ops": ["row_number"], "as": ["index"]}, | |
| { | |
| "type": "window", | |
| "ops": ["sum"], | |
| "fields": ["allowableWidth"], | |
| "sort": {"field": "index", "order": "ascending"}, | |
| "frame": [null, 0], | |
| "as": ["x"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(datum.x-datum.allowableWidth)+(configColumn.innerPadding*(datum.index-1))", | |
| "as": "x" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["lead"], | |
| "fields": ["x"], | |
| "sort": {"field": "index"}, | |
| "frame": [0, null], | |
| "as": ["x2"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.x2 || (datum.x+datum.allowableWidth)", | |
| "as": "x2" | |
| }, | |
| { | |
| "type": "joinaggregate", | |
| "ops": ["max"], | |
| "fields": ["alwaysShow"], | |
| "as": ["anyVisibleColumns"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "showDetails ? 'show' : (datum.alwaysShow ? 'show' : 'hide')", | |
| "as": "show" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["max"], | |
| "fields": ["show"], | |
| "frame": [0, null], | |
| "as": ["show"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.show === 'show' ? datum.x : 0", | |
| "as": "showX" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.show === 'show' ? datum.x2-datum.x : 0", | |
| "as": "showAllowableWidth" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["lead"], | |
| "fields": ["showAllowableWidth"], | |
| "sort": {"field": "index"}, | |
| "frame": [0, null], | |
| "as": ["lastShownRow"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "!isValid(datum.lastShownRow) || datum.lastShownRow === 0", | |
| "as": "lastShownRow" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "datum.show === 'show' && datum.lastShownRow ? datum.x2-datum.x : 0", | |
| "as": "showLastAllowableWidth" | |
| }, | |
| { | |
| "type": "window", | |
| "ops": ["last_value", "max", "max"], | |
| "fields": ["allowableWidth", "showX", "showLastAllowableWidth"], | |
| "sort": {"field": "index"}, | |
| "frame": [null, null], | |
| "as": ["lastAllowableWidth", "lastX", "showLastAllowableWidth"] | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "(showDetails ? datum.lastAllowableWidth : datum.showLastAllowableWidth)+datum.lastX", | |
| "as": "totalWidth" | |
| }, | |
| { | |
| "type": "formula", | |
| "expr": "!showDetails && !datum.anyVisibleColumns ? 0 : datum.totalWidth + configRow.levelIndentWidth", | |
| "as": "totalWidth" | |
| } | |
| ], | |
| "name": "column-data", | |
| "values": [ | |
| { | |
| "name": "name", | |
| "field": "name", | |
| "type": "text", | |
| "format": null, | |
| "label": "Task", | |
| "align": "left", | |
| "allowableWidth": 95, | |
| "alwaysShow": true, | |
| "boldValue": true | |
| }, | |
| { | |
| "name": "startDate", | |
| "field": "startDate", | |
| "type": "date", | |
| "format": "%m/%d/%Y", | |
| "label": "Start Date", | |
| "align": "right", | |
| "allowableWidth": 55, | |
| "alwaysShow": false, | |
| "boldValue": false | |
| }, | |
| { | |
| "name": "endDate", | |
| "field": "endDate", | |
| "type": "date", | |
| "format": "%m/%d/%Y", | |
| "label": "End Date", | |
| "align": "right", | |
| "allowableWidth": 55, | |
| "alwaysShow": false, | |
| "boldValue": false | |
| }, | |
| { | |
| "name": "duration", | |
| "field": "duration", | |
| "type": "text", | |
| "format": null, | |
| "label": "Duration", | |
| "align": "right", | |
| "allowableWidth": 50, | |
| "alwaysShow": false, | |
| "boldValue": false | |
| }, | |
| { | |
| "name": "progress", | |
| "field": "decimalPercentComplete", | |
| "type": "percentage", | |
| "format": ".0%", | |
| "label": "Progress", | |
| "align": "center", | |
| "allowableWidth": 75, | |
| "alwaysShow": false, | |
| "boldValue": false | |
| } | |
| ] | |
| } | |
| ], | |
| "config": {"text": {"font": "Segoe UI"}} | |
| } | 
  
    Sign up for free
    to join this conversation on GitHub.
    Already have an account?
    Sign in to comment