@teipublisher/pb-components
Version:
Collection of webcomponents underlying TEI Publisher
143 lines (130 loc) • 4.67 kB
JavaScript
import { LitElement, css, html } from 'lit-element';
import { pbMixin } from './pb-mixin';
import { translate } from './pb-i18n';
import { themableMixin } from './theming';
/**
* Zoom button to enlarge/shrink the font for the views. This component manages
* the global zoom level by setting CSS custom properties on the document root.
*
* @fires pb-zoom - sends an event for e.g. pb-views to react to
* @cssprop --pb-zoom-factor - the zoom factor, e.g. 1.0 for normal size, 1.5 for 150%, 0.5 for 50%
* @cssprop --pb-min-zoom - the minimum zoom factor, e.g. 0.5 for 50%
* @cssprop --pb-max-zoom - the maximum zoom factor, e.g. 3.0 for 300%
*/
export class PbZoom extends themableMixin(pbMixin(LitElement)) {
static get properties() {
return {
...super.properties,
/**
* Either 'in' or 'out'
*/
direction: {
type: String,
},
/**
* The current zoom factor
*/
zoomFactor: {
type: Number,
reflect: true,
attribute: 'zoom-factor'
},
};
}
constructor() {
super();
this.direction = 'in';
this.zoomFactor = 1.0;
}
connectedCallback() {
super.connectedCallback();
this._loadZoomPreference();
}
_handleClick(ev) {
ev.preventDefault();
ev.stopPropagation();
this.emitTo('pb-zoom', { direction: this.direction });
this.zoom(this.direction);
}
/**
* Zoom the displayed content by increasing or decreasing font size.
* Sets the zoom factor on the document root so it applies globally.
*
* @param {string} direction either `in` or `out`
*/
zoom(direction) {
const currentZoom = parseFloat(
getComputedStyle(document.documentElement).getPropertyValue('--pb-zoom-factor') || '1'
);
const step = 0.1;
const minZoom = 0.5;
const maxZoom = 2.0;
let newZoom = direction === 'in'
? Math.min(currentZoom + step, maxZoom)
: Math.max(currentZoom - step, minZoom);
document.documentElement.style.setProperty('--pb-zoom-factor', newZoom.toString());
this.zoomFactor = newZoom;
// Store user preference
localStorage.setItem('pb-zoom-preference', newZoom.toString());
}
/**
* Load the user's saved zoom preference from localStorage and apply it globally.
*/
_loadZoomPreference() {
const savedZoom = localStorage.getItem('pb-zoom-preference');
if (savedZoom) {
const zoomValue = parseFloat(savedZoom);
if (!isNaN(zoomValue)) {
document.documentElement.style.setProperty('--pb-zoom-factor', zoomValue.toString());
this.zoomFactor = zoomValue;
}
}
}
render() {
return html`
<a
href="#"
@click="${this._handleClick}"
title="${this.direction === 'in'
? translate('toolbar.zoom.in')
: translate('toolbar.zoom.out')} (current zoom: ${this.zoomFactor.toFixed(1)})"
>
<slot name="icon">
${this.direction === 'in'
? html`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path
d="M18.031 16.6168L22.3137 20.8995L20.8995 22.3137L16.6168 18.031C15.0769 19.263 13.124 20 11 20C6.032 20 2 15.968 2 11C2 6.032 6.032 2 11 2C15.968 2 20 6.032 20 11C20 13.124 19.263 15.0769 18.031 16.6168ZM16.0247 15.8748C17.2475 14.6146 18 12.8956 18 11C18 7.1325 14.8675 4 11 4C7.1325 4 4 7.1325 4 11C4 14.8675 7.1325 18 11 18C12.8956 18 14.6146 17.2475 15.8748 16.0247L16.0247 15.8748ZM10 10V7H12V10H15V12H12V15H10V12H7V10H10Z"
></path>
</svg>
`
: html`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path
d="M18.031 16.6168L22.3137 20.8995L20.8995 22.3137L16.6168 18.031C15.0769 19.263 13.124 20 11 20C6.032 20 2 15.968 2 11C2 6.032 6.032 2 11 2C15.968 2 20 6.032 20 11C20 13.124 19.263 15.0769 18.031 16.6168ZM16.0247 15.8748C17.2475 14.6146 18 12.8956 18 11C18 7.1325 14.8675 4 11 4C7.1325 4 4 7.1325 4 11C4 14.8675 7.1325 18 11 18C12.8956 18 14.6146 17.2475 15.8748 16.0247L16.0247 15.8748ZM7 10H15V12H7V10Z"
></path>
</svg>
`}
</slot>
</a>
`;
}
static get styles() {
return css`
a {
color: inherit;
text-decoration: none;
}
a:hover {
color: inherit;
}
`;
}
/**
* Fired when the user clicks the element.
*
* @event pb-zoom
* @param {String} direction: either 'in' or 'out'
*/
}
customElements.define('pb-zoom', PbZoom);