@primer/react
Version:
An implementation of GitHub's Primer Design System using React
83 lines (79 loc) • 3.57 kB
JavaScript
import { ReplyIcon } from '@primer/octicons-react';
import React__default, { createContext, useContext, useImperativeHandle, useState, useEffect } from 'react';
import { ToolbarButton } from './Toolbar.js';
import { SelectPanel } from '../../SelectPanel/SelectPanel.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); }
// SavedRepliesContext is separate from MarkdownEditorContext because the saved replies array is practically guarunteed to change
// on every render. If it was provided in the MarkdownEditorContext, it would cause the whole editor to rerender on every render.
const SavedRepliesContext = /*#__PURE__*/createContext(null);
const SavedRepliesButton = () => {
const context = useContext(SavedRepliesContext);
useImperativeHandle(context === null || context === void 0 ? void 0 : context.ref, () => ({
openMenu: () => {
setOpen(true);
}
}));
const [open, setOpen] = useState(false);
useEffect(() => setFilter(''), [open]);
const [filter, setFilter] = useState('');
// there's not much point in memoizing this since the savedReplies array is likely to change on every render
const items = context === null || context === void 0 ? void 0 : context.savedReplies.filter(({
name
}) => name.toLowerCase().includes(filter.toLowerCase())).map((reply, i) => ({
text: reply.name,
description: reply.content,
descriptionVariant: 'block',
trailingVisual: i < 9 ? `Ctrl + ${i + 1}` : undefined,
sx: {
// hide the leading visual container since we don't use the checkboxes
'& [class*=BaseVisualContainer]:first-child': {
display: 'none'
},
'& [class*=DescriptionContainer]': {
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
maxWidth: '100%'
}
}
}));
const onSelectItem = item => {
setOpen(false);
const reply = context === null || context === void 0 ? void 0 : context.savedReplies.find(({
name
}) => name === (item === null || item === void 0 ? void 0 : item.text));
if (reply) context === null || context === void 0 ? void 0 : context.onSelect(reply);
};
const onKeyDown = event => {
const keyInt = parseInt(event.key, 10);
if (items && event.ctrlKey && !Number.isNaN(keyInt) && keyInt >= 1 && keyInt <= 9) {
event.stopPropagation();
event.preventDefault();
onSelectItem(items[keyInt - 1]);
}
};
return items ? /*#__PURE__*/React__default.createElement(SelectPanel, {
renderAnchor: props => /*#__PURE__*/React__default.createElement(ToolbarButton, _extends({}, props, {
icon: ReplyIcon,
"aria-label": "Add saved reply (Ctrl + .)",
"aria-labelledby": undefined
})),
open: open,
onOpenChange: setOpen,
items: items,
filterValue: filter,
onFilterChange: setFilter,
placeholderText: "Search saved replies",
selected: undefined,
onSelectedChange: selection => {
onSelectItem(Array.isArray(selection) ? selection[0] : selection);
},
overlayProps: {
width: 'small',
maxHeight: 'small',
anchorSide: 'outside-right',
onKeyDown
}
}) : /*#__PURE__*/React__default.createElement(React__default.Fragment, null);
};
export { SavedRepliesButton, SavedRepliesContext };