From 0c297e40d68e941b1d6d68dcfa146dc567c5f1a0 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Mon, 30 Jun 2025 12:35:19 -0600 Subject: [PATCH 1/3] Access props from a props argument I feel that while this is more verbose, it reduces cognitive load when reading the component code. Previously, in order to know where a value comes from (i.e. props vs state), I needed to remember the names of all the props. With this change, I don't need to know that because I can see the value is accessed from the props object. --- packages/base/src/mainview/TemporalSlider.tsx | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/packages/base/src/mainview/TemporalSlider.tsx b/packages/base/src/mainview/TemporalSlider.tsx index 771ad1817..3251e2440 100644 --- a/packages/base/src/mainview/TemporalSlider.tsx +++ b/packages/base/src/mainview/TemporalSlider.tsx @@ -53,10 +53,7 @@ const stepMap = { year: millisecondsInDay * daysInYear, }; -const TemporalSlider: React.FC = ({ - model, - filterStates, -}) => { +const TemporalSlider: React.FC = props => { const [layerId, setLayerId] = useState(''); const [selectedFeature, setSelectedFeature] = useState(''); const [range, setRange] = useState({ start: 0, end: 1 }); // min/max of current range being displayed @@ -71,16 +68,16 @@ const TemporalSlider: React.FC = ({ const layerIdRef = useRef(''); const intervalRef = useRef(null); - const { featureProperties } = useGetProperties({ layerId, model }); + const { featureProperties } = useGetProperties({ layerId, model: props.model }); useEffect(() => { // This is for when the selected layer changes const handleClientStateChanged = () => { - if (!model.localState?.selected?.value) { + if (!props.model.localState?.selected?.value) { return; } - const selectedLayerId = Object.keys(model.localState.selected.value)[0]; + const selectedLayerId = Object.keys(props.model.localState.selected.value)[0]; // reset if (selectedLayerId !== layerIdRef.current) { @@ -112,19 +109,19 @@ const TemporalSlider: React.FC = ({ Object.keys(newValue).length === 0 || newValue.type !== oldValue.type ) { - model.toggleTemporalController(); + props.model.toggleTemporalController(); } }; // Initial state handleClientStateChanged(); - model.clientStateChanged.connect(handleClientStateChanged); - model.sharedLayersChanged.connect(handleLayerChange); + props.model.clientStateChanged.connect(handleClientStateChanged); + props.model.sharedLayersChanged.connect(handleLayerChange); return () => { - model.clientStateChanged.disconnect(handleClientStateChanged); - model.sharedLayersChanged.disconnect(handleLayerChange); + props.model.clientStateChanged.disconnect(handleClientStateChanged); + props.model.sharedLayersChanged.disconnect(handleLayerChange); removeFilter(); if (intervalRef.current) { clearInterval(intervalRef.current); @@ -167,7 +164,7 @@ const TemporalSlider: React.FC = ({ } // if we have state then remove the ms from the converted feature name - const currentState = filterStates[layerId]; + const currentState = props.filterStates[layerId]; const currentFeature = currentState?.feature.slice(0, -2); setValidFeatures(results); setSelectedFeature(currentFeature ?? results[0]); @@ -208,7 +205,7 @@ const TemporalSlider: React.FC = ({ ); //using filter item as a state object to restore prev values - const currentState = filterStates[layerId]; + const currentState = props.filterStates[layerId]; const step = Object.values(filteredSteps).slice(-1)[0] ?? stepMap.millisecond; @@ -220,12 +217,12 @@ const TemporalSlider: React.FC = ({ end: currentState?.betweenMax ?? min + step, }); - model.addFeatureAsMs(layerId, selectedFeature); + props.model.addFeatureAsMs(layerId, selectedFeature); }, [selectedFeature]); // minMax needs to be set before current value so the slider displays correctly useEffect(() => { - const currentState = filterStates[layerId]; + const currentState = props.filterStates[layerId]; setCurrentValue( typeof currentState?.value === 'number' ? currentState.value : minMax.min, @@ -267,7 +264,7 @@ const TemporalSlider: React.FC = ({ betweenMax: value + step, }; - const layer = model.getLayer(layerId); + const layer = props.model.getLayer(layerId); if (!layer) { return; } @@ -293,11 +290,11 @@ const TemporalSlider: React.FC = ({ // Apply the updated filters to the layer layer.filters = { logicalOp, appliedFilters }; - model.triggerLayerUpdate(layerId, layer); + props.model.triggerLayerUpdate(layerId, layer); }; const removeFilter = () => { - const layer = model.getLayer(layerIdRef.current); + const layer = props.model.getLayer(layerIdRef.current); if (!layer) { return; } @@ -318,7 +315,7 @@ const TemporalSlider: React.FC = ({ // Apply the updated filters to the layer layer.filters = { logicalOp, appliedFilters }; - model.triggerLayerUpdate(layerIdRef.current, layer); + props.model.triggerLayerUpdate(layerIdRef.current, layer); }; const playAnimation = () => { From 7918593eeb2a63e3be2c3495870373cc175c17c5 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Wed, 2 Jul 2025 11:15:06 -0600 Subject: [PATCH 2/3] Single `props` argument, applied to more components --- .../src/annotations/components/Annotation.tsx | 29 +++++------ .../src/annotations/components/Message.tsx | 11 ++-- .../color_ramp/CanvasSelectComponent.tsx | 11 ++-- .../components/color_ramp/ColorRamp.tsx | 26 ++++------ .../components/color_ramp/ColorRampEntry.tsx | 18 +++---- .../components/color_ramp/ModeSelectRow.tsx | 22 +++----- .../components/color_stops/StopContainer.tsx | 22 ++++---- .../components/color_stops/StopRow.tsx | 48 ++++++++---------- .../vector_layer/components/ValueSelect.tsx | 12 ++--- .../vector_layer/types/Canonical.tsx | 32 ++++++------ .../symbology/vector_layer/types/Heatmap.tsx | 23 ++++----- .../vector_layer/types/SimpleSymbol.tsx | 26 ++++------ .../src/mainview/CollaboratorPointers.tsx | 8 ++- .../base/src/mainview/FollowIndicator.tsx | 10 ++-- .../components/filter-panel/FilterRow.tsx | 50 +++++++++---------- .../identify-panel/IdentifyPanel.tsx | 21 ++++---- packages/base/src/statusbar/StatusBar.tsx | 23 ++++----- 17 files changed, 164 insertions(+), 228 deletions(-) diff --git a/packages/base/src/annotations/components/Annotation.tsx b/packages/base/src/annotations/components/Annotation.tsx index f3aa8196d..54989ceb3 100644 --- a/packages/base/src/annotations/components/Annotation.tsx +++ b/packages/base/src/annotations/components/Annotation.tsx @@ -19,37 +19,32 @@ export interface IAnnotationProps { children?: JSX.Element[] | JSX.Element; } -const Annotation: React.FC = ({ - itemId, - annotationModel, - rightPanelModel, - children, -}) => { +const Annotation: React.FC = props => { const [messageContent, setMessageContent] = useState(''); const [jgisModel, setJgisModel] = useState( - rightPanelModel?.jGISModel, + props.rightPanelModel?.jGISModel, ); - const annotation = annotationModel.getAnnotation(itemId); + const annotation = props.annotationModel.getAnnotation(props.itemId); const contents = useMemo(() => annotation?.contents ?? [], [annotation]); /** * Update the model when it changes. */ - rightPanelModel?.documentChanged.connect((_, widget) => { + props.rightPanelModel?.documentChanged.connect((_, widget) => { setJgisModel(widget?.model); }); const handleSubmit = () => { - annotationModel.addContent(itemId, messageContent); + props.annotationModel.addContent(props.itemId, messageContent); setMessageContent(''); }; const handleDelete = async () => { // If the annotation has no content // we remove it right away without prompting - if (!annotationModel.getAnnotation(itemId)?.contents.length) { - return annotationModel.removeAnnotation(itemId); + if (!props.annotationModel.getAnnotation(props.itemId)?.contents.length) { + return props.annotationModel.removeAnnotation(props.itemId); } const result = await showDialog({ @@ -59,24 +54,24 @@ const Annotation: React.FC = ({ }); if (result.button.accept) { - annotationModel.removeAnnotation(itemId); + props.annotationModel.removeAnnotation(props.itemId); } }; const centerOnAnnotation = () => { - jgisModel?.centerOnPosition(itemId); + jgisModel?.centerOnPosition(props.itemId); }; return (
- {children} + {props.children}
{contents.map(content => { return ( ); })} @@ -98,7 +93,7 @@ const Annotation: React.FC = ({ - {rightPanelModel && ( + {props.rightPanelModel && (
); diff --git a/packages/base/src/dialogs/symbology/vector_layer/components/ValueSelect.tsx b/packages/base/src/dialogs/symbology/vector_layer/components/ValueSelect.tsx index c1ead96ac..3736b05e2 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/components/ValueSelect.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/components/ValueSelect.tsx @@ -6,24 +6,20 @@ interface IValueSelectProps { setSelectedValue: (value: string) => void; } -const ValueSelect: React.FC = ({ - featureProperties, - selectedValue, - setSelectedValue, -}) => { +const ValueSelect: React.FC = props => { return (
@@ -90,14 +90,14 @@ const FilterRow: React.FC<{ ))}
); diff --git a/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx b/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx index 842f1111f..6750a9f75 100644 --- a/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx +++ b/packages/base/src/panelview/components/identify-panel/IdentifyPanel.tsx @@ -50,10 +50,7 @@ interface IIdentifyComponentProps { tracker: IJupyterGISTracker; } -const IdentifyPanelComponent: React.FC = ({ - controlPanelModel, - tracker, -}) => { +const IdentifyPanelComponent: React.FC = props => { const [widgetId, setWidgetId] = useState(''); const [features, setFeatures] = useState>(); const [visibleFeatures, setVisibleFeatures] = useState>({ @@ -61,36 +58,36 @@ const IdentifyPanelComponent: React.FC = ({ }); const [remoteUser, setRemoteUser] = useState(null); const [jgisModel, setJgisModel] = useState( - controlPanelModel?.jGISModel, + props.controlPanelModel?.jGISModel, ); const featuresRef = useRef(features); /** * Update the model when it changes. */ - controlPanelModel?.documentChanged.connect((_, widget) => { + props.controlPanelModel?.documentChanged.connect((_, widget) => { setJgisModel(widget?.model); }); // Reset state values when current widget changes useEffect(() => { const handleCurrentChanged = () => { - if (tracker.currentWidget?.id === widgetId) { + if (props.tracker.currentWidget?.id === widgetId) { return; } - if (tracker.currentWidget) { - setWidgetId(tracker.currentWidget.id); + if (props.tracker.currentWidget) { + setWidgetId(props.tracker.currentWidget.id); } setFeatures({}); setVisibleFeatures({ 0: true }); }; - tracker.currentChanged.connect(handleCurrentChanged); + props.tracker.currentChanged.connect(handleCurrentChanged); return () => { - tracker.currentChanged.disconnect(handleCurrentChanged); + props.tracker.currentChanged.disconnect(handleCurrentChanged); }; - }, []); + }, [props.tracker]); useEffect(() => { featuresRef.current = features; diff --git a/packages/base/src/statusbar/StatusBar.tsx b/packages/base/src/statusbar/StatusBar.tsx index 2563dd49c..d3e5524b0 100644 --- a/packages/base/src/statusbar/StatusBar.tsx +++ b/packages/base/src/statusbar/StatusBar.tsx @@ -16,17 +16,12 @@ interface IStatusBarProps { projection?: { code: string; units: string }; scale: number; } -const StatusBar: React.FC = ({ - jgisModel, - loading, - projection, - scale, -}) => { +const StatusBar: React.FC = props => { const [coords, setCoords] = useState({ x: 0, y: 0 }); useEffect(() => { const handleClientStateChanged = () => { - const pointer = jgisModel?.localState?.pointer?.value; + const pointer = props.jgisModel?.localState?.pointer?.value; if (!pointer) { return; @@ -35,16 +30,16 @@ const StatusBar: React.FC = ({ setCoords({ x: pointer?.coordinates.x, y: pointer?.coordinates.y }); }; - jgisModel.clientStateChanged.connect(handleClientStateChanged); + props.jgisModel.clientStateChanged.connect(handleClientStateChanged); return () => { - jgisModel.clientStateChanged.disconnect(handleClientStateChanged); + props.jgisModel.clientStateChanged.disconnect(handleClientStateChanged); }; - }, [jgisModel]); + }, [props.jgisModel]); return (
- {loading && ( + {props.loading && (
@@ -61,13 +56,13 @@ const StatusBar: React.FC = ({
{' '} - Scale: 1: {Math.trunc(scale)} + Scale: 1: {Math.trunc(props.scale)}
{' '} - {projection?.code ?? null} + {props.projection?.code ?? null}
-
Units: {projection?.units}
+
Units: {props.projection?.units}
); }; From d67f263f6b91386c697ffe424e9bdb73ebc458fd Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Wed, 2 Jul 2025 13:29:51 -0600 Subject: [PATCH 3/3] Apply single props argument to more components --- .../components/AnnotationFloater.tsx | 17 +++-- .../base/src/dialogs/layerBrowserDialog.tsx | 42 ++++++------- .../src/dialogs/symbology/symbologyDialog.tsx | 33 +++++----- .../symbology/tiff_layer/TiffRendering.tsx | 35 +++++------ .../tiff_layer/components/BandRow.tsx | 38 +++++------- .../tiff_layer/types/MultibandColor.tsx | 24 ++++--- .../types/SingleBandPseudoColor.tsx | 32 +++++----- .../vector_layer/VectorRendering.tsx | 26 +++----- .../vector_layer/types/Categorized.tsx | 52 ++++++++-------- .../vector_layer/types/Graduated.tsx | 62 +++++++++---------- 10 files changed, 161 insertions(+), 200 deletions(-) diff --git a/packages/base/src/annotations/components/AnnotationFloater.tsx b/packages/base/src/annotations/components/AnnotationFloater.tsx index dba0fd97d..bef71689a 100644 --- a/packages/base/src/annotations/components/AnnotationFloater.tsx +++ b/packages/base/src/annotations/components/AnnotationFloater.tsx @@ -4,11 +4,8 @@ import React, { useState } from 'react'; import Annotation, { IAnnotationProps } from './Annotation'; -const AnnotationFloater: React.FC = ({ - itemId, - annotationModel: model, -}) => { - const annotation = model.getAnnotation(itemId); +const AnnotationFloater: React.FC = props => { + const annotation = props.annotationModel.getAnnotation(props.itemId); const [isOpen, setIsOpen] = useState(annotation?.open); // Function that either @@ -17,15 +14,15 @@ const AnnotationFloater: React.FC = ({ // - closes the annotation if `!open` and the annotation is not empty const setOpenOrDelete = (open: boolean) => { if (open) { - model.updateAnnotation(itemId, { open: true }); + props.annotationModel.updateAnnotation(props.itemId, { open: true }); return setIsOpen(true); } - const current = model.getAnnotation(itemId); + const current = props.annotationModel.getAnnotation(props.itemId); if (!current?.contents.length) { - model.removeAnnotation(itemId); + props.annotationModel.removeAnnotation(props.itemId); } else { - model.updateAnnotation(itemId, { open: false }); + props.annotationModel.updateAnnotation(props.itemId, { open: false }); setIsOpen(false); } }; @@ -40,7 +37,7 @@ const AnnotationFloater: React.FC = ({ className="jGIS-FloatingAnnotation" style={{ visibility: isOpen ? 'visible' : 'hidden' }} > - +
{ diff --git a/packages/base/src/dialogs/layerBrowserDialog.tsx b/packages/base/src/dialogs/layerBrowserDialog.tsx index e8af9cc3f..631ec11ea 100644 --- a/packages/base/src/dialogs/layerBrowserDialog.tsx +++ b/packages/base/src/dialogs/layerBrowserDialog.tsx @@ -25,13 +25,7 @@ interface ILayerBrowserDialogProps { cancel: () => void; } -export const LayerBrowserComponent: React.FC = ({ - model, - registry, - formSchemaRegistry, - okSignalPromise, - cancel, -}) => { +export const LayerBrowserComponent: React.FC = props => { const [searchTerm, setSearchTerm] = useState(''); const [activeLayers, setActiveLayers] = useState([]); const [selectedCategory, setSelectedCategory] = @@ -39,19 +33,19 @@ export const LayerBrowserComponent: React.FC = ({ const [creatingCustomRaster, setCreatingCustomRaster] = useState(false); const [galleryWithCategory, setGalleryWithCategory] = - useState(registry); + useState(props.registry); - const providers = [...new Set(registry.map(item => item.source.provider))]; + const providers = [...new Set(props.registry.map(item => item.source.provider))]; const filteredGallery = galleryWithCategory.filter(item => item.name.toLowerCase().includes(searchTerm), ); useEffect(() => { - model.sharedModel.layersChanged.connect(handleLayerChange); + props.model.sharedModel.layersChanged.connect(handleLayerChange); return () => { - model.sharedModel.layersChanged.disconnect(handleLayerChange); + props.model.sharedModel.layersChanged.disconnect(handleLayerChange); }; }, []); @@ -61,7 +55,7 @@ export const LayerBrowserComponent: React.FC = ({ const handleLayerChange = (_: any, change: IJGISLayerDocChange) => { // The split is to get rid of the 'Layer' part of the name to match the names in the gallery setActiveLayers( - Object.values(model.sharedModel.layers).map( + Object.values(props.model.sharedModel.layers).map( layer => layer.name.split(' ')[0], ), ); @@ -79,8 +73,8 @@ export const LayerBrowserComponent: React.FC = ({ selectedCategory?.classList.remove('jGIS-layer-browser-category-selected'); const filteredGallery = sameAsOld - ? registry - : registry.filter(item => + ? props.registry + : props.registry.filter(item => item.source.provider?.includes(categoryTab.innerText), ); @@ -115,21 +109,21 @@ export const LayerBrowserComponent: React.FC = ({ name: tile.name + ' Layer', }; - model.sharedModel.addSource(sourceId, sourceModel); - model.addLayer(UUID.uuid4(), layerModel); + props.model.sharedModel.addSource(sourceId, sourceModel); + props.model.addLayer(UUID.uuid4(), layerModel); }; if (creatingCustomRaster) { // Disconnect any previous handler - okSignalPromise.promise.then(value => { - value.disconnect(cancel, this); + props.okSignalPromise.promise.then(value => { + value.disconnect(props.cancel, this); }); return (
= ({ minZoom: 0, attribution: '(C) OpenStreetMap contributors', }} - okSignalPromise={okSignalPromise} - cancel={cancel} + okSignalPromise={props.okSignalPromise} + cancel={props.cancel} />
); } // Ok is like cancel in the case of gallery item selections - okSignalPromise.promise.then(value => { - value.connect(cancel, this); + props.okSignalPromise.promise.then(value => { + value.connect(props.cancel, this); }); return ( diff --git a/packages/base/src/dialogs/symbology/symbologyDialog.tsx b/packages/base/src/dialogs/symbology/symbologyDialog.tsx index 4a3bee9cf..1d93acd84 100644 --- a/packages/base/src/dialogs/symbology/symbologyDialog.tsx +++ b/packages/base/src/dialogs/symbology/symbologyDialog.tsx @@ -39,12 +39,7 @@ export interface IStopRow { output: number | number[]; } -const SymbologyDialog: React.FC = ({ - model, - state, - okSignalPromise, - cancel, -}) => { +const SymbologyDialog: React.FC = props => { const [selectedLayer, setSelectedLayer] = useState(null); const [componentToRender, setComponentToRender] = useState(null); @@ -52,11 +47,11 @@ const SymbologyDialog: React.FC = ({ useEffect(() => { const handleClientStateChanged = () => { - if (!model.localState?.selected?.value) { + if (!props.model.localState?.selected?.value) { return; } - const currentLayer = Object.keys(model.localState.selected.value)[0]; + const currentLayer = Object.keys(props.model.localState.selected.value)[0]; setSelectedLayer(currentLayer); }; @@ -64,10 +59,10 @@ const SymbologyDialog: React.FC = ({ // Initial state handleClientStateChanged(); - model.clientStateChanged.connect(handleClientStateChanged); + props.model.clientStateChanged.connect(handleClientStateChanged); return () => { - model.clientStateChanged.disconnect(handleClientStateChanged); + props.model.clientStateChanged.disconnect(handleClientStateChanged); }; }, []); @@ -76,7 +71,7 @@ const SymbologyDialog: React.FC = ({ return; } - const layer = model.getLayer(selectedLayer); + const layer = props.model.getLayer(selectedLayer); if (!layer) { return; @@ -89,10 +84,10 @@ const SymbologyDialog: React.FC = ({ case 'HeatmapLayer': LayerSymbology = ( ); @@ -100,10 +95,10 @@ const SymbologyDialog: React.FC = ({ case 'WebGlLayer': LayerSymbology = ( ); diff --git a/packages/base/src/dialogs/symbology/tiff_layer/TiffRendering.tsx b/packages/base/src/dialogs/symbology/tiff_layer/TiffRendering.tsx index 3c9cefd23..fc132d3fc 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/TiffRendering.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/TiffRendering.tsx @@ -4,24 +4,21 @@ import { ISymbologyDialogProps } from '@/src/dialogs/symbology/symbologyDialog'; import MultibandColor from './types/MultibandColor'; import SingleBandPseudoColor from './types/SingleBandPseudoColor'; -const TiffRendering: React.FC = ({ - model, - state, - okSignalPromise, - cancel, - layerId, -}) => { +const TiffRendering: React.FC = props => { const renderTypes = ['Singleband Pseudocolor', 'Multiband Color']; const [selectedRenderType, setSelectedRenderType] = useState(); const [componentToRender, setComponentToRender] = useState(null); let RenderComponent; - if (!layerId) { + if (!props.layerId) { return; } useEffect(() => { - const layer = model.getLayer(layerId); + if (!props.layerId) { + throw new Error('Layer ID is required'); + } + const layer = props.model.getLayer(props.layerId); const renderType = layer?.parameters?.symbologyState?.renderType; setSelectedRenderType(renderType ?? 'Singleband Pseudocolor'); }, []); @@ -35,22 +32,22 @@ const TiffRendering: React.FC = ({ case 'Singleband Pseudocolor': RenderComponent = ( ); break; case 'Multiband Color': RenderComponent = ( ); break; diff --git a/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx b/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx index 62036c54b..dc7cd473e 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/components/BandRow.tsx @@ -22,17 +22,9 @@ interface IBandRowProps { * @param setBandRows Function to update band rows in parent * @param isMultibandColor Used to hide min/max input and add 'Unset' option to drop down menu for MultiBand symbology */ -const BandRow: React.FC = ({ - label, - index, - bandRow, - bandRows, - setSelectedBand, - setBandRows, - isMultibandColor, -}) => { - const [minValue, setMinValue] = useState(bandRow?.stats.minimum); - const [maxValue, setMaxValue] = useState(bandRow?.stats.maximum); +const BandRow: React.FC = props => { + const [minValue, setMinValue] = useState(props.bandRow?.stats.minimum); + const [maxValue, setMaxValue] = useState(props.bandRow?.stats.maximum); const handleMinValueChange = (event: { target: { value: string | number }; @@ -49,27 +41,27 @@ const BandRow: React.FC = ({ }; const setNewBands = () => { - const newBandRows = [...bandRows]; - newBandRows[index].stats.minimum = minValue; - newBandRows[index].stats.maximum = maxValue; - setBandRows(newBandRows); + const newBandRows = [...props.bandRows]; + newBandRows[props.index].stats.minimum = minValue; + newBandRows[props.index].stats.maximum = maxValue; + props.setBandRows(newBandRows); }; return ( <>
- +
- {isMultibandColor ? null : ( + {props.isMultibandColor ? null : (
= ({ - model, - okSignalPromise, - cancel, - layerId, -}) => { - if (!layerId) { +const MultibandColor: React.FC = props => { + if (!props.layerId) { return; } - const layer = model.getLayer(layerId); + const layer = props.model.getLayer(props.layerId); if (!layer?.parameters) { return; } - const { bandRows, setBandRows, loading } = useGetBandInfo(model, layer); + const { bandRows, setBandRows, loading } = useGetBandInfo(props.model, layer); const [selectedBands, setSelectedBands] = useState({ red: 1, @@ -50,12 +45,12 @@ const MultibandColor: React.FC = ({ useEffect(() => { populateOptions(); - okSignalPromise.promise.then(okSignal => { + props.okSignalPromise.promise.then(okSignal => { okSignal.connect(handleOk); }); return () => { - okSignalPromise.promise.then(okSignal => { + props.okSignalPromise.promise.then(okSignal => { okSignal.disconnect(handleOk, this); }); }; @@ -88,6 +83,9 @@ const MultibandColor: React.FC = ({ const handleOk = () => { // Update layer + if (!props.layerId) { + throw new Error('layerId is required for MultibandColor component'); + } if (!layer.parameters) { return; } @@ -119,8 +117,8 @@ const MultibandColor: React.FC = ({ layer.parameters.color = colorExpr; layer.type = 'WebGlLayer'; - model.sharedModel.updateLayer(layerId, layer); - cancel(); + props.model.sharedModel.updateLayer(props.layerId, layer); + props.cancel(); }; return ( diff --git a/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx b/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx index f483ca164..b4ccfbac7 100644 --- a/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx +++ b/packages/base/src/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.tsx @@ -23,16 +23,11 @@ import { GlobalStateDbManager } from '@/src/store'; export type InterpolationType = 'discrete' | 'linear' | 'exact'; -const SingleBandPseudoColor: React.FC = ({ - model, - okSignalPromise, - cancel, - layerId, -}) => { - if (!layerId) { +const SingleBandPseudoColor: React.FC = props => { + if (!props.layerId) { return; } - const layer = model.getLayer(layerId); + const layer = props.model.getLayer(props.layerId); if (!layer?.parameters) { return; } @@ -42,7 +37,7 @@ const SingleBandPseudoColor: React.FC = ({ const stateDb = GlobalStateDbManager.getInstance().getStateDb(); - const { bandRows, setBandRows, loading } = useGetBandInfo(model, layer); + const { bandRows, setBandRows, loading } = useGetBandInfo(props.model, layer); const [layerState, setLayerState] = useState(); const [selectedBand, setSelectedBand] = useState(1); @@ -62,12 +57,12 @@ const SingleBandPseudoColor: React.FC = ({ useEffect(() => { populateOptions(); - okSignalPromise.promise.then(okSignal => { + props.okSignalPromise.promise.then(okSignal => { okSignal.connect(handleOk); }); return () => { - okSignalPromise.promise.then(okSignal => { + props.okSignalPromise.promise.then(okSignal => { okSignal.disconnect(handleOk, this); }); }; @@ -87,7 +82,7 @@ const SingleBandPseudoColor: React.FC = ({ const populateOptions = async () => { const layerState = (await stateDb?.fetch( - `jupytergis:${layerId}`, + `jupytergis:${props.layerId}`, )) as ReadonlyJSONObject; setLayerState(layerState); @@ -158,13 +153,16 @@ const SingleBandPseudoColor: React.FC = ({ }; const handleOk = () => { + if (!props.layerId) { + throw new Error('layerId is required for SingleBandPseudoColor component'); + } // Update source const bandRow = bandRowsRef.current[selectedBand - 1]; if (!bandRow) { return; } const sourceId = layer.parameters?.source; - const source = model.getSource(sourceId); + const source = props.model.getSource(sourceId); if (!source || !source.parameters) { return; @@ -178,7 +176,7 @@ const SingleBandPseudoColor: React.FC = ({ source.parameters.urls[0] = sourceInfo; - model.sharedModel.updateSource(sourceId, source); + props.model.sharedModel.updateSource(sourceId, source); // Update layer if (!layer.parameters) { @@ -260,8 +258,8 @@ const SingleBandPseudoColor: React.FC = ({ layer.parameters.color = colorExpr; layer.type = 'WebGlLayer'; - model.sharedModel.updateLayer(layerId, layer); - cancel(); + props.model.sharedModel.updateLayer(props.layerId, layer); + props.cancel(); }; const addStopRow = () => { @@ -297,7 +295,7 @@ const SingleBandPseudoColor: React.FC = ({ let stops: number[] = []; const currentBand = bandRows[selectedBand - 1]; - const source = model.getSource(layer?.parameters?.source); + const source = props.model.getSource(layer?.parameters?.source); const sourceInfo = source?.parameters?.urls[0]; const nClasses = selectedMode === 'continuous' ? 52 : +numberOfShades; diff --git a/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx b/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx index 666401ec2..480f5eba2 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx @@ -101,29 +101,23 @@ const useLayerRenderType = ( setSelectedRenderType(renderType); }, []); -const VectorRendering: React.FC = ({ - model, - state, - okSignalPromise, - cancel, - layerId, -}) => { +const VectorRendering: React.FC = props => { const [selectedRenderType, setSelectedRenderType] = useState< VectorRenderType | undefined >(); const [symbologyTab, setSymbologyTab] = useState('color'); - if (!layerId) { + if (!props.layerId) { return; } - const layer = model.getLayer(layerId); + const layer = props.model.getLayer(props.layerId); if (!layer?.parameters) { return; } const { featureProperties, isLoading: featuresLoading } = useGetProperties({ - layerId, - model: model, + layerId: props.layerId, + model: props.model, }); useLayerRenderType(layer, setSelectedRenderType); @@ -186,11 +180,11 @@ const VectorRendering: React.FC = ({
= ({ - model, - state, - okSignalPromise, - cancel, - layerId, - symbologyTab, - selectableAttributesAndValues, -}) => { +const Categorized: React.FC = props => { const selectedAttributeRef = useRef(); const stopRowsRef = useRef(); const colorRampOptionsRef = useRef(); @@ -39,10 +31,10 @@ const Categorized: React.FC = ({ }); const manualStyleRef = useRef(manualStyle); - if (!layerId) { + if (!props.layerId) { return; } - const layer = model.getLayer(layerId); + const layer = props.model.getLayer(props.layerId); if (!layer?.parameters) { return; } @@ -52,12 +44,12 @@ const Categorized: React.FC = ({ setStopRows(valueColorPairs); - okSignalPromise.promise.then(okSignal => { + props.okSignalPromise.promise.then(okSignal => { okSignal.connect(handleOk, this); }); return () => { - okSignalPromise.promise.then(okSignal => { + props.okSignalPromise.promise.then(okSignal => { okSignal.disconnect(handleOk, this); }); }; @@ -93,7 +85,7 @@ const Categorized: React.FC = ({ radius: layer.parameters.color['circle-radius'] || 5, }); } - }, [layerId]); + }, [props.layerId]); useEffect(() => { manualStyleRef.current = manualStyle; @@ -104,10 +96,10 @@ const Categorized: React.FC = ({ const layerParams = layer.parameters as IVectorLayer; const attribute = layerParams.symbologyState?.value ?? - Object.keys(selectableAttributesAndValues)[0]; + Object.keys(props.selectableAttributesAndValues)[0]; setSelectedAttribute(attribute); - }, [selectableAttributesAndValues]); + }, [props.selectableAttributesAndValues]); useEffect(() => { selectedAttributeRef.current = selectedAttribute; @@ -129,7 +121,7 @@ const Categorized: React.FC = ({ }); const stops = Array.from( - selectableAttributesAndValues[selectedAttribute], + props.selectableAttributesAndValues[selectedAttribute], ).sort((a, b) => a - b); const valueColorPairs = Utils.getValueColorPairs( @@ -142,6 +134,9 @@ const Categorized: React.FC = ({ }; const handleOk = () => { + if (!props.layerId) { + throw new Error('layerId is required for Categorized component'); + } if (!layer.parameters) { return; } @@ -157,7 +152,7 @@ const Categorized: React.FC = ({ expr.push(stop.output); }); - if (symbologyTab === 'color') { + if (props.symbologyTab === 'color') { expr.push([0, 0, 0, 0.0]); // fallback color newStyle['fill-color'] = expr; @@ -181,7 +176,7 @@ const Categorized: React.FC = ({ colorRamp: colorRampOptionsRef.current?.selectedRamp, nClasses: colorRampOptionsRef.current?.numberOfShades, mode: colorRampOptionsRef.current?.selectedMode, - symbologyTab, + symbologyTab: props.symbologyTab, }; layer.parameters.symbologyState = symbologyState; @@ -191,11 +186,14 @@ const Categorized: React.FC = ({ layer.type = 'VectorLayer'; } - model.sharedModel.updateLayer(layerId, layer); - cancel(); + props.model.sharedModel.updateLayer(props.layerId, layer); + props.cancel(); }; const handleReset = (method: SymbologyTab) => { + if (!props.layerId) { + throw new Error('layerId is required for Categorized component'); + } if (!layer?.parameters) { return; } @@ -225,11 +223,11 @@ const Categorized: React.FC = ({ layer.parameters.color = newStyle; - model.sharedModel.updateLayer(layerId, layer); + props.model.sharedModel.updateLayer(props.layerId, layer); }; const body = (() => { - if (Object.keys(selectableAttributesAndValues).length === 0) { + if (Object.keys(props.selectableAttributesAndValues).length === 0) { return (

This symbology type is not available; no attributes contain numeric @@ -240,14 +238,14 @@ const Categorized: React.FC = ({ return ( <>

{/* Inputs depending on active tab */} - {symbologyTab === 'color' && ( + {props.symbologyTab === 'color' && ( <>
@@ -295,7 +293,7 @@ const Categorized: React.FC = ({ )} - {symbologyTab === 'radius' && ( + {props.symbologyTab === 'radius' && (
= ({ modeOptions={[]} classifyFunc={buildColorInfoFromClassification} showModeRow={false} - showRampSelector={symbologyTab === 'color'} + showRampSelector={props.symbologyTab === 'color'} /> = ({ - model, - state, - okSignalPromise, - cancel, - layerId, - symbologyTab, - selectableAttributesAndValues, -}) => { +const Graduated: React.FC = props => { const modeOptions = [ 'quantile', 'equal interval', @@ -55,10 +47,10 @@ const Graduated: React.FC = ({ const colorManualStyleRef = useRef(colorManualStyle); const radiusManualStyleRef = useRef(radiusManualStyle); - if (!layerId) { + if (!props.layerId) { return; } - const layer = model.getLayer(layerId); + const layer = props.model.getLayer(props.layerId); if (!layer?.parameters) { return; } @@ -66,12 +58,12 @@ const Graduated: React.FC = ({ useEffect(() => { updateStopRowsBasedOnLayer(); - okSignalPromise.promise.then(okSignal => { + props.okSignalPromise.promise.then(okSignal => { okSignal.connect(handleOk, this); }); return () => { - okSignalPromise.promise.then(okSignal => { + props.okSignalPromise.promise.then(okSignal => { okSignal.disconnect(handleOk, this); }); }; @@ -107,19 +99,19 @@ const Graduated: React.FC = ({ radius: layer.parameters.color['circle-radius'] || 5, }); } - }, [layerId]); + }, [props.layerId]); useEffect(() => { colorStopRowsRef.current = colorStopRows; radiusStopRowsRef.current = radiusStopRows; selectableAttributeRef.current = selectedAttribute; - symbologyTabRef.current = symbologyTab; + symbologyTabRef.current = props.symbologyTab; colorRampOptionsRef.current = colorRampOptions; }, [ colorStopRows, radiusStopRows, selectedAttribute, - symbologyTab, + props.symbologyTab, colorRampOptions, ]); @@ -132,10 +124,10 @@ const Graduated: React.FC = ({ const layerParams = layer.parameters as IVectorLayer; const attribute = layerParams.symbologyState?.value ?? - Object.keys(selectableAttributesAndValues)[0]; + Object.keys(props.selectableAttributesAndValues)[0]; setSelectedAttribute(attribute); - }, [selectableAttributesAndValues]); + }, [props.selectableAttributesAndValues]); const updateStopRowsBasedOnLayer = () => { if (!layer) { @@ -147,6 +139,9 @@ const Graduated: React.FC = ({ }; const handleOk = () => { + if (!props.layerId) { + throw new Error('layerId is required for Graduated component'); + } if (!layer.parameters) { return; } @@ -206,8 +201,8 @@ const Graduated: React.FC = ({ layer.type = 'VectorLayer'; } - model.sharedModel.updateLayer(layerId, layer); - cancel(); + props.model.sharedModel.updateLayer(props.layerId, layer); + props.cancel(); }; const buildColorInfoFromClassification = ( @@ -223,7 +218,7 @@ const Graduated: React.FC = ({ let stops; - const values = Array.from(selectableAttributesAndValues[selectedAttribute]); + const values = Array.from(props.selectableAttributesAndValues[selectedAttribute]); switch (selectedMode) { case 'quantile': @@ -262,11 +257,11 @@ const Graduated: React.FC = ({ } const stopOutputPairs = - symbologyTab === 'radius' + props.symbologyTab === 'radius' ? stops.map(v => ({ stop: v, output: v })) : Utils.getValueColorPairs(stops, selectedRamp, +numberOfShades); - if (symbologyTab === 'radius') { + if (props.symbologyTab === 'radius') { setRadiusStopRows(stopOutputPairs); } else { setColorStopRows(stopOutputPairs); @@ -274,6 +269,9 @@ const Graduated: React.FC = ({ }; const handleReset = (method: string) => { + if (!props.layerId) { + throw new Error('layerId is required for Graduated component'); + } if (!layer?.parameters) { return; } @@ -294,11 +292,11 @@ const Graduated: React.FC = ({ } layer.parameters.color = newStyle; - model.sharedModel.updateLayer(layerId, layer); + props.model.sharedModel.updateLayer(props.layerId, layer); }; const body = (() => { - if (Object.keys(selectableAttributesAndValues)?.length === 0) { + if (Object.keys(props.selectableAttributesAndValues)?.length === 0) { return (

This symbology type is not available; no attributes contain numeric @@ -309,12 +307,12 @@ const Graduated: React.FC = ({ return ( <>

- {symbologyTab === 'color' && ( + {props.symbologyTab === 'color' && ( <>
@@ -361,7 +359,7 @@ const Graduated: React.FC = ({
)} - {symbologyTab === 'radius' && ( + {props.symbologyTab === 'radius' && (
= ({ modeOptions={modeOptions} classifyFunc={buildColorInfoFromClassification} showModeRow={true} - showRampSelector={symbologyTab === 'color'} + showRampSelector={props.symbologyTab === 'color'} />