Skip to content

Commit

Permalink
EASI-4634 MentionTextArea Tiptap (#2882)
Browse files Browse the repository at this point in the history
* MentionTextArea
Tiptap lib

* DiscussionBoard folder
DiscussionModalWrapper
Sidepanel
  • Loading branch information
adamodd authored Nov 18, 2024
1 parent dcd703e commit a33d63a
Show file tree
Hide file tree
Showing 17 changed files with 1,321 additions and 22 deletions.
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
"@okta/okta-auth-js": "^7.8.0",
"@okta/okta-react": "^6.9.0",
"@okta/okta-signin-widget": "^7.23.0",
"@tiptap/extension-mention": "^2.9.1",
"@tiptap/pm": "^2.9.1",
"@tiptap/react": "^2.9.1",
"@tiptap/starter-kit": "^2.9.1",
"@tiptap/suggestion": "^2.9.1",
"@toast-ui/react-editor": "^3.2.3",
"@trussworks/react-uswds": "^3.2.0",
"@types/apollo-upload-client": "^17.0.2",
Expand Down Expand Up @@ -75,6 +80,7 @@
"redux-devtools-extension": "^2.13.9",
"redux-saga": "^1.3.0",
"redux-saga-routines": "^3.2.2",
"tippy.js": "^6.3.7",
"typescript": "^4.9.5",
"uuid": "^8.3.2",
"wildcard-mock-link": "^2.0.3",
Expand Down Expand Up @@ -179,7 +185,8 @@
"node-fetch": "2.6.1",
"graphql": "15.8.0",
"@graphql-typed-document-node/core": "3.2.0",
"@apollo/federation": "0.38.1"
"@apollo/federation": "0.38.1",
"prosemirror-model": "1.23.0"
},
"comments": {
"on_resolutions": {
Expand Down
111 changes: 111 additions & 0 deletions src/components/MentionTextArea/MentionList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/* MentionList renders the TipTap suggestion dropdown in addition to defining
defining keyboard events */

import React, {
forwardRef,
useEffect,
useImperativeHandle,
useState
} from 'react';
import { useTranslation } from 'react-i18next';

import Spinner from 'components/Spinner';

import './index.scss';

export const SuggestionLoading = () => {
return (
<div className="items width-full padding-1">
<Spinner size="small" />
</div>
);
};

// Handler dropdown scroll event on keypress
const scrollIntoView = () => {
const selectedElm = document.querySelector('.is-selected');
selectedElm?.scrollIntoView({ block: 'nearest' });
};

const MentionList = forwardRef((props: any, ref) => {
const { t } = useTranslation('discussionsMisc');

const [selectedIndex, setSelectedIndex] = useState(0);

// Sets the selected mention within the editor props
const selectItem = (index: any) => {
const item = props.items[index];

if (item) {
props.command({
id: item.username,
label: item.displayName,
'tag-type': item.tagType
});
}
};

const upHandler = () => {
setSelectedIndex(
(selectedIndex + props.items?.length - 1) % props.items?.length
);
scrollIntoView();
};

const downHandler = () => {
setSelectedIndex((selectedIndex + 1) % props.items?.length);
scrollIntoView();
};

const enterHandler = () => {
selectItem(selectedIndex);
};

useEffect(() => setSelectedIndex(0), [props.items]);

useImperativeHandle(ref, () => ({
onKeyDown: ({ event }: { event: any }) => {
if (event.key === 'ArrowUp' || (event.shiftKey && event.key === 'Tab')) {
upHandler();
return true;
}

if (
event.key === 'ArrowDown' ||
(!event.shiftKey && event.key === 'Tab')
) {
downHandler();
return true;
}

if (event.key === 'Enter') {
enterHandler();
return true;
}

return false;
}
}));

return (
<div className="items">
{props.items?.length ? (
props.items?.map((item: any, index: any) => (
<button
className={`item ${index === selectedIndex ? 'is-selected' : ''}`}
key={item.username}
id={item.username}
type="button"
onClick={() => selectItem(index)}
>
{item.displayName}
</button>
))
) : (
<div className="item">{t('noResults')}</div>
)}
</div>
);
});

export default MentionList;
104 changes: 104 additions & 0 deletions src/components/MentionTextArea/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
@use 'uswds-core' as *;

/* Basic editor styles */
.tiptap {
border: 1px solid black;
padding: .5rem;

p {
margin: 0px;
}

&__readonly {
margin-bottom: 1rem;

.ProseMirror {
outline: none;
border: none;
padding: 0;
}

&.notification__content {
p {
quotes: "" "";

&:first-child::before {
content: open-quote;
}

&:last-child::after {
content: close-quote;
}

span.react-renderer.node-mention {
& ~ .ProseMirror-trailingBreak {
display: none;
}
}
}
}
}

&__editable {
.ProseMirror {
min-height: 155px;
font-size: 16px;
line-height: 22px;
}
}
}

[data-tippy-root] {
width: 99.7%;
margin-left: .1rem !important;
}

.tippy-box {
max-width: none !important;
}

.mention {
color: #005EA2;
border: none;
background-color: transparent;
padding: 0;
}

.text-base-darker {
.mention {
color: color($theme-color-base-darker);
}
}

.text-base-darkest {
.mention {
color: color($theme-color-base-darkest);
}
}

.items {
position: relative;
background: #FFF;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0px 10px 20px rgba(0, 0, 0, 0.1);
border: 1px solid color($theme-color-base-lighter);
padding: 0;
max-height: 300px;
overflow: auto;
}

.item {
display: block;
border: none;
margin: 0;
width: 100%;
text-align: left;
background: transparent;
padding-top: 0.65rem;
padding-bottom: 0.65rem;
border-bottom: 1px solid color($theme-color-base-lighter);
min-width: 475px;

&.is-selected {
background-color: #d9e8f6;
}
}
Loading

0 comments on commit a33d63a

Please sign in to comment.