UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

134 lines (129 loc) 5.29 kB
import { subscribe } from '@github/paste-markdown'; import React__default, { forwardRef, useState, useMemo, useRef, useEffect } from 'react'; import { useDynamicTextareaHeight } from '../hooks/useDynamicTextareaHeight.js'; import InlineAutocomplete from '../InlineAutocomplete/InlineAutocomplete.js'; import { useEmojiSuggestions } from './suggestions/_useEmojiSuggestions.js'; import { useMentionSuggestions } from './suggestions/_useMentionSuggestions.js'; import { useReferenceSuggestions } from './suggestions/_useReferenceSuggestions.js'; import { useRefObjectAsForwardedRef } from '../../hooks/useRefObjectAsForwardedRef.js'; import Textarea from '../../Textarea/Textarea.js'; function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } const emptyArray = []; // constant reference to avoid re-running effects const MarkdownInput = /*#__PURE__*/forwardRef(({ value, onChange, disabled, placeholder, id, maxLength, onKeyDown, fullHeight, isDraggedOver, emojiSuggestions, mentionSuggestions, referenceSuggestions, minHeightLines, maxHeightLines, visible, monospace, pasteUrlsAsPlainText, ...props }, forwardedRef) => { const [suggestions, setSuggestions] = useState(null); const [event, setEvent] = useState(null); const { trigger: emojiTrigger, calculateSuggestions: calculateEmojiSuggestions } = useEmojiSuggestions(emojiSuggestions !== null && emojiSuggestions !== void 0 ? emojiSuggestions : emptyArray); const { trigger: mentionsTrigger, calculateSuggestions: calculateMentionSuggestions } = useMentionSuggestions(mentionSuggestions !== null && mentionSuggestions !== void 0 ? mentionSuggestions : emptyArray); const { trigger: referencesTrigger, calculateSuggestions: calculateReferenceSuggestions } = useReferenceSuggestions(referenceSuggestions !== null && referenceSuggestions !== void 0 ? referenceSuggestions : emptyArray); const triggers = useMemo(() => [mentionsTrigger, referencesTrigger, emojiTrigger], [mentionsTrigger, referencesTrigger, emojiTrigger]); const lastEventRef = useRef(null); const onHideSuggestions = () => { setEvent(null); setSuggestions(null); // the effect would do this anyway, but this allows React to batch the update }; // running the calculation in an effect (rather than in the onShowSuggestions handler) allows us // to automatically recalculate if the suggestions change while the menu is open useEffect(() => { if (!event) { setSuggestions(null); return; } (async function () { lastEventRef.current = event; setSuggestions('loading'); if (event.trigger.triggerChar === emojiTrigger.triggerChar) { setSuggestions(await calculateEmojiSuggestions(event.query)); } else if (event.trigger.triggerChar === mentionsTrigger.triggerChar) { setSuggestions(await calculateMentionSuggestions(event.query)); } else if (event.trigger.triggerChar === referencesTrigger.triggerChar) { setSuggestions(await calculateReferenceSuggestions(event.query)); } })(); }, [event, calculateEmojiSuggestions, calculateMentionSuggestions, calculateReferenceSuggestions, // The triggers never actually change because they are statically defined emojiTrigger, mentionsTrigger, referencesTrigger]); const ref = useRef(null); useRefObjectAsForwardedRef(forwardedRef, ref); useEffect(() => { const subscription = ref.current && subscribe(ref.current, { defaultPlainTextPaste: { urlLinks: pasteUrlsAsPlainText } }); return subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe; }, [pasteUrlsAsPlainText]); const dynamicHeightStyles = useDynamicTextareaHeight({ maxHeightLines, minHeightLines, elementRef: ref, value }); const heightStyles = fullHeight ? {} : dynamicHeightStyles; return /*#__PURE__*/React__default.createElement(InlineAutocomplete, { triggers: triggers, suggestions: suggestions, onShowSuggestions: setEvent, onHideSuggestions: onHideSuggestions, sx: { flex: 'auto' }, tabInsertsSuggestions: true }, /*#__PURE__*/React__default.createElement(Textarea, _extends({ id: id, ref: ref, placeholder: placeholder, maxLength: maxLength, value: value, onKeyDown: onKeyDown, disabled: disabled, "aria-label": "Markdown value", onChange: onChange, sx: { width: '100%', borderStyle: 'none', boxShadow: 'none', height: fullHeight ? '100%' : undefined, outline: theme => { return isDraggedOver ? `solid 2px ${theme.colors.accent.fg}` : undefined; }, display: visible ? undefined : 'none', '& textarea': { lineHeight: 1.2, resize: fullHeight ? 'none' : 'vertical', p: 2, fontFamily: monospace ? 'mono' : 'normal', ...heightStyles } } }, props))); }); MarkdownInput.displayName = 'MarkdownInput'; export { MarkdownInput };