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

Update custom dashboard integration to new spec #4754

Merged
merged 2 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 6 additions & 10 deletions web-common/src/features/custom-dashboards/Chart.svelte
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
<script lang="ts">
import { useChart } from "@rilldata/web-common/features/charts/selectors";
import { createRuntimeServiceGetChartData } from "@rilldata/web-common/runtime-client/manual-clients";
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";
import { queryClient } from "@rilldata/web-common/lib/svelte-query/globalQueryClient";
import type { VisualizationSpec } from "svelte-vega";
import VegaLiteRenderer from "../charts/render/VegaLiteRenderer.svelte";
import { V1ComponentSpecResolverProperties } from "@rilldata/web-common/runtime-client";

export let chartName: string;
export let chartView = false;
export let vegaSpec: string;
export let resolverProperties: V1ComponentSpecResolverProperties;

let error: string | null = null;
let parsedVegaSpec: VisualizationSpec | null = null;

$: chart = useChart($runtime.instanceId, chartName);

$: vegaSpec = $chart?.data?.component?.spec?.rendererProperties?.spec;

$: try {
parsedVegaSpec = vegaSpec
? (JSON.parse(vegaSpec) as VisualizationSpec)
Expand All @@ -25,24 +23,22 @@
error = JSON.stringify(e);
}

$: metricsQuery = $chart?.data?.component?.spec?.resolverProperties;

$: chartDataQuery = createRuntimeServiceGetChartData(
queryClient,
$runtime.instanceId,
chartName,
metricsQuery,
resolverProperties,
);

$: data = $chartDataQuery?.data;
</script>

