@jupytergis/base 0.12.2 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/lib/commands/index.js +2 -6
  2. package/lib/dialogs/symbology/hooks/useEffectiveSymbologyParams.d.ts +16 -0
  3. package/lib/dialogs/symbology/hooks/useEffectiveSymbologyParams.js +24 -0
  4. package/lib/dialogs/symbology/hooks/useOkSignal.d.ts +6 -0
  5. package/lib/dialogs/symbology/hooks/useOkSignal.js +25 -0
  6. package/lib/dialogs/symbology/symbologyDialog.d.ts +4 -2
  7. package/lib/dialogs/symbology/symbologyDialog.js +6 -10
  8. package/lib/dialogs/symbology/symbologyUtils.d.ts +25 -2
  9. package/lib/dialogs/symbology/symbologyUtils.js +74 -4
  10. package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +3 -3
  11. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +31 -34
  12. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +68 -62
  13. package/lib/dialogs/symbology/vector_layer/VectorRendering.js +33 -21
  14. package/lib/dialogs/symbology/vector_layer/types/Canonical.js +23 -24
  15. package/lib/dialogs/symbology/vector_layer/types/Categorized.js +48 -49
  16. package/lib/dialogs/symbology/vector_layer/types/Graduated.js +53 -62
  17. package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +35 -34
  18. package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.js +45 -47
  19. package/lib/formbuilder/objectform/StoryEditorForm.js +0 -17
  20. package/lib/formbuilder/objectform/baseform.d.ts +6 -0
  21. package/lib/formbuilder/objectform/baseform.js +21 -38
  22. package/lib/formbuilder/objectform/components/LayerSelect.d.ts +7 -0
  23. package/lib/formbuilder/objectform/components/LayerSelect.js +43 -0
  24. package/lib/formbuilder/objectform/components/OpacitySlider.d.ts +4 -0
  25. package/lib/formbuilder/objectform/components/OpacitySlider.js +40 -0
  26. package/lib/formbuilder/objectform/components/SegmentFormSymbology.d.ts +3 -0
  27. package/lib/formbuilder/objectform/components/SegmentFormSymbology.js +59 -0
  28. package/lib/formbuilder/objectform/layer/storySegmentLayerForm.d.ts +2 -2
  29. package/lib/formbuilder/objectform/layer/storySegmentLayerForm.js +19 -0
  30. package/lib/mainview/mainView.js +6 -1
  31. package/lib/panelview/rightpanel.d.ts +3 -1
  32. package/lib/panelview/rightpanel.js +2 -2
  33. package/lib/panelview/story-maps/StoryViewerPanel.d.ts +3 -1
  34. package/lib/panelview/story-maps/StoryViewerPanel.js +127 -19
  35. package/lib/shared/hooks/useLatest.d.ts +1 -0
  36. package/lib/shared/hooks/useLatest.js +8 -0
  37. package/lib/types.d.ts +1 -0
  38. package/lib/types.js +6 -1
  39. package/package.json +2 -2
  40. package/style/base.css +8 -0
  41. package/style/storyPanel.css +4 -4
@@ -1,10 +1,13 @@
1
- import React, { useEffect, useRef, useState } from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
  import { VectorClassifications } from "../../classificationModes";
3
3
  import ColorRampControls from "../../components/color_ramp/ColorRampControls";
4
4
  import StopContainer from "../../components/color_stops/StopContainer";
5
- import { Utils, VectorUtils } from "../../symbologyUtils";
5
+ import { useOkSignal } from "../../hooks/useOkSignal";
6
+ import { saveSymbology, Utils, VectorUtils, } from "../../symbologyUtils";
6
7
  import ValueSelect from "../components/ValueSelect";
7
- const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTab, selectableAttributesAndValues, }) => {
8
+ import { useLatest } from "../../../../shared/hooks/useLatest";
9
+ import { useEffectiveSymbologyParams } from '../../hooks/useEffectiveSymbologyParams';
10
+ const Graduated = ({ model, okSignalPromise, layerId, symbologyTab, selectableAttributesAndValues, isStorySegmentOverride, segmentId, }) => {
8
11
  const modeOptions = [
9
12
  'quantile',
10
13
  'equal interval',
@@ -12,11 +15,6 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
12
15
  'pretty',
13
16
  'logarithmic',
14
17
  ];
15
- const selectableAttributeRef = useRef();
16
- const symbologyTabRef = useRef();
17
- const colorStopRowsRef = useRef([]);
18
- const radiusStopRowsRef = useRef([]);
19
- const colorRampOptionsRef = useRef();
20
18
  const [selectedAttribute, setSelectedAttribute] = useState('');
21
19
  const [colorStopRows, setColorStopRows] = useState([]);
22
20
  const [radiusStopRows, setRadiusStopRows] = useState([]);
@@ -29,31 +27,34 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
29
27
  radius: 5,
30
28
  });
