feat: add basic search and filtering
This commit is contained in:
parent
7225f40e68
commit
c0e12531a2
|
@ -35,51 +35,54 @@ body {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing {
|
#listings > .listing {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-flow: row nowrap;
|
grid-template-columns: 3fr auto 2fr;
|
||||||
justify-content: space-between;
|
gap: 1em;
|
||||||
|
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
margin-bottom: 1em;
|
|
||||||
|
|
||||||
background-color: var(--row-background);
|
background-color: var(--row-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .left {
|
#listings > .listing:nth-child(2n) {
|
||||||
flex: 2 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body > .listing .middle {
|
|
||||||
flex: 1 0 0 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
body > .listing:nth-child(2n) {
|
|
||||||
background-color: var(--row-background-alternate);
|
background-color: var(--row-background-alternate);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .description {
|
#listings > .listing .description {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .stat {
|
#listings > .listing .description .desc-green {
|
||||||
|
color: var(--green-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#listings > .listing .description .desc-blue {
|
||||||
|
color: var(--light-blue-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#listings > .listing .description .desc-yellow {
|
||||||
|
color: var(--gold-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#listings > .listing .stat {
|
||||||
color: var(--meta-text);
|
color: var(--meta-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .duty {
|
#listings > .listing .duty {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .duty.cross {
|
#listings > .listing .duty.cross {
|
||||||
color: var(--cross-duty-text);
|
color: var(--cross-duty-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .duty.local {
|
#listings > .listing .duty.local {
|
||||||
color: var(--local-duty-text);
|
color: var(--local-duty-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .meta {
|
#listings > .listing .meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
@ -89,13 +92,13 @@ body > .listing .meta {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .meta > .item {
|
#listings > .listing .meta > .item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row nowrap;
|
flex-flow: row nowrap;
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .meta > .item .icon {
|
#listings > .listing .meta > .item .icon {
|
||||||
height: 1em;
|
height: 1em;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
fill: var(--text);
|
fill: var(--text);
|
||||||
|
@ -104,7 +107,7 @@ body > .listing .meta > .item .icon {
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party {
|
#listings > .listing .party {
|
||||||
margin-top: .5em;
|
margin-top: .5em;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@ -112,19 +115,19 @@ body > .listing .party {
|
||||||
gap: .5em;
|
gap: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .total {
|
#listings > .listing .party > .total {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .slot {
|
#listings > .listing .party > .slot {
|
||||||
width: 2em;
|
width: 2em;
|
||||||
height: 2em;
|
height: 2em;
|
||||||
border: 1px solid currentColor;
|
border: 1px solid currentColor;
|
||||||
margin-right: .5em;
|
margin-right: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .slot:not(.filled).dps.tank {
|
#listings > .listing .party > .slot:not(.filled).dps.tank {
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
to bottom,
|
to bottom,
|
||||||
var(--tank-blue) 0%,
|
var(--tank-blue) 0%,
|
||||||
|
@ -133,7 +136,7 @@ body > .listing .party > .slot:not(.filled).dps.tank {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .slot:not(.filled).dps.healer {
|
#listings > .listing .party > .slot:not(.filled).dps.healer {
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
to bottom,
|
to bottom,
|
||||||
var(--healer-green) 0%,
|
var(--healer-green) 0%,
|
||||||
|
@ -142,7 +145,7 @@ body > .listing .party > .slot:not(.filled).dps.healer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .slot:not(.filled).tank.healer {
|
#listings > .listing .party > .slot:not(.filled).tank.healer {
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
to bottom,
|
to bottom,
|
||||||
var(--tank-blue) 0%,
|
var(--tank-blue) 0%,
|
||||||
|
@ -151,7 +154,7 @@ body > .listing .party > .slot:not(.filled).tank.healer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .slot:not(.filled).tank.healer.dps {
|
#listings > .listing .party > .slot:not(.filled).tank.healer.dps {
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
to bottom,
|
to bottom,
|
||||||
var(--tank-blue) 0%,
|
var(--tank-blue) 0%,
|
||||||
|
@ -162,70 +165,65 @@ body > .listing .party > .slot:not(.filled).tank.healer.dps {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .slot:not(.filled).dps {
|
#listings > .listing .party > .slot:not(.filled).dps {
|
||||||
background-color: var(--dps-red);
|
background-color: var(--dps-red);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .slot.filled {
|
#listings > .listing .party > .slot.filled {
|
||||||
background-color: var(--slot-background);
|
background-color: var(--slot-background);
|
||||||
border-color: var(--icon-gold);
|
border-color: var(--icon-gold);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .slot.empty {
|
#listings > .listing .party > .slot.empty {
|
||||||
background-color: var(--slot-empty);
|
background-color: var(--slot-empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .slot.dps {
|
#listings > .listing .party > .slot.dps {
|
||||||
background-color: var(--dps-red);
|
background-color: var(--dps-red);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .slot.healer {
|
#listings > .listing .party > .slot.healer {
|
||||||
background-color: var(--healer-green);
|
background-color: var(--healer-green);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .slot.tank {
|
#listings > .listing .party > .slot.tank {
|
||||||
background-color: var(--tank-blue);
|
background-color: var(--tank-blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .slot > svg {
|
#listings > .listing .party > .slot > svg {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
fill: var(--icon-gold);
|
fill: var(--icon-gold);
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .party > .slot.filled:not(.dps):not(.healer):not(.tank) > svg {
|
#listings > .listing .party > .slot.filled:not(.dps):not(.healer):not(.tank) > svg {
|
||||||
fill: #C6C6C6;
|
fill: #C6C6C6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Really, this could be 26em */
|
@media (max-width: 50em) {
|
||||||
@media (max-width: 30em) {
|
#listings > .listing {
|
||||||
body > .listing {
|
grid-template-columns: repeat(auto-fit, 100%);
|
||||||
flex-flow: column nowrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing > :not(:first-child) {
|
#listings > .listing > :not(:first-child) {
|
||||||
margin-top: .5em;
|
margin: 0;
|
||||||
margin-left: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .meta {
|
#listings > .listing .meta {
|
||||||
flex-grow: 1;
|
|
||||||
|
|
||||||
margin-left: 0;
|
|
||||||
text-align: unset;
|
text-align: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .meta > .item {
|
#listings > .listing .meta > .item {
|
||||||
align-self: unset;
|
align-self: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .meta > .item .icon {
|
#listings > .listing .meta > .item .icon {
|
||||||
order: 1;
|
order: 1;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: .5em;
|
margin-right: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > .listing .meta > .item > .text {
|
#listings > .listing .meta > .item .text {
|
||||||
order: 2;
|
order: 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
41
assets/listings.js
Normal file
41
assets/listings.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
(function () {
|
||||||
|
let select = document.getElementById('data-centre-filter');
|
||||||
|
|
||||||
|
let data_centres = [];
|
||||||
|
for (let elem of document.querySelectorAll('#listings > .listing')) {
|
||||||
|
let centre = elem.dataset['centre'];
|
||||||
|
if (data_centres.indexOf(centre) === -1) {
|
||||||
|
data_centres.push(centre);
|
||||||
|
let opt = document.createElement('option');
|
||||||
|
opt.innerText = centre;
|
||||||
|
select.appendChild(opt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
valueNames: [
|
||||||
|
'duty',
|
||||||
|
'creator',
|
||||||
|
'description',
|
||||||
|
{data: ['centre']},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let list = new List('container', options);
|
||||||
|
|
||||||
|
select.addEventListener('change', () => {
|
||||||
|
let centre = select.value;
|
||||||
|
if (centre === 'All') {
|
||||||
|
list.filter();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`looking for ${centre}`);
|
||||||
|
|
||||||
|
list.filter(item => {
|
||||||
|
console.log(item.values().centre === centre)
|
||||||
|
return item.values().centre === centre;
|
||||||
|
// console.log(item.elm.dataset['centre']);
|
||||||
|
// return item.elm.dataset['centre'] === centre;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
|
@ -109,6 +109,45 @@ impl PartyFinderListing {
|
||||||
.map(|world| Cow::from(world.name()))
|
.map(|world| Cow::from(world.name()))
|
||||||
.unwrap_or_else(|| Cow::from(self.home_world.to_string()))
|
.unwrap_or_else(|| Cow::from(self.home_world.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prepend_flags(&self) -> (&'static str, String) {
|
||||||
|
let mut colour_class = "";
|
||||||
|
let mut flags = Vec::new();
|
||||||
|
|
||||||
|
if self.objective.contains(ObjectiveFlags::PRACTICE) {
|
||||||
|
flags.push("[Practice]");
|
||||||
|
colour_class = "desc-green";
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.objective.contains(ObjectiveFlags::DUTY_COMPLETION) {
|
||||||
|
flags.push("[Duty Completion]");
|
||||||
|
colour_class = "desc-blue";
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.objective.contains(ObjectiveFlags::LOOT) {
|
||||||
|
flags.push("[Loot]");
|
||||||
|
colour_class = "desc-yellow";
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.conditions.contains(ConditionFlags::DUTY_COMPLETE) {
|
||||||
|
flags.push("[Duty Complete]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.conditions.contains(ConditionFlags::DUTY_INCOMPLETE) {
|
||||||
|
flags.push("[Duty Incomplete]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.search_area.contains(SearchAreaFlags::ONE_PLAYER_PER_JOB) {
|
||||||
|
flags.push("[One Player per Job]");
|
||||||
|
}
|
||||||
|
|
||||||
|
(colour_class, flags.join(""))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data_centre_name(&self) -> Option<&'static str> {
|
||||||
|
crate::ffxiv::WORLDS.get(&u32::from(self.created_world))
|
||||||
|
.map(|w| w.data_center().name())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq)]
|
||||||
|
|
|
@ -79,6 +79,7 @@ fn assets() -> BoxedFilter<(impl Reply, )> {
|
||||||
icons()
|
icons()
|
||||||
.or(minireset())
|
.or(minireset())
|
||||||
.or(listings_css())
|
.or(listings_css())
|
||||||
|
.or(listings_js())
|
||||||
)
|
)
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
@ -104,6 +105,13 @@ fn listings_css() -> BoxedFilter<(impl Reply, )> {
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn listings_js() -> BoxedFilter<(impl Reply, )> {
|
||||||
|
warp::path("listings.js")
|
||||||
|
.and(warp::path::end())
|
||||||
|
.and(warp::fs::file("./assets/listings.js"))
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
fn index() -> BoxedFilter<(impl Reply, )> {
|
fn index() -> BoxedFilter<(impl Reply, )> {
|
||||||
let route = warp::path::end()
|
let route = warp::path::end()
|
||||||
.map(|| warp::redirect(Uri::from_static("/listings")));
|
.map(|| warp::redirect(Uri::from_static("/listings")));
|
||||||
|
|
|
@ -6,12 +6,23 @@ Remote Party Finder
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<link rel="stylesheet" href="/assets/listings.css"/>
|
<link rel="stylesheet" href="/assets/listings.css"/>
|
||||||
|
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/list.js/2.3.1/list.min.js"></script>
|
||||||
|
<script defer src="/assets/listings.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% for container in containers %}
|
<div id="container">
|
||||||
{% let listing = container.listing.borrow() %}
|
<input type="search" class="search" placeholder="Search"/>
|
||||||
<div class="listing" data-id="{{ listing.id }}" data-updated-minutes="{{ container.since_updated().num_minutes() }}">
|
<select id="data-centre-filter">
|
||||||
|
<option>All</option>
|
||||||
|
</select>
|
||||||
|
<div id="listings" class="list">
|
||||||
|
{% for container in containers %}
|
||||||
|
{% let listing = container.listing.borrow() %}
|
||||||
|
<div
|
||||||
|
class="listing"
|
||||||
|
data-id="{{ listing.id }}"
|
||||||
|
data-centre="{{ listing.data_centre_name().unwrap_or_default() }}">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
{% let duty_class %}
|
{% let duty_class %}
|
||||||
{% if listing.is_cross_world() %}
|
{% if listing.is_cross_world() %}
|
||||||
|
@ -22,10 +33,12 @@ Remote Party Finder
|
||||||
<div class="duty{{ duty_class }}">{{ listing.duty_name() }}</div>
|
<div class="duty{{ duty_class }}">{{ listing.duty_name() }}</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
{%- let desc = listing.description.full_text() %}
|
{%- let desc = listing.description.full_text() %}
|
||||||
{%- if desc.is_empty() -%}
|
{%- if desc.trim().is_empty() -%}
|
||||||
<em>None</em>
|
<em>None</em>
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
{{ desc }}
|
{%- let (colour_class, prepend_flags) = listing.prepend_flags() -%}
|
||||||
|
<span class="{{ colour_class }}">{{ prepend_flags }} </span>
|
||||||
|
{{- desc.trim() }}
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
</div>
|
</div>
|
||||||
<div class="party">
|
<div class="party">
|
||||||
|
@ -99,6 +112,8 @@ Remote Party Finder
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user