Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alternate charts panning #5635

Closed
wants to merge 60 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
3dfc1dc
allow clicking and dragging
lovincyrus Aug 16, 2024
4df21b4
tweak brush opacity, comments
lovincyrus Aug 19, 2024
979cf21
resolve interval data, access brush signal listener
lovincyrus Aug 19, 2024
4dc8ad9
bind interval data to scrub range
lovincyrus Aug 19, 2024
d18472b
clean up
lovincyrus Aug 19, 2024
9337346
support brush on other alt charts
lovincyrus Aug 20, 2024
8863b49
brush color change
lovincyrus Aug 20, 2024
bdd9ab6
wip
lovincyrus Aug 20, 2024
bbe63e5
workaround for clearing brush interval on esc
lovincyrus Aug 21, 2024
a1882bf
debounce chart brush
lovincyrus Aug 21, 2024
14c260f
lint
lovincyrus Aug 21, 2024
89215be
check for not cmd/ctrl+z when zooming in on scrub
lovincyrus Aug 21, 2024
c1bfa9a
build simple bar multi layer, support full height hover
lovincyrus Aug 21, 2024
e0cb62b
remove axis title and reorient axis in multi layer simple bar
lovincyrus Aug 21, 2024
66a0070
wip
lovincyrus Aug 21, 2024
8fb0cc4
temp fix of chart type selector
lovincyrus Aug 21, 2024
7447103
first pass in vega renderer for vega signals support
lovincyrus Aug 22, 2024
3ecc2f9
improve brush param checks before compiling vega lite to vega
lovincyrus Aug 22, 2024
52e59f2
rename for clarity, fix brush end
lovincyrus Aug 22, 2024
018ee62
ha! vega signals like pointerupdown vs. mouseupdown
lovincyrus Aug 23, 2024
c8fc9fd
update brush for brushend signal, update issScrubbing
lovincyrus Aug 23, 2024
6f3579e
fix multi layer bar y axis formatting
lovincyrus Aug 23, 2024
1dc2f15
reinstate tooltip for vega
lovincyrus Aug 23, 2024
817f7b4
temp fix for band width in multi layer vega lite
lovincyrus Aug 23, 2024
2811730
fix undefined in isSignalEqual, clean up
lovincyrus Aug 23, 2024
6fe82f1
use band size and scale to tweak relative bar width
lovincyrus Aug 23, 2024
3b7b830
clean up
lovincyrus Aug 23, 2024
20458c5
highlight bars in interval selection
lovincyrus Aug 23, 2024
0f3d970
remove global mark tooltip from vega config
lovincyrus Aug 23, 2024
47ab1aa
prettier
lovincyrus Aug 23, 2024
0fed1d0
adaptive scrub range to display range on every brush event
lovincyrus Aug 23, 2024
8cac8ac
lint, explored conditional tooltip with arr values in vega
lovincyrus Aug 23, 2024
ef0e7ef
add brush to other stacked charts
lovincyrus Aug 24, 2024
78e620d
finalize esc to clear interval, move logic to vega signal manager
lovincyrus Aug 24, 2024
1c19f25
lint
lovincyrus Aug 24, 2024
ff33362
revert to single layer for simple bar usage
lovincyrus Aug 26, 2024
6d897d1
update simple bar styles
lovincyrus Aug 26, 2024
064d252
fix brush clear isScrubbing
lovincyrus Aug 26, 2024
47bce99
update isScrubbing directly instead of passing through signal listeners
lovincyrus Aug 26, 2024
ba29b78
fix updateVegaOnTableHover
lovincyrus Aug 26, 2024
0d348f3
fix single layer simple bar brush on/off color
lovincyrus Aug 26, 2024
667d948
fix racy conditions, clean up
lovincyrus Aug 27, 2024
8f52d9a
clear scrub range when chart type changes, lint
lovincyrus Aug 27, 2024
38c124c
fix clear interval when switching chart type
lovincyrus Aug 27, 2024
6c96239
configure other chart types brush color, lint
lovincyrus Aug 28, 2024
b4e1666
fix brush opacity, address pr
lovincyrus Sep 3, 2024
c7c4599
clean up code for resetting scrub when changing chart type
lovincyrus Sep 3, 2024
109ab81
remove previous racy prevention, in favor of runAsync
lovincyrus Sep 3, 2024
20ec4c2
remove deadcode
lovincyrus Sep 3, 2024
b9f081d
lint
lovincyrus Sep 3, 2024
ca7cdce
clean up
lovincyrus Sep 3, 2024
9959209
coerce customFormatTypes in compiled spec, fix warning
lovincyrus Sep 3, 2024
7d46e48
clean up
lovincyrus Sep 3, 2024
216d95a
initial panning
lovincyrus Sep 6, 2024
169fcc7
hoist to pan left and right icon, wip panning
lovincyrus Sep 6, 2024
47c79e0
wire up updatePanRange
lovincyrus Sep 6, 2024
d37888a
position pan icons
lovincyrus Sep 6, 2024
2dfc67d
vertical align pan icons
lovincyrus Sep 12, 2024
3cb2f24
wip
lovincyrus Sep 13, 2024
d100c4c
wip
lovincyrus Sep 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions web-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"ua-parser-js": "^1.0.2",
"uuid": "^9.0.0",
"vega-lite": "^5.17.0",
"vega-typings": "^1.3.1",
"vitest": "^0.31.0",
"yaml": "^2.4.5",
"yup": "^1.4.0"
Expand Down
11 changes: 11 additions & 0 deletions web-common/src/components/icons/LeftPanIcon.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script lang="ts">
export let className = "";
</script>

