diff --git a/.playground/pages/composant/select/selectCheckbox.vue b/.playground/pages/composant/select/selectCheckbox.vue
index 6ab5f00..d435acd 100644
--- a/.playground/pages/composant/select/selectCheckbox.vue
+++ b/.playground/pages/composant/select/selectCheckbox.vue
@@ -17,6 +17,27 @@
empty-option-label=" "
/>
+
+
Avec tag + max tags (3)
+
+
+
+
Avec tag + couleurs
+
+
Avec tag + label
>([])
const labelValue = ref>([])
const labelValue1 = ref>([])
+const maxTagsValue = ref>(['fr', 'be', 'ch', 'ca', 'de'])
+const colorValue = ref>(['fr', 'be', 'de'])
const selectedValue = ref>(['fr'])
const hintValue = ref>([])
const errorValue = ref>([])
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5d277ea..e26c0fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -58,6 +58,7 @@ Liste des évolutions de la librairie Malio layer UI
* [#MUI-45] MalioDate : prop `markedDates` (`Record<"YYYY-MM-DD", 'success' | 'danger'>`) appliquant un fond tokenisé par jour dans la grille (générique, fourni par le consommateur ; précédence sélection/`today` > variante marquée > défaut) + event `month-change` (`{ month: 0-11, year }`) émis à l'ouverture du popover et à chaque navigation de mois. Sert l'écran *Heures* de SIRH (jours validés en vert, chargement du mois visible à la volée).
* Calendrier (Date/DateRange/DateTime/DateWeek) : sélecteur d'année (3ᵉ niveau de navigation — jours → mois → années) et grisage des mois et années hors `min`/`max`.
* MalioSidebar : slots `footer` / `footer-collapsed` pour ajouter un contenu en bas de la sidebar (profil, déconnexion, version…). Toujours collé en bas (la nav `flex-1` le pousse), reste visible quand la liste de liens scrolle ; bordure haute `m-primary` en mode déplié, à l'image du bloc logo.
+* [#MUI-49] SelectCheckbox : refonte du style des tags (fond `m-bg`, sans bordure, texte 18px/500). Nouvelle prop `maxTags` (nombre max de tags affichés, `0` = tous ; au-delà un tag `+N` résume le surplus) et champ `color` optionnel par option (couleur de fond du tag, sinon `m-bg`).
### Changed
* Cohérence du mode **`disabled`** sur toute la famille formulaire (calqué sur InputText : texte + label grisés, `cursor-not-allowed`, aucune affordance interactive). Concrètement, quand `disabled` : le **bouton « + »** d'ajout disparaît (InputPhone, InputEmail), l'**œil** de révélation disparaît (InputPassword), le **chevron** disparaît (Select, SelectCheckbox, InputAutocomplete), la **croix d'effacement** reste masquée (date, upload, time), le **label** passe en `text-m-muted` (Select, SelectCheckbox, famille Date via CalendarField, TimePicker), et les **tags** du SelectCheckbox + la valeur du Select passent en gris. (InputText, InputAmount, InputNumber, InputTextArea, InputRichText, Checkbox, RadioButton, InputUpload étaient déjà conformes.)
diff --git a/COMPONENTS.md b/COMPONENTS.md
index 2b527ce..26bed7e 100644
--- a/COMPONENTS.md
+++ b/COMPONENTS.md
@@ -427,8 +427,9 @@ Liste déroulante multi-sélection avec checkboxes.
| Prop | Type | Défaut | Description |
|------|------|--------|-------------|
| `modelValue` | `(string \| number)[]` | `[]` | Valeurs sélectionnées (v-model) |
-| `options` | `{ value: string \| number, text: string }[]` | `[]` | Options |
+| `options` | `{ value: string \| number, label: string, color?: string }[]` | `[]` | Options. `color` optionnel = couleur de fond du tag (sinon `m-bg`). |
| `displayTag` | `boolean` | `false` | Afficher les tags sélectionnés |
+| `maxTags` | `number` | `0` | Nombre max de tags affichés ; au-delà un tag `+N` résume le surplus. `0` = tous les tags. |
| `displaySelectAll` | `boolean` | `false` | Afficher "Tout sélectionner" |
| `selectAllLabel` | `string` | `'Tout sélectionner'` | Texte du sélecteur global |
| `label` | `string` | `''` | Label |
@@ -445,6 +446,8 @@ Liste déroulante multi-sélection avec checkboxes.
```vue
+
+
```
---
diff --git a/app/components/malio/select/SelectCheckbox.test.ts b/app/components/malio/select/SelectCheckbox.test.ts
index b30f7e3..19443b8 100644
--- a/app/components/malio/select/SelectCheckbox.test.ts
+++ b/app/components/malio/select/SelectCheckbox.test.ts
@@ -6,6 +6,7 @@ import SelectCheckbox from './SelectCheckbox.vue'
type Option = {
label: string
value: string | number
+ color?: string
}
type SelectCheckboxProps = {
@@ -21,6 +22,7 @@ type SelectCheckboxProps = {
textLabel?: string
rounded?: string
displayTag?: boolean
+ maxTags?: number
displaySelectAll?: boolean
selectAllLabel?: string
disabled?: boolean
@@ -380,4 +382,63 @@ describe('MalioSelectCheckbox', () => {
expect(msg.exists()).toBe(true)
expect(msg.classes()).not.toContain('min-h-[1rem]')
})
+
+ it('affiche tous les tags par défaut (maxTags non fourni)', () => {
+ const wrapper = mount(SelectCheckboxForTest, {
+ props: {modelValue: ['fr', 'be', 'ca'], options, displayTag: true},
+ })
+
+ expect(wrapper.find('[data-test="tags-overflow"]').exists()).toBe(false)
+ expect(wrapper.text()).toContain('France')
+ expect(wrapper.text()).toContain('Belgique')
+ expect(wrapper.text()).toContain('Canada')
+ })
+
+ it('limite le nombre de tags affichés et ajoute un badge +N', () => {
+ const manyOptions: Option[] = [
+ {label: 'France', value: 'fr'},
+ {label: 'Belgique', value: 'be'},
+ {label: 'Canada', value: 'ca'},
+ {label: 'Suisse', value: 'ch'},
+ {label: 'Allemagne', value: 'de'},
+ ]
+ const wrapper = mount(SelectCheckboxForTest, {
+ props: {modelValue: ['fr', 'be', 'ca', 'ch', 'de'], options: manyOptions, displayTag: true, maxTags: 3},
+ })
+
+ expect(wrapper.text()).toContain('France')
+ expect(wrapper.text()).toContain('Belgique')
+ expect(wrapper.text()).toContain('Canada')
+ expect(wrapper.text()).not.toContain('Suisse')
+ expect(wrapper.text()).not.toContain('Allemagne')
+ expect(wrapper.get('[data-test="tags-overflow"]').text()).toBe('+2')
+ })
+
+ it('n’affiche pas de badge +N quand le nombre de tags est sous la limite', () => {
+ const wrapper = mount(SelectCheckboxForTest, {
+ props: {modelValue: ['fr', 'be'], options, displayTag: true, maxTags: 3},
+ })
+
+ expect(wrapper.find('[data-test="tags-overflow"]').exists()).toBe(false)
+ })
+
+ it('applique la couleur de fond fournie par l’option', () => {
+ const wrapper = mount(SelectCheckboxForTest, {
+ props: {modelValue: ['fr'], options: [{label: 'France', value: 'fr', color: '#fde2e2'}], displayTag: true},
+ })
+
+ const tag = wrapper.findAll('span.inline-flex')[0]
+ expect(tag.attributes('style')).toContain('background-color')
+ expect(tag.classes()).not.toContain('bg-m-bg')
+ })
+
+ it('utilise bg-m-bg quand l’option n’a pas de couleur', () => {
+ const wrapper = mount(SelectCheckboxForTest, {
+ props: {modelValue: ['fr'], options, displayTag: true},
+ })
+
+ const tag = wrapper.findAll('span.inline-flex')[0]
+ expect(tag.classes()).toContain('bg-m-bg')
+ expect(tag.attributes('style')).toBeFalsy()
+ })
})
diff --git a/app/components/malio/select/SelectCheckbox.vue b/app/components/malio/select/SelectCheckbox.vue
index 5c16dd5..e06c329 100644
--- a/app/components/malio/select/SelectCheckbox.vue
+++ b/app/components/malio/select/SelectCheckbox.vue
@@ -89,13 +89,25 @@
:class="[label ? 'pt-1' : '']"
>
{{ option.label }}
+
+ +{{ hiddenTagsCount }}
+
@@ -284,6 +297,7 @@ const props = withDefaults(defineProps<{
textLabel?: string
rounded?: string
displayTag?: boolean
+ maxTags?: number
displaySelectAll?: boolean
selectAllLabel?: string
disabled?: boolean
@@ -305,6 +319,7 @@ const props = withDefaults(defineProps<{
textLabel: 'text-sm',
rounded: 'rounded-md',
displayTag: false,
+ maxTags: 0,
displaySelectAll: false,
selectAllLabel: 'Tout sélectionner',
disabled: false,
@@ -347,6 +362,12 @@ const selectedOptions = computed(() =>
const displayTags = computed(() =>
props.displayTag && selectedOptions.value.length > 0,
)
+const visibleTags = computed(() =>
+ props.maxTags > 0 ? selectedOptions.value.slice(0, props.maxTags) : selectedOptions.value,
+)
+const hiddenTagsCount = computed(() =>
+ props.maxTags > 0 ? Math.max(selectedOptions.value.length - props.maxTags, 0) : 0,
+)
const shouldFloatLabel = computed(() =>
isReadonly.value ? isOptionSelected.value : (isOpen.value || displayTags.value)
)