@jupytergis/base 0.12.1 → 0.12.2
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/dialogs/layerBrowserDialog.d.ts +3 -3
- package/lib/dialogs/layerBrowserDialog.js +9 -10
- package/lib/formbuilder/objectform/StoryEditorForm.js +5 -5
- package/lib/mainview/mainView.d.ts +8 -2
- package/lib/mainview/mainView.js +36 -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 +2 -1
- package/lib/panelview/rightpanel.js +12 -18
- 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 +10 -0
- package/lib/panelview/story-maps/StoryViewerPanel.js +44 -18
- 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/useMediaQuery.d.ts +9 -0
- package/lib/shared/hooks/useMediaQuery.js +32 -0
- package/lib/tools.js +21 -34
- package/package.json +5 -12
- package/style/base.css +7 -0
- package/style/shared/drawer.css +154 -0
- package/style/shared/sheet.css +258 -0
- package/style/storyPanel.css +32 -4
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { IJGISFormSchemaRegistry, IJupyterGISModel,
|
|
1
|
+
import { IJGISFormSchemaRegistry, IJupyterGISModel, ILayerGalleryEntry } from '@jupytergis/schema';
|
|
2
2
|
import { Dialog } from '@jupyterlab/apputils';
|
|
3
3
|
import { PromiseDelegate } from '@lumino/coreutils';
|
|
4
4
|
import { Signal } from '@lumino/signaling';
|
|
5
5
|
import React from 'react';
|
|
6
6
|
interface ILayerBrowserDialogProps {
|
|
7
7
|
model: IJupyterGISModel;
|
|
8
|
-
registry:
|
|
8
|
+
registry: ILayerGalleryEntry[];
|
|
9
9
|
formSchemaRegistry: IJGISFormSchemaRegistry;
|
|
10
10
|
okSignalPromise: PromiseDelegate<Signal<Dialog<any>, number>>;
|
|
11
11
|
cancel: () => void;
|
|
@@ -13,7 +13,7 @@ interface ILayerBrowserDialogProps {
|
|
|
13
13
|
export declare const LayerBrowserComponent: React.FC<ILayerBrowserDialogProps>;
|
|
14
14
|
export interface ILayerBrowserOptions {
|
|
15
15
|
model: IJupyterGISModel;
|
|
16
|
-
registry:
|
|
16
|
+
registry: ILayerGalleryEntry[];
|
|
17
17
|
formSchemaRegistry: IJGISFormSchemaRegistry;
|
|
18
18
|
}
|
|
19
19
|
export declare class LayerBrowserWidget extends Dialog<boolean> {
|
|
@@ -5,14 +5,14 @@ import { PromiseDelegate, UUID } from '@lumino/coreutils';
|
|
|
5
5
|
import { Signal } from '@lumino/signaling';
|
|
6
6
|
import React, { useEffect, useState } from 'react';
|
|
7
7
|
import { CreationFormWrapper } from './layerCreationFormDialog';
|
|
8
|
-
import CUSTOM_RASTER_IMAGE from '../../
|
|
8
|
+
import CUSTOM_RASTER_IMAGE from '../../layer_gallery/custom_raster.png';
|
|
9
9
|
export const LayerBrowserComponent = ({ model, registry, formSchemaRegistry, okSignalPromise, cancel, }) => {
|
|
10
10
|
const [searchTerm, setSearchTerm] = useState('');
|
|
11
11
|
const [activeLayers, setActiveLayers] = useState([]);
|
|
12
12
|
const [selectedCategory, setSelectedCategory] = useState();
|
|
13
13
|
const [creatingCustomRaster, setCreatingCustomRaster] = useState(false);
|
|
14
14
|
const [galleryWithCategory, setGalleryWithCategory] = useState(registry);
|
|
15
|
-
const providers = [...new Set(registry.map(item => item.
|
|
15
|
+
const providers = [...new Set(registry.map(item => item.provider))];
|
|
16
16
|
const filteredGallery = galleryWithCategory.filter(item => item.name.toLowerCase().includes(searchTerm));
|
|
17
17
|
useEffect(() => {
|
|
18
18
|
model.sharedModel.layersChanged.connect(handleLayerChange);
|
|
@@ -37,7 +37,7 @@ export const LayerBrowserComponent = ({ model, registry, formSchemaRegistry, okS
|
|
|
37
37
|
selectedCategory === null || selectedCategory === void 0 ? void 0 : selectedCategory.classList.remove('jGIS-layer-browser-category-selected');
|
|
38
38
|
const filteredGallery = sameAsOld
|
|
39
39
|
? registry
|
|
40
|
-
: registry.filter(item => { var _a; return (_a = item.
|
|
40
|
+
: registry.filter(item => { var _a; return (_a = item.provider) === null || _a === void 0 ? void 0 : _a.includes(categoryTab.innerText); });
|
|
41
41
|
setGalleryWithCategory(filteredGallery);
|
|
42
42
|
setSearchTerm('');
|
|
43
43
|
setSelectedCategory(sameAsOld ? null : categoryTab);
|
|
@@ -52,15 +52,13 @@ export const LayerBrowserComponent = ({ model, registry, formSchemaRegistry, okS
|
|
|
52
52
|
const handleTileClick = (tile) => {
|
|
53
53
|
const sourceId = UUID.uuid4();
|
|
54
54
|
const sourceModel = {
|
|
55
|
-
type:
|
|
55
|
+
type: tile.sourceType,
|
|
56
56
|
name: tile.name,
|
|
57
|
-
parameters: tile.
|
|
57
|
+
parameters: tile.sourceParameters,
|
|
58
58
|
};
|
|
59
59
|
const layerModel = {
|
|
60
|
-
type:
|
|
61
|
-
parameters: {
|
|
62
|
-
source: sourceId,
|
|
63
|
-
},
|
|
60
|
+
type: tile.layerType,
|
|
61
|
+
parameters: Object.assign(Object.assign({}, tile.layerParameters), { source: sourceId }),
|
|
64
62
|
visible: true,
|
|
65
63
|
name: tile.name + ' Layer',
|
|
66
64
|
};
|
|
@@ -113,7 +111,8 @@ export const LayerBrowserComponent = ({ model, registry, formSchemaRegistry, okS
|
|
|
113
111
|
React.createElement("div", { className: "jGIS-layer-browser-text-container" },
|
|
114
112
|
React.createElement("div", { className: "jGIS-layer-browser-text-info" },
|
|
115
113
|
React.createElement("h3", { className: "jGIS-layer-browser-text-header jGIS-layer-browser-text-general" }, tile.name)),
|
|
116
|
-
React.createElement("
|
|
114
|
+
React.createElement("div", null, tile.sourceType),
|
|
115
|
+
React.createElement("p", { className: "jGIS-layer-browser-text-general jGIS-layer-browser-text-source" }, tile.description))))))));
|
|
117
116
|
};
|
|
118
117
|
export class LayerBrowserWidget extends Dialog {
|
|
119
118
|
constructor(options) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BaseForm } from './baseform';
|
|
2
1
|
import { getCssVarAsColor } from "../../tools";
|
|
2
|
+
import { BaseForm } from './baseform';
|
|
3
3
|
/**
|
|
4
4
|
* The form to modify story map properties.
|
|
5
5
|
*/
|
|
@@ -10,7 +10,7 @@ export class StoryEditorPropertiesForm extends BaseForm {
|
|
|
10
10
|
uiSchema.presentationBgColor = {
|
|
11
11
|
'ui:widget': 'color',
|
|
12
12
|
};
|
|
13
|
-
uiSchema.
|
|
13
|
+
uiSchema.presentationTextColor = {
|
|
14
14
|
'ui:widget': 'color',
|
|
15
15
|
};
|
|
16
16
|
// Set default values from theme CSS variables when not already in data
|
|
@@ -22,11 +22,11 @@ export class StoryEditorPropertiesForm extends BaseForm {
|
|
|
22
22
|
schemaProps.presentationBgColor.default = defaultBg;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
if ((schemaProps === null || schemaProps === void 0 ? void 0 : schemaProps.
|
|
26
|
-
(data === null || data === void 0 ? void 0 : data.
|
|
25
|
+
if ((schemaProps === null || schemaProps === void 0 ? void 0 : schemaProps.presentationTextColor) &&
|
|
26
|
+
(data === null || data === void 0 ? void 0 : data.presentationTextColor) === undefined) {
|
|
27
27
|
const defaultText = getCssVarAsColor('--jp-ui-font-color0');
|
|
28
28
|
if (defaultText) {
|
|
29
|
-
schemaProps.
|
|
29
|
+
schemaProps.presentationTextColor.default = defaultText;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -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 = [];
|
|
@@ -1564,8 +1570,16 @@ export class MainView extends React.Component {
|
|
|
1564
1570
|
this._isPositionInitialized = true;
|
|
1565
1571
|
}
|
|
1566
1572
|
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1573
|
+
async _syncSettingsFromRegistry() {
|
|
1574
|
+
const composite = this._model.jgisSettings;
|
|
1575
|
+
if (composite) {
|
|
1576
|
+
this.setState({ jgisSettings: composite });
|
|
1577
|
+
this._onSettingsChanged();
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
_onSettingsChanged() {
|
|
1581
|
+
this.setState({ jgisSettings: this._model.jgisSettings });
|
|
1582
|
+
if (!this._Map) {
|
|
1569
1583
|
return;
|
|
1570
1584
|
}
|
|
1571
1585
|
const enabled = this._model.jgisSettings.zoomButtonsEnabled;
|
|
@@ -1795,6 +1809,14 @@ export class MainView extends React.Component {
|
|
|
1795
1809
|
if ((jgisLayer === null || jgisLayer === void 0 ? void 0 : jgisLayer.type) === 'StorySegmentLayer') {
|
|
1796
1810
|
const layerParams = jgisLayer.parameters;
|
|
1797
1811
|
const coords = getCenter(layerParams.extent);
|
|
1812
|
+
// Don't move map if we're already centered on the segment
|
|
1813
|
+
const viewCenter = this._Map.getView().getCenter();
|
|
1814
|
+
const centersEqual = viewCenter !== undefined &&
|
|
1815
|
+
Math.abs(viewCenter[0] - coords[0]) < 1e-9 &&
|
|
1816
|
+
Math.abs(viewCenter[1] - coords[1]) < 1e-9;
|
|
1817
|
+
if (centersEqual) {
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1798
1820
|
this._flyToPosition({ x: coords[0], y: coords[1] }, layerParams.zoom, ((_c = layerParams.transition.time) !== null && _c !== void 0 ? _c : 1) * 1000, // seconds -> ms
|
|
1799
1821
|
layerParams.transition.type);
|
|
1800
1822
|
return;
|
|
@@ -2064,11 +2086,18 @@ export class MainView extends React.Component {
|
|
|
2064
2086
|
height: '100%',
|
|
2065
2087
|
} },
|
|
2066
2088
|
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" },
|
|
2089
|
+
this._state && (React.createElement(LeftPanel, { model: this._model, commands: this._mainViewModel.commands, state: this._state, settings: this.state.jgisSettings })),
|
|
2090
|
+
this._formSchemaRegistry && this._annotationModel && (React.createElement(RightPanel, { model: this._model, commands: this._mainViewModel.commands, formSchemaRegistry: this._formSchemaRegistry, annotationModel: this._annotationModel, 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
2091
|
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 }))))),
|
|
2092
|
+
React.createElement(StoryViewerPanel, { ref: this.storyViewerPanelRef, model: this._model, isSpecta: this.state.isSpectaPresentation, className: "jgis-story-viewer-panel-specta-mod" }))))),
|
|
2071
2093
|
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 }))));
|
|
2094
|
+
!this.state.isSpectaPresentation && (React.createElement(StatusBar, { jgisModel: this._model, loading: this.state.loadingLayer, projection: this.state.viewProjection, scale: this.state.scale })))));
|
|
2073
2095
|
}
|
|
2074
2096
|
}
|
|
2097
|
+
// ! TODO make mainview a modern react component instead of a class
|
|
2098
|
+
/** Thin wrapper that injects isMobile from useMediaQuery so MainView can use it in JSX. */
|
|
2099
|
+
function MainViewWithMediaQuery(props) {
|
|
2100
|
+
const isMobile = useMediaQuery('(max-width: 768px)');
|
|
2101
|
+
return React.createElement(MainView, Object.assign({}, props, { isMobile: isMobile }));
|
|
2102
|
+
}
|
|
2103
|
+
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, 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,7 @@ interface IRightPanelProps {
|
|
|
6
6
|
annotationModel: IAnnotationModel;
|
|
7
7
|
model: IJupyterGISModel;
|
|
8
8
|
commands: CommandRegistry;
|
|
9
|
+
settings: IJupyterGISSettings;
|
|
9
10
|
}
|
|
10
11
|
export declare const RightPanel: React.FC<IRightPanelProps>;
|
|
11
12
|
export {};
|
|
@@ -9,7 +9,6 @@ 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);
|
|
14
13
|
// Only show editor when not in presentation mode and editorMode is true
|
|
15
14
|
const showEditor = !storyMapPresentationMode && editorMode;
|
|
@@ -20,19 +19,19 @@ export const RightPanel = props => {
|
|
|
20
19
|
? 'Story Editor'
|
|
21
20
|
: 'Story Map';
|
|
22
21
|
const tabInfo = [
|
|
23
|
-
!settings.objectPropertiesDisabled && !storyMapPresentationMode
|
|
22
|
+
!props.settings.objectPropertiesDisabled && !storyMapPresentationMode
|
|
24
23
|
? { name: 'objectProperties', title: 'Object Properties' }
|
|
25
24
|
: false,
|
|
26
|
-
!settings.storyMapsDisabled
|
|
25
|
+
!props.settings.storyMapsDisabled
|
|
27
26
|
? {
|
|
28
27
|
name: 'storyPanel',
|
|
29
28
|
title: storyPanelTitle,
|
|
30
29
|
}
|
|
31
30
|
: false,
|
|
32
|
-
!settings.annotationsDisabled
|
|
31
|
+
!props.settings.annotationsDisabled
|
|
33
32
|
? { name: 'annotations', title: 'Annotations' }
|
|
34
33
|
: false,
|
|
35
|
-
!settings.identifyDisabled
|
|
34
|
+
!props.settings.identifyDisabled
|
|
36
35
|
? { name: 'identifyPanel', title: 'Identified Features' }
|
|
37
36
|
: false,
|
|
38
37
|
].filter(Boolean);
|
|
@@ -43,9 +42,6 @@ export const RightPanel = props => {
|
|
|
43
42
|
return tabInfo.length > 0 ? tabInfo[0].name : '';
|
|
44
43
|
});
|
|
45
44
|
React.useEffect(() => {
|
|
46
|
-
const onSettingsChanged = () => {
|
|
47
|
-
setSettings(Object.assign({}, props.model.jgisSettings));
|
|
48
|
-
};
|
|
49
45
|
const onOptionsChanged = () => {
|
|
50
46
|
const { storyMapPresentationMode } = props.model.getOptions();
|
|
51
47
|
setStoryMapPresentationMode(storyMapPresentationMode !== null && storyMapPresentationMode !== void 0 ? storyMapPresentationMode : false);
|
|
@@ -63,19 +59,17 @@ export const RightPanel = props => {
|
|
|
63
59
|
setCurTab('identifyPanel');
|
|
64
60
|
}
|
|
65
61
|
};
|
|
66
|
-
props.model.settingsChanged.connect(onSettingsChanged);
|
|
67
62
|
props.model.sharedOptionsChanged.connect(onOptionsChanged);
|
|
68
63
|
props.model.clientStateChanged.connect(onAwerenessChanged);
|
|
69
64
|
return () => {
|
|
70
|
-
props.model.settingsChanged.disconnect(onSettingsChanged);
|
|
71
65
|
props.model.sharedOptionsChanged.disconnect(onOptionsChanged);
|
|
72
66
|
props.model.clientStateChanged.disconnect(onAwerenessChanged);
|
|
73
67
|
};
|
|
74
68
|
}, [props.model]);
|
|
75
|
-
const allRightTabsDisabled = settings.objectPropertiesDisabled &&
|
|
76
|
-
settings.annotationsDisabled &&
|
|
77
|
-
settings.identifyDisabled;
|
|
78
|
-
const rightPanelVisible = !settings.rightPanelDisabled && !allRightTabsDisabled;
|
|
69
|
+
const allRightTabsDisabled = props.settings.objectPropertiesDisabled &&
|
|
70
|
+
props.settings.annotationsDisabled &&
|
|
71
|
+
props.settings.identifyDisabled;
|
|
72
|
+
const rightPanelVisible = !props.settings.rightPanelDisabled && !allRightTabsDisabled;
|
|
79
73
|
const [selectedObjectProperties, setSelectedObjectProperties] = React.useState(undefined);
|
|
80
74
|
const toggleEditor = () => {
|
|
81
75
|
setEditorMode(!editorMode);
|
|
@@ -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
91
|
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" },
|
|
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
|
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { Button } from "../../shared/components/Button";
|
|
3
|
+
import { Drawer, DrawerContent, DrawerTrigger, } from "../../shared/components/Drawer";
|
|
4
|
+
import StoryViewerPanel from './StoryViewerPanel';
|
|
5
|
+
const MAIN_ID = 'jp-main-content-panel';
|
|
6
|
+
const SEGMENT_PANEL_ID = 'jgis-story-segment-panel';
|
|
7
|
+
const SEGMENT_HEADER_ID = 'jgis-story-segment-header';
|
|
8
|
+
const SNAP_FIRST_MIN = 0.3;
|
|
9
|
+
const SNAP_FIRST_MAX = 0.95;
|
|
10
|
+
const SNAP_FIRST_DEFAULT = 0.7;
|
|
11
|
+
/** Offset (px) for segment header height: margins from p and h1 in story content */
|
|
12
|
+
const SEGMENT_HEADER_OFFSET_PX = 16.8 * 2 + 18.76;
|
|
13
|
+
/**
|
|
14
|
+
* Compute the first snap point so that vaul's --snap-point-height (the
|
|
15
|
+
* transform offset) equals #jgis-story-segment-panel height minus #jgis-story-segment-header height.
|
|
16
|
+
* For a bottom drawer, offset = mainHeight * (1 - snapPoint), so
|
|
17
|
+
* snapPoint = (mainHeight - offset) / mainHeight.
|
|
18
|
+
*/
|
|
19
|
+
function getFirstSnapFromSegmentHeader(mainEl, segmentPanelEl, segmentHeaderEl) {
|
|
20
|
+
const mainHeight = mainEl.getBoundingClientRect().height;
|
|
21
|
+
const segmentPanelHeight = segmentPanelEl.getBoundingClientRect().height;
|
|
22
|
+
const segmentHeaderHeight = segmentHeaderEl.getBoundingClientRect().height;
|
|
23
|
+
const offsetPx = segmentPanelHeight - segmentHeaderHeight - SEGMENT_HEADER_OFFSET_PX;
|
|
24
|
+
if (mainHeight <= 0) {
|
|
25
|
+
return SNAP_FIRST_DEFAULT;
|
|
26
|
+
}
|
|
27
|
+
const fraction = (mainHeight - offsetPx) / mainHeight;
|
|
28
|
+
const clamped = Math.max(SNAP_FIRST_MIN, Math.min(SNAP_FIRST_MAX, fraction));
|
|
29
|
+
return clamped;
|
|
30
|
+
}
|
|
31
|
+
/** Build inline styles for specta presentation (bg and text color from story). */
|
|
32
|
+
function getSpectaPresentationStyle(model) {
|
|
33
|
+
const story = model.getSelectedStory().story;
|
|
34
|
+
const bgColor = story === null || story === void 0 ? void 0 : story.presentationBgColor;
|
|
35
|
+
const textColor = story === null || story === void 0 ? void 0 : story.presentationTextColor;
|
|
36
|
+
const style = {};
|
|
37
|
+
if (bgColor) {
|
|
38
|
+
style['--jgis-specta-bg-color'] = bgColor;
|
|
39
|
+
style.backgroundColor = bgColor;
|
|
40
|
+
}
|
|
41
|
+
if (textColor) {
|
|
42
|
+
style['--jgis-specta-text-color'] = textColor;
|
|
43
|
+
style.color = textColor;
|
|
44
|
+
}
|
|
45
|
+
return style;
|
|
46
|
+
}
|
|
47
|
+
export function MobileSpectaPanel({ model }) {
|
|
48
|
+
const [container, setContainer] = useState(null);
|
|
49
|
+
const [snapPoints, setSnapPoints] = useState([
|
|
50
|
+
SNAP_FIRST_DEFAULT,
|
|
51
|
+
1,
|
|
52
|
+
]);
|
|
53
|
+
const [snap, setSnap] = useState(snapPoints[0]);
|
|
54
|
+
const presentationStyle = getSpectaPresentationStyle(model);
|
|
55
|
+
// Keep active snap in sync with snapPoints so Vaul's --snap-point-height stays defined.
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const isInSnapPoints = snapPoints.some(p => p === snap ||
|
|
58
|
+
(typeof p === 'number' &&
|
|
59
|
+
typeof snap === 'number' &&
|
|
60
|
+
Math.abs(p - snap) < 1e-9));
|
|
61
|
+
if (!isInSnapPoints && snapPoints.length > 0) {
|
|
62
|
+
setSnap(snapPoints[0]);
|
|
63
|
+
}
|
|
64
|
+
}, [snapPoints, snap]);
|
|
65
|
+
// Observe #jgis-story-segment-panel (and re-attach when drawer reopens).
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
const mainEl = document.getElementById(MAIN_ID);
|
|
68
|
+
setContainer(mainEl);
|
|
69
|
+
if (!mainEl) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const updateFirstSnap = () => {
|
|
73
|
+
const segmentPanelEl = document.getElementById(SEGMENT_PANEL_ID);
|
|
74
|
+
const segmentHeaderEl = document.getElementById(SEGMENT_HEADER_ID);
|
|
75
|
+
if (segmentPanelEl && segmentHeaderEl) {
|
|
76
|
+
const firstSnap = getFirstSnapFromSegmentHeader(mainEl, segmentPanelEl, segmentHeaderEl);
|
|
77
|
+
setSnapPoints([firstSnap, 1]);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const resizeObserver = new ResizeObserver(() => updateFirstSnap());
|
|
81
|
+
let observedPanelEl = null;
|
|
82
|
+
const syncHeaderObserver = () => {
|
|
83
|
+
const segmentPanelEl = document.getElementById(SEGMENT_PANEL_ID);
|
|
84
|
+
const segmentHeaderEl = document.getElementById(SEGMENT_HEADER_ID);
|
|
85
|
+
if (!segmentPanelEl ||
|
|
86
|
+
!segmentHeaderEl ||
|
|
87
|
+
segmentPanelEl === observedPanelEl) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (observedPanelEl) {
|
|
91
|
+
resizeObserver.unobserve(observedPanelEl);
|
|
92
|
+
}
|
|
93
|
+
resizeObserver.observe(segmentPanelEl);
|
|
94
|
+
observedPanelEl = segmentPanelEl;
|
|
95
|
+
updateFirstSnap();
|
|
96
|
+
};
|
|
97
|
+
syncHeaderObserver();
|
|
98
|
+
const mutationObserver = new MutationObserver(syncHeaderObserver);
|
|
99
|
+
mutationObserver.observe(mainEl, {
|
|
100
|
+
childList: true,
|
|
101
|
+
subtree: true,
|
|
102
|
+
});
|
|
103
|
+
return () => {
|
|
104
|
+
resizeObserver.disconnect();
|
|
105
|
+
mutationObserver.disconnect();
|
|
106
|
+
};
|
|
107
|
+
}, []);
|
|
108
|
+
return (React.createElement("div", { className: "jgis-mobile-specta-trigger-wrapper" },
|
|
109
|
+
React.createElement(Drawer, { snapPoints: snapPoints, activeSnapPoint: snap, setActiveSnapPoint: setSnap, direction: "bottom", container: container, noBodyStyles: true },
|
|
110
|
+
React.createElement(DrawerTrigger, { asChild: true },
|
|
111
|
+
React.createElement(Button, null, "Open Story Panel")),
|
|
112
|
+
React.createElement(DrawerContent, { style: presentationStyle },
|
|
113
|
+
React.createElement(StoryViewerPanel, { isSpecta: true, isMobile: true, model: model })))));
|
|
114
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { StoryNavPlacement } from './StoryViewerPanel';
|
|
2
3
|
interface IStoryNavBarProps {
|
|
4
|
+
placement: StoryNavPlacement;
|
|
3
5
|
onPrev: () => void;
|
|
4
6
|
onNext: () => void;
|
|
5
7
|
hasPrev: boolean;
|
|
6
8
|
hasNext: boolean;
|
|
7
|
-
isSpecta: boolean;
|
|
8
9
|
}
|
|
9
|
-
declare function StoryNavBar({ onPrev, onNext, hasPrev, hasNext,
|
|
10
|
+
declare function StoryNavBar({ placement, onPrev, onNext, hasPrev, hasNext, }: IStoryNavBarProps): React.JSX.Element;
|
|
10
11
|
export default StoryNavBar;
|