<svg viewBox="0 0 33 33" xmlns="http://www.w3.org/2000/svg" class={className}>
<path
class="pan-button"
role="presentation"
d="M9.335 16.795L21.678 5.756C22.129 5.352 22.844 5.672 22.844 6.277L22.844 27.342C22.844 27.948 22.128 28.268 21.677 27.863L9.335 16.795Z"
/>
</svg>
11 changes: 11 additions & 0 deletions web-common/src/components/icons/RightPanIcon.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script lang="ts">
export let className = "";
</script>

<svg viewBox="0 0 33 33" xmlns="http://www.w3.org/2000/svg" class={className}>
<path
class="pan-button"
role="presentation"
d="M24.265 16.805L11.922 27.844C11.471 28.248 10.756 27.928 10.756 27.323L10.756 6.258C10.756 5.652 11.472 5.332 11.923 5.737L24.265 16.805Z"
/>
</svg>
4 changes: 2 additions & 2 deletions web-common/src/features/charts/render/VegaLiteRenderer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
export let customDashboard = false;
export let chartView = false;
export let tooltipFormatter: VLTooltipFormatter | undefined = undefined;
// Bind view to parent component
export let viewVL: View;

let contentRect = new DOMRect(0, 0, 0, 0);
Expand All @@ -33,7 +32,8 @@
$: if (viewVL && tooltipFormatter) {
const handler = new VegaLiteTooltipHandler(tooltipFormatter);
viewVL.tooltip(handler.handleTooltip);
viewVL.runAsync();
// https://stackoverflow.com/questions/59255654/vega-wont-update-until-the-mouse-has-brushed-over-the-div-containing-the-chart
void viewVL.runAsync();
}

