@khanacademy/perseus-editor 25.1.2 → 25.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/dist/es/index.js CHANGED
@@ -58,7 +58,7 @@ import arrowCounterClockwise from '@phosphor-icons/core/bold/arrow-counter-clock
58
58
  import Link$1 from '@khanacademy/wonder-blocks-link';
59
59
  import plusIcon from '@phosphor-icons/core/bold/plus-bold.svg';
60
60
 
61
- const libName="@khanacademy/perseus-editor";const libVersion="25.1.2";addLibraryVersionToPerseusDebug(libName,libVersion);
61
+ const libName="@khanacademy/perseus-editor";const libVersion="25.3.0";addLibraryVersionToPerseusDebug(libName,libVersion);
62
62
 
63
63
  var jsxRuntime = {exports: {}};
64
64
 
@@ -1462,7 +1462,7 @@ const IssueDetails=({issue})=>{const[expanded,setExpanded]=React.useState(false)
1462
1462
 
1463
1463
  const IssuesPanel=({issues=[]})=>{const[showPanel,setShowPanel]=useState(false);const hasWarnings=issues.length>0;const issuesCount=`${issues.length} issue${issues.length===1?"":"s"}`;const icon=hasWarnings?iconWarning:iconPass;const iconColor=hasWarnings?color.gold:color.green;const togglePanel=()=>{if(hasWarnings){setShowPanel(!showPanel);}};return jsxRuntimeExports.jsxs("div",{className:"perseus-widget-editor",children:[jsxRuntimeExports.jsxs("div",{className:"perseus-widget-editor-title",children:[jsxRuntimeExports.jsx("div",{className:"perseus-widget-editor-title-id",children:jsxRuntimeExports.jsxs(View,{style:{display:"flex",flexDirection:"row",alignItems:"center",gap:"0.25em"},onClick:togglePanel,children:[jsxRuntimeExports.jsx(ToggleableCaret,{isExpanded:showPanel}),jsxRuntimeExports.jsx("span",{children:"Issues"})]})}),jsxRuntimeExports.jsx(PhosphorIcon,{icon:icon,size:"medium",color:iconColor,testId:`issues-icon-${icon}`,style:{marginRight:"0.25em"}}),issuesCount]}),showPanel&&jsxRuntimeExports.jsx("div",{className:"perseus-widget-editor-panel",children:jsxRuntimeExports.jsx("div",{className:"perseus-widget-editor-content",children:issues.map(issue=>jsxRuntimeExports.jsx(IssueDetails,{issue:issue},issue.id))})})]})};
1464
1464
 
1465
- const{InfoTip: InfoTip$o}=components;class ItemExtrasEditor extends React.Component{shouldShowFinancialCalculatorOptions(){return this.props.financialCalculatorMonthlyPayment||this.props.financialCalculatorTotalAmount||this.props.financialCalculatorTimeToPayOff}render(){return jsxRuntimeExports.jsx("div",{className:"perseus-answer-editor",children:jsxRuntimeExports.jsxs("div",{className:"perseus-answer-options",children:[jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Show calculator",infoTip:"Use the calculator when completing difficult calculations is NOT the intent of the question. DON’T use the calculator when testing the student’s ability to complete different types of computations.",checked:this.props.calculator,onChange:newCheckedState=>{this.props.onChange({calculator:newCheckedState});}}),jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Show financial calculator",infoTip:"This provides the student with the ability to view a financial calculator, e.g., for answering financial questions. Once checked, requires at least one of the three options below to be checked.",checked:this.shouldShowFinancialCalculatorOptions(),onChange:newCheckedState=>{this.props.onChange({financialCalculatorMonthlyPayment:newCheckedState,financialCalculatorTotalAmount:newCheckedState,financialCalculatorTimeToPayOff:newCheckedState});}}),this.shouldShowFinancialCalculatorOptions()&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Include monthly payment",infoTip:"This provides the student with the ability to view a monthly payment calculator; e.g., given a loan amount, interest rate, and term, what is the monthly payment?",checked:this.props.financialCalculatorMonthlyPayment,onChange:newCheckedState=>{this.props.onChange({financialCalculatorMonthlyPayment:newCheckedState});},indent:true}),jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Include total amount",infoTip:"This provides the student with the ability to view a total amount calculator; e.g., given a monthly payment, interest rate, and term, what is the total amount to be paid?",checked:this.props.financialCalculatorTotalAmount,onChange:newCheckedState=>{this.props.onChange({financialCalculatorTotalAmount:newCheckedState});},indent:true}),jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Include time-to-pay-off",infoTip:"This provides the student with the ability to view a time to pay off calculator; e.g., given a loan amount, interest rate, and monthly payment, how long will it take to pay off the loan?",checked:this.props.financialCalculatorTimeToPayOff,onChange:newCheckedState=>{this.props.onChange({financialCalculatorTimeToPayOff:newCheckedState});},indent:true})]}),jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Show periodic table",infoTip:"This provides the student with the ability to view a periodic table of the elements, e.g., for answering chemistry questions.",checked:this.props.periodicTable,onChange:newCheckedState=>{this.props.onChange({periodicTable:newCheckedState,periodicTableWithKey:false});}}),this.props.periodicTable&&jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Include key/legend with periodic table",infoTip:"Include a key for HS courses; omit for AP chemistry.",checked:this.props.periodicTableWithKey,onChange:newCheckedState=>{this.props.onChange({periodicTableWithKey:newCheckedState});},indent:true}),jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Show z table (statistics)",infoTip:"This provides the student with the ability to view a table of critical values for the z distribution, e.g. for answering statistics questions.",checked:this.props.zTable,onChange:newCheckedState=>{this.props.onChange({zTable:newCheckedState});}}),jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Show t table (statistics)",infoTip:"This provides the student with the ability to view a table of critical values for the Student's t distribution, e.g. for answering statistics questions.",checked:this.props.tTable,onChange:newCheckedState=>{this.props.onChange({tTable:newCheckedState});}}),jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Show chi-squared table (statistics)",infoTip:"This provides the student with the ability to view a table of critical values for the chi-squared distribution, e.g. for answering statistics questions.",checked:this.props.chi2Table,onChange:newCheckedState=>{this.props.onChange({chi2Table:newCheckedState});}})]})})}constructor(...args){super(...args),this.serialize=()=>{const data={...ItemExtrasEditor.defaultProps};for(const key of ItemExtras){data[key]=!!this.props[key];}return data};}}ItemExtrasEditor.defaultProps={calculator:false,chi2Table:false,financialCalculatorMonthlyPayment:false,financialCalculatorTotalAmount:false,financialCalculatorTimeToPayOff:false,periodicTable:false,periodicTableWithKey:false,tTable:false,zTable:false};const ItemExtraCheckbox=props=>jsxRuntimeExports.jsx(View,{style:[styles$P.checkbox,props.indent?styles$P.indented:undefined],children:jsxRuntimeExports.jsx(Checkbox$1,{label:jsxRuntimeExports.jsxs(View,{style:{flexDirection:"row"},children:[props.label," ",jsxRuntimeExports.jsx(InfoTip$o,{children:props.infoTip})]}),checked:props.checked,onChange:newCheckedState=>props.onChange(newCheckedState)})});const styles$P=StyleSheet.create({indented:{marginInlineStart:spacing.large_24}});
1465
+ const{InfoTip: InfoTip$o}=components;class ItemExtrasEditor extends React.Component{shouldShowFinancialCalculatorOptions(){return this.props.financialCalculatorMonthlyPayment||this.props.financialCalculatorTotalAmount||this.props.financialCalculatorTimeToPayOff}render(){return jsxRuntimeExports.jsx("div",{className:"perseus-answer-editor",children:jsxRuntimeExports.jsxs("div",{className:"perseus-answer-options",children:[jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Show calculator",infoTip:"Use the calculator when completing difficult calculations is NOT the intent of the question. DON’T use the calculator when testing the student’s ability to complete different types of computations.",checked:this.props.calculator,onChange:newCheckedState=>{this.props.onChange({calculator:newCheckedState});}}),jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Show financial calculator",infoTip:"This provides the student with the ability to view a financial calculator, e.g., for answering financial questions. Once checked, requires at least one of the three options below to be checked.",checked:this.shouldShowFinancialCalculatorOptions(),onChange:newCheckedState=>{this.props.onChange({financialCalculatorMonthlyPayment:newCheckedState,financialCalculatorTotalAmount:newCheckedState,financialCalculatorTimeToPayOff:newCheckedState});}}),this.shouldShowFinancialCalculatorOptions()&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Include monthly payment",infoTip:"This provides the student with the ability to view a monthly payment calculator; e.g., given a loan amount, interest rate, and term, what is the monthly payment?",checked:this.props.financialCalculatorMonthlyPayment,onChange:newCheckedState=>{this.props.onChange({financialCalculatorMonthlyPayment:newCheckedState});},indent:true}),jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Include total amount",infoTip:"This provides the student with the ability to view a total amount calculator; e.g., given a monthly payment, interest rate, and term, what is the total amount to be paid?",checked:this.props.financialCalculatorTotalAmount,onChange:newCheckedState=>{this.props.onChange({financialCalculatorTotalAmount:newCheckedState});},indent:true}),jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Include time-to-pay-off",infoTip:"This provides the student with the ability to view a time to pay off calculator; e.g., given a loan amount, interest rate, and monthly payment, how long will it take to pay off the loan?",checked:this.props.financialCalculatorTimeToPayOff,onChange:newCheckedState=>{this.props.onChange({financialCalculatorTimeToPayOff:newCheckedState});},indent:true})]}),jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Show periodic table",infoTip:"This provides the student with the ability to view a periodic table of the elements, e.g., for answering chemistry questions.",checked:this.props.periodicTable,onChange:newCheckedState=>{this.props.onChange({periodicTable:newCheckedState,periodicTableWithKey:false});}}),this.props.periodicTable&&jsxRuntimeExports.jsx(ItemExtraCheckbox,{label:"Include key/legend with periodic table",infoTip:"Include a key for HS courses; omit for AP chemistry.",checked:this.props.periodicTableWithKey,onChange:newCheckedState=>{this.props.onChange({periodicTableWithKey:newCheckedState});},indent:true})]})})}constructor(...args){super(...args),this.serialize=()=>{const data={...ItemExtrasEditor.defaultProps};for(const key of ItemExtras){data[key]=!!this.props[key];}return data};}}ItemExtrasEditor.defaultProps={calculator:false,financialCalculatorMonthlyPayment:false,financialCalculatorTotalAmount:false,financialCalculatorTimeToPayOff:false,periodicTable:false,periodicTableWithKey:false};const ItemExtraCheckbox=props=>jsxRuntimeExports.jsx(View,{style:[styles$P.checkbox,props.indent?styles$P.indented:undefined],children:jsxRuntimeExports.jsx(Checkbox$1,{label:jsxRuntimeExports.jsxs(View,{style:{flexDirection:"row"},children:[props.label," ",jsxRuntimeExports.jsx(InfoTip$o,{children:props.infoTip})]}),checked:props.checked,onChange:newCheckedState=>props.onChange(newCheckedState)})});const styles$P=StyleSheet.create({indented:{marginInlineStart:spacing.large_24}});
1466
1466
 
1467
1467
  const WARNINGS={inaccessibleWidget:(widgetType,widgetId)=>({id:`${widgetId} inaccessible`,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)=>({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:"low",message:message})};
1468
1468
 
@@ -1573,36 +1573,36 @@ const EllipseSwatch=props=>{const{color,fillStyle,strokeStyle}=props;return jsxR
1573
1573
 
1574
1574
  const LineStrokeSelect=props=>{const{selectedValue,containerStyle,onChange}=props;return jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:[styles$v.lineStrokeSelect,containerStyle],children:["stroke",jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsxs(SingleSelect,{selectedValue:selectedValue,onChange:onChange,placeholder:"",children:[jsxRuntimeExports.jsx(OptionItem,{value:"solid",label:"solid"}),jsxRuntimeExports.jsx(OptionItem,{value:"dashed",label:"dashed"})]})]})};const styles$v=StyleSheet.create({lineStrokeSelect:{display:"flex",flexDirection:"row",alignItems:"center",minWidth:0}});
1575
1575
 
1576
+ const LineWeightSelect=props=>{const{selectedValue,containerStyle,onChange}=props;return jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:[{display:"flex",flexDirection:"row",alignItems:"center",minWidth:0},containerStyle],children:["weight",jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsxs(SingleSelect,{selectedValue:selectedValue,onChange:value=>onChange(value),placeholder:"",children:[jsxRuntimeExports.jsx(OptionItem,{value:"thin",label:"thin"}),jsxRuntimeExports.jsx(OptionItem,{value:"medium",label:"medium"}),jsxRuntimeExports.jsx(OptionItem,{value:"thick",label:"thick"})]})]})};
1577
+
1576
1578
  const{InfoTip: InfoTip$e}=components;function LockedFigureAria(props){const{ariaLabel,getPrepopulatedAriaLabel,onChangeProps}=props;const id=React.useId();const ariaLabelId=`aria-label-${id}`;const[loading,setLoading]=React.useState(false);return jsxRuntimeExports.jsxs(View,{children:[jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsxs(View,{style:styles$u.row,children:[jsxRuntimeExports.jsx(LabelMedium,{tag:"label",htmlFor:ariaLabelId,children:"Aria label"}),jsxRuntimeExports.jsx(Spring,{}),jsxRuntimeExports.jsxs(InfoTip$e,{children:["Aria label is used by screen readers to describe content to users who may be visually impaired. ",jsxRuntimeExports.jsx("br",{}),jsxRuntimeExports.jsx("br",{}),"Populating this field will make it so that users can use a screen reader to navigate to this point and hear the description.",jsxRuntimeExports.jsx("br",{}),jsxRuntimeExports.jsx("br",{}),"If you leave this field blank, the point will be hidden from screen readers. Users will not be able to navigate to this point using a screen reader."]})]}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxSmall_6}),jsxRuntimeExports.jsx(LabelXSmall,{style:styles$u.caption,children:"The figure is hidden from screen readers if this field is left blank."}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxSmall_6}),jsxRuntimeExports.jsx(TextArea,{id:ariaLabelId,value:loading?"Loading...":ariaLabel??"",onChange:newValue=>{onChangeProps({ariaLabel:newValue||undefined});},placeholder:"Ex. Point at (x, y)",rows:1,resizeType:"vertical"}),jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:pencilCircle,style:styles$u.button,onClick:()=>{setLoading(true);getPrepopulatedAriaLabel().then(ariaLabel=>{setLoading(false);onChangeProps({ariaLabel});});},children:"Auto-generate"})]})}const styles$u=StyleSheet.create({row:{flexDirection:"row",alignItems:"center"},button:{alignSelf:"start"},caption:{color:color.offBlack64}});
1577
1579
 
