@khanacademy/perseus-editor 36.0.0 → 37.0.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/es/index.js +3 -3
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/preview/message-types.d.ts +1 -1
- package/package.json +3 -3
package/dist/es/index.js
CHANGED
|
@@ -65,7 +65,7 @@ import minusCircle from '@phosphor-icons/core/regular/minus-circle.svg';
|
|
|
65
65
|
import checkIcon from '@phosphor-icons/core/bold/check-bold.svg';
|
|
66
66
|
import minusCircleIcon from '@phosphor-icons/core/bold/minus-circle-bold.svg';
|
|
67
67
|
|
|
68
|
-
const libName="@khanacademy/perseus-editor";const libVersion="
|
|
68
|
+
const libName="@khanacademy/perseus-editor";const libVersion="37.0.0";addLibraryVersionToPerseusDebug(libName,libVersion);
|
|
69
69
|
|
|
70
70
|
var jsxRuntime = {exports: {}};
|
|
71
71
|
|
|
@@ -1523,7 +1523,7 @@ class ItemEditor extends React.Component{serialize(){invariant(this.questionEdit
|
|
|
1523
1523
|
|
|
1524
1524
|
function buildDeviceApiOptions(inputs){return {...ApiOptions.defaults,...inputs.apiOptions,customKeypad:inputs.touch,isMobile:inputs.touch}}function createDeviceApiOptionsDeriver(){let lastInputs=null;let lastResult=null;return inputs=>{if(lastResult!=null&&lastInputs!=null&&lastInputs.apiOptions===inputs.apiOptions&&lastInputs.touch===inputs.touch){return lastResult}const result=buildDeviceApiOptions(inputs);lastInputs=inputs;lastResult=result;return result}}
|
|
1525
1525
|
|
|
1526
|
-
function lintRenderer(content,widgets){const parsed=PerseusMarkdown.parse(content,{});const linterContext={content,widgets,stack:[]};const linterIssues=(PerseusLinter.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));return [...linterIssues,...texIssues]}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]}
|
|
1526
|
+
function lintRenderer(content,widgets){const parsed=PerseusMarkdown.parse(content,{});const linterContext={content,widgets,stack:[]};const linterIssues=(PerseusLinter.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]}
|
|
1527
1527
|
|
|
1528
1528
|
const{HUD}=components;class EditorPage extends React.Component{componentDidMount(){this.setState({issues:gatherLinterIssues(this.props.question,this.props.hints,this.props.issues)});}getSnapshotBeforeUpdate(prevProps,prevState){if(!prevProps.jsonMode&&this.props.jsonMode){return {...this.itemEditor.current?.serialize()??{},hints:this.hintsEditor.current?.serialize()}}return null}componentDidUpdate(previousProps,prevState,snapshot){if(snapshot){this.setState({json:snapshot});return}if(!_.isEqual(previousProps.question,this.props.question)||!_.isEqual(previousProps.answerArea,this.props.answerArea)||!_.isEqual(previousProps.hints,this.props.hints)){this.syncJsonStateFromProps();}const questionOrHintsChanged=previousProps.question?.content!==this.props.question?.content||previousProps.question?.widgets!==this.props.question?.widgets||!_.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(){const issues1=this.itemEditor.current?.getSaveWarnings();const issues2=this.hintsEditor.current?.getSaveWarnings();return issues1.concat(issues2)}serialize(){if(this.props.jsonMode){return this.state.json}invariant(this.itemEditor.current,"cannot serialize EditorPage without ItemEditor");invariant(this.hintsEditor.current,"cannot serialize EditorPage without HintsEditor");return {...this.itemEditor.current.serialize(),hints:this.hintsEditor.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});const showEditor=!this.props.developerMode||!this.props.jsonMode;if(deviceBasedApiOptions.isMobile){className+=" "+ClassNames.MOBILE;}return jsxRuntimeExports.jsx(Dependencies.DependenciesContext.Provider,{value:this.props.dependencies,children:jsxRuntimeExports.jsx(ItemEditorContext.Provider,{value:{question:this.props.question,onEditorChange:this.handleEditorChange},children:jsxRuntimeExports.jsxs("div",{id:"perseus",className:className,children:[jsxRuntimeExports.jsxs("div",{style:{marginBlockEnd:10},children:[this.props.developerMode&&jsxRuntimeExports.jsxs("span",{children:[jsxRuntimeExports.jsxs("label",{children:[" ","Developer JSON Mode:"," ",jsxRuntimeExports.jsx("input",{type:"checkbox",checked:this.props.jsonMode,disabled:this.props.apiOptions?.editingDisabled,onChange:this.toggleJsonMode})]})," "]}),!this.props.jsonMode&&jsxRuntimeExports.jsx(ViewportResizer,{deviceType:this.props.previewDevice,onViewportSizeChanged:this.props.onPreviewDeviceChange}),!this.props.jsonMode&&jsxRuntimeExports.jsx(HUD,{message:"Style warnings",enabled:this.state.highlightLint,onClick:()=>{this.setState({highlightLint:!this.state.highlightLint});}})]}),this.props.developerMode&&this.props.jsonMode&&jsxRuntimeExports.jsx("div",{children:jsxRuntimeExports.jsx(JsonEditor,{multiLine:true,value:this.state.json,parser: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:[showEditor&&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})})})}),showEditor&&jsxRuntimeExports.jsx(ItemEditor,{ref:this.itemEditor,itemId:this.props.itemId,question:this.props.question,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})]}),showEditor&&jsxRuntimeExports.jsx(CombinedHintsEditor,{ref:this.hintsEditor,itemId:this.props.itemId,hints:this.props.hints,imageUploader:this.props.imageUploader,onChange:this.handleChange,deviceType:this.props.previewDevice,apiOptions:deviceBasedApiOptions,previewURL:this.props.previewURL,highlightLint:this.state.highlightLint,widgetIsOpen:this.state.widgetsAreOpen})]})})})}constructor(props){super(props),this.itemEditor=React.createRef(),this.hintsEditor=React.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.toggleJsonMode=()=>{this.setState({json:this.serialize()},()=>{this.props.onChange({jsonMode:!this.props.jsonMode});});},this.handleChange=toChange=>{const newProps=_(this.props).pick("question","hints","answerArea");_(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:_.pick(this.props,"question","answerArea","hints"),highlightLint:true,widgetsAreOpen:this.props.widgetsAreOpen??true,issues:[],axeCoreIssues:[],a11yScanningEnabled:false,highlightInstanceIds:[]};}}EditorPage.defaultProps={answerArea:getDefaultAnswerArea(),developerMode:false,hints:[],jsonMode:false,onChange:()=>{}};
|
|
1529
1529
|
|
|
@@ -1535,7 +1535,7 @@ var issuesList = {"axe-core":{"user-fixable":["aria-input-field-name","aria-mete
|
|
|
1535
1535
|
|
|
1536
1536
|
const assistanceNeededMessage="Developer assistance needed - Please send this exercise and warning info to the LEMS team for review.";const mapAxeResults=(results,type)=>{const prefix=type==="Alert"?"violation":"incomplete";const issues=[];const elementMap=new Map;results.forEach(result=>{const isUserFixable=type==="Alert"&&issuesList["axe-core"]["user-fixable"].some(testId=>testId===result.id);const instanceId=`${prefix}-${result.id}`;issues.push({source:"a11y",id:result.id,description:isUserFixable?"":assistanceNeededMessage,instanceId,helpUrl:result.helpUrl,help:result.help,impact:convertAxeImpactToIssueImpact(result.impact),message:getIssueMessage(result.nodes)});elementMap.set(instanceId,result.nodes.map(node=>node.element).filter(element=>element!=null));});return {issues,elementMap}};const getIssueMessage=nodes=>{return [...new Set(nodes.flatMap(node=>[...node.all,...node.any,...node.none].map(result=>result.message)))].join(". ")};const convertAxeImpactToIssueImpact=impact=>{switch(impact){case "critical":return "high";case "serious":return "high";case "moderate":return "medium";case "minor":return "low";default:return "low"}};
|
|
1537
1537
|
|
|
1538
|
-
function usePreviewPresenter(options={}){const{contentContainerRef}=options;const[content,setContent]=React.useState(null);const[contentVersion,setContentVersion]=React.useState(0);const[a11yScanningEnabled,setA11yScanningEnabled]=React.useState(false);const[highlightTargets,setHighlightTargets]=React.useState([]);const schedule=useActionScheduler();const iframe=window.frameElement;if(iframe==null){throw new Error("usePreviewPresenter must be used within an iframe")}React.useEffect(()=>{const handleMessage=event=>{if(event.source!==window.parent){return}const message=event.data;if(!isParentToIframeMessage(message)){return}switch(message.type){case "content-data":setContent(message.content);setContentVersion(message.contentVersion);break;case "iframe-init":setContent(message.content);setA11yScanningEnabled(message.a11yScanningEnabled);setContentVersion(message.contentVersion);break;case "set-a11y-scanning-enabled":setA11yScanningEnabled(message.enabled);break;case "highlight-issues":if(message.contentVersion!==contentVersionRef.current){break}setHighlightTargets(message.instanceIds.flatMap(instanceId=>elementMapRef.current.get(instanceId)??[]));break;case "clear-highlights":setHighlightTargets([]);break;default:throw new UnreachableCaseError(message)}};window.addEventListener("message",handleMessage);
|
|
1538
|
+
function postToParent(message){window.parent.postMessage(message,"/");}function usePreviewPresenter(options={}){const{contentContainerRef}=options;const[content,setContent]=React.useState(null);const[contentVersion,setContentVersion]=React.useState(0);const[a11yScanningEnabled,setA11yScanningEnabled]=React.useState(false);const[highlightTargets,setHighlightTargets]=React.useState([]);const schedule=useActionScheduler();const iframe=window.frameElement;if(iframe==null){throw new Error("usePreviewPresenter must be used within an iframe")}React.useEffect(()=>{const handleMessage=event=>{if(event.source!==window.parent){return}const message=event.data;if(!isParentToIframeMessage(message)){return}switch(message.type){case "content-data":setContent(message.content);setContentVersion(message.contentVersion);break;case "iframe-init":setContent(message.content);setA11yScanningEnabled(message.a11yScanningEnabled);setContentVersion(message.contentVersion);break;case "set-a11y-scanning-enabled":setA11yScanningEnabled(message.enabled);break;case "highlight-issues":if(message.contentVersion!==contentVersionRef.current){break}setHighlightTargets(message.instanceIds.flatMap(instanceId=>elementMapRef.current.get(instanceId)??[]));break;case "clear-highlights":setHighlightTargets([]);break;default:throw new UnreachableCaseError(message)}};window.addEventListener("message",handleMessage);postToParent(createPreviewIframeReadyMessage());return ()=>{window.removeEventListener("message",handleMessage);}},[]);const a11yScanningEnabledRef=React.useRef(a11yScanningEnabled);React.useEffect(()=>{a11yScanningEnabledRef.current=a11yScanningEnabled;},[a11yScanningEnabled]);const contentVersionRef=React.useRef(contentVersion);React.useEffect(()=>{contentVersionRef.current=contentVersion;},[contentVersion]);React.useEffect(()=>{setHighlightTargets([]);},[contentVersion]);const scanPromiseRef=React.useRef(null);const rescanRequestedRef=React.useRef(false);const elementMapRef=React.useRef(new Map);const startScan=React.useCallback(()=>{const container=contentContainerRef?.current;if(!a11yScanningEnabledRef.current||container==null){return}if(scanPromiseRef.current!=null){rescanRequestedRef.current=true;return}const scannedVersion=contentVersionRef.current;scanPromiseRef.current=(async()=>{const axe=(await import('axe-core')).default;axe.configure({reporter:"v2"});const results=await axe.run({include:container,exclude:[['[target="lint-help-window"]']]},{elementRef:true});if(!a11yScanningEnabledRef.current){return}const{issues:violations,elementMap:violationMap}=mapAxeResults(results.violations,"Alert");const{issues:incompletes,elementMap:incompleteMap}=mapAxeResults(results.incomplete,"Warning");elementMapRef.current=new Map([...violationMap,...incompleteMap]);postToParent(createPreviewA11yReportMessage(violations,incompletes,scannedVersion));})().finally(()=>{scanPromiseRef.current=null;if(rescanRequestedRef.current){rescanRequestedRef.current=false;startScan();}});},[contentContainerRef]);React.useEffect(()=>{const container=contentContainerRef?.current;if(!a11yScanningEnabled||content==null||container==null){return}const scheduledScan=schedule.timeout(startScan,1500);return ()=>scheduledScan.clear()},[content,contentVersion,a11yScanningEnabled,contentContainerRef,schedule,startScan]);const reportHeight=React.useCallback(height=>{if(window.parent==null){return}postToParent({source:PREVIEW_MESSAGE_SOURCE,type:"height-update",height});},[]);return {content,isMobile:iframe.dataset.mobile==="true",hasLintGutter:iframe.dataset.lintGutter==="true",reportHeight,a11yScanningEnabled,highlightTargets}}
|
|
1539
1539
|
|
|
1540
1540
|
function getOverlayRect(targetRect,containerRect){return {top:targetRect.top-containerRect.top-4,left:targetRect.left-containerRect.left-4,width:targetRect.width+8,height:targetRect.height+8}}function A11yOverlays({targets,container}){if(container==null){return null}const containerRect=container.getBoundingClientRect();return jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment,{children:targets.map((target,index)=>jsxRuntimeExports.jsx("div",{"data-testid":"a11y-overlay",style:{position:"absolute",border:"2px solid red",borderRadius:"4px",...getOverlayRect(target.getBoundingClientRect(),containerRect)}},index))})}
|
|
1541
1541
|
|