Add Channel Mention selection ability #7151
4 changed files with 55 additions and 25 deletions
|
@ -22,8 +22,8 @@ export default function ChannelMentionSuggestion(props: Props) {
|
||||||
<div className="channel-mention__suggestion">
|
<div className="channel-mention__suggestion">
|
||||||
<ChannelThumbnail xsmall uri={uri} />
|
<ChannelThumbnail xsmall uri={uri} />
|
||||||
<span className="channel-mention__suggestion-label">
|
<span className="channel-mention__suggestion-label">
|
||||||
<div className="channel-mention__suggestion-name">{claim.name}</div>
|
|
||||||
<div className="channel-mention__suggestion-title">{(claim.value && claim.value.title) || claim.name}</div>
|
<div className="channel-mention__suggestion-title">{(claim.value && claim.value.title) || claim.name}</div>
|
||||||
|
<div className="channel-mention__suggestion-name">{claim.name}</div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -45,6 +45,7 @@ export default function ChannelMentionSuggestions(props: Props) {
|
||||||
const comboboxInputRef: ElementRef<any> = React.useRef();
|
const comboboxInputRef: ElementRef<any> = React.useRef();
|
||||||
const comboboxListRef: ElementRef<any> = React.useRef();
|
const comboboxListRef: ElementRef<any> = React.useRef();
|
||||||
const [debouncedTerm, setDebouncedTerm] = React.useState('');
|
const [debouncedTerm, setDebouncedTerm] = React.useState('');
|
||||||
|
const mainEl = document.querySelector('.channel-mention__suggestions');
|
||||||
|
|
||||||
const isRefFocused = (ref) => ref && ref.current === document.activeElement;
|
const isRefFocused = (ref) => ref && ref.current === document.activeElement;
|
||||||
|
|
||||||
|
@ -95,14 +96,25 @@ export default function ChannelMentionSuggestions(props: Props) {
|
||||||
}, [isTyping, mentionTerm, hasMinLength, possibleMatches.length]);
|
}, [isTyping, mentionTerm, hasMinLength, possibleMatches.length]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|||||||
if (!inputRef) return;
|
if (!mainEl) return;
|
||||||
|
const header = document.querySelector('.header__navigation');
|
||||||
|
|
||||||
if (mentionTerm && isUriFromTermValid) {
|
function handleReflow() {
|
||||||
inputRef.current.classList.add('textarea-mention');
|
const boxAtTopOfPage = header && mainEl.getBoundingClientRect().top <= header.offsetHeight;
|
||||||
} else {
|
const boxAtBottomOfPage = mainEl.getBoundingClientRect().bottom >= window.innerHeight;
|
||||||
inputRef.current.classList.remove('textarea-mention');
|
|
||||||
|
if (boxAtTopOfPage) {
|
||||||
|
mainEl.setAttribute('flow-bottom', '');
|
||||||
}
|
}
|
||||||
}, [inputRef, isUriFromTermValid, mentionTerm]);
|
if (mainEl.getAttribute('flow-bottom') !== null && boxAtBottomOfPage) {
|
||||||
|
mainEl.removeAttribute('flow-bottom');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleReflow();
|
||||||
|
|
||||||
|
window.addEventListener('scroll', handleReflow);
|
||||||
|
return () => window.removeEventListener('scroll', handleReflow);
|
||||||
|
}, [mainEl]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!inputRef || !comboboxInputRef || !mentionTerm) return;
|
if (!inputRef || !comboboxInputRef || !mentionTerm) return;
|
||||||
|
@ -117,6 +129,7 @@ export default function ChannelMentionSuggestions(props: Props) {
|
||||||
const selectedItem = selectedId && document.querySelector(`li[id="${selectedId}"]`);
|
const selectedItem = selectedId && document.querySelector(`li[id="${selectedId}"]`);
|
||||||
if (selectedItem) selectedItem.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
if (selectedItem) selectedItem.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
||||||
} else {
|
} else {
|
||||||
|
// $FlowFixMe
|
||||||
comboboxInputRef.current.focus();
|
comboboxInputRef.current.focus();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -132,7 +145,10 @@ export default function ChannelMentionSuggestions(props: Props) {
|
||||||
handleSelect(mentionTerm, keyCode);
|
handleSelect(mentionTerm, keyCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isRefFocused(comboboxInputRef)) inputRef.current.focus();
|
if (isRefFocused(comboboxInputRef)) {
|
||||||
|
// $FlowFixMe
|
||||||
|
inputRef.current.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.channel-mention__suggestions[flow-bottom] {
|
||||||
|
top: 4rem;
|
||||||
|
bottom: auto;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top: none;
|
||||||
|
border-bottom-right-radius: var(--border-radius);
|
||||||
|
border-bottom-left-radius: var(--border-radius);
|
||||||
|
border-bottom: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.channel-mention__input--none {
|
.channel-mention__input--none {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
width: 0;
|
width: 0;
|
||||||
|
@ -59,7 +70,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-mention__suggestion {
|
.channel-mention__suggestion {
|
||||||
@extend .wunderbar__suggestion;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 var(--spacing-xxs);
|
||||||
|
margin-left: var(--spacing-xxs);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
@ -81,35 +95,31 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-mention__suggestion-name {
|
.channel-mention__suggestion-name {
|
||||||
display: inline;
|
@extend .wunderbar__suggestion-name;
|
||||||
margin-left: calc(var(--spacing-l) - var(--spacing-xxs));
|
margin-left: calc(var(--spacing-l) - var(--spacing-xxs));
|
||||||
|
|
||||||
&::after {
|
|
||||||
margin-left: var(--spacing-xxs);
|
|
||||||
content: '•';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-mention__suggestion-title {
|
.channel-mention__suggestion-title {
|
||||||
display: inline;
|
@extend .wunderbar__suggestion-title;
|
||||||
margin-left: var(--spacing-xxs);
|
margin-left: calc(var(--spacing-l) - var(--spacing-xxs));
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-mention__placeholder-suggestion {
|
.channel-mention__placeholder-suggestion {
|
||||||
@extend .wunderbar__suggestion-name;
|
@extend .wunderbar__placeholder-suggestion;
|
||||||
|
padding: 0 var(--spacing-xxs);
|
||||||
|
margin-left: var(--spacing-xxs);
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-mention__placeholder-label {
|
.channel-mention__placeholder-label {
|
||||||
@extend .wunderbar__suggestion-name;
|
@extend .wunderbar__placeholder-label;
|
||||||
|
margin-left: var(--spacing-m);
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-mention__placeholder-thumbnail {
|
.channel-mention__placeholder-thumbnail {
|
||||||
@extend .wunderbar__suggestion-name;
|
@extend .wunderbar__placeholder-thumbnail;
|
||||||
|
margin-left: var(--spacing-m);
|
||||||
}
|
}
|
||||||
.channel-mention__placeholder-info {
|
.channel-mention__placeholder-info {
|
||||||
@extend .wunderbar__suggestion-name;
|
@extend .wunderbar__placeholder-info;
|
||||||
}
|
margin-left: var(--spacing-m);
|
||||||
|
|
||||||
.textarea-mention {
|
|
||||||
color: var(--color-primary);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,10 @@ $thumbnailWidthSmall: 1rem;
|
||||||
.form-field--SimpleMDE {
|
.form-field--SimpleMDE {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-field__two-column {
|
||||||
|
column-count: 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment__create--reply {
|
.comment__create--reply {
|
||||||
|
|
Loading…
Reference in a new issue
https://github.com/lbryio/lbry-desktop/blob/master/ui/effects/use-throttle.js would this work here?