UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

370 lines (364 loc) • 15.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 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 _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = 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 _emotion = require("@instructure/emotion"); var _uiText = require("@instructure/ui-text"); var _uiIcons = require("@instructure/ui-icons"); var _uiAlerts = require("@instructure/ui-alerts"); 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 _styles = _interopRequireDefault(require("./styles")); var _theme = _interopRequireDefault(require("./theme")); var _formatMessage = _interopRequireDefault(require("@instructure/quiz-i18n/es/format-message")); var _uiA11yContent = require("@instructure/ui-a11y-content"); var _quizCommon = require("@instructure/quiz-common"); var _dec, _class, _FileUploadTake; /** @jsx jsx */ function _callSuper(_this, derived, args) { function isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { return !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (e) { return false; } } derived = (0, _getPrototypeOf2["default"])(derived); return (0, _possibleConstructorReturn2["default"])(_this, isNativeReflectConstruct() ? Reflect.construct(derived, args || [], (0, _getPrototypeOf2["default"])(_this).constructor) : derived.apply(_this, args)); } 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 FileUploadTake = exports["default"] = (_dec = (0, _quizCommon.withStyleOverrides)(_styles["default"], _theme["default"]), _dec(_class = (_FileUploadTake = /*#__PURE__*/function (_Component) { function FileUploadTake() { var _this2; (0, _classCallCheck2["default"])(this, FileUploadTake); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this2 = _callSuper(this, FileUploadTake, [].concat(args)); (0, _defineProperty2["default"])(_this2, "state", { files: [], messages: [] }); (0, _defineProperty2["default"])(_this2, "focusGroup", null); (0, _defineProperty2["default"])(_this2, "renderDescription", function () { return "".concat(_this2.fileCountA11yDescription(), " ").concat(_this2.renderAllowedTypesDescription() || ''); }); (0, _defineProperty2["default"])(_this2, "fileCountA11yDescription", function () { if (_this2.props.interactionData.restrictCount) { var fileResponseLength = _this2.allResponses().length; if (fileResponseLength === 0) return (0, _formatMessage["default"])('Maximum { fileCount } file(s) allowed', { fileCount: _this2.fileCountNumber() }); var requiredFilesCount = _this2.parseFilesCount(); return (0, _formatMessage["default"])('{ filesSubmitted } of a maximum of { filesRequired } file(s) submitted.', { filesRequired: requiredFilesCount, filesSubmitted: fileResponseLength }); } return (0, _formatMessage["default"])('{ fileCount } file(s) submitted.', { fileCount: _this2.fileCountNumber() }); }); (0, _defineProperty2["default"])(_this2, "fileCountNumber", function () { return _this2.props.interactionData.restrictCount ? _this2.parseFilesCount() - _this2.allResponses().length : _this2.allResponses().length; }); (0, _defineProperty2["default"])(_this2, "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 }); }, _this2.state.files); _this2.setState({ files: files }); }; }); (0, _defineProperty2["default"])(_this2, "handleDropRejected", function (files) { _this2.setState({ messages: [{ text: (0, _formatMessage["default"])('Invalid file type'), type: 'error' }] }); }); (0, _defineProperty2["default"])(_this2, "handleDropAccepted", function (files) { if (_this2.props.readOnly) { return; } var uploadFiles; if (_this2.props.interactionData.restrictCount) { var responseLength = _this2.parseResponse().length; uploadFiles = files.slice(0, _this2.parseFilesCount() - responseLength); } else { uploadFiles = files; } var parsedFiles = uploadFiles.map(function (file, index) { var id = Date.now() + index; var callback = _this2.makeUploadCallback(id, file); _this2.props.mediaUpload(id, file, callback); if ((0, _isImage["default"])(file.name)) { var reader = new FileReader(); reader.onload = _this2.makeLocalLoadHandler(id); reader.readAsDataURL(file); } return { id: id, url: null, name: file.name, size: file.size, isUploading: true }; }); _this2.setState({ files: (0, _concat["default"])(_this2.state.files, parsedFiles), messages: [] }, function () { // After browsing & selecting 1 or more files, focus on the last one _this2.focusGroup.focusLast('div'); }); }); (0, _defineProperty2["default"])(_this2, "makeRemoveHandler", function (_ref) { var id = _ref.id; return function () { var stateFiles = _this2.state.files; var response = _this2.parseResponse(); if (stateFiles.length + response.length === 1) { // Focus on FileDrop _this2.focusGroup.focusNext(FILEDROP_SELECTOR); } else { // Focus on another remove button if (_this2.focusGroup.previousExists(REMOVE_BUTTON_SELECTOR)) { _this2.focusGroup.focusPrevious(REMOVE_BUTTON_SELECTOR); } else { _this2.focusGroup.focusNext(REMOVE_BUTTON_SELECTOR); } } if ((0, _find["default"])({ id: id }, stateFiles)) { _this2.props.cancelMediaUpload(id); _this2.setState({ files: (0, _remove["default"])({ id: id }, stateFiles) }); } else { _this2.props.handleResponseUpdate((0, _remove["default"])({ id: id }, response)); } }; }); (0, _defineProperty2["default"])(_this2, "handleShifterRef", function (node) { _this2.focusGroup = node; }); (0, _defineProperty2["default"])(_this2, "allResponses", function () { return (0, _flow["default"])((0, _concat["default"])(_this2.state.files), (0, _sortBy["default"])('id'), (0, _uniqBy["default"])('id'))(_this2.parseResponse()); }); return _this2; } (0, _inherits2["default"])(FileUploadTake, _Component); return (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 _this3 = 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 = _this3.parseResponse(); _this3.props.handleResponseUpdate((0, _concat["default"])(response, { id: id, url: url, name: name, size: size })); _this3.setState({ files: (0, _remove["default"])({ id: id }, _this3.state.files) }); }; } }, { key: "renderFileDropContent", value: function renderFileDropContent() { var _this4 = this; return (0, _emotion.jsx)("div", { css: this.props.styles.fileDropContent }, (0, _emotion.jsx)("div", { css: this.props.styles.fileDropContentIcon }, (0, _emotion.jsx)(_uiIcons.IconUploadLine, null)), (0, _emotion.jsx)("div", { css: this.props.styles.fileDropContentLabel }, (0, _emotion.jsx)(_uiText.Text, { color: "primary" }, _formatMessage["default"].rich("Drag n' Drop here or <0>Browse</0>", [function (_ref3) { var children = _ref3.children; return (0, _emotion.jsx)("span", { key: "1", css: _this4.props.styles.fileDropContentLabelBrowse }, children); }])))); } }, { key: "renderAllowedTypesDescription", value: function renderAllowedTypesDescription() { var _this$props$propertie = this.props.properties, restrictTypes = _this$props$propertie.restrictTypes, allowedTypes = _this$props$propertie.allowedTypes; return restrictTypes && allowedTypes ? (0, _formatMessage["default"])('{ types } only!', { types: allowedTypes }) : null; } }, { 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 hideFileDrop = restrictCount && this.allResponses().length === this.parseFilesCount(); return (0, _emotion.jsx)("div", null, (0, _emotion.jsx)("div", { css: this.props.styles.printOnly }, (0, _emotion.jsx)(_uiAlerts.Alert, { variant: "warning" }, (0, _formatMessage["default"])('This question type cannot be printed'))), (0, _emotion.jsx)(_FocusGroup["default"], { ref: this.handleShifterRef, asComponent: _quizRce.ItemBodyWrapper, asProps: { itemBody: itemBody } }, (0, _emotion.jsx)(_FilesList["default"], { files: this.allResponses(), makeRemoveHandler: this.makeRemoveHandler, readOnly: this.props.readOnly }), hideFileDrop ? null : (0, _emotion.jsx)("div", null, (0, _emotion.jsx)("div", null, (0, _emotion.jsx)(_quizCommon.FileDrop, { messages: this.state.messages, accept: accept, onDropAccepted: this.handleDropAccepted, onDropRejected: this.handleDropRejected, shouldAllowMultiple: true, readOnly: readOnly, renderLabel: this.renderFileDropContent() })), restrictCount ? (0, _emotion.jsx)("div", null, (0, _emotion.jsx)(_uiAlerts.Alert, { variant: "info", margin: "x-small 0", hasShadow: false }, this.renderDescription())) : null)), (0, _emotion.jsx)(_uiA11yContent.ScreenReaderContent, null, this.renderDescription())); } }]); }(_react.Component), (0, _defineProperty2["default"])(_FileUploadTake, "displayName", 'FileUploadTake'), (0, _defineProperty2["default"])(_FileUploadTake, "componentId", "Quizzes".concat(_FileUploadTake.displayName)), (0, _defineProperty2["default"])(_FileUploadTake, "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"].string.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, styles: _propTypes["default"].object }), (0, _defineProperty2["default"])(_FileUploadTake, "defaultProps", { cancelMediaUpload: function cancelMediaUpload() {}, readOnly: false }), _FileUploadTake)) || _class);