1578
1580
  const LockedFigureSettingsActions=props=>{const{figureType,onMove,onRemove}=props;return jsxRuntimeExports.jsxs(View,{style:styles$t.container,children:[jsxRuntimeExports.jsx(Button,{startIcon:trashIcon$1,"aria-label":`Delete locked ${figureType}`,onClick:onRemove,kind:"tertiary",style:styles$t.deleteButton,children:"Delete"}),onMove&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(Spring,{}),jsxRuntimeExports.jsx(IconButton,{icon:caretDoubleUpIcon,kind:"tertiary",size:"small","aria-label":`Move locked ${figureType} to the back`,onClick:()=>onMove("back")}),jsxRuntimeExports.jsx(IconButton,{icon:caretUpIcon,kind:"tertiary",size:"small","aria-label":`Move locked ${figureType} backward`,onClick:()=>onMove("backward")}),jsxRuntimeExports.jsx(IconButton,{icon:caretDownIcon,kind:"tertiary",size:"small","aria-label":`Move locked ${figureType} forward`,onClick:()=>onMove("forward")}),jsxRuntimeExports.jsx(IconButton,{icon:caretDoubleDownIcon,kind:"tertiary",size:"small","aria-label":`Move locked ${figureType} to the front`,onClick:()=>onMove("front")})]})]})};const styles$t=StyleSheet.create({container:{width:"100%",flexDirection:"row",alignItems:"center",marginTop:spacing.xxxSmall_4},deleteButton:{marginInlineStart:-spacing.xxxSmall_4}});
1579
1581
 
1580
1582
  const{InfoTip: InfoTip$d}=components;function LockedLabelSettings(props){const{type,coord,color: color$1,size,text,expanded,onChangeProps,onMove,onRemove,onToggle,containerStyle}=props;return jsxRuntimeExports.jsxs(PerseusEditorAccordion,{expanded:expanded,onToggle:onToggle,header:jsxRuntimeExports.jsxs(View,{style:[styles$s.row,styles$s.accordionHeaderContainer],children:[jsxRuntimeExports.jsxs(LabelLarge,{children:["Label (",coord[0],", ",coord[1],")"]}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),text!==""&&jsxRuntimeExports.jsx(LabelLarge,{style:[{backgroundColor:color.white,color:lockedFigureColors[color$1]},styles$s.accordionHeader],children:text})]}),containerStyle:containerStyle,children:[jsxRuntimeExports.jsx(CoordinatePairInput,{coord:coord,onChange:newCoords=>{onChangeProps({coord:newCoords});},style:styles$s.spaceUnder}),jsxRuntimeExports.jsxs(View,{style:styles$s.row,children:[jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:[styles$s.row,styles$s.spaceUnder,{flexGrow:1}],children:["text",jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(TextField,{value:text,placeholder:"ex. $x^2$ or $\\frac{1}{2}$",onChange:newValue=>onChangeProps({text:newValue})})]}),jsxRuntimeExports.jsxs(InfoTip$d,{children:["Surround your text with $ for TeX.",jsxRuntimeExports.jsx("br",{}),"Example: ",`This circle has radius $\\frac{1}{2}$ units.`,jsxRuntimeExports.jsx("br",{}),jsxRuntimeExports.jsx("br",{}),'It is important to use TeX when appropriate for accessibility. The above example would be read as "This circle has radius one-half units" by screen readers.']})]}),jsxRuntimeExports.jsxs(View,{style:styles$s.row,children:[jsxRuntimeExports.jsx(ColorSelect,{selectedValue:color$1,onChange:newColor=>{onChangeProps({color:newColor});},style:styles$s.spaceUnder}),jsxRuntimeExports.jsx(Strut,{size:spacing.medium_16}),jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:styles$s.row,children:["size",jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsxs(SingleSelect,{selectedValue:size,onChange:newValue=>onChangeProps({size:newValue}),placeholder:"",children:[jsxRuntimeExports.jsx(OptionItem,{value:"small",label:"small"}),jsxRuntimeExports.jsx(OptionItem,{value:"medium",label:"medium"}),jsxRuntimeExports.jsx(OptionItem,{value:"large",label:"large"})]})]})]}),jsxRuntimeExports.jsx(LockedFigureSettingsActions,{figureType:type,onMove:onMove,onRemove:onRemove})]})}const styles$s=StyleSheet.create({accordionHeaderContainer:{whiteSpace:"nowrap"},accordionHeader:{padding:spacing.xxxSmall_4,marginInlineEnd:spacing.xSmall_8,borderRadius:spacing.xxxSmall_4,textOverflow:"ellipsis",overflow:"hidden"},row:{display:"flex",flexDirection:"row",alignItems:"center",minWidth:0},spaceUnder:{marginBottom:spacing.xSmall_8}});
1581
1583
 
1582
- const DEFAULT_COLOR="grayH";function getDefaultFigureForType(type){switch(type){case "point":return {type:"point",coord:[0,0],color:DEFAULT_COLOR,filled:true,labels:[]};case "line":return {type:"line",kind:"line",points:[getDefaultFigureForType("point"),{...getDefaultFigureForType("point"),coord:[2,2]}],color:DEFAULT_COLOR,lineStyle:"solid",showPoint1:false,showPoint2:false,labels:[]};case "vector":return {type:"vector",points:[[0,0],[2,2]],color:DEFAULT_COLOR,labels:[]};case "ellipse":return {type:"ellipse",center:[0,0],radius:[1,1],angle:0,color:DEFAULT_COLOR,fillStyle:"none",strokeStyle:"solid",labels:[]};case "polygon":return {type:"polygon",points:[[0,2],[-1,0],[1,0]],color:DEFAULT_COLOR,showVertices:false,fillStyle:"none",strokeStyle:"solid",weight:"medium",labels:[]};case "function":return {type:"function",color:DEFAULT_COLOR,strokeStyle:"solid",equation:"x^2",domain:[-Infinity,Infinity],directionalAxis:"x",labels:[]};case "label":return {type:"label",coord:[0,0],text:"label",color:DEFAULT_COLOR,size:"medium"};default:throw new UnreachableCaseError(type)}}function generateLockedFigureAppearanceDescription(color,strokeStyle="solid",fill,weight="medium"){const convertedColor=color==="grayH"?"gray":color;const weightString=weight==="medium"?"":` ${weight}`;const baseAppearance=`. Appearance${weightString} ${strokeStyle} ${convertedColor}`;switch(fill){case "none":return `${baseAppearance} border, with no fill.`;case "white":return `${baseAppearance} border, with a white fill.`;case "solid":case "translucent":return `${baseAppearance} border, with a ${fill} ${convertedColor} fill.`;case undefined:return `${baseAppearance}.`;default:throw new UnreachableCaseError(fill)}}async function generateSpokenMathDetails(mathString){const engine=await SpeechRuleEngine.setup("en");let convertedSpeech="";const parsedContent=mathOnlyParser(mathString);for(const piece of parsedContent){switch(piece.type){case "math":convertedSpeech+=engine.texToSpeech(piece.content);break;case "specialCharacter":convertedSpeech+=piece.content.length>1?piece.content.slice(1):piece.content;break;default:convertedSpeech+=piece.content;break}}return convertedSpeech}async function joinLabelsAsSpokenMath(labels){if(labels.length===0){return ""}const spokenLabelPromises=labels.map(label=>{return generateSpokenMathDetails(label.text)});const spokenLabels=await Promise.all(spokenLabelPromises);return ` ${spokenLabels.join(", ")}`}
1584
+ const DEFAULT_COLOR="grayH";function getDefaultFigureForType(type){switch(type){case "point":return {type:"point",coord:[0,0],color:DEFAULT_COLOR,filled:true,labels:[]};case "line":return {type:"line",kind:"line",points:[getDefaultFigureForType("point"),{...getDefaultFigureForType("point"),coord:[2,2]}],color:DEFAULT_COLOR,lineStyle:"solid",showPoint1:false,showPoint2:false,weight:"medium",labels:[]};case "vector":return {type:"vector",points:[[0,0],[2,2]],color:DEFAULT_COLOR,weight:"medium",labels:[]};case "ellipse":return {type:"ellipse",center:[0,0],radius:[1,1],angle:0,color:DEFAULT_COLOR,fillStyle:"none",strokeStyle:"solid",weight:"medium",labels:[]};case "polygon":return {type:"polygon",points:[[0,2],[-1,0],[1,0]],color:DEFAULT_COLOR,showVertices:false,fillStyle:"none",strokeStyle:"solid",weight:"medium",labels:[]};case "function":return {type:"function",color:DEFAULT_COLOR,strokeStyle:"solid",weight:"medium",equation:"x^2",domain:[-Infinity,Infinity],directionalAxis:"x",labels:[]};case "label":return {type:"label",coord:[0,0],text:"label",color:DEFAULT_COLOR,size:"medium"};default:throw new UnreachableCaseError(type)}}function generateLockedFigureAppearanceDescription(color,strokeStyle="solid",fill,weight="medium"){const convertedColor=color==="grayH"?"gray":color;const weightString=weight==="medium"?"":` ${weight}`;const baseAppearance=`. Appearance${weightString} ${strokeStyle} ${convertedColor}`;switch(fill){case "none":return `${baseAppearance} border, with no fill.`;case "white":return `${baseAppearance} border, with a white fill.`;case "solid":case "translucent":return `${baseAppearance} border, with a ${fill} ${convertedColor} fill.`;case undefined:return `${baseAppearance}.`;default:throw new UnreachableCaseError(fill)}}async function generateSpokenMathDetails(mathString){const engine=await SpeechRuleEngine.setup("en");let convertedSpeech="";const parsedContent=mathOnlyParser(mathString);for(const piece of parsedContent){switch(piece.type){case "math":convertedSpeech+=engine.texToSpeech(piece.content);break;case "specialCharacter":convertedSpeech+=piece.content.length>1?piece.content.slice(1):piece.content;break;default:convertedSpeech+=piece.content;break}}return convertedSpeech}async function joinLabelsAsSpokenMath(labels){if(labels.length===0){return ""}const spokenLabelPromises=labels.map(label=>{return generateSpokenMathDetails(label.text)});const spokenLabels=await Promise.all(spokenLabelPromises);return ` ${spokenLabels.join(", ")}`}
1583
1585
 
1584
- const{convertRadiansToDegrees}=angles;const{InfoTip: InfoTip$c}=components;const LockedEllipseSettings=props=>{const{center,radius,angle,color,labels,ariaLabel,fillStyle,strokeStyle,expanded,onToggle,onChangeProps,onMove,onRemove}=props;async function getPrepopulatedAriaLabel(){const visiblelabel=await joinLabelsAsSpokenMath(labels);const spokenCenterX=await generateSpokenMathDetails(`$${center[0]}$`);const spokenCenterY=await generateSpokenMathDetails(`$${center[1]}$`);const spokenRotation=await generateSpokenMathDetails(`$${convertRadiansToDegrees(angle)}$`);const isCircle=radius[0]===radius[1];let str="";if(isCircle){str+=`Circle${visiblelabel} with radius ${radius[0]}`;}else {str+=`Ellipse${visiblelabel} with x radius ${radius[0]} and y radius ${radius[1]}`;}str+=`, centered at ${spokenCenterX} comma ${spokenCenterY}`;if(!isCircle&&angle!==0){str+=`, rotated by ${spokenRotation} degrees`;}const ellipseAppearance=generateLockedFigureAppearanceDescription(color,strokeStyle,fillStyle);str+=ellipseAppearance;return str}function handleCenterChange(newCoord){const xOffset=newCoord[0]-center[0];const yOffset=newCoord[1]-center[1];const newProps={center:newCoord};newProps.labels=labels.map(label=>({...label,coord:[label.coord[0]+xOffset,label.coord[1]+yOffset]}));onChangeProps(newProps);}function handleColorChange(newValue){const newProps={color:newValue};newProps.labels=labels.map(label=>({...label,color:newValue}));onChangeProps(newProps);}function handleLabelChange(updatedLabel,labelIndex){const updatedLabels=[...labels];updatedLabels[labelIndex]={...labels[labelIndex],...updatedLabel};onChangeProps({labels:updatedLabels});}function handleLabelRemove(labelIndex){const updatedLabels=labels.filter((_,index)=>index!==labelIndex);onChangeProps({labels:updatedLabels});}return jsxRuntimeExports.jsxs(PerseusEditorAccordion,{expanded:expanded,onToggle:onToggle,header:jsxRuntimeExports.jsxs(View,{style:styles$r.row,children:[jsxRuntimeExports.jsx(LabelLarge,{children:`Ellipse (${center[0]}, ${center[1]}), radius ${radius[0]}, ${radius[1]}`}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(EllipseSwatch,{color:props.color,fillStyle:fillStyle,strokeStyle:strokeStyle})]}),children:[jsxRuntimeExports.jsxs(View,{style:styles$r.row,children:[jsxRuntimeExports.jsx(CoordinatePairInput,{coord:center,style:styles$r.spaceUnder,onChange:handleCenterChange}),jsxRuntimeExports.jsx(View,{style:styles$r.spaceUnder,children:jsxRuntimeExports.jsx(InfoTip$c,{children:"The coordinates for the center of the ellipse."})})]}),jsxRuntimeExports.jsx(CoordinatePairInput,{coord:radius,labels:["x radius","y radius"],style:styles$r.spaceUnder,onChange:newCoords=>onChangeProps({radius:newCoords})}),jsxRuntimeExports.jsx(AngleInput,{angle:angle,onChange:newAngle=>onChangeProps({angle:newAngle})}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsxs(View,{style:[styles$r.row,styles$r.spaceUnder],children:[jsxRuntimeExports.jsx(ColorSelect,{selectedValue:color,onChange:handleColorChange}),jsxRuntimeExports.jsx(Strut,{size:spacing.medium_16}),jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:[styles$r.row,styles$r.truncatedWidth],children:["fill",jsxRuntimeExports.jsx(Strut,{size:spacing.xxSmall_6}),jsxRuntimeExports.jsx(SingleSelect,{selectedValue:fillStyle,onChange:value=>onChangeProps({fillStyle:value}),placeholder:"",children:Object.keys(lockedFigureFillStyles).map(option=>jsxRuntimeExports.jsx(OptionItem,{value:option,label:option},option))})]})]}),jsxRuntimeExports.jsx(LineStrokeSelect,{selectedValue:strokeStyle,onChange:value=>onChangeProps({strokeStyle:value})}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(View,{style:styles$r.horizontalRule}),jsxRuntimeExports.jsx(LockedFigureAria,{ariaLabel:ariaLabel,getPrepopulatedAriaLabel:getPrepopulatedAriaLabel,onChangeProps:newProps=>{onChangeProps(newProps);}}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsx(View,{style:styles$r.horizontalRule}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LabelMedium,{children:"Visible labels"}),labels.map((label,labelIndex)=>createElement(LockedLabelSettings,{...label,key:labelIndex,expanded:true,onChangeProps:newLabel=>{handleLabelChange(newLabel,labelIndex);},onRemove:()=>{handleLabelRemove(labelIndex);},containerStyle:styles$r.labelContainer})),jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:plusCircle,onClick:()=>{const newLabel={...getDefaultFigureForType("label"),coord:[center[0],center[1]-labels.length],color:color};onChangeProps({labels:[...labels,newLabel]});},style:styles$r.addButton,children:"Add visible label"}),jsxRuntimeExports.jsx(LockedFigureSettingsActions,{figureType:props.type,onMove:onMove,onRemove:onRemove})]})};const styles$r=StyleSheet.create({row:{display:"flex",flexDirection:"row",alignItems:"center"},spaceUnder:{marginBottom:spacing.xSmall_8},truncatedWidth:{minWidth:0},addButton:{alignSelf:"start"},labelContainer:{backgroundColor:color.white},horizontalRule:{height:1,backgroundColor:color.offBlack16}});
1586
+ const{convertRadiansToDegrees}=angles;const{InfoTip: InfoTip$c}=components;const LockedEllipseSettings=props=>{const{center,radius,angle,color,labels,ariaLabel,fillStyle,strokeStyle,weight,expanded,onToggle,onChangeProps,onMove,onRemove}=props;async function getPrepopulatedAriaLabel(){const visiblelabel=await joinLabelsAsSpokenMath(labels);const spokenCenterX=await generateSpokenMathDetails(`$${center[0]}$`);const spokenCenterY=await generateSpokenMathDetails(`$${center[1]}$`);const spokenRotation=await generateSpokenMathDetails(`$${convertRadiansToDegrees(angle)}$`);const isCircle=radius[0]===radius[1];let str="";if(isCircle){str+=`Circle${visiblelabel} with radius ${radius[0]}`;}else {str+=`Ellipse${visiblelabel} with x radius ${radius[0]} and y radius ${radius[1]}`;}str+=`, centered at ${spokenCenterX} comma ${spokenCenterY}`;if(!isCircle&&angle!==0){str+=`, rotated by ${spokenRotation} degrees`;}const ellipseAppearance=generateLockedFigureAppearanceDescription(color,strokeStyle,fillStyle,weight);str+=ellipseAppearance;return str}function handleCenterChange(newCoord){const xOffset=newCoord[0]-center[0];const yOffset=newCoord[1]-center[1];const newProps={center:newCoord};newProps.labels=labels.map(label=>({...label,coord:[label.coord[0]+xOffset,label.coord[1]+yOffset]}));onChangeProps(newProps);}function handleColorChange(newValue){const newProps={color:newValue};newProps.labels=labels.map(label=>({...label,color:newValue}));onChangeProps(newProps);}function handleLabelChange(updatedLabel,labelIndex){const updatedLabels=[...labels];updatedLabels[labelIndex]={...labels[labelIndex],...updatedLabel};onChangeProps({labels:updatedLabels});}function handleLabelRemove(labelIndex){const updatedLabels=labels.filter((_,index)=>index!==labelIndex);onChangeProps({labels:updatedLabels});}return jsxRuntimeExports.jsxs(PerseusEditorAccordion,{expanded:expanded,onToggle:onToggle,header:jsxRuntimeExports.jsxs(View,{style:styles$r.row,children:[jsxRuntimeExports.jsx(LabelLarge,{children:`Ellipse (${center[0]}, ${center[1]}), radius ${radius[0]}, ${radius[1]}`}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(EllipseSwatch,{color:props.color,fillStyle:fillStyle,strokeStyle:strokeStyle})]}),children:[jsxRuntimeExports.jsxs(View,{style:styles$r.row,children:[jsxRuntimeExports.jsx(CoordinatePairInput,{coord:center,style:styles$r.spaceUnder,onChange:handleCenterChange}),jsxRuntimeExports.jsx(View,{style:styles$r.spaceUnder,children:jsxRuntimeExports.jsx(InfoTip$c,{children:"The coordinates for the center of the ellipse."})})]}),jsxRuntimeExports.jsx(CoordinatePairInput,{coord:radius,labels:["x radius","y radius"],style:styles$r.spaceUnder,onChange:newCoords=>onChangeProps({radius:newCoords})}),jsxRuntimeExports.jsx(AngleInput,{angle:angle,onChange:newAngle=>onChangeProps({angle:newAngle})}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsxs(View,{style:[styles$r.row,styles$r.spaceUnder],children:[jsxRuntimeExports.jsx(ColorSelect,{selectedValue:color,onChange:handleColorChange}),jsxRuntimeExports.jsx(Strut,{size:spacing.medium_16}),jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:[styles$r.row,styles$r.truncatedWidth],children:["fill",jsxRuntimeExports.jsx(Strut,{size:spacing.xxSmall_6}),jsxRuntimeExports.jsx(SingleSelect,{selectedValue:fillStyle,onChange:value=>onChangeProps({fillStyle:value}),placeholder:"",children:Object.keys(lockedFigureFillStyles).map(option=>jsxRuntimeExports.jsx(OptionItem,{value:option,label:option},option))})]})]}),jsxRuntimeExports.jsx(LineStrokeSelect,{selectedValue:strokeStyle,onChange:value=>onChangeProps({strokeStyle:value}),containerStyle:{marginBottom:sizing.size_080}}),jsxRuntimeExports.jsx(LineWeightSelect,{selectedValue:weight,onChange:value=>onChangeProps({weight:value})}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(View,{style:styles$r.horizontalRule}),jsxRuntimeExports.jsx(LockedFigureAria,{ariaLabel:ariaLabel,getPrepopulatedAriaLabel:getPrepopulatedAriaLabel,onChangeProps:newProps=>{onChangeProps(newProps);}}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsx(View,{style:styles$r.horizontalRule}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LabelMedium,{children:"Visible labels"}),labels.map((label,labelIndex)=>createElement(LockedLabelSettings,{...label,key:labelIndex,expanded:true,onChangeProps:newLabel=>{handleLabelChange(newLabel,labelIndex);},onRemove:()=>{handleLabelRemove(labelIndex);},containerStyle:styles$r.labelContainer})),jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:plusCircle,onClick:()=>{const newLabel={...getDefaultFigureForType("label"),coord:[center[0],center[1]-labels.length],color:color};onChangeProps({labels:[...labels,newLabel]});},style:styles$r.addButton,children:"Add visible label"}),jsxRuntimeExports.jsx(LockedFigureSettingsActions,{figureType:props.type,onMove:onMove,onRemove:onRemove})]})};const styles$r=StyleSheet.create({row:{display:"flex",flexDirection:"row",alignItems:"center"},spaceUnder:{marginBottom:spacing.xSmall_8},truncatedWidth:{minWidth:0},addButton:{alignSelf:"start"},labelContainer:{backgroundColor:color.white},horizontalRule:{height:1,backgroundColor:color.offBlack16}});
1585
1587
 
