@jupytergis/base 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands/index.js +2 -6
- package/lib/dialogs/layerBrowserDialog.d.ts +3 -3
- package/lib/dialogs/layerBrowserDialog.js +9 -10
- package/lib/dialogs/symbology/hooks/useEffectiveSymbologyParams.d.ts +16 -0
- package/lib/dialogs/symbology/hooks/useEffectiveSymbologyParams.js +24 -0
- package/lib/dialogs/symbology/hooks/useOkSignal.d.ts +6 -0
- package/lib/dialogs/symbology/hooks/useOkSignal.js +25 -0
- package/lib/dialogs/symbology/symbologyDialog.d.ts +4 -2
- package/lib/dialogs/symbology/symbologyDialog.js +6 -10
- package/lib/dialogs/symbology/symbologyUtils.d.ts +25 -2
- package/lib/dialogs/symbology/symbologyUtils.js +74 -4
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +3 -3
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +31 -34
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +68 -62
- package/lib/dialogs/symbology/vector_layer/VectorRendering.js +33 -21
- package/lib/dialogs/symbology/vector_layer/types/Canonical.js +23 -24
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +48 -49
- package/lib/dialogs/symbology/vector_layer/types/Graduated.js +53 -62
- package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +35 -34
- package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.js +45 -47
- package/lib/formbuilder/objectform/StoryEditorForm.js +1 -18
- package/lib/formbuilder/objectform/baseform.d.ts +6 -0
- package/lib/formbuilder/objectform/baseform.js +21 -38
- package/lib/formbuilder/objectform/components/LayerSelect.d.ts +7 -0
- package/lib/formbuilder/objectform/components/LayerSelect.js +43 -0
- package/lib/formbuilder/objectform/components/OpacitySlider.d.ts +4 -0
- package/lib/formbuilder/objectform/components/OpacitySlider.js +40 -0
- package/lib/formbuilder/objectform/components/SegmentFormSymbology.d.ts +3 -0
- package/lib/formbuilder/objectform/components/SegmentFormSymbology.js +59 -0
- package/lib/formbuilder/objectform/layer/storySegmentLayerForm.d.ts +2 -2
- package/lib/formbuilder/objectform/layer/storySegmentLayerForm.js +19 -0
- package/lib/mainview/mainView.d.ts +8 -2
- package/lib/mainview/mainView.js +41 -7
- package/lib/mainview/mainviewwidget.js +2 -2
- package/lib/panelview/leftpanel.d.ts +2 -1
- package/lib/panelview/leftpanel.js +28 -20
- package/lib/panelview/rightpanel.d.ts +4 -1
- package/lib/panelview/rightpanel.js +14 -20
- package/lib/panelview/story-maps/MobileSpectaPanel.d.ts +7 -0
- package/lib/panelview/story-maps/MobileSpectaPanel.js +114 -0
- package/lib/panelview/story-maps/StoryNavBar.d.ts +3 -2
- package/lib/panelview/story-maps/StoryNavBar.js +18 -6
- package/lib/panelview/story-maps/StoryViewerPanel.d.ts +13 -1
- package/lib/panelview/story-maps/StoryViewerPanel.js +168 -34
- package/lib/panelview/story-maps/components/StoryImageSection.d.ts +2 -7
- package/lib/panelview/story-maps/components/StoryImageSection.js +2 -4
- package/lib/panelview/story-maps/components/StorySubtitleSection.d.ts +2 -6
- package/lib/panelview/story-maps/components/StorySubtitleSection.js +2 -4
- package/lib/panelview/story-maps/components/StoryTitleSection.d.ts +2 -7
- package/lib/panelview/story-maps/components/StoryTitleSection.js +2 -3
- package/lib/shared/components/Button.js +2 -2
- package/lib/shared/components/Calendar.js +0 -1
- package/lib/shared/components/Checkbox.d.ts +1 -1
- package/lib/shared/components/Checkbox.js +1 -1
- package/lib/shared/components/Dialog.d.ts +1 -1
- package/lib/shared/components/Dialog.js +1 -1
- package/lib/shared/components/Drawer.d.ts +13 -0
- package/lib/shared/components/Drawer.js +59 -0
- package/lib/shared/components/DropdownMenu.d.ts +1 -1
- package/lib/shared/components/DropdownMenu.js +1 -1
- package/lib/shared/components/Popover.d.ts +1 -1
- package/lib/shared/components/Popover.js +1 -1
- package/lib/shared/components/RadioGroup.d.ts +1 -1
- package/lib/shared/components/RadioGroup.js +1 -1
- package/lib/shared/components/Sheet.d.ts +15 -0
- package/lib/shared/components/Sheet.js +64 -0
- package/lib/shared/components/Switch.d.ts +1 -1
- package/lib/shared/components/Switch.js +1 -1
- package/lib/shared/components/Tabs.d.ts +1 -1
- package/lib/shared/components/Tabs.js +1 -1
- package/lib/shared/components/ToggleGroup.d.ts +1 -1
- package/lib/shared/components/ToggleGroup.js +1 -1
- package/lib/shared/hooks/useLatest.d.ts +1 -0
- package/lib/shared/hooks/useLatest.js +8 -0
- package/lib/shared/hooks/useMediaQuery.d.ts +9 -0
- package/lib/shared/hooks/useMediaQuery.js +32 -0
- package/lib/tools.js +21 -34
- package/lib/types.d.ts +1 -0
- package/lib/types.js +6 -1
- package/package.json +5 -12
- package/style/base.css +15 -0
- package/style/shared/drawer.css +154 -0
- package/style/shared/sheet.css +258 -0
- package/style/storyPanel.css +36 -8
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Slider } from '@jupyter/react-components';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
function OpacitySlider({ formData, onChange }) {
|
|
4
|
+
var _a;
|
|
5
|
+
const [inputValue, setInputValue] = React.useState((_a = formData === null || formData === void 0 ? void 0 : formData.toFixed(1)) !== null && _a !== void 0 ? _a : '1');
|
|
6
|
+
React.useEffect(() => {
|
|
7
|
+
var _a;
|
|
8
|
+
const newValue = (_a = formData === null || formData === void 0 ? void 0 : formData.toFixed(1)) !== null && _a !== void 0 ? _a : '1';
|
|
9
|
+
if (newValue !== inputValue) {
|
|
10
|
+
setInputValue(newValue);
|
|
11
|
+
}
|
|
12
|
+
}, [formData]);
|
|
13
|
+
const handleSliderChange = (event) => {
|
|
14
|
+
const target = event.target;
|
|
15
|
+
if (target && '_value' in target) {
|
|
16
|
+
const sliderValue = parseFloat(target._value); // Slider value is in 0–10 range
|
|
17
|
+
const normalizedValue = sliderValue / 10; // Normalize to 0.1–1 range
|
|
18
|
+
onChange(normalizedValue);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const handleInputChange = (event) => {
|
|
22
|
+
const value = event.target.value;
|
|
23
|
+
setInputValue(value);
|
|
24
|
+
const parsedValue = parseFloat(value);
|
|
25
|
+
if (!isNaN(parsedValue) && parsedValue >= 0.1 && parsedValue <= 1) {
|
|
26
|
+
onChange(parsedValue);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
return (React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' } },
|
|
30
|
+
React.createElement(Slider, { min: 1, max: 10, step: 1, value: formData ? formData * 10 : 10, onChange: handleSliderChange }),
|
|
31
|
+
React.createElement("input", { type: "number", value: inputValue, step: 0.1, min: 0.1, onChange: handleInputChange, style: {
|
|
32
|
+
width: '50px',
|
|
33
|
+
textAlign: 'center',
|
|
34
|
+
border: '1px solid #ccc',
|
|
35
|
+
borderRadius: '4px',
|
|
36
|
+
padding: '4px',
|
|
37
|
+
marginBottom: '5px',
|
|
38
|
+
} })));
|
|
39
|
+
}
|
|
40
|
+
export default OpacitySlider;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { SymbologyWidget } from "../../../dialogs/symbology/symbologyDialog";
|
|
3
|
+
import { Button } from "../../../shared/components/Button";
|
|
4
|
+
import { GlobalStateDbManager } from "../../../store";
|
|
5
|
+
import { SYMBOLOGY_VALID_LAYER_TYPES } from "../../../types";
|
|
6
|
+
const SELECTION_SETTLE_MS = 100;
|
|
7
|
+
function LayerOverrideItem({ item, formContext }) {
|
|
8
|
+
var _a, _b;
|
|
9
|
+
const model = formContext === null || formContext === void 0 ? void 0 : formContext.model;
|
|
10
|
+
if (!model) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const state = GlobalStateDbManager.getInstance().getStateDb();
|
|
14
|
+
const currentItem = (_b = (_a = formContext === null || formContext === void 0 ? void 0 : formContext.formData) === null || _a === void 0 ? void 0 : _a.layerOverride) === null || _b === void 0 ? void 0 : _b[item.index];
|
|
15
|
+
const targetLayerId = currentItem === null || currentItem === void 0 ? void 0 : currentItem.targetLayer;
|
|
16
|
+
const selectedLayer = targetLayerId
|
|
17
|
+
? model.getLayer(targetLayerId)
|
|
18
|
+
: undefined;
|
|
19
|
+
const canOpenSymbology = Boolean(targetLayerId &&
|
|
20
|
+
selectedLayer &&
|
|
21
|
+
SYMBOLOGY_VALID_LAYER_TYPES.includes(selectedLayer.type));
|
|
22
|
+
const handleOpenSymbology = async () => {
|
|
23
|
+
if (!targetLayerId || !state || !selectedLayer) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const previousSelection = model.selected;
|
|
27
|
+
const segmentId = Object.keys(previousSelection !== null && previousSelection !== void 0 ? previousSelection : {}).find(key => { var _a; return ((_a = model.getLayer(key)) === null || _a === void 0 ? void 0 : _a.type) === 'StorySegmentLayer'; });
|
|
28
|
+
// Temporarily set the selected layer to the target layer
|
|
29
|
+
model.syncSelected({ [targetLayerId]: { type: 'layer' } });
|
|
30
|
+
await new Promise(resolve => setTimeout(resolve, SELECTION_SETTLE_MS));
|
|
31
|
+
const dialog = new SymbologyWidget({
|
|
32
|
+
model,
|
|
33
|
+
state,
|
|
34
|
+
isStorySegmentOverride: true,
|
|
35
|
+
segmentId,
|
|
36
|
+
});
|
|
37
|
+
await dialog.launch();
|
|
38
|
+
model.syncSelected(previousSelection !== null && previousSelection !== void 0 ? previousSelection : {});
|
|
39
|
+
};
|
|
40
|
+
return (React.createElement("div", { className: "jGIS-symbology-override-item" },
|
|
41
|
+
React.createElement("div", { style: { flex: 1 } }, item.children),
|
|
42
|
+
React.createElement("div", { style: { display: 'flex', gap: '1rem' } },
|
|
43
|
+
React.createElement(Button, { title: "Edit symbology override for the target layer", onClick: handleOpenSymbology, style: { width: '100%' }, disabled: !canOpenSymbology },
|
|
44
|
+
React.createElement("span", { className: "fa fa-brush", style: { marginRight: '4px' } }),
|
|
45
|
+
"Edit Symbology"),
|
|
46
|
+
item.hasRemove && (React.createElement(Button, { variant: "destructive", onClick: item.onDropIndexClick(item.index), title: "Remove item" }, "Remove")))));
|
|
47
|
+
}
|
|
48
|
+
export function ArrayFieldTemplate(props) {
|
|
49
|
+
return (React.createElement(React.Fragment, null,
|
|
50
|
+
React.createElement("div", null, props.title),
|
|
51
|
+
React.createElement("div", { style: {
|
|
52
|
+
display: 'flex',
|
|
53
|
+
flexDirection: 'column',
|
|
54
|
+
gap: '1rem',
|
|
55
|
+
alignItems: 'center',
|
|
56
|
+
} },
|
|
57
|
+
props.items.map(item => (React.createElement(LayerOverrideItem, { key: item.key, item: item, formContext: props.formContext }))),
|
|
58
|
+
props.canAdd && (React.createElement(Button, { onClick: props.onAddClick }, "Add Symbology Override")))));
|
|
59
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { IDict } from '@jupytergis/schema';
|
|
1
|
+
import { IDict, IStorySegmentLayer } from '@jupytergis/schema';
|
|
2
2
|
import { LayerPropertiesForm } from './layerform';
|
|
3
3
|
export declare class StorySegmentLayerPropertiesForm extends LayerPropertiesForm {
|
|
4
|
-
protected processSchema(data:
|
|
4
|
+
protected processSchema(data: IStorySegmentLayer | undefined, schema: IDict, uiSchema: IDict): void;
|
|
5
5
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { LayerPropertiesForm } from './layerform';
|
|
3
|
+
import { ArrayFieldTemplate } from '../components/SegmentFormSymbology';
|
|
3
4
|
import StorySegmentReset from '../components/StorySegmentReset';
|
|
4
5
|
export class StorySegmentLayerPropertiesForm extends LayerPropertiesForm {
|
|
5
6
|
processSchema(data, schema, uiSchema) {
|
|
7
|
+
var _a, _b, _c;
|
|
6
8
|
super.processSchema(data, schema, uiSchema);
|
|
7
9
|
if (!this.props.model.selected) {
|
|
8
10
|
return;
|
|
@@ -27,6 +29,23 @@ export class StorySegmentLayerPropertiesForm extends LayerPropertiesForm {
|
|
|
27
29
|
rows: 10,
|
|
28
30
|
},
|
|
29
31
|
} });
|
|
32
|
+
uiSchema['layerOverride'] = Object.assign(Object.assign({}, uiSchema['layerOverride']), { items: {
|
|
33
|
+
'ui:title': '',
|
|
34
|
+
targetLayer: {
|
|
35
|
+
'ui:field': 'layerSelect',
|
|
36
|
+
},
|
|
37
|
+
opacity: {
|
|
38
|
+
'ui:field': 'opacity',
|
|
39
|
+
},
|
|
40
|
+
}, 'ui:options': {
|
|
41
|
+
orderable: false,
|
|
42
|
+
}, 'ui:ArrayFieldTemplate': ArrayFieldTemplate });
|
|
43
|
+
// Remove properties that should not be displayed in the form
|
|
44
|
+
const layerOverrideItems = (_c = (_b = (_a = schema.properties) === null || _a === void 0 ? void 0 : _a.layerOverride) === null || _b === void 0 ? void 0 : _b.items) === null || _c === void 0 ? void 0 : _c.properties;
|
|
45
|
+
if (layerOverrideItems) {
|
|
46
|
+
delete layerOverrideItems.color;
|
|
47
|
+
delete layerOverrideItems.symbologyState;
|
|
48
|
+
}
|
|
30
49
|
this.removeFormEntry('zoom', data, schema, uiSchema);
|
|
31
50
|
}
|
|
32
51
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IAnnotation, IAnnotationModel, IDict, IJGISFilterItem, IJGISFormSchemaRegistry, IJGISLayer, IJGISSource } from '@jupytergis/schema';
|
|
1
|
+
import { IAnnotation, IAnnotationModel, IDict, IJGISFilterItem, IJGISFormSchemaRegistry, IJGISLayer, IJGISSource, IJupyterGISSettings } from '@jupytergis/schema';
|
|
2
2
|
import { User } from '@jupyterlab/services';
|
|
3
3
|
import { IStateDB } from '@jupyterlab/statedb';
|
|
4
4
|
import { Layer } from 'ol/layer';
|
|
@@ -10,6 +10,8 @@ interface IProps {
|
|
|
10
10
|
state?: IStateDB;
|
|
11
11
|
formSchemaRegistry?: IJGISFormSchemaRegistry;
|
|
12
12
|
annotationModel?: IAnnotationModel;
|
|
13
|
+
/** True when viewport matches (max-width: 768px). Injected by MainViewWithMediaQuery. */
|
|
14
|
+
isMobile?: boolean;
|
|
13
15
|
}
|
|
14
16
|
interface IStates {
|
|
15
17
|
id: string;
|
|
@@ -31,6 +33,7 @@ interface IStates {
|
|
|
31
33
|
}>;
|
|
32
34
|
displayTemporalController: boolean;
|
|
33
35
|
filterStates: IDict<IJGISFilterItem | undefined>;
|
|
36
|
+
jgisSettings: IJupyterGISSettings;
|
|
34
37
|
isSpectaPresentation: boolean;
|
|
35
38
|
}
|
|
36
39
|
export declare class MainView extends React.Component<IProps, IStates> {
|
|
@@ -136,6 +139,7 @@ export declare class MainView extends React.Component<IProps, IStates> {
|
|
|
136
139
|
removeLayer(id: string): void;
|
|
137
140
|
private _onClientSharedStateChanged;
|
|
138
141
|
private _onSharedOptionsChanged;
|
|
142
|
+
private _syncSettingsFromRegistry;
|
|
139
143
|
private _onSettingsChanged;
|
|
140
144
|
private updateOptions;
|
|
141
145
|
private _onViewChanged;
|
|
@@ -222,4 +226,6 @@ export declare class MainView extends React.Component<IProps, IStates> {
|
|
|
222
226
|
private _isSpectaPresentationInitialized;
|
|
223
227
|
private _storyScrollHandler;
|
|
224
228
|
}
|
|
225
|
-
|
|
229
|
+
/** Thin wrapper that injects isMobile from useMediaQuery so MainView can use it in JSX. */
|
|
230
|
+
declare function MainViewWithMediaQuery(props: IProps): React.JSX.Element;
|
|
231
|
+
export { MainViewWithMediaQuery };
|
package/lib/mainview/mainView.js
CHANGED
|
@@ -39,6 +39,7 @@ import * as React from 'react';
|
|
|
39
39
|
import AnnotationFloater from "../annotations/components/AnnotationFloater";
|
|
40
40
|
import { CommandIDs } from "../constants";
|
|
41
41
|
import { LoadingOverlay } from "../shared/components/loading";
|
|
42
|
+
import useMediaQuery from "../shared/hooks/useMediaQuery";
|
|
42
43
|
import StatusBar from "../statusbar/StatusBar";
|
|
43
44
|
import { debounce, isLightTheme, loadFile, throttle } from "../tools";
|
|
44
45
|
import CollaboratorPointers from './CollaboratorPointers';
|
|
@@ -47,6 +48,7 @@ import TemporalSlider from './TemporalSlider';
|
|
|
47
48
|
import { hexToRgb } from '../dialogs/symbology/colorRampUtils';
|
|
48
49
|
import { markerIcon } from '../icons';
|
|
49
50
|
import { LeftPanel, RightPanel } from '../panelview';
|
|
51
|
+
import { MobileSpectaPanel } from '../panelview/story-maps/MobileSpectaPanel';
|
|
50
52
|
import StoryViewerPanel from '../panelview/story-maps/StoryViewerPanel';
|
|
51
53
|
export class MainView extends React.Component {
|
|
52
54
|
constructor(props) {
|
|
@@ -529,7 +531,7 @@ export class MainView extends React.Component {
|
|
|
529
531
|
}
|
|
530
532
|
const story = this._model.getSelectedStory().story;
|
|
531
533
|
const bgColor = story === null || story === void 0 ? void 0 : story.presentationBgColor;
|
|
532
|
-
const textColor = story === null || story === void 0 ? void 0 : story.
|
|
534
|
+
const textColor = story === null || story === void 0 ? void 0 : story.presentationTextColor;
|
|
533
535
|
// Set background color
|
|
534
536
|
if (bgColor) {
|
|
535
537
|
const rgb = hexToRgb(bgColor);
|
|
@@ -634,6 +636,9 @@ export class MainView extends React.Component {
|
|
|
634
636
|
this._model.geolocationChanged.connect(this._handleGeolocationChanged, this);
|
|
635
637
|
this._model.flyToGeometrySignal.connect(this.flyToGeometry, this);
|
|
636
638
|
this._model.highlightFeatureSignal.connect(this.highlightFeatureOnMap, this);
|
|
639
|
+
Promise.resolve().then(() => {
|
|
640
|
+
this._syncSettingsFromRegistry();
|
|
641
|
+
});
|
|
637
642
|
// Watch isIdentifying and clear the highlight when Identify Tool is turned off
|
|
638
643
|
this._model.sharedModel.awareness.on('change', () => {
|
|
639
644
|
var _a;
|
|
@@ -653,6 +658,7 @@ export class MainView extends React.Component {
|
|
|
653
658
|
loadingErrors: [],
|
|
654
659
|
displayTemporalController: false,
|
|
655
660
|
filterStates: {},
|
|
661
|
+
jgisSettings: this._model.jgisSettings,
|
|
656
662
|
isSpectaPresentation: false,
|
|
657
663
|
};
|
|
658
664
|
this._sources = [];
|
|
@@ -1203,6 +1209,7 @@ export class MainView extends React.Component {
|
|
|
1203
1209
|
layerParameters = layer.parameters;
|
|
1204
1210
|
newMapLayer = new VectorTileLayer({
|
|
1205
1211
|
opacity: layerParameters.opacity,
|
|
1212
|
+
visible: layer.visible,
|
|
1206
1213
|
source: this._sources[layerParameters.source],
|
|
1207
1214
|
style: this.vectorLayerStyleRuleBuilder(layer),
|
|
1208
1215
|
});
|
|
@@ -1212,6 +1219,7 @@ export class MainView extends React.Component {
|
|
|
1212
1219
|
layerParameters = layer.parameters;
|
|
1213
1220
|
newMapLayer = new WebGlTileLayer({
|
|
1214
1221
|
opacity: 0.3,
|
|
1222
|
+
visible: layer.visible,
|
|
1215
1223
|
source: this._sources[layerParameters.source],
|
|
1216
1224
|
style: {
|
|
1217
1225
|
color: ['color', this.hillshadeMath()],
|
|
@@ -1223,6 +1231,7 @@ export class MainView extends React.Component {
|
|
|
1223
1231
|
layerParameters = layer.parameters;
|
|
1224
1232
|
newMapLayer = new ImageLayer({
|
|
1225
1233
|
opacity: layerParameters.opacity,
|
|
1234
|
+
visible: layer.visible,
|
|
1226
1235
|
source: this._sources[layerParameters.source],
|
|
1227
1236
|
});
|
|
1228
1237
|
break;
|
|
@@ -1232,6 +1241,7 @@ export class MainView extends React.Component {
|
|
|
1232
1241
|
// This is to handle python sending a None for the color
|
|
1233
1242
|
const layerOptions = {
|
|
1234
1243
|
opacity: layerParameters.opacity,
|
|
1244
|
+
visible: layer.visible,
|
|
1235
1245
|
source: this._sources[layerParameters.source],
|
|
1236
1246
|
};
|
|
1237
1247
|
if (layerParameters.color) {
|
|
@@ -1246,6 +1256,7 @@ export class MainView extends React.Component {
|
|
|
1246
1256
|
layerParameters = layer.parameters;
|
|
1247
1257
|
newMapLayer = new HeatmapLayer({
|
|
1248
1258
|
opacity: layerParameters.opacity,
|
|
1259
|
+
visible: layer.visible,
|
|
1249
1260
|
source: this._sources[layerParameters.source],
|
|
1250
1261
|
blur: (_b = layerParameters.blur) !== null && _b !== void 0 ? _b : 15,
|
|
1251
1262
|
radius: (_c = layerParameters.radius) !== null && _c !== void 0 ? _c : 8,
|
|
@@ -1564,8 +1575,16 @@ export class MainView extends React.Component {
|
|
|
1564
1575
|
this._isPositionInitialized = true;
|
|
1565
1576
|
}
|
|
1566
1577
|
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1578
|
+
async _syncSettingsFromRegistry() {
|
|
1579
|
+
const composite = this._model.jgisSettings;
|
|
1580
|
+
if (composite) {
|
|
1581
|
+
this.setState({ jgisSettings: composite });
|
|
1582
|
+
this._onSettingsChanged();
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
_onSettingsChanged() {
|
|
1586
|
+
this.setState({ jgisSettings: this._model.jgisSettings });
|
|
1587
|
+
if (!this._Map) {
|
|
1569
1588
|
return;
|
|
1570
1589
|
}
|
|
1571
1590
|
const enabled = this._model.jgisSettings.zoomButtonsEnabled;
|
|
@@ -1795,6 +1814,14 @@ export class MainView extends React.Component {
|
|
|
1795
1814
|
if ((jgisLayer === null || jgisLayer === void 0 ? void 0 : jgisLayer.type) === 'StorySegmentLayer') {
|
|
1796
1815
|
const layerParams = jgisLayer.parameters;
|
|
1797
1816
|
const coords = getCenter(layerParams.extent);
|
|
1817
|
+
// Don't move map if we're already centered on the segment
|
|
1818
|
+
const viewCenter = this._Map.getView().getCenter();
|
|
1819
|
+
const centersEqual = viewCenter !== undefined &&
|
|
1820
|
+
Math.abs(viewCenter[0] - coords[0]) < 1e-9 &&
|
|
1821
|
+
Math.abs(viewCenter[1] - coords[1]) < 1e-9;
|
|
1822
|
+
if (centersEqual) {
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1798
1825
|
this._flyToPosition({ x: coords[0], y: coords[1] }, layerParams.zoom, ((_c = layerParams.transition.time) !== null && _c !== void 0 ? _c : 1) * 1000, // seconds -> ms
|
|
1799
1826
|
layerParams.transition.type);
|
|
1800
1827
|
return;
|
|
@@ -2064,11 +2091,18 @@ export class MainView extends React.Component {
|
|
|
2064
2091
|
height: '100%',
|
|
2065
2092
|
} },
|
|
2066
2093
|
React.createElement("div", { className: "jgis-panels-wrapper" }, !this.state.isSpectaPresentation ? (React.createElement(React.Fragment, null,
|
|
2067
|
-
this._state && (React.createElement(LeftPanel, { model: this._model, commands: this._mainViewModel.commands, state: this._state })),
|
|
2068
|
-
this._formSchemaRegistry && this._annotationModel && (React.createElement(RightPanel, { model: this._model, commands: this._mainViewModel.commands, formSchemaRegistry: this._formSchemaRegistry, annotationModel: this._annotationModel })))) : (React.createElement("div", { className: "jgis-specta-right-panel-container-mod jgis-right-panel-container" },
|
|
2094
|
+
this._state && (React.createElement(LeftPanel, { model: this._model, commands: this._mainViewModel.commands, state: this._state, settings: this.state.jgisSettings })),
|
|
2095
|
+
this._formSchemaRegistry && this._annotationModel && (React.createElement(RightPanel, { model: this._model, commands: this._mainViewModel.commands, formSchemaRegistry: this._formSchemaRegistry, annotationModel: this._annotationModel, addLayer: this.addLayer.bind(this), removeLayer: this.removeLayer.bind(this), settings: this.state.jgisSettings })))) : this.props.isMobile ? (React.createElement(MobileSpectaPanel, { model: this._model })) : (React.createElement("div", { className: "jgis-specta-right-panel-container-mod jgis-right-panel-container" },
|
|
2069
2096
|
React.createElement("div", { ref: this.spectaContainerRef, className: "jgis-specta-story-panel-container" },
|
|
2070
|
-
React.createElement(StoryViewerPanel, { ref: this.storyViewerPanelRef, model: this._model, isSpecta: this.state.isSpectaPresentation }))))),
|
|
2097
|
+
React.createElement(StoryViewerPanel, { ref: this.storyViewerPanelRef, model: this._model, isSpecta: this.state.isSpectaPresentation, className: "jgis-story-viewer-panel-specta-mod" }))))),
|
|
2071
2098
|
React.createElement("div", { ref: this.controlsToolbarRef, className: "jgis-controls-toolbar" }))),
|
|
2072
|
-
React.createElement(StatusBar, { jgisModel: this._model, loading: this.state.loadingLayer, projection: this.state.viewProjection, scale: this.state.scale }))));
|
|
2099
|
+
!this.state.isSpectaPresentation && (React.createElement(StatusBar, { jgisModel: this._model, loading: this.state.loadingLayer, projection: this.state.viewProjection, scale: this.state.scale })))));
|
|
2073
2100
|
}
|
|
2074
2101
|
}
|
|
2102
|
+
// ! TODO make mainview a modern react component instead of a class
|
|
2103
|
+
/** Thin wrapper that injects isMobile from useMediaQuery so MainView can use it in JSX. */
|
|
2104
|
+
function MainViewWithMediaQuery(props) {
|
|
2105
|
+
const isMobile = useMediaQuery('(max-width: 768px)');
|
|
2106
|
+
return React.createElement(MainView, Object.assign({}, props, { isMobile: isMobile }));
|
|
2107
|
+
}
|
|
2108
|
+
export { MainViewWithMediaQuery };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReactWidget } from '@jupyterlab/apputils';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { MainViewWithMediaQuery } from './mainView';
|
|
4
4
|
export class JupyterGISMainViewPanel extends ReactWidget {
|
|
5
5
|
/**
|
|
6
6
|
* Construct a `JupyterGISPanel`.
|
|
@@ -12,6 +12,6 @@ export class JupyterGISMainViewPanel extends ReactWidget {
|
|
|
12
12
|
this._options = options;
|
|
13
13
|
}
|
|
14
14
|
render() {
|
|
15
|
-
return (React.createElement(
|
|
15
|
+
return (React.createElement(MainViewWithMediaQuery, { state: this._state, viewModel: this._options.mainViewModel, formSchemaRegistry: this._options.formSchemaRegistry, annotationModel: this._options.annotationModel }));
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IJupyterGISModel, SelectionType } from '@jupytergis/schema';
|
|
1
|
+
import { IJupyterGISModel, SelectionType, IJupyterGISSettings } from '@jupytergis/schema';
|
|
2
2
|
import { IStateDB } from '@jupyterlab/statedb';
|
|
3
3
|
import { CommandRegistry } from '@lumino/commands';
|
|
4
4
|
import { MouseEvent as ReactMouseEvent } from 'react';
|
|
@@ -12,6 +12,7 @@ interface ILeftPanelProps {
|
|
|
12
12
|
model: IJupyterGISModel;
|
|
13
13
|
state: IStateDB;
|
|
14
14
|
commands: CommandRegistry;
|
|
15
|
+
settings: IJupyterGISSettings;
|
|
15
16
|
}
|
|
16
17
|
export declare const LeftPanel: React.FC<ILeftPanelProps>;
|
|
17
18
|
export {};
|
|
@@ -6,32 +6,42 @@ import { PanelTabs, TabsContent, TabsList, TabsTrigger, } from '../shared/compon
|
|
|
6
6
|
import StacPanel from '../stacBrowser/components/StacPanel';
|
|
7
7
|
export const LeftPanel = (props) => {
|
|
8
8
|
var _a;
|
|
9
|
-
const [settings, setSettings] = React.useState(props.model.jgisSettings);
|
|
10
9
|
const [options, setOptions] = React.useState(props.model.getOptions());
|
|
11
10
|
const storyMapPresentationMode = (_a = options.storyMapPresentationMode) !== null && _a !== void 0 ? _a : false;
|
|
12
11
|
const [layerTree, setLayerTree] = React.useState(props.model.getLayerTree());
|
|
12
|
+
const hasSyncedInitialSelectionRef = React.useRef(false);
|
|
13
13
|
const tabInfo = [
|
|
14
|
-
!settings.layersDisabled
|
|
15
|
-
|
|
14
|
+
!props.settings.layersDisabled
|
|
15
|
+
? { name: 'layers', title: 'Layers' }
|
|
16
|
+
: false,
|
|
17
|
+
!props.settings.stacBrowserDisabled && !storyMapPresentationMode
|
|
16
18
|
? { name: 'stac', title: 'Stac Browser' }
|
|
17
19
|
: false,
|
|
18
|
-
!settings.filtersDisabled && !storyMapPresentationMode
|
|
20
|
+
!props.settings.filtersDisabled && !storyMapPresentationMode
|
|
19
21
|
? { name: 'filters', title: 'Filters' }
|
|
20
22
|
: false,
|
|
21
|
-
!settings.storyMapsDisabled
|
|
23
|
+
!props.settings.storyMapsDisabled
|
|
22
24
|
? { name: 'segments', title: 'Segments' }
|
|
23
25
|
: false,
|
|
24
26
|
].filter(Boolean);
|
|
25
27
|
const [curTab, setCurTab] = React.useState(tabInfo.length > 0 ? tabInfo[0].name : undefined);
|
|
26
28
|
React.useEffect(() => {
|
|
27
|
-
const onSettingsChanged = () => {
|
|
28
|
-
setSettings(Object.assign({}, props.model.jgisSettings));
|
|
29
|
-
};
|
|
30
29
|
const onOptionsChanged = () => {
|
|
31
30
|
setOptions(Object.assign({}, props.model.getOptions()));
|
|
32
31
|
};
|
|
33
32
|
const updateLayerTree = () => {
|
|
34
|
-
|
|
33
|
+
const freshTree = props.model.getLayerTree() || [];
|
|
34
|
+
setLayerTree(freshTree);
|
|
35
|
+
// Sync selected to top layer/group only the first time the tree has items
|
|
36
|
+
if (!hasSyncedInitialSelectionRef.current && freshTree.length > 0) {
|
|
37
|
+
hasSyncedInitialSelectionRef.current = true;
|
|
38
|
+
const lastItem = freshTree[freshTree.length - 1];
|
|
39
|
+
const lastId = typeof lastItem === 'string' ? lastItem : lastItem === null || lastItem === void 0 ? void 0 : lastItem.name;
|
|
40
|
+
const lastType = typeof lastItem === 'string' ? 'layer' : 'group';
|
|
41
|
+
if (lastId) {
|
|
42
|
+
props.model.syncSelected({ [lastId]: { type: lastType } }, props.model.getClientId().toString());
|
|
43
|
+
}
|
|
44
|
+
}
|
|
35
45
|
// Need to let command know when segments get populated
|
|
36
46
|
props.commands.notifyCommandChanged(CommandIDs.toggleStoryPresentationMode);
|
|
37
47
|
};
|
|
@@ -39,14 +49,12 @@ export const LeftPanel = (props) => {
|
|
|
39
49
|
props.model.syncSelected({ [payload.storySegmentId]: { type: 'layer' } }, props.model.getClientId().toString());
|
|
40
50
|
setCurTab('segments');
|
|
41
51
|
};
|
|
42
|
-
props.model.settingsChanged.connect(onSettingsChanged);
|
|
43
52
|
props.model.sharedOptionsChanged.connect(onOptionsChanged);
|
|
44
53
|
props.model.sharedModel.layersChanged.connect(updateLayerTree);
|
|
45
54
|
props.model.sharedModel.layerTreeChanged.connect(updateLayerTree);
|
|
46
55
|
props.model.segmentAdded.connect(onSegmentAdded);
|
|
47
56
|
updateLayerTree();
|
|
48
57
|
return () => {
|
|
49
|
-
props.model.settingsChanged.disconnect(onSettingsChanged);
|
|
50
58
|
props.model.sharedOptionsChanged.disconnect(onOptionsChanged);
|
|
51
59
|
props.model.sharedModel.layersChanged.disconnect(updateLayerTree);
|
|
52
60
|
props.model.sharedModel.layerTreeChanged.disconnect(updateLayerTree);
|
|
@@ -110,11 +118,11 @@ export const LeftPanel = (props) => {
|
|
|
110
118
|
}
|
|
111
119
|
props.model.sharedModel.updateStoryMap(storyId, Object.assign(Object.assign({}, story), { storySegments: storySegmentLayerTree }));
|
|
112
120
|
}, [storySegmentLayerTree]);
|
|
113
|
-
const allLeftTabsDisabled = settings.layersDisabled &&
|
|
114
|
-
settings.stacBrowserDisabled &&
|
|
115
|
-
settings.filtersDisabled &&
|
|
116
|
-
settings.storyMapsDisabled;
|
|
117
|
-
const leftPanelVisible = !settings.leftPanelDisabled && !allLeftTabsDisabled;
|
|
121
|
+
const allLeftTabsDisabled = props.settings.layersDisabled &&
|
|
122
|
+
props.settings.stacBrowserDisabled &&
|
|
123
|
+
props.settings.filtersDisabled &&
|
|
124
|
+
props.settings.storyMapsDisabled;
|
|
125
|
+
const leftPanelVisible = !props.settings.leftPanelDisabled && !allLeftTabsDisabled;
|
|
118
126
|
return (React.createElement("div", { className: "jgis-left-panel-container", style: { display: leftPanelVisible ? 'block' : 'none' } },
|
|
119
127
|
React.createElement(PanelTabs, { curTab: curTab, className: "jgis-panel-tabs" },
|
|
120
128
|
React.createElement(TabsList, null, tabInfo.map(tab => (React.createElement(TabsTrigger, { className: "jGIS-layer-browser-category", key: tab.name, value: tab.name, onClick: () => {
|
|
@@ -125,12 +133,12 @@ export const LeftPanel = (props) => {
|
|
|
125
133
|
setCurTab('');
|
|
126
134
|
}
|
|
127
135
|
} }, tab.title)))),
|
|
128
|
-
!settings.layersDisabled && (React.createElement(TabsContent, { value: "layers", className: "jgis-panel-tab-content jp-gis-layerPanel" },
|
|
136
|
+
!props.settings.layersDisabled && (React.createElement(TabsContent, { value: "layers", className: "jgis-panel-tab-content jp-gis-layerPanel" },
|
|
129
137
|
React.createElement(LayersBodyComponent, { model: props.model, commands: props.commands, state: props.state, layerTree: filteredLayerTree }))),
|
|
130
|
-
!settings.stacBrowserDisabled && (React.createElement(TabsContent, { value: "stac", className: "jgis-panel-tab-content jgis-panel-tab-content-stac-panel" },
|
|
138
|
+
!props.settings.stacBrowserDisabled && (React.createElement(TabsContent, { value: "stac", className: "jgis-panel-tab-content jgis-panel-tab-content-stac-panel" },
|
|
131
139
|
React.createElement(StacPanel, { model: props.model }))),
|
|
132
|
-
!settings.filtersDisabled && (React.createElement(TabsContent, { value: "filters", className: "jgis-panel-tab-content" },
|
|
140
|
+
!props.settings.filtersDisabled && (React.createElement(TabsContent, { value: "filters", className: "jgis-panel-tab-content" },
|
|
133
141
|
React.createElement(FilterComponent, { model: props.model }))),
|
|
134
|
-
!settings.storyMapsDisabled && (React.createElement(TabsContent, { value: "segments", className: "jgis-panel-tab-content" },
|
|
142
|
+
!props.settings.storyMapsDisabled && (React.createElement(TabsContent, { value: "segments", className: "jgis-panel-tab-content" },
|
|
135
143
|
React.createElement(LayersBodyComponent, { model: props.model, commands: props.commands, state: props.state, layerTree: storySegmentLayerTree }))))));
|
|
136
144
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IAnnotationModel, IJGISFormSchemaRegistry, IJupyterGISModel } from '@jupytergis/schema';
|
|
1
|
+
import { IAnnotationModel, IJGISFormSchemaRegistry, IJGISLayer, IJupyterGISModel, IJupyterGISSettings } from '@jupytergis/schema';
|
|
2
2
|
import { CommandRegistry } from '@lumino/commands';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
interface IRightPanelProps {
|
|
@@ -6,6 +6,9 @@ interface IRightPanelProps {
|
|
|
6
6
|
annotationModel: IAnnotationModel;
|
|
7
7
|
model: IJupyterGISModel;
|
|
8
8
|
commands: CommandRegistry;
|
|
9
|
+
settings: IJupyterGISSettings;
|
|
10
|
+
addLayer?: (id: string, layer: IJGISLayer, index: number) => Promise<void>;
|
|
11
|
+
removeLayer?: (id: string) => void;
|
|
9
12
|
}
|
|
10
13
|
export declare const RightPanel: React.FC<IRightPanelProps>;
|
|
11
14
|
export {};
|
|
@@ -9,8 +9,8 @@ import { PanelTabs, TabsContent, TabsList, TabsTrigger, } from '../shared/compon
|
|
|
9
9
|
export const RightPanel = props => {
|
|
10
10
|
var _a;
|
|
11
11
|
const [editorMode, setEditorMode] = React.useState(true);
|
|
12
|
-
const [settings, setSettings] = React.useState(props.model.jgisSettings);
|
|
13
12
|
const [storyMapPresentationMode, setStoryMapPresentationMode] = React.useState((_a = props.model.getOptions().storyMapPresentationMode) !== null && _a !== void 0 ? _a : false);
|
|
13
|
+
const [selectedObjectProperties, setSelectedObjectProperties] = React.useState(undefined);
|
|
14
14
|
// Only show editor when not in presentation mode and editorMode is true
|
|
15
15
|
const showEditor = !storyMapPresentationMode && editorMode;
|
|
16
16
|
// Tab title: "Story Map" in presentation mode, otherwise based on editorMode
|
|
@@ -20,19 +20,19 @@ export const RightPanel = props => {
|
|
|
20
20
|
? 'Story Editor'
|
|
21
21
|
: 'Story Map';
|
|
22
22
|
const tabInfo = [
|
|
23
|
-
!settings.objectPropertiesDisabled && !storyMapPresentationMode
|
|
23
|
+
!props.settings.objectPropertiesDisabled && !storyMapPresentationMode
|
|
24
24
|
? { name: 'objectProperties', title: 'Object Properties' }
|
|
25
25
|
: false,
|
|
26
|
-
!settings.storyMapsDisabled
|
|
26
|
+
!props.settings.storyMapsDisabled
|
|
27
27
|
? {
|
|
28
28
|
name: 'storyPanel',
|
|
29
29
|
title: storyPanelTitle,
|
|
30
30
|
}
|
|
31
31
|
: false,
|
|
32
|
-
!settings.annotationsDisabled
|
|
32
|
+
!props.settings.annotationsDisabled
|
|
33
33
|
? { name: 'annotations', title: 'Annotations' }
|
|
34
34
|
: false,
|
|
35
|
-
!settings.identifyDisabled
|
|
35
|
+
!props.settings.identifyDisabled
|
|
36
36
|
? { name: 'identifyPanel', title: 'Identified Features' }
|
|
37
37
|
: false,
|
|
38
38
|
].filter(Boolean);
|
|
@@ -43,9 +43,6 @@ export const RightPanel = props => {
|
|
|
43
43
|
return tabInfo.length > 0 ? tabInfo[0].name : '';
|
|
44
44
|
});
|
|
45
45
|
React.useEffect(() => {
|
|
46
|
-
const onSettingsChanged = () => {
|
|
47
|
-
setSettings(Object.assign({}, props.model.jgisSettings));
|
|
48
|
-
};
|
|
49
46
|
const onOptionsChanged = () => {
|
|
50
47
|
const { storyMapPresentationMode } = props.model.getOptions();
|
|
51
48
|
setStoryMapPresentationMode(storyMapPresentationMode !== null && storyMapPresentationMode !== void 0 ? storyMapPresentationMode : false);
|
|
@@ -63,20 +60,17 @@ export const RightPanel = props => {
|
|
|
63
60
|
setCurTab('identifyPanel');
|
|
64
61
|
}
|
|
65
62
|
};
|
|
66
|
-
props.model.settingsChanged.connect(onSettingsChanged);
|
|
67
63
|
props.model.sharedOptionsChanged.connect(onOptionsChanged);
|
|
68
64
|
props.model.clientStateChanged.connect(onAwerenessChanged);
|
|
69
65
|
return () => {
|
|
70
|
-
props.model.settingsChanged.disconnect(onSettingsChanged);
|
|
71
66
|
props.model.sharedOptionsChanged.disconnect(onOptionsChanged);
|
|
72
67
|
props.model.clientStateChanged.disconnect(onAwerenessChanged);
|
|
73
68
|
};
|
|
74
69
|
}, [props.model]);
|
|
75
|
-
const allRightTabsDisabled = settings.objectPropertiesDisabled &&
|
|
76
|
-
settings.annotationsDisabled &&
|
|
77
|
-
settings.identifyDisabled;
|
|
78
|
-
const rightPanelVisible = !settings.rightPanelDisabled && !allRightTabsDisabled;
|
|
79
|
-
const [selectedObjectProperties, setSelectedObjectProperties] = React.useState(undefined);
|
|
70
|
+
const allRightTabsDisabled = props.settings.objectPropertiesDisabled &&
|
|
71
|
+
props.settings.annotationsDisabled &&
|
|
72
|
+
props.settings.identifyDisabled;
|
|
73
|
+
const rightPanelVisible = !props.settings.rightPanelDisabled && !allRightTabsDisabled;
|
|
80
74
|
const toggleEditor = () => {
|
|
81
75
|
setEditorMode(!editorMode);
|
|
82
76
|
};
|
|
@@ -90,13 +84,13 @@ export const RightPanel = props => {
|
|
|
90
84
|
setCurTab('');
|
|
91
85
|
}
|
|
92
86
|
} }, tab.title)))),
|
|
93
|
-
!settings.objectPropertiesDisabled && (React.createElement(TabsContent, { value: "objectProperties", className: "jgis-panel-tab-content" },
|
|
87
|
+
!props.settings.objectPropertiesDisabled && (React.createElement(TabsContent, { value: "objectProperties", className: "jgis-panel-tab-content" },
|
|
94
88
|
React.createElement(ObjectPropertiesReact, { setSelectedObject: setSelectedObjectProperties, selectedObject: selectedObjectProperties, formSchemaRegistry: props.formSchemaRegistry, model: props.model }))),
|
|
95
|
-
!settings.storyMapsDisabled && (React.createElement(TabsContent, { value: "storyPanel", className: "jgis-panel-tab-content", style: { paddingTop: 0 } },
|
|
89
|
+
!props.settings.storyMapsDisabled && (React.createElement(TabsContent, { value: "storyPanel", className: "jgis-panel-tab-content", style: { paddingTop: 0 } },
|
|
96
90
|
!storyMapPresentationMode && (React.createElement(PreviewModeSwitch, { checked: !editorMode, onCheckedChange: toggleEditor })),
|
|
97
|
-
showEditor ? (React.createElement(StoryEditorPanel, { model: props.model, commands: props.commands })) : (React.createElement(StoryViewerPanel, { model: props.model, isSpecta: false })))),
|
|
98
|
-
!settings.annotationsDisabled && (React.createElement(TabsContent, { value: "annotations", className: "jgis-panel-tab-content" },
|
|
91
|
+
showEditor ? (React.createElement(StoryEditorPanel, { model: props.model, commands: props.commands })) : (React.createElement(StoryViewerPanel, { model: props.model, isSpecta: false, addLayer: props.addLayer, removeLayer: props.removeLayer })))),
|
|
92
|
+
!props.settings.annotationsDisabled && (React.createElement(TabsContent, { value: "annotations", className: "jgis-panel-tab-content" },
|
|
99
93
|
React.createElement(AnnotationsPanel, { annotationModel: props.annotationModel, jgisModel: props.model }))),
|
|
100
|
-
!settings.identifyDisabled && (React.createElement(TabsContent, { value: "identifyPanel", className: "jgis-panel-tab-content" },
|
|
94
|
+
!props.settings.identifyDisabled && (React.createElement(TabsContent, { value: "identifyPanel", className: "jgis-panel-tab-content" },
|
|
101
95
|
React.createElement(IdentifyPanelComponent, { model: props.model }))))));
|
|
102
96
|
};
|