Skip to content

Commit

Permalink
all sidebar tab icons are now svg-ed
Browse files Browse the repository at this point in the history
  • Loading branch information
telamonian committed Jun 4, 2019
1 parent 09f8ee0 commit 7817ce4
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 26 deletions.
94 changes: 93 additions & 1 deletion packages/application/src/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

import { DocumentRegistry } from '@jupyterlab/docregistry';

import { defaultIconRegistry } from '@jupyterlab/ui-components';

import { ArrayExt, find, IIterator, iter, toArray } from '@phosphor/algorithm';

import { PromiseDelegate, Token } from '@phosphor/coreutils';
Expand All @@ -11,6 +13,8 @@ import { Message, MessageLoop, IMessageHandler } from '@phosphor/messaging';

import { ISignal, Signal } from '@phosphor/signaling';

import { h, VirtualElement } from '@phosphor/virtualdom';

import {
BoxLayout,
BoxPanel,
Expand Down Expand Up @@ -1019,6 +1023,61 @@ namespace Private {
});
}

/**
* The default implementation of `IRenderer`.
*
* #### Notes
* Subclasses are free to reimplement rendering methods as needed.
*/
class SideBarRenderer extends TabBar.Renderer {
// /**
// * Render the virtual element for a tab.
// *
// * @param data - The data to use for rendering the tab.
// *
// * @returns A virtual element representing the tab.
// */
// renderTab(data: TabBar.IRenderData<any>): VirtualElement {
// let title = data.title.caption;
// let key = this.createTabKey(data);
// let style = this.createTabStyle(data);
// let className = this.createTabClass(data);
// let dataset = this.createTabDataset(data);
// return (
// h.li({ key, className, title, style, dataset },
// this.renderIcon(data),
// this.renderLabel(data),
// this.renderCloseIcon(data)
// )
// );
// }

/**
* Render the icon element for a tab.
*
* @param data - The data to use for rendering the tab.
*
* @returns A virtual element representing the tab icon.
*/
renderIcon(data: TabBar.IRenderData<any>): VirtualElement {
let className = this.createIconClass(data);
return h.div({ className });
}

// /**
// * Create the class name for the tab icon.
// *
// * @param data - The data to use for the tab.
// *
// * @returns The full class name for the tab icon.
// */
// createIconClass(data: TabBar.IRenderData<any>): string {
// let name = 'p-TabBar-tabIcon';
// let extra = data.title.iconClass;
// return extra ? `${name} ${extra}` : name;
// }
}

