move items to route

This commit is contained in:
nicwands 2026-06-15 12:53:51 -04:00
parent 41c42d5880
commit a065a1328d
10 changed files with 138 additions and 85 deletions

4
.env
View file

@ -1,5 +1,5 @@
VITE_STRAPI_URL=http://localhost:1337 VITE_STRAPI_URL=http://localhost:1337
VITE_STRAPI_API_TOKEN=7e379ac0df1d055e91f1add3c97947068f60dc943a208524c7e10201c9d66210d6dee3ab48155581892e2cabe7a285d12395ace15a1f982e68fe5078abce0aebb826ae7c701302aa932d638ec57cb10f71fee7971e0a2b87a7fdf8eeb848eea90fb97d45fc8af69e818d1d5a59dc79cbc24f0bd1830d78670b85775ad6e05c6b VITE_STRAPI_API_TOKEN=7e379ac0df1d055e91f1add3c97947068f60dc943a208524c7e10201c9d66210d6dee3ab48155581892e2cabe7a285d12395ace15a1f982e68fe5078abce0aebb826ae7c701302aa932d638ec57cb10f71fee7971e0a2b87a7fdf8eeb848eea90fb97d45fc8af69e818d1d5a59dc79cbc24f0bd1830d78670b85775ad6e05c6b
VITE_STRAPI_URL=https://cms.nicwands.com # VITE_STRAPI_URL=https://cms.nicwands.com
VITE_STRAPI_API_TOKEN=5934fb84b89fdca49ef58e4f493381848289fcfb09a5664c5a61d4673a6b81b10bdf1d364fa250d185049c14487b7297758c3653dbf37390f567d6d08d559b4c40aaf4dc10730a60b98011d6dc0553f2bd633b373739579d579e39a162f67ca6d8f0e5c5adb3537a7e94b303d79c4cb7f3f877e39158e37e5bd6838596ee9afe # VITE_STRAPI_API_TOKEN=5934fb84b89fdca49ef58e4f493381848289fcfb09a5664c5a61d4673a6b81b10bdf1d364fa250d185049c14487b7297758c3653dbf37390f567d6d08d559b4c40aaf4dc10730a60b98011d6dc0553f2bd633b373739579d579e39a162f67ca6d8f0e5c5adb3537a7e94b303d79c4cb7f3f877e39158e37e5bd6838596ee9afe

View file

@ -2,11 +2,7 @@
<lenis root :options="{ duration: 1 }"> <lenis root :options="{ duration: 1 }">
<div :class="classes" :style="styles" id="container"> <div :class="classes" :style="styles" id="container">
<suspense> <suspense>
<site-header /> <layout />
</suspense>
<suspense>
<router-view />
</suspense> </suspense>
</div> </div>
</lenis> </lenis>
@ -16,7 +12,6 @@
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
import loadFonts from '@fuzzco/font-loader' import loadFonts from '@fuzzco/font-loader'
import { useWindowSize } from '@vueuse/core' import { useWindowSize } from '@vueuse/core'
import useLenis from './composables/useLenis'
const { height } = useWindowSize() const { height } = useWindowSize()

1
src/components.d.ts vendored
View file

@ -13,6 +13,7 @@ declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
DlRow: typeof import('./components/DlRow.vue')['default'] DlRow: typeof import('./components/DlRow.vue')['default']
InfiniteScrollContainer: typeof import('./components/InfiniteScrollContainer.vue')['default'] InfiniteScrollContainer: typeof import('./components/InfiniteScrollContainer.vue')['default']
Layout: typeof import('./components/Layout.vue')['default']
Lenis: typeof import('./components/Lenis.vue')['default'] Lenis: typeof import('./components/Lenis.vue')['default']
LoadingSpinner: typeof import('./components/LoadingSpinner.vue')['default'] LoadingSpinner: typeof import('./components/LoadingSpinner.vue')['default']
MosaicModal: typeof import('./components/MosaicModal.vue')['default'] MosaicModal: typeof import('./components/MosaicModal.vue')['default']

