modal query string trigger
This commit is contained in:
parent
750af00e08
commit
499c23994b
15 changed files with 119 additions and 144 deletions
BIN
public/images/output.gif
Normal file
BIN
public/images/output.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 614 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB |
|
|
@ -2,7 +2,11 @@
|
||||||
<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>
|
||||||
<layout />
|
<site-header />
|
||||||
|
</suspense>
|
||||||
|
|
||||||
|
<suspense>
|
||||||
|
<router-view />
|
||||||
</suspense>
|
</suspense>
|
||||||
</div>
|
</div>
|
||||||
</lenis>
|
</lenis>
|
||||||
|
|
|
||||||
3
src/components.d.ts
vendored
3
src/components.d.ts
vendored
|
|
@ -11,11 +11,8 @@ export {}
|
||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
Dl: typeof import('./components/Dl.vue')['default']
|
|
||||||
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']
|
||||||
InfiniteScrollDemo: typeof import('./components/InfiniteScrollDemo.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']
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,7 @@
|
||||||
<slot
|
<slot
|
||||||
name="viewport"
|
name="viewport"
|
||||||
:viewport="viewport"
|
:viewport="viewport"
|
||||||
:index="viewport.index"
|
|
||||||
:content="viewport.content"
|
:content="viewport.content"
|
||||||
:isActive="viewport.index === currentViewportIndex"
|
|
||||||
>
|
>
|
||||||
<!-- Default viewport content -->
|
<!-- Default viewport content -->
|
||||||
<div class="default-viewport">
|
<div class="default-viewport">
|
||||||
|
|
@ -117,7 +115,7 @@ defineExpose({
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss">
|
||||||
.infinite-scroll-container {
|
.infinite-scroll-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="layout">
|
|
||||||
<suspense>
|
|
||||||
<site-header />
|
|
||||||
</suspense>
|
|
||||||
|
|
||||||
<main class="main-content">
|
|
||||||
<mosaic-scroll :items="mosaicItems" />
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
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" scoped>
|
|
||||||
.layout {
|
|
||||||
position: relative;
|
|
||||||
min-height: 100vh;
|
|
||||||
|
|
||||||
.main-content {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
|
||||||
background: var(--theme-bg);
|
|
||||||
color: var(--theme-fg);
|
|
||||||
|
|
||||||
code {
|
|
||||||
background: var(--grey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -8,8 +8,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="window content" @click.stop data-lenis-prevent>
|
<div class="window content" @click.stop data-lenis-prevent>
|
||||||
|
<lenis instance="mosaic-modal">
|
||||||
<h1 class="title">{{ item.title }}</h1>
|
<h1 class="title">{{ item.title }}</h1>
|
||||||
<strapi-content :content="item.content" />
|
<strapi-content :content="item.content" />
|
||||||
|
</lenis>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="window close" @click="emit('close')">Close X</button>
|
<button class="window close" @click="emit('close')">Close X</button>
|
||||||
|
|
@ -27,7 +29,7 @@ const emit = defineEmits(['close'])
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
padding-top: desktop-vh(27px);
|
padding-top: desktop-vw(27px);
|
||||||
|
|
||||||
.window {
|
.window {
|
||||||
background: var(--theme-bg);
|
background: var(--theme-bg);
|
||||||
|
|
@ -43,15 +45,18 @@ const emit = defineEmits(['close'])
|
||||||
|
|
||||||
.dl-row:first-child {
|
.dl-row:first-child {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.content {
|
&.content {
|
||||||
padding: desktop-vw(34px) desktop-vw(24px);
|
|
||||||
border-radius: desktop-vw(10px);
|
border-radius: desktop-vw(10px);
|
||||||
grid-column: 6 / span 7;
|
grid-column: 6 / span 7;
|
||||||
max-height: desktop-vh(879px);
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
|
.lenis {
|
||||||
|
max-height: desktop-vh(900px);
|
||||||
|
overflow: auto;
|
||||||
|
padding: desktop-vw(34px) desktop-vw(24px);
|
||||||
|
}
|
||||||
.title {
|
.title {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,8 @@
|
||||||
:initialIndex="0"
|
:initialIndex="0"
|
||||||
:generateContent="generateMosaicContent"
|
:generateContent="generateMosaicContent"
|
||||||
>
|
>
|
||||||
<template #viewport="{ viewport, content, isActive }">
|
<template #viewport="{ viewport, content }">
|
||||||
<mosaic-viewport
|
<mosaic-viewport :viewport="viewport" :content="content" />
|
||||||
:viewport="viewport"
|
|
||||||
:index="viewport.index"
|
|
||||||
:content="content"
|
|
||||||
:isActive="isActive"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</infinite-scroll-container>
|
</infinite-scroll-container>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -28,18 +23,6 @@ const props = defineProps({
|
||||||
// Initialize mosaic layout generator
|
// Initialize mosaic layout generator
|
||||||
const { generateLayout, populateCells } = useMosaicLayout()
|
const { generateLayout, populateCells } = useMosaicLayout()
|
||||||
|
|
||||||
// const generateGifContent = (cell) => {
|
|
||||||
// return {
|
|
||||||
// id: `gif-${cell.id}`,
|
|
||||||
// width: Math.floor(cell.width),
|
|
||||||
// height: Math.floor(cell.height),
|
|
||||||
// note: _shuffle(
|
|
||||||
// 'curabitur euismod ultrices porttitor. Vivamus magna sapien, pretium at ullamcorper semper, sollicitudin et magna curabitur eget dolor a neque facilisis semper pellentesque euismod luctus urna, non aliquam justo aliquet id vestibulum quis lobortis diam nulla facilisi phasellus et rutrum lectus vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent mattis felis condimentum, gravida lacus id, pretium neque. Donec cursus augue a dui placerat porttitor.Vestibulum rhoncus lectus nisl, nec gravida ex sollicitudin vel etiam dictum ac lacus ac elementum donec turpis eros, aliquam in nibh a, posuere tristique neque. Nunc cursus molestie consectetur.Integer placerat lorem eu nunc semper porta.'.split(
|
|
||||||
// ' ',
|
|
||||||
// ),
|
|
||||||
// ).join(' '),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
const generateCellContent = () => {
|
const generateCellContent = () => {
|
||||||
const itemIndex = Math.floor(Math.random() * props.items.length)
|
const itemIndex = Math.floor(Math.random() * props.items.length)
|
||||||
return props.items[itemIndex]
|
return props.items[itemIndex]
|
||||||
|
|
@ -53,8 +36,6 @@ const generateMosaicContent = async (index) => {
|
||||||
const targetCells = baseCellCount + variance
|
const targetCells = baseCellCount + variance
|
||||||
|
|
||||||
const layoutOptions = {
|
const layoutOptions = {
|
||||||
minCellWidth: 200,
|
|
||||||
minCellHeight: 200,
|
|
||||||
targetCells: targetCells,
|
targetCells: targetCells,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,7 +43,7 @@ const generateMosaicContent = async (index) => {
|
||||||
const cells = generateLayout(layoutOptions)
|
const cells = generateLayout(layoutOptions)
|
||||||
|
|
||||||
// Populate cells with GIF content
|
// Populate cells with GIF content
|
||||||
const populatedCells = populateCells(cells, generateCellContent)
|
const populatedCells = populateCells(cells.value, generateCellContent)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cells: populatedCells,
|
cells: populatedCells,
|
||||||
|
|
@ -70,7 +51,7 @@ const generateMosaicContent = async (index) => {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss">
|
||||||
.mosaic-scroll {
|
.mosaic-scroll {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mosaic-viewport" :class="{ active: isActive }">
|
<div class="mosaic-viewport">
|
||||||
<div class="mosaic-container">
|
<div class="mosaic-container">
|
||||||
<div
|
<div
|
||||||
v-for="cell in content.cells"
|
v-for="cell in content.cells"
|
||||||
|
|
@ -8,10 +8,10 @@
|
||||||
`layout-${cell.width >= cell.height ? 'landscape' : 'portrait'}`,
|
`layout-${cell.width >= cell.height ? 'landscape' : 'portrait'}`,
|
||||||
]"
|
]"
|
||||||
:style="{
|
:style="{
|
||||||
left: cell.x + 'px',
|
left: cell.x * 100 + 'vw',
|
||||||
top: cell.y + 'px',
|
top: cell.y * 100 + 'vh',
|
||||||
width: cell.width + 'px',
|
width: cell.width * 100 + 'vw',
|
||||||
height: cell.height + 'px',
|
height: cell.height * 100 + 'vh',
|
||||||
}"
|
}"
|
||||||
:key="`${viewport.index}-${cell.id}`"
|
:key="`${viewport.index}-${cell.id}`"
|
||||||
@click="openModal(cell.content)"
|
@click="openModal(cell.content)"
|
||||||
|
|
@ -36,37 +36,45 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import { useEventListener } from '@vueuse/core'
|
import { useEventListener } from '@vueuse/core'
|
||||||
import useLenis from '@/composables/useLenis'
|
import useLenis from '@/composables/useLenis'
|
||||||
|
import _kebabCase from 'lodash/kebabCase'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
viewport: {
|
viewport: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
index: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
content: {
|
content: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
isActive: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectedItem = ref(null)
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const openModal = (cell) => {
|
const selectedItem = computed(() => {
|
||||||
selectedItem.value = cell
|
const { item } = route.query
|
||||||
|
|
||||||
|
return props.content.cells.find((cell) => {
|
||||||
|
return _kebabCase(cell?.content?.title || '') === item
|
||||||
|
})?.content
|
||||||
|
})
|
||||||
|
|
||||||
|
const openModal = (cellContent) => {
|
||||||
|
router.push({
|
||||||
|
query: {
|
||||||
|
item: _kebabCase(cellContent.title),
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
selectedItem.value = null
|
router.push({
|
||||||
|
query: null,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close note on Escape key
|
// Close note on Escape key
|
||||||
|
|
@ -88,7 +96,7 @@ watch(selectedItem, (newItem, oldItem) => {
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss">
|
||||||
.mosaic-viewport {
|
.mosaic-viewport {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -110,12 +118,12 @@ watch(selectedItem, (newItem, oldItem) => {
|
||||||
|
|
||||||
&.layout-portrait {
|
&.layout-portrait {
|
||||||
.strapi-media {
|
.strapi-media {
|
||||||
width: 50%;
|
width: 40%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.layout-landscape {
|
&.layout-landscape {
|
||||||
.strapi-media {
|
.strapi-media {
|
||||||
height: 50%;
|
height: 40%;
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<header v-if="data" class="site-header layout-grid-inner">
|
<header v-if="data" class="site-header layout-grid-inner">
|
||||||
<img class="logo" src="/images/output_pixelated.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="data.header_copy" />
|
||||||
|
|
@ -23,7 +23,7 @@ const { data } = await useStrapiGlobal()
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
padding: desktop-vw(27px);
|
padding: desktop-vw(27px) var(--layout-margin);
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
|
|
@ -33,6 +33,7 @@ const { data } = await useStrapiGlobal()
|
||||||
.logo {
|
.logo {
|
||||||
width: desktop-vw(93px);
|
width: desktop-vw(93px);
|
||||||
height: auto;
|
height: auto;
|
||||||
|
filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import { useWindowSize } from '@vueuse/core'
|
import { useWindowSize } from '@vueuse/core'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { viewports } from '@/libs/theme'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mosaic Layout Generator
|
* Mosaic Layout Generator
|
||||||
|
|
@ -8,6 +10,9 @@ import { useWindowSize } from '@vueuse/core'
|
||||||
const { width: wWidth, height: wHeight } = useWindowSize()
|
const { width: wWidth, height: wHeight } = useWindowSize()
|
||||||
|
|
||||||
export const useMosaicLayout = () => {
|
export const useMosaicLayout = () => {
|
||||||
|
const dvw = (pixels) => (pixels / viewports.desktop.width) * wWidth.value
|
||||||
|
const dvh = (pixels) => (pixels / viewports.desktop.height) * wHeight.value
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a space-filling mosaic layout
|
* Generate a space-filling mosaic layout
|
||||||
* @param {Object} options Layout configuration
|
* @param {Object} options Layout configuration
|
||||||
|
|
@ -19,16 +24,17 @@ export const useMosaicLayout = () => {
|
||||||
* @param {number} options.maxCellHeight Maximum cell height
|
* @param {number} options.maxCellHeight Maximum cell height
|
||||||
* @param {number} options.targetCells Target number of cells to create (if specified, overrides subdivisionProbability)
|
* @param {number} options.targetCells Target number of cells to create (if specified, overrides subdivisionProbability)
|
||||||
* @param {number} options.subdivisionProbability Probability of subdivision (0-1) - used when targetCells is not specified
|
* @param {number} options.subdivisionProbability Probability of subdivision (0-1) - used when targetCells is not specified
|
||||||
* @returns {Array} Array of cell objects with x, y, width, height, id
|
* @returns {ComputedRef} Computed ref of array of cell objects with x, y, width, height, id
|
||||||
*/
|
*/
|
||||||
const generateLayout = (options = {}) => {
|
const generateLayout = (options = {}) => {
|
||||||
|
return computed(() => {
|
||||||
const {
|
const {
|
||||||
width = wWidth.value,
|
width = wWidth.value,
|
||||||
height = wHeight.value,
|
height = wHeight.value,
|
||||||
minCellWidth = 150,
|
minCellWidth = dvw(150),
|
||||||
minCellHeight = 150,
|
minCellHeight = dvh(150),
|
||||||
targetCells = 5,
|
targetCells = 5,
|
||||||
} = options
|
} = typeof options === 'function' ? options() : options
|
||||||
|
|
||||||
return generateLayoutWithCellCount(
|
return generateLayoutWithCellCount(
|
||||||
width,
|
width,
|
||||||
|
|
@ -37,6 +43,7 @@ export const useMosaicLayout = () => {
|
||||||
minCellWidth,
|
minCellWidth,
|
||||||
minCellHeight,
|
minCellHeight,
|
||||||
)
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -92,13 +99,7 @@ export const useMosaicLayout = () => {
|
||||||
let splitVertical = true
|
let splitVertical = true
|
||||||
if (canSubdivideWidth && canSubdivideHeight) {
|
if (canSubdivideWidth && canSubdivideHeight) {
|
||||||
const aspectRatio = areaToSplit.width / areaToSplit.height
|
const aspectRatio = areaToSplit.width / areaToSplit.height
|
||||||
if (aspectRatio > 1.5) {
|
splitVertical = aspectRatio > 1
|
||||||
splitVertical = true // Split wide areas vertically
|
|
||||||
} else if (aspectRatio < 0.67) {
|
|
||||||
splitVertical = false // Split tall areas horizontally
|
|
||||||
} else {
|
|
||||||
splitVertical = Math.random() > 0.5 // Random for square-ish areas
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
splitVertical = canSubdivideWidth
|
splitVertical = canSubdivideWidth
|
||||||
}
|
}
|
||||||
|
|
@ -157,13 +158,13 @@ export const useMosaicLayout = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert areas to cells
|
// Convert areas to cells, normalise to 0-1 so consumers can use vw/vh
|
||||||
return areas.map((area) => ({
|
return areas.map((area) => ({
|
||||||
id: area.id,
|
id: area.id,
|
||||||
x: area.x,
|
x: area.x / width,
|
||||||
y: area.y,
|
y: area.y / height,
|
||||||
width: area.width,
|
width: area.width / width,
|
||||||
height: area.height,
|
height: area.height / height,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,6 @@ import useLenis from '@/composables/useLenis'
|
||||||
|
|
||||||
const { height: wHeight } = useWindowSize()
|
const { height: wHeight } = useWindowSize()
|
||||||
|
|
||||||
export const useScrollProgress = (el, callback, entry = 0.5, exit = 0.5) => {
|
|
||||||
const isActive = ref(true)
|
|
||||||
const smoothProgress = ref(0)
|
|
||||||
|
|
||||||
const { height, top } = useElementBounding(el)
|
const { height, top } = useElementBounding(el)
|
||||||
|
|
||||||
const isIntersected = ref(false)
|
const isIntersected = ref(false)
|
||||||
|
|
@ -20,10 +16,6 @@ export const useScrollProgress = (el, callback, entry = 0.5, exit = 0.5) => {
|
||||||
isIntersected.value = isIntersecting
|
isIntersected.value = isIntersecting
|
||||||
})
|
})
|
||||||
|
|
||||||
useLenis(({ scroll }) => {
|
|
||||||
if (!isActive.value) return
|
|
||||||
if (!height.value || !wHeight.value) return
|
|
||||||
if (!isIntersected.value) return
|
|
||||||
|
|
||||||
const pageTop = scroll + top.value
|
const pageTop = scroll + top.value
|
||||||
|
|
||||||
|
|
@ -37,10 +29,6 @@ export const useScrollProgress = (el, callback, entry = 0.5, exit = 0.5) => {
|
||||||
callback?.(smoothProgress.value)
|
callback?.(smoothProgress.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const destroy = () => {
|
|
||||||
isActive.value = false
|
|
||||||
stop?.()
|
|
||||||
}
|
|
||||||
|
|
||||||
return { destroy }
|
return { destroy }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import './styles/main.scss'
|
import './styles/main.scss'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import { ViteSSG } from 'vite-ssg/single-page'
|
import { ViteSSG } from 'vite-ssg'
|
||||||
|
import routes from './routes'
|
||||||
import strapi from './plugins/strapi'
|
import strapi from './plugins/strapi'
|
||||||
|
|
||||||
export const createApp = ViteSSG(App, ({ app }) => {
|
export const createApp = ViteSSG(App, routes, ({ app }) => {
|
||||||
// Plugins
|
// Plugins
|
||||||
app.use(strapi)
|
app.use(strapi)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
23
src/pages/index.vue
Normal file
23
src/pages/index.vue
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<template>
|
||||||
|
<main class="home">
|
||||||
|
<mosaic-scroll :items="mosaicItems" />
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
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">
|
||||||
|
main.home {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
11
src/routes.js
Normal file
11
src/routes.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import index from './pages/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: index,
|
||||||
|
name: 'home',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue