diff --git a/Gruntfile.js b/Gruntfile.js index 261f0c72b..a5a43d053 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -199,7 +199,8 @@ module.exports = function(grunt) { "packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js", "packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectUserSettings.js", "packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js", - "packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js" + "packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js", + "packages/node_modules/@node-red/editor-client/src/js/ui/tour/*.js" ], dest: "packages/node_modules/@node-red/editor-client/public/red/red.js" }, @@ -326,6 +327,12 @@ module.exports = function(grunt) { ], tasks: ['jsonlint:keymaps','copy:build'] }, + tours: { + files: [ + 'packages/node_modules/@node-red/editor-client/src/tours/**/*.js' + ], + tasks: ['copy:build'] + }, misc: { files: [ 'CHANGELOG.md' @@ -423,6 +430,12 @@ module.exports = function(grunt) { src: '**', expand: true, dest: 'packages/node_modules/@node-red/editor-client/public/vendor/ace/' + }, + { + cwd: 'packages/node_modules/@node-red/editor-client/src/tours', + src: '**', + expand: true, + dest: 'packages/node_modules/@node-red/editor-client/public/red/tours/' } ] } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tour/tourGuide.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tour/tourGuide.js new file mode 100644 index 000000000..772c59b3c --- /dev/null +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tour/tourGuide.js @@ -0,0 +1,360 @@ +RED.tourGuide = (function() { + var activeListeners = []; + var shade; + var focus; + var popover; + var stepContent; + var targetElement; + var fullscreen; + + var tourCache = {}; + + function run(tourPath, done) { + done = done || function(err) { + if (err) { + console.error(err); + } + }; + if (tourCache[tourPath]) { + runTour(tourCache[tourPath],done); + } else { + import(tourPath).then(function(module) { + tourCache[tourPath] = module.default; + runTour(tourCache[tourPath],done); + }).catch(function(err) { + console.warn("Error loading tour:",err); + done(err); + }) + } + } + + function repositionFocus() { + if (targetElement) { + var pos = targetElement[0].getBoundingClientRect(); + var dimension = Math.max(50, Math.max(pos.width,pos.height)*1.5); + if (!fullscreen) { + focus.css({ + left: (pos.left+pos.width/2)+"px", + top: (pos.top+pos.height/2)+"px", + width: (2*dimension)+"px", + height: (2*dimension)+"px" + }) + focus[0].offsetHeight; // Flush CSS changes + focus.addClass("transition"); + focus.css({ + width: dimension+"px", + height: dimension+"px" + }) + } else { + focus.css({ + left: ($(window).width()/2)+"px", + top: ($(window).height()/2)+"px", + width: "0px", + height: "0px" + }) + } + if (popover) { + popover.move({ + target: targetElement, + }) + } + } + } + function runTour(tour, done) { + + shade = $('
').appendTo(document.body); + focus = $('').appendTo(shade); + + // var resizeTimer; + // + $(window).on("resize.red-ui-tourGuide", function() { + repositionFocus(); + }) + + + + var i = 0; + var state = { + index: 0, + count: tour.steps.length + }; + + function endTour(err) { + $(window).off("resize.red-ui-tourGuide"); + if (popover) { + popover.close(); + } + stepContent = null; + popover = null; + shade.remove(); + shade = null; + done(err); + } + function runStep(carryOn) { + if (carryOn === false) { + endTour(false); + return; + } + if (i === tour.steps.length) { + endTour(); + return + } + state.index = i; + // console.log("TOUR STEP",i+1,"OF",tour.steps.length) + try { + runTourStep(tour.steps[i++], state, runStep) + } catch(err) { + endTour(err); + return; + } + } + runStep(); + } + + function clearListeners() { + activeListeners.forEach(function(listener) { + if (listener.type === "dom-event") { + listener.target[0].removeEventListener(listener.event,listener.listener,listener.opts); + } else if (listener.type === "nr-event") { + RED.events.off(listener.event, listener.listener) + } + }) + activeListeners = []; + } + + function prepareStep(step, state, done) { + if (step.prepare) { + if (step.prepare.length === 0) { + step.prepare.call(state); + } else { + step.prepare.call(state, done) + return; + } + } + done(); + } + function completeStep(step, state, done) { + function finish() { + clearListeners(); + setTimeout(function() { + done(); + },0) + } + if (step.complete) { + if (step.complete.length === 0) { + step.complete.call(state); + } else { + step.complete.call(state, finish) + return; + } + } + finish(); + + } + function runTourStep(step, state, done) { + shade.fadeIn(); + prepareStep(step, state, function() { + var zIndex; + var direction = step.direction || "bottom"; + fullscreen = false; + + if (typeof step.element === "string") { + targetElement = $(step.element) + } else if (typeof step.element === "function") { + targetElement = step.element.call(state); + } else if (!step.element) { + targetElement = $(".red-ui-editor") + fullscreen = true; + direction = "inset"; + } else { + targetElement = step.element; + } + + if (targetElement.length === 0) { + targetElement = null; + shade.hide(); + throw new Error("Element not found") + } + + zIndex = targetElement.css("z-index"); + if (!fullscreen) { + targetElement.css("z-index",2002); + } + repositionFocus(); + focus.toggleClass("disableInteraction", step.interactive === false) + + if (!stepContent) { + stepContent = $(''); + } else { + stepContent.empty(); + } + $('').appendTo(stepContent).click(function(evt) { + evt.preventDefault(); + completeStep(step, state, function() { + done(false); + }); + }) + + var stepDescription = $('').appendTo(stepContent); + if (step.titleIcon) { + $('First, as you've already found, we now have this tour of new features. We'll only show the tour the first time you open the editor for each new version of Node-RED.
"+ + "You can choose not to see this tour in the future by disabling it under the View tab of User Settings.
", + }, + { + prepare:function() { + $("#red-ui-header-button-sidemenu").trigger("click"); + $("#menu-item-edit-menu").parent().addClass("open") + }, + complete: function() { + $("#menu-item-edit-menu").parent().removeClass("open") + }, + element: "#menu-item-edit-menu-submenu", + interactive: false, + direction: "left", + title: "New Edit menu", + description: "The main menu has been updated with a new 'Edit' section. This includes all of the familar options, like cut/paste and undo/redo.
"+ + "The menu now displays keyboard shortcuts for the options.
" + + }, + { + prepare: function() { + $("#red-ui-header-button-sidemenu").trigger("click"); + $("#menu-item-arrange-menu").parent().addClass("open") + }, + complete: function() { + $("#menu-item-arrange-menu").parent().removeClass("open") + }, + element: "#menu-item-arrange-menu-submenu", + interactive: false, + direction: "left", + title: "Arranging nodes", + description: "The new 'Arrange' section of the menu provides new options to help arrange your nodes. You can align them to a common edge, spread them out evenly or change their order.
", + }, + { + element: "#red-ui-workspace-tabs > li:first-child", + title: "Flow and Group level environment variables", + description: "Flows and Groups can now have their own environment variables that can be referenced by nodes inside them.
", + }, + { + prepare: function(done) { + RED.editor.editFlow(RED.nodes.workspace(RED.workspaces.active()),"editor-tab-envProperties"); + setTimeout(done,800); + }, + element: "#red-ui-tab-editor-tab-envProperties-link-button", + description: "Flows and Groups now have an Environment Variables section in their edit dialog.
" + }, + { + element: ".node-input-env-container-row .red-ui-editableList-addButton", + direction: "top", + description: 'The environment variables are listed in this table and new ones can be added by clicking the button.
', + complete: function() { + $("#node-dialog-cancel").trigger("click"); + } + }, + { + title: "And that's not all...", + description: "There's more still to come before 2.1.0 is released. Watch this space!
" + }, + ] +}