Skip to content
Snippets Groups Projects
Select Git revision
  • bf90b255e06dbe28e7f44e90b069f7f54f9dc262
  • mui5-annotation-on-video-stable default
  • get_setter_canvasSizeInformations
  • fix-error-div-into-p
  • annotation-on-video-v2
  • detached
  • annotation-on-video-r17
  • mui5
  • mui5-react-18
  • jacob-test
  • annotation-on-video protected
  • master
  • test-antoinev1
  • 20-fetch-thumbnail-on-annotation
  • add-research-field
  • Save
  • add-plugin
  • 14-wip-no-seek-to
  • 14-bug-on-video-time-control
  • 9_wip_videotests
  • _upgrade_material_ui
  • latest-tetras-16
  • v3.3.0
  • v3.2.0
  • v3.1.1
  • v3.1.0
  • v3.0.0
  • v3.0.0-rc.7
  • v3.0.0-rc.6
  • v3.0.0-rc.5
  • v3.0.0-rc.4
  • v3.0.0-rc.3
  • v3.0.0-rc.2
  • v3.0.0-rc.1
  • v3.0.0-beta.10
  • v3.0.0-beta.9
  • v3.0.0-beta.8
  • v3.0.0-beta.7
  • v3.0.0-beta.6
  • v3.0.0-beta.5
  • v3.0.0-beta.3
41 results

WindowTopMenuButton.js

