UNPKG

ldx-widgets

Version:

widgets

259 lines (218 loc) 7.79 kB
React = require 'react' createClass = require 'create-react-class' PropTypes = require 'prop-types' ProgressBar = React.createFactory(require './progress_bar') AlertModal = React.createFactory(require './alert_modal') utils = require '../utils' {IMAGE_TYPES} = require '../constants/file_types' {div, input, button, ul, li} = React.DOM FileInput = createClass displayName: 'FileInput' propTypes: openAlertModal: PropTypes.func.isRequired wrapperClass: PropTypes.string className: PropTypes.string multiple: PropTypes.bool chooseFileText: PropTypes.string removeFileText: PropTypes.string name: PropTypes.string.isRequired maxSize: PropTypes.number resolution: PropTypes.object disabled: PropTypes.bool uploadProgress: PropTypes.oneOfType [ PropTypes.object PropTypes.string PropTypes.number ] getDefaultProps: -> wrapperClass: 'file-input-wrapper' className: 'file-input' multiple: false chooseFileText: 'Choose file...' removeFileText: 'Remove file' maxSize: 1048576 # Default max size of 1MB disabled: no name: '' openAlertModal: -> ## Keep this commented out. This is how to form the resolution prop: # resolution: # max: # h: null # w: null # min: # h: null # w: null getInitialState: -> fileInputKey: 0 valid: true inputHasFile: false filename: '' render: -> {name, className, wrapperClass, multiple, uploadProgress, chooseFileText, removeFileText, showFileRemove, disabled, hideUpload} = @props {fileInputKey, inputHasFile, filename} = @state multiple = if multiple then 'multiple' else '' # If an object is passed, use that name property value. Otherwise, use the string directly. uploadProgress = uploadProgress[name] if typeof uploadProgress is 'object' wrapperClass += ' is-uploading' if uploadProgress? and uploadProgress isnt '' div { className: wrapperClass }, [ input { key: fileInputKey className: className type: 'file' ref: 'file' name: name multiple: multiple onChange: @handleChange style: display: if inputHasFile then 'none' else 'block' disabled: disabled } div { key: 'overlay' className: "file-overlay #{if uploadProgress? and uploadProgress isnt '' then 'is-uploading' else ''}" }, [ div { key: 'tools' className: 'overlay-tools' }, [ div { key: 'filename' className: 'filename' }, filename button { key: 'add' className: 'add-file' onClick: @handleFileClick }, chooseFileText unless hideUpload and inputHasFile button { key: 'remove' className: 'remove-file' onClick: @handleFileRemove }, removeFileText if inputHasFile or showFileRemove ] ProgressBar { key: 'progress' progress: uploadProgress } if uploadProgress? and uploadProgress isnt '' ] if not disabled ] clear: -> @setState fileInputKey: @state.fileInputKey + 1 inputHasFile: false filename: '' validate: (file, img) -> {fileTypes, maxSize, resolution} = @props {files} = @refs.file status = errors: [] URL = window.URL or window.webkitURL extension = file.name.split('.').pop().toLowerCase() # Check file size if maxSize? and file.size > maxSize then status.errors.push t "Size must be less than __maxSize__", maxSize: utils.bytesToSize(maxSize) # Check file extensions if fileTypes? # Use an array of qualified image types if typeof fileTypes is 'object' types = fileTypes.join(', ') # If set to imagesOnly, only image formats will be accepted else if typeof fileTypes is 'string' and fileTypes is 'imagesOnly' types = IMAGE_TYPES.join(', ') # Check the file type to see if it's allowed if types.length and types.search(extension) is -1 then status.errors.push t "File type must be __fileType__", fileType: types.toUpperCase() # For images, enforce resolution restrictions if resolution? and img? and URL? {min, max} = resolution resErrors = [] if max? if max.h and img.height > max.h then resErrors.push t "Less than __value__ in __measure__", value: "#{max.h}px", measure: 'height' if max.w and img.width > max.w then resErrors.push t "Less than __value__ in __measure__", value: "#{max.w}px", measure: 'width' if min? if min.h and img.height < min.h then resErrors.push t "Greater than __value__ in __measure__", value: "#{min.h}px", measure: 'height' if min.w and img.width < min.w then resErrors.push t "Greater than __value__ in __measure__", value: "#{min.w}px", measure: 'width' if resErrors.length status.errors.push(t 'Resolution requirements') status.errors.push(resErrors) return status showErrorMessage: (errors) -> @stopFilesLoop = true messages = [] # Parse the validation errors for error, index in errors if typeof error is 'object' and error.length for sErr in error messages.push li { key: index className: 'sub-item' }, sErr else messages.push li { key: index className: 'item' }, error if messages.length msg = ul { key: 'list' className: 'error-message-list' }, messages else msg = t "Selected files must match requirements" @props.openAlertModal msg @clear() handleFileRemove: -> # Report back the name of the element @props.onFileRemove?(@getValue(), @clear) handleFileClick: (e) -> e.stopPropagation() utils.synthesizeMouseEvent(@refs.file, 'click') handleChange: (e) -> {uploadProgress, onChange, resolution, name} = @props {files} = @refs.file @stopFilesLoop = false # Check for the URL class so we can use createObjectURL to assign data to images created dynamically # This is used to enforce resolution rules, and is not supported by IE9 URL = window.URL or window.webkitURL # If an upload is in progress, do nothing if uploadProgress[name]? then return e.preventDefault() # Set the filename to the first file @setState filename: "#{files[0].name} (#{utils.bytesToSize(files[0].size, 2)})" for file, index in files extension = file.name.split('.').pop().toLowerCase() isImage = extension in IMAGE_TYPES isLastFile = index is files.length - 1 # If an invalid file is detected, stop scanning if @stopFilesLoop @stopFilesLoop = false @setState filename: '' return # If an image and resolution constraints defined, wait for img onload if resolution? and isImage and URL? img = new Image() img.onload = => v = @validate(file, img) if v.errors.length then @showErrorMessage(v.errors) else if isLastFile and v onChange?(@getValue()) @setState inputHasFile: true img.src = URL.createObjectURL(file) # For all other files, run validation normally else v = @validate(file) if v.errors.length then @showErrorMessage(v.errors) else if isLastFile and v onChange?(@getValue()) @setState inputHasFile: true getValue: -> value = files: @refs.file.files name: @refs.file.name maxSize: @props.maxSize ref: @ return value module.exports = FileInput