UNPKG

photoswipe

Version:
186 lines (154 loc) 5.42 kB
import { createElement } from '../util/util.js'; /** @typedef {import('../photoswipe.js').default} PhotoSwipe */ /** * @template T * @typedef {import('../types.js').Methods<T>} Methods<T> */ /** * @typedef {Object} UIElementMarkupProps * @prop {boolean=} isCustomSVG * @prop {string} inner * @prop {string=} outlineID * @prop {number | string} [size] */ /** * @typedef {Object} UIElementData * @prop {DefaultUIElements | string} [name] * @prop {string=} className * @prop {UIElementMarkup=} html * @prop {boolean=} isButton * @prop {keyof HTMLElementTagNameMap} [tagName] * @prop {string=} title * @prop {string=} ariaLabel * @prop {(element: HTMLElement, pswp: PhotoSwipe) => void} [onInit] * @prop {Methods<PhotoSwipe> | ((e: MouseEvent, element: HTMLElement, pswp: PhotoSwipe) => void)} [onClick] * @prop {'bar' | 'wrapper' | 'root'} [appendTo] * @prop {number=} order */ /** @typedef {'arrowPrev' | 'arrowNext' | 'close' | 'zoom' | 'counter'} DefaultUIElements */ /** @typedef {string | UIElementMarkupProps} UIElementMarkup */ /** * @param {UIElementMarkup} [htmlData] */ function addElementHTML(htmlData) { if (typeof htmlData === 'string') { // Allow developers to provide full svg, // For example: // <svg viewBox="0 0 32 32" width="32" height="32" aria-hidden="true" class="pswp__icn"> // <path d="..." /> // <circle ... /> // </svg> // Can also be any HTML string. return htmlData; } if (!htmlData || !htmlData.isCustomSVG) { return ''; } const svgData = htmlData; let out = '<svg aria-hidden="true" class="pswp__icn" viewBox="0 0 %d %d" width="%d" height="%d">'; // replace all %d with size out = out.split('%d').join(/** @type {string} */ (svgData.size || 32)); // Icons may contain outline/shadow, // to make it we "clone" base icon shape and add border to it. // Icon itself and border are styled via CSS. // // Property shadowID defines ID of element that should be cloned. if (svgData.outlineID) { out += '<use class="pswp__icn-shadow" xlink:href="#' + svgData.outlineID + '"/>'; } out += svgData.inner; out += '</svg>'; return out; } class UIElement { /** * @param {PhotoSwipe} pswp * @param {UIElementData} data */ constructor(pswp, data) { const name = data.name || data.className; let elementHTML = data.html; // @ts-expect-error lookup only by `data.name` maybe? if (pswp.options[name] === false) { // exit if element is disabled from options return; } // Allow to override SVG icons from options // @ts-expect-error lookup only by `data.name` maybe? if (typeof pswp.options[name + 'SVG'] === 'string') { // arrowPrevSVG // arrowNextSVG // closeSVG // zoomSVG // @ts-expect-error lookup only by `data.name` maybe? elementHTML = pswp.options[name + 'SVG']; } pswp.dispatch('uiElementCreate', { data }); let className = ''; if (data.isButton) { className += 'pswp__button '; className += (data.className || `pswp__button--${data.name}`); } else { className += (data.className || `pswp__${data.name}`); } /** @type {HTMLElement} */ let element; let tagName = data.isButton ? (data.tagName || 'button') : (data.tagName || 'div'); tagName = /** @type {keyof HTMLElementTagNameMap} */ (tagName.toLowerCase()); element = createElement(className, tagName); if (data.isButton) { // create button element element = createElement(className, tagName); if (tagName === 'button') { /** @type {HTMLButtonElement} */ (element).type = 'button'; } let { title } = data; const { ariaLabel } = data; // @ts-expect-error lookup only by `data.name` maybe? if (typeof pswp.options[name + 'Title'] === 'string') { // @ts-expect-error lookup only by `data.name` maybe? title = pswp.options[name + 'Title']; } if (title) { element.title = title; } if (ariaLabel || title) { /** @type {HTMLElement} */ (element).setAttribute('aria-label', ariaLabel || title); } } element.innerHTML = addElementHTML(elementHTML); if (data.onInit) { data.onInit(element, pswp); } if (data.onClick) { element.onclick = (e) => { if (typeof data.onClick === 'string') { pswp[data.onClick](); } else { data.onClick(e, element, pswp); } }; } // Top bar is default position const appendTo = data.appendTo || 'bar'; let container; if (appendTo === 'bar') { if (!pswp.topBar) { pswp.topBar = createElement('pswp__top-bar pswp__hide-on-close', 'div', pswp.scrollWrap); } container = pswp.topBar; } else { // element outside of top bar gets a secondary class // that makes element fade out on close element.classList.add('pswp__hide-on-close'); if (appendTo === 'wrapper') { container = pswp.scrollWrap; } else { // root element container = pswp.element; } } container.appendChild(pswp.applyFilters('uiElement', element, data)); } } export default UIElement;