UNPKG

quiz-interactions

Version:

A React UI component Library for quiz interaction types.

393 lines (325 loc) 13 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf3 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _assign = _interopRequireDefault(require("lodash/fp/assign")); var _find = _interopRequireDefault(require("lodash/fp/find")); var _map = _interopRequireDefault(require("lodash/fp/map")); var _concat = _interopRequireDefault(require("lodash/fp/concat")); var _sortBy = _interopRequireDefault(require("lodash/fp/sortBy")); var _remove = _interopRequireDefault(require("lodash/fp/remove")); var _uniqBy = _interopRequireDefault(require("lodash/fp/uniqBy")); var _flow = _interopRequireDefault(require("lodash/fp/flow")); var _uiThemeable = require("@instructure/ui-themeable"); var _uiForms = require("@instructure/ui-forms"); var _uiElements = require("@instructure/ui-elements"); var _uiIcons = require("@instructure/ui-icons"); var _quizRce = require("@instructure/quiz-rce"); var _isImage = _interopRequireDefault(require("../common/isImage")); var _FocusGroup = _interopRequireDefault(require("../../common/components/FocusGroup")); var _FilesList = _interopRequireDefault(require("../common/FilesList")); var _theme = _interopRequireDefault(require("./theme")); var _formatMessage = _interopRequireDefault(require("@instructure/quiz-i18n/lib/format-message")); var _dec, _class, _class2, _temp; var styles = { componentId: 'lwHAW', template: function template(theme) { return "\n\n.lwHAW_DxCw{align-items:center;background-color:".concat(theme.fileDropContentBackgroundColor || 'inherit', ";display:flex;justify-content:center;padding:").concat(theme.fileDropContentPadding || 'inherit', " 0}\n\n.lwHAW_eeJQ{display:flex;font-size:").concat(theme.fileDropContentIconFontSize || 'inherit', "}\n\n.lwHAW_cmNy{display:flex;font-family:").concat(theme.fileDropContentFontFamily || 'inherit', ";padding-left:").concat(theme.fileDropContentLabelPadding || 'inherit', "}\n\n.lwHAW_eItU{color:").concat(theme.fileDropContentLabelBrowseColor || 'inherit', ";padding-left:").concat(theme.fileDropContentLabelBrowsePadding || 'inherit', ";text-decoration:underline}"); }, 'fileDropContent': 'lwHAW_DxCw', 'fileDropContentIcon': 'lwHAW_eeJQ', 'fileDropContentLabel': 'lwHAW_cmNy', 'fileDropContentLabelBrowse': 'lwHAW_eItU' }; var FILEDROP_SELECTOR = 'input[type=file]'; var REMOVE_BUTTON_SELECTOR = '[data-role=removeButton] button'; /** --- category: FileUpload --- File Upload Take component ```jsx_example function Example (props) { const exampleProps = { itemBody: 'Give three examples of people', interactionData: { restrictCount: true, filesCount: 3 }, properties: { restrictTypes: false, allowedTypes: '' }, userResponse: { value: [{ id: Date.now(), name: 'slide-3.jpg', url: 'http://instructure.github.io/images/slide-3.jpg', size: 49067 }] } } return ( <FileUploadTake {...exampleProps} {...props} /> ) } <SettingsSwitcher locales={LOCALES}> <TakeStateProvider> <Example /> </TakeStateProvider> </SettingsSwitcher> ``` **/ var _ref3 = /*#__PURE__*/ _react.default.createElement(_uiIcons.IconUploadLine, null); var FileUploadTake = (_dec = (0, _uiThemeable.themeable)(_theme.default, styles), _dec(_class = (_temp = _class2 = /*#__PURE__*/ function (_Component) { (0, _inherits2.default)(FileUploadTake, _Component); function FileUploadTake() { var _getPrototypeOf2; var _this; (0, _classCallCheck2.default)(this, FileUploadTake); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = (0, _possibleConstructorReturn2.default)(this, (_getPrototypeOf2 = (0, _getPrototypeOf3.default)(FileUploadTake)).call.apply(_getPrototypeOf2, [this].concat(args))); _this.state = { files: [], messages: [] }; _this.focusGroup = null; _this.makeLocalLoadHandler = function (id) { return function (e) { var files = (0, _map.default)(function (file) { if (file.id !== id) { return file; } return (0, _assign.default)(file, { url: e.target.result }); }, _this.state.files); _this.setState({ files: files }); }; }; _this.handleDropRejected = function (files) { _this.setState({ messages: [{ text: (0, _formatMessage.default)('Invalid file type'), type: 'error' }] }); }; _this.handleDropAccepted = function (files) { if (_this.props.readOnly) { return; } var uploadFiles; if (_this.props.interactionData.restrictCount) { var responseLength = _this.parseResponse().length; uploadFiles = files.slice(0, _this.parseFilesCount() - responseLength); } else { uploadFiles = files; } var parsedFiles = uploadFiles.map(function (file, index) { var id = Date.now() + index; var callback = _this.makeUploadCallback(id, file); _this.props.mediaUpload(id, file, callback); if ((0, _isImage.default)(file.name)) { var reader = new FileReader(); reader.onload = _this.makeLocalLoadHandler(id); reader.readAsDataURL(file); } return { id: id, url: null, name: file.name, size: file.size, isUploading: true }; }); _this.setState({ files: (0, _concat.default)(_this.state.files, parsedFiles), messages: [] }, function () { // After browsing & selecting 1 or more files, focus on the last one _this.focusGroup.focusLast('div'); }); }; _this.makeRemoveHandler = function (_ref) { var id = _ref.id; return function () { var stateFiles = _this.state.files; var response = _this.parseResponse(); if (stateFiles.length + response.length === 1) { // Focus on FileDrop _this.focusGroup.focusNext(FILEDROP_SELECTOR); } else { // Focus on another remove button if (_this.focusGroup.previousExists(REMOVE_BUTTON_SELECTOR)) { _this.focusGroup.focusPrevious(REMOVE_BUTTON_SELECTOR); } else { _this.focusGroup.focusNext(REMOVE_BUTTON_SELECTOR); } } if ((0, _find.default)({ id: id }, stateFiles)) { _this.props.cancelMediaUpload(id); _this.setState({ files: (0, _remove.default)({ id: id }, stateFiles) }); } else { _this.props.handleResponseUpdate((0, _remove.default)({ id: id }, response)); } }; }; _this.handleShifterRef = function (node) { _this.focusGroup = node; }; return _this; } (0, _createClass2.default)(FileUploadTake, [{ key: "parseFilesCount", value: function parseFilesCount() { return parseInt(this.props.interactionData.filesCount, 10); } }, { key: "parseResponse", value: function parseResponse() { var userResponse = this.props.userResponse; if (!userResponse || !userResponse.value || !userResponse.value.length) { return []; } return userResponse.value.filter(function (f) { return !!f; }); } }, { key: "makeUploadCallback", value: function makeUploadCallback(id, _ref2) { var _this2 = this; var name = _ref2.name, size = _ref2.size; return function (url) { // First add the new file to userResponse and THEN delete from state // So that we never lose track of a file (causes lost focus issues) // This means that for one rerender we'll have a file both in state & userResponse var response = _this2.parseResponse(); _this2.props.handleResponseUpdate((0, _concat.default)(response, { id: id, url: url, name: name, size: size })); _this2.setState({ files: (0, _remove.default)({ id: id }, _this2.state.files) }); }; } }, { key: "renderFileDropContent", value: function renderFileDropContent() { return _react.default.createElement("div", { className: styles.fileDropContent }, _react.default.createElement("div", { className: styles.fileDropContentIcon }, _ref3), _react.default.createElement("div", { className: styles.fileDropContentLabel }, _react.default.createElement(_uiElements.Text, { color: "primary" }, _formatMessage.default.rich("Drag n' Drop here or <0>Browse</0>", [function (_ref4) { var children = _ref4.children; return _react.default.createElement("span", { key: "1", className: styles.fileDropContentLabelBrowse }, children); }])))); } }, { key: "render", value: function render() { var restrictCount = this.props.interactionData.restrictCount; var _this$props = this.props, itemBody = _this$props.itemBody, properties = _this$props.properties, readOnly = _this$props.readOnly; var restrictTypes = properties.restrictTypes, allowedTypes = properties.allowedTypes; var accept = restrictTypes ? allowedTypes : ''; // The uniqBy is needed because in one rerender there's a file in both state & userResponse var allResponses = (0, _flow.default)((0, _concat.default)(this.state.files), (0, _sortBy.default)('id'), (0, _uniqBy.default)('id'))(this.parseResponse()); var hideFileDrop = restrictCount && allResponses.length === this.parseFilesCount(); var allowedTypesDescription = restrictTypes && allowedTypes ? _react.default.createElement(_uiElements.Text, { color: "secondary" }, (0, _formatMessage.default)('{ types } files only!', { types: allowedTypes })) : null; return _react.default.createElement(_FocusGroup.default, { ref: this.handleShifterRef, asComponent: _quizRce.ItemBodyWrapper, asProps: { itemBody: itemBody } }, _react.default.createElement(_FilesList.default, { files: allResponses, makeRemoveHandler: this.makeRemoveHandler, readOnly: this.props.readOnly }), hideFileDrop ? null : _react.default.createElement("div", null, _react.default.createElement(_uiForms.FileDrop, { messages: this.state.messages, accept: accept, onDropAccepted: this.handleDropAccepted, onDropRejected: this.handleDropRejected, allowMultiple: true, readOnly: readOnly, label: this.renderFileDropContent() }), allowedTypesDescription)); } }]); FileUploadTake.displayName = "FileUploadTake"; return FileUploadTake; }(_react.Component), _class2.propTypes = { itemBody: _propTypes.default.string.isRequired, readOnly: _propTypes.default.bool, handleResponseUpdate: _propTypes.default.func.isRequired, mediaUpload: _propTypes.default.func.isRequired, cancelMediaUpload: _propTypes.default.func, interactionData: _propTypes.default.shape({ restrictCount: _propTypes.default.bool.isRequired, filesCount: _propTypes.default.number.isRequired }).isRequired, properties: _propTypes.default.shape({ restrictTypes: _propTypes.default.bool.isRequired, allowedTypes: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.arrayOf(_propTypes.default.string)]) }).isRequired, userResponse: _propTypes.default.shape({ value: _propTypes.default.arrayOf(_propTypes.default.shape({ id: _propTypes.default.number.isRequired, name: _propTypes.default.string.isRequired, url: _propTypes.default.string.isRequired, size: _propTypes.default.number.isRequired })) }).isRequired }, _class2.defaultProps = { cancelMediaUpload: function cancelMediaUpload() {}, readOnly: false }, _temp)) || _class); exports.default = FileUploadTake;