@jupytergis/base 0.13.2 → 0.14.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/BaseCommandIDs.d.ts +14 -13
- package/lib/commands/BaseCommandIDs.js +14 -14
- package/lib/commands/index.js +528 -130
- package/lib/commands/operationCommands.d.ts +22 -0
- package/lib/commands/operationCommands.js +305 -0
- package/lib/constants.js +11 -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/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 +63 -36
- package/lib/mainview/mainviewmodel.js +2 -2
- package/lib/menus.js +8 -8
- package/lib/panelview/components/layers.js +5 -2
- 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 -9
- 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 +21 -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 +39 -0
- package/lib/panelview/story-maps/hooks/useStoryMap.js +252 -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/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 +29 -2
- package/lib/widget.js +14 -2
- 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 +0 -10
- package/style/shared/switch.css +8 -7
- package/style/spectaProgressBar.css +144 -0
- package/style/storyPanel.css +33 -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,62 +1,74 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { loadFile } from "../../../tools";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
this.state = {
|
|
11
|
-
schema: (_a = options.schema) !== null && _a !== void 0 ? _a : {}, // Ensure schema is never undefined
|
|
12
|
-
};
|
|
13
|
-
this.onFormChange = this.handleFormChange.bind(this);
|
|
14
|
-
this.fetchFieldNames(options.sourceData.inputLayer);
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { deepCopy, loadFile } from "../../../tools";
|
|
3
|
+
import { SchemaForm } from '../SchemaForm';
|
|
4
|
+
import { processBaseSchema, removeFormEntry } from '../schemaUtils';
|
|
5
|
+
import { useSchemaFormState } from '../useSchemaFormState';
|
|
6
|
+
async function fetchFieldNames(model, layerId) {
|
|
7
|
+
var _a, _b, _c;
|
|
8
|
+
if (!layerId) {
|
|
9
|
+
return [];
|
|
15
10
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
11
|
+
const layer = model.getLayer(layerId);
|
|
12
|
+
if (!((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.source)) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const source = model.getSource(layer.parameters.source);
|
|
16
|
+
if (!source || source.type !== 'GeoJSONSource') {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
const sourceData = source.parameters;
|
|
20
|
+
if (!(sourceData === null || sourceData === void 0 ? void 0 : sourceData.path)) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
const jsonData = await loadFile({
|
|
24
|
+
filepath: sourceData.path,
|
|
25
|
+
type: 'GeoJSONSource',
|
|
26
|
+
model,
|
|
27
|
+
});
|
|
28
|
+
if (!((_b = jsonData === null || jsonData === void 0 ? void 0 : jsonData.features) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
return Object.keys((_c = jsonData.features[0].properties) !== null && _c !== void 0 ? _c : {});
|
|
32
|
+
}
|
|
33
|
+
export function DissolveForm(props) {
|
|
34
|
+
const { schema: schemaProp, sourceData, syncData, model, filePath, formContext, ok, } = props;
|
|
35
|
+
const { formData, formContextValue, hasSchema, handleChangeBase, handleSubmitBase, } = useSchemaFormState({ sourceData, schemaProp, model, syncData });
|
|
36
|
+
const [features, setFeatures] = useState([]);
|
|
37
|
+
const submitButtonRef = useRef(null);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
fetchFieldNames(model, formData === null || formData === void 0 ? void 0 : formData.inputLayer)
|
|
40
|
+
.then(setFeatures)
|
|
41
|
+
.catch(() => setFeatures([]));
|
|
42
|
+
}, [model, formData === null || formData === void 0 ? void 0 : formData.inputLayer]);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (!ok) {
|
|
28
45
|
return;
|
|
29
46
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
handleFormChange(e) {
|
|
47
|
-
super.onFormChange(e);
|
|
48
|
-
if (e.formData.inputLayer) {
|
|
49
|
-
this.fetchFieldNames(e.formData.inputLayer);
|
|
47
|
+
const handler = () => {
|
|
48
|
+
var _a;
|
|
49
|
+
(_a = submitButtonRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
50
|
+
};
|
|
51
|
+
ok.connect(handler);
|
|
52
|
+
return () => {
|
|
53
|
+
ok.disconnect(handler);
|
|
54
|
+
};
|
|
55
|
+
}, [ok]);
|
|
56
|
+
const schema = useMemo(() => {
|
|
57
|
+
const schemaCopy = deepCopy(schemaProp !== null && schemaProp !== void 0 ? schemaProp : {});
|
|
58
|
+
if (schemaCopy.properties &&
|
|
59
|
+
schemaCopy.properties.dissolveField) {
|
|
60
|
+
schemaCopy.properties.dissolveField = Object.assign(Object.assign({}, schemaCopy.properties.dissolveField), { enum: [...features] });
|
|
50
61
|
}
|
|
62
|
+
return schemaCopy;
|
|
63
|
+
}, [schemaProp, features]);
|
|
64
|
+
const uiSchema = useMemo(() => {
|
|
65
|
+
const builtUiSchema = {};
|
|
66
|
+
const dataCopy = deepCopy(formData);
|
|
67
|
+
processBaseSchema(dataCopy, schema, builtUiSchema, formContext, removeFormEntry);
|
|
68
|
+
return builtUiSchema;
|
|
69
|
+
}, [schema, formData, formContext]);
|
|
70
|
+
if (!hasSchema) {
|
|
71
|
+
return null;
|
|
51
72
|
}
|
|
52
|
-
|
|
53
|
-
this.setState((prevState) => {
|
|
54
|
-
var _a, _b, _c;
|
|
55
|
-
return ({
|
|
56
|
-
schema: Object.assign(Object.assign({}, prevState.schema), { properties: Object.assign(Object.assign({}, (_a = prevState.schema) === null || _a === void 0 ? void 0 : _a.properties), { dissolveField: Object.assign(Object.assign({}, (_c = (_b = prevState.schema) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.dissolveField), { enum: [...this.features] }) }) }),
|
|
57
|
-
});
|
|
58
|
-
}, () => {
|
|
59
|
-
this.forceUpdate();
|
|
60
|
-
});
|
|
61
|
-
}
|
|
73
|
+
return (React.createElement(SchemaForm, { schema: schema, formData: formData, onChange: handleChangeBase, onSubmit: handleSubmitBase, formContext: formContextValue, filePath: filePath, uiSchema: uiSchema, submitButtonRef: submitButtonRef }));
|
|
62
74
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Used by ProcessingFormDialog when processingType is not 'Dissolve'.
|
|
3
|
+
*/
|
|
4
|
+
import { Dialog } from '@jupyterlab/apputils';
|
|
5
|
+
import { Signal } from '@lumino/signaling';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import type { IBaseFormProps } from "../../types";
|
|
8
|
+
export interface IProcessingFormWrapperProps extends IBaseFormProps {
|
|
9
|
+
/** Signal emitted by the dialog when OK is clicked; form submits when this fires. */
|
|
10
|
+
ok?: Signal<Dialog<any>, number>;
|
|
11
|
+
}
|
|
12
|
+
export declare function DefaultProcessingForm(props: IProcessingFormWrapperProps): React.ReactElement | null;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useRef } 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 DefaultProcessingForm(props) {
|
|
7
|
+
const { schema: schemaProp, sourceData, syncData, model, filePath, formContext, ok, } = props;
|
|
8
|
+
const { formData, schema, formContextValue, hasSchema, handleChangeBase, handleSubmitBase, } = useSchemaFormState({ sourceData, schemaProp, model, syncData });
|
|
9
|
+
const submitButtonRef = useRef(null);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (!ok) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const handler = () => {
|
|
15
|
+
var _a;
|
|
16
|
+
(_a = submitButtonRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
17
|
+
};
|
|
18
|
+
ok.connect(handler);
|
|
19
|
+
return () => {
|
|
20
|
+
ok.disconnect(handler);
|
|
21
|
+
};
|
|
22
|
+
}, [ok]);
|
|
23
|
+
const uiSchema = useMemo(() => {
|
|
24
|
+
const builtUiSchema = {};
|
|
25
|
+
const dataCopy = deepCopy(formData);
|
|
26
|
+
processBaseSchema(dataCopy, schema, builtUiSchema, formContext, removeFormEntry);
|
|
27
|
+
return builtUiSchema;
|
|
28
|
+
}, [schema, formData, formContext]);
|
|
29
|
+
if (!hasSchema) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return (React.createElement(SchemaForm, { schema: schema, formData: formData, onChange: handleChangeBase, onSubmit: handleSubmitBase, formContext: formContextValue, filePath: filePath, uiSchema: uiSchema, submitButtonRef: submitButtonRef }));
|
|
33
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for adapting JSON Schema and uiSchema before passing them to SchemaForm.
|
|
3
|
+
* Used by layer, source, and other object forms to hide fields, set array options,
|
|
4
|
+
* and apply read-only behaviour based on form context (create vs update).
|
|
5
|
+
*/
|
|
6
|
+
import { RJSFSchema, UiSchema } from '@rjsf/utils';
|
|
7
|
+
import { IDict } from "../../types";
|
|
8
|
+
/**
|
|
9
|
+
* Remove a property from form data, schema, and uiSchema.
|
|
10
|
+
*/
|
|
11
|
+
export declare function removeFormEntry(entry: string, data: IDict | undefined, schema: RJSFSchema, uiSchema: UiSchema): void;
|
|
12
|
+
/**
|
|
13
|
+
* Apply base processSchema: array options, opacity field, readOnly handling.
|
|
14
|
+
* Mutates schema, uiSchema, and optionally data (for readOnly removal in update).
|
|
15
|
+
*/
|
|
16
|
+
export declare function processBaseSchema(data: IDict | undefined, schema: RJSFSchema, uiSchema: UiSchema, formContext: 'create' | 'update', removeEntry: typeof removeFormEntry): void;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove a property from form data, schema, and uiSchema.
|
|
3
|
+
*/
|
|
4
|
+
export function removeFormEntry(entry, data, schema, uiSchema) {
|
|
5
|
+
if (data) {
|
|
6
|
+
delete data[entry];
|
|
7
|
+
}
|
|
8
|
+
if (schema.properties) {
|
|
9
|
+
delete schema.properties[entry];
|
|
10
|
+
}
|
|
11
|
+
delete uiSchema[entry];
|
|
12
|
+
if (schema.required && schema.required.includes(entry)) {
|
|
13
|
+
schema.required.splice(schema.required.indexOf(entry), 1);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Apply base processSchema: array options, opacity field, readOnly handling.
|
|
18
|
+
* Mutates schema, uiSchema, and optionally data (for readOnly removal in update).
|
|
19
|
+
*/
|
|
20
|
+
export function processBaseSchema(data, schema, uiSchema, formContext, removeEntry) {
|
|
21
|
+
var _a, _b, _c, _d;
|
|
22
|
+
if (!schema['properties']) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const props = schema.properties;
|
|
26
|
+
for (const [k, v] of Object.entries(props)) {
|
|
27
|
+
uiSchema[k] = (_a = uiSchema[k]) !== null && _a !== void 0 ? _a : {};
|
|
28
|
+
if (v && typeof v === 'object' && v['type'] === 'array') {
|
|
29
|
+
uiSchema[k]['ui:options'] = Object.assign({ orderable: false, removable: false, addable: false }, ((_b = uiSchema[k]['ui:options']) !== null && _b !== void 0 ? _b : {}));
|
|
30
|
+
const items = v['items'];
|
|
31
|
+
if (items &&
|
|
32
|
+
typeof items === 'object' &&
|
|
33
|
+
items['type'] === 'array') {
|
|
34
|
+
uiSchema[k].items = Object.assign({ 'ui:options': {
|
|
35
|
+
orderable: false,
|
|
36
|
+
removable: false,
|
|
37
|
+
addable: false,
|
|
38
|
+
} }, ((_c = uiSchema[k].items) !== null && _c !== void 0 ? _c : {}));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (v &&
|
|
42
|
+
typeof v === 'object' &&
|
|
43
|
+
v['type'] === 'object' &&
|
|
44
|
+
v.properties) {
|
|
45
|
+
processBaseSchema(data === null || data === void 0 ? void 0 : data[k], v, (_d = uiSchema[k]) !== null && _d !== void 0 ? _d : {}, formContext, removeEntry);
|
|
46
|
+
}
|
|
47
|
+
if (k === 'opacity') {
|
|
48
|
+
uiSchema[k]['ui:field'] = 'opacity';
|
|
49
|
+
}
|
|
50
|
+
if (v && typeof v === 'object' && v['readOnly']) {
|
|
51
|
+
if (formContext === 'create') {
|
|
52
|
+
delete v['readOnly'];
|
|
53
|
+
}
|
|
54
|
+
if (formContext === 'update') {
|
|
55
|
+
removeEntry(k, data, schema, uiSchema);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import { ISourceFormProps } from './sourceform';
|
|
5
|
-
/**
|
|
6
|
-
* The form to modify a GeoJSON source.
|
|
7
|
-
*/
|
|
8
|
-
export declare class GeoJSONSourcePropertiesForm extends PathBasedSourcePropertiesForm {
|
|
9
|
-
private _validate;
|
|
10
|
-
constructor(props: ISourceFormProps);
|
|
11
|
-
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
12
|
-
/**
|
|
13
|
-
* Validate the path, to avoid invalid path or invalid GeoJSON.
|
|
14
|
-
*
|
|
15
|
-
* @param path - the path to validate.
|
|
16
|
-
*/
|
|
17
|
-
protected _validatePath(path: string | undefined): Promise<void>;
|
|
18
|
-
protected onFormSubmit(e: ISubmitEvent<any>): void;
|
|
19
|
-
}
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ISourceFormProps } from './sourceform';
|
|
3
|
+
export declare function GeoJSONSourcePropertiesForm(props: ISourceFormProps): React.ReactElement | null;
|
|
@@ -1,81 +1,122 @@
|
|
|
1
1
|
import * as geojson from '@jupytergis/schema/src/schema/geojson.json';
|
|
2
2
|
import { showErrorMessage } from '@jupyterlab/apputils';
|
|
3
3
|
import { Ajv } from 'ajv';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
5
|
+
import { deepCopy, loadFile } from "../../../tools";
|
|
6
|
+
import { SchemaForm } from '../SchemaForm';
|
|
7
|
+
import { FileSelectorWidget } from '../fileselectorwidget';
|
|
8
|
+
import { processBaseSchema, removeFormEntry } from '../schemaUtils';
|
|
9
|
+
import { useSchemaFormState } from '../useSchemaFormState';
|
|
10
|
+
export function GeoJSONSourcePropertiesForm(props) {
|
|
11
|
+
const { schema: schemaProp, sourceData, syncData, model, filePath, formContext, dialogOptions, formErrorSignal, formSchemaRegistry, cancel, } = props;
|
|
12
|
+
const { formData, schema, formContextValue, hasSchema, handleChangeBase, handleSubmitBase, } = useSchemaFormState({
|
|
13
|
+
sourceData,
|
|
14
|
+
schemaProp,
|
|
15
|
+
model,
|
|
16
|
+
syncData,
|
|
17
|
+
cancel,
|
|
18
|
+
onAfterChange: dialogOptions
|
|
19
|
+
? (data) => {
|
|
20
|
+
dialogOptions.sourceData = Object.assign({}, data);
|
|
21
|
+
}
|
|
22
|
+
: undefined,
|
|
23
|
+
});
|
|
24
|
+
const [extraErrors, setExtraErrors] = useState({});
|
|
25
|
+
const validateGeoJSON = useMemo(() => {
|
|
13
26
|
const ajv = new Ajv();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
processSchema(data, schema, uiSchema) {
|
|
18
|
-
this.removeFormEntry('data', data, schema, uiSchema);
|
|
19
|
-
if (this.props.formContext === 'create') {
|
|
20
|
-
schema.properties.path.description =
|
|
21
|
-
'The local path to a GeoJSON file. (If no path/url is provided, an empty GeoJSON is created.)';
|
|
22
|
-
}
|
|
23
|
-
super.processSchema(data, schema, uiSchema);
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Validate the path, to avoid invalid path or invalid GeoJSON.
|
|
27
|
-
*
|
|
28
|
-
* @param path - the path to validate.
|
|
29
|
-
*/
|
|
30
|
-
async _validatePath(path) {
|
|
27
|
+
return ajv.compile(geojson);
|
|
28
|
+
}, []);
|
|
29
|
+
const validatePath = useCallback(async (path) => {
|
|
31
30
|
var _a;
|
|
32
|
-
const
|
|
33
|
-
let error = '';
|
|
31
|
+
const nextErrors = {};
|
|
34
32
|
let valid = true;
|
|
35
|
-
|
|
33
|
+
let error = '';
|
|
34
|
+
if (!path || !path.trim()) {
|
|
35
|
+
valid = false;
|
|
36
|
+
error = 'Path is required';
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
36
39
|
try {
|
|
37
40
|
const geoJSONData = await loadFile({
|
|
38
41
|
filepath: path,
|
|
39
|
-
type:
|
|
40
|
-
model
|
|
42
|
+
type: 'GeoJSONSource',
|
|
43
|
+
model,
|
|
41
44
|
});
|
|
42
|
-
valid =
|
|
45
|
+
valid = validateGeoJSON(geoJSONData);
|
|
43
46
|
if (!valid) {
|
|
44
47
|
error = `"${path}" is not a valid GeoJSON file`;
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
catch (e) {
|
|
51
|
+
valid = false;
|
|
48
52
|
error = `"${path}" is not a valid GeoJSON file: ${e}`;
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
if (!valid) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
nextErrors.path = { __errors: [error] };
|
|
57
|
+
if ((_a = validateGeoJSON.errors) === null || _a === void 0 ? void 0 : _a.length) {
|
|
58
|
+
validateGeoJSON.errors.reverse().forEach(err => {
|
|
59
|
+
var _a;
|
|
60
|
+
nextErrors.path.__errors.push((_a = err.message) !== null && _a !== void 0 ? _a : '');
|
|
61
|
+
});
|
|
62
|
+
}
|
|
59
63
|
}
|
|
60
64
|
else {
|
|
61
|
-
|
|
65
|
+
nextErrors.path = { __errors: [] };
|
|
62
66
|
}
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
setExtraErrors(nextErrors);
|
|
68
|
+
formErrorSignal === null || formErrorSignal === void 0 ? void 0 : formErrorSignal.emit(!valid);
|
|
69
|
+
}, [model, validateGeoJSON, formErrorSignal]);
|
|
70
|
+
const uiSchema = useMemo(() => {
|
|
71
|
+
var _a, _b;
|
|
72
|
+
const builtUiSchema = {};
|
|
73
|
+
const dataCopy = deepCopy(formData);
|
|
74
|
+
removeFormEntry('data', dataCopy, schema, builtUiSchema);
|
|
75
|
+
if (formContext === 'create' && ((_a = schema.properties) === null || _a === void 0 ? void 0 : _a.path)) {
|
|
76
|
+
schema.properties.path.description =
|
|
77
|
+
'The local path to a GeoJSON file. (If no path/url is provided, an empty GeoJSON is created.)';
|
|
65
78
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
79
|
+
processBaseSchema(dataCopy, schema, builtUiSchema, formContext, removeFormEntry);
|
|
80
|
+
const docManager = formSchemaRegistry === null || formSchemaRegistry === void 0 ? void 0 : formSchemaRegistry.getDocManager();
|
|
81
|
+
if (((_b = schema.properties) === null || _b === void 0 ? void 0 : _b.path) && docManager) {
|
|
82
|
+
builtUiSchema.path = {
|
|
83
|
+
'ui:widget': FileSelectorWidget,
|
|
84
|
+
'ui:options': {
|
|
85
|
+
docManager,
|
|
86
|
+
formOptions: props,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return builtUiSchema;
|
|
91
|
+
}, [schema, formData, formContext, formSchemaRegistry, props]);
|
|
92
|
+
const handleChange = useCallback((data) => {
|
|
93
|
+
handleChangeBase(data);
|
|
94
|
+
if (data.path !== undefined) {
|
|
95
|
+
validatePath(data.path);
|
|
96
|
+
}
|
|
97
|
+
}, [handleChangeBase, validatePath]);
|
|
98
|
+
const handleSubmit = useCallback((data) => {
|
|
99
|
+
var _a, _b;
|
|
100
|
+
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) {
|
|
101
|
+
showErrorMessage('Invalid file', extraErrors.path.__errors[0]);
|
|
71
102
|
return;
|
|
72
103
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
104
|
+
let submitted = Object.assign({}, data);
|
|
105
|
+
if (!submitted.path) {
|
|
106
|
+
submitted = Object.assign(Object.assign({}, submitted), { data: {
|
|
107
|
+
type: 'FeatureCollection',
|
|
108
|
+
features: [],
|
|
109
|
+
} });
|
|
110
|
+
}
|
|
111
|
+
handleSubmitBase(submitted);
|
|
112
|
+
}, [extraErrors, handleSubmitBase]);
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
if (formData === null || formData === void 0 ? void 0 : formData.path) {
|
|
115
|
+
validatePath(formData === null || formData === void 0 ? void 0 : formData.path);
|
|
78
116
|
}
|
|
79
|
-
|
|
117
|
+
}, []);
|
|
118
|
+
if (!hasSchema) {
|
|
119
|
+
return null;
|
|
80
120
|
}
|
|
121
|
+
return (React.createElement(SchemaForm, { schema: schema, formData: formData, onChange: handleChange, onSubmit: handleSubmit, formContext: formContextValue, filePath: filePath, uiSchema: uiSchema, extraErrors: extraErrors, formErrorSignal: formErrorSignal }));
|
|
81
122
|
}
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The form to modify a GeoTiff source.
|
|
6
|
-
*/
|
|
7
|
-
export declare class GeoTiffSourcePropertiesForm extends SourcePropertiesForm {
|
|
8
|
-
private _isSubmitted;
|
|
9
|
-
constructor(props: ISourceFormProps);
|
|
10
|
-
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
11
|
-
protected onFormChange(e: IChangeEvent): void;
|
|
12
|
-
protected onFormBlur(id: string, value: any): void;
|
|
13
|
-
protected onFormSubmit(e: ISubmitEvent<any>): Promise<void>;
|
|
14
|
-
/**
|
|
15
|
-
* Validate the URLs, ensuring that there is at least one object with required fields.
|
|
16
|
-
*
|
|
17
|
-
* @param urls - the URLs array to validate.
|
|
18
|
-
*/
|
|
19
|
-
private _validateUrls;
|
|
20
|
-
}
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ISourceFormProps } from './sourceform';
|
|
3
|
+
export declare function GeoTiffSourcePropertiesForm(props: ISourceFormProps): React.ReactElement | null;
|
|
@@ -1,80 +1,36 @@
|
|
|
1
1
|
import { showErrorMessage } from '@jupyterlab/apputils';
|
|
2
|
-
import {
|
|
3
|
-
import { getMimeType } from "../../../tools";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
// Customize the widget for urls
|
|
22
|
-
if (schema.properties && schema.properties.urls) {
|
|
23
|
-
const docManager = (_a = this.props.formChangedSignal) === null || _a === void 0 ? void 0 : _a.sender.props.formSchemaRegistry.getDocManager();
|
|
24
|
-
uiSchema.urls = Object.assign(Object.assign({}, uiSchema.urls), { items: Object.assign(Object.assign({}, uiSchema.urls.items), { url: {
|
|
25
|
-
'ui:widget': FileSelectorWidget,
|
|
26
|
-
'ui:options': {
|
|
27
|
-
docManager,
|
|
28
|
-
formOptions: this.props,
|
|
29
|
-
},
|
|
30
|
-
} }) });
|
|
31
|
-
}
|
|
32
|
-
// This is not user-editable
|
|
33
|
-
delete schema.properties.valid;
|
|
34
|
-
}
|
|
35
|
-
onFormChange(e) {
|
|
36
|
-
var _a;
|
|
37
|
-
super.onFormChange(e);
|
|
38
|
-
if ((_a = e.formData) === null || _a === void 0 ? void 0 : _a.urls) {
|
|
39
|
-
this._validateUrls(e.formData.urls);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
onFormBlur(id, value) {
|
|
43
|
-
// Is there a better way to spot the url text entry?
|
|
44
|
-
if (!id.endsWith('_urls')) {
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
this._validateUrls(value);
|
|
48
|
-
}
|
|
49
|
-
async onFormSubmit(e) {
|
|
50
|
-
this._isSubmitted = true;
|
|
51
|
-
// validate urls.url only when submitting for better performance
|
|
52
|
-
const { valid, errors } = await this._validateUrls(e.formData.urls);
|
|
53
|
-
if (!valid) {
|
|
54
|
-
if (errors.length > 0) {
|
|
55
|
-
showErrorMessage('Invalid URLs', errors[0]);
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { deepCopy, getMimeType } 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 GeoTiffSourcePropertiesForm(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);
|
|
56
19
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Validate the URLs, ensuring that there is at least one object with required fields.
|
|
63
|
-
*
|
|
64
|
-
* @param urls - the URLs array to validate.
|
|
65
|
-
*/
|
|
66
|
-
async _validateUrls(urls) {
|
|
67
|
-
const extraErrors = this.state.extraErrors;
|
|
20
|
+
: undefined,
|
|
21
|
+
});
|
|
22
|
+
const [extraErrors, setExtraErrors] = useState({});
|
|
23
|
+
const validateUrls = useCallback(async (urls, isSubmit) => {
|
|
68
24
|
const errors = [];
|
|
69
25
|
let valid = true;
|
|
70
26
|
if (urls && urls.length > 0) {
|
|
71
27
|
for (let i = 0; i < urls.length; i++) {
|
|
72
28
|
const { url, min, max } = urls[i];
|
|
73
|
-
if (
|
|
29
|
+
if (isSubmit) {
|
|
74
30
|
const mimeType = getMimeType(url);
|
|
75
31
|
if (!mimeType || !mimeType.startsWith('image/tiff')) {
|
|
76
32
|
valid = false;
|
|
77
|
-
errors.push(`"${url}" is not a valid ${
|
|
33
|
+
errors.push(`"${url}" is not a valid ${sourceType} file.`);
|
|
78
34
|
}
|
|
79
35
|
}
|
|
80
36
|
else {
|
|
@@ -103,15 +59,58 @@ export class GeoTiffSourcePropertiesForm extends SourcePropertiesForm {
|
|
|
103
59
|
errors.push('At least one valid URL with min/max values is required.');
|
|
104
60
|
valid = false;
|
|
105
61
|
}
|
|
106
|
-
|
|
107
|
-
|
|
62
|
+
const nextErrors = valid
|
|
63
|
+
? { urls: { __errors: [] } }
|
|
64
|
+
: { urls: { __errors: errors } };
|
|
65
|
+
setExtraErrors(nextErrors);
|
|
66
|
+
formErrorSignal === null || formErrorSignal === void 0 ? void 0 : formErrorSignal.emit(!valid);
|
|
67
|
+
return { valid, errors };
|
|
68
|
+
}, [sourceType, formErrorSignal]);
|
|
69
|
+
const schema = useMemo(() => {
|
|
70
|
+
const schemaCopy = deepCopy(schemaProp !== null && schemaProp !== void 0 ? schemaProp : {});
|
|
71
|
+
if (schemaCopy.properties) {
|
|
72
|
+
delete schemaCopy.properties.valid;
|
|
108
73
|
}
|
|
109
|
-
|
|
110
|
-
|
|
74
|
+
return schemaCopy;
|
|
75
|
+
}, [schemaProp]);
|
|
76
|
+
const uiSchema = useMemo(() => {
|
|
77
|
+
var _a, _b;
|
|
78
|
+
const builtUiSchema = {};
|
|
79
|
+
const dataCopy = deepCopy(formData);
|
|
80
|
+
processBaseSchema(dataCopy, schema, builtUiSchema, formContext, removeFormEntry);
|
|
81
|
+
const docManager = formSchemaRegistry === null || formSchemaRegistry === void 0 ? void 0 : formSchemaRegistry.getDocManager();
|
|
82
|
+
if (((_a = schema.properties) === null || _a === void 0 ? void 0 : _a.urls) && docManager) {
|
|
83
|
+
builtUiSchema.urls = Object.assign(Object.assign({}, builtUiSchema.urls), { items: Object.assign(Object.assign({}, (_b = builtUiSchema.urls) === null || _b === void 0 ? void 0 : _b.items), { url: {
|
|
84
|
+
'ui:widget': FileSelectorWidget,
|
|
85
|
+
'ui:options': {
|
|
86
|
+
docManager,
|
|
87
|
+
formOptions: props,
|
|
88
|
+
},
|
|
89
|
+
} }) });
|
|
111
90
|
}
|
|
112
|
-
|
|
113
|
-
|
|
91
|
+
return builtUiSchema;
|
|
92
|
+
}, [schema, formData, formContext, formSchemaRegistry, props]);
|
|
93
|
+
const handleChange = useCallback((data) => {
|
|
94
|
+
handleChangeBase(data);
|
|
95
|
+
if (data.urls) {
|
|
96
|
+
validateUrls(data.urls, false);
|
|
114
97
|
}
|
|
115
|
-
|
|
98
|
+
}, [handleChangeBase, validateUrls]);
|
|
99
|
+
const handleSubmit = useCallback(async (data) => {
|
|
100
|
+
const { valid, errors } = await validateUrls(data.urls, true);
|
|
101
|
+
if (!valid) {
|
|
102
|
+
if (errors.length > 0) {
|
|
103
|
+
showErrorMessage('Invalid URLs', errors[0]);
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
handleSubmitBase(data);
|
|
108
|
+
}, [validateUrls, handleSubmitBase]);
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
validateUrls(formData === null || formData === void 0 ? void 0 : formData.urls, false);
|
|
111
|
+
}, []);
|
|
112
|
+
if (!hasSchema) {
|
|
113
|
+
return null;
|
|
116
114
|
}
|
|
115
|
+
return (React.createElement(SchemaForm, { schema: schema, formData: formData, onChange: handleChange, onSubmit: handleSubmit, formContext: formContextValue, filePath: filePath, uiSchema: uiSchema, extraErrors: extraErrors, formErrorSignal: formErrorSignal }));
|
|
117
116
|
}
|