From 40d8bc6538d063829055d81e3595d0f2778817f6 Mon Sep 17 00:00:00 2001 From: telamonian Date: Wed, 8 Jan 2020 23:53:57 -0500 Subject: [PATCH 01/72] replaced JLIcon (and unwanted React sub-dep) in rendermime-interfaces --- packages/rendermime-interfaces/package.json | 2 +- packages/rendermime-interfaces/src/index.ts | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/rendermime-interfaces/package.json b/packages/rendermime-interfaces/package.json index f2dd0284112f..d776e84474a8 100644 --- a/packages/rendermime-interfaces/package.json +++ b/packages/rendermime-interfaces/package.json @@ -31,8 +31,8 @@ "watch": "tsc -b --watch" }, "dependencies": { - "@jupyterlab/ui-components": "^2.0.0-beta.2", "@lumino/coreutils": "^1.4.2", + "@lumino/virtualdom": "^1.4.0", "@lumino/widgets": "^1.10.0" }, "devDependencies": { diff --git a/packages/rendermime-interfaces/src/index.ts b/packages/rendermime-interfaces/src/index.ts index 5c25ebd7237a..d86def649948 100644 --- a/packages/rendermime-interfaces/src/index.ts +++ b/packages/rendermime-interfaces/src/index.ts @@ -4,8 +4,9 @@ |----------------------------------------------------------------------------*/ import { ReadonlyPartialJSONObject } from '@lumino/coreutils'; +import { VirtualElementPass } from '@lumino/virtualdom'; + import { Widget } from '@lumino/widgets'; -import { JLIcon } from '@jupyterlab/ui-components'; /** * A namespace for rendermime associated interfaces. @@ -158,9 +159,12 @@ export namespace IRenderMime { readonly iconLabel?: string; /** - * The icon (as JLIcon) for the file type. + * Set the icon renderer for the title. + * + * #### Notes + * A renderer is an object that supplies render and unrender functions. */ - readonly iconRenderer?: JLIcon; + readonly iconRenderer?: VirtualElementPass.IRenderer; /** * The file format for the file type ('text', 'base64', or 'json'). From 542a428b9d6aea2e4c5a3af22bb6185a893bd4b2 Mon Sep 17 00:00:00 2001 From: telamonian Date: Thu, 9 Jan 2020 23:29:15 -0500 Subject: [PATCH 02/72] improved accuracy of IFileType.iconRenderer typing --- packages/application/src/shell.ts | 25 +++--- packages/docregistry/src/registry.ts | 4 +- packages/extensionmanager/src/widget.tsx | 8 +- packages/filebrowser/src/listing.ts | 2 +- packages/rendermime-interfaces/src/index.ts | 19 ++++- packages/ui-components/src/icon/jlicon.tsx | 89 ++++++++++++++------ packages/ui-components/src/icon/tabbarsvg.ts | 12 +-- 7 files changed, 101 insertions(+), 58 deletions(-) diff --git a/packages/application/src/shell.ts b/packages/application/src/shell.ts index 3a00cf5fc3b3..111320120ac5 100644 --- a/packages/application/src/shell.ts +++ b/packages/application/src/shell.ts @@ -3,7 +3,7 @@ import { DocumentRegistry } from '@jupyterlab/docregistry'; -import { DockPanelSvg, JLIcon } from '@jupyterlab/ui-components'; +import { classes, DockPanelSvg } from '@jupyterlab/ui-components'; import { ArrayExt, find, IIterator, iter, toArray } from '@lumino/algorithm'; @@ -30,6 +30,7 @@ import { } from '@lumino/widgets'; import { JupyterFrontEnd } from './frontend'; +import { iconStyle } from '@jupyterlab/ui-components/lib/style/icon'; /** * The class name added to AppShell instances. @@ -773,12 +774,11 @@ export class LabShell extends Widget implements JupyterFrontEnd.IShell { title.dataset = { ...title.dataset, id: widget.id }; // set an appropriate style class for the iconRenderer - if (title.iconRenderer instanceof JLIcon) { - title.iconClass = title.iconRenderer.class({ - className: title.iconClass, - justify: 'center', - kind: 'mainAreaTab' - }); + if (title.iconRenderer) { + title.iconClass = classes( + title.iconClass, + iconStyle({ justify: 'center', kind: 'mainAreaTab' }) + ); } dock.addWidget(widget, { mode, ref }); @@ -1185,12 +1185,11 @@ namespace Private { title.dataset = { id: widget.id }; // set an appropriate style class for the iconRenderer - if (title.iconRenderer instanceof JLIcon) { - title.iconClass = title.iconRenderer.class({ - className: title.iconClass, - justify: 'center', - kind: 'sideBar' - }); + if (title.iconRenderer) { + title.iconClass = classes( + title.iconClass, + iconStyle({ justify: 'center', kind: 'sideBar' }) + ); } this._refreshVisibility(); diff --git a/packages/docregistry/src/registry.ts b/packages/docregistry/src/registry.ts index 4e9217a8adfe..807ecdd31201 100644 --- a/packages/docregistry/src/registry.ts +++ b/packages/docregistry/src/registry.ts @@ -1186,9 +1186,9 @@ export namespace DocumentRegistry { readonly iconLabel?: string; /** - * The icon (as JLIcon) for the file type. + * Set the icon renderer for the title. */ - readonly iconRenderer?: JLIcon; + readonly iconRenderer?: JLIcon.IJLIcon; /** * The content type of the new file. diff --git a/packages/extensionmanager/src/widget.tsx b/packages/extensionmanager/src/widget.tsx index fc95a5765410..001f1bb61faf 100644 --- a/packages/extensionmanager/src/widget.tsx +++ b/packages/extensionmanager/src/widget.tsx @@ -19,6 +19,7 @@ import ReactPaginate from 'react-paginate'; import { ListModel, IEntry, Action } from './model'; import { isJupyterOrg } from './query'; +import { iconStyle } from '@jupyterlab/ui-components/lib/style/icon'; // TODO: Replace pagination with lazy loading of lower search results @@ -343,7 +344,6 @@ export class CollapsibleSection extends React.Component< }; } - // TODO: swtich to iconRenderer /** * Render the collapsible section using the virtual DOM. */ @@ -353,11 +353,7 @@ export class CollapsibleSection extends React.Component<
{ this.handleCollapse(); }} diff --git a/packages/filebrowser/src/listing.ts b/packages/filebrowser/src/listing.ts index 2593ff5b77d9..603a6bf99b09 100644 --- a/packages/filebrowser/src/listing.ts +++ b/packages/filebrowser/src/listing.ts @@ -2113,7 +2113,7 @@ namespace Private { ...propsStyle }); } else { - caretDownIcon.recycle({ container, ...propsStyle }); + JLIcon.remove(container); } } } diff --git a/packages/rendermime-interfaces/src/index.ts b/packages/rendermime-interfaces/src/index.ts index d86def649948..9e01f20a9492 100644 --- a/packages/rendermime-interfaces/src/index.ts +++ b/packages/rendermime-interfaces/src/index.ts @@ -160,11 +160,8 @@ export namespace IRenderMime { /** * Set the icon renderer for the title. - * - * #### Notes - * A renderer is an object that supplies render and unrender functions. */ - readonly iconRenderer?: VirtualElementPass.IRenderer; + readonly iconRenderer?: IJLIcon; /** * The file format for the file type ('text', 'base64', or 'json'). @@ -231,6 +228,20 @@ export namespace IRenderMime { readonly default: IExtension | ReadonlyArray; } + /** + * The IJLIcon interface, which supplies element, render, + * and unrender functions + */ + export interface IJLIcon extends VirtualElementPass.IRenderer { + /** + * Create an icon as a DOM element + * + * @returns A DOM element that contains an (inline) svg element + * that displays an icon + */ + element: ({}) => HTMLElement; + } + /** * A widget which displays the contents of a mime model. */ diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index c79379ddd0db..9183e8558d8d 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -2,6 +2,7 @@ // Distributed under the terms of the Modified BSD License. import { UUID } from '@lumino/coreutils'; +import { VirtualElementPass } from '@lumino/virtualdom'; import React from 'react'; import ReactDOM from 'react-dom'; @@ -15,7 +16,7 @@ import blankSvg from '../../style/debug/blank.svg'; const blankDiv = document.createElement('div'); -export class JLIcon { +export class JLIcon implements JLIcon.IJLIcon { private static _debug: boolean = false; private static _instances = new Map(); @@ -108,6 +109,20 @@ export class JLIcon { return ; } + static remove(container: HTMLElement) { + // clean up all children + while (container.firstChild) { + container.firstChild.remove(); + } + + if (container.dataset?.iconClass) { + container.classList.remove(container.dataset.iconClass); + } + delete container.dataset?.iconClass; + + return container; + } + /** * Toggle icon debug from off-to-on, or vice-versa. * @@ -128,10 +143,31 @@ export class JLIcon { JLIcon._instances.set(this._className, this); } - class({ className, ...propsStyle }: { className?: string } & IIconStyle) { - return classesDedupe(className, iconStyle(propsStyle)); - } - + /** + * Create an icon as a DOM element + * + * @param className - a string that will be used as the class + * of the container element. Overrides any existing class + * + * @param container - a preexisting DOM element that + * will be used as the container for the svg element + * + * @param label - text that will be displayed adjacent + * to the icon + * + * @param title - a tooltip for the icon + * + * @param tag - if container is not explicitly + * provided, this tag will be used when creating the container + * + * @propsStyle - style parameters that get passed to TypeStyle in + * order to generate a style class. The style class will be added + * to the icon container's classes, while the style itself will be + * applied to any svg elements within the container. + * + * @returns A DOM element that contains an (inline) svg element + * that displays an icon + */ element({ className, container, @@ -179,25 +215,6 @@ export class JLIcon { return ret; } - recycle({ - className, - container, - ...propsStyle - }: { className?: string; container: HTMLElement } & IIconStyle): HTMLElement { - // clean up all children - while (container.firstChild) { - container.firstChild.remove(); - } - - // clean up any icon-related class names - const cls = this.class({ className, ...propsStyle }); - if (cls) { - container.classList.remove(cls); - } - - return container; - } - render(host: HTMLElement, props: JLIcon.IProps = {}): void { // TODO: move this title fix to the Lumino side host.removeAttribute('title'); @@ -268,10 +285,15 @@ export class JLIcon { if (className != null) { // override the container class with explicitly passed-in class + style class - container.className = classes(className, classStyle); + const classResolved = classes(className, classStyle); + container.className = classResolved; + container.dataset.iconClass = classResolved; } else if (classStyle) { // add the style class to the container class container.classList.add(classStyle); + container.dataset.iconClass = classStyle; + } else { + container.dataset.iconClass = ''; } if (title != null) { @@ -319,8 +341,9 @@ export class JLIcon { ); } else { + const classResolved = classes(className, iconStyle(propsStyle)); return ( - + {svgComponent} {label} @@ -345,6 +368,20 @@ export class JLIcon { * A namespace for JLIcon statics. */ export namespace JLIcon { + /** + * The IJLIcon interface, which supplies element, render, + * and unrender functions + */ + export interface IJLIcon extends VirtualElementPass.IRenderer { + /** + * Create an icon as a DOM element + * + * @returns A DOM element that contains an (inline) svg element + * that displays an icon + */ + element: ({}) => HTMLElement; + } + /** * The type of the JLIcon contructor params */ diff --git a/packages/ui-components/src/icon/tabbarsvg.ts b/packages/ui-components/src/icon/tabbarsvg.ts index b9749e4f0162..1ca9abe629f7 100644 --- a/packages/ui-components/src/icon/tabbarsvg.ts +++ b/packages/ui-components/src/icon/tabbarsvg.ts @@ -5,6 +5,8 @@ import { hpass, VirtualElement } from '@lumino/virtualdom'; import { DockPanel, TabBar, Widget } from '@lumino/widgets'; import { closeIcon } from './iconimports'; +import { iconStyle } from '../style/icon'; +import { classes } from '../utils'; /** * a widget which displays titles as a single row or column of tabs. @@ -36,12 +38,10 @@ export namespace TabBarSvg { * @returns A virtual element representing the tab close icon. */ renderCloseIcon(data: TabBar.IRenderData): VirtualElement { - const className = closeIcon.class({ - className: 'jp-icon-hover lm-TabBar-tabCloseIcon', - justify: 'center', - height: '16px', - width: '16px' - }); + const className = classes( + 'jp-icon-hover lm-TabBar-tabCloseIcon', + iconStyle({ justify: 'center', height: '16px', width: '16px' }) + ); return (hpass( 'div', From 00adfd5467a3cfb5ac4f479d8496097cf1bf6d4e Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 10 Jan 2020 13:51:40 -0500 Subject: [PATCH 03/72] use render method for listings icons, simplify iconRenderer typing --- packages/docregistry/src/registry.ts | 9 +++-- packages/filebrowser/src/listing.ts | 28 ++++++++----- packages/rendermime-interfaces/src/index.ts | 20 ++-------- packages/ui-components/src/icon/jlicon.tsx | 44 ++++++++------------- 4 files changed, 45 insertions(+), 56 deletions(-) diff --git a/packages/docregistry/src/registry.ts b/packages/docregistry/src/registry.ts index 807ecdd31201..b7a3a114d6a1 100644 --- a/packages/docregistry/src/registry.ts +++ b/packages/docregistry/src/registry.ts @@ -17,6 +17,8 @@ import { IDisposable, DisposableDelegate } from '@lumino/disposable'; import { ISignal, Signal } from '@lumino/signaling'; +import { VirtualElementPass } from '@lumino/virtualdom'; + import { DockLayout, Widget } from '@lumino/widgets'; import { ISessionContext, Toolbar } from '@jupyterlab/apputils'; @@ -38,7 +40,6 @@ import { fileIcon, folderIcon, imageIcon, - JLIcon, jsonIcon, markdownIcon, notebookIcon, @@ -1186,9 +1187,11 @@ export namespace DocumentRegistry { readonly iconLabel?: string; /** - * Set the icon renderer for the title. + * Set the icon renderer for the title. An icon renderer is any object + * with appropriate render and unrender methods. See JLIcon in + * @jupyterlab/ui-components for a reference implementation. */ - readonly iconRenderer?: JLIcon.IJLIcon; + readonly iconRenderer?: VirtualElementPass.IRenderer; /** * The content type of the new file. diff --git a/packages/filebrowser/src/listing.ts b/packages/filebrowser/src/listing.ts index 603a6bf99b09..767a330041cc 100644 --- a/packages/filebrowser/src/listing.ts +++ b/packages/filebrowser/src/listing.ts @@ -23,6 +23,7 @@ import { Contents } from '@jupyterlab/services'; import { caretDownIcon, caretUpIcon, + classes, fileIcon, JLIcon } from '@jupyterlab/ui-components'; @@ -51,7 +52,10 @@ import { ISignal, Signal } from '@lumino/signaling'; import { Widget } from '@lumino/widgets'; import { FileBrowserModel } from './model'; -import { IIconStyle } from '@jupyterlab/ui-components/lib/style/icon'; +import { + iconStyle, + IIconStyle +} from '@jupyterlab/ui-components/lib/style/icon'; /** * The class name added to DirListing widget. @@ -1834,20 +1838,24 @@ export namespace DirListing { const text = DOMUtils.findElement(node, ITEM_TEXT_CLASS); const modified = DOMUtils.findElement(node, ITEM_MODIFIED_CLASS); - const iconProps: JLIcon.IProps = { - className: ITEM_ICON_CLASS, - container: iconContainer, - justify: 'center', - kind: 'listing' - }; + iconContainer.className = classes( + ITEM_ICON_CLASS, + iconStyle({ + justify: 'center', + kind: 'listing' + }) + ); // render the icon svg node if (fileType?.iconRenderer) { - fileType.iconRenderer.element(iconProps); + fileType.iconRenderer.render(iconContainer); } else if (fileType?.iconClass) { - JLIcon.getElement({ name: fileType.iconClass, ...iconProps }); + JLIcon.getElement({ + name: fileType.iconClass, + container: iconContainer + }); } else { - fileIcon.element(iconProps); + fileIcon.render(iconContainer); } let hoverText = 'Name: ' + model.name; diff --git a/packages/rendermime-interfaces/src/index.ts b/packages/rendermime-interfaces/src/index.ts index 9e01f20a9492..83825a243868 100644 --- a/packages/rendermime-interfaces/src/index.ts +++ b/packages/rendermime-interfaces/src/index.ts @@ -159,9 +159,11 @@ export namespace IRenderMime { readonly iconLabel?: string; /** - * Set the icon renderer for the title. + * Set the icon renderer for the title. An icon renderer is any object + * with appropriate render and unrender methods. See JLIcon in + * @jupyterlab/ui-components for a reference implementation. */ - readonly iconRenderer?: IJLIcon; + readonly iconRenderer?: VirtualElementPass.IRenderer; /** * The file format for the file type ('text', 'base64', or 'json'). @@ -228,20 +230,6 @@ export namespace IRenderMime { readonly default: IExtension | ReadonlyArray; } - /** - * The IJLIcon interface, which supplies element, render, - * and unrender functions - */ - export interface IJLIcon extends VirtualElementPass.IRenderer { - /** - * Create an icon as a DOM element - * - * @returns A DOM element that contains an (inline) svg element - * that displays an icon - */ - element: ({}) => HTMLElement; - } - /** * A widget which displays the contents of a mime model. */ diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index 9183e8558d8d..27515070a0f8 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -4,7 +4,6 @@ import { UUID } from '@lumino/coreutils'; import { VirtualElementPass } from '@lumino/virtualdom'; import React from 'react'; -import ReactDOM from 'react-dom'; import { Text } from '@jupyterlab/coreutils'; @@ -16,7 +15,7 @@ import blankSvg from '../../style/debug/blank.svg'; const blankDiv = document.createElement('div'); -export class JLIcon implements JLIcon.IJLIcon { +export class JLIcon implements VirtualElementPass.IRenderer { private static _debug: boolean = false; private static _instances = new Map(); @@ -109,15 +108,20 @@ export class JLIcon implements JLIcon.IJLIcon { return ; } + /** + * Remove the svg element from a container element + */ static remove(container: HTMLElement) { // clean up all children while (container.firstChild) { container.firstChild.remove(); } + // remove the icon class recorded in the dataset if (container.dataset?.iconClass) { container.classList.remove(container.dataset.iconClass); } + // remove icon class from the dataset (even if empty) delete container.dataset?.iconClass; return container; @@ -215,11 +219,11 @@ export class JLIcon implements JLIcon.IJLIcon { return ret; } - render(host: HTMLElement, props: JLIcon.IProps = {}): void { + render(container: HTMLElement, props: JLIcon.IProps = {}): void { // TODO: move this title fix to the Lumino side - host.removeAttribute('title'); + container.removeAttribute('title'); - return ReactDOM.render(, host); + this.element({ container, ...props }); } resolveSvg(title?: string): HTMLElement | null { @@ -266,12 +270,11 @@ export class JLIcon implements JLIcon.IJLIcon { this._uuid = UUID.uuid4(); } - unrender(host: HTMLElement): void { - ReactDOM.unmountComponentAtNode(host); - } + unrender(container: HTMLElement): void {} protected _initContainer({ container, + className, propsStyle, title @@ -280,9 +283,12 @@ export class JLIcon implements JLIcon.IJLIcon { className?: string; propsStyle?: IIconStyle; title?: string; - }) { - const classStyle = iconStyle(propsStyle); + }): string { + if (title != null) { + container.title = title; + } + const classStyle = iconStyle(propsStyle); if (className != null) { // override the container class with explicitly passed-in class + style class const classResolved = classes(className, classStyle); @@ -296,9 +302,7 @@ export class JLIcon implements JLIcon.IJLIcon { container.dataset.iconClass = ''; } - if (title != null) { - container.title = title; - } + return container.dataset.iconClass; } protected _initReact() { @@ -368,20 +372,6 @@ export class JLIcon implements JLIcon.IJLIcon { * A namespace for JLIcon statics. */ export namespace JLIcon { - /** - * The IJLIcon interface, which supplies element, render, - * and unrender functions - */ - export interface IJLIcon extends VirtualElementPass.IRenderer { - /** - * Create an icon as a DOM element - * - * @returns A DOM element that contains an (inline) svg element - * that displays an icon - */ - element: ({}) => HTMLElement; - } - /** * The type of the JLIcon contructor params */ From 076cc66699ea4270e3b7b4b9fda9ea1033c83bcf Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 10 Jan 2020 18:05:33 -0500 Subject: [PATCH 04/72] integrity --- packages/ui-components/package.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 1986f225d135..69e447868c94 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -41,7 +41,6 @@ "@lumino/virtualdom": "^1.4.0", "@lumino/widgets": "^1.10.0", "react": "~16.9.0", - "react-dom": "~16.9.0", "typestyle": "^2.0.4" }, "devDependencies": { @@ -50,7 +49,6 @@ "@storybook/addon-actions": "^5.2.5", "@storybook/react": "^5.2.5", "@types/react": "~16.9.16", - "@types/react-dom": "~16.9.4", "@types/webpack-env": "^1.14.1", "awesome-typescript-loader": "^5.2.1", "babel-loader": "^8.0.6", From 5e2842e4daa7e4345295bbcb70aea4e5044d3b1d Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 10 Jan 2020 18:30:24 -0500 Subject: [PATCH 05/72] delinted --- packages/ui-components/src/icon/jlicon.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index 27515070a0f8..88e9c365d1fb 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -270,7 +270,9 @@ export class JLIcon implements VirtualElementPass.IRenderer { this._uuid = UUID.uuid4(); } - unrender(container: HTMLElement): void {} + unrender(container: HTMLElement): void { + return; + } protected _initContainer({ container, From b9aae6bcf8e336073b9bc5f3054e31bdfd523047 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 17 Jan 2020 20:18:58 -0500 Subject: [PATCH 06/72] initital stab at minimal IJLIcon interface current plan is to add to IJLIcon as the important bits of the JLIcon implementation get locked odwn --- packages/ui-components/src/icon/jlicon.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index 88e9c365d1fb..2582653eac45 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -2,7 +2,6 @@ // Distributed under the terms of the Modified BSD License. import { UUID } from '@lumino/coreutils'; -import { VirtualElementPass } from '@lumino/virtualdom'; import React from 'react'; import { Text } from '@jupyterlab/coreutils'; @@ -13,9 +12,7 @@ import { getReactAttrs, classes, classesDedupe } from '../utils'; import badSvg from '../../style/debug/bad.svg'; import blankSvg from '../../style/debug/blank.svg'; -const blankDiv = document.createElement('div'); - -export class JLIcon implements VirtualElementPass.IRenderer { +export class JLIcon implements JLIcon.IJLIcon { private static _debug: boolean = false; private static _instances = new Map(); @@ -190,8 +187,8 @@ export class JLIcon implements VirtualElementPass.IRenderer { // ensure that svg html is valid const svgElement = this.resolveSvg(); if (!svgElement) { - // bail if failing silently - return blankDiv; + // bail if failing silently, return blank element + return document.createElement('div'); } let ret: HTMLElement; @@ -375,9 +372,10 @@ export class JLIcon implements VirtualElementPass.IRenderer { */ export namespace JLIcon { /** - * The type of the JLIcon contructor params + * The IJLIcon interface. Outside of this interface the actual + * implementation of JLIcon may vary */ - export interface IOptions { + export interface IJLIcon { name: string; svgstr: string; } From fedda16aa92f611bbb43db75e3ed735a388bb85f Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 17 Jan 2020 20:29:40 -0500 Subject: [PATCH 07/72] bugfix --- packages/ui-components/src/icon/jlicon.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index 2582653eac45..269514c01850 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -133,7 +133,7 @@ export class JLIcon implements JLIcon.IJLIcon { JLIcon._debug = debug ?? !JLIcon._debug; } - constructor({ name, svgstr }: JLIcon.IOptions) { + constructor({ name, svgstr }: JLIcon.IJLIcon) { this.name = name; this._className = Private.nameToClassName(name); this.svgstr = svgstr; From 43570ea33268d906daa944d8c7383ea83eb8050a Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 17 Jan 2020 20:56:05 -0500 Subject: [PATCH 08/72] full revert of rendermime-interfaces; totally removed iconRenderer --- packages/rendermime-interfaces/package.json | 1 - packages/rendermime-interfaces/src/index.ts | 9 --------- packages/vega5-extension/package.json | 1 - packages/vega5-extension/src/index.ts | 7 +++---- packages/vega5-extension/style/index.css | 1 - packages/vega5-extension/tsconfig.json | 3 --- 6 files changed, 3 insertions(+), 19 deletions(-) diff --git a/packages/rendermime-interfaces/package.json b/packages/rendermime-interfaces/package.json index d776e84474a8..c121846c97aa 100644 --- a/packages/rendermime-interfaces/package.json +++ b/packages/rendermime-interfaces/package.json @@ -32,7 +32,6 @@ }, "dependencies": { "@lumino/coreutils": "^1.4.2", - "@lumino/virtualdom": "^1.4.0", "@lumino/widgets": "^1.10.0" }, "devDependencies": { diff --git a/packages/rendermime-interfaces/src/index.ts b/packages/rendermime-interfaces/src/index.ts index 83825a243868..f6417497924f 100644 --- a/packages/rendermime-interfaces/src/index.ts +++ b/packages/rendermime-interfaces/src/index.ts @@ -4,8 +4,6 @@ |----------------------------------------------------------------------------*/ import { ReadonlyPartialJSONObject } from '@lumino/coreutils'; -import { VirtualElementPass } from '@lumino/virtualdom'; - import { Widget } from '@lumino/widgets'; /** @@ -158,13 +156,6 @@ export namespace IRenderMime { */ readonly iconLabel?: string; - /** - * Set the icon renderer for the title. An icon renderer is any object - * with appropriate render and unrender methods. See JLIcon in - * @jupyterlab/ui-components for a reference implementation. - */ - readonly iconRenderer?: VirtualElementPass.IRenderer; - /** * The file format for the file type ('text', 'base64', or 'json'). */ diff --git a/packages/vega5-extension/package.json b/packages/vega5-extension/package.json index 3d10f9e13320..3dc4769f0a8e 100644 --- a/packages/vega5-extension/package.json +++ b/packages/vega5-extension/package.json @@ -35,7 +35,6 @@ }, "dependencies": { "@jupyterlab/rendermime-interfaces": "^2.0.0-beta.2", - "@jupyterlab/ui-components": "^2.0.0-beta.2", "@lumino/coreutils": "^1.4.2", "@lumino/widgets": "^1.10.0", "vega": "^5.9.0", diff --git a/packages/vega5-extension/src/index.ts b/packages/vega5-extension/src/index.ts index ff99d301882e..a147f2cd0a64 100644 --- a/packages/vega5-extension/src/index.ts +++ b/packages/vega5-extension/src/index.ts @@ -8,7 +8,6 @@ import { Widget } from '@lumino/widgets'; import * as VegaModuleType from 'vega-embed'; import { IRenderMime } from '@jupyterlab/rendermime-interfaces'; -import { vegaIcon } from '@jupyterlab/ui-components'; /** * The CSS class to add to the Vega and Vega-Lite widget. @@ -175,19 +174,19 @@ const extension: IRenderMime.IExtension = { mimeTypes: [VEGA_MIME_TYPE], name: 'vega5', extensions: ['.vg', '.vg.json', '.vega'], - iconRenderer: vegaIcon + iconClass: 'vega' }, { mimeTypes: [VEGALITE4_MIME_TYPE], name: 'vega-lite4', extensions: ['.vl', '.vl.json', '.vegalite'], - iconRenderer: vegaIcon + iconClass: 'vega' }, { mimeTypes: [VEGALITE3_MIME_TYPE], name: 'vega-lite3', extensions: [], - iconRenderer: vegaIcon + iconClass: 'vega' } ] }; diff --git a/packages/vega5-extension/style/index.css b/packages/vega5-extension/style/index.css index bd9f8cebad79..8fe68ab59e76 100644 --- a/packages/vega5-extension/style/index.css +++ b/packages/vega5-extension/style/index.css @@ -5,6 +5,5 @@ /* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ @import url('~@lumino/widgets/style/index.css'); -@import url('~@jupyterlab/ui-components/style/index.css'); @import url('./base.css'); diff --git a/packages/vega5-extension/tsconfig.json b/packages/vega5-extension/tsconfig.json index b6172864a0c9..a631202393b1 100644 --- a/packages/vega5-extension/tsconfig.json +++ b/packages/vega5-extension/tsconfig.json @@ -10,9 +10,6 @@ "references": [ { "path": "../rendermime-interfaces" - }, - { - "path": "../ui-components" } ] } From b185d801904e7dffbdbde4db35c4b85c95288772 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 17 Jan 2020 22:25:12 -0500 Subject: [PATCH 09/72] added namespacing to the JLIcon name prop --- buildutils/src/ensure-package.ts | 6 +- .../ui-components/src/icon/iconimports.ts | 128 +++++++++--------- packages/ui-components/src/icon/jlicon.tsx | 19 ++- packages/vega5-extension/src/index.ts | 6 +- 4 files changed, 87 insertions(+), 72 deletions(-) diff --git a/buildutils/src/ensure-package.ts b/buildutils/src/ensure-package.ts index fd953ea9ea81..c2d68522fa3e 100644 --- a/buildutils/src/ensure-package.ts +++ b/buildutils/src/ensure-package.ts @@ -355,6 +355,7 @@ export async function ensureUiComponents( dorequire: boolean = false ): Promise { const funcName = 'ensureUiComponents'; + const pkgName = utils.stem(pkgPath); let messages: string[] = []; const svgs = glob.sync(path.join(pkgPath, 'style/icons', '**/*.svg')); @@ -374,18 +375,19 @@ export async function ensureUiComponents( const svgname = utils.camelCase(name) + 'Svg'; const iconname = utils.camelCase(name) + 'Icon'; + const qualname = [pkgName, utils.stem(svg)].join(':'); if (dorequire) { // load the icon svg using `require` _jliconConstruction.push( - `export const ${iconname} = new JLIcon({ name: '${name}', svgstr: require('${svgpath}').default });` + `export const ${iconname} = new JLIcon({ name: '${qualname}', svgstr: require('${svgpath}').default });` ); } else { // load the icon svg using `import` _iconImportStatements.push(`import ${svgname} from '${svgpath}';`); _jliconConstruction.push( - `export const ${iconname} = new JLIcon({ name: '${name}', svgstr: ${svgname} });` + `export const ${iconname} = new JLIcon({ name: '${qualname}', svgstr: ${svgname} });` ); } }); diff --git a/packages/ui-components/src/icon/iconimports.ts b/packages/ui-components/src/icon/iconimports.ts index cc417095e512..80a115ab795a 100644 --- a/packages/ui-components/src/icon/iconimports.ts +++ b/packages/ui-components/src/icon/iconimports.ts @@ -74,67 +74,67 @@ import stopSvg from '../../style/icons/toolbar/stop.svg'; import undoSvg from '../../style/icons/toolbar/undo.svg'; // JLIcon instance construction -export const caretDownEmptyThinIcon = new JLIcon({ name: 'caret-down-empty-thin', svgstr: caretDownEmptyThinSvg }); -export const caretDownEmptyIcon = new JLIcon({ name: 'caret-down-empty', svgstr: caretDownEmptySvg }); -export const caretDownIcon = new JLIcon({ name: 'caret-down', svgstr: caretDownSvg }); -export const caretLeftIcon = new JLIcon({ name: 'caret-left', svgstr: caretLeftSvg }); -export const caretRightIcon = new JLIcon({ name: 'caret-right', svgstr: caretRightSvg }); -export const caretUpEmptyThinIcon = new JLIcon({ name: 'caret-up-empty-thin', svgstr: caretUpEmptyThinSvg }); -export const caretUpIcon = new JLIcon({ name: 'caret-up', svgstr: caretUpSvg }); -export const consoleIcon = new JLIcon({ name: 'console', svgstr: consoleSvg }); -export const fileIcon = new JLIcon({ name: 'file', svgstr: fileSvg }); -export const folderIcon = new JLIcon({ name: 'folder', svgstr: folderSvg }); -export const html5Icon = new JLIcon({ name: 'html5', svgstr: html5Svg }); -export const imageIcon = new JLIcon({ name: 'image', svgstr: imageSvg }); -export const inspectorIcon = new JLIcon({ name: 'inspector', svgstr: inspectorSvg }); -export const jsonIcon = new JLIcon({ name: 'json', svgstr: jsonSvg }); -export const keyboardIcon = new JLIcon({ name: 'keyboard', svgstr: keyboardSvg }); -export const launcherIcon = new JLIcon({ name: 'launcher', svgstr: launcherSvg }); -export const markdownIcon = new JLIcon({ name: 'markdown', svgstr: markdownSvg }); -export const notebookIcon = new JLIcon({ name: 'notebook', svgstr: notebookSvg }); -export const pythonIcon = new JLIcon({ name: 'python', svgstr: pythonSvg }); -export const rKernelIcon = new JLIcon({ name: 'r-kernel', svgstr: rKernelSvg }); -export const reactIcon = new JLIcon({ name: 'react', svgstr: reactSvg }); -export const settingsIcon = new JLIcon({ name: 'settings', svgstr: settingsSvg }); -export const spreadsheetIcon = new JLIcon({ name: 'spreadsheet', svgstr: spreadsheetSvg }); -export const textEditorIcon = new JLIcon({ name: 'text-editor', svgstr: textEditorSvg }); -export const vegaIcon = new JLIcon({ name: 'vega', svgstr: vegaSvg }); -export const yamlIcon = new JLIcon({ name: 'yaml', svgstr: yamlSvg }); -export const jupyterFaviconIcon = new JLIcon({ name: 'jupyter-favicon', svgstr: jupyterFaviconSvg }); -export const jupyterIcon = new JLIcon({ name: 'jupyter', svgstr: jupyterSvg }); -export const jupyterlabWordmarkIcon = new JLIcon({ name: 'jupyterlab-wordmark', svgstr: jupyterlabWordmarkSvg }); -export const caseSensitiveIcon = new JLIcon({ name: 'case-sensitive', svgstr: caseSensitiveSvg }); -export const regexIcon = new JLIcon({ name: 'regex', svgstr: regexSvg }); -export const buildIcon = new JLIcon({ name: 'build', svgstr: buildSvg }); -export const extensionIcon = new JLIcon({ name: 'extension', svgstr: extensionSvg }); -export const paletteIcon = new JLIcon({ name: 'palette', svgstr: paletteSvg }); -export const runningIcon = new JLIcon({ name: 'running', svgstr: runningSvg }); -export const tabIcon = new JLIcon({ name: 'tab', svgstr: tabSvg }); -export const kernelIcon = new JLIcon({ name: 'kernel', svgstr: kernelSvg }); -export const lineFormIcon = new JLIcon({ name: 'line-form', svgstr: lineFormSvg }); -export const listIcon = new JLIcon({ name: 'list', svgstr: listSvg }); -export const notTrustedIcon = new JLIcon({ name: 'not-trusted', svgstr: notTrustedSvg }); -export const terminalIcon = new JLIcon({ name: 'terminal', svgstr: terminalSvg }); -export const trustedIcon = new JLIcon({ name: 'trusted', svgstr: trustedSvg }); -export const addIcon = new JLIcon({ name: 'add', svgstr: addSvg }); -export const bugIcon = new JLIcon({ name: 'bug', svgstr: bugSvg }); -export const checkIcon = new JLIcon({ name: 'check', svgstr: checkSvg }); -export const circleEmptyIcon = new JLIcon({ name: 'circle-empty', svgstr: circleEmptySvg }); -export const circleIcon = new JLIcon({ name: 'circle', svgstr: circleSvg }); -export const closeIcon = new JLIcon({ name: 'close', svgstr: closeSvg }); -export const copyIcon = new JLIcon({ name: 'copy', svgstr: copySvg }); -export const cutIcon = new JLIcon({ name: 'cut', svgstr: cutSvg }); -export const downloadIcon = new JLIcon({ name: 'download', svgstr: downloadSvg }); -export const editIcon = new JLIcon({ name: 'edit', svgstr: editSvg }); -export const ellipsesIcon = new JLIcon({ name: 'ellipses', svgstr: ellipsesSvg }); -export const fileUploadIcon = new JLIcon({ name: 'file-upload', svgstr: fileUploadSvg }); -export const filterListIcon = new JLIcon({ name: 'filter-list', svgstr: filterListSvg }); -export const linkIcon = new JLIcon({ name: 'link', svgstr: linkSvg }); -export const newFolderIcon = new JLIcon({ name: 'new-folder', svgstr: newFolderSvg }); -export const pasteIcon = new JLIcon({ name: 'paste', svgstr: pasteSvg }); -export const refreshIcon = new JLIcon({ name: 'refresh', svgstr: refreshSvg }); -export const runIcon = new JLIcon({ name: 'run', svgstr: runSvg }); -export const saveIcon = new JLIcon({ name: 'save', svgstr: saveSvg }); -export const searchIcon = new JLIcon({ name: 'search', svgstr: searchSvg }); -export const stopIcon = new JLIcon({ name: 'stop', svgstr: stopSvg }); -export const undoIcon = new JLIcon({ name: 'undo', svgstr: undoSvg }); +export const caretDownEmptyThinIcon = new JLIcon({ name: 'ui-components:caret-down-empty-thin', svgstr: caretDownEmptyThinSvg }); +export const caretDownEmptyIcon = new JLIcon({ name: 'ui-components:caret-down-empty', svgstr: caretDownEmptySvg }); +export const caretDownIcon = new JLIcon({ name: 'ui-components:caret-down', svgstr: caretDownSvg }); +export const caretLeftIcon = new JLIcon({ name: 'ui-components:caret-left', svgstr: caretLeftSvg }); +export const caretRightIcon = new JLIcon({ name: 'ui-components:caret-right', svgstr: caretRightSvg }); +export const caretUpEmptyThinIcon = new JLIcon({ name: 'ui-components:caret-up-empty-thin', svgstr: caretUpEmptyThinSvg }); +export const caretUpIcon = new JLIcon({ name: 'ui-components:caret-up', svgstr: caretUpSvg }); +export const consoleIcon = new JLIcon({ name: 'ui-components:console', svgstr: consoleSvg }); +export const fileIcon = new JLIcon({ name: 'ui-components:file', svgstr: fileSvg }); +export const folderIcon = new JLIcon({ name: 'ui-components:folder', svgstr: folderSvg }); +export const html5Icon = new JLIcon({ name: 'ui-components:html5', svgstr: html5Svg }); +export const imageIcon = new JLIcon({ name: 'ui-components:image', svgstr: imageSvg }); +export const inspectorIcon = new JLIcon({ name: 'ui-components:inspector', svgstr: inspectorSvg }); +export const jsonIcon = new JLIcon({ name: 'ui-components:json', svgstr: jsonSvg }); +export const keyboardIcon = new JLIcon({ name: 'ui-components:keyboard', svgstr: keyboardSvg }); +export const launcherIcon = new JLIcon({ name: 'ui-components:launcher', svgstr: launcherSvg }); +export const markdownIcon = new JLIcon({ name: 'ui-components:markdown', svgstr: markdownSvg }); +export const notebookIcon = new JLIcon({ name: 'ui-components:notebook', svgstr: notebookSvg }); +export const pythonIcon = new JLIcon({ name: 'ui-components:python', svgstr: pythonSvg }); +export const rKernelIcon = new JLIcon({ name: 'ui-components:r-kernel', svgstr: rKernelSvg }); +export const reactIcon = new JLIcon({ name: 'ui-components:react', svgstr: reactSvg }); +export const settingsIcon = new JLIcon({ name: 'ui-components:settings', svgstr: settingsSvg }); +export const spreadsheetIcon = new JLIcon({ name: 'ui-components:spreadsheet', svgstr: spreadsheetSvg }); +export const textEditorIcon = new JLIcon({ name: 'ui-components:text-editor', svgstr: textEditorSvg }); +export const vegaIcon = new JLIcon({ name: 'ui-components:vega', svgstr: vegaSvg }); +export const yamlIcon = new JLIcon({ name: 'ui-components:yaml', svgstr: yamlSvg }); +export const jupyterFaviconIcon = new JLIcon({ name: 'ui-components:jupyter-favicon', svgstr: jupyterFaviconSvg }); +export const jupyterIcon = new JLIcon({ name: 'ui-components:jupyter', svgstr: jupyterSvg }); +export const jupyterlabWordmarkIcon = new JLIcon({ name: 'ui-components:jupyterlab-wordmark', svgstr: jupyterlabWordmarkSvg }); +export const caseSensitiveIcon = new JLIcon({ name: 'ui-components:case-sensitive', svgstr: caseSensitiveSvg }); +export const regexIcon = new JLIcon({ name: 'ui-components:regex', svgstr: regexSvg }); +export const buildIcon = new JLIcon({ name: 'ui-components:build', svgstr: buildSvg }); +export const extensionIcon = new JLIcon({ name: 'ui-components:extension', svgstr: extensionSvg }); +export const paletteIcon = new JLIcon({ name: 'ui-components:palette', svgstr: paletteSvg }); +export const runningIcon = new JLIcon({ name: 'ui-components:running', svgstr: runningSvg }); +export const tabIcon = new JLIcon({ name: 'ui-components:tab', svgstr: tabSvg }); +export const kernelIcon = new JLIcon({ name: 'ui-components:kernel', svgstr: kernelSvg }); +export const lineFormIcon = new JLIcon({ name: 'ui-components:line-form', svgstr: lineFormSvg }); +export const listIcon = new JLIcon({ name: 'ui-components:list', svgstr: listSvg }); +export const notTrustedIcon = new JLIcon({ name: 'ui-components:not-trusted', svgstr: notTrustedSvg }); +export const terminalIcon = new JLIcon({ name: 'ui-components:terminal', svgstr: terminalSvg }); +export const trustedIcon = new JLIcon({ name: 'ui-components:trusted', svgstr: trustedSvg }); +export const addIcon = new JLIcon({ name: 'ui-components:add', svgstr: addSvg }); +export const bugIcon = new JLIcon({ name: 'ui-components:bug', svgstr: bugSvg }); +export const checkIcon = new JLIcon({ name: 'ui-components:check', svgstr: checkSvg }); +export const circleEmptyIcon = new JLIcon({ name: 'ui-components:circle-empty', svgstr: circleEmptySvg }); +export const circleIcon = new JLIcon({ name: 'ui-components:circle', svgstr: circleSvg }); +export const closeIcon = new JLIcon({ name: 'ui-components:close', svgstr: closeSvg }); +export const copyIcon = new JLIcon({ name: 'ui-components:copy', svgstr: copySvg }); +export const cutIcon = new JLIcon({ name: 'ui-components:cut', svgstr: cutSvg }); +export const downloadIcon = new JLIcon({ name: 'ui-components:download', svgstr: downloadSvg }); +export const editIcon = new JLIcon({ name: 'ui-components:edit', svgstr: editSvg }); +export const ellipsesIcon = new JLIcon({ name: 'ui-components:ellipses', svgstr: ellipsesSvg }); +export const fileUploadIcon = new JLIcon({ name: 'ui-components:file-upload', svgstr: fileUploadSvg }); +export const filterListIcon = new JLIcon({ name: 'ui-components:filter-list', svgstr: filterListSvg }); +export const linkIcon = new JLIcon({ name: 'ui-components:link', svgstr: linkSvg }); +export const newFolderIcon = new JLIcon({ name: 'ui-components:new-folder', svgstr: newFolderSvg }); +export const pasteIcon = new JLIcon({ name: 'ui-components:paste', svgstr: pasteSvg }); +export const refreshIcon = new JLIcon({ name: 'ui-components:refresh', svgstr: refreshSvg }); +export const runIcon = new JLIcon({ name: 'ui-components:run', svgstr: runSvg }); +export const saveIcon = new JLIcon({ name: 'ui-components:save', svgstr: saveSvg }); +export const searchIcon = new JLIcon({ name: 'ui-components:search', svgstr: searchSvg }); +export const stopIcon = new JLIcon({ name: 'ui-components:stop', svgstr: stopSvg }); +export const undoIcon = new JLIcon({ name: 'ui-components:undo', svgstr: undoSvg }); diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index 269514c01850..35344d99e8fa 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -13,8 +13,9 @@ import badSvg from '../../style/debug/bad.svg'; import blankSvg from '../../style/debug/blank.svg'; export class JLIcon implements JLIcon.IJLIcon { - private static _debug: boolean = false; - private static _instances = new Map(); + /*********** + * statics * + ***********/ /** * Get any existing JLIcon instance by name. @@ -133,6 +134,13 @@ export class JLIcon implements JLIcon.IJLIcon { JLIcon._debug = debug ?? !JLIcon._debug; } + private static _debug: boolean = false; + private static _instances = new Map(); + + /*********** + * members * + ***********/ + constructor({ name, svgstr }: JLIcon.IJLIcon) { this.name = name; this._className = Private.nameToClassName(name); @@ -420,8 +428,13 @@ export namespace JLIcon { } namespace Private { + /** + * @param name - icon name. May be namespaced as per `some-pkg:foo-bar` + * + * @returns given a name of `some-pkg:foo-bar`, returns `jp-FooBarIcon` + */ export function nameToClassName(name: string): string { - return 'jp-' + Text.camelCase(name, true) + 'Icon'; + return 'jp-' + Text.camelCase(name.split(':').pop()!, true) + 'Icon'; } export function setTitleSvg(svgNode: HTMLElement, title: string): void { diff --git a/packages/vega5-extension/src/index.ts b/packages/vega5-extension/src/index.ts index a147f2cd0a64..ae0f10014f75 100644 --- a/packages/vega5-extension/src/index.ts +++ b/packages/vega5-extension/src/index.ts @@ -174,19 +174,19 @@ const extension: IRenderMime.IExtension = { mimeTypes: [VEGA_MIME_TYPE], name: 'vega5', extensions: ['.vg', '.vg.json', '.vega'], - iconClass: 'vega' + iconClass: 'ui-components:vega' }, { mimeTypes: [VEGALITE4_MIME_TYPE], name: 'vega-lite4', extensions: ['.vl', '.vl.json', '.vegalite'], - iconClass: 'vega' + iconClass: 'ui-components:vega' }, { mimeTypes: [VEGALITE3_MIME_TYPE], name: 'vega-lite3', extensions: [], - iconClass: 'vega' + iconClass: 'ui-components:vega' } ] }; From ddfcada9e3df785861e5dc81b4975ee615ace8e6 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 17 Jan 2020 23:17:41 -0500 Subject: [PATCH 10/72] type cleanup --- packages/docregistry/src/registry.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/docregistry/src/registry.ts b/packages/docregistry/src/registry.ts index b7a3a114d6a1..3b1a4b462d4a 100644 --- a/packages/docregistry/src/registry.ts +++ b/packages/docregistry/src/registry.ts @@ -17,8 +17,6 @@ import { IDisposable, DisposableDelegate } from '@lumino/disposable'; import { ISignal, Signal } from '@lumino/signaling'; -import { VirtualElementPass } from '@lumino/virtualdom'; - import { DockLayout, Widget } from '@lumino/widgets'; import { ISessionContext, Toolbar } from '@jupyterlab/apputils'; @@ -40,6 +38,7 @@ import { fileIcon, folderIcon, imageIcon, + JLIcon, jsonIcon, markdownIcon, notebookIcon, @@ -1187,11 +1186,9 @@ export namespace DocumentRegistry { readonly iconLabel?: string; /** - * Set the icon renderer for the title. An icon renderer is any object - * with appropriate render and unrender methods. See JLIcon in - * @jupyterlab/ui-components for a reference implementation. + * A JLIcon instance used to set the icon for the file type. */ - readonly iconRenderer?: VirtualElementPass.IRenderer; + readonly iconRenderer?: JLIcon; /** * The content type of the new file. From 77ae24eab4a8170f3e6c14ff41203f918eea1f20 Mon Sep 17 00:00:00 2001 From: telamonian Date: Mon, 20 Jan 2020 20:13:13 -0500 Subject: [PATCH 11/72] fixed unittests to deal with icon namespacing --- packages/shortcuts-extension/schema/shortcuts.json | 2 +- tests/test-apputils/src/toolbar.spec.ts | 8 ++++---- tests/test-codeeditor/src/jsoneditor.spec.ts | 10 ++++------ tests/test-notebook/src/default-toolbar.spec.ts | 12 ++++++------ 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/shortcuts-extension/schema/shortcuts.json b/packages/shortcuts-extension/schema/shortcuts.json index f7cfe7ac6b26..bc69a90b4a01 100644 --- a/packages/shortcuts-extension/schema/shortcuts.json +++ b/packages/shortcuts-extension/schema/shortcuts.json @@ -1,5 +1,5 @@ { - "jupyter.lab.setting-icon-class": "jp-KeyboardIcon", + "jupyter.lab.setting-icon-class": "ui-components:keyboard", "jupyter.lab.setting-icon-label": "Keyboard Shortcuts", "jupyter.lab.transform": true, "title": "Keyboard Shortcuts", diff --git a/tests/test-apputils/src/toolbar.spec.ts b/tests/test-apputils/src/toolbar.spec.ts index 02fe50c89048..170de47b77b1 100644 --- a/tests/test-apputils/src/toolbar.spec.ts +++ b/tests/test-apputils/src/toolbar.spec.ts @@ -289,7 +289,7 @@ describe('@jupyterlab/apputils', () => { const button = Toolbar.createInterruptButton(sessionContext); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector("[data-icon='stop']")).to.exist; + expect(button.node.querySelector("[data-icon$='stop']")).to.exist; }); }); @@ -298,7 +298,7 @@ describe('@jupyterlab/apputils', () => { const button = Toolbar.createRestartButton(sessionContext); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector("[data-icon='refresh']")).to.exist; + expect(button.node.querySelector("[data-icon$='refresh']")).to.exist; }); }); @@ -326,7 +326,7 @@ describe('@jupyterlab/apputils', () => { let called = false; sessionContext.statusChanged.connect((_, status) => { if (status === 'busy') { - expect(item.node.querySelector("[data-icon='circle']")).to.exist; + expect(item.node.querySelector("[data-icon$='circle']")).to.exist; called = true; } }); @@ -362,7 +362,7 @@ describe('@jupyterlab/apputils', () => { await sessionContext.initialize(); const item = Toolbar.createKernelStatusItem(sessionContext); expect(item.node.title).to.equal('Kernel Connecting'); - expect(item.node.querySelector("[data-icon='circle-empty']")).to + expect(item.node.querySelector("[data-icon$='circle-empty']")).to .exist; await sessionContext.initialize(); await sessionContext.session?.kernel?.info; diff --git a/tests/test-codeeditor/src/jsoneditor.spec.ts b/tests/test-codeeditor/src/jsoneditor.spec.ts index 4fb7e7c35fa6..f5d59bd87383 100644 --- a/tests/test-codeeditor/src/jsoneditor.spec.ts +++ b/tests/test-codeeditor/src/jsoneditor.spec.ts @@ -87,17 +87,15 @@ describe('codeeditor', () => { describe('#revertButtonNode', () => { it('should be the revert button node used by the editor', () => { - expect(editor.revertButtonNode.firstElementChild!.localName).to.equal( - 'svg' - ); + expect(editor.revertButtonNode.querySelector("[data-icon$='undo']")).to + .exist; }); }); describe('#commitButtonNode', () => { it('should be the commit button node used by the editor', () => { - expect(editor.commitButtonNode.firstElementChild!.localName).to.equal( - 'svg' - ); + expect(editor.commitButtonNode.querySelector("[data-icon$='check']")).to + .exist; }); }); diff --git a/tests/test-notebook/src/default-toolbar.spec.ts b/tests/test-notebook/src/default-toolbar.spec.ts index 7bc793dc6d03..dd819e6fddb8 100644 --- a/tests/test-notebook/src/default-toolbar.spec.ts +++ b/tests/test-notebook/src/default-toolbar.spec.ts @@ -62,7 +62,7 @@ describe('@jupyterlab/notebook', () => { const button = ToolbarItems.createSaveButton(panel); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector("[data-icon='save']")).to.exist; + expect(button.node.querySelector("[data-icon$='save']")).to.exist; }); }); @@ -81,7 +81,7 @@ describe('@jupyterlab/notebook', () => { const button = ToolbarItems.createInsertButton(panel); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector("[data-icon='add']")).to.exist; + expect(button.node.querySelector("[data-icon$='add']")).to.exist; button.dispose(); }); }); @@ -104,7 +104,7 @@ describe('@jupyterlab/notebook', () => { const button = ToolbarItems.createCutButton(panel); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector("[data-icon='cut']")).to.exist; + expect(button.node.querySelector("[data-icon$='cut']")).to.exist; button.dispose(); }); }); @@ -127,7 +127,7 @@ describe('@jupyterlab/notebook', () => { const button = ToolbarItems.createCopyButton(panel); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector("[data-icon='copy']")).to.exist; + expect(button.node.querySelector("[data-icon$='copy']")).to.exist; button.dispose(); }); }); @@ -149,7 +149,7 @@ describe('@jupyterlab/notebook', () => { const button = ToolbarItems.createPasteButton(panel); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector("[data-icon='paste']")).to.exist; + expect(button.node.querySelector("[data-icon$='paste']")).to.exist; button.dispose(); }); }); @@ -276,7 +276,7 @@ describe('@jupyterlab/notebook', () => { const button = ToolbarItems.createRunButton(panel); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector("[data-icon='run']")).to.exist; + expect(button.node.querySelector("[data-icon$='run']")).to.exist; }); }); }); From 49b3ffb1221ed5fff26d72d9a18bce532cf2007c Mon Sep 17 00:00:00 2001 From: telamonian Date: Tue, 21 Jan 2020 00:25:48 -0500 Subject: [PATCH 12/72] refined handling of icon container className --- packages/filebrowser/src/listing.ts | 1 + packages/ui-components/src/icon/jlicon.tsx | 19 ++++++------------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/packages/filebrowser/src/listing.ts b/packages/filebrowser/src/listing.ts index 767a330041cc..795cd77fd2ab 100644 --- a/packages/filebrowser/src/listing.ts +++ b/packages/filebrowser/src/listing.ts @@ -2122,6 +2122,7 @@ namespace Private { }); } else { JLIcon.remove(container); + container.className = HEADER_ITEM_ICON_CLASS; } } } diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index 35344d99e8fa..56299352be6c 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -115,12 +115,8 @@ export class JLIcon implements JLIcon.IJLIcon { container.firstChild.remove(); } - // remove the icon class recorded in the dataset - if (container.dataset?.iconClass) { - container.classList.remove(container.dataset.iconClass); - } - // remove icon class from the dataset (even if empty) - delete container.dataset?.iconClass; + // remove all classes + container.className = ''; return container; } @@ -300,16 +296,14 @@ export class JLIcon implements JLIcon.IJLIcon { // override the container class with explicitly passed-in class + style class const classResolved = classes(className, classStyle); container.className = classResolved; - container.dataset.iconClass = classResolved; + return classResolved; } else if (classStyle) { // add the style class to the container class container.classList.add(classStyle); - container.dataset.iconClass = classStyle; + return classStyle; } else { - container.dataset.iconClass = ''; + return ''; } - - return container.dataset.iconClass; } protected _initReact() { @@ -352,9 +346,8 @@ export class JLIcon implements JLIcon.IJLIcon { ); } else { - const classResolved = classes(className, iconStyle(propsStyle)); return ( - + {svgComponent} {label} From 30d390acad3361a6cc6cd3fe540163c50755cca3 Mon Sep 17 00:00:00 2001 From: telamonian Date: Thu, 23 Jan 2020 17:30:12 -0500 Subject: [PATCH 13/72] added `icon: string | {name: string, svgstr: string}` field --- packages/rendermime-interfaces/src/index.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/rendermime-interfaces/src/index.ts b/packages/rendermime-interfaces/src/index.ts index f6417497924f..8c3f2012bf8e 100644 --- a/packages/rendermime-interfaces/src/index.ts +++ b/packages/rendermime-interfaces/src/index.ts @@ -146,6 +146,13 @@ export namespace IRenderMime { */ readonly pattern?: string; + /** + * The icon for the file type. Can either be a string containing the name + * of an existing icon, or an object with {name, svgstr} fields, where + * svgstr is a string containing the raw contents of an svg file. + */ + readonly icon?: string | { name: string; svgstr: string }; + /** * The icon class name for the file type. */ From 346a52db03818112ff03ef187d0bbcf0f9c8bb17 Mon Sep 17 00:00:00 2001 From: telamonian Date: Thu, 23 Jan 2020 20:39:12 -0500 Subject: [PATCH 14/72] added JLIcon.resolve static method also fixed/added a bunch of doc string to JLIcon --- packages/ui-components/src/icon/jlicon.tsx | 150 ++++++++++++++++----- 1 file changed, 114 insertions(+), 36 deletions(-) diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index 56299352be6c..f8a3ccbb07d1 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -121,6 +121,58 @@ export class JLIcon implements JLIcon.IJLIcon { return container; } + /** + * Resolve an icon name or a {name, svgstr} pair into an + * actual JLIcon. + * + * @param icon - either a string with the name of an existing icon + * or an object with {name: string, svgstr: string} fields. + * + * @returns a JLIcon instance, or null if an icon name was passed in + * and lookup fails. + */ + static resolve(icon: JLIcon.IJLIconSpec): JLIcon | null { + if (icon instanceof JLIcon) { + // icon already is a JLIcon + return icon; + } + + if (typeof icon === 'string') { + // do a dynamic lookup of existing icon by name + return JLIcon._get(icon) ?? null; + } + + // icon is a non-JLIcon {name, svgstr} pair + return JLIcon._get(icon.name) ?? new JLIcon(icon) ?? null; + } + + /** + * Resolve a {name, svgstr} pair into an actual svg node. + */ + static resolveSvg({ name, svgstr }: JLIcon.IJLIcon): HTMLElement | null { + const svgDoc = new DOMParser().parseFromString(svgstr, 'image/svg+xml'); + + const svgError = svgDoc.querySelector('parsererror'); + + // structure of error element varies by browser, search at top level + if (svgError) { + // parse failed, svgElement will be an error box + const errmsg = `SVG HTML was malformed for JLIcon instance.\nname: ${name}, svgstr: ${svgstr}`; + if (JLIcon._debug) { + // fail noisily, render the error box + console.error(errmsg); + return svgError as HTMLElement; + } else { + // bad svg is always a real error, fail silently but warn + console.warn(errmsg); + return null; + } + } else { + // parse succeeded + return svgDoc.documentElement; + } + } + /** * Toggle icon debug from off-to-on, or vice-versa. * @@ -189,7 +241,7 @@ export class JLIcon implements JLIcon.IJLIcon { } // ensure that svg html is valid - const svgElement = this.resolveSvg(); + const svgElement = this._initSvg(); if (!svgElement) { // bail if failing silently, return blank element return document.createElement('div'); @@ -227,39 +279,6 @@ export class JLIcon implements JLIcon.IJLIcon { this.element({ container, ...props }); } - resolveSvg(title?: string): HTMLElement | null { - const svgDoc = new DOMParser().parseFromString( - this._svgstr, - 'image/svg+xml' - ); - const svgElement = svgDoc.documentElement; - - // structure of error element varies by browser, search at top level - if (svgDoc.getElementsByTagName('parsererror').length > 0) { - const errmsg = `SVG HTML was malformed for JLIcon instance.\nname: ${name}, svgstr: ${this._svgstr}`; - // parse failed, svgElement will be an error box - if (JLIcon._debug) { - // fail noisily, render the error box - console.error(errmsg); - return svgElement; - } else { - // bad svg is always a real error, fail silently but warn - console.warn(errmsg); - return null; - } - } else { - // parse succeeded - svgElement.dataset.icon = this.name; - svgElement.dataset.iconId = this._uuid; - - if (title) { - Private.setTitleSvg(svgElement, title); - } - - return svgElement; - } - } - get svgstr() { return this._svgstr; } @@ -322,7 +341,7 @@ export class JLIcon implements JLIcon.IJLIcon { const Tag = tag; // ensure that svg html is valid - const svgElement = this.resolveSvg(); + const svgElement = this._initSvg(); if (!svgElement) { // bail if failing silently return <>; @@ -360,7 +379,51 @@ export class JLIcon implements JLIcon.IJLIcon { return component; } + protected _initSvg(title?: string): HTMLElement | null { + const svgElement = JLIcon.resolveSvg(this); + + if (!svgElement) { + // bail on null svg element + return svgElement; + } + + if (svgElement.tagName !== 'parsererror') { + // svgElement is an actual svg node, augment it + svgElement.dataset.icon = this.name; + svgElement.dataset.iconId = this._uuid; + + if (title) { + Private.setTitleSvg(svgElement, title); + } + } + + return svgElement; + } + readonly name: string; + + /** + * A React component that will create the icon. + * + * @param className - a string that will be used as the class + * of the container element. Overrides any existing class + * + * @param container - a preexisting DOM element that + * will be used as the container for the svg element + * + * @param label - text that will be displayed adjacent + * to the icon + * + * @param title - a tooltip for the icon + * + * @param tag - if container is not explicitly + * provided, this tag will be used when creating the container + * + * @propsStyle - style parameters that get passed to TypeStyle in + * order to generate a style class. The style class will be added + * to the icon container's classes, while the style itself will be + * applied to any svg elements within the container. + */ readonly react: JLIcon.IReact; protected _className: string; @@ -377,10 +440,25 @@ export namespace JLIcon { * implementation of JLIcon may vary */ export interface IJLIcon { - name: string; + /** + * The name of the icon. By convention, the icon name will be namespaced + * as so: + * + * "pkg-name:icon-name" + */ + readonly name: string; + + /** + * A string containing the raw contents of an svg file. + */ svgstr: string; } + /** + * A type that can be resolved to a JLIcon instance. + */ + export type IJLIconSpec = string | IJLIcon; + /** * The input props for creating a new JLIcon */ From 8edce2fb45fde71ce4a7406271b4d04a81772417 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 24 Jan 2020 04:47:09 -0500 Subject: [PATCH 15/72] added complete implementation of dynamic icon svg replacement --- packages/ui-components/package.json | 1 + packages/ui-components/src/icon/jlicon.tsx | 51 +++++++++++++++++++--- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 69e447868c94..9b17530b6df6 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -38,6 +38,7 @@ "@blueprintjs/select": "^3.11.2", "@jupyterlab/coreutils": "^4.0.0-beta.2", "@lumino/coreutils": "^1.4.2", + "@lumino/signaling": "^1.3.4", "@lumino/virtualdom": "^1.4.0", "@lumino/widgets": "^1.10.0", "react": "~16.9.0", diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index f8a3ccbb07d1..5ee62b206144 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -2,6 +2,7 @@ // Distributed under the terms of the Modified BSD License. import { UUID } from '@lumino/coreutils'; +import { Signal } from '@lumino/signaling'; import React from 'react'; import { Text } from '@jupyterlab/coreutils'; @@ -241,7 +242,7 @@ export class JLIcon implements JLIcon.IJLIcon { } // ensure that svg html is valid - const svgElement = this._initSvg(); + const svgElement = this._initSvg({ uuid: this._uuid }); if (!svgElement) { // bail if failing silently, return blank element return document.createElement('div'); @@ -286,8 +287,23 @@ export class JLIcon implements JLIcon.IJLIcon { set svgstr(svgstr: string) { this._svgstr = svgstr; - // associate a unique id with this particular svgstr - this._uuid = UUID.uuid4(); + // associate a new unique id with this particular svgstr + const uuid = UUID.uuid4(); + const uuidOld = this._uuid; + this._uuid = uuid; + + // update icon elements created using .element method + document + .querySelectorAll(`[data-icon-id=${uuidOld}]`) + .forEach(oldSvgElement => { + const svgElement = this._initSvg({ uuid }); + if (svgElement) { + oldSvgElement.replaceWith(svgElement); + } + }); + + // trigger update of icon elements created using other methods + this._svgReplaced.emit(); } unrender(container: HTMLElement): void { @@ -338,6 +354,24 @@ export class JLIcon implements JLIcon.IJLIcon { }: JLIcon.IProps = {}, ref: React.RefObject ) => { + // set up component state via useState hook + const [, setId] = React.useState(this._uuid); + + // subscribe to svg replacement via useEffect hook + React.useEffect(() => { + const onSvgReplaced = () => { + setId(this._uuid); + }; + + this._svgReplaced.connect(onSvgReplaced); + + // specify cleanup callback as hook return + return () => { + this._svgReplaced.disconnect(onSvgReplaced); + }; + }); + + // make it so that tag can be used as a jsx component const Tag = tag; // ensure that svg html is valid @@ -379,7 +413,10 @@ export class JLIcon implements JLIcon.IJLIcon { return component; } - protected _initSvg(title?: string): HTMLElement | null { + protected _initSvg({ + title, + uuid + }: { title?: string; uuid?: string } = {}): HTMLElement | null { const svgElement = JLIcon.resolveSvg(this); if (!svgElement) { @@ -390,7 +427,10 @@ export class JLIcon implements JLIcon.IJLIcon { if (svgElement.tagName !== 'parsererror') { // svgElement is an actual svg node, augment it svgElement.dataset.icon = this.name; - svgElement.dataset.iconId = this._uuid; + + if (uuid) { + svgElement.dataset.iconId = uuid; + } if (title) { Private.setTitleSvg(svgElement, title); @@ -427,6 +467,7 @@ export class JLIcon implements JLIcon.IJLIcon { readonly react: JLIcon.IReact; protected _className: string; + protected _svgReplaced = new Signal(this); protected _svgstr: string; protected _uuid: string; } From 11fa6361671a3b4ce3ea6ca83ee6aad28712630f Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 24 Jan 2020 08:30:44 -0500 Subject: [PATCH 16/72] split out JLIcon.IRenderer from JLIcon --- packages/ui-components/package.json | 1 + packages/ui-components/src/icon/jlicon.tsx | 82 ++++++++++++++++++---- 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 9b17530b6df6..c72f1366cc23 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -42,6 +42,7 @@ "@lumino/virtualdom": "^1.4.0", "@lumino/widgets": "^1.10.0", "react": "~16.9.0", + "react-dom": "~16.9.0", "typestyle": "^2.0.4" }, "devDependencies": { diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index 5ee62b206144..23434e4b11c6 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -4,6 +4,7 @@ import { UUID } from '@lumino/coreutils'; import { Signal } from '@lumino/signaling'; import React from 'react'; +import ReactDOM from 'react-dom'; import { Text } from '@jupyterlab/coreutils'; @@ -13,7 +14,7 @@ import { getReactAttrs, classes, classesDedupe } from '../utils'; import badSvg from '../../style/debug/bad.svg'; import blankSvg from '../../style/debug/blank.svg'; -export class JLIcon implements JLIcon.IJLIcon { +export class JLIcon implements JLIcon.IJLIconRenderable { /*********** * statics * ***********/ @@ -132,7 +133,7 @@ export class JLIcon implements JLIcon.IJLIcon { * @returns a JLIcon instance, or null if an icon name was passed in * and lookup fails. */ - static resolve(icon: JLIcon.IJLIconSpec): JLIcon | null { + static resolve(icon: JLIcon.IResolvable): JLIcon | null { if (icon instanceof JLIcon) { // icon already is a JLIcon return icon; @@ -190,13 +191,22 @@ export class JLIcon implements JLIcon.IJLIcon { * members * ***********/ - constructor({ name, svgstr }: JLIcon.IJLIcon) { + constructor({ + name, + svgstr, + renderer + }: JLIcon.IJLIcon & { renderer?: JLIcon.IRenderer }) { this.name = name; this._className = Private.nameToClassName(name); this.svgstr = svgstr; this.react = this._initReact(); + if (!renderer) { + this.renderer = new JLIcon.ElementRenderer(); + this.renderer.icon = this; + } + JLIcon._instances.set(this.name, this); JLIcon._instances.set(this._className, this); } @@ -273,13 +283,6 @@ export class JLIcon implements JLIcon.IJLIcon { return ret; } - render(container: HTMLElement, props: JLIcon.IProps = {}): void { - // TODO: move this title fix to the Lumino side - container.removeAttribute('title'); - - this.element({ container, ...props }); - } - get svgstr() { return this._svgstr; } @@ -306,10 +309,6 @@ export class JLIcon implements JLIcon.IJLIcon { this._svgReplaced.emit(); } - unrender(container: HTMLElement): void { - return; - } - protected _initContainer({ container, @@ -466,6 +465,8 @@ export class JLIcon implements JLIcon.IJLIcon { */ readonly react: JLIcon.IReact; + readonly renderer: JLIcon.IRenderer; + protected _className: string; protected _svgReplaced = new Signal(this); protected _svgstr: string; @@ -476,6 +477,13 @@ export class JLIcon implements JLIcon.IJLIcon { * A namespace for JLIcon statics. */ export namespace JLIcon { + export interface IRenderer { + render: (container: HTMLElement, props?: IProps) => void; + unrender: (container: HTMLElement) => void; + + icon: IJLIcon; + } + /** * The IJLIcon interface. Outside of this interface the actual * implementation of JLIcon may vary @@ -495,10 +503,54 @@ export namespace JLIcon { svgstr: string; } + export interface IJLIconRenderable extends IJLIcon { + readonly renderer: IRenderer; + } + + // export type IRendererFactory = (icon: JLIcon) => IRenderer; + + // export abstract class Renderer implements IRenderer { + // constructor(protected icon: IJLIcon) {}; + // + // abstract render(container: HTMLElement): void; + // abstract unrender(container: HTMLElement): void; + // } + + export class ElementRenderer implements IRenderer { + render(container: HTMLElement, props: IProps = {}): void { + // TODO: move this title fix to the Lumino side + container.removeAttribute('title'); + + this.icon.element({ container, ...props }); + } + + unrender(container: HTMLElement): void {} + + public icon: JLIcon = blankIcon; + } + + export class ReactRenderer implements IRenderer { + render(container: HTMLElement, props: JLIcon.IProps = {}): void { + // TODO: move this title fix to the Lumino side + container.removeAttribute('title'); + + return ReactDOM.render( + , + container + ); + } + + unrender(container: HTMLElement): void { + ReactDOM.unmountComponentAtNode(container); + } + + public icon: JLIcon = blankIcon; + } + /** * A type that can be resolved to a JLIcon instance. */ - export type IJLIconSpec = string | IJLIcon; + export type IResolvable = string | IJLIcon; /** * The input props for creating a new JLIcon From a5ccf4ff482b075008607df5562e233c1d647e19 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 24 Jan 2020 10:01:14 -0500 Subject: [PATCH 17/72] fixed up FileType handling to match new icon stuff --- packages/docregistry/src/registry.ts | 104 +++++++++++++-------- packages/ui-components/src/icon/jlicon.tsx | 11 +-- 2 files changed, 66 insertions(+), 49 deletions(-) diff --git a/packages/docregistry/src/registry.ts b/packages/docregistry/src/registry.ts index 3b1a4b462d4a..73df11c80c67 100644 --- a/packages/docregistry/src/registry.ts +++ b/packages/docregistry/src/registry.ts @@ -66,10 +66,7 @@ export class DocumentRegistry implements IDisposable { let fts = options.initialFileTypes || DocumentRegistry.defaultFileTypes; fts.forEach(ft => { - let value: DocumentRegistry.IFileType = { - ...DocumentRegistry.fileTypeDefaults, - ...ft - }; + let value = new DocumentRegistry.FileType(ft); this._fileTypes.push(value); }); } @@ -290,7 +287,7 @@ export class DocumentRegistry implements IDisposable { ...DocumentRegistry.fileTypeDefaults, ...fileType }; - this._fileTypes.push(value); + this._fileTypes.push(new DocumentRegistry.FileType(value)); this._changed.emit({ type: 'fileType', @@ -557,7 +554,7 @@ export class DocumentRegistry implements IDisposable { /** * Get a file type by name. */ - getFileType(name: string): DocumentRegistry.IFileType | undefined { + getFileType(name: string): DocumentRegistry.FileType | undefined { name = name.toLowerCase(); return find(this._fileTypes, fileType => { return fileType.name.toLowerCase() === name; @@ -611,7 +608,7 @@ export class DocumentRegistry implements IDisposable { */ getFileTypeForModel( model: Partial - ): DocumentRegistry.IFileType { + ): DocumentRegistry.FileType { switch (model.type) { case 'directory': return ( @@ -643,8 +640,8 @@ export class DocumentRegistry implements IDisposable { * * @returns An ordered list of matching file types. */ - getFileTypesForPath(path: string): DocumentRegistry.IFileType[] { - let fts: DocumentRegistry.IFileType[] = []; + getFileTypesForPath(path: string): DocumentRegistry.FileType[] { + let fts: DocumentRegistry.FileType[] = []; let name = PathExt.basename(path); // Look for a pattern match first. @@ -691,7 +688,7 @@ export class DocumentRegistry implements IDisposable { private _widgetFactoriesForFileType: { [key: string]: string[]; } = Object.create(null); - private _fileTypes: DocumentRegistry.IFileType[] = []; + private _fileTypes: DocumentRegistry.FileType[] = []; private _extenders: { [key: string]: DocumentRegistry.WidgetExtension[]; } = Object.create(null); @@ -1175,6 +1172,13 @@ export namespace DocumentRegistry { */ readonly pattern?: string; + /** + * The icon for the file type. Can either be a string containing the name + * of an existing icon, or an object with {name, svgstr} fields, where + * svgstr is a string containing the raw contents of an svg file. + */ + readonly icon?: string | { name: string; svgstr: string } | null; + /** * The icon class name for the file type. */ @@ -1185,11 +1189,6 @@ export namespace DocumentRegistry { */ readonly iconLabel?: string; - /** - * A JLIcon instance used to set the icon for the file type. - */ - readonly iconRenderer?: JLIcon; - /** * The content type of the new file. */ @@ -1208,12 +1207,39 @@ export namespace DocumentRegistry { name: 'default', extensions: [], mimeTypes: [], - iconClass: '', - iconLabel: '', contentType: 'file', fileFormat: 'text' }; + /** + * A wrapper for IFileTypes + */ + export class FileType implements IFileType { + constructor(options: Partial) { + Object.assign(this, fileTypeDefaults, options); + + if (this.icon) { + // ensure that the icon is resolved to a JLIcon + this.icon = JLIcon.resolve(this.icon); + } + } + + get iconRenderer(): JLIcon.IRenderer | null { + return this.icon?.renderer ?? null; + } + + readonly name: string; + readonly mimeTypes: ReadonlyArray; + readonly extensions: ReadonlyArray; + readonly displayName: string; + readonly pattern: string; + readonly icon: JLIcon | null; + readonly iconClass: string; + readonly iconLabel: string; + readonly contentType: Contents.ContentType; + readonly fileFormat: Contents.FileFormat; + } + /** * An arguments object for the `changed` signal. */ @@ -1241,18 +1267,18 @@ export namespace DocumentRegistry { /** * The default text file type used by the document registry. */ - export const defaultTextFileType: IFileType = { + export const defaultTextFileType = new FileType({ ...fileTypeDefaults, name: 'text', mimeTypes: ['text/plain'], extensions: ['.txt'], - iconRenderer: fileIcon - }; + icon: fileIcon + }); /** * The default notebook file type used by the document registry. */ - export const defaultNotebookFileType: IFileType = { + export const defaultNotebookFileType = new FileType({ ...fileTypeDefaults, name: 'notebook', displayName: 'Notebook', @@ -1260,20 +1286,20 @@ export namespace DocumentRegistry { extensions: ['.ipynb'], contentType: 'notebook', fileFormat: 'json', - iconRenderer: notebookIcon - }; + icon: notebookIcon + }); /** * The default directory file type used by the document registry. */ - export const defaultDirectoryFileType: IFileType = { + export const defaultDirectoryFileType = new FileType({ ...fileTypeDefaults, name: 'directory', extensions: [], mimeTypes: ['text/directory'], contentType: 'directory', - iconRenderer: folderIcon - }; + icon: folderIcon + }); /** * The default file types used by the document registry. @@ -1287,56 +1313,56 @@ export namespace DocumentRegistry { displayName: 'Markdown File', extensions: ['.md'], mimeTypes: ['text/markdown'], - iconRenderer: markdownIcon + icon: markdownIcon }, { name: 'python', displayName: 'Python File', extensions: ['.py'], mimeTypes: ['text/x-python'], - iconRenderer: pythonIcon + icon: pythonIcon }, { name: 'json', displayName: 'JSON File', extensions: ['.json'], mimeTypes: ['application/json'], - iconRenderer: jsonIcon + icon: jsonIcon }, { name: 'csv', displayName: 'CSV File', extensions: ['.csv'], mimeTypes: ['text/csv'], - iconRenderer: spreadsheetIcon + icon: spreadsheetIcon }, { name: 'tsv', displayName: 'TSV File', extensions: ['.tsv'], mimeTypes: ['text/csv'], - iconRenderer: spreadsheetIcon + icon: spreadsheetIcon }, { name: 'r', displayName: 'R File', mimeTypes: ['text/x-rsrc'], extensions: ['.r'], - iconRenderer: rKernelIcon + icon: rKernelIcon }, { name: 'yaml', displayName: 'YAML File', mimeTypes: ['text/x-yaml', 'text/yaml'], extensions: ['.yaml', '.yml'], - iconRenderer: yamlIcon + icon: yamlIcon }, { name: 'svg', displayName: 'Image', mimeTypes: ['image/svg+xml'], extensions: ['.svg'], - iconRenderer: imageIcon, + icon: imageIcon, fileFormat: 'base64' }, { @@ -1344,7 +1370,7 @@ export namespace DocumentRegistry { displayName: 'Image', mimeTypes: ['image/tiff'], extensions: ['.tif', '.tiff'], - iconRenderer: imageIcon, + icon: imageIcon, fileFormat: 'base64' }, { @@ -1352,7 +1378,7 @@ export namespace DocumentRegistry { displayName: 'Image', mimeTypes: ['image/jpeg'], extensions: ['.jpg', '.jpeg'], - iconRenderer: imageIcon, + icon: imageIcon, fileFormat: 'base64' }, { @@ -1360,7 +1386,7 @@ export namespace DocumentRegistry { displayName: 'Image', mimeTypes: ['image/gif'], extensions: ['.gif'], - iconRenderer: imageIcon, + icon: imageIcon, fileFormat: 'base64' }, { @@ -1368,7 +1394,7 @@ export namespace DocumentRegistry { displayName: 'Image', mimeTypes: ['image/png'], extensions: ['.png'], - iconRenderer: imageIcon, + icon: imageIcon, fileFormat: 'base64' }, { @@ -1376,7 +1402,7 @@ export namespace DocumentRegistry { displayName: 'Image', mimeTypes: ['image/bmp'], extensions: ['.bmp'], - iconRenderer: imageIcon, + icon: imageIcon, fileFormat: 'base64' } ]; diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index 23434e4b11c6..7fc461e20895 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -145,7 +145,7 @@ export class JLIcon implements JLIcon.IJLIconRenderable { } // icon is a non-JLIcon {name, svgstr} pair - return JLIcon._get(icon.name) ?? new JLIcon(icon) ?? null; + return JLIcon._get(icon.name) ?? new JLIcon(icon); } /** @@ -507,15 +507,6 @@ export namespace JLIcon { readonly renderer: IRenderer; } - // export type IRendererFactory = (icon: JLIcon) => IRenderer; - - // export abstract class Renderer implements IRenderer { - // constructor(protected icon: IJLIcon) {}; - // - // abstract render(container: HTMLElement): void; - // abstract unrender(container: HTMLElement): void; - // } - export class ElementRenderer implements IRenderer { render(container: HTMLElement, props: IProps = {}): void { // TODO: move this title fix to the Lumino side From 6756532345229a63aa41d820a738940e5cdb2440 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 24 Jan 2020 10:22:44 -0500 Subject: [PATCH 18/72] removed icon backwards compatibility shim from filebrowser listing --- packages/filebrowser/src/listing.ts | 35 +++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/packages/filebrowser/src/listing.ts b/packages/filebrowser/src/listing.ts index 795cd77fd2ab..835ee8e0dcf8 100644 --- a/packages/filebrowser/src/listing.ts +++ b/packages/filebrowser/src/listing.ts @@ -1659,7 +1659,7 @@ export namespace DirListing { updateItemNode( node: HTMLElement, model: Contents.IModel, - fileType?: DocumentRegistry.IFileType + fileType?: DocumentRegistry.FileType ): void; /** @@ -1685,7 +1685,7 @@ export namespace DirListing { createDragImage( node: HTMLElement, count: number, - fileType?: DocumentRegistry.IFileType + fileType?: DocumentRegistry.FileType ): HTMLElement; } @@ -1832,7 +1832,7 @@ export namespace DirListing { updateItemNode( node: HTMLElement, model: Contents.IModel, - fileType?: DocumentRegistry.IFileType + fileType?: DocumentRegistry.FileType ): void { const iconContainer = DOMUtils.findElement(node, ITEM_ICON_CLASS); const text = DOMUtils.findElement(node, ITEM_TEXT_CLASS); @@ -1848,14 +1848,31 @@ export namespace DirListing { // render the icon svg node if (fileType?.iconRenderer) { + iconContainer.className = classes( + ITEM_ICON_CLASS, + iconStyle({ + justify: 'center', + kind: 'listing' + }) + ); + fileType.iconRenderer.render(iconContainer); } else if (fileType?.iconClass) { - JLIcon.getElement({ - name: fileType.iconClass, - container: iconContainer - }); + iconContainer.className = classes(ITEM_ICON_CLASS, fileType.iconClass); + + if (fileType.iconLabel) { + iconContainer.textContent = fileType.iconLabel; + } } else { - fileIcon.render(iconContainer); + iconContainer.className = classes( + ITEM_ICON_CLASS, + iconStyle({ + justify: 'center', + kind: 'listing' + }) + ); + + fileIcon.renderer.render(iconContainer); } let hoverText = 'Name: ' + model.name; @@ -1925,7 +1942,7 @@ export namespace DirListing { createDragImage( node: HTMLElement, count: number, - fileType?: DocumentRegistry.IFileType + fileType?: DocumentRegistry.FileType ): HTMLElement { let dragImage = node.cloneNode(true) as HTMLElement; let modified = DOMUtils.findElement(dragImage, ITEM_MODIFIED_CLASS); From ebe90e89a98e4284bad18dcd8b8764792b3e7dce Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 24 Jan 2020 10:38:57 -0500 Subject: [PATCH 19/72] some type fixes --- packages/filebrowser/src/listing.ts | 8 -------- packages/ui-components/src/icon/tabbarsvg.ts | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/filebrowser/src/listing.ts b/packages/filebrowser/src/listing.ts index 835ee8e0dcf8..db22b5a5de27 100644 --- a/packages/filebrowser/src/listing.ts +++ b/packages/filebrowser/src/listing.ts @@ -1838,14 +1838,6 @@ export namespace DirListing { const text = DOMUtils.findElement(node, ITEM_TEXT_CLASS); const modified = DOMUtils.findElement(node, ITEM_MODIFIED_CLASS); - iconContainer.className = classes( - ITEM_ICON_CLASS, - iconStyle({ - justify: 'center', - kind: 'listing' - }) - ); - // render the icon svg node if (fileType?.iconRenderer) { iconContainer.className = classes( diff --git a/packages/ui-components/src/icon/tabbarsvg.ts b/packages/ui-components/src/icon/tabbarsvg.ts index 1ca9abe629f7..0c360f17e218 100644 --- a/packages/ui-components/src/icon/tabbarsvg.ts +++ b/packages/ui-components/src/icon/tabbarsvg.ts @@ -46,7 +46,7 @@ export namespace TabBarSvg { return (hpass( 'div', { className }, - closeIcon + closeIcon.renderer ) as unknown) as VirtualElement; } } From 884726d8a940000ce44a7c546db66f849ddfcb37 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 24 Jan 2020 12:39:46 -0500 Subject: [PATCH 20/72] small cleanup of vega icon svg --- packages/ui-components/style/icons/filetype/vega.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui-components/style/icons/filetype/vega.svg b/packages/ui-components/style/icons/filetype/vega.svg index fc30c3b5315a..a1dd1914ecbf 100644 --- a/packages/ui-components/style/icons/filetype/vega.svg +++ b/packages/ui-components/style/icons/filetype/vega.svg @@ -1,6 +1,6 @@ From 83111f6490bec98773cad1f1569120d1a8ff2575 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 24 Jan 2020 18:24:29 -0500 Subject: [PATCH 21/72] fixed up typings related to JLIcon.IRenderer; added lots of docstrings --- packages/ui-components/src/icon/jlicon.tsx | 105 ++++++++++++++------- 1 file changed, 73 insertions(+), 32 deletions(-) diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index 7fc461e20895..0ec8593e5493 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -14,7 +14,7 @@ import { getReactAttrs, classes, classesDedupe } from '../utils'; import badSvg from '../../style/debug/bad.svg'; import blankSvg from '../../style/debug/blank.svg'; -export class JLIcon implements JLIcon.IJLIconRenderable { +export class JLIcon implements JLIcon.IJLIcon, JLIcon.IRenderer { /*********** * statics * ***********/ @@ -194,19 +194,25 @@ export class JLIcon implements JLIcon.IJLIconRenderable { constructor({ name, svgstr, - renderer - }: JLIcon.IJLIcon & { renderer?: JLIcon.IRenderer }) { + render, + unrender, + rendererClass = JLIcon.ElementRenderer + }: JLIcon.IOptions) { this.name = name; this._className = Private.nameToClassName(name); this.svgstr = svgstr; this.react = this._initReact(); - if (!renderer) { - this.renderer = new JLIcon.ElementRenderer(); - this.renderer.icon = this; + if (render && unrender) { + this.render = render; + this.unrender = unrender; + } else { + // set render and unrender methods based on the supplied rendererClass + const renderer = new rendererClass(this); + this.render = renderer.render.bind(this); + this.unrender = renderer.unrender.bind(this); } - JLIcon._instances.set(this.name, this); JLIcon._instances.set(this._className, this); } @@ -228,7 +234,7 @@ export class JLIcon implements JLIcon.IJLIconRenderable { * @param tag - if container is not explicitly * provided, this tag will be used when creating the container * - * @propsStyle - style parameters that get passed to TypeStyle in + * @param propsStyle - style parameters that get passed to TypeStyle in * order to generate a style class. The style class will be added * to the icon container's classes, while the style itself will be * applied to any svg elements within the container. @@ -458,16 +464,18 @@ export class JLIcon implements JLIcon.IJLIconRenderable { * @param tag - if container is not explicitly * provided, this tag will be used when creating the container * - * @propsStyle - style parameters that get passed to TypeStyle in + * @param propsStyle - style parameters that get passed to TypeStyle in * order to generate a style class. The style class will be added * to the icon container's classes, while the style itself will be * applied to any svg elements within the container. */ readonly react: JLIcon.IReact; - readonly renderer: JLIcon.IRenderer; + readonly render: (container: HTMLElement, props?: JLIcon.IProps) => void; + readonly unrender: (container: HTMLElement) => void; protected _className: string; + protected _icon = this; protected _svgReplaced = new Signal(this); protected _svgstr: string; protected _uuid: string; @@ -477,18 +485,10 @@ export class JLIcon implements JLIcon.IJLIconRenderable { * A namespace for JLIcon statics. */ export namespace JLIcon { - export interface IRenderer { - render: (container: HTMLElement, props?: IProps) => void; - unrender: (container: HTMLElement) => void; - - icon: IJLIcon; - } - /** - * The IJLIcon interface. Outside of this interface the actual - * implementation of JLIcon may vary + * The simplest possible interface for defining a generic icon. */ - export interface IJLIcon { + export interface IIcon { /** * The name of the icon. By convention, the icon name will be namespaced * as so: @@ -503,30 +503,62 @@ export namespace JLIcon { svgstr: string; } - export interface IJLIconRenderable extends IJLIcon { - readonly renderer: IRenderer; + /** + * Interface for generic renderer. Compatible with interface of + * Title.iconRenderer from @lumino/widgets + */ + export interface IRenderer { + render: (container: HTMLElement, props?: IProps) => void; + unrender: (container: HTMLElement) => void; + } + + /** + * The IJLIcon interface. Outside of this interface the actual + * implementation of JLIcon may vary + */ + export interface IJLIcon extends IIcon, IRenderer {} + + /** + * A type that can be resolved to a JLIcon instance. + */ + export type IResolvable = string | IJLIcon; + + /** + * Base implementation of IRenderer. + */ + export class Renderer implements IRenderer { + constructor(protected _icon: JLIcon) {} + + render(container: HTMLElement, props: IProps): void {} + unrender(container: HTMLElement): void {} } - export class ElementRenderer implements IRenderer { + /** + * Implementation of IRenderer that creates the icon svg node + * as a DOM element. + */ + export class ElementRenderer extends Renderer { render(container: HTMLElement, props: IProps = {}): void { // TODO: move this title fix to the Lumino side container.removeAttribute('title'); - this.icon.element({ container, ...props }); + this._icon.element({ container, ...props }); } unrender(container: HTMLElement): void {} - - public icon: JLIcon = blankIcon; } - export class ReactRenderer implements IRenderer { + /** + * Implementation of IRenderer that creates the icon svg node + * as a React component. + */ + export class ReactRenderer extends Renderer { render(container: HTMLElement, props: JLIcon.IProps = {}): void { // TODO: move this title fix to the Lumino side container.removeAttribute('title'); return ReactDOM.render( - , + , container ); } @@ -534,14 +566,15 @@ export namespace JLIcon { unrender(container: HTMLElement): void { ReactDOM.unmountComponentAtNode(container); } - - public icon: JLIcon = blankIcon; } /** - * A type that can be resolved to a JLIcon instance. + * Interface defining the parameters to be passed to the JLIcon + * constructor */ - export type IResolvable = string | IJLIcon; + export interface IOptions extends IIcon, Partial { + rendererClass?: typeof Renderer; + } /** * The input props for creating a new JLIcon @@ -577,8 +610,16 @@ export namespace JLIcon { title?: string; } + /** + * The properties that can be passing into the React component stored in + * the .react field of a JLIcon. + */ export type IReactProps = IProps & React.RefAttributes; + /** + * The complete type of the React component stored in the .react + * field of a JLIcon. + */ export type IReact = React.ForwardRefExoticComponent; } From 65cdd2af78d8af28c449906b40a6ea720710e1d4 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 24 Jan 2020 18:25:50 -0500 Subject: [PATCH 22/72] untangled IFileType vs FileType mess in DocumentRegistry - first commit since 30d390acad for which jlab successfully builds - still need to actually test out/run the code, see how many things the recent changes to icon handling broke --- packages/csvviewer-extension/src/index.ts | 4 +- packages/docregistry/src/mimedocument.ts | 9 +- packages/docregistry/src/registry.ts | 222 ++++++++++--------- packages/filebrowser/src/listing.ts | 6 +- packages/htmlviewer-extension/src/index.tsx | 8 +- packages/imageviewer-extension/src/index.ts | 2 +- packages/markdownviewer/src/widget.ts | 9 +- packages/notebook-extension/src/index.ts | 2 +- packages/ui-components/src/icon/tabbarsvg.ts | 2 +- 9 files changed, 138 insertions(+), 126 deletions(-) diff --git a/packages/csvviewer-extension/src/index.ts b/packages/csvviewer-extension/src/index.ts index 3eee63e689db..2205dc7e3921 100644 --- a/packages/csvviewer-extension/src/index.ts +++ b/packages/csvviewer-extension/src/index.ts @@ -130,7 +130,7 @@ function activateCsv( if (ft) { widget.title.iconClass = ft.iconClass!; widget.title.iconLabel = ft.iconLabel!; - widget.title.iconRenderer = ft.iconRenderer!; + widget.title.iconRenderer = ft.icon!; } // Set the theme for the new widget. widget.content.style = style; @@ -210,7 +210,7 @@ function activateTsv( if (ft) { widget.title.iconClass = ft.iconClass!; widget.title.iconLabel = ft.iconLabel!; - widget.title.iconRenderer = ft.iconRenderer!; + widget.title.iconRenderer = ft.icon!; } // Set the theme for the new widget. widget.content.style = style; diff --git a/packages/docregistry/src/mimedocument.ts b/packages/docregistry/src/mimedocument.ts index 9c751020ec6c..b2a222beaf0c 100644 --- a/packages/docregistry/src/mimedocument.ts +++ b/packages/docregistry/src/mimedocument.ts @@ -264,7 +264,10 @@ export class MimeDocumentFactory extends ABCWidgetFactory { this._rendermime = options.rendermime; this._renderTimeout = options.renderTimeout || 1000; this._dataType = options.dataType || 'string'; - this._fileType = options.primaryFileType; + + // resolve the passed in IFileType to a FileType + const ft = options.primaryFileType; + this._fileType = ft ? DocumentRegistry.FileType.resolve(ft) : ft; } /** @@ -289,7 +292,7 @@ export class MimeDocumentFactory extends ABCWidgetFactory { content.title.iconClass = ft?.iconClass ?? ''; content.title.iconLabel = ft?.iconLabel ?? ''; - content.title.iconRenderer = ft?.iconRenderer!; + content.title.iconRenderer = ft?.icon!; const widget = new MimeDocument({ content, context }); @@ -299,7 +302,7 @@ export class MimeDocumentFactory extends ABCWidgetFactory { private _rendermime: IRenderMimeRegistry; private _renderTimeout: number; private _dataType: 'string' | 'json'; - private _fileType: DocumentRegistry.IFileType | undefined; + private _fileType: DocumentRegistry.FileType | undefined; } /** diff --git a/packages/docregistry/src/registry.ts b/packages/docregistry/src/registry.ts index 73df11c80c67..0239bf37cb6f 100644 --- a/packages/docregistry/src/registry.ts +++ b/packages/docregistry/src/registry.ts @@ -64,11 +64,11 @@ export class DocumentRegistry implements IDisposable { } this._modelFactories['text'] = factory || new TextModelFactory(); - let fts = options.initialFileTypes || DocumentRegistry.defaultFileTypes; - fts.forEach(ft => { - let value = new DocumentRegistry.FileType(ft); - this._fileTypes.push(value); - }); + let fts = + options.initialFileTypes?.map(ft => + DocumentRegistry.FileType.resolve(ft) + ) || DocumentRegistry.defaultFileTypes; + fts.forEach(ft => this._fileTypes.push(ft)); } /** @@ -287,7 +287,7 @@ export class DocumentRegistry implements IDisposable { ...DocumentRegistry.fileTypeDefaults, ...fileType }; - this._fileTypes.push(new DocumentRegistry.FileType(value)); + this._fileTypes.push(DocumentRegistry.FileType.resolve(value)); this._changed.emit({ type: 'fileType', @@ -1215,6 +1215,14 @@ export namespace DocumentRegistry { * A wrapper for IFileTypes */ export class FileType implements IFileType { + static resolve(ft: Partial): FileType { + if (ft instanceof FileType) { + return ft; + } + + return new FileType(ft); + } + constructor(options: Partial) { Object.assign(this, fileTypeDefaults, options); @@ -1224,10 +1232,6 @@ export namespace DocumentRegistry { } } - get iconRenderer(): JLIcon.IRenderer | null { - return this.icon?.renderer ?? null; - } - readonly name: string; readonly mimeTypes: ReadonlyArray; readonly extensions: ReadonlyArray; @@ -1304,107 +1308,109 @@ export namespace DocumentRegistry { /** * The default file types used by the document registry. */ - export const defaultFileTypes: ReadonlyArray> = [ + export const defaultFileTypes: ReadonlyArray = [ defaultTextFileType, defaultNotebookFileType, defaultDirectoryFileType, - { - name: 'markdown', - displayName: 'Markdown File', - extensions: ['.md'], - mimeTypes: ['text/markdown'], - icon: markdownIcon - }, - { - name: 'python', - displayName: 'Python File', - extensions: ['.py'], - mimeTypes: ['text/x-python'], - icon: pythonIcon - }, - { - name: 'json', - displayName: 'JSON File', - extensions: ['.json'], - mimeTypes: ['application/json'], - icon: jsonIcon - }, - { - name: 'csv', - displayName: 'CSV File', - extensions: ['.csv'], - mimeTypes: ['text/csv'], - icon: spreadsheetIcon - }, - { - name: 'tsv', - displayName: 'TSV File', - extensions: ['.tsv'], - mimeTypes: ['text/csv'], - icon: spreadsheetIcon - }, - { - name: 'r', - displayName: 'R File', - mimeTypes: ['text/x-rsrc'], - extensions: ['.r'], - icon: rKernelIcon - }, - { - name: 'yaml', - displayName: 'YAML File', - mimeTypes: ['text/x-yaml', 'text/yaml'], - extensions: ['.yaml', '.yml'], - icon: yamlIcon - }, - { - name: 'svg', - displayName: 'Image', - mimeTypes: ['image/svg+xml'], - extensions: ['.svg'], - icon: imageIcon, - fileFormat: 'base64' - }, - { - name: 'tiff', - displayName: 'Image', - mimeTypes: ['image/tiff'], - extensions: ['.tif', '.tiff'], - icon: imageIcon, - fileFormat: 'base64' - }, - { - name: 'jpeg', - displayName: 'Image', - mimeTypes: ['image/jpeg'], - extensions: ['.jpg', '.jpeg'], - icon: imageIcon, - fileFormat: 'base64' - }, - { - name: 'gif', - displayName: 'Image', - mimeTypes: ['image/gif'], - extensions: ['.gif'], - icon: imageIcon, - fileFormat: 'base64' - }, - { - name: 'png', - displayName: 'Image', - mimeTypes: ['image/png'], - extensions: ['.png'], - icon: imageIcon, - fileFormat: 'base64' - }, - { - name: 'bmp', - displayName: 'Image', - mimeTypes: ['image/bmp'], - extensions: ['.bmp'], - icon: imageIcon, - fileFormat: 'base64' - } + ...[ + { + name: 'markdown', + displayName: 'Markdown File', + extensions: ['.md'], + mimeTypes: ['text/markdown'], + icon: markdownIcon + }, + { + name: 'python', + displayName: 'Python File', + extensions: ['.py'], + mimeTypes: ['text/x-python'], + icon: pythonIcon + }, + { + name: 'json', + displayName: 'JSON File', + extensions: ['.json'], + mimeTypes: ['application/json'], + icon: jsonIcon + }, + { + name: 'csv', + displayName: 'CSV File', + extensions: ['.csv'], + mimeTypes: ['text/csv'], + icon: spreadsheetIcon + }, + { + name: 'tsv', + displayName: 'TSV File', + extensions: ['.tsv'], + mimeTypes: ['text/csv'], + icon: spreadsheetIcon + }, + { + name: 'r', + displayName: 'R File', + mimeTypes: ['text/x-rsrc'], + extensions: ['.r'], + icon: rKernelIcon + }, + { + name: 'yaml', + displayName: 'YAML File', + mimeTypes: ['text/x-yaml', 'text/yaml'], + extensions: ['.yaml', '.yml'], + icon: yamlIcon + }, + { + name: 'svg', + displayName: 'Image', + mimeTypes: ['image/svg+xml'], + extensions: ['.svg'], + icon: imageIcon, + fileFormat: 'base64' + }, + { + name: 'tiff', + displayName: 'Image', + mimeTypes: ['image/tiff'], + extensions: ['.tif', '.tiff'], + icon: imageIcon, + fileFormat: 'base64' + }, + { + name: 'jpeg', + displayName: 'Image', + mimeTypes: ['image/jpeg'], + extensions: ['.jpg', '.jpeg'], + icon: imageIcon, + fileFormat: 'base64' + }, + { + name: 'gif', + displayName: 'Image', + mimeTypes: ['image/gif'], + extensions: ['.gif'], + icon: imageIcon, + fileFormat: 'base64' + }, + { + name: 'png', + displayName: 'Image', + mimeTypes: ['image/png'], + extensions: ['.png'], + icon: imageIcon, + fileFormat: 'base64' + }, + { + name: 'bmp', + displayName: 'Image', + mimeTypes: ['image/bmp'], + extensions: ['.bmp'], + icon: imageIcon, + fileFormat: 'base64' + } + ].map(ft => new FileType(ft as Partial)) ]; } diff --git a/packages/filebrowser/src/listing.ts b/packages/filebrowser/src/listing.ts index db22b5a5de27..b9cce68da3d5 100644 --- a/packages/filebrowser/src/listing.ts +++ b/packages/filebrowser/src/listing.ts @@ -1839,7 +1839,7 @@ export namespace DirListing { const modified = DOMUtils.findElement(node, ITEM_MODIFIED_CLASS); // render the icon svg node - if (fileType?.iconRenderer) { + if (fileType?.icon) { iconContainer.className = classes( ITEM_ICON_CLASS, iconStyle({ @@ -1848,7 +1848,7 @@ export namespace DirListing { }) ); - fileType.iconRenderer.render(iconContainer); + fileType.icon.render(iconContainer); } else if (fileType?.iconClass) { iconContainer.className = classes(ITEM_ICON_CLASS, fileType.iconClass); @@ -1864,7 +1864,7 @@ export namespace DirListing { }) ); - fileIcon.renderer.render(iconContainer); + fileIcon.render(iconContainer); } let hoverText = 'Name: ' + model.name; diff --git a/packages/htmlviewer-extension/src/index.tsx b/packages/htmlviewer-extension/src/index.tsx index 9c9b267dde02..f09ea64e07ad 100644 --- a/packages/htmlviewer-extension/src/index.tsx +++ b/packages/htmlviewer-extension/src/index.tsx @@ -48,15 +48,15 @@ function activateHTMLViewer( restorer: ILayoutRestorer | null ): IHTMLViewerTracker { // Add an HTML file type to the docregistry. - const ft: DocumentRegistry.IFileType = { + const ft = new DocumentRegistry.FileType({ name: 'html', contentType: 'file', fileFormat: 'text', displayName: 'HTML File', extensions: ['.html'], mimeTypes: ['text/html'], - iconRenderer: html5Icon - }; + icon: html5Icon + }); app.docRegistry.addFileType(ft); // Create a new viewer factory. @@ -97,7 +97,7 @@ function activateHTMLViewer( widget.title.iconClass = ft.iconClass ?? ''; widget.title.iconLabel = ft.iconLabel ?? ''; - widget.title.iconRenderer = ft.iconRenderer!; + widget.title.iconRenderer = ft.icon!; }); // Add a command to trust the active HTML document, diff --git a/packages/imageviewer-extension/src/index.ts b/packages/imageviewer-extension/src/index.ts index 17dfefb3d5c1..019388be6971 100644 --- a/packages/imageviewer-extension/src/index.ts +++ b/packages/imageviewer-extension/src/index.ts @@ -107,7 +107,7 @@ function activate( if (types.length > 0) { widget.title.iconClass = types[0].iconClass ?? ''; widget.title.iconLabel = types[0].iconLabel ?? ''; - widget.title.iconRenderer = types[0].iconRenderer!; + widget.title.iconRenderer = types[0].icon!; } }); diff --git a/packages/markdownviewer/src/widget.ts b/packages/markdownviewer/src/widget.ts index 105608d9d2f4..685242c227f1 100644 --- a/packages/markdownviewer/src/widget.ts +++ b/packages/markdownviewer/src/widget.ts @@ -294,8 +294,11 @@ export class MarkdownViewerFactory extends ABCWidgetFactory { */ constructor(options: MarkdownViewerFactory.IOptions) { super(Private.createRegistryOptions(options)); - this._fileType = options.primaryFileType; this._rendermime = options.rendermime; + + // resolve the passed in IFileType to a FileType + const ft = options.primaryFileType; + this._fileType = ft ? DocumentRegistry.FileType.resolve(ft) : ft; } /** @@ -311,13 +314,13 @@ export class MarkdownViewerFactory extends ABCWidgetFactory { const content = new MarkdownViewer({ context, renderer }); content.title.iconClass = this._fileType?.iconClass ?? ''; content.title.iconLabel = this._fileType?.iconLabel ?? ''; - content.title.iconRenderer = this._fileType?.iconRenderer!; + content.title.iconRenderer = this._fileType?.icon!; const widget = new MarkdownDocument({ content, context }); return widget; } - private _fileType: DocumentRegistry.IFileType | undefined; + private _fileType: DocumentRegistry.FileType | undefined; private _rendermime: IRenderMimeRegistry; } diff --git a/packages/notebook-extension/src/index.ts b/packages/notebook-extension/src/index.ts index 32aded4a56c7..620ef27a5c43 100644 --- a/packages/notebook-extension/src/index.ts +++ b/packages/notebook-extension/src/index.ts @@ -582,7 +582,7 @@ function activateNotebookHandler( // Set up the title icon widget.title.iconClass = ft?.iconClass ?? ''; widget.title.iconLabel = ft?.iconLabel ?? ''; - widget.title.iconRenderer = ft?.iconRenderer!; + widget.title.iconRenderer = ft?.icon!; // Notify the widget tracker if restore data needs to update. widget.context.pathChanged.connect(() => { diff --git a/packages/ui-components/src/icon/tabbarsvg.ts b/packages/ui-components/src/icon/tabbarsvg.ts index 0c360f17e218..1ca9abe629f7 100644 --- a/packages/ui-components/src/icon/tabbarsvg.ts +++ b/packages/ui-components/src/icon/tabbarsvg.ts @@ -46,7 +46,7 @@ export namespace TabBarSvg { return (hpass( 'div', { className }, - closeIcon.renderer + closeIcon ) as unknown) as VirtualElement; } } From b1a8127238ae20a5f34eef0b0a235ef0ee5c0599 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 24 Jan 2020 19:21:38 -0500 Subject: [PATCH 23/72] picked lint --- packages/ui-components/src/icon/jlicon.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index 0ec8593e5493..892276ca26f8 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -529,7 +529,9 @@ export namespace JLIcon { export class Renderer implements IRenderer { constructor(protected _icon: JLIcon) {} + // tslint:disable-next-line:no-empty render(container: HTMLElement, props: IProps): void {} + // tslint:disable-next-line:no-empty unrender(container: HTMLElement): void {} } @@ -545,6 +547,7 @@ export namespace JLIcon { this._icon.element({ container, ...props }); } + // tslint:disable-next-line:no-empty unrender(container: HTMLElement): void {} } From cca887d763ff18c88c89a07d30529219d74a3b89 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 24 Jan 2020 19:22:56 -0500 Subject: [PATCH 24/72] `IFileType.icon` now working in rendermime-interfaces; - fully functional example of `icon: string | {name, svgstr}` interface can be found in (the rendermime-interfaces based) vega5-extension pkg - initial testing indicates that the rest of the icons in jlab also made it through the recent changes mostly (completely?) unscathed - there do seem to be some real CI failures in test-docregistry --- packages/vega5-extension/src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vega5-extension/src/index.ts b/packages/vega5-extension/src/index.ts index ae0f10014f75..9a07d02a27dc 100644 --- a/packages/vega5-extension/src/index.ts +++ b/packages/vega5-extension/src/index.ts @@ -174,19 +174,19 @@ const extension: IRenderMime.IExtension = { mimeTypes: [VEGA_MIME_TYPE], name: 'vega5', extensions: ['.vg', '.vg.json', '.vega'], - iconClass: 'ui-components:vega' + icon: 'ui-components:vega' }, { mimeTypes: [VEGALITE4_MIME_TYPE], name: 'vega-lite4', extensions: ['.vl', '.vl.json', '.vegalite'], - iconClass: 'ui-components:vega' + icon: 'ui-components:vega' }, { mimeTypes: [VEGALITE3_MIME_TYPE], name: 'vega-lite3', extensions: [], - iconClass: 'ui-components:vega' + icon: 'ui-components:vega' } ] }; From 5f22efd5a328fc7ca290fcacce6a86d887f737d4 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 24 Jan 2020 19:52:53 -0500 Subject: [PATCH 25/72] added index.ts to src/style subpkg of ui-components --- packages/ui-components/src/style/index.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 packages/ui-components/src/style/index.ts diff --git a/packages/ui-components/src/style/index.ts b/packages/ui-components/src/style/index.ts new file mode 100644 index 000000000000..e6e4ba467cb9 --- /dev/null +++ b/packages/ui-components/src/style/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +export * from './icon'; From 73b92526a90171ce85c0ecd21eb3125a58d27707 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 24 Jan 2020 19:53:25 -0500 Subject: [PATCH 26/72] fixed external import of exports from src/style in ui-components pkg --- packages/application/src/shell.ts | 3 +-- packages/extensionmanager/src/widget.tsx | 2 +- packages/filebrowser/src/listing.ts | 6 ++---- packages/ui-components/src/index.ts | 1 + 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/application/src/shell.ts b/packages/application/src/shell.ts index 111320120ac5..1a5c628e84be 100644 --- a/packages/application/src/shell.ts +++ b/packages/application/src/shell.ts @@ -3,7 +3,7 @@ import { DocumentRegistry } from '@jupyterlab/docregistry'; -import { classes, DockPanelSvg } from '@jupyterlab/ui-components'; +import { classes, DockPanelSvg, iconStyle } from '@jupyterlab/ui-components'; import { ArrayExt, find, IIterator, iter, toArray } from '@lumino/algorithm'; @@ -30,7 +30,6 @@ import { } from '@lumino/widgets'; import { JupyterFrontEnd } from './frontend'; -import { iconStyle } from '@jupyterlab/ui-components/lib/style/icon'; /** * The class name added to AppShell instances. diff --git a/packages/extensionmanager/src/widget.tsx b/packages/extensionmanager/src/widget.tsx index 001f1bb61faf..08637ac3616a 100644 --- a/packages/extensionmanager/src/widget.tsx +++ b/packages/extensionmanager/src/widget.tsx @@ -7,6 +7,7 @@ import { Button, InputGroup, Collapse, + iconStyle, refreshIcon, jupyterIcon, caretRightIcon, @@ -19,7 +20,6 @@ import ReactPaginate from 'react-paginate'; import { ListModel, IEntry, Action } from './model'; import { isJupyterOrg } from './query'; -import { iconStyle } from '@jupyterlab/ui-components/lib/style/icon'; // TODO: Replace pagination with lazy loading of lower search results diff --git a/packages/filebrowser/src/listing.ts b/packages/filebrowser/src/listing.ts index b9cce68da3d5..08147e7e7a7c 100644 --- a/packages/filebrowser/src/listing.ts +++ b/packages/filebrowser/src/listing.ts @@ -25,6 +25,8 @@ import { caretUpIcon, classes, fileIcon, + iconStyle, + IIconStyle, JLIcon } from '@jupyterlab/ui-components'; @@ -52,10 +54,6 @@ import { ISignal, Signal } from '@lumino/signaling'; import { Widget } from '@lumino/widgets'; import { FileBrowserModel } from './model'; -import { - iconStyle, - IIconStyle -} from '@jupyterlab/ui-components/lib/style/icon'; /** * The class name added to DirListing widget. diff --git a/packages/ui-components/src/index.ts b/packages/ui-components/src/index.ts index 35977b28da98..9e6e5ccd34f7 100644 --- a/packages/ui-components/src/index.ts +++ b/packages/ui-components/src/index.ts @@ -4,5 +4,6 @@ export * from './blueprint'; export * from './components'; export * from './icon'; +export * from './style'; export * from './tokens'; export * from './utils'; From 5219caf320f8f69fdfa045318914082126a0c291 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 24 Jan 2020 20:45:28 -0500 Subject: [PATCH 27/72] added render-related fields to `IFileType.icon` interface --- packages/docregistry/src/registry.ts | 4 +-- packages/rendermime-interfaces/src/index.ts | 35 ++++++++++++++++++++- packages/ui-components/src/icon/jlicon.tsx | 14 ++++----- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/packages/docregistry/src/registry.ts b/packages/docregistry/src/registry.ts index 0239bf37cb6f..572ad4e850f8 100644 --- a/packages/docregistry/src/registry.ts +++ b/packages/docregistry/src/registry.ts @@ -1177,7 +1177,7 @@ export namespace DocumentRegistry { * of an existing icon, or an object with {name, svgstr} fields, where * svgstr is a string containing the raw contents of an svg file. */ - readonly icon?: string | { name: string; svgstr: string } | null; + readonly icon?: JLIcon.IResolvable; /** * The icon class name for the file type. @@ -1237,7 +1237,7 @@ export namespace DocumentRegistry { readonly extensions: ReadonlyArray; readonly displayName: string; readonly pattern: string; - readonly icon: JLIcon | null; + readonly icon?: JLIcon; readonly iconClass: string; readonly iconLabel: string; readonly contentType: Contents.ContentType; diff --git a/packages/rendermime-interfaces/src/index.ts b/packages/rendermime-interfaces/src/index.ts index 8c3f2012bf8e..71f8ec9c14c0 100644 --- a/packages/rendermime-interfaces/src/index.ts +++ b/packages/rendermime-interfaces/src/index.ts @@ -116,6 +116,39 @@ export namespace IRenderMime { readonly toolbarFactory?: (widget?: IRenderer) => IToolbarItem[]; } + export namespace LabIcon { + /** + * The simplest possible interface for defining a generic icon. + */ + export interface IIcon { + /** + * The name of the icon. By convention, the icon name will be namespaced + * as so: + * + * "pkg-name:icon-name" + */ + readonly name: string; + + /** + * A string containing the raw contents of an svg file. + */ + svgstr: string; + } + + /** + * Interface for generic renderer. + */ + export interface IRenderer { + render: (container: HTMLElement) => void; + unrender: (container: HTMLElement) => void; + } + + /** + * A type that can be resolved to a JLIcon instance. + */ + export type IResolvable = string | (IIcon & Partial); + } + /** * A file type to associate with the renderer. */ @@ -151,7 +184,7 @@ export namespace IRenderMime { * of an existing icon, or an object with {name, svgstr} fields, where * svgstr is a string containing the raw contents of an svg file. */ - readonly icon?: string | { name: string; svgstr: string }; + readonly icon?: LabIcon.IResolvable; /** * The icon class name for the file type. diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index 892276ca26f8..ef9e8ff13a08 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -133,7 +133,7 @@ export class JLIcon implements JLIcon.IJLIcon, JLIcon.IRenderer { * @returns a JLIcon instance, or null if an icon name was passed in * and lookup fails. */ - static resolve(icon: JLIcon.IResolvable): JLIcon | null { + static resolve(icon: JLIcon.IResolvable): JLIcon | undefined { if (icon instanceof JLIcon) { // icon already is a JLIcon return icon; @@ -141,7 +141,7 @@ export class JLIcon implements JLIcon.IJLIcon, JLIcon.IRenderer { if (typeof icon === 'string') { // do a dynamic lookup of existing icon by name - return JLIcon._get(icon) ?? null; + return JLIcon._get(icon); } // icon is a non-JLIcon {name, svgstr} pair @@ -518,11 +518,6 @@ export namespace JLIcon { */ export interface IJLIcon extends IIcon, IRenderer {} - /** - * A type that can be resolved to a JLIcon instance. - */ - export type IResolvable = string | IJLIcon; - /** * Base implementation of IRenderer. */ @@ -579,6 +574,11 @@ export namespace JLIcon { rendererClass?: typeof Renderer; } + /** + * A type that can be resolved to a JLIcon instance. + */ + export type IResolvable = string | (IIcon & Partial); + /** * The input props for creating a new JLIcon */ From 7ad325aa48513b463ec0dba05b2385b61532e363 Mon Sep 17 00:00:00 2001 From: telamonian Date: Sat, 25 Jan 2020 20:02:15 -0500 Subject: [PATCH 28/72] removed outdated IconRegistry based stories --- .../stories/iconreact.stories.tsx | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 packages/ui-components/stories/iconreact.stories.tsx diff --git a/packages/ui-components/stories/iconreact.stories.tsx b/packages/ui-components/stories/iconreact.stories.tsx deleted file mode 100644 index 4aa89d96138c..000000000000 --- a/packages/ui-components/stories/iconreact.stories.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Example story for styling an icon. - */ -// need this to avoid -// TS2686: 'React' refers to a UMD global, but the current file is a module. -import React from 'react'; - -import { DefaultIconReact } from '../src'; - -import '@jupyterlab/application/style/index.css'; -import '@jupyterlab/theme-light-extension/style/index.css'; - -export default { - component: DefaultIconReact, - title: 'IconReact' -}; - -export const buildIcon = () => ( - -); - -export const runningIcon = () => ( - -); - -export const html5Icon = () => ( -
- -
-); From 52a6b688a0484775b339e1ff518cb16c7a8ae425 Mon Sep 17 00:00:00 2001 From: telamonian Date: Sat, 25 Jan 2020 20:03:04 -0500 Subject: [PATCH 29/72] added stories based on new JLIcon icons --- .../ui-components/stories/labicon.stories.tsx | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 packages/ui-components/stories/labicon.stories.tsx diff --git a/packages/ui-components/stories/labicon.stories.tsx b/packages/ui-components/stories/labicon.stories.tsx new file mode 100644 index 000000000000..6cb80fd63cbe --- /dev/null +++ b/packages/ui-components/stories/labicon.stories.tsx @@ -0,0 +1,30 @@ +/** + * Example story for styling an icon. + */ +// need this to avoid +// TS2686: 'React' refers to a UMD global, but the current file is a module. +import React from 'react'; + +import { buildIcon, runningIcon, html5Icon } from '../src'; + +import '@jupyterlab/application/style/index.css'; +import '@jupyterlab/theme-light-extension/style/index.css'; + +export default { + // component: JLIcon, + title: 'IconReact' +}; + +// a Scene is part of a Story + +export const buildIconScene = () => ; + +export const runningIconScene = () => ( + +); + +export const html5IconScene = () => ( +
+ +
+); From be2f1ba47261827e90fe12349a3e4bb69c9772a9 Mon Sep 17 00:00:00 2001 From: telamonian Date: Sat, 25 Jan 2020 20:12:33 -0500 Subject: [PATCH 30/72] cleanup for new story --- packages/ui-components/stories/labicon.stories.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/ui-components/stories/labicon.stories.tsx b/packages/ui-components/stories/labicon.stories.tsx index 6cb80fd63cbe..2a38de4359e8 100644 --- a/packages/ui-components/stories/labicon.stories.tsx +++ b/packages/ui-components/stories/labicon.stories.tsx @@ -12,18 +12,14 @@ import '@jupyterlab/theme-light-extension/style/index.css'; export default { // component: JLIcon, - title: 'IconReact' + title: 'LabIcon' }; -// a Scene is part of a Story +export const build = () => ; -export const buildIconScene = () => ; +export const running = () => ; -export const runningIconScene = () => ( - -); - -export const html5IconScene = () => ( +export const html5 = () => (
From 67c8b8c1a431a16d63a456a311a28f978f018e1f Mon Sep 17 00:00:00 2001 From: telamonian Date: Sat, 25 Jan 2020 22:32:15 -0500 Subject: [PATCH 31/72] cleanup of interfaces/types and related docstrings --- packages/rendermime-interfaces/src/index.ts | 4 +- packages/ui-components/src/icon/jlicon.tsx | 129 +++++++++++--------- 2 files changed, 74 insertions(+), 59 deletions(-) diff --git a/packages/rendermime-interfaces/src/index.ts b/packages/rendermime-interfaces/src/index.ts index 71f8ec9c14c0..5e90612e90e7 100644 --- a/packages/rendermime-interfaces/src/index.ts +++ b/packages/rendermime-interfaces/src/index.ts @@ -139,8 +139,8 @@ export namespace IRenderMime { * Interface for generic renderer. */ export interface IRenderer { - render: (container: HTMLElement) => void; - unrender: (container: HTMLElement) => void; + readonly render: (container: HTMLElement) => void; + readonly unrender: (container: HTMLElement) => void; } /** diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index ef9e8ff13a08..82c27e250775 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -8,7 +8,7 @@ import ReactDOM from 'react-dom'; import { Text } from '@jupyterlab/coreutils'; -import { iconStyle, IIconStyle } from '../style/icon'; +import { iconStyle, IIconStyle } from '../style'; import { getReactAttrs, classes, classesDedupe } from '../utils'; import badSvg from '../../style/debug/bad.svg'; @@ -485,6 +485,10 @@ export class JLIcon implements JLIcon.IJLIcon, JLIcon.IRenderer { * A namespace for JLIcon statics. */ export namespace JLIcon { + /************** + * interfaces * + **************/ + /** * The simplest possible interface for defining a generic icon. */ @@ -508,8 +512,8 @@ export namespace JLIcon { * Title.iconRenderer from @lumino/widgets */ export interface IRenderer { - render: (container: HTMLElement, props?: IProps) => void; - unrender: (container: HTMLElement) => void; + readonly render: (container: HTMLElement) => void; + readonly unrender: (container: HTMLElement) => void; } /** @@ -518,54 +522,6 @@ export namespace JLIcon { */ export interface IJLIcon extends IIcon, IRenderer {} - /** - * Base implementation of IRenderer. - */ - export class Renderer implements IRenderer { - constructor(protected _icon: JLIcon) {} - - // tslint:disable-next-line:no-empty - render(container: HTMLElement, props: IProps): void {} - // tslint:disable-next-line:no-empty - unrender(container: HTMLElement): void {} - } - - /** - * Implementation of IRenderer that creates the icon svg node - * as a DOM element. - */ - export class ElementRenderer extends Renderer { - render(container: HTMLElement, props: IProps = {}): void { - // TODO: move this title fix to the Lumino side - container.removeAttribute('title'); - - this._icon.element({ container, ...props }); - } - - // tslint:disable-next-line:no-empty - unrender(container: HTMLElement): void {} - } - - /** - * Implementation of IRenderer that creates the icon svg node - * as a React component. - */ - export class ReactRenderer extends Renderer { - render(container: HTMLElement, props: JLIcon.IProps = {}): void { - // TODO: move this title fix to the Lumino side - container.removeAttribute('title'); - - return ReactDOM.render( - , - container - ); - } - - unrender(container: HTMLElement): void { - ReactDOM.unmountComponentAtNode(container); - } - } - /** * Interface defining the parameters to be passed to the JLIcon * constructor @@ -574,11 +530,6 @@ export namespace JLIcon { rendererClass?: typeof Renderer; } - /** - * A type that can be resolved to a JLIcon instance. - */ - export type IResolvable = string | (IIcon & Partial); - /** * The input props for creating a new JLIcon */ @@ -613,8 +564,17 @@ export namespace JLIcon { title?: string; } + /********* + * types * + *********/ + + /** + * A type that can be resolved to a JLIcon instance. + */ + export type IResolvable = string | (IIcon & Partial); + /** - * The properties that can be passing into the React component stored in + * The properties that can be passed into the React component stored in * the .react field of a JLIcon. */ export type IReactProps = IProps & React.RefAttributes; @@ -624,6 +584,61 @@ export namespace JLIcon { * field of a JLIcon. */ export type IReact = React.ForwardRefExoticComponent; + + /*********** + * classes * + ***********/ + + /** + * Base implementation of IRenderer. + */ + export class Renderer implements IRenderer { + constructor(protected _icon: JLIcon, protected _props: IProps) {} + + // tslint:disable-next-line:no-empty + render(container: HTMLElement): void {} + // tslint:disable-next-line:no-empty + unrender(container: HTMLElement): void {} + } + + /** + * Implementation of IRenderer that creates the icon svg node + * as a DOM element. + */ + export class ElementRenderer extends Renderer { + render(container: HTMLElement, props: JLIcon.IProps = {}): void { + // TODO: move this title fix to the Lumino side + container.removeAttribute('title'); + + this._icon.element({ container, ...this._props, ...props }); + } + + // tslint:disable-next-line:no-empty + unrender(container: HTMLElement): void {} + } + + /** + * Implementation of IRenderer that creates the icon svg node + * as a React component. + */ + export class ReactRenderer extends Renderer { + render(container: HTMLElement, props: JLIcon.IProps = {}): void { + // TODO: move this title fix to the Lumino side + container.removeAttribute('title'); + + return ReactDOM.render( + , + container + ); + } + + unrender(container: HTMLElement): void { + ReactDOM.unmountComponentAtNode(container); + } + } } namespace Private { From 8c82f285ac130cd6096a8f055e0dd0ee5d9c2bfc Mon Sep 17 00:00:00 2001 From: telamonian Date: Sat, 25 Jan 2020 22:34:38 -0500 Subject: [PATCH 32/72] bugfix to Renderer constructor signature typing --- packages/ui-components/src/icon/jlicon.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index 82c27e250775..c478f6e729f4 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -593,7 +593,7 @@ export namespace JLIcon { * Base implementation of IRenderer. */ export class Renderer implements IRenderer { - constructor(protected _icon: JLIcon, protected _props: IProps) {} + constructor(protected _icon: JLIcon, protected _props?: IProps) {} // tslint:disable-next-line:no-empty render(container: HTMLElement): void {} From 4f6c10416d98b7a1b697ea43dd41f377de691f4a Mon Sep 17 00:00:00 2001 From: telamonian Date: Sat, 25 Jan 2020 23:03:07 -0500 Subject: [PATCH 33/72] use full icon implementation for `.icon` field in docregistry --- packages/docregistry/src/registry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docregistry/src/registry.ts b/packages/docregistry/src/registry.ts index 572ad4e850f8..1ee678f34994 100644 --- a/packages/docregistry/src/registry.ts +++ b/packages/docregistry/src/registry.ts @@ -1177,7 +1177,7 @@ export namespace DocumentRegistry { * of an existing icon, or an object with {name, svgstr} fields, where * svgstr is a string containing the raw contents of an svg file. */ - readonly icon?: JLIcon.IResolvable; + readonly icon?: JLIcon; /** * The icon class name for the file type. From 655a20f50dbf5894033736cbed43056b9e624d4c Mon Sep 17 00:00:00 2001 From: telamonian Date: Sat, 25 Jan 2020 23:03:27 -0500 Subject: [PATCH 34/72] added icon upconversion to rendermime plugin creation routine --- packages/application/src/mimerenderers.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/application/src/mimerenderers.ts b/packages/application/src/mimerenderers.ts index a30f15dd1f87..56135cd70cca 100644 --- a/packages/application/src/mimerenderers.ts +++ b/packages/application/src/mimerenderers.ts @@ -13,6 +13,8 @@ import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; import { IRenderMime } from '@jupyterlab/rendermime-interfaces'; +import { JLIcon } from '@jupyterlab/ui-components'; + import { Token } from '@lumino/coreutils'; import { AttachedProperty } from '@lumino/properties'; @@ -123,6 +125,11 @@ export function createRendermimePlugin( if (item.fileTypes) { item.fileTypes.forEach(ft => { + if (ft.icon) { + // upconvert the contents of the icon field to a proper JLIcon + ft = { ...ft, icon: JLIcon.resolve(ft.icon) }; + } + app.docRegistry.addFileType(ft as DocumentRegistry.IFileType); }); } From 126a78e8334710cf678cdc2fa3db5b59f83237c0 Mon Sep 17 00:00:00 2001 From: telamonian Date: Sat, 25 Jan 2020 23:28:06 -0500 Subject: [PATCH 35/72] removed DocumentRegistry.FileType and reverts all usages to IFileType - automagic upconversion of file type icons will now only occur for the rendermime-interfaces version of IFileType.icon from rendermime-interfaces. Conversely, all icons specified in the docregistry version of IFileType.icon will need to be full JLIcon instances in the first place --- packages/docregistry/src/mimedocument.ts | 7 +- packages/docregistry/src/registry.ts | 268 +++++++++----------- packages/filebrowser/src/listing.ts | 8 +- packages/htmlviewer-extension/src/index.tsx | 4 +- packages/markdownviewer/src/widget.ts | 7 +- 5 files changed, 128 insertions(+), 166 deletions(-) diff --git a/packages/docregistry/src/mimedocument.ts b/packages/docregistry/src/mimedocument.ts index b2a222beaf0c..71b267544b23 100644 --- a/packages/docregistry/src/mimedocument.ts +++ b/packages/docregistry/src/mimedocument.ts @@ -264,10 +264,7 @@ export class MimeDocumentFactory extends ABCWidgetFactory { this._rendermime = options.rendermime; this._renderTimeout = options.renderTimeout || 1000; this._dataType = options.dataType || 'string'; - - // resolve the passed in IFileType to a FileType - const ft = options.primaryFileType; - this._fileType = ft ? DocumentRegistry.FileType.resolve(ft) : ft; + this._fileType = options.primaryFileType; } /** @@ -302,7 +299,7 @@ export class MimeDocumentFactory extends ABCWidgetFactory { private _rendermime: IRenderMimeRegistry; private _renderTimeout: number; private _dataType: 'string' | 'json'; - private _fileType: DocumentRegistry.FileType | undefined; + private _fileType: DocumentRegistry.IFileType | undefined; } /** diff --git a/packages/docregistry/src/registry.ts b/packages/docregistry/src/registry.ts index 1ee678f34994..26c650943230 100644 --- a/packages/docregistry/src/registry.ts +++ b/packages/docregistry/src/registry.ts @@ -64,11 +64,14 @@ export class DocumentRegistry implements IDisposable { } this._modelFactories['text'] = factory || new TextModelFactory(); - let fts = - options.initialFileTypes?.map(ft => - DocumentRegistry.FileType.resolve(ft) - ) || DocumentRegistry.defaultFileTypes; - fts.forEach(ft => this._fileTypes.push(ft)); + let fts = options.initialFileTypes || DocumentRegistry.defaultFileTypes; + fts.forEach(ft => { + let value: DocumentRegistry.IFileType = { + ...DocumentRegistry.fileTypeDefaults, + ...ft + }; + this._fileTypes.push(value); + }); } /** @@ -287,7 +290,7 @@ export class DocumentRegistry implements IDisposable { ...DocumentRegistry.fileTypeDefaults, ...fileType }; - this._fileTypes.push(DocumentRegistry.FileType.resolve(value)); + this._fileTypes.push(value); this._changed.emit({ type: 'fileType', @@ -554,7 +557,7 @@ export class DocumentRegistry implements IDisposable { /** * Get a file type by name. */ - getFileType(name: string): DocumentRegistry.FileType | undefined { + getFileType(name: string): DocumentRegistry.IFileType | undefined { name = name.toLowerCase(); return find(this._fileTypes, fileType => { return fileType.name.toLowerCase() === name; @@ -608,7 +611,7 @@ export class DocumentRegistry implements IDisposable { */ getFileTypeForModel( model: Partial - ): DocumentRegistry.FileType { + ): DocumentRegistry.IFileType { switch (model.type) { case 'directory': return ( @@ -640,8 +643,8 @@ export class DocumentRegistry implements IDisposable { * * @returns An ordered list of matching file types. */ - getFileTypesForPath(path: string): DocumentRegistry.FileType[] { - let fts: DocumentRegistry.FileType[] = []; + getFileTypesForPath(path: string): DocumentRegistry.IFileType[] { + let fts: DocumentRegistry.IFileType[] = []; let name = PathExt.basename(path); // Look for a pattern match first. @@ -688,7 +691,7 @@ export class DocumentRegistry implements IDisposable { private _widgetFactoriesForFileType: { [key: string]: string[]; } = Object.create(null); - private _fileTypes: DocumentRegistry.FileType[] = []; + private _fileTypes: DocumentRegistry.IFileType[] = []; private _extenders: { [key: string]: DocumentRegistry.WidgetExtension[]; } = Object.create(null); @@ -1211,39 +1214,6 @@ export namespace DocumentRegistry { fileFormat: 'text' }; - /** - * A wrapper for IFileTypes - */ - export class FileType implements IFileType { - static resolve(ft: Partial): FileType { - if (ft instanceof FileType) { - return ft; - } - - return new FileType(ft); - } - - constructor(options: Partial) { - Object.assign(this, fileTypeDefaults, options); - - if (this.icon) { - // ensure that the icon is resolved to a JLIcon - this.icon = JLIcon.resolve(this.icon); - } - } - - readonly name: string; - readonly mimeTypes: ReadonlyArray; - readonly extensions: ReadonlyArray; - readonly displayName: string; - readonly pattern: string; - readonly icon?: JLIcon; - readonly iconClass: string; - readonly iconLabel: string; - readonly contentType: Contents.ContentType; - readonly fileFormat: Contents.FileFormat; - } - /** * An arguments object for the `changed` signal. */ @@ -1271,18 +1241,18 @@ export namespace DocumentRegistry { /** * The default text file type used by the document registry. */ - export const defaultTextFileType = new FileType({ + export const defaultTextFileType: IFileType = { ...fileTypeDefaults, name: 'text', mimeTypes: ['text/plain'], extensions: ['.txt'], icon: fileIcon - }); + }; /** * The default notebook file type used by the document registry. */ - export const defaultNotebookFileType = new FileType({ + export const defaultNotebookFileType: IFileType = { ...fileTypeDefaults, name: 'notebook', displayName: 'Notebook', @@ -1291,126 +1261,124 @@ export namespace DocumentRegistry { contentType: 'notebook', fileFormat: 'json', icon: notebookIcon - }); + }; /** * The default directory file type used by the document registry. */ - export const defaultDirectoryFileType = new FileType({ + export const defaultDirectoryFileType: IFileType = { ...fileTypeDefaults, name: 'directory', extensions: [], mimeTypes: ['text/directory'], contentType: 'directory', icon: folderIcon - }); + }; /** * The default file types used by the document registry. */ - export const defaultFileTypes: ReadonlyArray = [ + export const defaultFileTypes: ReadonlyArray> = [ defaultTextFileType, defaultNotebookFileType, defaultDirectoryFileType, - ...[ - { - name: 'markdown', - displayName: 'Markdown File', - extensions: ['.md'], - mimeTypes: ['text/markdown'], - icon: markdownIcon - }, - { - name: 'python', - displayName: 'Python File', - extensions: ['.py'], - mimeTypes: ['text/x-python'], - icon: pythonIcon - }, - { - name: 'json', - displayName: 'JSON File', - extensions: ['.json'], - mimeTypes: ['application/json'], - icon: jsonIcon - }, - { - name: 'csv', - displayName: 'CSV File', - extensions: ['.csv'], - mimeTypes: ['text/csv'], - icon: spreadsheetIcon - }, - { - name: 'tsv', - displayName: 'TSV File', - extensions: ['.tsv'], - mimeTypes: ['text/csv'], - icon: spreadsheetIcon - }, - { - name: 'r', - displayName: 'R File', - mimeTypes: ['text/x-rsrc'], - extensions: ['.r'], - icon: rKernelIcon - }, - { - name: 'yaml', - displayName: 'YAML File', - mimeTypes: ['text/x-yaml', 'text/yaml'], - extensions: ['.yaml', '.yml'], - icon: yamlIcon - }, - { - name: 'svg', - displayName: 'Image', - mimeTypes: ['image/svg+xml'], - extensions: ['.svg'], - icon: imageIcon, - fileFormat: 'base64' - }, - { - name: 'tiff', - displayName: 'Image', - mimeTypes: ['image/tiff'], - extensions: ['.tif', '.tiff'], - icon: imageIcon, - fileFormat: 'base64' - }, - { - name: 'jpeg', - displayName: 'Image', - mimeTypes: ['image/jpeg'], - extensions: ['.jpg', '.jpeg'], - icon: imageIcon, - fileFormat: 'base64' - }, - { - name: 'gif', - displayName: 'Image', - mimeTypes: ['image/gif'], - extensions: ['.gif'], - icon: imageIcon, - fileFormat: 'base64' - }, - { - name: 'png', - displayName: 'Image', - mimeTypes: ['image/png'], - extensions: ['.png'], - icon: imageIcon, - fileFormat: 'base64' - }, - { - name: 'bmp', - displayName: 'Image', - mimeTypes: ['image/bmp'], - extensions: ['.bmp'], - icon: imageIcon, - fileFormat: 'base64' - } - ].map(ft => new FileType(ft as Partial)) + { + name: 'markdown', + displayName: 'Markdown File', + extensions: ['.md'], + mimeTypes: ['text/markdown'], + icon: markdownIcon + }, + { + name: 'python', + displayName: 'Python File', + extensions: ['.py'], + mimeTypes: ['text/x-python'], + icon: pythonIcon + }, + { + name: 'json', + displayName: 'JSON File', + extensions: ['.json'], + mimeTypes: ['application/json'], + icon: jsonIcon + }, + { + name: 'csv', + displayName: 'CSV File', + extensions: ['.csv'], + mimeTypes: ['text/csv'], + icon: spreadsheetIcon + }, + { + name: 'tsv', + displayName: 'TSV File', + extensions: ['.tsv'], + mimeTypes: ['text/csv'], + icon: spreadsheetIcon + }, + { + name: 'r', + displayName: 'R File', + mimeTypes: ['text/x-rsrc'], + extensions: ['.r'], + icon: rKernelIcon + }, + { + name: 'yaml', + displayName: 'YAML File', + mimeTypes: ['text/x-yaml', 'text/yaml'], + extensions: ['.yaml', '.yml'], + icon: yamlIcon + }, + { + name: 'svg', + displayName: 'Image', + mimeTypes: ['image/svg+xml'], + extensions: ['.svg'], + icon: imageIcon, + fileFormat: 'base64' + }, + { + name: 'tiff', + displayName: 'Image', + mimeTypes: ['image/tiff'], + extensions: ['.tif', '.tiff'], + icon: imageIcon, + fileFormat: 'base64' + }, + { + name: 'jpeg', + displayName: 'Image', + mimeTypes: ['image/jpeg'], + extensions: ['.jpg', '.jpeg'], + icon: imageIcon, + fileFormat: 'base64' + }, + { + name: 'gif', + displayName: 'Image', + mimeTypes: ['image/gif'], + extensions: ['.gif'], + icon: imageIcon, + fileFormat: 'base64' + }, + { + name: 'png', + displayName: 'Image', + mimeTypes: ['image/png'], + extensions: ['.png'], + icon: imageIcon, + fileFormat: 'base64' + }, + { + name: 'bmp', + displayName: 'Image', + mimeTypes: ['image/bmp'], + extensions: ['.bmp'], + icon: imageIcon, + fileFormat: 'base64' + } ]; } diff --git a/packages/filebrowser/src/listing.ts b/packages/filebrowser/src/listing.ts index 08147e7e7a7c..9c9c4292de05 100644 --- a/packages/filebrowser/src/listing.ts +++ b/packages/filebrowser/src/listing.ts @@ -1657,7 +1657,7 @@ export namespace DirListing { updateItemNode( node: HTMLElement, model: Contents.IModel, - fileType?: DocumentRegistry.FileType + fileType?: DocumentRegistry.IFileType ): void; /** @@ -1683,7 +1683,7 @@ export namespace DirListing { createDragImage( node: HTMLElement, count: number, - fileType?: DocumentRegistry.FileType + fileType?: DocumentRegistry.IFileType ): HTMLElement; } @@ -1830,7 +1830,7 @@ export namespace DirListing { updateItemNode( node: HTMLElement, model: Contents.IModel, - fileType?: DocumentRegistry.FileType + fileType?: DocumentRegistry.IFileType ): void { const iconContainer = DOMUtils.findElement(node, ITEM_ICON_CLASS); const text = DOMUtils.findElement(node, ITEM_TEXT_CLASS); @@ -1932,7 +1932,7 @@ export namespace DirListing { createDragImage( node: HTMLElement, count: number, - fileType?: DocumentRegistry.FileType + fileType?: DocumentRegistry.IFileType ): HTMLElement { let dragImage = node.cloneNode(true) as HTMLElement; let modified = DOMUtils.findElement(dragImage, ITEM_MODIFIED_CLASS); diff --git a/packages/htmlviewer-extension/src/index.tsx b/packages/htmlviewer-extension/src/index.tsx index f09ea64e07ad..40e840d6aa0c 100644 --- a/packages/htmlviewer-extension/src/index.tsx +++ b/packages/htmlviewer-extension/src/index.tsx @@ -48,7 +48,7 @@ function activateHTMLViewer( restorer: ILayoutRestorer | null ): IHTMLViewerTracker { // Add an HTML file type to the docregistry. - const ft = new DocumentRegistry.FileType({ + const ft: DocumentRegistry.IFileType = { name: 'html', contentType: 'file', fileFormat: 'text', @@ -56,7 +56,7 @@ function activateHTMLViewer( extensions: ['.html'], mimeTypes: ['text/html'], icon: html5Icon - }); + }; app.docRegistry.addFileType(ft); // Create a new viewer factory. diff --git a/packages/markdownviewer/src/widget.ts b/packages/markdownviewer/src/widget.ts index 685242c227f1..535cd99b9613 100644 --- a/packages/markdownviewer/src/widget.ts +++ b/packages/markdownviewer/src/widget.ts @@ -294,11 +294,8 @@ export class MarkdownViewerFactory extends ABCWidgetFactory { */ constructor(options: MarkdownViewerFactory.IOptions) { super(Private.createRegistryOptions(options)); + this._fileType = options.primaryFileType; this._rendermime = options.rendermime; - - // resolve the passed in IFileType to a FileType - const ft = options.primaryFileType; - this._fileType = ft ? DocumentRegistry.FileType.resolve(ft) : ft; } /** @@ -320,7 +317,7 @@ export class MarkdownViewerFactory extends ABCWidgetFactory { return widget; } - private _fileType: DocumentRegistry.FileType | undefined; + private _fileType: DocumentRegistry.IFileType | undefined; private _rendermime: IRenderMimeRegistry; } From 9e51d8fb8d7d605b882da7cb2f2c072f37db9369 Mon Sep 17 00:00:00 2001 From: telamonian Date: Sat, 25 Jan 2020 23:38:03 -0500 Subject: [PATCH 36/72] bind passed render/unrender funcs in JLIcon constructor --- packages/ui-components/src/icon/jlicon.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui-components/src/icon/jlicon.tsx b/packages/ui-components/src/icon/jlicon.tsx index c478f6e729f4..717321419720 100644 --- a/packages/ui-components/src/icon/jlicon.tsx +++ b/packages/ui-components/src/icon/jlicon.tsx @@ -205,8 +205,8 @@ export class JLIcon implements JLIcon.IJLIcon, JLIcon.IRenderer { this.react = this._initReact(); if (render && unrender) { - this.render = render; - this.unrender = unrender; + this.render = render.bind(this); + this.unrender = unrender.bind(this); } else { // set render and unrender methods based on the supplied rendererClass const renderer = new rendererClass(this); From 8964804d298ac87cc06e71185f57e87018462d53 Mon Sep 17 00:00:00 2001 From: telamonian Date: Sun, 26 Jan 2020 00:00:40 -0500 Subject: [PATCH 37/72] minor cleanup of ensureUiComponents func --- buildutils/src/ensure-package.ts | 50 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/buildutils/src/ensure-package.ts b/buildutils/src/ensure-package.ts index c2d68522fa3e..93e4e8cb8eab 100644 --- a/buildutils/src/ensure-package.ts +++ b/buildutils/src/ensure-package.ts @@ -24,10 +24,10 @@ const ICON_IMPORTS_TEMPLATE = ` import { JLIcon } from './jlicon'; // icon svg import statements -{{iconImportStatements}} +{{svgImportStatements}} // JLIcon instance construction -{{jliconConstruction}} +{{jliconConstructions}} `; const ICON_CSS_CLASSES_TEMPLATE = ` @@ -358,47 +358,47 @@ export async function ensureUiComponents( const pkgName = utils.stem(pkgPath); let messages: string[] = []; - const svgs = glob.sync(path.join(pkgPath, 'style/icons', '**/*.svg')); + const svgPaths = glob.sync(path.join(pkgPath, 'style/icons', '**/*.svg')); /* support for glob import of icon svgs */ const iconSrcDir = path.join(pkgPath, 'src/icon'); // build the per-icon import code - let _iconImportStatements: string[] = []; - let _jliconConstruction: string[] = []; - svgs.forEach(svg => { - const name = utils.stem(svg); - const svgpath = path - .relative(iconSrcDir, svg) + let _svgImportStatements: string[] = []; + let _jliconConstructions: string[] = []; + svgPaths.forEach(svgPath => { + const svgName = utils.stem(svgPath); + const svgImportPath = path + .relative(iconSrcDir, svgPath) .split(path.sep) .join('/'); - const svgname = utils.camelCase(name) + 'Svg'; - const iconname = utils.camelCase(name) + 'Icon'; - const qualname = [pkgName, utils.stem(svg)].join(':'); + const svgstrRef = utils.camelCase(svgName) + 'Svg'; + const iconRef = utils.camelCase(svgName) + 'Icon'; + const iconName = [pkgName, utils.stem(svgPath)].join(':'); if (dorequire) { // load the icon svg using `require` - _jliconConstruction.push( - `export const ${iconname} = new JLIcon({ name: '${qualname}', svgstr: require('${svgpath}').default });` + _jliconConstructions.push( + `export const ${iconRef} = new JLIcon({ name: '${iconName}', svgstr: require('${svgImportPath}').default });` ); } else { // load the icon svg using `import` - _iconImportStatements.push(`import ${svgname} from '${svgpath}';`); + _svgImportStatements.push(`import ${svgstrRef} from '${svgImportPath}';`); - _jliconConstruction.push( - `export const ${iconname} = new JLIcon({ name: '${qualname}', svgstr: ${svgname} });` + _jliconConstructions.push( + `export const ${iconRef} = new JLIcon({ name: '${iconName}', svgstr: ${svgstrRef} });` ); } }); - const iconImportStatements = _iconImportStatements.join('\n'); - const jliconConstruction = _jliconConstruction.join('\n'); + const svgImportStatements = _svgImportStatements.join('\n'); + const jliconConstructions = _jliconConstructions.join('\n'); // generate the actual contents of the iconImports file const iconImportsPath = path.join(iconSrcDir, 'iconimports.ts'); const iconImportsContents = utils.fromTemplate( HEADER_TEMPLATE + ICON_IMPORTS_TEMPLATE, - { funcName, iconImportStatements, jliconConstruction } + { funcName, svgImportStatements, jliconConstructions } ); messages.push(...ensureFile(iconImportsPath, iconImportsContents, false)); @@ -408,14 +408,14 @@ export async function ensureUiComponents( // build the per-icon import code let _iconCSSUrls: string[] = []; let _iconCSSDeclarations: string[] = []; - svgs.forEach(svg => { - const name = utils.stem(svg); - const urlName = 'jp-icon-' + name; - const className = 'jp-' + utils.camelCase(name, true) + 'Icon'; + svgPaths.forEach(svgPath => { + const svgName = utils.stem(svgPath); + const urlName = 'jp-icon-' + svgName; + const className = 'jp-' + utils.camelCase(svgName, true) + 'Icon'; _iconCSSUrls.push( `--${urlName}: url('${path - .relative(iconCSSDir, svg) + .relative(iconCSSDir, svgPath) .split(path.sep) .join('/')}');` ); From 554a73ea61c21378cdeff6c4656e647d491dd03b Mon Sep 17 00:00:00 2001 From: telamonian Date: Sun, 26 Jan 2020 09:02:30 -0500 Subject: [PATCH 38/72] change all refs to iconRenderer => icon across codebase - exception: refs to title.iconRenderer, where title is a Lumino Title obj, were left as-is --- packages/apputils/src/toolbar.tsx | 10 +++++----- packages/extensionmanager/src/widget.tsx | 4 ++-- packages/filebrowser-extension/src/index.ts | 2 +- packages/filebrowser/src/browser.ts | 4 ++-- packages/filebrowser/src/upload.ts | 2 +- packages/htmlviewer/src/index.tsx | 2 +- packages/notebook/src/default-toolbar.tsx | 12 ++++++------ packages/running-extension/src/index.ts | 2 +- packages/running/src/index.tsx | 8 ++++---- packages/terminal-extension/src/index.ts | 2 +- packages/ui-components/src/components/htmlselect.tsx | 6 +++--- 11 files changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/apputils/src/toolbar.tsx b/packages/apputils/src/toolbar.tsx index 75d4bc441750..1ba228ddba28 100644 --- a/packages/apputils/src/toolbar.tsx +++ b/packages/apputils/src/toolbar.tsx @@ -367,7 +367,7 @@ export namespace Toolbar { sessionContext: ISessionContext ): Widget { return new ToolbarButton({ - iconRenderer: stopIcon, + icon: stopIcon, onClick: () => { void sessionContext.session?.kernel?.interrupt(); }, @@ -383,7 +383,7 @@ export namespace Toolbar { dialogs?: ISessionContext.IDialogs ): Widget { return new ToolbarButton({ - iconRenderer: refreshIcon, + icon: refreshIcon, onClick: () => { void (dialogs ?? sessionContextDialogs).restart(sessionContext); }, @@ -450,7 +450,7 @@ export namespace ToolbarButtonComponent { label?: string; iconClass?: string; iconLabel?: string; - iconRenderer?: JLIcon; + icon?: JLIcon; tooltip?: string; onClick?: () => void; enabled?: boolean; @@ -483,9 +483,9 @@ export function ToolbarButtonComponent(props: ToolbarButtonComponent.IProps) { }; const Icon = () => { - if (props.iconRenderer) { + if (props.icon) { return ( -
{ this.handleCollapse(); @@ -542,7 +542,7 @@ export class ExtensionView extends VDomRenderer { headerElements={ { model.refreshInstalled(); }} diff --git a/packages/filebrowser-extension/src/index.ts b/packages/filebrowser-extension/src/index.ts index 39616f5009d1..41fd536da9ac 100644 --- a/packages/filebrowser-extension/src/index.ts +++ b/packages/filebrowser-extension/src/index.ts @@ -235,7 +235,7 @@ async function activateFactory( // Add a launcher toolbar item. let launcher = new ToolbarButton({ - iconRenderer: addIcon, + icon: addIcon, onClick: () => { return Private.createLauncher(commands, widget); }, diff --git a/packages/filebrowser/src/browser.ts b/packages/filebrowser/src/browser.ts index 21cb7072b119..25351ec82966 100644 --- a/packages/filebrowser/src/browser.ts +++ b/packages/filebrowser/src/browser.ts @@ -69,7 +69,7 @@ export class FileBrowser extends Widget { this._directoryPending = false; const newFolder = new ToolbarButton({ - iconRenderer: newFolderIcon, + icon: newFolderIcon, onClick: () => { this.createNewDirectory(); }, @@ -77,7 +77,7 @@ export class FileBrowser extends Widget { }); const uploader = new Uploader({ model }); const refresher = new ToolbarButton({ - iconRenderer: refreshIcon, + icon: refreshIcon, onClick: () => { void model.refresh(); }, diff --git a/packages/filebrowser/src/upload.ts b/packages/filebrowser/src/upload.ts index b89eb7e14ade..6d5f312b976f 100644 --- a/packages/filebrowser/src/upload.ts +++ b/packages/filebrowser/src/upload.ts @@ -15,7 +15,7 @@ export class Uploader extends ToolbarButton { */ constructor(options: Uploader.IOptions) { super({ - iconRenderer: fileUploadIcon, + icon: fileUploadIcon, onClick: () => { this._input.click(); }, diff --git a/packages/htmlviewer/src/index.tsx b/packages/htmlviewer/src/index.tsx index 87934cca674c..76f9aa825e0c 100644 --- a/packages/htmlviewer/src/index.tsx +++ b/packages/htmlviewer/src/index.tsx @@ -89,7 +89,7 @@ export class HTMLViewer extends DocumentWidget