Fix outside click in story replies
This commit is contained in:
parent
b449450098
commit
f64426fbe0
3 changed files with 56 additions and 40 deletions
|
@ -42,7 +42,7 @@ export class EmojiCompletion {
|
||||||
|
|
||||||
quill: Quill;
|
quill: Quill;
|
||||||
|
|
||||||
outsideClickDestructor: () => void;
|
outsideClickDestructor?: () => void;
|
||||||
|
|
||||||
constructor(quill: Quill, options: EmojiPickerOptions) {
|
constructor(quill: Quill, options: EmojiPickerOptions) {
|
||||||
this.results = [];
|
this.results = [];
|
||||||
|
@ -51,18 +51,6 @@ export class EmojiCompletion {
|
||||||
this.root = document.body.appendChild(document.createElement('div'));
|
this.root = document.body.appendChild(document.createElement('div'));
|
||||||
this.quill = quill;
|
this.quill = quill;
|
||||||
|
|
||||||
// Just to make sure that we don't propagate outside clicks until this
|
|
||||||
// is closed.
|
|
||||||
this.outsideClickDestructor = handleOutsideClick(
|
|
||||||
() => {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'quill.emoji.completion',
|
|
||||||
containerElements: [this.root],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const clearResults = () => {
|
const clearResults = () => {
|
||||||
if (this.results.length) {
|
if (this.results.length) {
|
||||||
this.reset();
|
this.reset();
|
||||||
|
@ -108,7 +96,8 @@ export class EmojiCompletion {
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.outsideClickDestructor();
|
this.outsideClickDestructor?.();
|
||||||
|
this.outsideClickDestructor = undefined;
|
||||||
this.root.remove();
|
this.root.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,14 +266,16 @@ export class EmojiCompletion {
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnmount(): void {
|
onUnmount(): void {
|
||||||
document.body.removeChild(this.root);
|
this.outsideClickDestructor?.();
|
||||||
|
this.outsideClickDestructor = undefined;
|
||||||
|
this.options.setEmojiPickerElement(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): void {
|
render(): void {
|
||||||
const { results: emojiResults, index: emojiResultsIndex } = this;
|
const { results: emojiResults, index: emojiResultsIndex } = this;
|
||||||
|
|
||||||
if (emojiResults.length === 0) {
|
if (emojiResults.length === 0) {
|
||||||
this.options.setEmojiPickerElement(null);
|
this.onUnmount();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,7 +365,21 @@ export class EmojiCompletion {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Popper>,
|
</Popper>,
|
||||||
document.body
|
this.root
|
||||||
|
);
|
||||||
|
|
||||||
|
// Just to make sure that we don't propagate outside clicks until this
|
||||||
|
// is closed.
|
||||||
|
this.outsideClickDestructor?.();
|
||||||
|
this.outsideClickDestructor = handleOutsideClick(
|
||||||
|
() => {
|
||||||
|
this.onUnmount();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'quill.emoji.completion',
|
||||||
|
containerElements: [this.root],
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.options.setEmojiPickerElement(element);
|
this.options.setEmojiPickerElement(element);
|
||||||
|
|
|
@ -43,7 +43,7 @@ export class MentionCompletion {
|
||||||
|
|
||||||
suggestionListRef: RefObject<HTMLDivElement>;
|
suggestionListRef: RefObject<HTMLDivElement>;
|
||||||
|
|
||||||
outsideClickDestructor: () => void;
|
outsideClickDestructor?: () => void;
|
||||||
|
|
||||||
constructor(quill: Quill, options: MentionCompletionOptions) {
|
constructor(quill: Quill, options: MentionCompletionOptions) {
|
||||||
this.results = [];
|
this.results = [];
|
||||||
|
@ -53,18 +53,6 @@ export class MentionCompletion {
|
||||||
this.quill = quill;
|
this.quill = quill;
|
||||||
this.suggestionListRef = React.createRef<HTMLDivElement>();
|
this.suggestionListRef = React.createRef<HTMLDivElement>();
|
||||||
|
|
||||||
// Just to make sure that we don't propagate outside clicks until this
|
|
||||||
// is closed.
|
|
||||||
this.outsideClickDestructor = handleOutsideClick(
|
|
||||||
() => {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'quill.emoji.completion',
|
|
||||||
containerElements: [this.root],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const clearResults = () => {
|
const clearResults = () => {
|
||||||
if (this.results.length) {
|
if (this.results.length) {
|
||||||
this.clearResults();
|
this.clearResults();
|
||||||
|
@ -92,7 +80,9 @@ export class MentionCompletion {
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.outsideClickDestructor();
|
this.outsideClickDestructor?.();
|
||||||
|
this.outsideClickDestructor = undefined;
|
||||||
|
|
||||||
this.root.remove();
|
this.root.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +210,9 @@ export class MentionCompletion {
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnmount(): void {
|
onUnmount(): void {
|
||||||
document.body.removeChild(this.root);
|
this.outsideClickDestructor?.();
|
||||||
|
this.outsideClickDestructor = undefined;
|
||||||
|
this.options.setMentionPickerElement(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): void {
|
render(): void {
|
||||||
|
@ -228,7 +220,7 @@ export class MentionCompletion {
|
||||||
const { getPreferredBadge, theme } = this.options;
|
const { getPreferredBadge, theme } = this.options;
|
||||||
|
|
||||||
if (memberResults.length === 0) {
|
if (memberResults.length === 0) {
|
||||||
this.options.setMentionPickerElement(null);
|
this.onUnmount();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,6 +285,20 @@ export class MentionCompletion {
|
||||||
this.root
|
this.root
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Just to make sure that we don't propagate outside clicks until this
|
||||||
|
// is closed.
|
||||||
|
this.outsideClickDestructor?.();
|
||||||
|
this.outsideClickDestructor = handleOutsideClick(
|
||||||
|
() => {
|
||||||
|
this.onUnmount();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'quill.mentions.completion',
|
||||||
|
containerElements: [this.root],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
this.options.setMentionPickerElement(element);
|
this.options.setMentionPickerElement(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,15 @@ export type ContainerElementType = Node | RefObject<Node> | null | undefined;
|
||||||
|
|
||||||
// TODO(indutny): DESKTOP-4177
|
// TODO(indutny): DESKTOP-4177
|
||||||
// A stack of handlers. Handlers are executed from the top to the bottom
|
// A stack of handlers. Handlers are executed from the top to the bottom
|
||||||
const fakeClickHandlers = new Array<(event: MouseEvent) => boolean>();
|
const fakeClickHandlers = new Array<{
|
||||||
|
name: string;
|
||||||
|
handleEvent: (event: MouseEvent) => boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
function runFakeClickHandlers(event: MouseEvent): void {
|
function runFakeClickHandlers(event: MouseEvent): void {
|
||||||
for (const handler of fakeClickHandlers.slice().reverse()) {
|
for (const entry of fakeClickHandlers.slice().reverse()) {
|
||||||
if (handler(event)) {
|
const { handleEvent } = entry;
|
||||||
|
if (handleEvent(event)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +29,7 @@ export type HandleOutsideClickOptionsType = Readonly<{
|
||||||
|
|
||||||
export const handleOutsideClick = (
|
export const handleOutsideClick = (
|
||||||
handler: ClickHandlerType,
|
handler: ClickHandlerType,
|
||||||
{ containerElements }: HandleOutsideClickOptionsType
|
{ name, containerElements }: HandleOutsideClickOptionsType
|
||||||
): (() => void) => {
|
): (() => void) => {
|
||||||
const handleEvent = (event: MouseEvent) => {
|
const handleEvent = (event: MouseEvent) => {
|
||||||
const target = event.target as Node;
|
const target = event.target as Node;
|
||||||
|
@ -49,19 +53,20 @@ export const handleOutsideClick = (
|
||||||
return handler(target);
|
return handler(target);
|
||||||
};
|
};
|
||||||
|
|
||||||
fakeClickHandlers.push(handleEvent);
|
const fakeHandler = { name, handleEvent };
|
||||||
|
fakeClickHandlers.push(fakeHandler);
|
||||||
if (fakeClickHandlers.length === 1) {
|
if (fakeClickHandlers.length === 1) {
|
||||||
const useCapture = true;
|
const useCapture = true;
|
||||||
document.addEventListener('click', runFakeClickHandlers, useCapture);
|
document.addEventListener('click', runFakeClickHandlers, useCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const index = fakeClickHandlers.indexOf(handleEvent);
|
const index = fakeClickHandlers.indexOf(fakeHandler);
|
||||||
fakeClickHandlers.splice(index, 1);
|
fakeClickHandlers.splice(index, 1);
|
||||||
|
|
||||||
if (fakeClickHandlers.length === 0) {
|
if (fakeClickHandlers.length === 0) {
|
||||||
const useCapture = true;
|
const useCapture = true;
|
||||||
document.removeEventListener('click', handleEvent, useCapture);
|
document.removeEventListener('click', runFakeClickHandlers, useCapture);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue