Skip to content

Commit

Permalink
Move css specific code to own file (#230427)
Browse files Browse the repository at this point in the history
Planning to add some additional css helpers. `dom` is getting fairly large and unfocused so I think we should start a new file just for helping write css
  • Loading branch information
mjbvz authored Oct 3, 2024
1 parent 03dbe67 commit f6c1c6d
Show file tree
Hide file tree
Showing 26 changed files with 94 additions and 66 deletions.
35 changes: 35 additions & 0 deletions src/vs/base/browser/cssValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { FileAccess } from '../common/network.js';
import { URI } from '../common/uri.js';

export function asCssValueWithDefault(cssPropertyValue: string | undefined, dflt: string): string {
if (cssPropertyValue !== undefined) {
const variableMatch = cssPropertyValue.match(/^\s*var\((.+)\)$/);
if (variableMatch) {
const varArguments = variableMatch[1].split(',', 2);
if (varArguments.length === 2) {
dflt = asCssValueWithDefault(varArguments[1].trim(), dflt);
}
return `var(${varArguments[0]}, ${dflt})`;
}
return cssPropertyValue;
}
return dflt;
}

export function asCSSPropertyValue(value: string) {
return `'${value.replace(/'/g, '%27')}'`;
}

/**
* returns url('...')
*/
export function asCSSUrl(uri: URI | null | undefined): string {
if (!uri) {
return `url('')`;
}
return `url('${FileAccess.uriToBrowserUri(uri).toString(true).replace(/'/g, '%27')}')`;
}
31 changes: 1 addition & 30 deletions src/vs/base/browser/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import * as event from '../common/event.js';
import * as dompurify from './dompurify/dompurify.js';
import { KeyCode } from '../common/keyCodes.js';
import { Disposable, DisposableStore, IDisposable, toDisposable } from '../common/lifecycle.js';
import { FileAccess, RemoteAuthorities, Schemas } from '../common/network.js';
import { RemoteAuthorities, Schemas } from '../common/network.js';
import * as platform from '../common/platform.js';
import { URI } from '../common/uri.js';
import { hash } from '../common/hash.js';
Expand Down Expand Up @@ -1676,35 +1676,6 @@ export function animate(targetWindow: Window, fn: () => void): IDisposable {

RemoteAuthorities.setPreferredWebSchema(/^https:/.test(mainWindow.location.href) ? 'https' : 'http');

/**
* returns url('...')
*/
export function asCSSUrl(uri: URI | null | undefined): string {
if (!uri) {
return `url('')`;
}
return `url('${FileAccess.uriToBrowserUri(uri).toString(true).replace(/'/g, '%27')}')`;
}

export function asCSSPropertyValue(value: string) {
return `'${value.replace(/'/g, '%27')}'`;
}

export function asCssValueWithDefault(cssPropertyValue: string | undefined, dflt: string): string {
if (cssPropertyValue !== undefined) {
const variableMatch = cssPropertyValue.match(/^\s*var\((.+)\)$/);
if (variableMatch) {
const varArguments = variableMatch[1].split(',', 2);
if (varArguments.length === 2) {
dflt = asCssValueWithDefault(varArguments[1].trim(), dflt);
}
return `var(${varArguments[0]}, ${dflt})`;
}
return cssPropertyValue;
}
return dflt;
}

export function triggerDownload(dataOrUri: Uint8Array | URI, name: string): void {

// If the data is provided as Buffer, we create a
Expand Down
3 changes: 2 additions & 1 deletion src/vs/base/browser/ui/iconLabel/iconLabel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import './iconlabel.css';
import * as dom from '../../dom.js';
import * as css from '../../cssValue.js';
import { HighlightedLabel } from '../highlightedlabel/highlightedLabel.js';
import { IHoverDelegate } from '../hover/hoverDelegate.js';
import { IMatch } from '../../../common/filters.js';
Expand Down Expand Up @@ -165,7 +166,7 @@ export class IconLabel extends Disposable {
} else {
iconNode = existingIconNode;
}
iconNode.style.backgroundImage = dom.asCSSUrl(options?.iconPath);
iconNode.style.backgroundImage = css.asCSSUrl(options?.iconPath);
} else if (existingIconNode) {
existingIconNode.remove();
}
Expand Down
5 changes: 3 additions & 2 deletions src/vs/base/browser/ui/inputbox/inputBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import * as dom from '../../dom.js';
import * as cssJs from '../../cssValue.js';
import { DomEmitter } from '../../event.js';
import { renderFormattedText, renderText } from '../../formattedTextRenderer.js';
import { IHistoryNavigationWidget } from '../../history.js';
Expand Down Expand Up @@ -403,7 +404,7 @@ export class InputBox extends Widget {
this.element.classList.add(this.classForType(message.type));

const styles = this.stylesForType(this.message.type);
this.element.style.border = `1px solid ${dom.asCssValueWithDefault(styles.border, 'transparent')}`;
this.element.style.border = `1px solid ${cssJs.asCssValueWithDefault(styles.border, 'transparent')}`;

if (this.message.content && (this.hasFocus() || force)) {
this._showMessage();
Expand Down Expand Up @@ -578,7 +579,7 @@ export class InputBox extends Widget {
this.input.style.color = foreground;

// there's always a border, even if the color is not set.
this.element.style.border = `1px solid ${dom.asCssValueWithDefault(border, 'transparent')}`;
this.element.style.border = `1px solid ${cssJs.asCssValueWithDefault(border, 'transparent')}`;
}

public layout(): void {
Expand Down
3 changes: 2 additions & 1 deletion src/vs/base/browser/ui/list/listWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/

import { IDragAndDropData } from '../../dnd.js';
import { asCssValueWithDefault, createStyleSheet, Dimension, EventHelper, getActiveElement, getWindow, isActiveElement, isEditableElement, isHTMLElement, isMouseEvent } from '../../dom.js';
import { createStyleSheet, Dimension, EventHelper, getActiveElement, getWindow, isActiveElement, isEditableElement, isHTMLElement, isMouseEvent } from '../../dom.js';
import { asCssValueWithDefault } from '../../cssValue.js';
import { DomEmitter } from '../../event.js';
import { IKeyboardEvent, StandardKeyboardEvent } from '../../keyboardEvent.js';
import { Gesture } from '../../touch.js';
Expand Down
3 changes: 2 additions & 1 deletion src/vs/base/browser/ui/selectBox/selectBoxCustom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import * as dom from '../../dom.js';
import * as cssJs from '../../cssValue.js';
import { DomEmitter } from '../../event.js';
import { IContentActionHandler } from '../../formattedTextRenderer.js';
import { StandardKeyboardEvent } from '../../keyboardEvent.js';
Expand Down Expand Up @@ -426,7 +427,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
private styleList() {
const background = this.styles.selectBackground ?? '';

const listBackground = dom.asCssValueWithDefault(this.styles.selectListBackground, background);
const listBackground = cssJs.asCssValueWithDefault(this.styles.selectListBackground, background);
this.selectDropDownListContainer.style.backgroundColor = listBackground;
this.selectionDetailsPane.style.backgroundColor = listBackground;
const optionsBorder = this.styles.focusBorder ?? '';
Expand Down
3 changes: 2 additions & 1 deletion src/vs/base/browser/ui/tree/abstractTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/

import { IDragAndDropData } from '../../dnd.js';
import { $, append, clearNode, createStyleSheet, getWindow, h, hasParentWithClass, isActiveElement, asCssValueWithDefault, isKeyboardEvent, addDisposableListener, isEditableElement } from '../../dom.js';
import { $, append, clearNode, createStyleSheet, getWindow, h, hasParentWithClass, isActiveElement, isKeyboardEvent, addDisposableListener, isEditableElement } from '../../dom.js';
import { asCssValueWithDefault } from '../../cssValue.js';
import { DomEmitter } from '../../event.js';
import { StandardKeyboardEvent } from '../../keyboardEvent.js';
import { ActionBar } from '../actionbar/actionbar.js';
Expand Down
3 changes: 2 additions & 1 deletion src/vs/base/test/browser/dom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/

import assert from 'assert';
import { $, asCssValueWithDefault, h, multibyteAwareBtoa, trackAttributes, copyAttributes, disposableWindowInterval, getWindows, getWindowsCount, getWindowId, getWindowById, hasWindow, getWindow, getDocument, isHTMLElement, SafeTriangle } from '../../browser/dom.js';
import { $, h, multibyteAwareBtoa, trackAttributes, copyAttributes, disposableWindowInterval, getWindows, getWindowsCount, getWindowId, getWindowById, hasWindow, getWindow, getDocument, isHTMLElement, SafeTriangle } from '../../browser/dom.js';
import { asCssValueWithDefault } from '../../../base/browser/cssValue.js';
import { ensureCodeWindow, isAuxiliaryWindow, mainWindow } from '../../browser/window.js';
import { DeferredPromise, timeout } from '../../common/async.js';
import { runWithFakedTimers } from '../common/timeTravelScheduler.js';
Expand Down
5 changes: 3 additions & 2 deletions src/vs/editor/browser/services/abstractCodeEditorService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import * as dom from '../../../base/browser/dom.js';
import * as cssJs from '../../../base/browser/cssValue.js';
import { Emitter, Event } from '../../../base/common/event.js';
import { IDisposable, DisposableStore, Disposable, toDisposable } from '../../../base/common/lifecycle.js';
import { LinkedList } from '../../../base/common/linkedList.js';
Expand Down Expand Up @@ -771,7 +772,7 @@ class DecorationCSSRules {
if (typeof opts !== 'undefined') {
this.collectBorderSettingsCSSText(opts, cssTextArr);
if (typeof opts.contentIconPath !== 'undefined') {
cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, dom.asCSSUrl(URI.revive(opts.contentIconPath))));
cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, cssJs.asCSSUrl(URI.revive(opts.contentIconPath))));
}
if (typeof opts.contentText === 'string') {
const truncated = opts.contentText.match(/^.*$/m)![0]; // only take first line
Expand All @@ -798,7 +799,7 @@ class DecorationCSSRules {
const cssTextArr: string[] = [];

if (typeof opts.gutterIconPath !== 'undefined') {
cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, dom.asCSSUrl(URI.revive(opts.gutterIconPath))));
cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, cssJs.asCSSUrl(URI.revive(opts.gutterIconPath))));
if (typeof opts.gutterIconSize !== 'undefined') {
cssTextArr.push(strings.format(_CSS_MAP.gutterIconSize, opts.gutterIconSize));
}
Expand Down
3 changes: 2 additions & 1 deletion src/vs/platform/actions/browser/menuEntryActionViewItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { $, addDisposableListener, append, asCSSUrl, EventType, ModifierKeyEmitter, prepend } from '../../../base/browser/dom.js';
import { $, addDisposableListener, append, EventType, ModifierKeyEmitter, prepend } from '../../../base/browser/dom.js';
import { asCSSUrl } from '../../../base/browser/cssValue.js';
import { StandardKeyboardEvent } from '../../../base/browser/keyboardEvent.js';
import { ActionViewItem, BaseActionViewItem, SelectActionViewItem } from '../../../base/browser/ui/actionbar/actionViewItems.js';
import { DropdownMenuActionViewItem, IDropdownMenuActionViewItemOptions } from '../../../base/browser/ui/dropdown/dropdownActionViewItem.js';
Expand Down
3 changes: 2 additions & 1 deletion src/vs/platform/quickinput/browser/quickInputTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import * as dom from '../../../base/browser/dom.js';
import * as cssJs from '../../../base/browser/cssValue.js';
import { Emitter, Event, EventBufferer, IValueWithChangeEvent } from '../../../base/common/event.js';
import { IHoverDelegate } from '../../../base/browser/ui/hover/hoverDelegate.js';
import { IListVirtualDelegate } from '../../../base/browser/ui/list/list.js';
Expand Down Expand Up @@ -428,7 +429,7 @@ class QuickPickItemElementRenderer extends BaseQuickInputListRenderer<QuickPickI
const icon = isDark(this.themeService.getColorTheme().type) ? mainItem.iconPath.dark : (mainItem.iconPath.light ?? mainItem.iconPath.dark);
const iconUrl = URI.revive(icon);
data.icon.className = 'quick-input-list-icon';
data.icon.style.backgroundImage = dom.asCSSUrl(iconUrl);
data.icon.style.backgroundImage = cssJs.asCSSUrl(iconUrl);
} else {
data.icon.style.backgroundImage = '';
data.icon.className = mainItem.iconClass ? `quick-input-list-icon ${mainItem.iconClass}` : '';
Expand Down
5 changes: 3 additions & 2 deletions src/vs/platform/quickinput/browser/quickInputUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import * as dom from '../../../base/browser/dom.js';
import * as cssJs from '../../../base/browser/cssValue.js';
import { DomEmitter } from '../../../base/browser/event.js';
import { Event } from '../../../base/common/event.js';
import { StandardKeyboardEvent } from '../../../base/browser/keyboardEvent.js';
Expand Down Expand Up @@ -33,8 +34,8 @@ function getIconClass(iconPath: { dark: URI; light?: URI } | undefined): string
iconClass = iconPathToClass[key];
} else {
iconClass = iconClassGenerator.nextId();
dom.createCSSRule(`.${iconClass}, .hc-light .${iconClass}`, `background-image: ${dom.asCSSUrl(iconPath.light || iconPath.dark)}`);
dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: ${dom.asCSSUrl(iconPath.dark)}`);
dom.createCSSRule(`.${iconClass}, .hc-light .${iconClass}`, `background-image: ${cssJs.asCSSUrl(iconPath.light || iconPath.dark)}`);
dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: ${cssJs.asCSSUrl(iconPath.dark)}`);
iconPathToClass[key] = iconClass;
}

Expand Down
2 changes: 1 addition & 1 deletion src/vs/platform/theme/browser/iconsStyleSheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { asCSSPropertyValue, asCSSUrl } from '../../../base/browser/dom.js';
import { asCSSPropertyValue, asCSSUrl } from '../../../base/browser/cssValue.js';
import { Emitter, Event } from '../../../base/common/event.js';
import { DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';
import { ThemeIcon } from '../../../base/common/themables.js';
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/browser/parts/banner/bannerPart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

import './media/bannerpart.css';
import { localize2 } from '../../../../nls.js';
import { $, addDisposableListener, append, asCSSUrl, clearNode, EventType, isHTMLElement } from '../../../../base/browser/dom.js';
import { $, addDisposableListener, append, clearNode, EventType, isHTMLElement } from '../../../../base/browser/dom.js';
import { asCSSUrl } from '../../../../base/browser/cssValue.js';
import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js';
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/browser/parts/paneCompositeBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { IInstantiationService } from '../../../platform/instantiation/common/in
import { IDisposable, DisposableStore, Disposable, DisposableMap } from '../../../base/common/lifecycle.js';
import { IColorTheme } from '../../../platform/theme/common/themeService.js';
import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from './compositeBar.js';
import { Dimension, createCSSRule, asCSSUrl, isMouseEvent } from '../../../base/browser/dom.js';
import { Dimension, createCSSRule, isMouseEvent } from '../../../base/browser/dom.js';
import { asCSSUrl } from '../../../base/browser/cssValue.js';
import { IStorageService, StorageScope, StorageTarget } from '../../../platform/storage/common/storage.js';
import { IExtensionService } from '../../services/extensions/common/extensions.js';
import { URI, UriComponents } from '../../../base/common/uri.js';
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/browser/parts/views/treeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { DataTransfers, IDragAndDropData } from '../../../../base/browser/dnd.js';
import * as DOM from '../../../../base/browser/dom.js';
import * as cssJs from '../../../../base/browser/cssValue.js';
import { renderMarkdownAsPlaintext } from '../../../../base/browser/markdownRenderer.js';
import { ActionBar, IActionViewItemProvider } from '../../../../base/browser/ui/actionbar/actionbar.js';
import { ActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js';
Expand Down Expand Up @@ -1328,7 +1329,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS

if (iconUrl) {
templateData.icon.className = 'custom-view-tree-node-item-icon';
templateData.icon.style.backgroundImage = DOM.asCSSUrl(iconUrl);
templateData.icon.style.backgroundImage = cssJs.asCSSUrl(iconUrl);
} else {
let iconClass: string | undefined;
if (this.shouldShowThemeIcon(!!resource, node.themeIcon)) {
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/browser/parts/views/viewPane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import './media/paneviewlet.css';
import * as nls from '../../../../nls.js';
import { Event, Emitter } from '../../../../base/common/event.js';
import { asCssVariable, foreground } from '../../../../platform/theme/common/colorRegistry.js';
import { after, append, $, trackFocus, EventType, addDisposableListener, createCSSRule, asCSSUrl, Dimension, reset, asCssValueWithDefault } from '../../../../base/browser/dom.js';
import { after, append, $, trackFocus, EventType, addDisposableListener, createCSSRule, Dimension, reset } from '../../../../base/browser/dom.js';
import { asCssValueWithDefault, asCSSUrl } from '../../../../base/browser/cssValue.js';
import { DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js';
import { Action, IAction, IActionRunner } from '../../../../base/common/actions.js';
import { ActionsOrientation, IActionViewItem, prepareActions } from '../../../../base/browser/ui/actionbar/actionbar.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { PLAINTEXT_LANGUAGE_ID } from '../../../../../editor/common/languages/mo
import { SnippetParser } from '../../../../../editor/contrib/snippet/browser/snippetParser.js';
import { AriaRole } from '../../../../../base/browser/ui/aria/aria.js';
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
import * as css from '../../../../../base/browser/cssValue.js';

// --- VIEW MODEL

Expand Down Expand Up @@ -436,14 +437,14 @@ export class CategoryElementRenderer implements ITreeRenderer<CategoryElement, F
} else if (URI.isUri(metadata.iconPath)) {
// background-image
template.icon.className = 'uri-icon';
template.icon.style.setProperty('--background-dark', dom.asCSSUrl(metadata.iconPath));
template.icon.style.setProperty('--background-light', dom.asCSSUrl(metadata.iconPath));
template.icon.style.setProperty('--background-dark', css.asCSSUrl(metadata.iconPath));
template.icon.style.setProperty('--background-light', css.asCSSUrl(metadata.iconPath));

} else if (metadata.iconPath) {
// background-image
template.icon.className = 'uri-icon';
template.icon.style.setProperty('--background-dark', dom.asCSSUrl(metadata.iconPath.dark));
template.icon.style.setProperty('--background-light', dom.asCSSUrl(metadata.iconPath.light));
template.icon.style.setProperty('--background-dark', css.asCSSUrl(metadata.iconPath.dark));
template.icon.style.setProperty('--background-light', css.asCSSUrl(metadata.iconPath.light));
}

template.label.setLabel(metadata.label, metadata.description, {
Expand Down Expand Up @@ -635,14 +636,14 @@ class TextEditElementTemplate {
} else if (URI.isUri(iconPath)) {
// background-image
this._icon.className = 'uri-icon';
this._icon.style.setProperty('--background-dark', dom.asCSSUrl(iconPath));
this._icon.style.setProperty('--background-light', dom.asCSSUrl(iconPath));
this._icon.style.setProperty('--background-dark', css.asCSSUrl(iconPath));
this._icon.style.setProperty('--background-light', css.asCSSUrl(iconPath));

} else {
// background-image
this._icon.className = 'uri-icon';
this._icon.style.setProperty('--background-dark', dom.asCSSUrl(iconPath.dark));
this._icon.style.setProperty('--background-light', dom.asCSSUrl(iconPath.light));
this._icon.style.setProperty('--background-dark', css.asCSSUrl(iconPath.dark));
this._icon.style.setProperty('--background-light', css.asCSSUrl(iconPath.light));
}
}

Expand Down
Loading

0 comments on commit f6c1c6d

Please sign in to comment.