@khanacademy/perseus-editor 27.4.1 → 27.5.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/index.js CHANGED
@@ -124,7 +124,7 @@ var xIcon__default = /*#__PURE__*/_interopDefaultCompat(xIcon);
124
124
  var checkIcon__default = /*#__PURE__*/_interopDefaultCompat(checkIcon);
125
125
  var minusCircleIcon__default = /*#__PURE__*/_interopDefaultCompat(minusCircleIcon);
126
126
 
127
- const libName="@khanacademy/perseus-editor";const libVersion="27.4.1";perseusUtils.addLibraryVersionToPerseusDebug(libName,libVersion);
127
+ const libName="@khanacademy/perseus-editor";const libVersion="27.5.0";perseusUtils.addLibraryVersionToPerseusDebug(libName,libVersion);
128
128
 
129
129
  var jsxRuntime = {exports: {}};
130
130
 
@@ -1573,9 +1573,9 @@ const AutoResizingTextArea=props=>{const textAreaRef=React__namespace.useRef(nul
1573
1573
 
1574
1574
  var styles$K = {"labelWithInfoTip":"perseus_yoPGwqna","dimensionsContainer":"perseus_4qo24hC2"};
1575
1575
 
1576
- const{InfoTip: InfoTip$k}=perseus.components;function ImageSettings({alt,backgroundImage,caption,onChange}){const uniqueId=React__namespace.useId();const altId=`${uniqueId}-alt`;const captionId=`${uniqueId}-caption`;if(!backgroundImage.url){return null}const dimensions=`${backgroundImage.width} x ${backgroundImage.height}`;const dimensionString=backgroundImage.width&&backgroundImage.height?dimensions:"unknown";return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[!perseus.Util.isLabeledSVG(backgroundImage.url)&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{style:{paddingBlockStart:0,marginBlockStart:wonderBlocksTokens.sizing.size_120,marginBlockEnd:wonderBlocksTokens.sizing.size_040,color:"var(--wb-semanticColor-core-foreground-neutral-strong)"},children:"Preview:"}),jsxRuntimeExports.jsx("img",{alt:`Preview: ${alt??"No alt text"}`,src:backgroundImage.url,style:{width:"100%"}})]}),jsxRuntimeExports.jsxs("div",{className:styles$K.dimensionsContainer,children:[jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{style:{padding:0,marginInlineEnd:wonderBlocksTokens.sizing.size_080,color:"var(--wb-semanticColor-core-foreground-neutral-strong)"},children:"Dimensions:"}),dimensionString]}),jsxRuntimeExports.jsxs("div",{className:styles$K.labelWithInfoTip,children:[jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{tag:"label",htmlFor:altId,children:"Alt text:"}),jsxRuntimeExports.jsx(InfoTip$k,{children:"This is important for screenreaders. The content of this alt text will be formatted as markdown (tables, emphasis, etc. are supported)."})]}),jsxRuntimeExports.jsx(AutoResizingTextArea,{id:altId,value:alt??"",onChange:value=>onChange({alt:value===""?undefined:value}),style:{marginBlockStart:wonderBlocksTokens.sizing.size_040,marginBlockEnd:wonderBlocksTokens.sizing.size_080}}),jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{tag:"label",htmlFor:captionId,children:"Caption:"}),jsxRuntimeExports.jsx(AutoResizingTextArea,{id:captionId,value:caption??"",onChange:value=>onChange({caption:value===""?undefined:value}),style:{marginBlockStart:wonderBlocksTokens.sizing.size_040,marginBlockEnd:wonderBlocksTokens.sizing.size_080}})]})}
1576
+ const{InfoTip: InfoTip$k}=perseus.components;function ImageSettings({alt,backgroundImage,caption,title,onChange}){const uniqueId=React__namespace.useId();const altId=`${uniqueId}-alt`;const titleId=`${uniqueId}-title`;const captionId=`${uniqueId}-caption`;if(!backgroundImage.url){return null}const dimensions=`${backgroundImage.width} x ${backgroundImage.height}`;const dimensionString=backgroundImage.width&&backgroundImage.height?dimensions:"unknown";return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[!perseus.Util.isLabeledSVG(backgroundImage.url)&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{style:{paddingBlockStart:0,marginBlockStart:wonderBlocksTokens.sizing.size_120,marginBlockEnd:wonderBlocksTokens.sizing.size_040,color:"var(--wb-semanticColor-core-foreground-neutral-strong)"},children:"Preview:"}),jsxRuntimeExports.jsx("img",{alt:`Preview: ${alt??"No alt text"}`,src:backgroundImage.url,style:{width:"100%"}})]}),jsxRuntimeExports.jsxs("div",{className:styles$K.dimensionsContainer,children:[jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{style:{padding:0,marginInlineEnd:wonderBlocksTokens.sizing.size_080,color:"var(--wb-semanticColor-core-foreground-neutral-strong)"},children:"Dimensions:"}),dimensionString]}),jsxRuntimeExports.jsxs("div",{className:styles$K.labelWithInfoTip,children:[jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{tag:"label",htmlFor:altId,children:"Alt text:"}),jsxRuntimeExports.jsx(InfoTip$k,{children:"This is important for screenreaders. The content of this alt text will be formatted as markdown (tables, emphasis, etc. are supported)."})]}),jsxRuntimeExports.jsx(AutoResizingTextArea,{id:altId,value:alt??"",onChange:value=>onChange({alt:value}),style:textAreaStyle}),jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{tag:"label",htmlFor:titleId,children:"Title:"}),jsxRuntimeExports.jsx(AutoResizingTextArea,{id:titleId,value:title??"",onChange:value=>onChange({title:value}),style:textAreaStyle}),jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{tag:"label",htmlFor:captionId,children:"Caption:"}),jsxRuntimeExports.jsx(AutoResizingTextArea,{id:captionId,value:caption??"",onChange:value=>onChange({caption:value}),style:textAreaStyle})]})}const textAreaStyle={marginBlockStart:wonderBlocksTokens.sizing.size_040,marginBlockEnd:wonderBlocksTokens.sizing.size_120};
1577
1577
 
