From 8ff53281f9607bd46be06b87b20bfcfdabe4f8af Mon Sep 17 00:00:00 2001 From: Kim Santiago <31145923+kisantia@users.noreply.github.com> Date: Thu, 9 Apr 2020 16:31:52 -0700 Subject: [PATCH] Dashboard toolbar overflow (#9796) * initial changes for actionbar collapsing * fix more not always all showing after resizing * collapse toolbar if window size is already small when dashboard is opened * make wrapping default behavior and collapse opt in * fix so keyboard navigation works in overflow * more keyboard fixing so that the actions in overflow get triggered * change overflow background with theme * change margin * udpate more button * use icon for ... * addressing comments * overflow css changes to match portal * arrow navigation working * handle tab and shift tab in overflow * keep arrow navigation within overflow * move reused code to helper methods * set roles for overflow * use actionsList instead of document.getElementById all the time * move collapsible action bar to its own class * renamve to overflowActionBar * fix focus rectangle around more element * hide overflow after an action is executed * hide overflow when clicking an action * hide overflow when focus leaves and loop focus within overflow when using arrow keys * fix double down arrow to move focus in overflow * update comment * fix clicking more not hiding overflow * add box-shadow for themes * fix hygiene error * fix hygiene error * widen focused outline for overflow actions --- src/sql/base/browser/ui/taskbar/actionbar.ts | 31 +- .../base/browser/ui/taskbar/media/taskbar.css | 53 +++ .../browser/ui/taskbar/overflowActionbar.ts | 328 ++++++++++++++++++ .../ui/taskbar/overflowActionbarStyles.ts | 43 +++ src/sql/base/browser/ui/taskbar/taskbar.ts | 33 +- .../browser/core/dashboardPage.component.ts | 2 +- .../browser/core/dashboardPanelStyles.ts | 2 +- src/vs/workbench/common/theme.ts | 8 +- 8 files changed, 474 insertions(+), 26 deletions(-) create mode 100644 src/sql/base/browser/ui/taskbar/overflowActionbar.ts create mode 100644 src/sql/base/browser/ui/taskbar/overflowActionbarStyles.ts diff --git a/src/sql/base/browser/ui/taskbar/actionbar.ts b/src/sql/base/browser/ui/taskbar/actionbar.ts index 822f72a65f..68975ce583 100644 --- a/src/sql/base/browser/ui/taskbar/actionbar.ts +++ b/src/sql/base/browser/ui/taskbar/actionbar.ts @@ -27,18 +27,18 @@ const defaultOptions: IActionBarOptions = { */ export class ActionBar extends ActionRunner implements IActionRunner { - private _options: IActionBarOptions; - private _actionRunner: IActionRunner; - private _context: any; + protected _options: IActionBarOptions; + protected _actionRunner: IActionRunner; + protected _context: any; // Items - private _items: IActionViewItem[]; - private _focusedItem?: number; - private _focusTracker: DOM.IFocusTracker; + protected _items: IActionViewItem[]; + protected _focusedItem?: number; + protected _focusTracker: DOM.IFocusTracker; // Elements - private _domNode: HTMLElement; - private _actionsList: HTMLElement; + protected _domNode: HTMLElement; + protected _actionsList: HTMLElement; constructor(container: HTMLElement, options: IActionBarOptions = defaultOptions) { super(); @@ -128,6 +128,7 @@ export class ActionBar extends ActionRunner implements IActionRunner { this._actionsList = document.createElement('ul'); this._actionsList.className = 'actions-container'; this._actionsList.setAttribute('role', 'toolbar'); + this._actionsList.id = 'actions-container'; if (this._options.ariaLabel) { this._actionsList.setAttribute('aria-label', this._options.ariaLabel); } @@ -145,7 +146,7 @@ export class ActionBar extends ActionRunner implements IActionRunner { } } - private updateFocusedItem(): void { + protected updateFocusedItem(): void { let actionIndex = 0; for (let i = 0; i < this._actionsList.children.length; i++) { let elem = this._actionsList.children[i]; @@ -155,7 +156,7 @@ export class ActionBar extends ActionRunner implements IActionRunner { break; } - if (elem.classList.contains('action-item')) { + if (elem.classList.contains('action-item') && i !== this._actionsList.children.length - 1) { actionIndex++; } } @@ -268,7 +269,7 @@ export class ActionBar extends ActionRunner implements IActionRunner { this.updateFocus(); } - private focusNext(): void { + protected focusNext(): void { if (typeof this._focusedItem === 'undefined') { this._focusedItem = this._items.length - 1; } @@ -288,7 +289,7 @@ export class ActionBar extends ActionRunner implements IActionRunner { this.updateFocus(); } - private focusPrevious(): void { + protected focusPrevious(): void { if (typeof this._focusedItem === 'undefined') { this._focusedItem = 0; } @@ -313,7 +314,7 @@ export class ActionBar extends ActionRunner implements IActionRunner { this.updateFocus(); } - private updateFocus(): void { + protected updateFocus(): void { if (typeof this._focusedItem === 'undefined') { this._domNode.focus(); return; @@ -329,7 +330,7 @@ export class ActionBar extends ActionRunner implements IActionRunner { actionItem.focus(); } } else { - if (types.isFunction(actionItem.blur)) { + if (actionItem && types.isFunction(actionItem.blur)) { actionItem.blur(); } } @@ -349,7 +350,7 @@ export class ActionBar extends ActionRunner implements IActionRunner { } } - private cancel(): void { + protected cancel(): void { if (document.activeElement instanceof HTMLElement) { (document.activeElement).blur(); // remove focus from focussed action } diff --git a/src/sql/base/browser/ui/taskbar/media/taskbar.css b/src/sql/base/browser/ui/taskbar/media/taskbar.css index 701d37cc0b..f0f18e4f35 100644 --- a/src/sql/base/browser/ui/taskbar/media/taskbar.css +++ b/src/sql/base/browser/ui/taskbar/media/taskbar.css @@ -47,6 +47,10 @@ margin-right: 5px; } +.carbon-taskbar .action-item.more { + padding-right: 15px; +} + .carbon-taskbar .action-label { background-repeat: no-repeat; background-position: 0% 50%; @@ -86,3 +90,52 @@ .carbon-taskbar .codicon { background-size: 11px; } + +.carbon-taskbar .overflow { + position:absolute; + right:0; + display:none; + z-index: 3; + padding-left: 0; + margin-top: 6px; + margin-right: 5px; +} + +.vs-dark .carbon-taskbar .overflow { + box-sizing: border-box; +} + +.hc-black .carbon-taskbar .overflow { + box-shadow: none; +} + +.carbon-taskbar .overflow li { + padding: 5px; + margin-right: 0; +} + +.carbon-taskbar .overflow li.focused a.action-label { + outline: none; +} + +.carbon-taskbar .overflow a { + padding-left: 20px; +} + +.carbon-taskbar .actions-container .action-item .action-label.moreActionsElement { + height: 18px; + margin-top: 3px; + background-position: center; + padding-right: 20px; +} + +.carbon-taskbar .overflow .action-item .action-label{ + background-size: 16px; + background-position: left; +} + +.overflow .taskbarSeparator { + height: 1px; + width: 100%; + margin-left: 0; +} diff --git a/src/sql/base/browser/ui/taskbar/overflowActionbar.ts b/src/sql/base/browser/ui/taskbar/overflowActionbar.ts new file mode 100644 index 0000000000..fa8339dbeb --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/overflowActionbar.ts @@ -0,0 +1,328 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import 'sql/base/browser/ui/taskbar/overflowActionbarStyles'; + +import { IAction } from 'vs/base/common/actions'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { + IActionBarOptions, ActionsOrientation, IActionViewItem, + IActionOptions +} from 'vs/base/browser/ui/actionbar/actionbar'; +import * as DOM from 'vs/base/browser/dom'; +import * as types from 'vs/base/common/types'; +import * as nls from 'vs/nls'; +import { debounce } from 'vs/base/common/decorators'; +import { ActionBar } from 'sql/base/browser/ui/taskbar/actionbar'; + +const defaultOptions: IActionBarOptions = { + orientation: ActionsOrientation.HORIZONTAL, + context: null +}; + +/** + * Extends Actionbar so that it overflows when the window is resized to be smaller than the actionbar instead of wrapping + */ +export class OverflowActionBar extends ActionBar { + // Elements + private _overflow: HTMLElement; + private _moreItemElement: HTMLElement; + private _moreActionsElement: HTMLElement; + + constructor(container: HTMLElement, options: IActionBarOptions = defaultOptions) { + super(container, options); + + this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, e => { + if (this._actionsList) { + this.resizeToolbar(); + } + })); + + this._overflow = document.createElement('ul'); + this._overflow.id = 'overflow'; + this._overflow.className = 'overflow'; + this._overflow.setAttribute('role', 'menu'); + this._domNode.appendChild(this._overflow); + + this._register(DOM.addDisposableListener(this._overflow, DOM.EventType.FOCUS_OUT, e => { + if (this._overflow && !DOM.isAncestor(e.relatedTarget as HTMLElement, this._overflow) && e.relatedTarget !== this._moreActionsElement) { + this.hideOverflowDisplay(); + } + })); + this._actionsList.style.flexWrap = 'nowrap'; + + container.appendChild(this._domNode); + } + + @debounce(300) + private resizeToolbar() { + let width = this._actionsList.offsetWidth; + let fullWidth = this._actionsList.scrollWidth; + + // collapse actions that are beyond the width of the toolbar + if (width < fullWidth) { + // create '•••' more element if it doesn't exist yet + if (!this._moreItemElement) { + this.createMoreItemElement(); + } + + this._moreItemElement.style.display = 'block'; + while (width < fullWidth) { + let index = this._actionsList.childNodes.length - 2; // remove the last toolbar action before the more actions '...' + if (index > -1) { + this.collapseItem(); + fullWidth = this._actionsList.scrollWidth; + } else { + break; + } + } + } else if (this._overflow?.hasChildNodes()) { // uncollapse actions if there is space for it + while (width === fullWidth && this._overflow.hasChildNodes()) { + // move placeholder in this._items + let placeHolderItem = this._items.splice(this._actionsList.childNodes.length - 1, 1); + this._items.splice(this._actionsList.childNodes.length, 0, placeHolderItem[0]); + + let item = this._overflow.removeChild(this._overflow.firstChild); + // change role back to button when it's in the toolbar + if ((item).className !== 'taskbarSeparator') { + (item.firstChild).setAttribute('role', 'button'); + } + this._actionsList.insertBefore(item, this._actionsList.lastChild); + + // if the action was too wide, collapse it again + if (this._actionsList.scrollWidth > this._actionsList.offsetWidth) { + // move placeholder in this._items + this.collapseItem(); + break; + } else if (!this._overflow.hasChildNodes()) { + this._moreItemElement.style.display = 'none'; + } + } + } + } + + private collapseItem(): void { + // move placeholder in this._items + let placeHolderItem = this._items.splice(this._actionsList.childNodes.length - 1, 1); + this._items.splice(this._actionsList.childNodes.length - 2, 0, placeHolderItem[0]); + + let index = this._actionsList.childNodes.length - 2; // remove the last toolbar action before the more actions '...' + let item = this._actionsList.removeChild(this._actionsList.childNodes[index]); + this._overflow.insertBefore(item, this._overflow.firstChild); + this._register(DOM.addDisposableListener(item, DOM.EventType.CLICK, (e => { this.hideOverflowDisplay(); }))); + + // change role to menuItem when it's in the overflow + if ((this._overflow.firstChild).className !== 'taskbarSeparator') { + (this._overflow.firstChild.firstChild).setAttribute('role', 'menuItem'); + } + } + + private createMoreItemElement(): void { + this._moreItemElement = document.createElement('li'); + this._moreItemElement.className = 'action-item more'; + this._moreItemElement.setAttribute('role', 'presentation'); + this._moreActionsElement = document.createElement('a'); + this._moreActionsElement.className = 'moreActionsElement action-label codicon toggle-more'; + this._moreActionsElement.setAttribute('role', 'button'); + this._moreActionsElement.title = nls.localize('toggleMore', "Toggle More"); + this._moreActionsElement.tabIndex = 0; + this._moreActionsElement.setAttribute('aria-haspopup', 'true'); + this._register(DOM.addDisposableListener(this._moreActionsElement, DOM.EventType.CLICK, (e => { + this.moreElementOnClick(e); + }))); + this._register(DOM.addDisposableListener(this._moreActionsElement, DOM.EventType.KEY_UP, (ev => { + let event = new StandardKeyboardEvent(ev); + if (event.keyCode === KeyCode.Enter || event.keyCode === KeyCode.Space) { + this.moreElementOnClick(event); + } + }))); + + this._register(DOM.addDisposableListener(this._overflow, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + let event = new StandardKeyboardEvent(e); + + // Close overflow if Escape is pressed + if (event.equals(KeyCode.Escape)) { + this.hideOverflowDisplay(); + this._moreActionsElement.focus(); + } else if (event.equals(KeyCode.UpArrow)) { + // up arrow on first element in overflow should move focus to the bottom of the overflow + if (this._focusedItem === this._actionsList.childElementCount) { + this._focusedItem = this._actionsList.childElementCount + this._overflow.childElementCount - 2; + this.updateFocus(); + } else { + this.focusPrevious(); + } + } else if (event.equals(KeyCode.DownArrow)) { + // down arrow on last element should move focus to the first element of the overflow + if (this._focusedItem === this._actionsList.childNodes.length + this._overflow.childNodes.length - 2) { + this._focusedItem = this._actionsList.childElementCount; + this.updateFocus(); + } else { + this.focusNext(); + } + } else if (event.equals(KeyMod.Shift | KeyCode.Tab)) { + this.hideOverflowDisplay(); + this._focusedItem = this._actionsList.childElementCount - 1; + this.updateFocus(); + } else if (event.equals(KeyCode.Tab)) { + this.hideOverflowDisplay(); + } + DOM.EventHelper.stop(event, true); + })); + + this._moreItemElement.appendChild(this._moreActionsElement); + this._actionsList.appendChild(this._moreItemElement); + this._items.push(undefined); // add place holder for more item element + } + + private moreElementOnClick(event: MouseEvent | StandardKeyboardEvent): void { + this._overflow.style.display = this._overflow.style.display === 'block' ? 'none' : 'block'; + if (this._overflow.style.display === 'block') { + this._focusedItem = this._actionsList.childElementCount; + this.updateFocus(); + } + DOM.EventHelper.stop(event, true); + } + + private hideOverflowDisplay(): void { + this._overflow.style.display = 'none'; + this._focusedItem = this._actionsList.childElementCount - 1; + } + + protected updateFocusedItem(): void { + let actionIndex = 0; + for (let i = 0; i < this._actionsList.children.length; i++) { + let elem = this._actionsList.children[i]; + + if (DOM.isAncestor(document.activeElement, elem)) { + this._focusedItem = actionIndex; + break; + } + + if (elem.classList.contains('action-item') && i !== this._actionsList.children.length - 1) { + actionIndex++; + } + } + + // move focus to overflow items if there are any + if (this._overflow) { + for (let i = 0; i < this._overflow.children.length; i++) { + let elem = this._overflow.children[i]; + + if (DOM.isAncestor(document.activeElement, elem)) { + this._focusedItem = actionIndex; + break; + } + + if (elem.classList.contains('action-item')) { + actionIndex++; + } + } + } + } + + /** + * Push an HTML Element onto the action bar UI in the position specified by options. + * Pushes to the last position if no options are provided. + */ + public pushElement(element: HTMLElement, options: IActionOptions = {}): void { + super.pushElement(element, options); + this.resizeToolbar(); + } + + /** + * Push an action onto the action bar UI in the position specified by options. + * Pushes to the last position if no options are provided. + */ + public pushAction(arg: IAction | IAction[], options: IActionOptions = {}): void { + super.pushAction(arg, options); + this.resizeToolbar(); + } + + protected focusNext(): void { + if (typeof this._focusedItem === 'undefined') { + this._focusedItem = this._items.length - 1; + } + + let startIndex = this._focusedItem; + let item: IActionViewItem; + + do { + this._focusedItem = (this._focusedItem + 1) % this._items.length; + item = this._items[this._focusedItem]; + } while (this._focusedItem !== startIndex && item && !item.isEnabled()); + + if (this._focusedItem === startIndex && item && !item.isEnabled()) { + this._focusedItem = undefined; + } + + this.updateFocus(); + } + + protected focusPrevious(): void { + if (typeof this._focusedItem === 'undefined') { + this._focusedItem = 0; + } + + let startIndex = this._focusedItem; + let item: IActionViewItem; + + do { + this._focusedItem = this._focusedItem - 1; + + if (this._focusedItem < 0) { + this._focusedItem = this._items.length - 1; + } + + item = this._items[this._focusedItem]; + } while (this._focusedItem !== startIndex && item && !item.isEnabled()); + + if (this._focusedItem === startIndex && item && !item.isEnabled()) { + this._focusedItem = undefined; + } + + this.updateFocus(); + } + + protected updateFocus(): void { + if (typeof this._focusedItem === 'undefined') { + this._domNode.focus(); + return; + } + + for (let i = 0; i < this._items.length; i++) { + let item = this._items[i]; + + let actionItem = item; + + if (i === this._focusedItem) { + // placeholder for location of moreActionsElement + if (!actionItem) { + this._moreActionsElement.focus(); + } + else if (types.isFunction(actionItem.focus)) { + actionItem.focus(); + } + } else { + if (actionItem && types.isFunction(actionItem.blur)) { + actionItem.blur(); + } + } + } + } + + protected cancel(): void { + super.cancel(); + + if (this._overflow) { + this.hideOverflowDisplay(); + } + } + + public run(action: IAction, context?: any): Promise { + this.hideOverflowDisplay(); + return this._actionRunner.run(action, context); + } +} diff --git a/src/sql/base/browser/ui/taskbar/overflowActionbarStyles.ts b/src/sql/base/browser/ui/taskbar/overflowActionbarStyles.ts new file mode 100644 index 0000000000..ad1a62075a --- /dev/null +++ b/src/sql/base/browser/ui/taskbar/overflowActionbarStyles.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// eslint-disable-next-line code-import-patterns +import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; +// eslint-disable-next-line code-import-patterns +import { EDITOR_PANE_BACKGROUND, DASHBOARD_BORDER, TOOLBAR_OVERFLOW_SHADOW } from 'vs/workbench/common/theme'; +// eslint-disable-next-line code-import-patterns +import { focusBorder } from 'vs/platform/theme/common/colorRegistry'; + +registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { + const overflowBackground = theme.getColor(EDITOR_PANE_BACKGROUND); + if (overflowBackground) { + collector.addRule(`.carbon-taskbar .overflow { + background-color: ${overflowBackground}; + }`); + } + + const overflowShadow = theme.getColor(TOOLBAR_OVERFLOW_SHADOW); + if (overflowShadow) { + collector.addRule(`.carbon-taskbar .overflow { + box-shadow: 0px 4px 4px ${overflowShadow}; + }`); + } + + const border = theme.getColor(DASHBOARD_BORDER); + if (border) { + collector.addRule(`.carbon-taskbar .overflow { + border: 1px solid ${border}; + }`); + } + + const activeOutline = theme.getColor(focusBorder); + if (activeOutline) { + collector.addRule(`.carbon-taskbar .overflow li.focused { + outline: 1px solid; + outline-offset: -3px; + outline-color: ${activeOutline} + }`); + } +}); diff --git a/src/sql/base/browser/ui/taskbar/taskbar.ts b/src/sql/base/browser/ui/taskbar/taskbar.ts index ebceddbb7d..312cded555 100644 --- a/src/sql/base/browser/ui/taskbar/taskbar.ts +++ b/src/sql/base/browser/ui/taskbar/taskbar.ts @@ -11,6 +11,7 @@ import { ActionBar } from './actionbar'; import { IActionRunner, IAction } from 'vs/base/common/actions'; import { ActionsOrientation, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IToolBarOptions } from 'vs/base/browser/ui/toolbar/toolbar'; +import { OverflowActionBar } from 'sql/base/browser/ui/taskbar/overflowActionbar'; /** * A wrapper for the different types of content a QueryTaskbar can display @@ -33,20 +34,36 @@ export class Taskbar { private options: IToolBarOptions; private actionBar: ActionBar; - constructor(container: HTMLElement, options: IToolBarOptions = { orientation: ActionsOrientation.HORIZONTAL }) { + constructor(container: HTMLElement, options: IToolBarOptions = { orientation: ActionsOrientation.HORIZONTAL }, collapseOverflow: boolean = false) { this.options = options; let element = document.createElement('div'); element.className = 'monaco-toolbar carbon-taskbar'; container.appendChild(element); - this.actionBar = new ActionBar(element, { - orientation: options.orientation, - ariaLabel: options.ariaLabel, - actionViewItemProvider: (action: IAction): IActionViewItem | undefined => { - return options.actionViewItemProvider ? options.actionViewItemProvider(action) : undefined; - } - }); + if (collapseOverflow) { + this.actionBar = new OverflowActionBar( + element, + { + orientation: options.orientation, + ariaLabel: options.ariaLabel, + actionViewItemProvider: (action: IAction): IActionViewItem | undefined => { + return options.actionViewItemProvider ? options.actionViewItemProvider(action) : undefined; + } + } + ); + } else { + this.actionBar = new ActionBar( + element, + { + orientation: options.orientation, + ariaLabel: options.ariaLabel, + actionViewItemProvider: (action: IAction): IActionViewItem | undefined => { + return options.actionViewItemProvider ? options.actionViewItemProvider(action) : undefined; + } + } + ); + } } /** diff --git a/src/sql/workbench/contrib/dashboard/browser/core/dashboardPage.component.ts b/src/sql/workbench/contrib/dashboard/browser/core/dashboardPage.component.ts index 2694812395..3e75c4c0fa 100644 --- a/src/sql/workbench/contrib/dashboard/browser/core/dashboardPage.component.ts +++ b/src/sql/workbench/contrib/dashboard/browser/core/dashboardPage.component.ts @@ -202,7 +202,7 @@ export abstract class DashboardPage extends AngularDisposable implements IConfig private createToolbar(parentElement: HTMLElement, tabId: string): void { // clear out toolbar DOM.clearNode(parentElement); - this.toolbar = this._register(new Taskbar(parentElement, { actionViewItemProvider: action => this.createActionItemProvider(action as Action) })); + this.toolbar = this._register(new Taskbar(parentElement, { actionViewItemProvider: action => this.createActionItemProvider(action as Action) }, true)); let content = []; content = this.getToolbarContent(tabId); if (tabId === this.homeTabId) { diff --git a/src/sql/workbench/contrib/dashboard/browser/core/dashboardPanelStyles.ts b/src/sql/workbench/contrib/dashboard/browser/core/dashboardPanelStyles.ts index 7d660e6ed3..bb0446b7e8 100644 --- a/src/sql/workbench/contrib/dashboard/browser/core/dashboardPanelStyles.ts +++ b/src/sql/workbench/contrib/dashboard/browser/core/dashboardPanelStyles.ts @@ -136,7 +136,7 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = } const sideBorder = theme.getColor(DASHBOARD_BORDER); - if (divider) { + if (sideBorder) { collector.addRule(`panel.dashboard-panel > .tabbedPanel.vertical > .title > .tabContainer { border-right-width: 1px; border-right-style: solid; diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 5e32e7dae9..e2833c75de 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { registerColor, editorBackground, contrastBorder, transparent, editorWidgetBackground, textLinkForeground, lighten, darken, focusBorder, activeContrastBorder, editorWidgetForeground, editorErrorForeground, editorWarningForeground, editorInfoForeground } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme } from 'vs/platform/theme/common/themeService'; -import { Color } from 'vs/base/common/color'; +import { Color, RGBA } from 'vs/base/common/color'; // < --- Workbench (not customizable) --- > @@ -636,3 +636,9 @@ export const DASHBOARD_PROPERTIES_NAME = registerColor('dashboardWidget.properti dark: '#8A8886', hc: '#FFFFFF' }, nls.localize('dashboardWidgetPropertiesName', "Color for dashboard properties widget names")); + +export const TOOLBAR_OVERFLOW_SHADOW = registerColor('toolbar.overflowShadow', { + light: new Color(new RGBA(0, 0, 0, .132)), + dark: new Color(new RGBA(0, 0, 0, 0.25)), + hc: null +}, nls.localize('toolbarOverflowShadow', "Toolbar overflow shadow color"));