From 465e28a22222a84310803eb14532a460a0089950 Mon Sep 17 00:00:00 2001
From: Paul Kaplan <pkaplan@media.mit.edu>
Date: Wed, 25 Jul 2018 12:12:07 -0400
Subject: [PATCH] Use img instead of CostumeCanvas

---
 .../costume-canvas/costume-canvas.jsx         | 134 ------------------
 .../sprite-selector-item.css                  |   2 +
 .../sprite-selector-item.jsx                  |   7 +-
 .../stage-selector/stage-selector.css         |   2 +
 .../stage-selector/stage-selector.jsx         |   7 +-
 .../sprite-selector-item.test.jsx.snap        |  22 +--
 .../components/sprite-selector-item.test.jsx  |  12 --
 7 files changed, 12 insertions(+), 174 deletions(-)
 delete mode 100644 src/components/costume-canvas/costume-canvas.jsx

diff --git a/src/components/costume-canvas/costume-canvas.jsx b/src/components/costume-canvas/costume-canvas.jsx
deleted file mode 100644
index 4375572c1..000000000
--- a/src/components/costume-canvas/costume-canvas.jsx
+++ /dev/null
@@ -1,134 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import svgToImage from 'svg-to-image';
-import xhr from 'xhr';
-
-/**
- * @fileoverview
- * A component for rendering Scratch costume URLs to canvases.
- * Use for sprite library, costume library, sprite selector, etc.
- * Props include width, height, and direction (direction in Scratch value).
- */
-
-class CostumeCanvas extends React.Component {
-    componentDidMount () {
-        this.load();
-    }
-    componentDidUpdate (prevProps) {
-        if (prevProps.url !== this.props.url) {
-            this.load();
-        } else if (
-            prevProps.width !== this.props.width ||
-            prevProps.height !== this.props.height ||
-            prevProps.direction !== this.props.direction
-        ) {
-            this.draw();
-        }
-    }
-    draw () {
-        if (!this.canvas) {
-            return;
-        }
-
-        // Draw the costume to the rendered canvas.
-        const img = this.img;
-        const context = this.canvas.getContext('2d');
-
-        // Scale to fit.
-        let scale;
-
-        // Choose the larger dimension to scale by.
-        if (img.width > img.height) {
-            scale = this.canvas.width / img.width;
-        } else {
-            scale = this.canvas.height / img.height;
-        }
-
-        // Rotate by the Scratch-value direction.
-        const angle = (-90 + this.props.direction) * Math.PI / 180;
-
-        // Rotation origin point will be center of the canvas.
-        const contextTranslateX = this.canvas.width / 2;
-        const contextTranslateY = this.canvas.height / 2;
-
-        // First, clear the canvas.
-        context.clearRect(0, 0,
-            this.canvas.width, this.canvas.height);
-
-        // Translate the context to the center of the canvas,
-        // then rotate canvas drawing by `angle`.
-        context.translate(contextTranslateX, contextTranslateY);
-        context.rotate(angle);
-        context.drawImage(img,
-            0, 0, img.width, img.height,
-            -(scale * img.width / 2), -(scale * img.height / 2),
-            scale * img.width,
-            scale * img.height);
-
-        // Reset the canvas rotation and translation to 0, (0, 0).
-        context.rotate(-angle);
-        context.translate(-contextTranslateX, -contextTranslateY);
-    }
-    load () {
-        // Draw the icon on our canvas.
-        const url = this.props.url;
-        if (url.indexOf('.svg') > -1) {
-            // Vector graphics: need to download with XDR and rasterize.
-            // Queue request asynchronously.
-            setTimeout(() => {
-                xhr.get({
-                    useXDR: true,
-                    url: url
-                }, (err, response, body) => {
-                    if (!err) {
-                        svgToImage(body, (svgErr, img) => {
-                            if (!svgErr) {
-                                this.img = img;
-                                this.draw();
-                            }
-                        });
-                    }
-                });
-            }, 0);
-
-        } else {
-            // Raster graphics: create Image and draw it.
-            const img = new Image();
-            img.src = url;
-            img.onload = () => {
-                this.img = img;
-                this.draw();
-            };
-        }
-    }
-    render () {
-        return (
-            <canvas
-                className={this.props.className}
-                height={this.props.height * (window.devicePixelRatio || 1)}
-                style={{
-                    height: `${this.props.height}px`,
-                    width: `${this.props.width}px`
-                }}
-                width={this.props.width * (window.devicePixelRatio || 1)}
-                ref={c => (this.canvas = c)} // eslint-disable-line react/jsx-sort-props
-            />
-        );
-    }
-}
-
-CostumeCanvas.defaultProps = {
-    width: 100,
-    height: 100,
-    direction: 90
-};
-
-CostumeCanvas.propTypes = {
-    className: PropTypes.string,
-    direction: PropTypes.number,
-    height: PropTypes.number,
-    url: PropTypes.string.isRequired,
-    width: PropTypes.number
-};
-
-export default CostumeCanvas;
diff --git a/src/components/sprite-selector-item/sprite-selector-item.css b/src/components/sprite-selector-item/sprite-selector-item.css
index 32a5339a7..24bf17395 100644
--- a/src/components/sprite-selector-item/sprite-selector-item.css
+++ b/src/components/sprite-selector-item/sprite-selector-item.css
@@ -42,6 +42,8 @@
 .sprite-image {
     margin: auto;
     user-select: none;
+    max-width: 32px;
+    max-height: 32px;
 }
 
 .sprite-info {
diff --git a/src/components/sprite-selector-item/sprite-selector-item.jsx b/src/components/sprite-selector-item/sprite-selector-item.jsx
index cb04c034a..50554f940 100644
--- a/src/components/sprite-selector-item/sprite-selector-item.jsx
+++ b/src/components/sprite-selector-item/sprite-selector-item.jsx
@@ -2,7 +2,6 @@ import classNames from 'classnames';
 import PropTypes from 'prop-types';
 import React from 'react';
 
-import CostumeCanvas from '../costume-canvas/costume-canvas.jsx';
 import CloseButton from '../close-button/close-button.jsx';
 import styles from './sprite-selector-item.css';
 import {ContextMenuTrigger} from 'react-contextmenu';
@@ -38,11 +37,9 @@ const SpriteSelectorItem = props => (
             <div className={styles.number}>{props.number}</div>
         )}
         {props.costumeURL ? (
-            <CostumeCanvas
+            <img
                 className={styles.spriteImage}
-                height={32}
-                url={props.costumeURL}
-                width={32}
+                src={props.costumeURL}
             />
         ) : null}
         <div className={styles.spriteInfo}>
