Created
June 12, 2018 08:44
-
-
Save lensraster/6ee5f8606deaf266763a8e72ccc79729 to your computer and use it in GitHub Desktop.
SAWA's logos.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {Component} from 'react' | |
import FileReaderInput from 'react-file-reader-input' | |
import { connect } from "react-redux" | |
import { | |
Modal, | |
ModalHeader, | |
ModalTitle, | |
ModalBody, | |
ModalFooter | |
} from 'react-modal-bootstrap' | |
import { bindActionCreators } from 'redux' | |
import { updateModal, resetModal, saveLogo, deleteLogo, steps_to_props, editLogo } from '../redux/brand_profile_modules/logos' | |
import NextButton from './next_button.inc' | |
const steps_messages = { | |
1: 'Add clear space around your logo. Be mindful of any clear space your logo file may already have.', | |
2: 'Can this logo be applied on white or light backgrounds?', | |
3: 'Does this logo work well on dark image backgrounds?', | |
4: 'Does this logo work well on a background of your primary color?', | |
5: 'Will you allow SAWA to generate a white version of this logo for use automatically?' | |
}; | |
class Logos extends Component { | |
constructor() { | |
super(); | |
this.step_id = 'logos'; | |
this.elements = {}; | |
this.upload_file = null; | |
this.state = {}; | |
} | |
componentDidMount() { | |
const $modal = $('.advanced-options-modal'); | |
const $sidebar = $('#logo-settings'); | |
const getElements = ($parent) => { | |
return { | |
$logo: $parent.find('.logo-preview'), | |
$cs_tool: $parent.find('.clear-space-tool'), | |
$selection: $parent.find('.selection') | |
} | |
}; | |
this.elements = { | |
modal: { | |
...getElements($modal), | |
$toggle: $("#usage-toggle") | |
}, | |
sidebar: getElements($sidebar) | |
}; | |
this.elements.modal.$toggle.candlestick({ | |
afterAction: this.handleToggleAction | |
}); | |
} | |
addLogo = (e, result) => { | |
this.upload_file = result[0][1]; | |
const modal_logo_url = URL.createObjectURL(this.upload_file); | |
this.elements.modal.$logo[0].onload = () => this.initClearSpaceTool('modal'); | |
this.props.updateModal({ modal_open: true, modal_logo_url }); | |
}; | |
changeLogo = (e, result) => { | |
this.upload_file = result[0][1]; | |
const modal_logo_url = URL.createObjectURL(this.upload_file); | |
this.elements.sidebar.$logo[0].onload = () => this.initClearSpaceTool('sidebar'); | |
this.props.updateModal({ modal_logo_url }); | |
}; | |
editLogo = (logo) => { | |
this.elements.sidebar.$logo[0].onload = () => this.initClearSpaceTool('sidebar'); | |
this.props.editLogo(logo); | |
}; | |
getBaseOffset = ({ $cs_tool, $logo }) => { | |
var container_width = $cs_tool.width(); | |
var logo_placeholder_width = $logo.outerWidth(); | |
var max_horizontal_clear_space = (container_width - logo_placeholder_width) / 2; | |
return { | |
top: parseInt($logo.css('margin-top')), | |
left: max_horizontal_clear_space, | |
width: logo_placeholder_width, | |
height: $logo.outerHeight() | |
}; | |
}; | |
initClearSpaceTool = (target) => { | |
if(target === 'modal') { | |
this.unsetLogoMask(); | |
} else if(target === 'sidebar') { | |
this.props.updateModal({ modal_logo_loading: false }); | |
} | |
const elements = this.elements[target]; | |
const { $logo, $selection, $cs_tool } = elements; | |
const base_offset = this.getBaseOffset(elements); | |
$logo[0].onload = undefined; | |
let margin_vert, margin_horiz; | |
if(this.props.modal_margin_string) { | |
let margin_values = JSON.parse(this.props.modal_margin_string); | |
margin_vert = margin_values.vertical; | |
margin_horiz = margin_values.horizontal; | |
} else { | |
margin_vert = margin_horiz = 15; // Defaults | |
} | |
// Convert to percents | |
margin_vert = $logo.outerHeight() * margin_vert / 100; | |
margin_horiz = $logo.outerWidth() * margin_horiz / 100; | |
const margin = { | |
top: margin_vert, | |
right: margin_horiz, | |
bottom: margin_vert, | |
left: margin_horiz | |
}; | |
// Make sure it doesn't exceeds the limit | |
margin.right = Math.min(margin.right, base_offset.left); | |
margin.left = Math.min(margin.left, base_offset.left); | |
$selection.show(); | |
$selection.css({ | |
top: base_offset.top - margin.top, | |
left: base_offset.left - margin.left, | |
width: base_offset.width + margin.left + margin.right, | |
height: base_offset.height + margin.top + margin.bottom | |
}); | |
var prev_right_point = 0; | |
var prev_bottom_point = 0; | |
var resize_directions = null; | |
var resizeOpposite = function(event, ui) { | |
if(!resize_directions) { | |
try { | |
var resize_handler_class_names = event.originalEvent.originalEvent.path[0].className; | |
var class_names = resize_handler_class_names.split(' '); | |
var common_class_index = class_names.indexOf('ui-resizable-handle'); | |
if (common_class_index !== -1) { | |
class_names.splice(common_class_index, 1); // Remove common class not to mess with it further | |
for (var i = 0; i<class_names.length; i++) { | |
if (class_names[i].indexOf('ui-resizable-') !== -1) { | |
resize_directions = class_names[i].replace('ui-resizable-', ''); | |
break; | |
} | |
} | |
} | |
} catch (error) {} | |
} | |
if(resize_directions) { | |
var container_height = $cs_tool.height(); | |
var container_width = $cs_tool.width(); | |
// Resize opposite dimentions | |
for (var i = 0; i<resize_directions.length; i++) { | |
switch (resize_directions[i]) { | |
case 'n': | |
var new_val = container_height - ui.position.top * 2; | |
$selection.css('height', new_val + 'px'); | |
break; | |
case 'w': | |
var new_val = container_width - ui.position.left * 2; | |
$selection.css('width', new_val + 'px'); | |
break; | |
case 's': | |
var new_val = container_height - ui.size.height - ui.position.top; | |
var diff = ui.position.top - new_val; | |
$selection.css({ | |
top: new_val + 'px', | |
height: (ui.size.height + diff) + 'px' | |
}); | |
break; | |
case 'e': | |
var new_val = container_width - ui.size.width - ui.position.left; | |
var diff = ui.position.left - new_val; | |
$selection.css({ | |
left: new_val + 'px', | |
width: (ui.size.width + diff) + 'px' | |
}); | |
break; | |
} | |
} | |
} | |
}; | |
var fixBorders = function(event, ui) { | |
var selection_position = $selection.position(); | |
var right_point = selection_position.left + $selection.width(); | |
var bottom_point = selection_position.top + $selection.height(); | |
var stop_resizing = false; | |
var css = {}; | |
if(selection_position.top > base_offset.top) { | |
$.extend(css, { top: base_offset.top }); | |
stop_resizing = true; | |
} | |
if(selection_position.left > base_offset.left) { | |
$.extend(css, { left: base_offset.left }); | |
stop_resizing = true; | |
} | |
if(right_point < base_offset.left + base_offset.width && right_point < prev_right_point) { | |
$.extend(css, { width: base_offset.left + base_offset.width - selection_position.left }); | |
stop_resizing = true; | |
} | |
if(bottom_point < base_offset.top + base_offset.height && bottom_point < prev_bottom_point) { | |
$.extend(css, { height: base_offset.top + base_offset.height - selection_position.top }); | |
stop_resizing = true; | |
} | |
if(stop_resizing) { | |
$selection.css(css).trigger('mouseup'); | |
$selection.parent().trigger('mousedown'); | |
} | |
prev_right_point = right_point; | |
prev_bottom_point = bottom_point; | |
}; | |
$selection.resizable({ | |
containment: '.clear-space-tool.cst-' + target, | |
handles: 'all', | |
minWidth: $logo.outerWidth(), | |
minHeight: $logo.outerHeight(), | |
resize: function(event, ui) { | |
resizeOpposite(event, ui); | |
fixBorders(event, ui); | |
}, | |
stop: function(event, ui) { | |
resize_directions = null; | |
fixBorders(event, ui); | |
} | |
}); | |
if(target === 'modal') { | |
if(window.Tips) { | |
window.Tips.assignTips('logo-modal-open'); | |
} | |
} | |
}; | |
resetModal = () => { | |
if(!this.props.modal_submitting) { | |
this.props.resetModal(); | |
this.upload_file = null; | |
try { // Try to destroy if already initializede | |
this.elements.modal.$selection.hide().resizable('destroy'); | |
} catch(e) {} | |
} | |
}; | |
unsetLogoMask = () => { | |
this.elements.modal.$logo.css({ | |
width: 'auto', | |
height: 'auto', | |
backgroundColor: 'transparent', | |
WebkitMaskImage: 'none', | |
maskImage: 'none' | |
}).attr('src', this.props.modal_logo_url); | |
}; | |
prevStep = () => { | |
this.goToStep(this.props.modal_step - 1); | |
}; | |
nextStep = () => { | |
this.goToStep(this.props.modal_step + 1); | |
}; | |
goToStep = (modal_step) => { | |
const { $logo, $toggle } = this.elements.modal; | |
if(modal_step !== 1) { | |
let step_answer = this.props.modal_answers[modal_step]; | |
$toggle.data('freeze', true); // This is to prevent updating the state | |
this.updateToggle(step_answer); | |
$toggle.data('freeze', false); | |
} | |
if(modal_step === 5) { | |
$logo.css({ | |
width: $logo.width(), | |
height: $logo.height(), | |
backgroundColor: 'white', | |
WebkitMaskImage: `url(${$logo.attr('src')})`, | |
MaskImage: `url(${$logo.attr('src')})`, | |
WebkitMaskSize: 'contain', | |
MaskSize: 'contain' | |
}).attr('src', '/img/transparent-pixel.png'); | |
} else if(this.props.modal_step === 5) { // Means we're switching back from the fifth step | |
this.unsetLogoMask(); | |
} | |
this.props.updateModal({ modal_step }); | |
}; | |
getLogoBg = (step) => { | |
let bg; | |
switch(step) { | |
case 2: | |
bg = '#F4F4F4'; | |
break; | |
case 3: | |
case 5: | |
bg = 'transparent url(/img/dark_photo_bg.png) 50% 50% no-repeat'; | |
break; | |
case 4: | |
bg = this.props.primary_color; | |
break; | |
} | |
if(bg) { | |
return {background: bg}; | |
} else { | |
return {}; | |
} | |
}; | |
updateToggle(value) { | |
let command; | |
if(value === true) { | |
command = 'on'; | |
} else if(value === false) { | |
command = 'off'; | |
} else { | |
command = 'reset'; | |
} | |
this.elements.modal.$toggle.candlestick(command); | |
} | |
handleToggleAction = (input, wrapper, action) => { | |
if(!input.data('freeze')) { | |
let value = (action === 'on'); | |
if(this.props.modal_answers[this.props.modal_step] !== value) { | |
let modal_answers = this.props.modal_answers.slice(); | |
modal_answers[this.props.modal_step] = value; | |
this.props.updateModal({modal_answers}); | |
} | |
} | |
}; | |
saveLogo = (target) => { | |
const base_offset = this.getBaseOffset(this.elements[target]); | |
const { $selection, $logo } = this.elements[target]; | |
const selection_position = $selection.position(); | |
const logo_width = $logo.outerWidth(); | |
const logo_height = $logo.outerHeight(); | |
var margin = { | |
vertical: ((base_offset.top - selection_position.top) * 100 / logo_height).toFixed(1), | |
horizontal: ((base_offset.left - selection_position.left) * 100 / logo_width).toFixed(1) | |
}; | |
let file = this.upload_file; | |
this.props.saveLogo(file, margin, this.resetModal); | |
}; | |
updateStepAnswer = (step, e) => { | |
const value = $(e.target).prop('checked'); | |
if(this.props.modal_answers[step] !== value) { | |
let modal_answers = this.props.modal_answers.slice(); | |
modal_answers[step] = value; | |
this.props.updateModal({modal_answers}); | |
} | |
}; | |
render() { | |
return ( | |
<div className="screen logos" style={{ display: this.props.current_step === this.step_id ? 'block' : 'none' }}> | |
<h2>Logos</h2> | |
<FileReaderInput accept="image/x-png,image/gif,image/jpeg" | |
onChange={ this.addLogo }> | |
<div className="sawa-label">LOGO VERSION</div> | |
<div className="empty-add-block inner"> | |
<i className="material-icons">add</i> | |
<span> | |
<span>Transparent PNG works best</span> | |
<span>File formats: PNG, JPG and GIF</span> | |
</span> | |
</div> | |
</FileReaderInput> | |
{this.props.logos.map((logo, i) => { | |
if(!logo.deleted) { | |
return ( | |
<div className="logo" key={'logo' + logo.id}> | |
<div className="sawa-label">{ logo.is_primary ? 'PRIMARY LOGO' : 'LOGO VERSION' }</div> | |
<div key={'logo' + i} className="inner"> | |
<div className="image" style={{ backgroundImage: `url(${logo.url})` }}></div> | |
<div className="buttons"> | |
{!logo.parent_id ? ( | |
<button className="btn sawa-gray btn-sm settings" onClick={ () => this.editLogo(logo) }> | |
<i className="material-icons">settings</i> | |
<span>Settings</span> | |
</button> | |
) : null} | |
<button className="btn sawa-gray btn-sm delete" onClick={ () => this.props.deleteLogo(logo) }> | |
<i className="material-icons">delete</i> | |
</button> | |
</div> | |
</div> | |
</div> | |
) | |
} | |
})} | |
<div> | |
<div className="help_block"> | |
<i className="material-icons">info_outline</i> | |
If your logo color looks distorted after upload, you may be using a print file by mistake. | |
<a href="http://www.fastprint.co.uk/blog/cmyk-vs-rgb-printing-what-is-the-difference-when-designing.html">Learn more.</a> | |
</div> | |
</div> | |
<Modal isOpen={ this.props.modal_open } onRequestHide={ this.resetModal } className="sawa-modal advanced-options-modal"> | |
<ModalHeader> | |
<button type="button" className="close" aria-label="Close" onClick={ this.resetModal }> | |
<i className="material-icons">clear</i> | |
</button> | |
<ModalTitle> | |
<span>LOGO SETTINGS</span> | |
<span>{ this.props.modal_step }/5</span> | |
</ModalTitle> | |
</ModalHeader> | |
<ModalBody> | |
<div className="sawa-small caps"> | |
{ this.props.modal_step === 1 ? 'Set clear space' : null } | |
{ this.props.modal_step === 2 ? 'Logo on light background' : null } | |
{ this.props.modal_step === 3 ? 'Logo on dark image background' : null } | |
{ this.props.modal_step === 4 ? 'Logo on primary color background' : null } | |
{ this.props.modal_step === 5 ? 'Automatic white logo generation' : null } | |
</div> | |
<div style={ this.getLogoBg(this.props.modal_step) } className={ 'clear-space-tool cst-modal step' + this.props.modal_step }> | |
<div className="selection"></div> | |
<img className="logo-preview" alt="" src={ this.props.modal_logo_url } /> | |
</div> | |
{this.props.modal_step === 1 ? ( | |
<div className="clear_space_variants"></div> | |
) : null} | |
<div className={ "message step" + this.props.modal_step }> | |
<div> | |
{ steps_messages[this.props.modal_step] } | |
</div> | |
<div></div> | |
</div> | |
</ModalBody> | |
<ModalFooter> | |
<div className="inner"> | |
<div> | |
{this.props.modal_step > 1 ? ( | |
<button className='btn btn-default' onClick={ this.prevStep }> | |
Prev | |
</button> | |
) : null} | |
</div> | |
<div> | |
<div className={ this.props.modal_step === 1 ? 'lefted_away' : null }> | |
<div> | |
<span onClick={() => this.updateToggle(false)}>No</span> | |
</div> | |
<div> | |
<input type="checkbox" name="test" value="" id="usage-toggle" /> | |
</div> | |
<div> | |
<span onClick={() => this.updateToggle(true)}>Yes</span> | |
</div> | |
</div> | |
</div> | |
<div> | |
{this.props.modal_step < 5 ? ( | |
<button className='btn btn-default' onClick={ this.nextStep } disabled={ this.props.modal_step !== 1 && this.props.modal_answers[this.props.modal_step] === undefined }> | |
Next | |
</button> | |
) : ( | |
<button className='btn btn-primary' onClick={ () => this.saveLogo('modal') } disabled={ this.props.modal_answers[this.props.modal_step] === undefined || this.props.modal_submitting }> | |
{this.props.modal_submitting ? ( | |
<i className="fa fa-spin fa-spinner"></i> | |
) : null} | |
Done | |
</button> | |
)} | |
</div> | |
</div> | |
</ModalFooter> | |
</Modal> | |
<div id="logo-settings" style={{ display: this.props.sidebar_open ? 'block' : 'none' }}> | |
<div className="overlay"> | |
<div className="sidebar"> | |
<div className="header"> | |
<h4 className="modal-title">LOGO SETTINGS</h4> | |
<button onClick={ this.resetModal }> | |
<i className="material-icons">clear</i> | |
</button> | |
</div> | |
<div className="scrollable"> | |
<div className="controls"> | |
<div className={ 'sawa-checkbox inverse' + (this.props.modal_logo_is_primary ? ' checked' : '') }> | |
<input type="checkbox" id="sidebar-logo-primary" checked={ this.props.modal_logo_is_primary } | |
onChange={ (e) => this.props.updateModal({ modal_logo_is_primary: $(e.target).prop('checked') }) } /> | |
<label htmlFor="sidebar-logo-primary"> | |
<div><i className="material-icons">done</i></div> Primary Logo | |
</label> | |
</div> | |
<FileReaderInput accept="image/x-png,image/gif,image/jpeg" | |
onChange={ this.changeLogo }> | |
<button className="btn sawa-gray btn-sm"> | |
<i className="material-icons">file_upload</i> Change Logo | |
</button> | |
</FileReaderInput> | |
</div> | |
<div className="sawa-small caps">Set Clear Space</div> | |
<div className={'clear-space-tool cst-sidebar' + (this.props.modal_logo_loading ? ' loading' : '') }> | |
<div className="selection"></div> | |
<img className="logo-preview" alt="Logo" src={ this.props.modal_logo_url } /> | |
<i className="fa fa-spin fa-spinner"></i> | |
</div> | |
<div className="clear_space_variants"></div> | |
<div className="appearance"> | |
{[2, 3, 4, 5].map(step => ( | |
<div className={ steps_to_props[step] } key={ steps_to_props[step] }> | |
<div className="logo_preview_container" style={ this.getLogoBg(step) }> | |
<div style={step !== 5 ? { | |
backgroundImage: `url(${ this.props.modal_logo_url })` | |
} : { | |
backgroundColor: 'white', | |
WebkitMaskImage: `url(${ this.props.modal_logo_url })`, | |
MaskImage: `url(${ this.props.modal_logo_url })`, | |
WebkitMaskPosition: '50% 50%', | |
MaskPosition: '50% 50%', | |
WebkitMaskRepeat: 'no-repeat', | |
MaskRepeat: 'no-repeat', | |
WebkitMaskSize: 'contain', | |
MaskSize: 'contain' | |
}} | |
/> | |
</div> | |
<div className="contents"> | |
<span>{ steps_messages[step] }</span> | |
<div> | |
<input type="checkbox" id={ 'toggle_Heading' + step } className="toggle bigger" checked={ this.props.modal_answers[step] } onChange={ (e) => this.updateStepAnswer(step, e) } /> | |
<label htmlFor={ 'toggle_Heading' + step }></label> | |
</div> | |
</div> | |
</div> | |
))} | |
</div> | |
</div> | |
<div className="bottom"> | |
<button className="btn btn-primary" onClick={ () => this.saveLogo('sidebar') }> | |
{ this.props.modal_submitting ? ( | |
<i className="fa fa-spin fa-spinner"></i> | |
) : null } | |
Done | |
</button> | |
<a onClick={ this.resetModal }>Cancel</a> | |
</div> | |
</div> | |
</div> | |
</div> | |
<NextButton step_id={ this.step_id } /> | |
</div> | |
) | |
} | |
} | |
const mapStateToProps = (state, ownProps) => { | |
const primary_color = _.find(state.brand.colors, c => c.type === 'PRI'); | |
return { | |
current_step: state.progress.current_step, | |
primary_color: primary_color ? primary_color.hex_code : 'transparent', | |
logos: state.brand.logos, | |
...state.brand.logo_modal | |
} | |
}; | |
const mapDispatchToProps = (dispatch) => bindActionCreators({ | |
updateModal, | |
resetModal, | |
saveLogo, | |
deleteLogo, | |
editLogo | |
}, dispatch); | |
export default connect(mapStateToProps, mapDispatchToProps)(Logos) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment