chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 { NodeModulesExternal } from "@perspective-dev/esbuild-plugin/external.js";
|
||||
import { build } from "@perspective-dev/esbuild-plugin/build.js";
|
||||
import "zx/globals";
|
||||
|
||||
const BUILD = [
|
||||
{
|
||||
define: {
|
||||
global: "window",
|
||||
},
|
||||
entryPoints: ["src/index.tsx"],
|
||||
format: "esm",
|
||||
loader: {},
|
||||
outfile: "dist/esm/index.js",
|
||||
plugins: [NodeModulesExternal()],
|
||||
},
|
||||
];
|
||||
|
||||
async function build_all() {
|
||||
await Promise.all(BUILD.map(build)).catch(() => process.exit(1));
|
||||
try {
|
||||
await $`tsc --project ./tsconfig.json`.stdio(
|
||||
"inherit",
|
||||
"inherit",
|
||||
"inherit",
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
build_all();
|
||||
@@ -0,0 +1,15 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 * as fs from "node:fs";
|
||||
|
||||
fs.rmSync("dist", { recursive: true, force: true });
|
||||
@@ -0,0 +1,15 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 "zx/globals";
|
||||
|
||||
await $`typedoc --tsconfig tsconfig.json --out ../../docs/static/react`;
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "@perspective-dev/react",
|
||||
"version": "4.5.2",
|
||||
"description": "React component wrappers for `<perspective-viewer>` and `<perspective-workspace>`",
|
||||
"keywords": [
|
||||
"perspective",
|
||||
"react",
|
||||
"data",
|
||||
"analytics",
|
||||
"visualization"
|
||||
],
|
||||
"homepage": "https://perspective-dev.github.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/perspective-dev/perspective"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/esm/index.d.ts",
|
||||
"import": "./dist/esm/index.js",
|
||||
"default": "./dist/esm/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "node ./build.mjs",
|
||||
"clean": "node ./clean.mjs",
|
||||
"docs": "node ./docs.mjs"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@perspective-dev/client": "workspace:",
|
||||
"@perspective-dev/viewer": "workspace:",
|
||||
"@perspective-dev/workspace": "workspace:",
|
||||
"@types/react": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@perspective-dev/esbuild-plugin": "workspace:",
|
||||
"superstore-arrow": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"typedoc": "catalog:",
|
||||
"zx": "catalog:"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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). ┃
|
||||
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
|
||||
/**
|
||||
*
|
||||
* # See Also
|
||||
*
|
||||
* [`react-example`](https://github.com/perspective-dev/perspective/tree/master/examples/react-example)
|
||||
* project from the Perspective GitHub repo.
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
export * from "./viewer";
|
||||
export * from "./workspace";
|
||||
@@ -0,0 +1,27 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 * as React from "react";
|
||||
|
||||
export function usePspListener<A>(
|
||||
el: HTMLElement | undefined | null,
|
||||
event: string,
|
||||
cb?: (x: A) => void,
|
||||
) {
|
||||
React.useEffect(() => {
|
||||
if (!cb || !el) return;
|
||||
const ctx = new AbortController();
|
||||
const callback = (e: Event) => cb((e as CustomEvent).detail);
|
||||
el?.addEventListener(event, callback, { signal: ctx.signal });
|
||||
return () => ctx.abort();
|
||||
}, [el, cb]);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 * as React from "react";
|
||||
import type * as psp from "@perspective-dev/client";
|
||||
import type * as pspViewer from "@perspective-dev/viewer";
|
||||
import { usePspListener } from "./utils";
|
||||
|
||||
function PerspectiveViewerImpl(props: PerspectiveViewerProps) {
|
||||
const [viewer, setViewer] =
|
||||
React.useState<pspViewer.HTMLPerspectiveViewerElement | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
viewer?.delete();
|
||||
};
|
||||
}, [viewer]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.client) {
|
||||
viewer?.load(props.client);
|
||||
} else {
|
||||
viewer?.eject();
|
||||
}
|
||||
}, [viewer, props.client]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.client && props.config) {
|
||||
viewer?.restore(props.config);
|
||||
}
|
||||
}, [viewer, props.client, JSON.stringify(props.config)]);
|
||||
|
||||
usePspListener(viewer, "perspective-click", props.onClick);
|
||||
usePspListener(viewer, "perspective-select", props.onSelect);
|
||||
usePspListener(viewer, "perspective-config-update", props.onConfigUpdate);
|
||||
|
||||
return (
|
||||
<perspective-viewer
|
||||
ref={setViewer}
|
||||
id={props.id}
|
||||
className={props.className}
|
||||
hidden={props.hidden}
|
||||
slot={props.slot}
|
||||
style={props.style}
|
||||
tabIndex={props.tabIndex}
|
||||
title={props.title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for the `<PerspectiveViewer>` component.
|
||||
*/
|
||||
export interface PerspectiveViewerProps {
|
||||
client?: psp.Client | Promise<psp.Client> | psp.Table | Promise<psp.Table>;
|
||||
config?: pspViewer.ViewerConfigUpdate;
|
||||
onConfigUpdate?: (config: pspViewer.ViewerConfigUpdate) => void;
|
||||
onClick?: (data: pspViewer.PerspectiveClickEventDetail) => void;
|
||||
onSelect?: (data: pspViewer.PerspectiveSelectEventDetail) => void;
|
||||
|
||||
// Applicable props from `React.HTMLAttributes`, which we cannot extend
|
||||
// directly because Perspective changes the signature of `onClick`.
|
||||
className?: string | undefined;
|
||||
hidden?: boolean | undefined;
|
||||
id?: string | undefined;
|
||||
slot?: string | undefined;
|
||||
style?: React.CSSProperties | undefined;
|
||||
tabIndex?: number | undefined;
|
||||
title?: string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A React wrapper component for `<perspective-viewer>` Custom Element.
|
||||
*/
|
||||
export const PerspectiveViewer: React.FC<PerspectiveViewerProps> = React.memo(
|
||||
PerspectiveViewerImpl,
|
||||
);
|
||||
@@ -0,0 +1,95 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 type * as psp from "@perspective-dev/client";
|
||||
import type * as psp_viewer from "@perspective-dev/viewer";
|
||||
import type * as pspWorkspace from "@perspective-dev/workspace";
|
||||
import { PerspectiveWorkspaceConfig } from "@perspective-dev/workspace";
|
||||
import * as utils from "./utils";
|
||||
import * as React from "react";
|
||||
|
||||
export interface NewViewEventDetail {
|
||||
config: psp_viewer.ViewerConfigUpdate;
|
||||
widget: pspWorkspace.PerspectiveViewerWidget;
|
||||
}
|
||||
|
||||
export interface ToggleGloalFilterEventDetail {
|
||||
widget: pspWorkspace.PerspectiveViewerWidget;
|
||||
isGlobalFilter: boolean;
|
||||
}
|
||||
|
||||
export interface PerspectiveWorkspaceProps
|
||||
extends React.HTMLAttributes<HTMLElement> {
|
||||
client: psp.Client | Promise<psp.Client>;
|
||||
layout: PerspectiveWorkspaceConfig;
|
||||
onLayoutUpdate?: (layout: PerspectiveWorkspaceConfig) => void;
|
||||
onNewView?: (detail: NewViewEventDetail) => void;
|
||||
onToggleGlobalFilter?: (detail: ToggleGloalFilterEventDetail) => void;
|
||||
}
|
||||
|
||||
const PerspectiveWorkspaceImpl = React.forwardRef<
|
||||
pspWorkspace.HTMLPerspectiveWorkspaceElement | undefined,
|
||||
PerspectiveWorkspaceProps
|
||||
>(
|
||||
(
|
||||
{
|
||||
client,
|
||||
layout,
|
||||
onLayoutUpdate,
|
||||
onNewView,
|
||||
onToggleGlobalFilter,
|
||||
...htmlAttributes
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const [workspace, setWorkspace] =
|
||||
React.useState<pspWorkspace.HTMLPerspectiveWorkspaceElement>();
|
||||
|
||||
React.useImperativeHandle(ref, () => workspace, [workspace]);
|
||||
React.useEffect(() => {
|
||||
if (workspace && layout) {
|
||||
workspace.restore(layout);
|
||||
}
|
||||
}, [workspace, layout]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (workspace && client) {
|
||||
workspace.load(client);
|
||||
}
|
||||
}, [workspace, client]);
|
||||
|
||||
utils.usePspListener(workspace, "workspace-new-view", onNewView);
|
||||
utils.usePspListener(
|
||||
workspace,
|
||||
"workspace-layout-update",
|
||||
onLayoutUpdate
|
||||
? ({ layout }: { layout: PerspectiveWorkspaceConfig }) =>
|
||||
workspace?.save().then((x) => onLayoutUpdate(x))
|
||||
: undefined,
|
||||
);
|
||||
|
||||
utils.usePspListener(
|
||||
workspace,
|
||||
"workspace-toggle-global-filter",
|
||||
onToggleGlobalFilter,
|
||||
);
|
||||
|
||||
return (
|
||||
<perspective-workspace
|
||||
ref={(r) => setWorkspace(r ?? undefined)}
|
||||
{...htmlAttributes}
|
||||
></perspective-workspace>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export const PerspectiveWorkspace = React.memo(PerspectiveWorkspaceImpl);
|
||||
@@ -0,0 +1,106 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 from "@perspective-dev/client";
|
||||
import perspective_viewer from "@perspective-dev/viewer";
|
||||
import "@perspective-dev/viewer-datagrid";
|
||||
import "@perspective-dev/viewer-charts";
|
||||
import "@perspective-dev/workspace";
|
||||
|
||||
// @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)),
|
||||
]);
|
||||
|
||||
import * as psp from "@perspective-dev/client";
|
||||
import type * as pspViewer from "@perspective-dev/viewer";
|
||||
|
||||
// @ts-ignore
|
||||
import SUPERSTORE_ARROW from "superstore-arrow/superstore.lz4.arrow?url";
|
||||
|
||||
import * as React from "react";
|
||||
import {
|
||||
PerspectiveViewer,
|
||||
PerspectiveWorkspace,
|
||||
} from "@perspective-dev/react";
|
||||
|
||||
import "@perspective-dev/viewer/dist/css/themes.css";
|
||||
import "./index.css";
|
||||
|
||||
const WORKER = await perspective.worker();
|
||||
|
||||
async function createNewSuperstoreTable(): Promise<psp.Table> {
|
||||
const req = fetch(SUPERSTORE_ARROW);
|
||||
const resp = await req;
|
||||
const buffer = await resp.arrayBuffer();
|
||||
return await WORKER.table(buffer);
|
||||
}
|
||||
|
||||
const CONFIG: pspViewer.ViewerConfigUpdate = {
|
||||
group_by: ["State"],
|
||||
};
|
||||
|
||||
interface ToolbarState {
|
||||
mounted: boolean;
|
||||
table?: Promise<psp.Table>;
|
||||
config: pspViewer.ViewerConfigUpdate;
|
||||
}
|
||||
|
||||
export const App: React.FC = () => {
|
||||
const [state, setState] = React.useState<ToolbarState>(() => ({
|
||||
mounted: true,
|
||||
table: createNewSuperstoreTable(),
|
||||
config: { ...CONFIG },
|
||||
}));
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
state.table?.then((table) => table?.delete({ lazy: true }));
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onConfigUpdate = (config: pspViewer.ViewerConfigUpdate) => {
|
||||
console.log("Config Update Event", config);
|
||||
setState({ ...state, config });
|
||||
};
|
||||
|
||||
const onClick = (detail: pspViewer.PerspectiveClickEventDetail) => {
|
||||
console.log("Click Event,", detail);
|
||||
};
|
||||
|
||||
const onSelect = (detail: pspViewer.PerspectiveSelectEventDetail) => {
|
||||
console.log("Select Event", detail);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
{state.mounted && (
|
||||
<>
|
||||
<PerspectiveViewer table={state.table} />
|
||||
<PerspectiveViewer
|
||||
table={state.table}
|
||||
config={state.config}
|
||||
onClick={onClick}
|
||||
onSelect={onSelect}
|
||||
onConfigUpdate={onConfigUpdate}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 { test, expect } from "@playwright/experimental-ct-react";
|
||||
|
||||
import { App } from "./basic.story";
|
||||
import { EmptyWorkspace, SingleView } from "./workspace.story";
|
||||
|
||||
test.describe("Perspective React", () => {
|
||||
test("The viewer loads with data in it", async ({ page, mount }) => {
|
||||
const comp = await mount(<App></App>);
|
||||
const count = await page.evaluate(async () => {
|
||||
await new Promise((x) => setTimeout(x, 1000));
|
||||
return document.querySelectorAll("perspective-viewer").length;
|
||||
});
|
||||
|
||||
expect(count).toBe(2);
|
||||
});
|
||||
|
||||
test("React workspace functionality", async ({ page, mount }) => {
|
||||
const comp = await mount(<EmptyWorkspace />);
|
||||
const toggleMount = comp.locator("button.toggle-mount");
|
||||
const addViewer = comp.locator("button.add-viewer");
|
||||
const workspace = comp.locator("perspective-workspace");
|
||||
const viewer = comp.locator("perspective-viewer");
|
||||
await toggleMount.waitFor();
|
||||
await addViewer.click();
|
||||
await addViewer.click();
|
||||
await addViewer.click();
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
document.querySelector("perspective-workspace")!.children
|
||||
.length === 3,
|
||||
);
|
||||
|
||||
await expect(viewer).toHaveCount(3);
|
||||
await toggleMount.click();
|
||||
await workspace.waitFor({ state: "detached" });
|
||||
|
||||
// TODO: This test gets stuck in CI
|
||||
await page.waitForTimeout(10);
|
||||
await toggleMount.click();
|
||||
await workspace.waitFor();
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
document.querySelector("perspective-workspace")!.children
|
||||
.length === 3,
|
||||
);
|
||||
await expect(viewer).toHaveCount(3);
|
||||
});
|
||||
|
||||
test("Adding a viewer in single-document mode leaves SDM", async ({
|
||||
page,
|
||||
mount,
|
||||
}) => {
|
||||
const name = "abcdef";
|
||||
const comp = await mount(<SingleView name={name} />);
|
||||
const addViewer = comp.locator("button.add-viewer");
|
||||
const viewer = comp.locator("perspective-viewer");
|
||||
const settingsBtn = comp.locator(`perspective-viewer #settings_button`);
|
||||
|
||||
await settingsBtn.waitFor();
|
||||
await addViewer.waitFor();
|
||||
await addViewer.click();
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
document.querySelector("perspective-workspace")!.children
|
||||
.length === 2,
|
||||
);
|
||||
|
||||
expect(await viewer.count()).toBe(2);
|
||||
await settingsBtn.first().click();
|
||||
const settingsPanel = viewer.locator("#settings_panel");
|
||||
await settingsPanel.waitFor();
|
||||
await addViewer.click();
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
document.querySelector("perspective-workspace")!.children
|
||||
.length === 3,
|
||||
);
|
||||
expect(await viewer.count()).toBe(3);
|
||||
await settingsPanel.waitFor({ state: "detached" });
|
||||
});
|
||||
});
|
||||
@@ -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). ┃
|
||||
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
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} />;
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist/esm",
|
||||
"rootDir": "./src",
|
||||
"moduleResolution": "bundler",
|
||||
"skipLibCheck": true,
|
||||
"jsx": "preserve",
|
||||
},
|
||||
"files": [
|
||||
"src/index.tsx"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user