Changes how tabs work ... to no longer have to rely on bootstrap tabs and the hacky timing work-arounds, tabs are actually quite easy and now give us much more control.

Have made this change as backwards compatable (some things are deprecated though). Will update the other editors now.
This commit is contained in:
Shannon
2015-07-30 18:44:15 +02:00
parent e4286bc35b
commit cfa2a30006
9 changed files with 236 additions and 29 deletions

View File

@@ -0,0 +1,21 @@
/**
* @ngdoc directive
* @name umbraco.directives.directive:umbEditorButtons
* @restrict E
* @function
* @description
* The button holder for editors (i.e. save, update, etc...)
**/
angular.module("umbraco.directives")
.directive('umbEditorButtons', function () {
return {
require: "^umbTabs",
restrict: 'E',
transclude: true,
template: '<div class="umb-tab-buttons" detect-fold ng-class="{\'umb-dimmed\': busy}" ng-transclude></div>',
link: function(scope, element, attrs, ngModel) {
}
};
});

View File

@@ -0,0 +1,82 @@
/**
* @ngdoc directive
* @name umbraco.directives.directive:umbTabs
* @restrict E
* @function
* @description
* Used to control tab changes in editors, this directive must be placed around any editors' tabs (including header tabs and body tabs)
**/
angular.module("umbraco.directives")
.directive('umbTabs', function() {
return {
//uses a controller to expose an API
controller: function ($scope, $attrs, $element, $parse) {
//NOTE: This whole $parse strategy is the same type of thing used by ngModel, we need to use it
// because angular 1.1.5 is stupid. Normally we could just make a scope with =umbTags but that doesn't
// work for attribute type directives, seems to only work for element directives and I don't want this to
// be an element directive.... so we're using this.
var tabsValueGet = $parse($attrs.umbTabs);
//internal props
var activeTabId = null;
var tabCollectionChangedCallbacks = [];
var activeTabChangedCallbacks = [];
var firstRun = false;
var tabs = [];
//public props/methods
this.getActiveTab = function() {
return activeTabId;
};
this.setActiveTab = function(tabId) {
activeTabId = tabId;
for (var callback in activeTabChangedCallbacks) {
activeTabChangedCallbacks[callback](activeTabId);
}
};
this.onTabCollectionChanged = function (callback) {
tabCollectionChangedCallbacks.push(callback);
};
this.onActiveTabChanged = function (callback) {
activeTabChangedCallbacks.push(callback);
};
$scope.$watch(function() {
return tabsValueGet($scope);
}, function (newValue, oldValue) {
if (newValue !== oldValue || (angular.isArray(newValue) && angular.isArray(oldValue) && newValue.length !== oldValue.length)) {
tabs = []; //reset first
for (var val in newValue) {
var tab = { id: newValue[val].id, label: newValue[val].label };
tabs.push(tab);
}
//set active tab to the first one - one time
if (firstRun === false) {
firstRun = true;
if (tabs.length > 0) {
activeTabId = tabs[0].id;
for (var activeTabCallback in activeTabChangedCallbacks) {
activeTabChangedCallbacks[activeTabCallback](activeTabId);
}
}
}
for (var callback in tabCollectionChangedCallbacks) {
tabCollectionChangedCallbacks[callback](tabs);
}
}
});
$scope.$on('$destroy', function () {
tabCollectionChangedCallbacks = [];
activeTabChangedCallbacks = [];
});
}
};
});

View File

