chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 { WasmPlugin } from "@perspective-dev/esbuild-plugin/wasm.js";
|
||||
import { WorkerPlugin } from "@perspective-dev/esbuild-plugin/worker.js";
|
||||
import { ResolvePlugin } from "@perspective-dev/esbuild-plugin/resolve.js";
|
||||
import { build } from "@perspective-dev/esbuild-plugin/build.js";
|
||||
import {
|
||||
bundle as bundleCss,
|
||||
bundleAsync as bundleCssAsync,
|
||||
} from "lightningcss";
|
||||
import * as fs from "node:fs";
|
||||
// import { createRequire } from "node:module";
|
||||
import {
|
||||
inlineUrlVisitor,
|
||||
resolveNPM,
|
||||
} from "@perspective-dev/viewer/tools.mjs";
|
||||
|
||||
import "zx/globals";
|
||||
|
||||
// const _require = createRequire(import.meta.url);
|
||||
|
||||
const BUILD = [
|
||||
{
|
||||
entryPoints: ["src/ts/perspective-workspace.ts"],
|
||||
define: {
|
||||
global: "window",
|
||||
},
|
||||
format: "esm",
|
||||
plugins: [
|
||||
// Inlining `lumino` and importing the `.ts` source saves _50kb_
|
||||
NodeModulesExternal("@lumino"),
|
||||
],
|
||||
loader: {
|
||||
".html": "text",
|
||||
".css": "text",
|
||||
},
|
||||
external: ["*.wasm"],
|
||||
outfile: "dist/esm/perspective-workspace.js",
|
||||
},
|
||||
{
|
||||
entryPoints: ["src/ts/perspective-workspace.ts"],
|
||||
define: {
|
||||
global: "window",
|
||||
},
|
||||
plugins: [
|
||||
ResolvePlugin({
|
||||
"@perspective-dev/client":
|
||||
"@perspective-dev/client/dist/esm/perspective.js",
|
||||
"@perspective-dev/viewer":
|
||||
"@perspective-dev/viewer/dist/esm/perspective-viewer.js",
|
||||
}),
|
||||
WasmPlugin(false),
|
||||
WorkerPlugin({ inline: false }),
|
||||
],
|
||||
format: "esm",
|
||||
splitting: true,
|
||||
loader: {
|
||||
".css": "text",
|
||||
".html": "text",
|
||||
},
|
||||
outdir: "dist/cdn",
|
||||
},
|
||||
];
|
||||
|
||||
async function build_all() {
|
||||
fs.mkdirSync("dist/css", { recursive: true });
|
||||
const { code: wsCode } = await bundleCssAsync({
|
||||
filename: "./src/css/workspace.css",
|
||||
resolver: resolveNPM(import.meta.url),
|
||||
minify: true,
|
||||
errorRecovery: true,
|
||||
visitor: inlineUrlVisitor("./src/css/workspace.css"),
|
||||
});
|
||||
|
||||
fs.writeFileSync("dist/css/workspace.css", wsCode);
|
||||
const { code: injCode } = await bundleCssAsync({
|
||||
filename: "./src/css/injected.css",
|
||||
resolver: resolveNPM(import.meta.url),
|
||||
minify: true,
|
||||
errorRecovery: true,
|
||||
visitor: inlineUrlVisitor("./src/css/workspace.css"),
|
||||
});
|
||||
|
||||
// Workspace themes — bundle with lightningcss (resolves @imports)
|
||||
fs.writeFileSync("dist/css/injected.css", injCode);
|
||||
const { code: proCode } = bundleCss({
|
||||
filename: "./src/themes/pro.css",
|
||||
minify: true,
|
||||
visitor: inlineUrlVisitor("./src/themes/pro.css"),
|
||||
});
|
||||
|
||||
fs.writeFileSync("dist/css/pro.css", proCode);
|
||||
const { code: proDarkCode } = bundleCss({
|
||||
filename: "./src/themes/pro-dark.css",
|
||||
minify: true,
|
||||
visitor: inlineUrlVisitor("./src/themes/pro-dark.css"),
|
||||
});
|
||||
|
||||
fs.writeFileSync("dist/css/pro-dark.css", proDarkCode);
|
||||
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.stdout);
|
||||
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,56 @@
|
||||
{
|
||||
"name": "@perspective-dev/workspace",
|
||||
"version": "4.5.2",
|
||||
"description": "Perspective Workspace",
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"src/**/*",
|
||||
"index.d.ts"
|
||||
],
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./dist/esm/perspective-workspace.js",
|
||||
"./esm/": "./dist/esm/",
|
||||
"./src/*": "./src/*",
|
||||
"./dist/*": "./dist/*",
|
||||
"./dist/themes/": "./src/themes/",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"unpkg": "./dist/cdn/perspective-workspace.js",
|
||||
"jsdelivr": "./dist/cdn/perspective-workspace.js",
|
||||
"scripts": {
|
||||
"build": "node ./build.mjs",
|
||||
"clean": "node ./clean.mjs"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/perspective-dev/perspective"
|
||||
},
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@perspective-dev/client": "workspace:",
|
||||
"@lumino/algorithm": ">=2 <3",
|
||||
"@lumino/commands": ">=2 <3",
|
||||
"@lumino/domutils": ">=2 <3",
|
||||
"@lumino/messaging": ">=2 <3",
|
||||
"@lumino/virtualdom": ">=2 <3",
|
||||
"@lumino/widgets": ">=2 <3",
|
||||
"@lumino/coreutils": ">=2 <3",
|
||||
"@lumino/signaling": ">=2 <3",
|
||||
"lodash": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@perspective-dev/viewer": "workspace:",
|
||||
"lightningcss": "catalog:",
|
||||
"@perspective-dev/esbuild-plugin": "workspace:",
|
||||
"@perspective-dev/test": "workspace:",
|
||||
"@types/lodash": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"zx": "catalog:"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
* ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
* ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
* ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
* ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
* ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
* ┃ 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). ┃
|
||||
* ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
*/
|
||||
|
||||
:host {
|
||||
.lm-DockPanel {
|
||||
overflow: var(--dock-panel--overflow, hidden);
|
||||
position: absolute;
|
||||
background-color: var(--detail--background-color, transparent);
|
||||
padding: var(--psp-workspace--spacing);
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
&[data-mode="single-document"] {
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.lm-Widget,
|
||||
.lm-Widget {
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.lm-DockPanel.ew,
|
||||
.lm-DockPanel.ew .lm-Widget,
|
||||
.lm-SplitPanel.ew,
|
||||
.lm-SplitPanel.ew .lm-Widget {
|
||||
cursor: ew-resize !important;
|
||||
}
|
||||
|
||||
.lm-DockPanel.ns,
|
||||
.lm-DockPanel.ns .lm-Widget,
|
||||
.lm-SplitPanel.ns,
|
||||
.lm-SplitPanel.ns .lm-Widget {
|
||||
cursor: ns-resize !important;
|
||||
}
|
||||
|
||||
.workspace-master-widget {
|
||||
min-width: 100px !important;
|
||||
}
|
||||
|
||||
.lm-DockPanel.resizing ::slotted(perspective-viewer),
|
||||
.lm-SplitPanel.resizing ::slotted(perspective-viewer) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.widget-blur ::slotted(perspective-viewer) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.lm-DockPanel-handle.resizing {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.perspective-scroll-panel {
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
||||
.lm-Panel {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.lm-DockPanel-handle {
|
||||
background-color: none;
|
||||
}
|
||||
|
||||
.lm-SplitPanel-handle {
|
||||
background-color: var(
|
||||
--psp-workspace--split-panel-handle--background-color
|
||||
);
|
||||
}
|
||||
|
||||
.lm-SplitPanel-handle,
|
||||
.lm-DockPanel-handle {
|
||||
transition: background-color 0.3s ease-out;
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
.lm-DockPanel-handle[data-orientation="horizontal"] {
|
||||
width: 10px !important;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
.lm-DockPanel-handle[data-orientation="vertical"] {
|
||||
height: 10px !important;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.lm-DockPanel-overlay {
|
||||
background: rgba(75, 75, 75, 0.2);
|
||||
border: 1px dashed #666;
|
||||
border-radius: 6px;
|
||||
transition-property: top, left, right, bottom;
|
||||
transition-duration: 50ms;
|
||||
transition-timing-function: linear;
|
||||
margin: 0px;
|
||||
}
|
||||
}
|
||||
@@ -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 "./viewer.css";
|
||||
@import "./menu.css";
|
||||
@@ -0,0 +1,130 @@
|
||||
/* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
* ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
* ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
* ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
* ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
* ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
* ┃ 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 "@lumino/widgets/style/menu.css";
|
||||
|
||||
:host {
|
||||
.lm-Menu {
|
||||
font-size: 12px;
|
||||
padding: 8px;
|
||||
background-color: var(--psp--background-color);
|
||||
color: var(--psp--color);
|
||||
border: 1px solid var(--psp-inactive--color);
|
||||
border-radius: 3px;
|
||||
max-width: 350px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.lm-Menu.perspective-workspace-menu {
|
||||
.lm-Menu-item {
|
||||
padding-left: 37px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.lm-Menu-item.lm-mod-active {
|
||||
background-color: var(--psp--color);
|
||||
color: var(--psp--background-color);
|
||||
}
|
||||
|
||||
.lm-Menu-item.lm-mod-disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.lm-Menu-itemIcon {
|
||||
width: 24px;
|
||||
height: 12px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.lm-Menu-itemLabel {
|
||||
flex: 1 1;
|
||||
padding: 4px 2px 4px 2px;
|
||||
}
|
||||
|
||||
.lm-Menu-itemMnemonic {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.lm-Menu-itemShortcut {
|
||||
padding: 4px 0px;
|
||||
}
|
||||
|
||||
.lm-Menu-itemSubmenuIcon {
|
||||
width: 24px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.lm-Menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
outline: none;
|
||||
|
||||
margin: 0 -8px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.lm-Menu-item[data-type="separator"] > div {
|
||||
padding: 0;
|
||||
height: 9px;
|
||||
}
|
||||
|
||||
.lm-Menu-item[data-type="separator"] > div::after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
border-top: 1px solid;
|
||||
}
|
||||
|
||||
.lm-MenuBar-menu {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.lm-MenuBar-item {
|
||||
padding: 10px 32px;
|
||||
border-left: 1px solid transparent;
|
||||
border-right: 1px solid transparent;
|
||||
color: #737373 !important;
|
||||
}
|
||||
|
||||
.lm-MenuBar-item.lm-mod-active {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.lm-MenuBar.lm-mod-active .lm-MenuBar-item.lm-mod-active {
|
||||
z-index: 10001;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
/* border-left: 1px solid #c0c0c0; */
|
||||
/* border-right: 1px solid #c0c0c0; */
|
||||
}
|
||||
|
||||
.lm-Menu-itemIcon:before {
|
||||
content: attr(content);
|
||||
font-family:
|
||||
"ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas",
|
||||
"Liberation Mono", monospace;
|
||||
}
|
||||
|
||||
[data-type="submenu"] .lm-Menu-itemSubmenuIcon:before {
|
||||
content: ">";
|
||||
font-family:
|
||||
"ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas",
|
||||
"Liberation Mono", monospace;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.lm-mod-drag-image.lm-TabBar-tab {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,378 @@
|
||||
/* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
* ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
* ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
* ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
* ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
* ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
* ┃ 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). ┃
|
||||
* ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
*/
|
||||
|
||||
:host {
|
||||
.lm-TabBar-tabLabel {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: var(--psp--color, #666);
|
||||
cursor: pointer;
|
||||
padding: 0px 6px;
|
||||
margin: 0;
|
||||
margin-left: 15.5px;
|
||||
margin-right: auto;
|
||||
flex: 0 1 auto;
|
||||
border-radius: 3px;
|
||||
&:hover {
|
||||
background-color: var(--psp--color);
|
||||
color: var(--psp--background-color);
|
||||
}
|
||||
}
|
||||
|
||||
/* .p-TabBar-tabLabel[value="[untitled]"] { */
|
||||
/* color: #ddd !important; */
|
||||
/* } */
|
||||
|
||||
.lm-TabBar-tabLabel:focus {
|
||||
outline: none;
|
||||
color: var(--workspace-secondary--color, #666) !important;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.lm-TabBar-tab.lm-mod-closable > .lm-TabBar-tabCloseIcon:before {
|
||||
content: var(--close-button--content, "\2715");
|
||||
}
|
||||
|
||||
.lm-TabBar-tab > .lm-TabBar-tabConfigIcon:before {
|
||||
content: var(--open-settings-button--content, "\1F527");
|
||||
font-family: var(--settings--font-family, "Arial");
|
||||
transition: color 0.5s ease;
|
||||
}
|
||||
|
||||
.lm-TabBar-tab.lm-mod-current.settings_open
|
||||
> .lm-TabBar-tabConfigIcon:before {
|
||||
content: var(--close-settings-button--content, "\1F527");
|
||||
}
|
||||
|
||||
.lm-TabBar-tab.lm-mod-current.linked > .lm-TabBar-tabConfigIcon:before {
|
||||
color: #22a0ce;
|
||||
}
|
||||
|
||||
.lm-TabBar-tab > .lm-TabBar-tabConfigIcon {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.lm-TabBar-tab.lm-mod-current > .lm-TabBar-tabConfigIcon {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.lm-mod-current {
|
||||
.lm-TabBar-tabConfigIcon,
|
||||
.lm-TabBar-tabCloseIcon {
|
||||
color: inherit;
|
||||
transition: color 0.2s ease-out;
|
||||
}
|
||||
|
||||
.lm-TabBar-tabConfigIcon:hover,
|
||||
.lm-TabBar-tabCloseIcon:hover {
|
||||
color: #1a7da1;
|
||||
transition: color 0.2s ease-out;
|
||||
}
|
||||
}
|
||||
|
||||
.lm-BoxPanel {
|
||||
overflow: scroll !important;
|
||||
}
|
||||
|
||||
.lm-TabBar,
|
||||
.lm-TabBar-tab {
|
||||
position: relative;
|
||||
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.lm-TabBar-tabLabel:empty::after {
|
||||
content: "untitled";
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.lm-mod-current .lm-TabBar-tabLabel:empty::after {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.lm-TabBar-tabLabel:empty::after {
|
||||
content: "untitled";
|
||||
}
|
||||
|
||||
.divider {
|
||||
left: 32px;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
right: 32px;
|
||||
|
||||
height: 1px;
|
||||
background-color: var(--psp-workspace--tabbar--border-color);
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.lm-TabBar-tab .drag-handle {
|
||||
pointer-events: all;
|
||||
height: 12px;
|
||||
width: 14px;
|
||||
-webkit-mask-image: var(--psp-icon--column-drag-handle--mask-image);
|
||||
mask-image: var(--psp-icon--column-drag-handle--mask-image);
|
||||
padding: 12px 0 12px 12px;
|
||||
background-repeat: no-repeat;
|
||||
background-color: var(--psp--color, red);
|
||||
content: "";
|
||||
display: inline-block;
|
||||
-webkit-mask-size: cover;
|
||||
mask-size: auto;
|
||||
mask-position: center;
|
||||
mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.lm-TabBar {
|
||||
/* z-index: -1; */
|
||||
pointer-events: none;
|
||||
.lm-TabBar-tabCloseIcon {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.bookmarks-button {
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
.lm-TabBar-tab.lm-mod-hidden {
|
||||
display: flex !important;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.lm-TabBar-tab.lm-mod-current .divider {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pfm-button-base {
|
||||
background: white !important;
|
||||
}
|
||||
|
||||
.pfm-button-base:active {
|
||||
background: #016bc6 !important;
|
||||
}
|
||||
|
||||
.lm-TabBar-content {
|
||||
padding: 0px;
|
||||
border-bottom: 0px solid #fff !important;
|
||||
border-left: #eaeaea;
|
||||
border-right: #eaeaea;
|
||||
}
|
||||
|
||||
.lm-TabBar-tab.lm-mod-current {
|
||||
.shadow {
|
||||
margin: -1px;
|
||||
position: absolute;
|
||||
width: calc(100% + 2px);
|
||||
height: 200%;
|
||||
pointer-events: none;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
&.settings_open .shadow {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.lm-TabBar-tab {
|
||||
/* margin: 0px 4px; */
|
||||
border-color: #eaeaea;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.lm-TabBar-content.inactive-blur > .lm-TabBar-tab {
|
||||
background-color: var(
|
||||
--workspace-inactive-blur--background-color,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.condensed .lm-TabBar-content {
|
||||
& > .lm-TabBar-tab.lm-mod-current .lm-TabBar-tabConfigIcon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
& > .lm-TabBar-tab.lm-mod-current.settings_open {
|
||||
.lm-TabBar-tabLabel {
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lm-TabBar-content > .lm-TabBar-tab.lm-mod-current.settings_open {
|
||||
border-width: 1px 1px 0px 1px !important;
|
||||
border-color: var(--psp-workspace--tabbar--border-color, #ddd);
|
||||
|
||||
.lm-TabBar-tabToolbar {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
margin-bottom: 0px;
|
||||
background-color: var(--psp-workspace--tabbar--border-color, #ddd);
|
||||
}
|
||||
|
||||
&:last-child:first-child .divider {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
.lm-TabBar-content > .lm-TabBar-tab.lm-mod-current.perspective_updating {
|
||||
.lm-TabBar-tabConfigIcon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.lm-TabBar-tabLoadingIcon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.lm-TabBar-content > .lm-TabBar-tab {
|
||||
max-width: 100000px !important;
|
||||
flex: 0 1 100000px !important;
|
||||
background: none;
|
||||
color: #737373;
|
||||
height: 40px !important;
|
||||
max-height: 40px !important;
|
||||
margin-bottom: -40px;
|
||||
transform: none !important;
|
||||
transition: color 0.2s ease-out;
|
||||
}
|
||||
|
||||
.lm-TabBar-content > .lm-TabBar-tab {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.lm-TabBar-content > .lm-TabBar-tab {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.lm-TabBar-content > .lm-TabBar-tab:hover {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.lm-TabBar-content > .lm-TabBar-tab.lm-mod-current {
|
||||
color: var(--workspace-secondary--color, #666);
|
||||
}
|
||||
|
||||
.lm-TabBar-tab.lm-mod-current .lm-TabBar-tabLabel {
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
.lm-TabBar-tabLabel {
|
||||
font-family: inherit;
|
||||
font-size: 12px;
|
||||
line-height: 22px;
|
||||
text-align: start;
|
||||
white-space: normal !important;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.lm-TabBar-tabCloseIcon,
|
||||
.lm-TabBar-tabConfigIcon {
|
||||
cursor: pointer !important;
|
||||
line-height: 22px !important;
|
||||
padding-right: 16.5px !important;
|
||||
}
|
||||
|
||||
.lm-TabBar-tabConfigIcon {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px !important;
|
||||
}
|
||||
|
||||
.lm-TabBar-tabLoadingIcon {
|
||||
padding-left: 11px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.bottom .lm-TabBar-tab {
|
||||
flex-basis: 10000px !important;
|
||||
max-width: 10000px !important;
|
||||
}
|
||||
|
||||
.lm-TabBar-content > .lm-TabBar-tab.lm-mod-current {
|
||||
color: var(--workspace-tabbar--color, #666) !important;
|
||||
/* border: var(--psp-workspace--tabbar--border, 1px solid #ddd); */
|
||||
/* border-width: var(--psp-workspace--tabbar-tab--border-width); */
|
||||
/* box-shadow: 0 13px 0 -12px var(--psp-inactive--border-color); */
|
||||
/* border-radius: var(--psp-workspace--tabbar--border-radius, 6px) */
|
||||
/* var(--psp-workspace--tabbar--border-radius, 6px) 0 0; */
|
||||
/* background-color: var(--workspace-tabbar--background-color, white); */
|
||||
background: none;
|
||||
height: 40px !important;
|
||||
max-height: 40px !important;
|
||||
margin-bottom: -40px;
|
||||
border: 0 solid transparent;
|
||||
border-width: 1px 1px 0px 1px;
|
||||
}
|
||||
|
||||
.bottom .lm-TabBar-tab.lm-mod-current {
|
||||
border: none;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.perspective-workspace.context-menu * .lm-TabBar.context-focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.perspective-workspace.context-menu * .lm-TabBar,
|
||||
.perspective-workspace.context-menu
|
||||
.lm-Widget.workspace-widget:not(.context-focus)
|
||||
.viewer-container {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.bookmarks-button {
|
||||
margin-left: 11px;
|
||||
margin-right: -13px;
|
||||
height: 20px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--psp-inactive--color);
|
||||
background-color: var(--psp--background-color);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--psp--color);
|
||||
border-color: var(--psp--color);
|
||||
}
|
||||
}
|
||||
|
||||
.bookmarks {
|
||||
background-repeat: no-repeat;
|
||||
background-color: var(--psp--color);
|
||||
content: "";
|
||||
display: inline-block;
|
||||
-webkit-mask-size: cover;
|
||||
mask-size: cover;
|
||||
cursor: pointer;
|
||||
width: 20px;
|
||||
height: 12px;
|
||||
-webkit-mask-image: var(--psp-icon--bookmarks--mask-image);
|
||||
mask-image: var(--psp-icon--bookmarks--mask-image);
|
||||
-webkit-mask-position: center;
|
||||
mask-position: center;
|
||||
-webkit-mask-size: auto;
|
||||
mask-size: auto;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-repeat: no-repeat;
|
||||
padding: 4px 6px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--psp--background-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
* ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
* ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
* ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
* ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
* ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
* ┃ 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). ┃
|
||||
* ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
*/
|
||||
|
||||
::slotted(.workspace-master-widget) {
|
||||
--psp-status-bar--display: none;
|
||||
--psp-label--config-button--content: var(
|
||||
--open-settings-button--content,
|
||||
"\1F527"
|
||||
) !important;
|
||||
}
|
||||
|
||||
.workspace-master-widget[settings] {
|
||||
--psp-label--config-button--content: var(
|
||||
--close-settings-button--content,
|
||||
"\1F527"
|
||||
) !important;
|
||||
}
|
||||
|
||||
:not(.workspace-master-widget).widget-maximize {
|
||||
--psp-label--config-button--content: var(
|
||||
--open-settings-button--content,
|
||||
"\1F527"
|
||||
) !important;
|
||||
}
|
||||
|
||||
:not(.workspace-master-widget).widget-maximize[settings] {
|
||||
--psp-label--config-button--content: var(
|
||||
--close-settings-button--content,
|
||||
"\1F527"
|
||||
) !important;
|
||||
}
|
||||
|
||||
.lm-DockPanel-widget {
|
||||
border: 1px solid #eaeaea;
|
||||
border-width: 0px !important;
|
||||
min-width: 300px;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
:not.workspace-master-widget {
|
||||
--psp-label--config-button--content: "" !important;
|
||||
}
|
||||
|
||||
.context-menu > perspective-viewer.context-focus {
|
||||
opacity: 1;
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.context-menu > perspective-viewer {
|
||||
filter: saturate(0);
|
||||
|
||||
& > * {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
:host {
|
||||
--psp-icon--bookmarks--mask-image: url("../svg/bookmark-icon.svg");
|
||||
}
|
||||
|
||||
::slotted(perspective-viewer) {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
display: block;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible !important;
|
||||
--psp-close-button--display: flex;
|
||||
}
|
||||
|
||||
::slotted(perspective-viewer.bookmarks-container:not([settings])) {
|
||||
--psp-icon--status-ok--mask-image: var(
|
||||
--psp-icon--bookmarks--mask-image
|
||||
) !important;
|
||||
--psp-status-icon--ok--border-color: var(--psp-inactive--color);
|
||||
--psp-status-icon--ok-hover--border-color: var(--psp--color);
|
||||
--psp-status-icon--ok-hover--background-color: var(--psp--color);
|
||||
--psp-status-icon--ok-hover--color: var(--psp--background-color);
|
||||
--psp-status-icon--ok--cursor: pointer;
|
||||
--psp-status-icon--pointer-events: all;
|
||||
}
|
||||
|
||||
::slotted(perspective-viewer::part(status-indicator)) {
|
||||
background-color: red;
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
::slotted(perspective-viewer:not(.widget-maximize)) {
|
||||
--psp-status-bar--padding: 0 36px 0 8px;
|
||||
}
|
||||
|
||||
:host-context(.lm-mod-override-cursor) {
|
||||
cursor: grabbing !important;
|
||||
}
|
||||
|
||||
:host-context(.lm-mod-override-cursor) ::slotted(perspective-viewer),
|
||||
.context-menu ::slotted(perspective-viewer) {
|
||||
--override-content-pointer-events: none;
|
||||
}
|
||||
@@ -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). ┃
|
||||
* ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
*/
|
||||
|
||||
.lm-DockPanel:not([data-mode="single-document"]) .viewer-container {
|
||||
border: var(
|
||||
--psp-workspace--tabbar--border,
|
||||
1px solid var(--psp-inactive--color)
|
||||
);
|
||||
border-width: var(--psp-workspace--tabbar--border-width);
|
||||
border-radius: var(--psp-workspace--tabbar--border-radius, 6px)
|
||||
var(--psp-workspace--tabbar--border-radius, 6px)
|
||||
var(--psp-workspace--tabbar--border-radius, 6px)
|
||||
var(--psp-workspace--tabbar--border-radius, 6px);
|
||||
}
|
||||
|
||||
.viewer-container {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.perspective-scroll-panel {
|
||||
.workspace-widget {
|
||||
min-width: 300px;
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-widget {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #eaedef;
|
||||
border-width: 0px !important;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
* ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
* ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
* ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
* ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
* ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
* ┃ 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 "@lumino/widgets/style/widget.css";
|
||||
@import "@lumino/widgets/style/accordionpanel.css";
|
||||
@import "@lumino/widgets/style/commandpalette.css";
|
||||
@import "@lumino/widgets/style/dockpanel.css";
|
||||
@import "@lumino/widgets/style/menu.css";
|
||||
@import "@lumino/widgets/style/menubar.css";
|
||||
@import "@lumino/widgets/style/scrollbar.css";
|
||||
@import "@lumino/widgets/style/splitpanel.css";
|
||||
@import "@lumino/widgets/style/tabbar.css";
|
||||
@import "@lumino/widgets/style/tabpanel.css";
|
||||
|
||||
/* @import "@lumino/widgets/style/widget.css";
|
||||
@import "@lumino/widgets/style/accordionpanel.css";
|
||||
@import "@lumino/widgets/style/commandpalette.css";
|
||||
@import "@lumino/widgets/style/dockpanel.css";
|
||||
@import "@lumino/widgets/style/menu.css";
|
||||
@import "@lumino/widgets/style/menubar.css";
|
||||
@import "@lumino/widgets/style/scrollbar.css";
|
||||
@import "@lumino/widgets/style/splitpanel.css";
|
||||
@import "@lumino/widgets/style/tabbar.css";
|
||||
@import "@lumino/widgets/style/tabpanel.css"; */
|
||||
@import "@lumino/widgets/style/index.css";
|
||||
|
||||
@import "./injected.css";
|
||||
@import "./tabbar.css";
|
||||
@import "./dockpanel.css";
|
||||
@import "./widget.css";
|
||||
|
||||
:host {
|
||||
--psp-workspace--spacing: 3px;
|
||||
|
||||
/* width: 100%; */
|
||||
/* height: 100%; */
|
||||
|
||||
.workspace {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.lm-SplitPanel {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.lm-SplitPanel-handle {
|
||||
background-color: var(
|
||||
--psp-workspace--split-panel-handle--background-color
|
||||
);
|
||||
}
|
||||
|
||||
.master-panel {
|
||||
background-color: var(--workspace-master--background-color, inherit);
|
||||
padding: 0px 0px 0px 0px;
|
||||
|
||||
.viewer-container {
|
||||
border-width: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.perspective-workspace.context-menu * .lm-SplitPanel-handle {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.perspective-workspace.context-menu > .lm-SplitPanel-handle {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.context-menu .lm-TabBar:not(.context-focus) {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<!--
|
||||
|
||||
Copyright (c) 2017, the Perspective Authors.
|
||||
|
||||
This file is part of the Perspective library, distributed under the terms of
|
||||
the Apache License 2.0. The full license can be found in the LICENSE file.
|
||||
|
||||
-->
|
||||
<template id="perspective-workspace">
|
||||
<div id="container" class="workspace"></div>
|
||||
</template>
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 11" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.66214 6.96476L0.5 9.86338V0.5H7.5V9.86338L4.33786 6.96476L4 6.65505L3.66214 6.96476Z" stroke="#042121"/>
|
||||
<path d="M15 4L17 6L19 4" stroke="#042121" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 296 B |
@@ -0,0 +1,10 @@
|
||||
<svg width="5" height="12" viewBox="0 0 5 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="3.49996" cy="11.0001" r="0.833333" fill="#6E6E6E"/>
|
||||
<circle cx="0.833333" cy="11.0001" r="0.833333" fill="#6E6E6E"/>
|
||||
<circle cx="3.49996" cy="7.66683" r="0.833333" fill="#6E6E6E"/>
|
||||
<circle cx="0.833333" cy="7.66683" r="0.833333" fill="#6E6E6E"/>
|
||||
<circle cx="3.49996" cy="4.33333" r="0.833333" fill="#6E6E6E"/>
|
||||
<circle cx="0.833333" cy="4.33333" r="0.833333" fill="#6E6E6E"/>
|
||||
<circle cx="3.49996" cy="1.00008" r="0.833333" fill="#6E6E6E"/>
|
||||
<circle cx="0.833333" cy="1.00008" r="0.833333" fill="#6E6E6E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 617 B |
@@ -0,0 +1,125 @@
|
||||
/* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
* ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
* ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
* ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
* ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
* ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
* ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
||||
* ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
||||
* ┃ This file is part of the Perspective library, distributed under the terms ┃
|
||||
* ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
||||
* ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */
|
||||
|
||||
perspective-workspace,
|
||||
perspective-workspace[theme="Pro Dark"],
|
||||
perspective-indicator[theme="Pro Dark"] {
|
||||
--psp-theme-name: "Pro Dark";
|
||||
}
|
||||
|
||||
perspective-workspace perspective-viewer {
|
||||
--psp-status-bar--height: 39px;
|
||||
--psp-plugin-selector--height: 47px;
|
||||
}
|
||||
|
||||
perspective-workspace perspective-viewer.widget-maximize {
|
||||
--modal-panel--margin: -4px 0 -4px 0;
|
||||
--psp-status-bar--border-radius: 6px 0 0 0;
|
||||
--psp-main-column--margin: 3px 0 3px 3px;
|
||||
--psp-main-column--border: 1px solid var(--psp-inactive--color);
|
||||
--psp-main-column--border-width: 1px 0px 1px 1px;
|
||||
--psp-main-column--border-radius: 6px 0 0 6px;
|
||||
--settings-button--margin: 10px 0 0 0;
|
||||
}
|
||||
|
||||
perspective-workspace {
|
||||
/* perspective-workspace-pro-base */
|
||||
font-family:
|
||||
"ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas",
|
||||
"Liberation Mono", monospace;
|
||||
--open-settings-button--content: "expand_more";
|
||||
--close-settings-button--content: "expand_less";
|
||||
--close-button--content: "\2715";
|
||||
--master-divider--background-color: #243136;
|
||||
--menu-maximize--content: "fullscreen";
|
||||
--menu-minimize--content: "fullscreen_exit";
|
||||
--menu-duplicate--content: "call_split";
|
||||
--menu-master--content: "cast";
|
||||
--menu-detail--content: "notes";
|
||||
--menu-export--content: "file_download";
|
||||
--menu-copy--content: "file_copy";
|
||||
--menu-reset--content: "autorenew";
|
||||
--menu-link--content: "link";
|
||||
--menu-unlink--content: "link_off";
|
||||
--menu-newmenu--content: "add";
|
||||
--menu-close--content: "close";
|
||||
--menu-new--content: "description";
|
||||
--menu-newview--content: "file_copy";
|
||||
|
||||
--psp-workspace--tabbar--border: 1px solid var(--psp-inactive--color);
|
||||
--psp-workspace--tabbar--border-width: 0px 1px 1px 1px;
|
||||
--psp-workspace--tabbar--border-radius: 6px;
|
||||
--psp-workspace--tabbar--border-color: var(--psp-inactive--color);
|
||||
--psp-workspace--tabbar-tab--border-width: 1px 1px 0px 1px;
|
||||
|
||||
/* Workspace */
|
||||
--psp-icon--column-drag-handle--mask-image: url("../svg/drag-handle.svg");
|
||||
--psp-icon--bookmarks--mask-image: url("../svg/bookmark-icon.svg");
|
||||
|
||||
/* perspective-viewer-pro-dark--colors */
|
||||
background-color: #242526;
|
||||
--psp-workspace--split-panel-handle--background-color: #242526;
|
||||
color: white;
|
||||
--psp--color: white;
|
||||
--psp-active--color: #2770a9;
|
||||
--psp-error--color: #ff9485;
|
||||
--psp-inactive--color: #61656e;
|
||||
--psp-inactive--border-color: #4c505b;
|
||||
--psp--background-color: #242526;
|
||||
--psp-active--background: rgba(39, 113, 170, 0.5);
|
||||
--psp-expression--operator--color: #c5c9d0;
|
||||
--psp-expression--function--color: #22a0ce;
|
||||
--psp-expression--error--color: rgb(255, 136, 136);
|
||||
--psp-calendar--filter: invert(1);
|
||||
--psp-input--filter: invert(1);
|
||||
--psp-warning--color: #242526;
|
||||
--psp-warning--background: var(--psp--color);
|
||||
|
||||
--psp-icon--select-arrow--mask-image: var(
|
||||
--psp-icon--select-arrow-light--mask-image
|
||||
);
|
||||
|
||||
--psp-icon--select-arrow-hover--mask-image: var(
|
||||
--psp-icon--select-arrow-dark--mask-image
|
||||
);
|
||||
|
||||
/* Syntax */
|
||||
--psp-code-editor--symbol--color: white;
|
||||
--psp-code-editor--literal--color: #7dc3f0;
|
||||
--psp-code-editor--operator--color: rgb(23, 166, 123);
|
||||
--psp-code-editor--comment--color: rgb(204, 120, 48);
|
||||
--psp-code-editor--column--color: #e18ee1;
|
||||
|
||||
background-color: #000202;
|
||||
color: white;
|
||||
--workspace-tabbar--background-color: #242526;
|
||||
--workspace-secondary--color: #c5c9d0;
|
||||
--psp-workspace--tabbar--border: 1px solid var(--psp-inactive--color);
|
||||
--psp-workspace--tabbar--border-width: 1px 1px 1px 1px;
|
||||
--psp-workspace--tabbar--border-radius: 6px;
|
||||
--psp-workspace--tabbar--border-color: var(--psp-inactive--color);
|
||||
--psp-workspace--tabbar-tab--border-width: 1px 1px 0 1px;
|
||||
}
|
||||
|
||||
perspective-viewer[theme="Pro Dark"].workspace-master-widget {
|
||||
--psp--background-color: #242526;
|
||||
}
|
||||
|
||||
perspective-workspace-menu {
|
||||
font-family:
|
||||
"ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas",
|
||||
"Liberation Mono", monospace;
|
||||
font-weight: 300;
|
||||
background: #2a2c2f !important;
|
||||
color: white !important;
|
||||
border: 1px solid #3b3f46 !important;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/* ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
* ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
* ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
* ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
* ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
* ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
* ┃ 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). ┃
|
||||
* ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */
|
||||
|
||||
.lm-cursor-backdrop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
perspective-workspace,
|
||||
perspective-workspace[theme="Pro Light"],
|
||||
perspective-indicator[theme="Pro Light"] {
|
||||
--psp-theme-name: "Pro Light";
|
||||
}
|
||||
|
||||
perspective-workspace {
|
||||
/* perspective-workspace-pro-base */
|
||||
font-family:
|
||||
"ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas",
|
||||
"Liberation Mono", monospace;
|
||||
--open-settings-button--content: "expand_more";
|
||||
--close-settings-button--content: "expand_less";
|
||||
--close-button--content: "\2715";
|
||||
--master-divider--background-color: #243136;
|
||||
--menu-maximize--content: "fullscreen";
|
||||
--menu-minimize--content: "fullscreen_exit";
|
||||
--menu-duplicate--content: "call_split";
|
||||
--menu-master--content: "cast";
|
||||
--menu-detail--content: "notes";
|
||||
--menu-export--content: "file_download";
|
||||
--menu-copy--content: "file_copy";
|
||||
--menu-reset--content: "autorenew";
|
||||
--menu-link--content: "link";
|
||||
--menu-unlink--content: "link_off";
|
||||
--menu-newmenu--content: "add";
|
||||
--menu-close--content: "close";
|
||||
--menu-new--content: "description";
|
||||
--menu-newview--content: "file_copy";
|
||||
--psp-workspace--split-panel-handle--background-color: #f2f4f6;
|
||||
|
||||
--psp-workspace--tabbar--border: 1px solid var(--psp-inactive--color);
|
||||
--psp-workspace--tabbar--border-width: 1px 1px 1px 1px;
|
||||
--psp-workspace--tabbar--border-radius: 6px;
|
||||
--psp-workspace--tabbar--border-color: var(--psp-inactive--color);
|
||||
--psp-workspace--tabbar-tab--border-width: 1px 1px 0px 1px;
|
||||
|
||||
/* Workspace */
|
||||
--psp-icon--column-drag-handle--mask-image: url("../svg/drag-handle.svg");
|
||||
--psp-icon--bookmarks--mask-image: url("../svg/bookmark-icon.svg");
|
||||
|
||||
/* perspective-viewer-pro--colors */
|
||||
color: #161616;
|
||||
background-color: #dadada;
|
||||
--psp--color: #161616;
|
||||
--psp-inactive--color: #ababab;
|
||||
--psp-inactive--border-color: #dadada;
|
||||
|
||||
--psp-active--color: #2670a9;
|
||||
--psp-error--color: #ff471e;
|
||||
--psp--background-color: #ffffff;
|
||||
--psp-icon-overflow-hint--color: rgba(0, 0, 0, 0.2);
|
||||
--psp-select--background-color: none;
|
||||
--psp-warning--background: #042121;
|
||||
--psp-warning--color: #fdfffd;
|
||||
|
||||
--psp-icon--select-arrow--mask-image: var(
|
||||
--psp-icon--select-arrow-dark--mask-image
|
||||
);
|
||||
|
||||
--psp-icon--select-arrow-hover--mask-image: var(
|
||||
--psp-icon--select-arrow-light--mask-image
|
||||
);
|
||||
|
||||
/* TODO deprecate me */
|
||||
--psp-icon-overflow-hint--color: #fdfffd;
|
||||
}
|
||||
|
||||
perspective-workspace perspective-viewer.widget-maximize {
|
||||
/* --modal-panel--margin: -4px 0 -4px 0; */
|
||||
--psp-status-bar--border-radius: 6px 0 0 0;
|
||||
--psp-main-column--margin: 3px 0 3px 3px;
|
||||
--psp-main-column--border: 1px solid var(--psp-inactive--color);
|
||||
--psp-main-column--border-width: 1px 0px 1px 1px;
|
||||
--psp-main-column--border-radius: 6px 0 0 6px;
|
||||
/* --settings-button--margin: 10px 0 0 0; */
|
||||
}
|
||||
|
||||
perspective-workspace perspective-viewer {
|
||||
--psp-status-bar--height: 39px;
|
||||
--psp-plugin-selector--height: 47px;
|
||||
}
|
||||
|
||||
perspective-viewer[theme="Pro Light"].workspace-master-widget {
|
||||
background-color: #f2f4f6;
|
||||
--psp--background-color: #f2f4f6;
|
||||
regular-table {
|
||||
td,
|
||||
th {
|
||||
border-color: #e0e4e9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
perspective-viewer {
|
||||
--psp-icon--bookmarks--mask-image: url("../svg/bookmark-icon.svg");
|
||||
}
|
||||
|
||||
perspective-workspace-menu {
|
||||
font-family:
|
||||
"ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas",
|
||||
"Liberation Mono", monospace;
|
||||
font-weight: 300;
|
||||
color: #161616;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 { HTMLPerspectiveWorkspaceElement } from "./perspective-workspace";
|
||||
import type * as React from "react";
|
||||
|
||||
type ReactPerspectiveWorkspaceAttributes<T> = React.HTMLAttributes<T>;
|
||||
|
||||
type JsxPerspectiveWorkspaceElement = {
|
||||
class?: string;
|
||||
} & React.DetailedHTMLProps<
|
||||
ReactPerspectiveWorkspaceAttributes<HTMLPerspectiveWorkspaceElement>,
|
||||
HTMLPerspectiveWorkspaceElement
|
||||
>;
|
||||
|
||||
// React <19
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
"perspective-workspace": JsxPerspectiveWorkspaceElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// React >=19
|
||||
|
||||
// @ts-ignore
|
||||
declare module "react/jsx-runtime" {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
"perspective-workspace": JsxPerspectiveWorkspaceElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
declare module "react/jsx-dev-runtime" {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
"perspective-workspace": JsxPerspectiveWorkspaceElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare module "react" {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
"perspective-workspace": JsxPerspectiveWorkspaceElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom Elements extensions
|
||||
|
||||
declare global {
|
||||
interface Document {
|
||||
createElement(
|
||||
tagName: "perspective-workspace",
|
||||
options?: ElementCreationOptions,
|
||||
): HTMLPerspectiveWorkspaceElement;
|
||||
querySelector<E extends Element = Element>(selectors: string): E | null;
|
||||
querySelector(
|
||||
selectors: "perspective-workspace",
|
||||
): HTMLPerspectiveWorkspaceElement | null;
|
||||
}
|
||||
|
||||
interface CustomElementRegistry {
|
||||
get(
|
||||
tagName: "perspective-workspace",
|
||||
): HTMLPerspectiveWorkspaceElement & typeof HTMLElement;
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
||||
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
||||
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
||||
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
||||
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
|
||||
declare module "*.css" {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module "*.html" {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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). ┃
|
||||
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
|
||||
// Reminder for future authors as to why this file exists:
|
||||
// https://github.com/evanw/esbuild/issues/1663
|
||||
@@ -0,0 +1,266 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 { MessageLoop } from "@lumino/messaging";
|
||||
import { Widget } from "@lumino/widgets";
|
||||
import type { HTMLPerspectiveViewerElement } from "@perspective-dev/viewer";
|
||||
import type * as psp from "@perspective-dev/client";
|
||||
import type * as psp_viewer from "@perspective-dev/viewer";
|
||||
import * as msg from "@lumino/messaging";
|
||||
|
||||
export { PerspectiveWorkspace, addViewer, genId } from "./workspace";
|
||||
export { PerspectiveViewerWidget } from "./workspace/widget";
|
||||
export * from "./extensions";
|
||||
import { PerspectiveWorkspace, PerspectiveWorkspaceConfig } from "./workspace";
|
||||
import { bindTemplate, CustomElementProto } from "./utils/custom_elements";
|
||||
import style from "../../dist/css/workspace.css";
|
||||
import template from "../html/workspace.html";
|
||||
|
||||
export { PerspectiveWorkspaceConfig };
|
||||
|
||||
/**
|
||||
* A Custom Element for coordinating a set of `<perspective-viewer>` light DOM
|
||||
* children. `<perspective-workspace>` is built on Lumino.js to allow a more
|
||||
* app-like experience than `<perspective-viewer>`, providing
|
||||
* these features additionally:
|
||||
*
|
||||
* - Docking, arranging, tabbing and max/min-ing of `<perspective-viewer>`s.
|
||||
* - Trivial `<perspective-viewer>` duplication.
|
||||
* - Global Filter sidebar, for using selection state of a pivot table to filter
|
||||
* siblings.
|
||||
* - Total persistence/serializable state and child state.
|
||||
* - Easy sharing/ownership of `Table()` among different `<perspective-viewer>`.
|
||||
* - A cool DOM-reactive API.
|
||||
*
|
||||
* There are a few ways to use this Custom Element. In plain HTML, you can
|
||||
* express your initial view simply:
|
||||
*
|
||||
* ```html
|
||||
* <perspective-workspace></perspective-workspace>
|
||||
* ```
|
||||
*
|
||||
* You can also use the DOM API in Javascript:
|
||||
*
|
||||
* ```javascript
|
||||
* const workspace = document.createElement("perspective-workspace");
|
||||
* document.body.appendChild(workspace);
|
||||
* ```
|
||||
*
|
||||
* This will yield a `<perspective-workspace> with the default layout. To load
|
||||
* a `Table()`, add it to `tables` via the `Map()` API where it will be
|
||||
* auto-wired into all matching `<perspective-viewer>`s immediately:
|
||||
*
|
||||
* ```javascript
|
||||
* workspace.tables.set("superstore", await worker.table(my_data));
|
||||
* ```
|
||||
*
|
||||
*
|
||||
*/
|
||||
export class HTMLPerspectiveWorkspaceElement extends HTMLElement {
|
||||
private workspace?: PerspectiveWorkspace;
|
||||
private _resize_observer?: ResizeObserver;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.setAutoSize(true);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Public
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Persists this `<perspective-workspace>` to a token `Object`. This object
|
||||
* is JSON serializable and describes the state of the Workspace and it's
|
||||
* child `<perspective-viewer>` elements. Some important keys:
|
||||
*
|
||||
* - `viewers`: The serialized state of `<perspective-viewer>` children,
|
||||
* named by `slot`.
|
||||
* - `detail`: The main layout.
|
||||
* - `master`: The contents of the Global Filter sidebar.
|
||||
*
|
||||
* While the `table` attribute is persisted for each `perspective-viewer`,
|
||||
* `Table`s themselves must be added to the `tables` property `Map()`
|
||||
* separately.
|
||||
*
|
||||
* @return {Object} A configuration token, compatible with
|
||||
* `restore(config)`.
|
||||
* @example
|
||||
* // Save this layout to local storage
|
||||
* const workspace = document.querySelector("perspective-workspace");
|
||||
* localStorage.set("CONFIG", JSON.stringify(workspace.save()));
|
||||
*/
|
||||
save(): Promise<PerspectiveWorkspaceConfig> {
|
||||
return this.workspace!.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore this `<perspective-workspace>` to a previous state captured by
|
||||
* `save()`. Calling this method will completely rewrite this element's
|
||||
* `innerHTML`, but may reuse `<perspective-viewer>` children depending
|
||||
* on the `slot` attribute names. However, it should always be possible
|
||||
* to recreate any given state from within the UI itself, as the attributes
|
||||
* on `<perspective-viewer>` itself create immutable views.
|
||||
*
|
||||
* While the `table` attribute is set for each `perspective-viewer`,
|
||||
* `Table`s themselves must be added to the `tables` property `Map()`
|
||||
* separately.
|
||||
* @param {Object} config A configuration token, as returned by `save()`.
|
||||
* @example
|
||||
* // Restore this layout from local storage
|
||||
* const workspace = document.querySelector("perspective-workspace");
|
||||
* workspace.restore(JSON.parse(localStorage.get("CONFIG"));
|
||||
*
|
||||
* // Add `Table` separately.
|
||||
* workspace.tables.set("superstore", await worker.table(data));
|
||||
*/
|
||||
async restore(layout: PerspectiveWorkspaceConfig) {
|
||||
await this.workspace!.restore(layout);
|
||||
}
|
||||
|
||||
async clear() {
|
||||
await this.restore({
|
||||
sizes: [],
|
||||
master: undefined,
|
||||
detail: { main: null },
|
||||
viewers: {},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Await all asynchronous tasks for all viewers in this workspace. This is
|
||||
* useful to make sure asynchonous side effects of synchronous methods calls
|
||||
* are applied.
|
||||
*/
|
||||
async flush() {
|
||||
if (!this.workspace) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg.MessageLoop.flush();
|
||||
await new Promise((x) => requestAnimationFrame(x));
|
||||
await this.workspace._mutex.lock(async () => {
|
||||
await Promise.all(
|
||||
Array.from(this.querySelectorAll("perspective-viewer")).map(
|
||||
(x) => {
|
||||
const psp_widget = x as HTMLPerspectiveViewerElement;
|
||||
return psp_widget
|
||||
.flush()
|
||||
.then(() => psp_widget.flush());
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new viewer to the workspace for a given `ViewerConfigUpdateExt`.
|
||||
* @param config
|
||||
*/
|
||||
async addViewer(config: psp_viewer.ViewerConfigUpdate) {
|
||||
await this.workspace!.addViewer(config);
|
||||
await this.flush();
|
||||
}
|
||||
|
||||
async load(client: psp.Client | Promise<psp.Client>) {
|
||||
if (this.workspace) {
|
||||
this.workspace.client.push(await client);
|
||||
} else {
|
||||
throw new Error("Workspace not mounted");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate this component's dimensions and recalculate.
|
||||
*/
|
||||
async resize() {
|
||||
this.workspace!.update();
|
||||
await this.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this workspace element should auto-size itself via a
|
||||
* `ResizeObserver`.
|
||||
*/
|
||||
setAutoSize(is_auto_size: boolean) {
|
||||
this._resize_observer?.unobserve(this);
|
||||
this._resize_observer = undefined;
|
||||
if (is_auto_size) {
|
||||
this._resize_observer = new ResizeObserver((...args) =>
|
||||
this.workspace?.update(),
|
||||
);
|
||||
|
||||
this._resize_observer.observe(this);
|
||||
}
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.workspace) {
|
||||
const container = this.shadowRoot!.querySelector("#container")!;
|
||||
this.workspace = new PerspectiveWorkspace(this);
|
||||
this._register_light_dom_listener();
|
||||
MessageLoop.sendMessage(this.workspace, Widget.Msg.BeforeAttach);
|
||||
container.insertBefore(this.workspace.node, null);
|
||||
MessageLoop.sendMessage(this.workspace, Widget.Msg.AfterAttach);
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* Private
|
||||
*
|
||||
*/
|
||||
|
||||
private _light_dom_changed() {
|
||||
if (!this.workspace) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.workspace._mutex.lock(async () => {
|
||||
const viewers = Array.from(
|
||||
this.childNodes,
|
||||
) as HTMLPerspectiveViewerElement[];
|
||||
|
||||
for (const viewer of viewers) {
|
||||
if (viewer.nodeType !== Node.ELEMENT_NODE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (viewer.tagName !== "PERSPECTIVE-VIEWER") {
|
||||
console.warn("Not a <perspective-viewer>");
|
||||
continue;
|
||||
}
|
||||
|
||||
let _task = this.workspace!.update_widget_for_viewer(
|
||||
viewer as HTMLPerspectiveViewerElement,
|
||||
);
|
||||
}
|
||||
|
||||
this.workspace!.remove_unslotted_widgets(viewers);
|
||||
this.workspace!.update_details_panel(viewers);
|
||||
});
|
||||
}
|
||||
|
||||
private _register_light_dom_listener() {
|
||||
let observer = new MutationObserver(this._light_dom_changed.bind(this));
|
||||
let config = { attributes: false, childList: true, subtree: false };
|
||||
observer.observe(this, config);
|
||||
this._light_dom_changed();
|
||||
}
|
||||
}
|
||||
|
||||
bindTemplate(
|
||||
template,
|
||||
style,
|
||||
)(HTMLPerspectiveWorkspaceElement as unknown as CustomElementProto);
|
||||
@@ -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). ┃
|
||||
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
|
||||
/**
|
||||
* Instantiate a Template DOM object from an HTML text string.
|
||||
*
|
||||
* Params
|
||||
* ------
|
||||
* template : An HTML string representing a template.
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* A Template DOM object.
|
||||
*/
|
||||
function importTemplate(template: string): HTMLTemplateElement {
|
||||
const div = document.createElement("div");
|
||||
div.innerHTML = template;
|
||||
return Array.prototype.slice.call(div.children)[0];
|
||||
}
|
||||
|
||||
export interface CustomElementProto extends CustomElementConstructor {
|
||||
connectedCallback(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple tool for creating Web Components v0.
|
||||
*
|
||||
* Params
|
||||
* ------
|
||||
* template : An HTML string representing a template. Should have an 'id'
|
||||
* attribute which will become the new Web Component's tag name.
|
||||
* proto : The new Web Component's prototype object, as per spec.
|
||||
*/
|
||||
export function registerElement(
|
||||
templateString: string,
|
||||
styleString: string,
|
||||
proto: CustomElementProto,
|
||||
) {
|
||||
const template = importTemplate(templateString);
|
||||
if (styleString) {
|
||||
template.innerHTML =
|
||||
`<style>${styleString.toString()}</style>` + template.innerHTML;
|
||||
}
|
||||
|
||||
const _perspective_element = class extends proto {
|
||||
private _initialized: boolean;
|
||||
private _initializing: boolean;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._initialized = false;
|
||||
this._initializing = false;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (this._initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._initializing = true;
|
||||
const node = document.importNode(template.content, true);
|
||||
const root = this.attachShadow({ mode: "open" });
|
||||
root.appendChild(node);
|
||||
if (proto.prototype.connectedCallback) {
|
||||
proto.prototype.connectedCallback.call(this);
|
||||
}
|
||||
|
||||
this._initializing = false;
|
||||
this._initialized = true;
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
return Object.getOwnPropertyNames(proto.prototype);
|
||||
}
|
||||
};
|
||||
|
||||
let name = template.getAttribute("id")!;
|
||||
window.customElements.define(name, _perspective_element);
|
||||
}
|
||||
|
||||
export function bindTemplate(template: string, ...styleStrings: string[]) {
|
||||
const style = styleStrings.map((x) => x.toString()).join("\n");
|
||||
return function (cls: CustomElementProto) {
|
||||
return registerElement(template, style, cls);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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). ┃
|
||||
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
|
||||
export class ObservableMap<K, V> extends Map<K, V> {
|
||||
_set_listener?: (name: K, val: V) => void;
|
||||
_delete_listener?: (name: K) => void;
|
||||
|
||||
set(name: K, item: V) {
|
||||
this._set_listener?.(name, item);
|
||||
super.set(name, item);
|
||||
return this;
|
||||
}
|
||||
|
||||
get(name: K): V {
|
||||
return super.get(name)!;
|
||||
}
|
||||
|
||||
delete(name: K) {
|
||||
this._delete_listener?.(name);
|
||||
return super.delete(name);
|
||||
}
|
||||
|
||||
addSetListener(listener: (name: K, val: V) => void) {
|
||||
this._set_listener = listener;
|
||||
}
|
||||
|
||||
addDeleteListener(listener: (name: K) => void) {
|
||||
this._delete_listener = listener;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 { CommandRegistry } from "@lumino/commands";
|
||||
import { Widget } from "@lumino/widgets";
|
||||
import { Signal } from "@lumino/signaling";
|
||||
|
||||
import type {
|
||||
HTMLPerspectiveViewerCopyMenuElement,
|
||||
HTMLPerspectiveViewerExportMenuElement,
|
||||
ViewerConfigUpdate,
|
||||
} from "@perspective-dev/viewer";
|
||||
|
||||
import type { PerspectiveWorkspace } from "./workspace";
|
||||
import { WorkspaceMenu } from "./menu";
|
||||
|
||||
export const createCommands = (
|
||||
workspace: PerspectiveWorkspace,
|
||||
indicator: HTMLElement,
|
||||
) => {
|
||||
const commands = new CommandRegistry();
|
||||
|
||||
commands.addCommand("workspace:export", {
|
||||
execute: async (args) => {
|
||||
const menu = document.createElement(
|
||||
"perspective-export-menu",
|
||||
) as unknown as HTMLPerspectiveViewerExportMenuElement;
|
||||
|
||||
workspace.apply_indicator_theme();
|
||||
const widget = workspace.getWidgetByName(
|
||||
args.widget_name as string,
|
||||
)!;
|
||||
|
||||
menu.__set_model(widget.viewer.__get_model());
|
||||
menu.open(indicator);
|
||||
workspace.get_context_menu()?.init_overlay?.();
|
||||
menu.addEventListener("blur", () => {
|
||||
const context_menu = workspace.get_context_menu()!;
|
||||
const signal = context_menu.aboutToClose as Signal<
|
||||
WorkspaceMenu,
|
||||
any
|
||||
>;
|
||||
signal.emit({});
|
||||
});
|
||||
},
|
||||
isEnabled: (args) => {
|
||||
if (workspace.get_context_menu()?.node.isConnected) {
|
||||
const box = workspace
|
||||
.get_context_menu()
|
||||
?.node.getBoundingClientRect();
|
||||
|
||||
if (box) {
|
||||
indicator.style.top = box.top + "px";
|
||||
indicator.style.left = box.left + "px";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
// iconClass: "menu-export",
|
||||
label: "Export",
|
||||
mnemonic: 0,
|
||||
});
|
||||
|
||||
commands.addCommand("workspace:copy", {
|
||||
execute: async (args) => {
|
||||
const menu = document.createElement(
|
||||
"perspective-copy-menu",
|
||||
) as HTMLPerspectiveViewerCopyMenuElement;
|
||||
|
||||
workspace.apply_indicator_theme();
|
||||
const widget = workspace.getWidgetByName(
|
||||
args.widget_name as string,
|
||||
)!;
|
||||
menu.__set_model(widget.viewer.__get_model());
|
||||
|
||||
menu.open(indicator);
|
||||
workspace.get_context_menu()?.init_overlay?.();
|
||||
menu.addEventListener("blur", () => {
|
||||
(
|
||||
workspace.get_context_menu()?.aboutToClose as
|
||||
| Signal<WorkspaceMenu, any>
|
||||
| undefined
|
||||
)?.emit({});
|
||||
});
|
||||
},
|
||||
isEnabled: (_) => {
|
||||
if (workspace.get_context_menu()?.node.isConnected) {
|
||||
const box = workspace
|
||||
.get_context_menu()
|
||||
?.node.getBoundingClientRect();
|
||||
|
||||
if (box) {
|
||||
indicator.style.top = box.top + "px";
|
||||
indicator.style.left = box.left + "px";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
// iconClass: "menu-copy",
|
||||
label: "Copy",
|
||||
mnemonic: 0,
|
||||
});
|
||||
|
||||
commands.addCommand("workspace:new", {
|
||||
execute: async (args) => {
|
||||
const widget = await workspace._createWidgetAndNode({
|
||||
config: {
|
||||
table: args.table as string,
|
||||
},
|
||||
slot: undefined,
|
||||
});
|
||||
|
||||
workspace.get_dock_panel().addWidget(widget, {
|
||||
mode: "split-right",
|
||||
ref: workspace.getWidgetByName(args.widget_name as string),
|
||||
});
|
||||
},
|
||||
// iconClass: "menu-new-tables",
|
||||
label: (args) => {
|
||||
return args.table as string;
|
||||
},
|
||||
});
|
||||
|
||||
commands.addCommand("workspace:newview", {
|
||||
execute: async (args) => {
|
||||
const widget = workspace.getWidgetByName(
|
||||
args.widget_name as string,
|
||||
);
|
||||
|
||||
const target_widget = workspace.getWidgetByName(
|
||||
args.target_widget_name as string,
|
||||
)!;
|
||||
|
||||
const config = (await target_widget.save()) as ViewerConfigUpdate;
|
||||
const new_widget = await workspace._createWidgetAndNode({
|
||||
config,
|
||||
slot: undefined,
|
||||
});
|
||||
|
||||
workspace.get_dock_panel().addWidget(new_widget, {
|
||||
mode: "split-right",
|
||||
ref: widget,
|
||||
});
|
||||
},
|
||||
// iconClass: "menu-new-tables",
|
||||
isVisible: (args) => {
|
||||
const target_widget = workspace.getWidgetByName(
|
||||
args.target_widget_name as string,
|
||||
)!;
|
||||
|
||||
return target_widget.title.label !== "";
|
||||
},
|
||||
label: (args) => {
|
||||
const target_widget = workspace.getWidgetByName(
|
||||
args.target_widget_name as string,
|
||||
)!;
|
||||
|
||||
return target_widget.title.label || "untitled";
|
||||
},
|
||||
});
|
||||
|
||||
commands.addCommand("workspace:reset", {
|
||||
execute: (args) => {
|
||||
workspace
|
||||
.getWidgetByName(args.widget_name as string)!
|
||||
.viewer.reset();
|
||||
},
|
||||
label: "Reset",
|
||||
mnemonic: 0,
|
||||
});
|
||||
|
||||
commands.addCommand("workspace:settings", {
|
||||
execute: async ({ widget_name }) => {
|
||||
const widget = workspace.getWidgetByName(widget_name as string);
|
||||
if (!widget) {
|
||||
throw new Error(`No widget ${widget_name}`);
|
||||
}
|
||||
|
||||
// if (!widget.viewer.hasAttribute("settings")) {
|
||||
// workspace._maximize(widget);
|
||||
requestAnimationFrame(() => widget.viewer.toggleConfig());
|
||||
// } else {
|
||||
// widget.viewer.toggleConfig();
|
||||
// }
|
||||
},
|
||||
isVisible: (args) => {
|
||||
const widget = workspace.getWidgetByName(
|
||||
args.widget_name as string,
|
||||
);
|
||||
|
||||
return widget?.parent! === (workspace.get_dock_panel() as Widget)
|
||||
? true
|
||||
: false;
|
||||
},
|
||||
label: (args) => {
|
||||
const widget = workspace.getWidgetByName(
|
||||
args.widget_name as string,
|
||||
)!;
|
||||
if (widget.viewer.hasAttribute("settings")) {
|
||||
return "Close Settings";
|
||||
} else {
|
||||
return "Open Settings";
|
||||
}
|
||||
},
|
||||
mnemonic: 0,
|
||||
});
|
||||
|
||||
commands.addCommand("workspace:duplicate", {
|
||||
execute: ({ widget_name }) =>
|
||||
workspace.duplicate(
|
||||
workspace.getWidgetByName(widget_name as string)!,
|
||||
),
|
||||
// iconClass: "menu-duplicate",
|
||||
isVisible: (args) => {
|
||||
return workspace.getWidgetByName(args.widget_name as string)
|
||||
?.parent! === (workspace.get_dock_panel() as Widget)
|
||||
? true
|
||||
: false;
|
||||
},
|
||||
label: "Duplicate",
|
||||
mnemonic: 0,
|
||||
});
|
||||
|
||||
commands.addCommand("workspace:master", {
|
||||
execute: (args) =>
|
||||
workspace.toggleMasterDetail(
|
||||
workspace.getWidgetByName(args.widget_name as string)!,
|
||||
),
|
||||
isVisible: (args) => {
|
||||
return !!workspace.getWidgetByName(args.widget_name as string)
|
||||
?._is_pivoted;
|
||||
},
|
||||
|
||||
label: (args) => {
|
||||
return workspace.getWidgetByName(args.widget_name as string)!
|
||||
.parent === workspace.get_dock_panel()
|
||||
? "Create Global Filter"
|
||||
: "Remove Global Filter";
|
||||
},
|
||||
mnemonic: 0,
|
||||
});
|
||||
|
||||
commands.addCommand("workspace:close", {
|
||||
execute: (args) => {
|
||||
workspace.getWidgetByName(args.widget_name as string)!.close();
|
||||
},
|
||||
// iconClass: "menu-close",
|
||||
label: () => "Close",
|
||||
mnemonic: 0,
|
||||
});
|
||||
|
||||
commands.addCommand("workspace:help", {
|
||||
// iconClass: "menu-close",
|
||||
execute: () => {},
|
||||
label: "Shift+Click for Browser Menu",
|
||||
isEnabled: () => false,
|
||||
// mnemonic: 0,
|
||||
});
|
||||
|
||||
return commands;
|
||||
};
|
||||
@@ -0,0 +1,156 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 { DockLayout, DockPanel, TabBar, Widget } from "@lumino/widgets";
|
||||
import { PerspectiveTabBar } from "./tabbar";
|
||||
import { PerspectiveTabBarRenderer } from "./tabbarrenderer";
|
||||
import { PerspectiveWorkspace } from "./workspace";
|
||||
import { PerspectiveViewerWidget } from "./widget";
|
||||
|
||||
class PerspectiveDockPanelRenderer extends DockPanel.Renderer {
|
||||
_workspace: PerspectiveWorkspace;
|
||||
|
||||
constructor(workspace: PerspectiveWorkspace) {
|
||||
super();
|
||||
this._workspace = workspace;
|
||||
}
|
||||
|
||||
createTabBar() {
|
||||
const tabbar = new PerspectiveTabBar(this._workspace, {
|
||||
renderer: new PerspectiveTabBarRenderer(false),
|
||||
});
|
||||
|
||||
tabbar.addClass("lm-DockPanel-tabBar");
|
||||
return tabbar;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore: extending a private member `_onTabDetachRequested`
|
||||
export class PerspectiveDockPanel extends DockPanel {
|
||||
_workspace: PerspectiveWorkspace;
|
||||
constructor(workspace: PerspectiveWorkspace) {
|
||||
super({ renderer: new PerspectiveDockPanelRenderer(workspace) });
|
||||
|
||||
// @ts-ignore: accessing a private member `_renderer`
|
||||
this._renderer.dock = this;
|
||||
this._workspace = workspace;
|
||||
}
|
||||
|
||||
_onTabDetachRequested(
|
||||
sender: TabBar<Widget>,
|
||||
args: TabBar.ITabDetachRequestedArgs<Widget>,
|
||||
) {
|
||||
// @ts-ignore: accessing a private member `_onTabDetachRequested`
|
||||
super._onTabDetachRequested(sender, args);
|
||||
|
||||
// blur widget on when it's being moved
|
||||
const widget = sender.titles[args.index].owner;
|
||||
const layout = this.layout as DockLayout;
|
||||
const old = layout.saveLayout();
|
||||
if (Array.from(layout.widgets()).length > 1) {
|
||||
layout.removeWidget(widget);
|
||||
}
|
||||
|
||||
widget.addClass("widget-blur");
|
||||
document.body.classList.add("lm-mod-override-cursor");
|
||||
|
||||
// @ts-ignore: accessing a private member `_drag`
|
||||
const drag = this._drag;
|
||||
if (drag) {
|
||||
drag.dragImage?.parentElement?.removeChild?.(drag.dragImage);
|
||||
drag.dragImage = null;
|
||||
drag._promise.then(() => {
|
||||
if (!widget.node.isConnected) {
|
||||
layout.restoreLayout(old);
|
||||
}
|
||||
|
||||
document.body.classList.remove("lm-mod-override-cursor");
|
||||
widget.removeClass("widget-blur");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static getWidgets(
|
||||
layout: DockPanel.ILayoutConfig,
|
||||
): PerspectiveViewerWidget[] {
|
||||
if (!!layout.main) {
|
||||
return PerspectiveDockPanel.getAreaWidgets(layout.main);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static getAreaWidgets(
|
||||
layout: DockLayout.AreaConfig,
|
||||
): PerspectiveViewerWidget[] {
|
||||
if (layout?.hasOwnProperty("children")) {
|
||||
const split_panel = layout as DockLayout.ISplitAreaConfig;
|
||||
return split_panel.children.flatMap((widget) =>
|
||||
PerspectiveDockPanel.getAreaWidgets(widget),
|
||||
);
|
||||
} else if (layout?.hasOwnProperty("widgets")) {
|
||||
const tab_panel = layout as DockLayout.ITabAreaConfig;
|
||||
return tab_panel.widgets as PerspectiveViewerWidget[];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
widgets(): IterableIterator<PerspectiveViewerWidget> {
|
||||
return super.widgets() as IterableIterator<PerspectiveViewerWidget>;
|
||||
}
|
||||
|
||||
static async mapWidgets(
|
||||
widgetFunc: (widget: any) => Promise<any>,
|
||||
layout: any,
|
||||
): Promise<DockPanel.ILayoutConfig> {
|
||||
if (!!layout.main) {
|
||||
layout.main = await PerspectiveDockPanel.mapAreaWidgets(
|
||||
widgetFunc,
|
||||
layout.main,
|
||||
);
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
static async mapAreaWidgets(
|
||||
widgetFunc: (widget: any) => Promise<any>,
|
||||
layout: DockLayout.AreaConfig,
|
||||
): Promise<DockLayout.AreaConfig> {
|
||||
if (layout.hasOwnProperty("children")) {
|
||||
const split_panel = layout as DockLayout.ISplitAreaConfig;
|
||||
split_panel.children = await Promise.all(
|
||||
split_panel.children.map((widget) =>
|
||||
PerspectiveDockPanel.mapAreaWidgets(widgetFunc, widget),
|
||||
),
|
||||
);
|
||||
} else if (layout.hasOwnProperty("widgets")) {
|
||||
const tab_panel = layout as DockLayout.ITabAreaConfig;
|
||||
tab_panel.widgets = await Promise.all(
|
||||
tab_panel.widgets.map(widgetFunc),
|
||||
);
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
onAfterAttach() {
|
||||
this.spacing =
|
||||
parseInt(
|
||||
window
|
||||
.getComputedStyle(this._workspace.element)
|
||||
.getPropertyValue("--psp-workspace--spacing"),
|
||||
) || 0;
|
||||
1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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). ┃
|
||||
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||
|
||||
export * from "./workspace";
|
||||
@@ -0,0 +1,213 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 { ElementExt } from "@lumino/domutils";
|
||||
import { MessageLoop } from "@lumino/messaging";
|
||||
import { h } from "@lumino/virtualdom";
|
||||
import { Menu, Widget } from "@lumino/widgets";
|
||||
|
||||
export class WorkspaceMenu extends Menu {
|
||||
private _host: ShadowRoot;
|
||||
private _workspace: HTMLElement;
|
||||
init_overlay?: () => void;
|
||||
|
||||
constructor(
|
||||
host: ShadowRoot,
|
||||
workspace: HTMLElement,
|
||||
options: Menu.IOptions,
|
||||
) {
|
||||
options.renderer = new MenuRenderer();
|
||||
super(options);
|
||||
this._host = host;
|
||||
this._workspace = workspace;
|
||||
(this as any)._openChildMenu = this._overrideOpenChildMenu.bind(this);
|
||||
}
|
||||
|
||||
open(x: number, y: number, options?: Menu.IOpenOptions) {
|
||||
options ||= {};
|
||||
options.host = this._host as any as HTMLElement;
|
||||
const box = this._workspace.getBoundingClientRect();
|
||||
super.open(x, y, options);
|
||||
const menu_box = this.node.getBoundingClientRect();
|
||||
if (
|
||||
menu_box.height + y > box.height &&
|
||||
menu_box.height + y < document.documentElement.clientHeight
|
||||
) {
|
||||
this.node.style.top = `-${menu_box.height}`;
|
||||
}
|
||||
|
||||
if (menu_box.width + x > box.width) {
|
||||
if (menu_box.width + x < document.documentElement.clientWidth) {
|
||||
this.node.style.left = `-${menu_box.width + x - box.width}px`;
|
||||
} else {
|
||||
this.node.style.left = `-${
|
||||
document.documentElement.clientWidth - box.width
|
||||
}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override this lumino private method because it will otherwise always
|
||||
// attach to `document.body`.
|
||||
private _overrideOpenChildMenu(activateFirst = false) {
|
||||
const self = this as any;
|
||||
let item = this.activeItem;
|
||||
if (!item || item.type !== "submenu" || !item.submenu) {
|
||||
self._closeChildMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
let submenu = item.submenu;
|
||||
if (submenu === self._childMenu) {
|
||||
return;
|
||||
}
|
||||
|
||||
Menu.saveWindowData();
|
||||
self._closeChildMenu();
|
||||
self._childMenu = submenu;
|
||||
self._childIndex = self._activeIndex;
|
||||
(submenu as any)._parentMenu = this;
|
||||
MessageLoop.sendMessage(this, Widget.Msg.UpdateRequest);
|
||||
let itemNode = this.contentNode.children[self._activeIndex];
|
||||
openSubmenu(submenu, itemNode as HTMLElement, self._host);
|
||||
if (activateFirst) {
|
||||
submenu.activeIndex = -1;
|
||||
submenu.activateNextItem();
|
||||
}
|
||||
|
||||
submenu.activate();
|
||||
}
|
||||
}
|
||||
|
||||
class MenuRenderer extends Menu.Renderer {
|
||||
formatLabel(data: Menu.IRenderData) {
|
||||
let { label, mnemonic } = data.item;
|
||||
if (mnemonic < 0 || mnemonic >= label.length) {
|
||||
return label;
|
||||
}
|
||||
|
||||
let prefix = label.slice(0, mnemonic);
|
||||
let suffix = label.slice(mnemonic + 1);
|
||||
let char = label[mnemonic];
|
||||
let span = h.span(
|
||||
{
|
||||
className: "lm-Menu-itemMnemonic p-Menu-itemMnemonic",
|
||||
},
|
||||
char,
|
||||
);
|
||||
|
||||
return [prefix, span, suffix];
|
||||
}
|
||||
|
||||
renderLabel(data: Menu.IRenderData) {
|
||||
let content = this.formatLabel(data);
|
||||
return h.div(
|
||||
{
|
||||
className: "lm-Menu-itemLabel p-Menu-itemLabel",
|
||||
},
|
||||
content,
|
||||
);
|
||||
}
|
||||
|
||||
renderSubmenu(data: Menu.IRenderData) {
|
||||
return h.div({
|
||||
className: "lm-Menu-itemSubmenuIcon" + " p-Menu-itemSubmenuIcon",
|
||||
});
|
||||
}
|
||||
|
||||
renderItem(data: Menu.IRenderData) {
|
||||
let className = this.createItemClass(data);
|
||||
let dataset = this.createItemDataset(data);
|
||||
let aria = this.createItemARIA(data);
|
||||
return h.li(
|
||||
{
|
||||
className,
|
||||
dataset,
|
||||
tabindex: "0",
|
||||
onfocus: data.onfocus,
|
||||
...aria,
|
||||
},
|
||||
this.renderLabel(data),
|
||||
this.renderShortcut(data),
|
||||
this.renderSubmenu(data),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent submenus from attaching outside the Shadow DOM.
|
||||
// Forked from [Lumino](https://github.com/jupyterlab/lumino/blob/main/packages/widgets/src/menu.ts).
|
||||
// [License](https://github.com/jupyterlab/lumino/blob/main/LICENSE)
|
||||
export function openSubmenu(
|
||||
submenu: Menu,
|
||||
itemNode: HTMLElement,
|
||||
host: HTMLElement,
|
||||
): void {
|
||||
const windowData = getWindowData();
|
||||
let px = windowData.pageXOffset;
|
||||
let py = windowData.pageYOffset;
|
||||
let cw = windowData.clientWidth;
|
||||
let ch = windowData.clientHeight;
|
||||
const hostData = (host as any).host.getBoundingClientRect();
|
||||
let hx = hostData.x;
|
||||
let hy = hostData.y;
|
||||
MessageLoop.sendMessage(submenu, Widget.Msg.UpdateRequest);
|
||||
let maxHeight = ch;
|
||||
let node = submenu.node;
|
||||
let style = node.style;
|
||||
style.opacity = "0";
|
||||
style.maxHeight = `${maxHeight}px`;
|
||||
Widget.attach(submenu, host);
|
||||
let { width, height } = node.getBoundingClientRect();
|
||||
let box = ElementExt.boxSizing(submenu.node);
|
||||
let itemRect = itemNode.getBoundingClientRect();
|
||||
let x = itemRect.right - SUBMENU_OVERLAP - hx;
|
||||
if (x + width > px + cw + hx) {
|
||||
x = itemRect.left + SUBMENU_OVERLAP - width;
|
||||
}
|
||||
|
||||
let y = itemRect.top - box.borderTop - box.paddingTop - hy;
|
||||
if (y + height > py + ch + hy) {
|
||||
y = itemRect.bottom + box.borderBottom + box.paddingBottom - height;
|
||||
}
|
||||
|
||||
style.transform = `translate(${Math.max(0, x)}px, ${Math.max(0, y)}px`;
|
||||
style.opacity = "1";
|
||||
}
|
||||
|
||||
export const SUBMENU_OVERLAP = 3;
|
||||
|
||||
let transientWindowDataCache: IWindowData | null = null;
|
||||
let transientCacheCounter: number = 0;
|
||||
|
||||
function getWindowData(): IWindowData {
|
||||
if (transientCacheCounter > 0) {
|
||||
transientCacheCounter--;
|
||||
return transientWindowDataCache!;
|
||||
}
|
||||
return _getWindowData();
|
||||
}
|
||||
|
||||
function _getWindowData(): IWindowData {
|
||||
return {
|
||||
pageXOffset: window.pageXOffset,
|
||||
pageYOffset: window.pageYOffset,
|
||||
clientWidth: document.documentElement.clientWidth,
|
||||
clientHeight: document.documentElement.clientHeight,
|
||||
};
|
||||
}
|
||||
|
||||
interface IWindowData {
|
||||
pageXOffset: number;
|
||||
pageYOffset: number;
|
||||
clientWidth: number;
|
||||
clientHeight: number;
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 { ArrayExt } from "@lumino/algorithm";
|
||||
import { ElementExt } from "@lumino/domutils";
|
||||
import { TabBar } from "@lumino/widgets";
|
||||
import { TabBarItems, PerspectiveTabBarRenderer } from "./tabbarrenderer";
|
||||
import { VirtualDOM } from "@lumino/virtualdom";
|
||||
import { CommandRegistry } from "@lumino/commands";
|
||||
import { Menu } from "@lumino/widgets";
|
||||
import { PerspectiveWorkspace } from "./workspace";
|
||||
import { Message } from "@lumino/messaging";
|
||||
import { Title } from "@lumino/widgets";
|
||||
import { Signal } from "@lumino/signaling";
|
||||
import { ReadonlyJSONObject, ReadonlyJSONValue } from "@lumino/coreutils";
|
||||
import { WorkspaceMenu } from "./menu";
|
||||
|
||||
export class PerspectiveTabBar extends TabBar<any> {
|
||||
_workspace: PerspectiveWorkspace;
|
||||
__content_node__?: HTMLElement;
|
||||
_menu?: Menu;
|
||||
__titles: string[];
|
||||
|
||||
constructor(workspace: PerspectiveWorkspace, options = {}) {
|
||||
super(options);
|
||||
this._addEventListeners();
|
||||
this.__content_node__ = undefined;
|
||||
this._workspace = workspace;
|
||||
this.__titles = [];
|
||||
}
|
||||
|
||||
get private_titles(): Title<any>[] {
|
||||
let titles: Title<any>[] = (this as any)._titles;
|
||||
return titles;
|
||||
}
|
||||
|
||||
onUpdateRequest(msg: Message) {
|
||||
// NOT INERT! This is a lumino bug fix.
|
||||
// lumino/virtualdom keeps a weakmap on contentNode which is later
|
||||
// reset - this causes the diff to double some elements. Memoizing
|
||||
// prevent collection from the weakmap.
|
||||
this.__content_node__ = this.contentNode;
|
||||
this.node.style.contain = "";
|
||||
|
||||
// super.onUpdateRequest(msg);
|
||||
|
||||
let titles: Title<any>[] = (this as any)._titles;
|
||||
let renderer = this.renderer;
|
||||
let currentTitle = this.currentTitle!;
|
||||
|
||||
// Another hack. `TabBar` selects by index and I don't want to fork this
|
||||
// logic, so insert empty divs until the indices match.
|
||||
let content = new Array();
|
||||
for (let i = 0, n = titles.length; i < n; ++i) {
|
||||
let title = titles[i];
|
||||
title.owner._titlebar = this;
|
||||
let current = title === currentTitle;
|
||||
let otherTitles = titles.filter((x) => x !== currentTitle);
|
||||
let onClick;
|
||||
if (otherTitles.length > 0) {
|
||||
onClick = this.onClick.bind(this, otherTitles, i);
|
||||
}
|
||||
|
||||
if (current) {
|
||||
content[i] = (
|
||||
renderer as unknown as PerspectiveTabBarRenderer
|
||||
).renderTab(
|
||||
{
|
||||
title,
|
||||
zIndex: titles.length + 1,
|
||||
current,
|
||||
} as TabBar.IRenderData<any>,
|
||||
onClick,
|
||||
);
|
||||
} else {
|
||||
content[i] = (
|
||||
renderer as unknown as PerspectiveTabBarRenderer
|
||||
).renderInert();
|
||||
}
|
||||
}
|
||||
|
||||
VirtualDOM.render(content, this.contentNode);
|
||||
this._check_shade();
|
||||
}
|
||||
|
||||
onClick(otherTitles: Title<any>[], index: number, event: MouseEvent) {
|
||||
const commands = new CommandRegistry();
|
||||
this._menu = new WorkspaceMenu(
|
||||
this._workspace.menu_elem.shadowRoot!,
|
||||
this._workspace.element,
|
||||
{
|
||||
commands,
|
||||
},
|
||||
);
|
||||
|
||||
this._menu.addClass("perspective-workspace-menu");
|
||||
this._menu.dataset.minwidth = this.__titles[index];
|
||||
for (const title of otherTitles) {
|
||||
this._menu.addItem({
|
||||
command: "tabbar:switch",
|
||||
args: { title } as unknown as ReadonlyJSONObject,
|
||||
});
|
||||
}
|
||||
|
||||
commands.addCommand("tabbar:switch", {
|
||||
execute: async ({ title }) => {
|
||||
const psp_title = title as any as Title<any>;
|
||||
const index = this.__titles.findIndex((t) => t === title);
|
||||
this.currentTitle = psp_title;
|
||||
(
|
||||
this.tabActivateRequested as unknown as Signal<
|
||||
TabBar<any>,
|
||||
TabBar.ITabActivateRequestedArgs<any>
|
||||
>
|
||||
).emit({
|
||||
index,
|
||||
title: psp_title,
|
||||
});
|
||||
},
|
||||
label: ({ title }) => {
|
||||
const psp_title = title as any as Title<any>;
|
||||
return psp_title.label || "untitled";
|
||||
},
|
||||
mnemonic: 0,
|
||||
});
|
||||
|
||||
const box = (
|
||||
(event.target as HTMLElement).shadowRoot?.querySelector(
|
||||
"#status_reconnect",
|
||||
) as HTMLElement
|
||||
).getBoundingClientRect();
|
||||
|
||||
const outer_box = this._workspace.element.getBoundingClientRect();
|
||||
this._menu.open(box.x - outer_box.x, box.y + box.height - outer_box.y);
|
||||
this._menu.aboutToClose.connect(() => {
|
||||
this._menu = undefined;
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
_check_shade() {
|
||||
if (
|
||||
Array.from(this.contentNode.children).filter(
|
||||
(x) =>
|
||||
x.classList.contains("settings_open") &&
|
||||
x.classList.contains("lm-mod-current"),
|
||||
).length > 0
|
||||
) {
|
||||
this.contentNode.classList.add("inactive-blur");
|
||||
} else {
|
||||
this.contentNode.classList.remove("inactive-blur");
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(event: MouseEvent) {
|
||||
this._menu?.close();
|
||||
this.retargetEvent(event);
|
||||
switch (event.type) {
|
||||
case "contextmenu":
|
||||
const widget = this.currentTitle?.owner;
|
||||
let parent = widget.parent;
|
||||
|
||||
// TODO There is probably a better way to find the workspace
|
||||
// relative to a widget command
|
||||
while (parent && !(parent instanceof PerspectiveWorkspace)) {
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
(parent as unknown as PerspectiveWorkspace).showContextMenu(
|
||||
widget,
|
||||
event,
|
||||
);
|
||||
|
||||
if (!event.shiftKey) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
break;
|
||||
case "mousedown":
|
||||
if ((event.target as HTMLElement).id === TabBarItems.Label) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "pointerdown":
|
||||
if ((event.target as HTMLElement).id === TabBarItems.Label) {
|
||||
const tabs = this.contentNode.children;
|
||||
|
||||
// Find the index of the released tab.
|
||||
const index = ArrayExt.findFirstIndex(tabs, (tab) => {
|
||||
return ElementExt.hitTest(
|
||||
tab,
|
||||
event.clientX,
|
||||
event.clientY,
|
||||
);
|
||||
});
|
||||
|
||||
if (index < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const title = this.titles[index];
|
||||
this._workspace._maximize(title.owner);
|
||||
requestAnimationFrame(() =>
|
||||
title.owner.viewer.toggleConfig(),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
super.handleEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shadow dom targets events at the host, not the clicked element, which
|
||||
* Lumino dislikes. This makes the event look like it is not crossing
|
||||
* the ShadowDom boundary.
|
||||
*
|
||||
*/
|
||||
retargetEvent(event: MouseEvent) {
|
||||
Object.defineProperty(event, "target", {
|
||||
value: event.composedPath()[0],
|
||||
enumerable: true,
|
||||
});
|
||||
return event;
|
||||
}
|
||||
|
||||
_addEventListeners() {
|
||||
this.node.addEventListener("dblclick", this);
|
||||
this.node.addEventListener("contextmenu", this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 { h } from "@lumino/virtualdom";
|
||||
import { TabBar } from "@lumino/widgets";
|
||||
|
||||
export const TabBarItems = {
|
||||
Config: "config",
|
||||
Label: "label",
|
||||
};
|
||||
|
||||
export const DEFAULT_TITLE = "untitled";
|
||||
|
||||
export class PerspectiveTabBarRenderer extends TabBar.Renderer {
|
||||
maximized: boolean;
|
||||
|
||||
constructor(maximized: boolean) {
|
||||
super();
|
||||
this.maximized = maximized;
|
||||
}
|
||||
|
||||
renderLabel(data: { title: { label?: string } }) {
|
||||
return h.span(
|
||||
{
|
||||
className: "lm-TabBar-tabLabel",
|
||||
id: TabBarItems.Label,
|
||||
},
|
||||
data.title.label || DEFAULT_TITLE,
|
||||
);
|
||||
}
|
||||
|
||||
renderInert() {
|
||||
return h.div();
|
||||
}
|
||||
|
||||
renderTab(
|
||||
data: TabBar.IRenderData<any>,
|
||||
onclick?: (this: HTMLElement, event: MouseEvent) => any,
|
||||
) {
|
||||
const title = data.title.caption;
|
||||
const key = this.createTabKey(data);
|
||||
const style = this.createTabStyle(data);
|
||||
let className = this.createTabClass(data);
|
||||
const dataset = this.createTabDataset(data);
|
||||
const more: h.Child[] = [];
|
||||
if (onclick) {
|
||||
data.title.owner.setCallback(onclick);
|
||||
data.title.owner.addClass("bookmarks-container");
|
||||
} else {
|
||||
data.title.owner.setCallback(undefined);
|
||||
data.title.owner.removeClass("bookmarks-container");
|
||||
}
|
||||
|
||||
return h.li(
|
||||
{ key, className, title, style, dataset },
|
||||
// this.renderDragHandle(),
|
||||
);
|
||||
}
|
||||
|
||||
// renderDragHandle() {
|
||||
// return h.div({
|
||||
// className: "drag-handle",
|
||||
// });
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 { Widget } from "@lumino/widgets";
|
||||
import { Message } from "@lumino/messaging";
|
||||
|
||||
import type * as psp_viewer from "@perspective-dev/viewer";
|
||||
import type * as psp from "@perspective-dev/client";
|
||||
import { PerspectiveTabBar } from "./tabbar";
|
||||
|
||||
interface IPerspectiveViewerWidgetOptions {
|
||||
node: HTMLElement;
|
||||
viewer: psp_viewer.HTMLPerspectiveViewerElement;
|
||||
onAttach?: () => void;
|
||||
}
|
||||
|
||||
export class PerspectiveViewerWidget extends Widget {
|
||||
viewer: psp_viewer.HTMLPerspectiveViewerElement;
|
||||
_title: string;
|
||||
_is_pivoted: boolean;
|
||||
_restore_config?: () => Promise<void>;
|
||||
_onAttach?: () => void;
|
||||
_titlebar?: PerspectiveTabBar;
|
||||
_deleted: boolean;
|
||||
_titlebar_callback?: (event: MouseEvent) => {};
|
||||
|
||||
constructor({ viewer, node, onAttach }: IPerspectiveViewerWidgetOptions) {
|
||||
super({ node });
|
||||
this.viewer = viewer;
|
||||
this._title = "";
|
||||
this._is_pivoted = false;
|
||||
this._onAttach = onAttach;
|
||||
this._deleted = false;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._title;
|
||||
}
|
||||
|
||||
toggleConfig(): Promise<void> {
|
||||
return this.viewer.toggleConfig();
|
||||
}
|
||||
|
||||
async load(table: psp.Table | Promise<psp.Table>) {
|
||||
let promises = [this.viewer.load(table)];
|
||||
if (this._restore_config) {
|
||||
promises.push(this._restore_config());
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
restore(config: psp_viewer.ViewerConfigUpdate) {
|
||||
this._title = config.title as string;
|
||||
this.title.label = config.title as string;
|
||||
this._is_pivoted = (config.group_by?.length || 0) > 0;
|
||||
return this.viewer.restore({ ...config });
|
||||
}
|
||||
|
||||
async save() {
|
||||
let config = await this.viewer.save();
|
||||
config["settings"] = false;
|
||||
return config;
|
||||
}
|
||||
|
||||
addClass(name: string) {
|
||||
super.addClass(name);
|
||||
this.viewer?.classList?.add?.(name);
|
||||
}
|
||||
|
||||
removeClass(name: string) {
|
||||
super.removeClass(name);
|
||||
this.viewer?.classList?.remove?.(name);
|
||||
}
|
||||
|
||||
setCallback(callback?: (event: MouseEvent) => {}) {
|
||||
this._titlebar_callback = callback;
|
||||
}
|
||||
|
||||
protected onAfterAttach(msg: Message) {
|
||||
super.onAfterAttach(msg);
|
||||
this._onAttach?.();
|
||||
}
|
||||
|
||||
onCloseRequest(msg: Message) {
|
||||
super.onCloseRequest(msg);
|
||||
return (async () => {
|
||||
if (this.viewer.parentElement) {
|
||||
this.viewer.parentElement.removeChild(this.viewer);
|
||||
}
|
||||
|
||||
if (!this._deleted) {
|
||||
await this.viewer.delete();
|
||||
this._deleted = true;
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,100 @@
|
||||
Row ID,Order ID,Order Date,Ship Date,Ship Mode,Customer ID,Segment,Country,City,State,Postal Code,Region,Product ID,Category,Sub-Category,Sales,Quantity,Discount,Profit
|
||||
1,CA-2013-152156,11/9/2013,11/12/2013,Second Class,CG-12520,Consumer,United States,Henderson,Kentucky,42420,South,FUR-BO-10001798,Furniture,Bookcases,261.96,2,0.0,41.9136
|
||||
2,CA-2013-152156,11/9/2013,11/12/2013,Second Class,CG-12520,Consumer,United States,Henderson,Kentucky,42420,South,FUR-CH-10000454,Furniture,Chairs,731.94,3,0.0,219.582
|
||||
3,CA-2013-138688,6/13/2013,6/17/2013,Second Class,DV-13045,Corporate,United States,Los Angeles,California,90036,West,OFF-LA-10000240,Office Supplies,Labels,14.62,2,0.0,6.8714
|
||||
4,US-2012-108966,10/11/2012,10/18/2012,Standard Class,SO-20335,Consumer,United States,Fort Lauderdale,Florida,33311,South,FUR-TA-10000577,Furniture,Tables,957.5775,5,0.45,-383.031
|
||||
5,US-2012-108966,10/11/2012,10/18/2012,Standard Class,SO-20335,Consumer,United States,Fort Lauderdale,Florida,33311,South,OFF-ST-10000760,Office Supplies,Storage,22.368,2,0.2,2.5164
|
||||
6,CA-2011-115812,6/9/2011,6/14/2011,Standard Class,BH-11710,Consumer,United States,Los Angeles,California,90032,West,FUR-FU-10001487,Furniture,Furnishings,48.86,7,0.0,14.1694
|
||||
7,CA-2011-115812,6/9/2011,6/14/2011,Standard Class,BH-11710,Consumer,United States,Los Angeles,California,90032,West,OFF-AR-10002833,Office Supplies,Art,7.28,4,0.0,1.9656
|
||||
8,CA-2011-115812,6/9/2011,6/14/2011,Standard Class,BH-11710,Consumer,United States,Los Angeles,California,90032,West,TEC-PH-10002275,Technology,Phones,907.152,6,0.2,90.7152
|
||||
9,CA-2011-115812,6/9/2011,6/14/2011,Standard Class,BH-11710,Consumer,United States,Los Angeles,California,90032,West,OFF-BI-10003910,Office Supplies,Binders,18.504,3,0.2,5.7825
|
||||
10,CA-2011-115812,6/9/2011,6/14/2011,Standard Class,BH-11710,Consumer,United States,Los Angeles,California,90032,West,OFF-AP-10002892,Office Supplies,Appliances,114.9,5,0.0,34.47
|
||||
11,CA-2011-115812,6/9/2011,6/14/2011,Standard Class,BH-11710,Consumer,United States,Los Angeles,California,90032,West,FUR-TA-10001539,Furniture,Tables,1706.184,9,0.2,85.3092
|
||||
12,CA-2011-115812,6/9/2011,6/14/2011,Standard Class,BH-11710,Consumer,United States,Los Angeles,California,90032,West,TEC-PH-10002033,Technology,Phones,911.424,4,0.2,68.3568
|
||||
13,CA-2014-114412,4/16/2014,4/21/2014,Standard Class,AA-10480,Consumer,United States,Concord,North Carolina,28027,South,OFF-PA-10002365,Office Supplies,Paper,15.552,3,0.2,5.4432
|
||||
14,CA-2013-161389,12/6/2013,12/11/2013,Standard Class,IM-15070,Consumer,United States,Seattle,Washington,98103,West,OFF-BI-10003656,Office Supplies,Binders,407.976,3,0.2,132.5922
|
||||
15,US-2012-118983,11/22/2012,11/26/2012,Standard Class,HP-14815,Home Office,United States,Fort Worth,Texas,76106,Central,OFF-AP-10002311,Office Supplies,Appliances,68.81,5,0.8,-123.858
|
||||
16,US-2012-118983,11/22/2012,11/26/2012,Standard Class,HP-14815,Home Office,United States,Fort Worth,Texas,76106,Central,OFF-BI-10000756,Office Supplies,Binders,2.544,3,0.8,-3.816
|
||||
17,CA-2011-105893,11/11/2011,11/18/2011,Standard Class,PK-19075,Consumer,United States,Madison,Wisconsin,53711,Central,OFF-ST-10004186,Office Supplies,Storage,665.88,6,0.0,13.3176
|
||||
18,CA-2011-167164,5/13/2011,5/15/2011,Second Class,AG-10270,Consumer,United States,West Jordan,Utah,84084,West,OFF-ST-10000107,Office Supplies,Storage,55.5,2,0.0,9.99
|
||||
19,CA-2011-143336,8/27/2011,9/1/2011,Second Class,ZD-21925,Consumer,United States,San Francisco,California,94109,West,OFF-AR-10003056,Office Supplies,Art,8.56,2,0.0,2.4824
|
||||
20,CA-2011-143336,8/27/2011,9/1/2011,Second Class,ZD-21925,Consumer,United States,San Francisco,California,94109,West,TEC-PH-10001949,Technology,Phones,213.48,3,0.2,16.011
|
||||
21,CA-2011-143336,8/27/2011,9/1/2011,Second Class,ZD-21925,Consumer,United States,San Francisco,California,94109,West,OFF-BI-10002215,Office Supplies,Binders,22.72,4,0.2,7.384
|
||||
22,CA-2013-137330,12/10/2013,12/14/2013,Standard Class,KB-16585,Corporate,United States,Fremont,Nebraska,68025,Central,OFF-AR-10000246,Office Supplies,Art,19.46,7,0.0,5.0596
|
||||
23,CA-2013-137330,12/10/2013,12/14/2013,Standard Class,KB-16585,Corporate,United States,Fremont,Nebraska,68025,Central,OFF-AP-10001492,Office Supplies,Appliances,60.34,7,0.0,15.6884
|
||||
24,US-2014-156909,7/17/2014,7/19/2014,Second Class,SF-20065,Consumer,United States,Philadelphia,Pennsylvania,19140,East,FUR-CH-10002774,Furniture,Chairs,71.372,2,0.3,-1.0196
|
||||
25,CA-2012-106320,9/25/2012,9/30/2012,Standard Class,EB-13870,Consumer,United States,Orem,Utah,84057,West,FUR-TA-10000577,Furniture,Tables,1044.63,3,0.0,240.2649
|
||||
26,CA-2013-121755,1/16/2013,1/20/2013,Second Class,EH-13945,Consumer,United States,Los Angeles,California,90049,West,OFF-BI-10001634,Office Supplies,Binders,11.648,2,0.2,4.2224
|
||||
27,CA-2013-121755,1/16/2013,1/20/2013,Second Class,EH-13945,Consumer,United States,Los Angeles,California,90049,West,TEC-AC-10003027,Technology,Accessories,90.57,3,0.0,11.7741
|
||||
28,US-2012-150630,9/17/2012,9/21/2012,Standard Class,TB-21520,Consumer,United States,Philadelphia,Pennsylvania,19140,East,FUR-BO-10004834,Furniture,Bookcases,3083.43,7,0.5,-1665.0522
|
||||
29,US-2012-150630,9/17/2012,9/21/2012,Standard Class,TB-21520,Consumer,United States,Philadelphia,Pennsylvania,19140,East,OFF-BI-10000474,Office Supplies,Binders,9.618,2,0.7,-7.0532
|
||||
30,US-2012-150630,9/17/2012,9/21/2012,Standard Class,TB-21520,Consumer,United States,Philadelphia,Pennsylvania,19140,East,FUR-FU-10004848,Furniture,Furnishings,124.2,3,0.2,15.525
|
||||
31,US-2012-150630,9/17/2012,9/21/2012,Standard Class,TB-21520,Consumer,United States,Philadelphia,Pennsylvania,19140,East,OFF-EN-10001509,Office Supplies,Envelopes,3.264,2,0.2,1.1016
|
||||
32,US-2012-150630,9/17/2012,9/21/2012,Standard Class,TB-21520,Consumer,United States,Philadelphia,Pennsylvania,19140,East,OFF-AR-10004042,Office Supplies,Art,86.304,6,0.2,9.7092
|
||||
33,US-2012-150630,9/17/2012,9/21/2012,Standard Class,TB-21520,Consumer,United States,Philadelphia,Pennsylvania,19140,East,OFF-BI-10001525,Office Supplies,Binders,6.858,6,0.7,-5.715
|
||||
34,US-2012-150630,9/17/2012,9/21/2012,Standard Class,TB-21520,Consumer,United States,Philadelphia,Pennsylvania,19140,East,OFF-AR-10001683,Office Supplies,Art,15.76,2,0.2,3.546
|
||||
35,CA-2014-107727,10/20/2014,10/24/2014,Second Class,MA-17560,Home Office,United States,Houston,Texas,77095,Central,OFF-PA-10000249,Office Supplies,Paper,29.472,3,0.2,9.9468
|
||||
36,CA-2013-117590,12/9/2013,12/11/2013,First Class,GH-14485,Corporate,United States,Richardson,Texas,75080,Central,TEC-PH-10004977,Technology,Phones,1097.544,7,0.2,123.4737
|
||||
37,CA-2013-117590,12/9/2013,12/11/2013,First Class,GH-14485,Corporate,United States,Richardson,Texas,75080,Central,FUR-FU-10003664,Furniture,Furnishings,190.92,5,0.6,-147.963
|
||||
38,CA-2012-117415,12/27/2012,12/31/2012,Standard Class,SN-20710,Home Office,United States,Houston,Texas,77041,Central,OFF-EN-10002986,Office Supplies,Envelopes,113.328,9,0.2,35.415
|
||||
39,CA-2012-117415,12/27/2012,12/31/2012,Standard Class,SN-20710,Home Office,United States,Houston,Texas,77041,Central,FUR-BO-10002545,Furniture,Bookcases,532.3992,3,0.32,-46.9764
|
||||
40,CA-2012-117415,12/27/2012,12/31/2012,Standard Class,SN-20710,Home Office,United States,Houston,Texas,77041,Central,FUR-CH-10004218,Furniture,Chairs,212.058,3,0.3,-15.147
|
||||
41,CA-2012-117415,12/27/2012,12/31/2012,Standard Class,SN-20710,Home Office,United States,Houston,Texas,77041,Central,TEC-PH-10000486,Technology,Phones,371.168,4,0.2,41.7564
|
||||
42,CA-2014-120999,9/11/2014,9/16/2014,Standard Class,LC-16930,Corporate,United States,Naperville,Illinois,60540,Central,TEC-PH-10004093,Technology,Phones,147.168,4,0.2,16.5564
|
||||
43,CA-2013-101343,7/18/2013,7/23/2013,Standard Class,RA-19885,Corporate,United States,Los Angeles,California,90049,West,OFF-ST-10003479,Office Supplies,Storage,77.88,2,0.0,3.894
|
||||
44,CA-2014-139619,9/20/2014,9/24/2014,Standard Class,ES-14080,Corporate,United States,Melbourne,Florida,32935,South,OFF-ST-10003282,Office Supplies,Storage,95.616,2,0.2,9.5616
|
||||
45,CA-2013-118255,3/12/2013,3/14/2013,First Class,ON-18715,Corporate,United States,Eagan,Minnesota,55122,Central,TEC-AC-10000171,Technology,Accessories,45.98,2,0.0,19.7714
|
||||
46,CA-2013-118255,3/12/2013,3/14/2013,First Class,ON-18715,Corporate,United States,Eagan,Minnesota,55122,Central,OFF-BI-10003291,Office Supplies,Binders,17.46,2,0.0,8.2062
|
||||
47,CA-2011-146703,10/20/2011,10/25/2011,Second Class,PO-18865,Consumer,United States,Westland,Michigan,48185,Central,OFF-ST-10001713,Office Supplies,Storage,211.96,4,0.0,8.4784
|
||||
48,CA-2013-169194,6/21/2013,6/26/2013,Standard Class,LH-16900,Consumer,United States,Dover,Delaware,19901,East,TEC-AC-10002167,Technology,Accessories,45.0,3,0.0,4.95
|
||||
49,CA-2013-169194,6/21/2013,6/26/2013,Standard Class,LH-16900,Consumer,United States,Dover,Delaware,19901,East,TEC-PH-10003988,Technology,Phones,21.8,2,0.0,6.104
|
||||
50,CA-2012-115742,4/18/2012,4/22/2012,Standard Class,DP-13000,Consumer,United States,New Albany,Indiana,47150,Central,OFF-BI-10004410,Office Supplies,Binders,38.22,6,0.0,17.9634
|
||||
51,CA-2012-115742,4/18/2012,4/22/2012,Standard Class,DP-13000,Consumer,United States,New Albany,Indiana,47150,Central,OFF-LA-10002762,Office Supplies,Labels,75.18,6,0.0,35.3346
|
||||
52,CA-2012-115742,4/18/2012,4/22/2012,Standard Class,DP-13000,Consumer,United States,New Albany,Indiana,47150,Central,FUR-FU-10001706,Furniture,Furnishings,6.16,2,0.0,2.9568
|
||||
53,CA-2012-115742,4/18/2012,4/22/2012,Standard Class,DP-13000,Consumer,United States,New Albany,Indiana,47150,Central,FUR-CH-10003061,Furniture,Chairs,89.99,1,0.0,17.0981
|
||||
54,CA-2013-105816,12/12/2013,12/18/2013,Standard Class,JM-15265,Corporate,United States,New York City,New York,10024,East,OFF-FA-10000304,Office Supplies,Fasteners,15.26,7,0.0,6.2566
|
||||
55,CA-2013-105816,12/12/2013,12/18/2013,Standard Class,JM-15265,Corporate,United States,New York City,New York,10024,East,TEC-PH-10002447,Technology,Phones,1029.95,5,0.0,298.6855
|
||||
56,CA-2013-111682,6/18/2013,6/19/2013,First Class,TB-21055,Consumer,United States,Troy,New York,12180,East,OFF-ST-10000604,Office Supplies,Storage,208.56,6,0.0,52.14
|
||||
57,CA-2013-111682,6/18/2013,6/19/2013,First Class,TB-21055,Consumer,United States,Troy,New York,12180,East,OFF-PA-10001569,Office Supplies,Paper,32.4,5,0.0,15.552
|
||||
58,CA-2013-111682,6/18/2013,6/19/2013,First Class,TB-21055,Consumer,United States,Troy,New York,12180,East,FUR-CH-10003968,Furniture,Chairs,319.41,5,0.1,7.098
|
||||
59,CA-2013-111682,6/18/2013,6/19/2013,First Class,TB-21055,Consumer,United States,Troy,New York,12180,East,OFF-PA-10000587,Office Supplies,Paper,14.56,2,0.0,6.9888
|
||||
60,CA-2013-111682,6/18/2013,6/19/2013,First Class,TB-21055,Consumer,United States,Troy,New York,12180,East,TEC-AC-10002167,Technology,Accessories,30.0,2,0.0,3.3
|
||||
61,CA-2013-111682,6/18/2013,6/19/2013,First Class,TB-21055,Consumer,United States,Troy,New York,12180,East,OFF-BI-10001460,Office Supplies,Binders,48.48,4,0.2,16.362
|
||||
62,CA-2013-111682,6/18/2013,6/19/2013,First Class,TB-21055,Consumer,United States,Troy,New York,12180,East,OFF-AR-10001868,Office Supplies,Art,1.68,1,0.0,0.84
|
||||
63,CA-2012-135545,11/24/2012,11/30/2012,Standard Class,KM-16720,Consumer,United States,Los Angeles,California,90004,West,TEC-AC-10004633,Technology,Accessories,13.98,2,0.0,6.1512
|
||||
64,CA-2012-135545,11/24/2012,11/30/2012,Standard Class,KM-16720,Consumer,United States,Los Angeles,California,90004,West,OFF-BI-10001078,Office Supplies,Binders,25.824,6,0.2,9.3612
|
||||
65,CA-2012-135545,11/24/2012,11/30/2012,Standard Class,KM-16720,Consumer,United States,Los Angeles,California,90004,West,OFF-PA-10003892,Office Supplies,Paper,146.73,3,0.0,68.9631
|
||||
66,CA-2012-135545,11/24/2012,11/30/2012,Standard Class,KM-16720,Consumer,United States,Los Angeles,California,90004,West,FUR-FU-10000397,Furniture,Furnishings,79.76,4,0.0,22.3328
|
||||
67,US-2012-164175,4/30/2012,5/5/2012,Standard Class,PS-18970,Home Office,United States,Chicago,Illinois,60610,Central,FUR-CH-10001146,Furniture,Chairs,213.115,5,0.3,-15.2225
|
||||
68,CA-2011-106376,12/5/2011,12/10/2011,Standard Class,BS-11590,Corporate,United States,Gilbert,Arizona,85234,West,OFF-AR-10002671,Office Supplies,Art,1113.024,8,0.2,111.3024
|
||||
69,CA-2011-106376,12/5/2011,12/10/2011,Standard Class,BS-11590,Corporate,United States,Gilbert,Arizona,85234,West,TEC-PH-10002726,Technology,Phones,167.968,4,0.2,62.988
|
||||
70,CA-2013-119823,6/5/2013,6/7/2013,First Class,KD-16270,Consumer,United States,Springfield,Virginia,22153,South,OFF-PA-10000482,Office Supplies,Paper,75.88,2,0.0,35.6636
|
||||
71,CA-2013-106075,9/19/2013,9/24/2013,Standard Class,HM-14980,Consumer,United States,New York City,New York,10009,East,OFF-BI-10004654,Office Supplies,Binders,4.616,1,0.2,1.731
|
||||
72,CA-2014-114440,9/15/2014,9/18/2014,Second Class,TB-21520,Consumer,United States,Jackson,Michigan,49201,Central,OFF-PA-10004675,Office Supplies,Paper,19.05,3,0.0,8.763
|
||||
73,US-2012-134026,4/26/2012,5/2/2012,Standard Class,JE-15745,Consumer,United States,Memphis,Tennessee,38109,South,FUR-CH-10000513,Furniture,Chairs,831.936,8,0.2,-114.3912
|
||||
74,US-2012-134026,4/26/2012,5/2/2012,Standard Class,JE-15745,Consumer,United States,Memphis,Tennessee,38109,South,FUR-FU-10003708,Furniture,Furnishings,97.04,2,0.2,1.213
|
||||
75,US-2012-134026,4/26/2012,5/2/2012,Standard Class,JE-15745,Consumer,United States,Memphis,Tennessee,38109,South,OFF-ST-10004123,Office Supplies,Storage,72.784,1,0.2,-18.196
|
||||
76,US-2014-118038,12/10/2014,12/12/2014,First Class,KB-16600,Corporate,United States,Houston,Texas,77041,Central,OFF-BI-10004182,Office Supplies,Binders,1.248,3,0.8,-1.9344
|
||||
77,US-2014-118038,12/10/2014,12/12/2014,First Class,KB-16600,Corporate,United States,Houston,Texas,77041,Central,FUR-FU-10000260,Furniture,Furnishings,9.708,3,0.6,-5.8248
|
||||
78,US-2014-118038,12/10/2014,12/12/2014,First Class,KB-16600,Corporate,United States,Houston,Texas,77041,Central,OFF-ST-10000615,Office Supplies,Storage,27.24,3,0.2,2.724
|
||||
79,US-2011-147606,11/26/2011,12/1/2011,Second Class,JE-15745,Consumer,United States,Houston,Texas,77070,Central,FUR-FU-10003194,Furniture,Furnishings,19.3,5,0.6,-14.475
|
||||
80,CA-2013-127208,6/13/2013,6/16/2013,First Class,SC-20770,Corporate,United States,Decatur,Alabama,35601,South,OFF-AP-10002118,Office Supplies,Appliances,208.16,1,0.0,56.2032
|
||||
81,CA-2013-127208,6/13/2013,6/16/2013,First Class,SC-20770,Corporate,United States,Decatur,Alabama,35601,South,OFF-BI-10002309,Office Supplies,Binders,16.74,3,0.0,8.0352
|
||||
82,CA-2011-139451,10/12/2011,10/16/2011,Standard Class,DN-13690,Consumer,United States,San Francisco,California,94122,West,OFF-AR-10002053,Office Supplies,Art,14.9,5,0.0,4.172
|
||||
83,CA-2011-139451,10/12/2011,10/16/2011,Standard Class,DN-13690,Consumer,United States,San Francisco,California,94122,West,OFF-ST-10002370,Office Supplies,Storage,21.39,1,0.0,6.2031
|
||||
84,CA-2012-149734,9/3/2012,9/8/2012,Standard Class,JC-16105,Corporate,United States,Durham,North Carolina,27707,South,OFF-EN-10000927,Office Supplies,Envelopes,200.984,7,0.2,62.8075
|
||||
85,US-2014-119662,11/14/2014,11/17/2014,First Class,CS-12400,Home Office,United States,Chicago,Illinois,60623,Central,OFF-ST-10003656,Office Supplies,Storage,230.376,3,0.2,-48.9549
|
||||
86,CA-2014-140088,5/29/2014,5/31/2014,Second Class,PO-18865,Consumer,United States,Columbia,South Carolina,29203,South,FUR-CH-10000863,Furniture,Chairs,301.96,2,0.0,33.2156
|
||||
87,CA-2014-155558,10/27/2014,11/3/2014,Standard Class,PG-18895,Consumer,United States,Rochester,Minnesota,55901,Central,TEC-AC-10001998,Technology,Accessories,19.99,1,0.0,6.7966
|
||||
88,CA-2014-155558,10/27/2014,11/3/2014,Standard Class,PG-18895,Consumer,United States,Rochester,Minnesota,55901,Central,OFF-LA-10000134,Office Supplies,Labels,6.16,2,0.0,2.9568
|
||||
89,CA-2013-159695,4/6/2013,4/11/2013,Second Class,GM-14455,Home Office,United States,Houston,Texas,77095,Central,OFF-ST-10003442,Office Supplies,Storage,158.368,7,0.2,13.8572
|
||||
90,CA-2013-109806,9/18/2013,9/23/2013,Standard Class,JS-15685,Corporate,United States,Los Angeles,California,90036,West,OFF-AR-10004930,Office Supplies,Art,20.1,3,0.0,6.633
|
||||
91,CA-2013-109806,9/18/2013,9/23/2013,Standard Class,JS-15685,Corporate,United States,Los Angeles,California,90036,West,TEC-PH-10004093,Technology,Phones,73.584,2,0.2,8.2782
|
||||
92,CA-2013-109806,9/18/2013,9/23/2013,Standard Class,JS-15685,Corporate,United States,Los Angeles,California,90036,West,OFF-PA-10000304,Office Supplies,Paper,6.48,1,0.0,3.1104
|
||||
93,CA-2012-149587,1/31/2012,2/5/2012,Second Class,KB-16315,Consumer,United States,Minneapolis,Minnesota,55407,Central,OFF-PA-10003177,Office Supplies,Paper,12.96,2,0.0,6.2208
|
||||
94,CA-2012-149587,1/31/2012,2/5/2012,Second Class,KB-16315,Consumer,United States,Minneapolis,Minnesota,55407,Central,FUR-FU-10003799,Furniture,Furnishings,53.34,3,0.0,16.5354
|
||||
95,CA-2012-149587,1/31/2012,2/5/2012,Second Class,KB-16315,Consumer,United States,Minneapolis,Minnesota,55407,Central,OFF-BI-10002852,Office Supplies,Binders,32.96,2,0.0,16.1504
|
||||
96,US-2014-109484,11/7/2014,11/13/2014,Standard Class,RB-19705,Home Office,United States,Portland,Oregon,97206,West,OFF-BI-10004738,Office Supplies,Binders,5.682,1,0.7,-3.788
|
||||
97,CA-2014-161018,11/10/2014,11/12/2014,Second Class,PN-18775,Home Office,United States,New York City,New York,10009,East,FUR-FU-10000629,Furniture,Furnishings,96.53,7,0.0,40.5426
|
||||
98,CA-2014-157833,6/18/2014,6/21/2014,First Class,KD-16345,Consumer,United States,San Francisco,California,94122,West,OFF-BI-10001721,Office Supplies,Binders,51.312,3,0.2,17.9592
|
||||
99,CA-2013-149223,9/7/2013,9/12/2013,Standard Class,ER-13855,Corporate,United States,Saint Paul,Minnesota,55106,Central,OFF-AP-10000358,Office Supplies,Appliances,77.88,6,0.0,22.5852
|
||||
|
@@ -0,0 +1,136 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 "@perspective-dev/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/tools/test/src/html/workspace-test.html");
|
||||
await page.evaluate(async () => {
|
||||
while (!window["__TEST_PERSPECTIVE_READY__"]) {
|
||||
await new Promise((x) => setTimeout(x, 10));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Context menu", () => {
|
||||
test("shows tables in the New Table submenu", async ({ page }) => {
|
||||
await page.evaluate(async () => {
|
||||
await window.__WORKER__.table("x\n1\n2\n", {
|
||||
name: "test_table_1",
|
||||
});
|
||||
|
||||
await window.__WORKER__.table("y\n3\n4\n", {
|
||||
name: "test_table_2",
|
||||
});
|
||||
});
|
||||
|
||||
// Right-click on the workspace to open context menu
|
||||
const workspace = page.locator("perspective-workspace");
|
||||
await workspace.click({
|
||||
button: "right",
|
||||
position: { x: 100, y: 100 },
|
||||
});
|
||||
|
||||
// Wait for the context menu to appear in the shadow DOM
|
||||
const shadowHost = page.locator("perspective-workspace");
|
||||
const menu = shadowHost.locator(".lm-Menu").first();
|
||||
await expect(menu).toBeVisible();
|
||||
|
||||
// Click on "New Table" to open the submenu
|
||||
const newTableItem = menu.locator(
|
||||
".lm-Menu-item:has(.lm-Menu-itemLabel:text('New Table'))",
|
||||
);
|
||||
await newTableItem.hover();
|
||||
|
||||
// Wait for submenu to appear
|
||||
const submenu = shadowHost.locator(".lm-Menu").nth(1);
|
||||
await expect(submenu).toBeVisible();
|
||||
|
||||
// Get the submenu content with table entries
|
||||
const submenuContent = submenu.locator(".lm-Menu-content");
|
||||
|
||||
// Wait for the table items to be populated (they are added asynchronously)
|
||||
// We expect at least our two test tables plus the default "superstore" table
|
||||
await expect(submenuContent.locator("> .lm-Menu-item")).toHaveCount(3, {
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
const menuItems = submenuContent.locator("> .lm-Menu-item");
|
||||
const itemCount = await menuItems.count();
|
||||
|
||||
// Get the labels of the first items (the tables)
|
||||
const labels = [];
|
||||
for (let i = 0; i < itemCount; i++) {
|
||||
const label = await menuItems
|
||||
.nth(i)
|
||||
.locator(".lm-Menu-itemLabel")
|
||||
.textContent();
|
||||
labels.push(label);
|
||||
}
|
||||
|
||||
// Verify our test tables appear in the menu
|
||||
expect(labels).toContain("test_table_1");
|
||||
expect(labels).toContain("test_table_2");
|
||||
expect(labels).toContain("superstore");
|
||||
});
|
||||
|
||||
test("context menu table entries have correct structure", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Create two tables and load the workspace
|
||||
await page.evaluate(async () => {
|
||||
await window.__WORKER__.table("a\n1\n", { name: "alpha" });
|
||||
await window.__WORKER__.table("b\n2\n", { name: "beta" });
|
||||
});
|
||||
|
||||
// Right-click on the workspace
|
||||
const workspace = page.locator("perspective-workspace");
|
||||
await workspace.click({
|
||||
button: "right",
|
||||
position: { x: 100, y: 100 },
|
||||
});
|
||||
|
||||
// Open the "New Table" submenu
|
||||
const shadowHost = page.locator("perspective-workspace");
|
||||
const menu = shadowHost.locator(".lm-Menu").first();
|
||||
await expect(menu).toBeVisible();
|
||||
|
||||
const newTableItem = menu.locator(
|
||||
".lm-Menu-item:has(.lm-Menu-itemLabel:text('New Table'))",
|
||||
);
|
||||
await newTableItem.hover();
|
||||
|
||||
const submenu = shadowHost.locator(".lm-Menu").nth(1);
|
||||
await expect(submenu).toBeVisible();
|
||||
|
||||
// Verify the DOM structure of table entries
|
||||
const submenuContent = submenu.locator(".lm-Menu-content");
|
||||
|
||||
// Wait for table items to be populated
|
||||
await expect(submenuContent.locator("> .lm-Menu-item")).toHaveCount(3, {
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
const firstItem = submenuContent.locator("> .lm-Menu-item").first();
|
||||
|
||||
// Check that the menu item has the expected Lumino classes
|
||||
await expect(firstItem).toHaveClass(/lm-Menu-item/);
|
||||
|
||||
// Check that it contains a label element
|
||||
const labelElement = firstItem.locator(".lm-Menu-itemLabel");
|
||||
await expect(labelElement).toBeVisible();
|
||||
|
||||
// Verify the label contains one of our table names
|
||||
const labelText = await labelElement.textContent();
|
||||
expect(["alpha", "beta", "superstore"]).toContain(labelText);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 } from "@perspective-dev/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/tools/test/src/html/workspace-test.html");
|
||||
await page.evaluate(async () => {
|
||||
while (!window["__TEST_PERSPECTIVE_READY__"]) {
|
||||
await new Promise((x) => setTimeout(x, 10));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Workspace table functions", () => {
|
||||
test("flush() waits until delete is resolved", async ({ page }) => {
|
||||
const config = {
|
||||
viewers: {
|
||||
One: { table: "superstore", name: "One" },
|
||||
},
|
||||
detail: {
|
||||
main: {
|
||||
currentIndex: 0,
|
||||
type: "tab-area",
|
||||
widgets: ["One"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
const table = await window.__WORKER__.open_table("superstore");
|
||||
await workspace.restore(config);
|
||||
const viewer_removed =
|
||||
workspace.querySelector("perspective-viewer");
|
||||
|
||||
workspace.removeChild(viewer_removed);
|
||||
await viewer_removed.flush();
|
||||
await table.delete();
|
||||
}, config);
|
||||
});
|
||||
|
||||
test("flush() waits until restore is applied and delete is resolved", async ({
|
||||
page,
|
||||
}) => {
|
||||
const config = {
|
||||
viewers: {
|
||||
One: { table: "superstore", name: "One" },
|
||||
},
|
||||
detail: {
|
||||
main: {
|
||||
currentIndex: 0,
|
||||
type: "tab-area",
|
||||
widgets: ["One"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
const table = await window.__WORKER__.open_table("superstore");
|
||||
workspace.restore(config); // no await
|
||||
await workspace.flush();
|
||||
const viewer_removed =
|
||||
workspace.querySelector("perspective-viewer");
|
||||
|
||||
workspace.removeChild(viewer_removed);
|
||||
await viewer_removed.flush();
|
||||
await table.delete();
|
||||
}, config);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 } from "@perspective-dev/test";
|
||||
import {
|
||||
compareLightDOMContents,
|
||||
compareShadowDOMContents,
|
||||
} from "@perspective-dev/test";
|
||||
|
||||
async function setupTestWorkspace(page) {
|
||||
await page.goto("/tools/test/src/html/workspace-test.html");
|
||||
await page.evaluate(async () => {
|
||||
while (!window["__TEST_PERSPECTIVE_READY__"]) {
|
||||
await new Promise((x) => setTimeout(x, 10));
|
||||
}
|
||||
});
|
||||
|
||||
await page.evaluate(async () => {
|
||||
const viewer = document.createElement("perspective-viewer");
|
||||
viewer.setAttribute("table", "superstore");
|
||||
viewer.setAttribute("name", "one");
|
||||
viewer.setAttribute("slot", "one");
|
||||
const viewer2 = document.createElement("perspective-viewer");
|
||||
viewer2.setAttribute("table", "superstore");
|
||||
viewer2.setAttribute("name", "two");
|
||||
viewer2.setAttribute("slot", "two");
|
||||
const workspace = document.getElementById("workspace");
|
||||
workspace.appendChild(viewer);
|
||||
workspace.appendChild(viewer2);
|
||||
await workspace.flush();
|
||||
});
|
||||
|
||||
await page.evaluate(async () => {
|
||||
const viewer = document.body.querySelector(
|
||||
'perspective-viewer[name="one"]',
|
||||
);
|
||||
const workspace = document.getElementById("workspace");
|
||||
workspace.removeChild(viewer);
|
||||
await workspace.flush();
|
||||
});
|
||||
}
|
||||
|
||||
test.describe("Workspace DOM", () => {
|
||||
test.describe("Light DOM", () => {
|
||||
test.describe("removeChild", () => {
|
||||
test("Remove One", async ({ page }) => {
|
||||
await setupTestWorkspace(page);
|
||||
|
||||
await compareLightDOMContents(
|
||||
page,
|
||||
"workspace-light-remove-one-child.txt",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Shadow DOM", () => {
|
||||
test.describe("removeChild", () => {
|
||||
test("Remove One", async ({ page }) => {
|
||||
await setupTestWorkspace(page);
|
||||
|
||||
await compareShadowDOMContents(
|
||||
page,
|
||||
"workspace-dark-remove-one-child.txt",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,472 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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/test";
|
||||
import {
|
||||
compareLightDOMContents,
|
||||
compareShadowDOMContents,
|
||||
} from "@perspective-dev/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/tools/test/src/html/workspace-test.html");
|
||||
await page.evaluate(async () => {
|
||||
while (!window["__TEST_PERSPECTIVE_READY__"]) {
|
||||
await new Promise((x) => setTimeout(x, 10));
|
||||
}
|
||||
});
|
||||
await page.evaluate(async () => {
|
||||
const { PerspectiveSelectDetail } = await import(
|
||||
"/node_modules/@perspective-dev/viewer/dist/cdn/perspective-viewer.js"
|
||||
);
|
||||
window.PerspectiveSelectDetail = PerspectiveSelectDetail;
|
||||
});
|
||||
});
|
||||
|
||||
function tests(context, compare) {
|
||||
// TODO: Implement this correctly
|
||||
test.skip("treemap filters work", async ({ page }) => {
|
||||
const config = {
|
||||
viewers: {
|
||||
One: {
|
||||
table: "superstore",
|
||||
name: "Test",
|
||||
group_by: ["State"],
|
||||
columns: ["Sales"],
|
||||
plugin: "Treemap",
|
||||
},
|
||||
Two: { table: "superstore", name: "One" },
|
||||
},
|
||||
master: {
|
||||
widgets: ["One"],
|
||||
},
|
||||
detail: {
|
||||
main: {
|
||||
currentIndex: 0,
|
||||
type: "tab-area",
|
||||
widgets: ["Two"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const cfg = await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.restore(config);
|
||||
await workspace.flush();
|
||||
const timer = Promise.withResolvers();
|
||||
workspace.addEventListener("workspace-layout-update", (x) => {
|
||||
timer.resolve();
|
||||
});
|
||||
|
||||
document
|
||||
.querySelector("perspective-viewer-charts-treemap")
|
||||
.shadowRoot.querySelector("g.treemap > g")
|
||||
.dispatchEvent(new Event("click"));
|
||||
|
||||
await timer.promise;
|
||||
return await workspace.save();
|
||||
}, config);
|
||||
|
||||
expect(cfg.viewers.Two.filter).toEqual([["State", "==", "Alabama"]]);
|
||||
return compare(page, `${context}-treemap-filters-work.txt`);
|
||||
});
|
||||
|
||||
test("Datagrid filters work", async ({ page }) => {
|
||||
const config = {
|
||||
viewers: {
|
||||
One: {
|
||||
table: "superstore",
|
||||
name: "Test",
|
||||
group_by: ["State"],
|
||||
columns: ["Sales"],
|
||||
plugin: "Datagrid",
|
||||
},
|
||||
Two: { table: "superstore", name: "One" },
|
||||
},
|
||||
master: {
|
||||
widgets: ["One"],
|
||||
},
|
||||
detail: {
|
||||
main: {
|
||||
currentIndex: 0,
|
||||
type: "tab-area",
|
||||
widgets: ["Two"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async function awaitConfigChange() {
|
||||
return await page.evaluate(async () => {
|
||||
let resolve;
|
||||
const timer = new Promise((x) => {
|
||||
resolve = x;
|
||||
});
|
||||
|
||||
workspace.addEventListener("workspace-layout-update", resolve);
|
||||
await timer;
|
||||
workspace.removeEventListener(
|
||||
"workspace-layout-update",
|
||||
resolve,
|
||||
);
|
||||
|
||||
return await workspace.save();
|
||||
});
|
||||
}
|
||||
|
||||
await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.restore(config);
|
||||
await workspace.flush();
|
||||
}, config);
|
||||
|
||||
let cfgPromise = awaitConfigChange();
|
||||
await page
|
||||
.locator(".workspace-master-widget perspective-viewer-datagrid")
|
||||
.locator("tbody tr:nth-child(6) th:last-of-type")
|
||||
.click();
|
||||
|
||||
let cfg = await cfgPromise;
|
||||
expect(cfg.viewers.Two.filter).toEqual([["State", "==", "Colorado"]]);
|
||||
|
||||
cfgPromise = awaitConfigChange();
|
||||
await page
|
||||
.locator(".workspace-master-widget perspective-viewer-datagrid")
|
||||
.locator("tbody tr:nth-child(6) th:last-of-type")
|
||||
.click();
|
||||
|
||||
cfg = await cfgPromise;
|
||||
expect(cfg.viewers.Two.filter).toEqual([]);
|
||||
|
||||
return compare(page, `${context}-datagrid-filters-work.txt`);
|
||||
});
|
||||
|
||||
test("removeConfigs removes a programmatically applied filter from slave viewers", async ({
|
||||
page,
|
||||
}) => {
|
||||
const config = {
|
||||
viewers: {
|
||||
One: {
|
||||
table: "superstore",
|
||||
name: "Test",
|
||||
group_by: ["State"],
|
||||
columns: ["Sales"],
|
||||
plugin: "Datagrid",
|
||||
},
|
||||
Two: { table: "superstore", name: "One" },
|
||||
},
|
||||
master: {
|
||||
widgets: ["One"],
|
||||
},
|
||||
detail: {
|
||||
main: {
|
||||
currentIndex: 0,
|
||||
type: "tab-area",
|
||||
widgets: ["Two"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async function awaitConfigChange() {
|
||||
return await page.evaluate(async () => {
|
||||
let resolve;
|
||||
const timer = new Promise((x) => {
|
||||
resolve = x;
|
||||
});
|
||||
|
||||
workspace.addEventListener("workspace-layout-update", resolve);
|
||||
await timer;
|
||||
workspace.removeEventListener(
|
||||
"workspace-layout-update",
|
||||
resolve,
|
||||
);
|
||||
|
||||
return await workspace.save();
|
||||
});
|
||||
}
|
||||
|
||||
await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.restore(config);
|
||||
await workspace.flush();
|
||||
}, config);
|
||||
|
||||
// Apply a filter for "Category" via programmatic dispatch.
|
||||
// "Category" is not in the master's group_by/split_by/filter, so it
|
||||
// would not be cleared by the candidates mechanism on deselect.
|
||||
let cfgPromise = awaitConfigChange();
|
||||
await page.evaluate(async () => {
|
||||
const masterViewer = document.querySelector(
|
||||
".workspace-master-widget",
|
||||
);
|
||||
masterViewer.dispatchEvent(
|
||||
new CustomEvent("perspective-global-filter", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: new PerspectiveSelectDetail(
|
||||
true,
|
||||
{},
|
||||
["Category"],
|
||||
[],
|
||||
[{ filter: [["Category", "==", "Furniture"]] }],
|
||||
),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
let cfg = await cfgPromise;
|
||||
expect(cfg.viewers.Two.filter).toEqual([
|
||||
["Category", "==", "Furniture"],
|
||||
]);
|
||||
|
||||
// Use removeConfigs to explicitly clear the Category filter from
|
||||
// slave viewers.
|
||||
cfgPromise = awaitConfigChange();
|
||||
await page.evaluate(async () => {
|
||||
const masterViewer = document.querySelector(
|
||||
".workspace-master-widget",
|
||||
);
|
||||
masterViewer.dispatchEvent(
|
||||
new CustomEvent("perspective-global-filter", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: new PerspectiveSelectDetail(
|
||||
true,
|
||||
{},
|
||||
[],
|
||||
[{ filter: [["Category", "==", "Furniture"]] }],
|
||||
[],
|
||||
),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
cfg = await cfgPromise;
|
||||
expect(cfg.viewers.Two.filter).toEqual([]);
|
||||
});
|
||||
|
||||
test("removeConfigs preserves other slave filters while clearing targeted column", async ({
|
||||
page,
|
||||
}) => {
|
||||
const config = {
|
||||
viewers: {
|
||||
One: {
|
||||
table: "superstore",
|
||||
name: "Test",
|
||||
group_by: ["State"],
|
||||
columns: ["Sales"],
|
||||
plugin: "Datagrid",
|
||||
},
|
||||
Two: { table: "superstore", name: "One" },
|
||||
},
|
||||
master: {
|
||||
widgets: ["One"],
|
||||
},
|
||||
detail: {
|
||||
main: {
|
||||
currentIndex: 0,
|
||||
type: "tab-area",
|
||||
widgets: ["Two"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async function awaitConfigChange() {
|
||||
return await page.evaluate(async () => {
|
||||
let resolve;
|
||||
const timer = new Promise((x) => {
|
||||
resolve = x;
|
||||
});
|
||||
|
||||
workspace.addEventListener("workspace-layout-update", resolve);
|
||||
await timer;
|
||||
workspace.removeEventListener(
|
||||
"workspace-layout-update",
|
||||
resolve,
|
||||
);
|
||||
|
||||
return await workspace.save();
|
||||
});
|
||||
}
|
||||
|
||||
await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.restore(config);
|
||||
await workspace.flush();
|
||||
}, config);
|
||||
|
||||
// Apply filters for both "Category" and "Segment" via programmatic
|
||||
// dispatch.
|
||||
let cfgPromise = awaitConfigChange();
|
||||
await page.evaluate(async () => {
|
||||
const masterViewer = document.querySelector(
|
||||
".workspace-master-widget",
|
||||
);
|
||||
masterViewer.dispatchEvent(
|
||||
new CustomEvent("perspective-global-filter", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: new PerspectiveSelectDetail(
|
||||
true,
|
||||
{},
|
||||
["Category", "Segment"],
|
||||
[],
|
||||
[
|
||||
{
|
||||
filter: [
|
||||
["Category", "==", "Furniture"],
|
||||
["Segment", "==", "Consumer"],
|
||||
],
|
||||
},
|
||||
],
|
||||
),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
let cfg = await cfgPromise;
|
||||
expect(cfg.viewers.Two.filter).toEqual([
|
||||
["Category", "==", "Furniture"],
|
||||
["Segment", "==", "Consumer"],
|
||||
]);
|
||||
|
||||
// Remove only "Category"; "Segment" filter should be preserved.
|
||||
cfgPromise = awaitConfigChange();
|
||||
await page.evaluate(async () => {
|
||||
const masterViewer = document.querySelector(
|
||||
".workspace-master-widget",
|
||||
);
|
||||
masterViewer.dispatchEvent(
|
||||
new CustomEvent("perspective-global-filter", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: new PerspectiveSelectDetail(
|
||||
true,
|
||||
{},
|
||||
[],
|
||||
[{ filter: [["Category", "==", "Furniture"]] }],
|
||||
[],
|
||||
),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
cfg = await cfgPromise;
|
||||
// Category is cleared, Segment is preserved.
|
||||
expect(cfg.viewers.Two.filter).toEqual([["Segment", "==", "Consumer"]]);
|
||||
});
|
||||
|
||||
test("Child classes of datagrid behave the same way", async ({ page }) => {
|
||||
const config = {
|
||||
viewers: {
|
||||
One: {
|
||||
table: "superstore",
|
||||
name: "Test",
|
||||
group_by: ["State"],
|
||||
columns: ["Sales"],
|
||||
plugin: "My Datagrid",
|
||||
},
|
||||
Two: { table: "superstore", name: "One" },
|
||||
},
|
||||
master: {
|
||||
widgets: ["One"],
|
||||
},
|
||||
detail: {
|
||||
main: {
|
||||
currentIndex: 0,
|
||||
type: "tab-area",
|
||||
widgets: ["Two"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await page.evaluate(async () => {
|
||||
class MyGrid extends customElements.get(
|
||||
"perspective-viewer-datagrid",
|
||||
) {
|
||||
get_static_config() {
|
||||
return {
|
||||
...super.get_static_config(),
|
||||
name: "My Datagrid",
|
||||
};
|
||||
}
|
||||
}
|
||||
customElements.define("my-grid", MyGrid);
|
||||
customElements.get("perspective-viewer").registerPlugin("my-grid");
|
||||
});
|
||||
|
||||
async function awaitConfigChange() {
|
||||
return await page.evaluate(async () => {
|
||||
let resolve;
|
||||
const timer = new Promise((x) => {
|
||||
resolve = x;
|
||||
});
|
||||
|
||||
workspace.addEventListener("workspace-layout-update", resolve);
|
||||
await timer;
|
||||
workspace.removeEventListener(
|
||||
"workspace-layout-update",
|
||||
resolve,
|
||||
);
|
||||
|
||||
return await workspace.save();
|
||||
});
|
||||
}
|
||||
|
||||
await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.restore(config);
|
||||
await workspace.flush();
|
||||
}, config);
|
||||
|
||||
let cfgPromise = awaitConfigChange();
|
||||
await page
|
||||
.locator(".workspace-master-widget my-grid")
|
||||
.locator("tbody tr:nth-child(6) th:last-of-type")
|
||||
|
||||
.click();
|
||||
let cfg = await cfgPromise;
|
||||
|
||||
expect(cfg.viewers.Two.filter).toEqual([["State", "==", "Colorado"]]);
|
||||
|
||||
cfgPromise = awaitConfigChange();
|
||||
await page
|
||||
.locator(".workspace-master-widget my-grid")
|
||||
.locator("tbody tr:nth-child(6) th:last-of-type")
|
||||
.click({
|
||||
delay: 10,
|
||||
});
|
||||
cfg = await cfgPromise;
|
||||
|
||||
// console.log("OLD", cfg.viewers.Two.filter);
|
||||
// await page.evaluate(async () => {
|
||||
// await new Promise((r) => setTimeout(r, 5000));
|
||||
// });
|
||||
// cfg = await page.evaluate(async () => {
|
||||
// const cfg = await workspace.save();
|
||||
// console.log("NEW", JSON.stringify(cfg.viewers.Two.filter));
|
||||
// return cfg;
|
||||
// });
|
||||
|
||||
expect(cfg.viewers.Two.filter).toEqual([]);
|
||||
|
||||
return compare(page, `${context}-my-datagrid-filters-work.txt`);
|
||||
});
|
||||
}
|
||||
|
||||
test.describe("Workspace global filters", () => {
|
||||
test.describe("Light DOM", () => {
|
||||
tests("light-dom", compareLightDOMContents);
|
||||
});
|
||||
|
||||
test.describe("Shadow DOM", () => {
|
||||
tests("shadow-dom", compareShadowDOMContents);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,117 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 } from "@perspective-dev/test";
|
||||
import {
|
||||
compareLightDOMContents,
|
||||
compareShadowDOMContents,
|
||||
} from "@perspective-dev/test";
|
||||
|
||||
async function createOneWorkspace(page) {
|
||||
// await new Promise((x) => setTimeout(x, 2000));
|
||||
await page.evaluate(async () => {
|
||||
document.querySelector("perspective-workspace").innerHTML = `
|
||||
<perspective-viewer table="superstore"></perspective-viewer>
|
||||
`;
|
||||
|
||||
const workspace = document.body.querySelector("perspective-workspace");
|
||||
await workspace.load(window.__WORKER__);
|
||||
await workspace.flush();
|
||||
});
|
||||
}
|
||||
|
||||
async function createMultipleViewers(page) {
|
||||
await page.evaluate(async () => {
|
||||
document.querySelector("perspective-workspace").innerHTML = `
|
||||
<perspective-viewer table="superstore"></perspective-viewer>
|
||||
<perspective-viewer table="superstore"></perspective-viewer>
|
||||
`;
|
||||
|
||||
const workspace = document.body.querySelector("perspective-workspace");
|
||||
await workspace.flush();
|
||||
});
|
||||
}
|
||||
|
||||
async function createMultipleViewersWithNames(page) {
|
||||
await page.evaluate(async () => {
|
||||
document.querySelector("perspective-workspace").innerHTML = `
|
||||
<perspective-viewer name="Table 1" table="superstore"></perspective-viewer>
|
||||
<perspective-viewer name="Table 2" table="superstore"></perspective-viewer>
|
||||
`;
|
||||
|
||||
const workspace = document.body.querySelector("perspective-workspace");
|
||||
await workspace.flush();
|
||||
});
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/tools/test/src/html/workspace-test.html");
|
||||
await page.evaluate(async () => {
|
||||
while (!window["__TEST_PERSPECTIVE_READY__"]) {
|
||||
await new Promise((x) => setTimeout(x, 10));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Workspace HTML", () => {
|
||||
test.describe("Light DOM", () => {
|
||||
test("Create One", async ({ page }) => {
|
||||
await createOneWorkspace(page);
|
||||
await compareLightDOMContents(
|
||||
page,
|
||||
"workspace-html-light-create-one.txt",
|
||||
);
|
||||
});
|
||||
|
||||
test("Create Multiple", async ({ page }) => {
|
||||
await createMultipleViewers(page);
|
||||
await compareLightDOMContents(
|
||||
page,
|
||||
"workspace-html-light-create-multiple.txt",
|
||||
);
|
||||
});
|
||||
|
||||
test("Create Multiple with names", async ({ page }) => {
|
||||
await createMultipleViewersWithNames(page);
|
||||
await compareLightDOMContents(
|
||||
page,
|
||||
"workspace-html-light-create-multiple-with-names.txt",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Shadow DOM", () => {
|
||||
test("Create One", async ({ page }) => {
|
||||
await createOneWorkspace(page);
|
||||
await compareShadowDOMContents(
|
||||
page,
|
||||
"workspace-html-shadow-create-one.txt",
|
||||
);
|
||||
});
|
||||
|
||||
test("Create Multiple", async ({ page }) => {
|
||||
await createMultipleViewers(page);
|
||||
await compareShadowDOMContents(
|
||||
page,
|
||||
"workspace-html-shadow-create-multiple.txt",
|
||||
);
|
||||
});
|
||||
|
||||
test("Create Multiple with names", async ({ page }) => {
|
||||
await createMultipleViewersWithNames(page);
|
||||
await compareShadowDOMContents(
|
||||
page,
|
||||
"workspace-html-shadow-create-multiple-with-names.txt",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,260 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 "@perspective-dev/test";
|
||||
import {
|
||||
compareLightDOMContents,
|
||||
compareShadowDOMContents,
|
||||
} from "@perspective-dev/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/tools/test/src/html/workspace-test.html");
|
||||
await page.evaluate(async () => {
|
||||
while (!window["__TEST_PERSPECTIVE_READY__"]) {
|
||||
await new Promise((x) => setTimeout(x, 10));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function tests(context, compare) {
|
||||
test("restore workspace with detail only", async ({ page }) => {
|
||||
const config = {
|
||||
viewers: {
|
||||
One: { table: "superstore", name: "One" },
|
||||
},
|
||||
detail: {
|
||||
main: {
|
||||
currentIndex: 0,
|
||||
type: "tab-area",
|
||||
widgets: ["One"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.restore(config);
|
||||
}, config);
|
||||
|
||||
await page.evaluate(async () => {
|
||||
await workspace.flush();
|
||||
});
|
||||
|
||||
return compare(
|
||||
page,
|
||||
`${context}-restore-workspace-with-detail-only.txt`,
|
||||
);
|
||||
});
|
||||
|
||||
test("restore workspace with master and detail", async ({ page }) => {
|
||||
const config = {
|
||||
viewers: {
|
||||
One: {
|
||||
table: "superstore",
|
||||
name: "Test",
|
||||
group_by: ["State"],
|
||||
columns: ["Sales", "Profit"],
|
||||
},
|
||||
Two: { table: "superstore", name: "One" },
|
||||
},
|
||||
master: {
|
||||
widgets: ["One"],
|
||||
},
|
||||
detail: {
|
||||
main: {
|
||||
currentIndex: 0,
|
||||
type: "tab-area",
|
||||
widgets: ["Two"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.restore(config);
|
||||
}, config);
|
||||
|
||||
await page.evaluate(async () => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.flush();
|
||||
});
|
||||
|
||||
return compare(
|
||||
page,
|
||||
`${context}-restore-workspace-with-master-and-detail.txt`,
|
||||
);
|
||||
});
|
||||
|
||||
test("restore workspace is symmetric with addViewer", async ({ page }) => {
|
||||
const initial = await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.addViewer({ table: "superstore" });
|
||||
await workspace.flush();
|
||||
const result = await workspace.save();
|
||||
return result;
|
||||
});
|
||||
|
||||
const second = await page.evaluate(async (initial) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.restore(initial);
|
||||
await workspace.flush();
|
||||
return await workspace.save();
|
||||
}, initial);
|
||||
|
||||
expect(initial).toEqual(second);
|
||||
|
||||
return compare(
|
||||
page,
|
||||
`${context}-restore-workspace-is-symmetric-with-addviewer.txt`,
|
||||
);
|
||||
});
|
||||
|
||||
test.describe("Toggle master/detail", () => {
|
||||
test("restore a blank view removes master panel", async ({ page }) => {
|
||||
const config = {
|
||||
detail: {
|
||||
main: {
|
||||
type: "tab-area",
|
||||
widgets: ["PERSPECTIVE_GENERATED_ID_1"],
|
||||
currentIndex: 0,
|
||||
},
|
||||
},
|
||||
master: { widgets: ["PERSPECTIVE_GENERATED_ID_0"], sizes: [1] },
|
||||
viewers: {
|
||||
PERSPECTIVE_GENERATED_ID_0: {
|
||||
table: "superstore",
|
||||
},
|
||||
PERSPECTIVE_GENERATED_ID_1: {
|
||||
table: "superstore",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const empty_config = {
|
||||
detail: {},
|
||||
viewers: {},
|
||||
};
|
||||
|
||||
// const x = await page.evaluate(async (config) => {
|
||||
// const workspace = document.getElementById("workspace");
|
||||
// await workspace.restore(config);
|
||||
// await workspace.flush();
|
||||
// return workspace.outerHTML;
|
||||
// }, config);
|
||||
|
||||
// test.expect(x).toEqual(
|
||||
// `<perspective-workspace id=\"workspace\"><perspective-viewer slot=\"PERSPECTIVE_GENERATED_ID_1\" table=\"superstore\" theme=\"Pro Light\" class=\"lm-DockPanel-widget\"><perspective-viewer-datagrid data-edit-mode=\"READ_ONLY\" style=\"position: absolute; inset: 0px; opacity: 1;\" class=\"edit-mode-allowed\"></perspective-viewer-datagrid><perspective-viewer-datagrid-toolbar slot=\"statusbar-extra\"></perspective-viewer-datagrid-toolbar></perspective-viewer><perspective-viewer slot=\"PERSPECTIVE_GENERATED_ID_0\" table=\"superstore\" theme=\"Pro Light\" class=\"workspace-master-widget lm-SplitPanel-child\" selectable=\"\"><perspective-viewer-datagrid data-edit-mode=\"READ_ONLY\" style=\"position: absolute; inset: 0px; opacity: 1;\"></perspective-viewer-datagrid><perspective-viewer-datagrid-toolbar slot=\"statusbar-extra\"></perspective-viewer-datagrid-toolbar></perspective-viewer></perspective-workspace>`,
|
||||
// );
|
||||
|
||||
await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.restore(config);
|
||||
await workspace.flush();
|
||||
}, empty_config);
|
||||
|
||||
return compare(
|
||||
page,
|
||||
`${context}-restore-a-blank-view-removes-master-panel.txt`,
|
||||
);
|
||||
});
|
||||
|
||||
test("restore a view which reuses a master viewer, removes master panel", async ({
|
||||
page,
|
||||
}) => {
|
||||
const config = {
|
||||
detail: {
|
||||
main: {
|
||||
type: "tab-area",
|
||||
widgets: ["PERSPECTIVE_GENERATED_ID_1"],
|
||||
currentIndex: 0,
|
||||
},
|
||||
},
|
||||
master: { widgets: ["PERSPECTIVE_GENERATED_ID_0"], sizes: [1] },
|
||||
viewers: {
|
||||
PERSPECTIVE_GENERATED_ID_0: {
|
||||
table: "superstore",
|
||||
title: "One",
|
||||
},
|
||||
PERSPECTIVE_GENERATED_ID_1: {
|
||||
table: "superstore",
|
||||
title: "Two",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const config2 = {
|
||||
detail: {
|
||||
main: {
|
||||
type: "split-area",
|
||||
orientation: "horizontal",
|
||||
children: [
|
||||
{
|
||||
type: "tab-area",
|
||||
widgets: ["PERSPECTIVE_GENERATED_ID_0"],
|
||||
currentIndex: 0,
|
||||
},
|
||||
{
|
||||
type: "tab-area",
|
||||
widgets: ["PERSPECTIVE_GENERATED_ID_1"],
|
||||
currentIndex: 0,
|
||||
},
|
||||
],
|
||||
sizes: [0.5, 0.5],
|
||||
},
|
||||
},
|
||||
master: { widgets: [], sizes: [] },
|
||||
viewers: {
|
||||
PERSPECTIVE_GENERATED_ID_0: {
|
||||
table: "superstore",
|
||||
title: "One",
|
||||
},
|
||||
PERSPECTIVE_GENERATED_ID_1: {
|
||||
table: "superstore",
|
||||
title: "Two",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// const x = await page.evaluate(async (config) => {
|
||||
// const workspace = document.getElementById("workspace");
|
||||
// await workspace.restore(config);
|
||||
// await workspace.flush();
|
||||
// return workspace.outerHTML;
|
||||
// }, config);
|
||||
|
||||
// test.expect(x).toEqual(
|
||||
// `<perspective-workspace id="workspace"><perspective-viewer slot="PERSPECTIVE_GENERATED_ID_1" table="superstore" theme="Pro Light" class="lm-DockPanel-widget"><perspective-viewer-datagrid data-edit-mode="READ_ONLY" style="position: absolute; inset: 0px; opacity: 1;" class="edit-mode-allowed"></perspective-viewer-datagrid><perspective-viewer-datagrid-toolbar slot="statusbar-extra"></perspective-viewer-datagrid-toolbar></perspective-viewer><perspective-viewer slot="PERSPECTIVE_GENERATED_ID_0" table="superstore" theme="Pro Light" class="workspace-master-widget lm-SplitPanel-child" selectable=""><perspective-viewer-datagrid data-edit-mode="READ_ONLY" style="position: absolute; inset: 0px; opacity: 1;"></perspective-viewer-datagrid><perspective-viewer-datagrid-toolbar slot="statusbar-extra"></perspective-viewer-datagrid-toolbar></perspective-viewer></perspective-workspace>`,
|
||||
// );
|
||||
|
||||
await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.restore(config);
|
||||
await workspace.flush();
|
||||
}, config2);
|
||||
|
||||
return compare(
|
||||
page,
|
||||
`${context}-restore-which-reseats-master-viewer.txt`,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test.describe("Workspace restore", () => {
|
||||
test.describe("Light DOM", () => {
|
||||
tests("light-dom", compareLightDOMContents);
|
||||
});
|
||||
|
||||
test.describe("Shadow DOM", () => {
|
||||
tests("shadow-dom", compareShadowDOMContents);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,164 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 "@perspective-dev/test";
|
||||
import {
|
||||
compareLightDOMContents,
|
||||
compareShadowDOMContents,
|
||||
} from "@perspective-dev/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/tools/test/src/html/workspace-test.html");
|
||||
await page.evaluate(async () => {
|
||||
while (!window["__TEST_PERSPECTIVE_READY__"]) {
|
||||
await new Promise((x) => setTimeout(x, 10));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function tests(context, compare) {
|
||||
// TODO: 3.x `Client` has no exact `replaceTable`
|
||||
test.skip("replaceTable() frees the `Table` before resolution", async ({
|
||||
page,
|
||||
}) => {
|
||||
const config = {
|
||||
viewers: {
|
||||
One: { table: "superstore", name: "One" },
|
||||
},
|
||||
detail: {
|
||||
main: {
|
||||
currentIndex: 0,
|
||||
type: "tab-area",
|
||||
widgets: ["One"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.restore(config);
|
||||
await workspace.replaceTable(
|
||||
"superstore",
|
||||
window.__WORKER__.table("x\n1"),
|
||||
);
|
||||
await window.__TABLE__.delete();
|
||||
}, config);
|
||||
|
||||
await page.evaluate(async () => {
|
||||
await workspace.flush();
|
||||
});
|
||||
|
||||
return compare(page, `${context}-replace-table-frees-table.txt`);
|
||||
});
|
||||
|
||||
test.skip("replaceTable() works when previous table errored", async ({
|
||||
page,
|
||||
}) => {
|
||||
const config = {
|
||||
viewers: {
|
||||
One: { table: "errored", name: "One" },
|
||||
},
|
||||
detail: {
|
||||
main: {
|
||||
currentIndex: 0,
|
||||
type: "tab-area",
|
||||
widgets: ["One"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = await page.evaluate(async (config) => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.addTable(
|
||||
"errored",
|
||||
new Promise((_, reject) => setTimeout(reject, 50)),
|
||||
);
|
||||
|
||||
try {
|
||||
await workspace.restore(config);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return e.toString();
|
||||
}
|
||||
}, config);
|
||||
|
||||
// NOTE This is the error message we expect when `restore()` is called
|
||||
// without a `Table`, subject to change.
|
||||
expect(result).toEqual(
|
||||
"Error: Failed to construct table from JsValue(undefined)",
|
||||
);
|
||||
await page.evaluate(async () => {
|
||||
await workspace.replaceTable(
|
||||
"errored",
|
||||
window.__WORKER__.table("x\n1"),
|
||||
);
|
||||
});
|
||||
|
||||
await page.evaluate(async () => {
|
||||
await workspace.flush();
|
||||
});
|
||||
|
||||
return compare(
|
||||
page,
|
||||
`${context}-replace-table-works-with-errored-table.txt`,
|
||||
);
|
||||
});
|
||||
|
||||
test("removeTable() smoke test", async ({ page }) => {
|
||||
await page.evaluate(async () => {
|
||||
await window.__WORKER__.table("x\n1\n", { name: "temptable" });
|
||||
const workspace = document.getElementById("workspace");
|
||||
await workspace.load(window.__WORKER__);
|
||||
await workspace.restore({
|
||||
viewers: {
|
||||
One: { table: "temptable", name: "One" },
|
||||
},
|
||||
detail: {
|
||||
main: {
|
||||
currentIndex: 0,
|
||||
type: "tab-area",
|
||||
widgets: ["One"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await workspace.flush();
|
||||
});
|
||||
|
||||
await page.evaluate(async () => {
|
||||
await workspace.restore({
|
||||
viewers: {},
|
||||
detail: {
|
||||
main: {
|
||||
currentIndex: 0,
|
||||
type: "tab-area",
|
||||
widgets: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await workspace.flush();
|
||||
});
|
||||
|
||||
return compare(page, `${context}-table-delete-works.txt`);
|
||||
});
|
||||
}
|
||||
|
||||
test.describe("Workspace table functions", () => {
|
||||
test.describe("Light DOM", () => {
|
||||
tests("light-dom", compareLightDOMContents);
|
||||
});
|
||||
|
||||
test.describe("Shadow DOM", () => {
|
||||
tests("shadow-dom", compareShadowDOMContents);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
||||
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
||||
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
||||
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
||||
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
||||
// ┃ 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 } from "@perspective-dev/test";
|
||||
import {
|
||||
compareLightDOMContents,
|
||||
compareShadowDOMContents,
|
||||
} from "@perspective-dev/test";
|
||||
|
||||
const BAD_LAYOUT = {
|
||||
sizes: [1],
|
||||
detail: {
|
||||
main: {
|
||||
type: "tab-area",
|
||||
widgets: [
|
||||
"PERSPECTIVE_GENERATED_ID_0",
|
||||
"PERSPECTIVE_GENERATED_ID_1",
|
||||
],
|
||||
currentIndex: 1,
|
||||
},
|
||||
},
|
||||
mode: "globalFilters",
|
||||
viewers: {
|
||||
PERSPECTIVE_GENERATED_ID_0: {
|
||||
plugin: "Sunburst",
|
||||
plugin_config: {},
|
||||
settings: false,
|
||||
theme: null,
|
||||
group_by: ["State"],
|
||||
split_by: [],
|
||||
columns: ["Quantity", null, null],
|
||||
filter: [],
|
||||
sort: [],
|
||||
expressions: {},
|
||||
aggregates: {},
|
||||
master: false,
|
||||
name: "one",
|
||||
table: "superstore",
|
||||
linked: false,
|
||||
},
|
||||
PERSPECTIVE_GENERATED_ID_1: {
|
||||
plugin: "Sunburst",
|
||||
plugin_config: {},
|
||||
settings: false,
|
||||
theme: null,
|
||||
group_by: ["State"],
|
||||
split_by: [],
|
||||
columns: ["Sales", null, null],
|
||||
filter: [],
|
||||
sort: [],
|
||||
expressions: {},
|
||||
aggregates: {},
|
||||
master: false,
|
||||
name: "two",
|
||||
table: "superstore",
|
||||
linked: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function tests(context, compare) {
|
||||
test.describe("visibility", () => {
|
||||
test("Sunburst charts do not loop forever when disconnected from DOM", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.evaluate(async (layout) => {
|
||||
await window.workspace.restore(layout);
|
||||
}, BAD_LAYOUT);
|
||||
|
||||
await page.evaluate(async () => {
|
||||
const viewer = document.body.querySelector(
|
||||
"perspective-viewer[slot=PERSPECTIVE_GENERATED_ID_0]",
|
||||
);
|
||||
const workspace = document.getElementById("workspace");
|
||||
workspace.removeChild(viewer);
|
||||
await workspace.flush();
|
||||
});
|
||||
|
||||
return compare(
|
||||
page,
|
||||
`sunburst-charts-does-not-loop-${context}.txt`,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/tools/test/src/html/workspace-test.html");
|
||||
await page.evaluate(async () => {
|
||||
while (!window["__TEST_PERSPECTIVE_READY__"]) {
|
||||
await new Promise((x) => setTimeout(x, 10));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Workspace Visibility", () => {
|
||||
test.describe("Light DOM", () => {
|
||||
tests("light-dom", compareLightDOMContents);
|
||||
});
|
||||
|
||||
test.describe("Shadow DOM", () => {
|
||||
tests("shadow-dom", compareShadowDOMContents);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
"strict": true,
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist/esm",
|
||||
"rootDir": "./src/ts",
|
||||
"moduleResolution": "bundler"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
Reference in New Issue
Block a user