Skip to content

Commit e68c896

Browse files
authored
Clear control widget on tab switching. (#94)
* Clear control widget on tab switching. * add ui test
1 parent a538f01 commit e68c896

File tree

16 files changed

+1670
-1434
lines changed

16 files changed

+1670
-1434
lines changed

src/commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export namespace CommandIDs {
1111
export const newTable = 'glue-control:new-table-viewer';
1212

1313
export const openControlPanel = 'glue-control:open-control-panel';
14+
15+
export const closeControlPanel = 'glue-control:close-control-panel';
1416
}
1517

1618
export interface INewViewerArgs {

src/document/sharedModel.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ export class GlueSessionSharedModel
9999
return this._tabsChanged;
100100
}
101101

102+
get localStateChanged(): ISignal<
103+
IGlueSessionSharedModel,
104+
{ keys: string[] }
105+
> {
106+
return this._localStateChanged;
107+
}
108+
102109
getValue(key: string): IDict | undefined {
103110
const content = this._contents.get(key);
104111
if (!content) {
@@ -194,6 +201,7 @@ export class GlueSessionSharedModel
194201
value: tab,
195202
emitter: emitter
196203
});
204+
this._localStateChanged.emit({ keys: ['selectedTab'] });
197205
}
198206

199207
getSelectedTab(): number | null {
@@ -273,4 +281,8 @@ export class GlueSessionSharedModel
273281
private _linksChanged = new Signal<IGlueSessionSharedModel, IDict>(this);
274282
private _tabChanged = new Signal<IGlueSessionSharedModel, IDict>(this);
275283
private _tabsChanged = new Signal<IGlueSessionSharedModel, IDict>(this);
284+
private _localStateChanged = new Signal<
285+
IGlueSessionSharedModel,
286+
{ keys: string[] }
287+
>(this);
276288
}

