Mise en ligne de la version 0.2.0
This commit is contained in:
15
web/src/app/shared/chips-input/chips-input.component.html
Normal file
15
web/src/app/shared/chips-input/chips-input.component.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<div class="chips-wrapper">
|
||||
<span class="chip" *ngFor="let tag of value; let i = index">
|
||||
{{ tag }}
|
||||
<button type="button" class="chip-remove" (click)="remove(i)" title="Retirer">
|
||||
<lucide-icon [img]="X" [size]="12"></lucide-icon>
|
||||
</button>
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
class="chip-input"
|
||||
[placeholder]="placeholder"
|
||||
[(ngModel)]="current"
|
||||
(keydown)="onKeyDown($event)"
|
||||
(blur)="add()" />
|
||||
</div>
|
||||
53
web/src/app/shared/chips-input/chips-input.component.scss
Normal file
53
web/src/app/shared/chips-input/chips-input.component.scss
Normal file
@@ -0,0 +1,53 @@
|
||||
.chips-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.55rem 0.65rem;
|
||||
background: #1a1a2e;
|
||||
border: 1px solid #2a2a3d;
|
||||
border-radius: 6px;
|
||||
|
||||
&:focus-within {
|
||||
border-color: #6c63ff;
|
||||
}
|
||||
}
|
||||
|
||||
.chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
padding: 0.2rem 0.55rem;
|
||||
background: #1e1c3a;
|
||||
color: #a5b4fc;
|
||||
border-radius: 999px;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.2;
|
||||
|
||||
.chip-remove {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
opacity: 0.7;
|
||||
|
||||
&:hover { opacity: 1; color: #fca5a5; }
|
||||
}
|
||||
}
|
||||
|
||||
.chip-input {
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 0.88rem;
|
||||
padding: 0.15rem 0;
|
||||
outline: none;
|
||||
|
||||
&::placeholder { color: #6b7280; }
|
||||
}
|
||||
68
web/src/app/shared/chips-input/chips-input.component.ts
Normal file
68
web/src/app/shared/chips-input/chips-input.component.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { LucideAngularModule, X } from 'lucide-angular';
|
||||
|
||||
/**
|
||||
* Composant réutilisable de saisie de chips (étiquettes textuelles).
|
||||
*
|
||||
* Usage :
|
||||
* <app-chips-input
|
||||
* [value]="tags"
|
||||
* (valueChange)="tags = $event"
|
||||
* placeholder="Ajouter un tag..."></app-chips-input>
|
||||
*
|
||||
* Règles UX :
|
||||
* - Entrée valide la saisie en cours
|
||||
* - Virgule valide aussi (pratique pour enchaîner plusieurs tags)
|
||||
* - Doublons silencieusement ignorés
|
||||
* - Espaces en début/fin trimmés
|
||||
* - Clic sur X retire le chip
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-chips-input',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule, LucideAngularModule],
|
||||
templateUrl: './chips-input.component.html',
|
||||
styleUrls: ['./chips-input.component.scss']
|
||||
})
|
||||
export class ChipsInputComponent {
|
||||
readonly X = X;
|
||||
|
||||
@Input() value: string[] = [];
|
||||
@Input() placeholder = 'Ajouter...';
|
||||
@Output() valueChange = new EventEmitter<string[]>();
|
||||
|
||||
/** Texte de l'input en cours de saisie. */
|
||||
current = '';
|
||||
|
||||
/** Ajoute le texte courant s'il est non vide et non déjà présent. */
|
||||
add(): void {
|
||||
const raw = this.current.trim().replace(/,$/, '').trim();
|
||||
if (!raw) return;
|
||||
if (this.value.includes(raw)) {
|
||||
this.current = '';
|
||||
return;
|
||||
}
|
||||
this.valueChange.emit([...this.value, raw]);
|
||||
this.current = '';
|
||||
}
|
||||
|
||||
/** Retire le chip à l'index donné. */
|
||||
remove(index: number): void {
|
||||
const next = [...this.value];
|
||||
next.splice(index, 1);
|
||||
this.valueChange.emit(next);
|
||||
}
|
||||
|
||||
/** Gère Enter et virgule comme déclencheurs d'ajout. */
|
||||
onKeyDown(event: KeyboardEvent): void {
|
||||
if (event.key === 'Enter' || event.key === ',') {
|
||||
event.preventDefault();
|
||||
this.add();
|
||||
} else if (event.key === 'Backspace' && this.current === '' && this.value.length > 0) {
|
||||
// Backspace sur input vide retire le dernier chip (UX courante).
|
||||
this.remove(this.value.length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user