Resizable panels and user prefs

This commit is contained in:
Mo Bitar
2017-12-27 12:17:29 -06:00
parent 38f2e345d9
commit 8c15438d00
16 changed files with 603 additions and 163 deletions

View File

@@ -36,3 +36,7 @@ function parametersFromURL(url) {
function isDesktopApplication() {
return window && window.process && window.process.type && window.process.versions["electron"];
}
function isMacApplication() {
return window && window.process && window.process.type && window.process.platform == "darwin";
}

View File

@@ -50,13 +50,20 @@ angular.module('app.frontend')
this.showMenu = false;
this.loadTagsString();
let onReady = () => {
this.noteReady = true;
$timeout(() => {
this.loadPreferences();
})
}
let associatedEditor = this.editorForNote(note);
if(associatedEditor) {
// setting note to not ready will remove the editor from view in a flash,
// so we only want to do this if switching between external editors
this.noteReady = false;
} else {
this.noteReady = true;
onReady();
}
if(this.editorComponent && this.editorComponent != associatedEditor) {
@@ -71,10 +78,10 @@ angular.module('app.frontend')
$timeout(() => {
this.enableComponent(associatedEditor);
this.editorComponent = associatedEditor;
this.noteReady = true;
onReady();
})
} else {
this.noteReady = true;
onReady();
}
if(note.safeText().length == 0 && note.dummy) {
@@ -124,6 +131,10 @@ angular.module('app.frontend')
this.note.setAppDataItem("prefersPlainEditor", true);
this.note.setDirty(true);
syncManager.sync();
$timeout(() => {
this.reloadFont();
})
}
this.editorComponent = editorComponent;
@@ -322,7 +333,73 @@ angular.module('app.frontend')
}
/* Resizability */
this.resizeControl = {};
this.onPanelResizeFinish = function(width, left, isMaxWidth) {
if(isMaxWidth) {
authManager.setUserPrefValue("editorWidth", null);
} else {
if(width !== undefined && width !== null) {
authManager.setUserPrefValue("editorWidth", width);
}
}
if(left !== undefined && left !== null) {
authManager.setUserPrefValue("editorLeft", left);
}
authManager.syncUserPreferences();
}
$rootScope.$on("user-preferences-changed", () => {
this.loadPreferences();
});
this.loadPreferences = function() {
this.monospaceFont = authManager.getUserPrefValue("monospaceFont", "monospace");
if(!document.getElementById("editor-content")) {
// Elements have not yet loaded due to ng-if around wrapper
return;
}
this.reloadFont();
let width = authManager.getUserPrefValue("editorWidth", null);
if(width !== null) {
this.resizeControl.setWidth(width);
}
let left = authManager.getUserPrefValue("editorLeft", null);
if(left !== null) {
this.resizeControl.setLeft(left);
}
}
this.reloadFont = function() {
var editable = document.getElementById("note-text-editor");
if(!editable) {
return;
}
if(this.monospaceFont) {
if(isMacApplication()) {
editable.style.fontFamily = "Menlo, Consolas, 'DejaVu Sans Mono', monospace";
} else {
editable.style.fontFamily = "monospace";
}
} else {
editable.style.fontFamily = "inherit";
}
}
this.toggleKey = function(key) {
this[key] = !this[key];
authManager.setUserPrefValue(key, this[key], true);
this.reloadFont();
}

View File

