From 8266c80c58df4c3e85557943d61aaa5eb413ae76 Mon Sep 17 00:00:00 2001 From: Chris Beer Date: Wed, 20 Nov 2024 13:34:19 -0800 Subject: [PATCH] Update CanvasLayers to a function. --- src/components/CanvasLayers.js | 441 ++++++++++++++++----------------- 1 file changed, 214 insertions(+), 227 deletions(-) diff --git a/src/components/CanvasLayers.js b/src/components/CanvasLayers.js index 87f784900..af0f76943 100644 --- a/src/components/CanvasLayers.js +++ b/src/components/CanvasLayers.js @@ -1,7 +1,6 @@ -import { Component } from 'react'; +import { useCallback, useId } from 'react'; import PropTypes from 'prop-types'; import { styled } from '@mui/material/styles'; -import { v4 as uuid } from 'uuid'; import Input from '@mui/material/Input'; import InputAdornment from '@mui/material/InputAdornment'; import List from '@mui/material/List'; @@ -41,47 +40,182 @@ const reorder = (list, startIndex, endIndex) => { }; /** */ -export class CanvasLayers extends Component { - /** */ - static getUseableLabel(resource, index) { - return (resource - && resource.getLabel - && resource.getLabel().length > 0) - ? resource.getLabel().getValue() - : String(index + 1); - } +function getUseableLabel(resource, index) { + return (resource + && resource.getLabel + && resource.getLabel().length > 0) + ? resource.getLabel().getValue() + : String(index + 1); +} - /** */ - constructor(props) { - super(props); - this.droppableId = uuid(); - this.onDragEnd = this.onDragEnd.bind(this); - this.handleOpacityChange = this.handleOpacityChange.bind(this); - this.setLayerVisibility = this.setLayerVisibility.bind(this); - this.moveToTop = this.moveToTop.bind(this); - } +/** @private */ +function Layer({ + resource, layerMetadata = {}, index, t, handleOpacityChange, setLayerVisibility, moveToTop, +}) { + const { width, height } = { height: undefined, width: 50 }; - /** */ - handleOpacityChange(layerId, value) { - const { - canvasId, updateLayers, windowId, - } = this.props; + const layer = { + opacity: 1, + visibility: true, + ...(layerMetadata || {}), + }; + + return ( +
+
+ + + {getUseableLabel(resource, index)} +
+ { setLayerVisibility(resource.id, !layer.visibility); }}> + { layer.visibility ? : } + + { layer.index !== 0 && ( + { moveToTop(resource.id); }}> + + + )} +
+
+
+
+ + + + handleOpacityChange(resource.id, e.target.value)} + endAdornment={%} + inputProps={{ + 'aria-label': t('layer_opacity'), + }} + /> + handleOpacityChange(resource.id, value)} + /> +
+
+ ); +} + +Layer.propTypes = { + handleOpacityChange: PropTypes.func.isRequired, + index: PropTypes.number.isRequired, + layerMetadata: PropTypes.objectOf(PropTypes.shape({ + opacity: PropTypes.number, + visibility: PropTypes.bool, + })), // eslint-disable-line react/forbid-prop-types + moveToTop: PropTypes.func.isRequired, + resource: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types + setLayerVisibility: PropTypes.func.isRequired, + t: PropTypes.func.isRequired, +}; + +/** @private */ +function DraggableLayer({ + children, resource, index, t, +}) { + return ( + + {(provided, snapshot) => ( + + + + + + + { children } + + )} + + ); +} + +DraggableLayer.propTypes = { + children: PropTypes.node.isRequired, + index: PropTypes.number.isRequired, + resource: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types + t: PropTypes.func.isRequired, +}; + +/** */ +export function CanvasLayers({ + canvasId, index, label, layers, layerMetadata = {}, t, totalSize, updateLayers, windowId, +}) { + const droppableId = useId(); + const handleOpacityChange = useCallback((layerId, value) => { const payload = { [layerId]: { opacity: value / 100.0 }, }; updateLayers(windowId, canvasId, payload); - } + }, [canvasId, updateLayers, windowId]); /** */ - onDragEnd(result) { - const { - canvasId, layers, updateLayers, windowId, - } = this.props; + const onDragEnd = useCallback((result) => { if (!result.destination) return; - if (result.destination.droppableId !== this.droppableId) return; - if (result.source.droppableId !== this.droppableId) return; + if (result.destination.droppableId !== droppableId) return; + if (result.source.droppableId !== droppableId) return; const sortedLayers = reorder( layers.map(l => l.id), @@ -95,27 +229,19 @@ export class CanvasLayers extends Component { }, {}); updateLayers(windowId, canvasId, payload); - } + }, [canvasId, droppableId, layers, updateLayers, windowId]); /** */ - setLayerVisibility(layerId, value) { - const { - canvasId, updateLayers, windowId, - } = this.props; - + const setLayerVisibility = useCallback((layerId, value) => { const payload = { [layerId]: { visibility: value }, }; updateLayers(windowId, canvasId, payload); - } + }, [canvasId, updateLayers, windowId]); /** */ - moveToTop(layerId) { - const { - canvasId, layers, updateLayers, windowId, - } = this.props; - + const moveToTop = useCallback((layerId) => { const sortedLayers = reorder(layers.map(l => l.id), layers.findIndex(l => l.id === layerId), 0); const payload = layers.reduce((acc, layer) => { @@ -124,189 +250,54 @@ export class CanvasLayers extends Component { }, {}); updateLayers(windowId, canvasId, payload); - } - - /** @private */ - renderLayer(resource, index) { - const { - layerMetadata, - t, - } = this.props; + }, [canvasId, layers, updateLayers, windowId]); - const { width, height } = { height: undefined, width: 50 }; - - const layer = { - opacity: 1, - visibility: true, - ...(layerMetadata || {})[resource.id], - }; - - return ( -
-
- - - {CanvasLayers.getUseableLabel(resource, index)} -
- { this.setLayerVisibility(resource.id, !layer.visibility); }}> - { layer.visibility ? : } - - { layer.index !== 0 && ( - { this.moveToTop(resource.id); }}> - - - )} -
-
-
-
- - - - this.handleOpacityChange(resource.id, e.target.value)} - endAdornment={%} - inputProps={{ - 'aria-label': t('layer_opacity'), - }} - /> - this.handleOpacityChange(resource.id, value)} - /> -
-
- ); - } - - /** @private */ - renderDraggableLayer(resource, index) { - const { - t, - } = this.props; - - return ( - - {(provided, snapshot) => ( - - + { totalSize > 1 && ( + + {t('annotationCanvasLabel', { context: `${index + 1}/${totalSize}`, label })} + + )} + + + {(provided, snapshot) => ( + - - - - - {this.renderLayer(resource, index)} - - )} - - ); - } - - /** */ - render() { - const { - index, - label, - layers, - t, - totalSize, - } = this.props; - - return ( - <> - { totalSize > 1 && ( - - {t('annotationCanvasLabel', { context: `${index + 1}/${totalSize}`, label })} - - )} - - - {(provided, snapshot) => ( - - { - layers && layers.map((r, i) => ( - this.renderDraggableLayer(r, i) - )) - } - {provided.placeholder} - - )} - - - - ); - } + { + layers && layers.map((r, i) => ( + + + + )) + } + {provided.placeholder} + + )} + + + + ); } CanvasLayers.propTypes = { @@ -323,7 +314,3 @@ CanvasLayers.propTypes = { updateLayers: PropTypes.func.isRequired, windowId: PropTypes.string.isRequired, }; - -CanvasLayers.defaultProps = { - layerMetadata: undefined, -};