UNPKG

@ryannerd/ember-toggle-button

Version:

Ember CLI ember-toggle-button component add-on.

238 lines (198 loc) 7.11 kB
import Ember from 'ember'; import layout from '../templates/components/ember-toggle-button'; const POLL_TIMER = 150; export default Ember.Component.extend( { // Needed since this component is an Ember add-on. layout: layout, // Used to generate a random string to be used as a unique Id. randomId: Ember.computed(function() { return Math.random().toString(36).substring(7); }), // The interval timer id (note this is a global property and is never updated via this.set() ) pollTimer: null, // Establish properties and set up hooks to the target element. init() { this._super(...arguments); // This sets the outer <span id={{targetId}}>around the target</span> let targetId = this.get('randomId'); this.set('targetId', targetId); // Set the toggle button id this.set('buttonId', 'toggle-button__' + targetId); // TODO: add toggleState - true=open, false=closed // this.set('toggleState', true); this.set('useDefaultToggleAction', this.getWithDefault('useDefaultToggleAction', true)); this.set('toggleWidth', this.getWithDefault('toggleWidth', '5%')); // Set the left and top offset to 0 as default unless overridden. this.set('topOffset', this.getWithDefault('topOffset', 0)); this.set('leftOffset', this.getWithDefault('leftOffset', 0)); // TODO: Add open-offset and closed-offset }, didInsertElement() { // Figure out the element that is wrapped in the toggle-button block. let targetId = this.get('targetId'); // Get the first element in the yield block. let target = Ember.$('span#' + targetId + ' >'); // Sanity check if (target.length === 1) { // Add toggle-target_xxx class as the target element identifier. target.addClass('toggle-target_' + targetId); // Set this class so that when elements are polled we can scoop up ALL elements. target.addClass('toggle-button__targetElement'); // Get the current position (left and top) and width of the target element let position = target.position(); // Sanity check if (position) { // Jquery magic let left = position.left; let top = position.top; let width = target.outerWidth(); let height = target.outerHeight(); // Save the original position and size of the target element as data-* attributes. // This is done to work around the fact that didInsertElement does not allow property changes. // And also calling this.get('topOffset') for example is an expensive call and was causing visible jitter. target.attr('data-original-top', top); target.attr('data-original-left', left); target.attr('data-original-width', width); target.attr('data-original-height', height); target.attr('data-toggle-button-top-offset', this.get('topOffset')); target.attr('data-toggle-button-left-offset', this.get('leftOffset')); /* TODO: Add open-offset and closed-offset target.attr('data-toggle-button-top-open-offset', this.get('topOffset')); target.attr('data-toggle-button-top-closed-offset', this.get('topOffset')); target.attr('data-toggle-button-left-open-offset', this.get('leftOffset')); target.attr('data-toggle-button-left-closed-offset', this.get('leftOffset')); */ // Save the toggle button id (including the hash) as a data-* attribute. target.attr('data-toggle-button__id', '#' + this.get('buttonId')); // If we don't have an element poll timer then we will create one. // Notice we are NOT using this.get(). if (this.pollTimer === null) { // Do not use this.set('pollTimer') to get around deprecation notices, // and so that pollTimer can be used globally (in case this component is used more than once in a template). this.pollTimer =setInterval(() => { this.pollElements(); }, POLL_TIMER); } } } }, /** * Use Jquery to select every element we are interested in and updates the position of the toggle button accordingly. * Fires every POLL_TIMER (see didInsertElement() ). * */ pollElements() { // Grab ALL the elements we are interested in. Ember.$('.toggle-button__targetElement').each(function(i, element) { // Process each element as a Jquery object let target = Ember.$(element); // Get the current position (left and top) and width of the target element let position = target.position(); // Sanity check if (position) { // Jquery magic let left = position.left; let top = position.top; let width = target.outerWidth(); // let height = target.outerHeight(); // Grab the button id from data-toggle-button-id attribute (this is assigned in didInsertElement). let buttonId = target.attr('data-toggle-button__id'); // Sanity check if (buttonId) { // Get the actual toggle button element via Jquery. let button = Ember.$(buttonId); // Sanity check if (button) { // Add the offsets to the top and left and calculate the position of the toggle button. let topOffset = target.attr('data-toggle-button-top-offset'); let newTop = top + parseInt(topOffset); let leftOffset = target.attr('data-toggle-button-left-offset'); let newLeft = (left + width + parseInt(leftOffset)); // TODO: calculate open-offset and closed-offset // Use Jquery again this time to set the position of the toggle button. button.css('top', newTop.toString() + 'px'); button.css('left', newLeft.toString() + 'px'); } } } }); }, // Unhook our pollTimer when the toggle-button component is removed. didDestroyElement() { this._super(...arguments); clearInterval(this.pollTimer); }, /** * Fires when ANYTHING is clicked on the page. * * @param e - event */ click: function(e) { // Sanity check if (e) { // Check if what was clicked is the button and not anything else. let element= Ember.$(e.target); // Sanity check if (element) { // We are only interested if the "actual" button was clicked on. if (element.attr('id') === this.get('buttonId')) { let isOpen; if (element.hasClass('toggle-right')) { element.removeClass('toggle-right'); element.addClass('toggle-left'); isOpen = false; } else { element.removeClass('toggle-left'); element.addClass('toggle-right'); isOpen = true; } // Let any bound controller know that the state changed and what the target is. let target = Ember.$('.toggle-target_' + this.get('targetId')); if (target) { if (this.get('useDefaultToggleAction')) { this.defaultToggleAction(isOpen, target); } this.sendAction('toggleButtonClicked', isOpen, target); } } } } }, defaultToggleAction(isOpen, target) { if (isOpen) { let originalWidth = target.attr('data-original-width'); originalWidth = parseInt(originalWidth) + 'px'; target.animate({width: originalWidth}, 150); } else { target.attr('data-original-width', target.outerWidth()); target.animate({width: this.get('toggleWidth')}, 150); } } });