@primer/react
Version:
An implementation of GitHub's Primer Design System using React
134 lines (129 loc) • 5.29 kB
JavaScript
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 };