{#if parsedVegaSpec}
<VegaLiteRenderer
{chartView}
customDashboard
{error}
{chartView}
data={{ table: data }}
spec={parsedVegaSpec}
{error}
/>
{/if}
42 changes: 31 additions & 11 deletions web-common/src/features/custom-dashboards/Component.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<script lang="ts" context="module">
import { onMount } from "svelte";
import { writable } from "svelte/store";
import type ResizeHandle from "./ResizeHandle.svelte";
import type { ComponentType } from "svelte";

import { builderActions, getAttrs, type Builder } from "bits-ui";

const zIndex = writable(0);
import Chart from "./Chart.svelte";
import Markdown from "./Markdown.svelte";
import {
ResourceKind,
useResource,
} from "../entity-management/resource-selectors";

const options = [0, 0.5, 1];
const allSides = options
Expand All @@ -15,6 +17,7 @@
</script>

<script lang="ts">
export let i: number;
export let builders: Builder[] = [];
export let left: number;
export let top: number;
Expand All @@ -26,16 +29,25 @@
export let interacting = false;
export let width: number;
export let height: number;
export let i: number;
export let localZIndex = 0;
export let chartView = false;
export let componentName: string;
export let instanceId: string;

$: resourceQuery = useResource(
instanceId,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats the idea of getting the instance id in the params? We have the global to avoid prop drilling.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing hard and fast here. Just an instinct to get implementation details out of what are essentially display components. The runtime global is likely to change structure in the future. It also just means that only one component is subscribing to the store as opposed to however many dashboard elements there are. Not critical, though.

componentName,
ResourceKind.Component,
);

$: ({ data: componentResource } = $resourceQuery);

$: ({ renderer, rendererProperties, resolverProperties } =
componentResource?.component?.spec ?? {});

let ResizeHandleComponent: ComponentType<ResizeHandle>;

onMount(async () => {
localZIndex = $zIndex;
zIndex.set(++localZIndex);

if (!embed) {
ResizeHandleComponent = (await import("./ResizeHandle.svelte")).default;
}
Expand All @@ -60,11 +72,11 @@
>
<div class="size-full relative">
{#if ResizeHandleComponent && !embed}
{#each allSides as side, i (i)}
{#each allSides as side (side)}
<svelte:component
this={ResizeHandleComponent}
{scale}
{i}
{scale}
{side}
position={[left, top]}
dimensions={[width, height]}
Expand All @@ -79,7 +91,15 @@
class:shadow-lg={interacting}
style:border-radius="{radius}px"
>
<slot />
{#if renderer === "vega_lite" && rendererProperties?.spec && resolverProperties}
<Chart
vegaSpec={rendererProperties?.spec}
chartName={componentName}
{resolverProperties}
/>
{:else if renderer === "markdown" && rendererProperties?.content}
<Markdown markdown={rendererProperties.content} fontSize={20} />
{/if}
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,47 @@
<script lang="ts">
import { V1DashboardItem } from "@rilldata/web-common/runtime-client";
import Chart from "./Chart.svelte";
import * as defaults from "./constants";
import Wrapper from "./Wrapper.svelte";
import DashboardWrapper from "./DashboardWrapper.svelte";
import Component from "./Component.svelte";
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";

export let columns = 20;
export let components: V1DashboardItem[];
export let items: V1DashboardItem[];
export let gap = 4;
export let chartView = false;

let contentRect: DOMRectReadOnly = new DOMRectReadOnly(0, 0, 0, 0);

$: instanceId = $runtime.instanceId;

$: gridWidth = contentRect.width;
$: scale = gridWidth / defaults.DASHBOARD_WIDTH;
$: gapSize = defaults.DASHBOARD_WIDTH * (gap / 1000);
$: gridCell = defaults.DASHBOARD_WIDTH / columns;
$: radius = gridCell * defaults.COMPONENT_RADIUS;

$: maxBottom = components.reduce((max, el) => {
$: maxBottom = items.reduce((max, el) => {
const bottom = Number(el.height) + Number(el.y);
return Math.max(max, bottom);
}, 0);
</script>

<Wrapper
<DashboardWrapper
bind:contentRect
color="bg-slate-50"
height={maxBottom * gridCell}
{scale}
height={maxBottom * gridCell}
width={defaults.DASHBOARD_WIDTH}
>
{#each components as component, i (i)}
{#if component.component && typeof component.component === "string"}
{#each items as component, i (i)}
{@const componentName = component.component}
{#if componentName}
<Component
{chartView}
embed
{i}
{instanceId}
{componentName}
{chartView}
{scale}
{radius}
padding={gapSize}
Expand All @@ -45,17 +50,7 @@
gridCell}
left={Number(component.x) * gridCell}
top={Number(component.y) * gridCell}
>
<!-- TODO -->
<!--{#if component.markdown}-->
<!-- <Markdown-->
<!-- markdown={component.markdown}-->
<!-- fontSize={component.fontSize ?? defaults.FONT_SIZE}-->
<!-- />-->
<!--{:else if component.chart}-->
<Chart {chartView} chartName={component.component} />
<!--{/if}-->
</Component>
/>
{/if}
{/each}
</Wrapper>
</DashboardWrapper>
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<script lang="ts">
import { V1DashboardItem } from "@rilldata/web-common/runtime-client";
import Element from "./Element.svelte";
import PreviewElement from "./PreviewElement.svelte";
import type { Vector } from "./types";
import { vector } from "./util";
import { createEventDispatcher } from "svelte";
import * as defaults from "./constants";
import Wrapper from "./Wrapper.svelte";
import DashboardWrapper from "./DashboardWrapper.svelte";
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";

const dispatch = createEventDispatcher();
const zeroVector = [0, 0] as [0, 0];
Expand All @@ -19,18 +20,17 @@

let contentRect: DOMRectReadOnly = new DOMRectReadOnly(0, 0, 0, 0);
let scrollOffset = 0;

let selectedIndex: number | null = null;
let changing = false;

let startMouse: Vector = [0, 0];
let mousePosition: Vector = [0, 0];
let initialElementDimensions: Vector = [0, 0];
let initialElementPosition: Vector = [0, 0];

let dimensionChange: [0 | 1 | -1, 0 | 1 | -1] = [0, 0];
let positionChange: [0 | 1, 0 | 1] = [0, 0];

$: instanceId = $runtime.instanceId;

$: gridWidth = contentRect.width;
$: scale = gridWidth / defaults.DASHBOARD_WIDTH;

Expand Down Expand Up @@ -163,25 +163,27 @@

<svelte:window on:mousemove={handleMouseMove} on:mouseup={handleMouseUp} />

<Wrapper
<DashboardWrapper
bind:contentRect
{changing}
{gapSize}
{gridCell}
height={maxBottom * gridCell}
on:click={deselect}
on:scroll={handleScroll}
{scrollOffset}
{radius}
{scale}
{showGrid}
height={maxBottom * gridCell}
width={defaults.DASHBOARD_WIDTH}
on:click={deselect}
on:scroll={handleScroll}
>
{#each items as component, i (i)}
{@const selected = i === selectedIndex}
{@const interacting = selected && changing}
<Element
{scale}
<PreviewElement
{instanceId}
{i}
{scale}
{component}
{radius}
{selected}
Expand All @@ -198,4 +200,4 @@
on:change={handleChange}
/>
{/each}
</Wrapper>
</DashboardWrapper>
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@

<div bind:contentRect class="wrapper {color}">
{#if GridLinesComponent && (showGrid || changing)}
<GridLinesComponent {gridCell} {scrollOffset} {gapSize} {radius} {scale} />
<svelte:component
this={GridLinesComponent}
{gridCell}
{scrollOffset}
{gapSize}
{radius}
{scale}
/>
{/if}
<div
role="presentation"
Expand Down
Loading
Loading