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 zustand store for datalayer configuration #2

Merged
merged 3 commits into from
Mar 13, 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
2 changes: 1 addition & 1 deletion datalayer/serverapplication.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class DatalayerExtensionApp(ExtensionAppJinjaMixin, ExtensionApp):
)
launcher_category = Unicode("Datalayer",
config=True,
help=("Category to use for the applicaton launcher."),
help=("Category to use for the application launcher."),
)
white_label = Bool(False,
config=True,
Expand Down
53 changes: 37 additions & 16 deletions src/Datalayer.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { useState, useEffect } from 'react';
import { JupyterFrontEnd } from '@jupyterlab/application';
import { ThemeProvider, BaseStyles, Box, } from '@primer/react';
import { ThemeProvider, BaseStyles, Box } from '@primer/react';
import { UnderlineNav } from '@primer/react';
import { DatalayerGreenPaddingIcon, JupyterLabIcon } from '@datalayer/icons-react';
import {
DatalayerGreenPaddingIcon,
JupyterLabIcon
} from '@datalayer/icons-react';
import AboutTab from './tabs/AboutTab';
import JupyterLabTab from './tabs/JupyterLabTab';
import { requestAPI } from './jupyterlab/handler';
import useStore from './state/zustand';
import useStore from './state';

export type DatalayerProps = {
jupyterFrontend?: JupyterFrontEnd;
}
};