1586
1588
  const LineSwatch=props=>{const{color,lineStyle}=props;return jsxRuntimeExports.jsx(View,{style:styles$q.container,children:jsxRuntimeExports.jsx(View,{"aria-label":`${color}, ${lineStyle}`,style:[styles$q.lineSwatch,{border:`5px ${lineStyle} ${lockedFigureColors[color]}`}]})})};const styles$q=StyleSheet.create({container:{backgroundColor:color.white,justifyContent:"center",padding:spacing.xSmall_8,borderRadius:spacing.xxxSmall_4},lineSwatch:{width:40}});
1587
1589
 
1588
1590
  const examples={linear:["x + 5","1/2x - 2"],polynomial:["1/2x^2 + 3x - 4","(1/3)x^3 - 2x^2 + 3x - 4"],trigonometric:["sin(x) * 3","arctan(2x) + 4"]};
1589
1591
 
1590
- const LockedFunctionSettings=props=>{const{color:lineColor,strokeStyle,equation,directionalAxis,domain,ariaLabel,onChangeProps,onMove,onRemove}=props;const labels=props.labels;const equationPrefix=directionalAxis==="x"?"y=":"x=";const lineLabel=`Function (${equationPrefix}${equation})`;const getDomainStringValues=domain=>{return [Number.isFinite(domain[0])?domain[0].toString():"",Number.isFinite(domain[1])?domain[1].toString():""]};const[domainEntries,setDomainEntries]=useState(getDomainStringValues(domain));const[exampleCategory,setExampleCategory]=useState("");useEffect(()=>{setDomainEntries(getDomainStringValues(domain));},[domain]);async function getPrepopulatedAriaLabel(){const visiblelabel=await joinLabelsAsSpokenMath(labels);let str=`Function${visiblelabel} with equation ${equationPrefix}${equation}`;if(Number.isFinite(domain[0])||Number.isFinite(domain[1])){str+=`, domain from ${domain[0]} to ${domain[1]}`;}const functionAppearance=generateLockedFigureAppearanceDescription(lineColor,strokeStyle);str+=functionAppearance;return str}function handlePropChange(property,newValue){const updatedProps={};updatedProps[property]=newValue;onChangeProps(updatedProps);}function handleDomainChange(limitIndex,newValueString){const newDomainEntries=[...domainEntries];newDomainEntries[limitIndex]=newValueString;setDomainEntries(newDomainEntries);const newDomain=[...domain];let newValue=parseFloat(newValueString);if(newValueString===""&&limitIndex===0){newValue=-Infinity;}else if(newValueString===""&&limitIndex===1){newValue=Infinity;}newDomain[limitIndex]=newValue;onChangeProps({domain:newDomain});}const exampleCategories=Object.keys(examples);const exampleCategorySelected=exampleCategory!=="";const exampleContent=exampleCategorySelected?examples[exampleCategory]:["Select category to see example equations"];function handleColorChange(newValue){const newProps={color:newValue};newProps.labels=labels.map(label=>({...label,color:newValue}));onChangeProps(newProps);}function handleLabelChange(updatedLabel,labelIndex){const updatedLabels=[...labels];updatedLabels[labelIndex]={...labels[labelIndex],...updatedLabel};onChangeProps({labels:updatedLabels});}function handleLabelRemove(labelIndex){const updatedLabels=labels.filter((_,index)=>index!==labelIndex);onChangeProps({labels:updatedLabels});}return jsxRuntimeExports.jsxs(PerseusEditorAccordion,{expanded:props.expanded,onToggle:props.onToggle,header:jsxRuntimeExports.jsxs(View,{style:styles$p.row,children:[jsxRuntimeExports.jsx(LabelLarge,{style:styles$p.accordionHeader,children:lineLabel}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(LineSwatch,{color:lineColor,lineStyle:strokeStyle})]}),children:[jsxRuntimeExports.jsxs(View,{style:[styles$p.row,styles$p.spaceUnder],children:[jsxRuntimeExports.jsx(ColorSelect,{selectedValue:lineColor,onChange:handleColorChange}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LineStrokeSelect,{selectedValue:strokeStyle,onChange:newValue=>{handlePropChange("strokeStyle",newValue);}})]}),jsxRuntimeExports.jsxs(View,{style:[styles$p.row,styles$p.rowSpace],children:[jsxRuntimeExports.jsxs(SingleSelect,{selectedValue:directionalAxis,onChange:newValue=>{handlePropChange("directionalAxis",newValue);},"aria-label":"equation prefix",style:[styles$p.dropdownLabel,styles$p.axisMenu],placeholder:"",children:[jsxRuntimeExports.jsx(OptionItem,{value:"x",label:"y ="}),jsxRuntimeExports.jsx(OptionItem,{value:"y",label:"x ="})]}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(TextField,{type:"text","aria-label":"equation",value:equation,onChange:newValue=>{handlePropChange("equation",newValue);},style:[styles$p.textField]})]}),jsxRuntimeExports.jsxs(View,{style:[styles$p.row,styles$p.rowSpace],children:[jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:[styles$p.dropdownLabel,styles$p.domainMin],children:["domain min",jsxRuntimeExports.jsx(Strut,{size:spacing.xxSmall_6}),jsxRuntimeExports.jsx(TextField,{type:"number",style:styles$p.domainMinField,value:domainEntries[0],onChange:newValue=>{handleDomainChange(0,newValue);}})]}),jsxRuntimeExports.jsx(Strut,{size:spacing.medium_16}),jsxRuntimeExports.jsxs(LabelMedium,{tag:"label","aria-label":"domain max",style:[styles$p.dropdownLabel,styles$p.domainMax],children:["max",jsxRuntimeExports.jsx(Strut,{size:spacing.xxSmall_6}),jsxRuntimeExports.jsx(TextField,{type:"number",style:styles$p.domainMaxField,value:domainEntries[1],onChange:newValue=>{handleDomainChange(1,newValue);}})]})]}),jsxRuntimeExports.jsxs(PerseusEditorAccordion,{header:jsxRuntimeExports.jsx(LabelLarge,{children:"Example Functions"}),expanded:false,containerStyle:styles$p.exampleWorkspace,panelStyle:styles$p.exampleAccordionPanel,children:[jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:styles$p.dropdownLabel,children:["Choose a category",jsxRuntimeExports.jsx(Strut,{size:spacing.xxSmall_6}),jsxRuntimeExports.jsx(SingleSelect,{selectedValue:exampleCategory,onChange:setExampleCategory,placeholder:"examples",children:exampleCategories.map(category=>{return jsxRuntimeExports.jsx(OptionItem,{value:category,label:category},category)})})]}),exampleCategorySelected&&jsxRuntimeExports.jsx("ul",{className:css(styles$p.exampleContainer),children:exampleContent.map((example,index)=>jsxRuntimeExports.jsx(ExampleItem,{category:exampleCategory,example:example,index:index,pasteEquationFn:handlePropChange},index))})]}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(View,{style:styles$p.horizontalRule}),jsxRuntimeExports.jsx(LockedFigureAria,{ariaLabel:ariaLabel,getPrepopulatedAriaLabel:getPrepopulatedAriaLabel,onChangeProps:newProps=>{onChangeProps(newProps);}}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsx(View,{style:styles$p.horizontalRule}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LabelMedium,{children:"Visible labels"}),labels.map((label,labelIndex)=>jsxRuntimeExports.jsx(LockedLabelSettings,{...label,expanded:true,onChangeProps:newLabel=>{handleLabelChange(newLabel,labelIndex);},onRemove:()=>{handleLabelRemove(labelIndex);},containerStyle:styles$p.labelContainer},labelIndex)),jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:plusCircle,onClick:()=>{const newLabel={...getDefaultFigureForType("label"),coord:[0,-labels.length],color:lineColor};onChangeProps({labels:[...labels,newLabel]});},style:styles$p.addButton,children:"Add visible label"}),jsxRuntimeExports.jsx(LockedFigureSettingsActions,{figureType:props.type,onMove:onMove,onRemove:onRemove})]})};const ExampleItem=props=>{const{category,example,index,pasteEquationFn}=props;const exampleId=useId();return jsxRuntimeExports.jsxs("li",{className:css(styles$p.exampleRow),children:[jsxRuntimeExports.jsx(IconButton,{icon:autoPasteIcon,kind:"tertiary","aria-label":"paste example","aria-describedby":exampleId,onClick:()=>pasteEquationFn("equation",example),size:"medium",style:styles$p.copyPasteButton}),jsxRuntimeExports.jsx(IconButton,{icon:copyIcon,kind:"tertiary","aria-label":"copy example","aria-describedby":exampleId,onClick:()=>navigator.clipboard.writeText(example),size:"medium",style:styles$p.copyPasteButton}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsx(View,{style:styles$p.exampleContent,id:exampleId,children:example})]},`${category}-${index}`)};const styles$p=StyleSheet.create({accordionHeader:{textOverflow:"ellipsis",maxWidth:"calc(100% - 64px)",overflow:"hidden",whiteSpace:"nowrap"},axisMenu:{minWidth:"auto"},copyPasteButton:{flexShrink:"0",margin:"0 2px"},domainMin:{justifyContent:"space-between",width:"calc(((100% - 141px) / 2) + 88.7px)",textWrap:"nowrap"},domainMinField:{width:"calc(100% - 88.7px)"},domainMax:{width:"calc(((100% - 141px) / 2) + 36.2px)"},domainMaxField:{width:"calc(100% - 36.2px)"},dropdownLabel:{alignItems:"center",display:"flex"},exampleAccordionPanel:{alignItems:"start",paddingBottom:"12px",flexDirection:"row",flexWrap:"wrap"},exampleContainer:{background:"white",border:`1px solid ${color.fadedOffBlack16}`,borderRadius:"4px",flexGrow:"1",listStyleType:"none",maxHeight:"88px",margin:"8px 0 0 0",overflowY:"scroll",padding:"4px 12px 4px 4px"},exampleContent:{fontFamily:`"Lato", sans-serif`,flexGrow:"1",color:color.offBlack},exampleRow:{alignItems:"center",display:"flex",flexDirection:"row",minHeight:"44px"},exampleWorkspace:{background:color.white50},rowSpace:{marginTop:spacing.xSmall_8},row:{display:"flex",flexDirection:"row",alignItems:"center"},textField:{flexGrow:"1"},addButton:{alignSelf:"start"},horizontalRule:{height:1,backgroundColor:color.offBlack16},labelContainer:{backgroundColor:color.white}});
1592
+ const LockedFunctionSettings=props=>{const{color:lineColor,strokeStyle,equation,directionalAxis,domain,weight,ariaLabel,onChangeProps,onMove,onRemove}=props;const labels=props.labels;const equationPrefix=directionalAxis==="x"?"y=":"x=";const lineLabel=`Function (${equationPrefix}${equation})`;const getDomainStringValues=domain=>{return [Number.isFinite(domain[0])?domain[0].toString():"",Number.isFinite(domain[1])?domain[1].toString():""]};const[domainEntries,setDomainEntries]=useState(getDomainStringValues(domain));const[exampleCategory,setExampleCategory]=useState("");useEffect(()=>{setDomainEntries(getDomainStringValues(domain));},[domain]);async function getPrepopulatedAriaLabel(){const visiblelabel=await joinLabelsAsSpokenMath(labels);let str=`Function${visiblelabel} with equation ${equationPrefix}${equation}`;if(Number.isFinite(domain[0])||Number.isFinite(domain[1])){str+=`, domain from ${domain[0]} to ${domain[1]}`;}const functionAppearance=generateLockedFigureAppearanceDescription(lineColor,strokeStyle,undefined,weight);str+=functionAppearance;return str}function handlePropChange(property,newValue){const updatedProps={};updatedProps[property]=newValue;onChangeProps(updatedProps);}function handleDomainChange(limitIndex,newValueString){const newDomainEntries=[...domainEntries];newDomainEntries[limitIndex]=newValueString;setDomainEntries(newDomainEntries);const newDomain=[...domain];let newValue=parseFloat(newValueString);if(newValueString===""&&limitIndex===0){newValue=-Infinity;}else if(newValueString===""&&limitIndex===1){newValue=Infinity;}newDomain[limitIndex]=newValue;onChangeProps({domain:newDomain});}const exampleCategories=Object.keys(examples);const exampleCategorySelected=exampleCategory!=="";const exampleContent=exampleCategorySelected?examples[exampleCategory]:["Select category to see example equations"];function handleColorChange(newValue){const newProps={color:newValue};newProps.labels=labels.map(label=>({...label,color:newValue}));onChangeProps(newProps);}function handleLabelChange(updatedLabel,labelIndex){const updatedLabels=[...labels];updatedLabels[labelIndex]={...labels[labelIndex],...updatedLabel};onChangeProps({labels:updatedLabels});}function handleLabelRemove(labelIndex){const updatedLabels=labels.filter((_,index)=>index!==labelIndex);onChangeProps({labels:updatedLabels});}return jsxRuntimeExports.jsxs(PerseusEditorAccordion,{expanded:props.expanded,onToggle:props.onToggle,header:jsxRuntimeExports.jsxs(View,{style:styles$p.row,children:[jsxRuntimeExports.jsx(LabelLarge,{style:styles$p.accordionHeader,children:lineLabel}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(LineSwatch,{color:lineColor,lineStyle:strokeStyle})]}),children:[jsxRuntimeExports.jsxs(View,{style:[styles$p.row,{marginBottom:sizing.size_080}],children:[jsxRuntimeExports.jsx(ColorSelect,{selectedValue:lineColor,onChange:handleColorChange}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LineStrokeSelect,{selectedValue:strokeStyle,onChange:newValue=>{handlePropChange("strokeStyle",newValue);}})]}),jsxRuntimeExports.jsx(LineWeightSelect,{selectedValue:weight,onChange:value=>onChangeProps({weight:value})}),jsxRuntimeExports.jsxs(View,{style:[styles$p.row,styles$p.rowSpace],children:[jsxRuntimeExports.jsxs(SingleSelect,{selectedValue:directionalAxis,onChange:newValue=>{handlePropChange("directionalAxis",newValue);},"aria-label":"equation prefix",style:[styles$p.dropdownLabel,styles$p.axisMenu],placeholder:"",children:[jsxRuntimeExports.jsx(OptionItem,{value:"x",label:"y ="}),jsxRuntimeExports.jsx(OptionItem,{value:"y",label:"x ="})]}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(TextField,{type:"text","aria-label":"equation",value:equation,onChange:newValue=>{handlePropChange("equation",newValue);},style:[styles$p.textField]})]}),jsxRuntimeExports.jsxs(View,{style:[styles$p.row,styles$p.rowSpace],children:[jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:[styles$p.dropdownLabel,styles$p.domainMin],children:["domain min",jsxRuntimeExports.jsx(Strut,{size:spacing.xxSmall_6}),jsxRuntimeExports.jsx(TextField,{type:"number",style:styles$p.domainMinField,value:domainEntries[0],onChange:newValue=>{handleDomainChange(0,newValue);}})]}),jsxRuntimeExports.jsx(Strut,{size:spacing.medium_16}),jsxRuntimeExports.jsxs(LabelMedium,{tag:"label","aria-label":"domain max",style:[styles$p.dropdownLabel,styles$p.domainMax],children:["max",jsxRuntimeExports.jsx(Strut,{size:spacing.xxSmall_6}),jsxRuntimeExports.jsx(TextField,{type:"number",style:styles$p.domainMaxField,value:domainEntries[1],onChange:newValue=>{handleDomainChange(1,newValue);}})]})]}),jsxRuntimeExports.jsxs(PerseusEditorAccordion,{header:jsxRuntimeExports.jsx(LabelLarge,{children:"Example Functions"}),expanded:false,containerStyle:styles$p.exampleWorkspace,panelStyle:styles$p.exampleAccordionPanel,children:[jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:styles$p.dropdownLabel,children:["Choose a category",jsxRuntimeExports.jsx(Strut,{size:spacing.xxSmall_6}),jsxRuntimeExports.jsx(SingleSelect,{selectedValue:exampleCategory,onChange:setExampleCategory,placeholder:"examples",children:exampleCategories.map(category=>{return jsxRuntimeExports.jsx(OptionItem,{value:category,label:category},category)})})]}),exampleCategorySelected&&jsxRuntimeExports.jsx("ul",{className:css(styles$p.exampleContainer),children:exampleContent.map((example,index)=>jsxRuntimeExports.jsx(ExampleItem,{category:exampleCategory,example:example,index:index,pasteEquationFn:handlePropChange},index))})]}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(View,{style:styles$p.horizontalRule}),jsxRuntimeExports.jsx(LockedFigureAria,{ariaLabel:ariaLabel,getPrepopulatedAriaLabel:getPrepopulatedAriaLabel,onChangeProps:newProps=>{onChangeProps(newProps);}}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsx(View,{style:styles$p.horizontalRule}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LabelMedium,{children:"Visible labels"}),labels.map((label,labelIndex)=>jsxRuntimeExports.jsx(LockedLabelSettings,{...label,expanded:true,onChangeProps:newLabel=>{handleLabelChange(newLabel,labelIndex);},onRemove:()=>{handleLabelRemove(labelIndex);},containerStyle:styles$p.labelContainer},labelIndex)),jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:plusCircle,onClick:()=>{const newLabel={...getDefaultFigureForType("label"),coord:[0,-labels.length],color:lineColor};onChangeProps({labels:[...labels,newLabel]});},style:styles$p.addButton,children:"Add visible label"}),jsxRuntimeExports.jsx(LockedFigureSettingsActions,{figureType:props.type,onMove:onMove,onRemove:onRemove})]})};const ExampleItem=props=>{const{category,example,index,pasteEquationFn}=props;const exampleId=useId();return jsxRuntimeExports.jsxs("li",{className:css(styles$p.exampleRow),children:[jsxRuntimeExports.jsx(IconButton,{icon:autoPasteIcon,kind:"tertiary","aria-label":"paste example","aria-describedby":exampleId,onClick:()=>pasteEquationFn("equation",example),size:"medium",style:styles$p.copyPasteButton}),jsxRuntimeExports.jsx(IconButton,{icon:copyIcon,kind:"tertiary","aria-label":"copy example","aria-describedby":exampleId,onClick:()=>navigator.clipboard.writeText(example),size:"medium",style:styles$p.copyPasteButton}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsx(View,{style:styles$p.exampleContent,id:exampleId,children:example})]},`${category}-${index}`)};const styles$p=StyleSheet.create({accordionHeader:{textOverflow:"ellipsis",maxWidth:"calc(100% - 64px)",overflow:"hidden",whiteSpace:"nowrap"},axisMenu:{minWidth:"auto"},copyPasteButton:{flexShrink:"0",margin:"0 2px"},domainMin:{justifyContent:"space-between",width:"calc(((100% - 141px) / 2) + 88.7px)",textWrap:"nowrap"},domainMinField:{width:"calc(100% - 88.7px)"},domainMax:{width:"calc(((100% - 141px) / 2) + 36.2px)"},domainMaxField:{width:"calc(100% - 36.2px)"},dropdownLabel:{alignItems:"center",display:"flex"},exampleAccordionPanel:{alignItems:"start",paddingBottom:"12px",flexDirection:"row",flexWrap:"wrap"},exampleContainer:{background:"white",border:`1px solid ${color.fadedOffBlack16}`,borderRadius:"4px",flexGrow:"1",listStyleType:"none",maxHeight:"88px",margin:"8px 0 0 0",overflowY:"scroll",padding:"4px 12px 4px 4px"},exampleContent:{fontFamily:`"Lato", sans-serif`,flexGrow:"1",color:color.offBlack},exampleRow:{alignItems:"center",display:"flex",flexDirection:"row",minHeight:"44px"},exampleWorkspace:{background:color.white50},rowSpace:{marginTop:spacing.xSmall_8},row:{display:"flex",flexDirection:"row",alignItems:"center"},textField:{flexGrow:"1"},addButton:{alignSelf:"start"},horizontalRule:{height:1,backgroundColor:color.offBlack16},labelContainer:{backgroundColor:color.white}});
1591
1593
 
1592
1594
  const LabeledSwitch=props=>{const{checked,label,style,onChange}=props;const switchId=useId();return jsxRuntimeExports.jsxs(View,{style:[styles$o.row,style],children:[jsxRuntimeExports.jsx(Switch,{id:switchId,checked:checked,onChange:onChange}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(LabelMedium,{tag:"label",htmlFor:switchId,children:label})]})};const styles$o=StyleSheet.create({row:{flexDirection:"row",alignItems:"center"}});
1593
1595
 
1594
1596
  const LockedPointSettings=props=>{const{headerLabel,coord,color:pointColor,filled=true,labels,ariaLabel,onChangeProps,onMove,onRemove,showPoint,error,expanded,onTogglePoint,onToggle}=props;const isDefiningPoint=!onMove&&!onRemove;async function getPrepopulatedAriaLabel(){const visiblelabel=await joinLabelsAsSpokenMath(labels);const spokenX=await generateSpokenMathDetails(`$${coord[0]}$`);const spokenY=await generateSpokenMathDetails(`$${coord[1]}$`);let str=`Point${visiblelabel} at ${spokenX} comma ${spokenY}`;const pointAppearance=generateLockedFigureAppearanceDescription(pointColor);str+=pointAppearance;return str}function handleColorChange(newValue){const newProps={color:newValue};newProps.labels=labels.map(label=>({...label,color:newValue}));onChangeProps(newProps);}function handleCoordChange(newCoord){const xOffset=newCoord[0]-coord[0];const yOffset=newCoord[1]-coord[1];const newProps={coord:newCoord};newProps.labels=labels.map(label=>({...label,coord:[label.coord[0]+xOffset,label.coord[1]+yOffset]}));onChangeProps(newProps);}function handleLabelChange(updatedLabel,labelIndex){const updatedLabels=[...labels];updatedLabels[labelIndex]={...labels[labelIndex],...updatedLabel};onChangeProps({labels:updatedLabels});}function handleLabelRemove(labelIndex){const updatedLabels=labels.filter((_,index)=>index!==labelIndex);onChangeProps({labels:updatedLabels});}return jsxRuntimeExports.jsxs(PerseusEditorAccordion,{expanded:expanded,onToggle:onToggle,containerStyle:isDefiningPoint?styles$n.definingContainer:undefined,panelStyle:isDefiningPoint?styles$n.definingPanel:undefined,header:jsxRuntimeExports.jsxs(View,{style:styles$n.row,children:[jsxRuntimeExports.jsx(LabelLarge,{children:`${headerLabel||"Point"} (${coord[0]}, ${coord[1]})`}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(ColorSwatch,{color:pointColor,filled:filled})]}),children:[jsxRuntimeExports.jsx(CoordinatePairInput,{coord:coord,style:styles$n.spaceUnder,onChange:handleCoordChange,error:!!error}),onTogglePoint&&jsxRuntimeExports.jsx(LabeledSwitch,{label:"show point on graph",checked:!!showPoint,style:showPoint&&styles$n.spaceUnder,onChange:onTogglePoint}),(!isDefiningPoint||showPoint)&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(ColorSelect,{selectedValue:pointColor,onChange:handleColorChange,style:styles$n.spaceUnder}),jsxRuntimeExports.jsx(LabeledSwitch,{label:"open point",checked:!filled,onChange:newValue=>{onChangeProps({filled:!newValue});}})]}),!isDefiningPoint&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(View,{style:styles$n.horizontalRule}),jsxRuntimeExports.jsx(LockedFigureAria,{ariaLabel:ariaLabel,getPrepopulatedAriaLabel:getPrepopulatedAriaLabel,onChangeProps:newProps=>{onChangeProps(newProps);}})]}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsx(View,{style:styles$n.horizontalRule}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LabelMedium,{children:"Visible labels"}),labels.map((label,labelIndex)=>createElement(LockedLabelSettings,{...label,key:labelIndex,containerStyle:!isDefiningPoint&&styles$n.lockedPointLabelContainer,expanded:true,onChangeProps:newLabel=>{handleLabelChange(newLabel,labelIndex);},onRemove:()=>{handleLabelRemove(labelIndex);}})),jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:plusCircle,onClick:()=>{const newLabel={...getDefaultFigureForType("label"),coord:[coord[0]+.5,coord[1]-labels.length],color:pointColor};onChangeProps({labels:[...labels,newLabel]});},style:styles$n.addButton,children:"Add visible label"}),onRemove&&jsxRuntimeExports.jsx(LockedFigureSettingsActions,{figureType:props.type,onMove:onMove,onRemove:onRemove})]})};const styles$n=StyleSheet.create({definingContainer:{marginTop:spacing.xSmall_8,marginBottom:0,marginLeft:-spacing.xxxSmall_4,marginRight:-spacing.xxxSmall_4,backgroundColor:color.white},definingPanel:{paddingBottom:spacing.xxSmall_6},lockedPointLabelContainer:{backgroundColor:color.white},row:{flexDirection:"row",alignItems:"center"},spaceUnder:{marginBottom:spacing.xSmall_8},addButton:{alignSelf:"start"},horizontalRule:{height:1,backgroundColor:color.offBlack16}});
