@pie-element/image-cloze-association 10.3.0-beta.0 → 10.3.0

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/root.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"root.js","names":["_react","_interopRequireDefault","require","_propTypes","_core","_drag","_reactTransitionGroup","_renderUi","_styles","_NotInterested","_correctAnswerToggle","_translator","_lodashEs","_imageContainer","_interactiveSection","_possibleResponses2","_utilsCorrectness","_possibleResponse","translator","Translator","generateId","Math","random","toString","substring","Date","getTime","StyledUiLayout","styled","UiLayout","color","text","backgroundColor","background","position","maxWidth","height","StyledTeacherInstructions","Collapsible","theme","marginBottom","spacing","StyledRationale","marginTop","ImageClozeAssociationComponent","React","Component","constructor","props","_defineProperty2","default","event","active","data","current","setState","draggingElement","isValidDrop","over","model","duplicateResponses","draggedItem","responseArea","containerIndex","undefined","shouldDisableAnimation","id","value","handleOnAnswerRemove","handleOnAnswerSelect","state","imgRegex","containsImage","test","createElement","key","canDrag","onDragBegin","isOverlay","containerStyle","answerChoiceTransparency","opacity","padding","margin","possibleResponses","answer","filter","response","responseContainerIndex","updateAnswer","answers","maxResponsePerZone","answersToStore","answersInThisContainer","answersInOtherContainers","forEach","a","push","length","shiftedItem","shift","maxResponsePerZoneWarning","Array","isArray","filterPossibleAnswers","shouldNotPushInPossibleResponses","showCorrect","responseContainers","session","possibleResponsesWithIds","map","item","index","groupedAnswers","groupBy","limitedAnswers","flatMap","grp","slice","possibleResponsesFiltered","find","render","disabled","extraCSSRules","image","responseAreaFill","stimulus","responseCorrect","validation","teacherInstructions","prompt","autoplayAudioEnabled","showDashedBorder","mode","rationale","language","uiStyle","responseContainerPadding","imageDropTargetPadding","fontSizeFactor","customAudioButton","isEvaluateMode","showToggle","possibilityListPosition","isVertical","validResponse","correctAnswers","showRationale","hasText","hasMedia","showTeacherInstructions","container","i","images","v","isCorrect","warningMessage","t","lng","count","answersToShow","getAnswersCorrectness","getUnansweredAnswers","sharedImageProps","onAnswerSelect","onDragAnswerBegin","onDragStart","onDragAnswerEnd","onDragEnd","renderImage","_extends2","renderPossibleResponses","Fragment","WarningInfo","message","onAnswerRemove","customStyle","minWidth","width","minHeight","DragProvider","labels","hidden","visible","PreviewPrompt","className","show","toggled","onToggle","toggleCorrect","DragOverlay","dropAnimation","renderDragOverlay","exports","WarningContainer","display","alignItems","WarningMessage","paddingLeft","userSelect","nodeRef","useRef","TransitionGroup","CSSTransition","classNames","timeout","ref","fontSize","dangerouslySetInnerHTML","__html","propTypes","PropTypes","string","object","isRequired","func","_default"],"sources":["../src/root.jsx"],"sourcesContent":["import React from 'react';\nimport PropTypes from 'prop-types';\nimport { DragOverlay } from '@dnd-kit/core';\nimport { DragProvider } from '@pie-lib/drag';\nimport { CSSTransition, TransitionGroup } from 'react-transition-group';\nimport { color, Collapsible, PreviewPrompt, UiLayout, hasText, hasMedia } from '@pie-lib/render-ui';\nimport { styled } from '@mui/material/styles';\nimport NotInterestedIcon from '@mui/icons-material/NotInterested';\nimport CorrectAnswerToggle from '@pie-lib/correct-answer-toggle';\nimport Translator from '@pie-lib/translator';\nimport { flatMap, groupBy } from 'lodash-es';\n\nconst { translator } = Translator;\nimport Image from './image-container';\nimport InteractiveSection from './interactive-section';\nimport PossibleResponses from './possible-responses';\nimport { getUnansweredAnswers, getAnswersCorrectness } from './utils-correctness';\nimport PossibleResponse from './possible-response';\n\nconst generateId = () => Math.random().toString(36).substring(2) + new Date().getTime().toString(36);\n\nconst StyledUiLayout = styled(UiLayout)({\n color: color.text(),\n backgroundColor: color.background(),\n position: 'relative',\n '& img': {\n maxWidth: '100%',\n height: 'auto',\n },\n});\n\nconst StyledTeacherInstructions = styled(Collapsible)(({ theme }) => ({\n marginBottom: theme.spacing(2),\n}));\n\nconst StyledRationale = styled(Collapsible)(({ theme }) => ({\n marginTop: theme.spacing(2),\n}));\n\nexport class ImageClozeAssociationComponent extends React.Component {\n constructor(props) {\n super(props);\n const {\n model: { possibleResponses, responseContainers, duplicateResponses, maxResponsePerZone },\n session,\n } = props;\n let { answers } = session || {};\n // set id for each possible response\n const possibleResponsesWithIds = (possibleResponses || []).map((item, index) => ({\n value: item,\n id: `${index}`,\n }));\n\n let groupedAnswers = groupBy(answers || [], 'containerIndex');\n // keep only last maxResponsePerZone answers for each zone\n let limitedAnswers = flatMap(groupedAnswers, (grp) => grp.slice(-(maxResponsePerZone || 1)));\n answers = limitedAnswers\n // set id for each answer\n .map((answer, index) => ({ ...answer, id: `${index}` }))\n // return only answer which have a valid container index\n .filter((answer) => answer.containerIndex < responseContainers.length);\n\n const possibleResponsesFiltered = possibleResponsesWithIds.filter(\n (response) => !answers.find((answer) => answer.value === response.value),\n );\n this.state = {\n answers: answers || [],\n draggingElement: { id: '', value: '' },\n possibleResponses: duplicateResponses ? possibleResponsesWithIds : possibleResponsesFiltered,\n // set id for each response containers\n responseContainers: (responseContainers || []).map((item, index) => ({\n index,\n ...item,\n id: `${index}`,\n })),\n maxResponsePerZone: maxResponsePerZone || 1,\n showCorrect: false,\n isValidDrop: false,\n };\n }\n\n onDragStart = (event) => {\n const { active } = event;\n\n if (active?.data?.current) {\n this.setState({\n draggingElement: active.data.current,\n isValidDrop: false,\n });\n }\n };\n\n onDragEnd = (event) => {\n const { active, over } = event;\n const { model } = this.props;\n const { duplicateResponses } = model || {};\n\n // Check if drop is valid\n const draggedItem = active?.data?.current;\n const responseArea = over?.data?.current;\n const isValidDrop =\n over &&\n active &&\n draggedItem &&\n responseArea &&\n responseArea.containerIndex !== undefined;\n\n const shouldDisableAnimation = isValidDrop && duplicateResponses;\n\n this.setState({\n draggingElement: { id: '', value: '' },\n isValidDrop: shouldDisableAnimation,\n });\n\n if (!over || !active) {\n return;\n }\n\n if (!draggedItem) {\n return;\n }\n\n if (over.id === 'ica-board') {\n this.handleOnAnswerRemove(draggedItem);\n return;\n }\n\n if (responseArea) {\n this.handleOnAnswerSelect(draggedItem, responseArea.containerIndex);\n }\n };\n\n renderDragOverlay = () => {\n const { draggingElement } = this.state;\n const { model } = this.props;\n\n if (!draggingElement.id) return null;\n\n // check if the response contains an image\n const imgRegex = /<img[^>]+src=\"([^\">]+)\"/;\n const containsImage = imgRegex.test(draggingElement.value);\n\n return (\n <PossibleResponse\n key={draggingElement.id}\n canDrag={false}\n data={draggingElement}\n onDragBegin={() => {}}\n isOverlay\n containerStyle={{\n ...(model.answerChoiceTransparency ? { opacity: '0.8' } : {}),\n ...(!containsImage ? { padding: '0 10px', margin: '4px 6px !important' } : {}),\n }}\n />\n );\n };\n\n filterPossibleAnswers = (possibleResponses, answer) =>\n possibleResponses.filter((response) => response.value !== answer.value);\n\n handleOnAnswerSelect = (answer, responseContainerIndex) => {\n const {\n model: { duplicateResponses },\n updateAnswer,\n } = this.props;\n const { answers, maxResponsePerZone } = this.state;\n let { possibleResponses } = this.state;\n let answersToStore;\n\n const answersInThisContainer = [];\n const answersInOtherContainers = [];\n\n answers.forEach((a) => {\n if (a.containerIndex === responseContainerIndex) {\n answersInThisContainer.push(a);\n } else {\n answersInOtherContainers.push(a);\n }\n });\n\n if (maxResponsePerZone === answersInThisContainer.length) {\n const shiftedItem = answersInThisContainer[0];\n if (maxResponsePerZone === 1) {\n answersInThisContainer.shift(); // FIFO\n } else {\n this.setState({ maxResponsePerZoneWarning: true });\n return;\n }\n\n // if duplicates are not allowed, make sure to put the shifted value back in possible responses\n if (!duplicateResponses) {\n possibleResponses = Array.isArray(possibleResponses) ? possibleResponses : [];\n\n possibleResponses.push({\n ...shiftedItem,\n containerIndex: undefined,\n id: shiftedItem.id || generateId(),\n });\n }\n\n // answers will be:\n // + shifted answers for the current container\n // + if duplicatesAllowed, all the other answers from other containers\n // else: all the answers from other containers that are not having the same value\n // + new answer\n answersToStore = [\n ...answersInThisContainer, // shifted\n // TODO allow duplicates case Question: should we remove answer from a container if dragged to another container?\n // if yes, this should do it: add a.id !== answer.id instead of 'true'\n ...answersInOtherContainers.filter((a) => (duplicateResponses ? true : a.value !== answer.value)), // un-shifted\n {\n ...answer,\n containerIndex: responseContainerIndex,\n ...(duplicateResponses ? { id: generateId() } : {}),\n },\n ];\n } else {\n // answers will be:\n // + if duplicatesAllowed, all the other answers, except the one that was dragged\n // else: all the answers that are not having the same value\n // + new answer\n answersToStore = [\n // TODO allow duplicates case Question: should we remove answer from a container if dragged to another container?\n // if yes, this should do it: add a.id !== answer.id instead of 'true'\n ...answers.filter((a) => (duplicateResponses ? a.id !== answer.id : a.value !== answer.value)),\n {\n ...answer,\n containerIndex: responseContainerIndex,\n ...(duplicateResponses ? { id: generateId() } : {}),\n },\n ];\n }\n this.setState({\n maxResponsePerZoneWarning: false,\n answers: answersToStore,\n possibleResponses:\n // for single response per container remove answer from possible responses\n duplicateResponses ? possibleResponses : this.filterPossibleAnswers(possibleResponses, answer),\n });\n updateAnswer(answersToStore);\n };\n\n handleOnAnswerRemove = (answer) => {\n const {\n model: { duplicateResponses },\n updateAnswer,\n } = this.props;\n const { answers, possibleResponses } = this.state;\n const answersToStore = answers.filter((a) => a.id !== answer.id);\n const shouldNotPushInPossibleResponses = answer.containerIndex === undefined; // don't duplicate possible responses\n\n this.setState({\n maxResponsePerZoneWarning: false,\n answers: answersToStore,\n // push back into possible responses the removed answer if responses cannot be duplicated\n possibleResponses:\n duplicateResponses || shouldNotPushInPossibleResponses\n ? possibleResponses\n : [\n ...possibleResponses,\n {\n ...answer,\n containerIndex: undefined,\n },\n ],\n });\n updateAnswer(answersToStore);\n };\n\n toggleCorrect = (showCorrect) => this.setState({ showCorrect });\n\n render() {\n const {\n model: {\n disabled,\n duplicateResponses,\n extraCSSRules,\n image,\n responseAreaFill,\n stimulus,\n responseCorrect,\n validation,\n teacherInstructions,\n prompt,\n autoplayAudioEnabled,\n showDashedBorder,\n mode,\n rationale,\n language,\n uiStyle = {},\n answerChoiceTransparency,\n responseContainerPadding,\n imageDropTargetPadding,\n fontSizeFactor,\n customAudioButton,\n },\n } = this.props;\n const {\n answers,\n draggingElement,\n possibleResponses,\n responseContainers,\n maxResponsePerZone,\n maxResponsePerZoneWarning,\n showCorrect,\n isValidDrop,\n } = this.state;\n const isEvaluateMode = mode === 'evaluate';\n const showToggle = isEvaluateMode && !responseCorrect;\n const { possibilityListPosition = 'bottom' } = uiStyle || {};\n const isVertical = possibilityListPosition === 'left' || possibilityListPosition === 'right';\n\n const { validResponse } = validation || {};\n const correctAnswers = [];\n const showRationale = rationale && (hasText(rationale) || hasMedia(rationale));\n const showTeacherInstructions =\n teacherInstructions && (hasText(teacherInstructions) || hasMedia(teacherInstructions));\n\n if (validResponse) {\n (validResponse.value || []).forEach((container, i) => {\n (container.images || []).forEach((v) => {\n correctAnswers.push({\n value: v,\n containerIndex: i,\n isCorrect: true,\n });\n });\n });\n }\n\n const warningMessage = translator.t('imageClozeAssociation.reachedLimit_other', {\n lng: language,\n count: maxResponsePerZone,\n });\n\n let answersToShow =\n responseCorrect !== undefined ? getAnswersCorrectness(answers, validation, duplicateResponses) : answers;\n\n if (responseCorrect === false && maxResponsePerZone === 1) {\n answersToShow = [...answersToShow, ...getUnansweredAnswers(answersToShow, validation)];\n }\n\n const sharedImageProps = {\n draggingElement,\n duplicateResponses,\n image,\n onAnswerSelect: this.handleOnAnswerSelect,\n onDragAnswerBegin: this.onDragStart,\n onDragAnswerEnd: this.onDragEnd,\n responseContainers,\n showDashedBorder,\n responseAreaFill,\n responseContainerPadding,\n imageDropTargetPadding,\n maxResponsePerZone,\n };\n\n const renderImage = () => (\n <Image\n {...sharedImageProps}\n canDrag={showCorrect && showToggle ? false : !disabled}\n answers={showCorrect && showToggle ? correctAnswers : answersToShow}\n answerChoiceTransparency={!(showCorrect && showToggle) ? answerChoiceTransparency : undefined}\n />\n );\n\n const renderPossibleResponses = () => {\n if (showCorrect && showToggle) return null;\n\n return (\n <React.Fragment>\n {maxResponsePerZoneWarning && <WarningInfo message={warningMessage} />}\n <PossibleResponses\n canDrag={!disabled}\n data={possibleResponses}\n onAnswerRemove={this.handleOnAnswerRemove}\n onDragBegin={this.onDragStart}\n answerChoiceTransparency={answerChoiceTransparency}\n customStyle={{\n minWidth: isVertical ? '130px' : image?.width || 'fit-content',\n }}\n isVertical={isVertical}\n minHeight={isVertical ? image?.height : undefined}\n />\n </React.Fragment>\n );\n };\n\n return (\n <DragProvider onDragStart={this.onDragStart} onDragEnd={this.onDragEnd}>\n <StyledUiLayout extraCSSRules={extraCSSRules} id={'main-container'} fontSizeFactor={fontSizeFactor}>\n {showTeacherInstructions && (\n <StyledTeacherInstructions\n labels={{\n hidden: 'Show Teacher Instructions',\n visible: 'Hide Teacher Instructions',\n }}\n >\n <PreviewPrompt prompt={teacherInstructions} />\n </StyledTeacherInstructions>\n )}\n\n <PreviewPrompt\n className=\"prompt\"\n prompt={prompt}\n autoplayAudioEnabled={autoplayAudioEnabled}\n customAudioButton={customAudioButton}\n />\n\n <PreviewPrompt prompt={stimulus} />\n\n <CorrectAnswerToggle\n show={showToggle}\n toggled={showCorrect}\n onToggle={this.toggleCorrect}\n language={language}\n />\n\n <InteractiveSection responseCorrect={showCorrect && showToggle ? true : responseCorrect} uiStyle={uiStyle}>\n {renderImage()}\n {renderPossibleResponses()}\n </InteractiveSection>\n\n {showRationale && (\n <StyledRationale\n labels={{\n hidden: 'Show Rationale',\n visible: 'Hide Rationale',\n }}\n >\n <PreviewPrompt prompt={rationale} />\n </StyledRationale>\n )}\n </StyledUiLayout>\n {/* Disable drop animation for valid drops to prevent visual snap-back */}\n {/* Keep default animation for invalid drops to show visual feedback */}\n <DragOverlay dropAnimation={isValidDrop ? null : undefined}>\n {this.renderDragOverlay()}\n </DragOverlay>\n </DragProvider>\n );\n }\n}\n\nconst WarningContainer = styled('div')(({ theme }) => ({\n margin: `0 ${theme.spacing(2)}`,\n backgroundColor: '#dddddd',\n padding: theme.spacing(1),\n display: 'flex',\n alignItems: 'center',\n '& svg': {\n height: '30px',\n },\n '& h1': {\n padding: '0px',\n margin: '0px',\n },\n}));\n\nconst WarningMessage = styled('span')(({ theme }) => ({\n paddingLeft: theme.spacing(0.5),\n userSelect: 'none',\n}));\n\nconst WarningInfo = ({ message }) => {\n const nodeRef = React.useRef(null);\n\n return (\n <TransitionGroup>\n <CSSTransition classNames={'fb'} key=\"fb\" timeout={300} nodeRef={nodeRef}>\n <WarningContainer ref={nodeRef} key=\"panel\">\n <NotInterestedIcon color={'secondary'} fontSize={'small'} />\n <WarningMessage dangerouslySetInnerHTML={{ __html: message }} />\n </WarningContainer>\n </CSSTransition>\n </TransitionGroup>\n );\n};\n\nWarningInfo.propTypes = {\n message: PropTypes.string,\n};\n\nImageClozeAssociationComponent.propTypes = {\n model: PropTypes.object.isRequired,\n session: PropTypes.object,\n updateAnswer: PropTypes.func.isRequired,\n};\n\nexport default ImageClozeAssociationComponent;\n"],"mappings":";;;;;;;;;AAAA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,UAAA,GAAAF,sBAAA,CAAAC,OAAA;AACA,IAAAE,KAAA,GAAAF,OAAA;AACA,IAAAG,KAAA,GAAAH,OAAA;AACA,IAAAI,qBAAA,GAAAJ,OAAA;AACA,IAAAK,SAAA,GAAAL,OAAA;AACA,IAAAM,OAAA,GAAAN,OAAA;AACA,IAAAO,cAAA,GAAAR,sBAAA,CAAAC,OAAA;AACA,IAAAQ,oBAAA,GAAAT,sBAAA,CAAAC,OAAA;AACA,IAAAS,WAAA,GAAAV,sBAAA,CAAAC,OAAA;AACA,IAAAU,SAAA,GAAAV,OAAA;AAGA,IAAAW,eAAA,GAAAZ,sBAAA,CAAAC,OAAA;AACA,IAAAY,mBAAA,GAAAb,sBAAA,CAAAC,OAAA;AACA,IAAAa,mBAAA,GAAAd,sBAAA,CAAAC,OAAA;AACA,IAAAc,iBAAA,GAAAd,OAAA;AACA,IAAAe,iBAAA,GAAAhB,sBAAA,CAAAC,OAAA;AALA,MAAM;EAAEgB;AAAW,CAAC,GAAGC,mBAAU;AAOjC,MAAMC,UAAU,GAAGA,CAAA,KAAMC,IAAI,CAACC,MAAM,CAAC,CAAC,CAACC,QAAQ,CAAC,EAAE,CAAC,CAACC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAIC,IAAI,CAAC,CAAC,CAACC,OAAO,CAAC,CAAC,CAACH,QAAQ,CAAC,EAAE,CAAC;AAEpG,MAAMI,cAAc,GAAG,IAAAC,cAAM,EAACC,kBAAQ,CAAC,CAAC;EACtCC,KAAK,EAAEA,eAAK,CAACC,IAAI,CAAC,CAAC;EACnBC,eAAe,EAAEF,eAAK,CAACG,UAAU,CAAC,CAAC;EACnCC,QAAQ,EAAE,UAAU;EACpB,OAAO,EAAE;IACPC,QAAQ,EAAE,MAAM;IAChBC,MAAM,EAAE;EACV;AACF,CAAC,CAAC;AAEF,MAAMC,yBAAyB,GAAG,IAAAT,cAAM,EAACU,qBAAW,CAAC,CAAC,CAAC;EAAEC;AAAM,CAAC,MAAM;EACpEC,YAAY,EAAED,KAAK,CAACE,OAAO,CAAC,CAAC;AAC/B,CAAC,CAAC,CAAC;AAEH,MAAMC,eAAe,GAAG,IAAAd,cAAM,EAACU,qBAAW,CAAC,CAAC,CAAC;EAAEC;AAAM,CAAC,MAAM;EAC1DI,SAAS,EAAEJ,KAAK,CAACE,OAAO,CAAC,CAAC;AAC5B,CAAC,CAAC,CAAC;AAEI,MAAMG,8BAA8B,SAASC,cAAK,CAACC,SAAS,CAAC;EAClEC,WAAWA,CAACC,KAAK,EAAE;IACjB,KAAK,CAACA,KAAK,CAAC;IAAC,IAAAC,gBAAA,CAAAC,OAAA,uBAwCAC,KAAK,IAAK;MACvB,MAAM;QAAEC;MAAO,CAAC,GAAGD,KAAK;MAExB,IAAIC,MAAM,EAAEC,IAAI,EAAEC,OAAO,EAAE;QACzB,IAAI,CAACC,QAAQ,CAAC;UACZC,eAAe,EAAEJ,MAAM,CAACC,IAAI,CAACC,OAAO;UACpCG,WAAW,EAAE;QACf,CAAC,CAAC;MACJ;IACF,CAAC;IAAA,IAAAR,gBAAA,CAAAC,OAAA,qBAEYC,KAAK,IAAK;MACrB,MAAM;QAAEC,MAAM;QAAEM;MAAK,CAAC,GAAGP,KAAK;MAC9B,MAAM;QAAEQ;MAAM,CAAC,GAAG,IAAI,CAACX,KAAK;MAC5B,MAAM;QAAEY;MAAmB,CAAC,GAAGD,KAAK,IAAI,CAAC,CAAC;;MAE1C;MACA,MAAME,WAAW,GAAGT,MAAM,EAAEC,IAAI,EAAEC,OAAO;MACzC,MAAMQ,YAAY,GAAGJ,IAAI,EAAEL,IAAI,EAAEC,OAAO;MACxC,MAAMG,WAAW,GACfC,IAAI,IACJN,MAAM,IACNS,WAAW,IACXC,YAAY,IACZA,YAAY,CAACC,cAAc,KAAKC,SAAS;MAE3C,MAAMC,sBAAsB,GAAGR,WAAW,IAAIG,kBAAkB;MAEhE,IAAI,CAACL,QAAQ,CAAC;QACZC,eAAe,EAAE;UAAEU,EAAE,EAAE,EAAE;UAAEC,KAAK,EAAE;QAAG,CAAC;QACtCV,WAAW,EAAEQ;MACf,CAAC,CAAC;MAEF,IAAI,CAACP,IAAI,IAAI,CAACN,MAAM,EAAE;QACpB;MACF;MAEA,IAAI,CAACS,WAAW,EAAE;QAChB;MACF;MAEA,IAAIH,IAAI,CAACQ,EAAE,KAAK,WAAW,EAAE;QAC3B,IAAI,CAACE,oBAAoB,CAACP,WAAW,CAAC;QACtC;MACF;MAEA,IAAIC,YAAY,EAAE;QAChB,IAAI,CAACO,oBAAoB,CAACR,WAAW,EAAEC,YAAY,CAACC,cAAc,CAAC;MACrE;IACF,CAAC;IAAA,IAAAd,gBAAA,CAAAC,OAAA,6BAEmB,MAAM;MACxB,MAAM;QAAEM;MAAgB,CAAC,GAAG,IAAI,CAACc,KAAK;MACtC,MAAM;QAAEX;MAAM,CAAC,GAAG,IAAI,CAACX,KAAK;MAE5B,IAAI,CAACQ,eAAe,CAACU,EAAE,EAAE,OAAO,IAAI;;MAEpC;MACA,MAAMK,QAAQ,GAAG,yBAAyB;MAC1C,MAAMC,aAAa,GAAGD,QAAQ,CAACE,IAAI,CAACjB,eAAe,CAACW,KAAK,CAAC;MAE1D,oBACEnE,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAACzD,iBAAA,CAAAiC,OAAgB;QACfyB,GAAG,EAAEnB,eAAe,CAACU,EAAG;QACxBU,OAAO,EAAE,KAAM;QACfvB,IAAI,EAAEG,eAAgB;QACtBqB,WAAW,EAAEA,CAAA,KAAM,CAAC,CAAE;QACtBC,SAAS;QACTC,cAAc,EAAE;UACd,IAAIpB,KAAK,CAACqB,wBAAwB,GAAG;YAAEC,OAAO,EAAE;UAAM,CAAC,GAAG,CAAC,CAAC,CAAC;UAC7D,IAAI,CAACT,aAAa,GAAG;YAAEU,OAAO,EAAE,QAAQ;YAAEC,MAAM,EAAE;UAAqB,CAAC,GAAG,CAAC,CAAC;QAC/E;MAAE,CACH,CAAC;IAEN,CAAC;IAAA,IAAAlC,gBAAA,CAAAC,OAAA,iCAEuB,CAACkC,iBAAiB,EAAEC,MAAM,KAChDD,iBAAiB,CAACE,MAAM,CAAEC,QAAQ,IAAKA,QAAQ,CAACpB,KAAK,KAAKkB,MAAM,CAAClB,KAAK,CAAC;IAAA,IAAAlB,gBAAA,CAAAC,OAAA,gCAElD,CAACmC,MAAM,EAAEG,sBAAsB,KAAK;MACzD,MAAM;QACJ7B,KAAK,EAAE;UAAEC;QAAmB,CAAC;QAC7B6B;MACF,CAAC,GAAG,IAAI,CAACzC,KAAK;MACd,MAAM;QAAE0C,OAAO;QAAEC;MAAmB,CAAC,GAAG,IAAI,CAACrB,KAAK;MAClD,IAAI;QAAEc;MAAkB,CAAC,GAAG,IAAI,CAACd,KAAK;MACtC,IAAIsB,cAAc;MAElB,MAAMC,sBAAsB,GAAG,EAAE;MACjC,MAAMC,wBAAwB,GAAG,EAAE;MAEnCJ,OAAO,CAACK,OAAO,CAAEC,CAAC,IAAK;QACrB,IAAIA,CAAC,CAACjC,cAAc,KAAKyB,sBAAsB,EAAE;UAC/CK,sBAAsB,CAACI,IAAI,CAACD,CAAC,CAAC;QAChC,CAAC,MAAM;UACLF,wBAAwB,CAACG,IAAI,CAACD,CAAC,CAAC;QAClC;MACF,CAAC,CAAC;MAEF,IAAIL,kBAAkB,KAAKE,sBAAsB,CAACK,MAAM,EAAE;QACxD,MAAMC,WAAW,GAAGN,sBAAsB,CAAC,CAAC,CAAC;QAC7C,IAAIF,kBAAkB,KAAK,CAAC,EAAE;UAC5BE,sBAAsB,CAACO,KAAK,CAAC,CAAC,CAAC,CAAC;QAClC,CAAC,MAAM;UACL,IAAI,CAAC7C,QAAQ,CAAC;YAAE8C,yBAAyB,EAAE;UAAK,CAAC,CAAC;UAClD;QACF;;QAEA;QACA,IAAI,CAACzC,kBAAkB,EAAE;UACvBwB,iBAAiB,GAAGkB,KAAK,CAACC,OAAO,CAACnB,iBAAiB,CAAC,GAAGA,iBAAiB,GAAG,EAAE;UAE7EA,iBAAiB,CAACa,IAAI,CAAC;YACrB,GAAGE,WAAW;YACdpC,cAAc,EAAEC,SAAS;YACzBE,EAAE,EAAEiC,WAAW,CAACjC,EAAE,IAAI9C,UAAU,CAAC;UACnC,CAAC,CAAC;QACJ;;QAEA;QACA;QACA;QACA;QACA;QACAwE,cAAc,GAAG,CACf,GAAGC,sBAAsB;QAAE;QAC3B;QACA;QACA,GAAGC,wBAAwB,CAACR,MAAM,CAAEU,CAAC,IAAMpC,kBAAkB,GAAG,IAAI,GAAGoC,CAAC,CAAC7B,KAAK,KAAKkB,MAAM,CAAClB,KAAM,CAAC;QAAE;QACnG;UACE,GAAGkB,MAAM;UACTtB,cAAc,EAAEyB,sBAAsB;UACtC,IAAI5B,kBAAkB,GAAG;YAAEM,EAAE,EAAE9C,UAAU,CAAC;UAAE,CAAC,GAAG,CAAC,CAAC;QACpD,CAAC,CACF;MACH,CAAC,MAAM;QACL;QACA;QACA;QACA;QACAwE,cAAc,GAAG;QACf;QACA;QACA,GAAGF,OAAO,CAACJ,MAAM,CAAEU,CAAC,IAAMpC,kBAAkB,GAAGoC,CAAC,CAAC9B,EAAE,KAAKmB,MAAM,CAACnB,EAAE,GAAG8B,CAAC,CAAC7B,KAAK,KAAKkB,MAAM,CAAClB,KAAM,CAAC,EAC9F;UACE,GAAGkB,MAAM;UACTtB,cAAc,EAAEyB,sBAAsB;UACtC,IAAI5B,kBAAkB,GAAG;YAAEM,EAAE,EAAE9C,UAAU,CAAC;UAAE,CAAC,GAAG,CAAC,CAAC;QACpD,CAAC,CACF;MACH;MACA,IAAI,CAACmC,QAAQ,CAAC;QACZ8C,yBAAyB,EAAE,KAAK;QAChCX,OAAO,EAAEE,cAAc;QACvBR,iBAAiB;QACf;QACAxB,kBAAkB,GAAGwB,iBAAiB,GAAG,IAAI,CAACoB,qBAAqB,CAACpB,iBAAiB,EAAEC,MAAM;MACjG,CAAC,CAAC;MACFI,YAAY,CAACG,cAAc,CAAC;IAC9B,CAAC;IAAA,IAAA3C,gBAAA,CAAAC,OAAA,gCAEuBmC,MAAM,IAAK;MACjC,MAAM;QACJ1B,KAAK,EAAE;UAAEC;QAAmB,CAAC;QAC7B6B;MACF,CAAC,GAAG,IAAI,CAACzC,KAAK;MACd,MAAM;QAAE0C,OAAO;QAAEN;MAAkB,CAAC,GAAG,IAAI,CAACd,KAAK;MACjD,MAAMsB,cAAc,GAAGF,OAAO,CAACJ,MAAM,CAAEU,CAAC,IAAKA,CAAC,CAAC9B,EAAE,KAAKmB,MAAM,CAACnB,EAAE,CAAC;MAChE,MAAMuC,gCAAgC,GAAGpB,MAAM,CAACtB,cAAc,KAAKC,SAAS,CAAC,CAAC;;MAE9E,IAAI,CAACT,QAAQ,CAAC;QACZ8C,yBAAyB,EAAE,KAAK;QAChCX,OAAO,EAAEE,cAAc;QACvB;QACAR,iBAAiB,EACfxB,kBAAkB,IAAI6C,gCAAgC,GAClDrB,iBAAiB,GACjB,CACE,GAAGA,iBAAiB,EACpB;UACE,GAAGC,MAAM;UACTtB,cAAc,EAAEC;QAClB,CAAC;MAEX,CAAC,CAAC;MACFyB,YAAY,CAACG,cAAc,CAAC;IAC9B,CAAC;IAAA,IAAA3C,gBAAA,CAAAC,OAAA,yBAEgBwD,WAAW,IAAK,IAAI,CAACnD,QAAQ,CAAC;MAAEmD;IAAY,CAAC,CAAC;IAnO7D,MAAM;MACJ/C,KAAK,EAAE;QAAEyB,iBAAiB,EAAjBA,kBAAiB;QAAEuB,kBAAkB;QAAE/C,kBAAkB,EAAlBA,mBAAkB;QAAE+B,kBAAkB,EAAlBA;MAAmB,CAAC;MACxFiB;IACF,CAAC,GAAG5D,KAAK;IACT,IAAI;MAAE0C,OAAO,EAAPA;IAAQ,CAAC,GAAGkB,OAAO,IAAI,CAAC,CAAC;IAC/B;IACA,MAAMC,wBAAwB,GAAG,CAACzB,kBAAiB,IAAI,EAAE,EAAE0B,GAAG,CAAC,CAACC,IAAI,EAAEC,KAAK,MAAM;MAC/E7C,KAAK,EAAE4C,IAAI;MACX7C,EAAE,EAAE,GAAG8C,KAAK;IACd,CAAC,CAAC,CAAC;IAEH,IAAIC,cAAc,GAAG,IAAAC,iBAAO,EAACxB,QAAO,IAAI,EAAE,EAAE,gBAAgB,CAAC;IAC7D;IACA,IAAIyB,cAAc,GAAG,IAAAC,iBAAO,EAACH,cAAc,EAAGI,GAAG,IAAKA,GAAG,CAACC,KAAK,CAAC,EAAE3B,mBAAkB,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5FD,QAAO,GAAGyB;IACR;IAAA,CACCL,GAAG,CAAC,CAACzB,MAAM,EAAE2B,KAAK,MAAM;MAAE,GAAG3B,MAAM;MAAEnB,EAAE,EAAE,GAAG8C,KAAK;IAAG,CAAC,CAAC;IACvD;IAAA,CACC1B,MAAM,CAAED,MAAM,IAAKA,MAAM,CAACtB,cAAc,GAAG4C,kBAAkB,CAACT,MAAM,CAAC;IAExE,MAAMqB,yBAAyB,GAAGV,wBAAwB,CAACvB,MAAM,CAC9DC,QAAQ,IAAK,CAACG,QAAO,CAAC8B,IAAI,CAAEnC,MAAM,IAAKA,MAAM,CAAClB,KAAK,KAAKoB,QAAQ,CAACpB,KAAK,CACzE,CAAC;IACD,IAAI,CAACG,KAAK,GAAG;MACXoB,OAAO,EAAEA,QAAO,IAAI,EAAE;MACtBlC,eAAe,EAAE;QAAEU,EAAE,EAAE,EAAE;QAAEC,KAAK,EAAE;MAAG,CAAC;MACtCiB,iBAAiB,EAAExB,mBAAkB,GAAGiD,wBAAwB,GAAGU,yBAAyB;MAC5F;MACAZ,kBAAkB,EAAE,CAACA,kBAAkB,IAAI,EAAE,EAAEG,GAAG,CAAC,CAACC,IAAI,EAAEC,KAAK,MAAM;QACnEA,KAAK;QACL,GAAGD,IAAI;QACP7C,EAAE,EAAE,GAAG8C,KAAK;MACd,CAAC,CAAC,CAAC;MACHrB,kBAAkB,EAAEA,mBAAkB,IAAI,CAAC;MAC3Ce,WAAW,EAAE,KAAK;MAClBjD,WAAW,EAAE;IACf,CAAC;EACH;EAgMAgE,MAAMA,CAAA,EAAG;IACP,MAAM;MACJ9D,KAAK,EAAE;QACL+D,QAAQ;QACR9D,kBAAkB;QAClB+D,aAAa;QACbC,KAAK;QACLC,gBAAgB;QAChBC,QAAQ;QACRC,eAAe;QACfC,UAAU;QACVC,mBAAmB;QACnBC,MAAM;QACNC,oBAAoB;QACpBC,gBAAgB;QAChBC,IAAI;QACJC,SAAS;QACTC,QAAQ;QACRC,OAAO,GAAG,CAAC,CAAC;QACZxD,wBAAwB;QACxByD,wBAAwB;QACxBC,sBAAsB;QACtBC,cAAc;QACdC;MACF;IACF,CAAC,GAAG,IAAI,CAAC5F,KAAK;IACd,MAAM;MACJ0C,OAAO;MACPlC,eAAe;MACf4B,iBAAiB;MACjBuB,kBAAkB;MAClBhB,kBAAkB;MAClBU,yBAAyB;MACzBK,WAAW;MACXjD;IACF,CAAC,GAAG,IAAI,CAACa,KAAK;IACd,MAAMuE,cAAc,GAAGR,IAAI,KAAK,UAAU;IAC1C,MAAMS,UAAU,GAAGD,cAAc,IAAI,CAACd,eAAe;IACrD,MAAM;MAAEgB,uBAAuB,GAAG;IAAS,CAAC,GAAGP,OAAO,IAAI,CAAC,CAAC;IAC5D,MAAMQ,UAAU,GAAGD,uBAAuB,KAAK,MAAM,IAAIA,uBAAuB,KAAK,OAAO;IAE5F,MAAM;MAAEE;IAAc,CAAC,GAAGjB,UAAU,IAAI,CAAC,CAAC;IAC1C,MAAMkB,cAAc,GAAG,EAAE;IACzB,MAAMC,aAAa,GAAGb,SAAS,KAAK,IAAAc,iBAAO,EAACd,SAAS,CAAC,IAAI,IAAAe,kBAAQ,EAACf,SAAS,CAAC,CAAC;IAC9E,MAAMgB,uBAAuB,GAC3BrB,mBAAmB,KAAK,IAAAmB,iBAAO,EAACnB,mBAAmB,CAAC,IAAI,IAAAoB,kBAAQ,EAACpB,mBAAmB,CAAC,CAAC;IAExF,IAAIgB,aAAa,EAAE;MACjB,CAACA,aAAa,CAAC9E,KAAK,IAAI,EAAE,EAAE4B,OAAO,CAAC,CAACwD,SAAS,EAAEC,CAAC,KAAK;QACpD,CAACD,SAAS,CAACE,MAAM,IAAI,EAAE,EAAE1D,OAAO,CAAE2D,CAAC,IAAK;UACtCR,cAAc,CAACjD,IAAI,CAAC;YAClB9B,KAAK,EAAEuF,CAAC;YACR3F,cAAc,EAAEyF,CAAC;YACjBG,SAAS,EAAE;UACb,CAAC,CAAC;QACJ,CAAC,CAAC;MACJ,CAAC,CAAC;IACJ;IAEA,MAAMC,cAAc,GAAG1I,UAAU,CAAC2I,CAAC,CAAC,0CAA0C,EAAE;MAC9EC,GAAG,EAAEvB,QAAQ;MACbwB,KAAK,EAAEpE;IACT,CAAC,CAAC;IAEF,IAAIqE,aAAa,GACfjC,eAAe,KAAK/D,SAAS,GAAG,IAAAiG,uCAAqB,EAACvE,OAAO,EAAEsC,UAAU,EAAEpE,kBAAkB,CAAC,GAAG8B,OAAO;IAE1G,IAAIqC,eAAe,KAAK,KAAK,IAAIpC,kBAAkB,KAAK,CAAC,EAAE;MACzDqE,aAAa,GAAG,CAAC,GAAGA,aAAa,EAAE,GAAG,IAAAE,sCAAoB,EAACF,aAAa,EAAEhC,UAAU,CAAC,CAAC;IACxF;IAEA,MAAMmC,gBAAgB,GAAG;MACvB3G,eAAe;MACfI,kBAAkB;MAClBgE,KAAK;MACLwC,cAAc,EAAE,IAAI,CAAC/F,oBAAoB;MACzCgG,iBAAiB,EAAE,IAAI,CAACC,WAAW;MACnCC,eAAe,EAAE,IAAI,CAACC,SAAS;MAC/B7D,kBAAkB;MAClByB,gBAAgB;MAChBP,gBAAgB;MAChBY,wBAAwB;MACxBC,sBAAsB;MACtB/C;IACF,CAAC;IAED,MAAM8E,WAAW,GAAGA,CAAA,kBAClBzK,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAAC7D,eAAA,CAAAqC,OAAK,MAAAwH,SAAA,CAAAxH,OAAA,MACAiH,gBAAgB;MACpBvF,OAAO,EAAE8B,WAAW,IAAIoC,UAAU,GAAG,KAAK,GAAG,CAACpB,QAAS;MACvDhC,OAAO,EAAEgB,WAAW,IAAIoC,UAAU,GAAGI,cAAc,GAAGc,aAAc;MACpEhF,wBAAwB,EAAE,EAAE0B,WAAW,IAAIoC,UAAU,CAAC,GAAG9D,wBAAwB,GAAGhB;IAAU,EAC/F,CACF;IAED,MAAM2G,uBAAuB,GAAGA,CAAA,KAAM;MACpC,IAAIjE,WAAW,IAAIoC,UAAU,EAAE,OAAO,IAAI;MAE1C,oBACE9I,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAAC1E,MAAA,CAAAkD,OAAK,CAAC0H,QAAQ,QACZvE,yBAAyB,iBAAIrG,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAACmG,WAAW;QAACC,OAAO,EAAElB;MAAe,CAAE,CAAC,eACtE5J,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAAC3D,mBAAA,CAAAmC,OAAiB;QAChB0B,OAAO,EAAE,CAAC8C,QAAS;QACnBrE,IAAI,EAAE+B,iBAAkB;QACxB2F,cAAc,EAAE,IAAI,CAAC3G,oBAAqB;QAC1CS,WAAW,EAAE,IAAI,CAACyF,WAAY;QAC9BtF,wBAAwB,EAAEA,wBAAyB;QACnDgG,WAAW,EAAE;UACXC,QAAQ,EAAEjC,UAAU,GAAG,OAAO,GAAGpB,KAAK,EAAEsD,KAAK,IAAI;QACnD,CAAE;QACFlC,UAAU,EAAEA,UAAW;QACvBmC,SAAS,EAAEnC,UAAU,GAAGpB,KAAK,EAAExF,MAAM,GAAG4B;MAAU,CACnD,CACa,CAAC;IAErB,CAAC;IAED,oBACEhE,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAACrE,KAAA,CAAA+K,YAAY;MAACd,WAAW,EAAE,IAAI,CAACA,WAAY;MAACE,SAAS,EAAE,IAAI,CAACA;IAAU,gBACrExK,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAAC/C,cAAc;MAACgG,aAAa,EAAEA,aAAc;MAACzD,EAAE,EAAE,gBAAiB;MAACyE,cAAc,EAAEA;IAAe,GAChGW,uBAAuB,iBACtBtJ,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAACrC,yBAAyB;MACxBgJ,MAAM,EAAE;QACNC,MAAM,EAAE,2BAA2B;QACnCC,OAAO,EAAE;MACX;IAAE,gBAEFvL,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAACnE,SAAA,CAAAiL,aAAa;MAACtD,MAAM,EAAED;IAAoB,CAAE,CACpB,CAC5B,eAEDjI,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAACnE,SAAA,CAAAiL,aAAa;MACZC,SAAS,EAAC,QAAQ;MAClBvD,MAAM,EAAEA,MAAO;MACfC,oBAAoB,EAAEA,oBAAqB;MAC3CS,iBAAiB,EAAEA;IAAkB,CACtC,CAAC,eAEF5I,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAACnE,SAAA,CAAAiL,aAAa;MAACtD,MAAM,EAAEJ;IAAS,CAAE,CAAC,eAEnC9H,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAAChE,oBAAA,CAAAwC,OAAmB;MAClBwI,IAAI,EAAE5C,UAAW;MACjB6C,OAAO,EAAEjF,WAAY;MACrBkF,QAAQ,EAAE,IAAI,CAACC,aAAc;MAC7BtD,QAAQ,EAAEA;IAAS,CACpB,CAAC,eAEFvI,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAAC5D,mBAAA,CAAAoC,OAAkB;MAAC6E,eAAe,EAAErB,WAAW,IAAIoC,UAAU,GAAG,IAAI,GAAGf,eAAgB;MAACS,OAAO,EAAEA;IAAQ,GACvGiC,WAAW,CAAC,CAAC,EACbE,uBAAuB,CAAC,CACP,CAAC,EAEpBxB,aAAa,iBACZnJ,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAAChC,eAAe;MACd2I,MAAM,EAAE;QACNC,MAAM,EAAE,gBAAgB;QACxBC,OAAO,EAAE;MACX;IAAE,gBAEFvL,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAACnE,SAAA,CAAAiL,aAAa;MAACtD,MAAM,EAAEI;IAAU,CAAE,CACpB,CAEL,CAAC,eAGjBtI,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAACtE,KAAA,CAAA0L,WAAW;MAACC,aAAa,EAAEtI,WAAW,GAAG,IAAI,GAAGO;IAAU,GACxD,IAAI,CAACgI,iBAAiB,CAAC,CACb,CACD,CAAC;EAEnB;AACF;AAACC,OAAA,CAAArJ,8BAAA,GAAAA,8BAAA;AAED,MAAMsJ,gBAAgB,GAAG,IAAAtK,cAAM,EAAC,KAAK,CAAC,CAAC,CAAC;EAAEW;AAAM,CAAC,MAAM;EACrD4C,MAAM,EAAE,KAAK5C,KAAK,CAACE,OAAO,CAAC,CAAC,CAAC,EAAE;EAC/BT,eAAe,EAAE,SAAS;EAC1BkD,OAAO,EAAE3C,KAAK,CAACE,OAAO,CAAC,CAAC,CAAC;EACzB0J,OAAO,EAAE,MAAM;EACfC,UAAU,EAAE,QAAQ;EACpB,OAAO,EAAE;IACPhK,MAAM,EAAE;EACV,CAAC;EACD,MAAM,EAAE;IACN8C,OAAO,EAAE,KAAK;IACdC,MAAM,EAAE;EACV;AACF,CAAC,CAAC,CAAC;AAEH,MAAMkH,cAAc,GAAG,IAAAzK,cAAM,EAAC,MAAM,CAAC,CAAC,CAAC;EAAEW;AAAM,CAAC,MAAM;EACpD+J,WAAW,EAAE/J,KAAK,CAACE,OAAO,CAAC,GAAG,CAAC;EAC/B8J,UAAU,EAAE;AACd,CAAC,CAAC,CAAC;AAEH,MAAM1B,WAAW,GAAGA,CAAC;EAAEC;AAAQ,CAAC,KAAK;EACnC,MAAM0B,OAAO,GAAG3J,cAAK,CAAC4J,MAAM,CAAC,IAAI,CAAC;EAElC,oBACEzM,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAACpE,qBAAA,CAAAoM,eAAe,qBACd1M,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAACpE,qBAAA,CAAAqM,aAAa;IAACC,UAAU,EAAE,IAAK;IAACjI,GAAG,EAAC,IAAI;IAACkI,OAAO,EAAE,GAAI;IAACL,OAAO,EAAEA;EAAQ,gBACvExM,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAACwH,gBAAgB;IAACY,GAAG,EAAEN,OAAQ;IAAC7H,GAAG,EAAC;EAAO,gBACzC3E,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAACjE,cAAA,CAAAyC,OAAiB;IAACpB,KAAK,EAAE,WAAY;IAACiL,QAAQ,EAAE;EAAQ,CAAE,CAAC,eAC5D/M,MAAA,CAAAkD,OAAA,CAAAwB,aAAA,CAAC2H,cAAc;IAACW,uBAAuB,EAAE;MAAEC,MAAM,EAAEnC;IAAQ;EAAE,CAAE,CAC/C,CACL,CACA,CAAC;AAEtB,CAAC;AAEDD,WAAW,CAACqC,SAAS,GAAG;EACtBpC,OAAO,EAAEqC,kBAAS,CAACC;AACrB,CAAC;AAEDxK,8BAA8B,CAACsK,SAAS,GAAG;EACzCvJ,KAAK,EAAEwJ,kBAAS,CAACE,MAAM,CAACC,UAAU;EAClC1G,OAAO,EAAEuG,kBAAS,CAACE,MAAM;EACzB5H,YAAY,EAAE0H,kBAAS,CAACI,IAAI,CAACD;AAC/B,CAAC;AAAC,IAAAE,QAAA,GAAAvB,OAAA,CAAA/I,OAAA,GAEaN,8BAA8B","ignoreList":[]}
1
+ {"version":3,"file":"root.js","names":["_react","_interopRequireDefault","require","_propTypes","_core","_drag","_reactTransitionGroup","_renderUi","_styles","_NotInterested","_correctAnswerToggle","_translator","_lodashEs","_imageContainer","_interactiveSection","_possibleResponses2","_utilsCorrectness","_possibleResponse","_keyboardCoordinates","translator","Translator","generateId","Math","random","toString","substring","Date","getTime","CLICK_AFTER_DRAG_GUARD_MS","StyledUiLayout","styled","UiLayout","color","text","backgroundColor","background","position","maxWidth","height","StyledTeacherInstructions","Collapsible","theme","marginBottom","spacing","StyledRationale","marginTop","ImageClozeAssociationComponent","React","Component","constructor","props","_defineProperty2","default","event","active","data","current","setState","draggingElement","isValidDrop","selectedResponse","over","model","duplicateResponses","draggedItem","responseArea","containerIndex","undefined","shouldDisableAnimation","id","value","cancelSelection","lastDragEndAt","now","handleOnAnswerRemove","handleOnAnswerSelect","a","b","state","isSameResponse","document","dispatchEvent","KeyboardEvent","code","bubbles","cancelable","endAnyLiveKeyboardDrag","isClickSoonAfterDragEnd","toggleResponseSelection","placeSelectedResponse","imgRegex","containsImage","test","createElement","key","canDrag","onDragBegin","isOverlay","containerStyle","answerChoiceTransparency","opacity","padding","margin","possibleResponses","answer","filter","response","responseContainerIndex","updateAnswer","answers","maxResponsePerZone","answersToStore","answersInThisContainer","answersInOtherContainers","forEach","push","length","shiftedItem","shift","maxResponsePerZoneWarning","Array","isArray","filterPossibleAnswers","shouldNotPushInPossibleResponses","showCorrect","responseContainers","session","possibleResponsesWithIds","map","item","index","groupedAnswers","groupBy","limitedAnswers","flatMap","grp","slice","possibleResponsesFiltered","find","render","disabled","extraCSSRules","image","responseAreaFill","stimulus","responseCorrect","validation","teacherInstructions","prompt","autoplayAudioEnabled","showDashedBorder","mode","rationale","language","uiStyle","responseContainerPadding","imageDropTargetPadding","fontSizeFactor","customAudioButton","isEvaluateMode","showToggle","possibilityListPosition","isVertical","validResponse","correctAnswers","showRationale","hasText","hasMedia","showTeacherInstructions","container","i","images","v","isCorrect","warningMessage","t","lng","count","answersToShow","getAnswersCorrectness","getUnansweredAnswers","sharedImageProps","onAnswerSelect","onDragAnswerBegin","onDragStart","onDragAnswerEnd","onDragEnd","onSelectClick","onResponseClick","onPlacementClick","renderImage","_extends2","renderPossibleResponses","Fragment","WarningInfo","message","onAnswerRemove","customStyle","minWidth","width","minHeight","DragProvider","onDragCancel","keyboardCoordinateGetter","closestDroppableKeyboardCoordinates","keyboardCodes","start","cancel","end","labels","hidden","visible","PreviewPrompt","className","show","toggled","onToggle","toggleCorrect","DragOverlay","dropAnimation","renderDragOverlay","exports","WarningContainer","display","alignItems","WarningMessage","paddingLeft","userSelect","nodeRef","useRef","TransitionGroup","CSSTransition","classNames","timeout","ref","fontSize","dangerouslySetInnerHTML","__html","propTypes","PropTypes","string","object","isRequired","func","_default"],"sources":["../src/root.jsx"],"sourcesContent":["import React from 'react';\nimport PropTypes from 'prop-types';\nimport { DragOverlay } from '@dnd-kit/core';\nimport { DragProvider } from '@pie-lib/drag';\nimport { CSSTransition, TransitionGroup } from 'react-transition-group';\nimport { color, Collapsible, PreviewPrompt, UiLayout, hasText, hasMedia } from '@pie-lib/render-ui';\nimport { styled } from '@mui/material/styles';\nimport NotInterestedIcon from '@mui/icons-material/NotInterested';\nimport CorrectAnswerToggle from '@pie-lib/correct-answer-toggle';\nimport Translator from '@pie-lib/translator';\nimport { flatMap, groupBy } from 'lodash-es';\n\nconst { translator } = Translator;\nimport Image from './image-container';\nimport InteractiveSection from './interactive-section';\nimport PossibleResponses from './possible-responses';\nimport { getUnansweredAnswers, getAnswersCorrectness } from './utils-correctness';\nimport PossibleResponse from './possible-response';\nimport { closestDroppableKeyboardCoordinates } from './keyboard-coordinates';\n\nconst generateId = () => Math.random().toString(36).substring(2) + new Date().getTime().toString(36);\n\n// A click that lands right after a real drag gesture ends (pointer drag-and-drop, or\n// the browser's own synthetic click for a keyboard Space/Enter) must be ignored by the\n// click-to-select/click-to-place handlers below, or it would immediately reopen or\n// re-trigger a selection for a drag that just completed.\nconst CLICK_AFTER_DRAG_GUARD_MS = 250;\n\nconst StyledUiLayout = styled(UiLayout)({\n color: color.text(),\n backgroundColor: color.background(),\n position: 'relative',\n '& img': {\n maxWidth: '100%',\n height: 'auto',\n },\n});\n\nconst StyledTeacherInstructions = styled(Collapsible)(({ theme }) => ({\n marginBottom: theme.spacing(2),\n}));\n\nconst StyledRationale = styled(Collapsible)(({ theme }) => ({\n marginTop: theme.spacing(2),\n}));\n\nexport class ImageClozeAssociationComponent extends React.Component {\n constructor(props) {\n super(props);\n const {\n model: { possibleResponses, responseContainers, duplicateResponses, maxResponsePerZone },\n session,\n } = props;\n let { answers } = session || {};\n // set id for each possible response\n const possibleResponsesWithIds = (possibleResponses || []).map((item, index) => ({\n value: item,\n id: `${index}`,\n }));\n\n let groupedAnswers = groupBy(answers || [], 'containerIndex');\n // keep only last maxResponsePerZone answers for each zone\n let limitedAnswers = flatMap(groupedAnswers, (grp) => grp.slice(-(maxResponsePerZone || 1)));\n answers = limitedAnswers\n // set id for each answer\n .map((answer, index) => ({ ...answer, id: `${index}` }))\n // return only answer which have a valid container index\n .filter((answer) => answer.containerIndex < responseContainers.length);\n\n const possibleResponsesFiltered = possibleResponsesWithIds.filter(\n (response) => !answers.find((answer) => answer.value === response.value),\n );\n this.state = {\n answers: answers || [],\n draggingElement: { id: '', value: '' },\n possibleResponses: duplicateResponses ? possibleResponsesWithIds : possibleResponsesFiltered,\n // set id for each response containers\n responseContainers: (responseContainers || []).map((item, index) => ({\n index,\n ...item,\n id: `${index}`,\n })),\n maxResponsePerZone: maxResponsePerZone || 1,\n showCorrect: false,\n isValidDrop: false,\n selectedResponse: null,\n };\n this.lastDragEndAt = 0;\n }\n\n onDragStart = (event) => {\n const { active } = event;\n\n if (active?.data?.current) {\n this.setState({\n draggingElement: active.data.current,\n isValidDrop: false,\n selectedResponse: active.data.current,\n });\n }\n };\n\n onDragEnd = (event) => {\n const { active, over } = event;\n const { model } = this.props;\n const { duplicateResponses } = model || {};\n\n // Check if drop is valid\n const draggedItem = active?.data?.current;\n const responseArea = over?.data?.current;\n const isValidDrop =\n over &&\n active &&\n draggedItem &&\n responseArea &&\n responseArea.containerIndex !== undefined;\n\n const shouldDisableAnimation = isValidDrop && duplicateResponses;\n\n this.setState({\n draggingElement: { id: '', value: '' },\n isValidDrop: shouldDisableAnimation,\n });\n\n this.cancelSelection();\n this.lastDragEndAt = Date.now();\n\n if (!over || !active) {\n return;\n }\n\n if (!draggedItem) {\n return;\n }\n\n if (over.id === 'ica-board') {\n if (draggedItem.containerIndex !== undefined) {\n this.handleOnAnswerRemove(draggedItem);\n }\n return;\n }\n\n if (responseArea) {\n this.handleOnAnswerSelect(draggedItem, responseArea.containerIndex);\n }\n };\n\n onDragCancel = () => {\n this.setState({ draggingElement: { id: '', value: '' } });\n this.cancelSelection();\n this.lastDragEndAt = Date.now();\n };\n\n isSameResponse = (a, b) => !!a && !!b && a.id === b.id && a.containerIndex === b.containerIndex;\n\n // Click-to-select semantics: selecting the currently-selected response again clears\n // the selection instead of re-selecting it.\n toggleResponseSelection = (data) => {\n this.setState((state) => ({\n selectedResponse: this.isSameResponse(state.selectedResponse, data) ? null : data,\n }));\n };\n\n cancelSelection = () => {\n this.setState({ selectedResponse: null });\n };\n\n // If a real dnd-kit drag (started via keyboard Space/Enter) is still live when a\n // click completes the placement below, it needs to be cleanly ended — otherwise\n // dnd-kit would still think a drag is in progress. Escape is already configured as\n // this sensor's cancel key (see the keyboardCodes passed to DragProvider below), and\n // dispatching it as a real DOM KeyboardEvent is how dnd-kit's own document-level\n // listener is reached from outside its sensor.\n //\n // Only dispatch when a drag is actually live (draggingElement.id is truthy) — this is\n // a synthetic Escape keydown on `document`, so an unconditional dispatch would also be\n // observed by any other document-level Escape listener (host player modals/dialogs,\n // or another mounted instance of this same component) even when nothing here actually\n // needed cancelling.\n endAnyLiveKeyboardDrag = () => {\n if (!this.state.draggingElement.id) {\n return;\n }\n\n document.dispatchEvent(new KeyboardEvent('keydown', { code: 'Escape', bubbles: true, cancelable: true }));\n };\n\n placeSelectedResponse = (containerIndex) => {\n const { selectedResponse } = this.state;\n\n if (!selectedResponse) {\n return;\n }\n\n if (containerIndex === undefined) {\n // Placing into the pool = removing it from wherever it currently is\n if (selectedResponse.containerIndex !== undefined) {\n this.handleOnAnswerRemove(selectedResponse);\n }\n } else {\n this.handleOnAnswerSelect(selectedResponse, containerIndex);\n }\n\n this.cancelSelection();\n this.endAnyLiveKeyboardDrag();\n this.lastDragEndAt = Date.now();\n };\n\n isClickSoonAfterDragEnd = () => Date.now() - this.lastDragEndAt < CLICK_AFTER_DRAG_GUARD_MS;\n\n onResponseClick = (data) => {\n if (this.isClickSoonAfterDragEnd()) {\n return;\n }\n\n // A click that selects/deselects/switches a tile must end any dnd-kit drag that's\n // still live from an earlier keyboard Space/Enter pick-up first — otherwise dnd-kit\n // keeps thinking that earlier item is being dragged (it ignores new sensor\n // activation while a drag is active) while selectedResponse visually points at\n // whatever this click just selected. Ending the stale drag first (rather than\n // after) matters: ending it also cancels the current selection as a side effect\n // (see onDragCancel above), so doing it before\n // toggleResponseSelection lets this click's own selection be the one that sticks.\n this.endAnyLiveKeyboardDrag();\n this.toggleResponseSelection(data);\n };\n\n onPlacementClick = (containerIndex) => {\n if (this.isClickSoonAfterDragEnd()) {\n return;\n }\n\n this.placeSelectedResponse(containerIndex);\n };\n\n renderDragOverlay = () => {\n const { draggingElement } = this.state;\n const { model } = this.props;\n\n if (!draggingElement.id) return null;\n\n // check if the response contains an image\n const imgRegex = /<img[^>]+src=\"([^\">]+)\"/;\n const containsImage = imgRegex.test(draggingElement.value);\n\n return (\n <PossibleResponse\n key={draggingElement.id}\n canDrag={false}\n data={draggingElement}\n onDragBegin={() => {}}\n isOverlay\n containerStyle={{\n ...(model.answerChoiceTransparency ? { opacity: '0.8' } : {}),\n ...(!containsImage ? { padding: '0 10px', margin: '4px 6px !important' } : {}),\n }}\n />\n );\n };\n\n filterPossibleAnswers = (possibleResponses, answer) =>\n possibleResponses.filter((response) => response.value !== answer.value);\n\n handleOnAnswerSelect = (answer, responseContainerIndex) => {\n const {\n model: { duplicateResponses },\n updateAnswer,\n } = this.props;\n const { answers, maxResponsePerZone } = this.state;\n let { possibleResponses } = this.state;\n let answersToStore;\n\n const answersInThisContainer = [];\n const answersInOtherContainers = [];\n\n answers.forEach((a) => {\n if (a.containerIndex === responseContainerIndex) {\n answersInThisContainer.push(a);\n } else {\n answersInOtherContainers.push(a);\n }\n });\n\n if (maxResponsePerZone === answersInThisContainer.length) {\n const shiftedItem = answersInThisContainer[0];\n if (maxResponsePerZone === 1) {\n answersInThisContainer.shift(); // FIFO\n } else {\n this.setState({ maxResponsePerZoneWarning: true });\n return;\n }\n\n // if duplicates are not allowed, make sure to put the shifted value back in possible responses\n if (!duplicateResponses) {\n possibleResponses = Array.isArray(possibleResponses) ? possibleResponses : [];\n\n possibleResponses.push({\n ...shiftedItem,\n containerIndex: undefined,\n id: shiftedItem.id || generateId(),\n });\n }\n\n // answers will be:\n // + shifted answers for the current container\n // + if duplicatesAllowed, all the other answers from other containers\n // else: all the answers from other containers that are not having the same value\n // + new answer\n answersToStore = [\n ...answersInThisContainer, // shifted\n // TODO allow duplicates case Question: should we remove answer from a container if dragged to another container?\n // if yes, this should do it: add a.id !== answer.id instead of 'true'\n ...answersInOtherContainers.filter((a) => (duplicateResponses ? true : a.value !== answer.value)), // un-shifted\n {\n ...answer,\n containerIndex: responseContainerIndex,\n ...(duplicateResponses ? { id: generateId() } : {}),\n },\n ];\n } else {\n // answers will be:\n // + if duplicatesAllowed, all the other answers, except the one that was dragged\n // else: all the answers that are not having the same value\n // + new answer\n answersToStore = [\n // TODO allow duplicates case Question: should we remove answer from a container if dragged to another container?\n // if yes, this should do it: add a.id !== answer.id instead of 'true'\n ...answers.filter((a) => (duplicateResponses ? a.id !== answer.id : a.value !== answer.value)),\n {\n ...answer,\n containerIndex: responseContainerIndex,\n ...(duplicateResponses ? { id: generateId() } : {}),\n },\n ];\n }\n this.setState({\n maxResponsePerZoneWarning: false,\n answers: answersToStore,\n possibleResponses:\n // for single response per container remove answer from possible responses\n duplicateResponses ? possibleResponses : this.filterPossibleAnswers(possibleResponses, answer),\n });\n updateAnswer(answersToStore);\n };\n\n handleOnAnswerRemove = (answer) => {\n const {\n model: { duplicateResponses },\n updateAnswer,\n } = this.props;\n const { answers, possibleResponses } = this.state;\n const answersToStore = answers.filter((a) => a.id !== answer.id);\n const shouldNotPushInPossibleResponses = answer.containerIndex === undefined; // don't duplicate possible responses\n\n this.setState({\n maxResponsePerZoneWarning: false,\n answers: answersToStore,\n // push back into possible responses the removed answer if responses cannot be duplicated\n possibleResponses:\n duplicateResponses || shouldNotPushInPossibleResponses\n ? possibleResponses\n : [\n ...possibleResponses,\n {\n ...answer,\n containerIndex: undefined,\n },\n ],\n });\n updateAnswer(answersToStore);\n };\n\n toggleCorrect = (showCorrect) => this.setState({ showCorrect });\n\n render() {\n const {\n model: {\n disabled,\n duplicateResponses,\n extraCSSRules,\n image,\n responseAreaFill,\n stimulus,\n responseCorrect,\n validation,\n teacherInstructions,\n prompt,\n autoplayAudioEnabled,\n showDashedBorder,\n mode,\n rationale,\n language,\n uiStyle = {},\n answerChoiceTransparency,\n responseContainerPadding,\n imageDropTargetPadding,\n fontSizeFactor,\n customAudioButton,\n },\n } = this.props;\n const {\n answers,\n draggingElement,\n possibleResponses,\n responseContainers,\n maxResponsePerZone,\n maxResponsePerZoneWarning,\n showCorrect,\n isValidDrop,\n } = this.state;\n const isEvaluateMode = mode === 'evaluate';\n const showToggle = isEvaluateMode && !responseCorrect;\n const { possibilityListPosition = 'bottom' } = uiStyle || {};\n const isVertical = possibilityListPosition === 'left' || possibilityListPosition === 'right';\n\n const { validResponse } = validation || {};\n const correctAnswers = [];\n const showRationale = rationale && (hasText(rationale) || hasMedia(rationale));\n const showTeacherInstructions =\n teacherInstructions && (hasText(teacherInstructions) || hasMedia(teacherInstructions));\n\n if (validResponse) {\n (validResponse.value || []).forEach((container, i) => {\n (container.images || []).forEach((v) => {\n correctAnswers.push({\n value: v,\n containerIndex: i,\n isCorrect: true,\n });\n });\n });\n }\n\n const warningMessage = translator.t('imageClozeAssociation.reachedLimit_other', {\n lng: language,\n count: maxResponsePerZone,\n });\n\n let answersToShow =\n responseCorrect !== undefined ? getAnswersCorrectness(answers, validation, duplicateResponses) : answers;\n\n if (responseCorrect === false && maxResponsePerZone === 1) {\n answersToShow = [...answersToShow, ...getUnansweredAnswers(answersToShow, validation)];\n }\n\n const sharedImageProps = {\n draggingElement,\n duplicateResponses,\n image,\n onAnswerSelect: this.handleOnAnswerSelect,\n onDragAnswerBegin: this.onDragStart,\n onDragAnswerEnd: this.onDragEnd,\n responseContainers,\n showDashedBorder,\n responseAreaFill,\n responseContainerPadding,\n imageDropTargetPadding,\n maxResponsePerZone,\n selectedResponse: this.state.selectedResponse,\n onSelectClick: this.onResponseClick,\n onPlacementClick: this.onPlacementClick,\n };\n\n const renderImage = () => (\n <Image\n {...sharedImageProps}\n canDrag={showCorrect && showToggle ? false : !disabled}\n answers={showCorrect && showToggle ? correctAnswers : answersToShow}\n answerChoiceTransparency={!(showCorrect && showToggle) ? answerChoiceTransparency : undefined}\n />\n );\n\n const renderPossibleResponses = () => {\n if (showCorrect && showToggle) return null;\n\n return (\n <React.Fragment>\n {maxResponsePerZoneWarning && <WarningInfo message={warningMessage} />}\n <PossibleResponses\n canDrag={!disabled}\n data={possibleResponses}\n onAnswerRemove={this.handleOnAnswerRemove}\n onDragBegin={this.onDragStart}\n answerChoiceTransparency={answerChoiceTransparency}\n customStyle={{\n minWidth: isVertical ? '130px' : image?.width || 'fit-content',\n }}\n isVertical={isVertical}\n minHeight={isVertical ? image?.height : undefined}\n selectedResponse={this.state.selectedResponse}\n onSelectClick={this.onResponseClick}\n onPlacementClick={this.onPlacementClick}\n />\n </React.Fragment>\n );\n };\n\n return (\n <DragProvider\n onDragStart={this.onDragStart}\n onDragEnd={this.onDragEnd}\n onDragCancel={this.onDragCancel}\n keyboardCoordinateGetter={closestDroppableKeyboardCoordinates}\n keyboardCodes={{ start: ['Space', 'Enter'], cancel: ['Escape'], end: ['Space', 'Enter'] }}\n >\n <StyledUiLayout extraCSSRules={extraCSSRules} id={'main-container'} fontSizeFactor={fontSizeFactor}>\n {showTeacherInstructions && (\n <StyledTeacherInstructions\n labels={{\n hidden: 'Show Teacher Instructions',\n visible: 'Hide Teacher Instructions',\n }}\n >\n <PreviewPrompt prompt={teacherInstructions} />\n </StyledTeacherInstructions>\n )}\n\n <PreviewPrompt\n className=\"prompt\"\n prompt={prompt}\n autoplayAudioEnabled={autoplayAudioEnabled}\n customAudioButton={customAudioButton}\n />\n\n <PreviewPrompt prompt={stimulus} />\n\n <CorrectAnswerToggle\n show={showToggle}\n toggled={showCorrect}\n onToggle={this.toggleCorrect}\n language={language}\n />\n\n <InteractiveSection responseCorrect={showCorrect && showToggle ? true : responseCorrect} uiStyle={uiStyle}>\n {renderImage()}\n {renderPossibleResponses()}\n </InteractiveSection>\n\n {showRationale && (\n <StyledRationale\n labels={{\n hidden: 'Show Rationale',\n visible: 'Hide Rationale',\n }}\n >\n <PreviewPrompt prompt={rationale} />\n </StyledRationale>\n )}\n </StyledUiLayout>\n {/* Disable drop animation for valid drops to prevent visual snap-back */}\n {/* Keep default animation for invalid drops to show visual feedback */}\n <DragOverlay dropAnimation={isValidDrop ? null : undefined}>\n {this.renderDragOverlay()}\n </DragOverlay>\n </DragProvider>\n );\n }\n}\n\nconst WarningContainer = styled('div')(({ theme }) => ({\n margin: `0 ${theme.spacing(2)}`,\n backgroundColor: '#dddddd',\n padding: theme.spacing(1),\n display: 'flex',\n alignItems: 'center',\n '& svg': {\n height: '30px',\n },\n '& h1': {\n padding: '0px',\n margin: '0px',\n },\n}));\n\nconst WarningMessage = styled('span')(({ theme }) => ({\n paddingLeft: theme.spacing(0.5),\n userSelect: 'none',\n}));\n\nconst WarningInfo = ({ message }) => {\n const nodeRef = React.useRef(null);\n\n return (\n <TransitionGroup>\n <CSSTransition classNames={'fb'} key=\"fb\" timeout={300} nodeRef={nodeRef}>\n <WarningContainer ref={nodeRef} key=\"panel\">\n <NotInterestedIcon color={'secondary'} fontSize={'small'} />\n <WarningMessage dangerouslySetInnerHTML={{ __html: message }} />\n </WarningContainer>\n </CSSTransition>\n </TransitionGroup>\n );\n};\n\nWarningInfo.propTypes = {\n message: PropTypes.string,\n};\n\nImageClozeAssociationComponent.propTypes = {\n model: PropTypes.object.isRequired,\n session: PropTypes.object,\n updateAnswer: PropTypes.func.isRequired,\n};\n\nexport default ImageClozeAssociationComponent;\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pie-element/image-cloze-association",
3
3
  "repository": "pie-framework/pie-elements",
4
- "version": "10.3.0-beta.0",
4
+ "version": "10.3.0",
5
5
  "description": "",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -13,10 +13,10 @@
13
13
  "@mui/icons-material": "^7.3.4",
14
14
  "@mui/material": "^7.3.4",
15
15
  "@pie-framework/pie-player-events": "^0.1.0",
16
- "@pie-lib/correct-answer-toggle": "4.0.5",
17
- "@pie-lib/drag": "4.0.6",
16
+ "@pie-lib/correct-answer-toggle": "4.0.7",
17
+ "@pie-lib/drag": "4.1.2",
18
18
  "@pie-lib/math-rendering": "5.1.0",
19
- "@pie-lib/render-ui": "6.1.3",
19
+ "@pie-lib/render-ui": "7.0.1",
20
20
  "@pie-lib/translator": "4.0.2",
21
21
  "classnames": "^2.2.6",
22
22
  "humps": "^2.0.1",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "author": "pie framework developers",
29
29
  "license": "ISC",
30
- "gitHead": "3cd84089e89efe27cd153b42338950b58941a151",
30
+ "gitHead": "5635523a84dd61f007275dcb853627b1ff0c6792",
31
31
  "scripts": {
32
32
  "postpublish": "../../scripts/postpublish"
33
33
  },
@@ -0,0 +1,169 @@
1
+ import React from 'react';
2
+ import { render, fireEvent } from '@testing-library/react';
3
+ import ImageDropTarget from '../image-drop-target';
4
+
5
+ jest.mock('@dnd-kit/core', () => ({
6
+ useDroppable: () => ({
7
+ setNodeRef: jest.fn(),
8
+ isOver: false,
9
+ }),
10
+ }));
11
+
12
+ jest.mock('../possible-response', () => (props) => (
13
+ <div data-testid={`possible-response-${props.data.id}`} onClick={() => props.onSelectClick?.(props.data)}>
14
+ {props.data.value}
15
+ </div>
16
+ ));
17
+
18
+ describe('ImageDropTarget', () => {
19
+ const baseProps = {
20
+ answers: [],
21
+ canDrag: true,
22
+ containerStyle: {},
23
+ draggingElement: { id: '' },
24
+ onDragAnswerBegin: jest.fn(),
25
+ onDragAnswerEnd: jest.fn(),
26
+ onDrop: jest.fn(),
27
+ index: 2,
28
+ };
29
+
30
+ describe('empty container', () => {
31
+ it('is a native tab stop (role=button, tabIndex=0) when canDrag', () => {
32
+ const { container } = render(<ImageDropTarget {...baseProps} />);
33
+ const target = container.firstChild;
34
+
35
+ expect(target.getAttribute('role')).toBe('button');
36
+ expect(target.getAttribute('tabindex')).toBe('0');
37
+ });
38
+
39
+ it('is not a tab stop when canDrag is false, and has no role either (I2)', () => {
40
+ const { container } = render(<ImageDropTarget {...baseProps} canDrag={false} />);
41
+ const target = container.firstChild;
42
+
43
+ expect(target.getAttribute('tabindex')).toBe('-1');
44
+ expect(target.getAttribute('role')).toBeNull();
45
+ });
46
+
47
+ it('places the selection here on click', () => {
48
+ const onPlacementClick = jest.fn();
49
+ const { container } = render(
50
+ <ImageDropTarget
51
+ {...baseProps}
52
+ selectedResponse={{ id: '9', containerIndex: undefined }}
53
+ onPlacementClick={onPlacementClick}
54
+ />,
55
+ );
56
+
57
+ fireEvent.click(container.firstChild);
58
+
59
+ expect(onPlacementClick).toHaveBeenCalledWith(2);
60
+ });
61
+
62
+ it('does nothing on click when nothing is selected', () => {
63
+ const onPlacementClick = jest.fn();
64
+ const { container } = render(<ImageDropTarget {...baseProps} onPlacementClick={onPlacementClick} />);
65
+
66
+ fireEvent.click(container.firstChild);
67
+
68
+ expect(onPlacementClick).not.toHaveBeenCalled();
69
+ });
70
+
71
+ it('places the selection here on Space/Enter', () => {
72
+ const onPlacementClick = jest.fn();
73
+ const { container } = render(
74
+ <ImageDropTarget
75
+ {...baseProps}
76
+ selectedResponse={{ id: '9', containerIndex: undefined }}
77
+ onPlacementClick={onPlacementClick}
78
+ />,
79
+ );
80
+
81
+ fireEvent.keyDown(container.firstChild, { code: 'Space' });
82
+
83
+ expect(onPlacementClick).toHaveBeenCalledWith(2);
84
+ });
85
+ });
86
+
87
+ describe('occupied container', () => {
88
+ const answers = [{ id: '7', value: 'Placed', containerIndex: 2 }];
89
+
90
+ it('is not its own tab stop and has no role either — occupied containers must not become unlabeled buttons for screen readers (I2)', () => {
91
+ const { container } = render(<ImageDropTarget {...baseProps} answers={answers} />);
92
+ const target = container.firstChild;
93
+
94
+ expect(target.getAttribute('tabindex')).toBe('-1');
95
+ expect(target.getAttribute('role')).toBeNull();
96
+ });
97
+
98
+ it('places the selection here when clicking empty background (not a specific tile)', () => {
99
+ const onPlacementClick = jest.fn();
100
+ const { container } = render(
101
+ <ImageDropTarget
102
+ {...baseProps}
103
+ answers={answers}
104
+ selectedResponse={{ id: '9', containerIndex: undefined }}
105
+ onPlacementClick={onPlacementClick}
106
+ />,
107
+ );
108
+
109
+ fireEvent.click(container.firstChild);
110
+
111
+ expect(onPlacementClick).toHaveBeenCalledWith(2);
112
+ });
113
+
114
+ it('forwards selection props to the placed tile, which handles its own click', () => {
115
+ const onSelectClick = jest.fn();
116
+ const { getByTestId } = render(
117
+ <ImageDropTarget {...baseProps} answers={answers} onSelectClick={onSelectClick} />,
118
+ );
119
+
120
+ fireEvent.click(getByTestId('possible-response-7'));
121
+
122
+ expect(onSelectClick).toHaveBeenCalledWith(answers[0]);
123
+ });
124
+ });
125
+
126
+ describe('hover affordance during click-to-select (I3)', () => {
127
+ it('gets the is-over highlight and a pointer cursor on hover when something is selected', () => {
128
+ const { container } = render(
129
+ <ImageDropTarget {...baseProps} selectedResponse={{ id: '9', containerIndex: undefined }} />,
130
+ );
131
+ const target = container.firstChild;
132
+
133
+ expect(target.className).not.toMatch(/\bis-over\b/);
134
+
135
+ fireEvent.mouseEnter(target);
136
+ expect(target.className).toMatch(/\bis-over\b/);
137
+ expect(target.style.cursor).toBe('pointer');
138
+
139
+ fireEvent.mouseLeave(target);
140
+ expect(target.className).not.toMatch(/\bis-over\b/);
141
+ });
142
+
143
+ it('does not show the is-over highlight on hover when nothing is selected', () => {
144
+ const { container } = render(<ImageDropTarget {...baseProps} selectedResponse={null} />);
145
+ const target = container.firstChild;
146
+
147
+ fireEvent.mouseEnter(target);
148
+
149
+ expect(target.className).not.toMatch(/\bis-over\b/);
150
+ expect(target.style.cursor).toBe('');
151
+ });
152
+
153
+ it('does not show the is-over highlight on hover when canDrag is false, even with a selection', () => {
154
+ const { container } = render(
155
+ <ImageDropTarget
156
+ {...baseProps}
157
+ canDrag={false}
158
+ selectedResponse={{ id: '9', containerIndex: undefined }}
159
+ />,
160
+ );
161
+ const target = container.firstChild;
162
+
163
+ fireEvent.mouseEnter(target);
164
+
165
+ expect(target.className).not.toMatch(/\bis-over\b/);
166
+ expect(target.style.cursor).toBe('');
167
+ });
168
+ });
169
+ });
@@ -0,0 +1,127 @@
1
+ import { KeyboardCode } from '@dnd-kit/core';
2
+ import { closestDroppableKeyboardCoordinates } from '../keyboard-coordinates';
3
+
4
+ function rectsToContext(rects) {
5
+ const droppableRects = new Map(Object.entries(rects));
6
+ const droppableContainers = new Map(Object.keys(rects).map((id) => [id, { disabled: false }]));
7
+
8
+ return { droppableRects, droppableContainers };
9
+ }
10
+
11
+ function makeEvent(code, shiftKey = false) {
12
+ return { code, preventDefault: () => {}, shiftKey };
13
+ }
14
+
15
+ // Mirrors dnd-kit's own KeyboardSensor, which always derives collisionRect's top-left
16
+ // from the dragged item's current on-screen position on every keydown — so a test's
17
+ // collisionRect must track currentCoordinates the same way on every simulated press,
18
+ // not stay fixed while currentCoordinates changes across multiple presses.
19
+ function press(rects, currentCoordinates, itemSize, event) {
20
+ const context = {
21
+ ...rectsToContext(rects),
22
+ collisionRect: { left: currentCoordinates.x, top: currentCoordinates.y, ...itemSize },
23
+ };
24
+
25
+ return closestDroppableKeyboardCoordinates(event, { context, currentCoordinates });
26
+ }
27
+
28
+ describe('closestDroppableKeyboardCoordinates', () => {
29
+ describe('arrow keys', () => {
30
+ it("nudges the dragged item by dnd-kit's own default step (25px), unchanged", () => {
31
+ const context = { ...rectsToContext({}), collisionRect: { left: 0, top: 0, width: 100, height: 40 } };
32
+ const currentCoordinates = { x: 10, y: 20 };
33
+
34
+ expect(
35
+ closestDroppableKeyboardCoordinates(makeEvent(KeyboardCode.Down), { context, currentCoordinates }),
36
+ ).toEqual({ x: 10, y: 45 });
37
+ expect(
38
+ closestDroppableKeyboardCoordinates(makeEvent(KeyboardCode.Up), { context, currentCoordinates }),
39
+ ).toEqual({ x: 10, y: -5 });
40
+ expect(
41
+ closestDroppableKeyboardCoordinates(makeEvent(KeyboardCode.Right), { context, currentCoordinates }),
42
+ ).toEqual({ x: 35, y: 20 });
43
+ expect(
44
+ closestDroppableKeyboardCoordinates(makeEvent(KeyboardCode.Left), { context, currentCoordinates }),
45
+ ).toEqual({ x: -15, y: 20 });
46
+ });
47
+ });
48
+
49
+ describe('Tab / Shift+Tab', () => {
50
+ // Two response containers plus the choices pool ("ica-board"), laid out
51
+ // top-to-bottom like they would be over an image.
52
+ const container0Rect = { left: 0, top: 0, width: 200, height: 40, right: 200, bottom: 40 };
53
+ const container1Rect = { left: 0, top: 60, width: 200, height: 40, right: 200, bottom: 100 };
54
+ const poolRect = { left: 0, top: 120, width: 400, height: 300, right: 400, bottom: 420 };
55
+ const baseRects = {
56
+ 'response-container-0': container0Rect,
57
+ 'response-container-1': container1Rect,
58
+ 'ica-board': poolRect,
59
+ };
60
+
61
+ it('jumps to the next droppable in top-to-bottom order, landing at its center-left point', () => {
62
+ const currentCoordinates = { x: container0Rect.left, y: container0Rect.top };
63
+
64
+ const next = press(baseRects, currentCoordinates, { width: 200, height: 40 }, makeEvent('Tab'));
65
+
66
+ expect(next).toEqual({ x: container1Rect.left, y: container1Rect.top + container1Rect.height / 2 });
67
+ });
68
+
69
+ it('cycles backwards with Shift+Tab, wrapping to the pool', () => {
70
+ const currentCoordinates = { x: container0Rect.left, y: container0Rect.top };
71
+
72
+ const next = press(baseRects, currentCoordinates, { width: 200, height: 40 }, makeEvent('Tab', true));
73
+
74
+ expect(next).toEqual({ x: poolRect.left, y: poolRect.top + poolRect.height / 2 });
75
+ });
76
+
77
+ it('keeps advancing on repeated Shift+Tab when a container is much wider than the dragged item', () => {
78
+ // Regression case for the bug already found and fixed in match-list: matching the
79
+ // "current" target by reconstructing a center from the dragged item's own small
80
+ // size (instead of checking which target's rect actually contains it) makes the
81
+ // *next* press re-match a completely different droppable once the item is
82
+ // actually sitting on a wide target, so it looks like the press does nothing.
83
+ // Only two targets here (a wide container and the pool), to isolate this from
84
+ // sort-order effects.
85
+ const wideContainerRect = { left: 0, top: 0, width: 900, height: 40, right: 900, bottom: 40 };
86
+ const rects = { 'response-container-0': wideContainerRect, 'ica-board': poolRect };
87
+ const itemSize = { width: 100, height: 40 };
88
+
89
+ // Start inside the pool, Shift+Tab to the only other target: the wide container.
90
+ const afterFirst = press(rects, { x: poolRect.left, y: poolRect.top }, itemSize, makeEvent('Tab', true));
91
+
92
+ expect(afterFirst).toEqual({ x: 0, y: 20 }); // wide container's center-left point
93
+
94
+ // Now sitting exactly at that dropPosition — Shift+Tab again must advance back
95
+ // to the pool, not re-match some other droppable.
96
+ const afterSecond = press(rects, afterFirst, itemSize, makeEvent('Tab', true));
97
+
98
+ expect(afterSecond).toEqual({ x: poolRect.left, y: poolRect.top + poolRect.height / 2 });
99
+ });
100
+
101
+ it("correctly identifies the pool as the current target when picking up an item positioned near the pool's edge, close to a small neighboring container", () => {
102
+ // Regression case for the OTHER failure mode: matching "current target" purely
103
+ // by nearest-dropPosition (instead of containment first) can misidentify the
104
+ // pool as some small, unrelated nearby container, because a large droppable's
105
+ // dropPosition is anchored at ITS OWN vertical middle — which can be far from an
106
+ // item that's sitting near the droppable's edge, even though the item is clearly
107
+ // still inside it.
108
+ const tallPoolRect = { left: 0, top: 0, width: 400, height: 600, right: 400, bottom: 600 };
109
+ const smallContainerRect = { left: 0, top: -60, width: 100, height: 40, right: 100, bottom: -20 };
110
+ const rects = { 'response-container-0': smallContainerRect, 'ica-board': tallPoolRect };
111
+ const itemSize = { width: 100, height: 40 };
112
+
113
+ // The item sits just inside the pool's top edge (y: 10-50) — nowhere near the
114
+ // pool's own vertical middle (y: 300), but visually still inside it, and closer
115
+ // in raw distance to the small container's anchor point (top: -60, center y:
116
+ // -40) than to the pool's anchor point (center y: 300).
117
+ const currentCoordinates = { x: 0, y: 10 };
118
+
119
+ const next = press(rects, currentCoordinates, itemSize, makeEvent('Tab'));
120
+
121
+ // Tab forward from the pool lands on the only other target: the small
122
+ // container — proving the pool, not the small container, was identified as
123
+ // "current".
124
+ expect(next).toEqual({ x: smallContainerRect.left, y: smallContainerRect.top + smallContainerRect.height / 2 });
125
+ });
126
+ });
127
+ });
@@ -0,0 +1,113 @@
1
+ import React from 'react';
2
+ import { render, fireEvent } from '@testing-library/react';
3
+ import PossibleResponse from '../possible-response';
4
+
5
+ jest.mock('@dnd-kit/core', () => ({
6
+ useDraggable: () => ({
7
+ setNodeRef: jest.fn(),
8
+ attributes: {},
9
+ listeners: {},
10
+ isDragging: false,
11
+ }),
12
+ }));
13
+
14
+ describe('PossibleResponse', () => {
15
+ const baseProps = {
16
+ canDrag: true,
17
+ data: { id: '0', value: 'Choice A' },
18
+ onDragBegin: jest.fn(),
19
+ };
20
+
21
+ it('selects a pool item on click when nothing else is selected', () => {
22
+ const onSelectClick = jest.fn();
23
+ const onPlacementClick = jest.fn();
24
+ const { container } = render(
25
+ <PossibleResponse
26
+ {...baseProps}
27
+ selectedResponse={null}
28
+ onSelectClick={onSelectClick}
29
+ onPlacementClick={onPlacementClick}
30
+ />,
31
+ );
32
+
33
+ fireEvent.click(container.firstChild);
34
+
35
+ expect(onSelectClick).toHaveBeenCalledWith(baseProps.data);
36
+ expect(onPlacementClick).not.toHaveBeenCalled();
37
+ });
38
+
39
+ it('toggles off (calls onSelectClick again) when clicking the already-selected item', () => {
40
+ const onSelectClick = jest.fn();
41
+ const onPlacementClick = jest.fn();
42
+ const { container } = render(
43
+ <PossibleResponse
44
+ {...baseProps}
45
+ selectedResponse={{ id: '0', containerIndex: undefined }}
46
+ onSelectClick={onSelectClick}
47
+ onPlacementClick={onPlacementClick}
48
+ />,
49
+ );
50
+
51
+ fireEvent.click(container.firstChild);
52
+
53
+ expect(onSelectClick).toHaveBeenCalledWith(baseProps.data);
54
+ expect(onPlacementClick).not.toHaveBeenCalled();
55
+ });
56
+
57
+ it('selects a different pool item on click even while something else is selected (pool items are never placement targets)', () => {
58
+ const onSelectClick = jest.fn();
59
+ const onPlacementClick = jest.fn();
60
+ const { container } = render(
61
+ <PossibleResponse
62
+ {...baseProps}
63
+ selectedResponse={{ id: '1', containerIndex: 2 }}
64
+ onSelectClick={onSelectClick}
65
+ onPlacementClick={onPlacementClick}
66
+ />,
67
+ );
68
+
69
+ fireEvent.click(container.firstChild);
70
+
71
+ expect(onSelectClick).toHaveBeenCalledWith(baseProps.data);
72
+ expect(onPlacementClick).not.toHaveBeenCalled();
73
+ });
74
+
75
+ it('places the current selection into this tile\'s container when clicking an already-placed tile that is not the selection', () => {
76
+ const onSelectClick = jest.fn();
77
+ const onPlacementClick = jest.fn();
78
+ const placedData = { id: '5', value: 'Choice B', containerIndex: 3 };
79
+ const { container } = render(
80
+ <PossibleResponse
81
+ {...baseProps}
82
+ data={placedData}
83
+ selectedResponse={{ id: '1', containerIndex: undefined }}
84
+ onSelectClick={onSelectClick}
85
+ onPlacementClick={onPlacementClick}
86
+ />,
87
+ );
88
+
89
+ fireEvent.click(container.firstChild);
90
+
91
+ expect(onPlacementClick).toHaveBeenCalledWith(3);
92
+ expect(onSelectClick).not.toHaveBeenCalled();
93
+ });
94
+
95
+ it('does nothing on click when disabled (canDrag=false)', () => {
96
+ const onSelectClick = jest.fn();
97
+ const onPlacementClick = jest.fn();
98
+ const { container } = render(
99
+ <PossibleResponse
100
+ {...baseProps}
101
+ canDrag={false}
102
+ selectedResponse={null}
103
+ onSelectClick={onSelectClick}
104
+ onPlacementClick={onPlacementClick}
105
+ />,
106
+ );
107
+
108
+ fireEvent.click(container.firstChild);
109
+
110
+ expect(onSelectClick).not.toHaveBeenCalled();
111
+ expect(onPlacementClick).not.toHaveBeenCalled();
112
+ });
113
+ });