UNPKG

6.17 kBPlain TextView Raw
1/* -----------------------------------------------------------------------------
2| Copyright (c) Jupyter Development Team.
3| Distributed under the terms of the Modified BSD License.
4|----------------------------------------------------------------------------*/
5
6import { CommandRegistry } from '@lumino/commands';
7import { JSONExt, ReadonlyPartialJSONObject } from '@lumino/coreutils';
8import { IDisposable } from '@lumino/disposable';
9import { ElementDataset } from '@lumino/virtualdom';
10
11/**
12 * The command data attribute added to nodes that are connected.
13 */
14const COMMAND_ATTR = 'commandlinker-command';
15
16/**
17 * The args data attribute added to nodes that are connected.
18 */
19const ARGS_ATTR = 'commandlinker-args';
20
21/**
22 * A static class that provides helper methods to generate clickable nodes that
23 * execute registered commands with pre-populated arguments.
24 */
25export class CommandLinker implements IDisposable {
26 /**
27 * Instantiate a new command linker.
28 */
29 constructor(options: CommandLinker.IOptions) {
30 this._commands = options.commands;
31 document.body.addEventListener('click', this);
32 }
33
34 /**
35 * Test whether the linker is disposed.
36 */
37 get isDisposed(): boolean {
38 return this._isDisposed;
39 }
40
41 /**
42 * Dispose of the resources held by the linker.
43 */
44 dispose(): void {
45 if (this.isDisposed) {
46 return;
47 }
48 this._isDisposed = true;
49 document.body.removeEventListener('click', this);
50 }
51
52 /**
53 * Connect a command/argument pair to a given node so that when it is clicked,
54 * the command will execute.
55 *
56 * @param node - The node being connected.
57 *
58 * @param command - The command ID to execute upon click.
59 *
60 * @param args - The arguments with which to invoke the command.
61 *
62 * @returns The same node that was passed in, after it has been connected.
63 *
64 * #### Notes
65 * Only `click` events will execute the command on a connected node. So, there
66 * are two considerations that are relevant:
67 * 1. If a node is connected, the default click action will be prevented.
68 * 2. The `HTMLElement` passed in should be clickable.
69 */
70 connectNode(
71 node: HTMLElement,
72 command: string,
73 args?: ReadonlyPartialJSONObject
74 ): HTMLElement {
75 node.setAttribute(`data-${COMMAND_ATTR}`, command);
76 if (args !== void 0) {
77 node.setAttribute(`data-${ARGS_ATTR}`, JSON.stringify(args));
78 }
79 return node;
80 }
81
82 /**
83 * Disconnect a node that has been connected to execute a command on click.
84 *
85 * @param node - The node being disconnected.
86 *
87 * @returns The same node that was passed in, after it has been disconnected.
88 *
89 * #### Notes
90 * This method is safe to call multiple times and is safe to call on nodes
91 * that were never connected.
92 *
93 * This method can be called on rendered virtual DOM nodes that were populated
94 * using the `populateVNodeDataset` method in order to disconnect them from
95 * executing their command/argument pair.
96 */
97 disconnectNode(node: HTMLElement): HTMLElement {
98 node.removeAttribute(`data-${COMMAND_ATTR}`);
99 node.removeAttribute(`data-${ARGS_ATTR}`);
100 return node;
101 }
102
103 /**
104 * Handle the DOM events for the command linker helper class.
105 *
106 * @param event - The DOM event sent to the class.
107 *
108 * #### Notes
109 * This method implements the DOM `EventListener` interface and is
110 * called in response to events on the panel's DOM node. It should
111 * not be called directly by user code.
112 */
113 handleEvent(event: Event): void {
114 switch (event.type) {
115 case 'click':
116 this._evtClick(event as MouseEvent);
117 break;
118 default:
119 return;
120 }
121 }
122
123 /**
124 * Populate the `dataset` attribute within the collection of attributes used
125 * to instantiate a virtual DOM node with the values necessary for its
126 * rendered DOM node to respond to clicks by executing a command/argument
127 * pair.
128 *
129 * @param command - The command ID to execute upon click.
130 *
131 * @param args - The arguments with which to invoke the command.
132 *
133 * @returns A `dataset` collection for use within virtual node attributes.
134 *
135 * #### Notes
136 * The return value can be used on its own as the value for the `dataset`
137 * attribute of a virtual element, or it can be added to an existing `dataset`
138 * as in the example below.
139 *
140 * #### Example
141 * ```typescript
142 * let command = 'some:command-id';
143 * let args = { alpha: 'beta' };
144 * let anchor = h.a({
145 * className: 'some-class',
146 * dataset: {
147 * foo: '1',
148 * bar: '2',
149 * ../...linker.populateVNodeDataset(command, args)
150 * }
151 * }, 'some text');
152 * ```
153 */
154 populateVNodeDataset(
155 command: string,
156 args?: ReadonlyPartialJSONObject
157 ): ElementDataset {
158 let dataset: ElementDataset;
159 if (args !== void 0) {
160 dataset = { [ARGS_ATTR]: JSON.stringify(args), [COMMAND_ATTR]: command };
161 } else {
162 dataset = { [COMMAND_ATTR]: command };
163 }
164 return dataset;
165 }
166
167 /**
168 * The global click handler that deploys commands/argument pairs that are
169 * attached to the node being clicked.
170 */
171 private _evtClick(event: MouseEvent): void {
172 let target = event.target as HTMLElement;
173 while (target && target.parentElement) {
174 if (target.hasAttribute(`data-${COMMAND_ATTR}`)) {
175 event.preventDefault();
176 const command = target.getAttribute(`data-${COMMAND_ATTR}`);
177 if (!command) {
178 return;
179 }
180 const argsValue = target.getAttribute(`data-${ARGS_ATTR}`);
181 let args = JSONExt.emptyObject;
182 if (argsValue) {
183 args = JSON.parse(argsValue);
184 }
185 void this._commands.execute(command, args);
186 return;
187 }
188 target = target.parentElement;
189 }
190 }
191
192 private _commands: CommandRegistry;
193 private _isDisposed = false;
194}
195
196/**
197 * A namespace for command linker statics.
198 */
199export namespace CommandLinker {
200 /**
201 * The instantiation options for a command linker.
202 */
203 export interface IOptions {
204 /**
205 * The command registry instance that all linked commands will use.
206 */
207 commands: CommandRegistry;
208 }
209}