1595
1597
 
1596
- const lengthZeroStr="The line cannot have length 0.";const LockedLineSettings=props=>{const{kind,points,color:lineColor,lineStyle="solid",showPoint1,showPoint2,labels,ariaLabel,onChangeProps,onMove,onRemove}=props;const[point1,point2]=points;const capitalizeKind=kind.charAt(0).toUpperCase()+kind.slice(1);const lineLabel=`${capitalizeKind} (${point1.coord[0]},
1597
- ${point1.coord[1]}), (${point2.coord[0]}, ${point2.coord[1]})`;const isInvalid=vector.equal(point1.coord,point2.coord);async function getPrepopulatedAriaLabel(){const visiblelabel=await joinLabelsAsSpokenMath(labels);const point1VisibleLabel=await joinLabelsAsSpokenMath(point1.labels);const point2VisibleLabel=await joinLabelsAsSpokenMath(point2.labels);const spokenPoint1X=await generateSpokenMathDetails(`$${point1.coord[0]}$`);const spokenPoint1Y=await generateSpokenMathDetails(`$${point1.coord[1]}$`);const spokenPoint2X=await generateSpokenMathDetails(`$${point2.coord[0]}$`);const spokenPoint2Y=await generateSpokenMathDetails(`$${point2.coord[1]}$`);let str;switch(kind){case "line":str=`${capitalizeKind}${visiblelabel} through point${point1VisibleLabel} at ${spokenPoint1X} comma ${spokenPoint1Y} and point${point2VisibleLabel} at ${spokenPoint2X} comma ${spokenPoint2Y}`;break;case "ray":str=`${capitalizeKind}${visiblelabel} from point${point1VisibleLabel} at ${spokenPoint1X} comma ${spokenPoint1Y} through point${point2VisibleLabel} at ${spokenPoint2X} comma ${spokenPoint2Y}`;break;case "segment":str=`${capitalizeKind}${visiblelabel} from point${point1VisibleLabel} at ${spokenPoint1X} comma ${spokenPoint1Y} to point${point2VisibleLabel} at ${spokenPoint2X} comma ${spokenPoint2Y}`;break;default:throw new UnreachableCaseError(kind,"Unknown line kind")}const lineAppearance=generateLockedFigureAppearanceDescription(lineColor,lineStyle);str+=lineAppearance;return str}function handleChangePoint(newPointProps,index){const newPoints=[...points];newPoints[index]={...points[index],...newPointProps};const oldMidpoint=vec.midpoint(points[0].coord,points[1].coord);const newMidpoint=vec.midpoint(newPoints[0].coord,newPoints[1].coord);const offset=[newMidpoint[0]-oldMidpoint[0],newMidpoint[1]-oldMidpoint[1]];const newLabels=labels.map((label,labelIndex)=>({...label,coord:[label.coord[0]+offset[0],label.coord[1]+offset[1]]}));onChangeProps({points:newPoints,labels:newLabels});}function handleColorChange(newColor){const newLabels=labels.map(label=>({...label,color:newColor}));onChangeProps({color:newColor,points:[{...point1,color:newColor,labels:point1.labels.map(label=>({...label,color:newColor}))},{...point2,color:newColor,labels:point2.labels.map(label=>({...label,color:newColor}))}],labels:newLabels});}function handleLabelChange(updatedLabel,labelIndex){const updatedLabels=[...labels];updatedLabels[labelIndex]={...labels[labelIndex],...updatedLabel};onChangeProps({labels:updatedLabels});}function handleLabelRemove(labelIndex){const updatedLabels=labels.filter((_,index)=>index!==labelIndex);onChangeProps({labels:updatedLabels});}return jsxRuntimeExports.jsxs(PerseusEditorAccordion,{expanded:props.expanded,onToggle:props.onToggle,header:jsxRuntimeExports.jsxs(View,{style:styles$m.row,children:[jsxRuntimeExports.jsx(LabelLarge,{children:lineLabel}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(LineSwatch,{color:lineColor,lineStyle:lineStyle})]}),children:[jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:[styles$m.row,styles$m.spaceUnder],children:["kind",jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsxs(SingleSelect,{selectedValue:kind,onChange:value=>onChangeProps({kind:value}),placeholder:"",children:[jsxRuntimeExports.jsx(OptionItem,{value:"line",label:"line"}),jsxRuntimeExports.jsx(OptionItem,{value:"ray",label:"ray"}),jsxRuntimeExports.jsx(OptionItem,{value:"segment",label:"segment"})]})]}),jsxRuntimeExports.jsxs(View,{style:styles$m.row,children:[jsxRuntimeExports.jsx(ColorSelect,{selectedValue:lineColor,onChange:handleColorChange}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LineStrokeSelect,{selectedValue:lineStyle,onChange:value=>onChangeProps({lineStyle:value})})]}),isInvalid&&jsxRuntimeExports.jsx(LabelMedium,{style:styles$m.errorText,children:lengthZeroStr}),jsxRuntimeExports.jsx(LockedPointSettings,{headerLabel:"Point 1",expanded:true,showPoint:showPoint1,error:isInvalid?lengthZeroStr:null,...point1,onTogglePoint:newValue=>onChangeProps({showPoint1:newValue}),onChangeProps:newProps=>handleChangePoint(newProps,0)}),jsxRuntimeExports.jsx(LockedPointSettings,{headerLabel:"Point 2",expanded:true,showPoint:showPoint2,error:isInvalid?lengthZeroStr:null,...point2,onTogglePoint:newValue=>onChangeProps({showPoint2:newValue}),onChangeProps:newProps=>handleChangePoint(newProps,1)}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(View,{style:styles$m.horizontalRule}),jsxRuntimeExports.jsx(LockedFigureAria,{ariaLabel:ariaLabel,getPrepopulatedAriaLabel:getPrepopulatedAriaLabel,onChangeProps:newProps=>{onChangeProps(newProps);}}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsx(View,{style:styles$m.horizontalRule}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LabelMedium,{children:"Visible labels"}),labels.map((label,labelIndex)=>createElement(LockedLabelSettings,{...label,key:labelIndex,expanded:true,onChangeProps:newLabel=>{handleLabelChange(newLabel,labelIndex);},onRemove:()=>{handleLabelRemove(labelIndex);},containerStyle:styles$m.labelContainer})),jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:plusCircle,onClick:()=>{const offsetPerLabel=[0,-1];const labelLocation=vec.add(vec.scale(offsetPerLabel,labels.length),vec.midpoint(points[0].coord,points[1].coord));const newLabel={...getDefaultFigureForType("label"),coord:labelLocation,color:lineColor};onChangeProps({labels:[...labels,newLabel]});},style:styles$m.addButton,children:"Add visible label"}),jsxRuntimeExports.jsx(LockedFigureSettingsActions,{figureType:props.type,onMove:onMove,onRemove:onRemove})]})};const styles$m=StyleSheet.create({row:{display:"flex",flexDirection:"row",alignItems:"center"},spaceUnder:{marginBottom:spacing.xSmall_8},errorText:{color:color.red},addButton:{alignSelf:"start"},horizontalRule:{height:1,backgroundColor:color.offBlack16},labelContainer:{backgroundColor:color.white}});
1598
-
1599
- const LineWeightSelect=props=>{const{selectedValue,containerStyle,onChange}=props;return jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:[{display:"flex",flexDirection:"row",alignItems:"center",minWidth:0},containerStyle],children:["weight",jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsxs(SingleSelect,{selectedValue:selectedValue,onChange:value=>onChange(value),placeholder:"",children:[jsxRuntimeExports.jsx(OptionItem,{value:"thin",label:"thin"}),jsxRuntimeExports.jsx(OptionItem,{value:"medium",label:"medium"}),jsxRuntimeExports.jsx(OptionItem,{value:"thick",label:"thick"})]})]})};
1598
+ const lengthZeroStr="The line cannot have length 0.";const LockedLineSettings=props=>{const{kind,points,color:lineColor,lineStyle="solid",showPoint1,showPoint2,weight,labels,ariaLabel,onChangeProps,onMove,onRemove}=props;const[point1,point2]=points;const capitalizeKind=kind.charAt(0).toUpperCase()+kind.slice(1);const lineLabel=`${capitalizeKind} (${point1.coord[0]},
1599
+ ${point1.coord[1]}), (${point2.coord[0]}, ${point2.coord[1]})`;const isInvalid=vector.equal(point1.coord,point2.coord);async function getPrepopulatedAriaLabel(){const visiblelabel=await joinLabelsAsSpokenMath(labels);const point1VisibleLabel=await joinLabelsAsSpokenMath(point1.labels);const point2VisibleLabel=await joinLabelsAsSpokenMath(point2.labels);const spokenPoint1X=await generateSpokenMathDetails(`$${point1.coord[0]}$`);const spokenPoint1Y=await generateSpokenMathDetails(`$${point1.coord[1]}$`);const spokenPoint2X=await generateSpokenMathDetails(`$${point2.coord[0]}$`);const spokenPoint2Y=await generateSpokenMathDetails(`$${point2.coord[1]}$`);let str;switch(kind){case "line":str=`${capitalizeKind}${visiblelabel} through point${point1VisibleLabel} at ${spokenPoint1X} comma ${spokenPoint1Y} and point${point2VisibleLabel} at ${spokenPoint2X} comma ${spokenPoint2Y}`;break;case "ray":str=`${capitalizeKind}${visiblelabel} from point${point1VisibleLabel} at ${spokenPoint1X} comma ${spokenPoint1Y} through point${point2VisibleLabel} at ${spokenPoint2X} comma ${spokenPoint2Y}`;break;case "segment":str=`${capitalizeKind}${visiblelabel} from point${point1VisibleLabel} at ${spokenPoint1X} comma ${spokenPoint1Y} to point${point2VisibleLabel} at ${spokenPoint2X} comma ${spokenPoint2Y}`;break;default:throw new UnreachableCaseError(kind,"Unknown line kind")}const lineAppearance=generateLockedFigureAppearanceDescription(lineColor,lineStyle,undefined,weight);str+=lineAppearance;return str}function handleChangePoint(newPointProps,index){const newPoints=[...points];newPoints[index]={...points[index],...newPointProps};const oldMidpoint=vec.midpoint(points[0].coord,points[1].coord);const newMidpoint=vec.midpoint(newPoints[0].coord,newPoints[1].coord);const offset=[newMidpoint[0]-oldMidpoint[0],newMidpoint[1]-oldMidpoint[1]];const newLabels=labels.map((label,labelIndex)=>({...label,coord:[label.coord[0]+offset[0],label.coord[1]+offset[1]]}));onChangeProps({points:newPoints,labels:newLabels});}function handleColorChange(newColor){const newLabels=labels.map(label=>({...label,color:newColor}));onChangeProps({color:newColor,points:[{...point1,color:newColor,labels:point1.labels.map(label=>({...label,color:newColor}))},{...point2,color:newColor,labels:point2.labels.map(label=>({...label,color:newColor}))}],labels:newLabels});}function handleLabelChange(updatedLabel,labelIndex){const updatedLabels=[...labels];updatedLabels[labelIndex]={...labels[labelIndex],...updatedLabel};onChangeProps({labels:updatedLabels});}function handleLabelRemove(labelIndex){const updatedLabels=labels.filter((_,index)=>index!==labelIndex);onChangeProps({labels:updatedLabels});}return jsxRuntimeExports.jsxs(PerseusEditorAccordion,{expanded:props.expanded,onToggle:props.onToggle,header:jsxRuntimeExports.jsxs(View,{style:styles$m.row,children:[jsxRuntimeExports.jsx(LabelLarge,{children:lineLabel}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(LineSwatch,{color:lineColor,lineStyle:lineStyle})]}),children:[jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:[styles$m.row,styles$m.spaceUnder],children:["kind",jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsxs(SingleSelect,{selectedValue:kind,onChange:value=>onChangeProps({kind:value}),placeholder:"",children:[jsxRuntimeExports.jsx(OptionItem,{value:"line",label:"line"}),jsxRuntimeExports.jsx(OptionItem,{value:"ray",label:"ray"}),jsxRuntimeExports.jsx(OptionItem,{value:"segment",label:"segment"})]})]}),jsxRuntimeExports.jsxs(View,{style:[styles$m.row,styles$m.spaceUnder],children:[jsxRuntimeExports.jsx(ColorSelect,{selectedValue:lineColor,onChange:handleColorChange}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LineStrokeSelect,{selectedValue:lineStyle,onChange:value=>onChangeProps({lineStyle:value})})]}),jsxRuntimeExports.jsx(LineWeightSelect,{selectedValue:weight,onChange:value=>onChangeProps({weight:value})}),isInvalid&&jsxRuntimeExports.jsx(LabelMedium,{style:styles$m.errorText,children:lengthZeroStr}),jsxRuntimeExports.jsx(LockedPointSettings,{headerLabel:"Point 1",expanded:true,showPoint:showPoint1,error:isInvalid?lengthZeroStr:null,...point1,onTogglePoint:newValue=>onChangeProps({showPoint1:newValue}),onChangeProps:newProps=>handleChangePoint(newProps,0)}),jsxRuntimeExports.jsx(LockedPointSettings,{headerLabel:"Point 2",expanded:true,showPoint:showPoint2,error:isInvalid?lengthZeroStr:null,...point2,onTogglePoint:newValue=>onChangeProps({showPoint2:newValue}),onChangeProps:newProps=>handleChangePoint(newProps,1)}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(View,{style:styles$m.horizontalRule}),jsxRuntimeExports.jsx(LockedFigureAria,{ariaLabel:ariaLabel,getPrepopulatedAriaLabel:getPrepopulatedAriaLabel,onChangeProps:newProps=>{onChangeProps(newProps);}}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsx(View,{style:styles$m.horizontalRule}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LabelMedium,{children:"Visible labels"}),labels.map((label,labelIndex)=>createElement(LockedLabelSettings,{...label,key:labelIndex,expanded:true,onChangeProps:newLabel=>{handleLabelChange(newLabel,labelIndex);},onRemove:()=>{handleLabelRemove(labelIndex);},containerStyle:styles$m.labelContainer})),jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:plusCircle,onClick:()=>{const offsetPerLabel=[0,-1];const labelLocation=vec.add(vec.scale(offsetPerLabel,labels.length),vec.midpoint(points[0].coord,points[1].coord));const newLabel={...getDefaultFigureForType("label"),coord:labelLocation,color:lineColor};onChangeProps({labels:[...labels,newLabel]});},style:styles$m.addButton,children:"Add visible label"}),jsxRuntimeExports.jsx(LockedFigureSettingsActions,{figureType:props.type,onMove:onMove,onRemove:onRemove})]})};const styles$m=StyleSheet.create({row:{display:"flex",flexDirection:"row",alignItems:"center"},spaceUnder:{marginBottom:spacing.xSmall_8},errorText:{color:color.red},addButton:{alignSelf:"start"},horizontalRule:{height:1,backgroundColor:color.offBlack16},labelContainer:{backgroundColor:color.white}});
1600
1600
 
