mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
add designer property grouping support (#17485)
* add properties grouping support * revert unexpected changes * use common color
This commit is contained in:
@@ -37,11 +37,12 @@ export interface IDesignerStyle {
|
|||||||
checkboxStyles?: ICheckboxStyles;
|
checkboxStyles?: ICheckboxStyles;
|
||||||
buttonStyles?: IButtonStyles;
|
buttonStyles?: IButtonStyles;
|
||||||
paneSeparator?: Color;
|
paneSeparator?: Color;
|
||||||
|
groupHeaderBackground?: Color;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DesignerUIComponent = InputBox | Checkbox | Table<Slick.SlickData> | SelectBox;
|
export type DesignerUIComponent = InputBox | Checkbox | Table<Slick.SlickData> | SelectBox;
|
||||||
|
|
||||||
export type CreateComponentFunc = (container: HTMLElement, component: DesignerDataPropertyInfo, editIdentifier: DesignerEditIdentifier) => DesignerUIComponent;
|
export type CreateComponentsFunc = (container: HTMLElement, components: DesignerDataPropertyInfo[], editIdentifierGetter: (property: DesignerDataPropertyInfo) => DesignerEditIdentifier) => DesignerUIComponent[];
|
||||||
export type SetComponentValueFunc = (definition: DesignerDataPropertyInfo, component: DesignerUIComponent, data: DesignerViewModel) => void;
|
export type SetComponentValueFunc = (definition: DesignerDataPropertyInfo, component: DesignerUIComponent, data: DesignerViewModel) => void;
|
||||||
|
|
||||||
export class Designer extends Disposable implements IThemable {
|
export class Designer extends Disposable implements IThemable {
|
||||||
@@ -65,6 +66,7 @@ export class Designer extends Disposable implements IThemable {
|
|||||||
private _buttons: Button[] = [];
|
private _buttons: Button[] = [];
|
||||||
private _inputDisposable: DisposableStore;
|
private _inputDisposable: DisposableStore;
|
||||||
private _loadingTimeoutHandle: any;
|
private _loadingTimeoutHandle: any;
|
||||||
|
private _groupHeaders: HTMLElement[] = [];
|
||||||
|
|
||||||
constructor(private readonly _container: HTMLElement,
|
constructor(private readonly _container: HTMLElement,
|
||||||
private readonly _contextViewProvider: IContextViewProvider) {
|
private readonly _contextViewProvider: IContextViewProvider) {
|
||||||
@@ -143,12 +145,10 @@ export class Designer extends Disposable implements IThemable {
|
|||||||
onDidChange: Event.None
|
onDidChange: Event.None
|
||||||
}, Sizing.Distribute);
|
}, Sizing.Distribute);
|
||||||
|
|
||||||
this._propertiesPane = new DesignerPropertiesPane(this._propertiesPaneContainer, (container, component, identifier) => {
|
this._propertiesPane = new DesignerPropertiesPane(this._propertiesPaneContainer, (container, components, identifierGetter) => {
|
||||||
return this.createComponent(container, component, identifier, false, false);
|
return this.createComponents(container, components, this._propertiesPane.componentMap, this._propertiesPane.groupHeaders, identifierGetter, false, true);
|
||||||
}, (definition, component, viewModel) => {
|
}, (definition, component, viewModel) => {
|
||||||
this.setComponentValue(definition, component, viewModel);
|
this.setComponentValue(definition, component, viewModel);
|
||||||
}, (component) => {
|
|
||||||
this.styleComponent(component);
|
|
||||||
});
|
});
|
||||||
const editor = DOM.$('div');
|
const editor = DOM.$('div');
|
||||||
editor.innerText = 'script pane placeholder';
|
editor.innerText = 'script pane placeholder';
|
||||||
@@ -171,6 +171,12 @@ export class Designer extends Disposable implements IThemable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private styleGroupHeader(header: HTMLElement): void {
|
||||||
|
if (this._styles.groupHeaderBackground) {
|
||||||
|
header.style.backgroundColor = this._styles.groupHeaderBackground.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public style(styles: IDesignerStyle): void {
|
public style(styles: IDesignerStyle): void {
|
||||||
this._styles = styles;
|
this._styles = styles;
|
||||||
this._componentMap.forEach((value, key, map) => {
|
this._componentMap.forEach((value, key, map) => {
|
||||||
@@ -178,7 +184,9 @@ export class Designer extends Disposable implements IThemable {
|
|||||||
this.styleComponent(value.component);
|
this.styleComponent(value.component);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this._propertiesPane.style();
|
this._propertiesPane.componentMap.forEach((value) => {
|
||||||
|
this.styleComponent(value.component);
|
||||||
|
});
|
||||||
this._verticalSplitView.style({
|
this._verticalSplitView.style({
|
||||||
separatorBorder: styles.paneSeparator
|
separatorBorder: styles.paneSeparator
|
||||||
});
|
});
|
||||||
@@ -190,6 +198,14 @@ export class Designer extends Disposable implements IThemable {
|
|||||||
this._buttons.forEach((button) => {
|
this._buttons.forEach((button) => {
|
||||||
this.styleComponent(button);
|
this.styleComponent(button);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._groupHeaders.forEach((header) => {
|
||||||
|
this.styleGroupHeader(header);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._propertiesPane.groupHeaders.forEach((header) => {
|
||||||
|
this.styleGroupHeader(header);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public layout(dimension: DOM.Dimension) {
|
public layout(dimension: DOM.Dimension) {
|
||||||
@@ -214,6 +230,7 @@ export class Designer extends Disposable implements IThemable {
|
|||||||
this._tabbedPanel.clearTabs();
|
this._tabbedPanel.clearTabs();
|
||||||
this._propertiesPane.clear();
|
this._propertiesPane.clear();
|
||||||
this._inputDisposable?.dispose();
|
this._inputDisposable?.dispose();
|
||||||
|
this._groupHeaders = [];
|
||||||
|
|
||||||
|
|
||||||
// Initialize with new input
|
// Initialize with new input
|
||||||
@@ -247,9 +264,7 @@ export class Designer extends Disposable implements IThemable {
|
|||||||
private initializeDesigner(): void {
|
private initializeDesigner(): void {
|
||||||
const view = this._input.view;
|
const view = this._input.view;
|
||||||
if (view.components) {
|
if (view.components) {
|
||||||
view.components.forEach(component => {
|
this.createComponents(this._topContentContainer, view.components, this._componentMap, this._groupHeaders, component => component.propertyName, true, false);
|
||||||
this.createComponent(this._topContentContainer, component, component.propertyName, true, true);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
view.tabs.forEach(tab => {
|
view.tabs.forEach(tab => {
|
||||||
this._tabbedPanel.pushTab(this.createTabView(tab));
|
this._tabbedPanel.pushTab(this.createTabView(tab));
|
||||||
@@ -415,8 +430,8 @@ export class Designer extends Disposable implements IThemable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createTabView(tab: DesignerTab): IPanelTab {
|
private createTabView(tab: DesignerTab): IPanelTab {
|
||||||
const view = new DesignerTabPanelView(tab, (container, component, identifier) => {
|
const view = new DesignerTabPanelView(tab, (container, components, identifierGetter) => {
|
||||||
return this.createComponent(container, component, identifier, true, false);
|
return this.createComponents(container, components, this._componentMap, this._groupHeaders, identifierGetter, true, false);
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
identifier: tab.title,
|
identifier: tab.title,
|
||||||
@@ -487,7 +502,47 @@ export class Designer extends Disposable implements IThemable {
|
|||||||
this._supressEditProcessing = false;
|
this._supressEditProcessing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createComponent(container: HTMLElement, componentDefinition: DesignerDataPropertyInfo, editIdentifier: DesignerEditIdentifier, addToComponentMap: boolean, setWidth: boolean): DesignerUIComponent {
|
private createComponents(container: HTMLElement,
|
||||||
|
components: DesignerDataPropertyInfo[],
|
||||||
|
componentMap: Map<string, { defintion: DesignerDataPropertyInfo, component: DesignerUIComponent }>,
|
||||||
|
groupHeaders: HTMLElement[],
|
||||||
|
identifierGetter: (definition: DesignerDataPropertyInfo) => DesignerEditIdentifier,
|
||||||
|
setWidth: boolean, skipTableCreation: boolean = false): DesignerUIComponent[] {
|
||||||
|
const uiComponents = [];
|
||||||
|
const groupNames = [];
|
||||||
|
const componentsToCreate = skipTableCreation ? components.filter(component => component.componentType !== 'table') : components;
|
||||||
|
componentsToCreate.forEach(component => {
|
||||||
|
if (component.group && groupNames.indexOf(component.group) === -1) {
|
||||||
|
groupNames.push(component.group);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// only show groups when there are multiple of them.
|
||||||
|
if (groupNames.length < 2) {
|
||||||
|
componentsToCreate.forEach(component => {
|
||||||
|
uiComponents.push(this.createComponent(container, component, identifierGetter(component), componentMap, setWidth));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
groupNames.forEach(group => {
|
||||||
|
const groupHeader = container.appendChild(DOM.$('div.full-row'));
|
||||||
|
groupHeaders.push(groupHeader);
|
||||||
|
this.styleGroupHeader(groupHeader);
|
||||||
|
groupHeader.innerText = group;
|
||||||
|
componentsToCreate.forEach(component => {
|
||||||
|
if (component.group === group) {
|
||||||
|
uiComponents.push(this.createComponent(container, component, identifierGetter(component), componentMap, setWidth));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return uiComponents;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createComponent(container: HTMLElement,
|
||||||
|
componentDefinition: DesignerDataPropertyInfo,
|
||||||
|
editIdentifier: DesignerEditIdentifier,
|
||||||
|
componentMap: Map<string, { defintion: DesignerDataPropertyInfo, component: DesignerUIComponent }>,
|
||||||
|
setWidth: boolean): DesignerUIComponent {
|
||||||
let component: DesignerUIComponent;
|
let component: DesignerUIComponent;
|
||||||
switch (componentDefinition.componentType) {
|
switch (componentDefinition.componentType) {
|
||||||
case 'input':
|
case 'input':
|
||||||
@@ -642,12 +697,11 @@ export class Designer extends Disposable implements IThemable {
|
|||||||
default:
|
default:
|
||||||
throw new Error(localize('tableDesigner.unknownComponentType', "The component type: {0} is not supported", componentDefinition.componentType));
|
throw new Error(localize('tableDesigner.unknownComponentType', "The component type: {0} is not supported", componentDefinition.componentType));
|
||||||
}
|
}
|
||||||
if (addToComponentMap) {
|
componentMap.set(componentDefinition.propertyName, {
|
||||||
this._componentMap.set(componentDefinition.propertyName, {
|
|
||||||
defintion: componentDefinition,
|
defintion: componentDefinition,
|
||||||
component: component
|
component: component
|
||||||
});
|
});
|
||||||
}
|
|
||||||
this.styleComponent(component);
|
this.styleComponent(component);
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { CreateComponentFunc, DesignerUIComponent, SetComponentValueFunc } from 'sql/base/browser/ui/designer/designer';
|
import { CreateComponentsFunc, DesignerUIComponent, SetComponentValueFunc } from 'sql/base/browser/ui/designer/designer';
|
||||||
import { DesignerViewModel, DesignerEditIdentifier, DesignerDataPropertyInfo, InputBoxProperties, NameProperty } from 'sql/base/browser/ui/designer/interfaces';
|
import { DesignerViewModel, DesignerDataPropertyInfo, InputBoxProperties, NameProperty } from 'sql/base/browser/ui/designer/interfaces';
|
||||||
import * as DOM from 'vs/base/browser/dom';
|
import * as DOM from 'vs/base/browser/dom';
|
||||||
import { equals } from 'vs/base/common/objects';
|
import { equals } from 'vs/base/common/objects';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
@@ -26,14 +26,23 @@ export class DesignerPropertiesPane {
|
|||||||
private _contentElement: HTMLElement;
|
private _contentElement: HTMLElement;
|
||||||
private _currentContext?: PropertiesPaneObjectContext;
|
private _currentContext?: PropertiesPaneObjectContext;
|
||||||
private _componentMap = new Map<string, { defintion: DesignerDataPropertyInfo, component: DesignerUIComponent }>();
|
private _componentMap = new Map<string, { defintion: DesignerDataPropertyInfo, component: DesignerUIComponent }>();
|
||||||
|
private _groupHeaders: HTMLElement[] = [];
|
||||||
|
|
||||||
constructor(container: HTMLElement, private _createComponent: CreateComponentFunc, private _setComponentValue: SetComponentValueFunc, private _styleComponent: (component: DesignerUIComponent) => void) {
|
constructor(container: HTMLElement, private _createComponents: CreateComponentsFunc, private _setComponentValue: SetComponentValueFunc) {
|
||||||
const titleContainer = container.appendChild(DOM.$('.title-container'));
|
const titleContainer = container.appendChild(DOM.$('.title-container'));
|
||||||
this._titleElement = titleContainer.appendChild(DOM.$('div'));
|
this._titleElement = titleContainer.appendChild(DOM.$('div'));
|
||||||
this._contentElement = container.appendChild(DOM.$('.properties-content.components-grid'));
|
this._contentElement = container.appendChild(DOM.$('.properties-content.components-grid'));
|
||||||
this._titleElement.innerText = localize('tableDesigner.propertiesPaneTitle', "Properties");
|
this._titleElement.innerText = localize('tableDesigner.propertiesPaneTitle', "Properties");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get groupHeaders(): HTMLElement[] {
|
||||||
|
return this._groupHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get componentMap(): Map<string, { defintion: DesignerDataPropertyInfo, component: DesignerUIComponent }> {
|
||||||
|
return this._componentMap;
|
||||||
|
}
|
||||||
|
|
||||||
public get context(): PropertiesPaneObjectContext | undefined {
|
public get context(): PropertiesPaneObjectContext | undefined {
|
||||||
return this._currentContext;
|
return this._currentContext;
|
||||||
}
|
}
|
||||||
@@ -43,34 +52,21 @@ export class DesignerPropertiesPane {
|
|||||||
value.component.dispose();
|
value.component.dispose();
|
||||||
});
|
});
|
||||||
this._componentMap.clear();
|
this._componentMap.clear();
|
||||||
|
this._groupHeaders = [];
|
||||||
DOM.clearNode(this._contentElement);
|
DOM.clearNode(this._contentElement);
|
||||||
this._currentContext = undefined;
|
this._currentContext = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public style() {
|
|
||||||
this._componentMap.forEach((value) => {
|
|
||||||
this._styleComponent(value.component);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public show(item: ObjectInfo): void {
|
public show(item: ObjectInfo): void {
|
||||||
if (!equals(item.context, this._currentContext)) {
|
if (!equals(item.context, this._currentContext)) {
|
||||||
this.clear();
|
this.clear();
|
||||||
this._currentContext = item.context;
|
this._currentContext = item.context;
|
||||||
item.components.forEach((value) => {
|
this._createComponents(this._contentElement, item.components, (property) => {
|
||||||
// todo: handle table type in properties pane
|
return this._currentContext === 'root' ? property.propertyName : {
|
||||||
if (value.componentType !== 'table') {
|
|
||||||
const editIdentifier: DesignerEditIdentifier = this._currentContext === 'root' ? value.propertyName : {
|
|
||||||
parentProperty: this._currentContext.parentProperty,
|
parentProperty: this._currentContext.parentProperty,
|
||||||
index: this._currentContext.index,
|
index: this._currentContext.index,
|
||||||
property: value.propertyName
|
property: property.propertyName
|
||||||
};
|
};
|
||||||
const component = this._createComponent(this._contentElement, value, editIdentifier);
|
|
||||||
this._componentMap.set(value.propertyName, {
|
|
||||||
component: component,
|
|
||||||
defintion: value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const name = (<InputBoxProperties>item.viewModel[NameProperty])?.value ?? '';
|
const name = (<InputBoxProperties>item.viewModel[NameProperty])?.value ?? '';
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { IPanelView } from 'sql/base/browser/ui/panel/panel';
|
|||||||
import { Table } from 'sql/base/browser/ui/table/table';
|
import { Table } from 'sql/base/browser/ui/table/table';
|
||||||
import { Disposable } from 'vs/base/common/lifecycle';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
import * as DOM from 'vs/base/browser/dom';
|
import * as DOM from 'vs/base/browser/dom';
|
||||||
import { CreateComponentFunc } from 'sql/base/browser/ui/designer/designer';
|
import { CreateComponentsFunc } from 'sql/base/browser/ui/designer/designer';
|
||||||
|
|
||||||
const ButtonHeight = 30;
|
const ButtonHeight = 30;
|
||||||
const HorizontalPadding = 10;
|
const HorizontalPadding = 10;
|
||||||
@@ -17,13 +17,13 @@ const VerticalPadding = 20;
|
|||||||
export class DesignerTabPanelView extends Disposable implements IPanelView {
|
export class DesignerTabPanelView extends Disposable implements IPanelView {
|
||||||
private _componentsContainer: HTMLElement;
|
private _componentsContainer: HTMLElement;
|
||||||
private _tables: Table<Slick.SlickData>[] = [];
|
private _tables: Table<Slick.SlickData>[] = [];
|
||||||
constructor(private readonly _tab: DesignerTab, private _createComponent: CreateComponentFunc) {
|
constructor(private readonly _tab: DesignerTab, private _createComponents: CreateComponentsFunc) {
|
||||||
super();
|
super();
|
||||||
this._componentsContainer = DOM.$('.components-grid');
|
this._componentsContainer = DOM.$('.components-grid');
|
||||||
this._tab.components.forEach(componentDefition => {
|
const uiComponents = this._createComponents(this._componentsContainer, this._tab.components, component => component.propertyName);
|
||||||
const component = this._createComponent(this._componentsContainer, componentDefition, componentDefition.propertyName);
|
uiComponents.forEach(component => {
|
||||||
if (componentDefition.componentType === 'table') {
|
if (component instanceof Table) {
|
||||||
this._tables.push(component as Table<Slick.SlickData>);
|
this._tables.push(component);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import { contrastBorder, registerColor } from 'vs/platform/theme/common/colorReg
|
|||||||
import { Color, RGBA } from 'vs/base/common/color';
|
import { Color, RGBA } from 'vs/base/common/color';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
|
|
||||||
|
// Common
|
||||||
|
export const GroupHeaderBackground = registerColor('groupHeaderBackground', { dark: '#252526', light: '#F3F3F3', hc: '#000000' }, nls.localize('groupHeaderBackground', "Background color of the group header."));
|
||||||
|
|
||||||
// -- Welcome Page Colors
|
// -- Welcome Page Colors
|
||||||
export const tileBoxShadowColor = new Color(new RGBA(0, 1, 4, 0.13));
|
export const tileBoxShadowColor = new Color(new RGBA(0, 1, 4, 0.13));
|
||||||
export const textShadow = new Color(new RGBA(0, 0, 0, 0.25));
|
export const textShadow = new Color(new RGBA(0, 0, 0, 0.25));
|
||||||
|
|||||||
@@ -385,7 +385,8 @@ export function attachDesignerStyler(widget: any, themeService: IThemeService):
|
|||||||
tableStyles: tableStyles,
|
tableStyles: tableStyles,
|
||||||
checkboxStyles: checkboxStyles,
|
checkboxStyles: checkboxStyles,
|
||||||
buttonStyles: buttonStyles,
|
buttonStyles: buttonStyles,
|
||||||
paneSeparator: cr.resolveColorValue(sqlcr.DesignerPaneSeparator, colorTheme)
|
paneSeparator: cr.resolveColorValue(sqlcr.DesignerPaneSeparator, colorTheme),
|
||||||
|
groupHeaderBackground: cr.resolveColorValue(sqlcr.GroupHeaderBackground, colorTheme)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
|||||||
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
|
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
|
||||||
import { ServiceOptionType } from 'sql/platform/connection/common/interfaces';
|
import { ServiceOptionType } from 'sql/platform/connection/common/interfaces';
|
||||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
||||||
|
import { GroupHeaderBackground } from 'sql/platform/theme/common/colorRegistry';
|
||||||
|
|
||||||
export interface IOptionsDialogOptions extends IModalOptions {
|
export interface IOptionsDialogOptions extends IModalOptions {
|
||||||
cancelLabel?: string;
|
cancelLabel?: string;
|
||||||
@@ -99,7 +100,7 @@ export class OptionsDialog extends Modal {
|
|||||||
private updateTheme(theme: IColorTheme): void {
|
private updateTheme(theme: IColorTheme): void {
|
||||||
const borderColor = theme.getColor(contrastBorder);
|
const borderColor = theme.getColor(contrastBorder);
|
||||||
const border = borderColor ? borderColor.toString() : '';
|
const border = borderColor ? borderColor.toString() : '';
|
||||||
const backgroundColor = theme.getColor(SIDE_BAR_BACKGROUND);
|
const backgroundColor = theme.getColor(GroupHeaderBackground);
|
||||||
if (this._dividerBuilder) {
|
if (this._dividerBuilder) {
|
||||||
this._dividerBuilder.style.borderTopWidth = border ? '1px' : '';
|
this._dividerBuilder.style.borderTopWidth = border ? '1px' : '';
|
||||||
this._dividerBuilder.style.borderTopStyle = border ? 'solid' : '';
|
this._dividerBuilder.style.borderTopStyle = border ? 'solid' : '';
|
||||||
|
|||||||
Reference in New Issue
Block a user