UNPKG

@sussudio/platform

Version:

Internal APIs for VS Code's service injection the base services.

276 lines (275 loc) 9.99 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? (desc = Object.getOwnPropertyDescriptor(target, key)) : desc, d; if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function') r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if ((d = decorators[i])) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); }; }; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as dom from '@sussudio/base/browser/dom.mjs'; import { HighlightedLabel } from '@sussudio/base/browser/ui/highlightedlabel/highlightedLabel.mjs'; import { KeybindingLabel } from '@sussudio/base/browser/ui/keybindingLabel/keybindingLabel.mjs'; import { List } from '@sussudio/base/browser/ui/list/listWidget.mjs'; import { Codicon } from '@sussudio/base/common/codicons.mjs'; import { Disposable } from '@sussudio/base/common/lifecycle.mjs'; import { OS } from '@sussudio/base/common/platform.mjs'; import '../../../css!./actionWidget.mjs'; import { localize } from 'vscode-nls.mjs'; import { IContextViewService } from '../../contextview/browser/contextView.mjs'; import { IKeybindingService } from '../../keybinding/common/keybinding.mjs'; export const acceptSelectedActionCommand = 'acceptSelectedCodeAction'; export const previewSelectedActionCommand = 'previewSelectedCodeAction'; class HeaderRenderer { get templateId() { return 'header' /* ActionListItemKind.Header */; } renderTemplate(container) { container.classList.add('group-header'); const text = document.createElement('span'); container.append(text); return { container, text }; } renderElement(element, _index, templateData) { templateData.text.textContent = element.group?.title ?? ''; } disposeTemplate(_templateData) { // noop } } let ActionItemRenderer = class ActionItemRenderer { _supportsPreview; _keybindingService; get templateId() { return 'action' /* ActionListItemKind.Action */; } constructor(_supportsPreview, _keybindingService) { this._supportsPreview = _supportsPreview; this._keybindingService = _keybindingService; } renderTemplate(container) { container.classList.add(this.templateId); const icon = document.createElement('div'); icon.className = 'icon'; container.append(icon); const text = document.createElement('span'); text.className = 'title'; container.append(text); const keybinding = new KeybindingLabel(container, OS); return { container, icon, text, keybinding }; } renderElement(element, _index, data) { if (element.group?.icon) { data.icon.className = element.group.icon.codicon.classNames; data.icon.style.color = element.group.icon.color ?? ''; } else { data.icon.className = Codicon.lightBulb.classNames; data.icon.style.color = 'var(--vscode-editorLightBulb-foreground)'; } if (!element.item || !element.label) { return; } data.text.textContent = stripNewlines(element.label); if (!element.keybinding) { dom.hide(data.keybinding.element); } else { data.keybinding.set(element.keybinding); dom.show(data.keybinding.element); } const actionTitle = this._keybindingService.lookupKeybinding(acceptSelectedActionCommand)?.getLabel(); const previewTitle = this._keybindingService.lookupKeybinding(previewSelectedActionCommand)?.getLabel(); data.container.classList.toggle('option-disabled', element.disabled); if (element.disabled) { data.container.title = element.label; } else if (actionTitle && previewTitle) { if (this._supportsPreview) { data.container.title = localize( { key: 'label-preview', comment: ['placeholders are keybindings, e.g "F2 to apply, Shift+F2 to preview"'] }, '{0} to apply, {1} to preview', actionTitle, previewTitle, ); } else { data.container.title = localize( { key: 'label', comment: ['placeholder is a keybinding, e.g "F2 to apply"'] }, '{0} to apply', actionTitle, ); } } else { data.container.title = ''; } if (element.description) { const label = new HighlightedLabel(dom.append(data.container, dom.$('span.label-description'))); label.element.classList.add('action-list-description'); label.set(element.description); } } disposeTemplate(_templateData) { // noop } }; ActionItemRenderer = __decorate([__param(1, IKeybindingService)], ActionItemRenderer); class AcceptSelectedEvent extends UIEvent { constructor() { super('acceptSelectedAction'); } } class PreviewSelectedEvent extends UIEvent { constructor() { super('previewSelectedAction'); } } let ActionList = class ActionList extends Disposable { _delegate; _contextViewService; _keybindingService; domNode; _list; _actionLineHeight = 24; _headerLineHeight = 26; _allMenuItems; constructor(user, preview, items, _delegate, _contextViewService, _keybindingService) { super(); this._delegate = _delegate; this._contextViewService = _contextViewService; this._keybindingService = _keybindingService; this.domNode = document.createElement('div'); this.domNode.classList.add('actionList'); const virtualDelegate = { getHeight: (element) => element.kind === 'header' /* ActionListItemKind.Header */ ? this._headerLineHeight : this._actionLineHeight, getTemplateId: (element) => element.kind, }; this._list = this._register( new List( user, this.domNode, virtualDelegate, [new ActionItemRenderer(preview, this._keybindingService), new HeaderRenderer()], { keyboardSupport: false, accessibilityProvider: { getAriaLabel: (element) => { if (element.kind === 'action' /* ActionListItemKind.Action */) { let label = element.label ? stripNewlines(element?.label) : ''; if (element.disabled) { label = localize( { key: 'customQuickFixWidget.labels', comment: [`Action widget labels for accessibility.`] }, '{0}, Disabled Reason: {1}', label, element.disabled, ); } return label; } return null; }, getWidgetAriaLabel: () => localize({ key: 'customQuickFixWidget', comment: [`An action widget option`] }, 'Action Widget'), getRole: (e) => (e.kind === 'action' /* ActionListItemKind.Action */ ? 'option' : 'separator'), getWidgetRole: () => 'listbox', }, }, ), ); this._register(this._list.onMouseClick((e) => this.onListClick(e))); this._register(this._list.onMouseOver((e) => this.onListHover(e))); this._register(this._list.onDidChangeFocus(() => this._list.domFocus())); this._register(this._list.onDidChangeSelection((e) => this.onListSelection(e))); this._allMenuItems = items; this._list.splice(0, this._list.length, this._allMenuItems); if (this._list.length) { this.focusNext(); } } focusCondition(element) { return !element.disabled && element.kind === 'action' /* ActionListItemKind.Action */; } hide(didCancel) { this._delegate.onHide(didCancel); this._contextViewService.hideContextView(); } layout(minWidth) { // Updating list height, depending on how many separators and headers there are. const numHeaders = this._allMenuItems.filter((item) => item.kind === 'header').length; const height = this._allMenuItems.length * this._actionLineHeight; const heightWithHeaders = height + numHeaders * this._headerLineHeight - numHeaders * this._actionLineHeight; this._list.layout(heightWithHeaders); // For finding width dynamically (not using resize observer) const itemWidths = this._allMenuItems.map((_, index) => { const element = document.getElementById(this._list.getElementID(index)); if (element) { element.style.width = 'auto'; const width = element.getBoundingClientRect().width; element.style.width = ''; return width; } return 0; }); // resize observer - can be used in the future since list widget supports dynamic height but not width const width = Math.max(...itemWidths, minWidth); this._list.layout(heightWithHeaders, width); this.domNode.style.height = `${heightWithHeaders}px`; this._list.domFocus(); return width; } focusPrevious() { this._list.focusPrevious(1, true, undefined, this.focusCondition); } focusNext() { this._list.focusNext(1, true, undefined, this.focusCondition); } acceptSelected(preview) { const focused = this._list.getFocus(); if (focused.length === 0) { return; } const focusIndex = focused[0]; const element = this._list.element(focusIndex); if (!this.focusCondition(element)) { return; } const event = preview ? new PreviewSelectedEvent() : new AcceptSelectedEvent(); this._list.setSelection([focusIndex], event); } onListSelection(e) { if (!e.elements.length) { return; } const element = e.elements[0]; if (element.item && this.focusCondition(element)) { this._delegate.onSelect(element.item, e.browserEvent instanceof PreviewSelectedEvent); } else { this._list.setSelection([]); } } onListHover(e) { this._list.setFocus(typeof e.index === 'number' ? [e.index] : []); } onListClick(e) { if (e.element && this.focusCondition(e.element)) { this._list.setFocus([]); } } }; ActionList = __decorate([__param(4, IContextViewService), __param(5, IKeybindingService)], ActionList); export { ActionList }; function stripNewlines(str) { return str.replace(/\r\n|\r|\n/g, ' '); }