- gestione dell'editor delle pagine (non funzionante!)

This commit is contained in:
Surya Paolo
2025-09-16 17:30:28 +02:00
parent cb3baf3dbb
commit 95fa0b9ac0
63 changed files with 1647 additions and 2737 deletions

View File

@@ -0,0 +1,15 @@
.cmy-image-gallery {
&.layout-grid {
.gallery-grid { display: grid; }
.gallery-item {
cursor: pointer;
.gallery-img { border-radius: 10px; overflow: hidden; }
.gallery-caption { font-size: 0.9rem; opacity: 0.8; margin-top: 6px; }
}
}
&.layout-carousel {
.carousel-img { max-width: 100%; border-radius: 12px; }
}
.lightbox-card { width: min(92vw, 1100px); }
.lightbox-img { max-height: 76vh; object-fit: contain; }
}

View File

@@ -0,0 +1,59 @@
import { defineComponent, ref, computed, type Ref } from 'vue';
export type GalleryImage = {
src: string;
alt?: string;
caption?: string;
};
export default defineComponent({
name: 'CMyImageGallery',
props: {
images: { type: Array as () => GalleryImage[], required: true },
layout: { type: String as () => 'grid' | 'carousel', default: 'grid' },
cols: { type: Number, default: 3 },
gap: { type: Number, default: 12 },
ratio: { type: Number, default: 1 },
lightbox: { type: Boolean, default: false }
},
emits: ['imageClick'],
setup(props, { emit }) {
const slide: Ref<number> = ref(0);
const lightboxOpen = ref(false);
const currentIndex = ref(0);
const gridStyle = computed(() => ({
gridTemplateColumns: `repeat(${props.cols}, 1fr)`,
gap: props.gap + 'px'
}));
const currentImage = computed(() => props.images?.[currentIndex.value] ?? null);
function onImageClick(idx: number) {
emit('imageClick', idx, props.images[idx]);
if (props.lightbox) {
currentIndex.value = idx;
lightboxOpen.value = true;
}
}
function next() {
if (!props.images?.length) return;
currentIndex.value = (currentIndex.value + 1) % props.images.length;
}
function prev() {
if (!props.images?.length) return;
currentIndex.value = (currentIndex.value - 1 + props.images.length) % props.images.length;
}
return {
slide,
lightboxOpen,
currentIndex,
currentImage,
gridStyle,
onImageClick,
next,
prev
};
}
});

View File

@@ -0,0 +1,67 @@
<template>
<div class="cmy-image-gallery" :class="[`layout-${layout}`]">
<!-- GRID -->
<div v-if="layout === 'grid'" class="gallery-grid" :style="gridStyle">
<div
v-for="(img, idx) in images"
:key="idx"
class="gallery-item"
@click="onImageClick(idx)"
role="button"
tabindex="0"
>
<q-img :src="img.src" :alt="img.alt || ''" :ratio="ratio" class="gallery-img" />
<div v-if="img.caption" class="gallery-caption">{{ img.caption }}</div>
</div>
</div>
<!-- CAROUSEL -->
<q-carousel
v-else
v-model="slide"
animated
arrows
swipeable
infinite
class="gallery-carousel"
height="auto"
>
<q-carousel-slide
v-for="(img, idx) in images"
:key="idx"
:name="idx"
class="column items-center q-pa-md"
>
<q-img :src="img.src" :alt="img.alt || ''" :ratio="ratio" class="carousel-img" />
<div v-if="img.caption" class="gallery-caption q-mt-sm">{{ img.caption }}</div>
</q-carousel-slide>
</q-carousel>
<!-- Lightbox -->
<q-dialog v-model="lightboxOpen" persistent>
<q-card class="lightbox-card">
<q-card-section class="row items-center justify-between">
<div class="text-subtitle1">{{ currentImage?.caption }}</div>
<q-btn flat round icon="close" @click="lightboxOpen = false" />
</q-card-section>
<q-separator />
<q-card-section class="q-pa-none">
<q-img :src="currentImage?.src" :alt="currentImage?.alt || ''" class="lightbox-img" />
</q-card-section>
<q-card-actions align="between">
<q-btn flat icon="chevron_left" @click="prev" />
<q-btn flat icon="chevron_right" @click="next" />
</q-card-actions>
</q-card>
</q-dialog>
</div>
</template>
<script lang="ts" src="./CMyImageGallery.ts">
</script>
<style lang="scss" scoped>
@import './CMyImageGallery.scss';
</style>

View File

@@ -0,0 +1 @@
export {default as CMyImageGallery} from './CMyImageGallery.vue'