atheos-ide
Version:
Web-based IDE framework
396 lines (319 loc) • 12.5 kB
JavaScript
//////////////////////////////////////////////////////////////////////////////80
// Sidebar
//////////////////////////////////////////////////////////////////////////////80
// Copyright (c) Atheos & Liam Siira (Atheos.io), distributed as-is and without
// warranty under the MIT License. See [root]/LICENSE.md for more.
// This information must remain intact.
//////////////////////////////////////////////////////////////////////////////80
// Authors: Codiad Team, @Fluidbyte, Atheos Team, @hlsiira
//////////////////////////////////////////////////////////////////////////////80
// Notes:
// The opening and closing functions for each sidebar originally had some sort
// of jquery proxy function, a timeout, and a data method for storing reference
// to that timeout. Removing them seems to have had no ill effects. My guess is
// that it was an original attempt at the hoverIntent plugin, but who knows.
// Keeping this in mind in case I ever have to come back to it.
// JSFiddle Link: http://jsfiddle.net/npXQx/
//
// Currently, I'm not overly happy with the layout, but it is a lot easier to
// maintain I think. The left/right sidebars are seperate objects with their own
// functions.
//
// Need to implement changing the sidebar settings such as duration of hover and
// the trigger event.
//
// Sidebar module currently called from:
// Components/Active/init.js
// - Liam Siira
//////////////////////////////////////////////////////////////////////////////80
(function(global) {
'use strict';
var atheos = global.atheos;
var self = null;
var editor = null;
carbon.subscribe('system.loadMinor', () => atheos.sidebars.init());
atheos.sidebars = {
leftLockedVisible: true,
rightLockedVisible: false,
isLeftSidebarOpen: true,
isRightSidebarOpen: false,
leftTrigger: 'hover',
rightTrigger: 'hover',
hoverDuration: 300,
dragging: false,
//////////////////////////////////////////////////////////////////////
// Sidebar Initialization
//////////////////////////////////////////////////////////////////////
init: function() {
self = this;
editor = oX('#editor-region');
this.sbLeft.init();
this.sbRight.init();
carbon.subscribe('settings.loaded', function() {
var sbLeftWidth = atheos.storage('sidebars.sb-left-width'),
sbRightWidth = atheos.storage('sidebars.sb-right-width');
var handleWidth = oX('.handle').width(),
marginL = handleWidth,
marginR = handleWidth;
self.leftTrigger = atheos.storage('sidebars.leftTrigger') || 'hover';
self.rightTrigger = atheos.storage('sidebars.rightTrigger') || 'hover';
if (atheos.storage('sidebars.leftLockedVisible') === false) {
oX('#sb_left .lock').trigger('click');
}
if (atheos.storage('sidebars.rightLockedVisible') === true) {
oX('#sb_right .lock').trigger('click');
}
if (sbLeftWidth !== null) {
sbLeftWidth = parseInt(sbLeftWidth, 10);
oX('#sb_left').css('width', sbLeftWidth + 'px');
}
if (sbRightWidth !== null) {
sbRightWidth = parseInt(sbRightWidth, 10);
oX('#sb_right').css('width', sbRightWidth + 'px');
}
if (self.leftLockedVisible) {
marginL = oX('#sb_left').width();
} else if (sbLeftWidth !== null) {
oX('#sb_left').css('left', ((sbLeftWidth - handleWidth) * -1) + 'px');
self.sbLeft.close();
}
if (self.rightLockedVisible) {
marginR = oX('#sb_right').width();
self.sbRight.open();
} else if (sbRightWidth !== null) {
oX('#sb_right').css('right', ((sbRightWidth - handleWidth) * -1) + 'px');
}
editor.css({
'margin-left': marginL + 'px',
'margin-right': marginR + 'px',
});
});
},
//////////////////////////////////////////////////////////////////////
// Left Sidebar
//////////////////////////////////////////////////////////////////////
sbLeft: {
sidebar: null,
handle: null,
icon: null,
timeoutOpen: null,
timeoutClose: null,
hoverDuration: 300,
init: function() {
this.sidebar = oX('#sb_left');
this.handle = oX('#sb_left .handle');
this.icon = oX('#sb_left .lock');
this.hoverDuration = atheos.storage('sidebars.hoverDuration') || 300;
this.icon.on('click', function(e) {
self.sbLeft.lock();
});
this.handle.on('mousedown', () => {
self.resize(this.sidebar.el, 'left');
});
this.handle.on('click', function() {
if (!self.dragging && self.leftTrigger === 'click') {
self.sbLeft.open();
}
});
this.sidebar.on('mouseover', function() {
if (!self.dragging && self.leftTrigger === 'hover') {
self.sbLeft.open();
}
});
this.sidebar.on('mouseout', function() {
// Events is designed around event bubbling. Some events, like MouseLeave, don't bubble.
// In order to achieve MouseLeave with events, I needed to create a method that capture
// the mouseout event, and converts it into a mouseleave. This function checks if
// elment the mouse moved to is the target element, or a child of; if it is, then the
// mouse did not leave the parent element.
// InspiredBy: http://jsfiddle.net/amasad/TH9Hv/8/
var trigger = self.sbLeft.sidebar.el,
destination = event.toElement || event.relatedTarget,
mouseLeft = (destination === trigger) ? true : !trigger.contains(destination);
if (!self.dragging && mouseLeft) {
self.sbLeft.close();
}
});
},
open: function() {
var sidebarWidth = this.sidebar.width();
if (this.timeoutClose) {
clearTimeout(this.timeoutClose);
}
this.timeoutOpen = setTimeout((function() {
this.sidebar.css('left', '0px');
editor.css('margin-left', sidebarWidth + 'px');
setTimeout(function() {
atheos.sidebars.isLeftSidebarOpen = true;
atheos.active.updateTabDropdownVisibility();
editor.trigger('h-resize-root');
}, 300);
}).bind(this), this.hoverDuration);
},
close: function() {
var sidebarWidth = this.sidebar.width(),
sidebarHandleWidth = this.handle.width();
if (this.timeoutOpen) {
clearTimeout(this.timeoutOpen);
}
sidebarWidth = this.sidebar.width();
this.timeoutClose = setTimeout((function() {
if (!self.leftLockedVisible) {
this.sidebar.css('left', (-sidebarWidth + sidebarHandleWidth) + 'px');
editor.css('margin-left', '15px');
setTimeout(function() {
atheos.sidebars.isLeftSidebarOpen = false;
atheos.active.updateTabDropdownVisibility();
editor.trigger('h-resize-root');
}, 300);
}
}).bind(this), this.hoverDuration);
},
lock: function() {
if (self.leftLockedVisible) {
this.icon.replaceClass('fa-lock', 'fa-unlock');
this.handle.addClass('unlocked');
} else {
this.icon.replaceClass('fa-unlock', 'fa-lock');
this.handle.removeClass('unlocked');
}
self.leftLockedVisible = !(self.leftLockedVisible);
atheos.settings.save('sidebars.leftLockedVisible', self.leftLockedVisible, true);
atheos.storage('sidebars.leftLockedVisible', self.leftLockedVisible);
}
},
//////////////////////////////////////////////////////////////////////
// Right Sidebar
//////////////////////////////////////////////////////////////////////
sbRight: {
sidebar: null,
handle: null,
icon: null,
timeoutOpen: null,
timeoutClose: null,
hoverDuration: 300,
init: function() {
this.sidebar = oX('#sb_right');
this.handle = oX('#sb_right .handle');
this.icon = oX('#sb_right .lock');
this.hoverDuration = atheos.storage('sidebars.hoverDuration') || 300;
this.icon.on('click', function(e) {
self.sbRight.lock();
});
this.handle.on('mousedown', () => {
self.resize(this.sidebar.el, 'right');
});
this.handle.on('click', function() {
if (!self.dragging && self.rightTrigger === 'click') {
self.sbRight.open();
}
});
this.sidebar.on('mouseover', function() {
if (!self.dragging && self.rightTrigger === 'hover') {
self.sbRight.open();
}
});
this.sidebar.on('mouseout', function(e) {
// Events is designed around event bubbling. Some events, like MouseLeave, don't bubble.
// In order to achieve MouseLeave with events, I needed to create a method that capture
// the mouseout event, and converts it into a mouseleave. This function checks if
// elment the mouse moved to is the target element, or a child of; if it is, then the
// mouse did not leave the parent element.
// InspiredBy: http://jsfiddle.net/amasad/TH9Hv/8/
var trigger = self.sbRight.sidebar.el,
destination = event.toElement || event.relatedTarget,
mouseLeft = (destination === trigger) ? true : !trigger.contains(destination);
if (!self.dragging && mouseLeft) {
self.sbRight.close();
}
});
},
open: function() {
var sidebarWidth = this.sidebar.width();
if (this.sidebar.data && this.sidebar.data.timeoutClose) {
clearTimeout(this.sidebar.data.timeoutClose);
}
if (this.timeoutClose) {
clearTimeout(this.timeoutClose);
}
this.timeoutOpen = setTimeout((function() {
this.sidebar.css('right', '0px');
editor.css('margin-right', sidebarWidth + 'px');
setTimeout(function() {
self.isRightSidebarOpen = true;
atheos.active.updateTabDropdownVisibility();
editor.trigger('h-resize-root');
}, 300);
}).bind(this), this.hoverDuration);
},
close: function() {
var sidebarWidth = this.sidebar.width(),
sidebarHandleWidth = this.handle.width();
if (this.timeoutOpen) {
clearTimeout(this.timeoutOpen);
}
this.timeoutClose = setTimeout((function() {
if (!self.rightLockedVisible) {
this.sidebar.css('right', -(sidebarWidth - sidebarHandleWidth) + 'px');
editor.css('margin-right', '15px');
setTimeout(function() {
self.isRightSidebarOpen = false;
atheos.active.updateTabDropdownVisibility();
editor.trigger('h-resize-root');
}, 300);
}
}).bind(this), this.hoverDuration);
},
lock: function() {
if (self.rightLockedVisible) {
this.icon.replaceClass('fa-lock', 'fa-unlock');
this.handle.addClass('unlocked');
} else {
this.icon.replaceClass('fa-unlock', 'fa-lock');
this.handle.removeClass('unlocked');
}
self.rightLockedVisible = !(self.rightLockedVisible);
atheos.settings.save('sidebars.rightLockedVisible', self.rightLockedVisible, true);
atheos.storage('sidebars.rightLockedVisible', self.rightLockedVisible);
}
},
//////////////////////////////////////////////////////////////////////
// Sidebar Resize Function
//////////////////////////////////////////////////////////////////////
resize: function(sidebar, side) {
//References: http://jsfiddle.net/8wtq17L8/ & https://jsfiddle.net/tovic/Xcb8d/
if (sidebar === null) {
return;
}
var rect = sidebar.getBoundingClientRect(),
modalX = rect.left,
ed = editor.el,
width;
ed.style.transition = 'none';
self.dragging = true;
var moveElement = function(event) {
if (side === 'left') {
width = (modalX + event.clientX + 10);
} else {
width = (window.innerWidth - event.clientX + 10);
}
width = width > 14 ? width + 'px' : '15px';
sidebar.style.width = width;
ed.style['margin-' + side] = width;
};
function removeListeners() {
setTimeout(function() {
ed.style.transition = '';
editor.css('margin-' + side, width);
atheos.settings.save('sidebars.sb-' + side + '-width', width);
}, 200);
atheos.storage('sidebars.sb-' + side + '-width', width);
self.dragging = false;
document.removeEventListener('mousemove', moveElement, false);
document.removeEventListener('mouseup', removeListeners, false);
}
document.addEventListener('mousemove', moveElement, false);
document.addEventListener('mouseup', removeListeners, false);
}
};
})(this);