Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New: Allow scrolling for multi-page image files #308

Merged
merged 12 commits into from Aug 16, 2017
7 changes: 5 additions & 2 deletions src/lib/Controls.js
Expand Up @@ -4,8 +4,8 @@ import { CLASS_HIDDEN } from './constants';

const SHOW_PREVIEW_CONTROLS_CLASS = 'box-show-preview-controls';
const CONTROLS_BUTTON_CLASS = 'bp-controls-btn';
const CONTROLS_PAGE_NUM_INPUT_CLASS = 'bp-doc-page-num-input';
const CONTROLS_PAGE_NUM_WRAPPER_CLASS = 'bp-doc-page-num-wrapper';
const CONTROLS_PAGE_NUM_INPUT_CLASS = 'bp-page-num-input';
const CONTROLS_PAGE_NUM_WRAPPER_CLASS = 'bp-page-num-wrapper';
const CONTROLS_AUTO_HIDE_TIMEOUT_IN_MILLIS = 2000;

class Controls {
Expand All @@ -24,6 +24,9 @@ class Controls {
/** @property {boolean} - Whether browser supports touch */
hasTouch = Browser.hasTouch();

/** @property {HTMLElement} - Page num input element */
pageNumInputEl;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing


/**
* [constructor]
*
Expand Down
65 changes: 65 additions & 0 deletions src/lib/Controls.scss
Expand Up @@ -16,6 +16,71 @@
position: relative;
table-layout: fixed;
transition: opacity .5s;

// Page num input CSS
.bp-page-num {
min-width: 48px;
width: auto; // Let page num expand as needed

span {
display: inline;
font-size: 14px;
}
}

.bp-page-num-wrapper {
background-color: #444;
border-radius: 3px;
margin: 5px;
padding: 7px 5px;
}

/* stylelint-disable property-no-vendor-prefix */
// Removes the spinner for number type inputs in Webkit browsers
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
}

// Removes the spinner for number type inputs in Firefox
input[type=number] {
-moz-appearance: textfield;
}

/* stylelint-enable property-no-vendor-prefix */

.bp-page-num-input {
font-size: 14px;
margin: 0 auto;
position: absolute;
text-align: center;
visibility: hidden;
width: 44px; // hard-coded to solve layout issues
}

&.show-page-number-input {
.bp-page-num-wrapper {
background-color: transparent;
border: none;
padding: 0;
}

.bp-page-num {
opacity: 1;
}

.bp-current-page,
.bp-page-num-divider,
.bp-total-pages {
display: none;
}

.bp-page-num-input {
display: inline-block;
position: static;
visibility: visible;
}
}
}

