@kksdev/ds-angular 1.4.0 → 1.5.0

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,12 +1,12 @@
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 } 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, ViewEncapsulation } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule, DOCUMENT, NgClass } 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';
8
8
  import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
9
- import { faInfoCircle, faTimesCircle, faExclamationTriangle, faCheckCircle, faTimes, faEye, faEyeSlash, faClose, faCircleInfo, faCircleXmark, faCircleExclamation, faCircleCheck, faChevronDown, faChevronLeft, faChevronRight, faAnglesLeft, faAnglesRight, faCheck, faSearch, faXmark, faSpinner, faCalendar, faCloudArrowUp, faFile, faFileImage, faFilePdf, faFileWord, faFileExcel, faInbox, faStar, faStarHalfStroke, faClock, faChevronUp, faFolder, faFolderOpen } from '@fortawesome/free-solid-svg-icons';
9
+ import { faInfoCircle, faTimesCircle, faExclamationTriangle, faCheckCircle, faTimes, faEye, faEyeSlash, faClose, faCircleInfo, faCircleXmark, faCircleExclamation, faCircleCheck, faChevronDown, faChevronLeft, faChevronRight, faAnglesLeft, faAnglesRight, faCheck, faSearch, faXmark, faSpinner, faCalendar, faCloudArrowUp, faFile, faFileImage, faFilePdf, faFileWord, faFileExcel, faInbox, faStar, faStarHalfStroke, faClock, faChevronUp, faFolder, faFolderOpen, faCircle, faCircleHalfStroke, faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
10
10
  import * as i1$2 from '@angular/router';
11
11
  import { RouterModule } from '@angular/router';
12
12
  import * as i1$3 from '@angular/cdk/overlay';
@@ -10163,6 +10163,1274 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
10163
10163
  args: [{ selector: 'ds-tree', standalone: true, imports: [CommonModule, DsTreeNodeComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"containerClasses()\" role=\"tree\">\n @if (processedData().length === 0) {\n <div class=\"ds-tree__empty\">\n <p>No data available</p>\n </div>\n } @else {\n @for (node of processedData(); track node.id) {\n <ds-tree-node\n [node]=\"node\"\n [level]=\"0\"\n [selectable]=\"selectable()\"\n [checkable]=\"checkable()\"\n [showIcon]=\"showIcon()\"\n [selectedNodeId]=\"selectedNodeId()\"\n [expandedNodeIds]=\"expandedNodeIds()\"\n [checkedNodeIds]=\"checkedNodeIds()\"\n [getIndeterminateState]=\"getIndeterminateState.bind(this)\"\n (nodeClick)=\"onNodeClick($event)\"\n (nodeToggle)=\"onNodeToggle($event)\"\n (nodeCheck)=\"onNodeCheck($event.node, $event.checked)\">\n </ds-tree-node>\n }\n }\n</div>\n", styles: [".ds-tree{display:block;width:100%;background-color:var(--tree-bg, var(--white));border:1px solid var(--tree-border, var(--gray-300));border-radius:var(--radius-2);padding:var(--space-2);max-height:400px;overflow-y:auto}.ds-tree__empty{display:flex;align-items:center;justify-content:center;padding:var(--space-8);color:var(--text-muted, var(--gray-500));font-size:var(--font-size-sm)}.ds-tree--sm{font-size:var(--font-size-sm);padding:var(--space-1)}.ds-tree--lg{font-size:var(--font-size-lg);padding:var(--space-3)}.ds-tree::-webkit-scrollbar{width:8px}.ds-tree::-webkit-scrollbar-track{background:var(--gray-100);border-radius:var(--radius-1)}.ds-tree::-webkit-scrollbar-thumb{background:var(--gray-400);border-radius:var(--radius-1)}.ds-tree::-webkit-scrollbar-thumb:hover{background:var(--gray-500)}\n"] }]
10164
10164
  }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], checkable: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkable", required: false }] }], expandAll: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandAll", required: false }] }], showIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcon", required: false }] }], showLine: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLine", required: false }] }], draggable: [{ type: i0.Input, args: [{ isSignal: true, alias: "draggable", required: false }] }], virtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScroll", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], loadChildren: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadChildren", required: false }] }], nodeSelect: [{ type: i0.Output, args: ["nodeSelect"] }], nodeExpand: [{ type: i0.Output, args: ["nodeExpand"] }], nodeCheck: [{ type: i0.Output, args: ["nodeCheck"] }] } });
10165
10165
 