1601
1601
  const PolygonSwatch=props=>{const{color,fillStyle,strokeStyle}=props;return jsxRuntimeExports.jsx(View,{"aria-label":`${color}, stroke ${strokeStyle}, fill ${fillStyle}`,style:[styles$l.container,{border:`4px ${strokeStyle} ${lockedFigureColors[color]}`}],children:jsxRuntimeExports.jsx(View,{style:[styles$l.innerSquare,{backgroundColor:lockedFigureColors[color],opacity:fillStyle==="white"?0:lockedFigureFillStyles[fillStyle]}]})})};const styles$l=StyleSheet.create({container:{outline:`2px solid ${color.offWhite}`,width:spacing.large_24,height:spacing.large_24,backgroundColor:color.white,alignItems:"center",justifyContent:"center"},innerSquare:{width:20,height:20}});
1602
1602
 
1603
- const LockedPolygonSettings=props=>{const{points,color,showVertices,fillStyle,strokeStyle,weight="medium",labels,ariaLabel,expanded,onToggle,onChangeProps,onMove,onRemove}=props;async function getPrepopulatedAriaLabel(){const visiblelabel=await joinLabelsAsSpokenMath(labels);let str=`Polygon${visiblelabel} with ${points.length} sides, vertices at `;const pointsList=await Promise.all(points.map(async([x,y])=>{const spokenX=await generateSpokenMathDetails(`$${x}$`);const spokenY=await generateSpokenMathDetails(`$${y}$`);return `${spokenX} comma ${spokenY}`}));str+=pointsList.join(", ");const polygonAppearance=generateLockedFigureAppearanceDescription(color,strokeStyle,fillStyle,weight);str+=polygonAppearance;return str}function handleColorChange(newValue){const newProps={color:newValue};newProps.labels=labels.map(label=>({...label,color:newValue}));onChangeProps(newProps);}function handlePolygonMove(movement){switch(movement){case "up":onChangeProps({points:points.map(([x,y])=>[x,y+1]),labels:labels.map(label=>({...label,coord:[label.coord[0],label.coord[1]+1]}))});break;case "down":onChangeProps({points:points.map(([x,y])=>[x,y-1]),labels:labels.map(label=>({...label,coord:[label.coord[0],label.coord[1]-1]}))});break;case "left":onChangeProps({points:points.map(([x,y])=>[x-1,y]),labels:labels.map(label=>({...label,coord:[label.coord[0]-1,label.coord[1]]}))});break;case "right":onChangeProps({points:points.map(([x,y])=>[x+1,y]),labels:labels.map(label=>({...label,coord:[label.coord[0]+1,label.coord[1]]}))});break}}function handleLabelChange(updatedLabel,labelIndex){const updatedLabels=[...labels];updatedLabels[labelIndex]={...labels[labelIndex],...updatedLabel};onChangeProps({labels:updatedLabels});}function handleLabelRemove(labelIndex){const updatedLabels=labels.filter((_,index)=>index!==labelIndex);onChangeProps({labels:updatedLabels});}return jsxRuntimeExports.jsxs(PerseusEditorAccordion,{expanded:expanded,onToggle:onToggle,header:jsxRuntimeExports.jsxs(View,{style:styles$k.row,children:[jsxRuntimeExports.jsx(LabelLarge,{children:`Polygon, ${points.length} sides`}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(PolygonSwatch,{color:color,fillStyle:fillStyle,strokeStyle:strokeStyle})]}),children:[jsxRuntimeExports.jsxs(View,{style:[styles$k.row,styles$k.spaceUnder],children:[jsxRuntimeExports.jsx(ColorSelect,{selectedValue:color,onChange:handleColorChange}),jsxRuntimeExports.jsx(Strut,{size:spacing.medium_16}),jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:[styles$k.row,styles$k.truncatedWidth],children:["fill",jsxRuntimeExports.jsx(Strut,{size:spacing.xxSmall_6}),jsxRuntimeExports.jsx(SingleSelect,{selectedValue:fillStyle,onChange:value=>onChangeProps({fillStyle:value}),placeholder:"",children:Object.keys(lockedFigureFillStyles).map(option=>jsxRuntimeExports.jsx(OptionItem,{value:option,label:option},option))})]})]}),jsxRuntimeExports.jsx(LineStrokeSelect,{selectedValue:strokeStyle,onChange:value=>onChangeProps({strokeStyle:value}),containerStyle:styles$k.spaceUnder}),jsxRuntimeExports.jsx(LineWeightSelect,{selectedValue:weight,onChange:value=>onChangeProps({weight:value}),containerStyle:styles$k.spaceUnder}),jsxRuntimeExports.jsx(LabeledSwitch,{label:"show vertices",checked:showVertices,onChange:newValue=>onChangeProps({showVertices:newValue}),style:styles$k.spaceUnder}),jsxRuntimeExports.jsxs(PerseusEditorAccordion,{header:jsxRuntimeExports.jsx(LabelLarge,{children:"Points"}),expanded:true,containerStyle:styles$k.pointAccordionContainer,panelStyle:styles$k.pointAccordionPanel,children:[points.map((point,index)=>{const pointLabel=String.fromCharCode(65+index);return jsxRuntimeExports.jsxs(View,{style:[styles$k.row,styles$k.spaceUnder],children:[jsxRuntimeExports.jsx(LabelLarge,{children:`${pointLabel}:`}),jsxRuntimeExports.jsx(Strut,{size:spacing.medium_16}),jsxRuntimeExports.jsx(CoordinatePairInput,{coord:point,labels:["x","y"],onChange:newValue=>{const newPoints=[...points];newPoints[index]=newValue;props.onChangeProps({points:newPoints});}}),points.length>3&&jsxRuntimeExports.jsx(IconButton,{"aria-label":`Delete polygon point ${pointLabel}`,icon:minusCircle,kind:"tertiary",actionType:"destructive",onClick:()=>{const newPoints=[...points];newPoints.splice(index,1);props.onChangeProps({points:newPoints});},style:styles$k.icon})]},`locked-polygon-point-index-${index}`)}),jsxRuntimeExports.jsxs(View,{style:[styles$k.row,styles$k.polygonActionsContainer],children:[jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:plusCircle,onClick:()=>{props.onChangeProps({points:[...points,[0,0]]});},children:"Add point"}),jsxRuntimeExports.jsx(Spring,{}),jsxRuntimeExports.jsxs(View,{style:styles$k.movementButtonsContainer,children:[jsxRuntimeExports.jsx(IconButton,{"aria-label":"Move polygon up",size:"small",icon:arrowFatUp,kind:"tertiary",onClick:()=>handlePolygonMove("up")}),jsxRuntimeExports.jsxs(View,{style:styles$k.row,children:[jsxRuntimeExports.jsx(IconButton,{"aria-label":"Move polygon left",size:"small",icon:arrowFatLeft,kind:"tertiary",onClick:()=>handlePolygonMove("left")}),jsxRuntimeExports.jsx(IconButton,{"aria-label":"Move polygon down",size:"small",icon:arrowFatDown,kind:"tertiary",onClick:()=>handlePolygonMove("down")}),jsxRuntimeExports.jsx(IconButton,{"aria-label":"Move polygon right",size:"small",icon:arrowFatRight,kind:"tertiary",onClick:()=>handlePolygonMove("right")})]})]})]})]}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(View,{style:styles$k.horizontalRule}),jsxRuntimeExports.jsx(LockedFigureAria,{ariaLabel:ariaLabel,getPrepopulatedAriaLabel:getPrepopulatedAriaLabel,onChangeProps:newProps=>{onChangeProps(newProps);}}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsx(View,{style:styles$k.horizontalRule}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LabelMedium,{children:"Visible labels"}),labels.map((label,labelIndex)=>createElement(LockedLabelSettings,{...label,key:labelIndex,expanded:true,onChangeProps:newLabel=>{handleLabelChange(newLabel,labelIndex);},onRemove:()=>{handleLabelRemove(labelIndex);},containerStyle:styles$k.labelContainer})),jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:plusCircle,onClick:()=>{const newLabel={...getDefaultFigureForType("label"),coord:[points[0][0],points[0][1]-labels.length],color:color};onChangeProps({labels:[...labels,newLabel]});},style:styles$k.addButton,children:"Add visible label"}),jsxRuntimeExports.jsx(LockedFigureSettingsActions,{figureType:props.type,onMove:onMove,onRemove:onRemove})]})};const styles$k=StyleSheet.create({row:{display:"flex",flexDirection:"row",alignItems:"center"},pointAccordionContainer:{backgroundColor:color.white},pointAccordionPanel:{alignItems:"start"},icon:{marginInlineStart:spacing.xxxSmall_4},polygonActionsContainer:{width:"100%"},movementButtonsContainer:{display:"flex",flexDirection:"column",alignItems:"center",minWidth:"fit-content"},spaceUnder:{marginBottom:spacing.xSmall_8},truncatedWidth:{minWidth:0},addButton:{alignSelf:"start"},labelContainer:{backgroundColor:color.white},horizontalRule:{height:1,backgroundColor:color.offBlack16}});
1603
+ const LockedPolygonSettings=props=>{const{points,color,showVertices,fillStyle,strokeStyle,weight,labels,ariaLabel,expanded,onToggle,onChangeProps,onMove,onRemove}=props;async function getPrepopulatedAriaLabel(){const visiblelabel=await joinLabelsAsSpokenMath(labels);let str=`Polygon${visiblelabel} with ${points.length} sides, vertices at `;const pointsList=await Promise.all(points.map(async([x,y])=>{const spokenX=await generateSpokenMathDetails(`$${x}$`);const spokenY=await generateSpokenMathDetails(`$${y}$`);return `${spokenX} comma ${spokenY}`}));str+=pointsList.join(", ");const polygonAppearance=generateLockedFigureAppearanceDescription(color,strokeStyle,fillStyle,weight);str+=polygonAppearance;return str}function handleColorChange(newValue){const newProps={color:newValue};newProps.labels=labels.map(label=>({...label,color:newValue}));onChangeProps(newProps);}function handlePolygonMove(movement){switch(movement){case "up":onChangeProps({points:points.map(([x,y])=>[x,y+1]),labels:labels.map(label=>({...label,coord:[label.coord[0],label.coord[1]+1]}))});break;case "down":onChangeProps({points:points.map(([x,y])=>[x,y-1]),labels:labels.map(label=>({...label,coord:[label.coord[0],label.coord[1]-1]}))});break;case "left":onChangeProps({points:points.map(([x,y])=>[x-1,y]),labels:labels.map(label=>({...label,coord:[label.coord[0]-1,label.coord[1]]}))});break;case "right":onChangeProps({points:points.map(([x,y])=>[x+1,y]),labels:labels.map(label=>({...label,coord:[label.coord[0]+1,label.coord[1]]}))});break}}function handleLabelChange(updatedLabel,labelIndex){const updatedLabels=[...labels];updatedLabels[labelIndex]={...labels[labelIndex],...updatedLabel};onChangeProps({labels:updatedLabels});}function handleLabelRemove(labelIndex){const updatedLabels=labels.filter((_,index)=>index!==labelIndex);onChangeProps({labels:updatedLabels});}return jsxRuntimeExports.jsxs(PerseusEditorAccordion,{expanded:expanded,onToggle:onToggle,header:jsxRuntimeExports.jsxs(View,{style:styles$k.row,children:[jsxRuntimeExports.jsx(LabelLarge,{children:`Polygon, ${points.length} sides`}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(PolygonSwatch,{color:color,fillStyle:fillStyle,strokeStyle:strokeStyle})]}),children:[jsxRuntimeExports.jsxs(View,{style:[styles$k.row,styles$k.spaceUnder],children:[jsxRuntimeExports.jsx(ColorSelect,{selectedValue:color,onChange:handleColorChange}),jsxRuntimeExports.jsx(Strut,{size:spacing.medium_16}),jsxRuntimeExports.jsxs(LabelMedium,{tag:"label",style:[styles$k.row,styles$k.truncatedWidth],children:["fill",jsxRuntimeExports.jsx(Strut,{size:spacing.xxSmall_6}),jsxRuntimeExports.jsx(SingleSelect,{selectedValue:fillStyle,onChange:value=>onChangeProps({fillStyle:value}),placeholder:"",children:Object.keys(lockedFigureFillStyles).map(option=>jsxRuntimeExports.jsx(OptionItem,{value:option,label:option},option))})]})]}),jsxRuntimeExports.jsx(LineStrokeSelect,{selectedValue:strokeStyle,onChange:value=>onChangeProps({strokeStyle:value}),containerStyle:styles$k.spaceUnder}),jsxRuntimeExports.jsx(LineWeightSelect,{selectedValue:weight,onChange:value=>onChangeProps({weight:value}),containerStyle:styles$k.spaceUnder}),jsxRuntimeExports.jsx(LabeledSwitch,{label:"show vertices",checked:showVertices,onChange:newValue=>onChangeProps({showVertices:newValue}),style:styles$k.spaceUnder}),jsxRuntimeExports.jsxs(PerseusEditorAccordion,{header:jsxRuntimeExports.jsx(LabelLarge,{children:"Points"}),expanded:true,containerStyle:styles$k.pointAccordionContainer,panelStyle:styles$k.pointAccordionPanel,children:[points.map((point,index)=>{const pointLabel=String.fromCharCode(65+index);return jsxRuntimeExports.jsxs(View,{style:[styles$k.row,styles$k.spaceUnder],children:[jsxRuntimeExports.jsx(LabelLarge,{children:`${pointLabel}:`}),jsxRuntimeExports.jsx(Strut,{size:spacing.medium_16}),jsxRuntimeExports.jsx(CoordinatePairInput,{coord:point,labels:["x","y"],onChange:newValue=>{const newPoints=[...points];newPoints[index]=newValue;props.onChangeProps({points:newPoints});}}),points.length>3&&jsxRuntimeExports.jsx(IconButton,{"aria-label":`Delete polygon point ${pointLabel}`,icon:minusCircle,kind:"tertiary",actionType:"destructive",onClick:()=>{const newPoints=[...points];newPoints.splice(index,1);props.onChangeProps({points:newPoints});},style:styles$k.icon})]},`locked-polygon-point-index-${index}`)}),jsxRuntimeExports.jsxs(View,{style:[styles$k.row,styles$k.polygonActionsContainer],children:[jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:plusCircle,onClick:()=>{props.onChangeProps({points:[...points,[0,0]]});},children:"Add point"}),jsxRuntimeExports.jsx(Spring,{}),jsxRuntimeExports.jsxs(View,{style:styles$k.movementButtonsContainer,children:[jsxRuntimeExports.jsx(IconButton,{"aria-label":"Move polygon up",size:"small",icon:arrowFatUp,kind:"tertiary",onClick:()=>handlePolygonMove("up")}),jsxRuntimeExports.jsxs(View,{style:styles$k.row,children:[jsxRuntimeExports.jsx(IconButton,{"aria-label":"Move polygon left",size:"small",icon:arrowFatLeft,kind:"tertiary",onClick:()=>handlePolygonMove("left")}),jsxRuntimeExports.jsx(IconButton,{"aria-label":"Move polygon down",size:"small",icon:arrowFatDown,kind:"tertiary",onClick:()=>handlePolygonMove("down")}),jsxRuntimeExports.jsx(IconButton,{"aria-label":"Move polygon right",size:"small",icon:arrowFatRight,kind:"tertiary",onClick:()=>handlePolygonMove("right")})]})]})]})]}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(View,{style:styles$k.horizontalRule}),jsxRuntimeExports.jsx(LockedFigureAria,{ariaLabel:ariaLabel,getPrepopulatedAriaLabel:getPrepopulatedAriaLabel,onChangeProps:newProps=>{onChangeProps(newProps);}}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsx(View,{style:styles$k.horizontalRule}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LabelMedium,{children:"Visible labels"}),labels.map((label,labelIndex)=>createElement(LockedLabelSettings,{...label,key:labelIndex,expanded:true,onChangeProps:newLabel=>{handleLabelChange(newLabel,labelIndex);},onRemove:()=>{handleLabelRemove(labelIndex);},containerStyle:styles$k.labelContainer})),jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:plusCircle,onClick:()=>{const newLabel={...getDefaultFigureForType("label"),coord:[points[0][0],points[0][1]-labels.length],color:color};onChangeProps({labels:[...labels,newLabel]});},style:styles$k.addButton,children:"Add visible label"}),jsxRuntimeExports.jsx(LockedFigureSettingsActions,{figureType:props.type,onMove:onMove,onRemove:onRemove})]})};const styles$k=StyleSheet.create({row:{display:"flex",flexDirection:"row",alignItems:"center"},pointAccordionContainer:{backgroundColor:color.white},pointAccordionPanel:{alignItems:"start"},icon:{marginInlineStart:spacing.xxxSmall_4},polygonActionsContainer:{width:"100%"},movementButtonsContainer:{display:"flex",flexDirection:"column",alignItems:"center",minWidth:"fit-content"},spaceUnder:{marginBottom:spacing.xSmall_8},truncatedWidth:{minWidth:0},addButton:{alignSelf:"start"},labelContainer:{backgroundColor:color.white},horizontalRule:{height:1,backgroundColor:color.offBlack16}});
1604
1604
 
