From 0a009f28eff6b196a81713d493c71cacbb9a2d43 Mon Sep 17 00:00:00 2001 From: Jason Grout Date: Mon, 16 Sep 2019 14:07:53 -0700 Subject: [PATCH] Backport PR #7192: progress on icons: added inline svg icon support to toolbar buttons (cherry picked from commit 0a989e6ed5dc7326eed44a621a2d12f537e0d3e6) --- buildutils/src/ensure-package.ts | 65 ++++++++++------- packages/application/style/icons.css | 32 --------- packages/apputils/src/toolbar.tsx | 14 ++-- packages/filebrowser/src/listing.ts | 15 ++-- packages/launcher/src/index.tsx | 15 ++-- packages/statusbar/src/deprecated.tsx | 52 ++++++++++++++ packages/statusbar/src/index.ts | 6 +- packages/statusbar/src/style/icon.ts | 15 ---- packages/statusbar/src/style/variables.ts | 3 - .../style/icons/md/copy.svg | 6 -- .../style/icons/md/cut.svg | 7 -- .../style/icons/md/ic_add_24px.svg | 1 - .../style/icons/md/ic_refresh_18px.svg | 1 - .../style/icons/md/paste.svg | 4 -- .../style/icons/md/run.svg | 4 -- .../style/icons/md/save.svg | 4 -- .../style/icons/md/stop.svg | 4 -- packages/theme-dark-extension/style/urls.css | 8 --- .../style/icons/md/copy.svg | 6 -- .../style/icons/md/cut.svg | 7 -- .../style/icons/md/ic_add_24px.svg | 1 - .../style/icons/md/ic_refresh_18px.svg | 1 - .../style/icons/md/paste.svg | 4 -- .../style/icons/md/run.svg | 4 -- .../style/icons/md/save.svg | 4 -- .../style/icons/md/stop.svg | 4 -- packages/theme-light-extension/style/urls.css | 8 --- .../ui-components/src/icon/iconimports.ts | 18 ++++- .../ui-components/src/icon/iconregistry.tsx | 71 ++++++++++++++++--- packages/ui-components/src/icon/interfaces.ts | 36 ++++++++++ packages/ui-components/src/style/icon.ts | 8 +++ packages/ui-components/style/deprecated.css | 32 +++++++++ .../ui-components/style/icons/toolbar/add.svg | 5 ++ .../style/icons/toolbar/copy.svg | 8 +++ .../ui-components/style/icons/toolbar/cut.svg | 9 +++ .../style/icons/toolbar/paste.svg | 6 ++ .../style/icons/toolbar/refresh.svg | 5 ++ .../ui-components/style/icons/toolbar/run.svg | 6 ++ .../style/icons/toolbar/save.svg | 6 ++ .../style/icons/toolbar/stop.svg | 6 ++ tests/test-apputils/src/toolbar.spec.ts | 8 +-- .../test-notebook/src/default-toolbar.spec.ts | 24 +++---- 42 files changed, 342 insertions(+), 201 deletions(-) create mode 100644 packages/statusbar/src/deprecated.tsx delete mode 100644 packages/statusbar/src/style/icon.ts delete mode 100644 packages/theme-dark-extension/style/icons/md/copy.svg delete mode 100644 packages/theme-dark-extension/style/icons/md/cut.svg delete mode 100644 packages/theme-dark-extension/style/icons/md/ic_add_24px.svg delete mode 100644 packages/theme-dark-extension/style/icons/md/ic_refresh_18px.svg delete mode 100644 packages/theme-dark-extension/style/icons/md/paste.svg delete mode 100644 packages/theme-dark-extension/style/icons/md/run.svg delete mode 100644 packages/theme-dark-extension/style/icons/md/save.svg delete mode 100644 packages/theme-dark-extension/style/icons/md/stop.svg delete mode 100644 packages/theme-light-extension/style/icons/md/copy.svg delete mode 100644 packages/theme-light-extension/style/icons/md/cut.svg delete mode 100644 packages/theme-light-extension/style/icons/md/ic_add_24px.svg delete mode 100644 packages/theme-light-extension/style/icons/md/ic_refresh_18px.svg delete mode 100644 packages/theme-light-extension/style/icons/md/paste.svg delete mode 100644 packages/theme-light-extension/style/icons/md/run.svg delete mode 100644 packages/theme-light-extension/style/icons/md/save.svg delete mode 100644 packages/theme-light-extension/style/icons/md/stop.svg create mode 100644 packages/ui-components/style/icons/toolbar/add.svg create mode 100644 packages/ui-components/style/icons/toolbar/copy.svg create mode 100644 packages/ui-components/style/icons/toolbar/cut.svg create mode 100644 packages/ui-components/style/icons/toolbar/paste.svg create mode 100644 packages/ui-components/style/icons/toolbar/refresh.svg create mode 100644 packages/ui-components/style/icons/toolbar/run.svg create mode 100644 packages/ui-components/style/icons/toolbar/save.svg create mode 100644 packages/ui-components/style/icons/toolbar/stop.svg diff --git a/buildutils/src/ensure-package.ts b/buildutils/src/ensure-package.ts index f4665ada0cd4..04999b0c5cf0 100644 --- a/buildutils/src/ensure-package.ts +++ b/buildutils/src/ensure-package.ts @@ -330,10 +330,15 @@ export async function ensurePackage( * of ui-components/style/icons. * * @param pkgPath - The path to the @jupyterlab/ui-components package. + * @param dorequire - If true, use `require` function in place of `import` + * statements when loading the icon svg files * * @returns A list of changes that were made to ensure the package. */ -export async function ensureUiComponents(pkgPath: string): Promise { +export async function ensureUiComponents( + pkgPath: string, + dorequire: boolean = false +): Promise { const funcName = 'ensureUiComponents'; let messages: string[] = []; @@ -347,14 +352,23 @@ export async function ensureUiComponents(pkgPath: string): Promise { 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) - .split(path.sep) - .join('/')}';` - ); - _iconModelDeclarations.push(`{ name: '${name}', svg: ${nameCamel} }`); + const svgpath = path + .relative(iconSrcDir, svg) + .split(path.sep) + .join('/'); + + if (dorequire) { + // load the icon svg using `require` + _iconModelDeclarations.push( + `{ name: '${name}', svg: require('${svgpath}').default }` + ); + } else { + // load the icon svg using `import` + const nameCamel = utils.camelCase(name) + 'Svg'; + + _iconImportStatements.push(`import ${nameCamel} from '${svgpath}';`); + _iconModelDeclarations.push(`{ name: '${name}', svg: ${nameCamel} }`); + } }); const iconImportStatements = _iconImportStatements.join('\n'); const iconModelDeclarations = _iconModelDeclarations.join(',\n'); @@ -457,7 +471,7 @@ export interface IEnsurePackageOptions { * 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, + * @param fpath: The path to the file being checked. The file must exist, * or else this function does nothing. * * @param contents: The desired file contents. @@ -469,35 +483,36 @@ export interface IEnsurePackageOptions { * @returns a string array with 0 or 1 messages. */ function ensureFile( - path: string, + fpath: string, contents: string, prettify: boolean = true ): string[] { let messages: string[] = []; - if (!fs.existsSync(path)) { + if (!fs.existsSync(fpath)) { // bail messages.push( - `Tried to ensure the contents of ./${path}, but the file does not exist` + `Tried to ensure the contents of ${fpath}, 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 }); - } + // (maybe) run the newly generated contents through prettier before comparing + let formatted = prettify + ? prettier.format(contents, { filepath: fpath, singleQuote: true }) + : contents; - const prev = fs.readFileSync(path, { - encoding: 'utf8' - }); - // Normalize line endings to match current content + const prev = fs.readFileSync(fpath, { encoding: 'utf8' }); if (prev.indexOf('\r') !== -1) { - contents = contents.replace(/\n/g, '\r\n'); + // Normalize line endings to match current content + formatted = formatted.replace(/\n/g, '\r\n'); } - if (prev !== contents) { - fs.writeFileSync(path, contents); - messages.push(`Updated ./${path}`); + if (prev !== formatted) { + // Write out changes and notify + fs.writeFileSync(fpath, formatted); + + const msgpath = fpath.startsWith('/') ? fpath : `./${fpath}`; + messages.push(`Updated ${msgpath}`); } return messages; diff --git a/packages/application/style/icons.css b/packages/application/style/icons.css index a21e91197379..569cd0e4b676 100644 --- a/packages/application/style/icons.css +++ b/packages/application/style/icons.css @@ -237,10 +237,6 @@ background-size: 20px; } -.jp-AddIcon { - background-image: var(--jp-icon-add); -} - .jp-BugIcon { background-image: var(--jp-icon-bug); } @@ -261,14 +257,6 @@ background-image: var(--jp-icon-console); } -.jp-CopyIcon { - background-image: var(--jp-icon-copy); -} - -.jp-CutIcon { - background-image: var(--jp-icon-cut); -} - .jp-DirectionsRunIcon { background-image: var(--jp-icon-directions-run); } @@ -355,30 +343,10 @@ background-image: var(--jp-icon-new-folder); } -.jp-PasteIcon { - background-image: var(--jp-icon-paste); -} - -.jp-RefreshIcon { - background-image: var(--jp-icon-refresh); -} - -.jp-RunIcon { - background-image: var(--jp-icon-run); -} - -.jp-SaveIcon { - background-image: var(--jp-icon-save); -} - .jp-SettingsIcon { background-image: var(--jp-icon-settings); } -.jp-StopIcon { - background-image: var(--jp-icon-stop); -} - .jp-TextEditorIcon { background-image: var(--jp-icon-text-editor); } diff --git a/packages/apputils/src/toolbar.tsx b/packages/apputils/src/toolbar.tsx index 31514550d848..f614b866a1f7 100644 --- a/packages/apputils/src/toolbar.tsx +++ b/packages/apputils/src/toolbar.tsx @@ -5,7 +5,7 @@ import { UseSignal, ReactWidget } from './vdom'; import { Kernel } from '@jupyterlab/services'; -import { Button } from '@jupyterlab/ui-components'; +import { Button, DefaultIconReact } from '@jupyterlab/ui-components'; import { IIterator, find, map, some } from '@phosphor/algorithm'; @@ -468,11 +468,13 @@ export function ToolbarButtonComponent(props: ToolbarButtonComponent.IProps) { minimal > {props.iconClassName && ( - )} {props.label && ( diff --git a/packages/filebrowser/src/listing.ts b/packages/filebrowser/src/listing.ts index 2fa7e151195e..de49962a0644 100644 --- a/packages/filebrowser/src/listing.ts +++ b/packages/filebrowser/src/listing.ts @@ -1765,22 +1765,23 @@ export namespace DirListing { let modified = DOMUtils.findElement(node, ITEM_MODIFIED_CLASS); if (fileType) { - // add icon as svg node. Can be styled using CSS - if ( - !this._iconRegistry.icon({ + // TODO: remove workaround if...else/code in else clause in v2.0.0 + // workaround for 1.0.x versions of Jlab pulling in 1.1.x versions of filebrowser + if (this._iconRegistry) { + // add icon as svg node. Can be styled using CSS + this._iconRegistry.icon({ name: fileType.iconClass, className: ITEM_ICON_CLASS, title: fileType.iconLabel, + fallback: true, container: icon, center: true, kind: 'listing' - }) - ) { + }); + } else { // add icon as CSS background image. Can't be styled using CSS icon.className = `${ITEM_ICON_CLASS} ${fileType.iconClass || ''}`; icon.textContent = fileType.iconLabel || ''; - // clean up the svg icon annotation, if any - delete icon.dataset.icon; } } else { // use default icon as CSS background image diff --git a/packages/launcher/src/index.tsx b/packages/launcher/src/index.tsx index 021fefa89f76..0e0f2abb029c 100644 --- a/packages/launcher/src/index.tsx +++ b/packages/launcher/src/index.tsx @@ -425,16 +425,15 @@ function Card( )} - ) : defaultIconRegistry.contains(iconClass) ? ( - ) : (
-
+
)}
diff --git a/packages/statusbar/src/deprecated.tsx b/packages/statusbar/src/deprecated.tsx new file mode 100644 index 000000000000..8be4627b2551 --- /dev/null +++ b/packages/statusbar/src/deprecated.tsx @@ -0,0 +1,52 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ + +/* This file is a workaround for 1.0.x versions of Jlab pulling in 1.1.x +versions of statusbar. +TODO: delete this file in Jlab 2.0 +*/ + +import * as React from 'react'; + +import { classes, style } from 'typestyle/lib'; + +import { NestedCSSProperties } from 'typestyle/lib/types'; + +const icon = (): NestedCSSProperties => { + return { + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', + backgroundSize: '18px', + minHeight: '24px', + width: '20px' + }; +}; + +/** + * (DEPRECATED) A namespace for IconItem statics. + */ +export namespace IconItem { + /** + * Props for an IconItem + */ + export interface IProps { + /** + * A CSS class name for the icon. + */ + source: string; + } +} + +/** + * (DEPRECATED) 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/index.ts b/packages/statusbar/src/index.ts index 4455c9b240cc..60ebbd28c883 100644 --- a/packages/statusbar/src/index.ts +++ b/packages/statusbar/src/index.ts @@ -3,8 +3,10 @@ | Distributed under the terms of the Modified BSD License. |----------------------------------------------------------------------------*/ -export * from './statusbar'; -export * from './style/statusbar'; export * from './components'; export * from './defaults'; +export * from './style/statusbar'; + +export * from './deprecated'; +export * from './statusbar'; export * from './tokens'; diff --git a/packages/statusbar/src/style/icon.ts b/packages/statusbar/src/style/icon.ts deleted file mode 100644 index f65da7124bd1..000000000000 --- a/packages/statusbar/src/style/icon.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Jupyter Development Team. -// Distributed under the terms of the Modified BSD License. - -import vars from './variables'; -import { NestedCSSProperties } from 'typestyle/lib/types'; - -export default (): NestedCSSProperties => { - return { - backgroundRepeat: 'no-repeat', - backgroundPosition: 'center', - backgroundSize: vars.iconImageSize, - minHeight: vars.height, - width: vars.iconWidth - }; -}; diff --git a/packages/statusbar/src/style/variables.ts b/packages/statusbar/src/style/variables.ts index 740034fd49aa..8ae02561a2ab 100644 --- a/packages/statusbar/src/style/variables.ts +++ b/packages/statusbar/src/style/variables.ts @@ -12,9 +12,6 @@ export default { textClickColor: 'white', itemMargin: '2px', itemPadding: '6px', - textIconHalfSpacing: '3px', statusBarPadding: '10px', - iconImageSize: '18px', - iconWidth: '20px', interItemHalfSpacing: '2px' // this amount accounts for half the spacing between items }; diff --git a/packages/theme-dark-extension/style/icons/md/copy.svg b/packages/theme-dark-extension/style/icons/md/copy.svg deleted file mode 100644 index e1b59759d309..000000000000 --- a/packages/theme-dark-extension/style/icons/md/copy.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/packages/theme-dark-extension/style/icons/md/cut.svg b/packages/theme-dark-extension/style/icons/md/cut.svg deleted file mode 100644 index 1e3351b9dc12..000000000000 --- a/packages/theme-dark-extension/style/icons/md/cut.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/packages/theme-dark-extension/style/icons/md/ic_add_24px.svg b/packages/theme-dark-extension/style/icons/md/ic_add_24px.svg deleted file mode 100644 index cafb68718402..000000000000 --- a/packages/theme-dark-extension/style/icons/md/ic_add_24px.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/theme-dark-extension/style/icons/md/ic_refresh_18px.svg b/packages/theme-dark-extension/style/icons/md/ic_refresh_18px.svg deleted file mode 100644 index ee8a04ac9bc4..000000000000 --- a/packages/theme-dark-extension/style/icons/md/ic_refresh_18px.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/theme-dark-extension/style/icons/md/paste.svg b/packages/theme-dark-extension/style/icons/md/paste.svg deleted file mode 100644 index 0d7db4321fa6..000000000000 --- a/packages/theme-dark-extension/style/icons/md/paste.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/theme-dark-extension/style/icons/md/run.svg b/packages/theme-dark-extension/style/icons/md/run.svg deleted file mode 100644 index bce5f5218573..000000000000 --- a/packages/theme-dark-extension/style/icons/md/run.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/theme-dark-extension/style/icons/md/save.svg b/packages/theme-dark-extension/style/icons/md/save.svg deleted file mode 100644 index a7656f7c7755..000000000000 --- a/packages/theme-dark-extension/style/icons/md/save.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/theme-dark-extension/style/icons/md/stop.svg b/packages/theme-dark-extension/style/icons/md/stop.svg deleted file mode 100644 index 206aee20908e..000000000000 --- a/packages/theme-dark-extension/style/icons/md/stop.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/theme-dark-extension/style/urls.css b/packages/theme-dark-extension/style/urls.css index fde04072bfbe..fc1e2f5ba40d 100644 --- a/packages/theme-dark-extension/style/urls.css +++ b/packages/theme-dark-extension/style/urls.css @@ -30,7 +30,6 @@ --jp-about-header-logo: url('icons/jupyter/jupyter.svg'); --jp-about-header-wordmark: url('images/jupyterlab-wordmark.svg'); - --jp-icon-add: url('icons/md/ic_add_24px.svg'); --jp-icon-bug: url('icons/md/bug.svg'); --jp-icon-caretdown: url('icons/md/caretdown.svg'); --jp-icon-caretleft: url('icons/md/caretleft.svg'); @@ -43,8 +42,6 @@ --jp-icon-close: url('icons/md/close.svg'); --jp-icon-console-selected: url('icons/jupyter/console_selected.svg'); --jp-icon-console: url('icons/jupyter/console.svg'); - --jp-icon-copy: url('icons/md/copy.svg'); - --jp-icon-cut: url('icons/md/cut.svg'); --jp-icon-download: url('icons/md/download.svg'); --jp-icon-edit: url('icons/md/edit.svg'); --jp-icon-ellipses: url('icons/md/ellipses.svg'); @@ -64,10 +61,6 @@ --jp-icon-link: url('icons/md/link.svg'); --jp-icon-more: url('icons/md/more-horiz.svg'); --jp-icon-new-folder: url('icons/md/ic_create_new_folder_24px.svg'); - --jp-icon-paste: url('icons/md/paste.svg'); - --jp-icon-refresh: url('icons/md/ic_refresh_18px.svg'); - --jp-icon-run: url('icons/md/run.svg'); - --jp-icon-save: url('icons/md/save.svg'); --jp-icon-search-white: url('icons/md/search.svg'); --jp-icon-search: url('icons/md/search.svg'); --jp-icon-search-arrow-up: url('icons/jupyter/search_arrow_up.svg'); @@ -76,7 +69,6 @@ --jp-icon-search-regex: url('icons/jupyter/search_regex.svg'); --jp-icon-settings-selected: url('icons/md/ic_settings_24px_selected.svg'); --jp-icon-settings: url('icons/md/ic_settings_24px.svg'); - --jp-icon-stop: url('icons/md/stop.svg'); --jp-icon-stop-circle: url('icons/md/stop-circle.svg'); --jp-icon-text-editor-selected: url('icons/md/ic_format_align_left_24px_selected.svg'); --jp-icon-text-editor: url('icons/md/ic_format_align_left_24px.svg'); diff --git a/packages/theme-light-extension/style/icons/md/copy.svg b/packages/theme-light-extension/style/icons/md/copy.svg deleted file mode 100644 index 61061a9df5b6..000000000000 --- a/packages/theme-light-extension/style/icons/md/copy.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/packages/theme-light-extension/style/icons/md/cut.svg b/packages/theme-light-extension/style/icons/md/cut.svg deleted file mode 100644 index 5a271c806353..000000000000 --- a/packages/theme-light-extension/style/icons/md/cut.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/packages/theme-light-extension/style/icons/md/ic_add_24px.svg b/packages/theme-light-extension/style/icons/md/ic_add_24px.svg deleted file mode 100644 index 2905c503bc5a..000000000000 --- a/packages/theme-light-extension/style/icons/md/ic_add_24px.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/theme-light-extension/style/icons/md/ic_refresh_18px.svg b/packages/theme-light-extension/style/icons/md/ic_refresh_18px.svg deleted file mode 100644 index 0f8143838d94..000000000000 --- a/packages/theme-light-extension/style/icons/md/ic_refresh_18px.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/theme-light-extension/style/icons/md/paste.svg b/packages/theme-light-extension/style/icons/md/paste.svg deleted file mode 100644 index 09a691dc69ca..000000000000 --- a/packages/theme-light-extension/style/icons/md/paste.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/theme-light-extension/style/icons/md/run.svg b/packages/theme-light-extension/style/icons/md/run.svg deleted file mode 100644 index daf8bc93be78..000000000000 --- a/packages/theme-light-extension/style/icons/md/run.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/theme-light-extension/style/icons/md/save.svg b/packages/theme-light-extension/style/icons/md/save.svg deleted file mode 100644 index f2c5385d60dd..000000000000 --- a/packages/theme-light-extension/style/icons/md/save.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/theme-light-extension/style/icons/md/stop.svg b/packages/theme-light-extension/style/icons/md/stop.svg deleted file mode 100644 index 19d1bad72d15..000000000000 --- a/packages/theme-light-extension/style/icons/md/stop.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/theme-light-extension/style/urls.css b/packages/theme-light-extension/style/urls.css index caf70dd34f84..36c5c3841128 100644 --- a/packages/theme-light-extension/style/urls.css +++ b/packages/theme-light-extension/style/urls.css @@ -30,7 +30,6 @@ --jp-about-header-logo: url('icons/jupyter/jupyter.svg'); --jp-about-header-wordmark: url('images/jupyterlab-wordmark.svg'); - --jp-icon-add: url('icons/md/ic_add_24px.svg'); --jp-icon-bug: url('icons/md/bug.svg'); --jp-icon-caretdown: url('icons/md/caretdown.svg'); --jp-icon-caretleft: url('icons/md/caretleft.svg'); @@ -43,8 +42,6 @@ --jp-icon-close: url('icons/md/close.svg'); --jp-icon-console-selected: url('icons/jupyter/console_selected.svg'); --jp-icon-console: url('icons/jupyter/console.svg'); - --jp-icon-copy: url('icons/md/copy.svg'); - --jp-icon-cut: url('icons/md/cut.svg'); --jp-icon-download: url('icons/md/download.svg'); --jp-icon-edit: url('icons/md/edit.svg'); --jp-icon-ellipses: url('icons/md/ellipses.svg'); @@ -64,10 +61,6 @@ --jp-icon-link: url('icons/md/link.svg'); --jp-icon-more: url('icons/md/more-horiz.svg'); --jp-icon-new-folder: url('icons/md/ic_create_new_folder_24px.svg'); - --jp-icon-paste: url('icons/md/paste.svg'); - --jp-icon-refresh: url('icons/md/ic_refresh_18px.svg'); - --jp-icon-run: url('icons/md/run.svg'); - --jp-icon-save: url('icons/md/save.svg'); --jp-icon-search-white: url('icons/md/search-white.svg'); --jp-icon-search: url('icons/md/search.svg'); --jp-icon-search-arrow-up: url('icons/jupyter/search_arrow_up.svg'); @@ -76,7 +69,6 @@ --jp-icon-search-regex: url('icons/jupyter/search_regex.svg'); --jp-icon-settings-selected: url('icons/md/ic_settings_24px_selected.svg'); --jp-icon-settings: url('icons/md/ic_settings_24px.svg'); - --jp-icon-stop: url('icons/md/stop.svg'); --jp-icon-stop-circle: url('icons/md/stop-circle.svg'); --jp-icon-text-editor-selected: url('icons/md/ic_format_align_left_24px_selected.svg'); --jp-icon-text-editor: url('icons/md/ic_format_align_left_24px.svg'); diff --git a/packages/ui-components/src/icon/iconimports.ts b/packages/ui-components/src/icon/iconimports.ts index 3a9151eae78a..c368090d0736 100644 --- a/packages/ui-components/src/icon/iconimports.ts +++ b/packages/ui-components/src/icon/iconimports.ts @@ -31,6 +31,14 @@ 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'; +import addSvg from '../../style/icons/toolbar/add.svg'; +import copySvg from '../../style/icons/toolbar/copy.svg'; +import cutSvg from '../../style/icons/toolbar/cut.svg'; +import pasteSvg from '../../style/icons/toolbar/paste.svg'; +import refreshSvg from '../../style/icons/toolbar/refresh.svg'; +import runSvg from '../../style/icons/toolbar/run.svg'; +import saveSvg from '../../style/icons/toolbar/save.svg'; +import stopSvg from '../../style/icons/toolbar/stop.svg'; // defaultIcons definition export namespace IconImports { @@ -57,6 +65,14 @@ export namespace IconImports { { name: 'line-form', svg: lineFormSvg }, { name: 'not-trusted', svg: notTrustedSvg }, { name: 'terminal', svg: terminalSvg }, - { name: 'trusted', svg: trustedSvg } + { name: 'trusted', svg: trustedSvg }, + { name: 'add', svg: addSvg }, + { name: 'copy', svg: copySvg }, + { name: 'cut', svg: cutSvg }, + { name: 'paste', svg: pasteSvg }, + { name: 'refresh', svg: refreshSvg }, + { name: 'run', svg: runSvg }, + { name: 'save', svg: saveSvg }, + { name: 'stop', svg: stopSvg } ]; } diff --git a/packages/ui-components/src/icon/iconregistry.tsx b/packages/ui-components/src/icon/iconregistry.tsx index 9699a946a4c8..99c1ef2a9152 100644 --- a/packages/ui-components/src/icon/iconregistry.tsx +++ b/packages/ui-components/src/icon/iconregistry.tsx @@ -42,7 +42,7 @@ export class IconRegistry implements IIconRegistry { } contains(name: string): boolean { - return name in this._svg || name in this._classNameToName; + return !!this._resolveName(name); } /** @@ -51,21 +51,47 @@ export class IconRegistry implements IIconRegistry { icon( props: Icon.INodeOptions & { container?: HTMLElement } ): HTMLElement | null { - const { name, className, title, container, ...propsStyle } = props; + const { + name, + className, + title, + fallback, + container, + ...propsStyle + } = props; // we may have been handed a className in place of name let resolvedName = this.resolveName(name); if (!resolvedName) { - // bail if failing silently or icon node is already set + // TODO: remove fallback in jlab 2.0 + if (fallback) { + if (container) { + container.textContent = title || ''; + container.className = classes( + name, + className, + propsStyle ? iconStyleFlat(propsStyle) : '' + ); + return container; + } else { + // the non-container fallback isn't implemented + console.error('unimplemented'); + return null; + } + } + + // bail if failing silently return null; } + + // check if icon element is already set if ( container && container.dataset.icon && container.dataset.icon === resolvedName && container.children[0] ) { - // return the existing icon node + // return the existing icon element return container.children[0] as HTMLElement; } @@ -112,12 +138,27 @@ export class IconRegistry implements IIconRegistry { iconReact( props: Icon.INodeOptions & { tag?: 'div' | 'span' } ): React.ReactElement { - const { name, className, title, tag, ...propsStyle } = props; + const { name, className, title, fallback, tag, ...propsStyle } = props; const Tag = tag || 'div'; // we may have been handed a className in place of name const resolvedName = this.resolveName(name); if (!resolvedName) { + // TODO: remove fallback in jlab 2.0 + if (fallback) { + return ( + + {title || ''} + + ); + } + // bail if failing silently return <>; } @@ -129,6 +170,10 @@ export class IconRegistry implements IIconRegistry { return <>; } + if (title) { + Private.setTitleSvg(svgElement, title); + } + return ( 'jp-FooBarIcon' + */ className?: string; + + /** + * A string containing the html corresponding to an SVG element + */ svg: string; } @@ -56,8 +68,32 @@ export namespace Icon { * The options used when creating an icon node */ export interface INodeOptions extends IIconStyle { + /** + * The icon name. For a 'foo-bar.svg' file, the icon name is 'foo-bar'. + * For backwards compatibility, 'jp-FooBarIcon' is also a valid icon name. + * + * TODO: until Jlab 2.0 + * If fallback is set, the name is added to the className + * of the resulting icon node + */ name: string; + + /** + * Extra classNames, used in addition to the typestyle className + */ className?: string; + + /** + * Icon title + */ title?: string; + + /** + * If true, if icon name resolution fails, fallback to old + * icon handling behavior. + * + * TODO: remove in Jlab 2.0 + */ + fallback?: boolean; } } diff --git a/packages/ui-components/src/style/icon.ts b/packages/ui-components/src/style/icon.ts index b4f9910eec7f..f509f2a0da93 100644 --- a/packages/ui-components/src/style/icon.ts +++ b/packages/ui-components/src/style/icon.ts @@ -26,6 +26,7 @@ export type IconKindType = | 'splash' | 'statusBar' | 'tabManager' + | 'toolbarButton' | 'unset'; export interface IIconStyle extends NestedCSSProperties { @@ -126,6 +127,11 @@ const iconCSSTabManager: NestedCSSProperties = { width: '16px' }; +const iconCSSToolbarButton: NestedCSSProperties = { + height: '16px', + width: '16px' +}; + const iconCSSKind: { [k in IconKindType]: NestedCSSProperties } = { breadCrumb: iconCSSBreadCrumb, dockPanelBar: iconCSSDockPanelBar, @@ -137,6 +143,7 @@ const iconCSSKind: { [k in IconKindType]: NestedCSSProperties } = { splash: iconCSSSplash, statusBar: iconCSSStatusBar, tabManager: iconCSSTabManager, + toolbarButton: iconCSSToolbarButton, unset: {} }; @@ -194,6 +201,7 @@ const containerCSSKind: { [k in IconKindType]: NestedCSSProperties } = { splash: containerCSSSplash, statusBar: {}, tabManager: containerCSSTabManager, + toolbarButton: {}, unset: {} }; diff --git a/packages/ui-components/style/deprecated.css b/packages/ui-components/style/deprecated.css index 70ec78758d03..c813a229be34 100644 --- a/packages/ui-components/style/deprecated.css +++ b/packages/ui-components/style/deprecated.css @@ -35,6 +35,14 @@ --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'); + --jp-icon-add: url('icons/toolbar/add.svg'); + --jp-icon-copy: url('icons/toolbar/copy.svg'); + --jp-icon-cut: url('icons/toolbar/cut.svg'); + --jp-icon-paste: url('icons/toolbar/paste.svg'); + --jp-icon-refresh: url('icons/toolbar/refresh.svg'); + --jp-icon-run: url('icons/toolbar/run.svg'); + --jp-icon-save: url('icons/toolbar/save.svg'); + --jp-icon-stop: url('icons/toolbar/stop.svg'); } /* Icon CSS class declarations */ @@ -108,3 +116,27 @@ .jp-TrustedIcon { background-image: var(--jp-icon-trusted); } +.jp-AddIcon { + background-image: var(--jp-icon-add); +} +.jp-CopyIcon { + background-image: var(--jp-icon-copy); +} +.jp-CutIcon { + background-image: var(--jp-icon-cut); +} +.jp-PasteIcon { + background-image: var(--jp-icon-paste); +} +.jp-RefreshIcon { + background-image: var(--jp-icon-refresh); +} +.jp-RunIcon { + background-image: var(--jp-icon-run); +} +.jp-SaveIcon { + background-image: var(--jp-icon-save); +} +.jp-StopIcon { + background-image: var(--jp-icon-stop); +} diff --git a/packages/ui-components/style/icons/toolbar/add.svg b/packages/ui-components/style/icons/toolbar/add.svg new file mode 100644 index 000000000000..928b83f878a9 --- /dev/null +++ b/packages/ui-components/style/icons/toolbar/add.svg @@ -0,0 +1,5 @@ + + + diff --git a/packages/ui-components/style/icons/toolbar/copy.svg b/packages/ui-components/style/icons/toolbar/copy.svg new file mode 100644 index 000000000000..bebc64a422d9 --- /dev/null +++ b/packages/ui-components/style/icons/toolbar/copy.svg @@ -0,0 +1,8 @@ + + + + diff --git a/packages/ui-components/style/icons/toolbar/cut.svg b/packages/ui-components/style/icons/toolbar/cut.svg new file mode 100644 index 000000000000..5430ef9eee20 --- /dev/null +++ b/packages/ui-components/style/icons/toolbar/cut.svg @@ -0,0 +1,9 @@ + + + diff --git a/packages/ui-components/style/icons/toolbar/paste.svg b/packages/ui-components/style/icons/toolbar/paste.svg new file mode 100644 index 000000000000..47dc31c96706 --- /dev/null +++ b/packages/ui-components/style/icons/toolbar/paste.svg @@ -0,0 +1,6 @@ + + + diff --git a/packages/ui-components/style/icons/toolbar/refresh.svg b/packages/ui-components/style/icons/toolbar/refresh.svg new file mode 100644 index 000000000000..4d86c2788a70 --- /dev/null +++ b/packages/ui-components/style/icons/toolbar/refresh.svg @@ -0,0 +1,5 @@ + + + diff --git a/packages/ui-components/style/icons/toolbar/run.svg b/packages/ui-components/style/icons/toolbar/run.svg new file mode 100644 index 000000000000..91483f031f81 --- /dev/null +++ b/packages/ui-components/style/icons/toolbar/run.svg @@ -0,0 +1,6 @@ + + + diff --git a/packages/ui-components/style/icons/toolbar/save.svg b/packages/ui-components/style/icons/toolbar/save.svg new file mode 100644 index 000000000000..cb3fad009ea2 --- /dev/null +++ b/packages/ui-components/style/icons/toolbar/save.svg @@ -0,0 +1,6 @@ + + + diff --git a/packages/ui-components/style/icons/toolbar/stop.svg b/packages/ui-components/style/icons/toolbar/stop.svg new file mode 100644 index 000000000000..1b12e6331e5a --- /dev/null +++ b/packages/ui-components/style/icons/toolbar/stop.svg @@ -0,0 +1,6 @@ + + + diff --git a/tests/test-apputils/src/toolbar.spec.ts b/tests/test-apputils/src/toolbar.spec.ts index 6f079b9852be..8ff2347957ea 100644 --- a/tests/test-apputils/src/toolbar.spec.ts +++ b/tests/test-apputils/src/toolbar.spec.ts @@ -286,20 +286,20 @@ describe('@jupyterlab/apputils', () => { }); describe('.createInterruptButton()', () => { - it("should have the `'jp-StopIcon'` class", async () => { + it("should add an inline svg node with the 'stop' icon", async () => { const button = Toolbar.createInterruptButton(session); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector('.jp-StopIcon')).to.exist; + expect(button.node.querySelector("[data-icon='stop']")).to.exist; }); }); describe('.createRestartButton()', () => { - it("should have the `'jp-RefreshIcon'` class", async () => { + it("should add an inline svg node with the 'refresh' icon", async () => { const button = Toolbar.createRestartButton(session); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector('.jp-RefreshIcon')).to.exist; + expect(button.node.querySelector("[data-icon='refresh']")).to.exist; }); }); diff --git a/tests/test-notebook/src/default-toolbar.spec.ts b/tests/test-notebook/src/default-toolbar.spec.ts index a80329a14fb5..e895ad6fe413 100644 --- a/tests/test-notebook/src/default-toolbar.spec.ts +++ b/tests/test-notebook/src/default-toolbar.spec.ts @@ -58,11 +58,11 @@ describe('@jupyterlab/notebook', () => { button.dispose(); }); - it("should have the `'jp-SaveIcon'` class", async () => { + it("should add an inline svg node with the 'save' icon", async () => { const button = ToolbarItems.createSaveButton(panel); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector('.jp-SaveIcon')).to.exist; + expect(button.node.querySelector("[data-icon='save']")).to.exist; }); }); @@ -77,11 +77,11 @@ describe('@jupyterlab/notebook', () => { button.dispose(); }); - it("should have the `'jp-AddIcon'` class", async () => { + it("should add an inline svg node with the 'add' icon", async () => { const button = ToolbarItems.createInsertButton(panel); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector('.jp-AddIcon')).to.exist; + expect(button.node.querySelector("[data-icon='add']")).to.exist; button.dispose(); }); }); @@ -100,11 +100,11 @@ describe('@jupyterlab/notebook', () => { button.dispose(); }); - it("should have the `'jp-CutIcon'` class", async () => { + it("should add an inline svg node with the 'cut' icon", async () => { const button = ToolbarItems.createCutButton(panel); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector('.jp-CutIcon')).to.exist; + expect(button.node.querySelector("[data-icon='cut']")).to.exist; button.dispose(); }); }); @@ -123,11 +123,11 @@ describe('@jupyterlab/notebook', () => { button.dispose(); }); - it("should have the `'jp-CopyIcon'` class", async () => { + it("should add an inline svg node with the 'copy' icon", async () => { const button = ToolbarItems.createCopyButton(panel); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector('.jp-CopyIcon')).to.exist; + expect(button.node.querySelector("[data-icon='copy']")).to.exist; button.dispose(); }); }); @@ -145,11 +145,11 @@ describe('@jupyterlab/notebook', () => { button.dispose(); }); - it("should have the `'jp-PasteIcon'` class", async () => { + it("should add an inline svg node with the 'paste' icon", async () => { const button = ToolbarItems.createPasteButton(panel); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector('.jp-PasteIcon')).to.exist; + expect(button.node.querySelector("[data-icon='paste']")).to.exist; button.dispose(); }); }); @@ -270,11 +270,11 @@ describe('@jupyterlab/notebook', () => { await p.promise; }).timeout(30000); // Allow for slower CI - it("should have the `'jp-RunIcon'` class", async () => { + it("should add an inline svg node with the 'run' icon", async () => { const button = ToolbarItems.createRunButton(panel); Widget.attach(button, document.body); await framePromise(); - expect(button.node.querySelector('.jp-RunIcon')).to.exist; + expect(button.node.querySelector("[data-icon='run']")).to.exist; }); }); });