Merge pull request #2261 from umbraco/temp-U4-10565

U4-10565 Add introduction tour to the overall Umbraco UI
This commit is contained in:
Claus
2017-10-25 14:47:22 +02:00
committed by GitHub
46 changed files with 672 additions and 387 deletions
@@ -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: "=?",
}
};
@@ -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: "="
}
};
@@ -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;
@@ -6,6 +6,7 @@
var directive = {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: 'views/components/application/umbtour/umb-tour-step-header.html',
scope: {
title: "="
@@ -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: "@?"
}
};
@@ -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
};
@@ -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;
@@ -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: "<p>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.</p><p>In this quick tour we will introduce you to the main areas of Umbraco and show you how to best get started.</p>",
type: "intro"
},
{
element: "#applications",
elementPreventClick: true,
title: "Sections",
content: "These are the <b>Sections</b> and allows you to navigate the different areas of Umbraco.",
backdropOpacity: 0.6
},
{
element: "#tree",
elementPreventClick: true,
title: "The Tree",
content: "This is the <b>Tree</b> 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 <b>user photo</b> to open the user profile dialog.",
event: "click",
backdropOpacity: 0.6
},
{
element: "[data-element~='overlay-user']",
elementPreventClick: true,
title: "User profile",
content: "<p>This is where you can see details about your user, change your password and log out of Umbraco.</p><p>In the <b>User section</b> you will be able to do more advaned user management.</p>"
},
{
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: "<p>In the help drawer you will find articles and videos related to the section you are using.</p><p>This is also where you will find the next tour on how to get started with Umbraco.</p>",
backdropOpacity: 0.6
}
]
},
{
"name": "Create document type",
"alias": "umbIntroCreateDocType",
"group": "Getting Started",
"steps": [
{
title: "Create your first Document Type",
content: "<p>Step 1 of any site is to create a <strong>Document Type</strong>. 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 <strong>template</strong>.</p><p>In this tour you will learn how to set up a basic Document Type with a data field to enter a short text.</p>",
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 <b>Settings section</b> we will find the document types.",
event: "click",
backdropOpacity: 0.6
},
{
element: "#tree [data-element='tree-item-documentTypes']",
title: "Create document type",
content: "<p>Hover the document types tree and click the <b>three small dots</b> to open the context menu</p>",
content: "<p>Hover the document types tree and click the <b>three small dots</b> to open the <b>context menu</b>.</p>",
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 <b>Document Type</b> to create a new document type with a template",
content: "<p>Click <b>Document Type</b> to create a new document type with a template.</p><p>We will use the template in a later tour when we need to render our content.</p>",
event: "click"
},
{
element: "[data-element='editor-name-field']",
title: "Enter a name",
content: "<p>Our document type needs a name. Enter <b>Home</b> in the field and click <i>Next</i></p>",
content: "<p>Our document type needs a name. Enter <code>Home</code> in the field and click <b>Next</b>.",
view: "doctypename"
},
{
element: "[data-element='editor-description']",
title: "Enter a description",
content: "<p>A description helps the content editor pick the right document type when creating content:<br/><pre>The home to our website</pre></p>"
content: "<p>A description helps to pick the right document type when creating content.</p><p>Write a description to our Home page. It could be: <br/><pre>The home to our website</pre></p>"
},
{
element: "[data-element='group-add']",
@@ -50,63 +119,65 @@
{
element: "[data-element='group-name']",
title: "Enter a name",
content: "Enter <b>Content</b> in the tab name"
content: "Enter <code>Content</code> in the tab name."
},
{
element: "[data-element='property-add']",
title: "Add a property",
content: "<p>Properties are the different types of data on our content page.</p><p>On our Home page we wan't to add a welcome text.</p><p>Click <b>Add property</b> to open the property dialog</p>",
content: "<p>Properties are the different types of data on our content page.</p><p>On our Home page we wan't to add a welcome text.</p><p>Click <b>Add property</b> to open the property dialog.</p>",
event: "click"
},
{
element: "[data-element~='overlay-property-settings'] [data-element='property-name']",
title: "Enter a name",
content: "Enter <b>Welcome Text</b> as name for the property",
content: "Enter <code>Welcome Text</code> 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:<br/> <pre>Write a nice introduction text so the visitors feel welcome</pre>"
content: "<p>A description will help to fill in the right content.</p><p>Enter a description for the property editor. It could be:<br/> <pre>Write a nice introduction text so the visitors feel welcome</pre></p>"
},
{
element: "[data-element~='overlay-property-settings'] [data-element='editor-add']",
title: "Add editor",
content: "The editor define what data type the property is. Click <b>Add editor</b> to open the editor picker dialog",
content: "The editor defines what data type the property is. Click <b>Add editor</b> to open the editor picker dialog.",
event: "click"
},
{
element: "[data-element~='overlay-editor-picker']",
elementPreventClick: true,
title: "Editor picker",
content: "<p>In the editor picker dialog we can pick one of the many build in editor.</p>"
},
{
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 <b>Textarea</b> editor which allows the content editor to enter long texts",
content: "Select the <b>Textarea</b> 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 <b>Submit</b> to save the editor and any changes you may have made to the editor settings.",
content: "Click <b>Submit</b> to save the editor.",
event: "click"
},
{
element: "[data-element~='overlay-property-settings'] [data-element='overlay-submit']",
title: "Save property",
content: "Click <b>Submit</b> to add the property",
element: "[data-element~='overlay-property-settings'] [data-element='button-overlaySubmit']",
title: "Add property to document type",
content: "Click <b>Submit</b> 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 <b>Save</b> to create your document type",
content: "All we need now is to save the document type. Click <b>Save</b> 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: "<p>The <b>Content section</b> contains the content of the website. Content is displayed as <b>nodes</b> in the content tree.</p><p>In this tour we will learn how to create our <b>Home</b> page for our website.</p>",
type: "intro"
},
{
element: "[data-element='tree-root']",
title: "Open context menu",
content: "<p>Open the context menu by hovering the root of the content section.</p><p>Now click the <b>three small dots</b> to the right</p>",
content: "<p>Open the context menu by hovering the root of the content section.</p><p>Now click the <b>three small dots</b> to the right.</p>",
event: "click",
eventElement: "[data-element='tree-root'] [data-element='tree-item-options']"
},
{
element: "[data-element='action-create-home']",
title: "Create Home page",
content: "<p>Click on <b>Home</b> to create a new page of type <i>Home</i></p>",
content: "<p>Click on <b>Home</b> to create a new page of type <b>Home</b>.</p>",
event: "click"
},
{
element: "[data-element='editor-content'] [data-element='editor-name-field']",
title: "Give your new page a name",
content: "<p>Our new page needs a name. Enter <b>Home</b> in the field and click <i>Next</i></p>",
content: "<p>Our new page needs a name. Enter <code>Home</code> in the field and click <b>Next</b>.</p>",
view: "nodename"
},
{
element: "[data-element='editor-content'] [data-element='property-welcomeText']",
title: "Add a welcome text",
content: "<p>Add content to the <b>Welcome Text</b> field</p><p>If you don't have any ideas here is a start:<br/> <pre>I am learning Umbraco. High Five I Rock #H5IR</pre></p>"
content: "<p>Add content to the <b>Welcome Text</b> field</p><p>If you don't have any ideas here is a start:<br/> <pre>I am learning Umbraco. High Five I Rock #H5IR</pre>.</p>"
},
{
element: "[data-element='editor-content'] [data-element='button-group-primary']",
element: "[data-element='editor-content'] [data-element='button-saveAndPublish']",
title: "Save and publish",
content: "<p>Now click the <b>Save and publish</b> button to save and publish your changes</p>",
content: "<p>Now click the <b>Save and publish</b> button to save and publish your changes.</p>",
event: "click"
}
]
@@ -153,16 +229,22 @@
"alias": "umbIntroRenderInTemplate",
"group": "Getting Started",
"steps": [
{
title: "Render your content in a template",
content: "<p>Templating in Umbraco builds on the concept of <b>Razor Views</b> from asp.net MVC. - This tour is a sneak peak on how to write templates in Umbraco.</p><p>In this tour we will learn how to render content from our <b>Home</b> document type so we can see the content added to our Home page.</p>",
type: "intro"
},
{
element: "#applications [data-element='section-settings']",
title: "Navigate to the settings section",
content: "In the <b>Settings</b> section you will find the templates for all your document types",
event: "click"
title: "Navigate to the Settings section",
content: "<p>In the <b>Settings</b> section you will find all the templates</p><p>It is of course also possible to edit all your code files in your favorite code editor.</p>",
event: "click",
backdropOpacity: 0.6
},
{
element: "#tree [data-element='tree-item-templates']",
title: "Expand the Templates node",
content: "<p>To see all our templates click the <b>small triangle</b> to the left of the templates node</p>",
content: "<p>To see all our templates click the <b>small triangle</b> to the left of the templates node.</p>",
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: "<p>Click the <b>Home</b> template to open and edit it</p>",
content: "<p>Click the <b>Home</b> template to open and edit it.</p>",
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: '<p>Templates can be edited here or in your favorite code editor.</p><p>To render the value we entered on the <b>Home</b> page add the following to the template:<br/> <pre>@Model.Content.GetPropertyValue("welcomeText")</pre></p>'
content: '<p>The template can be edited here or in your favorite code editor.</p><p>To render the field from the document type add the following to the template:<br/> <pre>@Model.Content.GetPropertyValue("welcomeText")</pre></p>'
},
{
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 <b>Save button</b> and your template will be saved.",
event: "click"
}
]
@@ -191,22 +274,28 @@
"alias": "umbIntroViewHomePage",
"group": "Getting Started",
"steps": [
{
title: "View your Umbraco site",
content: "<p>Our three main components to a page is done: <b>Document type, Template, and Content</b> - it is now time to see the result.</p><p>In this tour we will learn how to see our published website.</p>",
type: "intro"
},
{
element: "#tree [data-element='tree-item-Home']",
title: "Open the Home page",
content: "<p>Click the <b>Home</b> page to open it</p>",
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: "<p>Under the properties tab you will find default information about the content item</p>",
content: "<p>Under the properties tab you will find the default information about a content item.</p>",
event: "click"
},
{
element: "[data-element='editor-content'] [data-element='property-_umb_urls']",
title: "Open page",
content: "<p>Click the <b>Link</b> to view your page.</p><p>Tip: Click the preview button in the bottom right corner to preview changes without publishing them</p>",
content: "<p>Click the <b>Link to document</b> <i class='icon-out'></i> to view your page.</p><p>Tip: Click the preview button in the bottom right corner to preview changes without publishing them.</p>",
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: "<p>A website would be boring without media content. In Umbraco you can manage all your images, documents, videos etc. in the <b>Media section</b>. Here you can upload and organise your media items and see details about each item.</p><p>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.</p>",
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 <b>media</b> 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: "<p>Hover the media root and click the <b>three small dots</b> on the right side of the item</p>",
content: "<p>Let's first create a folder for our images. Hover the media root and click the <b>three small dots</b> on the right side of the item.</p>",
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: "<p>Select the <b>Folder</b> options to create a new folder</p>",
content: "<p>Select the <b>Folder</b> options to select the type folder.</p>",
event: "click"
},
{
element: "[data-element='editor-media'] [data-element='editor-name-field']",
title: "Enter a name",
content: "<p>Enter <b>'My folder'</b> in the field</p>"
content: "<p>Enter <code>My folder</code> in the field.</p>"
},
{
element: "[data-element='editor-media'] [data-element='button-save']",
@@ -250,7 +345,7 @@
{
element: "[data-element='editor-media'] [data-element='dropzone']",
title: "Upload images",
content: "<p>In the upload area you can upload your media items.</p><p>Click the <b>Upload button</b> and select some images on your computer and upload them.</p>",
content: "<p>In the upload area you can upload your media items.</p><p>Click the <b>Upload button</b> and select a couple of images on your computer and upload them.</p>",
view: "uploadimages"
},
{
@@ -263,17 +358,17 @@
{
element: "[data-element='editor-media'] [data-element='property-umbracoFile']",
title: "The uploaded image",
content: "<p>Here you can see the image you have uploaded.</p><p>Use the dot in the center of the image to set a focal point on the image.</p>"
content: "<p>Here you can see the image you have uploaded.</p><p>You can use the dot in the center of the image to set a focal point on the image.</p>"
},
{
element: "[data-element='editor-media'] [data-element='property-umbracoBytes']",
title: "Image size",
content: "<p>You will also find other details about the image, like the size</p><p>You can add extra properties to an image by creating or editing the <b>Media types</b></p>"
content: "<p>You will also find other details about the image, like the size.</p><p>You can add extra properties to an image by creating or editing the <b>Media types</b></p>"
},
{
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;
@@ -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;
}));
@@ -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;
}
@@ -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;
}
}
@@ -31,7 +31,7 @@
<h5>{{key}}</h5>
<ul class="umb-card-grid" ng-mouseleave="vm.hideDetailsOverlay()">
<li ng-repeat="systemDataType in value | orderBy:'name'"
data-element="editor-{{systemDataType.alias}}"
data-element="editor-{{systemDataType.name}}"
ng-mouseover="vm.showDetailsOverlay(systemDataType)"
ng-click="vm.pickEditor(systemDataType)"
class="-four-in-row">
@@ -49,7 +49,7 @@
<h5>{{key}}</h5>
<ul class="umb-card-grid" ng-mouseleave="vm.hideDetailsOverlay()">
<li ng-repeat="dataType in value | orderBy:'name'"
data-element="editor-{{dataType.alias}}"
data-element="editor-{{dataType.name}}"
ng-mouseover="vm.showDetailsOverlay(dataType)"
ng-click="vm.pickDataType(dataType)"
class="-four-in-row">
@@ -11,6 +11,7 @@
</p>
<umb-button
alias="editUser"
type="link"
href="#/users/users/user/{{user.id}}"
action="model.close()"
@@ -21,6 +22,7 @@
</umb-button>
<umb-button
alias="changePassword"
type="button"
action="togglePasswordFields()"
label="Change password"
@@ -29,6 +31,7 @@
</umb-button>
<umb-button
alias="logOut"
type="button"
action="logout()"
shortcut="ctrl+shift+l"
@@ -4,7 +4,7 @@
function NodeNameController($scope) {
var vm = this;
var element = angular.element($scope.currentStep.element);
var element = angular.element($scope.model.currentStep.element);
vm.error = false;
@@ -12,7 +12,7 @@
function initNextStep() {
if(element.val() === 'Home') {
$scope.nextStep();
$scope.model.nextStep();
} else {
vm.error = true;
}
@@ -20,5 +20,5 @@
}
angular.module("umbraco").controller("Umbraco.Tours.umbIntroCreateDocType.NodeNameController", NodeNameController);
angular.module("umbraco").controller("Umbraco.Tours.UmbIntroCreateContent.NodeNameController", NodeNameController);
})();
@@ -1,26 +1,30 @@
<umb-tour-step ng-controller="Umbraco.Tours.umbIntroCreateDocType.NodeNameController as vm">
<div ng-controller="Umbraco.Tours.UmbIntroCreateContent.NodeNameController as vm">
<umb-tour-step>
<umb-tour-step-header
title="model.currentStep.title">
</umb-tour-step-header>
<umb-tour-step-header
title="currentStep.title">
</umb-tour-step-header>
<umb-tour-step-content
content="currentStep.content">
<div ng-if="vm.error" class="color-red" style="margin-top: 5px;">Please enter <b>Home</b> in the field</div>
</umb-tour-step-content>
<umb-tour-step-content
content="model.currentStep.content">
<div ng-if="vm.error" class="color-red" style="margin-top: 5px;">Please enter <b>Home</b> in the field</div>
</umb-tour-step-content>
<umb-tour-step-footer class="flex justify-between items-center">
<umb-tour-step-footer class="flex justify-between items-center">
<umb-tour-step-counter
current-step="currentStepIndex + 1"
total-steps="tour.steps.length">
</umb-tour-step-counter>
<umb-tour-step-counter
current-step="model.currentStepIndex + 1"
total-steps="model.steps.length">
</umb-tour-step-counter>
<div>
<umb-button size="xs" button-style="link" type="button" action="endTour()" label="End tour"></umb-button>
<umb-button size="xs" button-style="success" type="button" action="vm.initNextStep()" label="Next"></umb-button>
</div>
<div>
<umb-button size="xs" button-style="link" type="button" action="model.endTour()" label="End tour"></umb-button>
<umb-button size="xs" button-style="success" type="button" action="vm.initNextStep()" label="Next"></umb-button>
</div>
</umb-tour-step-footer>
</umb-tour-step-footer>
</umb-tour-step>
</umb-tour-step>
</div>
@@ -4,7 +4,7 @@
function DocTypeNameController($scope) {
var vm = this;
var element = angular.element($scope.currentStep.element);
var element = angular.element($scope.model.currentStep.element);
vm.error = false;
@@ -12,7 +12,7 @@
function initNextStep() {
if(element.val() === 'Home') {
$scope.nextStep();
$scope.model.nextStep();
} else {
vm.error = true;
}
@@ -1,26 +1,30 @@
<umb-tour-step ng-controller="Umbraco.Tours.UmbIntroCreateDocType.DocTypeNameController as vm">
<div ng-controller="Umbraco.Tours.UmbIntroCreateDocType.DocTypeNameController as vm">
<umb-tour-step>
<umb-tour-step-header
title="model.currentStep.title">
</umb-tour-step-header>
<umb-tour-step-header
title="currentStep.title">
</umb-tour-step-header>
<umb-tour-step-content
content="currentStep.content">
<div ng-if="vm.error" class="color-red" style="margin-top: 5px;">Please enter <b>Home</b> in the field</div>
</umb-tour-step-content>
<umb-tour-step-content
content="model.currentStep.content">
<div ng-if="vm.error" class="color-red" style="margin-top: 5px;">Please enter <b>Home</b> in the field</div>
</umb-tour-step-content>
<umb-tour-step-footer class="flex justify-between items-center">
<umb-tour-step-footer class="flex justify-between items-center">
<umb-tour-step-counter
current-step="currentStepIndex + 1"
total-steps="tour.steps.length">
</umb-tour-step-counter>
<umb-tour-step-counter
current-step="model.currentStepIndex + 1"
total-steps="model.steps.length">
</umb-tour-step-counter>
<div>
<umb-button size="xs" button-style="link" type="button" action="endTour()" label="End tour"></umb-button>
<umb-button size="xs" button-style="success" type="button" action="vm.initNextStep()" label="Next"></umb-button>
</div>
<div>
<umb-button size="xs" button-style="link" type="button" action="model.endTour()" label="End tour"></umb-button>
<umb-button size="xs" button-style="success" type="button" action="vm.initNextStep()" label="Next"></umb-button>
</div>
</umb-tour-step-footer>
</umb-tour-step-footer>
</umb-tour-step>
</umb-tour-step>
</div>
@@ -4,7 +4,7 @@
function PropertyNameController($scope) {
var vm = this;
var element = angular.element($scope.currentStep.element);
var element = angular.element($scope.model.currentStep.element);
vm.error = false;
@@ -12,7 +12,7 @@
function initNextStep() {
if(element.val() === 'Welcome Text') {
$scope.nextStep();
$scope.model.nextStep();
} else {
vm.error = true;
}
@@ -1,26 +1,30 @@
<umb-tour-step ng-controller="Umbraco.Tours.UmbIntroCreateDocType.PropertyNameController as vm">
<div ng-controller="Umbraco.Tours.UmbIntroCreateDocType.PropertyNameController as vm">
<umb-tour-step>
<umb-tour-step-header
title="model.currentStep.title">
</umb-tour-step-header>
<umb-tour-step-header
title="currentStep.title">
</umb-tour-step-header>
<umb-tour-step-content
content="currentStep.content">
<div ng-if="vm.error" class="color-red" style="margin-top: 5px;">Please enter <b>Welcome Text</b> in the field</div>
</umb-tour-step-content>
<umb-tour-step-content
content="model.currentStep.content">
<div ng-if="vm.error" class="color-red" style="margin-top: 5px;">Please enter <b>Welcome Text</b> in the field</div>
</umb-tour-step-content>
<umb-tour-step-footer class="flex justify-between items-center">
<umb-tour-step-footer class="flex justify-between items-center">
<umb-tour-step-counter
current-step="currentStepIndex + 1"
total-steps="tour.steps.length">
</umb-tour-step-counter>
<umb-tour-step-counter
current-step="model.currentStepIndex + 1"
total-steps="model.steps.length">
</umb-tour-step-counter>
<div>
<umb-button size="xs" button-style="link" type="button" action="endTour()" label="End tour"></umb-button>
<umb-button size="xs" button-style="success" type="button" action="vm.initNextStep()" label="Next"></umb-button>
</div>
<div>
<umb-button size="xs" button-style="link" type="button" action="model.endTour()" label="End tour"></umb-button>
<umb-button size="xs" button-style="success" type="button" action="vm.initNextStep()" label="Next"></umb-button>
</div>
</umb-tour-step-footer>
</umb-tour-step-footer>
</umb-tour-step>
</umb-tour-step>
</div>
@@ -4,7 +4,7 @@
function UploadImagesController($scope, editorState, mediaResource) {
var vm = this;
var element = angular.element($scope.currentStep.element);
var element = angular.element($scope.model.currentStep.element);
vm.error = false;
@@ -24,7 +24,7 @@
var children = data;
if(children.items && children.items.length > 0) {
$scope.nextStep();
$scope.model.nextStep();
} else {
vm.error = true;
}
@@ -1,26 +1,30 @@
<umb-tour-step ng-controller="Umbraco.Tours.UmbIntroMediaSection.UploadImagesController as vm">
<div ng-controller="Umbraco.Tours.UmbIntroMediaSection.UploadImagesController as vm">
<umb-tour-step-header
title="currentStep.title">
</umb-tour-step-header>
<umb-tour-step-content
content="currentStep.content">
<div ng-if="vm.error" class="color-red" style="margin-top: 5px;">Please upload an image</div>
</umb-tour-step-content>
<umb-tour-step>
<umb-tour-step-footer class="flex justify-between items-center">
<umb-tour-step-header
title="model.currentStep.title">
</umb-tour-step-header>
<umb-tour-step-content
content="model.currentStep.content">
<div ng-if="vm.error" class="color-red" style="margin-top: 5px;">Please upload an image</div>
</umb-tour-step-content>
<umb-tour-step-counter
current-step="currentStepIndex + 1"
total-steps="tour.steps.length">
</umb-tour-step-counter>
<umb-tour-step-footer class="flex justify-between items-center">
<div>
<umb-button size="xs" button-style="link" type="button" action="endTour()" label="End tour"></umb-button>
<umb-button size="xs" button-style="success" type="button" action="vm.initNextStep()" label="Next" state="vm.buttonState"></umb-button>
</div>
<umb-tour-step-counter
current-step="model.currentStepIndex + 1"
total-steps="model.steps.length">
</umb-tour-step-counter>
</umb-tour-step-footer>
<div>
<umb-button size="xs" button-style="link" type="button" action="model.endTour()" label="End tour"></umb-button>
<umb-button size="xs" button-style="success" type="button" action="vm.initNextStep()" label="Next" state="vm.buttonState"></umb-button>
</div>
</umb-tour-step>
</umb-tour-step-footer>
</umb-tour-step>
</div>
@@ -4,12 +4,12 @@
function TemplatesTreeController($scope) {
var vm = this;
var eventElement = angular.element($scope.currentStep.eventElement);
var eventElement = angular.element($scope.model.currentStep.eventElement);
function onInit() {
// check if tree is already open - if it is - go to next step
if(eventElement.hasClass("icon-navigation-down")) {
$scope.nextStep();
$scope.model.nextStep();
}
}
@@ -1,24 +1,26 @@
<umb-tour-step ng-controller="Umbraco.Tours.UmbIntroRenderInTemplate.TemplatesTreeController as vm">
<div ng-controller="Umbraco.Tours.UmbIntroRenderInTemplate.TemplatesTreeController as vm">
<umb-tour-step>
<umb-tour-step-header
title="model.currentStep.title">
</umb-tour-step-header>
<umb-tour-step-header
title="currentStep.title">
</umb-tour-step-header>
<umb-tour-step-content
content="currentStep.content">
</umb-tour-step-content>
<umb-tour-step-content
content="model.currentStep.content">
</umb-tour-step-content>
<umb-tour-step-footer class="flex justify-between items-center">
<umb-tour-step-footer class="flex justify-between items-center">
<umb-tour-step-counter
current-step="currentStepIndex + 1"
total-steps="tour.steps.length">
</umb-tour-step-counter>
<umb-tour-step-counter
current-step="model.currentStepIndex + 1"
total-steps="model.steps.length">
</umb-tour-step-counter>
<div>
<umb-button size="xs" button-style="link" type="button" action="endTour()" label="End tour"></umb-button>
</div>
<div>
<umb-button size="xs" button-style="link" type="button" action="model.endTour()" label="End tour"></umb-button>
</div>
</umb-tour-step-footer>
</umb-tour-step-footer>
</umb-tour-step>
</umb-tour-step>
</div>
@@ -1,15 +1,19 @@
<div ng-click="clickBackdrop($event)">
<div class="umb-backdrop" ng-click="clickBackdrop($event)">
<!-- Backdrop with highlight -->
<svg ng-if="element" class="umb-backdrop" xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect class="umb-backdrop__rect umb-backdrop__rect--left" height="100%"/>
<rect class="umb-backdrop__rect umb-backdrop__rect--top" width="100%"/>
<rect class="umb-backdrop__rect umb-backdrop__rect--bottom" width="100%"/>
<rect class="umb-backdrop__rect umb-backdrop__rect--right" height="100%" width="100%"/>
<svg ng-if="highlightElement" class="umb-backdrop__backdrop" xmlns="http://www.w3.org/2000/svg" version="1.1" ng-style="{'fill-opacity': backdropOpacity }">
<rect class="umb-backdrop__rect umb-backdrop__rect--left" height="100%" />
<rect class="umb-backdrop__rect umb-backdrop__rect--top" width="100% "/>
<rect class="umb-backdrop__rect umb-backdrop__rect--bottom" width="100%" />
<rect class="umb-backdrop__rect umb-backdrop__rect--right" height="100%" width="100%" />
</svg>
<!-- Full screen backdrop -->
<svg ng-if="!element" class="umb-backdrop" xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect class="umb-backdrop__rect" height="100%" width="100%"/>
<svg ng-if="!highlightElement" class="umb-backdrop__backdrop" xmlns="http://www.w3.org/2000/svg" version="1.1" ng-style="{'fill-opacity': backdropOpacity }">
<rect class="umb-backdrop__rect" height="100%" width="100%" />
</svg>
<!-- Prevent clicks in highlighted area -->
<div ng-if="highlightPreventClick" class="umb-backdrop__highlight-prevent-click"></div>
</div>
@@ -13,12 +13,13 @@
<div ng-controller="Umbraco.SearchController" ng-if="authenticated">
<!-- Search form -->
<div id="search-form">
<div data-element="global-search" id="search-form">
<div class="umb-modalcolumn-header">
<form class="form-search" novalidate>
<i class="icon-search"></i>
<input type="text"
<input data-element="global-search-field"
type="text"
hotkey="ctrl+space"
id="search-field"
ng-model="searchTerm"
@@ -1,7 +1,7 @@
<div>
<div id="applications" ng-class="{faded:stickyNavigation}">
<ul class="sections">
<li id="section-avatar" class="avatar">
<li data-element="section-user" id="section-avatar" class="avatar">
<a href="#" ng-click="avatarClick()" hotkey="ctrl+shift+u" title="{{user.name}}" prevent-default>
<umb-avatar
size="xs"
@@ -1,53 +1,80 @@
<div class="umb-tour">
<div class="umb-tour__popover shadow-depth-2" ng-class="{
'umb-tour__popover--top': position === 'top',
'umb-tour__popover--right': position === 'right',
'umb-tour__popover--bottom': position === 'bottom',
'umb-tour__popover--left': position === 'left'}">
<div class="umb-loader umb-tour__loader" ng-if="loadingStep"></div>
<div class="umb-tour__popover shadow-depth-2" ng-class="{'umb-tour__popover--l': model.currentStep.type === 'intro' || model.currentStepIndex === model.steps.length}">
<div ng-if="loadingStep">
<umb-load-indicator></umb-load-indicator>
</div>
<div ng-if="!configuredView && !loadingStep">
<div ng-if="!configuredView && !elementNotFound">
<!-- Regular steps -->
<umb-tour-step ng-if="currentStepIndex < tour.steps.length">
<umb-tour-step ng-if="model.currentStepIndex < model.steps.length">
<umb-tour-step-header
title="currentStep.title">
title="model.currentStep.title">
</umb-tour-step-header>
<umb-tour-step-content
content="currentStep.content">
content="model.currentStep.content">
</umb-tour-step-content>
<umb-tour-step-footer class="flex justify-between items-center">
<umb-tour-step-counter
current-step="currentStepIndex + 1"
total-steps="tour.steps.length">
current-step="model.currentStepIndex + 1"
total-steps="model.steps.length">
</umb-tour-step-counter>
<div>
<umb-button size="xs" button-style="link" type="button" action="endTour()" label="End tour"></umb-button>
<umb-button size="xs" ng-if="!currentStep.event" button-style="success" type="button" action="nextStep()" label="Next"></umb-button>
<div ng-if="model.currentStep.type !== 'intro'">
<umb-button size="xs" button-style="link" type="button" action="model.endTour()" label="End tour"></umb-button>
<umb-button size="xs" ng-if="!model.currentStep.event" button-style="success" type="button" action="model.nextStep()" label="Next"></umb-button>
</div>
<div ng-if="model.currentStep.type === 'intro'">
<umb-button size="m" button-style="link" type="button" action="model.endTour()" label="End tour"></umb-button>
<umb-button size="m" button-style="success" type="button" action="model.nextStep()" label="Start tour"></umb-button>
</div>
</umb-tour-step-footer>
</umb-tour-step>
<!-- Outro step -->
<div ng-if="currentStepIndex === tour.steps.length && !loadingStep">
<div class="umb-tour__title">WOOOHOOO!</div>
<div class="umb-tour__content">Your completed the tour. That is amazing!</div>
<div class="umb-tour__button-container-center">
<umb-button type="button" button-style="success" size="xs" action="completeTour()" label="Complete"></umb-button>
</div>
</div>
<umb-tour-step ng-if="model.currentStepIndex === model.steps.length" class="tc">
<umb-tour-step-content>
<div class="flex items-center justify-center">
<umb-checkmark size="xl" checked="true"></umb-checkmark>
</div>
<h3 class="bold">Congratulations!</h3>
<p>You have reached the end of the <b>{{model.name}}</b> tour - way to go!</p>
</umb-tour-step-content>
<umb-tour-step-footer>
<umb-button type="button" button-style="success" size="m" action="model.completeTour()" label="Complete"></umb-button>
</umb-tour-step-footer>
</umb-tour-step>
</div>
<div ng-if="configuredView && !loadingStep" ng-include="configuredView"></div>
<!-- Custom step view -->
<div ng-if="configuredView && !loadingStep && !elementNotFound" ng-include="configuredView"></div>
<!-- Dom element not found error -->
<div ng-if="elementNotFound && !loadingStep">
<umb-tour-step class="tc">
<umb-tour-step-header>
<h4 class="bold color-red">Oh, we got lost!</h4>
</umb-tour-step-header>
<umb-tour-step-content>
<p>We lost the next step <b>{{ model.currentStep.title }}</b> and don't know where to go.</p>
<p>Please go back and start the tour again.</p>
</umb-tour-step-content>
<umb-tour-step-footer>
<umb-button size="s" button-style="success" type="button" action="model.endTour()" label="End tour"></umb-button>
</umb-tour-step-footer>
</umb-tour-step>
</div>
</div>
@@ -1,4 +1,4 @@
<div class="umb-tour__content">
<div class="umb-tour-step__content">
<div ng-bind-html="content"></div>
<div ng-transclude></div>
</div>
@@ -1 +1 @@
<div class="umb-tour__step-counter">Step {{ currentStep }} of {{ totalSteps }}</div>
<div class="umb-tour-step__counter">{{ currentStep }}/{{ totalSteps }}</div>
@@ -1 +1 @@
<div ng-transclude></div>
<div class="umb-tour-step__footer" ng-transclude></div>
@@ -1 +1,4 @@
<div class="umb-tour__title">{{title}}</div>
<div class="umb-tour-step__header">
<div class="umb-tour-step__title">{{title}}</div>
<div ng-transclude></div>
</div>
@@ -1 +1 @@
<div ng-transclude></div>
<div class="umb-tour-step umb-tour-step--{{size}}" ng-transclude></div>
@@ -2,7 +2,7 @@
<umb-button
ng-if="defaultButton"
data-element="button-group-primary"
alias="{{defaultButton.alias ? defaultButton.alias : 'groupPrimary' }}"
type="button"
action="defaultButton.handler()"
button-style="success"
@@ -18,8 +18,8 @@
</a>
<ul aria-labelledby="dLabel" class="dropdown-menu bottom-up umb-button-group__sub-buttons" ng-if="subButtons.length > 0" role="menu" ng-class="{'-align-right': float === 'right'}">
<li data-element="button-group-secondary-{{$index}}" ng-repeat="subButton in subButtons">
<a href="#" ng-click="subButton.handler()" hotkey="{{subButton.hotKey}}" hotkey-when-hidden="{{subButton.hotKeyWhenHidden}}" prevent-default>
<li ng-repeat="subButton in subButtons">
<a data-element="{{subButton.alias ? 'button-' + subButton.alias : 'button-group-secondary-' + $index }}" href="#" ng-click="subButton.handler()" hotkey="{{subButton.hotKey}}" hotkey-when-hidden="{{subButton.hotKeyWhenHidden}}" prevent-default>
<localize key="{{subButton.labelKey}}">{{subButton.labelKey}}</localize>
</a>
</li>
@@ -1,4 +1,4 @@
<div class="umb-button" ng-class="{'umb-button--block': blockElement}">
<div class="umb-button" ng-class="{'umb-button--block': blockElement}" data-element="{{ alias ? 'button-' + alias : '' }}">
<div class="icon-check umb-button__success" ng-class="{'-hidden': state !== 'success', '-white': style}"></div>
@@ -50,7 +50,7 @@
<umb-editor-footer-content-right>
<umb-button
data-element="button-return-to-list-view"
alias="returnToListView"
ng-if="page.listViewPath"
type="link"
href="#{{page.listViewPath}}"
@@ -59,7 +59,7 @@
</umb-button>
<umb-button
data-element="button-preview"
alias="preview"
ng-if="!page.isNew && content.allowPreview"
type="button"
button-style="info"
@@ -35,7 +35,7 @@
<div class="umb-overlay-drawer__align-right">
<umb-button
data-element="overlay-cancel-submit"
alias="overlayCancelSubmit"
action="cancelConfirmSubmit()"
button-style="link"
label="Cancel"
@@ -55,7 +55,7 @@
<div class="umb-overlay-drawer__align-right" ng-if="!model.confirmSubmit.show">
<umb-button
data-element="overlay-close"
alias="overlayClose"
action="closeOverLay()"
button-style="link"
label-key="{{model.closeButtonLabelKey}}"
@@ -64,7 +64,7 @@
</umb-button>
<umb-button
data-element="overlay-submit"
alias="overlaySubmit"
button-style="success"
label-key="{{model.submitButtonLabelKey}}"
label="{{model.submitButtonLabel}}"
@@ -5,7 +5,7 @@
<umb-editor-sub-header-content-right>
<umb-button
data-element="button-compositions"
alias="compositions"
ng-if="compositions !== false"
type="button"
button-style="link"
@@ -15,7 +15,7 @@
</umb-button>
<umb-button
data-element="button-reorder"
alias="reorder"
ng-if="sorting !== false"
type="button"
button-style="link"
@@ -24,7 +24,7 @@
<!-- Select files -->
<div
data-element="button-upload-media"
data-element="button-uploadMedia"
class="file-select"
ngf-select
ng-model="filesHolder"
@@ -28,8 +28,21 @@ function startUpDynamicContentController($timeout, dashboardResource, assetsServ
vm.startTour = startTour;
function onInit() {
// load tours
vm.tours = tourService.getGroupedTours();
// get list of completed tours
var completedTours = tourService.getCompletedTours();
// get intro tour
var introTour = tourService.getTourByAlias("umbIntroIntroduction");
// start tour if it hasn't been completed
if(completedTours.indexOf(introTour.alias) === -1) {
tourService.startTour(introTour);
}
}
function startTour(tour) {
@@ -121,6 +121,7 @@
if (result) {
//Models builder mode:
vm.page.defaultButton = {
alias: "save",
hotKey: "ctrl+s",
hotKeyWhenHidden: true,
labelKey: "buttons_save",
@@ -129,6 +130,7 @@
handler: function () { vm.save(); }
};
vm.page.subButtons = [{
alias: "saveAndGenerateModels",
hotKey: "ctrl+g",
hotKeyWhenHidden: true,
labelKey: "buttons_saveAndGenerateModels",
@@ -40,6 +40,7 @@
<umb-editor-footer-content-right>
<umb-button
alias="save"
ng-if="!vm.page.modelsBuilder"
type="button"
action="vm.save()"
@@ -12,10 +12,8 @@
<ul class="umb-actions-child">
<li data-element="action-{{docType.alias}}" ng-repeat="docType in allowedTypes">
<a href="#media/media/edit/{{currentNode.id}}?doctype={{docType.alias}}&create=true" ng-click="nav.hideNavigation()">
<a ng-href="" ng-click="createMediaItem(docType)" prevent-default>
<i class="large {{docType.icon}}"></i>
<span class="menu-label">
{{docType.name}}
<small>
@@ -47,7 +47,7 @@
<umb-editor-footer-content-right>
<umb-button
data-element="button-return-to-list"
alias="returnToList"
ng-if="page.listViewPath"
type="link"
href="#{{page.listViewPath}}"
@@ -56,7 +56,7 @@
</umb-button>
<umb-button
data-element="button-save"
alias="save"
type="submit"
label="Save"
label-key="buttons_save"
@@ -6,11 +6,16 @@
* @description
* The controller for the media creation dialog
*/
function mediaCreateController($scope, $routeParams, mediaTypeResource, iconHelper) {
function mediaCreateController($scope, $routeParams, $location, mediaTypeResource, iconHelper, navigationService) {
mediaTypeResource.getAllowedTypes($scope.currentNode.id).then(function(data) {
$scope.allowedTypes = iconHelper.formatContentTypeIcons(data);
});
$scope.createMediaItem = function(docType) {
$location.path("/media/media/edit/" + $scope.currentNode.id).search("doctype", docType.alias).search("create", "true");
navigationService.hideMenu();
};
}
@@ -30,7 +30,7 @@
<div ng-class="{'btn-group umb-era-button-group': vm.template.masterTemplateAlias}" style="margin-right: 10px;">
<button
data-element="button-master-template"
data-element="button-masterTemplate"
type="button"
class="umb-era-button umb-button--s"
ng-click="vm.openMasterTemplateOverlay()">
@@ -75,7 +75,7 @@
</div>
<button
data-element="button-query-builder"
data-element="button-queryBuilder"
type="button"
style="margin-right: 10px;"
class="umb-era-button umb-button--s"
@@ -118,7 +118,7 @@
<umb-editor-footer-content-right>
<umb-button
data-element="button-save"
alias="save"
type="submit"
button-style="success"
state="vm.page.saveButtonState"
@@ -56,7 +56,7 @@
<div ng-hide="!authenticated" ng-cloak>
<!-- help dialog controller by the help button - this also forces the backoffice UI to shift 400px -->
<umb-drawer ng-if="drawer.show" model="drawer.model" view="drawer.view"></umb-drawer>
<umb-drawer data-element="drawer" ng-if="drawer.show" model="drawer.model" view="drawer.view"></umb-drawer>
<div id="mainwrapper" class="clearfix" ng-click="closeDialogs($event)">
@@ -68,7 +68,7 @@
<umb-tour
ng-if="tour.show"
tour="tour">
model="tour">
</umb-tour>
<umb-notifications></umb-notifications>
@@ -79,7 +79,9 @@
<umb-backdrop
ng-if="backdrop.show"
element="backdrop.element"
backdrop-opacity="backdrop.opacity"
highlight-element="backdrop.element"
highlight-prevent-click="backdrop.elementPreventClick"
disable-events-on-click="backdrop.disableEventsOnClick">
</umb-backdrop>