1605
- const lengthErrorMessage="The vector cannot have length 0.";const LockedVectorSettings=props=>{const{points,color:lineColor,labels,ariaLabel,onChangeProps,onMove,onRemove}=props;const[tail,tip]=points;const lineLabel=`Vector (${tail[0]}, ${tail[1]}), (${tip[0]}, ${tip[1]})`;const isInvalid=vector.equal(tail,tip);async function getPrepopulatedAriaLabel(){const visiblelabel=await joinLabelsAsSpokenMath(labels);const spokenTailX=await generateSpokenMathDetails(`$${tail[0]}$`);const spokenTailY=await generateSpokenMathDetails(`$${tail[1]}$`);const spokenTipX=await generateSpokenMathDetails(`$${tip[0]}$`);const spokenTipY=await generateSpokenMathDetails(`$${tip[1]}$`);let str=`Vector${visiblelabel} from ${spokenTailX} comma ${spokenTailY} to ${spokenTipX} comma ${spokenTipY}`;const vectorAppearance=generateLockedFigureAppearanceDescription(lineColor);str+=vectorAppearance;return str}function handleChangePoint(newCoord,index){if(typeof newCoord!=="undefined"){const newPoints=[...points];newPoints[index]=[...newCoord];const oldMidpoint=vec.midpoint(tail,tip);const newMidpoint=vec.midpoint(newPoints[0],newPoints[1]);const offset=vec.sub(newMidpoint,oldMidpoint);const newLabels=labels.map(label=>({...label,coord:vec.add(label.coord,offset)}));onChangeProps({points:newPoints,labels:newLabels});}}function handleColorChange(newColor){const newProps={color:newColor};newProps.labels=labels.map(label=>({...label,color:newColor}));onChangeProps(newProps);}function handleLabelChange(updatedLabel,labelIndex){const updatedLabels=[...labels];updatedLabels[labelIndex]={...labels[labelIndex],...updatedLabel};onChangeProps({labels:updatedLabels});}function handleLabelRemove(labelIndex){const updatedLabels=labels.filter((_,index)=>index!==labelIndex);onChangeProps({labels:updatedLabels});}return jsxRuntimeExports.jsxs(PerseusEditorAccordion,{expanded:props.expanded,onToggle:props.onToggle,header:jsxRuntimeExports.jsxs(View,{style:styles$j.row,children:[jsxRuntimeExports.jsx(LabelLarge,{children:lineLabel}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(LineSwatch,{color:lineColor,lineStyle:"solid"})]}),children:[jsxRuntimeExports.jsx(View,{style:[styles$j.row,styles$j.spaceUnder],children:jsxRuntimeExports.jsx(ColorSelect,{selectedValue:lineColor,onChange:handleColorChange})}),isInvalid&&jsxRuntimeExports.jsx(LabelMedium,{style:styles$j.errorText,children:lengthErrorMessage}),jsxRuntimeExports.jsx(PerseusEditorAccordion,{expanded:true,containerStyle:styles$j.container,panelStyle:styles$j.accordionPanel,header:jsxRuntimeExports.jsx(View,{style:styles$j.row,children:jsxRuntimeExports.jsx(LabelLarge,{children:`Tail (${tail[0]}, ${tail[1]})`})}),children:jsxRuntimeExports.jsx(CoordinatePairInput,{coord:tail,error:isInvalid,onChange:newProps=>{handleChangePoint(newProps,0);}})}),jsxRuntimeExports.jsx(PerseusEditorAccordion,{expanded:true,containerStyle:styles$j.container,panelStyle:styles$j.accordionPanel,header:jsxRuntimeExports.jsx(View,{style:styles$j.row,children:jsxRuntimeExports.jsx(LabelLarge,{children:`Tip (${tip[0]}, ${tip[1]})`})}),children:jsxRuntimeExports.jsx(CoordinatePairInput,{coord:tip,error:isInvalid,onChange:newProps=>{handleChangePoint(newProps,1);}})}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(View,{style:styles$j.horizontalRule}),jsxRuntimeExports.jsx(LockedFigureAria,{ariaLabel:ariaLabel,getPrepopulatedAriaLabel:getPrepopulatedAriaLabel,onChangeProps:newProps=>{onChangeProps(newProps);}}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsx(View,{style:styles$j.horizontalRule}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LabelMedium,{children:"Visible labels"}),labels.map((label,labelIndex)=>createElement(LockedLabelSettings,{...label,key:labelIndex,expanded:true,onChangeProps:newLabel=>{handleLabelChange(newLabel,labelIndex);},onRemove:()=>{handleLabelRemove(labelIndex);},containerStyle:styles$j.labelContainer})),jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:plusCircle,onClick:()=>{const offsetPerLabel=[0,-1];const labelLocation=vec.add(vec.scale(offsetPerLabel,labels.length),vec.midpoint(tail,tip));const newLabel={...getDefaultFigureForType("label"),coord:labelLocation,color:lineColor};onChangeProps({labels:[...labels,newLabel]});},style:styles$j.addButton,children:"Add visible label"}),jsxRuntimeExports.jsx(LockedFigureSettingsActions,{figureType:props.type,onMove:onMove,onRemove:onRemove})]})};const styles$j=StyleSheet.create({accordionPanel:{paddingBottom:spacing.medium_16},container:{marginTop:spacing.xSmall_8,marginBottom:0,marginLeft:-spacing.xxxSmall_4,marginRight:-spacing.xxxSmall_4,backgroundColor:color.white},errorText:{color:color.red,marginTop:spacing.xSmall_8},row:{flexDirection:"row",alignItems:"center"},addButton:{alignSelf:"start"},horizontalRule:{height:1,backgroundColor:color.offBlack16},labelContainer:{backgroundColor:color.white}});
1605
+ const lengthErrorMessage="The vector cannot have length 0.";const LockedVectorSettings=props=>{const{points,color:lineColor,weight,labels,ariaLabel,onChangeProps,onMove,onRemove}=props;const[tail,tip]=points;const lineLabel=`Vector (${tail[0]}, ${tail[1]}), (${tip[0]}, ${tip[1]})`;const isInvalid=vector.equal(tail,tip);async function getPrepopulatedAriaLabel(){const visiblelabel=await joinLabelsAsSpokenMath(labels);const spokenTailX=await generateSpokenMathDetails(`$${tail[0]}$`);const spokenTailY=await generateSpokenMathDetails(`$${tail[1]}$`);const spokenTipX=await generateSpokenMathDetails(`$${tip[0]}$`);const spokenTipY=await generateSpokenMathDetails(`$${tip[1]}$`);let str=`Vector${visiblelabel} from ${spokenTailX} comma ${spokenTailY} to ${spokenTipX} comma ${spokenTipY}`;const vectorAppearance=generateLockedFigureAppearanceDescription(lineColor,"solid",undefined,weight);str+=vectorAppearance;return str}function handleChangePoint(newCoord,index){if(typeof newCoord!=="undefined"){const newPoints=[...points];newPoints[index]=[...newCoord];const oldMidpoint=vec.midpoint(tail,tip);const newMidpoint=vec.midpoint(newPoints[0],newPoints[1]);const offset=vec.sub(newMidpoint,oldMidpoint);const newLabels=labels.map(label=>({...label,coord:vec.add(label.coord,offset)}));onChangeProps({points:newPoints,labels:newLabels});}}function handleColorChange(newColor){const newProps={color:newColor};newProps.labels=labels.map(label=>({...label,color:newColor}));onChangeProps(newProps);}function handleLabelChange(updatedLabel,labelIndex){const updatedLabels=[...labels];updatedLabels[labelIndex]={...labels[labelIndex],...updatedLabel};onChangeProps({labels:updatedLabels});}function handleLabelRemove(labelIndex){const updatedLabels=labels.filter((_,index)=>index!==labelIndex);onChangeProps({labels:updatedLabels});}return jsxRuntimeExports.jsxs(PerseusEditorAccordion,{expanded:props.expanded,onToggle:props.onToggle,header:jsxRuntimeExports.jsxs(View,{style:styles$j.row,children:[jsxRuntimeExports.jsx(LabelLarge,{children:lineLabel}),jsxRuntimeExports.jsx(Strut,{size:spacing.xSmall_8}),jsxRuntimeExports.jsx(LineSwatch,{color:lineColor,lineStyle:"solid"})]}),children:[jsxRuntimeExports.jsx(ColorSelect,{selectedValue:lineColor,onChange:handleColorChange,style:{marginBottom:sizing.size_080}}),jsxRuntimeExports.jsx(LineWeightSelect,{selectedValue:weight,onChange:value=>onChangeProps({weight:value})}),isInvalid&&jsxRuntimeExports.jsx(LabelMedium,{style:styles$j.errorText,children:lengthErrorMessage}),jsxRuntimeExports.jsx(PerseusEditorAccordion,{expanded:true,containerStyle:styles$j.container,panelStyle:styles$j.accordionPanel,header:jsxRuntimeExports.jsx(View,{style:styles$j.row,children:jsxRuntimeExports.jsx(LabelLarge,{children:`Tail (${tail[0]}, ${tail[1]})`})}),children:jsxRuntimeExports.jsx(CoordinatePairInput,{coord:tail,error:isInvalid,onChange:newProps=>{handleChangePoint(newProps,0);}})}),jsxRuntimeExports.jsx(PerseusEditorAccordion,{expanded:true,containerStyle:styles$j.container,panelStyle:styles$j.accordionPanel,header:jsxRuntimeExports.jsx(View,{style:styles$j.row,children:jsxRuntimeExports.jsx(LabelLarge,{children:`Tip (${tip[0]}, ${tip[1]})`})}),children:jsxRuntimeExports.jsx(CoordinatePairInput,{coord:tip,error:isInvalid,onChange:newProps=>{handleChangePoint(newProps,1);}})}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(View,{style:styles$j.horizontalRule}),jsxRuntimeExports.jsx(LockedFigureAria,{ariaLabel:ariaLabel,getPrepopulatedAriaLabel:getPrepopulatedAriaLabel,onChangeProps:newProps=>{onChangeProps(newProps);}}),jsxRuntimeExports.jsx(Strut,{size:spacing.xxxSmall_4}),jsxRuntimeExports.jsx(View,{style:styles$j.horizontalRule}),jsxRuntimeExports.jsx(Strut,{size:spacing.small_12}),jsxRuntimeExports.jsx(LabelMedium,{children:"Visible labels"}),labels.map((label,labelIndex)=>createElement(LockedLabelSettings,{...label,key:labelIndex,expanded:true,onChangeProps:newLabel=>{handleLabelChange(newLabel,labelIndex);},onRemove:()=>{handleLabelRemove(labelIndex);},containerStyle:styles$j.labelContainer})),jsxRuntimeExports.jsx(Button,{kind:"tertiary",startIcon:plusCircle,onClick:()=>{const offsetPerLabel=[0,-1];const labelLocation=vec.add(vec.scale(offsetPerLabel,labels.length),vec.midpoint(tail,tip));const newLabel={...getDefaultFigureForType("label"),coord:labelLocation,color:lineColor};onChangeProps({labels:[...labels,newLabel]});},style:styles$j.addButton,children:"Add visible label"}),jsxRuntimeExports.jsx(LockedFigureSettingsActions,{figureType:props.type,onMove:onMove,onRemove:onRemove})]})};const styles$j=StyleSheet.create({accordionPanel:{paddingBottom:spacing.medium_16},container:{marginTop:spacing.xSmall_8,marginBottom:0,marginLeft:-spacing.xxxSmall_4,marginRight:-spacing.xxxSmall_4,backgroundColor:color.white},errorText:{color:color.red,marginTop:spacing.xSmall_8},row:{flexDirection:"row",alignItems:"center"},addButton:{alignSelf:"start"},horizontalRule:{height:1,backgroundColor:color.offBlack16},labelContainer:{backgroundColor:color.white}});
1606
1606
 
1607
1607
  const LockedFigureSettings=props=>{switch(props.type){case "point":return jsxRuntimeExports.jsx(LockedPointSettings,{...props});case "line":return jsxRuntimeExports.jsx(LockedLineSettings,{...props});case "vector":return jsxRuntimeExports.jsx(LockedVectorSettings,{...props});case "ellipse":return jsxRuntimeExports.jsx(LockedEllipseSettings,{...props});case "polygon":return jsxRuntimeExports.jsx(LockedPolygonSettings,{...props});case "function":return jsxRuntimeExports.jsx(LockedFunctionSettings,{...props});case "label":return jsxRuntimeExports.jsx(LockedLabelSettings,{...props});default:throw new UnreachableCaseError(props)}};
1608
1608
 
@@ -1685,7 +1685,7 @@ const{InfoTip: InfoTip$3,NumberInput: NumberInput$2,RangeInput,TextListEditor: T
1685
1685
 
1686
1686
  const{NumberInput: NumberInput$1,TextInput}=components;function validateOptions(height,programID){const errors=[];if(programID===""){errors.push("The program ID is required.");}if(!Number.isInteger(height)||height<1){errors.push("The height must be a positive integer.");}return errors}class PythonProgramEditor extends React.Component{serialize(){return {programID:this.props.programID,height:this.props.height}}render(){return jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsxs("label",{children:["User Program ID:"," ",jsxRuntimeExports.jsx(TextInput,{value:this.props.programID,onChange:this.change("programID"),placeholder:"123"})]}),jsxRuntimeExports.jsx("br",{}),jsxRuntimeExports.jsxs("label",{children:["Height:"," ",jsxRuntimeExports.jsx(NumberInput$1,{value:this.props.height,onChange:this.change("height"),placeholder:"400"})]})]})}constructor(...args){super(...args),this.change=(...args)=>{return Changeable.change.apply(this,args)},this.getSaveWarnings=()=>{return validateOptions(this.props.height,this.props.programID)};}}PythonProgramEditor.widgetName="python-program";PythonProgramEditor.defaultProps=pythonProgramLogic.defaultWidgetOptions;
