diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbbackdrop.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbbackdrop.directive.js index 7139cd5682..e45074d003 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbbackdrop.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbbackdrop.directive.js @@ -16,7 +16,7 @@ function onInit() { - if (scope.element) { + if (scope.highlightElement) { setHighlight(); } @@ -27,13 +27,13 @@ $timeout(function () { // The element to highlight - var highlightElement = angular.element(scope.element); + var highlightElement = angular.element(scope.highlightElement); - if(highlightElement) { + if(highlightElement && highlightElement.length > 0) { var offset = highlightElement.offset(); - var width = highlightElement.outerWidth(true); - var height = highlightElement.outerHeight(true); + var width = highlightElement.outerWidth(); + var height = highlightElement.outerHeight(); // Rounding numbers var topDistance = offset.top.toFixed(); @@ -53,6 +53,12 @@ rectBottom.css({ "height": "100%", "y": topAndHeight, "x": leftDistance }); rectLeft.css({ "width": leftDistance }); + // Prevent interaction in the highlighted area + if(scope.highlightPreventClick) { + var preventClickElement = el.find(".umb-backdrop__highlight-prevent-click"); + preventClickElement.css({ "width": width, "height": height, "left": offset.left, "top": offset.top }); + } + } }); @@ -63,7 +69,7 @@ setHighlight(); } - events.push(scope.$watch("element", function (newValue, oldValue) { + events.push(scope.$watch("highlightElement", function (newValue, oldValue) { if(!newValue) {return;} if(newValue === oldValue) {return;} setHighlight(); @@ -90,8 +96,10 @@ templateUrl: "views/components/application/umb-backdrop.html", link: link, scope: { - element: "=", - disableEventsOnClick: "=" + backdropOpacity: "=?", + highlightElement: "=?", + highlightPreventClick: "=?", + disableEventsOnClick: "=?", } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js index 12696f9441..b6b40492be 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour.directive.js @@ -1,48 +1,46 @@ (function () { 'use strict'; - function TourDirective($timeout, $http, tourService, backdropService) { + function TourDirective($timeout, $http, $q, tourService, backdropService) { function link(scope, el, attr, ctrl) { var popover; - scope.totalSteps; - scope.currentStepIndex; - scope.currentStep; scope.loadingStep = false; + scope.elementNotFound = false; - scope.nextStep = function() { + scope.model.nextStep = function() { nextStep(); }; - scope.endTour = function() { + scope.model.endTour = function() { unbindEvent(); tourService.endTour(); backdropService.close(); }; - scope.completeTour = function() { + scope.model.completeTour = function() { unbindEvent(); - tourService.completeTour(scope.tour); + tourService.completeTour(scope.model); backdropService.close(); }; function onInit() { popover = el.find(".umb-tour__popover"); - scope.totalSteps = scope.tour.steps.length; - scope.currentStepIndex = 0; + popover.hide(); + scope.model.currentStepIndex = 0; backdropService.open({disableEventsOnClick: true}); startStep(); } function setView() { - if (scope.currentStep.view && scope.tour.alias) { + if (scope.model.currentStep.view && scope.model.alias) { //we do this to avoid a hidden dialog to start loading unconfigured views before the first activation - var configuredView = scope.currentStep.view; - if (scope.currentStep.view.indexOf(".html") === -1) { - var viewAlias = scope.currentStep.view.toLowerCase(); - var tourAlias = scope.tour.alias.toLowerCase(); + var configuredView = scope.model.currentStep.view; + if (scope.model.currentStep.view.indexOf(".html") === -1) { + var viewAlias = scope.model.currentStep.view.toLowerCase(); + var tourAlias = scope.model.alias.toLowerCase(); configuredView = "views/common/tours/" + tourAlias + "/" + viewAlias + "/" + viewAlias + ".html"; } if (configuredView !== scope.configuredView) { @@ -54,93 +52,113 @@ } function nextStep() { - scope.currentStepIndex++; - if(scope.currentStepIndex !== scope.tour.steps.length) { + popover.hide(); + scope.model.currentStepIndex++; + // make sure we don't go too far + if(scope.model.currentStepIndex !== scope.model.steps.length) { startStep(); + // tour completed - final step + } else { + scope.loadingStep = true; + + waitForPendingRerequests().then(function(){ + scope.loadingStep = false; + // clear current step + scope.model.currentStep = {}; + // set popover position to center + setPopoverPosition(null); + // remove backdrop hightlight and custom opacity + backdropService.setHighlight(null); + backdropService.setOpacity(null); + }); } } function startStep() { + scope.loadingStep = true; + backdropService.setOpacity(scope.model.steps[scope.model.currentStepIndex].backdropOpacity); + backdropService.setHighlight(null); - // we need to make sure that all requests are done - var timer = window.setInterval(function(){ + waitForPendingRerequests().then(function() { - console.log("pending", $http.pendingRequests.length); - console.log("document ready", document.readyState); + scope.model.currentStep = scope.model.steps[scope.model.currentStepIndex]; + + setView(); - scope.loadingStep = true; - - backdropService.setHighlight(null); - - // check for pending requests both in angular and on the document - if($http.pendingRequests.length === 0 && document.readyState === "complete") { - console.log("Everything is DONE JOHN"); - - scope.currentStep = scope.tour.steps[scope.currentStepIndex]; - - clearInterval(timer); - - setView(); - - positionPopover(); - - // if a custom event needs to be bound we do it now - if(scope.currentStep.event) { - bindEvent(); - } - - scope.loadingStep = false; + // if highlight element is set - find it + findHighlightElement(); + // if a custom event needs to be bound we do it now + if(scope.model.currentStep.event) { + bindEvent(); } - - }, 50); + scope.loadingStep = false; + + }); } - function positionPopover() { + function findHighlightElement() { + + scope.elementNotFound = false; $timeout(function () { - var element = $(scope.currentStep.element); - var scrollParent = element.scrollParent(); + // if an element isn't set - show the popover in the center + if(!scope.model.currentStep.element) { + setPopoverPosition(null); + return; + } - console.log("scrollParent", scrollParent); + var element = angular.element(scope.model.currentStep.element); + + // we couldn't find the element in the dom - abort and show error + if(element.length === 0) { + scope.elementNotFound = true; + setPopoverPosition(null); + return; + } + + var scrollParent = element.scrollParent(); // Detect if scroll is needed if (element[0].offsetTop > scrollParent[0].clientHeight) { - console.log("SCROOOOOOOL"); scrollParent.animate({ scrollTop: element[0].offsetTop }, function () { // Animation complete. - console.log("ANIMATION COMPLETE"); - _position(); - backdropService.setHighlight(scope.currentStep.element); + setPopoverPosition(element); + backdropService.setHighlight(scope.model.currentStep.element, scope.model.currentStep.elementPreventClick); }); } else { - _position(); - backdropService.setHighlight(scope.currentStep.element); + setPopoverPosition(element); + backdropService.setHighlight(scope.model.currentStep.element, scope.model.currentStep.elementPreventClick); } }); - function _position() { + } - $timeout(function () { + function setPopoverPosition(element) { + + $timeout(function () { + + var position = "center"; + var margin = 20; + var css = {}; + + var popoverWidth = popover.outerWidth(); + var popoverHeight = popover.outerHeight(); + var popoverOffset = popover.offset(); + var documentWidth = angular.element(document).width(); + var documentHeight = angular.element(document).height(); + + if(element) { - var element = $(scope.currentStep.element); var offset = element.offset(); - var width = element.outerWidth(true); - var height = element.outerHeight(true); + var width = element.outerWidth(); + var height = element.outerHeight(); - var popoverWidth = popover.outerWidth(true); - var popoverHeight = popover.outerHeight(true); - var popoverOffset = popover.offset(); - var documentWidth = $(document).width(); - var documentHeight = $(document).height(); - var position; - var css = {}; - // messure available space on each side of the target element var space = { "top": offset.top, @@ -149,53 +167,74 @@ "left": offset.left }; - console.log("SPACE", space); - console.log("document width", documentWidth); - console.log("document height", documentHeight); - // get the posistion with most available space position = findMax(space); - if(position === "top") { - if (offset.left < documentWidth/2) { - css = {top: offset.top - popoverHeight, left: offset.left}; + if (position === "top") { + if (offset.left < documentWidth / 2) { + css.top = offset.top - popoverHeight - margin; + css.left = offset.left; } else { - css = {top: offset.top - popoverHeight, left: offset.left - popoverWidth + width}; + css.top = offset.top - popoverHeight - margin; + css.left = offset.left - popoverWidth + width; } } - if(position === "right") { - if (offset.top < documentHeight/2) { - css = {top: offset.top, left: offset.left + width}; + if (position === "right") { + if (offset.top < documentHeight / 2) { + css.top = offset.top; + css.left = offset.left + width + margin; } else { - css = {top: offset.top + height - popoverHeight, left: offset.left + width}; + css.top = offset.top + height - popoverHeight; + css.left = offset.left + width + margin; } } - if(position === "bottom") { - if (offset.left < documentWidth/2) { - css = {top: offset.top + height, left: offset.left}; + if (position === "bottom") { + if (offset.left < documentWidth / 2) { + css.top = offset.top + height + margin; + css.left = offset.left; } else { - css = {top: offset.top + height, left: offset.left - popoverWidth + width}; + css.top = offset.top + height + margin; + css.left = offset.left - popoverWidth + width; } } - if(position === "left") { - if (offset.top < documentHeight/2) { - css = {top: offset.top, left: offset.left - popoverWidth}; + if (position === "left") { + if (offset.top < documentHeight / 2) { + css.top = offset.top; + css.left = offset.left - popoverWidth - margin; } else { - css = {top: offset.top + height - popoverHeight, left: offset.left - popoverWidth}; + css.top = offset.top + height - popoverHeight; + css.left = offset.left - popoverWidth - margin; } } - popover.css(css); + } else { + // if there is no dom element center the popover + css.top = "calc(50% - " + popoverHeight/2 + "px)"; + css.left = "calc(50% - " + popoverWidth/2 + "px)"; + } - scope.position = position; + popover.css(css).fadeIn("fast"); + + }); - }); - } + } + function waitForPendingRerequests() { + var deferred = $q.defer(); + var timer = window.setInterval(function(){ + // check for pending requests both in angular and on the document + if($http.pendingRequests.length === 0 && document.readyState === "complete") { + $timeout(function(){ + deferred.resolve(); + clearInterval(timer); + }); + } + }, 50); + return deferred.promise; } function findMax(obj) { @@ -211,35 +250,52 @@ } function bindEvent() { - var eventName = scope.currentStep.event + ".step-" + scope.currentStepIndex; - if(scope.currentStep.eventElement) { - $(scope.currentStep.eventElement).on(eventName, handleEvent); - console.log("bind", eventName); - } else { - $(scope.currentStep.element).on(eventName, handleEvent); - console.log("bind", eventName); + + var bindToElement = scope.model.currentStep.element; + var eventName = scope.model.currentStep.event + ".step-" + scope.model.currentStepIndex; + var removeEventName = "remove.step-" + scope.model.currentStepIndex; + var handled = false; + + if(scope.model.currentStep.eventElement) { + bindToElement = scope.model.currentStep.eventElement; } + + $(bindToElement).on(eventName, function(){ + if(!handled) { + unbindEvent(); + nextStep(); + handled = true; + } + }); + + // Hack: we do this to handle cases where ng-if is used and removes the element we need to click. + // for some reason it seems the elements gets removed before the event is raised. This is a temp solution which assumes: + // "if you ask me to click on an element, and it suddenly gets removed from the dom, let's go on to the next step". + $(bindToElement).on(removeEventName, function () { + if(!handled) { + unbindEvent(); + nextStep(); + handled = true; + } + }); + } function unbindEvent() { - var eventName = scope.currentStep.event + ".step-" + scope.currentStepIndex; - if(scope.currentStep.eventElement) { - $(scope.currentStep.eventElement).off(eventName); - console.log("unbind", eventName); + var eventName = scope.model.currentStep.event + ".step-" + scope.model.currentStepIndex; + var removeEventName = "remove.step-" + scope.model.currentStepIndex; + + if(scope.model.currentStep.eventElement) { + angular.element(scope.model.currentStep.eventElement).off(eventName); + angular.element(scope.model.currentStep.eventElement).off(removeEventName); } else { - $(scope.currentStep.element).off(eventName); - console.log("unbind", eventName); + angular.element(scope.model.currentStep.element).off(eventName); + angular.element(scope.model.currentStep.element).off(removeEventName); } } - function handleEvent() { - //alert("event happened"); - unbindEvent(); - nextStep(); - } - function resize() { - positionPopover(); + findHighlightElement(); } onInit(); @@ -248,6 +304,7 @@ scope.$on('$destroy', function () { $(window).off('resize.umbTour'); + unbindEvent(); }); } @@ -259,7 +316,7 @@ templateUrl: 'views/components/application/umb-tour.html', link: link, scope: { - tour: "=" + model: "=" } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstep.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstep.directive.js index 039d8bb838..e840634865 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstep.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstep.directive.js @@ -7,7 +7,10 @@ restrict: 'E', replace: true, transclude: true, - templateUrl: 'views/components/application/umbtour/umb-tour-step.html' + templateUrl: 'views/components/application/umbtour/umb-tour-step.html', + scope: { + size: "@?" + } }; return directive; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstepheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstepheader.directive.js index 60c052e2e3..9d32ad87a4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstepheader.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstepheader.directive.js @@ -6,6 +6,7 @@ var directive = { restrict: 'E', replace: true, + transclude: true, templateUrl: 'views/components/application/umbtour/umb-tour-step-header.html', scope: { title: "=" diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js index b503648317..4b0bdb6c71 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js @@ -149,7 +149,8 @@ Use this directive to render an umbraco button. The directive can be used to gen labelKey: "@?", icon: "@?", disabled: "=", - size: "@?" + size: "@?", + alias: "@?" } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/backdrop.service.js b/src/Umbraco.Web.UI.Client/src/common/services/backdrop.service.js index 3cdd1f2b21..b0053dc4bb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/backdrop.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/backdrop.service.js @@ -4,7 +4,9 @@ function backdropService(eventsService) { var args = { + opacity: null, element: null, + elementPreventClick: false, disableEventsOnClick: false, show: false }; @@ -25,21 +27,26 @@ } function close() { - args.element = null; args.show = false; - eventsService.emit("appState.backdrop", args); } - function setHighlight(element) { + function setOpacity(opacity) { + args.opacity = opacity; + eventsService.emit("appState.backdrop", args); + } + + function setHighlight(element, preventClick) { args.element = element; + args.elementPreventClick = preventClick; eventsService.emit("appState.backdrop", args); } var service = { open: open, close: close, + setOpacity: setOpacity, setHighlight: setHighlight }; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index 694cc00148..2c3dafd8c1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -134,7 +134,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica labelKey: "buttons_saveAndPublish", handler: args.methods.saveAndPublish, hotKey: "ctrl+p", - hotKeyWhenHidden: true + hotKeyWhenHidden: true, + alias: "saveAndPublish" }; case "H": //send to publish @@ -143,7 +144,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica labelKey: "buttons_saveToPublish", handler: args.methods.sendToPublish, hotKey: "ctrl+p", - hotKeyWhenHidden: true + hotKeyWhenHidden: true, + alias: "sendToPublish" }; case "A": //save @@ -152,7 +154,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica labelKey: "buttons_save", handler: args.methods.save, hotKey: "ctrl+s", - hotKeyWhenHidden: true + hotKeyWhenHidden: true, + alias: "save" }; case "Z": //unpublish @@ -161,7 +164,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica labelKey: "content_unPublish", handler: args.methods.unPublish, hotKey: "ctrl+u", - hotKeyWhenHidden: true + hotKeyWhenHidden: true, + alias: "unpublish" }; default: return null; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js index de6ef7f93d..aad11af94a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js @@ -6,40 +6,109 @@ var localStorageKey = "umbTours"; var tours = [ + { + "name": "Introduction", + "alias": "umbIntroIntroduction", + "group": "Getting Started", + "steps": [ + { + title: "Welcome to Umbraco - The Friendly CMS", + content: "
Thank you for choosing Umbraco - we think this could be the beginning of something beautiful. While it may feel overwhelming at first, we've done a lot to make the learning curve as smooth and fast as possible.
In this quick tour we will introduce you to the main areas of Umbraco and show you how to best get started.
", + type: "intro" + }, + { + element: "#applications", + elementPreventClick: true, + title: "Sections", + content: "These are the Sections and allows you to navigate the different areas of Umbraco.", + backdropOpacity: 0.6 + }, + { + element: "#tree", + elementPreventClick: true, + title: "The Tree", + content: "This is the Tree and will contain all the content of your website." + }, + { + element: "[data-element='global-search-field']", + title: "Search", + content: "The search allows you to quickly find content across sections within Umbraco." + }, + { + element: "#applications [data-element='section-user']", + title: "User profile", + content: "Click on the user photo to open the user profile dialog.", + event: "click", + backdropOpacity: 0.6 + }, + { + element: "[data-element~='overlay-user']", + elementPreventClick: true, + title: "User profile", + content: "This is where you can see details about your user, change your password and log out of Umbraco.
In the User section you will be able to do more advaned user management.
" + }, + { + element: "[data-element~='overlay-user'] [data-element='button-overlayClose']", + title: "User profile", + content: "Let's close the user profile again", + event: "click" + }, + { + element: "#applications [data-element='section-help']", + title: "Help", + content: "If you ever find yourself in trouble click here to open the help drawer.", + event: "click", + backdropOpacity: 0.6 + }, + { + element: "[data-element='drawer']", + elementPreventClick: true, + title: "Help", + content: "In the help drawer you will find articles and videos related to the section you are using.
This is also where you will find the next tour on how to get started with Umbraco.
", + backdropOpacity: 0.6 + } + ] + }, { "name": "Create document type", "alias": "umbIntroCreateDocType", "group": "Getting Started", "steps": [ + { + title: "Create your first Document Type", + content: "Step 1 of any site is to create a Document Type. A Document Type is a data container where you can add data fields. The editor can then input data and Umbraco can use it to output it in the relevant parts of a template.
In this tour you will learn how to set up a basic Document Type with a data field to enter a short text.
", + type: "intro" + }, { element: "#applications [data-element='section-settings']", title: "Navigate to the settings sections", - content: "In the settings section we will find the document types", - event: "click" + content: "In the Settings section we will find the document types.", + event: "click", + backdropOpacity: 0.6 }, { element: "#tree [data-element='tree-item-documentTypes']", title: "Create document type", - content: "Hover the document types tree and click the three small dots to open the context menu
", + content: "Hover the document types tree and click the three small dots to open the context menu.
", event: "click", eventElement: "#tree [data-element='tree-item-documentTypes'] [data-element='tree-item-options']" }, { element: "#dialog [data-element='action-documentType']", title: "Create document type", - content: "Click Document Type to create a new document type with a template", + content: "Click Document Type to create a new document type with a template.
We will use the template in a later tour when we need to render our content.
", event: "click" }, { element: "[data-element='editor-name-field']", title: "Enter a name", - content: "Our document type needs a name. Enter Home in the field and click Next
", + content: "Our document type needs a name. Enter Home in the field and click Next.",
view: "doctypename"
},
{
element: "[data-element='editor-description']",
title: "Enter a description",
- content: "
A description helps the content editor pick the right document type when creating content:
The home to our website" + content: "
A description helps to pick the right document type when creating content.
Write a description to our Home page. It could be:
The home to our website" }, { element: "[data-element='group-add']", @@ -50,63 +119,65 @@ { element: "[data-element='group-name']", title: "Enter a name", - content: "Enter Content in the tab name" + content: "Enter
Content in the tab name."
},
{
element: "[data-element='property-add']",
title: "Add a property",
- content: "Properties are the different types of data on our content page.
On our Home page we wan't to add a welcome text.
Click Add property to open the property dialog
", + content: "Properties are the different types of data on our content page.
On our Home page we wan't to add a welcome text.
Click Add property to open the property dialog.
", event: "click" }, { element: "[data-element~='overlay-property-settings'] [data-element='property-name']", title: "Enter a name", - content: "Enter Welcome Text as name for the property", + content: "EnterWelcome Text as name for the property.",
view: "propertyname"
},
{
element: "[data-element~='overlay-property-settings'] [data-element='property-description']",
title: "Enter a description",
- content: "Enter a description for the property editor:Write a nice introduction text so the visitors feel welcome" + content: "
A description will help to fill in the right content.
Enter a description for the property editor. It could be:
Write a nice introduction text so the visitors feel welcome" }, { element: "[data-element~='overlay-property-settings'] [data-element='editor-add']", title: "Add editor", - content: "The editor define what data type the property is. Click Add editor to open the editor picker dialog", + content: "The editor defines what data type the property is. Click Add editor to open the editor picker dialog.", event: "click" }, { element: "[data-element~='overlay-editor-picker']", + elementPreventClick: true, title: "Editor picker", content: "
In the editor picker dialog we can pick one of the many build in editor.
" }, { - element: "[data-element~='overlay-editor-picker'] [data-element='editor-Umbraco.TextboxMultiple']", + element: "[data-element~='overlay-editor-picker'] [data-element='editor-Textarea']", title: "Select editor", - content: "Select the Textarea editor which allows the content editor to enter long texts", + content: "Select the Textarea editor which allows us to enter long texts.", event: "click" }, { element: "[data-element~='overlay-editor-settings']", + elementPreventClick: true, title: "Editor settings", - content: "Each editor can have individual settings. We don't need to change any of these now." + content: "Each property editor can have individual settings. We don't want to change any of these now." }, { - element: "[data-element~='overlay-editor-settings'] [data-element='overlay-submit']", + element: "[data-element~='overlay-editor-settings'] [data-element='button-overlaySubmit']", title: "Save editor", - content: "Click Submit to save the editor and any changes you may have made to the editor settings.", + content: "Click Submit to save the editor.", event: "click" }, { - element: "[data-element~='overlay-property-settings'] [data-element='overlay-submit']", - title: "Save property", - content: "Click Submit to add the property", + element: "[data-element~='overlay-property-settings'] [data-element='button-overlaySubmit']", + title: "Add property to document type", + content: "Click Submit to add the property to the document type.", event: "click" }, { - element: "[data-element='button-group-primary']", + element: "[data-element='button-save']", title: "Save the document type", - content: "Click Save to create your document type", + content: "All we need now is to save the document type. Click Save to create and save your new document type.", event: "click" } ] @@ -116,34 +187,39 @@ "alias": "umbIntroCreateContent", "group": "Getting Started", "steps": [ + { + title: "Creating your first content node", + content: "The Content section contains the content of the website. Content is displayed as nodes in the content tree.
In this tour we will learn how to create our Home page for our website.
", + type: "intro" + }, { element: "[data-element='tree-root']", title: "Open context menu", - content: "Open the context menu by hovering the root of the content section.
Now click the three small dots to the right
", + content: "Open the context menu by hovering the root of the content section.
Now click the three small dots to the right.
", event: "click", eventElement: "[data-element='tree-root'] [data-element='tree-item-options']" }, { element: "[data-element='action-create-home']", title: "Create Home page", - content: "Click on Home to create a new page of type Home
", + content: "Click on Home to create a new page of type Home.
", event: "click" }, { element: "[data-element='editor-content'] [data-element='editor-name-field']", title: "Give your new page a name", - content: "Our new page needs a name. Enter Home in the field and click Next
", + content: "Our new page needs a name. Enter Home in the field and click Next.
Add content to the Welcome Text field
If you don't have any ideas here is a start:
I am learning Umbraco. High Five I Rock #H5IR" + content: "
Add content to the Welcome Text field
If you don't have any ideas here is a start:
I am learning Umbraco. High Five I Rock #H5IR." }, { - element: "[data-element='editor-content'] [data-element='button-group-primary']", + element: "[data-element='editor-content'] [data-element='button-saveAndPublish']", title: "Save and publish", - content: "
Now click the Save and publish button to save and publish your changes
", + content: "Now click the Save and publish button to save and publish your changes.
", event: "click" } ] @@ -153,16 +229,22 @@ "alias": "umbIntroRenderInTemplate", "group": "Getting Started", "steps": [ + { + title: "Render your content in a template", + content: "Templating in Umbraco builds on the concept of Razor Views from asp.net MVC. - This tour is a sneak peak on how to write templates in Umbraco.
In this tour we will learn how to render content from our Home document type so we can see the content added to our Home page.
", + type: "intro" + }, { element: "#applications [data-element='section-settings']", - title: "Navigate to the settings section", - content: "In the Settings section you will find the templates for all your document types", - event: "click" + title: "Navigate to the Settings section", + content: "In the Settings section you will find all the templates
It is of course also possible to edit all your code files in your favorite code editor.
", + event: "click", + backdropOpacity: 0.6 }, { element: "#tree [data-element='tree-item-templates']", title: "Expand the Templates node", - content: "To see all our templates click the small triangle to the left of the templates node
", + content: "To see all our templates click the small triangle to the left of the templates node.
", event: "click", eventElement: "#tree [data-element='tree-item-templates'] [data-element='tree-item-expand']", view: "templatetree" @@ -170,18 +252,19 @@ { element: "#tree [data-element='tree-item-templates'] [data-element='tree-item-Home']", title: "Open Home template", - content: "Click the Home template to open and edit it
", + content: "Click the Home template to open and edit it.
", + eventElement: "#tree [data-element='tree-item-templates'] [data-element='tree-item-Home'] a.umb-tree-item__label", event: "click" }, { element: "[data-element='editor-templates'] [data-element='code-editor']", title: "Edit template", - content: 'Templates can be edited here or in your favorite code editor.
To render the value we entered on the Home page add the following to the template:
@Model.Content.GetPropertyValue("welcomeText")'
+ content: 'The template can be edited here or in your favorite code editor.
To render the field from the document type add the following to the template:
@Model.Content.GetPropertyValue("welcomeText")'
},
{
element: "[data-element='editor-templates'] [data-element='button-save']",
title: "Save the template",
- content: "Click the save button and your template will be saved",
+ content: "Click the Save button and your template will be saved.",
event: "click"
}
]
@@ -191,22 +274,28 @@
"alias": "umbIntroViewHomePage",
"group": "Getting Started",
"steps": [
+ {
+ title: "View your Umbraco site",
+ content: "Our three main components to a page is done: Document type, Template, and Content - it is now time to see the result.
In this tour we will learn how to see our published website.
", + type: "intro" + }, { element: "#tree [data-element='tree-item-Home']", title: "Open the Home page", content: "Click the Home page to open it
", - event: "click" + event: "click", + eventElement: "#tree [data-element='tree-item-Home'] a.umb-tree-item__label" }, { element: "[data-element='editor-content'] [data-element='tab-Generic properties']", title: "Properties", - content: "Under the properties tab you will find default information about the content item
", + content: "Under the properties tab you will find the default information about a content item.
", event: "click" }, { element: "[data-element='editor-content'] [data-element='property-_umb_urls']", title: "Open page", - content: "Click the Link to view your page.
Tip: Click the preview button in the bottom right corner to preview changes without publishing them
", + content: "Click the Link to document to view your page.
Tip: Click the preview button in the bottom right corner to preview changes without publishing them.
", event: "click", eventElement: "[data-element='editor-content'] [data-element='property-_umb_urls'] a[target='_blank']" } @@ -217,29 +306,35 @@ "alias": "umbIntroMediaSection", "group": "Getting Started", "steps": [ + { + title: "How to use the media library", + content: "A website would be boring without media content. In Umbraco you can manage all your images, documents, videos etc. in the Media section. Here you can upload and organise your media items and see details about each item.
In this tour we will learn how to upload and orginise your Media library in Umbraco. It will also show you how to view details about a specific media item.
", + type: "intro" + }, { element: "#applications [data-element='section-media']", title: "Navigate to the media section", - content: "The media section is where you will manage all your media items", - event: "click" + content: "The media section is where you will manage all your media items.", + event: "click", + backdropOpacity: 0.6 }, { element: "#tree [data-element='tree-root']", title: "Create a new folder", - content: "Hover the media root and click the three small dots on the right side of the item
", + content: "Let's first create a folder for our images. Hover the media root and click the three small dots on the right side of the item.
", event: "click", eventElement: "#tree [data-element='tree-root'] [data-element='tree-item-options']" }, { element: "#dialog [data-element='action-Folder']", title: "Create a new folder", - content: "Select the Folder options to create a new folder
", + content: "Select the Folder options to select the type folder.
", event: "click" }, { element: "[data-element='editor-media'] [data-element='editor-name-field']", title: "Enter a name", - content: "Enter 'My folder' in the field
" + content: "Enter My folder in the field.
In the upload area you can upload your media items.
Click the Upload button and select some images on your computer and upload them.
", + content: "In the upload area you can upload your media items.
Click the Upload button and select a couple of images on your computer and upload them.
", view: "uploadimages" }, { @@ -263,17 +358,17 @@ { element: "[data-element='editor-media'] [data-element='property-umbracoFile']", title: "The uploaded image", - content: "Here you can see the image you have uploaded.
Use the dot in the center of the image to set a focal point on the image.
" + content: "Here you can see the image you have uploaded.
You can use the dot in the center of the image to set a focal point on the image.
" }, { element: "[data-element='editor-media'] [data-element='property-umbracoBytes']", title: "Image size", - content: "You will also find other details about the image, like the size
You can add extra properties to an image by creating or editing the Media types
" + content: "You will also find other details about the image, like the size.
You can add extra properties to an image by creating or editing the Media types
" }, { element: "[data-element='editor-media'] [data-element='tab-Generic properties']", title: "Properties", - content: "Like the content section you can also find default properties about the media item. You will find these under the properties tab", + content: "Like the content section you can also find default properties about the media item. You will find these under the properties tab.", event: "click" }, { @@ -284,7 +379,7 @@ { element: "[data-element='editor-media'] [data-element='property-_umb_updatedate']", title: "Last edited", - content: "...and information about when the item has been created and edited." + content: "...and information about when the media item has been created and edited." } ] } @@ -314,6 +409,17 @@ return groupedTours; } + function getTourByAlias(tourAlias) { + var tour = _.findWhere(tours, {alias: tourAlias}); + return tour; + } + + function getCompletedTours() { + var completedTours = localStorageService.get(localStorageKey); + var aliases = _.pluck(completedTours, "alias"); + return aliases; + } + /////////// function setCompletedTours() { @@ -370,7 +476,9 @@ endTour: endTour, completeTour: completeTour, getAllTours: getAllTours, - getGroupedTours: getGroupedTours + getGroupedTours: getGroupedTours, + getTourByAlias: getTourByAlias, + getCompletedTours: getCompletedTours }; return service; diff --git a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js index 6814c9b52d..b4536f0e35 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js @@ -154,18 +154,15 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $ })); evts.push(eventsService.on("appState.tour.start", function (name, args) { - console.log("start tour event", args); $scope.tour = args; $scope.tour.show = true; })); evts.push(eventsService.on("appState.tour.end", function () { - console.log("end tour event"); $scope.tour = null; })); evts.push(eventsService.on("appState.tour.complete", function () { - console.log("completed tour event"); $scope.tour = null; })); diff --git a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-backdrop.less b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-backdrop.less index 0355cde4f5..438f82859e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-backdrop.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-backdrop.less @@ -8,9 +8,19 @@ pointer-events: none; } +.umb-backdrop__backdrop { + height: 100%; + width: 100%; + fill-opacity: 0.4; +} + .umb-backdrop__rect { position: absolute; pointer-events: all; - opacity: 0.4; margin: 0; +} + +.umb-backdrop__highlight-prevent-click { + position: absolute; + pointer-events: all; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-tour.less b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-tour.less index cc92514bd6..5e405c0363 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/application/umb-tour.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/application/umb-tour.less @@ -1,50 +1,62 @@ +.umb-tour__loader { + background: @white; + z-index: 10000; + position: fixed; + height: 5px; +} + .umb-tour__popover { position: fixed; - top: 50%; - left: 50%; background: @white; border-radius: @baseBorderRadius; - padding: 15px; - max-width: 250px; - min-width: 150px; z-index: 10000; + width: 250px; + max-width: 100%; + box-sizing: border-box; + padding: 15px; + + h1, h2, h3, h4, h5 { + font-weight: bold; + color: @black; + } } -.umb-tour__popover--top { - margin-bottom: 20px; - transform: none; +.umb-tour__popover--l { + padding: 30px; + width: 500px; + + .umb-tour-step__header { + margin-bottom: 30px; + margin-top: 10px; + } + + .umb-tour-step__title { + font-size: 20px; + } + + .umb-tour-step__content { + margin-bottom: 25px; + } } -.umb-tour__popover--right { - margin-left: 20px; - transform: none; -} - -.umb-tour__popover--bottom { - margin-top: 20px; - transform: none; -} - -.umb-tour__popover--left { - margin-right: 20px; - transform: none; -} - -.umb-tour__step-counter { +.umb-tour-step__counter { font-size: 13px; color: @gray-5; } -.umb-tour__title { +.umb-tour-step__header { + margin-top: 5px; margin-bottom: 10px; +} + +.umb-tour-step__title { font-weight: bold; color: @black; font-size: 15px; - margin-top: 5px; } -.umb-tour__content { +.umb-tour-step__content { margin-bottom: 15px; font-size: 15px; line-height: 1.6em; -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/editorpicker/editorpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/editorpicker/editorpicker.html index 66e08eba0a..ceeb27ee42 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/editorpicker/editorpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/editorpicker/editorpicker.html @@ -31,7 +31,7 @@