@haniffalab/cherita-react 1.3.0-dev.2025-06-04.6573608f → 1.3.0-dev.2025-06-06.0b38976b

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.
@@ -34,7 +34,7 @@ const INITIAL_VIEW_STATE = {
34
34
  bearing: 0
35
35
  };
36
36
  export function Scatterplot(_ref) {
37
- var _settings$selectedObs3, _features$features2, _obsmData$serverError, _xData$serverError, _obsData$serverError, _labelObsData$serverE, _settings$selectedVar, _settings$selectedObs4, _obsmData$data;
37
+ var _settings$selectedObs2, _settings$selectedObs5, _features$features2, _obsmData$serverError, _xData$serverError, _obsData$serverError, _labelObsData$serverE, _settings$selectedVar, _settings$selectedObs6, _data$positions;
38
38
  let {
39
39
  radius = null,
40
40
  setShowObs,
@@ -55,13 +55,12 @@ export function Scatterplot(_ref) {
55
55
  const deckRef = useRef(null);
56
56
  const [viewState, setViewState] = useState(INITIAL_VIEW_STATE);
57
57
  const [isRendering, setIsRendering] = useState(true);
58
+ const [radiusScale, setRadiusScale] = useState(radius || 1);
59
+ const [isPending, setIsPending] = useState(false);
58
60
  const [data, setData] = useState({
59
- ids: [],
60
61
  positions: [],
61
- values: [],
62
- sliceValues: []
62
+ values: []
63
63
  });
64
- const [radiusScale, setRadiusScale] = useState(radius || 1);
65
64
 
66
65
  // EditableGeoJsonLayer
67
66
  const [mode, setMode] = useState(() => ViewMode);
@@ -88,21 +87,34 @@ export function Scatterplot(_ref) {
88
87
  return rs;
89
88
  }, [radius]);
90
89
  useEffect(() => {
91
- if (!obsmData.isPending && !obsmData.serverError) {
92
- var _deckRef$current, _deckRef$current2;
93
- setIsRendering(true);
90
+ if (obsmData.isPending || settings.colorEncoding === COLOR_ENCODINGS.VAR && xData.isPending || settings.colorEncoding === COLOR_ENCODINGS.OBS && obsData.isPending) {
91
+ setIsPending(true);
92
+ } else {
93
+ setIsPending(false);
94
94
  setData(d => {
95
- return _objectSpread(_objectSpread({}, d), {}, {
96
- positions: obsmData.data
97
- });
95
+ let values = d.values;
96
+ if (settings.colorEncoding === COLOR_ENCODINGS.VAR) {
97
+ values = !xData.serverError ? xData.data : values;
98
+ } else if (settings.colorEncoding === COLOR_ENCODINGS.OBS) {
99
+ values = !obsData.serverError ? obsData.data : values;
100
+ }
101
+ return {
102
+ positions: !obsmData.serverError ? obsmData.data : d.positions,
103
+ values: values
104
+ };
98
105
  });
106
+ }
107
+ }, [obsData.data, obsData.isPending, obsData.serverError, obsmData.data, obsmData.isPending, obsmData.serverError, settings.colorEncoding, xData.data, xData.isPending, xData.serverError]);
108
+ useEffect(() => {
109
+ if (data.positions && !!data.positions.length) {
110
+ var _deckRef$current, _deckRef$current2;
99
111
  const mapHelper = new MapHelper();
100
112
  const {
101
113
  latitude,
102
114
  longitude,
103
115
  zoom,
104
116
  bounds
105
- } = mapHelper.fitBounds(obsmData.data, {
117
+ } = mapHelper.fitBounds(data.positions, {
106
118
  width: deckRef === null || deckRef === void 0 || (_deckRef$current = deckRef.current) === null || _deckRef$current === void 0 || (_deckRef$current = _deckRef$current.deck) === null || _deckRef$current === void 0 ? void 0 : _deckRef$current.width,
107
119
  height: deckRef === null || deckRef === void 0 || (_deckRef$current2 = deckRef.current) === null || _deckRef$current2 === void 0 || (_deckRef$current2 = _deckRef$current2.deck) === null || _deckRef$current2 === void 0 ? void 0 : _deckRef$current2.height
108
120
  });
@@ -114,15 +126,8 @@ export function Scatterplot(_ref) {
114
126
  zoom: zoom
115
127
  });
116
128
  });
117
- } else if (!obsmData.isPending && obsmData.serverError) {
118
- setIsRendering(true);
119
- setData(d => {
120
- return _objectSpread(_objectSpread({}, d), {}, {
121
- positions: []
122
- });
123
- });
124
129
  }
125
- }, [settings.selectedObsm, getRadiusScale, obsmData.data, obsmData.isPending, obsmData.serverError]);
130
+ }, [settings.selectedObsm, getRadiusScale, obsmData.data, obsmData.isPending, obsmData.serverError, data.positions]);
126
131
  const getBounds = useCallback(() => {
127
132
  var _deckRef$current3, _deckRef$current4;
128
133
  const {
@@ -139,65 +144,47 @@ export function Scatterplot(_ref) {
139
144
  zoom
140
145
  };
141
146
  }, [data.positions]);
142
- useEffect(() => {
143
- if (settings.colorEncoding === COLOR_ENCODINGS.VAR) {
144
- setIsRendering(true);
145
- if (!xData.isPending && !xData.serverError) {
146
- // @TODO: add condition to check obs slicing
147
- setData(d => {
148
- return _objectSpread(_objectSpread({}, d), {}, {
149
- values: xData.data
150
- });
151
- });
152
- } else if (!xData.isPending && xData.serverError) {
153
- setData(d => {
154
- return _objectSpread(_objectSpread({}, d), {}, {
155
- values: []
156
- });
157
- });
158
- }
159
- }
160
- }, [settings.colorEncoding, xData.data, xData.isPending, xData.serverError, getColor]);
161
- useEffect(() => {
162
- if (settings.colorEncoding === COLOR_ENCODINGS.OBS) {
163
- setIsRendering(true);
164
- if (!obsData.isPending && !obsData.serverError) {
165
- setData(d => {
166
- return _objectSpread(_objectSpread({}, d), {}, {
167
- values: obsData.data
168
- });
169
- });
170
- } else if (!obsData.isPending && obsData.serverError) {
171
- setData(d => {
172
- return _objectSpread(_objectSpread({}, d), {}, {
173
- values: []
174
- });
175
- });
176
- }
177
- } else if (settings.colorEncoding === COLOR_ENCODINGS.VAR && settings.sliceBy.obs) {
178
- if (!obsData.isPending && !obsData.serverError) {
179
- setData(d => {
180
- return _objectSpread(_objectSpread({}, d), {}, {
181
- sliceValues: obsData.data
182
- });
183
- });
184
- } else if (!obsData.isPending && obsData.serverError) {
185
- setData(d => {
186
- return _objectSpread(_objectSpread({}, d), {}, {
187
- sliceValues: []
188
- });
189
- });
190
- }
147
+
148
+ // Make stable references for getOriginalIndex and sortedIndexMap
149
+ const identityGetOriginalIndex = useCallback(i => i, []);
150
+ const identitySortedIndexMap = useMemo(() => ({
151
+ get: key => key
152
+ }), []);
153
+ const {
154
+ sortedData,
155
+ getOriginalIndex,
156
+ sortedIndexMap
157
+ } = useMemo(() => {
158
+ var _settings$selectedObs;
159
+ if ((settings.colorEncoding === COLOR_ENCODINGS.VAR || settings.colorEncoding === COLOR_ENCODINGS.OBS && ((_settings$selectedObs = settings.selectedObs) === null || _settings$selectedObs === void 0 ? void 0 : _settings$selectedObs.type) === OBS_TYPES.CONTINUOUS) && data.positions && data.values && data.positions.length === data.values.length) {
160
+ const sortedIndices = _.map(data.values, (_v, i) => i).sort((a, b) => data.values[a] - data.values[b]);
161
+ const sortedIndexMap = new Map(_.map(sortedIndices, (originalIndex, sortedIndex) => [originalIndex, sortedIndex]));
162
+ return {
163
+ sortedData: _.mapValues(data, (v, _k) => {
164
+ return v ? _.at(v, sortedIndices) : v;
165
+ }),
166
+ getOriginalIndex: i => sortedIndices[i],
167
+ sortedIndexMap: sortedIndexMap
168
+ };
191
169
  }
192
- }, [settings.colorEncoding, obsData.data, obsData.isPending, obsData.serverError, settings.sliceBy.obs]);
170
+ return {
171
+ sortedData: data,
172
+ getOriginalIndex: identityGetOriginalIndex,
173
+ // return original index
174
+ sortedIndexMap: identitySortedIndexMap // return original index
175
+ };
176
+ }, [data, identityGetOriginalIndex, identitySortedIndexMap, settings.colorEncoding, (_settings$selectedObs2 = settings.selectedObs) === null || _settings$selectedObs2 === void 0 ? void 0 : _settings$selectedObs2.type]);
177
+ const sortedObsIndices = useMemo(() => {
178
+ return obsIndices ? new Set(Array.from(obsIndices, i => sortedIndexMap.get(i))) : obsIndices;
179
+ }, [obsIndices, sortedIndexMap]);
193
180
  const isCategorical = useMemo(() => {
194
181
  if (settings.colorEncoding === COLOR_ENCODINGS.OBS) {
195
- var _settings$selectedObs, _settings$selectedObs2;
196
- return ((_settings$selectedObs = settings.selectedObs) === null || _settings$selectedObs === void 0 ? void 0 : _settings$selectedObs.type) === OBS_TYPES.CATEGORICAL || ((_settings$selectedObs2 = settings.selectedObs) === null || _settings$selectedObs2 === void 0 ? void 0 : _settings$selectedObs2.type) === OBS_TYPES.BOOLEAN;
182
+ var _settings$selectedObs3, _settings$selectedObs4;
183
+ return ((_settings$selectedObs3 = settings.selectedObs) === null || _settings$selectedObs3 === void 0 ? void 0 : _settings$selectedObs3.type) === OBS_TYPES.CATEGORICAL || ((_settings$selectedObs4 = settings.selectedObs) === null || _settings$selectedObs4 === void 0 ? void 0 : _settings$selectedObs4.type) === OBS_TYPES.BOOLEAN;
197
184
  } else {
198
185
  return false;
199
186
  }
200
- }, [settings.colorEncoding, (_settings$selectedObs3 = settings.selectedObs) === null || _settings$selectedObs3 === void 0 ? void 0 : _settings$selectedObs3.type]);
187
+ }, [settings.colorEncoding, (_settings$selectedObs5 = settings.selectedObs) === null || _settings$selectedObs5 === void 0 ? void 0 : _settings$selectedObs5.type]);
201
188
  useEffect(() => {
202
189
  dispatch({
203
190
  type: "set.controls.valueRange",
@@ -215,27 +202,27 @@ export function Scatterplot(_ref) {
215
202
  let {
216
203
  index
217
204
  } = _ref2;
218
- const grayOut = obsIndices && !obsIndices.has(index);
205
+ const grayOut = isPending || sortedObsIndices && !sortedObsIndices.has(index);
219
206
  return getColor({
220
- value: (data.values[index] - min) / (max - min),
207
+ value: (sortedData.values[index] - min) / (max - min),
221
208
  categorical: isCategorical,
222
209
  grayOut: grayOut
223
210
  }) || [0, 0, 0, 100];
224
- }, [data.values, obsIndices, getColor, isCategorical, max, min]);
211
+ }, [isPending, sortedObsIndices, getColor, sortedData.values, min, max, isCategorical]);
225
212
 
226
213
  // @TODO: add support for pseudospatial hover to reflect in radius
227
214
  const getRadius = useCallback((_d, _ref3) => {
228
215
  let {
229
216
  index
230
217
  } = _ref3;
231
- const grayOut = obsIndices && !obsIndices.has(index);
218
+ const grayOut = sortedObsIndices && !sortedObsIndices.has(index);
232
219
  return grayOut ? 1 : 3;
233
- }, [obsIndices]);
220
+ }, [sortedObsIndices]);
234
221
  const memoizedLayers = useMemo(() => {
235
222
  return [new ScatterplotLayer({
236
223
  id: "cherita-layer-scatterplot",
237
224
  pickable: true,
238
- data: data.positions,
225
+ data: sortedData.positions,
239
226
  radiusScale: radiusScale,
240
227
  radiusMinPixels: 1,
241
228
  getPosition: d => d,
@@ -279,7 +266,7 @@ export function Scatterplot(_ref) {
279
266
  }
280
267
  }
281
268
  })];
282
- }, [data.positions, features, getFillColor, getRadius, mode, radiusScale, selectedFeatureIndexes]);
269
+ }, [sortedData.positions, features, getFillColor, getRadius, mode, radiusScale, selectedFeatureIndexes]);
283
270
  const layers = useDeferredValue(mode === ViewMode ? memoizedLayers.reverse() : memoizedLayers); // draw scatterplot on top of polygons when in ViewMode
