Make images in source overlay as gallery

This commit is contained in:
Franc Camps-Febrer
2019-01-16 13:06:28 -05:00
parent 21f7cab098
commit 92a5c08b22
2 changed files with 123 additions and 58 deletions

View File

@@ -6,27 +6,36 @@ import Spinner from './presentational/Spinner'
import NoSource from './presentational/NoSource'
// TODO: move render functions into presentational components
function SourceOverlay ({ source, onCancel }) {
function renderError() {
class SourceOverlay extends React.Component {
constructor() {
super()
this.state = {
idx: 0
}
}
renderError() {
return (
<NoSource failedUrls={["NOT ALL SOURCES AVAILABLE IN APPLICATION YET"]} />
)
}
function renderImage(path) {
renderImage(path) {
return (
<div className='source-image-container'>
<Img
className='source-image'
src={path}
loader={<div style={{ width: '400px', height: '400px' }}><Spinner /></div>}
unloader={<NoSource failedUrls={source.paths} />}
unloader={<NoSource failedUrls={this.props.source.paths} />}
/>
</div>
)
}
function renderVideo(path) {
renderVideo(path) {
// NB: assume only one video
return (
<div className="media-player">
@@ -39,26 +48,26 @@ function SourceOverlay ({ source, onCancel }) {
)
}
function renderText(path) {
renderText(path) {
return (
<div className='source-text-container'>
<Md
path={path}
loader={<Spinner />}
unloader={renderError()}
unloader={() => this.renderError()}
/>
</div>
)
}
function renderNoSupport(ext) {
renderNoSupport(ext) {
return (
<NoSource failedUrls={[`Application does not support extension: ${ext}`]} />
)
}
function toMedia(path) {
toMedia(path) {
let type;
switch (true) {
case /\.(png|jpg)$/.test(path):
@@ -73,7 +82,7 @@ function SourceOverlay ({ source, onCancel }) {
return { type, path }
}
function getTypeCounts(media) {
getTypeCounts(media) {
let counts = { Image: 0, Video: 0, Text: 0 }
media.forEach(m => {
counts[m.type] += 1
@@ -81,21 +90,21 @@ function SourceOverlay ({ source, onCancel }) {
return counts
}
function _renderPath(media) {
_renderPath(media) {
const { path, type } = media
switch (type) {
case 'Image':
return renderImage(path)
return this.renderImage(path)
case 'Video':
return renderVideo(path)
return this.renderVideo(path)
case 'Text':
return renderText(path)
return this.renderText(path)
default:
return renderNoSupport(path.split('.')[1])
return this.renderNoSupport(path.split('.')[1])
}
}
function _renderCounts(counts) {
_renderCounts(counts) {
const strFor = type =>
counts[type] > 0 ?
`${counts[type]} ${type.toLowerCase()}${counts[type] > 1 ? 's': ''}`
@@ -113,52 +122,72 @@ function SourceOverlay ({ source, onCancel }) {
)
}
function _renderContent(media) {
_renderContent(media) {
const el = document.querySelector(`.source-media-gallery`);
const shiftW = (!!el) ? el.getBoundingClientRect().width : 0;
return (
<React.Fragment>
{media.map(_renderPath)}
</React.Fragment>
<div className="source-media-gallery" style={{ transition: 'transform 0.2s ease', transform: `translate(${this.state.idx * -shiftW}px)`}}>
{media.map((m) => this._renderPath(m))}
</div>
)
}
if (typeof(source) !== 'object') {
return renderError()
onShiftGallery(shift) {
if (this.state.idx === 0 && shift === -1) return;
if (this.state.idx - 1 === this.props.source.paths.length && shift === 1) return
this.setState({ idx: this.state.idx+shift });
}
const {id, url, title, paths, date, type, desc} = source
const media = paths.map(toMedia)
const counts = getTypeCounts(media)
_renderControls() {
return (
<React.Fragment>
<div className="back" onClick={() => this.onShiftGallery(-1)}><svg><path d="M0,-7.847549217020565L6.796176979388489,3.9237746085102825L-6.796176979388489,3.9237746085102825Z"></path></svg></div>
<div className="next" onClick={() => this.onShiftGallery(1)}><svg><path d="M0,-7.847549217020565L6.796176979388489,3.9237746085102825L-6.796176979388489,3.9237746085102825Z"></path></svg></div>
</React.Fragment>
);
}
return (
<div className="mo-overlay" onClick={onCancel}>
<div className="mo-container" onClick={(e) => { e.stopPropagation(); }}>
<div className="mo-header">
<div className="mo-header-close" onClick={onCancel}>
<button className="side-menu-burg is-active"><span></span></button>
render () {
if (typeof(this.props.source) !== 'object') {
return this.renderError()
}
const {id, url, title, paths, date, type, desc} = this.props.source
const media = paths.map(this.toMedia)
const counts = this.getTypeCounts(media)
return (
<div className="mo-overlay" onClick={this.props.onCancel}>
<div className="mo-container" onClick={(e) => { e.stopPropagation(); }}>
<div className="mo-header">
<div className="mo-header-close" onClick={this.props.onCancel}>
<button className="side-menu-burg is-active"><span></span></button>
</div>
<div className="mo-header-text">{this.props.source.title}</div>
</div>
<div className="mo-header-text">{source.title}</div>
</div>
<div className="mo-media-container">
{_renderContent(media)}
</div>
<div className="mo-meta-container">
<div className="mo-box">
{title? <p><b>{title}</b></p> : null}
<div>{_renderCounts(counts)}</div>
<hr />
{type ? <h4>Media type</h4> : null}
{type ? <p><i className="material-icons left">perm_media</i>{type}</p> : null}
{date ? <h4>Date</h4> : null}
{date ? <p><i className="material-icons left">today</i>{date}</p>: null}
{url ? <h4>Link</h4> : null}
{url ? <span><i className="material-icons left">link</i><a href={url} target="_blank">Link to original URL</a></span> : null}
{desc ? <hr /> : null}
{desc ? <div>{desc}</div> : null}
<div className="mo-media-container">
{(media.length > 1) ? this._renderControls() : ''}
{this._renderContent(media)}
</div>
<div className="mo-meta-container">
<div className="mo-box">
<p style={{ textAlign: 'center'}}>{`${this.state.idx+1} / ${paths.length}`}</p>
{title? <p><b>{title}</b></p> : null}
<div>{this._renderCounts(counts)}</div>
{type ? <h4>Media type</h4> : null}
{type ? <p><i className="material-icons left">perm_media</i>{type}</p> : null}
{date ? <h4>Date</h4> : null}
{date ? <p><i className="material-icons left">today</i>{date}</p>: null}
{url ? <h4>Link</h4> : null}
{url ? <span><i className="material-icons left">link</i><a href={url} target="_blank">Link to original URL</a></span> : null}
{desc ? <hr /> : null}
{desc ? <div>{desc}</div> : null}
</div>
</div>
</div>
</div>
</div>
)
)
}
}
export default SourceOverlay

View File

@@ -65,6 +65,19 @@ $header-inset: 10px;
padding-right: $padding;
font-family: "Lato", Helvetica, sans-serif;
}
.back, .next {
position: absolute;
width: 30px;
height: 30px;
background: $darkgrey;
color: $offwhite;
cursor: pointer;
box-shadow: 0 19px 19px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.22);
svg path { fill: $offwhite; }
}
.back { left: 10px; svg path { transform: translate(17px,15px)rotate(-90deg)} }
.next { right: 10px; svg path { transform: translate(17px,15px)rotate(90deg)} }
}
.mo-header {
@@ -95,11 +108,16 @@ $header-inset: 10px;
.mo-media-container {
background-color: rgba(239, 239, 239, 0.9);
flex: 1;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
overflow-x: hidden;
box-sizing: border-box;
min-width: 100%;
width: 100%;
max-height: 60vh;
padding: 20px;
overflow-y: auto;
font-family: "Lato", Helvetica, sans-serif;
.media-player {
@@ -223,16 +241,34 @@ $header-inset: 10px;
}
}
.source-image-container, .source-text-container {
padding: $padding;
display: inline-block;
.source-media-gallery {
display: flex;
flex-direction: row;
height: 100%;
transition: transform 0.6s ease 0s;
}
.source-text-container {
padding: 0 10em;
display: flex;
justify-content: center;
align-items: center;
}
.source-image-container, .media-player {
display: flex;
justify-content: center;
width: calc(100% - 20px);
height: 100%;
min-width: calc(100% - 20px);
margin: 0 10px;
background: $lightwhite;
border-radius: 2px;
}
.source-image, .source-video {
max-width: calc(100% - 20px);
max-height: 350px !important;
// height: 100%;
max-height: calc(100% - 20px);
padding: 10px;
}