@pie-element/multiple-choice 9.13.0 → 9.13.1-next.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -277,7 +277,12 @@ var MultipleChoice = /*#__PURE__*/function (_HTMLElement) {
277
277
  audio.removeEventListener('ended', handleEnded);
278
278
  };
279
279
 
280
- audio.addEventListener('ended', handleEnded);
280
+ audio.addEventListener('ended', handleEnded); // store references to remove later
281
+
282
+ _this2._audio = audio;
283
+ _this2._handlePlaying = handlePlaying;
284
+ _this2._handleEnded = handleEnded;
285
+ _this2._enableAudio = enableAudio;
281
286
  observer.disconnect();
282
287
  }
283
288
  });
@@ -302,6 +307,16 @@ var MultipleChoice = /*#__PURE__*/function (_HTMLElement) {
302
307
  window.removeEventListener('keydown', this._boundHandleKeyDown);
303
308
  this._keyboardEventsEnabled = false;
304
309
  }
310
+
311
+ document.removeEventListener('click', this._enableAudio);
312
+
313
+ if (this._audio) {
314
+ this._audio.removeEventListener('playing', this._handlePlaying);
315
+
316
+ this._audio.removeEventListener('ended', this._handleEnded);
317
+
318
+ this._audio = null;
319
+ }
305
320
  }
