Better click outside handling

This commit is contained in:
Mo Bitar
2017-12-26 12:21:27 -06:00
parent 8569098e8a
commit fc4abbbaf6
11 changed files with 138 additions and 28 deletions

View File

@@ -606,7 +606,14 @@ class ComponentManager {
}
component.window = componentWindow;
component.sessionKey = Neeto.crypto.generateUUID();
this.sendMessageToComponent(component, {action: "component-registered", sessionKey: component.sessionKey, componentData: component.componentData});
this.sendMessageToComponent(component, {
action: "component-registered",
sessionKey: component.sessionKey,
componentData: component.componentData,
data: {
environment: isDesktopApplication() ? "desktop" : "web"
}
});
this.postThemeToComponent(component);
}

View File

@@ -1,27 +1,102 @@
// via https://github.com/IamAdamJowett/angular-click-outside
angular
.module('app.frontend')
.directive('clickOutside', ['$document', function($document) {
.directive('clickOutside', ['$document', '$parse', '$timeout', function($document, $parse, $timeout) {
return {
restrict: 'A',
replace: false,
link : function($scope, $element, attrs) {
link: function($scope, elem, attr) {
$timeout(() => {
// postpone linking to next digest to allow for unique id generation
var classList = (attr.outsideIfNot !== undefined) ? attr.outsideIfNot.split(/[ ,]+/) : [],
fn;
var didApplyClickOutside = false;
function eventHandler(e) {
var i,
element,
r,
id,
classNames,
l;
$element.bind('click', function(e) {
didApplyClickOutside = false;
if (attrs.isOpen) {
e.stopPropagation();
// check if our element already hidden and abort if so
if (angular.element(elem).hasClass("ng-hide")) {
return;
}
// if there is no click target, no point going on
if (!e || !e.target) {
return;
}
// loop through the available elements, looking for classes in the class list that might match and so will eat
for (element = e.target; element; element = element.parentNode) {
// check if the element is the same element the directive is attached to and exit if so (props @CosticaPuntaru)
if (element === elem[0]) {
return;
}
// now we have done the initial checks, start gathering id's and classes
id = element.id,
classNames = element.className,
l = classList.length;
// Unwrap SVGAnimatedString classes
if (classNames && classNames.baseVal !== undefined) {
classNames = classNames.baseVal;
}
// if there are no class names on the element clicked, skip the check
if (classNames || id) {
// loop through the elements id's and classnames looking for exceptions
for (i = 0; i < l; i++) {
//prepare regex for class word matching
r = new RegExp('\\b' + classList[i] + '\\b');
// check for exact matches on id's or classes, but only if they exist in the first place
if ((id !== undefined && id === classList[i]) || (classNames && r.test(classNames))) {
// now let's exit out as it is an element that has been defined as being ignored for clicking outside
return;
}
}
}
}
// if we have got this far, then we are good to go with processing the command passed in via the click-outside attribute
$timeout(function() {
fn = $parse(attr['clickOutside']);
fn($scope, { event: e });
});
}
});
$document.bind('click', function() {
if(!didApplyClickOutside) {
$scope.$apply(attrs.clickOutside);
didApplyClickOutside = true;
// if the devices has a touchscreen, listen for this event
if (_hasTouch()) {
$document.on('touchstart', eventHandler);
}
})
}
// still listen for the click event even if there is touch to cater for touchscreen laptops
$document.on('click', eventHandler);
// when the scope is destroyed, clean up the documents event handlers as we don't want it hanging around
$scope.$on('$destroy', function() {
if (_hasTouch()) {
$document.off('touchstart', eventHandler);
}
$document.off('click', eventHandler);
});
/**
* @description Private function to attempt to figure out if we are on a touch device
* @private
**/
function _hasTouch() {
// works on most browsers, IE10/11 and Surface
return 'ontouchstart' in window || navigator.maxTouchPoints;
};
});
}
}]);
}
}]);

View File

@@ -33,7 +33,6 @@ class ComponentModal {
if(component.active) {
$timeout(function(){
var iframe = componentManager.iframeForComponent(component);
console.log("iframe", iframe, component);
if(iframe) {
iframe.onload = function() {
componentManager.registerComponentWindow(component, iframe.contentWindow);
@@ -44,7 +43,6 @@ class ComponentModal {
},
actionHandler: function(component, action, data) {
if(action == "set-size") {
console.log("componentModalReceivedAction SetSize", component);
componentManager.handleSetSizeEvent(component, data);
}
}.bind(this)});

View File

@@ -22,7 +22,7 @@ class EditorMenu {
if(editor) {
editor.conflict_of = null; // clear conflict if applicable
if(editor.local && !isDesktopApplication()) {
alert("This editor is installed ")
alert("This editor is installed locally and is available only through Standard Notes for Desktop.")
return;
}
}

View File

@@ -26,12 +26,19 @@ class PermissionsModal {
$scope.callback(false);
$scope.dismiss();
}
}
controller($scope, modelManager) {
$scope.formattedPermissions = $scope.permissions.map(function(permission){
if(permission.name === "stream-items") {
var title = "Access to ";
var types = permission.content_types.map(function(type){
return (type + "s").toLowerCase();
var desc = modelManager.humanReadableDisplayForContentType(type);
if(desc) {
return desc + "s";
} else {
return "items of type " + type;
}
})
var typesString = "";
var separator = ", ";

View File

@@ -47,10 +47,14 @@ class RoomBar {
if(room.show) {
this.componentManager.activateComponent(room);
} else {
this.componentManager.deactivateComponent(room);
$scope.hideRoom(room);
}
}
$scope.hideRoom = function(room) {
room.show = false;
this.componentManager.deactivateComponent(room);
}
}

View File

@@ -400,6 +400,25 @@ class ModelManager {
return JSON.stringify(data, null, 2 /* pretty print */);
}
/*
Misc
*/
humanReadableDisplayForContentType(contentType) {
return {
"Note" : "note",
"Tag" : "tag",
"Extension" : "action-based extension",
"SN|Component" : "component",
"SN|Editor" : "editor",
"SN|Theme" : "theme",
"SF|Extension" : "server extension",
"SF|MFA" : "server multi-factor authentication setting"
}[contentType];
}
}
angular.module('app.frontend').service('modelManager', ModelManager);