Skip to content
Draft
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
38 changes: 38 additions & 0 deletions examples/react-workspace-example/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ 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 esbuild from "esbuild";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { dirname } from "path";

const __dirname = dirname(fileURLToPath(import.meta.url));

await esbuild.build({
entryPoints: ["src/index.tsx"],
outdir: "dist",
format: "esm",
bundle: true,
sourcemap: "linked",
target: "es2022",
loader: {
".arrow": "file",
".wasm": "file",
},
assetNames: "[name]",
});

fs.writeFileSync(
path.join(__dirname, "dist/index.html"),
fs.readFileSync(path.join(__dirname, "src/index.html")).toString()
);
21 changes: 21 additions & 0 deletions examples/react-workspace-example/globals.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ 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). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

declare module "*.wasm" {
const content: string;
export default content;
}

declare module "*.arrow" {
const content: string;
export default content;
}
29 changes: 29 additions & 0 deletions examples/react-workspace-example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "react-workspace-example",
"private": true,
"version": "3.7.4",
"description": "An example app using @finos/perspective-react and its workspace component",
"type": "module",
"scripts": {
"build": "node build.js",
"start": "node build.js && http-server dist"
},
"keywords": [],
"license": "Apache-2.0",
"dependencies": {
"@finos/perspective": "workspace:^",
"@finos/perspective-viewer": "workspace:^",
"@finos/perspective-viewer-d3fc": "workspace:^",
"@finos/perspective-viewer-datagrid": "workspace:^",
"@finos/perspective-react": "workspace:^",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"superstore-arrow": "^3.0.0"
},
"devDependencies": {
"esbuild": "^0.25.5",
"http-server": "^14.1.1",
"@types/react": "^18",
"@types/react-dom": "^18"
}
}
68 changes: 68 additions & 0 deletions examples/react-workspace-example/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
* ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
* ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
* ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
* ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
* ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
* ┃ 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). ┃
* ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
*/

body {
margin: 0;
background-color: #f0f0f0;
font-family: "ui-monospace", "SFMono-Regular", "SF Mono", "Menlo",
"Consolas", "Liberation Mono", monospace;
}

.container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;

display: grid;
gap: 8px;
grid-template-columns: 1fr 1fr;
grid-template-rows: 45px 1fr;
}

perspective-viewer {
grid-row: 2;
}

.toolbar {
grid-row: 1;
grid-column-start: 1;
grid-column-end: 3;

display: flex;
flex-direction: row;
gap: 10px;
padding: 10px;
justify-content: stretch;
border-bottom: 1px solid #666;
}

button {
font-family: "ui-monospace", "SFMono-Regular", "SF Mono", "Menlo",
"Consolas", "Liberation Mono", monospace;
}

.workspace-container {
display: flex;
flex-direction: column;

.workspace-toolbar {
display: flex;
flex-direction: row;
}

perspective-workspace {
height: 100vh;
}
}
19 changes: 19 additions & 0 deletions examples/react-workspace-example/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!--

Copyright (c) 2017, the Perspective Authors.

This file is part of the Perspective library, distributed under the terms of
the Apache License 2.0. The full license can be found in the LICENSE file.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Perspective React Example</title>
<link rel="stylesheet" href="./index.css" />
<script type="module" src="./index.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
154 changes: 154 additions & 0 deletions examples/react-workspace-example/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ 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). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

// # [Perspective bootstrapping](https://perspective.finos.org/guide/how_to/javascript/importing.html)

// Here we're initializing the WASM interpreter that powers the perspective API
// and viewer, as covered in the [user guide section on bundling](https://perspective.finos.org/guide/how_to/javascript/importing.html).
// This example is written assuming that the bundler is configured
// to treat these files as a "file" and returns a path as the default export.
// Use ./build.js as an example. The type stubs are in ./globals.d.ts

import perspective from "@finos/perspective";
import perspective_viewer from "@finos/perspective-viewer";
import "@finos/perspective-viewer-datagrid";
import "@finos/perspective-viewer-d3fc";

import SERVER_WASM from "@finos/perspective/dist/wasm/perspective-server.wasm";
import CLIENT_WASM from "@finos/perspective-viewer/dist/wasm/perspective-viewer.wasm";

await Promise.all([
perspective.init_server(fetch(SERVER_WASM)),
perspective_viewer.init_client(fetch(CLIENT_WASM)),
]);

// # Data Source

// Data source creates a static Web Worker instance of Perspective engine, and a
// table creation function which both downloads data and loads it into the
// engine.

