mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Add background color for null cell in query editor result grid (#22370)
This commit is contained in:
@@ -85,7 +85,7 @@ export function hyperLinkFormatter(row: number | undefined, cell: any | undefine
|
|||||||
/**
|
/**
|
||||||
* Format all text to replace all new lines with spaces and performs HTML entity encoding
|
* Format all text to replace all new lines with spaces and performs HTML entity encoding
|
||||||
*/
|
*/
|
||||||
export function textFormatter(row: number | undefined, cell: any | undefined, value: any, columnDef: any | undefined, dataContext: any | undefined): string {
|
export function textFormatter(row: number | undefined, cell: any | undefined, value: any, columnDef: any | undefined, dataContext: any | undefined, addClasses?: string): string | { text: string, addClasses: string } {
|
||||||
let cellClasses = 'grid-cell-value-container';
|
let cellClasses = 'grid-cell-value-container';
|
||||||
let valueToDisplay = '';
|
let valueToDisplay = '';
|
||||||
let titleValue = '';
|
let titleValue = '';
|
||||||
@@ -122,7 +122,13 @@ export function textFormatter(row: number | undefined, cell: any | undefined, va
|
|||||||
titleValue = valueToDisplay;
|
titleValue = valueToDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<span title="${titleValue}" style="${cellStyle}" class="${cellClasses}">${valueToDisplay}</span>`;
|
const formattedValue = `<span title="${titleValue}" style="${cellStyle}" class="${cellClasses}">${valueToDisplay}</span>`;
|
||||||
|
|
||||||
|
if (addClasses) {
|
||||||
|
return { text: formattedValue, addClasses: addClasses };
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCellDisplayValue(cellValue: string): string {
|
function getCellDisplayValue(cellValue: string): string {
|
||||||
@@ -132,7 +138,7 @@ function getCellDisplayValue(cellValue: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function iconCssFormatter(row: number | undefined, cell: any | undefined, value: any, columnDef: any | undefined, dataContext: any | undefined): string {
|
export function iconCssFormatter(row: number | undefined, cell: any | undefined, value: any, columnDef: any | undefined, dataContext: any | undefined): string | { text: string, addClasses: string } {
|
||||||
if (isCssIconCellValue(value)) {
|
if (isCssIconCellValue(value)) {
|
||||||
return `<div role="image" title="${escape(value.title ?? '')}" aria-label="${escape(value.title ?? '')}" class="grid-cell-value-container icon codicon slick-icon-cell-content ${value.iconCssClass}"></div>`;
|
return `<div role="image" title="${escape(value.title ?? '')}" aria-label="${escape(value.title ?? '')}" class="grid-cell-value-container icon codicon slick-icon-cell-content ${value.iconCssClass}"></div>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ suite('Grid shared services tests', () => {
|
|||||||
isNull: false
|
isNull: false
|
||||||
};
|
};
|
||||||
let formattedHtml = SharedServices.textFormatter(undefined, undefined, cellValue, undefined, undefined);
|
let formattedHtml = SharedServices.textFormatter(undefined, undefined, cellValue, undefined, undefined);
|
||||||
|
if (typeof formattedHtml !== 'string') {
|
||||||
|
formattedHtml = formattedHtml.text;
|
||||||
|
}
|
||||||
|
|
||||||
// Then the result is HTML for a span element containing the cell value's display value as plain text
|
// Then the result is HTML for a span element containing the cell value's display value as plain text
|
||||||
verifyFormattedHtml(formattedHtml, testText);
|
verifyFormattedHtml(formattedHtml, testText);
|
||||||
@@ -24,6 +27,9 @@ suite('Grid shared services tests', () => {
|
|||||||
test('textFormatter should encode HTML when formatting a string', () => {
|
test('textFormatter should encode HTML when formatting a string', () => {
|
||||||
// If I format a string that contains HTML
|
// If I format a string that contains HTML
|
||||||
let formattedHtml = SharedServices.textFormatter(undefined, undefined, testText, undefined, undefined);
|
let formattedHtml = SharedServices.textFormatter(undefined, undefined, testText, undefined, undefined);
|
||||||
|
if (typeof formattedHtml !== 'string') {
|
||||||
|
formattedHtml = formattedHtml.text;
|
||||||
|
}
|
||||||
|
|
||||||
// Then the result is HTML for a span element containing the given text as plain text
|
// Then the result is HTML for a span element containing the given text as plain text
|
||||||
verifyFormattedHtml(formattedHtml, testText);
|
verifyFormattedHtml(formattedHtml, testText);
|
||||||
|
|||||||
@@ -91,3 +91,6 @@ export const calloutDialogShadowColor = registerColor('calloutDialog.shadow', {
|
|||||||
|
|
||||||
// Designer
|
// Designer
|
||||||
export const DesignerPaneSeparator = registerColor('designer.paneSeparator', { light: '#DDDDDD', dark: '#8A8886', hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('designer.paneSeparator', 'The pane separator color.'));
|
export const DesignerPaneSeparator = registerColor('designer.paneSeparator', { light: '#DDDDDD', dark: '#8A8886', hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('designer.paneSeparator', 'The pane separator color.'));
|
||||||
|
|
||||||
|
// Query Editor
|
||||||
|
export const queryEditorNullBackground = registerColor('queryEditor.nullBackground', { light: '#FFFFE1', dark: '#4B0082', hcDark: '#000000', hcLight: '#FFFFE1' }, nls.localize('queryEditorNullBackground', 'The background color for null values in the query editor results grid.'));
|
||||||
|
|||||||
@@ -17,14 +17,14 @@ import { IGridActionContext, SaveResultAction, CopyResultAction, SelectAllGridAc
|
|||||||
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
||||||
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
|
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
|
||||||
import { escape } from 'sql/base/common/strings';
|
import { escape } from 'sql/base/common/strings';
|
||||||
import { hyperLinkFormatter, textFormatter } from 'sql/base/browser/ui/table/formatters';
|
import { DBCellValue, hyperLinkFormatter, textFormatter } from 'sql/base/browser/ui/table/formatters';
|
||||||
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
|
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
|
||||||
|
|
||||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||||
import { Emitter, Event } from 'vs/base/common/event';
|
import { Emitter, Event } from 'vs/base/common/event';
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||||
import { Disposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
|
import { Disposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
|
||||||
import { range } from 'vs/base/common/arrays';
|
import { range } from 'vs/base/common/arrays';
|
||||||
@@ -56,6 +56,7 @@ import { CopyAction } from 'vs/editor/contrib/clipboard/browser/clipboard';
|
|||||||
import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/browser/format';
|
import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/browser/format';
|
||||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||||
|
import { queryEditorNullBackground } from 'sql/platform/theme/common/colorRegistry';
|
||||||
|
|
||||||
const ROW_HEIGHT = 29;
|
const ROW_HEIGHT = 29;
|
||||||
const HEADER_HEIGHT = 26;
|
const HEADER_HEIGHT = 26;
|
||||||
@@ -80,6 +81,9 @@ const MIN_GRID_HEIGHT = (MIN_GRID_HEIGHT_ROWS * ROW_HEIGHT) + HEADER_HEIGHT + ES
|
|||||||
// or '{', and there must be a '}' or ']' to close it.
|
// or '{', and there must be a '}' or ']' to close it.
|
||||||
const IsJsonRegex = /^\s*[\{|\[][\S\s]*[\}\]]\s*$/g;
|
const IsJsonRegex = /^\s*[\{|\[][\S\s]*[\}\]]\s*$/g;
|
||||||
|
|
||||||
|
// The css class for null cell
|
||||||
|
const NULL_CELL_CSS_CLASS = 'cell-null';
|
||||||
|
|
||||||
export class GridPanel extends Disposable {
|
export class GridPanel extends Disposable {
|
||||||
private container = document.createElement('div');
|
private container = document.createElement('div');
|
||||||
private scrollableView: ScrollableView;
|
private scrollableView: ScrollableView;
|
||||||
@@ -431,7 +435,7 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
|
|||||||
? localize('xmlShowplan', "XML Showplan")
|
? localize('xmlShowplan', "XML Showplan")
|
||||||
: escape(c.columnName),
|
: escape(c.columnName),
|
||||||
field: i.toString(),
|
field: i.toString(),
|
||||||
formatter: c.isXml ? hyperLinkFormatter : (row: number | undefined, cell: any | undefined, value: ICellValue, columnDef: any | undefined, dataContext: any | undefined): string => {
|
formatter: c.isXml ? hyperLinkFormatter : (row: number | undefined, cell: any | undefined, value: ICellValue, columnDef: any | undefined, dataContext: any | undefined): string | { text: string, addClasses: string } => {
|
||||||
return queryResultTextFormatter(this.gridConfig.showJsonAsLink, row, cell, value, columnDef, dataContext);
|
return queryResultTextFormatter(this.gridConfig.showJsonAsLink, row, cell, value, columnDef, dataContext);
|
||||||
},
|
},
|
||||||
width: this.state.columnSizes && this.state.columnSizes[i] ? this.state.columnSizes[i] : undefined
|
width: this.state.columnSizes && this.state.columnSizes[i] ? this.state.columnSizes[i] : undefined
|
||||||
@@ -567,6 +571,13 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
|
|||||||
refreshColumns: !autoSizeOnRender // The auto size columns plugin refreshes the columns so we don't need to refresh twice if both plugins are on.
|
refreshColumns: !autoSizeOnRender // The auto size columns plugin refreshes the columns so we don't need to refresh twice if both plugins are on.
|
||||||
});
|
});
|
||||||
this._register(attachTableFilterStyler(this.filterPlugin, this.themeService));
|
this._register(attachTableFilterStyler(this.filterPlugin, this.themeService));
|
||||||
|
this._register(registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
|
||||||
|
const nullBackground = theme.getColor(queryEditorNullBackground);
|
||||||
|
if (nullBackground) {
|
||||||
|
collector.addRule(`.${NULL_CELL_CSS_CLASS} { background: ${nullBackground};}`);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
this.table.registerPlugin(this.filterPlugin);
|
this.table.registerPlugin(this.filterPlugin);
|
||||||
if (this.styles) {
|
if (this.styles) {
|
||||||
this.table.style(this.styles);
|
this.table.style(this.styles);
|
||||||
@@ -1004,10 +1015,10 @@ function isJsonCell(value: ICellValue): boolean {
|
|||||||
return !!(value && !value.isNull && value.displayValue?.match(IsJsonRegex));
|
return !!(value && !value.isNull && value.displayValue?.match(IsJsonRegex));
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryResultTextFormatter(showJsonAsLink: boolean, row: number | undefined, cell: any | undefined, value: ICellValue, columnDef: any | undefined, dataContext: any | undefined): string {
|
function queryResultTextFormatter(showJsonAsLink: boolean, row: number | undefined, cell: any | undefined, value: ICellValue, columnDef: any | undefined, dataContext: any | undefined): string | { text: string, addClasses: string } {
|
||||||
if (showJsonAsLink && isJsonCell(value)) {
|
if (showJsonAsLink && isJsonCell(value)) {
|
||||||
return hyperLinkFormatter(row, cell, value, columnDef, dataContext);
|
return hyperLinkFormatter(row, cell, value, columnDef, dataContext);
|
||||||
} else {
|
} else {
|
||||||
return textFormatter(row, cell, value, columnDef, dataContext);
|
return textFormatter(row, cell, value, columnDef, dataContext, (DBCellValue.isDBCellValue(value) && value.isNull) ? NULL_CELL_CSS_CLASS : undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/typings/slickgrid.d.ts
vendored
2
src/typings/slickgrid.d.ts
vendored
@@ -1576,7 +1576,7 @@ declare namespace Slick {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Formatter<T extends SlickData> {
|
export interface Formatter<T extends SlickData> {
|
||||||
(row: number, cell: number, value: any, columnDef: Column<T>, dataContext: T): string | undefined;
|
(row: number, cell: number, value: any, columnDef: Column<T>, dataContext: T): string | undefined | { text: string, addClasses: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
export module Formatters {
|
export module Formatters {
|
||||||
|
|||||||
Reference in New Issue
Block a user