Added tag filter dialog.

This commit is contained in:
DebaucheryLibrarian
2021-01-03 22:53:51 +01:00
parent f27af19670
commit 7bbb2f3557
18 changed files with 329 additions and 16 deletions

View File

@@ -0,0 +1,106 @@
<template>
<teleport to="body">
<div
class="container"
@click="$emit('close')"
>
<div
class="dialog"
@click.stop
>
<div
v-if="title || $slots.header"
class="header"
>
<slot name="header">
<h2 class="header-title">{{ title }}</h2>
</slot>
<Icon
icon="cross2"
class="close"
@click="$emit('close')"
/>
</div>
<div class="body">
<slot />
</div>
</div>
</div>
</teleport>
</template>
<script>
export default {
props: {
title: {
type: String,
default: null,
},
},
emits: ['close'],
};
</script>
<style lang="scss" scoped>
.container {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 1rem;
background: var(--darken);
z-index: 10;
}
.dialog {
display: flex;
flex-direction: column;
max-width: 100%;
max-height: 100%;
background: var(--background);
box-shadow: 0 0 3px var(--darken-weak);
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
background: var(--primary);
color: var(--text-light);
font-size: 1.5rem;
font-weight: bold;
text-transform: capitalize;
}
.header-title {
padding: .5rem .5rem .5rem 1rem;
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
font-size: 1.25rem;
}
.close {
height: 100%;
padding: 0 1rem;
fill: var(--lighten);
&:hover {
fill: var(--lighten-strong);
cursor: pointer;
}
}
.body {
padding: 1rem;
flex-grow: 1;
overflow: auto;
}
</style>

View File

@@ -0,0 +1,99 @@
<template>
<label class="check-container noselect">
<span class="check-label">{{ label }}</span>
<input
v-show="false"
:id="`checkbox-${uid}`"
:checked="checked"
type="checkbox"
class="check-checkbox"
@change="$emit('change', $event.target.checked)"
>
<label
:for="`checkbox-${uid}`"
class="check"
/>
</label>
</template>
<script>
export default {
props: {
checked: {
type: Boolean,
default: false,
},
label: {
type: String,
default: null,
},
},
emits: ['change'],
};
</script>
<style lang="scss" scoped>
@import 'breakpoints';
.check-container {
display: flex;
justify-content: space-between;
cursor: pointer;
}
.check {
width: 1.25rem;
height: 1.25rem;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
position: relative;
background-color: var(--shadow-hint);
cursor: pointer;
transition: background .15s ease;
&::after {
content: '';
width: .5rem;
height: .3rem;
border: solid 2px var(--text-light);
border-top: none;
border-right: none;
margin: -.2rem 0 0 0;
transform: rotateZ(-45deg) scaleX(0);
transition: transform .15s ease;
}
}
.check-cross .check::before {
content: '';
width: 100%;
height: 100%;
position: absolute;
background: url('/img/icons/cross3.svg') no-repeat center/80%;
opacity: .15;
transition: transform .1s ease;
}
.check-checkbox:checked + .check {
background: var(--primary);
&::after {
transform: rotateZ(-45deg) scaleX(1);
}
&::before {
transform: scaleX(0);
}
}
.check-label {
overflow: hidden;
text-transform: capitalize;
text-overflow: ellipsis;
margin: 0 .5rem 0 0;
}
</style>

View File

@@ -0,0 +1,69 @@
<template>
<Dialog
title="filters"
@close="$emit('close')"
>
<h3 class="form-heading">Show me</h3>
<ul class="tags nolist">
<li
v-for="tag in tags"
:key="tag"
class="tags-item"
>
<Checkbox
:checked="!tagFilter.includes(tag)"
:label="tag"
class="tag"
@change="(state) => filterTag(tag, state)"
/>
</li>
</ul>
</Dialog>
</template>
<script>
import Checkbox from '../form/checkbox.vue';
function tagFilter() {
return this.$store.state.ui.tagFilter;
}
function filterTag(tag, isChecked) {
if (isChecked) {
this.$store.dispatch('setTagFilter', this.tagFilter.filter(filteredTag => filteredTag !== tag));
} else {
this.$store.dispatch('setTagFilter', this.tagFilter.concat(tag));
}
}
export default {
components: {
Checkbox,
},
data() {
return {
tags: ['anal', 'gay', 'transsexual', 'bisexual', 'pissing'],
};
},
computed: {
tagFilter,
},
emits: ['close'],
methods: {
filterTag,
},
};
</script>
<style lang="scss" scoped>
.tags-item {
width: 20rem;
max-width: 100%;
display: block;
}
.tag {
padding: .5rem 0;
}
</style>

