Adds support for external visualizer functions.
This commit is contained in:
@@ -1,27 +1,27 @@
|
||||
//// Uncomment the visualization that you work on to increase webpack build speed!
|
||||
//// If you create a new visualization, add it here!
|
||||
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/ast-visualizer"; /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/graph/dot-graphviz-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/graph/graph-graphviz-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/graph/graph-visjs-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/grid-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/image-visualizer"; /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/monaco-text-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/monaco-text-diff-visualizer"; /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/perspective-table-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/plotly-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/simple-text-visualizer"; /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/source-visualizer"; /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/svg-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/tree-visualizer"; /*
|
||||
|
||||
// This bundles the monaco editor. Uncomment it to load monaco dynamically.
|
||||
// Dynamic loading increases webpack build speed significantly.
|
||||
if (typeof process === undefined) {
|
||||
// We check for process so that we don't load monaco from nodejs.
|
||||
require("monaco-editor");
|
||||
}
|
||||
|
||||
// This import bundles *all* visualizations. This makes webpack super slow.
|
||||
import "@hediet/visualization-bundle"; // */
|
||||
//// Uncomment the visualization that you work on to increase webpack build speed!
|
||||
//// If you create a new visualization, add it here!
|
||||
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/ast-visualizer"; /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/graph/dot-graphviz-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/graph/graph-graphviz-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/graph/graph-visjs-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/grid-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/image-visualizer"; /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/monaco-text-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/monaco-text-diff-visualizer"; /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/perspective-table-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/plotly-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/simple-text-visualizer"; /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/source-visualizer"; /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/svg-visualizer" /*
|
||||
// import "@hediet/visualization-bundle/dist/visualizers/tree-visualizer"; /*
|
||||
|
||||
// This bundles the monaco editor. Uncomment it to load monaco dynamically.
|
||||
// Dynamic loading increases webpack build speed significantly.
|
||||
if (typeof process === undefined) {
|
||||
// We check for process so that we don't load monaco from nodejs.
|
||||
require("monaco-editor");
|
||||
}
|
||||
|
||||
// This import bundles *all* visualizations. This makes webpack super slow.
|
||||
import "@hediet/visualization-bundle"; // */
|
||||
|
||||
@@ -46,7 +46,7 @@ export class VisJsGraphViewer extends React.Component<{
|
||||
shape: n.shape,
|
||||
});
|
||||
}
|
||||
this.nodes.forEach(item => {
|
||||
this.nodes.forEach((item) => {
|
||||
if (!newNodes.has(item.id)) {
|
||||
this.nodes.remove(item);
|
||||
}
|
||||
@@ -74,7 +74,7 @@ export class VisJsGraphViewer extends React.Component<{
|
||||
],
|
||||
});
|
||||
}
|
||||
this.edges.forEach(item => {
|
||||
this.edges.forEach((item) => {
|
||||
if (!newEdges.has(item.id)) {
|
||||
this.edges.remove(item);
|
||||
}
|
||||
@@ -99,6 +99,92 @@ export class VisJsGraphViewer extends React.Component<{
|
||||
},
|
||||
},
|
||||
};
|
||||
const network = new Network(this.divRef.current!, data, options);
|
||||
this.network = new Network(this.divRef.current!, data, options);
|
||||
this.divRef.current!.setAttribute("tabindex", "0");
|
||||
document.addEventListener("copy", this.onCopy);
|
||||
}
|
||||
|
||||
private network: Network | undefined;
|
||||
private readonly onCopy = (e: ClipboardEvent) => {
|
||||
if (!this.network) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!(
|
||||
document.activeElement &&
|
||||
document.activeElement.className === "vis-network"
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const n = this.network as any;
|
||||
|
||||
const nodesText: string[] = [];
|
||||
|
||||
let id = 10;
|
||||
const visJsIdToNodeId = new Map<string, number>();
|
||||
|
||||
// export to draw.io
|
||||
for (const node of Object.values(n.body.nodes) as any) {
|
||||
const label = node.shape.options.label;
|
||||
if (label === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let style = "";
|
||||
if (node.shape.constructor.name === "Ellipse") {
|
||||
style += "ellipse;";
|
||||
} else {
|
||||
style += "rounded=1;";
|
||||
}
|
||||
|
||||
const nodeId = id++;
|
||||
visJsIdToNodeId.set(node.id, nodeId);
|
||||
|
||||
nodesText.push(
|
||||
`
|
||||
<mxCell id="${nodeId}" value="${label}" style="${style}" vertex="1" parent="1">
|
||||
<mxGeometry x="${node.x}" y="${node.y}" width="${node.shape.width}" height="${node.shape.height}" as="geometry"/>
|
||||
</mxCell>`
|
||||
);
|
||||
}
|
||||
|
||||
for (const edge of Object.values(n.body.edges) as any) {
|
||||
const label = edge.options.label || "";
|
||||
const edgeId = id++;
|
||||
|
||||
nodesText.push(
|
||||
`
|
||||
<mxCell id="${edgeId}" value="${label}" edge="1" source="${visJsIdToNodeId.get(
|
||||
edge.from.id
|
||||
)}" target="${visJsIdToNodeId.get(edge.to.id)}" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>`
|
||||
);
|
||||
}
|
||||
|
||||
const data = `
|
||||
<mxGraphModel>
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
|
||||
${nodesText.join("\n")}
|
||||
</root>
|
||||
</mxGraphModel>`;
|
||||
|
||||
/*
|
||||
<mxCell id="2" value="xxx" edge="1" source="3" target="4" parent="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
*/
|
||||
|
||||
e.clipboardData!.setData("text/plain", encodeURIComponent(data));
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener("copy", this.onCopy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import * as React from "react";
|
||||
import {
|
||||
sOpenObject,
|
||||
sLiteral,
|
||||
sArrayOf,
|
||||
sLiteral,
|
||||
sNull,
|
||||
sNumber,
|
||||
sOpenObject,
|
||||
sOptionalProp,
|
||||
sAny,
|
||||
sProp,
|
||||
sString,
|
||||
sUnion,
|
||||
sNumber,
|
||||
sNull,
|
||||
} from "@hediet/semantic-json";
|
||||
import { makeLazyLoadable } from "../../utils/LazyLoadable";
|
||||
import { Deferred } from "@hediet/std/synchronization";
|
||||
import {
|
||||
createLazyReactVisualization,
|
||||
createVisualizer,
|
||||
globalVisualizationFactory,
|
||||
createReactVisualization,
|
||||
createLazyReactVisualization,
|
||||
} from "@hediet/visualization-core";
|
||||
import * as React from "react";
|
||||
import { visualizationNs } from "../../consts";
|
||||
import { makeLazyLoadable } from "../../utils/LazyLoadable";
|
||||
|
||||
const PlotlyViewerLazyLoadable = makeLazyLoadable(
|
||||
async () => (await import("./PlotlyViewer")).PlotlyViewer
|
||||
@@ -45,16 +44,22 @@ export const plotlyVisualizer = createVisualizer({
|
||||
x: sOptionalProp(sDatumArr),
|
||||
y: sOptionalProp(sDatumArr),
|
||||
z: sOptionalProp(sDatumArr),
|
||||
cells: sOptionalProp(sOpenObject({
|
||||
values: sArrayOf(sDatumArr),
|
||||
})),
|
||||
header: sOptionalProp(sOpenObject({
|
||||
values: sDatumArr,
|
||||
})),
|
||||
domain: sOptionalProp(sOpenObject({
|
||||
x: sArrayOf(sNumber()),
|
||||
y: sArrayOf(sNumber()),
|
||||
})),
|
||||
cells: sOptionalProp(
|
||||
sOpenObject({
|
||||
values: sArrayOf(sDatumArr),
|
||||
})
|
||||
),
|
||||
header: sOptionalProp(
|
||||
sOpenObject({
|
||||
values: sDatumArr,
|
||||
})
|
||||
),
|
||||
domain: sOptionalProp(
|
||||
sOpenObject({
|
||||
x: sArrayOf(sNumber()),
|
||||
y: sArrayOf(sNumber()),
|
||||
})
|
||||
),
|
||||
type: sOptionalProp(
|
||||
sUnion([
|
||||
sLiteral("bar"),
|
||||
@@ -129,14 +134,20 @@ export const plotlyVisualizer = createVisualizer({
|
||||
priority: 1000,
|
||||
preload: PlotlyViewerLazyLoadable.preload,
|
||||
},
|
||||
({ theme, readyCallback }) => (
|
||||
<PlotlyViewerLazyLoadable
|
||||
data={data.data}
|
||||
layout={data.layout}
|
||||
theme={theme}
|
||||
onReady={readyCallback}
|
||||
/>
|
||||
)
|
||||
({ theme }) => {
|
||||
const b = new Deferred();
|
||||
return {
|
||||
node: (
|
||||
<PlotlyViewerLazyLoadable
|
||||
data={data.data}
|
||||
layout={data.layout}
|
||||
theme={theme}
|
||||
onReady={() => b.resolve}
|
||||
/>
|
||||
),
|
||||
ready: b.promise,
|
||||
};
|
||||
}
|
||||
),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@hediet/visualization-core",
|
||||
"description": "Provides infrastructure to find registered visualizations for given json data",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/hediet/visualization",
|
||||
"repository": {
|
||||
|
||||
30
visualization-core/src/CanvasVisualization.tsx
Normal file
30
visualization-core/src/CanvasVisualization.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import * as React from "react";
|
||||
import { createReactVisualization } from "./ReactVisualization";
|
||||
import { Visualization, Visualizer } from "./Visualizer";
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
|
||||
export function createCanvas2DVisualization(
|
||||
sourceVisualizer: Visualizer,
|
||||
options: { priority: number },
|
||||
render: (context: CanvasRenderingContext2D) => Disposable | void
|
||||
): Visualization {
|
||||
return createReactVisualization(
|
||||
sourceVisualizer,
|
||||
{ priority: options.priority },
|
||||
() => (
|
||||
<canvas
|
||||
ref={(canvas) => {
|
||||
if (canvas) {
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// We don't want to use try/catch to swallow the error, so we use setTimeout to protect the caller
|
||||
setTimeout(() => {
|
||||
render(ctx);
|
||||
}, 0);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -7,93 +7,75 @@ import {
|
||||
asVisualizationId,
|
||||
VisualizationRenderOptions,
|
||||
} from "./Visualizer";
|
||||
import { Deferred } from "@hediet/std/synchronization";
|
||||
|
||||
interface Options {
|
||||
id?: string;
|
||||
name?: string;
|
||||
interface ReactVisualizationArgs {
|
||||
priority: number;
|
||||
preload?: (() => Promise<void>) | undefined;
|
||||
}
|
||||
|
||||
export abstract class BaseReactVisualization implements Visualization<unknown> {
|
||||
class ReactVisualization implements Visualization<unknown> {
|
||||
public readonly id: VisualizationId;
|
||||
public readonly name: string;
|
||||
public readonly priority: number;
|
||||
private readonly _preload: (() => Promise<void>) | undefined;
|
||||
|
||||
constructor(
|
||||
public readonly sourceVisualizer: Visualizer,
|
||||
options: Options
|
||||
options: ReactVisualizationArgs,
|
||||
private readonly getReactNode: (
|
||||
options: ReactVisualizationRenderArgs
|
||||
) => {
|
||||
node: React.ReactChild;
|
||||
ready: Promise<void>;
|
||||
}
|
||||
) {
|
||||
this.id = asVisualizationId(
|
||||
options.id || sourceVisualizer.id.toString()
|
||||
);
|
||||
this.name = options.name || sourceVisualizer.name;
|
||||
this.id = asVisualizationId(sourceVisualizer.id.toString());
|
||||
this.name = sourceVisualizer.name;
|
||||
this.priority = options.priority;
|
||||
this._preload = options.preload;
|
||||
}
|
||||
|
||||
public preload(): Promise<void> {
|
||||
return this._preload ? this._preload() : Promise.resolve();
|
||||
}
|
||||
|
||||
public render(
|
||||
target: HTMLDivElement,
|
||||
options: VisualizationRenderOptions<unknown>
|
||||
): { renderState: unknown } {
|
||||
const node = this.getReactNode({
|
||||
): { renderState: unknown; ready: Promise<void> } {
|
||||
const { node, ready } = this.getReactNode({
|
||||
theme: options.theme,
|
||||
readyCallback: options.readyCallback,
|
||||
});
|
||||
// We could use this to get access to theme vars:
|
||||
//getComputedStyle(target).getPropertyValue("--background-color")
|
||||
// getComputedStyle(target).getPropertyValue("--background-color")
|
||||
|
||||
ReactDom.render(<>{node}</>, target);
|
||||
return { renderState: undefined };
|
||||
return { renderState: undefined, ready };
|
||||
}
|
||||
|
||||
public abstract preload(): Promise<void>;
|
||||
protected abstract getReactNode(
|
||||
options: ReactVisualizationRenderOptions
|
||||
): React.ReactChild;
|
||||
}
|
||||
|
||||
export interface ReactVisualizationRenderOptions {
|
||||
export interface ReactVisualizationRenderArgs {
|
||||
theme: Theme;
|
||||
readyCallback: () => void;
|
||||
}
|
||||
|
||||
class ReactVisualization extends BaseReactVisualization {
|
||||
private readonly _preload?: () => Promise<void>;
|
||||
|
||||
constructor(
|
||||
sourceVisualizer: Visualizer,
|
||||
options: Options & { preload?: () => Promise<void> },
|
||||
private readonly renderReactNode: (
|
||||
options: ReactVisualizationRenderOptions
|
||||
) => React.ReactChild
|
||||
) {
|
||||
super(sourceVisualizer, options);
|
||||
this._preload = options.preload;
|
||||
}
|
||||
|
||||
protected getReactNode(
|
||||
options: ReactVisualizationRenderOptions
|
||||
): React.ReactChild {
|
||||
return this.renderReactNode(options);
|
||||
}
|
||||
|
||||
public async preload(): Promise<void> {
|
||||
if (this._preload) {
|
||||
return await this._preload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createReactVisualization(
|
||||
sourceVisualizer: Visualizer,
|
||||
options: Options & { preload?: () => Promise<void> },
|
||||
renderReactNode: (options: { theme: Theme }) => React.ReactChild
|
||||
args: ReactVisualizationArgs,
|
||||
renderReactNode: (args: ReactVisualizationRenderArgs) => React.ReactChild
|
||||
) {
|
||||
return new ReactVisualization(sourceVisualizer, options, options => (
|
||||
<ComponentWithOnMount
|
||||
onDidMount={options.readyCallback}
|
||||
child={renderReactNode(options)}
|
||||
/>
|
||||
));
|
||||
return new ReactVisualization(sourceVisualizer, args, (options) => {
|
||||
const deferred = new Deferred();
|
||||
return {
|
||||
node: (
|
||||
<ComponentWithOnMount
|
||||
onDidMount={() => deferred.resolve()}
|
||||
child={renderReactNode(options)}
|
||||
/>
|
||||
),
|
||||
ready: deferred.promise,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
class ComponentWithOnMount extends React.Component<{
|
||||
@@ -111,12 +93,11 @@ class ComponentWithOnMount extends React.Component<{
|
||||
|
||||
export function createLazyReactVisualization(
|
||||
sourceVisualizer: Visualizer,
|
||||
options: Options & { preload?: () => Promise<void> },
|
||||
renderReactNode: (
|
||||
options: ReactVisualizationRenderOptions
|
||||
) => React.ReactChild
|
||||
args: ReactVisualizationArgs,
|
||||
renderReactNode: (args: ReactVisualizationRenderArgs) => {
|
||||
node: React.ReactChild;
|
||||
ready: Promise<void>;
|
||||
}
|
||||
) {
|
||||
return new ReactVisualization(sourceVisualizer, options, options =>
|
||||
renderReactNode(options)
|
||||
);
|
||||
return new ReactVisualization(sourceVisualizer, args, renderReactNode);
|
||||
}
|
||||
|
||||
26
visualization-core/src/RegisterVisualizerFn.ts
Normal file
26
visualization-core/src/RegisterVisualizerFn.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import * as semanticJson from "@hediet/semantic-json";
|
||||
import { createCanvas2DVisualization } from "./CanvasVisualization";
|
||||
import { CreateVisualizerOptions } from "./createVisualizer";
|
||||
import { Visualization, Visualizer } from "./Visualizer";
|
||||
|
||||
export type RegisterVisualizerFn = (
|
||||
register: <TData>(
|
||||
visualizationOptions: CreateVisualizerOptions<TData>
|
||||
) => void,
|
||||
lib: Lib
|
||||
) => void;
|
||||
|
||||
interface Lib {
|
||||
semanticJson: typeof semanticJson;
|
||||
|
||||
createCanvas2DVisualization: (
|
||||
visualizer: Visualizer,
|
||||
options: { priority: number },
|
||||
render: (context: CanvasRenderingContext2D) => void
|
||||
) => Visualization;
|
||||
}
|
||||
|
||||
export const libImplementation: Lib = {
|
||||
semanticJson,
|
||||
createCanvas2DVisualization,
|
||||
};
|
||||
@@ -20,15 +20,15 @@ export class VisualizationView extends React.Component<{
|
||||
|
||||
renderVisualization() {
|
||||
const { visualization, theme } = this.props;
|
||||
const { renderState } = visualization.render(this.ref.current!, {
|
||||
const { renderState, ready } = visualization.render(this.ref.current!, {
|
||||
theme,
|
||||
previousRenderState: this.visualizationRenderState,
|
||||
readyCallback: () => {
|
||||
const r = this.props.onReady;
|
||||
if (r) {
|
||||
r();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
ready.then(() => {
|
||||
if (this.props.onReady) {
|
||||
return this.props.onReady();
|
||||
}
|
||||
});
|
||||
|
||||
this.visualizationRenderState = renderState;
|
||||
|
||||
@@ -1,18 +1,48 @@
|
||||
import { BaseSerializer } from "@hediet/semantic-json";
|
||||
import { Theme } from "./Theme";
|
||||
|
||||
export interface Visualizer {
|
||||
readonly id: VisualizerId;
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* Allows to deserialize JSON into an visualization.
|
||||
* This object is also used to inspect the schema of that JSON.
|
||||
*/
|
||||
readonly serializer: BaseSerializer<Visualization>;
|
||||
}
|
||||
|
||||
export interface VisualizerId extends String {
|
||||
__brand: "VisualizerId";
|
||||
}
|
||||
|
||||
export function asVisualizerId(id: string): VisualizerId {
|
||||
return (id as unknown) as VisualizerId;
|
||||
return id as unknown as VisualizerId;
|
||||
}
|
||||
|
||||
export interface Visualizer {
|
||||
readonly id: VisualizerId;
|
||||
export interface Visualization<TRenderState = any> {
|
||||
readonly id: VisualizationId;
|
||||
readonly name: string;
|
||||
readonly serializer: BaseSerializer<Visualization>;
|
||||
readonly priority: number;
|
||||
|
||||
render(
|
||||
target: HTMLDivElement,
|
||||
options: VisualizationRenderOptions<TRenderState>
|
||||
): {
|
||||
renderState: TRenderState;
|
||||
/**
|
||||
* Is resolved when the visualization stabilized and is ready to be shown.
|
||||
* Note that the visualization can be shown before this promise resolves.
|
||||
* This promise is useful for loading animations or integrations.
|
||||
*/
|
||||
ready: Promise<void>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Preloads resources to speed up first rendering.
|
||||
* The visualization does not need to be rendered for this.
|
||||
*/
|
||||
preload(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface VisualizationId extends String {
|
||||
@@ -20,28 +50,10 @@ export interface VisualizationId extends String {
|
||||
}
|
||||
|
||||
export function asVisualizationId(id: string): VisualizationId {
|
||||
return (id as unknown) as VisualizationId;
|
||||
return id as unknown as VisualizationId;
|
||||
}
|
||||
|
||||
export interface VisualizationRenderOptions<TRenderState> {
|
||||
theme: Theme;
|
||||
previousRenderState: TRenderState | undefined;
|
||||
readyCallback: () => void;
|
||||
}
|
||||
|
||||
export interface Visualization<TRenderState = any> {
|
||||
readonly id: VisualizationId;
|
||||
readonly name: string;
|
||||
readonly priority: number;
|
||||
readonly sourceVisualizer: Visualizer;
|
||||
|
||||
render(
|
||||
target: HTMLDivElement,
|
||||
options: VisualizationRenderOptions<TRenderState>
|
||||
): { renderState: TRenderState };
|
||||
|
||||
/**
|
||||
* Preloads resources to speed up first rendering.
|
||||
*/
|
||||
preload(): Promise<void>;
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export class VisualizationFactory {
|
||||
|
||||
private _getSerializer(map: ObservableMap<string, Visualizer>) {
|
||||
return sUnionMany(
|
||||
[...map.values()].map(v => v.serializer.asSerializer()),
|
||||
[...map.values()].map((v) => v.serializer.asSerializer()),
|
||||
{ processingStrategy: "all" }
|
||||
);
|
||||
}
|
||||
@@ -25,6 +25,14 @@ export class VisualizationFactory {
|
||||
this.visualizers.set(visualizer.id.toString(), visualizer);
|
||||
}
|
||||
|
||||
public getRegisteredVisualizers(): Visualizer[] {
|
||||
return [...this.visualizers.values()];
|
||||
}
|
||||
|
||||
public getRegisteredHiddenVisualizer(): Visualizer[] {
|
||||
return [...this.hiddenVisualizers.values()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Hidden visualizers don't appear in `getSerializer` and should only be presented to the user if he explicitly selects it.
|
||||
* `getVisualizations(...).bestVisualization` is only the result of a hidden visualizer if the user requests a hidden visualization id.
|
||||
@@ -38,11 +46,11 @@ export class VisualizationFactory {
|
||||
preferredVisualization: VisualizationId | undefined
|
||||
): Visualizations {
|
||||
const u = this.getSerializer();
|
||||
const result = u.deserialize((data as unknown) as JSONValue);
|
||||
const result = u.deserialize(data as unknown as JSONValue);
|
||||
const allVisualizations = result.value || [];
|
||||
|
||||
const u2 = this._getSerializer(this.hiddenVisualizers);
|
||||
const hiddenResult = u2.deserialize((data as unknown) as JSONValue);
|
||||
const hiddenResult = u2.deserialize(data as unknown as JSONValue);
|
||||
allVisualizations.push(...(hiddenResult.value || []));
|
||||
|
||||
allVisualizations.sort((a, b) => b.priority - a.priority);
|
||||
@@ -53,7 +61,7 @@ export class VisualizationFactory {
|
||||
}
|
||||
if (preferredVisualization) {
|
||||
const preferred = allVisualizations.find(
|
||||
vis => vis.id === preferredVisualization
|
||||
(vis) => vis.id === preferredVisualization
|
||||
);
|
||||
if (preferred) {
|
||||
bestVisualization = preferred;
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
import { BaseSerializer } from "@hediet/semantic-json";
|
||||
import { Visualization, Visualizer, asVisualizerId } from "./Visualizer";
|
||||
|
||||
export function createVisualizer<TData>(options: {
|
||||
export interface CreateVisualizerOptions<TData> {
|
||||
/**
|
||||
* A unique id for this visualizer.
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* A human readable name of this visualizer.
|
||||
* Should be in English language.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
serializer: BaseSerializer<TData>;
|
||||
getVisualization: (data: TData, self: Visualizer) => Visualization;
|
||||
}): Visualizer {
|
||||
}
|
||||
|
||||
export function createVisualizer<TData>(
|
||||
options: CreateVisualizerOptions<TData>
|
||||
): Visualizer {
|
||||
const result: Visualizer = {
|
||||
id: asVisualizerId(options.id),
|
||||
name: options.name,
|
||||
serializer: options.serializer.refine({
|
||||
fromIntermediate: data => options.getVisualization(data, result),
|
||||
fromIntermediate: (data) => options.getVisualization(data, result),
|
||||
canSerialize: (val): val is Visualization => false,
|
||||
toIntermediate: () => {
|
||||
throw new Error("not supported");
|
||||
@@ -5,3 +5,4 @@ export * from "./VisualizerRegistry";
|
||||
export * from "./VisualizationView";
|
||||
export * from "./Theme";
|
||||
export * from "./createVisualizer";
|
||||
export * from "./RegisterVisualizerFn";
|
||||
|
||||
Reference in New Issue
Block a user