@finos/perspective-viewer
Version:
The `<perspective-viewer>` Custom Element, frontend for Perspective.js
280 lines (246 loc) • 10.6 kB
text/typescript
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ 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 { View } from "@finos/perspective";
// import type * as perspective from "@finos/perspective";
/**
* The `IPerspectiveViewerPlugin` interface defines the necessary API for a
* `<perspective-viewer>` plugin, which also must be an `HTMLElement` via the
* Custom Elements API or otherwise. Rather than implement this API from
* scratch however, the simplest way is to inherit from
* `<perspective-viewer-plugin>`, which implements `IPerspectiveViewerPlugin`
* with non-offensive default implementations, where only the `draw()` and
* `name()` methods need be overridden to get started with a simple plugin.
*
* Note that plugins are frozen once a `<perspective-viewer>` has been
* instantiated, so generally new plugin code must be executed at the module
* level (if packaged as a library), or during application init to ensure global
* availability of a plugin.
*
* @example
* ```javascript
* const BasePlugin = customElements.get("perspective-viewer-plugin");
* class MyPlugin extends BasePlugin {
* get name() {
* return "My Plugin";
* }
* async draw(view) {
* const count = await view.num_rows();
* this.innerHTML = `View has ${count} rows`;
* }
* }
*
* customElements.define("my-plugin", MyPlugin);
* const Viewer = customElements.get("perspective-viewer");
* Viewer.registerPlugin("my-plugin");
* ```
* @noInheritDoc
*/
export interface IPerspectiveViewerPlugin {
/**
* The name for this plugin, which is used as both it's unique key for use
* as a parameter for the `plugin` field of a `ViewerConfig`, and as the
* display name for this plugin in the `<perspective-viewer>` UI.
*/
get name(): string;
/**
* Select mode determines how column add/remove buttons behave for this
* plugin. `"select"` mode exclusively selects the added column, removing
* other columns. `"toggle"` mode toggles the column on or off (dependent
* on column state), leaving existing columns alone.
*/
get select_mode(): string | undefined;
/**
* The minimum number of columns required for this plugin to operate.
* This mostly affects drag/drop and column remove button behavior,
* preventing the use from applying configs which violate this min.
*
* While this option can technically be `undefined` (as in the case of
* `@finos/perspective-viewer-datagrid`), doing so currently has nearly
* identical behavior to 1.
*/
get min_config_columns(): number | undefined;
/**
* The named column labels, if desired. Named columns behave differently
* in drag/drop mode than unnamed columns, having replace/swap behavior
* rather than insert. If provided, the length of `config_column_names`
* _must_ be `>= min_config_columns`, as this is assumed by the drag/drop
* logic.
*/
get config_column_names(): string[] | undefined;
/**
* The load priority of the plugin. If the plugin shares priority with another,
* the first to load has a higher priority.
*
* A larger number has a higher priority.
*
* The plugin with the highest priority will be loaded by default by the Perspective viewer.
* If you would like to instead begin with a lower priority plugin, choose it explicitly with
* a `HTMLPerspectiveViewerPluginElement.restore` call.
*/
get priority(): number | undefined;
/**
* Given a column's grouping (determined by indexing it in `plugin.config_column_names`)
* and its view type, determines whether or not to render column styles in the settings sidebar.
* Implementing this function and `column_style_config` allows the plugin to interface with the viewer's column configuration API.
*/
can_render_column_styles?: (view_type: string, group: string) => boolean;
/**
* Determines which column configuration controls are populated in the viewer.
* Corresponds to the data the plugin will recieve on save.
* Implementing this function and `can_render_column_styles` allows the plugin to interface with the viewer's column configuration API.
*/
column_style_config?: (view_type: string, group: string) => any;
/**
* Render this plugin using the provided `View`. While there is no
* provision to cancel a render in progress per se, calling a method on
* a `View` which has been deleted will throw an exception.
*
* @example
* ```
* async draw(view: perspective.View): Promise<void> {
* const csv = await view.to_csv();
* this.innerHTML = `<pre>${csv}</pre>`;
* }
* ```
*/
draw(view: View): Promise<void>;
/**
* Draw under the assumption that the `ViewConfig` has not changed since
* the previous call to `draw()`, but the underlying data has. Defaults to
* dispatch to `draw()`.
*
* @example
* ```javascript
* async update(view: perspective.View): Promise<void> {
* return this.draw(view);
* }
* ```
*/
update(view: View): Promise<void>;
/**
* Clear this plugin, though it is up to the discretion of the plugin
* author to determine what this means. Defaults to resetting this
* element's `innerHTML`, so be sure to override if you want custom
* behavior.
*
* @example
* ```javascript
* async clear(): Promise<void> {
* this.innerHTML = "";
* }
* ```
*/
clear(): Promise<void>;
/**
* Like `update()`, but for when the dimensions of the plugin have changed
* and the underlying data has not.
*/
resize(): Promise<void>;
/**
* Notify the plugin that the style environment has changed. Useful for
* plugins which read CSS styles via `window.getComputedStyle()`.
*/
restyle(): Promise<void>;
/**
* Save this plugin's state to a JSON-serializable value. While this value
* can be anything, it should work reciprocally with `restore()` to return
* this plugin's renderer to the same state, though potentially with a
* different `View`.
*
* `save()` should be used for user-persistent settings that are
* data-agnostic, so the user can have persistent view during refresh or
* reload. For example, `@finos/perspective-viewer-d3fc` uses
* `plugin_config` to remember the user-repositionable legend coordinates.
*/
save(): Promise<any>;
/**
* Restore this plugin to a state previously returned by `save()`.
*/
restore(config: any): Promise<void>;
/**
* Free any resources acquired by this plugin and prepare to be deleted.
*/
delete(): Promise<void>;
}
/**
* The `<perspective-viewer-plugin>` element, the default perspective plugin
* which is registered and activated automcatically when a
* `<perspective-viewer>` is loaded without plugins. While you will not
* typically instantiate this class directly, it is simple enough to function
* as a good "default" plugin implementation which can be extended to create
* custom plugins.
*
* @example
* ```javascript
* class MyPlugin extends customElements.get("perspective-viewer-plugin") {
* // Custom plugin overrides
* }
* ```
* @noInheritDoc
*/
export class HTMLPerspectiveViewerPluginElement
extends HTMLElement
implements IPerspectiveViewerPlugin
{
constructor() {
super();
}
get name(): string {
return "Debug";
}
get select_mode(): "select" | "toggle" {
return "select";
}
get min_config_columns(): number | undefined {
return undefined;
}
get config_column_names(): string[] | undefined {
return undefined;
}
get priority(): number {
return 0;
}
can_render_column_styles(): boolean {
return false;
}
column_style_config(): any {
{
}
}
async update(view: View): Promise<void> {
return this.draw(view);
}
async draw(view: View): Promise<void> {
this.style.backgroundColor = "#fff";
const csv = await view.to_csv();
const css = `margin:0;overflow:scroll;position:absolute;width:100%;height:100%`;
this.innerHTML = `<pre style='${css}'>${csv}</pre>`;
}
async clear(): Promise<void> {
this.innerHTML = "";
}
async resize(): Promise<void> {
// Not Implemented
}
async restyle(): Promise<void> {
// Not Implemented
}
async save(): Promise<any> {
// Not Implemented
}
async restore(): Promise<void> {
// Not Implemented
}
async delete(): Promise<void> {
// Not Implemented
}
}