From 65f55902ce64900b514a5a2cd0cdb81863df0c43 Mon Sep 17 00:00:00 2001
From: Anthony Geourjon <anthony.geourjon@tetras-libre.fr>
Date: Fri, 29 Dec 2023 17:13:57 +0100
Subject: [PATCH] MigratingAnnotationCreation to MUI5. It's possible to display
 the creation annotation window, not validate the creation.

---
 src/AnnotationCreation.js |   6 +-
 src/HMSInput.js           | 168 +++++++++++++++-----------------------
 src/ImageFormField.js     |  84 ++++++++-----------
 3 files changed, 105 insertions(+), 153 deletions(-)

diff --git a/src/AnnotationCreation.js b/src/AnnotationCreation.js
index 062ac07..c1ab3dc 100644
--- a/src/AnnotationCreation.js
+++ b/src/AnnotationCreation.js
@@ -538,7 +538,7 @@ class AnnotationCreation extends Component {
                         <Alarm fontSize="small" />
                       </ToggleButton>
                     </div>
-                    {/* <HMSInput seconds={tstart} onChange={this.updateTstart} /> */}
+                    <HMSInput seconds={tstart} onChange={this.updateTstart} />
                   </div>
                   <div style={{
                     border: '1px solid rgba(0, 0, 0, 0.12)',
@@ -580,7 +580,7 @@ class AnnotationCreation extends Component {
                         <Alarm fontSize="small" />
                       </ToggleButton>
                     </div>
-                    {/* <HMSInput seconds={tend} onChange={this.updateTend} /> */}
+                    <HMSInput seconds={tend} onChange={this.updateTend} />
                   </div>
                 </div>
               </>
@@ -594,7 +594,7 @@ class AnnotationCreation extends Component {
                 </Typography>
               </Grid>
               <Grid item xs={12} style={{ marginBottom: 10 }}>
-                {/* <ImageFormField value={image} onChange={this.handleImgChange} /> */}
+                <ImageFormField value={image} onChange={this.handleImgChange} />
               </Grid>
             </Grid>
           </div>
diff --git a/src/HMSInput.js b/src/HMSInput.js
index ef04452..8270c58 100644
--- a/src/HMSInput.js
+++ b/src/HMSInput.js
@@ -1,117 +1,81 @@
-import React, { Component } from 'react';
+import React, { useState, useEffect } from 'react';
 import PropTypes from 'prop-types';
-import { withStyles } from '@mui/material/styles';
-import { Input } from '@mui/material';
+import { Input, styled } from '@mui/material';
 import { secondsToHMSarray } from './utils';
 
-/** hh:mm:ss input which behave like a single input for parent */
-class HMSInput extends Component {
-  /** Initialize state structure & bindings */
-  constructor(props) {
-    super(props);
+const StyledInput = styled(Input)(({ theme }) => ({
+  height: 'fit-content',
+  margin: '2px',
+  '& input[type=number]': {
+    '-moz-appearance': 'textfield',
+  },
+  '& input[type=number]::-webkit-outer-spin-button, & input[type=number]::-webkit-inner-spin-button': {
+    '-webkit-appearance': 'none',
+    margin: 0,
+  },
+}));
 
-    // eslint-disable-next-line react/destructuring-assignment
-    const [h, m, s] = secondsToHMSarray(this.props.seconds);
-    this.state = {
-      hours: h,
-      minutes: m,
-      seconds: s,
-    };
-    this.someChange = this.someChange.bind(this);
-  }
+const StyledHMSLabel = styled('span')({
+  color: 'grey',
+});
 
-  /** update */
-  componentDidUpdate(prevProps) {
-    const { seconds } = this.props;
-    if (prevProps.seconds === seconds) return;
-    const [h, m, s] = secondsToHMSarray(seconds);
-    this.setState({
-      hours: h,
-      minutes: m,
-      seconds: s,
-    });
-  }
+const StyledRoot = styled('div')({
+  alignItems: 'center',
+  display: 'flex',
+});
 
-  /** If one value is updated, tell the parent component the total seconds counts */
-  someChange(ev) {
-    const { onChange } = this.props;
-    const { state } = this;
-    state[ev.target.name] = Number(ev.target.value);
-    onChange(state.hours * 3600 + state.minutes * 60 + state.seconds);
-  }
+function HMSInput({ seconds, onChange }) {
+  const [hms, setHms] = useState(secondsToHMSarray(seconds));
 
-  /** Render */
-  render() {
-    const { hours, minutes, seconds } = this.state;
-    const { classes } = this.props;
-    return (
-      <div className={classes.root}>
-        <div className={classes.root}>
-          <Input
-            className={classes.input}
-            variant="filled"
-            type="number"
-            min="0"
-            pattern
-            name="hours"
-            value={hours}
-            onChange={this.someChange}
-            inputProps={{ style: { textAlign: 'center' } }}
-          />
-          <span className={classes.hmsLabel}>h</span>
-          <Input className={classes.input} type="number" min="0" max="59" name="minutes" value={minutes} onChange={this.someChange} inputProps={{ style: { textAlign: 'center' } }} />
-          <span className={classes.hmsLabel}>m</span>
-          <Input className={classes.input} type="number" min="0" max="59" name="seconds" value={seconds} onChange={this.someChange} inputProps={{ style: { textAlign: 'center' } }} />
-          <span className={classes.hmsLabel}>s</span>
-        </div>
-      </div>
-    );
-  }
-}
+  useEffect(() => {
+    setHms(secondsToHMSarray(seconds));
+  }, [seconds]);
 
-/** */
-const styles = (theme) => ({
-  root: {
-    alignItems: 'center',
-    display: 'flex',
-  },
-  // eslint-disable-next-line sort-keys
-  flexcol: {
-    display: 'flex',
-    flexDirection: 'column',
-    justifyContent: 'center',
-  },
-  hmsLabel: {
-    color: 'grey',
-  },
-  // eslint-disable-next-line sort-keys
-  input: {
-    height: 'fit-content',
-    margin: '2px',
-    // remove arrow from field for Firefox
-    '& input[type=number]': {
-      '-moz-appearance': 'textfield',
-    },
-    // remove arrow from field for Chrome, Safari and Opera
-    '& input[type=number]::-webkit-outer-spin-button': {
-      '-webkit-appearance': 'none',
-      margin: 0,
-    },
-    '& input[type=number]::-webkit-inner-spin-button': {
-      '-webkit-appearance': 'none',
-      margin: 0,
-    },
-  },
-});
+  const someChange = (ev) => {
+    const newState = { ...hms, [ev.target.name]: Number(ev.target.value) };
+    setHms(newState);
+    onChange(newState.hours * 3600 + newState.minutes * 60 + newState.seconds);
+  };
+
+  return (
+    <StyledRoot>
+      <StyledInput
+        variant="filled"
+        type="number"
+        min="0"
+        name="hours"
+        value={hms.hours}
+        onChange={someChange}
+        inputProps={{ style: { textAlign: 'center' } }}
+      />
+      <StyledHMSLabel>h</StyledHMSLabel>
+      <StyledInput
+        type="number"
+        min="0"
+        max="59"
+        name="minutes"
+        value={hms.minutes}
+        onChange={someChange}
+        inputProps={{ style: { textAlign: 'center' } }}
+      />
+      <StyledHMSLabel>m</StyledHMSLabel>
+      <StyledInput
+        type="number"
+        min="0"
+        max="59"
+        name="seconds"
+        value={hms.seconds}
+        onChange={someChange}
+        inputProps={{ style: { textAlign: 'center' } }}
+      />
+      <StyledHMSLabel>s</StyledHMSLabel>
+    </StyledRoot>
+  );
+}
 
 HMSInput.propTypes = {
-  // eslint-disable-next-line react/forbid-prop-types
-  classes: PropTypes.object.isRequired,
   onChange: PropTypes.func.isRequired,
   seconds: PropTypes.number.isRequired,
 };
 
-HMSInput.defaultProps = {
-};
-
 export default HMSInput;
diff --git a/src/ImageFormField.js b/src/ImageFormField.js
index 12ab29c..5fe3d13 100644
--- a/src/ImageFormField.js
+++ b/src/ImageFormField.js
@@ -1,62 +1,50 @@
-import React, { Component } from 'react';
+import React, { useRef, useState, useEffect } from 'react';
 import PropTypes from 'prop-types';
-import { withStyles } from '@mui/material/styles';
-import { TextField } from '@mui/material';
+import { TextField, styled } from '@mui/material';
 
-/** URL input with an <img> preview */
-class ImageFormField extends Component {
-  /** */
-  constructor(props) {
-    super(props);
+const StyledRoot = styled('div')(({ theme }) => ({
+  alignItems: 'center',
+  display: 'flex',
+  flexDirection: 'column',
+  justifyContent: 'center',
+}));
 
-    this.inputRef = React.createRef();
-  }
+function ImageFormField({ value: image, onChange }) {
+  const inputRef = useRef(null);
+  const [imgIsValid, setImgIsValid] = useState(false);
 
-  /** Render input field and a preview if the input is valid */
-  render() {
-    const { value: image, classes, onChange } = this.props;
-    const imgIsValid = this.inputRef.current
-      ? (image.id && this.inputRef.current.checkValidity()) : image.id;
-    const imgUrl = image.id === null ? '' : image.id;
-    return (
-      <div className={classes.root}>
-        <TextField
-          value={imgUrl}
-          onChange={(ev) => onChange(ev.target.value)}
-          error={imgUrl !== '' && !imgIsValid}
-          margin="dense"
-          label="Image URL"
-          type="url"
-          fullWidth
-          inputRef={this.inputRef}
-        />
-        { imgIsValid
-          && <img src={image.id} width="100%" height="auto" alt="loading failed" /> }
-      </div>
-    );
-  }
-}
+  useEffect(() => {
+    if (inputRef.current) {
+      setImgIsValid(image.id && inputRef.current.checkValidity());
+    } else {
+      setImgIsValid(!!image.id);
+    }
+  }, [image]);
+
+  const imgUrl = image.id === null ? '' : image.id;
 
-/** custom css */
-const styles = (theme) => ({
-  root: {
-    alignItems: 'center',
-    display: 'flex',
-    flexDirection: 'column',
-    justifyContent: 'center',
-  },
-});
+  return (
+    <StyledRoot>
+      <TextField
+        value={imgUrl}
+        onChange={(ev) => onChange(ev.target.value)}
+        error={imgUrl !== '' && !imgIsValid}
+        margin="dense"
+        label="Image URL"
+        type="url"
+        fullWidth
+        inputRef={inputRef}
+      />
+      {imgIsValid && <img src={image.id} width="100%" height="auto" alt="loading failed" />}
+    </StyledRoot>
+  );
+}
 
 ImageFormField.propTypes = {
-  // eslint-disable-next-line react/forbid-prop-types
-  classes: PropTypes.object.isRequired,
   onChange: PropTypes.func.isRequired,
   value: PropTypes.shape({
     id: PropTypes.string,
   }).isRequired,
 };
 
-ImageFormField.defaultProps = {
-};
-
 export default ImageFormField;
-- 
GitLab