@finos/perspective-viewer
Version:
The `<perspective-viewer>` Custom Element, frontend for Perspective.js
247 lines (219 loc) • 7.83 kB
text/typescript
/******************************************************************************
*
* Copyright (c) 2018, 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.
*
*/
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 extends HTMLElement {
/**
* 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(): "select" | "toggle" | 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;
/**
* 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: perspective.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: perspective.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 PerspectiveViewerPluginElement
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;
}
async update(view: perspective.View): Promise<void> {
return this.draw(view);
}
async draw(view: perspective.View): Promise<void> {
this.style.backgroundColor = "#fff";
const csv = await view.to_csv({config: {delimiter: "|"}});
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
}
}
if (
document.createElement("perspective-viewer-plugin").constructor ===
HTMLElement
) {
window.customElements.define(
"perspective-viewer-plugin",
PerspectiveViewerPluginElement
);
}