View File

@@ -147,8 +147,8 @@
</li>
<li
class="menu-item disabled"
@click.stop
class="menu-item"
@click="showFilters = true"
>
<Icon icon="filter" />Filters
</li>
@@ -157,6 +157,11 @@
</template>
</Tooltip>
<Filters
v-if="showFilters"
@close="showFilters = false"
/>
<Search class="search-full" />
<Tooltip
@@ -189,6 +194,7 @@
import { mapState } from 'vuex';
import Search from './search.vue';
import Filters from './filters.vue';
import logo from '../../img/logo.svg';
@@ -211,6 +217,7 @@ function setSfw(enabled) {
export default {
components: {
Search,
Filters,
},
props: {
toggleSidebar: {
@@ -222,6 +229,7 @@ export default {
return {
logo,
searching: false,
showFilters: false,
};
},
computed: {

5
assets/css/_forms.scss Normal file
View File

@@ -0,0 +1,5 @@
.form-heading {
margin: 0 0 .5rem 0;
font-size: 1rem;
font-weight: bold;
}

View File

@@ -1,4 +1,5 @@
@import 'theme';
@import 'forms';
@import 'inputs';
@import 'states';
@import 'tooltip';

View File

@@ -159,7 +159,7 @@ function initEntitiesActions(store, router) {
orderBy,
afterTime: store.getters.after,
beforeTime: store.getters.before,
exclude: store.state.ui.filter,
exclude: store.state.ui.tagFilter,
});
if (!entity) {

View File

@@ -15,8 +15,11 @@ import Container from '../components/container/container.vue';
import Icon from '../components/icon/icon.vue';
import Footer from '../components/footer/footer.vue';
import Tooltip from '../components/tooltip/tooltip.vue';
import Dialog from '../components/dialog/dialog.vue';
async function init() {
let uid = 0;
const store = initStore(router);
const app = createApp(Container);
const events = mitt();
@@ -38,6 +41,7 @@ async function init() {
Footer,
Tooltip,
'v-popover': Tooltip,
Dialog,
},
data() {
return {
@@ -60,6 +64,10 @@ async function init() {
isAfter: (dateA, dateB) => dayjs(dateA).isAfter(dateB),
isBefore: (dateA, dateB) => dayjs(dateA).isBefore(dateB),
},
beforeCreate() {
this.uid = uid;
uid += 1;
},
});
app.directive('tooltip', {

View File

@@ -24,7 +24,7 @@ function initReleasesActions(store, router) {
after,
before,
orderBy,
exclude: store.state.ui.filter,
exclude: store.state.ui.tagFilter,
});
return {

View File

@@ -99,7 +99,7 @@ function initTagsActions(store, _router) {
before,
orderBy: orderBy === 'DATE_DESC' ? 'DATE_DESC' : 'DATE_ASC',
offset: Math.max(0, (pageNumber - 1)) * limit,
exclude: store.state.ui.filter,
exclude: store.state.ui.tagFilter.filter(tagFilter => tagFilter !== tagSlug),
});
return {

View File

@@ -2,9 +2,11 @@ import { graphql } from '../api';
import { curateRelease, curateActor } from '../curate';
function initUiActions(_store, _router) {
function setFilter({ commit }, filter) {
commit('setFilter', filter);
localStorage.setItem('filter', filter);
function setTagFilter({ commit }, filter) {
const tagFilter = Array.from(new Set(filter));
commit('setTagFilter', tagFilter);
localStorage.setItem('tagFilter', tagFilter);
}
function setRange({ commit }, range) {
@@ -184,7 +186,7 @@ function initUiActions(_store, _router) {
return {
search,
setFilter,
setTagFilter,
setRange,
setBatch,
setSfw,

View File

@@ -1,5 +1,5 @@
function setFilter(state, filter) {
state.filter = filter;
function setTagFilter(state, tagFilter) {
state.tagFilter = tagFilter;
}
function setRange(state, range) {
@@ -19,7 +19,7 @@ function setTheme(state, theme) {
}
export default {
setFilter,
setTagFilter,
setRange,
setBatch,
setSfw,

View File

@@ -1,10 +1,10 @@
const storedFilter = localStorage.getItem('filter');
const storedTagFilter = localStorage.getItem('tagFilter');
const storedBatch = localStorage.getItem('batch');
const storedSfw = localStorage.getItem('sfw');
const storedTheme = localStorage.getItem('theme');
export default {
filter: storedFilter ? storedFilter.split(',') : [],
tagFilter: storedTagFilter ? storedTagFilter.split(',') : [],
range: 'latest',
batch: storedBatch || 'all',
sfw: storedSfw === 'true' || false,