@jupytergis/base 0.1.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.d.ts +11 -0
- package/lib/commands.js +809 -0
- package/lib/console/consoleview.d.ts +24 -0
- package/lib/console/consoleview.js +55 -0
- package/lib/console/index.d.ts +1 -0
- package/lib/console/index.js +1 -0
- package/lib/constants.d.ts +57 -0
- package/lib/constants.js +89 -0
- package/lib/dialogs/components/symbology/BandRendering.d.ts +4 -0
- package/lib/dialogs/components/symbology/BandRendering.js +29 -0
- package/lib/dialogs/components/symbology/BandRow.d.ts +10 -0
- package/lib/dialogs/components/symbology/BandRow.js +43 -0
- package/lib/dialogs/components/symbology/SingleBandPseudoColor.d.ts +20 -0
- package/lib/dialogs/components/symbology/SingleBandPseudoColor.js +281 -0
- package/lib/dialogs/components/symbology/StopRow.d.ts +11 -0
- package/lib/dialogs/components/symbology/StopRow.js +58 -0
- package/lib/dialogs/formdialog.d.ts +31 -0
- package/lib/dialogs/formdialog.js +68 -0
- package/lib/dialogs/layerBrowserDialog.d.ts +25 -0
- package/lib/dialogs/layerBrowserDialog.js +141 -0
- package/lib/dialogs/symbologyDialog.d.ts +23 -0
- package/lib/dialogs/symbologyDialog.js +68 -0
- package/lib/dialogs/terrainDialog.d.ts +21 -0
- package/lib/dialogs/terrainDialog.js +60 -0
- package/lib/formbuilder/creationform.d.ts +56 -0
- package/lib/formbuilder/creationform.js +117 -0
- package/lib/formbuilder/editform.d.ts +24 -0
- package/lib/formbuilder/editform.js +60 -0
- package/lib/formbuilder/formselectors.d.ts +5 -0
- package/lib/formbuilder/formselectors.js +38 -0
- package/lib/formbuilder/index.d.ts +6 -0
- package/lib/formbuilder/index.js +6 -0
- package/lib/formbuilder/objectform/baseform.d.ts +79 -0
- package/lib/formbuilder/objectform/baseform.js +167 -0
- package/lib/formbuilder/objectform/geojsonsource.d.ts +19 -0
- package/lib/formbuilder/objectform/geojsonsource.js +80 -0
- package/lib/formbuilder/objectform/hillshadeLayerForm.d.ts +8 -0
- package/lib/formbuilder/objectform/hillshadeLayerForm.js +12 -0
- package/lib/formbuilder/objectform/layerform.d.ts +19 -0
- package/lib/formbuilder/objectform/layerform.js +17 -0
- package/lib/formbuilder/objectform/tilesourceform.d.ts +7 -0
- package/lib/formbuilder/objectform/tilesourceform.js +60 -0
- package/lib/formbuilder/objectform/vectorlayerform.d.ts +15 -0
- package/lib/formbuilder/objectform/vectorlayerform.js +88 -0
- package/lib/formbuilder/objectform/webGlLayerForm.d.ts +8 -0
- package/lib/formbuilder/objectform/webGlLayerForm.js +10 -0
- package/lib/icons.d.ts +6 -0
- package/lib/icons.js +31 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +10 -0
- package/lib/mainview/index.d.ts +3 -0
- package/lib/mainview/index.js +3 -0
- package/lib/mainview/mainView.d.ts +113 -0
- package/lib/mainview/mainView.js +743 -0
- package/lib/mainview/mainviewmodel.d.ts +24 -0
- package/lib/mainview/mainviewmodel.js +34 -0
- package/lib/mainview/mainviewwidget.d.ts +14 -0
- package/lib/mainview/mainviewwidget.js +18 -0
- package/lib/mainview/spinner.d.ts +6 -0
- package/lib/mainview/spinner.js +5 -0
- package/lib/panelview/components/filter-panel/Filter.d.ts +19 -0
- package/lib/panelview/components/filter-panel/Filter.js +223 -0
- package/lib/panelview/components/filter-panel/FilterRow.d.ts +9 -0
- package/lib/panelview/components/filter-panel/FilterRow.js +61 -0
- package/lib/panelview/components/layers.d.ts +13 -0
- package/lib/panelview/components/layers.js +275 -0
- package/lib/panelview/components/sources.d.ts +10 -0
- package/lib/panelview/components/sources.js +147 -0
- package/lib/panelview/header.d.ts +11 -0
- package/lib/panelview/header.js +20 -0
- package/lib/panelview/index.d.ts +5 -0
- package/lib/panelview/index.js +5 -0
- package/lib/panelview/leftpanel.d.ts +51 -0
- package/lib/panelview/leftpanel.js +125 -0
- package/lib/panelview/model.d.ts +19 -0
- package/lib/panelview/model.js +30 -0
- package/lib/panelview/objectproperties.d.ts +17 -0
- package/lib/panelview/objectproperties.js +92 -0
- package/lib/panelview/rightpanel.d.ts +19 -0
- package/lib/panelview/rightpanel.js +45 -0
- package/lib/toolbar/index.d.ts +2 -0
- package/lib/toolbar/index.js +2 -0
- package/lib/toolbar/usertoolbaritem.d.ts +19 -0
- package/lib/toolbar/usertoolbaritem.js +57 -0
- package/lib/toolbar/widget.d.ts +22 -0
- package/lib/toolbar/widget.js +104 -0
- package/lib/tools.d.ts +25 -0
- package/lib/tools.js +215 -0
- package/lib/types.d.ts +11 -0
- package/lib/types.js +1 -0
- package/lib/widget.d.ts +49 -0
- package/lib/widget.js +144 -0
- package/package.json +95 -0
- package/style/base.css +55 -0
- package/style/colorExpression.css +36 -0
- package/style/dialog.css +8 -0
- package/style/filterPanel.css +70 -0
- package/style/icons/geojson.svg +12 -0
- package/style/icons/mound.svg +9 -0
- package/style/icons/nonvisibility.svg +8 -0
- package/style/icons/raster.svg +5 -0
- package/style/icons/visibility.svg +7 -0
- package/style/index.css +6 -0
- package/style/index.js +6 -0
- package/style/layerBrowser.css +256 -0
- package/style/leftPanel.css +150 -0
- package/style/symbologyDialog.css +86 -0
- package/style/terrainDialog.css +14 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { deepCopy } from '../tools';
|
|
13
|
+
import { PromiseDelegate, UUID } from '@lumino/coreutils';
|
|
14
|
+
import { Signal } from '@lumino/signaling';
|
|
15
|
+
import * as React from 'react';
|
|
16
|
+
import { getLayerTypeForm, getSourceTypeForm } from './formselectors';
|
|
17
|
+
/**
|
|
18
|
+
* Form for creating a source, a layer or both at the same time
|
|
19
|
+
*/
|
|
20
|
+
export class CreationForm extends React.Component {
|
|
21
|
+
constructor(props) {
|
|
22
|
+
super(props);
|
|
23
|
+
this.sourceFormChangedSignal = new Signal(this);
|
|
24
|
+
this.filePath = props.context.path;
|
|
25
|
+
this.jGISModel = props.context.model;
|
|
26
|
+
}
|
|
27
|
+
render() {
|
|
28
|
+
var _a;
|
|
29
|
+
const sourceId = UUID.uuid4();
|
|
30
|
+
let layerSchema = undefined;
|
|
31
|
+
const LayerForm = getLayerTypeForm(this.props.layerType || 'RasterLayer');
|
|
32
|
+
const layerData = deepCopy(this.props.layerData || {});
|
|
33
|
+
if (this.props.createLayer) {
|
|
34
|
+
if (!this.props.layerType) {
|
|
35
|
+
console.error('Cannot create a layer without specifying its type');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
layerSchema = deepCopy(this.props.formSchemaRegistry.getSchemas().get(this.props.layerType));
|
|
39
|
+
if (!layerSchema) {
|
|
40
|
+
console.error(`Cannot find schema for ${this.props.layerType}`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// If a source is created as part of this form, remove the source selection from the layer form
|
|
44
|
+
if (this.props.createSource) {
|
|
45
|
+
(_a = layerSchema.properties) === null || _a === void 0 ? true : delete _a.source;
|
|
46
|
+
layerData.source = sourceId;
|
|
47
|
+
}
|
|
48
|
+
layerSchema['required'] = ['name', ...layerSchema['required']];
|
|
49
|
+
layerSchema['properties'] = Object.assign({ name: { type: 'string', description: 'The name of the layer' } }, layerSchema['properties']);
|
|
50
|
+
}
|
|
51
|
+
let sourceSchema = undefined;
|
|
52
|
+
const SourceForm = getSourceTypeForm(this.props.sourceType || 'RasterSource');
|
|
53
|
+
if (this.props.sourceType) {
|
|
54
|
+
sourceSchema = deepCopy(this.props.formSchemaRegistry.getSchemas().get(this.props.sourceType));
|
|
55
|
+
if (!sourceSchema) {
|
|
56
|
+
console.error(`Cannot find schema for ${this.props.sourceType}`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (!this.props.createLayer) {
|
|
60
|
+
sourceSchema['required'] = ['name', ...sourceSchema['required']];
|
|
61
|
+
sourceSchema['properties'] = Object.assign({ name: { type: 'string', description: 'The name of the source' } }, sourceSchema['properties']);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const creationPromises = [];
|
|
65
|
+
let layerCreationPromise;
|
|
66
|
+
let sourceCreationPromise;
|
|
67
|
+
if (this.props.createLayer) {
|
|
68
|
+
layerCreationPromise = new PromiseDelegate();
|
|
69
|
+
creationPromises.push(layerCreationPromise.promise);
|
|
70
|
+
}
|
|
71
|
+
if (this.props.createSource) {
|
|
72
|
+
sourceCreationPromise = new PromiseDelegate();
|
|
73
|
+
creationPromises.push(sourceCreationPromise.promise);
|
|
74
|
+
}
|
|
75
|
+
// Perform the layer/source creation
|
|
76
|
+
Promise.all(creationPromises).then(async () => {
|
|
77
|
+
if (this.props.createSource) {
|
|
78
|
+
let actualName = '';
|
|
79
|
+
const _a = (await (sourceCreationPromise === null || sourceCreationPromise === void 0 ? void 0 : sourceCreationPromise.promise)), { name } = _a, sourceData = __rest(_a, ["name"]);
|
|
80
|
+
actualName =
|
|
81
|
+
name ||
|
|
82
|
+
(await (layerCreationPromise === null || layerCreationPromise === void 0 ? void 0 : layerCreationPromise.promise)).name + ' Source';
|
|
83
|
+
const sourceModel = {
|
|
84
|
+
type: this.props.sourceType || 'RasterSource',
|
|
85
|
+
name: actualName,
|
|
86
|
+
parameters: sourceData
|
|
87
|
+
};
|
|
88
|
+
this.jGISModel.sharedModel.addSource(sourceId, sourceModel);
|
|
89
|
+
}
|
|
90
|
+
if (this.props.createLayer) {
|
|
91
|
+
let actualName = '';
|
|
92
|
+
const _b = (await (layerCreationPromise === null || layerCreationPromise === void 0 ? void 0 : layerCreationPromise.promise)), { name } = _b, layerData = __rest(_b, ["name"]);
|
|
93
|
+
actualName =
|
|
94
|
+
name ||
|
|
95
|
+
(await (layerCreationPromise === null || layerCreationPromise === void 0 ? void 0 : layerCreationPromise.promise)).name + ' Layer';
|
|
96
|
+
const layerModel = {
|
|
97
|
+
type: this.props.layerType || 'RasterLayer',
|
|
98
|
+
parameters: layerData,
|
|
99
|
+
visible: true,
|
|
100
|
+
name: actualName
|
|
101
|
+
};
|
|
102
|
+
this.jGISModel.addLayer(UUID.uuid4(), layerModel);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return (React.createElement("div", null,
|
|
106
|
+
this.props.createSource && (React.createElement("div", null,
|
|
107
|
+
React.createElement("h3", null, "Source Properties"),
|
|
108
|
+
React.createElement(SourceForm, { formContext: "create", model: this.jGISModel, filePath: `${this.filePath}::panel`, schema: sourceSchema, sourceData: this.props.sourceData, syncData: (properties) => {
|
|
109
|
+
sourceCreationPromise === null || sourceCreationPromise === void 0 ? void 0 : sourceCreationPromise.resolve(properties);
|
|
110
|
+
}, ok: this.props.ok, cancel: this.props.cancel, formChangedSignal: this.sourceFormChangedSignal, formErrorSignal: this.props.formErrorSignal }))),
|
|
111
|
+
this.props.createLayer && (React.createElement("div", null,
|
|
112
|
+
React.createElement("h3", null, "Layer Properties"),
|
|
113
|
+
React.createElement(LayerForm, { formContext: "create", sourceType: this.props.sourceType, model: this.jGISModel, filePath: `${this.filePath}::panel`, schema: layerSchema, sourceData: layerData, syncData: (properties) => {
|
|
114
|
+
layerCreationPromise === null || layerCreationPromise === void 0 ? void 0 : layerCreationPromise.resolve(properties);
|
|
115
|
+
}, ok: this.props.ok, cancel: this.props.cancel, sourceFormChangedSignal: this.sourceFormChangedSignal, formErrorSignal: this.props.formErrorSignal })))));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
2
|
+
import { IJGISFormSchemaRegistry, IJupyterGISModel } from '@jupytergis/schema';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
export interface IEditFormProps {
|
|
5
|
+
/**
|
|
6
|
+
* The layer to edit
|
|
7
|
+
*/
|
|
8
|
+
layer: string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* The source to edit
|
|
11
|
+
*/
|
|
12
|
+
source: string | undefined;
|
|
13
|
+
formSchemaRegistry: IJGISFormSchemaRegistry;
|
|
14
|
+
context: DocumentRegistry.IContext<IJupyterGISModel>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Form for editing a source, a layer or both at the same time
|
|
18
|
+
*/
|
|
19
|
+
export declare class EditForm extends React.Component<IEditFormProps, any> {
|
|
20
|
+
syncObjectProperties(id: string | undefined, properties: {
|
|
21
|
+
[key: string]: any;
|
|
22
|
+
}): Promise<void>;
|
|
23
|
+
render(): React.JSX.Element | undefined;
|
|
24
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { deepCopy } from '../tools';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { getLayerTypeForm, getSourceTypeForm } from './formselectors';
|
|
4
|
+
/**
|
|
5
|
+
* Form for editing a source, a layer or both at the same time
|
|
6
|
+
*/
|
|
7
|
+
export class EditForm extends React.Component {
|
|
8
|
+
async syncObjectProperties(id, properties) {
|
|
9
|
+
if (!id) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
this.props.context.model.sharedModel.updateObjectParameters(id, properties);
|
|
13
|
+
}
|
|
14
|
+
render() {
|
|
15
|
+
let layerSchema = undefined;
|
|
16
|
+
let LayerForm = undefined;
|
|
17
|
+
let layerData = undefined;
|
|
18
|
+
if (this.props.layer) {
|
|
19
|
+
const layer = this.props.context.model.getLayer(this.props.layer);
|
|
20
|
+
if (!layer) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
LayerForm = getLayerTypeForm((layer === null || layer === void 0 ? void 0 : layer.type) || 'RasterLayer');
|
|
24
|
+
layerData = deepCopy((layer === null || layer === void 0 ? void 0 : layer.parameters) || {});
|
|
25
|
+
layerSchema = deepCopy(this.props.formSchemaRegistry.getSchemas().get(layer.type));
|
|
26
|
+
if (!layerSchema) {
|
|
27
|
+
console.error(`Cannot find schema for ${layer.type}`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
let sourceSchema = undefined;
|
|
32
|
+
let SourceForm = undefined;
|
|
33
|
+
let sourceData = undefined;
|
|
34
|
+
let source = undefined;
|
|
35
|
+
if (this.props.source) {
|
|
36
|
+
source = this.props.context.model.getSource(this.props.source);
|
|
37
|
+
if (!source) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
SourceForm = getSourceTypeForm((source === null || source === void 0 ? void 0 : source.type) || 'RasterSource');
|
|
41
|
+
sourceData = deepCopy((source === null || source === void 0 ? void 0 : source.parameters) || {});
|
|
42
|
+
sourceSchema = deepCopy(this.props.formSchemaRegistry.getSchemas().get(source.type));
|
|
43
|
+
if (!sourceSchema) {
|
|
44
|
+
console.error(`Cannot find schema for ${source.type}`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return (React.createElement("div", null,
|
|
49
|
+
this.props.layer && LayerForm && (React.createElement("div", null,
|
|
50
|
+
React.createElement("h3", null, "Layer Properties"),
|
|
51
|
+
React.createElement(LayerForm, { formContext: "create", sourceType: (source === null || source === void 0 ? void 0 : source.type) || 'RasterSource', model: this.props.context.model, filePath: `${this.props.context.path}::panel`, schema: layerSchema, sourceData: layerData, syncData: (properties) => {
|
|
52
|
+
this.syncObjectProperties(this.props.layer, properties);
|
|
53
|
+
} }))),
|
|
54
|
+
this.props.source && SourceForm && (React.createElement("div", null,
|
|
55
|
+
React.createElement("h3", null, "Source Properties"),
|
|
56
|
+
React.createElement(SourceForm, { formContext: "create", model: this.props.context.model, filePath: `${this.props.context.path}::panel`, schema: sourceSchema, sourceData: sourceData, syncData: (properties) => {
|
|
57
|
+
this.syncObjectProperties(this.props.source, properties);
|
|
58
|
+
} })))));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { LayerType, SourceType } from '@jupytergis/schema';
|
|
2
|
+
import { BaseForm } from './objectform/baseform';
|
|
3
|
+
import { LayerPropertiesForm } from './objectform/layerform';
|
|
4
|
+
export declare function getLayerTypeForm(layerType: LayerType): typeof LayerPropertiesForm;
|
|
5
|
+
export declare function getSourceTypeForm(sourceType: SourceType): typeof BaseForm;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { BaseForm } from './objectform/baseform';
|
|
2
|
+
import { GeoJSONSourcePropertiesForm } from './objectform/geojsonsource';
|
|
3
|
+
import { HillshadeLayerPropertiesForm } from './objectform/hillshadeLayerForm';
|
|
4
|
+
import { LayerPropertiesForm } from './objectform/layerform';
|
|
5
|
+
import { TileSourcePropertiesForm } from './objectform/tilesourceform';
|
|
6
|
+
import { VectorLayerPropertiesForm } from './objectform/vectorlayerform';
|
|
7
|
+
import { WebGlLayerPropertiesForm } from './objectform/webGlLayerForm';
|
|
8
|
+
export function getLayerTypeForm(layerType) {
|
|
9
|
+
let LayerForm = LayerPropertiesForm;
|
|
10
|
+
switch (layerType) {
|
|
11
|
+
case 'VectorTileLayer':
|
|
12
|
+
case 'VectorLayer':
|
|
13
|
+
LayerForm = VectorLayerPropertiesForm;
|
|
14
|
+
break;
|
|
15
|
+
case 'HillshadeLayer':
|
|
16
|
+
LayerForm = HillshadeLayerPropertiesForm;
|
|
17
|
+
break;
|
|
18
|
+
case 'WebGlLayer':
|
|
19
|
+
LayerForm = WebGlLayerPropertiesForm;
|
|
20
|
+
break;
|
|
21
|
+
// ADD MORE FORM TYPES HERE
|
|
22
|
+
}
|
|
23
|
+
return LayerForm;
|
|
24
|
+
}
|
|
25
|
+
export function getSourceTypeForm(sourceType) {
|
|
26
|
+
let SourceForm = BaseForm;
|
|
27
|
+
switch (sourceType) {
|
|
28
|
+
case 'GeoJSONSource':
|
|
29
|
+
SourceForm = GeoJSONSourcePropertiesForm;
|
|
30
|
+
break;
|
|
31
|
+
case 'RasterSource':
|
|
32
|
+
case 'VectorTileSource':
|
|
33
|
+
SourceForm = TileSourcePropertiesForm;
|
|
34
|
+
break;
|
|
35
|
+
// ADD MORE FORM TYPES HERE
|
|
36
|
+
}
|
|
37
|
+
return SourceForm;
|
|
38
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { IChangeEvent, ISubmitEvent } from '@rjsf/core';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { IJupyterGISModel } from '@jupytergis/schema';
|
|
4
|
+
import { Dialog } from '@jupyterlab/apputils';
|
|
5
|
+
import { Signal } from '@lumino/signaling';
|
|
6
|
+
import { IDict } from '../../types';
|
|
7
|
+
export interface IBaseFormStates {
|
|
8
|
+
schema?: IDict;
|
|
9
|
+
extraErrors?: any;
|
|
10
|
+
}
|
|
11
|
+
export interface IBaseFormProps {
|
|
12
|
+
/**
|
|
13
|
+
* The context of the form, whether it's for creating an object or updating its properties. This will have the effect of showing or not inputs for readonly properties.
|
|
14
|
+
*/
|
|
15
|
+
formContext: 'update' | 'create';
|
|
16
|
+
/**
|
|
17
|
+
* The source data for filling the form
|
|
18
|
+
*/
|
|
19
|
+
sourceData: IDict | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Path to the file
|
|
22
|
+
*/
|
|
23
|
+
filePath?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Current GIS model
|
|
26
|
+
*/
|
|
27
|
+
model: IJupyterGISModel;
|
|
28
|
+
/**
|
|
29
|
+
* callback for syncing back the data into the model upon form submit
|
|
30
|
+
* @param properties
|
|
31
|
+
*/
|
|
32
|
+
syncData: (properties: IDict) => void;
|
|
33
|
+
/**
|
|
34
|
+
* The schema for the rjsf formk
|
|
35
|
+
*/
|
|
36
|
+
schema?: IDict;
|
|
37
|
+
/**
|
|
38
|
+
* Ok signal. This is the signal sent by the parent dialog upon "Ok" button click. No ok button will be displayed if defined.
|
|
39
|
+
*/
|
|
40
|
+
ok?: Signal<Dialog<any>, number>;
|
|
41
|
+
/**
|
|
42
|
+
* Cancel callback
|
|
43
|
+
*/
|
|
44
|
+
cancel?: () => void;
|
|
45
|
+
/**
|
|
46
|
+
* A signal emitting when the form changed
|
|
47
|
+
*/
|
|
48
|
+
formChangedSignal?: Signal<any, IDict<any>>;
|
|
49
|
+
/**
|
|
50
|
+
* A signal emitting when the form has extra errors, with a boolean whether there are some
|
|
51
|
+
* extra errors or not.
|
|
52
|
+
*/
|
|
53
|
+
formErrorSignal?: Signal<Dialog<any>, boolean>;
|
|
54
|
+
}
|
|
55
|
+
export declare const LuminoSchemaForm: (props: React.PropsWithChildren<any>) => JSX.Element;
|
|
56
|
+
/**
|
|
57
|
+
* Generate a form to edit a layer/source type. This class is meant to be sub-classed to create more refined forms for specific layers/sources.
|
|
58
|
+
*
|
|
59
|
+
* It will be up to the user of this class to actually perform the creation/edit using syncdata.
|
|
60
|
+
*/
|
|
61
|
+
export declare class BaseForm extends React.Component<IBaseFormProps, IBaseFormStates> {
|
|
62
|
+
constructor(props: IBaseFormProps);
|
|
63
|
+
componentDidUpdate(prevProps: IBaseFormProps, prevState: IBaseFormStates): void;
|
|
64
|
+
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
65
|
+
/**
|
|
66
|
+
* Remove a specific entry from the form. Can be used in subclasses if needed while under processSchema.
|
|
67
|
+
* @param entry The entry name
|
|
68
|
+
* @param data The form data
|
|
69
|
+
* @param schema The form schema
|
|
70
|
+
* @param uiSchema The form uiSchema
|
|
71
|
+
*/
|
|
72
|
+
protected removeFormEntry(entry: string, data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
73
|
+
protected syncData(properties: IDict<any> | undefined): void;
|
|
74
|
+
protected onFormChange(e: IChangeEvent): void;
|
|
75
|
+
protected onFormBlur(id: string, value: any): void;
|
|
76
|
+
protected onFormSubmit(e: ISubmitEvent<any>): void;
|
|
77
|
+
render(): React.ReactNode;
|
|
78
|
+
protected currentFormData: IDict<any> | undefined;
|
|
79
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { SchemaForm } from '@deathbeds/jupyterlab-rjsf';
|
|
2
|
+
import { MessageLoop } from '@lumino/messaging';
|
|
3
|
+
import { Widget } from '@lumino/widgets';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { deepCopy } from '../../tools';
|
|
6
|
+
// Reusing the datalayer/jupyter-react component:
|
|
7
|
+
// https://github.com/datalayer/jupyter-react/blob/main/packages/react/src/jupyter/lumino/Lumino.tsx
|
|
8
|
+
export const LuminoSchemaForm = (props) => {
|
|
9
|
+
const ref = React.useRef(null);
|
|
10
|
+
const { children } = props;
|
|
11
|
+
React.useEffect(() => {
|
|
12
|
+
const widget = children;
|
|
13
|
+
try {
|
|
14
|
+
MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach);
|
|
15
|
+
ref.current.insertBefore(widget.node, null);
|
|
16
|
+
MessageLoop.sendMessage(widget, Widget.Msg.AfterAttach);
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
console.warn('Exception while attaching Lumino widget.', e);
|
|
20
|
+
}
|
|
21
|
+
return () => {
|
|
22
|
+
try {
|
|
23
|
+
if (widget.isAttached || widget.node.isConnected) {
|
|
24
|
+
Widget.detach(widget);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
console.warn('Exception while detaching Lumino widget.', e);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}, [children]);
|
|
32
|
+
return React.createElement("div", { ref: ref });
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Generate a form to edit a layer/source type. This class is meant to be sub-classed to create more refined forms for specific layers/sources.
|
|
36
|
+
*
|
|
37
|
+
* It will be up to the user of this class to actually perform the creation/edit using syncdata.
|
|
38
|
+
*/
|
|
39
|
+
export class BaseForm extends React.Component {
|
|
40
|
+
constructor(props) {
|
|
41
|
+
super(props);
|
|
42
|
+
this.currentFormData = deepCopy(this.props.sourceData);
|
|
43
|
+
this.state = {
|
|
44
|
+
schema: props.schema,
|
|
45
|
+
extraErrors: {}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
componentDidUpdate(prevProps, prevState) {
|
|
49
|
+
if (prevProps.sourceData !== this.props.sourceData) {
|
|
50
|
+
this.currentFormData = deepCopy(this.props.sourceData);
|
|
51
|
+
const schema = deepCopy(this.props.schema);
|
|
52
|
+
this.setState(old => (Object.assign(Object.assign({}, old), { schema })));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
processSchema(data, schema, uiSchema) {
|
|
56
|
+
if (!schema['properties']) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
Object.entries(schema['properties']).forEach(([k, v]) => {
|
|
60
|
+
uiSchema[k] = {};
|
|
61
|
+
if (v['type'] === 'array') {
|
|
62
|
+
// Remove array buttons
|
|
63
|
+
uiSchema[k] = Object.assign({ 'ui:options': {
|
|
64
|
+
orderable: false,
|
|
65
|
+
removable: false,
|
|
66
|
+
addable: false
|
|
67
|
+
} }, uiSchema[k]);
|
|
68
|
+
if (v['items']['type'] === 'array') {
|
|
69
|
+
uiSchema[k] = Object.assign({ items: Object.assign({ 'ui:options': {
|
|
70
|
+
orderable: false,
|
|
71
|
+
removable: false,
|
|
72
|
+
addable: false
|
|
73
|
+
} }, uiSchema[k]['items']) }, uiSchema[k]);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (v['type'] === 'object') {
|
|
77
|
+
this.processSchema(data, v, uiSchema[k]);
|
|
78
|
+
}
|
|
79
|
+
// Don't show readOnly properties when it's a form for updating an object
|
|
80
|
+
if (v['readOnly']) {
|
|
81
|
+
if (this.props.formContext === 'create') {
|
|
82
|
+
delete v['readOnly'];
|
|
83
|
+
}
|
|
84
|
+
if (this.props.formContext === 'update') {
|
|
85
|
+
this.removeFormEntry(k, data, schema, uiSchema);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Remove a specific entry from the form. Can be used in subclasses if needed while under processSchema.
|
|
92
|
+
* @param entry The entry name
|
|
93
|
+
* @param data The form data
|
|
94
|
+
* @param schema The form schema
|
|
95
|
+
* @param uiSchema The form uiSchema
|
|
96
|
+
*/
|
|
97
|
+
removeFormEntry(entry, data, schema, uiSchema) {
|
|
98
|
+
if (data) {
|
|
99
|
+
delete data[entry];
|
|
100
|
+
}
|
|
101
|
+
delete schema.properties[entry];
|
|
102
|
+
delete uiSchema[entry];
|
|
103
|
+
if (schema.required && schema.required.includes(entry)) {
|
|
104
|
+
schema.required.splice(schema.required.indexOf(entry), 1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
syncData(properties) {
|
|
108
|
+
if (!properties) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
this.props.syncData(properties);
|
|
112
|
+
}
|
|
113
|
+
onFormChange(e) {
|
|
114
|
+
this.currentFormData = e.formData;
|
|
115
|
+
if (this.props.formChangedSignal) {
|
|
116
|
+
this.props.formChangedSignal.emit(this.currentFormData || {});
|
|
117
|
+
}
|
|
118
|
+
if (this.props.formErrorSignal) {
|
|
119
|
+
const extraErrors = Object.keys(this.state.extraErrors).length > 0;
|
|
120
|
+
this.props.formErrorSignal.emit(extraErrors);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
onFormBlur(id, value) {
|
|
124
|
+
// This is a no-op here
|
|
125
|
+
}
|
|
126
|
+
onFormSubmit(e) {
|
|
127
|
+
this.currentFormData = e.formData;
|
|
128
|
+
this.syncData(this.currentFormData);
|
|
129
|
+
this.props.cancel && this.props.cancel();
|
|
130
|
+
}
|
|
131
|
+
render() {
|
|
132
|
+
var _a, _b;
|
|
133
|
+
if (this.props.schema) {
|
|
134
|
+
const schema = Object.assign(Object.assign({}, this.state.schema), { additionalProperties: true });
|
|
135
|
+
const formData = this.currentFormData;
|
|
136
|
+
const uiSchema = {
|
|
137
|
+
additionalProperties: {
|
|
138
|
+
'ui:label': false,
|
|
139
|
+
classNames: 'jGIS-hidden-field'
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
this.processSchema(formData, schema, uiSchema);
|
|
143
|
+
const submitRef = React.createRef();
|
|
144
|
+
// When the parent "ok" button gets clicked, submit
|
|
145
|
+
(_a = this.props.ok) === null || _a === void 0 ? void 0 : _a.connect(() => {
|
|
146
|
+
var _a;
|
|
147
|
+
(_a = submitRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
148
|
+
});
|
|
149
|
+
const formSchema = new SchemaForm(schema, {
|
|
150
|
+
liveValidate: true,
|
|
151
|
+
formData,
|
|
152
|
+
onChange: this.onFormChange.bind(this),
|
|
153
|
+
onSubmit: this.onFormSubmit.bind(this),
|
|
154
|
+
onBlur: this.onFormBlur.bind(this),
|
|
155
|
+
uiSchema,
|
|
156
|
+
children: (React.createElement("button", { ref: submitRef, type: "submit", style: { display: 'none' } })),
|
|
157
|
+
extraErrors: this.state.extraErrors
|
|
158
|
+
});
|
|
159
|
+
return (React.createElement("div", { className: "jGIS-property-panel", "data-path": (_b = this.props.filePath) !== null && _b !== void 0 ? _b : '' },
|
|
160
|
+
React.createElement("div", { className: "jGIS-property-outer" },
|
|
161
|
+
React.createElement(LuminoSchemaForm, null, formSchema)),
|
|
162
|
+
!this.props.ok && (React.createElement("div", { className: "jGIS-property-buttons" },
|
|
163
|
+
React.createElement("button", { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: () => { var _a; return (_a = submitRef.current) === null || _a === void 0 ? void 0 : _a.click(); } },
|
|
164
|
+
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, "Ok"))))));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { IDict } from '@jupytergis/schema';
|
|
2
|
+
import { ISubmitEvent } from '@rjsf/core';
|
|
3
|
+
import { BaseForm, IBaseFormProps } from './baseform';
|
|
4
|
+
/**
|
|
5
|
+
* The form to modify a GeoJSON source.
|
|
6
|
+
*/
|
|
7
|
+
export declare class GeoJSONSourcePropertiesForm extends BaseForm {
|
|
8
|
+
constructor(props: IBaseFormProps);
|
|
9
|
+
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
10
|
+
protected onFormBlur(id: string, value: any): void;
|
|
11
|
+
protected onFormSubmit(e: ISubmitEvent<any>): void;
|
|
12
|
+
/**
|
|
13
|
+
* Validate the path, to avoid invalid path or invalid GeoJSON.
|
|
14
|
+
*
|
|
15
|
+
* @param path - the path to validate.
|
|
16
|
+
*/
|
|
17
|
+
private _validatePath;
|
|
18
|
+
private _validate;
|
|
19
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { showErrorMessage } from '@jupyterlab/apputils';
|
|
2
|
+
import { Ajv } from 'ajv';
|
|
3
|
+
import * as geojson from 'geojson-schema/GeoJSON.json';
|
|
4
|
+
import { BaseForm } from './baseform';
|
|
5
|
+
/**
|
|
6
|
+
* The form to modify a GeoJSON source.
|
|
7
|
+
*/
|
|
8
|
+
export class GeoJSONSourcePropertiesForm extends BaseForm {
|
|
9
|
+
constructor(props) {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
super(props);
|
|
12
|
+
const ajv = new Ajv();
|
|
13
|
+
this._validate = ajv.compile(geojson);
|
|
14
|
+
this._validatePath((_b = (_a = props.sourceData) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : '');
|
|
15
|
+
}
|
|
16
|
+
processSchema(data, schema, uiSchema) {
|
|
17
|
+
if ((data === null || data === void 0 ? void 0 : data.path) !== '') {
|
|
18
|
+
this.removeFormEntry('data', data, schema, uiSchema);
|
|
19
|
+
}
|
|
20
|
+
super.processSchema(data, schema, uiSchema);
|
|
21
|
+
if (!schema.properties || !data) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// This is not user-editable
|
|
25
|
+
delete schema.properties.valid;
|
|
26
|
+
}
|
|
27
|
+
onFormBlur(id, value) {
|
|
28
|
+
// Is there a better way to spot the path text entry?
|
|
29
|
+
if (!id.endsWith('_path')) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
this._validatePath(value);
|
|
33
|
+
}
|
|
34
|
+
onFormSubmit(e) {
|
|
35
|
+
var _a, _b, _c;
|
|
36
|
+
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) {
|
|
37
|
+
showErrorMessage('Invalid JSON file', this.state.extraErrors.path.__errors[0]);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
super.onFormSubmit(e);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Validate the path, to avoid invalid path or invalid GeoJSON.
|
|
44
|
+
*
|
|
45
|
+
* @param path - the path to validate.
|
|
46
|
+
*/
|
|
47
|
+
async _validatePath(path) {
|
|
48
|
+
const extraErrors = {
|
|
49
|
+
path: {
|
|
50
|
+
__errors: []
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
this.props.model
|
|
54
|
+
.readGeoJSON(path)
|
|
55
|
+
.then(async (geoJSONData) => {
|
|
56
|
+
var _a;
|
|
57
|
+
const valid = this._validate(geoJSONData);
|
|
58
|
+
if (!valid) {
|
|
59
|
+
extraErrors.path.__errors = [`"${path}" is not a valid GeoJSON file`];
|
|
60
|
+
(_a = this._validate.errors) === null || _a === void 0 ? void 0 : _a.reverse().forEach(error => {
|
|
61
|
+
extraErrors.path.__errors.push(error.message);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
delete extraErrors.path;
|
|
66
|
+
}
|
|
67
|
+
this.setState({ extraErrors });
|
|
68
|
+
if (this.props.formErrorSignal) {
|
|
69
|
+
this.props.formErrorSignal.emit(!valid);
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
.catch(e => {
|
|
73
|
+
extraErrors.path.__errors = [`Cannot read "${path}"`];
|
|
74
|
+
this.setState({ extraErrors });
|
|
75
|
+
if (this.props.formErrorSignal) {
|
|
76
|
+
this.props.formErrorSignal.emit(true);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { IDict } from '@jupytergis/schema';
|
|
2
|
+
import { LayerPropertiesForm } from './layerform';
|
|
3
|
+
/**
|
|
4
|
+
* The form to modify a hillshade layer.
|
|
5
|
+
*/
|
|
6
|
+
export declare class HillshadeLayerPropertiesForm extends LayerPropertiesForm {
|
|
7
|
+
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
8
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { LayerPropertiesForm } from './layerform';
|
|
2
|
+
/**
|
|
3
|
+
* The form to modify a hillshade layer.
|
|
4
|
+
*/
|
|
5
|
+
export class HillshadeLayerPropertiesForm extends LayerPropertiesForm {
|
|
6
|
+
processSchema(data, schema, uiSchema) {
|
|
7
|
+
super.processSchema(data, schema, uiSchema);
|
|
8
|
+
uiSchema['shadowColor'] = {
|
|
9
|
+
'ui:widget': 'color'
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { IDict, SourceType } from '@jupytergis/schema';
|
|
2
|
+
import { BaseForm, IBaseFormProps } from './baseform';
|
|
3
|
+
import { Signal } from '@lumino/signaling';
|
|
4
|
+
export interface ILayerProps extends IBaseFormProps {
|
|
5
|
+
/**
|
|
6
|
+
* The source type for the layer
|
|
7
|
+
*/
|
|
8
|
+
sourceType: SourceType;
|
|
9
|
+
/**
|
|
10
|
+
* The signal emitted when the attached source form has changed, if it exists
|
|
11
|
+
*/
|
|
12
|
+
sourceFormChangedSignal?: Signal<any, IDict<any>>;
|
|
13
|
+
}
|
|
14
|
+
export declare class LayerPropertiesForm extends BaseForm {
|
|
15
|
+
props: ILayerProps;
|
|
16
|
+
protected sourceFormChangedSignal: Signal<any, IDict<any>> | undefined;
|
|
17
|
+
constructor(props: ILayerProps);
|
|
18
|
+
protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
|
|
19
|
+
}
|