Skip to content

Instantly share code, notes, and snippets.

@lensraster
Created June 12, 2018 08:44
Show Gist options
  • Save lensraster/6ee5f8606deaf266763a8e72ccc79729 to your computer and use it in GitHub Desktop.
Save lensraster/6ee5f8606deaf266763a8e72ccc79729 to your computer and use it in GitHub Desktop.
SAWA's logos.js
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