@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/es/index.js CHANGED
@@ -61,7 +61,7 @@ import xIcon from '@phosphor-icons/core/regular/x.svg';
61
61
  import checkIcon from '@phosphor-icons/core/bold/check-bold.svg';
62
62
  import minusCircleIcon from '@phosphor-icons/core/bold/minus-circle-bold.svg';
63
63
 
64
- const libName="@khanacademy/perseus-editor";const libVersion="27.4.1";addLibraryVersionToPerseusDebug(libName,libVersion);
64
+ const libName="@khanacademy/perseus-editor";const libVersion="27.5.0";addLibraryVersionToPerseusDebug(libName,libVersion);
65
65
 
66
66
  var jsxRuntime = {exports: {}};
67
67
 
@@ -1510,9 +1510,9 @@ const AutoResizingTextArea=props=>{const textAreaRef=React.useRef(null);React.us
1510
1510
 
1511
1511
  var styles$K = {"labelWithInfoTip":"perseus_yoPGwqna","dimensionsContainer":"perseus_4qo24hC2"};
1512
1512
 
1513
- const{InfoTip: InfoTip$k}=components;function ImageSettings({alt,backgroundImage,caption,onChange}){const uniqueId=React.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:[!Util.isLabeledSVG(backgroundImage.url)&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(HeadingXSmall,{style:{paddingBlockStart:0,marginBlockStart:sizing.size_120,marginBlockEnd: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(HeadingXSmall,{style:{padding:0,marginInlineEnd:sizing.size_080,color:"var(--wb-semanticColor-core-foreground-neutral-strong)"},children:"Dimensions:"}),dimensionString]}),jsxRuntimeExports.jsxs("div",{className:styles$K.labelWithInfoTip,children:[jsxRuntimeExports.jsx(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:sizing.size_040,marginBlockEnd:sizing.size_080}}),jsxRuntimeExports.jsx(HeadingXSmall,{tag:"label",htmlFor:captionId,children:"Caption:"}),jsxRuntimeExports.jsx(AutoResizingTextArea,{id:captionId,value:caption??"",onChange:value=>onChange({caption:value===""?undefined:value}),style:{marginBlockStart:sizing.size_040,marginBlockEnd:sizing.size_080}})]})}
1513
+ const{InfoTip: InfoTip$k}=components;function ImageSettings({alt,backgroundImage,caption,title,onChange}){const uniqueId=React.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:[!Util.isLabeledSVG(backgroundImage.url)&&jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(HeadingXSmall,{style:{paddingBlockStart:0,marginBlockStart:sizing.size_120,marginBlockEnd: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(HeadingXSmall,{style:{padding:0,marginInlineEnd:sizing.size_080,color:"var(--wb-semanticColor-core-foreground-neutral-strong)"},children:"Dimensions:"}),dimensionString]}),jsxRuntimeExports.jsxs("div",{className:styles$K.labelWithInfoTip,children:[jsxRuntimeExports.jsx(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(HeadingXSmall,{tag:"label",htmlFor:titleId,children:"Title:"}),jsxRuntimeExports.jsx(AutoResizingTextArea,{id:titleId,value:title??"",onChange:value=>onChange({title:value}),style:textAreaStyle}),jsxRuntimeExports.jsx(HeadingXSmall,{tag:"label",htmlFor:captionId,children:"Caption:"}),jsxRuntimeExports.jsx(AutoResizingTextArea,{id:captionId,value:caption??"",onChange:value=>onChange({caption:value}),style:textAreaStyle})]})}const textAreaStyle={marginBlockStart:sizing.size_040,marginBlockEnd:sizing.size_120};
1514
1514
 
1515
- const{InfoTip: InfoTip$j}=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__default.useId();const urlId=`${uniqueId}-url`;const[urlFieldValue,setUrlFieldValue]=React__default.useState(backgroundImage.url||"");const[backgroundImageError,setBackgroundImageError]=React__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 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(HeadingXSmall,{tag:"label",htmlFor:urlId,children:"Image url:"}),jsxRuntimeExports.jsx(InfoTip$j,{children:"Paste an image or graphie image URL."})]}),backgroundImageError&&jsxRuntimeExports.jsx(Banner,{kind:"critical",layout:"floating",text:backgroundImageError}),jsxRuntimeExports.jsx(TextField,{id:urlId,value:urlFieldValue,onBlur:e=>onUrlChange(e.target.value),onChange:value=>setUrlFieldValue(value),style:{marginBlockStart:sizing.size_040,marginBlockEnd:sizing.size_080}})]})}
1515
+ const{InfoTip: InfoTip$j}=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__default.useId();const urlId=`${uniqueId}-url`;const[urlFieldValue,setUrlFieldValue]=React__default.useState(backgroundImage.url||"");const[backgroundImageError,setBackgroundImageError]=React__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 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(HeadingXSmall,{tag:"label",htmlFor:urlId,children:"Image url:"}),jsxRuntimeExports.jsx(InfoTip$j,{children:"Paste an image or graphie image URL."})]}),backgroundImageError&&jsxRuntimeExports.jsx(Banner,{kind:"critical",layout:"floating",text:backgroundImageError}),jsxRuntimeExports.jsx(TextField,{id:urlId,value:urlFieldValue,onBlur:e=>onUrlChange(e.target.value),onChange:value=>setUrlFieldValue(value),style:{marginBlockStart:sizing.size_040,marginBlockEnd:sizing.size_080}})]})}
1516
1516
 