284
271
 
285
272
  useEffect(() => {
@@ -322,21 +309,21 @@ export function Scatterplot(_ref) {
322
309
  if (settings.colorEncoding === COLOR_ENCODINGS.OBS && settings.selectedObs && !_.some(settings.labelObs, {
323
310
  name: settings.selectedObs.name
324
311
  })) {
325
- var _obsData$data;
326
- text.push(getLabel(settings.selectedObs, (_obsData$data = obsData.data) === null || _obsData$data === void 0 ? void 0 : _obsData$data[index]));
312
+ var _data$values;
313
+ text.push(getLabel(settings.selectedObs, (_data$values = data.values) === null || _data$values === void 0 ? void 0 : _data$values[getOriginalIndex(index)]));
327
314
  }
328
315
  if (settings.colorEncoding === COLOR_ENCODINGS.VAR && settings.selectedVar) {
329
- var _xData$data;
330
- text.push(getLabel(settings.selectedVar, (_xData$data = xData.data) === null || _xData$data === void 0 ? void 0 : _xData$data[index], true));
316
+ var _data$values2;
317
+ text.push(getLabel(settings.selectedVar, (_data$values2 = data.values) === null || _data$values2 === void 0 ? void 0 : _data$values2[getOriginalIndex(index)], true));
331
318
  }
332
319
  if (settings.labelObs.length) {
333
320
  text.push(..._.map(labelObsData.data, (v, k) => {
334
321
  const labelObs = _.find(settings.labelObs, o => o.name === k);
335
- return getLabel(labelObs, v[index]);
322
+ return getLabel(labelObs, v[getOriginalIndex(index)]);
336
323
  }));
337
324
  }
338
325
  if (!text.length) return;
339
- const grayOut = obsIndices && !obsIndices.has(index);
326
+ const grayOut = sortedObsIndices && !sortedObsIndices.has(index);
340
327
  return {
341
328
  text: text.length ? _.compact(text).join("\n") : null,
342
329
  className: grayOut ? "tooltip-grayout" : "deck-tooltip",
@@ -349,8 +336,7 @@ export function Scatterplot(_ref) {
349
336
  }
350
337
  };
351
338
  };
352
- const isPending = (isRendering || xData.isPending || obsmData.isPending) && !obsmData.isPending;
353
- const error = settings.selectedObsm && ((_obsmData$serverError = obsmData.serverError) === null || _obsmData$serverError === void 0 ? void 0 : _obsmData$serverError.length) || settings.colorEncoding === COLOR_ENCODINGS.VAR && ((_xData$serverError = xData.serverError) === null || _xData$serverError === void 0 ? void 0 : _xData$serverError.length) || settings.colorEncoding === COLOR_ENCODINGS.OBS && ((_obsData$serverError = obsData.serverError) === null || _obsData$serverError === void 0 ? void 0 : _obsData$serverError.length) || settings.labelObs.lengh && ((_labelObsData$serverE = labelObsData.serverError) === null || _labelObsData$serverE === void 0 ? void 0 : _labelObsData$serverE.length);
339
+ const error = settings.selectedObsm && ((_obsmData$serverError = obsmData.serverError) === null || _obsmData$serverError === void 0 ? void 0 : _obsmData$serverError.length) || settings.colorEncoding === COLOR_ENCODINGS.VAR && ((_xData$serverError = xData.serverError) === null || _xData$serverError === void 0 ? void 0 : _xData$serverError.length) || settings.colorEncoding === COLOR_ENCODINGS.OBS && ((_obsData$serverError = obsData.serverError) === null || _obsData$serverError === void 0 ? void 0 : _obsData$serverError.length) || settings.labelObs.length && ((_labelObsData$serverE = labelObsData.serverError) === null || _labelObsData$serverE === void 0 ? void 0 : _labelObsData$serverE.length);
354
340
  return /*#__PURE__*/React.createElement("div", {
355
341
  className: "cherita-container-scatterplot"
356
342
  }, /*#__PURE__*/React.createElement("div", {
@@ -397,13 +383,13 @@ export function Scatterplot(_ref) {
397
383
  className: "cherita-spatial-footer"
398
384
  }, /*#__PURE__*/React.createElement("div", {
399
385
  className: "cherita-toolbox-footer"
400
- }, error && !isPending && /*#__PURE__*/React.createElement(Alert, {
386
+ }, !!error && !isRendering && /*#__PURE__*/React.createElement(Alert, {
401
387
  variant: "danger"
402
388
  }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
403
389
  icon: faTriangleExclamation
404
390
  }), "\xA0Error loading data"), /*#__PURE__*/React.createElement(Toolbox, {
405
- mode: settings.colorEncoding === COLOR_ENCODINGS.VAR ? (_settings$selectedVar = settings.selectedVar) === null || _settings$selectedVar === void 0 ? void 0 : _settings$selectedVar.name : settings.colorEncoding === COLOR_ENCODINGS.OBS ? (_settings$selectedObs4 = settings.selectedObs) === null || _settings$selectedObs4 === void 0 ? void 0 : _settings$selectedObs4.name : null,
406
- obsLength: parseInt((_obsmData$data = obsmData.data) === null || _obsmData$data === void 0 ? void 0 : _obsmData$data.length),
391
+ mode: settings.colorEncoding === COLOR_ENCODINGS.VAR ? (_settings$selectedVar = settings.selectedVar) === null || _settings$selectedVar === void 0 ? void 0 : _settings$selectedVar.name : settings.colorEncoding === COLOR_ENCODINGS.OBS ? (_settings$selectedObs6 = settings.selectedObs) === null || _settings$selectedObs6 === void 0 ? void 0 : _settings$selectedObs6.name : null,
392
+ obsLength: parseInt((_data$positions = data.positions) === null || _data$positions === void 0 ? void 0 : _data$positions.length),
407
393
  slicedLength: parseInt(slicedLength)
408
394
  })), /*#__PURE__*/React.createElement(Legend, {
409
395
  isCategorical: isCategorical,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haniffalab/cherita-react",
3
- "version": "1.3.0-dev.2025-06-04.6573608f",
3
+ "version": "1.3.0-dev.2025-06-06.0b38976b",
4
4
  "author": "Haniffa Lab",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -90,7 +90,8 @@
90
90
  "build:babel": "npm run build:esm && npm run build:cjs",
91
91
  "build:scss": "sass --load-path=node_modules src/scss/cherita-bootstrap.scss dist/css/cherita.css",
92
92
  "copy:scss": "cpx 'src/scss/**/*' 'scss'",
93
- "build": "npm run build:babel && npm run build:scss && npm run copy:scss",
93
+ "copy:assets": "cpx 'src/assets/**/*' 'dist/assets'",
94
+ "build": "npm run build:babel && npm run build:scss && npm run copy:scss && npm run copy:assets",
94
95
  "test": "jest --watchAll",
95
96
  "test:ci": "jest --coverage --ci --no-watch",
96
97
  "lint:scss": "stylelint 'src/**/*.scss' --fix",
@@ -126,5 +127,5 @@
126
127
  "url": "https://github.com/haniffalab/cherita-react/issues"
127
128
  },
128
129
  "homepage": "https://github.com/haniffalab/cherita-react#readme",
129
- "prereleaseSha": "6573608f6ab26442b5badaea1be8d5dff17b1344"
130
+ "prereleaseSha": "0b38976bfa4499c6f24545ed2f278f078633d27b"
130
131
  }