From cc78bf1200fddc9d27d015e48e1d29de4367bac5 Mon Sep 17 00:00:00 2001
From: Dominic Masters <dominic@domsplace.com>
Date: Wed, 24 Oct 2018 17:58:05 +1100
Subject: [PATCH] Cleaned (and untested) image loading.

---
 public/objects/image/Image.jsx                | 197 +++++++++---------
 public/objects/image/LoadableImage.jsx        |  43 ++--
 .../image/LoadableImage.scss}                 |   4 +-
 3 files changed, 129 insertions(+), 115 deletions(-)
 rename public/{styles/objects/_loadable-image.scss => objects/image/LoadableImage.scss} (96%)

diff --git a/public/objects/image/Image.jsx b/public/objects/image/Image.jsx
index 8f9aed5..57c1afb 100644
--- a/public/objects/image/Image.jsx
+++ b/public/objects/image/Image.jsx
@@ -24,120 +24,119 @@
 import React from 'react';
 import LoadableImage from './LoadableImage';
 
-export default class Image extends React.Component {
-  constructor(props) {
-    super(props);
-  }
+export default (props) => {
+  let newProps = {...props};
 
-  onLoad(e) {
-    if(this.props.onLoad) this.props.onLoad(e);
-  }
+  let {
+    loadable, image, src, alt, width, height, sources, onLoad, onError,
+    maxWidth, maxHeight, images
+  } = props;
 
-  onError() {
-    if(this.props.onError) this.props.onErorr(e);
-  }
 
-  render() {
-    if(this.props.loadable) {
-      return <LoadableImage {...this.props} />;
+  [
+    "loadable", "image", "src", "alt", "width", "height", "sources", "onLoad",
+    "onError", "maxWidth", "maxHeight", "images"
+  ].forEach(e => delete newProps[e]);
+
+  width = parseInt(width);
+  maxWidth = parseInt(maxWidth);
+  height = parseInt(height);
+  maxHeight = parseInt(maxHeight);
+
+  if(loadable) return <LoadableImage {...props} />;
+
+  //Prop Manipulation
+  if(image) {
+    if(Array.isArray(image)) {
+      sources = sources || image;
+    } else {
+      src = src || image;
     }
+  }
 
-    let sourceProps = Object.assign({}, this.props);
+  if(src) {
+    if(src.images) sources = sources || src.images;
+    if(src.width) width = width || src.width;
+    if(src.height) height = height || src.height;
+  }
 
-    //Prop Manipulation
-    if(sourceProps.image) {
-      if(Array.isArray(sourceProps.image)) {
-        sourceProps.sources = sourceProps.image;
-      } else {
-        sourceProps.src = sourceProps.image;
+  //Image
+  sources = sources || {};
+  let sourceElements = [];
+
+  let defaultSrc = src;
+  let defaultAlt = alt;
+  let defaultWidth = width;
+  let defaultHeight = height;
+
+  if(sources) {
+    //Iterate over supplied sources
+    for(let i = 0; i < sources.length; i++) {
+      let source = sources[i];
+      let width = source.size || source.width;
+      let isLast = (i+1) === sources.length;
+
+      for(let scale = 1; scale <= 4; scale++) {//4 = max scales
+        let scaledWidth = Math.round(width / scale);
+        let o = {...source};
+        o.scale = scale;
+        o.isLast = isLast;
+        sources[scaledWidth] = sources[scaledWidth] || [];
+        sources[scaledWidth].push(o);
       }
     }
 
-    if(sourceProps.src) {
-      if(sourceProps.src.images) sourceProps.sources = sourceProps.src.images;
-      if(sourceProps.src.width) sourceProps.width = sourceProps.src.width;
-      if(sourceProps.src.height) sourceProps.height = sourceProps.src.height;
-    }
+    //Sort by size in descending order
+    let keys = Object.keys(sources);
+    keys.sort((l, r) => {
+      return parseInt(l) - parseInt(r);
+    });
 
-    //Image
-    let sourceElements = [];
-    let sources = {};
+    let breakNext = false;
+    for(let i = 0; i < keys.length; i++) {
+      if(breakNext) break;
+      let k = keys[i];//The pixel size
 
-    let defaultSrc = sourceProps.src;
-    let defaultAlt = sourceProps.alt;
-    let defaultWidth = sourceProps.width;
-    let defaultHeight = sourceProps.height;
+      let ss = sources[k];//Sources at this pixel resolution
+      let mediaQuery = `(max-width:${k}px)`;
+      let sss = [];
 
-    if(sourceProps.sources) {
-      //Iterate over supplied sources
-      for(let i = 0; i < sourceProps.sources.length; i++) {
-        let x = sourceProps.sources[i];
-        let width = x.size || x.width;
-        let isLast = (i+1) === sourceProps.sources.length;
-
-        for(let scale = 1; scale <= 4; scale++) {
-          let scaledWidth = Math.round(width / scale);
-          let o = Object.assign({}, x);
-          o.scale = scale;
-          o.isLast = isLast;
-          sources[scaledWidth] = sources[scaledWidth] || [];
-          sources[scaledWidth].push(o);
-        }
+      let isNextBreak = false;
+      if(maxWidth && (i+1 < keys.length)) {
+        if(keys[i+1] > parseInt(maxWidth)) isNextBreak = true;
+      }
+      if(isNextBreak) {
+        breakNext = true;
+        mediaQuery = `(min-width:${k}px)`;
       }
 
-      let keys = Object.keys(sources);
-      keys.sort((l, r) => {
-        return parseInt(l) - parseInt(r);
-      });
-      let breakNext = false;
-      for(let i = 0; i < keys.length; i++) {
-        if(breakNext) break;
-        let k = keys[i];//The pixel size
-
-        let ss = sources[k];//Sources at this pixel resolution
-        let mediaQuery = '(max-width:'+k+'px)';
-        let sss = [];
-
-        let isNextBreak = false;
-        if(this.props.maxWidth && (i+1 < keys.length)) {
-          if(keys[i+1] > parseInt(this.props.maxWidth)) isNextBreak = true;
-        }
-        if(isNextBreak) {
-          breakNext = true;
-          mediaQuery = '(min-width:'+k+'px)';
-        }
-
-        if(ss.length && ss[0].isLast) {
-          let prev = i > 0 ? keys[i-1] : 0;
-          mediaQuery = '(min-width:'+prev+'px)';
-        }
-
-        for(let x = 0; x < ss.length; x++) {
-          let scale = ss[x];
-          let source = scale.src || scale.path;
-          sss.push( source + (scale.scale && scale.scale!=1 ? " "+scale.scale+"x" : "") );
-        }
-
-        sourceElements.push(
-          <source media={mediaQuery} srcSet={ sss.join(", ") } key={i} />
-        );
+      if(ss.length && ss[0].isLast) {
+        let prev = i > 0 ? keys[i-1] : 0;
+        mediaQuery = `(min-width:${prev}px)`;
       }
-    }
 
-    return (
-      <picture>
-        { sourceElements }
-        <img
-          onLoad={ this.onLoad.bind(this) }
-          onError={ this.onError.bind(this) }
-          src={ defaultSrc }
-          alt={ defaultAlt }
-          className={ sourceProps.className }
-          width={ defaultWidth }
-          height={ defaultHeight }
-          title={sourceProps.title}
-        />
-      </picture>
-    );
+      for(let x = 0; x < ss.length; x++) {
+        let scale = ss[x];
+        let source = scale.src || scale.path;
+        sss.push( source + (scale.scale && scale.scale!=1 ? " "+scale.scale+"x" : "") );
+      }
+
+      sourceElements.push(<source media={mediaQuery} srcSet={sss.join(", ")} key={i} />);
+    }
   }
+
+  return (
+    <picture>
+      { sourceElements }
+      <img
+        {...newProps}
+        onLoad={ onLoad }
+        onError={ onError }
+        src={ defaultSrc }
+        alt={ defaultAlt }
+        width={ defaultWidth }
+        height={ defaultHeight }
+      />
+    </picture>
+  );
 }
diff --git a/public/objects/image/LoadableImage.jsx b/public/objects/image/LoadableImage.jsx
index 81df0c8..4c20bd5 100644
--- a/public/objects/image/LoadableImage.jsx
+++ b/public/objects/image/LoadableImage.jsx
@@ -22,11 +22,14 @@
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 import React from 'react';
+
+import Styles from './LoadableImage.scss';
+
 import Image from './Image';
 import Loader from './../loading/Loader';
 import BoxSizer from './../layout/BoxSizer';
 
-class LoadableImage extends React.Component {
+export default class LoadableImage extends React.Component {
   constructor(props) {
     super(props);
 
@@ -39,37 +42,49 @@ class LoadableImage extends React.Component {
   componentWillUnmount() {}
 
   onLoad() {
+    let { onLoad } = this.props;
+    if(onLoad) onLoad();
+
     this.setState({
       loading: false
     });
   }
 
   onError() {
+    let { onError } = this.props;
+    if(onError) onError();
+
     this.setState({
       loading: false
     });
   }
 
   render() {
-    let p = Object.assign({}, this.props);
-    p.loadable = false;
-    p.onLoad = this.onLoad.bind(this);
-    let image = <Image {...p} />;
+    let newProps = {...this.props};
+    let { loading } = this.state;
+    let { className, width, height } = this.props;
 
-    let loader,imageSizerDuringLoad;
+    let loader,imageSizer;
+    let image = <Image {...newProps} className="o-loadable-image__image" />;
 
-    if(this.state.loading) {
+    let clazz = "o-loadable-image";
+
+    if(loading) {
+      clazz += " is-loading";
       loader = <Loader />;
-      if(p.width && p.height) {
-        imageSizerDuringLoad = <BoxSizer ratioWidth={p.width} ratioHeight={p.height} />
+      if(width && height) {
+        imageSizer = <BoxSizer ratioWidth={width} ratioHeight={height} />
       }
+    } else {
+      clazz += " is-loaded";
     }
 
-    return (
-      <div className={"o-loadable-image " + (this.state.loading ? "is-loading" : "is-loaded")}>
-        { loader }
-        { imageSizerDuringLoad }
+    if(className) clazz += ` ${className}`;
 
+    return (
+      <div className={className}>
+        { loader }
+        { imageSizer }
         <div className="o-loadable-image__image-container">
           { image }
         </div>
@@ -77,5 +92,3 @@ class LoadableImage extends React.Component {
     );
   }
 }
-
-export default LoadableImage;
diff --git a/public/styles/objects/_loadable-image.scss b/public/objects/image/LoadableImage.scss
similarity index 96%
rename from public/styles/objects/_loadable-image.scss
rename to public/objects/image/LoadableImage.scss
index 6db34ab..91359c0 100644
--- a/public/styles/objects/_loadable-image.scss
+++ b/public/objects/image/LoadableImage.scss
@@ -7,6 +7,8 @@
  *  Version:
  *    1.0.0 - 2018/07/11
  */
+@import '~@styles/global';
+
 @include t-keyframes(o-loadable-image--load-flash) {
   from {
     background: transparent;
@@ -31,7 +33,7 @@
 
   &.is-loading {
     position: relative;
-    
+
     @include t-animation-name(o-loadable-image--load-flash);
     @include t-animation-timing-function(linear);
     @include t-animation-duration(2s);