1687
1687
 
1688
- class ChoiceEditor extends React.Component{render(){const checkedClass=this.props.choice.correct?"correct":"incorrect";let placeholder="Type a choice here...";if(this.props.choice.isNoneOfTheAbove){placeholder=this.props.choice.correct?"Type the answer to reveal to the user...":"None of the above";}const editor=jsxRuntimeExports.jsx(Editor,{ref:"content-editor",apiOptions:this.props.apiOptions,content:this.props.choice.content||"",widgetEnabled:false,placeholder:placeholder,disabled:this.props.choice.isNoneOfTheAbove&&!this.props.choice.correct,onChange:this.props.onContentChange});const rationaleEditor=jsxRuntimeExports.jsx(Editor,{ref:"rationale-editor",apiOptions:this.props.apiOptions,content:this.props.choice.rationale||"",widgetEnabled:false,placeholder:`Why is this choice ${checkedClass}?`,onChange:this.props.onRationaleChange});const deleteLink=jsxRuntimeExports.jsx("a",{className:"simple-button orange delete-choice",href:"#",onClick:e=>{e.stopPropagation();e.preventDefault();this.props.onDelete();},title:"Remove this choice",children:"Remove this choice"});return jsxRuntimeExports.jsxs("div",{className:"choice-rationale-editors",children:[jsxRuntimeExports.jsx("div",{className:`choice-editor ${checkedClass}`,children:editor}),jsxRuntimeExports.jsx("div",{className:"rationale-editor",children:rationaleEditor}),this.props.showDelete&&deleteLink]})}}class RadioEditor extends React.Component{onRationaleChange(choiceIndex,newRationale){const choices=this.props.choices.slice();choices[choiceIndex]=_.extend({},choices[choiceIndex],{rationale:newRationale});if(newRationale===""){delete choices[choiceIndex].rationale;}this.props.onChange({choices:choices});}serialize(){const{choices,randomize,multipleSelect,countChoices,hasNoneOfTheAbove,deselectEnabled}=this.props;return {choices,randomize,multipleSelect,countChoices,hasNoneOfTheAbove,deselectEnabled,numCorrect:deriveNumCorrect({...this.props,numCorrect:undefined})}}render(){const numCorrect=_.reduce(this.props.choices,function(memo,choice){return choice.correct?memo+1:memo},0);return jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx(Link$1,{href:"https://www.khanacademy.org/internal-courses/content-creation-best-practices/xe46daa512cd9c644:question-writing/xe46daa512cd9c644:multiple-choice/a/stems",target:"_blank",children:"Multiple choice best practices"}),jsxRuntimeExports.jsxs("div",{className:"perseus-widget-row",children:[jsxRuntimeExports.jsx(LabeledSwitch,{label:"Randomize order",checked:this.props.randomize,onChange:value=>{this.props.onChange({randomize:value});},style:{marginBlockEnd:sizing.size_060}}),jsxRuntimeExports.jsx(LabeledSwitch,{label:"Multiple selections",checked:this.props.multipleSelect,onChange:value=>{this.onMultipleSelectChange({multipleSelect:value});},style:{marginBlockEnd:sizing.size_060}}),this.props.multipleSelect&&jsxRuntimeExports.jsx(LabeledSwitch,{label:"Specify number correct",checked:this.props.countChoices,onChange:value=>{this.onCountChoicesChange({countChoices:value});},style:{marginBlockEnd:sizing.size_060}})]}),jsxRuntimeExports.jsx(BaseRadio,{multipleSelect:this.props.multipleSelect,countChoices:this.props.countChoices,numCorrect:numCorrect,editMode:true,labelWrap:false,apiOptions:this.props.apiOptions,reviewMode:false,choices:this.props.choices.map((choice,i)=>{return {content:jsxRuntimeExports.jsx(ChoiceEditor,{ref:`choice-editor${i}`,apiOptions:this.props.apiOptions,choice:choice,onContentChange:newProps=>{if(newProps.content!=null){this.onContentChange(i,newProps.content);}},onRationaleChange:newProps=>{if(newProps.content!=null){this.onRationaleChange(i,newProps.content);}},onDelete:()=>this.onDelete(i),showDelete:this.props.choices.length>=2}),isNoneOfTheAbove:choice.isNoneOfTheAbove,checked:choice.correct}},this),onChange:this.onChange}),jsxRuntimeExports.jsxs("div",{className:"add-choice-container",children:[jsxRuntimeExports.jsx(Button,{size:"small",kind:"tertiary",startIcon:plusIcon,onClick:this.addChoice.bind(this,false),children:"Add a choice"}),jsxRuntimeExports.jsx(Strut,{size:spacing.large_24}),jsxRuntimeExports.jsx(Button,{size:"small",kind:"tertiary",startIcon:plusIcon,onClick:this.addChoice.bind(this,true),children:"None of the above"})]})]})}constructor(...args){super(...args),this.onMultipleSelectChange=allowMultiple=>{allowMultiple=allowMultiple.multipleSelect;const numCorrect=_.reduce(this.props.choices,function(memo,choice){return choice.correct?memo+1:memo},0);if(!allowMultiple&&numCorrect>1){const choices=_.map(this.props.choices,function(choice){return _.defaults({correct:false},choice)});this.props.onChange({multipleSelect:allowMultiple,choices:choices});}else {this.props.onChange({multipleSelect:allowMultiple});}},this.onCountChoicesChange=count=>{count=count.countChoices;this.props.onChange({countChoices:count});},this.onChange=({checked})=>{const choices=this.props.choices.map((choice,i)=>{return {...choice,correct:checked[i],content:choice.isNoneOfTheAbove&&!checked[i]?"":choice.content}});this.props.onChange({choices:choices,numCorrect:deriveNumCorrect({...this.props,choices,numCorrect:undefined})});},this.onContentChange=(choiceIndex,newContent)=>{const choices=this.props.choices.slice();choices[choiceIndex]=_.extend({},choices[choiceIndex],{content:newContent});this.props.onChange({choices:choices});},this.onDelete=choiceIndex=>{const choices=this.props.choices.slice();const deleted=choices[choiceIndex];choices.splice(choiceIndex,1);this.props.onChange({choices:choices,hasNoneOfTheAbove:this.props.hasNoneOfTheAbove&&!deleted.isNoneOfTheAbove});},this.addChoice=(noneOfTheAbove,e)=>{e.preventDefault();const choices=this.props.choices.slice();const newChoice={isNoneOfTheAbove:noneOfTheAbove,content:""};const addIndex=choices.length-(this.props.hasNoneOfTheAbove?1:0);choices.splice(addIndex,0,newChoice);this.props.onChange({choices:choices,hasNoneOfTheAbove:noneOfTheAbove||this.props.hasNoneOfTheAbove},()=>{this.refs[`choice-editor${addIndex}`].refs["content-editor"].focus();});},this.focus=()=>{this.refs["choice-editor0"].refs["content-editor"].focus();return true},this.getSaveWarnings=()=>{if(!_.some(_.pluck(this.props.choices,"correct"))){return ["No choice is marked as correct."]}return []};}}RadioEditor.widgetName="radio";RadioEditor.defaultProps=radioLogic.defaultWidgetOptions;
1688
+ class ChoiceEditor extends React.Component{render(){const checkedClass=this.props.choice.correct?"correct":"incorrect";let placeholder="Type a choice here...";if(this.props.choice.isNoneOfTheAbove){placeholder=this.props.choice.correct?"Type the answer to reveal to the user...":"None of the above";}const editor=jsxRuntimeExports.jsx(Editor,{ref:"content-editor",apiOptions:this.props.apiOptions,content:this.props.choice.content||"",widgetEnabled:false,placeholder:placeholder,disabled:this.props.choice.isNoneOfTheAbove&&!this.props.choice.correct,onChange:this.props.onContentChange});const rationaleEditor=jsxRuntimeExports.jsx(Editor,{ref:"rationale-editor",apiOptions:this.props.apiOptions,content:this.props.choice.rationale||"",widgetEnabled:false,placeholder:`Why is this choice ${checkedClass}?`,onChange:this.props.onRationaleChange});const deleteLink=jsxRuntimeExports.jsx("a",{className:"simple-button orange delete-choice",href:"#",onClick:e=>{e.stopPropagation();e.preventDefault();this.props.onDelete();},title:"Remove this choice",children:"Remove this choice"});return jsxRuntimeExports.jsxs("div",{className:"choice-rationale-editors",children:[jsxRuntimeExports.jsx("div",{className:`choice-editor ${checkedClass}`,children:editor}),jsxRuntimeExports.jsx("div",{className:"rationale-editor",children:rationaleEditor}),this.props.showDelete&&deleteLink]})}}class RadioEditor extends React.Component{onRationaleChange(choiceIndex,newRationale){const choices=this.props.choices.slice();choices[choiceIndex]=_.extend({},choices[choiceIndex],{rationale:newRationale});if(newRationale===""){delete choices[choiceIndex].rationale;}this.props.onChange({choices:choices});}serialize(){const{choices,randomize,multipleSelect,countChoices,hasNoneOfTheAbove,deselectEnabled}=this.props;return {choices,randomize,multipleSelect,countChoices,hasNoneOfTheAbove,deselectEnabled,numCorrect:deriveNumCorrect(choices)}}render(){const numCorrect=deriveNumCorrect(this.props.choices);return jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsx(Link$1,{href:"https://www.khanacademy.org/internal-courses/content-creation-best-practices/xe46daa512cd9c644:question-writing/xe46daa512cd9c644:multiple-choice/a/stems",target:"_blank",children:"Multiple choice best practices"}),jsxRuntimeExports.jsxs("div",{className:"perseus-widget-row",children:[jsxRuntimeExports.jsx(LabeledSwitch,{label:"Randomize order",checked:this.props.randomize,onChange:value=>{this.props.onChange({randomize:value});},style:{marginBlockEnd:sizing.size_060}}),jsxRuntimeExports.jsx(LabeledSwitch,{label:"Multiple selections",checked:this.props.multipleSelect,onChange:value=>{this.onMultipleSelectChange({multipleSelect:value});},style:{marginBlockEnd:sizing.size_060}}),this.props.multipleSelect&&jsxRuntimeExports.jsx(LabeledSwitch,{label:"Specify number correct",checked:this.props.countChoices,onChange:value=>{this.onCountChoicesChange({countChoices:value});},style:{marginBlockEnd:sizing.size_060}})]}),jsxRuntimeExports.jsx(BaseRadio,{multipleSelect:this.props.multipleSelect,countChoices:this.props.countChoices,numCorrect:numCorrect,editMode:true,labelWrap:false,apiOptions:this.props.apiOptions,reviewMode:false,choices:this.props.choices.map((choice,i)=>{return {content:jsxRuntimeExports.jsx(ChoiceEditor,{ref:`choice-editor${i}`,apiOptions:this.props.apiOptions,choice:choice,onContentChange:newProps=>{if(newProps.content!=null){this.onContentChange(i,newProps.content);}},onRationaleChange:newProps=>{if(newProps.content!=null){this.onRationaleChange(i,newProps.content);}},onDelete:()=>this.onDelete(i),showDelete:this.props.choices.length>=2}),isNoneOfTheAbove:choice.isNoneOfTheAbove,checked:choice.correct}},this),onChange:this.onChange}),jsxRuntimeExports.jsxs("div",{className:"add-choice-container",children:[jsxRuntimeExports.jsx(Button,{size:"small",kind:"tertiary",startIcon:plusIcon,onClick:this.addChoice.bind(this,false),children:"Add a choice"}),jsxRuntimeExports.jsx(Strut,{size:spacing.large_24}),jsxRuntimeExports.jsx(Button,{size:"small",kind:"tertiary",startIcon:plusIcon,onClick:this.addChoice.bind(this,true),children:"None of the above"})]})]})}constructor(...args){super(...args),this.onMultipleSelectChange=allowMultiple=>{const isMultipleSelect=allowMultiple.multipleSelect;let choices=this.props.choices;if(!isMultipleSelect){const numCorrect=deriveNumCorrect(choices);if(numCorrect>1){choices=choices.map(choice=>{return {...choice,correct:false}});}}this.props.onChange({multipleSelect:isMultipleSelect,choices,numCorrect:deriveNumCorrect(choices)});},this.onCountChoicesChange=count=>{const countChoices=count.countChoices;this.props.onChange({countChoices});},this.onChange=({checked})=>{const choices=this.props.choices.map((choice,i)=>{return {...choice,correct:checked[i],content:choice.isNoneOfTheAbove&&!checked[i]?"":choice.content}});this.props.onChange({choices,numCorrect:deriveNumCorrect(choices)});},this.onContentChange=(choiceIndex,newContent)=>{const choices=this.props.choices.slice();choices[choiceIndex]=_.extend({},choices[choiceIndex],{content:newContent});this.props.onChange({choices:choices});},this.onDelete=choiceIndex=>{const choices=this.props.choices.slice();const deleted=choices[choiceIndex];choices.splice(choiceIndex,1);this.props.onChange({choices,hasNoneOfTheAbove:this.props.hasNoneOfTheAbove&&!deleted.isNoneOfTheAbove,numCorrect:deriveNumCorrect(choices)});},this.addChoice=(noneOfTheAbove,e)=>{e.preventDefault();const choices=this.props.choices.slice();const newChoice={isNoneOfTheAbove:noneOfTheAbove,content:""};const addIndex=choices.length-(this.props.hasNoneOfTheAbove?1:0);choices.splice(addIndex,0,newChoice);this.props.onChange({choices:choices,hasNoneOfTheAbove:noneOfTheAbove||this.props.hasNoneOfTheAbove},()=>{this.refs[`choice-editor${addIndex}`].refs["content-editor"].focus();});},this.focus=()=>{this.refs["choice-editor0"].refs["content-editor"].focus();return true},this.getSaveWarnings=()=>{if(!_.some(_.pluck(this.props.choices,"correct"))){return ["No choice is marked as correct."]}return []};}}RadioEditor.widgetName="radio";RadioEditor.defaultProps=radioLogic.defaultWidgetOptions;
1689
1689
 
