提交 4f053d82 编写于 作者: R Rob Lourens

Settings exclude control - replace fancypants List with basic dom manipulation

上级 8d14a78c
...@@ -33,35 +33,39 @@ ...@@ -33,35 +33,39 @@
margin-top: 1px; margin-top: 1px;
} }
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row:hover .monaco-action-bar, .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row {
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.focused .monaco-action-bar { position: relative;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row:hover .monaco-action-bar,
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row.focused .monaco-action-bar {
display: block; display: block;
} }
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row .monaco-action-bar .action-label { .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .action-label {
width: 16px; width: 16px;
height: 16px; height: 16px;
padding: 2px; padding: 2px;
margin-right: 2px; margin-right: 2px;
} }
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row .monaco-action-bar .setting-excludeAction-edit { .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-edit {
margin-right: 4px; margin-right: 4px;
} }
.vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row .monaco-action-bar .setting-excludeAction-edit { .vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-edit {
background: url(edit.svg) center center no-repeat; background: url(edit.svg) center center no-repeat;
} }
.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row .monaco-action-bar .setting-excludeAction-edit { .vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-edit {
background: url(edit_inverse.svg) center center no-repeat; background: url(edit_inverse.svg) center center no-repeat;
} }
.vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row .monaco-action-bar .setting-excludeAction-remove { .vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-remove {
background: url(action-remove.svg) center center no-repeat; background: url(action-remove.svg) center center no-repeat;
} }
.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row .monaco-action-bar .setting-excludeAction-remove { .vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-remove {
background: url(action-remove-dark.svg) center center no-repeat; background: url(action-remove-dark.svg) center center no-repeat;
} }
...@@ -82,30 +86,30 @@ ...@@ -82,30 +86,30 @@
display: inline-block; display: inline-block;
} }
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newExcludeItem { .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newExcludeItem {
display: flex; display: flex;
} }
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newExcludeItem .setting-exclude-patternInput, .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newExcludeItem .setting-exclude-patternInput,
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newExcludeItem .setting-exclude-siblingInput { .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newExcludeItem .setting-exclude-siblingInput {
display: none; display: none;
flex: 1; flex: 1;
max-width: 200px; max-width: 200px;
} }
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newPattern .setting-exclude-patternInput { .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newPattern .setting-exclude-patternInput {
display: inline-block; display: inline-block;
} }
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newPatternWithSibling .setting-exclude-patternInput { .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newPatternWithSibling .setting-exclude-patternInput {
margin-right: 5px; margin-right: 5px;
} }
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newPatternWithSibling .setting-exclude-patternInput, .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newPatternWithSibling .setting-exclude-patternInput,
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newPatternWithSibling .setting-exclude-siblingInput { .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newPatternWithSibling .setting-exclude-siblingInput {
display: inline-block; display: inline-block;
} }
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list { .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-widget {
margin-bottom: 10px; margin-bottom: 10px;
} }
...@@ -8,7 +8,6 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; ...@@ -8,7 +8,6 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { Button } from 'vs/base/browser/ui/button/button'; import { Button } from 'vs/base/browser/ui/button/button';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { IRenderer, IVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { IAction } from 'vs/base/common/actions'; import { IAction } from 'vs/base/common/actions';
import { Emitter, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
import { KeyCode } from 'vs/base/common/keyCodes'; import { KeyCode } from 'vs/base/common/keyCodes';
...@@ -16,9 +15,7 @@ import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; ...@@ -16,9 +15,7 @@ import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
import 'vs/css!./media/settingsWidgets'; import 'vs/css!./media/settingsWidgets';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { foreground, inputBackground, inputBorder, inputForeground, listHoverBackground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { WorkbenchList } from 'vs/platform/list/browser/listService';
import { foreground, inputBackground, inputBorder, inputForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { attachButtonStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { attachButtonStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
...@@ -77,7 +74,12 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { ...@@ -77,7 +74,12 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
const foregroundColor = theme.getColor(foreground); const foregroundColor = theme.getColor(foreground);
if (foregroundColor) { if (foregroundColor) {
collector.addRule(`.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label { color: ${foregroundColor}; };`); collector.addRule(`.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label { color: ${foregroundColor}; }`);
}
const listHoverBackgroundColor = theme.getColor(listHoverBackground);
if (listHoverBackgroundColor) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row:hover { background-color: ${listHoverBackgroundColor}; }`);
} }
}); });
...@@ -88,8 +90,8 @@ enum AddItemMode { ...@@ -88,8 +90,8 @@ enum AddItemMode {
} }
export class ExcludeSettingListModel { export class ExcludeSettingListModel {
private _dataItems: IExcludeItem[]; private _dataItems: IExcludeItem[] = [];
private _newItem: AddItemMode; private _newItem = AddItemMode.None;
get items(): IExcludeItem[] { get items(): IExcludeItem[] {
const items = [ const items = [
...@@ -133,7 +135,8 @@ interface IExcludeChangeEvent { ...@@ -133,7 +135,8 @@ interface IExcludeChangeEvent {
} }
export class ExcludeSettingWidget extends Disposable { export class ExcludeSettingWidget extends Disposable {
private list: WorkbenchList<IExcludeItem>; private listElement: HTMLElement;
private renderedDisposables: IDisposable[] = [];
private model = new ExcludeSettingListModel(); private model = new ExcludeSettingListModel();
...@@ -143,23 +146,11 @@ export class ExcludeSettingWidget extends Disposable { ...@@ -143,23 +146,11 @@ export class ExcludeSettingWidget extends Disposable {
constructor( constructor(
container: HTMLElement, container: HTMLElement,
@IThemeService private themeService: IThemeService, @IThemeService private themeService: IThemeService,
@IInstantiationService private instantiationService: IInstantiationService @IContextViewService private contextViewService: IContextViewService
) { ) {
super(); super();
const dataRenderer = new ExcludeDataItemRenderer(); this.listElement = DOM.append(container, $('.setting-exclude-widget'));
this._register(dataRenderer.onDidRemoveExclude(key => this._onDidChangeExclude.fire({ originalPattern: key, pattern: undefined })));
this._register(dataRenderer.onEditExclude(key => {
// this.model
}));
const newItemRenderer = this.instantiationService.createInstance(NewExcludeRenderer);
const delegate = new ExcludeSettingListDelegate();
this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [newItemRenderer, dataRenderer], {
identityProvider: element => element.id,
multipleSelectionSupport: false
}) as WorkbenchList<IExcludeItem>;
this._register(this.list);
const addPatternButton = this._register(new Button(container)); const addPatternButton = this._register(new Button(container));
addPatternButton.label = localize('addPattern', "Add Pattern"); addPatternButton.label = localize('addPattern', "Add Pattern");
...@@ -169,6 +160,8 @@ export class ExcludeSettingWidget extends Disposable { ...@@ -169,6 +160,8 @@ export class ExcludeSettingWidget extends Disposable {
this.model.setAddItemMode(AddItemMode.Pattern); this.model.setAddItemMode(AddItemMode.Pattern);
this.update(); this.update();
})); }));
this.update();
} }
setValue(excludeValue: any): void { setValue(excludeValue: any): void {
...@@ -177,66 +170,15 @@ export class ExcludeSettingWidget extends Disposable { ...@@ -177,66 +170,15 @@ export class ExcludeSettingWidget extends Disposable {
} }
private update(): void { private update(): void {
this.list.splice(0, this.list.length, this.model.items); DOM.clearNode(this.listElement);
this.renderedDisposables = dispose(this.renderedDisposables);
const listHeight = 22 * this.model.items.length;
this.list.layout(listHeight);
this.list.getHTMLElement().style.height = listHeight + 'px';
}
}
interface IExcludeDataItem {
id: string;
pattern: string;
sibling?: string;
}
interface INewExcludeItem {
id: string;
mode: AddItemMode;
}
type IExcludeItem = IExcludeDataItem | INewExcludeItem;
function isExcludeDataItem(excludeItem: IExcludeItem): excludeItem is IExcludeDataItem {
return !!(<IExcludeDataItem>excludeItem).pattern;
}
interface IExcludeDataItemTemplate { this.model.items
container: HTMLElement; .map(item => this.renderItem(item))
.forEach(itemElement => this.listElement.appendChild(itemElement));
actionBar: ActionBar;
patternElement: HTMLElement;
siblingElement: HTMLElement;
toDispose: IDisposable[];
}
class ExcludeDataItemRenderer implements IRenderer<IExcludeDataItem, IExcludeDataItemTemplate> { const listHeight = 22 * this.model.items.length;
static readonly templateId: string = 'excludeDataItem'; this.listElement.style.height = listHeight + 'px';
private readonly _onDidRemoveExclude: Emitter<string> = new Emitter<string>();
public readonly onDidRemoveExclude: Event<string> = this._onDidRemoveExclude.event;
private readonly _onEditExclude: Emitter<string> = new Emitter<string>();
public readonly onEditExclude: Event<string> = this._onEditExclude.event;
get templateId(): string {
return ExcludeDataItemRenderer.templateId;
}
renderTemplate(container: HTMLElement): IExcludeDataItemTemplate {
const toDispose = [];
const actionBar = new ActionBar(container);
toDispose.push(actionBar);
return {
container,
patternElement: DOM.append(container, $('.setting-exclude-pattern')),
siblingElement: DOM.append(container, $('.setting-exclude-sibling')),
toDispose,
actionBar
};
} }
private createDeleteAction(key: string): IAction { private createDeleteAction(key: string): IAction {
...@@ -245,7 +187,7 @@ class ExcludeDataItemRenderer implements IRenderer<IExcludeDataItem, IExcludeDat ...@@ -245,7 +187,7 @@ class ExcludeDataItemRenderer implements IRenderer<IExcludeDataItem, IExcludeDat
enabled: true, enabled: true,
id: 'workbench.action.removeExcludeItem', id: 'workbench.action.removeExcludeItem',
tooltip: localize('removeExcludeItem', "Remove Exclude Item"), tooltip: localize('removeExcludeItem', "Remove Exclude Item"),
run: () => this._onDidRemoveExclude.fire(key) run: () => this._onDidChangeExclude.fire({ originalPattern: key, pattern: undefined })
}; };
} }
...@@ -255,140 +197,111 @@ class ExcludeDataItemRenderer implements IRenderer<IExcludeDataItem, IExcludeDat ...@@ -255,140 +197,111 @@ class ExcludeDataItemRenderer implements IRenderer<IExcludeDataItem, IExcludeDat
enabled: true, enabled: true,
id: 'workbench.action.editExcludeItem', id: 'workbench.action.editExcludeItem',
tooltip: localize('editExcludeItem', "Edit Exclude Item"), tooltip: localize('editExcludeItem', "Edit Exclude Item"),
run: () => this._onEditExclude.fire(key) run: () => { }
}; };
} }
renderElement(element: IExcludeDataItem, index: number, templateData: IExcludeDataItemTemplate): void { private renderItem(item: IExcludeItem): HTMLElement {
templateData.patternElement.textContent = element.pattern; return isExcludeDataItem(item) ?
templateData.siblingElement.textContent = element.sibling && ('when: ' + element.sibling); this.renderDataItem(item) :
this.renderNewItem(item);
templateData.actionBar.clear();
templateData.actionBar.push([
this.createEditAction(element.pattern),
this.createDeleteAction(element.pattern)
], { icon: true, label: false });
templateData.container.title = element.sibling ?
localize('excludeSiblingHintLabel', "Exclude files matching `{0}`, only when a file matching `{1}` is present", element.pattern, element.sibling) :
localize('excludePatternHintLabel', "Exclude files matching `{0}`", element.pattern);
}
disposeElement(element: IExcludeDataItem, index: number, templateData: IExcludeDataItemTemplate): void {
} }
disposeTemplate(templateData: IExcludeDataItemTemplate): void { private renderDataItem(item: IExcludeDataItem): HTMLElement {
dispose(templateData.toDispose); const rowElement = $('.setting-exclude-row');
} const actionBar = new ActionBar(rowElement);
} this.renderedDisposables.push(actionBar);
interface INewExcludeItemTemplate { const patternElement = DOM.append(rowElement, $('.setting-exclude-pattern'));
container: HTMLElement; const siblingElement = DOM.append(rowElement, $('.setting-exclude-sibling'));
patternElement.textContent = item.pattern;
siblingElement.textContent = item.sibling && ('when: ' + item.sibling);
patternInput: InputBox; actionBar.push([
siblingInput: InputBox; this.createEditAction(item.pattern),
toDispose: IDisposable[]; this.createDeleteAction(item.pattern)
} ], { icon: true, label: false });
interface INewExcludeItemEvent {
pattern: string;
sibling?: string;
}
class NewExcludeRenderer implements IRenderer<INewExcludeItem, INewExcludeItemTemplate> {
static readonly templateId: string = 'newExcludeItem';
private readonly _onNewExcludeItem: Emitter<INewExcludeItemEvent> = new Emitter<INewExcludeItemEvent>(); rowElement.title = item.sibling ?
public readonly onNewExcludeItem: Event<INewExcludeItemEvent> = this._onNewExcludeItem.event; localize('excludeSiblingHintLabel', "Exclude files matching `{0}`, only when a file matching `{1}` is present", item.pattern, item.sibling) :
localize('excludePatternHintLabel', "Exclude files matching `{0}`", item.pattern);
constructor( return rowElement;
@IContextViewService private contextViewService: IContextViewService,
@IThemeService private themeService: IThemeService
) {
}
get templateId(): string {
return NewExcludeRenderer.templateId;
} }
renderTemplate(container: HTMLElement): INewExcludeItemTemplate { private renderNewItem(item: INewExcludeItem): HTMLElement {
const toDispose = []; const rowElement = $('.setting-exclude-new-row');
const onKeydown = (e: StandardKeyboardEvent) => { const onKeydown = (e: StandardKeyboardEvent) => {
if (e.equals(KeyCode.Enter)) { if (e.equals(KeyCode.Enter)) {
this._onNewExcludeItem.fire({ this._onDidChangeExclude.fire({
originalPattern: undefined,
pattern: patternInput.value, pattern: patternInput.value,
sibling: siblingInput.value // sibling: siblingInput.value
}); });
} }
}; };
const patternInput = new InputBox(container, this.contextViewService, { const patternInput = new InputBox(rowElement, this.contextViewService, {
placeholder: localize('excludePatternInputPlaceholder', "Exclude Pattern...") placeholder: localize('excludePatternInputPlaceholder', "Exclude Pattern...")
}); });
patternInput.element.classList.add('setting-exclude-patternInput'); patternInput.element.classList.add('setting-exclude-patternInput');
toDispose.push(attachInputBoxStyler(patternInput, this.themeService, { this.renderedDisposables.push(attachInputBoxStyler(patternInput, this.themeService, {
inputBackground: settingsTextInputBackground, inputBackground: settingsTextInputBackground,
inputForeground: settingsTextInputForeground, inputForeground: settingsTextInputForeground,
inputBorder: settingsTextInputBorder inputBorder: settingsTextInputBorder
})); }));
toDispose.push(patternInput); this.renderedDisposables.push(patternInput);
toDispose.push(DOM.addStandardDisposableListener(patternInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown)); this.renderedDisposables.push(DOM.addStandardDisposableListener(patternInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown));
const siblingInput = new InputBox(container, this.contextViewService, { const siblingInput = new InputBox(rowElement, this.contextViewService, {
placeholder: localize('excludeSiblingInputPlaceholder', "When Pattern Is Present...") placeholder: localize('excludeSiblingInputPlaceholder', "When Pattern Is Present...")
}); });
siblingInput.element.classList.add('setting-exclude-siblingInput'); siblingInput.element.classList.add('setting-exclude-siblingInput');
toDispose.push(siblingInput); this.renderedDisposables.push(siblingInput);
toDispose.push(attachInputBoxStyler(siblingInput, this.themeService, { this.renderedDisposables.push(attachInputBoxStyler(siblingInput, this.themeService, {
inputBackground: settingsTextInputBackground, inputBackground: settingsTextInputBackground,
inputForeground: settingsTextInputForeground, inputForeground: settingsTextInputForeground,
inputBorder: settingsTextInputBorder inputBorder: settingsTextInputBorder
})); }));
toDispose.push(DOM.addStandardDisposableListener(siblingInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown)); this.renderedDisposables.push(DOM.addStandardDisposableListener(siblingInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown));
rowElement.classList.add('setting-exclude-newExcludeItem');
rowElement.classList.remove('setting-exclude-newPattern');
rowElement.classList.remove('setting-exclude-newPatternWithSibling');
if (item.mode === AddItemMode.Pattern) {
rowElement.classList.add('setting-exclude-newPattern');
patternInput.focus();
patternInput.select();
} else if (item.mode === AddItemMode.PatternWithSibling) {
rowElement.classList.add('setting-exclude-newPatternWithSibling');
}
return { return rowElement;
container,
patternInput,
siblingInput,
toDispose
};
} }
renderElement(element: INewExcludeItem, index: number, templateData: INewExcludeItemTemplate): void { dispose() {
templateData.container.classList.add('setting-exclude-newExcludeItem'); super.dispose();
this.renderedDisposables = dispose(this.renderedDisposables);
templateData.container.classList.remove('setting-exclude-newPattern');
templateData.container.classList.remove('setting-exclude-newPatternWithSibling');
if (element.mode === AddItemMode.Pattern) {
templateData.container.classList.add('setting-exclude-newPattern');
templateData.patternInput.focus();
templateData.patternInput.select();
} else if (element.mode === AddItemMode.PatternWithSibling) {
templateData.container.classList.add('setting-exclude-newPatternWithSibling');
}
} }
}
disposeElement(element: INewExcludeItem, index: number, templateData: INewExcludeItemTemplate): void { interface IExcludeDataItem {
} id: string;
pattern: string;
sibling?: string;
}
disposeTemplate(templateData: INewExcludeItemTemplate): void { interface INewExcludeItem {
dispose(templateData.toDispose); id: string;
} mode: AddItemMode;
} }
class ExcludeSettingListDelegate implements IVirtualDelegate<IExcludeItem> { type IExcludeItem = IExcludeDataItem | INewExcludeItem;
getHeight(element: IExcludeItem): number {
return 22;
}
getTemplateId(element: IExcludeItem): string { function isExcludeDataItem(excludeItem: IExcludeItem): excludeItem is IExcludeDataItem {
if (isExcludeDataItem(element)) { return !!(<IExcludeDataItem>excludeItem).pattern;
return ExcludeDataItemRenderer.templateId;
} else {
return NewExcludeRenderer.templateId;
}
}
} }
// class EditExcludeItemAction extends Action { // class EditExcludeItemAction extends Action {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册