blob: 03ceed82a12af9b6fb8d10306a95b994b45fa203 [file] [log] [blame]
'use strict';
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
var _constants = require('../utils/constants');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var MDL_RIPPLE_CONTAINER = 'mdlext-lightboard__slide__ripple-container'; /**
* @license
* Copyright 2016 Leif Olsen. All Rights Reserved.
*
* 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.
*
* This code is built with Google Material Design Lite,
* which is Licensed under the Apache License, Version 2.0
*/
/**
* A lightboard is a translucent surface illuminated from behind, used for situations
* where a shape laid upon the surface needs to be seen with high contrast. In the "old days" of photography
* photograpers used a lightboard to get a quick view of their slides. The goal is to create a responsive lightbox
* design, based on flex layout, similar to what is used in Adobe LightRoom to browse images.
*/
(function () {
'use strict';
//const LIGHTBOARD = 'mdlext-lightboard';
var LIGHTBOARD_ROLE = 'grid';
var SLIDE = 'mdlext-lightboard__slide';
var SLIDE_ROLE = 'gridcell';
var SLIDE_TABSTOP = 'mdlext-lightboard__slide__frame';
/**
* @constructor
* @param {Element} element The element that will be upgraded.
*/
var MaterialExtLightboard = function MaterialExtLightboard(element) {
// Stores the element.
this.element_ = element;
// Initialize instance.
this.init();
};
window['MaterialExtLightboard'] = MaterialExtLightboard;
// Helpers
var getSlide = function getSlide(element) {
return element ? element.closest('.' + SLIDE) : null;
};
// Private methods.
/**
* Select a slide, i.e. set aria-selected="true"
* @param element
* @private
*/
MaterialExtLightboard.prototype.selectSlide_ = function (element) {
var slide = getSlide(element);
if (slide && !slide.hasAttribute('aria-selected')) {
[].concat((0, _toConsumableArray3.default)(this.element_.querySelectorAll('.' + SLIDE + '[aria-selected="true"]'))).forEach(function (selectedSlide) {
return selectedSlide.removeAttribute('aria-selected');
});
slide.setAttribute('aria-selected', 'true');
}
};
/**
* Dispatch select event
* @param {Element} slide The slide that caused the event
* @private
*/
MaterialExtLightboard.prototype.dispatchSelectEvent_ = function (slide) {
this.element_.dispatchEvent(new CustomEvent('select', {
bubbles: true,
cancelable: true,
detail: { source: slide }
}));
};
/**
* Handles custom command event, 'first', 'next', 'prev', 'last', 'select' or upgrade
* @param event. A custom event
* @private
*/
MaterialExtLightboard.prototype.commandHandler_ = function (event) {
event.preventDefault();
event.stopPropagation();
if (event && event.detail) {
this.command(event.detail);
}
};
// Public methods
/**
* Initialize lightboard slides
* @public
*/
MaterialExtLightboard.prototype.upgradeSlides = function () {
var addRipple = function addRipple(slide) {
// Use slide frame as ripple container
if (!slide.querySelector('.' + MDL_RIPPLE_CONTAINER)) {
var a = slide.querySelector('.' + SLIDE_TABSTOP);
if (a) {
var rippleContainer = a;
rippleContainer.classList.add(MDL_RIPPLE_CONTAINER);
rippleContainer.classList.add(_constants.MDL_RIPPLE_EFFECT);
var ripple = document.createElement('span');
ripple.classList.add(_constants.MDL_RIPPLE);
rippleContainer.appendChild(ripple);
componentHandler.upgradeElement(rippleContainer, _constants.MDL_RIPPLE_COMPONENT);
}
}
};
var hasRippleEffect = this.element_.classList.contains(_constants.MDL_RIPPLE_EFFECT);
[].concat((0, _toConsumableArray3.default)(this.element_.querySelectorAll('.' + SLIDE))).forEach(function (slide) {
slide.setAttribute('role', SLIDE_ROLE);
if (!slide.querySelector('a')) {
slide.setAttribute('tabindex', '0');
}
if (hasRippleEffect) {
addRipple(slide);
}
});
};
MaterialExtLightboard.prototype['upgradeSlides'] = MaterialExtLightboard.prototype.upgradeSlides;
/**
* Execute command
* @param detail
* @public
*/
MaterialExtLightboard.prototype.command = function (detail) {
var _this = this;
var firstSlide = function firstSlide() {
return _this.element_.querySelector('.' + SLIDE + ':first-child');
};
var lastSlide = function lastSlide() {
return _this.element_.querySelector('.' + SLIDE + ':last-child');
};
var nextSlide = function nextSlide() {
var slide = _this.element_.querySelector('.' + SLIDE + '[aria-selected="true"]').nextElementSibling;
return slide ? slide : firstSlide();
};
var prevSlide = function prevSlide() {
var slide = _this.element_.querySelector('.' + SLIDE + '[aria-selected="true"]').previousElementSibling;
return slide ? slide : lastSlide();
};
if (detail && detail.action) {
var action = detail.action,
target = detail.target;
var slide = void 0;
switch (action.toLowerCase()) {
case 'select':
slide = getSlide(target);
this.dispatchSelectEvent_(slide);
break;
case 'first':
slide = firstSlide();
break;
case 'next':
slide = nextSlide();
break;
case 'prev':
slide = prevSlide();
break;
case 'last':
slide = lastSlide();
break;
case 'upgrade':
this.upgradeSlides();
break;
default:
throw new Error('Unknown action "' + action + '". Action must be one of "first", "next", "prev", "last", "select" or "upgrade"');
}
if (slide) {
var a = slide.querySelector('a');
if (a) {
a.focus();
} else {
slide.focus();
}
// Workaround for JSDom testing:
// In JsDom 'element.focus()' does not trigger any focus event
if (!slide.hasAttribute('aria-selected')) {
this.selectSlide_(slide);
}
}
}
};
MaterialExtLightboard.prototype['command'] = MaterialExtLightboard.prototype.command;
/**
* Initialize component
*/
MaterialExtLightboard.prototype.init = function () {
var _this2 = this;
var keydownHandler = function keydownHandler(event) {
if (event.target !== _this2.element_) {
var action = void 0;
var target = void 0;
switch (event.keyCode) {
case _constants.VK_HOME:
action = 'first';
break;
case _constants.VK_END:
action = 'last';
break;
case _constants.VK_ARROW_UP:
case _constants.VK_ARROW_LEFT:
action = 'prev';
break;
case _constants.VK_ARROW_DOWN:
case _constants.VK_ARROW_RIGHT:
action = 'next';
break;
case _constants.VK_ENTER:
case _constants.VK_SPACE:
action = 'select';
target = event.target;
break;
}
if (action) {
event.preventDefault();
event.stopPropagation();
_this2.command({ action: action, target: target });
}
}
};
var clickHandler = function clickHandler(event) {
event.preventDefault();
event.stopPropagation();
if (event.target !== _this2.element_) {
_this2.command({ action: 'select', target: event.target });
}
};
var focusHandler = function focusHandler(event) {
event.preventDefault();
event.stopPropagation();
if (event.target !== _this2.element_) {
_this2.selectSlide_(event.target);
}
};
if (this.element_) {
this.element_.setAttribute('role', LIGHTBOARD_ROLE);
if (this.element_.classList.contains(_constants.MDL_RIPPLE_EFFECT)) {
this.element_.classList.add(_constants.MDL_RIPPLE_EFFECT_IGNORE_EVENTS);
}
// Remove listeners, just in case ...
this.element_.removeEventListener('command', this.commandHandler_);
this.element_.removeEventListener('keydown', keydownHandler);
this.element_.removeEventListener('click', clickHandler);
this.element_.removeEventListener('focus', focusHandler);
this.element_.addEventListener('command', this.commandHandler_.bind(this), false);
this.element_.addEventListener('keydown', keydownHandler, true);
this.element_.addEventListener('click', clickHandler, true);
this.element_.addEventListener('focus', focusHandler, true);
this.upgradeSlides();
this.element_.classList.add(_constants.IS_UPGRADED);
}
};
// The component registers itself. It can assume componentHandler is available
// in the global scope.
/* eslint no-undef: 0 */
/* jshint undef:false */
componentHandler.register({
constructor: MaterialExtLightboard,
classAsString: 'MaterialExtLightboard',
cssClass: 'mdlext-js-lightboard',
widget: true
});
})();