Blame
  • AnnotationCreation.js 14.47 KiB
    import React, { Component } from 'react';
    import PropTypes from 'prop-types';
    import Button from '@material-ui/core/Button';
    import Typography from '@material-ui/core/Typography';
    import Paper from '@material-ui/core/Paper';
    import Grid from '@material-ui/core/Grid';
    import ToggleButton from '@material-ui/lab/ToggleButton';
    import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup';
    import RectangleIcon from '@material-ui/icons/CheckBoxOutlineBlank';
    import CircleIcon from '@material-ui/icons/RadioButtonUnchecked';
    import PolygonIcon from '@material-ui/icons/Timeline';
    import GestureIcon from '@material-ui/icons/Gesture';
    import ClosedPolygonIcon from '@material-ui/icons/ChangeHistory';
    import OpenPolygonIcon from '@material-ui/icons/ShowChart';
    import FormatColorFillIcon from '@material-ui/icons/FormatColorFill';
    import StrokeColorIcon from '@material-ui/icons/BorderColor';
    import LineWeightIcon from '@material-ui/icons/LineWeight';
    import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
    import FormatShapesIcon from '@material-ui/icons/FormatShapes';
    import Popover from '@material-ui/core/Popover';
    import Divider from '@material-ui/core/Divider';
    import MenuItem from '@material-ui/core/MenuItem';
    import ClickAwayListener from '@material-ui/core/ClickAwayListener';
    import MenuList from '@material-ui/core/MenuList';
    import { SketchPicker } from 'react-color';
    import { v4 as uuid } from 'uuid';
    import { withStyles } from '@material-ui/core/styles';
    import CompanionWindow from 'mirador/dist/es/src/containers/CompanionWindow';
    import AnnotationDrawing from './AnnotationDrawing';
    import TextEditor from './TextEditor';
    import WebAnnotation from './WebAnnotation';
    import CursorIcon from './icons/Cursor';
    
    /** */
    class AnnotationCreation extends Component {
      /** */
      constructor(props) {
        super(props);
        const annoState = {};
        if (props.annotation) {
          if (Array.isArray(props.annotation.body)) {
            annoState.tags = [];
            props.annotation.body.forEach((body) => {
              if (body.purpose === 'tagging') {
                annoState.tags.push(body.value);
              } else {
                annoState.annoBody = body.value;
              }
            });
          } else {
            annoState.annoBody = props.annotation.body.value;
          }
          if (props.annotation.target.selector) {
            if (Array.isArray(props.annotation.target.selector)) {
              props.annotation.target.selector.forEach((selector) => {
                if (selector.type === 'SvgSelector') {
                  annoState.svg = selector.value;
                } else if (selector.type === 'FragmentSelector') {
                  annoState.xywh = selector.value.replace('xywh=', '');
                }
              });
            } else {
              annoState.svg = props.annotation.target.selector.value;
            }
          }
        }
    
        const toolState = {
          activeTool: 'cursor',
          closedMode: 'closed',
          currentColorType: false,
          fillColor: null,
          strokeColor: '#00BFFF',
          strokeWidth: 3,
          ...(props.config.annotation.defaults || {}),
        };
    
        this.state = {
          ...toolState,
          annoBody: '',
          colorPopoverOpen: false,
          lineWeightPopoverOpen: false,
          popoverAnchorEl: null,
          popoverLineWeightAnchorEl: null,
          svg: null,
          textEditorStateBustingKey: 0,
          xywh: null,
          ...annoState,
        };
    
        this.submitForm = this.submitForm.bind(this);
        this.updateBody = this.updateBody.bind(this);
        this.updateGeometry = this.updateGeometry.bind(this);
        this.changeTool = this.changeTool.bind(this);
        this.changeClosedMode = this.changeClosedMode.bind(this);
        this.openChooseColor = this.openChooseColor.bind(this);
        this.openChooseLineWeight = this.openChooseLineWeight.bind(this);
        this.handleLineWeightSelect = this.handleLineWeightSelect.bind(this);
        this.handleCloseLineWeight = this.handleCloseLineWeight.bind(this);
        this.closeChooseColor = this.closeChooseColor.bind(this);
        this.updateStrokeColor = this.updateStrokeColor.bind(this);
      }
    
      /** */
      handleCloseLineWeight(e) {
        this.setState({
          lineWeightPopoverOpen: false,
          popoverLineWeightAnchorEl: null,
        });
      }
    
      /** */
      handleLineWeightSelect(e) {
        this.setState({
          lineWeightPopoverOpen: false,
          popoverLineWeightAnchorEl: null,
          strokeWidth: e.currentTarget.value,
        });
      }
    
      /** */
      openChooseColor(e) {
        this.setState({
          colorPopoverOpen: true,
          currentColorType: e.currentTarget.value,
          popoverAnchorEl: e.currentTarget,
        });
      }
    
      /** */
      openChooseLineWeight(e) {
        this.setState({
          lineWeightPopoverOpen: true,
          popoverLineWeightAnchorEl: e.currentTarget,
        });
      }
    
      /** */
      closeChooseColor(e) {
        this.setState({
          colorPopoverOpen: false,
          currentColorType: null,
          popoverAnchorEl: null,
        });
      }
    
      /** */
      updateStrokeColor(color) {
        const { currentColorType } = this.state;
        this.setState({
          [currentColorType]: color.hex,
        });
      }
    
      /** */
      submitForm(e) {
        e.preventDefault();
        const {
          annotation, canvases, receiveAnnotation, config,
        } = this.props;
        const {
          annoBody, tags, xywh, svg, textEditorStateBustingKey,
        } = this.state;
        canvases.forEach((canvas) => {
          const storageAdapter = config.annotation.adapter(canvas.id);
          const anno = new WebAnnotation({
            body: annoBody,
            canvasId: canvas.id,
            id: (annotation && annotation.id) || `${uuid()}`,
            manifestId: canvas.options.resource.id,
            svg,
            tags,
            xywh,
          }).toJson();
          if (annotation) {
            storageAdapter.update(anno).then((annoPage) => {
              receiveAnnotation(canvas.id, storageAdapter.annotationPageId, annoPage);
            });
          } else {
            storageAdapter.create(anno).then((annoPage) => {
              receiveAnnotation(canvas.id, storageAdapter.annotationPageId, annoPage);
            });
          }
        });
    
        this.setState({
          annoBody: '',
          svg: null,
          textEditorStateBustingKey: textEditorStateBustingKey + 1,
          xywh: null,
        });
      }
    
      /** */
      changeTool(e, tool) {
        this.setState({
          activeTool: tool,
        });
      }
    
      /** */
      changeClosedMode(e) {
        this.setState({
          closedMode: e.currentTarget.value,
        });
      }
    
      /** */
      updateBody(annoBody) {
        this.setState({ annoBody });
      }
    
      /** */
      updateGeometry({ svg, xywh }) {
        this.setState({
          svg, xywh,
        });
      }
    
      /** */
      render() {
        const {
          annotation, classes, closeCompanionWindow, id, windowId,
        } = this.props;
    
        const {
          activeTool, colorPopoverOpen, currentColorType, fillColor, popoverAnchorEl, strokeColor,
          popoverLineWeightAnchorEl, lineWeightPopoverOpen, strokeWidth, closedMode, annoBody, svg,
          textEditorStateBustingKey,
        } = this.state;
        return (
          <CompanionWindow
            title={annotation ? 'Edit annotation' : 'New annotation'}
            windowId={windowId}
            id={id}
          >
            <AnnotationDrawing
              activeTool={activeTool}
              fillColor={fillColor}
              strokeColor={strokeColor}
              strokeWidth={strokeWidth}
              closed={closedMode === 'closed'}
              svg={svg}
              updateGeometry={this.updateGeometry}
              windowId={windowId}
            />
            <form onSubmit={this.submitForm} className={classes.section}>
              <Grid container>
                <Grid item xs={12}>
                  <Typography variant="overline">
                    Target
                  </Typography>
                </Grid>
                <Grid item xs={12}>
                  <Paper elevation={0} className={classes.paper}>
                    <ToggleButtonGroup
                      className={classes.grouped}
                      value={activeTool}
                      exclusive
                      onChange={this.changeTool}
                      aria-label="tool selection"
                      size="small"
                    >
                      <ToggleButton value="cursor" aria-label="select cursor">
                        <CursorIcon />
                      </ToggleButton>
                      <ToggleButton value="edit" aria-label="select cursor">
                        <FormatShapesIcon />
                      </ToggleButton>
                    </ToggleButtonGroup>
                    <Divider flexItem orientation="vertical" className={classes.divider} />
                    <ToggleButtonGroup
                      className={classes.grouped}
                      value={activeTool}
                      exclusive
                      onChange={this.changeTool}
                      aria-label="tool selection"
                      size="small"
                    >
                      <ToggleButton value="rectangle" aria-label="add a rectangle">
                        <RectangleIcon />
                      </ToggleButton>
                      <ToggleButton value="ellipse" aria-label="add a circle">
                        <CircleIcon />
                      </ToggleButton>
                      <ToggleButton value="polygon" aria-label="add a polygon">
                        <PolygonIcon />
                      </ToggleButton>
                      <ToggleButton value="freehand" aria-label="free hand polygon">
                        <GestureIcon />
                      </ToggleButton>
                    </ToggleButtonGroup>
                  </Paper>
                </Grid>
              </Grid>
              <Grid container>
                <Grid item xs={12}>
                  <Typography variant="overline">
                    Style
                  </Typography>
                </Grid>
                <Grid item xs={12}>
                  <ToggleButtonGroup
                    aria-label="style selection"
                    size="small"
                  >
                    <ToggleButton
                      value="strokeColor"
                      aria-label="select color"
                      onClick={this.openChooseColor}
                    >
                      <StrokeColorIcon style={{ fill: strokeColor }} />
                      <ArrowDropDownIcon />
                    </ToggleButton>
                    <ToggleButton
                      value="strokeColor"
                      aria-label="select line weight"
                      onClick={this.openChooseLineWeight}
                    >
                      <LineWeightIcon />
                      <ArrowDropDownIcon />
                    </ToggleButton>
                    <ToggleButton
                      value="fillColor"
                      aria-label="select color"
                      onClick={this.openChooseColor}
                    >
                      <FormatColorFillIcon style={{ fill: fillColor }} />
                      <ArrowDropDownIcon />
                    </ToggleButton>
                  </ToggleButtonGroup>
    
                  <Divider flexItem orientation="vertical" className={classes.divider} />
                  { /* close / open polygon mode only for freehand drawing mode. */
                    activeTool === 'freehand'
                      ? (
                        <ToggleButtonGroup
                          size="small"
                          value={closedMode}
                          onChange={this.changeClosedMode}
                        >
                          <ToggleButton value="closed">
                            <ClosedPolygonIcon />
                          </ToggleButton>
                          <ToggleButton value="open">
                            <OpenPolygonIcon />
                          </ToggleButton>
                        </ToggleButtonGroup>
                      )
                      : null
                  }
    
                </Grid>
              </Grid>
              <Grid container>
                <Grid item xs={12}>
                  <Typography variant="overline">
                    Content
                  </Typography>
                </Grid>
                <Grid item xs={12}>
                  <TextEditor
                    key={textEditorStateBustingKey}
                    annoHtml={annoBody}
                    updateAnnotationBody={this.updateBody}
                  />
                </Grid>
              </Grid>
              <Button onClick={closeCompanionWindow}>
                Cancel
              </Button>
              <Button variant="contained" color="primary" type="submit">
                Save
              </Button>
            </form>
            <Popover
              open={lineWeightPopoverOpen}
              anchorEl={popoverLineWeightAnchorEl}
            >
              <Paper>
                <ClickAwayListener onClickAway={this.handleCloseLineWeight}>
                  <MenuList autoFocus role="listbox">
                    {[1, 3, 5, 10, 50].map((option, index) => (
                      <MenuItem
                        key={option}
                        onClick={this.handleLineWeightSelect}
                        value={option}
                        selected={option == strokeWidth}
                        role="option"
                        aria-selected={option == strokeWidth}
                      >
                        {option}
                      </MenuItem>
                    ))}
                  </MenuList>
                </ClickAwayListener>
              </Paper>
            </Popover>
            <Popover
              open={colorPopoverOpen}
              anchorEl={popoverAnchorEl}
              onClose={this.closeChooseColor}
            >
              <SketchPicker
                // eslint-disable-next-line react/destructuring-assignment
                color={this.state[currentColorType] || {}}
                onChangeComplete={this.updateStrokeColor}
              />
            </Popover>
          </CompanionWindow>
        );
      }
    }
    
    /** */
    const styles = (theme) => ({
      divider: {
        margin: theme.spacing(1, 0.5),
      },
      grouped: {
        '&:first-child': {
          borderRadius: theme.shape.borderRadius,
        },
        '&:not(:first-child)': {
          borderRadius: theme.shape.borderRadius,
        },
        border: 'none',
        margin: theme.spacing(0.5),
      },
      paper: {
        display: 'flex',
        flexWrap: 'wrap',
      },
      section: {
        paddingBottom: theme.spacing(1),
        paddingLeft: theme.spacing(2),
        paddingRight: theme.spacing(1),
        paddingTop: theme.spacing(2),
      },
    });
    
    AnnotationCreation.propTypes = {
      annotation: PropTypes.object, // eslint-disable-line react/forbid-prop-types
      canvases: PropTypes.arrayOf(
        PropTypes.shape({ id: PropTypes.string, index: PropTypes.number }),
      ),
      classes: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
      closeCompanionWindow: PropTypes.func,
      config: PropTypes.shape({
        annotation: PropTypes.shape({
          adapter: PropTypes.func,
          defaults: PropTypes.objectOf(
            PropTypes.oneOfType(
              [PropTypes.bool, PropTypes.func, PropTypes.number, PropTypes.string]
            )
          ),
        }),
      }).isRequired,
      id: PropTypes.string.isRequired,
      receiveAnnotation: PropTypes.func.isRequired,
      windowId: PropTypes.string.isRequired,
    };
    
    AnnotationCreation.defaultProps = {
      annotation: null,
      canvases: [],
      closeCompanionWindow: () => {},
    };
    
    export default withStyles(styles)(AnnotationCreation);