@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,19 +1,29 @@
1
1
  import { Button } from '@jupyterlab/ui-components';
2
- import React, { useEffect, useRef, useState } from 'react';
2
+ import React, { useEffect, useState } from 'react';
3
3
  import { GeoTiffClassifications } from "../../classificationModes";
4
4
  import ColorRampControls from "../../components/color_ramp/ColorRampControls";
5
5
  import StopRow from "../../components/color_stops/StopRow";
6
6
  import useGetBandInfo from "../../hooks/useGetBandInfo";
7
- import { Utils } from "../../symbologyUtils";
7
+ import { useOkSignal } from "../../hooks/useOkSignal";
8
+ import { saveSymbology, Utils, } from "../../symbologyUtils";
8
9
  import BandRow from "../components/BandRow";
9
10
  import { LoadingOverlay } from "../../../../shared/components/loading";
11
+ import { useLatest } from "../../../../shared/hooks/useLatest";
10
12
  import { GlobalStateDbManager } from "../../../../store";
11
- const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) => {
13
+ import { useEffectiveSymbologyParams } from '../../hooks/useEffectiveSymbologyParams';
14
+ const SingleBandPseudoColor = ({ model, okSignalPromise, layerId, isStorySegmentOverride, segmentId, }) => {
12
15
  if (!layerId) {
13
16
  return;
14
17
  }
15
18
  const layer = model.getLayer(layerId);
16
- if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
19
+ const params = useEffectiveSymbologyParams({
20
+ model,
21
+ layerId: layerId,
22
+ layer,
23
+ isStorySegmentOverride,
24
+ segmentId,
25
+ });
26
+ if (!params || !layer) {
17
27
  return;
18
28
  }
19
29
  const functions = ['discrete', 'linear', 'exact'];
@@ -29,56 +39,40 @@ const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) =>
29
39
  const [stopRows, setStopRows] = useState([]);
30
40
  const [selectedFunction, setSelectedFunction] = useState('linear');
31
41
  const [colorRampOptions, setColorRampOptions] = useState();
32
- const stopRowsRef = useRef();
33
- const bandRowsRef = useRef([]);
34
- const selectedFunctionRef = useRef();
35
- const colorRampOptionsRef = useRef();
36
- const selectedBandRef = useRef();
42
+ const stopRowsRef = useLatest(stopRows);
43
+ const bandRowsRef = useLatest(bandRows);
44
+ const selectedFunctionRef = useLatest(selectedFunction);
45
+ const colorRampOptionsRef = useLatest(colorRampOptions);
46
+ const selectedBandRef = useLatest(selectedBand);
37
47
  useEffect(() => {
38
48
  populateOptions();
39
- okSignalPromise.promise.then(okSignal => {
40
- okSignal.connect(handleOk);
41
- });
42
- return () => {
43
- okSignalPromise.promise.then(okSignal => {
44
- okSignal.disconnect(handleOk, this);
45
- });
46
- };
47
49
  }, []);
48
50
  useEffect(() => {
49
- bandRowsRef.current = bandRows;
50
51
  buildColorInfo();
51
52
  }, [bandRows]);
52
- useEffect(() => {
53
- stopRowsRef.current = stopRows;
54
- selectedFunctionRef.current = selectedFunction;
55
- colorRampOptionsRef.current = colorRampOptions;
56
- selectedBandRef.current = selectedBand;
57
- }, [stopRows, selectedFunction, colorRampOptions, selectedBand, layerState]);
58
53
  const populateOptions = async () => {
59
54
  var _a, _b, _c, _d;
60
55
  const layerState = (await (stateDb === null || stateDb === void 0 ? void 0 : stateDb.fetch(`jupytergis:${layerId}`)));
61
56
  setLayerState(layerState);
62
- const layerParams = layer.parameters;
63
- const band = (_b = (_a = layerParams.symbologyState) === null || _a === void 0 ? void 0 : _a.band) !== null && _b !== void 0 ? _b : 1;
64
- const interpolation = (_d = (_c = layerParams.symbologyState) === null || _c === void 0 ? void 0 : _c.interpolation) !== null && _d !== void 0 ? _d : 'linear';
57
+ const band = (_b = (_a = params.symbologyState) === null || _a === void 0 ? void 0 : _a.band) !== null && _b !== void 0 ? _b : 1;
58
+ const interpolation = (_d = (_c = params.symbologyState) === null || _c === void 0 ? void 0 : _c.interpolation) !== null && _d !== void 0 ? _d : 'linear';
65
59
  setSelectedBand(band);
66
60
  setSelectedFunction(interpolation);
67
61
  };
68
62
  const buildColorInfo = () => {
69
- var _a;
70
63
  // This it to parse a color object on the layer
71
- if (!((_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.color) || !layerState) {
64
+ if (!params.color || !layerState) {
72
65
  return;
73
66
  }
74
- const color = layer.parameters.color;
67
+ const color = params.color;
75
68
  // If color is a string we don't need to parse
76
- if (typeof color === 'string') {
69
+ // Otherwise color expression should be an array (e.g. ['interpolate', ...] or ['case', ...])
70
+ if (!Array.isArray(color)) {
77
71
  return;
78
72
  }
73
+ // ! wtf ? dont use statedb just read from the file??
79
74
  const isQuantile = layerState.selectedMode === 'quantile';
80
75
  const valueColorPairs = [];
81
- // So if it's not a string then it's an array and we parse
82
76
  // Color[0] is the operator used for the color expression
83
77
  switch (color[0]) {
84
78
  case 'interpolate': {
@@ -89,7 +83,7 @@ const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) =>
89
83
  // Sixth and on is value:color pairs
90
84
  for (let i = 5; i < color.length; i += 2) {
91
85
  const obj = {
92
- stop: scaleValue(color[i], isQuantile),
86
+ stop: scaleValue(Number(color[i]), isQuantile),
93
87
  output: color[i + 1],
94
88
  };
95
89
  valueColorPairs.push(obj);
@@ -105,8 +99,11 @@ const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) =>
105
99
  // Fifth is color
106
100
  // Last element is fallback value
107
101
  for (let i = 3; i < color.length - 1; i += 2) {
102
+ const stopVal = Number(Array.isArray(color[i])
103
+ ? color[i][2]
104
+ : color[i]);
108
105
  const obj = {
109
- stop: scaleValue(color[i][2], isQuantile),
106
+ stop: scaleValue(stopVal, isQuantile),
110
107
  output: color[i + 1],
111
108
  };
112
109
  valueColorPairs.push(obj);
@@ -118,26 +115,11 @@ const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) =>
118
115
  };
119
116
  const handleOk = () => {
120
117
  var _a, _b, _c, _d, _e, _f, _g, _h;
121
- // Update source
122
118
  const bandRow = bandRowsRef.current[selectedBand - 1];
123
119
  if (!bandRow) {
124
120
  return;
125
121
  }
126
- const sourceId = (_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.source;
127
- const source = model.getSource(sourceId);
128
- if (!source || !source.parameters) {
129
- return;
130
- }
131
- const isQuantile = ((_b = colorRampOptionsRef.current) === null || _b === void 0 ? void 0 : _b.selectedMode) === 'quantile';
132
- const sourceInfo = source.parameters.urls[0];
133
- sourceInfo.min = bandRow.stats.minimum;
134
- sourceInfo.max = bandRow.stats.maximum;
135
- source.parameters.urls[0] = sourceInfo;
136
- model.sharedModel.updateSource(sourceId, source);
137
- // Update layer
138
- if (!layer.parameters) {
139
- return;
140
- }
122
+ const isQuantile = ((_a = colorRampOptionsRef.current) === null || _a === void 0 ? void 0 : _a.selectedMode) === 'quantile';
141
123
  // TODO: Different viewers will have different types
142
124
  let colorExpr = [];
143
125
  switch (selectedFunctionRef.current) {
@@ -146,7 +128,7 @@ const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) =>
146
128
  colorExpr.push(['band', selectedBand]);
147
129
  // Set NoData values to transparent
148
130
  colorExpr.push(0.0, [0.0, 0.0, 0.0, 0.0]);
149
- (_c = stopRowsRef.current) === null || _c === void 0 ? void 0 : _c.map(stop => {
131
+ (_b = stopRowsRef.current) === null || _b === void 0 ? void 0 : _b.map(stop => {
150
132
  colorExpr.push(unscaleValue(stop.stop, isQuantile));
151
133
  colorExpr.push(stop.output);
152
134
  });
@@ -157,7 +139,7 @@ const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) =>
157
139
  // Set NoData values to transparent
158
140
  colorExpr.push(['==', ['band', selectedBand], 0]);
159
141
  colorExpr.push([0.0, 0.0, 0.0, 0.0]);
160
- (_d = stopRowsRef.current) === null || _d === void 0 ? void 0 : _d.map(stop => {
142
+ (_c = stopRowsRef.current) === null || _c === void 0 ? void 0 : _c.map(stop => {
161
143
  colorExpr.push([
162
144
  '<=',
163
145
  ['band', selectedBand],
@@ -174,7 +156,7 @@ const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) =>
174
156
  // Set NoData values to transparent
175
157
  colorExpr.push(['==', ['band', selectedBand], 0]);
176
158
  colorExpr.push([0.0, 0.0, 0.0, 0.0]);
177
- (_e = stopRowsRef.current) === null || _e === void 0 ? void 0 : _e.map(stop => {
159
+ (_d = stopRowsRef.current) === null || _d === void 0 ? void 0 : _d.map(stop => {
178
160
  colorExpr.push([
179
161
  '==',
180
162
  ['band', selectedBand],
@@ -191,16 +173,40 @@ const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) =>
191
173
  renderType: 'Singleband Pseudocolor',
192
174
  band: selectedBandRef.current,
193
175
  interpolation: selectedFunctionRef.current,
194
- colorRamp: (_f = colorRampOptionsRef.current) === null || _f === void 0 ? void 0 : _f.selectedRamp,
195
- nClasses: (_g = colorRampOptionsRef.current) === null || _g === void 0 ? void 0 : _g.numberOfShades,
196
- mode: (_h = colorRampOptionsRef.current) === null || _h === void 0 ? void 0 : _h.selectedMode,
176
+ colorRamp: (_e = colorRampOptionsRef.current) === null || _e === void 0 ? void 0 : _e.selectedRamp,
177
+ nClasses: ((_f = colorRampOptionsRef.current) === null || _f === void 0 ? void 0 : _f.numberOfShades) !== undefined
178
+ ? String(colorRampOptionsRef.current.numberOfShades)
179
+ : undefined,
180
+ mode: (_g = colorRampOptionsRef.current) === null || _g === void 0 ? void 0 : _g.selectedMode,
197
181
  };
198
- layer.parameters.symbologyState = symbologyState;
199
- layer.parameters.color = colorExpr;
200
- layer.type = 'WebGlLayer';
201
- model.sharedModel.updateLayer(layerId, layer);
202
- cancel();
182
+ if (!isStorySegmentOverride) {
183
+ // Update source
184
+ const sourceId = (_h = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _h === void 0 ? void 0 : _h.source;
185
+ const source = model.getSource(sourceId);
186
+ if (!source || !source.parameters) {
187
+ return;
188
+ }
189
+ const sourceInfo = source.parameters.urls[0];
190
+ sourceInfo.min = bandRow.stats.minimum;
191
+ sourceInfo.max = bandRow.stats.maximum;
192
+ source.parameters.urls[0] = sourceInfo;
193
+ model.sharedModel.updateSource(sourceId, source);
194
+ }
195
+ saveSymbology({
196
+ model,
197
+ layerId,
198
+ isStorySegmentOverride,
199
+ segmentId,
200
+ payload: {
201
+ symbologyState,
202
+ color: colorExpr,
203
+ },
204
+ mutateLayerBeforeSave: targetLayer => {
205
+ targetLayer.type = 'WebGlLayer';
206
+ },
207
+ });
203
208
  };
209
+ useOkSignal(okSignalPromise, handleOk);
204
210
  const addStopRow = () => {
205
211
  setStopRows([
206
212
  {
@@ -274,7 +280,7 @@ const SingleBandPseudoColor = ({ model, okSignalPromise, cancel, layerId, }) =>
274
280
  React.createElement("select", { name: "function-select", id: "function-select", className: "jp-mod-styled", value: selectedFunction, style: { textTransform: 'capitalize' }, onChange: event => {
275
281
  setSelectedFunction(event.target.value);
276
282
  } }, functions.map((func, funcIndex) => (React.createElement("option", { key: func, value: func, style: { textTransform: 'capitalize' } }, func)))))),
277
- bandRows.length > 0 && (React.createElement(ColorRampControls, { layerParams: layer.parameters, modeOptions: modeOptions, classifyFunc: buildColorInfoFromClassification, showModeRow: true, showRampSelector: true })),
283
+ bandRows.length > 0 && (React.createElement(ColorRampControls, { layerParams: params, modeOptions: modeOptions, classifyFunc: buildColorInfoFromClassification, showModeRow: true, showRampSelector: true })),
278
284
  React.createElement("div", { className: "jp-gis-stop-container" },
279
285
  React.createElement("div", { className: "jp-gis-stop-labels", style: { display: 'flex', gap: 6 } },
280
286
  React.createElement("span", { style: { flex: '0 0 18%' } },
@@ -47,35 +47,47 @@ const getSelectableRenderTypes = (featureProperties, layerType) => {
47
47
  ]);
48
48
  return Object.fromEntries(entries);
49
49
  };
50
- const useLayerRenderType = (layer, setSelectedRenderType) => useEffect(() => {
51
- var _a, _b;
52
- let renderType = (_b = (_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.symbologyState) === null || _b === void 0 ? void 0 : _b.renderType;
53
- if (!renderType) {
54
- renderType = layer.type === 'HeatmapLayer' ? 'Heatmap' : 'Single Symbol';
55
- }
56
- setSelectedRenderType(renderType);
57
- }, []);
58
- const VectorRendering = ({ model, state, okSignalPromise, cancel, layerId, }) => {
59
- const [selectedRenderType, setSelectedRenderType] = useState();
50
+ const VectorRendering = ({ model, okSignalPromise, layerId, isStorySegmentOverride = false, segmentId, }) => {
60
51
  const [symbologyTab, setSymbologyTab] = useState('color');
61
- if (!layerId) {
62
- return;
63
- }
64
- const layer = model.getLayer(layerId);
65
- if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
66
- return;
67
- }
52
+ const [selectedRenderType, setSelectedRenderType] = useState();
53
+ const layer = layerId !== undefined ? model.getLayer(layerId) : null;
54
+ useEffect(() => {
55
+ var _a, _b, _c, _d, _e;
56
+ if (!layer) {
57
+ return;
58
+ }
59
+ let renderType;
60
+ if (isStorySegmentOverride) {
61
+ const segment = segmentId ? model.getLayer(segmentId) : undefined;
62
+ if (!segment) {
63
+ return;
64
+ }
65
+ const override = (_b = (_a = segment.parameters) === null || _a === void 0 ? void 0 : _a.layerOverride) === null || _b === void 0 ? void 0 : _b.find((override) => override.targetLayer === layerId);
66
+ if (!override) {
67
+ return;
68
+ }
69
+ renderType = (_c = override.symbologyState) === null || _c === void 0 ? void 0 : _c.renderType;
70
+ }
71
+ else {
72
+ renderType = (_e = (_d = layer.parameters) === null || _d === void 0 ? void 0 : _d.symbologyState) === null || _e === void 0 ? void 0 : _e.renderType;
73
+ }
74
+ if (!renderType) {
75
+ renderType = layer.type === 'HeatmapLayer' ? 'Heatmap' : 'Single Symbol';
76
+ }
77
+ setSelectedRenderType(renderType);
78
+ }, []);
68
79
  const { featureProperties, isLoading: featuresLoading } = useGetProperties({
69
80
  layerId,
70
81
  model: model,
71
82
  });
72
- useLayerRenderType(layer, setSelectedRenderType);
83
+ if (!layerId || !(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
84
+ return null;
85
+ }
73
86
  if (featuresLoading) {
74
87
  return React.createElement("p", null, "Loading...");
75
88
  }
76
89
  if (selectedRenderType === undefined) {
77
- // typeguard
78
- return;
90
+ return null;
79
91
  }
80
92
  const selectableRenderTypes = getSelectableRenderTypes(featureProperties, layer.type);
81
93
  const selectedRenderTypeProps = selectableRenderTypes[selectedRenderType];
@@ -90,7 +102,7 @@ const VectorRendering = ({ model, state, okSignalPromise, cancel, layerId, }) =>
90
102
  .filter(([renderType, renderTypeProps]) => renderTypeProps.layerTypeSupported &&
91
103
  !(renderType === 'Heatmap' && symbologyTab === 'radius'))
92
104
  .map(([renderType, _]) => (React.createElement("option", { key: renderType, value: renderType }, renderType)))))),
93
- React.createElement(selectedRenderTypeProps.component, Object.assign({ model: model, state: state, okSignalPromise: okSignalPromise, cancel: cancel, layerId: layerId }, (selectedRenderTypeProps.isTabbed ? { symbologyTab } : {}), (selectedRenderTypeProps.selectableAttributesAndValues
105
+ React.createElement(selectedRenderTypeProps.component, Object.assign({ model: model, okSignalPromise: okSignalPromise, layerId: layerId, isStorySegmentOverride: isStorySegmentOverride, segmentId: segmentId }, (selectedRenderTypeProps.isTabbed ? { symbologyTab } : {}), (selectedRenderTypeProps.selectableAttributesAndValues
94
106
  ? {
95
107
  selectableAttributesAndValues: selectedRenderTypeProps.selectableAttributesAndValues,
96
108
  }
@@ -1,8 +1,11 @@
1
- import React, { useEffect, useRef, useState } from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
+ import { useOkSignal } from "../../hooks/useOkSignal";
3
+ import { saveSymbology } from "../../symbologyUtils";
2
4
  import ValueSelect from "../components/ValueSelect";
3
- const Canonical = ({ model, state, okSignalPromise, cancel, layerId, selectableAttributesAndValues, }) => {
4
- const selectedValueRef = useRef();
5
+ import { useLatest } from "../../../../shared/hooks/useLatest";
6
+ const Canonical = ({ model, okSignalPromise, layerId, selectableAttributesAndValues, isStorySegmentOverride, segmentId, }) => {
5
7
  const [selectedValue, setSelectedValue] = useState('');
8
+ const selectedValueRef = useLatest(selectedValue);
6
9
  if (!layerId) {
7
10
  return;
8
11
  }
@@ -10,30 +13,17 @@ const Canonical = ({ model, state, okSignalPromise, cancel, layerId, selectableA
10
13
  if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
11
14
  return;
12
15
  }
13
- useEffect(() => {
14
- okSignalPromise.promise.then(okSignal => {
15
- okSignal.connect(handleOk, this);
16
- });
17
- return () => {
18
- okSignalPromise.promise.then(okSignal => {
19
- okSignal.disconnect(handleOk, this);
20
- });
21
- };
22
- }, [selectedValue]);
23
16
  useEffect(() => {
24
17
  var _a, _b;
25
18
  const layerParams = layer.parameters;
26
19
  const value = (_b = (_a = layerParams.symbologyState) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : Object.keys(selectableAttributesAndValues)[0];
27
20
  setSelectedValue(value);
28
21
  }, [selectableAttributesAndValues]);
29
- useEffect(() => {
30
- selectedValueRef.current = selectedValue;
31
- }, [selectedValue]);
32
22
  const handleOk = () => {
33
23
  if (!layer.parameters) {
34
24
  return;
35
25
  }
36
- const colorExpr = ['get', selectedValue];
26
+ const colorExpr = ['get', selectedValueRef.current];
37
27
  const newStyle = Object.assign({}, layer.parameters.color);
38
28
  newStyle['fill-color'] = colorExpr;
39
29
  newStyle['stroke-color'] = colorExpr;
@@ -42,14 +32,23 @@ const Canonical = ({ model, state, okSignalPromise, cancel, layerId, selectableA
42
32
  renderType: 'Canonical',
43
33
  value: selectedValueRef.current,
44
34
  };
45
- layer.parameters.symbologyState = symbologyState;
46
- layer.parameters.color = newStyle;
47
- if (layer.type === 'HeatmapLayer') {
48
- layer.type = 'VectorLayer';
49
- }
50
- model.sharedModel.updateLayer(layerId, layer);
51
- cancel();
35
+ saveSymbology({
36
+ model,
37
+ layerId,
38
+ isStorySegmentOverride,
39
+ segmentId,
40
+ payload: {
41
+ symbologyState,
42
+ color: newStyle,
43
+ },
44
+ mutateLayerBeforeSave: targetLayer => {
45
+ if (targetLayer.type === 'HeatmapLayer') {
46
+ targetLayer.type = 'VectorLayer';
47
+ }
48
+ },
49
+ });
52
50
  };
51
+ useOkSignal(okSignalPromise, handleOk);
53
52
  const body = (() => {
54
53
  var _a;
55
54
  if (((_a = Object.keys(selectableAttributesAndValues)) === null || _a === void 0 ? void 0 : _a.length) === 0) {
@@ -1,12 +1,12 @@
1
- import React, { useEffect, useRef, useState } from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
  import ColorRampControls from "../../components/color_ramp/ColorRampControls";
3
3
  import StopContainer from "../../components/color_stops/StopContainer";
4
- import { Utils, VectorUtils } from "../../symbologyUtils";
4
+ import { useOkSignal } from "../../hooks/useOkSignal";
5
+ import { Utils, VectorUtils, saveSymbology, } from "../../symbologyUtils";
5
6
  import ValueSelect from "../components/ValueSelect";
6
- const Categorized = ({ model, state, okSignalPromise, cancel, layerId, symbologyTab, selectableAttributesAndValues, }) => {
7
- const selectedAttributeRef = useRef();
8
- const stopRowsRef = useRef();
9
- const colorRampOptionsRef = useRef();
7
+ import { useLatest } from "../../../../shared/hooks/useLatest";
8
+ import { useEffectiveSymbologyParams } from '../../hooks/useEffectiveSymbologyParams';
9
+ const Categorized = ({ model, okSignalPromise, layerId, symbologyTab, selectableAttributesAndValues, isStorySegmentOverride, segmentId, }) => {
10
10
  const [selectedAttribute, setSelectedAttribute] = useState('');
11
11
  const [stopRows, setStopRows] = useState([]);
12
12
  const [colorRampOptions, setColorRampOptions] = useState();
@@ -16,34 +16,35 @@ const Categorized = ({ model, state, okSignalPromise, cancel, layerId, symbology
16
16
  strokeWidth: 1.25,
17
17
  radius: 5,
18
18
  });
19
- const manualStyleRef = useRef(manualStyle);
19
+ const manualStyleRef = useLatest(manualStyle);
20
20
  const [reverseRamp, setReverseRamp] = useState(false);
21
+ const selectedAttributeRef = useLatest(selectedAttribute);
22
+ const stopRowsRef = useLatest(stopRows);
23
+ const colorRampOptionsRef = useLatest(colorRampOptions);
21
24
  if (!layerId) {
22
25
  return;
23
26
  }
24
27
  const layer = model.getLayer(layerId);
25
- if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
28
+ const params = useEffectiveSymbologyParams({
29
+ model,
30
+ layerId: layerId,
31
+ layer,
32
+ isStorySegmentOverride,
33
+ segmentId,
34
+ });
35
+ if (!params) {
26
36
  return;
27
37
  }
28
38
  useEffect(() => {
29
- const valueColorPairs = VectorUtils.buildColorInfo(layer);
39
+ const valueColorPairs = VectorUtils.buildColorInfo(params);
30
40
  setStopRows(valueColorPairs);
31
- okSignalPromise.promise.then(okSignal => {
32
- okSignal.connect(handleOk, this);
33
- });
34
- return () => {
35
- okSignalPromise.promise.then(okSignal => {
36
- okSignal.disconnect(handleOk, this);
37
- });
38
- };
39
41
  }, []);
40
42
  useEffect(() => {
41
- var _a;
42
- if ((_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.color) {
43
- const fillColor = layer.parameters.color['fill-color'];
44
- const circleFillColor = layer.parameters.color['circle-fill-color'];
45
- const strokeColor = layer.parameters.color['stroke-color'];
46
- const circleStrokeColor = layer.parameters.color['circle-stroke-color'];
43
+ if (params.color) {
44
+ const fillColor = params.color['fill-color'];
45
+ const circleFillColor = params.color['circle-fill-color'];
46
+ const strokeColor = params.color['stroke-color'];
47
+ const circleStrokeColor = params.color['circle-stroke-color'];
47
48
  const isSimpleColor = (val) => typeof val === 'string' && /^#?[0-9A-Fa-f]{3,8}$/.test(val);
48
49
  setManualStyle({
49
50
  fillColor: isSimpleColor(fillColor)
@@ -56,28 +57,19 @@ const Categorized = ({ model, state, okSignalPromise, cancel, layerId, symbology
56
57
  : isSimpleColor(circleStrokeColor)
57
58
  ? circleStrokeColor
58
59
  : '#3399CC',
59
- strokeWidth: layer.parameters.color['stroke-width'] ||
60
- layer.parameters.color['circle-stroke-width'] ||
60
+ strokeWidth: params.color['stroke-width'] ||
61
+ params.color['circle-stroke-width'] ||
61
62
  1.25,
62
- radius: layer.parameters.color['circle-radius'] || 5,
63
+ radius: params.color['circle-radius'] || 5,
63
64
  });
64
65
  }
65
66
  }, [layerId]);
66
- useEffect(() => {
67
- manualStyleRef.current = manualStyle;
68
- }, [manualStyle]);
69
67
  useEffect(() => {
70
68
  var _a, _b;
71
69
  // We only want number values here
72
- const layerParams = layer.parameters;
73
- const attribute = (_b = (_a = layerParams.symbologyState) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : Object.keys(selectableAttributesAndValues)[0];
70
+ const attribute = (_b = (_a = params.symbologyState) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : Object.keys(selectableAttributesAndValues)[0];
74
71
  setSelectedAttribute(attribute);
75
72
  }, [selectableAttributesAndValues]);
76
- useEffect(() => {
77
- selectedAttributeRef.current = selectedAttribute;
78
- stopRowsRef.current = stopRows;
79
- colorRampOptionsRef.current = colorRampOptions;
80
- }, [selectedAttribute, stopRows, colorRampOptions]);
81
73
  const buildColorInfoFromClassification = (selectedMode, numberOfShades, selectedRamp, setIsLoading) => {
82
74
  setColorRampOptions({
83
75
  selectedFunction: '',
@@ -91,10 +83,7 @@ const Categorized = ({ model, state, okSignalPromise, cancel, layerId, symbology
91
83
  };
92
84
  const handleOk = () => {
93
85
  var _a;
94
- if (!layer.parameters) {
95
- return;
96
- }
97
- const newStyle = Object.assign({}, layer.parameters.color);
86
+ const newStyle = Object.assign({}, params.color);
98
87
  if (stopRowsRef.current && stopRowsRef.current.length > 0) {
99
88
  // Classification applied (for color)
100
89
  const expr = ['case'];
@@ -125,19 +114,28 @@ const Categorized = ({ model, state, okSignalPromise, cancel, layerId, symbology
125
114
  symbologyTab,
126
115
  reverse: reverseRamp,
127
116
  };
128
- layer.parameters.symbologyState = symbologyState;
129
- layer.parameters.color = newStyle;
130
- if (layer.type === 'HeatmapLayer') {
131
- layer.type = 'VectorLayer';
132
- }
133
- model.sharedModel.updateLayer(layerId, layer);
134
- cancel();
117
+ saveSymbology({
118
+ model,
119
+ layerId,
120
+ isStorySegmentOverride,
121
+ segmentId,
122
+ payload: {
123
+ symbologyState,
124
+ color: newStyle,
125
+ },
126
+ mutateLayerBeforeSave: targetLayer => {
127
+ if (targetLayer.type === 'HeatmapLayer') {
128
+ targetLayer.type = 'VectorLayer';
129
+ }
130
+ },
131
+ });
135
132
  };
133
+ useOkSignal(okSignalPromise, handleOk);
136
134
  const handleReset = (method) => {
137
135
  if (!(layer === null || layer === void 0 ? void 0 : layer.parameters)) {
138
136
  return;
139
137
  }
140
- const newStyle = Object.assign({}, layer.parameters.color);
138
+ const newStyle = Object.assign({}, params.color);
141
139
  if (method === 'color') {
142
140
  delete newStyle['fill-color'];
143
141
  delete newStyle['stroke-color'];
@@ -190,7 +188,8 @@ const Categorized = ({ model, state, okSignalPromise, cancel, layerId, symbology
190
188
  React.createElement("input", { type: "checkbox", checked: reverseRamp, onChange: e => setReverseRamp(e.target.checked) }),
191
189
  "Reverse Color Ramp"))),
192
190
  React.createElement("div", { className: "jp-gis-layer-symbology-container" },
193
- React.createElement(ColorRampControls, { layerParams: layer.parameters, modeOptions: [], classifyFunc: buildColorInfoFromClassification, showModeRow: false, showRampSelector: symbologyTab === 'color' }),
191
+ "//! only needs symbology state",
192
+ React.createElement(ColorRampControls, { layerParams: params, modeOptions: [], classifyFunc: buildColorInfoFromClassification, showModeRow: false, showRampSelector: symbologyTab === 'color' }),
194
193
  React.createElement(StopContainer, { selectedMethod: '', stopRows: stopRows, setStopRows: setStopRows }))));
195
194
  }
196
195
  })();