3 Commits d289d17b2e ... 63f3c6409f

Author SHA1 Message Date
  Bachir Soussi Chiadmi 63f3c6409f refactored grid with flex, displayed footer tabs, diplay results, toggle results display 4 years ago
  Bachir Soussi Chiadmi c9e7a335c1 added footer tabs 4 years ago
  Bachir Soussi Chiadmi 8e9a8489e4 ran npm update 4 years ago

+ 2 - 1
.eslintrc.js

@@ -26,6 +26,7 @@ module.exports = {
     'vue/singleline-html-element-content-newline': 'off',
     'vue/multiline-html-element-content-newline': 'off',
     'vue/max-attributes-per-line': 'off',
-    'vue/no-v-html': 'off'
+    'vue/no-v-html': 'off',
+    'vue-a11y/label-has-for': 'off'
   }
 }

+ 180 - 9
assets/css/app.scss

@@ -1,7 +1,7 @@
 @import './base/reset';
 @import './base/variables';
 @import './base/colors';
-@import './base/grid';
+@import './base/grid-flex';
 @import './base/layout';
 @import './base/fonts';
 
@@ -10,6 +10,9 @@ body{
   color: #1a1a1a;
 }
 
+#root{
+}
+
 header[role="banner"]{
   div.wrapper{
     display: grid;
@@ -51,17 +54,185 @@ header[role="banner"]{
 section[role="main-content"]{
 
 }
-footer[role="search-bar"]{
-  #search{
-    color: #fff;
-    background-color: $bleuroi;
+footer[role="tools"]{
+  #history{
+    background-color: $or;
+    padding:1.2em $side-padding;
   }
   #results{
-    // color: #1a1a1a;
     background-color: $gris;
+    max-height: 50vh;
+    padding:1.2em $side-padding;
+    section.col-1{
+      .results-count{
+        font-size: 0.756em;
+      }
+    }
+    .results-list{
+      overflow-x: hidden;
+      .wrapper{
+        height:100%;
+        overflow-y: auto;
+        width:calc(100% + 1em);
+        padding-right: 1em;
+        >ul{
+          padding:0;
+          display: flex;
+          flex-direction: row;
+          flex-wrap: wrap;
+        }
+      }
+      li.result{
+        flex-basis: percentage(2/$default_sum);
+        height: 5.3em;
+        overflow: hidden;
+        margin-bottom: 1em;
+        padding-right: 0.7em;
+        box-sizing: border-box;
+      }
+      article.result.item{
+        header{
+          h1{
+            font-size: 0.882em;
+            font-weight: normal;
+            margin:0 0 0.5em 0;
+          }
+        }
+        .extract{
+          p{
+            font-size: 0.882em;
+            margin:0;
+          }
+          code{
+            background-color: lighten(desaturate($rouge,20%), 20%);
+          }
+        }
+      }
+    }
   }
-  #history{
-    // color: #1a1a1a;
-    background-color: $or;
+  #footer-bottom{
+    padding:0 $side-padding;
+    background-color: $bleuroi;
+    &>*{
+      // disable grid gap
+      padding-right: 0;
+    }
+    #footer-tabs{
+      ul{
+        padding:0; margin:0;
+        display: flex;
+        flex-direction: column;
+        li{
+          flex: 1 1 auto;
+          .wrapper{
+            box-sizing: border-box;
+            line-height: 0.6em;
+            height:2em;
+            width: calc(100% + $side-padding);
+            margin-left:-$side-padding;
+            padding:0.3em 0.5em 0.3em $side-padding;
+          }
+          &.history .wrapper{
+            background-color: $or;
+          }
+          &.results .wrapper{
+            background-color: $gris;
+          }
+          span{
+            font-size: 0.693em;
+            font-weight: 400;
+            text-transform: uppercase;
+            cursor: pointer;
+          }
+        }
+      }
+    }
+    #search{
+      color: #fff;
+      background-color: $bleuroi;
+      form{
+        padding: 0.7em;
+        label[for="keys"]{
+          display: none;
+        }
+        input[type="text"]{
+          padding:0.3em;
+          font-size: 0.756em;
+          line-height: 1;
+          height:1em;
+          border:none;
+          border-radius: 2px;
+        }
+        // input[type="submit"]{
+        //   #submit-search{
+        //     border:none;
+        //
+        //   }
+        // }
+        span.mdi{
+          display: inline-block;
+          font-size: 1.2em;
+          line-height:1.1;
+          vertical-align:middle;
+          width:1.2em; height:1.2em;
+          border-radius: 0.6em;
+          background-color: #fff;
+          color: $bleuroi;
+          text-align: center;
+          font-weight: 700;
+          cursor: pointer;
+        }
+      }
+    }
+  }
+  h2{
+    margin:0;
+    font-size: 0.756em;
+    font-weight: 400;
+    text-transform: uppercase;
+    padding:0;
   }
 }
