mirror of
https://github.com/ThisIsBenny/wishlist-app.git
synced 2025-04-19 15:27:41 +00:00
feature: dark-mode
This commit is contained in:
parent
1a2cc6dfeb
commit
e0abc98b19
13 changed files with 170 additions and 40 deletions
|
@ -7,7 +7,10 @@
|
|||
<title>Wunschlisten</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<div
|
||||
id="app"
|
||||
class="bg-white dark:bg-stone-900 text-black dark:text-white/75"
|
||||
></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
98
package-lock.json
generated
98
package-lock.json
generated
|
@ -9,6 +9,7 @@
|
|||
"dependencies": {
|
||||
"@prisma/client": "^3.8.1",
|
||||
"@tailwindcss/line-clamp": "^0.3.1",
|
||||
"@vueuse/core": "^7.5.5",
|
||||
"axios": "^0.25.0",
|
||||
"fastify": "^3.27.0",
|
||||
"fastify-compress": "^4.0.1",
|
||||
|
@ -991,6 +992,78 @@
|
|||
"vue": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "7.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-7.5.5.tgz",
|
||||
"integrity": "sha512-RBDqmIoGfak4h3xdXa/Av+ibkb8NY044wEy6+PG2FAWNaID8/FkqmSFjbxogrbmpSX1yZ1PBHrM8DUp/FrIpbg==",
|
||||
"dependencies": {
|
||||
"@vueuse/shared": "7.5.5",
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.1.0",
|
||||
"vue": "^2.6.0 || ^3.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
},
|
||||
"vue": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/core/node_modules/@vueuse/shared": {
|
||||
"version": "7.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-7.5.5.tgz",
|
||||
"integrity": "sha512-mzzTsotHQRPnPAChy8iCv6ek/90CKYhAFyMRgNsMxpT0afZJkbMO/X0OaOu/1NuGbgb8UVjlsWKmCUgKTOF5hA==",
|
||||
"dependencies": {
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.1.0",
|
||||
"vue": "^2.6.0 || ^3.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
},
|
||||
"vue": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/core/node_modules/vue-demi": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.1.tgz",
|
||||
"integrity": "sha512-QL3ny+wX8c6Xm1/EZylbgzdoDolye+VpCXRhI2hug9dJTP3OUJ3lmiKN3CsVV3mOJKwFi0nsstbgob0vG7aoIw==",
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/abab": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
||||
|
@ -8029,6 +8102,31 @@
|
|||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"@vueuse/core": {
|
||||
"version": "7.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-7.5.5.tgz",
|
||||
"integrity": "sha512-RBDqmIoGfak4h3xdXa/Av+ibkb8NY044wEy6+PG2FAWNaID8/FkqmSFjbxogrbmpSX1yZ1PBHrM8DUp/FrIpbg==",
|
||||
"requires": {
|
||||
"@vueuse/shared": "7.5.5",
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vueuse/shared": {
|
||||
"version": "7.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-7.5.5.tgz",
|
||||
"integrity": "sha512-mzzTsotHQRPnPAChy8iCv6ek/90CKYhAFyMRgNsMxpT0afZJkbMO/X0OaOu/1NuGbgb8UVjlsWKmCUgKTOF5hA==",
|
||||
"requires": {
|
||||
"vue-demi": "*"
|
||||
}
|
||||
},
|
||||
"vue-demi": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.1.tgz",
|
||||
"integrity": "sha512-QL3ny+wX8c6Xm1/EZylbgzdoDolye+VpCXRhI2hug9dJTP3OUJ3lmiKN3CsVV3mOJKwFi0nsstbgob0vG7aoIw==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"abab": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
"dependencies": {
|
||||
"@prisma/client": "^3.8.1",
|
||||
"@tailwindcss/line-clamp": "^0.3.1",
|
||||
"@vueuse/core": "^7.5.5",
|
||||
"axios": "^0.25.0",
|
||||
"fastify": "^3.27.0",
|
||||
"fastify-compress": "^4.0.1",
|
||||
|
|
58
src/App.vue
58
src/App.vue
|
@ -1,34 +1,38 @@
|
|||
<template>
|
||||
<main class="app h-screen max-w-[900px] mx-auto p-10">
|
||||
<router-view v-slot="{ Component }">
|
||||
<template v-if="Component">
|
||||
<div
|
||||
v-if="error"
|
||||
class="flex flex-row space-x-2 items-center content-center justify-center m-20 text-red-500"
|
||||
>
|
||||
<IconError class="w-4 h-4" />
|
||||
<span>{{ t('errors.generic.text') }}</span>
|
||||
</div>
|
||||
<suspense v-else>
|
||||
<template #default>
|
||||
<component :is="Component"></component>
|
||||
</template>
|
||||
<template #fallback>
|
||||
<div
|
||||
class="flex flex-row space-x-2 items-center content-center justify-center m-20"
|
||||
>
|
||||
<IconSpinner class="w-4 h-4" />
|
||||
<span> {{ t('common.loading.text') }} </span>
|
||||
</div>
|
||||
</template>
|
||||
</suspense>
|
||||
</template>
|
||||
</router-view>
|
||||
</main>
|
||||
<modal-overlay />
|
||||
<div class="h-screen max-w-[900px] mx-auto">
|
||||
<Header />
|
||||
<main class="h-full">
|
||||
<router-view v-slot="{ Component }">
|
||||
<template v-if="Component">
|
||||
<div
|
||||
v-if="error"
|
||||
class="flex flex-row space-x-2 items-center content-center justify-center m-20 text-red-500"
|
||||
>
|
||||
<IconError class="w-4 h-4" />
|
||||
<span>{{ t('errors.generic.text') }}</span>
|
||||
</div>
|
||||
<suspense v-else>
|
||||
<template #default>
|
||||
<component :is="Component"></component>
|
||||
</template>
|
||||
<template #fallback>
|
||||
<div
|
||||
class="flex flex-row space-x-2 items-center content-center justify-center m-20"
|
||||
>
|
||||
<IconSpinner class="w-4 h-4" />
|
||||
<span> {{ t('common.loading.text') }} </span>
|
||||
</div>
|
||||
</template>
|
||||
</suspense>
|
||||
</template>
|
||||
</router-view>
|
||||
</main>
|
||||
<modal-overlay />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onErrorCaptured, ref } from 'vue'
|
||||
import Header from '@/components/Header.vue'
|
||||
import { IconSpinner, IconError } from '@/components/icons'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
|
|
|
@ -5,7 +5,7 @@ defineProps<{
|
|||
</script>
|
||||
<template>
|
||||
<button
|
||||
class="border-2 border-stone-200 text-stone-500 hover:bg-stone-100 rounded-md py-1 px-2 flex-row items-center w-fit inline-flex justify-center"
|
||||
class="border-2 border-stone-200 dark:border-stone-700 text-stone-500 dark:text-white/70 hover:bg-stone-100 dark:hover:bg-stone-700 rounded-md py-1 px-2 flex-row items-center w-fit inline-flex justify-center"
|
||||
>
|
||||
<component v-if="icon" :is="icon" class="w-4 h-4 mr-1" />
|
||||
<span>
|
||||
|
|
15
src/components/Header.vue
Normal file
15
src/components/Header.vue
Normal file
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<header class="py-4 flex flex-row-reverse opacity-60">
|
||||
<div @click="toggleMode"><IconLightDark class="h-6 w-6" /></div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useColorMode } from '@vueuse/core'
|
||||
import { IconLightDark } from '@/components/icons'
|
||||
const mode = useColorMode()
|
||||
|
||||
const toggleMode = () => {
|
||||
mode.value = mode.value === 'dark' ? 'light' : 'dark'
|
||||
}
|
||||
</script>
|
|
@ -10,7 +10,7 @@ defineProps<{
|
|||
<img :src="imageSrc" class="object-cover w-full h-full" :alt="title" />
|
||||
<div
|
||||
v-if="title"
|
||||
class="text-background absolute top-0 inset-x-0 h-40 flex justify-center items-center text-md text-white text-center font-bold"
|
||||
class="text-background absolute top-0 inset-x-0 h-40 flex justify-center items-center text-md text-white dark:text-white/75 text-center font-bold"
|
||||
>
|
||||
{{ title }}
|
||||
</div>
|
||||
|
|
|
@ -15,7 +15,7 @@ const { t } = useI18n()
|
|||
|
||||
<template>
|
||||
<div
|
||||
class="h-fit sm:h-40 flex flex-col sm:flex-row space-x-0 sm:space-x-2 rounded-md border-stone-200 border-2 overflow-hidden"
|
||||
class="h-fit sm:h-40 flex flex-col sm:flex-row space-x-0 sm:space-x-2 rounded-md border-stone-200 dark:border-stone-700 border-2 overflow-hidden"
|
||||
>
|
||||
<img
|
||||
v-if="image"
|
||||
|
@ -49,7 +49,7 @@ const { t } = useI18n()
|
|||
:href="url"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="text-sm mt-1 text-stone-500 flex flex-row items-center w-fit"
|
||||
class="text-sm mt-1 text-stone-500 dark:text-white/60 flex flex-row items-center w-fit"
|
||||
>
|
||||
<IconLink class="mr-1 w-4 h-4" />
|
||||
<span>{{
|
||||
|
|
8
src/components/icons/IconLightDark.vue
Normal file
8
src/components/icons/IconLightDark.vue
Normal file
|
@ -0,0 +1,8 @@
|
|||
<template>
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M7.5 2c-1.79 1.15-3 3.18-3 5.5s1.21 4.35 3.03 5.5C4.46 13 2 10.54 2 7.5A5.5 5.5 0 0 1 7.5 2m11.57 1.5l1.43 1.43L4.93 20.5L3.5 19.07L19.07 3.5m-6.18 2.43L11.41 5L9.97 6l.42-1.7L9 3.24l1.75-.12l.58-1.65L12 3.1l1.73.03l-1.35 1.13l.51 1.67m-3.3 3.61l-1.16-.73l-1.12.78l.34-1.32l-1.09-.83l1.36-.09l.45-1.29l.51 1.27l1.36.03l-1.05.87l.4 1.31M19 13.5a5.5 5.5 0 0 1-5.5 5.5c-1.22 0-2.35-.4-3.26-1.07l7.69-7.69c.67.91 1.07 2.04 1.07 3.26m-4.4 6.58l2.77-1.15l-.24 3.35l-2.53-2.2m4.33-2.7l1.15-2.77l2.2 2.54l-3.35.23m1.15-4.96l-1.14-2.78l3.34.24l-2.2 2.54M9.63 18.93l2.77 1.15l-2.53 2.19l-.24-3.34z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
|
@ -3,3 +3,5 @@ export { default as IconCart } from './IconCart.vue'
|
|||
export { default as IconImagePlaceholder } from './IconImagePlaceholder.vue'
|
||||
export { default as IconLink } from './IconLink.vue'
|
||||
export { default as IconSpinner } from './IconSpinner.vue'
|
||||
export { default as IconNoGift } from './IconNoGift.vue'
|
||||
export { default as IconLightDark } from './IconLightDark.vue'
|
||||
|
|
|
@ -6,7 +6,7 @@ import { useRoute } from 'vue-router'
|
|||
import { useWishlistStore, useModal } from '@/composables'
|
||||
import Tile from '@/components/Tile.vue'
|
||||
import WishlistItem from '@/components/WishlistItem.vue'
|
||||
import IconNoGift from '../components/icons/IconNoGift.vue'
|
||||
import { IconNoGift } from '../components/icons'
|
||||
|
||||
const route = useRoute()
|
||||
const modal = useModal()
|
||||
|
@ -67,7 +67,7 @@ const bought = async (item: WishlistItemType): Promise<void> => {
|
|||
</div>
|
||||
<div v-else class="h-1/2 w-full flex justify-center">
|
||||
<div
|
||||
class="flex flex-wrap flex-col sm:flex-row items-center sm:space-x-2 justify-center text-center sm:text-left text-xl text-gray-600/75"
|
||||
class="flex flex-wrap flex-col sm:flex-row items-center sm:space-x-2 justify-center text-center sm:text-left text-xl text-gray-600/75 dark:text-white/70"
|
||||
>
|
||||
<IconNoGift class="h-10 w-10" />
|
||||
<span>{{ t('pages.detail-view.main.empty-list.text') }}</span>
|
||||
|
|
|
@ -9,18 +9,16 @@ await fetch()
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<h1 class="text-3xl text-center">{{ t('common.app-title.text') }}</h1>
|
||||
<h1 class="text-3xl text-semibold text-center">
|
||||
{{ t('common.app-title.text') }}
|
||||
</h1>
|
||||
<div v-if="lists" class="flex flex-row flex-wrap justify-around p-10">
|
||||
<router-link
|
||||
v-for="(item, index) in lists"
|
||||
:key="index"
|
||||
:to="'/' + item.slugUrlText"
|
||||
>
|
||||
<Tile
|
||||
:title="item.title"
|
||||
:image-src="item.imageSrc"
|
||||
class="m-4 hover:ring-2 ring-slate-500"
|
||||
/>
|
||||
<Tile :title="item.title" :image-src="item.imageSrc" class="m-4" />
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* eslint-disable no-undef */
|
||||
module.exports = {
|
||||
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
|
|
Loading…
Add table
Reference in a new issue