@perspective-dev/react 4.5.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.d.ts +0 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +4 -4
- package/dist/esm/utils.d.ts +1 -1
- package/dist/esm/viewer.d.ts +1 -1
- package/package.json +2 -3
- package/src/index.tsx +0 -1
- package/src/utils.tsx +2 -1
- package/src/viewer.tsx +31 -11
- package/test/js/basic.story.tsx +0 -2
- package/test/js/index.css +1 -1
- package/test/js/react.spec.tsx +25 -28
- package/test/js/workspace.story.tsx +55 -43
- package/dist/esm/workspace.d.ts +0 -21
- package/src/workspace.tsx +0 -95
package/dist/esm/index.d.ts
CHANGED
package/dist/esm/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import*as i from"react";import*as a from"react";function c(e,t,r,n=s=>s.detail){a.useEffect(()=>{if(!r||!e)return;let s=new AbortController,l=o=>r(n(o));return e?.addEventListener(t,l,{signal:s.signal}),()=>s.abort()},[e,r])}function d(e){let[t,r]=i.useState(null);return i.useEffect(()=>()=>{t?.delete()},[t]),i.useEffect(()=>{if(!t)return;let n=!1;return(async()=>{if(!e.client){await t.eject();return}await t.load(e.client),!(n||!e.config)&&("panels"in e.config?await t.restoreWorkspace(e.config):await t.restore(e.config))})(),()=>{n=!0}},[t,e.client,JSON.stringify(e.config)]),c(t,"perspective-click",e.onClick),c(t,"perspective-select",e.onSelect),c(t,"perspective-config-update",e.onConfigUpdate,n=>n.detail.getConfig()),i.createElement("perspective-viewer",{ref:r,id:e.id,className:e.className,hidden:e.hidden,slot:e.slot,style:e.style,tabIndex:e.tabIndex,title:e.title})}var u=i.memo(d);export{u as PerspectiveViewer};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/viewer.tsx", "../../src/utils.tsx"
|
|
4
|
-
"sourcesContent": ["// \u250F\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n// \u2503 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588\u2584 \u2580\u2588\u2588\u2588 \u2588 \u2503\n// \u2503 \u2584\u2584\u2584\u2584\u2584\u2588 \u2588\u2584\u2584\u2584\u2584\u2584 \u2584\u2584\u2584\u2584\u2584\u2588 \u2580\u2580\u2580\u2580\u2580\u2588\u2580\u2580\u2580\u2580\u2580 \u2588 \u2580\u2580\u2580\u2580\u2580\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2584 \u2580\u2588 \u2588 \u2580\u2580\u2580\u2580\u2580 \u2503\n// \u2503 \u2588\u2580\u2580\u2580\u2580\u2580 \u2588\u2580\u2580\u2580\u2580\u2580 \u2588\u2580\u2588\u2588\u2580\u2580 \u2584\u2584\u2584\u2584\u2584 \u2588 \u2584\u2584\u2584\u2584\u2584\u2588 \u2584\u2584\u2584\u2584\u2584\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2584 \u2588 \u2584\u2584\u2584\u2584\u2584 \u2503\n// \u2503 \u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2580\u2588\u2584 \u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2584 \u2588 \u2503\n// \u2523\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u252B\n// \u2503 Copyright (c) 2017, the Perspective Authors. \u2503\n// \u2503 \u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C \u2503\n// \u2503 This file is part of the Perspective library, distributed under the terms \u2503\n// \u2503 of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). \u2503\n// \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251B\n\nimport * as React from \"react\";\nimport type * as psp from \"@perspective-dev/client\";\nimport type * as pspViewer from \"@perspective-dev/viewer\";\nimport { usePspListener } from \"./utils\";\n\nfunction PerspectiveViewerImpl(props: PerspectiveViewerProps) {\n const [viewer, setViewer] =\n React.useState<pspViewer.HTMLPerspectiveViewerElement | null>(null);\n\n React.useEffect(() => {\n return () => {\n viewer?.delete();\n };\n }, [viewer]);\n\n React.useEffect(() => {\n if (props.client) {\n viewer?.load(props.client);\n } else {\n viewer?.eject();\n }\n }, [viewer, props.client]);\n\n React.useEffect(() => {\n if (props.client && props.config) {\n viewer?.restore(props.config);\n }\n }, [viewer, props.client, JSON.stringify(props.config)]);\n\n usePspListener(viewer, \"perspective-click\", props.onClick);\n usePspListener(viewer, \"perspective-select\", props.onSelect);\n usePspListener(viewer, \"perspective-config-update\", props.onConfigUpdate);\n\n return (\n <perspective-viewer\n ref={setViewer}\n id={props.id}\n className={props.className}\n hidden={props.hidden}\n slot={props.slot}\n style={props.style}\n tabIndex={props.tabIndex}\n title={props.title}\n />\n );\n}\n\n/**\n * Props for the `<PerspectiveViewer>` component.\n */\nexport interface PerspectiveViewerProps {\n client?: psp.Client | Promise<psp.Client> | psp.Table | Promise<psp.Table>;\n config?: pspViewer.ViewerConfigUpdate;\n onConfigUpdate?: (config: pspViewer.ViewerConfigUpdate) => void;\n onClick?: (data: pspViewer.PerspectiveClickEventDetail) => void;\n onSelect?: (data: pspViewer.PerspectiveSelectEventDetail) => void;\n\n // Applicable props from `React.HTMLAttributes`, which we cannot extend\n // directly because Perspective changes the signature of `onClick`.\n className?: string | undefined;\n hidden?: boolean | undefined;\n id?: string | undefined;\n slot?: string | undefined;\n style?: React.CSSProperties | undefined;\n tabIndex?: number | undefined;\n title?: string | undefined;\n}\n\n/**\n * A React wrapper component for `<perspective-viewer>` Custom Element.\n */\nexport const PerspectiveViewer: React.FC<PerspectiveViewerProps> = React.memo(\n PerspectiveViewerImpl,\n);\n", "// \u250F\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n// \u2503 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588\u2584 \u2580\u2588\u2588\u2588 \u2588 \u2503\n// \u2503 \u2584\u2584\u2584\u2584\u2584\u2588 \u2588\u2584\u2584\u2584\u2584\u2584 \u2584\u2584\u2584\u2584\u2584\u2588 \u2580\u2580\u2580\u2580\u2580\u2588\u2580\u2580\u2580\u2580\u2580 \u2588 \u2580\u2580\u2580\u2580\u2580\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2584 \u2580\u2588 \u2588 \u2580\u2580\u2580\u2580\u2580 \u2503\n// \u2503 \u2588\u2580\u2580\u2580\u2580\u2580 \u2588\u2580\u2580\u2580\u2580\u2580 \u2588\u2580\u2588\u2588\u2580\u2580 \u2584\u2584\u2584\u2584\u2584 \u2588 \u2584\u2584\u2584\u2584\u2584\u2588 \u2584\u2584\u2584\u2584\u2584\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2584 \u2588 \u2584\u2584\u2584\u2584\u2584 \u2503\n// \u2503 \u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2580\u2588\u2584 \u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2584 \u2588 \u2503\n// \u2523\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u252B\n// \u2503 Copyright (c) 2017, the Perspective Authors. \u2503\n// \u2503 \u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C \u2503\n// \u2503 This file is part of the Perspective library, distributed under the terms \u2503\n// \u2503 of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). \u2503\n// \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251B\n\nimport * as React from \"react\";\n\nexport function usePspListener<A>(\n el: HTMLElement | undefined | null,\n event: string,\n cb?: (x: A) => void,\n) {\n React.useEffect(() => {\n if (!cb || !el) return;\n const ctx = new AbortController();\n const callback = (e: Event) => cb((e as CustomEvent).detail);\n el?.addEventListener(event, callback, { signal: ctx.signal });\n return () => ctx.abort();\n }, [el, cb]);\n}\n", "// \u250F\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n// \u2503 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588\u2584 \u2580\u2588\u2588\u2588 \u2588 \u2503\n// \u2503 \u2584\u2584\u2584\u2584\u2584\u2588 \u2588\u2584\u2584\u2584\u2584\u2584 \u2584\u2584\u2584\u2584\u2584\u2588 \u2580\u2580\u2580\u2580\u2580\u2588\u2580\u2580\u2580\u2580\u2580 \u2588 \u2580\u2580\u2580\u2580\u2580\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2584 \u2580\u2588 \u2588 \u2580\u2580\u2580\u2580\u2580 \u2503\n// \u2503 \u2588\u2580\u2580\u2580\u2580\u2580 \u2588\u2580\u2580\u2580\u2580\u2580 \u2588\u2580\u2588\u2588\u2580\u2580 \u2584\u2584\u2584\u2584\u2584 \u2588 \u2584\u2584\u2584\u2584\u2584\u2588 \u2584\u2584\u2584\u2584\u2584\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2584 \u2588 \u2584\u2584\u2584\u2584\u2584 \u2503\n// \u2503 \u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2580\u2588\u2584 \u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2584 \u2588 \u2503\n// \u2523\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u252B\n// \u2503 Copyright (c) 2017, the Perspective Authors. \u2503\n// \u2503 \u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C \u2503\n// \u2503 This file is part of the Perspective library, distributed under the terms \u2503\n// \u2503 of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). \u2503\n// \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251B\n\nimport type * as psp from \"@perspective-dev/client\";\nimport type * as psp_viewer from \"@perspective-dev/viewer\";\nimport type * as pspWorkspace from \"@perspective-dev/workspace\";\nimport { PerspectiveWorkspaceConfig } from \"@perspective-dev/workspace\";\nimport * as utils from \"./utils\";\nimport * as React from \"react\";\n\nexport interface NewViewEventDetail {\n config: psp_viewer.ViewerConfigUpdate;\n widget: pspWorkspace.PerspectiveViewerWidget;\n}\n\nexport interface ToggleGloalFilterEventDetail {\n widget: pspWorkspace.PerspectiveViewerWidget;\n isGlobalFilter: boolean;\n}\n\nexport interface PerspectiveWorkspaceProps\n extends React.HTMLAttributes<HTMLElement> {\n client: psp.Client | Promise<psp.Client>;\n layout: PerspectiveWorkspaceConfig;\n onLayoutUpdate?: (layout: PerspectiveWorkspaceConfig) => void;\n onNewView?: (detail: NewViewEventDetail) => void;\n onToggleGlobalFilter?: (detail: ToggleGloalFilterEventDetail) => void;\n}\n\nconst PerspectiveWorkspaceImpl = React.forwardRef<\n pspWorkspace.HTMLPerspectiveWorkspaceElement | undefined,\n PerspectiveWorkspaceProps\n>(\n (\n {\n client,\n layout,\n onLayoutUpdate,\n onNewView,\n onToggleGlobalFilter,\n ...htmlAttributes\n },\n ref,\n ) => {\n const [workspace, setWorkspace] =\n React.useState<pspWorkspace.HTMLPerspectiveWorkspaceElement>();\n\n React.useImperativeHandle(ref, () => workspace, [workspace]);\n React.useEffect(() => {\n if (workspace && layout) {\n workspace.restore(layout);\n }\n }, [workspace, layout]);\n\n React.useEffect(() => {\n if (workspace && client) {\n workspace.load(client);\n }\n }, [workspace, client]);\n\n utils.usePspListener(workspace, \"workspace-new-view\", onNewView);\n utils.usePspListener(\n workspace,\n \"workspace-layout-update\",\n onLayoutUpdate\n ? ({ layout }: { layout: PerspectiveWorkspaceConfig }) =>\n workspace?.save().then((x) => onLayoutUpdate(x))\n : undefined,\n );\n\n utils.usePspListener(\n workspace,\n \"workspace-toggle-global-filter\",\n onToggleGlobalFilter,\n );\n\n return (\n <perspective-workspace\n ref={(r) => setWorkspace(r ?? undefined)}\n {...htmlAttributes}\n ></perspective-workspace>\n );\n },\n);\n\nexport const PerspectiveWorkspace = React.memo(PerspectiveWorkspaceImpl);\n"],
|
|
5
|
-
"mappings": "AAYA,UAAYA,MAAW,QCAvB,UAAYC,MAAW,QAEhB,SAASC,EACZC,EACAC,EACAC,
|
|
6
|
-
"names": ["React", "React", "usePspListener", "el", "event", "cb", "
|
|
3
|
+
"sources": ["../../src/viewer.tsx", "../../src/utils.tsx"],
|
|
4
|
+
"sourcesContent": ["// \u250F\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n// \u2503 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588\u2584 \u2580\u2588\u2588\u2588 \u2588 \u2503\n// \u2503 \u2584\u2584\u2584\u2584\u2584\u2588 \u2588\u2584\u2584\u2584\u2584\u2584 \u2584\u2584\u2584\u2584\u2584\u2588 \u2580\u2580\u2580\u2580\u2580\u2588\u2580\u2580\u2580\u2580\u2580 \u2588 \u2580\u2580\u2580\u2580\u2580\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2584 \u2580\u2588 \u2588 \u2580\u2580\u2580\u2580\u2580 \u2503\n// \u2503 \u2588\u2580\u2580\u2580\u2580\u2580 \u2588\u2580\u2580\u2580\u2580\u2580 \u2588\u2580\u2588\u2588\u2580\u2580 \u2584\u2584\u2584\u2584\u2584 \u2588 \u2584\u2584\u2584\u2584\u2584\u2588 \u2584\u2584\u2584\u2584\u2584\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2584 \u2588 \u2584\u2584\u2584\u2584\u2584 \u2503\n// \u2503 \u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2580\u2588\u2584 \u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2584 \u2588 \u2503\n// \u2523\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u252B\n// \u2503 Copyright (c) 2017, the Perspective Authors. \u2503\n// \u2503 \u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C \u2503\n// \u2503 This file is part of the Perspective library, distributed under the terms \u2503\n// \u2503 of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). \u2503\n// \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251B\n\nimport * as React from \"react\";\nimport type * as psp from \"@perspective-dev/client\";\nimport type * as pspViewer from \"@perspective-dev/viewer\";\nimport { usePspListener } from \"./utils\";\n\nfunction PerspectiveViewerImpl(props: PerspectiveViewerProps) {\n const [viewer, setViewer] =\n React.useState<pspViewer.HTMLPerspectiveViewerElement | null>(null);\n\n React.useEffect(() => {\n return () => {\n viewer?.delete();\n };\n }, [viewer]);\n\n React.useEffect(() => {\n if (!viewer) {\n return;\n }\n\n let cancelled = false;\n (async () => {\n if (!props.client) {\n await viewer.eject();\n return;\n }\n\n await viewer.load(props.client);\n if (cancelled || !props.config) {\n return;\n }\n\n if (\"panels\" in props.config) {\n await viewer.restoreWorkspace(props.config);\n } else {\n await viewer.restore(props.config);\n }\n })();\n\n return () => {\n cancelled = true;\n };\n }, [viewer, props.client, JSON.stringify(props.config)]);\n\n usePspListener(viewer, \"perspective-click\", props.onClick);\n usePspListener(viewer, \"perspective-select\", props.onSelect);\n usePspListener(\n viewer,\n \"perspective-config-update\",\n props.onConfigUpdate,\n (e) => e.detail.getConfig(),\n );\n\n return (\n <perspective-viewer\n ref={setViewer}\n id={props.id}\n className={props.className}\n hidden={props.hidden}\n slot={props.slot}\n style={props.style}\n tabIndex={props.tabIndex}\n title={props.title}\n />\n );\n}\n\n/**\n * Props for the `<PerspectiveViewer>` component.\n */\nexport interface PerspectiveViewerProps {\n client?: psp.Client | Promise<psp.Client> | psp.Table | Promise<psp.Table>;\n config?: pspViewer.ViewerConfigUpdate | pspViewer.WorkspaceConfigUpdate;\n onConfigUpdate?: (config: pspViewer.ViewerConfigUpdate) => void;\n onClick?: (data: pspViewer.PerspectiveClickEventDetail) => void;\n onSelect?: (data: pspViewer.PerspectiveSelectEventDetail) => void;\n\n // Applicable props from `React.HTMLAttributes`, which we cannot extend\n // directly because Perspective changes the signature of `onClick`.\n className?: string | undefined;\n hidden?: boolean | undefined;\n id?: string | undefined;\n slot?: string | undefined;\n style?: React.CSSProperties | undefined;\n tabIndex?: number | undefined;\n title?: string | undefined;\n}\n\n/**\n * A React wrapper component for `<perspective-viewer>` Custom Element.\n */\nexport const PerspectiveViewer: React.FC<PerspectiveViewerProps> = React.memo(\n PerspectiveViewerImpl,\n);\n", "// \u250F\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2513\n// \u2503 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588\u2584 \u2580\u2588\u2588\u2588 \u2588 \u2503\n// \u2503 \u2584\u2584\u2584\u2584\u2584\u2588 \u2588\u2584\u2584\u2584\u2584\u2584 \u2584\u2584\u2584\u2584\u2584\u2588 \u2580\u2580\u2580\u2580\u2580\u2588\u2580\u2580\u2580\u2580\u2580 \u2588 \u2580\u2580\u2580\u2580\u2580\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2584 \u2580\u2588 \u2588 \u2580\u2580\u2580\u2580\u2580 \u2503\n// \u2503 \u2588\u2580\u2580\u2580\u2580\u2580 \u2588\u2580\u2580\u2580\u2580\u2580 \u2588\u2580\u2588\u2588\u2580\u2580 \u2584\u2584\u2584\u2584\u2584 \u2588 \u2584\u2584\u2584\u2584\u2584\u2588 \u2584\u2584\u2584\u2584\u2584\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2584 \u2588 \u2584\u2584\u2584\u2584\u2584 \u2503\n// \u2503 \u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2580\u2588\u2584 \u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588 \u2588\u2588\u2588\u258C\u2590\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2584 \u2588 \u2503\n// \u2523\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u252B\n// \u2503 Copyright (c) 2017, the Perspective Authors. \u2503\n// \u2503 \u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C\u254C \u2503\n// \u2503 This file is part of the Perspective library, distributed under the terms \u2503\n// \u2503 of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). \u2503\n// \u2517\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u251B\n\nimport * as React from \"react\";\n\nexport function usePspListener<A>(\n el: HTMLElement | undefined | null,\n event: string,\n cb?: (x: A) => void,\n map: (e: CustomEvent) => A = (e) => e.detail,\n) {\n React.useEffect(() => {\n if (!cb || !el) return;\n const ctx = new AbortController();\n const callback = (e: Event) => cb(map(e as CustomEvent));\n el?.addEventListener(event, callback, { signal: ctx.signal });\n return () => ctx.abort();\n }, [el, cb]);\n}\n"],
|
|
5
|
+
"mappings": "AAYA,UAAYA,MAAW,QCAvB,UAAYC,MAAW,QAEhB,SAASC,EACZC,EACAC,EACAC,EACAC,EAA8BC,GAAMA,EAAE,OACxC,CACQ,YAAU,IAAM,CAClB,GAAI,CAACF,GAAM,CAACF,EAAI,OAChB,IAAMK,EAAM,IAAI,gBACVC,EAAYF,GAAaF,EAAGC,EAAIC,CAAgB,CAAC,EACvD,OAAAJ,GAAI,iBAAiBC,EAAOK,EAAU,CAAE,OAAQD,EAAI,MAAO,CAAC,EACrD,IAAMA,EAAI,MAAM,CAC3B,EAAG,CAACL,EAAIE,CAAE,CAAC,CACf,CDVA,SAASK,EAAsBC,EAA+B,CAC1D,GAAM,CAACC,EAAQC,CAAS,EACd,WAAwD,IAAI,EAEtE,OAAM,YAAU,IACL,IAAM,CACTD,GAAQ,OAAO,CACnB,EACD,CAACA,CAAM,CAAC,EAEL,YAAU,IAAM,CAClB,GAAI,CAACA,EACD,OAGJ,IAAIE,EAAY,GAChB,OAAC,SAAY,CACT,GAAI,CAACH,EAAM,OAAQ,CACf,MAAMC,EAAO,MAAM,EACnB,MACJ,CAEA,MAAMA,EAAO,KAAKD,EAAM,MAAM,EAC1B,EAAAG,GAAa,CAACH,EAAM,UAIpB,WAAYA,EAAM,OAClB,MAAMC,EAAO,iBAAiBD,EAAM,MAAM,EAE1C,MAAMC,EAAO,QAAQD,EAAM,MAAM,EAEzC,GAAG,EAEI,IAAM,CACTG,EAAY,EAChB,CACJ,EAAG,CAACF,EAAQD,EAAM,OAAQ,KAAK,UAAUA,EAAM,MAAM,CAAC,CAAC,EAEvDI,EAAeH,EAAQ,oBAAqBD,EAAM,OAAO,EACzDI,EAAeH,EAAQ,qBAAsBD,EAAM,QAAQ,EAC3DI,EACIH,EACA,4BACAD,EAAM,eACLK,GAAMA,EAAE,OAAO,UAAU,CAC9B,EAGI,gBAAC,sBACG,IAAKH,EACL,GAAIF,EAAM,GACV,UAAWA,EAAM,UACjB,OAAQA,EAAM,OACd,KAAMA,EAAM,KACZ,MAAOA,EAAM,MACb,SAAUA,EAAM,SAChB,MAAOA,EAAM,MACjB,CAER,CA0BO,IAAMM,EAA4D,OACrEP,CACJ",
|
|
6
|
+
"names": ["React", "React", "usePspListener", "el", "event", "cb", "map", "e", "ctx", "callback", "PerspectiveViewerImpl", "props", "viewer", "setViewer", "cancelled", "usePspListener", "e", "PerspectiveViewer"]
|
|
7
7
|
}
|
package/dist/esm/utils.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function usePspListener<A>(el: HTMLElement | undefined | null, event: string, cb?: (x: A) => void): void;
|
|
1
|
+
export declare function usePspListener<A>(el: HTMLElement | undefined | null, event: string, cb?: (x: A) => void, map?: (e: CustomEvent) => A): void;
|
package/dist/esm/viewer.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type * as pspViewer from "@perspective-dev/viewer";
|
|
|
6
6
|
*/
|
|
7
7
|
export interface PerspectiveViewerProps {
|
|
8
8
|
client?: psp.Client | Promise<psp.Client> | psp.Table | Promise<psp.Table>;
|
|
9
|
-
config?: pspViewer.ViewerConfigUpdate;
|
|
9
|
+
config?: pspViewer.ViewerConfigUpdate | pspViewer.WorkspaceConfigUpdate;
|
|
10
10
|
onConfigUpdate?: (config: pspViewer.ViewerConfigUpdate) => void;
|
|
11
11
|
onClick?: (data: pspViewer.PerspectiveClickEventDetail) => void;
|
|
12
12
|
onSelect?: (data: pspViewer.PerspectiveSelectEventDetail) => void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@perspective-dev/react",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "React component wrappers for `<perspective-viewer>`
|
|
3
|
+
"version": "5.0.0",
|
|
4
|
+
"description": "React component wrappers for `<perspective-viewer>`",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"perspective",
|
|
7
7
|
"react",
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@perspective-dev/client": "",
|
|
33
33
|
"@perspective-dev/viewer": "",
|
|
34
|
-
"@perspective-dev/workspace": "",
|
|
35
34
|
"@types/react": ">17 <20",
|
|
36
35
|
"react": ">17 <20",
|
|
37
36
|
"react-dom": ">17 <20"
|
package/src/index.tsx
CHANGED
package/src/utils.tsx
CHANGED
|
@@ -16,11 +16,12 @@ export function usePspListener<A>(
|
|
|
16
16
|
el: HTMLElement | undefined | null,
|
|
17
17
|
event: string,
|
|
18
18
|
cb?: (x: A) => void,
|
|
19
|
+
map: (e: CustomEvent) => A = (e) => e.detail,
|
|
19
20
|
) {
|
|
20
21
|
React.useEffect(() => {
|
|
21
22
|
if (!cb || !el) return;
|
|
22
23
|
const ctx = new AbortController();
|
|
23
|
-
const callback = (e: Event) => cb((e as CustomEvent)
|
|
24
|
+
const callback = (e: Event) => cb(map(e as CustomEvent));
|
|
24
25
|
el?.addEventListener(event, callback, { signal: ctx.signal });
|
|
25
26
|
return () => ctx.abort();
|
|
26
27
|
}, [el, cb]);
|
package/src/viewer.tsx
CHANGED
|
@@ -26,22 +26,42 @@ function PerspectiveViewerImpl(props: PerspectiveViewerProps) {
|
|
|
26
26
|
}, [viewer]);
|
|
27
27
|
|
|
28
28
|
React.useEffect(() => {
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
} else {
|
|
32
|
-
viewer?.eject();
|
|
29
|
+
if (!viewer) {
|
|
30
|
+
return;
|
|
33
31
|
}
|
|
34
|
-
}, [viewer, props.client]);
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
let cancelled = false;
|
|
34
|
+
(async () => {
|
|
35
|
+
if (!props.client) {
|
|
36
|
+
await viewer.eject();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
await viewer.load(props.client);
|
|
41
|
+
if (cancelled || !props.config) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if ("panels" in props.config) {
|
|
46
|
+
await viewer.restoreWorkspace(props.config);
|
|
47
|
+
} else {
|
|
48
|
+
await viewer.restore(props.config);
|
|
49
|
+
}
|
|
50
|
+
})();
|
|
51
|
+
|
|
52
|
+
return () => {
|
|
53
|
+
cancelled = true;
|
|
54
|
+
};
|
|
40
55
|
}, [viewer, props.client, JSON.stringify(props.config)]);
|
|
41
56
|
|
|
42
57
|
usePspListener(viewer, "perspective-click", props.onClick);
|
|
43
58
|
usePspListener(viewer, "perspective-select", props.onSelect);
|
|
44
|
-
usePspListener(
|
|
59
|
+
usePspListener(
|
|
60
|
+
viewer,
|
|
61
|
+
"perspective-config-update",
|
|
62
|
+
props.onConfigUpdate,
|
|
63
|
+
(e) => e.detail.getConfig(),
|
|
64
|
+
);
|
|
45
65
|
|
|
46
66
|
return (
|
|
47
67
|
<perspective-viewer
|
|
@@ -62,7 +82,7 @@ function PerspectiveViewerImpl(props: PerspectiveViewerProps) {
|
|
|
62
82
|
*/
|
|
63
83
|
export interface PerspectiveViewerProps {
|
|
64
84
|
client?: psp.Client | Promise<psp.Client> | psp.Table | Promise<psp.Table>;
|
|
65
|
-
config?: pspViewer.ViewerConfigUpdate;
|
|
85
|
+
config?: pspViewer.ViewerConfigUpdate | pspViewer.WorkspaceConfigUpdate;
|
|
66
86
|
onConfigUpdate?: (config: pspViewer.ViewerConfigUpdate) => void;
|
|
67
87
|
onClick?: (data: pspViewer.PerspectiveClickEventDetail) => void;
|
|
68
88
|
onSelect?: (data: pspViewer.PerspectiveSelectEventDetail) => void;
|
package/test/js/basic.story.tsx
CHANGED
|
@@ -14,7 +14,6 @@ import perspective from "@perspective-dev/client";
|
|
|
14
14
|
import perspective_viewer from "@perspective-dev/viewer";
|
|
15
15
|
import "@perspective-dev/viewer-datagrid";
|
|
16
16
|
import "@perspective-dev/viewer-charts";
|
|
17
|
-
import "@perspective-dev/workspace";
|
|
18
17
|
|
|
19
18
|
// @ts-ignore
|
|
20
19
|
import SERVER_WASM from "@perspective-dev/server/dist/wasm/perspective-server.wasm?url";
|
|
@@ -36,7 +35,6 @@ import SUPERSTORE_ARROW from "superstore-arrow/superstore.lz4.arrow?url";
|
|
|
36
35
|
import * as React from "react";
|
|
37
36
|
import {
|
|
38
37
|
PerspectiveViewer,
|
|
39
|
-
PerspectiveWorkspace,
|
|
40
38
|
} from "@perspective-dev/react";
|
|
41
39
|
|
|
42
40
|
import "@perspective-dev/viewer/dist/css/themes.css";
|
package/test/js/index.css
CHANGED
package/test/js/react.spec.tsx
CHANGED
|
@@ -30,19 +30,19 @@ test.describe("Perspective React", () => {
|
|
|
30
30
|
const comp = await mount(<EmptyWorkspace />);
|
|
31
31
|
const toggleMount = comp.locator("button.toggle-mount");
|
|
32
32
|
const addViewer = comp.locator("button.add-viewer");
|
|
33
|
-
const workspace = comp.locator("perspective-
|
|
34
|
-
const viewer = comp.locator("perspective-viewer");
|
|
33
|
+
const workspace = comp.locator("perspective-viewer");
|
|
35
34
|
await toggleMount.waitFor();
|
|
36
35
|
await addViewer.click();
|
|
37
36
|
await addViewer.click();
|
|
38
37
|
await addViewer.click();
|
|
39
38
|
await page.waitForFunction(
|
|
40
|
-
() =>
|
|
41
|
-
|
|
42
|
-
.
|
|
39
|
+
(expected) =>
|
|
40
|
+
(
|
|
41
|
+
document.querySelector("perspective-viewer") as any
|
|
42
|
+
)?.getPanelNames?.().length === expected,
|
|
43
|
+
3,
|
|
43
44
|
);
|
|
44
45
|
|
|
45
|
-
await expect(viewer).toHaveCount(3);
|
|
46
46
|
await toggleMount.click();
|
|
47
47
|
await workspace.waitFor({ state: "detached" });
|
|
48
48
|
|
|
@@ -51,43 +51,40 @@ test.describe("Perspective React", () => {
|
|
|
51
51
|
await toggleMount.click();
|
|
52
52
|
await workspace.waitFor();
|
|
53
53
|
await page.waitForFunction(
|
|
54
|
-
() =>
|
|
55
|
-
|
|
56
|
-
.
|
|
54
|
+
(expected) =>
|
|
55
|
+
(
|
|
56
|
+
document.querySelector("perspective-viewer") as any
|
|
57
|
+
)?.getPanelNames?.().length === expected,
|
|
58
|
+
3,
|
|
57
59
|
);
|
|
58
|
-
await expect(viewer).toHaveCount(3);
|
|
59
60
|
});
|
|
60
61
|
|
|
61
|
-
test("Adding a
|
|
62
|
-
page,
|
|
63
|
-
mount,
|
|
64
|
-
}) => {
|
|
62
|
+
test("Adding a panel to a lone-panel viewer", async ({ page, mount }) => {
|
|
65
63
|
const name = "abcdef";
|
|
66
64
|
const comp = await mount(<SingleView name={name} />);
|
|
67
65
|
const addViewer = comp.locator("button.add-viewer");
|
|
68
|
-
const
|
|
69
|
-
|
|
66
|
+
const settingsBtn = comp.locator(
|
|
67
|
+
`perspective-viewer perspective-viewer-tab .psp-tab-settings`,
|
|
68
|
+
);
|
|
70
69
|
|
|
71
70
|
await settingsBtn.waitFor();
|
|
72
71
|
await addViewer.waitFor();
|
|
73
72
|
await addViewer.click();
|
|
74
73
|
await page.waitForFunction(
|
|
75
|
-
() =>
|
|
76
|
-
|
|
77
|
-
.
|
|
74
|
+
(expected) =>
|
|
75
|
+
(
|
|
76
|
+
document.querySelector("perspective-viewer") as any
|
|
77
|
+
)?.getPanelNames?.().length === expected,
|
|
78
|
+
2,
|
|
78
79
|
);
|
|
79
80
|
|
|
80
|
-
expect(await viewer.count()).toBe(2);
|
|
81
|
-
await settingsBtn.first().click();
|
|
82
|
-
const settingsPanel = viewer.locator("#settings_panel");
|
|
83
|
-
await settingsPanel.waitFor();
|
|
84
81
|
await addViewer.click();
|
|
85
82
|
await page.waitForFunction(
|
|
86
|
-
() =>
|
|
87
|
-
|
|
88
|
-
.
|
|
83
|
+
(expected) =>
|
|
84
|
+
(
|
|
85
|
+
document.querySelector("perspective-viewer") as any
|
|
86
|
+
)?.getPanelNames?.().length === expected,
|
|
87
|
+
3,
|
|
89
88
|
);
|
|
90
|
-
expect(await viewer.count()).toBe(3);
|
|
91
|
-
await settingsPanel.waitFor({ state: "detached" });
|
|
92
89
|
});
|
|
93
90
|
});
|
|
@@ -9,27 +9,19 @@
|
|
|
9
9
|
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
-
import "@perspective-dev/workspace";
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
HTMLPerspectiveWorkspaceElement,
|
|
16
|
-
PerspectiveWorkspaceConfig,
|
|
17
|
-
} from "@perspective-dev/workspace";
|
|
18
12
|
|
|
19
13
|
import * as React from "react";
|
|
20
14
|
|
|
21
|
-
import {
|
|
15
|
+
import { PerspectiveViewer } from "@perspective-dev/react";
|
|
22
16
|
|
|
23
17
|
import perspective_viewer from "@perspective-dev/viewer";
|
|
18
|
+
import type { ViewerConfigUpdate } from "@perspective-dev/viewer";
|
|
24
19
|
import "@perspective-dev/viewer-datagrid";
|
|
25
20
|
import "@perspective-dev/viewer-charts";
|
|
26
|
-
import "@perspective-dev/workspace";
|
|
27
|
-
import * as Workspace from "@perspective-dev/workspace";
|
|
28
21
|
|
|
29
22
|
import * as perspective from "@perspective-dev/client";
|
|
30
23
|
|
|
31
24
|
import "@perspective-dev/viewer/dist/css/themes.css";
|
|
32
|
-
import "@perspective-dev/workspace/dist/css/pro.css";
|
|
33
25
|
import "./index.css";
|
|
34
26
|
|
|
35
27
|
// @ts-ignore
|
|
@@ -45,19 +37,51 @@ await Promise.all([
|
|
|
45
37
|
|
|
46
38
|
const CLIENT = await perspective.worker();
|
|
47
39
|
|
|
40
|
+
/// The merged `<perspective-viewer>` whole-element config: a `regular-layout`
|
|
41
|
+
/// tree + a per-panel `ViewerConfig` map. (Formerly
|
|
42
|
+
/// `@perspective-dev/workspace`'s `PerspectiveWorkspaceConfig`.)
|
|
43
|
+
interface MultiPanelConfig {
|
|
44
|
+
layout?: Record<string, unknown>;
|
|
45
|
+
panels: Record<string, Record<string, unknown>>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Append a panel, re-deriving an even horizontal split of one stack per
|
|
49
|
+
/// panel. (Formerly `Workspace.addViewer`.)
|
|
50
|
+
function addPanel(
|
|
51
|
+
config: MultiPanelConfig,
|
|
52
|
+
panel: Record<string, unknown>,
|
|
53
|
+
id: string,
|
|
54
|
+
): MultiPanelConfig {
|
|
55
|
+
const panels = { ...config.panels, [id]: panel };
|
|
56
|
+
const ids = Object.keys(panels);
|
|
57
|
+
return {
|
|
58
|
+
layout: {
|
|
59
|
+
type: "split-layout",
|
|
60
|
+
orientation: "horizontal",
|
|
61
|
+
sizes: ids.map(() => 1 / ids.length),
|
|
62
|
+
children: ids.map((k) => ({
|
|
63
|
+
type: "tab-layout",
|
|
64
|
+
tabs: [k],
|
|
65
|
+
selected: 0,
|
|
66
|
+
})),
|
|
67
|
+
},
|
|
68
|
+
panels,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
48
72
|
interface WorkspaceState {
|
|
49
|
-
|
|
73
|
+
config: MultiPanelConfig;
|
|
50
74
|
mounted: boolean;
|
|
51
75
|
}
|
|
52
76
|
|
|
53
77
|
interface WorkspaceAppProps {
|
|
54
|
-
|
|
78
|
+
config: MultiPanelConfig;
|
|
55
79
|
onSpecial?: () => void;
|
|
56
80
|
}
|
|
57
81
|
|
|
58
82
|
const WorkspaceApp: React.FC<WorkspaceAppProps> = (props) => {
|
|
59
83
|
const [state, setState] = React.useState<WorkspaceState>({
|
|
60
|
-
|
|
84
|
+
config: props.config,
|
|
61
85
|
mounted: true,
|
|
62
86
|
});
|
|
63
87
|
|
|
@@ -65,35 +89,30 @@ const WorkspaceApp: React.FC<WorkspaceAppProps> = (props) => {
|
|
|
65
89
|
const name = window.crypto.randomUUID();
|
|
66
90
|
const data = `a,b,c\n${Math.random()},${Math.random()},${Math.random()}`;
|
|
67
91
|
CLIENT.table(data, { name });
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
state.layout,
|
|
92
|
+
const config = addPanel(
|
|
93
|
+
state.config,
|
|
71
94
|
{
|
|
72
95
|
table: name,
|
|
73
96
|
title: name,
|
|
74
97
|
},
|
|
75
|
-
|
|
98
|
+
name,
|
|
76
99
|
);
|
|
77
100
|
|
|
78
101
|
setState({
|
|
79
102
|
...state,
|
|
80
|
-
|
|
103
|
+
config,
|
|
81
104
|
});
|
|
82
105
|
};
|
|
83
106
|
|
|
84
107
|
const onClickToggleMount = () =>
|
|
85
108
|
setState((old) => ({ ...old, mounted: !state.mounted }));
|
|
86
109
|
|
|
87
|
-
const onLayoutUpdate = (layout: PerspectiveWorkspaceConfig) => {
|
|
88
|
-
setState({ ...state, layout });
|
|
89
|
-
};
|
|
90
|
-
|
|
91
110
|
React.useEffect(() => {
|
|
92
111
|
setState((s) => ({
|
|
93
112
|
...s,
|
|
94
|
-
|
|
113
|
+
config: props.config,
|
|
95
114
|
}));
|
|
96
|
-
}, [props.
|
|
115
|
+
}, [props.config]);
|
|
97
116
|
|
|
98
117
|
return (
|
|
99
118
|
<div className="workspace-container">
|
|
@@ -111,37 +130,30 @@ const WorkspaceApp: React.FC<WorkspaceAppProps> = (props) => {
|
|
|
111
130
|
)}
|
|
112
131
|
</div>
|
|
113
132
|
{state.mounted && (
|
|
114
|
-
<
|
|
133
|
+
<PerspectiveViewer
|
|
115
134
|
client={CLIENT}
|
|
116
|
-
|
|
117
|
-
onLayoutUpdate={onLayoutUpdate}
|
|
135
|
+
config={state.config as unknown as ViewerConfigUpdate}
|
|
118
136
|
/>
|
|
119
137
|
)}
|
|
120
138
|
</div>
|
|
121
139
|
);
|
|
122
140
|
};
|
|
123
141
|
|
|
124
|
-
/// Renders the app with a default empty workspace
|
|
142
|
+
/// Renders the app with a default empty workspace (a multi-panel viewer always
|
|
143
|
+
/// has at least one — empty — seed panel).
|
|
125
144
|
export const EmptyWorkspace: React.FC = () => {
|
|
126
|
-
return
|
|
127
|
-
<WorkspaceApp
|
|
128
|
-
layout={{ sizes: [1], viewers: {}, detail: { main: null } }}
|
|
129
|
-
/>
|
|
130
|
-
);
|
|
145
|
+
return <WorkspaceApp config={{ panels: {} }} />;
|
|
131
146
|
};
|
|
132
147
|
|
|
133
148
|
export const SingleView: React.FC<{ name: string }> = ({ name }) => {
|
|
134
149
|
const _table = CLIENT.table("a,b,c\n1,2,3", { name });
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
currentIndex: 0,
|
|
141
|
-
widgets: [name],
|
|
142
|
-
},
|
|
150
|
+
const config: MultiPanelConfig = {
|
|
151
|
+
layout: {
|
|
152
|
+
type: "tab-layout",
|
|
153
|
+
tabs: [name],
|
|
154
|
+
selected: 0,
|
|
143
155
|
},
|
|
144
|
-
|
|
156
|
+
panels: {
|
|
145
157
|
[name]: {
|
|
146
158
|
table: name,
|
|
147
159
|
columns: ["a", "b", "c"],
|
|
@@ -150,5 +162,5 @@ export const SingleView: React.FC<{ name: string }> = ({ name }) => {
|
|
|
150
162
|
},
|
|
151
163
|
};
|
|
152
164
|
|
|
153
|
-
return <WorkspaceApp
|
|
165
|
+
return <WorkspaceApp config={config} />;
|
|
154
166
|
};
|
package/dist/esm/workspace.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type * as psp from "@perspective-dev/client";
|
|
2
|
-
import type * as psp_viewer from "@perspective-dev/viewer";
|
|
3
|
-
import type * as pspWorkspace from "@perspective-dev/workspace";
|
|
4
|
-
import { PerspectiveWorkspaceConfig } from "@perspective-dev/workspace";
|
|
5
|
-
import * as React from "react";
|
|
6
|
-
export interface NewViewEventDetail {
|
|
7
|
-
config: psp_viewer.ViewerConfigUpdate;
|
|
8
|
-
widget: pspWorkspace.PerspectiveViewerWidget;
|
|
9
|
-
}
|
|
10
|
-
export interface ToggleGloalFilterEventDetail {
|
|
11
|
-
widget: pspWorkspace.PerspectiveViewerWidget;
|
|
12
|
-
isGlobalFilter: boolean;
|
|
13
|
-
}
|
|
14
|
-
export interface PerspectiveWorkspaceProps extends React.HTMLAttributes<HTMLElement> {
|
|
15
|
-
client: psp.Client | Promise<psp.Client>;
|
|
16
|
-
layout: PerspectiveWorkspaceConfig;
|
|
17
|
-
onLayoutUpdate?: (layout: PerspectiveWorkspaceConfig) => void;
|
|
18
|
-
onNewView?: (detail: NewViewEventDetail) => void;
|
|
19
|
-
onToggleGlobalFilter?: (detail: ToggleGloalFilterEventDetail) => void;
|
|
20
|
-
}
|
|
21
|
-
export declare const PerspectiveWorkspace: React.NamedExoticComponent<PerspectiveWorkspaceProps & React.RefAttributes<pspWorkspace.HTMLPerspectiveWorkspaceElement | undefined>>;
|
package/src/workspace.tsx
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
-
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
-
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
-
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
-
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
-
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
-
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
-
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
-
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
-
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
-
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
-
|
|
13
|
-
import type * as psp from "@perspective-dev/client";
|
|
14
|
-
import type * as psp_viewer from "@perspective-dev/viewer";
|
|
15
|
-
import type * as pspWorkspace from "@perspective-dev/workspace";
|
|
16
|
-
import { PerspectiveWorkspaceConfig } from "@perspective-dev/workspace";
|
|
17
|
-
import * as utils from "./utils";
|
|
18
|
-
import * as React from "react";
|
|
19
|
-
|
|
20
|
-
export interface NewViewEventDetail {
|
|
21
|
-
config: psp_viewer.ViewerConfigUpdate;
|
|
22
|
-
widget: pspWorkspace.PerspectiveViewerWidget;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface ToggleGloalFilterEventDetail {
|
|
26
|
-
widget: pspWorkspace.PerspectiveViewerWidget;
|
|
27
|
-
isGlobalFilter: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface PerspectiveWorkspaceProps
|
|
31
|
-
extends React.HTMLAttributes<HTMLElement> {
|
|
32
|
-
client: psp.Client | Promise<psp.Client>;
|
|
33
|
-
layout: PerspectiveWorkspaceConfig;
|
|
34
|
-
onLayoutUpdate?: (layout: PerspectiveWorkspaceConfig) => void;
|
|
35
|
-
onNewView?: (detail: NewViewEventDetail) => void;
|
|
36
|
-
onToggleGlobalFilter?: (detail: ToggleGloalFilterEventDetail) => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const PerspectiveWorkspaceImpl = React.forwardRef<
|
|
40
|
-
pspWorkspace.HTMLPerspectiveWorkspaceElement | undefined,
|
|
41
|
-
PerspectiveWorkspaceProps
|
|
42
|
-
>(
|
|
43
|
-
(
|
|
44
|
-
{
|
|
45
|
-
client,
|
|
46
|
-
layout,
|
|
47
|
-
onLayoutUpdate,
|
|
48
|
-
onNewView,
|
|
49
|
-
onToggleGlobalFilter,
|
|
50
|
-
...htmlAttributes
|
|
51
|
-
},
|
|
52
|
-
ref,
|
|
53
|
-
) => {
|
|
54
|
-
const [workspace, setWorkspace] =
|
|
55
|
-
React.useState<pspWorkspace.HTMLPerspectiveWorkspaceElement>();
|
|
56
|
-
|
|
57
|
-
React.useImperativeHandle(ref, () => workspace, [workspace]);
|
|
58
|
-
React.useEffect(() => {
|
|
59
|
-
if (workspace && layout) {
|
|
60
|
-
workspace.restore(layout);
|
|
61
|
-
}
|
|
62
|
-
}, [workspace, layout]);
|
|
63
|
-
|
|
64
|
-
React.useEffect(() => {
|
|
65
|
-
if (workspace && client) {
|
|
66
|
-
workspace.load(client);
|
|
67
|
-
}
|
|
68
|
-
}, [workspace, client]);
|
|
69
|
-
|
|
70
|
-
utils.usePspListener(workspace, "workspace-new-view", onNewView);
|
|
71
|
-
utils.usePspListener(
|
|
72
|
-
workspace,
|
|
73
|
-
"workspace-layout-update",
|
|
74
|
-
onLayoutUpdate
|
|
75
|
-
? ({ layout }: { layout: PerspectiveWorkspaceConfig }) =>
|
|
76
|
-
workspace?.save().then((x) => onLayoutUpdate(x))
|
|
77
|
-
: undefined,
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
utils.usePspListener(
|
|
81
|
-
workspace,
|
|
82
|
-
"workspace-toggle-global-filter",
|
|
83
|
-
onToggleGlobalFilter,
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
return (
|
|
87
|
-
<perspective-workspace
|
|
88
|
-
ref={(r) => setWorkspace(r ?? undefined)}
|
|
89
|
-
{...htmlAttributes}
|
|
90
|
-
></perspective-workspace>
|
|
91
|
-
);
|
|
92
|
-
},
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
export const PerspectiveWorkspace = React.memo(PerspectiveWorkspaceImpl);
|