40
src/components/Layout.vue Normal file
View file

@ -0,0 +1,40 @@
<template>
<div v-if="global" class="layout">
<site-header :copy="headerCopy" :dl="headerDl" />
<mosaic-scroll :items="mosaicItems" />
<suspense>
<router-view />
</suspense>
</div>
</template async>
<script setup>
import { computed } from 'vue';
import { useSeoMeta } from '@unhead/vue'
import { useStrapiGlobal } from '@/composables/useStrapi'
const { data: global } = await useStrapiGlobal()
const headerCopy = computed(() => global.value?.header_copy)
const headerDl = computed(() => global.value?.header_dl)
const mosaicItems = computed(() => global.value?.mosaic_items || [])
// SEO
const seo = computed(() => global.value?.seo)
useSeoMeta({
title: seo.value?.title || '',
description: seo.value?.description || '',
ogImage: {
url: seo.value?.image?.url || '',
},
})
</script>
<style lang="scss">
.layout {
position: relative;
width: 100%;
}
</style>

View file

@ -1,10 +1,7 @@
<template> <template>
<div <div class="mosaic-modal theme-dark layout-grid-inner" @click="onClose">
class="mosaic-modal theme-dark layout-grid-inner"
@click="emit('close')"
>
<div class="meta"> <div class="meta">
<div class="window dl" @click.stop> <div v-if="item.dl?.length" class="window dl" @click.stop>
<dl-row v-for="row in item.dl" :row="row" /> <dl-row v-for="row in item.dl" :row="row" />
</div> </div>
@ -28,15 +25,17 @@
</lenis> </lenis>
</div> </div>
<button class="window close" @click="emit('close')">Close X</button> <button class="window close" @click="onClose">Close X</button>
</div> </div>
</template> </template>
<script setup> <script setup>
const props = defineProps({ item: Object }) import { useRouter } from 'vue-router'
console.log(props.item)
const emit = defineEmits(['close']) const props = defineProps({ item: Object })
const router = useRouter()
const onClose = () => router.push('/')
</script> </script>
<style lang="scss"> <style lang="scss">
@ -60,6 +59,7 @@ const emit = defineEmits(['close'])
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: desktop-vw(8px); gap: desktop-vw(8px);
margin-bottom: var(--layout-margin);
.dl-row:first-child { .dl-row:first-child {
border-top: none; border-top: none;
@ -67,7 +67,6 @@ const emit = defineEmits(['close'])
} }
} }
&.gif { &.gif {
margin-top: var(--layout-margin);
padding: 1rem; padding: 1rem;
text-align: center; text-align: center;

View file

@ -1,7 +1,7 @@
<template> <template>
<div class="mosaic-viewport"> <div class="mosaic-viewport">
<div class="mosaic-container"> <div class="mosaic-container">
<div <router-link
v-for="cell in content.cells" v-for="cell in content.cells"
:class="[ :class="[
'mosaic-cell', 'mosaic-cell',
@ -13,26 +13,20 @@
width: cell.width * 100 + 'vw', width: cell.width * 100 + 'vw',
height: cell.height * 100 + 'vh', height: cell.height * 100 + 'vh',
}" }"
:to="{
name: 'mosaic-item',
params: { id: cell.content.slug },
}"
:key="`${viewport.index}-${cell.id}`" :key="`${viewport.index}-${cell.id}`"
@click="openModal(cell.content)"
> >
<strapi-media <strapi-media
:image="cell.content.image" :image="cell.content.image"
:aspect="100" :aspect="100"
fit="contain" fit="contain"
/> />
</router-link>
</div> </div>
</div> </div>
</div>
<!-- Content modal -->
<teleport to="#container">
<mosaic-modal
v-if="selectedItem"
:item="selectedItem"
@close="closeModal"
/>
</teleport>
</template> </template>
<script setup> <script setup>
@ -52,47 +46,47 @@ const props = defineProps({
}, },
}) })
const route = useRoute() // const route = useRoute()
const router = useRouter() // const router = useRouter()
const selectedItem = computed(() => { // const selectedItem = computed(() => {
const { item } = route.query // const { item } = route.query
return props.content.cells.find((cell) => { // return props.content.cells.find((cell) => {
return cell?.content?.slug === item // return cell?.content?.slug === item
})?.content // })?.content
}) // })
const openModal = (cellContent) => { // const openModal = (cellContent) => {
router.push({ // router.push({
query: { // query: {
item: cellContent.slug, // item: cellContent.slug,
}, // },
}) // })
} // }
const closeModal = () => { // const closeModal = () => {
router.push({ // router.push({
query: null, // query: null,
}) // })
} // }
// Close note on Escape key // // Close note on Escape key
const handleKeydown = (event) => { // const handleKeydown = (event) => {
if (event.key === 'Escape' && selectedItem.value) { // if (event.key === 'Escape' && selectedItem.value) {
closeModal() // closeModal()
} // }
} // }
useEventListener('keydown', handleKeydown) // useEventListener('keydown', handleKeydown)
// Pause scroll when modal is open // // Pause scroll when modal is open
const lenis = useLenis() // const lenis = useLenis()
watch(selectedItem, (newItem, oldItem) => { // watch(selectedItem, (newItem, oldItem) => {
if (newItem && !oldItem) { // if (newItem && !oldItem) {
lenis.value.stop() // lenis.value.stop()
} else if (oldItem && !newItem) { // } else if (oldItem && !newItem) {
lenis.value.start() // lenis.value.start()
} // }
}) // })
</script> </script>
<style lang="scss"> <style lang="scss">

