diff --git a/src/examples/image_carousel/README.md b/src/examples/image_carousel/README.md new file mode 100644 index 00000000..6b0e6c76 --- /dev/null +++ b/src/examples/image_carousel/README.md @@ -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. + diff --git a/src/examples/image_carousel/c3_image_carousel.png b/src/examples/image_carousel/c3_image_carousel.png new file mode 100644 index 00000000..d553f69e Binary files /dev/null and b/src/examples/image_carousel/c3_image_carousel.png differ diff --git a/src/examples/image_carousel/constants.js b/src/examples/image_carousel/constants.js new file mode 100644 index 00000000..f9643082 --- /dev/null +++ b/src/examples/image_carousel/constants.js @@ -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; } +`; \ No newline at end of file diff --git a/src/examples/image_carousel/imageViewer.js b/src/examples/image_carousel/imageViewer.js new file mode 100644 index 00000000..3143fe00 --- /dev/null +++ b/src/examples/image_carousel/imageViewer.js @@ -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 ( +