+
+
+//  ___
+// |_ _|__ ___ _ _  ___
+//  | |/ _/ _ \ ' \(_-<
+// |___\__\___/_||_/__/
+
+span.mdi-close{
+  cursor: pointer;
+}
+
+@keyframes spin {
+    from {
+        transform:rotate(0deg);
+    }
+    to {
+        transform:rotate(360deg);
+    }
+}
+
+span.mdi-loading{
+  animation-name: spin;
+  animation-duration: 2000ms;
+  animation-iteration-count: infinite;
+  animation-timing-function: linear;
+}
+
+
+//  _                    _ _   _
+// | |_ _ _ __ _ _ _  __(_) |_(_)___ _ _  ___
+// |  _| '_/ _` | ' \(_-< |  _| / _ \ ' \(_-<
+//  \__|_| \__,_|_||_/__/_|\__|_\___/_||_/__/
+
+.fade-roll-enter-active{
+  transition: all .3s ease-in-out;
+}
+.fade-roll-leave-active{
+  transition: all .8s ease;
+}
+.fade-roll-enter, .fade-roll-leave-to{
+  opacity:0;
+  // height:1px;
+}

+ 86 - 0
assets/css/base/_grid-flex.scss

@@ -0,0 +1,86 @@
+// http://www.thesassway.com/intermediate/simple-grid-mixins
+
+
+@mixin row() {
+  display:flex;
+  flex-direction: row;
+  flex-wrap: nowrap;
+  align-items: stretch;
+  // &:after{
+  //   content:"";
+  //   clear:both;
+  //   display: block;
+  // }
+}
+
+.row{
+  @include row;
+}
+.row-rl{
+  @include row;
+}
+
+%col-reset {
+    box-sizing: border-box;
+}
+
+@mixin col($col, $offset: 0, $sum: $default_sum, $gap: $default_gap, $align: top) {
+  @extend %col-reset;
+  padding-left: $gap*$offset;
+  @if $col == $default_sum {
+    // if last col, then no gap
+    padding-right: 0;
+  }@else{
+    padding-right: $gap;
+  }
+  &:last-child{padding-right: 0;}
+
+  // no offset with flex ??
+  // margin-left: percentage(($col/$sum)*$offset);
+
+  // col width
+  flex-basis: percentage($col/$sum);
+}
+
+@for $c from 1 through $default_sum {
+  .col-#{$c} {
+      @include col($c);
+  }
+
+  // small
+  .small-col-#{$c} {
+    @media only screen and (max-width: $small-bp) {
+      @include col($c);
+    }
+  }
+
+  // medium
+  .med-col-#{$c} {
+    @media only screen and (min-width: $small-bp+1) and (max-width: $med-bp) {
+      @include col($c);
+    }
+  }
+
+  // large
+  .large-col-#{$c} {
+    @media only screen and (min-width: $med-bp+1) {
+      @include col($c);
+    }
+  }
+
+}
+
+@for $c from 1 through $default_sum - 1 {
+  @for $o from 1 through $default_sum - $c {
+    .col-#{$c}-offset-#{$o} {
+      @include col($c, $o);
+    }
+  }
+}
+
+// TODO: replace with align-self:flex-start or flex-end
+// .col.float-right{
+//   float: right;
+//   padding-right: 0;
+//   padding-left: $default_gap;
+// }