/**
* A class which manages a side bar and related stacked panel.
*/
Expand All @@ -1031,7 +1090,8 @@ namespace Private {
this._sideBar = new TabBar<Widget>({
insertBehavior: 'none',
removeBehavior: 'none',
allowDeselect: true
allowDeselect: true,
renderer: new SideBarRenderer()
});
this._stackedPanel = new StackedPanel();
this._sideBar.hide();
Expand Down Expand Up @@ -1177,6 +1237,38 @@ namespace Private {
private _refreshVisibility(): void {
this._sideBar.setHidden(this._sideBar.titles.length === 0);
this._stackedPanel.setHidden(this._sideBar.currentTitle === null);

// filthy hack to get the tab icons
defaultIconRegistry.override({
name: 'extension',
className: 'jp-ExtensionIcon',
center: true,
kind: 'sideBar'
});
defaultIconRegistry.override({
name: 'folder',
className: 'jp-FolderIcon',
center: true,
kind: 'sideBar'
});
defaultIconRegistry.override({
name: 'palette',
className: 'jp-PaletteIcon',
center: true,
kind: 'sideBar'
});
defaultIconRegistry.override({
name: 'running',
className: 'jp-RunningIcon',
center: true,
kind: 'sideBar'
});
defaultIconRegistry.override({
name: 'tab',
className: 'jp-TabIcon',
center: true,
kind: 'sideBar'
});
}

/**
Expand Down
10 changes: 5 additions & 5 deletions packages/application/style/icons.css
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
}

.jp-ExtensionIcon {
background-image: var(--jp-icon-extension);
/*background-image: var(--jp-icon-extension);*/
}

.jp-FileIcon {
Expand All @@ -120,7 +120,7 @@
}

.jp-FolderIcon {
background-image: var(--jp-icon-folder);
/*background-image: var(--jp-icon-folder);*/
}

.jp-HomeIcon {
Expand Down Expand Up @@ -190,7 +190,7 @@
}

.jp-PaletteIcon {
background-image: var(--jp-icon-palette);
/*background-image: var(--jp-icon-palette);*/
}

.jp-PasteIcon {
Expand All @@ -214,7 +214,7 @@
}

.jp-RunningIcon {
background-image: var(--jp-icon-stop-circle);
/*background-image: var(--jp-icon-stop-circle);*/
}

.jp-SaveIcon {
Expand All @@ -234,7 +234,7 @@
}

.jp-TabIcon {
background-image: var(--jp-icon-tab);
/*background-image: var(--jp-icon-tab);*/
}

.jp-TerminalIcon {
Expand Down
1 change: 1 addition & 0 deletions packages/running-extension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function activate(
): void {
let running = new RunningSessions({ manager: app.serviceManager });
running.id = 'jp-running-sessions';
running.title.icon = 'running';
running.title.iconClass = 'jp-RunningIcon jp-SideBar-tabIcon';
running.title.caption = 'Running Terminals and Kernels';

Expand Down
101 changes: 82 additions & 19 deletions packages/ui-components/src/icon/icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,42 +22,56 @@ import statusBarSvg from '../../style/icons/status-bar.svg';
import terminalSvg from '../../style/icons/terminal.svg';
import trustedSvg from '../../style/icons/trusted.svg';

const _defaultIcons: ReadonlyArray<IconRegistry.IModel> = [
import extensionSvg from '../../style/tabicons/extension.svg'; // originally ic-extension-24px.svg
import folderSvg from '../../style/tabicons/folder.svg'; // originally ic-folder-24px.svg
import paletteSvg from '../../style/tabicons/palette.svg'; // originally ic-palette-24px.svg
import runningSvg from '../../style/tabicons/running.svg'; // originally stop-circle.svg
import tabSvg from '../../style/tabicons/tab.svg'; // originally ic-tab-24px.svg

const _defaultIcons: ReadonlyArray<IconRegistry.IconModel> = [
{ name: 'html5', svg: html5Svg },
{ name: 'kernel', svg: kernelSvg },
{ name: 'jupyter-favicon', svg: jupyterFaviconSvg },
{ name: 'line-form', svg: lineFormSvg },
{ name: 'not-trusted', svg: notTrustedSvg },
{ name: 'status-bar', svg: statusBarSvg },
{ name: 'terminal', svg: terminalSvg },
{ name: 'trusted', svg: trustedSvg }
{ name: 'trusted', svg: trustedSvg },

{ name: 'extension', svg: extensionSvg },
{ name: 'folder', svg: folderSvg },
{ name: 'palette', svg: paletteSvg },
{ name: 'running', svg: runningSvg },
{ name: 'tab', svg: tabSvg }
];
/* tslint:enable */

/**
* The icon registry class.
*/
export class IconRegistry {
constructor(...icons: IconRegistry.IModel[]) {
constructor(...icons: IconRegistry.IconModel[]) {
if (icons.length) {
this.addIcon(...icons);
} else {
this.addIcon(..._defaultIcons);
}
}

addIcon(...icons: IconRegistry.IModel[]): void {
icons.forEach(
(icon: IconRegistry.IModel) => (this._svgs[icon.name] = icon.svg)
);
addIcon(...icons: IconRegistry.IconModel[]): void {
icons.forEach((icon: IconRegistry.IModel) => {
this._classNameToName[icon.className] = icon.name;
this._nameToClassName[icon.name] = icon.className;
this._svgs[icon.name] = icon.svg;
});
}

svg(name: string): string {
if (!(name in this._svgs)) {
console.error(`Invalid icon name: ${name}`);
}
get classNameToName(): { [key: string]: string } {
return this._classNameToName;
}

return this._svgs[name];
get nameToClassName(): { [key: string]: string } {
return this._nameToClassName;
}

/**
Expand All @@ -78,13 +92,13 @@ export class IconRegistry {
container.textContent = '';
container.appendChild(svgNode);

// add icon styling class to the container, if not already present
let classNames = classes(
className,
propsStyle ? iconStyle(propsStyle) : ''
);
if (!container.className.includes(classNames)) {
container.className = classes(container.className, classNames);
let styleClass = propsStyle ? iconStyle(propsStyle) : '';
if (className) {
// override the className, if explicitly passed
container.className = classes(className, styleClass);
} else if (!container.className.includes(styleClass)) {
// add icon styling class to the container's class, if not already present
container.className = classes(container.className, styleClass);
}
} else {
// add icon styling class directly to the svg node
Expand Down Expand Up @@ -115,6 +129,30 @@ export class IconRegistry {
);
}

override(props: { name: string; className: string } & IIconStyle) {
const { name, className, ...propsStyle } = props;

for (let container of document.getElementsByClassName(
className
) as HTMLCollectionOf<HTMLElement>) {
defaultIconRegistry.icon({
name: name,
container: container,
...propsStyle
});
}
}

svg(name: string): string {
if (!(name in this._svgs)) {
console.error(`Invalid icon name: ${name}`);
}

return this._svgs[name];
}

private _classNameToName: { [key: string]: string } = Object.create(null);
private _nameToClassName: { [key: string]: string } = Object.create(null);
private _svgs: { [key: string]: string } = Object.create(null);
}

Expand All @@ -135,6 +173,19 @@ export const IconReact = (
export namespace IconRegistry {
export interface IModel {
name: string;
className?: string;
svg: string;
}

export class IconModel implements IModel {
constructor(imod: IModel) {
if (!imod.className) {
this.className = Private.classNameFromName(imod.name);
}
}

name: string;
className?: string;
svg: string;
}

Expand All @@ -148,6 +199,18 @@ export namespace IconRegistry {
}

namespace Private {
function camelize(str: string): string {
return str.replace(/(?:^\w|[A-Z]|\b\w|\s+|-+)/g, function(match, index) {
console.log(match);
if (+match === 0 || match[0] === '-') return '';
return match.toUpperCase();
});
}

export function classNameFromName(name: string): string {
return 'jp-' + camelize(name);
}

export function parseSvg(svg: string): HTMLElement {
let parser = new DOMParser();
return parser.parseFromString(svg, 'image/svg+xml').documentElement;
Expand Down
6 changes: 5 additions & 1 deletion packages/ui-components/src/style/icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ const iconCSSListing: NestedCSSProperties = {
height: '16px'
};

const iconCSSSideBar: NestedCSSProperties = {
width: '20px'
};

const iconCSSStatusBar: NestedCSSProperties = {
left: '0px',
top: '0px',
Expand All @@ -47,7 +51,7 @@ const iconCSSTab: NestedCSSProperties = {

const iconCSSKind: { [k in iconKindType]: NestedCSSProperties } = {
listing: iconCSSListing,
sideBar: {},
sideBar: iconCSSSideBar,
statusBar: iconCSSStatusBar,
tab: iconCSSTab,
unset: {}
Expand Down
7 changes: 7 additions & 0 deletions packages/ui-components/style/tabicons/extension.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions packages/ui-components/style/tabicons/folder.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions packages/ui-components/style/tabicons/palette.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions packages/ui-components/style/tabicons/running.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7817ce4

Please sign in to comment.