chore: import upstream snapshot with attribution
publish / version_or_publish (push) Has been cancelled
storybook-build / changes (push) Has been cancelled
storybook-build / :storybook-build (push) Has been cancelled
Sync Gradio Skills to Hugging Face / sync-skills (push) Has been cancelled
functional / changes (push) Has been cancelled
functional / build-frontend (push) Has been cancelled
functional / functional-test-SSR=false (push) Has been cancelled
functional / functional-reload (push) Has been cancelled
js / changes (push) Has been cancelled
js / js-test (push) Has been cancelled
docs-build / changes (push) Has been cancelled
docs-build / docs-build (push) Has been cancelled
docs-build / website-build (push) Has been cancelled
functional / functional-test-SSR=true (push) Has been cancelled
hygiene / hygiene-test (push) Has been cancelled
python / changes (push) Has been cancelled
python / build (push) Has been cancelled
python / test-ubuntu-latest-flaky (push) Has been cancelled
python / test-ubuntu-latest-not-flaky (push) Has been cancelled
python / test-windows-latest-flaky (push) Has been cancelled
python / test-windows-latest-not-flaky (push) Has been cancelled
publish / version_or_publish (push) Has been cancelled
storybook-build / changes (push) Has been cancelled
storybook-build / :storybook-build (push) Has been cancelled
Sync Gradio Skills to Hugging Face / sync-skills (push) Has been cancelled
functional / changes (push) Has been cancelled
functional / build-frontend (push) Has been cancelled
functional / functional-test-SSR=false (push) Has been cancelled
functional / functional-reload (push) Has been cancelled
js / changes (push) Has been cancelled
js / js-test (push) Has been cancelled
docs-build / changes (push) Has been cancelled
docs-build / docs-build (push) Has been cancelled
docs-build / website-build (push) Has been cancelled
functional / functional-test-SSR=true (push) Has been cancelled
hygiene / hygiene-test (push) Has been cancelled
python / changes (push) Has been cancelled
python / build (push) Has been cancelled
python / test-ubuntu-latest-flaky (push) Has been cancelled
python / test-ubuntu-latest-not-flaky (push) Has been cancelled
python / test-windows-latest-flaky (push) Has been cancelled
python / test-windows-latest-not-flaky (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
<script lang="ts">
|
||||
//@ts-nocheck
|
||||
import { Plot as PlotIcon } from "@gradio/icons";
|
||||
import { Empty } from "@gradio/atoms";
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
import type { ThemeMode } from "../types";
|
||||
import { untrack } from "svelte";
|
||||
|
||||
let {
|
||||
value,
|
||||
theme_mode,
|
||||
caption,
|
||||
bokeh_version,
|
||||
show_actions_button,
|
||||
_selectable,
|
||||
x_lim,
|
||||
show_fullscreen_button,
|
||||
show_label,
|
||||
on_change,
|
||||
onselect
|
||||
}: {
|
||||
value: null | string;
|
||||
theme_mode: ThemeMode;
|
||||
caption: string;
|
||||
bokeh_version: string | null;
|
||||
show_actions_button: boolean;
|
||||
_selectable: boolean;
|
||||
x_lim: [number, number] | null;
|
||||
show_fullscreen_button: boolean;
|
||||
show_label: boolean;
|
||||
on_change: () => void;
|
||||
onselect?: (data: SelectData) => void;
|
||||
} = $props();
|
||||
|
||||
let PlotComponent: any = $state(null);
|
||||
let loaded_plotly_css = $state(false);
|
||||
let key = $state(0);
|
||||
|
||||
const plotTypeMapping = {
|
||||
plotly: () => import("./plot_types/PlotlyPlot.svelte"),
|
||||
bokeh: () => import("./plot_types/BokehPlot.svelte"),
|
||||
matplotlib: () => import("./plot_types/MatplotlibPlot.svelte"),
|
||||
altair: () => import("./plot_types/AltairPlot.svelte")
|
||||
};
|
||||
|
||||
let loadedPlotTypeMapping: Record<string, any> = {};
|
||||
|
||||
const is_browser = typeof window !== "undefined";
|
||||
let _type = $state(null);
|
||||
|
||||
$effect(() => {
|
||||
let type = value?.type;
|
||||
untrack(() => {
|
||||
key = key + 1;
|
||||
if (type !== _type) {
|
||||
PlotComponent = null;
|
||||
}
|
||||
if (type && type in plotTypeMapping && is_browser) {
|
||||
if (loadedPlotTypeMapping[type]) {
|
||||
PlotComponent = loadedPlotTypeMapping[type];
|
||||
} else {
|
||||
plotTypeMapping[type]().then((module) => {
|
||||
PlotComponent = module.default;
|
||||
loadedPlotTypeMapping[type] = PlotComponent;
|
||||
});
|
||||
}
|
||||
}
|
||||
_type = type;
|
||||
});
|
||||
on_change();
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if value && PlotComponent}
|
||||
{#key key}
|
||||
<PlotComponent
|
||||
{value}
|
||||
colors={[]}
|
||||
{theme_mode}
|
||||
{show_label}
|
||||
{caption}
|
||||
{bokeh_version}
|
||||
{show_actions_button}
|
||||
{_selectable}
|
||||
{x_lim}
|
||||
bind:loaded_plotly_css
|
||||
{onselect}
|
||||
/>
|
||||
{/key}
|
||||
{:else}
|
||||
<Empty unpadded_box={true} size="large"><PlotIcon /></Empty>
|
||||
{/if}
|
||||
@@ -0,0 +1,197 @@
|
||||
<script lang="ts">
|
||||
//@ts-nocheck
|
||||
import { set_config } from "./altair_utils";
|
||||
import { untrack } from "svelte";
|
||||
import type { TopLevelSpec as Spec } from "vega-lite";
|
||||
import vegaEmbed from "vega-embed";
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
import type { View } from "vega";
|
||||
|
||||
let {
|
||||
value,
|
||||
colors = [],
|
||||
caption,
|
||||
show_actions_button,
|
||||
_selectable,
|
||||
onselect
|
||||
}: {
|
||||
value: any;
|
||||
colors?: string[];
|
||||
caption: string;
|
||||
show_actions_button: boolean;
|
||||
_selectable: boolean;
|
||||
onselect?: (data: SelectData) => void;
|
||||
} = $props();
|
||||
|
||||
let element: HTMLElement;
|
||||
let parent_element: HTMLElement;
|
||||
let view: View | undefined;
|
||||
|
||||
let computed_style = window.getComputedStyle(document.body);
|
||||
|
||||
let old_spec: Spec = $state(null);
|
||||
let spec_width: number = $state(0);
|
||||
|
||||
let plot = $derived(value?.plot);
|
||||
let spec = $derived.by(() => {
|
||||
if (!plot) return null;
|
||||
let parsed_spec = JSON.parse(plot) as Spec;
|
||||
|
||||
if (parsed_spec && parsed_spec.params && !_selectable) {
|
||||
parsed_spec.params = parsed_spec.params.filter(
|
||||
(param) => param.name !== "brush"
|
||||
);
|
||||
}
|
||||
|
||||
untrack(() => {
|
||||
if (old_spec !== parsed_spec) {
|
||||
old_spec = parsed_spec;
|
||||
spec_width = parsed_spec.width;
|
||||
}
|
||||
|
||||
if (value.chart && parsed_spec) {
|
||||
parsed_spec = set_config(
|
||||
parsed_spec,
|
||||
computed_style,
|
||||
value.chart as string,
|
||||
colors
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return parsed_spec;
|
||||
});
|
||||
|
||||
let fit_width_to_parent = $derived(
|
||||
spec?.encoding?.column?.field ||
|
||||
spec?.encoding?.row?.field ||
|
||||
value.chart === undefined
|
||||
? false
|
||||
: true
|
||||
);
|
||||
|
||||
const get_width = (): number => {
|
||||
return Math.min(
|
||||
parent_element.offsetWidth,
|
||||
spec_width || parent_element.offsetWidth
|
||||
);
|
||||
};
|
||||
let resize_callback = (): void => {};
|
||||
const renderPlot = (): void => {
|
||||
if (view) {
|
||||
view.finalize();
|
||||
view = undefined;
|
||||
}
|
||||
if (fit_width_to_parent) {
|
||||
spec.width = get_width();
|
||||
}
|
||||
vegaEmbed(element, spec, { actions: show_actions_button }).then(
|
||||
function (result): void {
|
||||
view = result.view;
|
||||
resize_callback = () => {
|
||||
view.signal("width", get_width()).run();
|
||||
};
|
||||
|
||||
if (!_selectable) return;
|
||||
const callback = (event, item): void => {
|
||||
const brushValue = view.signal("brush");
|
||||
if (brushValue) {
|
||||
if (Object.keys(brushValue).length === 0) {
|
||||
onselect?.({
|
||||
value: null,
|
||||
index: null,
|
||||
selected: false
|
||||
});
|
||||
} else {
|
||||
const key = Object.keys(brushValue)[0];
|
||||
let range: [number, number] = brushValue[key].map(
|
||||
(x) => x / 1000
|
||||
);
|
||||
onselect?.({
|
||||
value: brushValue,
|
||||
index: range,
|
||||
selected: true
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
view.addEventListener("mouseup", callback);
|
||||
view.addEventListener("touchup", callback);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
if (fit_width_to_parent && spec.width !== parent_element.offsetWidth) {
|
||||
resize_callback();
|
||||
}
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (!element || !parent_element || !spec) return;
|
||||
|
||||
renderPlot();
|
||||
resizeObserver.observe(parent_element);
|
||||
|
||||
return () => {
|
||||
if (view) {
|
||||
view.finalize();
|
||||
view = undefined;
|
||||
}
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div data-testid={"altair"} class="altair layout" bind:this={parent_element}>
|
||||
<div bind:this={element}></div>
|
||||
{#if caption}
|
||||
<div class="caption layout">
|
||||
{caption}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.altair :global(canvas) {
|
||||
padding: 6px;
|
||||
}
|
||||
.altair :global(.vega-embed) {
|
||||
padding: 0px !important;
|
||||
}
|
||||
.altair :global(.vega-actions) {
|
||||
right: 0px !important;
|
||||
}
|
||||
.layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: var(--size-full);
|
||||
height: var(--size-full);
|
||||
color: var(--body-text-color);
|
||||
}
|
||||
.altair {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: var(--size-full);
|
||||
height: var(--size-full);
|
||||
}
|
||||
.caption {
|
||||
font-size: var(--text-sm);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
:global(#vg-tooltip-element) {
|
||||
font-family: var(--font) !important;
|
||||
font-size: var(--text-xs) !important;
|
||||
box-shadow: none !important;
|
||||
background-color: var(--block-background-fill) !important;
|
||||
border: 1px solid var(--border-color-primary) !important;
|
||||
color: var(--body-text-color) !important;
|
||||
}
|
||||
:global(#vg-tooltip-element .key) {
|
||||
color: var(--body-text-color-subdued) !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,166 @@
|
||||
<script lang="ts">
|
||||
//@ts-nocheck
|
||||
let { value, bokeh_version }: { value: any; bokeh_version: string | null } =
|
||||
$props();
|
||||
|
||||
const div_id = `bokehDiv-${Math.random().toString(5).substring(2)}`;
|
||||
let plot = $derived(value?.plot);
|
||||
|
||||
function static_base(version: string): string {
|
||||
const root =
|
||||
(typeof window !== "undefined" && window.gradio_config?.root) || "";
|
||||
return `${root}/static/bokeh/${version}`;
|
||||
}
|
||||
|
||||
function bokeh_local_url(version: string, filename: string): string {
|
||||
return `${static_base(version)}/${filename}`;
|
||||
}
|
||||
|
||||
function bokeh_cdn_url(version: string, filename: string): string {
|
||||
if (filename === `bokeh-${version}.min.js`) {
|
||||
return `https://cdn.bokeh.org/bokeh/release/${filename}`;
|
||||
}
|
||||
return `https://cdn.pydata.org/bokeh/release/${filename}`;
|
||||
}
|
||||
|
||||
function load_script(
|
||||
src: string,
|
||||
fallback?: string
|
||||
): Promise<HTMLScriptElement> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement("script");
|
||||
script.onload = () => resolve(script);
|
||||
script.onerror = () => {
|
||||
if (fallback) {
|
||||
const fallback_script = document.createElement("script");
|
||||
fallback_script.onload = () => resolve(fallback_script);
|
||||
fallback_script.onerror = () => {
|
||||
console.error(`Failed to load Bokeh script: ${src}`);
|
||||
reject(new Error(`Failed to load ${src}`));
|
||||
};
|
||||
fallback_script.src = fallback;
|
||||
document.head.appendChild(fallback_script);
|
||||
} else {
|
||||
console.error(`Failed to load Bokeh script: ${src}`);
|
||||
reject(new Error(`Failed to load ${src}`));
|
||||
}
|
||||
};
|
||||
script.src = src;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
async function embed_bokeh(_plot: string): Promise<void> {
|
||||
if (document) {
|
||||
const el = document.getElementById(div_id);
|
||||
if (el) {
|
||||
el.innerHTML = "";
|
||||
}
|
||||
}
|
||||
if (window.Bokeh) {
|
||||
load_bokeh();
|
||||
let plotObj = JSON.parse(_plot);
|
||||
await window.Bokeh.embed.embed_item(plotObj, div_id);
|
||||
}
|
||||
}
|
||||
|
||||
const main_src = bokeh_version
|
||||
? bokeh_local_url(bokeh_version, `bokeh-${bokeh_version}.min.js`)
|
||||
: null;
|
||||
|
||||
const main_fallback = bokeh_version
|
||||
? bokeh_cdn_url(bokeh_version, `bokeh-${bokeh_version}.min.js`)
|
||||
: null;
|
||||
|
||||
const plugins_src = bokeh_version
|
||||
? [
|
||||
bokeh_local_url(bokeh_version, `bokeh-widgets-${bokeh_version}.min.js`),
|
||||
bokeh_local_url(bokeh_version, `bokeh-tables-${bokeh_version}.min.js`),
|
||||
bokeh_local_url(bokeh_version, `bokeh-gl-${bokeh_version}.min.js`),
|
||||
bokeh_local_url(bokeh_version, `bokeh-api-${bokeh_version}.min.js`)
|
||||
]
|
||||
: [];
|
||||
|
||||
const plugins_fallback = bokeh_version
|
||||
? [
|
||||
bokeh_cdn_url(bokeh_version, `bokeh-widgets-${bokeh_version}.min.js`),
|
||||
bokeh_cdn_url(bokeh_version, `bokeh-tables-${bokeh_version}.min.js`),
|
||||
bokeh_cdn_url(bokeh_version, `bokeh-gl-${bokeh_version}.min.js`),
|
||||
bokeh_cdn_url(bokeh_version, `bokeh-api-${bokeh_version}.min.js`)
|
||||
]
|
||||
: [];
|
||||
|
||||
let loaded = $state(false);
|
||||
let plugin_scripts: HTMLScriptElement[] = $state([]);
|
||||
|
||||
async function load_plugins(): Promise<void> {
|
||||
plugin_scripts = await Promise.all(
|
||||
plugins_src.map((src, i) => load_script(src, plugins_fallback[i]))
|
||||
);
|
||||
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
function handle_bokeh_loaded(): void {
|
||||
load_plugins();
|
||||
}
|
||||
|
||||
let added_main_script = false;
|
||||
|
||||
function load_bokeh(): HTMLScriptElement | null {
|
||||
const script = document.createElement("script");
|
||||
script.onload = handle_bokeh_loaded;
|
||||
script.onerror = () => {
|
||||
if (main_fallback) {
|
||||
load_script(main_fallback)
|
||||
.then(handle_bokeh_loaded)
|
||||
.catch(() => {});
|
||||
}
|
||||
};
|
||||
script.src = main_src;
|
||||
const is_bokeh_script_present = document.head.querySelector(
|
||||
`script[src="${main_src}"]`
|
||||
);
|
||||
if (!is_bokeh_script_present) {
|
||||
document.head.appendChild(script);
|
||||
added_main_script = true;
|
||||
return script;
|
||||
}
|
||||
handle_bokeh_loaded();
|
||||
return null;
|
||||
}
|
||||
|
||||
const main_script = bokeh_version ? load_bokeh() : null;
|
||||
|
||||
$effect(() => {
|
||||
if (loaded && plot) {
|
||||
embed_bokeh(plot);
|
||||
}
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
return () => {
|
||||
if (
|
||||
added_main_script &&
|
||||
main_script &&
|
||||
document.head.contains(main_script)
|
||||
) {
|
||||
document.head.removeChild(main_script);
|
||||
}
|
||||
plugin_scripts.forEach((child) => {
|
||||
if (document.head.contains(child)) {
|
||||
document.head.removeChild(child);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div data-testid={"bokeh"} id={div_id} class="gradio-bokeh" />
|
||||
|
||||
<style>
|
||||
.gradio-bokeh {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
let { value }: { value: any } = $props();
|
||||
|
||||
let plot = $derived(value?.plot);
|
||||
</script>
|
||||
|
||||
<div data-testid={"matplotlib"} class="matplotlib layout">
|
||||
<img src={plot} alt={`${value.chart} plot visualising provided data`} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: var(--size-full);
|
||||
height: var(--size-full);
|
||||
color: var(--body-text-color);
|
||||
}
|
||||
.matplotlib img {
|
||||
object-fit: contain;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,60 @@
|
||||
<script lang="ts">
|
||||
//@ts-nocheck
|
||||
import Plotly from "plotly.js-dist-min";
|
||||
import { untrack } from "svelte";
|
||||
|
||||
let {
|
||||
value,
|
||||
show_label,
|
||||
loaded_plotly_css = $bindable(false)
|
||||
}: {
|
||||
value: any;
|
||||
show_label: boolean;
|
||||
loaded_plotly_css?: boolean;
|
||||
} = $props();
|
||||
|
||||
let plot = $derived(value?.plot);
|
||||
|
||||
let plot_div: HTMLDivElement;
|
||||
let plotly_global_style: HTMLElement;
|
||||
|
||||
function load_plotly_css(): void {
|
||||
if (!loaded_plotly_css) {
|
||||
plotly_global_style = document.getElementById("plotly.js-style-global");
|
||||
const plotly_style_clone = plotly_global_style.cloneNode();
|
||||
plot_div.appendChild(plotly_style_clone);
|
||||
for (const rule of plotly_global_style.sheet.cssRules) {
|
||||
plotly_style_clone.sheet.insertRule(rule.cssText);
|
||||
}
|
||||
loaded_plotly_css = true;
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (!plot_div || !plot) return;
|
||||
|
||||
// load_plotly_css writes loaded_plotly_css; untrack it so this effect
|
||||
// doesn't re-run and collapse an autosize plot to zero height.
|
||||
untrack(() => load_plotly_css());
|
||||
|
||||
let plotObj = JSON.parse(plot);
|
||||
|
||||
plotObj.config = plotObj.config || {};
|
||||
plotObj.config.responsive = true;
|
||||
plotObj.responsive = true;
|
||||
plotObj.layout.autosize = true;
|
||||
|
||||
if (plotObj.layout.margin == undefined) {
|
||||
plotObj.layout.margin = {};
|
||||
}
|
||||
if (plotObj.layout.title && show_label) {
|
||||
plotObj.layout.margin.t = Math.max(100, plotObj.layout.margin.t || 0);
|
||||
}
|
||||
plotObj.layout.margin.autoexpand = true;
|
||||
|
||||
Plotly.react(plot_div, plotObj.data, plotObj.layout, plotObj.config);
|
||||
Plotly.Plots.resize(plot_div);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div data-testid={"plotly"} bind:this={plot_div}></div>
|
||||
@@ -0,0 +1,111 @@
|
||||
import { colors as color_palette } from "@gradio/theme";
|
||||
import { get_next_color } from "@gradio/utils";
|
||||
import type { Config, TopLevelSpec as Spec } from "vega-lite";
|
||||
|
||||
export function set_config(
|
||||
spec: Spec,
|
||||
computed_style: CSSStyleDeclaration,
|
||||
chart_type: string,
|
||||
colors: string[]
|
||||
): Spec {
|
||||
let accentColor = computed_style.getPropertyValue("--color-accent");
|
||||
let bodyTextColor = computed_style.getPropertyValue("--body-text-color");
|
||||
let borderColorPrimary = computed_style.getPropertyValue(
|
||||
"--border-color-primary"
|
||||
);
|
||||
let fontFamily = computed_style.fontFamily;
|
||||
let titleWeight = computed_style.getPropertyValue(
|
||||
"--block-title-text-weight"
|
||||
) as "bold" | "normal" | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
|
||||
const fontToPxVal = (font: string): number => {
|
||||
return font.endsWith("px") ? parseFloat(font.slice(0, -2)) : 12;
|
||||
};
|
||||
let textSizeMd = fontToPxVal(computed_style.getPropertyValue("--text-md"));
|
||||
let textSizeSm = fontToPxVal(computed_style.getPropertyValue("--text-sm"));
|
||||
let config: Config = {
|
||||
autosize: { type: "fit", contains: "padding" },
|
||||
axis: {
|
||||
labelFont: fontFamily,
|
||||
labelColor: bodyTextColor,
|
||||
titleFont: fontFamily,
|
||||
titleColor: bodyTextColor,
|
||||
tickColor: borderColorPrimary,
|
||||
labelFontSize: textSizeSm,
|
||||
gridColor: borderColorPrimary,
|
||||
titleFontWeight: "normal",
|
||||
titleFontSize: textSizeSm,
|
||||
labelFontWeight: "normal",
|
||||
domain: false,
|
||||
labelAngle: 0
|
||||
},
|
||||
legend: {
|
||||
labelColor: bodyTextColor,
|
||||
labelFont: fontFamily,
|
||||
titleColor: bodyTextColor,
|
||||
titleFont: fontFamily,
|
||||
titleFontWeight: "normal",
|
||||
titleFontSize: textSizeSm,
|
||||
labelFontWeight: "normal",
|
||||
offset: 2
|
||||
},
|
||||
title: {
|
||||
color: bodyTextColor,
|
||||
font: fontFamily,
|
||||
fontSize: textSizeMd,
|
||||
fontWeight: titleWeight,
|
||||
anchor: "middle"
|
||||
},
|
||||
view: {
|
||||
stroke: borderColorPrimary
|
||||
}
|
||||
};
|
||||
spec.config = config;
|
||||
// @ts-ignore (unsure why the following are not typed in Spec)
|
||||
let encoding: any = spec.encoding;
|
||||
// @ts-ignore
|
||||
let layer: any = spec.layer;
|
||||
switch (chart_type) {
|
||||
case "scatter":
|
||||
spec.config.mark = { stroke: accentColor };
|
||||
if (encoding.color && encoding.color.type == "nominal") {
|
||||
encoding.color.scale.range = encoding.color.scale.range.map(
|
||||
(_: string, i: number) => get_color(colors, i)
|
||||
);
|
||||
} else if (encoding.color && encoding.color.type == "quantitative") {
|
||||
encoding.color.scale.range = ["#eff6ff", "#1e3a8a"];
|
||||
encoding.color.scale.range.interpolate = "hsl";
|
||||
}
|
||||
break;
|
||||
case "line":
|
||||
spec.config.mark = { stroke: accentColor, cursor: "crosshair" };
|
||||
layer.forEach((d: any) => {
|
||||
if (d.encoding.color) {
|
||||
d.encoding.color.scale.range = d.encoding.color.scale.range.map(
|
||||
(_: any, i: any) => get_color(colors, i)
|
||||
);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "bar":
|
||||
spec.config.mark = { opacity: 0.8, fill: accentColor };
|
||||
if (encoding.color) {
|
||||
encoding.color.scale.range = encoding.color.scale.range.map(
|
||||
(_: any, i: any) => get_color(colors, i)
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
|
||||
function get_color(colors: string[], index: number): string {
|
||||
let current_color = colors[index % colors.length];
|
||||
|
||||
if (current_color && current_color in color_palette) {
|
||||
return color_palette[current_color as keyof typeof color_palette]?.primary;
|
||||
} else if (!current_color) {
|
||||
return color_palette[get_next_color(index) as keyof typeof color_palette]
|
||||
.primary;
|
||||
}
|
||||
return current_color;
|
||||
}
|
||||
Reference in New Issue
Block a user