diff --git a/src/components/comments/canvas-pin-adapter/index.ts b/src/components/comments/canvas-pin-adapter/index.ts index 33954b8c..c2501c00 100644 --- a/src/components/comments/canvas-pin-adapter/index.ts +++ b/src/components/comments/canvas-pin-adapter/index.ts @@ -61,10 +61,12 @@ export class CanvasPin implements PinAdapter { * @returns {void} * */ public destroy(): void { + this.isActive = false; this.removeListeners(); this.removeAnnotationsPins(); this.pins = new Map(); this.divWrapper.remove(); + this.divWrapper = null; this.onPinFixedObserver.destroy(); this.onPinFixedObserver = null; this.canvas.style.cursor = 'default'; @@ -255,7 +257,7 @@ export class CanvasPin implements PinAdapter { this.renderTemporaryPin(); } - requestAnimationFrame(this.animate); + this.animateFrame = requestAnimationFrame(this.animate); }; /** diff --git a/src/components/form-elements/index.ts b/src/components/form-elements/index.ts index 8b470498..01452c66 100644 --- a/src/components/form-elements/index.ts +++ b/src/components/form-elements/index.ts @@ -394,7 +394,7 @@ export class FormElements extends BaseComponent { this.room?.emit(FieldEvents.INTERACTION + target.id, { fieldId: target.id, - color: this.localParticipant.slot.color, + color: this.localParticipant.slot?.color, }); const canSync = this.canSyncContent(target.id); @@ -402,7 +402,7 @@ export class FormElements extends BaseComponent { const payload: InputPayload & FocusPayload = { value: target.value, - color: this.localParticipant.slot.color, + color: this.localParticipant.slot?.color, fieldId: target.id, showOutline: this.canUpdateColor(target.id), syncContent: canSync, @@ -604,16 +604,17 @@ export class FormElements extends BaseComponent { timestamp, ...params }: SocketEvent) => { - if (syncContent && this.canSyncContent(fieldId)) { + this.publish(FieldEvents.CONTENT_CHANGE, { + value, + fieldId, + attribute, + userId: presence.id, + userName: presence.name, + timestamp, + }); + + if (syncContent && this.canSyncContent(fieldId) && presence.id !== this.localParticipant.id) { this.fields[fieldId][attribute] = value; - this.publish(FieldEvents.CONTENT_CHANGE, { - value, - fieldId, - attribute, - userId: presence.id, - userName: presence.name, - timestamp, - }); } if (showOutline && this.canUpdateColor(fieldId)) { @@ -659,10 +660,10 @@ export class FormElements extends BaseComponent { ); } - private canSyncContent(fieldId: string): boolean { + private canSyncContent = (fieldId: string): boolean => { return ( (!this.flags.disableRealtimeSync && this.enabledRealtimeSyncFields[fieldId] !== false) || - this.enabledRealtimeSyncFields[fieldId] + !!this.enabledRealtimeSyncFields[fieldId] ); - } + }; } diff --git a/src/components/who-is-online/index.test.ts b/src/components/who-is-online/index.test.ts index 3647fd83..c7974a16 100644 --- a/src/components/who-is-online/index.test.ts +++ b/src/components/who-is-online/index.test.ts @@ -39,6 +39,7 @@ const generateMockParticipant = ({ name, disableDropdown, isPrivate, + avatar: {}, }, }; diff --git a/src/components/who-is-online/index.ts b/src/components/who-is-online/index.ts index 51b650d3..f3c03689 100644 --- a/src/components/who-is-online/index.ts +++ b/src/components/who-is-online/index.ts @@ -171,7 +171,7 @@ export class WhoIsOnline extends BaseComponent { this.room.presence.get((list) => { const dataList = list - .filter((participant) => participant.data['id']) + .filter((participant) => participant.data['id'] && participant.data['avatar']) .map(({ data }: { data: any }) => { const tooltip = this.getTooltipData(data); const controls = this.getControls(data); @@ -183,6 +183,8 @@ export class WhoIsOnline extends BaseComponent { }; }) as WhoIsOnlineParticipant[]; + if (!dataList.length) return; + const localParticipantIndex = dataList.findIndex((participant) => { return participant.id === this.localParticipantId; }); diff --git a/src/web-components/comments/components/comment-input.ts b/src/web-components/comments/components/comment-input.ts index 0d4bc44a..4ae6c029 100644 --- a/src/web-components/comments/components/comment-input.ts +++ b/src/web-components/comments/components/comment-input.ts @@ -81,6 +81,10 @@ export class CommentsCommentInput extends WebComponentsBaseElement { return this.shadowRoot!.querySelector('.comments__input__textarea') as HTMLTextAreaElement; } + private get mentionButton() { + return this.shadowRoot!.querySelector('.mention-button') as HTMLButtonElement; + } + private get sendBtn() { return this.shadowRoot!.querySelector('.comments__input__send-button') as HTMLButtonElement; } @@ -145,7 +149,7 @@ export class CommentsCommentInput extends WebComponentsBaseElement { updated(changedProperties: Map) { if (changedProperties.has('mode') && this.mode === CommentMode.EDITABLE) { - this.focusInput() + this.focusInput(); this.updateHeight(); this.sendBtn.disabled = false; this.btnActive = true; @@ -199,13 +203,13 @@ export class CommentsCommentInput extends WebComponentsBaseElement { if (this.commentInput?.value.length === 0) this.btnActive = false; else this.btnActive = true; - if (e.data === null) return; - this.autoCompleteHandler.setInput(e); const caretIndex = this.autoCompleteHandler.getSelectionStart(); const keyData = this.autoCompleteHandler.getLastKeyBeforeCaret(caretIndex); const keyIndex = keyData?.keyIndex ?? -1; + if (caretIndex === -1) return; + let searchText = this.autoCompleteHandler.searchMention(caretIndex, keyIndex); let position = this.autoCompleteHandler.getSelectionPosition(); @@ -350,6 +354,9 @@ export class CommentsCommentInput extends WebComponentsBaseElement { private onTextareaLoseFocus = (e) => { const target = e.explicitOriginalTarget?.parentNode?.host; + const mentionBtn = this.mentionButton; + if (mentionBtn === e.explicitOriginalTarget || mentionBtn === e.relatedTarget) return; + // explicitOriginalTarget is for Firefox // relatedTarget is for Chrome if ( @@ -459,12 +466,11 @@ export class CommentsCommentInput extends WebComponentsBaseElement { >
-
${this.commentInputOptions()} ${this.commentInputEditableOptions()} diff --git a/src/web-components/comments/components/mention-list.ts b/src/web-components/comments/components/mention-list.ts index 4624a09f..87ca8cb5 100644 --- a/src/web-components/comments/components/mention-list.ts +++ b/src/web-components/comments/components/mention-list.ts @@ -17,8 +17,8 @@ export class CommentsMentionList extends WebComponentsBaseElement { } static styles = styles; - - declare participants: ParticipantByGroupApi[] + + declare participants: ParticipantByGroupApi[]; static properties = { participants: { type: Object }, @@ -27,7 +27,7 @@ export class CommentsMentionList extends WebComponentsBaseElement { protected updated(_changedProperties: PropertyValueMap | Map): void { if (_changedProperties.has('participants') && this.participants.length > 0) { this.showMentionList(); - return + return; } this.hideMentionList(); @@ -35,31 +35,31 @@ export class CommentsMentionList extends WebComponentsBaseElement { showMentionList = () => { const mentionList = this.shadowRoot?.getElementById('mention-list'); - mentionList?.style.setProperty('display', 'block'); + mentionList?.style.setProperty('display', 'block'); mentionList?.style.setProperty('margin-top', '1px'); mentionList.addEventListener('wheel', this.stopHandleZoom); - } + }; hideMentionList = () => { const mentionList = this.shadowRoot?.getElementById('mention-list'); mentionList.removeEventListener('wheel', this.stopHandleZoom); - mentionList?.style.setProperty('display', 'none'); - } + mentionList?.style.setProperty('display', 'none'); + }; private selectParticipant = (participant) => { this.emitEvent('participant-selected', participant, { bubbles: false, composed: false, - }) + }); - this.hideMentionList() - } + this.hideMentionList(); + }; private stopHandleZoom = (event) => { const menu = this.shadowRoot?.getElementById('mention-list'); menu.scrollTop += event.deltaY; event.preventDefault(); - } + }; private getAvatar(participant: ParticipantByGroupApi) { if (participant.avatar) { @@ -74,19 +74,18 @@ export class CommentsMentionList extends WebComponentsBaseElement { protected render() { const mentionItem = (participant) => html`
this.selectParticipant(participant)}> - ${this.getAvatar(participant)} + ${this.getAvatar(participant)}
${participant.name}
- ` + `; return html`
${repeat( - this.participants, - (participant: ParticipantByGroupApi) => participant.id, - (participant) => html` - ${mentionItem(participant)} - `)} + this.participants, + (participant: ParticipantByGroupApi) => participant.id, + (participant) => html` ${mentionItem(participant)} `, + )}
`; } diff --git a/src/web-components/comments/css/mention-list.style.ts b/src/web-components/comments/css/mention-list.style.ts index d33844ba..f044eed6 100644 --- a/src/web-components/comments/css/mention-list.style.ts +++ b/src/web-components/comments/css/mention-list.style.ts @@ -2,7 +2,7 @@ import { css } from 'lit'; export const mentionListStyle = css` #mention-list { - position: absolute; + position: fixed; z-index: 1; max-height: 200px; overflow-y: auto; @@ -12,7 +12,7 @@ export const mentionListStyle = css` width: 216px; text-align: -webkit-center; border-radius: 2px; - box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, 0.30); + box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, 0.3); padding-top: 4px; padding-bottom: 4px; /* Stiling scroll WebKit (Firefox) */ diff --git a/src/web-components/comments/utils/mention-handler.ts b/src/web-components/comments/utils/mention-handler.ts index cf7ac73c..b9053502 100644 --- a/src/web-components/comments/utils/mention-handler.ts +++ b/src/web-components/comments/utils/mention-handler.ts @@ -1,45 +1,48 @@ import { ParticipantByGroupApi } from '../../../common/types/participant.types'; - const MENTION_ACTION = { SHOW: 'show', - HIDE: 'hide' -} + HIDE: 'hide', +}; const DEFAULT_HIDE_MENTION_LIST = { action: MENTION_ACTION.HIDE, mentions: [], findDigitParticipant: false, -} +}; type hideMentionList = { action: string; mentions: ParticipantByGroupApi[]; - findDigitParticipant: boolean + findDigitParticipant: boolean; }; -const matchParticipant = (name: string, position, participantList: ParticipantByGroupApi[]): hideMentionList => { - let mentionList = [] +const matchParticipant = ( + name: string, + position, + participantList: ParticipantByGroupApi[], +): hideMentionList => { + let mentionList = []; - mentionList = participantList?.filter((participant: ParticipantByGroupApi) => participant?.email) + mentionList = participantList?.filter((participant: ParticipantByGroupApi) => participant?.email); if (name.length > 0) { - mentionList = mentionList - .filter((participant: ParticipantByGroupApi) => participant?.name - .toLowerCase() - .search(name.toLowerCase()) !== -1 - ); - if (name === mentionList[0]?.name?.toLowerCase()) { - const mentions = prepareMentionList(mentionList, position); - return { - action: MENTION_ACTION.HIDE, - mentions, - findDigitParticipant: true - } - } + mentionList = mentionList.filter( + (participant: ParticipantByGroupApi) => + participant?.name.toLowerCase().search(name.toLowerCase()) !== -1, + ); + + if (name === mentionList[0]?.name?.toLowerCase()) { + const mentions = prepareMentionList(mentionList, position); + return { + action: MENTION_ACTION.HIDE, + mentions, + findDigitParticipant: true, + }; + } } if (!(mentionList?.length > 0)) { - return DEFAULT_HIDE_MENTION_LIST + return DEFAULT_HIDE_MENTION_LIST; } const mentions = prepareMentionList(mentionList, position); @@ -47,9 +50,9 @@ const matchParticipant = (name: string, position, participantList: ParticipantBy return { action: MENTION_ACTION.SHOW, mentions, - findDigitParticipant: false - } -} + findDigitParticipant: false, + }; +}; const prepareMentionList = (users: ParticipantByGroupApi[], position): ParticipantByGroupApi[] => { return users.map((user: ParticipantByGroupApi) => ({ @@ -57,10 +60,11 @@ const prepareMentionList = (users: ParticipantByGroupApi[], position): Participa name: user.name, avatar: user.avatar, email: user.email, - position - })) -} + position, + })); +}; export default { - matchParticipant: (name, position, participantList: ParticipantByGroupApi[]) => matchParticipant(name, position, participantList), -} + matchParticipant: (name, position, participantList: ParticipantByGroupApi[]) => + matchParticipant(name, position, participantList), +};