Created
January 24, 2017 04:25
-
-
Save chris-schmitz/935a6fc9811c6508893fbf1b329f7638 to your computer and use it in GitHub Desktop.
search bar/query builder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
Note: This gist is from code I mocked up in the jsbin: http://jsbin.com/moqido/16/edit?js,output | |
--> | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width"> | |
<title>JS Bin</title> | |
<style id="jsbin-css"> | |
.search-bar{ | |
background-color: lightgray; | |
} | |
.search-bar > ul{ | |
width: 100%; | |
display: flex; | |
justify-content: center; | |
margin:0; | |
} | |
.search-bar > ul > li{ | |
display: inline-block; | |
margin: 5px; | |
} | |
.search-bar > ul > li > button{ | |
background-color: cyan; | |
outline: none; | |
border: 0; | |
padding:20px; | |
width: 150px; | |
font-size:20px; | |
} | |
.search-drawer{ | |
background-color: gray; | |
padding: 0; | |
display: flex; | |
flex-direction: column; | |
} | |
.search-drawer > div { | |
display: flex; | |
text-align: center; | |
justify-content: space-around; | |
flex: 1; | |
} | |
.search-drawer > div > ul { | |
list-style: none; | |
} | |
.search-drawer > div > ul > li > button{ | |
background-color: white; | |
border: none; | |
outline: none; | |
margin: 5px; | |
padding: 5px; | |
width: 100px; | |
} | |
.search-drawer > div > ul > li > button.selected{ | |
background-color: cyan; | |
} | |
.search-drawer > button{ | |
background-color: cyan; | |
border: 0; | |
padding:10px; | |
width: 100px; | |
font-size:15px; | |
outline: none; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="search-bar-wrapper"> | |
<search-bar :search-options="searchOptionKeys"></search-bar> | |
<search-drawer v-if="showDrawer"></search-drawer> | |
</div> | |
<p> | |
Content not bound to viewmodel | |
</p> | |
<script type="text/x-template" id="search-bar"> | |
<div class="search-bar"> | |
Sort by: | |
<ul> | |
<li v-for="option in searchOptions"> | |
<button v-text="option" @click="showDrawer"></button> | |
</li> | |
</ul> | |
</div> | |
</script> | |
<script type="text/x-template" id="search-drawer"> | |
<div class="search-drawer"> | |
<div> | |
<ul v-for="(options, index) in state.searchOptions"> | |
<li>{{index}}</li> | |
<li v-for="option in options"> | |
<button @click="toggleInQuery(index, option)" :class="{'selected':option.includeInQuery}"> | |
{{ option.label }} | |
</button> | |
</li> | |
</ul> | |
</div> | |
<label> | |
Show QueryString as it's being built: | |
<input type="checkbox" v-model="showQueryStringInProgress"> | |
</label> | |
<p v-if="showQueryStringInProgress"> | |
{{ queryString }} | |
</p> | |
<button @click="buildAndClose">Apply</button> | |
</div> | |
</script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script> | |
<script id="jsbin-javascript"> | |
Vue.component('search-bar',{ | |
template: '#search-bar', | |
props: ['searchOptions'], | |
data: function(){ | |
return { | |
state: state | |
} | |
}, | |
methods:{ | |
showDrawer: function(){ | |
this.state.showDrawer = true | |
} | |
} | |
}) | |
Vue.component('search-drawer',{ | |
template: '#search-drawer', | |
data: function(){ | |
return { | |
state: state, | |
showQueryStringInProgress: false, | |
} | |
}, | |
computed:{ | |
queryString: function(){ | |
return Object.keys(state.searchOptions) | |
.map(function(key){ | |
return {key: key, details: state.searchOptions[key]} | |
}) | |
.map(function(optionDetails){ | |
// woof this one is ugly. I'm sure there's a way of flattening | |
// this double map while accomplishing the same goal, but I'm | |
// _hella_ tired so I'm not even gonna worry about it now!!!!:Oomg! | |
return optionDetails.details.map(function(details){ | |
details.key = optionDetails.key | |
return details | |
}) | |
}) | |
.reduce(function(carry, next){ return carry.concat(next)},[]) //flatten | |
.filter(function(optionData){ | |
return optionData.includeInQuery === true | |
}) | |
.map(function(optionDetails){ | |
return optionDetails.key + "=" +optionDetails.queryString | |
}) | |
.reduce(function(carry, next){ | |
return carry + next + "&" | |
},'') | |
} | |
}, | |
methods:{ | |
buildAndClose: function(){ | |
var queryString = "?" + this.queryString.substr(0, this.queryString.length - 1) // the last character is always going to be a & because of the last reduce in the queryString chain | |
alert("this is the query string we're gonna use (also printed in console.log): " + queryString) | |
console.log(queryString) | |
this.state.showDrawer = false | |
}, | |
toggleInQuery: function(optionType, option){ | |
// this is a _cheap shit_ way of handling the toggle. Refactor | |
// the shared state to be a store and extract this to a store method | |
// so that your component doesn't have to know this level of detailed | |
// knowledge about our shared state. And do that _in the morning_ ;) | |
if(optionType === 'date'){ | |
if(option.queryString === 'newest-to-oldest'){ | |
this.state.searchOptions.date[1].includeInQuery = false | |
} else { | |
this.state.searchOptions.date[0].includeInQuery = false | |
} | |
} | |
option.includeInQuery = !option.includeInQuery | |
} | |
} | |
}) | |
var state = { | |
showDrawer: true, // default false | |
searchOptions:{ | |
category:[ | |
{queryString: "what-is-modern-ag", label: "What is Modern Ag?", includeInQuery: false}, | |
{queryString: "using-less-water", label:"Using Less Water", includeInQuery: false}, | |
{queryString: "using-less-soil", label:"Using Less Soil", includeInQuery: false}, | |
{queryString: "using-less-energy", label:"Using Less Energy", includeInQuery: false}, | |
{queryString: "biodiversity", label:"Biodiversity", includeInQuery: false}, | |
{queryString: "technology-spotlight", label:"Technology Spotlight", includeInQuery: false} | |
], | |
type:[ | |
{queryString: "articles", label:"Articles", includeInQuery: false}, | |
{queryString: "questions", label:"Questions", includeInQuery: false} | |
], | |
date: [ | |
{ queryString: "newest-to-oldest", label:"Newest to Oldest", includeInQuery: false}, | |
{ queryString: "oldest-to-newest", label:"Oldest to Newest", includeInQuery: false} | |
] | |
} | |
} | |
new Vue({ | |
el: '#search-bar-wrapper', | |
data: state, | |
computed:{ | |
searchOptionKeys: function(){ | |
return Object | |
.keys(this.searchOptions) | |
// can add maps to title case it or jsut use uppercase key names | |
} | |
} | |
}) | |
</script> | |
<script id="jsbin-source-css" type="text/css">.search-bar{ | |
background-color: lightgray; | |
} | |
.search-bar > ul{ | |
width: 100%; | |
display: flex; | |
justify-content: center; | |
margin:0; | |
} | |
.search-bar > ul > li{ | |
display: inline-block; | |
margin: 5px; | |
} | |
.search-bar > ul > li > button{ | |
background-color: cyan; | |
outline: none; | |
border: 0; | |
padding:20px; | |
width: 150px; | |
font-size:20px; | |
} | |
.search-drawer{ | |
background-color: gray; | |
padding: 0; | |
display: flex; | |
flex-direction: column; | |
} | |
.search-drawer > div { | |
display: flex; | |
text-align: center; | |
justify-content: space-around; | |
flex: 1; | |
} | |
.search-drawer > div > ul { | |
list-style: none; | |
} | |
.search-drawer > div > ul > li > button{ | |
background-color: white; | |
border: none; | |
outline: none; | |
margin: 5px; | |
padding: 5px; | |
width: 100px; | |
} | |
.search-drawer > div > ul > li > button.selected{ | |
background-color: cyan; | |
} | |
.search-drawer > button{ | |
background-color: cyan; | |
border: 0; | |
padding:10px; | |
width: 100px; | |
font-size:15px; | |
outline: none; | |
}</script> | |
<script id="jsbin-source-javascript" type="text/javascript">Vue.component('search-bar',{ | |
template: '#search-bar', | |
props: ['searchOptions'], | |
data: function(){ | |
return { | |
state: state | |
} | |
}, | |
methods:{ | |
showDrawer: function(){ | |
this.state.showDrawer = true | |
} | |
} | |
}) | |
Vue.component('search-drawer',{ | |
template: '#search-drawer', | |
data: function(){ | |
return { | |
state: state, | |
showQueryStringInProgress: false, | |
} | |
}, | |
computed:{ | |
queryString: function(){ | |
return Object.keys(state.searchOptions) | |
.map(function(key){ | |
return {key: key, details: state.searchOptions[key]} | |
}) | |
.map(function(optionDetails){ | |
// woof this one is ugly. I'm sure there's a way of flattening | |
// this double map while accomplishing the same goal, but I'm | |
// _hella_ tired so I'm not even gonna worry about it now!!!!:Oomg! | |
return optionDetails.details.map(function(details){ | |
details.key = optionDetails.key | |
return details | |
}) | |
}) | |
.reduce(function(carry, next){ return carry.concat(next)},[]) //flatten | |
.filter(function(optionData){ | |
return optionData.includeInQuery === true | |
}) | |
.map(function(optionDetails){ | |
return optionDetails.key + "=" +optionDetails.queryString | |
}) | |
.reduce(function(carry, next){ | |
return carry + next + "&" | |
},'') | |
} | |
}, | |
methods:{ | |
buildAndClose: function(){ | |
var queryString = "?" + this.queryString.substr(0, this.queryString.length - 1) // the last character is always going to be a & because of the last reduce in the queryString chain | |
alert("this is the query string we're gonna use (also printed in console.log): " + queryString) | |
console.log(queryString) | |
this.state.showDrawer = false | |
}, | |
toggleInQuery: function(optionType, option){ | |
// this is a _cheap shit_ way of handling the toggle. Refactor | |
// the shared state to be a store and extract this to a store method | |
// so that your component doesn't have to know this level of detailed | |
// knowledge about our shared state. And do that _in the morning_ ;) | |
if(optionType === 'date'){ | |
if(option.queryString === 'newest-to-oldest'){ | |
this.state.searchOptions.date[1].includeInQuery = false | |
} else { | |
this.state.searchOptions.date[0].includeInQuery = false | |
} | |
} | |
option.includeInQuery = !option.includeInQuery | |
} | |
} | |
}) | |
var state = { | |
showDrawer: true, // default false | |
searchOptions:{ | |
category:[ | |
{queryString: "what-is-modern-ag", label: "What is Modern Ag?", includeInQuery: false}, | |
{queryString: "using-less-water", label:"Using Less Water", includeInQuery: false}, | |
{queryString: "using-less-soil", label:"Using Less Soil", includeInQuery: false}, | |
{queryString: "using-less-energy", label:"Using Less Energy", includeInQuery: false}, | |
{queryString: "biodiversity", label:"Biodiversity", includeInQuery: false}, | |
{queryString: "technology-spotlight", label:"Technology Spotlight", includeInQuery: false} | |
], | |
type:[ | |
{queryString: "articles", label:"Articles", includeInQuery: false}, | |
{queryString: "questions", label:"Questions", includeInQuery: false} | |
], | |
date: [ | |
{ queryString: "newest-to-oldest", label:"Newest to Oldest", includeInQuery: false}, | |
{ queryString: "oldest-to-newest", label:"Oldest to Newest", includeInQuery: false} | |
] | |
} | |
} | |
new Vue({ | |
el: '#search-bar-wrapper', | |
data: state, | |
computed:{ | |
searchOptionKeys: function(){ | |
return Object | |
.keys(this.searchOptions) | |
// can add maps to title case it or jsut use uppercase key names | |
} | |
} | |
})</script></body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.search-bar{ | |
background-color: lightgray; | |
} | |
.search-bar > ul{ | |
width: 100%; | |
display: flex; | |
justify-content: center; | |
margin:0; | |
} | |
.search-bar > ul > li{ | |
display: inline-block; | |
margin: 5px; | |
} | |
.search-bar > ul > li > button{ | |
background-color: cyan; | |
outline: none; | |
border: 0; | |
padding:20px; | |
width: 150px; | |
font-size:20px; | |
} | |
.search-drawer{ | |
background-color: gray; | |
padding: 0; | |
display: flex; | |
flex-direction: column; | |
} | |
.search-drawer > div { | |
display: flex; | |
text-align: center; | |
justify-content: space-around; | |
flex: 1; | |
} | |
.search-drawer > div > ul { | |
list-style: none; | |
} | |
.search-drawer > div > ul > li > button{ | |
background-color: white; | |
border: none; | |
outline: none; | |
margin: 5px; | |
padding: 5px; | |
width: 100px; | |
} | |
.search-drawer > div > ul > li > button.selected{ | |
background-color: cyan; | |
} | |
.search-drawer > button{ | |
background-color: cyan; | |
border: 0; | |
padding:10px; | |
width: 100px; | |
font-size:15px; | |
outline: none; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Vue.component('search-bar',{ | |
template: '#search-bar', | |
props: ['searchOptions'], | |
data: function(){ | |
return { | |
state: state | |
} | |
}, | |
methods:{ | |
showDrawer: function(){ | |
this.state.showDrawer = true | |
} | |
} | |
}) | |
Vue.component('search-drawer',{ | |
template: '#search-drawer', | |
data: function(){ | |
return { | |
state: state, | |
showQueryStringInProgress: false, | |
} | |
}, | |
computed:{ | |
queryString: function(){ | |
return Object.keys(state.searchOptions) | |
.map(function(key){ | |
return {key: key, details: state.searchOptions[key]} | |
}) | |
.map(function(optionDetails){ | |
// woof this one is ugly. I'm sure there's a way of flattening | |
// this double map while accomplishing the same goal, but I'm | |
// _hella_ tired so I'm not even gonna worry about it now!!!!:Oomg! | |
return optionDetails.details.map(function(details){ | |
details.key = optionDetails.key | |
return details | |
}) | |
}) | |
.reduce(function(carry, next){ return carry.concat(next)},[]) //flatten | |
.filter(function(optionData){ | |
return optionData.includeInQuery === true | |
}) | |
.map(function(optionDetails){ | |
return optionDetails.key + "=" +optionDetails.queryString | |
}) | |
.reduce(function(carry, next){ | |
return carry + next + "&" | |
},'') | |
} | |
}, | |
methods:{ | |
buildAndClose: function(){ | |
var queryString = "?" + this.queryString.substr(0, this.queryString.length - 1) // the last character is always going to be a & because of the last reduce in the queryString chain | |
alert("this is the query string we're gonna use (also printed in console.log): " + queryString) | |
console.log(queryString) | |
this.state.showDrawer = false | |
}, | |
toggleInQuery: function(optionType, option){ | |
// this is a _cheap shit_ way of handling the toggle. Refactor | |
// the shared state to be a store and extract this to a store method | |
// so that your component doesn't have to know this level of detailed | |
// knowledge about our shared state. And do that _in the morning_ ;) | |
if(optionType === 'date'){ | |
if(option.queryString === 'newest-to-oldest'){ | |
this.state.searchOptions.date[1].includeInQuery = false | |
} else { | |
this.state.searchOptions.date[0].includeInQuery = false | |
} | |
} | |
option.includeInQuery = !option.includeInQuery | |
} | |
} | |
}) | |
var state = { | |
showDrawer: true, // default false | |
searchOptions:{ | |
category:[ | |
{queryString: "what-is-modern-ag", label: "What is Modern Ag?", includeInQuery: false}, | |
{queryString: "using-less-water", label:"Using Less Water", includeInQuery: false}, | |
{queryString: "using-less-soil", label:"Using Less Soil", includeInQuery: false}, | |
{queryString: "using-less-energy", label:"Using Less Energy", includeInQuery: false}, | |
{queryString: "biodiversity", label:"Biodiversity", includeInQuery: false}, | |
{queryString: "technology-spotlight", label:"Technology Spotlight", includeInQuery: false} | |
], | |
type:[ | |
{queryString: "articles", label:"Articles", includeInQuery: false}, | |
{queryString: "questions", label:"Questions", includeInQuery: false} | |
], | |
date: [ | |
{ queryString: "newest-to-oldest", label:"Newest to Oldest", includeInQuery: false}, | |
{ queryString: "oldest-to-newest", label:"Oldest to Newest", includeInQuery: false} | |
] | |
} | |
} | |
new Vue({ | |
el: '#search-bar-wrapper', | |
data: state, | |
computed:{ | |
searchOptionKeys: function(){ | |
return Object | |
.keys(this.searchOptions) | |
// can add maps to title case it or jsut use uppercase key names | |
} | |
} | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment