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