1517
1517
  class ImageEditor extends React.Component{serialize(){return 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=imageLogic.defaultWidgetOptions;
1518
1518
 
@@ -1711,7 +1711,7 @@ function RadioImageEditor({initialImageUrl,initialImageAltText,containerClassNam
1711
1711
  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=PerseusMarkdown.parse(markdownContent,{});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}
1712
1712
 
1713
1713
  const RadioOptionContentAndImageEditor=props=>{const{content,choiceIndex,onContentChange,isNoneOfTheAbove}=props;const uniqueId=React.useId();const contentTextAreaId=`${uniqueId}-content-textarea`;const[proxiedContent,setProxiedContent]=React.useState("");const[images,setImages]=React.useState([]);const[addingImage,setAddingImage]=React.useState(false);React.useEffect(()=>{const[proxiedContent,images]=setImageProxyFromMarkdownContent(content??"");setProxiedContent(proxiedContent);setImages(images);},[content]);const handleAddImage=(choiceIndex,imageUrl,imageAltText)=>{const newContent=`${content}
1714
- ![${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(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(HeadingXSmall,{tag:"label",htmlFor:contentTextAreaId,style:{marginBlockEnd: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,{startIcon:plusIcon,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:semanticColor.surface.primary,marginBlockStart:sizing.size_040,marginBlockEnd:sizing.size_040},panelStyle:{paddingBlockEnd:sizing.size_120},children:[jsxRuntimeExports.jsx("img",{src:image.url,alt:image.altText,style:{marginBlockEnd: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]})};
1714
+ ![${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(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(HeadingXSmall,{tag:"label",htmlFor:contentTextAreaId,style:{marginBlockEnd: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,{startIcon:plusIcon,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:semanticColor.core.background.base.default,marginBlockStart:sizing.size_040,marginBlockEnd:sizing.size_040},panelStyle:{paddingBlockEnd:sizing.size_120},children:[jsxRuntimeExports.jsx("img",{src:image.url,alt:image.altText,style:{marginBlockEnd: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]})};
1715
1715
 
1716
1716
  function RadioOptionSettingsActions({content,showDelete,showMove,onDelete,onMove}){return jsxRuntimeExports.jsxs("div",{className:styles.radioOptionActionsContainer,children:[showDelete&&jsxRuntimeExports.jsx(Button,{size:"small",kind:"tertiary",startIcon:trashIcon$1,onClick:()=>{if(window.confirm(`Are you sure you want to remove this choice?
1717
1717