feat: Ajout de composant (#23)
All checks were successful
Release / release (push) Successful in 1m14s

| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

- [x] Pas de régression
- [x] TU/TI/TF rédigée
- [x] TU/TI/TF OK
- [x] CHANGELOG modifié

Co-authored-by: kevin <kevin@yuno.malio.fr>
Co-authored-by: Kevin Boudet <kevin@yuno.malio.fr>
Reviewed-on: #23
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #23.
This commit is contained in:
2026-03-26 07:40:04 +00:00
committed by Autin
parent 898bc0f761
commit 82c4cfaa90
78 changed files with 8700 additions and 155 deletions

View File

@@ -0,0 +1,112 @@
<script setup lang="ts">
</script>
<template>
<div class="grid grid-cols-1 items-start gap-6 md:grid-cols-2">
<div class="rounded-lg border p-6">
<h2 class="mb-6 text-xl font-bold">Primary</h2>
<div class="grid grid-cols-2 items-start gap-3">
<p class="text-xs font-semibold text-m-muted">Default</p>
<p class="text-xs font-semibold text-m-muted">Default</p>
<MalioButton label="Valider" />
<MalioButton label="Enregistrer" />
<p class="text-xs font-semibold text-m-muted">Hover</p>
<p class="text-xs font-semibold text-m-muted">Hover</p>
<MalioButton label="Valider" button-class="bg-m-btn-primary-hover" />
<MalioButton label="Valider" button-class="bg-m-btn-primary-hover" />
<p class="text-xs font-semibold text-m-muted">Active</p>
<p class="text-xs font-semibold text-m-muted">Active</p>
<MalioButton label="Valider" button-class="bg-m-btn-primary-active" />
<MalioButton label="Valider" button-class="bg-m-btn-primary-active" />
<p class="text-xs font-semibold text-m-muted">Disabled</p>
<p class="text-xs font-semibold text-m-muted">Disabled</p>
<MalioButton label="Valider" disabled />
<MalioButton label="Valider" disabled />
</div>
</div>
<div class="rounded-lg border p-6">
<h2 class="mb-6 text-xl font-bold">Secondary</h2>
<div class="grid grid-cols-2 items-start gap-3">
<p class="text-xs font-semibold text-m-muted">Default</p>
<p class="text-xs font-semibold text-m-muted">Default + icon</p>
<MalioButton label="Modifier" variant="secondary" />
<MalioButton label="Modifier" variant="secondary" icon-name="mdi:pencil" icon-position="left" />
<p class="text-xs font-semibold text-m-muted">Hover</p>
<p class="text-xs font-semibold text-m-muted">Hover + icon</p>
<MalioButton label="Modifier" variant="secondary" button-class="bg-m-btn-secondary-hover" />
<MalioButton label="Modifier" variant="secondary" button-class="bg-m-btn-secondary-hover" icon-name="mdi:pencil" icon-position="left" />
<p class="text-xs font-semibold text-m-muted">Active</p>
<p class="text-xs font-semibold text-m-muted">Active + icon</p>
<MalioButton label="Modifier" variant="secondary" button-class="bg-m-btn-secondary-active" />
<MalioButton label="Modifier" variant="secondary" button-class="bg-m-btn-secondary-active" icon-name="mdi:pencil" icon-position="left" />
<p class="text-xs font-semibold text-m-muted">Disabled</p>
<p class="text-xs font-semibold text-m-muted">Disabled + icon</p>
<MalioButton label="Modifier" variant="secondary" disabled />
<MalioButton label="Modifier" variant="secondary" disabled icon-name="mdi:pencil" icon-position="left" />
</div>
</div>
<div class="rounded-lg border p-6">
<h2 class="mb-6 text-xl font-bold">Tertiary</h2>
<div class="grid grid-cols-2 items-start gap-3">
<p class="text-xs font-semibold text-m-muted">Default</p>
<p class="text-xs font-semibold text-m-muted">Default</p>
<MalioButton label="Voir plus" variant="tertiary" />
<MalioButton label="Afficher" variant="tertiary" />
<p class="text-xs font-semibold text-m-muted">Hover</p>
<p class="text-xs font-semibold text-m-muted">Hover</p>
<MalioButton label="Voir plus" variant="tertiary" button-class="border-m-btn-primary-hover text-m-btn-primary-hover" />
<MalioButton label="Afficher" variant="tertiary" button-class="border-m-btn-primary-hover text-m-btn-primary-hover" />
<p class="text-xs font-semibold text-m-muted">Active</p>
<p class="text-xs font-semibold text-m-muted">Active</p>
<MalioButton label="Voir plus" variant="tertiary" button-class="border-m-btn-primary-active text-m-btn-primary-active" />
<MalioButton label="Afficher" variant="tertiary" button-class="border-m-btn-primary-active text-m-btn-primary-active" />
<p class="text-xs font-semibold text-m-muted">Disabled</p>
<p class="text-xs font-semibold text-m-muted">Disabled</p>
<MalioButton label="Voir plus" variant="tertiary" disabled />
<MalioButton label="Afficher" variant="tertiary" disabled />
</div>
</div>
<div class="rounded-lg border p-6">
<h2 class="mb-6 text-xl font-bold">Danger</h2>
<div class="grid grid-cols-2 items-start gap-3">
<p class="text-xs font-semibold text-m-muted">Default</p>
<p class="text-xs font-semibold text-m-muted">Default</p>
<MalioButton label="Supprimer" variant="danger" icon-name="mdi:trash" icon-position="left" />
<MalioButton label="Supprimer" variant="danger" icon-name="mdi:cancel-bold" icon-position="left" />
<p class="text-xs font-semibold text-m-muted">Hover</p>
<p class="text-xs font-semibold text-m-muted">Hover</p>
<MalioButton label="Supprimer" variant="danger" button-class="bg-m-btn-danger-hover" icon-name="mdi:trash" icon-position="left" />
<MalioButton label="Supprimer" variant="danger" button-class="bg-m-btn-danger-hover" icon-name="mdi:cancel-bold" icon-position="left" />
<p class="text-xs font-semibold text-m-muted">Active</p>
<p class="text-xs font-semibold text-m-muted">Active</p>
<MalioButton label="Supprimer" variant="danger" button-class="bg-m-btn-danger-active" icon-name="mdi:trash" icon-position="left" />
<MalioButton label="Supprimer" variant="danger" button-class="bg-m-btn-danger-active" icon-name="mdi:cancel-bold" icon-position="left" />
<p class="text-xs font-semibold text-m-muted">Disabled</p>
<p class="text-xs font-semibold text-m-muted">Disabled</p>
<MalioButton label="Supprimer" variant="danger" disabled icon-name="mdi:trash" icon-position="left" />
<MalioButton label="Supprimer" variant="danger" disabled icon-name="mdi:cancel-bold" icon-position="left" />
</div>
</div>
<div class="rounded-lg border p-6">
<h2 class="mb-6 text-xl font-bold">Avec icône</h2>
<div class="grid grid-cols-2 items-start gap-3">
<MalioButton label="Valider" icon-name="mdi:check" />
<MalioButton label="Icône à gauche" icon-name="mdi:arrow-left" icon-position="left" />
<MalioButton label="Modifier" variant="secondary" icon-name="mdi:pencil-outline" />
<MalioButton label="Annuler" variant="tertiary" icon-name="mdi:close" />
<MalioButton label="Supprimer" variant="danger" icon-name="mdi:trash-can-outline" />
</div>
</div>
<div class="rounded-lg border p-6">
<h2 class="mb-6 text-xl font-bold">Largeur personnalisée</h2>
<div class="grid grid-cols-2 items-start gap-3">
<MalioButton label="Pleine largeur" button-class="w-full" />
<MalioButton label="Compact" button-class="w-auto px-6" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,155 @@
<template>
<div class="grid grid-cols-1 items-start gap-6 md:grid-cols-2">
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Simple</h2>
<div class="flex gap-4">
<MalioButtonIcon
icon="mdi:arrow-left"
aria-label="Retour"
/>
<MalioButtonIcon
icon="mdi:pencil-outline"
aria-label="Modifier"
/>
<MalioButtonIcon
icon="mdi:close"
aria-label="Fermer"
/>
</div>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Icônes variées</h2>
<div class="flex gap-4">
<MalioButtonIcon
icon="mdi:download"
aria-label="Télécharger"
/>
<MalioButtonIcon
icon="mdi:bell-outline"
aria-label="Notifications"
/>
<MalioButtonIcon
icon="mdi:cog-outline"
aria-label="Paramètres"
/>
<MalioButtonIcon
icon="mdi:view-grid-outline"
aria-label="Grille"
/>
<MalioButtonIcon
icon="mdi:format-list-bulleted"
aria-label="Liste"
/>
<MalioButtonIcon
icon="mdi:folder-outline"
aria-label="Dossier"
/>
</div>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Désactivé</h2>
<div class="flex gap-4">
<MalioButtonIcon
icon="mdi:arrow-left"
aria-label="Retour"
disabled
/>
<MalioButtonIcon
icon="mdi:pencil-outline"
aria-label="Modifier"
disabled
/>
<MalioButtonIcon
icon="mdi:close"
aria-label="Fermer"
disabled
/>
</div>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Taille personnalisée</h2>
<div class="flex items-center gap-4">
<MalioButtonIcon
icon="mdi:arrow-left"
aria-label="Petit"
:icon-size="16"
/>
<MalioButtonIcon
icon="mdi:arrow-left"
aria-label="Normal"
:icon-size="24"
/>
<MalioButtonIcon
icon="mdi:arrow-left"
aria-label="Grand"
:icon-size="32"
/>
</div>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Ghost</h2>
<div class="flex gap-4">
<MalioButtonIcon
icon="mdi:arrow-left"
aria-label="Retour"
variant="ghost"
/>
<MalioButtonIcon
icon="mdi:pencil-outline"
aria-label="Modifier"
variant="ghost"
/>
<MalioButtonIcon
icon="mdi:cancel-bold"
aria-label="Fermer"
variant="ghost"
/>
</div>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Ghost désactivé</h2>
<div class="flex gap-4">
<MalioButtonIcon
icon="mdi:arrow-left"
aria-label="Retour"
variant="ghost"
disabled
/>
<MalioButtonIcon
icon="mdi:pencil-outline"
aria-label="Modifier"
variant="ghost"
disabled
/>
</div>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Avec événement click</h2>
<div class="flex items-center gap-4">
<MalioButtonIcon
icon="mdi:plus"
aria-label="Incrémenter"
@click="counter++"
/>
<span class="text-lg font-semibold">{{ counter }}</span>
<MalioButtonIcon
icon="mdi:minus"
aria-label="Décrémenter"
@click="counter--"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
const counter = ref(0)
</script>

View File

@@ -0,0 +1,101 @@
<template>
<div class="grid grid-cols-1 items-start gap-6 md:grid-cols-2">
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Simple</h2>
<MalioCheckbox
v-model="simpleValue"
label="Accepter les conditions"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Coche par default</h2>
<MalioCheckbox
v-model="checkedValue"
label="Recevoir la newsletter"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Hint</h2>
<MalioCheckbox
v-model="hintValue"
label="J'accepte le traitement des donnees"
hint="Vous pouvez retirer votre consentement a tout moment."
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Erreur</h2>
<MalioCheckbox
:model-value="false"
label="Accepter les CGU"
error="Ce champ est obligatoire."
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Succès</h2>
<MalioCheckbox
:model-value="true"
label="Adresse vérifiée"
success="Choix valide."
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Disabled et Readonly</h2>
<div class="space-y-4">
<MalioCheckbox
:model-value="true"
label="Option désactivée"
disabled
/>
<MalioCheckbox
:model-value="true"
label="Option readonly"
readonly
/>
</div>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Plusieurs checkbox</h2>
<div class="space-y-4">
<MalioCheckbox
label="Option 1"
/>
<MalioCheckbox
label="Option 2"
/>
<MalioCheckbox
label="Option 3"
/>
</div>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Plusieurs checkbox avec v-for</h2>
<div class="space-y-4">
<MalioCheckbox
v-for="option in options"
:key="option"
:label="option"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import MalioCheckbox from '../../../../app/components/malio/checkbox/Checkbox.vue'
const simpleValue = ref(false)
const checkedValue = ref(true)
const hintValue = ref(false)
const options = [
'Option A',
'Option B',
'Option C',
'Option D',
]
</script>

View File

@@ -0,0 +1,49 @@
<script setup lang="ts">
import { ref } from 'vue'
const drawerDefault = ref(false)
const drawerNoClose = ref(false)
const drawerCustomWidth = ref(false)
const drawerWithForm = ref(false)
</script>
<template>
<div class="grid grid-cols-1 items-start gap-6 md:grid-cols-2">
<div class="rounded-lg border p-6">
<h2 class="mb-6 text-xl font-bold">Drawer simple</h2>
<MalioButton label="Ouvrir le drawer" @click="drawerDefault = true" />
<MalioDrawer v-model="drawerDefault" title="Titre du drawer">
<p class="text-m-text">Contenu du drawer. Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</MalioDrawer>
</div>
<div class="rounded-lg border p-6">
<h2 class="mb-6 text-xl font-bold">Sans bouton fermer</h2>
<MalioButton label="Ouvrir le drawer" variant="secondary" @click="drawerNoClose = true" />
<MalioDrawer v-model="drawerNoClose" title="Sans croix" :show-close="false">
<p class="text-m-text">Ce drawer n'a pas de bouton fermer. Cliquez sur le backdrop pour fermer.</p>
</MalioDrawer>
</div>
<div class="rounded-lg border p-6">
<h2 class="mb-6 text-xl font-bold">Largeur personnalisée</h2>
<MalioButton label="Ouvrir le drawer large" variant="tertiary" @click="drawerCustomWidth = true" />
<MalioDrawer v-model="drawerCustomWidth" title="Drawer large" drawer-class="max-w-2xl">
<p class="text-m-text">Ce drawer utilise une largeur personnalisée via drawerClass.</p>
</MalioDrawer>
</div>
<div class="rounded-lg border p-6">
<h2 class="mb-6 text-xl font-bold">Avec formulaire</h2>
<MalioButton label="Ouvrir le formulaire" variant="danger" @click="drawerWithForm = true" />
<MalioDrawer v-model="drawerWithForm" title="Formulaire">
<div class="flex flex-col gap-4">
<MalioInputText label="Nom" />
<MalioInputText label="Prénom" />
<MalioInputText label="Email" />
<MalioButton label="Enregistrer" button-class="w-full" @click="drawerWithForm = false" />
</div>
</MalioDrawer>
</div>
</div>
</template>

View File

@@ -0,0 +1,80 @@
<template>
<div class="grid grid-cols-1 items-start gap-6 md:grid-cols-2">
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Simple</h2>
<MalioInputNumber
v-model="simpleValue"
label="Quantite"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Valeur initiale</h2>
<MalioInputNumber
v-model="initialValue"
label="Participants"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Avec bornes</h2>
<MalioInputNumber
v-model="boundedValue"
label="Places"
:min="1"
:max="5"
hint="Minimum 1, maximum 5"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Desactive</h2>
<MalioInputNumber
v-model="disabledValue"
label="Articles"
disabled
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Readonly</h2>
<MalioInputNumber
v-model="readonlyValue"
label="Tickets"
readonly
hint="Valeur verrouillee"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Erreur</h2>
<MalioInputNumber
v-model="errorValue"
label="Quantite"
:min="1"
error="La quantite minimale est 1"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Succes</h2>
<MalioInputNumber
v-model="successValue"
label="Quantite"
success="Quantite validee"
/>
</div>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
const simpleValue = ref('')
const initialValue = ref('3')
const boundedValue = ref('2')
const disabledValue = ref('4')
const readonlyValue = ref('7')
const errorValue = ref('0')
const successValue = ref('2')
</script>

View File

@@ -0,0 +1,99 @@
<template>
<div class="grid grid-cols-1 items-start gap-6 md:grid-cols-2">
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Simple</h2>
<MalioInputPassword />
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Avec label</h2>
<MalioInputPassword
v-model="passwordValue"
label="Mot de passe"
name="password"
autocomplete="current-password"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Sans icône</h2>
<MalioInputPassword
label="Mot de passe"
:display-icon="false"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Désactivé</h2>
<MalioInputPassword
model-value="motdepasse123"
disabled
label="Mot de passe"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Readonly</h2>
<MalioInputPassword
model-value="lectureseule"
readonly
label="Mot de passe"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Avec hint</h2>
<MalioInputPassword
label="Mot de passe"
hint="8 caractères minimum"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Erreur</h2>
<MalioInputPassword
model-value="abc"
label="Mot de passe"
error="Le mot de passe est trop court"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Succès</h2>
<MalioInputPassword
model-value="motdepasse123"
label="Mot de passe"
success="Mot de passe valide"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Validation dynamique</h2>
<MalioInputPassword
v-model="dynamicPassword"
label="Mot de passe"
hint="8 caractères minimum, 1 majuscule, 1 chiffre"
:error="dynamicError"
:success="dynamicSuccess"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
const passwordValue = ref('')
const dynamicPassword = ref('')
const passwordRegex = /^(?=.*[A-Z])(?=.*\d).{8,}$/
const isDynamicValid = computed(() => passwordRegex.test(dynamicPassword.value))
const dynamicError = computed(() => {
if (!dynamicPassword.value) return ''
return isDynamicValid.value ? '' : 'Mot de passe invalide'
})
const dynamicSuccess = computed(() => {
if (!dynamicPassword.value) return ''
return isDynamicValid.value ? 'Mot de passe valide' : ''
})
</script>

View File

@@ -92,7 +92,7 @@
<script setup lang="ts">
import {ref} from 'vue'
import MalioInputTextArea from '../../../app/components/malio/InputTextArea.vue'
import MalioInputTextArea from '../../../../app/components/malio/input/InputTextArea.vue'
const hintValue = ref('')
const iconValue = ref('')

View File

@@ -0,0 +1,88 @@
<template>
<div class="grid grid-cols-1 items-start gap-6 md:grid-cols-2">
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Simple</h2>
<MalioInputUpload label="Fichier" />
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Avec label et v-model</h2>
<MalioInputUpload
v-model="uploadValue"
label="Téléverser un document"
/>
<p class="mt-2 text-sm text-gray-500">Valeur : {{ uploadValue || '(aucun)' }}</p>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Avec accept (PDF)</h2>
<MalioInputUpload
label="Document PDF"
accept=".pdf"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Désactivé</h2>
<MalioInputUpload
model-value="document.pdf"
disabled
label="Fichier"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Avec hint</h2>
<MalioInputUpload
label="Fichier"
hint="Formats acceptés : PDF, DOC, DOCX"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Erreur</h2>
<MalioInputUpload
model-value="image.bmp"
label="Fichier"
error="Format non supporté"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Succès</h2>
<MalioInputUpload
model-value="rapport.pdf"
label="Fichier"
success="Fichier valide"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Validation dynamique</h2>
<MalioInputUpload
v-model="dynamicUpload"
label="Document PDF"
accept=".pdf"
hint="Seuls les fichiers PDF sont acceptés"
:error="dynamicError"
:success="dynamicSuccess"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
const uploadValue = ref('')
const dynamicUpload = ref('')
const dynamicError = computed(() => {
if (!dynamicUpload.value) return ''
return dynamicUpload.value.endsWith('.pdf') ? '' : 'Seuls les fichiers PDF sont acceptés'
})
const dynamicSuccess = computed(() => {
if (!dynamicUpload.value) return ''
return dynamicUpload.value.endsWith('.pdf') ? 'Fichier PDF valide' : ''
})
</script>

View File

@@ -93,7 +93,7 @@
<script setup lang="ts">
import {ref} from 'vue'
import MalioRadioButton from '../../../app/components/malio/RadioButton.vue'
import MalioRadioButton from '../../../../app/components/malio/radio/RadioButton.vue'
const options = [
{label: 'Option 1', value: 'option1'},

View File

@@ -0,0 +1,193 @@
<template>
<div class="grid grid-cols-1 items-start gap-6 md:grid-cols-2">
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Simple</h2>
<MalioSelectCheckbox
v-model="basicValue"
:options="options"
empty-option-label="Aucune selection"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Avec tag</h2>
<MalioSelectCheckbox
v-model="labelValue"
:options="options"
displayTag="true"
empty-option-label=" "
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Avec tag + label</h2>
<MalioSelectCheckbox
v-model="labelValue1"
:options="options"
displayTag="true"
label="Pays"
empty-option-label=" "
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Avec label</h2>
<MalioSelectCheckbox
v-model="labelValue"
:options="options"
label="Pays"
empty-option-label="Aucune selection"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Valeur preselectionnee</h2>
<MalioSelectCheckbox
v-model="selectedValue"
:options="options"
label="Pays"
empty-option-label="Aucune selection"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Hint</h2>
<MalioSelectCheckbox
v-model="hintValue"
:options="options"
label="Pays"
hint="Choisissez votre pays"
empty-option-label="Aucune selection"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Erreur</h2>
<MalioSelectCheckbox
v-model="errorValue"
:options="options"
label="Pays"
error="Ce champ est obligatoire"
empty-option-label="Aucune selection"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Succes</h2>
<MalioSelectCheckbox
v-model="successValue"
:options="options"
label="Pays"
success="Selection validee"
empty-option-label="Aucune selection"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Desactive</h2>
<MalioSelectCheckbox
v-model="disabledValue"
:options="options"
label="Pays"
disabled
empty-option-label="Aucune selection"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Sans options</h2>
<MalioSelectCheckbox
v-model="emptyValue"
label="Pays"
empty-option-label="Aucun pays disponible"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Tout sélectionner</h2>
<MalioSelectCheckbox
v-model="selectAllValue"
:options="options"
label="Pays"
:display-select-all="true"
empty-option-label="Aucune selection"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Tout sélectionner (label custom)</h2>
<MalioSelectCheckbox
v-model="selectAllCustomValue"
:options="options"
label="Pays"
:display-select-all="true"
select-all-label="Cocher tout"
empty-option-label="Aucune selection"
/>
</div>
<div class="rounded-lg border p-4 md:col-span-2">
<h2 class="mb-4 text-xl font-bold">Liste longue</h2>
<MalioSelectCheckbox
v-model="longListValue"
:options="longOptions"
label="Pays"
hint="Permet de verifier la scrollbar"
empty-option-label="Aucune selection"
/>
</div>
<div class="rounded-lg border p-4 md:col-span-2">
<h2 class="mb-4 text-xl font-bold">Ouverture en bas de page</h2>
<div class="h-64" />
<MalioSelectCheckbox
v-model="bottomValue"
:options="longOptions"
label="Ouverture adaptative"
hint="A ouvrir pres du bas de la page"
empty-option-label="Aucune selection"
/>
</div>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
const options = [
{label: 'France', value: 'fr'},
{label: 'Belgique', value: 'be'},
{label: 'Suisse', value: 'ch'},
{label: 'Canada', value: 'ca'},
{label: 'Allemagne', value: 'de'},
{label: 'Espagne', value: 'es'},
{label: 'Italie', value: 'it'},
{label: 'Portugal', value: 'pt'},
]
const longOptions = [
...options,
{label: 'Pays-Bas', value: 'nl'},
{label: 'Suede', value: 'se'},
{label: 'Norvege', value: 'no'},
{label: 'Danemark', value: 'dk'},
{label: 'Finlande', value: 'fi'},
{label: 'Autriche', value: 'at'},
{label: 'Irlande', value: 'ie'},
{label: 'Grece', value: 'gr'},
{label: 'Pologne', value: 'pl'},
{label: 'Hongrie', value: 'hu'},
{label: 'Republique tcheque', value: 'cz'},
]
const basicValue = ref<Array<string | number>>([])
const labelValue = ref<Array<string | number>>([])
const labelValue1 = ref<Array<string | number>>([])
const selectedValue = ref<Array<string | number>>(['fr'])
const hintValue = ref<Array<string | number>>([])
const errorValue = ref<Array<string | number>>([])
const successValue = ref<Array<string | number>>(['be'])
const disabledValue = ref<Array<string | number>>(['ca'])
const emptyValue = ref<Array<string | number>>([])
const selectAllValue = ref<Array<string | number>>([])
const selectAllCustomValue = ref<Array<string | number>>([])
const longListValue = ref<Array<string | number>>([])
const bottomValue = ref<Array<string | number>>([])
</script>

View File

@@ -0,0 +1,131 @@
<template>
<div class="flex gap-8" style="height: calc(100vh - 100px)">
<MalioSidebar
v-model="collapsed1"
:sections="sectionsShort"
>
<template #logo>
<img src="/LOGO_MALIO.png" alt="Malio" />
</template>
<template #logo-collapsed>
<img src="/LOGO_MALIO_COLLAPSED.png" alt="Malio" />
</template>
</MalioSidebar>
<MalioSidebar
v-model="collapsed2"
:sections="sectionsLong"
>
<template #logo>
<img src="/LOGO_MALIO.png" alt="Malio" />
</template>
<template #logo-collapsed>
<img src="/LOGO_MALIO_COLLAPSED.png" alt="Malio" />
</template>
</MalioSidebar>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
const collapsed1 = ref(false)
const collapsed2 = ref(false)
const sectionsShort = [
{
label: 'LOGISTIQUE / TRANSPORT',
icon: 'mdi:truck-delivery',
items: [
{label: 'Réception / Expédition', to: '/reception'},
{label: 'Validation expédition', to: '/validation'},
],
},
{
label: 'COMMERCIAL',
icon: 'mdi:handshake',
items: [
{label: 'Répertoire fournisseurs', to: '/fournisseurs'},
{label: 'Répertoire clients', to: '/clients'},
],
},
]
const sectionsLong = [
{
label: 'LOGISTIQUE / TRANSPORT',
icon: 'mdi:truck-delivery',
items: [
{label: 'Réception / Expédition', to: '/reception'},
{label: 'Validation expédition', to: '/validation'},
{label: 'Voyage', to: '/voyage'},
{label: 'Ticket de pesée', to: '/pesee'},
{label: 'Bon de réception', to: '/bon-reception'},
{label: "Bon d'expédition", to: '/bon-expedition'},
],
},
{
label: 'USINE / PRODUCTION',
icon: 'mdi:factory',
items: [
{label: 'Fabrication en cours', to: '/fabrication'},
{label: 'Liste des fabrications', to: '/fabrications'},
],
},
{
label: 'COMMERCIAL',
icon: 'mdi:handshake',
items: [
{label: 'Répertoire fournisseurs', to: '/fournisseurs'},
{label: 'Compagnie fournisseurs', to: '/compagnie-fournisseurs'},
{label: 'Répertoire clients', to: '/clients'},
{label: 'Contrats en cours', to: '/contrats'},
{label: 'Commissions Clients', to: '/commissions'},
{label: 'Attribution expédition', to: '/attribution'},
],
},
{
label: 'PRIX',
icon: 'mdi:tag',
items: [
{label: "Prix d'achat/vente", to: '/prix-achat'},
{label: "Prix d'achat spécifiques", to: '/prix-specifiques'},
{label: 'Prix de ventes clients', to: '/prix-vente'},
],
},
{
label: 'FACTURATION',
icon: 'mdi:receipt',
items: [
{label: 'Expéditions à facturer', to: '/expeditions-facturer'},
{label: 'Factures', to: '/factures'},
],
},
{
label: 'TECHNIQUE',
icon: 'mdi:cog',
items: [
{label: 'Répertoire prestataires', to: '/prestataires'},
{label: 'Répertoire transporteurs', to: '/transporteurs'},
],
},
{
label: 'SUIVI HEURES',
icon: 'mdi:clock-outline',
items: [
{label: 'Heure Usine', to: '/heure-usine'},
{label: 'Heure Extras', to: '/heure-extras'},
{label: 'Heure Ferme', to: '/heure-ferme'},
],
},
{
label: 'ADMINISTRATION',
icon: 'mdi:shield-account',
items: [
{label: 'Catalogue produits', to: '/catalogue'},
{label: 'Éditer étiquettes', to: '/etiquettes'},
{label: 'Organisation catégorie', to: '/organisation'},
],
},
]
</script>

View File

@@ -0,0 +1,66 @@
<template>
<div class="grid grid-cols-1 items-start gap-6">
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Simple</h2>
<MalioTabList v-model="simpleValue" :tabs="tabs">
<template #qualimat><p class="p-4">Contenu Qualimat</p></template>
<template #adresses><p class="p-4">Contenu Adresses</p></template>
<template #contacts><p class="p-4">Contenu Contacts</p></template>
<template #comptabilite><p class="p-4">Contenu Comptabilité</p></template>
</MalioTabList>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Sans icônes</h2>
<MalioTabList v-model="noIconValue" :tabs="tabsNoIcon">
<template #tab1><p class="p-4">Contenu onglet 1</p></template>
<template #tab2><p class="p-4">Contenu onglet 2</p></template>
<template #tab3><p class="p-4">Contenu onglet 3</p></template>
</MalioTabList>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Non contrôlé (sans v-model)</h2>
<MalioTabList :tabs="tabs">
<template #qualimat><p class="p-4">Contenu Qualimat</p></template>
<template #adresses><p class="p-4">Contenu Adresses</p></template>
<template #contacts><p class="p-4">Contenu Contacts</p></template>
<template #comptabilite><p class="p-4">Contenu Comptabilité</p></template>
</MalioTabList>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Deux onglets</h2>
<MalioTabList v-model="twoTabValue" :tabs="tabsTwo">
<template #general><p class="p-4">Informations générales</p></template>
<template #details><p class="p-4">Détails avancés</p></template>
</MalioTabList>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const tabs = [
{ key: 'qualimat', label: 'Qualimat', icon: 'mdi:certificate-outline' },
{ key: 'adresses', label: 'Adresses', icon: 'mdi:map-marker-outline' },
{ key: 'contacts', label: 'Contacts', icon: 'mdi:account-box-outline' },
{ key: 'comptabilite', label: 'Comptabilité', icon: 'mdi:web' },
]
const tabsNoIcon = [
{ key: 'tab1', label: 'Onglet 1' },
{ key: 'tab2', label: 'Onglet 2' },
{ key: 'tab3', label: 'Onglet 3' },
]
const tabsTwo = [
{ key: 'general', label: 'Général', icon: 'mdi:information-outline' },
{ key: 'details', label: 'Détails', icon: 'mdi:cog-outline' },
]
const simpleValue = ref('qualimat')
const noIconValue = ref('tab1')
const twoTabValue = ref('general')
</script>

View File

@@ -0,0 +1,87 @@
<template>
<div class="grid grid-cols-1 items-start gap-6 md:grid-cols-2">
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Simple</h2>
<MalioTime v-model="simpleValue" />
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Avec label</h2>
<MalioTime
v-model="labeledValue"
label="Heure de depart"
name="departure-time"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Valeur initiale</h2>
<MalioTime
v-model="initialValue"
label="Heure d'arrivee"
hint="Format HH:MM"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Required</h2>
<MalioTime
v-model="requiredValue"
label="Heure limite"
required
hint="Champ obligatoire"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Desactive</h2>
<MalioTime
v-model="disabledValue"
label="Heure verrouillee"
disabled
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Readonly</h2>
<MalioTime
v-model="readonlyValue"
label="Heure en lecture seule"
readonly
hint="Visible mais non modifiable"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Erreur</h2>
<MalioTime
v-model="errorValue"
label="Heure de fermeture"
error="L'heure saisie n'est pas valide"
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Succes</h2>
<MalioTime
v-model="successValue"
label="Heure confirmee"
success="Horaire enregistre"
/>
</div>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import MalioTime from '../../../../app/components/malio/time/Time.vue'
const simpleValue = ref('')
const labeledValue = ref('')
const initialValue = ref('08:30')
const requiredValue = ref('')
const disabledValue = ref('14:15')
const readonlyValue = ref('18:45')
const errorValue = ref('25:90')
const successValue = ref('09:00')
</script>

View File

@@ -9,17 +9,41 @@
Liste des composants
</button>
<nav class="mt-6 flex flex-col gap-2">
<button
v-for="item in items"
:key="item.name"
type="button"
class="rounded px-3 py-2 text-left text-black font-bold hover:bg-m-primary hover:text-white"
:class="selectedName === item.name ? 'bg-m-secondary text-white' : ''"
@click="selectOrToggle(item.name)"
<nav class="mt-6 flex flex-col gap-1">
<div
v-for="group in groups"
:key="group.category"
>
{{ item.label }}
</button>
<button
type="button"
class="flex w-full items-center justify-between rounded px-3 py-2 text-left text-black font-bold hover:bg-m-primary/10"
@click="toggleCategory(group.category)"
>
{{ group.category }}
<span
class="text-xs transition-transform duration-200"
:class="openCategories.has(group.category) ? 'rotate-90' : ''"
>
&#9654;
</span>
</button>
<div
v-if="openCategories.has(group.category)"
class="ml-3 flex flex-col gap-1 border-l border-gray-300 pl-2"
>
<button
v-for="item in group.items"
:key="item.name"
type="button"
class="rounded px-3 py-1.5 text-left text-sm text-black hover:bg-m-primary hover:text-white"
:class="selectedName === item.name ? 'bg-m-primary/50 text-white' : ''"
@click="selectItem(item.name)"
>
{{ item.label }}
</button>
</div>
</div>
</nav>
</aside>
@@ -48,7 +72,7 @@
</template>
<script setup lang="ts">
import { computed, ref, watch, shallowRef } from 'vue'
import {computed, reactive, ref, watch, shallowRef} from 'vue'
type LoadedModule = {
default: unknown
@@ -59,8 +83,13 @@ type Item = {
label: string
}
const componentModules = import.meta.glob('../../app/components/malio/*.vue')
const demoModules = import.meta.glob('./composant/*.vue')
type Group = {
category: string
items: Item[]
}
const componentModules = import.meta.glob('../../app/components/malio/**/*.vue')
const demoModules = import.meta.glob('./composant/**/*.vue')
const demoByName: Record<string, () => Promise<LoadedModule>> =
Object.fromEntries(
@@ -70,31 +99,55 @@ const demoByName: Record<string, () => Promise<LoadedModule>> =
}),
)
const items = computed<Item[]>(() =>
Object.keys(componentModules).map((file) => {
const name = file.split('/').pop()?.replace('.vue', '') ?? ''
return {
name,
label: name,
}
}),
)
const groups = computed<Group[]>(() => {
const categoryMap = new Map<string, Item[]>()
Object.keys(componentModules).forEach((file) => {
const parts = file.split('/')
const name = parts.pop()?.replace('.vue', '') ?? ''
const category = parts.pop() ?? ''
if (!categoryMap.has(category)) {
categoryMap.set(category, [])
}
categoryMap.get(category)!.push({name, label: name})
})
return Array.from(categoryMap.entries())
.sort(([a], [b]) => a.localeCompare(b))
.map(([category, items]) => ({
category: category.charAt(0).toUpperCase() + category.slice(1),
items: items.sort((a, b) => a.label.localeCompare(b.label)),
}))
})
const openCategories = reactive(new Set<string>())
const selectedName = ref('')
const hasInitializedSelection = ref(false)
watch(
items,
groups,
(val) => {
if (!hasInitializedSelection.value && val.length > 0) {
selectedName.value = val[0].name
openCategories.add(val[0].category)
if (val[0].items.length > 0) {
selectedName.value = val[0].items[0].name
}
hasInitializedSelection.value = true
}
},
{ immediate: true },
{immediate: true},
)
function selectOrToggle(name: string) {
function toggleCategory(category: string) {
if (openCategories.has(category)) {
openCategories.delete(category)
} else {
openCategories.add(category)
}
}
function selectItem(name: string) {
selectedName.value = selectedName.value === name ? '' : name
}