diff --git a/.cleanignore b/.cleanignore index 5692056f792b..2d46c794a336 100644 --- a/.cleanignore +++ b/.cleanignore @@ -20,4 +20,6 @@ @.idea/ # ms IDE stuff +@*.code-workspace +@.history @.vscode diff --git a/.gitignore b/.gitignore index 177d5f9d1439..f09a1eb3f2d3 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,6 @@ junit.xml .idea/ # ms IDE stuff +*.code-workspace +.history .vscode diff --git a/buildutils/src/ensure-package.ts b/buildutils/src/ensure-package.ts index fd953ea9ea81..ae60ec6ada63 100644 --- a/buildutils/src/ensure-package.ts +++ b/buildutils/src/ensure-package.ts @@ -21,13 +21,13 @@ const HEADER_TEMPLATE = ` `; const ICON_IMPORTS_TEMPLATE = ` -import { JLIcon } from './jlicon'; +import { LabIcon } from './labicon'; // icon svg import statements -{{iconImportStatements}} +{{svgImportStatements}} -// JLIcon instance construction -{{jliconConstruction}} +// LabIcon instance construction +{{labiconConstructions}} `; const ICON_CSS_CLASSES_TEMPLATE = ` @@ -355,48 +355,52 @@ export async function ensureUiComponents( dorequire: boolean = false ): Promise { const funcName = 'ensureUiComponents'; + const pkgName = utils.stem(pkgPath); let messages: string[] = []; - const svgs = glob.sync(path.join(pkgPath, 'style/icons', '**/*.svg')); + const svgPaths = glob.sync(path.join(pkgPath, 'style/icons', '**/*.svg')); /* support for glob import of icon svgs */ const iconSrcDir = path.join(pkgPath, 'src/icon'); // build the per-icon import code - let _iconImportStatements: string[] = []; - let _jliconConstruction: string[] = []; - svgs.forEach(svg => { - const name = utils.stem(svg); - const svgpath = path - .relative(iconSrcDir, svg) + let _svgImportStatements: string[] = []; + let _labiconConstructions: string[] = []; + svgPaths.forEach(svgPath => { + const svgName = utils.stem(svgPath); + const svgImportPath = path + .relative(iconSrcDir, svgPath) .split(path.sep) .join('/'); - const svgname = utils.camelCase(name) + 'Svg'; - const iconname = utils.camelCase(name) + 'Icon'; + const svgstrRef = utils.camelCase(svgName) + 'Svgstr'; + const iconRef = utils.camelCase(svgName) + 'Icon'; + const iconName = [pkgName, utils.stem(svgPath)].join(':'); if (dorequire) { // load the icon svg using `require` - _jliconConstruction.push( - `export const ${iconname} = new JLIcon({ name: '${name}', svgstr: require('${svgpath}').default });` + _labiconConstructions.push( + `export const ${iconRef} = new LabIcon({ name: '${iconName}', svgstr: require('${svgImportPath}').default });` ); } else { // load the icon svg using `import` - _iconImportStatements.push(`import ${svgname} from '${svgpath}';`); + _svgImportStatements.push(`import ${svgstrRef} from '${svgImportPath}';`); - _jliconConstruction.push( - `export const ${iconname} = new JLIcon({ name: '${name}', svgstr: ${svgname} });` + _labiconConstructions.push( + `export const ${iconRef} = new LabIcon({ name: '${iconName}', svgstr: ${svgstrRef} });` ); } }); - const iconImportStatements = _iconImportStatements.join('\n'); - const jliconConstruction = _jliconConstruction.join('\n'); + + // sort the statements and then join them + const svgImportStatements = _svgImportStatements.sort().join('\n'); + const labiconConstructions = _labiconConstructions.sort().join('\n'); // generate the actual contents of the iconImports file const iconImportsPath = path.join(iconSrcDir, 'iconimports.ts'); const iconImportsContents = utils.fromTemplate( HEADER_TEMPLATE + ICON_IMPORTS_TEMPLATE, - { funcName, iconImportStatements, jliconConstruction } + { funcName, svgImportStatements, labiconConstructions } ); messages.push(...ensureFile(iconImportsPath, iconImportsContents, false)); @@ -406,14 +410,14 @@ export async function ensureUiComponents( // build the per-icon import code let _iconCSSUrls: string[] = []; let _iconCSSDeclarations: string[] = []; - svgs.forEach(svg => { - const name = utils.stem(svg); - const urlName = 'jp-icon-' + name; - const className = 'jp-' + utils.camelCase(name, true) + 'Icon'; + svgPaths.forEach(svgPath => { + const svgName = utils.stem(svgPath); + const urlName = 'jp-icon-' + svgName; + const className = 'jp-' + utils.camelCase(svgName, true) + 'Icon'; _iconCSSUrls.push( `--${urlName}: url('${path - .relative(iconCSSDir, svg) + .relative(iconCSSDir, svgPath) .split(path.sep) .join('/')}');` ); @@ -421,8 +425,10 @@ export async function ensureUiComponents( `.${className} {background-image: var(--${urlName})}` ); }); - const iconCSSUrls = _iconCSSUrls.join('\n'); - const iconCSSDeclarations = _iconCSSDeclarations.join('\n'); + + // sort the statements and then join them + const iconCSSUrls = _iconCSSUrls.sort().join('\n'); + const iconCSSDeclarations = _iconCSSDeclarations.sort().join('\n'); // generate the actual contents of the iconCSSClasses file const iconCSSClassesPath = path.join(iconCSSDir, 'deprecated.css'); diff --git a/dev_mode/package.json b/dev_mode/package.json index 0f7d6e01dd41..d411e1951791 100644 --- a/dev_mode/package.json +++ b/dev_mode/package.json @@ -169,7 +169,7 @@ "@jupyterlab/vdom-extension": "~2.0.0-beta.3", "@jupyterlab/vega5-extension": "~2.0.0-beta.3", "@lumino/algorithm": "^1.2.3", - "@lumino/application": "^1.8.0", + "@lumino/application": "^1.8.2", "@lumino/commands": "^1.9.2", "@lumino/coreutils": "^1.4.2", "@lumino/disposable": "^1.3.4", @@ -178,8 +178,8 @@ "@lumino/messaging": "^1.3.3", "@lumino/properties": "^1.1.6", "@lumino/signaling": "^1.3.4", - "@lumino/virtualdom": "^1.4.0", - "@lumino/widgets": "^1.10.0", + "@lumino/virtualdom": "^1.5.0", + "@lumino/widgets": "^1.10.2", "react": "~16.9.0", "react-dom": "~16.9.0" }, @@ -391,6 +391,7 @@ "@jupyterlab/test-statedb": "../tests/test-statedb", "@jupyterlab/test-statusbar": "../tests/test-statusbar", "@jupyterlab/test-terminal": "../tests/test-terminal", + "@jupyterlab/test-ui-components": "../tests/test-ui-components", "@jupyterlab/testutils": "../testutils", "@jupyterlab/mock-extension": "../jupyterlab/tests/mock_packages/extension" } diff --git a/docs/environment.yml b/docs/environment.yml index f88bb96291df..1a1f1e2156cd 100644 --- a/docs/environment.yml +++ b/docs/environment.yml @@ -8,5 +8,6 @@ dependencies: - sphinx_rtd_theme - recommonmark - pytest +- jsx-lexer - pip: - pytest-check-links diff --git a/docs/source/developer/extension_migration.rst b/docs/source/developer/extension_migration.rst index ce9e854a4e0f..b692247d5b58 100644 --- a/docs/source/developer/extension_migration.rst +++ b/docs/source/developer/extension_migration.rst @@ -168,3 +168,11 @@ For example, consider how the ``@jupyterlab/debugger`` extension's ``await sessionContext.session?.kernel?.info`` to check when a kernel is ready and retrieve its details. -- `jupyterlab/debugger#337 `__ + +Using the new icon system and ``LabIcon`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. note:: + + For full API documentation and examples of how to use + the new icon support based on ``LabIcon`` from ``@jupyterlab/ui-components``, + `consult the repository `__. diff --git a/docs/source/developer/extension_points.rst b/docs/source/developer/extension_points.rst index 9a26e937dd11..f7ab6d9aa345 100644 --- a/docs/source/developer/extension_points.rst +++ b/docs/source/developer/extension_points.rst @@ -17,9 +17,13 @@ and more detailed descriptions of their public APIs may be found in the :local: :depth: 1 + Commands ~~~~~~~~ +Add a Command to the Command Registry +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Perhaps the most common way to add functionality to JupyterLab is via commands. These are lightweight objects that include a function to execute combined with additional metadata, including how they are labeled and when they are to be enabled. @@ -68,10 +72,10 @@ After a command has been added to the application command registry you can add them to various places in the application user interface, where they will be rendered using the metadata you provided. -Command Palette -~~~~~~~~~~~~~~~ +Add a Command to the Command Palette +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In order to add a command to the command palette, you need to request the +In order to add an existing, registered command to the command palette, you need to request the ``ICommandPalette`` token in your extension. Here is an example showing how to add a command to the command palette (given by ``palette``): @@ -95,139 +99,183 @@ Your command ``label`` function can then check the ``args`` it is provided for ` and return a different label in that case. This can be useful to make a single command flexible enough to work in multiple contexts. -Main Menu -~~~~~~~~~ -There are three main ways to extend JupyterLab's main menu. +Context Menu +~~~~~~~~~~~~ -1. You can add your own menu to the menu bar. -2. You can add new commands to the existing menus. -3. You can register your extension with one of the existing semantic menu items. +The application context menu is shown when the user right-clicks, +and is populated with menu items that are most relevant to the thing that the user clicked. -In all three cases, you should request the ``IMainMenu`` token for your extension. +The context menu system determines which items to show based on +`CSS selectors `__. +It propagates up the DOM tree and tests whether a given HTML element +matches the CSS selector provided by a given command. -Adding a New Menu -^^^^^^^^^^^^^^^^^ +Here is an example showing how to add a command to the application context menu: -To add a new menu to the menu bar, you need to create a new -`Lumino menu `__. +.. code:: typescript -You can then add commands to the menu in a similar way to the command palette, -and add that menu to the main menu bar: + app.contextMenu.addItem({ + command: commandID, + selector: '.jp-Notebook' + }) + +In this example, the command indicated by ``commandID`` is shown whenever the user +right-clicks on a DOM element matching ``.jp-Notebook`` (that is to say, a notebook). +The selector can be any valid CSS selector, and may target your own UI elements, or existing ones. +A list of CSS selectors currently used by context menu commands is given in :ref:`css-selectors`. + + +.. _copy_shareable_link: + +Copy Shareable Link +~~~~~~~~~~~~~~~~~~~ + +The file browser provides a context menu item "Copy Shareable Link". The +desired behavior will vary by deployment and the users it serves. The file +browser supports overriding the behavior of this item. .. code:: typescript - const menu = new Menu({ commands: app.commands }); - menu.addItem({ - command: commandID, - args: {}, - }); + import { + IFileBrowserFactory + } from '@jupyterlab/filebrowser'; - mainMenu.addMenu(menu, { rank: 40 }); + import { + JupyterFrontEnd, JupyterFrontEndPlugin + } from '@jupyterlab/application'; -As with the command palette, you can optionally pass in ``args`` to customize the -rendering and execution behavior of the command in the menu context. + const shareFile: JupyterFrontEndPlugin = { + activate: activateShareFile, + id: commandID, + requires: [IFileBrowserFactory], + autoStart: true + }; -Adding a New Command to an Existing Menu -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + function activateShareFile( + app: JupyterFrontEnd, + factory: IFileBrowserFactory + ): void { + const { commands } = app; + const { tracker } = factory; -In many cases you will want to add your commands to the existing JupyterLab menus -rather than creating a separate menu for your extension. -Because the top-level JupyterLab menus are shared among many extensions, -the API for adding items is slightly different. -In this case, you provide a list of commands and a rank, -and these commands will be displayed together in a separate group within an existing menu. + commands.addCommand('filebrowser:share-main', { + execute: () => { + const widget = tracker.currentWidget; + if (!widget) { + return; + } + const path = encodeURI(widget.selectedItems().next().path); + // Do something with path. + }, + isVisible: () => + tracker.currentWidget && + toArray(tracker.currentWidget.selectedItems()).length === 1, + iconClass: 'jp-MaterialIcon jp-LinkIcon', + label: 'Copy Shareable Link' + }); + } -For instance, to add a command group with ``firstCommandID`` and ``secondCommandID`` -to the File menu, you would do the following: +Note that before enabling this plugin in the usual way, you must *disable* the +default plugin provided by the built-in file browser. -.. code:: typescript +.. code:: bash - mainMenu.fileMenu.addGroup([ - { - command: firstCommandID, - }, - { - command: secondCommandID, - } - ], 40 /* rank */); + jupyter labextension disable @jupyterlab/filebrowser-extension:share-file -Registering a Semantic Menu Item -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Icons +~~~~~ -There are some commands in the JupyterLab menu system that are considered -common and important enough that they are treated differently. +``LabIcon`` is the icon class used by JupyterLab, and is part of the new icon +system introduced in JupyterLab v2.0. -For instance, we anticipate that many activities may want to provide a command -to close themselves and perform some cleanup operation (like closing a console and shutting down its kernel). -Rather than having a proliferation of similar menu items for this common operation -of "closing-and-cleanup", we provide a single command that can adapt itself to this use case, -which we term a "semantic menu item". -For this example, it is the File Menu ``closeAndCleaners`` set. +How JupyterLab handles icons +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Here is an example of using the ``closeAndCleaners`` semantic menu item: +The ui-components package provides icons to the rest of JupyterLab, in the +form of a set of ``LabIcon`` instances (currently about 80). All of the icons +in the core JupyterLab packages are rendered using one of these ``LabIcon`` +instances. -.. code:: typescript +Using the icons in your own code +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - mainMenu.fileMenu.closeAndCleaners.add({ - tracker, - action: 'Shutdown', - name: 'My Activity', - closeAndCleanup: current => { - current.close(); - return current.shutdown(); - } - }); +You can use any of JupyterLab icons in your own code via an ``import`` +statement. For example, to use ``jupyterIcon`` you would first do: -In this example, ``tracker`` is a :ref:`widget-tracker`, which allows the menu -item to determine whether to delegate the menu command to your activity, -``name`` is a name given to your activity in the menu label, -``action`` is a verb given to the cleanup operation in the menu label, -and ``closeAndCleanup`` is the actual function that performs the cleanup operation. -So if the current application activity is held in the ``tracker``, -then the menu item will show ``Shutdown My Activity``, and delegate to the -``closeAndCleanup`` function that was provided. +.. code:: typescript -More examples for how to register semantic menu items are found throughout the JupyterLab code base. -The available semantic menu items are: + import { jupyterIcon } from "@jupyterlab/ui-components"; -- ``IEditMenu.IUndoer``: an activity that knows how to undo and redo. -- ``IEditMenu.IClearer``: an activity that knows how to clear its content. -- ``IEditMenu.IGoToLiner``: an activity that knows how to jump to a given line. -- ``IFileMenu.ICloseAndCleaner``: an activity that knows how to close and clean up after itself. -- ``IFileMenu.IConsoleCreator``: an activity that knows how to create an attached code console for itself. -- ``IHelpMenu.IKernelUser``: an activity that knows how to get a related kernel session. -- ``IKernelMenu.IKernelUser``: an activity that can perform various kernel-related operations. -- ``IRunMenu.ICodeRunner``: an activity that can run code from its content. -- ``IViewMenu.IEditorViewer``: an activity that knows how to set various view-related options on a text editor that it owns. +How to render an icon into a DOM node +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Icons can be added as children to any ``div`` or ``span`` nodes using the +`icon.element(...)` method (where ``icon`` is any instance of ``LabIcon``). +For example, to render the Jupyter icon you could do: -Context Menu -~~~~~~~~~~~~ +.. code:: typescript -The application context menu is shown when the user right-clicks, -and is populated with menu items that are most relevant to the thing that the user clicked. + jupyterIcon.element({ + container: elem, + height: '16px', + width: '16px', + marginLeft: '2px' + }); + +where ``elem`` is any ``HTMLElement`` with a ``div`` or ``span`` tag. As shown in +the above example, the icon can be styled by passing CSS parameters into +`.element(...)`. Any valid CSS parameter can be used, with one caveat: +snake case params have to be converted to camel case. For example, instead +of `foo-bar: '8px'`, you'd need to use `fooBar: '8px'`. + +How to render an icon as a React component +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Icons can also be rendered using React. The `icon.react` parameter holds a +standard React component that will display the icon on render. Like any React +component, `icon.react` can be used in various ways. + +For example, here is how you would add the Jupyter icon to the render tree of +another React component: + +.. code:: jsx + + public render() { + return ( +
+
+ + "and here's a text node" +
+
+ ); + } + +Alternatively, you can just render the icon directly into any existing DOM +node ``elem`` by using the ``ReactDOM`` module: -The context menu system determines which items to show based on -`CSS selectors `__. -It propagates up the DOM tree and tests whether a given HTML element -matches the CSS selector provided by a given command. +.. code:: typescript -Here is an example showing how to add a command to the application context menu: + ReactDOM.render(jupyterIcon.react, elem); + +If do you use ``ReactDOM`` to render, and if the ``elem`` node is ever removed +from the DOM, you'll first need to clean it up: .. code:: typescript - app.contextMenu.addItem({ - command: commandID, - selector: '.jp-Notebook' - }) + ReactDOM.unmountComponentAtNode(elem); -In this example, the command indicated by ``commandID`` is shown whenever the user -right-clicks on a DOM element matching ``.jp-Notebook`` (that is to say, a notebook). -The selector can be any valid CSS selector, and may target your own UI elements, or existing ones. -A list of CSS selectors currently used by context menu commands is given in :ref:`css-selectors`. +This cleanup step is not a special property of ``LabIcon``, but is instead +needed for any React component that is rendered directly at the top level +by ``ReactDOM``: failure to call `unmountComponentAtNode` can result in a +`memory leak `__. Keyboard Shortcuts @@ -313,6 +361,115 @@ and then adds the terminal to the right area: }); +Main Menu +~~~~~~~~~ + +There are three main ways to extend JupyterLab's main menu. + +1. You can add your own menu to the menu bar. +2. You can add new commands to the existing menus. +3. You can register your extension with one of the existing semantic menu items. + +In all three cases, you should request the ``IMainMenu`` token for your extension. + +Adding a New Menu +^^^^^^^^^^^^^^^^^ + +To add a new menu to the menu bar, you need to create a new +`Lumino menu `__. + +You can then add commands to the menu in a similar way to the command palette, +and add that menu to the main menu bar: + +.. code:: typescript + + const menu = new Menu({ commands: app.commands }); + menu.addItem({ + command: commandID, + args: {}, + }); + + mainMenu.addMenu(menu, { rank: 40 }); + +As with the command palette, you can optionally pass in ``args`` to customize the +rendering and execution behavior of the command in the menu context. + + +Adding a New Command to an Existing Menu +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In many cases you will want to add your commands to the existing JupyterLab menus +rather than creating a separate menu for your extension. +Because the top-level JupyterLab menus are shared among many extensions, +the API for adding items is slightly different. +In this case, you provide a list of commands and a rank, +and these commands will be displayed together in a separate group within an existing menu. + +For instance, to add a command group with ``firstCommandID`` and ``secondCommandID`` +to the File menu, you would do the following: + +.. code:: typescript + + mainMenu.fileMenu.addGroup([ + { + command: firstCommandID, + }, + { + command: secondCommandID, + } + ], 40 /* rank */); + + +Registering a Semantic Menu Item +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There are some commands in the JupyterLab menu system that are considered +common and important enough that they are treated differently. + +For instance, we anticipate that many activities may want to provide a command +to close themselves and perform some cleanup operation (like closing a console and shutting down its kernel). +Rather than having a proliferation of similar menu items for this common operation +of "closing-and-cleanup", we provide a single command that can adapt itself to this use case, +which we term a "semantic menu item". +For this example, it is the File Menu ``closeAndCleaners`` set. + +Here is an example of using the ``closeAndCleaners`` semantic menu item: + +.. code:: typescript + + mainMenu.fileMenu.closeAndCleaners.add({ + tracker, + action: 'Shutdown', + name: 'My Activity', + closeAndCleanup: current => { + current.close(); + return current.shutdown(); + } + }); + +In this example, ``tracker`` is a :ref:`widget-tracker`, which allows the menu +item to determine whether to delegate the menu command to your activity, +``name`` is a name given to your activity in the menu label, +``action`` is a verb given to the cleanup operation in the menu label, +and ``closeAndCleanup`` is the actual function that performs the cleanup operation. +So if the current application activity is held in the ``tracker``, +then the menu item will show ``Shutdown My Activity``, and delegate to the +``closeAndCleanup`` function that was provided. + +More examples for how to register semantic menu items are found throughout the JupyterLab code base. +The available semantic menu items are: + +- ``IEditMenu.IUndoer``: an activity that knows how to undo and redo. +- ``IEditMenu.IClearer``: an activity that knows how to clear its content. +- ``IEditMenu.IGoToLiner``: an activity that knows how to jump to a given line. +- ``IFileMenu.ICloseAndCleaner``: an activity that knows how to close and clean up after itself. +- ``IFileMenu.IConsoleCreator``: an activity that knows how to create an attached code console for itself. +- ``IHelpMenu.IKernelUser``: an activity that knows how to get a related kernel session. +- ``IKernelMenu.IKernelUser``: an activity that can perform various kernel-related operations. +- ``IRunMenu.ICodeRunner``: an activity that can run code from its content. +- ``IViewMenu.IEditorViewer``: an activity that knows how to set various view-related options on a text editor that it owns. + + Status Bar ~~~~~~~~~~ @@ -359,61 +516,3 @@ Widget tracker tokens are provided for many activities in JupyterLab, including notebooks, consoles, text files, mime documents, and terminals. If you are adding your own activities to JupyterLab, you might consider providing a ``WidgetTracker`` token of your own, so that other extensions can make use of it. - -.. _copy_shareable_link: - -Copy Shareable Link -~~~~~~~~~~~~~~~~~~~ - -The file browser provides a context menu item "Copy Shareable Link". The -desired behavior will vary by deployment and the users it serves. The file -browser supports overriding the behavior of this item. - -.. code:: typescript - - import { - IFileBrowserFactory - } from '@jupyterlab/filebrowser'; - - import { - JupyterFrontEnd, JupyterFrontEndPlugin - } from '@jupyterlab/application'; - - - const shareFile: JupyterFrontEndPlugin = { - activate: activateShareFile, - id: commandID, - requires: [IFileBrowserFactory], - autoStart: true - }; - - function activateShareFile( - app: JupyterFrontEnd, - factory: IFileBrowserFactory - ): void { - const { commands } = app; - const { tracker } = factory; - - commands.addCommand('filebrowser:share-main', { - execute: () => { - const widget = tracker.currentWidget; - if (!widget) { - return; - } - const path = encodeURI(widget.selectedItems().next().path); - // Do something with path. - }, - isVisible: () => - tracker.currentWidget && - toArray(tracker.currentWidget.selectedItems()).length === 1, - iconClass: 'jp-MaterialIcon jp-LinkIcon', - label: 'Copy Shareable Link' - }); - } - -Note that before enabling this plugin in the usual way, you must *disable* the -default plugin provided by the built-in file browser. - -.. code:: bash - - jupyter labextension disable @jupyterlab/filebrowser-extension:share-file diff --git a/docs/source/getting_started/changelog.rst b/docs/source/getting_started/changelog.rst index e70cd6fe18f1..256a56cacb92 100644 --- a/docs/source/getting_started/changelog.rst +++ b/docs/source/getting_started/changelog.rst @@ -40,6 +40,7 @@ For developers * Switch from ``@phosphor`` to ``@lumino`` dependencies. (`#7582 `__, `#7534 `__) * Factor out the ``settingsregistry`` and ``statedb`` packages from coreutils (`#7681 `__, `#7615 `__) * Rework services architecture (sessions, kernels, terminals). Rename ``ClientSession`` to ``SessionContext`` (`#7252 `__, `#7674 `__) +* Clean up handling of Icons under unified LabIcon (`#7767 `__) * Upgrade to TypeScript 3.7 (`#7522 `__) * Remove ``polling`` from coreutils in favor for ``@lumino/polling`` (`#7617 `__) * TypeScript strict null checking in core packages (`#7657 `__, `#7607 `__) diff --git a/examples/cell/package.json b/examples/cell/package.json index 427cfd1352d3..64dca2009c8c 100644 --- a/examples/cell/package.json +++ b/examples/cell/package.json @@ -16,7 +16,7 @@ "@jupyterlab/services": "^5.0.0-beta.3", "@jupyterlab/theme-light-extension": "^2.0.0-beta.3", "@lumino/commands": "^1.9.2", - "@lumino/widgets": "^1.10.0", + "@lumino/widgets": "^1.10.2", "es6-promise": "~4.2.8" }, "devDependencies": { diff --git a/examples/console/package.json b/examples/console/package.json index e6fbe25270ce..12c76f4f869c 100644 --- a/examples/console/package.json +++ b/examples/console/package.json @@ -14,7 +14,7 @@ "@jupyterlab/services": "^5.0.0-beta.3", "@jupyterlab/theme-light-extension": "^2.0.0-beta.3", "@lumino/commands": "^1.9.2", - "@lumino/widgets": "^1.10.0", + "@lumino/widgets": "^1.10.2", "es6-promise": "~4.2.8" }, "devDependencies": { diff --git a/examples/filebrowser/package.json b/examples/filebrowser/package.json index 34eb800af5da..403fb2b7f278 100644 --- a/examples/filebrowser/package.json +++ b/examples/filebrowser/package.json @@ -18,7 +18,7 @@ "@jupyterlab/theme-light-extension": "^2.0.0-beta.3", "@lumino/algorithm": "^1.2.3", "@lumino/commands": "^1.9.2", - "@lumino/widgets": "^1.10.0", + "@lumino/widgets": "^1.10.2", "es6-promise": "~4.2.8" }, "devDependencies": { diff --git a/examples/notebook/package.json b/examples/notebook/package.json index 7727d2a24e4e..dead7f00ec74 100644 --- a/examples/notebook/package.json +++ b/examples/notebook/package.json @@ -19,7 +19,7 @@ "@jupyterlab/services": "^5.0.0-beta.3", "@jupyterlab/theme-light-extension": "^2.0.0-beta.3", "@lumino/commands": "^1.9.2", - "@lumino/widgets": "^1.10.0", + "@lumino/widgets": "^1.10.2", "es6-promise": "~4.2.8" }, "devDependencies": { diff --git a/examples/terminal/package.json b/examples/terminal/package.json index af72a42fb81f..92afb3aefdb7 100644 --- a/examples/terminal/package.json +++ b/examples/terminal/package.json @@ -11,7 +11,7 @@ "@jupyterlab/services": "^5.0.0-beta.3", "@jupyterlab/terminal": "^2.0.0-beta.3", "@jupyterlab/theme-light-extension": "^2.0.0-beta.3", - "@lumino/widgets": "^1.10.0", + "@lumino/widgets": "^1.10.2", "es6-promise": "~4.2.8" }, "devDependencies": { diff --git a/jupyterlab/tests/mock_packages/mimeextension/package.json b/jupyterlab/tests/mock_packages/mimeextension/package.json index abfad738eeab..d9d97fd0620e 100644 --- a/jupyterlab/tests/mock_packages/mimeextension/package.json +++ b/jupyterlab/tests/mock_packages/mimeextension/package.json @@ -3,7 +3,7 @@ "version": "0.3.0", "private": true, "dependencies": { - "@lumino/widgets": "^1.10.0" + "@lumino/widgets": "^1.10.2" }, "jupyterlab": { "mimeExtension": true diff --git a/packages/application-extension/package.json b/packages/application-extension/package.json index e86920c959e6..e7cbdd84fe29 100644 --- a/packages/application-extension/package.json +++ b/packages/application-extension/package.json @@ -46,7 +46,7 @@ "@lumino/algorithm": "^1.2.3", "@lumino/coreutils": "^1.4.2", "@lumino/disposable": "^1.3.4", - "@lumino/widgets": "^1.10.0", + "@lumino/widgets": "^1.10.2", "react": "~16.9.0" }, "devDependencies": { diff --git a/packages/application/package.json b/packages/application/package.json index b5b3663ed1a2..63415e5bdb0e 100644 --- a/packages/application/package.json +++ b/packages/application/package.json @@ -45,7 +45,7 @@ "@jupyterlab/statedb": "^2.0.0-beta.3", "@jupyterlab/ui-components": "^2.0.0-beta.3", "@lumino/algorithm": "^1.2.3", - "@lumino/application": "^1.8.0", + "@lumino/application": "^1.8.2", "@lumino/commands": "^1.9.2", "@lumino/coreutils": "^1.4.2", "@lumino/disposable": "^1.3.4", @@ -53,7 +53,7 @@ "@lumino/polling": "^1.0.3", "@lumino/properties": "^1.1.6", "@lumino/signaling": "^1.3.4", - "@lumino/widgets": "^1.10.0" + "@lumino/widgets": "^1.10.2" }, "devDependencies": { "rimraf": "~3.0.0", diff --git a/packages/application/src/mimerenderers.ts b/packages/application/src/mimerenderers.ts index a30f15dd1f87..6dbe9fe3bc3d 100644 --- a/packages/application/src/mimerenderers.ts +++ b/packages/application/src/mimerenderers.ts @@ -13,6 +13,8 @@ import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; import { IRenderMime } from '@jupyterlab/rendermime-interfaces'; +import { LabIcon } from '@jupyterlab/ui-components'; + import { Token } from '@lumino/coreutils'; import { AttachedProperty } from '@lumino/properties'; @@ -123,6 +125,11 @@ export function createRendermimePlugin( if (item.fileTypes) { item.fileTypes.forEach(ft => { + if (ft.icon) { + // upconvert the contents of the icon field to a proper LabIcon + ft = { ...ft, icon: LabIcon.resolve({ icon: ft.icon }) }; + } + app.docRegistry.addFileType(ft as DocumentRegistry.IFileType); }); } diff --git a/packages/application/src/shell.ts b/packages/application/src/shell.ts index 3a00cf5fc3b3..6d73229130d0 100644 --- a/packages/application/src/shell.ts +++ b/packages/application/src/shell.ts @@ -3,7 +3,7 @@ import { DocumentRegistry } from '@jupyterlab/docregistry'; -import { DockPanelSvg, JLIcon } from '@jupyterlab/ui-components'; +import { DockPanelSvg, LabIcon } from '@jupyterlab/ui-components'; import { ArrayExt, find, IIterator, iter, toArray } from '@lumino/algorithm'; @@ -773,8 +773,8 @@ export class LabShell extends Widget implements JupyterFrontEnd.IShell { title.dataset = { ...title.dataset, id: widget.id }; // set an appropriate style class for the iconRenderer - if (title.iconRenderer instanceof JLIcon) { - title.iconClass = title.iconRenderer.class({ + if (title.iconRenderer) { + title.iconClass = LabIcon.UNSTABLE_style({ className: title.iconClass, justify: 'center', kind: 'mainAreaTab' @@ -1185,8 +1185,8 @@ namespace Private { title.dataset = { id: widget.id }; // set an appropriate style class for the iconRenderer - if (title.iconRenderer instanceof JLIcon) { - title.iconClass = title.iconRenderer.class({ + if (title.iconRenderer) { + title.iconClass = LabIcon.UNSTABLE_style({ className: title.iconClass, justify: 'center', kind: 'sideBar' diff --git a/packages/apputils-extension/package.json b/packages/apputils-extension/package.json index 9ed725fc53c6..6c0fa2f27022 100644 --- a/packages/apputils-extension/package.json +++ b/packages/apputils-extension/package.json @@ -49,7 +49,7 @@ "@lumino/coreutils": "^1.4.2", "@lumino/disposable": "^1.3.4", "@lumino/polling": "^1.0.3", - "@lumino/widgets": "^1.10.0", + "@lumino/widgets": "^1.10.2", "es6-promise": "~4.2.8" }, "devDependencies": { diff --git a/packages/apputils/package.json b/packages/apputils/package.json index 4419d6daa80a..5f2d86fa59c4 100644 --- a/packages/apputils/package.json +++ b/packages/apputils/package.json @@ -48,8 +48,8 @@ "@lumino/messaging": "^1.3.3", "@lumino/properties": "^1.1.6", "@lumino/signaling": "^1.3.4", - "@lumino/virtualdom": "^1.4.0", - "@lumino/widgets": "^1.10.0", + "@lumino/virtualdom": "^1.5.0", + "@lumino/widgets": "^1.10.2", "@types/react": "~16.9.16", "react": "~16.9.0", "react-dom": "~16.9.0", diff --git a/packages/apputils/src/toolbar.tsx b/packages/apputils/src/toolbar.tsx index 75d4bc441750..b31ee58d6c36 100644 --- a/packages/apputils/src/toolbar.tsx +++ b/packages/apputils/src/toolbar.tsx @@ -7,7 +7,7 @@ import { circleEmptyIcon, circleIcon, classes, - JLIcon, + LabIcon, refreshIcon, stopIcon } from '@jupyterlab/ui-components'; @@ -367,7 +367,7 @@ export namespace Toolbar { sessionContext: ISessionContext ): Widget { return new ToolbarButton({ - iconRenderer: stopIcon, + icon: stopIcon, onClick: () => { void sessionContext.session?.kernel?.interrupt(); }, @@ -383,7 +383,7 @@ export namespace Toolbar { dialogs?: ISessionContext.IDialogs ): Widget { return new ToolbarButton({ - iconRenderer: refreshIcon, + icon: refreshIcon, onClick: () => { void (dialogs ?? sessionContextDialogs).restart(sessionContext); }, @@ -450,7 +450,7 @@ export namespace ToolbarButtonComponent { label?: string; iconClass?: string; iconLabel?: string; - iconRenderer?: JLIcon; + icon?: LabIcon; tooltip?: string; onClick?: () => void; enabled?: boolean; @@ -482,31 +482,6 @@ export function ToolbarButtonComponent(props: ToolbarButtonComponent.IProps) { } }; - const Icon = () => { - if (props.iconRenderer) { - return ( - - ); - } else if (props.iconClass) { - return ( - - ); - } else { - return <>; - } - }; - return (