31
29
  const [reverseRamp, setReverseRamp] = useState(false);
32
- const colorManualStyleRef = useRef(colorManualStyle);
33
- const radiusManualStyleRef = useRef(radiusManualStyle);
30
+ const selectableAttributeRef = useLatest(selectedAttribute);
31
+ const symbologyTabRef = useLatest(symbologyTab);
32
+ const colorStopRowsRef = useLatest(colorStopRows);
33
+ const radiusStopRowsRef = useLatest(radiusStopRows);
34
+ const colorRampOptionsRef = useLatest(colorRampOptions);
35
+ const colorManualStyleRef = useLatest(colorManualStyle);
36
+ const radiusManualStyleRef = useLatest(radiusManualStyle);
34
37
  if (!layerId) {
35
38
  return;
36
39
  }
37
40
  const layer = model.getLayer(layerId);
38
- if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
41
+ const params = useEffectiveSymbologyParams({
42
+ model,
43
+ layerId: layerId,
44
+ layer,
45
+ isStorySegmentOverride,
46
+ segmentId,
47
+ });
48
+ if (!params) {
39
49
  return;
40
50
  }
41
51
  useEffect(() => {
42
52
  updateStopRowsBasedOnLayer();
43
- okSignalPromise.promise.then(okSignal => {
44
- okSignal.connect(handleOk, this);
45
- });
46
- return () => {
47
- okSignalPromise.promise.then(okSignal => {
48
- okSignal.disconnect(handleOk, this);
49
- });
50
- };
51
53
  }, []);
