Perspective Workspace - A Custom Element for coordinating multiple perspective-viewer instances with docking, tabbing, filtering, and state management capabilities for interactive analytics dashboards
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Table sharing and coordination system for managing perspective Tables across multiple viewers within the workspace.
Core functionality for adding, retrieving, and managing perspective Tables.
/**
* Table management operations available on both custom element and workspace
*/
interface TableManager {
/** Add a table by name for sharing across multiple viewers */
addTable(name: string, table: Promise<psp.Table>): Promise<void>;
/** Retrieve table by name */
getTable(name: string): psp.Table | Promise<psp.Table>;
/** Replace an existing table with a new one */
replaceTable(name: string, table: Promise<psp.Table>): Promise<void>;
/** Remove table by name from the workspace */
removeTable(name: string): boolean;
/** Observable map of all tables in the workspace */
readonly tables: ObservableMap<string, psp.Table | Promise<psp.Table>>;
}Usage Examples:
import { HTMLPerspectiveWorkspaceElement } from "@finos/perspective-workspace";
import * as perspective from "@finos/perspective";
const workspace = document.createElement("perspective-workspace") as HTMLPerspectiveWorkspaceElement;
// Add tables from different data sources
const salesData = await perspective.table([
{ product: "Widget", sales: 100, region: "North" },
{ product: "Gadget", sales: 200, region: "South" }
]);
const userStats = await perspective.table([
{ user: "Alice", sessions: 45, country: "US" },
{ user: "Bob", sessions: 32, country: "UK" }
]);
// Add tables to workspace
await workspace.addTable("sales", Promise.resolve(salesData));
await workspace.addTable("users", Promise.resolve(userStats));
// Retrieve and use tables
const sales = workspace.getTable("sales");
if (sales) {
console.log("Sales table available");
}
// Replace a table with updated data
const updatedSales = await perspective.table([
{ product: "Widget", sales: 150, region: "North" },
{ product: "Gadget", sales: 250, region: "South" },
{ product: "Tool", sales: 75, region: "East" }
]);
await workspace.replaceTable("sales", Promise.resolve(updatedSales));
// Remove table when no longer needed
const removed = workspace.removeTable("sales");
console.log("Table removed:", removed);Enhanced Map implementation that provides notifications when tables are added or removed.
/**
* Observable Map class extending native Map with listener support
* Provides notifications when tables are set or deleted
*/
class ObservableMap<K, V> extends Map<K, V> {
/** Set value with listener notification */
set(name: K, item: V): this;
/** Get value by key */
get(name: K): V | undefined;
/** Delete value with listener notification */
delete(name: K): boolean;
/** Add listener for set operations */
addSetListener(listener: (name: K, val: V) => void): void;
/** Add listener for delete operations */
addDeleteListener(listener: (name: K) => void): void;
}Usage Examples:
import { ObservableMap } from "@finos/perspective-workspace";
const tableMap = new ObservableMap<string, psp.Table>();
// Listen for table additions
tableMap.addSetListener((name, table) => {
console.log(`Table "${name}" added to workspace`);
});
// Listen for table removals
tableMap.addDeleteListener((name) => {
console.log(`Table "${name}" removed from workspace`);
});
// These operations will trigger listeners
tableMap.set("myTable", table);
tableMap.delete("myTable");Tables added to the workspace can be referenced by name in viewer configurations, enabling multiple viewers to share the same data source efficiently.
Usage Examples:
// Add a shared table
workspace.addTable("stockPrices", stockTable);
// Multiple viewers can reference the same table
workspace.addViewer({
table: "stockPrices",
columns: ["symbol", "price"],
group_by: ["sector"]
});
workspace.addViewer({
table: "stockPrices", // Same table, different view
plugin: "d3_candlestick",
columns: ["open", "high", "low", "close"]
});
workspace.addViewer({
table: "stockPrices", // Same table, aggregated view
plugin: "d3_bar",
columns: ["price"],
group_by: ["sector"],
aggregates: { price: "avg" }
});The system supports both synchronous and asynchronous table loading patterns.
Usage Examples:
// Asynchronous table loading
const loadDataAsync = async () => {
// Table loads in background while workspace is being set up
workspace.addTable("asyncData",
fetch("/api/data")
.then(response => response.json())
.then(data => perspective.table(data))
);
// Viewer can be added immediately, will connect when table loads
workspace.addViewer({
table: "asyncData",
columns: ["name", "value"]
});
};
// Synchronous table loading
const loadDataSync = async () => {
const data = await fetch("/api/data").then(r => r.json());
const table = await perspective.table(data);
workspace.addTable("syncData", Promise.resolve(table));
workspace.addViewer({
table: "syncData",
columns: ["name", "value"]
});
};