import cn from "classnames" ; import React , { FunctionComponent } from "react" ; import { SPOILER_CLASSNAME } from "coral-common/constants" ; import { ALL_FEATURES , createSanitize , Sanitize , } from "coral-common/helpers/sanitize" ; import { useCoralContext } from "coral-framework/lib/bootstrap/CoralContext" ; import styles from "./HTMLContent.css" ; /**
 * Sanitize html content and find spoiler tags.
 */ const sanitizeAndFindSpoilerTags = ( ( ) => { /** Resused instance */ let sanitize = null ; /** Found spoiler tags during sanitization will be placed here. */ let spoilerTags = [ ] ; return ( window , source ) : [ HTMLElement , Element [ ] ] => { if ( ! sanitize ) { sanitize = createSanitize ( window , { features : ALL_FEATURES , modify : ( purify ) => { purify . addHook ( "afterSanitizeAttributes" , ( node ) => { if ( node . tagName === "SPAN" && node . className === SPOILER_CLASSNAME ) { spoilerTags . push ( node ) ; } } ) ; } , } ) ; } const sanitized = sanitize ( source ) ; const ret = spoilerTags ; spoilerTags = [ ] ; return [ sanitized , ret ] ; } ; } ) ( ) ; /**
 * Makes sure SpoilerTag Handler is registered in `global`.
 */ function registerSpoilerTagHandler ( window ) { if ( "handleCoralSpoilerButton" in window ) { return ; } ( window as any ) . handleCoralSpoilerButton = ( el , event ) => { if ( "key" in event && event . key !== " " && event . key !== "Enter" && event . key !== "Spacebar" ) { return ; } el . innerHTML = el . firstElementChild ! . innerHTML ; el . removeAttribute ( "onClick" ) ; el . removeAttribute ( "title" ) ; el . removeAttribute ( "tabindex" ) ; el . removeAttribute ( "role" ) ; el . classList . add ( `${ SPOILER_CLASSNAME }-reveal` ) ; } ; } export function transform ( window , source ) { const [ sanitized , spoilerTags ] = sanitizeAndFindSpoilerTags ( window , source ) ; registerSpoilerTagHandler ( window ) ; spoilerTags . forEach ( ( node ) => { node . setAttribute ( "onmousedown" , "javascript: event.preventDefault()" ) ; node . setAttribute ( "onclick" , "handleCoralSpoilerButton(this, event)" ) ; node . setAttribute ( "onkeydown" , "handleCoralSpoilerButton(this, event)" ) ; node . setAttribute ( "role" , "button" ) ; node . setAttribute ( "title" , "Reveal spoiler" ) ; node . setAttribute ( "tabindex" , "0" ) ; node . innerHTML = `<span aria-hidden="true">${ node . innerHTML }</span>` ; } ) ; return sanitized . innerHTML ; } interface HTMLContentProps { children : string ; className ? : string ; } const HTMLContent = ( { children , className , } ) => { const { window } = useCoralContext ( ) ; return ( < div className = { cn ( styles . root , className ) } dangerouslySetInnerHTML = { { __html : transform ( window , children ) } } />); } ; export default HTMLContent ;