Compare commits

..

28 Commits

Author SHA1 Message Date
tristan 2df5aff1b7 feat : wip ajout du composant Datatable 2026-03-25 08:20:04 +01:00
tristan f09f8a91ac [#MUI-15] Création d'un composant drawer (#21)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

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

Reviewed-on: #21
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-03-24 10:49:27 +00:00
tristan bcadd46ce2 [#MUI-2] Faire un MCP pour la librairie de composant (#20)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

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

Reviewed-on: #20
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-03-24 10:31:20 +00:00
tristan e76337502a [#MUI-10] Création d'un composant bouton (#19)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

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

Reviewed-on: #19
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-03-24 10:12:28 +00:00
tristan 968b7087b5 [#MUI-23] Revoir la config couleur tailwind (#18)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

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

Reviewed-on: #18
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-03-24 09:05:23 +00:00
tristan 3deba3f369 [#MUI-20] Développer le composant Menu (#17)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

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

Reviewed-on: #17
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-03-23 16:36:16 +00:00
tristan cf46ab0c85 [#MUI-11] Création d'un composant navigation par onglets (#16)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

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

Reviewed-on: #16
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-03-23 07:48:55 +00:00
tristan 09cc3edf6f feat : reorganisation de la structure projet 2026-03-20 14:22:40 +01:00
tristan c95a3657c0 [#MUI-14] Création d'un composant bouton icône (#15)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

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

Reviewed-on: #15
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-03-20 11:00:38 +00:00
tristan 9843f4d032 feat : ajout de state dans les histoires des composants 2026-03-19 17:45:03 +01:00
tristan 9d9b9c9dc4 feat : ajout d'un sélecteur "Tout cocher" dans le composant SelectCheckbox 2026-03-19 17:30:52 +01:00
tristan 187ef52865 [#MUI-9] Ajout composant upload (#14)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

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

Reviewed-on: #14
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-03-19 09:51:37 +00:00
tristan 9925f1ced4 [#MUI-8] Ajout composant mot de passe (#13)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

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

Reviewed-on: #13
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-03-19 09:43:55 +00:00
tristan ded414ba1a [#MUI12] Correction composant Nombre et Taux horaire (#12)
| 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é

Reviewed-on: #12
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-03-19 08:22:37 +00:00
kevin 11d60e687b [#366] Création d'un composant de type Select checkbox (#11)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|           #366       |            Création d'un composant de type Select checkbox     |

## 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é

Reviewed-on: #11
Co-authored-by: kevin <kevin@yuno.malio.fr>
Co-committed-by: kevin <kevin@yuno.malio.fr>
2026-03-17 12:28:57 +00:00
kevin d3038994c3 [#407] Création d'un composant de type time (#10)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|       #407         |           Création d'un composant de type time      |

## 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
- [ ] CHANGELOG modifié

Reviewed-on: #10
Co-authored-by: kevin <kevin@yuno.malio.fr>
Co-committed-by: kevin <kevin@yuno.malio.fr>
2026-03-17 12:28:33 +00:00
kevin 0d350e12c6 Merge pull request '[#365] Création d'un composant de type Number' (#9) from feat/365-creation-composant-number into develop
Reviewed-on: #9
2026-03-11 15:16:18 +00:00
tristan c6acaace27 Merge remote-tracking branch 'origin/develop' into develop 2026-03-08 20:10:32 +01:00
tristan 927c7c3c70 Merge remote-tracking branch 'origin/main' into develop 2026-03-08 20:10:02 +01:00
kevin bf0aa92497 [#363] Création d'un composant de type checkbox (#5)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|        #363          |        Création d'un composant de type checkbox       |

## 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: tristan <tristan@yuno.malio.fr>
Reviewed-on: #5
Co-authored-by: kevin <kevin@yuno.malio.fr>
Co-committed-by: kevin <kevin@yuno.malio.fr>
2026-03-08 19:07:53 +00:00
kevin 88dd76a0e4 [#364 ] Création d'un composant de type radio (#6)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|           #364        |       Création d'un composant de type radio          |

## 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é

Reviewed-on: #6
Co-authored-by: kevin <kevin@yuno.malio.fr>
Co-committed-by: kevin <kevin@yuno.malio.fr>
2026-03-08 18:59:50 +00:00
kevin cc04114f89 feat : ajout du composant input number 2026-03-05 09:38:56 +01:00
kevin f456ea4ddf feat : ajout du composant input number 2026-03-04 13:15:43 +01:00
kevin 77364daa67 [#362] Création d'un composant de type Montant (#4)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|          #362        |       Création d'un composant de type Montant          |

## 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é

Reviewed-on: #4
Reviewed-by: Autin <tristan@yuno.malio.fr>
Co-authored-by: kevin <kevin@yuno.malio.fr>
Co-committed-by: kevin <kevin@yuno.malio.fr>
2026-03-03 10:42:39 +00:00
kevin 1ab7b2427a [#337] Création d'un composant Select (#3)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|       #337           |           Création d'un composant Select      |

## Description de la PR

## Modification du .env

## Check list

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

Co-authored-by: tristan <tristan@yuno.malio.fr>
Reviewed-on: #3
Reviewed-by: Autin <tristan@yuno.malio.fr>
Co-authored-by: kevin <kevin@yuno.malio.fr>
Co-committed-by: kevin <kevin@yuno.malio.fr>
2026-03-02 13:24:58 +00:00
tristan 82ecc9cfe2 feat : ajout config vitest/make/pre-commit/commit-msg + un exemple de test vitest 2026-02-23 11:29:16 +01:00
tristan 65d9060e26 feat : ajout du template de MR + CHANGELOG.md 2026-02-23 11:11:31 +01:00
tristan ec4c157226 fix: readme.md 2026-02-19 11:18:36 +01:00
13 changed files with 372 additions and 421 deletions
@@ -82,16 +82,6 @@
/>
</div>
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Peu d'elements (2)</h2>
<MalioSelect
v-model="shortListValue"
:options="shortOptions"
label="Civilite"
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>
<MalioSelect
@@ -131,11 +121,6 @@ const options = [
{label: 'Portugal', value: 'pt'},
]
const shortOptions = [
{label: 'Monsieur', value: 'M'},
{label: 'Madame', value: 'Mme'},
]
const longOptions = [
...options,
{label: 'Pays-Bas', value: 'nl'},
@@ -159,7 +144,6 @@ const errorValue = ref<string | number | null>(null)
const successValue = ref<string | number | null>('be')
const disabledValue = ref<string | number | null>('ca')
const emptyValue = ref<string | number | null>(null)
const shortListValue = ref<string | number | null>(null)
const longListValue = ref<string | number | null>(null)
const bottomValue = ref<string | number | null>(null)
</script>
+1 -11
View File
@@ -163,17 +163,8 @@ Liste déroulante.
| `options` | `{ value: string \| number, text: string }[]` | `[]` | Options disponibles |
| `emptyOptionLabel` | `string` | `''` | Placeholder option vide |
| `label` | `string` | `''` | Label |
| `hint` | `string` | `''` | Message d'aide |
| `error` | `string` | `''` | Message d'erreur |
| `success` | `string` | `''` | Message de succès |
| `disabled` | `boolean` | `false` | Désactivé |
| `groupClass` | `string` | `''` | Classes CSS conteneur (twMerge) |
| `minWidth` | `string` | `'w-96'` | Classe largeur minimum |
| `maxWidth` | `string` | `''` | Classe largeur maximum |
| `rounded` | `string` | `'rounded-md'` | Classe border-radius |
| `textField` | `string` | `'text-lg'` | Classe taille texte bouton |
| `textValue` | `string` | `'text-lg'` | Classe taille texte valeur |
| `textLabel` | `string` | `'text-sm'` | Classe taille texte label |
| `error` | `string` | `''` | Message d'erreur |
**Events :** `update:modelValue(value: string | number | null)`
**Slots :** `icon` (icône dropdown custom)
@@ -181,7 +172,6 @@ Liste déroulante.
```vue
<MalioSelect v-model="pays" label="Pays" :options="[{ value: 'FR', text: 'France' }, { value: 'BE', text: 'Belgique' }]" />
<MalioSelect v-model="ville" label="Ville" :options="villes" empty-option-label="Choisir..." />
<MalioSelect v-model="civilite" label="Civilité" :options="civilites" group-class="mt-0" />
```
---
@@ -114,7 +114,7 @@ describe('MalioCheckbox', () => {
})
expect(wrapper.get('input').attributes('aria-invalid')).toBe('true')
expect(wrapper.get('label').classes()).toContain('text-m-error')
expect(wrapper.get('label').classes()).toContain('text-m-danger')
expect(wrapper.get('p').text()).toBe('You must accept')
})
@@ -125,7 +125,7 @@ describe('MalioCheckbox', () => {
})
expect(wrapper.get('p').text()).toBe('Invalid')
expect(wrapper.get('p').classes()).toContain('text-m-error')
expect(wrapper.get('p').classes()).toContain('text-m-danger')
})
it('shows success styles and message when there is no error', () => {
+8 -8
View File
@@ -110,7 +110,7 @@ const mergedLabelClass = computed(() =>
twMerge(
'cbx text-black',
disabled.value ? 'cursor-not-allowed text-black/60' : '',
hasError.value ? 'text-m-error' : '',
hasError.value ? 'text-m-danger' : '',
hasSuccess.value ? 'text-m-success' : '',
props.labelClass,
),
@@ -120,7 +120,7 @@ const mergedMessageClass = computed(() =>
twMerge(
'text-xs',
hasError.value
? 'text-m-error'
? 'text-m-danger'
: hasSuccess.value
? 'text-m-success'
: 'text-m-muted',
@@ -200,14 +200,14 @@ const onChange = (event: Event) => {
stroke-dashoffset: 0;
}
.inp-cbx + .cbx.text-m-error span:first-child {
border-color: rgb(var(--m-error) / 1);
.inp-cbx + .cbx.text-m-danger span:first-child {
border-color: rgb(var(--m-danger) / 1);
}
.cbx.text-m-error span:first-child svg {
stroke: rgb(var(--m-error) / 1);
.cbx.text-m-danger span:first-child svg {
stroke: rgb(var(--m-danger) / 1);
}
.inp-cbx:checked + .cbx.text-m-error span:first-child {
border-color: rgb(var(--m-error) / 1);
.inp-cbx:checked + .cbx.text-m-danger span:first-child {
border-color: rgb(var(--m-danger) / 1);
}
.inp-cbx + .cbx.text-m-success span:first-child {
+28 -32
View File
@@ -1,25 +1,25 @@
<template>
<Teleport to="body">
<Transition
name="drawer"
appear
@after-leave="isRendered = false"
<div
v-if="isOpen"
:id="componentId"
class="fixed inset-0 z-50"
v-bind="attrs"
>
<div
v-if="isRendered && isOpen"
:id="componentId"
class="fixed inset-0 z-50 flex justify-end"
v-bind="attrs"
>
<Transition name="drawer-backdrop">
<div
v-if="isOpen"
class="absolute inset-0 bg-black/40"
data-test="backdrop"
@click="close"
/>
</Transition>
<Transition name="drawer-panel">
<div
v-if="isOpen"
:class="twMerge(
'relative z-50 flex h-full w-full max-w-md flex-col bg-white shadow-xl',
'absolute right-0 top-0 h-full w-full max-w-md bg-white shadow-xl',
drawerClass,
)"
role="dialog"
@@ -51,18 +51,19 @@
</div>
<div
class="flex-1 overflow-y-auto px-5"
class="overflow-y-auto px-5"
style="max-height: calc(100% - 96px)"
>
<slot />
</div>
</div>
</div>
</Transition>
</Transition>
</div>
</Teleport>
</template>
<script setup lang="ts">
import { computed, ref, useAttrs, useId, watch } from 'vue'
import { computed, ref, useAttrs, useId } from 'vue'
import { Icon as IconifyIcon } from '@iconify/vue'
import { twMerge } from 'tailwind-merge'
@@ -102,12 +103,6 @@ const isOpen = computed(() =>
isControlled.value ? props.modelValue! : localValue.value,
)
const isRendered = ref(isOpen.value)
watch(isOpen, (val) => {
if (val) isRendered.value = true
})
function close() {
if (!isControlled.value) {
localValue.value = false
@@ -117,23 +112,24 @@ function close() {
</script>
<style scoped>
.drawer-enter-active,
.drawer-leave-active {
.drawer-backdrop-enter-active,
.drawer-backdrop-leave-active {
transition: opacity 0.2s ease;
}
.drawer-enter-active > div:last-child,
.drawer-leave-active > div:last-child {
transition: transform 0.3s ease;
}
.drawer-enter-from,
.drawer-leave-to {
.drawer-backdrop-enter-from,
.drawer-backdrop-leave-to {
opacity: 0;
}
.drawer-enter-from > div:last-child,
.drawer-leave-to > div:last-child {
.drawer-panel-enter-active,
.drawer-panel-leave-active {
transition: transform 0.2s ease, opacity 0.2s ease;
}
.drawer-panel-enter-from,
.drawer-panel-leave-to {
transform: translateX(100%);
opacity: 0;
}
</style>
+57 -59
View File
@@ -1,69 +1,67 @@
<template>
<div>
<div
:class="mergedGroupClass"
<div
:class="mergedGroupClass"
>
<input
:id="inputId"
:name="name"
:autocomplete="autocomplete"
:class="mergedInputClass"
:required="required"
:maxlength="maxLength"
:minlength="minLength"
:disabled="disabled"
:value="currentValue"
:readonly="readonly"
:aria-invalid="!!error"
:aria-describedby="describedBy"
v-bind="attrs"
type="text"
inputmode="decimal"
placeholder="_"
@input="onInput"
@focus="isFocused = true"
@blur="onBlur"
>
<input
:id="inputId"
:name="name"
:autocomplete="autocomplete"
:class="mergedInputClass"
:required="required"
:maxlength="maxLength"
:minlength="minLength"
:disabled="disabled"
:value="currentValue"
:readonly="readonly"
:aria-invalid="!!error"
:aria-describedby="describedBy"
v-bind="attrs"
type="text"
inputmode="decimal"
placeholder="_"
@input="onInput"
@focus="isFocused = true"
@blur="onBlur"
>
<label
v-if="label"
:for="inputId"
:class="mergedLabelClass"
>
{{ label }}
</label>
<label
v-if="label"
:for="inputId"
:class="mergedLabelClass"
>
{{ label }}
</label>
<IconifyIcon
v-if="iconName"
:icon="iconName"
:width="iconSize"
:height="iconSize"
data-test="icon"
:class="[
<IconifyIcon
v-if="iconName"
:icon="iconName"
:width="iconSize"
:height="iconSize"
data-test="icon"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success' : iconColor,
iconPositionClass,
]"
/>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${inputId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success' : iconColor,
iconPositionClass,
? 'text-m-success'
: 'text-m-muted',
'mt-1 text-xs ml-[2px] ',
]"
/>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${inputId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success'
: 'text-m-muted',
'mt-1 text-xs ml-[2px] ',
]"
>
{{ hint || error || success }}
</p>
</div>
>
{{ hint || error || success }}
</p>
</template>
<script setup lang="ts">
@@ -135,7 +133,7 @@ const isFilled = computed(() => currentValue.value.trim().length > 0)
const mergedGroupClass = computed(() =>
twMerge(
'relative flex h-12 w-full items-center',
'relative mt-4 flex h-12 w-full items-center',
props.groupClass,
),
)
+63 -65
View File
@@ -1,70 +1,68 @@
<template>
<div>
<div :class="mergedGroupClass" >
<label
v-if="label"
:for="inputId"
:class="mergedLabelClass"
>
{{ label }}
</label>
<button
type="button"
:disabled="isMinusDisabled"
@click="decrement"
>
<IconifyIcon
icon="mdi:minus"
:class="mergedButtonMinusClass"
/>
</button>
<input
:id="inputId"
:name="name"
autocomplete="off"
:class="mergedInputClass"
:style="inputWidthStyle"
:value="displayedValue"
:required="required"
:disabled="disabled"
:readonly="readonly"
:aria-invalid="!!error"
:aria-describedby="describedBy"
v-bind="attrs"
type="text"
inputmode="numeric"
placeholder="_"
@input="onInput"
@focus="isFocused = true"
@blur="isFocused = false"
>
<button
type="button"
:disabled="isPlusDisabled"
@click="increment"
>
<IconifyIcon
icon="mdi:plus"
:class="mergedButtonPlusClass"
/>
</button>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${inputId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success'
: 'text-m-muted',
'text-xs ml-[2px] ',
]"
<div :class="mergedGroupClass" >
<label
v-if="label"
:for="inputId"
:class="mergedLabelClass"
>
{{ hint || error || success }}
</p>
{{ label }}
</label>
<button
type="button"
:disabled="isMinusDisabled"
@click="decrement"
>
<IconifyIcon
icon="mdi:minus"
:class="mergedButtonMinusClass"
/>
</button>
<input
:id="inputId"
:name="name"
autocomplete="off"
:class="mergedInputClass"
:style="inputWidthStyle"
:value="displayedValue"
:required="required"
:disabled="disabled"
:readonly="readonly"
:aria-invalid="!!error"
:aria-describedby="describedBy"
v-bind="attrs"
type="text"
inputmode="numeric"
placeholder="_"
@input="onInput"
@focus="isFocused = true"
@blur="isFocused = false"
>
<button
type="button"
:disabled="isPlusDisabled"
@click="increment"
>
<IconifyIcon
icon="mdi:plus"
:class="mergedButtonPlusClass"
/>
</button>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${inputId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success'
: 'text-m-muted',
'text-xs ml-[2px] ',
]"
>
{{ hint || error || success }}
</p>
</template>
<script setup lang="ts">
@@ -172,7 +170,7 @@ const isPlusDisabled = computed(() =>
const mergedGroupClass = computed(() =>
twMerge(
'relative flex h-12 w-full items-center',
'relative mt-4 flex h-12 w-full items-center',
props.groupClass,
),
)
+57 -59
View File
@@ -1,69 +1,67 @@
<template>
<div>
<div
:class="mergedGroupClass"
<div
:class="mergedGroupClass"
>
<input
:id="inputId"
:name="name"
:autocomplete="autocomplete"
:class="mergedInputClass"
:required="required"
:maxlength="maxLength"
:minlength="minLength"
:disabled="disabled"
:value="currentValue"
:readonly="readonly"
:aria-invalid="!!error"
:aria-describedby="describedBy"
v-bind="attrs"
placeholder="_"
:type="isPasswordVisible ? 'text' : 'password'"
@input="onInput"
@focus="isFocused = true"
@blur="isFocused = false"
>
<input
:id="inputId"
:name="name"
:autocomplete="autocomplete"
:class="mergedInputClass"
:required="required"
:maxlength="maxLength"
:minlength="minLength"
:disabled="disabled"
:value="currentValue"
:readonly="readonly"
:aria-invalid="!!error"
:aria-describedby="describedBy"
v-bind="attrs"
placeholder="_"
:type="isPasswordVisible ? 'text' : 'password'"
@input="onInput"
@focus="isFocused = true"
@blur="isFocused = false"
>
<label
v-if="label"
:for="inputId"
:class="mergedLabelClass"
>
{{ label }}
</label>
<label
v-if="label"
:for="inputId"
:class="mergedLabelClass"
>
{{ label }}
</label>
<IconifyIcon
v-if="displayIcon"
:icon="isPasswordVisible ? 'mdi:eye-outline' : 'mdi:eye-off-outline'"
:width="24"
:height="24"
data-test="icon"
:class="[
<IconifyIcon
v-if="displayIcon"
:icon="isPasswordVisible ? 'mdi:eye-outline' : 'mdi:eye-off-outline'"
:width="24"
:height="24"
data-test="icon"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success' : 'text-m-muted',
'cursor-pointer absolute right-[10px] top-1/2 -translate-y-1/2',
]"
@click="toggleVisibility"
/>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${inputId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success' : 'text-m-muted',
'cursor-pointer absolute right-[10px] top-1/2 -translate-y-1/2',
? 'text-m-success'
: 'text-m-muted',
'mt-1 text-xs ml-[2px] ',
]"
@click="toggleVisibility"
/>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${inputId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success'
: 'text-m-muted',
'mt-1 text-xs ml-[2px] ',
]"
>
{{ hint || error || success }}
</p>
</div>
>
{{ hint || error || success }}
</p>
</template>
<script setup lang="ts">
@@ -134,7 +132,7 @@ const hasSuccess = computed(() => !!props.success)
const isFilled = computed(() => currentValue.value.trim().length > 0)
const mergedGroupClass = computed(() =>
twMerge(
'relative flex h-12 w-full items-center',
'relative mt-4 flex h-12 w-full items-center',
props.groupClass,
),
)
+57 -59
View File
@@ -1,69 +1,67 @@
<template>
<div>
<div
:class="mergedGroupClass"
<div
:class="mergedGroupClass"
>
<input
:id="inputId"
v-maska="mask"
:name="name"
:autocomplete="autocomplete"
:class="mergedInputClass"
:required="required"
:maxlength="maxLength"
:minlength="minLength"
:disabled="disabled"
:value="currentValue"
:readonly="readonly"
:aria-invalid="!!error"
:aria-describedby="describedBy"
v-bind="attrs"
placeholder="_"
type="text"
@input="onInput"
@focus="isFocused = true"
@blur="isFocused = false"
>
<input
:id="inputId"
v-maska="mask"
:name="name"
:autocomplete="autocomplete"
:class="mergedInputClass"
:required="required"
:maxlength="maxLength"
:minlength="minLength"
:disabled="disabled"
:value="currentValue"
:readonly="readonly"
:aria-invalid="!!error"
:aria-describedby="describedBy"
v-bind="attrs"
placeholder="_"
type="text"
@input="onInput"
@focus="isFocused = true"
@blur="isFocused = false"
>
<label
v-if="label"
:for="inputId"
:class="mergedLabelClass"
>
{{ label }}
</label>
<label
v-if="label"
:for="inputId"
:class="mergedLabelClass"
>
{{ label }}
</label>
<IconifyIcon
v-if="iconName"
:icon="iconName"
:width="iconSize"
:height="iconSize"
data-test="icon"
:class="[
<IconifyIcon
v-if="iconName"
:icon="iconName"
:width="iconSize"
:height="iconSize"
data-test="icon"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success' : iconColor,
iconPositionClass,
]"
/>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${inputId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success' : iconColor,
iconPositionClass,
? 'text-m-success'
: 'text-m-muted',
'mt-1 text-xs ml-[2px] ',
]"
/>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${inputId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success'
: 'text-m-muted',
'mt-1 text-xs ml-[2px] ',
]"
>
{{ hint || error || success }}
</p>
</div>
>
{{ hint || error || success }}
</p>
</template>
<script setup lang="ts">
@@ -140,7 +138,7 @@ const hasSuccess = computed(() => !!props.success)
const isFilled = computed(() => currentValue.value.trim().length > 0)
const mergedGroupClass = computed(() =>
twMerge(
'relative flex h-12 w-full items-center',
'relative mt-4 flex h-12 w-full items-center',
props.groupClass,
),
)
+59 -61
View File
@@ -1,72 +1,70 @@
<template>
<div>
<div
:class="mergedGroupClass"
<div
:class="mergedGroupClass"
>
<input
ref="fileInputRef"
type="file"
:accept="accept"
class="hidden"
:disabled="disabled"
@change="onFileChange"
>
<input
ref="fileInputRef"
type="file"
:accept="accept"
class="hidden"
:disabled="disabled"
@change="onFileChange"
>
<input
:id="inputId"
:class="mergedInputClass"
:disabled="disabled"
:value="currentDisplayValue"
:readonly="true"
:aria-invalid="!!error"
:aria-describedby="describedBy"
v-bind="attrs"
placeholder="_"
type="text"
@click="openFilePicker"
@focus="isFocused = true"
@blur="isFocused = false"
>
<input
:id="inputId"
:class="mergedInputClass"
:disabled="disabled"
:value="currentDisplayValue"
:readonly="true"
:aria-invalid="!!error"
:aria-describedby="describedBy"
v-bind="attrs"
placeholder="_"
type="text"
@click="openFilePicker"
@focus="isFocused = true"
@blur="isFocused = false"
>
<label
v-if="label"
:for="inputId"
:class="mergedLabelClass"
>
{{ label }}
</label>
<label
v-if="label"
:for="inputId"
:class="mergedLabelClass"
>
{{ label }}
</label>
<IconifyIcon
v-if="displayIcon"
icon="mdi:cloud-arrow-up-outline"
:width="24"
:height="24"
data-test="icon"
:class="[
<IconifyIcon
v-if="displayIcon"
icon="mdi:cloud-arrow-up-outline"
:width="24"
:height="24"
data-test="icon"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success' : 'text-m-muted',
'pointer-events-none absolute right-[10px] top-1/2 -translate-y-1/2',
]"
/>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${inputId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success' : 'text-m-muted',
'pointer-events-none absolute right-[10px] top-1/2 -translate-y-1/2',
? 'text-m-success'
: 'text-m-muted',
'mt-1 text-xs ml-[2px] ',
]"
/>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${inputId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success'
: 'text-m-muted',
'mt-1 text-xs ml-[2px] ',
]"
>
{{ hint || error || success }}
</p>
</div>
>
{{ hint || error || success }}
</p>
</template>
<script setup lang="ts">
@@ -123,7 +121,7 @@ const hasSuccess = computed(() => !!props.success)
const isFilled = computed(() => currentDisplayValue.value.trim().length > 0)
const mergedGroupClass = computed(() =>
twMerge(
'relative flex h-12 w-full items-center',
'relative mt-4 flex h-12 w-full items-center',
props.groupClass,
),
)
+20 -26
View File
@@ -1,9 +1,9 @@
<template>
<div>
<div
ref="root"
:class="mergedGroupClass"
>
<div
ref="root"
class="relative mt-4 w-full"
:class="[minWidth, maxWidth]"
>
<button
:id="buttonId"
type="button"
@@ -134,28 +134,26 @@
{{ opt.label || '\u00A0' }}
</li>
</ul>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${buttonId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success'
: 'text-m-muted',
'mt-1 ml-[2px] text-xs',
]"
>
{{ error || success || hint }}
</p>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${buttonId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success'
: 'text-m-muted',
'mt-1 ml-[2px] text-xs',
]"
>
{{ error || success || hint }}
</p>
</template>
<script setup lang="ts">
import {computed, onBeforeUnmount, onMounted, ref, useId, nextTick} from 'vue'
import {Icon as IconifyIcon} from '@iconify/vue'
import {twMerge} from 'tailwind-merge'
defineOptions({name: 'MalioSelect', inheritAttrs: false})
@@ -178,7 +176,6 @@ const props = withDefaults(defineProps<{
textLabel?: string
rounded?: string
disabled?: boolean
groupClass?: string
}>(), {
options: () => [],
emptyOptionLabel: '',
@@ -193,7 +190,6 @@ const props = withDefaults(defineProps<{
textLabel: 'text-sm',
rounded: 'rounded-md',
disabled: false,
groupClass: '',
})
const emit = defineEmits<{
@@ -212,9 +208,6 @@ const normalizedOptions = computed<Option[]>(() => [
{label: props.emptyOptionLabel, value: null},
...props.options,
])
const mergedGroupClass = computed(() =>
twMerge('relative w-full', props.minWidth, props.maxWidth, props.groupClass),
)
const hasError = computed(() => !!props.error)
const hasSuccess = computed(() => !!props.success && !hasError.value)
const isOptionSelected = computed(() =>
@@ -322,6 +315,7 @@ onBeforeUnmount(() => document.removeEventListener('mousedown', onClickOutside))
:deep(ul[role="listbox"]) {
scrollbar-width: auto;
scrollbar-gutter: stable;
}
:deep(.select-scrollbar-primary) {
+19 -21
View File
@@ -1,10 +1,9 @@
<template>
<div>
<div
ref="root"
class="relative w-full"
:class="[minWidth, maxWidth]"
>
<div
ref="root"
class="relative mt-4 w-full"
:class="[minWidth, maxWidth]"
>
<button
:id="buttonId"
type="button"
@@ -185,22 +184,21 @@
/>
</li>
</ul>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${buttonId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success'
: 'text-m-muted',
'mt-1 ml-[2px] text-xs',
]"
>
{{ error || success || hint }}
</p>
</div>
<p
v-if="hint || hasError || hasSuccess"
:id="`${buttonId}-describedby`"
:class="[
hasError
? 'text-m-danger'
: hasSuccess
? 'text-m-success'
: 'text-m-muted',
'mt-1 ml-[2px] text-xs',
]"
>
{{ error || success || hint }}
</p>
</template>
<script setup lang="ts">
+1 -2
View File
@@ -6,8 +6,7 @@
"files": [
"app/**",
"nuxt.config.ts",
"README.md",
"COMPONENTS.md"
"README.md"
],
"scripts": {
"dev": "nuxi dev .playground",