diff --git a/src/components/stage-selector/stage-selector.css b/src/components/stage-selector/stage-selector.css
index 4e35dd4b6..a16875990 100644
--- a/src/components/stage-selector/stage-selector.css
+++ b/src/components/stage-selector/stage-selector.css
@@ -89,6 +89,8 @@ $header-height: calc($stage-menu-height - 2px);
     border: 1px solid $ui-black-transparent;
     border-radius: .25rem;
     box-shadow: inset 0 0 4px $ui-black-transparent;
+    max-width: 64px;
+    max-height: 48px;
 }
 
 .add-button {
diff --git a/src/components/stage-selector/stage-selector.jsx b/src/components/stage-selector/stage-selector.jsx
index 50161bf8c..f5de8b7a8 100644
--- a/src/components/stage-selector/stage-selector.jsx
+++ b/src/components/stage-selector/stage-selector.jsx
@@ -5,7 +5,6 @@ import {defineMessages, intlShape, injectIntl, FormattedMessage} from 'react-int
 
 import Box from '../box/box.jsx';
 import ActionMenu from '../action-menu/action-menu.jsx';
-import CostumeCanvas from '../costume-canvas/costume-canvas.jsx';
 import styles from './stage-selector.css';
 
 import backdropIcon from '../action-menu/icon--backdrop.svg';
@@ -78,11 +77,9 @@ const StageSelector = props => {
                 </div>
             </div>
             {url ? (
-                <CostumeCanvas
+                <img
                     className={styles.costumeCanvas}
-                    height={48}
-                    url={url}
-                    width={64}
+                    src={url}
                 />
             ) : null}
             <div className={styles.label}>
diff --git a/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap b/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap
index 2c276ff65..237e3dc29 100644
--- a/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap
+++ b/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap
@@ -30,16 +30,9 @@ exports[`SpriteSelectorItemComponent matches snapshot when given a number and de
   >
     5
   </div>
-  <canvas
+  <img
     className={undefined}
-    height={32}
-    style={
-      Object {
-        "height": "32px",
-        "width": "32px",
-      }
-    }
-    width={32}
+    src="https://scratch.mit.edu/foo/bar/pony"
   />
   <div
     className={undefined}
@@ -113,16 +106,9 @@ exports[`SpriteSelectorItemComponent matches snapshot when selected 1`] = `
       src="test-file-stub"
     />
   </div>
-  <canvas
+  <img
     className={undefined}
-    height={32}
-    style={
-      Object {
-        "height": "32px",
-        "width": "32px",
-      }
-    }
-    width={32}
+    src="https://scratch.mit.edu/foo/bar/pony"
   />
   <div
     className={undefined}
diff --git a/test/unit/components/sprite-selector-item.test.jsx b/test/unit/components/sprite-selector-item.test.jsx
index 1bf52fe97..49e3fa705 100644
--- a/test/unit/components/sprite-selector-item.test.jsx
+++ b/test/unit/components/sprite-selector-item.test.jsx
@@ -1,7 +1,6 @@
 import React from 'react';
 import {mountWithIntl, shallowWithIntl, componentWithIntl} from '../../helpers/intl-helpers.jsx';
 import SpriteSelectorItemComponent from '../../../src/components/sprite-selector-item/sprite-selector-item';
-import CostumeCanvas from '../../../src/components/costume-canvas/costume-canvas';
 import CloseButton from '../../../src/components/close-button/close-button';
 
 describe('SpriteSelectorItemComponent', () => {
@@ -73,17 +72,6 @@ describe('SpriteSelectorItemComponent', () => {
         expect(onDeleteButtonClick).toHaveBeenCalled();
     });
 
-    test('creates a CostumeCanvas when a costume url is defined', () => {
-        const wrapper = shallowWithIntl(getComponent());
-        expect(wrapper.find(CostumeCanvas).exists()).toBe(true);
-    });
-
-    test('does not create a CostumeCanvas when a costume url is null', () => {
-        costumeURL = null;
-        const wrapper = shallowWithIntl(getComponent());
-        expect(wrapper.find(CostumeCanvas).exists()).toBe(false);
-    });
-
     test('it has a context menu with delete menu item and callback', () => {
         const wrapper = mountWithIntl(getComponent());
         const contextMenu = wrapper.find('ContextMenu');
-- 
GitLab