1578
- const{InfoTip: InfoTip$j}=perseus.components;const INTERNALLY_HOSTED_DOMAINS="("+"ka-.*.s3.amazonaws.com|"+"(fastly|cdn).kastatic.org|"+"khanacademy.org|"+"kasandbox.org"+")";const INTERNALLY_HOSTED_URL_RE=new RegExp("^(https?|web\\+graphie)://[^/]*"+INTERNALLY_HOSTED_DOMAINS);function ImageUrlInput({backgroundImage,onChange}){const uniqueId=React__namespace.default.useId();const urlId=`${uniqueId}-url`;const[urlFieldValue,setUrlFieldValue]=React__namespace.default.useState(backgroundImage.url||"");const[backgroundImageError,setBackgroundImageError]=React__namespace.default.useState(null);function setUrl(url,width,height){const image={...backgroundImage};image.url=url;image.width=width;image.height=height;const box=[image.width,image.height];onChange({backgroundImage:image,box:box});}async function onUrlChange(url){if(!url){setBackgroundImageError(null);onChange({backgroundImage:{},box:undefined});return}if(url&&!INTERNALLY_HOSTED_URL_RE.test(url)){setBackgroundImageError("Images must be from sites hosted by Khan Academy. "+"Please input a Khan Academy-owned address, or use the "+"Add Image tool to rehost an existing image");return}setBackgroundImageError(null);try{const size=await perseus.Util.getImageSizeModern(url);setUrl(url,size[0],size[1]);}catch(error){setBackgroundImageError(`There was an error loading the image URL: ${JSON.stringify(error,null,2)}`);}}return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsxs("div",{className:styles$K.labelWithInfoTip,children:[jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{tag:"label",htmlFor:urlId,children:"Image url:"}),jsxRuntimeExports.jsx(InfoTip$j,{children:"Paste an image or graphie image URL."})]}),backgroundImageError&&jsxRuntimeExports.jsx(Banner__default.default,{kind:"critical",layout:"floating",text:backgroundImageError}),jsxRuntimeExports.jsx(wonderBlocksForm.TextField,{id:urlId,value:urlFieldValue,onBlur:e=>onUrlChange(e.target.value),onChange:value=>setUrlFieldValue(value),style:{marginBlockStart:wonderBlocksTokens.sizing.size_040,marginBlockEnd:wonderBlocksTokens.sizing.size_080}})]})}
1578
+ const{InfoTip: InfoTip$j}=perseus.components;const INTERNALLY_HOSTED_DOMAINS="("+"ka-.*.s3.amazonaws.com|"+"(fastly|cdn).kastatic.org|"+"khanacademy.org|"+"kasandbox.org"+")";const INTERNALLY_HOSTED_URL_RE=new RegExp("^(https?|web\\+graphie)://[^/]*"+INTERNALLY_HOSTED_DOMAINS);function ImageUrlInput({backgroundImage,onChange}){const uniqueId=React__namespace.default.useId();const urlId=`${uniqueId}-url`;const[urlFieldValue,setUrlFieldValue]=React__namespace.default.useState(backgroundImage.url||"");const[backgroundImageError,setBackgroundImageError]=React__namespace.default.useState(null);function setUrl(url,width,height){const image={...backgroundImage};image.url=url;image.width=width;image.height=height;const box=[image.width,image.height];onChange({backgroundImage:image,box:box});}async function onUrlChange(url){if(!url){setBackgroundImageError(null);setUrl(url,0,0);return}if(url&&!INTERNALLY_HOSTED_URL_RE.test(url)){setBackgroundImageError("Images must be from sites hosted by Khan Academy. "+"Please input a Khan Academy-owned address, or use the "+"Add Image tool to rehost an existing image");return}setBackgroundImageError(null);try{const size=await perseus.Util.getImageSizeModern(url);setUrl(url,size[0],size[1]);}catch(error){setBackgroundImageError(`There was an error loading the image URL: ${JSON.stringify(error,null,2)}`);}}return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsxs("div",{className:styles$K.labelWithInfoTip,children:[jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{tag:"label",htmlFor:urlId,children:"Image url:"}),jsxRuntimeExports.jsx(InfoTip$j,{children:"Paste an image or graphie image URL."})]}),backgroundImageError&&jsxRuntimeExports.jsx(Banner__default.default,{kind:"critical",layout:"floating",text:backgroundImageError}),jsxRuntimeExports.jsx(wonderBlocksForm.TextField,{id:urlId,value:urlFieldValue,onBlur:e=>onUrlChange(e.target.value),onChange:value=>setUrlFieldValue(value),style:{marginBlockStart:wonderBlocksTokens.sizing.size_040,marginBlockEnd:wonderBlocksTokens.sizing.size_080}})]})}
1579
1579
 