52
54
  useEffect(() => {
53
- var _a;
54
- if ((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.color) {
55
- const strokeColor = layer.parameters.color['stroke-color'];
56
- const circleStrokeColor = layer.parameters.color['circle-stroke-color'];
55
+ if (params.color) {
56
+ const strokeColor = params.color['stroke-color'];
57
+ const circleStrokeColor = params.color['circle-stroke-color'];
57
58
  const isSimpleColor = (val) => typeof val === 'string' && /^#?[0-9A-Fa-f]{3,8}$/.test(val);
58
59
  setColorManualStyle({
59
60
  strokeColor: isSimpleColor(strokeColor)
@@ -61,51 +62,30 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
61
62
  : isSimpleColor(circleStrokeColor)
62
63
  ? circleStrokeColor
63
64
  : '#3399CC',
64
- strokeWidth: layer.parameters.color['stroke-width'] ||
65
- layer.parameters.color['circle-stroke-width'] ||
65
+ strokeWidth: params.color['stroke-width'] ||
66
+ params.color['circle-stroke-width'] ||
66
67
  1.25,
67
68
  });
68
69
  setRadiusManualStyle({
69
- radius: layer.parameters.color['circle-radius'] || 5,
70
+ radius: params.color['circle-radius'] || 5,
70
71
  });
71
72
  }
72
73
  }, [layerId]);
73
- useEffect(() => {
74
- colorStopRowsRef.current = colorStopRows;
75
- radiusStopRowsRef.current = radiusStopRows;
76
- selectableAttributeRef.current = selectedAttribute;
77
- symbologyTabRef.current = symbologyTab;
78
- colorRampOptionsRef.current = colorRampOptions;
79
- }, [
80
- colorStopRows,
81
- radiusStopRows,
82
- selectedAttribute,
83
- symbologyTab,
84
- colorRampOptions,
85
- ]);
86
- useEffect(() => {
87
- colorManualStyleRef.current = colorManualStyle;
88
- radiusManualStyleRef.current = radiusManualStyle;
89
- }, [colorManualStyle, radiusManualStyle]);
90
74
  useEffect(() => {
91
75
  var _a, _b;
92
- const layerParams = layer.parameters;
93
- const attribute = (_b = (_a = layerParams.symbologyState) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : Object.keys(selectableAttributesAndValues)[0];
76
+ const attribute = (_b = (_a = params.symbologyState) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : Object.keys(selectableAttributesAndValues)[0];
94
77
  setSelectedAttribute(attribute);
95
78
  }, [selectableAttributesAndValues]);
96
79
  const updateStopRowsBasedOnLayer = () => {
97
80
  if (!layer) {
98
81
  return;
99
82
  }
100
- setColorStopRows(VectorUtils.buildColorInfo(layer));
83
+ setColorStopRows(VectorUtils.buildColorInfo(params));
101
84
  setRadiusStopRows(VectorUtils.buildRadiusInfo(layer));
102
85
  };
103
86
  const handleOk = () => {
104
87
  var _a, _b, _c;
105
- if (!layer.parameters) {
106
- return;
107
- }
108
- const newStyle = Object.assign({}, layer.parameters.color);
88
+ const newStyle = Object.assign({}, params.color);
109
89
  // Apply color symbology
110
90
  if (colorStopRowsRef.current.length > 0) {
111
91
  const colorExpr = [
@@ -145,8 +125,7 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
145
125
  else {
146
126
  newStyle['circle-radius'] = radiusManualStyleRef.current.radius;
147
127
  }
148
- layer.parameters.color = newStyle;
149
- layer.parameters.symbologyState = {
128
+ const symbologyState = {
150
129
  renderType: 'Graduated',
151
130
  value: selectableAttributeRef.current,
152
131
  method: symbologyTabRef.current,
@@ -155,12 +134,23 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
155
134
  mode: (_c = colorRampOptionsRef.current) === null || _c === void 0 ? void 0 : _c.selectedMode,
156
135
  reverse: reverseRamp,
157
136
  };
158
- if (layer.type === 'HeatmapLayer') {
159
- layer.type = 'VectorLayer';
160
- }
161
- model.sharedModel.updateLayer(layerId, layer);
162
- cancel();
137
+ saveSymbology({
138
+ model,
139
+ layerId,
140
+ isStorySegmentOverride,
141
+ segmentId,
142
+ payload: {
143
+ symbologyState,
144
+ color: newStyle,
145
+ },
146
+ mutateLayerBeforeSave: targetLayer => {
147
+ if (targetLayer.type === 'HeatmapLayer') {
148
+ targetLayer.type = 'VectorLayer';
149
+ }
150
+ },
151
+ });
163
152
  };
153
+ useOkSignal(okSignalPromise, handleOk);
164
154
  const buildColorInfoFromClassification = (selectedMode, numberOfShades, selectedRamp) => {
165
155
  setColorRampOptions({
166
156
  selectedRamp,
@@ -200,10 +190,7 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
200
190
  }
201
191
  };
202
192
  const handleReset = (method) => {
203
- if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
204
- return;
205
- }
206
- const newStyle = Object.assign({}, layer.parameters.color);
193
+ const newStyle = Object.assign({}, params.color);
207
194
  if (method === 'color') {
208
195
  delete newStyle['stroke-color'];
209
196
  setColorStopRows([]);
@@ -213,6 +200,10 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
213
200
  delete newStyle['circle-radius'];
214
201
  setRadiusStopRows([]);
215
202
  }
203
+ const layer = model.getLayer(layerId);
204
+ if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
205
+ return;
206
+ }
216
207
  layer.parameters.color = newStyle;
217
208
  model.sharedModel.updateLayer(layerId, layer);
218
209
  };
@@ -251,7 +242,7 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
251
242
  React.createElement("label", null,
252
243
  React.createElement("input", { type: "checkbox", checked: reverseRamp, onChange: e => setReverseRamp(e.target.checked) }),
253
244
  "Reverse Color Ramp"))),
254
- React.createElement(ColorRampControls, { layerParams: layer.parameters, modeOptions: modeOptions, classifyFunc: buildColorInfoFromClassification, showModeRow: true, showRampSelector: symbologyTab === 'color' }),
245
+ React.createElement(ColorRampControls, { layerParams: params, modeOptions: modeOptions, classifyFunc: buildColorInfoFromClassification, showModeRow: true, showRampSelector: symbologyTab === 'color' }),
255
246
  React.createElement(StopContainer, { selectedMethod: symbologyTab || 'color', stopRows: symbologyTab === 'color' ? colorStopRows : radiusStopRows, setStopRows: symbologyTab === 'color' ? setColorStopRows : setRadiusStopRows })));
256
247
  }
257
248
  })();
