Skip to content
This repository has been archived by the owner on Apr 21, 2022. It is now read-only.

Commit

Permalink
Merge pull request #94 from therocketsloth/compthree/image_vis
Browse files Browse the repository at this point in the history
Image Carousel Block
  • Loading branch information
RichardCzechowski authored Nov 19, 2019
2 parents 3dc8054 + ecd2852 commit ed1b0f1
Show file tree
Hide file tree
Showing 6 changed files with 427 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/examples/image_carousel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Comp Three Image Carousel

This image carousel component allows for the display of images. These images can be provided either as image URLs which are publicly accessible or as a Base64 string in the database.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
229 changes: 229 additions & 0 deletions src/examples/image_carousel/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
export const CAROUSELCSS = `
.carousel .control-arrow, .carousel.carousel-slider .control-arrow {
-webkit-transition: all 0.25s ease-in;
-moz-transition: all 0.25s ease-in;
-ms-transition: all 0.25s ease-in;
-o-transition: all 0.25s ease-in;
transition: all 0.25s ease-in;
opacity: 0.4;
filter: alpha(opacity=40);
position: absolute;
z-index: 2;
top: 20px;
background: none;
border: 0;
font-size: 32px;
cursor: pointer; }
.carousel .control-arrow:hover {
opacity: 1;
filter: alpha(opacity=100); }
.carousel .control-arrow:before, .carousel.carousel-slider .control-arrow:before {
margin: 0 5px;
display: inline-block;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
content: ''; }
.carousel .control-disabled.control-arrow {
opacity: 0;
filter: alpha(opacity=0);
cursor: inherit;
display: none; }
.carousel .control-prev.control-arrow {
left: 0; }
.carousel .control-prev.control-arrow:before {
border-right: 8px solid #fff; }
.carousel .control-next.control-arrow {
right: 0; }
.carousel .control-next.control-arrow:before {
border-left: 8px solid #fff; }
.carousel {
position: relative;
width: 100%; }
.carousel * {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box; }
.carousel img {
width: 100%;
display: inline-block;
pointer-events: none; }
.carousel .carousel {
position: relative; }
.carousel .control-arrow {
outline: 0;
border: 0;
background: none;
top: 50%;
margin-top: -13px;
font-size: 18px; }
.carousel .thumbs-wrapper {
margin: 20px;
overflow: hidden; }
.carousel .thumbs {
-webkit-transition: all 0.15s ease-in;
-moz-transition: all 0.15s ease-in;
-ms-transition: all 0.15s ease-in;
-o-transition: all 0.15s ease-in;
transition: all 0.15s ease-in;
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
-ms-transform: translate3d(0, 0, 0);
-o-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
position: relative;
list-style: none;
white-space: nowrap; }
.carousel .thumb {
-webkit-transition: border 0.15s ease-in;
-moz-transition: border 0.15s ease-in;
-ms-transition: border 0.15s ease-in;
-o-transition: border 0.15s ease-in;
transition: border 0.15s ease-in;
display: inline-block;
width: 80px;
margin-right: 6px;
white-space: nowrap;
overflow: hidden;
border: 3px solid #fff;
padding: 2px; }
.carousel .thumb:focus {
border: 3px solid #ccc;
outline: none; }
.carousel .thumb.selected, .carousel .thumb:hover {
border: 3px solid #333; }
.carousel .thumb img {
vertical-align: top; }
.carousel.carousel-slider {
position: relative;
margin: 0;
overflow: hidden; }
.carousel.carousel-slider .control-arrow {
top: 0;
color: #fff;
font-size: 26px;
bottom: 0;
margin-top: 0;
padding: 5px; }
.carousel.carousel-slider .control-arrow:hover {
background: rgba(0, 0, 0, 0.2); }
.carousel .slider-wrapper {
overflow: hidden;
margin: auto;
width: 100%;
-webkit-transition: height 0.15s ease-in;
-moz-transition: height 0.15s ease-in;
-ms-transition: height 0.15s ease-in;
-o-transition: height 0.15s ease-in;
transition: height 0.15s ease-in; }
.carousel .slider-wrapper.axis-horizontal .slider {
-ms-box-orient: horizontal;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -moz-flex;
display: -webkit-flex;
display: flex; }
.carousel .slider-wrapper.axis-horizontal .slider .slide {
flex-direction: column;
flex-flow: column; }
.carousel .slider-wrapper.axis-vertical {
-ms-box-orient: horizontal;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -moz-flex;
display: -webkit-flex;
display: flex; }
.carousel .slider-wrapper.axis-vertical .slider {
-webkit-flex-direction: column;
flex-direction: column; }
.carousel .slider {
margin: 0;
padding: 0;
position: relative;
list-style: none;
width: 100%; }
.carousel .slider.animated {
-webkit-transition: all 0.35s ease-in-out;
-moz-transition: all 0.35s ease-in-out;
-ms-transition: all 0.35s ease-in-out;
-o-transition: all 0.35s ease-in-out;
transition: all 0.35s ease-in-out; }
.carousel .slide {
min-width: 100%;
margin: 0;
position: relative;
text-align: center;
background: #000; }
.carousel .slide img {
width: 100%;
vertical-align: top;
border: 0; }
.carousel .slide iframe {
display: inline-block;
width: calc(100% - 80px);
margin: 0 40px 40px;
border: 0; }
.carousel .slide .legend {
-webkit-transition: all 0.5s ease-in-out;
-moz-transition: all 0.5s ease-in-out;
-ms-transition: all 0.5s ease-in-out;
-o-transition: all 0.5s ease-in-out;
transition: all 0.5s ease-in-out;
position: absolute;
bottom: 40px;
left: 50%;
margin-left: -45%;
width: 90%;
border-radius: 10px;
background: #000;
color: #fff;
padding: 10px;
font-size: 12px;
text-align: center;
opacity: 0.25;
-webkit-transition: opacity 0.35s ease-in-out;
-moz-transition: opacity 0.35s ease-in-out;
-ms-transition: opacity 0.35s ease-in-out;
-o-transition: opacity 0.35s ease-in-out;
transition: opacity 0.35s ease-in-out; }
.carousel .control-dots {
position: absolute;
bottom: 0;
margin: 10px 0;
text-align: center;
width: 100%; }
@media (min-width: 960px) {
.carousel .control-dots {
bottom: 0; } }
.carousel .control-dots .dot {
-webkit-transition: opacity 0.25s ease-in;
-moz-transition: opacity 0.25s ease-in;
-ms-transition: opacity 0.25s ease-in;
-o-transition: opacity 0.25s ease-in;
transition: opacity 0.25s ease-in;
opacity: 0.3;
filter: alpha(opacity=30);
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.9);
background: #fff;
border-radius: 50%;
width: 8px;
height: 8px;
cursor: pointer;
display: inline-block;
margin: 0 8px; }
.carousel .control-dots .dot.selected, .carousel .control-dots .dot:hover {
opacity: 1;
filter: alpha(opacity=100); }
.carousel .carousel-status {
position: absolute;
top: 0;
right: 0;
padding: 5px;
font-size: 10px;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.9);
color: #fff; }
.carousel:hover .slide .legend {
opacity: 1; }
`;
131 changes: 131 additions & 0 deletions src/examples/image_carousel/imageViewer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import React from 'react';
import { Carousel } from 'react-responsive-carousel';

