@kksdev/ds-angular 1.7.2 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, Component, output, HostBinding, model, signal, computed, forwardRef, effect, ElementRef, ViewChildren, ViewChild, EventEmitter, inject, HostListener, ContentChild, Output, Directive, DestroyRef, Injectable, ChangeDetectionStrategy, viewChild, Input, ViewEncapsulation } from '@angular/core';
2
+ import { input, Component, output, HostBinding, model, signal, computed, forwardRef, effect, ElementRef, ViewChildren, ViewChild, EventEmitter, inject, HostListener, ContentChild, Output, Directive, DestroyRef, Injectable, contentChildren, ChangeDetectionStrategy, viewChild, Input, ViewEncapsulation } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
- import { CommonModule, DOCUMENT, NgClass } from '@angular/common';
4
+ import { CommonModule, DOCUMENT, NgClass, NgTemplateOutlet } from '@angular/common';
5
5
  import * as i1$1 from '@fortawesome/angular-fontawesome';
6
6
  import { FaIconComponent, FontAwesomeModule } from '@fortawesome/angular-fontawesome';
7
7
  import * as i1$4 from '@angular/forms';
@@ -5215,6 +5215,82 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
5215
5215
  args: ['blur']
5216
5216
  }] } });
5217
5217
 
5218
+ let accordionItemIdCounter = 0;
5219
+ /**
5220
+ * # DsAccordionItem
5221
+ *
5222
+ * Composant enfant pour le mode template-driven de DsAccordion.
5223
+ * Permet d'injecter du contenu Angular riche (composants, HTML, etc.).
5224
+ *
5225
+ * ## Usage
5226
+ *
5227
+ * ```html
5228
+ * <ds-accordion [multiple]="true">
5229
+ * <ds-accordion-item header="Section 1" [badge]="items.length">
5230
+ * <div>Contenu riche ici</div>
5231
+ * <my-component />
5232
+ * </ds-accordion-item>
5233
+ * </ds-accordion>
5234
+ * ```
5235
+ *
5236
+ * @component
5237
+ */
5238
+ class DsAccordionItem {
5239
+ /**
5240
+ * Texte du header.
5241
+ */
5242
+ header = input('', ...(ngDevMode ? [{ debugName: "header" }] : []));
5243
+ /**
5244
+ * Badge optionnel affiché à côté du header (ex: compteur).
5245
+ */
5246
+ badge = input(undefined, ...(ngDevMode ? [{ debugName: "badge" }] : []));
5247
+ /**
5248
+ * ID unique de l'item. Auto-généré si non fourni.
5249
+ */
5250
+ id = input(`accordion-item-${++accordionItemIdCounter}`, ...(ngDevMode ? [{ debugName: "id" }] : []));
5251
+ /**
5252
+ * Désactiver cet item.
5253
+ * @default false
5254
+ */
5255
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
5256
+ /**
5257
+ * Template du contenu projeté.
5258
+ * @internal
5259
+ */
5260
+ contentTemplate;
5261
+ /**
5262
+ * État d'expansion (contrôlé par le parent DsAccordion).
5263
+ * @internal
5264
+ */
5265
+ _expanded = signal(false, ...(ngDevMode ? [{ debugName: "_expanded" }] : []));
5266
+ /**
5267
+ * Index dans la liste (injecté par le parent).
5268
+ * @internal
5269
+ */
5270
+ _index = signal(0, ...(ngDevMode ? [{ debugName: "_index" }] : []));
5271
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsAccordionItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
5272
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.15", type: DsAccordionItem, isStandalone: true, selector: "ds-accordion-item", inputs: { header: { classPropertyName: "header", publicName: "header", isSignal: true, isRequired: false, transformFunction: null }, badge: { classPropertyName: "badge", publicName: "badge", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "contentTemplate", first: true, predicate: ["contentTemplate"], descendants: true, static: true }], ngImport: i0, template: `
5273
+ <ng-template #contentTemplate>
5274
+ <ng-content />
5275
+ </ng-template>
5276
+ `, isInline: true });
5277
+ }
5278
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsAccordionItem, decorators: [{
5279
+ type: Component,
5280
+ args: [{
5281
+ selector: 'ds-accordion-item',
5282
+ standalone: true,
5283
+ template: `
5284
+ <ng-template #contentTemplate>
5285
+ <ng-content />
5286
+ </ng-template>
5287
+ `,
5288
+ }]
5289
+ }], propDecorators: { header: [{ type: i0.Input, args: [{ isSignal: true, alias: "header", required: false }] }], badge: [{ type: i0.Input, args: [{ isSignal: true, alias: "badge", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], contentTemplate: [{
5290
+ type: ViewChild,
5291
+ args: ['contentTemplate', { static: true }]
5292
+ }] } });
5293
+
5218
5294
  /**
5219
5295
  * # DsAccordion
5220
5296
  *
@@ -5242,9 +5318,11 @@ class DsAccordion {
5242
5318
  // Icône FontAwesome
5243
5319
  faChevronDown = faChevronDown;
5244
5320
  /**
5245
- * Liste des items de l'accordion.
5321
+ * Liste des items de l'accordion (mode data-driven).
5322
+ * Laisser vide pour utiliser le mode template-driven avec <ds-accordion-item>.
5323
+ * @default []
5246
5324
  */
5247
- items = input.required(...(ngDevMode ? [{ debugName: "items" }] : []));
5325
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : []));
5248
5326
  /**
5249
5327
  * Permettre l'expansion de plusieurs items simultanément.
5250
5328
  * @default false
@@ -5274,6 +5352,19 @@ class DsAccordion {
5274
5352
  * Événement émis lors du changement d'état d'un item.
5275
5353
  */
5276
5354
  itemChange = output();
5355
+ /**
5356
+ * Items enfants en mode template-driven.
5357
+ * @internal
5358
+ */
5359
+ accordionItems = contentChildren(DsAccordionItem, ...(ngDevMode ? [{ debugName: "accordionItems" }] : []));
5360
+ /**
5361
+ * Mode de fonctionnement (auto-détecté).
5362
+ * - 'data' : si [items] est fourni et non vide
5363
+ * - 'template' : si des <ds-accordion-item> enfants sont présents
5364
+ */
5365
+ mode = computed(() => {
5366
+ return this.items().length > 0 ? 'data' : 'template';
5367
+ }, ...(ngDevMode ? [{ debugName: "mode" }] : []));
5277
5368
  /**
5278
5369
  * État interne des items ouverts.
5279
5370
  */
@@ -5413,13 +5504,103 @@ class DsAccordion {
5413
5504
  getContentId(item) {
5414
5505
  return `accordion-content-${item.id}`;
5415
5506
  }
5507
+ // ==========================================
5508
+ // Mode template-driven
5509
+ // ==========================================
5510
+ /**
5511
+ * Initialiser les index des items enfants.
5512
+ */
5513
+ ngAfterContentInit() {
5514
+ this.updateTemplateItemsIndex();
5515
+ }
5516
+ /**
5517
+ * Mettre à jour les index des items template.
5518
+ */
5519
+ updateTemplateItemsIndex() {
5520
+ this.accordionItems().forEach((item, index) => {
5521
+ item._index.set(index);
5522
+ });
5523
+ }
5524
+ /**
5525
+ * Toggle un item en mode template-driven.
5526
+ */
5527
+ toggleTemplateItem(item) {
5528
+ if (this.disabled() || item.disabled())
5529
+ return;
5530
+ const itemId = item.id();
5531
+ const isCurrentlyExpanded = this.isExpanded(itemId);
5532
+ if (isCurrentlyExpanded) {
5533
+ this._expandedIds.update((set) => {
5534
+ const newSet = new Set(set);
5535
+ newSet.delete(itemId);
5536
+ return newSet;
5537
+ });
5538
+ }
5539
+ else {
5540
+ if (!this.multiple()) {
5541
+ this._expandedIds.set(new Set([itemId]));
5542
+ }
5543
+ else {
5544
+ this._expandedIds.update((set) => {
5545
+ const newSet = new Set(set);
5546
+ newSet.add(itemId);
5547
+ return newSet;
5548
+ });
5549
+ }
5550
+ }
5551
+ item._expanded.set(!isCurrentlyExpanded);
5552
+ this.itemChange.emit({
5553
+ itemId,
5554
+ expanded: !isCurrentlyExpanded,
5555
+ expandedItems: Array.from(this._expandedIds()),
5556
+ });
5557
+ }
5558
+ /**
5559
+ * Gestion du clavier en mode template-driven.
5560
+ */
5561
+ onTemplateKeydown(event, item, index) {
5562
+ const items = this.accordionItems();
5563
+ switch (event.key) {
5564
+ case 'Enter':
5565
+ case ' ':
5566
+ event.preventDefault();
5567
+ this.toggleTemplateItem(item);
5568
+ break;
5569
+ case 'ArrowDown':
5570
+ event.preventDefault();
5571
+ this.focusItem(Math.min(index + 1, items.length - 1));
5572
+ break;
5573
+ case 'ArrowUp':
5574
+ event.preventDefault();
5575
+ this.focusItem(Math.max(index - 1, 0));
5576
+ break;
5577
+ case 'Home':
5578
+ event.preventDefault();
5579
+ this.focusItem(0);
5580
+ break;
5581
+ case 'End':
5582
+ event.preventDefault();
5583
+ this.focusItem(items.length - 1);
5584
+ break;
5585
+ }
5586
+ }
5587
+ /**
5588
+ * Classes CSS pour un item template-driven.
5589
+ */
5590
+ getTemplateItemClasses(item) {
5591
+ return [
5592
+ 'ds-accordion__item',
5593
+ this.isExpanded(item.id()) ? 'ds-accordion__item--expanded' : '',
5594
+ item.disabled() ? 'ds-accordion__item--disabled' : '',
5595
+ ].filter(Boolean);
5596
+ }
5416
5597
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component });
5417
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsAccordion, isStandalone: true, selector: "ds-accordion", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, expandedIds: { classPropertyName: "expandedIds", publicName: "expandedIds", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemChange: "itemChange" }, ngImport: i0, template: "<div [ngClass]=\"containerClasses()\">\n @for (item of items(); track item.id; let i = $index) {\n <div [ngClass]=\"getItemClasses(item)\">\n <!-- Header -->\n <button\n type=\"button\"\n class=\"ds-accordion__header\"\n [id]=\"getHeaderId(item)\"\n [attr.aria-expanded]=\"isExpanded(item.id)\"\n [attr.aria-controls]=\"getContentId(item)\"\n [disabled]=\"disabled() || item.disabled\"\n (click)=\"toggleItem(item)\"\n (keydown)=\"onKeydown($event, item, i)\">\n <span class=\"ds-accordion__title\">{{ item.header }}</span>\n <fa-icon\n class=\"ds-accordion__icon\"\n [icon]=\"faChevronDown\"\n [class.ds-accordion__icon--rotated]=\"isExpanded(item.id)\"\n aria-hidden=\"true\">\n </fa-icon>\n </button>\n\n <!-- Contenu -->\n <div\n class=\"ds-accordion__content\"\n [id]=\"getContentId(item)\"\n role=\"region\"\n [attr.aria-labelledby]=\"getHeaderId(item)\"\n [attr.aria-hidden]=\"!isExpanded(item.id)\"\n [class.ds-accordion__content--expanded]=\"isExpanded(item.id)\">\n <div class=\"ds-accordion__body\">\n {{ item.content }}\n </div>\n </div>\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.ds-accordion{font-family:var(--font-family-base)}.ds-accordion__item--disabled{opacity:.5;pointer-events:none}.ds-accordion__header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:var(--accordion-header-padding-md, var(--space-4));background-color:var(--accordion-header-bg, var(--background-main));border:none;cursor:pointer;text-align:left;font-family:inherit;font-size:var(--accordion-header-font-size-md, var(--font-size-3));font-weight:var(--font-weight-medium);color:var(--accordion-header-text, var(--text-default));border-radius:0;transition:background-color var(--duration-fast) var(--easing-default),color var(--duration-fast) var(--easing-default)}.ds-accordion__header:hover:not(:disabled){background-color:var(--accordion-header-hover-bg, var(--surface-hover))}.ds-accordion__header:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--accordion-focus-color, var(--color-primary))}.ds-accordion__header:active:not(:disabled){transform:scale(.995)}.ds-accordion__header:disabled{cursor:not-allowed}.ds-accordion__title{flex:1}.ds-accordion__icon{flex-shrink:0;transition:transform var(--duration-normal) var(--easing-default);color:var(--accordion-icon-color, var(--text-muted))}.ds-accordion__icon--rotated{transform:rotate(180deg)}.ds-accordion__content{max-height:0;overflow:hidden;transition:max-height var(--duration-normal) var(--easing-default)}.ds-accordion__content--expanded{max-height:var(--accordion-content-max-height)}.ds-accordion__body{padding:var(--accordion-body-padding-md, var(--space-4));color:var(--accordion-body-text, var(--text-muted));font-size:var(--accordion-body-font-size-md, var(--font-size-2));line-height:var(--line-height-relaxed)}.ds-accordion--default .ds-accordion__item{border-bottom:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color))}.ds-accordion--default .ds-accordion__item:last-child{border-bottom:none}.ds-accordion--bordered{border:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color));border-radius:var(--accordion-radius, var(--radius-2));overflow:hidden}.ds-accordion--bordered .ds-accordion__item{border-bottom:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color))}.ds-accordion--bordered .ds-accordion__item:last-child{border-bottom:none}.ds-accordion--separated .ds-accordion__item{margin-bottom:var(--accordion-item-gap, var(--space-2));border:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color));border-radius:var(--accordion-radius, var(--radius-2));overflow:hidden}.ds-accordion--separated .ds-accordion__item:last-child{margin-bottom:0}.ds-accordion--separated .ds-accordion__item--expanded{border-color:var(--accordion-expanded-border-color, var(--color-primary))}.ds-accordion--sm .ds-accordion__header{padding:var(--accordion-header-padding-sm, var(--space-2) var(--space-3));font-size:var(--accordion-header-font-size-sm, var(--font-size-2))}.ds-accordion--sm .ds-accordion__body{padding:var(--accordion-body-padding-sm, var(--space-2) var(--space-3));font-size:var(--accordion-body-font-size-sm, var(--font-size-1))}.ds-accordion--lg .ds-accordion__header{padding:var(--accordion-header-padding-lg, var(--space-5) var(--space-6));font-size:var(--accordion-header-font-size-lg, var(--font-size-4))}.ds-accordion--lg .ds-accordion__body{padding:var(--accordion-body-padding-lg, var(--space-5) var(--space-6));font-size:var(--accordion-body-font-size-lg, var(--font-size-3))}.ds-accordion--disabled{opacity:.5;pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FontAwesomeModule }, { kind: "component", type: i1$1.FaIconComponent, selector: "fa-icon", inputs: ["icon", "title", "animation", "mask", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"], outputs: ["iconChange", "titleChange", "animationChange", "maskChange", "flipChange", "sizeChange", "pullChange", "borderChange", "inverseChange", "symbolChange", "rotateChange", "fixedWidthChange", "transformChange", "a11yRoleChange"] }] });
5598
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsAccordion, isStandalone: true, selector: "ds-accordion", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, expandedIds: { classPropertyName: "expandedIds", publicName: "expandedIds", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemChange: "itemChange" }, queries: [{ propertyName: "accordionItems", predicate: DsAccordionItem, isSignal: true }], ngImport: i0, template: "<div [ngClass]=\"containerClasses()\">\n <!-- Mode data-driven (r\u00E9trocompatible) -->\n @if (mode() === 'data') {\n @for (item of items(); track item.id; let i = $index) {\n <div [ngClass]=\"getItemClasses(item)\">\n <!-- Header -->\n <button\n type=\"button\"\n class=\"ds-accordion__header\"\n [id]=\"getHeaderId(item)\"\n [attr.aria-expanded]=\"isExpanded(item.id)\"\n [attr.aria-controls]=\"getContentId(item)\"\n [disabled]=\"disabled() || item.disabled\"\n (click)=\"toggleItem(item)\"\n (keydown)=\"onKeydown($event, item, i)\">\n <span class=\"ds-accordion__title\">{{ item.header }}</span>\n <fa-icon\n class=\"ds-accordion__icon\"\n [icon]=\"faChevronDown\"\n [class.ds-accordion__icon--rotated]=\"isExpanded(item.id)\"\n aria-hidden=\"true\">\n </fa-icon>\n </button>\n\n <!-- Contenu -->\n <div\n class=\"ds-accordion__content\"\n [id]=\"getContentId(item)\"\n role=\"region\"\n [attr.aria-labelledby]=\"getHeaderId(item)\"\n [attr.aria-hidden]=\"!isExpanded(item.id)\"\n [class.ds-accordion__content--expanded]=\"isExpanded(item.id)\">\n <div class=\"ds-accordion__body\">\n {{ item.content }}\n </div>\n </div>\n </div>\n }\n }\n\n <!-- Mode template-driven (contenu riche) -->\n @if (mode() === 'template') {\n @for (item of accordionItems(); track item.id(); let i = $index) {\n <div [ngClass]=\"getTemplateItemClasses(item)\">\n <!-- Header -->\n <button\n type=\"button\"\n class=\"ds-accordion__header\"\n [id]=\"'accordion-header-' + item.id()\"\n [attr.aria-expanded]=\"isExpanded(item.id())\"\n [attr.aria-controls]=\"'accordion-content-' + item.id()\"\n [disabled]=\"disabled() || item.disabled()\"\n (click)=\"toggleTemplateItem(item)\"\n (keydown)=\"onTemplateKeydown($event, item, i)\">\n <span class=\"ds-accordion__title\">{{ item.header() }}</span>\n @if (item.badge() !== undefined) {\n <ds-badge class=\"ds-accordion__badge\" size=\"sm\" type=\"neutral\">\n {{ item.badge() }}\n </ds-badge>\n }\n <fa-icon\n class=\"ds-accordion__icon\"\n [icon]=\"faChevronDown\"\n [class.ds-accordion__icon--rotated]=\"isExpanded(item.id())\"\n aria-hidden=\"true\">\n </fa-icon>\n </button>\n\n <!-- Contenu -->\n <div\n class=\"ds-accordion__content\"\n [id]=\"'accordion-content-' + item.id()\"\n role=\"region\"\n [attr.aria-labelledby]=\"'accordion-header-' + item.id()\"\n [attr.aria-hidden]=\"!isExpanded(item.id())\"\n [class.ds-accordion__content--expanded]=\"isExpanded(item.id())\">\n <div class=\"ds-accordion__body\">\n <ng-container *ngTemplateOutlet=\"item.contentTemplate\" />\n </div>\n </div>\n </div>\n }\n }\n</div>\n", styles: ["@charset \"UTF-8\";.ds-accordion{font-family:var(--font-family-base)}.ds-accordion__item--disabled{opacity:.5;pointer-events:none}.ds-accordion__header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:var(--accordion-header-padding-md, var(--space-4));background-color:var(--accordion-header-bg, var(--background-main));border:none;cursor:pointer;text-align:left;font-family:inherit;font-size:var(--accordion-header-font-size-md, var(--font-size-3));font-weight:var(--font-weight-medium);color:var(--accordion-header-text, var(--text-default));border-radius:0;transition:background-color var(--duration-fast) var(--easing-default),color var(--duration-fast) var(--easing-default)}.ds-accordion__header:hover:not(:disabled){background-color:var(--accordion-header-hover-bg, var(--surface-hover))}.ds-accordion__header:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--accordion-focus-color, var(--color-primary))}.ds-accordion__header:active:not(:disabled){transform:scale(.995)}.ds-accordion__header:disabled{cursor:not-allowed}.ds-accordion__title{flex:1}.ds-accordion__icon{flex-shrink:0;transition:transform var(--duration-normal) var(--easing-default);color:var(--accordion-icon-color, var(--text-muted))}.ds-accordion__icon--rotated{transform:rotate(180deg)}.ds-accordion__badge{margin-left:var(--space-2);margin-right:var(--space-2);flex-shrink:0}.ds-accordion__content{max-height:0;overflow:hidden;transition:max-height var(--duration-normal) var(--easing-default)}.ds-accordion__content--expanded{max-height:var(--accordion-content-max-height)}.ds-accordion__body{padding:var(--accordion-body-padding-md, var(--space-4));color:var(--accordion-body-text, var(--text-muted));font-size:var(--accordion-body-font-size-md, var(--font-size-2));line-height:var(--line-height-relaxed)}.ds-accordion--default .ds-accordion__item{border-bottom:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color))}.ds-accordion--default .ds-accordion__item:last-child{border-bottom:none}.ds-accordion--bordered{border:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color));border-radius:var(--accordion-radius, var(--radius-2));overflow:hidden}.ds-accordion--bordered .ds-accordion__item{border-bottom:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color))}.ds-accordion--bordered .ds-accordion__item:last-child{border-bottom:none}.ds-accordion--separated .ds-accordion__item{margin-bottom:var(--accordion-item-gap, var(--space-2));border:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color));border-radius:var(--accordion-radius, var(--radius-2));overflow:hidden}.ds-accordion--separated .ds-accordion__item:last-child{margin-bottom:0}.ds-accordion--separated .ds-accordion__item--expanded{border-color:var(--accordion-expanded-border-color, var(--color-primary))}.ds-accordion--sm .ds-accordion__header{padding:var(--accordion-header-padding-sm, var(--space-2) var(--space-3));font-size:var(--accordion-header-font-size-sm, var(--font-size-2))}.ds-accordion--sm .ds-accordion__body{padding:var(--accordion-body-padding-sm, var(--space-2) var(--space-3));font-size:var(--accordion-body-font-size-sm, var(--font-size-1))}.ds-accordion--lg .ds-accordion__header{padding:var(--accordion-header-padding-lg, var(--space-5) var(--space-6));font-size:var(--accordion-header-font-size-lg, var(--font-size-4))}.ds-accordion--lg .ds-accordion__body{padding:var(--accordion-body-padding-lg, var(--space-5) var(--space-6));font-size:var(--accordion-body-font-size-lg, var(--font-size-3))}.ds-accordion--disabled{opacity:.5;pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FontAwesomeModule }, { kind: "component", type: i1$1.FaIconComponent, selector: "fa-icon", inputs: ["icon", "title", "animation", "mask", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"], outputs: ["iconChange", "titleChange", "animationChange", "maskChange", "flipChange", "sizeChange", "pullChange", "borderChange", "inverseChange", "symbolChange", "rotateChange", "fixedWidthChange", "transformChange", "a11yRoleChange"] }, { kind: "component", type: DsBadge, selector: "ds-badge", inputs: ["type", "size", "iconStart", "iconEnd", "variant", "shape", "color"] }] });
5418
5599
  }
5419
5600
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsAccordion, decorators: [{
5420
5601
  type: Component,
5421
- args: [{ selector: 'ds-accordion', imports: [CommonModule, FontAwesomeModule], template: "<div [ngClass]=\"containerClasses()\">\n @for (item of items(); track item.id; let i = $index) {\n <div [ngClass]=\"getItemClasses(item)\">\n <!-- Header -->\n <button\n type=\"button\"\n class=\"ds-accordion__header\"\n [id]=\"getHeaderId(item)\"\n [attr.aria-expanded]=\"isExpanded(item.id)\"\n [attr.aria-controls]=\"getContentId(item)\"\n [disabled]=\"disabled() || item.disabled\"\n (click)=\"toggleItem(item)\"\n (keydown)=\"onKeydown($event, item, i)\">\n <span class=\"ds-accordion__title\">{{ item.header }}</span>\n <fa-icon\n class=\"ds-accordion__icon\"\n [icon]=\"faChevronDown\"\n [class.ds-accordion__icon--rotated]=\"isExpanded(item.id)\"\n aria-hidden=\"true\">\n </fa-icon>\n </button>\n\n <!-- Contenu -->\n <div\n class=\"ds-accordion__content\"\n [id]=\"getContentId(item)\"\n role=\"region\"\n [attr.aria-labelledby]=\"getHeaderId(item)\"\n [attr.aria-hidden]=\"!isExpanded(item.id)\"\n [class.ds-accordion__content--expanded]=\"isExpanded(item.id)\">\n <div class=\"ds-accordion__body\">\n {{ item.content }}\n </div>\n </div>\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.ds-accordion{font-family:var(--font-family-base)}.ds-accordion__item--disabled{opacity:.5;pointer-events:none}.ds-accordion__header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:var(--accordion-header-padding-md, var(--space-4));background-color:var(--accordion-header-bg, var(--background-main));border:none;cursor:pointer;text-align:left;font-family:inherit;font-size:var(--accordion-header-font-size-md, var(--font-size-3));font-weight:var(--font-weight-medium);color:var(--accordion-header-text, var(--text-default));border-radius:0;transition:background-color var(--duration-fast) var(--easing-default),color var(--duration-fast) var(--easing-default)}.ds-accordion__header:hover:not(:disabled){background-color:var(--accordion-header-hover-bg, var(--surface-hover))}.ds-accordion__header:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--accordion-focus-color, var(--color-primary))}.ds-accordion__header:active:not(:disabled){transform:scale(.995)}.ds-accordion__header:disabled{cursor:not-allowed}.ds-accordion__title{flex:1}.ds-accordion__icon{flex-shrink:0;transition:transform var(--duration-normal) var(--easing-default);color:var(--accordion-icon-color, var(--text-muted))}.ds-accordion__icon--rotated{transform:rotate(180deg)}.ds-accordion__content{max-height:0;overflow:hidden;transition:max-height var(--duration-normal) var(--easing-default)}.ds-accordion__content--expanded{max-height:var(--accordion-content-max-height)}.ds-accordion__body{padding:var(--accordion-body-padding-md, var(--space-4));color:var(--accordion-body-text, var(--text-muted));font-size:var(--accordion-body-font-size-md, var(--font-size-2));line-height:var(--line-height-relaxed)}.ds-accordion--default .ds-accordion__item{border-bottom:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color))}.ds-accordion--default .ds-accordion__item:last-child{border-bottom:none}.ds-accordion--bordered{border:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color));border-radius:var(--accordion-radius, var(--radius-2));overflow:hidden}.ds-accordion--bordered .ds-accordion__item{border-bottom:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color))}.ds-accordion--bordered .ds-accordion__item:last-child{border-bottom:none}.ds-accordion--separated .ds-accordion__item{margin-bottom:var(--accordion-item-gap, var(--space-2));border:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color));border-radius:var(--accordion-radius, var(--radius-2));overflow:hidden}.ds-accordion--separated .ds-accordion__item:last-child{margin-bottom:0}.ds-accordion--separated .ds-accordion__item--expanded{border-color:var(--accordion-expanded-border-color, var(--color-primary))}.ds-accordion--sm .ds-accordion__header{padding:var(--accordion-header-padding-sm, var(--space-2) var(--space-3));font-size:var(--accordion-header-font-size-sm, var(--font-size-2))}.ds-accordion--sm .ds-accordion__body{padding:var(--accordion-body-padding-sm, var(--space-2) var(--space-3));font-size:var(--accordion-body-font-size-sm, var(--font-size-1))}.ds-accordion--lg .ds-accordion__header{padding:var(--accordion-header-padding-lg, var(--space-5) var(--space-6));font-size:var(--accordion-header-font-size-lg, var(--font-size-4))}.ds-accordion--lg .ds-accordion__body{padding:var(--accordion-body-padding-lg, var(--space-5) var(--space-6));font-size:var(--accordion-body-font-size-lg, var(--font-size-3))}.ds-accordion--disabled{opacity:.5;pointer-events:none}\n"] }]
5422
- }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], expandedIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedIds", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], itemChange: [{ type: i0.Output, args: ["itemChange"] }] } });
5602
+ args: [{ selector: 'ds-accordion', imports: [CommonModule, FontAwesomeModule, NgTemplateOutlet, DsBadge], template: "<div [ngClass]=\"containerClasses()\">\n <!-- Mode data-driven (r\u00E9trocompatible) -->\n @if (mode() === 'data') {\n @for (item of items(); track item.id; let i = $index) {\n <div [ngClass]=\"getItemClasses(item)\">\n <!-- Header -->\n <button\n type=\"button\"\n class=\"ds-accordion__header\"\n [id]=\"getHeaderId(item)\"\n [attr.aria-expanded]=\"isExpanded(item.id)\"\n [attr.aria-controls]=\"getContentId(item)\"\n [disabled]=\"disabled() || item.disabled\"\n (click)=\"toggleItem(item)\"\n (keydown)=\"onKeydown($event, item, i)\">\n <span class=\"ds-accordion__title\">{{ item.header }}</span>\n <fa-icon\n class=\"ds-accordion__icon\"\n [icon]=\"faChevronDown\"\n [class.ds-accordion__icon--rotated]=\"isExpanded(item.id)\"\n aria-hidden=\"true\">\n </fa-icon>\n </button>\n\n <!-- Contenu -->\n <div\n class=\"ds-accordion__content\"\n [id]=\"getContentId(item)\"\n role=\"region\"\n [attr.aria-labelledby]=\"getHeaderId(item)\"\n [attr.aria-hidden]=\"!isExpanded(item.id)\"\n [class.ds-accordion__content--expanded]=\"isExpanded(item.id)\">\n <div class=\"ds-accordion__body\">\n {{ item.content }}\n </div>\n </div>\n </div>\n }\n }\n\n <!-- Mode template-driven (contenu riche) -->\n @if (mode() === 'template') {\n @for (item of accordionItems(); track item.id(); let i = $index) {\n <div [ngClass]=\"getTemplateItemClasses(item)\">\n <!-- Header -->\n <button\n type=\"button\"\n class=\"ds-accordion__header\"\n [id]=\"'accordion-header-' + item.id()\"\n [attr.aria-expanded]=\"isExpanded(item.id())\"\n [attr.aria-controls]=\"'accordion-content-' + item.id()\"\n [disabled]=\"disabled() || item.disabled()\"\n (click)=\"toggleTemplateItem(item)\"\n (keydown)=\"onTemplateKeydown($event, item, i)\">\n <span class=\"ds-accordion__title\">{{ item.header() }}</span>\n @if (item.badge() !== undefined) {\n <ds-badge class=\"ds-accordion__badge\" size=\"sm\" type=\"neutral\">\n {{ item.badge() }}\n </ds-badge>\n }\n <fa-icon\n class=\"ds-accordion__icon\"\n [icon]=\"faChevronDown\"\n [class.ds-accordion__icon--rotated]=\"isExpanded(item.id())\"\n aria-hidden=\"true\">\n </fa-icon>\n </button>\n\n <!-- Contenu -->\n <div\n class=\"ds-accordion__content\"\n [id]=\"'accordion-content-' + item.id()\"\n role=\"region\"\n [attr.aria-labelledby]=\"'accordion-header-' + item.id()\"\n [attr.aria-hidden]=\"!isExpanded(item.id())\"\n [class.ds-accordion__content--expanded]=\"isExpanded(item.id())\">\n <div class=\"ds-accordion__body\">\n <ng-container *ngTemplateOutlet=\"item.contentTemplate\" />\n </div>\n </div>\n </div>\n }\n }\n</div>\n", styles: ["@charset \"UTF-8\";.ds-accordion{font-family:var(--font-family-base)}.ds-accordion__item--disabled{opacity:.5;pointer-events:none}.ds-accordion__header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:var(--accordion-header-padding-md, var(--space-4));background-color:var(--accordion-header-bg, var(--background-main));border:none;cursor:pointer;text-align:left;font-family:inherit;font-size:var(--accordion-header-font-size-md, var(--font-size-3));font-weight:var(--font-weight-medium);color:var(--accordion-header-text, var(--text-default));border-radius:0;transition:background-color var(--duration-fast) var(--easing-default),color var(--duration-fast) var(--easing-default)}.ds-accordion__header:hover:not(:disabled){background-color:var(--accordion-header-hover-bg, var(--surface-hover))}.ds-accordion__header:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--accordion-focus-color, var(--color-primary))}.ds-accordion__header:active:not(:disabled){transform:scale(.995)}.ds-accordion__header:disabled{cursor:not-allowed}.ds-accordion__title{flex:1}.ds-accordion__icon{flex-shrink:0;transition:transform var(--duration-normal) var(--easing-default);color:var(--accordion-icon-color, var(--text-muted))}.ds-accordion__icon--rotated{transform:rotate(180deg)}.ds-accordion__badge{margin-left:var(--space-2);margin-right:var(--space-2);flex-shrink:0}.ds-accordion__content{max-height:0;overflow:hidden;transition:max-height var(--duration-normal) var(--easing-default)}.ds-accordion__content--expanded{max-height:var(--accordion-content-max-height)}.ds-accordion__body{padding:var(--accordion-body-padding-md, var(--space-4));color:var(--accordion-body-text, var(--text-muted));font-size:var(--accordion-body-font-size-md, var(--font-size-2));line-height:var(--line-height-relaxed)}.ds-accordion--default .ds-accordion__item{border-bottom:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color))}.ds-accordion--default .ds-accordion__item:last-child{border-bottom:none}.ds-accordion--bordered{border:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color));border-radius:var(--accordion-radius, var(--radius-2));overflow:hidden}.ds-accordion--bordered .ds-accordion__item{border-bottom:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color))}.ds-accordion--bordered .ds-accordion__item:last-child{border-bottom:none}.ds-accordion--separated .ds-accordion__item{margin-bottom:var(--accordion-item-gap, var(--space-2));border:var(--accordion-border-width) solid var(--accordion-border-color, var(--border-color));border-radius:var(--accordion-radius, var(--radius-2));overflow:hidden}.ds-accordion--separated .ds-accordion__item:last-child{margin-bottom:0}.ds-accordion--separated .ds-accordion__item--expanded{border-color:var(--accordion-expanded-border-color, var(--color-primary))}.ds-accordion--sm .ds-accordion__header{padding:var(--accordion-header-padding-sm, var(--space-2) var(--space-3));font-size:var(--accordion-header-font-size-sm, var(--font-size-2))}.ds-accordion--sm .ds-accordion__body{padding:var(--accordion-body-padding-sm, var(--space-2) var(--space-3));font-size:var(--accordion-body-font-size-sm, var(--font-size-1))}.ds-accordion--lg .ds-accordion__header{padding:var(--accordion-header-padding-lg, var(--space-5) var(--space-6));font-size:var(--accordion-header-font-size-lg, var(--font-size-4))}.ds-accordion--lg .ds-accordion__body{padding:var(--accordion-body-padding-lg, var(--space-5) var(--space-6));font-size:var(--accordion-body-font-size-lg, var(--font-size-3))}.ds-accordion--disabled{opacity:.5;pointer-events:none}\n"] }]
5603
+ }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], expandedIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedIds", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], itemChange: [{ type: i0.Output, args: ["itemChange"] }], accordionItems: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DsAccordionItem), { isSignal: true }] }] } });
5423
5604
 
5424
5605
  /**
5425
5606
  * # DsPagination
@@ -15929,6 +16110,611 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
15929
16110
  args: [{ selector: 'ds-list-group', standalone: true, imports: [CommonModule, FontAwesomeModule, DsBadge], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"groupClasses()\">\n <!-- Header du groupe -->\n <div\n class=\"ds-list-group__header\"\n [attr.role]=\"isCollapsible() ? 'button' : null\"\n [attr.tabindex]=\"isCollapsible() ? 0 : null\"\n [attr.aria-expanded]=\"isCollapsible() ? isExpanded() : null\"\n (click)=\"toggle()\"\n (keydown)=\"handleKeyDown($event)\"\n >\n <!-- Chevron (collapsible uniquement) -->\n @if (isCollapsible()) {\n <fa-icon\n [icon]=\"chevronIcon()\"\n class=\"ds-list-group__chevron\"\n [class.ds-list-group__chevron--expanded]=\"isExpanded()\"\n />\n }\n\n <!-- Ic\u00F4ne personnalis\u00E9e -->\n @if (icon()) {\n <fa-icon [icon]=\"icon()!\" class=\"ds-list-group__icon\" />\n }\n\n <!-- Titre -->\n <span class=\"ds-list-group__title\">{{ title() }}</span>\n\n <!-- Sous-titre -->\n @if (subtitle()) {\n <span class=\"ds-list-group__subtitle\">{{ subtitle() }}</span>\n }\n\n <!-- Compteur (badge) -->\n @if (showCount()) {\n <ds-badge type=\"neutral\" size=\"sm\" class=\"ds-list-group__count\">\n {{ count() }}\n </ds-badge>\n }\n </div>\n\n <!-- Contenu du groupe -->\n @if (isExpanded()) {\n <div\n class=\"ds-list-group__content\"\n role=\"group\"\n [attr.aria-label]=\"title()\"\n >\n <ng-content />\n </div>\n }\n</div>\n", styles: [":host{display:block}.ds-list-group__header{display:flex;align-items:center;gap:var(--space-2, 8px);padding:var(--space-2, 8px) var(--space-3, 12px);font-size:var(--font-size-1, 12px);font-weight:var(--font-weight-semibold, 600);color:var(--text-muted, #9ca3af);text-transform:uppercase;letter-spacing:.05em;-webkit-user-select:none;user-select:none}.ds-list-group--collapsible .ds-list-group__header{cursor:pointer;border-radius:var(--radius-1, 4px);transition:background-color .15s ease}.ds-list-group--collapsible .ds-list-group__header:hover{background-color:var(--background-hover, rgba(255, 255, 255, .05))}.ds-list-group--collapsible .ds-list-group__header:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:-2px}.ds-list-group--sticky .ds-list-group__header{position:sticky;top:0;background:var(--background-main, #111827);z-index:10;border-bottom:1px solid var(--border-default, #374151)}.ds-list-group__chevron{font-size:10px;color:var(--text-muted, #9ca3af);transition:transform .2s ease;flex-shrink:0}.ds-list-group__chevron--expanded,.ds-list-group--collapsed .ds-list-group__chevron{transform:rotate(0)}.ds-list-group__icon{font-size:var(--font-size-2, 14px);color:var(--text-muted, #9ca3af);flex-shrink:0}.ds-list-group__title{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ds-list-group__subtitle{font-weight:var(--font-weight-normal, 400);color:var(--text-muted, #6b7280);text-transform:none;letter-spacing:normal}.ds-list-group__count{flex-shrink:0}.ds-list-group--collapsed .ds-list-group__content{display:none}\n"] }]
15930
16111
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }], expandedChange: [{ type: i0.Output, args: ["expandedChange"] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }] } });
15931
16112
 
16113
+ /**
16114
+ * Chip coloré pour afficher une entité sélectionnée dans ds-entity-picker
16115
+ *
16116
+ * @example
16117
+ * ```html
16118
+ * <ds-entity-chip
16119
+ * [option]="{ value: '1', label: 'Important', color: '#ef4444', emoji: '🏷️' }"
16120
+ * [removable]="true"
16121
+ * (removed)="onRemove()"
16122
+ * />
16123
+ * ```
16124
+ */
16125
+ class DsEntityChip {
16126
+ /** Option à afficher */
16127
+ option = input.required(...(ngDevMode ? [{ debugName: "option" }] : []));
16128
+ /** Taille du chip */
16129
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
16130
+ /** Afficher le bouton de suppression */
16131
+ removable = input(true, ...(ngDevMode ? [{ debugName: "removable" }] : []));
16132
+ /** Chip désactivé */
16133
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
16134
+ /** Événement de suppression */
16135
+ removed = output();
16136
+ /** Icône de fermeture */
16137
+ closeIcon = faXmark;
16138
+ /** Couleur du chip (depuis l'option ou fallback) */
16139
+ chipColor = computed(() => {
16140
+ return this.option().color || 'var(--gray-400)';
16141
+ }, ...(ngDevMode ? [{ debugName: "chipColor" }] : []));
16142
+ /** Couleur de fond (10% opacity) */
16143
+ backgroundColor = computed(() => {
16144
+ const color = this.option().color;
16145
+ if (!color) {
16146
+ return 'var(--gray-100)';
16147
+ }
16148
+ // Utilise color-mix pour créer une version transparente
16149
+ return `color-mix(in srgb, ${color} 12%, transparent)`;
16150
+ }, ...(ngDevMode ? [{ debugName: "backgroundColor" }] : []));
16151
+ /** Gestion du clic sur le bouton supprimer */
16152
+ handleRemove(event) {
16153
+ event.stopPropagation();
16154
+ event.preventDefault();
16155
+ if (this.disabled()) {
16156
+ return;
16157
+ }
16158
+ this.removed.emit(this.option());
16159
+ }
16160
+ /** Gestion du clavier */
16161
+ handleKeyDown(event) {
16162
+ if (this.disabled()) {
16163
+ return;
16164
+ }
16165
+ if (this.removable() && (event.key === 'Delete' || event.key === 'Backspace')) {
16166
+ event.preventDefault();
16167
+ this.removed.emit(this.option());
16168
+ }
16169
+ }
16170
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsEntityChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
16171
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsEntityChip, isStandalone: true, selector: "ds-entity-chip", inputs: { option: { classPropertyName: "option", publicName: "option", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { removed: "removed" }, ngImport: i0, template: `
16172
+ <div
16173
+ class="ds-entity-chip"
16174
+ [class.ds-entity-chip--sm]="size() === 'sm'"
16175
+ [class.ds-entity-chip--md]="size() === 'md'"
16176
+ [class.ds-entity-chip--lg]="size() === 'lg'"
16177
+ [class.ds-entity-chip--disabled]="disabled()"
16178
+ [style.--chip-color]="chipColor()"
16179
+ [style.background-color]="backgroundColor()"
16180
+ [style.border-color]="chipColor()"
16181
+ [attr.tabindex]="disabled() ? -1 : 0"
16182
+ (keydown)="handleKeyDown($event)"
16183
+ >
16184
+ @if (option().emoji) {
16185
+ <span class="ds-entity-chip__emoji">{{ option().emoji }}</span>
16186
+ } @else if (option().icon) {
16187
+ <fa-icon
16188
+ class="ds-entity-chip__icon"
16189
+ [icon]="option().icon!"
16190
+ [fixedWidth]="true"
16191
+ [style.color]="chipColor()"
16192
+ />
16193
+ }
16194
+
16195
+ <span class="ds-entity-chip__label">{{ option().label }}</span>
16196
+
16197
+ @if (removable() && !disabled()) {
16198
+ <button
16199
+ type="button"
16200
+ class="ds-entity-chip__remove"
16201
+ [attr.aria-label]="'Supprimer ' + option().label"
16202
+ (click)="handleRemove($event)"
16203
+ >
16204
+ <fa-icon [icon]="closeIcon" [fixedWidth]="true" />
16205
+ </button>
16206
+ }
16207
+ </div>
16208
+ `, isInline: true, styles: [".ds-entity-chip{display:inline-flex;align-items:center;gap:var(--space-1, .25rem);padding:var(--space-1, .25rem) var(--space-2, .5rem);border-radius:var(--radius-md, 6px);font-family:var(--font-family-base, sans-serif);font-size:var(--font-size-sm, .875rem);font-weight:500;line-height:1.4;border:1px solid var(--chip-color, var(--gray-300));background-color:color-mix(in srgb,var(--chip-color, var(--gray-300)) 10%,transparent);color:var(--text-default, #1a1a1a);transition:background-color var(--duration-fast, .15s) var(--easing-default, ease),transform var(--duration-fast, .15s) var(--easing-default, ease);-webkit-user-select:none;user-select:none;outline:none;max-width:200px}.ds-entity-chip:focus-visible{box-shadow:0 0 0 2px var(--background-main, #fff),0 0 0 4px var(--chip-color, var(--color-primary))}.ds-entity-chip__emoji{flex-shrink:0;font-size:1em}.ds-entity-chip__icon{flex-shrink:0;font-size:.875em}.ds-entity-chip__label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}.ds-entity-chip__remove{display:flex;align-items:center;justify-content:center;padding:var(--space-0-5, .125rem);margin:0;margin-left:var(--space-1, .25rem);background:none;border:none;border-radius:var(--radius-sm, 4px);cursor:pointer;color:var(--chip-color, var(--gray-600));opacity:.7;transition:opacity var(--duration-fast, .15s) var(--easing-default, ease),background-color var(--duration-fast, .15s) var(--easing-default, ease),transform var(--duration-fast, .15s) var(--easing-default, ease);flex-shrink:0}.ds-entity-chip__remove:hover{opacity:1;background-color:color-mix(in srgb,var(--chip-color, var(--gray-300)) 20%,transparent)}.ds-entity-chip__remove:active{transform:scale(.9)}.ds-entity-chip__remove:focus-visible{outline:none;opacity:1;box-shadow:0 0 0 2px var(--chip-color, var(--color-primary))}.ds-entity-chip__remove fa-icon{font-size:.75rem}.ds-entity-chip--sm{padding:var(--space-0-5, .125rem) var(--space-1-5, .375rem);font-size:var(--font-size-xs, .75rem);gap:var(--space-0-5, .125rem);max-width:150px}.ds-entity-chip--sm .ds-entity-chip__remove{padding:.0625rem}.ds-entity-chip--sm .ds-entity-chip__remove fa-icon{font-size:.625rem}.ds-entity-chip--lg{padding:var(--space-1-5, .375rem) var(--space-3, .75rem);font-size:var(--font-size-base, 1rem);gap:var(--space-2, .5rem);max-width:250px}.ds-entity-chip--lg .ds-entity-chip__remove fa-icon{font-size:.875rem}.ds-entity-chip--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FontAwesomeModule }, { kind: "component", type: i1$1.FaIconComponent, selector: "fa-icon", inputs: ["icon", "title", "animation", "mask", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"], outputs: ["iconChange", "titleChange", "animationChange", "maskChange", "flipChange", "sizeChange", "pullChange", "borderChange", "inverseChange", "symbolChange", "rotateChange", "fixedWidthChange", "transformChange", "a11yRoleChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
16209
+ }
16210
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsEntityChip, decorators: [{
16211
+ type: Component,
16212
+ args: [{ selector: 'ds-entity-chip', standalone: true, imports: [CommonModule, FontAwesomeModule], template: `
16213
+ <div
16214
+ class="ds-entity-chip"
16215
+ [class.ds-entity-chip--sm]="size() === 'sm'"
16216
+ [class.ds-entity-chip--md]="size() === 'md'"
16217
+ [class.ds-entity-chip--lg]="size() === 'lg'"
16218
+ [class.ds-entity-chip--disabled]="disabled()"
16219
+ [style.--chip-color]="chipColor()"
16220
+ [style.background-color]="backgroundColor()"
16221
+ [style.border-color]="chipColor()"
16222
+ [attr.tabindex]="disabled() ? -1 : 0"
16223
+ (keydown)="handleKeyDown($event)"
16224
+ >
16225
+ @if (option().emoji) {
16226
+ <span class="ds-entity-chip__emoji">{{ option().emoji }}</span>
16227
+ } @else if (option().icon) {
16228
+ <fa-icon
16229
+ class="ds-entity-chip__icon"
16230
+ [icon]="option().icon!"
16231
+ [fixedWidth]="true"
16232
+ [style.color]="chipColor()"
16233
+ />
16234
+ }
16235
+
16236
+ <span class="ds-entity-chip__label">{{ option().label }}</span>
16237
+
16238
+ @if (removable() && !disabled()) {
16239
+ <button
16240
+ type="button"
16241
+ class="ds-entity-chip__remove"
16242
+ [attr.aria-label]="'Supprimer ' + option().label"
16243
+ (click)="handleRemove($event)"
16244
+ >
16245
+ <fa-icon [icon]="closeIcon" [fixedWidth]="true" />
16246
+ </button>
16247
+ }
16248
+ </div>
16249
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".ds-entity-chip{display:inline-flex;align-items:center;gap:var(--space-1, .25rem);padding:var(--space-1, .25rem) var(--space-2, .5rem);border-radius:var(--radius-md, 6px);font-family:var(--font-family-base, sans-serif);font-size:var(--font-size-sm, .875rem);font-weight:500;line-height:1.4;border:1px solid var(--chip-color, var(--gray-300));background-color:color-mix(in srgb,var(--chip-color, var(--gray-300)) 10%,transparent);color:var(--text-default, #1a1a1a);transition:background-color var(--duration-fast, .15s) var(--easing-default, ease),transform var(--duration-fast, .15s) var(--easing-default, ease);-webkit-user-select:none;user-select:none;outline:none;max-width:200px}.ds-entity-chip:focus-visible{box-shadow:0 0 0 2px var(--background-main, #fff),0 0 0 4px var(--chip-color, var(--color-primary))}.ds-entity-chip__emoji{flex-shrink:0;font-size:1em}.ds-entity-chip__icon{flex-shrink:0;font-size:.875em}.ds-entity-chip__label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}.ds-entity-chip__remove{display:flex;align-items:center;justify-content:center;padding:var(--space-0-5, .125rem);margin:0;margin-left:var(--space-1, .25rem);background:none;border:none;border-radius:var(--radius-sm, 4px);cursor:pointer;color:var(--chip-color, var(--gray-600));opacity:.7;transition:opacity var(--duration-fast, .15s) var(--easing-default, ease),background-color var(--duration-fast, .15s) var(--easing-default, ease),transform var(--duration-fast, .15s) var(--easing-default, ease);flex-shrink:0}.ds-entity-chip__remove:hover{opacity:1;background-color:color-mix(in srgb,var(--chip-color, var(--gray-300)) 20%,transparent)}.ds-entity-chip__remove:active{transform:scale(.9)}.ds-entity-chip__remove:focus-visible{outline:none;opacity:1;box-shadow:0 0 0 2px var(--chip-color, var(--color-primary))}.ds-entity-chip__remove fa-icon{font-size:.75rem}.ds-entity-chip--sm{padding:var(--space-0-5, .125rem) var(--space-1-5, .375rem);font-size:var(--font-size-xs, .75rem);gap:var(--space-0-5, .125rem);max-width:150px}.ds-entity-chip--sm .ds-entity-chip__remove{padding:.0625rem}.ds-entity-chip--sm .ds-entity-chip__remove fa-icon{font-size:.625rem}.ds-entity-chip--lg{padding:var(--space-1-5, .375rem) var(--space-3, .75rem);font-size:var(--font-size-base, 1rem);gap:var(--space-2, .5rem);max-width:250px}.ds-entity-chip--lg .ds-entity-chip__remove fa-icon{font-size:.875rem}.ds-entity-chip--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}\n"] }]
16250
+ }], propDecorators: { option: [{ type: i0.Input, args: [{ isSignal: true, alias: "option", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], removable: [{ type: i0.Input, args: [{ isSignal: true, alias: "removable", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], removed: [{ type: i0.Output, args: ["removed"] }] } });
16251
+
16252
+ /**
16253
+ * Entity Picker - Sélecteur d'entités riches
16254
+ *
16255
+ * Permet la sélection d'entités avec icônes, couleurs, emojis.
16256
+ * Supporte la sélection simple et multiple avec affichage en chips colorés.
16257
+ *
16258
+ * @example
16259
+ * ```html
16260
+ * <!-- Sélection simple -->
16261
+ * <ds-entity-picker
16262
+ * [options]="tags"
16263
+ * [(ngModel)]="selectedTag"
16264
+ * placeholder="Choisir un tag"
16265
+ * />
16266
+ *
16267
+ * <!-- Sélection multiple -->
16268
+ * <ds-entity-picker
16269
+ * [options]="categories"
16270
+ * [multiple]="true"
16271
+ * [(ngModel)]="selectedCategories"
16272
+ * [allowCreate]="true"
16273
+ * (createRequested)="onCreateCategory($event)"
16274
+ * />
16275
+ * ```
16276
+ */
16277
+ class DsEntityPicker {
16278
+ // ─────────────────────────────────────────────────────────────────────────────
16279
+ // Inputs
16280
+ // ─────────────────────────────────────────────────────────────────────────────
16281
+ /** Liste des options disponibles */
16282
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
16283
+ /** Active la sélection multiple */
16284
+ multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : []));
16285
+ /** Permet la création d'options à la volée */
16286
+ allowCreate = input(false, ...(ngDevMode ? [{ debugName: "allowCreate" }] : []));
16287
+ /** Placeholder du champ de recherche */
16288
+ placeholder = input('Rechercher...', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
16289
+ /** Taille du composant */
16290
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
16291
+ /** Mode d'affichage des sélections multiples */
16292
+ displayMode = input('chip', ...(ngDevMode ? [{ debugName: "displayMode" }] : []));
16293
+ /** Afficher le bouton clear */
16294
+ clearable = input(true, ...(ngDevMode ? [{ debugName: "clearable" }] : []));
16295
+ /** Nombre maximum de sélections (multi-select) */
16296
+ maxSelections = input(undefined, ...(ngDevMode ? [{ debugName: "maxSelections" }] : []));
16297
+ /** Label du champ */
16298
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
16299
+ /** Message d'erreur */
16300
+ error = input('', ...(ngDevMode ? [{ debugName: "error" }] : []));
16301
+ /** Texte d'aide */
16302
+ helper = input('', ...(ngDevMode ? [{ debugName: "helper" }] : []));
16303
+ /** Champ désactivé */
16304
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
16305
+ /** Champ obligatoire */
16306
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
16307
+ /** Caractères minimum avant filtrage */
16308
+ minChars = input(0, ...(ngDevMode ? [{ debugName: "minChars" }] : []));
16309
+ /** Texte si aucun résultat */
16310
+ noResultsText = input('Aucun résultat', ...(ngDevMode ? [{ debugName: "noResultsText" }] : []));
16311
+ /** Template du texte de création (utiliser {query} comme placeholder) */
16312
+ createText = input('Créer "{query}"', ...(ngDevMode ? [{ debugName: "createText" }] : []));
16313
+ /** Nom du champ (pour formulaires) */
16314
+ name = input('', ...(ngDevMode ? [{ debugName: "name" }] : []));
16315
+ // ─────────────────────────────────────────────────────────────────────────────
16316
+ // Outputs
16317
+ // ─────────────────────────────────────────────────────────────────────────────
16318
+ /** Émis lors d'un changement de sélection */
16319
+ selectionChange = output();
16320
+ /** Émis lors d'une demande de création */
16321
+ createRequested = output();
16322
+ /** Émis lors d'un changement de recherche */
16323
+ searchChange = output();
16324
+ /** Émis à l'ouverture du panel */
16325
+ opened = output();
16326
+ /** Émis à la fermeture du panel */
16327
+ closed = output();
16328
+ // ─────────────────────────────────────────────────────────────────────────────
16329
+ // Internal state
16330
+ // ─────────────────────────────────────────────────────────────────────────────
16331
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
16332
+ searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : []));
16333
+ focusedIndex = signal(-1, ...(ngDevMode ? [{ debugName: "focusedIndex" }] : []));
16334
+ internalValue = signal(null, ...(ngDevMode ? [{ debugName: "internalValue" }] : []));
16335
+ cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : []));
16336
+ inputElement;
16337
+ listbox;
16338
+ onChange = () => { };
16339
+ onTouched = () => { };
16340
+ // Icons
16341
+ iconPlus = faPlus;
16342
+ iconChevron = faChevronDown;
16343
+ iconClear = faXmark;
16344
+ iconSearch = faSearch;
16345
+ // CDK Overlay positions
16346
+ overlayPositions = AUTOCOMPLETE_POSITIONS;
16347
+ // ─────────────────────────────────────────────────────────────────────────────
16348
+ // Computed
16349
+ // ─────────────────────────────────────────────────────────────────────────────
16350
+ isDisabled = computed(() => this.disabled() || this.cvaDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
16351
+ /** Options sélectionnées (objets complets) */
16352
+ selectedOptions = computed(() => {
16353
+ const value = this.internalValue();
16354
+ const opts = this.options();
16355
+ if (value === null)
16356
+ return [];
16357
+ if (this.multiple()) {
16358
+ const values = Array.isArray(value) ? value : [value];
16359
+ return opts.filter(opt => values.includes(opt.value));
16360
+ }
16361
+ else {
16362
+ const found = opts.find(opt => opt.value === value);
16363
+ return found ? [found] : [];
16364
+ }
16365
+ }, ...(ngDevMode ? [{ debugName: "selectedOptions" }] : []));
16366
+ /** Texte affiché dans l'input (mode single) */
16367
+ displayValue = computed(() => {
16368
+ if (this.multiple())
16369
+ return '';
16370
+ const selected = this.selectedOptions();
16371
+ return selected.length > 0 ? selected[0].label : '';
16372
+ }, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
16373
+ /** Options filtrées selon la recherche */
16374
+ filteredOptions = computed(() => {
16375
+ const query = this.searchQuery().toLowerCase().trim();
16376
+ const minLen = this.minChars();
16377
+ const selected = this.selectedOptions().map(o => o.value);
16378
+ if (query.length < minLen) {
16379
+ // En multi-select, exclure les options déjà sélectionnées
16380
+ if (this.multiple()) {
16381
+ return this.options().filter(opt => !selected.includes(opt.value));
16382
+ }
16383
+ return this.options();
16384
+ }
16385
+ let results = this.options().filter(opt => {
16386
+ const matchLabel = opt.label.toLowerCase().includes(query);
16387
+ const matchDesc = opt.description?.toLowerCase().includes(query);
16388
+ return matchLabel || matchDesc;
16389
+ });
16390
+ // En multi-select, exclure les options déjà sélectionnées
16391
+ if (this.multiple()) {
16392
+ results = results.filter(opt => !selected.includes(opt.value));
16393
+ }
16394
+ return results;
16395
+ }, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : []));
16396
+ /** Peut créer une nouvelle option */
16397
+ canCreate = computed(() => {
16398
+ if (!this.allowCreate())
16399
+ return false;
16400
+ const query = this.searchQuery().trim();
16401
+ if (!query)
16402
+ return false;
16403
+ // Vérifie si l'option n'existe pas déjà
16404
+ const exists = this.options().some(opt => opt.label.toLowerCase() === query.toLowerCase());
16405
+ return !exists;
16406
+ }, ...(ngDevMode ? [{ debugName: "canCreate" }] : []));
16407
+ /** Afficher le dropdown */
16408
+ shouldShowDropdown = computed(() => {
16409
+ return this.isOpen();
16410
+ }, ...(ngDevMode ? [{ debugName: "shouldShowDropdown" }] : []));
16411
+ /** Classes du container */
16412
+ containerClasses = computed(() => ({
16413
+ 'ds-entity-picker': true,
16414
+ 'ds-entity-picker--open': this.isOpen(),
16415
+ 'ds-entity-picker--disabled': this.isDisabled(),
16416
+ 'ds-entity-picker--error': !!this.error(),
16417
+ 'ds-entity-picker--multiple': this.multiple(),
16418
+ [`ds-entity-picker--${this.size()}`]: true,
16419
+ }), ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
16420
+ /** Classes de l'input */
16421
+ inputClasses = computed(() => ({
16422
+ 'ds-entity-picker__input': true,
16423
+ 'ds-entity-picker__input--has-value': this.multiple()
16424
+ ? this.selectedOptions().length > 0
16425
+ : !!this.internalValue(),
16426
+ }), ...(ngDevMode ? [{ debugName: "inputClasses" }] : []));
16427
+ /** ID unique pour le listbox */
16428
+ listboxId = computed(() => `${this.name() || 'entity-picker'}-listbox`, ...(ngDevMode ? [{ debugName: "listboxId" }] : []));
16429
+ /** ID unique pour l'input */
16430
+ inputId = computed(() => `${this.name() || 'entity-picker'}-input`, ...(ngDevMode ? [{ debugName: "inputId" }] : []));
16431
+ /** Texte du compteur (mode count) */
16432
+ countText = computed(() => {
16433
+ const count = this.selectedOptions().length;
16434
+ if (count === 0)
16435
+ return '';
16436
+ return `${count} sélectionné${count > 1 ? 's' : ''}`;
16437
+ }, ...(ngDevMode ? [{ debugName: "countText" }] : []));
16438
+ /** Limite de sélection atteinte */
16439
+ maxReached = computed(() => {
16440
+ const max = this.maxSelections();
16441
+ if (max === undefined)
16442
+ return false;
16443
+ return this.selectedOptions().length >= max;
16444
+ }, ...(ngDevMode ? [{ debugName: "maxReached" }] : []));
16445
+ /** Texte formaté pour l'option de création */
16446
+ formattedCreateText = computed(() => {
16447
+ return this.createText().replace('{query}', this.searchQuery());
16448
+ }, ...(ngDevMode ? [{ debugName: "formattedCreateText" }] : []));
16449
+ // ─────────────────────────────────────────────────────────────────────────────
16450
+ // ControlValueAccessor
16451
+ // ─────────────────────────────────────────────────────────────────────────────
16452
+ writeValue(value) {
16453
+ this.internalValue.set(value);
16454
+ }
16455
+ registerOnChange(fn) {
16456
+ this.onChange = fn;
16457
+ }
16458
+ registerOnTouched(fn) {
16459
+ this.onTouched = fn;
16460
+ }
16461
+ setDisabledState(isDisabled) {
16462
+ this.cvaDisabled.set(isDisabled);
16463
+ }
16464
+ // ─────────────────────────────────────────────────────────────────────────────
16465
+ // Public methods
16466
+ // ─────────────────────────────────────────────────────────────────────────────
16467
+ onInputFocus() {
16468
+ if (this.isDisabled())
16469
+ return;
16470
+ this.open();
16471
+ }
16472
+ onInputBlur() {
16473
+ this.onTouched();
16474
+ // Fermeture retardée pour permettre le clic sur une option
16475
+ setTimeout(() => {
16476
+ if (!this.isOpen())
16477
+ return;
16478
+ this.close();
16479
+ }, 200);
16480
+ }
16481
+ onSearchInput(event) {
16482
+ const input = event.target;
16483
+ this.searchQuery.set(input.value);
16484
+ this.searchChange.emit(input.value);
16485
+ this.focusedIndex.set(0);
16486
+ if (!this.isOpen() && input.value.length >= this.minChars()) {
16487
+ this.open();
16488
+ }
16489
+ }
16490
+ open() {
16491
+ if (this.isDisabled() || this.isOpen())
16492
+ return;
16493
+ this.isOpen.set(true);
16494
+ this.focusedIndex.set(0);
16495
+ this.opened.emit();
16496
+ }
16497
+ close() {
16498
+ if (!this.isOpen())
16499
+ return;
16500
+ this.isOpen.set(false);
16501
+ this.searchQuery.set('');
16502
+ this.focusedIndex.set(-1);
16503
+ this.closed.emit();
16504
+ }
16505
+ selectOption(option) {
16506
+ if (option.disabled || this.isDisabled())
16507
+ return;
16508
+ if (this.multiple()) {
16509
+ // Mode multi-sélection
16510
+ const current = this.internalValue() || [];
16511
+ const newValues = [...current, option.value];
16512
+ this.internalValue.set(newValues);
16513
+ this.onChange(newValues);
16514
+ // Émettre les options complètes
16515
+ const selectedOpts = this.options().filter(o => newValues.includes(o.value));
16516
+ this.selectionChange.emit(selectedOpts);
16517
+ // Reset recherche mais garde le panel ouvert
16518
+ this.searchQuery.set('');
16519
+ this.inputElement?.nativeElement?.focus();
16520
+ }
16521
+ else {
16522
+ // Mode sélection simple
16523
+ this.internalValue.set(option.value);
16524
+ this.onChange(option.value);
16525
+ this.selectionChange.emit(option);
16526
+ this.close();
16527
+ }
16528
+ }
16529
+ removeOption(option) {
16530
+ if (!this.multiple() || this.isDisabled())
16531
+ return;
16532
+ const current = this.internalValue() || [];
16533
+ const newValues = current.filter(v => v !== option.value);
16534
+ this.internalValue.set(newValues.length > 0 ? newValues : null);
16535
+ this.onChange(newValues.length > 0 ? newValues : null);
16536
+ const selectedOpts = this.options().filter(o => newValues.includes(o.value));
16537
+ this.selectionChange.emit(selectedOpts.length > 0 ? selectedOpts : null);
16538
+ }
16539
+ clear(event) {
16540
+ event?.stopPropagation();
16541
+ event?.preventDefault();
16542
+ if (this.isDisabled())
16543
+ return;
16544
+ this.internalValue.set(this.multiple() ? [] : null);
16545
+ this.onChange(this.multiple() ? [] : null);
16546
+ this.selectionChange.emit(null);
16547
+ this.searchQuery.set('');
16548
+ this.inputElement?.nativeElement?.focus();
16549
+ }
16550
+ requestCreate() {
16551
+ if (!this.canCreate())
16552
+ return;
16553
+ const value = this.searchQuery().trim();
16554
+ this.createRequested.emit(value);
16555
+ this.searchQuery.set('');
16556
+ this.close();
16557
+ }
16558
+ // ─────────────────────────────────────────────────────────────────────────────
16559
+ // Keyboard navigation
16560
+ // ─────────────────────────────────────────────────────────────────────────────
16561
+ onKeyDown(event) {
16562
+ if (this.isDisabled())
16563
+ return;
16564
+ switch (event.key) {
16565
+ case 'ArrowDown':
16566
+ event.preventDefault();
16567
+ if (!this.isOpen()) {
16568
+ this.open();
16569
+ }
16570
+ else {
16571
+ this.moveFocus(1);
16572
+ }
16573
+ break;
16574
+ case 'ArrowUp':
16575
+ event.preventDefault();
16576
+ if (this.isOpen()) {
16577
+ this.moveFocus(-1);
16578
+ }
16579
+ break;
16580
+ case 'Enter':
16581
+ if (this.isOpen()) {
16582
+ event.preventDefault();
16583
+ const idx = this.focusedIndex();
16584
+ const options = this.filteredOptions();
16585
+ if (idx >= 0 && options[idx] && !options[idx].disabled) {
16586
+ this.selectOption(options[idx]);
16587
+ }
16588
+ else if (this.canCreate()) {
16589
+ this.requestCreate();
16590
+ }
16591
+ }
16592
+ break;
16593
+ case 'Escape':
16594
+ if (this.isOpen()) {
16595
+ event.preventDefault();
16596
+ this.close();
16597
+ }
16598
+ break;
16599
+ case 'Backspace':
16600
+ // Supprimer le dernier chip si l'input est vide en mode multi
16601
+ if (this.multiple() && !this.searchQuery()) {
16602
+ const selected = this.selectedOptions();
16603
+ if (selected.length > 0) {
16604
+ this.removeOption(selected[selected.length - 1]);
16605
+ }
16606
+ }
16607
+ break;
16608
+ case 'Tab':
16609
+ if (this.isOpen()) {
16610
+ this.close();
16611
+ }
16612
+ break;
16613
+ }
16614
+ }
16615
+ // ─────────────────────────────────────────────────────────────────────────────
16616
+ // Click outside
16617
+ // ─────────────────────────────────────────────────────────────────────────────
16618
+ onDocumentClick(event) {
16619
+ const target = event.target;
16620
+ if (!target.closest('.ds-entity-picker')) {
16621
+ this.close();
16622
+ }
16623
+ }
16624
+ // ─────────────────────────────────────────────────────────────────────────────
16625
+ // Helpers
16626
+ // ─────────────────────────────────────────────────────────────────────────────
16627
+ moveFocus(delta) {
16628
+ const options = this.filteredOptions();
16629
+ const canCreateOpt = this.canCreate() ? 1 : 0;
16630
+ const total = options.length + canCreateOpt;
16631
+ if (total === 0)
16632
+ return;
16633
+ let newIndex = this.focusedIndex() + delta;
16634
+ // Wrap around
16635
+ if (newIndex < 0)
16636
+ newIndex = total - 1;
16637
+ if (newIndex >= total)
16638
+ newIndex = 0;
16639
+ // Skip disabled options
16640
+ let attempts = 0;
16641
+ while (newIndex < options.length && options[newIndex]?.disabled && attempts < total) {
16642
+ newIndex += delta;
16643
+ if (newIndex < 0)
16644
+ newIndex = total - 1;
16645
+ if (newIndex >= total)
16646
+ newIndex = 0;
16647
+ attempts++;
16648
+ }
16649
+ this.focusedIndex.set(newIndex);
16650
+ this.scrollFocusedOptionIntoView();
16651
+ }
16652
+ scrollFocusedOptionIntoView() {
16653
+ const index = this.focusedIndex();
16654
+ if (index < 0)
16655
+ return;
16656
+ const optionId = this.getOptionId(index);
16657
+ const optionElement = document.getElementById(optionId);
16658
+ if (optionElement) {
16659
+ optionElement.scrollIntoView({
16660
+ block: 'nearest',
16661
+ behavior: 'smooth',
16662
+ });
16663
+ }
16664
+ }
16665
+ isOptionFocused(index) {
16666
+ return this.focusedIndex() === index;
16667
+ }
16668
+ isOptionSelected(option) {
16669
+ const value = this.internalValue();
16670
+ if (value === null)
16671
+ return false;
16672
+ if (this.multiple()) {
16673
+ return value.includes(option.value);
16674
+ }
16675
+ return value === option.value;
16676
+ }
16677
+ getOptionId(index) {
16678
+ return `${this.name() || 'entity-picker'}-option-${index}`;
16679
+ }
16680
+ isCreateFocused() {
16681
+ return this.canCreate() && this.focusedIndex() === this.filteredOptions().length;
16682
+ }
16683
+ trackOption(_, option) {
16684
+ return option.value;
16685
+ }
16686
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsEntityPicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
16687
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsEntityPicker, isStandalone: true, selector: "ds-entity-picker", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, allowCreate: { classPropertyName: "allowCreate", publicName: "allowCreate", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, displayMode: { classPropertyName: "displayMode", publicName: "displayMode", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, maxSelections: { classPropertyName: "maxSelections", publicName: "maxSelections", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, helper: { classPropertyName: "helper", publicName: "helper", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null }, noResultsText: { classPropertyName: "noResultsText", publicName: "noResultsText", isSignal: true, isRequired: false, transformFunction: null }, createText: { classPropertyName: "createText", publicName: "createText", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange", createRequested: "createRequested", searchChange: "searchChange", opened: "opened", closed: "closed" }, host: { listeners: { "keydown": "onKeyDown($event)", "document:click": "onDocumentClick($event)" } }, providers: [
16688
+ {
16689
+ provide: NG_VALUE_ACCESSOR,
16690
+ useExisting: forwardRef(() => DsEntityPicker),
16691
+ multi: true,
16692
+ },
16693
+ ], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["inputElement"], descendants: true }, { propertyName: "listbox", first: true, predicate: ["listbox"], descendants: true }], ngImport: i0, template: "<div [ngClass]=\"containerClasses()\">\n <!-- Label -->\n @if (label()) {\n <label class=\"ds-entity-picker__label\" [attr.for]=\"inputId()\">\n {{ label() }}\n @if (required()) {\n <span class=\"ds-entity-picker__required\">*</span>\n }\n </label>\n }\n\n <!-- Input wrapper -->\n <div class=\"ds-entity-picker__wrapper\"\n #triggerOrigin=\"cdkOverlayOrigin\"\n cdkOverlayOrigin>\n <!-- Chips (mode multiple) -->\n @if (multiple() && displayMode() === 'chip') {\n <div class=\"ds-entity-picker__chips\">\n @for (option of selectedOptions(); track option.value) {\n <ds-entity-chip\n [option]=\"option\"\n [size]=\"size()\"\n [removable]=\"!isDisabled()\"\n [disabled]=\"isDisabled()\"\n (removed)=\"removeOption($event)\"\n />\n }\n </div>\n }\n\n <!-- Count badge (mode count) -->\n @if (multiple() && displayMode() === 'count' && selectedOptions().length > 0) {\n <span class=\"ds-entity-picker__count\">\n {{ countText() }}\n </span>\n }\n\n <!-- Input -->\n <div class=\"ds-entity-picker__input-container\">\n <input\n #inputElement\n type=\"text\"\n [id]=\"inputId()\"\n [ngClass]=\"inputClasses()\"\n [placeholder]=\"multiple() ? placeholder() : (displayValue() || placeholder())\"\n [disabled]=\"isDisabled()\"\n [value]=\"multiple() ? searchQuery() : (searchQuery() || displayValue())\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-controls]=\"listboxId()\"\n [attr.aria-activedescendant]=\"focusedIndex() >= 0 ? getOptionId(focusedIndex()) : null\"\n role=\"combobox\"\n autocomplete=\"off\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (input)=\"onSearchInput($event)\"\n />\n\n <!-- Icons -->\n <span class=\"ds-entity-picker__icons\">\n @if (clearable() && (selectedOptions().length > 0 || searchQuery())) {\n <button\n type=\"button\"\n class=\"ds-entity-picker__clear\"\n aria-label=\"Effacer\"\n tabindex=\"-1\"\n (mousedown)=\"clear($event)\"\n >\n <fa-icon [icon]=\"iconClear\" />\n </button>\n }\n <span class=\"ds-entity-picker__arrow\" [class.ds-entity-picker__arrow--open]=\"isOpen()\">\n <fa-icon [icon]=\"iconChevron\" />\n </span>\n </span>\n </div>\n </div>\n\n <!-- Dropdown (CDK Overlay) -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"triggerOrigin\"\n [cdkConnectedOverlayOpen]=\"shouldShowDropdown()\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayWidth]=\"triggerOrigin.elementRef.nativeElement.offsetWidth\"\n [cdkConnectedOverlayHasBackdrop]=\"false\"\n [cdkConnectedOverlayPanelClass]=\"'ds-entity-picker-overlay'\">\n <div class=\"ds-entity-picker__dropdown\" role=\"presentation\">\n <ul\n #listbox\n [id]=\"listboxId()\"\n class=\"ds-entity-picker__listbox\"\n role=\"listbox\"\n [attr.aria-label]=\"label() || 'Options'\"\n [attr.aria-multiselectable]=\"multiple()\"\n >\n @for (option of filteredOptions(); track trackOption($index, option); let i = $index) {\n <li\n [id]=\"getOptionId(i)\"\n class=\"ds-entity-picker__option\"\n [class.ds-entity-picker__option--focused]=\"isOptionFocused(i)\"\n [class.ds-entity-picker__option--selected]=\"isOptionSelected(option)\"\n [class.ds-entity-picker__option--disabled]=\"option.disabled || maxReached()\"\n [style.--option-color]=\"option.color || 'var(--gray-400)'\"\n role=\"option\"\n [attr.aria-selected]=\"isOptionSelected(option)\"\n [attr.aria-disabled]=\"option.disabled || maxReached()\"\n (mousedown)=\"selectOption(option)\"\n >\n <!-- Indicateur de couleur -->\n @if (option.color) {\n <span\n class=\"ds-entity-picker__option-indicator\"\n [style.background-color]=\"option.color\"\n ></span>\n }\n\n <!-- Emoji ou ic\u00F4ne -->\n @if (option.emoji) {\n <span class=\"ds-entity-picker__option-emoji\">{{ option.emoji }}</span>\n } @else if (option.icon) {\n <fa-icon\n class=\"ds-entity-picker__option-icon\"\n [icon]=\"option.icon\"\n [style.color]=\"option.color\"\n />\n }\n\n <!-- Contenu texte -->\n <span class=\"ds-entity-picker__option-content\">\n <span class=\"ds-entity-picker__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"ds-entity-picker__option-description\">{{ option.description }}</span>\n }\n </span>\n\n <!-- Check si s\u00E9lectionn\u00E9 -->\n @if (isOptionSelected(option)) {\n <svg\n class=\"ds-entity-picker__check\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n }\n </li>\n } @empty {\n @if (!canCreate()) {\n <li class=\"ds-entity-picker__empty\" role=\"option\" aria-disabled=\"true\">\n {{ noResultsText() }}\n </li>\n }\n }\n\n <!-- Option de cr\u00E9ation -->\n @if (canCreate()) {\n <li\n class=\"ds-entity-picker__option ds-entity-picker__option--create\"\n [class.ds-entity-picker__option--focused]=\"isCreateFocused()\"\n role=\"option\"\n (mousedown)=\"requestCreate()\"\n >\n <fa-icon class=\"ds-entity-picker__option-icon\" [icon]=\"iconPlus\" />\n <span class=\"ds-entity-picker__option-content\">\n <span class=\"ds-entity-picker__option-label\">\n {{ formattedCreateText() }}\n </span>\n </span>\n </li>\n }\n </ul>\n </div>\n </ng-template>\n\n <!-- Helper / Error -->\n @if (error()) {\n <span class=\"ds-entity-picker__error\" role=\"alert\">{{ error() }}</span>\n } @else if (helper()) {\n <span class=\"ds-entity-picker__helper\">{{ helper() }}</span>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.ds-entity-picker{display:flex;flex-direction:column;gap:var(--space-1, .25rem);position:relative;width:100%;font-family:var(--font-family-base, sans-serif)}.ds-entity-picker__label{display:block;font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--text-default, #1a1a1a);margin-bottom:var(--space-1, .25rem)}.ds-entity-picker__required{color:var(--error, #ef4444);margin-left:var(--space-0-5, .125rem)}.ds-entity-picker__wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-1, .25rem);padding:var(--space-1-5, .375rem) var(--space-2, .5rem);min-height:var(--input-height-md, 40px);background-color:var(--input-bg, #fff);border:1px solid var(--input-border, #d1d5db);border-radius:var(--radius-md, 6px);transition:border-color var(--duration-fast, .15s) var(--easing-default, ease),box-shadow var(--duration-fast, .15s) var(--easing-default, ease);cursor:text}.ds-entity-picker__wrapper:hover:not(.ds-entity-picker--disabled .ds-entity-picker__wrapper){border-color:var(--input-border-hover, #9ca3af)}.ds-entity-picker--open .ds-entity-picker__wrapper,.ds-entity-picker__wrapper:focus-within{border-color:var(--color-primary, #6366f1);box-shadow:0 0 0 3px var(--primary-100, rgba(99, 102, 241, .1))}.ds-entity-picker--error .ds-entity-picker__wrapper{border-color:var(--error, #ef4444)}.ds-entity-picker--error .ds-entity-picker__wrapper:focus-within{box-shadow:0 0 0 3px var(--error-100, rgba(239, 68, 68, .1))}.ds-entity-picker--disabled .ds-entity-picker__wrapper{background-color:var(--input-bg-disabled, #f3f4f6);border-color:var(--input-border-disabled, #e5e7eb);cursor:not-allowed;opacity:.6}.ds-entity-picker__chips{display:flex;flex-wrap:wrap;gap:var(--space-1, .25rem);max-width:100%}.ds-entity-picker__count{display:inline-flex;align-items:center;padding:var(--space-0-5, .125rem) var(--space-2, .5rem);background-color:var(--primary-100, #eef2ff);color:var(--color-primary, #6366f1);font-size:var(--font-size-sm, .875rem);font-weight:500;border-radius:var(--radius-pill, 9999px)}.ds-entity-picker__input-container{display:flex;align-items:center;flex:1;min-width:120px;position:relative}.ds-entity-picker__input{flex:1;min-width:0;padding:var(--space-1, .25rem) 0;border:none;background:transparent;font-family:inherit;font-size:var(--font-size-base, 1rem);color:var(--text-default, #1a1a1a);outline:none}.ds-entity-picker__input::placeholder{color:var(--text-muted, #9ca3af)}.ds-entity-picker__input:disabled{cursor:not-allowed}.ds-entity-picker__icons{display:flex;align-items:center;gap:var(--space-1, .25rem);margin-left:var(--space-1, .25rem);flex-shrink:0}.ds-entity-picker__clear{display:flex;align-items:center;justify-content:center;padding:var(--space-1, .25rem);border:none;background:none;color:var(--text-muted, #9ca3af);cursor:pointer;border-radius:var(--radius-sm, 4px);transition:color var(--duration-fast, .15s) var(--easing-default, ease),background-color var(--duration-fast, .15s) var(--easing-default, ease)}.ds-entity-picker__clear:hover{color:var(--text-default, #1a1a1a);background-color:var(--gray-100, #f3f4f6)}.ds-entity-picker__arrow{display:flex;align-items:center;color:var(--text-muted, #9ca3af);transition:transform var(--duration-fast, .15s) var(--easing-default, ease)}.ds-entity-picker__arrow--open{transform:rotate(180deg)}.ds-entity-picker__dropdown{background-color:var(--dropdown-bg, #fff);border:1px solid var(--dropdown-border, #e5e7eb);border-radius:var(--radius-md, 6px);box-shadow:var(--shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, .1));max-height:280px;overflow-y:auto;animation:slideDown .15s ease-out}@keyframes slideDown{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.ds-entity-picker__listbox{list-style:none;margin:0;padding:var(--space-1, .25rem)}.ds-entity-picker__option{display:flex;align-items:center;gap:var(--space-2, .5rem);padding:var(--space-2, .5rem) var(--space-3, .75rem);border-radius:var(--radius-sm, 4px);cursor:pointer;transition:background-color var(--duration-fast, .15s) var(--easing-default, ease)}.ds-entity-picker__option:hover:not(.ds-entity-picker__option--disabled){background-color:var(--gray-50, #f9fafb)}.ds-entity-picker__option--focused:not(.ds-entity-picker__option--disabled){background-color:var(--gray-100, #f3f4f6)}.ds-entity-picker__option--selected{background-color:var(--primary-50, #eef2ff)}.ds-entity-picker__option--selected:hover{background-color:var(--primary-100, #e0e7ff)}.ds-entity-picker__option--disabled{opacity:.5;cursor:not-allowed}.ds-entity-picker__option--create{border-top:1px solid var(--gray-200, #e5e7eb);margin-top:var(--space-1, .25rem);padding-top:var(--space-3, .75rem);color:var(--color-primary, #6366f1)}.ds-entity-picker__option--create .ds-entity-picker__option-icon{color:var(--color-primary, #6366f1)}.ds-entity-picker__option-indicator{width:4px;height:100%;min-height:24px;border-radius:var(--radius-pill, 9999px);flex-shrink:0;margin-right:var(--space-1, .25rem)}.ds-entity-picker__option-emoji{font-size:1.125rem;flex-shrink:0}.ds-entity-picker__option-icon{font-size:1rem;flex-shrink:0;color:var(--option-color, var(--gray-600))}.ds-entity-picker__option-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--space-0-5, .125rem)}.ds-entity-picker__option-label{font-size:var(--font-size-base, 1rem);color:var(--text-default, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ds-entity-picker__option-description{font-size:var(--font-size-sm, .875rem);color:var(--text-muted, #6b7280);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ds-entity-picker__check{flex-shrink:0;color:var(--color-primary, #6366f1)}.ds-entity-picker__empty{padding:var(--space-3, .75rem);text-align:center;color:var(--text-muted, #9ca3af);font-size:var(--font-size-sm, .875rem)}.ds-entity-picker__helper,.ds-entity-picker__error{font-size:var(--font-size-sm, .875rem);margin-top:var(--space-1, .25rem)}.ds-entity-picker__helper{color:var(--text-muted, #6b7280)}.ds-entity-picker__error{color:var(--error, #ef4444)}.ds-entity-picker--sm .ds-entity-picker__wrapper{min-height:var(--input-height-sm, 32px);padding:var(--space-1, .25rem) var(--space-1-5, .375rem)}.ds-entity-picker--sm .ds-entity-picker__input{font-size:var(--font-size-sm, .875rem)}.ds-entity-picker--sm .ds-entity-picker__option{padding:var(--space-1-5, .375rem) var(--space-2, .5rem)}.ds-entity-picker--sm .ds-entity-picker__option-label{font-size:var(--font-size-sm, .875rem)}.ds-entity-picker--sm .ds-entity-picker__option-description{font-size:var(--font-size-xs, .75rem)}.ds-entity-picker--lg .ds-entity-picker__wrapper{min-height:var(--input-height-lg, 48px);padding:var(--space-2, .5rem) var(--space-3, .75rem)}.ds-entity-picker--lg .ds-entity-picker__input{font-size:var(--font-size-lg, 1.125rem)}.ds-entity-picker--lg .ds-entity-picker__option{padding:var(--space-3, .75rem) var(--space-4, 1rem)}.ds-entity-picker--lg .ds-entity-picker__option-label{font-size:var(--font-size-lg, 1.125rem)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FontAwesomeModule }, { kind: "component", type: i1$1.FaIconComponent, selector: "fa-icon", inputs: ["icon", "title", "animation", "mask", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"], outputs: ["iconChange", "titleChange", "animationChange", "maskChange", "flipChange", "sizeChange", "pullChange", "borderChange", "inverseChange", "symbolChange", "rotateChange", "fixedWidthChange", "transformChange", "a11yRoleChange"] }, { kind: "component", type: DsEntityChip, selector: "ds-entity-chip", inputs: ["option", "size", "removable", "disabled"], outputs: ["removed"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
16694
+ }
16695
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsEntityPicker, decorators: [{
16696
+ type: Component,
16697
+ args: [{ selector: 'ds-entity-picker', standalone: true, imports: [CommonModule, FontAwesomeModule, DsEntityChip, CdkConnectedOverlay, CdkOverlayOrigin], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
16698
+ {
16699
+ provide: NG_VALUE_ACCESSOR,
16700
+ useExisting: forwardRef(() => DsEntityPicker),
16701
+ multi: true,
16702
+ },
16703
+ ], template: "<div [ngClass]=\"containerClasses()\">\n <!-- Label -->\n @if (label()) {\n <label class=\"ds-entity-picker__label\" [attr.for]=\"inputId()\">\n {{ label() }}\n @if (required()) {\n <span class=\"ds-entity-picker__required\">*</span>\n }\n </label>\n }\n\n <!-- Input wrapper -->\n <div class=\"ds-entity-picker__wrapper\"\n #triggerOrigin=\"cdkOverlayOrigin\"\n cdkOverlayOrigin>\n <!-- Chips (mode multiple) -->\n @if (multiple() && displayMode() === 'chip') {\n <div class=\"ds-entity-picker__chips\">\n @for (option of selectedOptions(); track option.value) {\n <ds-entity-chip\n [option]=\"option\"\n [size]=\"size()\"\n [removable]=\"!isDisabled()\"\n [disabled]=\"isDisabled()\"\n (removed)=\"removeOption($event)\"\n />\n }\n </div>\n }\n\n <!-- Count badge (mode count) -->\n @if (multiple() && displayMode() === 'count' && selectedOptions().length > 0) {\n <span class=\"ds-entity-picker__count\">\n {{ countText() }}\n </span>\n }\n\n <!-- Input -->\n <div class=\"ds-entity-picker__input-container\">\n <input\n #inputElement\n type=\"text\"\n [id]=\"inputId()\"\n [ngClass]=\"inputClasses()\"\n [placeholder]=\"multiple() ? placeholder() : (displayValue() || placeholder())\"\n [disabled]=\"isDisabled()\"\n [value]=\"multiple() ? searchQuery() : (searchQuery() || displayValue())\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-controls]=\"listboxId()\"\n [attr.aria-activedescendant]=\"focusedIndex() >= 0 ? getOptionId(focusedIndex()) : null\"\n role=\"combobox\"\n autocomplete=\"off\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (input)=\"onSearchInput($event)\"\n />\n\n <!-- Icons -->\n <span class=\"ds-entity-picker__icons\">\n @if (clearable() && (selectedOptions().length > 0 || searchQuery())) {\n <button\n type=\"button\"\n class=\"ds-entity-picker__clear\"\n aria-label=\"Effacer\"\n tabindex=\"-1\"\n (mousedown)=\"clear($event)\"\n >\n <fa-icon [icon]=\"iconClear\" />\n </button>\n }\n <span class=\"ds-entity-picker__arrow\" [class.ds-entity-picker__arrow--open]=\"isOpen()\">\n <fa-icon [icon]=\"iconChevron\" />\n </span>\n </span>\n </div>\n </div>\n\n <!-- Dropdown (CDK Overlay) -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"triggerOrigin\"\n [cdkConnectedOverlayOpen]=\"shouldShowDropdown()\"\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\n [cdkConnectedOverlayWidth]=\"triggerOrigin.elementRef.nativeElement.offsetWidth\"\n [cdkConnectedOverlayHasBackdrop]=\"false\"\n [cdkConnectedOverlayPanelClass]=\"'ds-entity-picker-overlay'\">\n <div class=\"ds-entity-picker__dropdown\" role=\"presentation\">\n <ul\n #listbox\n [id]=\"listboxId()\"\n class=\"ds-entity-picker__listbox\"\n role=\"listbox\"\n [attr.aria-label]=\"label() || 'Options'\"\n [attr.aria-multiselectable]=\"multiple()\"\n >\n @for (option of filteredOptions(); track trackOption($index, option); let i = $index) {\n <li\n [id]=\"getOptionId(i)\"\n class=\"ds-entity-picker__option\"\n [class.ds-entity-picker__option--focused]=\"isOptionFocused(i)\"\n [class.ds-entity-picker__option--selected]=\"isOptionSelected(option)\"\n [class.ds-entity-picker__option--disabled]=\"option.disabled || maxReached()\"\n [style.--option-color]=\"option.color || 'var(--gray-400)'\"\n role=\"option\"\n [attr.aria-selected]=\"isOptionSelected(option)\"\n [attr.aria-disabled]=\"option.disabled || maxReached()\"\n (mousedown)=\"selectOption(option)\"\n >\n <!-- Indicateur de couleur -->\n @if (option.color) {\n <span\n class=\"ds-entity-picker__option-indicator\"\n [style.background-color]=\"option.color\"\n ></span>\n }\n\n <!-- Emoji ou ic\u00F4ne -->\n @if (option.emoji) {\n <span class=\"ds-entity-picker__option-emoji\">{{ option.emoji }}</span>\n } @else if (option.icon) {\n <fa-icon\n class=\"ds-entity-picker__option-icon\"\n [icon]=\"option.icon\"\n [style.color]=\"option.color\"\n />\n }\n\n <!-- Contenu texte -->\n <span class=\"ds-entity-picker__option-content\">\n <span class=\"ds-entity-picker__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"ds-entity-picker__option-description\">{{ option.description }}</span>\n }\n </span>\n\n <!-- Check si s\u00E9lectionn\u00E9 -->\n @if (isOptionSelected(option)) {\n <svg\n class=\"ds-entity-picker__check\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n }\n </li>\n } @empty {\n @if (!canCreate()) {\n <li class=\"ds-entity-picker__empty\" role=\"option\" aria-disabled=\"true\">\n {{ noResultsText() }}\n </li>\n }\n }\n\n <!-- Option de cr\u00E9ation -->\n @if (canCreate()) {\n <li\n class=\"ds-entity-picker__option ds-entity-picker__option--create\"\n [class.ds-entity-picker__option--focused]=\"isCreateFocused()\"\n role=\"option\"\n (mousedown)=\"requestCreate()\"\n >\n <fa-icon class=\"ds-entity-picker__option-icon\" [icon]=\"iconPlus\" />\n <span class=\"ds-entity-picker__option-content\">\n <span class=\"ds-entity-picker__option-label\">\n {{ formattedCreateText() }}\n </span>\n </span>\n </li>\n }\n </ul>\n </div>\n </ng-template>\n\n <!-- Helper / Error -->\n @if (error()) {\n <span class=\"ds-entity-picker__error\" role=\"alert\">{{ error() }}</span>\n } @else if (helper()) {\n <span class=\"ds-entity-picker__helper\">{{ helper() }}</span>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.ds-entity-picker{display:flex;flex-direction:column;gap:var(--space-1, .25rem);position:relative;width:100%;font-family:var(--font-family-base, sans-serif)}.ds-entity-picker__label{display:block;font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--text-default, #1a1a1a);margin-bottom:var(--space-1, .25rem)}.ds-entity-picker__required{color:var(--error, #ef4444);margin-left:var(--space-0-5, .125rem)}.ds-entity-picker__wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-1, .25rem);padding:var(--space-1-5, .375rem) var(--space-2, .5rem);min-height:var(--input-height-md, 40px);background-color:var(--input-bg, #fff);border:1px solid var(--input-border, #d1d5db);border-radius:var(--radius-md, 6px);transition:border-color var(--duration-fast, .15s) var(--easing-default, ease),box-shadow var(--duration-fast, .15s) var(--easing-default, ease);cursor:text}.ds-entity-picker__wrapper:hover:not(.ds-entity-picker--disabled .ds-entity-picker__wrapper){border-color:var(--input-border-hover, #9ca3af)}.ds-entity-picker--open .ds-entity-picker__wrapper,.ds-entity-picker__wrapper:focus-within{border-color:var(--color-primary, #6366f1);box-shadow:0 0 0 3px var(--primary-100, rgba(99, 102, 241, .1))}.ds-entity-picker--error .ds-entity-picker__wrapper{border-color:var(--error, #ef4444)}.ds-entity-picker--error .ds-entity-picker__wrapper:focus-within{box-shadow:0 0 0 3px var(--error-100, rgba(239, 68, 68, .1))}.ds-entity-picker--disabled .ds-entity-picker__wrapper{background-color:var(--input-bg-disabled, #f3f4f6);border-color:var(--input-border-disabled, #e5e7eb);cursor:not-allowed;opacity:.6}.ds-entity-picker__chips{display:flex;flex-wrap:wrap;gap:var(--space-1, .25rem);max-width:100%}.ds-entity-picker__count{display:inline-flex;align-items:center;padding:var(--space-0-5, .125rem) var(--space-2, .5rem);background-color:var(--primary-100, #eef2ff);color:var(--color-primary, #6366f1);font-size:var(--font-size-sm, .875rem);font-weight:500;border-radius:var(--radius-pill, 9999px)}.ds-entity-picker__input-container{display:flex;align-items:center;flex:1;min-width:120px;position:relative}.ds-entity-picker__input{flex:1;min-width:0;padding:var(--space-1, .25rem) 0;border:none;background:transparent;font-family:inherit;font-size:var(--font-size-base, 1rem);color:var(--text-default, #1a1a1a);outline:none}.ds-entity-picker__input::placeholder{color:var(--text-muted, #9ca3af)}.ds-entity-picker__input:disabled{cursor:not-allowed}.ds-entity-picker__icons{display:flex;align-items:center;gap:var(--space-1, .25rem);margin-left:var(--space-1, .25rem);flex-shrink:0}.ds-entity-picker__clear{display:flex;align-items:center;justify-content:center;padding:var(--space-1, .25rem);border:none;background:none;color:var(--text-muted, #9ca3af);cursor:pointer;border-radius:var(--radius-sm, 4px);transition:color var(--duration-fast, .15s) var(--easing-default, ease),background-color var(--duration-fast, .15s) var(--easing-default, ease)}.ds-entity-picker__clear:hover{color:var(--text-default, #1a1a1a);background-color:var(--gray-100, #f3f4f6)}.ds-entity-picker__arrow{display:flex;align-items:center;color:var(--text-muted, #9ca3af);transition:transform var(--duration-fast, .15s) var(--easing-default, ease)}.ds-entity-picker__arrow--open{transform:rotate(180deg)}.ds-entity-picker__dropdown{background-color:var(--dropdown-bg, #fff);border:1px solid var(--dropdown-border, #e5e7eb);border-radius:var(--radius-md, 6px);box-shadow:var(--shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, .1));max-height:280px;overflow-y:auto;animation:slideDown .15s ease-out}@keyframes slideDown{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.ds-entity-picker__listbox{list-style:none;margin:0;padding:var(--space-1, .25rem)}.ds-entity-picker__option{display:flex;align-items:center;gap:var(--space-2, .5rem);padding:var(--space-2, .5rem) var(--space-3, .75rem);border-radius:var(--radius-sm, 4px);cursor:pointer;transition:background-color var(--duration-fast, .15s) var(--easing-default, ease)}.ds-entity-picker__option:hover:not(.ds-entity-picker__option--disabled){background-color:var(--gray-50, #f9fafb)}.ds-entity-picker__option--focused:not(.ds-entity-picker__option--disabled){background-color:var(--gray-100, #f3f4f6)}.ds-entity-picker__option--selected{background-color:var(--primary-50, #eef2ff)}.ds-entity-picker__option--selected:hover{background-color:var(--primary-100, #e0e7ff)}.ds-entity-picker__option--disabled{opacity:.5;cursor:not-allowed}.ds-entity-picker__option--create{border-top:1px solid var(--gray-200, #e5e7eb);margin-top:var(--space-1, .25rem);padding-top:var(--space-3, .75rem);color:var(--color-primary, #6366f1)}.ds-entity-picker__option--create .ds-entity-picker__option-icon{color:var(--color-primary, #6366f1)}.ds-entity-picker__option-indicator{width:4px;height:100%;min-height:24px;border-radius:var(--radius-pill, 9999px);flex-shrink:0;margin-right:var(--space-1, .25rem)}.ds-entity-picker__option-emoji{font-size:1.125rem;flex-shrink:0}.ds-entity-picker__option-icon{font-size:1rem;flex-shrink:0;color:var(--option-color, var(--gray-600))}.ds-entity-picker__option-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--space-0-5, .125rem)}.ds-entity-picker__option-label{font-size:var(--font-size-base, 1rem);color:var(--text-default, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ds-entity-picker__option-description{font-size:var(--font-size-sm, .875rem);color:var(--text-muted, #6b7280);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ds-entity-picker__check{flex-shrink:0;color:var(--color-primary, #6366f1)}.ds-entity-picker__empty{padding:var(--space-3, .75rem);text-align:center;color:var(--text-muted, #9ca3af);font-size:var(--font-size-sm, .875rem)}.ds-entity-picker__helper,.ds-entity-picker__error{font-size:var(--font-size-sm, .875rem);margin-top:var(--space-1, .25rem)}.ds-entity-picker__helper{color:var(--text-muted, #6b7280)}.ds-entity-picker__error{color:var(--error, #ef4444)}.ds-entity-picker--sm .ds-entity-picker__wrapper{min-height:var(--input-height-sm, 32px);padding:var(--space-1, .25rem) var(--space-1-5, .375rem)}.ds-entity-picker--sm .ds-entity-picker__input{font-size:var(--font-size-sm, .875rem)}.ds-entity-picker--sm .ds-entity-picker__option{padding:var(--space-1-5, .375rem) var(--space-2, .5rem)}.ds-entity-picker--sm .ds-entity-picker__option-label{font-size:var(--font-size-sm, .875rem)}.ds-entity-picker--sm .ds-entity-picker__option-description{font-size:var(--font-size-xs, .75rem)}.ds-entity-picker--lg .ds-entity-picker__wrapper{min-height:var(--input-height-lg, 48px);padding:var(--space-2, .5rem) var(--space-3, .75rem)}.ds-entity-picker--lg .ds-entity-picker__input{font-size:var(--font-size-lg, 1.125rem)}.ds-entity-picker--lg .ds-entity-picker__option{padding:var(--space-3, .75rem) var(--space-4, 1rem)}.ds-entity-picker--lg .ds-entity-picker__option-label{font-size:var(--font-size-lg, 1.125rem)}\n"] }]
16704
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], allowCreate: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowCreate", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], displayMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayMode", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], maxSelections: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelections", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], helper: [{ type: i0.Input, args: [{ isSignal: true, alias: "helper", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], minChars: [{ type: i0.Input, args: [{ isSignal: true, alias: "minChars", required: false }] }], noResultsText: [{ type: i0.Input, args: [{ isSignal: true, alias: "noResultsText", required: false }] }], createText: [{ type: i0.Input, args: [{ isSignal: true, alias: "createText", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], createRequested: [{ type: i0.Output, args: ["createRequested"] }], searchChange: [{ type: i0.Output, args: ["searchChange"] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], inputElement: [{
16705
+ type: ViewChild,
16706
+ args: ['inputElement']
16707
+ }], listbox: [{
16708
+ type: ViewChild,
16709
+ args: ['listbox']
16710
+ }], onKeyDown: [{
16711
+ type: HostListener,
16712
+ args: ['keydown', ['$event']]
16713
+ }], onDocumentClick: [{
16714
+ type: HostListener,
16715
+ args: ['document:click', ['$event']]
16716
+ }] } });
16717
+
15932
16718
  /*
15933
16719
  * Components barrel export
15934
16720
  */
