Compare commits
3 commits
38cd37cf74
...
0a73fcbcf5
Author | SHA1 | Date | |
---|---|---|---|
0a73fcbcf5 | |||
50ef15c1a6 | |||
3fd26b4e66 |
11 changed files with 253 additions and 39 deletions
11
app/assets/icons/sort_asc.svg
Executable file
11
app/assets/icons/sort_asc.svg
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||||
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%"
|
||||||
|
height="100%" viewBox="0 0 100 100"
|
||||||
|
version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xml:space="preserve"
|
||||||
|
xmlns:serif="http://www.serif.com/"
|
||||||
|
style="fill: currentColor">
|
||||||
|
<path d="M26.208,59.542l-1.208,1.25l-0,-31.625c-0,-2.286 -1.881,-4.167 -4.167,-4.167c-2.285,0 -4.166,1.881 -4.166,4.167l-0,31.625l-1.209,-1.25c-0.784,-0.785 -1.849,-1.226 -2.958,-1.226c-2.295,0 -4.184,1.889 -4.184,4.184c0,1.109 0.441,2.174 1.226,2.958l8.333,8.334c0.396,0.379 0.864,0.676 1.375,0.875c1.008,0.445 2.159,0.445 3.167,-0c0.511,-0.199 0.978,-0.496 1.375,-0.875l8.333,-8.334c0.784,-0.784 1.225,-1.849 1.225,-2.958c0,-2.295 -1.888,-4.184 -4.183,-4.184c-1.109,0 -2.174,0.441 -2.959,1.226m19.625,-26.209l12.5,0c2.286,0 4.167,-1.881 4.167,-4.166c-0,-2.286 -1.881,-4.167 -4.167,-4.167l-12.5,0c-2.285,0 -4.166,1.881 -4.166,4.167c-0,2.285 1.881,4.166 4.166,4.166m41.667,12.5l-41.667,0c-2.285,0 -4.166,1.881 -4.166,4.167c-0,2.286 1.881,4.167 4.166,4.167l25,-0c2.286,-0 4.167,-1.881 4.167,-4.167c-0,-2.286 -1.881,-4.167 -4.167,-4.167m16.667,20.834l-41.667,-0c-2.285,-0 -4.166,1.881 -4.166,4.166c-0,2.286 1.881,4.167 4.166,4.167l41.667,0c2.286,0 4.167,-1.881 4.167,-4.167c-0,-2.285 -1.881,-4.166 -4.167,-4.166"
|
||||||
|
style="fill: currentColor"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
4
app/assets/icons/sort_desc.svg
Executable file
4
app/assets/icons/sort_desc.svg
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||||
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><path d="M26.208,59.542l-1.208,1.25l-0,-31.625c-0,-2.286 -1.881,-4.167 -4.167,-4.167c-2.285,0 -4.166,1.881 -4.166,4.167l-0,31.625l-1.209,-1.25c-0.784,-0.785 -1.849,-1.226 -2.958,-1.226c-2.295,0 -4.184,1.889 -4.184,4.184c0,1.109 0.441,2.174 1.226,2.958l8.333,8.334c0.396,0.379 0.864,0.676 1.375,0.875c1.008,0.445 2.159,0.445 3.167,-0c0.511,-0.199 0.978,-0.496 1.375,-0.875l8.333,-8.334c0.784,-0.784 1.225,-1.849 1.225,-2.958c0,-2.295 -1.888,-4.184 -4.183,-4.184c-1.109,0 -2.174,0.441 -2.959,1.226m19.625,-26.209l41.667,0c2.286,0 4.167,-1.881 4.167,-4.166c-0,-2.286 -1.881,-4.167 -4.167,-4.167l-41.667,0c-2.285,0 -4.166,1.881 -4.166,4.167c-0,2.285 1.881,4.166 4.166,4.166m41.667,12.5l-41.667,0c-2.285,0 -4.166,1.881 -4.166,4.167c-0,2.286 1.881,4.167 4.166,4.167l25,-0c2.286,-0 4.167,-1.881 4.167,-4.167c-0,-2.286 -1.881,-4.167 -4.167,-4.167m16.667,20.834l-41.667,-0c-2.285,-0 -4.166,1.881 -4.166,4.166c-0,2.286 1.881,4.167 4.166,4.167l12.5,0c2.286,0 4.167,-1.881 4.167,-4.167c-0,-2.285 -1.881,-4.166 -4.167,-4.166" style="fill: currentColor"/></svg>
|
After Width: | Height: | Size: 1.5 KiB |
64
app/assets/styles/form/dropdown.css
Normal file
64
app/assets/styles/form/dropdown.css
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
.DropDown {
|
||||||
|
--border-color: var(--color-light);
|
||||||
|
--label-color: var(--color-darkest);
|
||||||
|
--background-color: var(--color-lightest);
|
||||||
|
--arrow-color: var(--color-main-dark);
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
--background-color: var(--color-main-dark);
|
||||||
|
--label-color: var(--color-lightest);
|
||||||
|
--arrow-color: var(--color-lightest);
|
||||||
|
--border-color: var(--color-main-dark);
|
||||||
|
|
||||||
|
& ul {
|
||||||
|
scale: 1 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& button {
|
||||||
|
all: unset;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--label-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius-default);
|
||||||
|
background: var(--background-color);
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: .5rem;
|
||||||
|
transition: var(--transition-default);
|
||||||
|
width: 70px;
|
||||||
|
|
||||||
|
& .icon {
|
||||||
|
color: var(--arrow-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& ul {
|
||||||
|
position: absolute;
|
||||||
|
list-style: none;
|
||||||
|
transform-origin: top;
|
||||||
|
scale: 1 0;
|
||||||
|
transition: var(--transition-default);
|
||||||
|
color: var(--color-darkest);
|
||||||
|
border: 1px solid var(--color-main-dark);
|
||||||
|
border-radius: var(--radius-default);
|
||||||
|
background: var(--color-lightest);
|
||||||
|
box-shadow: var(--box-shadow-z2);
|
||||||
|
width: 100%;
|
||||||
|
z-index: 2000;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
& li {
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background: var(--color-main-dark);
|
||||||
|
color: var(--color-lightest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
app/assets/styles/form/togglebutton.css
Normal file
14
app/assets/styles/form/togglebutton.css
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
.ToggleButton {
|
||||||
|
all: unset;
|
||||||
|
color: var(--color-lightest);
|
||||||
|
background: var(--color-main-dark);
|
||||||
|
padding: .5rem .8rem;
|
||||||
|
border-radius: var(--radius-default);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
& .icon {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,24 +14,25 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-bar {
|
.filter-bar {
|
||||||
background: var(--color-lightest);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: var(--padding-default);
|
padding: var(--padding-default);
|
||||||
|
|
||||||
& > button {
|
& .box {
|
||||||
all: unset;
|
display: flex;
|
||||||
cursor: pointer;
|
flex-direction: column;
|
||||||
color: var(--color-main-darkest);
|
gap: .5rem;
|
||||||
font-weight: bolder;
|
|
||||||
font-family: 'Roboto', sans-serif;
|
|
||||||
|
|
||||||
&.active {
|
& strong {
|
||||||
color: var(--color-main-darkest);
|
color: var(--color-darkest);
|
||||||
|
font-size: .8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.active) {
|
& > div {
|
||||||
opacity: .5;
|
background: var(--color-lightest);
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: .5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: var(--color-darkest);
|
color: var(--color-darkest);
|
||||||
border-bottom: 1px solid var(--color-light);
|
border-bottom: 1px dashed var(--color-light);
|
||||||
|
|
||||||
.bottom {
|
.bottom {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
45
app/components/Pp/Form/DropDown.vue
Normal file
45
app/components/Pp/Form/DropDown.vue
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="DropDown"
|
||||||
|
:class="{ active }"
|
||||||
|
ref="dropdown"
|
||||||
|
>
|
||||||
|
<button @click="active = !active">
|
||||||
|
<span>{{ current.label }}</span>
|
||||||
|
<Icon class="icon" name="uil:angle-down" mode="svg" />
|
||||||
|
</button>
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
v-for="element in elements"
|
||||||
|
@click="click(element)"
|
||||||
|
:class="{ selected : element.value === current.value }"
|
||||||
|
>
|
||||||
|
{{ element.label }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { DropDownElement } from '../../../../shared/DropDown'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
elements : DropDownElement[]
|
||||||
|
current : DropDownElement
|
||||||
|
}
|
||||||
|
|
||||||
|
const { elements = [] } = defineProps<Props>()
|
||||||
|
const emit = defineEmits(['click'])
|
||||||
|
|
||||||
|
const active = ref(false)
|
||||||
|
const dropdown = useTemplateRef<HTMLDivElement>('dropdown')
|
||||||
|
|
||||||
|
const click = (element : DropDownElement) => {
|
||||||
|
emit('click', element)
|
||||||
|
active.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
onClickOutside(dropdown, () => active.value = false)
|
||||||
|
})
|
||||||
|
</script>
|
21
app/components/Pp/Form/ToggleButton.vue
Normal file
21
app/components/Pp/Form/ToggleButton.vue
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<button class="ToggleButton" @click="click">
|
||||||
|
<Icon class="icon" :name="icons[current]!" mode="svg" />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
type Props = {
|
||||||
|
icons : string[]
|
||||||
|
current: number
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
const emit = defineEmits(['click'])
|
||||||
|
|
||||||
|
const click = () => emit('click')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -13,13 +13,32 @@
|
||||||
@update="updateCard()"
|
@update="updateCard()"
|
||||||
/>
|
/>
|
||||||
<section class="content flex-col">
|
<section class="content flex-col">
|
||||||
<div class="content-text">
|
|
||||||
<h1>Mit ProPapier Preise vergleichen und sparen</h1>
|
|
||||||
</div>
|
|
||||||
<aside class="filter-bar">
|
<aside class="filter-bar">
|
||||||
<button v-for="(button, index) in filterButtons" @click="() => sort(index)" :class="{ 'active': button.active }">
|
<div class="box">
|
||||||
{{ button.label }}
|
<strong>Kategorie:</strong>
|
||||||
</button>
|
<div>
|
||||||
|
<PpFormDropDown
|
||||||
|
:elements="sortElements"
|
||||||
|
:current="currentSortElement!"
|
||||||
|
@click="setSortElement"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
<strong>Preise Sortieren nach:</strong>
|
||||||
|
<div>
|
||||||
|
<PpFormDropDown
|
||||||
|
:elements="sortElements"
|
||||||
|
:current="currentSortElement!"
|
||||||
|
@click="setSortElement"
|
||||||
|
/>
|
||||||
|
<PpFormToggleButton
|
||||||
|
:icons="['pp:sort-desc', 'pp:sort-asc']"
|
||||||
|
:current="currentSortDirection"
|
||||||
|
@click="countSortDirection"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
<div class="flex-col" role="list" v-if="cards.length">
|
<div class="flex-col" role="list" v-if="cards.length">
|
||||||
<PpPriceCard
|
<PpPriceCard
|
||||||
|
@ -52,15 +71,37 @@
|
||||||
import type { Card } from '../../shared/Card'
|
import type { Card } from '../../shared/Card'
|
||||||
import type { Button } from '../../shared/ButtonGroup'
|
import type { Button } from '../../shared/ButtonGroup'
|
||||||
import { PpPriceCardDialog, PpDeleteDialog, PpPriceCard } from '#components'
|
import { PpPriceCardDialog, PpDeleteDialog, PpPriceCard } from '#components'
|
||||||
|
import type { DropDownElement } from '../../shared/DropDown'
|
||||||
|
|
||||||
const cards = useLocalStorage<Card[]>('cards', [])
|
const cards = useLocalStorage<Card[]>('cards', [])
|
||||||
const currentSort = useLocalStorage<number>('sort', 0)
|
const currentSort = useLocalStorage<number>('sort', 0)
|
||||||
|
const currentSortDirection = useLocalStorage<number>('sortDirection', 0)
|
||||||
const currentCard = ref<Card>()
|
const currentCard = ref<Card>()
|
||||||
const currentCardIndex = ref<number>(-1)
|
const currentCardIndex = ref<number>(-1)
|
||||||
const modal = useTemplateRef<typeof PpPriceCardDialog>('modal')
|
const modal = useTemplateRef<typeof PpPriceCardDialog>('modal')
|
||||||
const deleteModal = useTemplateRef<typeof PpDeleteDialog>('deleteModal')
|
const deleteModal = useTemplateRef<typeof PpDeleteDialog>('deleteModal')
|
||||||
const priceCards = useTemplateRef<(typeof PpPriceCard)[]>('priceCard')
|
const priceCards = useTemplateRef<(typeof PpPriceCard)[]>('priceCard')
|
||||||
|
|
||||||
|
const sortElements : DropDownElement[] = [
|
||||||
|
{
|
||||||
|
label: 'Rollen',
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Blatt',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Lagen',
|
||||||
|
value: 2,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
const currentSortElement = ref<DropDownElement>(sortElements[0]!)
|
||||||
|
|
||||||
|
const setSortElement = (element : DropDownElement) => {
|
||||||
|
sort(element.value as number)
|
||||||
|
}
|
||||||
|
|
||||||
const createCard = (uuid : string) : Card => ({
|
const createCard = (uuid : string) : Card => ({
|
||||||
uuid,
|
uuid,
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -70,7 +111,6 @@ const createCard = (uuid : string) : Card => ({
|
||||||
layers: '',
|
layers: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const addCard = (card : Card) => {
|
const addCard = (card : Card) => {
|
||||||
cards.value.unshift({ ...card })
|
cards.value.unshift({ ...card })
|
||||||
sort()
|
sort()
|
||||||
|
@ -111,37 +151,23 @@ const openDeleteModal = () => {
|
||||||
deleteModal.value?.$el.showModal()
|
deleteModal.value?.$el.showModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterButtons = ref<Button[]>([
|
|
||||||
{
|
|
||||||
label: 'Rollen',
|
|
||||||
icon: 'uil:toilet-paper',
|
|
||||||
active: currentSort.value === 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Blatt',
|
|
||||||
icon: 'uil:file-landscape',
|
|
||||||
active: currentSort.value === 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Lagen',
|
|
||||||
icon: 'uil:layer-group',
|
|
||||||
active: currentSort.value === 2,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
const sortBy = (key : 'ppr' | 'pps' | 'ppl') => {
|
const sortBy = (key : 'ppr' | 'pps' | 'ppl') => {
|
||||||
cards.value.sort((a, b) => {
|
cards.value.sort((a, b) => {
|
||||||
const aCard = priceCards.value?.find(card => card.uuid === a.uuid) || null
|
const aCard = priceCards.value?.find(card => card.uuid === a.uuid) || null
|
||||||
const bCard = priceCards.value?.find(card => card.uuid === b.uuid) || null
|
const bCard = priceCards.value?.find(card => card.uuid === b.uuid) || null
|
||||||
if (!aCard || !bCard) return 0
|
if (!aCard || !bCard) return 0
|
||||||
|
|
||||||
|
if (currentSortDirection.value === 0) {
|
||||||
|
return bCard[key] - aCard[key]
|
||||||
|
}
|
||||||
|
|
||||||
return aCard[key] - bCard[key]
|
return aCard[key] - bCard[key]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const sort = async (index : number = currentSort.value) => {
|
const sort = async (index : number = currentSort.value) => {
|
||||||
currentSort.value = index
|
currentSort.value = index
|
||||||
filterButtons.value.forEach(button => { button.active = false })
|
currentSortElement.value = sortElements[index]!
|
||||||
filterButtons.value[index]!.active = true
|
|
||||||
|
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
|
@ -151,4 +177,12 @@ const sort = async (index : number = currentSort.value) => {
|
||||||
case 2: return sortBy('ppl')
|
case 2: return sortBy('ppl')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const countSortDirection = () => {
|
||||||
|
let newSortDirection = currentSortDirection.value + 1
|
||||||
|
if (newSortDirection > 1) newSortDirection = 0
|
||||||
|
|
||||||
|
currentSortDirection.value = newSortDirection
|
||||||
|
sort()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -54,11 +54,27 @@ export default defineNuxtConfig({
|
||||||
'./app/assets/styles/priceCard.css',
|
'./app/assets/styles/priceCard.css',
|
||||||
'./app/assets/styles/form/textfield.css',
|
'./app/assets/styles/form/textfield.css',
|
||||||
'./app/assets/styles/form/search.css',
|
'./app/assets/styles/form/search.css',
|
||||||
|
'./app/assets/styles/form/dropdown.css',
|
||||||
|
'./app/assets/styles/form/togglebutton.css',
|
||||||
'./app/assets/styles/toolbar.css',
|
'./app/assets/styles/toolbar.css',
|
||||||
'./app/assets/styles/page.css',
|
'./app/assets/styles/page.css',
|
||||||
'./app/assets/styles/dialog.css',
|
'./app/assets/styles/dialog.css',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
icon: {
|
||||||
|
customCollections: [
|
||||||
|
{
|
||||||
|
prefix: 'pp',
|
||||||
|
dir: './app/assets/icons'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
provider: 'iconify',
|
||||||
|
serverBundle: 'local',
|
||||||
|
clientBundle: {
|
||||||
|
scan: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
site: {
|
site: {
|
||||||
url: 'https://pro-papier.de',
|
url: 'https://pro-papier.de',
|
||||||
name: 'ProPapier',
|
name: 'ProPapier',
|
||||||
|
|
4
shared/DropDown.ts
Normal file
4
shared/DropDown.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export type DropDownElement = {
|
||||||
|
label: string
|
||||||
|
value: string | number
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue