diff --git a/buildutils/package.json b/buildutils/package.json index efd55c58e117..eaae3a4f49b5 100644 --- a/buildutils/package.json +++ b/buildutils/package.json @@ -49,6 +49,7 @@ "mini-css-extract-plugin": "~0.6.0", "package-json": "^6.3.0", "path": "~0.12.7", + "prettier": "^1.18.2", "semver": "^6.1.0", "sort-package-json": "~1.22.1", "typescript": "~3.5.1", @@ -60,6 +61,7 @@ "@types/inquirer": "^6.0.3", "@types/mini-css-extract-plugin": "^0.2.0", "@types/node": "^12.0.2", + "@types/prettier": "^1.16.4", "@types/webpack": "^4.4.32", "rimraf": "~2.6.2" } diff --git a/buildutils/src/build.ts b/buildutils/src/build.ts index 2e14db75aab3..de622b45a756 100644 --- a/buildutils/src/build.ts +++ b/buildutils/src/build.ts @@ -183,10 +183,7 @@ export namespace Build { }, { test: /\.svg/, - use: [ - { loader: 'svg-url-loader', options: {} }, - { loader: 'svgo-loader', options: { plugins: [] } } - ] + use: [{ loader: 'svg-url-loader', options: { encoding: 'none' } }] }, { test: /\.(png|jpg|gif|ttf|woff|woff2|eot)(\?v=[0-9]\.[0-9]\.[0-9])?$/, diff --git a/buildutils/src/ensure-package.ts b/buildutils/src/ensure-package.ts index 1c4e4991b1d6..2e007c8f972f 100644 --- a/buildutils/src/ensure-package.ts +++ b/buildutils/src/ensure-package.ts @@ -6,17 +6,48 @@ import * as fs from 'fs-extra'; import * as glob from 'glob'; import * as path from 'path'; +import * as prettier from 'prettier'; import * as ts from 'typescript'; import { getDependency } from './get-dependency'; import * as utils from './utils'; -const CSS_HEADER = ` +const HEADER_TEMPLATE = ` /*----------------------------------------------------------------------------- | Copyright (c) Jupyter Development Team. | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ -/* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ +/* This file was auto-generated by {{funcName}}() in @jupyterlab/buildutils */ +`; + +const ICON_IMPORTS_TEMPLATE = ` +import { Icon } from './interfaces'; + +// icon svg import statements +{{iconImportStatements}} + +// defaultIcons definition +export namespace IconImports { + export const defaultIcons: ReadonlyArray = [ + {{iconModelDeclarations}} + ]; +} +`; + +const ICON_CSS_CLASSES_TEMPLATE = ` +/** + * (DEPRECATED) Support for consuming icons as CSS background images + */ + +/* Icons urls */ + +:root { + {{iconCSSUrls}} +} + +/* Icon CSS class declarations */ + +{{iconCSSDeclarations}} `; /** @@ -138,17 +169,20 @@ export async function ensurePackage( // Template the CSS index file. if (cssImports && fs.existsSync(path.join(pkgPath, 'style/base.css'))) { - let cssIndex = CSS_HEADER.trim(); + const funcName = 'ensurePackage'; + let cssIndexContents = utils.fromTemplate( + HEADER_TEMPLATE, + { funcName }, + { end: '' } + ); cssImports.forEach(cssImport => { - cssIndex += `\n@import url('~${cssImport}');`; + cssIndexContents += `\n@import url('~${cssImport}');`; }); - cssIndex += "\n\n@import url('./base.css');\n"; - const cssPath = path.join(pkgPath, 'style/index.css'); - const prev = fs.readFileSync(cssPath, { encoding: 'utf8' }); - if (prev !== cssIndex) { - messages.push(`Updated ./${data.style}`); - fs.writeFileSync(cssPath, cssIndex); - } + cssIndexContents += "\n\n@import url('./base.css');\n"; + + // write out cssIndexContents, if needed + const cssIndexPath = path.join(pkgPath, 'style/index.css'); + messages.push(...ensureFile(cssIndexPath, cssIndexContents, false)); } // Look for unused packages @@ -290,6 +324,78 @@ export async function ensurePackage( return messages; } +/** + * An extra ensure function just for the @jupyterlab/ui-components package. + * Ensures that the icon svg import statements are synced with the contents + * of ui-components/style/icons. + * + * @param pkgPath - The path to the @jupyterlab/ui-components package. + * + * @returns A list of changes that were made to ensure the package. + */ +export async function ensureUiComponents(pkgPath: string): Promise { + const funcName = 'ensureUiComponents'; + let messages: string[] = []; + + const svgs = 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 _iconModelDeclarations: string[] = []; + svgs.forEach(svg => { + const name = utils.stem(svg); + const nameCamel = utils.camelCase(name) + 'Svg'; + _iconImportStatements.push( + `import ${nameCamel} from '${path.relative(iconSrcDir, svg)}';` + ); + _iconModelDeclarations.push(`{ name: '${name}', svg: ${nameCamel} }`); + }); + const iconImportStatements = _iconImportStatements.join('\n'); + const iconModelDeclarations = _iconModelDeclarations.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, iconModelDeclarations } + ); + messages.push(...ensureFile(iconImportsPath, iconImportsContents)); + + /* support for deprecated icon CSS classes */ + const iconCSSDir = path.join(pkgPath, 'style'); + + // 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'; + + _iconCSSUrls.push( + `--${urlName}: url('${path.relative(iconCSSDir, svg)}');` + ); + _iconCSSDeclarations.push( + `.${className} {background-image: var(--${urlName})}` + ); + }); + const iconCSSUrls = _iconCSSUrls.join('\n'); + const iconCSSDeclarations = _iconCSSDeclarations.join('\n'); + + // generate the actual contents of the iconCSSClasses file + const iconCSSClassesPath = path.join(iconCSSDir, 'deprecated.css'); + const iconCSSClassesContent = utils.fromTemplate( + HEADER_TEMPLATE + ICON_CSS_CLASSES_TEMPLATE, + { funcName, iconCSSUrls, iconCSSDeclarations } + ); + messages.push(...ensureFile(iconCSSClassesPath, iconCSSClassesContent)); + + return messages; +} + /** * The options used to ensure a package. */ @@ -340,6 +446,53 @@ export interface IEnsurePackageOptions { differentVersions?: string[]; } +/** + * Ensure that contents of a file match a supplied string. If they do match, + * do nothing and return an empty array. If they don't match, overwrite the + * file and return an array with an update message. + * + * @param path: The path to the file being checked. The file must exist, + * or else this function does nothing. + * + * @param contents: The desired file contents. + * + * @param prettify: default = true. If true, format the contents with + * `prettier` before comparing/writing. Set to false only if you already + * know your code won't be modified later by the `prettier` git commit hook. + * + * @returns a string array with 0 or 1 messages. + */ +function ensureFile( + path: string, + contents: string, + prettify: boolean = true +): string[] { + let messages: string[] = []; + + if (!fs.existsSync(path)) { + // bail + messages.push( + `Tried to ensure the contents of ./${path}, but the file does not exist` + ); + return messages; + } + + // run the newly generated contents through prettier before comparing + if (prettify) { + contents = prettier.format(contents, { filepath: path, singleQuote: true }); + } + + const prev = fs.readFileSync(path, { + encoding: 'utf8' + }); + if (prev !== contents) { + fs.writeFileSync(path, contents); + messages.push(`Updated ./${path}`); + } + + return messages; +} + /** * Extract the module imports from a TypeScript source file. * diff --git a/buildutils/src/ensure-repo.ts b/buildutils/src/ensure-repo.ts index b02fa34df2ba..425c502eb521 100644 --- a/buildutils/src/ensure-repo.ts +++ b/buildutils/src/ensure-repo.ts @@ -13,7 +13,11 @@ */ import * as path from 'path'; import * as utils from './utils'; -import { ensurePackage, IEnsurePackageOptions } from './ensure-package'; +import { + ensurePackage, + ensureUiComponents, + IEnsurePackageOptions +} from './ensure-package'; type Dict = { [key: string]: T }; @@ -341,6 +345,16 @@ export async function ensureIntegrity(): Promise { } } + // ensure the icon svg imports + pkgMessages = await ensureUiComponents(pkgPaths['@jupyterlab/ui-components']); + if (pkgMessages.length > 0) { + let pkgName = '@jupyterlab/ui-components'; + if (!messages[pkgName]) { + messages[pkgName] = []; + } + messages[pkgName] = messages[pkgName].concat(pkgMessages); + } + // Handle the top level package. let corePath = path.resolve('.', 'package.json'); let coreData: any = utils.readJSONFile(corePath); diff --git a/buildutils/src/utils.ts b/buildutils/src/utils.ts index 504a9c8e5971..1d7535d93e29 100644 --- a/buildutils/src/utils.ts +++ b/buildutils/src/utils.ts @@ -114,6 +114,54 @@ export function writeJSONFile(filePath: string, data: any): boolean { return false; } +/** + * Simple template substitution for template vars of the form {{name}} + * + * @param templ: the template string. + * Ex: `This header generated by {{funcName}}` + * + * @param subs: an object in which the parameter keys are the template + * variables and the parameter values are the substitutions. + * + * @param options: function options. + * + * @param options.autoindent: default = true. If true, will try to match + * indentation level of {{var}} in substituted template. + * + * @param options.end: default = '\n'. Inserted at the end of + * a template post-substitution and post-trim. + * + * @returns the input template with all {{vars}} substituted, then `.trim`-ed. + */ +export function fromTemplate( + templ: string, + subs: Dict, + options: { autoindent?: boolean; end?: string } = {} +) { + // default options values + const autoindent = + options.autoindent === undefined ? true : options.autoindent; + const end = options.end === undefined ? '\n' : options.end; + + Object.keys(subs).forEach(key => { + const val = subs[key]; + + if (autoindent) { + // try to match the indentation level of the {{var}} in the input template. + templ = templ.split(`{{${key}}}`).reduce((acc, cur) => { + // Regex: 0 or more non-newline whitespaces followed by end of string + let indentRe = acc.match(/([^\S\r\n]*).*$/); + let indent = indentRe ? indentRe[1] : ''; + return acc + val.split('\n').join('\n' + indent) + cur; + }); + } else { + templ = templ.split(`{{${key}}}`).join(val); + } + }); + + return templ.trim() + end; +} + /** * * Call a command, checking its status. @@ -286,3 +334,43 @@ export function ensureUnixPathSep(source: string) { } return source.replace(backSlash, '/'); } + +/** + * Get the last portion of a path, without its extension (if any). + * + * @param path - The file path. + * + * @returns the last part of the path, sans extension. + */ +export function stem(path: string): string { + return path + .split('\\') + .pop() + .split('/') + .pop() + .split('.') + .shift(); +} + +/** + * Given a 'snake-case', 'snake_case', or 'snake case' string, + * will return the camel case version: 'snakeCase'. + * + * @param str: the snake-case input string. + * + * @param upper: default = false. If true, the first letter of the + * returned string will be capitalized. + * + * @returns the camel case version of the input string. + */ +export function camelCase(str: string, upper: boolean = false): string { + return str.replace(/(?:^\w|[A-Z]|\b\w|\s+|-+|_+)/g, function(match, index) { + if (+match === 0 || match[0] === '-') { + return ''; + } else if (index === 0 && !upper) { + return match.toLowerCase(); + } else { + return match.toUpperCase(); + } + }); +} diff --git a/dev_mode/imports.css b/dev_mode/imports.css index 47f1c8393e13..4b5aab968a1c 100644 --- a/dev_mode/imports.css +++ b/dev_mode/imports.css @@ -31,6 +31,7 @@ @import url('~@jupyterlab/tabmanager-extension/style/index.css'); @import url('~@jupyterlab/terminal-extension/style/index.css'); @import url('~@jupyterlab/tooltip-extension/style/index.css'); +@import url('~@jupyterlab/ui-components-extension/style/index.css'); @import url('~@jupyterlab/vdom-extension/style/index.css'); @import url('~@jupyterlab/vega4-extension/style/index.css'); @import url('~@jupyterlab/vega5-extension/style/index.css'); diff --git a/dev_mode/package.json b/dev_mode/package.json index ae0b22b451d3..c7bbeea24c55 100644 --- a/dev_mode/package.json +++ b/dev_mode/package.json @@ -51,6 +51,7 @@ "@jupyterlab/theme-dark-extension": "~1.1.0-alpha.1", "@jupyterlab/theme-light-extension": "~1.1.0-alpha.1", "@jupyterlab/tooltip-extension": "~1.1.0-alpha.1", + "@jupyterlab/ui-components-extension": "~1.1.0-alpha.1", "@jupyterlab/vdom-extension": "~1.1.0-alpha.1", "@jupyterlab/vega4-extension": "~1.1.0-alpha.1", "@jupyterlab/vega5-extension": "~1.1.0-alpha.1" @@ -155,6 +156,7 @@ "@jupyterlab/tooltip": "~1.1.0-alpha.1", "@jupyterlab/tooltip-extension": "~1.1.0-alpha.1", "@jupyterlab/ui-components": "~1.1.0-alpha.1", + "@jupyterlab/ui-components-extension": "~1.1.0-alpha.1", "@jupyterlab/vdom": "~1.1.0-alpha.1", "@jupyterlab/vdom-extension": "~1.1.0-alpha.1", "@jupyterlab/vega4-extension": "~1.1.0-alpha.1", @@ -210,6 +212,7 @@ "@jupyterlab/theme-dark-extension": "", "@jupyterlab/theme-light-extension": "", "@jupyterlab/tooltip-extension": "", + "@jupyterlab/ui-components-extension": "", "@jupyterlab/vdom-extension": "" }, "mimeExtensions": { @@ -333,6 +336,7 @@ "@jupyterlab/tooltip": "../packages/tooltip", "@jupyterlab/tooltip-extension": "../packages/tooltip-extension", "@jupyterlab/ui-components": "../packages/ui-components", + "@jupyterlab/ui-components-extension": "../packages/ui-components-extension", "@jupyterlab/vdom": "../packages/vdom", "@jupyterlab/vdom-extension": "../packages/vdom-extension", "@jupyterlab/vega4-extension": "../packages/vega4-extension", diff --git a/dev_mode/webpack.config.js b/dev_mode/webpack.config.js index 78c1769a515d..5a2612582ddf 100644 --- a/dev_mode/webpack.config.js +++ b/dev_mode/webpack.config.js @@ -189,8 +189,22 @@ module.exports = [ }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, use: 'file-loader' }, { + // in css files, svg is loaded as a url formatted string test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - use: 'url-loader?limit=10000&mimetype=image/svg+xml' + issuer: { test: /\.css$/ }, + use: { + loader: 'svg-url-loader', + options: { encoding: 'none', limit: 10000 } + } + }, + { + // in ts and tsx files (both of which compile to js), + // svg is loaded as a raw string + test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, + issuer: { test: /\.js$/ }, + use: { + loader: 'raw-loader' + } } ] }, diff --git a/examples/app/index.js b/examples/app/index.js index 4b340f301721..aa9bf195b5d0 100644 --- a/examples/app/index.js +++ b/examples/app/index.js @@ -39,7 +39,8 @@ window.addEventListener('load', async function() { require('@jupyterlab/terminal-extension'), require('@jupyterlab/theme-dark-extension'), require('@jupyterlab/theme-light-extension'), - require('@jupyterlab/tooltip-extension') + require('@jupyterlab/tooltip-extension'), + require('@jupyterlab/ui-components-extension') ]; var lab = new JupyterLab(); lab.registerPluginModules(mods); diff --git a/examples/filebrowser/package.json b/examples/filebrowser/package.json index 1bf960f1e249..9b2d960bae55 100644 --- a/examples/filebrowser/package.json +++ b/examples/filebrowser/package.json @@ -16,6 +16,7 @@ "@jupyterlab/fileeditor": "^1.1.0-alpha.1", "@jupyterlab/services": "^4.1.0-alpha.1", "@jupyterlab/theme-light-extension": "^1.1.0-alpha.1", + "@jupyterlab/ui-components": "^1.1.0-alpha.1", "@phosphor/algorithm": "^1.1.3", "@phosphor/commands": "^1.6.3", "@phosphor/widgets": "^1.8.0", diff --git a/examples/filebrowser/src/index.ts b/examples/filebrowser/src/index.ts index fd22a47a41c9..d93c4e493a64 100644 --- a/examples/filebrowser/src/index.ts +++ b/examples/filebrowser/src/index.ts @@ -21,19 +21,21 @@ import { ServiceManager } from '@jupyterlab/services'; import { Dialog, ToolbarButton, showDialog } from '@jupyterlab/apputils'; -import { FileBrowser, FileBrowserModel } from '@jupyterlab/filebrowser'; +import { + CodeMirrorEditorFactory, + CodeMirrorMimeTypeService +} from '@jupyterlab/codemirror'; import { DocumentManager } from '@jupyterlab/docmanager'; import { DocumentRegistry } from '@jupyterlab/docregistry'; -import { - CodeMirrorEditorFactory, - CodeMirrorMimeTypeService -} from '@jupyterlab/codemirror'; +import { FileBrowser, FileBrowserModel } from '@jupyterlab/filebrowser'; import { FileEditorFactory } from '@jupyterlab/fileeditor'; +import { defaultIconRegistry } from '@jupyterlab/ui-components'; + function main(): void { let manager = new ServiceManager(); void manager.ready.then(() => { @@ -85,7 +87,10 @@ function createApp(manager: ServiceManager.IManager): void { let commands = new CommandRegistry(); - let fbModel = new FileBrowserModel({ manager: docManager }); + let fbModel = new FileBrowserModel({ + manager: docManager, + iconRegistry: defaultIconRegistry + }); let fbWidget = new FileBrowser({ id: 'filebrowser', model: fbModel diff --git a/jupyterlab/staging/webpack.config.js b/jupyterlab/staging/webpack.config.js index 78c1769a515d..5a2612582ddf 100644 --- a/jupyterlab/staging/webpack.config.js +++ b/jupyterlab/staging/webpack.config.js @@ -189,8 +189,22 @@ module.exports = [ }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, use: 'file-loader' }, { + // in css files, svg is loaded as a url formatted string test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - use: 'url-loader?limit=10000&mimetype=image/svg+xml' + issuer: { test: /\.css$/ }, + use: { + loader: 'svg-url-loader', + options: { encoding: 'none', limit: 10000 } + } + }, + { + // in ts and tsx files (both of which compile to js), + // svg is loaded as a raw string + test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, + issuer: { test: /\.js$/ }, + use: { + loader: 'raw-loader' + } } ] }, diff --git a/package.json b/package.json index 0790ee3d7fbd..98273ec575d3 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "lerna": "^3.13.2", "lint-staged": "^8.1.5", "open-cli": "^5.0.0", - "prettier": "^1.17.0", + "prettier": "^1.18.2", "tslint": "^5.15.0", "tslint-config-prettier": "^1.18.0", "tslint-plugin-prettier": "^2.0.1", diff --git a/packages/application/package.json b/packages/application/package.json index cc8c7853c8b4..f1a0ee521bda 100644 --- a/packages/application/package.json +++ b/packages/application/package.json @@ -42,6 +42,7 @@ "@jupyterlab/rendermime": "^1.1.0-alpha.1", "@jupyterlab/rendermime-interfaces": "^1.4.0-alpha.1", "@jupyterlab/services": "^4.1.0-alpha.1", + "@jupyterlab/ui-components": "^1.1.0-alpha.1", "@phosphor/algorithm": "^1.1.3", "@phosphor/application": "^1.6.3", "@phosphor/commands": "^1.6.3", diff --git a/packages/application/src/shell.ts b/packages/application/src/shell.ts index 25d3f821849b..ca9778a0cdae 100644 --- a/packages/application/src/shell.ts +++ b/packages/application/src/shell.ts @@ -5,6 +5,8 @@ import { Debouncer } from '@jupyterlab/coreutils'; import { DocumentRegistry } from '@jupyterlab/docregistry'; +import { DockPanelSvg, TabBarSvg } from '@jupyterlab/ui-components'; + import { ArrayExt, find, IIterator, iter, toArray } from '@phosphor/algorithm'; import { PromiseDelegate, Token } from '@phosphor/coreutils'; @@ -176,7 +178,9 @@ export class LabShell extends Widget implements JupyterFrontEnd.IShell { let bottomPanel = (this._bottomPanel = new BoxPanel()); let topPanel = (this._topPanel = new Panel()); let hboxPanel = new BoxPanel(); - let dockPanel = (this._dockPanel = new DockPanel()); + let dockPanel = (this._dockPanel = new DockPanelSvg({ + kind: 'dockPanelBar' + })); let headerPanel = (this._headerPanel = new Panel()); MessageLoop.installMessageHook(dockPanel, this._dockChildHook); @@ -1028,7 +1032,8 @@ namespace Private { * Construct a new side bar handler. */ constructor(side: string) { - this._sideBar = new TabBar({ + this._sideBar = new TabBarSvg({ + kind: 'sideBar', insertBehavior: 'none', removeBehavior: 'none', allowDeselect: true diff --git a/packages/application/style/icons.css b/packages/application/style/icons.css index 3b59b877e285..a21e91197379 100644 --- a/packages/application/style/icons.css +++ b/packages/application/style/icons.css @@ -3,6 +3,204 @@ | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ +/** + * Support for icons as inline SVG HTMLElements + */ + +/* recolor the primary elements of an icon */ +.jp-icon0[fill] { + fill: var(--jp-inverse-layout-color0); +} +.jp-icon1[fill] { + fill: var(--jp-inverse-layout-color1); +} +.jp-icon2[fill] { + fill: var(--jp-inverse-layout-color2); +} +.jp-icon3[fill] { + fill: var(--jp-inverse-layout-color3); +} +.jp-icon4[fill] { + fill: var(--jp-inverse-layout-color4); +} + +.jp-icon0[stroke] { + stroke: var(--jp-inverse-layout-color0); +} +.jp-icon1[stroke] { + stroke: var(--jp-inverse-layout-color1); +} +.jp-icon2[stroke] { + stroke: var(--jp-inverse-layout-color2); +} +.jp-icon3[stroke] { + stroke: var(--jp-inverse-layout-color3); +} +.jp-icon4[stroke] { + stroke: var(--jp-inverse-layout-color4); +} + +/* recolor the accent elements of an icon */ +.jp-icon-accent0[fill] { + fill: var(--jp-layout-color0); +} +.jp-icon-accent1[fill] { + fill: var(--jp-layout-color1); +} +.jp-icon-accent2[fill] { + fill: var(--jp-layout-color2); +} +.jp-icon-accent3[fill] { + fill: var(--jp-layout-color3); +} +.jp-icon-accent4[fill] { + fill: var(--jp-layout-color4); +} + +.jp-icon-accent0[stroke] { + stroke: var(--jp-layout-color0); +} +.jp-icon-accent1[stroke] { + stroke: var(--jp-layout-color1); +} +.jp-icon-accent2[stroke] { + stroke: var(--jp-layout-color2); +} +.jp-icon-accent3[stroke] { + stroke: var(--jp-layout-color3); +} +.jp-icon-accent4[stroke] { + stroke: var(--jp-layout-color4); +} + +/* brand icon colors. Same for light and dark */ +.jp-icon-brand0[fill] { + fill: var(--jp-brand-color0); +} +.jp-icon-brand1[fill] { + fill: var(--jp-brand-color1); +} +.jp-icon-brand2[fill] { + fill: var(--jp-brand-color2); +} +.jp-icon-brand3[fill] { + fill: var(--jp-brand-color3); +} +.jp-icon-brand4[fill] { + fill: var(--jp-brand-color4); +} + +.jp-icon-brand0[stroke] { + stroke: var(--jp-brand-color0); +} +.jp-icon-brand1[stroke] { + stroke: var(--jp-brand-color1); +} +.jp-icon-brand2[stroke] { + stroke: var(--jp-brand-color2); +} +.jp-icon-brand3[stroke] { + stroke: var(--jp-brand-color3); +} +.jp-icon-brand4[stroke] { + stroke: var(--jp-brand-color4); +} + +/* warn icon colors. Same for light and dark */ +.jp-icon-warn0[fill] { + fill: var(--jp-warn-color0); +} +.jp-icon-warn1[fill] { + fill: var(--jp-warn-color1); +} +.jp-icon-warn2[fill] { + fill: var(--jp-warn-color2); +} +.jp-icon-warn3[fill] { + fill: var(--jp-warn-color3); +} + +.jp-icon-warn0[stroke] { + stroke: var(--jp-warn-color0); +} +.jp-icon-warn1[stroke] { + stroke: var(--jp-warn-color1); +} +.jp-icon-warn2[stroke] { + stroke: var(--jp-warn-color2); +} +.jp-icon-warn3[stroke] { + stroke: var(--jp-warn-color3); +} + +/* icon colors that contrast well with each other and most backgrounds */ +.jp-icon-contrast0[fill] { + fill: var(--jp-icon-contrast-color0); +} +.jp-icon-contrast1[fill] { + fill: var(--jp-icon-contrast-color1); +} +.jp-icon-contrast2[fill] { + fill: var(--jp-icon-contrast-color2); +} +.jp-icon-contrast3[fill] { + fill: var(--jp-icon-contrast-color3); +} + +.jp-icon-contrast0[stroke] { + stroke: var(--jp-icon-contrast-color0); +} +.jp-icon-contrast1[stroke] { + stroke: var(--jp-icon-contrast-color1); +} +.jp-icon-contrast2[stroke] { + stroke: var(--jp-icon-contrast-color2); +} +.jp-icon-contrast3[stroke] { + stroke: var(--jp-icon-contrast-color3); +} + +/* CSS for icons in selected items in the settings editor */ +#setting-editor .jp-PluginList .jp-mod-selected .jp-icon-selectable[fill] { + fill: white; +} + +#setting-editor + .jp-PluginList + .jp-mod-selected + .jp-icon-selectable-inverse[fill] { + fill: var(--jp-brand-color1); +} + +/* CSS for icons in selected filebrowser listing items */ +.jp-DirListing-item.jp-mod-selected .jp-icon-selectable[fill] { + fill: white; +} + +.jp-DirListing-item.jp-mod-selected .jp-icon-selectable-inverse[fill] { + fill: var(--jp-brand-color1); +} + +/* CSS for icons in selected tabs in the sidebar tab manager */ +#tab-manager .p-TabBar-tab.jp-mod-active .jp-icon-selectable[fill] { + fill: white; +} + +#tab-manager .p-TabBar-tab.jp-mod-active .jp-icon-selectable-inverse[fill] { + fill: var(--jp-brand-color1); +} + +/* special handling for splash icon CSS. While the theme CSS reloads during + splash, the splash icon can loose theming. To prevent that, we set a + default for its color variable */ +:root { + --jp-warn-color0: var(--md-orange-700); +} + +/** + * (DEPRECATED) Support for icons as CSS `background-images` + */ + .jp-MaterialIcon { min-width: 16px; min-height: 16px; @@ -95,12 +293,8 @@ background-image: var(--jp-icon-ellipses); } -.jp-ExtensionIcon { - background-image: var(--jp-icon-extension); -} - -.jp-FileIcon { - background-image: var(--jp-icon-file); +.jp-FileUploadIcon { + background-image: var(--jp-icon-file-upload); } .jp-FilledCircleIcon { @@ -111,10 +305,6 @@ background-image: var(--jp-icon-filter-list); } -.jp-FolderIcon { - background-image: var(--jp-icon-folder); -} - .jp-HomeIcon { background-image: var(--jp-icon-home); min-width: 16px; @@ -123,14 +313,6 @@ vertical-align: sub; } -.jp-ImageIcon { - background-image: var(--jp-icon-image); -} - -.jp-JSONIcon { - background-image: var(--jp-icon-json); -} - .jp-JupyterIcon { background-image: var(--jp-image-jupyter); background-size: 16px; @@ -140,10 +322,6 @@ padding-right: 0px; } -.jp-KernelIcon { - background-image: var(--jp-icon-kernel); -} - .jp-KeyboardIcon { background-image: var(--jp-icon-keyboard); } @@ -169,10 +347,6 @@ width: 20px; } -.jp-MarkdownIcon { - background-image: var(--jp-icon-markdown); -} - .jp-MoreHorizIcon { background-image: var(--jp-icon-more); } @@ -181,30 +355,10 @@ background-image: var(--jp-icon-new-folder); } -.jp-NotebookIcon { - background-image: var(--jp-icon-notebook); -} - -.jp-FolderIcon { - background-image: var(--jp-icon-folder); -} - -.jp-PaletteIcon { - background-image: var(--jp-icon-palette); -} - .jp-PasteIcon { background-image: var(--jp-icon-paste); } -.jp-PythonIcon { - background-image: var(--jp-icon-python); -} - -.jp-RKernelIcon { - background-image: var(--jp-icon-r); -} - .jp-RefreshIcon { background-image: var(--jp-icon-refresh); } @@ -213,10 +367,6 @@ background-image: var(--jp-icon-run); } -.jp-RunningIcon { - background-image: var(--jp-icon-stop-circle); -} - .jp-SaveIcon { background-image: var(--jp-icon-save); } @@ -225,22 +375,10 @@ background-image: var(--jp-icon-settings); } -.jp-SpreadsheetIcon { - background-image: var(--jp-icon-spreadsheet); -} - .jp-StopIcon { background-image: var(--jp-icon-stop); } -.jp-TabIcon { - background-image: var(--jp-icon-tab); -} - -.jp-TerminalIcon { - background-image: var(--jp-icon-terminal); -} - .jp-TextEditorIcon { background-image: var(--jp-icon-text-editor); } @@ -249,14 +387,6 @@ background-image: var(--jp-icon-undo); } -.jp-FileUploadIcon { - background-image: var(--jp-icon-file-upload); -} - -.jp-YAMLIcon { - background-image: var(--jp-icon-yaml); -} - .jp-VegaIcon { background-image: var(--jp-icon-vega); } diff --git a/packages/application/style/images.css b/packages/application/style/images.css index 6c966fe3036f..51cedf03ee4b 100644 --- a/packages/application/style/images.css +++ b/packages/application/style/images.css @@ -3,6 +3,8 @@ | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ +/* CSS for specific icons */ + .jp-ImageJupyterLab { background-image: var(--jp-image-jupyterlab); } @@ -11,10 +13,6 @@ background-image: var(--jp-image-jupyterlab-wordmark); } -.jp-ImageJupyter-favicon { - background-image: var(--jp-image-jupyter-favicon); -} - .jp-JupyterIcon { background-image: var(--jp-image-jupyter); background-size: 16px; diff --git a/packages/application/style/index.css b/packages/application/style/index.css index 664fa92bba98..6ece208121b6 100644 --- a/packages/application/style/index.css +++ b/packages/application/style/index.css @@ -5,6 +5,7 @@ /* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ @import url('~@phosphor/widgets/style/index.css'); +@import url('~@jupyterlab/ui-components/style/index.css'); @import url('~@jupyterlab/apputils/style/index.css'); @import url('~@jupyterlab/docregistry/style/index.css'); @import url('~font-awesome/css/font-awesome.min.css'); diff --git a/packages/application/tsconfig.json b/packages/application/tsconfig.json index 3dfa87de215c..27ada107b7cf 100644 --- a/packages/application/tsconfig.json +++ b/packages/application/tsconfig.json @@ -23,6 +23,9 @@ }, { "path": "../services" + }, + { + "path": "../ui-components" } ] } diff --git a/packages/apputils-extension/package.json b/packages/apputils-extension/package.json index 2f9ff1262af9..9dac53222948 100644 --- a/packages/apputils-extension/package.json +++ b/packages/apputils-extension/package.json @@ -41,6 +41,7 @@ "@jupyterlab/apputils": "^1.1.0-alpha.1", "@jupyterlab/coreutils": "^3.1.0-alpha.1", "@jupyterlab/mainmenu": "^1.1.0-alpha.1", + "@jupyterlab/ui-components": "^1.1.0-alpha.1", "@phosphor/algorithm": "^1.1.3", "@phosphor/commands": "^1.6.3", "@phosphor/coreutils": "^1.3.1", diff --git a/packages/apputils-extension/src/index.ts b/packages/apputils-extension/src/index.ts index e447f9540b69..c07def7b84f6 100644 --- a/packages/apputils-extension/src/index.ts +++ b/packages/apputils-extension/src/index.ts @@ -34,6 +34,8 @@ import { import { IMainMenu } from '@jupyterlab/mainmenu'; +import { defaultIconRegistry } from '@jupyterlab/ui-components'; + import { PromiseDelegate } from '@phosphor/coreutils'; import { DisposableDelegate } from '@phosphor/disposable'; @@ -291,6 +293,13 @@ const splash: JupyterFrontEndPlugin = { galaxy.id = 'galaxy'; logo.id = 'main-logo'; + defaultIconRegistry.icon({ + name: 'jupyter-favicon', + container: logo, + center: true, + kind: 'splash' + }); + galaxy.appendChild(logo); ['1', '2', '3'].forEach(id => { const moon = document.createElement('div'); diff --git a/packages/apputils-extension/style/images/jupyter-favicon.svg b/packages/apputils-extension/style/images/jupyter-favicon.svg deleted file mode 100644 index 41d846fc9d5f..000000000000 --- a/packages/apputils-extension/style/images/jupyter-favicon.svg +++ /dev/null @@ -1,33 +0,0 @@ - - - - logo - Created with Sketch. - - - - - \ No newline at end of file diff --git a/packages/apputils-extension/style/index.css b/packages/apputils-extension/style/index.css index 39f879fb3639..0f1065092855 100644 --- a/packages/apputils-extension/style/index.css +++ b/packages/apputils-extension/style/index.css @@ -5,6 +5,7 @@ /* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ @import url('~@phosphor/widgets/style/index.css'); +@import url('~@jupyterlab/ui-components/style/index.css'); @import url('~@jupyterlab/apputils/style/index.css'); @import url('~@jupyterlab/application/style/index.css'); @import url('~@jupyterlab/mainmenu/style/index.css'); diff --git a/packages/apputils-extension/style/splash.css b/packages/apputils-extension/style/splash.css index c810edccd31b..891adef97267 100644 --- a/packages/apputils-extension/style/splash.css +++ b/packages/apputils-extension/style/splash.css @@ -33,18 +33,6 @@ height: 100%; } -#main-logo { - background-image: url('./images/jupyter-favicon.svg'); - background-repeat: no-repeat; - background-size: 100px; - position: absolute; - background-position: center; - width: 100%; - height: 100%; - z-index: 1; - animation: 0.3s fade-in linear forwards; -} - .planet { background-repeat: no-repeat; background-size: cover; diff --git a/packages/apputils-extension/tsconfig.json b/packages/apputils-extension/tsconfig.json index 17ecbea70fb2..298728e8d135 100644 --- a/packages/apputils-extension/tsconfig.json +++ b/packages/apputils-extension/tsconfig.json @@ -17,6 +17,9 @@ }, { "path": "../mainmenu" + }, + { + "path": "../ui-components" } ] } diff --git a/packages/apputils/src/domutils.ts b/packages/apputils/src/domutils.ts index 8237b231f50f..ee14f99fdc98 100644 --- a/packages/apputils/src/domutils.ts +++ b/packages/apputils/src/domutils.ts @@ -34,6 +34,18 @@ export namespace DOMUtils { return parent.querySelector(`.${className}`) as HTMLElement; } + /** + * Find the first element matching a class name. + */ + export function findElements( + parent: HTMLElement, + className: string + ): HTMLCollectionOf { + return parent.getElementsByClassName(className) as HTMLCollectionOf< + HTMLElement + >; + } + /** * Create a DOM id with prefix "id-" to solve bug for UUIDs beginning with numbers. */ diff --git a/packages/apputils/style/index.css b/packages/apputils/style/index.css index 9a38dcba26bc..38810a9cb009 100644 --- a/packages/apputils/style/index.css +++ b/packages/apputils/style/index.css @@ -4,7 +4,7 @@ |----------------------------------------------------------------------------*/ /* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ -@import url('~@jupyterlab/ui-components/style/index.css'); @import url('~@phosphor/widgets/style/index.css'); +@import url('~@jupyterlab/ui-components/style/index.css'); @import url('./base.css'); diff --git a/packages/coreutils/src/path.ts b/packages/coreutils/src/path.ts index b7ee7919dce7..db1b57d12514 100644 --- a/packages/coreutils/src/path.ts +++ b/packages/coreutils/src/path.ts @@ -61,6 +61,23 @@ export namespace PathExt { return posix.extname(path); } + /** + * Get the last portion of a path, without its extension (if any). + * + * @param path - The file path. + * + * @returns the last part of the path, sans extension. + */ + export function stem(path: string): string { + return path + .split('\\') + .pop() + .split('/') + .pop() + .split('.') + .shift(); + } + /** * Normalize a string path, reducing '..' and '.' parts. * When multiple slashes are found, they're replaced by a single one; when the path contains a trailing slash, it is preserved. On Windows backslashes are used. diff --git a/packages/coreutils/src/text.ts b/packages/coreutils/src/text.ts index ef8de712b5fb..7892a1b11a7e 100644 --- a/packages/coreutils/src/text.ts +++ b/packages/coreutils/src/text.ts @@ -71,6 +71,29 @@ export namespace Text { return jsIdx; } + /** + * Given a 'snake-case', 'snake_case', or 'snake case' string, + * will return the camel case version: 'snakeCase'. + * + * @param str: the snake-case input string. + * + * @param upper: default = false. If true, the first letter of the + * returned string will be capitalized. + * + * @returns the camel case version of the input string. + */ + export function camelCase(str: string, upper: boolean = false): string { + return str.replace(/(?:^\w|[A-Z]|\b\w|\s+|-+|_+)/g, function(match, index) { + if (+match === 0 || match[0] === '-') { + return ''; + } else if (index === 0 && !upper) { + return match.toLowerCase(); + } else { + return match.toUpperCase(); + } + }); + } + /** * Given a string, title case the words in the string. * diff --git a/packages/docregistry/src/registry.ts b/packages/docregistry/src/registry.ts index f234672f5963..cebc01a43648 100644 --- a/packages/docregistry/src/registry.ts +++ b/packages/docregistry/src/registry.ts @@ -1120,7 +1120,7 @@ export namespace DocumentRegistry { name: 'default', extensions: [], mimeTypes: [], - iconClass: 'jp-MaterialIcon jp-FileIcon', + iconClass: 'jp-FileIcon', iconLabel: '', contentType: 'file', fileFormat: 'text' @@ -1171,7 +1171,7 @@ export namespace DocumentRegistry { extensions: ['.ipynb'], contentType: 'notebook', fileFormat: 'json', - iconClass: 'jp-MaterialIcon jp-NotebookIcon' + iconClass: 'jp-NotebookIcon' }; /** @@ -1183,7 +1183,7 @@ export namespace DocumentRegistry { extensions: [], mimeTypes: ['text/directory'], contentType: 'directory', - iconClass: 'jp-MaterialIcon jp-FolderIcon' + iconClass: 'jp-FolderIcon' }; /** @@ -1198,56 +1198,56 @@ export namespace DocumentRegistry { displayName: 'Markdown File', extensions: ['.md'], mimeTypes: ['text/markdown'], - iconClass: 'jp-MaterialIcon jp-MarkdownIcon' + iconClass: 'jp-MarkdownIcon' }, { name: 'python', displayName: 'Python File', extensions: ['.py'], mimeTypes: ['text/x-python'], - iconClass: 'jp-MaterialIcon jp-PythonIcon' + iconClass: 'jp-PythonIcon' }, { name: 'json', displayName: 'JSON File', extensions: ['.json'], mimeTypes: ['application/json'], - iconClass: 'jp-MaterialIcon jp-JSONIcon' + iconClass: 'jp-JsonIcon' }, { name: 'csv', displayName: 'CSV File', extensions: ['.csv'], mimeTypes: ['text/csv'], - iconClass: 'jp-MaterialIcon jp-SpreadsheetIcon' + iconClass: 'jp-SpreadsheetIcon' }, { name: 'tsv', displayName: 'TSV File', extensions: ['.tsv'], mimeTypes: ['text/csv'], - iconClass: 'jp-MaterialIcon jp-SpreadsheetIcon' + iconClass: 'jp-SpreadsheetIcon' }, { name: 'r', displayName: 'R File', mimeTypes: ['text/x-rsrc'], extensions: ['.r'], - iconClass: 'jp-MaterialIcon jp-RKernelIcon' + iconClass: 'jp-RKernelIcon' }, { name: 'yaml', displayName: 'YAML File', mimeTypes: ['text/x-yaml', 'text/yaml'], extensions: ['.yaml', '.yml'], - iconClass: 'jp-MaterialIcon jp-YAMLIcon' + iconClass: 'jp-YamlIcon' }, { name: 'svg', displayName: 'Image', mimeTypes: ['image/svg+xml'], extensions: ['.svg'], - iconClass: 'jp-MaterialIcon jp-ImageIcon', + iconClass: 'jp-ImageIcon', fileFormat: 'base64' }, { @@ -1255,7 +1255,7 @@ export namespace DocumentRegistry { displayName: 'Image', mimeTypes: ['image/tiff'], extensions: ['.tif', '.tiff'], - iconClass: 'jp-MaterialIcon jp-ImageIcon', + iconClass: 'jp-ImageIcon', fileFormat: 'base64' }, { @@ -1263,7 +1263,7 @@ export namespace DocumentRegistry { displayName: 'Image', mimeTypes: ['image/jpeg'], extensions: ['.jpg', '.jpeg'], - iconClass: 'jp-MaterialIcon jp-ImageIcon', + iconClass: 'jp-ImageIcon', fileFormat: 'base64' }, { @@ -1271,7 +1271,7 @@ export namespace DocumentRegistry { displayName: 'Image', mimeTypes: ['image/gif'], extensions: ['.gif'], - iconClass: 'jp-MaterialIcon jp-ImageIcon', + iconClass: 'jp-ImageIcon', fileFormat: 'base64' }, { @@ -1279,7 +1279,7 @@ export namespace DocumentRegistry { displayName: 'Image', mimeTypes: ['image/png'], extensions: ['.png'], - iconClass: 'jp-MaterialIcon jp-ImageIcon', + iconClass: 'jp-ImageIcon', fileFormat: 'base64' }, { @@ -1287,7 +1287,7 @@ export namespace DocumentRegistry { displayName: 'Image', mimeTypes: ['image/bmp'], extensions: ['.bmp'], - iconClass: 'jp-MaterialIcon jp-ImageIcon', + iconClass: 'jp-ImageIcon', fileFormat: 'base64' } ]; diff --git a/packages/filebrowser-extension/package.json b/packages/filebrowser-extension/package.json index eb2d4dded6fb..944e08b9d03c 100644 --- a/packages/filebrowser-extension/package.json +++ b/packages/filebrowser-extension/package.json @@ -45,6 +45,7 @@ "@jupyterlab/mainmenu": "^1.1.0-alpha.1", "@jupyterlab/services": "^4.1.0-alpha.1", "@jupyterlab/statusbar": "^1.1.0-alpha.1", + "@jupyterlab/ui-components": "^1.1.0-alpha.1", "@phosphor/algorithm": "^1.1.3", "@phosphor/commands": "^1.6.3", "@phosphor/messaging": "^1.2.3", diff --git a/packages/filebrowser-extension/src/index.ts b/packages/filebrowser-extension/src/index.ts index 50f5b6b4db22..e2ccd771f5f1 100644 --- a/packages/filebrowser-extension/src/index.ts +++ b/packages/filebrowser-extension/src/index.ts @@ -43,6 +43,8 @@ import { Contents } from '@jupyterlab/services'; import { IStatusBar } from '@jupyterlab/statusbar'; +import { IIconRegistry } from '@jupyterlab/ui-components'; + import { IIterator, map, reduce, toArray } from '@phosphor/algorithm'; import { CommandRegistry } from '@phosphor/commands'; @@ -128,7 +130,7 @@ const factory: JupyterFrontEndPlugin = { activate: activateFactory, id: '@jupyterlab/filebrowser-extension:factory', provides: IFileBrowserFactory, - requires: [IDocumentManager, IStateDB] + requires: [IIconRegistry, IDocumentManager, IStateDB] }; /** @@ -200,6 +202,7 @@ export default plugins; */ function activateFactory( app: JupyterFrontEnd, + icoReg: IIconRegistry, docManager: IDocumentManager, state: IStateDB ): IFileBrowserFactory { @@ -210,6 +213,7 @@ function activateFactory( options: IFileBrowserFactory.IOptions = {} ) => { const model = new FileBrowserModel({ + iconRegistry: icoReg, manager: docManager, driveName: options.driveName || '', refreshInterval: options.refreshInterval, diff --git a/packages/filebrowser-extension/tsconfig.json b/packages/filebrowser-extension/tsconfig.json index ccbf069617a4..863de97f9c19 100644 --- a/packages/filebrowser-extension/tsconfig.json +++ b/packages/filebrowser-extension/tsconfig.json @@ -32,6 +32,9 @@ }, { "path": "../statusbar" + }, + { + "path": "../ui-components" } ] } diff --git a/packages/filebrowser/package.json b/packages/filebrowser/package.json index 51a41f50e91b..7f31b361607f 100644 --- a/packages/filebrowser/package.json +++ b/packages/filebrowser/package.json @@ -41,6 +41,7 @@ "@jupyterlab/docregistry": "^1.1.0-alpha.1", "@jupyterlab/services": "^4.1.0-alpha.1", "@jupyterlab/statusbar": "^1.1.0-alpha.1", + "@jupyterlab/ui-components": "^1.1.0-alpha.1", "@phosphor/algorithm": "^1.1.3", "@phosphor/coreutils": "^1.3.1", "@phosphor/disposable": "^1.2.0", diff --git a/packages/filebrowser/src/crumbs.ts b/packages/filebrowser/src/crumbs.ts index 542677e4cbc8..7aa2989c45d1 100644 --- a/packages/filebrowser/src/crumbs.ts +++ b/packages/filebrowser/src/crumbs.ts @@ -17,6 +17,8 @@ import { PathExt, PageConfig } from '@jupyterlab/coreutils'; import { renameFile } from '@jupyterlab/docmanager'; +import { defaultIconRegistry } from '@jupyterlab/ui-components'; + import { FileBrowserModel } from './model'; /** @@ -30,10 +32,15 @@ const MATERIAL_CLASS = 'jp-MaterialIcon'; const BREADCRUMB_CLASS = 'jp-BreadCrumbs'; /** - * The class name added to add the folder icon for the breadcrumbs + * The class name for the folder icon for the breadcrumbs home */ const BREADCRUMB_HOME = 'jp-FolderIcon'; +/** + * The class name for the breadcrumbs home node + */ +const BREADCRUMB_HOME_CLASS = 'jp-BreadCrumbs-home'; + /** * The class named associated to the ellipses icon */ @@ -159,7 +166,10 @@ export class BreadCrumbs extends Widget { // Find a valid click target. let node = event.target as HTMLElement; while (node && node !== this.node) { - if (node.classList.contains(BREADCRUMB_ITEM_CLASS)) { + if ( + node.classList.contains(BREADCRUMB_ITEM_CLASS) || + node.classList.contains(BREADCRUMB_HOME_CLASS) + ) { let index = ArrayExt.findFirstIndex( this._crumbs, value => value === node @@ -353,7 +363,12 @@ namespace Private { */ export function createCrumbs(): ReadonlyArray { let home = document.createElement('span'); - home.className = `${MATERIAL_CLASS} ${BREADCRUMB_HOME} ${BREADCRUMB_ITEM_CLASS}`; + defaultIconRegistry.icon({ + name: BREADCRUMB_HOME, + className: BREADCRUMB_HOME_CLASS, + container: home, + kind: 'breadCrumb' + }); home.title = PageConfig.getOption('serverRoot') || 'Jupyter Server Root'; let ellipsis = document.createElement('span'); ellipsis.className = diff --git a/packages/filebrowser/src/listing.ts b/packages/filebrowser/src/listing.ts index ff507164f9cc..98aa5425ead6 100644 --- a/packages/filebrowser/src/listing.ts +++ b/packages/filebrowser/src/listing.ts @@ -20,6 +20,8 @@ import { DocumentRegistry } from '@jupyterlab/docregistry'; import { Contents } from '@jupyterlab/services'; +import { IIconRegistry } from '@jupyterlab/ui-components'; + import { ArrayExt, ArrayIterator, @@ -186,7 +188,9 @@ export class DirListing extends Widget { */ constructor(options: DirListing.IOptions) { super({ - node: (options.renderer || DirListing.defaultRenderer).createNode() + node: (options.renderer = + options.renderer || + new DirListing.Renderer(options.model.iconRegistry)).createNode() }); this.addClass(DIR_LISTING_CLASS); this._model = options.model; @@ -196,7 +200,7 @@ export class DirListing extends Widget { this._editNode = document.createElement('input'); this._editNode.className = EDITOR_CLASS; this._manager = this._model.manager; - this._renderer = options.renderer || DirListing.defaultRenderer; + this._renderer = options.renderer; const headerNode = DOMUtils.findElement(this.node, HEADER_CLASS); this._renderer.populateHeaderNode(headerNode); @@ -736,11 +740,14 @@ export class DirListing extends Widget { content.appendChild(node); } - // Remove extra classes from the nodes. + // Remove extra classes/data from the nodes. nodes.forEach(item => { item.classList.remove(SELECTED_CLASS); item.classList.remove(RUNNING_CLASS); item.classList.remove(CUT_CLASS); + if (item.children[0]) { + delete (item.children[0] as HTMLElement).dataset.icon; + } }); // Add extra classes to item nodes based on widget state. @@ -1638,6 +1645,10 @@ export namespace DirListing { * The default implementation of an `IRenderer`. */ export class Renderer implements IRenderer { + constructor(icoReg: IIconRegistry) { + this._iconRegistry = icoReg; + } + /** * Create the DOM node for a dir listing. */ @@ -1757,9 +1768,23 @@ export namespace DirListing { let modified = DOMUtils.findElement(node, ITEM_MODIFIED_CLASS); if (fileType) { - icon.textContent = fileType.iconLabel || ''; - icon.className = `${ITEM_ICON_CLASS} ${fileType.iconClass || ''}`; + // add icon as svg node. Can be styled using CSS + if ( + !this._iconRegistry.icon({ + name: fileType.iconClass, + className: ITEM_ICON_CLASS, + title: fileType.iconLabel, + container: icon, + center: true, + kind: 'listing' + }) + ) { + // add icon as CSS background image. Can't be styled using CSS + icon.className = `${ITEM_ICON_CLASS} ${fileType.iconClass || ''}`; + icon.textContent = fileType.iconLabel || ''; + } } else { + // use default icon as CSS background image icon.textContent = ''; icon.className = ITEM_ICON_CLASS; } @@ -1843,12 +1868,8 @@ export namespace DirListing { node.appendChild(icon); return node; } + _iconRegistry: IIconRegistry; } - - /** - * The default `IRenderer` instance. - */ - export const defaultRenderer = new Renderer(); } /** diff --git a/packages/filebrowser/src/model.ts b/packages/filebrowser/src/model.ts index 8f458a0c9555..5f6392759a5c 100644 --- a/packages/filebrowser/src/model.ts +++ b/packages/filebrowser/src/model.ts @@ -1,6 +1,8 @@ // Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. +import { showDialog, Dialog } from '@jupyterlab/apputils'; + import { IChangedArgs, IStateDB, @@ -13,6 +15,8 @@ import { IDocumentManager, shouldOverwrite } from '@jupyterlab/docmanager'; import { Contents, Kernel, Session } from '@jupyterlab/services'; +import { IIconRegistry } from '@jupyterlab/ui-components'; + import { ArrayIterator, each, @@ -28,8 +32,6 @@ import { IDisposable } from '@phosphor/disposable'; import { ISignal, Signal } from '@phosphor/signaling'; -import { showDialog, Dialog } from '@jupyterlab/apputils'; - /** * The default duration of the auto-refresh in ms */ @@ -68,6 +70,7 @@ export class FileBrowserModel implements IDisposable { * Construct a new file browser model. */ constructor(options: FileBrowserModel.IOptions) { + this.iconRegistry = options.iconRegistry; this.manager = options.manager; this._driveName = options.driveName || ''; let rootPath = this._driveName ? this._driveName + ':' : ''; @@ -109,6 +112,11 @@ export class FileBrowserModel implements IDisposable { }); } + /** + * The icon registry instance used by the file browser model. + */ + readonly iconRegistry: IIconRegistry; + /** * The document manager instance used by the file browser model. */ @@ -637,6 +645,11 @@ export namespace FileBrowserModel { * An options object for initializing a file browser. */ export interface IOptions { + /** + * An icon registry instance. + */ + iconRegistry: IIconRegistry; + /** * A document manager instance. */ @@ -650,15 +663,15 @@ export namespace FileBrowserModel { driveName?: string; /** - * An optional state database. If provided, the model will restore which - * folder was last opened when it is restored. + * The time interval for browser refreshing, in ms. */ - state?: IStateDB; + refreshInterval?: number; /** - * The time interval for browser refreshing, in ms. + * An optional state database. If provided, the model will restore which + * folder was last opened when it is restored. */ - refreshInterval?: number; + state?: IStateDB; } } diff --git a/packages/filebrowser/style/base.css b/packages/filebrowser/style/base.css index 4290aa23651e..8253a2287bb4 100644 --- a/packages/filebrowser/style/base.css +++ b/packages/filebrowser/style/base.css @@ -225,10 +225,7 @@ color: limegreen; content: '\25CF'; font-size: 8px; - position: relative; - width: 100%; - height: 100%; - top: -2px; + position: absolute; left: -8px; } @@ -256,7 +253,7 @@ background-image: var(--jp-icon-python-selected); } -.jp-DirListing-item.jp-mod-selected .jp-DirListing-itemIcon.jp-JSONIcon { +.jp-DirListing-item.jp-mod-selected .jp-DirListing-itemIcon.jp-JsonIcon { background-image: var(--jp-icon-json-selected); } @@ -268,7 +265,7 @@ background-image: var(--jp-icon-r-selected); } -.jp-DirListing-item.jp-mod-selected .jp-DirListing-itemIcon.jp-YAMLIcon { +.jp-DirListing-item.jp-mod-selected .jp-DirListing-itemIcon.jp-YamlIcon { background-image: var(--jp-icon-yaml-selected); } diff --git a/packages/filebrowser/style/index.css b/packages/filebrowser/style/index.css index a04ddc3c646f..edbcab7a21dd 100644 --- a/packages/filebrowser/style/index.css +++ b/packages/filebrowser/style/index.css @@ -5,6 +5,7 @@ /* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ @import url('~@phosphor/widgets/style/index.css'); +@import url('~@jupyterlab/ui-components/style/index.css'); @import url('~@jupyterlab/apputils/style/index.css'); @import url('~@phosphor/dragdrop/style/index.css'); @import url('~@jupyterlab/docregistry/style/index.css'); diff --git a/packages/filebrowser/tsconfig.json b/packages/filebrowser/tsconfig.json index cb06e46e40c7..19a64feeabb3 100644 --- a/packages/filebrowser/tsconfig.json +++ b/packages/filebrowser/tsconfig.json @@ -23,6 +23,9 @@ }, { "path": "../statusbar" + }, + { + "path": "../ui-components" } ] } diff --git a/packages/fileeditor-extension/src/index.ts b/packages/fileeditor-extension/src/index.ts index 2379f659544c..e3e68dd52627 100644 --- a/packages/fileeditor-extension/src/index.ts +++ b/packages/fileeditor-extension/src/index.ts @@ -54,7 +54,7 @@ const EDITOR_ICON_CLASS = 'jp-MaterialIcon jp-TextEditorIcon'; /** * The class name for the text editor icon from the default theme. */ -const MARKDOWN_ICON_CLASS = 'jp-MaterialIcon jp-MarkdownIcon'; +const MARKDOWN_ICON_CLASS = 'jp-MarkdownIcon'; /** * The name of the factory that creates editor widgets. diff --git a/packages/htmlviewer-extension/src/index.tsx b/packages/htmlviewer-extension/src/index.tsx index 4d6d6ed24589..59b1876bb301 100644 --- a/packages/htmlviewer-extension/src/index.tsx +++ b/packages/htmlviewer-extension/src/index.tsx @@ -20,9 +20,9 @@ import { } from '@jupyterlab/htmlviewer'; /** - * The CSS class for an HTML5 icon. + * The name for an HTML5 icon. */ -const CSS_ICON_CLASS = 'jp-MaterialIcon jp-HTMLIcon'; +const ICON_NAME = 'html5'; /** * Command IDs used by the plugin. @@ -58,7 +58,7 @@ function activateHTMLViewer( displayName: 'HTML File', extensions: ['.html'], mimeTypes: ['text/html'], - iconClass: CSS_ICON_CLASS + iconClass: ICON_NAME }; app.docRegistry.addFileType(ft); @@ -98,6 +98,7 @@ function activateHTMLViewer( app.commands.notifyCommandChanged(CommandIDs.trustHTML); }); + // widget.node.appendChild(HTML5Icon); widget.title.iconClass = ft.iconClass; widget.title.iconLabel = ft.iconLabel; }); diff --git a/packages/htmlviewer-extension/style/base.css b/packages/htmlviewer-extension/style/base.css deleted file mode 100644 index 8ef6cabcc54a..000000000000 --- a/packages/htmlviewer-extension/style/base.css +++ /dev/null @@ -1,9 +0,0 @@ -/*----------------------------------------------------------------------------- -| Copyright (c) Jupyter Development Team. -| Distributed under the terms of the Modified BSD License. -|----------------------------------------------------------------------------*/ - -/* Document icon */ -.jp-HTMLIcon { - background-image: url('./html5-icon.svg'); -} diff --git a/packages/htmlviewer-extension/style/html5-icon.svg b/packages/htmlviewer-extension/style/html5-icon.svg deleted file mode 100644 index b8b075fa046b..000000000000 --- a/packages/htmlviewer-extension/style/html5-icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - HTML5 Logo - - - - - - \ No newline at end of file diff --git a/packages/htmlviewer-extension/style/index.css b/packages/htmlviewer-extension/style/index.css index cbc7122d5fbc..32422713a5d6 100644 --- a/packages/htmlviewer-extension/style/index.css +++ b/packages/htmlviewer-extension/style/index.css @@ -8,5 +8,3 @@ @import url('~@jupyterlab/docregistry/style/index.css'); @import url('~@jupyterlab/application/style/index.css'); @import url('~@jupyterlab/htmlviewer/style/index.css'); - -@import url('./base.css'); diff --git a/packages/json-extension/style/index.css b/packages/json-extension/style/index.css index 3a4b711feb1e..bacc5f067317 100644 --- a/packages/json-extension/style/index.css +++ b/packages/json-extension/style/index.css @@ -4,8 +4,8 @@ |----------------------------------------------------------------------------*/ /* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ -@import url('~@jupyterlab/ui-components/style/index.css'); @import url('~@phosphor/widgets/style/index.css'); +@import url('~@jupyterlab/ui-components/style/index.css'); @import url('~@jupyterlab/apputils/style/index.css'); @import url('./base.css'); diff --git a/packages/launcher/package.json b/packages/launcher/package.json index 9a64f1aedddd..7d87d19a3445 100644 --- a/packages/launcher/package.json +++ b/packages/launcher/package.json @@ -36,6 +36,7 @@ }, "dependencies": { "@jupyterlab/apputils": "^1.1.0-alpha.1", + "@jupyterlab/ui-components": "^1.1.0-alpha.1", "@phosphor/algorithm": "^1.1.3", "@phosphor/commands": "^1.6.3", "@phosphor/coreutils": "^1.3.1", diff --git a/packages/launcher/src/index.tsx b/packages/launcher/src/index.tsx index 4bc885919dec..021fefa89f76 100644 --- a/packages/launcher/src/index.tsx +++ b/packages/launcher/src/index.tsx @@ -7,6 +7,12 @@ import { VDomRenderer } from '@jupyterlab/apputils'; +import { + combineClasses, + DefaultIconReact, + defaultIconRegistry +} from '@jupyterlab/ui-components'; + import { ArrayExt, ArrayIterator, @@ -188,17 +194,31 @@ export class Launcher extends VDomRenderer { // Now create the sections for each category orderedCategories.forEach(cat => { const item = categories[cat][0] as ILauncher.IItemOptions; - let iconClass = - `${this._commands.iconClass(item.command, { - ...item.args, - cwd: this.cwd - })} ` + 'jp-Launcher-sectionIcon jp-Launcher-icon'; + let iconClass = this._commands.iconClass(item.command, { + ...item.args, + cwd: this.cwd + }); let kernel = KERNEL_CATEGORIES.indexOf(cat) > -1; if (cat in categories) { section = (
- {kernel &&
} + {kernel && defaultIconRegistry.contains(iconClass) ? ( + + ) : ( +
+ )}

{cat}

@@ -384,6 +404,7 @@ function Card( }; // Return the VDOM element. + const iconClass = kernel ? '' : commands.iconClass(command, args); return (
-
- {item.kernelIconUrl && kernel && ( - - )} - {!item.kernelIconUrl && !kernel && ( -
- )} - {!item.kernelIconUrl && kernel && ( -
- {label[0].toUpperCase()} -
- )} -
+ {kernel ? ( +
+ {item.kernelIconUrl ? ( + + ) : ( +
+ {label[0].toUpperCase()} +
+ )} +
+ ) : defaultIconRegistry.contains(iconClass) ? ( + + ) : ( +
+
+
+ )}

{label}

diff --git a/packages/launcher/style/index.css b/packages/launcher/style/index.css index 546e18daa8a5..bacc5f067317 100644 --- a/packages/launcher/style/index.css +++ b/packages/launcher/style/index.css @@ -5,6 +5,7 @@ /* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ @import url('~@phosphor/widgets/style/index.css'); +@import url('~@jupyterlab/ui-components/style/index.css'); @import url('~@jupyterlab/apputils/style/index.css'); @import url('./base.css'); diff --git a/packages/launcher/tsconfig.json b/packages/launcher/tsconfig.json index 1a4f97efe5ff..58ebc16e0a36 100644 --- a/packages/launcher/tsconfig.json +++ b/packages/launcher/tsconfig.json @@ -8,6 +8,9 @@ "references": [ { "path": "../apputils" + }, + { + "path": "../ui-components" } ] } diff --git a/packages/metapackage/package.json b/packages/metapackage/package.json index 80baee2a34fa..dd0f3a67767c 100644 --- a/packages/metapackage/package.json +++ b/packages/metapackage/package.json @@ -101,6 +101,7 @@ "@jupyterlab/tooltip": "^1.1.0-alpha.1", "@jupyterlab/tooltip-extension": "^1.1.0-alpha.1", "@jupyterlab/ui-components": "^1.1.0-alpha.1", + "@jupyterlab/ui-components-extension": "^1.1.0-alpha.1", "@jupyterlab/vdom": "^1.1.0-alpha.1", "@jupyterlab/vdom-extension": "^1.1.0-alpha.1", "@jupyterlab/vega4-extension": "^1.1.0-alpha.1", diff --git a/packages/metapackage/tsconfig.json b/packages/metapackage/tsconfig.json index dc0d758dfaf5..2c99cf8e1b42 100644 --- a/packages/metapackage/tsconfig.json +++ b/packages/metapackage/tsconfig.json @@ -213,6 +213,9 @@ { "path": "../ui-components" }, + { + "path": "../ui-components-extension" + }, { "path": "../vdom" }, diff --git a/packages/notebook/src/truststatus.tsx b/packages/notebook/src/truststatus.tsx index 99d7b0d0e242..e6761424c35f 100644 --- a/packages/notebook/src/truststatus.tsx +++ b/packages/notebook/src/truststatus.tsx @@ -6,7 +6,7 @@ import { INotebookModel, Notebook } from '.'; import { Cell } from '@jupyterlab/cells'; -import { IconItem } from '@jupyterlab/statusbar'; +import { DefaultIconReact } from '@jupyterlab/ui-components'; import { toArray } from '@phosphor/algorithm'; @@ -44,8 +44,13 @@ function cellTrust( function NotebookTrustComponent( props: NotebookTrustComponent.IProps ): React.ReactElement { - const source = cellTrust(props)[1]; - return ; + if (props.allCellsTrusted) { + return ; + } else { + return ( + + ); + } } /** diff --git a/packages/notebook/style/base.css b/packages/notebook/style/base.css index b289c121f49a..0f75309ccfac 100644 --- a/packages/notebook/style/base.css +++ b/packages/notebook/style/base.css @@ -19,7 +19,6 @@ |----------------------------------------------------------------------------*/ @import './toolbar.css'; -@import './status.css'; /*----------------------------------------------------------------------------- | Notebook diff --git a/packages/notebook/style/index.css b/packages/notebook/style/index.css index 9927f8adda6c..7b77c736221c 100644 --- a/packages/notebook/style/index.css +++ b/packages/notebook/style/index.css @@ -4,8 +4,8 @@ |----------------------------------------------------------------------------*/ /* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ -@import url('~@jupyterlab/ui-components/style/index.css'); @import url('~@phosphor/widgets/style/index.css'); +@import url('~@jupyterlab/ui-components/style/index.css'); @import url('~@jupyterlab/apputils/style/index.css'); @import url('~@phosphor/dragdrop/style/index.css'); @import url('~@jupyterlab/codeeditor/style/index.css'); diff --git a/packages/notebook/style/not-trusted-icon-dark.svg b/packages/notebook/style/not-trusted-icon-dark.svg deleted file mode 100644 index 8439a93c6e70..000000000000 --- a/packages/notebook/style/not-trusted-icon-dark.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/notebook/style/not-trusted-icon-light.svg b/packages/notebook/style/not-trusted-icon-light.svg deleted file mode 100644 index cb63b74c2de6..000000000000 --- a/packages/notebook/style/not-trusted-icon-light.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/notebook/style/status.css b/packages/notebook/style/status.css deleted file mode 100644 index 6a6fd3f01564..000000000000 --- a/packages/notebook/style/status.css +++ /dev/null @@ -1,28 +0,0 @@ -/*----------------------------------------------------------------------------- -| Copyright (c) Jupyter Development Team. -| Distributed under the terms of the Modified BSD License. -|----------------------------------------------------------------------------*/ - -/* - * icons for light themes - */ - -[data-jp-theme-light='true'] .jp-StatusItem-untrusted { - background-image: url('./not-trusted-icon-light.svg'); -} - -[data-jp-theme-light='true'] .jp-StatusItem-trusted { - background-image: url('./trusted-icon-light.svg'); -} - -/* - * icons for dark themes - */ - -[data-jp-theme-light='false'] .jp-StatusItem-untrusted { - background-image: url('./not-trusted-icon-dark.svg'); -} - -[data-jp-theme-light='false'] .jp-StatusItem-trusted { - background-image: url('./trusted-icon-dark.svg'); -} diff --git a/packages/notebook/style/trusted-icon-dark.svg b/packages/notebook/style/trusted-icon-dark.svg deleted file mode 100644 index b92040c37a58..000000000000 --- a/packages/notebook/style/trusted-icon-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/notebook/style/trusted-icon-light.svg b/packages/notebook/style/trusted-icon-light.svg deleted file mode 100644 index 7f4fc23bac83..000000000000 --- a/packages/notebook/style/trusted-icon-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/settingeditor/package.json b/packages/settingeditor/package.json index 62f775ef96c1..7629367600be 100644 --- a/packages/settingeditor/package.json +++ b/packages/settingeditor/package.json @@ -40,6 +40,7 @@ "@jupyterlab/coreutils": "^3.1.0-alpha.1", "@jupyterlab/inspector": "^1.1.0-alpha.1", "@jupyterlab/rendermime": "^1.1.0-alpha.1", + "@jupyterlab/ui-components": "^1.1.0-alpha.1", "@phosphor/commands": "^1.6.3", "@phosphor/coreutils": "^1.3.1", "@phosphor/messaging": "^1.2.3", diff --git a/packages/settingeditor/src/pluginlist.tsx b/packages/settingeditor/src/pluginlist.tsx index 1f7dd5977e68..5daada29c4d2 100644 --- a/packages/settingeditor/src/pluginlist.tsx +++ b/packages/settingeditor/src/pluginlist.tsx @@ -5,6 +5,12 @@ import { ISettingRegistry } from '@jupyterlab/coreutils'; +import { + combineClasses, + DefaultIconReact, + defaultIconRegistry +} from '@jupyterlab/ui-components'; + import { Message } from '@phosphor/messaging'; import { ISignal, Signal } from '@phosphor/signaling'; @@ -253,9 +259,11 @@ namespace Private { const { id, schema, version } = plugin; const itemTitle = `${schema.description}\n${id}\n${version}`; const image = getHint(ICON_CLASS_KEY, registry, plugin); - const iconClass = `jp-MaterialIcon jp-PluginList-icon${ - image ? ' ' + image : '' - }`; + const iconClass = combineClasses( + image, + 'jp-PluginList-icon', + 'jp-MaterialIcon' + ); const iconTitle = getHint(ICON_LABEL_KEY, registry, plugin); return ( @@ -265,7 +273,17 @@ namespace Private { key={id} title={itemTitle} > - + {defaultIconRegistry.contains(image) ? ( + + ) : ( + + )} {schema.title || id} ); diff --git a/packages/settingeditor/style/index.css b/packages/settingeditor/style/index.css index 8e4c3c1c1782..fd84f4f05eda 100644 --- a/packages/settingeditor/style/index.css +++ b/packages/settingeditor/style/index.css @@ -5,6 +5,7 @@ /* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ @import url('~@phosphor/widgets/style/index.css'); +@import url('~@jupyterlab/ui-components/style/index.css'); @import url('~@jupyterlab/apputils/style/index.css'); @import url('~@jupyterlab/codeeditor/style/index.css'); @import url('~@jupyterlab/rendermime/style/index.css'); diff --git a/packages/settingeditor/tsconfig.json b/packages/settingeditor/tsconfig.json index e38e704a2b31..75553a5273ab 100644 --- a/packages/settingeditor/tsconfig.json +++ b/packages/settingeditor/tsconfig.json @@ -20,6 +20,9 @@ }, { "path": "../rendermime" + }, + { + "path": "../ui-components" } ] } diff --git a/packages/statusbar/package.json b/packages/statusbar/package.json index 82f488fa2961..7dc5e6123832 100644 --- a/packages/statusbar/package.json +++ b/packages/statusbar/package.json @@ -35,6 +35,7 @@ "@jupyterlab/codeeditor": "^1.1.0-alpha.1", "@jupyterlab/coreutils": "^3.1.0-alpha.1", "@jupyterlab/services": "^4.1.0-alpha.1", + "@jupyterlab/ui-components": "^1.1.0-alpha.1", "@phosphor/algorithm": "^1.1.3", "@phosphor/coreutils": "^1.3.1", "@phosphor/disposable": "^1.2.0", diff --git a/packages/statusbar/src/components/icon.tsx b/packages/statusbar/src/components/icon.tsx deleted file mode 100644 index 74bcd444c583..000000000000 --- a/packages/statusbar/src/components/icon.tsx +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Jupyter Development Team. -// Distributed under the terms of the Modified BSD License. - -import * as React from 'react'; - -import { classes, style } from 'typestyle/lib'; - -import icon from '../style/icon'; - -/** - * A namespace for IconItem statics. - */ -export namespace IconItem { - /** - * Props for an IconItem - */ - export interface IProps { - /** - * A CSS class name for the icon. - */ - source: string; - } -} - -/** - * A functional tsx component for an icon. - */ -export function IconItem( - props: IconItem.IProps & React.HTMLAttributes -): React.ReactElement { - const { source, className, ...rest } = props; - return ( -
- ); -} diff --git a/packages/statusbar/src/components/index.ts b/packages/statusbar/src/components/index.ts index 1f2699acab31..bd165a2abf5a 100644 --- a/packages/statusbar/src/components/index.ts +++ b/packages/statusbar/src/components/index.ts @@ -1,7 +1,6 @@ // Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. -export * from './icon'; export * from './progressBar'; export * from './text'; export * from './group'; diff --git a/packages/statusbar/src/defaults/lineCol.tsx b/packages/statusbar/src/defaults/lineCol.tsx index 2240c2da940c..d46425ace093 100644 --- a/packages/statusbar/src/defaults/lineCol.tsx +++ b/packages/statusbar/src/defaults/lineCol.tsx @@ -7,7 +7,11 @@ import { VDomRenderer, VDomModel, ReactWidget } from '@jupyterlab/apputils'; import { CodeEditor } from '@jupyterlab/codeeditor'; -import { interactiveItem, showPopup, Popup, TextItem } from '..'; +import { DefaultIconReact } from '@jupyterlab/ui-components'; + +import { classes } from 'typestyle/lib'; + +import { interactiveItem, Popup, showPopup, TextItem } from '..'; import { lineFormWrapper, @@ -15,11 +19,11 @@ import { lineFormSearch, lineFormWrapperFocusWithin, lineFormCaption, + lineFormButtonDiv, + lineFormButtonIcon, lineFormButton } from '../style/lineForm'; -import { classes } from 'typestyle/lib'; - /** * A namespace for LineFormComponent statics. */ @@ -111,12 +115,14 @@ class LineFormComponent extends React.Component< this._textInput = input; }} /> - - +
+ + +
} /> @@ -69,18 +69,11 @@ export const InputGroup = (props: IInputGroupProps & CommonProps) => { return ( ); }; -export const Icon = (props: IIconProps) => ( - -); - export const Collapse = (props: ICollapseProps & CommonProps) => ( ); @@ -88,13 +81,13 @@ export const Collapse = (props: ICollapseProps & CommonProps) => ( export const HTMLSelect = (props: IHTMLSelectProps & CommonProps) => ( ); export const Select = (props: ISelectProps & CommonProps) => ( ); diff --git a/packages/ui-components/src/icon/iconimports.ts b/packages/ui-components/src/icon/iconimports.ts new file mode 100644 index 000000000000..3a9151eae78a --- /dev/null +++ b/packages/ui-components/src/icon/iconimports.ts @@ -0,0 +1,62 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ + +/* This file was auto-generated by ensureUiComponents() in @jupyterlab/buildutils */ + +import { Icon } from './interfaces'; + +// icon svg import statements +import fileSvg from '../../style/icons/filetype/file.svg'; +import folderSvg from '../../style/icons/filetype/folder.svg'; +import html5Svg from '../../style/icons/filetype/html5.svg'; +import imageSvg from '../../style/icons/filetype/image.svg'; +import jsonSvg from '../../style/icons/filetype/json.svg'; +import markdownSvg from '../../style/icons/filetype/markdown.svg'; +import notebookSvg from '../../style/icons/filetype/notebook.svg'; +import pythonSvg from '../../style/icons/filetype/python.svg'; +import rKernelSvg from '../../style/icons/filetype/r-kernel.svg'; +import reactSvg from '../../style/icons/filetype/react.svg'; +import spreadsheetSvg from '../../style/icons/filetype/spreadsheet.svg'; +import yamlSvg from '../../style/icons/filetype/yaml.svg'; +import buildSvg from '../../style/icons/sidebar/build.svg'; +import extensionSvg from '../../style/icons/sidebar/extension.svg'; +import paletteSvg from '../../style/icons/sidebar/palette.svg'; +import runningSvg from '../../style/icons/sidebar/running.svg'; +import tabSvg from '../../style/icons/sidebar/tab.svg'; +import jupyterFaviconSvg from '../../style/icons/splash/jupyter-favicon.svg'; +import kernelSvg from '../../style/icons/statusbar/kernel.svg'; +import lineFormSvg from '../../style/icons/statusbar/line-form.svg'; +import notTrustedSvg from '../../style/icons/statusbar/not-trusted.svg'; +import terminalSvg from '../../style/icons/statusbar/terminal.svg'; +import trustedSvg from '../../style/icons/statusbar/trusted.svg'; + +// defaultIcons definition +export namespace IconImports { + export const defaultIcons: ReadonlyArray = [ + { name: 'file', svg: fileSvg }, + { name: 'folder', svg: folderSvg }, + { name: 'html5', svg: html5Svg }, + { name: 'image', svg: imageSvg }, + { name: 'json', svg: jsonSvg }, + { name: 'markdown', svg: markdownSvg }, + { name: 'notebook', svg: notebookSvg }, + { name: 'python', svg: pythonSvg }, + { name: 'r-kernel', svg: rKernelSvg }, + { name: 'react', svg: reactSvg }, + { name: 'spreadsheet', svg: spreadsheetSvg }, + { name: 'yaml', svg: yamlSvg }, + { name: 'build', svg: buildSvg }, + { name: 'extension', svg: extensionSvg }, + { name: 'palette', svg: paletteSvg }, + { name: 'running', svg: runningSvg }, + { name: 'tab', svg: tabSvg }, + { name: 'jupyter-favicon', svg: jupyterFaviconSvg }, + { name: 'kernel', svg: kernelSvg }, + { name: 'line-form', svg: lineFormSvg }, + { name: 'not-trusted', svg: notTrustedSvg }, + { name: 'terminal', svg: terminalSvg }, + { name: 'trusted', svg: trustedSvg } + ]; +} diff --git a/packages/ui-components/src/icon/iconregistry.tsx b/packages/ui-components/src/icon/iconregistry.tsx new file mode 100644 index 000000000000..d2a5d70b6bfe --- /dev/null +++ b/packages/ui-components/src/icon/iconregistry.tsx @@ -0,0 +1,212 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +import React from 'react'; +import { classes } from 'typestyle/lib'; + +import { Text } from '@jupyterlab/coreutils'; + +import { IIconRegistry, Icon } from './interfaces'; +import { IconImports } from './iconimports'; +import { iconStyle, iconStyleFlat } from '../style/icon'; + +import badSvg from '../../style/debug/bad.svg'; +import blankSvg from '../../style/debug/blank.svg'; + +/** + * The icon registry class. + */ +export class IconRegistry implements IIconRegistry { + constructor(options: IconRegistry.IOptions = {}) { + this._debug = !!options.debug; + + let icons = options.initialIcons || IconImports.defaultIcons; + this.addIcon(...icons); + + // add the bad state and blank icons + this.addIcon( + { name: 'bad', svg: badSvg }, + { name: 'blank', svg: blankSvg } + ); + } + + addIcon(...icons: Icon.IModel[]): void { + icons.forEach((icon: Icon.IModel) => { + let className = icon.className + ? icon.className + : IconRegistry.iconClassName(icon.name); + this._classNameToName[className] = icon.name; + this._nameToClassName[icon.name] = className; + this._svg[icon.name] = icon.svg; + }); + } + + contains(name: string): boolean { + return name in this._svg || name in this._classNameToName; + } + + /** + * Get the icon as an HTMLElement of tag + */ + icon( + props: Icon.INodeOptions & { container?: HTMLElement } + ): HTMLElement | null { + const { name, className, title, container, ...propsStyle } = props; + + // we may have been handed a className in place of name + let resolvedName = this.resolveName(name); + if ( + !resolvedName || + (container && + container.dataset.icon && + container.dataset.icon === resolvedName) + ) { + // bail if failing silently or icon node is already set + return; + } + + let svgNode = Private.parseSvg(this.svg(resolvedName)); + + if (title) { + Private.setTitleSvg(svgNode, title); + } + + if (container) { + // clear any existing icon in container (and all other child elements) + container.textContent = ''; + container.dataset.icon = resolvedName; + container.appendChild(svgNode); + + let styleClass = propsStyle ? iconStyle(propsStyle) : ''; + if (className || className === '') { + // override the className, if explicitly passed + container.className = classes(className, styleClass); + } else if (!container.classList.contains(styleClass)) { + // add icon styling class to the container's class, if not already present + container.className = classes(container.className, styleClass); + } + } else { + // add icon styling class directly to the svg node + svgNode.setAttribute( + 'class', + classes(className, propsStyle ? iconStyleFlat(propsStyle) : '') + ); + } + + return svgNode; + } + + /** + * Get the icon as a ReactElement of tag + * TODO: figure out how to remove the unnecessary outer + */ + iconReact( + props: Icon.INodeOptions & { tag?: 'div' | 'span' } + ): React.ReactElement { + const { name, className, title, tag, ...propsStyle } = props; + const Tag = tag || 'div'; + + // we may have been handed a className in place of name + let resolvedName = this.resolveName(name); + if (!resolvedName) { + // bail if failing silently + return <>; + } + + return ( + + ); + } + + resolveName(name: string): string { + if (!(name in this._svg)) { + // assume name is really a className, split the className into parts and check each part + for (let className of name.split(/\s+/)) { + if (className in this._classNameToName) { + return this._classNameToName[className]; + } + } + if (this._debug) { + // couldn't resolve name, mark as bad and warn + console.error(`Invalid icon name: ${name}`); + return 'bad'; + } else { + // couldn't resolve name, fail silently + return ''; + } + } + + return name; + } + + svg(name: string): string { + return this._svg[name]; + } + + static iconClassName(name: string): string { + return 'jp-' + Text.camelCase(name, true) + 'Icon'; + } + + private _classNameToName: { [key: string]: string } = Object.create(null); + private _debug: boolean = false; + private _nameToClassName: { [key: string]: string } = Object.create(null); + private _svg: { [key: string]: string } = Object.create(null); +} + +/** + * The defaultIconRegistry instance. + */ +export const defaultIconRegistry: IconRegistry = new IconRegistry(); + +/** + * Alias for defaultIconRegistry.iconReact that can be used as a React component + */ +export const DefaultIconReact = ( + props: Icon.INodeOptions & { tag?: 'div' | 'span' } +): React.ReactElement => { + return defaultIconRegistry.iconReact(props); +}; + +export namespace IconRegistry { + /** + * The options used to create an icon registry. + */ + export interface IOptions { + /** + * The initial icons for the registry. + * The [[Icon.defaultIcons]] will be used if not given. + */ + initialIcons?: Icon.IModel[]; + + /** + * If the debug flag is set, missing icons will raise a warning + * and be visually marked with an "X". Otherwise, missing icons + * will fail silently. + */ + debug?: boolean; + } +} + +namespace Private { + export function parseSvg(svg: string): HTMLElement { + let parser = new DOMParser(); + return parser.parseFromString(svg, 'image/svg+xml').documentElement; + } + + export function setTitleSvg(svgNode: HTMLElement, title: string): void { + // add a title node to the top level svg node + let titleNodes = svgNode.getElementsByTagName('title'); + if (titleNodes) { + titleNodes[0].textContent = title; + } else { + let titleNode = document.createElement('title'); + titleNode.textContent = title; + svgNode.appendChild(titleNode); + } + } +} diff --git a/packages/ui-components/src/icon/index.ts b/packages/ui-components/src/icon/index.ts new file mode 100644 index 000000000000..c64a470388c7 --- /dev/null +++ b/packages/ui-components/src/icon/index.ts @@ -0,0 +1,7 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +export * from './iconimports'; +export * from './iconregistry'; +export * from './interfaces'; +export * from './tabbarsvg'; diff --git a/packages/ui-components/src/icon/interfaces.ts b/packages/ui-components/src/icon/interfaces.ts new file mode 100644 index 000000000000..e1f6e7c6c74d --- /dev/null +++ b/packages/ui-components/src/icon/interfaces.ts @@ -0,0 +1,63 @@ +import { Token } from '@phosphor/coreutils'; + +import { IIconStyle } from '../style/icon'; +import React from 'react'; + +/** + * The interface for an object that keeps a registry of inline + * svg icons. Has methods for setting up inline svg icons as + * either `HTMLElement` or `ReactElement` + */ +export interface IIconRegistry { + /** + * Add the raw text representation of an svg icon to this registry + */ + addIcon(...icons: Icon.IModel[]): void; + + /** + * Check if any icon of name `name` has been registered. + * Exact matches only + */ + contains(name: string): boolean; + + /** + * Get the icon as an HTMLElement of tag + */ + icon( + props: Icon.INodeOptions & { container: HTMLElement } + ): HTMLElement | null; + + /** + * Get the icon as a ReactElement of tag + */ + iconReact( + props: Icon.INodeOptions & { tag?: 'div' | 'span' } + ): React.ReactElement; +} + +/** + * The IIconRegistry token. + */ +export const IIconRegistry = new Token( + '@jupyterlab/ui-components:IIconRegistry' +); + +export namespace Icon { + /** + * A representation of the resources underlying an inline svg icon + */ + export interface IModel { + name: string; + className?: string; + svg: string; + } + + /** + * The options used when creating an icon node + */ + export interface INodeOptions extends IIconStyle { + name: string; + className?: string; + title?: string; + } +} diff --git a/packages/ui-components/src/icon/tabbarsvg.ts b/packages/ui-components/src/icon/tabbarsvg.ts new file mode 100644 index 000000000000..f48845193340 --- /dev/null +++ b/packages/ui-components/src/icon/tabbarsvg.ts @@ -0,0 +1,140 @@ +import { Message } from '@phosphor/messaging'; + +import { h, VirtualElement } from '@phosphor/virtualdom'; + +import { DockPanel, TabBar, Widget } from '@phosphor/widgets'; + +import { IconKindType } from '../style/icon'; +import { defaultIconRegistry } from './iconregistry'; + +/** + * A widget which displays titles as a single row or column of tabs. Tweaked + * to enable the use of inline svgs as tab icons. + */ +export class TabBarSvg extends TabBar { + /** + * Construct a new tab bar. Sets the (icon) kind and overrides + * the default renderer. + * + * @param options - The options for initializing the tab bar. + */ + constructor(options: TabBarSvg.IOptions) { + options.renderer = options.renderer || TabBarSvg.defaultRenderer; + super(options); + + this._kind = options.kind; + } + + /** + * A message handler invoked on an `'update-request'` message. Adds svg + * nodes to icon nodes as appropriate + */ + protected onUpdateRequest(msg: Message): void { + super.onUpdateRequest(msg); + + for (let itab in this.contentNode.children) { + let tab = this.contentNode.children[itab]; + let title = this.titles[itab]; + let iconNode = tab.children ? (tab.children[0] as HTMLElement) : null; + + if (iconNode) { + // add the svg node, if not already present + defaultIconRegistry.icon({ + name: title.iconClass, + className: '', + container: iconNode, + center: true, + kind: this._kind + }); + } + } + } + + protected _kind: IconKindType; +} + +export namespace TabBarSvg { + export interface IOptions extends TabBar.IOptions { + /** + * The kind of icon this tab bar widget should render. + * Adds preset styling to the icons. + */ + kind?: IconKindType; + } + + /** + * A modified implementation of the TabBar Renderer. + */ + export class Renderer extends TabBar.Renderer { + /** + * Render the icon element for a tab. This version avoids clobbering + * the icon node's children. + * + * @param data - The data to use for rendering the tab. + * + * @returns A virtual element representing the tab icon. + */ + renderIcon(data: TabBar.IRenderData): VirtualElement { + let className = this.createIconClass(data); + return h.div({ className }); + } + } + + export const defaultRenderer = new Renderer(); +} + +/** + * A widget which provides a flexible docking area for widgets.Tweaked + * to enable the use of inline svgs as tab icons. + */ +export class DockPanelSvg extends DockPanel { + /** + * Construct a new dock panel. Overrides the default renderer + * and sets the (icon) kind + * + * @param options - The options for initializing the panel. + */ + constructor(options: DockPanelSvg.IOptions) { + if (!options.renderer) { + options.renderer = new DockPanelSvg.Renderer(options.kind); + } + + super(options); + } +} + +export namespace DockPanelSvg { + export interface IOptions extends DockPanel.IOptions { + /** + * The kind of icon this dock panel widget should render. + * Adds preset styling to the icons. + */ + kind?: IconKindType; + } + + /** + * A modified implementation of the DockPanel Renderer. + */ + export class Renderer extends DockPanel.Renderer { + constructor(kind?: IconKindType) { + super(); + this._kind = kind; + } + + /** + * Create a new tab bar (with inline svg icons enabled + * for use with a dock panel. + * + * @returns A new tab bar for a dock panel. + */ + createTabBar(): TabBarSvg { + let bar = new TabBarSvg({ + kind: this._kind + }); + bar.addClass('p-DockPanel-tabBar'); + return bar; + } + + _kind: IconKindType; + } +} diff --git a/packages/ui-components/src/index.ts b/packages/ui-components/src/index.ts new file mode 100644 index 000000000000..cabb6173cab6 --- /dev/null +++ b/packages/ui-components/src/index.ts @@ -0,0 +1,6 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +export * from './blueprint'; +export * from './icon'; +export * from './utils'; diff --git a/packages/ui-components/src/style/icon.ts b/packages/ui-components/src/style/icon.ts new file mode 100644 index 000000000000..b4f9910eec7f --- /dev/null +++ b/packages/ui-components/src/style/icon.ts @@ -0,0 +1,260 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +import { cssRule, style } from 'typestyle/lib'; +import { NestedCSSProperties } from 'typestyle/lib/types'; + +/** + * - breadCrumb: The path icons above the filebrowser + * - dockPanelBar: The tab icons above the main area + * - launcherCard: The icons for the cards at the bottom of the launcher + * - launcherSection: The icons to left of the Launcher section headers + * - listing: The icons to the left of the filebrowser listing items + * - settingsEditor: The icons to the left of each section of the settings editor + * - sideBar: The icons for the sidebar (default to the left of the main window) + * - splash: The icon used for the splash screen + * - tabManager: The icons for the tabManager in the sidebar + */ +export type IconKindType = + | 'breadCrumb' + | 'dockPanelBar' + | 'launcherCard' + | 'launcherSection' + | 'listing' + | 'settingsEditor' + | 'sideBar' + | 'splash' + | 'statusBar' + | 'tabManager' + | 'unset'; + +export interface IIconStyle extends NestedCSSProperties { + /** + * center the icon svg in its container + */ + center?: boolean; + + /** + * the kind of the icon, associated with a default stylesheet + */ + kind?: IconKindType; +} + +/** + * styles for centering node inside of containers + */ +const containerCSSCenter: NestedCSSProperties = { + alignItems: 'center', + display: 'flex' +}; + +const iconCSSCenter: NestedCSSProperties = { + display: 'block', + margin: '0 auto', + width: '100%' +}; + +/** + * icon kind specific styles + */ +const iconCSSBreadCrumb: NestedCSSProperties = { + borderRadius: 'var(--jp-border-radius)', + cursor: 'pointer', + margin: '0px 2px', + padding: '0px 2px', + height: '16px', + width: '16px', + verticalAlign: 'middle', + $nest: { + '&:hover': { + backgroundColor: 'var(--jp-layout-color2)' + }, + '&:first-child': { + marginLeft: '0px' + }, + ['.jp-mod-dropTarget']: { + backgroundColor: 'var(--jp-brand-color2)', + opacity: 0.7 + } + } +}; + +const iconCSSDockPanelBar: NestedCSSProperties = { + height: '14px', + width: '14px' +}; + +const iconCSSLauncherCard: NestedCSSProperties = { + height: 'var(--jp-private-launcher-large-icon-size)', + width: 'var(--jp-private-launcher-large-icon-size)' +}; + +const iconCSSLauncherSection: NestedCSSProperties = { + marginRight: '12px', + height: 'var(--jp-private-launcher-small-icon-size)', + width: 'var(--jp-private-launcher-small-icon-size)' +}; + +const iconCSSListing: NestedCSSProperties = { + height: '16px', + width: '16px' +}; + +const iconCSSSettingsEditor: NestedCSSProperties = { + height: '16px', + width: '16px' +}; + +const iconCSSSideBar: NestedCSSProperties = { + width: '20px' +}; + +const iconCSSSplash: NestedCSSProperties = { + width: '100px' +}; + +const iconCSSStatusBar: NestedCSSProperties = { + left: '0px', + top: '0px', + height: '18px', + width: '20px', + position: 'relative' +}; + +const iconCSSTabManager: NestedCSSProperties = { + height: '16px', + width: '16px' +}; + +const iconCSSKind: { [k in IconKindType]: NestedCSSProperties } = { + breadCrumb: iconCSSBreadCrumb, + dockPanelBar: iconCSSDockPanelBar, + launcherCard: iconCSSLauncherCard, + launcherSection: iconCSSLauncherSection, + listing: iconCSSListing, + settingsEditor: iconCSSSettingsEditor, + sideBar: iconCSSSideBar, + splash: iconCSSSplash, + statusBar: iconCSSStatusBar, + tabManager: iconCSSTabManager, + unset: {} +}; + +/** + * container kind specific styles + */ +const containerCSSDockPanelBar: NestedCSSProperties = { + marginRight: '4px' +}; + +const containerCSSLauncherCard: NestedCSSProperties = { + height: 'var(--jp-private-launcher-card-icon-height)' +}; + +const containerCSSListing: NestedCSSProperties = { + flex: '0 0 20px', + marginRight: '4px', + position: 'relative' +}; + +const containerCSSSettingsEditor: NestedCSSProperties = { + display: 'inline-block', + flex: '0 0 20px', + marginLeft: '2px', + marginRight: '1px', + position: 'relative', + height: '20px', + width: '20px' +}; + +const containerCSSSideBar: NestedCSSProperties = { + transform: 'rotate(90deg)' +}; + +const containerCSSSplash: NestedCSSProperties = { + animation: '0.3s fade-in linear forwards', + height: '100%', + width: '100%', + zIndex: 1 +}; + +const containerCSSTabManager: NestedCSSProperties = { + marginRight: '2px', + position: 'relative' +}; + +const containerCSSKind: { [k in IconKindType]: NestedCSSProperties } = { + breadCrumb: {}, + dockPanelBar: containerCSSDockPanelBar, + launcherCard: containerCSSLauncherCard, + launcherSection: {}, + listing: containerCSSListing, + settingsEditor: containerCSSSettingsEditor, + sideBar: containerCSSSideBar, + splash: containerCSSSplash, + statusBar: {}, + tabManager: containerCSSTabManager, + unset: {} +}; + +/** + * for putting together the icon kind style with any user input styling, + * as well as styling from optional flags like `center` + */ +function iconCSS(props: IIconStyle): NestedCSSProperties { + const { kind, center, ...propsCSS } = props; + + return { + ...(center ? iconCSSCenter : {}), + ...(kind ? iconCSSKind[kind] : {}), + ...propsCSS + }; +} + +/** + * for putting together the container kind style with any + * styling from optional flags like `center` + */ +function containerCSS(props: IIconStyle): NestedCSSProperties { + const { kind, center } = props; + + return { + ...(center ? containerCSSCenter : {}), + ...(kind ? containerCSSKind[kind] : {}) + }; +} + +/** + * for setting the style on the container of an svg node representing an icon + */ +export const iconStyle = (props: IIconStyle): string => { + return style({ + ...containerCSS(props), + $nest: { + ['svg']: iconCSS(props) + } + }); +}; + +/** + * for setting the style directly on the svg node representing an icon + */ +export const iconStyleFlat = (props: IIconStyle): string => { + return style(iconCSS(props)); +}; + +// TODO: Figure out a better cludge for styling current sidebar tab selection +cssRule( + `.p-TabBar-tab.p-mod-current .${iconStyle({ + center: true, + kind: 'sideBar' + })}`, + { + transform: + 'rotate(90deg)\n' + + ' translate(\n' + + ' calc(-0.5 * var(--jp-border-width)),\n' + + ' calc(-0.5 * var(--jp-border-width))\n' + + ' )' + } +); diff --git a/packages/ui-components/src/svg.d.ts b/packages/ui-components/src/svg.d.ts new file mode 100644 index 000000000000..90e4deda5555 --- /dev/null +++ b/packages/ui-components/src/svg.d.ts @@ -0,0 +1,58 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +// including this file in a package allows for the use of import statements +// with svg files. Example: `import xSvg from 'path/xSvg.svg'` + +// for use with raw-loader in Webpack. +// The svg will be imported as a raw string + +declare module '*.svg' { + const value: string; + export default value; +} + +// for use with svg-react-loader in Webpack. +// The svg will be imported as a ReactElement + +// declare module '*.svg' { +// import { HTMLAttributes } from 'react'; +// const value: React.ComponentType>; +// export default value; +// } + +// as an alternative to importing svgs one at a time, you can do a glob import +// using `context.requires`. This is a Webpack only extension. Implementation: + +// import { PathExt } from '@jupyterlab/coreutils'; +// +// /** +// * Import all svgs from a directory. The input argument should be +// * of the form `require.context('raw-loader!', true, /\.svg$/)`. +// * should be a string literal path, as this is needed by `require`. +// */ +// export function importSvgs(r: any, exclude: string[] = []): IModel[] { +// const excset = new Set(exclude); +// +// return r.keys().reduce((svgs: IModel[], item: string, index: number) => { +// const name = PathExt.stem(item); +// if (!excset.has(name)) { +// svgs.push({ name: name, svg: r(item).default }); +// } +// return svgs; +// }, []); +// } +// +// // create the array of default icon models +// let icons: IModel[]; +// try { +// // require.context is supplied by Webpack, and doesn't play nice with jest +// icons = importSvgs( +// require.context('raw-loader!../../style/icons', true, /\.svg$/), +// ['bad', 'blank'] +// ); +// } catch (e) { +// // fallback for jest tests +// icons = []; +// } +// export const defaultIcons: ReadonlyArray = icons; diff --git a/packages/ui-components/src/utils.ts b/packages/ui-components/src/utils.ts index 69ba135d38a8..ba5a549a732b 100644 --- a/packages/ui-components/src/utils.ts +++ b/packages/ui-components/src/utils.ts @@ -1,6 +1,6 @@ // Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. -export function combineClassNames(...classNames: (string | undefined)[]) { - return classNames.join(' '); +export function combineClasses(...classNames: (string | undefined)[]) { + return classNames.filter(c => !!c).join(' '); } diff --git a/packages/ui-components/style/base.css b/packages/ui-components/style/base.css index 8adb7976093f..d8730de3cf3f 100644 --- a/packages/ui-components/style/base.css +++ b/packages/ui-components/style/base.css @@ -112,3 +112,7 @@ input::placeholder { select { box-sizing: border-box; } + +/* Sibling imports */ +@import './deprecated.css'; +@import './deprecatedExtra.css'; diff --git a/packages/ui-components/style/debug/bad.svg b/packages/ui-components/style/debug/bad.svg new file mode 100644 index 000000000000..1fe7dcd245ef --- /dev/null +++ b/packages/ui-components/style/debug/bad.svg @@ -0,0 +1,7 @@ + + + diff --git a/packages/ui-components/style/debug/blank.svg b/packages/ui-components/style/debug/blank.svg new file mode 100644 index 000000000000..d44952fd4948 --- /dev/null +++ b/packages/ui-components/style/debug/blank.svg @@ -0,0 +1,7 @@ + + + diff --git a/packages/ui-components/style/deprecated.css b/packages/ui-components/style/deprecated.css new file mode 100644 index 000000000000..70ec78758d03 --- /dev/null +++ b/packages/ui-components/style/deprecated.css @@ -0,0 +1,110 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ + +/* This file was auto-generated by ensureUiComponents() in @jupyterlab/buildutils */ + +/** + * (DEPRECATED) Support for consuming icons as CSS background images + */ + +/* Icons urls */ + +:root { + --jp-icon-file: url('icons/filetype/file.svg'); + --jp-icon-folder: url('icons/filetype/folder.svg'); + --jp-icon-html5: url('icons/filetype/html5.svg'); + --jp-icon-image: url('icons/filetype/image.svg'); + --jp-icon-json: url('icons/filetype/json.svg'); + --jp-icon-markdown: url('icons/filetype/markdown.svg'); + --jp-icon-notebook: url('icons/filetype/notebook.svg'); + --jp-icon-python: url('icons/filetype/python.svg'); + --jp-icon-r-kernel: url('icons/filetype/r-kernel.svg'); + --jp-icon-react: url('icons/filetype/react.svg'); + --jp-icon-spreadsheet: url('icons/filetype/spreadsheet.svg'); + --jp-icon-yaml: url('icons/filetype/yaml.svg'); + --jp-icon-build: url('icons/sidebar/build.svg'); + --jp-icon-extension: url('icons/sidebar/extension.svg'); + --jp-icon-palette: url('icons/sidebar/palette.svg'); + --jp-icon-running: url('icons/sidebar/running.svg'); + --jp-icon-tab: url('icons/sidebar/tab.svg'); + --jp-icon-jupyter-favicon: url('icons/splash/jupyter-favicon.svg'); + --jp-icon-kernel: url('icons/statusbar/kernel.svg'); + --jp-icon-line-form: url('icons/statusbar/line-form.svg'); + --jp-icon-not-trusted: url('icons/statusbar/not-trusted.svg'); + --jp-icon-terminal: url('icons/statusbar/terminal.svg'); + --jp-icon-trusted: url('icons/statusbar/trusted.svg'); +} + +/* Icon CSS class declarations */ + +.jp-FileIcon { + background-image: var(--jp-icon-file); +} +.jp-FolderIcon { + background-image: var(--jp-icon-folder); +} +.jp-Html5Icon { + background-image: var(--jp-icon-html5); +} +.jp-ImageIcon { + background-image: var(--jp-icon-image); +} +.jp-JsonIcon { + background-image: var(--jp-icon-json); +} +.jp-MarkdownIcon { + background-image: var(--jp-icon-markdown); +} +.jp-NotebookIcon { + background-image: var(--jp-icon-notebook); +} +.jp-PythonIcon { + background-image: var(--jp-icon-python); +} +.jp-RKernelIcon { + background-image: var(--jp-icon-r-kernel); +} +.jp-ReactIcon { + background-image: var(--jp-icon-react); +} +.jp-SpreadsheetIcon { + background-image: var(--jp-icon-spreadsheet); +} +.jp-YamlIcon { + background-image: var(--jp-icon-yaml); +} +.jp-BuildIcon { + background-image: var(--jp-icon-build); +} +.jp-ExtensionIcon { + background-image: var(--jp-icon-extension); +} +.jp-PaletteIcon { + background-image: var(--jp-icon-palette); +} +.jp-RunningIcon { + background-image: var(--jp-icon-running); +} +.jp-TabIcon { + background-image: var(--jp-icon-tab); +} +.jp-JupyterFaviconIcon { + background-image: var(--jp-icon-jupyter-favicon); +} +.jp-KernelIcon { + background-image: var(--jp-icon-kernel); +} +.jp-LineFormIcon { + background-image: var(--jp-icon-line-form); +} +.jp-NotTrustedIcon { + background-image: var(--jp-icon-not-trusted); +} +.jp-TerminalIcon { + background-image: var(--jp-icon-terminal); +} +.jp-TrustedIcon { + background-image: var(--jp-icon-trusted); +} diff --git a/packages/theme-dark-extension/style/icons/jupyter/csv_selected.svg b/packages/ui-components/style/deprecated/selected/csv_selected.svg similarity index 100% rename from packages/theme-dark-extension/style/icons/jupyter/csv_selected.svg rename to packages/ui-components/style/deprecated/selected/csv_selected.svg diff --git a/packages/theme-dark-extension/style/icons/jupyter/file_selected.svg b/packages/ui-components/style/deprecated/selected/file_selected.svg similarity index 100% rename from packages/theme-dark-extension/style/icons/jupyter/file_selected.svg rename to packages/ui-components/style/deprecated/selected/file_selected.svg diff --git a/packages/theme-dark-extension/style/icons/md/ic_folder_24px_selected.svg b/packages/ui-components/style/deprecated/selected/ic_folder_24px_selected.svg similarity index 100% rename from packages/theme-dark-extension/style/icons/md/ic_folder_24px_selected.svg rename to packages/ui-components/style/deprecated/selected/ic_folder_24px_selected.svg diff --git a/packages/theme-dark-extension/style/icons/md/ic_memory_24px_selected.svg b/packages/ui-components/style/deprecated/selected/ic_memory_24px_selected.svg similarity index 100% rename from packages/theme-dark-extension/style/icons/md/ic_memory_24px_selected.svg rename to packages/ui-components/style/deprecated/selected/ic_memory_24px_selected.svg diff --git a/packages/theme-dark-extension/style/icons/jupyter/image_selected.svg b/packages/ui-components/style/deprecated/selected/image_selected.svg similarity index 100% rename from packages/theme-dark-extension/style/icons/jupyter/image_selected.svg rename to packages/ui-components/style/deprecated/selected/image_selected.svg diff --git a/packages/theme-dark-extension/style/icons/jupyter/json_selected.svg b/packages/ui-components/style/deprecated/selected/json_selected.svg similarity index 100% rename from packages/theme-dark-extension/style/icons/jupyter/json_selected.svg rename to packages/ui-components/style/deprecated/selected/json_selected.svg diff --git a/packages/theme-dark-extension/style/icons/jupyter/markdown_selected.svg b/packages/ui-components/style/deprecated/selected/markdown_selected.svg similarity index 100% rename from packages/theme-dark-extension/style/icons/jupyter/markdown_selected.svg rename to packages/ui-components/style/deprecated/selected/markdown_selected.svg diff --git a/packages/theme-dark-extension/style/icons/jupyter/notebook_selected.svg b/packages/ui-components/style/deprecated/selected/notebook_selected.svg similarity index 100% rename from packages/theme-dark-extension/style/icons/jupyter/notebook_selected.svg rename to packages/ui-components/style/deprecated/selected/notebook_selected.svg diff --git a/packages/theme-dark-extension/style/icons/jupyter/python_selected.svg b/packages/ui-components/style/deprecated/selected/python_selected.svg similarity index 100% rename from packages/theme-dark-extension/style/icons/jupyter/python_selected.svg rename to packages/ui-components/style/deprecated/selected/python_selected.svg diff --git a/packages/theme-dark-extension/style/icons/jupyter/r_selected.svg b/packages/ui-components/style/deprecated/selected/r_selected.svg similarity index 100% rename from packages/theme-dark-extension/style/icons/jupyter/r_selected.svg rename to packages/ui-components/style/deprecated/selected/r_selected.svg diff --git a/packages/theme-dark-extension/style/icons/jupyter/yml_selected.svg b/packages/ui-components/style/deprecated/selected/yml_selected.svg similarity index 100% rename from packages/theme-dark-extension/style/icons/jupyter/yml_selected.svg rename to packages/ui-components/style/deprecated/selected/yml_selected.svg diff --git a/packages/ui-components/style/deprecatedExtra.css b/packages/ui-components/style/deprecatedExtra.css new file mode 100644 index 000000000000..25bd79e43ebb --- /dev/null +++ b/packages/ui-components/style/deprecatedExtra.css @@ -0,0 +1,25 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ + +/** + * This file contains support for deprecated CSS not covered in the + * auto-generated `deprecated.css`. + */ + +/* vars for consuming "selected" icons as CSS background images */ + +:root { + --jp-icon-file-selected: url('deprecated/selected/file_selected.svg'); + --jp-icon-folder-selected: url('deprecated/selected/ic_folder_24px_selected.svg'); + --jp-icon-image-selected: url('deprecated/selected/image_selected.svg'); + --jp-icon-json-selected: url('deprecated/selected/json_selected.svg'); + --jp-icon-kernel-selected: url('deprecated/selected/ic_memory_24px_selected.svg'); + --jp-icon-markdown-selected: url('deprecated/selected/markdown_selected.svg'); + --jp-icon-notebook-selected: url('deprecated/selected/notebook_selected.svg'); + --jp-icon-python-selected: url('deprecated/selected/python_selected.svg'); + --jp-icon-r-selected: url('deprecated/selected/r_selected.svg'); + --jp-icon-spreadsheet-selected: url('deprecated/selected/csv_selected.svg'); + --jp-icon-yaml-selected: url('deprecated/selected/yml_selected.svg'); +} diff --git a/packages/ui-components/style/icons/filetype/file.svg b/packages/ui-components/style/icons/filetype/file.svg new file mode 100644 index 000000000000..0526ea36ef24 --- /dev/null +++ b/packages/ui-components/style/icons/filetype/file.svg @@ -0,0 +1,7 @@ + + + + diff --git a/packages/ui-components/style/icons/filetype/folder.svg b/packages/ui-components/style/icons/filetype/folder.svg new file mode 100644 index 000000000000..e4d6d832d610 --- /dev/null +++ b/packages/ui-components/style/icons/filetype/folder.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui-components/style/icons/filetype/html5.svg b/packages/ui-components/style/icons/filetype/html5.svg new file mode 100644 index 000000000000..d4c7978c5d4c --- /dev/null +++ b/packages/ui-components/style/icons/filetype/html5.svg @@ -0,0 +1,11 @@ + + + + + + + + + + > + diff --git a/packages/ui-components/style/icons/filetype/image.svg b/packages/ui-components/style/icons/filetype/image.svg new file mode 100644 index 000000000000..7035ee0c4747 --- /dev/null +++ b/packages/ui-components/style/icons/filetype/image.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/ui-components/style/icons/filetype/json.svg b/packages/ui-components/style/icons/filetype/json.svg new file mode 100644 index 000000000000..b840e84d4718 --- /dev/null +++ b/packages/ui-components/style/icons/filetype/json.svg @@ -0,0 +1,16 @@ + + + + diff --git a/packages/ui-components/style/icons/filetype/markdown.svg b/packages/ui-components/style/icons/filetype/markdown.svg new file mode 100644 index 000000000000..691d2037ef02 --- /dev/null +++ b/packages/ui-components/style/icons/filetype/markdown.svg @@ -0,0 +1,10 @@ + + + + diff --git a/packages/ui-components/style/icons/filetype/notebook.svg b/packages/ui-components/style/icons/filetype/notebook.svg new file mode 100644 index 000000000000..cbf8bba2f6d2 --- /dev/null +++ b/packages/ui-components/style/icons/filetype/notebook.svg @@ -0,0 +1,8 @@ + + + + diff --git a/packages/ui-components/style/icons/filetype/python.svg b/packages/ui-components/style/icons/filetype/python.svg new file mode 100644 index 000000000000..57e750cc0281 --- /dev/null +++ b/packages/ui-components/style/icons/filetype/python.svg @@ -0,0 +1,13 @@ + + + + diff --git a/packages/ui-components/style/icons/filetype/r-kernel.svg b/packages/ui-components/style/icons/filetype/r-kernel.svg new file mode 100644 index 000000000000..28353efbe037 --- /dev/null +++ b/packages/ui-components/style/icons/filetype/r-kernel.svg @@ -0,0 +1,9 @@ + + + + diff --git a/packages/ui-components/style/icons/filetype/react.svg b/packages/ui-components/style/icons/filetype/react.svg new file mode 100644 index 000000000000..be0691e3aa1c --- /dev/null +++ b/packages/ui-components/style/icons/filetype/react.svg @@ -0,0 +1,32 @@ + + + diff --git a/packages/ui-components/style/icons/filetype/spreadsheet.svg b/packages/ui-components/style/icons/filetype/spreadsheet.svg new file mode 100644 index 000000000000..97b81167ca2a --- /dev/null +++ b/packages/ui-components/style/icons/filetype/spreadsheet.svg @@ -0,0 +1,6 @@ + + + + diff --git a/packages/ui-components/style/icons/filetype/yaml.svg b/packages/ui-components/style/icons/filetype/yaml.svg new file mode 100644 index 000000000000..9a1772a39826 --- /dev/null +++ b/packages/ui-components/style/icons/filetype/yaml.svg @@ -0,0 +1,10 @@ + + + + diff --git a/packages/ui-components/style/icons/sidebar/build.svg b/packages/ui-components/style/icons/sidebar/build.svg new file mode 100755 index 000000000000..32ddc5709bea --- /dev/null +++ b/packages/ui-components/style/icons/sidebar/build.svg @@ -0,0 +1,7 @@ + + + diff --git a/packages/ui-components/style/icons/sidebar/extension.svg b/packages/ui-components/style/icons/sidebar/extension.svg new file mode 100644 index 000000000000..e7c930bdad64 --- /dev/null +++ b/packages/ui-components/style/icons/sidebar/extension.svg @@ -0,0 +1,7 @@ + + + diff --git a/packages/ui-components/style/icons/sidebar/palette.svg b/packages/ui-components/style/icons/sidebar/palette.svg new file mode 100644 index 000000000000..bccca056d078 --- /dev/null +++ b/packages/ui-components/style/icons/sidebar/palette.svg @@ -0,0 +1,7 @@ + + + diff --git a/packages/ui-components/style/icons/sidebar/running.svg b/packages/ui-components/style/icons/sidebar/running.svg new file mode 100644 index 000000000000..b10ef9c297cb --- /dev/null +++ b/packages/ui-components/style/icons/sidebar/running.svg @@ -0,0 +1,7 @@ + + > + diff --git a/packages/ui-components/style/icons/sidebar/tab.svg b/packages/ui-components/style/icons/sidebar/tab.svg new file mode 100644 index 000000000000..4ce0b473dcb6 --- /dev/null +++ b/packages/ui-components/style/icons/sidebar/tab.svg @@ -0,0 +1,7 @@ + + + diff --git a/packages/ui-components/style/icons/splash/jupyter-favicon.svg b/packages/ui-components/style/icons/splash/jupyter-favicon.svg new file mode 100644 index 000000000000..755b30eec9ca --- /dev/null +++ b/packages/ui-components/style/icons/splash/jupyter-favicon.svg @@ -0,0 +1,5 @@ + + Jupyter Favicon + + + diff --git a/packages/ui-components/style/icons/statusbar/kernel.svg b/packages/ui-components/style/icons/statusbar/kernel.svg new file mode 100644 index 000000000000..e97ce48f2349 --- /dev/null +++ b/packages/ui-components/style/icons/statusbar/kernel.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/statusbar/style/line-form.svg b/packages/ui-components/style/icons/statusbar/line-form.svg similarity index 57% rename from packages/statusbar/style/line-form.svg rename to packages/ui-components/style/icons/statusbar/line-form.svg index 7491cb849bab..b4fd1ef77088 100644 --- a/packages/statusbar/style/line-form.svg +++ b/packages/ui-components/style/icons/statusbar/line-form.svg @@ -1,4 +1,4 @@ - + diff --git a/packages/ui-components/style/icons/statusbar/not-trusted.svg b/packages/ui-components/style/icons/statusbar/not-trusted.svg new file mode 100644 index 000000000000..4a0936fee37a --- /dev/null +++ b/packages/ui-components/style/icons/statusbar/not-trusted.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/ui-components/style/icons/statusbar/terminal.svg b/packages/ui-components/style/icons/statusbar/terminal.svg new file mode 100644 index 000000000000..e8d49b4e91a1 --- /dev/null +++ b/packages/ui-components/style/icons/statusbar/terminal.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ui-components/style/icons/statusbar/trusted.svg b/packages/ui-components/style/icons/statusbar/trusted.svg new file mode 100644 index 000000000000..8da29db6c18f --- /dev/null +++ b/packages/ui-components/style/icons/statusbar/trusted.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/ui-components/style/index.css b/packages/ui-components/style/index.css index ee417b10abe9..f7e375acb680 100644 --- a/packages/ui-components/style/index.css +++ b/packages/ui-components/style/index.css @@ -5,7 +5,7 @@ /* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */ @import url('~@blueprintjs/core/lib/css/blueprint.css'); -@import url('~@blueprintjs/icons/lib/css/blueprint-icons.css'); @import url('~@blueprintjs/select/lib/css/blueprint-select.css'); +@import url('~@phosphor/widgets/style/index.css'); @import url('./base.css'); diff --git a/packages/ui-components/tdoptions.json b/packages/ui-components/tdoptions.json index 60d100d16c84..330558b24eac 100644 --- a/packages/ui-components/tdoptions.json +++ b/packages/ui-components/tdoptions.json @@ -7,7 +7,8 @@ "lib.es2015.d.ts", "lib.es2015.collection.d.ts", "lib.es2015.promise.d.ts", - "lib.dom.d.ts" + "lib.dom.d.ts", + "lib.dom.iterable.d.ts" ], "out": "../../docs/api/ui-components", "baseUrl": ".", @@ -16,5 +17,5 @@ }, "esModuleInterop": true, "jsx": "react", - "types": ["webpack-env"] + "types": ["webpack-env", "node"] } diff --git a/packages/ui-components/tsconfig.json b/packages/ui-components/tsconfig.json index b25ed53167d9..3460c40ab1d8 100644 --- a/packages/ui-components/tsconfig.json +++ b/packages/ui-components/tsconfig.json @@ -2,9 +2,13 @@ "extends": "../../tsconfigbase", "compilerOptions": { "outDir": "lib", - "types": ["webpack-env"], + "types": ["webpack-env", "node"], "rootDir": "src" }, - "include": ["src/*"], - "references": [] + "include": ["src/**/*"], + "references": [ + { + "path": "../coreutils" + } + ] } diff --git a/packages/vdom-extension/src/index.ts b/packages/vdom-extension/src/index.ts index 8a781ee03d37..8b009b2cf1cd 100644 --- a/packages/vdom-extension/src/index.ts +++ b/packages/vdom-extension/src/index.ts @@ -18,9 +18,9 @@ import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; import { RenderedVDOM, IVDOMTracker } from '@jupyterlab/vdom'; /** - * The CSS class for a VDOM icon. + * The name for a VDOM icon. */ -const CSS_ICON_CLASS = 'jp-MaterialIcon jp-VDOMIcon'; +const ICON_NAME = 'react'; /** * The MIME type for VDOM. @@ -82,7 +82,7 @@ const plugin: JupyterFrontEndPlugin = { name: 'vdom', mimeTypes: [MIME_TYPE], extensions: ['.vdom', '.vdom.json'], - iconClass: CSS_ICON_CLASS + iconClass: ICON_NAME }); const factory = new MimeDocumentFactory({ diff --git a/packages/vdom-extension/style/base.css b/packages/vdom-extension/style/base.css deleted file mode 100644 index 3aa74aba24ad..000000000000 --- a/packages/vdom-extension/style/base.css +++ /dev/null @@ -1,29 +0,0 @@ -/** - Copyright (c) Jupyter Development Team. - Distributed under the terms of the Modified BSD License. -*/ - -/* Add CSS variables to :root */ -:root { - --jp-icon-vdom: url('./react.svg'); -} - -/* Base styles */ -.jp-RenderedVDOM { - width: 100%; - height: 100%; - padding: 0; - overflow: auto; -} - -/* Document styles */ -.jp-MimeDocument .jp-RenderedVDOM { - padding: 5px; -} - -/* Document icon */ -.jp-VDOMIcon { - background-image: var(--jp-icon-vdom); - background-size: 24px; - background-position: center !important; -} diff --git a/packages/vdom-extension/style/index.css b/packages/vdom-extension/style/index.css index c6533b9403f0..6300595458a5 100644 --- a/packages/vdom-extension/style/index.css +++ b/packages/vdom-extension/style/index.css @@ -10,5 +10,3 @@ @import url('~@jupyterlab/application/style/index.css'); @import url('~@jupyterlab/notebook/style/index.css'); @import url('~@jupyterlab/vdom/style/index.css'); - -@import url('./base.css'); diff --git a/packages/vdom-extension/style/react.svg b/packages/vdom-extension/style/react.svg deleted file mode 100644 index 5592ebec6d60..000000000000 --- a/packages/vdom-extension/style/react.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - diff --git a/packages/vdom/style/base.css b/packages/vdom/style/base.css index 3aa74aba24ad..850eccd402f0 100644 --- a/packages/vdom/style/base.css +++ b/packages/vdom/style/base.css @@ -3,11 +3,6 @@ Distributed under the terms of the Modified BSD License. */ -/* Add CSS variables to :root */ -:root { - --jp-icon-vdom: url('./react.svg'); -} - /* Base styles */ .jp-RenderedVDOM { width: 100%; @@ -20,10 +15,3 @@ .jp-MimeDocument .jp-RenderedVDOM { padding: 5px; } - -/* Document icon */ -.jp-VDOMIcon { - background-image: var(--jp-icon-vdom); - background-size: 24px; - background-position: center !important; -} diff --git a/packages/vdom/style/react.svg b/packages/vdom/style/react.svg deleted file mode 100644 index 5592ebec6d60..000000000000 --- a/packages/vdom/style/react.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - diff --git a/tests/test-filebrowser/package.json b/tests/test-filebrowser/package.json index e198a68a1c9a..e04486bc16e4 100644 --- a/tests/test-filebrowser/package.json +++ b/tests/test-filebrowser/package.json @@ -22,6 +22,7 @@ "@jupyterlab/filebrowser": "^1.1.0-alpha.1", "@jupyterlab/services": "^4.1.0-alpha.1", "@jupyterlab/testutils": "^1.1.0-alpha.1", + "@jupyterlab/ui-components": "^1.1.0-alpha.1", "@phosphor/algorithm": "^1.1.3", "@phosphor/coreutils": "^1.3.1", "@phosphor/messaging": "^1.2.3", diff --git a/tests/test-filebrowser/src/crumbs.spec.ts b/tests/test-filebrowser/src/crumbs.spec.ts index df06b2607ee6..8ce03e54c59a 100644 --- a/tests/test-filebrowser/src/crumbs.spec.ts +++ b/tests/test-filebrowser/src/crumbs.spec.ts @@ -7,19 +7,23 @@ import { DocumentManager, IDocumentManager } from '@jupyterlab/docmanager'; import { DocumentRegistry, TextModelFactory } from '@jupyterlab/docregistry'; +import { BreadCrumbs, FileBrowserModel } from '@jupyterlab/filebrowser'; + import { ServiceManager } from '@jupyterlab/services'; +import { framePromise, signalToPromise } from '@jupyterlab/testutils'; + +import { defaultIconRegistry, IIconRegistry } from '@jupyterlab/ui-components'; + import { Message, MessageLoop } from '@phosphor/messaging'; import { Widget } from '@phosphor/widgets'; import { simulate } from 'simulate-event'; -import { BreadCrumbs, FileBrowserModel } from '@jupyterlab/filebrowser'; - -import { framePromise, signalToPromise } from '@jupyterlab/testutils'; - +const HOME_ITEM_CLASS = 'jp-BreadCrumbs-home'; const ITEM_CLASS = 'jp-BreadCrumbs-item'; +const ITEM_QUERY = `.${HOME_ITEM_CLASS}, .${ITEM_CLASS}`; class LogCrumbs extends BreadCrumbs { methods: string[] = []; @@ -47,6 +51,7 @@ class LogCrumbs extends BreadCrumbs { } describe('filebrowser/model', () => { + let iconRegistry: IIconRegistry; let manager: IDocumentManager; let serviceManager: ServiceManager.IManager; let registry: DocumentRegistry; @@ -68,6 +73,7 @@ describe('filebrowser/model', () => { textModelFactory: new TextModelFactory() }); serviceManager = new ServiceManager({ standby: 'never' }); + iconRegistry = defaultIconRegistry; manager = new DocumentManager({ registry, opener, @@ -91,7 +97,7 @@ describe('filebrowser/model', () => { }); beforeEach(async () => { - model = new FileBrowserModel({ manager }); + model = new FileBrowserModel({ iconRegistry, manager }); await model.cd(path); crumbs = new LogCrumbs({ model }); }); @@ -105,7 +111,7 @@ describe('filebrowser/model', () => { it('should create a new BreadCrumbs instance', () => { const bread = new BreadCrumbs({ model }); expect(bread).to.be.an.instanceof(BreadCrumbs); - const items = crumbs.node.getElementsByClassName(ITEM_CLASS); + const items = crumbs.node.querySelectorAll(ITEM_QUERY); expect(items.length).to.equal(1); }); @@ -119,26 +125,26 @@ describe('filebrowser/model', () => { it('should switch to the parent directory', async () => { Widget.attach(crumbs, document.body); MessageLoop.sendMessage(crumbs, Widget.Msg.UpdateRequest); - let items = crumbs.node.getElementsByClassName(ITEM_CLASS); + let items = crumbs.node.querySelectorAll(ITEM_QUERY); expect(items.length).to.equal(4); const promise = signalToPromise(model.pathChanged); expect(items[2].textContent).to.equal(second); simulate(items[2], 'click'); await promise; MessageLoop.sendMessage(crumbs, Widget.Msg.UpdateRequest); - items = crumbs.node.getElementsByClassName(ITEM_CLASS); + items = crumbs.node.querySelectorAll(ITEM_QUERY); expect(items.length).to.equal(3); }); it('should switch to the home directory', async () => { Widget.attach(crumbs, document.body); MessageLoop.sendMessage(crumbs, Widget.Msg.UpdateRequest); - let items = crumbs.node.getElementsByClassName(ITEM_CLASS); + let items = crumbs.node.querySelectorAll(ITEM_QUERY); const promise = signalToPromise(model.pathChanged); simulate(items[0], 'click'); await promise; MessageLoop.sendMessage(crumbs, Widget.Msg.UpdateRequest); - items = crumbs.node.getElementsByClassName(ITEM_CLASS); + items = crumbs.node.querySelectorAll(ITEM_QUERY); expect(items.length).to.equal(1); expect(model.path).to.equal(''); }); @@ -146,12 +152,12 @@ describe('filebrowser/model', () => { it('should switch to the grandparent directory', async () => { Widget.attach(crumbs, document.body); MessageLoop.sendMessage(crumbs, Widget.Msg.UpdateRequest); - let items = crumbs.node.getElementsByClassName(ITEM_CLASS); + let items = crumbs.node.querySelectorAll(ITEM_QUERY); const promise = signalToPromise(model.pathChanged); simulate(items[1], 'click'); await promise; MessageLoop.sendMessage(crumbs, Widget.Msg.UpdateRequest); - items = crumbs.node.getElementsByClassName(ITEM_CLASS); + items = crumbs.node.querySelectorAll(ITEM_QUERY); expect(items.length).to.equal(2); expect(model.path).to.equal(first); }); @@ -159,13 +165,13 @@ describe('filebrowser/model', () => { it('should refresh the current directory', async () => { Widget.attach(crumbs, document.body); MessageLoop.sendMessage(crumbs, Widget.Msg.UpdateRequest); - let items = crumbs.node.getElementsByClassName(ITEM_CLASS); + let items = crumbs.node.querySelectorAll(ITEM_QUERY); const promise = signalToPromise(model.refreshed); expect(items[3].textContent).to.equal(third); simulate(items[3], 'click'); await promise; MessageLoop.sendMessage(crumbs, Widget.Msg.UpdateRequest); - items = crumbs.node.getElementsByClassName(ITEM_CLASS); + items = crumbs.node.querySelectorAll(ITEM_QUERY); expect(items.length).to.equal(4); expect(model.path).to.equal(path); }); @@ -198,14 +204,14 @@ describe('filebrowser/model', () => { describe('#onUpdateRequest()', () => { it('should be called when the model updates', async () => { - const model = new FileBrowserModel({ manager }); + const model = new FileBrowserModel({ iconRegistry, manager }); await model.cd(path); crumbs = new LogCrumbs({ model }); await model.cd('..'); await framePromise(); expect(crumbs.methods).to.contain('onUpdateRequest'); - const items = crumbs.node.getElementsByClassName(ITEM_CLASS); + const items = crumbs.node.querySelectorAll(ITEM_QUERY); expect(items.length).to.equal(3); model.dispose(); }); diff --git a/tests/test-filebrowser/src/model.spec.ts b/tests/test-filebrowser/src/model.spec.ts index b9886109012b..cd2542ba0c79 100644 --- a/tests/test-filebrowser/src/model.spec.ts +++ b/tests/test-filebrowser/src/model.spec.ts @@ -11,24 +11,27 @@ import { DocumentManager, IDocumentManager } from '@jupyterlab/docmanager'; import { DocumentRegistry, TextModelFactory } from '@jupyterlab/docregistry'; -import { - Contents, - ContentsManager, - ServiceManager -} from '@jupyterlab/services'; - import { FileBrowserModel, LARGE_FILE_SIZE, CHUNK_SIZE } from '@jupyterlab/filebrowser'; +import { + Contents, + ContentsManager, + ServiceManager +} from '@jupyterlab/services'; + import { acceptDialog, dismissDialog, signalToPromises, sleep } from '@jupyterlab/testutils'; + +import { defaultIconRegistry, IIconRegistry } from '@jupyterlab/ui-components'; + import { toArray } from '@phosphor/algorithm'; /** @@ -55,6 +58,7 @@ class DelayedContentsManager extends ContentsManager { } describe('filebrowser/model', () => { + let iconRegistry: IIconRegistry; let manager: IDocumentManager; let serviceManager: ServiceManager.IManager; let registry: DocumentRegistry; @@ -73,6 +77,7 @@ describe('filebrowser/model', () => { textModelFactory: new TextModelFactory() }); serviceManager = new ServiceManager({ standby: 'never' }); + iconRegistry = defaultIconRegistry; manager = new DocumentManager({ registry, opener, @@ -83,7 +88,7 @@ describe('filebrowser/model', () => { beforeEach(async () => { await state.clear(); - model = new FileBrowserModel({ manager, state }); + model = new FileBrowserModel({ iconRegistry, manager, state }); const contents = await manager.newUntitled({ type: 'file' }); name = contents.name; return model.cd(); @@ -96,7 +101,7 @@ describe('filebrowser/model', () => { describe('FileBrowserModel', () => { describe('#constructor()', () => { it('should construct a new file browser model', () => { - model = new FileBrowserModel({ manager }); + model = new FileBrowserModel({ iconRegistry, manager }); expect(model).to.be.an.instanceof(FileBrowserModel); }); }); @@ -260,7 +265,7 @@ describe('filebrowser/model', () => { opener, manager: delayedServiceManager }); - model = new FileBrowserModel({ manager, state }); // Should delay 1000ms + model = new FileBrowserModel({ iconRegistry, manager, state }); // Should delay 1000ms // An initial refresh is called in the constructor. // If it is too slow, it can come in after the directory change, @@ -280,7 +285,7 @@ describe('filebrowser/model', () => { describe('#restore()', () => { it('should restore based on ID', async () => { const id = 'foo'; - const model2 = new FileBrowserModel({ manager, state }); + const model2 = new FileBrowserModel({ iconRegistry, manager, state }); await model.restore(id); await model.cd('src'); expect(model.path).to.equal('src'); @@ -292,7 +297,7 @@ describe('filebrowser/model', () => { it('should be safe to call multiple times', async () => { const id = 'bar'; - const model2 = new FileBrowserModel({ manager, state }); + const model2 = new FileBrowserModel({ iconRegistry, manager, state }); await model.restore(id); await model.cd('src'); expect(model.path).to.equal('src'); diff --git a/tests/test-filebrowser/tsconfig.json b/tests/test-filebrowser/tsconfig.json index bae9b844966a..fe415b7aed6a 100644 --- a/tests/test-filebrowser/tsconfig.json +++ b/tests/test-filebrowser/tsconfig.json @@ -25,6 +25,9 @@ }, { "path": "../../testutils" + }, + { + "path": "../../packages/ui-components" } ] } diff --git a/yarn.lock b/yarn.lock index 252bdcf304e0..e1caed24660d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -641,7 +641,7 @@ resize-observer-polyfill "^1.5.0" tslib "^1.9.0" -"@blueprintjs/icons@^3.3.0", "@blueprintjs/icons@^3.8.0": +"@blueprintjs/icons@^3.8.0": version "3.9.0" resolved "https://registry.yarnpkg.com/@blueprintjs/icons/-/icons-3.9.0.tgz#173f70b0d6a573d2a97066c54b4d110ffadeee51" integrity sha512-kq1Bh6PtOF4PcuxcDme8NmnSlkfO0IV89FriZGo6zSA1+OOzSwzvoKqa6S7vJe8xCPPLO5r7lE9AjeOuGeH97g== @@ -2018,6 +2018,11 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/prettier@^1.16.4": + version "1.18.0" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.18.0.tgz#d2dbe4d5f76b455138f13a2d881278e2c06a733d" + integrity sha512-5N6WK/XXs9PLPpge2KOmOSaIym2vIo32GsrxM5YOFs7uZ8R9L/acg+hQzWsfwoHEpasqQkH0+3LzLTbiF1GFLQ== + "@types/prop-types@*": version "15.7.1" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6" @@ -7617,7 +7622,7 @@ karma-ie-launcher@^1.0.0: resolved "https://registry.yarnpkg.com/karma-ie-launcher/-/karma-ie-launcher-1.0.0.tgz#497986842c490190346cd89f5494ca9830c6d59c" integrity sha1-SXmGhCxJAZA0bNifVJTKmDDG1Zw= dependencies: - lodash "^4.6.2" + lodash "^4.6.1" karma-mocha-reporter@^2.2.5: version "2.2.5" @@ -9723,7 +9728,7 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^1.17.0: +prettier@^1.17.0, prettier@^1.18.2: version "1.18.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw== @@ -10689,7 +10694,7 @@ sanitize-html@~1.20.1: lodash.escaperegexp "^4.1.2" lodash.isplainobject "^4.0.6" lodash.isstring "^4.0.1" - lodash.mergewith "^4.6.2" + lodash.mergewith "^4.6.1" postcss "^7.0.5" srcset "^1.0.0" xtend "^4.0.1"