diff --git a/css/items.css b/css/items.css index 80b3c6c..6535941 100644 --- a/css/items.css +++ b/css/items.css @@ -1,32 +1,101 @@ -.searchbox { - grid-column: 1; - padding: 0%; - display: grid; - grid-template-columns: repeat(4, 1fr); - width: 100%; - gap: 5px; +/* type selectors */ + +#type-box { + cursor: pointer; + overflow: auto; +} +#type-box > div { + display: inline-block; + width: calc(100% / 12); + float: left; } -.items { - grid-column: 1; - padding: 0%; - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - width: 100%; - gap: 5px; - grid-template-rows: masonry; +.type-selected { + background-color: #0a0; } -.itemsearch { - padding: 0%; - display: grid; - grid-template-columns: 1fr; - width: 100%; - gap: 5px; +/* rarity selectors */ + +#rarity-box { + cursor: pointer; + width: 58.33%; + text-align: center; +} +#rarity-box > div { + width: calc(100% / 7); + aspect-ratio: 1/1; + position: relative; + display: inline-block; +} +#rarity-box > div > b { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} +#rarity-box > div.rarity-selected > b { + color: black; } -.searchinput { - width: 100%; - max-width: 200px; +#rarity-normal { color: #FFFFFF; } +#rarity-normal.rarity-selected { background-color: #FFFFFF; } +#rarity-unique { color: #FFFF55; } +#rarity-unique.rarity-selected { background-color: #FFFF55; } +#rarity-set { color: #55FF55; } +#rarity-set.rarity-selected { background-color: #55FF55; } +#rarity-rare { color: #FF55FF; } +#rarity-rare.rarity-selected { background-color: #FF55FF; } +#rarity-legendary { color: #55FFFF; } +#rarity-legendary.rarity-selected { background-color: #55FFFF; } +#rarity-fabled { color: #FF5555; } +#rarity-fabled.rarity-selected { background-color: #FF5555; } +#rarity-mythic { color: #AA00AA; } +#rarity-mythic.rarity-selected { background-color: #AA00AA; } + +/* filters */ +.filter-row { + border: 1px solid #121212; +} +.filter-dragged-over { + border: 1px solid white; } +.reorder-filter { + height: 2ch; + cursor: pointer; +} + +input.filter-input { + width: 27ch!important; + max-width: 90%; + display: inline-block; +} + +.asc-icon { + opacity: 0.25; + width: 1.75ch; +} +.desc-icon { + opacity: 0.25; + width: 1.75ch; + transform: rotate(180deg); +} +img.asc-sel { + opacity: 0.85; +} + +input.min-max-input { + width: 6ch!important; + display: inline-block; +} + +#filter-container > div > div > * { + margin: 0 2px; +} + +/* item display */ +@media (min-width: 576px) and (max-width: 991px) { + div.col-sm-6 { + width: 100%; + } +} \ No newline at end of file diff --git a/items/index.html b/items/index.html index 9cd7d5b..6503497 100644 --- a/items/index.html +++ b/items/index.html @@ -18,6 +18,7 @@ + -
-
-
+
+
+ -
+
+
-
+
Name:
- -

-
-
-
Category:
- - - -

-
-
-
Rarity:
- - - -

-
-
-
Level Range:
- +

-
-
Filter 1:
- +
+
Type: All   None
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
Filter 2:
- -

-
-
-
Filter 3:
- -

-
-
-
Filter 4:
- -

-
-
-
- +
+
Rarity: All   None
+
+ +
N
U
S
R
L
F
M
+
+

-
- +
+
+
+
Filters:
+
+
+ + Add Filter
+
+
+
+ +
+
+ +
+
@@ -177,6 +151,7 @@
+ diff --git a/js/drag_drop_touch.js b/js/drag_drop_touch.js new file mode 100644 index 0000000..4cf6cb7 --- /dev/null +++ b/js/drag_drop_touch.js @@ -0,0 +1,8 @@ +/* + DragDropTouch by Bernardo Castilho + https://github.com/Bernardo-Castilho/dragdroptouch + MIT License + + Note: it only works on a real phone for some reason, it doesn't work on desktop browser simulations of phones for some reason. +*/ +var DragDropTouch;!function(t){"use strict";var e=function(){function t(){this._dropEffect="move",this._effectAllowed="all",this._data={}}return Object.defineProperty(t.prototype,"dropEffect",{get:function(){return this._dropEffect},set:function(t){this._dropEffect=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"effectAllowed",{get:function(){return this._effectAllowed},set:function(t){this._effectAllowed=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"types",{get:function(){return Object.keys(this._data)},enumerable:!0,configurable:!0}),t.prototype.clearData=function(t){null!=t?delete this._data[t.toLowerCase()]:this._data={}},t.prototype.getData=function(t){return this._data[t.toLowerCase()]||""},t.prototype.setData=function(t,e){this._data[t.toLowerCase()]=e},t.prototype.setDragImage=function(t,e,s){var o=i._instance;o._imgCustom=t,o._imgOffset={x:e,y:s}},t}();t.DataTransfer=e;var i=function(){function t(){if(this._lastClick=0,t._instance)throw"DragDropTouch instance already created.";var e=!1;if(document.addEventListener("test",function(){},{get passive(){return e=!0,!0}}),navigator.maxTouchPoints){var i=document,s=this._touchstart.bind(this),o=this._touchmove.bind(this),n=this._touchend.bind(this),r=!!e&&{passive:!1,capture:!1};i.addEventListener("touchstart",s,r),i.addEventListener("touchmove",o,r),i.addEventListener("touchend",n),i.addEventListener("touchcancel",n)}}return t.getInstance=function(){return t._instance},t.prototype._touchstart=function(e){var i=this;if(this._shouldHandle(e)){if(Date.now()-this._lastClickt._PRESSHOLDMARGIN},t.prototype._shouldStartDragging=function(e){var i=this._getDelta(e);return i>t._THRESHOLD||t._ISPRESSHOLDMODE&&i>=t._PRESSHOLDTHRESHOLD},t.prototype._reset=function(){this._destroyImage(),this._dragSource=null,this._lastTouch=null,this._lastTarget=null,this._ptDown=null,this._isDragEnabled=!1,this._isDropZone=!1,this._dataTransfer=new e,clearInterval(this._pressHoldInterval)},t.prototype._getPoint=function(t,e){return t&&t.touches&&(t=t.touches[0]),{x:e?t.pageX:t.clientX,y:e?t.pageY:t.clientY}},t.prototype._getDelta=function(e){if(t._ISPRESSHOLDMODE&&!this._ptDown)return 0;var i=this._getPoint(e);return Math.abs(i.x-this._ptDown.x)+Math.abs(i.y-this._ptDown.y)},t.prototype._getTarget=function(t){for(var e=this._getPoint(t),i=document.elementFromPoint(e.x,e.y);i&&"none"==getComputedStyle(i).pointerEvents;)i=i.parentElement;return i},t.prototype._createImage=function(e){this._img&&this._destroyImage();var i=this._imgCustom||this._dragSource;if(this._img=i.cloneNode(!0),this._copyStyle(i,this._img),this._img.style.top=this._img.style.left="-9999px",!this._imgCustom){var s=i.getBoundingClientRect(),o=this._getPoint(e);this._imgOffset={x:o.x-s.left,y:o.y-s.top},this._img.style.opacity=t._OPACITY.toString()}this._moveImage(e),document.body.appendChild(this._img)},t.prototype._destroyImage=function(){this._img&&this._img.parentElement&&this._img.parentElement.removeChild(this._img),this._img=null,this._imgCustom=null},t.prototype._moveImage=function(t){var e=this;requestAnimationFrame(function(){if(e._img){var i=e._getPoint(t,!0),s=e._img.style;s.position="absolute",s.pointerEvents="none",s.zIndex="999999",s.left=Math.round(i.x-e._imgOffset.x)+"px",s.top=Math.round(i.y-e._imgOffset.y)+"px"}})},t.prototype._copyProps=function(t,e,i){for(var s=0;s 200) {break;} let item = items_copy[i].itemExp; let box = make_elem('div', ['col-lg-3', 'col-sm-6', 'p-2'], {id: 'item'+i}); - //box.addEventListener("dblclick", function() {set_item(item);}); TODO: ?? let bckgrdbox = make_elem("div", ["dark-7", "rounded", "px-2", "col-auto"], {id: 'item'+i+'b'}); box.append(bckgrdbox); @@ -141,47 +137,69 @@ let expr_parser; function do_item_search() { window.scrollTo(0, 0); let queries = []; - queries.push('f:name?="'+document.getElementById("item-name-choice").value.trim()+'"'); - const cat_or_type = document.getElementById("item-category-choice").value; - if (item_types.includes(cat_or_type)) { - queries.push('f:type="'+cat_or_type+'"'); - } - else if (item_categories.includes(cat_or_type)) { - queries.push('f:cat="'+cat_or_type+'"'); + // name + if (document.getElementById("item-name-choice").value != "") { + queries.push("f:name?=\"" + document.getElementById("item-name-choice").value.trim() + "\""); } - let rarity = document.getElementById("item-rarity-choice").value; - if (rarity) { - if (rarity === "ANY") { - - } - else if (rarity === "Sane") { - queries.push('f:tiername!="mythic"'); - } - else { - queries.push('f:tiername="'+rarity+'"'); + // types + let allTypes = true; + let typeQuery = "f:(" + for (const type of Object.keys(types)) { + if (types[type]) { + typeQuery += "type=\"" + type + "\"|"; + } else { + allTypes = false; } } + if (!allTypes) { + queries.push(typeQuery.substring(0, typeQuery.length - 1) + ")"); + } - let level_raw = document.getElementById("item-level-choice").value; - if (!level_raw) { level_raw = '1-106'; }; - const level_dat = level_raw.split("-"); - queries.push('f:(lvl>='+parseInt(level_dat[0])+'&lvl<='+parseInt(level_dat[1])+')'); + // rarities + let allRarities = true; + let rarityQuery = "f:(" + for (const rarity of Object.keys(rarities)) { + if (rarities[rarity]) { + rarityQuery += "tiername=\"" + rarity + "\"|"; + } else { + allRarities = false; + } + } + if (!allRarities) { + queries.push(rarityQuery.substring(0, rarityQuery.length - 1) + ")"); + } - for (let i = 1; i <= 4; ++i) { - let raw_dat = document.getElementById("filter"+i+"-choice").value; - let filter_dat = translate_mappings[raw_dat]; - if (filter_dat !== undefined) { - queries.push("s:"+filter_dat); - queries.push("f:"+filter_dat+"!=0"); - continue; + // filters + for (const filter of filters) { + let min = parseInt(filter.min_elem.value); + let max = parseInt(filter.max_elem.value); + if (min > max) { + continue; // invalid range } - filter_dat = special_mappings[raw_dat]; - if (filter_dat !== undefined) { - queries.push(filter_dat); - continue; + let zero_in_min_max = (isNaN(min) || min <= 0) && (isNaN(max) || max >= 0); + + let raw_name = filter.input_elem.value; + let filter_name = translate_mappings[raw_name]; + if (filter_name === undefined) { + filter_name = special_mappings[raw_name]; + if (filter_name === undefined) { + continue; // not a valid filter + } + filter_name = "(" + filter_name + ")"; } + + if (!isNaN(min)) { + queries.push("f:" + filter_name + ">=" + min); + } + if (!isNaN(max)) { + queries.push("f:" + filter_name + "<=" + max); + } + if (zero_in_min_max) { + queries.push("f:" + filter_name + "!=0"); + } + queries.push("s:" + (filter.ascending ? "0-" : "") + filter_name); } let filter_query = "true"; @@ -218,67 +236,221 @@ function do_item_search() { displayItems(results); } -function reset_item_search() { - const reset_fields = ["item-name-choice", "item-category-choice", "item-rarity-choice", "item-level-choice", "filter1-choice", "filter2-choice", "filter3-choice", "filter4-choice"] - for (const field of reset_fields) { - document.getElementById(field).value = ""; - } -} - function init_items() { search_db = items.filter( i => ! i.remapID ).map( i => [i, expandItem(i, [])] ); expr_parser = new ExprParser(itemQueryProps, itemQueryFuncs); - //init dropdowns - let filter_inputs = new Map([["item-category", ["ALL", "armor", "helmet", "chestplate", "leggings", "boots", "accessory", "ring", "bracelet", "necklace", "weapon", "wand", "spear", "bow", "dagger", "relik"]], - ["item-rarity", ["ANY", "Normal", "Unique", "Set", "Rare", "Legendary", "Fabled", "Mythic", "Sane"]], - ["filter1", item_filters], - ["filter2", item_filters], - ["filter3", item_filters], - ["filter4", item_filters]]); - for (const [field, data] of filter_inputs) { - let field_choice = document.getElementById(field+"-choice"); - // show dropdown on click - field_choice.onclick = function() {field_choice.dispatchEvent(new Event('input', {bubbles:true}));}; - filter_inputs.set(field, new autoComplete({ - data: { - src: data, - }, - threshold: 0, - selector: "#"+ field +"-choice", - wrapper: false, - resultsList: { - maxResults: 100, - tabSelect: true, - noResults: true, - class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", - element: (list, data) => { - let position = document.getElementById(field+'-choice').getBoundingClientRect(); - list.style.top = position.bottom + window.scrollY +"px"; - list.style.left = position.x+"px"; - list.style.width = position.width+"px"; - list.style.maxHeight = position.height * 4 +"px"; - if (!data.results.length) { - const message = make_elem('li', ['scaled-font'], {textContent: "No results found!"}); - list.prepend(message); + // init type buttons + for (const type of Object.keys(types)) { + document.getElementById("type-" + type).addEventListener("click", function() { + types[type] = !types[type]; + this.classList.toggle("type-selected"); + }); + } + document.getElementById("all-types").addEventListener("click", function() { + for (const type of Object.keys(types)) { + types[type] = true; + document.getElementById("type-" + type).classList.add("type-selected"); + } + }); + document.getElementById("none-types").addEventListener("click", function() { + for (const type of Object.keys(types)) { + types[type] = false; + document.getElementById("type-" + type).classList.remove("type-selected"); + } + }); + + // init rarity buttons + for (const rarity of Object.keys(rarities)) { + document.getElementById("rarity-" + rarity).addEventListener("click", function() { + rarities[rarity] = !rarities[rarity]; + this.classList.toggle("rarity-selected"); + }); + } + document.getElementById("all-rarities").addEventListener("click", function() { + for (const rarity of Object.keys(rarities)) { + rarities[rarity] = true; + document.getElementById("rarity-" + rarity).classList.add("rarity-selected"); + } + }); + document.getElementById("none-rarities").addEventListener("click", function() { + for (const rarity of Object.keys(rarities)) { + rarities[rarity] = false; + document.getElementById("rarity-" + rarity).classList.remove("rarity-selected"); + } + }); + + // filters + document.getElementById("add-filter").addEventListener("click", create_filter); + create_filter(); + filters[0].input_elem.value = "Combat Level"; + init_filter_drag(); +} + +function reset_item_search() { + document.getElementById("item-name-choice").value = ""; + document.getElementById("all-types").click(); + document.getElementById("all-rarities").click(); +} + +function create_filter() { + let data = {ascending: false}; + + let row = make_elem("div", ["row", "filter-row"], {}); + let col = make_elem("div", ["col"], {}); + row.appendChild(col); + data.div = row; + + let reorder_img = make_elem("img", ["reorder-filter"], {src: "../media/icons/3-lines.svg", draggable: "true"}); + col.appendChild(reorder_img); + + let filter_input = make_elem("input", + ["col", "border-dark", "text-light", "dark-5", "rounded", "scaled-font", "form-control", "form-control-sm", "filter-input"], + {id: "filter-input-" + filter_id_counter, type: "text", placeholder: "Filter"} + ); + filter_id_counter++; + col.appendChild(filter_input); + data.input_elem = filter_input; + + let asc_desc = make_elem("div", [], {style: "cursor: pointer; display: inline-block;"}); + asc_desc.appendChild(make_elem("img", ["desc-icon", "asc-sel"], {src: "../media/icons/triangle.svg"})); + asc_desc.appendChild(make_elem("img", ["asc-icon"], {src: "../media/icons/triangle.svg"})); + asc_desc.addEventListener("click", function() { + data.ascending = !data.ascending; + asc_desc.children[0].classList.toggle("asc-sel"); + asc_desc.children[1].classList.toggle("asc-sel"); + }); + col.appendChild(asc_desc); + + let min = make_elem("input", + ["col", "border-dark", "text-light", "dark-5", "rounded", "scaled-font", "form-control", "form-control-sm", "min-max-input"], + {type: "number", placeholder: "-\u221E"} + ); + col.appendChild(min); + data.min_elem = min; + + let to = make_elem("span", [], {innerHTML: " to "}); + col.appendChild(to); + + let max = make_elem("input", + ["col", "border-dark", "text-light", "dark-5", "rounded", "scaled-font", "form-control", "form-control-sm", "min-max-input"], + {type: "number", placeholder: "\u221E"} + ); + col.appendChild(max); + data.max_elem = max; + + document.getElementById("filter-container").insertBefore(row, document.getElementById("add-filter").parentElement); + + filters.push(data); + init_filter_dropdown(data); +} + +let currently_dragging = null; +function init_filter_drag() { + let container = document.getElementById("filter-container"); + + container.addEventListener("dragstart", function(e) { + if (e.path[0].classList.contains("reorder-filter")) { + currently_dragging = filters[Array.from(e.path[3].children).indexOf(e.path[2]) - 1]; + } else { + e.preventDefault(); + } + }); + + container.addEventListener("dragenter", function(e) { + e.preventDefault(); + }); + + container.addEventListener("dragleave", function(e) { + e.preventDefault(); + }); + + container.addEventListener("dragend", function(e) { + e.preventDefault(); + for (const el of document.getElementsByClassName("filter-dragged-over")) { + el.classList.remove("filter-dragged-over"); + } + currently_dragging = null; + }); + + container.addEventListener("dragover", function(e) { + e.preventDefault(); + for (const el of document.getElementsByClassName("filter-dragged-over")) { + el.classList.remove("filter-dragged-over"); + } + if (!e.path.includes(currently_dragging.div)) { + for (let i = 0; i < e.path.length; i++) { + if (e.path[i].classList.contains("filter-row")) { + e.path[i].classList.add("filter-dragged-over"); + break; + } + } + } + }); + + container.addEventListener("drop", function(e) { + e.preventDefault(); + for (const el of document.getElementsByClassName("filter-dragged-over")) { + el.classList.remove("filter-dragged-over"); + } + if (!e.path.includes(currently_dragging.div)) { + for (let i = 0; i < e.path.length; i++) { + if (e.path[i].classList.contains("filter-row")) { + let old_index = filters.indexOf(currently_dragging); + let new_index = Array.from(e.path[i + 1].children).indexOf(e.path[i]) - 1; + filters.splice(old_index, 1); + filters.splice(new_index, 0, currently_dragging); + currently_dragging.div.remove(); + container.insertBefore(currently_dragging.div, container.children[new_index + 1]); + break; + } + } + } + currently_dragging = null; + }); +} + +function init_filter_dropdown(filter) { + let field_choice = filter.input_elem; + field_choice.onclick = function() {field_choice.dispatchEvent(new Event('input', {bubbles:true}));}; + filter.autoComplete = new autoComplete({ + data: { + src: item_filters, + }, + threshold: 0, + selector: "#" + field_choice.id, + wrapper: false, + resultsList: { + maxResults: 100, + tabSelect: true, + noResults: true, + class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", + element: (list, data) => { + let position = field_choice.getBoundingClientRect(); + list.style.top = position.bottom + window.scrollY +"px"; + list.style.left = position.x+"px"; + list.style.width = position.width+"px"; + list.style.maxHeight = position.height * 4 +"px"; + if (!data.results.length) { + const message = make_elem('li', ['scaled-font'], {textContent: "No results found!"}); + list.prepend(message); + }; + }, + }, + resultItem: { + class: "scaled-font search-item", + selected: "dark-5", + }, + events: { + input: { + selection: (event) => { + if (event.detail.selection.value) { + event.target.value = event.detail.selection.value; }; }, }, - resultItem: { - class: "scaled-font search-item", - selected: "dark-5", - }, - events: { - input: { - selection: (event) => { - if (event.detail.selection.value) { - event.target.value = event.detail.selection.value; - }; - }, - }, - } - })); - } + } + }); } (async function() { diff --git a/media/icons/3-lines.svg b/media/icons/3-lines.svg new file mode 100644 index 0000000..b1c0a2d --- /dev/null +++ b/media/icons/3-lines.svg @@ -0,0 +1,8 @@ + + + diff --git a/media/icons/triangle.svg b/media/icons/triangle.svg new file mode 100644 index 0000000..0c07a50 --- /dev/null +++ b/media/icons/triangle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file