提交 1789af2b 编写于 作者: P Peng Lyu

Use customized toolbar for reactions.

上级 b819c766
......@@ -9,7 +9,7 @@ import { Event } from 'vs/base/common/event';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable } from 'vs/base/common/lifecycle';
import { isObject } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
......@@ -1234,6 +1234,8 @@ export interface NewCommentAction {
*/
export interface CommentReaction {
readonly label?: string;
readonly iconPath?: UriComponents;
readonly count?: number;
readonly hasReacted?: boolean;
readonly canEdit?: boolean;
}
......
......@@ -873,6 +873,8 @@ declare module 'vscode' {
interface CommentReaction {
readonly label?: string;
readonly iconPath?: string | Uri;
count?: number;
readonly hasReacted?: boolean;
}
......
......@@ -250,7 +250,13 @@ function convertFromComment(comment: modes.Comment): vscode.Comment {
canEdit: comment.canEdit,
canDelete: comment.canDelete,
isDraft: comment.isDraft,
commentReactions: comment.commentReactions
commentReactions: comment.commentReactions ? comment.commentReactions.map(reaction => {
return {
label: reaction.label,
count: reaction.count,
hasReacted: reaction.hasReacted
};
}) : undefined
};
}
......@@ -273,6 +279,8 @@ function convertToComment(provider: vscode.DocumentCommentProvider | vscode.Work
commentReactions: vscodeComment.commentReactions ? vscodeComment.commentReactions.map(reaction => {
return {
label: reaction.label,
iconPath: reaction.iconPath ? extHostTypeConverter.pathOrURIToURI(reaction.iconPath) : undefined,
count: reaction.count,
hasReacted: reaction.hasReacted,
canEdit: (reaction.hasReacted && providerCanDeleteReaction) || (!reaction.hasReacted && providerCanAddReaction)
};
......
......@@ -311,7 +311,7 @@ export function fromRangeOrRangeWithMessage(ranges: vscode.Range[] | vscode.Deco
}
}
function pathOrURIToURI(value: string | URI): URI {
export function pathOrURIToURI(value: string | URI): URI {
if (typeof value === 'undefined') {
return value;
}
......
......@@ -9,7 +9,7 @@ import * as modes from 'vs/editor/common/modes';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ActionsOrientation, ActionItem, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { Button } from 'vs/base/browser/ui/button/button';
import { Action, IActionRunner } from 'vs/base/common/actions';
import { Action, IActionRunner, IAction } from 'vs/base/common/actions';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { ITextModel } from 'vs/editor/common/model';
......@@ -32,10 +32,41 @@ import { assign } from 'vs/base/common/objects';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { DropdownMenuActionItem } from 'vs/base/browser/ui/dropdown/dropdown';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
const UPDATE_COMMENT_LABEL = nls.localize('label.updateComment', "Update comment");
const UPDATE_IN_PROGRESS_LABEL = nls.localize('label.updatingComment', "Updating comment...");
class ToggleReactionsAction extends Action {
static readonly ID = 'toolbar.toggle.pickReactions';
private _menuActions: IAction[];
private toggleDropdownMenu: () => void;
constructor(toggleDropdownMenu: () => void, title?: string) {
title = title || nls.localize('pickReactions', "Pick Reactions...");
super(ToggleReactionsAction.ID, title, 'toggle-reactions', true);
this.toggleDropdownMenu = toggleDropdownMenu;
}
run(): Promise<any> {
this.toggleDropdownMenu();
return Promise.resolve(true);
}
get menuActions() {
return this._menuActions;
}
set menuActions(actions: IAction[]) {
this._menuActions = actions;
}
}
export class CommentNode extends Disposable {
private _domNode: HTMLElement;
private _body: HTMLElement;
......@@ -96,7 +127,7 @@ export class CommentNode extends Disposable {
this._body.appendChild(this._md);
if (this.comment.commentReactions && this.comment.commentReactions.length) {
this.createReactions(this._commentDetailsContainer);
this.createReactionsContainer(this._commentDetailsContainer);
}
this._domNode.setAttribute('aria-label', `${comment.userName}, ${comment.body.value}`);
......@@ -120,6 +151,13 @@ export class CommentNode extends Disposable {
}
const actions: Action[] = [];
let reactionGroup = this.commentService.getReactionGroup(this.owner);
if (reactionGroup && reactionGroup.length) {
let toggleReactionAction = this.createReactionPicker();
actions.push(toggleReactionAction);
}
if (this.comment.canEdit) {
this._editAction = this.createEditAction(commentDetailsContainer);
actions.push(this._editAction);
......@@ -134,37 +172,35 @@ export class CommentNode extends Disposable {
const actionsContainer = dom.append(header, dom.$('.comment-actions.hidden'));
this.toolbar = new ToolBar(actionsContainer, this.contextMenuService, {
actionItemProvider: action => this.actionItemProvider(action as Action),
actionItemProvider: action => {
if (action.id === ToggleReactionsAction.ID) {
return new DropdownMenuActionItem(
action,
(<ToggleReactionsAction>action).menuActions,
this.contextMenuService,
action => {
return this.actionItemProvider(action as Action);
},
this.actionRunner,
undefined,
'toolbar-toggle-pickReactions',
() => { return AnchorAlignment.RIGHT; }
);
}
return this.actionItemProvider(action as Action);
},
orientation: ActionsOrientation.HORIZONTAL
});
this.registerActionBarListeners(actionsContainer);
let reactionActions: Action[] = [];
let reactionGroup = this.commentService.getReactionGroup(this.owner);
if (reactionGroup && reactionGroup.length) {
reactionActions = reactionGroup.map((reaction) => {
return new Action(`reaction.command.${reaction.label}`, `${reaction.label}`, '', true, async () => {
try {
await this.commentService.addReaction(this.owner, this.resource, this.comment, reaction);
} catch (e) {
const error = e.message
? nls.localize('commentAddReactionError', "Deleting the comment reaction failed: {0}.", e.message)
: nls.localize('commentAddReactionDefaultError', "Deleting the comment reaction failed");
this.notificationService.error(error);
}
});
});
}
this.toolbar.setActions(actions, reactionActions)();
this.toolbar.setActions(actions, [])();
this._toDispose.push(this.toolbar);
}
}
actionItemProvider(action: Action) {
let options = {};
if (action.id === 'comment.delete' || action.id === 'comment.edit') {
if (action.id === 'comment.delete' || action.id === 'comment.edit' || action.id === ToggleReactionsAction.ID) {
options = { label: false, icon: true };
} else {
options = { label: true, icon: true };
......@@ -174,13 +210,77 @@ export class CommentNode extends Disposable {
return item;
}
private createReactions(commentDetailsContainer: HTMLElement): void {
private createReactionPicker(): ToggleReactionsAction {
let toggleReactionActionItem: DropdownMenuActionItem;
let toggleReactionAction = this._register(new ToggleReactionsAction(() => {
if (toggleReactionActionItem) {
toggleReactionActionItem.show();
}
}, 'Pick'));
let reactionMenuActions: Action[] = [];
let reactionGroup = this.commentService.getReactionGroup(this.owner);
if (reactionGroup && reactionGroup.length) {
reactionMenuActions = reactionGroup.map((reaction) => {
return new Action(`reaction.command.${reaction.label}`, `${reaction.label}`, '', true, async () => {
try {
await this.commentService.addReaction(this.owner, this.resource, this.comment, reaction);
} catch (e) {
const error = e.message
? nls.localize('commentAddReactionError', "Deleting the comment reaction failed: {0}.", e.message)
: nls.localize('commentAddReactionDefaultError', "Deleting the comment reaction failed");
this.notificationService.error(error);
}
});
});
}
toggleReactionAction.menuActions = reactionMenuActions;
toggleReactionActionItem = new DropdownMenuActionItem(
toggleReactionAction,
(<ToggleReactionsAction>toggleReactionAction).menuActions,
this.contextMenuService,
action => {
if (action.id === ToggleReactionsAction.ID) {
return toggleReactionActionItem;
}
return this.actionItemProvider(action as Action);
},
this.actionRunner,
undefined,
'toolbar-toggle-pickReactions',
() => { return AnchorAlignment.RIGHT; }
);
return toggleReactionAction;
}
private createReactionsContainer(commentDetailsContainer: HTMLElement): void {
this._actionsContainer = dom.append(commentDetailsContainer, dom.$('div.comment-reactions'));
this._reactionsActionBar = new ActionBar(this._actionsContainer, {});
this._reactionsActionBar = new ActionBar(this._actionsContainer, {
actionItemProvider: action => {
if (action.id === ToggleReactionsAction.ID) {
return new DropdownMenuActionItem(
action,
(<ToggleReactionsAction>action).menuActions,
this.contextMenuService,
action => {
return this.actionItemProvider(action as Action);
},
this.actionRunner,
undefined,
'toolbar-toggle-pickReactions',
() => { return AnchorAlignment.RIGHT; }
);
}
return this.actionItemProvider(action as Action);
}
});
this._toDispose.push(this._reactionsActionBar);
let reactionActions = this.comment.commentReactions!.map(reaction => {
return new Action(`reaction.${reaction.label}`, `${reaction.label}`, reaction.hasReacted && reaction.canEdit ? 'active' : '', reaction.canEdit, async () => {
this.comment.commentReactions!.map(reaction => {
let action = new Action(`reaction.${reaction.label}`, `${reaction.label}`, reaction.hasReacted && reaction.canEdit ? 'active' : '', reaction.canEdit, async () => {
try {
if (reaction.hasReacted) {
await this.commentService.deleteReaction(this.owner, this.resource, this.comment, reaction);
......@@ -202,9 +302,15 @@ export class CommentNode extends Disposable {
this.notificationService.error(error);
}
});
this._reactionsActionBar.push(action, { label: true, icon: true });
});
reactionActions.forEach(action => this._reactionsActionBar!.push(action, { label: true, icon: true }));
let reactionGroup = this.commentService.getReactionGroup(this.owner);
if (reactionGroup && reactionGroup.length) {
let toggleReactionAction = this.createReactionPicker();
this._reactionsActionBar.push(toggleReactionAction, { label: false, icon: true });
}
}
private createCommentEditor(): void {
......@@ -383,7 +489,7 @@ export class CommentNode extends Disposable {
}
if (this.comment.commentReactions && this.comment.commentReactions.length) {
this.createReactions(this._commentDetailsContainer);
this.createReactionsContainer(this._commentDetailsContainer);
}
}
......
......@@ -748,13 +748,11 @@ registerThemingParticipant((theme, collector) => {
const statusBarItemHoverBackground = theme.getColor(STATUS_BAR_ITEM_HOVER_BACKGROUND);
if (statusBarItemHoverBackground) {
collector.addRule(`.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label.active:hover { background-color: ${statusBarItemHoverBackground}; border: 1px solid grey;
border-radius: 3px; }`);
collector.addRule(`.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label.active:hover { background-color: ${statusBarItemHoverBackground}; border: 1px solid grey;}`);
}
const statusBarItemActiveBackground = theme.getColor(STATUS_BAR_ITEM_ACTIVE_BACKGROUND);
if (statusBarItemActiveBackground) {
collector.addRule(`.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label.active:active { background-color: ${statusBarItemActiveBackground}; border: 1px solid grey;
border-radius: 3px;}`);
collector.addRule(`.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label.active { background-color: ${statusBarItemActiveBackground}; border: 1px solid grey;}`);
}
});
<svg width="26" height="16" viewBox="0 0 26 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 8.25008H4.08333V11.1667H2.91667V8.25008H0V7.08341H2.91667V4.16675H4.08333V7.08341H7V8.25008V8.25008Z" fill="#C8C8C8"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18 0C13.58 0 10 3.58 10 8C10 12.42 13.58 16 18 16C22.42 16 26 12.42 26 8C26 3.58 22.42 0 18 0V0ZM22.81 12.81C22.18 13.44 21.45 13.92 20.64 14.26C19.81 14.62 18.92 14.79 18 14.79C17.08 14.79 16.19 14.62 15.36 14.26C14.55 13.92 13.81 13.43 13.19 12.81C12.57 12.19 12.08 11.45 11.74 10.64C11.38 9.81 11.21 8.92 11.21 8C11.21 7.08 11.38 6.19 11.74 5.36C12.08 4.55 12.57 3.81 13.19 3.19C13.81 2.57 14.55 2.08 15.36 1.74C16.19 1.38 17.08 1.21 18 1.21C18.92 1.21 19.81 1.38 20.64 1.74C21.45 2.08 22.19 2.57 22.81 3.19C23.43 3.81 23.92 4.55 24.26 5.36C24.62 6.19 24.79 7.08 24.79 8C24.79 8.92 24.62 9.81 24.26 10.64C23.92 11.45 23.43 12.19 22.81 12.81V12.81ZM14 6.8V6.21C14 5.55 14.53 5.02 15.2 5.02H15.79C16.45 5.02 16.98 5.55 16.98 6.21V6.8C16.98 7.47 16.45 8 15.79 8H15.2C14.53 8 14 7.47 14 6.8V6.8ZM19 6.8V6.21C19 5.55 19.53 5.02 20.2 5.02H20.79C21.45 5.02 21.98 5.55 21.98 6.21V6.8C21.98 7.47 21.45 8 20.79 8H20.2C19.53 8 19 7.47 19 6.8V6.8ZM23 10C22.28 11.88 20.09 13 18 13C15.91 13 13.72 11.87 13 10C12.86 9.61 13.23 9 13.66 9H22.25C22.66 9 23.14 9.61 23 10V10Z" fill="#C8C8C8"/>
</svg>
\ No newline at end of file
<svg width="26" height="16" viewBox="0 0 26 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 8.25008H4.08333V11.1667H2.91667V8.25008H0V7.08341H2.91667V4.16675H4.08333V7.08341H7V8.25008V8.25008Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18 0C13.58 0 10 3.58 10 8C10 12.42 13.58 16 18 16C22.42 16 26 12.42 26 8C26 3.58 22.42 0 18 0V0ZM22.81 12.81C22.18 13.44 21.45 13.92 20.64 14.26C19.81 14.62 18.92 14.79 18 14.79C17.08 14.79 16.19 14.62 15.36 14.26C14.55 13.92 13.81 13.43 13.19 12.81C12.57 12.19 12.08 11.45 11.74 10.64C11.38 9.81 11.21 8.92 11.21 8C11.21 7.08 11.38 6.19 11.74 5.36C12.08 4.55 12.57 3.81 13.19 3.19C13.81 2.57 14.55 2.08 15.36 1.74C16.19 1.38 17.08 1.21 18 1.21C18.92 1.21 19.81 1.38 20.64 1.74C21.45 2.08 22.19 2.57 22.81 3.19C23.43 3.81 23.92 4.55 24.26 5.36C24.62 6.19 24.79 7.08 24.79 8C24.79 8.92 24.62 9.81 24.26 10.64C23.92 11.45 23.43 12.19 22.81 12.81V12.81ZM14 6.8V6.21C14 5.55 14.53 5.02 15.2 5.02H15.79C16.45 5.02 16.98 5.55 16.98 6.21V6.8C16.98 7.47 16.45 8 15.79 8H15.2C14.53 8 14 7.47 14 6.8V6.8ZM19 6.8V6.21C19 5.55 19.53 5.02 20.2 5.02H20.79C21.45 5.02 21.98 5.55 21.98 6.21V6.8C21.98 7.47 21.45 8 20.79 8H20.2C19.53 8 19 7.47 19 6.8V6.8ZM23 10C22.28 11.88 20.09 13 18 13C15.91 13 13.72 11.87 13 10C12.86 9.61 13.23 9 13.66 9H22.25C22.66 9 23.14 9.61 23 10V10Z" fill="white"/>
</svg>
<svg width="26" height="16" viewBox="0 0 26 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 8.25008H4.08333V11.1667H2.91667V8.25008H0V7.08341H2.91667V4.16675H4.08333V7.08341H7V8.25008V8.25008Z" fill="#4B4B4B"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18 0C13.58 0 10 3.58 10 8C10 12.42 13.58 16 18 16C22.42 16 26 12.42 26 8C26 3.58 22.42 0 18 0V0ZM22.81 12.81C22.18 13.44 21.45 13.92 20.64 14.26C19.81 14.62 18.92 14.79 18 14.79C17.08 14.79 16.19 14.62 15.36 14.26C14.55 13.92 13.81 13.43 13.19 12.81C12.57 12.19 12.08 11.45 11.74 10.64C11.38 9.81 11.21 8.92 11.21 8C11.21 7.08 11.38 6.19 11.74 5.36C12.08 4.55 12.57 3.81 13.19 3.19C13.81 2.57 14.55 2.08 15.36 1.74C16.19 1.38 17.08 1.21 18 1.21C18.92 1.21 19.81 1.38 20.64 1.74C21.45 2.08 22.19 2.57 22.81 3.19C23.43 3.81 23.92 4.55 24.26 5.36C24.62 6.19 24.79 7.08 24.79 8C24.79 8.92 24.62 9.81 24.26 10.64C23.92 11.45 23.43 12.19 22.81 12.81V12.81ZM14 6.8V6.21C14 5.55 14.53 5.02 15.2 5.02H15.79C16.45 5.02 16.98 5.55 16.98 6.21V6.8C16.98 7.47 16.45 8 15.79 8H15.2C14.53 8 14 7.47 14 6.8V6.8ZM19 6.8V6.21C19 5.55 19.53 5.02 20.2 5.02H20.79C21.45 5.02 21.98 5.55 21.98 6.21V6.8C21.98 7.47 21.45 8 20.79 8H20.2C19.53 8 19 7.47 19 6.8V6.8ZM23 10C22.28 11.88 20.09 13 18 13C15.91 13 13.72 11.87 13 10C12.86 9.61 13.23 9 13.66 9H22.25C22.66 9 23.14 9.61 23 10V10Z" fill="#4B4B4B"/>
</svg>
......@@ -153,7 +153,39 @@
.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label.active {
border: 1px solid grey;
border-radius: 3px;
}
.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label.toolbar-toggle-pickReactions {
background-image: url(./reaction.svg);
width: 16px;
height: 16px;
background-repeat: no-repeat;
background-position: center;
}
.monaco-editor.vs-dark .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label.toolbar-toggle-pickReactions {
background-image: url(./reaction-dark.svg);
}
.monaco-editor.hc-black .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label.toolbar-toggle-pickReactions {
background-image: url(./reaction-hc.svg);
}
.monaco-editor .review-widget .body .review-comment .comment-title .action-label.toolbar-toggle-pickReactions {
background-image: url(./reaction.svg);
width: 22px;
height: 18px;
background-size: 100% auto;
background-position: center;
background-repeat: no-repeat;
}
.monaco-editor.vs-dark .review-widget .body .review-comment .comment-title .action-label.toolbar-toggle-pickReactions {
background-image: url(./reaction-dark.svg);
}
.monaco-editor.hc-black .review-widget .body .review-comment .comment-title .action-label.toolbar-toggle-pickReactions {
background-image: url(./reaction-hc.svg);
}
.monaco-editor .review-widget .body .review-comment .review-comment-contents .comment-reactions .action-item a.action-label.disabled {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册