@jupytergis/base 0.13.3 → 0.14.1
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/BaseCommandIDs.d.ts +14 -14
- package/lib/commands/BaseCommandIDs.js +14 -15
- package/lib/commands/index.js +516 -135
- package/lib/commands/operationCommands.d.ts +22 -0
- package/lib/commands/operationCommands.js +305 -0
- package/lib/constants.js +9 -9
- package/lib/dialogs/ProcessingFormDialog.d.ts +1 -1
- package/lib/dialogs/ProcessingFormDialog.js +2 -2
- package/lib/dialogs/layerBrowserDialog.d.ts +2 -0
- package/lib/dialogs/layerBrowserDialog.js +12 -5
- package/lib/dialogs/layerCreationFormDialog.d.ts +7 -0
- package/lib/dialogs/layerCreationFormDialog.js +10 -2
- package/lib/dialogs/symbology/components/color_ramp/ColorRampControls.d.ts +2 -1
- package/lib/dialogs/symbology/components/color_ramp/ColorRampControls.js +21 -19
- package/lib/dialogs/symbology/components/color_ramp/ColorRampSelector.d.ts +3 -1
- package/lib/dialogs/symbology/components/color_ramp/ColorRampSelector.js +13 -5
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +6 -4
- package/lib/dialogs/symbology/vector_layer/VectorRendering.js +6 -5
- package/lib/dialogs/symbology/vector_layer/components/ValueSelect.js +3 -1
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +7 -11
- package/lib/dialogs/symbology/vector_layer/types/Graduated.js +7 -10
- package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +7 -8
- package/lib/formbuilder/creationform.d.ts +8 -8
- package/lib/formbuilder/creationform.js +130 -85
- package/lib/formbuilder/editform.d.ts +1 -7
- package/lib/formbuilder/editform.js +64 -52
- package/lib/formbuilder/formselectors.d.ts +5 -4
- package/lib/formbuilder/index.d.ts +1 -1
- package/lib/formbuilder/index.js +1 -1
- package/lib/formbuilder/objectform/SchemaForm.d.ts +36 -0
- package/lib/formbuilder/objectform/SchemaForm.js +77 -0
- package/lib/formbuilder/objectform/StoryEditorForm.d.ts +3 -9
- package/lib/formbuilder/objectform/StoryEditorForm.js +20 -14
- package/lib/formbuilder/objectform/components/LayerSelect.js +3 -8
- package/lib/formbuilder/objectform/components/SegmentFormSymbology.js +23 -10
- package/lib/formbuilder/objectform/components/SourcePropertiesField.d.ts +7 -0
- package/lib/formbuilder/objectform/components/SourcePropertiesField.js +29 -0
- package/lib/formbuilder/objectform/components/StorySegmentReset.js +1 -1
- package/lib/formbuilder/objectform/fileselectorwidget.js +1 -1
- package/lib/formbuilder/objectform/layer/heatmapLayerForm.d.ts +3 -12
- package/lib/formbuilder/objectform/layer/heatmapLayerForm.js +87 -55
- package/lib/formbuilder/objectform/layer/hillshadeLayerForm.d.ts +3 -8
- package/lib/formbuilder/objectform/layer/hillshadeLayerForm.js +36 -10
- package/lib/formbuilder/objectform/layer/layerform.d.ts +7 -9
- package/lib/formbuilder/objectform/layer/layerform.js +33 -20
- package/lib/formbuilder/objectform/layer/storySegmentLayerForm.d.ts +3 -5
- package/lib/formbuilder/objectform/layer/storySegmentLayerForm.js +73 -29
- package/lib/formbuilder/objectform/layer/vectorlayerform.d.ts +3 -14
- package/lib/formbuilder/objectform/layer/vectorlayerform.js +36 -29
- package/lib/formbuilder/objectform/layer/webGlLayerForm.d.ts +3 -10
- package/lib/formbuilder/objectform/layer/webGlLayerForm.js +37 -13
- package/lib/formbuilder/objectform/process/dissolveProcessForm.d.ts +8 -18
- package/lib/formbuilder/objectform/process/dissolveProcessForm.js +68 -56
- package/lib/formbuilder/objectform/processingForm.d.ts +12 -0
- package/lib/formbuilder/objectform/processingForm.js +33 -0
- package/lib/formbuilder/objectform/schemaUtils.d.ts +16 -0
- package/lib/formbuilder/objectform/schemaUtils.js +59 -0
- package/lib/formbuilder/objectform/source/geojsonsource.d.ts +3 -19
- package/lib/formbuilder/objectform/source/geojsonsource.js +94 -53
- package/lib/formbuilder/objectform/source/geotiffsource.d.ts +3 -20
- package/lib/formbuilder/objectform/source/geotiffsource.js +73 -74
- package/lib/formbuilder/objectform/source/pathbasedsource.d.ts +3 -19
- package/lib/formbuilder/objectform/source/pathbasedsource.js +76 -75
- package/lib/formbuilder/objectform/source/sourceform.d.ts +7 -8
- package/lib/formbuilder/objectform/source/sourceform.js +28 -11
- package/lib/formbuilder/objectform/source/tilesourceform.d.ts +3 -7
- package/lib/formbuilder/objectform/source/tilesourceform.js +63 -53
- package/lib/formbuilder/objectform/useSchemaFormState.d.ts +48 -0
- package/lib/formbuilder/objectform/useSchemaFormState.js +35 -0
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/keybindings.json +10 -10
- package/lib/mainview/mainView.d.ts +11 -7
- package/lib/mainview/mainView.js +62 -35
- package/lib/mainview/mainviewmodel.js +2 -2
- package/lib/menus.js +8 -8
- package/lib/panelview/components/layers.js +5 -2
- package/lib/panelview/filter-panel/Filter.d.ts +3 -0
- package/lib/panelview/filter-panel/Filter.js +9 -9
- package/lib/panelview/leftpanel.js +0 -7
- package/lib/panelview/objectproperties.js +2 -2
- package/lib/panelview/rightpanel.js +17 -2
- package/lib/panelview/story-maps/SpectaPanel.d.ts +15 -0
- package/lib/panelview/story-maps/SpectaPanel.js +35 -0
- package/lib/panelview/story-maps/StoryViewerPanel.d.ts +24 -10
- package/lib/panelview/story-maps/StoryViewerPanel.js +22 -268
- package/lib/panelview/story-maps/{PreviewModeSwitch.js → components/PreviewModeSwitch.js} +1 -1
- package/lib/panelview/story-maps/components/SpectaDesktopView.d.ts +22 -0
- package/lib/panelview/story-maps/components/SpectaDesktopView.js +49 -0
- package/lib/panelview/story-maps/components/SpectaMobileView.d.ts +17 -0
- package/lib/panelview/story-maps/{MobileSpectaPanel.js → components/SpectaMobileView.js} +8 -22
- package/lib/panelview/story-maps/{StoryNavBar.d.ts → components/StoryNavBar.d.ts} +1 -1
- package/lib/panelview/story-maps/{StoryNavBar.js → components/StoryNavBar.js} +2 -4
- package/lib/panelview/story-maps/hooks/useStoryMap.d.ts +40 -0
- package/lib/panelview/story-maps/hooks/useStoryMap.js +255 -0
- package/lib/processing/index.d.ts +2 -2
- package/lib/processing/index.js +62 -35
- package/lib/processing/processingCommands.d.ts +1 -1
- package/lib/processing/processingCommands.js +26 -6
- package/lib/shared/components/Collapsible.d.ts +6 -0
- package/lib/shared/components/Collapsible.js +26 -0
- package/lib/stacBrowser/components/filter-extension/QueryableComboBox.js +1 -3
- package/lib/statusbar/SpectaPresentationProgressBar.d.ts +7 -0
- package/lib/statusbar/SpectaPresentationProgressBar.js +40 -0
- package/lib/toolbar/widget.js +4 -2
- package/lib/tools.d.ts +6 -0
- package/lib/tools.js +9 -0
- package/lib/types.d.ts +30 -3
- package/package.json +2 -4
- package/style/base.css +23 -1
- package/style/dialog.css +5 -0
- package/style/leftPanel.css +14 -37
- package/style/shared/button.css +2 -15
- package/style/shared/input.css +2 -2
- package/style/shared/switch.css +8 -7
- package/style/spectaProgressBar.css +144 -0
- package/style/storyPanel.css +38 -0
- package/style/symbologyDialog.css +2 -2
- package/lib/formbuilder/objectform/baseform.d.ts +0 -91
- package/lib/formbuilder/objectform/baseform.js +0 -231
- package/lib/panelview/story-maps/MobileSpectaPanel.d.ts +0 -7
- /package/lib/panelview/story-maps/{PreviewModeSwitch.d.ts → components/PreviewModeSwitch.d.ts} +0 -0
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The form to modify a PathBasedSource source.
|
|
6
|
-
*/
|
|
7
|
-
export declare class PathBasedSourcePropertiesForm extends SourcePropertiesForm {
|
|
8
|
-
constructor(props: ISourceFormProps);
|
|
9
|
-
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
10
|
-
protected onFormBlur(id: string, value: any): void;
|
|
11
|
-
protected onFormChange(e: IChangeEvent): void;
|
|
12
|
-
protected onFormSubmit(e: ISubmitEvent<any>): void;
|
|
13
|
-
/**
|
|
14
|
-
* Validate the path, to avoid invalid path.
|
|
15
|
-
*
|
|
16
|
-
* @param path - the path to validate.
|
|
17
|
-
*/
|
|
18
|
-
protected _validatePath(path: string): Promise<void>;
|
|
19
|
-
}
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ISourceFormProps } from './sourceform';
|
|
3
|
+
export declare function PathBasedSourcePropertiesForm(props: ISourceFormProps): React.ReactElement | null;
|
|
@@ -1,70 +1,29 @@
|
|
|
1
1
|
import { showErrorMessage } from '@jupyterlab/apputils';
|
|
2
|
-
import {
|
|
3
|
-
import { loadFile } from "../../../tools";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
uiSchema.path = {
|
|
26
|
-
'ui:widget': FileSelectorWidget,
|
|
27
|
-
'ui:options': {
|
|
28
|
-
docManager,
|
|
29
|
-
formOptions: this.props,
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
// This is not user-editable
|
|
34
|
-
delete schema.properties.valid;
|
|
35
|
-
}
|
|
36
|
-
onFormBlur(id, value) {
|
|
37
|
-
// Is there a better way to spot the path text entry?
|
|
38
|
-
if (!id.endsWith('_path')) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
this._validatePath(value);
|
|
42
|
-
}
|
|
43
|
-
// we need to use `onFormChange` instead of `onFormBlur` because it's no longer a text field
|
|
44
|
-
onFormChange(e) {
|
|
45
|
-
var _a;
|
|
46
|
-
super.onFormChange(e);
|
|
47
|
-
if (((_a = e.formData) === null || _a === void 0 ? void 0 : _a.path) !== undefined) {
|
|
48
|
-
this._validatePath(e.formData.path);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
onFormSubmit(e) {
|
|
52
|
-
var _a, _b, _c;
|
|
53
|
-
if (((_c = (_b = (_a = this.state.extraErrors) === null || _a === void 0 ? void 0 : _a.path) === null || _b === void 0 ? void 0 : _b.__errors) === null || _c === void 0 ? void 0 : _c.length) >= 1) {
|
|
54
|
-
showErrorMessage('Invalid file', this.state.extraErrors.path.__errors[0]);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
super.onFormSubmit(e);
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Validate the path, to avoid invalid path.
|
|
61
|
-
*
|
|
62
|
-
* @param path - the path to validate.
|
|
63
|
-
*/
|
|
64
|
-
async _validatePath(path) {
|
|
65
|
-
const extraErrors = this.state.extraErrors;
|
|
66
|
-
let error = '';
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { deepCopy, loadFile } from "../../../tools";
|
|
4
|
+
import { SchemaForm } from '../SchemaForm';
|
|
5
|
+
import { FileSelectorWidget } from '../fileselectorwidget';
|
|
6
|
+
import { processBaseSchema, removeFormEntry } from '../schemaUtils';
|
|
7
|
+
import { useSchemaFormState } from '../useSchemaFormState';
|
|
8
|
+
export function PathBasedSourcePropertiesForm(props) {
|
|
9
|
+
const { schema: schemaProp, sourceData, syncData, model, filePath, formContext, sourceType, dialogOptions, formErrorSignal, formSchemaRegistry, cancel, } = props;
|
|
10
|
+
const { formData, formContextValue, hasSchema, handleChangeBase, handleSubmitBase, } = useSchemaFormState({
|
|
11
|
+
sourceData,
|
|
12
|
+
schemaProp,
|
|
13
|
+
model,
|
|
14
|
+
syncData,
|
|
15
|
+
cancel,
|
|
16
|
+
onAfterChange: dialogOptions
|
|
17
|
+
? (data) => {
|
|
18
|
+
dialogOptions.sourceData = Object.assign({}, data);
|
|
19
|
+
}
|
|
20
|
+
: undefined,
|
|
21
|
+
});
|
|
22
|
+
const [extraErrors, setExtraErrors] = useState({});
|
|
23
|
+
const validatePath = useCallback(async (path) => {
|
|
24
|
+
const nextErrors = {};
|
|
67
25
|
let valid = true;
|
|
26
|
+
let error = '';
|
|
68
27
|
if (!path) {
|
|
69
28
|
valid = false;
|
|
70
29
|
error = 'Path is required';
|
|
@@ -73,26 +32,68 @@ export class PathBasedSourcePropertiesForm extends SourcePropertiesForm {
|
|
|
73
32
|
try {
|
|
74
33
|
await loadFile({
|
|
75
34
|
filepath: path,
|
|
76
|
-
type:
|
|
77
|
-
model
|
|
35
|
+
type: sourceType,
|
|
36
|
+
model,
|
|
78
37
|
});
|
|
79
38
|
}
|
|
80
39
|
catch (e) {
|
|
81
40
|
valid = false;
|
|
82
|
-
error = `"${path}" is not a valid ${
|
|
41
|
+
error = `"${path}" is not a valid ${sourceType} file.`;
|
|
83
42
|
}
|
|
84
43
|
}
|
|
85
44
|
if (!valid) {
|
|
86
|
-
|
|
87
|
-
__errors: [error],
|
|
88
|
-
};
|
|
89
|
-
this.setState(old => (Object.assign(Object.assign({}, old), { extraErrors })));
|
|
45
|
+
nextErrors.path = { __errors: [error] };
|
|
90
46
|
}
|
|
91
47
|
else {
|
|
92
|
-
|
|
48
|
+
nextErrors.path = { __errors: [] };
|
|
93
49
|
}
|
|
94
|
-
|
|
95
|
-
|
|
50
|
+
setExtraErrors(nextErrors);
|
|
51
|
+
formErrorSignal === null || formErrorSignal === void 0 ? void 0 : formErrorSignal.emit(!valid);
|
|
52
|
+
}, [model, sourceType, formErrorSignal]);
|
|
53
|
+
const schema = useMemo(() => {
|
|
54
|
+
const schemaCopy = deepCopy(schemaProp !== null && schemaProp !== void 0 ? schemaProp : {});
|
|
55
|
+
if (schemaCopy.properties) {
|
|
56
|
+
delete schemaCopy.properties.valid;
|
|
96
57
|
}
|
|
58
|
+
return schemaCopy;
|
|
59
|
+
}, [schemaProp]);
|
|
60
|
+
const uiSchema = useMemo(() => {
|
|
61
|
+
var _a;
|
|
62
|
+
const builtUiSchema = {};
|
|
63
|
+
const dataCopy = deepCopy(formData);
|
|
64
|
+
processBaseSchema(dataCopy, schema, builtUiSchema, formContext, removeFormEntry);
|
|
65
|
+
const docManager = formSchemaRegistry === null || formSchemaRegistry === void 0 ? void 0 : formSchemaRegistry.getDocManager();
|
|
66
|
+
if (((_a = schema.properties) === null || _a === void 0 ? void 0 : _a.path) && docManager) {
|
|
67
|
+
builtUiSchema.path = {
|
|
68
|
+
'ui:widget': FileSelectorWidget,
|
|
69
|
+
'ui:options': {
|
|
70
|
+
docManager,
|
|
71
|
+
formOptions: props,
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return builtUiSchema;
|
|
76
|
+
}, [schema, formData, formContext, formSchemaRegistry, props]);
|
|
77
|
+
const handleChange = useCallback((data) => {
|
|
78
|
+
handleChangeBase(data);
|
|
79
|
+
if (data.path !== undefined) {
|
|
80
|
+
validatePath(data.path);
|
|
81
|
+
}
|
|
82
|
+
}, [handleChangeBase, validatePath]);
|
|
83
|
+
const handleSubmit = useCallback((data) => {
|
|
84
|
+
var _a, _b;
|
|
85
|
+
if (((_b = (_a = extraErrors === null || extraErrors === void 0 ? void 0 : extraErrors.path) === null || _a === void 0 ? void 0 : _a.__errors) === null || _b === void 0 ? void 0 : _b.length) >= 1) {
|
|
86
|
+
showErrorMessage('Invalid file', extraErrors.path.__errors[0]);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
handleSubmitBase(data);
|
|
90
|
+
}, [extraErrors, handleSubmitBase]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
var _a;
|
|
93
|
+
validatePath((_a = formData === null || formData === void 0 ? void 0 : formData.path) !== null && _a !== void 0 ? _a : '');
|
|
94
|
+
}, []);
|
|
95
|
+
if (!hasSchema) {
|
|
96
|
+
return null;
|
|
97
97
|
}
|
|
98
|
+
return (React.createElement(SchemaForm, { schema: schema, formData: formData, onChange: handleChange, onSubmit: handleSubmit, formContext: formContextValue, filePath: filePath, uiSchema: uiSchema, extraErrors: extraErrors, formErrorSignal: formErrorSignal }));
|
|
98
99
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base (default) source form and props.
|
|
3
|
+
* Used for RasterSource and any source type without a dedicated form.
|
|
4
|
+
*/
|
|
1
5
|
import { IDict, SourceType } from '@jupytergis/schema';
|
|
2
6
|
import { Signal } from '@lumino/signaling';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import type { IBaseFormProps } from "../../../types";
|
|
5
9
|
export interface ISourceFormProps extends IBaseFormProps {
|
|
6
10
|
/**
|
|
7
11
|
* The source type for this form.
|
|
@@ -16,9 +20,4 @@ export interface ISourceFormProps extends IBaseFormProps {
|
|
|
16
20
|
*/
|
|
17
21
|
dialogOptions?: any;
|
|
18
22
|
}
|
|
19
|
-
export declare
|
|
20
|
-
props: ISourceFormProps;
|
|
21
|
-
protected sourceFormChangedSignal: Signal<any, IDict<any>> | undefined;
|
|
22
|
-
constructor(props: ISourceFormProps);
|
|
23
|
-
protected onFormChange(e: IChangeEvent): void;
|
|
24
|
-
}
|
|
23
|
+
export declare function SourcePropertiesForm(props: ISourceFormProps): React.ReactElement | null;
|
|
@@ -1,13 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { deepCopy } from "../../../tools";
|
|
3
|
+
import { SchemaForm } from '../SchemaForm';
|
|
4
|
+
import { processBaseSchema, removeFormEntry } from '../schemaUtils';
|
|
5
|
+
import { useSchemaFormState } from '../useSchemaFormState';
|
|
6
|
+
export function SourcePropertiesForm(props) {
|
|
7
|
+
const { schema: schemaProp, sourceData, syncData, model, filePath, formContext, dialogOptions, cancel, formErrorSignal, } = props;
|
|
8
|
+
const { formData, schema, formContextValue, hasSchema, handleChangeBase, handleSubmitBase, } = useSchemaFormState({
|
|
9
|
+
sourceData,
|
|
10
|
+
schemaProp,
|
|
11
|
+
model,
|
|
12
|
+
syncData,
|
|
13
|
+
cancel,
|
|
14
|
+
onAfterChange: dialogOptions
|
|
15
|
+
? (data) => {
|
|
16
|
+
dialogOptions.sourceData = Object.assign({}, data);
|
|
17
|
+
}
|
|
18
|
+
: undefined,
|
|
19
|
+
});
|
|
20
|
+
const uiSchema = useMemo(() => {
|
|
21
|
+
const builtUiSchema = {};
|
|
22
|
+
const dataCopy = deepCopy(formData);
|
|
23
|
+
processBaseSchema(dataCopy, schema, builtUiSchema, formContext, removeFormEntry);
|
|
24
|
+
return builtUiSchema;
|
|
25
|
+
}, [schema, formData, formContext]);
|
|
26
|
+
if (!hasSchema) {
|
|
27
|
+
return null;
|
|
12
28
|
}
|
|
29
|
+
return (React.createElement(SchemaForm, { schema: schema, formData: formData, onChange: handleChangeBase, onSubmit: handleSubmitBase, formContext: formContextValue, filePath: filePath, uiSchema: uiSchema, formErrorSignal: formErrorSignal }));
|
|
13
30
|
}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
export declare
|
|
4
|
-
private _urlParameters;
|
|
5
|
-
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
6
|
-
protected onFormBlur(id: string, value: any): void;
|
|
7
|
-
}
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ISourceFormProps } from './sourceform';
|
|
3
|
+
export declare function TileSourcePropertiesForm(props: ISourceFormProps): React.ReactElement | null;
|
|
@@ -1,60 +1,70 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { deepCopy } from "../../../tools";
|
|
3
|
+
import { SchemaForm } from '../SchemaForm';
|
|
4
|
+
import { processBaseSchema, removeFormEntry } from '../schemaUtils';
|
|
5
|
+
import { useSchemaFormState } from '../useSchemaFormState';
|
|
6
|
+
function getUrlParameters(url) {
|
|
7
|
+
if (!url || typeof url !== 'string') {
|
|
8
|
+
return [];
|
|
6
9
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
const regex = /\{([^}]+)\}/g;
|
|
11
|
+
const matches = [];
|
|
12
|
+
let match;
|
|
13
|
+
while ((match = regex.exec(url)) !== null) {
|
|
14
|
+
if (['max_zoom', 'min_zoom', 'x', 'y', 'z'].includes(match[1])) {
|
|
15
|
+
continue;
|
|
11
16
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
matches.push(match[1]);
|
|
18
|
+
}
|
|
19
|
+
return matches;
|
|
20
|
+
}
|
|
21
|
+
export function TileSourcePropertiesForm(props) {
|
|
22
|
+
const { schema: schemaProp, sourceData, syncData, model, filePath, formContext, dialogOptions, cancel, formErrorSignal, } = props;
|
|
23
|
+
const { formData, schema, formContextValue, hasSchema, handleChangeBase, handleSubmitBase, } = useSchemaFormState({
|
|
24
|
+
sourceData,
|
|
25
|
+
schemaProp,
|
|
26
|
+
model,
|
|
27
|
+
syncData,
|
|
28
|
+
cancel,
|
|
29
|
+
onAfterChange: dialogOptions
|
|
30
|
+
? (data) => {
|
|
31
|
+
dialogOptions.sourceData = Object.assign({}, data);
|
|
19
32
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
33
|
+
: undefined,
|
|
34
|
+
});
|
|
35
|
+
const uiSchema = useMemo(() => {
|
|
36
|
+
const builtUiSchema = {};
|
|
37
|
+
const dataCopy = deepCopy(formData);
|
|
38
|
+
processBaseSchema(dataCopy, schema, builtUiSchema, formContext, removeFormEntry);
|
|
39
|
+
if (schema.properties && (formData === null || formData === void 0 ? void 0 : formData.url)) {
|
|
40
|
+
const urlParams = getUrlParameters(formData.url);
|
|
41
|
+
if (urlParams.length === 0) {
|
|
42
|
+
removeFormEntry('urlParameters', dataCopy, schema, builtUiSchema);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const propertiesSchema = {};
|
|
46
|
+
schema.properties.urlParameters = {
|
|
47
|
+
type: 'object',
|
|
48
|
+
required: urlParams,
|
|
49
|
+
properties: propertiesSchema,
|
|
50
|
+
};
|
|
51
|
+
for (const parameterName of urlParams) {
|
|
52
|
+
if (parameterName === 'time') {
|
|
53
|
+
propertiesSchema[parameterName] = {
|
|
54
|
+
type: 'string',
|
|
55
|
+
format: 'date',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
propertiesSchema[parameterName] = { type: 'string' };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
48
62
|
}
|
|
49
63
|
}
|
|
64
|
+
return builtUiSchema;
|
|
65
|
+
}, [schema, formData, formContext]);
|
|
66
|
+
if (!hasSchema) {
|
|
67
|
+
return null;
|
|
50
68
|
}
|
|
51
|
-
|
|
52
|
-
super.onFormBlur(id, value);
|
|
53
|
-
// Is there a better way to spot the url text entry?
|
|
54
|
-
if (!id.endsWith('_url')) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
// Force a rerender on url change, as it probably changes the schema
|
|
58
|
-
this.forceUpdate();
|
|
59
|
-
}
|
|
69
|
+
return (React.createElement(SchemaForm, { schema: schema, formData: formData, onChange: handleChangeBase, onSubmit: handleSubmitBase, formContext: formContextValue, filePath: filePath, uiSchema: uiSchema, formErrorSignal: formErrorSignal }));
|
|
60
70
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared state for schema-based forms: form data synced from sourceData,
|
|
3
|
+
* copied schema, form context, and optional base change/submit handlers.
|
|
4
|
+
* Forms use handleChangeBase/handleSubmitBase directly or wrap them for
|
|
5
|
+
* validation and extra logic.
|
|
6
|
+
*/
|
|
7
|
+
import { IJupyterGISModel } from '@jupytergis/schema';
|
|
8
|
+
import { RJSFSchema } from '@rjsf/utils';
|
|
9
|
+
import { type Dispatch, type SetStateAction } from 'react';
|
|
10
|
+
import { IDict } from "../../types";
|
|
11
|
+
export interface IUseSchemaFormStateProps {
|
|
12
|
+
/** External data to sync into the form (e.g. layer/source parameters). */
|
|
13
|
+
sourceData: IDict | undefined;
|
|
14
|
+
/** JSON Schema for the form (will be deep-copied). */
|
|
15
|
+
schemaProp: IDict | undefined;
|
|
16
|
+
/** Current JGIS model. */
|
|
17
|
+
model: IJupyterGISModel;
|
|
18
|
+
/** Called when form data changes (enables handleChangeBase). */
|
|
19
|
+
syncData?: (data: IDict) => void;
|
|
20
|
+
/** Called when form is submitted (enables handleSubmitBase). */
|
|
21
|
+
cancel?: () => void;
|
|
22
|
+
/** Optional side effect after change (e.g. update dialogOptions.layerData). */
|
|
23
|
+
onAfterChange?: (data: IDict) => void;
|
|
24
|
+
}
|
|
25
|
+
export interface IUseSchemaFormStateResult {
|
|
26
|
+
/** Current form data (controlled state). */
|
|
27
|
+
formData: IDict;
|
|
28
|
+
/** Set form data (e.g. from onChange). */
|
|
29
|
+
setFormData: Dispatch<SetStateAction<IDict>>;
|
|
30
|
+
/** Schema to pass to SchemaForm (deep copy of schemaProp). */
|
|
31
|
+
schema: RJSFSchema;
|
|
32
|
+
/** Form context value { model, formData } for SchemaForm. */
|
|
33
|
+
formContextValue: {
|
|
34
|
+
model: IJupyterGISModel;
|
|
35
|
+
formData: IDict;
|
|
36
|
+
};
|
|
37
|
+
/** Whether the form has a schema (false => form may render null). */
|
|
38
|
+
hasSchema: boolean;
|
|
39
|
+
/** Base change handler: setFormData, syncData, onAfterChange. Use or wrap. */
|
|
40
|
+
handleChangeBase: (data: IDict) => void;
|
|
41
|
+
/** Base submit handler: syncData(data), cancel?.(). Use or wrap. */
|
|
42
|
+
handleSubmitBase: (data: IDict) => void;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Manages form data state, schema copy, and form context for object forms.
|
|
46
|
+
* Callers supply uiSchema, handleChange, handleSubmit, and any validation.
|
|
47
|
+
*/
|
|
48
|
+
export declare function useSchemaFormState(props: IUseSchemaFormStateProps): IUseSchemaFormStateResult;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState, } from 'react';
|
|
2
|
+
import { deepCopy } from "../../tools";
|
|
3
|
+
/**
|
|
4
|
+
* Manages form data state, schema copy, and form context for object forms.
|
|
5
|
+
* Callers supply uiSchema, handleChange, handleSubmit, and any validation.
|
|
6
|
+
*/
|
|
7
|
+
export function useSchemaFormState(props) {
|
|
8
|
+
const { sourceData, schemaProp, model, syncData, cancel, onAfterChange } = props;
|
|
9
|
+
const [formData, setFormData] = useState(() => deepCopy(sourceData !== null && sourceData !== void 0 ? sourceData : {}));
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (sourceData !== undefined) {
|
|
12
|
+
setFormData(deepCopy(sourceData !== null && sourceData !== void 0 ? sourceData : {}));
|
|
13
|
+
}
|
|
14
|
+
}, [sourceData]);
|
|
15
|
+
const schema = useMemo(() => deepCopy(schemaProp !== null && schemaProp !== void 0 ? schemaProp : {}), [schemaProp]);
|
|
16
|
+
const formContextValue = useMemo(() => ({ model, formData }), [model, formData]);
|
|
17
|
+
const handleChangeBase = useCallback((data) => {
|
|
18
|
+
setFormData(data);
|
|
19
|
+
syncData === null || syncData === void 0 ? void 0 : syncData(data);
|
|
20
|
+
onAfterChange === null || onAfterChange === void 0 ? void 0 : onAfterChange(data);
|
|
21
|
+
}, [syncData, onAfterChange]);
|
|
22
|
+
const handleSubmitBase = useCallback((data) => {
|
|
23
|
+
syncData === null || syncData === void 0 ? void 0 : syncData(data);
|
|
24
|
+
cancel === null || cancel === void 0 ? void 0 : cancel();
|
|
25
|
+
}, [syncData, cancel]);
|
|
26
|
+
return {
|
|
27
|
+
formData,
|
|
28
|
+
setFormData,
|
|
29
|
+
schema,
|
|
30
|
+
formContextValue,
|
|
31
|
+
hasSchema: schemaProp !== null,
|
|
32
|
+
handleChangeBase,
|
|
33
|
+
handleSubmitBase,
|
|
34
|
+
};
|
|
35
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ export * from './annotations';
|
|
|
2
2
|
export * from './commands/index';
|
|
3
3
|
export * from './constants';
|
|
4
4
|
export * from './dialogs/layerCreationFormDialog';
|
|
5
|
-
export * from './formbuilder/objectform/baseform';
|
|
6
5
|
export * from './icons';
|
|
7
6
|
export * from './mainview';
|
|
8
7
|
export * from './menus';
|
package/lib/index.js
CHANGED
|
@@ -2,7 +2,6 @@ export * from './annotations';
|
|
|
2
2
|
export * from './commands/index';
|
|
3
3
|
export * from './constants';
|
|
4
4
|
export * from './dialogs/layerCreationFormDialog';
|
|
5
|
-
export * from './formbuilder/objectform/baseform';
|
|
6
5
|
export * from './icons';
|
|
7
6
|
export * from './mainview';
|
|
8
7
|
export * from './menus';
|
package/lib/keybindings.json
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"command": "jupytergis:storyPrev",
|
|
4
|
+
"keys": ["ArrowLeft"],
|
|
5
|
+
"selector": ".data-jgis-keybinding"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"command": "jupytergis:storyNext",
|
|
9
|
+
"keys": ["ArrowRight"],
|
|
10
|
+
"selector": ".data-jgis-keybinding"
|
|
11
|
+
},
|
|
2
12
|
{
|
|
3
13
|
"command": "jupytergis:undo",
|
|
4
14
|
"keys": ["Accel Z"],
|
|
@@ -19,16 +29,6 @@
|
|
|
19
29
|
"keys": ["Accel I"],
|
|
20
30
|
"selector": ".data-jgis-keybinding"
|
|
21
31
|
},
|
|
22
|
-
{
|
|
23
|
-
"command": "jupytergis:removeSource",
|
|
24
|
-
"keys": ["Delete"],
|
|
25
|
-
"selector": ".data-jgis-keybinding .jp-gis-source.jp-gis-sourceUnused"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"command": "jupytergis:renameSource",
|
|
29
|
-
"keys": ["F2"],
|
|
30
|
-
"selector": ".data-jgis-keybinding .jp-gis-source"
|
|
31
|
-
},
|
|
32
32
|
{
|
|
33
33
|
"command": "jupytergis:removeSelected",
|
|
34
34
|
"keys": ["Delete"],
|
|
@@ -5,13 +5,13 @@ import { Layer } from 'ol/layer';
|
|
|
5
5
|
import * as React from 'react';
|
|
6
6
|
import { ClientPointer } from './CollaboratorPointers';
|
|
7
7
|
import { MainViewModel } from './mainviewmodel';
|
|
8
|
-
interface
|
|
8
|
+
interface IMainViewProps {
|
|
9
9
|
viewModel: MainViewModel;
|
|
10
10
|
state?: IStateDB;
|
|
11
11
|
formSchemaRegistry?: IJGISFormSchemaRegistry;
|
|
12
12
|
annotationModel?: IAnnotationModel;
|
|
13
13
|
/** True when viewport matches (max-width: 768px). Injected by MainViewWithMediaQuery. */
|
|
14
|
-
isMobile
|
|
14
|
+
isMobile: boolean;
|
|
15
15
|
}
|
|
16
16
|
interface IStates {
|
|
17
17
|
id: string;
|
|
@@ -35,11 +35,12 @@ interface IStates {
|
|
|
35
35
|
filterStates: IDict<IJGISFilterItem | undefined>;
|
|
36
36
|
jgisSettings: IJupyterGISSettings;
|
|
37
37
|
isSpectaPresentation: boolean;
|
|
38
|
+
initialLayersReady: boolean;
|
|
38
39
|
}
|
|
39
|
-
export declare class MainView extends React.Component<
|
|
40
|
-
constructor(props:
|
|
40
|
+
export declare class MainView extends React.Component<IMainViewProps, IStates> {
|
|
41
|
+
constructor(props: IMainViewProps);
|
|
41
42
|
componentDidMount(): Promise<void>;
|
|
42
|
-
componentDidUpdate(prevProps:
|
|
43
|
+
componentDidUpdate(prevProps: IMainViewProps, prevState: IStates): void;
|
|
43
44
|
componentWillUnmount(): void;
|
|
44
45
|
generateMap(center: number[], zoom: number): Promise<void>;
|
|
45
46
|
updateCenter: () => void;
|
|
@@ -182,7 +183,6 @@ export declare class MainView extends React.Component<IProps, IStates> {
|
|
|
182
183
|
private _removeAllInteractions;
|
|
183
184
|
private _setupStoryScrollListener;
|
|
184
185
|
private _cleanupStoryScrollListener;
|
|
185
|
-
private _updateSpectaPresentationColors;
|
|
186
186
|
private _onSharedMetadataChanged;
|
|
187
187
|
private _computeAnnotationPosition;
|
|
188
188
|
private _updateAnnotation;
|
|
@@ -198,6 +198,8 @@ export declare class MainView extends React.Component<IProps, IStates> {
|
|
|
198
198
|
private _handleGeolocationChanged;
|
|
199
199
|
private _handleThemeChange;
|
|
200
200
|
private _handleWindowResize;
|
|
201
|
+
private _handleSpectaTouchStart;
|
|
202
|
+
private _handleSpectaTouchEnd;
|
|
201
203
|
render(): JSX.Element;
|
|
202
204
|
private _clickCoords;
|
|
203
205
|
private _commands;
|
|
@@ -227,7 +229,9 @@ export declare class MainView extends React.Component<IProps, IStates> {
|
|
|
227
229
|
private _storyScrollHandler;
|
|
228
230
|
private _clearStoryScrollGuard;
|
|
229
231
|
private _pendingStoryScrollRafId;
|
|
232
|
+
private _initialLayersCount;
|
|
233
|
+
private _spectaTouchStartX;
|
|
230
234
|
}
|
|
231
235
|
/** Thin wrapper that injects isMobile from useMediaQuery so MainView can use it in JSX. */
|
|
232
|
-
declare function MainViewWithMediaQuery(props:
|
|
236
|
+
declare function MainViewWithMediaQuery(props: Omit<IMainViewProps, 'isMobile'>): React.JSX.Element;
|
|
233
237
|
export { MainViewWithMediaQuery };
|