@@ -33,21 +33,61 @@ angular.module('app.frontend')
})
.controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager, storageManager) {
this.sortBy = storageManager.getItem("sortBy") || "created_at";
this.showArchived = storageManager.getBooleanValue("showArchived") || false;
this.sortDescending = this.sortBy != "title";
this.panelController = {};
$rootScope.$on("user-preferences-changed", () => {
this.loadPreferences();
});
this.loadPreferences = function() {
this.sortBy = authManager.getUserPrefValue("sortBy", "created_at");
this.sortDescending = this.sortBy != "title";
this.showArchived = authManager.getUserPrefValue("showArchived", false);
this.hidePinned = authManager.getUserPrefValue("hidePinned", false);
this.hideNotePreview = authManager.getUserPrefValue("hideNotePreview", false);
this.hideDate = authManager.getUserPrefValue("hideDate", false);
this.hideTags = authManager.getUserPrefValue("hideTags", false);
let width = authManager.getUserPrefValue("notesPanelWidth");
if(width) {
this.panelController.setWidth(width);
}
}
this.loadPreferences();
this.onPanelResize = function(newWidth) {
authManager.setUserPrefValue("notesPanelWidth", newWidth);
authManager.syncUserPreferences();
}
angular.element(document).ready(() => {
this.loadPreferences();
});
$rootScope.$on("editorFocused", function(){
this.showMenu = false;
}.bind(this))
$rootScope.$on("noteDeleted", function() {
this.selectFirstNote(false);
$timeout(this.onNoteRemoval.bind(this));
}.bind(this))
$rootScope.$on("noteArchived", function() {
this.selectFirstNote(false);
}.bind(this))
$timeout(this.onNoteRemoval.bind(this));
}.bind(this));
// When a note is removed from the list
this.onNoteRemoval = function() {
let visibleNotes = this.visibleNotes();
if(this.selectedIndex < visibleNotes.length) {
this.selectNote(visibleNotes[this.selectedIndex]);
} else {
this.selectNote(visibleNotes[visibleNotes.length - 1]);
}
}
this.DefaultNotesToDisplayValue = 20;
@@ -56,26 +96,39 @@ angular.module('app.frontend')
this.notesToDisplay += this.DefaultNotesToDisplayValue
}
this.panelTitle = function() {
if(this.noteFilter.text.length) {
return `${this.tag.notes.filter((i) => {return i.visible;}).length} search results`;
} else if(this.tag) {
return `${this.tag.title} notes`;
}
}
this.optionsSubtitle = function() {
var base = "Sorting by";
var base = "";
if(this.sortBy == "created_at") {
base += " date added";
base += " Date Added";
} else if(this.sortBy == "updated_at") {
base += " date modifed";
base += " Date Modifed";
} else if(this.sortBy == "title") {
base += " title";
base += " Title";
}
if(this.showArchived && (!this.tag || !this.tag.archiveTag)) {
base += " | Including archived"
base += " | + Archived"
}
if(this.hidePinned) {
base += " | Pinned"
}
return base;
}
this.toggleShowArchived = function() {
this.showArchived = !this.showArchived;
storageManager.setBooleanValue("showArchived", this.showArchived);
this.toggleKey = function(key) {
this[key] = !this[key];
authManager.setUserPrefValue(key, this[key]);
authManager.syncUserPreferences();
}
this.tagDidChange = function(tag, oldTag) {
@@ -108,10 +161,14 @@ angular.module('app.frontend')
this.selectFirstNote(createNew);
}
this.selectFirstNote = function(createNew) {
var visibleNotes = this.sortedNotes.filter(function(note){
this.visibleNotes = function() {
return this.sortedNotes.filter(function(note){
return note.visible;
});
}
this.selectFirstNote = function(createNew) {
var visibleNotes = this.visibleNotes();
if(visibleNotes.length > 0) {
this.selectNote(visibleNotes[0]);
@@ -121,6 +178,7 @@ angular.module('app.frontend')
}
this.selectNote = function(note) {
if(!note) { return; }
this.selectedNote = note;
note.conflict_of = null; // clear conflict
this.selectionMade()(note);
@@ -142,7 +200,7 @@ angular.module('app.frontend')
return note.visible;
}
if(note.archived && !this.showArchived) {
if((note.archived && !this.showArchived) || (note.pinned && this.hidePinned)) {
note.visible = false;
return note.visible;
}
@@ -188,7 +246,8 @@ angular.module('app.frontend')
this.setSortBy = function(type) {
this.sortBy = type;
storageManager.setItem("sortBy", type);
authManager.setUserPrefValue("sortBy", this.sortBy);
authManager.syncUserPreferences();
}
});

View File

@@ -34,10 +34,30 @@ angular.module('app.frontend')
}
}
})
.controller('TagsCtrl', function ($rootScope, modelManager, $timeout, componentManager) {
.controller('TagsCtrl', function ($rootScope, modelManager, $timeout, componentManager, authManager) {
var initialLoad = true;
this.panelController = {};
$rootScope.$on("user-preferences-changed", () => {
this.loadPreferences();
});
this.loadPreferences = function() {
let width = authManager.getUserPrefValue("tagsPanelWidth");
if(width) {
this.panelController.setWidth(width);
}
}
this.loadPreferences();
this.onPanelResize = function(newWidth) {
authManager.setUserPrefValue("tagsPanelWidth", newWidth);
authManager.syncUserPreferences();
}
this.componentManager = componentManager;
componentManager.registerHandler({identifier: "tags", areas: ["tags-list"], activationHandler: function(component){

View File

@@ -298,6 +298,7 @@ angular.module('app.frontend')
singletonManager.registerSingleton({content_type: prefsContentType}, (resolvedSingleton) => {
console.log("AuthManager received resolved UserPreferences", resolvedSingleton);
this.userPreferences = resolvedSingleton;
this.userPreferencesDidChange();
}, () => {
// Safe to create. Create and return object.
var prefs = new Item({content_type: prefsContentType});
@@ -308,5 +309,28 @@ angular.module('app.frontend')
return prefs;
});
this.userPreferencesDidChange = function() {
$rootScope.$broadcast("user-preferences-changed");
}
this.syncUserPreferences = function() {
this.userPreferences.setDirty(true);
$rootScope.sync();
}
this.getUserPrefValue = function(key, defaultValue) {
if(!this.userPreferences) { return; }
var value = this.userPreferences.getAppDataItem(key);
return (value !== undefined && value != null) ? value : defaultValue;
}
this.setUserPrefValue = function(key, value, sync) {
if(!this.userPreferences) { console.log("Prefs are null, not setting value", key); return; }
this.userPreferences.setAppDataItem(key, value);
if(sync) {
this.syncUserPreferences();
}
}
}
});

View File

@@ -0,0 +1,201 @@
class PanelResizer {
constructor() {
this.restrict = "E";
this.templateUrl = "frontend/directives/panel-resizer.html";
this.scope = {
index: "=",
panelId: "=",
onResize: "&",
onResizeFinish: "&",
control: "=",
alwaysVisible: "=",
minWidth: "=",
property: "=",
hoverable: "=",
collapsable: "="
};
}
link(scope, elem, attrs, ctrl) {
scope.elem = elem;
scope.control.setWidth = function(value) {
scope.setWidth(value, true);
}
scope.control.setLeft = function(value) {
scope.setLeft(value);
}
}
controller($scope, $element, modelManager, extensionManager) {
'ngInject';
let panel = document.getElementById($scope.panelId);
if(!panel) {
console.log("Panel not found for", $scope.panelId);
}
let resizerColumn = $element[0];
let resizerWidth = resizerColumn.offsetWidth;
let minWidth = $scope.minWidth || resizerWidth;
function getParentRect() {
return panel.parentNode.getBoundingClientRect();
}
var pressed = false;
var startWidth = panel.scrollWidth, startX, lastDownX, collapsed, lastWidth = startWidth, startLeft, lastLeft;
let appFrame = document.getElementById("app").getBoundingClientRect();
if($scope.alwaysVisible) {
console.log("Adding always visible", $scope.alwaysVisible);
resizerColumn.classList.add("always-visible");
}
if($scope.hoverable) {
resizerColumn.classList.add("hoverable");
}
$scope.setWidth = function(width, finish) {
if(width < minWidth) {
width = minWidth;
}
let parentRect = getParentRect();
if(width > parentRect.width) {
width = parentRect.width;
}
if(width == parentRect.width) {
panel.style.width = "100%";
panel.style.flexBasis = "100%";
} else {
panel.style.flexBasis = width + "px";
panel.style.width = width + "px";
}
lastWidth = width;
if(finish) {
$scope.finishSettingWidth();
}
}
$scope.setLeft = function(left) {
panel.style.left = left + "px";
lastLeft = left;
}
$scope.finishSettingWidth = function() {
if(!$scope.collapsable) {
return;
}
if(lastWidth <= minWidth) {
collapsed = true;
} else {
collapsed = false;
}
if(collapsed) {
resizerColumn.classList.add("collapsed");
} else {
resizerColumn.classList.remove("collapsed");
}
}
resizerColumn.addEventListener("mousedown", function(event){
pressed = true;
lastDownX = event.clientX;
startWidth = panel.scrollWidth;
startLeft = panel.offsetLeft;
panel.classList.add("no-selection");
if($scope.hoverable) {
resizerColumn.classList.add("dragging");
}
})
document.addEventListener("mousemove", function(event){
if(!pressed) {
return;
}
event.preventDefault();
if($scope.property && $scope.property == 'left') {
handleLeftEvent(event);
} else {
handleWidthEvent(event);
}
})
function handleWidthEvent(event) {
var rect = panel.getBoundingClientRect();
var panelMaxX = rect.left + (startWidth || panel.style.maxWidth);
var x = event.clientX;
let deltaX = x - lastDownX;
var newWidth = startWidth + deltaX;
$scope.setWidth(newWidth, false);
if($scope.onResize()) {
$scope.onResize()(lastWidth, panel);
}
}
function handleLeftEvent(event) {
var panelRect = panel.getBoundingClientRect();
var x = event.clientX;
let deltaX = x - lastDownX;
var newLeft = startLeft + deltaX;
if(newLeft < 0) {
newLeft = 0;
deltaX = -startLeft;
}
let parentRect = getParentRect();
var newWidth = startWidth - deltaX;
if(newWidth < minWidth) {
newWidth = minWidth;
}
if(newWidth > parentRect.width) {
newWidth = parentRect.width;
}
if(newLeft + newWidth > parentRect.width) {
newLeft = parentRect.width - newWidth;
}
$scope.setLeft(newLeft, false);
$scope.setWidth(newWidth, false);
}
document.addEventListener("mouseup", function(event){
if(pressed) {
pressed = false;
resizerColumn.classList.remove("dragging");
panel.classList.remove("no-selection");
let isMaxWidth = lastWidth == getParentRect().width;
if($scope.onResizeFinish) {
$scope.onResizeFinish()(lastWidth, lastLeft, isMaxWidth);
}
$scope.finishSettingWidth();
}
})
}
}
angular.module('app.frontend').directive('panelResizer', () => new PanelResizer);