1690
1690
  const{InfoTip: InfoTip$2,TextListEditor}=components;const HORIZONTAL="horizontal";const VERTICAL="vertical";class SorterEditor extends React.Component{render(){const editor=this;return jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsxs("div",{children:[" ","Correct answer:"," ",jsxRuntimeExports.jsx(InfoTip$2,{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(TextListEditor,{options:this.props.correct,onChange:function(options,cb){editor.props.onChange({correct:options},cb);},layout:this.props.layout}),jsxRuntimeExports.jsxs("div",{children:[jsxRuntimeExports.jsxs("label",{children:[" ","Layout:"," ",jsxRuntimeExports.jsxs("select",{value:this.props.layout,onChange:this.onLayoutChange,children:[jsxRuntimeExports.jsx("option",{value:HORIZONTAL,children:"Horizontal"}),jsxRuntimeExports.jsx("option",{value:VERTICAL,children:"Vertical"})]})]}),jsxRuntimeExports.jsx(InfoTip$2,{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(Checkbox$1,{label:"Padding:",checked:this.props.padding,onChange:value=>{this.props.onChange({padding:value});}}),jsxRuntimeExports.jsx(InfoTip$2,{children:jsxRuntimeExports.jsx("p",{children:"Padding is good for text, but not needed for images."})})]})]})}constructor(...args){super(...args),this.onLayoutChange=e=>{this.props.onChange({layout:e.target.value});},this.serialize=()=>{return _.pick(this.props,"correct","layout","padding")};}}SorterEditor.propTypes={correct:PropTypes.array,layout:PropTypes.oneOf([HORIZONTAL,VERTICAL]),padding:PropTypes.bool};SorterEditor.widgetName="sorter";SorterEditor.defaultProps=sorterLogic.defaultWidgetOptions;
1691
1691