@@ -16415,5 +17201,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
16415
17201
  * Generated bundle index. Do not edit.
16416
17202
  */
16417
17203
 
16418
- export { AUTOCOMPLETE_POSITIONS, DROPDOWN_POSITIONS, DROPDOWN_POSITIONS_RIGHT, DROPDOWN_POSITIONS_TOP, DsAccordion, DsAlert, DsAvatar, DsBadge, DsBreadcrumb, DsButton, DsCalendar, DsCard, DsCarousel, DsCheckbox, DsCheckboxList, DsChip, DsColorPicker, DsCombobox, DsContainer, DsDatePicker, DsDivider, DsDrawer, DsDropdown, DsEmpty, DsFileUpload, DsI18nService, DsInputField, DsInputNumber, DsInputTextarea, DsList, DsListGroup, DsListItem, DsMenu, DsModalComponent, DsNavList, DsNotificationContainerComponent, DsNotificationItemComponent, DsNotificationService, DsPagination, DsPasswordStrength, DsPopover, DsPopoverComponent, DsProgressBar, DsRadioGroup, DsRating, DsSearchInput, DsSegmentedControl, DsSelect, DsSidebar, DsSidebarFooterItemComponent, DsSidebarItemComponent, DsSkeleton, DsSlider, DsStepper, DsTable, DsTabs, DsTimePicker, DsTimeline, DsToastComponent, DsToastContainerComponent, DsToastService, DsToggle, DsTooltip, DsTooltipComponent, DsTransfer, DsTree, IconRegistryService, POPOVER_POSITIONS, PrimitiveBadge, PrimitiveButton, PrimitiveCheckbox, PrimitiveInput, PrimitiveRadio, PrimitiveTextarea, PrimitiveToggle, SIDEBAR_POPOVER_POSITIONS_LEFT, SIDEBAR_POPOVER_POSITIONS_RIGHT, TOOLTIP_POSITIONS, generateId, generateShortId };
17204
+ export { AUTOCOMPLETE_POSITIONS, DROPDOWN_POSITIONS, DROPDOWN_POSITIONS_RIGHT, DROPDOWN_POSITIONS_TOP, DsAccordion, DsAccordionItem, DsAlert, DsAvatar, DsBadge, DsBreadcrumb, DsButton, DsCalendar, DsCard, DsCarousel, DsCheckbox, DsCheckboxList, DsChip, DsColorPicker, DsCombobox, DsContainer, DsDatePicker, DsDivider, DsDrawer, DsDropdown, DsEmpty, DsEntityChip, DsEntityPicker, DsFileUpload, DsI18nService, DsInputField, DsInputNumber, DsInputTextarea, DsList, DsListGroup, DsListItem, DsMenu, DsModalComponent, DsNavList, DsNotificationContainerComponent, DsNotificationItemComponent, DsNotificationService, DsPagination, DsPasswordStrength, DsPopover, DsPopoverComponent, DsProgressBar, DsRadioGroup, DsRating, DsSearchInput, DsSegmentedControl, DsSelect, DsSidebar, DsSidebarFooterItemComponent, DsSidebarItemComponent, DsSkeleton, DsSlider, DsStepper, DsTable, DsTabs, DsTimePicker, DsTimeline, DsToastComponent, DsToastContainerComponent, DsToastService, DsToggle, DsTooltip, DsTooltipComponent, DsTransfer, DsTree, IconRegistryService, POPOVER_POSITIONS, PrimitiveBadge, PrimitiveButton, PrimitiveCheckbox, PrimitiveInput, PrimitiveRadio, PrimitiveTextarea, PrimitiveToggle, SIDEBAR_POPOVER_POSITIONS_LEFT, SIDEBAR_POPOVER_POSITIONS_RIGHT, TOOLTIP_POSITIONS, generateId, generateShortId };
16419
17205
  //# sourceMappingURL=kksdev-ds-angular.mjs.map