$: options = <EmbedOptions>{
Expand Down
194 changes: 194 additions & 0 deletions web-common/src/features/charts/render/VegaRenderer.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
<script lang="ts">
import CancelCircle from "@rilldata/web-common/components/icons/CancelCircle.svelte";
import { getRillTheme } from "@rilldata/web-common/features/charts/render/vega-config";
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";
import { get } from "svelte/store";
import {
SignalListeners,
Vega,
View,
VisualizationSpec,
type EmbedOptions,
} from "svelte-vega";
import { ExpressionFunction, VLTooltipFormatter } from "../types";
import { VegaLiteTooltipHandler } from "./vega-tooltip";
import { createEventDispatcher } from "svelte";
import { getStateManagers } from "../../dashboards/state-managers/state-managers";
import { PanDirection } from "../../dashboards/time-dimension-details/types";
import LeftPanIcon from "@rilldata/web-common/components/icons/LeftPanIcon.svelte";
import RightPanIcon from "@rilldata/web-common/components/icons/RightPanIcon.svelte";

export let data: Record<string, unknown> = {};
export let spec: VisualizationSpec;
export let signalListeners: SignalListeners = {};
export let expressionFunctions: ExpressionFunction = {};
export let error: string | null = null;
export let customDashboard = false;
export let chartView = false;
export let tooltipFormatter: VLTooltipFormatter | undefined = undefined;
export let view: View;

let contentRect = new DOMRect(0, 0, 0, 0);
let jwt = get(runtime).jwt;

const dispatch = createEventDispatcher();
const {
selectors: {
charts: { canPanLeft, canPanRight, getNewPanRange },
},
} = getStateManagers();

$: width = contentRect.width;
$: height = contentRect.height * 0.95 - 80;

$: if (view && tooltipFormatter) {
const handler = new VegaLiteTooltipHandler(tooltipFormatter);
view.tooltip(handler.handleTooltip);
// https://stackoverflow.com/questions/59255654/vega-wont-update-until-the-mouse-has-brushed-over-the-div-containing-the-chart
void view.runAsync();
}

$: options = <EmbedOptions>{
config: getRillTheme(),
renderer: "svg",
actions: false,
logLevel: 0, // only show errors
width: customDashboard ? width : undefined,
expressionFunctions,
height: chartView || !customDashboard ? undefined : height,
loader: {
baseURL: `${get(runtime).host}/v1/instances/${get(runtime).instanceId}/assets/`,
...(jwt &&
jwt.token && {
http: {
headers: {
Authorization: `Bearer ${jwt.token}`,
},
},
}),
},
};

const onError = (e: CustomEvent<{ error: Error }>) => {
error = e.detail.error.message;
};

function panCharts(direction: PanDirection) {
const panRange = $getNewPanRange(direction);
if (!panRange) return;
const { start, end } = panRange;
dispatch("pan", { start, end });
}

let showControls = true;

function handleMouseEnter() {
showControls = true;
}

function handleMouseLeave() {
showControls = true;
}

$: midY = contentRect.height / 2;
$: translateXLeft = `translateX(-12px)`;
$: translateXRight = `translateX(20px)`;
</script>

<div
bind:contentRect
class:bg-white={customDashboard}
class:px-4={customDashboard}
class:pb-2={customDashboard}
class="vega-renderer no-scrollbars size-full flex flex-col items-center justify-center relative mr-6"
data-width={width}
data-height={height}
on:mouseenter={handleMouseEnter}
on:mouseleave={handleMouseLeave}
role="figure"
>
{#if error}
<div
class="size-full text-[3.2em] flex flex-col items-center justify-center gap-y-2"
>
<CancelCircle />
{error}
</div>
{:else}
<Vega
{data}
{spec}
{signalListeners}
{options}
bind:view
on:onError={onError}
/>
{#if showControls}
<div class="vega-pan-controls">
{#if $canPanLeft}
<button
class="pan-button absolute left-0 -translate-y-1/2 w-8 h-8 pointer-events-auto cursor-pointer fill-slate-400 hover:fill-slate-300"
style="top: {midY}px; transform: ${translateXLeft};"
on:click={() => panCharts("left")}
aria-label="Pan left"
>
<LeftPanIcon />
</button>
{/if}
{#if $canPanRight}
<button
class="pan-button absolute right-0 -translate-y-1/2 w-8 h-8 pointer-events-auto cursor-pointer fill-slate-400 hover:fill-slate-300"
style="top: {midY}px; transform: {translateXRight};"
on:click={() => panCharts("right")}
aria-label="Pan right"
>
<RightPanIcon />
</button>
{/if}
</div>
{/if}
{/if}
</div>

<style lang="postcss">
:global(.vega-embed) {
width: 100%;
}

:global(#rill-vg-tooltip) {
@apply absolute border border-slate-300 p-3 rounded-lg pointer-events-none;
background: white;
& h2 {
@apply text-slate-500 text-sm font-semibold mb-2;
}

& table {
@apply border-spacing-0;
}

& td {
@apply truncate py-0.5;
}

& td.key {
@apply text-left px-1 font-normal truncate;
max-width: 250px;
}

& td.value {
@apply text-left truncate font-semibold ui-copy-number;
max-width: 250px;
}
}

.no-scrollbars {
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* Internet Explorer and Edge */
}

.no-scrollbars::-webkit-scrollbar {
width: 0px;
height: 0px;
background: transparent; /* Chrome/Safari/Webkit */
}
</style>
3 changes: 0 additions & 3 deletions web-common/src/features/charts/render/vega-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ export const getRillTheme: () => Config = () => ({
autosize: {
type: "fit-x",
},
mark: {
tooltip: true,
},
arc: { fill: defaultMarkColor },
area: {
line: { stroke: MainLineColor, strokeWidth: 1 },
Expand Down
37 changes: 37 additions & 0 deletions web-common/src/features/charts/render/vega-signals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* Util methods for handling vega signals
*/

import { TimeRange } from "@rilldata/web-common/lib/time/types";

export function resolveSignalField(value: unknown, field: string) {
if (typeof value === "object" && value !== null) {
return Array.isArray(value[field]) ? value[field][0] : undefined;
Expand All @@ -27,3 +29,38 @@ export function resolveSignalTimeField(value: unknown) {
}
return undefined;
}

export function resolveSignalIntervalField(
value: unknown,
): TimeRange | undefined {
/**
* Time range fields can be either 'ts' or end with '_ts'
* We check for both cases and return a TimeRange if a valid array of two timestamps is found.
*/
if (typeof value === "object" && value !== null) {
const checkAndCreateTimeRange = (arr: unknown): TimeRange | undefined => {
if (Array.isArray(arr) && arr.length === 2) {
const [start, end] = arr;
return {
start: new Date(start),
end: new Date(end),
};
}
return undefined;
};

// Check for 'ts' key first
if ("ts" in value) {
return checkAndCreateTimeRange(value["ts"]);
}

// If 'ts' is not found, check for keys ending with '_ts'
for (const key in value) {
if (key.endsWith("_ts")) {
const timeRange = checkAndCreateTimeRange(value[key]);
if (timeRange) return timeRange;
}
}
}
return undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@
function handleChartTypeChange(type: TDDChart, isDisabled: boolean) {
if (isDisabled) return;
metricsExplorerStore.setTDDChartType(metricViewName, type);

// Reset scrub range when chart type changes
if (type !== chartType) {
metricsExplorerStore.setSelectedScrubRange(metricViewName, undefined);
}
}

// switch to default if current selected chart is not available
Expand All @@ -51,13 +56,12 @@
{#each chartTypeTabs as { label, id, Icon } (label)}
{@const active = chartType === id}
{@const disabled = !hasComparison && comparisonCharts.includes(id)}
<div class:bg-primary-100={active} class="chart-icon-wrapper">
<IconButton
{disabled}
disableHover
tooltipLocation="top"
on:click={() => handleChartTypeChange(id, disabled)}
>
<button
class:bg-primary-100={active}
class="chart-icon-wrapper"
on:click={() => handleChartTypeChange(id, disabled)}
>
<IconButton {disabled} disableHover tooltipLocation="top">
<Icon
primaryColor={disabled ? "#9CA3AF" : "var(--color-primary-700)"}
secondaryColor={disabled ? "#CBD5E1" : "var(--color-primary-300)"}
Expand All @@ -67,7 +71,7 @@
{label}
</svelte:fragment>
</IconButton>
</div>
</button>
{/each}
</div>

Expand Down
Loading