From 22ad13d04a4fd9316943484c839a3335ff7765c6 Mon Sep 17 00:00:00 2001 From: Timothy Bess Date: Thu, 9 Jan 2025 04:00:57 +0900 Subject: [PATCH] Initial implementation of React perspective viewer wrapper. --- examples/react-example/package.json | 11 +- examples/react-example/src/index.css | 34 +++ examples/react-example/src/index.html | 7 +- examples/react-example/src/index.tsx | 153 ++++++++++-- examples/react-example/tsconfig.json | 19 +- .../{webpack.config.js => webpack.config.cjs} | 0 packages/perspective-react/build.js | 43 ++++ packages/perspective-react/package.json | 28 +++ packages/perspective-react/src/index.tsx | 225 ++++++++++++++++++ packages/perspective-react/tsconfig.json | 17 ++ pnpm-lock.yaml | 98 ++++---- pnpm-workspace.yaml | 1 + tools/perspective-scripts/setup.mjs | 5 + 13 files changed, 561 insertions(+), 80 deletions(-) rename examples/react-example/{webpack.config.js => webpack.config.cjs} (100%) create mode 100644 packages/perspective-react/build.js create mode 100644 packages/perspective-react/package.json create mode 100644 packages/perspective-react/src/index.tsx create mode 100644 packages/perspective-react/tsconfig.json diff --git a/examples/react-example/package.json b/examples/react-example/package.json index 278a66bd86..28bb0d0c8b 100644 --- a/examples/react-example/package.json +++ b/examples/react-example/package.json @@ -3,6 +3,7 @@ "private": true, "version": "3.2.1", "description": "An example app built using `@finos/perspective-viewer`.", + "type": "module", "scripts": { "start": "webpack serve --open", "webpack": "webpack --colour" @@ -14,13 +15,15 @@ "@finos/perspective-viewer": "workspace:^", "@finos/perspective-viewer-d3fc": "workspace:^", "@finos/perspective-viewer-datagrid": "workspace:^", - "react": "^17.0.2", - "react-dom": "^17.0.2" + "@finos/perspective-react": "workspace:^", + "react": "^18.0.0", + "react-dom": "^18.0.0" }, "devDependencies": { "@finos/perspective-webpack-plugin": "workspace:^", - "@types/react": "^17.0.2", - "@types/react-dom": "^17.0.2", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "html-webpack-plugin": "^5.1.0", "source-map-loader": "^0.2.4", "ts-loader": "^6.2.1" } diff --git a/examples/react-example/src/index.css b/examples/react-example/src/index.css index 0e04e633ef..049aafbc4e 100644 --- a/examples/react-example/src/index.css +++ b/examples/react-example/src/index.css @@ -20,3 +20,37 @@ perspective-viewer { right: 0; bottom: 0; } + +body { + margin: 0; + background-color: #f0f0f0; +} + +.viewer-container { + display: flex; + position: relative; + flex-direction: column; + flex-grow: 1; +} + +.workspace { + display: flex; + width: 100%; + height: 100%; + gap: 5px; +} + +.container { + display: flex; + flex-direction: column; + height: 100vh; + overflow: hidden; +} + +.toolbar { + display: flex; + flex-direction: row; + gap: 10px; + padding: 10px; + background-color: #cecece; +} diff --git a/examples/react-example/src/index.html b/examples/react-example/src/index.html index 7bf915527d..3728010414 100644 --- a/examples/react-example/src/index.html +++ b/examples/react-example/src/index.html @@ -8,11 +8,10 @@ - + Perspective React Example
- - - \ No newline at end of file + + diff --git a/examples/react-example/src/index.tsx b/examples/react-example/src/index.tsx index bb0ceb89e3..9782504d86 100644 --- a/examples/react-example/src/index.tsx +++ b/examples/react-example/src/index.tsx @@ -11,47 +11,160 @@ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ import * as React from "react"; -import * as ReactDOM from "react-dom"; +import { createRoot } from "react-dom/client"; import * as perspective from "@finos/perspective"; import "@finos/perspective-viewer"; import "@finos/perspective-viewer-datagrid"; import "@finos/perspective-viewer-d3fc"; +import { ViewerConfigUpdate } from "@finos/perspective-viewer"; import { - PerspectiveViewerConfig, - HTMLPerspectiveViewerElement, -} from "@finos/perspective-viewer"; + PerspectiveViewer, + usePspActions, + PerspectiveProvider, + usePsp, +} from "@finos/perspective-react"; import "./index.css"; -const worker = perspective.default.worker(); +const worker = await perspective.worker(); -const getTable = async (): Promise => { +async function getTable(): Promise { const req = fetch("./superstore.lz4.arrow"); const resp = await req; const buffer = await resp.arrayBuffer(); return await worker.table(buffer as any); -}; +} -const config: PerspectiveViewerConfig = { +const config: ViewerConfigUpdate = { group_by: ["State"], }; -const App = (): React.ReactElement => { - const viewer = React.useRef(null); +const Root: React.FC = () => { + return ( + + + + ); +}; + +// TODO: Add viewers in a loop keyed by a unique id that virtually scrolls. + +const App: React.FC = () => { + const actions = usePspActions(); + const [state, setState] = React.useState({ + mounted: true, + selectedTable: "superstore", + }); React.useEffect(() => { - getTable().then((table) => { - if (viewer.current) { - viewer.current.load(Promise.resolve(table)); - viewer.current.restore(config); - } - }); + actions.addTable({ table: "superstore", promise: getTable() }); }, []); - return ; + return ( +
+ +
+
+ {state.mounted && ( + + )} +
+
+ {state.mounted && ( + + )} +
+
+
+ ); +}; + +interface ToolbarState { + mounted: boolean; + selectedTable: string; +} + +const Toolbar: React.FC<{ + state: ToolbarState; + setState: React.Dispatch>; +}> = ({ state, setState }) => { + const actions = usePspActions(); + const psp = usePsp(); + + const selectOptions = Object.keys(psp.tables).map((table) => ( + + )); + + return ( +
+
+ +
+ + + +
+ ); }; -window.addEventListener("load", () => { - ReactDOM.render(, document.getElementById("root")); -}); +createRoot(document.getElementById("root")!).render(); diff --git a/examples/react-example/tsconfig.json b/examples/react-example/tsconfig.json index 5d5a1d72cc..de6514b455 100644 --- a/examples/react-example/tsconfig.json +++ b/examples/react-example/tsconfig.json @@ -1,8 +1,11 @@ { "compilerOptions": { "moduleResolution": "bundler", - "target": "es5", - "lib": ["es6", "dom"], + "target": "es2020", + "lib": [ + "es2020", + "dom" + ], "module": "esnext", "strict": true, "alwaysStrict": true, @@ -10,15 +13,15 @@ "noImplicitReturns": true, "noImplicitThis": true, "noImplicitUseStrict": false, - "noUnusedLocals": true, + "noUnusedLocals": false, "strictNullChecks": true, "skipLibCheck": true, "removeComments": false, - "jsx": "react", + "jsx": "react-jsx", "allowSyntheticDefaultImports": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, - "importHelpers": true, + "importHelpers": false, "noEmitHelpers": true, "inlineSourceMap": false, "sourceMap": true, @@ -27,5 +30,7 @@ "downlevelIteration": true, "pretty": true }, - "exclude": ["node_modules"] -} + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/examples/react-example/webpack.config.js b/examples/react-example/webpack.config.cjs similarity index 100% rename from examples/react-example/webpack.config.js rename to examples/react-example/webpack.config.cjs diff --git a/packages/perspective-react/build.js b/packages/perspective-react/build.js new file mode 100644 index 0000000000..70bfe1994b --- /dev/null +++ b/packages/perspective-react/build.js @@ -0,0 +1,43 @@ +// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃ +// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃ +// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃ +// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃ +// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ +// ┃ 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 { build } from "@finos/perspective-esbuild-plugin/build.js"; +import "zx/globals"; + +const BUILD = [ + { + define: { + global: "window", + }, + entryPoints: ["src/index.tsx"], + format: "esm", + external: ["react", "react-dom"], + loader: {}, + outfile: "dist/esm/index.js", + }, +]; + +async function build_all() { + await Promise.all(BUILD.map(build)).catch(() => process.exit(1)); + try { + await $`npx tsc --project ./tsconfig.json`.stdio( + "inherit", + "inherit", + "inherit" + ); + } catch (e) { + console.error(e); + process.exit(1); + } +} + +build_all(); diff --git a/packages/perspective-react/package.json b/packages/perspective-react/package.json new file mode 100644 index 0000000000..b7ab60bcae --- /dev/null +++ b/packages/perspective-react/package.json @@ -0,0 +1,28 @@ +{ + "name": "@finos/perspective-react", + "version": "1.0.0", + "description": "", + "main": "index.js", + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@finos/perspective": "workspace:^" + }, + "exports": { + ".": "./dist/esm/index.js" + }, + "type": "module", + "scripts": { + "build": "node ./build.js", + "clean": "rimraf dist" + }, + "peerDependencies": { + "@types/react": "^18.0.0", + "react": "^18.0.0" + }, + "devDependencies": { + "@finos/perspective-esbuild-plugin": "workspace:^", + "zx": "^8.3.0" + } +} diff --git a/packages/perspective-react/src/index.tsx b/packages/perspective-react/src/index.tsx new file mode 100644 index 0000000000..d0729dcdd7 --- /dev/null +++ b/packages/perspective-react/src/index.tsx @@ -0,0 +1,225 @@ +// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃ +// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃ +// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃ +// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃ +// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ +// ┃ 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 "@finos/perspective"; +import type { + HTMLPerspectiveViewerElement, + ViewerConfigUpdate, +} from "@finos/perspective-viewer"; + +type PspAction = + | { + type: "addTable"; + table: string; + promise: Promise; + } + | { + type: "removeTable"; + table: string; + }; + +type GeneratorsByType = { + [K in T["type"]]: (fields: Omit, "type">) => T; +}; + +type FunctionsByType = { + [K in T["type"]]: (fields: Omit, "type">) => void; +}; + +function createGenerator(): GeneratorsByType { + return new Proxy( + {}, + { + get: (_, type: string) => { + return (fields: Record) => ({ + type, + ...fields, + }); + }, + } + ) as GeneratorsByType; +} + +const actions: GeneratorsByType = createGenerator(); + +export function usePspActions(): FunctionsByType { + const dispatch = usePspDispatch(); + return new Proxy( + {}, + { + get: (_, type: string) => { + return (fields: Record) => + dispatch((actions as any)[type](fields)); + }, + } + ) as FunctionsByType; +} + +interface PspState { + tables: Record>; +} + +const PspContext = React.createContext(null); + +const PspDispatchContext = + React.createContext | null>(null); + +export const PerspectiveProvider: React.FC = ({ + children, +}) => { + const [state, dispatch] = React.useReducer( + (state: PspState, action: PspAction) => { + switch (action.type) { + case "addTable": + return { + ...state, + tables: { + ...state.tables, + [action.table]: action.promise, + }, + }; + case "removeTable": + const { [action.table]: _, ...tables } = state.tables; + return { ...state, tables }; + default: + return state; + } + }, + { tables: {} } + ); + + return ( + + + {children} + + + ); +}; + +export function usePsp(): PspState { + const ctx = React.useContext(PspContext); + if (!ctx) { + throw new Error("usePsp must be used within a PerspectiveProvider"); + } + return ctx; +} + +function usePspDispatch(): React.Dispatch { + const ctx = React.useContext(PspDispatchContext); + if (!ctx) { + throw new Error( + "usePspDispatch must be used within a PerspectiveProvider" + ); + } + return ctx; +} + +function validateProps(props: PerspectiveViewerProps) { + if (props.selectedTable && props.tableData) { + throw new Error( + "Cannot provide both `selectedTable` and `tableData` props" + ); + } +} + +interface PerspectiveViewerProps { + selectedTable?: string; + config?: ViewerConfigUpdate; + onConfigUpdate?: (e: ViewerConfigUpdate) => void; + tableData?: string | Array | Record; +} + +const PerspectiveViewerInternal: React.FC = (props) => { + validateProps(props); + const { selectedTable, config, onConfigUpdate, tableData } = props; + + const [viewer, setViewer] = React.useState(); + const [version, setVersion] = React.useState(0); + const versionRef = React.useRef(version); + const ctx = usePsp(); + const tablePromise = selectedTable ? ctx.tables[selectedTable] : undefined; + + // Allows the effect block to access the latest `onConfigUpdate` function without + // needing to re-register the event listener every time it changes. + const onConfigUpdateRef = React.useRef(onConfigUpdate); + onConfigUpdateRef.current = onConfigUpdate; + + const lastConfigRef = React.useRef(); + const [loaded, setLoaded] = React.useState(false); + + React.useEffect(() => { + if (!loaded) { + return; + } + if (viewer && config) { + // Check identity to avoid infinite loop. + if (lastConfigRef.current !== config) { + viewer.restore(config); + } + } + }, [viewer, config, loaded]); + + React.useEffect(() => { + if (viewer) { + const configUpdate = (e: CustomEvent) => { + lastConfigRef.current = e.detail; + onConfigUpdateRef.current && + onConfigUpdateRef.current(e.detail); + }; + viewer.addEventListener("perspective-config-update", configUpdate); + return () => { + viewer.removeEventListener( + "perspective-config-update", + configUpdate + ); + }; + } + }, [viewer]); + + React.useEffect(() => { + if (viewer && selectedTable) { + if (selectedTable in ctx.tables) { + const ts = new Date().toISOString(); + viewer.load(tablePromise).then(() => { + // if the viewer we're loading is deleted mid-load, + // setting loaded will cause a restore before load + // is complete on the new viewer. + if (version === versionRef.current) { + setLoaded(true); + console.log(ts, "loaded", version, versionRef.current); + } else { + console.log( + ts, + "cancelled_load", + version, + versionRef.current + ); + } + }); + return () => { + // Force recreation of webcomponent on next render. + setVersion(version + 1); + versionRef.current = version + 1; + setLoaded(false); + }; + } + } + }, [viewer, tablePromise, selectedTable]); + + return ; +}; + +export const PerspectiveViewer: React.FC = React.memo( + PerspectiveViewerInternal +); diff --git a/packages/perspective-react/tsconfig.json b/packages/perspective-react/tsconfig.json new file mode 100644 index 0000000000..d43d402571 --- /dev/null +++ b/packages/perspective-react/tsconfig.json @@ -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": "react-jsx", + }, + "files": [ + "src/index.tsx" + ] +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f68bdb5535..bb01246774 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -417,6 +417,9 @@ importers: '@finos/perspective': specifier: workspace:^ version: link:../../rust/perspective-js + '@finos/perspective-react': + specifier: workspace:^ + version: link:../../packages/perspective-react '@finos/perspective-viewer': specifier: workspace:^ version: link:../../rust/perspective-viewer @@ -427,21 +430,24 @@ importers: specifier: workspace:^ version: link:../../packages/perspective-viewer-datagrid react: - specifier: ^17.0.2 - version: 17.0.2 + specifier: ^18.0.0 + version: 18.3.1 react-dom: - specifier: ^17.0.2 - version: 17.0.2(react@17.0.2) + specifier: ^18.0.0 + version: 18.3.1(react@18.3.1) devDependencies: '@finos/perspective-webpack-plugin': specifier: workspace:^ version: link:../../packages/perspective-webpack-plugin '@types/react': - specifier: ^17.0.2 - version: 17.0.82 + specifier: ^18.0.0 + version: 18.3.8 '@types/react-dom': - specifier: ^17.0.2 - version: 17.0.25 + specifier: ^18.0.0 + version: 18.3.5(@types/react@18.3.8) + html-webpack-plugin: + specifier: ^5.1.0 + version: 5.6.0(webpack@5.94.0(esbuild@0.14.54)(webpack-cli@4.10.0(webpack@5.94.0))) source-map-loader: specifier: ^0.2.4 version: 0.2.4 @@ -607,6 +613,25 @@ importers: specifier: ^8.1.8 version: 8.1.9 + packages/perspective-react: + dependencies: + '@finos/perspective': + specifier: workspace:^ + version: link:../../rust/perspective-js + '@types/react': + specifier: ^18.0.0 + version: 18.3.8 + react: + specifier: ^18.0.0 + version: 18.3.1 + devDependencies: + '@finos/perspective-esbuild-plugin': + specifier: workspace:^ + version: link:../perspective-esbuild-plugin + zx: + specifier: ^8.3.0 + version: 8.3.0 + packages/perspective-viewer-d3fc: dependencies: '@finos/perspective': @@ -3088,8 +3113,10 @@ packages: '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - '@types/react-dom@17.0.25': - resolution: {integrity: sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA==} + '@types/react-dom@18.3.5': + resolution: {integrity: sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==} + peerDependencies: + '@types/react': ^18.0.0 '@types/react-router-config@5.0.11': resolution: {integrity: sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw==} @@ -7385,11 +7412,6 @@ packages: typescript: optional: true - react-dom@17.0.2: - resolution: {integrity: sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==} - peerDependencies: - react: 17.0.2 - react-dom@18.3.1: resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: @@ -7447,10 +7469,6 @@ packages: peerDependencies: react: '>=15' - react@17.0.2: - resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==} - engines: {node: '>=0.10.0'} - react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -7705,9 +7723,6 @@ packages: sax@1.4.1: resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} - scheduler@0.20.2: - resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==} - scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -8807,6 +8822,11 @@ packages: engines: {node: '>= 12.17.0'} hasBin: true + zx@8.3.0: + resolution: {integrity: sha512-L8mY3yfJwo3a8ZDD6f9jZzAcRWJZYcV8GauZmBxLB/aSTwaMzMIEVpPp2Kyx+7yF0gdvuxKnMxAZRft9UCawiw==} + engines: {node: '>= 12.17.0'} + hasBin: true + snapshots: '@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.17.2)': @@ -10390,7 +10410,7 @@ snapshots: '@docusaurus/react-loadable@6.0.0(react@18.3.1)': dependencies: - '@types/react': 17.0.82 + '@types/react': 18.3.8 react: 18.3.1 '@docusaurus/theme-classic@3.5.2(@types/react@18.3.8)(esbuild@0.14.54)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.2.2)(webpack-cli@4.10.0(webpack@5.94.0))': @@ -12345,26 +12365,26 @@ snapshots: '@types/range-parser@1.2.7': {} - '@types/react-dom@17.0.25': + '@types/react-dom@18.3.5(@types/react@18.3.8)': dependencies: - '@types/react': 17.0.82 + '@types/react': 18.3.8 '@types/react-router-config@5.0.11': dependencies: '@types/history': 4.7.11 - '@types/react': 17.0.82 + '@types/react': 18.3.8 '@types/react-router': 5.1.20 '@types/react-router-dom@5.3.3': dependencies: '@types/history': 4.7.11 - '@types/react': 17.0.82 + '@types/react': 18.3.8 '@types/react-router': 5.1.20 '@types/react-router@5.1.20': dependencies: '@types/history': 4.7.11 - '@types/react': 17.0.82 + '@types/react': 18.3.8 '@types/react@17.0.82': dependencies: @@ -17427,13 +17447,6 @@ snapshots: - supports-color - vue-template-compiler - react-dom@17.0.2(react@17.0.2): - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react: 17.0.2 - scheduler: 0.20.2 - react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 @@ -17505,11 +17518,6 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - react@17.0.2: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -17827,11 +17835,6 @@ snapshots: sax@1.4.1: {} - scheduler@0.20.2: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -19147,3 +19150,8 @@ snapshots: optionalDependencies: '@types/fs-extra': 11.0.4 '@types/node': 22.5.5 + + zx@8.3.0: + optionalDependencies: + '@types/fs-extra': 11.0.4 + '@types/node': 22.5.5 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 09201bb8d3..828c79654c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -10,6 +10,7 @@ packages: - "packages/perspective-viewer-openlayers" - "packages/perspective-workspace" - "packages/perspective-jupyterlab" + - "packages/perspective-react" - "packages/perspective-webpack-plugin" - "packages/perspective-cli" - "rust/generate-metadata" diff --git a/tools/perspective-scripts/setup.mjs b/tools/perspective-scripts/setup.mjs index 2308cb28ff..a16aec37da 100644 --- a/tools/perspective-scripts/setup.mjs +++ b/tools/perspective-scripts/setup.mjs @@ -160,6 +160,11 @@ async function focus_package() { name: "perspective-cli", value: "perspective-cli", }, + { + key: "a", + name: "perspective-react", + value: "perspective-react", + }, ]; const new_config = await inquirer.prompt([ {