+ 8 - 6
assets/css/base/_layout.scss

@@ -1,3 +1,5 @@
+$side-padding:3em;
+
 body, html{
   position: relative;
   width: 100%;
@@ -24,21 +26,21 @@ body{
   header[role="banner"]{
     flex: 0 0 auto;
     @extend %layout-element;
-    padding:1em 1em 0 1em;
+    padding:1em $side-padding 0 $side-padding;
   }
   section[role="main-content"]{
     flex:1 1 auto;
     @extend %layout-element;
-    padding:0 1em 0 1em;
+    padding:0 $side-padding 0 $side-padding;
     overflow-y: auto;
     overflow-x: hidden;
   }
-  footer[role="search-bar"]{
+  footer[role="tools"]{
     flex:0 0 auto;
     @extend %layout-element;
     // padding-bottom: 1em;
-    >*{
-      padding:0.5em 1em;
-    }
+    // >*{
+    //   padding:0.5em 1em;
+    // }
   }
 }

+ 1 - 1
assets/css/base/_variables.scss

@@ -2,7 +2,7 @@ $base_font_size:16px;
 
 // grid
 $default_gap: 1em;
-$default_sum: 12;
+$default_sum: 12; // total number of columns
 
 $small-bp:768px;
 $med-bp:1080px;

File diff suppressed because it is too large
+ 309 - 506
package-lock.json


+ 25 - 25
package.json

@@ -18,51 +18,51 @@
   "author": "Daniel Cook",
   "license": "MIT",
   "dependencies": {
-    "axios": "^0.18.0",
+    "axios": "^0.18.1",
     "vue": "^2.6.10",
     "vue-meta": "^1.6.0",
-    "vue-router": "^3.0.2",
-    "vuex": "^3.1.0"
+    "vue-router": "^3.1.3",
+    "vuex": "^3.1.1"
   },
   "devDependencies": {
-    "@babel/core": "^7.4.0",
-    "@babel/preset-env": "^7.4.2",
+    "@babel/core": "^7.6.0",
+    "@babel/preset-env": "^7.6.0",
     "@vue/test-utils": "^1.0.0-beta.29",
     "babel-core": "^7.0.0-bridge.0",
-    "babel-eslint": "^10.0.1",
-    "babel-jest": "^24.5.0",
-    "babel-loader": "^8.0.5",
-    "copy-webpack-plugin": "^5.0.2",
+    "babel-eslint": "^10.0.3",
+    "babel-jest": "^24.9.0",
+    "babel-loader": "^8.0.6",
+    "copy-webpack-plugin": "^5.0.4",
     "css-loader": "^2.1.1",
-    "eslint": "^5.15.3",
+    "eslint": "^5.16.0",
     "eslint-config-standard": "^12.0.0",
-    "eslint-loader": "^2.1.2",
-    "eslint-plugin-import": "^2.16.0",
+    "eslint-loader": "^2.2.1",
+    "eslint-plugin-import": "^2.18.2",
     "eslint-plugin-node": "^8.0.1",
-    "eslint-plugin-promise": "^4.0.1",
-    "eslint-plugin-standard": "^4.0.0",
-    "eslint-plugin-vue": "^5.2.2",
+    "eslint-plugin-promise": "^4.2.1",
+    "eslint-plugin-standard": "^4.0.1",
+    "eslint-plugin-vue": "^5.2.3",
     "eslint-plugin-vue-a11y": "0.0.28",
-    "file-loader": "^4.1.0",
+    "file-loader": "^4.2.0",
     "html-webpack-plugin": "^3.2.0",
-    "jest": "^24.5.0",
+    "jest": "^24.9.0",
     "jest-serializer-vue": "^2.0.2",
     "mini-css-extract-plugin": "^0.5.0",
     "node-sass": "^4.12.0",
-    "sass-loader": "^7.1.0",
+    "sass-loader": "^7.3.1",
     "style-loader": "^0.23.1",
-    "stylus": "^0.54.5",
+    "stylus": "^0.54.7",
     "stylus-loader": "^3.0.2",
     "uglify-es": "^3.3.9",
     "url-loader": "^1.1.2",
-    "vue-jest": "^3.0.4",
-    "vue-loader": "^15.7.0",
+    "vue-jest": "^3.0.5",
+    "vue-loader": "^15.7.1",
     "vue-server-renderer": "^2.6.10",
     "vue-style-loader": "^4.1.2",
     "vue-template-compiler": "^2.6.10",
-    "webpack": "^4.29.6",
-    "webpack-cli": "^3.3.0",
-    "webpack-dev-server": "^3.2.1",
-    "webpack-merge": "^4.2.1"
+    "webpack": "^4.40.2",
+    "webpack-cli": "^3.3.9",
+    "webpack-dev-server": "^3.8.1",
+    "webpack-merge": "^4.2.2"
   }
 }

+ 18 - 8
src/App.vue

@@ -14,20 +14,24 @@
     <section role="main-content">
       <RouterView />
     </section>
-    <footer role="search-bar">
+    <footer role="tools">
       <History />
-      <Results />
-      <Search />
+      <Results v-if="resultsOpened" />
+      <div id="footer-bottom" class="row">
+        <FooterTabs />
+        <Search />
+      </div>
     </footer>
   </div>
 </template>
 
 <script>
 import HeaderMenu from './components/nav/HeaderMenu'
-import Search from './components/nav/Search'
-import Results from './components/nav/Results'
 import History from './components/nav/History'
-// import { mapState, mapActions } from 'vuex'
+import Results from './components/nav/Results'
+import Search from './components/nav/Search'
+import FooterTabs from './components/nav/FooterTabs'
+import { mapState } from 'vuex'
 
 export default {
   metaInfo: {
@@ -38,9 +42,15 @@ export default {
   },
   components: {
     HeaderMenu,
-    Search,
+    History,
     Results,
-    History
+    Search,
+    FooterTabs
+  },
+  computed: {
+    ...mapState({
+      resultsOpened: state => state.Search.opened
+    })
   }
 }
 </script>

+ 49 - 0
src/components/nav/FooterTabs.vue

@@ -0,0 +1,49 @@
+<template>
+  <div id="footer-tabs" class="col-1">
+    <ul>
+      <li class="history">
+        <div class="wrapper">
+          <span>Historique de consultation</span>
+        </div>
+      </li>
+      <li class="results">
+        <div class="wrapper">
+          <span
+            v-if="resultsItems.length && !resultsOpened"
+            title="Ouvrir les resultats"
+            @click.prevent="openResults"
+            @keydown.enter.prevent="openResults"
+          >
+            Resultas
+          </span>
+        </div>
+      </li>
+    </ul>
+  </div>
+</template>
+
+<script>
+
+import { mapState } from 'vuex'
+
+export default {
+  name: 'FooterTabs',
+  computed: {
+    resultsOpened: {
+      get () { return this.$store.state.Search.opened },
+      set (value) { this.$store.commit('Search/setOpened', value) }
+    },
+    ...mapState({
+      resultsItems: state => state.Search.results
+    })
+  },
+  methods: {
+    openResults () {
+      this.resultsOpened = true
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 1 - 1
src/components/nav/History.vue

@@ -1,6 +1,6 @@
 <template>
   <div id="history">
-    History
+    <h2>Historique de consultation</h2>
   </div>
 </template>
 <script>

+ 38 - 14
src/components/nav/Results.vue

@@ -1,16 +1,33 @@
 <template>
-  <div id="results">
-    <h2>Resultats</h2>
-    <h3>{{ keys }}</h3>
-    <div class="results-list">
-      <ul v-if="results.length">
-        <li v-for="result in results" :key="result.uuid">
-          <ResultItem :result="result" />
-        </li>
-      </ul>
+  <transition name="fade-roll">
+    <div
+      v-if="opened"
+      id="results"
+      class="row"
+    >
+      <section class="col-1">
+        <h2>Resultats</h2>
+        <span class="results-count">{{ results.length }} resultat(s)</span>
+      </section>
+      <section class="col-10 results-list">
+        <div class="wrapper">
+          <ul v-if="results.length">
+            <li v-for="result in results" :key="result.uuid" class="result">
+              <ResultItem :result="result" />
+            </li>
+          </ul>
+        </div>
+      </section>
+      <section class="col-1 tools">
+        <span
+          class="mdi mdi-close"
+          title="close"
+          @click.prevent="close"
+          @keydown.enter.prevent="close"
+        />
+      </section>
     </div>
-
-  </div>
+  </transition>
 </template>
 
 <script>
@@ -23,14 +40,21 @@ export default {
   components: {
     ResultItem
   },
-  data: () => ({
-
-  }),
   computed: {
+    opened: {
+      get () { return this.$store.state.Search.opened },
+      set (value) { this.$store.commit('Search/setOpened', value) }
+    },
     ...mapState({
       keys: state => state.Search.keys,
       results: state => state.Search.results
     })
+  },
+  methods: {
+    close () {
+      console.log('clicked on close results')
+      this.opened = false
+    }
   }
 }
 </script>

+ 30 - 15
src/components/nav/Search.vue

@@ -1,30 +1,42 @@
 <template>
-  <div id="search">
-    <form class="" action="index.html" method="post">
-      <label for="keys">
-        Search
-        <input
-          id="keys"
-          v-model="keys"
-          type="text"
-          placeholder="search"
-        >
-      </label>
+  <div id="search" class="col-11">
+    <form class="search-form">
+      <label for="keys">Search</label>
       <input
-        id="search"
+        id="keys"
+        v-model="keys"
+        type="text"
+        placeholder="search"
+        @keydown.enter.prevent="submit"
+      >
+      <span
+        v-if="!isloading"
+        class="mdi mdi-magnify"
+        title="rechercher"
+        @click.prevent="submit"
+        @keydown.enter.prevent="submit"
+      />
+      <span
+        v-else
+        class="mdi mdi-loading"
+        title="chargement"
+      />
+      <!-- <input
+        id="submit-search"
         type="submit"
         name="search"
         value="Search"
+        class="mdi mdi-magnify"
         @click.prevent="submit"
         @keyup.enter="submit"
-      >
+      > -->
     </form>
   </div>
 </template>
 
 <script>
 
-import { mapActions } from 'vuex'
+import { mapActions, mapState } from 'vuex'
 
 export default {
   name: 'Search',
@@ -35,7 +47,10 @@ export default {
     keys: {
       get () { return this.$store.state.Search.keys },
       set (value) { this.$store.commit('Search/setKeys', value) }
-    }
+    },
+    ...mapState({
+      isloading: state => state.Search.isloading
+    })
   },
   methods: {
     ...mapActions({

+ 13 - 1
src/store/modules/search.js

@@ -7,7 +7,9 @@ export default {
   // initial state
   state: {
     keys: '',
-    results: []
+    results: [],
+    isloading: false,
+    opened: false
   },
 
   // getters
@@ -20,6 +22,12 @@ export default {
     },
     setResults (state, content) {
       state.results = content
+    },
+    setIsloading (state, isloading) {
+      state.isloading = isloading
+    },
+    setOpened (state, opened) {
+      state.opened = opened
     }
   },
 
@@ -27,6 +35,7 @@ export default {
   actions: {
     getResults ({ dispatch, commit, state }) {
       console.log('getResults', state.keys)
+      commit('setIsloading', true)
       let params = {
         search: state.keys
       }
@@ -35,10 +44,13 @@ export default {
       return REST.get(`/search?` + q)
         .then(({ data }) => {
           console.log('search REST: data', data)
+          commit('setIsloading', false)
+          commit('setOpened', true)
           commit('setResults', data.content)
         })
         .catch((error) => {
           console.warn('Issue with search', error)
+          commit('setIsloading', false)
           Promise.reject(error)
         })
     }

Some files were not shown because too many files changed in this diff