← All changes
|
assets/src/apps/js/admin/react/question/index.js
+350
-350
4.3.9
→
4.4.8
View file →
| @@ -1,350 +1,350 @@ | ||
| 1 | -import { Component } from '@wordpress/element'; | |
| 2 | -import { withSelect, withDispatch, select as wpSelect } from '@wordpress/data'; | |
| 3 | -import { compose } from '@wordpress/compose'; | |
| 4 | -import { __ } from '@wordpress/i18n'; | |
| 5 | - | |
| 6 | -const { debounce } = lodash; | |
| 7 | - | |
| 8 | -import store from './store'; | |
| 9 | - | |
| 10 | -const stripSlashes = function stripSlashes( str ) { | |
| 11 | - return ( str + '' ).replace( /\\(.?)/g, function( s, n1 ) { | |
| 12 | - switch ( n1 ) { | |
| 13 | - case '\\': | |
| 14 | - return '\\'; | |
| 15 | - case '0': | |
| 16 | - return '\u0000'; | |
| 17 | - case '': | |
| 18 | - return ''; | |
| 19 | - default: | |
| 20 | - return n1; | |
| 21 | - } | |
| 22 | - } ); | |
| 23 | -}; | |
| 24 | - | |
| 25 | -class Editor extends Component { | |
| 26 | - constructor( props ) { | |
| 27 | - super( ...arguments ); | |
| 28 | - | |
| 29 | - this.state = { | |
| 30 | - blanks: [], | |
| 31 | - }; | |
| 32 | - } | |
| 33 | - | |
| 34 | - componentDidMount() { | |
| 35 | - const { | |
| 36 | - setData, | |
| 37 | - initSettings, | |
| 38 | - } = this.props; | |
| 39 | - const { blankOptions } = initSettings; | |
| 40 | - const newBlanks = {}; | |
| 41 | - | |
| 42 | - blankOptions.map( ( option ) => { | |
| 43 | - const optionBlanks = this.getBlanks( option.text ); | |
| 44 | - newBlanks[ option.question_answer_id ] = optionBlanks ? optionBlanks[ 0 ] : []; | |
| 45 | - } ); | |
| 46 | - | |
| 47 | - setData( { | |
| 48 | - question: { | |
| 49 | - ...initSettings, | |
| 50 | - blanks: newBlanks, | |
| 51 | - }, | |
| 52 | - } ); | |
| 53 | - } | |
| 54 | - | |
| 55 | - /** | |
| 56 | - * Parse blanks options from content and update state. | |
| 57 | - * | |
| 58 | - * @param option | |
| 59 | - * @param text | |
| 60 | - */ | |
| 61 | - setContent = ( option, text ) => { | |
| 62 | - const optionBlanks = this.getBlanks( text ); | |
| 63 | - const { | |
| 64 | - blankOptions, | |
| 65 | - blanks, | |
| 66 | - updateOption, | |
| 67 | - setData, | |
| 68 | - id, | |
| 69 | - } = this.props; | |
| 70 | - | |
| 71 | - const newState = { | |
| 72 | - blankOptions: blankOptions.map( ( opt ) => { | |
| 73 | - return opt.question_answer_id == option.question_answer_id ? { | |
| 74 | - ...opt, | |
| 75 | - text, | |
| 76 | - } : opt; | |
| 77 | - } ), | |
| 78 | - blanks: { | |
| 79 | - ...blanks, | |
| 80 | - [ option.question_answer_id ]: optionBlanks ? optionBlanks[ 0 ] : [], | |
| 81 | - }, | |
| 82 | - }; | |
| 83 | - | |
| 84 | - setData( newState, 'question' ); | |
| 85 | - this.queue = this.queue || {}; | |
| 86 | - this.queue[ option.question_answer_id ] = [ { | |
| 87 | - text, | |
| 88 | - blanks: newState.blanks[ option.question_answer_id ], | |
| 89 | - }, option.question_answer_id, id ]; | |
| 90 | - | |
| 91 | - this.updateOption(); | |
| 92 | - }; | |
| 93 | - | |
| 94 | - updateOption = debounce( () => { | |
| 95 | - const { | |
| 96 | - updateOption, | |
| 97 | - } = this.props; | |
| 98 | - | |
| 99 | - const queue = this.queue ? Object.values( this.queue ) : []; | |
| 100 | - | |
| 101 | - queue.map( ( item ) => { | |
| 102 | - if ( item ) { | |
| 103 | - updateOption( item[ 0 ], item[ 1 ], item[ 2 ] ); | |
| 104 | - delete this.queue[ item[ 1 ] ]; | |
| 105 | - } | |
| 106 | - } ); | |
| 107 | - }, 1000 ); | |
| 108 | - | |
| 109 | - /** | |
| 110 | - * Get blanks from a content and return. | |
| 111 | - * | |
| 112 | - * @param content | |
| 113 | - * @return {Array} | |
| 114 | - */ | |
| 115 | - getBlanks = ( content ) => { | |
| 116 | - // if (undefined === content) { | |
| 117 | - // content = this.state.passage; | |
| 118 | - // } | |
| 119 | - | |
| 120 | - const blanks = []; | |
| 121 | - const shortcodes = content.match( /\{\{([^\{\"\'].*?)\}\}/g ); | |
| 122 | - | |
| 123 | - if ( shortcodes ) { | |
| 124 | - shortcodes.map( ( shortcode ) => { | |
| 125 | - blanks.push( this.getBlank( shortcode ) ); | |
| 126 | - } ); | |
| 127 | - } | |
| 128 | - return blanks; | |
| 129 | - }; | |
| 130 | - | |
| 131 | - /** | |
| 132 | - * Parse details from a blank. | |
| 133 | - * | |
| 134 | - * @param blank | |
| 135 | - * @return {{words: Array, tip: string, corrects: Array}} | |
| 136 | - */ | |
| 137 | - getBlank = ( blank ) => { | |
| 138 | - const contents = blank.match( /\{\{(.*)\}\}/ ); | |
| 139 | - const words = contents && contents[ 1 ] ? contents[ 1 ].split( '/' ) : []; | |
| 140 | - const matchTip = words.length ? words[ words.length - 1 ].match( /(.*)(\s(\"(.*)\"|\'(.*)\'))/ ) : false; | |
| 141 | - const corrects = []; | |
| 142 | - | |
| 143 | - // Remove tip from last word. (last-word "This is tip of the blank") | |
| 144 | - if ( matchTip ) { | |
| 145 | - words[ words.length - 1 ] = matchTip[ 1 ]; | |
| 146 | - } | |
| 147 | - | |
| 148 | - words.map( ( word, i ) => { | |
| 149 | - // Match the word wrapped by [ and ] is CORRECT word | |
| 150 | - const matchCorrect = word.match( /\[(.*)\]/ ); | |
| 151 | - | |
| 152 | - if ( matchCorrect ) { | |
| 153 | - // Remove the "[" and "]" | |
| 154 | - matchCorrect[ 1 ] = stripSlashes( matchCorrect[ 1 ] ); | |
| 155 | - corrects.push( matchCorrect[ 1 ] ); | |
| 156 | - words[ i ] = matchCorrect[ 1 ]; | |
| 157 | - } else { | |
| 158 | - words[ i ] = stripSlashes( word ); | |
| 159 | - } | |
| 160 | - } ); | |
| 161 | - | |
| 162 | - return { | |
| 163 | - words: words.filter( ( w ) => { | |
| 164 | - return w && w.length; | |
| 165 | - } ), | |
| 166 | - tip: matchTip ? stripSlashes( matchTip[ 4 ] || matchTip[ 5 ] || '' ) : '', | |
| 167 | - corrects: corrects.filter( ( w ) => { | |
| 168 | - return w && w.length; | |
| 169 | - } ), | |
| 170 | - }; | |
| 171 | - }; | |
| 172 | - | |
| 173 | - /** | |
| 174 | - * Event handler for each input in blanks. | |
| 175 | - * | |
| 176 | - * @param answer | |
| 177 | - */ | |
| 178 | - onChangeOption = ( answer ) => ( event ) => { | |
| 179 | - const { | |
| 180 | - instantParseBlanks, | |
| 181 | - } = this.props; | |
| 182 | - | |
| 183 | - if ( instantParseBlanks ) { | |
| 184 | - this.setContent( answer, event.target.value ); | |
| 185 | - } | |
| 186 | - }; | |
| 187 | - | |
| 188 | - /** | |
| 189 | - * Event handler to add new blank to question | |
| 190 | - * | |
| 191 | - * @param event | |
| 192 | - */ | |
| 193 | - addBlank = ( event ) => { | |
| 194 | - const { | |
| 195 | - addOption, | |
| 196 | - id, | |
| 197 | - } = this.props; | |
| 198 | - | |
| 199 | - addOption( id, { text: 'asdasdasd' } ); | |
| 200 | - }; | |
| 201 | - | |
| 202 | - /** | |
| 203 | - * Event handler to remove a blank. | |
| 204 | - * | |
| 205 | - * @param option | |
| 206 | - */ | |
| 207 | - removeBlank = ( option ) => () => { | |
| 208 | - const { | |
| 209 | - id, | |
| 210 | - removeOption, | |
| 211 | - } = this.props; | |
| 212 | - | |
| 213 | - removeOption( id, option.question_answer_id ); | |
| 214 | - }; | |
| 215 | - | |
| 216 | - /** | |
| 217 | - * Get html of passage content for preview. | |
| 218 | - * | |
| 219 | - * @return {string} | |
| 220 | - */ | |
| 221 | - getPreview = () => { | |
| 222 | - const { | |
| 223 | - blankOptions, | |
| 224 | - blankFillsStyle, | |
| 225 | - blanksStyle, | |
| 226 | - } = this.props; | |
| 227 | - | |
| 228 | - if ( ! blankOptions ) { | |
| 229 | - return ''; | |
| 230 | - } | |
| 231 | - | |
| 232 | - const preview = blankOptions.map( ( answer ) => { | |
| 233 | - const blanks = this.getBlanks( answer.text ); | |
| 234 | - const blank = blanks ? blanks[ 0 ] : {}; | |
| 235 | - | |
| 236 | - let html = ''; | |
| 237 | - | |
| 238 | - if ( blank && blank.words.length ) { | |
| 239 | - html = '<span class="blank-input"></span>'; | |
| 240 | - | |
| 241 | - if ( blank.words.length > 1 ) { | |
| 242 | - switch ( blankFillsStyle ) { | |
| 243 | - case 'select': | |
| 244 | - html += '<select>' + blank.words.map( ( word ) => { | |
| 245 | - return `<option value=${ word }>${ word }</option>`; | |
| 246 | - } ).join( '' ) + '</select>'; | |
| 247 | - | |
| 248 | - break; | |
| 249 | - case 'enumeration': | |
| 250 | - html += '(' + blank.words.map( ( word ) => { | |
| 251 | - return `<code>${ word }</code>`; | |
| 252 | - } ).join( ', ' ) + ')'; | |
| 253 | - break; | |
| 254 | - } | |
| 255 | - } | |
| 256 | - | |
| 257 | - if ( blank.tip ) { | |
| 258 | - html += '?'; | |
| 259 | - } | |
| 260 | - } | |
| 261 | - | |
| 262 | - return ( '' + answer.text ).replace( /\{\{(.*)\}\}/, html ); | |
| 263 | - } ).join( blanksStyle === 'paragraphs' ? '</div><div>' : ( blanksStyle === 'ordered' ? '</li><li>' : ' ' ) ); | |
| 264 | - | |
| 265 | - return blanksStyle === 'paragraphs' ? `<div>${ preview }</div>` : ( blanksStyle === 'ordered' ? `<ol><li>${ preview }</li></ol>` : preview ); | |
| 266 | - }; | |
| 267 | - | |
| 268 | - render() { | |
| 269 | - const { | |
| 270 | - blanks, | |
| 271 | - blankOptions, | |
| 272 | - } = this.props; | |
| 273 | - | |
| 274 | - return <React.Fragment> | |
| 275 | - <div className="blank-options"> | |
| 276 | - { blankOptions && ( | |
| 277 | - <ul className="blanks"> | |
| 278 | - { | |
| 279 | - blankOptions.map( ( answer ) => { | |
| 280 | - const blankOptions = blanks[ answer.question_answer_id ] || {}; | |
| 281 | - | |
| 282 | - return <li className="blank" key={ answer.question_answer_id }> | |
| 283 | - <textarea className="blank-content" onChange={ this.onChangeOption( answer ) } | |
| 284 | - value={ answer.text }> | |
| 285 | - </textarea> | |
| 286 | - { blankOptions.words && ( | |
| 287 | - <div className="blank-words"> | |
| 288 | - <label>{ __( 'Words fill', 'learnpress' ) }</label> | |
| 289 | - <p> | |
| 290 | - { | |
| 291 | - blankOptions.words && blankOptions.words.map( ( word, i ) => { | |
| 292 | - const className = [ 'word' ]; | |
| 293 | - ( blankOptions.corrects || [] ).indexOf( word ) !== -1 && className.push( 'correct' ); | |
| 294 | - | |
| 295 | - return <code key={ `word-${ word }-${ i }` } | |
| 296 | - className={ className.join( ' ' ) }>{ word }</code>; | |
| 297 | - } ) | |
| 298 | - } | |
| 299 | - </p> | |
| 300 | - </div> | |
| 301 | - ) | |
| 302 | - } | |
| 303 | - | |
| 304 | - { blankOptions.tip && ( | |
| 305 | - <div className="blank-tip"> | |
| 306 | - <label>{ __( 'Tip', 'learnpress' ) }</label> | |
| 307 | - <p>{ blankOptions.tip }</p> | |
| 308 | - </div> | |
| 309 | - ) | |
| 310 | - } | |
| 311 | - | |
| 312 | - <button className="button button-remove" | |
| 313 | - onClick={ this.removeBlank( answer ) }>{ __( 'Remove', 'learnpress' ) }</button> | |
| 314 | - </li>; | |
| 315 | - } ) | |
| 316 | - } | |
| 317 | - </ul> ) | |
| 318 | - } | |
| 319 | - <button className="button" onClick={ this.addBlank }>{ __( 'Add Blank', 'learnpress' ) }</button> | |
| 320 | - </div> | |
| 321 | - <div className="passage-preview" dangerouslySetInnerHTML={ { __html: this.getPreview() } }> | |
| 322 | - </div> | |
| 323 | - </React.Fragment>; | |
| 324 | - } | |
| 325 | -} | |
| 326 | - | |
| 327 | -export default compose( [ | |
| 328 | - withSelect( ( select ) => { | |
| 329 | - const { | |
| 330 | - getData, | |
| 331 | - } = select( 'learnpress/question' ); | |
| 332 | - | |
| 333 | - return getData( 'question' ); | |
| 334 | - } ), | |
| 335 | - withDispatch( ( dispatch ) => { | |
| 336 | - const { | |
| 337 | - setData, | |
| 338 | - addOption, | |
| 339 | - removeOption, | |
| 340 | - updateOption, | |
| 341 | - } = dispatch( 'learnpress/question' ); | |
| 342 | - | |
| 343 | - return { | |
| 344 | - setData, | |
| 345 | - addOption, | |
| 346 | - removeOption, | |
| 347 | - updateOption, | |
| 348 | - }; | |
| 349 | - } ), | |
| 350 | -] )( Editor ); | |
| 1 | +import { Component } from '@wordpress/element'; | |
| 2 | +import { withSelect, withDispatch, select as wpSelect } from '@wordpress/data'; | |
| 3 | +import { compose } from '@wordpress/compose'; | |
| 4 | +import { __ } from '@wordpress/i18n'; | |
| 5 | + | |
| 6 | +const { debounce } = lodash; | |
| 7 | + | |
| 8 | +import store from './store'; | |
| 9 | + | |
| 10 | +const stripSlashes = function stripSlashes( str ) { | |
| 11 | + return ( str + '' ).replace( /\\(.?)/g, function( s, n1 ) { | |
| 12 | + switch ( n1 ) { | |
| 13 | + case '\\': | |
| 14 | + return '\\'; | |
| 15 | + case '0': | |
| 16 | + return '\u0000'; | |
| 17 | + case '': | |
| 18 | + return ''; | |
| 19 | + default: | |
| 20 | + return n1; | |
| 21 | + } | |
| 22 | + } ); | |
| 23 | +}; | |
| 24 | + | |
| 25 | +class Editor extends Component { | |
| 26 | + constructor( props ) { | |
| 27 | + super( ...arguments ); | |
| 28 | + | |
| 29 | + this.state = { | |
| 30 | + blanks: [], | |
| 31 | + }; | |
| 32 | + } | |
| 33 | + | |
| 34 | + componentDidMount() { | |
| 35 | + const { | |
| 36 | + setData, | |
| 37 | + initSettings, | |
| 38 | + } = this.props; | |
| 39 | + const { blankOptions } = initSettings; | |
| 40 | + const newBlanks = {}; | |
| 41 | + | |
| 42 | + blankOptions.map( ( option ) => { | |
| 43 | + const optionBlanks = this.getBlanks( option.text ); | |
| 44 | + newBlanks[ option.question_answer_id ] = optionBlanks ? optionBlanks[ 0 ] : []; | |
| 45 | + } ); | |
| 46 | + | |
| 47 | + setData( { | |
| 48 | + question: { | |
| 49 | + ...initSettings, | |
| 50 | + blanks: newBlanks, | |
| 51 | + }, | |
| 52 | + } ); | |
| 53 | + } | |
| 54 | + | |
| 55 | + /** | |
| 56 | + * Parse blanks options from content and update state. | |
| 57 | + * | |
| 58 | + * @param option | |
| 59 | + * @param text | |
| 60 | + */ | |
| 61 | + setContent = ( option, text ) => { | |
| 62 | + const optionBlanks = this.getBlanks( text ); | |
| 63 | + const { | |
| 64 | + blankOptions, | |
| 65 | + blanks, | |
| 66 | + updateOption, | |
| 67 | + setData, | |
| 68 | + id, | |
| 69 | + } = this.props; | |
| 70 | + | |
| 71 | + const newState = { | |
| 72 | + blankOptions: blankOptions.map( ( opt ) => { | |
| 73 | + return opt.question_answer_id == option.question_answer_id ? { | |
| 74 | + ...opt, | |
| 75 | + text, | |
| 76 | + } : opt; | |
| 77 | + } ), | |
| 78 | + blanks: { | |
| 79 | + ...blanks, | |
| 80 | + [ option.question_answer_id ]: optionBlanks ? optionBlanks[ 0 ] : [], | |
| 81 | + }, | |
| 82 | + }; | |
| 83 | + | |
| 84 | + setData( newState, 'question' ); | |
| 85 | + this.queue = this.queue || {}; | |
| 86 | + this.queue[ option.question_answer_id ] = [ { | |
| 87 | + text, | |
| 88 | + blanks: newState.blanks[ option.question_answer_id ], | |
| 89 | + }, option.question_answer_id, id ]; | |
| 90 | + | |
| 91 | + this.updateOption(); | |
| 92 | + }; | |
| 93 | + | |
| 94 | + updateOption = debounce( () => { | |
| 95 | + const { | |
| 96 | + updateOption, | |
| 97 | + } = this.props; | |
| 98 | + | |
| 99 | + const queue = this.queue ? Object.values( this.queue ) : []; | |
| 100 | + | |
| 101 | + queue.map( ( item ) => { | |
| 102 | + if ( item ) { | |
| 103 | + updateOption( item[ 0 ], item[ 1 ], item[ 2 ] ); | |
| 104 | + delete this.queue[ item[ 1 ] ]; | |
| 105 | + } | |
| 106 | + } ); | |
| 107 | + }, 1000 ); | |
| 108 | + | |
| 109 | + /** | |
| 110 | + * Get blanks from a content and return. | |
| 111 | + * | |
| 112 | + * @param content | |
| 113 | + * @return {Array} | |
| 114 | + */ | |
| 115 | + getBlanks = ( content ) => { | |
| 116 | + // if (undefined === content) { | |
| 117 | + // content = this.state.passage; | |
| 118 | + // } | |
| 119 | + | |
| 120 | + const blanks = []; | |
| 121 | + const shortcodes = content.match( /\{\{([^\{\"\'].*?)\}\}/g ); | |
| 122 | + | |
| 123 | + if ( shortcodes ) { | |
| 124 | + shortcodes.map( ( shortcode ) => { | |
| 125 | + blanks.push( this.getBlank( shortcode ) ); | |
| 126 | + } ); | |
| 127 | + } | |
| 128 | + return blanks; | |
| 129 | + }; | |
| 130 | + | |
| 131 | + /** | |
| 132 | + * Parse details from a blank. | |
| 133 | + * | |
| 134 | + * @param blank | |
| 135 | + * @return {{words: Array, tip: string, corrects: Array}} | |
| 136 | + */ | |
| 137 | + getBlank = ( blank ) => { | |
| 138 | + const contents = blank.match( /\{\{(.*)\}\}/ ); | |
| 139 | + const words = contents && contents[ 1 ] ? contents[ 1 ].split( '/' ) : []; | |
| 140 | + const matchTip = words.length ? words[ words.length - 1 ].match( /(.*)(\s(\"(.*)\"|\'(.*)\'))/ ) : false; | |
| 141 | + const corrects = []; | |
| 142 | + | |
| 143 | + // Remove tip from last word. (last-word "This is tip of the blank") | |
| 144 | + if ( matchTip ) { | |
| 145 | + words[ words.length - 1 ] = matchTip[ 1 ]; | |
| 146 | + } | |
| 147 | + | |
| 148 | + words.map( ( word, i ) => { | |
| 149 | + // Match the word wrapped by [ and ] is CORRECT word | |
| 150 | + const matchCorrect = word.match( /\[(.*)\]/ ); | |
| 151 | + | |
| 152 | + if ( matchCorrect ) { | |
| 153 | + // Remove the "[" and "]" | |
| 154 | + matchCorrect[ 1 ] = stripSlashes( matchCorrect[ 1 ] ); | |
| 155 | + corrects.push( matchCorrect[ 1 ] ); | |
| 156 | + words[ i ] = matchCorrect[ 1 ]; | |
| 157 | + } else { | |
| 158 | + words[ i ] = stripSlashes( word ); | |
| 159 | + } | |
| 160 | + } ); | |
| 161 | + | |
| 162 | + return { | |
| 163 | + words: words.filter( ( w ) => { | |
| 164 | + return w && w.length; | |
| 165 | + } ), | |
| 166 | + tip: matchTip ? stripSlashes( matchTip[ 4 ] || matchTip[ 5 ] || '' ) : '', | |
| 167 | + corrects: corrects.filter( ( w ) => { | |
| 168 | + return w && w.length; | |
| 169 | + } ), | |
| 170 | + }; | |
| 171 | + }; | |
| 172 | + | |
| 173 | + /** | |
| 174 | + * Event handler for each input in blanks. | |
| 175 | + * | |
| 176 | + * @param answer | |
| 177 | + */ | |
| 178 | + onChangeOption = ( answer ) => ( event ) => { | |
| 179 | + const { | |
| 180 | + instantParseBlanks, | |
| 181 | + } = this.props; | |
| 182 | + | |
| 183 | + if ( instantParseBlanks ) { | |
| 184 | + this.setContent( answer, event.target.value ); | |
| 185 | + } | |
| 186 | + }; | |
| 187 | + | |
| 188 | + /** | |
| 189 | + * Event handler to add new blank to question | |
| 190 | + * | |
| 191 | + * @param event | |
| 192 | + */ | |
| 193 | + addBlank = ( event ) => { | |
| 194 | + const { | |
| 195 | + addOption, | |
| 196 | + id, | |
| 197 | + } = this.props; | |
| 198 | + | |
| 199 | + addOption( id, { text: 'asdasdasd' } ); | |
| 200 | + }; | |
| 201 | + | |
| 202 | + /** | |
| 203 | + * Event handler to remove a blank. | |
| 204 | + * | |
| 205 | + * @param option | |
| 206 | + */ | |
| 207 | + removeBlank = ( option ) => () => { | |
| 208 | + const { | |
| 209 | + id, | |
| 210 | + removeOption, | |
| 211 | + } = this.props; | |
| 212 | + | |
| 213 | + removeOption( id, option.question_answer_id ); | |
| 214 | + }; | |
| 215 | + | |
| 216 | + /** | |
| 217 | + * Get html of passage content for preview. | |
| 218 | + * | |
| 219 | + * @return {string} | |
| 220 | + */ | |
| 221 | + getPreview = () => { | |
| 222 | + const { | |
| 223 | + blankOptions, | |
| 224 | + blankFillsStyle, | |
| 225 | + blanksStyle, | |
| 226 | + } = this.props; | |
| 227 | + | |
| 228 | + if ( ! blankOptions ) { | |
| 229 | + return ''; | |
| 230 | + } | |
| 231 | + | |
| 232 | + const preview = blankOptions.map( ( answer ) => { | |
| 233 | + const blanks = this.getBlanks( answer.text ); | |
| 234 | + const blank = blanks ? blanks[ 0 ] : {}; | |
| 235 | + | |
| 236 | + let html = ''; | |
| 237 | + | |
| 238 | + if ( blank && blank.words.length ) { | |
| 239 | + html = '<span class="blank-input"></span>'; | |
| 240 | + | |
| 241 | + if ( blank.words.length > 1 ) { | |
| 242 | + switch ( blankFillsStyle ) { | |
| 243 | + case 'select': | |
| 244 | + html += '<select>' + blank.words.map( ( word ) => { | |
| 245 | + return `<option value=${ word }>${ word }</option>`; | |
| 246 | + } ).join( '' ) + '</select>'; | |
| 247 | + | |
| 248 | + break; | |
| 249 | + case 'enumeration': | |
| 250 | + html += '(' + blank.words.map( ( word ) => { | |
| 251 | + return `<code>${ word }</code>`; | |
| 252 | + } ).join( ', ' ) + ')'; | |
| 253 | + break; | |
| 254 | + } | |
| 255 | + } | |
| 256 | + | |
| 257 | + if ( blank.tip ) { | |
| 258 | + html += '?'; | |
| 259 | + } | |
| 260 | + } | |
| 261 | + | |
| 262 | + return ( '' + answer.text ).replace( /\{\{(.*)\}\}/, html ); | |
| 263 | + } ).join( blanksStyle === 'paragraphs' ? '</div><div>' : ( blanksStyle === 'ordered' ? '</li><li>' : ' ' ) ); | |
| 264 | + | |
| 265 | + return blanksStyle === 'paragraphs' ? `<div>${ preview }</div>` : ( blanksStyle === 'ordered' ? `<ol><li>${ preview }</li></ol>` : preview ); | |
| 266 | + }; | |
| 267 | + | |
| 268 | + render() { | |
| 269 | + const { | |
| 270 | + blanks, | |
| 271 | + blankOptions, | |
| 272 | + } = this.props; | |
| 273 | + | |
| 274 | + return <React.Fragment> | |
| 275 | + <div className="blank-options"> | |
| 276 | + { blankOptions && ( | |
| 277 | + <ul className="blanks"> | |
| 278 | + { | |
| 279 | + blankOptions.map( ( answer ) => { | |
| 280 | + const blankOptions = blanks[ answer.question_answer_id ] || {}; | |
| 281 | + | |
| 282 | + return <li className="blank" key={ answer.question_answer_id }> | |
| 283 | + <textarea className="blank-content" onChange={ this.onChangeOption( answer ) } | |
| 284 | + value={ answer.text }> | |
| 285 | + </textarea> | |
| 286 | + { blankOptions.words && ( | |
| 287 | + <div className="blank-words"> | |
| 288 | + <label>{ __( 'Words fill', 'learnpress' ) }</label> | |
| 289 | + <p> | |
| 290 | + { | |
| 291 | + blankOptions.words && blankOptions.words.map( ( word, i ) => { | |
| 292 | + const className = [ 'word' ]; | |
| 293 | + ( blankOptions.corrects || [] ).indexOf( word ) !== -1 && className.push( 'correct' ); | |
| 294 | + | |
| 295 | + return <code key={ `word-${ word }-${ i }` } | |
| 296 | + className={ className.join( ' ' ) }>{ word }</code>; | |
| 297 | + } ) | |
| 298 | + } | |
| 299 | + </p> | |
| 300 | + </div> | |
| 301 | + ) | |
| 302 | + } | |
| 303 | + | |
| 304 | + { blankOptions.tip && ( | |
| 305 | + <div className="blank-tip"> | |
| 306 | + <label>{ __( 'Tip', 'learnpress' ) }</label> | |
| 307 | + <p>{ blankOptions.tip }</p> | |
| 308 | + </div> | |
| 309 | + ) | |
| 310 | + } | |
| 311 | + | |
| 312 | + <button className="button button-remove" | |
| 313 | + onClick={ this.removeBlank( answer ) }>{ __( 'Remove', 'learnpress' ) }</button> | |
| 314 | + </li>; | |
| 315 | + } ) | |
| 316 | + } | |
| 317 | + </ul> ) | |
| 318 | + } | |
| 319 | + <button className="button" onClick={ this.addBlank }>{ __( 'Add Blank', 'learnpress' ) }</button> | |
| 320 | + </div> | |
| 321 | + <div className="passage-preview" dangerouslySetInnerHTML={ { __html: this.getPreview() } }> | |
| 322 | + </div> | |
| 323 | + </React.Fragment>; | |
| 324 | + } | |
| 325 | +} | |
| 326 | + | |
| 327 | +export default compose( [ | |
| 328 | + withSelect( ( select ) => { | |
| 329 | + const { | |
| 330 | + getData, | |
| 331 | + } = select( 'learnpress/question' ); | |
| 332 | + | |
| 333 | + return getData( 'question' ); | |
| 334 | + } ), | |
| 335 | + withDispatch( ( dispatch ) => { | |
| 336 | + const { | |
| 337 | + setData, | |
| 338 | + addOption, | |
| 339 | + removeOption, | |
| 340 | + updateOption, | |
| 341 | + } = dispatch( 'learnpress/question' ); | |
| 342 | + | |
| 343 | + return { | |
| 344 | + setData, | |
| 345 | + addOption, | |
| 346 | + removeOption, | |
| 347 | + updateOption, | |
| 348 | + }; | |
| 349 | + } ), | |
| 350 | +] )( Editor ); | |