UNPKG

ldx-widgets

Version:

widgets

405 lines (371 loc) 13.7 kB
(function() { var ConfirmSave, ESCAPE, KEY_S, Modal, PropTypes, React, Spinner, StyleSheet, assign, button, createClass, css, dialogueMixin, div, enterKeyFrames, header, ref, ref1, ref2, span, styles; React = require('react'); createClass = require('create-react-class'); PropTypes = require('prop-types'); ref = require('aphrodite/no-important'), StyleSheet = ref.StyleSheet, css = ref.css; assign = require('lodash/assign'); dialogueMixin = require('../mixins/dialogue_mixin'); ref1 = require('../constants/keyboard'), ESCAPE = ref1.ESCAPE, KEY_S = ref1.KEY_S; ConfirmSave = React.createFactory(require('./confirm_save')); Spinner = React.createFactory(require('./spinner')); ref2 = require('react-dom-factories'), div = ref2.div, header = ref2.header, span = ref2.span, button = ref2.button; /*& @props.title - OPTIONAL - [String] title for the modal header @props.buttons - OPTIONAL - [Array] Array of button objects with name, handler to be called on click, and disabled boolean, eg... ``` [ { name: 'Save' handler: @save disabled: no } ] ``` @props.children - REQUIRED - [Element] React element (or array of elements) to inserted as the modal body @props.styleOverride - OPTIONAL - [Object] aphrodite style object, optionally can contain .modal, .header, .title, .actionButton class defs, eg... ``` { modalBase: {} modalEnter: {} modal: {} header: {} title: {} actionButton: {} } ``` @props.close - OPTIONAL - [Function] Function that closes the overlay, passed automatically by the overlay framework @props.onClose - OPTIONAL - [Function] Function that is called right before the modal closes @props.showClose - OPTIONAL - [Boolean] Defaults to yes, set it to no to not show the close button @props.closeAfterSave - OPTIONAL - [Boolean] Defaults to yes, set it to no to prevent the modal from calling @props.close after a save is complete Note: in this case you MUST pass an onSaveComplete handler that sets the saveState to null after a save @props.onSaveComplete - OPTIONAL - [Function] Function that is called right after the saveState is set to complete and the success indicator finishes animating @props.closeBtnText - OPTIONAL - [String] Defaults to 'Cancel', text to display in the close button @props.animateIn - OPTIONAL - [Boolean] Defaults to yes, whether or not to bouce the modal on enter @props.loading - OPTIONAL - [Boolean] Defaults to no, whether or not to show the spinner instead of the children @props.draggable - OPTIONAL - [Boolean] Defaults to yes, whether or not the modal can be dragged from it's header @props.stopPropagation - OPTIONAL - [Boolean] Defaults to yes, whether or not the modal stop click from bubbling above it @props.displayProgressBar - OPTIONAL - [Boolean] Defaults to no, whether or not the confirm/save show the progress bar instead of the spinner @props.uploadProgress -OPTIONAL Progress of a file being uploaded & */ Modal = createClass({ displayName: 'Modal', mixins: [dialogueMixin], propTypes: { styleOverride: PropTypes.shape({ modal: PropTypes.object, header: PropTypes.object, title: PropTypes.object, actionButton: PropTypes.object }), title: PropTypes.string, buttons: PropTypes.array, close: PropTypes.func.isRequired, onClose: PropTypes.func, onSaveComplete: PropTypes.func, showClose: PropTypes.bool, closeAfterSave: PropTypes.bool, closeBtnText: PropTypes.string, unSavedMessage: PropTypes.string, unSavedDialogueHeight: PropTypes.number, unSavedChanges: PropTypes.bool, onSaveFail: PropTypes.func, inLineStyle: PropTypes.object, animateIn: PropTypes.bool, loading: PropTypes.bool, draggable: PropTypes.bool, stopPropagation: PropTypes.bool, spinnerProps: PropTypes.object, displayProgressBar: PropTypes.bool, uploadProgress: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) }, getDefaultProps: function() { return { styleOverride: {}, inLineStyle: {}, buttons: [], showClose: true, closeAfterSave: true, unSavedDialogueHeight: 100, saveState: null, saveMessage: null, unSavedChanges: false, animateIn: true, loading: false, draggable: true, stopPropagation: true, spinnerProps: { length: 7, radius: 7, lines: 12, width: 2 }, displayProgressBar: false, uploadProgress: '' }; }, getInitialState: function() { return { dragX: 0, dragY: 0 }; }, componentWillMount: function() { this.closeBtnText = t('Cancel'); return document.addEventListener('keydown', this.handleKeyPress); }, componentDidMount: function() { setTimeout((function(_this) { return function() { return _this.calculateMaxDrags(); }; })(this), 0); return window.addEventListener('resize', this.calculateMaxDrags); }, componentWillUnmount: function() { document.removeEventListener('keydown', this.handleKeyPress); return window.removeEventListener('resize', this.calculateMaxDrags); }, render: function() { var animateIn, animateStyle, b, buttons, children, closeBtnText, displayProgressBar, dragX, dragY, draggable, headerChildren, i, inLineStyle, loading, onSaveFail, ref3, ref4, saveMessage, saveState, showClose, spinnerProps, stopPropagation, styleOverride, title, uploadProgress; ref3 = this.props, styleOverride = ref3.styleOverride, title = ref3.title, buttons = ref3.buttons, closeBtnText = ref3.closeBtnText, showClose = ref3.showClose, children = ref3.children, saveState = ref3.saveState, saveMessage = ref3.saveMessage, onSaveFail = ref3.onSaveFail, inLineStyle = ref3.inLineStyle, animateIn = ref3.animateIn, loading = ref3.loading, spinnerProps = ref3.spinnerProps, draggable = ref3.draggable, stopPropagation = ref3.stopPropagation, displayProgressBar = ref3.displayProgressBar, uploadProgress = ref3.uploadProgress; ref4 = this.state, dragX = ref4.dragX, dragY = ref4.dragY; closeBtnText = closeBtnText || this.closeBtnText; assign(styles, styleOverride); inLineStyle = assign({}, inLineStyle, { transform: "translate(" + dragX + "px, " + dragY + "px) translateZ(0px)", WebkitTransform: "translate(" + dragX + "px, " + dragY + "px) translateZ(0px)", msTransform: "translate(" + dragX + "px, " + dragY + "px)" }); assign(spinnerProps, { key: 'spinner' }); headerChildren = []; if (title != null) { headerChildren.push(span({ key: 'title', className: css(styles.title) }, title)); } if (showClose) { headerChildren.push(button({ key: 'close', className: css(styles.actionButton), onClick: this.closeWithCheck }, closeBtnText)); } for (i = buttons.length - 1; i >= 0; i += -1) { b = buttons[i]; headerChildren.push(button({ key: b.name, className: css(styles.actionButton), onClick: b.handler, disabled: b.disabled }, b.name)); } animateStyle = animateIn ? styles.modalEnter : null; return div({ className: css(styles.modalBase, animateStyle, styles.modal), style: inLineStyle, onClick: stopPropagation ? this.handleClick : null }, [ header({ key: 'header', className: css(styles.header), onMouseDown: draggable ? this.handleMouseDown : null, ref: (function(_this) { return function(header1) { _this.header = header1; }; })(this) }, headerChildren), this.dialogueBox(), saveState != null ? ConfirmSave({ key: 'confirm', done: this.saveComplete, fail: function() { return typeof onSaveFail === "function" ? onSaveFail() : void 0; }, saveMessage: saveMessage, saveState: saveState, displayProgressBar: displayProgressBar, uploadProgress: uploadProgress }) : void 0, loading ? Spinner(spinnerProps) : children ]); }, handleClick: function(e) { return e.stopPropagation(); }, closeWithCheck: function() { var ref3, unSavedChanges, unSavedDialogueHeight, unSavedMessage; ref3 = this.props, unSavedMessage = ref3.unSavedMessage, unSavedDialogueHeight = ref3.unSavedDialogueHeight, unSavedChanges = ref3.unSavedChanges; if (unSavedChanges) { return this.showDialogue({ message: unSavedMessage || t('There are unsaved changes. How do you want to proceed?'), confirmText: t('Discard Changes'), height: unSavedDialogueHeight, confirmCallback: this.close }); } else { return this.close(); } }, close: function() { var close, onClose, ref3; ref3 = this.props, close = ref3.close, onClose = ref3.onClose; if (typeof onClose === "function") { onClose(); } return close(); }, saveComplete: function() { var close, closeAfterSave, onSaveComplete, ref3; ref3 = this.props, close = ref3.close, onSaveComplete = ref3.onSaveComplete, closeAfterSave = ref3.closeAfterSave; if (typeof onSaveComplete === "function") { onSaveComplete(); } if (closeAfterSave) { return close(); } }, handleKeyPress: function(e) { var buttons, keyCode, metaKey, ref3; keyCode = e.keyCode, metaKey = e.metaKey; if (keyCode === ESCAPE) { this.closeWithCheck(); } if (keyCode === KEY_S && metaKey) { e.preventDefault(); buttons = this.props.buttons; if (((ref3 = buttons[0]) != null ? ref3.name : void 0) === t('Save')) { return buttons[0].handler(); } } }, handleMouseDown: function(e) { var dragX, dragY, ref3; e.preventDefault(); ref3 = this.state, dragX = ref3.dragX, dragY = ref3.dragY; this.startDragX = dragX; this.startDragY = dragY; this.startX = e.clientX; this.startY = e.clientY; document.addEventListener('mousemove', this.handleMouseMove); return document.addEventListener('mouseup', this.handleMouseUp); }, handleMouseMove: function(e) { var dragX, dragY; e.preventDefault(); dragX = this.startDragX + (e.clientX - this.startX); dragY = this.startDragY + (e.clientY - this.startY); dragY = dragY < this.minDragY ? this.minDragY : dragY; dragY = dragY > this.maxDragY ? this.maxDragY : dragY; dragX = dragX < this.minDragX ? this.minDragX : dragX; dragX = dragX > this.maxDragX ? this.maxDragX : dragX; return this.setState({ dragX: dragX, dragY: dragY }); }, handleMouseUp: function() { document.removeEventListener('mousemove', this.handleMouseMove); return document.removeEventListener('mouseup', this.handleMouseUp); }, calculateMaxDrags: function() { var innerHeight, innerWidth, offsetHeight, offsetLeft, offsetTop, offsetWidth, ref3, ref4; ref3 = this.header.parentNode, offsetTop = ref3.offsetTop, offsetLeft = ref3.offsetLeft; ref4 = this.header, offsetHeight = ref4.offsetHeight, offsetWidth = ref4.offsetWidth; innerHeight = window.innerHeight, innerWidth = window.innerWidth; this.maxDragX = innerWidth - (offsetLeft + offsetWidth); this.maxDragY = innerHeight - (offsetTop + offsetHeight); this.minDragX = 0 - offsetLeft; return this.minDragY = 0 - offsetTop; } }); enterKeyFrames = { '0%': { transform: 'scale(0.9)', opacity: '0' }, '40%': { opacity: '1', transform: 'scale(1.05)' }, '100%': { transform: 'scale(1)' } }; styles = StyleSheet.create({ modalBase: { position: 'absolute', backgroundColor: 'white', borderRadius: '8px', overflow: 'hidden' }, modalEnter: { animationName: [enterKeyFrames], animationDuration: '.15s', animationIterationCount: '1', animationTimingFunction: 'ease-out' }, modal: { top: '10%', left: '50%', width: '600px', height: '500px', marginLeft: '-300px' }, header: { position: 'relative', width: '100%', height: '44px', lineHeight: '44px', backgroundColor: 'rgb(246,246,246)', borderBottom: '1px solid rgb(190,190,190)', borderTopLeftRadius: '8px', borderTopRightRadius: '8px', margin: '0px', cursor: 'pointer' }, title: { display: 'inline-block', fontSize: '16px', fontWeight: 'normal', textAlign: 'left', color: 'rgb(113,113,113)', overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis', marginLeft: '15px' }, actionButton: { float: 'right', color: 'rgb(0,127,255)', height: '28px', textAlign: 'center', lineHeight: '26px', marginTop: '8px', marginRight: '15px', fontSize: '13px', ':disabled': { color: 'rgb(204,204,204)' } } }); module.exports = Modal; }).call(this);