{"version":3,"sources":["webpack:///./app/javascript/crayons/Buttons/Button.jsx","webpack:///./app/javascript/crayons/Links/Link.jsx","webpack:///./app/javascript/crayons/CTAs/CTA.jsx","webpack:///./app/javascript/crayons/ButtonGroup/ButtonGroup.jsx","webpack:///./app/javascript/crayons/Dropdown/Dropdown.jsx","webpack:///./app/javascript/crayons/formElements/FormField/FormField.jsx","webpack:///./app/javascript/crayons/formElements/RadioButton/RadioButton.jsx","webpack:///./app/javascript/crayons/Icons/Icon.jsx","webpack:///./app/javascript/crayons/MobileDrawer/MobileDrawer.jsx","webpack:///./app/javascript/crayons/navigation/MobileDrawerNavigation/MobileDrawerNavigation.jsx","webpack:///./app/javascript/common-prop-types/user-prop-types.js","webpack:///./app/javascript/common-prop-types/selected-tags-prop-types.js","webpack:///./app/javascript/utilities/calculateTextAreaHeight.js","webpack:///./app/javascript/utilities/textAreaUtils.js","webpack:///./app/javascript/common-prop-types/default-children-prop-types.js","webpack:///./app/javascript/Snackbar/index.js","webpack:///./app/javascript/shared/components/useKeyboardShortcuts.js","webpack:///./app/javascript/crayons/Button/index.js","webpack:///./app/javascript/packs/validateFileInputs.js","webpack:///./app/javascript/utilities/viewport.js","webpack:///./app/javascript/shared/components/useMediaQuery.js","webpack:///./app/javascript/common-prop-types/article-prop-types.js","webpack:///./app/javascript/crayons/Button/Button.jsx","webpack:///./app/javascript/Snackbar/SnackbarItem.jsx","webpack:///./app/javascript/shared/components/focusTrap.jsx","webpack:///./app/javascript/utilities/dropdownUtils.js","webpack:///./app/javascript/utilities/debounceAction.js","webpack:///./app/javascript/article-form/actions.js","webpack:///./app/javascript/crayons/Modal/Modal.jsx","webpack:///./app/javascript/crayons/Spinner/Spinner.jsx","webpack:///./app/javascript/Snackbar/Snackbar.jsx","webpack:///./app/javascript/crayons/MarkdownToolbar/icons.jsx","webpack:///./app/javascript/crayons/MarkdownToolbar/markdownSyntaxFormatters.js","webpack:///./app/javascript/crayons/MarkdownToolbar/MarkdownToolbar.jsx","webpack:///./app/javascript/article-form/components/ClipboardButton.jsx","webpack:///./app/javascript/article-form/components/ImageUploader.jsx"],"names":["ButtonNew","props","children","primary","icon","rounded","destructive","type","className","tooltip","onKeyUp","otherProps","useState","suppressTooltip","setSuppressTooltip","classes","classNames","event","key","focusable","src","displayName","propTypes","defaultChildrenPropTypes","PropTypes","bool","oneOf","string","oneOfType","node","func","elementType","Link","href","variant","block","undefined","isRequired","CTA","ButtonGroup","role","Dropdown","triggerButtonId","dropdownContentId","dropdownContentCloseButtonId","onOpen","onClose","restOfProps","isInitialized","setIsInitialized","useLayoutEffect","initializeDropdown","triggerElementId","id","length","defaultProps","FormField","RadioButton","value","name","checked","onClick","Icon","InternalIcon","native","MobileDrawer","title","clickOutsideDeactivates","selector","onDeactivate","arrayOf","shape","url","isCurrentPage","userPropTypes","profile_image_url","summary","selectedTagsPropTypes","tags","onKeyPress","hiddenTextarea","SIZING_STYLE","calculateNodeStyling","style","window","getComputedStyle","boxSizing","getPropertyValue","paddingSize","parseFloat","borderSize","sizingStyle","map","join","getCursorXY","input","selectionPoint","bodyRect","document","body","getBoundingClientRect","elementRect","inputY","top","scrollTop","inputX","left","scrollLeft","div","createElement","copyStyle","prop","inputValue","tagName","replace","textContent","substr","height","width","span","appendChild","spanX","offsetLeft","spanY","offsetTop","removeChild","x","y","getMentionWordData","textArea","selectionStart","valueBeforeKeystroke","isUserMention","indexOfMentionStart","indexOfAutocompleteStart","getLastIndexOfCharacter","content","selectionIndex","character","breakOnCharacters","currentCharacter","charAt","previousCharacter","includes","getNextIndexOfCharacter","nextCharacter","getNumberOfNewLinesPrecedingSelection","count","searchIndex","getNumberOfNewLinesFollowingSelection","selectionEnd","getSelectionData","textBeforeSelection","substring","textAfterSelection","selectedText","useTextAreaAutoResize","setTextArea","constrainToContentHeight","setConstrainToContentHeight","additionalElements","setAdditionalElements","useEffect","resizeTextArea","allContentHeights","element","uiTextNode","setAttribute","placeholder","baseHeight","scrollHeight","calculateTextAreaHeight","Math","max","newHeight","forEach","addEventListener","removeEventListener","object","isFormField","HTMLElement","nodeName","toLowerCase","getAttribute","isContentEditable","callShortcut","e","keys","chain","shortcuts","shortcut","code","defaultOptions","timeout","useKeyboardShortcuts","eventTarget","options","storedShortcuts","keyChain","setKeyChain","mergedOptions","setMergedOptions","newOptions","setTimeout","clearTimeout","Object","keyEvent","defaultPrevented","ctrlKeyEntry","ctrlKey","cmdKeyEntry","metaKey","altKeyEntry","altKey","shiftKeyEntry","shiftKey","target","Node","newChain","KeyboardShortcuts","number","instanceOf","Element","Window","MAX_FILE_SIZE_MB","freeze","image","video","PERMITTED_FILE_TYPES","addErrorMessage","fileInput","msg","fileInputField","error","color","innerHTML","classList","add","parentNode","append","validateFileSize","file","fileType","maxFileSizeMb","dataset","fileSizeErrorHandler","fileSizeMb","size","toFixed","isValidFileSize","Number","errorMessage","handleFileSizeError","validateFileType","permittedFileTypes","JSON","parse","fileTypeErrorHandler","isValidFileType","handleFileTypeError","validateFileNameLength","maxFileNameLength","fileNameLengthErrorHandler","isValidFileNameLength","handleFileNameLengthError","validateFileInput","isValidFileInput","querySelector","remove","removeErrorMessage","files","Array","from","i","split","validateFileInputs","validFileInputs","fileInputs","querySelectorAll","isInViewport","allowPartialVisibility","boundingRect","clientHeight","innerHeight","documentElement","clientWidth","innerWidth","topIsInViewport","rightIsInViewport","right","bottomIsInViewport","bottom","leftIsInViewport","topIsOutOfViewport","bottomIsOutOfViewport","BREAKPOINTS","Small","Medium","Large","useMediaQuery","query","mediaQuery","matchMedia","matches","match","setMatch","handler","addListener","removeListener","articleSnippetResultPropTypes","body_text","articlePropTypes","path","cloudinary_video_url","video_duration_in_minutes","type_of","class_name","flare_tag","bg_color_hex","text_color_hex","tag_list","cached_tag_list_array","podcast","slug","image_url","user_id","user","username","organization","profile_image_90","highlight","public_reactions_count","reactions_count","comments_count","reading_time","getAdditionalClassNames","contentType","inverted","disabled","additionalClassNames","Button","buttonType","onMouseOver","onMouseOut","onFocus","onBlur","tabIndex","ComponentName","snackbarItemProps","actions","message","lifespan","SnackbarItem","text","FocusTrap","focusTrap","useRef","deactivate","useCallback","currentLocationHref","location","routeChangeObserver","MutationObserver","mutations","some","current","disconnect","createFocusTrap","escapeDeactivates","activate","observe","childList","escape","getDropdownRepositionListener","debounceAction","handleDropdownRepositions","isDropdownCurrentlyOpen","display","opacity","removeProperty","INTERACTIVE_ELEMENTS_QUERY","closeDropdown","dropdownContent","getElementById","triggerButton","keyUpListener","onCloseCleanupActions","focus","contains","activeElement","clickOutsideListener","openDropdown","action","time","config","leading","configs","debounce","previewArticle","payload","successCb","failureCb","fetch","method","headers","Accept","csrfToken","stringify","article_body","credentials","then","response","json","status","processPayload","previewShowing","helpShowing","previewResponse","helpHTML","imageManagementShowing","moreConfigShowing","errors","submitArticle","onSuccess","onError","article","current_state_path","generateUploadFormdata","token","formData","FormData","entries","generateMainImage","signal","Error","links","processImageUpload","images","handleImageSuccess","handleImageFailure","CloseIcon","viewBox","xmlns","d","Modal","overlay","closeOnClickOutside","focusTrapSelector","Spinner","fill","snackbarItems","addSnackbarItem","snackbarItem","isArray","push","Snackbar","state","snacks","pollingId","paused","pauseLifespan","resumeLifespan","this","initializePolling","_event","stopPropagation","pollingTime","setInterval","newSnacks","updateSnackbarItems","snack","lifespanTimeoutId","decreaseLifespan","addCloseButton","setState","prevState","filter","potentialSnackToFilterOut","updatedSnacks","slice","currentSnack","ref","index","Component","Overflow","Help","ORDERED_LIST_ITEM_REGEX","MARKDOWN_LINK_REGEX","URL_PLACEHOLDER_TEXT","getNewLinePrefixSuffixes","numberOfNewLinesBeforeSelection","numberOfNewLinesFollowingSelection","numberOfNewLinesNeededAtStart","newLinesPrefix","String","prototype","padStart","newLinesSuffix","undoOrAddFormattingForInlineSyntax","prefix","suffix","prefixLength","suffixLength","editSelectionStart","editSelectionEnd","replaceSelectionWith","newCursorStart","newCursorEnd","undoOrAddFormattingForMultilineSyntax","linePrefix","blockPrefix","blockSuffix","formattedText","lastNewLine","lineStart","splitByNewLine","line","every","unformattedText","newLinePrefixLength","cursorStartBaseline","cursorStartBlockPrefixOffset","cursorStartLinePrefixOffset","coreSyntaxFormatters","bold","label","getKeyboardShortcut","modifier","Runtime","getOSKeyboardModifierKeyString","command","tooltipHint","toUpperCase","getFormatting","italic","link","startingText","basicFormattingForEmptySelection","indexOfLinkStructureEnd","urlText","handleLinkFormattingForEmptyTextSelection","startsWith","basicFormattingForLinkSelection","indexOfSyntaxOpen","textToReplaceMarkdown","handleLinkFormattingForUrlSelection","linkDescriptionEnd","linkText","handleUndoMarkdownLinkSelection","orderedList","newLineSuffixLength","newText","indexOfFullStop","indexOf","formattedList","textChunk","unorderedList","heading","currentLineSelectionStart","indexOfFirstLineCharacter","currentHeadingIndex","adjustingHeading","cursorOffset","quote","codeBlock","secondarySyntaxFormatters","underline","strikethrough","divider","UPLOADING_IMAGE_PLACEHOLDER","MarkdownToolbar","textAreaId","overflowMenuOpen","setOverflowMenuOpen","storedCursorPosition","setStoredCursorPosition","smallScreen","markdownSyntaxFormatters","keyboardShortcuts","fromEntries","syntaxName","preventDefault","insertSyntax","clickOutsideHandler","escapePressHandler","getElementsByClassName","handleToolbarButtonKeyPress","nextButton","sibling","nextElementSibling","getNextMatchingSibling","previousButton","previousElementSibling","getPreviousMatchingSibling","firstButton","allButtons","lastButton","contentEditable","preventScroll","setSelectionRange","execCommand","textAreaValue","getNewTextAreaValueWithEdits","dispatchEvent","Event","handleImageUploadEnd","imageMarkdown","currentTextAreaValue","indexOfPlaceholder","newTextValue","differenceInLength","getSecondaryFormatterButtons","isOverflow","controlName","tabindex","ImageUploader","editorVersion","onImageUploadStart","textWithPlaceholder","newCursorPosition","onImageUploadSuccess","onImageUploadError","buttonProps","rel","CopyIcon","ClipboardButton","imageLinks","onCopy","imageUrls","showCopyMessage","for","readOnly","imageLink","ImageIcon","CancelIcon","SpinnerOrCancel","imageUploaderReducer","uploadErrorMessage","uploadingImage","insertionImageUrls","initNativeImagePicker","ForemMobile","injectNativeMessage","NativeIosV1ImageUpload","V2EditorImageUpload","handleInsertionImageUpload","useNativeUpload","handleNativeMessage","abortRequestController","setAbortRequestController","actionTooltip","onChange","controller","AbortController","accept","abort","click","V1EditorImageUpload","showCopiedImageText","setShowCopiedImageText","class","multiple","imageMarkdownInput","copyToClipboard","Honeybadger","notify","useReducer","dispatch","onUploadError","abortSignal","handleInsertImageUploadSuccess","innerText","detail","namespace","isNativeIOS"],"mappings":"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","file":"js/1-fe2c83a23d2182b0ea27.chunk.js","sourcesContent":["import { h } from 'preact';\nimport PropTypes from 'prop-types';\nimport { useState } from 'preact/hooks';\nimport classNames from 'classnames/bind';\nimport { defaultChildrenPropTypes } from '../../common-prop-types/default-children-prop-types';\nimport { Icon } from '@crayons';\n\nexport const ButtonNew = (props) => {\n const {\n children,\n primary,\n icon,\n rounded,\n destructive,\n type = 'button',\n className,\n tooltip,\n onKeyUp,\n ...otherProps\n } = props;\n\n const [suppressTooltip, setSuppressTooltip] = useState(false);\n\n const handleKeyUp = (event) => {\n onKeyUp?.(event);\n if (!tooltip) {\n return;\n }\n setSuppressTooltip(event.key === 'Escape');\n };\n\n const classes = classNames('c-btn', {\n 'c-btn--primary': primary,\n 'c-btn--destructive': destructive,\n 'c-btn--icon-left': icon && children,\n 'c-btn--icon-alone': icon && !children,\n 'crayons-tooltip__activator': tooltip,\n 'radius-full': rounded,\n [className]: className,\n });\n\n return (\n \n {icon && (\n \n )}\n {children}\n {tooltip ? (\n \n {tooltip}\n \n ) : null}\n \n );\n};\n\nButtonNew.displayName = 'ButtonNew';\n\nButtonNew.propTypes = {\n children: defaultChildrenPropTypes,\n primary: PropTypes.bool,\n rounded: PropTypes.bool,\n destructive: PropTypes.bool,\n type: PropTypes.oneOf(['button', 'submit']),\n className: PropTypes.string,\n tooltip: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),\n onKeyUp: PropTypes.func,\n icon: PropTypes.elementType,\n};\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\nimport classNames from 'classnames/bind';\nimport { defaultChildrenPropTypes } from '../../common-prop-types/default-children-prop-types';\nimport { Icon } from '@crayons';\n\nexport const Link = (props) => {\n const {\n children,\n href = '#',\n variant,\n block,\n icon,\n rounded,\n className,\n ...otherProps\n } = props;\n\n const classes = classNames('c-link', {\n [`c-link--${variant}`]: variant,\n 'c-link--icon-left': icon && children,\n 'c-link--icon-alone': icon && !children,\n 'c-link--block': block,\n 'radius-full': rounded,\n [className]: className,\n });\n\n return (\n \n {icon && (\n \n )}\n {children}\n \n );\n};\n\nLink.displayName = 'Link';\n\nLink.propTypes = {\n variant: PropTypes.oneOf([undefined, 'branded']),\n block: PropTypes.bool,\n rounded: PropTypes.bool,\n href: PropTypes.string.isRequired,\n className: PropTypes.string,\n children: defaultChildrenPropTypes,\n icon: PropTypes.elementType,\n};\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\nimport classNames from 'classnames/bind';\nimport { defaultChildrenPropTypes } from '../../common-prop-types/default-children-prop-types';\nimport { Icon } from '@crayons';\n\nexport const CTA = (props) => {\n const {\n children,\n href = '#',\n variant,\n icon,\n className,\n ...otherProps\n } = props;\n\n const classes = classNames('c-cta', {\n [`c-cta--${variant}`]: variant,\n 'c-cta--icon-left': icon && children,\n [className]: className,\n });\n\n return (\n \n {icon && (\n \n )}\n {children}\n \n );\n};\n\nCTA.displayName = 'CTA';\n\nCTA.propTypes = {\n variant: PropTypes.oneOf([undefined, 'branded']),\n rounded: PropTypes.bool,\n href: PropTypes.string.isRequired,\n className: PropTypes.string,\n children: defaultChildrenPropTypes.isRequired,\n icon: PropTypes.elementType,\n};\n","import { h } from 'preact';\nimport { defaultChildrenPropTypes } from '../../common-prop-types/default-children-prop-types';\n\nexport const ButtonGroup = ({ children }) => (\n
\n {children}\n
\n);\n\nButtonGroup.displayName = 'ButtonGroup';\n\nButtonGroup.propTypes = {\n children: defaultChildrenPropTypes,\n};\n","import { h } from 'preact';\nimport { useLayoutEffect, useState } from 'preact/hooks';\nimport PropTypes from 'prop-types';\nimport { defaultChildrenPropTypes } from '../../common-prop-types/default-children-prop-types';\nimport { initializeDropdown } from '@utilities/dropdownUtils';\n\n/**\n * A component to render a dropdown with the provided children.\n * This component handles the attachment of all open/close click events and listeners.\n *\n * @param {Object} props\n * @param {Array} props.children Children to be rendered inside the dropdown, passed via composition\n * @param {String} props.className Optional string of classnames to be applied to the dropdown (e.g for positioning)\n * @param {String} props.triggerButtonId The ID of the button element which should open and close the dropdown\n * @param {String} props.dropdownContentId The ID to be applied to the dropdown itself\n * @param {String} props.dropdownContentCloseButtonId An optional ID for any button inside the dropdown content itself which should close it\n * @param {Function} props.onOpen Optional callback for any side-effects needed when the dropdown opens\n * @param {Function} props.onClose Optional callback for any side-effects needed when the dropdown closes\n *\n * @example\n *
\n * \n * \n * {dropdownInnerContent}\n * \n *
\n */\nexport const Dropdown = ({\n children,\n className,\n triggerButtonId,\n dropdownContentId,\n dropdownContentCloseButtonId,\n onOpen = () => {},\n onClose = () => {},\n ...restOfProps\n}) => {\n const [isInitialized, setIsInitialized] = useState(false);\n useLayoutEffect(() => {\n if (!isInitialized) {\n initializeDropdown({\n triggerElementId: triggerButtonId,\n dropdownContentId,\n dropdownContentCloseButtonId,\n onOpen,\n onClose,\n });\n\n setIsInitialized(true);\n }\n }, [\n dropdownContentId,\n triggerButtonId,\n dropdownContentCloseButtonId,\n isInitialized,\n onOpen,\n onClose,\n ]);\n\n return (\n 0 ? ` ${className}` : ''\n }`}\n {...restOfProps}\n >\n {children}\n \n );\n};\n\nDropdown.defaultProps = {\n className: undefined,\n};\n\nDropdown.displayName = 'Dropdown';\n\nDropdown.propTypes = {\n children: defaultChildrenPropTypes.isRequired,\n className: PropTypes.string,\n triggerButtonId: PropTypes.string.isRequired,\n dropdownContentId: PropTypes.string.isRequired,\n dropdownContentCloseButtonId: PropTypes.string,\n onOpen: PropTypes.func,\n onClose: PropTypes.func,\n};\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\nimport { defaultChildrenPropTypes } from '../../../common-prop-types';\n\n// Only radio and checkboxes require an additional CSS class (variant class). Other form elements do not.\n\nexport const FormField = ({ children, variant }) => {\n return (\n 0 ? ` crayons-field--${variant}` : ''\n }`}\n >\n {children}\n \n );\n};\n\nFormField.displayName = 'FormField';\n\nFormField.defaultProps = {\n variant: undefined,\n};\n\nFormField.propTypes = {\n children: defaultChildrenPropTypes.isRequired,\n variant: PropTypes.oneOf(['radio', 'checkbox']),\n};\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\n\nexport const RadioButton = (props) => {\n const { id, value, name, className, checked, onClick, ...otherProps } = props;\n\n return (\n 0 ? ` ${className}` : ''\n }`}\n checked={checked}\n onClick={onClick}\n type=\"radio\"\n {...otherProps}\n />\n );\n};\n\nRadioButton.displayName = 'RadioButton';\n\nRadioButton.defaultProps = {\n id: undefined,\n className: undefined,\n checked: false,\n name: undefined,\n};\n\nRadioButton.propTypes = {\n id: PropTypes.string,\n value: PropTypes.string.isRequired,\n className: PropTypes.string,\n checked: PropTypes.bool,\n name: PropTypes.string,\n onClick: PropTypes.func.isRequired,\n};\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\nimport classNames from 'classnames/bind';\n\nexport const Icon = ({\n src: InternalIcon,\n native,\n className,\n ...otherProps\n}) => {\n return (\n \n );\n};\n\nIcon.displayName = 'Icon';\n\nIcon.propTypes = {\n native: PropTypes.bool,\n className: PropTypes.string,\n src: PropTypes.elementType.isRequired,\n};\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\nimport { defaultChildrenPropTypes } from '../../common-prop-types';\nimport { FocusTrap } from '../../shared/components/focusTrap';\n\n/**\n * A component that creates a full-width modal that slides in from the bottom of viewport.\n *\n *\n * @param {object} props\n * @param {Array} props.children\n * @param {string} props.title The title to be applied to the dialog, surfaced to screen reader users\n * @param {Function} props.onClose Action to complete when user opts to close the drawer\n *\n * @example\n * const [isDrawerOpen, setIsDrawerOpen] = useState(false);\n * return (\n *
\n * \n * {isDrawerOpen && (\n * setIsDrawerOpen(false)}\n * >\n *

Lorem ipsum

\n * \n * \n * )}\n *
\n * );\n */\nexport const MobileDrawer = ({ children, title, onClose = () => {} }) => {\n return (\n
\n
\n \n \n {children}\n
\n \n
\n );\n};\n\nMobileDrawer.propTypes = {\n children: defaultChildrenPropTypes.isRequired,\n title: PropTypes.string.isRequired,\n onClose: PropTypes.func,\n};\n","import { h, Fragment } from 'preact';\nimport { useState } from 'preact/hooks';\nimport PropTypes from 'prop-types';\nimport { MobileDrawer } from '@crayons/MobileDrawer';\nimport { Button } from '@crayons/Button';\n\nconst OverflowIcon = () => (\n \n \n \n);\n\nconst CheckIcon = () => (\n \n \n \n);\n\n/**\n * Renders a page heading with a button which activates a with a list of the given navigation links.\n *\n * @param {object} props\n * @param {number} headingLevel The level of heading to render as the page title (e.g. 1-6)\n * @param {string} navigationTitle The title to be used for the navigation element (e.g. 'Feed timeframes')\n * @param {Array} navigationLinks An array of navigationLink objects to display\n *\n * @example\n * \n */\nexport const MobileDrawerNavigation = ({\n headingLevel,\n navigationTitle,\n navigationLinks,\n}) => {\n const [isDrawerOpen, setIsDrawerOpen] = useState(false);\n const currentPage = navigationLinks.find((item) => item.isCurrentPage);\n\n const Heading = `h${headingLevel}`;\n\n return (\n \n
\n {currentPage.displayName}\n setIsDrawerOpen(true)}\n />\n
\n\n {isDrawerOpen && (\n setIsDrawerOpen(false)}\n >\n \n setIsDrawerOpen(false)}\n >\n Cancel\n \n
\n )}\n \n );\n};\n\nMobileDrawerNavigation.propTypes = {\n headingLevel: PropTypes.oneOf([1, 2, 3, 4, 5, 6]).isRequired,\n navigationTitle: PropTypes.string.isRequired,\n navigationLinks: PropTypes.arrayOf(\n PropTypes.shape({\n url: PropTypes.string,\n isCurrentPage: PropTypes.bool,\n displayName: PropTypes.string,\n }),\n ).isRequired,\n};\n","import PropTypes from 'prop-types';\n\nexport const userPropTypes = PropTypes.shape({\n id: PropTypes.string.isRequired,\n name: PropTypes.string.isRequired,\n profile_image_url: PropTypes.string.isRequired,\n summary: PropTypes.string.isRequired,\n});\n","import PropTypes from 'prop-types';\n\nexport const selectedTagsPropTypes = PropTypes.shape({\n tags: PropTypes.arrayOf(PropTypes.string).isRequired,\n onClick: PropTypes.func.isRequired,\n onKeyPress: PropTypes.func.isRequired,\n});\n","// These styles are applied to the hidden element we use to measure the height.\n// !important styles are used to ensure no matter what style properties are attached to the given textarea, the hidden textarea will never become visible or cause layout jumps\nconst HIDDEN_TEXTAREA_STYLE = `\nmin-height:0 !important;\nmax-height:none !important;\nheight:0 !important;\nvisibility:hidden !important;\noverflow:hidden !important;\nposition:absolute !important;\nz-index:-1000 !important;\ntop:0 !important;\nright:0 !important\n`;\n\nconst SIZING_STYLE = [\n 'letter-spacing',\n 'line-height',\n 'padding-top',\n 'padding-bottom',\n 'font-family',\n 'font-weight',\n 'font-size',\n 'text-rendering',\n 'text-transform',\n 'width',\n 'text-indent',\n 'padding-left',\n 'padding-right',\n 'border-width',\n 'box-sizing',\n];\n\nlet hiddenTextarea;\n\n/**\n * Helper function to get the height of the textarea based on the current text content\n *\n * @param {HTMLElement} uiTextNode The textarea to measure height of\n *\n * @returns {{height: number}} Object with the calculated height\n */\nexport const calculateTextAreaHeight = (uiTextNode) => {\n if (!hiddenTextarea) {\n hiddenTextarea = document.createElement('textarea');\n document.body.appendChild(hiddenTextarea);\n }\n\n // Copy all CSS properties that have an impact on the height of the content in\n // the textbox\n const {\n paddingSize,\n borderSize,\n boxSizing,\n sizingStyle,\n } = calculateNodeStyling(uiTextNode);\n\n // Need to have the overflow attribute to hide the scrollbar otherwise\n // text-lines will not calculated properly as the shadow will technically be\n // narrower for content\n hiddenTextarea.setAttribute(\n 'style',\n `${sizingStyle};${HIDDEN_TEXTAREA_STYLE}`,\n );\n hiddenTextarea.value = uiTextNode.value || uiTextNode.placeholder || 'x';\n\n const baseHeight = hiddenTextarea.scrollHeight;\n\n if (boxSizing === 'border-box') {\n // border-box: add border, since height = content + padding + border\n return { height: baseHeight + borderSize };\n } else if (boxSizing === 'content-box') {\n // remove padding, since height = content\n return { height: baseHeight - paddingSize };\n }\n\n return { height: baseHeight };\n};\n\nconst calculateNodeStyling = (node) => {\n const style = window.getComputedStyle(node);\n\n const boxSizing =\n style.getPropertyValue('box-sizing') ||\n style.getPropertyValue('-moz-box-sizing') ||\n style.getPropertyValue('-webkit-box-sizing');\n\n const paddingSize =\n parseFloat(style.getPropertyValue('padding-bottom')) +\n parseFloat(style.getPropertyValue('padding-top'));\n\n const borderSize =\n parseFloat(style.getPropertyValue('border-bottom-width')) +\n parseFloat(style.getPropertyValue('border-top-width'));\n\n const sizingStyle = SIZING_STYLE.map(\n (name) => `${name}:${style.getPropertyValue(name)}`,\n ).join(';');\n\n return {\n sizingStyle,\n paddingSize,\n borderSize,\n boxSizing,\n };\n};\n","import { useEffect, useState } from 'preact/hooks';\nimport { calculateTextAreaHeight } from '@utilities/calculateTextAreaHeight';\n\n/**\n * A helper function to get the X/Y coordinates of the current cursor position within an element.\n * For a full explanation see the post by Jhey Tompkins: https://medium.com/@jh3y/how-to-where-s-the-caret-getting-the-xy-position-of-the-caret-a24ba372990a\n *\n * @param {element} input The DOM element the cursor is to be found within\n * @param {number} selectionPoint The current cursor position (e.g. either selectionStart or selectionEnd)\n *\n * @returns {object} An object with x and y properties (e.g. {x: 10, y: 0})\n *\n * @example\n * const coordinates = getCursorXY(elementRef.current, elementRef.current.selectionStart)\n */\nexport const getCursorXY = (input, selectionPoint) => {\n const bodyRect = document.body.getBoundingClientRect();\n const elementRect = input.getBoundingClientRect();\n\n const inputY = elementRect.top - bodyRect.top - input.scrollTop;\n const inputX = elementRect.left - bodyRect.left - input.scrollLeft;\n\n // create a dummy element with the computed style of the input\n const div = document.createElement('div');\n const copyStyle = getComputedStyle(input);\n for (const prop of copyStyle) {\n div.style[prop] = copyStyle[prop];\n }\n\n // set the div to the correct position\n div.style['position'] = 'absolute';\n div.style['top'] = `${inputY}px`;\n div.style['left'] = `${inputX}px`;\n div.style['opacity'] = 0;\n\n // replace whitespace with '.' when filling the dummy element if it's a single line \n const swap = '.';\n const inputValue =\n input.tagName === 'INPUT' ? input.value.replace(/ /g, swap) : input.value;\n\n // set the div content to that of the textarea up until selection point\n div.textContent = inputValue.substr(0, selectionPoint);\n\n if (input.tagName === 'TEXTAREA') div.style.height = 'auto';\n // if a single line input then the div needs to be single line and not break out like a text area\n if (input.tagName === 'INPUT') div.style.width = 'auto';\n\n // marker element to obtain caret position\n const span = document.createElement('span');\n // give the span the textContent of remaining content so that the recreated dummy element is as close as possible\n span.textContent = inputValue.substr(selectionPoint) || '.';\n\n // append the span marker to the div and the dummy element to the body\n div.appendChild(span);\n document.body.appendChild(div);\n\n // get the marker position, this is the caret position top and left relative to the input\n const { offsetLeft: spanX, offsetTop: spanY } = span;\n\n // remove dummy element\n document.body.removeChild(div);\n\n // return object with the x and y of the caret. account for input positioning so that you don't need to wrap the input\n return {\n x: inputX + spanX,\n y: inputY + spanY,\n };\n};\n\n/**\n * A helper function that searches back to the beginning of the currently typed word (indicated by cursor position) and verifies whether it begins with an '@' symbol for user mention\n *\n * @param {element} textArea The text area or input to inspect the current word of\n * @returns {{isUserMention: boolean, indexOfMentionStart: number}} Object with the word's mention data\n *\n * @example\n * const { isUserMention, indexOfMentionStart } = getMentionWordData(textArea);\n * if (isUserMention) {\n * // Do something\n * }\n */\nexport const getMentionWordData = (textArea) => {\n const { selectionStart, value: valueBeforeKeystroke } = textArea;\n\n if (selectionStart === 0 || valueBeforeKeystroke === '') {\n return {\n isUserMention: false,\n indexOfMentionStart: -1,\n };\n }\n\n const indexOfAutocompleteStart = getLastIndexOfCharacter({\n content: valueBeforeKeystroke,\n selectionIndex: selectionStart,\n character: '@',\n breakOnCharacters: [' ', '', '\\n'],\n });\n\n return {\n isUserMention: indexOfAutocompleteStart !== -1,\n indexOfMentionStart: indexOfAutocompleteStart,\n };\n};\n\n/**\n * Searches backwards through text content for the last occurrence of the given character\n *\n * @param {Object} params\n * @param {string} content The chunk of text to search within\n * @param {number} selectionIndex The starting point to search from\n * @param {string} character The character to search for\n * @param {string[]} breakOnCharacters Any characters which should result in an immediate halt to the search\n * @returns {number} Index of the last occurrence of the character, or -1 if it isn't found\n */\nexport const getLastIndexOfCharacter = ({\n content,\n selectionIndex,\n character,\n breakOnCharacters = [],\n}) => {\n const currentCharacter = content.charAt(selectionIndex);\n const previousCharacter = content.charAt(selectionIndex - 1);\n\n if (currentCharacter === character) {\n return selectionIndex;\n }\n\n if (selectionIndex !== 0 && !breakOnCharacters.includes(previousCharacter)) {\n return getLastIndexOfCharacter({\n content,\n selectionIndex: selectionIndex - 1,\n character,\n breakOnCharacters,\n });\n }\n\n return -1;\n};\n\n/**\n * Searches forwards through text content for the next occurrence of the given character\n *\n * @param {Object} params\n * @param {string} content The chunk of text to search within\n * @param {number} selectionIndex The starting point to search from\n * @param {string} character The character to search for\n * @param {string[]} breakOnCharacters Any characters which should result in an immediate halt to the search\n * @returns {number} Index of the next occurrence of the character, or -1 if it isn't found\n */\nexport const getNextIndexOfCharacter = ({\n content,\n selectionIndex,\n character,\n breakOnCharacters = [],\n}) => {\n const currentCharacter = content.charAt(selectionIndex);\n const nextCharacter = content.charAt(selectionIndex + 1);\n\n if (currentCharacter === character) {\n return selectionIndex;\n }\n\n if (\n selectionIndex <= content.length &&\n !breakOnCharacters.includes(nextCharacter)\n ) {\n return getNextIndexOfCharacter({\n content,\n selectionIndex: selectionIndex + 1,\n character,\n breakOnCharacters,\n });\n }\n\n return -1;\n};\n\n/**\n * Counts how many new lines come immediately before the user's current selection start\n * @param {object} args\n * @param {number} args.selectionStart The index of user's current selection start\n * @param {string} args.value The value of the textarea\n *\n * @returns {number} Number of new lines directly before selection start\n */\nexport const getNumberOfNewLinesPrecedingSelection = ({\n selectionStart,\n value,\n}) => {\n if (selectionStart === 0) {\n return 0;\n }\n\n let count = 0;\n let searchIndex = selectionStart - 1;\n\n while (searchIndex >= 0 && value.charAt(searchIndex) === '\\n') {\n count++;\n searchIndex--;\n }\n\n return count;\n};\n\n/**\n * Counts how many new lines come immediately after the user's current selection end\n *\n * @param {object} args\n * @param {number} args.selectionEnd The index of user's current selection end\n * @param {string} args.value The value of the textarea\n *\n * @returns {number} the count of new line characters immediately following selection\n */\nexport const getNumberOfNewLinesFollowingSelection = ({\n selectionEnd,\n value,\n}) => {\n if (selectionEnd === value.length) {\n return 0;\n }\n\n let count = 0;\n let searchIndex = selectionEnd;\n\n while (searchIndex < value.length && value.charAt(searchIndex) === '\\n') {\n count++;\n searchIndex++;\n }\n\n return count;\n};\n\n/**\n * Retrieve data about the user's current text selection\n *\n * @param {Object} params\n * @param {number} selectionStart The start point of user's selection\n * @param {number} selectionEnd The end point of user's selection\n * @param {string} value The current value of the textarea\n * @returns {Object} object containing the text chunks before and after insertion, and the currently selected text\n */\nexport const getSelectionData = ({ selectionStart, selectionEnd, value }) => ({\n textBeforeSelection: value.substring(0, selectionStart),\n textAfterSelection: value.substring(selectionEnd, value.length),\n selectedText: value.substring(selectionStart, selectionEnd),\n});\n\n/**\n * This hook can be used to keep the height of a textarea in step with the current content height, avoiding a scrolling textarea.\n * An optional array of additional elements can be set. If provided, all elements will be set to the greatest content height.\n * Optionally, it can be specified to also constrain the max-height to the content height. Otherwise the max-height will continue to be managed only by the textarea's CSS\n *\n * @example\n *\n * const { setTextArea } = useTextAreaAutoResize();\n * setTextArea(myTextAreaRef.current);\n * setAdditionalElements([myOtherElement.current]);\n */\nexport const useTextAreaAutoResize = () => {\n const [textArea, setTextArea] = useState(null);\n const [constrainToContentHeight, setConstrainToContentHeight] =\n useState(false);\n const [additionalElements, setAdditionalElements] = useState([]);\n\n useEffect(() => {\n if (!textArea) {\n return;\n }\n\n const resizeTextArea = () => {\n const allElements = [textArea, ...additionalElements];\n\n const allContentHeights = allElements.map(\n (element) => calculateTextAreaHeight(element).height,\n );\n\n const height = Math.max(...allContentHeights);\n const newHeight = `${height}px`;\n\n [textArea, ...additionalElements].forEach((element) => {\n element.style['min-height'] = newHeight;\n if (constrainToContentHeight) {\n // Don't allow the textarea to grow to a size larger than the content\n element.style['max-height'] = newHeight;\n }\n });\n };\n\n // Resize on first attach\n resizeTextArea();\n // Resize on subsequent value changes\n textArea.addEventListener('input', resizeTextArea);\n\n return () => textArea.removeEventListener('input', resizeTextArea);\n }, [textArea, additionalElements, constrainToContentHeight]);\n\n return { setTextArea, setAdditionalElements, setConstrainToContentHeight };\n};\n","import PropTypes from 'prop-types';\n\n// Use this whenever you need the standard children prop.\nexport const defaultChildrenPropTypes = PropTypes.oneOfType([\n PropTypes.arrayOf(PropTypes.node),\n PropTypes.node,\n PropTypes.object,\n PropTypes.arrayOf(PropTypes.object),\n]);\n","export * from './Snackbar';\nexport * from './SnackbarItem';\n","import { useState, useEffect } from 'preact/hooks';\nimport PropTypes from 'prop-types';\n\n/**\n * Checker that return true if element is a form element\n *\n * @param {node} element to be checked\n *\n * @returns {boolean} isFormField\n */\nfunction isFormField(element) {\n if (element instanceof HTMLElement === false) return false;\n\n const name = element.nodeName.toLowerCase();\n const type = (element.getAttribute('type') || '').toLowerCase();\n return (\n name === 'select' ||\n name === 'textarea' ||\n (name === 'input' &&\n type !== 'submit' &&\n type !== 'reset' &&\n type !== 'checkbox' &&\n type !== 'radio') ||\n element.isContentEditable\n );\n}\n\n/**\n * Function to handle converting key presses to callback functions\n *\n * @param {KeyboardEvent} e Keyboard event\n * @param {String} keys special keys formatted in a string\n * @param {Array} chain array of past keys\n * @param {Object} shortcuts object containing callback functions\n *\n * @returns {Array} New chain\n */\nconst callShortcut = (e, keys, chain, shortcuts) => {\n const shortcut =\n chain && chain.length > 0\n ? shortcuts[`${chain.join('~')}~${e.code}`]\n : shortcuts[`${keys}${e.code}`] ||\n shortcuts[`${keys}${e.key.toLowerCase()}`];\n\n // if a valid shortcut is found call it and reset the chain\n if (shortcut) {\n shortcut(e);\n return [];\n }\n\n // if we have keys don't add to the chain\n if (keys || e.key === 'Shift') {\n return [];\n }\n\n return [...chain, e.code];\n};\n\n// Default options to be used if null\nconst defaultOptions = {\n timeout: 0, // The default is zero as we want no delays between keystrokes by default.\n};\n\n/**\n * hook that can be added to a component to listen\n * for keyboard presses\n *\n * @example\n * const shortcuts = {\n * 'ctrl+alt+KeyG': (e) => {\n * e.preventDefault();\n * alert('Control Alt G has been pressed');\n * },\n * 'KeyG~KeyH': (e) => {\n * e.preventDefault();\n * alert('G has been pressed quickly followed by H');\n * },\n * '?': (e) => {\n * setIsHelpVisible(true);\n * }\n * }\n *\n * useKeyboardShortcuts(shortcuts, someElementOrWindowObject, {timeout: 1500});\n *\n * @param {object} shortcuts List of keyboard shortcuts/event\n * @param {EventTarget} [eventTarget=window] An event target.\n * @param {object} [options = {}] An object for extra options\n *\n */\nexport function useKeyboardShortcuts(\n shortcuts,\n eventTarget = window,\n options = {},\n) {\n const [storedShortcuts] = useState(shortcuts);\n const [keyChain, setKeyChain] = useState([]);\n const [mergedOptions, setMergedOptions] = useState({\n ...defaultOptions,\n ...options,\n });\n\n // update mergedOptions if options prop changes\n useEffect(() => {\n const newOptions = {};\n if (typeof options.timeout === 'number')\n newOptions.timeout = options.timeout;\n setMergedOptions({ ...defaultOptions, ...newOptions });\n }, [options.timeout]);\n\n // clear key chain after timeout is reached\n useEffect(() => {\n if (keyChain.length <= 0) return;\n\n const timeout = window.setTimeout(() => {\n clearTimeout(timeout);\n setKeyChain([]);\n }, mergedOptions.timeout);\n\n return () => clearTimeout(timeout);\n }, [keyChain.length, mergedOptions.timeout]);\n\n // set up event listeners\n useEffect(() => {\n if (!storedShortcuts || Object.keys(storedShortcuts).length === 0) return;\n\n const keyEvent = (e) => {\n if (e.defaultPrevented) return;\n\n const ctrlKeyEntry = e.ctrlKey ? 'ctrl+' : '';\n const cmdKeyEntry = e.metaKey ? 'cmd+' : '';\n const altKeyEntry = e.altKey ? 'alt+' : '';\n const shiftKeyEntry = e.shiftKey ? 'shift+' : '';\n\n // We build the special keys string in an opinionated order to ensure consistency\n const keys = `${ctrlKeyEntry}${cmdKeyEntry}${altKeyEntry}${shiftKeyEntry}`;\n\n // If no special keys, except shift, are pressed and focus is inside a field return\n if (e.target instanceof Node && isFormField(e.target) && !keys) return;\n\n const newChain = callShortcut(e, keys, keyChain, storedShortcuts);\n\n // update keychain with latest chain\n setKeyChain(newChain);\n };\n\n eventTarget.addEventListener('keydown', keyEvent);\n\n return () => eventTarget.removeEventListener('keydown', keyEvent);\n }, [keyChain, storedShortcuts, eventTarget]);\n}\n\n/**\n * A component that can be added to a component to listen\n * for keyboard presses using the useKeyboardShortcuts hook\n *\n * @example\n * const shortcuts = {\n * 'ctrl+alt+KeyG': (e) => {\n * e.preventDefault();\n * alert('Control Alt G has been pressed')\n * }\n * }\n *\n * \n * \n *\n * @param {object} shortcuts List of keyboard shortcuts/event\n * @param {EventTarget} [eventTarget=window] An event target.\n * @param {object} [options = {}] An object for extra options\n *\n */\nexport function KeyboardShortcuts({ shortcuts, eventTarget, options }) {\n useKeyboardShortcuts(shortcuts, eventTarget, options);\n\n return null;\n}\n\nKeyboardShortcuts.propTypes = {\n shortcuts: PropTypes.object.isRequired,\n options: PropTypes.shape({\n timeout: PropTypes.number,\n }),\n eventTarget: PropTypes.oneOfType([\n PropTypes.instanceOf(Element),\n PropTypes.instanceOf(Window),\n ]),\n};\n\nKeyboardShortcuts.defaultProps = {\n shortcuts: {},\n options: {},\n eventTarget: window,\n};\n","export * from './Button';\n","/**\n * @file Manages logic to validate file uploads client-side. In general, the\n * validations work by looping over input form fields with a type of file and\n * checking the size and format of the files upload by the user.\n */\n\n/**\n * An object containing the top level MIME type as the key and the max file\n * size in MB for the value. To use a different value than these defaults,\n * simply add a data-max-file-mb attribute to the input form field with the\n * max file size in MB. If that attribute is found, it takes priority over these\n * defaults.\n *\n * @constant {Object.}\n */\nconst MAX_FILE_SIZE_MB = Object.freeze({\n image: 25,\n video: 50,\n});\n\n/**\n * Permitted file types using the top level MIME type (i.e. image for\n * image/png). To specify permitted file types, simply add a\n * data-permitted-file-types attribute to the input form field as an Array of\n * strings specifying the top level MIME types that are permitted.\n *\n * @constant {string[]}\n */\nconst PERMITTED_FILE_TYPES = ['image'];\n\n/**\n * The maximum length of the file name to prevent errors on the backend when a\n * file name is too long.\n *\n * @constant {number}\n */\nconst MAX_FILE_NAME_LENGTH = 250;\n\n/**\n * Removes any pre-existing error messages from the DOM related to file\n * validation.\n *\n * @param {HTMLElement} fileInput - An input form field with type of file\n */\nfunction removeErrorMessage(fileInput) {\n const errorMessage = fileInput.parentNode.querySelector(\n 'div.file-upload-error',\n );\n\n if (errorMessage) {\n errorMessage.remove();\n }\n}\n\n/**\n * Adds error messages in the form of a div with red text.\n *\n * @param {HTMLElement} fileInput - An input form field with type of file\n * @param {string} msg - The error message to be displayed to the user\n *\n * @returns {HTMLElement} The error element that was added to the DOM\n */\nfunction addErrorMessage(fileInput, msg) {\n const fileInputField = fileInput;\n const error = document.createElement('div');\n error.style.color = 'red';\n error.innerHTML = msg;\n error.classList.add('file-upload-error');\n\n fileInputField.parentNode.append(error);\n}\n\n/**\n * Handles errors for files that are too large.\n *\n * @param {object} fileSizeErrorHandler - A custom function to be ran after the default error handling\n * @param {HTMLElement} fileInput - An input form field with type of file\n * @param {number} fileSizeMb - The size of the file in MB\n * @param {?number} maxFileSizeMb - The max file size limit in MB\n */\nfunction handleFileSizeError(\n fileSizeErrorHandler,\n fileInput,\n fileSizeMb,\n maxFileSizeMb,\n) {\n const fileInputField = fileInput;\n fileInputField.value = null;\n\n if (fileSizeErrorHandler) {\n fileSizeErrorHandler();\n } else {\n let errorMessage = `File size too large (${fileSizeMb} MB).`;\n\n // If a user uploads a file type that we haven't defined a max size limit for then maxFileSizeMb\n // could be NaN\n if (maxFileSizeMb >= 0) {\n errorMessage += ` The limit is ${maxFileSizeMb} MB.`;\n }\n\n addErrorMessage(fileInput, errorMessage);\n }\n}\n\n/**\n * Handles errors for files that are not a valid format.\n *\n * @param {object} fileSizeErrorHandler - A custom function to be ran after the default error handling\n * @param {HTMLElement} fileInput - An input form field with type of file\n * @param {string} fileType - The top level file type (i.e. image for image/png)\n * @param {string[]} permittedFileTypes - The top level file types (i.e. image for image/png) that are permitted\n */\nfunction handleFileTypeError(\n fileTypeErrorHandler,\n fileInput,\n fileType,\n permittedFileTypes,\n) {\n const fileInputField = fileInput;\n fileInputField.value = null;\n\n if (fileTypeErrorHandler) {\n fileTypeErrorHandler();\n } else {\n const errorMessage = `Invalid file format (${fileType}). Only ${permittedFileTypes.join(\n ', ',\n )} files are permitted.`;\n addErrorMessage(fileInput, errorMessage);\n }\n}\n\n/**\n * Handles errors for files with names that are too long.\n *\n * @param {object} fileNameLengthErrorHandler - A custom function to be ran after the default error handling\n * @param {HTMLElement} fileInput - An input form field with type of file\n * @param {number} maxFileNameLength - The max number of characters permitted for a file name\n */\nfunction handleFileNameLengthError(\n fileNameLengthErrorHandler,\n fileInput,\n maxFileNameLength,\n) {\n const fileInputField = fileInput;\n fileInputField.value = null;\n\n if (fileNameLengthErrorHandler) {\n fileNameLengthErrorHandler();\n } else {\n const errorMessage = `File name is too long. It can't be longer than ${maxFileNameLength} characters.`;\n addErrorMessage(fileInput, errorMessage);\n }\n}\n\n/**\n * Validates the file size and handles the error if it's invalid.\n *\n * @external File\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/File File}\n *\n * @param {File} file - The file attached by the user\n * @param {string} fileType - The top level file type (i.e. image for image/png)\n * @param {HTMLElement} fileInput - An input form field with type of file\n *\n * @returns {Boolean} Returns false if the file is too big. Otherwise, returns true.\n */\nfunction validateFileSize(file, fileType, fileInput) {\n let { maxFileSizeMb } = fileInput.dataset;\n\n const { fileSizeErrorHandler } = fileInput.dataset;\n\n const fileSizeMb = (file.size / (1024 * 1024)).toFixed(2);\n maxFileSizeMb = Number(maxFileSizeMb || MAX_FILE_SIZE_MB[fileType]);\n\n const isValidFileSize = fileSizeMb <= maxFileSizeMb;\n\n if (!isValidFileSize) {\n handleFileSizeError(\n fileSizeErrorHandler,\n fileInput,\n fileSizeMb,\n maxFileSizeMb,\n );\n }\n\n return isValidFileSize;\n}\n\n/**\n * Validates the file type and handles the error if it's invalid.\n *\n * @external File\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/File File}\n *\n * @param {File} file - The file attached by the user\n * @param {string} fileType - The top level file type (i.e. image for image/png)\n * @param {HTMLElement} fileInput - An input form field with type of file\n *\n * @returns {Boolean} Returns false if the files is an invalid format. Otherwise, returns true.\n */\nfunction validateFileType(file, fileType, fileInput) {\n let { permittedFileTypes } = fileInput.dataset;\n\n if (permittedFileTypes) {\n permittedFileTypes = JSON.parse(permittedFileTypes);\n }\n\n permittedFileTypes = permittedFileTypes || PERMITTED_FILE_TYPES;\n\n const { fileTypeErrorHandler } = fileInput.dataset;\n\n const isValidFileType = permittedFileTypes.includes(fileType);\n\n if (!isValidFileType) {\n handleFileTypeError(\n fileTypeErrorHandler,\n fileInput,\n fileType,\n permittedFileTypes,\n );\n }\n\n return isValidFileType;\n}\n\n/**\n * Validates the length of the file name and handles the error if it's invalid.\n *\n * @external File\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/File File}\n *\n * @param {File} file - The file attached by the user\n * @param {HTMLElement} fileInput - An input form field with type of file\n *\n * @returns {Boolean} Returns false if the file name is too long. Otherwise, returns true.\n */\nfunction validateFileNameLength(file, fileInput) {\n let { maxFileNameLength } = fileInput.dataset;\n\n maxFileNameLength = Number(maxFileNameLength || MAX_FILE_NAME_LENGTH);\n\n const { fileNameLengthErrorHandler } = fileInput.dataset;\n\n const isValidFileNameLength = file.name.length <= maxFileNameLength;\n\n if (!isValidFileNameLength) {\n handleFileNameLengthError(\n fileNameLengthErrorHandler,\n fileInput,\n maxFileNameLength,\n );\n }\n\n return isValidFileNameLength;\n}\n\n/**\n * This is the core function to handle validations of uploaded files. It loops\n * through all the uploaded files for the given fileInput and checks the file\n * size, file format, and file name length. If a file fails a validation, the\n * error is handled.\n *\n * @param {HTMLElement} fileInput - An input form field with type of file\n *\n * @returns {Boolean} Returns false if any files failed validations. Otherwise, returns true.\n */\nfunction validateFileInput(fileInput) {\n let isValidFileInput = true;\n\n removeErrorMessage(fileInput);\n const files = Array.from(fileInput.files);\n\n for (let i = 0; i < files.length; i += 1) {\n const file = files[i];\n const fileType = file.type.split('/')[0];\n\n const isValidFileSize = validateFileSize(file, fileType, fileInput);\n\n if (!isValidFileSize) {\n isValidFileInput = false;\n break;\n }\n\n const isValidFileType = validateFileType(file, fileType, fileInput);\n\n if (!isValidFileType) {\n isValidFileInput = false;\n break;\n }\n\n const isValidFileNameLength = validateFileNameLength(file, fileInput);\n\n if (!isValidFileNameLength) {\n isValidFileInput = false;\n break;\n }\n }\n\n return isValidFileInput;\n}\n\n/**\n * This function is designed to be exported in areas where we are doing more\n * custom implementations of file uploading using Preact. It can then be used\n * in Preact event handlers. It loops through all file input fields on the DOM\n * and validates any attached files.\n *\n * @returns {Boolean} Returns false if any files failed validations. Otherwise, returns true.\n */\nexport function validateFileInputs() {\n let validFileInputs = true;\n const fileInputs = document.querySelectorAll('input[type=\"file\"]');\n\n for (let i = 0; i < fileInputs.length; i += 1) {\n const fileInput = fileInputs[i];\n const validFileInput = validateFileInput(fileInput);\n\n if (!validFileInput) {\n validFileInputs = false;\n break;\n }\n }\n\n return validFileInputs;\n}\n\n// This is written so that it works automagically by just including this pack\n// in a view.\nconst fileInputs = document.querySelectorAll('input[type=\"file\"]');\n\nfileInputs.forEach((fileInput) => {\n fileInput.addEventListener('change', () => {\n validateFileInput(fileInput);\n });\n});\n","/**\n * Checks if an element is visible in the viewport\n *\n * @example\n * const element = document.getElementById('element');\n * isInViewport({element, allowPartialVisibility = true}); // true or false\n *\n * @param {HTMLElement} element - The HTML element to check\n * @param {number} [offsetTop=0] - Part of the screen to ignore counting from the top\n * @param {boolean} [allowPartialVisibility=false] - A boolean to flip the check between partial or completely visible in the viewport\n * @returns {boolean} isInViewport - true if the element is visible in the viewport\n */\nexport function isInViewport({\n element,\n offsetTop = 0,\n allowPartialVisibility = false,\n}) {\n const boundingRect = element.getBoundingClientRect();\n const clientHeight =\n window.innerHeight || document.documentElement.clientHeight;\n const clientWidth = window.innerWidth || document.documentElement.clientWidth;\n const topIsInViewport =\n boundingRect.top <= clientHeight && boundingRect.top >= offsetTop;\n const rightIsInViewport =\n boundingRect.right >= 0 && boundingRect.right <= clientWidth;\n const bottomIsInViewport =\n boundingRect.bottom >= offsetTop && boundingRect.bottom <= clientHeight;\n const leftIsInViewport =\n boundingRect.left <= clientWidth && boundingRect.left >= 0;\n const topIsOutOfViewport = boundingRect.top <= offsetTop;\n const bottomIsOutOfViewport = boundingRect.bottom >= clientHeight;\n const elementSpansEntireViewport =\n topIsOutOfViewport && bottomIsOutOfViewport;\n\n if (allowPartialVisibility) {\n return (\n (topIsInViewport || bottomIsInViewport || elementSpansEntireViewport) &&\n (leftIsInViewport || rightIsInViewport)\n );\n }\n return (\n topIsInViewport &&\n bottomIsInViewport &&\n leftIsInViewport &&\n rightIsInViewport\n );\n}\n","import { useState, useEffect } from 'preact/hooks';\n\n/**\n * Pre-defined breakpoints for width.\n *\n * Note: These were copied from _import.scss.\n */\nexport const BREAKPOINTS = Object.freeze({\n Small: 640,\n Medium: 768,\n Large: 1024,\n});\n\n/**\n * A custom Preact hook for evaluating whether or not a CSS media query is matched or not.\n *\n * @param {string} query The media query to evaluate.\n *\n * @returns {boolean} True if the media query is matched, false otherwise.\n *\n * @example\n * import { useMediaQuery } from '@components/useMediaQuery';\n *\n * function SomeComponent({ query }) {\n * const matchesBreakpoint = useMediaQuery(query);\n *\n * if (!matchesBreakpoint) {\n * return null;\n * }\n *\n * return \n * }\n */\nexport const useMediaQuery = (query) => {\n const mediaQuery = window.matchMedia(query);\n\n const [match, setMatch] = useState(!!mediaQuery.matches);\n\n useEffect(() => {\n const handler = () => {\n setMatch(!!mediaQuery.matches);\n };\n mediaQuery.addListener(handler);\n\n return () => mediaQuery.removeListener(handler);\n });\n\n return match;\n};\n","import PropTypes from 'prop-types';\n\nexport const articleSnippetResultPropTypes = PropTypes.shape({\n body_text: PropTypes.arrayOf(PropTypes.string),\n});\n\nexport const articlePropTypes = PropTypes.shape({\n id: PropTypes.number.isRequired,\n title: PropTypes.string.isRequired,\n path: PropTypes.string.isRequired,\n cloudinary_video_url: PropTypes.string,\n video_duration_in_minutes: PropTypes.string,\n type_of: PropTypes.oneOf(['podcast_episodes']),\n class_name: PropTypes.oneOf(['PodcastEpisode', 'User', 'Article']),\n flare_tag: PropTypes.shape({\n name: PropTypes.string.isRequired,\n bg_color_hex: PropTypes.string,\n text_color_hex: PropTypes.string,\n }),\n tag_list: PropTypes.arrayOf(PropTypes.string),\n cached_tag_list_array: PropTypes.arrayOf(PropTypes.string),\n podcast: PropTypes.shape({\n slug: PropTypes.string.isRequired,\n title: PropTypes.string.isRequired,\n image_url: PropTypes.string.isRequired,\n }),\n user_id: PropTypes.number.isRequired,\n user: PropTypes.shape({\n username: PropTypes.string.isRequired,\n name: PropTypes.string.isRequired,\n }),\n organization: PropTypes.shape({\n name: PropTypes.string.isRequired,\n profile_image_90: PropTypes.string.isRequired,\n slug: PropTypes.string.isRequired,\n }),\n highlight: articleSnippetResultPropTypes,\n public_reactions_count: PropTypes.number,\n reactions_count: PropTypes.number,\n comments_count: PropTypes.number,\n reading_time: PropTypes.number,\n});\n","import { h } from 'preact';\nimport { useState } from 'preact/hooks';\nimport PropTypes from 'prop-types';\nimport { defaultChildrenPropTypes } from '../../common-prop-types';\n\nfunction getAdditionalClassNames({\n variant,\n className,\n contentType,\n size,\n inverted,\n disabled,\n tooltip,\n}) {\n let additionalClassNames = '';\n\n if (variant && variant.length > 0 && variant !== 'primary') {\n additionalClassNames += ` crayons-btn--${variant}`;\n }\n\n if (size && size.length > 0 && size !== 'default') {\n additionalClassNames += ` crayons-btn--${size}`;\n }\n\n if (contentType && contentType.length > 0 && contentType !== 'text') {\n additionalClassNames += ` crayons-btn--${contentType}`;\n }\n\n if (disabled) {\n additionalClassNames += ' crayons-btn--disabled';\n }\n\n if (inverted) {\n additionalClassNames += ' crayons-btn--inverted';\n }\n\n if (className && className.length > 0) {\n additionalClassNames += ` ${className}`;\n }\n\n if (tooltip) {\n additionalClassNames += ` crayons-tooltip__activator`;\n }\n\n return additionalClassNames;\n}\n\nexport const Button = (props) => {\n const {\n children,\n variant = 'primary',\n tagName,\n inverted,\n contentType,\n size,\n className,\n icon,\n url,\n buttonType,\n disabled,\n onClick,\n onMouseOver,\n onMouseOut,\n onFocus,\n onBlur,\n onKeyUp,\n tabIndex,\n title,\n tooltip,\n ...restOfProps\n } = props;\n\n const [suppressTooltip, setSuppressTooltip] = useState(false);\n\n const handleKeyUp = (event) => {\n onKeyUp?.(event);\n if (!tooltip) {\n return;\n }\n setSuppressTooltip(event.key === 'Escape');\n };\n\n const ComponentName = tagName;\n const Icon = icon;\n const otherProps =\n tagName === 'button'\n ? { type: buttonType, disabled }\n : { href: disabled ? undefined : url };\n\n return (\n \n {contentType !== 'text' && contentType !== 'icon-right' && Icon && (\n \n )}\n {(contentType === 'text' ||\n contentType === 'icon-left' ||\n contentType === 'icon-right') &&\n children}\n {contentType !== 'text' && contentType === 'icon-right' && Icon && (\n \n )}\n {tooltip ? (\n \n {tooltip}\n \n ) : null}\n \n );\n};\n\nButton.displayName = 'Button';\n\nButton.defaultProps = {\n className: undefined,\n icon: undefined,\n url: undefined,\n buttonType: 'button',\n disabled: false,\n inverted: false,\n onClick: undefined,\n onMouseOver: undefined,\n onMouseOut: undefined,\n onFocus: undefined,\n onBlur: undefined,\n tabIndex: undefined,\n title: undefined,\n tagName: 'button',\n size: 'default',\n contentType: 'text',\n variant: 'primary',\n};\n\nButton.propTypes = {\n children: defaultChildrenPropTypes,\n variant: PropTypes.oneOf([\n 'primary',\n 'secondary',\n 'outlined',\n 'danger',\n 'ghost',\n 'ghost-brand',\n 'ghost-success',\n 'ghost-warning',\n 'ghost-danger',\n ]),\n contentType: PropTypes.oneOf([\n 'text',\n 'icon-left',\n 'icon-right',\n 'icon',\n 'icon-rounded',\n ]).isRequired,\n inverted: PropTypes.bool,\n tagName: PropTypes.oneOf(['a', 'button']).isRequired,\n className: PropTypes.string,\n icon: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),\n url: PropTypes.string,\n buttonType: PropTypes.string,\n disabled: PropTypes.bool,\n size: PropTypes.oneOf(['default', 's', 'l', 'xl']).isRequired,\n onClick: PropTypes.func,\n onMouseOver: PropTypes.func,\n onMouseOut: PropTypes.func,\n onFocus: PropTypes.func,\n onBlur: PropTypes.func,\n tabIndex: PropTypes.number,\n title: PropTypes.string,\n tooltip: PropTypes.node,\n};\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\nimport { defaultChildrenPropTypes } from '../common-prop-types';\nimport { Button } from '@crayons';\n\nexport const snackbarItemProps = {\n children: defaultChildrenPropTypes.isRequired,\n actions: PropTypes.arrayOf(\n PropTypes.shape({\n message: PropTypes.string.isRequired,\n handler: PropTypes.func.isRequired,\n lifespan: PropTypes.number.isRequired,\n }),\n ),\n};\n\nexport const SnackbarItem = ({ message, actions = [] }) => (\n
\n
\n {message}\n
\n
\n {actions.map(({ text, handler }) => (\n \n ))}\n
\n
\n);\n\nSnackbarItem.displayName = 'SnackbarItem';\n\nSnackbarItem.propTypes = snackbarItemProps.isRequired;\n","import PropTypes from 'prop-types';\nimport { h, Fragment } from 'preact';\nimport { useLayoutEffect, useRef, useCallback } from 'preact/hooks';\nimport { createFocusTrap } from 'focus-trap';\nimport { defaultChildrenPropTypes } from '../../common-prop-types';\nimport { KeyboardShortcuts } from './useKeyboardShortcuts';\n\n/**\n * Wrapper component to create a focus trap within the HTML element found by the given selector\n *\n * @example\n * import { FocusTrap } from \"shared/components/FocusTrap\";\n *\n * const ExampleComponent = ({ onClose }) => (\n * \n *
\n * \n *
\n *
\n * )\n *\n * @param {string} selector The CSS selector for the element where focus is to be trapped\n * @param {Array} children Child element(s) passed via composition\n * @param {Function} onDeactivate Callback function to be called when the focus trap is deactivated by navigation or Escape press\n * @param {boolean} clickOutsideDeactivates If true, the focus trap will deactivate when a user clicks outside of the trap area\n */\nexport const FocusTrap = ({\n selector,\n children,\n onDeactivate,\n clickOutsideDeactivates = false,\n}) => {\n const focusTrap = useRef(null);\n const deactivate = useCallback(() => onDeactivate(), [onDeactivate]);\n\n useLayoutEffect(() => {\n const currentLocationHref = document.location.href;\n const routeChangeObserver = new MutationObserver((mutations) => {\n const hasRouteChanged = mutations.some(\n () => currentLocationHref !== document.location.href,\n );\n\n // Ensure trap deactivates if user navigates from the page\n if (hasRouteChanged) {\n focusTrap.current?.deactivate();\n routeChangeObserver.disconnect();\n }\n });\n\n focusTrap.current = createFocusTrap(selector, {\n escapeDeactivates: false,\n clickOutsideDeactivates,\n onDeactivate: deactivate,\n });\n\n focusTrap.current.activate();\n routeChangeObserver.observe(document.querySelector('body'), {\n childList: true,\n });\n\n return () => {\n focusTrap.current.deactivate();\n routeChangeObserver.disconnect();\n };\n }, [clickOutsideDeactivates, selector, deactivate]);\n\n const shortcuts = {\n escape: onDeactivate,\n };\n\n return (\n \n {children}\n \n \n );\n};\n\nFocusTrap.defaultProps = {\n selector: '.crayons-modal',\n onDeactivate: () => {},\n};\n\nFocusTrap.propTypes = {\n selector: PropTypes.string,\n children: defaultChildrenPropTypes.isRequired,\n onDeactivate: PropTypes.func,\n};\n","import { isInViewport } from '@utilities/viewport';\nimport { debounceAction } from '@utilities/debounceAction';\n\n/**\n * Helper function designed to be used on scroll to detect when dropdowns should switch from dropping downwards/upwards.\n * The action is debounced since scroll events are usually fired several at a time.\n *\n * @returns {Function} a debounced function that handles the repositioning of dropdowns\n * @example\n *\n * document.addEventListener('scroll', getDropdownRepositionListener());\n */\nexport const getDropdownRepositionListener = () =>\n debounceAction(handleDropdownRepositions);\n\n/**\n * Checks for all dropdowns on the page which have the attribute 'data-repositioning-dropdown', signalling\n * they should dynamically change between dropping downwards or upwards, depending on viewport position.\n *\n * Any dropdowns not fully in view when dropping down will be switched to dropping upwards.\n */\nconst handleDropdownRepositions = () => {\n // Select all of the dropdowns which should reposition\n const allRepositioningDropdowns = document.querySelectorAll(\n '[data-repositioning-dropdown]',\n );\n\n for (const element of allRepositioningDropdowns) {\n // Default to dropping downwards\n element.classList.remove('reverse');\n\n const isDropdownCurrentlyOpen = element.style.display === 'block';\n\n if (!isDropdownCurrentlyOpen) {\n // We can't determine position on an element with display:none, so we \"show\" the dropdown with 0 opacity very temporarily\n element.style.opacity = 0;\n element.style.display = 'block';\n }\n\n if (!isInViewport({ element })) {\n // If the element isn't fully visible when dropping down, reverse the direction\n element.classList.add('reverse');\n }\n\n if (!isDropdownCurrentlyOpen) {\n // Revert the temporary changes to determine position\n element.style.removeProperty('display');\n element.style.removeProperty('opacity');\n }\n }\n};\n\n/**\n * Helper query string to identify interactive/focusable HTML elements\n */\nconst INTERACTIVE_ELEMENTS_QUERY =\n 'button, [href], input:not([type=\"hidden\"]), select, textarea, [tabindex=\"0\"]';\n\n/**\n * Open the given dropdown, updating aria attributes, and focusing the first interactive element\n *\n * @param {Object} args\n * @param {string} args.triggerElementId The id of the button which activates the dropdown\n * @param {string} args.dropdownContent The id of the dropdown content element\n */\nconst openDropdown = ({ triggerElementId, dropdownContentId }) => {\n const dropdownContent = document.getElementById(dropdownContentId);\n const triggerElement = document.getElementById(triggerElementId);\n\n triggerElement.setAttribute('aria-expanded', 'true');\n\n // Style set inline to prevent specificity issues\n dropdownContent.style.display = 'block';\n\n // Send focus to the first suitable element\n dropdownContent.querySelector(INTERACTIVE_ELEMENTS_QUERY)?.focus();\n};\n\n/**\n * Close the given dropdown, updating aria attributes\n *\n * @param {Object} args\n * @param {string} args.triggerElementId The id of the button which activates the dropdown\n * @param {string} args.dropdownContent The id of the dropdown content element\n * @param {Function} args.onClose Optional function for any side-effects which should occur on dropdown close\n */\nconst closeDropdown = ({ triggerElementId, dropdownContentId, onClose }) => {\n const dropdownContent = document.getElementById(dropdownContentId);\n\n if (!dropdownContent) {\n // Component may have unmounted\n return;\n }\n\n document\n .getElementById(triggerElementId)\n ?.setAttribute('aria-expanded', 'false');\n\n // Remove the inline style added when we opened the dropdown\n dropdownContent.style.removeProperty('display');\n\n onClose?.();\n};\n\n/**\n * A helper function to initialize dropdown behaviors. This function attaches open/close click and keyup listeners,\n * and makes sure relevant aria properties and keyboard focus are updated.\n *\n * @param {Object} args\n * @param {string} args.triggerButtonElementId The ID of the button which triggers the dropdown open/close behavior\n * @param {string} args.dropdownContentId The ID of the dropdown content which should open/close on trigger button press\n * @param {string} args.dropdownContentCloseButtonId Optional ID of any button within the dropdown content which should close the dropdown\n * @param {Function} args.onClose An optional callback for when the dropdown is closed. This can be passed to execute any side-effects required when the dropdown closes.\n * @param {Function} args.onOpen An optional callback for when the dropdown is opened. This can be passed to execute any side-effects required when the dropdown opens.\n *\n * @returns {{closeDropdown: Function}} Object with callback to close the initialized dropdown\n */\nexport const initializeDropdown = ({\n triggerElementId,\n dropdownContentId,\n dropdownContentCloseButtonId,\n onClose,\n onOpen,\n}) => {\n const triggerButton = document.getElementById(triggerElementId);\n const dropdownContent = document.getElementById(dropdownContentId);\n\n if (!triggerButton || !dropdownContent) {\n // The required props haven't been provided, do nothing\n return;\n }\n\n // Ensure default values have been applied\n triggerButton.setAttribute('aria-expanded', 'false');\n triggerButton.setAttribute('aria-controls', dropdownContentId);\n triggerButton.setAttribute('aria-haspopup', 'true');\n\n const keyUpListener = ({ key }) => {\n if (key === 'Escape') {\n // Close the dropdown and return focus to the trigger button to prevent focus being lost\n const isCurrentlyOpen =\n triggerButton.getAttribute('aria-expanded') === 'true';\n if (isCurrentlyOpen) {\n closeDropdown({\n triggerElementId,\n dropdownContentId,\n onClose: onCloseCleanupActions,\n });\n triggerButton.focus();\n }\n } else if (key === 'Tab') {\n // Close the dropdown if the user has tabbed away from it\n const isInsideDropdown = dropdownContent?.contains(\n document.activeElement,\n );\n if (!isInsideDropdown) {\n closeDropdown({\n triggerElementId,\n dropdownContentId,\n onClose: onCloseCleanupActions,\n });\n }\n }\n };\n\n // Close the dropdown if user has clicked outside\n const clickOutsideListener = ({ target }) => {\n if (\n target !== triggerButton &&\n !dropdownContent.contains(target) &&\n !triggerButton.contains(target)\n ) {\n closeDropdown({\n triggerElementId,\n dropdownContentId,\n onClose: onCloseCleanupActions,\n });\n\n // If the user did not click on another interactive item, return focus to the trigger\n if (!target.matches(INTERACTIVE_ELEMENTS_QUERY)) {\n triggerButton.focus();\n }\n }\n };\n\n // Any necessary side effects required on dropdown close\n const onCloseCleanupActions = () => {\n onClose?.();\n document.removeEventListener('keyup', keyUpListener);\n document.removeEventListener('click', clickOutsideListener);\n };\n\n // Add the main trigger button toggle functionality\n triggerButton.addEventListener('click', () => {\n if (\n document\n .getElementById(triggerElementId)\n ?.getAttribute('aria-expanded') === 'true'\n ) {\n closeDropdown({\n triggerElementId,\n dropdownContentId,\n onClose: onCloseCleanupActions,\n });\n } else {\n openDropdown({\n triggerElementId,\n dropdownContentId,\n });\n onOpen?.();\n\n document.addEventListener('keyup', keyUpListener);\n document.addEventListener('click', clickOutsideListener);\n }\n });\n\n if (dropdownContentCloseButtonId) {\n // The dropdown content has a 'close' button inside that we also need to handle\n document\n .getElementById(dropdownContentCloseButtonId)\n ?.addEventListener('click', () => {\n closeDropdown({\n triggerElementId,\n dropdownContentId,\n onClose: onCloseCleanupActions,\n });\n\n document.getElementById(triggerElementId)?.focus();\n });\n }\n\n return {\n closeDropdown: () => {\n closeDropdown({\n triggerElementId,\n dropdownContentId,\n onClose: onCloseCleanupActions,\n });\n },\n };\n};\n","import debounce from 'lodash.debounce';\n\n/**\n * A util function to wrap any action with lodash's `debounce` (https://lodash.com/docs/#debounce).\n * To use this util, wrap it in the util like so: debounceAction(onSearchBoxType.bind(this));\n *\n * By default, this util uses a default time of 300ms, and includes a default config of `{ leading: false }`.\n * These values can be overridden: debounceAction(this.onSearchBoxType.bind(this), { time: 100, config: { leading: true }});\n *\n *\n * @param {Function} action - The function that should be wrapped with `debounce`.\n * @param {Number} [time=300] - The number of milliseconds to wait.\n * @param {Object} [config={ leading: false }] - Any configuration for the debounce function.\n *\n * @returns {Function} A function wrapped in `debounce`.\n */\nexport function debounceAction(\n action,\n { time = 300, config = { leading: false } } = {},\n) {\n const configs = { ...config };\n return debounce(action, time, configs);\n}\n","import { validateFileInputs } from '../packs/validateFileInputs';\n\nexport function previewArticle(payload, successCb, failureCb) {\n fetch('/articles/preview', {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'X-CSRF-Token': window.csrfToken,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n article_body: payload,\n }),\n credentials: 'same-origin',\n })\n .then(async (response) => {\n const payload = await response.json();\n\n if (response.status !== 200) {\n throw payload;\n }\n\n return payload;\n })\n .then(successCb)\n .catch(failureCb);\n}\n\nexport function getArticle() {}\n\nfunction processPayload(payload) {\n const {\n /* eslint-disable no-unused-vars */\n previewShowing,\n helpShowing,\n previewResponse,\n helpHTML,\n imageManagementShowing,\n moreConfigShowing,\n errors,\n /* eslint-enable no-unused-vars */\n ...neededPayload\n } = payload;\n return neededPayload;\n}\n\nexport function submitArticle({ payload, onSuccess, onError }) {\n const method = payload.id ? 'PUT' : 'POST';\n const url = payload.id ? `/articles/${payload.id}` : '/articles';\n fetch(url, {\n method,\n headers: {\n Accept: 'application/json',\n 'X-CSRF-Token': window.csrfToken,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n article: processPayload(payload),\n }),\n credentials: 'same-origin',\n })\n .then((response) => response.json())\n .then((response) => {\n if (response.current_state_path) {\n onSuccess();\n window.location.replace(response.current_state_path);\n } else {\n onError(response);\n }\n })\n .catch(onError);\n}\n\nfunction generateUploadFormdata(payload) {\n const token = window.csrfToken;\n const formData = new FormData();\n formData.append('authenticity_token', token);\n\n Object.entries(payload.image).forEach(([_, value]) =>\n formData.append('image[]', value),\n );\n\n return formData;\n}\n\nexport function generateMainImage({ payload, successCb, failureCb, signal }) {\n fetch('/image_uploads', {\n method: 'POST',\n headers: {\n 'X-CSRF-Token': window.csrfToken,\n },\n body: generateUploadFormdata(payload),\n credentials: 'same-origin',\n signal,\n })\n .then((response) => response.json())\n .then((json) => {\n if (json.error) {\n throw new Error(json.error);\n }\n const { links } = json;\n const { image } = payload;\n return successCb({ links, image });\n })\n .catch(failureCb);\n}\n\n/**\n * Processes images for upload.\n *\n * @param {FileList} images Images to be uploaded.\n * @param {Function} handleImageSuccess The handler that runs when the image is uploaded successfully.\n * @param {Function} handleImageFailure The handler that runs when the image upload fails.\n */\nexport function processImageUpload(\n images,\n handleImageSuccess,\n handleImageFailure,\n) {\n // Currently only one image is supported for upload.\n if (images.length > 0 && validateFileInputs()) {\n const payload = { image: images };\n\n generateMainImage({\n payload,\n successCb: handleImageSuccess,\n failureCb: handleImageFailure,\n });\n }\n}\n","import { h } from 'preact';\nimport PropTypes from 'prop-types';\nimport { FocusTrap } from '../../shared/components/focusTrap';\nimport { defaultChildrenPropTypes } from '../../common-prop-types';\nimport { Button } from '@crayons';\n\nfunction getAdditionalClassNames({ size, className }) {\n let additionalClassNames = '';\n\n if (size && size.length > 0 && size !== 'default') {\n additionalClassNames += ` crayons-modal--${size}`;\n }\n\n if (className && className.length > 0) {\n additionalClassNames += ` ${className}`;\n }\n\n return additionalClassNames;\n}\n\nconst CloseIcon = () => (\n \n Close\n \n \n);\n\n/**\n * A modal component which can be presented with or without an overlay.\n * The modal is presented within a focus trap for accessibility purposes - please note that the selector used for the focusTrap must be unique on the given page, otherwise focus may be trapped on the wrong element.\n *\n * @param {Object} props\n * @param {Array} props.children The content to be displayed inside the Modal. Can be provided by composition (see example).\n * @param {string} props.size The desired modal size ('s', 'm' or 'default')\n * @param {string} props.className Optional additional classnames to apply to the modal container\n * @param {string} props.title The title to be displayed in the modal heading. If provided, a title bar with a close button will be displayed.\n * @param {boolean} props.overlay Whether or not to show a semi-opaque overlay behind the modal\n * @param {Function} props.onClose Callback for any function to be executed on close button click or Escape\n * @param {boolean} props.closeOnClickOutside Whether the modal should close if the user clicks outside of it\n * @param {string} props.focusTrapSelector The CSS selector for where to trap the user's focus. This should be unique to the page in which the modal is presented.\n *\n * @example\n * \n
\n

Some modal content

\n
\n \n */\nexport const Modal = ({\n children,\n size,\n className,\n title,\n overlay = true,\n onClose = () => {},\n closeOnClickOutside = false,\n focusTrapSelector = '.crayons-modal',\n}) => {\n return (\n \n \n \n {title && (\n
\n

{title}

\n \n
\n )}\n
{children}
\n \n {overlay && (\n \n )}\n \n \n );\n};\n\nModal.displayName = 'Modal';\n\nModal.defaultProps = {\n size: 'default',\n};\n\nModal.propTypes = {\n children: defaultChildrenPropTypes.isRequired,\n className: PropTypes.string,\n title: PropTypes.string.isRequired,\n overlay: PropTypes.bool,\n onClose: PropTypes.func,\n size: PropTypes.oneOf(['default', 's', 'm']).isRequired,\n focusTrapSelector: PropTypes.string,\n};\n","import { h } from 'preact';\n\nexport const Spinner = () => (\n \n \n \n);\n","import { h, Component } from 'preact';\nimport PropTypes from 'prop-types';\nimport { SnackbarItem } from './SnackbarItem';\n\nlet snackbarItems = [];\n\nexport function addSnackbarItem(snackbarItem) {\n if (!Array.isArray(snackbarItem.actions)) {\n snackbarItem.actions = []; // eslint-disable-line no-param-reassign\n }\n\n snackbarItems.push(snackbarItem);\n}\n\nexport class Snackbar extends Component {\n state = {\n snacks: [],\n };\n\n pollingId;\n\n paused = false;\n\n pauseLifespan;\n\n resumeLifespan;\n\n componentDidMount() {\n this.initializePolling();\n }\n\n componentDidUpdate() {\n if (!this.pauseLifespan) {\n this.pauseLifespan = (_event) => {\n this.paused = true;\n };\n\n this.resumeLifespan = (event) => {\n event.stopPropagation();\n this.paused = false;\n };\n\n this.element.addEventListener('mouseover', this.pauseLifespan);\n this.element.addEventListener('mouseout', this.resumeLifespan, true);\n }\n }\n\n componentWillUnmount() {\n if (this.element) {\n this.element.removeEventListener('mouseover', this.pauseLifespan);\n this.element.addEventListener('mouseout', this.resumeLifespan);\n }\n }\n\n initializePolling() {\n const { pollingTime, lifespan } = this.props;\n\n this.pollingId = setInterval(() => {\n if (snackbarItems.length > 0) {\n // Need to add the lifespan to snackbar items because each second that goes by, we\n // decrease the lifespan until it is no more.\n const newSnacks = snackbarItems.map((snackbarItem) => ({\n ...snackbarItem,\n lifespan,\n }));\n\n snackbarItems = [];\n\n this.updateSnackbarItems(newSnacks);\n\n // Start the lifespan countdowns for each new snackbar item.\n newSnacks.forEach((snack) => {\n // eslint-disable-next-line no-param-reassign\n snack.lifespanTimeoutId = setTimeout(() => {\n this.decreaseLifespan(snack);\n }, 1000);\n\n if (snack.addCloseButton) {\n // Adds an optional close button if addDefaultACtion is true.\n snack.actions.push({\n text: 'Dismiss',\n handler: () => {\n this.setState((prevState) => {\n return {\n prevState,\n snacks: prevState.snacks.filter(\n (potentialSnackToFilterOut) =>\n potentialSnackToFilterOut !== snack,\n ),\n };\n });\n },\n });\n }\n });\n }\n }, pollingTime);\n }\n\n updateSnackbarItems(newSnacks) {\n this.setState((prevState) => {\n let updatedSnacks = [...prevState.snacks, ...newSnacks];\n\n if (updatedSnacks.length > 3) {\n const snacksToBeDiscarded = updatedSnacks.slice(\n 0,\n updatedSnacks.length - 3,\n );\n\n snacksToBeDiscarded.forEach(({ lifespanTimeoutId }) => {\n clearTimeout(lifespanTimeoutId);\n });\n\n updatedSnacks = updatedSnacks.slice(updatedSnacks.length - 3);\n }\n\n return { ...prevState, snacks: updatedSnacks };\n });\n }\n\n decreaseLifespan(snack) {\n /* eslint-disable no-param-reassign */\n if (!this.paused && snack.lifespan === 0) {\n clearTimeout(snack.lifespanTimeoutId);\n\n this.setState((prevState) => {\n const snacks = prevState.snacks.filter(\n (currentSnack) => currentSnack !== snack,\n );\n\n return {\n ...prevState,\n snacks,\n };\n });\n\n return;\n }\n\n if (!this.paused) {\n snack.lifespan -= 1;\n }\n\n snack.lifespanTimeoutId = setTimeout(() => {\n this.decreaseLifespan(snack);\n }, 1000);\n /* eslint-enable no-param-reassign */\n }\n\n render() {\n const { snacks } = this.state;\n\n return (\n 0 ? 'crayons-snackbar' : 'hidden'}\n ref={(element) => {\n this.element = element;\n }}\n >\n {snacks.map(({ message, actions = [] }, index) => (\n \n ))}\n \n );\n }\n}\n\nSnackbar.defaultProps = {\n lifespan: 5,\n pollingTime: 300,\n};\n\nSnackbar.displayName = 'Snackbar';\n\nSnackbar.propTypes = {\n lifespan: PropTypes.number,\n pollingTime: PropTypes.number,\n};\n","import { h } from 'preact';\n\nexport const Bold = () => (\n \n \n \n);\n\nexport const Italic = () => (\n \n \n \n);\n\nexport const Link = () => (\n \n \n \n);\n\nexport const OrderedList = () => (\n \n \n \n);\n\nexport const UnorderedList = () => (\n \n \n \n);\n\nexport const Heading = () => (\n \n \n \n);\n\nexport const Quote = () => (\n \n \n \n);\n\nexport const Code = () => (\n \n \n \n);\n\nexport const CodeBlock = () => (\n \n \n \n);\n\nexport const Overflow = () => (\n \n \n \n);\n\nexport const Underline = () => (\n \n \n \n);\n\nexport const Strikethrough = () => (\n \n \n \n);\n\nexport const Divider = () => (\n \n \n \n \n \n \n \n \n \n \n \n \n);\n\nexport const Help = () => (\n \n \n \n);\n","/* global Runtime */\nimport {\n getLastIndexOfCharacter,\n getNextIndexOfCharacter,\n getNumberOfNewLinesFollowingSelection,\n getNumberOfNewLinesPrecedingSelection,\n getSelectionData,\n} from '../../utilities/textAreaUtils';\nimport {\n Bold,\n Italic,\n Link,\n OrderedList,\n UnorderedList,\n Heading,\n Quote,\n Code,\n CodeBlock,\n Underline,\n Strikethrough,\n Divider,\n} from './icons';\n\nconst ORDERED_LIST_ITEM_REGEX = /^\\d+\\.\\s+.*/;\nconst MARKDOWN_LINK_REGEX =\n /^\\[([\\w\\s\\d]*)\\]\\((url|(https?:\\/\\/[\\w\\d./?=#]+))\\)$/;\nconst URL_PLACEHOLDER_TEXT = 'url';\n\nconst NUMBER_OF_NEW_LINES_BEFORE_BLOCK_SYNTAX = 2;\nconst NUMBER_OF_NEW_LINES_BEFORE_AFTER_SYNTAX = 1;\n\nconst getNewLinePrefixSuffixes = ({ selectionStart, selectionEnd, value }) => {\n const numberOfNewLinesBeforeSelection = getNumberOfNewLinesPrecedingSelection(\n { selectionStart, value },\n );\n const numberOfNewLinesFollowingSelection =\n getNumberOfNewLinesFollowingSelection({ selectionEnd, value });\n\n // We only add new lines if we're not at the beginning of the text area\n const numberOfNewLinesNeededAtStart =\n selectionStart === 0\n ? 0\n : NUMBER_OF_NEW_LINES_BEFORE_BLOCK_SYNTAX -\n numberOfNewLinesBeforeSelection;\n\n const newLinesPrefix = String.prototype.padStart(\n numberOfNewLinesNeededAtStart,\n '\\n',\n );\n\n const newLinesSuffix =\n numberOfNewLinesFollowingSelection >=\n NUMBER_OF_NEW_LINES_BEFORE_AFTER_SYNTAX\n ? ''\n : '\\n';\n\n return { newLinesPrefix, newLinesSuffix };\n};\n\nconst handleLinkFormattingForEmptyTextSelection = ({\n textBeforeSelection,\n textAfterSelection,\n value,\n selectionStart,\n selectionEnd,\n}) => {\n const basicFormattingForEmptySelection = {\n editSelectionStart: selectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: `[](${URL_PLACEHOLDER_TEXT})`,\n newCursorStart: selectionStart + 3,\n newCursorEnd: selectionEnd + 6,\n };\n\n // Directly after inserting a link with a URL highlighted, cursor is inside the link description '[]'\n // Check if we are inside empty link description remove the link syntax if so\n const directlySurroundedByLinkStructure =\n textBeforeSelection.slice(-1) === '[' &&\n textAfterSelection.slice(0, 2) === '](';\n\n if (!directlySurroundedByLinkStructure)\n return basicFormattingForEmptySelection;\n\n // Search for the closing bracket of markdown link\n const indexOfLinkStructureEnd = getNextIndexOfCharacter({\n content: value,\n selectionIndex: selectionStart,\n character: ')',\n breakOnCharacters: [' ', '\\n'],\n });\n\n if (indexOfLinkStructureEnd === -1) return basicFormattingForEmptySelection;\n\n // Remove the markdown link structure, preserving the link text if it isn't the \"url\" placeholder\n const urlText = value.slice(selectionEnd + 2, indexOfLinkStructureEnd);\n\n return {\n editSelectionStart: selectionStart - 1,\n editSelectionEnd: indexOfLinkStructureEnd + 1,\n replaceSelectionWith: urlText === URL_PLACEHOLDER_TEXT ? '' : urlText,\n newCursorStart: selectionStart - 1,\n newCursorEnd: selectionEnd - 1,\n };\n};\n\nconst handleLinkFormattingForUrlSelection = ({\n textBeforeSelection,\n textAfterSelection,\n value,\n selectionStart,\n selectionEnd,\n selectedText,\n}) => {\n const basicFormattingForLinkSelection = {\n editSelectionStart: selectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: `[](${selectedText})`,\n newCursorStart: selectionStart + 1,\n newCursorEnd: selectionStart + 1,\n };\n\n // Check if the text selection is likely inside a currently formatted markdown link\n const directlySurroundedByLinkStructure =\n textBeforeSelection.slice(-2) === '](' &&\n textAfterSelection.slice(0, 1) === ')';\n\n if (!directlySurroundedByLinkStructure)\n return basicFormattingForLinkSelection;\n\n // Get the index of where the current link opens so we can get the text inside the square brackets\n const indexOfSyntaxOpen = getLastIndexOfCharacter({\n content: value,\n selectionIndex: selectionStart,\n character: '[',\n });\n\n // If link syntax is incomplete, format the selection as a link\n if (indexOfSyntaxOpen === -1) return basicFormattingForLinkSelection;\n\n // Replace the markdown with the link text in square brackets, if available\n let textToReplaceMarkdown = textBeforeSelection.slice(\n indexOfSyntaxOpen + 1,\n -2,\n );\n\n // If not available, take the URL as long as it's not the placeholder 'url' text\n if (textToReplaceMarkdown === '') {\n textToReplaceMarkdown =\n selectedText === URL_PLACEHOLDER_TEXT ? '' : selectedText;\n }\n\n return {\n editSelectionStart: indexOfSyntaxOpen,\n editSelectionEnd: selectionEnd + 1,\n replaceSelectionWith: textToReplaceMarkdown,\n newCursorStart: indexOfSyntaxOpen,\n newCursorEnd: indexOfSyntaxOpen + textToReplaceMarkdown.length,\n };\n};\n\nconst handleUndoMarkdownLinkSelection = ({\n selectedText,\n selectionStart,\n selectionEnd,\n}) => {\n const linkDescriptionEnd = getNextIndexOfCharacter({\n content: selectedText,\n selectionIndex: 0,\n character: ']',\n });\n\n let textToReplaceMarkdown = selectedText.slice(1, linkDescriptionEnd);\n\n // Keep the URL instead if no link description exists\n if (textToReplaceMarkdown === '') {\n const linkText = selectedText.slice(linkDescriptionEnd + 2, -1);\n textToReplaceMarkdown = linkText === URL_PLACEHOLDER_TEXT ? '' : linkText;\n }\n\n return {\n editSelectionStart: selectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: textToReplaceMarkdown,\n newCursorStart: selectionStart,\n newCursorEnd: selectionStart + textToReplaceMarkdown.length,\n };\n};\n\nconst isStringStartAUrl = (string) => {\n const startingText = string.substring(0, 8);\n return startingText === 'https://' || startingText.startsWith('http://');\n};\n\nconst undoOrAddFormattingForInlineSyntax = ({\n value,\n selectionStart,\n selectionEnd,\n prefix,\n suffix,\n}) => {\n const { length: prefixLength } = prefix;\n const { length: suffixLength } = suffix;\n const { selectedText, textBeforeSelection, textAfterSelection } =\n getSelectionData({ selectionStart, selectionEnd, value });\n\n // Check if selected text has prefix/suffix\n const selectedTextAlreadyFormatted =\n selectedText.slice(0, prefixLength) === prefix &&\n selectedText.slice(-1 * suffixLength) === suffix;\n\n if (selectedTextAlreadyFormatted) {\n return {\n editSelectionStart: selectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: selectedText.slice(prefixLength, -1 * suffixLength),\n newCursorStart: selectionStart,\n newCursorEnd: selectionEnd - (prefixLength + suffixLength),\n };\n }\n\n // Check if immediate surrounding content has prefix/suffix\n const surroundingTextHasFormatting =\n textBeforeSelection.substring(textBeforeSelection.length - prefixLength) ===\n prefix && textAfterSelection.substring(0, suffixLength) === suffix;\n\n if (surroundingTextHasFormatting) {\n return {\n editSelectionStart: selectionStart - prefixLength,\n editSelectionEnd: selectionEnd + suffixLength,\n replaceSelectionWith: selectedText,\n newCursorStart: selectionStart - prefixLength,\n newCursorEnd: selectionEnd - prefixLength,\n };\n }\n\n // No formatting to undo - format the selected text\n return {\n editSelectionStart: selectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: `${prefix}${selectedText}${suffix}`,\n newCursorStart: selectionStart + prefixLength,\n newCursorEnd: selectionEnd + prefixLength,\n };\n};\n\nconst undoOrAddFormattingForMultilineSyntax = ({\n selectionStart,\n selectionEnd,\n value,\n linePrefix,\n blockPrefix,\n blockSuffix,\n}) => {\n const { selectedText, textBeforeSelection, textAfterSelection } =\n getSelectionData({ selectionStart, selectionEnd, value });\n\n let formattedText = selectedText;\n\n if (linePrefix) {\n const { length: prefixLength } = linePrefix;\n\n // If no selection, check if we're in a freshly inserted syntax\n if (selectedText === '') {\n const lastNewLine =\n textBeforeSelection === ''\n ? -1\n : getLastIndexOfCharacter({\n content: value,\n selectionIndex: selectionStart - 1,\n character: '\\n',\n });\n\n const lineStart = lastNewLine === -1 ? 0 : lastNewLine + 1;\n\n if (\n textBeforeSelection.slice(lineStart, lineStart + prefixLength) ===\n linePrefix\n ) {\n // Remove the list formatting\n\n return {\n editSelectionStart: lineStart,\n editSelectionEnd: lineStart + prefixLength,\n replaceSelectionWith: '',\n newCursorStart: selectionStart - prefixLength,\n newCursorEnd: selectionEnd - prefixLength,\n };\n }\n }\n\n // Split by new lines and check each line has formatting\n const splitByNewLine = selectedText\n .split('\\n')\n .filter((line) => line !== '');\n\n const isAlreadyFormatted =\n splitByNewLine.length > 0 &&\n splitByNewLine.every(\n (line) => line.slice(0, prefixLength) === linePrefix,\n );\n\n if (isAlreadyFormatted) {\n // Remove the formatting\n const unformattedText = splitByNewLine\n .map((line) => line.slice(prefixLength))\n .join('\\n');\n\n return {\n editSelectionStart: selectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: unformattedText,\n newCursorStart: selectionStart,\n newCursorEnd:\n selectionEnd + (unformattedText.length - selectedText.length),\n };\n }\n\n // Otherwise add the prefix to each line to create the new formatted text\n formattedText =\n selectedText === ''\n ? linePrefix\n : splitByNewLine.map((line) => `${linePrefix}${line}`).join('\\n');\n } else {\n // Uses only block prefix and suffix\n const { length: prefixLength } = blockPrefix;\n const { length: suffixLength } = blockSuffix;\n\n // does the selection start and end with the prefix/suffix\n const selectionIsFormatted =\n selectedText.slice(0, prefixLength) === blockPrefix &&\n selectedText.slice(-1 * suffixLength) === blockSuffix;\n\n if (selectionIsFormatted) {\n return {\n editSelectionStart: selectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: selectedText.slice(\n prefixLength,\n -1 * suffixLength,\n ),\n newCursorStart: selectionStart,\n newCursorEnd: selectionEnd - prefixLength - suffixLength,\n };\n }\n\n // or does the prefix/suffix plus new line chars immediately precede and follow the selection\n const surroundingTextIsFormatted =\n textBeforeSelection.slice(-1 * prefixLength) === blockPrefix &&\n textAfterSelection.slice(0, suffixLength) === blockSuffix;\n\n if (surroundingTextIsFormatted) {\n return {\n editSelectionStart: selectionStart - prefixLength,\n editSelectionEnd: selectionEnd + suffixLength,\n replaceSelectionWith: selectedText,\n newCursorStart: selectionStart - prefixLength,\n newCursorEnd: selectionEnd - prefixLength,\n };\n }\n }\n\n // Add the formatting\n\n const { newLinesPrefix, newLinesSuffix } = getNewLinePrefixSuffixes({\n selectionStart,\n selectionEnd,\n value,\n });\n const { length: newLinePrefixLength } = newLinesPrefix;\n\n const cursorStartBaseline = selectionStart + newLinePrefixLength;\n const cursorStartBlockPrefixOffset = blockPrefix ? blockPrefix.length : 0;\n const cursorStartLinePrefixOffset =\n selectedText === '' && linePrefix ? linePrefix.length : 0;\n\n return {\n editSelectionStart: selectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: `${newLinesPrefix}${\n blockPrefix ? blockPrefix : ''\n }${formattedText}${blockSuffix ? blockSuffix : ''}${newLinesSuffix}`,\n newCursorStart:\n cursorStartBaseline +\n cursorStartBlockPrefixOffset +\n cursorStartLinePrefixOffset,\n newCursorEnd:\n selectionEnd +\n formattedText.length -\n selectedText.length +\n newLinePrefixLength +\n (blockPrefix?.length || 0),\n };\n};\n\nexport const getNewTextAreaValueWithEdits = ({\n textAreaValue,\n editSelectionStart,\n editSelectionEnd,\n replaceSelectionWith,\n}) =>\n `${textAreaValue.substring(\n 0,\n editSelectionStart,\n )}${replaceSelectionWith}${textAreaValue.substring(editSelectionEnd)}`;\n\nexport const coreSyntaxFormatters = {\n bold: {\n icon: Bold,\n label: 'Bold',\n getKeyboardShortcut: () => {\n const modifier = Runtime.getOSKeyboardModifierKeyString();\n return {\n command: `${modifier}+b`,\n tooltipHint: `${modifier.toUpperCase()} + B`,\n };\n },\n getFormatting: ({ selectionStart, selectionEnd, value }) => {\n return undoOrAddFormattingForInlineSyntax({\n selectionStart,\n selectionEnd,\n value,\n prefix: '**',\n suffix: '**',\n });\n },\n },\n italic: {\n icon: Italic,\n label: 'Italic',\n getKeyboardShortcut: () => {\n const modifier = Runtime.getOSKeyboardModifierKeyString();\n return {\n command: `${modifier}+i`,\n tooltipHint: `${modifier.toUpperCase()} + I`,\n };\n },\n getFormatting: ({ selectionStart, selectionEnd, value }) => {\n return undoOrAddFormattingForInlineSyntax({\n selectionStart,\n selectionEnd,\n value,\n prefix: '_',\n suffix: '_',\n });\n },\n },\n link: {\n icon: Link,\n label: 'Link',\n getKeyboardShortcut: () => {\n const modifier = Runtime.getOSKeyboardModifierKeyString();\n return {\n command: `${modifier}+k`,\n tooltipHint: `${modifier.toUpperCase()} + K`,\n };\n },\n getFormatting: ({ selectionStart, selectionEnd, value }) => {\n const { selectedText, textBeforeSelection, textAfterSelection } =\n getSelectionData({ selectionStart, selectionEnd, value });\n\n if (selectedText === '') {\n return handleLinkFormattingForEmptyTextSelection({\n textBeforeSelection,\n textAfterSelection,\n value,\n selectionStart,\n selectionEnd,\n });\n }\n\n if (\n isStringStartAUrl(selectedText) ||\n selectedText === URL_PLACEHOLDER_TEXT\n ) {\n return handleLinkFormattingForUrlSelection({\n textBeforeSelection,\n textAfterSelection,\n value,\n selectionStart,\n selectedText,\n selectionEnd,\n });\n }\n\n // If the whole selectedText matches markdown link formatting, undo it\n if (selectedText.match(MARKDOWN_LINK_REGEX)) {\n return handleUndoMarkdownLinkSelection({\n selectedText,\n selectionStart,\n selectionEnd,\n textBeforeSelection,\n textAfterSelection,\n });\n }\n\n // Finally, handle the case where link syntax is inserted for a selection other than a URL\n return {\n editSelectionStart: selectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: `[${selectedText}](${URL_PLACEHOLDER_TEXT})`,\n newCursorStart: selectionStart + selectedText.length + 3,\n newCursorEnd: selectionEnd + 6,\n };\n },\n },\n orderedList: {\n icon: OrderedList,\n label: 'Ordered list',\n getFormatting: ({ selectionStart, selectionEnd, value }) => {\n const { selectedText, textBeforeSelection } = getSelectionData({\n selectionStart,\n selectionEnd,\n value,\n });\n\n const { newLinesPrefix, newLinesSuffix } = getNewLinePrefixSuffixes({\n selectionStart,\n selectionEnd,\n value,\n });\n const { length: newLinePrefixLength } = newLinesPrefix;\n const { length: newLineSuffixLength } = newLinesSuffix;\n\n if (selectedText === '') {\n // Check start of line for whether we're in an empty ordered list\n const lastNewLine =\n textBeforeSelection === ''\n ? -1\n : getLastIndexOfCharacter({\n content: value,\n selectionIndex: selectionStart - 1,\n character: '\\n',\n });\n\n const lineStart = lastNewLine === -1 ? 0 : lastNewLine + 1;\n\n if (textBeforeSelection.slice(lineStart, lineStart + 3) === '1. ') {\n // Remove the list formatting\n return {\n editSelectionStart: lineStart,\n editSelectionEnd: lineStart + 3,\n replaceSelectionWith: '',\n newCursorStart: selectionStart - 3,\n newCursorEnd: selectionEnd - 3,\n };\n }\n }\n\n if (selectedText === '') {\n // Otherwise insert an empty list for an empty selection\n return {\n editSelectionStart: selectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: `${newLinesPrefix}1. ${newLinesSuffix}`,\n newCursorStart: selectionStart + 3 + newLinePrefixLength,\n newCursorEnd: selectionEnd + 3 + newLinePrefixLength,\n };\n }\n\n const splitByNewLine = selectedText.split('\\n');\n\n const isAlreadyAnOrderedList = splitByNewLine.every(\n (line) => line.match(ORDERED_LIST_ITEM_REGEX) || line === '',\n );\n\n if (isAlreadyAnOrderedList) {\n // Undo formatting\n const newText = splitByNewLine\n .filter((line) => line !== '')\n .map((line) => {\n const indexOfFullStop = line.indexOf('.');\n return line.substring(indexOfFullStop + 2);\n })\n .join('\\n');\n\n return {\n editSelectionStart: selectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: newText,\n newCursorStart: selectionStart + selectedText.indexOf('.') - 1,\n newCursorEnd: selectionEnd + newText.length - selectedText.length,\n };\n }\n // Otherwise convert to an ordered list\n const formattedList = `${newLinesPrefix}${splitByNewLine\n .map((textChunk, index) => `${index + 1}. ${textChunk}`)\n .join('\\n')}${newLinesSuffix}`;\n\n const cursorOffsetStart =\n selectedText.length === 0 ? 4 : newLinePrefixLength;\n\n return {\n editSelectionStart: selectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: formattedList,\n newCursorStart: selectionStart + cursorOffsetStart,\n newCursorEnd:\n selectionStart + formattedList.length - newLineSuffixLength,\n };\n },\n },\n unorderedList: {\n icon: UnorderedList,\n label: 'Unordered list',\n getFormatting: ({ selectionStart, selectionEnd, value }) => {\n return undoOrAddFormattingForMultilineSyntax({\n selectionStart,\n selectionEnd,\n value,\n linePrefix: '- ',\n });\n },\n },\n heading: {\n icon: Heading,\n label: 'Heading',\n getFormatting: ({ selectionStart, selectionEnd, value }) => {\n let currentLineSelectionStart = selectionStart;\n\n // The 'heading' formatter changes insertion based on the existing heading level of the current line\n // So we find the start of the current line and check for '#' characters\n if (selectionStart > 0) {\n const lastNewLine = getLastIndexOfCharacter({\n content: value,\n selectionIndex: selectionStart - 1,\n character: '\\n',\n });\n\n const indexOfFirstLineCharacter =\n lastNewLine === -1 ? 0 : lastNewLine + 1;\n\n if (value.charAt(indexOfFirstLineCharacter) === '#') {\n currentLineSelectionStart = indexOfFirstLineCharacter;\n }\n }\n\n const { selectedText } = getSelectionData({\n selectionStart: currentLineSelectionStart,\n selectionEnd,\n value,\n });\n\n let currentHeadingIndex = 0;\n while (selectedText.charAt(currentHeadingIndex) === '#') {\n currentHeadingIndex++;\n }\n\n // After h4, revert to no heading at all\n if (currentHeadingIndex >= 4) {\n return {\n editSelectionStart: currentLineSelectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: selectedText.substring(5),\n newCursorStart: selectionStart - 5,\n newCursorEnd: selectionEnd - 5,\n };\n }\n\n const { newLinesPrefix, newLinesSuffix } = getNewLinePrefixSuffixes({\n selectionStart,\n selectionEnd,\n value,\n });\n const { length: newLinePrefixLength } = newLinesPrefix;\n\n const adjustingHeading = currentHeadingIndex > 0;\n const cursorOffset = adjustingHeading ? 1 : 3 + newLinePrefixLength;\n\n return {\n editSelectionStart: adjustingHeading\n ? currentLineSelectionStart\n : selectionStart,\n editSelectionEnd: selectionEnd,\n replaceSelectionWith: adjustingHeading\n ? `#${selectedText}`\n : `${newLinesPrefix}## ${selectedText}${newLinesSuffix}`,\n newCursorStart: selectionStart + cursorOffset,\n newCursorEnd: selectionEnd + cursorOffset,\n };\n },\n },\n quote: {\n icon: Quote,\n label: 'Quote',\n getFormatting: ({ selectionStart, selectionEnd, value }) =>\n undoOrAddFormattingForMultilineSyntax({\n selectionStart,\n selectionEnd,\n value,\n linePrefix: '> ',\n }),\n },\n code: {\n icon: Code,\n label: 'Code',\n getFormatting: ({ selectionStart, selectionEnd, value }) =>\n undoOrAddFormattingForInlineSyntax({\n selectionStart,\n selectionEnd,\n value,\n prefix: '`',\n suffix: '`',\n }),\n },\n codeBlock: {\n icon: CodeBlock,\n label: 'Code block',\n getFormatting: ({ selectionStart, selectionEnd, value }) =>\n undoOrAddFormattingForMultilineSyntax({\n selectionStart,\n selectionEnd,\n value,\n blockPrefix: '```\\n',\n blockSuffix: '\\n```',\n }),\n },\n};\n\nexport const secondarySyntaxFormatters = {\n underline: {\n icon: Underline,\n label: 'Underline',\n getKeyboardShortcut: () => {\n const modifier = Runtime.getOSKeyboardModifierKeyString();\n return {\n command: `${modifier}+u`,\n tooltipHint: `${modifier.toUpperCase()} + U`,\n };\n },\n getFormatting: ({ selectionStart, selectionEnd, value }) =>\n undoOrAddFormattingForInlineSyntax({\n selectionStart,\n selectionEnd,\n value,\n prefix: '',\n suffix: '',\n }),\n },\n strikethrough: {\n icon: Strikethrough,\n label: 'Strikethrough',\n getKeyboardShortcut: () => {\n const modifier = Runtime.getOSKeyboardModifierKeyString();\n return {\n command: `${modifier}+shift+x`,\n tooltipHint: `${modifier.toUpperCase()} + SHIFT + X`,\n };\n },\n getFormatting: ({ selectionStart, selectionEnd, value }) =>\n undoOrAddFormattingForInlineSyntax({\n selectionStart,\n selectionEnd,\n value,\n prefix: '~~',\n suffix: '~~',\n }),\n },\n divider: {\n icon: Divider,\n label: 'Line divider',\n getFormatting: ({ selectionStart, selectionEnd, value }) =>\n undoOrAddFormattingForMultilineSyntax({\n selectionStart,\n selectionEnd,\n value,\n blockPrefix: '---\\n',\n blockSuffix: '',\n }),\n },\n};\n","import { h } from 'preact';\nimport { useState, useLayoutEffect } from 'preact/hooks';\nimport { ImageUploader } from '../../article-form/components/ImageUploader';\nimport {\n coreSyntaxFormatters,\n secondarySyntaxFormatters,\n getNewTextAreaValueWithEdits,\n} from './markdownSyntaxFormatters';\nimport { Overflow, Help } from './icons';\nimport { Button } from '@crayons';\nimport { KeyboardShortcuts } from '@components/useKeyboardShortcuts';\nimport { BREAKPOINTS, useMediaQuery } from '@components/useMediaQuery';\nimport { getSelectionData } from '@utilities/textAreaUtils';\n\n// Placeholder text displayed while an image is uploading\nconst UPLOADING_IMAGE_PLACEHOLDER = '![Uploading image](...)';\n\n/**\n * Returns the next sibling in the DOM which matches the given CSS selector.\n * This makes sure that only toolbar buttons are cycled through on Arrow key press,\n * and not e.g. the hidden file input from ImageUploader\n *\n * @param {HTMLElement} element The current HTML element\n * @param {string} selector The CSS selector to match\n * @returns\n */\nconst getNextMatchingSibling = (element, selector) => {\n let sibling = element.nextElementSibling;\n\n while (sibling) {\n if (sibling.matches(selector)) return sibling;\n sibling = sibling.nextElementSibling;\n }\n};\n\n/**\n * Returns the previous sibling in the DOM which matches the given CSS selector.\n * This makes sure that only toolbar buttons are cycled through on Arrow key press,\n * and not e.g. the hidden file input from ImageUploader\n *\n * @param {HTMLElement} element The current HTML element\n * @param {string} selector The CSS selector to match\n * @returns\n */\nconst getPreviousMatchingSibling = (element, selector) => {\n let sibling = element.previousElementSibling;\n\n while (sibling) {\n if (sibling.matches(selector)) return sibling;\n sibling = sibling.previousElementSibling;\n }\n};\n\n/**\n * UI component providing markdown shortcuts, to be inserted into the textarea with the given ID\n *\n * @param {object} props\n * @param {string} props.textAreaId The ID of the textarea the markdown formatting should be added to\n */\nexport const MarkdownToolbar = ({ textAreaId }) => {\n const [textArea, setTextArea] = useState(null);\n const [overflowMenuOpen, setOverflowMenuOpen] = useState(false);\n const [storedCursorPosition, setStoredCursorPosition] = useState({});\n const smallScreen = useMediaQuery(`(max-width: ${BREAKPOINTS.Medium - 1}px)`);\n\n const markdownSyntaxFormatters = {\n ...coreSyntaxFormatters,\n ...secondarySyntaxFormatters,\n };\n\n const keyboardShortcuts = Object.fromEntries(\n Object.keys(markdownSyntaxFormatters)\n .filter(\n (syntaxName) =>\n !!markdownSyntaxFormatters[syntaxName].getKeyboardShortcut,\n )\n .map((syntaxName) => {\n const { command } =\n markdownSyntaxFormatters[syntaxName].getKeyboardShortcut?.();\n return [\n command,\n (e) => {\n e.preventDefault();\n insertSyntax(syntaxName);\n },\n ];\n }),\n );\n\n useLayoutEffect(() => {\n setTextArea(document.getElementById(textAreaId));\n }, [textAreaId]);\n\n useLayoutEffect(() => {\n // If a user resizes their screen, make sure roving tabindex continues to operate\n const focusableToolbarButton = document.querySelector(\n '.toolbar-btn[tabindex=\"0\"]',\n );\n if (!focusableToolbarButton) {\n document.querySelector('.toolbar-btn').setAttribute('tabindex', '0');\n }\n }, [smallScreen]);\n\n useLayoutEffect(() => {\n const clickOutsideHandler = ({ target }) => {\n if (target.id !== 'overflow-menu-button') {\n setOverflowMenuOpen(false);\n }\n };\n\n const escapePressHandler = ({ key }) => {\n if (key === 'Escape') {\n setOverflowMenuOpen(false);\n document.getElementById('overflow-menu-button').focus();\n }\n if (key === 'Tab') {\n setOverflowMenuOpen(false);\n }\n };\n\n if (overflowMenuOpen) {\n document\n .getElementById('overflow-menu')\n .getElementsByClassName('overflow-menu-btn')[0]\n .focus();\n\n document.addEventListener('keyup', escapePressHandler);\n document.addEventListener('click', clickOutsideHandler);\n } else {\n document.removeEventListener('keyup', escapePressHandler);\n document.removeEventListener('click', clickOutsideHandler);\n }\n\n return () => {\n document.removeEventListener('keyup', escapePressHandler);\n document.removeEventListener('click', clickOutsideHandler);\n };\n }, [overflowMenuOpen]);\n\n // Handles keyboard 'roving tabindex' pattern for toolbar\n const handleToolbarButtonKeyPress = (event, className) => {\n const { key, target } = event;\n\n const nextButton = getNextMatchingSibling(target, `.${className}`);\n const previousButton = getPreviousMatchingSibling(target, `.${className}`);\n\n switch (key) {\n case 'ArrowRight':\n event.preventDefault();\n target.setAttribute('tabindex', '-1');\n if (nextButton) {\n nextButton.setAttribute('tabindex', 0);\n nextButton.focus();\n } else {\n const firstButton = document.querySelector(`.${className}`);\n firstButton.setAttribute('tabindex', '0');\n firstButton.focus();\n }\n break;\n case 'ArrowLeft':\n event.preventDefault();\n target.setAttribute('tabindex', '-1');\n if (previousButton) {\n previousButton.setAttribute('tabindex', 0);\n previousButton.focus();\n } else {\n const allButtons = document.getElementsByClassName(className);\n const lastButton = allButtons[allButtons.length - 1];\n lastButton.setAttribute('tabindex', '0');\n lastButton.focus();\n }\n break;\n case 'ArrowDown':\n if (target.id === 'overflow-menu-button') {\n event.preventDefault();\n setOverflowMenuOpen(true);\n }\n break;\n }\n };\n\n const insertSyntax = (syntaxName) => {\n setOverflowMenuOpen(false);\n\n const {\n newCursorStart,\n newCursorEnd,\n editSelectionStart,\n editSelectionEnd,\n replaceSelectionWith,\n } = markdownSyntaxFormatters[syntaxName].getFormatting(textArea);\n\n // We try to update the textArea with document.execCommand, which requires the contentEditable attribute to be true.\n // The value is later toggled back to 'false'\n textArea.contentEditable = 'true';\n textArea.focus({ preventScroll: true });\n textArea.setSelectionRange(editSelectionStart, editSelectionEnd);\n\n try {\n // We first try to use execCommand which allows the change to be correctly added to the undo queue.\n // document.execCommand is deprecated, but the API which will eventually replace it is still incoming (https://w3c.github.io/input-events/)\n if (replaceSelectionWith === '') {\n document.execCommand('delete', false);\n } else {\n document.execCommand('insertText', false, replaceSelectionWith);\n }\n } catch {\n // In the event of any error using execCommand, we make sure the text area updates (but undo queue will not)\n textArea.value = getNewTextAreaValueWithEdits({\n textAreaValue: textArea.value,\n editSelectionStart,\n editSelectionEnd,\n replaceSelectionWith,\n });\n }\n\n textArea.contentEditable = 'false';\n textArea.dispatchEvent(new Event('input'));\n textArea.setSelectionRange(newCursorStart, newCursorEnd);\n };\n\n const handleImageUploadStarted = () => {\n const { textBeforeSelection, textAfterSelection } =\n getSelectionData(textArea);\n\n const { selectionEnd } = storedCursorPosition;\n\n const textWithPlaceholder = `${textBeforeSelection}\\n${UPLOADING_IMAGE_PLACEHOLDER}${textAfterSelection}`;\n textArea.value = textWithPlaceholder;\n // Make sure Editor text area updates via linkstate\n textArea.dispatchEvent(new Event('input'));\n\n textArea.focus({ preventScroll: true });\n\n // Set cursor to the end of the placeholder\n const newCursorPosition =\n selectionEnd + UPLOADING_IMAGE_PLACEHOLDER.length + 1;\n\n textArea.setSelectionRange(newCursorPosition, newCursorPosition);\n };\n\n const handleImageUploadEnd = (imageMarkdown = '') => {\n const {\n selectionStart,\n selectionEnd,\n value: currentTextAreaValue,\n } = textArea;\n\n const indexOfPlaceholder = currentTextAreaValue.indexOf(\n UPLOADING_IMAGE_PLACEHOLDER,\n );\n\n // User has deleted placeholder, nothing to do\n if (indexOfPlaceholder === -1) return;\n\n const newTextValue = textArea.value.replace(\n UPLOADING_IMAGE_PLACEHOLDER,\n imageMarkdown,\n );\n\n textArea.value = newTextValue;\n // Make sure Editor text area updates via linkstate\n textArea.dispatchEvent(new Event('input'));\n\n // The change to image markdown length does not affect cursor position\n if (indexOfPlaceholder > selectionStart) {\n textArea.setSelectionRange(selectionStart, selectionEnd);\n return;\n }\n\n const differenceInLength =\n imageMarkdown.length - UPLOADING_IMAGE_PLACEHOLDER.length;\n\n textArea.setSelectionRange(\n selectionStart + differenceInLength,\n selectionEnd + differenceInLength,\n );\n };\n\n const getSecondaryFormatterButtons = (isOverflow) =>\n Object.keys(secondarySyntaxFormatters).map((controlName, index) => {\n const { icon, label, getKeyboardShortcut } =\n secondarySyntaxFormatters[controlName];\n\n return (\n