init backend, added admin page in front

Signed-off-by: Nicolas Froger <nicolas@kektus.xyz>
This commit is contained in:
Nicolas Froger 2024-07-25 03:00:51 +02:00
commit ddc6c64f0f
No known key found for this signature in database
89 changed files with 5083 additions and 9 deletions

View file

@ -0,0 +1,46 @@
<script setup>
import { useProvideCarousel } from "./useCarousel";
import { cn } from "@/lib/utils";
const props = defineProps({
opts: { type: null, required: false },
plugins: { type: null, required: false },
orientation: { type: String, required: false, default: "horizontal" },
class: { type: null, required: false },
});
const emits = defineEmits(["init-api"]);
const carouselArgs = useProvideCarousel(props, emits);
defineExpose(carouselArgs);
function onKeyDown(event) {
const prevKey = props.orientation === "vertical" ? "ArrowUp" : "ArrowLeft";
const nextKey = props.orientation === "vertical" ? "ArrowDown" : "ArrowRight";
if (event.key === prevKey) {
event.preventDefault();
carouselArgs.scrollPrev();
return;
}
if (event.key === nextKey) {
event.preventDefault();
carouselArgs.scrollNext();
}
}
</script>
<template>
<div
:class="cn('relative', props.class)"
role="region"
aria-roledescription="carousel"
tabindex="0"
@keydown="onKeyDown"
>
<slot v-bind="carouselArgs" />
</div>
</template>

View file

@ -0,0 +1,31 @@
<script setup>
import { useCarousel } from "./useCarousel";
import { cn } from "@/lib/utils";
defineOptions({
inheritAttrs: false,
});
const props = defineProps({
class: { type: null, required: false },
});
const { carouselRef, orientation } = useCarousel();
</script>
<template>
<div ref="carouselRef" class="overflow-hidden">
<div
:class="
cn(
'flex',
orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',
props.class,
)
"
v-bind="$attrs"
>
<slot />
</div>
</div>
</template>

View file

@ -0,0 +1,26 @@
<script setup>
import { useCarousel } from "./useCarousel";
import { cn } from "@/lib/utils";
const props = defineProps({
class: { type: null, required: false },
});
const { orientation } = useCarousel();
</script>
<template>
<div
role="group"
aria-roledescription="slide"
:class="
cn(
'min-w-0 shrink-0 grow-0 basis-full',
orientation === 'horizontal' ? 'pl-4' : 'pt-4',
props.class,
)
"
>
<slot />
</div>
</template>

View file

@ -0,0 +1,34 @@
<script setup>
import { ArrowRight } from "lucide-vue-next";
import { useCarousel } from "./useCarousel";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
const props = defineProps({
class: { type: null, required: false },
});
const { orientation, canScrollNext, scrollNext } = useCarousel();
</script>
<template>
<Button
:disabled="!canScrollNext"
:class="
cn(
'touch-manipulation absolute h-8 w-8 rounded-full p-0',
orientation === 'horizontal'
? '-right-12 top-1/2 -translate-y-1/2'
: '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',
props.class,
)
"
variant="outline"
@click="scrollNext"
>
<slot>
<ArrowRight class="h-4 w-4 text-current" />
<span class="sr-only">Next Slide</span>
</slot>
</Button>
</template>

View file

@ -0,0 +1,34 @@
<script setup>
import { ArrowLeft } from "lucide-vue-next";
import { useCarousel } from "./useCarousel";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
const props = defineProps({
class: { type: null, required: false },
});
const { orientation, canScrollPrev, scrollPrev } = useCarousel();
</script>
<template>
<Button
:disabled="!canScrollPrev"
:class="
cn(
'touch-manipulation absolute h-8 w-8 rounded-full p-0',
orientation === 'horizontal'
? '-left-12 top-1/2 -translate-y-1/2'
: '-top-12 left-1/2 -translate-x-1/2 rotate-90',
props.class,
)
"
variant="outline"
@click="scrollPrev"
>
<slot>
<ArrowLeft class="h-4 w-4 text-current" />
<span class="sr-only">Previous Slide</span>
</slot>
</Button>
</template>

View file

@ -0,0 +1,6 @@
export { default as Carousel } from "./Carousel.vue";
export { default as CarouselContent } from "./CarouselContent.vue";
export { default as CarouselItem } from "./CarouselItem.vue";
export { default as CarouselPrevious } from "./CarouselPrevious.vue";
export { default as CarouselNext } from "./CarouselNext.vue";
export { useCarousel } from "./useCarousel";

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1,61 @@
import { createInjectionState } from "@vueuse/core";
import emblaCarouselVue from "embla-carousel-vue";
import { onMounted, ref } from "vue";
const [useProvideCarousel, useInjectCarousel] = createInjectionState(
({ opts, orientation, plugins }, emits) => {
const [emblaNode, emblaApi] = emblaCarouselVue(
{
...opts,
axis: orientation === "horizontal" ? "x" : "y",
},
plugins,
);
function scrollPrev() {
emblaApi.value?.scrollPrev();
}
function scrollNext() {
emblaApi.value?.scrollNext();
}
const canScrollNext = ref(false);
const canScrollPrev = ref(false);
function onSelect(api) {
canScrollNext.value = api?.canScrollNext() || false;
canScrollPrev.value = api?.canScrollPrev() || false;
}
onMounted(() => {
if (!emblaApi.value) return;
emblaApi.value?.on("init", onSelect);
emblaApi.value?.on("reInit", onSelect);
emblaApi.value?.on("select", onSelect);
emits("init-api", emblaApi.value);
});
return {
carouselRef: emblaNode,
carouselApi: emblaApi,
canScrollPrev,
canScrollNext,
scrollPrev,
scrollNext,
orientation,
};
},
);
function useCarousel() {
const carouselState = useInjectCarousel();
if (!carouselState)
throw new Error("useCarousel must be used within a <Carousel />");
return carouselState;
}
export { useCarousel, useProvideCarousel };