View file

@ -1,19 +1,20 @@
<template> <template>
<header v-if="data" class="site-header layout-grid-inner"> <header class="site-header layout-grid-inner">
<img class="logo" src="/images/output.gif" /> <img class="logo" src="/images/output.gif" />
<div class="info theme-red"> <div class="info theme-red">
<strapi-content class="copy" :content="data.header_copy" /> <strapi-content class="copy" :content="copy" />
<dl-row v-for="row in data.header_dl" :row="row" /> <dl-row v-for="row in dl" :row="row" />
</div> </div>
</header> </header>
</template> </template>
<script setup> <script setup>
import { useStrapiGlobal } from '@/composables/useStrapi' const props = defineProps({
copy: Array,
const { data } = await useStrapiGlobal() dl: Array,
})
</script> </script>
<style lang="scss"> <style lang="scss">

View file

@ -1,19 +1,8 @@
<template> <template>
<main class="home"> <main class="home"></main>
<mosaic-scroll :items="mosaicItems" />
</main>
</template> </template>
<script setup> <script setup></script>
import { useStrapiSingle } from '@/composables/useStrapi'
import useStrapiHead from '@/composables/useStrapiHead'
import { computed } from 'vue'
const { data } = await useStrapiSingle('home')
useStrapiHead()
const mosaicItems = computed(() => data.value?.mosaic_items || [])
</script>
<style lang="scss"> <style lang="scss">
main.home { main.home {

28
src/pages/mosaic-item.vue Normal file
View file

@ -0,0 +1,28 @@
<template>
<main class="mosaic-item">
<mosaic-modal :item="activeItem" />
</main>
</template>
<script setup>
import { useStrapiGlobal } from '@/composables/useStrapi'
import { computed } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const id = route.params.id
const { data: global } = await useStrapiGlobal()
const mosaicItems = computed(() => global.value?.mosaic_items || [])
const activeItem = computed(() =>
mosaicItems.value.find((item) => item.slug === id),
)
</script>
<style lang="scss">
main.mosaic-item {
position: relative;
width: 100%;
}
</style>

View file

@ -1,4 +1,5 @@
import index from './pages/index.vue' import index from './pages/index.vue'
import mosaicItem from './pages/mosaic-item.vue'
export default { export default {
routes: [ routes: [
@ -7,5 +8,10 @@ export default {
component: index, component: index,
name: 'home', name: 'home',
}, },
{
path: '/:id',
component: mosaicItem,
name: 'mosaic-item',
},
], ],
} }