@@ -1,12 +1,23 @@
1
1
  import colormap from 'colormap';
2
- import React, { useEffect, useRef, useState } from 'react';
2
+ import React, { useEffect, useState } from 'react';
3
3
  import ColorRampSelector from "../../components/color_ramp/ColorRampSelector";
4
- const Heatmap = ({ model, state, okSignalPromise, cancel, layerId, }) => {
4
+ import { useOkSignal } from "../../hooks/useOkSignal";
5
+ import { saveSymbology, } from "../../symbologyUtils";
6
+ import { useLatest } from "../../../../shared/hooks/useLatest";
7
+ import { useEffectiveSymbologyParams } from '../../hooks/useEffectiveSymbologyParams';
8
+ const Heatmap = ({ model, okSignalPromise, layerId, isStorySegmentOverride, segmentId, }) => {
5
9
  if (!layerId) {
6
10
  return;
7
11
  }
8
12
  const layer = model.getLayer(layerId);
9
- if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
13
+ const params = useEffectiveSymbologyParams({
14
+ model,
15
+ layerId: layerId,
16
+ layer,
17
+ isStorySegmentOverride,
18
+ segmentId,
19
+ });
20
+ if (!params) {
10
21
  return;
11
22
  }
12
23
  const [selectedRamp, setSelectedRamp] = useState('viridis');
@@ -15,40 +26,21 @@ const Heatmap = ({ model, state, okSignalPromise, cancel, layerId, }) => {
15
26
  blur: 15,
16
27
  });
17
28
  const [reverseRamp, setReverseRamp] = useState(false);
18
- const selectedRampRef = useRef('viridis');
19
- const heatmapOptionsRef = useRef({
20
- radius: 8,
21
- blur: 15,
22
- });
23
- const reverseRampRef = useRef(false);
29
+ const selectedRampRef = useLatest(selectedRamp);
30
+ const heatmapOptionsRef = useLatest(heatmapOptions);
31
+ const reverseRampRef = useLatest(reverseRamp);
24
32
  useEffect(() => {
25
33
  populateOptions();
26
- okSignalPromise.promise.then(okSignal => {
27
- okSignal.connect(handleOk, this);
28
- });
29
- return () => {
30
- okSignalPromise.promise.then(okSignal => {
31
- okSignal.disconnect(handleOk, this);
32
- });
33
- };
34
34
  }, []);
35
- useEffect(() => {
36
- selectedRampRef.current = selectedRamp;
37
- heatmapOptionsRef.current = heatmapOptions;
38
- reverseRampRef.current = reverseRamp;
39
- }, [selectedRamp, heatmapOptions, reverseRamp]);
40
35
  const populateOptions = async () => {
41
36
  var _a;
42
37
  let colorRamp;
43
- if ((_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.symbologyState) {
44
- colorRamp = layer.parameters.symbologyState.colorRamp;
38
+ if ((_a = params.symbologyState) === null || _a === void 0 ? void 0 : _a.colorRamp) {
39
+ colorRamp = params.symbologyState.colorRamp;
45
40
  }
46
41
  setSelectedRamp(colorRamp ? colorRamp : 'viridis');
47
42
  };
48
43
  const handleOk = () => {
49
- if (!layer.parameters) {
50
- return;
51
- }
52
44
  let colorMap = colormap({
53
45
  colormap: selectedRampRef.current,
54
46
  nshades: 9,
@@ -62,14 +54,23 @@ const Heatmap = ({ model, state, okSignalPromise, cancel, layerId, }) => {
62
54
  colorRamp: selectedRampRef.current,
63
55
  reverse: reverseRampRef.current,
64
56
  };
65
- layer.parameters.symbologyState = symbologyState;
66
- layer.parameters.color = colorMap;
67
- layer.parameters.blur = heatmapOptionsRef.current.blur;
68
- layer.parameters.radius = heatmapOptionsRef.current.radius;
69
- layer.type = 'HeatmapLayer';
70
- model.sharedModel.updateLayer(layerId, layer);
71
- cancel();
57
+ saveSymbology({
58
+ model,
59
+ layerId,
60
+ isStorySegmentOverride,
61
+ segmentId,
62
+ payload: {
63
+ symbologyState,
64
+ color: colorMap,
65
+ },
66
+ mutateLayerBeforeSave: targetLayer => {
67
+ targetLayer.parameters.blur = heatmapOptionsRef.current.blur;
68
+ targetLayer.parameters.radius = heatmapOptionsRef.current.radius;
69
+ targetLayer.type = 'HeatmapLayer';
70
+ },
71
+ });
72
72
  };
73
+ useOkSignal(okSignalPromise, handleOk);
73
74
  return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
74
75
  React.createElement("p", null, "Represent features based on their density using a heatmap."),
75
76
  React.createElement("div", { className: "jp-gis-symbology-row jp-gis-heatmap" },
@@ -1,7 +1,10 @@
1
- import React, { useEffect, useRef, useState } from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
+ import { useEffectiveSymbologyParams } from "../../hooks/useEffectiveSymbologyParams";
3
+ import { useOkSignal } from "../../hooks/useOkSignal";
4
+ import { saveSymbology, } from "../../symbologyUtils";
5
+ import { useLatest } from "../../../../shared/hooks/useLatest";
2
6
  import { parseColor } from "../../../../tools";
3
- const SimpleSymbol = ({ model, state, okSignalPromise, cancel, layerId, symbologyTab, }) => {
4
- const styleRef = useRef();
7
+ const SimpleSymbol = ({ model, okSignalPromise, layerId, symbologyTab, isStorySegmentOverride, segmentId, }) => {
5
8
  const [style, setStyle] = useState({
6
9
  fillColor: '#3399CC',
7
10
  joinStyle: 'round',
@@ -10,49 +13,30 @@ const SimpleSymbol = ({ model, state, okSignalPromise, cancel, layerId, symbolog
10
13
  strokeWidth: 1.25,
11
14
  radius: 5,
12
15
  });
13
- const joinStyleOptions = ['bevel', 'round', 'miter'];
14
- const capStyleOptions = ['butt', 'round', 'square'];
15
- if (!layerId) {
16
- return;
17
- }
18
- const layer = model.getLayer(layerId);
19
- if (!layer) {
20
- return;
21
- }
16
+ const styleRef = useLatest(style);
17
+ const layer = layerId !== undefined ? model.getLayer(layerId) : null;
18
+ const params = useEffectiveSymbologyParams({
19
+ model,
20
+ layerId: layerId,
21
+ layer,
22
+ isStorySegmentOverride,
23
+ segmentId,
24
+ });
22
25
  useEffect(() => {
23
- if (!layer.parameters) {
26
+ var _a;
27
+ if (!params) {
24
28
  return;
25
29
  }
26
- const initStyle = async () => {
27
- var _a;
28
- if (!layer.parameters) {
29
- return;
30
+ if (((_a = params.symbologyState) === null || _a === void 0 ? void 0 : _a.renderType) === 'Single Symbol' && params.color) {
31
+ const parsed = parseColor(params.color);
32
+ if (parsed) {
33
+ setStyle(parsed);
30
34
  }
31
- const renderType = (_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.symbologyState.renderType;
32
- if (renderType === 'Single Symbol') {
33
- // Parse with fallback logic inside
34
- const parsedStyle = parseColor(layer.parameters.color);
35
- if (parsedStyle) {
36
- setStyle(parsedStyle);
37
- }
38
- }
39
- };
40
- initStyle();
41
- okSignalPromise.promise.then(okSignal => {
42
- okSignal.connect(handleOk, this);
43
- });
44
- return () => {
45
- okSignalPromise.promise.then(okSignal => {
46
- okSignal.disconnect(handleOk, this);
47
- });
48
- };
49
- }, []);
50
- useEffect(() => {
51
- styleRef.current = style;
52
- }, [style]);
35
+ }
36
+ }, [params]);
53
37
  const handleOk = () => {
54
38
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
55
- if (!layer.parameters) {
39
+ if (!layerId || !(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
56
40
  return;
57
41
  }
58
42
  const styleExpr = {
@@ -71,14 +55,28 @@ const SimpleSymbol = ({ model, state, okSignalPromise, cancel, layerId, symbolog
71
55
  const symbologyState = {
72
56
  renderType: 'Single Symbol',
73
57
  };
74
- layer.parameters.symbologyState = symbologyState;
75
- layer.parameters.color = styleExpr;
76
- if (layer.type === 'HeatmapLayer') {
77
- layer.type = 'VectorLayer';
78
- }
79
- model.sharedModel.updateLayer(layerId, layer);
80
- cancel();
58
+ saveSymbology({
59
+ model,
60
+ layerId,
61
+ isStorySegmentOverride,
62
+ segmentId,
63
+ payload: {
64
+ symbologyState,
65
+ color: styleExpr,
66
+ },
67
+ mutateLayerBeforeSave: targetLayer => {
68
+ if (targetLayer.type === 'HeatmapLayer') {
69
+ targetLayer.type = 'VectorLayer';
70
+ }
71
+ },
72
+ });
81
73
  };
74
+ useOkSignal(okSignalPromise, handleOk);
75
+ const joinStyleOptions = ['bevel', 'round', 'miter'];
76
+ const capStyleOptions = ['butt', 'round', 'square'];
77
+ if (!layerId || !layer) {
78
+ return null;
79
+ }
82
80
  const renderColorTab = () => (React.createElement(React.Fragment, null,
83
81
  React.createElement("div", { className: "jp-gis-symbology-row" },
84
82
  React.createElement("label", { htmlFor: 'vector-value-select' }, "Fill Color:"),
@@ -1,4 +1,3 @@
1
- import { getCssVarAsColor } from "../../tools";
2
1
  import { BaseForm } from './baseform';
3
2
  /**
4
3
  * The form to modify story map properties.
@@ -13,21 +12,5 @@ export class StoryEditorPropertiesForm extends BaseForm {
13
12
  uiSchema.presentationTextColor = {
14
13
  'ui:widget': 'color',
15
14
  };
16
- // Set default values from theme CSS variables when not already in data
17
- const schemaProps = schema.properties;
18
- if ((schemaProps === null || schemaProps === void 0 ? void 0 : schemaProps.presentationBgColor) &&
19
- (data === null || data === void 0 ? void 0 : data.presentationBgColor) === undefined) {
20
- const defaultBg = getCssVarAsColor('--jp-layout-color0');
21
- if (defaultBg) {
22
- schemaProps.presentationBgColor.default = defaultBg;
23
- }
24
- }
25
- if ((schemaProps === null || schemaProps === void 0 ? void 0 : schemaProps.presentationTextColor) &&
26
- (data === null || data === void 0 ? void 0 : data.presentationTextColor) === undefined) {
27
- const defaultText = getCssVarAsColor('--jp-ui-font-color0');
28
- if (defaultText) {
29
- schemaProps.presentationTextColor.default = defaultText;
30
- }
31
- }
32
15
  }
33
16
  }
@@ -5,6 +5,10 @@ import { IChangeEvent, ISubmitEvent } from '@rjsf/core';
5
5
  import { RJSFSchema, UiSchema } from '@rjsf/utils';
6
6
  import * as React from 'react';
7
7
  import { IDict } from "../../types";
8
+ export interface IJupyterGISFormContext<TFormData = IDict | undefined> {
9
+ model: IJupyterGISModel;
10
+ formData: TFormData;
11
+ }
8
12
  export interface IBaseFormStates {
9
13
  schema?: RJSFSchema;
10
14
  extraErrors?: any;
@@ -59,6 +63,8 @@ export interface IBaseFormProps {
59
63
  * It will be up to the user of this class to actually perform the creation/edit using syncdata.
60
64
  */
61
65
  export declare class BaseForm extends React.Component<IBaseFormProps, IBaseFormStates> {
66
+ /** Skip syncData for the initial onChange (RJSF populating form), only sync on user edits. */
67
+ private isInitialLoadRef;
62
68
  constructor(props: IBaseFormProps);
63
69
  componentDidUpdate(prevProps: IBaseFormProps, prevState: IBaseFormStates): void;
64
70
  componentDidMount(): void;
@@ -9,13 +9,18 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { Slider } from '@jupyter/react-components';
13
12
  import { FormComponent } from '@jupyterlab/ui-components';
14
13
  import validatorAjv8 from '@rjsf/validator-ajv8';
15
14
  import * as React from 'react';
16
15
  import { deepCopy } from "../../tools";
16
+ import { LayerSelect } from './components/LayerSelect';
17
+ import OpacitySlider from './components/OpacitySlider';
17
18
  const WrappedFormComponent = props => {
18
- const { fields } = props, rest = __rest(props, ["fields"]);
19
+ const rest = __rest(props, []);
20
+ const fields = {
21
+ opacity: OpacitySlider,
22
+ layerSelect: LayerSelect,
23
+ };
19
24
  return (React.createElement(FormComponent, Object.assign({}, rest, { validator: validatorAjv8, fields: Object.assign({}, fields) })));
20
25
  };
21
26
  /**
@@ -26,6 +31,8 @@ const WrappedFormComponent = props => {
26
31
  export class BaseForm extends React.Component {
27
32
  constructor(props) {
28
33
  super(props);
34
+ /** Skip syncData for the initial onChange (RJSF populating form), only sync on user edits. */
35
+ this.isInitialLoadRef = true;
29
36
  this.currentFormData = deepCopy(this.props.sourceData);
30
37
  this.state = {
31
38
  schema: props.schema,
@@ -37,6 +44,7 @@ export class BaseForm extends React.Component {
37
44
  this.currentFormData = deepCopy(this.props.sourceData);
38
45
  const schema = deepCopy(this.props.schema);
39
46
  this.setState(old => (Object.assign(Object.assign({}, old), { schema })));
47
+ this.isInitialLoadRef = true;
40
48
  }
41
49
  }
42
50
  componentDidMount() {
@@ -72,40 +80,7 @@ export class BaseForm extends React.Component {
72
80
  }
73
81
  if (k === 'opacity') {
74
82
  uiSchema[k] = {
75
- 'ui:field': (props) => {
76
- const [inputValue, setInputValue] = React.useState(props.formData.toFixed(1));
77
- React.useEffect(() => {
78
- setInputValue(props.formData.toFixed(1));
79
- }, [props.formData]);
80
- const handleSliderChange = (event) => {
81
- const target = event.target;
82
- if (target && '_value' in target) {
83
- const sliderValue = parseFloat(target._value); // Slider value is in 0–10 range
84
- const normalizedValue = sliderValue / 10; // Normalize to 0.1–1 range
85
- props.onChange(normalizedValue);
86
- }
87
- };
88
- const handleInputChange = (event) => {
89
- const value = event.target.value;
90
- setInputValue(value);
91
- const parsedValue = parseFloat(value);
92
- if (!isNaN(parsedValue) &&
93
- parsedValue >= 0.1 &&
94
- parsedValue <= 1) {
95
- props.onChange(parsedValue);
96
- }
97
- };
98
- return (React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' } },
99
- React.createElement(Slider, { min: 1, max: 10, step: 1, valueAsNumber: props.formData * 10, onChange: handleSliderChange }),
100
- React.createElement("input", { type: "number", value: inputValue, step: 0.1, min: 0.1, max: 1, onChange: handleInputChange, style: {
101
- width: '50px',
102
- textAlign: 'center',
103
- border: '1px solid #ccc',
104
- borderRadius: '4px',
105
- padding: '4px',
106
- marginBottom: '5px',
107
- } })));
108
- },
83
+ 'ui:field': 'opacity',
109
84
  };
110
85
  }
111
86
  // Don't show readOnly properties when it's a form for updating an object
@@ -154,7 +129,12 @@ export class BaseForm extends React.Component {
154
129
  this.props.formErrorSignal.emit(extraErrors);
155
130
  }
156
131
  if (this.props.formContext === 'update') {
157
- this.syncData(this.currentFormData);
132
+ if (!this.isInitialLoadRef) {
133
+ this.syncData(this.currentFormData);
134
+ }
135
+ else {
136
+ this.isInitialLoadRef = false;
137
+ }
158
138
  }
159
139
  }
160
140
  onFormBlur(id, value) {
@@ -191,7 +171,10 @@ export class BaseForm extends React.Component {
191
171
  (_a = submitRef.current) === null || _a === void 0 ? void 0 : _a.click();
192
172
  }
193
173
  } },
194
- React.createElement(WrappedFormComponent, { schema: schema, uiSchema: uiSchema, formData: formData, onSubmit: this.onFormSubmit.bind(this), onChange: this.onFormChange.bind(this), onBlur: this.onFormBlur.bind(this), ok: this.props.ok, cancel: this.props.cancel, liveValidate: true, children: React.createElement("button", { ref: submitRef, type: "submit", style: { display: 'none' } }), extraErrors: this.state.extraErrors }))));
174
+ React.createElement(WrappedFormComponent, { schema: schema, uiSchema: uiSchema, formData: formData, formContext: {
175
+ model: this.props.model,
176
+ formData,
177
+ }, onSubmit: this.onFormSubmit.bind(this), onChange: this.onFormChange.bind(this), onBlur: this.onFormBlur.bind(this), ok: this.props.ok, cancel: this.props.cancel, liveValidate: true, children: React.createElement("button", { ref: submitRef, type: "submit", style: { display: 'none' } }), extraErrors: this.state.extraErrors }))));
195
178
  }
196
179
  }
197
180
  }
@@ -0,0 +1,7 @@
1
+ import { FieldProps } from '@rjsf/utils';
2
+ import React from 'react';
3
+ /**
4
+ * Simple select populated with layers (valid types only).
5
+ * Used as the targetLayer field inside layerOverride array items.
6
+ */
7
+ export declare function LayerSelect(props: FieldProps): React.JSX.Element | null;
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ function extractlayerOverrideIndex(idSchema) {
3
+ var _a;
4
+ const id = (_a = idSchema === null || idSchema === void 0 ? void 0 : idSchema.$id) !== null && _a !== void 0 ? _a : '';
5
+ const match = id.match(/layerOverride_(\d+)/);
6
+ return match ? parseInt(match[1], 10) : undefined;
7
+ }
8
+ /**
9
+ * Simple select populated with layers (valid types only).
10
+ * Used as the targetLayer field inside layerOverride array items.
11
+ */
12
+ export function LayerSelect(props) {
13
+ var _a, _b, _c, _d, _e, _f;
14
+ const { idSchema, formContext, formData, onChange } = props;
15
+ const context = formContext;
16
+ const model = context === null || context === void 0 ? void 0 : context.model;
17
+ const fullFormData = (_a = context === null || context === void 0 ? void 0 : context.formData) !== null && _a !== void 0 ? _a : formData;
18
+ const arrayIndex = extractlayerOverrideIndex(idSchema !== null && idSchema !== void 0 ? idSchema : {});
19
+ const value = arrayIndex !== undefined && ((_b = fullFormData === null || fullFormData === void 0 ? void 0 : fullFormData.layerOverride) === null || _b === void 0 ? void 0 : _b[arrayIndex])
20
+ ? ((_c = fullFormData.layerOverride[arrayIndex].targetLayer) !== null && _c !== void 0 ? _c : '')
21
+ : '';
22
+ if (!model) {
23
+ return null;
24
+ }
25
+ const layerOverride = (_d = fullFormData === null || fullFormData === void 0 ? void 0 : fullFormData.layerOverride) !== null && _d !== void 0 ? _d : [];
26
+ const currentTargetLayer = arrayIndex !== undefined
27
+ ? (_f = (_e = fullFormData === null || fullFormData === void 0 ? void 0 : fullFormData.layerOverride) === null || _e === void 0 ? void 0 : _e[arrayIndex]) === null || _f === void 0 ? void 0 : _f.targetLayer
28
+ : undefined;
29
+ const usedTargetLayerIds = new Set(layerOverride
30
+ .filter((_, i) => i !== arrayIndex)
31
+ .map(override => override.targetLayer)
32
+ .filter(id => id !== undefined && id !== '')
33
+ .filter(id => id !== currentTargetLayer));
34
+ const availableLayers = model.getLayers();
35
+ const optionsList = Object.entries(availableLayers).filter(([layerId]) => !usedTargetLayerIds.has(layerId));
36
+ const handleChange = (e) => {
37
+ const newValue = e.target.value;
38
+ onChange(newValue === '' ? undefined : newValue);
39
+ };
40
+ return (React.createElement("select", { value: value !== null && value !== void 0 ? value : '', onChange: handleChange, style: { width: '100%' } },
41
+ React.createElement("option", { value: "" }, "Select a layer"),
42
+ optionsList.map(([layerId, layer]) => (React.createElement("option", { key: layerId, value: layerId }, layer.name.charAt(0).toUpperCase() + layer.name.slice(1))))));
43
+ }
@@ -0,0 +1,4 @@
1
+ import { FieldProps } from '@rjsf/utils';
2
+ import React from 'react';
3
+ declare function OpacitySlider({ formData, onChange }: FieldProps<number>): React.JSX.Element;
4
+ export default OpacitySlider;