Files
2026-07-13 12:25:07 +08:00

155 lines
5.8 KiB
TypeScript

// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃ This file is part of the Perspective library, distributed under the terms ┃
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
import "@perspective-dev/workspace";
import {
HTMLPerspectiveWorkspaceElement,
PerspectiveWorkspaceConfig,
} from "@perspective-dev/workspace";
import * as React from "react";
import { PerspectiveWorkspace } from "@perspective-dev/react";
import perspective_viewer from "@perspective-dev/viewer";
import "@perspective-dev/viewer-datagrid";
import "@perspective-dev/viewer-charts";
import "@perspective-dev/workspace";
import * as Workspace from "@perspective-dev/workspace";
import * as perspective from "@perspective-dev/client";
import "@perspective-dev/viewer/dist/css/themes.css";
import "@perspective-dev/workspace/dist/css/pro.css";
import "./index.css";
// @ts-ignore
import SERVER_WASM from "@perspective-dev/server/dist/wasm/perspective-server.wasm?url";
// @ts-ignore
import CLIENT_WASM from "@perspective-dev/viewer/dist/wasm/perspective-viewer.wasm?url";
await Promise.all([
perspective.init_server(fetch(SERVER_WASM)),
perspective_viewer.init_client(fetch(CLIENT_WASM)),
]);
const CLIENT = await perspective.worker();
interface WorkspaceState {
layout: PerspectiveWorkspaceConfig;
mounted: boolean;
}
interface WorkspaceAppProps {
layout: PerspectiveWorkspaceConfig;
onSpecial?: () => void;
}
const WorkspaceApp: React.FC<WorkspaceAppProps> = (props) => {
const [state, setState] = React.useState<WorkspaceState>({
layout: props.layout,
mounted: true,
});
const onClickAddViewer = () => {
const name = window.crypto.randomUUID();
const data = `a,b,c\n${Math.random()},${Math.random()},${Math.random()}`;
CLIENT.table(data, { name });
const nextId = Workspace.genId(state.layout);
const layout = Workspace.addViewer(
state.layout,
{
table: name,
title: name,
},
nextId,
);
setState({
...state,
layout,
});
};
const onClickToggleMount = () =>
setState((old) => ({ ...old, mounted: !state.mounted }));
const onLayoutUpdate = (layout: PerspectiveWorkspaceConfig) => {
setState({ ...state, layout });
};
React.useEffect(() => {
setState((s) => ({
...s,
layout: props.layout,
}));
}, [props.layout]);
return (
<div className="workspace-container">
<div className="workspace-toolbar">
<button className="toggle-mount" onClick={onClickToggleMount}>
Toggle Mount
</button>
<button className="add-viewer" onClick={onClickAddViewer}>
Add Viewer
</button>
{props.onSpecial && (
<button className="special" onClick={props.onSpecial}>
Special Third Button
</button>
)}
</div>
{state.mounted && (
<PerspectiveWorkspace
client={CLIENT}
layout={state.layout}
onLayoutUpdate={onLayoutUpdate}
/>
)}
</div>
);
};
/// Renders the app with a default empty workspace
export const EmptyWorkspace: React.FC = () => {
return (
<WorkspaceApp
layout={{ sizes: [1], viewers: {}, detail: { main: null } }}
/>
);
};
export const SingleView: React.FC<{ name: string }> = ({ name }) => {
const _table = CLIENT.table("a,b,c\n1,2,3", { name });
const layout: PerspectiveWorkspaceConfig = {
sizes: [1],
detail: {
main: {
type: "tab-area",
currentIndex: 0,
widgets: [name],
},
},
viewers: {
[name]: {
table: name,
columns: ["a", "b", "c"],
title: name,
},
},
};
return <WorkspaceApp layout={layout} />;
};