import type * as psp from "@finos/perspective";
import * as Workspace from "@finos/perspective-workspace";

import * as React from "react";
import { createRoot } from "react-dom/client";
import { PerspectiveWorkspace } from "@finos/perspective-react";
import { PerspectiveWorkspaceConfig } from "@finos/perspective-workspace";

import "@finos/perspective-viewer/dist/css/pro.css";
import "@finos/perspective-workspace/dist/css/pro.css";
import "./index.css";

const CLIENT = await perspective.worker();

interface WorkspaceState {
mounted: boolean;
config: PerspectiveWorkspaceConfig<string>;
tables: Record<string, Promise<psp.Table>>;
/// This object is kept for the 'swap tables' button.
/// It is a backup set of tables that correspond in keys to `tables`
/// but with different data.
swapTables: Record<string, Promise<psp.Table>>;
/// if false use `tables` and true use `swapTables` in the workspace
swap: boolean;
}

const WorkspaceApp: React.FC = () => {
const [state, setState] = React.useState<WorkspaceState>({
mounted: true,
tables: {},
swapTables: {},
config: {
sizes: [],
viewers: {},
detail: undefined,
},
swap: false,
});

const onClickAddViewer = React.useCallback(async () => {
const name = window.crypto.randomUUID().slice(0, 8);
const data = `a,b,c\n${Math.random()},${Math.random()},${Math.random()}`;
const swapData = `a,b,c\n${Math.random()},${Math.random()},${Math.random()}\n${Math.random()},${Math.random()},${Math.random()}`;
// dont assign internal names to the tables they are not used by the workspace
const t = CLIENT.table(data);
const swap = CLIENT.table(swapData);
const config = Workspace.addViewer(state.config, {
table: name,
title: name,
});
const tables = { ...state.tables, [name]: t };
const swapTables = { ...state.swapTables, [name]: swap };
setState({
...state,
tables,
config,
swapTables,
});
}, [state]);

const onLayoutUpdate: (detail: {
layout: PerspectiveWorkspaceConfig<string>;
tables: Record<string, psp.Table | Promise<psp.Table>>;
}) => void = React.useCallback(
({ layout, tables }) => {
const newTables = Object.fromEntries(
Object.entries(tables).map(([k, v]) => [k, Promise.resolve(v)])
);
setState({
...state,
config: layout,
tables: state.swap ? state.tables : newTables,
swapTables: state.swap ? newTables : state.swapTables,
});
},
[state]
);

const onClickToggleMount = () =>
setState((old) => ({ ...old, mounted: !state.mounted }));

// swaps the tables out but uses the same name of them.
// this keeps the layout the same, but the data within each viewer changes
const swapTables = React.useCallback(() => {
setState({
...state,
swap: !state.swap,
});
}, [state]);

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>
<button className="swap" onClick={swapTables}>
Swap underlying tables
</button>
</div>
{state.mounted && (
<PerspectiveWorkspace
tables={state.swap ? state.swapTables : state.tables}
config={state.config}
onLayoutUpdate={onLayoutUpdate}
/>
)}
</div>
);
};

createRoot(document.getElementById("root")!).render(<WorkspaceApp />);
36 changes: 36 additions & 0 deletions examples/react-workspace-example/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"compilerOptions": {
"moduleResolution": "bundler",
"target": "ESNext",
"module": "ESNext",
"lib": [
"ES2020",
"dom"
],
"strict": true,
"alwaysStrict": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitUseStrict": false,
"noUnusedLocals": false,
"strictNullChecks": true,
"skipLibCheck": false,
"removeComments": false,
"jsx": "react-jsx",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"importHelpers": false,
"noEmitHelpers": true,
"inlineSourceMap": false,
"sourceMap": true,
"emitDecoratorMetadata": false,
"experimentalDecorators": true,
"downlevelIteration": true,
"pretty": true
},
"exclude": [
"node_modules"
]
}
4 changes: 3 additions & 1 deletion packages/perspective-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"author": "",
"license": "ISC",
"dependencies": {
"@finos/perspective": "workspace:^"
"@finos/perspective": "workspace:^",
"async-mutex": "0.5.0"
},
"exports": {
".": "./dist/esm/index.js"
Expand All @@ -24,6 +25,7 @@
"peerDependencies": {
"@finos/perspective": "workspace:^",
"@finos/perspective-viewer": "workspace:^",
"@finos/perspective-workspace": "workspace:^",
"@types/react": "^18",
"react": "^18",
"react-dom": "^18"
Expand Down
Loading