Path: blob/main/src/vs/workbench/contrib/comments/browser/commentThreadHeader.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as dom from '../../../../base/browser/dom.js';6import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js';7import { Action, ActionRunner } from '../../../../base/common/actions.js';8import { Codicon } from '../../../../base/common/codicons.js';9import { Disposable, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js';10import * as strings from '../../../../base/common/strings.js';11import * as languages from '../../../../editor/common/languages.js';12import { IRange } from '../../../../editor/common/core/range.js';13import * as nls from '../../../../nls.js';14import { createActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';15import { IMenu, MenuItemAction, SubmenuItemAction } from '../../../../platform/actions/common/actions.js';16import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';17import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';18import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js';19import { ThemeIcon } from '../../../../base/common/themables.js';20import { CommentMenus } from './commentMenus.js';21import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';22import { MarshalledId } from '../../../../base/common/marshallingIds.js';23import { StandardMouseEvent } from '../../../../base/browser/mouseEvent.js';24import { MarshalledCommentThread } from '../../../common/comments.js';25import { CommentCommandId } from '../common/commentCommandIds.js';2627const collapseIcon = registerIcon('review-comment-collapse', Codicon.chevronUp, nls.localize('collapseIcon', 'Icon to collapse a review comment.'));28const COLLAPSE_ACTION_CLASS = 'expand-review-action ' + ThemeIcon.asClassName(collapseIcon);29const DELETE_ACTION_CLASS = 'expand-review-action ' + ThemeIcon.asClassName(Codicon.trashcan);3031function threadHasComments(comments: ReadonlyArray<languages.Comment> | undefined): comments is ReadonlyArray<languages.Comment> {32return !!comments && comments.length > 0;33}3435export class CommentThreadHeader<T = IRange> extends Disposable {36private _headElement: HTMLElement;37private _headingLabel!: HTMLElement;38private _actionbarWidget!: ActionBar;39private _collapseAction!: Action;40private _contextMenuActionRunner: ActionRunner | undefined;4142constructor(43container: HTMLElement,44private _delegate: { collapse: () => void },45private _commentMenus: CommentMenus,46private _commentThread: languages.CommentThread<T>,47@IContextKeyService private readonly _contextKeyService: IContextKeyService,48@IInstantiationService private readonly _instantiationService: IInstantiationService,49@IContextMenuService private readonly _contextMenuService: IContextMenuService50) {51super();52this._headElement = <HTMLDivElement>dom.$('.head');53container.appendChild(this._headElement);54this._register(toDisposable(() => this._headElement.remove()));55this._fillHead();56}5758protected _fillHead(): void {59const titleElement = dom.append(this._headElement, dom.$('.review-title'));6061this._headingLabel = dom.append(titleElement, dom.$('span.filename'));62this.createThreadLabel();6364const actionsContainer = dom.append(this._headElement, dom.$('.review-actions'));65this._actionbarWidget = new ActionBar(actionsContainer, {66actionViewItemProvider: createActionViewItem.bind(undefined, this._instantiationService)67});6869this._register(this._actionbarWidget);7071const collapseClass = threadHasComments(this._commentThread.comments) ? COLLAPSE_ACTION_CLASS : DELETE_ACTION_CLASS;72this._collapseAction = new Action(CommentCommandId.Hide, nls.localize('label.collapse', "Collapse"), collapseClass, true, () => this._delegate.collapse());73if (!threadHasComments(this._commentThread.comments)) {74const commentsChanged: MutableDisposable<IDisposable> = this._register(new MutableDisposable());75commentsChanged.value = this._commentThread.onDidChangeComments(() => {76if (threadHasComments(this._commentThread.comments)) {77this._collapseAction.class = COLLAPSE_ACTION_CLASS;78commentsChanged.clear();79}80});81}8283const menu = this._commentMenus.getCommentThreadTitleActions(this._contextKeyService);84this._register(menu);85this.setActionBarActions(menu);8687this._register(menu);88this._register(menu.onDidChange(e => {89this.setActionBarActions(menu);90}));9192this._register(dom.addDisposableListener(this._headElement, dom.EventType.CONTEXT_MENU, e => {93return this.onContextMenu(e);94}));9596this._actionbarWidget.context = this._commentThread;97}9899private setActionBarActions(menu: IMenu): void {100const groups = menu.getActions({ shouldForwardArgs: true }).reduce((r, [, actions]) => [...r, ...actions], <(MenuItemAction | SubmenuItemAction)[]>[]);101this._actionbarWidget.clear();102this._actionbarWidget.push([...groups, this._collapseAction], { label: false, icon: true });103}104105updateCommentThread(commentThread: languages.CommentThread<T>) {106this._commentThread = commentThread;107108this._actionbarWidget.context = this._commentThread;109this.createThreadLabel();110}111112createThreadLabel() {113let label: string | undefined;114label = this._commentThread.label;115116if (label === undefined) {117if (!(this._commentThread.comments && this._commentThread.comments.length)) {118label = nls.localize('startThread', "Start discussion");119}120}121122if (label) {123this._headingLabel.textContent = strings.escape(label);124this._headingLabel.setAttribute('aria-label', label);125}126}127128updateHeight(headHeight: number) {129this._headElement.style.height = `${headHeight}px`;130this._headElement.style.lineHeight = this._headElement.style.height;131}132133private onContextMenu(e: MouseEvent) {134const actions = this._commentMenus.getCommentThreadTitleContextActions(this._contextKeyService);135if (!actions.length) {136return;137}138const event = new StandardMouseEvent(dom.getWindow(this._headElement), e);139if (!this._contextMenuActionRunner) {140this._contextMenuActionRunner = this._register(new ActionRunner());141}142this._contextMenuService.showContextMenu({143getAnchor: () => event,144getActions: () => actions,145actionRunner: this._contextMenuActionRunner,146getActionsContext: (): MarshalledCommentThread => {147return {148commentControlHandle: this._commentThread.controllerHandle,149commentThreadHandle: this._commentThread.commentThreadHandle,150$mid: MarshalledId.CommentThread151};152},153});154}155}156157158