10166
+ /**
10167
+ * # DsPasswordStrength
10168
+ *
10169
+ * Composant indicateur de force de mot de passe avec affichage visuel
10170
+ * (barres de progression) et optionnel des critères de validation.
10171
+ *
10172
+ * ## Usage
10173
+ *
10174
+ * ```html
10175
+ * <ds-password-strength
10176
+ * [password]="userPassword"
10177
+ * [minLength]="8"
10178
+ * [showLabel]="true"
10179
+ * [showCriteria]="true"
10180
+ * (strengthChange)="handleStrengthChange($event)" />
10181
+ * ```
10182
+ *
10183
+ * ## Calcul de la force
10184
+ *
10185
+ * - `none`: Champ vide
10186
+ * - `weak`: Longueur < minLength OU uniquement lettres
10187
+ * - `medium`: Longueur ≥ minLength ET (lettres+chiffres OU lettres+spéciaux)
10188
+ * - `strong`: Longueur ≥ minLength ET majuscules + minuscules + chiffres + spéciaux
10189
+ *
10190
+ * ## Accessibilité
10191
+ *
10192
+ * - Attribut `role="status"` pour annoncer les changements
10193
+ * - Label ARIA dynamique selon la force
10194
+ * - Couleurs sémantiques (error/warning/success)
10195
+ *
10196
+ * @component
10197
+ */
10198
+ class DsPasswordStrength {
10199
+ /**
10200
+ * Mot de passe à évaluer.
10201
+ * @default ''
10202
+ */
10203
+ password = input('', ...(ngDevMode ? [{ debugName: "password" }] : []));
10204
+ /**
10205
+ * Longueur minimale requise.
10206
+ * @default 8
10207
+ */
10208
+ minLength = input(8, ...(ngDevMode ? [{ debugName: "minLength" }] : []));
10209
+ /**
10210
+ * Afficher le label textuel (ex: "Faible", "Moyen", "Fort").
10211
+ * @default true
10212
+ */
10213
+ showLabel = input(true, ...(ngDevMode ? [{ debugName: "showLabel" }] : []));
10214
+ /**
10215
+ * Afficher les critères détaillés de validation.
10216
+ * @default false
10217
+ */
10218
+ showCriteria = input(false, ...(ngDevMode ? [{ debugName: "showCriteria" }] : []));
10219
+ /**
10220
+ * Taille du composant.
10221
+ * @default 'md'
10222
+ */
10223
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
10224
+ /**
10225
+ * Émis lorsque la force du mot de passe change.
10226
+ */
10227
+ strengthChange = output();
10228
+ /**
10229
+ * Icônes FontAwesome pour les critères.
10230
+ */
10231
+ faCheck = faCheck;
10232
+ faTimes = faTimes;
10233
+ /**
10234
+ * Regex patterns pour la validation.
10235
+ */
10236
+ hasUpperCase = /[A-Z]/;
10237
+ hasLowerCase = /[a-z]/;
10238
+ hasNumber = /[0-9]/;
10239
+ hasSpecialChar = /[^A-Za-z0-9]/;
10240
+ /**
10241
+ * Calcul de la force du mot de passe.
10242
+ */
10243
+ strength = computed(() => {
10244
+ const pwd = this.password();
10245
+ const minLen = this.minLength();
10246
+ if (!pwd || pwd.length === 0) {
10247
+ return 'none';
10248
+ }
10249
+ const hasUpper = this.hasUpperCase.test(pwd);
10250
+ const hasLower = this.hasLowerCase.test(pwd);
10251
+ const hasNum = this.hasNumber.test(pwd);
10252
+ const hasSpecial = this.hasSpecialChar.test(pwd);
10253
+ const lengthValid = pwd.length >= minLen;
10254
+ // Weak: longueur insuffisante OU uniquement lettres
10255
+ if (!lengthValid) {
10256
+ return 'weak';
10257
+ }
10258
+ const hasLetters = hasUpper || hasLower;
10259
+ const hasOnlyLetters = hasLetters && !hasNum && !hasSpecial;
10260
+ if (hasOnlyLetters) {
10261
+ return 'weak';
10262
+ }
10263
+ // Strong: toutes les catégories présentes
10264
+ if (hasUpper && hasLower && hasNum && hasSpecial) {
10265
+ return 'strong';
10266
+ }
10267
+ // Medium: au moins deux catégories
10268
+ return 'medium';
10269
+ }, ...(ngDevMode ? [{ debugName: "strength" }] : []));
10270
+ /**
10271
+ * Label textuel de la force (pour affichage).
10272
+ */
10273
+ strengthLabel = computed(() => {
10274
+ const labels = {
10275
+ none: '',
10276
+ weak: 'Faible',
10277
+ medium: 'Moyen',
10278
+ strong: 'Fort',
10279
+ };
10280
+ return labels[this.strength()];
10281
+ }, ...(ngDevMode ? [{ debugName: "strengthLabel" }] : []));
10282
+ /**
10283
+ * Label ARIA pour accessibilité.
10284
+ */
10285
+ ariaLabel = computed(() => {
10286
+ const str = this.strength();
10287
+ if (str === 'none') {
10288
+ return 'Force du mot de passe : non définie';
10289
+ }
10290
+ return `Force du mot de passe : ${this.strengthLabel()}`;
10291
+ }, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
10292
+ /**
10293
+ * Classes CSS calculées pour le conteneur.
10294
+ */
10295
+ containerClasses = computed(() => {
10296
+ return [
10297
+ 'ds-password-strength',
10298
+ `ds-password-strength--${this.size()}`,
10299
+ `ds-password-strength--${this.strength()}`,
10300
+ ].filter(Boolean).join(' ');
10301
+ }, ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
10302
+ /**
10303
+ * Barres indicatrices (3 barres).
10304
+ * Chaque barre est active selon la force.
10305
+ */
10306
+ bars = computed(() => {
10307
+ const str = this.strength();
10308
+ const barCount = 3;
10309
+ const activeCount = {
10310
+ none: 0,
10311
+ weak: 1,
10312
+ medium: 2,
10313
+ strong: 3,
10314
+ };
10315
+ const count = activeCount[str];
10316
+ return Array.from({ length: barCount }, (_, index) => ({
10317
+ active: index < count,
10318
+ level: str,
10319
+ }));
10320
+ }, ...(ngDevMode ? [{ debugName: "bars" }] : []));
10321
+ /**
10322
+ * Liste des critères de validation (pour affichage détaillé).
10323
+ */
10324
+ criteria = computed(() => {
10325
+ const pwd = this.password();
10326
+ const minLen = this.minLength();
10327
+ return [
10328
+ {
10329
+ key: 'length',
10330
+ label: `Au moins ${minLen} caractères`,
10331
+ valid: pwd.length >= minLen,
10332
+ },
10333
+ {
10334
+ key: 'uppercase',
10335
+ label: 'Contient des majuscules',
10336
+ valid: this.hasUpperCase.test(pwd),
10337
+ },
10338
+ {
10339
+ key: 'lowercase',
10340
+ label: 'Contient des minuscules',
10341
+ valid: this.hasLowerCase.test(pwd),
10342
+ },
10343
+ {
10344
+ key: 'number',
10345
+ label: 'Contient des chiffres',
10346
+ valid: this.hasNumber.test(pwd),
10347
+ },
10348
+ {
10349
+ key: 'special',
10350
+ label: 'Contient des caractères spéciaux',
10351
+ valid: this.hasSpecialChar.test(pwd),
10352
+ },
10353
+ ];
10354
+ }, ...(ngDevMode ? [{ debugName: "criteria" }] : []));
10355
+ /**
10356
+ * Constructeur : émet les changements de force via effect.
10357
+ */
10358
+ constructor() {
10359
+ let previousStrength = 'none';
10360
+ // Émettre strengthChange à chaque changement
10361
+ effect(() => {
10362
+ const currentStrength = this.strength();
10363
+ if (currentStrength !== previousStrength) {
10364
+ previousStrength = currentStrength;
10365
+ this.strengthChange.emit(currentStrength);
10366
+ }
10367
+ });
10368
+ }
10369
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsPasswordStrength, deps: [], target: i0.ɵɵFactoryTarget.Component });
10370
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsPasswordStrength, isStandalone: true, selector: "ds-password-strength", inputs: { password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null }, showLabel: { classPropertyName: "showLabel", publicName: "showLabel", isSignal: true, isRequired: false, transformFunction: null }, showCriteria: { classPropertyName: "showCriteria", publicName: "showCriteria", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { strengthChange: "strengthChange" }, ngImport: i0, template: "<div [ngClass]=\"containerClasses()\" role=\"status\" [attr.aria-label]=\"ariaLabel()\">\n <!-- Barres indicatrices (3 barres) -->\n <div class=\"ds-password-strength__bars\">\n @for (bar of bars(); track $index) {\n <div\n class=\"ds-password-strength__bar\"\n [class.ds-password-strength__bar--active]=\"bar.active\"\n [class.ds-password-strength__bar--weak]=\"bar.active && bar.level === 'weak'\"\n [class.ds-password-strength__bar--medium]=\"bar.active && bar.level === 'medium'\"\n [class.ds-password-strength__bar--strong]=\"bar.active && bar.level === 'strong'\">\n </div>\n }\n </div>\n\n <!-- Label optionnel -->\n @if (showLabel() && strength() !== 'none') {\n <span\n class=\"ds-password-strength__label\"\n [class]=\"'ds-password-strength__label--' + strength()\">\n {{ strengthLabel() }}\n </span>\n }\n\n <!-- Crit\u00E8res d\u00E9taill\u00E9s optionnels -->\n @if (showCriteria()) {\n <ul class=\"ds-password-strength__criteria\">\n @for (criterion of criteria(); track criterion.key) {\n <li\n class=\"ds-password-strength__criterion\"\n [class.ds-password-strength__criterion--valid]=\"criterion.valid\">\n <fa-icon [icon]=\"criterion.valid ? faCheck : faTimes\" />\n <span>{{ criterion.label }}</span>\n </li>\n }\n </ul>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.ds-password-strength{display:flex;flex-direction:column;gap:var(--password-strength-label-gap)}.ds-password-strength__bars{display:flex;gap:var(--password-strength-bar-gap);align-items:center}.ds-password-strength__bar{flex:1;height:var(--password-strength-bar-height-md);background-color:var(--password-strength-bg);border-radius:var(--password-strength-bar-radius);transition:background-color .3s ease}.ds-password-strength__bar--active.ds-password-strength__bar--weak{background-color:var(--password-strength-weak)}.ds-password-strength__bar--active.ds-password-strength__bar--medium{background-color:var(--password-strength-medium)}.ds-password-strength__bar--active.ds-password-strength__bar--strong{background-color:var(--password-strength-strong)}.ds-password-strength__label{font-size:var(--password-strength-label-font-size);color:var(--password-strength-text);font-weight:var(--font-weight-medium);text-align:right}.ds-password-strength__label--weak{color:var(--password-strength-weak)}.ds-password-strength__label--medium{color:var(--password-strength-medium)}.ds-password-strength__label--strong{color:var(--password-strength-strong)}.ds-password-strength__criteria{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--space-1)}.ds-password-strength__criterion{display:flex;align-items:center;gap:var(--space-2);font-size:var(--font-size-2);color:var(--password-strength-criterion-invalid);transition:color .2s ease}.ds-password-strength__criterion fa-icon{font-size:.875rem}.ds-password-strength__criterion--valid{color:var(--password-strength-criterion-valid)}.ds-password-strength--sm .ds-password-strength__bar{height:var(--password-strength-bar-height-sm)}.ds-password-strength--sm .ds-password-strength__label,.ds-password-strength--sm .ds-password-strength__criterion{font-size:var(--font-size-1)}.ds-password-strength--md .ds-password-strength__bar{height:var(--password-strength-bar-height-md)}.ds-password-strength--md .ds-password-strength__label{font-size:var(--password-strength-label-font-size)}.ds-password-strength--md .ds-password-strength__criterion{font-size:var(--font-size-2)}.ds-password-strength--lg .ds-password-strength__bar{height:var(--password-strength-bar-height-lg)}.ds-password-strength--lg .ds-password-strength__label,.ds-password-strength--lg .ds-password-strength__criterion{font-size:var(--font-size-3)}\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"] }] });
10371
+ }
10372
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsPasswordStrength, decorators: [{
10373
+ type: Component,
10374
+ args: [{ selector: 'ds-password-strength', imports: [CommonModule, FontAwesomeModule], template: "<div [ngClass]=\"containerClasses()\" role=\"status\" [attr.aria-label]=\"ariaLabel()\">\n <!-- Barres indicatrices (3 barres) -->\n <div class=\"ds-password-strength__bars\">\n @for (bar of bars(); track $index) {\n <div\n class=\"ds-password-strength__bar\"\n [class.ds-password-strength__bar--active]=\"bar.active\"\n [class.ds-password-strength__bar--weak]=\"bar.active && bar.level === 'weak'\"\n [class.ds-password-strength__bar--medium]=\"bar.active && bar.level === 'medium'\"\n [class.ds-password-strength__bar--strong]=\"bar.active && bar.level === 'strong'\">\n </div>\n }\n </div>\n\n <!-- Label optionnel -->\n @if (showLabel() && strength() !== 'none') {\n <span\n class=\"ds-password-strength__label\"\n [class]=\"'ds-password-strength__label--' + strength()\">\n {{ strengthLabel() }}\n </span>\n }\n\n <!-- Crit\u00E8res d\u00E9taill\u00E9s optionnels -->\n @if (showCriteria()) {\n <ul class=\"ds-password-strength__criteria\">\n @for (criterion of criteria(); track criterion.key) {\n <li\n class=\"ds-password-strength__criterion\"\n [class.ds-password-strength__criterion--valid]=\"criterion.valid\">\n <fa-icon [icon]=\"criterion.valid ? faCheck : faTimes\" />\n <span>{{ criterion.label }}</span>\n </li>\n }\n </ul>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.ds-password-strength{display:flex;flex-direction:column;gap:var(--password-strength-label-gap)}.ds-password-strength__bars{display:flex;gap:var(--password-strength-bar-gap);align-items:center}.ds-password-strength__bar{flex:1;height:var(--password-strength-bar-height-md);background-color:var(--password-strength-bg);border-radius:var(--password-strength-bar-radius);transition:background-color .3s ease}.ds-password-strength__bar--active.ds-password-strength__bar--weak{background-color:var(--password-strength-weak)}.ds-password-strength__bar--active.ds-password-strength__bar--medium{background-color:var(--password-strength-medium)}.ds-password-strength__bar--active.ds-password-strength__bar--strong{background-color:var(--password-strength-strong)}.ds-password-strength__label{font-size:var(--password-strength-label-font-size);color:var(--password-strength-text);font-weight:var(--font-weight-medium);text-align:right}.ds-password-strength__label--weak{color:var(--password-strength-weak)}.ds-password-strength__label--medium{color:var(--password-strength-medium)}.ds-password-strength__label--strong{color:var(--password-strength-strong)}.ds-password-strength__criteria{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--space-1)}.ds-password-strength__criterion{display:flex;align-items:center;gap:var(--space-2);font-size:var(--font-size-2);color:var(--password-strength-criterion-invalid);transition:color .2s ease}.ds-password-strength__criterion fa-icon{font-size:.875rem}.ds-password-strength__criterion--valid{color:var(--password-strength-criterion-valid)}.ds-password-strength--sm .ds-password-strength__bar{height:var(--password-strength-bar-height-sm)}.ds-password-strength--sm .ds-password-strength__label,.ds-password-strength--sm .ds-password-strength__criterion{font-size:var(--font-size-1)}.ds-password-strength--md .ds-password-strength__bar{height:var(--password-strength-bar-height-md)}.ds-password-strength--md .ds-password-strength__label{font-size:var(--password-strength-label-font-size)}.ds-password-strength--md .ds-password-strength__criterion{font-size:var(--font-size-2)}.ds-password-strength--lg .ds-password-strength__bar{height:var(--password-strength-bar-height-lg)}.ds-password-strength--lg .ds-password-strength__label,.ds-password-strength--lg .ds-password-strength__criterion{font-size:var(--font-size-3)}\n"] }]
10375
+ }], ctorParameters: () => [], propDecorators: { password: [{ type: i0.Input, args: [{ isSignal: true, alias: "password", required: false }] }], minLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLength", required: false }] }], showLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabel", required: false }] }], showCriteria: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCriteria", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], strengthChange: [{ type: i0.Output, args: ["strengthChange"] }] } });
10376
+
10377
+ /**
10378
+ * # DsTimeline
10379
+ *
10380
+ * Composant pour afficher une liste d'événements dans un ordre chronologique vertical.
10381
+ *
10382
+ * ## Usage
10383
+ *
10384
+ * ```html
10385
+ * <ds-timeline
10386
+ * [items]="events"
10387
+ * mode="left"
10388
+ * size="md"
10389
+ * [pending]="false"
10390
+ * (itemClick)="onItemClick($event)">
10391
+ * </ds-timeline>
10392
+ * ```
10393
+ *
10394
+ * ## Modes
10395
+ *
10396
+ * - `left` : Contenu aligné à droite de la ligne
10397
+ * - `right` : Contenu aligné à gauche de la ligne
10398
+ * - `alternate` : Contenu alterné gauche/droite
10399
+ *
10400
+ * ## Accessibilité
10401
+ *
10402
+ * - Liste sémantique avec `role="list"` et `role="listitem"`
10403
+ * - Dates et contenus avec attributs ARIA
10404
+ * - Navigation clavier supportée
10405
+ *
10406
+ * @component
10407
+ */
10408
+ class DsTimeline {
10409
+ /**
10410
+ * Liste des événements à afficher.
10411
+ */
10412
+ items = input.required(...(ngDevMode ? [{ debugName: "items" }] : []));
10413
+ /**
10414
+ * Mode d'affichage (position du contenu).
10415
+ * @default 'left'
10416
+ */
10417
+ mode = input('left', ...(ngDevMode ? [{ debugName: "mode" }] : []));
10418
+ /**
10419
+ * Taille du composant.
10420
+ * @default 'md'
10421
+ */
10422
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
10423
+ /**
10424
+ * Affiche un indicateur "en cours" à la fin.
10425
+ * @default false
10426
+ */
10427
+ pending = input(false, ...(ngDevMode ? [{ debugName: "pending" }] : []));
10428
+ /**
10429
+ * Texte de l'indicateur pending.
10430
+ * @default 'En cours...'
10431
+ */
10432
+ pendingContent = input('En cours...', ...(ngDevMode ? [{ debugName: "pendingContent" }] : []));
10433
+ /**
10434
+ * Inverse l'ordre d'affichage (plus récent en haut).
10435
+ * @default false
10436
+ */
10437
+ reverse = input(false, ...(ngDevMode ? [{ debugName: "reverse" }] : []));
10438
+ /**
10439
+ * Événement émis au clic sur un item.
10440
+ */
10441
+ itemClick = output();
10442
+ /**
10443
+ * Icônes par défaut.
10444
+ */
10445
+ defaultIcon = faCircle;
10446
+ pendingIcon = faCircleHalfStroke;
10447
+ /**
10448
+ * Items triés (reverse si demandé).
10449
+ */
10450
+ sortedItems = computed(() => {
10451
+ const items = [...this.items()];
10452
+ return this.reverse() ? items.reverse() : items;
10453
+ }, ...(ngDevMode ? [{ debugName: "sortedItems" }] : []));
10454
+ /**
10455
+ * Classes CSS du conteneur.
10456
+ */
10457
+ containerClasses = computed(() => {
10458
+ return [
10459
+ 'ds-timeline',
10460
+ `ds-timeline--${this.mode()}`,
10461
+ `ds-timeline--${this.size()}`,
10462
+ ].filter(Boolean);
10463
+ }, ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
10464
+ /**
10465
+ * Classes CSS pour un item.
10466
+ */
10467
+ getItemClasses(item, index) {
10468
+ const position = this.getItemPosition(index);
10469
+ return [
10470
+ 'ds-timeline__item',
10471
+ `ds-timeline__item--${position}`,
10472
+ item.color ? `ds-timeline__item--${item.color}` : 'ds-timeline__item--default',
10473
+ ].filter(Boolean);
10474
+ }
10475
+ /**
10476
+ * Classes CSS pour le point d'un item.
10477
+ */
10478
+ getDotClasses(item) {
10479
+ return [
10480
+ 'ds-timeline__dot',
10481
+ item.icon ? 'ds-timeline__dot--with-icon' : '',
10482
+ item.color ? `ds-timeline__dot--${item.color}` : 'ds-timeline__dot--default',
10483
+ ].filter(Boolean);
10484
+ }
10485
+ /**
10486
+ * Détermine la position d'un item en fonction du mode.
10487
+ */
10488
+ getItemPosition(index) {
10489
+ const mode = this.mode();
10490
+ if (mode === 'left')
10491
+ return 'right'; // Contenu à droite de la ligne
10492
+ if (mode === 'right')
10493
+ return 'left'; // Contenu à gauche de la ligne
10494
+ // Mode alternate : alterner selon l'index
10495
+ return index % 2 === 0 ? 'right' : 'left';
10496
+ }
10497
+ /**
10498
+ * Gère le clic sur un item.
10499
+ */
10500
+ handleItemClick(item, index) {
10501
+ this.itemClick.emit({ item, index });
10502
+ }
10503
+ /**
10504
+ * Gère la touche Enter/Space sur un item.
10505
+ */
10506
+ handleKeydown(event, item, index) {
10507
+ if (event.key === 'Enter' || event.key === ' ') {
10508
+ event.preventDefault();
10509
+ this.handleItemClick(item, index);
10510
+ }
10511
+ }
10512
+ /**
10513
+ * Icône à afficher pour un item.
10514
+ */
10515
+ getIcon(item) {
10516
+ return item.icon || this.defaultIcon;
10517
+ }
10518
+ /**
10519
+ * Vérifie si c'est le dernier item.
10520
+ */
10521
+ isLastItem(index) {
10522
+ return index === this.sortedItems().length - 1;
10523
+ }
10524
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsTimeline, deps: [], target: i0.ɵɵFactoryTarget.Component });
10525
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsTimeline, isStandalone: true, selector: "ds-timeline", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, pending: { classPropertyName: "pending", publicName: "pending", isSignal: true, isRequired: false, transformFunction: null }, pendingContent: { classPropertyName: "pendingContent", publicName: "pendingContent", isSignal: true, isRequired: false, transformFunction: null }, reverse: { classPropertyName: "reverse", publicName: "reverse", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick" }, ngImport: i0, template: "<div [ngClass]=\"containerClasses()\" role=\"list\" aria-label=\"Timeline\">\n @for (item of sortedItems(); track $index) {\n <div\n [ngClass]=\"getItemClasses(item, $index)\"\n role=\"listitem\"\n tabindex=\"0\"\n (click)=\"handleItemClick(item, $index)\"\n (keydown)=\"handleKeydown($event, item, $index)\"\n [class.ds-timeline__item--last]=\"isLastItem($index) && !pending()\">\n\n <!-- Point de la timeline -->\n <div [ngClass]=\"getDotClasses(item)\" [attr.aria-label]=\"item.date || '\u00C9v\u00E9nement'\">\n @if (item.icon) {\n <fa-icon [icon]=\"getIcon(item)\" class=\"ds-timeline__icon\" aria-hidden=\"true\"></fa-icon>\n }\n </div>\n\n <!-- Ligne de connexion -->\n @if (!isLastItem($index) || pending()) {\n <div class=\"ds-timeline__line\" aria-hidden=\"true\"></div>\n }\n\n <!-- Contenu -->\n <div class=\"ds-timeline__content\">\n @if (item.date) {\n <div class=\"ds-timeline__date\">{{ item.date }}</div>\n }\n <div class=\"ds-timeline__text\">{{ item.content }}</div>\n </div>\n </div>\n }\n\n <!-- Indicateur pending -->\n @if (pending()) {\n <div\n class=\"ds-timeline__item ds-timeline__item--pending ds-timeline__item--{{ getItemPosition(sortedItems().length) }}\"\n role=\"listitem\">\n <div class=\"ds-timeline__dot ds-timeline__dot--pending ds-timeline__dot--with-icon\">\n <fa-icon [icon]=\"pendingIcon\" class=\"ds-timeline__icon ds-timeline__icon--pending\" aria-hidden=\"true\"></fa-icon>\n </div>\n <div class=\"ds-timeline__content\">\n <div class=\"ds-timeline__text ds-timeline__text--pending\">{{ pendingContent() }}</div>\n </div>\n </div>\n }\n</div>\n", styles: [".ds-timeline{display:flex;flex-direction:column;position:relative;width:100%}.ds-timeline__item{position:relative;display:grid;padding-bottom:var(--timeline-item-gap);cursor:pointer;transition:opacity .2s ease}.ds-timeline__item:focus{outline:2px solid var(--color-primary);outline-offset:2px;border-radius:var(--radius-1)}.ds-timeline__item:focus-visible{outline:2px solid var(--color-primary)}.ds-timeline__item--last{padding-bottom:0}.ds-timeline__item--pending{cursor:default;animation:pulse 2s ease-in-out infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.7}}.ds-timeline--left .ds-timeline__item{grid-template-columns:auto 1fr;grid-template-areas:\"dot content\" \"line content\"}.ds-timeline--right .ds-timeline__item{grid-template-columns:1fr auto;grid-template-areas:\"content dot\" \"content line\";text-align:right}.ds-timeline--right .ds-timeline__item .ds-timeline__content{padding-left:0;padding-right:var(--timeline-content-gap)}.ds-timeline--alternate .ds-timeline__item--left{grid-template-columns:1fr auto;grid-template-areas:\"content dot\" \"content line\";text-align:right}.ds-timeline--alternate .ds-timeline__item--left .ds-timeline__content{padding-left:0;padding-right:var(--timeline-content-gap)}.ds-timeline--alternate .ds-timeline__item--right{grid-template-columns:auto 1fr;grid-template-areas:\"dot content\" \"line content\"}.ds-timeline__dot{grid-area:dot;width:var(--timeline-dot-size);height:var(--timeline-dot-size);border-radius:50%;border:var(--timeline-dot-border-width) solid var(--timeline-dot-border-color);background:var(--timeline-dot-bg);display:flex;align-items:center;justify-content:center;flex-shrink:0;position:relative;z-index:2;transition:all .3s ease}.ds-timeline__dot--default{background:var(--timeline-dot-bg-default);border-color:var(--timeline-dot-border-default)}.ds-timeline__dot--primary{background:var(--timeline-dot-bg-primary);border-color:var(--timeline-dot-border-primary)}.ds-timeline__dot--success{background:var(--timeline-dot-bg-success);border-color:var(--timeline-dot-border-success)}.ds-timeline__dot--warning{background:var(--timeline-dot-bg-warning);border-color:var(--timeline-dot-border-warning)}.ds-timeline__dot--error{background:var(--timeline-dot-bg-error);border-color:var(--timeline-dot-border-error)}.ds-timeline__dot--info{background:var(--timeline-dot-bg-info);border-color:var(--timeline-dot-border-info)}.ds-timeline__dot--pending{background:var(--timeline-dot-bg-pending);border-color:var(--timeline-dot-border-pending)}.ds-timeline__dot--with-icon{background:transparent;border:none}.ds-timeline__item:hover .ds-timeline__dot{transform:scale(1.1)}.ds-timeline__icon{font-size:var(--timeline-icon-size);color:var(--timeline-icon-color)}.ds-timeline__icon--pending{color:var(--timeline-icon-color-pending);animation:spin 2s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.ds-timeline__line{grid-area:line;width:var(--timeline-line-width);background:var(--timeline-line-color);justify-self:center;flex:1;min-height:var(--timeline-item-gap);margin-top:4px}.ds-timeline__content{grid-area:content;padding-left:var(--timeline-content-gap);display:flex;flex-direction:column;gap:var(--timeline-content-inner-gap);min-width:0}.ds-timeline__date{font-size:var(--timeline-date-font-size);color:var(--timeline-date-color);font-weight:500}.ds-timeline__text{font-size:var(--timeline-text-font-size);color:var(--timeline-text-color);line-height:1.5}.ds-timeline__text--pending{color:var(--timeline-text-color-pending);font-style:italic}.ds-timeline--sm{--timeline-dot-size: var(--timeline-dot-size-sm);--timeline-line-width: var(--timeline-line-width-sm);--timeline-item-gap: var(--timeline-item-gap-sm);--timeline-content-gap: var(--timeline-content-gap-sm);--timeline-content-inner-gap: var(--timeline-content-inner-gap-sm);--timeline-date-font-size: var(--timeline-date-font-size-sm);--timeline-text-font-size: var(--timeline-text-font-size-sm);--timeline-icon-size: var(--timeline-icon-size-sm)}.ds-timeline--md{--timeline-dot-size: var(--timeline-dot-size-md);--timeline-line-width: var(--timeline-line-width-md);--timeline-item-gap: var(--timeline-item-gap-md);--timeline-content-gap: var(--timeline-content-gap-md);--timeline-content-inner-gap: var(--timeline-content-inner-gap-md);--timeline-date-font-size: var(--timeline-date-font-size-md);--timeline-text-font-size: var(--timeline-text-font-size-md);--timeline-icon-size: var(--timeline-icon-size-md)}.ds-timeline--lg{--timeline-dot-size: var(--timeline-dot-size-lg);--timeline-line-width: var(--timeline-line-width-lg);--timeline-item-gap: var(--timeline-item-gap-lg);--timeline-content-gap: var(--timeline-content-gap-lg);--timeline-content-inner-gap: var(--timeline-content-inner-gap-lg);--timeline-date-font-size: var(--timeline-date-font-size-lg);--timeline-text-font-size: var(--timeline-text-font-size-lg);--timeline-icon-size: var(--timeline-icon-size-lg)}\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"] }] });
10526
+ }
10527
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsTimeline, decorators: [{
10528
+ type: Component,
10529
+ args: [{ selector: 'ds-timeline', imports: [CommonModule, FontAwesomeModule], template: "<div [ngClass]=\"containerClasses()\" role=\"list\" aria-label=\"Timeline\">\n @for (item of sortedItems(); track $index) {\n <div\n [ngClass]=\"getItemClasses(item, $index)\"\n role=\"listitem\"\n tabindex=\"0\"\n (click)=\"handleItemClick(item, $index)\"\n (keydown)=\"handleKeydown($event, item, $index)\"\n [class.ds-timeline__item--last]=\"isLastItem($index) && !pending()\">\n\n <!-- Point de la timeline -->\n <div [ngClass]=\"getDotClasses(item)\" [attr.aria-label]=\"item.date || '\u00C9v\u00E9nement'\">\n @if (item.icon) {\n <fa-icon [icon]=\"getIcon(item)\" class=\"ds-timeline__icon\" aria-hidden=\"true\"></fa-icon>\n }\n </div>\n\n <!-- Ligne de connexion -->\n @if (!isLastItem($index) || pending()) {\n <div class=\"ds-timeline__line\" aria-hidden=\"true\"></div>\n }\n\n <!-- Contenu -->\n <div class=\"ds-timeline__content\">\n @if (item.date) {\n <div class=\"ds-timeline__date\">{{ item.date }}</div>\n }\n <div class=\"ds-timeline__text\">{{ item.content }}</div>\n </div>\n </div>\n }\n\n <!-- Indicateur pending -->\n @if (pending()) {\n <div\n class=\"ds-timeline__item ds-timeline__item--pending ds-timeline__item--{{ getItemPosition(sortedItems().length) }}\"\n role=\"listitem\">\n <div class=\"ds-timeline__dot ds-timeline__dot--pending ds-timeline__dot--with-icon\">\n <fa-icon [icon]=\"pendingIcon\" class=\"ds-timeline__icon ds-timeline__icon--pending\" aria-hidden=\"true\"></fa-icon>\n </div>\n <div class=\"ds-timeline__content\">\n <div class=\"ds-timeline__text ds-timeline__text--pending\">{{ pendingContent() }}</div>\n </div>\n </div>\n }\n</div>\n", styles: [".ds-timeline{display:flex;flex-direction:column;position:relative;width:100%}.ds-timeline__item{position:relative;display:grid;padding-bottom:var(--timeline-item-gap);cursor:pointer;transition:opacity .2s ease}.ds-timeline__item:focus{outline:2px solid var(--color-primary);outline-offset:2px;border-radius:var(--radius-1)}.ds-timeline__item:focus-visible{outline:2px solid var(--color-primary)}.ds-timeline__item--last{padding-bottom:0}.ds-timeline__item--pending{cursor:default;animation:pulse 2s ease-in-out infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.7}}.ds-timeline--left .ds-timeline__item{grid-template-columns:auto 1fr;grid-template-areas:\"dot content\" \"line content\"}.ds-timeline--right .ds-timeline__item{grid-template-columns:1fr auto;grid-template-areas:\"content dot\" \"content line\";text-align:right}.ds-timeline--right .ds-timeline__item .ds-timeline__content{padding-left:0;padding-right:var(--timeline-content-gap)}.ds-timeline--alternate .ds-timeline__item--left{grid-template-columns:1fr auto;grid-template-areas:\"content dot\" \"content line\";text-align:right}.ds-timeline--alternate .ds-timeline__item--left .ds-timeline__content{padding-left:0;padding-right:var(--timeline-content-gap)}.ds-timeline--alternate .ds-timeline__item--right{grid-template-columns:auto 1fr;grid-template-areas:\"dot content\" \"line content\"}.ds-timeline__dot{grid-area:dot;width:var(--timeline-dot-size);height:var(--timeline-dot-size);border-radius:50%;border:var(--timeline-dot-border-width) solid var(--timeline-dot-border-color);background:var(--timeline-dot-bg);display:flex;align-items:center;justify-content:center;flex-shrink:0;position:relative;z-index:2;transition:all .3s ease}.ds-timeline__dot--default{background:var(--timeline-dot-bg-default);border-color:var(--timeline-dot-border-default)}.ds-timeline__dot--primary{background:var(--timeline-dot-bg-primary);border-color:var(--timeline-dot-border-primary)}.ds-timeline__dot--success{background:var(--timeline-dot-bg-success);border-color:var(--timeline-dot-border-success)}.ds-timeline__dot--warning{background:var(--timeline-dot-bg-warning);border-color:var(--timeline-dot-border-warning)}.ds-timeline__dot--error{background:var(--timeline-dot-bg-error);border-color:var(--timeline-dot-border-error)}.ds-timeline__dot--info{background:var(--timeline-dot-bg-info);border-color:var(--timeline-dot-border-info)}.ds-timeline__dot--pending{background:var(--timeline-dot-bg-pending);border-color:var(--timeline-dot-border-pending)}.ds-timeline__dot--with-icon{background:transparent;border:none}.ds-timeline__item:hover .ds-timeline__dot{transform:scale(1.1)}.ds-timeline__icon{font-size:var(--timeline-icon-size);color:var(--timeline-icon-color)}.ds-timeline__icon--pending{color:var(--timeline-icon-color-pending);animation:spin 2s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.ds-timeline__line{grid-area:line;width:var(--timeline-line-width);background:var(--timeline-line-color);justify-self:center;flex:1;min-height:var(--timeline-item-gap);margin-top:4px}.ds-timeline__content{grid-area:content;padding-left:var(--timeline-content-gap);display:flex;flex-direction:column;gap:var(--timeline-content-inner-gap);min-width:0}.ds-timeline__date{font-size:var(--timeline-date-font-size);color:var(--timeline-date-color);font-weight:500}.ds-timeline__text{font-size:var(--timeline-text-font-size);color:var(--timeline-text-color);line-height:1.5}.ds-timeline__text--pending{color:var(--timeline-text-color-pending);font-style:italic}.ds-timeline--sm{--timeline-dot-size: var(--timeline-dot-size-sm);--timeline-line-width: var(--timeline-line-width-sm);--timeline-item-gap: var(--timeline-item-gap-sm);--timeline-content-gap: var(--timeline-content-gap-sm);--timeline-content-inner-gap: var(--timeline-content-inner-gap-sm);--timeline-date-font-size: var(--timeline-date-font-size-sm);--timeline-text-font-size: var(--timeline-text-font-size-sm);--timeline-icon-size: var(--timeline-icon-size-sm)}.ds-timeline--md{--timeline-dot-size: var(--timeline-dot-size-md);--timeline-line-width: var(--timeline-line-width-md);--timeline-item-gap: var(--timeline-item-gap-md);--timeline-content-gap: var(--timeline-content-gap-md);--timeline-content-inner-gap: var(--timeline-content-inner-gap-md);--timeline-date-font-size: var(--timeline-date-font-size-md);--timeline-text-font-size: var(--timeline-text-font-size-md);--timeline-icon-size: var(--timeline-icon-size-md)}.ds-timeline--lg{--timeline-dot-size: var(--timeline-dot-size-lg);--timeline-line-width: var(--timeline-line-width-lg);--timeline-item-gap: var(--timeline-item-gap-lg);--timeline-content-gap: var(--timeline-content-gap-lg);--timeline-content-inner-gap: var(--timeline-content-inner-gap-lg);--timeline-date-font-size: var(--timeline-date-font-size-lg);--timeline-text-font-size: var(--timeline-text-font-size-lg);--timeline-icon-size: var(--timeline-icon-size-lg)}\n"] }]
10530
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], pending: [{ type: i0.Input, args: [{ isSignal: true, alias: "pending", required: false }] }], pendingContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "pendingContent", required: false }] }], reverse: [{ type: i0.Input, args: [{ isSignal: true, alias: "reverse", required: false }] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }] } });
10531
+
10532
+ /**
10533
+ * DS Transfer
10534
+ *
10535
+ * Composant permettant de transférer des items entre deux listes (source et cible).
10536
+ * Inclut recherche, sélection multiple, et navigation clavier.
10537
+ *
10538
+ * @example
10539
+ * <ds-transfer
10540
+ * [source]="sourceItems"
10541
+ * [target]="targetItems"
10542
+ * sourceTitle="Disponibles"
10543
+ * targetTitle="Sélectionnés"
10544
+ * [showSearch]="true"
10545
+ * (transferChange)="onTransfer($event)"
10546
+ * />
10547
+ */
10548
+ class DsTransfer {
10549
+ // Inputs
10550
+ source = input([], ...(ngDevMode ? [{ debugName: "source" }] : []));
10551
+ target = input([], ...(ngDevMode ? [{ debugName: "target" }] : []));
10552
+ sourceTitle = input('Source', ...(ngDevMode ? [{ debugName: "sourceTitle" }] : []));
10553
+ targetTitle = input('Target', ...(ngDevMode ? [{ debugName: "targetTitle" }] : []));
10554
+ showSearch = input(true, ...(ngDevMode ? [{ debugName: "showSearch" }] : []));
10555
+ showSelectAll = input(true, ...(ngDevMode ? [{ debugName: "showSelectAll" }] : []));
10556
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
10557
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
10558
+ // Outputs
10559
+ transferChange = output();
10560
+ // Internal state
10561
+ sourceSelected = signal(new Set(), ...(ngDevMode ? [{ debugName: "sourceSelected" }] : []));
10562
+ targetSelected = signal(new Set(), ...(ngDevMode ? [{ debugName: "targetSelected" }] : []));
10563
+ sourceSearchQuery = signal('', ...(ngDevMode ? [{ debugName: "sourceSearchQuery" }] : []));
10564
+ targetSearchQuery = signal('', ...(ngDevMode ? [{ debugName: "targetSearchQuery" }] : []));
10565
+ // Computed
10566
+ filteredSource = computed(() => {
10567
+ const query = this.sourceSearchQuery().toLowerCase();
10568
+ if (!query)
10569
+ return this.source();
10570
+ return this.source().filter(item => item.label.toLowerCase().includes(query) ||
10571
+ item.description?.toLowerCase().includes(query));
10572
+ }, ...(ngDevMode ? [{ debugName: "filteredSource" }] : []));
10573
+ filteredTarget = computed(() => {
10574
+ const query = this.targetSearchQuery().toLowerCase();
10575
+ if (!query)
10576
+ return this.target();
10577
+ return this.target().filter(item => item.label.toLowerCase().includes(query) ||
10578
+ item.description?.toLowerCase().includes(query));
10579
+ }, ...(ngDevMode ? [{ debugName: "filteredTarget" }] : []));
10580
+ sourceSelectableCount = computed(() => this.filteredSource().filter(item => !item.disabled).length, ...(ngDevMode ? [{ debugName: "sourceSelectableCount" }] : []));
10581
+ targetSelectableCount = computed(() => this.filteredTarget().filter(item => !item.disabled).length, ...(ngDevMode ? [{ debugName: "targetSelectableCount" }] : []));
10582
+ isAllSourceSelected = computed(() => {
10583
+ const count = this.sourceSelectableCount();
10584
+ return count > 0 && this.sourceSelected().size === count;
10585
+ }, ...(ngDevMode ? [{ debugName: "isAllSourceSelected" }] : []));
10586
+ isAllTargetSelected = computed(() => {
10587
+ const count = this.targetSelectableCount();
10588
+ return count > 0 && this.targetSelected().size === count;
10589
+ }, ...(ngDevMode ? [{ debugName: "isAllTargetSelected" }] : []));
10590
+ canTransferToTarget = computed(() => this.sourceSelected().size > 0 && !this.disabled(), ...(ngDevMode ? [{ debugName: "canTransferToTarget" }] : []));
10591
+ canTransferToSource = computed(() => this.targetSelected().size > 0 && !this.disabled(), ...(ngDevMode ? [{ debugName: "canTransferToSource" }] : []));
10592
+ containerClasses = computed(() => ({
10593
+ 'ds-transfer': true,
10594
+ 'ds-transfer--disabled': this.disabled(),
10595
+ [`ds-transfer--${this.size()}`]: true,
10596
+ }), ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
10597
+ // Public methods
10598
+ transferToTarget() {
10599
+ if (!this.canTransferToTarget())
10600
+ return;
10601
+ const selectedKeys = Array.from(this.sourceSelected());
10602
+ const itemsToTransfer = this.source().filter(item => selectedKeys.includes(item.key));
10603
+ const newSource = this.source().filter(item => !selectedKeys.includes(item.key));
10604
+ const newTarget = [...this.target(), ...itemsToTransfer];
10605
+ this.sourceSelected.set(new Set());
10606
+ this.emitChange(newSource, newTarget);
10607
+ }
10608
+ transferToSource() {
10609
+ if (!this.canTransferToSource())
10610
+ return;
10611
+ const selectedKeys = Array.from(this.targetSelected());
10612
+ const itemsToTransfer = this.target().filter(item => selectedKeys.includes(item.key));
10613
+ const newTarget = this.target().filter(item => !selectedKeys.includes(item.key));
10614
+ const newSource = [...this.source(), ...itemsToTransfer];
10615
+ this.targetSelected.set(new Set());
10616
+ this.emitChange(newSource, newTarget);
10617
+ }
10618
+ toggleSelectAll(direction) {
10619
+ if (this.disabled())
10620
+ return;
10621
+ const isSource = direction === 'left';
10622
+ const items = isSource ? this.filteredSource() : this.filteredTarget();
10623
+ const selected = isSource ? this.sourceSelected() : this.targetSelected();
10624
+ const isAllSelected = isSource ? this.isAllSourceSelected() : this.isAllTargetSelected();
10625
+ const newSelected = new Set();
10626
+ if (!isAllSelected) {
10627
+ items.forEach(item => {
10628
+ if (!item.disabled) {
10629
+ newSelected.add(item.key);
10630
+ }
10631
+ });
10632
+ }
10633
+ if (isSource) {
10634
+ this.sourceSelected.set(newSelected);
10635
+ }
10636
+ else {
10637
+ this.targetSelected.set(newSelected);
10638
+ }
10639
+ }
10640
+ toggleItemSelection(item, direction) {
10641
+ if (this.disabled() || item.disabled)
10642
+ return;
10643
+ const isSource = direction === 'left';
10644
+ const selected = isSource ? this.sourceSelected() : this.targetSelected();
10645
+ const newSelected = new Set(selected);
10646
+ if (newSelected.has(item.key)) {
10647
+ newSelected.delete(item.key);
10648
+ }
10649
+ else {
10650
+ newSelected.add(item.key);
10651
+ }
10652
+ if (isSource) {
10653
+ this.sourceSelected.set(newSelected);
10654
+ }
10655
+ else {
10656
+ this.targetSelected.set(newSelected);
10657
+ }
10658
+ }
10659
+ isItemSelected(item, direction) {
10660
+ const selected = direction === 'left' ? this.sourceSelected() : this.targetSelected();
10661
+ return selected.has(item.key);
10662
+ }
10663
+ onSearchInput(event, direction) {
10664
+ const input = event.target;
10665
+ const query = input.value;
10666
+ if (direction === 'left') {
10667
+ this.sourceSearchQuery.set(query);
10668
+ }
10669
+ else {
10670
+ this.targetSearchQuery.set(query);
10671
+ }
10672
+ }
10673
+ clearSearch(direction) {
10674
+ if (direction === 'left') {
10675
+ this.sourceSearchQuery.set('');
10676
+ }
10677
+ else {
10678
+ this.targetSearchQuery.set('');
10679
+ }
10680
+ }
10681
+ getSelectedCount(direction) {
10682
+ return direction === 'left'
10683
+ ? this.sourceSelected().size
10684
+ : this.targetSelected().size;
10685
+ }
10686
+ getItemCount(direction) {
10687
+ return direction === 'left'
10688
+ ? this.filteredSource().length
10689
+ : this.filteredTarget().length;
10690
+ }
10691
+ emitChange(source, target) {
10692
+ this.transferChange.emit({ source, target });
10693
+ }
10694
+ // ARIA helpers
10695
+ getListboxId(direction) {
10696
+ return `ds-transfer-${direction}-listbox`;
10697
+ }
10698
+ getItemId(item, direction) {
10699
+ return `ds-transfer-${direction}-item-${item.key}`;
10700
+ }
10701
+ sourceListboxLabel = computed(() => `${this.sourceTitle()} (${this.getSelectedCount('left')}/${this.getItemCount('left')} sélectionné(s))`, ...(ngDevMode ? [{ debugName: "sourceListboxLabel" }] : []));
10702
+ targetListboxLabel = computed(() => `${this.targetTitle()} (${this.getSelectedCount('right')}/${this.getItemCount('right')} sélectionné(s))`, ...(ngDevMode ? [{ debugName: "targetListboxLabel" }] : []));
10703
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsTransfer, deps: [], target: i0.ɵɵFactoryTarget.Component });
10704
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsTransfer, isStandalone: true, selector: "ds-transfer", inputs: { source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null }, target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, sourceTitle: { classPropertyName: "sourceTitle", publicName: "sourceTitle", isSignal: true, isRequired: false, transformFunction: null }, targetTitle: { classPropertyName: "targetTitle", publicName: "targetTitle", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, showSelectAll: { classPropertyName: "showSelectAll", publicName: "showSelectAll", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { transferChange: "transferChange" }, ngImport: i0, template: "<div [ngClass]=\"containerClasses()\" role=\"group\" aria-label=\"Transfer component\">\n <!-- Source panel -->\n <div class=\"ds-transfer__panel ds-transfer__panel--source\">\n <!-- Header -->\n <div class=\"ds-transfer__header\">\n <div class=\"ds-transfer__title\">\n <span>{{ sourceTitle() }}</span>\n <span class=\"ds-transfer__count\">\n {{ getSelectedCount('left') }} / {{ source().length }}\n </span>\n </div>\n @if (showSelectAll()) {\n <primitive-checkbox\n [checked]=\"isAllSourceSelected()\"\n [indeterminate]=\"sourceSelected().size > 0 && !isAllSourceSelected()\"\n [disabled]=\"disabled() || sourceSelectableCount() === 0\"\n (checkedChange)=\"toggleSelectAll('left')\"\n size=\"sm\"\n label=\"Tout s\u00E9lectionner\"\n class=\"ds-transfer__select-all\"\n />\n }\n </div>\n\n <!-- Search -->\n @if (showSearch()) {\n <div class=\"ds-transfer__search\">\n <input\n type=\"text\"\n class=\"ds-transfer__search-input\"\n placeholder=\"Rechercher...\"\n [value]=\"sourceSearchQuery()\"\n (input)=\"onSearchInput($event, 'left')\"\n [disabled]=\"disabled()\"\n aria-label=\"Rechercher dans les items source\"\n />\n @if (sourceSearchQuery()) {\n <button\n type=\"button\"\n class=\"ds-transfer__search-clear\"\n (click)=\"clearSearch('left')\"\n aria-label=\"Effacer la recherche\"\n >\n \u2715\n </button>\n }\n </div>\n }\n\n <!-- List -->\n <div\n class=\"ds-transfer__list\"\n role=\"listbox\"\n [attr.aria-label]=\"sourceListboxLabel()\"\n [attr.id]=\"getListboxId('left')\"\n [attr.aria-multiselectable]=\"true\"\n >\n @if (filteredSource().length === 0) {\n <div class=\"ds-transfer__empty\">\n @if (sourceSearchQuery()) {\n Aucun r\u00E9sultat\n } @else {\n Aucun item disponible\n }\n </div>\n } @else {\n @for (item of filteredSource(); track item.key) {\n <div\n class=\"ds-transfer__item\"\n [class.ds-transfer__item--selected]=\"isItemSelected(item, 'left')\"\n [class.ds-transfer__item--disabled]=\"item.disabled\"\n role=\"option\"\n [attr.aria-selected]=\"isItemSelected(item, 'left')\"\n [attr.aria-disabled]=\"item.disabled\"\n [attr.id]=\"getItemId(item, 'left')\"\n (click)=\"toggleItemSelection(item, 'left')\"\n >\n <primitive-checkbox\n [checked]=\"isItemSelected(item, 'left')\"\n [disabled]=\"disabled() || (item.disabled ?? false)\"\n (checkedChange)=\"toggleItemSelection(item, 'left')\"\n size=\"sm\"\n />\n <div class=\"ds-transfer__item-content\">\n <div class=\"ds-transfer__item-label\">{{ item.label }}</div>\n @if (item.description) {\n <div class=\"ds-transfer__item-description\">{{ item.description }}</div>\n }\n </div>\n </div>\n }\n }\n </div>\n </div>\n\n <!-- Transfer buttons -->\n <div class=\"ds-transfer__actions\">\n <ds-button\n variant=\"secondary\"\n size=\"sm\"\n [disabled]=\"!canTransferToTarget()\"\n (click)=\"transferToTarget()\"\n aria-label=\"Transf\u00E9rer les items s\u00E9lectionn\u00E9s vers la droite\"\n >\n \u203A\n </ds-button>\n <ds-button\n variant=\"secondary\"\n size=\"sm\"\n [disabled]=\"!canTransferToSource()\"\n (click)=\"transferToSource()\"\n aria-label=\"Transf\u00E9rer les items s\u00E9lectionn\u00E9s vers la gauche\"\n >\n \u2039\n </ds-button>\n </div>\n\n <!-- Target panel -->\n <div class=\"ds-transfer__panel ds-transfer__panel--target\">\n <!-- Header -->\n <div class=\"ds-transfer__header\">\n <div class=\"ds-transfer__title\">\n <span>{{ targetTitle() }}</span>\n <span class=\"ds-transfer__count\">\n {{ getSelectedCount('right') }} / {{ target().length }}\n </span>\n </div>\n @if (showSelectAll()) {\n <primitive-checkbox\n [checked]=\"isAllTargetSelected()\"\n [indeterminate]=\"targetSelected().size > 0 && !isAllTargetSelected()\"\n [disabled]=\"disabled() || targetSelectableCount() === 0\"\n (checkedChange)=\"toggleSelectAll('right')\"\n size=\"sm\"\n label=\"Tout s\u00E9lectionner\"\n class=\"ds-transfer__select-all\"\n />\n }\n </div>\n\n <!-- Search -->\n @if (showSearch()) {\n <div class=\"ds-transfer__search\">\n <input\n type=\"text\"\n class=\"ds-transfer__search-input\"\n placeholder=\"Rechercher...\"\n [value]=\"targetSearchQuery()\"\n (input)=\"onSearchInput($event, 'right')\"\n [disabled]=\"disabled()\"\n aria-label=\"Rechercher dans les items cible\"\n />\n @if (targetSearchQuery()) {\n <button\n type=\"button\"\n class=\"ds-transfer__search-clear\"\n (click)=\"clearSearch('right')\"\n aria-label=\"Effacer la recherche\"\n >\n \u2715\n </button>\n }\n </div>\n }\n\n <!-- List -->\n <div\n class=\"ds-transfer__list\"\n role=\"listbox\"\n [attr.aria-label]=\"targetListboxLabel()\"\n [attr.id]=\"getListboxId('right')\"\n [attr.aria-multiselectable]=\"true\"\n >\n @if (filteredTarget().length === 0) {\n <div class=\"ds-transfer__empty\">\n @if (targetSearchQuery()) {\n Aucun r\u00E9sultat\n } @else {\n Aucun item s\u00E9lectionn\u00E9\n }\n </div>\n } @else {\n @for (item of filteredTarget(); track item.key) {\n <div\n class=\"ds-transfer__item\"\n [class.ds-transfer__item--selected]=\"isItemSelected(item, 'right')\"\n [class.ds-transfer__item--disabled]=\"item.disabled\"\n role=\"option\"\n [attr.aria-selected]=\"isItemSelected(item, 'right')\"\n [attr.aria-disabled]=\"item.disabled\"\n [attr.id]=\"getItemId(item, 'right')\"\n (click)=\"toggleItemSelection(item, 'right')\"\n >\n <primitive-checkbox\n [checked]=\"isItemSelected(item, 'right')\"\n [disabled]=\"disabled() || (item.disabled ?? false)\"\n (checkedChange)=\"toggleItemSelection(item, 'right')\"\n size=\"sm\"\n />\n <div class=\"ds-transfer__item-content\">\n <div class=\"ds-transfer__item-label\">{{ item.label }}</div>\n @if (item.description) {\n <div class=\"ds-transfer__item-description\">{{ item.description }}</div>\n }\n </div>\n </div>\n }\n }\n </div>\n </div>\n</div>\n", styles: [".ds-transfer{display:flex;align-items:center;gap:var(--transfer-gap, 1rem);width:100%}.ds-transfer__panel{flex:1;display:flex;flex-direction:column;border:1px solid var(--transfer-border-color, var(--gray-300));border-radius:var(--transfer-border-radius, var(--radius-2));background:var(--transfer-bg, var(--white));overflow:hidden}.ds-transfer__header{display:flex;align-items:center;justify-content:space-between;padding:var(--transfer-header-padding, .75rem 1rem);border-bottom:1px solid var(--transfer-header-border-color, var(--gray-200));background:var(--transfer-header-bg, var(--gray-50))}.ds-transfer__title{display:flex;align-items:center;gap:.5rem;font-weight:600;font-size:var(--transfer-title-font-size, var(--font-size-sm));color:var(--transfer-title-color, var(--text-default))}.ds-transfer__count{font-weight:400;font-size:var(--font-size-xs);color:var(--transfer-count-color, var(--text-light))}.ds-transfer__search{position:relative;padding:var(--transfer-search-padding, .75rem 1rem);border-bottom:1px solid var(--transfer-search-border-color, var(--gray-200))}.ds-transfer__search-input{width:100%;padding:var(--transfer-search-input-padding, .5rem 2rem .5rem .75rem);border:1px solid var(--transfer-search-input-border, var(--gray-300));border-radius:var(--transfer-search-input-radius, var(--radius-1));font-size:var(--font-size-sm);color:var(--transfer-search-input-text, var(--text-default));background:var(--transfer-search-input-bg, var(--white))}.ds-transfer__search-input::placeholder{color:var(--transfer-search-placeholder, var(--text-lighter))}.ds-transfer__search-input:focus{outline:none;border-color:var(--transfer-search-input-focus, var(--color-primary));box-shadow:0 0 0 3px var(--transfer-search-input-focus-shadow, rgba(59, 130, 246, .1))}.ds-transfer__search-input:disabled{opacity:.6;cursor:not-allowed;background:var(--transfer-search-input-disabled-bg, var(--gray-50))}.ds-transfer__search-clear{position:absolute;top:50%;right:1.5rem;transform:translateY(-50%);padding:.25rem;border:none;background:transparent;color:var(--transfer-search-clear-color, var(--text-lighter));cursor:pointer;font-size:1rem;line-height:1}.ds-transfer__search-clear:hover{color:var(--transfer-search-clear-hover, var(--text-default))}.ds-transfer__search-clear:focus{outline:2px solid var(--color-primary);outline-offset:2px}.ds-transfer__list{flex:1;overflow-y:auto;min-height:var(--transfer-list-min-height, 300px);max-height:var(--transfer-list-max-height, 400px)}.ds-transfer__empty{display:flex;align-items:center;justify-content:center;height:100%;padding:2rem;color:var(--transfer-empty-color, var(--text-lighter));font-size:var(--font-size-sm);text-align:center}.ds-transfer__item{display:flex;align-items:flex-start;gap:.75rem;padding:var(--transfer-item-padding, .75rem 1rem);border-bottom:1px solid var(--transfer-item-border, var(--gray-100));cursor:pointer;transition:background-color .2s}.ds-transfer__item:hover:not(.ds-transfer__item--disabled){background:var(--transfer-item-hover-bg, var(--gray-50))}.ds-transfer__item--selected{background:var(--transfer-item-selected-bg, var(--blue-50))}.ds-transfer__item--selected:hover:not(.ds-transfer__item--selected--disabled){background:var(--transfer-item-selected-hover-bg, var(--blue-100))}.ds-transfer__item--disabled{opacity:.5;cursor:not-allowed}.ds-transfer__item:last-child{border-bottom:none}.ds-transfer__item-content{flex:1;min-width:0}.ds-transfer__item-label{font-size:var(--transfer-item-font-size, var(--font-size-sm));font-weight:500;color:var(--transfer-item-label-color, var(--text-default));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ds-transfer__item-description{margin-top:.25rem;font-size:var(--font-size-xs);color:var(--transfer-item-description-color, var(--text-light));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ds-transfer__actions{display:flex;flex-direction:column;gap:var(--transfer-actions-gap, .5rem);align-self:center}.ds-transfer--sm .ds-transfer__header,.ds-transfer--sm .ds-transfer__search{padding:.5rem .75rem}.ds-transfer--sm .ds-transfer__search-input{padding:.375rem 1.5rem .375rem .5rem;font-size:var(--font-size-xs)}.ds-transfer--sm .ds-transfer__item{padding:.5rem .75rem}.ds-transfer--sm .ds-transfer__item-label{font-size:var(--font-size-xs)}.ds-transfer--sm .ds-transfer__list{min-height:200px;max-height:300px}.ds-transfer--lg .ds-transfer__header,.ds-transfer--lg .ds-transfer__search{padding:1rem 1.25rem}.ds-transfer--lg .ds-transfer__search-input{padding:.625rem 2.5rem .625rem 1rem;font-size:var(--font-size-base)}.ds-transfer--lg .ds-transfer__item{padding:1rem 1.25rem}.ds-transfer--lg .ds-transfer__item-label{font-size:var(--font-size-base)}.ds-transfer--lg .ds-transfer__list{min-height:400px;max-height:500px}.ds-transfer--disabled{opacity:.6;cursor:not-allowed}.ds-transfer--disabled .ds-transfer__panel,.ds-transfer--disabled .ds-transfer__item{pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: PrimitiveCheckbox, selector: "primitive-checkbox", inputs: ["checked", "indeterminate", "disabled", "size", "label", "checkboxId"], outputs: ["checkedChange"] }, { kind: "component", type: DsButton, selector: "ds-button", inputs: ["variant", "appearance", "size", "submit", "disabled", "iconStart", "iconEnd", "loading", "block"], outputs: ["clicked"] }] });
10705
+ }
10706
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsTransfer, decorators: [{
10707
+ type: Component,
10708
+ args: [{ selector: 'ds-transfer', standalone: true, imports: [CommonModule, FormsModule, PrimitiveCheckbox, DsButton], template: "<div [ngClass]=\"containerClasses()\" role=\"group\" aria-label=\"Transfer component\">\n <!-- Source panel -->\n <div class=\"ds-transfer__panel ds-transfer__panel--source\">\n <!-- Header -->\n <div class=\"ds-transfer__header\">\n <div class=\"ds-transfer__title\">\n <span>{{ sourceTitle() }}</span>\n <span class=\"ds-transfer__count\">\n {{ getSelectedCount('left') }} / {{ source().length }}\n </span>\n </div>\n @if (showSelectAll()) {\n <primitive-checkbox\n [checked]=\"isAllSourceSelected()\"\n [indeterminate]=\"sourceSelected().size > 0 && !isAllSourceSelected()\"\n [disabled]=\"disabled() || sourceSelectableCount() === 0\"\n (checkedChange)=\"toggleSelectAll('left')\"\n size=\"sm\"\n label=\"Tout s\u00E9lectionner\"\n class=\"ds-transfer__select-all\"\n />\n }\n </div>\n\n <!-- Search -->\n @if (showSearch()) {\n <div class=\"ds-transfer__search\">\n <input\n type=\"text\"\n class=\"ds-transfer__search-input\"\n placeholder=\"Rechercher...\"\n [value]=\"sourceSearchQuery()\"\n (input)=\"onSearchInput($event, 'left')\"\n [disabled]=\"disabled()\"\n aria-label=\"Rechercher dans les items source\"\n />\n @if (sourceSearchQuery()) {\n <button\n type=\"button\"\n class=\"ds-transfer__search-clear\"\n (click)=\"clearSearch('left')\"\n aria-label=\"Effacer la recherche\"\n >\n \u2715\n </button>\n }\n </div>\n }\n\n <!-- List -->\n <div\n class=\"ds-transfer__list\"\n role=\"listbox\"\n [attr.aria-label]=\"sourceListboxLabel()\"\n [attr.id]=\"getListboxId('left')\"\n [attr.aria-multiselectable]=\"true\"\n >\n @if (filteredSource().length === 0) {\n <div class=\"ds-transfer__empty\">\n @if (sourceSearchQuery()) {\n Aucun r\u00E9sultat\n } @else {\n Aucun item disponible\n }\n </div>\n } @else {\n @for (item of filteredSource(); track item.key) {\n <div\n class=\"ds-transfer__item\"\n [class.ds-transfer__item--selected]=\"isItemSelected(item, 'left')\"\n [class.ds-transfer__item--disabled]=\"item.disabled\"\n role=\"option\"\n [attr.aria-selected]=\"isItemSelected(item, 'left')\"\n [attr.aria-disabled]=\"item.disabled\"\n [attr.id]=\"getItemId(item, 'left')\"\n (click)=\"toggleItemSelection(item, 'left')\"\n >\n <primitive-checkbox\n [checked]=\"isItemSelected(item, 'left')\"\n [disabled]=\"disabled() || (item.disabled ?? false)\"\n (checkedChange)=\"toggleItemSelection(item, 'left')\"\n size=\"sm\"\n />\n <div class=\"ds-transfer__item-content\">\n <div class=\"ds-transfer__item-label\">{{ item.label }}</div>\n @if (item.description) {\n <div class=\"ds-transfer__item-description\">{{ item.description }}</div>\n }\n </div>\n </div>\n }\n }\n </div>\n </div>\n\n <!-- Transfer buttons -->\n <div class=\"ds-transfer__actions\">\n <ds-button\n variant=\"secondary\"\n size=\"sm\"\n [disabled]=\"!canTransferToTarget()\"\n (click)=\"transferToTarget()\"\n aria-label=\"Transf\u00E9rer les items s\u00E9lectionn\u00E9s vers la droite\"\n >\n \u203A\n </ds-button>\n <ds-button\n variant=\"secondary\"\n size=\"sm\"\n [disabled]=\"!canTransferToSource()\"\n (click)=\"transferToSource()\"\n aria-label=\"Transf\u00E9rer les items s\u00E9lectionn\u00E9s vers la gauche\"\n >\n \u2039\n </ds-button>\n </div>\n\n <!-- Target panel -->\n <div class=\"ds-transfer__panel ds-transfer__panel--target\">\n <!-- Header -->\n <div class=\"ds-transfer__header\">\n <div class=\"ds-transfer__title\">\n <span>{{ targetTitle() }}</span>\n <span class=\"ds-transfer__count\">\n {{ getSelectedCount('right') }} / {{ target().length }}\n </span>\n </div>\n @if (showSelectAll()) {\n <primitive-checkbox\n [checked]=\"isAllTargetSelected()\"\n [indeterminate]=\"targetSelected().size > 0 && !isAllTargetSelected()\"\n [disabled]=\"disabled() || targetSelectableCount() === 0\"\n (checkedChange)=\"toggleSelectAll('right')\"\n size=\"sm\"\n label=\"Tout s\u00E9lectionner\"\n class=\"ds-transfer__select-all\"\n />\n }\n </div>\n\n <!-- Search -->\n @if (showSearch()) {\n <div class=\"ds-transfer__search\">\n <input\n type=\"text\"\n class=\"ds-transfer__search-input\"\n placeholder=\"Rechercher...\"\n [value]=\"targetSearchQuery()\"\n (input)=\"onSearchInput($event, 'right')\"\n [disabled]=\"disabled()\"\n aria-label=\"Rechercher dans les items cible\"\n />\n @if (targetSearchQuery()) {\n <button\n type=\"button\"\n class=\"ds-transfer__search-clear\"\n (click)=\"clearSearch('right')\"\n aria-label=\"Effacer la recherche\"\n >\n \u2715\n </button>\n }\n </div>\n }\n\n <!-- List -->\n <div\n class=\"ds-transfer__list\"\n role=\"listbox\"\n [attr.aria-label]=\"targetListboxLabel()\"\n [attr.id]=\"getListboxId('right')\"\n [attr.aria-multiselectable]=\"true\"\n >\n @if (filteredTarget().length === 0) {\n <div class=\"ds-transfer__empty\">\n @if (targetSearchQuery()) {\n Aucun r\u00E9sultat\n } @else {\n Aucun item s\u00E9lectionn\u00E9\n }\n </div>\n } @else {\n @for (item of filteredTarget(); track item.key) {\n <div\n class=\"ds-transfer__item\"\n [class.ds-transfer__item--selected]=\"isItemSelected(item, 'right')\"\n [class.ds-transfer__item--disabled]=\"item.disabled\"\n role=\"option\"\n [attr.aria-selected]=\"isItemSelected(item, 'right')\"\n [attr.aria-disabled]=\"item.disabled\"\n [attr.id]=\"getItemId(item, 'right')\"\n (click)=\"toggleItemSelection(item, 'right')\"\n >\n <primitive-checkbox\n [checked]=\"isItemSelected(item, 'right')\"\n [disabled]=\"disabled() || (item.disabled ?? false)\"\n (checkedChange)=\"toggleItemSelection(item, 'right')\"\n size=\"sm\"\n />\n <div class=\"ds-transfer__item-content\">\n <div class=\"ds-transfer__item-label\">{{ item.label }}</div>\n @if (item.description) {\n <div class=\"ds-transfer__item-description\">{{ item.description }}</div>\n }\n </div>\n </div>\n }\n }\n </div>\n </div>\n</div>\n", styles: [".ds-transfer{display:flex;align-items:center;gap:var(--transfer-gap, 1rem);width:100%}.ds-transfer__panel{flex:1;display:flex;flex-direction:column;border:1px solid var(--transfer-border-color, var(--gray-300));border-radius:var(--transfer-border-radius, var(--radius-2));background:var(--transfer-bg, var(--white));overflow:hidden}.ds-transfer__header{display:flex;align-items:center;justify-content:space-between;padding:var(--transfer-header-padding, .75rem 1rem);border-bottom:1px solid var(--transfer-header-border-color, var(--gray-200));background:var(--transfer-header-bg, var(--gray-50))}.ds-transfer__title{display:flex;align-items:center;gap:.5rem;font-weight:600;font-size:var(--transfer-title-font-size, var(--font-size-sm));color:var(--transfer-title-color, var(--text-default))}.ds-transfer__count{font-weight:400;font-size:var(--font-size-xs);color:var(--transfer-count-color, var(--text-light))}.ds-transfer__search{position:relative;padding:var(--transfer-search-padding, .75rem 1rem);border-bottom:1px solid var(--transfer-search-border-color, var(--gray-200))}.ds-transfer__search-input{width:100%;padding:var(--transfer-search-input-padding, .5rem 2rem .5rem .75rem);border:1px solid var(--transfer-search-input-border, var(--gray-300));border-radius:var(--transfer-search-input-radius, var(--radius-1));font-size:var(--font-size-sm);color:var(--transfer-search-input-text, var(--text-default));background:var(--transfer-search-input-bg, var(--white))}.ds-transfer__search-input::placeholder{color:var(--transfer-search-placeholder, var(--text-lighter))}.ds-transfer__search-input:focus{outline:none;border-color:var(--transfer-search-input-focus, var(--color-primary));box-shadow:0 0 0 3px var(--transfer-search-input-focus-shadow, rgba(59, 130, 246, .1))}.ds-transfer__search-input:disabled{opacity:.6;cursor:not-allowed;background:var(--transfer-search-input-disabled-bg, var(--gray-50))}.ds-transfer__search-clear{position:absolute;top:50%;right:1.5rem;transform:translateY(-50%);padding:.25rem;border:none;background:transparent;color:var(--transfer-search-clear-color, var(--text-lighter));cursor:pointer;font-size:1rem;line-height:1}.ds-transfer__search-clear:hover{color:var(--transfer-search-clear-hover, var(--text-default))}.ds-transfer__search-clear:focus{outline:2px solid var(--color-primary);outline-offset:2px}.ds-transfer__list{flex:1;overflow-y:auto;min-height:var(--transfer-list-min-height, 300px);max-height:var(--transfer-list-max-height, 400px)}.ds-transfer__empty{display:flex;align-items:center;justify-content:center;height:100%;padding:2rem;color:var(--transfer-empty-color, var(--text-lighter));font-size:var(--font-size-sm);text-align:center}.ds-transfer__item{display:flex;align-items:flex-start;gap:.75rem;padding:var(--transfer-item-padding, .75rem 1rem);border-bottom:1px solid var(--transfer-item-border, var(--gray-100));cursor:pointer;transition:background-color .2s}.ds-transfer__item:hover:not(.ds-transfer__item--disabled){background:var(--transfer-item-hover-bg, var(--gray-50))}.ds-transfer__item--selected{background:var(--transfer-item-selected-bg, var(--blue-50))}.ds-transfer__item--selected:hover:not(.ds-transfer__item--selected--disabled){background:var(--transfer-item-selected-hover-bg, var(--blue-100))}.ds-transfer__item--disabled{opacity:.5;cursor:not-allowed}.ds-transfer__item:last-child{border-bottom:none}.ds-transfer__item-content{flex:1;min-width:0}.ds-transfer__item-label{font-size:var(--transfer-item-font-size, var(--font-size-sm));font-weight:500;color:var(--transfer-item-label-color, var(--text-default));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ds-transfer__item-description{margin-top:.25rem;font-size:var(--font-size-xs);color:var(--transfer-item-description-color, var(--text-light));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ds-transfer__actions{display:flex;flex-direction:column;gap:var(--transfer-actions-gap, .5rem);align-self:center}.ds-transfer--sm .ds-transfer__header,.ds-transfer--sm .ds-transfer__search{padding:.5rem .75rem}.ds-transfer--sm .ds-transfer__search-input{padding:.375rem 1.5rem .375rem .5rem;font-size:var(--font-size-xs)}.ds-transfer--sm .ds-transfer__item{padding:.5rem .75rem}.ds-transfer--sm .ds-transfer__item-label{font-size:var(--font-size-xs)}.ds-transfer--sm .ds-transfer__list{min-height:200px;max-height:300px}.ds-transfer--lg .ds-transfer__header,.ds-transfer--lg .ds-transfer__search{padding:1rem 1.25rem}.ds-transfer--lg .ds-transfer__search-input{padding:.625rem 2.5rem .625rem 1rem;font-size:var(--font-size-base)}.ds-transfer--lg .ds-transfer__item{padding:1rem 1.25rem}.ds-transfer--lg .ds-transfer__item-label{font-size:var(--font-size-base)}.ds-transfer--lg .ds-transfer__list{min-height:400px;max-height:500px}.ds-transfer--disabled{opacity:.6;cursor:not-allowed}.ds-transfer--disabled .ds-transfer__panel,.ds-transfer--disabled .ds-transfer__item{pointer-events:none}\n"] }]
10709
+ }], propDecorators: { source: [{ type: i0.Input, args: [{ isSignal: true, alias: "source", required: false }] }], target: [{ type: i0.Input, args: [{ isSignal: true, alias: "target", required: false }] }], sourceTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "sourceTitle", required: false }] }], targetTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "targetTitle", required: false }] }], showSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearch", required: false }] }], showSelectAll: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSelectAll", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], transferChange: [{ type: i0.Output, args: ["transferChange"] }] } });
10710
+
10711
+ class DsCarousel {
10712
+ // Config
10713
+ slides = input([], ...(ngDevMode ? [{ debugName: "slides" }] : []));
10714
+ autoplay = input(false, ...(ngDevMode ? [{ debugName: "autoplay" }] : []));
10715
+ autoplaySpeed = input(3000, ...(ngDevMode ? [{ debugName: "autoplaySpeed" }] : []));
10716
+ effect = input('slide', ...(ngDevMode ? [{ debugName: "effect" }] : []));
10717
+ dots = input(true, ...(ngDevMode ? [{ debugName: "dots" }] : []));
10718
+ dotsPosition = input('bottom', ...(ngDevMode ? [{ debugName: "dotsPosition" }] : []));
10719
+ arrows = input(true, ...(ngDevMode ? [{ debugName: "arrows" }] : []));
10720
+ infinite = input(true, ...(ngDevMode ? [{ debugName: "infinite" }] : []));
10721
+ pauseOnHover = input(true, ...(ngDevMode ? [{ debugName: "pauseOnHover" }] : []));
10722
+ activeIndex = input(0, ...(ngDevMode ? [{ debugName: "activeIndex" }] : []));
10723
+ // Events
10724
+ slideChange = output();
10725
+ // ViewChild
10726
+ trackElement;
10727
+ slideElements;
10728
+ // Icons
10729
+ icons = {
10730
+ left: faChevronLeft,
10731
+ right: faChevronRight,
10732
+ };
10733
+ // Internal state
10734
+ internalActiveIndex = signal(0, ...(ngDevMode ? [{ debugName: "internalActiveIndex" }] : []));
10735
+ isPaused = signal(false, ...(ngDevMode ? [{ debugName: "isPaused" }] : []));
10736
+ isTransitioning = signal(false, ...(ngDevMode ? [{ debugName: "isTransitioning" }] : []));
10737
+ autoplayTimer = null;
10738
+ startX = 0;
10739
+ startY = 0;
10740
+ isDragging = false;
10741
+ constructor() {
10742
+ // Sync external activeIndex with internal
10743
+ effect(() => {
10744
+ const extIndex = this.activeIndex();
10745
+ const totalSlides = this.slides().length;
10746
+ if (totalSlides > 0 && extIndex >= 0 && extIndex < totalSlides) {
10747
+ this.internalActiveIndex.set(extIndex);
10748
+ }
10749
+ });
10750
+ // Autoplay effect
10751
+ effect(() => {
10752
+ if (this.autoplay() && !this.isPaused()) {
10753
+ this.startAutoplay();
10754
+ }
10755
+ else {
10756
+ this.stopAutoplay();
10757
+ }
10758
+ });
10759
+ }
10760
+ ngAfterViewInit() {
10761
+ // Initialiser l'index actif
10762
+ const initialIndex = this.activeIndex();
10763
+ if (initialIndex >= 0 && initialIndex < this.slides().length) {
10764
+ this.internalActiveIndex.set(initialIndex);
10765
+ }
10766
+ }
10767
+ ngOnDestroy() {
10768
+ this.stopAutoplay();
10769
+ }
10770
+ containerClasses = computed(() => {
10771
+ const classes = ['ds-carousel'];
10772
+ classes.push(`ds-carousel--${this.effect()}`);
10773
+ classes.push(`ds-carousel--dots-${this.dotsPosition()}`);
10774
+ if (this.isTransitioning())
10775
+ classes.push('ds-carousel--transitioning');
10776
+ return classes.join(' ');
10777
+ }, ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
10778
+ trackStyle = computed(() => {
10779
+ if (this.effect() === 'slide') {
10780
+ const offset = -this.internalActiveIndex() * 100;
10781
+ return { transform: `translateX(${offset}%)` };
10782
+ }
10783
+ return {};
10784
+ }, ...(ngDevMode ? [{ debugName: "trackStyle" }] : []));
10785
+ slideClasses(index) {
10786
+ const classes = ['ds-carousel__slide'];
10787
+ const active = this.internalActiveIndex();
10788
+ if (index === active) {
10789
+ classes.push('ds-carousel__slide--active');
10790
+ }
10791
+ if (this.effect() === 'fade') {
10792
+ classes.push('ds-carousel__slide--fade');
10793
+ }
10794
+ return classes.join(' ');
10795
+ }
10796
+ dotClasses(index) {
10797
+ const classes = ['ds-carousel__dot'];
10798
+ if (index === this.internalActiveIndex()) {
10799
+ classes.push('ds-carousel__dot--active');
10800
+ }
10801
+ return classes.join(' ');
10802
+ }
10803
+ // Public methods
10804
+ next() {
10805
+ const totalSlides = this.slides().length;
10806
+ if (totalSlides === 0)
10807
+ return;
10808
+ const currentIndex = this.internalActiveIndex();
10809
+ let nextIndex = currentIndex + 1;
10810
+ if (nextIndex >= totalSlides) {
10811
+ nextIndex = this.infinite() ? 0 : currentIndex;
10812
+ }
10813
+ if (nextIndex !== currentIndex) {
10814
+ this.goTo(nextIndex);
10815
+ }
10816
+ }
10817
+ prev() {
10818
+ const totalSlides = this.slides().length;
10819
+ if (totalSlides === 0)
10820
+ return;
10821
+ const currentIndex = this.internalActiveIndex();
10822
+ let prevIndex = currentIndex - 1;
10823
+ if (prevIndex < 0) {
10824
+ prevIndex = this.infinite() ? totalSlides - 1 : currentIndex;
10825
+ }
10826
+ if (prevIndex !== currentIndex) {
10827
+ this.goTo(prevIndex);
10828
+ }
10829
+ }
10830
+ goTo(index) {
10831
+ const totalSlides = this.slides().length;
10832
+ if (index < 0 || index >= totalSlides || index === this.internalActiveIndex()) {
10833
+ return;
10834
+ }
10835
+ this.isTransitioning.set(true);
10836
+ this.internalActiveIndex.set(index);
10837
+ const slide = this.slides()[index];
10838
+ this.slideChange.emit({ index, slide });
10839
+ // Reset transitioning flag
10840
+ setTimeout(() => {
10841
+ this.isTransitioning.set(false);
10842
+ }, 300);
10843
+ // Reset autoplay
10844
+ if (this.autoplay()) {
10845
+ this.stopAutoplay();
10846
+ this.startAutoplay();
10847
+ }
10848
+ }
10849
+ pause() {
10850
+ this.isPaused.set(true);
10851
+ this.stopAutoplay();
10852
+ }
10853
+ play() {
10854
+ this.isPaused.set(false);
10855
+ if (this.autoplay()) {
10856
+ this.startAutoplay();
10857
+ }
10858
+ }
10859
+ onMouseEnter() {
10860
+ if (this.pauseOnHover() && this.autoplay()) {
10861
+ this.pause();
10862
+ }
10863
+ }
10864
+ onMouseLeave() {
10865
+ if (this.pauseOnHover() && this.autoplay()) {
10866
+ this.play();
10867
+ }
10868
+ }
10869
+ onKeydown(event) {
10870
+ switch (event.key) {
10871
+ case 'ArrowLeft':
10872
+ event.preventDefault();
10873
+ this.prev();
10874
+ break;
10875
+ case 'ArrowRight':
10876
+ event.preventDefault();
10877
+ this.next();
10878
+ break;
10879
+ case 'Home':
10880
+ event.preventDefault();
10881
+ this.goTo(0);
10882
+ break;
10883
+ case 'End':
10884
+ event.preventDefault();
10885
+ this.goTo(this.slides().length - 1);
10886
+ break;
10887
+ }
10888
+ }
10889
+ // Touch/swipe handlers
10890
+ onTouchStart(event) {
10891
+ this.startX = event.touches[0].clientX;
10892
+ this.startY = event.touches[0].clientY;
10893
+ this.isDragging = false;
10894
+ }
10895
+ onTouchMove(event) {
10896
+ if (!this.isDragging) {
10897
+ const deltaX = Math.abs(event.touches[0].clientX - this.startX);
10898
+ const deltaY = Math.abs(event.touches[0].clientY - this.startY);
10899
+ // Detect horizontal swipe
10900
+ if (deltaX > deltaY && deltaX > 10) {
10901
+ this.isDragging = true;
10902
+ }
10903
+ }
10904
+ }
10905
+ onTouchEnd(event) {
10906
+ if (!this.isDragging)
10907
+ return;
10908
+ const endX = event.changedTouches[0].clientX;
10909
+ const deltaX = endX - this.startX;
10910
+ if (Math.abs(deltaX) > 50) {
10911
+ if (deltaX > 0) {
10912
+ this.prev();
10913
+ }
10914
+ else {
10915
+ this.next();
10916
+ }
10917
+ }
10918
+ this.isDragging = false;
10919
+ }
10920
+ startAutoplay() {
10921
+ this.stopAutoplay();
10922
+ this.autoplayTimer = setInterval(() => {
10923
+ this.next();
10924
+ }, this.autoplaySpeed());
10925
+ }
10926
+ stopAutoplay() {
10927
+ if (this.autoplayTimer) {
10928
+ clearInterval(this.autoplayTimer);
10929
+ this.autoplayTimer = null;
10930
+ }
10931
+ }
10932
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsCarousel, deps: [], target: i0.ɵɵFactoryTarget.Component });
10933
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsCarousel, isStandalone: true, selector: "ds-carousel", inputs: { slides: { classPropertyName: "slides", publicName: "slides", isSignal: true, isRequired: false, transformFunction: null }, autoplay: { classPropertyName: "autoplay", publicName: "autoplay", isSignal: true, isRequired: false, transformFunction: null }, autoplaySpeed: { classPropertyName: "autoplaySpeed", publicName: "autoplaySpeed", isSignal: true, isRequired: false, transformFunction: null }, effect: { classPropertyName: "effect", publicName: "effect", isSignal: true, isRequired: false, transformFunction: null }, dots: { classPropertyName: "dots", publicName: "dots", isSignal: true, isRequired: false, transformFunction: null }, dotsPosition: { classPropertyName: "dotsPosition", publicName: "dotsPosition", isSignal: true, isRequired: false, transformFunction: null }, arrows: { classPropertyName: "arrows", publicName: "arrows", isSignal: true, isRequired: false, transformFunction: null }, infinite: { classPropertyName: "infinite", publicName: "infinite", isSignal: true, isRequired: false, transformFunction: null }, pauseOnHover: { classPropertyName: "pauseOnHover", publicName: "pauseOnHover", isSignal: true, isRequired: false, transformFunction: null }, activeIndex: { classPropertyName: "activeIndex", publicName: "activeIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { slideChange: "slideChange" }, viewQueries: [{ propertyName: "trackElement", first: true, predicate: ["track"], descendants: true }, { propertyName: "slideElements", predicate: ["slideElement"], descendants: true }], ngImport: i0, template: "<div\n [class]=\"containerClasses()\"\n (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\"\n (keydown)=\"onKeydown($event)\"\n (touchstart)=\"onTouchStart($event)\"\n (touchmove)=\"onTouchMove($event)\"\n (touchend)=\"onTouchEnd($event)\"\n tabindex=\"0\"\n role=\"region\"\n aria-roledescription=\"carousel\"\n [attr.aria-label]=\"'Carousel with ' + slides().length + ' slides'\"\n>\n <!-- Arrows -->\n @if (arrows() && slides().length > 1) {\n <button\n class=\"ds-carousel__arrow ds-carousel__arrow--left\"\n (click)=\"prev()\"\n [attr.aria-label]=\"'Previous slide'\"\n type=\"button\"\n >\n <fa-icon [icon]=\"icons.left\" />\n </button>\n\n <button\n class=\"ds-carousel__arrow ds-carousel__arrow--right\"\n (click)=\"next()\"\n [attr.aria-label]=\"'Next slide'\"\n type=\"button\"\n >\n <fa-icon [icon]=\"icons.right\" />\n </button>\n }\n\n <!-- Slides container -->\n <div class=\"ds-carousel__container\">\n <div\n class=\"ds-carousel__track\"\n #track\n [ngStyle]=\"trackStyle()\"\n role=\"list\"\n >\n @for (slide of slides(); track slide.id; let i = $index) {\n <div\n class=\"{{ slideClasses(i) }}\"\n #slideElement\n role=\"listitem\"\n [attr.aria-hidden]=\"i !== internalActiveIndex()\"\n [attr.aria-label]=\"'Slide ' + (i + 1) + ' of ' + slides().length\"\n >\n @if (slide.image) {\n <img\n [src]=\"slide.image\"\n [alt]=\"slide.alt || slide.title || 'Slide ' + (i + 1)\"\n class=\"ds-carousel__image\"\n />\n }\n\n @if (slide.title || slide.description) {\n <div class=\"ds-carousel__content\">\n @if (slide.title) {\n <h3 class=\"ds-carousel__title\">{{ slide.title }}</h3>\n }\n @if (slide.description) {\n <p class=\"ds-carousel__description\">{{ slide.description }}</p>\n }\n </div>\n }\n\n <!-- Content projection for custom slides -->\n <ng-content select=\"[carousel-slide]\" />\n </div>\n }\n </div>\n </div>\n\n <!-- Dots -->\n @if (dots() && slides().length > 1) {\n <div\n class=\"ds-carousel__dots\"\n role=\"tablist\"\n [attr.aria-label]=\"'Carousel navigation'\"\n >\n @for (slide of slides(); track slide.id; let i = $index) {\n <button\n type=\"button\"\n [class]=\"dotClasses(i)\"\n (click)=\"goTo(i)\"\n role=\"tab\"\n [attr.aria-selected]=\"i === internalActiveIndex()\"\n [attr.aria-label]=\"'Go to slide ' + (i + 1)\"\n [attr.aria-controls]=\"'carousel-slide-' + i\"\n >\n <span class=\"ds-carousel__dot-inner\"></span>\n </button>\n }\n </div>\n }\n</div>\n", styles: [".ds-carousel{position:relative;width:100%;overflow:hidden;background-color:var(--carousel-bg, var(--gray-50));border-radius:var(--carousel-radius, var(--radius-2));outline:none}.ds-carousel__container{position:relative;width:100%;height:400px;overflow:hidden}.ds-carousel__track{display:flex;height:100%;transition:transform .3s ease-in-out}.ds-carousel__slide{position:relative;flex:0 0 100%;width:100%;height:100%;display:flex;align-items:center;justify-content:center;opacity:1;transition:opacity .3s ease-in-out}.ds-carousel__slide--fade{position:absolute;top:0;left:0;opacity:0;transition:opacity .5s ease-in-out}.ds-carousel__slide--fade.ds-carousel__slide--active{opacity:1;z-index:1}.ds-carousel__image{width:100%;height:100%;object-fit:cover;display:block}.ds-carousel__content{position:absolute;bottom:0;left:0;right:0;padding:var(--carousel-content-padding, var(--space-6));background:var(--carousel-content-bg, linear-gradient(to top, rgba(0, 0, 0, .7), transparent));color:var(--carousel-content-text, var(--white))}.ds-carousel__title{margin:0 0 var(--space-2) 0;font-size:var(--carousel-title-size, var(--font-size-5));font-weight:var(--font-weight-bold, 700);line-height:1.2}.ds-carousel__description{margin:0;font-size:var(--carousel-description-size, var(--font-size-3));line-height:1.5;opacity:.95}.ds-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:10;display:flex;align-items:center;justify-content:center;width:var(--carousel-arrow-size, 40px);height:var(--carousel-arrow-size, 40px);padding:0;background-color:var(--carousel-arrow-bg, rgba(255, 255, 255, .9));color:var(--carousel-arrow-color, var(--gray-800));border:none;border-radius:var(--carousel-arrow-radius, 50%);cursor:pointer;transition:all .2s ease;box-shadow:var(--carousel-arrow-shadow, var(--shadow-md))}.ds-carousel__arrow:hover{background-color:var(--carousel-arrow-bg-hover, var(--white));color:var(--carousel-arrow-color-hover, var(--color-primary));transform:translateY(-50%) scale(1.1)}.ds-carousel__arrow:active{transform:translateY(-50%) scale(.95)}.ds-carousel__arrow:focus-visible{outline:2px solid var(--carousel-arrow-focus, var(--color-primary));outline-offset:2px}.ds-carousel__arrow--left{left:var(--carousel-arrow-offset, var(--space-4))}.ds-carousel__arrow--right{right:var(--carousel-arrow-offset, var(--space-4))}.ds-carousel__arrow fa-icon{font-size:var(--carousel-arrow-icon-size, var(--font-size-4))}.ds-carousel__dots{position:absolute;z-index:10;display:flex;gap:var(--carousel-dots-gap, var(--space-2));padding:var(--carousel-dots-padding, var(--space-4))}.ds-carousel--dots-bottom .ds-carousel__dots{bottom:0;left:50%;transform:translate(-50%);flex-direction:row}.ds-carousel--dots-top .ds-carousel__dots{top:0;left:50%;transform:translate(-50%);flex-direction:row}.ds-carousel--dots-left .ds-carousel__dots{top:50%;left:0;transform:translateY(-50%);flex-direction:column}.ds-carousel--dots-right .ds-carousel__dots{top:50%;right:0;transform:translateY(-50%);flex-direction:column}.ds-carousel__dot{padding:0;border:none;background:transparent;cursor:pointer;transition:all .3s ease}.ds-carousel__dot:focus-visible{outline:2px solid var(--carousel-dot-focus, var(--color-primary));outline-offset:2px;border-radius:50%}.ds-carousel__dot-inner{display:block;width:var(--carousel-dot-size, 10px);height:var(--carousel-dot-size, 10px);background-color:var(--carousel-dot-bg, rgba(255, 255, 255, .5));border-radius:50%;transition:all .3s ease}.ds-carousel__dot--active .ds-carousel__dot-inner{background-color:var(--carousel-dot-bg-active, var(--white));width:var(--carousel-dot-size-active, 12px);height:var(--carousel-dot-size-active, 12px)}.ds-carousel__dot:hover .ds-carousel__dot-inner{background-color:var(--carousel-dot-bg-hover, rgba(255, 255, 255, .8));transform:scale(1.2)}.ds-carousel--fade .ds-carousel__track{position:relative}.ds-carousel--fade .ds-carousel__slide{position:absolute;top:0;left:0}.ds-carousel--transitioning{-webkit-user-select:none;user-select:none}@media (max-width: 768px){.ds-carousel__container{height:300px}.ds-carousel__content{padding:var(--space-4)}.ds-carousel__title{font-size:var(--font-size-4)}.ds-carousel__description{font-size:var(--font-size-2)}.ds-carousel__arrow{width:32px;height:32px}.ds-carousel__arrow fa-icon{font-size:var(--font-size-3)}.ds-carousel__arrow--left,.ds-carousel__arrow--right{--carousel-arrow-offset: var(--space-2)}}@media (max-width: 480px){.ds-carousel__container{height:250px}.ds-carousel__arrow{width:28px;height:28px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { 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"] }] });
10934
+ }
10935
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsCarousel, decorators: [{
10936
+ type: Component,
10937
+ args: [{ selector: 'ds-carousel', imports: [CommonModule, FontAwesomeModule], template: "<div\n [class]=\"containerClasses()\"\n (mouseenter)=\"onMouseEnter()\"\n (mouseleave)=\"onMouseLeave()\"\n (keydown)=\"onKeydown($event)\"\n (touchstart)=\"onTouchStart($event)\"\n (touchmove)=\"onTouchMove($event)\"\n (touchend)=\"onTouchEnd($event)\"\n tabindex=\"0\"\n role=\"region\"\n aria-roledescription=\"carousel\"\n [attr.aria-label]=\"'Carousel with ' + slides().length + ' slides'\"\n>\n <!-- Arrows -->\n @if (arrows() && slides().length > 1) {\n <button\n class=\"ds-carousel__arrow ds-carousel__arrow--left\"\n (click)=\"prev()\"\n [attr.aria-label]=\"'Previous slide'\"\n type=\"button\"\n >\n <fa-icon [icon]=\"icons.left\" />\n </button>\n\n <button\n class=\"ds-carousel__arrow ds-carousel__arrow--right\"\n (click)=\"next()\"\n [attr.aria-label]=\"'Next slide'\"\n type=\"button\"\n >\n <fa-icon [icon]=\"icons.right\" />\n </button>\n }\n\n <!-- Slides container -->\n <div class=\"ds-carousel__container\">\n <div\n class=\"ds-carousel__track\"\n #track\n [ngStyle]=\"trackStyle()\"\n role=\"list\"\n >\n @for (slide of slides(); track slide.id; let i = $index) {\n <div\n class=\"{{ slideClasses(i) }}\"\n #slideElement\n role=\"listitem\"\n [attr.aria-hidden]=\"i !== internalActiveIndex()\"\n [attr.aria-label]=\"'Slide ' + (i + 1) + ' of ' + slides().length\"\n >\n @if (slide.image) {\n <img\n [src]=\"slide.image\"\n [alt]=\"slide.alt || slide.title || 'Slide ' + (i + 1)\"\n class=\"ds-carousel__image\"\n />\n }\n\n @if (slide.title || slide.description) {\n <div class=\"ds-carousel__content\">\n @if (slide.title) {\n <h3 class=\"ds-carousel__title\">{{ slide.title }}</h3>\n }\n @if (slide.description) {\n <p class=\"ds-carousel__description\">{{ slide.description }}</p>\n }\n </div>\n }\n\n <!-- Content projection for custom slides -->\n <ng-content select=\"[carousel-slide]\" />\n </div>\n }\n </div>\n </div>\n\n <!-- Dots -->\n @if (dots() && slides().length > 1) {\n <div\n class=\"ds-carousel__dots\"\n role=\"tablist\"\n [attr.aria-label]=\"'Carousel navigation'\"\n >\n @for (slide of slides(); track slide.id; let i = $index) {\n <button\n type=\"button\"\n [class]=\"dotClasses(i)\"\n (click)=\"goTo(i)\"\n role=\"tab\"\n [attr.aria-selected]=\"i === internalActiveIndex()\"\n [attr.aria-label]=\"'Go to slide ' + (i + 1)\"\n [attr.aria-controls]=\"'carousel-slide-' + i\"\n >\n <span class=\"ds-carousel__dot-inner\"></span>\n </button>\n }\n </div>\n }\n</div>\n", styles: [".ds-carousel{position:relative;width:100%;overflow:hidden;background-color:var(--carousel-bg, var(--gray-50));border-radius:var(--carousel-radius, var(--radius-2));outline:none}.ds-carousel__container{position:relative;width:100%;height:400px;overflow:hidden}.ds-carousel__track{display:flex;height:100%;transition:transform .3s ease-in-out}.ds-carousel__slide{position:relative;flex:0 0 100%;width:100%;height:100%;display:flex;align-items:center;justify-content:center;opacity:1;transition:opacity .3s ease-in-out}.ds-carousel__slide--fade{position:absolute;top:0;left:0;opacity:0;transition:opacity .5s ease-in-out}.ds-carousel__slide--fade.ds-carousel__slide--active{opacity:1;z-index:1}.ds-carousel__image{width:100%;height:100%;object-fit:cover;display:block}.ds-carousel__content{position:absolute;bottom:0;left:0;right:0;padding:var(--carousel-content-padding, var(--space-6));background:var(--carousel-content-bg, linear-gradient(to top, rgba(0, 0, 0, .7), transparent));color:var(--carousel-content-text, var(--white))}.ds-carousel__title{margin:0 0 var(--space-2) 0;font-size:var(--carousel-title-size, var(--font-size-5));font-weight:var(--font-weight-bold, 700);line-height:1.2}.ds-carousel__description{margin:0;font-size:var(--carousel-description-size, var(--font-size-3));line-height:1.5;opacity:.95}.ds-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:10;display:flex;align-items:center;justify-content:center;width:var(--carousel-arrow-size, 40px);height:var(--carousel-arrow-size, 40px);padding:0;background-color:var(--carousel-arrow-bg, rgba(255, 255, 255, .9));color:var(--carousel-arrow-color, var(--gray-800));border:none;border-radius:var(--carousel-arrow-radius, 50%);cursor:pointer;transition:all .2s ease;box-shadow:var(--carousel-arrow-shadow, var(--shadow-md))}.ds-carousel__arrow:hover{background-color:var(--carousel-arrow-bg-hover, var(--white));color:var(--carousel-arrow-color-hover, var(--color-primary));transform:translateY(-50%) scale(1.1)}.ds-carousel__arrow:active{transform:translateY(-50%) scale(.95)}.ds-carousel__arrow:focus-visible{outline:2px solid var(--carousel-arrow-focus, var(--color-primary));outline-offset:2px}.ds-carousel__arrow--left{left:var(--carousel-arrow-offset, var(--space-4))}.ds-carousel__arrow--right{right:var(--carousel-arrow-offset, var(--space-4))}.ds-carousel__arrow fa-icon{font-size:var(--carousel-arrow-icon-size, var(--font-size-4))}.ds-carousel__dots{position:absolute;z-index:10;display:flex;gap:var(--carousel-dots-gap, var(--space-2));padding:var(--carousel-dots-padding, var(--space-4))}.ds-carousel--dots-bottom .ds-carousel__dots{bottom:0;left:50%;transform:translate(-50%);flex-direction:row}.ds-carousel--dots-top .ds-carousel__dots{top:0;left:50%;transform:translate(-50%);flex-direction:row}.ds-carousel--dots-left .ds-carousel__dots{top:50%;left:0;transform:translateY(-50%);flex-direction:column}.ds-carousel--dots-right .ds-carousel__dots{top:50%;right:0;transform:translateY(-50%);flex-direction:column}.ds-carousel__dot{padding:0;border:none;background:transparent;cursor:pointer;transition:all .3s ease}.ds-carousel__dot:focus-visible{outline:2px solid var(--carousel-dot-focus, var(--color-primary));outline-offset:2px;border-radius:50%}.ds-carousel__dot-inner{display:block;width:var(--carousel-dot-size, 10px);height:var(--carousel-dot-size, 10px);background-color:var(--carousel-dot-bg, rgba(255, 255, 255, .5));border-radius:50%;transition:all .3s ease}.ds-carousel__dot--active .ds-carousel__dot-inner{background-color:var(--carousel-dot-bg-active, var(--white));width:var(--carousel-dot-size-active, 12px);height:var(--carousel-dot-size-active, 12px)}.ds-carousel__dot:hover .ds-carousel__dot-inner{background-color:var(--carousel-dot-bg-hover, rgba(255, 255, 255, .8));transform:scale(1.2)}.ds-carousel--fade .ds-carousel__track{position:relative}.ds-carousel--fade .ds-carousel__slide{position:absolute;top:0;left:0}.ds-carousel--transitioning{-webkit-user-select:none;user-select:none}@media (max-width: 768px){.ds-carousel__container{height:300px}.ds-carousel__content{padding:var(--space-4)}.ds-carousel__title{font-size:var(--font-size-4)}.ds-carousel__description{font-size:var(--font-size-2)}.ds-carousel__arrow{width:32px;height:32px}.ds-carousel__arrow fa-icon{font-size:var(--font-size-3)}.ds-carousel__arrow--left,.ds-carousel__arrow--right{--carousel-arrow-offset: var(--space-2)}}@media (max-width: 480px){.ds-carousel__container{height:250px}.ds-carousel__arrow{width:28px;height:28px}}\n"] }]
10938
+ }], ctorParameters: () => [], propDecorators: { slides: [{ type: i0.Input, args: [{ isSignal: true, alias: "slides", required: false }] }], autoplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplay", required: false }] }], autoplaySpeed: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplaySpeed", required: false }] }], effect: [{ type: i0.Input, args: [{ isSignal: true, alias: "effect", required: false }] }], dots: [{ type: i0.Input, args: [{ isSignal: true, alias: "dots", required: false }] }], dotsPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "dotsPosition", required: false }] }], arrows: [{ type: i0.Input, args: [{ isSignal: true, alias: "arrows", required: false }] }], infinite: [{ type: i0.Input, args: [{ isSignal: true, alias: "infinite", required: false }] }], pauseOnHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "pauseOnHover", required: false }] }], activeIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIndex", required: false }] }], slideChange: [{ type: i0.Output, args: ["slideChange"] }], trackElement: [{
10939
+ type: ViewChild,
10940
+ args: ['track', { static: false }]
10941
+ }], slideElements: [{
10942
+ type: ViewChildren,
10943
+ args: ['slideElement']
10944
+ }] } });
10945
+
10946
+ class DsNotificationService {
10947
+ notifications = signal([], ...(ngDevMode ? [{ debugName: "notifications" }] : []));
10948
+ nextId = 1;
10949
+ notifications$ = this.notifications.asReadonly();
10950
+ open(config) {
10951
+ const id = config.id || `notification-${this.nextId++}`;
10952
+ const notification = {
10953
+ ...config,
10954
+ id,
10955
+ timestamp: Date.now(),
10956
+ closable: config.closable ?? true,
10957
+ duration: config.duration ?? 4500
10958
+ };
10959
+ // Set default icons based on type
10960
+ if (!notification.icon && notification.type) {
10961
+ notification.icon = this.getDefaultIcon(notification.type);
10962
+ }
10963
+ this.notifications.update(items => [...items, notification]);
10964
+ // Auto-dismiss if duration > 0
10965
+ if (notification.duration && notification.duration > 0) {
10966
+ setTimeout(() => {
10967
+ this.close(id);
10968
+ }, notification.duration);
10969
+ }
10970
+ return id;
10971
+ }
10972
+ info(title, message, config) {
10973
+ return this.open({
10974
+ ...config,
10975
+ title,
10976
+ message,
10977
+ type: 'info'
10978
+ });
10979
+ }
10980
+ success(title, message, config) {
10981
+ return this.open({
10982
+ ...config,
10983
+ title,
10984
+ message,
10985
+ type: 'success'
10986
+ });
10987
+ }
10988
+ warning(title, message, config) {
10989
+ return this.open({
10990
+ ...config,
10991
+ title,
10992
+ message,
10993
+ type: 'warning'
10994
+ });
10995
+ }
10996
+ error(title, message, config) {
10997
+ return this.open({
10998
+ ...config,
10999
+ title,
11000
+ message,
11001
+ type: 'error'
11002
+ });
11003
+ }
11004
+ close(id) {
11005
+ this.notifications.update(items => items.filter(item => item.id !== id));
11006
+ }
11007
+ closeAll() {
11008
+ this.notifications.set([]);
11009
+ }
11010
+ getDefaultIcon(type) {
11011
+ switch (type) {
11012
+ case 'info':
11013
+ return faCircleInfo;
11014
+ case 'success':
11015
+ return faCircleCheck;
11016
+ case 'warning':
11017
+ return faTriangleExclamation;
11018
+ case 'error':
11019
+ return faCircleXmark;
11020
+ default:
11021
+ return faCircleInfo;
11022
+ }
11023
+ }
11024
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsNotificationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
11025
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsNotificationService, providedIn: 'root' });
11026
+ }
11027
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsNotificationService, decorators: [{
11028
+ type: Injectable,
11029
+ args: [{
11030
+ providedIn: 'root'
11031
+ }]
11032
+ }] });
11033
+
11034
+ class DsNotificationItemComponent {
11035
+ notification = input.required(...(ngDevMode ? [{ debugName: "notification" }] : []));
11036
+ closeClick = output();
11037
+ actionClick = output();
11038
+ closeIcon = faXmark;
11039
+ notificationClasses = () => {
11040
+ const classes = ['ds-notification'];
11041
+ const notification = this.notification();
11042
+ if (notification.type) {
11043
+ classes.push(`ds-notification--${notification.type}`);
11044
+ }
11045
+ return classes;
11046
+ };
11047
+ onClose() {
11048
+ this.closeClick.emit(this.notification().id);
11049
+ }
11050
+ onActionClick(index) {
11051
+ const notification = this.notification();
11052
+ if (notification.actions && notification.actions[index]) {
11053
+ notification.actions[index].handler();
11054
+ this.actionClick.emit({ id: notification.id, actionIndex: index });
11055
+ }
11056
+ }
11057
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsNotificationItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11058
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsNotificationItemComponent, isStandalone: true, selector: "ds-notification-item", inputs: { notification: { classPropertyName: "notification", publicName: "notification", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { closeClick: "closeClick", actionClick: "actionClick" }, ngImport: i0, template: "<div\n [class]=\"notificationClasses().join(' ')\"\n role=\"alert\"\n [attr.aria-live]=\"notification().type === 'error' ? 'assertive' : 'polite'\"\n [attr.aria-atomic]=\"true\"\n>\n <div class=\"ds-notification__header\">\n <div class=\"ds-notification__icon-title\">\n @if (notification().icon) {\n <fa-icon\n [icon]=\"notification().icon\"\n class=\"ds-notification__icon\"\n [attr.aria-hidden]=\"true\"\n />\n }\n <h4 class=\"ds-notification__title\">{{ notification().title }}</h4>\n </div>\n @if (notification().closable) {\n <button\n type=\"button\"\n class=\"ds-notification__close\"\n (click)=\"onClose()\"\n [attr.aria-label]=\"'Fermer la notification'\"\n >\n <fa-icon [icon]=\"closeIcon\" />\n </button>\n }\n </div>\n\n <div class=\"ds-notification__body\">\n <p class=\"ds-notification__message\">{{ notification().message }}</p>\n </div>\n\n @if (notification().actions && notification().actions!.length > 0) {\n <div class=\"ds-notification__actions\">\n @for (action of notification().actions!; track $index) {\n <button\n type=\"button\"\n class=\"ds-notification__action ds-notification__action--{{ action.variant || 'secondary' }}\"\n (click)=\"onActionClick($index)\"\n >\n {{ action.label }}\n </button>\n }\n </div>\n }\n</div>\n", styles: [".ds-notification-container{position:fixed;z-index:var(--notification-z-index, 1050);pointer-events:none;display:flex;flex-direction:column;gap:var(--notification-gap, var(--space-3, .75rem));max-width:var(--notification-max-width, 384px);width:100%}.ds-notification-container--topRight{top:var(--notification-offset-y, var(--space-6, 1.5rem));right:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-container--topLeft{top:var(--notification-offset-y, var(--space-6, 1.5rem));left:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-container--bottomRight{bottom:var(--notification-offset-y, var(--space-6, 1.5rem));right:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-container--bottomLeft{bottom:var(--notification-offset-y, var(--space-6, 1.5rem));left:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-wrapper{pointer-events:auto;animation:var(--notification-animation-in, slideInRight .3s ease-out)}@keyframes slideInRight{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes slideInLeft{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}.ds-notification{background:var(--notification-bg, var(--color-white, #ffffff));border:var(--notification-border-width, 1px) solid var(--notification-border-color, var(--gray-300, #d1d5db));border-radius:var(--notification-radius, var(--radius-2, .5rem));box-shadow:var(--notification-shadow, var(--shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, .1)));padding:var(--notification-padding, var(--space-4, 1rem));display:flex;flex-direction:column;gap:var(--notification-gap-inner, var(--space-3, .75rem));width:100%}.ds-notification__header{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--space-2, .5rem)}.ds-notification__icon-title{display:flex;align-items:center;gap:var(--notification-icon-gap, var(--space-2, .5rem));flex:1}.ds-notification__icon{font-size:var(--notification-icon-size, 1.25rem);flex-shrink:0;color:var(--notification-icon-color, var(--gray-500, #6b7280))}.ds-notification__title{margin:0;font-size:var(--notification-title-size, var(--font-size-base, 1rem));font-weight:var(--notification-title-weight, 600);color:var(--notification-title-color, var(--gray-900, #111827));line-height:1.4}.ds-notification__close{background:transparent;border:none;padding:var(--space-1, .25rem);cursor:pointer;color:var(--notification-close-color, var(--gray-400, #9ca3af));line-height:1;transition:color .2s ease;flex-shrink:0}.ds-notification__close:hover{color:var(--notification-close-hover-color, var(--gray-600, #4b5563))}.ds-notification__close:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:2px;border-radius:var(--radius-1, .25rem)}.ds-notification__body{padding-left:calc(var(--notification-icon-size, 1.25rem) + var(--notification-icon-gap, var(--space-2, .5rem)))}.ds-notification__message{margin:0;font-size:var(--notification-message-size, var(--font-size-sm, .875rem));color:var(--notification-message-color, var(--gray-600, #4b5563));line-height:1.5}.ds-notification__actions{display:flex;gap:var(--space-2, .5rem);padding-left:calc(var(--notification-icon-size, 1.25rem) + var(--notification-icon-gap, var(--space-2, .5rem)))}.ds-notification__action{padding:var(--space-2, .5rem) var(--space-3, .75rem);font-size:var(--font-size-sm, .875rem);font-weight:500;border-radius:var(--radius-1, .25rem);cursor:pointer;transition:all .2s ease;border:1px solid transparent}.ds-notification__action--primary{background:var(--color-primary, #3b82f6);color:var(--color-white, #ffffff);border-color:var(--color-primary, #3b82f6)}.ds-notification__action--primary:hover{background:var(--primary-600, #2563eb);border-color:var(--primary-600, #2563eb)}.ds-notification__action--secondary{background:var(--gray-100, #f3f4f6);color:var(--gray-700, #374151);border-color:var(--gray-300, #d1d5db)}.ds-notification__action--secondary:hover{background:var(--gray-200, #e5e7eb)}.ds-notification__action--ghost{background:transparent;color:var(--color-primary, #3b82f6)}.ds-notification__action--ghost:hover{background:var(--primary-50, #eff6ff)}.ds-notification__action:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:2px}.ds-notification--info .ds-notification__icon{color:var(--notification-info-icon, var(--color-primary, #3b82f6))}.ds-notification--success .ds-notification__icon{color:var(--notification-success-icon, var(--success, #10b981))}.ds-notification--warning .ds-notification__icon{color:var(--notification-warning-icon, var(--warning, #f59e0b))}.ds-notification--error .ds-notification__icon{color:var(--notification-error-icon, var(--error, #ef4444))}.ds-notification-container--topLeft .ds-notification-wrapper,.ds-notification-container--bottomLeft .ds-notification-wrapper{animation:slideInLeft .3s ease-out}\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"] }] });
11059
+ }
11060
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsNotificationItemComponent, decorators: [{
11061
+ type: Component,
11062
+ args: [{ selector: 'ds-notification-item', standalone: true, imports: [CommonModule, FontAwesomeModule], template: "<div\n [class]=\"notificationClasses().join(' ')\"\n role=\"alert\"\n [attr.aria-live]=\"notification().type === 'error' ? 'assertive' : 'polite'\"\n [attr.aria-atomic]=\"true\"\n>\n <div class=\"ds-notification__header\">\n <div class=\"ds-notification__icon-title\">\n @if (notification().icon) {\n <fa-icon\n [icon]=\"notification().icon\"\n class=\"ds-notification__icon\"\n [attr.aria-hidden]=\"true\"\n />\n }\n <h4 class=\"ds-notification__title\">{{ notification().title }}</h4>\n </div>\n @if (notification().closable) {\n <button\n type=\"button\"\n class=\"ds-notification__close\"\n (click)=\"onClose()\"\n [attr.aria-label]=\"'Fermer la notification'\"\n >\n <fa-icon [icon]=\"closeIcon\" />\n </button>\n }\n </div>\n\n <div class=\"ds-notification__body\">\n <p class=\"ds-notification__message\">{{ notification().message }}</p>\n </div>\n\n @if (notification().actions && notification().actions!.length > 0) {\n <div class=\"ds-notification__actions\">\n @for (action of notification().actions!; track $index) {\n <button\n type=\"button\"\n class=\"ds-notification__action ds-notification__action--{{ action.variant || 'secondary' }}\"\n (click)=\"onActionClick($index)\"\n >\n {{ action.label }}\n </button>\n }\n </div>\n }\n</div>\n", styles: [".ds-notification-container{position:fixed;z-index:var(--notification-z-index, 1050);pointer-events:none;display:flex;flex-direction:column;gap:var(--notification-gap, var(--space-3, .75rem));max-width:var(--notification-max-width, 384px);width:100%}.ds-notification-container--topRight{top:var(--notification-offset-y, var(--space-6, 1.5rem));right:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-container--topLeft{top:var(--notification-offset-y, var(--space-6, 1.5rem));left:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-container--bottomRight{bottom:var(--notification-offset-y, var(--space-6, 1.5rem));right:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-container--bottomLeft{bottom:var(--notification-offset-y, var(--space-6, 1.5rem));left:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-wrapper{pointer-events:auto;animation:var(--notification-animation-in, slideInRight .3s ease-out)}@keyframes slideInRight{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes slideInLeft{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}.ds-notification{background:var(--notification-bg, var(--color-white, #ffffff));border:var(--notification-border-width, 1px) solid var(--notification-border-color, var(--gray-300, #d1d5db));border-radius:var(--notification-radius, var(--radius-2, .5rem));box-shadow:var(--notification-shadow, var(--shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, .1)));padding:var(--notification-padding, var(--space-4, 1rem));display:flex;flex-direction:column;gap:var(--notification-gap-inner, var(--space-3, .75rem));width:100%}.ds-notification__header{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--space-2, .5rem)}.ds-notification__icon-title{display:flex;align-items:center;gap:var(--notification-icon-gap, var(--space-2, .5rem));flex:1}.ds-notification__icon{font-size:var(--notification-icon-size, 1.25rem);flex-shrink:0;color:var(--notification-icon-color, var(--gray-500, #6b7280))}.ds-notification__title{margin:0;font-size:var(--notification-title-size, var(--font-size-base, 1rem));font-weight:var(--notification-title-weight, 600);color:var(--notification-title-color, var(--gray-900, #111827));line-height:1.4}.ds-notification__close{background:transparent;border:none;padding:var(--space-1, .25rem);cursor:pointer;color:var(--notification-close-color, var(--gray-400, #9ca3af));line-height:1;transition:color .2s ease;flex-shrink:0}.ds-notification__close:hover{color:var(--notification-close-hover-color, var(--gray-600, #4b5563))}.ds-notification__close:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:2px;border-radius:var(--radius-1, .25rem)}.ds-notification__body{padding-left:calc(var(--notification-icon-size, 1.25rem) + var(--notification-icon-gap, var(--space-2, .5rem)))}.ds-notification__message{margin:0;font-size:var(--notification-message-size, var(--font-size-sm, .875rem));color:var(--notification-message-color, var(--gray-600, #4b5563));line-height:1.5}.ds-notification__actions{display:flex;gap:var(--space-2, .5rem);padding-left:calc(var(--notification-icon-size, 1.25rem) + var(--notification-icon-gap, var(--space-2, .5rem)))}.ds-notification__action{padding:var(--space-2, .5rem) var(--space-3, .75rem);font-size:var(--font-size-sm, .875rem);font-weight:500;border-radius:var(--radius-1, .25rem);cursor:pointer;transition:all .2s ease;border:1px solid transparent}.ds-notification__action--primary{background:var(--color-primary, #3b82f6);color:var(--color-white, #ffffff);border-color:var(--color-primary, #3b82f6)}.ds-notification__action--primary:hover{background:var(--primary-600, #2563eb);border-color:var(--primary-600, #2563eb)}.ds-notification__action--secondary{background:var(--gray-100, #f3f4f6);color:var(--gray-700, #374151);border-color:var(--gray-300, #d1d5db)}.ds-notification__action--secondary:hover{background:var(--gray-200, #e5e7eb)}.ds-notification__action--ghost{background:transparent;color:var(--color-primary, #3b82f6)}.ds-notification__action--ghost:hover{background:var(--primary-50, #eff6ff)}.ds-notification__action:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:2px}.ds-notification--info .ds-notification__icon{color:var(--notification-info-icon, var(--color-primary, #3b82f6))}.ds-notification--success .ds-notification__icon{color:var(--notification-success-icon, var(--success, #10b981))}.ds-notification--warning .ds-notification__icon{color:var(--notification-warning-icon, var(--warning, #f59e0b))}.ds-notification--error .ds-notification__icon{color:var(--notification-error-icon, var(--error, #ef4444))}.ds-notification-container--topLeft .ds-notification-wrapper,.ds-notification-container--bottomLeft .ds-notification-wrapper{animation:slideInLeft .3s ease-out}\n"] }]
11063
+ }], propDecorators: { notification: [{ type: i0.Input, args: [{ isSignal: true, alias: "notification", required: true }] }], closeClick: [{ type: i0.Output, args: ["closeClick"] }], actionClick: [{ type: i0.Output, args: ["actionClick"] }] } });
11064
+
11065
+ class DsNotificationContainerComponent {
11066
+ notificationService = inject(DsNotificationService);
11067
+ placement = input('topRight', ...(ngDevMode ? [{ debugName: "placement" }] : []));
11068
+ maxStack = input(5, ...(ngDevMode ? [{ debugName: "maxStack" }] : []));
11069
+ notifications = this.notificationService.notifications$;
11070
+ visibleNotifications = computed(() => {
11071
+ const all = this.notifications();
11072
+ const max = this.maxStack();
11073
+ return all.slice(-max);
11074
+ }, ...(ngDevMode ? [{ debugName: "visibleNotifications" }] : []));
11075
+ containerClasses = computed(() => {
11076
+ const classes = ['ds-notification-container'];
11077
+ classes.push(`ds-notification-container--${this.placement()}`);
11078
+ return classes.join(' ');
11079
+ }, ...(ngDevMode ? [{ debugName: "containerClasses" }] : []));
11080
+ onClose(id) {
11081
+ this.notificationService.close(id);
11082
+ }
11083
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsNotificationContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11084
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsNotificationContainerComponent, isStandalone: true, selector: "ds-notification-container", inputs: { placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, maxStack: { classPropertyName: "maxStack", publicName: "maxStack", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
11085
+ <div [class]="containerClasses()">
11086
+ @for (notification of visibleNotifications(); track notification.id) {
11087
+ <ds-notification-item
11088
+ [notification]="notification"
11089
+ (closeClick)="onClose($event)"
11090
+ class="ds-notification-wrapper"
11091
+ />
11092
+ }
11093
+ </div>
11094
+ `, isInline: true, styles: [".ds-notification-container{position:fixed;z-index:var(--notification-z-index, 1050);pointer-events:none;display:flex;flex-direction:column;gap:var(--notification-gap, var(--space-3, .75rem));max-width:var(--notification-max-width, 384px);width:100%}.ds-notification-container--topRight{top:var(--notification-offset-y, var(--space-6, 1.5rem));right:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-container--topLeft{top:var(--notification-offset-y, var(--space-6, 1.5rem));left:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-container--bottomRight{bottom:var(--notification-offset-y, var(--space-6, 1.5rem));right:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-container--bottomLeft{bottom:var(--notification-offset-y, var(--space-6, 1.5rem));left:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-wrapper{pointer-events:auto;animation:var(--notification-animation-in, slideInRight .3s ease-out)}@keyframes slideInRight{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes slideInLeft{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}.ds-notification{background:var(--notification-bg, var(--color-white, #ffffff));border:var(--notification-border-width, 1px) solid var(--notification-border-color, var(--gray-300, #d1d5db));border-radius:var(--notification-radius, var(--radius-2, .5rem));box-shadow:var(--notification-shadow, var(--shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, .1)));padding:var(--notification-padding, var(--space-4, 1rem));display:flex;flex-direction:column;gap:var(--notification-gap-inner, var(--space-3, .75rem));width:100%}.ds-notification__header{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--space-2, .5rem)}.ds-notification__icon-title{display:flex;align-items:center;gap:var(--notification-icon-gap, var(--space-2, .5rem));flex:1}.ds-notification__icon{font-size:var(--notification-icon-size, 1.25rem);flex-shrink:0;color:var(--notification-icon-color, var(--gray-500, #6b7280))}.ds-notification__title{margin:0;font-size:var(--notification-title-size, var(--font-size-base, 1rem));font-weight:var(--notification-title-weight, 600);color:var(--notification-title-color, var(--gray-900, #111827));line-height:1.4}.ds-notification__close{background:transparent;border:none;padding:var(--space-1, .25rem);cursor:pointer;color:var(--notification-close-color, var(--gray-400, #9ca3af));line-height:1;transition:color .2s ease;flex-shrink:0}.ds-notification__close:hover{color:var(--notification-close-hover-color, var(--gray-600, #4b5563))}.ds-notification__close:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:2px;border-radius:var(--radius-1, .25rem)}.ds-notification__body{padding-left:calc(var(--notification-icon-size, 1.25rem) + var(--notification-icon-gap, var(--space-2, .5rem)))}.ds-notification__message{margin:0;font-size:var(--notification-message-size, var(--font-size-sm, .875rem));color:var(--notification-message-color, var(--gray-600, #4b5563));line-height:1.5}.ds-notification__actions{display:flex;gap:var(--space-2, .5rem);padding-left:calc(var(--notification-icon-size, 1.25rem) + var(--notification-icon-gap, var(--space-2, .5rem)))}.ds-notification__action{padding:var(--space-2, .5rem) var(--space-3, .75rem);font-size:var(--font-size-sm, .875rem);font-weight:500;border-radius:var(--radius-1, .25rem);cursor:pointer;transition:all .2s ease;border:1px solid transparent}.ds-notification__action--primary{background:var(--color-primary, #3b82f6);color:var(--color-white, #ffffff);border-color:var(--color-primary, #3b82f6)}.ds-notification__action--primary:hover{background:var(--primary-600, #2563eb);border-color:var(--primary-600, #2563eb)}.ds-notification__action--secondary{background:var(--gray-100, #f3f4f6);color:var(--gray-700, #374151);border-color:var(--gray-300, #d1d5db)}.ds-notification__action--secondary:hover{background:var(--gray-200, #e5e7eb)}.ds-notification__action--ghost{background:transparent;color:var(--color-primary, #3b82f6)}.ds-notification__action--ghost:hover{background:var(--primary-50, #eff6ff)}.ds-notification__action:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:2px}.ds-notification--info .ds-notification__icon{color:var(--notification-info-icon, var(--color-primary, #3b82f6))}.ds-notification--success .ds-notification__icon{color:var(--notification-success-icon, var(--success, #10b981))}.ds-notification--warning .ds-notification__icon{color:var(--notification-warning-icon, var(--warning, #f59e0b))}.ds-notification--error .ds-notification__icon{color:var(--notification-error-icon, var(--error, #ef4444))}.ds-notification-container--topLeft .ds-notification-wrapper,.ds-notification-container--bottomLeft .ds-notification-wrapper{animation:slideInLeft .3s ease-out}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsNotificationItemComponent, selector: "ds-notification-item", inputs: ["notification"], outputs: ["closeClick", "actionClick"] }] });
11095
+ }
11096
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsNotificationContainerComponent, decorators: [{
11097
+ type: Component,
11098
+ args: [{ selector: 'ds-notification-container', standalone: true, imports: [CommonModule, DsNotificationItemComponent], template: `
11099
+ <div [class]="containerClasses()">
11100
+ @for (notification of visibleNotifications(); track notification.id) {
11101
+ <ds-notification-item
11102
+ [notification]="notification"
11103
+ (closeClick)="onClose($event)"
11104
+ class="ds-notification-wrapper"
11105
+ />
11106
+ }
11107
+ </div>
11108
+ `, styles: [".ds-notification-container{position:fixed;z-index:var(--notification-z-index, 1050);pointer-events:none;display:flex;flex-direction:column;gap:var(--notification-gap, var(--space-3, .75rem));max-width:var(--notification-max-width, 384px);width:100%}.ds-notification-container--topRight{top:var(--notification-offset-y, var(--space-6, 1.5rem));right:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-container--topLeft{top:var(--notification-offset-y, var(--space-6, 1.5rem));left:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-container--bottomRight{bottom:var(--notification-offset-y, var(--space-6, 1.5rem));right:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-container--bottomLeft{bottom:var(--notification-offset-y, var(--space-6, 1.5rem));left:var(--notification-offset-x, var(--space-6, 1.5rem))}.ds-notification-wrapper{pointer-events:auto;animation:var(--notification-animation-in, slideInRight .3s ease-out)}@keyframes slideInRight{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}@keyframes slideInLeft{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}.ds-notification{background:var(--notification-bg, var(--color-white, #ffffff));border:var(--notification-border-width, 1px) solid var(--notification-border-color, var(--gray-300, #d1d5db));border-radius:var(--notification-radius, var(--radius-2, .5rem));box-shadow:var(--notification-shadow, var(--shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, .1)));padding:var(--notification-padding, var(--space-4, 1rem));display:flex;flex-direction:column;gap:var(--notification-gap-inner, var(--space-3, .75rem));width:100%}.ds-notification__header{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--space-2, .5rem)}.ds-notification__icon-title{display:flex;align-items:center;gap:var(--notification-icon-gap, var(--space-2, .5rem));flex:1}.ds-notification__icon{font-size:var(--notification-icon-size, 1.25rem);flex-shrink:0;color:var(--notification-icon-color, var(--gray-500, #6b7280))}.ds-notification__title{margin:0;font-size:var(--notification-title-size, var(--font-size-base, 1rem));font-weight:var(--notification-title-weight, 600);color:var(--notification-title-color, var(--gray-900, #111827));line-height:1.4}.ds-notification__close{background:transparent;border:none;padding:var(--space-1, .25rem);cursor:pointer;color:var(--notification-close-color, var(--gray-400, #9ca3af));line-height:1;transition:color .2s ease;flex-shrink:0}.ds-notification__close:hover{color:var(--notification-close-hover-color, var(--gray-600, #4b5563))}.ds-notification__close:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:2px;border-radius:var(--radius-1, .25rem)}.ds-notification__body{padding-left:calc(var(--notification-icon-size, 1.25rem) + var(--notification-icon-gap, var(--space-2, .5rem)))}.ds-notification__message{margin:0;font-size:var(--notification-message-size, var(--font-size-sm, .875rem));color:var(--notification-message-color, var(--gray-600, #4b5563));line-height:1.5}.ds-notification__actions{display:flex;gap:var(--space-2, .5rem);padding-left:calc(var(--notification-icon-size, 1.25rem) + var(--notification-icon-gap, var(--space-2, .5rem)))}.ds-notification__action{padding:var(--space-2, .5rem) var(--space-3, .75rem);font-size:var(--font-size-sm, .875rem);font-weight:500;border-radius:var(--radius-1, .25rem);cursor:pointer;transition:all .2s ease;border:1px solid transparent}.ds-notification__action--primary{background:var(--color-primary, #3b82f6);color:var(--color-white, #ffffff);border-color:var(--color-primary, #3b82f6)}.ds-notification__action--primary:hover{background:var(--primary-600, #2563eb);border-color:var(--primary-600, #2563eb)}.ds-notification__action--secondary{background:var(--gray-100, #f3f4f6);color:var(--gray-700, #374151);border-color:var(--gray-300, #d1d5db)}.ds-notification__action--secondary:hover{background:var(--gray-200, #e5e7eb)}.ds-notification__action--ghost{background:transparent;color:var(--color-primary, #3b82f6)}.ds-notification__action--ghost:hover{background:var(--primary-50, #eff6ff)}.ds-notification__action:focus-visible{outline:2px solid var(--color-primary, #3b82f6);outline-offset:2px}.ds-notification--info .ds-notification__icon{color:var(--notification-info-icon, var(--color-primary, #3b82f6))}.ds-notification--success .ds-notification__icon{color:var(--notification-success-icon, var(--success, #10b981))}.ds-notification--warning .ds-notification__icon{color:var(--notification-warning-icon, var(--warning, #f59e0b))}.ds-notification--error .ds-notification__icon{color:var(--notification-error-icon, var(--error, #ef4444))}.ds-notification-container--topLeft .ds-notification-wrapper,.ds-notification-container--bottomLeft .ds-notification-wrapper{animation:slideInLeft .3s ease-out}\n"] }]
11109
+ }], propDecorators: { placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], maxStack: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxStack", required: false }] }] } });
11110
+
11111
+ /**
11112
+ * DsCalendar - Composant calendrier mensuel/annuel avec gestion d'événements
11113
+ *
11114
+ * @example
11115
+ * ```html
11116
+ * <ds-calendar
11117
+ * [value]="currentDate"
11118
+ * [events]="calendarEvents"
11119
+ * (dateSelect)="onDateSelect($event)"
11120
+ * (eventClick)="onEventClick($event)">
11121
+ * </ds-calendar>
11122
+ * ```
11123
+ */
11124
+ class DsCalendar {
11125
+ // =========================================================================
11126
+ // INPUTS (Signals Angular 20)
11127
+ // =========================================================================
11128
+ /**
11129
+ * Date courante affichée
11130
+ */
11131
+ value = input(new Date(), ...(ngDevMode ? [{ debugName: "value" }] : []));
11132
+ /**
11133
+ * Mode d'affichage (month ou year)
11134
+ */
11135
+ mode = input('month', ...(ngDevMode ? [{ debugName: "mode" }] : []));
11136
+ /**
11137
+ * Événements à afficher dans le calendrier
11138
+ */
11139
+ events = input([], ...(ngDevMode ? [{ debugName: "events" }] : []));
11140
+ /**
11141
+ * Taille du calendrier
11142
+ */
11143
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
11144
+ /**
11145
+ * Locale pour les noms de jours et mois
11146
+ */
11147
+ locale = input('fr-FR', ...(ngDevMode ? [{ debugName: "locale" }] : []));
11148
+ /**
11149
+ * Premier jour de la semaine (0=dimanche, 1=lundi)
11150
+ */
11151
+ firstDayOfWeek = input(1, ...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : []));
11152
+ /**
11153
+ * Fonction pour désactiver certaines dates
11154
+ */
11155
+ disabledDate = input(undefined, ...(ngDevMode ? [{ debugName: "disabledDate" }] : []));
11156
+ // =========================================================================
11157
+ // OUTPUTS
11158
+ // =========================================================================
11159
+ /**
11160
+ * Émis lors de la sélection d'une date
11161
+ */
11162
+ dateSelect = output();
11163
+ /**
11164
+ * Émis lors du changement de mois
11165
+ */
11166
+ monthChange = output();
11167
+ /**
11168
+ * Émis lors du changement de mode
11169
+ */
11170
+ modeChange = output();
11171
+ /**
11172
+ * Émis lors du clic sur un événement
11173
+ */
11174
+ eventClick = output();
11175
+ // =========================================================================
11176
+ // ÉTAT INTERNE
11177
+ // =========================================================================
11178
+ /**
11179
+ * Date actuellement affichée (mois/année)
11180
+ */
11181
+ displayDate = signal(new Date(), ...(ngDevMode ? [{ debugName: "displayDate" }] : []));
11182
+ /**
11183
+ * Mode actuel (interne)
11184
+ */
11185
+ currentMode = signal('month', ...(ngDevMode ? [{ debugName: "currentMode" }] : []));
11186
+ // =========================================================================
11187
+ // ICÔNES FONTAWESOME
11188
+ // =========================================================================
11189
+ icons = {
11190
+ chevronLeft: faChevronLeft,
11191
+ chevronRight: faChevronRight,
11192
+ };
11193
+ // =========================================================================
11194
+ // COMPUTED SIGNALS
11195
+ // =========================================================================
11196
+ /**
11197
+ * Classes CSS de l'hôte
11198
+ */
11199
+ hostClasses = computed(() => {
11200
+ const classes = ['ds-calendar'];
11201
+ classes.push(`ds-calendar--${this.size()}`);
11202
+ classes.push(`ds-calendar--${this.currentMode()}`);
11203
+ return classes.join(' ');
11204
+ }, ...(ngDevMode ? [{ debugName: "hostClasses" }] : []));
11205
+ /**
11206
+ * Titre du calendrier (mois + année)
11207
+ */
11208
+ calendarTitle = computed(() => {
11209
+ const date = this.displayDate();
11210
+ const locale = this.locale();
11211
+ if (this.currentMode() === 'year') {
11212
+ return date.getFullYear().toString();
11213
+ }
11214
+ const monthName = date.toLocaleDateString(locale, { month: 'long' });
11215
+ const year = date.getFullYear();
11216
+ return `${monthName.charAt(0).toUpperCase() + monthName.slice(1)} ${year}`;
11217
+ }, ...(ngDevMode ? [{ debugName: "calendarTitle" }] : []));
11218
+ /**
11219
+ * Noms des jours de la semaine
11220
+ */
11221
+ weekDays = computed(() => {
11222
+ const locale = this.locale();
11223
+ const firstDay = this.firstDayOfWeek();
11224
+ const days = [];
11225
+ // Générer les noms des jours à partir du premier jour de la semaine
11226
+ for (let i = 0; i < 7; i++) {
11227
+ const date = new Date(2024, 0, firstDay + i); // Année 2024 (lundi = 1er janvier)
11228
+ const dayName = date.toLocaleDateString(locale, { weekday: 'short' });
11229
+ days.push(dayName.charAt(0).toUpperCase() + dayName.slice(1));
11230
+ }
11231
+ return days;
11232
+ }, ...(ngDevMode ? [{ debugName: "weekDays" }] : []));
11233
+ /**
11234
+ * Grille des jours du mois (6 semaines)
11235
+ */
11236
+ calendarGrid = computed(() => {
11237
+ const date = this.displayDate();
11238
+ const firstDay = this.firstDayOfWeek();
11239
+ const events = this.events();
11240
+ const disabledFn = this.disabledDate();
11241
+ const today = new Date();
11242
+ today.setHours(0, 0, 0, 0);
11243
+ const year = date.getFullYear();
11244
+ const month = date.getMonth();
11245
+ // Premier jour du mois
11246
+ const firstDayOfMonth = new Date(year, month, 1);
11247
+ let startDay = firstDayOfMonth.getDay() - firstDay;
11248
+ if (startDay < 0)
11249
+ startDay += 7;
11250
+ // Calculer la date de début de la grille (peut être dans le mois précédent)
11251
+ const startDate = new Date(year, month, 1 - startDay);
11252
+ const days = [];
11253
+ // Générer 42 jours (6 semaines x 7 jours)
11254
+ for (let i = 0; i < 42; i++) {
11255
+ const currentDate = new Date(startDate);
11256
+ currentDate.setDate(startDate.getDate() + i);
11257
+ currentDate.setHours(0, 0, 0, 0);
11258
+ const isCurrentMonth = currentDate.getMonth() === month;
11259
+ const isToday = currentDate.getTime() === today.getTime();
11260
+ const isDisabled = disabledFn ? disabledFn(currentDate) : false;
11261
+ // Trouver les événements pour ce jour
11262
+ const dayEvents = events.filter((event) => {
11263
+ const eventDate = new Date(event.date);
11264
+ eventDate.setHours(0, 0, 0, 0);
11265
+ return eventDate.getTime() === currentDate.getTime();
11266
+ });
11267
+ days.push({
11268
+ date: currentDate,
11269
+ isCurrentMonth,
11270
+ isToday,
11271
+ isDisabled,
11272
+ events: dayEvents,
11273
+ });
11274
+ }
11275
+ return days;
11276
+ }, ...(ngDevMode ? [{ debugName: "calendarGrid" }] : []));
11277
+ /**
11278
+ * Grille des mois (vue année)
11279
+ */
11280
+ monthGrid = computed(() => {
11281
+ const date = this.displayDate();
11282
+ const locale = this.locale();
11283
+ const currentMonth = new Date().getMonth();
11284
+ const currentYear = new Date().getFullYear();
11285
+ const displayYear = date.getFullYear();
11286
+ const months = [];
11287
+ for (let i = 0; i < 12; i++) {
11288
+ const monthDate = new Date(displayYear, i, 1);
11289
+ const monthName = monthDate.toLocaleDateString(locale, { month: 'long' });
11290
+ months.push({
11291
+ monthIndex: i,
11292
+ label: monthName.charAt(0).toUpperCase() + monthName.slice(1),
11293
+ isCurrentMonth: i === currentMonth && displayYear === currentYear,
11294
+ });
11295
+ }
11296
+ return months;
11297
+ }, ...(ngDevMode ? [{ debugName: "monthGrid" }] : []));
11298
+ // =========================================================================
11299
+ // EFFECTS
11300
+ // =========================================================================
11301
+ constructor() {
11302
+ // Synchroniser displayDate avec value au démarrage
11303
+ effect(() => {
11304
+ const value = this.value();
11305
+ this.displayDate.set(new Date(value));
11306
+ }, { allowSignalWrites: true });
11307
+ // Synchroniser currentMode avec mode
11308
+ effect(() => {
11309
+ this.currentMode.set(this.mode());
11310
+ }, { allowSignalWrites: true });
11311
+ }
11312
+ // =========================================================================
11313
+ // MÉTHODES PUBLIQUES
11314
+ // =========================================================================
11315
+ /**
11316
+ * Navigue vers le mois précédent
11317
+ */
11318
+ previousMonth() {
11319
+ const current = this.displayDate();
11320
+ const newDate = new Date(current.getFullYear(), current.getMonth() - 1, 1);
11321
+ this.displayDate.set(newDate);
11322
+ this.monthChange.emit(newDate);
11323
+ }
11324
+ /**
11325
+ * Navigue vers le mois suivant
11326
+ */
11327
+ nextMonth() {
11328
+ const current = this.displayDate();
11329
+ const newDate = new Date(current.getFullYear(), current.getMonth() + 1, 1);
11330
+ this.displayDate.set(newDate);
11331
+ this.monthChange.emit(newDate);
11332
+ }
11333
+ /**
11334
+ * Navigue vers l'année précédente
11335
+ */
11336
+ previousYear() {
11337
+ const current = this.displayDate();
11338
+ const newDate = new Date(current.getFullYear() - 1, current.getMonth(), 1);
11339
+ this.displayDate.set(newDate);
11340
+ this.monthChange.emit(newDate);
11341
+ }
11342
+ /**
11343
+ * Navigue vers l'année suivante
11344
+ */
11345
+ nextYear() {
11346
+ const current = this.displayDate();
11347
+ const newDate = new Date(current.getFullYear() + 1, current.getMonth(), 1);
11348
+ this.displayDate.set(newDate);
11349
+ this.monthChange.emit(newDate);
11350
+ }
11351
+ /**
11352
+ * Sélectionne une date
11353
+ */
11354
+ selectDate(day) {
11355
+ if (day.isDisabled)
11356
+ return;
11357
+ this.dateSelect.emit(new Date(day.date));
11358
+ }
11359
+ /**
11360
+ * Sélectionne un mois (en mode année)
11361
+ */
11362
+ selectMonth(month) {
11363
+ const current = this.displayDate();
11364
+ const newDate = new Date(current.getFullYear(), month.monthIndex, 1);
11365
+ this.displayDate.set(newDate);
11366
+ this.currentMode.set('month');
11367
+ this.modeChange.emit('month');
11368
+ }
11369
+ /**
11370
+ * Toggle entre mode mois et année
11371
+ */
11372
+ toggleMode() {
11373
+ const newMode = this.currentMode() === 'month' ? 'year' : 'month';
11374
+ this.currentMode.set(newMode);
11375
+ this.modeChange.emit(newMode);
11376
+ }
11377
+ /**
11378
+ * Gère le clic sur un événement
11379
+ */
11380
+ onEventClick(event, mouseEvent) {
11381
+ mouseEvent.stopPropagation();
11382
+ this.eventClick.emit(event);
11383
+ }
11384
+ /**
11385
+ * Retourne les classes CSS d'un jour
11386
+ */
11387
+ getDayClasses(day) {
11388
+ const classes = ['ds-calendar__day'];
11389
+ if (!day.isCurrentMonth) {
11390
+ classes.push('ds-calendar__day--other-month');
11391
+ }
11392
+ if (day.isToday) {
11393
+ classes.push('ds-calendar__day--today');
11394
+ }
11395
+ if (day.isDisabled) {
11396
+ classes.push('ds-calendar__day--disabled');
11397
+ }
11398
+ if (day.events.length > 0) {
11399
+ classes.push('ds-calendar__day--has-events');
11400
+ }
11401
+ return classes.join(' ');
11402
+ }
11403
+ /**
11404
+ * Retourne les classes CSS d'un mois
11405
+ */
11406
+ getMonthClasses(month) {
11407
+ const classes = ['ds-calendar__month'];
11408
+ if (month.isCurrentMonth) {
11409
+ classes.push('ds-calendar__month--current');
11410
+ }
11411
+ return classes.join(' ');
11412
+ }
11413
+ /**
11414
+ * Retourne les classes CSS d'un événement
11415
+ */
11416
+ getEventClasses(event) {
11417
+ const classes = ['ds-calendar__event'];
11418
+ if (event.type) {
11419
+ classes.push(`ds-calendar__event--${event.type}`);
11420
+ }
11421
+ return classes.join(' ');
11422
+ }
11423
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsCalendar, deps: [], target: i0.ɵɵFactoryTarget.Component });
11424
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: DsCalendar, isStandalone: true, selector: "ds-calendar", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, events: { classPropertyName: "events", publicName: "events", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, disabledDate: { classPropertyName: "disabledDate", publicName: "disabledDate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dateSelect: "dateSelect", monthChange: "monthChange", modeChange: "modeChange", eventClick: "eventClick" }, host: { properties: { "class": "hostClasses()" }, classAttribute: "ds-calendar" }, ngImport: i0, template: "<div class=\"ds-calendar__container\">\n <!-- Header avec navigation et s\u00E9lecteur mode -->\n <div class=\"ds-calendar__header\">\n <div class=\"ds-calendar__nav\">\n @if (currentMode() === 'month') {\n <button\n type=\"button\"\n class=\"ds-calendar__nav-btn\"\n (click)=\"previousMonth()\"\n aria-label=\"Mois pr\u00E9c\u00E9dent\"\n >\n <fa-icon [icon]=\"icons.chevronLeft\"></fa-icon>\n </button>\n } @else {\n <button\n type=\"button\"\n class=\"ds-calendar__nav-btn\"\n (click)=\"previousYear()\"\n aria-label=\"Ann\u00E9e pr\u00E9c\u00E9dente\"\n >\n <fa-icon [icon]=\"icons.chevronLeft\"></fa-icon>\n </button>\n }\n\n <button\n type=\"button\"\n class=\"ds-calendar__title\"\n (click)=\"toggleMode()\"\n [attr.aria-label]=\"currentMode() === 'month' ? 'Voir l\\'ann\u00E9e' : 'Voir le mois'\"\n >\n {{ calendarTitle() }}\n </button>\n\n @if (currentMode() === 'month') {\n <button\n type=\"button\"\n class=\"ds-calendar__nav-btn\"\n (click)=\"nextMonth()\"\n aria-label=\"Mois suivant\"\n >\n <fa-icon [icon]=\"icons.chevronRight\"></fa-icon>\n </button>\n } @else {\n <button\n type=\"button\"\n class=\"ds-calendar__nav-btn\"\n (click)=\"nextYear()\"\n aria-label=\"Ann\u00E9e suivante\"\n >\n <fa-icon [icon]=\"icons.chevronRight\"></fa-icon>\n </button>\n }\n </div>\n\n <div class=\"ds-calendar__mode-selector\">\n <button\n type=\"button\"\n class=\"ds-calendar__mode-btn\"\n [class.ds-calendar__mode-btn--active]=\"currentMode() === 'month'\"\n (click)=\"currentMode() === 'year' && toggleMode()\"\n >\n Mois\n </button>\n <button\n type=\"button\"\n class=\"ds-calendar__mode-btn\"\n [class.ds-calendar__mode-btn--active]=\"currentMode() === 'year'\"\n (click)=\"currentMode() === 'month' && toggleMode()\"\n >\n Ann\u00E9e\n </button>\n </div>\n </div>\n\n <!-- Vue mois -->\n @if (currentMode() === 'month') {\n <div class=\"ds-calendar__month-view\">\n <!-- Noms des jours de la semaine -->\n <div class=\"ds-calendar__weekdays\">\n @for (day of weekDays(); track day) {\n <div class=\"ds-calendar__weekday\">{{ day }}</div>\n }\n </div>\n\n <!-- Grille des jours -->\n <div class=\"ds-calendar__grid\">\n @for (day of calendarGrid(); track day.date.getTime()) {\n <button\n type=\"button\"\n [class]=\"getDayClasses(day)\"\n [disabled]=\"day.isDisabled\"\n (click)=\"selectDate(day)\"\n [attr.aria-label]=\"day.date.toLocaleDateString(locale())\"\n [attr.aria-current]=\"day.isToday ? 'date' : null\"\n >\n <span class=\"ds-calendar__day-number\">{{ day.date.getDate() }}</span>\n\n @if (day.events.length > 0) {\n <div class=\"ds-calendar__events\">\n @for (event of day.events; track event.id) {\n <div\n [class]=\"getEventClasses(event)\"\n [title]=\"event.title\"\n (click)=\"onEventClick(event, $event)\"\n role=\"button\"\n [attr.aria-label]=\"event.title\"\n >\n <span class=\"ds-calendar__event-dot\"></span>\n @if (size() === 'lg') {\n <span class=\"ds-calendar__event-title\">{{ event.title }}</span>\n }\n </div>\n }\n </div>\n }\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Vue ann\u00E9e -->\n @if (currentMode() === 'year') {\n <div class=\"ds-calendar__year-view\">\n <div class=\"ds-calendar__months-grid\">\n @for (month of monthGrid(); track month.monthIndex) {\n <button\n type=\"button\"\n [class]=\"getMonthClasses(month)\"\n (click)=\"selectMonth(month)\"\n [attr.aria-label]=\"month.label\"\n [attr.aria-current]=\"month.isCurrentMonth ? 'true' : null\"\n >\n {{ month.label }}\n </button>\n }\n </div>\n </div>\n }\n</div>\n", styles: [".ds-calendar{display:block;width:100%}.ds-calendar__container{background:var(--calendar-bg, var(--white));border:1px solid var(--calendar-border, var(--gray-200));border-radius:var(--calendar-radius, var(--radius-2));overflow:hidden}.ds-calendar__header{display:flex;align-items:center;justify-content:space-between;padding:var(--calendar-header-padding, var(--space-4));border-bottom:1px solid var(--calendar-border, var(--gray-200));background:var(--calendar-header-bg, var(--gray-50))}.ds-calendar__nav{display:flex;align-items:center;gap:var(--calendar-nav-gap, var(--space-2))}.ds-calendar__nav-btn{display:inline-flex;align-items:center;justify-content:center;width:var(--calendar-nav-btn-size, 32px);height:var(--calendar-nav-btn-size, 32px);padding:0;background:transparent;border:1px solid var(--calendar-nav-btn-border, var(--gray-300));border-radius:var(--calendar-nav-btn-radius, var(--radius-1));color:var(--calendar-nav-btn-color, var(--gray-700));font-size:var(--calendar-nav-btn-font-size, var(--font-size-3));cursor:pointer;transition:all .2s ease}.ds-calendar__nav-btn:hover:not(:disabled){background:var(--calendar-nav-btn-hover-bg, var(--gray-100));border-color:var(--calendar-nav-btn-hover-border, var(--gray-400));color:var(--calendar-nav-btn-hover-color, var(--gray-900))}.ds-calendar__nav-btn:active:not(:disabled){transform:scale(.95)}.ds-calendar__nav-btn:disabled{opacity:.5;cursor:not-allowed}.ds-calendar__title{padding:var(--calendar-title-padding, var(--space-2) var(--space-3));background:transparent;border:1px solid transparent;border-radius:var(--calendar-title-radius, var(--radius-1));color:var(--calendar-title-color, var(--gray-900));font-size:var(--calendar-title-font-size, var(--font-size-4));font-weight:var(--calendar-title-font-weight, 600);cursor:pointer;transition:all .2s ease}.ds-calendar__title:hover{background:var(--calendar-title-hover-bg, var(--gray-100));border-color:var(--calendar-title-hover-border, var(--gray-300))}.ds-calendar__mode-selector{display:flex;gap:var(--calendar-mode-gap, var(--space-1));background:var(--calendar-mode-bg, var(--white));border:1px solid var(--calendar-mode-border, var(--gray-300));border-radius:var(--calendar-mode-radius, var(--radius-1));padding:var(--calendar-mode-padding, 2px)}.ds-calendar__mode-btn{padding:var(--calendar-mode-btn-padding, var(--space-1) var(--space-3));background:transparent;border:none;border-radius:var(--calendar-mode-btn-radius, var(--radius-1));color:var(--calendar-mode-btn-color, var(--gray-700));font-size:var(--calendar-mode-btn-font-size, var(--font-size-2));cursor:pointer;transition:all .2s ease}.ds-calendar__mode-btn:hover{color:var(--calendar-mode-btn-hover-color, var(--gray-900))}.ds-calendar__mode-btn--active{background:var(--calendar-mode-btn-active-bg, var(--color-primary));color:var(--calendar-mode-btn-active-color, var(--white))}.ds-calendar__month-view{padding:var(--calendar-month-view-padding, var(--space-4))}.ds-calendar__weekdays{display:grid;grid-template-columns:repeat(7,1fr);gap:var(--calendar-weekday-gap, var(--space-1));margin-bottom:var(--calendar-weekday-margin, var(--space-2))}.ds-calendar__weekday{text-align:center;color:var(--calendar-weekday-color, var(--gray-600));font-size:var(--calendar-weekday-font-size, var(--font-size-2));font-weight:var(--calendar-weekday-font-weight, 600);text-transform:uppercase;padding:var(--calendar-weekday-padding, var(--space-2) 0)}.ds-calendar__grid{display:grid;grid-template-columns:repeat(7,1fr);gap:var(--calendar-grid-gap, var(--space-1))}.ds-calendar__day{position:relative;aspect-ratio:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;padding:var(--calendar-day-padding, var(--space-2));background:var(--calendar-day-bg, transparent);border:1px solid var(--calendar-day-border, transparent);border-radius:var(--calendar-day-radius, var(--radius-1));color:var(--calendar-day-color, var(--gray-900));font-size:var(--calendar-day-font-size, var(--font-size-3));cursor:pointer;transition:all .2s ease}.ds-calendar__day:hover:not(:disabled){background:var(--calendar-day-hover-bg, var(--gray-100));border-color:var(--calendar-day-hover-border, var(--gray-300))}.ds-calendar__day--other-month{color:var(--calendar-day-other-month-color, var(--gray-400))}.ds-calendar__day--today{background:var(--calendar-day-today-bg, var(--blue-50));border-color:var(--calendar-day-today-border, var(--color-primary));color:var(--calendar-day-today-color, var(--color-primary));font-weight:600}.ds-calendar__day--disabled{opacity:.4;cursor:not-allowed;background:var(--calendar-day-disabled-bg, var(--gray-50))}.ds-calendar__day--has-events .ds-calendar__day-number{font-weight:600}.ds-calendar__day-number{line-height:1;margin-bottom:var(--calendar-day-number-margin, var(--space-1))}.ds-calendar__events{display:flex;flex-direction:column;gap:var(--calendar-events-gap, 2px);width:100%;margin-top:auto}.ds-calendar__event{display:flex;align-items:center;gap:var(--calendar-event-gap, var(--space-1));padding:var(--calendar-event-padding, 2px);border-radius:var(--calendar-event-radius, var(--radius-1));cursor:pointer;transition:all .2s ease}.ds-calendar__event:hover{transform:scale(1.05)}.ds-calendar__event-dot{width:var(--calendar-event-dot-size, 6px);height:var(--calendar-event-dot-size, 6px);border-radius:50%;background:var(--calendar-event-dot-color, var(--gray-500));flex-shrink:0}.ds-calendar__event-title{font-size:var(--calendar-event-title-font-size, var(--font-size-1));color:var(--calendar-event-title-color, var(--gray-700));white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ds-calendar__event--success .ds-calendar__event-dot{background:var(--calendar-event-success-color, var(--success))}.ds-calendar__event--warning .ds-calendar__event-dot{background:var(--calendar-event-warning-color, var(--warning))}.ds-calendar__event--error .ds-calendar__event-dot{background:var(--calendar-event-error-color, var(--error))}.ds-calendar__year-view{padding:var(--calendar-year-view-padding, var(--space-4))}.ds-calendar__months-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--calendar-months-gap, var(--space-3))}.ds-calendar__month{padding:var(--calendar-month-padding, var(--space-4));background:var(--calendar-month-bg, transparent);border:1px solid var(--calendar-month-border, var(--gray-200));border-radius:var(--calendar-month-radius, var(--radius-2));color:var(--calendar-month-color, var(--gray-900));font-size:var(--calendar-month-font-size, var(--font-size-3));font-weight:500;cursor:pointer;transition:all .2s ease;text-align:center}.ds-calendar__month:hover{background:var(--calendar-month-hover-bg, var(--gray-50));border-color:var(--calendar-month-hover-border, var(--gray-300))}.ds-calendar__month--current{background:var(--calendar-month-current-bg, var(--blue-50));border-color:var(--calendar-month-current-border, var(--color-primary));color:var(--calendar-month-current-color, var(--color-primary));font-weight:600}.ds-calendar--sm .ds-calendar__header{padding:var(--calendar-header-padding-sm, var(--space-2))}.ds-calendar--sm .ds-calendar__title{font-size:var(--calendar-title-font-size-sm, var(--font-size-3))}.ds-calendar--sm .ds-calendar__weekday{font-size:var(--calendar-weekday-font-size-sm, var(--font-size-1))}.ds-calendar--sm .ds-calendar__day{padding:var(--calendar-day-padding-sm, var(--space-1));font-size:var(--calendar-day-font-size-sm, var(--font-size-2))}.ds-calendar--sm .ds-calendar__month{padding:var(--calendar-month-padding-sm, var(--space-2));font-size:var(--calendar-month-font-size-sm, var(--font-size-2))}.ds-calendar--lg .ds-calendar__header{padding:var(--calendar-header-padding-lg, var(--space-6))}.ds-calendar--lg .ds-calendar__title{font-size:var(--calendar-title-font-size-lg, var(--font-size-5))}.ds-calendar--lg .ds-calendar__weekday{font-size:var(--calendar-weekday-font-size-lg, var(--font-size-3))}.ds-calendar--lg .ds-calendar__day{padding:var(--calendar-day-padding-lg, var(--space-3));font-size:var(--calendar-day-font-size-lg, var(--font-size-4))}.ds-calendar--lg .ds-calendar__month{padding:var(--calendar-month-padding-lg, var(--space-6));font-size:var(--calendar-month-font-size-lg, var(--font-size-4))}\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, encapsulation: i0.ViewEncapsulation.None });
11425
+ }
11426
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DsCalendar, decorators: [{
11427
+ type: Component,
11428
+ args: [{ selector: 'ds-calendar', standalone: true, imports: [CommonModule, FontAwesomeModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
11429
+ class: 'ds-calendar',
11430
+ '[class]': 'hostClasses()',
11431
+ }, template: "<div class=\"ds-calendar__container\">\n <!-- Header avec navigation et s\u00E9lecteur mode -->\n <div class=\"ds-calendar__header\">\n <div class=\"ds-calendar__nav\">\n @if (currentMode() === 'month') {\n <button\n type=\"button\"\n class=\"ds-calendar__nav-btn\"\n (click)=\"previousMonth()\"\n aria-label=\"Mois pr\u00E9c\u00E9dent\"\n >\n <fa-icon [icon]=\"icons.chevronLeft\"></fa-icon>\n </button>\n } @else {\n <button\n type=\"button\"\n class=\"ds-calendar__nav-btn\"\n (click)=\"previousYear()\"\n aria-label=\"Ann\u00E9e pr\u00E9c\u00E9dente\"\n >\n <fa-icon [icon]=\"icons.chevronLeft\"></fa-icon>\n </button>\n }\n\n <button\n type=\"button\"\n class=\"ds-calendar__title\"\n (click)=\"toggleMode()\"\n [attr.aria-label]=\"currentMode() === 'month' ? 'Voir l\\'ann\u00E9e' : 'Voir le mois'\"\n >\n {{ calendarTitle() }}\n </button>\n\n @if (currentMode() === 'month') {\n <button\n type=\"button\"\n class=\"ds-calendar__nav-btn\"\n (click)=\"nextMonth()\"\n aria-label=\"Mois suivant\"\n >\n <fa-icon [icon]=\"icons.chevronRight\"></fa-icon>\n </button>\n } @else {\n <button\n type=\"button\"\n class=\"ds-calendar__nav-btn\"\n (click)=\"nextYear()\"\n aria-label=\"Ann\u00E9e suivante\"\n >\n <fa-icon [icon]=\"icons.chevronRight\"></fa-icon>\n </button>\n }\n </div>\n\n <div class=\"ds-calendar__mode-selector\">\n <button\n type=\"button\"\n class=\"ds-calendar__mode-btn\"\n [class.ds-calendar__mode-btn--active]=\"currentMode() === 'month'\"\n (click)=\"currentMode() === 'year' && toggleMode()\"\n >\n Mois\n </button>\n <button\n type=\"button\"\n class=\"ds-calendar__mode-btn\"\n [class.ds-calendar__mode-btn--active]=\"currentMode() === 'year'\"\n (click)=\"currentMode() === 'month' && toggleMode()\"\n >\n Ann\u00E9e\n </button>\n </div>\n </div>\n\n <!-- Vue mois -->\n @if (currentMode() === 'month') {\n <div class=\"ds-calendar__month-view\">\n <!-- Noms des jours de la semaine -->\n <div class=\"ds-calendar__weekdays\">\n @for (day of weekDays(); track day) {\n <div class=\"ds-calendar__weekday\">{{ day }}</div>\n }\n </div>\n\n <!-- Grille des jours -->\n <div class=\"ds-calendar__grid\">\n @for (day of calendarGrid(); track day.date.getTime()) {\n <button\n type=\"button\"\n [class]=\"getDayClasses(day)\"\n [disabled]=\"day.isDisabled\"\n (click)=\"selectDate(day)\"\n [attr.aria-label]=\"day.date.toLocaleDateString(locale())\"\n [attr.aria-current]=\"day.isToday ? 'date' : null\"\n >\n <span class=\"ds-calendar__day-number\">{{ day.date.getDate() }}</span>\n\n @if (day.events.length > 0) {\n <div class=\"ds-calendar__events\">\n @for (event of day.events; track event.id) {\n <div\n [class]=\"getEventClasses(event)\"\n [title]=\"event.title\"\n (click)=\"onEventClick(event, $event)\"\n role=\"button\"\n [attr.aria-label]=\"event.title\"\n >\n <span class=\"ds-calendar__event-dot\"></span>\n @if (size() === 'lg') {\n <span class=\"ds-calendar__event-title\">{{ event.title }}</span>\n }\n </div>\n }\n </div>\n }\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Vue ann\u00E9e -->\n @if (currentMode() === 'year') {\n <div class=\"ds-calendar__year-view\">\n <div class=\"ds-calendar__months-grid\">\n @for (month of monthGrid(); track month.monthIndex) {\n <button\n type=\"button\"\n [class]=\"getMonthClasses(month)\"\n (click)=\"selectMonth(month)\"\n [attr.aria-label]=\"month.label\"\n [attr.aria-current]=\"month.isCurrentMonth ? 'true' : null\"\n >\n {{ month.label }}\n </button>\n }\n </div>\n </div>\n }\n</div>\n", styles: [".ds-calendar{display:block;width:100%}.ds-calendar__container{background:var(--calendar-bg, var(--white));border:1px solid var(--calendar-border, var(--gray-200));border-radius:var(--calendar-radius, var(--radius-2));overflow:hidden}.ds-calendar__header{display:flex;align-items:center;justify-content:space-between;padding:var(--calendar-header-padding, var(--space-4));border-bottom:1px solid var(--calendar-border, var(--gray-200));background:var(--calendar-header-bg, var(--gray-50))}.ds-calendar__nav{display:flex;align-items:center;gap:var(--calendar-nav-gap, var(--space-2))}.ds-calendar__nav-btn{display:inline-flex;align-items:center;justify-content:center;width:var(--calendar-nav-btn-size, 32px);height:var(--calendar-nav-btn-size, 32px);padding:0;background:transparent;border:1px solid var(--calendar-nav-btn-border, var(--gray-300));border-radius:var(--calendar-nav-btn-radius, var(--radius-1));color:var(--calendar-nav-btn-color, var(--gray-700));font-size:var(--calendar-nav-btn-font-size, var(--font-size-3));cursor:pointer;transition:all .2s ease}.ds-calendar__nav-btn:hover:not(:disabled){background:var(--calendar-nav-btn-hover-bg, var(--gray-100));border-color:var(--calendar-nav-btn-hover-border, var(--gray-400));color:var(--calendar-nav-btn-hover-color, var(--gray-900))}.ds-calendar__nav-btn:active:not(:disabled){transform:scale(.95)}.ds-calendar__nav-btn:disabled{opacity:.5;cursor:not-allowed}.ds-calendar__title{padding:var(--calendar-title-padding, var(--space-2) var(--space-3));background:transparent;border:1px solid transparent;border-radius:var(--calendar-title-radius, var(--radius-1));color:var(--calendar-title-color, var(--gray-900));font-size:var(--calendar-title-font-size, var(--font-size-4));font-weight:var(--calendar-title-font-weight, 600);cursor:pointer;transition:all .2s ease}.ds-calendar__title:hover{background:var(--calendar-title-hover-bg, var(--gray-100));border-color:var(--calendar-title-hover-border, var(--gray-300))}.ds-calendar__mode-selector{display:flex;gap:var(--calendar-mode-gap, var(--space-1));background:var(--calendar-mode-bg, var(--white));border:1px solid var(--calendar-mode-border, var(--gray-300));border-radius:var(--calendar-mode-radius, var(--radius-1));padding:var(--calendar-mode-padding, 2px)}.ds-calendar__mode-btn{padding:var(--calendar-mode-btn-padding, var(--space-1) var(--space-3));background:transparent;border:none;border-radius:var(--calendar-mode-btn-radius, var(--radius-1));color:var(--calendar-mode-btn-color, var(--gray-700));font-size:var(--calendar-mode-btn-font-size, var(--font-size-2));cursor:pointer;transition:all .2s ease}.ds-calendar__mode-btn:hover{color:var(--calendar-mode-btn-hover-color, var(--gray-900))}.ds-calendar__mode-btn--active{background:var(--calendar-mode-btn-active-bg, var(--color-primary));color:var(--calendar-mode-btn-active-color, var(--white))}.ds-calendar__month-view{padding:var(--calendar-month-view-padding, var(--space-4))}.ds-calendar__weekdays{display:grid;grid-template-columns:repeat(7,1fr);gap:var(--calendar-weekday-gap, var(--space-1));margin-bottom:var(--calendar-weekday-margin, var(--space-2))}.ds-calendar__weekday{text-align:center;color:var(--calendar-weekday-color, var(--gray-600));font-size:var(--calendar-weekday-font-size, var(--font-size-2));font-weight:var(--calendar-weekday-font-weight, 600);text-transform:uppercase;padding:var(--calendar-weekday-padding, var(--space-2) 0)}.ds-calendar__grid{display:grid;grid-template-columns:repeat(7,1fr);gap:var(--calendar-grid-gap, var(--space-1))}.ds-calendar__day{position:relative;aspect-ratio:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;padding:var(--calendar-day-padding, var(--space-2));background:var(--calendar-day-bg, transparent);border:1px solid var(--calendar-day-border, transparent);border-radius:var(--calendar-day-radius, var(--radius-1));color:var(--calendar-day-color, var(--gray-900));font-size:var(--calendar-day-font-size, var(--font-size-3));cursor:pointer;transition:all .2s ease}.ds-calendar__day:hover:not(:disabled){background:var(--calendar-day-hover-bg, var(--gray-100));border-color:var(--calendar-day-hover-border, var(--gray-300))}.ds-calendar__day--other-month{color:var(--calendar-day-other-month-color, var(--gray-400))}.ds-calendar__day--today{background:var(--calendar-day-today-bg, var(--blue-50));border-color:var(--calendar-day-today-border, var(--color-primary));color:var(--calendar-day-today-color, var(--color-primary));font-weight:600}.ds-calendar__day--disabled{opacity:.4;cursor:not-allowed;background:var(--calendar-day-disabled-bg, var(--gray-50))}.ds-calendar__day--has-events .ds-calendar__day-number{font-weight:600}.ds-calendar__day-number{line-height:1;margin-bottom:var(--calendar-day-number-margin, var(--space-1))}.ds-calendar__events{display:flex;flex-direction:column;gap:var(--calendar-events-gap, 2px);width:100%;margin-top:auto}.ds-calendar__event{display:flex;align-items:center;gap:var(--calendar-event-gap, var(--space-1));padding:var(--calendar-event-padding, 2px);border-radius:var(--calendar-event-radius, var(--radius-1));cursor:pointer;transition:all .2s ease}.ds-calendar__event:hover{transform:scale(1.05)}.ds-calendar__event-dot{width:var(--calendar-event-dot-size, 6px);height:var(--calendar-event-dot-size, 6px);border-radius:50%;background:var(--calendar-event-dot-color, var(--gray-500));flex-shrink:0}.ds-calendar__event-title{font-size:var(--calendar-event-title-font-size, var(--font-size-1));color:var(--calendar-event-title-color, var(--gray-700));white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ds-calendar__event--success .ds-calendar__event-dot{background:var(--calendar-event-success-color, var(--success))}.ds-calendar__event--warning .ds-calendar__event-dot{background:var(--calendar-event-warning-color, var(--warning))}.ds-calendar__event--error .ds-calendar__event-dot{background:var(--calendar-event-error-color, var(--error))}.ds-calendar__year-view{padding:var(--calendar-year-view-padding, var(--space-4))}.ds-calendar__months-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--calendar-months-gap, var(--space-3))}.ds-calendar__month{padding:var(--calendar-month-padding, var(--space-4));background:var(--calendar-month-bg, transparent);border:1px solid var(--calendar-month-border, var(--gray-200));border-radius:var(--calendar-month-radius, var(--radius-2));color:var(--calendar-month-color, var(--gray-900));font-size:var(--calendar-month-font-size, var(--font-size-3));font-weight:500;cursor:pointer;transition:all .2s ease;text-align:center}.ds-calendar__month:hover{background:var(--calendar-month-hover-bg, var(--gray-50));border-color:var(--calendar-month-hover-border, var(--gray-300))}.ds-calendar__month--current{background:var(--calendar-month-current-bg, var(--blue-50));border-color:var(--calendar-month-current-border, var(--color-primary));color:var(--calendar-month-current-color, var(--color-primary));font-weight:600}.ds-calendar--sm .ds-calendar__header{padding:var(--calendar-header-padding-sm, var(--space-2))}.ds-calendar--sm .ds-calendar__title{font-size:var(--calendar-title-font-size-sm, var(--font-size-3))}.ds-calendar--sm .ds-calendar__weekday{font-size:var(--calendar-weekday-font-size-sm, var(--font-size-1))}.ds-calendar--sm .ds-calendar__day{padding:var(--calendar-day-padding-sm, var(--space-1));font-size:var(--calendar-day-font-size-sm, var(--font-size-2))}.ds-calendar--sm .ds-calendar__month{padding:var(--calendar-month-padding-sm, var(--space-2));font-size:var(--calendar-month-font-size-sm, var(--font-size-2))}.ds-calendar--lg .ds-calendar__header{padding:var(--calendar-header-padding-lg, var(--space-6))}.ds-calendar--lg .ds-calendar__title{font-size:var(--calendar-title-font-size-lg, var(--font-size-5))}.ds-calendar--lg .ds-calendar__weekday{font-size:var(--calendar-weekday-font-size-lg, var(--font-size-3))}.ds-calendar--lg .ds-calendar__day{padding:var(--calendar-day-padding-lg, var(--space-3));font-size:var(--calendar-day-font-size-lg, var(--font-size-4))}.ds-calendar--lg .ds-calendar__month{padding:var(--calendar-month-padding-lg, var(--space-6));font-size:var(--calendar-month-font-size-lg, var(--font-size-4))}\n"] }]
11432
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], events: [{ type: i0.Input, args: [{ isSignal: true, alias: "events", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: false }] }], disabledDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledDate", required: false }] }], dateSelect: [{ type: i0.Output, args: ["dateSelect"] }], monthChange: [{ type: i0.Output, args: ["monthChange"] }], modeChange: [{ type: i0.Output, args: ["modeChange"] }], eventClick: [{ type: i0.Output, args: ["eventClick"] }] } });
11433
+
10166
11434
  /*
10167
11435
  * Components barrel export
10168
11436
  */
@@ -10734,5 +12002,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
10734
12002
  * Generated bundle index. Do not edit.
10735
12003
  */
10736
12004
 
10737
- export { AUTOCOMPLETE_POSITIONS, BUTTON_APPEARANCE_OPTIONS, BUTTON_SIZE_OPTIONS, BUTTON_VARIANT_OPTIONS, DROPDOWN_POSITIONS, DsAccordion, DsAlert, DsAvatar, DsBadge, DsBreadcrumb, DsButton, DsCard, DsCheckbox, DsChip, DsCombobox, DsContainer, DsDatePicker, DsDivider, DsDrawer, DsDropdown, DsEmpty, DsFileUpload, DsI18nService, DsInputField, DsInputTextarea, DsMenu, DsModalComponent, DsPagination, DsPopover, DsPopoverComponent, DsProgressBar, DsRadioGroup, DsRating, DsSearchInput, DsSelect, DsSkeleton, DsSlider, DsStepper, DsTable, DsTabs, DsTimePicker, DsToastComponent, DsToastContainerComponent, DsToastService, DsToggle, DsTooltip, DsTooltipComponent, DsTree, IconRegistryService, POPOVER_POSITIONS, PrimitiveBadge, PrimitiveButton, PrimitiveCheckbox, PrimitiveInput, PrimitiveRadio, PrimitiveTextarea, PrimitiveToggle, TOOLTIP_POSITIONS, buildButtonArgTypes, buildButtonArgs, createSizeRender, createVariantRender };
12005
+ export { AUTOCOMPLETE_POSITIONS, BUTTON_APPEARANCE_OPTIONS, BUTTON_SIZE_OPTIONS, BUTTON_VARIANT_OPTIONS, DROPDOWN_POSITIONS, DsAccordion, DsAlert, DsAvatar, DsBadge, DsBreadcrumb, DsButton, DsCalendar, DsCard, DsCarousel, DsCheckbox, DsChip, DsCombobox, DsContainer, DsDatePicker, DsDivider, DsDrawer, DsDropdown, DsEmpty, DsFileUpload, DsI18nService, DsInputField, DsInputTextarea, DsMenu, DsModalComponent, DsNotificationContainerComponent, DsNotificationItemComponent, DsNotificationService, DsPagination, DsPasswordStrength, DsPopover, DsPopoverComponent, DsProgressBar, DsRadioGroup, DsRating, DsSearchInput, DsSelect, 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, TOOLTIP_POSITIONS, buildButtonArgTypes, buildButtonArgs, createSizeRender, createVariantRender };
10738
12006
  //# sourceMappingURL=kksdev-ds-angular.mjs.map