UNPKG

15.3 kBJavaScriptView Raw
1"use strict";
2// *****************************************************************************
3// Copyright (C) 2017 TypeFox and others.
4//
5// This program and the accompanying materials are made available under the
6// terms of the Eclipse Public License v. 2.0 which is available at
7// http://www.eclipse.org/legal/epl-2.0.
8//
9// This Source Code may also be made available under the following Secondary
10// Licenses when the conditions for such availability set forth in the Eclipse
11// Public License v. 2.0 are satisfied: GNU General Public License, version 2
12// with the GNU Classpath Exception which is available at
13// https://www.gnu.org/software/classpath/license.html.
14//
15// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16// *****************************************************************************
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.togglePinned = exports.unlock = exports.lock = exports.isLocked = exports.pin = exports.unpin = exports.isPinned = exports.waitForHidden = exports.waitForRevealed = exports.waitForClosed = exports.addClipboardListener = exports.addKeyListener = exports.addEventListener = exports.EventListenerObject = exports.createIconButton = exports.setEnabled = exports.BaseWidget = exports.UnsafeWidgetUtilities = exports.DEFAULT_SCROLL_OPTIONS = exports.LOCKED_CLASS = exports.PINNED_CLASS = exports.FOCUS_CLASS = exports.SELECTED_CLASS = exports.CODICON_LOADING_CLASSES = exports.BUSY_CLASS = exports.COLLAPSED_CLASS = exports.CODICON_TREE_ITEM_CLASSES = exports.EXPANSION_TOGGLE_CLASS = exports.DISABLED_CLASS = exports.codicon = exports.codiconArray = exports.ACTION_ITEM = void 0;
19const tslib_1 = require("tslib");
20/* eslint-disable @typescript-eslint/no-explicit-any */
21const inversify_1 = require("inversify");
22const widgets_1 = require("@phosphor/widgets");
23const messaging_1 = require("@phosphor/messaging");
24const common_1 = require("../../common");
25const keys_1 = require("../keyboard/keys");
26const perfect_scrollbar_1 = require("perfect-scrollbar");
27(0, inversify_1.decorate)((0, inversify_1.injectable)(), widgets_1.Widget);
28(0, inversify_1.decorate)((0, inversify_1.unmanaged)(), widgets_1.Widget, 0);
29(0, tslib_1.__exportStar)(require("@phosphor/widgets"), exports);
30(0, tslib_1.__exportStar)(require("@phosphor/messaging"), exports);
31exports.ACTION_ITEM = 'action-label';
32function codiconArray(name, actionItem = false) {
33 const array = ['codicon', `codicon-${name}`];
34 if (actionItem) {
35 array.push(exports.ACTION_ITEM);
36 }
37 return array;
38}
39exports.codiconArray = codiconArray;
40function codicon(name, actionItem = false) {
41 return `codicon codicon-${name}${actionItem ? ` ${exports.ACTION_ITEM}` : ''}`;
42}
43exports.codicon = codicon;
44exports.DISABLED_CLASS = 'theia-mod-disabled';
45exports.EXPANSION_TOGGLE_CLASS = 'theia-ExpansionToggle';
46exports.CODICON_TREE_ITEM_CLASSES = codiconArray('chevron-down');
47exports.COLLAPSED_CLASS = 'theia-mod-collapsed';
48exports.BUSY_CLASS = 'theia-mod-busy';
49exports.CODICON_LOADING_CLASSES = codiconArray('loading');
50exports.SELECTED_CLASS = 'theia-mod-selected';
51exports.FOCUS_CLASS = 'theia-mod-focus';
52exports.PINNED_CLASS = 'theia-mod-pinned';
53exports.LOCKED_CLASS = 'theia-mod-locked';
54exports.DEFAULT_SCROLL_OPTIONS = {
55 suppressScrollX: true,
56 minScrollbarLength: 35,
57};
58/**
59 * At a number of places in the code, we have effectively reimplemented Phosphor's Widget.attach and Widget.detach,
60 * but omitted the checks that Phosphor expects to be performed for those operations. That is a bad idea, because it
61 * means that we are telling widgets that they are attached or detached when not all the conditions that should apply
62 * do apply. We should explicitly mark those locations so that we know where we should go fix them later.
63 */
64var UnsafeWidgetUtilities;
65(function (UnsafeWidgetUtilities) {
66 /**
67 * Ordinarily, the following checks should be performed before detaching a widget:
68 * It should not be the child of another widget
69 * It should be attached and it should be a child of document.body
70 */
71 function detach(widget) {
72 messaging_1.MessageLoop.sendMessage(widget, widgets_1.Widget.Msg.BeforeDetach);
73 widget.node.remove();
74 messaging_1.MessageLoop.sendMessage(widget, widgets_1.Widget.Msg.AfterDetach);
75 }
76 UnsafeWidgetUtilities.detach = detach;
77 ;
78 /**
79 * @param ref The child of the host element to insert the widget before.
80 * Ordinarily the following checks should be performed:
81 * The widget should have no parent
82 * The widget should not be attached, and its node should not be a child of document.body
83 * The host should be a child of document.body
84 * We often violate the last condition.
85 */
86 // eslint-disable-next-line no-null/no-null
87 function attach(widget, host, ref = null) {
88 messaging_1.MessageLoop.sendMessage(widget, widgets_1.Widget.Msg.BeforeAttach);
89 host.insertBefore(widget.node, ref);
90 messaging_1.MessageLoop.sendMessage(widget, widgets_1.Widget.Msg.AfterAttach);
91 }
92 UnsafeWidgetUtilities.attach = attach;
93 ;
94})(UnsafeWidgetUtilities = exports.UnsafeWidgetUtilities || (exports.UnsafeWidgetUtilities = {}));
95let BaseWidget = class BaseWidget extends widgets_1.Widget {
96 constructor(options) {
97 super(options);
98 this.onScrollYReachEndEmitter = new common_1.Emitter();
99 this.onScrollYReachEnd = this.onScrollYReachEndEmitter.event;
100 this.onScrollUpEmitter = new common_1.Emitter();
101 this.onScrollUp = this.onScrollUpEmitter.event;
102 this.onDidChangeVisibilityEmitter = new common_1.Emitter();
103 this.onDidChangeVisibility = this.onDidChangeVisibilityEmitter.event;
104 this.onDidDisposeEmitter = new common_1.Emitter();
105 this.onDidDispose = this.onDidDisposeEmitter.event;
106 this.toDispose = new common_1.DisposableCollection(this.onDidDisposeEmitter, common_1.Disposable.create(() => this.onDidDisposeEmitter.fire()), this.onScrollYReachEndEmitter, this.onScrollUpEmitter, this.onDidChangeVisibilityEmitter);
107 this.toDisposeOnDetach = new common_1.DisposableCollection();
108 }
109 dispose() {
110 if (this.isDisposed) {
111 return;
112 }
113 super.dispose();
114 this.toDispose.dispose();
115 }
116 onCloseRequest(msg) {
117 super.onCloseRequest(msg);
118 this.dispose();
119 }
120 onBeforeAttach(msg) {
121 if (this.title.iconClass === '') {
122 this.title.iconClass = 'no-icon';
123 }
124 super.onBeforeAttach(msg);
125 }
126 onAfterDetach(msg) {
127 if (this.title.iconClass === 'no-icon') {
128 this.title.iconClass = '';
129 }
130 super.onAfterDetach(msg);
131 }
132 onBeforeDetach(msg) {
133 this.toDisposeOnDetach.dispose();
134 super.onBeforeDetach(msg);
135 }
136 onAfterAttach(msg) {
137 super.onAfterAttach(msg);
138 if (this.scrollOptions) {
139 (async () => {
140 const container = await this.getScrollContainer();
141 container.style.overflow = 'hidden';
142 this.scrollBar = new perfect_scrollbar_1.default(container, this.scrollOptions);
143 this.disableScrollBarFocus(container);
144 this.toDisposeOnDetach.push(addEventListener(container, 'ps-y-reach-end', () => { this.onScrollYReachEndEmitter.fire(undefined); }));
145 this.toDisposeOnDetach.push(addEventListener(container, 'ps-scroll-up', () => { this.onScrollUpEmitter.fire(undefined); }));
146 this.toDisposeOnDetach.push(common_1.Disposable.create(() => {
147 if (this.scrollBar) {
148 this.scrollBar.destroy();
149 this.scrollBar = undefined;
150 }
151 container.style.overflow = 'initial';
152 }));
153 })();
154 }
155 }
156 getScrollContainer() {
157 return this.node;
158 }
159 disableScrollBarFocus(scrollContainer) {
160 for (const thumbs of [scrollContainer.getElementsByClassName('ps__thumb-x'), scrollContainer.getElementsByClassName('ps__thumb-y')]) {
161 for (let i = 0; i < thumbs.length; i++) {
162 const element = thumbs.item(i);
163 if (element) {
164 element.removeAttribute('tabIndex');
165 }
166 }
167 }
168 }
169 onUpdateRequest(msg) {
170 super.onUpdateRequest(msg);
171 if (this.scrollBar) {
172 this.scrollBar.update();
173 }
174 }
175 addUpdateListener(element, type, useCapture) {
176 this.addEventListener(element, type, e => {
177 this.update();
178 e.preventDefault();
179 }, useCapture);
180 }
181 addEventListener(element, type, listener, useCapture) {
182 this.toDisposeOnDetach.push(addEventListener(element, type, listener, useCapture));
183 }
184 addKeyListener(element, keysOrKeyCodes, action, ...additionalEventTypes) {
185 this.toDisposeOnDetach.push(addKeyListener(element, keysOrKeyCodes, action, ...additionalEventTypes));
186 }
187 addClipboardListener(element, type, listener) {
188 this.toDisposeOnDetach.push(addClipboardListener(element, type, listener));
189 }
190 getPreviewNode() {
191 return this.node;
192 }
193 setFlag(flag) {
194 super.setFlag(flag);
195 if (flag === widgets_1.Widget.Flag.IsVisible) {
196 this.onDidChangeVisibilityEmitter.fire(this.isVisible);
197 }
198 }
199 clearFlag(flag) {
200 super.clearFlag(flag);
201 if (flag === widgets_1.Widget.Flag.IsVisible) {
202 this.onDidChangeVisibilityEmitter.fire(this.isVisible);
203 }
204 }
205};
206BaseWidget = (0, tslib_1.__decorate)([
207 (0, inversify_1.injectable)(),
208 (0, tslib_1.__metadata)("design:paramtypes", [Object])
209], BaseWidget);
210exports.BaseWidget = BaseWidget;
211function setEnabled(element, enabled) {
212 element.classList.toggle(exports.DISABLED_CLASS, !enabled);
213 element.tabIndex = enabled ? 0 : -1;
214}
215exports.setEnabled = setEnabled;
216function createIconButton(...classNames) {
217 const icon = document.createElement('i');
218 icon.classList.add(...classNames);
219 const button = document.createElement('span');
220 button.tabIndex = 0;
221 button.appendChild(icon);
222 return button;
223}
224exports.createIconButton = createIconButton;
225var EventListenerObject;
226(function (EventListenerObject) {
227 function is(listener) {
228 return (0, common_1.isObject)(listener) && 'handleEvent' in listener;
229 }
230 EventListenerObject.is = is;
231})(EventListenerObject = exports.EventListenerObject || (exports.EventListenerObject = {}));
232function addEventListener(element, type, listener, useCapture) {
233 element.addEventListener(type, listener, useCapture);
234 return common_1.Disposable.create(() => element.removeEventListener(type, listener, useCapture));
235}
236exports.addEventListener = addEventListener;
237function addKeyListener(element, keysOrKeyCodes, action, ...additionalEventTypes) {
238 const toDispose = new common_1.DisposableCollection();
239 const keyCodePredicate = (() => {
240 if (typeof keysOrKeyCodes === 'function') {
241 return keysOrKeyCodes;
242 }
243 else {
244 return (actual) => keys_1.KeysOrKeyCodes.toKeyCodes(keysOrKeyCodes).some(k => k.equals(actual));
245 }
246 })();
247 toDispose.push(addEventListener(element, 'keydown', e => {
248 const kc = keys_1.KeyCode.createKeyCode(e);
249 if (keyCodePredicate(kc)) {
250 const result = action(e);
251 if (typeof result !== 'boolean' || result) {
252 e.stopPropagation();
253 e.preventDefault();
254 }
255 }
256 }));
257 for (const type of additionalEventTypes) {
258 toDispose.push(addEventListener(element, type, e => {
259 // eslint-disable-next-line @typescript-eslint/no-explicit-any
260 const event = type['keydown'];
261 const result = action(event);
262 if (typeof result !== 'boolean' || result) {
263 e.stopPropagation();
264 e.preventDefault();
265 }
266 }));
267 }
268 return toDispose;
269}
270exports.addKeyListener = addKeyListener;
271function addClipboardListener(element, type, listener) {
272 const documentListener = (e) => {
273 const activeElement = document.activeElement;
274 if (activeElement && element.contains(activeElement)) {
275 if (EventListenerObject.is(listener)) {
276 listener.handleEvent(e);
277 }
278 else {
279 listener.bind(element)(e);
280 }
281 }
282 };
283 document.addEventListener(type, documentListener);
284 return common_1.Disposable.create(() => document.removeEventListener(type, documentListener));
285}
286exports.addClipboardListener = addClipboardListener;
287/**
288 * Resolves when the given widget is detached and hidden.
289 */
290function waitForClosed(widget) {
291 return waitForVisible(widget, false, false);
292}
293exports.waitForClosed = waitForClosed;
294/**
295 * Resolves when the given widget is attached and visible.
296 */
297function waitForRevealed(widget) {
298 return waitForVisible(widget, true, true);
299}
300exports.waitForRevealed = waitForRevealed;
301/**
302 * Resolves when the given widget is hidden regardless of attachment.
303 */
304function waitForHidden(widget) {
305 return waitForVisible(widget, false);
306}
307exports.waitForHidden = waitForHidden;
308function waitForVisible(widget, visible, attached) {
309 if ((typeof attached !== 'boolean' || widget.isAttached === attached) &&
310 (widget.isVisible === visible || (widget.node.style.visibility !== 'hidden') === visible)) {
311 return new Promise(resolve => window.requestAnimationFrame(() => resolve()));
312 }
313 return new Promise(resolve => {
314 const waitFor = () => window.requestAnimationFrame(() => {
315 if ((typeof attached !== 'boolean' || widget.isAttached === attached) &&
316 (widget.isVisible === visible || (widget.node.style.visibility !== 'hidden') === visible)) {
317 window.requestAnimationFrame(() => resolve());
318 }
319 else {
320 waitFor();
321 }
322 });
323 waitFor();
324 });
325}
326function isPinned(title) {
327 const pinnedState = !title.closable && title.className.includes(exports.PINNED_CLASS);
328 return pinnedState;
329}
330exports.isPinned = isPinned;
331function unpin(title) {
332 title.closable = true;
333 title.className = title.className.replace(exports.PINNED_CLASS, '').trim();
334}
335exports.unpin = unpin;
336function pin(title) {
337 title.closable = false;
338 if (!title.className.includes(exports.PINNED_CLASS)) {
339 title.className += ` ${exports.PINNED_CLASS}`;
340 }
341}
342exports.pin = pin;
343function isLocked(title) {
344 return title.className.includes(exports.LOCKED_CLASS);
345}
346exports.isLocked = isLocked;
347function lock(title) {
348 if (!title.className.includes(exports.LOCKED_CLASS)) {
349 title.className += ` ${exports.LOCKED_CLASS}`;
350 }
351}
352exports.lock = lock;
353function unlock(title) {
354 title.className = title.className.replace(exports.LOCKED_CLASS, '').trim();
355}
356exports.unlock = unlock;
357function togglePinned(title) {
358 if (title) {
359 if (isPinned(title)) {
360 unpin(title);
361 }
362 else {
363 pin(title);
364 }
365 }
366}
367exports.togglePinned = togglePinned;
368//# sourceMappingURL=widget.js.map
\No newline at end of file