@@ -1,5 +1,14 @@
/**
* @ngdoc directive
* @name umbraco.directives.directive:umbHeader
* @restrict E
* @function
* @deprecated since version 7.3 - use umbTabHeader instead
* @description
* The header on an editor that contains tabs using bootstrap tabs - THIS IS OBSOLETE, use umbTabHeader instead
**/
angular.module("umbraco.directives")
.directive('umbHeader', function($parse, $timeout){
.directive('umbHeader', function ($parse, $timeout) {
return {
restrict: 'E',
replace: true,

View File

@@ -4,11 +4,39 @@
* @restrict E
**/
angular.module("umbraco.directives")
.directive('umbTab', function(){
return {
.directive('umbTab', function ($parse) {
return {
require: "?^umbTabs",
restrict: 'E',
replace: true,
transclude: 'true',
templateUrl: 'views/directives/umb-tab.html'
};
scope: {
id : "@",
tabId : "@rel"
},
transclude: 'true',
templateUrl: 'views/directives/umb-tab.html',
link: function(scope, elem, attrs, tabsCtrl) {
function toggleVisibility(tabId) {
if (scope.tabId === String(tabId)) {
elem.show();
}
else {
elem.hide();
}
}
//need to make this optional for backwards compat since before we used to
// use bootstrap tabs and now we use our own faster implementation which
// gives us far more control but will still support the old way.
if (tabsCtrl != null) {
tabsCtrl.onActiveTabChanged(function (tabId) {
toggleVisibility(tabId);
});
toggleVisibility(tabsCtrl.getActiveTab());
}
}
};
});

View File

@@ -0,0 +1,41 @@
/**
* @ngdoc directive
* @name umbraco.directives.directive:umbTabHeader
* @restrict E
* @function
* @description
* The header on an editor that contains tabs
**/
angular.module("umbraco.directives").directive('umbTabHeader', function($parse, $timeout) {
return {
require: "^umbTabs",
restrict: 'E',
replace: true,
transclude: 'true',
templateUrl: 'views/directives/umb-tab-header.html',
link: function(scope, iElement, iAttrs, tabsCtrl) {
scope.showTabs = false;
scope.activeTabId = null;
scope.tabs = [];
tabsCtrl.onTabCollectionChanged(function (tabs) {
scope.tabs = tabs;
scope.showTabs = scope.tabs.length > 0;
});
tabsCtrl.onActiveTabChanged(function (tabId) {
scope.activeTabId = tabId;
});
scope.changeTab = function(tabId) {
tabsCtrl.setActiveTab(tabId);
};
$timeout(function() {
//TODO: We'll need to destroy this I'm assuming!
$('.nav-pills, .nav-tabs').tabdrop();
}, 500);
}
};
});

View File

@@ -74,16 +74,19 @@ angular.module("umbraco.directives")
}
}));
$(window).bind("resize", function () {
var resizeCallback = function() {
_.debounce(function() {
//set the global app state
appState.setGlobalState("isTablet", ($(window).width() <= minScreenSize));
setTreeMode();
}, 100);
};
//set the global app state
appState.setGlobalState("isTablet", ($(window).width() <= minScreenSize));
setTreeMode();
});
$(window).bind("resize", resizeCallback);
//ensure to unregister from all events and kill jquery plugins
scope.$on('$destroy', function () {
$(window).unbind("resize", resizeCallback);
for (var e in evts) {
eventsService.unsubscribe(evts[e]);
}
@@ -91,7 +94,6 @@ angular.module("umbraco.directives")
navInnerContainer.resizable("destroy");
});
//init
//set the global app state
appState.setGlobalState("isTablet", ($(window).width() <= minScreenSize));

View File

@@ -12,7 +12,10 @@ angular.module("umbraco.directives.html")
var state = false,
parent = $(".umb-panel-body"),
winHeight = $(window).height(),
calculate = _.throttle(function(){
calculate = _.debounce(function () {
console.log("calculating...");
if(el && el.is(":visible") && !el.hasClass("umb-bottom-bar")){
//var parent = el.parent();
var hasOverflow = parent.innerHeight() < parent[0].scrollHeight;
@@ -22,7 +25,7 @@ angular.module("umbraco.directives.html")
}
}
return state;
}, 1000);
}, 500);
scope.$watch(calculate, function(newVal, oldVal) {
if(newVal !== oldVal){
@@ -34,12 +37,19 @@ angular.module("umbraco.directives.html")
}
});
$(window).bind("resize", function () {
winHeight = $(window).height();
el.removeClass("umb-bottom-bar");
state = false;
calculate();
});
var resizeCallback = function() {
winHeight = $(window).height();
el.removeClass("umb-bottom-bar");
state = false;
calculate();
};
$(window).bind("resize", resizeCallback);
//ensure to unbind!
scope.$on('$destroy', function () {
$(window).unbind("resize", resizeCallback);
});
$('a[data-toggle="tab"]').on('shown', function (e) {
calculate();

View File

@@ -4,8 +4,8 @@
ng-submit="save()"
val-form-manager>
<umb-panel ng-class="{'editor-breadcrumb': ancestors && ancestors.length > 0}">
<umb-header tabs="content.tabs">
<umb-panel umb-tabs="content.tabs" ng-class="{'editor-breadcrumb': ancestors && ancestors.length > 0}">
<umb-tab-header>
<div class="span7">
<umb-content-name placeholder="@placeholders_entername"
@@ -25,7 +25,7 @@
</div>
</div>
</umb-header>
</umb-tab-header>
<umb-tab-view>
<umb-tab id="tab{{tab.id}}" rel="{{tab.id}}" ng-repeat="tab in content.tabs">
@@ -34,9 +34,8 @@
ng-repeat="property in tab.properties">
<umb-editor model="property"></umb-editor>
</umb-property>
<div class="umb-tab-buttons" detect-fold ng-class="{'umb-dimmed': busy}">
<umb-editor-buttons>
<div class="btn-group" ng-show="listViewPath">
<a class="btn" href="#{{listViewPath}}">
@@ -72,12 +71,13 @@
</ul>
</div>
</div>
</umb-editor-buttons>
</div>
</umb-tab>
</umb-tab-view>
<ul class="umb-panel-footer-nav nav nav-pills" ng-if="ancestors && ancestors.length > 0">
<li ng-repeat="ancestor in ancestors">
<a href="#/content/content/edit/{{ancestor.id}}">{{ancestor.name}}</a>

View File

@@ -0,0 +1,14 @@
<div class="umb-panel-header">
<div class="row-fluid">
<div ng-transclude></div>
<ul ng-show="showTabs" class="nav nav-tabs umb-nav-tabs span12">
<li ng-class="{active: activeTabId == tab.id, 'tab-error': tabHasError}" ng-repeat="tab in tabs" val-tab>
<a href="#tab{{tab.id}}" ng-click="changeTab(tab.id)" prevent-default>{{ tab.label }}</a>
</li>
</ul>
</div>
</div>