Skip to content

Instantly share code, notes, and snippets.

@Giammaria
Last active February 6, 2025 19:53
Show Gist options
  • Save Giammaria/6d489d8ef1d28019ce19c7bb39b657a3 to your computer and use it in GitHub Desktop.
Save Giammaria/6d489d8ef1d28019ce19c7bb39b657a3 to your computer and use it in GitHub Desktop.
20241116_sf_mousewheel_measures_poc_v
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"height": 300,
"autosize": {"type": "fit-x", "resize": true},
"signals": [
{
"name": "desiredWidth",
"value": 1000,
"update": "clamp(desiredWidth, 200, 1400)"
},
{
"name": "columnsWidthPercent",
"value": 0,
"update": "clamp(columnsWidthPercent, 0, 0.5)"
},
{
"name": "offsetUnit",
"value": "hour",
"update": "indexof(['hours', 'day', 'month', 'year'], offsetUnit) < 0 ? 'month' : offsetUnit"
},
{
"name": "negativeDateOffsetFromToday",
"value": -5,
"update": "clamp(negativeDateOffsetFromToday, -10, 0)"
},
{
"name": "positiveOffsetFromToday",
"value": 5,
"update": "clamp(positiveOffsetFromToday, 1, 10)"
},
{"name": "recordCount", "value": 5, "update": "clamp(recordCount, 1, 100)"},
{
"name": "blockHeight",
"value": 15,
"update": "clamp(blockHeight, 10, 50)"
},
{"name": "columnsWidth", "update": "desiredWidth*columnsWidthPercent"},
{"name": "ganttWidth", "update": "desiredWidth*(1-columnsWidthPercent)"},
{
"name": "configToggleMouseWheelGranularity",
"description": "configurations for the mouse wheel granularity control",
"update": "{enabled: true, initialValue: false, xOffset: 0, track: {height: 7.5, width: 25, cornerRadius: 5, fill: '#EEE', stroke: '#777', strokeWidth: 1}, handle: {stroke: '#777', strokeWidth: 1, fill: '#fff'}, label: {text: 'Mouse wheel granularity', font: 'Segoe UI', fontSize: 12, fill: '#666', fontStyle: 'regular', dx: 5}, on: {fill: '#d1e0ec', fillOpacity: 1, stroke: '#777', strokeWidth: 1}, tooltip: {object: {title: 'Mouse Wheel Date Granularity', '• ‎ ‎ ‎ ‎ ‎ ‎': 'Click here to toggle the ability to adjust date granularity using the mouse wheel.', '• ‎ ‎ ‎ ‎ ‎ ‎‎': 'Alternatively, press the Ctrl key to quickly toggle this capability on or off.', '‎‎':'‎', 'Note': 'When toggled off, the mouse wheel will allow for vertical scrolling when necessary.'}}}"
},
{
"name": "todayDate",
"update": "utc(year(now()),month(now()),date(now()))"
},
{
"name": "scrollDomain",
"update": "[xDomainBounds.max+span(xDomainInitial)/2, ((initialDomainCenter - xDomainBounds.max)+initialDomainCenter)-span(xDomainInitial)/2]"
},
{
"name": "horizontalScrollPercentage",
"init": "{current: 0.5, previous: 0.5, debug: 'start'}",
"on": [
{
"events": {
"type": "pointermove",
"source": "scope",
"consume": true,
"markname": "gantt-mini-map-interactive-rect",
"debounce": 10,
"between": [{"type": "pointerdown"}, {"type": "pointerup"}]
},
"update": "inrange(invert('xScaleMiniMap', x(group())-60), xDomain) ? horizontalScrollPercentage : {current: clamp(1-((domain('xScaleMiniMap')[1] - +invert('xScaleMiniMap', x(group())-60) + ((1-((domain('xScaleMiniMap')[1] - +invert('xScaleMiniMap', x(group())-60)))/span(domain('xScaleMiniMap'))) < horizontalScrollPercentage.current ? 0 : span(xDomain))))/span(domain('xScaleMiniMap')), 0,1), previous: horizontalScrollPercentage.current === 0 ? span(xDomain)/span(domain('xScaleMiniMap')) : horizontalScrollPercentage.current}"
},
{
"events": {
"type": "pointerdown",
"source": "scope",
"consume": true,
"markname": "gantt-mini-map-interactive-rect"
},
"update": "inrange(invert('xScaleMiniMap', x(group())), xDomain) ? horizontalScrollPercentage : {current: clamp(1-((domain('xScaleMiniMap')[1]- +invert('xScaleMiniMap', x(group()))+span(xDomain)/2))/span(domain('xScaleMiniMap')), 0,1), previous: horizontalScrollPercentage.current === 0 ? span(xDomain)/span(domain('xScaleMiniMap')) : horizontalScrollPercentage.current}"
},
{
"events": {"type": "pointerup"},
"update": "horizontalScrollPercentage"
},
{
"events": "window:keydown",
"update": "indexof(['ArrowRight', 'ArrowLeft'], event.code) < 0 ? horizontalScrollPercentage : event.code === 'ArrowRight' ? {current: clamp(horizontalScrollPercentage.current+0.01, 0,1), previous: clamp(horizontalScrollPercentage.current, 0.01, 0.99)} : {current: clamp(horizontalScrollPercentage.current-0.01, 0,1), previous: clamp(horizontalScrollPercentage.current, 0.01, 0.99)}"
}
]
},
{"name": "minDateBandwidth", "value": 20},
{
"name": "mouseWheelGranularity",
"description": "the currently toggled value for the mouse wheel granularity control",
"init": "configToggleMouseWheelGranularity.initialValue",
"on": [
{
"events": "@mouseWheel-granularity-clickable-rect:pointerdown",
"update": "!mouseWheelGranularity"
},
{
"events": "window:keydown[event.ctrlKey]{0, 100}",
"update": "mouseWheelGranularity ? false : true"
}
]
},
{
"name": "wheelDelta",
"value": 0,
"on": [
{
"events": "wheel!",
"force": true,
"update": "mouseWheelGranularity && x()>columnsWidth?pow(1.001, (event.deltaY) * pow(16, event.deltaMode)) : 0"
},
{
"events": "wheel![event.ctrlKey]",
"force": true,
"update": "x()>columnsWidth?pow(1.001, (event.deltaY) * pow(16, event.deltaMode)) : 0"
}
]
},
{
"name": "zoom",
"value": 1,
"on": [
{
"events": "wheel!",
"force": true,
"update": "!mouseWheelGranularity || wheelDelta === 0 ? zoom : wheelDelta"
}
]
},
{"name": "xDomainInitial", "update": "data('xDomainInitial')[0]['domain']"},
{
"name": "initialDomainCenter",
"update": "xDomainInitial[0] + span(xDomainInitial) / 2"
},
{
"name": "currentDomainCenter",
"init": "initialDomainCenter",
"on": [
{
"events": {"signal": "horizontalScrollPercentage.current"},
"update": "xDomain[0] + span(xDomain) / 2"
},
{
"events": {"signal": "xDomain"},
"update": "xDomain[0] + span(xDomain) / 2"
}
]
},
{
"name": "xDomainBounds",
"update": "{min: span(data('xDomainInitial')[0]['hourDomain']), max: round((ganttWidth/0.075)* scale('dateUnitIncrementMS', 'day'))}"
},
{
"name": "xDomain",
"init": "xDomainInitial",
"on": [
{
"events": {"signal": "xDomainPreliminary"},
"update": "span(xDomainPreliminary)<xDomainBounds.min ? [currentDomainCenter - xDomainBounds.min/2, currentDomainCenter + xDomainBounds.min/2] : [max(xDomainPreliminary[0], scrollDomain[0]), min(xDomainPreliminary[1], scrollDomain[1])]"
}
]
},
{
"name": "xDomainPreliminary",
"value": [0, 0],
"on": [
{
"events": {"signal": "zoom"},
"update": "wheelDelta === 0 ? xDomainPreliminary : [currentDomainCenter + (xDomain[0] - currentDomainCenter) * zoom, currentDomainCenter + (xDomain[1] - currentDomainCenter) * zoom]"
},
{
"events": {"signal": "horizontalScrollPercentage.current"},
"update": "[min(scale('scaleXScroll', horizontalScrollPercentage.current), scale('scaleXScroll', 1)-span(xDomain)), span(xDomain)+scale('scaleXScroll', horizontalScrollPercentage.current)]"
}
]
},
{"name": "today", "update": "utc(year(now()),month(now()),date(now()))"},
{
"name": "currentXBandwidth",
"update": "(round((scale('x', timeOffset('hours', utchours(0,0,0,0),1)) - scale('x', utchours(0,0,0,0))) *100)/100)"
},
{"name": "thresholdMinuteBandwidth", "update": "17.78"},
{"name": "thresholdHourBandwidth", "update": "0.6"},
{"name": "thresholdDayBandwidth", "update": "0.26"},
{"name": "thresholdMonthBandwidth", "update": "0.04"},
{"name": "thresholdYearBandwidth", "update": "0.03"},
{
"name": "xCurrentUnit",
"update": "(currentXBandwidth>=thresholdMinuteBandwidth ? 'hour': currentXBandwidth>=thresholdHourBandwidth ? 'day' : currentXBandwidth >= thresholdDayBandwidth ? 'month' : currentXBandwidth >= thresholdMonthBandwidth ? 'month' : 'year')"
},
{
"name": "xCurrentDateFormat",
"update": "['%H', '%d', currentXBandwidth >= 0.12 ? '%B %Y' : '%b', '%Y'][indexof(['hour', 'day', 'month', 'year'],xCurrentUnit)]"
},
{
"name": "xCurrentDateLabelDX",
"update": "xCurrentUnit === 'hour' ? ((scale('x', utchours(0,0,0,0))-scale('x', timeOffset('hours', utchours(0,0,0,0), -1)))/2) : (scale('x', now())-scale('x', timeOffset(xCurrentUnit, now(), -1)))/2"
},
{
"name": "xCurrentUnitOffset",
"update": "xCurrentUnit === 'year' ? null : ['hour', 'day', 'month', 'year'][indexof(['hour','day', 'month'], xCurrentUnit)+1]"
},
{
"name": "xCurrentDateFormatOffset",
"update": "['%H', '%d %B %Y', '%B %Y', '%Y'][indexof(['hour', 'day', 'month', 'year'],xCurrentUnitOffset)]"
},
{
"name": "xCurrentDateOffsetLabelDX",
"update": "xCurrentUnitOffset === 'hour' ? ((scale('x', utchours(0,0,0,0))-scale('x', timeOffset('hours', utchours(0,0,0,0), -1)))/2) : (scale('x', now())-scale('x', timeOffset(xCurrentUnitOffset, now(), -1)))/2"
},
{"name": "miniMapHeight", "value": 10},
{
"name": "ganttMiniMapMouseDown",
"description": "boolean indicating whether the horizontal mini-map scrollbar is currently being clicked",
"value": false,
"on": [
{
"events": "@gantt-mini-map-interactive-rect:pointerdown",
"update": "true"
},
{"events": {"type": "pointerup"}, "update": "false"}
]
},
{
"name": "ganttMiniMapX",
"value": null,
"on": [
{
"events": "@gantt-mini-map-interactive-rect:pointerdown",
"update": "1-((domain('xScaleMiniMap')[1]- +invert('xScaleMiniMap', x(group())))/span(domain('xScaleMiniMap')))"
},
{"events": {"type": "pointerup"}, "update": "null"}
]
},
{
"name": "ganttMiniMapMouseOver",
"description": "boolean indicating whether the horizontal mini-map scrollbar is currently being moused over",
"value": false,
"on": [
{
"events": "@gantt-mini-map-interactive-rect:mouseover",
"update": "true"
},
{
"events": "@gantt-mini-map-interactive-rect:mouseout",
"update": "false"
}
]
},
{"name": "width", "update": "desiredWidth"},
{
"name": "padding",
"update": "{'top': 5, 'right': 5, 'bottom': ganttMiniMapMouseDown ? 0 : 5, 'left': 5}"
},
{"name": "showDebugArea", "value": false}
],
"marks": [
{
"name": "group-header",
"type": "group",
"encode": {
"update": {
"x": {"value": 0},
"width": {"signal": "desiredWidth"},
"height": {"signal": "20"},
"y": {"value": -50}
}
},
"marks": [
{
"name": "group_mouseWheelGranularity",
"description": "group for the marks that make up the toggle control to hide/show details",
"type": "group",
"interactive": false,
"clip": false,
"encode": {
"update": {
"y": {"value": 0},
"x": {"signal": "columnsWidth+ganttWidth-2.5"}
}
},
"marks": [
{
"name": "track_rect",
"description": "the track for the toggle control",
"type": "rect",
"interactive": false,
"encode": {
"update": {
"y": {
"signal": "configToggleMouseWheelGranularity.track.height/2"
},
"height": {
"signal": "configToggleMouseWheelGranularity.track.height"
},
"x": {
"signal": "-configToggleMouseWheelGranularity.track.width-5"
},
"width": {
"signal": "configToggleMouseWheelGranularity.track.width"
},
"cornerRadius": {
"signal": "configToggleMouseWheelGranularity.track.cornerRadius"
},
"fill": {
"signal": "mouseWheelGranularity ? configToggleMouseWheelGranularity.on.fill : configToggleMouseWheelGranularity.track.fill"
},
"stroke": {
"signal": "configToggleMouseWheelGranularity.track.stroke"
},
"strokeWidth": {
"signal": "configToggleMouseWheelGranularity.track.strokeWidth"
}
}
}
},
{
"name": "toggle_outer_arc",
"description": "the circle mark that serves as the the 'toggle handle'",
"from": {"data": "track_rect"},
"type": "arc",
"interactive": false,
"encode": {
"enter": {
"y": {
"signal": "datum.bounds.y1+configToggleMouseWheelGranularity.track.height/2"
},
"innerRadius": {"value": 0},
"outerRadius": {
"signal": "configToggleMouseWheelGranularity.track.height*0.9"
},
"startAngle": {"signal": "0"},
"endAngle": {"signal": "2*PI"},
"stroke": {
"signal": "configToggleMouseWheelGranularity.handle.stroke || '#BBB'"
},
"strokeWidth": {
"signal": "configToggleMouseWheelGranularity.handle.strokeWidth"
},
"fill": {
"signal": "configToggleMouseWheelGranularity.handle.fill || '#fff'"
}
},
"update": {
"x": {
"signal": "mouseWheelGranularity ? datum.bounds.x2-configToggleMouseWheelGranularity.track.height*0.9 : datum.bounds.x1+configToggleMouseWheelGranularity.track.height*0.9"
}
}
}
},
{
"name": "mouseWheelGranularity_text",
"description": "the title for the toggle control",
"type": "text",
"from": {"data": "track_rect"},
"interactive": false,
"encode": {
"update": {
"x": {"signal": "datum.bounds.x1"},
"dx": {
"signal": "-configToggleMouseWheelGranularity.label.dx"
},
"y": {
"signal": "datum.bounds.y1+(datum.bounds.y2-datum.bounds.y1)/2"
},
"text": {
"signal": "configToggleMouseWheelGranularity.label.text || ''"
},
"baseline": {"value": "middle"},
"font": {
"signal": "configToggleMouseWheelGranularity.label.font"
},
"fontSize": {
"signal": "configToggleMouseWheelGranularity.label.fontSize"
},
"fontStyle": {
"signal": "configToggleMouseWheelGranularity.label.fontStyle"
},
"align": {"value": "right"},
"fill": {
"signal": "configToggleMouseWheelGranularity.label.fill"
}
}
}
}
]
},
{
"name": "mouseWheel-granularity-clickable-rect",
"type": "rect",
"from": {"data": "group_mouseWheelGranularity"},
"interactive": true,
"encode": {
"update": {
"y": {"signal": "datum.bounds.y1"},
"y2": {"signal": "datum.bounds.y2"},
"x": {"signal": "datum.bounds.x1"},
"x2": {"signal": "datum.bounds.x2"},
"fill": {"value": "transparent"},
"tooltip": {
"signal": "configToggleMouseWheelGranularity.tooltip.object"
},
"cursor": {"value": "pointer"}
}
}
}
]
},
{
"name": "rect-background",
"type": "rect",
"encode": {
"update": {
"width": {"signal": "desiredWidth-1"},
"height": {"signal": "height"},
"fill": {"value": "transparent"},
"stroke": {"value": "#AAA"},
"strokeWidth": {"value": 0.1}
}
}
},
{
"name": "rect-gantt-background",
"type": "rect",
"encode": {
"update": {
"x": {"signal": "columnsWidth"},
"width": {"signal": "ganttWidth-1"},
"height": {"signal": "height"},
"fill": {"value": "transparent"},
"stroke": {"value": "#AAA"},
"strokeWidth": {"value": 0.1}
}
}
},
{
"name": "group-gantt-mini-map",
"type": "group",
"encode": {
"update": {
"x": {"signal": "ganttMiniMapMouseDown ? 0 : 60"},
"y": {"signal": "ganttMiniMapMouseDown ? 0 : height+5"}
}
},
"marks": [
{
"name": "track",
"type": "rect",
"encode": {
"update": {
"x": {"signal": "ganttMiniMapMouseDown ? 60 : 0"},
"width": {"signal": "range('xScaleMiniMap')[1]"},
"y": {
"signal": "(ganttMiniMapMouseDown ? height+5 : 0) + miniMapHeight/2",
"offset": -0.15
},
"height": {"value": 0.3},
"fill": {"value": "#AAA"},
"opacity": {
"signal": "scale('xScaleMiniMap', xDomain[0]) === range('xScaleMiniMap')[0] ? 0 : 1"
}
}
}
},
{
"name": "rect-domain-max-brush",
"type": "rect",
"from": {"data": "track"},
"encode": {
"update": {
"y": {"signal": "datum.bounds.y1 - miniMapHeight/2"},
"x": {
"scale": "xScaleMiniMap",
"signal": "xDomain[0]",
"offset": {"signal": "(ganttMiniMapMouseDown ? 60 : 0) -0.5"}
},
"x2": {
"scale": "xScaleMiniMap",
"signal": "xDomain[1]",
"offset": {"signal": "(ganttMiniMapMouseDown ? 60 : 0) + 0.5"}
},
"height": {"signal": "miniMapHeight"},
"fill": {"signal": "ganttMiniMapMouseOver ? '#eee' : '#fff'"},
"opacity": {
"signal": "scale('xScaleMiniMap', xDomain[0]) === range('xScaleMiniMap')[0] ? 0 : 1"
}
}
}
},
{
"name": "time-series-rect",
"type": "rect",
"from": {"data": "track"},
"encode": {
"update": {
"x": {
"signal": "datum.bounds.x1+scale('xScaleMiniMap', xDomainInitial[0])"
},
"x2": {
"signal": "datum.bounds.x1+scale('xScaleMiniMap', xDomainInitial[1])"
},
"y": {
"signal": "datum.bounds.y1+(datum.bounds.y2-datum.bounds.y1)/2"
},
"y2": {
"signal": "datum.bounds.y1+(datum.bounds.y2-datum.bounds.y1)/2"
},
"strokeWidth": {"signal": "miniMapHeight/2"},
"stroke": {"value": "#BCC9D4"},
"fill": {"value": "transparent"},
"cursor": {
"signal": "(ganttMiniMapMouseOver || ganttMiniMapMouseDown) && scale('xScaleMiniMap', xDomain[0]) !== range('xScaleMiniMap')[0] ? 'pointer' : 'default'"
}
}
}
},
{
"name": "rect-domain-min-tick",
"from": {"data": "rect-domain-max-brush"},
"type": "rect",
"encode": {
"update": {
"x": {"signal": "datum.bounds.x1"},
"y": {"signal": "datum.bounds.y1"},
"width": {"value": 1},
"height": {"signal": "miniMapHeight"},
"fill": {"value": "#000"},
"opacity": {
"signal": "xDomain[0] <= scrollDomain[0] && xDomain[1] >= scrollDomain[1] ? 0 : 1"
}
}
}
},
{
"name": "rect-domain-max-tick",
"from": {"data": "rect-domain-max-brush"},
"type": "rect",
"encode": {
"update": {
"x": {"signal": "datum.bounds.x2"},
"y": {"signal": "datum.bounds.y1"},
"width": {"value": 1},
"height": {"signal": "miniMapHeight"},
"fill": {"value": "#000"},
"opacity": {
"signal": "xDomain[0] <= scrollDomain[0] && xDomain[1] >= scrollDomain[1] ? 0 : 1"
}
}
}
},
{
"name": "text-date-unit-label",
"type": "text",
"from": {"data": "track"},
"encode": {
"update": {
"x": {"signal": "datum.bounds.x1-60+5"},
"y": {"signal": "datum.bounds.y1"},
"baseline": {"value": "middle"},
"text": {
"signal": "upper(substring(xCurrentUnit, 0, 1))+substring(xCurrentUnit, 1, length(xCurrentUnit))+' View'"
},
"fontSize": {"value": 9},
"fill": {"value": "#666"}
}
}
},
{
"name": "text-date-granularity-reset-label",
"type": "text",
"from": {"data": "track"},
"encode": {
"update": {
"x": {"signal": "datum.bounds.x2+10"},
"y": {"signal": "datum.bounds.y1"},
"baseline": {"value": "middle"},
"text": {
"signal": "'Reset'"
},
"fontSize": {"value": 9},
"fill": {"value": "#666"}
}
}
},
{
"name": "gantt-mini-map-interactive-rect",
"type": "rect",
"from": {"data": "track"},
"encode": {
"update": {
"x": {"signal": "ganttMiniMapMouseDown ? 0 : datum.bounds.x1"},
"x2": {
"signal": "(ganttMiniMapMouseDown ? padding.right*4 : 0) + datum.bounds.x2"
},
"y": {
"signal": "ganttMiniMapMouseDown ? 0 : datum.bounds.y1 - miniMapHeight/2-5"
},
"y2": {
"signal": "datum.bounds.y2 + miniMapHeight/2+10 + (ganttMiniMapMouseDown ? 5 : 0)"
},
"fillOpacity": {"value": 0},
"cursor": {
"signal": "(ganttMiniMapMouseOver || ganttMiniMapMouseDown) && scale('xScaleMiniMap', xDomain[0]) !== range('xScaleMiniMap')[0] ? 'pointer' : 'default'"
}
}
}
}
]
},
{
"name": "footer-divider",
"type": "rect",
"interactive": false,
"encode": {
"update": {
"x": {"value": 0},
"width": {"signal": "showDebugArea ? desiredWidth : 0"},
"y": {"signal": "showDebugArea ? height+40 : 0"},
"height": {"value": 0},
"stroke": {"value": "#000"},
"strokeWidth": {"signal": "showDebugArea ? 0.5 : 0"}
}
}
},
{
"name": "config-group",
"type": "group",
"from": {"data": "footer-divider"},
"interactive": false,
"encode": {"update": {"y": {"signal": "datum.bounds.y2", "offset": 5}}},
"marks": [
{
"name": "config-labels",
"from": {"data": "configurations"},
"type": "text",
"encode": {
"update": {
"y": {"signal": "showDebugArea ? (datum.index-1)*14 : 0"},
"text": {"signal": "showDebugArea ? datum.label : null"},
"baseline": {"value": "top"},
"fill": {"value": "#333"}
}
}
},
{
"name": "config-values",
"from": {"data": "config-labels"},
"type": "text",
"encode": {
"update": {
"x": {"signal": "datum.bounds.x2", "offset": 5},
"y": {"signal": "showDebugArea ? datum.y : null"},
"text": {"signal": "showDebugArea ? datum.datum.value : null"},
"baseline": {"value": "top"},
"fontWeight": {"value": 600}
}
}
}
]
},
{
"name": "properties-group",
"type": "group",
"from": {"data": "footer-divider"},
"interactive": false,
"encode": {
"update": {
"x": {"signal": "desiredWidth/2"},
"y": {"signal": "datum.bounds.y2", "offset": 5}
}
},
"marks": [
{
"name": "property-labels",
"from": {"data": "properties"},
"type": "text",
"encode": {
"update": {
"y": {"signal": "showDebugArea ? (datum.index-1)*14 : 0"},
"text": {"signal": "showDebugArea ? datum.label : null"},
"baseline": {"value": "top"},
"fill": {"value": "#333"}
}
}
},
{
"name": "property-values",
"from": {"data": "property-labels"},
"type": "text",
"encode": {
"update": {
"x": {"signal": "datum.bounds.x2", "offset": 5},
"y": {"signal": "showDebugArea ? datum.y : 0"},
"text": {"signal": "showDebugArea ? datum.datum.value : null"},
"baseline": {"value": "top"},
"fontWeight": {"value": 600}
}
}
}
]
}
],
"scales": [
{
"name": "dateUnitIncrementMS",
"type": "ordinal",
"domain": ["hours", "day", "month", "year"],
"range": [3600000, 86400000, 2628000000, 31540000000]
},
{
"name": "x",
"type": "time",
"domain": {"signal": "xDomain"},
"range": {"signal": "[0,ganttWidth]"}
},
{
"name": "scaleXScroll",
"type": "linear",
"clamp": true,
"domain": {"signal": "[0, 1]"},
"range": {"signal": "scrollDomain"}
},
{
"name": "xScaleMiniMap",
"type": "time",
"domain": {
"signal": "[initialDomainCenter - xDomainBounds.max/2, initialDomainCenter + xDomainBounds.max/2]"
},
"range": {"signal": "[0, ganttWidth-122]"}
}
],
"axes": [
{
"description": "Bottom date axis",
"ticks": true,
"labelPadding": {"signal": "xCurrentDateFormat === '%B %Y' ? 2 :-12"},
"scale": "x",
"position": {"signal": "columnsWidth"},
"orient": "top",
"domainColor": "#CCC",
"domainWidth": 0.25,
"tickSize": 15,
"tickOpacity": 1,
"grid": true,
"zindex": -1,
"labelOverlap": true,
"formatType": "time",
"labelBound": true,
"tickCount": {
"signal": "xCurrentUnit === 'hour' ? 24*(span(xDomain)/86400000) : xCurrentUnit"
},
"format": {"signal": "xCurrentDateFormat"},
"labelSeparation": 3,
"tickExtra": true,
"encode": {
"labels": {
"update": {
"dx": {"signal": "xCurrentDateLabelDX"},
"text": {
"signal": "xCurrentDateFormat === '%B %Y' ? split(datum.label, ' ') : datum.label"
}
}
},
"ticks": {
"update": {
"stroke": {"value": "#CCC"},
"strokeWidth": {
"signal": "xCurrentUnit === 'day' && datum.label === '01' ? 0 : indexof(datum.label, 'Jan') >= 0 ? 0 : 0.35"
}
}
},
"grid": {
"update": {
"stroke": {"value": "#CCC"},
"strokeWidth": {
"signal": "xCurrentUnit === 'day' && datum.label === '01' ? 0 : indexof(datum.label, 'Jan') >= 0 ? 0 : 0.35"
}
}
}
}
},
{
"description": "Top date axis",
"title": {
"signal": "xCurrentDateFormatOffset === '%B %Y' && (toDate(utcFormat((xDomain[0] + (xDomain[1]-xDomain[0])/2), '01-%B-%Y')) < domain('x')[0]) ? utcFormat((xDomain[0] + (xDomain[1]-xDomain[0])/2), '%B %Y') : null"
},
"titleX": {"signal": "ganttWidth/2"},
"titleY": -25,
"titleBaseline": {"value": "middle"},
"titleFontSize": {"value": 10},
"titleFontWeight": "400",
"scale": "x",
"position": {"signal": "columnsWidth"},
"domain": false,
"orient": "top",
"offset": 0,
"tickSize": 15,
"tickOpacity": 1,
"tickWidth": 0.5,
"tickColor": "#666",
"labelBaseline": "bottom",
"grid": true,
"gridWidth": 0.5,
"gridColor": {"value": "#666"},
"zindex": 1,
"labelPadding": 5,
"labelOverlap": true,
"formatType": "time",
"labelBound": true,
"tickCount": {"signal": "(xCurrentUnitOffset || 0)"},
"format": {"signal": "xCurrentDateFormatOffset"},
"labelSeparation": 5,
"encode": {
"labels": {
"update": {
"text": {
"signal": "xCurrentDateFormat === '%B %Y' ? null : datum.label"
},
"x": {
"signal": "xCurrentDateFormatOffset === '%Y' ? toDate(utcFormat(datum.value, '01-Jun-%Y')) > xDomain[1] ? scale('x', datum.value)+(scale('x', xDomain[1])-scale('x', datum.value))/2 : scale('x', toDate(utcFormat(datum.value, '01-Jun-%Y'))) : xCurrentDateFormatOffset === '%B %Y' ? toDate(utcFormat(datum.value, '17-%b-%Y')) > xDomain[1] ? scale('x', datum.value)+(scale('x', xDomain[1])-scale('x', datum.value))/2 : scale('x', toDate(utcFormat(datum.value, '15-%b-%Y'))) : scale('x', datum.value)"
},
"dx": {
"signal": "xCurrentDateFormatOffset === '%Y' || xCurrentDateFormatOffset === '%B %Y' ? 0 : xCurrentDateOffsetLabelDX"
},
"align": {"value": "center"}
}
}
}
}
],
"data": [
{
"name": "configurations",
"values": [{"label": null, "value": null}],
"transform": [
{
"type": "formula",
"expr": "['Configurations (for testing purposes only): ', '• Desired Canvas Width: ' + desiredWidth + 'px', '• Columns Percent of Width: ' + format(columnsWidthPercent, '.0%'), '• Initial Date Unit Offset: ' + offsetUnit, '• Initial Date Domain: ' + utcFormat(xDomainInitial[0], '%d-%b-%y %H:%M') + ' - ' + utcFormat(xDomainInitial[1], '%d-%b-%y %H:%M'), '• Record Count in Dataset: ' + recordCount, '• Block Height: ' + blockHeight]",
"as": "label"
},
{"type": "flatten", "fields": ["label"]},
{"type": "formula", "expr": "split(datum.label, ': ')", "as": "label"},
{
"type": "formula",
"expr": "trim(datum.label[1]) === '' ? null : trim(datum.label[1])",
"as": "value"
},
{"type": "formula", "expr": "datum.label[0]+':'", "as": "label"},
{"type": "window", "ops": ["row_number"], "as": ["index"]}
]
},
{
"name": "properties",
"values": [{"label": null, "value": null}],
"transform": [
{
"type": "formula",
"expr": "['Properties (for testing purposes only): ', '• Current Domain: '+utcFormat(xDomain[0], '%d-%b-%y %H:%M') + ' - ' + utcFormat(xDomain[1], '%d-%b-%y %H:%M'), '• Current Date Unit: '+xCurrentUnit, '• Current Domain Span: '+ format(span(xDomain)/scale('dateUnitIncrementMS', replace(xCurrentUnit, 'hour', 'hours')), '.2f') + ' ' + xCurrentUnit+('s'), '• Current Domain Percentage: '+format(span(xDomain)/(xDomainBounds.max-xDomainBounds.min), '.1%')]",
"as": "label"
},
{"type": "flatten", "fields": ["label"]},
{"type": "formula", "expr": "split(datum.label, ': ')", "as": "label"},
{
"type": "formula",
"expr": "trim(datum.label[1]) === '' ? null : trim(datum.label[1])",
"as": "value"
},
{"type": "formula", "expr": "datum.label[0]+':'", "as": "label"},
{"type": "window", "ops": ["row_number"], "as": ["index"]}
]
},
{
"name": "dataset",
"values": [{"startDate": null}],
"transform": [
{"type": "formula", "expr": "offsetUnit", "as": "offsetUnit"},
{
"type": "formula",
"expr": "negativeDateOffsetFromToday",
"as": "negativeDateOffsetFromToday"
},
{
"type": "formula",
"expr": "positiveOffsetFromToday",
"as": "positiveOffsetFromToday"
},
{"type": "formula", "expr": "recordCount", "as": "recordCount"},
{"type": "formula", "expr": "todayDate", "as": "startDate"},
{
"type": "formula",
"expr": "timeOffset(datum.offsetUnit, datum.startDate, datum.negativeDateOffsetFromToday)",
"as": "startDate"
},
{
"type": "formula",
"expr": "timeOffset(datum.offsetUnit, todayDate, datum.positiveOffsetFromToday)",
"as": "endDate"
},
{
"type": "formula",
"expr": "(datum.endDate-datum.startDate)/datum.recordCount",
"as": "recordIncrement"
},
{
"type": "formula",
"expr": "sequence(+datum.startDate+datum.recordIncrement/2, +datum.endDate+datum.recordIncrement, datum.recordIncrement/2)",
"as": "endDate"
},
{"type": "flatten", "fields": ["endDate"], "as": ["endDate"]},
{"type": "formula", "expr": "+datum.startDate", "as": "startDate"},
{"type": "window", "ops": ["row_number"], "as": ["index"]},
{
"type": "formula",
"expr": "datum.endDate<todayDate ? null : datum.endDate-todayDate",
"as": "afterTodayDuration"
},
{
"type": "window",
"ops": ["sum"],
"fields": ["afterTodayDuration"],
"as": ["aggAfterTodayDuration"]
},
{
"type": "formula",
"expr": "datum.afterTodayDuration === datum.aggAfterTodayDuration ? datum.startDate : (random()*(datum.endDate-datum.startDate))+datum.startDate",
"as": "startDate"
},
{
"type": "formula",
"expr": "utcFormat(datum.startDate, '%Y-%m-%dT%H:%M:%S.%LZ')",
"as": "startDateFormatted"
},
{
"type": "formula",
"expr": "utcFormat(datum.endDate, '%Y-%m-%dT%H:%M:%S.%LZ')",
"as": "endDateFormatted"
},
{
"type": "formula",
"expr": "datum.startDate+' '+datum.endDate",
"as": "sort"
},
{"type": "collect", "sort": {"field": "sort", "order": "ascending"}}
]
},
{
"name": "xDomainInitial",
"source": "dataset",
"transform": [
{
"type": "aggregate",
"fields": ["startDate", "endDate"],
"ops": ["min", "max"],
"as": ["minDate", "maxDate"]
},
{
"type": "formula",
"expr": "[datum.minDate-scale('dateUnitIncrementMS', 'hours'),datum.maxDate+scale('dateUnitIncrementMS', 'hours')]",
"as": "domain"
},
{
"type": "formula",
"expr": "[utcFormat(datum.domain[0], '%d-%b-%Y %H:%M:%S.%LZ'), utcFormat(datum.domain[1], '%d-%b-%Y %H:%M:%S.%LZ')]",
"as": "domainFormatted"
},
{
"type": "formula",
"expr": "[datum.minDate-scale('dateUnitIncrementMS', 'hours'), datum.minDate + ((ganttWidth-minDateBandwidth)/minDateBandwidth)*scale('dateUnitIncrementMS', 'hours')]",
"as": "hourDomain"
},
{
"type": "formula",
"expr": "[datum.minDate-scale('dateUnitIncrementMS', 'day'), datum.minDate + ((ganttWidth-minDateBandwidth)/minDateBandwidth)*scale('dateUnitIncrementMS', 'day')]",
"as": "dayDomain"
},
{
"type": "formula",
"expr": "ceil(span(datum.domain)/scale('dateUnitIncrementMS', 'year'))",
"as": "yearCount"
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment