UNPKG

@instructure/quiz-interactions

Version:

A React UI component Library for quiz interaction types.

106 lines (88 loc) 3.49 kB
import sortBy from 'lodash/sortBy' import setFromArray from '../../../../util/setFromArray' export default class EditStemController { constructor(props) { this.props = props } updateProps = props => { this.props = props } textForStemItem = stemItem => { if (stemItem.type !== 'blank') { return stemItem.value } const matchingData = this.props.scoringData.value.find(sd => { return sd.id === stemItem.blankId }) return matchingData.scoringData.blankText } // =========== // ACTIONS // =========== insertTextAtCursor(text, e) { if (window.getSelection) { const sel = window.getSelection() if (sel.getRangeAt && sel.rangeCount) { const range = sel.getRangeAt(0) range.deleteContents() const newNode = document.createTextNode(text) range.insertNode(newNode) // This merges adjacent text nodes, as we just inserted a new one. // Necessary to prevent weird selection errors from happening on some // browsers, since they'll otherwise disagree on where the selection begins. e.target.normalize() const newRange = sel.getRangeAt(0) // We want to extend the selection to a text node rather than an element // to prevent more browser compatibility issues. const newContainer = newRange.startContainer.nodeType === Node.ELEMENT_NODE ? newRange.startContainer.childNodes[0] : newRange.startContainer try { sel.extend(newContainer, newRange.startOffset + text.length) } catch { // Safari errors out inexplicably on above line, so this is a workaround. // All other methods I tried fail on at least one other browser. newRange.setEnd(newContainer, newRange.endOffset + text.length) sel.addRange(newRange) } sel.collapseToEnd() } } else if (document.selection && document.selection.createRange) { document.selection.createRange().text = text } } handlePaste = e => { e.stopPropagation() e.preventDefault() const clipboardData = e.clipboardData || window.clipboardData const pastedData = clipboardData.getData('Text') this.insertTextAtCursor(pastedData, e) } // If a new blank was added, we should focus on it. If a blank was deleted, // we need to focus on the textual stem item that the blank's text was merged // into, and report the correct cursor position to focus on. getFocusData(prevProps) { const prevIds = setFromArray(prevProps.stemItems.map(item => item.id)) const currIds = setFromArray(this.props.stemItems.map(item => item.id)) const newItems = this.props.stemItems.filter(item => !prevIds.has(item.id)) const deletedItems = sortBy( prevProps.stemItems.filter(item => !currIds.has(item.id)), ['position'], ) if (newItems.length === 0 && deletedItems.length === 0) { return null } const newBlank = newItems.find(item => item.type === 'blank') if (newBlank) { return {stemItem: newBlank, cursorPosition: null} } const lastDeletedItem = deletedItems[deletedItems.length - 1] const sortedItems = sortBy(this.props.stemItems, ['position']) const modifiedItem = sortedItems[deletedItems[0].position - 1] return { stemItem: modifiedItem || sortedItems[0], cursorPosition: modifiedItem.value.length - lastDeletedItem.value.length, } } }