UNPKG

@textback/notification-widget

Version:

TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project.

699 lines (576 loc) 27.8 kB
import MobileDetect from 'mobile-detect'; import assign from 'lodash/assign'; import Component from '../index.js'; import stringifyAttributes from '../../utils/stringifyAttributes.js'; import cookiesEx from '../../utils/cookiesEx.js'; import appInsights from '../../../sdk/utils/appInsights'; import config from '../../config'; import widgetsStorage from '../../utils/widgetsStorage.js'; import constants from '../../../sdk/utils/constants'; import SDK from '../../../sdk/index.js'; import iconBell from '../../icons/bell-icon.png'; import iconBroadcast from '../../icons/broadcast-icon.png'; import iconPaperPlane from '../../icons/paper-plane-icon.png'; import iconTbLogoLightRu from '../../icons/tb-logo-light-ru.svg' import iconTbLogoLightEn from '../../icons/tb-logo-light-en.svg' import iconTbLogoDarkRu from '../../icons/tb-logo-dark-ru.svg' import iconTbLogoDarkEn from '../../icons/tb-logo-dark-en.svg' import iconChatWindow from '../../icons/icon_whatsapp_hollow.svg'; import iconClose from '../../icons/icon_close.svg'; import './normalize.scss'; import './styles.scss'; import Wahunter from '../tb-nw-wahunter/index.js'; const channelButtons = { vk: require('../tb-notification-button/vk'), tg: require('../tb-notification-button/telegram'), facebook: require('../tb-notification-button/facebook'), viber: require('../tb-notification-button/viber'), whatsapp: require('../tb-notification-button/whatsapp'), whatsappb: require('../tb-notification-button/whatsappb'), max: require('../tb-notification-button/max') }; const DEFAULT_BUTTONS_MARKUP = { style: 'gradient', displayMode: 'horizontal', align: 'center', }; const getDeviceDisplayMap = () => { const md = new MobileDetect(window.navigator.userAgent); const isDesktop = () => !(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)); return new Map([ ['pc', isDesktop()], ['mobile', md.phone()], ['tablet', md.tablet()] ]); }; const showOnDevice = deviceList => { if (deviceList === null || deviceList === undefined) return true; return deviceList.map(i => getDeviceDisplayMap().get(i)).some(x => x); }; export default class Widget extends Component { constructor() { super(...arguments); this.apiPath = this.apiPath || config.apiPath; this.onlyManual = this.onlyManual || this.element.hasAttribute('only-manual'); this.previewMode = this.previewMode || this.element.hasAttribute('preview-mode'); const predefinedConfig = this.widgetConfig; //override widgetId property with data from widgetConfig if (!!predefinedConfig) { this.widgetId = predefinedConfig.id; this.hideTbBrandLogo = predefinedConfig.hideTbBrandLogo; } let widgetInitPromise = SDK.getWidget(this.widgetId); if (!widgetInitPromise) { // init widget widgetInitPromise = SDK.initWidget({ widgetId: this.widgetId, apiPath: this.apiPath, insecureContext: this.data, secureContextToken: this.secureContextToken, overrideConfig: predefinedConfig, }, this.previewMode); } widgetInitPromise = widgetInitPromise .then(widget => { this.widget = widget; this.widgetUserId = widget.widgetUserId; this.config = widget.getConfig(); this.config.hideTbBrandLogo = this.hideTbBrandLogo; this.config.type = this.config.type || constants.DEFAULT_WIDGET_TYPE; this.config.markUp = this.config.markUp || {}; this.config.markUp.buttons = assign({}, DEFAULT_BUTTONS_MARKUP, this.config.markUp.buttons); this.widgetSubscriptions = widget.subscriptions; this.deepLink = widget.deeplink; this.lang = this.config.lang this.showWidgetSetting = (this.widget.config && this.widget.config.showWidgetSetting) ? this.widget.config.showWidgetSetting.devices : null; const aiKey = widget.aiKey; return appInsights.init(aiKey, this.widgetUserId, this.config.accountId); }) .then(res => { this.set({ ready: showOnDevice(this.showWidgetSetting) }); return this; }, err => { console.error(err); appInsights.trackWidgetEvent(this.config.id, 'notificationWidget.error', err); this.set({ error: err }); return this; }); widgetsStorage.setWidget(this.widgetId, widgetInitPromise); } get defaults() { return { widgetId: this.element.getAttribute('widget-id'), secureContextToken: this.element.getAttribute('secure-context-token'), apiPath: this.element.getAttribute('api-path') || config.apiPath, hideTbBrandLogo: this.element.hasAttribute('hide-tb-brand-logo') || config.hideTbBrandLogo, }; } render() { super.render(); const wahunterElem = this.element.querySelector('tb-nw-wahunter'); if (wahunterElem) { new Wahunter({ element: wahunterElem, widgetAPI: this.widget, parentElement: this.element, }); } this.element.id = this.element.id ? this.element.id : 'tb-notification-widget'; const buttons = this.element.querySelectorAll('tb-notification-button'); if (buttons) { for (let i = 0; i < buttons.length; i++) { const channel = buttons[i].getAttribute('channel'); new channelButtons[channel]({ element: buttons[i], accountId: this.config.accountId, userId: this.userId, apiPath: this.apiPath, insecureContext: this.data, secureContextToken: this.secureContextToken, deepLink: this.deepLink, lang: this.lang, vkApiId: this.config.vkApiId, widgetId: this.config.id, widget: this, widgetAPI: this.widget, }); } } if (!this.config || !this.config.displayMode) return; if (this.config.displayMode === "popup") { let sessionShowCounterName = constants.COOKIE_NAME_PREFIX + this.widgetId + '_s_show_counter'; let daysShowCounterName = constants.COOKIE_NAME_PREFIX + this.widgetId + '_days_show_counter'; let daysCloseCounterName = constants.COOKIE_NAME_PREFIX + this.widgetId + '_days_close_counter'; let totalCloseCounterName = constants.COOKIE_NAME_PREFIX + this.widgetId + '_total_close_counter'; let showWidgetSetting = this.config.showWidgetSetting; let popup = this.element.getElementsByTagName("tb-notification-widget-overlay")[0]; this.element.addEventListener("click", (e) => { const target = e.target; if (!target.matches('#tb-notification-widget-close') && !target.matches('tb-notification-widget-overlay')) return; popup.className += " hidden closed"; e.preventDefault(); appInsights.trackWidgetEvent(this.config.id, 'notificationWidget.click.close', 'userAction'); if (showWidgetSetting) { let daysTimeout = showWidgetSetting.showAfterDays * constants.SECONDS_IN_DAY; cookiesEx.incrementIntCookie(daysCloseCounterName, daysTimeout); cookiesEx.incrementIntCookie(totalCloseCounterName, constants.WIDGET_USER_ID_COOKIE_TTL); } }); if (this.config.widgetKind === 'WAHUNTER') { // For uniformity of config object, "showIfSubscribed" here is "show wahunter again if it allready was run" const wahunterWasRun = cookiesEx.getBoolCookie(`${constants.COOKIE_NAME_PREFIX + this.widgetId}_wahunter_was_run`); const showIfWasRun = showWidgetSetting.showIfSubscribed; if (wahunterWasRun && !showIfWasRun) return; } if (showWidgetSetting) { if (!showWidgetSetting.showIfSubscribed && this.widgetSubscriptions.length > 0) { appInsights.trackWidgetEvent(this.config.id, 'notificationWidget.popup.notShown', 'alreadySubscribed'); return; } let daysTimeout = showWidgetSetting.showAfterDays * constants.SECONDS_IN_DAY; let sessionTimeout = showWidgetSetting.showSessionLength * 60; let daysShowCounter = cookiesEx.incrementIntCookie(daysShowCounterName, daysTimeout); let sessionShowCounter = cookiesEx.incrementIntCookie(sessionShowCounterName, sessionTimeout); let daysCloseCounter = cookiesEx.getIntCookie(daysCloseCounterName) || 0; let totalCloseCounter = cookiesEx.getIntCookie(totalCloseCounterName) || 0; const sendNonDisplayed = (reason) => { appInsights.trackWidgetEvent(this.config.id, 'notificationWidget.popup.notShown', reason); return true; }; if (sessionShowCounter > showWidgetSetting.showTimesInSession && sendNonDisplayed('timesPerSession') || // Не более N раз в сессию (showWidgetSetting.showTimesInRow > 0 && daysShowCounter > showWidgetSetting.showTimesInRow && sendNonDisplayed('timesPerDays')) ||// Не более N раз в M дней (showWidgetSetting.closeTimesInRow > 0 && daysCloseCounter >= showWidgetSetting.closeTimesInRow && sendNonDisplayed('closedTimesPerDays')) || // Не более N закрытий в M дней (showWidgetSetting.closeTimesInTotal > 0 && totalCloseCounter >= showWidgetSetting.closeTimesInTotal && sendNonDisplayed('closedTimes')) // Не более N закрытий всего ) { return; } } if (this.onlyManual) { popup.className = 'hidden'; return; } if (this.config.displayOptions && (this.config.displayOptions.onTimeout || this.config.displayOptions.onLeave)) { let telemetryFlag = false; if (this.config.displayOptions.onTimeout && showOnDevice(this.showWidgetSetting)) { setTimeout(() => { if (popup.className.indexOf('closed') === -1 && !telemetryFlag) { telemetryFlag = true; appInsights.trackWidgetEvent(this.config.id, 'notificationWidget.popup.show', 'timeout'); } popup.className = popup.className.indexOf('closed') === -1 ? "" : popup.className; }, +this.config.displayOptions.timeoutDelay * 1000); } if (this.config.displayOptions.onLeave && showOnDevice(this.showWidgetSetting)) { document.addEventListener('mousemove', (e) => { if (e.clientY < 50) { if (popup.className.indexOf('closed') === -1 && !telemetryFlag) { telemetryFlag = true; appInsights.trackWidgetEvent(this.config.id, 'notificationWidget.popup.show', 'onLeave'); } popup.className = popup.className.indexOf('closed') === -1 ? "" : popup.className; } }); } } } if (this.config.displayMode === "corner-popup" && showOnDevice(this.showWidgetSetting)) { const popupControlButtonElem = this.element.querySelector('tb-widget-corner-popup-button'); const popupElem = popupControlButtonElem.parentElement; const popupTooltipContainer = popupElem.querySelector('tb-widget-corner-popup-tooltip-container'); const fullscreenCloseButtonElem = popupElem.querySelector('.tb-fullscreen-close-rectangle .tb-icon-close'); fullscreenCloseButtonElem.addEventListener('click', (e) => { popupElem.classList.remove('tb-opened-corner-popup'); }); popupControlButtonElem.addEventListener('click', (e) => { popupElem.classList.toggle('tb-opened-corner-popup'); }); popupElem.addEventListener('wahunter-success', (e) => { popupTooltipContainer.style.display = 'none'; }); } } get header() { if (this.config.markUp && this.config.markUp.header && this.config.markUp.header.value) { var color = this.config.markUp.header.color ? 'style="color: ' + this.config.markUp.header.color + '"' : undefined; var result = ''; result += '<tb-widget-header-line ' + color + ' class="tb-widget-header-line">' + this.config.markUp.header.value + '</tb-widget-header-line>'; return '<tb-widget-header-container>' + result + '</tb-widget-header-container>'; } else { return ''; } } get description() { if (this.config.markUp && this.config.markUp.description && this.config.markUp.description.value) { var color = this.config.markUp.description.color ? 'style="color: ' + this.config.markUp.description.color + '"' : undefined; var result = ''; result += '<tb-widget-description-line ' + color + ' class="tb-widget-description-line">' + this.config.markUp.description.value + '</tb-widget-description-line>' return '<tb-widget-description-container>' + result + '</tb-widget-description-container>'; } else { return ''; } } static iconPreset(val) { switch (val) { case 'widgetIconBell': return iconBell; case 'widgetIconBroadcast': return iconBroadcast; case 'widgetIconPaperPlane': return iconPaperPlane; } } get icon() { if (this.config.markUp && this.config.markUp.icon && this.config.markUp.icon.type === "custom") { return '<tb-notification-widget-icon_body>' + '<img src="' + this.config.markUp.icon.url + '">' + '</tb-notification-widget-icon_body>' } if (this.config.markUp && this.config.markUp.icon && this.config.markUp.icon.type === "preset") { return '<tb-notification-widget-icon_body>' + '<img src="' + Widget.iconPreset(this.config.markUp.icon.value) + '">' + '</tb-notification-widget-icon_body>' } else { return ''; } } getTbBrandBadge({ displayMode, widgetKind }) { const utmMedium = widgetKind === 'WAHUNTER' ? 'hunter' : 'widget-subscribe'; if (widgetKind === 'WAHUNTER') { return ` <a target="_blank" href="//textback.ru/?utm_source=TextBack_tool&utm_medium=${utmMedium}&utm_campaign=${window.location.origin}${window.location.pathname}&utm_content=${displayMode}" id="tb-notification-brand-badge" style="${displayMode === 'inline' ? 'fill:inherit; color: inherit;' : ''}" > ${this.iconLogo } </a> `; } return ` <a target="_blank" href="//textback.ru/?utm_source=TextBack_tool&utm_medium=${utmMedium}&utm_campaign=${window.location.origin}${window.location.pathname}" id="tb-notification-brand-badge" style="${displayMode === 'inline' ? 'fill:inherit; color: inherit;' : ''}" > ${this.iconLogo } </a> `; } get image() { if (!this.config.markUp.image || !this.config.markUp.image.show) return ''; return ` <tb-notification-widget-image style="background-image: url(${this.config.markUp.image.url})"> <img src="${this.config.markUp.image.url}"> </tb-notification-widget-image> `; } get pallet() { return this.config && this.config.markUp.body.palette && this.config.markUp.body.palette === "light" ? 'tb-pallet-light' : 'tb-pallet-dark' } get iconLogo() { if (this.pallet === 'tb-pallet-dark' && this.lang === 'ru') { return iconTbLogoDarkRu; } else if (this.pallet === 'tb-pallet-dark' && this.lang === 'en') { return iconTbLogoDarkEn; } else if (this.pallet === 'tb-pallet-light' && this.lang === 'ru') { return iconTbLogoLightRu; } else if (this.pallet === 'tb-pallet-light' && this.lang === 'en') { return iconTbLogoLightEn; } else { return iconTbLogoLightEn } } get template() { if (this.ready && this.config && this.config.displayMode) { switch (this.config.displayMode) { case 'popup': return this.popupTemplate; case 'inline': return this.inlineTemplate; case 'corner-popup': return this.cornerPopupTemplate; default: return ''; } } else if (this.error) { console.warn(`could not render widget. reason: ${this.error}`); } return ''; } get popupTemplate() { this.element.className = (this.config.type === constants.WIDGET_TYPE_WIDGET ? 'tb-no-api-call' : ''); const markUp = this.config.markUp; const isPopupWithImage = markUp.image && markUp.image.show; let imageUrlNotEmpty; let popupStyleClass = this.pallet; if (this.config.widgetKind === 'WAHUNTER' || isPopupWithImage) { imageUrlNotEmpty = !!markUp.image.url.trim(); popupStyleClass += ` tb-popup-with-image tb-popup-${(markUp.image.align !== 'top' && imageUrlNotEmpty) ? 'horizontal' : 'vertical'}`; } if (this.config.widgetKind === 'WAHUNTER') { let wahunterImageAlignClassName = `tb-nw-wahunter_${imageUrlNotEmpty ? ('image-align_' + markUp.image.align) : 'without-image'}`; return ` <tb-notification-widget-inner id="tb-notification-widget-inner" class="tb-notification-widget-inner"> <tb-notification-widget-overlay class="hidden"> <tb-notification-widget-popup class="${popupStyleClass}"> <span id="tb-notification-widget-close" class="tb-notification-widget-close-icon">&times;</span> ${this.config.hideTbBrandLogo ? '' : this.getTbBrandBadge(this.config)} <tb-nw-wahunter class="tb-nw-wahunter tb-nw-wahunter_mode_popup ${wahunterImageAlignClassName}"></tb-nw-wahunter> </tb-notification-widget-popup> </tb-notification-widget-overlay> </tb-notification-widget-inner> `; } const buttonsStyle = (this.config.type === constants.WIDGET_TYPE_WIDGET ? 'square' : 'rounded'); const buttonsStyleClass = (this.config.type === constants.WIDGET_TYPE_WIDGET ? 'tb-btn-style-square' : 'tb-btn-style-rounded'); let body = ''; const channels = this.widget.getEnabledChannels(); body = channels.map((channel, index) => { const item = assign({}, channel.config, { type: this.config.type, markUp: assign({}, markUp.buttons, { style: buttonsStyle }), }); const attributes = stringifyAttributes({ channel: item.channel, config: item, }); return item.enabled ? `<div><tb-notification-button id="tb-notification-button-${index}" ${attributes}></tb-notification-button></div>` : ''; }).join(''); let popupBodyStyleClass = 'tb-notification-widget-popup-body'; if (isPopupWithImage) { popupBodyStyleClass += ` ${popupBodyStyleClass}_image-align_${imageUrlNotEmpty ? markUp.image.align : 'none'}` } return ` <tb-notification-widget-inner id="tb-notification-widget-inner" class="tb-notification-widget-inner"> <tb-notification-widget-overlay class="hidden"> <tb-notification-widget-popup class="${popupStyleClass}"> <span id="tb-notification-widget-close" class="tb-notification-widget-close-icon">&times;</span> ${this.config.hideTbBrandLogo ? '' : this.getTbBrandBadge(this.config)} <tb-notification-widget-popup-body class="${popupBodyStyleClass}"> ${(markUp.image && markUp.image.show) ? this.image : this.icon} <tb-notification-widget-popup-main> ${this.header} ${this.description} <div class="tb-widget-buttons tb-btn-align-center ${buttonsStyleClass}">${body}</div> </tb-notification-widget-popup-main> </tb-notification-widget-popup-body> </tb-notification-widget-popup> </tb-notification-widget-overlay> </tb-notification-widget-inner> `; } get inlineTemplate() { this.element.className = (this.config.type === constants.WIDGET_TYPE_WIDGET ? 'tb-no-api-call' : ''); const buttonsContainerClasses = this.config.type === constants.WIDGET_TYPE_WIDGET ? this.getButtonsClasses() : 'tb-btn-style-rounded tb-btn-mode-horizontal tb-btn-align-left'; const channels = this.widget.getEnabledChannels(); const body = channels.map((channel, index) => { const item = assign({}, channel.config, { type: this.config.type, markUp: this.config.markUp.buttons }); const attributes = stringifyAttributes({ channel: item.channel, config: item, }); if (item.enabled) { let html = `<tb-notification-button id="tb-notification-button-${index}" ${attributes}></tb-notification-button>`; if (this.config.markUp.buttons.displayMode === 'vertical') { html = `<div>${html}</div>`; } return html; } else { return ''; } }).join(''); appInsights.trackWidgetEvent(this.config.id, 'notificationWidget.inline.show', 'displaySettings'); if (this.config.widgetKind === 'WAHUNTER') { return ` <tb-notification-widget-inner id="tb-notification-widget-inner" class="tb-notification-widget-inner"> <tb-nw-wahunter class="tb-nw-wahunter tb-nw-wahunter_mode_inline tb-nw-wahunter_inline-mode-align_${this.config.markUp.buttons.align}"></tb-nw-wahunter> </tb-notification-widget-inner> `; } return ` <tb-notification-widget-inner id="tb-notification-widget-inner" class="tb-notification-widget-inner"> <div class="tb-widget-buttons ${buttonsContainerClasses}"> ${body} </div> </tb-notification-widget-inner> `; } get cornerPopupTemplate() { const markUp = this.config.markUp; if (this.config.widgetKind === 'WAHUNTER') { return ` <tb-notification-widget-inner id="tb-notification-widget-inner" class="tb-notification-widget-inner"> <tb-notification-widget-corner-popup> <tb-widget-corner-popup-window class="${this.pallet}"> <div class="tb-fullscreen-close-rectangle"> ${iconClose} </div> <tb-nw-wahunter class="tb-nw-wahunter tb-nw-wahunter_mode_corner-popup"></tb-nw-wahunter> ${this.config.hideTbBrandLogo ? '' : this.getTbBrandBadge(this.config)} </tb-widget-corner-popup-window> <tb-widget-corner-popup-button style="background: ${markUp.wahunterButton.color}"> ${iconChatWindow} ${iconClose} </tb-widget-corner-popup-button> <tb-widget-corner-popup-tooltip-container class="${markUp.description.showOnMobile ? 'tb-widget-corner-popup-tooltip-container_show-on-mobile' : ''}" style="${markUp.description.value.trim() ? '' : 'display:none'}" > <tb-widget-corner-popup-tooltip style="color:${markUp.description.color}">\ ${markUp.description.value}\ </tb-widget-corner-popup-tooltip> <tb-widget-corner-popup-tooltip-arrow /> </tb-widget-corner-popup-tooltip-container> </tb-notification-widget-corner-popup> </tb-notification-widget-inner> `; } return ''; } static get tagName() { return 'tb-notification-widget'; } static getWidget(widgetId) { return widgetsStorage.getWidget(widgetId); } show() { if (this.config && this.config.displayMode && this.config.displayMode === "popup") { const popup = this.element.getElementsByTagName("tb-notification-widget-overlay")[0]; if (popup.className.indexOf('hidden') !== -1 || popup.className.indexOf('closed')) { appInsights.trackWidgetEvent(this.config.id, 'notificationWidget.popup.show', 'manual'); } popup.className = ''; } } showWithRules() { let showWidgetSetting = this.config.showWidgetSetting; let sessionShowCounterName = constants.COOKIE_NAME_PREFIX + this.widgetId + '_s_show_counter'; let daysShowCounterName = constants.COOKIE_NAME_PREFIX + this.widgetId + '_days_show_counter'; let daysCloseCounterName = constants.COOKIE_NAME_PREFIX + this.widgetId + '_days_close_counter'; let totalCloseCounterName = constants.COOKIE_NAME_PREFIX + this.widgetId + '_total_close_counter'; if (showWidgetSetting) { if (!showWidgetSetting.showIfSubscribed && this.widgetSubscriptions.length > 0) { return false; } let daysTimeout = showWidgetSetting.showAfterDays * constants.SECONDS_IN_DAY; let sessionTimeout = showWidgetSetting.showSessionLength * 60; let daysShowCounter = cookiesEx.incrementIntCookie(daysShowCounterName, daysTimeout); let sessionShowCounter = cookiesEx.incrementIntCookie(sessionShowCounterName, sessionTimeout); let daysCloseCounter = cookiesEx.getIntCookie(daysCloseCounterName) || 0; let totalCloseCounter = cookiesEx.getIntCookie(totalCloseCounterName) || 0; if (sessionShowCounter > showWidgetSetting.showTimesInSession || (showWidgetSetting.showTimesInRow > 0 && daysShowCounter > showWidgetSetting.showTimesInRow) || (showWidgetSetting.closeTimesInRow > 0 && daysCloseCounter >= showWidgetSetting.closeTimesInRow) || (showWidgetSetting.closeTimesInTotal > 0 && totalCloseCounter >= showWidgetSetting.closeTimesInTotal)) { return false; } } if (this.config && this.config.displayMode && this.config.displayMode === "popup") { const popup = this.element.getElementsByTagName("tb-notification-widget-overlay")[0]; if (popup.className.indexOf('hidden') !== -1 || popup.className.indexOf('closed')) { appInsights.trackWidgetEvent(this.config.id, 'notificationWidget.popup.show', 'manual'); } popup.className = ''; return popup; } return false; } hide() { if (this.config && this.config.displayMode && this.config.displayMode === "popup") { const popup = this.element.getElementsByTagName("tb-notification-widget-overlay")[0]; if (popup.className.indexOf('hidden') !== -1) { popup.className += 'hidden'; } } } getButtonsClasses() { const classes = []; if (this.config.markUp && this.config.markUp.buttons) { const markUp = this.config.markUp.buttons; classes.push(getButtonsStyleClass(markUp.style)); classes.push(getButtonsAlignmentClass(markUp.align)); classes.push(getButtonsDisplayModeClass(markUp.displayMode)); } function getButtonsStyleClass(style) { const prefix = 'tb-btn-style'; switch (style) { case 'rounded': return `${prefix}-rounded`; case 'icons': return `${prefix}-icons`; case 'gradient': return `${prefix}-gradient`; default: return `${prefix}-square`; } } function getButtonsAlignmentClass(align) { const prefix = 'tb-btn-align'; switch (align) { case 'left': return `${prefix}-left`; case 'right': return `${prefix}-right`; default: return `${prefix}-center`; } } function getButtonsDisplayModeClass(mode) { const prefix = 'tb-btn-mode'; if (mode === 'vertical') { return `${prefix}-vertical`; } else { return `${prefix}-horizontal`; } } return classes.join(' '); } };