@khanacademy/perseus-editor 41.0.0 → 42.1.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/dist/editor-page.d.ts +7 -18
- package/dist/es/index.css +1 -1
- package/dist/es/index.css.map +1 -1
- package/dist/es/index.js +6 -6
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/package.json +47 -47
package/dist/index.js
CHANGED
|
@@ -134,7 +134,7 @@ var minusCircleIcon__default = /*#__PURE__*/_interopDefaultCompat(minusCircleIco
|
|
|
134
134
|
var arrowDown__default = /*#__PURE__*/_interopDefaultCompat(arrowDown);
|
|
135
135
|
var arrowUp__default = /*#__PURE__*/_interopDefaultCompat(arrowUp);
|
|
136
136
|
|
|
137
|
-
const libName="@khanacademy/perseus-editor";const libVersion="
|
|
137
|
+
const libName="@khanacademy/perseus-editor";const libVersion="42.1.0";perseusUtils.addLibraryVersionToPerseusDebug(libName,libVersion);
|
|
138
138
|
|
|
139
139
|
var jsxRuntime = {exports: {}};
|
|
140
140
|
|
|
@@ -1486,7 +1486,7 @@ const SCREEN_SIZES={phone:{width:320,height:480,framedWidth:320},tablet:{width:7
|
|
|
1486
1486
|
|
|
1487
1487
|
function createA11yContextValue(overrides={}){return {setIssueHighlight:()=>{},a11yScanningEnabled:false,setA11yScanningEnabled:()=>{},highlightInstanceIds:[],onA11yReport:()=>{},axeCoreIssues:[],...overrides}}const A11yContext=React__namespace.createContext(null);
|
|
1488
1488
|
|
|
1489
|
-
const IMAGE_MARKDOWN_REGEX=/!\[[^\]]*\]\([^)]+\)/g;function getFirstAvailableWidgetIndex(widgetType,widgets){const widgetNames=Object.keys(widgets);for(let i=1;i<=widgetNames.length+1;i++){if(!widgetNames.includes(`${widgetType} ${i}`)){return i}}return 1}async function convertImageMarkdownToImageWidget(question,onEditorChange){const{content,widgets}=question;const newWidgets={...widgets};const matches=Array.from(content.matchAll(IMAGE_MARKDOWN_REGEX));if(matches.length===0){return}const replacements=[];for(const match of matches){const imageIndex=getFirstAvailableWidgetIndex("image",newWidgets);const imageNode=perseus.PerseusMarkdown.parse(match[0],{});const imageUrl=imageNode[0].content[0].target;const imageAlt=imageNode[0].content[0].alt;const imageSize=await perseus.Util.getImageSizeModern(imageUrl);const[width,height]=imageSize;newWidgets[`image ${imageIndex}`]=perseusCore.generateImageWidget({options:perseusCore.generateImageOptions({backgroundImage:{url:imageUrl,width,height},alt:imageAlt})});replacements.push({original:match[0],replacement:`[[☃ image ${imageIndex}]]`});}let newContent=content;for(const{original,replacement}of replacements){newContent=newContent.replace(original,replacement);}onEditorChange({content:newContent,widgets:newWidgets});}function getCtaForIssueId(issueId,question,onEditorChange){switch(issueId){case "image-markdown":return {label:"Convert all image markdown to widget",onClick:()=>convertImageMarkdownToImageWidget(question,onEditorChange)};default:return null}}
|
|
1489
|
+
const IMAGE_MARKDOWN_REGEX=/!\[[^\]]*\]\([^)]+\)/g;function getFirstAvailableWidgetIndex(widgetType,widgets){const widgetNames=Object.keys(widgets);for(let i=1;i<=widgetNames.length+1;i++){if(!widgetNames.includes(`${widgetType} ${i}`)){return i}}return 1}async function convertImageMarkdownToImageWidget(question,onEditorChange){const{content,widgets}=question;const newWidgets={...widgets};const matches=Array.from(content.matchAll(IMAGE_MARKDOWN_REGEX));if(matches.length===0){return}const replacements=[];for(const match of matches){const imageIndex=getFirstAvailableWidgetIndex("image",newWidgets);const imageNode=perseus.PerseusMarkdown.parse(match[0],{});const imageUrl=imageNode[0].content[0].target;const imageAlt=imageNode[0].content[0].alt;const imageSize=await perseus.Util.getImageSizeModern(imageUrl);const[width,height]=imageSize;newWidgets[`image ${imageIndex}`]=perseusCore.generateImageWidget({options:perseusCore.generateImageOptions({backgroundImage:{url:imageUrl,width,height},alt:imageAlt})});replacements.push({original:match[0],replacement:`[[☃ image ${imageIndex}]]`});}let newContent=content;for(const{original,replacement}of replacements){newContent=newContent.replace(original,replacement);}onEditorChange({content:newContent,widgets:newWidgets,images:{}});}function getCtaForIssueId(issueId,question,onEditorChange){switch(issueId){case "image-markdown":return {label:"Convert all image markdown to widget",onClick:()=>convertImageMarkdownToImageWidget(question,onEditorChange)};default:return null}}
|
|
1490
1490
|
|
|
1491
1491
|
const defaultItemEditorContext={question:{content:"",widgets:{},images:{}},onEditorChange:()=>{}};const ItemEditorContext=React__namespace.createContext(defaultItemEditorContext);function useItemEditorContext(){return React__namespace.useContext(ItemEditorContext)}
|
|
1492
1492
|
|
|
@@ -1528,7 +1528,7 @@ class WidgetSelect extends React__namespace.Component{shouldComponentUpdate(){re
|
|
|
1528
1528
|
|
|
1529
1529
|
function setPerseusClipboardData({text,widgets}){const dummyElement=document.createElement("span");dummyElement.setAttribute("data-perseus-widgets",JSON.stringify(widgets));return navigator.clipboard.write([new ClipboardItem({"text/plain":text,"text/html":dummyElement.outerHTML})])}async function getPerseusClipboardData(){const clipboardItems=await navigator.clipboard.read();let widgets={};let text="";for(const item of clipboardItems){if(item.types.includes("text/html")){const dummyDiv=document.createElement("span");const htmlBlob=await item.getType("text/html");dummyDiv.innerHTML=await htmlBlob.text();const widgetsJson=dummyDiv.querySelector("[data-perseus-widgets]")?.getAttribute("data-perseus-widgets");if(widgetsJson){widgets=JSON.parse(widgetsJson);}}if(item.types.includes("text/plain")){const plainTextBlob=await item.getType("text/plain");text=await plainTextBlob.text();}}return {widgets,text}}
|
|
1530
1530
|
|
|
1531
|
-
const widgetPlaceholder="[[☃ {id}]]";const widgetRegExp="(\\[\\[☃ {id}\\]\\])";const rWidgetSplit=new RegExp(widgetRegExp.replace("{id}","[a-z-]+ [0-9]+"),"g");const shortcutRegexp=/^\[\[([a-z-]+)$/;const ENDS_WITH_A_PARAGRAPH=/(?:\n{2,}|^\n*)$/;const TRAILING_NEWLINES=/(\n*)$/;const LEADING_NEWLINES=/^(\n*)/;const commafyInteger=n=>{let str=n.toString();if(str.length>=5){str=str.replace(/(\d)(?=(\d{3})+$)/g,"$1{,}");}return str};const makeEndWithAParagraphIfNecessary=content=>{if(!ENDS_WITH_A_PARAGRAPH.test(content)){const match=TRAILING_NEWLINES.exec(content);if(match){const newlines=match[1];return content+"\n\n".slice(0,2-newlines.length)}}return content};const makeStartWithAParagraphAlways=content=>{const match=LEADING_NEWLINES.exec(content);if(!match){return content}const newlines=match[1];return "\n\n".slice(0,2-newlines.length)+content};const IMAGE_REGEX$1=/!\[[^\]]*\]\(([^\s)]+)[^)]*\)/g;const allMatches=function(regex,str){const result=[];while(true){const match=regex.exec(str);if(!match){break}result.push(match);}return result};const imageUrlsFromContent=function(content){return allMatches(IMAGE_REGEX$1,content).map(capture=>capture[1])};class Editor extends React__namespace.Component{static getDerivedStateFromProps(props,state){return {textAreaValue:props.content,rememberedWidgetsForUndo:{...state.rememberedWidgetsForUndo,...props.widgets}}}componentDidMount(){this.lastUserValue=null;this._sizeImages(this.props);}componentDidUpdate(prevProps){const textarea=this.textarea.current;if(this.lastUserValue!=null&&textarea){textarea.focus();textarea.value=this.lastUserValue;textarea.selectionStart=0;textarea.setSelectionRange(0,prevProps.content.length);document.execCommand("insertText",false,this.props.content);this.lastUserValue=null;}if(this._pendingCursorPos!=null&&textarea){perseus.Util.textarea.moveCursor(textarea,this._pendingCursorPos);this._pendingCursorPos=null;}if(this.props.content!==prevProps.content){this._sizeImages(this.props);}}getWidgetEditor(id,type){if(!perseus.Widgets.getEditor(type)){return}return jsxRuntimeExports.jsx(WidgetEditor,{widgetInfo:this.props.widgets[id],ref:id,id:id,onChange:this._handleWidgetEditorChange.bind(this,id),onRemove:this._handleWidgetEditorRemove.bind(this,id),apiOptions:this.props.apiOptions,widgetIsOpen:this.props.widgetIsOpen},id)}getWidgetsReferencedIn(content){const referencedWidgetIds=perseusCore.getWidgetIdsFromContent(content);const allWidgets={...this.state.rememberedWidgetsForUndo,...this.props.widgets};const referencedWidgets={};for(const id of referencedWidgetIds){if(allWidgets[id]!=null){referencedWidgets[id]=allWidgets[id];}}return referencedWidgets}serialize(){const widgets={};const widgetIds=___default.default.intersection(this.widgetIds,Object.keys(this.refs));___default.default.each(widgetIds,id=>{widgets[id]=this.refs[id].serialize();});return {content:this.props.content,images:this.props.images,widgets}}render(){let pieces;let widgets;let underlayPieces;let templatesDropDown;let widgetsAndTemplates;let wordCountDisplay;if(this.props.showWordCount){const numChars=perseus.PerseusMarkdown.characterCount(this.props.content);const numWords=Math.floor(numChars/6);wordCountDisplay=jsxRuntimeExports.jsx("span",{className:"perseus-editor-word-count",title:"~"+commafyInteger(numWords)+" words ("+commafyInteger(numChars)+" characters)",children:commafyInteger(numWords)});}if(this.props.widgetEnabled){pieces=this.props.content.split(rWidgetSplit);widgets={};underlayPieces=[];for(let i=0;i<pieces.length;i++){if(i%2===0){underlayPieces.push(pieces[i]);}else {const match=perseus.Util.rWidgetParts.exec(pieces[i]);if(match!=null){const id=match[1];const type=match[2];const duplicate=id in widgets;widgets[id]=this.getWidgetEditor(id,type);const classes=(duplicate||!widgets[id]?"error ":"")+("");const key=duplicate?i:id;underlayPieces.push(jsxRuntimeExports.jsx("b",{className:classes,children:pieces[i]},key));}}}this.widgetIds=Object.keys(widgets);const insertTemplateString="Insert template…";templatesDropDown=jsxRuntimeExports.jsxs("select",{onChange:this.addTemplate,"data-testid":"editor__template-select",children:[jsxRuntimeExports.jsx("option",{value:"",children:insertTemplateString}),jsxRuntimeExports.jsx("option",{disabled:true,children:"--"}),jsxRuntimeExports.jsx("option",{value:"table",children:"Table"}),jsxRuntimeExports.jsx("option",{value:"titledTable",children:"Titled table"}),jsxRuntimeExports.jsx("option",{value:"alignment",children:"Aligned equations"}),jsxRuntimeExports.jsx("option",{value:"piecewise",children:"Piecewise function"}),Object.keys(this.props.additionalTemplates).length>0&&jsxRuntimeExports.jsx("option",{disabled:true,children:"--"}),Object.entries(this.props.additionalTemplates).map(([key])=>jsxRuntimeExports.jsx("option",{value:key,children:key},key)),jsxRuntimeExports.jsx("option",{disabled:true,children:"--"}),jsxRuntimeExports.jsx("option",{value:"allWidgets",children:"All widgets (for testing)"})]});const widgetNodes=Object.values(widgets);widgetsAndTemplates=jsxRuntimeExports.jsxs("div",{className:"perseus-editor-widgets",children:[jsxRuntimeExports.jsxs("div",{className:"perseus-editor-widgets-selectors",children:[jsxRuntimeExports.jsx(WidgetSelect,{onChange:this._addWidget}),templatesDropDown,wordCountDisplay]}),widgetNodes]});}else {underlayPieces=[this.props.content];}underlayPieces.push(jsxRuntimeExports.jsx("br",{},"end"));const completeTextarea=[jsxRuntimeExports.jsx("div",{className:"perseus-textarea-underlay",ref:this.underlay,children:underlayPieces},"underlay"),jsxRuntimeExports.jsx("textarea",{ref:this.textarea,"aria-label":"Markdown content",onChange:this.handleChange,onKeyDown:this._handleKeyDown,onCopy:this._maybeCopyWidgets,onCut:this._maybeCopyWidgets,onPaste:this._maybePasteWidgets,placeholder:this.props.placeholder,disabled:this.props.disabled,value:this.state.textAreaValue},"textarea")];let textareaWrapper;if(this.props.imageUploader){textareaWrapper=jsxRuntimeExports.jsx(DragTarget,{onDrop:this.handleDrop,className:"perseus-textarea-pair",children:completeTextarea});}else {textareaWrapper=jsxRuntimeExports.jsx("div",{className:"perseus-textarea-pair",children:completeTextarea});}const contentWithoutWidgets=this.props.content.replace(/\[\[\u2603 (([a-z-]+) ([0-9]+))\]\]/g,"");const noPrompt=contentWithoutWidgets.trim().length===0;const noWidgets=!/\[\[\u2603 (([a-z-]+) ([0-9]+))\]\]/g.test(this.props.content);const warningStyle={borderTop:"none",padding:4,backgroundColor:"pink"};const editingDisabled=this.props.apiOptions.editingDisabled;return jsxRuntimeExports.jsxs("div",{"data-testid":"perseus-single-editor",className:"perseus-single-editor "+(this.props.className||"")+(editingDisabled?" perseus-editor-disabled":""),children:[textareaWrapper,this.props.warnNoPrompt&&noPrompt&&jsxRuntimeExports.jsx("div",{style:warningStyle,children:"Graded Groups should contain a prompt"}),this.props.warnNoWidgets&&noWidgets&&jsxRuntimeExports.jsx("div",{style:warningStyle,children:"Graded Groups should contain at least one widget"}),widgetsAndTemplates]})}constructor(...args){super(...args),this._pendingCursorPos=null,this.underlay=React__namespace.createRef(),this.textarea=React__namespace.createRef(),this.state={textAreaValue:this.props.content,rememberedWidgetsForUndo:{}},this._handleWidgetEditorChange=(id,newWidgetInfo)=>{const widgets=Object.assign({},this.props.widgets);widgets[id]=Object.assign({},widgets[id],newWidgetInfo);this.props.onChange({widgets});},this._handleWidgetEditorRemove=id=>{if(!confirm("Are you sure you want to delete this item?")){return}const textarea=this.textarea.current;const re=new RegExp(widgetRegExp.replace("{id}",id),"gm");this.props.onChange({content:textarea?.value.replace(re,"")});},this._sizeImages=props=>{const imageUrls=imageUrlsFromContent(props.content);const images=___default.default.pick(props.images,imageUrls);const newImageUrls=___default.default.filter(imageUrls,url=>!images[url]);___default.default.each(newImageUrls,url=>{perseus.Util.getImageSize(url,(width,height)=>{images[url]={width:width,height:height};props.onChange({images:___default.default.clone(images)});});});},this.handleDrop=e=>{const{imageUploader}=this.props;let content=this.state.textAreaValue||"";const dataTransfer="dataTransfer"in e?e.dataTransfer:null;if(!dataTransfer||!imageUploader){return}const files=dataTransfer.files;if(files.length===0){const imageUrl=dataTransfer.getData("URL");if(imageUrl){const newContent=content+"\n\n";this.lastUserValue=this.props.content;this.props.onChange({content:newContent});}return}const origContent=this.state.textAreaValue;___default.default(files).chain().map(function(file){if(!file.type.match("image.*")){return null}const sentinel="☃ "+___default.default.uniqueId("image_");content+="\n\n";return {file:file,sentinel:sentinel}}).reject(fileAndSentinel=>fileAndSentinel===null).tap(()=>{this.lastUserValue=origContent;this.props.onChange({content:content});}).each(fileAndSentinel=>{imageUploader(fileAndSentinel.file,url=>{this.lastUserValue=origContent;this.props.onChange({content:this.state.textAreaValue.replace(fileAndSentinel.sentinel,url)});});});},this.handleChange=e=>{const newValue=e.currentTarget.value;this.setState({textAreaValue:newValue});const widgets=this.getWidgetsReferencedIn(newValue);if(newValue!==this.props.content){this.props.onChange({content:newValue,widgets});}},this._handleKeyDown=e=>{e.stopPropagation();if(e.key==="Tab"){const textarea=this.textarea.current;const word=perseus.Util.textarea.getWordBeforeCursor(textarea);const matches=word.string.toLowerCase().match(shortcutRegexp);if(matches!=null){const text=matches[1];const widgets=perseus.Widgets.getAllWidgetTypes();const matchingWidgets=widgets.filter(name=>{return name.substring(0,text.length)===text});if(matchingWidgets.length===1){const widgetType=matchingWidgets[0];this._addWidgetToContent(this.props.content,[word.pos.start,word.pos.end+1],widgetType);}e.preventDefault();}}},this._maybeCopyWidgets=e=>{const textarea=e.currentTarget;const selectedText=textarea.value.substring(textarea.selectionStart,textarea.selectionEnd);const widgetIds=___default.default.map(selectedText.match(rWidgetSplit),syntax=>{return perseus.Util.rWidgetParts.exec(syntax)[1]});const widgetData=___default.default.pick(this.serialize().widgets,widgetIds);setPerseusClipboardData({text:selectedText,widgets:widgetData}).catch(err=>perseus.Log.error("failed to copy data to clipboard","Internal",{cause:err}));},this._maybePasteWidgets=async e=>{e.preventDefault();const textarea=e.currentTarget;const{widgets,text:textToBePasted}=await getPerseusClipboardData();const safeWidgetMapping=this._safeWidgetNameMapping(widgets);const safeWidgetData={};for(const[key,data]of Object.entries(widgets)){safeWidgetData[safeWidgetMapping[key]]=data;}const safeText=textToBePasted.replace(rWidgetSplit,syntax=>{const match=perseus.Util.rWidgetParts.exec(syntax);const completeWidget=match[0];const widget=match[1];return completeWidget.replace(widget,safeWidgetMapping[widget])});const selectionStart=textarea.selectionStart;const newContent=this.state.textAreaValue.substr(0,selectionStart)+safeText+this.state.textAreaValue.substr(textarea.selectionEnd);this.lastUserValue=this.state.textAreaValue;this._pendingCursorPos=selectionStart+safeText.length;this.props.onChange({content:newContent,widgets:{...safeWidgetData,...this.getWidgetsReferencedIn(newContent)}});},this._safeWidgetNameMapping=widgetData=>{const widgets=Object.keys(widgetData).map(name=>name.split(" "));const widgetTypes=___default.default.uniq(widgets.map(widget=>widget[0]));const existingWidgets=Object.keys(this.props.widgets).map(name=>name.split(" "));const safeWidgetNums={};___default.default.each(widgetTypes,type=>{safeWidgetNums[type]=___default.default.chain(existingWidgets).filter(existingWidget=>existingWidget[0]===type).map(existingWidget=>+existingWidget[1]+1).max().value();safeWidgetNums[type]=Math.max(safeWidgetNums[type],1);});const safeWidgetMapping={};___default.default.each(widgets,widget=>{const widgetName=widget.join(" ");const widgetType=widget[0];safeWidgetMapping[widgetName]=`${widgetType} ${safeWidgetNums[widgetType]}`;safeWidgetNums[widgetType]++;});return safeWidgetMapping},this._addWidgetToContent=(oldContent,cursorRange,widgetType)=>{const allWidgetIds=___default.default.map(oldContent.match(rWidgetSplit),syntax=>{const match=perseus.Util.rWidgetParts.exec(syntax);const type=match[2];const num=+match[3];return [type,num]});const widgetNum=___default.default.reduce(allWidgetIds,(currentNum,otherId)=>{const[otherType,otherNum]=otherId;if(otherType===widgetType){return Math.max(otherNum+1,currentNum)}return currentNum},1);const id=widgetType+" "+widgetNum;const widgetContent=widgetPlaceholder.replace("{id}",id);const isBlock=perseusCore.CoreWidgetRegistry.getDefaultAlignment(widgetType)==="block";const selectedText=oldContent.slice(cursorRange[0],cursorRange[1]);const prelude=oldContent.slice(0,cursorRange[0]);const postlude=oldContent.slice(cursorRange[1]);const newPrelude=isBlock?makeEndWithAParagraphIfNecessary(prelude):prelude;const newPostlude=isBlock?makeStartWithAParagraphAlways(postlude):postlude;const newContent=newPrelude+widgetContent+newPostlude;const newWidgets={...this.props.widgets};const widgetEditor=perseus.Widgets.getEditor(widgetType);const initializeWidgetOptionsParams={selectedText};const startWidgetOptions=widgetEditor?.initializeWidgetOptions?.(initializeWidgetOptionsParams);const defaultProps=widgetEditor?.defaultProps;newWidgets[id]={options:startWidgetOptions||defaultProps,type:widgetType,version:perseusCore.CoreWidgetRegistry.getCurrentVersion(widgetType)};this.lastUserValue=this.props.content;this._pendingCursorPos=newContent.length-postlude.length;this.props.onChange({content:newContent,widgets:newWidgets});},this._addWidget=widgetType=>{const textarea=this.textarea.current;if(!textarea){return}this._addWidgetToContent(this.props.content,[textarea.selectionStart,textarea.selectionEnd],widgetType);textarea.focus();},this.addTemplate=e=>{const templateType=e.currentTarget.value;if(templateType===""){return}e.currentTarget.value="";let oldContent=this.props.content;oldContent=oldContent.replace(/\n*$/,"\n\n");let template;if(templateType==="table"){template="header 1 | header 2 | header 3\n"+"- | - | -\n"+"data 1 | data 2 | data 3\n"+"data 4 | data 5 | data 6\n"+"data 7 | data 8 | data 9";}else if(templateType==="titledTable"){template="|| **Table title** ||\n"+"header 1 | header 2 | header 3\n"+"- | - | -\n"+"data 1 | data 2 | data 3\n"+"data 4 | data 5 | data 6\n"+"data 7 | data 8 | data 9";}else if(templateType==="alignment"){template="$\\begin{align} \n"+"\\\\\\\\\n"+"\\end{align}$";}else if(templateType==="piecewise"){template="$f(x) = \\begin{cases}\n"+"7 & \\text{if }x=1 \\\\\n"+"f(x-1)+5 & \\text{if }x > 1\n"+"\\end{cases}$";}else if(templateType==="allWidgets"){template=perseus.Widgets.getAllWidgetTypes().map(type=>`[[${perseus.Util.snowman} ${type} 1]]`).join("\n\n");}else if(templateType in this.props.additionalTemplates){template=this.props.additionalTemplates[templateType];}else {throw new perseusCore.PerseusError("Invalid template type: "+templateType,perseusCore.Errors.InvalidInput,{metadata:{templateType}})}const newContent=oldContent+template;this.lastUserValue=this.props.content;this.props.onChange({content:newContent});},this.getSaveWarnings=()=>{const widgetIds=___default.default.intersection(this.widgetIds,Object.keys(this.refs));const warnings=___default.default(widgetIds).chain().map(id=>{const issuesFunc=this.refs[id].getSaveWarnings;const issues=issuesFunc?issuesFunc():[];return ___default.default.map(issues,issue=>id+": "+issue)}).flatten(true).value();return warnings},this.focus=()=>{const textarea=this.textarea.current;if(textarea){textarea.focus();}},this.focusAndMoveToEnd=()=>{this.focus();const textarea=this.textarea.current;if(textarea){textarea.selectionStart=textarea.value.length;textarea.selectionEnd=textarea.value.length;}};}}Editor.defaultProps={content:"",placeholder:"",widgets:{},images:{},disabled:false,widgetEnabled:true,showWordCount:false,warnNoPrompt:false,warnNoWidgets:false,additionalTemplates:{},apiOptions:perseus.ApiOptions.defaults};
|
|
1531
|
+
const widgetPlaceholder="[[☃ {id}]]";const widgetRegExp="(\\[\\[☃ {id}\\]\\])";const rWidgetSplit=new RegExp(widgetRegExp.replace("{id}","[a-z-]+ [0-9]+"),"g");const shortcutRegexp=/^\[\[([a-z-]+)$/;const ENDS_WITH_A_PARAGRAPH=/(?:\n{2,}|^\n*)$/;const TRAILING_NEWLINES=/(\n*)$/;const LEADING_NEWLINES=/^(\n*)/;const commafyInteger=n=>{let str=n.toString();if(str.length>=5){str=str.replace(/(\d)(?=(\d{3})+$)/g,"$1{,}");}return str};const makeEndWithAParagraphIfNecessary=content=>{if(!ENDS_WITH_A_PARAGRAPH.test(content)){const match=TRAILING_NEWLINES.exec(content);if(match){const newlines=match[1];return content+"\n\n".slice(0,2-newlines.length)}}return content};const makeStartWithAParagraphAlways=content=>{const match=LEADING_NEWLINES.exec(content);if(!match){return content}const newlines=match[1];return "\n\n".slice(0,2-newlines.length)+content};const IMAGE_REGEX$1=/!\[[^\]]*\]\(([^\s)]+)[^)]*\)/g;const allMatches=function(regex,str){const result=[];while(true){const match=regex.exec(str);if(!match){break}result.push(match);}return result};const imageUrlsFromContent=function(content){return allMatches(IMAGE_REGEX$1,content).map(capture=>capture[1])};class Editor extends React__namespace.Component{static getDerivedStateFromProps(props,state){return {textAreaValue:props.content,rememberedWidgetsForUndo:{...state.rememberedWidgetsForUndo,...props.widgets}}}componentDidMount(){this.lastUserValue=null;this._sizeImages(this.props);}componentDidUpdate(prevProps){const textarea=this.textarea.current;if(this.lastUserValue!=null&&textarea){textarea.focus();textarea.value=this.lastUserValue;textarea.selectionStart=0;textarea.setSelectionRange(0,prevProps.content.length);document.execCommand("insertText",false,this.props.content);this.lastUserValue=null;}if(this._pendingCursorPos!=null&&textarea){perseus.Util.textarea.moveCursor(textarea,this._pendingCursorPos);this._pendingCursorPos=null;}if(this.props.content!==prevProps.content){this._sizeImages(this.props);}}getWidgetEditor(id,type){if(!perseus.Widgets.getEditor(type)){return}return jsxRuntimeExports.jsx(WidgetEditor,{widgetInfo:this.props.widgets[id],ref:id,id:id,onChange:this._handleWidgetEditorChange.bind(this,id),onRemove:this._handleWidgetEditorRemove.bind(this,id),apiOptions:this.props.apiOptions,widgetIsOpen:this.props.widgetIsOpen},id)}getWidgetsReferencedIn(content){const referencedWidgetIds=perseusCore.getWidgetIdsFromContent(content);const allWidgets={...this.state.rememberedWidgetsForUndo,...this.props.widgets};const referencedWidgets={};for(const id of referencedWidgetIds){if(allWidgets[id]!=null){referencedWidgets[id]=allWidgets[id];}}return referencedWidgets}serialize(){const widgets={};const widgetIds=___default.default.intersection(this.widgetIds,Object.keys(this.refs));___default.default.each(widgetIds,id=>{widgets[id]=this.refs[id].serialize();});return {content:this.props.content,images:this.props.images,widgets}}render(){let pieces;let widgets;let underlayPieces;let templatesDropDown;let widgetsAndTemplates;let wordCountDisplay;if(this.props.showWordCount){const numChars=perseus.PerseusMarkdown.characterCount(this.props.content);const numWords=Math.floor(numChars/6);wordCountDisplay=jsxRuntimeExports.jsx("span",{className:"perseus-editor-word-count",title:"~"+commafyInteger(numWords)+" words ("+commafyInteger(numChars)+" characters)",children:commafyInteger(numWords)});}if(this.props.widgetEnabled){pieces=this.props.content.split(rWidgetSplit);widgets={};underlayPieces=[];for(let i=0;i<pieces.length;i++){if(i%2===0){underlayPieces.push(pieces[i]);}else {const match=perseus.Util.rWidgetParts.exec(pieces[i]);if(match!=null){const id=match[1];const type=match[2];const duplicate=id in widgets;widgets[id]=this.getWidgetEditor(id,type);const classes=(duplicate||!widgets[id]?"error ":"")+("");const key=duplicate?i:id;underlayPieces.push(jsxRuntimeExports.jsx("b",{className:classes,children:pieces[i]},key));}}}this.widgetIds=Object.keys(widgets);const insertTemplateString="Insert template…";templatesDropDown=jsxRuntimeExports.jsxs("select",{onChange:this.addTemplate,"data-testid":"editor__template-select",children:[jsxRuntimeExports.jsx("option",{value:"",children:insertTemplateString}),jsxRuntimeExports.jsx("option",{disabled:true,children:"--"}),jsxRuntimeExports.jsx("option",{value:"table",children:"Table"}),jsxRuntimeExports.jsx("option",{value:"titledTable",children:"Titled table"}),jsxRuntimeExports.jsx("option",{value:"alignment",children:"Aligned equations"}),jsxRuntimeExports.jsx("option",{value:"piecewise",children:"Piecewise function"}),Object.keys(this.props.additionalTemplates).length>0&&jsxRuntimeExports.jsx("option",{disabled:true,children:"--"}),Object.entries(this.props.additionalTemplates).map(([key])=>jsxRuntimeExports.jsx("option",{value:key,children:key},key))]});const widgetNodes=Object.values(widgets);widgetsAndTemplates=jsxRuntimeExports.jsxs("div",{className:"perseus-editor-widgets",children:[jsxRuntimeExports.jsxs("div",{className:"perseus-editor-widgets-selectors",children:[jsxRuntimeExports.jsx(WidgetSelect,{onChange:this._addWidget}),templatesDropDown,wordCountDisplay]}),widgetNodes]});}else {underlayPieces=[this.props.content];}underlayPieces.push(jsxRuntimeExports.jsx("br",{},"end"));const completeTextarea=[jsxRuntimeExports.jsx("div",{className:"perseus-textarea-underlay",ref:this.underlay,children:underlayPieces},"underlay"),jsxRuntimeExports.jsx("textarea",{ref:this.textarea,"aria-label":"Markdown content",onChange:this.handleChange,onKeyDown:this._handleKeyDown,onCopy:this._maybeCopyWidgets,onCut:this._maybeCopyWidgets,onPaste:this._maybePasteWidgets,placeholder:this.props.placeholder,disabled:this.props.disabled,value:this.state.textAreaValue},"textarea")];let textareaWrapper;if(this.props.imageUploader){textareaWrapper=jsxRuntimeExports.jsx(DragTarget,{onDrop:this.handleDrop,className:"perseus-textarea-pair",children:completeTextarea});}else {textareaWrapper=jsxRuntimeExports.jsx("div",{className:"perseus-textarea-pair",children:completeTextarea});}const contentWithoutWidgets=this.props.content.replace(/\[\[\u2603 (([a-z-]+) ([0-9]+))\]\]/g,"");const noPrompt=contentWithoutWidgets.trim().length===0;const noWidgets=!/\[\[\u2603 (([a-z-]+) ([0-9]+))\]\]/g.test(this.props.content);const warningStyle={borderTop:"none",padding:4,backgroundColor:"pink"};const editingDisabled=this.props.apiOptions.editingDisabled;return jsxRuntimeExports.jsxs("div",{"data-testid":"perseus-single-editor",className:"perseus-single-editor "+(this.props.className||"")+(editingDisabled?" perseus-editor-disabled":""),children:[textareaWrapper,this.props.warnNoPrompt&&noPrompt&&jsxRuntimeExports.jsx("div",{style:warningStyle,children:"Graded Groups should contain a prompt"}),this.props.warnNoWidgets&&noWidgets&&jsxRuntimeExports.jsx("div",{style:warningStyle,children:"Graded Groups should contain at least one widget"}),widgetsAndTemplates]})}constructor(...args){super(...args),this._pendingCursorPos=null,this.underlay=React__namespace.createRef(),this.textarea=React__namespace.createRef(),this.state={textAreaValue:this.props.content,rememberedWidgetsForUndo:{}},this._handleWidgetEditorChange=(id,newWidgetInfo)=>{const widgets=Object.assign({},this.props.widgets);widgets[id]=Object.assign({},widgets[id],newWidgetInfo);this.props.onChange({widgets});},this._handleWidgetEditorRemove=id=>{if(!confirm("Are you sure you want to delete this item?")){return}const textarea=this.textarea.current;const re=new RegExp(widgetRegExp.replace("{id}",id),"gm");this.props.onChange({content:textarea?.value.replace(re,"")});},this._sizeImages=props=>{const imageUrls=imageUrlsFromContent(props.content);const images=___default.default.pick(props.images,imageUrls);const newImageUrls=___default.default.filter(imageUrls,url=>!images[url]);___default.default.each(newImageUrls,url=>{perseus.Util.getImageSize(url,(width,height)=>{images[url]={width:width,height:height};props.onChange({images:___default.default.clone(images)});});});},this.handleDrop=e=>{const{imageUploader}=this.props;let content=this.state.textAreaValue||"";const dataTransfer="dataTransfer"in e?e.dataTransfer:null;if(!dataTransfer||!imageUploader){return}const files=dataTransfer.files;if(files.length===0){const imageUrl=dataTransfer.getData("URL");if(imageUrl){const newContent=content+"\n\n";this.lastUserValue=this.props.content;this.props.onChange({content:newContent});}return}const origContent=this.state.textAreaValue;___default.default(files).chain().map(function(file){if(!file.type.match("image.*")){return null}const sentinel="☃ "+___default.default.uniqueId("image_");content+="\n\n";return {file:file,sentinel:sentinel}}).reject(fileAndSentinel=>fileAndSentinel===null).tap(()=>{this.lastUserValue=origContent;this.props.onChange({content:content});}).each(fileAndSentinel=>{imageUploader(fileAndSentinel.file,url=>{this.lastUserValue=origContent;this.props.onChange({content:this.state.textAreaValue.replace(fileAndSentinel.sentinel,url)});});});},this.handleChange=e=>{const newValue=e.currentTarget.value;this.setState({textAreaValue:newValue});const widgets=this.getWidgetsReferencedIn(newValue);if(newValue!==this.props.content){this.props.onChange({content:newValue,widgets});}},this._handleKeyDown=e=>{e.stopPropagation();if(e.key==="Tab"){const textarea=this.textarea.current;const word=perseus.Util.textarea.getWordBeforeCursor(textarea);const matches=word.string.toLowerCase().match(shortcutRegexp);if(matches!=null){const text=matches[1];const widgets=perseus.Widgets.getAllWidgetTypes();const matchingWidgets=widgets.filter(name=>{return name.substring(0,text.length)===text});if(matchingWidgets.length===1){const widgetType=matchingWidgets[0];this._addWidgetToContent(this.props.content,[word.pos.start,word.pos.end+1],widgetType);}e.preventDefault();}}},this._maybeCopyWidgets=e=>{const textarea=e.currentTarget;const selectedText=textarea.value.substring(textarea.selectionStart,textarea.selectionEnd);const widgetIds=___default.default.map(selectedText.match(rWidgetSplit),syntax=>{return perseus.Util.rWidgetParts.exec(syntax)[1]});const widgetData=___default.default.pick(this.serialize().widgets,widgetIds);setPerseusClipboardData({text:selectedText,widgets:widgetData}).catch(err=>perseus.Log.error("failed to copy data to clipboard","Internal",{cause:err}));},this._maybePasteWidgets=async e=>{e.preventDefault();const textarea=e.currentTarget;const{widgets,text:textToBePasted}=await getPerseusClipboardData();const safeWidgetMapping=this._safeWidgetNameMapping(widgets);const safeWidgetData={};for(const[key,data]of Object.entries(widgets)){safeWidgetData[safeWidgetMapping[key]]=data;}const safeText=textToBePasted.replace(rWidgetSplit,syntax=>{const match=perseus.Util.rWidgetParts.exec(syntax);const completeWidget=match[0];const widget=match[1];return completeWidget.replace(widget,safeWidgetMapping[widget])});const selectionStart=textarea.selectionStart;const newContent=this.state.textAreaValue.substr(0,selectionStart)+safeText+this.state.textAreaValue.substr(textarea.selectionEnd);this.lastUserValue=this.state.textAreaValue;this._pendingCursorPos=selectionStart+safeText.length;this.props.onChange({content:newContent,widgets:{...safeWidgetData,...this.getWidgetsReferencedIn(newContent)}});},this._safeWidgetNameMapping=widgetData=>{const widgets=Object.keys(widgetData).map(name=>name.split(" "));const widgetTypes=___default.default.uniq(widgets.map(widget=>widget[0]));const existingWidgets=Object.keys(this.props.widgets).map(name=>name.split(" "));const safeWidgetNums={};___default.default.each(widgetTypes,type=>{safeWidgetNums[type]=___default.default.chain(existingWidgets).filter(existingWidget=>existingWidget[0]===type).map(existingWidget=>+existingWidget[1]+1).max().value();safeWidgetNums[type]=Math.max(safeWidgetNums[type],1);});const safeWidgetMapping={};___default.default.each(widgets,widget=>{const widgetName=widget.join(" ");const widgetType=widget[0];safeWidgetMapping[widgetName]=`${widgetType} ${safeWidgetNums[widgetType]}`;safeWidgetNums[widgetType]++;});return safeWidgetMapping},this._addWidgetToContent=(oldContent,cursorRange,widgetType)=>{const allWidgetIds=___default.default.map(oldContent.match(rWidgetSplit),syntax=>{const match=perseus.Util.rWidgetParts.exec(syntax);const type=match[2];const num=+match[3];return [type,num]});const widgetNum=___default.default.reduce(allWidgetIds,(currentNum,otherId)=>{const[otherType,otherNum]=otherId;if(otherType===widgetType){return Math.max(otherNum+1,currentNum)}return currentNum},1);const id=widgetType+" "+widgetNum;const widgetContent=widgetPlaceholder.replace("{id}",id);const isBlock=perseusCore.CoreWidgetRegistry.getDefaultAlignment(widgetType)==="block";const selectedText=oldContent.slice(cursorRange[0],cursorRange[1]);const prelude=oldContent.slice(0,cursorRange[0]);const postlude=oldContent.slice(cursorRange[1]);const newPrelude=isBlock?makeEndWithAParagraphIfNecessary(prelude):prelude;const newPostlude=isBlock?makeStartWithAParagraphAlways(postlude):postlude;const newContent=newPrelude+widgetContent+newPostlude;const newWidgets={...this.props.widgets};const widgetEditor=perseus.Widgets.getEditor(widgetType);const initializeWidgetOptionsParams={selectedText};const startWidgetOptions=widgetEditor?.initializeWidgetOptions?.(initializeWidgetOptionsParams);const defaultProps=widgetEditor?.defaultProps;newWidgets[id]={options:startWidgetOptions||defaultProps,type:widgetType,version:perseusCore.CoreWidgetRegistry.getCurrentVersion(widgetType)};this.lastUserValue=this.props.content;this._pendingCursorPos=newContent.length-postlude.length;this.props.onChange({content:newContent,widgets:newWidgets});},this._addWidget=widgetType=>{const textarea=this.textarea.current;if(!textarea){return}this._addWidgetToContent(this.props.content,[textarea.selectionStart,textarea.selectionEnd],widgetType);textarea.focus();},this.addTemplate=e=>{const templateType=e.currentTarget.value;if(templateType===""){return}e.currentTarget.value="";let oldContent=this.props.content;oldContent=oldContent.replace(/\n*$/,"\n\n");let template;if(templateType==="table"){template="header 1 | header 2 | header 3\n"+"- | - | -\n"+"data 1 | data 2 | data 3\n"+"data 4 | data 5 | data 6\n"+"data 7 | data 8 | data 9";}else if(templateType==="titledTable"){template="|| **Table title** ||\n"+"header 1 | header 2 | header 3\n"+"- | - | -\n"+"data 1 | data 2 | data 3\n"+"data 4 | data 5 | data 6\n"+"data 7 | data 8 | data 9";}else if(templateType==="alignment"){template="$\\begin{align} \n"+"\\\\\\\\\n"+"\\end{align}$";}else if(templateType==="piecewise"){template="$f(x) = \\begin{cases}\n"+"7 & \\text{if }x=1 \\\\\n"+"f(x-1)+5 & \\text{if }x > 1\n"+"\\end{cases}$";}else if(templateType in this.props.additionalTemplates){template=this.props.additionalTemplates[templateType];}else {throw new perseusCore.PerseusError("Invalid template type: "+templateType,perseusCore.Errors.InvalidInput,{metadata:{templateType}})}const newContent=oldContent+template;this.lastUserValue=this.props.content;this.props.onChange({content:newContent});},this.getSaveWarnings=()=>{const widgetIds=___default.default.intersection(this.widgetIds,Object.keys(this.refs));const warnings=___default.default(widgetIds).chain().map(id=>{const issuesFunc=this.refs[id].getSaveWarnings;const issues=issuesFunc?issuesFunc():[];return ___default.default.map(issues,issue=>id+": "+issue)}).flatten(true).value();return warnings},this.focus=()=>{const textarea=this.textarea.current;if(textarea){textarea.focus();}},this.focusAndMoveToEnd=()=>{this.focus();const textarea=this.textarea.current;if(textarea){textarea.selectionStart=textarea.value.length;textarea.selectionEnd=textarea.value.length;}};}}Editor.defaultProps={content:"",placeholder:"",widgets:{},images:{},disabled:false,widgetEnabled:true,showWordCount:false,warnNoPrompt:false,warnNoWidgets:false,additionalTemplates:{},apiOptions:perseus.ApiOptions.defaults};
|
|
1532
1532
|
|
|
1533
1533
|
const SEVERITY_ERROR=1;const SEVERITY_WARNING=2;const SEVERITY_GUIDELINE=3;const SEVERITY_MAP={[SEVERITY_ERROR]:"high",[SEVERITY_WARNING]:"medium",[SEVERITY_GUIDELINE]:"low"};const WARNINGS={inaccessibleWidget:(widgetType,widgetId)=>({id:`${widgetId} inaccessible`,instanceId:`inaccessible-widget-${widgetId}`,description:`This ${widgetType} widget (${widgetId}) is inaccessible. Consider using an alternative to support all learners. Please check out the following documentation on compliant widget options.`,helpUrl:"https://khanacademy.atlassian.net/wiki/spaces/LC/pages/1909489691/Widget+Fundamentals",help:"Widget Fundamentals",impact:"medium",message:"Selecting inaccessible widgets for a practice item will result in this exercise being hidden from users with 'Hide visually dependant content' setting set to true. Please select another widget or create an alternative practice item."}),genericLinterWarning:(rule,message,severity)=>({id:rule,description:message,help:"Learn more about best practices for authoring items",helpUrl:"https://docs.google.com/document/d/1N13f4sY-7EXWDwQ04ivA9vJBVvPPd60qjBT73B4NHuM/edit?tab=t.0",impact:SEVERITY_MAP[severity??SEVERITY_GUIDELINE],message:message}),texError:(math,errorMessage,index)=>({id:`tex-debug-info-${index+1}`,description:"If your math doesn't display correctly, these errors might help you troubleshoot. Message #content-kitchen for help.",help:"Learn more about TeX syntax",helpUrl:"https://khan.github.io/KaTeX/",impact:"medium",message:`Error in: ${math}
|
|
1534
1534
|
|
|
@@ -1594,7 +1594,7 @@ function buildDeviceApiOptions(inputs){return {...perseus.ApiOptions.defaults,..
|
|
|
1594
1594
|
|
|
1595
1595
|
function lintRenderer(content,widgets){const parsed=perseus.PerseusMarkdown.parse(content,{});const linterContext={content,widgets,stack:[]};const linterIssues=(PerseusLinter__namespace.runLinter(parsed,linterContext,false)??[]).map(warning=>{if(warning.rule==="inaccessible-widget"){return WARNINGS.inaccessibleWidget(warning.metadata?.widgetType??"unknown",warning.metadata?.widgetId??"unknown")}return WARNINGS.genericLinterWarning(warning.rule,warning.message,warning.severity)});const texIssues=detectTexErrors(content).map((error,i)=>WARNINGS.texError(error.math,error.message,i));const ruleCounts=new Map;return [...linterIssues,...texIssues].map(issue=>{const ordinal=ruleCounts.get(issue.id)??0;ruleCounts.set(issue.id,ordinal+1);return {...issue,instanceId:issue.instanceId??`${issue.id}-${ordinal}`}})}function gatherLinterIssues(question,hints,hostIssues){const questionIssues=lintRenderer(question?.content??"",question?.widgets);const hintIssues=hints.flatMap((hint,i)=>lintRenderer(hint.content??"",hint.widgets??undefined).map(issue=>({...issue,id:`hint-${i+1}-${issue.id}`,instanceId:`hint-${i+1}-${getIssueKey(issue)}`,message:`Hint ${i+1}: ${issue.message}`})));return [...hostIssues??[],...questionIssues,...hintIssues]}
|
|
1596
1596
|
|
|
1597
|
-
const{HUD}=perseus.components;class EditorPage extends React__namespace.Component{componentDidMount(){this.setState({issues:gatherLinterIssues(this.props.question,this.props.hints,this.props.issues)});}getSnapshotBeforeUpdate(prevProps){if(!prevProps.jsonMode&&this.props.jsonMode){return this.itemEditor.current?.serialize()??{}}return null}componentDidUpdate(previousProps,prevState,snapshot){if(snapshot){this.setState({json:snapshot});return}if(!___default.default.isEqual(previousProps.question,this.props.question)||!___default.default.isEqual(previousProps.answerArea,this.props.answerArea)||!___default.default.isEqual(previousProps.hints,this.props.hints)){this.syncJsonStateFromProps();}const questionOrHintsChanged=previousProps.question?.content!==this.props.question?.content||previousProps.question?.widgets!==this.props.question?.widgets||!___default.default.isEqual(previousProps.hints,this.props.hints);if(questionOrHintsChanged){this.setState({issues:gatherLinterIssues(this.props.question,this.props.hints,this.props.issues)});}}syncJsonStateFromProps(){this.setState({json:{question:this.props.question,answerArea:this.props.answerArea,hints:this.props.hints}});}getSaveWarnings(){return this.itemEditor.current?.getSaveWarnings()}serialize(){if(this.props.jsonMode){return this.state.json}invariant__default.default(this.itemEditor.current,"cannot serialize EditorPage without ItemEditor");return this.itemEditor.current.serialize()}render(){let className="framework-perseus";const editingDisabled=this.props.apiOptions?.editingDisabled??false;const touch=this.props.previewDevice==="phone"||this.props.previewDevice==="tablet";const deviceBasedApiOptions=this.deriveDeviceApiOptions({apiOptions:this.props.apiOptions,touch});
|
|
1597
|
+
const{HUD}=perseus.components;class EditorPage extends React__namespace.Component{componentDidMount(){this.setState({issues:gatherLinterIssues(this.props.question,this.props.hints,this.props.issues)});}getSnapshotBeforeUpdate(prevProps){if(!prevProps.jsonMode&&this.props.jsonMode){return this.itemEditor.current?.serialize()??{}}return null}componentDidUpdate(previousProps,prevState,snapshot){if(snapshot){this.setState({json:snapshot});return}if(!___default.default.isEqual(previousProps.question,this.props.question)||!___default.default.isEqual(previousProps.answerArea,this.props.answerArea)||!___default.default.isEqual(previousProps.hints,this.props.hints)){this.syncJsonStateFromProps();}const questionOrHintsChanged=previousProps.question?.content!==this.props.question?.content||previousProps.question?.widgets!==this.props.question?.widgets||!___default.default.isEqual(previousProps.hints,this.props.hints);if(questionOrHintsChanged){this.setState({issues:gatherLinterIssues(this.props.question,this.props.hints,this.props.issues)});}}syncJsonStateFromProps(){this.setState({json:{question:this.props.question,answerArea:this.props.answerArea,hints:this.props.hints}});}getSaveWarnings(){return this.itemEditor.current?.getSaveWarnings()}serialize(){if(this.props.jsonMode){return this.state.json}invariant__default.default(this.itemEditor.current,"cannot serialize EditorPage without ItemEditor");return this.itemEditor.current.serialize()}render(){let className="framework-perseus";const editingDisabled=this.props.apiOptions?.editingDisabled??false;const touch=this.props.previewDevice==="phone"||this.props.previewDevice==="tablet";const deviceBasedApiOptions=this.deriveDeviceApiOptions({apiOptions:this.props.apiOptions,touch});if(deviceBasedApiOptions.isMobile){className+=" "+perseus.ClassNames.MOBILE;}return jsxRuntimeExports.jsx(perseus.Dependencies.DependenciesContext.Provider,{value:this.props.dependencies,children:jsxRuntimeExports.jsx(ItemEditorContext.Provider,{value:{question:this.props.question,onEditorChange:this.handleEditorChange,editingDisabled:this.props.apiOptions?.editingDisabled},children:jsxRuntimeExports.jsxs("div",{id:"perseus",className:className,children:[jsxRuntimeExports.jsx("div",{style:{marginBlockEnd:10},children:!this.props.jsonMode&&jsxRuntimeExports.jsx(HUD,{message:"Style warnings",enabled:this.state.highlightLint,onClick:()=>{this.setState({highlightLint:!this.state.highlightLint});}})}),this.props.jsonMode?jsxRuntimeExports.jsx("div",{children:jsxRuntimeExports.jsx(JsonEditor,{multiLine:true,value:this.state.json,parser:perseusCore.parseAndMigratePerseusItem,onChange:this.changeJSON,editingDisabled:editingDisabled})}):jsxRuntimeExports.jsxs(A11yContext.Provider,{value:createA11yContextValue({setIssueHighlight:this.setIssueHighlight,a11yScanningEnabled:this.state.a11yScanningEnabled,setA11yScanningEnabled:this.setA11yScanningEnabled,highlightInstanceIds:this.state.highlightInstanceIds,onA11yReport:this.handleA11yReport,axeCoreIssues:this.state.axeCoreIssues}),children:[jsxRuntimeExports.jsx("div",{className:"perseus-editor-table",children:jsxRuntimeExports.jsx("div",{className:"perseus-editor-row",children:jsxRuntimeExports.jsx("div",{className:"perseus-editor-left-cell",children:jsxRuntimeExports.jsx(IssuesPanel,{issues:this.state.issues})})})}),jsxRuntimeExports.jsx(ItemEditor,{ref:this.itemEditor,itemId:this.props.itemId,question:this.props.question,hints:this.props.hints,answerArea:this.props.answerArea,imageUploader:this.props.imageUploader,onChange:this.handleChange,deviceType:this.props.previewDevice,widgetIsOpen:this.state.widgetsAreOpen,apiOptions:deviceBasedApiOptions,previewURL:this.props.previewURL,additionalTemplates:this.props.additionalTemplates,highlightLint:this.state.highlightLint,problemNum:this.props.problemNum})]})]})})})}constructor(props){super(props),this.itemEditor=React__namespace.createRef(),this.deriveDeviceApiOptions=createDeviceApiOptionsDeriver(),this.setIssueHighlight=(instanceId,highlighted)=>{this.setState(prevState=>{const current=prevState.highlightInstanceIds;if(highlighted===current.includes(instanceId)){return null}return {highlightInstanceIds:highlighted?[...current,instanceId]:current.filter(id=>id!==instanceId)}});},this.handleA11yReport=report=>{this.setState({axeCoreIssues:report?[...report.violations,...report.needsReview]:[]});},this.setA11yScanningEnabled=enabled=>{this.setState({a11yScanningEnabled:enabled});},this.handleChange=toChange=>{const newProps=___default.default(this.props).pick("question","hints","answerArea");___default.default(newProps).extend(toChange);this.props.onChange(newProps);},this.handleEditorChange=newProps=>{this.handleChange({question:{...this.props.question,...newProps}});},this.changeJSON=newJson=>{this.setState({json:newJson});this.props.onChange(newJson);};this.state={json:___default.default.pick(this.props,"question","answerArea","hints"),highlightLint:true,widgetsAreOpen:this.props.widgetsAreOpen??true,issues:[],axeCoreIssues:[],a11yScanningEnabled:false,highlightInstanceIds:[]};}}EditorPage.defaultProps={answerArea:perseusCore.getDefaultAnswerArea(),hints:[],jsonMode:false,onChange:()=>{}};
|
|
1598
1598
|
|
|
1599
1599
|
function ContentPreview({question,apiOptions,seamless,linterContext,legacyPerseusLint,previewDevice,reviewMode}){const i18n=perseus.usePerseusI18n();const isMobile=previewDevice!=="desktop";const className=isMobile?"perseus-mobile":"";return jsxRuntimeExports.jsx(wonderBlocksCore.View,{className:`framework-perseus ${className}`,style:[styles$P.container,!seamless?styles$P.gutter:undefined],children:jsxRuntimeExports.jsx(keypadContext.StatefulKeypadContextProvider,{children:jsxRuntimeExports.jsx(keypadContext.KeypadContext.Consumer,{children:({setKeypadActive,keypadElement,setKeypadElement})=>jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(perseus.UserInputManager,{widgets:question?.widgets||{},problemNum:0,children:({userInput,handleUserInput,initializeUserInput})=>jsxRuntimeExports.jsx(perseus.Renderer,{strings:i18n.strings,apiOptions:{...apiOptions,isMobile},keypadElement:keypadElement,linterContext:linterContext,legacyPerseusLint:legacyPerseusLint,userInput:userInput,handleUserInput:handleUserInput,initializeUserInput:initializeUserInput,reviewMode:reviewMode,...question})}),jsxRuntimeExports.jsx(mathInput.MobileKeypad,{onAnalyticsEvent:()=>Promise.resolve(),onDismiss:()=>setKeypadActive(false),onElementMounted:setKeypadElement})]})})})})}const styles$P=aphrodite.StyleSheet.create({container:{padding:wonderBlocksTokens.spacing.xxxSmall_4,containerType:"inline-size",containerName:"perseus-root"},gutter:{marginInlineEnd:lintGutterWidth}});
|
|
1600
1600
|
|
|
@@ -1938,7 +1938,7 @@ const cardInputStyle={flexGrow:1};function CardEditor({index,value,isFirst,isLas
|
|
|
1938
1938
|
|
|
1939
1939
|
var styles = {"editor":"perseus_nJ-Uq5eT","section":"perseus_vTiphIsj","cards":"perseus_xhRr18kA","addCard":"perseus_cwHzt54c"};
|
|
1940
1940
|
|
|
1941
|
-
const
|
|
1941
|
+
const minCards=2;const defaultOptions=perseusCore.sorterLogic.defaultWidgetOptions;const layoutOptions={horizontal:"Horizontal",vertical:"Vertical"};const SorterEditor=React__namespace.forwardRef(function SorterEditor({correct=defaultOptions.correct,layout=defaultOptions.layout,padding=defaultOptions.padding,onChange},ref){React__namespace.useImperativeHandle(ref,()=>({serialize:()=>{return {correct,layout,padding}},getSaveWarnings:()=>{const warnings=[];if(correct.length<minCards){warnings.push(`Sorter requires at least ${minCards} cards.`);}if(correct.some(card=>card.trim()==="")){warnings.push("Sorter cards cannot be blank.");}return warnings}}),[correct,layout,padding]);const onCardChange=(index,value)=>{const newCorrect=[...correct];newCorrect[index]=value;onChange({correct:newCorrect});};const onAddCard=()=>{if(correct.length>=perseusCore.SORTER_MAX_CARDS){return}onChange({correct:[...correct,""]});};const onMoveCard=(index,offset)=>{const newCorrect=[...correct];const newIndex=index+offset;[newCorrect[index],newCorrect[newIndex]]=[newCorrect[newIndex],newCorrect[index]];onChange({correct:newCorrect});};const onDeleteCard=index=>{onChange({correct:correct.filter((card,i)=>i!==index)});};return jsxRuntimeExports.jsxs(wonderBlocksCore.View,{className:styles.editor,children:[jsxRuntimeExports.jsxs(wonderBlocksCore.View,{className:styles.section,children:[jsxRuntimeExports.jsxs("div",{children:["Correct answer"," ",jsxRuntimeExports.jsxs(InfoTip,{children:[jsxRuntimeExports.jsx("p",{children:"Enter the correct answer (in the correct order) here. The preview on the right will have the cards in a randomized order, which is how the student will see them."}),jsxRuntimeExports.jsx("p",{children:"For horizontal orientation, the top input represents the leftmost card and the bottom input represents the rightmost card."})]})]}),jsxRuntimeExports.jsx(wonderBlocksCore.View,{tag:"ol",className:styles.cards,children:correct.map((card,i)=>jsxRuntimeExports.jsx(CardEditor,{index:i,value:card,isFirst:i===0,isLast:i===correct.length-1,onChange:value=>onCardChange(i,value),onMoveUp:()=>onMoveCard(i,-1),onMoveDown:()=>onMoveCard(i,1),onDelete:()=>onDeleteCard(i)},i))}),jsxRuntimeExports.jsx(Button__default.default,{kind:"tertiary",startIcon:plusCircle__default.default,className:styles.addCard,disabled:correct.length>=perseusCore.SORTER_MAX_CARDS,onClick:onAddCard,children:"Add a card"})]}),jsxRuntimeExports.jsxs(wonderBlocksCore.View,{children:[jsxRuntimeExports.jsx(wonderBlocksLabeledField.LabeledField,{label:"Layout",field:jsxRuntimeExports.jsx(TypedSingleSelect,{options:layoutOptions,selectedValue:layout,onChange:newLayout=>onChange({layout:newLayout})})}),jsxRuntimeExports.jsx(InfoTip,{children:jsxRuntimeExports.jsx("p",{children:"Use the horizontal layout for short text and small images. The vertical layout is best for longer text and larger images."})})]}),jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx(wonderBlocksForm.Checkbox,{label:"Padding",checked:padding,onChange:value=>{onChange({padding:value});}}),jsxRuntimeExports.jsx(InfoTip,{children:jsxRuntimeExports.jsx("p",{children:"Padding is good for text, but not needed for images."})})]})]})});var SorterEditor$1 = Object.assign(SorterEditor,{defaultProps:defaultOptions});
|
|
1942
1942
|
|
|
1943
1943
|
const{NumberInput}=perseus.components;const Table=perseus.TableWidget.widget;class TableEditor extends React__namespace.Component{render(){const tableProps={options:{headers:this.props.headers,rows:this.props.rows,columns:this.props.columns,answers:this.props.answers},onChange:this.props.onChange,userInput:this.props.answers,handleUserInput:userInput=>{if(this.props.apiOptions?.editingDisabled){return}this.props.onChange({answers:userInput});},apiOptions:this.props.apiOptions,editableHeaders:true,onFocus:()=>{},onBlur:()=>{},trackInteraction:()=>{},Editor:Editor};return jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx("div",{className:"perseus-widget-row",children:jsxRuntimeExports.jsxs("label",{children:["Number of columns:"," ",jsxRuntimeExports.jsx(NumberInput,{ref:this.numberOfColumns,value:this.props.columns,onChange:val=>{if(val){this.onSizeInput(this.props.rows,val);}},useArrowKeys:true})]})}),jsxRuntimeExports.jsx("div",{className:"perseus-widget-row",children:jsxRuntimeExports.jsxs("label",{children:["Number of rows:"," ",jsxRuntimeExports.jsx(NumberInput,{ref:"numberOfRows",value:this.props.rows,onChange:val=>{if(val){this.onSizeInput(val,this.props.columns);}},useArrowKeys:true})]})}),jsxRuntimeExports.jsxs("div",{children:[" ","Table of answers:"," ",jsxRuntimeExports.jsx(InfoTip,{children:jsxRuntimeExports.jsx("p",{children:"The student has to fill out all cells in the table. For partially filled tables create a table using the template, and insert text input boxes as desired."})})]}),jsxRuntimeExports.jsx("div",{children:jsxRuntimeExports.jsx(Table,{...tableProps})})]})}constructor(...args){super(...args),this.numberOfColumns=React__namespace.createRef(),this.focus=()=>{this.numberOfColumns.current?.focus();},this.onSizeInput=(numRawRows,numRawColumns)=>{let rows=+numRawRows||0;let columns=+numRawColumns||0;rows=Math.min(Math.max(1,rows),30);columns=Math.min(Math.max(1,columns),6);const oldColumns=this.props.columns;const oldRows=this.props.rows;const answers=this.props.answers;if(rows<=oldRows){answers.length=rows;}else {___default.default(rows-oldRows).times(function(){answers.push(perseus.Util.stringArrayOfSize(oldColumns));});}function fixColumnSizing(array){if(columns<=oldColumns){array.length=columns;}else {___default.default(columns-oldColumns).times(function(){array.push("");});}}const headers=this.props.headers;fixColumnSizing(headers);___default.default.each(answers,fixColumnSizing);this.props.onChange({rows:rows,columns:columns,answers:answers,headers:headers});},this.serialize=()=>{const json=___default.default.pick(this.props,"headers","rows","columns");return ___default.default.extend({},json,{answers:this.props.answers.map(___default.default.clone)})};}}TableEditor.propTypes={rows:PropTypes__default.default.number,columns:PropTypes__default.default.number,headers:PropTypes__default.default.arrayOf(PropTypes__default.default.string),answers:PropTypes__default.default.arrayOf(PropTypes__default.default.arrayOf(PropTypes__default.default.string))};TableEditor.defaultProps=perseusCore.tableLogic.defaultWidgetOptions;
|
|
1944
1944
|
|