36881e3770
In order to eventually get rid of SystemJS and start using native `import`s instead, we'll need to provide "complete" file identifiers since otherwise there'll be MIME type errors when attempting to use `import`.
193 lines
5.9 KiB
JavaScript
193 lines
5.9 KiB
JavaScript
/* Copyright 2017 Mozilla Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import { clamp, NullL10n } from "./ui_utils.js";
|
|
|
|
const SIDEBAR_WIDTH_VAR = "--sidebar-width";
|
|
const SIDEBAR_MIN_WIDTH = 200; // pixels
|
|
const SIDEBAR_RESIZING_CLASS = "sidebarResizing";
|
|
|
|
/**
|
|
* @typedef {Object} PDFSidebarResizerOptions
|
|
* @property {HTMLDivElement} outerContainer - The outer container
|
|
* (encasing both the viewer and sidebar elements).
|
|
* @property {HTMLDivElement} resizer - The DOM element that can be dragged in
|
|
* order to adjust the width of the sidebar.
|
|
*/
|
|
|
|
class PDFSidebarResizer {
|
|
/**
|
|
* @param {PDFSidebarResizerOptions} options
|
|
* @param {EventBus} eventBus - The application event bus.
|
|
* @param {IL10n} l10n - Localization service.
|
|
*/
|
|
constructor(options, eventBus, l10n = NullL10n) {
|
|
this.enabled = false;
|
|
this.isRTL = false;
|
|
this.sidebarOpen = false;
|
|
this.doc = document.documentElement;
|
|
this._width = null;
|
|
this._outerContainerWidth = null;
|
|
this._boundEvents = Object.create(null);
|
|
|
|
this.outerContainer = options.outerContainer;
|
|
this.resizer = options.resizer;
|
|
this.eventBus = eventBus;
|
|
this.l10n = l10n;
|
|
|
|
if (
|
|
(typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) &&
|
|
(typeof CSS === "undefined" ||
|
|
typeof CSS.supports !== "function" ||
|
|
!CSS.supports(SIDEBAR_WIDTH_VAR, `calc(-1 * ${SIDEBAR_MIN_WIDTH}px)`))
|
|
) {
|
|
console.warn(
|
|
"PDFSidebarResizer: " +
|
|
"The browser does not support resizing of the sidebar."
|
|
);
|
|
return;
|
|
}
|
|
this.enabled = true;
|
|
this.resizer.classList.remove("hidden"); // Show the resizer DOM element.
|
|
|
|
this.l10n.getDirection().then(dir => {
|
|
this.isRTL = dir === "rtl";
|
|
});
|
|
this._addEventListeners();
|
|
}
|
|
|
|
/**
|
|
* @type {number}
|
|
*/
|
|
get outerContainerWidth() {
|
|
if (!this._outerContainerWidth) {
|
|
this._outerContainerWidth = this.outerContainer.clientWidth;
|
|
}
|
|
return this._outerContainerWidth;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* returns {boolean} Indicating if the sidebar width was updated.
|
|
*/
|
|
_updateWidth(width = 0) {
|
|
if (!this.enabled) {
|
|
return false;
|
|
}
|
|
// Prevent the sidebar from becoming too narrow, or from occupying more
|
|
// than half of the available viewer width.
|
|
const newWidth = clamp(
|
|
width,
|
|
SIDEBAR_MIN_WIDTH,
|
|
Math.floor(this.outerContainerWidth / 2)
|
|
);
|
|
// Only update the UI when the sidebar width did in fact change.
|
|
if (newWidth === this._width) {
|
|
return false;
|
|
}
|
|
this._width = newWidth;
|
|
this.doc.style.setProperty(SIDEBAR_WIDTH_VAR, `${newWidth}px`);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
_mouseMove(evt) {
|
|
let width = evt.clientX;
|
|
// For sidebar resizing to work correctly in RTL mode, invert the width.
|
|
if (this.isRTL) {
|
|
width = this.outerContainerWidth - width;
|
|
}
|
|
this._updateWidth(width);
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
_mouseUp(evt) {
|
|
// Re-enable the `transition-duration` rules when sidebar resizing ends...
|
|
this.outerContainer.classList.remove(SIDEBAR_RESIZING_CLASS);
|
|
// ... and ensure that rendering will always be triggered.
|
|
this.eventBus.dispatch("resize", { source: this });
|
|
|
|
const _boundEvents = this._boundEvents;
|
|
window.removeEventListener("mousemove", _boundEvents.mouseMove);
|
|
window.removeEventListener("mouseup", _boundEvents.mouseUp);
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
_addEventListeners() {
|
|
if (!this.enabled) {
|
|
return;
|
|
}
|
|
const _boundEvents = this._boundEvents;
|
|
_boundEvents.mouseMove = this._mouseMove.bind(this);
|
|
_boundEvents.mouseUp = this._mouseUp.bind(this);
|
|
|
|
this.resizer.addEventListener("mousedown", evt => {
|
|
if (evt.button !== 0) {
|
|
return;
|
|
}
|
|
// Disable the `transition-duration` rules when sidebar resizing begins,
|
|
// in order to improve responsiveness and to avoid visual glitches.
|
|
this.outerContainer.classList.add(SIDEBAR_RESIZING_CLASS);
|
|
|
|
window.addEventListener("mousemove", _boundEvents.mouseMove);
|
|
window.addEventListener("mouseup", _boundEvents.mouseUp);
|
|
});
|
|
|
|
this.eventBus.on("sidebarviewchanged", evt => {
|
|
this.sidebarOpen = !!(evt && evt.view);
|
|
});
|
|
|
|
this.eventBus.on("resize", evt => {
|
|
// When the *entire* viewer is resized, such that it becomes narrower,
|
|
// ensure that the sidebar doesn't end up being too wide.
|
|
if (!evt || evt.source !== window) {
|
|
return;
|
|
}
|
|
// Always reset the cached width when the viewer is resized.
|
|
this._outerContainerWidth = null;
|
|
|
|
if (!this._width) {
|
|
// The sidebar hasn't been resized, hence no need to adjust its width.
|
|
return;
|
|
}
|
|
// NOTE: If the sidebar is closed, we don't need to worry about
|
|
// visual glitches nor ensure that rendering is triggered.
|
|
if (!this.sidebarOpen) {
|
|
this._updateWidth(this._width);
|
|
return;
|
|
}
|
|
this.outerContainer.classList.add(SIDEBAR_RESIZING_CLASS);
|
|
const updated = this._updateWidth(this._width);
|
|
|
|
Promise.resolve().then(() => {
|
|
this.outerContainer.classList.remove(SIDEBAR_RESIZING_CLASS);
|
|
// Trigger rendering if the sidebar width changed, to avoid
|
|
// depending on the order in which 'resize' events are handled.
|
|
if (updated) {
|
|
this.eventBus.dispatch("resize", { source: this });
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
export { PDFSidebarResizer };
|