const URLREGEX = new RegExp("((http|https)(:\/\/))?([a-zA-Z0-9]+[.]{1}){2}[a-zA-Z0-9]+(\/{1}[a-zA-Z0-9]+)*\/?", "igm");
// Regular expression to check formal correctness of base64 encoded strings
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/atob
const b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;

const DOTS_THRESHOLD = 15;

const isUrlCheck = (strToCheck) => {
return URLREGEX.test(strToCheck);
}

const isBase64StringCheck = (strToCheck) => {
if (strToCheck && strToCheck.length > 150 && b64re.test(strToCheck)) {
try {
return btoa(atob(strToCheck)) === strToCheck;
} catch (err) {
return false;
}
}
return false;
}

// NOTE: this method loops over column key names without looking at column order
const findImageCol = (stateData) => {
let tmpImageColData = {
type: {
url: false,
base64: false
},
name: null
}

for (let row of stateData) {
for (let colName of Object.keys(row)) {
if (isUrlCheck(row[colName].value)) {
tmpImageColData.type.url = true;
tmpImageColData.type.base64 = false; // set this in case we found b64 before finding a url
tmpImageColData.name = colName;
return tmpImageColData;
} else if (isBase64StringCheck(row[colName].value)) {
tmpImageColData.type.base64 = true;
tmpImageColData.name = colName;
}
}
// stop looping rows if we found a valid image column
if (tmpImageColData.name) {
return tmpImageColData;
}
}
// resets this.state.imageColData to falsy if no valid image col was found
return tmpImageColData;
}

// Create (or import) our react component
export default class ImageViewer extends React.Component {
constructor () {
super();

// Set initial state to a loading or no data message, initialize imageColData
this.state = {
data: null,
queryResponse: null,
imageColData: { // flatten
type: {
url: false,
base64: false
},
name: null
}
};
}

// component mount/recv props, should component update - lifecycle method
// if there is new data, call again to find column ...

// render our data
render() {
if (!this.state.data) {
return (
<div>No Image Data Found</div>
);
}

if (!this.state.imageColData.name) {
this.state.imageColData = findImageCol(this.state.data);
}

// stop if no valid image data column found
if (!this.state.imageColData.name) {
return (
<div>Please select at least one field with an image url or a base64 encoded image.</div>
);
}

// check first row for the image column, if it is not present find the new valid image column
// Rerun the image column check to make sure there is still a valid column, this needs to be refreshed when the
// explore is changed in looker.
if (typeof this.state.data[0][this.state.imageColData.name] === 'undefined') {
this.state.imageColData = findImageCol(this.state.data);
}

let tableRows = this.state.data.map((row, idx) => {
let val = row[this.state.imageColData.name].value; // image url or base64 string

if (this.state.imageColData.type.base64) {
val = `data:image/image;base64,${val}`;
}

return (
<div key={idx}>
<img src={val} />
</div>
);
});

// display image index linked dots in bottom of carousel, dots will stack into additional rows they overrun the carousel width
let showIndicatorsBool = true;
if (tableRows.length > DOTS_THRESHOLD) {
showIndicatorsBool = false;
}

return (
<Carousel dynamicHeight={true} showIndicators={showIndicatorsBool} showThumbs={false} swipeable={false}>
{tableRows}
</Carousel>
);
}
}
Loading

0 comments on commit ed1b0f1

Please sign in to comment.