From 05d7619cc1864faf8948da6ef9113c4c92d524ab Mon Sep 17 00:00:00 2001 From: Croos3r <dorian.moy@epitech.eu> Date: Thu, 18 Jul 2024 11:19:35 +0200 Subject: [PATCH] feat(redirection-warning): added LLink component to proxy every link by a warning if it goes outside of the current site --- src/components/LLink.vue | 44 +++++++++++++++++++ .../ui/alert-dialog/AlertDialog.vue | 14 ++++++ .../ui/alert-dialog/AlertDialogAction.vue | 20 +++++++++ .../ui/alert-dialog/AlertDialogCancel.vue | 22 ++++++++++ .../ui/alert-dialog/AlertDialogContent.vue | 42 ++++++++++++++++++ .../alert-dialog/AlertDialogDescription.vue | 25 +++++++++++ .../ui/alert-dialog/AlertDialogFooter.vue | 21 +++++++++ .../ui/alert-dialog/AlertDialogHeader.vue | 16 +++++++ .../ui/alert-dialog/AlertDialogTitle.vue | 22 ++++++++++ .../ui/alert-dialog/AlertDialogTrigger.vue | 11 +++++ src/components/ui/alert-dialog/index.ts | 9 ++++ src/components/ui/button/Button.vue | 26 +++++++++++ src/components/ui/button/index.ts | 35 +++++++++++++++ 13 files changed, 307 insertions(+) create mode 100644 src/components/LLink.vue create mode 100644 src/components/ui/alert-dialog/AlertDialog.vue create mode 100644 src/components/ui/alert-dialog/AlertDialogAction.vue create mode 100644 src/components/ui/alert-dialog/AlertDialogCancel.vue create mode 100644 src/components/ui/alert-dialog/AlertDialogContent.vue create mode 100644 src/components/ui/alert-dialog/AlertDialogDescription.vue create mode 100644 src/components/ui/alert-dialog/AlertDialogFooter.vue create mode 100644 src/components/ui/alert-dialog/AlertDialogHeader.vue create mode 100644 src/components/ui/alert-dialog/AlertDialogTitle.vue create mode 100644 src/components/ui/alert-dialog/AlertDialogTrigger.vue create mode 100644 src/components/ui/alert-dialog/index.ts create mode 100644 src/components/ui/button/Button.vue create mode 100644 src/components/ui/button/index.ts diff --git a/src/components/LLink.vue b/src/components/LLink.vue new file mode 100644 index 0000000..460a41b --- /dev/null +++ b/src/components/LLink.vue @@ -0,0 +1,44 @@ +<script lang="ts" setup> +import { LinkHTMLAttributes, ref } from "vue"; +import { AlertDialog, AlertDialogContent, AlertDialogTrigger } from "@/components/ui/alert-dialog"; +import { AlertDialogOverlay, AlertDialogPortal } from "radix-vue"; +import LButton from "@/components/LButton.vue"; + +interface IProps extends /* @vue-ignore */ LinkHTMLAttributes {} + +withDefaults(defineProps<IProps>(), {}); + +const open = ref(false); +</script> + +<template> + <RouterLink v-if="href && href.startsWith('/')" :to="href"> + <slot /> + </RouterLink> + <AlertDialog v-model:open="open"> + <AlertDialogTrigger as-child> + <a> + <slot /> + </a> + </AlertDialogTrigger> + <AlertDialogPortal> + <AlertDialogOverlay class="fixed inset-0 z-30 backdrop-blur-sm" /> + <AlertDialogContent class="flex flex-col gap-y-6 bg-primary px-6 py-9 text-neutral-white"> + <h6 class="body-default text-center font-bold">Leaving our website</h6> + <p class="body-tiny text-center"> + You will be redirected to an external website <strong class="font-bold">({{ $attrs.href }})</strong> that is + not under our control. We cannot guarantee the accuracy, security, or reliability of the information provided + on these external sites. + </p> + <div class="gap-x- flex justify-center gap-x-3"> + <a v-bind="$attrs"> + <LButton class="w-[150px]" light text="Continue" @click="open = false" /> + </a> + <LButton class="w-[150px]" light text="Cancel" @click="open = false" /> + </div> + </AlertDialogContent> + </AlertDialogPortal> + </AlertDialog> +</template> + +<style scoped></style> diff --git a/src/components/ui/alert-dialog/AlertDialog.vue b/src/components/ui/alert-dialog/AlertDialog.vue new file mode 100644 index 0000000..8fb30de --- /dev/null +++ b/src/components/ui/alert-dialog/AlertDialog.vue @@ -0,0 +1,14 @@ +<script setup lang="ts"> +import { type AlertDialogEmits, type AlertDialogProps, AlertDialogRoot, useForwardPropsEmits } from 'radix-vue' + +const props = defineProps<AlertDialogProps>() +const emits = defineEmits<AlertDialogEmits>() + +const forwarded = useForwardPropsEmits(props, emits) +</script> + +<template> + <AlertDialogRoot v-bind="forwarded"> + <slot /> + </AlertDialogRoot> +</template> diff --git a/src/components/ui/alert-dialog/AlertDialogAction.vue b/src/components/ui/alert-dialog/AlertDialogAction.vue new file mode 100644 index 0000000..092d6cd --- /dev/null +++ b/src/components/ui/alert-dialog/AlertDialogAction.vue @@ -0,0 +1,20 @@ +<script setup lang="ts"> +import { type HTMLAttributes, computed } from 'vue' +import { AlertDialogAction, type AlertDialogActionProps } from 'radix-vue' +import { cn } from '@/lib/utils' +import { buttonVariants } from '@/components/ui/button' + +const props = defineProps<AlertDialogActionProps & { class?: HTMLAttributes['class'] }>() + +const delegatedProps = computed(() => { + const { class: _, ...delegated } = props + + return delegated +}) +</script> + +<template> + <AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)"> + <slot /> + </AlertDialogAction> +</template> diff --git a/src/components/ui/alert-dialog/AlertDialogCancel.vue b/src/components/ui/alert-dialog/AlertDialogCancel.vue new file mode 100644 index 0000000..907f24c --- /dev/null +++ b/src/components/ui/alert-dialog/AlertDialogCancel.vue @@ -0,0 +1,22 @@ +<script lang="ts" setup> +import { computed, type HTMLAttributes } from "vue"; +import { AlertDialogCancel, type AlertDialogCancelProps } from "radix-vue"; +import { cn } from "@/lib/utils"; +import { buttonVariants } from "@/components/ui/button"; + +const props = defineProps<AlertDialogCancelProps & { class?: HTMLAttributes["class"] }>(); + +const delegatedProps = computed(() => { + const { class: _, ...delegated } = props; + return delegated; +}); +</script> + +<template> + <AlertDialogCancel + :class="cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', props.class)" + v-bind="delegatedProps" + > + <slot /> + </AlertDialogCancel> +</template> diff --git a/src/components/ui/alert-dialog/AlertDialogContent.vue b/src/components/ui/alert-dialog/AlertDialogContent.vue new file mode 100644 index 0000000..93ef36f --- /dev/null +++ b/src/components/ui/alert-dialog/AlertDialogContent.vue @@ -0,0 +1,42 @@ +<script lang="ts" setup> +import { computed, type HTMLAttributes } from "vue"; +import { + AlertDialogContent, + type AlertDialogContentEmits, + type AlertDialogContentProps, + AlertDialogOverlay, + AlertDialogPortal, + useForwardPropsEmits, +} from "radix-vue"; +import { cn } from "@/lib/utils"; + +const props = defineProps<AlertDialogContentProps & { class?: HTMLAttributes["class"] }>(); +const emits = defineEmits<AlertDialogContentEmits>(); + +const delegatedProps = computed(() => { + const { class: _, ...delegated } = props; + + return delegated; +}); + +const forwarded = useForwardPropsEmits(delegatedProps, emits); +</script> + +<template> + <AlertDialogPortal> + <AlertDialogOverlay + class="bg-black/80 fixed inset-0 z-50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" + /> + <AlertDialogContent + :class=" + cn( + 'fixed left-1/2 top-1/2 z-50 grid w-[45vw] max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg', + props.class, + ) + " + v-bind="forwarded" + > + <slot /> + </AlertDialogContent> + </AlertDialogPortal> +</template> diff --git a/src/components/ui/alert-dialog/AlertDialogDescription.vue b/src/components/ui/alert-dialog/AlertDialogDescription.vue new file mode 100644 index 0000000..9682cbb --- /dev/null +++ b/src/components/ui/alert-dialog/AlertDialogDescription.vue @@ -0,0 +1,25 @@ +<script setup lang="ts"> +import { type HTMLAttributes, computed } from 'vue' +import { + AlertDialogDescription, + type AlertDialogDescriptionProps, +} from 'radix-vue' +import { cn } from '@/lib/utils' + +const props = defineProps<AlertDialogDescriptionProps & { class?: HTMLAttributes['class'] }>() + +const delegatedProps = computed(() => { + const { class: _, ...delegated } = props + + return delegated +}) +</script> + +<template> + <AlertDialogDescription + v-bind="delegatedProps" + :class="cn('text-sm text-muted-foreground', props.class)" + > + <slot /> + </AlertDialogDescription> +</template> diff --git a/src/components/ui/alert-dialog/AlertDialogFooter.vue b/src/components/ui/alert-dialog/AlertDialogFooter.vue new file mode 100644 index 0000000..55d0a0e --- /dev/null +++ b/src/components/ui/alert-dialog/AlertDialogFooter.vue @@ -0,0 +1,21 @@ +<script setup lang="ts"> +import type { HTMLAttributes } from 'vue' +import { cn } from '@/lib/utils' + +const props = defineProps<{ + class?: HTMLAttributes['class'] +}>() +</script> + +<template> + <div + :class=" + cn( + 'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2', + props.class, + ) + " + > + <slot /> + </div> +</template> diff --git a/src/components/ui/alert-dialog/AlertDialogHeader.vue b/src/components/ui/alert-dialog/AlertDialogHeader.vue new file mode 100644 index 0000000..c61c449 --- /dev/null +++ b/src/components/ui/alert-dialog/AlertDialogHeader.vue @@ -0,0 +1,16 @@ +<script setup lang="ts"> +import type { HTMLAttributes } from 'vue' +import { cn } from '@/lib/utils' + +const props = defineProps<{ + class?: HTMLAttributes['class'] +}>() +</script> + +<template> + <div + :class="cn('flex flex-col gap-y-2 text-center sm:text-left', props.class)" + > + <slot /> + </div> +</template> diff --git a/src/components/ui/alert-dialog/AlertDialogTitle.vue b/src/components/ui/alert-dialog/AlertDialogTitle.vue new file mode 100644 index 0000000..50c583d --- /dev/null +++ b/src/components/ui/alert-dialog/AlertDialogTitle.vue @@ -0,0 +1,22 @@ +<script setup lang="ts"> +import { type HTMLAttributes, computed } from 'vue' +import { AlertDialogTitle, type AlertDialogTitleProps } from 'radix-vue' +import { cn } from '@/lib/utils' + +const props = defineProps<AlertDialogTitleProps & { class?: HTMLAttributes['class'] }>() + +const delegatedProps = computed(() => { + const { class: _, ...delegated } = props + + return delegated +}) +</script> + +<template> + <AlertDialogTitle + v-bind="delegatedProps" + :class="cn('text-lg font-semibold', props.class)" + > + <slot /> + </AlertDialogTitle> +</template> diff --git a/src/components/ui/alert-dialog/AlertDialogTrigger.vue b/src/components/ui/alert-dialog/AlertDialogTrigger.vue new file mode 100644 index 0000000..4f5e2fd --- /dev/null +++ b/src/components/ui/alert-dialog/AlertDialogTrigger.vue @@ -0,0 +1,11 @@ +<script setup lang="ts"> +import { AlertDialogTrigger, type AlertDialogTriggerProps } from 'radix-vue' + +const props = defineProps<AlertDialogTriggerProps>() +</script> + +<template> + <AlertDialogTrigger v-bind="props"> + <slot /> + </AlertDialogTrigger> +</template> diff --git a/src/components/ui/alert-dialog/index.ts b/src/components/ui/alert-dialog/index.ts new file mode 100644 index 0000000..91d138a --- /dev/null +++ b/src/components/ui/alert-dialog/index.ts @@ -0,0 +1,9 @@ +export { default as AlertDialog } from './AlertDialog.vue' +export { default as AlertDialogTrigger } from './AlertDialogTrigger.vue' +export { default as AlertDialogContent } from './AlertDialogContent.vue' +export { default as AlertDialogHeader } from './AlertDialogHeader.vue' +export { default as AlertDialogTitle } from './AlertDialogTitle.vue' +export { default as AlertDialogDescription } from './AlertDialogDescription.vue' +export { default as AlertDialogFooter } from './AlertDialogFooter.vue' +export { default as AlertDialogAction } from './AlertDialogAction.vue' +export { default as AlertDialogCancel } from './AlertDialogCancel.vue' diff --git a/src/components/ui/button/Button.vue b/src/components/ui/button/Button.vue new file mode 100644 index 0000000..5cfd668 --- /dev/null +++ b/src/components/ui/button/Button.vue @@ -0,0 +1,26 @@ +<script setup lang="ts"> +import type { HTMLAttributes } from 'vue' +import { Primitive, type PrimitiveProps } from 'radix-vue' +import { type ButtonVariants, buttonVariants } from '.' +import { cn } from '@/lib/utils' + +interface Props extends PrimitiveProps { + variant?: ButtonVariants['variant'] + size?: ButtonVariants['size'] + class?: HTMLAttributes['class'] +} + +const props = withDefaults(defineProps<Props>(), { + as: 'button', +}) +</script> + +<template> + <Primitive + :as="as" + :as-child="asChild" + :class="cn(buttonVariants({ variant, size }), props.class)" + > + <slot /> + </Primitive> +</template> diff --git a/src/components/ui/button/index.ts b/src/components/ui/button/index.ts new file mode 100644 index 0000000..1b00c32 --- /dev/null +++ b/src/components/ui/button/index.ts @@ -0,0 +1,35 @@ +import { type VariantProps, cva } from 'class-variance-authority' + +export { default as Button } from './Button.vue' + +export const buttonVariants = cva( + 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground hover:bg-primary/90', + destructive: + 'bg-destructive text-destructive-foreground hover:bg-destructive/90', + outline: + 'border border-input bg-background hover:bg-accent hover:text-accent-foreground', + secondary: + 'bg-secondary text-secondary-foreground hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'text-primary underline-offset-4 hover:underline', + }, + size: { + default: 'h-10 px-4 py-2', + xs: 'h-7 rounded px-2', + sm: 'h-9 rounded-md px-3', + lg: 'h-11 rounded-md px-8', + icon: 'h-10 w-10', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + }, +) + +export type ButtonVariants = VariantProps<typeof buttonVariants>