Skip to content

Commit

Permalink
Add keyboard shortcuts to sources panel
Browse files Browse the repository at this point in the history
  • Loading branch information
gjmooney committed Jul 25, 2024
1 parent f866ea3 commit 4282bac
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 258 deletions.
152 changes: 13 additions & 139 deletions packages/base/src/panelview/components/layers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import {
IJGISLayerGroup,
IJGISLayerTree,
IJupyterGISClientState,
IJupyterGISModel,
ISelection,
SelectionType
IJupyterGISModel
} from '@jupytergis/schema';
import { DOMUtils } from '@jupyterlab/apputils';
import {
Expand All @@ -13,7 +11,6 @@ import {
ReactWidget,
caretDownIcon
} from '@jupyterlab/ui-components';
import { Message } from '@lumino/messaging';
import { Panel } from '@lumino/widgets';
import React, {
MouseEvent as ReactMouseEvent,
Expand All @@ -23,6 +20,7 @@ import React, {
import { icons } from '../../constants';
import { nonVisibilityIcon, visibilityIcon } from '../../icons';
import { IControlPanelModel } from '../../types';
import { ILeftPanelClickHandlerParams, ILeftPanelOptions } from '../leftpanel';

const LAYERS_PANEL_CLASS = 'jp-gis-layerPanel';
const LAYER_GROUP_CLASS = 'jp-gis-layerGroup';
Expand All @@ -34,33 +32,15 @@ const LAYER_TITLE_CLASS = 'jp-gis-layerTitle';
const LAYER_ICON_CLASS = 'jp-gis-layerIcon';
const LAYER_TEXT_CLASS = 'jp-gis-layerText';

/**
* The namespace for the layers panel.
*/
export namespace LayersPanel {
/**
* Options of the layers panel widget.
*/
export interface IOptions {
model: IControlPanelModel;
}

export interface IClickHandlerParams {
type: SelectionType;
item: string;
nodeId?: string;
event: ReactMouseEvent;
}
}

/**
* The layers panel widget.
*/
export class LayersPanel extends Panel {
constructor(options: LayersPanel.IOptions) {
constructor(options: ILeftPanelOptions) {
super();
this._model = options.model;
this._lastSelectedNodeId = '';
this._onSelect = options.onSelect;

this.id = 'jupytergis::layerTree';
this.addClass(LAYERS_PANEL_CLASS);

Expand All @@ -74,126 +54,20 @@ export class LayersPanel extends Panel {
);
}

protected onAfterAttach(msg: Message): void {
super.onAfterAttach(msg);
const node = this.node;
node.addEventListener('mouseup', this);
}

protected onBeforeDetach(msg: Message): void {
super.onBeforeDetach(msg);
const node = this.node;
node.removeEventListener('mouseup', this);
}

handleEvent(event: Event): void {
switch (event.type) {
case 'mouseup':
this._mouseUpEvent(event as MouseEvent);
break;
default:
break;
}
}

private _mouseUpEvent(event: MouseEvent): void {
// If we click on empty space in the layer panel, keep the focus on the last selected element
const node = document.getElementById(this._lastSelectedNodeId);
if (!node) {
return;
}

node.focus();
}

/**
* Function to call when a layer is selected from a component of the panel.
*
* @param item - the selected layer or group.
*/
private _onSelect = ({
private _model: IControlPanelModel | undefined;
private _onSelect: ({
type,
item,
nodeId,
event
}: LayersPanel.IClickHandlerParams) => {
if (!this._model || !nodeId) {
return;
}

const { jGISModel } = this._model;
const selectedValue = jGISModel?.localState?.selected?.value;
const node = document.getElementById(nodeId);

if (!node) {
return;
}

node.tabIndex = 0;
node.focus();

// Early return if no selection exists
if (!selectedValue) {
this.resetSelected(type, nodeId, item);
return;
}

// Don't want to reset selected if right clicking a selected item
if (!event.ctrlKey && event.button === 2 && item in selectedValue) {
return;
}

// Reset selection for normal left click
if (!event.ctrlKey) {
this.resetSelected(type, nodeId, item);
return;
}

if (nodeId) {
// Check if new selection is the same type as previous selections
const isSelectedSameType = Object.values(selectedValue).some(
selection => selection.type === type
);

if (!isSelectedSameType) {
// Selecting a new type, so reset selected
this.resetSelected(type, nodeId, item);
return;
}

// If types are the same add the selection
const updatedSelectedValue = {
...selectedValue,
[item]: { type, selectedNodeId: nodeId }
};
this._lastSelectedNodeId = nodeId;

jGISModel.syncSelected(updatedSelectedValue, this.id);
}
};

resetSelected(type: SelectionType, nodeId?: string, item?: string) {
const selection: { [key: string]: ISelection } = {};
if (item && nodeId) {
selection[item] = {
type,
selectedNodeId: nodeId
};
this._lastSelectedNodeId = nodeId;
}
this._model?.jGISModel?.syncSelected(selection, this.id);
}

private _model: IControlPanelModel | undefined;
private _lastSelectedNodeId: string;
nodeId
}: ILeftPanelClickHandlerParams) => void;
}

/**
* Properties of the layers body component.
*/
interface IBodyProps {
model: IControlPanelModel;
onSelect: ({ type, item, nodeId }: LayersPanel.IClickHandlerParams) => void;
onSelect: ({ type, item, nodeId }: ILeftPanelClickHandlerParams) => void;
}

/**
Expand All @@ -215,7 +89,7 @@ function LayersBodyComponent(props: IBodyProps): JSX.Element {
item,
nodeId,
event
}: LayersPanel.IClickHandlerParams) => {
}: ILeftPanelClickHandlerParams) => {
props.onSelect({ type, item, nodeId, event });
};

Expand Down Expand Up @@ -270,7 +144,7 @@ function LayersBodyComponent(props: IBodyProps): JSX.Element {
interface ILayerGroupProps {
gisModel: IJupyterGISModel | undefined;
group: IJGISLayerGroup | undefined;
onClick: ({ type, item, nodeId }: LayersPanel.IClickHandlerParams) => void;
onClick: ({ type, item, nodeId }: ILeftPanelClickHandlerParams) => void;
}

/**
Expand Down Expand Up @@ -363,7 +237,7 @@ function LayerGroupComponent(props: ILayerGroupProps): JSX.Element {
interface ILayerProps {
gisModel: IJupyterGISModel | undefined;
layerId: string;
onClick: ({ type, item, nodeId }: LayersPanel.IClickHandlerParams) => void;
onClick: ({ type, item, nodeId }: ILeftPanelClickHandlerParams) => void;
}

function isSelected(layerId: string, model: IJupyterGISModel | undefined) {
Expand Down
108 changes: 12 additions & 96 deletions packages/base/src/panelview/components/sources.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import {
IJupyterGISClientState,
IJupyterGISModel,
ISelection,
SelectionType
} from '@jupytergis/schema';
import { IJupyterGISClientState, IJupyterGISModel } from '@jupytergis/schema';
import { DOMUtils } from '@jupyterlab/apputils';
import { LabIcon, ReactWidget } from '@jupyterlab/ui-components';
import { Panel } from '@lumino/widgets';
import React, { MouseEvent, useEffect, useState } from 'react';
import { icons } from '../../constants';
import { IControlPanelModel } from '../../types';
import { ILeftPanelClickHandlerParams, ILeftPanelOptions } from '../leftpanel';

const SOURCES_PANEL_CLASS = 'jp-gis-sourcePanel';
const SOURCE_CLASS = 'jp-gis-source';
Expand All @@ -19,32 +15,15 @@ const SOURCE_TEXT_CLASS = 'jp-gis-sourceText';
const SOURCE_UNUSED = 'jp-gis-sourceUnused';
const SOURCE_INFO = 'jp-gis-sourceInfo';

/**
* The namespace for the sources panel.
*/
export namespace SourcesPanel {
/**
* Options of the sources panel widget.
*/
export interface IOptions {
model: IControlPanelModel;
}

export interface IClickHandlerParams {
type: SelectionType;
item: string;
nodeId?: string;
event: MouseEvent;
}
}

/**
* The sources panel widget.
*/
export class SourcesPanel extends Panel {
constructor(options: SourcesPanel.IOptions) {
constructor(options: ILeftPanelOptions) {
super();
this._model = options.model;
this._onSelect = options.onSelect;

this.id = 'jupytergis::sourcesPanel';
this.addClass(SOURCES_PANEL_CLASS);

Expand All @@ -58,83 +37,20 @@ export class SourcesPanel extends Panel {
);
}

/**
* Function to call when a source is selected from a component of the panel.
*
* @param item - the selected source.
*/
private _onSelect = ({
private _model: IControlPanelModel | undefined;
private _onSelect: ({
type,
item,
nodeId,
event
}: SourcesPanel.IClickHandlerParams) => {
if (!this._model) {
return;
}

const { jGISModel } = this._model;
const selectedValue = jGISModel?.localState?.selected?.value;

// Early return if no selection exists
if (!selectedValue) {
this.resetSelected(type, nodeId, item);
return;
}

// Don't want to reset selected if right clicking a selected item
if (!event.ctrlKey && event.button === 2 && item in selectedValue) {
return;
}

// Reset selection for normal left click
if (!event.ctrlKey) {
this.resetSelected(type, nodeId, item);
return;
}

if (nodeId) {
// Check if new selection is the same type as previous selections
const isSelectedSameType = Object.values(selectedValue).some(
selection => selection.type === type
);

if (!isSelectedSameType) {
// Selecting a new type, so reset selected
this.resetSelected(type, nodeId, item);
return;
}

// If types are the same add the selection
const updatedSelectedValue = {
...selectedValue,
[item]: { type, selectedNodeId: nodeId }
};

jGISModel.syncSelected(updatedSelectedValue, this.id);
}
};

resetSelected(type: SelectionType, nodeId?: string, item?: string) {
const selection: { [key: string]: ISelection } = {};
if (item && nodeId) {
selection[item] = {
type,
selectedNodeId: nodeId
};
}
this._model?.jGISModel?.syncSelected(selection, this.id);
}

private _model: IControlPanelModel | undefined;
nodeId
}: ILeftPanelClickHandlerParams) => void;
}

/**
* Properties of the sources body component.
*/
interface IBodyProps {
model: IControlPanelModel;
onSelect: ({ type, item, nodeId }: SourcesPanel.IClickHandlerParams) => void;
onSelect: ({ type, item, nodeId }: ILeftPanelClickHandlerParams) => void;
}

/**
Expand All @@ -156,7 +72,7 @@ function SourcesBodyComponent(props: IBodyProps): JSX.Element {
item,
nodeId,
event
}: SourcesPanel.IClickHandlerParams) => {
}: ILeftPanelClickHandlerParams) => {
props.onSelect({ type, item, nodeId, event });
};

Expand Down Expand Up @@ -206,7 +122,7 @@ function SourcesBodyComponent(props: IBodyProps): JSX.Element {
interface ISourceProps {
gisModel: IJupyterGISModel | undefined;
sourceId: string;
onClick: ({ type, item, nodeId }: SourcesPanel.IClickHandlerParams) => void;
onClick: ({ type, item, nodeId }: ILeftPanelClickHandlerParams) => void;
}

function isSelected(sourceId: string, model: IJupyterGISModel | undefined) {
Expand Down
Loading

0 comments on commit 4282bac

Please sign in to comment.