UNPKG

@randstad-design/orbit-multitheme

Version:

multitheme Front-end code based on Randstad Human Forward components

507 lines (446 loc) 15.4 kB
import ElementHelpers from '../helpers/element-helpers'; import trapFocus from '../helpers/trapFocus'; /** * Declare constants */ const attributeBase = 'data-rs-chat'; export class Chat { constructor(element) { this.element = element; this.amountTagsChecked; this.box = ElementHelpers.getElementsByAttribute(this.attributes.box); this.button = ElementHelpers.getElementByAttribute(this.attributes.button); this.close = ElementHelpers.getElementByAttribute(this.attributes.close); this.header = ElementHelpers.getElementByAttribute(this.attributes.header); this.inputDialog = ElementHelpers.getElementByAttribute( this.attributes.input ); this.languageNotification = ElementHelpers.getElementByAttribute( this.attributes.languageNotification ); this.languageSaveButton = ElementHelpers.getElementByAttribute( this.attributes.languageSaveButton ); this.languageSelector = ElementHelpers.getElementByAttribute( this.attributes.languageSelector ); this.minimized = ElementHelpers.getElementByAttribute( this.attributes.minimized ); this.popup = ElementHelpers.getElementByAttribute(this.attributes.popup); this.popupHide = ElementHelpers.getElementsByAttribute( this.attributes.popupHide ); this.popupShow = ElementHelpers.getElementByAttribute( this.attributes.popupShow ); this.quickSuggestButton; this.saveLanguageButton = ElementHelpers.getElementByAttribute( this.attributes.languageSaveButton ); this.main = ElementHelpers.getElementByAttribute(this.attributes.main); this.settings = ElementHelpers.getElementByAttribute( this.attributes.settings ); this.settingsButton = ElementHelpers.getElementsByAttribute( this.attributes.settingsButton ); this.tagsChecked = []; this.tagDialog = ElementHelpers.getElementByAttribute( this.attributes.tagsDialog ); this.textarea = ElementHelpers.getElementByAttribute( this.attributes.textarea ); this.deselectButton = ElementHelpers.getElementByAttribute( this.attributes.deselectButton ); this.submitButton = ElementHelpers.getElementByAttribute( this.attributes.submitButton ); this.elementIsOpen; this.keystroke = 0; this.addEventHandlers(); } /** * Declare classes */ get classes() { return { dividerIsHidden: 'divider--is-hidden', displayNone: 'display-none', isActive: 'is-active' }; } /** * Declare attribute constants */ get attributes() { return { box: attributeBase + '-box', button: attributeBase + '-button', close: attributeBase + '-close', main: attributeBase + '-main', content: attributeBase + '-content', deselectButton: attributeBase + '-tags-deselect-button', header: attributeBase + '-header', input: attributeBase + '-input', languageNotification: attributeBase + '-language-notification', languageSaveButton: attributeBase + '-language-save-button', languageSelector: attributeBase + '-language-selector', minimized: attributeBase + '-minimized', popup: attributeBase + '-popup', popupHide: attributeBase + '-popup-hide', popupShow: attributeBase + '-popup-show', quickSuggest: attributeBase + '-quick-suggest', quickSuggestButton: attributeBase + '-quick-suggest-button', saveLanguageButton: attributeBase + '-save-language', settings: attributeBase + '-settings', settingsButton: attributeBase + '-settings-button', submitButton: attributeBase + '-tags-submit-button', tags: attributeBase + '-tags', tagsDialog: attributeBase + '-tags-dialog', tagsDialogLabel: attributeBase + '-tags-dialog-label', textarea: attributeBase + '-textarea' }; } /** * Add event handlers */ addEventHandlers() { this.handleHeaderDivider(); this.toggleInputButton(); this.quickSuggestButtonsHandler(); this.tagHandler(); this.close.addEventListener('click', (event) => { this.closeChat(event); }); this.minimized.addEventListener('click', () => { this.openChat(); }); this.userInputToSpeechBubble(); this.toggleSettingsHandler(); this.languageSelectorHandler(); this.popUpHandler(); this.languageSaveButtonHandler(); // handle key down event - close modal if escape is pressed document.addEventListener('keydown', (keydownEvent) => { this.handleButtonKeys(keydownEvent); }); } /** * Adds a divider to the header when scrolling in the chat box */ handleHeaderDivider() { this.box.forEach((element) => { element.addEventListener('scroll', () => { const scrollTop = element.scrollTop; const header = element.previousElementSibling; const headerClassList = header.classList; if (scrollTop > 1) { headerClassList.remove(this.classes.dividerIsHidden); } else { headerClassList.add(this.classes.dividerIsHidden); } }); }); } /** * Toggles the button from enabled to disabled when typing in the textarea */ toggleInputButton() { this.textarea.addEventListener('input', () => { if (this.textarea.value.trim().length === 0) { this.button.disabled = true; } else { this.button.disabled = false; } }); } /** * Opens the chat box when clicking on the chat-minimized button */ openChat() { if (this.minimized) { this.minimized.classList.add(this.classes.displayNone); this.element.classList.remove(this.classes.displayNone); this.elementIsOpen = true; } } /** * Closes the chatbox when clicking on the close button */ closeChat(event) { if (this.close && this.minimized) { event.preventDefault(); this.element.classList.add(this.classes.displayNone); this.minimized.classList.remove(this.classes.displayNone); this.elementIsOpen = false; this.keystroke = 0; } } /** * Creates a speech bubble when clicking on the quick suggestion buttons */ quickSuggestButtonsHandler() { this.quickSuggestButton = ElementHelpers.getElementsByAttribute( this.attributes.quickSuggestButton ); if (this.quickSuggestButton) { this.quickSuggestButton.forEach((item) => { item.addEventListener('click', () => { const text = item.innerHTML; const parentElement = item.parentElement; parentElement.removeAttribute(this.attributes.quickSuggest); parentElement.setAttribute(this.attributes.content, 'user'); parentElement.innerHTML = `<div class="chat__content chat__content--user speech-bubble speech-bubble--user"> ${text} </div>`; this.box.scrollTop = this.box.scrollHeight; }); }); } } /** * Stores the array of the selected tags and the amount of tags that are checked in a global variable */ getArrayOfSelectedTags(event) { if (event.target.checked) { this.tagsChecked.push(event.target.value); } else { let index = this.tagsChecked.indexOf(event.target.value); if (index !== -1) { this.tagsChecked.splice(index, 1); } } this.amountTagsChecked = this.tagsChecked.length; } /** * Handles the visibility of the tag dialog and content in the dialog when the tags are checked/unchecked */ tagHandler() { this.tags = ElementHelpers.getElementByAttribute(this.attributes.tags); if (!this.tags) { return; } const checkboxes = ElementHelpers.getElementsByAttributeWithinElement( this.tags, 'data-rs-tags-checkbox' ); const tagDialogLabel = ElementHelpers.getElementByAttribute( this.attributes.tagsDialogLabel ); const label = tagDialogLabel ? tagDialogLabel.getAttribute(this.attributes.tagsDialogLabel) : '@tag selected'; for (let i = 0; checkboxes[i]; i++) { checkboxes[i].addEventListener('change', (event) => { this.getArrayOfSelectedTags(event); if (this.amountTagsChecked > 0) { this.tagDialog.classList.remove('hidden'); this.inputDialog.classList.add('hidden'); this.textarea.setAttribute('aria-hidden', true); this.button.setAttribute('aria-hidden', true); this.deselectButton.removeAttribute('aria-hidden'); this.submitButton.removeAttribute('aria-hidden'); tagDialogLabel.innerText = label.replace( '@tag', this.amountTagsChecked ); } else { this.tagDialog.classList.add('hidden'); this.inputDialog.classList.remove('hidden'); this.textarea.removeAttribute('aria-hidden'); this.button.removeAttribute('aria-hidden'); this.deselectButton.setAttribute('aria-hidden', true); this.submitButton.setAttribute('aria-hidden', true); } }); } this.handleTagDialogButtons(); } /** * Handles the buttons in the tag dialog. Changes the tags that are selected into a user speechbubble, or deselect all checkboxes. */ handleTagDialogButtons() { const checkboxes = ElementHelpers.getElementsByAttributeWithinElement( this.tags, 'data-rs-tags-checkbox' ); this.deselectButton.addEventListener('click', () => { this.tagsChecked = []; checkboxes.forEach((item) => { if (item.checked) { // Fake a click to fire the eventlistener in tag.js item.click(); } }); checkboxes[0].focus(); }); this.submitButton.addEventListener('click', () => { const text = this.tagsChecked.join(', '); // The content should be user content this.tags.setAttribute(this.attributes.content, 'user'); // The attribute tags should not be there anymore since it's a speech bubble now this.tags.removeAttribute(this.attributes.tags); this.tags.innerHTML = `<div class="chat__content chat__content--user speech-bubble speech-bubble--user"> ${text} </div>`; this.tagDialog.classList.add('hidden'); this.inputDialog.classList.remove('hidden'); this.box.scrollTop = this.box.scrollHeight; this.submitButton.setAttribute('aria-hidden', true); this.deselectButton.setAttribute('aria-hidden', true); this.textarea.removeAttribute('aria-hidden'); this.button.removeAttribute('aria-hidden'); this.textarea.focus(); }); } createSpeechBubble(e) { if (e.type === 'click' || e.key === 'Enter') { const inputText = this.textarea.value; // Remove the quickSuggestionButtons if they exist const quickSuggestion = ElementHelpers.getElementByAttribute( this.attributes.quickSuggest ); if (quickSuggestion) { quickSuggestion.remove(); } // Remove the tags if they exist if (this.tags) { this.tags.remove(); } // If the last element is from the bot it should add a --first modifier to it const box = ElementHelpers.getElementByAttribute( this.attributes.box, 'chat' ); box.innerHTML = box.innerHTML += `<div class='chat-content__wrapper' data-rs-chat-content='user'><div class="chat__content chat__content--user speech-bubble speech-bubble--user"> ${inputText}</div></div>`; if (box.lastElementChild) { const islastElementFromBot = box.lastElementChild.getAttribute(this.attributes.content) === 'bot'; if (islastElementFromBot) { box.lastElementChild.classList.add('chat-content__wrapper--first'); } } // Empty the text box this.textarea.value = ''; // Set height back to original' this.textarea.style.height = 'auto'; // Disable the button this.button.disabled = true; // Scroll to the bottom of the box box.scrollTop = box.scrollHeight; } } /** * Changes the input text to a user speech bubble when clicking on the button */ userInputToSpeechBubble() { this.button.addEventListener('click', (e) => { this.createSpeechBubble(e); this.textarea.focus(); }); this.textarea.addEventListener('keydown', (e) => { this.createSpeechBubble(e); this.textarea.focus(); }); } toggleSettingsHandler() { this.settingsButton.forEach((item) => { item.addEventListener('click', () => { this.openAndCloseSettings(); }); }); } languageSelectorHandler() { this.languageSelector.addEventListener('change', () => { const value = this.languageSelector.value; if (value !== '') { this.languageSaveButton.removeAttribute('disabled'); this.languageNotification.classList.remove('display-none'); } else { this.languageSaveButton.setAttribute('disabled', 'disabled'); this.languageNotification.classList.add('display-none'); } }); } popUpHandler() { let focusableElements = []; this.popupShow.addEventListener('click', () => { this.popup.classList.add('is-active'); focusableElements = ElementHelpers.getKeyboardFocusableElements( this.settings ); focusableElements.forEach((el) => { el.setAttribute('aria-hidden', true); }); return focusableElements; }); this.popupHide.forEach((element) => { element.addEventListener('click', () => { this.popup.classList.remove('is-active'); focusableElements.forEach((el) => el.removeAttribute('aria-hidden')); this.settingsButton.focus(); }); }); } languageSaveButtonHandler() { this.saveLanguageButton.addEventListener('click', () => { const box = ElementHelpers.getElementByAttribute( this.attributes.box, 'chat' ); // Empty the box content box.innerHTML = ''; this.openAndCloseSettings(); }); } openAndCloseSettings() { const settingsPanelClasses = this.settings.classList; if (!settingsPanelClasses.contains(this.classes.isActive)) { settingsPanelClasses.add(this.classes.isActive); setTimeout(() => { this.main.classList.add('hidden'); }, 500); } else { this.main.classList.remove('hidden'); settingsPanelClasses.remove(this.classes.isActive); this.settingsButton[0].focus(); } } /** * Handle button keys - handle key presses escape * * @param {Event} event - event triggered */ handleButtonKeys(event) { if (event != null) { switch (ElementHelpers.getKeyCodeOnKeyDownEvent(event)) { case 'Escape': if (this.elementIsOpen) { this.closeChat(event); } break; // eslint-disable-next-line no-case-declarations case 'Tab': const activePanel = !this.main.classList.contains('hidden') ? this.main : this.settings; trapFocus(event, activePanel, this.elementIsOpen, this.keystroke); if (this.elementIsOpen) { this.keystroke++; } break; } } } /** * Get selector */ static getSelector() { return `[${attributeBase}]`; } }