.box-show-preview-controls .bp-controls {
Expand Down
231 changes: 231 additions & 0 deletions src/lib/PageControls.js
@@ -0,0 +1,231 @@
import EventEmitter from 'events';
import fullscreen from './Fullscreen';
import Browser from './Browser';
import { decodeKeydown } from './util';
import { ICON_DROP_DOWN, ICON_DROP_UP } from './icons/icons';

const SHOW_PAGE_NUM_INPUT_CLASS = 'show-page-number-input';
const CONTROLS_PAGE_NUM_WRAPPER_CLASS = 'bp-page-num-wrapper';
const CONTROLS_CURRENT_PAGE = 'bp-current-page';
const CONTROLS_PAGE_NUM_INPUT_CLASS = 'bp-page-num-input';
const CONTROLS_TOTAL_PAGES = 'bp-total-pages';
const PAGE_NUM = 'bp-page-num';
const PREV_PAGE = 'bp-previous-page';
const NEXT_PAGE = 'bp-next-page';

const pageNumTemplate = `
<div class='${CONTROLS_PAGE_NUM_WRAPPER_CLASS}'>
<span class=${CONTROLS_CURRENT_PAGE}>1</span>
<input type='number' pattern='[0-9]*' min='1' value='' size='3' class='${CONTROLS_PAGE_NUM_INPUT_CLASS}' />
<span class='bp-page-num-divider'>&nbsp;/&nbsp;</span>
<span class='${CONTROLS_TOTAL_PAGES}'>1</span>
</div>`.replace(/>\s*</g, '><');

class PageControls extends EventEmitter {
/**
* [constructor]
*
* @param {HTMLElement} controls - Viewer controls
* @param {Function} previousPage - Previous page handler
* @param {Function} nextPage - Next page handler
* @return {Controls} Instance of controls
*/
constructor(controls, previousPage, nextPage) {
super();

this.controls = controls;
this.controlsEl = controls.controlsEl;
this.currentPageEl = controls.currentPageEl;
this.pageNumInputEl = controls.pageNumInputEl;

this.controls.add(__('previous_page'), previousPage, `bp-previous-page-icon ${PREV_PAGE}`, ICON_DROP_UP);
this.controls.add(__('enter_page_num'), this.showPageNumInput.bind(this), PAGE_NUM, pageNumTemplate);
this.controls.add(__('next_page'), nextPage, `bp-next-page-icon ${NEXT_PAGE}`, ICON_DROP_DOWN);
}

/**
* Initializes page number selector.
*
* @private
* @param {number} pagesCount - Total number of page
* @return {void}
*/
init(pagesCount) {
const pageNumEl = this.controlsEl.querySelector(`.${PAGE_NUM}`);
this.pagesCount = pagesCount;

// Update total page number
const totalPageEl = pageNumEl.querySelector(`.${CONTROLS_TOTAL_PAGES}`);
totalPageEl.textContent = pagesCount;

// Keep reference to page number input and current page elements
this.pageNumInputEl = pageNumEl.querySelector(`.${CONTROLS_PAGE_NUM_INPUT_CLASS}`);
this.pageNumInputEl.setAttribute('max', pagesCount);

this.currentPageEl = pageNumEl.querySelector(`.${CONTROLS_CURRENT_PAGE}`);
}

/**
* Replaces the page number display with an input box that allows the user to type in a page number
*
* @private
* @return {void}
*/
showPageNumInput() {
// show the input box with the current page number selected within it
this.controlsEl.classList.add(SHOW_PAGE_NUM_INPUT_CLASS);

this.pageNumInputEl.value = this.currentPageEl.textContent;
this.pageNumInputEl.focus();
this.pageNumInputEl.select();

// finish input when input is blurred or enter key is pressed
this.pageNumInputEl.addEventListener('blur', this.pageNumInputBlurHandler.bind(this));
this.pageNumInputEl.addEventListener('keydown', this.pageNumInputKeydownHandler.bind(this));
}

/**
* Hide the page number input
*
* @private
* @return {void}
*/
hidePageNumInput() {
this.controlsEl.classList.remove(SHOW_PAGE_NUM_INPUT_CLASS);
this.pageNumInputEl.removeEventListener('blur', this.pageNumInputBlurHandler);
this.pageNumInputEl.removeEventListener('keydown', this.pageNumInputKeydownHandler);
}

/**
* Disables or enables previous/next pagination buttons depending on
* current page number.
*
* @param {number} currentPageNum - Current page number
* @param {number} pagesCount - Total number of page
* @return {void}
*/
checkPaginationButtons(currentPageNum, pagesCount) {
const pageNumButtonEl = this.controlsEl.querySelector(`.${PAGE_NUM}`);
const previousPageButtonEl = this.controlsEl.querySelector(`${PREV_PAGE}`);
const nextPageButtonEl = this.controlsEl.querySelector(`.${NEXT_PAGE}`);

// Safari disables keyboard input in fullscreen before Safari 10.1
const isSafariFullscreen = Browser.getName() === 'Safari' && fullscreen.isFullscreen(this.controlsEl);

// Disable page number selector if there is only one page or less
if (pageNumButtonEl) {
if (pagesCount <= 1 || isSafariFullscreen) {
pageNumButtonEl.disabled = true;
} else {
pageNumButtonEl.disabled = false;
}
}

// Disable previous page if on first page, otherwise enable
if (previousPageButtonEl) {
if (currentPageNum === 1) {
previousPageButtonEl.disabled = true;
} else {
previousPageButtonEl.disabled = false;
}
}

// Disable next page if on last page, otherwise enable
if (nextPageButtonEl) {
if (currentPageNum === pagesCount) {
nextPageButtonEl.disabled = true;
} else {
nextPageButtonEl.disabled = false;
}
}
}

/**
* Update page number in page control widget.
*
* @private
* @param {number} pageNum - Number of page to update to
* @return {void}
*/
updateCurrentPage(pageNum) {
let truePageNum = pageNum;

// refine the page number to fall within bounds
if (pageNum > this.pagesCount) {
truePageNum = this.pagesCount;
} else if (pageNum < 1) {
truePageNum = 1;
}

if (this.pageNumInputEl) {
this.pageNumInputEl.value = truePageNum;
}

if (this.currentPageEl) {
this.currentPageEl.textContent = truePageNum;
}

this.currentPageNumber = truePageNum;
this.checkPaginationButtons(this.currentPageNumber, this.pagesCount);
}

/**
* Blur handler for page number input.
*
* @private
* @param {Event} event Blur event
* @return {void}
*/
pageNumInputBlurHandler(event) {
const target = event.target;
const pageNum = parseInt(target.value, 10);

if (!isNaN(pageNum)) {
this.emit('setpage', pageNum);
}

this.hidePageNumInput();
}

/**
* Keydown handler for page number input.
*
* @private
* @param {Event} event - Keydown event
* @return {void}
*/
pageNumInputKeydownHandler(event) {
const key = decodeKeydown(event);

switch (key) {
case 'Enter':
case 'Tab':
// The keycode of the 'next' key on Android Chrome is 9, which maps to 'Tab'.
// this.docEl.focus();
// We normally trigger the blur handler by blurring the input
// field, but this doesn't work for IE in fullscreen. For IE,
// we blur the page behind the controls - this unfortunately
// is an IE-only solution that doesn't work with other browsers
if (Browser.getName() !== 'Explorer') {
event.target.blur();
}

event.stopPropagation();
event.preventDefault();
break;

case 'Escape':
this.hidePageNumInput();
// this.docEl.focus();

event.stopPropagation();
event.preventDefault();
break;

default:
break;
}
}
}

export default PageControls;