From 5c53d6a2524db1ea213c5b2d449835c0bdbfcf9e Mon Sep 17 00:00:00 2001 From: Gauss_Taylor Date: Thu, 19 Jun 2025 17:18:48 +0200 Subject: [PATCH 01/17] Embedded panels --- package.json | 145 +++++---- packages/base/src/Lumino.tsx | 43 +++ .../src/annotations/components/Annotation.tsx | 15 +- packages/base/src/mainview/mainView.tsx | 30 ++ packages/base/src/mainview/mainviewwidget.tsx | 26 +- .../base/src/panelview/annotationPanel.tsx | 56 +--- .../components/filter-panel/Filter.tsx | 90 ++---- .../identify-panel/IdentifyPanel.tsx | 81 +---- .../base/src/panelview/components/layers.tsx | 209 ++++++------- packages/base/src/panelview/leftpanel.tsx | 285 ++++-------------- .../base/src/panelview/objectproperties.tsx | 77 ++--- packages/base/src/panelview/rightpanel.tsx | 173 +++++------ packages/base/src/shared/components/Tabs.tsx | 25 +- packages/base/src/stacBrowser/StacBrowser.tsx | 32 +- packages/base/src/stacBrowser/StacPanel.tsx | 8 +- .../stacBrowser/components/StacPanelView.tsx | 2 +- packages/base/src/widget.ts | 30 +- packages/base/style/base.css | 20 +- packages/base/style/shared/tabs.css | 3 +- packages/base/style/stacBrowser.css | 4 +- packages/base/style/tabPanel.css | 99 ++++++ python/jupytergis_core/src/factory.ts | 16 +- .../jupytergis_core/src/jgisplugin/plugins.ts | 11 + python/jupytergis_lab/src/index.ts | 61 +--- python/jupytergis_lab/src/notebookrenderer.ts | 5 +- python/jupytergis_qgis/src/plugins.ts | 17 +- yarn.lock | 52 ++++ 27 files changed, 732 insertions(+), 883 deletions(-) create mode 100644 packages/base/src/Lumino.tsx create mode 100644 packages/base/style/tabPanel.css diff --git a/package.json b/package.json index bf63917bd..ee8315c59 100644 --- a/package.json +++ b/package.json @@ -1,76 +1,73 @@ { - "name": "@jupytergis/jupytergis-root", - "version": "0.6.2", - "private": true, - "homepage": "https://github.com/geojupyter/jupytergis", - "repository": { - "type": "git", - "url": "https://github.com/geojupyter/jupytergis.git" - }, - "license": "BSD-3-Clause", - "description": "A JupyterLab extension for GIS.", - "keywords": [ - "jupyter", - "jupyterlab", - "jupyterlab-extension" - ], - "bugs": { - "url": "https://github.com/geojupyter/jupytergis/issues" - }, - "author": { - "name": "Jupytergis contributors" - }, - "files": [ - "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", - "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}" - ], - "workspaces": [ - "packages/*", - "python/jupytergis_core", - "python/jupytergis_lab", - "python/jupytergis_qgis" - ], - "scripts": { - "build": "lerna run build", - "build:prod": "lerna run build:prod --skip-nx-cache", - "build:test": "lerna run build:test", - "build:dev": "lerna run build:dev", - "bump:js:version": "lerna version --no-push --force-publish --no-git-tag-version --yes", - "clean": "lerna run clean", - "clean:all": "lerna run clean:all", - "eslint": "eslint . --ext .ts,.tsx --cache --fix", - "eslint:check": "eslint . --ext .ts,.tsx", - "prettier": "prettier --write \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml}\"", - "prettier:check": "prettier --check \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml}\"", - "lint:check": "jlpm run prettier:check && jlpm run eslint:check", - "lint": "jlpm run prettier && jlpm run eslint", - "test": "lerna run test", - "dev": "python scripts/dev-install.py", - "build:packages": "python scripts/build_packages.py", - "watch:lib": "lerna run --stream watch", - "watch": "lerna watch -- lerna run build --scope=\\$LERNA_PACKAGE_NAME --include-dependents", - "build:lab": "lerna run" - }, - "resolutions": { - "@jupyterlab/apputils": "^4.0.0", - "@lumino/coreutils": "^2.0.0", - "@jupyterlab/notebook": "^4.0.0", - "@jupyterlab/services": " ^7.0.0" - }, - "devDependencies": { - "@types/webpack-env": "^1.18.5", - "@typescript-eslint/eslint-plugin": "5.55.0", - "@typescript-eslint/parser": "5.55.0", - "copy-webpack-plugin": "^10.0.0", - "eslint": "^8.36.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-prettier": "^5.0.1", - "lerna": "^8.1.9", - "npm-run-all": "^4.1.5", - "prettier": "^3.0.0", - "rimraf": "^3.0.2", - "typescript": "^5", - "webpack": "^5.76.3" - } + "name": "@jupytergis/jupytergis-root", + "version": "0.6.2", + "private": true, + "homepage": "https://github.com/geojupyter/jupytergis", + "repository": { + "type": "git", + "url": "https://github.com/geojupyter/jupytergis.git" + }, + "license": "BSD-3-Clause", + "description": "A JupyterLab extension for GIS.", + "keywords": ["jupyter", "jupyterlab", "jupyterlab-extension"], + "bugs": { + "url": "https://github.com/geojupyter/jupytergis/issues" + }, + "author": { + "name": "Jupytergis contributors" + }, + "files": [ + "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}" + ], + "workspaces": [ + "packages/*", + "python/jupytergis_core", + "python/jupytergis_lab", + "python/jupytergis_qgis" + ], + "scripts": { + "build": "lerna run build", + "build:prod": "lerna run build:prod --skip-nx-cache", + "build:test": "lerna run build:test", + "build:dev": "lerna run build:dev", + "bump:js:version": "lerna version --no-push --force-publish --no-git-tag-version --yes", + "clean": "lerna run clean", + "clean:all": "lerna run clean:all", + "eslint": "eslint . --ext .ts,.tsx --cache --fix", + "eslint:check": "eslint . --ext .ts,.tsx", + "prettier": "prettier --write \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml}\"", + "prettier:check": "prettier --check \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml}\"", + "lint:check": "jlpm run prettier:check && jlpm run eslint:check", + "lint": "jlpm run prettier && jlpm run eslint", + "test": "lerna run test", + "dev": "python scripts/dev-install.py", + "build:packages": "python scripts/build_packages.py", + "watch:lib": "lerna run --stream watch", + "watch": "lerna watch -- lerna run build --scope=\\$LERNA_PACKAGE_NAME --include-dependents", + "build:lab": "lerna run" + }, + "resolutions": { + "@jupyterlab/apputils": "^4.0.0", + "@lumino/coreutils": "^2.0.0", + "@jupyterlab/notebook": "^4.0.0", + "@jupyterlab/services": " ^7.0.0" + }, + "devDependencies": { + "@types/webpack-env": "^1.18.5", + "@typescript-eslint/eslint-plugin": "5.55.0", + "@typescript-eslint/parser": "5.55.0", + "copy-webpack-plugin": "^10.0.0", + "eslint": "^8.36.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-prettier": "^5.0.1", + "lerna": "^8.1.9", + "npm-run-all": "^4.1.5", + "prettier": "^3.0.0", + "rimraf": "^3.0.2", + "typescript": "^5", + "webpack": "^5.76.3" + }, + "dependencies": {} } diff --git a/packages/base/src/Lumino.tsx b/packages/base/src/Lumino.tsx new file mode 100644 index 000000000..85e62de0b --- /dev/null +++ b/packages/base/src/Lumino.tsx @@ -0,0 +1,43 @@ +import { Widget } from '@lumino/widgets'; +import * as React from 'react'; + +type LuminoProps = { + id?: string; + height?: string | number; + children: Widget; +}; + +export const Lumino = (props: LuminoProps) => { + const ref = React.useRef(null); + const { children, id, height } = props; + React.useEffect(() => { + if (ref && ref.current) { + try { + Widget.attach(children, ref.current); + } catch (e) { + console.warn('Exception while attaching Lumino widget.', e); + } + return () => { + try { + if (children.isAttached || children.node.isConnected) { + children.dispose(); + Widget.detach(children); + } + } catch (e) { + // no-op. + // console.debug('Exception while detaching Lumino widget.', e); + } + }; + } + }, [ref, children]); + return ( +
+ ); +}; + +Lumino.defaultProps = { + id: 'lumino-id', + height: '100%', +}; + +export default Lumino; diff --git a/packages/base/src/annotations/components/Annotation.tsx b/packages/base/src/annotations/components/Annotation.tsx index f3aa8196d..16d6ce394 100644 --- a/packages/base/src/annotations/components/Annotation.tsx +++ b/packages/base/src/annotations/components/Annotation.tsx @@ -9,13 +9,12 @@ import { showDialog, Dialog } from '@jupyterlab/apputils'; import { Button } from '@jupyterlab/ui-components'; import React, { useMemo, useState } from 'react'; -import { IControlPanelModel } from '@/src/types'; import { Message } from './Message'; export interface IAnnotationProps { itemId: string; annotationModel: IAnnotationModel; - rightPanelModel?: IControlPanelModel; + rightPanelModel?: IJupyterGISModel; children?: JSX.Element[] | JSX.Element; } @@ -26,20 +25,10 @@ const Annotation: React.FC = ({ children, }) => { const [messageContent, setMessageContent] = useState(''); - const [jgisModel, setJgisModel] = useState( - rightPanelModel?.jGISModel, - ); - + const jgisModel = rightPanelModel; const annotation = annotationModel.getAnnotation(itemId); const contents = useMemo(() => annotation?.contents ?? [], [annotation]); - /** - * Update the model when it changes. - */ - rightPanelModel?.documentChanged.connect((_, widget) => { - setJgisModel(widget?.model); - }); - const handleSubmit = () => { annotationModel.addContent(itemId, messageContent); setMessageContent(''); diff --git a/packages/base/src/mainview/mainView.tsx b/packages/base/src/mainview/mainView.tsx index 8b0a1ece8..07d600ead 100644 --- a/packages/base/src/mainview/mainView.tsx +++ b/packages/base/src/mainview/mainView.tsx @@ -1,6 +1,7 @@ import { MapChange } from '@jupyter/ydoc'; import { IAnnotation, + IAnnotationModel, IDict, IGeoTiffSource, IHeatmapLayer, @@ -8,6 +9,7 @@ import { IImageLayer, IImageSource, IJGISFilterItem, + IJGISFormSchemaRegistry, IJGISLayer, IJGISLayerDocChange, IJGISLayerTreeDocChange, @@ -34,6 +36,7 @@ import { import { showErrorMessage } from '@jupyterlab/apputils'; import { IObservableMap, ObservableMap } from '@jupyterlab/observables'; import { User } from '@jupyterlab/services'; +import { IStateDB } from '@jupyterlab/statedb'; import { CommandRegistry } from '@lumino/commands'; import { JSONValue, UUID } from '@lumino/coreutils'; import { ContextMenu } from '@lumino/widgets'; @@ -90,6 +93,7 @@ import CollaboratorPointers, { ClientPointer } from './CollaboratorPointers'; import { FollowIndicator } from './FollowIndicator'; import TemporalSlider from './TemporalSlider'; import { MainViewModel } from './mainviewmodel'; +import { LeftPanelComponent, RightPanelComponent } from '../panelview'; type OlLayerTypes = | TileLayer @@ -102,6 +106,9 @@ type OlLayerTypes = | ImageLayer; interface IProps { viewModel: MainViewModel; + state?: IStateDB; + formSchemaRegistry?: IJGISFormSchemaRegistry; + annotationModel?: IAnnotationModel; } interface IStates { @@ -123,6 +130,11 @@ interface IStates { export class MainView extends React.Component { constructor(props: IProps) { super(props); + this._state = props.state; + + this._formSchemaRegistry = props.formSchemaRegistry; + + this._annotationModel = props.annotationModel; // Enforce the map to take the full available width in the case of Jupyter Notebook viewer const el = document.getElementById('main-panel'); @@ -2220,6 +2232,21 @@ export class MainView extends React.Component { scale={this.state.scale} />
+ + {this._state && ( + + )} + {this._formSchemaRegistry && this._annotationModel && ( + + )} ); } @@ -2240,4 +2267,7 @@ export class MainView extends React.Component { private _originalFeatures: IDict[]> = {}; private _highlightLayer: VectorLayer; private _updateCenter: CallableFunction; + private _state?: IStateDB; + private _formSchemaRegistry?: IJGISFormSchemaRegistry; + private _annotationModel?: IAnnotationModel; } diff --git a/packages/base/src/mainview/mainviewwidget.tsx b/packages/base/src/mainview/mainviewwidget.tsx index 465d77a8b..c04aa80c3 100644 --- a/packages/base/src/mainview/mainviewwidget.tsx +++ b/packages/base/src/mainview/mainviewwidget.tsx @@ -1,22 +1,40 @@ +import { IAnnotationModel, IJGISFormSchemaRegistry } from '@jupytergis/schema'; import { ReactWidget } from '@jupyterlab/apputils'; +import { IStateDB } from '@jupyterlab/statedb'; import * as React from 'react'; import { MainView } from './mainView'; import { MainViewModel } from './mainviewmodel'; +export interface IOptions { + mainViewModel: MainViewModel; + state?: IStateDB; + formSchemaRegistry?: IJGISFormSchemaRegistry; + annotationModel?: IAnnotationModel; +} + export class JupyterGISMainViewPanel extends ReactWidget { /** * Construct a `JupyterGISPanel`. */ - constructor(options: { mainViewModel: MainViewModel }) { + constructor(options: IOptions) { super(); - this._mainViewModel = options.mainViewModel; + this._state = options.state; this.addClass('jp-jupytergis-panel'); + this._options = options; } render(): JSX.Element { - return ; + return ( + + ); } - private _mainViewModel: MainViewModel; + private _state?: IStateDB; + private _options: IOptions; } diff --git a/packages/base/src/panelview/annotationPanel.tsx b/packages/base/src/panelview/annotationPanel.tsx index abc5d1b94..f4d7e3553 100644 --- a/packages/base/src/panelview/annotationPanel.tsx +++ b/packages/base/src/panelview/annotationPanel.tsx @@ -1,19 +1,19 @@ -import { IAnnotationModel } from '@jupytergis/schema'; -import { PanelWithToolbar, ReactWidget } from '@jupyterlab/ui-components'; +import { IAnnotationModel, IJupyterGISModel } from '@jupytergis/schema'; import React, { Component } from 'react'; import Annotation from '@/src/annotations/components/Annotation'; -import { IControlPanelModel } from '@/src/types'; interface IAnnotationPanelProps { annotationModel: IAnnotationModel; - rightPanelModel: IControlPanelModel; + rightPanelModel: IJupyterGISModel; } export class AnnotationsPanel extends Component { constructor(props: IAnnotationPanelProps) { super(props); + console.log(props.annotationModel); + const updateCallback = () => { this.forceUpdate(); }; @@ -21,18 +21,8 @@ export class AnnotationsPanel extends Component { this._annotationModel = props.annotationModel; this._rightPanelModel = props.rightPanelModel; - this._annotationModel.modelChanged.connect(async () => { - // await this._annotationModel?.context?.ready; - - this._annotationModel?.model?.sharedMetadataChanged.disconnect( - updateCallback, - ); - this._annotationModel = props.annotationModel; - this._annotationModel?.model?.sharedMetadataChanged.connect( - updateCallback, - ); - this.forceUpdate(); - }); + this._annotationModel?.model?.sharedMetadataChanged.connect(updateCallback); + this.forceUpdate(); } render(): JSX.Element { @@ -59,37 +49,5 @@ export class AnnotationsPanel extends Component { } private _annotationModel: IAnnotationModel; - private _rightPanelModel: IControlPanelModel; -} - -export class Annotations extends PanelWithToolbar { - constructor(options: Annotations.IOptions) { - super({}); - - this.title.label = 'Annotations'; - this.addClass('jgis-scrollable'); - - this._annotationModel = options.annotationModel; - this._rightPanelModel = options.rightPanelModel; - - this._widget = ReactWidget.create( - , - ); - - this.addWidget(this._widget); - } - - private _widget: ReactWidget; - private _annotationModel: IAnnotationModel; - private _rightPanelModel: IControlPanelModel; -} - -export namespace Annotations { - export interface IOptions { - annotationModel: IAnnotationModel; - rightPanelModel: IControlPanelModel; - } + private _rightPanelModel: IJupyterGISModel; } diff --git a/packages/base/src/panelview/components/filter-panel/Filter.tsx b/packages/base/src/panelview/components/filter-panel/Filter.tsx index ce694754d..464898560 100644 --- a/packages/base/src/panelview/components/filter-panel/Filter.tsx +++ b/packages/base/src/panelview/components/filter-panel/Filter.tsx @@ -3,58 +3,20 @@ import { IDict, IJGISFilterItem, IJupyterGISModel, - IJupyterGISTracker, } from '@jupytergis/schema'; -import { Button, ReactWidget } from '@jupyterlab/ui-components'; -import { Panel } from '@lumino/widgets'; +import { Button } from '@jupyterlab/ui-components'; import { cloneDeep } from 'lodash'; import React, { ChangeEvent, useEffect, useRef, useState } from 'react'; import { debounce, loadFile } from '@/src/tools'; -import { IControlPanelModel } from '@/src/types'; import FilterRow from './FilterRow'; -/** - * The filters panel widget. - */ -export class FilterPanel extends Panel { - constructor(options: FilterPanel.IOptions) { - super(); - this._model = options.model; - this._tracker = options.tracker; - - this.id = 'jupytergis::layerTree'; - // this.addClass(LAYERS_PANEL_CLASS); - - this.addWidget( - ReactWidget.create( - , - ), - ); - } - - private _model: IControlPanelModel | undefined; - private _tracker: IJupyterGISTracker; -} - -export namespace FilterPanel { - export interface IOptions { - model: IControlPanelModel; - tracker: IJupyterGISTracker; - } -} - interface IFilterComponentProps { - model: IControlPanelModel; - tracker: IJupyterGISTracker; + model: IJupyterGISModel; } const FilterComponent: React.FC = props => { const featuresInLayerRef = useRef({}); - const [widgetId, setWidgetId] = useState(''); const [logicalOp, setLogicalOp] = useState('all'); const [selectedLayer, setSelectedLayer] = useState(''); const [shouldDisplay, setShouldDisplay] = useState(false); @@ -62,42 +24,14 @@ const FilterComponent: React.FC = props => { const [featuresInLayer, setFeaturesInLayer] = useState< Record> >({}); - const [model, setModel] = useState( - props.model.jGISModel, - ); - - props.model?.documentChanged.connect((_, widget) => { - setModel(widget?.model); - }); - - // Reset state values when current widget changes - useEffect(() => { - const handleCurrentChanged = () => { - if (props.tracker.currentWidget?.id === widgetId) { - return; - } - - if (props.tracker.currentWidget) { - setWidgetId(props.tracker.currentWidget.id); - } - setFeaturesInLayer({}); - setFilterRows([]); - setLogicalOp('all'); - setSelectedLayer(''); - }; - props.tracker.currentChanged.connect(handleCurrentChanged); - - return () => { - props.tracker.currentChanged.disconnect(handleCurrentChanged); - }; - }, []); + const model = props.model; useEffect(() => { // Keep layer selected when widget changes if (model?.localState?.selected?.value) { setSelectedLayer(Object.keys(model?.localState?.selected?.value)[0]); } - }, [widgetId]); + }, []); useEffect(() => { const handleClientStateChanged = () => { @@ -112,7 +46,7 @@ const FilterComponent: React.FC = props => { const handleSharedOptionsChanged = (_: any, keys: any) => { // model changes when current widget changes, don't want this to run in that case - if (props.tracker.currentWidget?.id === widgetId && keys.has('zoom')) { + if (keys.has('zoom')) { if (!model?.localState?.selected?.value) { return; } @@ -261,7 +195,10 @@ const FilterComponent: React.FC = props => { return; } - layer.filters = { logicalOp: op ?? logicalOp, appliedFilters: filters }; + layer.filters = { + logicalOp: op ?? logicalOp, + appliedFilters: filters, + }; model?.sharedModel.updateLayer(selectedLayer, layer); }; @@ -293,7 +230,11 @@ const FilterComponent: React.FC = props => { ))}
-
+
)} + {!shouldDisplay && ( +
No layer selected
+ )} ); }; diff --git a/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx b/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx index 842f1111f..10e479036 100644 --- a/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx +++ b/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx @@ -4,93 +4,26 @@ import { IDict, IJupyterGISClientState, IJupyterGISModel, - IJupyterGISTracker, } from '@jupytergis/schema'; import { User } from '@jupyterlab/services'; -import { LabIcon, ReactWidget, caretDownIcon } from '@jupyterlab/ui-components'; -import { Panel } from '@lumino/widgets'; +import { LabIcon, caretDownIcon } from '@jupyterlab/ui-components'; import React, { useEffect, useRef, useState } from 'react'; -import { IControlPanelModel } from '@/src/types'; - -export class IdentifyPanel extends Panel { - constructor(options: IdentifyPanel.IOptions) { - super(); - this._model = options.model; - this._tracker = options.tracker; - - this.id = 'jupytergis::identifyPanel'; - this.title.caption = 'Identify'; - this.title.label = 'Identify'; - this.addClass('jgis-scrollable'); - - this.addWidget( - ReactWidget.create( - , - ), - ); - } - - private _model: IControlPanelModel | undefined; - private _tracker: IJupyterGISTracker; -} - -export namespace IdentifyPanel { - export interface IOptions { - model: IControlPanelModel; - tracker: IJupyterGISTracker; - } -} - interface IIdentifyComponentProps { - controlPanelModel: IControlPanelModel; - tracker: IJupyterGISTracker; + model: IJupyterGISModel; } -const IdentifyPanelComponent: React.FC = ({ - controlPanelModel, - tracker, -}) => { - const [widgetId, setWidgetId] = useState(''); +export const IdentifyPanelComponent = (options: IIdentifyComponentProps) => { const [features, setFeatures] = useState>(); const [visibleFeatures, setVisibleFeatures] = useState>({ 0: true, }); const [remoteUser, setRemoteUser] = useState(null); - const [jgisModel, setJgisModel] = useState( - controlPanelModel?.jGISModel, - ); + const jgisModel = options.model; const featuresRef = useRef(features); - /** - * Update the model when it changes. - */ - controlPanelModel?.documentChanged.connect((_, widget) => { - setJgisModel(widget?.model); - }); // Reset state values when current widget changes - useEffect(() => { - const handleCurrentChanged = () => { - if (tracker.currentWidget?.id === widgetId) { - return; - } - - if (tracker.currentWidget) { - setWidgetId(tracker.currentWidget.id); - } - setFeatures({}); - setVisibleFeatures({ 0: true }); - }; - tracker.currentChanged.connect(handleCurrentChanged); - - return () => { - tracker.currentChanged.disconnect(handleCurrentChanged); - }; - }, []); useEffect(() => { featuresRef.current = features; @@ -216,7 +149,9 @@ const IdentifyPanelComponent: React.FC = ({ {typeof value === 'string' && /<\/?[a-z][\s\S]*>/i.test(value) ? ( ) : ( {String(value)} @@ -230,5 +165,3 @@ const IdentifyPanelComponent: React.FC = ({
); }; - -export default IdentifyPanel; diff --git a/packages/base/src/panelview/components/layers.tsx b/packages/base/src/panelview/components/layers.tsx index e5069a0b3..455310bef 100644 --- a/packages/base/src/panelview/components/layers.tsx +++ b/packages/base/src/panelview/components/layers.tsx @@ -3,32 +3,25 @@ import { IJGISLayerTree, IJupyterGISClientState, IJupyterGISModel, + ISelection, + SelectionType, } from '@jupytergis/schema'; import { DOMUtils } from '@jupyterlab/apputils'; import { IStateDB } from '@jupyterlab/statedb'; -import { - Button, - LabIcon, - ReactWidget, - caretDownIcon, -} from '@jupyterlab/ui-components'; -import { ReadonlyPartialJSONObject } from '@lumino/coreutils'; -import { Panel } from '@lumino/widgets'; +import { Button, LabIcon, caretDownIcon } from '@jupyterlab/ui-components'; +import { CommandRegistry } from '@lumino/commands'; +import { ReadonlyPartialJSONObject, UUID } from '@lumino/coreutils'; import React, { MouseEvent as ReactMouseEvent, useEffect, useState, } from 'react'; -import { icons } from '@/src/constants'; +import { CommandIDs, icons } from '@/src/constants'; import { nonVisibilityIcon, visibilityIcon } from '@/src/icons'; -import { - ILayerPanelOptions, - ILeftPanelClickHandlerParams, -} from '@/src/panelview/leftpanel'; -import { IControlPanelModel } from '@/src/types'; +import { ILeftPanelClickHandlerParams } from '@/src/panelview/leftpanel'; -const LAYERS_PANEL_CLASS = 'jp-gis-layerPanel'; +// const LAYERS_PANEL_CLASS = 'jp-gis-layerPanel'; const LAYER_GROUP_CLASS = 'jp-gis-layerGroup'; const LAYER_GROUP_HEADER_CLASS = 'jp-gis-layerGroupHeader'; const LAYER_GROUP_COLLAPSER_CLASS = 'jp-gis-layerGroupCollapser'; @@ -39,119 +32,108 @@ const LAYER_ICON_CLASS = 'jp-gis-layerIcon'; const LAYER_TEXT_CLASS = 'jp-gis-layerText data-jgis-keybinding'; /** - * The layers panel widget. + * Properties of the layers body component. */ -export class LayersPanel extends Panel { - constructor(options: ILayerPanelOptions) { - super(); - this._model = options.model; - this._onSelect = options.onSelect; - this._state = options.state; - - this.id = 'jupytergis::layerTree'; - this.addClass(LAYERS_PANEL_CLASS); - - this.addWidget( - ReactWidget.create( - , - ), - ); - this.node.ondragover = this._onDragOver; - this.node.ondrop = this._onDrop; - } +interface IBodyProps { + model: IJupyterGISModel; + commands: CommandRegistry; + state: IStateDB; +} - // This happens when dragging over empty space below the tree. - private _onDragOver = (e: DragEvent) => { - e.stopPropagation(); - e.preventDefault(); - this.node.appendChild(Private.dragIndicator); - Private.dragInfo.dragOverElement = null; - Private.dragInfo.dragOverPosition = null; - }; +/** + * The body component of the panel. + */ +export function LayersBodyComponent(props: IBodyProps): JSX.Element { + const model = props.model; + const id = UUID.uuid4(); + + const [layerTree, setLayerTree] = useState( + model?.getLayerTree() || [], + ); - private _onDrop = (e: DragEvent) => { - Private.dragIndicator.style.display = 'none'; + const notifyCommands = () => { + // Notify commands that need updating + props.commands.notifyCommandChanged(CommandIDs.identify); + props.commands.notifyCommandChanged(CommandIDs.temporalController); + }; - if (this._model === undefined) { + const onSelect = ({ + type, + item, + nodeId, + event, + }: ILeftPanelClickHandlerParams) => { + if (!props.model || !nodeId) { return; } - const { jGISModel: model } = this._model; - const { draggedElement, dragOverElement, dragOverPosition } = - Private.dragInfo; + const selectedValue = props.model.localState?.selected?.value; + const node = document.getElementById(nodeId); - if (dragOverElement === 'error') { + if (!node) { return; } - if (!draggedElement) { - return; - } - const draggedId = draggedElement.dataset.id; - if (!draggedId) { - return; - } + node.tabIndex = 0; + node.focus(); - // Element has been dropped in the empty zone below the tree. - if (dragOverElement === null) { - model?.moveItemsToGroup([draggedId], '', 0); + // Early return if no selection exists + if (!selectedValue) { + resetSelected(type, nodeId, item); return; } - const dragOverId = dragOverElement.dataset.id; - if (!dragOverId) { + // Don't want to reset selected if right clicking a selected item + if (!event.ctrlKey && event.button === 2 && item in selectedValue) { return; } - // Handle the special case where we want to drop the element on top of the first - // element of a group. - if ( - dragOverElement.classList.contains(LAYER_GROUP_HEADER_CLASS) && - dragOverPosition === 'below' - ) { - model?.moveItemsToGroup([draggedId], dragOverId); + // Reset selection for normal left click + if (!event.ctrlKey) { + resetSelected(type, nodeId, item); return; } - model?.moveItemRelatedTo( - draggedId, - dragOverId, - dragOverPosition === 'above', - ); - }; + if (nodeId) { + // Check if new selection is the same type as previous selections + const isSelectedSameType = Object.values(selectedValue).some( + selection => selection.type === type, + ); - private _model: IControlPanelModel | undefined; - private _state: IStateDB; - private _onSelect: ({ - type, - item, - nodeId, - }: ILeftPanelClickHandlerParams) => void; -} + if (!isSelectedSameType) { + // Selecting a new type, so reset selected + resetSelected(type, nodeId, item); + return; + } -/** - * Properties of the layers body component. - */ -interface IBodyProps { - model: IControlPanelModel; - state: IStateDB; - onSelect: ({ type, item, nodeId }: ILeftPanelClickHandlerParams) => void; -} + // If types are the same add the selection + const updatedSelectedValue = { + ...selectedValue, + [item]: { type, selectedNodeId: nodeId }, + }; -/** - * The body component of the panel. - */ -const LayersBodyComponent: React.FC = props => { - const [model, setModel] = useState( - props.model?.jGISModel, - ); - const [layerTree, setLayerTree] = useState( - model?.getLayerTree() || [], - ); + props.model.syncSelected(updatedSelectedValue, id); + + notifyCommands(); + } + }; + + const resetSelected = ( + type: SelectionType, + nodeId?: string, + item?: string, + ) => { + const selection: { [key: string]: ISelection } = {}; + if (item && nodeId) { + selection[item] = { + type, + selectedNodeId: nodeId, + }; + } + props.model.syncSelected(selection, id); + + notifyCommands(); + }; /** * Propagate the layer selection. @@ -162,7 +144,7 @@ const LayersBodyComponent: React.FC = props => { nodeId, event, }: ILeftPanelClickHandlerParams) => { - props.onSelect({ type, item, nodeId, event }); + onSelect({ type, item, nodeId, event }); }; /** @@ -182,14 +164,6 @@ const LayersBodyComponent: React.FC = props => { }; }, [model]); - /** - * Update the model when it changes. - */ - props.model?.documentChanged.connect((_, widget) => { - setModel(widget?.model); - setLayerTree(widget?.model?.getLayerTree() || []); - }); - return (
{layerTree @@ -215,7 +189,7 @@ const LayersBodyComponent: React.FC = props => { )}
); -}; +} /** * Properties of the layer group component. @@ -404,7 +378,12 @@ const LayerComponent: React.FC = props => { const setSelection = (event: ReactMouseEvent) => { const childId = event.currentTarget.children.namedItem(id)?.id; - onClick({ type: 'layer', item: layerId, nodeId: childId, event }); + onClick({ + type: 'layer', + item: layerId, + nodeId: childId, + event, + }); }; return ( diff --git a/packages/base/src/panelview/leftpanel.tsx b/packages/base/src/panelview/leftpanel.tsx index 29ad9c1e0..61d0021f6 100644 --- a/packages/base/src/panelview/leftpanel.tsx +++ b/packages/base/src/panelview/leftpanel.tsx @@ -1,28 +1,24 @@ -import { - IJupyterGISModel, - IJupyterGISTracker, - ISelection, - JupyterGISDoc, - SelectionType, -} from '@jupytergis/schema'; +import { IJupyterGISModel, SelectionType } from '@jupytergis/schema'; import { IStateDB } from '@jupyterlab/statedb'; -import { SidePanel } from '@jupyterlab/ui-components'; import { CommandRegistry } from '@lumino/commands'; -import { Message } from '@lumino/messaging'; import { MouseEvent as ReactMouseEvent } from 'react'; +import * as React from 'react'; -import { CommandIDs } from '@/src/constants'; -import StacPanel from '@/src/stacBrowser/StacPanel'; -import { IControlPanelModel } from '@/src/types'; -import { FilterPanel } from './components/filter-panel/Filter'; -import { LayersPanel } from './components/layers'; -import { ControlPanelHeader } from './header'; +import { LayersBodyComponent } from './components/layers'; +import { + PanelTabs, + TabsContent, + TabsList, + TabsTrigger, +} from '../shared/components/Tabs'; +import StacBrowser from '../stacBrowser/StacBrowser'; +import FilterComponent from './components/filter-panel/Filter'; /** * Options of the left panel widget. */ export interface ILeftPanelOptions { - model: IControlPanelModel; + model: IJupyterGISModel; onSelect: ({ type, item, nodeId }: ILeftPanelClickHandlerParams) => void; } @@ -37,208 +33,59 @@ export interface ILeftPanelClickHandlerParams { event: ReactMouseEvent; } -export class LeftPanelWidget extends SidePanel { - constructor(options: LeftPanelWidget.IOptions) { - super(); - this.addClass('jGIS-sidepanel-widget'); - this.addClass('data-jgis-keybinding'); - this.node.tabIndex = 0; - - this._model = options.model; - this._state = options.state; - this._commands = options.commands; - - const header = new ControlPanelHeader(); - this.header.addWidget(header); - - const layerTree = new LayersPanel({ - model: this._model, - state: this._state, - onSelect: this._onSelect, - }); - - layerTree.title.caption = 'Layer tree'; - layerTree.title.label = 'Layers'; - this.addWidget(layerTree); - - const stacPanel = new StacPanel({ - model: this._model, - tracker: options.tracker, - }); - - stacPanel.title.caption = 'STAC'; - stacPanel.title.label = 'STAC'; - this.addWidget(stacPanel); - - const filterPanel = new FilterPanel({ - model: this._model, - tracker: options.tracker, - }); - - filterPanel.title.caption = 'Filters'; - filterPanel.title.label = 'Filters'; - this.addWidget(filterPanel); - - this._handleFileChange = () => { - header.title.label = this._currentModel?.filePath || '-'; - }; - - options.tracker.currentChanged.connect((_, changed) => { - if (changed) { - if (this._currentModel) { - this._currentModel.pathChanged.disconnect(this._handleFileChange); - } - this._currentModel = changed.model; - header.title.label = changed.model.filePath; - this._currentModel.pathChanged.connect(this._handleFileChange); - } else { - header.title.label = '-'; - this._currentModel = null; - } - }); - } - - dispose(): void { - super.dispose(); - } - - 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 = ({ - type, - item, - nodeId, - event, - }: ILeftPanelClickHandlerParams) => { - 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); - - this._notifyCommands(); - } - }; - - 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); - this._notifyCommands(); - } - - private _notifyCommands() { - // Notify commands that need updating - this._commands.notifyCommandChanged(CommandIDs.identify); - this._commands.notifyCommandChanged(CommandIDs.temporalController); - } - - private _handleFileChange: () => void; - private _currentModel: IJupyterGISModel | null; - private _lastSelectedNodeId: string; - private _model: IControlPanelModel; - private _state: IStateDB; - private _commands: CommandRegistry; +interface ILeftComponentProps { + model: IJupyterGISModel; + state: IStateDB; + commands: CommandRegistry; } -export namespace LeftPanelWidget { - export interface IOptions { - model: IControlPanelModel; - tracker: IJupyterGISTracker; - state: IStateDB; - commands: CommandRegistry; - } - - export interface IProps { - filePath?: string; - sharedModel?: JupyterGISDoc; - } -} +export const LeftPanelComponent = (options: ILeftComponentProps) => { + const tabInfo = [ + { name: 'layers', title: 'Layers' }, + { name: 'stac', title: 'Stac Browser' }, + { name: 'filters', title: 'Filters' }, + ]; + const [curTab, setCurTab] = React.useState( + tabInfo[0].name, + ); + + return ( +
+ + + {tabInfo.map(e => { + return ( + { + console.log('HERE'); + if (curTab !== e.name) { + setCurTab(e.name); + } else { + setCurTab(''); + } + }} + > + {e.title} + + ); + })} + + + + + + + + + , + + +
+ ); +}; diff --git a/packages/base/src/panelview/objectproperties.tsx b/packages/base/src/panelview/objectproperties.tsx index 86f57d73e..bf9c0310e 100644 --- a/packages/base/src/panelview/objectproperties.tsx +++ b/packages/base/src/panelview/objectproperties.tsx @@ -2,87 +2,47 @@ import { IJGISFormSchemaRegistry, IJupyterGISClientState, IJupyterGISModel, - IJupyterGISTracker, } from '@jupytergis/schema'; -import { ReactWidget } from '@jupyterlab/apputils'; -import { PanelWithToolbar } from '@jupyterlab/ui-components'; import { Panel } from '@lumino/widgets'; import * as React from 'react'; import { v4 as uuid } from 'uuid'; import { EditForm } from '@/src/formbuilder/editform'; -import { IControlPanelModel } from '@/src/types'; - -export class ObjectProperties extends PanelWithToolbar { - constructor(params: ObjectProperties.IOptions) { - super(params); - this.title.label = 'Objects Properties'; - const body = ReactWidget.create( - , - ); - this.addWidget(body); - this.addClass('jGIS-sidebar-propertiespanel'); - } -} interface IStates { model: IJupyterGISModel | undefined; - selectedObject?: string; clientId: number | null; // ID of the yjs client id: string; // ID of the component, it is used to identify which component - //is the source of awareness updates. + selectedObject?: string; + setSelectedObject: any; } interface IProps { - cpModel: IControlPanelModel; - tracker: IJupyterGISTracker; formSchemaRegistry: IJGISFormSchemaRegistry; + model: IJupyterGISModel; + selectedObject: string | undefined; + setSelectedObject: any; } -class ObjectPropertiesReact extends React.Component { +export class ObjectPropertiesReact extends React.Component { constructor(props: IProps) { super(props); this.state = { - model: props.tracker.currentWidget?.model, - clientId: null, + clientId: props.model.getClientId(), id: uuid(), + model: props.model, + selectedObject: props.selectedObject, + setSelectedObject: props.setSelectedObject, }; - this.props.cpModel.jGISModel?.sharedLayersChanged.connect( - this._sharedJGISModelChanged, + this.props.model.clientStateChanged.connect( + this._onClientSharedStateChanged, ); - this.props.cpModel.jGISModel?.sharedSourcesChanged.connect( + + this.props.model?.sharedLayersChanged.connect(this._sharedJGISModelChanged); + this.props.model?.sharedSourcesChanged.connect( this._sharedJGISModelChanged, ); - this.props.cpModel.documentChanged.connect((_, changed) => { - if (changed) { - this.props.cpModel.disconnect(this._sharedJGISModelChanged); - this.props.cpModel.disconnect(this._onClientSharedStateChanged); - - changed.model.sharedLayersChanged.connect(this._sharedJGISModelChanged); - changed.model.sharedSourcesChanged.connect( - this._sharedJGISModelChanged, - ); - changed.model.clientStateChanged.connect( - this._onClientSharedStateChanged, - ); - this.setState(old => ({ - ...old, - model: changed.model, - filePath: changed.model.filePath, - clientId: changed.model.getClientId(), - })); - } else { - this.setState({ - model: undefined, - selectedObject: undefined, - }); - } - }); } private _sharedJGISModelChanged = (): void => { @@ -110,6 +70,7 @@ class ObjectPropertiesReact extends React.Component { const selectedObjectIds = Object.keys(selection || {}); // Only show object properties if ONE object is selected if (selection === undefined || selectedObjectIds.length !== 1) { + this.state.setSelectedObject(undefined); this.setState(old => ({ ...old, selectedObject: undefined, @@ -119,6 +80,7 @@ class ObjectPropertiesReact extends React.Component { const selectedObject = selectedObjectIds[0]; if (selectedObject !== this.state.selectedObject) { + this.state.setSelectedObject(selectedObject); this.setState(old => ({ ...old, selectedObject, @@ -131,7 +93,7 @@ class ObjectPropertiesReact extends React.Component { const selectedObject = this.state.selectedObject; if (!selectedObject || !this.state.model) { - return
; + return
No layer selected
; } let layerId: string | undefined = undefined; @@ -164,8 +126,7 @@ export namespace ObjectProperties { * Instantiation options for `ObjectProperties`. */ export interface IOptions extends Panel.IOptions { - controlPanelModel: IControlPanelModel; + model: IJupyterGISModel; formSchemaRegistry: IJGISFormSchemaRegistry; - tracker: IJupyterGISTracker; } } diff --git a/packages/base/src/panelview/rightpanel.tsx b/packages/base/src/panelview/rightpanel.tsx index 49d985c25..4be489292 100644 --- a/packages/base/src/panelview/rightpanel.tsx +++ b/packages/base/src/panelview/rightpanel.tsx @@ -2,108 +2,85 @@ import { IAnnotationModel, IJGISFormSchemaRegistry, IJupyterGISModel, - IJupyterGISTracker, - JupyterGISDoc, } from '@jupytergis/schema'; -import { SidePanel } from '@jupyterlab/ui-components'; +import * as React from 'react'; -import { IControlPanelModel } from '@/src/types'; -import { Annotations } from './annotationPanel'; -import IdentifyPanel from './components/identify-panel/IdentifyPanel'; -import { ControlPanelHeader } from './header'; -import { ObjectProperties } from './objectproperties'; - -export class RightPanelWidget extends SidePanel { - constructor(options: RightPanelWidget.IOptions) { - super(); - this.addClass('jGIS-sidepanel-widget'); - this.addClass('data-jgis-keybinding'); - this.node.tabIndex = 0; - - this._model = options.model; - this._annotationModel = options.annotationModel; - - const header = new ControlPanelHeader(); - this.header.addWidget(header); - const properties = new ObjectProperties({ - controlPanelModel: this._model, - formSchemaRegistry: options.formSchemaRegistry, - tracker: options.tracker, - }); - - this.addWidget(properties); - - const annotations = new Annotations({ - rightPanelModel: this._model, - annotationModel: this._annotationModel, - }); - this.addWidget(annotations); - - const identifyPanel = new IdentifyPanel({ - model: this._model, - tracker: options.tracker, - }); - identifyPanel.title.caption = 'Identify'; - identifyPanel.title.label = 'Identify'; - identifyPanel.addClass('jgis-scrollable'); - this.addWidget(identifyPanel); +import { AnnotationsPanel } from './annotationPanel'; +import { IdentifyPanelComponent } from './components/identify-panel/IdentifyPanel'; +import { ObjectPropertiesReact } from './objectproperties'; +import { + PanelTabs, + TabsContent, + TabsList, + TabsTrigger, +} from '../shared/components/Tabs'; - this._handleFileChange = () => { - header.title.label = this._currentModel?.filePath || '-'; - }; +interface IRightComponentProps { + formSchemaRegistry: IJGISFormSchemaRegistry; + annotationModel: IAnnotationModel; + model: IJupyterGISModel; +} - this._model.documentChanged.connect((_, changed) => { - if (changed) { - if (changed.model.sharedModel.editable) { - header.title.label = changed.model.filePath; - properties.show(); - } else { - header.title.label = `${changed.model.filePath} - Read Only`; - properties.hide(); - } - } else { - header.title.label = '-'; - } - }); +export const RightPanelComponent = (options: IRightComponentProps) => { + const [selectedObjectProperties, setSelectedObjectProperties] = + React.useState(undefined); - options.tracker.currentChanged.connect(async (_, changed) => { - if (changed) { - if (this._currentModel) { - this._currentModel.pathChanged.disconnect(this._handleFileChange); - } - this._currentModel = changed.model; - header.title.label = this._currentModel.filePath; - this._currentModel.pathChanged.connect(this._handleFileChange); - this._annotationModel.model = - options.tracker.currentWidget?.model || undefined; - // await changed.context.ready; - } else { - header.title.label = '-'; - this._currentModel = null; - this._annotationModel.model = undefined; - } - }); - } + const tabInfo = [ + { name: 'objectProperties', title: 'Object Properties' }, + { name: 'annotations', title: 'Annotations' }, + { name: 'identifyPanel', title: 'Identify Panels' }, + ]; - dispose(): void { - super.dispose(); - } + const [curTab, setCurTab] = React.useState( + tabInfo[0].name, + ); - private _currentModel: IJupyterGISModel | null; - private _handleFileChange: () => void; - private _model: IControlPanelModel; - private _annotationModel: IAnnotationModel; -} - -export namespace RightPanelWidget { - export interface IOptions { - model: IControlPanelModel; - tracker: IJupyterGISTracker; - formSchemaRegistry: IJGISFormSchemaRegistry; - annotationModel: IAnnotationModel; - } - export interface IProps { - filePath?: string; - sharedModel?: JupyterGISDoc; - } -} + return ( +
+ + + {tabInfo.map(e => { + return ( + { + console.log('HERE'); + if (curTab !== e.name) { + setCurTab(e.name); + } else { + setCurTab(''); + } + }} + > + {e.title} + + ); + })} + + + + + + + + + + + +
+ ); +}; diff --git a/packages/base/src/shared/components/Tabs.tsx b/packages/base/src/shared/components/Tabs.tsx index a09fd4738..b9cab62a2 100644 --- a/packages/base/src/shared/components/Tabs.tsx +++ b/packages/base/src/shared/components/Tabs.tsx @@ -3,6 +3,11 @@ import * as React from 'react'; import { cn } from './utils'; +interface IPanelTabProps { + className: string; + curTab: string | undefined; + children: any; +} const Tabs: React.FC> = ({ className, ...props @@ -16,6 +21,24 @@ const Tabs: React.FC> = ({ ); }; +const PanelTabs: React.FC = ({ + className, + curTab, + children, +}) => { + return ( + { + return; + }} + children={children} + /> + ); +}; + const TabsList: React.FC> = ({ className, ...props @@ -53,4 +76,4 @@ const TabsContent: React.FC< ); }; -export { Tabs, TabsContent, TabsList, TabsTrigger }; +export { Tabs, TabsContent, TabsList, TabsTrigger, PanelTabs }; diff --git a/packages/base/src/stacBrowser/StacBrowser.tsx b/packages/base/src/stacBrowser/StacBrowser.tsx index 5fa0ca456..8b4cab61d 100644 --- a/packages/base/src/stacBrowser/StacBrowser.tsx +++ b/packages/base/src/stacBrowser/StacBrowser.tsx @@ -1,36 +1,14 @@ -import { - IJupyterGISModel, - IJupyterGISTracker, - IJupyterGISWidget, -} from '@jupytergis/schema'; -import React, { useEffect, useState } from 'react'; +import { IJupyterGISModel } from '@jupytergis/schema'; +import * as React from 'react'; import StacPanelView from '@/src/stacBrowser/components/StacPanelView'; -import { IControlPanelModel } from '@/src/types'; interface IStacBrowserDialogProps { - controlPanelModel: IControlPanelModel; + controlPanelModel: IJupyterGISModel; } -const StacBrowser = ({ controlPanelModel }: IStacBrowserDialogProps) => { - const [jgisModel, setJgisModel] = useState( - controlPanelModel?.jGISModel, - ); - - useEffect(() => { - const handleCurrentChanged = ( - _: IJupyterGISTracker, - widget: IJupyterGISWidget | null, - ) => { - setJgisModel(widget?.model); - }; - - controlPanelModel.documentChanged.connect(handleCurrentChanged); - - return () => { - controlPanelModel.documentChanged.disconnect(handleCurrentChanged); - }; - }, [controlPanelModel]); +const StacBrowser = ({ controlPanelModel: model }: IStacBrowserDialogProps) => { + const jgisModel = model; return ; }; diff --git a/packages/base/src/stacBrowser/StacPanel.tsx b/packages/base/src/stacBrowser/StacPanel.tsx index d8f2dfc78..38db1da28 100644 --- a/packages/base/src/stacBrowser/StacPanel.tsx +++ b/packages/base/src/stacBrowser/StacPanel.tsx @@ -1,10 +1,9 @@ -import { IJupyterGISTracker } from '@jupytergis/schema'; +import { IJupyterGISModel } from '@jupytergis/schema'; import { ReactWidget } from '@jupyterlab/ui-components'; import { Panel } from '@lumino/widgets'; import React from 'react'; import StacBrowser from '@/src/stacBrowser/StacBrowser'; -import { IControlPanelModel } from '@/src/types'; export class StacPanel extends Panel { constructor(options: StacPanel.IOptions) { @@ -23,13 +22,12 @@ export class StacPanel extends Panel { ); } - private _model: IControlPanelModel | undefined; + private _model: IJupyterGISModel | undefined; } export namespace StacPanel { export interface IOptions { - model: IControlPanelModel; - tracker: IJupyterGISTracker; + model: IJupyterGISModel; } } diff --git a/packages/base/src/stacBrowser/components/StacPanelView.tsx b/packages/base/src/stacBrowser/components/StacPanelView.tsx index 06f3dcdba..c269f7576 100644 --- a/packages/base/src/stacBrowser/components/StacPanelView.tsx +++ b/packages/base/src/stacBrowser/components/StacPanelView.tsx @@ -39,7 +39,7 @@ const StacPanelView = ({ model }: IStacViewProps) => { } return ( - + Filters diff --git a/packages/base/src/widget.ts b/packages/base/src/widget.ts index 474e114ae..7d07300f3 100644 --- a/packages/base/src/widget.ts +++ b/packages/base/src/widget.ts @@ -1,4 +1,6 @@ import { + IAnnotationModel, + IJGISFormSchemaRegistry, IJupyterGISDocumentWidget, IJupyterGISModel, IJupyterGISOutputWidget, @@ -7,6 +9,7 @@ import { MainAreaWidget } from '@jupyterlab/apputils'; import { ConsolePanel, IConsoleTracker } from '@jupyterlab/console'; import { DocumentWidget } from '@jupyterlab/docregistry'; import { IObservableMap, ObservableMap } from '@jupyterlab/observables'; +import { IStateDB } from '@jupyterlab/statedb'; import { CommandRegistry } from '@lumino/commands'; import { JSONValue } from '@lumino/coreutils'; import { MessageLoop } from '@lumino/messaging'; @@ -96,10 +99,19 @@ export namespace JupyterGISOutputWidget { export class JupyterGISPanel extends SplitPanel { constructor(options: JupyterGISPanel.IOptions) { super({ orientation: 'vertical', spacing: 0 }); - const { model, consoleTracker, commandRegistry, ...consoleOption } = - options; + const { + model, + consoleTracker, + state, + commandRegistry, + formSchemaRegistry, + annotationModel, + ...consoleOption + } = options; + + this._state = state; this._initModel({ model, commandRegistry }); - this._initView(); + this._initView(formSchemaRegistry, annotationModel); this._consoleOption = { commandRegistry, ...consoleOption }; this._consoleTracker = consoleTracker; } @@ -116,9 +128,15 @@ export class JupyterGISPanel extends SplitPanel { }); } - _initView() { + _initView( + formSchemaRegistry?: IJGISFormSchemaRegistry, + annotationModel?: IAnnotationModel, + ) { this._jupyterGISMainViewPanel = new JupyterGISMainViewPanel({ mainViewModel: this._mainViewModel, + state: this._state, + formSchemaRegistry: formSchemaRegistry, + annotationModel: annotationModel, }); this.addWidget(this._jupyterGISMainViewPanel); SplitPanel.setStretch(this._jupyterGISMainViewPanel, 1); @@ -233,6 +251,7 @@ export class JupyterGISPanel extends SplitPanel { }, 250); } + private _state: IStateDB | undefined; private _mainViewModel: MainViewModel; private _view: ObservableMap; private _jupyterGISMainViewPanel: JupyterGISMainViewPanel; @@ -246,6 +265,9 @@ export namespace JupyterGISPanel { export interface IOptions extends Partial { model: IJupyterGISModel; commandRegistry: CommandRegistry; + state?: IStateDB; consoleTracker?: IConsoleTracker; + formSchemaRegistry?: IJGISFormSchemaRegistry; + annotationModel?: IAnnotationModel; } } diff --git a/packages/base/style/base.css b/packages/base/style/base.css index 71e35404a..7561673b6 100644 --- a/packages/base/style/base.css +++ b/packages/base/style/base.css @@ -10,7 +10,7 @@ @import url('./symbologyDialog.css'); @import url('./statusBar.css'); @import url('./temporalSlider.css'); -@import url('./stacBrowser.css'); +@import url('./tabPanel.css'); @import url('ol/ol.css'); @import url('./shared/button.css'); @import url('./shared/toggle.css'); @@ -34,7 +34,7 @@ /* Overwrite forms CSS */ .jGIS-property-panel .array-item-list { display: flex; - gap: 1rem; + background-color: var(--jp-layout-color0); } .jGIS-property-panel .rjsf .array-item:not(:last-child) { @@ -85,3 +85,19 @@ button.jp-mod-styled.jp-mod-reject { white-space: nowrap; border-width: 0; } + +.jgis-right-panel-container { + width: 330px; + top: 30px; + right: 0px; + position: absolute; + margin: 5px; +} + +.jgis-left-panel-container { + position: absolute; + width: 250px; + top: 30px; + left: 0px; + margin: 5px; +} diff --git a/packages/base/style/shared/tabs.css b/packages/base/style/shared/tabs.css index fe5606cf8..b2d46bf2a 100644 --- a/packages/base/style/shared/tabs.css +++ b/packages/base/style/shared/tabs.css @@ -3,12 +3,11 @@ height: 2.5rem; align-items: center; justify-content: space-evenly; - border-radius: 0.375rem; background-color: var(--jp-layout-color2); - padding: 0.25rem; color: var(--jp-ui-font-color0); gap: 1rem; width: 100%; + font-size: 9px; } .jgis-tabs-trigger { diff --git a/packages/base/style/stacBrowser.css b/packages/base/style/stacBrowser.css index 3085f83dd..ed16e9cb0 100644 --- a/packages/base/style/stacBrowser.css +++ b/packages/base/style/stacBrowser.css @@ -1,7 +1,9 @@ -.jgis-stac-browser-main { +.jgis-stac-panel-main { display: flex; flex-direction: column; gap: 1rem; + justify-content: center; + align-items: center; } .jgis-stac-browser-collection { diff --git a/packages/base/style/tabPanel.css b/packages/base/style/tabPanel.css new file mode 100644 index 000000000..cb76ef4ce --- /dev/null +++ b/packages/base/style/tabPanel.css @@ -0,0 +1,99 @@ +.jgis-panel-tab-content { + border-radius: 5px; + font-size: 10px; +} + +.jgis-stac-panel-main { + display: flex; + flex-direction: column; + gap: 1rem; + justify-content: center; + align-items: center; + background-color: var(--jp-layout-color0); +} + +.jgis-stac-browser-collection { + flex-wrap: wrap; + margin-top: 0.5rem; +} + +.jgis-stac-browser-section-item { + border-radius: 1rem !important; + cursor: pointer; +} + +.jgis-stac-browser-results-item { + border-radius: 1rem !important; + cursor: pointer; + white-space: normal !important; + height: auto !important; +} + +.jgis-stac-browser-results-list { + display: flex; + flex-direction: column; + gap: 1rem; + word-break: break-word; + padding: 0.25rem; + margin-right: 0.5rem; +} + +.jgis-stac-browser-date-picker { + display: flex; + justify-content: space-around; + flex-wrap: wrap; + gap: 0.5rem; +} + +.jgis-stac-browser-filters-panel { + display: flex; + flex-direction: column; + gap: 0.5rem; + align-items: flex-start; + padding: 0 0.5rem; +} + +.jgis-stac-filter-trigger { + max-width: fit-content; + border: 1px solid var(--jp-border-color0); + padding: 0.5rem; + cursor: pointer; + font-weight: bold; + display: flex; + align-items: center; + gap: 0.15rem; +} + +.jgis-stac-filter-trigger:disabled { + cursor: not-allowed; +} + +.jgis-stac-filter-section-container { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.jgis-stac-filter-section-badges { + display: flex; + flex-wrap: wrap; + gap: 0.25rem; +} + +.jgis-stac-datepicker-icon { + width: 1rem; + height: 1rem; +} + +.jgis-stac-badge { + gap: 0.25rem; + padding-right: 0.3rem !important; +} + +.jgis-stac-badge-icon:hover { + background-color: color-mix( + in srgb, + var(--jp-error-color0), + transparent 20% + ) !important; +} diff --git a/python/jupytergis_core/src/factory.ts b/python/jupytergis_core/src/factory.ts index a1a16979e..5817e5e6c 100644 --- a/python/jupytergis_core/src/factory.ts +++ b/python/jupytergis_core/src/factory.ts @@ -8,12 +8,15 @@ import { JupyterGISModel, IJupyterGISTracker, IJGISExternalCommandRegistry, + IJGISFormSchemaRegistry, + IAnnotationModel, } from '@jupytergis/schema'; import { IEditorMimeTypeService } from '@jupyterlab/codeeditor'; import { ConsolePanel, IConsoleTracker } from '@jupyterlab/console'; import { ABCWidgetFactory, DocumentRegistry } from '@jupyterlab/docregistry'; import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; import { Contents, ServiceManager } from '@jupyterlab/services'; +import { IStateDB } from '@jupyterlab/statedb'; import { CommandRegistry } from '@lumino/commands'; interface IOptions extends DocumentRegistry.IWidgetFactoryOptions { @@ -27,6 +30,9 @@ interface IOptions extends DocumentRegistry.IWidgetFactoryOptions { consoleTracker?: IConsoleTracker; backendCheck?: () => boolean; drive?: ICollaborativeDrive | null; + formSchemaRegistry: IJGISFormSchemaRegistry; + state: IStateDB; + annotationModel: IAnnotationModel; } export class JupyterGISDocumentWidgetFactory extends ABCWidgetFactory< @@ -65,6 +71,7 @@ export class JupyterGISDocumentWidgetFactory extends ABCWidgetFactory< if (this._contentsManager) { model.contentsManager = this._contentsManager; } + const content = new JupyterGISPanel({ model, manager: this.options.manager, @@ -73,13 +80,20 @@ export class JupyterGISDocumentWidgetFactory extends ABCWidgetFactory< rendermime: this.options.rendermime, consoleTracker: this.options.consoleTracker, commandRegistry: this.options.commands, + state: this.options.state, + formSchemaRegistry: this.options.formSchemaRegistry, + annotationModel: this.options.annotationModel, }); const toolbar = new ToolbarWidget({ commands: this._commands, model, externalCommands: this._externalCommandRegistry.getCommands(), }); - return new JupyterGISDocumentWidget({ context, content, toolbar }); + return new JupyterGISDocumentWidget({ + context, + content, + toolbar, + }); } private _commands: CommandRegistry; diff --git a/python/jupytergis_core/src/jgisplugin/plugins.ts b/python/jupytergis_core/src/jgisplugin/plugins.ts index d3c390680..f1139b060 100644 --- a/python/jupytergis_core/src/jgisplugin/plugins.ts +++ b/python/jupytergis_core/src/jgisplugin/plugins.ts @@ -13,6 +13,8 @@ import { JupyterGISDoc, SCHEMA_VERSION, ProcessingMerge, + IJGISFormSchemaRegistry, + IJGISFormSchemaRegistryToken, } from '@jupytergis/schema'; import { JupyterFrontEnd, @@ -31,6 +33,7 @@ import { IFileBrowserFactory } from '@jupyterlab/filebrowser'; import { ILauncher } from '@jupyterlab/launcher'; import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; import { ISettingRegistry } from '@jupyterlab/settingregistry'; +import { IStateDB } from '@jupyterlab/statedb'; import { JupyterGISDocumentWidgetFactory } from '../factory'; import { JupyterGISModelFactory } from './modelfactory'; @@ -53,10 +56,13 @@ const activate = async ( consoleTracker: IConsoleTracker, annotationModel: IAnnotationModel, settingRegistry: ISettingRegistry, + formSchemaRegistry: IJGISFormSchemaRegistry, + state: IStateDB, launcher: ILauncher | null, palette: ICommandPalette | null, drive: ICollaborativeDrive | null, ): Promise => { + formSchemaRegistry && state; if (PageConfig.getOption('jgis_expose_maps')) { window.jupytergisMaps = {}; } @@ -82,7 +88,10 @@ const activate = async ( contentFactory, rendermime, mimeTypeService: editorServices.mimeTypeService, + formSchemaRegistry: formSchemaRegistry, consoleTracker, + state: state, + annotationModel: annotationModel, }); // Registering the widget factory @@ -261,6 +270,8 @@ const jGISPlugin: JupyterFrontEndPlugin = { IConsoleTracker, IAnnotationToken, ISettingRegistry, + IJGISFormSchemaRegistryToken, + IStateDB, ], optional: [ILauncher, ICommandPalette, ICollaborativeDrive], autoStart: true, diff --git a/python/jupytergis_lab/src/index.ts b/python/jupytergis_lab/src/index.ts index 205f4b7c3..33c76502d 100644 --- a/python/jupytergis_lab/src/index.ts +++ b/python/jupytergis_lab/src/index.ts @@ -1,30 +1,22 @@ import { CommandIDs, - ControlPanelModel, GlobalStateDbManager, - LeftPanelWidget, - RightPanelWidget, addCommands, createDefaultLayerRegistry, - logoMiniIcon, rasterSubMenu, vectorSubMenu, } from '@jupytergis/base'; import { - IAnnotationModel, - IAnnotationToken, IJGISFormSchemaRegistry, IJGISFormSchemaRegistryToken, IJGISLayerBrowserRegistry, IJGISLayerBrowserRegistryToken, IJGISLayerItem, IJupyterGISDocTracker, - IJupyterGISTracker, IJupyterGISWidget, ProcessingMerge, } from '@jupytergis/schema'; import { - ILayoutRestorer, JupyterFrontEnd, JupyterFrontEndPlugin, } from '@jupyterlab/application'; @@ -37,8 +29,6 @@ import { ContextMenu, Menu } from '@lumino/widgets'; import { notebookRendererPlugin } from './notebookrenderer'; -const NAME_SPACE = 'jupytergis'; - const plugin: JupyterFrontEndPlugin = { id: 'jupytergis:lab:main-menu', autoStart: true, @@ -225,55 +215,6 @@ const plugin: JupyterFrontEndPlugin = { }, }; -const controlPanel: JupyterFrontEndPlugin = { - id: 'jupytergis:lab:controlpanel', - autoStart: true, - requires: [ - ILayoutRestorer, - IJupyterGISDocTracker, - IJGISFormSchemaRegistryToken, - IStateDB, - IAnnotationToken, - ], - activate: ( - app: JupyterFrontEnd, - restorer: ILayoutRestorer, - tracker: IJupyterGISTracker, - formSchemaRegistry: IJGISFormSchemaRegistry, - state: IStateDB, - annotationModel: IAnnotationModel, - ) => { - const controlModel = new ControlPanelModel({ tracker }); - - const leftControlPanel = new LeftPanelWidget({ - model: controlModel, - tracker, - state, - commands: app.commands, - }); - leftControlPanel.id = 'jupytergis::leftControlPanel'; - leftControlPanel.title.caption = 'JupyterGIS Control Panel'; - leftControlPanel.title.icon = logoMiniIcon; - - const rightControlPanel = new RightPanelWidget({ - model: controlModel, - tracker, - formSchemaRegistry, - annotationModel, - }); - rightControlPanel.id = 'jupytergis::rightControlPanel'; - rightControlPanel.title.caption = 'JupyterGIS Control Panel'; - rightControlPanel.title.icon = logoMiniIcon; - - if (restorer) { - restorer.add(leftControlPanel, `${NAME_SPACE}-left`); - restorer.add(rightControlPanel, `${NAME_SPACE}-right`); - } - app.shell.add(leftControlPanel, 'left', { rank: 2000 }); - app.shell.add(rightControlPanel, 'right', { rank: 2000 }); - }, -}; - /** * Populates the application menus for the notebook. */ @@ -360,4 +301,4 @@ function buildGroupsMenu( }); } -export default [plugin, controlPanel, notebookRendererPlugin]; +export default [plugin, notebookRendererPlugin]; diff --git a/python/jupytergis_lab/src/notebookrenderer.ts b/python/jupytergis_lab/src/notebookrenderer.ts index e86b9d751..c38d381ae 100644 --- a/python/jupytergis_lab/src/notebookrenderer.ts +++ b/python/jupytergis_lab/src/notebookrenderer.ts @@ -81,7 +81,10 @@ export class YJupyterGISLuminoWidget extends Panel { */ private _buildWidget = (options: IOptions) => { const { commands, model, externalCommands, tracker } = options; - const content = new JupyterGISPanel({ model, commandRegistry: commands }); + const content = new JupyterGISPanel({ + model, + commandRegistry: commands, + }); let toolbar: Toolbar | undefined = undefined; if (model.filePath) { toolbar = new ToolbarWidget({ diff --git a/python/jupytergis_qgis/src/plugins.ts b/python/jupytergis_qgis/src/plugins.ts index 00008cdec..21e5141ce 100644 --- a/python/jupytergis_qgis/src/plugins.ts +++ b/python/jupytergis_qgis/src/plugins.ts @@ -17,6 +17,8 @@ import { IJupyterGISWidget, IAnnotationModel, IAnnotationToken, + IJGISFormSchemaRegistry, + IJGISFormSchemaRegistryToken, } from '@jupytergis/schema'; import { JupyterFrontEnd, @@ -36,6 +38,7 @@ import { ConsolePanel, IConsoleTracker } from '@jupyterlab/console'; import { PathExt } from '@jupyterlab/coreutils'; import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; import { ISettingRegistry } from '@jupyterlab/settingregistry'; +import { IStateDB } from '@jupyterlab/statedb'; import { Widget } from '@lumino/widgets'; import { QGSModelFactory, QGZModelFactory } from './modelfactory'; @@ -80,6 +83,8 @@ const activate = async ( consoleTracker: IConsoleTracker, annotationModel: IAnnotationModel, settingRegistry: ISettingRegistry, + formSchemaRegistry: IJGISFormSchemaRegistry, + state: IStateDB, commandPalette: ICommandPalette | null, ): Promise => { const fcCheck = await requestAPI<{ installed: boolean }>( @@ -113,6 +118,9 @@ const activate = async ( rendermime, mimeTypeService: editorServices.mimeTypeService, consoleTracker, + formSchemaRegistry, + state, + annotationModel, }); const QGZWidgetFactory = new JupyterGISDocumentWidgetFactory({ name: 'JupyterGIS QGZ Factory', @@ -128,6 +136,9 @@ const activate = async ( rendermime, mimeTypeService: editorServices.mimeTypeService, consoleTracker, + formSchemaRegistry, + state, + annotationModel, }); // Registering the widget factory @@ -285,7 +296,9 @@ const activate = async ( } else if (response.logs.warnings.length) { const bodyElement = document.createElement('pre'); bodyElement.textContent = `${filepath} has been exported with warnings\n - ${response.logs.warnings.join('\n - ')}`; - const body = new Widget({ node: bodyElement }); + const body = new Widget({ + node: bodyElement, + }); await showDialog({ title: 'Export the project to QGZ file', body, @@ -319,6 +332,8 @@ export const qgisplugin: JupyterFrontEndPlugin = { IConsoleTracker, IAnnotationToken, ISettingRegistry, + IJGISFormSchemaRegistryToken, + IStateDB, ], optional: [ICommandPalette], autoStart: true, diff --git a/yarn.lock b/yarn.lock index ee3e74696..aa482a4a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -999,6 +999,7 @@ __metadata: lerna: ^8.1.9 npm-run-all: ^4.1.5 prettier: ^3.0.0 + react-rnd: ^10.5.2 rimraf: ^3.0.2 typescript: ^5 webpack: ^5.76.3 @@ -4855,6 +4856,13 @@ __metadata: languageName: node linkType: hard +"clsx@npm:^1.1.1": + version: 1.2.1 + resolution: "clsx@npm:1.2.1" + checksum: 30befca8019b2eb7dbad38cff6266cf543091dae2825c856a62a8ccf2c3ab9c2907c4d12b288b73101196767f66812365400a227581484a05f968b0307cfaf12 + languageName: node + linkType: hard + "clsx@npm:^2.1.1": version: 2.1.1 resolution: "clsx@npm:2.1.1" @@ -10267,6 +10275,16 @@ __metadata: languageName: node linkType: hard +"re-resizable@npm:6.11.2": + version: 6.11.2 + resolution: "re-resizable@npm:6.11.2" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 6e754c459f5e3b878184a4f3bffaef2f64ee32e4375e6a93f5f302eecc55d15f91fc7970d28a3ceb17b36292b421039b9cfcfe2ecef7785f44aeeb2d1baefa1a + languageName: node + linkType: hard + "react-day-picker@npm:^9.7.0": version: 9.8.0 resolution: "react-day-picker@npm:9.8.0" @@ -10292,6 +10310,19 @@ __metadata: languageName: node linkType: hard +"react-draggable@npm:4.4.6": + version: 4.4.6 + resolution: "react-draggable@npm:4.4.6" + dependencies: + clsx: ^1.1.1 + prop-types: ^15.8.1 + peerDependencies: + react: ">= 16.3.0" + react-dom: ">= 16.3.0" + checksum: 9b15aac59244873ac4561c5a2bead43a56e18d406e0a5f242bd4f9d151c074530c02b99387983104bf43417292f9cf8d063e554ed08d88792235e3fbc965f1b8 + languageName: node + linkType: hard + "react-draggable@npm:^4.4.5": version: 4.5.0 resolution: "react-draggable@npm:4.5.0" @@ -10361,6 +10392,20 @@ __metadata: languageName: node linkType: hard +"react-rnd@npm:^10.5.2": + version: 10.5.2 + resolution: "react-rnd@npm:10.5.2" + dependencies: + re-resizable: 6.11.2 + react-draggable: 4.4.6 + tslib: 2.6.2 + peerDependencies: + react: ">=16.3.0" + react-dom: ">=16.3.0" + checksum: 0ca7f46fabd0f095998ba45c7463d2a433b5a91b1691f0ec1a210aae527ede09d0f75f57259f8579faf5dd677cfae6ec8a5767ec2c95444005698f31a8dc6e13 + languageName: node + linkType: hard + "react-style-singleton@npm:^2.2.2, react-style-singleton@npm:^2.2.3": version: 2.2.3 resolution: "react-style-singleton@npm:2.2.3" @@ -11747,6 +11792,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:2.6.2": + version: 2.6.2 + resolution: "tslib@npm:2.6.2" + checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad + languageName: node + linkType: hard + "tslib@npm:^1.13.0, tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" From f7b5d51f1449e72a1b2ec49341cc0a1736dc53a4 Mon Sep 17 00:00:00 2001 From: Gauss_Taylor Date: Thu, 17 Jul 2025 15:37:07 +0200 Subject: [PATCH 02/17] Add shadow --- package.json | 145 ++++++++++++++++--------------- packages/base/style/tabPanel.css | 3 + yarn.lock | 52 ----------- 3 files changed, 77 insertions(+), 123 deletions(-) diff --git a/package.json b/package.json index ee8315c59..bf63917bd 100644 --- a/package.json +++ b/package.json @@ -1,73 +1,76 @@ { - "name": "@jupytergis/jupytergis-root", - "version": "0.6.2", - "private": true, - "homepage": "https://github.com/geojupyter/jupytergis", - "repository": { - "type": "git", - "url": "https://github.com/geojupyter/jupytergis.git" - }, - "license": "BSD-3-Clause", - "description": "A JupyterLab extension for GIS.", - "keywords": ["jupyter", "jupyterlab", "jupyterlab-extension"], - "bugs": { - "url": "https://github.com/geojupyter/jupytergis/issues" - }, - "author": { - "name": "Jupytergis contributors" - }, - "files": [ - "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", - "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}" - ], - "workspaces": [ - "packages/*", - "python/jupytergis_core", - "python/jupytergis_lab", - "python/jupytergis_qgis" - ], - "scripts": { - "build": "lerna run build", - "build:prod": "lerna run build:prod --skip-nx-cache", - "build:test": "lerna run build:test", - "build:dev": "lerna run build:dev", - "bump:js:version": "lerna version --no-push --force-publish --no-git-tag-version --yes", - "clean": "lerna run clean", - "clean:all": "lerna run clean:all", - "eslint": "eslint . --ext .ts,.tsx --cache --fix", - "eslint:check": "eslint . --ext .ts,.tsx", - "prettier": "prettier --write \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml}\"", - "prettier:check": "prettier --check \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml}\"", - "lint:check": "jlpm run prettier:check && jlpm run eslint:check", - "lint": "jlpm run prettier && jlpm run eslint", - "test": "lerna run test", - "dev": "python scripts/dev-install.py", - "build:packages": "python scripts/build_packages.py", - "watch:lib": "lerna run --stream watch", - "watch": "lerna watch -- lerna run build --scope=\\$LERNA_PACKAGE_NAME --include-dependents", - "build:lab": "lerna run" - }, - "resolutions": { - "@jupyterlab/apputils": "^4.0.0", - "@lumino/coreutils": "^2.0.0", - "@jupyterlab/notebook": "^4.0.0", - "@jupyterlab/services": " ^7.0.0" - }, - "devDependencies": { - "@types/webpack-env": "^1.18.5", - "@typescript-eslint/eslint-plugin": "5.55.0", - "@typescript-eslint/parser": "5.55.0", - "copy-webpack-plugin": "^10.0.0", - "eslint": "^8.36.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-prettier": "^5.0.1", - "lerna": "^8.1.9", - "npm-run-all": "^4.1.5", - "prettier": "^3.0.0", - "rimraf": "^3.0.2", - "typescript": "^5", - "webpack": "^5.76.3" - }, - "dependencies": {} + "name": "@jupytergis/jupytergis-root", + "version": "0.6.2", + "private": true, + "homepage": "https://github.com/geojupyter/jupytergis", + "repository": { + "type": "git", + "url": "https://github.com/geojupyter/jupytergis.git" + }, + "license": "BSD-3-Clause", + "description": "A JupyterLab extension for GIS.", + "keywords": [ + "jupyter", + "jupyterlab", + "jupyterlab-extension" + ], + "bugs": { + "url": "https://github.com/geojupyter/jupytergis/issues" + }, + "author": { + "name": "Jupytergis contributors" + }, + "files": [ + "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}" + ], + "workspaces": [ + "packages/*", + "python/jupytergis_core", + "python/jupytergis_lab", + "python/jupytergis_qgis" + ], + "scripts": { + "build": "lerna run build", + "build:prod": "lerna run build:prod --skip-nx-cache", + "build:test": "lerna run build:test", + "build:dev": "lerna run build:dev", + "bump:js:version": "lerna version --no-push --force-publish --no-git-tag-version --yes", + "clean": "lerna run clean", + "clean:all": "lerna run clean:all", + "eslint": "eslint . --ext .ts,.tsx --cache --fix", + "eslint:check": "eslint . --ext .ts,.tsx", + "prettier": "prettier --write \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml}\"", + "prettier:check": "prettier --check \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml}\"", + "lint:check": "jlpm run prettier:check && jlpm run eslint:check", + "lint": "jlpm run prettier && jlpm run eslint", + "test": "lerna run test", + "dev": "python scripts/dev-install.py", + "build:packages": "python scripts/build_packages.py", + "watch:lib": "lerna run --stream watch", + "watch": "lerna watch -- lerna run build --scope=\\$LERNA_PACKAGE_NAME --include-dependents", + "build:lab": "lerna run" + }, + "resolutions": { + "@jupyterlab/apputils": "^4.0.0", + "@lumino/coreutils": "^2.0.0", + "@jupyterlab/notebook": "^4.0.0", + "@jupyterlab/services": " ^7.0.0" + }, + "devDependencies": { + "@types/webpack-env": "^1.18.5", + "@typescript-eslint/eslint-plugin": "5.55.0", + "@typescript-eslint/parser": "5.55.0", + "copy-webpack-plugin": "^10.0.0", + "eslint": "^8.36.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-prettier": "^5.0.1", + "lerna": "^8.1.9", + "npm-run-all": "^4.1.5", + "prettier": "^3.0.0", + "rimraf": "^3.0.2", + "typescript": "^5", + "webpack": "^5.76.3" + } } diff --git a/packages/base/style/tabPanel.css b/packages/base/style/tabPanel.css index cb76ef4ce..d35de64b9 100644 --- a/packages/base/style/tabPanel.css +++ b/packages/base/style/tabPanel.css @@ -10,6 +10,9 @@ justify-content: center; align-items: center; background-color: var(--jp-layout-color0); + box-shadow: + 0 4px 8px 0 rgba(0, 0, 0, 0.2), + 0 6px 20px 0 rgba(0, 0, 0, 0.19); } .jgis-stac-browser-collection { diff --git a/yarn.lock b/yarn.lock index aa482a4a6..ee3e74696 100644 --- a/yarn.lock +++ b/yarn.lock @@ -999,7 +999,6 @@ __metadata: lerna: ^8.1.9 npm-run-all: ^4.1.5 prettier: ^3.0.0 - react-rnd: ^10.5.2 rimraf: ^3.0.2 typescript: ^5 webpack: ^5.76.3 @@ -4856,13 +4855,6 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^1.1.1": - version: 1.2.1 - resolution: "clsx@npm:1.2.1" - checksum: 30befca8019b2eb7dbad38cff6266cf543091dae2825c856a62a8ccf2c3ab9c2907c4d12b288b73101196767f66812365400a227581484a05f968b0307cfaf12 - languageName: node - linkType: hard - "clsx@npm:^2.1.1": version: 2.1.1 resolution: "clsx@npm:2.1.1" @@ -10275,16 +10267,6 @@ __metadata: languageName: node linkType: hard -"re-resizable@npm:6.11.2": - version: 6.11.2 - resolution: "re-resizable@npm:6.11.2" - peerDependencies: - react: ^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: 6e754c459f5e3b878184a4f3bffaef2f64ee32e4375e6a93f5f302eecc55d15f91fc7970d28a3ceb17b36292b421039b9cfcfe2ecef7785f44aeeb2d1baefa1a - languageName: node - linkType: hard - "react-day-picker@npm:^9.7.0": version: 9.8.0 resolution: "react-day-picker@npm:9.8.0" @@ -10310,19 +10292,6 @@ __metadata: languageName: node linkType: hard -"react-draggable@npm:4.4.6": - version: 4.4.6 - resolution: "react-draggable@npm:4.4.6" - dependencies: - clsx: ^1.1.1 - prop-types: ^15.8.1 - peerDependencies: - react: ">= 16.3.0" - react-dom: ">= 16.3.0" - checksum: 9b15aac59244873ac4561c5a2bead43a56e18d406e0a5f242bd4f9d151c074530c02b99387983104bf43417292f9cf8d063e554ed08d88792235e3fbc965f1b8 - languageName: node - linkType: hard - "react-draggable@npm:^4.4.5": version: 4.5.0 resolution: "react-draggable@npm:4.5.0" @@ -10392,20 +10361,6 @@ __metadata: languageName: node linkType: hard -"react-rnd@npm:^10.5.2": - version: 10.5.2 - resolution: "react-rnd@npm:10.5.2" - dependencies: - re-resizable: 6.11.2 - react-draggable: 4.4.6 - tslib: 2.6.2 - peerDependencies: - react: ">=16.3.0" - react-dom: ">=16.3.0" - checksum: 0ca7f46fabd0f095998ba45c7463d2a433b5a91b1691f0ec1a210aae527ede09d0f75f57259f8579faf5dd677cfae6ec8a5767ec2c95444005698f31a8dc6e13 - languageName: node - linkType: hard - "react-style-singleton@npm:^2.2.2, react-style-singleton@npm:^2.2.3": version: 2.2.3 resolution: "react-style-singleton@npm:2.2.3" @@ -11792,13 +11747,6 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.6.2": - version: 2.6.2 - resolution: "tslib@npm:2.6.2" - checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad - languageName: node - linkType: hard - "tslib@npm:^1.13.0, tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" From a4cff5768753a6aac2f8e460243329bed082d38c Mon Sep 17 00:00:00 2001 From: Gauss-Taylor-Euler <137942464+Gauss-Taylor-Euler@users.noreply.github.com> Date: Fri, 18 Jul 2025 11:37:11 +0200 Subject: [PATCH 03/17] Update packages/base/src/panelview/components/layers.tsx Co-authored-by: martinRenou --- packages/base/src/panelview/components/layers.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/base/src/panelview/components/layers.tsx b/packages/base/src/panelview/components/layers.tsx index 455310bef..e3b326178 100644 --- a/packages/base/src/panelview/components/layers.tsx +++ b/packages/base/src/panelview/components/layers.tsx @@ -21,7 +21,6 @@ import { CommandIDs, icons } from '@/src/constants'; import { nonVisibilityIcon, visibilityIcon } from '@/src/icons'; import { ILeftPanelClickHandlerParams } from '@/src/panelview/leftpanel'; -// const LAYERS_PANEL_CLASS = 'jp-gis-layerPanel'; const LAYER_GROUP_CLASS = 'jp-gis-layerGroup'; const LAYER_GROUP_HEADER_CLASS = 'jp-gis-layerGroupHeader'; const LAYER_GROUP_COLLAPSER_CLASS = 'jp-gis-layerGroupCollapser'; From 2a2b36056b571cde66e47b9450fce80672a5f0ed Mon Sep 17 00:00:00 2001 From: Gauss_Taylor Date: Fri, 18 Jul 2025 11:44:17 +0200 Subject: [PATCH 04/17] Made suggested changes --- packages/base/src/Lumino.tsx | 43 ------------------- .../base/src/panelview/annotationPanel.tsx | 2 - packages/base/src/panelview/leftpanel.tsx | 1 - packages/base/src/panelview/rightpanel.tsx | 1 - 4 files changed, 47 deletions(-) delete mode 100644 packages/base/src/Lumino.tsx diff --git a/packages/base/src/Lumino.tsx b/packages/base/src/Lumino.tsx deleted file mode 100644 index 85e62de0b..000000000 --- a/packages/base/src/Lumino.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { Widget } from '@lumino/widgets'; -import * as React from 'react'; - -type LuminoProps = { - id?: string; - height?: string | number; - children: Widget; -}; - -export const Lumino = (props: LuminoProps) => { - const ref = React.useRef(null); - const { children, id, height } = props; - React.useEffect(() => { - if (ref && ref.current) { - try { - Widget.attach(children, ref.current); - } catch (e) { - console.warn('Exception while attaching Lumino widget.', e); - } - return () => { - try { - if (children.isAttached || children.node.isConnected) { - children.dispose(); - Widget.detach(children); - } - } catch (e) { - // no-op. - // console.debug('Exception while detaching Lumino widget.', e); - } - }; - } - }, [ref, children]); - return ( -
- ); -}; - -Lumino.defaultProps = { - id: 'lumino-id', - height: '100%', -}; - -export default Lumino; diff --git a/packages/base/src/panelview/annotationPanel.tsx b/packages/base/src/panelview/annotationPanel.tsx index f4d7e3553..fc85e16d3 100644 --- a/packages/base/src/panelview/annotationPanel.tsx +++ b/packages/base/src/panelview/annotationPanel.tsx @@ -12,8 +12,6 @@ export class AnnotationsPanel extends Component { constructor(props: IAnnotationPanelProps) { super(props); - console.log(props.annotationModel); - const updateCallback = () => { this.forceUpdate(); }; diff --git a/packages/base/src/panelview/leftpanel.tsx b/packages/base/src/panelview/leftpanel.tsx index 61d0021f6..c40a709f1 100644 --- a/packages/base/src/panelview/leftpanel.tsx +++ b/packages/base/src/panelview/leftpanel.tsx @@ -59,7 +59,6 @@ export const LeftPanelComponent = (options: ILeftComponentProps) => { className="jGIS-layer-browser-category" value={e.name} onClick={() => { - console.log('HERE'); if (curTab !== e.name) { setCurTab(e.name); } else { diff --git a/packages/base/src/panelview/rightpanel.tsx b/packages/base/src/panelview/rightpanel.tsx index 4be489292..8ef5f7ce8 100644 --- a/packages/base/src/panelview/rightpanel.tsx +++ b/packages/base/src/panelview/rightpanel.tsx @@ -45,7 +45,6 @@ export const RightPanelComponent = (options: IRightComponentProps) => { className="jGIS-layer-browser-category" value={e.name} onClick={() => { - console.log('HERE'); if (curTab !== e.name) { setCurTab(e.name); } else { From ffb13570fe6490d2bdf980137b9c458e92bea929 Mon Sep 17 00:00:00 2001 From: Gauss_Taylor Date: Mon, 21 Jul 2025 13:10:37 +0200 Subject: [PATCH 05/17] Solve a bug with identify panel --- packages/base/src/mainview/mainView.tsx | 2 +- .../src/panelview/components/identify-panel/IdentifyPanel.tsx | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/base/src/mainview/mainView.tsx b/packages/base/src/mainview/mainView.tsx index 07d600ead..a3ff14971 100644 --- a/packages/base/src/mainview/mainView.tsx +++ b/packages/base/src/mainview/mainView.tsx @@ -2236,7 +2236,7 @@ export class MainView extends React.Component { {this._state && ( )} diff --git a/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx b/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx index 10e479036..97d510ab0 100644 --- a/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx +++ b/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx @@ -87,6 +87,8 @@ export const IdentifyPanelComponent = (options: IIdentifyComponentProps) => { })); }; + console.log(features); + return (
Date: Mon, 21 Jul 2025 13:51:11 +0200 Subject: [PATCH 06/17] Make suggested changes --- packages/base/style/shared/tabs.css | 1 + packages/base/style/tabPanel.css | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/base/style/shared/tabs.css b/packages/base/style/shared/tabs.css index b2d46bf2a..64714cfb7 100644 --- a/packages/base/style/shared/tabs.css +++ b/packages/base/style/shared/tabs.css @@ -8,6 +8,7 @@ gap: 1rem; width: 100%; font-size: 9px; + border-radius: 5px; } .jgis-tabs-trigger { diff --git a/packages/base/style/tabPanel.css b/packages/base/style/tabPanel.css index d35de64b9..a663db06f 100644 --- a/packages/base/style/tabPanel.css +++ b/packages/base/style/tabPanel.css @@ -13,6 +13,7 @@ box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); + border-radius: 5px; } .jgis-stac-browser-collection { From 720c7565880ff478973ff803cf60edcc0cb867c9 Mon Sep 17 00:00:00 2001 From: Gauss_Taylor Date: Tue, 22 Jul 2025 14:16:10 +0200 Subject: [PATCH 07/17] Made suggested changes --- packages/base/src/panelview/leftpanel.tsx | 144 ++++++++++------- packages/base/src/panelview/rightpanel.tsx | 152 ++++++++++-------- .../stacBrowser/components/StacPanelView.tsx | 129 ++++++++------- packages/base/style/base.css | 80 ++++----- packages/base/style/shared/tabs.css | 88 +++++----- packages/base/style/stacBrowser.css | 102 ++++++------ packages/base/style/tabPanel.css | 111 ++++++------- packages/base/style/temporalSlider.css | 45 +++--- 8 files changed, 446 insertions(+), 405 deletions(-) diff --git a/packages/base/src/panelview/leftpanel.tsx b/packages/base/src/panelview/leftpanel.tsx index c40a709f1..90e9646d5 100644 --- a/packages/base/src/panelview/leftpanel.tsx +++ b/packages/base/src/panelview/leftpanel.tsx @@ -6,10 +6,10 @@ import * as React from 'react'; import { LayersBodyComponent } from './components/layers'; import { - PanelTabs, - TabsContent, - TabsList, - TabsTrigger, + PanelTabs, + TabsContent, + TabsList, + TabsTrigger, } from '../shared/components/Tabs'; import StacBrowser from '../stacBrowser/StacBrowser'; import FilterComponent from './components/filter-panel/Filter'; @@ -18,73 +18,97 @@ import FilterComponent from './components/filter-panel/Filter'; * Options of the left panel widget. */ export interface ILeftPanelOptions { - model: IJupyterGISModel; - onSelect: ({ type, item, nodeId }: ILeftPanelClickHandlerParams) => void; + model: IJupyterGISModel; + onSelect: ({ + type, + item, + nodeId, + }: ILeftPanelClickHandlerParams) => void; } export interface ILayerPanelOptions extends ILeftPanelOptions { - state: IStateDB; + state: IStateDB; } export interface ILeftPanelClickHandlerParams { - type: SelectionType; - item: string; - nodeId?: string; - event: ReactMouseEvent; + type: SelectionType; + item: string; + nodeId?: string; + event: ReactMouseEvent; } interface ILeftComponentProps { - model: IJupyterGISModel; - state: IStateDB; - commands: CommandRegistry; + model: IJupyterGISModel; + state: IStateDB; + commands: CommandRegistry; } export const LeftPanelComponent = (options: ILeftComponentProps) => { - const tabInfo = [ - { name: 'layers', title: 'Layers' }, - { name: 'stac', title: 'Stac Browser' }, - { name: 'filters', title: 'Filters' }, - ]; - const [curTab, setCurTab] = React.useState( - tabInfo[0].name, - ); + const tabInfo = [ + { name: 'layers', title: 'Layers' }, + { name: 'stac', title: 'Stac Browser' }, + { name: 'filters', title: 'Filters' }, + ]; + const [curTab, setCurTab] = React.useState( + tabInfo[0].name, + ); - return ( -
- - - {tabInfo.map(e => { - return ( - { - if (curTab !== e.name) { - setCurTab(e.name); - } else { - setCurTab(''); - } - }} - > - {e.title} - - ); - })} - - - - - - - - - , - - -
- ); + return ( +
+ + + {tabInfo.map(e => { + return ( + { + if ( + curTab !== + e.name + ) { + setCurTab( + e.name, + ); + } else { + setCurTab( + '', + ); + } + }} + > + {e.title} + + ); + })} + + + + + + + + + + , + + +
+ ); }; diff --git a/packages/base/src/panelview/rightpanel.tsx b/packages/base/src/panelview/rightpanel.tsx index 8ef5f7ce8..b9be5998e 100644 --- a/packages/base/src/panelview/rightpanel.tsx +++ b/packages/base/src/panelview/rightpanel.tsx @@ -1,7 +1,7 @@ import { - IAnnotationModel, - IJGISFormSchemaRegistry, - IJupyterGISModel, + IAnnotationModel, + IJGISFormSchemaRegistry, + IJupyterGISModel, } from '@jupytergis/schema'; import * as React from 'react'; @@ -9,77 +9,95 @@ import { AnnotationsPanel } from './annotationPanel'; import { IdentifyPanelComponent } from './components/identify-panel/IdentifyPanel'; import { ObjectPropertiesReact } from './objectproperties'; import { - PanelTabs, - TabsContent, - TabsList, - TabsTrigger, + PanelTabs, + TabsContent, + TabsList, + TabsTrigger, } from '../shared/components/Tabs'; interface IRightComponentProps { - formSchemaRegistry: IJGISFormSchemaRegistry; - annotationModel: IAnnotationModel; - model: IJupyterGISModel; + formSchemaRegistry: IJGISFormSchemaRegistry; + annotationModel: IAnnotationModel; + model: IJupyterGISModel; } export const RightPanelComponent = (options: IRightComponentProps) => { - const [selectedObjectProperties, setSelectedObjectProperties] = - React.useState(undefined); + const [selectedObjectProperties, setSelectedObjectProperties] = + React.useState(undefined); - const tabInfo = [ - { name: 'objectProperties', title: 'Object Properties' }, - { name: 'annotations', title: 'Annotations' }, - { name: 'identifyPanel', title: 'Identify Panels' }, - ]; + const tabInfo = [ + { name: 'objectProperties', title: 'Object Properties' }, + { name: 'annotations', title: 'Annotations' }, + { name: 'identifyPanel', title: 'Identify Panels' }, + ]; - const [curTab, setCurTab] = React.useState( - tabInfo[0].name, - ); + const [curTab, setCurTab] = React.useState( + tabInfo[0].name, + ); - return ( -
- - - {tabInfo.map(e => { - return ( - { - if (curTab !== e.name) { - setCurTab(e.name); - } else { - setCurTab(''); - } - }} - > - {e.title} - - ); - })} - - - - - - - - - - - -
- ); + return ( +
+ + + {tabInfo.map(e => { + return ( + { + if ( + curTab !== + e.name + ) { + setCurTab( + e.name, + ); + } else { + setCurTab( + '', + ); + } + }} + > + {e.title} + + ); + })} + + + + + + + + + + + +
+ ); }; diff --git a/packages/base/src/stacBrowser/components/StacPanelView.tsx b/packages/base/src/stacBrowser/components/StacPanelView.tsx index c269f7576..098887fd3 100644 --- a/packages/base/src/stacBrowser/components/StacPanelView.tsx +++ b/packages/base/src/stacBrowser/components/StacPanelView.tsx @@ -2,78 +2,83 @@ import { IJupyterGISModel } from '@jupytergis/schema'; import React from 'react'; import { - Tabs, - TabsContent, - TabsList, - TabsTrigger, + Tabs, + TabsContent, + TabsList, + TabsTrigger, } from '@/src/shared/components/Tabs'; import useStacSearch from '@/src/stacBrowser/hooks/useStacSearch'; import StacPanelFilters from './StacPanelFilters'; import StacPanelResults from './StacPanelResults'; interface IStacViewProps { - model?: IJupyterGISModel; + model?: IJupyterGISModel; } const StacPanelView = ({ model }: IStacViewProps) => { - const { - filterState, - filterSetters, - results, - startTime, - setStartTime, - endTime, - setEndTime, - totalPages, - currentPage, - totalResults, - handlePaginationClick, - handleResultClick, - formatResult, - isLoading, - useWorldBBox, - setUseWorldBBox, - } = useStacSearch({ model }); + const { + filterState, + filterSetters, + results, + startTime, + setStartTime, + endTime, + setEndTime, + totalPages, + currentPage, + totalResults, + handlePaginationClick, + handleResultClick, + formatResult, + isLoading, + useWorldBBox, + setUseWorldBBox, + } = useStacSearch({ model }); - if (!model) { - return null; - } + if (!model) { + return null; + } - return ( - - - - Filters - - {`Results (${totalResults})`} - - - - - - - - - ); + return ( + + + + Filters + + {`Results (${totalResults})`} + + + + + + + + + ); }; export default StacPanelView; diff --git a/packages/base/style/base.css b/packages/base/style/base.css index 7561673b6..6b3090672 100644 --- a/packages/base/style/base.css +++ b/packages/base/style/base.css @@ -22,82 +22,82 @@ @import url('./shared/checkbox.css'); .errors { - color: var(--jp-warn-color0); - font-weight: bold; + color: var(--jp-warn-color0); + font-weight: bold; } .jGIS-Toolbar-GroupName { - font-size: var(--jp-ui-font-size0); - padding-left: 3px; + font-size: var(--jp-ui-font-size0); + padding-left: 3px; } /* Overwrite forms CSS */ .jGIS-property-panel .array-item-list { - display: flex; - background-color: var(--jp-layout-color0); + display: flex; + background-color: var(--jp-layout-color0); } .jGIS-property-panel .rjsf .array-item:not(:last-child) { - border-bottom: none; - margin-bottom: unset; - padding-bottom: unset; + border-bottom: none; + margin-bottom: unset; + padding-bottom: unset; } .jGIS-property-panel .rjsf .array-item { - flex: 1 0 0%; - align-items: center; + flex: 1 0 0%; + align-items: center; } .jGIS-property-panel .rjsf fieldset fieldset { - padding-left: unset; - border-left: none; + padding-left: unset; + border-left: none; } .jGIS-property-panel .rjsf .array-item-list:has(.field-array) { - flex-direction: column; + flex-direction: column; } .jp-gis-text-label { - margin: 0; - padding: 0; - font-weight: bold; - display: block; - position: relative; + margin: 0; + padding: 0; + font-weight: bold; + display: block; + position: relative; } /*This is being upstreamed. Will remove once upstream's been fixed.*/ button.jp-mod-styled.jp-mod-accept { - background-color: var(--jp-brand-color1) !important; + background-color: var(--jp-brand-color1) !important; } button.jp-mod-styled.jp-mod-reject { - background-color: var(--jp-layout-color4) !important; + background-color: var(--jp-layout-color4) !important; } .sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; } .jgis-right-panel-container { - width: 330px; - top: 30px; - right: 0px; - position: absolute; - margin: 5px; + width: 330px; + top: 30px; + right: 0px; + position: absolute; + margin: 5px; } .jgis-left-panel-container { - position: absolute; - width: 250px; - top: 30px; - left: 0px; - margin: 5px; + position: absolute; + width: 250px; + top: 30px; + left: 0px; + margin: 5px; } diff --git a/packages/base/style/shared/tabs.css b/packages/base/style/shared/tabs.css index 64714cfb7..0ab37ba6d 100644 --- a/packages/base/style/shared/tabs.css +++ b/packages/base/style/shared/tabs.css @@ -1,57 +1,71 @@ +.jgis-panel-tabs { + display: flex; + flex-direction: column; + gap: 1rem; + justify-content: center; + align-items: center; + background-color: var(--jp-layout-color0); + box-shadow: + 0 4px 8px 0 rgba(0, 0, 0, 0.2), + 0 6px 20px 0 rgba(0, 0, 0, 0.19); + border-radius: 5px; +} .jgis-tabs-list { - display: inline-flex; - height: 2.5rem; - align-items: center; - justify-content: space-evenly; - background-color: var(--jp-layout-color2); - color: var(--jp-ui-font-color0); - gap: 1rem; - width: 100%; - font-size: 9px; - border-radius: 5px; + display: inline-flex; + height: 2.5rem; + align-items: center; + justify-content: space-evenly; + background-color: var(--jp-layout-color2); + color: var(--jp-ui-font-color0); + gap: 1rem; + width: 100%; + font-size: 9px; + border-radius: 5px; } .jgis-tabs-trigger { - display: inline-flex; - align-items: center; - justify-content: center; - white-space: nowrap; - border-radius: 0.25rem; - padding: 0.25rem 0.5rem; - font-size: var(--jp-ui-font-size2); - font-weight: 500; - outline: none; - transition: all 0.3s ease; - border: 1px solid transparent; - background-color: transparent; + display: inline-flex; + align-items: center; + justify-content: center; + white-space: nowrap; + border-radius: 0.25rem; + padding: 0.25rem 0.5rem; + font-size: var(--jp-ui-font-size2); + font-weight: 500; + outline: none; + transition: all 0.3s ease; + border: 1px solid transparent; + background-color: transparent; } .jgis-tabs-trigger:focus-visible { - box-shadow: - 0 0 0 2px var(--jp-accent-color0), - 0 0 0 4px var(--jp-accent-color0); + box-shadow: + 0 0 0 2px var(--jp-accent-color0), + 0 0 0 4px var(--jp-accent-color0); } .jgis-tabs-trigger:disabled { - pointer-events: none; - opacity: 0.5; + pointer-events: none; + opacity: 0.5; } .jgis-tabs-trigger[data-state='active'] { - background-color: var(--jp-layout-color2); - color: var(--jp-ui-font-color0); - box-shadow: 0 1px 2px - color-mix(in srgb, var(--jp-layout-color0), transparent 90%); + background-color: var(--jp-layout-color2); + color: var(--jp-ui-font-color0); + box-shadow: 0 1px 2px + color-mix(in srgb, var(--jp-layout-color0), transparent 90%); } .jgis-tabs-content { - outline: none; - width: 100%; + outline: none; + width: 100%; + overflow-y: scroll; + max-height: 600px; } .jgis-tabs-content:focus-visible { - outline: none; - box-shadow: - 0 0 0 2px var(--jp-accent-color0), - 0 0 0 4px var(--jp-accent-color0); + outline: none; + box-shadow: + 0 0 0 2px var(--jp-accent-color0), + 0 0 0 4px var(--jp-accent-color0); } diff --git a/packages/base/style/stacBrowser.css b/packages/base/style/stacBrowser.css index ed16e9cb0..b0725f740 100644 --- a/packages/base/style/stacBrowser.css +++ b/packages/base/style/stacBrowser.css @@ -1,93 +1,85 @@ -.jgis-stac-panel-main { - display: flex; - flex-direction: column; - gap: 1rem; - justify-content: center; - align-items: center; -} - .jgis-stac-browser-collection { - flex-wrap: wrap; - margin-top: 0.5rem; + flex-wrap: wrap; + margin-top: 0.5rem; } .jgis-stac-browser-section-item { - border-radius: 1rem !important; - cursor: pointer; + border-radius: 1rem !important; + cursor: pointer; } .jgis-stac-browser-results-item { - border-radius: 1rem !important; - cursor: pointer; - white-space: normal !important; - height: auto !important; + border-radius: 1rem !important; + cursor: pointer; + white-space: normal !important; + height: auto !important; } .jgis-stac-browser-results-list { - display: flex; - flex-direction: column; - gap: 1rem; - word-break: break-word; - padding: 0.25rem; - margin-right: 0.5rem; + display: flex; + flex-direction: column; + gap: 1rem; + word-break: break-word; + padding: 0.25rem; + margin-right: 0.5rem; } .jgis-stac-browser-date-picker { - display: flex; - justify-content: space-around; - flex-wrap: wrap; - gap: 0.5rem; + display: flex; + justify-content: space-around; + flex-wrap: wrap; + gap: 0.5rem; } .jgis-stac-browser-filters-panel { - display: flex; - flex-direction: column; - gap: 0.5rem; - align-items: flex-start; - padding: 0 0.5rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + align-items: flex-start; + padding: 0 0.5rem; } .jgis-stac-filter-trigger { - max-width: fit-content; - border: 1px solid var(--jp-border-color0); - padding: 0.5rem; - cursor: pointer; - font-weight: bold; - display: flex; - align-items: center; - gap: 0.15rem; + max-width: fit-content; + border: 1px solid var(--jp-border-color0); + padding: 0.5rem; + cursor: pointer; + font-weight: bold; + display: flex; + align-items: center; + gap: 0.15rem; } .jgis-stac-filter-trigger:disabled { - cursor: not-allowed; + cursor: not-allowed; } .jgis-stac-filter-section-container { - display: flex; - flex-direction: column; - gap: 0.25rem; + display: flex; + flex-direction: column; + gap: 0.25rem; } .jgis-stac-filter-section-badges { - display: flex; - flex-wrap: wrap; - gap: 0.25rem; + display: flex; + flex-wrap: wrap; + gap: 0.25rem; } .jgis-stac-datepicker-icon { - width: 1rem; - height: 1rem; + width: 1rem; + height: 1rem; } .jgis-stac-badge { - gap: 0.25rem; - padding-right: 0.3rem !important; + gap: 0.25rem; + padding-right: 0.3rem !important; } .jgis-stac-badge-icon:hover { - background-color: color-mix( - in srgb, - var(--jp-error-color0), - transparent 20% - ) !important; + background-color: color-mix( + in srgb, + var(--jp-error-color0), + transparent 20% + ) !important; } diff --git a/packages/base/style/tabPanel.css b/packages/base/style/tabPanel.css index a663db06f..983c0be30 100644 --- a/packages/base/style/tabPanel.css +++ b/packages/base/style/tabPanel.css @@ -1,103 +1,90 @@ .jgis-panel-tab-content { - border-radius: 5px; - font-size: 10px; -} - -.jgis-stac-panel-main { - display: flex; - flex-direction: column; - gap: 1rem; - justify-content: center; - align-items: center; - background-color: var(--jp-layout-color0); - box-shadow: - 0 4px 8px 0 rgba(0, 0, 0, 0.2), - 0 6px 20px 0 rgba(0, 0, 0, 0.19); - border-radius: 5px; + border-radius: 5px; + font-size: 10px; } .jgis-stac-browser-collection { - flex-wrap: wrap; - margin-top: 0.5rem; + flex-wrap: wrap; + margin-top: 0.5rem; } .jgis-stac-browser-section-item { - border-radius: 1rem !important; - cursor: pointer; + border-radius: 1rem !important; + cursor: pointer; } .jgis-stac-browser-results-item { - border-radius: 1rem !important; - cursor: pointer; - white-space: normal !important; - height: auto !important; + border-radius: 1rem !important; + cursor: pointer; + white-space: normal !important; + height: auto !important; } .jgis-stac-browser-results-list { - display: flex; - flex-direction: column; - gap: 1rem; - word-break: break-word; - padding: 0.25rem; - margin-right: 0.5rem; + display: flex; + flex-direction: column; + gap: 1rem; + word-break: break-word; + padding: 0.25rem; + margin-right: 0.5rem; } .jgis-stac-browser-date-picker { - display: flex; - justify-content: space-around; - flex-wrap: wrap; - gap: 0.5rem; + display: flex; + justify-content: space-around; + flex-wrap: wrap; + gap: 0.5rem; } .jgis-stac-browser-filters-panel { - display: flex; - flex-direction: column; - gap: 0.5rem; - align-items: flex-start; - padding: 0 0.5rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + align-items: flex-start; + padding: 0 0.5rem; } .jgis-stac-filter-trigger { - max-width: fit-content; - border: 1px solid var(--jp-border-color0); - padding: 0.5rem; - cursor: pointer; - font-weight: bold; - display: flex; - align-items: center; - gap: 0.15rem; + max-width: fit-content; + border: 1px solid var(--jp-border-color0); + padding: 0.5rem; + cursor: pointer; + font-weight: bold; + display: flex; + align-items: center; + gap: 0.15rem; } .jgis-stac-filter-trigger:disabled { - cursor: not-allowed; + cursor: not-allowed; } .jgis-stac-filter-section-container { - display: flex; - flex-direction: column; - gap: 0.25rem; + display: flex; + flex-direction: column; + gap: 0.25rem; } .jgis-stac-filter-section-badges { - display: flex; - flex-wrap: wrap; - gap: 0.25rem; + display: flex; + flex-wrap: wrap; + gap: 0.25rem; } .jgis-stac-datepicker-icon { - width: 1rem; - height: 1rem; + width: 1rem; + height: 1rem; } .jgis-stac-badge { - gap: 0.25rem; - padding-right: 0.3rem !important; + gap: 0.25rem; + padding-right: 0.3rem !important; } .jgis-stac-badge-icon:hover { - background-color: color-mix( - in srgb, - var(--jp-error-color0), - transparent 20% - ) !important; + background-color: color-mix( + in srgb, + var(--jp-error-color0), + transparent 20% + ) !important; } diff --git a/packages/base/style/temporalSlider.css b/packages/base/style/temporalSlider.css index 4df1df185..ecd5095cf 100644 --- a/packages/base/style/temporalSlider.css +++ b/packages/base/style/temporalSlider.css @@ -1,47 +1,48 @@ .jp-gis-temporal-slider-container { - display: flex; - flex-direction: column; - gap: 0.2rem; - padding: 0.5rem; - background-color: var(--jp-layout-color1); - border-bottom: 1px solid var(--jp-border-color1); + display: flex; + flex-direction: column; + gap: 0.2rem; + padding: 0.5rem; + background-color: var(--jp-layout-color1); + border-bottom: 1px solid var(--jp-border-color1); + z-index: 99; } .jp-gis-temporal-slider-container span, .jp-gis-temporal-slider-container label { - font-weight: bold; + font-weight: bold; } .jp-gis-temporal-slider-row { - display: flex; - gap: 0.25rem; - justify-content: space-between; - align-items: center; + display: flex; + gap: 0.25rem; + justify-content: space-between; + align-items: center; } .jp-gis-temporal-slider-controls { - display: flex; - flex-grow: 1; - justify-content: space-around; + display: flex; + flex-grow: 1; + justify-content: space-around; } .jp-gis-temporal-slider-sub-controls { - display: flex; - justify-content: center; - align-items: center; - gap: 0.25rem; + display: flex; + justify-content: center; + align-items: center; + gap: 0.25rem; } .jp-gis-temporal-slider-sub-controls > input { - width: 35px; + width: 35px; } select, select option { - text-transform: capitalize; + text-transform: capitalize; } .jp-gis-temporal-slider { - flex: 1 0 40%; - min-width: 300px; + flex: 1 0 40%; + min-width: 300px; } From f885ec8afa95bd2e8d185d33e2d91e15b54e23fa Mon Sep 17 00:00:00 2001 From: Gauss_Taylor Date: Tue, 22 Jul 2025 14:17:02 +0200 Subject: [PATCH 08/17] Lint --- packages/base/src/panelview/leftpanel.tsx | 144 +++++++---------- packages/base/src/panelview/rightpanel.tsx | 152 ++++++++---------- .../stacBrowser/components/StacPanelView.tsx | 129 +++++++-------- packages/base/style/base.css | 80 ++++----- packages/base/style/shared/tabs.css | 98 +++++------ packages/base/style/stacBrowser.css | 94 +++++------ packages/base/style/tabPanel.css | 98 +++++------ packages/base/style/temporalSlider.css | 46 +++--- 8 files changed, 397 insertions(+), 444 deletions(-) diff --git a/packages/base/src/panelview/leftpanel.tsx b/packages/base/src/panelview/leftpanel.tsx index 90e9646d5..46f74da11 100644 --- a/packages/base/src/panelview/leftpanel.tsx +++ b/packages/base/src/panelview/leftpanel.tsx @@ -6,10 +6,10 @@ import * as React from 'react'; import { LayersBodyComponent } from './components/layers'; import { - PanelTabs, - TabsContent, - TabsList, - TabsTrigger, + PanelTabs, + TabsContent, + TabsList, + TabsTrigger, } from '../shared/components/Tabs'; import StacBrowser from '../stacBrowser/StacBrowser'; import FilterComponent from './components/filter-panel/Filter'; @@ -18,97 +18,73 @@ import FilterComponent from './components/filter-panel/Filter'; * Options of the left panel widget. */ export interface ILeftPanelOptions { - model: IJupyterGISModel; - onSelect: ({ - type, - item, - nodeId, - }: ILeftPanelClickHandlerParams) => void; + model: IJupyterGISModel; + onSelect: ({ type, item, nodeId }: ILeftPanelClickHandlerParams) => void; } export interface ILayerPanelOptions extends ILeftPanelOptions { - state: IStateDB; + state: IStateDB; } export interface ILeftPanelClickHandlerParams { - type: SelectionType; - item: string; - nodeId?: string; - event: ReactMouseEvent; + type: SelectionType; + item: string; + nodeId?: string; + event: ReactMouseEvent; } interface ILeftComponentProps { - model: IJupyterGISModel; - state: IStateDB; - commands: CommandRegistry; + model: IJupyterGISModel; + state: IStateDB; + commands: CommandRegistry; } export const LeftPanelComponent = (options: ILeftComponentProps) => { - const tabInfo = [ - { name: 'layers', title: 'Layers' }, - { name: 'stac', title: 'Stac Browser' }, - { name: 'filters', title: 'Filters' }, - ]; - const [curTab, setCurTab] = React.useState( - tabInfo[0].name, - ); + const tabInfo = [ + { name: 'layers', title: 'Layers' }, + { name: 'stac', title: 'Stac Browser' }, + { name: 'filters', title: 'Filters' }, + ]; + const [curTab, setCurTab] = React.useState( + tabInfo[0].name, + ); - return ( -
- - - {tabInfo.map(e => { - return ( - { - if ( - curTab !== - e.name - ) { - setCurTab( - e.name, - ); - } else { - setCurTab( - '', - ); - } - }} - > - {e.title} - - ); - })} - - - - - - - - - - , - - -
- ); + return ( +
+ + + {tabInfo.map(e => { + return ( + { + if (curTab !== e.name) { + setCurTab(e.name); + } else { + setCurTab(''); + } + }} + > + {e.title} + + ); + })} + + + + + + + + + , + + +
+ ); }; diff --git a/packages/base/src/panelview/rightpanel.tsx b/packages/base/src/panelview/rightpanel.tsx index b9be5998e..3ee0b9bc0 100644 --- a/packages/base/src/panelview/rightpanel.tsx +++ b/packages/base/src/panelview/rightpanel.tsx @@ -1,7 +1,7 @@ import { - IAnnotationModel, - IJGISFormSchemaRegistry, - IJupyterGISModel, + IAnnotationModel, + IJGISFormSchemaRegistry, + IJupyterGISModel, } from '@jupytergis/schema'; import * as React from 'react'; @@ -9,95 +9,77 @@ import { AnnotationsPanel } from './annotationPanel'; import { IdentifyPanelComponent } from './components/identify-panel/IdentifyPanel'; import { ObjectPropertiesReact } from './objectproperties'; import { - PanelTabs, - TabsContent, - TabsList, - TabsTrigger, + PanelTabs, + TabsContent, + TabsList, + TabsTrigger, } from '../shared/components/Tabs'; interface IRightComponentProps { - formSchemaRegistry: IJGISFormSchemaRegistry; - annotationModel: IAnnotationModel; - model: IJupyterGISModel; + formSchemaRegistry: IJGISFormSchemaRegistry; + annotationModel: IAnnotationModel; + model: IJupyterGISModel; } export const RightPanelComponent = (options: IRightComponentProps) => { - const [selectedObjectProperties, setSelectedObjectProperties] = - React.useState(undefined); + const [selectedObjectProperties, setSelectedObjectProperties] = + React.useState(undefined); - const tabInfo = [ - { name: 'objectProperties', title: 'Object Properties' }, - { name: 'annotations', title: 'Annotations' }, - { name: 'identifyPanel', title: 'Identify Panels' }, - ]; + const tabInfo = [ + { name: 'objectProperties', title: 'Object Properties' }, + { name: 'annotations', title: 'Annotations' }, + { name: 'identifyPanel', title: 'Identify Panels' }, + ]; - const [curTab, setCurTab] = React.useState( - tabInfo[0].name, - ); + const [curTab, setCurTab] = React.useState( + tabInfo[0].name, + ); - return ( -
- - - {tabInfo.map(e => { - return ( - { - if ( - curTab !== - e.name - ) { - setCurTab( - e.name, - ); - } else { - setCurTab( - '', - ); - } - }} - > - {e.title} - - ); - })} - - - - - - - - - - - -
- ); + return ( +
+ + + {tabInfo.map(e => { + return ( + { + if (curTab !== e.name) { + setCurTab(e.name); + } else { + setCurTab(''); + } + }} + > + {e.title} + + ); + })} + + + + + + + + + + + +
+ ); }; diff --git a/packages/base/src/stacBrowser/components/StacPanelView.tsx b/packages/base/src/stacBrowser/components/StacPanelView.tsx index 098887fd3..4c6e23cc7 100644 --- a/packages/base/src/stacBrowser/components/StacPanelView.tsx +++ b/packages/base/src/stacBrowser/components/StacPanelView.tsx @@ -2,83 +2,78 @@ import { IJupyterGISModel } from '@jupytergis/schema'; import React from 'react'; import { - Tabs, - TabsContent, - TabsList, - TabsTrigger, + Tabs, + TabsContent, + TabsList, + TabsTrigger, } from '@/src/shared/components/Tabs'; import useStacSearch from '@/src/stacBrowser/hooks/useStacSearch'; import StacPanelFilters from './StacPanelFilters'; import StacPanelResults from './StacPanelResults'; interface IStacViewProps { - model?: IJupyterGISModel; + model?: IJupyterGISModel; } const StacPanelView = ({ model }: IStacViewProps) => { - const { - filterState, - filterSetters, - results, - startTime, - setStartTime, - endTime, - setEndTime, - totalPages, - currentPage, - totalResults, - handlePaginationClick, - handleResultClick, - formatResult, - isLoading, - useWorldBBox, - setUseWorldBBox, - } = useStacSearch({ model }); + const { + filterState, + filterSetters, + results, + startTime, + setStartTime, + endTime, + setEndTime, + totalPages, + currentPage, + totalResults, + handlePaginationClick, + handleResultClick, + formatResult, + isLoading, + useWorldBBox, + setUseWorldBBox, + } = useStacSearch({ model }); - if (!model) { - return null; - } + if (!model) { + return null; + } - return ( - - - - Filters - - {`Results (${totalResults})`} - - - - - - - - - ); + return ( + + + + Filters + + {`Results (${totalResults})`} + + + + + + + + + ); }; export default StacPanelView; diff --git a/packages/base/style/base.css b/packages/base/style/base.css index 6b3090672..7561673b6 100644 --- a/packages/base/style/base.css +++ b/packages/base/style/base.css @@ -22,82 +22,82 @@ @import url('./shared/checkbox.css'); .errors { - color: var(--jp-warn-color0); - font-weight: bold; + color: var(--jp-warn-color0); + font-weight: bold; } .jGIS-Toolbar-GroupName { - font-size: var(--jp-ui-font-size0); - padding-left: 3px; + font-size: var(--jp-ui-font-size0); + padding-left: 3px; } /* Overwrite forms CSS */ .jGIS-property-panel .array-item-list { - display: flex; - background-color: var(--jp-layout-color0); + display: flex; + background-color: var(--jp-layout-color0); } .jGIS-property-panel .rjsf .array-item:not(:last-child) { - border-bottom: none; - margin-bottom: unset; - padding-bottom: unset; + border-bottom: none; + margin-bottom: unset; + padding-bottom: unset; } .jGIS-property-panel .rjsf .array-item { - flex: 1 0 0%; - align-items: center; + flex: 1 0 0%; + align-items: center; } .jGIS-property-panel .rjsf fieldset fieldset { - padding-left: unset; - border-left: none; + padding-left: unset; + border-left: none; } .jGIS-property-panel .rjsf .array-item-list:has(.field-array) { - flex-direction: column; + flex-direction: column; } .jp-gis-text-label { - margin: 0; - padding: 0; - font-weight: bold; - display: block; - position: relative; + margin: 0; + padding: 0; + font-weight: bold; + display: block; + position: relative; } /*This is being upstreamed. Will remove once upstream's been fixed.*/ button.jp-mod-styled.jp-mod-accept { - background-color: var(--jp-brand-color1) !important; + background-color: var(--jp-brand-color1) !important; } button.jp-mod-styled.jp-mod-reject { - background-color: var(--jp-layout-color4) !important; + background-color: var(--jp-layout-color4) !important; } .sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; } .jgis-right-panel-container { - width: 330px; - top: 30px; - right: 0px; - position: absolute; - margin: 5px; + width: 330px; + top: 30px; + right: 0px; + position: absolute; + margin: 5px; } .jgis-left-panel-container { - position: absolute; - width: 250px; - top: 30px; - left: 0px; - margin: 5px; + position: absolute; + width: 250px; + top: 30px; + left: 0px; + margin: 5px; } diff --git a/packages/base/style/shared/tabs.css b/packages/base/style/shared/tabs.css index 0ab37ba6d..c0af9c835 100644 --- a/packages/base/style/shared/tabs.css +++ b/packages/base/style/shared/tabs.css @@ -1,71 +1,71 @@ .jgis-panel-tabs { - display: flex; - flex-direction: column; - gap: 1rem; - justify-content: center; - align-items: center; - background-color: var(--jp-layout-color0); - box-shadow: - 0 4px 8px 0 rgba(0, 0, 0, 0.2), - 0 6px 20px 0 rgba(0, 0, 0, 0.19); - border-radius: 5px; + display: flex; + flex-direction: column; + gap: 1rem; + justify-content: center; + align-items: center; + background-color: var(--jp-layout-color0); + box-shadow: + 0 4px 8px 0 rgba(0, 0, 0, 0.2), + 0 6px 20px 0 rgba(0, 0, 0, 0.19); + border-radius: 5px; } .jgis-tabs-list { - display: inline-flex; - height: 2.5rem; - align-items: center; - justify-content: space-evenly; - background-color: var(--jp-layout-color2); - color: var(--jp-ui-font-color0); - gap: 1rem; - width: 100%; - font-size: 9px; - border-radius: 5px; + display: inline-flex; + height: 2.5rem; + align-items: center; + justify-content: space-evenly; + background-color: var(--jp-layout-color2); + color: var(--jp-ui-font-color0); + gap: 1rem; + width: 100%; + font-size: 9px; + border-radius: 5px; } .jgis-tabs-trigger { - display: inline-flex; - align-items: center; - justify-content: center; - white-space: nowrap; - border-radius: 0.25rem; - padding: 0.25rem 0.5rem; - font-size: var(--jp-ui-font-size2); - font-weight: 500; - outline: none; - transition: all 0.3s ease; - border: 1px solid transparent; - background-color: transparent; + display: inline-flex; + align-items: center; + justify-content: center; + white-space: nowrap; + border-radius: 0.25rem; + padding: 0.25rem 0.5rem; + font-size: var(--jp-ui-font-size2); + font-weight: 500; + outline: none; + transition: all 0.3s ease; + border: 1px solid transparent; + background-color: transparent; } .jgis-tabs-trigger:focus-visible { - box-shadow: - 0 0 0 2px var(--jp-accent-color0), - 0 0 0 4px var(--jp-accent-color0); + box-shadow: + 0 0 0 2px var(--jp-accent-color0), + 0 0 0 4px var(--jp-accent-color0); } .jgis-tabs-trigger:disabled { - pointer-events: none; - opacity: 0.5; + pointer-events: none; + opacity: 0.5; } .jgis-tabs-trigger[data-state='active'] { - background-color: var(--jp-layout-color2); - color: var(--jp-ui-font-color0); - box-shadow: 0 1px 2px - color-mix(in srgb, var(--jp-layout-color0), transparent 90%); + background-color: var(--jp-layout-color2); + color: var(--jp-ui-font-color0); + box-shadow: 0 1px 2px + color-mix(in srgb, var(--jp-layout-color0), transparent 90%); } .jgis-tabs-content { - outline: none; - width: 100%; - overflow-y: scroll; - max-height: 600px; + outline: none; + width: 100%; + overflow-y: scroll; + max-height: 600px; } .jgis-tabs-content:focus-visible { - outline: none; - box-shadow: - 0 0 0 2px var(--jp-accent-color0), - 0 0 0 4px var(--jp-accent-color0); + outline: none; + box-shadow: + 0 0 0 2px var(--jp-accent-color0), + 0 0 0 4px var(--jp-accent-color0); } diff --git a/packages/base/style/stacBrowser.css b/packages/base/style/stacBrowser.css index b0725f740..9ff06b24c 100644 --- a/packages/base/style/stacBrowser.css +++ b/packages/base/style/stacBrowser.css @@ -1,85 +1,85 @@ .jgis-stac-browser-collection { - flex-wrap: wrap; - margin-top: 0.5rem; + flex-wrap: wrap; + margin-top: 0.5rem; } .jgis-stac-browser-section-item { - border-radius: 1rem !important; - cursor: pointer; + border-radius: 1rem !important; + cursor: pointer; } .jgis-stac-browser-results-item { - border-radius: 1rem !important; - cursor: pointer; - white-space: normal !important; - height: auto !important; + border-radius: 1rem !important; + cursor: pointer; + white-space: normal !important; + height: auto !important; } .jgis-stac-browser-results-list { - display: flex; - flex-direction: column; - gap: 1rem; - word-break: break-word; - padding: 0.25rem; - margin-right: 0.5rem; + display: flex; + flex-direction: column; + gap: 1rem; + word-break: break-word; + padding: 0.25rem; + margin-right: 0.5rem; } .jgis-stac-browser-date-picker { - display: flex; - justify-content: space-around; - flex-wrap: wrap; - gap: 0.5rem; + display: flex; + justify-content: space-around; + flex-wrap: wrap; + gap: 0.5rem; } .jgis-stac-browser-filters-panel { - display: flex; - flex-direction: column; - gap: 0.5rem; - align-items: flex-start; - padding: 0 0.5rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + align-items: flex-start; + padding: 0 0.5rem; } .jgis-stac-filter-trigger { - max-width: fit-content; - border: 1px solid var(--jp-border-color0); - padding: 0.5rem; - cursor: pointer; - font-weight: bold; - display: flex; - align-items: center; - gap: 0.15rem; + max-width: fit-content; + border: 1px solid var(--jp-border-color0); + padding: 0.5rem; + cursor: pointer; + font-weight: bold; + display: flex; + align-items: center; + gap: 0.15rem; } .jgis-stac-filter-trigger:disabled { - cursor: not-allowed; + cursor: not-allowed; } .jgis-stac-filter-section-container { - display: flex; - flex-direction: column; - gap: 0.25rem; + display: flex; + flex-direction: column; + gap: 0.25rem; } .jgis-stac-filter-section-badges { - display: flex; - flex-wrap: wrap; - gap: 0.25rem; + display: flex; + flex-wrap: wrap; + gap: 0.25rem; } .jgis-stac-datepicker-icon { - width: 1rem; - height: 1rem; + width: 1rem; + height: 1rem; } .jgis-stac-badge { - gap: 0.25rem; - padding-right: 0.3rem !important; + gap: 0.25rem; + padding-right: 0.3rem !important; } .jgis-stac-badge-icon:hover { - background-color: color-mix( - in srgb, - var(--jp-error-color0), - transparent 20% - ) !important; + background-color: color-mix( + in srgb, + var(--jp-error-color0), + transparent 20% + ) !important; } diff --git a/packages/base/style/tabPanel.css b/packages/base/style/tabPanel.css index 983c0be30..5e2e7b46e 100644 --- a/packages/base/style/tabPanel.css +++ b/packages/base/style/tabPanel.css @@ -1,90 +1,90 @@ .jgis-panel-tab-content { - border-radius: 5px; - font-size: 10px; + border-radius: 5px; + font-size: 10px; } .jgis-stac-browser-collection { - flex-wrap: wrap; - margin-top: 0.5rem; + flex-wrap: wrap; + margin-top: 0.5rem; } .jgis-stac-browser-section-item { - border-radius: 1rem !important; - cursor: pointer; + border-radius: 1rem !important; + cursor: pointer; } .jgis-stac-browser-results-item { - border-radius: 1rem !important; - cursor: pointer; - white-space: normal !important; - height: auto !important; + border-radius: 1rem !important; + cursor: pointer; + white-space: normal !important; + height: auto !important; } .jgis-stac-browser-results-list { - display: flex; - flex-direction: column; - gap: 1rem; - word-break: break-word; - padding: 0.25rem; - margin-right: 0.5rem; + display: flex; + flex-direction: column; + gap: 1rem; + word-break: break-word; + padding: 0.25rem; + margin-right: 0.5rem; } .jgis-stac-browser-date-picker { - display: flex; - justify-content: space-around; - flex-wrap: wrap; - gap: 0.5rem; + display: flex; + justify-content: space-around; + flex-wrap: wrap; + gap: 0.5rem; } .jgis-stac-browser-filters-panel { - display: flex; - flex-direction: column; - gap: 0.5rem; - align-items: flex-start; - padding: 0 0.5rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + align-items: flex-start; + padding: 0 0.5rem; } .jgis-stac-filter-trigger { - max-width: fit-content; - border: 1px solid var(--jp-border-color0); - padding: 0.5rem; - cursor: pointer; - font-weight: bold; - display: flex; - align-items: center; - gap: 0.15rem; + max-width: fit-content; + border: 1px solid var(--jp-border-color0); + padding: 0.5rem; + cursor: pointer; + font-weight: bold; + display: flex; + align-items: center; + gap: 0.15rem; } .jgis-stac-filter-trigger:disabled { - cursor: not-allowed; + cursor: not-allowed; } .jgis-stac-filter-section-container { - display: flex; - flex-direction: column; - gap: 0.25rem; + display: flex; + flex-direction: column; + gap: 0.25rem; } .jgis-stac-filter-section-badges { - display: flex; - flex-wrap: wrap; - gap: 0.25rem; + display: flex; + flex-wrap: wrap; + gap: 0.25rem; } .jgis-stac-datepicker-icon { - width: 1rem; - height: 1rem; + width: 1rem; + height: 1rem; } .jgis-stac-badge { - gap: 0.25rem; - padding-right: 0.3rem !important; + gap: 0.25rem; + padding-right: 0.3rem !important; } .jgis-stac-badge-icon:hover { - background-color: color-mix( - in srgb, - var(--jp-error-color0), - transparent 20% - ) !important; + background-color: color-mix( + in srgb, + var(--jp-error-color0), + transparent 20% + ) !important; } diff --git a/packages/base/style/temporalSlider.css b/packages/base/style/temporalSlider.css index ecd5095cf..df15f3c79 100644 --- a/packages/base/style/temporalSlider.css +++ b/packages/base/style/temporalSlider.css @@ -1,48 +1,48 @@ .jp-gis-temporal-slider-container { - display: flex; - flex-direction: column; - gap: 0.2rem; - padding: 0.5rem; - background-color: var(--jp-layout-color1); - border-bottom: 1px solid var(--jp-border-color1); - z-index: 99; + display: flex; + flex-direction: column; + gap: 0.2rem; + padding: 0.5rem; + background-color: var(--jp-layout-color1); + border-bottom: 1px solid var(--jp-border-color1); + z-index: 99; } .jp-gis-temporal-slider-container span, .jp-gis-temporal-slider-container label { - font-weight: bold; + font-weight: bold; } .jp-gis-temporal-slider-row { - display: flex; - gap: 0.25rem; - justify-content: space-between; - align-items: center; + display: flex; + gap: 0.25rem; + justify-content: space-between; + align-items: center; } .jp-gis-temporal-slider-controls { - display: flex; - flex-grow: 1; - justify-content: space-around; + display: flex; + flex-grow: 1; + justify-content: space-around; } .jp-gis-temporal-slider-sub-controls { - display: flex; - justify-content: center; - align-items: center; - gap: 0.25rem; + display: flex; + justify-content: center; + align-items: center; + gap: 0.25rem; } .jp-gis-temporal-slider-sub-controls > input { - width: 35px; + width: 35px; } select, select option { - text-transform: capitalize; + text-transform: capitalize; } .jp-gis-temporal-slider { - flex: 1 0 40%; - min-width: 300px; + flex: 1 0 40%; + min-width: 300px; } From 027a1a305464e271e12a024f51dc99e19efdb336 Mon Sep 17 00:00:00 2001 From: Gauss_Taylor Date: Wed, 23 Jul 2025 16:23:18 +0200 Subject: [PATCH 09/17] Solve a bug and made suggested changes --- .../base/src/panelview/annotationPanel.tsx | 8 +-- .../components/filter-panel/Filter.tsx | 3 +- .../identify-panel/IdentifyPanel.tsx | 2 - .../base/src/panelview/components/layers.tsx | 61 ++++++++++++++++++- packages/base/src/panelview/index.ts | 1 - packages/base/src/panelview/leftpanel.tsx | 2 +- packages/base/src/panelview/model.ts | 54 ---------------- packages/base/src/panelview/rightpanel.tsx | 2 +- packages/base/src/stacBrowser/StacBrowser.tsx | 8 +-- packages/base/src/stacBrowser/StacPanel.tsx | 34 ----------- packages/base/src/types.ts | 17 +----- packages/base/src/widget.ts | 19 +++--- packages/base/style/tabPanel.css | 1 + 13 files changed, 81 insertions(+), 131 deletions(-) delete mode 100644 packages/base/src/panelview/model.ts delete mode 100644 packages/base/src/stacBrowser/StacPanel.tsx diff --git a/packages/base/src/panelview/annotationPanel.tsx b/packages/base/src/panelview/annotationPanel.tsx index fc85e16d3..f58d47d4a 100644 --- a/packages/base/src/panelview/annotationPanel.tsx +++ b/packages/base/src/panelview/annotationPanel.tsx @@ -5,7 +5,7 @@ import Annotation from '@/src/annotations/components/Annotation'; interface IAnnotationPanelProps { annotationModel: IAnnotationModel; - rightPanelModel: IJupyterGISModel; + jgisModel: IJupyterGISModel; } export class AnnotationsPanel extends Component { @@ -17,7 +17,7 @@ export class AnnotationsPanel extends Component { }; this._annotationModel = props.annotationModel; - this._rightPanelModel = props.rightPanelModel; + this._jgisModel = props.jgisModel; this._annotationModel?.model?.sharedMetadataChanged.connect(updateCallback); this.forceUpdate(); @@ -34,7 +34,7 @@ export class AnnotationsPanel extends Component { return (
@@ -47,5 +47,5 @@ export class AnnotationsPanel extends Component { } private _annotationModel: IAnnotationModel; - private _rightPanelModel: IJupyterGISModel; + private _jgisModel: IJupyterGISModel; } diff --git a/packages/base/src/panelview/components/filter-panel/Filter.tsx b/packages/base/src/panelview/components/filter-panel/Filter.tsx index 464898560..76e8c3830 100644 --- a/packages/base/src/panelview/components/filter-panel/Filter.tsx +++ b/packages/base/src/panelview/components/filter-panel/Filter.tsx @@ -15,7 +15,7 @@ interface IFilterComponentProps { model: IJupyterGISModel; } -const FilterComponent: React.FC = props => { +const FilterComponent: React.FC = ({ model }) => { const featuresInLayerRef = useRef({}); const [logicalOp, setLogicalOp] = useState('all'); const [selectedLayer, setSelectedLayer] = useState(''); @@ -24,7 +24,6 @@ const FilterComponent: React.FC = props => { const [featuresInLayer, setFeaturesInLayer] = useState< Record> >({}); - const model = props.model; useEffect(() => { // Keep layer selected when widget changes diff --git a/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx b/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx index 97d510ab0..10e479036 100644 --- a/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx +++ b/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx @@ -87,8 +87,6 @@ export const IdentifyPanelComponent = (options: IIdentifyComponentProps) => { })); }; - console.log(features); - return (
{ + console.log('_onDragOver'); + e.stopPropagation(); + e.preventDefault(); + Private.dragInfo.dragOverElement = null; + Private.dragInfo.dragOverPosition = null; + }; + + const _onDrop = (e: React.DragEvent) => { + console.log('_onDrop'); + Private.dragIndicator.style.display = 'none'; + + if (model === undefined) { + return; + } + + const { draggedElement, dragOverElement, dragOverPosition } = + Private.dragInfo; + + if (dragOverElement === 'error') { + return; + } + + if (!draggedElement) { + return; + } + const draggedId = draggedElement.dataset.id; + if (!draggedId) { + return; + } + + // Element has been dropped in the empty zone below the tree. + if (dragOverElement === null) { + model?.moveItemsToGroup([draggedId], '', 0); + return; + } + + const dragOverId = dragOverElement.dataset.id; + if (!dragOverId) { + return; + } + + // Handle the special case where we want to drop the element on top of the first + // element of a group. + if ( + dragOverElement.classList.contains(LAYER_GROUP_HEADER_CLASS) && + dragOverPosition === 'below' + ) { + model?.moveItemsToGroup([draggedId], dragOverId); + return; + } + + model?.moveItemRelatedTo( + draggedId, + dragOverId, + dragOverPosition === 'above', + ); + }; + const onSelect = ({ type, item, @@ -164,7 +223,7 @@ export function LayersBodyComponent(props: IBodyProps): JSX.Element { }, [model]); return ( -
+
{layerTree .slice() .reverse() diff --git a/packages/base/src/panelview/index.ts b/packages/base/src/panelview/index.ts index 111735d1a..5e1b8ed00 100644 --- a/packages/base/src/panelview/index.ts +++ b/packages/base/src/panelview/index.ts @@ -1,5 +1,4 @@ export * from './header'; export * from './leftpanel'; -export * from './model'; export * from './objectproperties'; export * from './rightpanel'; diff --git a/packages/base/src/panelview/leftpanel.tsx b/packages/base/src/panelview/leftpanel.tsx index 46f74da11..4cf939f90 100644 --- a/packages/base/src/panelview/leftpanel.tsx +++ b/packages/base/src/panelview/leftpanel.tsx @@ -79,7 +79,7 @@ export const LeftPanelComponent = (options: ILeftComponentProps) => { > - + , diff --git a/packages/base/src/panelview/model.ts b/packages/base/src/panelview/model.ts deleted file mode 100644 index 03f8ff96d..000000000 --- a/packages/base/src/panelview/model.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { - IJupyterGISDoc, - IJupyterGISModel, - IJupyterGISTracker, - IJupyterGISWidget, -} from '@jupytergis/schema'; -import { ISignal } from '@lumino/signaling'; - -import { IControlPanelModel } from '@/src/types'; - -export class ControlPanelModel implements IControlPanelModel { - constructor(options: ControlPanelModel.IOptions) { - this._tracker = options.tracker; - this._documentChanged = this._tracker.currentChanged; - } - - get documentChanged(): ISignal { - return this._documentChanged; - } - - get filePath(): string | undefined { - return this._tracker.currentWidget?.model.filePath; - } - - get jGISModel(): IJupyterGISModel | undefined { - return this._tracker.currentWidget?.model; - } - - get sharedModel(): IJupyterGISDoc | undefined { - return this._tracker.currentWidget?.model.sharedModel; - } - - disconnect(f: any): void { - this._tracker.forEach(w => { - w.model.sharedLayersChanged.disconnect(f); - w.model.sharedSourcesChanged.disconnect(f); - w.model.sharedOptionsChanged.disconnect(f); - }); - this._tracker.forEach(w => w.model.themeChanged.disconnect(f)); - this._tracker.forEach(w => w.model.clientStateChanged.disconnect(f)); - } - - private readonly _tracker: IJupyterGISTracker; - private _documentChanged: ISignal< - IJupyterGISTracker, - IJupyterGISWidget | null - >; -} - -namespace ControlPanelModel { - export interface IOptions { - tracker: IJupyterGISTracker; - } -} diff --git a/packages/base/src/panelview/rightpanel.tsx b/packages/base/src/panelview/rightpanel.tsx index 3ee0b9bc0..e28125429 100644 --- a/packages/base/src/panelview/rightpanel.tsx +++ b/packages/base/src/panelview/rightpanel.tsx @@ -71,7 +71,7 @@ export const RightPanelComponent = (options: IRightComponentProps) => { diff --git a/packages/base/src/stacBrowser/StacBrowser.tsx b/packages/base/src/stacBrowser/StacBrowser.tsx index 8b4cab61d..190b53abe 100644 --- a/packages/base/src/stacBrowser/StacBrowser.tsx +++ b/packages/base/src/stacBrowser/StacBrowser.tsx @@ -4,12 +4,10 @@ import * as React from 'react'; import StacPanelView from '@/src/stacBrowser/components/StacPanelView'; interface IStacBrowserDialogProps { - controlPanelModel: IJupyterGISModel; + model: IJupyterGISModel; } -const StacBrowser = ({ controlPanelModel: model }: IStacBrowserDialogProps) => { - const jgisModel = model; - - return ; +const StacBrowser: React.FC = ({ model }) => { + return ; }; export default StacBrowser; diff --git a/packages/base/src/stacBrowser/StacPanel.tsx b/packages/base/src/stacBrowser/StacPanel.tsx deleted file mode 100644 index 38db1da28..000000000 --- a/packages/base/src/stacBrowser/StacPanel.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { IJupyterGISModel } from '@jupytergis/schema'; -import { ReactWidget } from '@jupyterlab/ui-components'; -import { Panel } from '@lumino/widgets'; -import React from 'react'; - -import StacBrowser from '@/src/stacBrowser/StacBrowser'; - -export class StacPanel extends Panel { - constructor(options: StacPanel.IOptions) { - super(); - this._model = options.model; - - this.id = 'jupytergis::stacPanel'; - this.title.caption = 'STAC'; - this.title.label = 'STAC'; - this.addClass('jgis-scrollable'); - - this.addWidget( - ReactWidget.create( - , - ), - ); - } - - private _model: IJupyterGISModel | undefined; -} - -export namespace StacPanel { - export interface IOptions { - model: IJupyterGISModel; - } -} - -export default StacPanel; diff --git a/packages/base/src/types.ts b/packages/base/src/types.ts index 3048db673..a0c9d872f 100644 --- a/packages/base/src/types.ts +++ b/packages/base/src/types.ts @@ -1,12 +1,5 @@ -import { - IDict, - IJupyterGISDoc, - IJupyterGISModel, - IJupyterGISTracker, - IJupyterGISWidget, -} from '@jupytergis/schema'; +import { IDict, IJupyterGISWidget } from '@jupytergis/schema'; import { WidgetTracker } from '@jupyterlab/apputils'; -import { ISignal } from '@lumino/signaling'; import { Map } from 'ol'; export { IDict }; @@ -14,14 +7,6 @@ export type ValueOf = T[keyof T]; export type JupyterGISTracker = WidgetTracker; -export interface IControlPanelModel { - disconnect(f: any): void; - documentChanged: ISignal; - filePath: string | undefined; - jGISModel: IJupyterGISModel | undefined; - sharedModel: IJupyterGISDoc | undefined; -} - export type SymbologyTab = 'color' | 'radius'; export type VectorRenderType = diff --git a/packages/base/src/widget.ts b/packages/base/src/widget.ts index 7d07300f3..21ba98d41 100644 --- a/packages/base/src/widget.ts +++ b/packages/base/src/widget.ts @@ -97,17 +97,16 @@ export namespace JupyterGISOutputWidget { } export class JupyterGISPanel extends SplitPanel { - constructor(options: JupyterGISPanel.IOptions) { + constructor({ + model, + consoleTracker, + state, + commandRegistry, + formSchemaRegistry, + annotationModel, + ...consoleOption + }: JupyterGISPanel.IOptions) { super({ orientation: 'vertical', spacing: 0 }); - const { - model, - consoleTracker, - state, - commandRegistry, - formSchemaRegistry, - annotationModel, - ...consoleOption - } = options; this._state = state; this._initModel({ model, commandRegistry }); diff --git a/packages/base/style/tabPanel.css b/packages/base/style/tabPanel.css index 5e2e7b46e..5be5249f0 100644 --- a/packages/base/style/tabPanel.css +++ b/packages/base/style/tabPanel.css @@ -1,6 +1,7 @@ .jgis-panel-tab-content { border-radius: 5px; font-size: 10px; + padding-bottom: 50px; } .jgis-stac-browser-collection { From 3b584bf112f46f207fcbc438f9a78c535f2aade3 Mon Sep 17 00:00:00 2001 From: Gauss_Taylor Date: Mon, 28 Jul 2025 13:16:07 +0200 Subject: [PATCH 10/17] Made suggested changes --- packages/base/src/index.ts | 2 +- packages/base/src/mainview/mainView.tsx | 10 +++---- .../identify-panel/IdentifyPanel.tsx | 27 ++++++++--------- .../base/src/panelview/components/layers.tsx | 12 ++------ packages/base/src/panelview/leftpanel.tsx | 30 +++++++------------ .../base/src/panelview/objectproperties.tsx | 11 ------- packages/base/src/panelview/rightpanel.tsx | 18 +++++------ packages/base/src/stacBrowser/StacBrowser.tsx | 13 -------- .../{StacPanelView.tsx => StacPanel.tsx} | 4 +-- packages/base/src/stacBrowser/index.ts | 2 +- 10 files changed, 41 insertions(+), 88 deletions(-) delete mode 100644 packages/base/src/stacBrowser/StacBrowser.tsx rename packages/base/src/stacBrowser/components/{StacPanelView.tsx => StacPanel.tsx} (95%) diff --git a/packages/base/src/index.ts b/packages/base/src/index.ts index 1a9096e83..9c33714cb 100644 --- a/packages/base/src/index.ts +++ b/packages/base/src/index.ts @@ -8,8 +8,8 @@ export * from './icons'; export * from './mainview'; export * from './menus'; export * from './panelview'; -export * from './stacBrowser'; export * from './store'; +export * from './stacBrowser'; export * from './toolbar'; export * from './tools'; export * from './types'; diff --git a/packages/base/src/mainview/mainView.tsx b/packages/base/src/mainview/mainView.tsx index a3ff14971..1f786e6fe 100644 --- a/packages/base/src/mainview/mainView.tsx +++ b/packages/base/src/mainview/mainView.tsx @@ -93,7 +93,7 @@ import CollaboratorPointers, { ClientPointer } from './CollaboratorPointers'; import { FollowIndicator } from './FollowIndicator'; import TemporalSlider from './TemporalSlider'; import { MainViewModel } from './mainviewmodel'; -import { LeftPanelComponent, RightPanelComponent } from '../panelview'; +import { LeftPanel, RightPanel } from '../panelview'; type OlLayerTypes = | TileLayer @@ -2234,18 +2234,18 @@ export class MainView extends React.Component {
{this._state && ( - + > )} {this._formSchemaRegistry && this._annotationModel && ( - + > )} ); diff --git a/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx b/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx index 10e479036..e8269d75d 100644 --- a/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx +++ b/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx @@ -13,13 +13,14 @@ interface IIdentifyComponentProps { model: IJupyterGISModel; } -export const IdentifyPanelComponent = (options: IIdentifyComponentProps) => { +export const IdentifyPanelComponent: React.FC = ({ + model, +}) => { const [features, setFeatures] = useState>(); const [visibleFeatures, setVisibleFeatures] = useState>({ 0: true, }); const [remoteUser, setRemoteUser] = useState(null); - const jgisModel = options.model; const featuresRef = useRef(features); @@ -34,7 +35,7 @@ export const IdentifyPanelComponent = (options: IIdentifyComponentProps) => { sender: IJupyterGISModel, clients: Map, ) => { - const remoteUserId = jgisModel?.localState?.remoteUser; + const remoteUserId = model?.localState?.remoteUser; // If following a collaborator if (remoteUserId) { @@ -50,34 +51,30 @@ export const IdentifyPanelComponent = (options: IIdentifyComponentProps) => { } // If not following a collaborator - const identifiedFeatures = - jgisModel?.localState?.identifiedFeatures?.value; + const identifiedFeatures = model?.localState?.identifiedFeatures?.value; if (!identifiedFeatures) { setFeatures({}); return; } - if ( - jgisModel.isIdentifying && - featuresRef.current !== identifiedFeatures - ) { + if (model.isIdentifying && featuresRef.current !== identifiedFeatures) { setFeatures(identifiedFeatures); } }; - jgisModel?.clientStateChanged.connect(handleClientStateChanged); + model?.clientStateChanged.connect(handleClientStateChanged); return () => { - jgisModel?.clientStateChanged.disconnect(handleClientStateChanged); + model?.clientStateChanged.disconnect(handleClientStateChanged); }; - }, [jgisModel]); + }, [model]); const highlightFeatureOnMap = (feature: any) => { - jgisModel?.highlightFeatureSignal?.emit(feature); + model?.highlightFeatureSignal?.emit(feature); const geometry = feature.geometry || feature._geometry; - jgisModel?.flyToGeometrySignal?.emit(geometry); + model?.flyToGeometrySignal?.emit(geometry); }; const toggleFeatureVisibility = (index: number) => { @@ -91,7 +88,7 @@ export const IdentifyPanelComponent = (options: IIdentifyComponentProps) => {
= props => { const model = props.model; const id = UUID.uuid4(); @@ -57,7 +51,6 @@ export function LayersBodyComponent(props: IBodyProps): JSX.Element { }; const _onDragOver = (e: React.DragEvent) => { - console.log('_onDragOver'); e.stopPropagation(); e.preventDefault(); Private.dragInfo.dragOverElement = null; @@ -65,7 +58,6 @@ export function LayersBodyComponent(props: IBodyProps): JSX.Element { }; const _onDrop = (e: React.DragEvent) => { - console.log('_onDrop'); Private.dragIndicator.style.display = 'none'; if (model === undefined) { @@ -247,7 +239,7 @@ export function LayersBodyComponent(props: IBodyProps): JSX.Element { )}
); -} +}; /** * Properties of the layer group component. diff --git a/packages/base/src/panelview/leftpanel.tsx b/packages/base/src/panelview/leftpanel.tsx index 4cf939f90..659d7f252 100644 --- a/packages/base/src/panelview/leftpanel.tsx +++ b/packages/base/src/panelview/leftpanel.tsx @@ -11,21 +11,9 @@ import { TabsList, TabsTrigger, } from '../shared/components/Tabs'; -import StacBrowser from '../stacBrowser/StacBrowser'; +import StacPanel from '../stacBrowser/components/StacPanel'; import FilterComponent from './components/filter-panel/Filter'; -/** - * Options of the left panel widget. - */ -export interface ILeftPanelOptions { - model: IJupyterGISModel; - onSelect: ({ type, item, nodeId }: ILeftPanelClickHandlerParams) => void; -} - -export interface ILayerPanelOptions extends ILeftPanelOptions { - state: IStateDB; -} - export interface ILeftPanelClickHandlerParams { type: SelectionType; item: string; @@ -33,13 +21,15 @@ export interface ILeftPanelClickHandlerParams { event: ReactMouseEvent; } -interface ILeftComponentProps { +interface ILeftPanelProps { model: IJupyterGISModel; state: IStateDB; commands: CommandRegistry; } -export const LeftPanelComponent = (options: ILeftComponentProps) => { +export const LeftPanel: React.FC = ( + props: ILeftPanelProps, +) => { const tabInfo = [ { name: 'layers', title: 'Layers' }, { name: 'stac', title: 'Stac Browser' }, @@ -73,16 +63,16 @@ export const LeftPanelComponent = (options: ILeftComponentProps) => { - + - , + ,
diff --git a/packages/base/src/panelview/objectproperties.tsx b/packages/base/src/panelview/objectproperties.tsx index bf9c0310e..78b88d109 100644 --- a/packages/base/src/panelview/objectproperties.tsx +++ b/packages/base/src/panelview/objectproperties.tsx @@ -3,7 +3,6 @@ import { IJupyterGISClientState, IJupyterGISModel, } from '@jupytergis/schema'; -import { Panel } from '@lumino/widgets'; import * as React from 'react'; import { v4 as uuid } from 'uuid'; @@ -120,13 +119,3 @@ export class ObjectPropertiesReact extends React.Component { ); } } - -export namespace ObjectProperties { - /** - * Instantiation options for `ObjectProperties`. - */ - export interface IOptions extends Panel.IOptions { - model: IJupyterGISModel; - formSchemaRegistry: IJGISFormSchemaRegistry; - } -} diff --git a/packages/base/src/panelview/rightpanel.tsx b/packages/base/src/panelview/rightpanel.tsx index e28125429..8e056468c 100644 --- a/packages/base/src/panelview/rightpanel.tsx +++ b/packages/base/src/panelview/rightpanel.tsx @@ -15,20 +15,20 @@ import { TabsTrigger, } from '../shared/components/Tabs'; -interface IRightComponentProps { +interface IRightPanelProps { formSchemaRegistry: IJGISFormSchemaRegistry; annotationModel: IAnnotationModel; model: IJupyterGISModel; } -export const RightPanelComponent = (options: IRightComponentProps) => { +export const RightPanel: React.FC = props => { const [selectedObjectProperties, setSelectedObjectProperties] = React.useState(undefined); const tabInfo = [ { name: 'objectProperties', title: 'Object Properties' }, { name: 'annotations', title: 'Annotations' }, - { name: 'identifyPanel', title: 'Identify Panels' }, + { name: 'identifyPanel', title: 'Identify Features' }, ]; const [curTab, setCurTab] = React.useState( @@ -64,20 +64,18 @@ export const RightPanelComponent = (options: IRightComponentProps) => { - +
diff --git a/packages/base/src/stacBrowser/StacBrowser.tsx b/packages/base/src/stacBrowser/StacBrowser.tsx deleted file mode 100644 index 190b53abe..000000000 --- a/packages/base/src/stacBrowser/StacBrowser.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { IJupyterGISModel } from '@jupytergis/schema'; -import * as React from 'react'; - -import StacPanelView from '@/src/stacBrowser/components/StacPanelView'; - -interface IStacBrowserDialogProps { - model: IJupyterGISModel; -} - -const StacBrowser: React.FC = ({ model }) => { - return ; -}; -export default StacBrowser; diff --git a/packages/base/src/stacBrowser/components/StacPanelView.tsx b/packages/base/src/stacBrowser/components/StacPanel.tsx similarity index 95% rename from packages/base/src/stacBrowser/components/StacPanelView.tsx rename to packages/base/src/stacBrowser/components/StacPanel.tsx index 4c6e23cc7..db89cb31a 100644 --- a/packages/base/src/stacBrowser/components/StacPanelView.tsx +++ b/packages/base/src/stacBrowser/components/StacPanel.tsx @@ -14,7 +14,7 @@ import StacPanelResults from './StacPanelResults'; interface IStacViewProps { model?: IJupyterGISModel; } -const StacPanelView = ({ model }: IStacViewProps) => { +const StacPanel = ({ model }: IStacViewProps) => { const { filterState, filterSetters, @@ -76,4 +76,4 @@ const StacPanelView = ({ model }: IStacViewProps) => { ); }; -export default StacPanelView; +export default StacPanel; diff --git a/packages/base/src/stacBrowser/index.ts b/packages/base/src/stacBrowser/index.ts index 2e8049a34..19e4e8a3e 100644 --- a/packages/base/src/stacBrowser/index.ts +++ b/packages/base/src/stacBrowser/index.ts @@ -1 +1 @@ -export * from './StacBrowser'; +export * from './components/StacPanel'; From daaaaeea846c169625910de102072b8d147d052f Mon Sep 17 00:00:00 2001 From: Gauss_Taylor Date: Mon, 28 Jul 2025 16:24:15 +0200 Subject: [PATCH 11/17] Add panels to notebook view --- python/jupytergis_lab/src/notebookrenderer.ts | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/python/jupytergis_lab/src/notebookrenderer.ts b/python/jupytergis_lab/src/notebookrenderer.ts index c38d381ae..50915250a 100644 --- a/python/jupytergis_lab/src/notebookrenderer.ts +++ b/python/jupytergis_lab/src/notebookrenderer.ts @@ -11,6 +11,10 @@ import { IJupyterGISDoc, IJupyterGISDocTracker, JupyterGISModel, + IJGISFormSchemaRegistry, + IAnnotationModel, + IJGISFormSchemaRegistryToken, + IAnnotationToken, } from '@jupytergis/schema'; import { JupyterFrontEnd, @@ -21,6 +25,7 @@ import { ConsolePanel } from '@jupyterlab/console'; import { PathExt } from '@jupyterlab/coreutils'; import { NotebookPanel } from '@jupyterlab/notebook'; import { Contents } from '@jupyterlab/services'; +import { IStateDB } from '@jupyterlab/statedb'; import { Toolbar } from '@jupyterlab/ui-components'; import { CommandRegistry } from '@lumino/commands'; import { MessageLoop } from '@lumino/messaging'; @@ -80,10 +85,21 @@ export class YJupyterGISLuminoWidget extends Panel { * @param options */ private _buildWidget = (options: IOptions) => { - const { commands, model, externalCommands, tracker } = options; + const { + commands, + model, + externalCommands, + tracker, + formSchemaRegistry, + state, + annotationModel, + } = options; const content = new JupyterGISPanel({ model, commandRegistry: commands, + formSchemaRegistry, + state, + annotationModel, }); let toolbar: Toolbar | undefined = undefined; if (model.filePath) { @@ -110,6 +126,9 @@ interface IOptions { model: JupyterGISModel; externalCommands?: IJGISExternalCommandRegistry; tracker?: JupyterGISTracker; + formSchemaRegistry?: IJGISFormSchemaRegistry; + state?: IStateDB; + annotationModel?: IAnnotationModel; } export const notebookRendererPlugin: JupyterFrontEndPlugin = { @@ -120,6 +139,9 @@ export const notebookRendererPlugin: JupyterFrontEndPlugin = { IJupyterGISDocTracker, IJupyterYWidgetManager, ICollaborativeDrive, + IStateDB, + IJGISFormSchemaRegistryToken, + IAnnotationToken, ], activate: ( app: JupyterFrontEnd, @@ -127,6 +149,9 @@ export const notebookRendererPlugin: JupyterFrontEndPlugin = { jgisTracker?: JupyterGISTracker, yWidgetManager?: IJupyterYWidgetManager, drive?: ICollaborativeDrive, + formSchemaRegistry?: IJGISFormSchemaRegistry, + state?: IStateDB, + annotationModel?: IAnnotationModel, ): void => { if (!yWidgetManager) { console.error('Missing IJupyterYWidgetManager token!'); @@ -207,7 +232,11 @@ export const notebookRendererPlugin: JupyterFrontEndPlugin = { commands: app.commands, model: yModel.jupyterGISModel, externalCommands: externalCommandRegistry, + tracker: jgisTracker, + annotationModel, + state, + formSchemaRegistry, }); this._jgisWidget = widget.jgisWidget; From db109ba810d0453b5a5ff42845a3fb8f5f885ded Mon Sep 17 00:00:00 2001 From: Gauss_Taylor Date: Wed, 30 Jul 2025 12:08:40 +0200 Subject: [PATCH 12/17] Made suggested changes --- packages/base/src/annotations/components/Annotation.tsx | 7 +++---- packages/base/src/panelview/annotationPanel.tsx | 2 +- packages/base/style/shared/tabs.css | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/base/src/annotations/components/Annotation.tsx b/packages/base/src/annotations/components/Annotation.tsx index 16d6ce394..a4118f30b 100644 --- a/packages/base/src/annotations/components/Annotation.tsx +++ b/packages/base/src/annotations/components/Annotation.tsx @@ -14,18 +14,17 @@ import { Message } from './Message'; export interface IAnnotationProps { itemId: string; annotationModel: IAnnotationModel; - rightPanelModel?: IJupyterGISModel; + jgisModel?: IJupyterGISModel; children?: JSX.Element[] | JSX.Element; } const Annotation: React.FC = ({ itemId, annotationModel, - rightPanelModel, + jgisModel, children, }) => { const [messageContent, setMessageContent] = useState(''); - const jgisModel = rightPanelModel; const annotation = annotationModel.getAnnotation(itemId); const contents = useMemo(() => annotation?.contents ?? [], [annotation]); @@ -87,7 +86,7 @@ const Annotation: React.FC = ({ - {rightPanelModel && ( + {jgisModel && (