const Datalayer = (props: DatalayerProps) => {
const { jupyterFrontend } = props;
Expand All @@ -19,14 +22,14 @@ const Datalayer = (props: DatalayerProps) => {
const [version, setVersion] = useState('');
useEffect(() => {
requestAPI<any>('config')
.then(data => {
setVersion(data.version);
})
.catch(reason => {
console.error(
`Error while accessing the jupyter server datalayer extension.\n${reason}`
);
});
.then(data => {
setVersion(data.version);
})
.catch(reason => {
console.error(
`Error while accessing the jupyter server datalayer extension.\n${reason}`
);
});
});
return (
<>
Expand All @@ -35,23 +38,41 @@ const Datalayer = (props: DatalayerProps) => {
<Box>
<Box>
<UnderlineNav aria-label="datalayer">
<UnderlineNav.Item aria-label="jupyterlab" aria-current={intTab === 0 ? "page" : undefined} icon={() => <JupyterLabIcon colored/>} onSelect={e => {e.preventDefault(); setTab(0.0);}}>
<UnderlineNav.Item
aria-label="jupyterlab"
aria-current={intTab === 0 ? 'page' : undefined}
icon={() => <JupyterLabIcon colored />}
onSelect={e => {
e.preventDefault();
setTab(0.0);
}}
>
JupyterLab
</UnderlineNav.Item>
<UnderlineNav.Item aria-label="about" aria-current={intTab === 1 ? "page" : undefined} icon={() => <DatalayerGreenPaddingIcon colored/>} onSelect={e => {e.preventDefault(); setTab(1.0);}}>
<UnderlineNav.Item
aria-label="about"
aria-current={intTab === 1 ? 'page' : undefined}
icon={() => <DatalayerGreenPaddingIcon colored />}
onSelect={e => {
e.preventDefault();
setTab(1.0);
}}
>
About
</UnderlineNav.Item>
</UnderlineNav>
</Box>
<Box m={3}>
{intTab === 0 && jupyterFrontend && <JupyterLabTab jupyterFrontend={jupyterFrontend} />}
{intTab === 0 && jupyterFrontend && (
<JupyterLabTab jupyterFrontend={jupyterFrontend} />
)}
{intTab === 1 && <AboutTab version={version} />}
</Box>
</Box>
</BaseStyles>
</ThemeProvider>
</>
);
}
};

export default Datalayer;
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './constants';
export * from './jupyterlab';

export * from './state';
97 changes: 47 additions & 50 deletions src/jupyterlab/index.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,23 @@
import { Token } from '@lumino/coreutils';
import { ISignal, Signal } from '@lumino/signaling';
import { JupyterFrontEnd, JupyterFrontEndPlugin, ILayoutRestorer } from '@jupyterlab/application';
import { MainAreaWidget, ICommandPalette, WidgetTracker } from '@jupyterlab/apputils';
import {
JupyterFrontEnd,
JupyterFrontEndPlugin,
ILayoutRestorer
} from '@jupyterlab/application';
import {
MainAreaWidget,
ICommandPalette,
WidgetTracker
} from '@jupyterlab/apputils';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { ILauncher } from '@jupyterlab/launcher';
import { PromiseDelegate } from '@lumino/coreutils';
import icon from '@datalayer/icons-react/data1/DatalayerGreenPaddingIconJupyterLab';
import { requestAPI } from './handler';
import { DatalayerWidget } from './widget';
import { datalayerStore } from '../state';
import { IDatalayerConfig, IDatalayer, DatalayerConfiguration } from './tokens';

import '../../style/index.css';

export type IDatalayerConfig = {
apiServerUrl: string,
launcherCategory: string,
whiteLabel: boolean,
};

export class DatalayerConfiguration {
private _configuration?: IDatalayerConfig;
private _configurationChanged: Signal<DatalayerConfiguration, IDatalayerConfig | undefined>;
constructor() {
this._configurationChanged = new Signal<DatalayerConfiguration, IDatalayerConfig | undefined>(this);
}
set configuration(configuration: IDatalayerConfig | undefined) {
this._configuration = configuration;
this._configurationChanged.emit(configuration)
}
get configuration() {
return this._configuration;
}
get configurationChanged(): ISignal<DatalayerConfiguration, IDatalayerConfig | undefined> {
return this._configurationChanged;
}
}

export type IDatalayer = {
configuration: DatalayerConfiguration,
};

export const IDatalayer = new Token<IDatalayer>(
'@datalayer/core:plugin'
);
export * from './tokens';

/**
* The command IDs used by the plugin.k
Expand All @@ -65,7 +42,7 @@ const plugin: JupyterFrontEndPlugin<IDatalayer> = {
palette: ICommandPalette,
settingRegistry?: ISettingRegistry,
launcher?: ILauncher,
restorer?: ILayoutRestorer,
restorer?: ILayoutRestorer
) => {
tracker.forEach(widget => widget.dispose());
console.log(`${plugin.id} is deactivated`);
Expand All @@ -75,31 +52,45 @@ const plugin: JupyterFrontEndPlugin<IDatalayer> = {
palette: ICommandPalette,
settingRegistry?: ISettingRegistry,
launcher?: ILauncher,
restorer?: ILayoutRestorer,
restorer?: ILayoutRestorer
): IDatalayer => {
const datalayer: IDatalayer = {
configuration: new DatalayerConfiguration(),
}
const ready = new PromiseDelegate<void>();
const datalayer: IDatalayer = Object.freeze({
configuration: new DatalayerConfiguration(),
ready: ready.promise
});
storeConfiguration(datalayer.configuration.configuration);
datalayer.configuration.configurationChanged.connect((_, config) => {
storeConfiguration(config);
});

requestAPI<any>('config')
.then(data => {
console.log('Received Datalayer configuration', data);
const configuration = {
apiServerUrl: data.settings.api_server_url,
launcherCategory: data.settings.launcher_category,
whiteLabel: data.settings.white_label,
}
whiteLabel: data.settings.white_label
};
datalayer.configuration.configuration = configuration;
ready.resolve();

// Don't add user interface elements in white label
if (configuration.whiteLabel) {
return;
}

const { commands } = app;
const command = CommandIDs.create;
if (!tracker) {
tracker = new WidgetTracker<MainAreaWidget<DatalayerWidget>>({
namespace: 'datalayer',
namespace: 'datalayer'
});
}
if (restorer) {
void restorer.restore(tracker, {
command,
name: () => 'datalayer',
name: () => 'datalayer'
});
}
commands.addCommand(command, {
Expand All @@ -118,12 +109,13 @@ const plugin: JupyterFrontEndPlugin<IDatalayer> = {
const category = configuration.launcherCategory;
palette.addItem({ command, category });
const settingsUpdated = (settings: ISettingRegistry.ISettings) => {
const showInLauncher = settings.get('showInLauncher').composite as boolean;
const showInLauncher = settings.get('showInLauncher')
.composite as boolean;
if (launcher && showInLauncher) {
launcher.add({
command,
category,
rank: 1.1,
rank: 1.1
});
}
};
Expand All @@ -141,13 +133,18 @@ const plugin: JupyterFrontEndPlugin<IDatalayer> = {
}
})
.catch(reason => {
ready.reject(reason);
console.error(
`Error while accessing the jupyter server extension.\n${reason}`
);
}
);
});
console.log(`JupyterLab plugin ${plugin.id} is activated.`);
return datalayer;

function storeConfiguration(configuration?: IDatalayerConfig) {
const { setConfiguration } = datalayerStore.getState();
setConfiguration(configuration);
}
}
};

Expand Down
42 changes: 42 additions & 0 deletions src/jupyterlab/tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Token } from '@lumino/coreutils';
import { ISignal, Signal } from '@lumino/signaling';

export type IDatalayerConfig = {
apiServerUrl: string;
launcherCategory: string;
whiteLabel: boolean;
};

export type IDatalayer = {
configuration: DatalayerConfiguration;
ready: Promise<void>;
};

export const IDatalayer = new Token<IDatalayer>('@datalayer/core:plugin');

export class DatalayerConfiguration {
private _configuration?: IDatalayerConfig;
private _configurationChanged: Signal<
DatalayerConfiguration,
IDatalayerConfig | undefined
>;
constructor() {
this._configurationChanged = new Signal<
DatalayerConfiguration,
IDatalayerConfig | undefined
>(this);
}
set configuration(configuration: IDatalayerConfig | undefined) {
this._configuration = configuration;
this._configurationChanged.emit(configuration);
}
get configuration() {
return this._configuration;
}
get configurationChanged(): ISignal<
DatalayerConfiguration,
IDatalayerConfig | undefined
> {
return this._configurationChanged;
}
}
36 changes: 36 additions & 0 deletions src/state/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { createStore } from 'zustand/vanilla';
import { useStore } from 'zustand';
import type { IDatalayerConfig } from '../jupyterlab/tokens';

export type DatalayerState = {
tab: number;
getIntTab: () => number;
setTab: (tab: number) => void;

/**
* Global Datalayer configuration
*/
configuration?: IDatalayerConfig;
/**
* Set the global datalayer configuration
*/
setConfiguration: (configuration?: IDatalayerConfig) => void;
};

export const datalayerStore = createStore<DatalayerState>((set, get) => ({
tab: 0.0,
getIntTab: () => Math.floor(get().tab),
setTab: (tab: number) => set((state: DatalayerState) => ({ tab })),
configuration: undefined,
setConfiguration: (configuration?: IDatalayerConfig) => {
set(state => ({ configuration }));
}
}));

export function useDatalayerStore(): DatalayerState;
export function useDatalayerStore<T>(selector: (state: DatalayerState) => T): T;
export function useDatalayerStore<T>(selector?: (state: DatalayerState) => T) {
return useStore(datalayerStore, selector!);
}

export default useDatalayerStore;
15 changes: 0 additions & 15 deletions src/state/zustand.ts

This file was deleted.

Loading
Loading