diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js index 651edf5bfd..f83f441d66 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbtabbedcontent.directive.js @@ -13,6 +13,18 @@ //expose the property/methods for other directives to use this.content = $scope.content; + $scope.activeVariant = _.find(this.content.variants, variant => { + return variant.active; + }); + + $scope.defaultVariant = _.find(this.content.variants, variant => { + return variant.language.isDefault; + }); + + $scope.unlockInvariantValue = function(property) { + property.unlockInvariantValue = !property.unlockInvariantValue; + }; + $scope.$watch("tabbedContentForm.$dirty", function (newValue, oldValue) { if (newValue === true) { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js index 69457a6f10..302378b8c0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js @@ -7,7 +7,9 @@ angular.module("umbraco.directives") .directive('umbProperty', function (umbPropEditorHelper, userService) { return { scope: { - property: "=" + property: "=", + showInherit: "<", + inheritsFrom: "<" }, transclude: true, restrict: 'E', diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js index 0aa2dc02c3..32cbbb31ec 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbpropertyeditor.directive.js @@ -12,7 +12,7 @@ function umbPropEditor(umbPropEditorHelper) { scope: { model: "=", isPreValue: "@", - preview: "@" + preview: "<" }, require: "^^form", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js new file mode 100644 index 0000000000..800efb8c28 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/util/disabletabindex.directive.js @@ -0,0 +1,46 @@ +angular.module("umbraco.directives") + .directive('disableTabindex', function (tabbableService) { + + return { + restrict: 'A', //Can only be used as an attribute, + scope: { + "disableTabindex": "<" + }, + link: function (scope, element, attrs) { + + if(scope.disableTabindex) { + //Select the node that will be observed for mutations (native DOM element not jQLite version) + var targetNode = element[0]; + + //Watch for DOM changes - so when the property editor subview loads in + //We can be notified its updated the child elements inside the DIV we are watching + var observer = new MutationObserver(domChange); + + // Options for the observer (which mutations to observe) + var config = { attributes: true, childList: true, subtree: false }; + + function domChange(mutationsList, observer){ + for(var mutation of mutationsList) { + + //DOM items have been added or removed + if (mutation.type == 'childList') { + + //Check if any child items in mutation.target contain an input + var childInputs = tabbableService.tabbable(mutation.target); + + //For each item in childInputs - override or set HTML attribute tabindex="-1" + angular.forEach(childInputs, function(element){ + angular.element(element).attr('tabindex', '-1'); + }); + } + } + } + + // Start observing the target node for configured mutations + //GO GO GO + observer.observe(targetNode, config); + } + + } + }; +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js new file mode 100644 index 0000000000..4d8d5f68f3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/tabbable.service.js @@ -0,0 +1,223 @@ +//tabbable JS Lib (Wrapped in angular service) +//https://github.com/davidtheclark/tabbable + +(function() { + 'use strict'; + + function tabbableService() { + + var candidateSelectors = [ + 'input', + 'select', + 'textarea', + 'a[href]', + 'button', + '[tabindex]', + 'audio[controls]', + 'video[controls]', + '[contenteditable]:not([contenteditable="false"])' + ]; + var candidateSelector = candidateSelectors.join(','); + + var matches = typeof Element === 'undefined' + ? function () {} + : Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; + + function tabbable(el, options) { + options = options || {}; + + var elementDocument = el.ownerDocument || el; + var regularTabbables = []; + var orderedTabbables = []; + + var untouchabilityChecker = new UntouchabilityChecker(elementDocument); + var candidates = el.querySelectorAll(candidateSelector); + + if (options.includeContainer) { + if (matches.call(el, candidateSelector)) { + candidates = Array.prototype.slice.apply(candidates); + candidates.unshift(el); + } + } + + var i, candidate, candidateTabindex; + for (i = 0; i < candidates.length; i++) { + candidate = candidates[i]; + + if (!isNodeMatchingSelectorTabbable(candidate, untouchabilityChecker)) continue; + + candidateTabindex = getTabindex(candidate); + if (candidateTabindex === 0) { + regularTabbables.push(candidate); + } else { + orderedTabbables.push({ + documentOrder: i, + tabIndex: candidateTabindex, + node: candidate + }); + } + } + + var tabbableNodes = orderedTabbables + .sort(sortOrderedTabbables) + .map(function(a) { return a.node }) + .concat(regularTabbables); + + return tabbableNodes; + } + + tabbable.isTabbable = isTabbable; + tabbable.isFocusable = isFocusable; + + function isNodeMatchingSelectorTabbable(node, untouchabilityChecker) { + if ( + !isNodeMatchingSelectorFocusable(node, untouchabilityChecker) + || isNonTabbableRadio(node) + || getTabindex(node) < 0 + ) { + return false; + } + return true; + } + + function isTabbable(node, untouchabilityChecker) { + if (!node) throw new Error('No node provided'); + if (matches.call(node, candidateSelector) === false) return false; + return isNodeMatchingSelectorTabbable(node, untouchabilityChecker); + } + + function isNodeMatchingSelectorFocusable(node, untouchabilityChecker) { + untouchabilityChecker = untouchabilityChecker || new UntouchabilityChecker(node.ownerDocument || node); + if ( + node.disabled + || isHiddenInput(node) + || untouchabilityChecker.isUntouchable(node) + ) { + return false; + } + return true; + } + + var focusableCandidateSelector = candidateSelectors.concat('iframe').join(','); + function isFocusable(node, untouchabilityChecker) { + if (!node) throw new Error('No node provided'); + if (matches.call(node, focusableCandidateSelector) === false) return false; + return isNodeMatchingSelectorFocusable(node, untouchabilityChecker); + } + + function getTabindex(node) { + var tabindexAttr = parseInt(node.getAttribute('tabindex'), 10); + if (!isNaN(tabindexAttr)) return tabindexAttr; + // Browsers do not return `tabIndex` correctly for contentEditable nodes; + // so if they don't have a tabindex attribute specifically set, assume it's 0. + if (isContentEditable(node)) return 0; + return node.tabIndex; + } + + function sortOrderedTabbables(a, b) { + return a.tabIndex === b.tabIndex ? a.documentOrder - b.documentOrder : a.tabIndex - b.tabIndex; + } + + // Array.prototype.find not available in IE. + function find(list, predicate) { + for (var i = 0, length = list.length; i < length; i++) { + if (predicate(list[i])) return list[i]; + } + } + + function isContentEditable(node) { + return node.contentEditable === 'true'; + } + + function isInput(node) { + return node.tagName === 'INPUT'; + } + + function isHiddenInput(node) { + return isInput(node) && node.type === 'hidden'; + } + + function isRadio(node) { + return isInput(node) && node.type === 'radio'; + } + + function isNonTabbableRadio(node) { + return isRadio(node) && !isTabbableRadio(node); + } + + function getCheckedRadio(nodes) { + for (var i = 0; i < nodes.length; i++) { + if (nodes[i].checked) { + return nodes[i]; + } + } + } + + function isTabbableRadio(node) { + if (!node.name) return true; + // This won't account for the edge case where you have radio groups with the same + // in separate forms on the same page. + var radioSet = node.ownerDocument.querySelectorAll('input[type="radio"][name="' + node.name + '"]'); + var checked = getCheckedRadio(radioSet); + return !checked || checked === node; + } + + // An element is "untouchable" if *it or one of its ancestors* has + // `visibility: hidden` or `display: none`. + function UntouchabilityChecker(elementDocument) { + this.doc = elementDocument; + // Node cache must be refreshed on every check, in case + // the content of the element has changed. The cache contains tuples + // mapping nodes to their boolean result. + this.cache = []; + } + + // getComputedStyle accurately reflects `visibility: hidden` of ancestors + // but not `display: none`, so we need to recursively check parents. + UntouchabilityChecker.prototype.hasDisplayNone = function hasDisplayNone(node, nodeComputedStyle) { + if (node === this.doc.documentElement) return false; + + // Search for a cached result. + var cached = find(this.cache, function(item) { + return item === node; + }); + if (cached) return cached[1]; + + nodeComputedStyle = nodeComputedStyle || this.doc.defaultView.getComputedStyle(node); + + var result = false; + + if (nodeComputedStyle.display === 'none') { + result = true; + } else if (node.parentNode) { + result = this.hasDisplayNone(node.parentNode); + } + + this.cache.push([node, result]); + + return result; + } + + UntouchabilityChecker.prototype.isUntouchable = function isUntouchable(node) { + if (node === this.doc.documentElement) return false; + var computedStyle = this.doc.defaultView.getComputedStyle(node); + if (this.hasDisplayNone(node, computedStyle)) return true; + return computedStyle.visibility === 'hidden'; + } + + //module.exports = tabbable; + + //////////// + + var service = { + tabbable: tabbable, + isTabbable: isTabbable, + isFocusable: isFocusable + }; + + return service; + } + + angular.module('umbraco.services').factory('tabbableService', tabbableService); + + })(); diff --git a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js index 2912755ce7..5d8f46c6db 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js @@ -194,7 +194,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar $rootScope.emptySection = false; } } - + })); //Listen for section state changes @@ -222,10 +222,21 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar }); })); - evts.push(eventsService.on("editors.languages.languageCreated", function (e, args) { - loadLanguages().then(function (languages) { - $scope.languages = languages; - }); + //Emitted when a language is created or an existing one saved/edited + evts.push(eventsService.on("editors.languages.languageSaved", function (e, args) { + console.log('lang event listen args', args); + if(args.isNew){ + //A new language has been created - reload languages for tree + loadLanguages().then(function (languages) { + $scope.languages = languages; + }); + } + else if(args.language.isDefault){ + //A language was saved and was set to be the new default (refresh the tree, so its at the top) + loadLanguages().then(function (languages) { + $scope.languages = languages; + }); + } })); //This reacts to clicks passed to the body element which emits a global call to close all dialogs diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 3100433a4a..b21255a85f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -170,6 +170,7 @@ // Utilities @import "utilities/layout/_display.less"; +@import "utilities/theme/_opacity.less"; @import "utilities/typography/_text-decoration.less"; @import "utilities/typography/_white-space.less"; @import "utilities/_flexbox.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor.less index f045b0adca..d699193c24 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor.less @@ -201,7 +201,6 @@ a.umb-variant-switcher__toggle { .umb-variant-switcher__name { display: block; - font-weight: bold; } .umb-variant-switcher__state { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less index cbea6987e7..8a43c95e6e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-editor.less @@ -1,3 +1,4 @@ -.umb-property-editor.-not-clickable { +.umb-property-editor--preview { pointer-events: none; -} + user-select: none; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/utilities/theme/_opacity.less b/src/Umbraco.Web.UI.Client/src/less/utilities/theme/_opacity.less new file mode 100644 index 0000000000..4550827cdc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/utilities/theme/_opacity.less @@ -0,0 +1,19 @@ +/* + + Opacity + +*/ + +.o-100 { opacity: 1; } +.o-90 { opacity: 0.9; } +.o-80 { opacity: 0.8; } +.o-70 { opacity: 0.7; } +.o-60 { opacity: 0.6; } +.o-50 { opacity: 0.5; } +.o-40 { opacity: 0.4; } +.o-30 { opacity: 0.3; } +.o-20 { opacity: 0.2; } +.o-10 { opacity: 0.1; } +.o-05 { opacity: 0.05; } +.o-025 { opacity: 0.025; } +.o-0 { opacity: 0; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html index 570ea3990b..ddb0396f34 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html @@ -11,7 +11,7 @@
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html index b125457ab0..cdacea7cf1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/content/umb-tabbed-content.html @@ -8,8 +8,20 @@