ソースを参照

[spark] Improved add snippet UX

This introduces a radial menu for snippet type selection when adding a new snippet, the menu is shown on hover.
Johan Ahlen 10 年 前
コミット
e723ea6

+ 30 - 22
apps/spark/src/spark/static/spark/css/spark.css

@@ -123,20 +123,9 @@ body {
   margin: 10px;
   margin-top: 60px;
   margin-bottom: 60px;
-  background-color: #F1F1F1;
-}
-
-.add-snippet .CodeMirror {
-  opacity: .30;
-  border: 1px solid #DDD;
-  cursor: default;
-}
-
-.add-snippet .CodeMirror-lines {
-  cursor: default;
 }
 
-.add-snippet .overlay {
+.add-snippet-button {
   z-index: 300;
   margin-left: auto;
   margin-right: auto;
@@ -144,19 +133,38 @@ body {
   padding: 20px;
 }
 
-.add-snippet .overlay select {
-  float: left;
-  margin-top: 20px;
-  margin-right: 10px;
-}
-
-.add-snippet .overlay i {
+.add-last-used-snippet {
   color: #CCC;
   cursor: pointer;
 }
 
-.add-snippet .overlay i:hover, .add-snippet:hover .overlay i {
-  color: #666;
+.add-snippet-alt {
+  z-index: 400;
+  position: absolute;
+  background-color: #61abd1;
+  color: #DBE8F1;
+  line-height: 50px;
+  height: 50px;
+  width: 50px;
+  border-radius: 25px;
+  text-align: center;
+}
+
+.add-last-used-snippet,
+.add-snippet-alt {
+  -webkit-transition: color 0.3s ease, -webkit-transform 0.3s;
+  -ms-transition: color 0.3s ease, -ms-transform 0.3s;
+  transition: color 0.3s ease, transform 0.3s;
+}
+
+.add-snippet-alt:hover {
+  color: #FFF;
+}
+.add-last-used-snippet:hover,
+.add-snippet-alt:hover {
+  -webkit-transform: scale(1.3, 1.3);
+  -ms-transform: scale(1.3, 1.3);
+  transform: scale(1.3, 1.3);
 }
 
 .spark-btn {
@@ -490,4 +498,4 @@ table.airy tr td {
   display: inline-block;
   margin: 0 5px;
   position: relative;
-}
+}

+ 3 - 7
apps/spark/src/spark/templates/notebook.mako

@@ -37,14 +37,11 @@ ${ editorComponents.includes() }
   <%def name="addSnippetHTML()">
     <h1 class="empty" data-bind="visible: snippets().length == 0">${ _('Add a snippet to start your new notebook') }</h1>
 
-    <div class="add-snippet pointer" data-bind="click: function(notebook, e){ if (!($(e.target).is('select'))){ newSnippet(); } }">
-      <div class="overlay pointer">
-        <select data-bind="options: $root.availableSnippets, value: selectedSnippet, optionsText: 'name', optionsValue: 'type'" style="width: 115px">
-        </select>
-        <i class="fa fa-plus-circle fa-5x" title="${ _('Add a new snippet') }"></i>
+    <div class="add-snippet">
+      <div class="add-snippet-button pointer" style="position:relative; width:65px; text-align: center;" data-bind="radialMenu: { alternatives: $root.availableSnippets, selected: selectedSnippet, mainAlternative: selectedSnippet, onSelect: newSnippet, alternativeCss: 'add-snippet-alt' }">
+        <i class="add-last-used-snippet fa fa-plus-circle fa-5x" title="${ _('Add a new snippet') }"></i>
       </div>
     </div>
-    <div class="overlay" style="padding-bottom:70px"></div>
   </%def>
 </%editorComponents:commonHTML>
 
@@ -70,7 +67,6 @@ ${ editorComponents.commonJS() }
     baseURL: "${ autocomplete_base_url | n,unicode }"
   });
 
-
   huePubSub.subscribe('assist.mainObjectChange', function (db) {
     aceAutocomplete.setDatabase(db);
   });

+ 72 - 0
desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js

@@ -148,6 +148,78 @@ ko.bindingHandlers.numericTextInput = {
   }
 };
 
+ko.bindingHandlers.radialMenu = {
+  init: function(element, valueAccessor) {
+    var $element = $(element);
+    var options = valueAccessor();
+
+    // This binding currently expects each alternative to have an observable
+    // named "type"
+    var alternatives = options.alternatives;
+    var selected = options.selected; // Will be set before onSelect is called
+    var mainAlt = options.mainAlternative; // Alternative for clicking center
+    var onSelect = options.onSelect || $.noop;
+    var minRadius = options.minRadius || 70;
+    var alternativeCss = options.alternativeCss;
+    var alternativeSize = options.alternativeSize || 65;
+
+    var allAlternatives = $("<div>").hide();
+
+    var hideTimeout = -1;
+    var hideAlternatives = function () {
+      clearTimeout(hideTimeout);
+      hideTimeout = setTimeout(function () {
+        allAlternatives.fadeOut();
+      }, 600);
+    };
+
+    var select = function (selectedValue) {
+      selected(selectedValue);
+      onSelect(selectedValue);
+      clearTimeout(hideTimeout);
+      allAlternatives.fadeOut();
+    };
+
+    if (alternatives().length > 1) {
+      window.setTimeout(function() {
+        var circumference = alternatives().length * alternativeSize;
+        var radius = Math.max(minRadius, circumference / Math.PI / 2);
+        var radIncrements = 2 * Math.PI / alternatives().length;
+        var currentRad = alternatives().length == 2 ? 2 * Math.PI : -0.5 * Math.PI;
+        var iconRadius = $element.find("i").width() / 2;
+
+        $.each(alternatives(), function (index, alternative) {
+          $("<div>")
+            .text(alternative.type())
+            .addClass(alternativeCss)
+            .css("left", radius * Math.cos(currentRad) + iconRadius)
+            .css("top", radius * Math.sin(currentRad) + iconRadius)
+            .on("click", function () {
+              select(alternative.type());
+            })
+            .on("mouseenter", function () {
+              clearTimeout(hideTimeout);
+            })
+            .on("mouseleave", hideAlternatives)
+            .appendTo(allAlternatives);
+          currentRad += radIncrements;
+        });
+        $element.append(allAlternatives);
+      }, 500);
+    }
+
+    $element.find("i")
+      .on("click", function() {
+        select(mainAlt());
+      })
+      .on("mouseenter", function() {
+        clearTimeout(hideTimeout);
+        allAlternatives.fadeIn();
+      })
+      .on("mouseleave", hideAlternatives);
+  }
+};
+
 ko.bindingHandlers.freshereditor = {
   init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
     var _el = $(element);