Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add public API for adding sections #44

Merged
merged 2 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ import {
} from '@jupyterlab/application';
import { ICommandPalette, MainAreaWidget } from '@jupyterlab/apputils';
import { FileBrowserModel, IDefaultFileBrowser } from '@jupyterlab/filebrowser';
import { ILauncher, LauncherModel } from '@jupyterlab/launcher';
import { ILauncher } from '@jupyterlab/launcher';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { ITranslator } from '@jupyterlab/translation';
import { addIcon, launcherIcon } from '@jupyterlab/ui-components';
import { find } from '@lumino/algorithm';
import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
import { DockPanel, TabBar, Widget } from '@lumino/widgets';
import { NewLauncher as Launcher } from './launcher';
import { CommandIDs, ILauncherDatabase, MAIN_PLUGIN_ID } from './types';
import { NewModel as Model } from './model';
import {
CommandIDs,
ILauncherDatabase,
INewLauncher,
MAIN_PLUGIN_ID
} from './types';
import { addCommands } from './commands';
import { sessionDialogsPlugin } from './dialogs';
import { databasePlugin } from './database';
Expand Down Expand Up @@ -54,10 +60,10 @@ function activate(
labShell: ILabShell | null,
palette: ICommandPalette | null,
defaultBrowser: IDefaultFileBrowser | null
): ILauncher {
): INewLauncher {
const { commands, shell } = app;
const trans = translator.load('jupyterlab-new-launcher');
const model = new LauncherModel();
const model = new Model();

if (
navigator.userAgent.indexOf('AppleWebKit') !== -1 &&
Expand Down
185 changes: 108 additions & 77 deletions src/launcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import {
consoleIcon
} from '@jupyterlab/ui-components';
import * as React from 'react';
import { NewModel } from './model';
import {
IItem,
IKernelItem,
ILastUsedDatabase,
IFavoritesDatabase,
ISettingsLayout
ISettingsLayout,
ISectionOptions
} from './types';
import { fileIcon, starIcon } from './icons';
import { Item } from './item';
Expand All @@ -35,6 +37,7 @@ function LauncherBody(props: {
settings: ISettingRegistry.ISettings;
favouritesChanged: ISignal<IFavoritesDatabase, void>;
lastUsedChanged: ISignal<ILastUsedDatabase, void>;
sections: ISectionOptions[];
}): React.ReactElement {
const { trans, cwd, typeItems, otherItems, favouritesChanged } = props;
const [query, updateQuery] = React.useState<string>('');
Expand Down Expand Up @@ -104,79 +107,29 @@ function LauncherBody(props: {
const startCollapsed = props.settings.composite
.collapsedSections as ISettingsLayout['collapsedSections'];

return (
<div className="jp-LauncherBody">
<div className="jp-NewLauncher-TopBar">
<div className="jp-Launcher-cwd">
<h3>
{trans.__('Current folder:')} <code>{cwd ? cwd : '/'}</code>
</h3>
</div>
<div className="jp-NewLauncher-OtherItems">
{otherItems.map(item => (
<TypeCard item={item} trans={trans} />
))}
</div>
</div>
{searchAll ? (
<div className="jp-Launcher-searchBox">
<FilterBox
placeholder={trans.__('Filter')}
updateFilter={(_, query) => {
updateQuery(query ?? '');
}}
initialQuery={''}
useFuzzyFilter={false}
/>
</div>
) : null}
<CollapsibleSection
className="jp-Launcher-openByType"
title={trans.__('Create Empty')}
icon={fileIcon}
open={startCollapsed['create-empty'] !== 'collapsed'}
>
{typeItems
const builtinSections: ISectionOptions[] = [
{
className: 'jp-Launcher-openByType',
title: trans.__('Create Empty'),
icon: fileIcon,
id: 'create-empty',
rank: 1,
render: () =>
typeItems
.filter(
item =>
!query ||
item.label.toLowerCase().indexOf(query.toLowerCase()) !== -1
)
.map(item => (
<TypeCard item={item} trans={trans} />
))}
</CollapsibleSection>
{showStarred ? (
<CollapsibleSection
className="jp-Launcher-openByKernel"
title={trans.__('Starred')}
icon={starIcon}
open={startCollapsed['starred'] !== 'collapsed'}
>
{starred.length > 0 ? (
<KernelTable
items={starred}
commands={props.commands}
showSearchBox={!searchAll}
showWidgetType={true}
query={query}
settings={props.settings}
trans={trans}
onClick={item => item.execute()}
favouritesChanged={props.favouritesChanged}
lastUsedChanged={props.lastUsedChanged}
/>
) : (
'No starred items'
)}
</CollapsibleSection>
) : null}
<CollapsibleSection
className="jp-Launcher-openByKernel jp-Launcher-launchNotebook"
title={trans.__('Launch New Notebook')}
icon={notebookIcon}
open={startCollapsed['launch-notebook'] !== 'collapsed'}
>
.map(item => <TypeCard item={item} trans={trans} />)
},
{
className: 'jp-Launcher-openByKernel jp-Launcher-launchNotebook',
title: trans.__('Launch New Notebook'),
icon: notebookIcon,
id: 'launch-notebook',
rank: 3,
render: () => (
<KernelTable
items={props.notebookItems}
commands={props.commands}
Expand All @@ -188,13 +141,15 @@ function LauncherBody(props: {
favouritesChanged={props.favouritesChanged}
lastUsedChanged={props.lastUsedChanged}
/>
</CollapsibleSection>
<CollapsibleSection
className="jp-Launcher-openByKernel jp-Launcher-launchConsole"
title={trans.__('Launch New Console')}
icon={consoleIcon}
open={startCollapsed['launch-console'] !== 'collapsed'}
>
)
},
{
className: 'jp-Launcher-openByKernel jp-Launcher-launchConsole',
title: trans.__('Launch New Console'),
icon: consoleIcon,
id: 'launch-console',
rank: 5,
render: () => (
<KernelTable
items={props.consoleItems}
commands={props.commands}
Expand All @@ -206,7 +161,75 @@ function LauncherBody(props: {
favouritesChanged={props.favouritesChanged}
lastUsedChanged={props.lastUsedChanged}
/>
</CollapsibleSection>
)
}
];
if (showStarred) {
builtinSections.push({
className: 'jp-Launcher-openByKernel',
title: trans.__('Starred'),
icon: starIcon,
id: 'starred',
rank: 2,
render: () =>
starred.length > 0 ? (
<KernelTable
items={starred}
commands={props.commands}
showSearchBox={!searchAll}
showWidgetType={true}
query={query}
settings={props.settings}
trans={trans}
onClick={item => item.execute()}
favouritesChanged={props.favouritesChanged}
lastUsedChanged={props.lastUsedChanged}
/>
) : (
'No starred items'
)
});
}
const allSections = [...builtinSections, ...props.sections];

return (
<div className="jp-LauncherBody">
<div className="jp-NewLauncher-TopBar">
<div className="jp-Launcher-cwd">
<h3>
{trans.__('Current folder:')} <code>{cwd ? cwd : '/'}</code>
</h3>
</div>
<div className="jp-NewLauncher-OtherItems">
{otherItems.map(item => (
<TypeCard item={item} trans={trans} />
))}
</div>
</div>
{searchAll ? (
<div className="jp-Launcher-searchBox">
<FilterBox
placeholder={trans.__('Filter')}
updateFilter={(_, query) => {
updateQuery(query ?? '');
}}
initialQuery={''}
useFuzzyFilter={false}
/>
</div>
) : null}
{allSections
.sort((a, b) => a.rank - b.rank)
.map(section => (
<CollapsibleSection
className={section.className}
title={section.title}
icon={section.icon}
open={startCollapsed[section.id] !== 'collapsed'}
>
{section.render()}
</CollapsibleSection>
))}
</div>
);
}
Expand All @@ -216,6 +239,7 @@ export namespace NewLauncher {
lastUsedDatabase: ILastUsedDatabase;
favoritesDatabase: IFavoritesDatabase;
settings: ISettingRegistry.ISettings;
model: NewModel;
}
}

Expand All @@ -229,9 +253,15 @@ export class NewLauncher extends Launcher {
this._lastUsedDatabase = options.lastUsedDatabase;
this._favoritesDatabase = options.favoritesDatabase;
this._settings = options.settings;
this._newModel = options.model;
this._newModel.sectionAdded.connect(() => {
this.update();
});
}
private _lastUsedDatabase: ILastUsedDatabase;
private _favoritesDatabase: IFavoritesDatabase;
private _newModel: NewModel;

trans: TranslationBundle;

renderCommand = (item: ILauncher.IItemOptions): IItem => {
Expand Down Expand Up @@ -334,6 +364,7 @@ export class NewLauncher extends Launcher {
settings={this._settings}
favouritesChanged={this._favoritesDatabase.changed}
lastUsedChanged={this._lastUsedDatabase.changed}
sections={this._newModel.sections}
/>
);
}
Expand Down
15 changes: 15 additions & 0 deletions src/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { INewLauncher, ISectionOptions } from './types';
import { ISignal, Signal } from '@lumino/signaling';
import { LauncherModel } from '@jupyterlab/launcher';

export class NewModel extends LauncherModel implements INewLauncher {
sections: ISectionOptions[] = [];
addSection(options: ISectionOptions) {
this.sections.push(options);
this._sectionAdded.emit();
}
get sectionAdded(): ISignal<NewModel, void> {
return this._sectionAdded;
}
private _sectionAdded = new Signal<NewModel, void>(this);
}
14 changes: 14 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,23 @@ import type { ILauncher } from '@jupyterlab/launcher';
import type { VirtualElement } from '@lumino/virtualdom';
import type { ISignal } from '@lumino/signaling';
import { Token } from '@lumino/coreutils';
import type { LabIcon } from '@jupyterlab/ui-components';

export const MAIN_PLUGIN_ID = 'jupyterlab-new-launcher:plugin';

export interface INewLauncher extends ILauncher {
addSection(options: ISectionOptions): void;
}

export interface ISectionOptions {
id: string;
title: string;
className: string;
icon: LabIcon;
render: () => React.ReactNode;
rank: number;
}

/**
* The command IDs used by the launcher plugin.
*/
Expand Down
Loading