diff --git a/src/Ui/media/Notifications.coffee b/src/Ui/media/Notifications.coffee index 1a7f94fa..00c66761 100644 --- a/src/Ui/media/Notifications.coffee +++ b/src/Ui/media/Notifications.coffee @@ -21,12 +21,16 @@ class Notifications # Create element elem = $(".notification.template", @elem).clone().removeClass("template") elem.addClass("notification-#{type}").addClass("notification-#{id}") + if type == "progress" + elem.addClass("notification-done") # Update text if type == "error" $(".notification-icon", elem).html("!") else if type == "done" $(".notification-icon", elem).html("
") + else if type == "progress" + $(".notification-icon", elem).html("
") else if type == "ask" $(".notification-icon", elem).html("?") else @@ -64,6 +68,8 @@ class Notifications $(".select", elem).on "click", => @close elem + return elem + close: (elem) -> elem.stop().animate {"width": 0, "opacity": 0}, 700, "easeInOutCubic" diff --git a/src/Ui/media/Wrapper.coffee b/src/Ui/media/Wrapper.coffee index d553fba0..6f31bcd1 100644 --- a/src/Ui/media/Wrapper.coffee +++ b/src/Ui/media/Wrapper.coffee @@ -53,6 +53,8 @@ class Wrapper if "-" in message.params[0] # - in first param: message id defined [id, type] = message.params[0].split("-") @notifications.add(id, type, message.params[1], message.params[2]) + else if cmd == "progress" # Display notification + @actionProgress(message) else if cmd == "prompt" # Prompt input @displayPrompt message.params[0], message.params[1], message.params[2], (res) => @ws.response message.id, res @@ -109,6 +111,8 @@ class Wrapper @actionConfirm(message) else if cmd == "wrapperPrompt" # Prompt input @actionPrompt(message) + else if cmd == "wrapperProgress" # Progress bar + @actionProgress(message) else if cmd == "wrapperSetViewport" # Set the viewport @actionSetViewport(message) else if cmd == "wrapperSetTitle" @@ -197,8 +201,6 @@ class Wrapper body = $(""+message.params[1]+"") @notifications.add("notification-#{message.id}", message.params[0], body, message.params[2]) - - displayConfirm: (message, caption, cb) -> body = $(""+message+"") button = $("#{caption}") # Add confirm button @@ -249,6 +251,49 @@ class Wrapper @displayPrompt message.params[0], type, caption, (res) => @sendInner {"cmd": "response", "to": message.id, "result": res} # Response to confirm + actionProgress: (message) -> + message.params = @toHtmlSafe(message.params) # Escape html + percent = Math.min(100, message.params[2])/100 + offset = 75-(percent*75) + circle = """ +
+ + +
+ """ + body = ""+message.params[1]+"" + circle + elem = $(".notification-#{message.params[0]}") + if elem.length + width = $(".body .message", elem).outerWidth() + $(".body .message", elem).html(message.params[1]) + if $(".body .message", elem).css("width") == "" + $(".body .message", elem).css("width", width) + $(".body .circle-fg", elem).css("stroke-dashoffset", offset) + else + elem = @notifications.add(message.params[0], "progress", $(body)) + if percent > 0 + $(".body .circle-bg", elem).css {"animation-play-state": "paused", "stroke-dasharray": "180px"} + + if $(".notification-icon", elem).data("done") + return false + else if message.params[2] >= 100 # Done + $(".circle-fg", elem).css("transition", "all 0.3s ease-in-out") + setTimeout (-> + $(".notification-icon", elem).css {transform: "scale(1)", opacity: 1} + $(".notification-icon .icon-success", elem).css {transform: "rotate(45deg) scale(1)"} + ), 300 + setTimeout (=> + @notifications.close elem + ), 3000 + $(".notification-icon", elem).data("done", true) + else if message.params[2] < 0 # Error + $(".body .circle-fg", elem).css("stroke", "#ec6f47").css("transition", "transition: all 0.3s ease-in-out") + setTimeout (=> + $(".notification-icon", elem).css {transform: "scale(1)", opacity: 1} + elem.removeClass("notification-done").addClass("notification-error") + $(".notification-icon .icon-success", elem).removeClass("icon-success").html("!") + ), 300 + $(".notification-icon", elem).data("done", true) actionSetViewport: (message) -> diff --git a/src/Ui/media/Wrapper.css b/src/Ui/media/Wrapper.css index 13681a26..4b84d4b2 100644 --- a/src/Ui/media/Wrapper.css +++ b/src/Ui/media/Wrapper.css @@ -45,7 +45,7 @@ a { color: black } color: #4F4F4F; font-family: 'Lucida Grande', 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px; /*border: 1px solid rgba(210, 206, 205, 0.2)*/ } .notification-icon { - display: block; width: 50px; height: 50px; position: absolute; float: left; z-index: 1; + display: block; width: 50px; height: 50px; position: absolute; float: left; z-index: 2; text-align: center; background-color: #e74c3c; line-height: 45px; vertical-align: bottom; font-size: 40px; color: white; } .notification .body { @@ -82,6 +82,18 @@ a { color: black } .notification .input { padding: 6px; border: 1px solid #DDD; margin-left: 10px; border-bottom: 2px solid #DDD; border-radius: 1px; margin-right: -11px; transition: all 0.3s } .notification .input:focus { border-color: #95a5a6; outline: none } +/* Notification progress */ +.notification .circle { width: 50px; height: 50px; position: absolute; left: -50px; top: 0px; background-color: #e2e9ec; z-index: 1; background: linear-gradient(405deg, rgba(226, 233, 236, 0.8), #efefef); } +.notification .circle-svg { margin-left: 10px; margin-top: 10px; transform: rotateZ(-90deg); } +.notification .circle-bg { stroke: #FFF; stroke-width: 2px; animation: rolling 0.4s infinite linear; stroke-dasharray: 40px; transition: all 1s } +.notification .circle-fg { stroke-dashoffset: 200; stroke: #2ecc71; stroke-width: 2px; stroke-dasharray: 75px; transition: all 5s cubic-bezier(0.19, 1, 0.22, 1); } +.notification-progress .notification-icon { opacity: 0; transform: scale(0); transition: all 0.3s ease-in-out } +.notification-progress .icon-success { transform: rotate(45deg) scale(0); transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); } +@keyframes rolling { + 0% { stroke-dashoffset: 80px } + 100% { stroke-dashoffset: 0px } +} + /* Icons (based on http://nicolasgallagher.com/pure-css-gui-icons/demo/) */ .icon-success { left:6px; width:5px; height:12px; border-width:0 5px 5px 0; border-style:solid; border-color:white; margin-left: 20px; margin-top: 15px; transform:rotate(45deg) } diff --git a/src/Ui/media/all.css b/src/Ui/media/all.css index 0321f9a4..e960a0b9 100644 --- a/src/Ui/media/all.css +++ b/src/Ui/media/all.css @@ -50,7 +50,7 @@ a { color: black } color: #4F4F4F; font-family: 'Lucida Grande', 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px; /*border: 1px solid rgba(210, 206, 205, 0.2)*/ } .notification-icon { - display: block; width: 50px; height: 50px; position: absolute; float: left; z-index: 1; + display: block; width: 50px; height: 50px; position: absolute; float: left; z-index: 2; text-align: center; background-color: #e74c3c; line-height: 45px; vertical-align: bottom; font-size: 40px; color: white; } .notification .body { @@ -87,6 +87,27 @@ a { color: black } .notification .input { padding: 6px; border: 1px solid #DDD; margin-left: 10px; border-bottom: 2px solid #DDD; -webkit-border-radius: 1px; -moz-border-radius: 1px; -o-border-radius: 1px; -ms-border-radius: 1px; border-radius: 1px ; margin-right: -11px; -webkit-transition: all 0.3s ; -moz-transition: all 0.3s ; -o-transition: all 0.3s ; -ms-transition: all 0.3s ; transition: all 0.3s } .notification .input:focus { border-color: #95a5a6; outline: none } +/* Notification progress */ +.notification .circle { width: 50px; height: 50px; position: absolute; left: -50px; top: 0px; background-color: #e2e9ec; z-index: 1; background: -webkit-linear-gradient(405deg, rgba(226, 233, 236, 0.8), #efefef);background: -moz-linear-gradient(405deg, rgba(226, 233, 236, 0.8), #efefef);background: -o-linear-gradient(405deg, rgba(226, 233, 236, 0.8), #efefef);background: -ms-linear-gradient(405deg, rgba(226, 233, 236, 0.8), #efefef);background: linear-gradient(405deg, rgba(226, 233, 236, 0.8), #efefef); } +.notification .circle-svg { margin-left: 10px; margin-top: 10px; -webkit-transform: rotateZ(-90deg); -moz-transform: rotateZ(-90deg); -o-transform: rotateZ(-90deg); -ms-transform: rotateZ(-90deg); transform: rotateZ(-90deg) ; } +.notification .circle-bg { stroke: #FFF; stroke-width: 2px; -webkit-animation: rolling 0.4s infinite linear; -moz-animation: rolling 0.4s infinite linear; -o-animation: rolling 0.4s infinite linear; -ms-animation: rolling 0.4s infinite linear; animation: rolling 0.4s infinite linear ; stroke-dasharray: 40px; -webkit-transition: all 1s ; -moz-transition: all 1s ; -o-transition: all 1s ; -ms-transition: all 1s ; transition: all 1s } +.notification .circle-fg { stroke-dashoffset: 200; stroke: #2ecc71; stroke-width: 2px; stroke-dasharray: 75px; -webkit-transition: all 5s cubic-bezier(0.19, 1, 0.22, 1); -moz-transition: all 5s cubic-bezier(0.19, 1, 0.22, 1); -o-transition: all 5s cubic-bezier(0.19, 1, 0.22, 1); -ms-transition: all 5s cubic-bezier(0.19, 1, 0.22, 1); transition: all 5s cubic-bezier(0.19, 1, 0.22, 1) ; } +.notification-progress .notification-icon { opacity: 0; -webkit-transform: scale(0); -moz-transform: scale(0); -o-transform: scale(0); -ms-transform: scale(0); transform: scale(0) ; -webkit-transition: all 0.3s ease-in-out ; -moz-transition: all 0.3s ease-in-out ; -o-transition: all 0.3s ease-in-out ; -ms-transition: all 0.3s ease-in-out ; transition: all 0.3s ease-in-out } +.notification-progress .icon-success { -webkit-transform: rotate(45deg) scale(0); -moz-transform: rotate(45deg) scale(0); -o-transform: rotate(45deg) scale(0); -ms-transform: rotate(45deg) scale(0); transform: rotate(45deg) scale(0) ; -webkit-transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); -moz-transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); -o-transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); -ms-transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55) ; } +@keyframes rolling { + 0% { stroke-dashoffset: 80px } + 100% { stroke-dashoffset: 0px } +} +@-webkit-keyframes rolling { + 0% { stroke-dashoffset: 80px } + 100% { stroke-dashoffset: 0px } +} +@-moz-keyframes rolling { + 0% { stroke-dashoffset: 80px } + 100% { stroke-dashoffset: 0px } +} + + /* Icons (based on http://nicolasgallagher.com/pure-css-gui-icons/demo/) */ .icon-success { left:6px; width:5px; height:12px; border-width:0 5px 5px 0; border-style:solid; border-color:white; margin-left: 20px; margin-top: 15px; transform:rotate(45deg) } diff --git a/src/Ui/media/all.js b/src/Ui/media/all.js index d4dcd375..ecf9ddac 100644 --- a/src/Ui/media/all.js +++ b/src/Ui/media/all.js @@ -688,10 +688,15 @@ jQuery.extend( jQuery.easing, } elem = $(".notification.template", this.elem).clone().removeClass("template"); elem.addClass("notification-" + type).addClass("notification-" + id); + if (type === "progress") { + elem.addClass("notification-done"); + } if (type === "error") { $(".notification-icon", elem).html("!"); } else if (type === "done") { $(".notification-icon", elem).html("
"); + } else if (type === "progress") { + $(".notification-icon", elem).html("
"); } else if (type === "ask") { $(".notification-icon", elem).html("?"); } else { @@ -735,11 +740,12 @@ jQuery.extend( jQuery.easing, return false; }; })(this)); - return $(".select", elem).on("click", (function(_this) { + $(".select", elem).on("click", (function(_this) { return function() { return _this.close(elem); }; })(this)); + return elem; }; Notifications.prototype.close = function(elem) { @@ -848,6 +854,8 @@ jQuery.extend( jQuery.easing, _ref = message.params[0].split("-"), id = _ref[0], type = _ref[1]; } return this.notifications.add(id, type, message.params[1], message.params[2]); + } else if (cmd === "progress") { + return this.actionProgress(message); } else if (cmd === "prompt") { return this.displayPrompt(message.params[0], message.params[1], message.params[2], (function(_this) { return function(res) { @@ -915,6 +923,8 @@ jQuery.extend( jQuery.easing, return this.actionConfirm(message); } else if (cmd === "wrapperPrompt") { return this.actionPrompt(message); + } else if (cmd === "wrapperProgress") { + return this.actionProgress(message); } else if (cmd === "wrapperSetViewport") { return this.actionSetViewport(message); } else if (cmd === "wrapperSetTitle") { @@ -1127,6 +1137,65 @@ jQuery.extend( jQuery.easing, })(this)); }; + Wrapper.prototype.actionProgress = function(message) { + var body, circle, elem, offset, percent, width; + message.params = this.toHtmlSafe(message.params); + percent = Math.min(100, message.params[2]) / 100; + offset = 75 - (percent * 75); + circle = "
\n \n \n
"; + body = "" + message.params[1] + "" + circle; + elem = $(".notification-" + message.params[0]); + if (elem.length) { + width = $(".body .message", elem).outerWidth(); + $(".body .message", elem).html(message.params[1]); + if ($(".body .message", elem).css("width") === "") { + $(".body .message", elem).css("width", width); + } + $(".body .circle-fg", elem).css("stroke-dashoffset", offset); + } else { + elem = this.notifications.add(message.params[0], "progress", $(body)); + } + if (percent > 0) { + $(".body .circle-bg", elem).css({ + "animation-play-state": "paused", + "stroke-dasharray": "180px" + }); + } + if ($(".notification-icon", elem).data("done")) { + return false; + } else if (message.params[2] >= 100) { + $(".circle-fg", elem).css("transition", "all 0.3s ease-in-out"); + setTimeout((function() { + $(".notification-icon", elem).css({ + transform: "scale(1)", + opacity: 1 + }); + return $(".notification-icon .icon-success", elem).css({ + transform: "rotate(45deg) scale(1)" + }); + }), 300); + setTimeout(((function(_this) { + return function() { + return _this.notifications.close(elem); + }; + })(this)), 3000); + return $(".notification-icon", elem).data("done", true); + } else if (message.params[2] < 0) { + $(".body .circle-fg", elem).css("stroke", "#ec6f47").css("transition", "transition: all 0.3s ease-in-out"); + setTimeout(((function(_this) { + return function() { + $(".notification-icon", elem).css({ + transform: "scale(1)", + opacity: 1 + }); + elem.removeClass("notification-done").addClass("notification-error"); + return $(".notification-icon .icon-success", elem).removeClass("icon-success").html("!"); + }; + })(this)), 300); + return $(".notification-icon", elem).data("done", true); + } + }; + Wrapper.prototype.actionSetViewport = function(message) { this.log("actionSetViewport", message); if ($("#viewport").length > 0) {