src/leftPanel/config/configPanel.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ export class ConfigPanel extends SidePanel {
8989
private _createHeader(): void {
9090
this.toolbar.addItem('Header', this._panelHeader);
9191
this._model.displayConfigRequested.connect(this._updateHeader, this);
92+
this._model.clearConfigRequested.connect(() => {
93+
this._panelHeader.node.innerHTML = '';
94+
if (this._model.currentSessionWidget) {
95+
this._headerData.delete(this._model.currentSessionWidget);
96+
}
97+
}, this);
9298
}
9399

94100
private _updateHeader(

src/leftPanel/config/configWidgetModel.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export class ConfigWidgetModel implements IDisposable {
2323
this._config = options.config;
2424
this._model.glueSessionChanged.connect(this._sessionChanged, this);
2525
this._model.displayConfigRequested.connect(this._showConfig, this);
26+
this._model.clearConfigRequested.connect(this._clearConfig, this);
2627
}
2728

2829
get config(): 'Layer' | 'Viewer' {
@@ -43,9 +44,24 @@ export class ConfigWidgetModel implements IDisposable {
4344

4445
this._disposed = true;
4546
this._model.glueSessionChanged.disconnect(this._sessionChanged);
47+
this._model.displayConfigRequested.disconnect(this._showConfig);
48+
this._model.clearConfigRequested.disconnect(this._clearConfig);
49+
4650
Signal.clearData(this);
4751
}
4852

53+
private _clearConfig(): void {
54+
const context = this._model.currentSessionContext();
55+
56+
if (context && this._currentSessionWidget) {
57+
const output = this._outputs.get(this._currentSessionWidget);
58+
if (!output) {
59+
return;
60+
}
61+
output.model.clear();
62+
this._currentArgs = undefined;
63+
}
64+
}
4965
private _showConfig(
5066
sender: IControlPanelModel,
5167
args: IRequestConfigDisplay

src/leftPanel/model.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ export class ControlPanelModel implements IControlPanelModel {
4949
> {
5050
return this._displayConfigRequested;
5151
}
52+
get clearConfigRequested(): ISignal<IControlPanelModel, void> {
53+
return this._clearConfigRequested;
54+
}
5255

5356
get selectedDataset(): string | null {
5457
return this._selectedDataset;
@@ -80,7 +83,9 @@ export class ControlPanelModel implements IControlPanelModel {
8083
displayConfig(args: IRequestConfigDisplay): void {
8184
this._displayConfigRequested.emit(args);
8285
}
83-
86+
clearConfig(): void {
87+
this._clearConfigRequested.emit();
88+
}
8489
private _onTabsChanged(_: any, e: any): void {
8590
this._tabs = this._sessionModel?.sharedModel.tabs ?? {};
8691
this._tabsChanged.emit();
@@ -100,6 +105,7 @@ export class ControlPanelModel implements IControlPanelModel {
100105
IControlPanelModel,
101106
IRequestConfigDisplay
102107
>(this);
108+
private _clearConfigRequested = new Signal<IControlPanelModel, void>(this);
103109

104110
private _sessionModel?: IGlueSessionModel;
105111
}

src/leftPanel/plugin.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,15 @@ function addCommands(
158158
});
159159
}
160160
});
161+
162+
commands.addCommand(CommandIDs.closeControlPanel, {
163+
execute: () => {
164+
if (!controlModel.sharedModel) {
165+
return;
166+
}
167+
controlModel.clearConfig();
168+
}
169+
});
161170
}
162171

163172
export const controlPanel: JupyterFrontEndPlugin<void> = {

src/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export interface IGlueSessionSharedModel
5050
linksChanged: ISignal<IGlueSessionSharedModel, IDict>;
5151
tabChanged: ISignal<IGlueSessionSharedModel, IDict>;
5252
tabsChanged: ISignal<IGlueSessionSharedModel, IDict>;
53-
53+
localStateChanged: ISignal<IGlueSessionSharedModel, { keys: string[] }>;
5454
addTab(): void;
5555
getTabNames(): string[];
5656
getTabData(tabName: string): IDict<IGlueSessionViewerTypes> | undefined;
@@ -104,8 +104,10 @@ export interface IControlPanelModel {
104104
tabsChanged: ISignal<IControlPanelModel, void>;
105105
currentSessionWidget: IGlueSessionWidget | null;
106106
displayConfigRequested: ISignal<IControlPanelModel, IRequestConfigDisplay>;
107+
clearConfigRequested: ISignal<IControlPanelModel, void>;
107108
getTabs(): IGlueSessionTabs;
108109
displayConfig(args: IRequestConfigDisplay): void;
110+
clearConfig(): void;
109111
currentSessionContext(): ISessionContext | undefined;
110112
}
111113

src/viewPanel/sessionWidget.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ export class SessionWidget extends BoxPanel {
187187
args: TabBar.ICurrentChangedArgs<Widget>
188188
) {
189189
this._model.setSelectedTab(args.currentIndex);
190+
this._commands.execute(CommandIDs.closeControlPanel);
190191
}
191192

192193
private _spinner: HTMLDivElement;

src/viewPanel/tabLayout.ts

Lines changed: 91 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -111,62 +111,53 @@ export class TabLayout extends Layout {
111111
}
112112

113113
/**
114-
* Dispose of the resources held by the widget.
114+
* Create an iterator over the widgets in the layout.
115+
*
116+
* @returns A new iterator over the widgets in the layout.
115117
*/
116-
dispose(): void {
117-
this._grid.destroy();
118-
super.dispose();
118+
*[Symbol.iterator](): IterableIterator<Widget> {
119+
yield* this._gridItems.values();
119120
}
120121

121122
/**
122-
* Init the gridstack layout
123+
* Helper to get access to underlying GridStack object
123124
*/
124-
init(): void {
125-
super.init();
126-
if (this.parent) {
127-
this.parent.node.appendChild(this._gridHost);
128-
}
129-
// fake window resize event to resize bqplot
130-
window.dispatchEvent(new Event('resize'));
125+
get grid(): GridStack {
126+
return this._grid;
131127
}
132128

133129
/**
134-
* Handle `update-request` messages sent to the widget.
130+
* Get the list of `GridStackItem` (Lumino widgets).
135131
*/
136-
protected onUpdateRequest(msg: Message): void {
137-
const items = this._grid?.getGridItems();
138-
items?.forEach(item => {
139-
this._grid.removeWidget(item, true, false);
140-
this._grid.addWidget(item);
141-
});
132+
get gridItems(): Map<string, GridStackItem> {
133+
return this._gridItems;
142134
}
143135

144136
/**
145-
* Handle `resize-request` messages sent to the widget.
137+
* Get the list of `GridItemHTMLElement`.
146138
*/
147-
protected onResize(msg: Message): void {
148-
// Using timeout to wait until the resize stop
149-
// rerendering all the widgets every time uses
150-
// too much resources
151-
clearTimeout(this._resizeTimeout);
152-
this._resizeTimeout = setTimeout(this._onResizeStops, 500);
153-
this._prepareGrid();
139+
get gridElements(): GridItemHTMLElement[] {
140+
return this._grid.getGridItems() ?? [];
154141
}
155142

156143
/**
157-
* Handle `fit-request` messages sent to the widget.
144+
* Dispose of the resources held by the widget.
158145
*/
159-
protected onFitRequest(msg: Message): void {
160-
this._prepareGrid();
146+
dispose(): void {
147+
this._grid.destroy();
148+
super.dispose();
161149
}
162150

163151
/**
164-
* Create an iterator over the widgets in the layout.
165-
*
166-
* @returns A new iterator over the widgets in the layout.
152+
* Init the gridstack layout
167153
*/
168-
*[Symbol.iterator](): IterableIterator<Widget> {
169-
yield* this._gridItems.values();
154+
init(): void {
155+
super.init();
156+
if (this.parent) {
157+
this.parent.node.appendChild(this._gridHost);
158+
}
159+
// fake window resize event to resize bqplot
160+
window.dispatchEvent(new Event('resize'));
170161
}
171162

172163
/**
@@ -178,27 +169,6 @@ export class TabLayout extends Layout {
178169
return;
179170
}
180171

181-
/**
182-
* Helper to get access to underlying GridStack object
183-
*/
184-
get grid(): GridStack {
185-
return this._grid;
186-
}
187-
188-
/**
189-
* Get the list of `GridStackItem` (Lumino widgets).
190-
*/
191-
get gridItems(): Map<string, GridStackItem> {
192-
return this._gridItems;
193-
}
194-
195-
/**
196-
* Get the list of `GridItemHTMLElement`.
197-
*/
198-
get gridElements(): GridItemHTMLElement[] {
199-
return this._grid.getGridItems() ?? [];
200-
}
201-
202172
/**
203173
* Add new cell to gridstack.
204174
*
@@ -273,11 +243,75 @@ export class TabLayout extends Layout {
273243
items?.forEach(item => this._grid.removeWidget(item, true, false));
274244
}
275245

246+
/**
247+
* Unselect all items of the grid layout.
248+
*/
249+
unselectGridItems(): void {
250+
this._grid.getGridItems().forEach(i => i.classList.remove('grid-selected'));
251+
}
252+
253+
/**
254+
* Handle `update-request` messages sent to the widget.
255+
*/
256+
protected onUpdateRequest(msg: Message): void {
257+
const items = this._grid?.getGridItems();
258+
items?.forEach(item => {
259+
this._grid.removeWidget(item, true, false);
260+
this._grid.addWidget(item);
261+
});
262+
}
263+
264+
/**
265+
* Handle `resize-request` messages sent to the widget.
266+
*/
267+
protected onResize(msg: Message): void {
268+
// Using timeout to wait until the resize stop
269+
// rerendering all the widgets every time uses
270+
// too much resources
271+
clearTimeout(this._resizeTimeout);
272+
this._resizeTimeout = setTimeout(this._onResizeStops, 500);
273+
this._prepareGrid();
274+
}
275+
276+
/**
277+
* Handle `fit-request` messages sent to the widget.
278+
*/
279+
protected onFitRequest(msg: Message): void {
280+
this._prepareGrid();
281+
}
282+
283+
/**
284+
* Handle `after-attach` messages sent to the widget.
285+
*/
286+
protected onAfterAttach(msg: Message): void {
287+
super.onAfterAttach(msg);
288+
this._gridHost.addEventListener('click', this._onClick.bind(this));
289+
}
290+
291+
/**
292+
* Handle `before-attach` messages sent to the widget.
293+
*/
294+
protected onBeforeDetach(msg: Message): void {
295+
this._gridHost.removeEventListener('click', this._onClick.bind(this));
296+
297+
super.onBeforeDetach(msg);
298+
}
299+
300+
/**
301+
* Handle click event to the widget.
302+
*/
303+
private _onClick(event: MouseEvent) {
304+
if (event.target === event.currentTarget) {
305+
this._commands.execute(CommandIDs.closeControlPanel);
306+
this.unselectGridItems();
307+
}
308+
}
309+
276310
/**
277311
* Handle edit request of a grid item.
278312
*/
279313
private _handleEdit(item: GridStackItem): void {
280-
this._grid.getGridItems().forEach(i => i.classList.remove('grid-selected'));
314+
this.unselectGridItems();
281315
item.node.classList.add('grid-selected');
282316
this._commands.execute(CommandIDs.openControlPanel, {
283317
cellId: item.cellIdentity,

src/viewPanel/tabView.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export class TabView extends Widget {
3737

3838
layout.gridItemChanged.connect(this._onLayoutChanged, this);
3939
this._model.tabChanged.connect(this._onTabChanged, this);
40+
this._model.localStateChanged.connect(this._onLocalStateChanged, this);
4041
}
4142

4243
dispose(): void {
@@ -49,7 +50,7 @@ export class TabView extends Widget {
4950
this
5051
);
5152
this._model.tabChanged.disconnect(this._onTabChanged, this);
52-
53+
this._model.localStateChanged.disconnect(this._onLocalStateChanged);
5354
super.dispose();
5455
}
5556

@@ -321,6 +322,14 @@ export class TabView extends Widget {
321322
}
322323
}
323324

325+
private _onLocalStateChanged(
326+
sender: IGlueSessionSharedModel,
327+
changes: { keys: string[] }
328+
) {
329+
if (changes.keys.includes('selectedTab')) {
330+
(this.layout as TabLayout).unselectGridItems();
331+
}
332+
}
324333
private _selectedItem: GridStackItem | null = null;
325334
private _model: IGlueSessionSharedModel;
326335
private _context: DocumentRegistry.IContext<GlueSessionModel>;

0 commit comments

Comments
 (0)