diff --git a/src/main.rs b/src/main.rs index 39b95bebf..d04e1c96b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,16 +2,34 @@ extern crate serde_json; use actix_web::{web, web::Data, App, HttpRequest, HttpResponse, HttpServer, Responder, get, post}; -use handlebars::Handlebars; +use handlebars::*; use meilisearch_sdk::{document::*, indexes::*, client::*, search::*}; use serde::{Serialize, Deserialize}; use actix_files as fs; +#[derive(Clone, Copy)] +struct ContainsHelper; + +impl HelperDef for ContainsHelper { + fn call<'reg: 'rc, 'rc>(&self, h: &Helper<'reg, 'rc>, r: &'reg Handlebars<'_>, ctx: &'rc Context, rc: &mut RenderContext<'reg, 'rc>, out: &mut dyn Output) -> HelperResult { + let array = h.param(0).map(|v| serde_json::from_value::>(v.value().clone()).unwrap()).ok_or(RenderError::new("Parameter not found!"))?; + let mut value = h.param(1).map(|v| v.value().as_str().unwrap()).ok_or(RenderError::new("Parameter not found!"))?; + + let tmpl = if array.contains(&String::from(value)) { h.template() } else { h.inverse() }; + + match tmpl { + Some(ref t) => t.render(r, ctx, rc, out), + None => Ok(()), + } + } +} + #[derive(Serialize, Deserialize, Debug)] struct Mod { mod_id: usize, title: String, description: String, + keywords: Vec, } impl Document for Mod { @@ -90,9 +108,12 @@ async fn index(hb: web::Data>) -> HttpResponse { async fn main() -> std::io::Result<()> { //Handlebars let mut handlebars = Handlebars::new(); + + handlebars.register_helper("contains", Box::new(ContainsHelper)); handlebars .register_templates_directory(".hbs", "./templates") .unwrap(); + let handlebars_ref = web::Data::new(handlebars); //Search @@ -105,21 +126,25 @@ async fn main() -> std::io::Result<()> { mod_id: 0, title: String::from("Magic Mod"), description: String::from("An illustrious magic mod for magical wizards"), + keywords: vec![String::from("Fabric"), String::from("Magic")], }, Mod { mod_id: 1, title: String::from("Tech Mod"), description: String::from("An technological mod for complete NERDS"), + keywords: vec![String::from("Fabric"), String::from("Utility"), String::from("Technology")], }, Mod { mod_id: 2, title: String::from("Hood Mod"), description: String::from("A hood mod to roleplay as if you were a real street person. Some adventure stuff too"), + keywords: vec![String::from("Forge"), String::from("Adventure"), String::from("Technology")] }, Mod { mod_id: 3, title: String::from("Adventure Mod"), description: String::from("An epic gamer adventure mod for epic adventure gamers"), + keywords: vec![String::from("Forge"), String::from("Magic"), String::from("Adventure")] }, ], Some("mod_id")).unwrap(); diff --git a/static/css/main.css b/static/css/main.css new file mode 100644 index 000000000..58f4ad02d --- /dev/null +++ b/static/css/main.css @@ -0,0 +1,49 @@ +@import url('https://fonts.googleapis.com/css?family=Montserrat:400,600'); + +body { + margin: 0; + font-family: 'Montserrat', sans-serif; +} + +.temp-circle-logo { + height: 50px; + width: 50px; + background-color: white; + border-radius: 50%; + display: inline-block; + margin-left: 20%; +} + +.site-header { + position: sticky; + background-color: black; + width: 100%; + height: 75px; + margin-bottom: 10px; + display: flex; + align-items: center; +} + +.site-header h2 { + color: white; + font-weight: bolder; + padding: 0 20px 0 10px; +} + +.links-container { + margin: 0 auto; +} +.links-container a { + color: white; + padding: 0 30px; + text-decoration: none; +} + +.rounded-border { + border-radius: 10px; + border: 1px; +} + +.gray-border { + border: 1px solid darkgray; +} \ No newline at end of file diff --git a/static/css/multiselect.css b/static/css/multiselect.css deleted file mode 100644 index 5d9c5f5e5..000000000 --- a/static/css/multiselect.css +++ /dev/null @@ -1,122 +0,0 @@ -.multiselect-wrapper { - width: 180px; - display: inline-block; - white-space: nowrap; - font-size: 12px; - font-family: "Segoe UI", Verdana, Helvetica, Sans-Serif; -} - -.multiselect-wrapper .multiselect-input { - width: 100%; - padding-right: 50px; -} - -.multiselect-wrapper label { - display: block; - font-size: 12px; - font-weight : 600; -} - -.multiselect-wrapper .multiselect-list { - z-index: 1; - position: absolute; - display: none; - background-color: white; - border: 1px solid grey; - border-bottom-left-radius: 2px; - border-bottom-right-radius: 2px; - margin-top: -2px; -} - -.multiselect-wrapper .multiselect-list.active { - display: block; -} - -.multiselect-wrapper .multiselect-list > span { - font-weight: bold; -} - -.multiselect-wrapper .multiselect-list .multiselect-checkbox { - margin-right: 2px; -} - -.multiselect-wrapper .multiselect-list > span, -.multiselect-wrapper .multiselect-list li { - cursor: default; -} - -.multiselect-wrapper .multiselect-list { - padding: 5px; - min-width: 200px; -} - -.multiselect-wrapper ul { - list-style: none; - display: block; - position: relative; - padding: 0px; - margin: 0px; - max-height: 200px; - overflow-y: auto; - overflow-x: hidden; -} - -.multiselect-wrapper ul li { - padding-right: 20px; - display: block; -} - -.multiselect-wrapper ul li.active { - background-color: rgb(0, 102, 255); - color: white; -} - -.multiselect-wrapper ul li:hover { - background-color: rgb(0, 102, 255); - color: white; -} - -.multiselect-input-div { - height: 34px; -} - -.multiselect-input-div input{ - border: 1px solid #ababab; - background : #fff; - margin: 5px 0 6px 0; - padding: 5px; - vertical-align:middle; -} - -.multiselect-count { - position: relative; - text-align: center; - border-radius: 2px; - background-color: lightblue; - display: inline-block !important; - padding: 2px 7px; - left: -45px; -} - -.multiselect-wrapper.disabled .multiselect-dropdown-arrow { - border-top: 5px solid lightgray; -} - -.multiselect-wrapper.disabled .multiselect-count { - background-color: lightgray; -} - -.multiselect-dropdown-arrow { - width: 0; - height: 0; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - border-top: 5px solid black; - position: absolute; - line-height: 20px; - text-align: center; - display: inline-block !important; - margin-top: 17px; - margin-left: -42px; -} - diff --git a/static/css/search.css b/static/css/search.css new file mode 100644 index 000000000..498608039 --- /dev/null +++ b/static/css/search.css @@ -0,0 +1,102 @@ +.search-container { + width: 100%; + display: flex; + align-content: center; +} + +.search-bar { + margin: 0 auto; + width: 56.25%; + height: 2em; +} + +.results { + width: 75%; + margin: auto; +} + +.result { + display: flex; + height: 100px; + margin: 30px auto; + padding: 5px; + width: 75%; + flex-direction: row; + align-items: center; +} + +.result img { + padding: 0 10px 0 5px; +} + +.result-info { + display: flex; + flex-direction: column; +} + +.result-info * { + padding: 0 5px 0 0; + margin: 0; +} + +.result-badges { + margin: 0 1em 20px auto; + align-self: center; + height: 75px; + display: flex; + flex-direction: column; +} + +.result-badge { + display: flex; + flex-direction: row; + align-items: center; + height: 25px; + margin: 2px 0; + padding: 2px 5px; + width: 155px; +} + +.result-badge img { + height: 23px; +} + +.result-badge p { + margin: 0 auto; + font-weight: bolder; +} + +.download-badge { + background-color: limegreen; + color: white; +} + +.forge-badge { + color: #e1a15a; + background-color: #1e2d44; +} + +.fabric-badge { + color: #585849; + background-color: #fffdd0; +} + +.tech-badge { + color: white; + background-color: red; +} + +.adventure-badge { + color: white; + background-color: saddlebrown; +} + +.magic-badge { + color: white; + background-color: rebeccapurple; +} + +.utility-badge { + color: white; + background-color: orangered; +} \ No newline at end of file diff --git a/static/css/style.css b/static/css/style.css deleted file mode 100644 index 83aac1803..000000000 --- a/static/css/style.css +++ /dev/null @@ -1,48 +0,0 @@ -@import url('https://fonts.googleapis.com/css?family=Montserrat:400,600'); - -body { - margin: 0; - font-family: 'Montserrat', sans-serif; -} - -#categories_multiSelect { - width: 200px; -} - -.results { - width: 75%; - margin: auto; -} - -.result { - display: flex; - height: 100px; - margin: 30px auto; - padding: 5px; - width: 75%; - flex-direction: row; - align-items: center; -} - -.result img { - padding: 0 10px 0 0; -} - -.result-info { - display: flex; - flex-direction: column; -} - -.result-info * { - padding: 0 5px 0 0; - margin: 0; -} - -.rounded-border { - border-radius: 10px; - border: 1px; -} - -.gray-border { - border: 1px solid darkgray; -} \ No newline at end of file diff --git a/static/images/icon/adventure.svg b/static/images/icon/adventure.svg new file mode 100644 index 000000000..5599a1491 --- /dev/null +++ b/static/images/icon/adventure.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icon/download.svg b/static/images/icon/download.svg new file mode 100644 index 000000000..05d7c9bbf --- /dev/null +++ b/static/images/icon/download.svg @@ -0,0 +1,2 @@ + + diff --git a/static/images/icon/fabric.png b/static/images/icon/fabric.png new file mode 100644 index 000000000..4ab8370ff Binary files /dev/null and b/static/images/icon/fabric.png differ diff --git a/static/images/icon/forge.jpg b/static/images/icon/forge.jpg new file mode 100644 index 000000000..749dba107 Binary files /dev/null and b/static/images/icon/forge.jpg differ diff --git a/static/images/icon/magic.svg b/static/images/icon/magic.svg new file mode 100644 index 000000000..c13c657ed --- /dev/null +++ b/static/images/icon/magic.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/icon/tech.svg b/static/images/icon/tech.svg new file mode 100644 index 000000000..9f9a73ee5 --- /dev/null +++ b/static/images/icon/tech.svg @@ -0,0 +1,2 @@ + + diff --git a/static/images/icon/util.svg b/static/images/icon/util.svg new file mode 100644 index 000000000..0e4f2830d --- /dev/null +++ b/static/images/icon/util.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/js/multiselect.min.js b/static/js/multiselect.min.js deleted file mode 100644 index b2bf236e4..000000000 --- a/static/js/multiselect.min.js +++ /dev/null @@ -1,44 +0,0 @@ -if(!m_helper) -{var m_helper={removeNode:function(id){var el=document.getElementById(id);if(el){el.parentNode.removeChild(el)}},insertAfter:function(item,target){var parent=target.parentNode;if(target.nextElementSibling){parent.insertBefore(item,target.nextElementSibling)}else{parent.appendChild(item)}},hide:function(element){element.style.display='none'},hideAll:function(array){for(var i=0;i-1){window.multiselects._items.splice(index,1);window.multiselects.splice(index,1)}},select:function(val){this._toggle(val,!0)},deselect:function(val){this._toggle(val,!1)},setIsEnabled(isEnabled){if(this._isEnabled===isEnabled)return;var wrapperItem=document.getElementById(this._getIdentifier());if(isEnabled){wrapperItem.classList.remove('disabled')}else{wrapperItem.classList.add('disabled')} - m_helper.setDisabled(this._item,!isEnabled);m_helper.setDisabled(document.getElementById(this._getInputFieldIdentifier()),!isEnabled);this._isEnabled=isEnabled},_toggle:function(val,setCheck){var self=this;if(val){m_helper.each(document.getElementById(this._getIdentifier()).querySelectorAll('.multiselect-checkbox'),function(e){if(e.dataset.val==val){if(setCheck&&!e.checked){m_helper.check(e);self._onCheckBoxChange(e,self)}else if(!setCheck&&e.checked){m_helper.uncheck(e);self._onCheckBoxChange(e,self)}}});self._updateText(self)}},selectAll:function(val){var selectAllChkBox=document.querySelector('#'+this._getIdentifier()+' .multiselect-checkbox');m_helper.check(selectAllChkBox);this._onCheckBoxChange(selectAllChkBox,this);this._updateText(this)},deselectAll:function(){var selectAllChkBox=document.querySelector('#'+this._getIdentifier()+' .multiselect-checkbox');m_helper.uncheck(selectAllChkBox);this._onCheckBoxChange(selectAllChkBox,this);this._updateText(this)},_checkboxClickEvents:{},setCheckBoxClick(id,handler){if(typeof handler==="function"){this._checkboxClickEvents[id]=handler}else{console.error("Checkbox click handler for checkbox value="+id+" is not a function")} - return this},_appendEvents:function(){var self=this;document.getElementById(self._getInputFieldIdentifier()).addEventListener('focus',function(event){self._showList(self);document.getElementById(self._getInputFieldIdentifier()).value='';m_helper.each(window.multiselects,function(e){if(document.getElementById(e._getItemListIdentifier()).offsetParent&&m_helper.parent(event.target,e._getIdentifier())){e._hideList(self)}})});document.getElementById(self._getInputFieldIdentifier()).addEventListener('click',function(){self._showList(self);document.getElementById(self._getInputFieldIdentifier()).value=''});document.getElementById(self._getIdentifier()).addEventListener('click',function(event){event=event||window.event;var target=event.target||event.srcElement;if(m_helper.parent(target,self._getIdentifier())){event.stopPropagation()}});document.getElementById(self._getItemListIdentifier()).addEventListener('mouseover',function(){self._showList(self)});m_helper.each(document.getElementById(self._getIdentifier()).querySelectorAll('.multiselect-checkbox'),function(e){e.addEventListener('change',function(event){self._onCheckBoxChange(e,self,event)})});var onInput=function(){var text=this.value.toLowerCase();if(!text||text==''){m_helper.show(document.querySelector('#'+self._getItemListIdentifier()+' > span'));m_helper.show(document.querySelector('#'+self._getItemListIdentifier()+' > hr'));m_helper.showAll(document.querySelectorAll('#'+self._getItemListIdentifier()+' li'))}else{m_helper.hide(document.querySelector('#'+self._getItemListIdentifier()+' > span'));m_helper.hide(document.querySelector('#'+self._getItemListIdentifier()+' > hr'));var array=Array.prototype.filter.call(document.querySelectorAll('#'+self._getItemListIdentifier()+' li span'),function(obj){return obj.innerHTML.toLowerCase().indexOf(text)>-1});m_helper.hideAll(document.querySelectorAll('#'+self._getItemListIdentifier()+' li'));m_helper.each(array,function(e){m_helper.show(e.parentElement.parentElement)})}} - document.getElementById(self._getInputFieldIdentifier()).addEventListener('propertychange',onInput);document.getElementById(self._getInputFieldIdentifier()).addEventListener('input',onInput)},_onCheckBoxChange:function(checkbox,self,event){if(!checkbox.dataset.multiselectElement){var checkedState=self._performSelectAll(checkbox,self);if(typeof self._checkboxClickEvents.checkboxAll==="function"){self._checkboxClickEvents.checkboxAll(checkbox,{checked:checkedState})}} - else{var checkedState=self._performSelectItem(checkbox,self);if(typeof self._checkboxClickEvents[checkedState.id]==="function"){self._checkboxClickEvents[checkedState.id](checkbox,checkedState)} - self._updateSelectAll(self)} - self._forceUpdate()},_performSelectItem:function(checkbox,self){var item=JSON.parse(checkbox.dataset.multiselectElement);if(checkbox.checked){self._itemCounter++;m_helper.select(this._item.options[item.index]);m_helper.setActive(checkbox.parentElement.parentElement);return{id:item.id,checked:!0}} - self._itemCounter--;m_helper.deselect(this._item.options[item.index]);m_helper.setUnactive(checkbox.parentElement.parentElement);return{id:item.id,checked:!1}},_performSelectAll:function(checkbox,self){var items=self._getItems();if(checkbox.checked){self._itemCounter=items.length;m_helper.each(items,function(e){m_helper.setActive(e.multiselectElement.parentElement.parentElement);m_helper.select(self._item.options[e.index]);m_helper.check(e.multiselectElement)});return!0} - self._itemCounter=0;m_helper.each(items,function(e){e.multiselectElement.parentElement.parentElement.classList.remove('active');m_helper.deselect(self._item.options[e.index]);m_helper.uncheck(e.multiselectElement)});return!1},_updateSelectAll:function(self){var allChkBox=document.getElementById(self._getItemListIdentifier()).querySelector('input[type=checkbox]');if(self._itemCounter==self._getItems().length){allChkBox.checked=!0} - else if(allChkBox.checked){allChkBox.checked=!1}},_hideList:function(context,event){m_helper.setUnactive(document.getElementById(context._getItemListIdentifier()));m_helper.show(document.getElementById(context._getItemListIdentifier()).querySelector('span'));m_helper.show(document.getElementById(context._getItemListIdentifier()).querySelector('hr'));m_helper.showAll(document.getElementById(context._getItemListIdentifier()).querySelectorAll('li'));context._updateText(context);if(event) - event.stopPropagation()},_updateText:function(context){var activeItems=document.getElementById(context._getItemListIdentifier()).querySelectorAll('ul .active');if(activeItems.length>0){var val='';for(var i=0;i<(activeItems.length<5?activeItems.length:5);i++){val+=activeItems[i].innerText+", "} - val=val.substr(0,val.length-2);if(val.length>20){val=val.substr(0,17)+'...'}} - if(activeItems.length==document.getElementById(context._getItemListIdentifier()).querySelectorAll('ul li').length){val='All selected'} - document.getElementById(context._getInputFieldIdentifier()).value=val?val:''},_showList:function(context){m_helper.setActive(document.getElementById(context._getItemListIdentifier()))},_forceUpdate:function(){var badge=document.getElementById(this._getInputBadgeIdentifier());badge.style.visibility='hidden';if(this._itemCounter!=0){badge.innerHTML=this._itemCounter;badge.style.visibility='visible';var ddArrow=badge.nextElementSibling;if(this._itemCounter<10){badge.style.left='-45px';ddArrow.style.marginLeft='-42px'} - else if(this._itemCounter<100){badge.style.left='-50px';ddArrow.style.marginLeft='-47px'} - else if(this._itemCounter<1000){badge.style.left='-55px';ddArrow.style.marginLeft='-52px'} - else if(this._itemCounter<10000){badge.style.left='-60px';ddArrow.style.marginLeft='-57px'}}},_items:undefined,_itemCounter:0,_isEnabled:!0,_getItems:function(){if(this._items==undefined){var result=[];var opts=this._item.options;for(var i=0;i - - - - + + Search -

Test Mod Search

-
- - + + +
+
+ +
+ + +
-
{{#each results}}
@@ -37,14 +43,49 @@

{{this.title}}

{{this.description}}

+
+ {{#contains this.keywords "Technology"}} +
+ tech +

TECH

+
+ {{/contains}} + {{#contains this.keywords "Adventure"}} +
+ adventure +

ADVENTURE

+
+ {{/contains}} + {{#contains this.keywords "Magic"}} +
+ magic +

MAGIC

+
+ {{/contains}} + {{#contains this.keywords "Utility"}} +
+ util +

UTILITY

+
+ {{/contains}} + {{#contains this.keywords "Forge"}} +
+ forge +

FORGE

+
+ {{/contains}} + {{#contains this.keywords "Fabric"}} +
+ fabric +

FABRIC

+
+ {{/contains}} +
{{/each}} -