From a6c1c0c730f1608baaa4467bd84ee56b704b00a2 Mon Sep 17 00:00:00 2001
From: Devin Howard <devin@callysto.com>
Date: Sun, 5 Mar 2017 00:51:51 +0800
Subject: [PATCH] ability to select/unselect all metacodes in custom set with
 keyboard shortcut (fix #390) (#1078)

* ability to select/unselect all metacodes in custom set with keyboard shortcut

* select all button

* nicer all/none buttons
---
 app/assets/stylesheets/application.scss.erb | 62 +++++++++++++--------
 app/views/shared/_switchmetacodes.html.erb  |  6 +-
 frontend/src/Metamaps/Create.js             | 43 +++++++++++++-
 frontend/src/Metamaps/Listeners.js          |  7 ++-
 4 files changed, 92 insertions(+), 26 deletions(-)

diff --git a/app/assets/stylesheets/application.scss.erb b/app/assets/stylesheets/application.scss.erb
index b16983df..2045628c 100644
--- a/app/assets/stylesheets/application.scss.erb
+++ b/app/assets/stylesheets/application.scss.erb
@@ -2311,6 +2311,9 @@ and it won't be important on password protected instances */
 }
 /* switch metacode set */
 
+#switchMetacodes > p {
+    margin: 16px 0 16px 0;
+}
 #metacodeSwitchTabs {
     width: 100%;
     font-size: 17px;
@@ -2318,28 +2321,43 @@ and it won't be important on password protected instances */
     border: none;
     background: none;
     padding: 0;
-}
-#metacodeSwitchTabs .setDesc {
-    margin-bottom: 5px;
-    font-family: 'din-medium', helvetica, sans-serif;
-    color: #424242;
-    font-size: 14px;
-    text-align: justify;
-    padding-right: 16px;
-}
-#switchMetacodes > p {
-    margin: 16px 0 16px 0;
-}
-#metacodeSwitchTabs > ul {
-    width: 130px;
-}
-#metacodeSwitchTabs > ul li {
-    font-size: 14px;
-    text-transform: uppercase;
-}
-#metacodeSwitchTabs li.ui-state-active a {
-    color: #00BCD4;
-    cursor: pointer;
+
+    .setDesc,
+    .selectAll,
+    .selectNone {
+        margin-bottom: 5px;
+        font-family: 'din-medium', helvetica, sans-serif;
+        color: #424242;
+        font-size: 14px;
+        text-align: justify;
+        padding-right: 16px;
+        display: inline-block;
+    }
+
+    .selectAll,
+    .selectNone {
+      float: right;
+      cursor: pointer;
+
+      &:hover,
+      &.selected {
+        color: #00bcd4;
+      }
+    }
+
+    & > ul {
+        width: 130px;
+
+        li {
+            font-size: 14px;
+            text-transform: uppercase;
+        }
+    }
+
+    li.ui-state-active a {
+        color: #00BCD4;
+        cursor: pointer;
+    }
 }
 .metacodeSwitchTab {
     max-height: 300px;
diff --git a/app/views/shared/_switchmetacodes.html.erb b/app/views/shared/_switchmetacodes.html.erb
index b5607065..9dbdabb6 100644
--- a/app/views/shared/_switchmetacodes.html.erb
+++ b/app/views/shared/_switchmetacodes.html.erb
@@ -91,7 +91,9 @@
   </div>
   <% end %>
     <div id="metacodeSwitchTabsCustom">
-        <p class="setDesc">Choose Your Metacodes</p>
+        <div class="setDesc">Choose Your Metacodes</div>
+        <div class="selectNone">NONE</div>
+        <div class="selectAll">ALL</div>
         <% @list = '' %>
         <% metacodesInUse = user_metacodes() %> 
         <% Metacode.order("name").all.each_with_index do |m, index| %>
@@ -116,4 +118,4 @@
 <script>
   Metamaps.Create.selectedMetacodeSet = "metacodeset-<%= selectedSet %>"
   Metamaps.Create.selectedMetacodeSetIndex = <%= index %>
-</script>
\ No newline at end of file
+</script>
diff --git a/frontend/src/Metamaps/Create.js b/frontend/src/Metamaps/Create.js
index 466900e5..b01642c5 100644
--- a/frontend/src/Metamaps/Create.js
+++ b/frontend/src/Metamaps/Create.js
@@ -28,6 +28,8 @@ const Create = {
     }).addClass('ui-tabs-vertical ui-helper-clearfix')
     $('#metacodeSwitchTabs .ui-tabs-nav li').removeClass('ui-corner-top').addClass('ui-corner-left')
     $('.customMetacodeList li').click(self.toggleMetacodeSelected) // within the custom metacode set tab
+    $('.selectAll').click(self.metacodeSelectorSelectAll)
+    $('.selectNone').click(self.metacodeSelectorSelectNone)
   },
   toggleMetacodeSelected: function() {
     var self = Create
@@ -43,6 +45,46 @@ const Create = {
       self.newSelectedMetacodes.push($(this).attr('id'))
       self.newSelectedMetacodeNames.push($(this).attr('data-name'))
     }
+    self.updateSelectAllColors()
+  },
+  updateSelectAllColors: function() {
+    $('.selectAll, .selectNone').removeClass('selected')
+    if (Create.metacodeSelectorAreAllSelected()) {
+      $('.selectAll').addClass('selected')
+    } else if (Create.metacodeSelectorAreNoneSelected()) {
+      $('.selectNone').addClass('selected')
+    }
+  },
+  metacodeSelectorSelectAll: function() {
+    $('.customMetacodeList li.toggledOff').each(Create.toggleMetacodeSelected)
+    Create.updateSelectAllColors()
+  },
+  metacodeSelectorSelectNone: function() {
+    $('.customMetacodeList li').not('.toggledOff').each(Create.toggleMetacodeSelected)
+    Create.updateSelectAllColors()
+  },
+  metacodeSelectorAreAllSelected: function() {
+    return $('.customMetacodeList li').toArray()
+             .map(li => !$(li).is('.toggledOff')) // note the ! on this line
+             .reduce((curr, prev) => curr && prev)
+  },
+  metacodeSelectorAreNoneSelected: function() {
+    return $('.customMetacodeList li').toArray()
+             .map(li => $(li).is('.toggledOff'))
+             .reduce((curr, prev) => curr && prev)
+  },
+  metacodeSelectorToggleSelectAll: function() {
+    // should be called when Create.isSwitchingSet is true and .customMetacodeList is visible
+    if (!Create.isSwitchingSet) return
+    if (!$('.customMetacodeList').is(':visible')) return
+
+    // If all are selected, then select none. Otherwise, select all.
+    if (Create.metacodeSelectorAreAllSelected()) {
+      Create.metacodeSelectorSelectNone()
+    } else {
+      // if some, but not all, are selected, it still runs this function
+      Create.metacodeSelectorSelectAll()
+    }
   },
   updateMetacodeSet: function(set, index, custom) {
     if (custom && Create.newSelectedMetacodes.length === 0) {
@@ -114,7 +156,6 @@ const Create = {
       }
     })
   },
-
   cancelMetacodeSetSwitch: function() {
     var self = Create
     self.isSwitchingSet = false
diff --git a/frontend/src/Metamaps/Listeners.js b/frontend/src/Metamaps/Listeners.js
index c3b644df..ea34f396 100644
--- a/frontend/src/Metamaps/Listeners.js
+++ b/frontend/src/Metamaps/Listeners.js
@@ -1,6 +1,7 @@
 /* global $ */
 
 import Active from './Active'
+import Create from './Create'
 import Control from './Control'
 import DataModel from './DataModel'
 import JIT from './JIT'
@@ -35,7 +36,11 @@ const Listeners = {
           Control.deleteSelected()
           break
         case 65: // if a or A is pressed
-          if ((e.ctrlKey || e.metaKey) && onCanvas) {
+          if (Create.isSwitchingSet && e.ctrlKey || e.metaKey) {
+            Create.metacodeSelectorToggleSelectAll()
+            e.preventDefault()
+            break
+          } else if ((e.ctrlKey || e.metaKey) && onCanvas) {
             const nodesCount = Object.keys(Visualize.mGraph.graph.nodes).length
             const selectedNodesCount = Selected.Nodes.length
             e.preventDefault()