1580
1580
  class ImageEditor extends React__namespace.Component{serialize(){return perseus.EditorJsonify.serialize.call(this)}render(){return jsxRuntimeExports.jsxs("div",{className:"perseus-image-editor",children:[jsxRuntimeExports.jsx(ImageUrlInput,{...this.props}),this.props.backgroundImage.url&&jsxRuntimeExports.jsx(ImageSettings,{...this.props})]})}}ImageEditor.displayName="ImageEditor";ImageEditor.widgetName="image";ImageEditor.defaultProps=perseusCore.imageLogic.defaultWidgetOptions;
1581
1581
 
@@ -1774,7 +1774,7 @@ function RadioImageEditor({initialImageUrl,initialImageAltText,containerClassNam
1774
1774
  function getMovedChoices(choices,hasNoneOfTheAbove,choiceIndex,movement){const newChoices=[...choices];const[removedChoice]=newChoices.splice(choiceIndex,1);switch(movement){case "top":if(choiceIndex===0){return choices}newChoices.unshift(removedChoice);break;case "up":if(choiceIndex===0){return choices}newChoices.splice(choiceIndex-1,0,removedChoice);break;case "down":if(choiceIndex===choices.length-1){return choices}if(choiceIndex===choices.length-2&&hasNoneOfTheAbove){return choices}newChoices.splice(choiceIndex+1,0,removedChoice);break;case "bottom":if(choiceIndex===choices.length-1){return choices}if(hasNoneOfTheAbove){const removedNoneOfTheAbove=newChoices.pop();newChoices.push(removedChoice);if(removedNoneOfTheAbove){newChoices.push(removedNoneOfTheAbove);}}else {newChoices.push(removedChoice);}break}return newChoices}function setImageProxyFromMarkdownContent(markdownContent){const images=[];const parsedMarkdown=perseus.PerseusMarkdown.parse(markdownContent,{});perseus.PerseusMarkdown.traverseContent(parsedMarkdown,node=>{if(node.type==="image"){images.push({url:node.target||"",altText:node.alt||""});}});let proxiedContent=markdownContent;images.forEach((image,index)=>{const originalPattern=`![${image.altText}](${image.url})`;const replacement=`![Image ${index+1}]`;const patternIndex=proxiedContent.indexOf(originalPattern);if(patternIndex!==-1){proxiedContent=proxiedContent.substring(0,patternIndex)+replacement+proxiedContent.substring(patternIndex+originalPattern.length);}});return [proxiedContent,images]}function setMarkdownContentFromImageProxy(proxiedContent,images){let markdownContent=proxiedContent;for(let i=0;i<images.length;i++){const image=images[i];markdownContent=markdownContent.replace(`![Image ${i+1}]`,`![${image.altText}](${image.url})`);}return markdownContent}
1775
1775
 
1776
1776
  const RadioOptionContentAndImageEditor=props=>{const{content,choiceIndex,onContentChange,isNoneOfTheAbove}=props;const uniqueId=React__namespace.useId();const contentTextAreaId=`${uniqueId}-content-textarea`;const[proxiedContent,setProxiedContent]=React__namespace.useState("");const[images,setImages]=React__namespace.useState([]);const[addingImage,setAddingImage]=React__namespace.useState(false);React__namespace.useEffect(()=>{const[proxiedContent,images]=setImageProxyFromMarkdownContent(content??"");setProxiedContent(proxiedContent);setImages(images);},[content]);const handleAddImage=(choiceIndex,imageUrl,imageAltText)=>{const newContent=`${content}
1777
- ![${imageAltText}](${imageUrl})`;onContentChange(choiceIndex,newContent);};const handleDeleteImage=imageIndex=>{const substr=`![Image ${imageIndex+1}]`;const newProxiedContent=proxiedContent.replace(substr,"");setProxiedContent(newProxiedContent);const newContent=setMarkdownContentFromImageProxy(newProxiedContent,images);onContentChange(choiceIndex,newContent);};const handleUpdateImage=(imageIndex,url,altText)=>{const newImages=[...images];newImages[imageIndex]={url,altText};setImages(newImages);const newContent=setMarkdownContentFromImageProxy(proxiedContent,newImages);onContentChange(choiceIndex,newContent);};const handleContentChange=(choiceIndex,newProxiedContent)=>{setProxiedContent(newProxiedContent);const newContent=setMarkdownContentFromImageProxy(newProxiedContent,images);onContentChange(choiceIndex,newContent);};const handlePaste=e=>{const imageURL=e.clipboardData.getData("text");if(imageURL.includes("cdn.kastatic.org")||imageURL.includes("graphie")){e.preventDefault();handleAddImage(choiceIndex,imageURL,"");}};const handleDeleteImageConfirmation=imageIndex=>{if(window.confirm("Are you sure you want to delete this image?")){handleDeleteImage(imageIndex);}};if(isNoneOfTheAbove){return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{tag:"label",htmlFor:contentTextAreaId,children:"Content"}),jsxRuntimeExports.jsx(AutoResizingTextArea,{id:contentTextAreaId,value:"None of the above",disabled:true,onChange:()=>{}})]})}return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{tag:"label",htmlFor:contentTextAreaId,style:{marginBlockEnd:wonderBlocksTokens.sizing.size_040},children:"Content"}),jsxRuntimeExports.jsx(AutoResizingTextArea,{id:contentTextAreaId,value:proxiedContent,placeholder:"Type a choice here...",onChange:value=>{handleContentChange(choiceIndex,value);},onPaste:handlePaste}),!addingImage&&jsxRuntimeExports.jsx(Button__default.default,{startIcon:plusIcon__default.default,size:"small",kind:"tertiary",style:{alignSelf:"flex-start"},onClick:()=>{setAddingImage(true);},children:"Add image"}),addingImage&&jsxRuntimeExports.jsx(RadioImageEditor,{initialImageUrl:"",initialImageAltText:"",containerClassName:styles.imageEditorContainer,onSave:(imageUrl,imageAltText)=>{handleAddImage(choiceIndex,imageUrl,imageAltText);},onClose:()=>{setAddingImage(false);}}),images?.map((image,imageIndex)=>jsxRuntimeExports.jsxs(PerseusEditorAccordion,{header:`Image ${imageIndex+1}`,expanded:true,containerStyle:{backgroundColor:wonderBlocksTokens.semanticColor.surface.primary,marginBlockStart:wonderBlocksTokens.sizing.size_040,marginBlockEnd:wonderBlocksTokens.sizing.size_040},panelStyle:{paddingBlockEnd:wonderBlocksTokens.sizing.size_120},children:[jsxRuntimeExports.jsx("img",{src:image.url,alt:image.altText,style:{marginBlockEnd:wonderBlocksTokens.sizing.size_080}}),jsxRuntimeExports.jsx(RadioImageEditor,{initialImageUrl:image.url,initialImageAltText:image.altText,onSave:(imageUrl,imageAltText)=>{handleUpdateImage(imageIndex,imageUrl,imageAltText);},onDelete:()=>{handleDeleteImageConfirmation(imageIndex);}})]},image.url))??null]})};
1777
+ ![${imageAltText}](${imageUrl})`;onContentChange(choiceIndex,newContent);};const handleDeleteImage=imageIndex=>{const substr=`![Image ${imageIndex+1}]`;const newProxiedContent=proxiedContent.replace(substr,"");setProxiedContent(newProxiedContent);const newContent=setMarkdownContentFromImageProxy(newProxiedContent,images);onContentChange(choiceIndex,newContent);};const handleUpdateImage=(imageIndex,url,altText)=>{const newImages=[...images];newImages[imageIndex]={url,altText};setImages(newImages);const newContent=setMarkdownContentFromImageProxy(proxiedContent,newImages);onContentChange(choiceIndex,newContent);};const handleContentChange=(choiceIndex,newProxiedContent)=>{setProxiedContent(newProxiedContent);const newContent=setMarkdownContentFromImageProxy(newProxiedContent,images);onContentChange(choiceIndex,newContent);};const handlePaste=e=>{const imageURL=e.clipboardData.getData("text");if(imageURL.includes("cdn.kastatic.org")||imageURL.includes("graphie")){e.preventDefault();handleAddImage(choiceIndex,imageURL,"");}};const handleDeleteImageConfirmation=imageIndex=>{if(window.confirm("Are you sure you want to delete this image?")){handleDeleteImage(imageIndex);}};if(isNoneOfTheAbove){return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{tag:"label",htmlFor:contentTextAreaId,children:"Content"}),jsxRuntimeExports.jsx(AutoResizingTextArea,{id:contentTextAreaId,value:"None of the above",disabled:true,onChange:()=>{}})]})}return jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(wonderBlocksTypography.HeadingXSmall,{tag:"label",htmlFor:contentTextAreaId,style:{marginBlockEnd:wonderBlocksTokens.sizing.size_040},children:"Content"}),jsxRuntimeExports.jsx(AutoResizingTextArea,{id:contentTextAreaId,value:proxiedContent,placeholder:"Type a choice here...",onChange:value=>{handleContentChange(choiceIndex,value);},onPaste:handlePaste}),!addingImage&&jsxRuntimeExports.jsx(Button__default.default,{startIcon:plusIcon__default.default,size:"small",kind:"tertiary",style:{alignSelf:"flex-start"},onClick:()=>{setAddingImage(true);},children:"Add image"}),addingImage&&jsxRuntimeExports.jsx(RadioImageEditor,{initialImageUrl:"",initialImageAltText:"",containerClassName:styles.imageEditorContainer,onSave:(imageUrl,imageAltText)=>{handleAddImage(choiceIndex,imageUrl,imageAltText);},onClose:()=>{setAddingImage(false);}}),images?.map((image,imageIndex)=>jsxRuntimeExports.jsxs(PerseusEditorAccordion,{header:`Image ${imageIndex+1}`,expanded:true,containerStyle:{backgroundColor:wonderBlocksTokens.semanticColor.core.background.base.default,marginBlockStart:wonderBlocksTokens.sizing.size_040,marginBlockEnd:wonderBlocksTokens.sizing.size_040},panelStyle:{paddingBlockEnd:wonderBlocksTokens.sizing.size_120},children:[jsxRuntimeExports.jsx("img",{src:image.url,alt:image.altText,style:{marginBlockEnd:wonderBlocksTokens.sizing.size_080}}),jsxRuntimeExports.jsx(RadioImageEditor,{initialImageUrl:image.url,initialImageAltText:image.altText,onSave:(imageUrl,imageAltText)=>{handleUpdateImage(imageIndex,imageUrl,imageAltText);},onDelete:()=>{handleDeleteImageConfirmation(imageIndex);}})]},image.url))??null]})};
1778
1778
 
1779
1779
  function RadioOptionSettingsActions({content,showDelete,showMove,onDelete,onMove}){return jsxRuntimeExports.jsxs("div",{className:styles.radioOptionActionsContainer,children:[showDelete&&jsxRuntimeExports.jsx(Button__default.default,{size:"small",kind:"tertiary",startIcon:trashIcon__default$1.default,onClick:()=>{if(window.confirm(`Are you sure you want to remove this choice?
1780
1780