You've already forked AstralRinth
forked from didirus/AstralRinth
Organize components, switch auth to not use session
This commit is contained in:
34
components/ui/Advertisement.vue
Normal file
34
components/ui/Advertisement.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="ad-wrapper">
|
||||
<adsbygoogle
|
||||
ad-slot="7510690716"
|
||||
:ad-format="format"
|
||||
:page-url="pageUrl ? pageUrl : undefined"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Advertisement',
|
||||
props: {
|
||||
format: {
|
||||
type: String,
|
||||
default: 'horizontal',
|
||||
},
|
||||
pageUrl: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ad-wrapper {
|
||||
width: 100%;
|
||||
@extend %card;
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
}
|
||||
</style>
|
||||
132
components/ui/Categories.vue
Normal file
132
components/ui/Categories.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div class="categories">
|
||||
<p v-if="categories.includes('fabric')">
|
||||
<FabricLoader aria-hidden="true" />
|
||||
Fabric
|
||||
</p>
|
||||
<p v-if="categories.includes('forge')">
|
||||
<ForgeLoader aria-hidden="true" />
|
||||
Forge
|
||||
</p>
|
||||
<p v-if="categories.includes('technology')">
|
||||
<TechCategory aria-hidden="true" />
|
||||
Technology
|
||||
</p>
|
||||
<p v-if="categories.includes('adventure')">
|
||||
<AdventureCategory aria-hidden="true" />
|
||||
Adventure
|
||||
</p>
|
||||
<p v-if="categories.includes('magic')">
|
||||
<MagicCategory aria-hidden="true" />
|
||||
Magic
|
||||
</p>
|
||||
<p v-if="categories.includes('utility')">
|
||||
<UtilityCategory aria-hidden="true" />
|
||||
Utility
|
||||
</p>
|
||||
<p v-if="categories.includes('decoration')">
|
||||
<DecorationCategory aria-hidden="true" />
|
||||
Decoration
|
||||
</p>
|
||||
<p v-if="categories.includes('library')">
|
||||
<LibraryCategory aria-hidden="true" />
|
||||
Library
|
||||
</p>
|
||||
<p v-if="categories.includes('cursed')">
|
||||
<CursedCategory aria-hidden="true" />
|
||||
Cursed
|
||||
</p>
|
||||
<p v-if="categories.includes('worldgen')">
|
||||
<WorldGenCategory aria-hidden="true" />
|
||||
Worldgen
|
||||
</p>
|
||||
<p v-if="categories.includes('storage')">
|
||||
<StorageCategory aria-hidden="true" />
|
||||
Storage
|
||||
</p>
|
||||
<p v-if="categories.includes('food')">
|
||||
<FoodCategory aria-hidden="true" />
|
||||
Food
|
||||
</p>
|
||||
<p v-if="categories.includes('equipment')">
|
||||
<EquipmentCategory aria-hidden="true" />
|
||||
Equipment
|
||||
</p>
|
||||
<p v-if="categories.includes('misc')">
|
||||
<MiscCategory aria-hidden="true" />
|
||||
Misc
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TechCategory from '~/assets/images/categories/tech.svg'
|
||||
import AdventureCategory from '~/assets/images/categories/adventure.svg'
|
||||
import CursedCategory from '~/assets/images/categories/cursed.svg'
|
||||
import DecorationCategory from '~/assets/images/categories/decoration.svg'
|
||||
import EquipmentCategory from '~/assets/images/categories/equipment.svg'
|
||||
import FoodCategory from '~/assets/images/categories/food.svg'
|
||||
import LibraryCategory from '~/assets/images/categories/library.svg'
|
||||
import MagicCategory from '~/assets/images/categories/magic.svg'
|
||||
import MiscCategory from '~/assets/images/categories/misc.svg'
|
||||
import StorageCategory from '~/assets/images/categories/storage.svg'
|
||||
import UtilityCategory from '~/assets/images/categories/utility.svg'
|
||||
import WorldGenCategory from '~/assets/images/categories/worldgen.svg'
|
||||
|
||||
import ForgeLoader from '~/assets/images/categories/forge.svg'
|
||||
import FabricLoader from '~/assets/images/categories/fabric.svg'
|
||||
|
||||
export default {
|
||||
name: 'Categories',
|
||||
components: {
|
||||
TechCategory,
|
||||
AdventureCategory,
|
||||
CursedCategory,
|
||||
DecorationCategory,
|
||||
EquipmentCategory,
|
||||
FoodCategory,
|
||||
LibraryCategory,
|
||||
MagicCategory,
|
||||
MiscCategory,
|
||||
StorageCategory,
|
||||
UtilityCategory,
|
||||
WorldGenCategory,
|
||||
ForgeLoader,
|
||||
FabricLoader,
|
||||
},
|
||||
props: {
|
||||
categories: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.categories {
|
||||
@extend %row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
p {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
background-color: var(--color-category-bg);
|
||||
border-radius: var(--size-rounded-max);
|
||||
color: var(--color-category-text);
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 0.25em;
|
||||
margin-right: 0.5em;
|
||||
padding: 0.4em 0.7em;
|
||||
font-size: var(--font-size-sm);
|
||||
height: 1em;
|
||||
svg {
|
||||
width: 15px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
152
components/ui/ConfirmPopup.vue
Normal file
152
components/ui/ConfirmPopup.vue
Normal file
@@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<Popup :show-popup="display">
|
||||
<div class="popup-delete">
|
||||
<span class="title">{{ title }}</span>
|
||||
<span class="description">
|
||||
{{ description }}
|
||||
</span>
|
||||
<label v-if="hasToType" for="confirmation" class="confirmation-label">
|
||||
<span>
|
||||
To confirm your action, please type
|
||||
<span class="confirmation-text">{{ confirmationText }}</span>
|
||||
to continue
|
||||
</span>
|
||||
</label>
|
||||
<input
|
||||
v-if="hasToType"
|
||||
id="confirmation"
|
||||
v-model="confirmation_typed"
|
||||
type="text"
|
||||
placeholder="Type the input needed to continue"
|
||||
@input="type"
|
||||
/>
|
||||
<div class="actions">
|
||||
<button class="button" @click="cancel">Cancel</button>
|
||||
<button
|
||||
class="button warn-button"
|
||||
:disabled="action_disabled"
|
||||
@click="proceed"
|
||||
>
|
||||
{{ proceedLabel }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ConfirmPopup',
|
||||
props: {
|
||||
confirmationText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hasToType: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: 'No title defined',
|
||||
required: true,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: 'No description defined',
|
||||
required: true,
|
||||
},
|
||||
proceedLabel: {
|
||||
type: String,
|
||||
default: 'Proceed',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
action_disabled: this.hasToType,
|
||||
confirmation_typed: '',
|
||||
display: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cancel() {
|
||||
this.display = false
|
||||
},
|
||||
proceed() {
|
||||
this.display = false
|
||||
this.$emit('proceed')
|
||||
},
|
||||
type() {
|
||||
if (this.hasToType) {
|
||||
this.action_disabled =
|
||||
this.confirmation_typed.toLowerCase() !==
|
||||
this.confirmationText.toLowerCase()
|
||||
}
|
||||
},
|
||||
show() {
|
||||
this.display = true
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.popup-delete {
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@media screen and (min-width: 900px) {
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
max-width: 40vw;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.25rem;
|
||||
align-self: stretch;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
word-wrap: break-word;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.confirmation-label {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.confirmation-text {
|
||||
font-weight: bold;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 1.5rem;
|
||||
|
||||
button {
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
margin: 0.75rem 1rem;
|
||||
padding: 0.75rem 0;
|
||||
}
|
||||
|
||||
.warn-button {
|
||||
transition: background-color 1s, color 1s;
|
||||
color: var(--color-brand-inverted);
|
||||
background-color: var(--color-badge-red-bg);
|
||||
|
||||
&:disabled {
|
||||
background-color: var(--color-button-bg);
|
||||
color: var(--color-button-text-disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
88
components/ui/FileInput.vue
Normal file
88
components/ui/FileInput.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<label class="button" @drop.prevent="addFile" @dragover.prevent>
|
||||
<span>
|
||||
{{ text }}
|
||||
</span>
|
||||
<input
|
||||
type="file"
|
||||
:multiple="multiple"
|
||||
:accept="accept"
|
||||
@change="onChange"
|
||||
/>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FileInput',
|
||||
props: {
|
||||
prompt: {
|
||||
type: String,
|
||||
default: 'Select file',
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
accept: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
text: this.prompt,
|
||||
files: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(files, shouldNotReset) {
|
||||
if (!shouldNotReset) this.files = files.target.files
|
||||
|
||||
const length = this.files.length
|
||||
if (length === 0) {
|
||||
this.text = this.prompt
|
||||
} else if (length === 1) {
|
||||
this.text = '1 file selected'
|
||||
} else if (length > 1) {
|
||||
this.text = length + ' files selected'
|
||||
}
|
||||
this.$emit('change', this.files)
|
||||
},
|
||||
addFile(e) {
|
||||
const droppedFiles = e.dataTransfer.files
|
||||
|
||||
if (!this.multiple) this.files = []
|
||||
|
||||
if (!droppedFiles) return
|
||||
;[...droppedFiles].forEach((f) => {
|
||||
this.files.push(f)
|
||||
})
|
||||
|
||||
if (!this.multiple && this.files.length > 0) this.files = [this.files[0]]
|
||||
|
||||
if (this.files.length > 0) this.onChange(null, true)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: var(--spacing-card-sm) var(--spacing-card-md);
|
||||
}
|
||||
|
||||
span {
|
||||
border: 2px dashed var(--color-divider-dark);
|
||||
border-radius: var(--size-rounded-control);
|
||||
padding: var(--spacing-card-md) var(--spacing-card-lg);
|
||||
}
|
||||
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
278
components/ui/ProjectCard.vue
Normal file
278
components/ui/ProjectCard.vue
Normal file
@@ -0,0 +1,278 @@
|
||||
<template>
|
||||
<article class="project-card">
|
||||
<div class="icon">
|
||||
<nuxt-link v-if="isModrinth" :to="'/mod/' + id">
|
||||
<img
|
||||
:src="iconUrl ? iconUrl : 'https://cdn.modrinth.com/placeholder.svg'"
|
||||
:alt="name"
|
||||
/>
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="top">
|
||||
<h2 class="title">
|
||||
<nuxt-link v-if="isModrinth" :to="'/mod/' + id">{{ name }}</nuxt-link>
|
||||
<a v-else :href="pageUrl">{{ name }}</a>
|
||||
</h2>
|
||||
<p v-if="author" class="author">
|
||||
by <nuxt-link :to="'/user/' + author">{{ author }}</nuxt-link>
|
||||
</p>
|
||||
</div>
|
||||
<p class="description">
|
||||
{{ description }}
|
||||
</p>
|
||||
<div :class="{ vertical: editMode }" class="bottom">
|
||||
<div class="stats">
|
||||
<div v-if="status !== null" class="stat">
|
||||
<div class="info">
|
||||
<h4>Status</h4>
|
||||
<span v-if="status === 'approved'" class="badge green">
|
||||
Approved
|
||||
</span>
|
||||
<span v-if="status === 'rejected'" class="badge red">
|
||||
Rejected
|
||||
</span>
|
||||
<span v-if="status === 'draft'" class="badge yellow">Draft</span>
|
||||
<span v-if="status === 'processing'" class="badge yellow">
|
||||
Processing
|
||||
</span>
|
||||
<span v-if="status === 'unlisted'" class="badge gray">
|
||||
Unlisted
|
||||
</span>
|
||||
<span v-if="status === 'unknown'" class="badge gray">
|
||||
Unknown
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
<div class="info">
|
||||
<h4>Downloads</h4>
|
||||
<p class="value">{{ formatNumber(downloads) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<CalendarIcon aria-hidden="true" />
|
||||
<div class="info">
|
||||
<h4>Created</h4>
|
||||
<p
|
||||
v-tooltip="
|
||||
$dayjs(createdAt).format(
|
||||
'[Created on] YYYY-MM-DD [at] HH:mm A'
|
||||
)
|
||||
"
|
||||
class="value"
|
||||
>
|
||||
{{ $dayjs(createdAt).fromNow() }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<EditIcon aria-hidden="true" />
|
||||
<div class="info">
|
||||
<h4>Updated</h4>
|
||||
<p
|
||||
v-tooltip="
|
||||
$dayjs(updatedAt).format(
|
||||
'[Updated on] YYYY-MM-DD [at] HH:mm A'
|
||||
)
|
||||
"
|
||||
class="value"
|
||||
>
|
||||
{{ $dayjs(updatedAt).fromNow() }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="latestVersion" class="stat">
|
||||
<TagIcon aria-hidden="true" />
|
||||
<div class="info">
|
||||
<h4>Available For</h4>
|
||||
<p class="value">
|
||||
{{ latestVersion }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Categories :categories="categories" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="editMode" class="buttons">
|
||||
<slot />
|
||||
</div>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Categories from '~/components/ui/Categories'
|
||||
|
||||
import CalendarIcon from '~/assets/images/utils/calendar.svg'
|
||||
import DownloadIcon from '~/assets/images/utils/download.svg'
|
||||
import EditIcon from '~/assets/images/utils/edit.svg'
|
||||
import TagIcon from '~/assets/images/utils/tag.svg'
|
||||
|
||||
export default {
|
||||
name: 'ProjectCard',
|
||||
components: {
|
||||
Categories,
|
||||
CalendarIcon,
|
||||
DownloadIcon,
|
||||
EditIcon,
|
||||
TagIcon,
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: 'modrinth-0',
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: 'Mod Name',
|
||||
},
|
||||
author: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: 'A mod description',
|
||||
},
|
||||
pageUrl: {
|
||||
type: String,
|
||||
default: '#',
|
||||
},
|
||||
authorUrl: {
|
||||
type: String,
|
||||
default: '#',
|
||||
},
|
||||
iconUrl: {
|
||||
type: String,
|
||||
default: '#',
|
||||
},
|
||||
downloads: {
|
||||
type: String,
|
||||
default: '0',
|
||||
},
|
||||
createdAt: {
|
||||
type: String,
|
||||
default: '0000-00-00',
|
||||
},
|
||||
updatedAt: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
latestVersion: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
categories: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
editMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
isModrinth: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
formatNumber(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.project-card {
|
||||
@extend %row;
|
||||
@extend %card-spaced-b;
|
||||
width: 100%;
|
||||
.icon {
|
||||
margin: auto 0;
|
||||
img {
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
margin: var(--spacing-card-md);
|
||||
border-radius: var(--size-rounded-icon);
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
.info {
|
||||
@extend %column;
|
||||
flex-grow: 1;
|
||||
.top {
|
||||
@extend %row;
|
||||
flex-wrap: wrap;
|
||||
flex-shrink: 0;
|
||||
margin-top: var(--spacing-card-md);
|
||||
margin-right: var(--spacing-card-md);
|
||||
.title {
|
||||
margin: 0;
|
||||
color: var(--color-text-dark);
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
.author {
|
||||
margin: auto 0 0 0.5rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
.description {
|
||||
margin: var(--spacing-card-sm) var(--spacing-card-md) 0 0;
|
||||
height: 100%;
|
||||
color: var(--color-text-dark);
|
||||
}
|
||||
.bottom {
|
||||
@extend %column;
|
||||
flex-shrink: 0;
|
||||
margin-top: var(--spacing-card-sm);
|
||||
margin-right: var(--spacing-card-md);
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
flex-direction: row;
|
||||
&.vertical {
|
||||
flex-direction: column;
|
||||
.categories {
|
||||
margin-top: var(--spacing-card-sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stats {
|
||||
@extend %row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@media screen and (min-width: 900px) {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.stat {
|
||||
@extend %stat;
|
||||
}
|
||||
}
|
||||
.categories {
|
||||
@media screen and (min-width: 1024px) {
|
||||
flex-direction: row;
|
||||
margin: auto 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttons {
|
||||
@extend %column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
114
components/ui/search/Pagination.vue
Normal file
114
components/ui/search/Pagination.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div v-if="pages.length > 1" class="columns paginates">
|
||||
<button
|
||||
:class="{ disabled: currentPage === 1 }"
|
||||
class="paginate has-icon"
|
||||
aria-label="Previous Page"
|
||||
@click="currentPage !== 1 ? switchPage(currentPage - 1) : null"
|
||||
>
|
||||
<LeftArrowIcon />
|
||||
</button>
|
||||
<div
|
||||
v-for="(item, index) in pages"
|
||||
:key="'page-' + item"
|
||||
:class="{
|
||||
'page-number': currentPage !== item,
|
||||
}"
|
||||
class="page-number-container"
|
||||
>
|
||||
<div v-if="pages[index - 1] + 1 !== item && item !== 1" class="has-icon">
|
||||
<GapIcon />
|
||||
</div>
|
||||
<button
|
||||
:class="{ 'page-number current': currentPage === item }"
|
||||
@click="currentPage !== item ? switchPage(item) : null"
|
||||
>
|
||||
{{ item }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
:class="{ disabled: currentPage === pages[pages.length - 1] }"
|
||||
class="paginate has-icon"
|
||||
aria-label="Next Page"
|
||||
@click="
|
||||
currentPage !== pages[pages.length - 1]
|
||||
? switchPage(currentPage + 1)
|
||||
: null
|
||||
"
|
||||
>
|
||||
<RightArrowIcon />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GapIcon from '~/assets/images/utils/gap.svg'
|
||||
import LeftArrowIcon from '~/assets/images/utils/left-arrow.svg'
|
||||
import RightArrowIcon from '~/assets/images/utils/right-arrow.svg'
|
||||
|
||||
export default {
|
||||
name: 'Pagination',
|
||||
components: {
|
||||
GapIcon,
|
||||
LeftArrowIcon,
|
||||
RightArrowIcon,
|
||||
},
|
||||
props: {
|
||||
currentPage: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
pages: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
switchPage(newPage) {
|
||||
this.$emit('switch-page', newPage)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
button {
|
||||
min-width: 2rem;
|
||||
padding: 0 0.5rem;
|
||||
height: 2rem;
|
||||
border-radius: 2rem;
|
||||
background: transparent;
|
||||
&.page-number.current {
|
||||
background: var(--color-button-bg-hover);
|
||||
color: var(--color-button-text-hover);
|
||||
cursor: default;
|
||||
}
|
||||
&.paginate.disabled {
|
||||
background: none;
|
||||
color: var(--color-button-text-disabled);
|
||||
cursor: default;
|
||||
}
|
||||
&:hover {
|
||||
background: var(--color-button-bg-active);
|
||||
color: var(--color-button-text-active);
|
||||
}
|
||||
}
|
||||
|
||||
.has-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 0.5rem;
|
||||
height: 2rem;
|
||||
svg {
|
||||
width: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.page-number-container {
|
||||
display: flex;
|
||||
max-height: 2rem;
|
||||
}
|
||||
</style>
|
||||
71
components/ui/search/SearchFilter.vue
Normal file
71
components/ui/search/SearchFilter.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<p
|
||||
class="filter"
|
||||
:class="{
|
||||
'filter-active': activeFilters.includes(facetName),
|
||||
cursed: displayName == 'FlameAnvil',
|
||||
}"
|
||||
@click="toggle"
|
||||
>
|
||||
<slot></slot>
|
||||
{{ displayName }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SearchFilter',
|
||||
props: {
|
||||
facetName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
displayName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
activeFilters: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
this.$emit('toggle', this.facetName)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding: 0.4rem 0.3rem;
|
||||
margin: 3px 0 0 0.5rem;
|
||||
font-size: 1rem;
|
||||
letter-spacing: 0.02rem;
|
||||
@extend %transparent-clickable;
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
padding: 0.2rem 0.3rem;
|
||||
}
|
||||
|
||||
svg {
|
||||
color: var(--color-icon);
|
||||
margin-right: 5px;
|
||||
height: 1rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-active {
|
||||
@extend %transparent-clickable.selected;
|
||||
svg {
|
||||
color: var(--color-brand-light);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user