Table Designer: Move columns UI (#19154)

* format

* added buttons and initial drag plugin

* initial drag and drop working

* add actions and taskbar

* drag and drop bugfix and other changes

* fix few issues

* more changes

* fix all move and insertion issues

* PRcomments

* fit and finish comments

* remove dead code

* bump sts

* add style for object being dragged

* add plugin to copyright filter

* Add to eslintrc

* fix drag contrast ratio

* generalize logic for cell focus

* demo feedback

* feedback

* add action state

* feedback

* remove unncecessary check

* add move actions to context menu

* change to const

* fix bug with tables and fix drop color

Co-authored-by: chgagnon <chgagnon@microsoft.com>
This commit is contained in:
Aditya Bist
2022-05-12 10:49:45 -07:00
committed by GitHub
parent fe7ac55e97
commit 784d8e9e96
12 changed files with 621 additions and 61 deletions

View File

@@ -16,7 +16,7 @@ import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/spl
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IInputBoxStyles, InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
import 'vs/css!./media/designer';
import { ITableStyles } from 'sql/base/browser/ui/table/interfaces';
import { ITableMouseEvent, ITableStyles } from 'sql/base/browser/ui/table/interfaces';
import { IThemable } from 'vs/base/common/styler';
import { Checkbox, ICheckboxStyles } from 'sql/base/browser/ui/checkbox/checkbox';
import { Table } from 'sql/base/browser/ui/table/table';
@@ -33,18 +33,24 @@ import { Codicon } from 'vs/base/common/codicons';
import { Color } from 'vs/base/common/color';
import { LoadingSpinner } from 'sql/base/browser/ui/loadingSpinner/loadingSpinner';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { DesignerIssuesTabPanelView } from 'sql/workbench/browser/designer/designerIssuesTabPanelView';
import { DesignerScriptEditorTabPanelView } from 'sql/workbench/browser/designer/designerScriptEditorTabPanelView';
import { DesignerPropertyPathValidator } from 'sql/workbench/browser/designer/designerPropertyPathValidator';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { listActiveSelectionBackground, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { listActiveSelectionBackground, listActiveSelectionForeground, listHoverBackground } from 'vs/platform/theme/common/colorRegistry';
import { alert } from 'vs/base/browser/ui/aria/aria';
import { layoutDesignerTable, TableHeaderRowHeight, TableRowHeight } from 'sql/workbench/browser/designer/designerTableUtil';
import { Dropdown, IDropdownStyles } from 'sql/base/browser/ui/editableDropdown/browser/dropdown';
import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
import { IAction } from 'vs/base/common/actions';
import { InsertAfterSelectedRowAction, InsertBeforeSelectedRowAction, AddRowAction, DesignerTableActionContext, MoveRowDownAction, MoveRowUpAction, DesignerTableAction } from 'sql/workbench/browser/designer/tableActions';
import { RowMoveManager, RowMoveOnDragEventData } from 'sql/base/browser/ui/table/plugins/rowMoveManager.plugin';
import { ITaskbarContent, Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectionModel.plugin';
import { listFocusAndSelectionBackground } from 'sql/platform/theme/common/colors';
export interface IDesignerStyle {
tabbedPanelStyles?: ITabbedPanelStyles;
@@ -90,12 +96,13 @@ export class Designer extends Disposable implements IThemable {
private _input: DesignerComponentInput;
private _tableCellEditorFactory: TableCellEditorFactory;
private _propertiesPane: DesignerPropertiesPane;
private _buttons: Button[] = [];
private _inputDisposable: DisposableStore;
private _loadingTimeoutHandle: any;
private _groupHeaders: HTMLElement[] = [];
private _issuesView: DesignerIssuesTabPanelView;
private _scriptEditorView: DesignerScriptEditorTabPanelView;
private _taskbars: Taskbar[] = [];
private _actionsMap: Map<Taskbar, DesignerTableAction[]> = new Map<Taskbar, DesignerTableAction[]>();
private _onStyleChangeEventEmitter = new Emitter<void>();
constructor(private readonly _container: HTMLElement,
@@ -103,7 +110,8 @@ export class Designer extends Disposable implements IThemable {
@IContextViewService private readonly _contextViewProvider: IContextViewService,
@INotificationService private readonly _notificationService: INotificationService,
@IDialogService private readonly _dialogService: IDialogService,
@IThemeService private readonly _themeService: IThemeService) {
@IThemeService private readonly _themeService: IThemeService,
@IContextMenuService private readonly _contextMenuService: IContextMenuService,) {
super();
this._tableCellEditorFactory = new TableCellEditorFactory(
{
@@ -209,6 +217,7 @@ export class Designer extends Disposable implements IThemable {
} else if (component instanceof TabbedPanel) {
component.style(this._styles.tabbedPanelStyles);
} else if (component instanceof Table) {
this.removeTableSelectionStyles();
component.style(this._styles.tableStyles);
} else if (component instanceof Button) {
component.style(this._styles.buttonStyles);
@@ -219,6 +228,17 @@ export class Designer extends Disposable implements IThemable {
}
}
private removeTableSelectionStyles(): void {
this._styles.tableStyles.listActiveSelectionBackground = undefined;
this._styles.tableStyles.listActiveSelectionForeground = undefined;
this._styles.tableStyles.listFocusAndSelectionBackground = undefined;
this._styles.tableStyles.listFocusAndSelectionForeground = undefined;
this._styles.tableStyles.listInactiveFocusBackground = undefined;
this._styles.tableStyles.listInactiveFocusForeground = undefined;
this._styles.tableStyles.listInactiveSelectionBackground = undefined;
this._styles.tableStyles.listInactiveSelectionForeground = undefined;
}
private styleGroupHeader(header: HTMLElement): void {
if (this._styles.groupHeaderBackground) {
header.style.backgroundColor = this._styles.groupHeaderBackground.toString();
@@ -243,10 +263,6 @@ export class Designer extends Disposable implements IThemable {
separatorBorder: styles.paneSeparator
});
this._buttons.forEach((button) => {
this.styleComponent(button);
});
this._groupHeaders.forEach((header) => {
this.styleGroupHeader(header);
});
@@ -304,14 +320,13 @@ export class Designer extends Disposable implements IThemable {
}
private clearUI(): void {
this._buttons.forEach(button => button.dispose());
this._buttons = [];
this._componentMap.forEach(item => item.component.dispose());
this._componentMap.clear();
DOM.clearNode(this._topContentContainer);
this._contentTabbedPanel.clearTabs();
this._propertiesPane.clear();
this._groupHeaders = [];
this._taskbars.map(t => t.dispose());
}
private initializeDesigner(): void {
@@ -328,6 +343,27 @@ export class Designer extends Disposable implements IThemable {
this.restoreUIState();
}
private handleCellFocusAfterAddOrMove(edit: DesignerEdit): void {
if (edit.path.length === 2) {
const propertyName = edit.path[0] as string;
const index = edit.type === DesignerEditType.Add ? edit.path[1] as number : edit.value as number;
const table = this._componentMap.get(propertyName).component as Table<Slick.SlickData>;
const tableProperties = this._componentMap.get(propertyName).defintion.componentProperties as DesignerTableProperties;
let selectedCellIndex = tableProperties.itemProperties.findIndex(p => p.componentType === 'input');
selectedCellIndex = tableProperties.canMoveRows ? selectedCellIndex + 1 : selectedCellIndex;
try {
table.grid.resetActiveCell();
table.setActiveCell(index, selectedCellIndex);
table.setSelectedRows([index]);
}
catch {
// Ignore the slick grid error when setting active cell.
}
} else {
this.updatePropertiesPane(this._propertiesPane.objectPath);
}
}
private handleEditProcessedEvent(args: DesignerEditProcessedEventArgs): void {
const edit = args.edit;
this._supressEditProcessing = true;
@@ -344,21 +380,8 @@ export class Designer extends Disposable implements IThemable {
this.updateComponentValues();
this.layoutTabbedPanel();
}
if (edit.type === DesignerEditType.Add) {
// For tables in the main view, move focus to the first cell of the newly added row, and the properties pane will be showing the new object.
if (edit.path.length === 1) {
const propertyName = edit.path[0] as string;
const tableData = this._input.viewModel[propertyName] as DesignerTableProperties;
const table = this._componentMap.get(propertyName).component as Table<Slick.SlickData>;
try {
table.setActiveCell(tableData.data.length - 1, 0);
}
catch {
// Ignore the slick grid error when setting active cell.
}
} else {
this.updatePropertiesPane(this._propertiesPane.objectPath);
}
if (edit.type === DesignerEditType.Add || edit.type === DesignerEditType.Move) {
this.handleCellFocusAfterAddOrMove(edit);
} else if (edit.type === DesignerEditType.Update) {
// for edit, update the properties pane with new values of current object.
this.updatePropertiesPane(this._propertiesPane.objectPath);
@@ -574,7 +597,7 @@ export class Designer extends Disposable implements IThemable {
}
}
private handleEdit(edit: DesignerEdit): void {
public handleEdit(edit: DesignerEdit): void {
if (this._supressEditProcessing) {
return;
}
@@ -797,28 +820,7 @@ export class Designer extends Disposable implements IThemable {
container.appendChild(DOM.$('.full-row')).appendChild(DOM.$('span.component-label')).innerText = componentDefinition.componentProperties?.title ?? '';
}
const tableProperties = componentDefinition.componentProperties as DesignerTableProperties;
if (tableProperties.canAddRows) {
const buttonContainer = container.appendChild(DOM.$('.full-row')).appendChild(DOM.$('.add-row-button-container'));
const addNewText = tableProperties.labelForAddNewButton ?? localize('designer.newRowText', "Add New");
const addRowButton = new Button(buttonContainer, {
title: addNewText,
secondary: true
});
addRowButton.onDidClick(() => {
this.handleEdit({
type: DesignerEditType.Add,
path: propertyPath,
source: view
});
});
this.styleComponent(addRowButton);
addRowButton.label = addNewText;
addRowButton.icon = {
id: `add-row-button new codicon`
};
addRowButton.ariaLabel = localize('designer.newRowButtonAriaLabel', "Add new row to '{0}' table", tableProperties.ariaLabel);
this._buttons.push(addRowButton);
}
const taskbar = this.addTableTaskbar(container, tableProperties);
const tableContainer = container.appendChild(DOM.$('.full-row'));
const table = new Table(tableContainer, {
dataProvider: new TableDataView()
@@ -836,12 +838,55 @@ export class Designer extends Disposable implements IThemable {
headerRowHeight: TableHeaderRowHeight,
editorLock: new Slick.EditorLock()
});
table.grid.setSelectionModel(new RowSelectionModel());
if (taskbar) {
taskbar.context = { table: table, path: propertyPath, source: view };
this._actionsMap.get(taskbar).map(a => a.table = table);
}
const columns: Slick.Column<Slick.SlickData>[] = [];
if (tableProperties.canInsertRows || tableProperties.canMoveRows) {
// Add move context menu actions
this._register(table.onContextMenu((e) => {
this.openContextMenu(table, e, propertyPath, view, tableProperties);
}));
}
if (tableProperties.canMoveRows) {
// Add row move drag and drop
const moveRowsPlugin = new RowMoveManager({
cancelEditOnDrag: true,
id: 'moveRow',
iconCssClass: Codicon.grabber.classNames,
title: localize('designer.moveRowText', 'Move Row'),
width: 20,
resizable: false,
isFontIcon: true,
behavior: 'selectAndMove'
});
table.registerPlugin(moveRowsPlugin);
moveRowsPlugin.onMoveRows.subscribe((e: Slick.EventData, data: RowMoveOnDragEventData) => {
const row = data.rows[0];
// no point in moving before or after itself
if (row === data.insertBefore || row === data.insertBefore - 1) {
e.stopPropagation();
return;
}
this.handleEdit({
type: DesignerEditType.Move,
path: [...propertyPath, row],
source: view,
value: data.insertBefore < row ? data.insertBefore : data.insertBefore - 1
});
});
table.grid.registerPlugin(moveRowsPlugin);
columns.push(moveRowsPlugin.definition);
}
table.ariaLabel = tableProperties.ariaLabel;
const columns = tableProperties.columns.map(propName => {
columns.push(...tableProperties.columns.map((propName, index) => {
const propertyDefinition = tableProperties.itemProperties.find(item => item.propertyName === propName);
switch (propertyDefinition.componentType) {
case 'checkbox':
const checkboxColumn = new CheckBoxColumn({
id: index.toString(),
field: propertyDefinition.propertyName,
name: propertyDefinition.componentProperties.title,
width: propertyDefinition.componentProperties.width as number
@@ -859,6 +904,7 @@ export class Designer extends Disposable implements IThemable {
case 'dropdown':
const dropdownProperties = propertyDefinition.componentProperties as DropDownProperties;
return {
id: index.toString(),
name: dropdownProperties.title,
field: propertyDefinition.propertyName,
editor: this._tableCellEditorFactory.getDropdownEditorClass({ view: view, path: propertyPath }, dropdownProperties.values as string[], dropdownProperties.isEditable),
@@ -867,13 +913,14 @@ export class Designer extends Disposable implements IThemable {
default:
const inputProperties = propertyDefinition.componentProperties as InputBoxProperties;
return {
id: index.toString(),
name: inputProperties.title,
field: propertyDefinition.propertyName,
editor: this._tableCellEditorFactory.getTextEditorClass({ view: view, path: propertyPath }, inputProperties.inputType),
width: inputProperties.width as number
};
}
});
}));
if (tableProperties.canRemoveRows) {
const deleteRowColumn = new ButtonColumn({
id: 'deleteRow',
@@ -908,7 +955,10 @@ export class Designer extends Disposable implements IThemable {
table.grid.onBeforeEditCell.subscribe((e, data): boolean => {
return data.item[data.column.field].enabled !== false;
});
let currentTableActions = [];
if (taskbar) {
currentTableActions = this._actionsMap.get(taskbar);
}
table.grid.onActiveCellChanged.subscribe((e, data) => {
if (view === 'TabsView' || view === 'TopContentView') {
if (data.row !== undefined) {
@@ -925,12 +975,20 @@ export class Designer extends Disposable implements IThemable {
this._propertiesPane.updateDescription(componentDefinition);
}
}
if (data.row !== undefined) {
currentTableActions.forEach(a => a.updateState(data.row));
table.grid.setSelectedRows([data.row]);
}
});
table.onBlur((e) => {
currentTableActions.forEach(a => a.updateState());
table.grid.setSelectedRows([]);
table.grid.resetActiveCell();
});
component = table;
break;
default:
throw new Error(localize('tableDesigner.unknownComponentType', "The component type: {0} is not supported", componentDefinition.componentType));
throw new Error(localize('designer.unknownComponentType', "The component type: {0} is not supported", componentDefinition.componentType));
}
componentMap.set(componentDefinition.propertyName, {
defintion: componentDefinition,
@@ -941,6 +999,78 @@ export class Designer extends Disposable implements IThemable {
return component;
}
private addTableTaskbar(container: HTMLElement, tableProperties: DesignerTableProperties): Taskbar | undefined {
if (tableProperties.canAddRows || tableProperties.canMoveRows) {
const taskbarContainer = container.appendChild(DOM.$('.full-row')).appendChild(DOM.$('.add-row-button-container'));
const taskbar = new Taskbar(taskbarContainer);
const actions = [];
if (tableProperties.canAddRows) {
const addRowAction = this._instantiationService.createInstance(AddRowAction, this, tableProperties);
actions.push(addRowAction);
}
if (tableProperties.canMoveRows) {
const moveUpAction = this._instantiationService.createInstance(MoveRowUpAction, this);
const moveDownAction = this._instantiationService.createInstance(MoveRowDownAction, this);
actions.push(moveUpAction);
actions.push(moveDownAction);
}
const taskbarContent: ITaskbarContent[] = actions.map((a) => { return { action: a }; });
taskbar.setContent(taskbarContent);
this._actionsMap.set(taskbar, actions);
return taskbar;
}
return undefined;
}
private openContextMenu(
table: Table<Slick.SlickData>,
event: ITableMouseEvent,
propertyPath: DesignerPropertyPath,
view: DesignerUIArea,
tableProperties: DesignerTableProperties
): void {
const rowIndex = event.cell.row;
const tableActionContext: DesignerTableActionContext = {
table: table,
path: propertyPath,
source: view,
selectedRow: rowIndex
};
const data = table.grid.getData() as Slick.DataProvider<Slick.SlickData>;
if (!data || rowIndex >= data.getLength()) {
return undefined;
}
const actions = this.getTableActions(tableProperties);
actions.forEach(a => {
if (a instanceof DesignerTableAction) {
a.table = table;
a.updateState(rowIndex);
}
});
this._contextMenuService.showContextMenu({
getAnchor: () => event.anchor,
getActions: () => actions,
getActionsContext: () => (tableActionContext)
});
}
private getTableActions(tableProperties: DesignerTableProperties): IAction[] {
const actions: IAction[] = [];
if (tableProperties.canInsertRows) {
const insertRowBefore = this._instantiationService.createInstance(InsertBeforeSelectedRowAction, this);
const insertRowAfter = this._instantiationService.createInstance(InsertAfterSelectedRowAction, this);
actions.push(insertRowBefore);
actions.push(insertRowAfter);
}
if (tableProperties.canMoveRows) {
const moveRowUp = this._instantiationService.createInstance(MoveRowUpAction, this);
const moveRowDown = this._instantiationService.createInstance(MoveRowDownAction, this);
actions.push(moveRowUp);
actions.push(moveRowDown);
}
return actions;
}
private startLoading(message: string, timeout: number): void {
this._loadingTimeoutHandle = setTimeout(() => {
this._loadingSpinner.loadingMessage = message;
@@ -979,3 +1109,27 @@ export class Designer extends Disposable implements IThemable {
}
}
}
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
const listHoverBackgroundColor = theme.getColor(listHoverBackground);
const listActiveSelectionBackgroundColor = theme.getColor(listActiveSelectionBackground);
const listFocusSelectionBackgroundColor = theme.getColor(listFocusAndSelectionBackground);
if (listHoverBackgroundColor) {
collector.addRule(`
.designer-component .slick-cell.isDragging {
background-color: ${listHoverBackgroundColor};
}
.designer-component .slick-reorder-proxy {
background: ${listActiveSelectionBackgroundColor};
opacity: 0.5;
}
.vs-dark .designer-component .slick-reorder-proxy {
opacity: 0.3;
}
.designer-component .slick-reorder-guide {
background: ${listFocusSelectionBackgroundColor};
opacity: 1;
}
`);
}
});

View File

@@ -187,6 +187,14 @@ export interface DesignerTableProperties extends ComponentProperties {
* Whether user can remove rows from the table. The default value is true.
*/
canRemoveRows?: boolean;
/**
* Whether user can move rows from one index to another. The default value is false.
*/
canMoveRows?: boolean;
/**
* Whether user can insert rows at a given index to the table. The default value is false.
*/
canInsertRows?: boolean;
/**
* Whether to show confirmation when user removes a row. The default value is false.
*/
@@ -214,7 +222,8 @@ export interface DesignerTableComponentRowData {
export enum DesignerEditType {
Add = 0,
Remove = 1,
Update = 2
Update = 2,
Move = 3
}
export interface DesignerEdit {

View File

@@ -114,10 +114,13 @@
width: fit-content;
background-repeat: no-repeat;
background-size: 13px;
padding-left: 17px;
background-position: 2px center;
}
.designer-component .add-row-button-container .actions-container {
padding-left: 0px !important;
}
.designer-component .top-content-container .components-grid {
padding-bottom: 10px;
}
@@ -152,3 +155,7 @@
padding-top: 10px;
padding-left: 25px;
}
.designer-component .codicon-grabber {
cursor: move;
}

View File

@@ -0,0 +1,194 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Table } from 'sql/base/browser/ui/table/table';
import { Designer } from 'sql/workbench/browser/designer/designer';
import { DesignerEditType, DesignerPropertyPath, DesignerTableProperties, DesignerUIArea } from 'sql/workbench/browser/designer/interfaces';
import { Action } from 'vs/base/common/actions';
import { localize } from 'vs/nls';
export interface DesignerTableActionContext {
table: Table<Slick.SlickData>;
path: DesignerPropertyPath;
source: DesignerUIArea;
selectedRow?: number;
}
export class DesignerTableAction extends Action {
protected _table: Table<Slick.SlickData>;
constructor(
id: string,
label: string,
icon: string,
protected needsRowSelection: boolean
) {
super(id, label, icon);
}
public set table(table: Table<Slick.SlickData>) {
this._table = table;
}
public updateState(row?: number) {
if (row === undefined) {
if (!this.needsRowSelection) {
this.enabled = true;
} else {
this.enabled = false;
}
}
}
}
export class AddRowAction extends DesignerTableAction {
public static ID = 'designer.addRowAction';
public static ICON = 'add-row-button new codicon';
public static LABEL = localize('designer.addColumnAction', 'Add New');
constructor(
private designer: Designer,
tableProperties: DesignerTableProperties,
) {
super(AddRowAction.ID, tableProperties.labelForAddNewButton || AddRowAction.LABEL, AddRowAction.ICON, false);
this.designer = designer;
this._tooltip = localize('designer.newRowButtonAriaLabel', "Add new row to '{0}' table", tableProperties.ariaLabel);
}
public override async run(context: DesignerTableActionContext): Promise<void> {
const lastIndex = context.table.getData().getItems().length;
return new Promise((resolve) => {
this.designer.handleEdit({
type: DesignerEditType.Add,
path: [...context.path, lastIndex],
source: context.source,
});
resolve();
});
}
}
export class MoveRowUpAction extends DesignerTableAction {
public static ID = 'designer.moveRowUpAction';
public static ICON = 'move-row-up-button arrow-up codicon';
public static LABEL = localize('designer.moveRowUpAction', 'Move Up');
constructor(private designer: Designer) {
super(MoveRowUpAction.ID, MoveRowUpAction.LABEL, MoveRowUpAction.ICON, true);
this.designer = designer;
this._tooltip = localize('designer.moveRowUpButtonAriaLabel', "Move selected row up one position");
this.enabled = false;
}
public override async run(context: DesignerTableActionContext): Promise<void> {
let rowIndex = context.selectedRow ?? context.table.getSelectedRows()[0];
if (rowIndex - 1 < 0) {
return;
}
return new Promise((resolve) => {
this.designer.handleEdit({
type: DesignerEditType.Move,
path: [...context.path, rowIndex],
source: context.source,
value: rowIndex - 1
});
resolve();
});
}
public override updateState(row?: number): void {
if (row === 0) {
this.enabled = false;
} else {
this.enabled = true;
}
super.updateState(row);
}
}
export class MoveRowDownAction extends DesignerTableAction {
public static ID = 'designer.moveRowDownAction';
public static ICON = 'move-row-down-button arrow-down codicon';
public static LABEL = localize('designer.moveRowDownAction', 'Move Down');
constructor(private designer: Designer) {
super(MoveRowDownAction.ID, MoveRowDownAction.LABEL, MoveRowDownAction.ICON, true);
this.designer = designer;
this._tooltip = localize('designer.moveRowDownButtonAriaLabel', "Move selected row down one position");
this.enabled = false;
}
public override async run(context: DesignerTableActionContext): Promise<void> {
let rowIndex = context.selectedRow ?? context.table.getSelectedRows()[0];
const tableData = context.table.getData().getItems();
if (rowIndex + 1 >= tableData.length) {
return;
}
return new Promise((resolve) => {
this.designer.handleEdit({
type: DesignerEditType.Move,
path: [...context.path, rowIndex],
source: context.source,
value: rowIndex + 1
});
resolve();
});
}
public override updateState(row?: number): void {
super.updateState(row);
if (row === this._table.getData().getLength() - 1) {
this.enabled = false;
} else {
this.enabled = true;
}
super.updateState(row);
}
}
export class InsertBeforeSelectedRowAction extends Action {
public static ID = 'designer.insertBeforeSelectedRow';
public static LABEL = localize('designer.insertBeforeSelectedRow', 'Insert before');
constructor(private designer: Designer) {
super(InsertBeforeSelectedRowAction.ID, InsertBeforeSelectedRowAction.LABEL, 'insertBeforeSelectedRow', true);
this.designer = designer;
}
public override async run(context: DesignerTableActionContext): Promise<void> {
const rowIndex = context.selectedRow;
return new Promise((resolve) => {
this.designer.handleEdit({
type: DesignerEditType.Add,
path: [...context.path, rowIndex],
source: context.source
});
resolve();
});
}
}
export class InsertAfterSelectedRowAction extends Action {
public static ID = 'designer.insertAfterSelectedColumn';
public static LABEL = localize('designer.insertAfterSelectedColumn', 'Insert after');
constructor(private designer: Designer) {
super(InsertAfterSelectedRowAction.ID, InsertAfterSelectedRowAction.LABEL, 'insertAfterSelectedColumn', true);
}
public override async run(context: DesignerTableActionContext): Promise<void> {
const rowIndex = context.selectedRow;
return new Promise((resolve) => {
this.designer.handleEdit({
type: DesignerEditType.Add,
path: [...context.path, rowIndex + 1],
source: context.source
});
resolve();
});
}
}