306
321
  /**
307
322
  * Handles global keyboard events for selecting or toggling multiple-choice answers.
@@ -312,6 +327,8 @@ var MultipleChoice = /*#__PURE__*/function (_HTMLElement) {
312
327
  }, {
313
328
  key: "handleKeyDown",
314
329
  value: function handleKeyDown(event) {
330
+ var _this$_model$choices;
331
+
315
332
  if (!this._model || !this._session) {
316
333
  return;
317
334
  }
@@ -324,7 +341,7 @@ var MultipleChoice = /*#__PURE__*/function (_HTMLElement) {
324
341
 
325
342
  var choiceIndex = keyToIndex(event.key);
326
343
 
327
- if (choiceIndex === undefined || choiceIndex >= this._model.choices.length) {
344
+ if (choiceIndex === undefined || choiceIndex <= -1 || choiceIndex >= ((_this$_model$choices = this._model.choices) === null || _this$_model$choices === void 0 ? void 0 : _this$_model$choices.length)) {
328
345
  return;
329
346
  }
330
347
 
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.js"],"names":["log","isComplete","session","model","audioComplete","autoplayAudioEnabled","completeAudioEnabled","value","choiceMode","minSelections","maxSelections","selections","length","MultipleChoice","_model","_session","_boundHandleKeyDown","handleKeyDown","bind","_keyboardEventsEnabled","_rerender","element","React","createElement","Main","onChoiceChanged","_onChange","onShowCorrectToggle","setAttribute","setLangAttribute","ReactDOM","render","keyboardEventsEnabled","enableKeyboardEvents","leading","trailing","_dispatchResponseChanged","dispatchEvent","SessionChangedEvent","tagName","toLowerCase","_dispatchModelSet","ModelSetEvent","undefined","language","lang","slice","s","data","info","document","id","Object","assign","style","position","top","width","height","display","justifyContent","alignItems","background","zIndex","img","src","alt","appendChild","observer","MutationObserver","mutationsList","forEach","mutation","type","audio","querySelector","isInsidePrompt","closest","_createAudioInfoToast","container","enableAudio","play","removeChild","removeEventListener","setTimeout","paused","addEventListener","handlePlaying","audioStartTime","Date","getTime","handleEnded","audioEndTime","disconnect","observe","childList","subtree","window","event","keyToIndex","key","numOffset","letterOffset","test","charCodeAt","choiceIndex","choices","currentValue","choiceId","newValue","selected","includes","selector","HTMLElement"],"mappings":"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","sourcesContent":["import Main from './main';\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport debounce from 'lodash/debounce';\nimport debug from 'debug';\nimport { ModelSetEvent, SessionChangedEvent } from '@pie-framework/pie-player-events';\nimport { renderMath } from '@pie-lib/pie-toolbox/math-rendering';\nimport { updateSessionValue, updateSessionMetadata } from './session-updater';\n\nconst log = debug('pie-ui:multiple-choice');\n\nexport const isComplete = (session, model, audioComplete) => {\n const { autoplayAudioEnabled, completeAudioEnabled } = model || {};\n\n if (autoplayAudioEnabled && completeAudioEnabled && !audioComplete) {\n return false;\n }\n\n if (!session || !session.value) {\n return false;\n }\n\n const { choiceMode, minSelections = 1, maxSelections } = model || {};\n const selections = session.value.length || 0;\n\n if (choiceMode === 'radio') {\n return !!selections;\n }\n\n if (selections < minSelections || selections > maxSelections) {\n return false;\n }\n\n return true;\n};\n\nexport default class MultipleChoice extends HTMLElement {\n constructor() {\n super();\n this._model = null;\n this._session = null;\n this.audioComplete = false;\n this._boundHandleKeyDown = this.handleKeyDown.bind(this);\n this._keyboardEventsEnabled = false;\n\n this._rerender = debounce(\n () => {\n if (this._model && this._session) {\n var element = React.createElement(Main, {\n model: this._model,\n session: this._session,\n onChoiceChanged: this._onChange.bind(this),\n onShowCorrectToggle: this.onShowCorrectToggle.bind(this),\n });\n\n //TODO: aria-label is set in the _rerender because we need to change it when the model.choiceMode is updated. Consider revisiting the placement of the aria-label setting in the _rerender\n this.setAttribute(\n 'aria-label',\n this._model.choiceMode === 'radio' ? 'Multiple Choice Question' : 'Multiple Correct Answer Question',\n );\n this.setAttribute('role', 'region');\n this.setLangAttribute();\n\n ReactDOM.render(element, this, () => {\n log('render complete - render math');\n renderMath(this);\n });\n\n if (this._model.keyboardEventsEnabled === true && !this._keyboardEventsEnabled) {\n this.enableKeyboardEvents();\n }\n } else {\n log('skip');\n }\n },\n 50,\n { leading: false, trailing: true },\n );\n\n this._dispatchResponseChanged = debounce(() => {\n this.dispatchEvent(\n new SessionChangedEvent(this.tagName.toLowerCase(), isComplete(this._session, this._model, this.audioComplete)),\n );\n });\n\n this._dispatchModelSet = debounce(\n () => {\n this.dispatchEvent(\n new ModelSetEvent(\n this.tagName.toLowerCase(),\n isComplete(this._session, this._model),\n this._model !== undefined,\n ),\n );\n },\n 50,\n { leading: false, trailing: true },\n );\n }\n\n onShowCorrectToggle() {\n renderMath(this);\n }\n\n setLangAttribute() {\n const language = this._model && typeof this._model.language ? this._model.language : '';\n const lang = language ? language.slice(0, 2) : 'en';\n this.setAttribute('lang', lang);\n }\n\n set model(s) {\n this._model = s;\n this._rerender();\n this._dispatchModelSet();\n }\n\n get session() {\n return this._session;\n }\n\n set session(s) {\n this._session = s;\n this._rerender();\n //TODO: remove this session-changed should only be emit on user change\n this._dispatchResponseChanged();\n }\n\n _onChange(data) {\n updateSessionValue(this._session, this._model.choiceMode, data);\n this._dispatchResponseChanged();\n this._rerender();\n }\n\n _createAudioInfoToast() {\n const info = document.createElement('div');\n info.id = 'play-audio-info';\n\n Object.assign(info.style, {\n position: 'absolute',\n top: 0,\n width:'100%',\n height: '100%',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n background: 'white',\n zIndex: '1000'\n });\n\n const img = document.createElement('img');\n img.src = 'https://student.assessment.renaissance.com/ce/quizenginecap/assets/img/playAppsSel.gif';\n img.alt = 'Click anywhere to enable audio autoplay';\n img.width = 500;\n img.height = 300;\n\n info.appendChild(img);\n return info;\n }\n\n connectedCallback() {\n this._rerender();\n\n // Observation: audio in Chrome will have the autoplay attribute,\n // while other browsers will not have the autoplay attribute and will need a user interaction to play the audio\n // This workaround fixes the issue of audio being cached and played on any user interaction in Safari and Firefox\n const observer = new MutationObserver((mutationsList, observer) => {\n mutationsList.forEach((mutation) => {\n if (mutation.type === 'childList') {\n const audio = this.querySelector('audio');\n const isInsidePrompt = audio && audio.closest('#preview-prompt');\n\n if (!this._model) return;\n if (!this._model.autoplayAudioEnabled) return;\n if (audio && !isInsidePrompt) return;\n if (!audio) return;\n\n const info = this._createAudioInfoToast();\n const container = this.querySelector('#main-container');\n const enableAudio = () => {\n if (this.querySelector('#play-audio-info')) {\n audio.play();\n container.removeChild(info);\n }\n\n document.removeEventListener('click', enableAudio);\n };\n\n // if the audio is paused, it means the user has not interacted with the page yet and the audio will not play\n // FIX FOR SAFARI: play with a slight delay to check if autoplay was blocked\n setTimeout(() => {\n if (audio.paused && !this.querySelector('#play-audio-info')) {\n // add info message as a toast to enable audio playback\n container.appendChild(info);\n document.addEventListener('click', enableAudio);\n } else {\n document.removeEventListener('click', enableAudio);\n }\n }, 500);\n\n // we need to listen for the playing event to remove the toast in case the audio plays because of re-rendering\n const handlePlaying = () => {\n updateSessionMetadata(this._session, { audioStartTime: new Date().getTime() });\n \n const info = this.querySelector('#play-audio-info');\n if (info) {\n container.removeChild(info);\n }\n\n audio.removeEventListener('playing', handlePlaying);\n };\n\n audio.addEventListener('playing', handlePlaying);\n\n // we need to listen for the ended event to update the isComplete state\n const handleEnded = () => {\n updateSessionMetadata(this._session, { audioEndTime: new Date().getTime() });\n this.audioComplete = true;\n this._dispatchResponseChanged();\n audio.removeEventListener('ended', handleEnded);\n };\n\n audio.addEventListener('ended', handleEnded);\n\n observer.disconnect();\n }\n });\n });\n\n observer.observe(this, { childList: true, subtree: true });\n }\n\n enableKeyboardEvents() {\n if (!this._keyboardEventsEnabled) {\n window.addEventListener('keydown', this._boundHandleKeyDown);\n this._keyboardEventsEnabled = true;\n }\n }\n\n disconnectedCallback() {\n if (this._keyboardEventsEnabled) {\n window.removeEventListener('keydown', this._boundHandleKeyDown);\n this._keyboardEventsEnabled = false;\n }\n }\n\n /**\n * Handles global keyboard events for selecting or toggling multiple-choice answers.\n * Maps keys (1-9, 0, a-j, A-J) to choices and updates the session state accordingly.\n * Ensures valid key presses toggle or select the appropriate choice based on the model.\n */\n handleKeyDown(event) {\n if (!this._model || !this._session) {\n return;\n }\n\n const keyToIndex = (key) => {\n const numOffset = key >= '1' && key <= '9' ? key - '1' : key === '0' ? 9 : -1;\n const letterOffset = /^[a-jA-J]$/.test(key) ? key.toLowerCase().charCodeAt(0) - 'a'.charCodeAt(0) : -1;\n return numOffset >= 0 ? numOffset : letterOffset;\n };\n\n const choiceIndex = keyToIndex(event.key);\n\n if (choiceIndex === undefined || choiceIndex >= this._model.choices.length) {\n return;\n }\n\n const currentValue = this._session.value || [];\n const choiceId = this._model.choices[choiceIndex].value;\n\n const newValue = {\n value: choiceId,\n selected: !currentValue.includes(choiceId),\n selector: 'Keyboard',\n };\n\n this._onChange(newValue);\n }\n}\n"],"file":"index.js"}
1
+ {"version":3,"sources":["../src/index.js"],"names":["log","isComplete","session","model","audioComplete","autoplayAudioEnabled","completeAudioEnabled","value","choiceMode","minSelections","maxSelections","selections","length","MultipleChoice","_model","_session","_boundHandleKeyDown","handleKeyDown","bind","_keyboardEventsEnabled","_rerender","element","React","createElement","Main","onChoiceChanged","_onChange","onShowCorrectToggle","setAttribute","setLangAttribute","ReactDOM","render","keyboardEventsEnabled","enableKeyboardEvents","leading","trailing","_dispatchResponseChanged","dispatchEvent","SessionChangedEvent","tagName","toLowerCase","_dispatchModelSet","ModelSetEvent","undefined","language","lang","slice","s","data","info","document","id","Object","assign","style","position","top","width","height","display","justifyContent","alignItems","background","zIndex","img","src","alt","appendChild","observer","MutationObserver","mutationsList","forEach","mutation","type","audio","querySelector","isInsidePrompt","closest","_createAudioInfoToast","container","enableAudio","play","removeChild","removeEventListener","setTimeout","paused","addEventListener","handlePlaying","audioStartTime","Date","getTime","handleEnded","audioEndTime","_audio","_handlePlaying","_handleEnded","_enableAudio","disconnect","observe","childList","subtree","window","event","keyToIndex","key","numOffset","letterOffset","test","charCodeAt","choiceIndex","choices","currentValue","choiceId","newValue","selected","includes","selector","HTMLElement"],"mappings":"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","sourcesContent":["import Main from './main';\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport debounce from 'lodash/debounce';\nimport debug from 'debug';\nimport { ModelSetEvent, SessionChangedEvent } from '@pie-framework/pie-player-events';\nimport { renderMath } from '@pie-lib/pie-toolbox/math-rendering';\nimport { updateSessionValue, updateSessionMetadata } from './session-updater';\n\nconst log = debug('pie-ui:multiple-choice');\n\nexport const isComplete = (session, model, audioComplete) => {\n const { autoplayAudioEnabled, completeAudioEnabled } = model || {};\n\n if (autoplayAudioEnabled && completeAudioEnabled && !audioComplete) {\n return false;\n }\n\n if (!session || !session.value) {\n return false;\n }\n\n const { choiceMode, minSelections = 1, maxSelections } = model || {};\n const selections = session.value.length || 0;\n\n if (choiceMode === 'radio') {\n return !!selections;\n }\n\n if (selections < minSelections || selections > maxSelections) {\n return false;\n }\n\n return true;\n};\n\nexport default class MultipleChoice extends HTMLElement {\n constructor() {\n super();\n this._model = null;\n this._session = null;\n this.audioComplete = false;\n this._boundHandleKeyDown = this.handleKeyDown.bind(this);\n this._keyboardEventsEnabled = false;\n\n this._rerender = debounce(\n () => {\n if (this._model && this._session) {\n var element = React.createElement(Main, {\n model: this._model,\n session: this._session,\n onChoiceChanged: this._onChange.bind(this),\n onShowCorrectToggle: this.onShowCorrectToggle.bind(this),\n });\n\n //TODO: aria-label is set in the _rerender because we need to change it when the model.choiceMode is updated. Consider revisiting the placement of the aria-label setting in the _rerender\n this.setAttribute(\n 'aria-label',\n this._model.choiceMode === 'radio' ? 'Multiple Choice Question' : 'Multiple Correct Answer Question',\n );\n this.setAttribute('role', 'region');\n this.setLangAttribute();\n\n ReactDOM.render(element, this, () => {\n log('render complete - render math');\n renderMath(this);\n });\n\n if (this._model.keyboardEventsEnabled === true && !this._keyboardEventsEnabled) {\n this.enableKeyboardEvents();\n }\n } else {\n log('skip');\n }\n },\n 50,\n { leading: false, trailing: true },\n );\n\n this._dispatchResponseChanged = debounce(() => {\n this.dispatchEvent(\n new SessionChangedEvent(this.tagName.toLowerCase(), isComplete(this._session, this._model, this.audioComplete)),\n );\n });\n\n this._dispatchModelSet = debounce(\n () => {\n this.dispatchEvent(\n new ModelSetEvent(\n this.tagName.toLowerCase(),\n isComplete(this._session, this._model),\n this._model !== undefined,\n ),\n );\n },\n 50,\n { leading: false, trailing: true },\n );\n }\n\n onShowCorrectToggle() {\n renderMath(this);\n }\n\n setLangAttribute() {\n const language = this._model && typeof this._model.language ? this._model.language : '';\n const lang = language ? language.slice(0, 2) : 'en';\n this.setAttribute('lang', lang);\n }\n\n set model(s) {\n this._model = s;\n this._rerender();\n this._dispatchModelSet();\n }\n\n get session() {\n return this._session;\n }\n\n set session(s) {\n this._session = s;\n this._rerender();\n //TODO: remove this session-changed should only be emit on user change\n this._dispatchResponseChanged();\n }\n\n _onChange(data) {\n updateSessionValue(this._session, this._model.choiceMode, data);\n this._dispatchResponseChanged();\n this._rerender();\n }\n\n _createAudioInfoToast() {\n const info = document.createElement('div');\n info.id = 'play-audio-info';\n\n Object.assign(info.style, {\n position: 'absolute',\n top: 0,\n width:'100%',\n height: '100%',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n background: 'white',\n zIndex: '1000'\n });\n\n const img = document.createElement('img');\n img.src = 'https://student.assessment.renaissance.com/ce/quizenginecap/assets/img/playAppsSel.gif';\n img.alt = 'Click anywhere to enable audio autoplay';\n img.width = 500;\n img.height = 300;\n\n info.appendChild(img);\n return info;\n }\n\n connectedCallback() {\n this._rerender();\n\n // Observation: audio in Chrome will have the autoplay attribute,\n // while other browsers will not have the autoplay attribute and will need a user interaction to play the audio\n // This workaround fixes the issue of audio being cached and played on any user interaction in Safari and Firefox\n const observer = new MutationObserver((mutationsList, observer) => {\n mutationsList.forEach((mutation) => {\n if (mutation.type === 'childList') {\n const audio = this.querySelector('audio');\n const isInsidePrompt = audio && audio.closest('#preview-prompt');\n\n if (!this._model) return;\n if (!this._model.autoplayAudioEnabled) return;\n if (audio && !isInsidePrompt) return;\n if (!audio) return;\n\n const info = this._createAudioInfoToast();\n const container = this.querySelector('#main-container');\n const enableAudio = () => {\n if (this.querySelector('#play-audio-info')) {\n audio.play();\n container.removeChild(info);\n }\n\n document.removeEventListener('click', enableAudio);\n };\n\n // if the audio is paused, it means the user has not interacted with the page yet and the audio will not play\n // FIX FOR SAFARI: play with a slight delay to check if autoplay was blocked\n setTimeout(() => {\n if (audio.paused && !this.querySelector('#play-audio-info')) {\n // add info message as a toast to enable audio playback\n container.appendChild(info);\n document.addEventListener('click', enableAudio);\n } else {\n document.removeEventListener('click', enableAudio);\n }\n }, 500);\n\n // we need to listen for the playing event to remove the toast in case the audio plays because of re-rendering\n const handlePlaying = () => {\n updateSessionMetadata(this._session, { audioStartTime: new Date().getTime() });\n\n const info = this.querySelector('#play-audio-info');\n if (info) {\n container.removeChild(info);\n }\n\n audio.removeEventListener('playing', handlePlaying);\n };\n\n audio.addEventListener('playing', handlePlaying);\n\n // we need to listen for the ended event to update the isComplete state\n const handleEnded = () => {\n updateSessionMetadata(this._session, { audioEndTime: new Date().getTime() });\n this.audioComplete = true;\n this._dispatchResponseChanged();\n audio.removeEventListener('ended', handleEnded);\n };\n\n audio.addEventListener('ended', handleEnded);\n\n // store references to remove later\n this._audio = audio;\n this._handlePlaying = handlePlaying;\n this._handleEnded = handleEnded;\n this._enableAudio = enableAudio;\n\n observer.disconnect();\n }\n });\n });\n\n observer.observe(this, { childList: true, subtree: true });\n }\n\n enableKeyboardEvents() {\n if (!this._keyboardEventsEnabled) {\n window.addEventListener('keydown', this._boundHandleKeyDown);\n this._keyboardEventsEnabled = true;\n }\n }\n\n disconnectedCallback() {\n if (this._keyboardEventsEnabled) {\n window.removeEventListener('keydown', this._boundHandleKeyDown);\n this._keyboardEventsEnabled = false;\n }\n\n document.removeEventListener('click', this._enableAudio);\n\n if (this._audio) {\n this._audio.removeEventListener('playing', this._handlePlaying);\n this._audio.removeEventListener('ended', this._handleEnded);\n this._audio = null;\n }\n }\n\n /**\n * Handles global keyboard events for selecting or toggling multiple-choice answers.\n * Maps keys (1-9, 0, a-j, A-J) to choices and updates the session state accordingly.\n * Ensures valid key presses toggle or select the appropriate choice based on the model.\n */\n handleKeyDown(event) {\n if (!this._model || !this._session) {\n return;\n }\n\n const keyToIndex = (key) => {\n const numOffset = key >= '1' && key <= '9' ? key - '1' : key === '0' ? 9 : -1;\n const letterOffset = /^[a-jA-J]$/.test(key) ? key.toLowerCase().charCodeAt(0) - 'a'.charCodeAt(0) : -1;\n return numOffset >= 0 ? numOffset : letterOffset;\n };\n\n const choiceIndex = keyToIndex(event.key);\n\n if (choiceIndex === undefined || choiceIndex <= -1 || choiceIndex >= this._model.choices?.length) {\n return;\n }\n\n const currentValue = this._session.value || [];\n const choiceId = this._model.choices[choiceIndex].value;\n\n const newValue = {\n value: choiceId,\n selected: !currentValue.includes(choiceId),\n selector: 'Keyboard',\n };\n\n this._onChange(newValue);\n }\n}\n"],"file":"index.js"}
package/module/element.js CHANGED
@@ -13101,6 +13101,27 @@ const ReactDOM = _dll_react_dom;
13101
13101
  const {debounce: debounce} = _dll_lodash;
13102
13102
  const debug = _dll_debug;
13103
13103
  const {renderMath: renderMath} = _dll_pie_lib__pie_toolbox_math_rendering;
13104
+ function _optionalChain(ops) {
13105
+ let lastAccessLHS = undefined;
13106
+ let value = ops[0];
13107
+ let i = 1;
13108
+ while (i < ops.length) {
13109
+ const op = ops[i];
13110
+ const fn = ops[i + 1];
13111
+ i += 2;
13112
+ if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) {
13113
+ return undefined;
13114
+ }
13115
+ if (op === 'access' || op === 'optionalAccess') {
13116
+ lastAccessLHS = value;
13117
+ value = fn(value);
13118
+ } else if (op === 'call' || op === 'optionalCall') {
13119
+ value = fn((...args) => value.call(lastAccessLHS, ...args));
13120
+ lastAccessLHS = undefined;
13121
+ }
13122
+ }
13123
+ return value;
13124
+ }
13104
13125
  const log = debug('pie-ui:multiple-choice');
13105
13126
  const isComplete = (session, model, audioComplete) => {
13106
13127
  const {autoplayAudioEnabled, completeAudioEnabled} = model || ({});
@@ -13259,6 +13280,10 @@ class MultipleChoice extends HTMLElement {
13259
13280
  audio.removeEventListener('ended', handleEnded);
13260
13281
  };
13261
13282
  audio.addEventListener('ended', handleEnded);
13283
+ this._audio = audio;
13284
+ this._handlePlaying = handlePlaying;
13285
+ this._handleEnded = handleEnded;
13286
+ this._enableAudio = enableAudio;
13262
13287
  observer.disconnect();
13263
13288
  }
13264
13289
  });
@@ -13279,6 +13304,12 @@ class MultipleChoice extends HTMLElement {
13279
13304
  window.removeEventListener('keydown', this._boundHandleKeyDown);
13280
13305
  this._keyboardEventsEnabled = false;
13281
13306
  }
13307
+ document.removeEventListener('click', this._enableAudio);
13308
+ if (this._audio) {
13309
+ this._audio.removeEventListener('playing', this._handlePlaying);
13310
+ this._audio.removeEventListener('ended', this._handleEnded);
13311
+ this._audio = null;
13312
+ }
13282
13313
  }
13283
13314
  handleKeyDown(event) {
13284
13315
  if (!this._model || !this._session) {
@@ -13290,7 +13321,7 @@ class MultipleChoice extends HTMLElement {
13290
13321
  return numOffset >= 0 ? numOffset : letterOffset;
13291
13322
  };
13292
13323
  const choiceIndex = keyToIndex(event.key);
13293
- if (choiceIndex === undefined || choiceIndex >= this._model.choices.length) {
13324
+ if (choiceIndex === undefined || choiceIndex <= -1 || choiceIndex >= _optionalChain([this, 'access', _ => _._model, 'access', _2 => _2.choices, 'optionalAccess', _3 => _3.length])) {
13294
13325
  return;
13295
13326
  }
13296
13327
  const currentValue = this._session.value || [];
package/module/index.html CHANGED
@@ -2,7 +2,7 @@
2
2
  <!doctype html>
3
3
  <html>
4
4
  <head>
5
- <title>@pie-element/multiple-choice@9.12.7</title>
5
+ <title>@pie-element/multiple-choice@9.13.0</title>
6
6
  <script
7
7
  type="module"
8
8
  src="https://cdn.jsdelivr.net/npm/@pslb/demo-el@^1.0.0/dist/demo-el/demo-el.esm.js"></script>
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pie-element/multiple-choice",
3
- "version": "9.12.7",
3
+ "version": "9.13.0",
4
4
  "modules": [
5
5
  {
6
6
  "name": "@pie-lib/pie-toolbox-math-rendering-module",
package/module/print.html CHANGED
@@ -2,7 +2,7 @@
2
2
  <!doctype html>
3
3
  <html>
4
4
  <head>
5
- <title>@pie-element/multiple-choice@9.12.7</title>
5
+ <title>@pie-element/multiple-choice@9.13.0</title>
6
6
  <link
7
7
  href="https://fonts.googleapis.com/css?family=Roboto&display=swap"
8
8
  rel="stylesheet"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pie-element/multiple-choice",
3
3
  "repository": "pie-framework/pie-elements",
4
- "version": "9.13.0",
4
+ "version": "9.13.1-next.11+d030a51b5",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -19,7 +19,7 @@
19
19
  "react-test-renderer": "^16.3.2",
20
20
  "react-transition-group": "^2.3.1"
21
21
  },
22
- "gitHead": "1abc2ce54f9ccb41022d4caa592a084a6ebb235c",
22
+ "gitHead": "d030a51b53d7c9188f667389ee6dbac0950ede47",
23
23
  "scripts": {
24
24
  "postpublish": "../../scripts/postpublish"
25
25
  },